diff --git a/docs/ADR-GKS-GENESISRAG17.md b/docs/ADR-GKS-GENESISRAG17.md index eaacce7..3bd2664 100644 --- a/docs/ADR-GKS-GENESISRAG17.md +++ b/docs/ADR-GKS-GENESISRAG17.md @@ -1,7 +1,7 @@ --- -version: "0.5.2b" +version: "0.5.3b" created_at: "2026-09-07T23:30:00+07:00,RWANG,working-tree" -last_update: "2026-09-27T10:00:00+07:00,Claude Opus 5.5,working-tree" +last_update: "2026-09-27T17:00:00+07:00,Claude Opus 5.5,working-tree" status: "accepted" approval_owner: "Boss (บอส)" approval_recorded_at: "2026-09-07T23:00:00+07:00" @@ -232,6 +232,7 @@ evidence. | Version | Date | Status | Summary | Commit Hash | Agent | |---|---|---|---|---|---| | 0.5.2b | 2026-09-27 | accepted | Cross-reference: ADR-GKS-PIPELINE-VISIBILITY (accepted 2026-09-27) keeps submit-time canonical entities invisible to legacy reads until a mentioning run is PUBLISHED; pipeline tools and Stage 9 reuse are unchanged. | working-tree | Claude Opus 5.5 | +| 0.5.3b | 2026-09-27 | accepted | GKS-PIP-001 (owner decision): a submitted batch must list its nine stage identities in catalog order. Receipts are still matched to the stored decision order-insensitively, so decisions stored before this rule are not stranded. See GKS-PORT-CONTRACT 0.10.2b. | working-tree | Claude | | 0.5.1b | 2026-09-27 | accepted | Added the optional `GKS_PIPELINE_WORKER_CREDENTIAL` split so source and worker roles are bound to distinct secrets; unset keeps the single-credential compatibility mode. | working-tree | Claude Opus 5.5 | | 0.5.0b | 2026-09-11 | accepted | Implemented structured-record profile contract revision 2 on the GKS side (ADR-075 Phase 2, rollout step 2 of 3): `PIPELINE_ONTOLOGY_VERSION` is `ontology_v2`, `PIPELINE_SUPPORTED_ONTOLOGY_VERSIONS` is `{ontology_v1, ontology_v2}`, the Stage 11 `validEndpoint` ternary is replaced by the per-version predicate -> endpoint table `PIPELINE_ONTOLOGY_ENDPOINTS` (v2 adds `HAS_COMPONENT`, `PRICED_AT`, `IN_CATEGORY` and the `PACKAGE`/`CATEGORY`/`PRICE_TIER` endpoint types), and the Stage 17 knowledge dimension checks set membership and each fact against its own version instead of equality with `ontology_v1`. `rule_v1`, `parseStructuredClaim`, Stage 12 and the C-10 bitemporal count are unchanged. Must merge after the GenesisBlock worker change that accepts both versions. | working-tree | Claude Opus 5 | | 0.4.4b | 2026-09-11 | accepted | Fixed a residual of contract item C-10: the Stage 17 expected bitemporal lane count now includes HELD rows that carry valid time, not only facts, using the same row classification the worker applies (`temporalRows()` covers facts and held together; a row is dated unless validFrom/validTo are undefined/null/`not_applicable` and status is undefined/`not_applicable`). A decision with a dated held row previously disagreed with the worker's count even though the 0.4.3b fix already matched on facts alone. The all-`not_applicable` path (expect 0, lane `not_applicable`) now also considers facts and held together. Held rows already make the knowledge dimension WARN; this only corrects the graph-dimension reason, never whether anything publishes. | working-tree | Claude Opus 5 | diff --git a/docs/GKS-PORT-CONTRACT.md b/docs/GKS-PORT-CONTRACT.md index 986ab61..d2bbe82 100644 --- a/docs/GKS-PORT-CONTRACT.md +++ b/docs/GKS-PORT-CONTRACT.md @@ -1,7 +1,7 @@ --- -version: "0.10.1b" +version: "0.10.2b" created_at: "2026-08-12T10:05:34+07:00,ATHER,working-tree" -last_update: "2026-09-27T10:00:00+07:00,Claude" +last_update: "2026-09-27T17:00:00+07:00,Claude" status: "beta" approval_owner: "Boss (บอส)" approval_recorded_at: "2026-08-12T10:16:19+07:00" @@ -190,7 +190,15 @@ idempotent replies. A same identity with a different hash is a conflict. ### Stage identity and receipt ordering The nine stage identities in a batch are fixed to stages 9 through 17 and their -`DPS-KI-*` ids. They are carried into receipts and evidence unchanged. The +`DPS-KI-*` ids. A submitted batch lists them **in catalog order**, 9 through 17. +Any other order is `gks_invalid_request`, and the message names the first +position that is wrong, for example `stages[1] must be stage 10: stages are +listed in catalog order, 9 through 17.` + +The identities are carried into receipts and evidence unchanged. A receipt is +matched to its stored decision regardless of the order it lists them in. A +decision stored before the order rule keeps the order it was submitted in, and +the worker echoes that order, so rejecting it would strand the decision. The physical order is: ```text @@ -542,6 +550,7 @@ implementation package name appears in the client. | Version | Date | Status | Summary | Commit Hash | Agent | |---|---|---|---|---|---| | 0.10.1b | 2026-09-27 | beta | Per ADR-GKS-PIPELINE-VISIBILITY (accepted 2026-09-27): legacy reads, the legacy resolution pool and D9 operands exclude unpublished GenesisRAG17 entities; `lookupResolutionCandidates` gains `includeUnpublishedPipeline` for Stage 9 reuse; D9 MERGE refuses to supersede a pipeline-origin entity (`gks_conflict`). No tool request or result shape changes. | working-tree | Claude | +| 0.10.2b | 2026-09-27 | beta | GKS-PIP-001 (owner decision): `gks_pipeline_submit` requires stage identities in catalog order 9 through 17. Receipts stay order-insensitive against the stored decision. | working-tree | Claude | | 0.10.0b | 2026-09-24 | beta | Implements optional per-client hash-backed direct HTTP read grants while preserving the MSP profile; grants are not enabled by default and production rollout remains separate. | working-tree | RWANG | | 0.9.0b | 2026-09-24 | beta | Adds the approved direct-client read-only grant profile while preserving MSP auth for governed writes; concrete identity verification and activation remain unimplemented. | working-tree | RWANG | | 0.8.1b | 2026-09-22 | beta | Removed the stale port-version-1 statement that contradicted the selected GKS-owned SQLite production profile; deployment evidence remains separately gated. | working-tree | RWANG | diff --git a/docs/reports/2026-09-27-p1-c0-closure.md b/docs/reports/2026-09-27-p1-c0-closure.md index 4d6d75d..676a8bc 100644 --- a/docs/reports/2026-09-27-p1-c0-closure.md +++ b/docs/reports/2026-09-27-p1-c0-closure.md @@ -1,7 +1,7 @@ --- -version: "0.1.2b" +version: "0.1.3b" created_at: "2026-09-27T10:30:00+07:00,Claude,working-tree" -last_update: "2026-09-27T16:00:00+07:00,Claude" +last_update: "2026-09-27T17:00:00+07:00,Claude" status: "candidate" superseded_by: null attributes: @@ -138,7 +138,7 @@ versioned rollout, not with this closure. | Requirement | Current behaviour | Proposed change | |---|---|---| | GKS-API-002 | ~~`jsonrpc: "2.0"` is not validated~~ | **Done:** any other version is a -32600 Invalid Request ([ADR-GKS-C0-QUALIFICATION](../ADR-GKS-C0-QUALIFICATION.md) D3, 0.3.1) | -| GKS-PIP-001 | stage identities are accepted in any order | enforce catalog order | +| GKS-PIP-001 | ~~stage identities are accepted in any order~~ | **Done:** a submitted batch must list them in catalog order; receipts stay order-insensitive ([GKS-PORT-CONTRACT](../GKS-PORT-CONTRACT.md) 0.10.2b) | | GKS-ING-004 | hashes the decoded string, so a lone surrogate becomes U+FFFD | hash raw bytes, or reject lone surrogates | | GKS-ONT-002 | legacy promote relations have no endpoint type check | apply the ontology endpoint table | | GKS-PIP-008 | a legacy export with an out-of-range cursor returns empty | return `gks_invalid_request`, as the pipeline export does | @@ -153,3 +153,4 @@ versioned rollout, not with this closure. | 0.1.0b | 2026-09-27 | candidate | P1 C0 closure: executable baseline lock and CI gate, lost-response replay PASS, IMMEDIATE write transactions and error-code discipline, C0 acceptance evidence | working-tree | Claude | | 0.1.1b | 2026-09-27 | candidate | GKS-MIG-002 closed by the C0.4 golden corpus v2 | working-tree | Claude | | 0.1.2b | 2026-09-27 | candidate | GKS-API-002 closed: JSON-RPC version is validated | working-tree | Claude | +| 0.1.3b | 2026-09-27 | candidate | GKS-PIP-001 closed: submitted stage identities must be in catalog order | working-tree | Claude | diff --git a/packages/gks-contracts/src/pipeline.mjs b/packages/gks-contracts/src/pipeline.mjs index b7ddfbe..66c0b61 100644 --- a/packages/gks-contracts/src/pipeline.mjs +++ b/packages/gks-contracts/src/pipeline.mjs @@ -252,7 +252,12 @@ export function authorizePipelineRequest(input, { relayCredential, role, scope } return Object.freeze({ principalId: principal.principalId, role: principal.role, scope: principal.scope }); } -export function validateStageIdentities(input, runId, label = "stages") { +// GKS-PIP-001: a submitted batch lists its stage identities in catalog order, +// 9 through 17. Receipts pass `catalogOrder: false`: they echo a stored +// decision, which is matched order-insensitively (persistence +// sameStageIdentity), and a decision stored before this rule keeps the order +// it was submitted in. Requiring the order there would strand it. +export function validateStageIdentities(input, runId, label = "stages", { catalogOrder = true } = {}) { if (!Array.isArray(input) || input.length !== PIPELINE_STAGE_CATALOG.length) invalid(`${label} must contain exactly the nine stage identities.`); const seen = new Set(); const normalized = input.map((stage, index) => { @@ -262,6 +267,9 @@ export function validateStageIdentities(input, runId, label = "stages") { const expected = PIPELINE_STAGE_BY_NUMBER[stageNumber]; if (stage.pipelineStageId !== expected.pipelineStageId) invalid(`${label}[${index}].pipelineStageId does not match stage ${stageNumber}.`); if (seen.has(stageNumber)) invalid(`${label} contains duplicate stage ${stageNumber}.`); + if (catalogOrder && stageNumber !== PIPELINE_STAGE_CATALOG[index].stageNumber) { + invalid(`${label}[${index}] must be stage ${PIPELINE_STAGE_CATALOG[index].stageNumber}: stages are listed in catalog order, 9 through 17.`); + } seen.add(stageNumber); for (const key of ["runId", "executionStepId", "attemptId"]) requirePipelineString(stage[key], `${label}[${index}].${key}`); if (stage.runId !== runId) invalid(`${label}[${index}].runId must match runId.`); @@ -455,7 +463,7 @@ export function validatePipelineReceipt(input) { const runId = requirePipelineString(input.runId, "receipt.runId"); const decisionId = requirePipelineString(input.decisionId, "receipt.decisionId"); const decisionHash = validateHash(input.decisionHash, "receipt.decisionHash"); - const stages = validateStageIdentities(input.stages, runId, "receipt.stages"); + const stages = validateStageIdentities(input.stages, runId, "receipt.stages", { catalogOrder: false }); const graphReceiptHash = validateHash(input.graphReceiptHash, "receipt.graphReceiptHash"); const derivedHash = validateHash(input.derivedHash, "receipt.derivedHash"); if (!isPlainObject(input.executionTimes)) invalid("receipt.executionTimes is required."); @@ -506,7 +514,7 @@ export function validatePipelineGraphReceipt(input) { const runId = requirePipelineString(input.runId, "graphReceipt.runId"); const decisionId = requirePipelineString(input.decisionId, "graphReceipt.decisionId"); const decisionHash = validateHash(input.decisionHash, "graphReceipt.decisionHash"); - const stages = validateStageIdentities(input.stages, runId, "graphReceipt.stages"); + const stages = validateStageIdentities(input.stages, runId, "graphReceipt.stages", { catalogOrder: false }); if (!isPlainObject(input.transaction)) invalid("graphReceipt.transaction is required."); for (const field of ["id", "frontier", "checkpoint"]) requirePipelineString(input.transaction[field], `graphReceipt.transaction.${field}`); if (!isPlainObject(input.readback) || typeof input.readback.ok !== "boolean") invalid("graphReceipt.readback is invalid."); diff --git a/scripts/c0-corpus/build-cases.mjs b/scripts/c0-corpus/build-cases.mjs index 9c2626d..90ef0a5 100644 --- a/scripts/c0-corpus/build-cases.mjs +++ b/scripts/c0-corpus/build-cases.mjs @@ -215,6 +215,10 @@ const TOOL_STEPS = { const batch = batchFor("c0-submit"); await pipelineSteps(s, batch, "submit"); s.call("replay", "gks_pipeline_submit", { schemaVersion: PIPELINE_SCHEMA_VERSION, scope: batch.scope, batch, ...source }, { ok: true, match: { idempotent: true } }); + // GKS-PIP-001: stage identities are listed in catalog order, 9 through 17. + const unordered = batchFor("c0-submit-unordered"); + const stages = [unordered.stages[0], unordered.stages[2], unordered.stages[1], ...unordered.stages.slice(3)]; + s.call("outOfOrder", "gks_pipeline_submit", { schemaVersion: PIPELINE_SCHEMA_VERSION, scope: unordered.scope, batch: { ...unordered, stages }, ...source }, { toolError: "gks_invalid_request", toolMessage: "stages[1] must be stage 10: stages are listed in catalog order, 9 through 17." }); }, async gks_pipeline_claim(s) { await pipelineSteps(s, batchFor("c0-claim"), "claim"); }, async gks_pipeline_graph_receipt(s) { diff --git a/tests/contract/c0-acceptance.test.mjs b/tests/contract/c0-acceptance.test.mjs index 4435ce6..c9f48bb 100644 --- a/tests/contract/c0-acceptance.test.mjs +++ b/tests/contract/c0-acceptance.test.mjs @@ -147,12 +147,36 @@ describe("C0 identity and pipeline acceptance", () => { ["a duplicated stage", (stages) => [...stages.slice(0, 8), stages[0]]], ["a missing stage", (stages) => stages.slice(0, 8)], ["a stage under the wrong DPS-KI id", (stages) => stages.map((stage, index) => (index === 0 ? { ...stage, pipelineStageId: stages[1].pipelineStageId } : stage))], + ["stages out of catalog order", (stages) => [stages[0], stages[2], stages[1], ...stages.slice(3)]], + ["stages in reverse order", (stages) => [...stages].reverse()], ])("rejects %s", async (_label, mutate) => { const { service } = harness(); const batch = makeBatch({ id: "batch-stage-identities" }); await expect(service.pipelineSubmit(submitArgs({ ...batch, stages: mutate(batch.stages) }))).rejects.toMatchObject({ code: "gks_invalid_request" }); }); + it("names the first stage that breaks catalog order", async () => { + const { service } = harness(); + const batch = makeBatch({ id: "batch-stage-order" }); + const swapped = [batch.stages[0], batch.stages[2], batch.stages[1], ...batch.stages.slice(3)]; + await expect(service.pipelineSubmit(submitArgs({ ...batch, stages: swapped }))).rejects.toMatchObject({ + code: "gks_invalid_request", + message: "stages[1] must be stage 10: stages are listed in catalog order, 9 through 17.", + }); + }); + + // Receipts are matched to the stored decision without regard to order: a + // decision stored before the order rule keeps its submitted order, and the + // worker echoes whatever the decision carries. + it("still accepts a graph receipt whose stage identities are listed out of order", async () => { + const { service } = harness(); + const batch = makeBatch({ id: "batch-receipt-stage-order" }); + const { decision } = await submitAndClaim(service, batch); + const receipt = graphReceiptFor(decision); + const result = await service.pipelineGraphReceipt({ receipt: { ...receipt, stages: [...receipt.stages].reverse() }, ...auth(batch.scope, "worker") }); + expect(result).toMatchObject({ accepted: true, idempotent: false }); + }); + // GKS-PIP-003: claim is non-destructive -- two workers can see one decision, // and the second worker's identical receipt is an idempotent replay while a // different one is a conflict. diff --git a/tests/fixtures/c0-qualification/cases/C0.4-TOOL-010.json b/tests/fixtures/c0-qualification/cases/C0.4-TOOL-010.json index 246ca8f..653637c 100644 --- a/tests/fixtures/c0-qualification/cases/C0.4-TOOL-010.json +++ b/tests/fixtures/c0-qualification/cases/C0.4-TOOL-010.json @@ -556,6 +556,277 @@ "idempotent": true } } + }, + { + "kind": "call", + "name": "outOfOrder", + "frame": { + "jsonrpc": "2.0", + "id": 3, + "method": "tools/call", + "params": { + "name": "gks_pipeline_submit", + "arguments": { + "schemaVersion": "genesisrag17.v1", + "scope": { + "portfolioId": "c0-portfolio", + "tenantId": "c0-tenant", + "businessId": "c0-business", + "workspaceId": "", + "agentId": "c0-agent", + "visibility": "private" + }, + "batch": { + "schemaVersion": "genesisrag17.v1", + "batchId": "c0-submit-unordered", + "idempotencyKey": "c0-submit-unordered-idempotency", + "scope": { + "portfolioId": "c0-portfolio", + "tenantId": "c0-tenant", + "businessId": "c0-business", + "workspaceId": "", + "agentId": "c0-agent", + "visibility": "private" + }, + "runId": "c0-submit-unordered-run", + "stages": [ + { + "stageNumber": 9, + "pipelineStageId": "DPS-KI-ENTITY-RESOLVE", + "runId": "c0-submit-unordered-run", + "executionStepId": "c0-submit-unordered-step-9", + "attemptId": "c0-submit-unordered-attempt-9" + }, + { + "stageNumber": 11, + "pipelineStageId": "DPS-KI-ONTOLOGY-MAP", + "runId": "c0-submit-unordered-run", + "executionStepId": "c0-submit-unordered-step-11", + "attemptId": "c0-submit-unordered-attempt-11" + }, + { + "stageNumber": 10, + "pipelineStageId": "DPS-KI-FACT-EXTRACT", + "runId": "c0-submit-unordered-run", + "executionStepId": "c0-submit-unordered-step-10", + "attemptId": "c0-submit-unordered-attempt-10" + }, + { + "stageNumber": 12, + "pipelineStageId": "DPS-KI-TEMPORAL-MAP", + "runId": "c0-submit-unordered-run", + "executionStepId": "c0-submit-unordered-step-12", + "attemptId": "c0-submit-unordered-attempt-12" + }, + { + "stageNumber": 13, + "pipelineStageId": "DPS-KI-GRAPH-BUILD", + "runId": "c0-submit-unordered-run", + "executionStepId": "c0-submit-unordered-step-13", + "attemptId": "c0-submit-unordered-attempt-13" + }, + { + "stageNumber": 14, + "pipelineStageId": "DPS-KI-ENRICH", + "runId": "c0-submit-unordered-run", + "executionStepId": "c0-submit-unordered-step-14", + "attemptId": "c0-submit-unordered-attempt-14" + }, + { + "stageNumber": 15, + "pipelineStageId": "DPS-KI-EMBED", + "runId": "c0-submit-unordered-run", + "executionStepId": "c0-submit-unordered-step-15", + "attemptId": "c0-submit-unordered-attempt-15" + }, + { + "stageNumber": 16, + "pipelineStageId": "DPS-KI-INDEX", + "runId": "c0-submit-unordered-run", + "executionStepId": "c0-submit-unordered-step-16", + "attemptId": "c0-submit-unordered-attempt-16" + }, + { + "stageNumber": 17, + "pipelineStageId": "DPS-KI-QUALITY-GATE", + "runId": "c0-submit-unordered-run", + "executionStepId": "c0-submit-unordered-step-17", + "attemptId": "c0-submit-unordered-attempt-17" + } + ], + "source": { + "sourceId": "c0-submit-unordered-source", + "rawArtifactId": "c0-submit-unordered-raw", + "parsedArtifactId": "c0-submit-unordered-parsed", + "documentId": "c0-submit-unordered-document", + "version": "1", + "contentHash": "3d85099fe6eeebd2d69b103c8ea4824dddd4604ccbcef4d1a37da3f8ded2be5a", + "content": "Alice works for Acme Ltd.\nAlice purchased Atlas.\nBob works for Beacon Ltd.\nBob purchased Nimbus.\nCarol works for Cedar Ltd." + }, + "policy": { + "allowEmbedding": true, + "allowPublication": true + }, + "chunks": [ + { + "chunkId": "c0-submit-unordered-chunk-1", + "parsedArtifactId": "c0-submit-unordered-parsed", + "ordinal": 0, + "text": "Alice works for Acme Ltd.", + "contentHash": "c0d7514564bbcdd5dfa3beb8823955437e0fc20d708d3a826cedd02a5b4d518c", + "startOffset": 0, + "endOffset": 25 + }, + { + "chunkId": "c0-submit-unordered-chunk-2", + "parsedArtifactId": "c0-submit-unordered-parsed", + "ordinal": 1, + "text": "Alice purchased Atlas.", + "contentHash": "936fc7235133947fc6eb15157f76115945dbf3d53558ef5d02083f6f49fbada9", + "startOffset": 26, + "endOffset": 48 + }, + { + "chunkId": "c0-submit-unordered-chunk-3", + "parsedArtifactId": "c0-submit-unordered-parsed", + "ordinal": 2, + "text": "Bob works for Beacon Ltd.", + "contentHash": "cc900b0204ad4e3f5de47590f3740c7b8a9ee5db7b4362d68c98069d285a8aac", + "startOffset": 49, + "endOffset": 74 + }, + { + "chunkId": "c0-submit-unordered-chunk-4", + "parsedArtifactId": "c0-submit-unordered-parsed", + "ordinal": 3, + "text": "Bob purchased Nimbus.", + "contentHash": "da1bf96e80de46bfaa7ea63ae9a430f95efc28853943fb14218dbe0c44c6a1ba", + "startOffset": 75, + "endOffset": 96 + }, + { + "chunkId": "c0-submit-unordered-chunk-5", + "parsedArtifactId": "c0-submit-unordered-parsed", + "ordinal": 4, + "text": "Carol works for Cedar Ltd.", + "contentHash": "7c895c367fc4817e1d76f13b5d3fa76259376a4f0116cf3c5ce23962719aefdc", + "startOffset": 97, + "endOffset": 123 + } + ], + "mentions": [ + { + "sourceMentionId": "c0-submit-unordered-mention-1-1", + "resolutionKey": "alice", + "semanticType": "Person", + "name": "Alice", + "chunkId": "c0-submit-unordered-chunk-1", + "startOffset": 0, + "endOffset": 5 + }, + { + "sourceMentionId": "c0-submit-unordered-mention-1-2", + "resolutionKey": "acme", + "semanticType": "Organization", + "name": "Acme Ltd.", + "chunkId": "c0-submit-unordered-chunk-1", + "startOffset": 16, + "endOffset": 25 + }, + { + "sourceMentionId": "c0-submit-unordered-mention-2-1", + "resolutionKey": "alice", + "semanticType": "Person", + "name": "Alice", + "chunkId": "c0-submit-unordered-chunk-2", + "startOffset": 0, + "endOffset": 5 + }, + { + "sourceMentionId": "c0-submit-unordered-mention-2-2", + "resolutionKey": "atlas", + "semanticType": "Product", + "name": "Atlas", + "chunkId": "c0-submit-unordered-chunk-2", + "startOffset": 16, + "endOffset": 21 + }, + { + "sourceMentionId": "c0-submit-unordered-mention-3-1", + "resolutionKey": "bob", + "semanticType": "Person", + "name": "Bob", + "chunkId": "c0-submit-unordered-chunk-3", + "startOffset": 0, + "endOffset": 3 + }, + { + "sourceMentionId": "c0-submit-unordered-mention-3-2", + "resolutionKey": "beacon", + "semanticType": "Organization", + "name": "Beacon Ltd.", + "chunkId": "c0-submit-unordered-chunk-3", + "startOffset": 14, + "endOffset": 25 + }, + { + "sourceMentionId": "c0-submit-unordered-mention-4-1", + "resolutionKey": "bob", + "semanticType": "Person", + "name": "Bob", + "chunkId": "c0-submit-unordered-chunk-4", + "startOffset": 0, + "endOffset": 3 + }, + { + "sourceMentionId": "c0-submit-unordered-mention-4-2", + "resolutionKey": "nimbus", + "semanticType": "Product", + "name": "Nimbus", + "chunkId": "c0-submit-unordered-chunk-4", + "startOffset": 14, + "endOffset": 20 + }, + { + "sourceMentionId": "c0-submit-unordered-mention-5-1", + "resolutionKey": "carol", + "semanticType": "Person", + "name": "Carol", + "chunkId": "c0-submit-unordered-chunk-5", + "startOffset": 0, + "endOffset": 5 + }, + { + "sourceMentionId": "c0-submit-unordered-mention-5-2", + "resolutionKey": "cedar", + "semanticType": "Organization", + "name": "Cedar Ltd.", + "chunkId": "c0-submit-unordered-chunk-5", + "startOffset": 16, + "endOffset": 26 + } + ] + }, + "relayCredential": "c0-fixture-pipeline-relay", + "authenticatedPrincipal": { + "principalId": "source-principal", + "role": "source", + "scope": { + "portfolioId": "c0-portfolio", + "tenantId": "c0-tenant", + "businessId": "c0-business", + "workspaceId": "", + "agentId": "c0-agent", + "visibility": "private" + } + } + } + } + }, + "expect": { + "toolError": "gks_invalid_request", + "toolMessage": "stages[1] must be stage 10: stages are listed in catalog order, 9 through 17." + } } ] } diff --git a/tests/fixtures/c0-qualification/expected/C0.4-TOOL-010.json b/tests/fixtures/c0-qualification/expected/C0.4-TOOL-010.json index 52456b6..4571e9e 100644 --- a/tests/fixtures/c0-qualification/expected/C0.4-TOOL-010.json +++ b/tests/fixtures/c0-qualification/expected/C0.4-TOOL-010.json @@ -62,5 +62,27 @@ } } } + }, + { + "step": 3, + "kind": "call", + "name": "outOfOrder", + "response": { + "jsonrpc": "2.0", + "id": 3, + "result": { + "isError": true, + "content": [ + { + "type": "text", + "text": "stages[1] must be stage 10: stages are listed in catalog order, 9 through 17." + } + ], + "structuredContent": { + "code": "gks_invalid_request", + "message": "stages[1] must be stage 10: stages are listed in catalog order, 9 through 17." + } + } + } } ] diff --git a/tests/fixtures/c0-qualification/registry.json b/tests/fixtures/c0-qualification/registry.json index 24d755b..10598f2 100644 --- a/tests/fixtures/c0-qualification/registry.json +++ b/tests/fixtures/c0-qualification/registry.json @@ -381,8 +381,8 @@ "runnable": true, "fixture": "cases/C0.4-TOOL-010.json", "expectedResult": "expected/C0.4-TOOL-010.json", - "requestSha256": "13ad5b9d4c4c246f40e5cb011d5e9c657465e206a520dce5db3ac83fb8d959ab", - "expectedResultSha256": "f62ce397b65dfee3bfa94b8bf70c4d6efbe1c3cf34c58a57a95167da9114d72e", + "requestSha256": "e668f27268150d21056f499326118abe1ca2ccf9ce160a5e7b555f46f7a3441c", + "expectedResultSha256": "20df9d9d67c4e4c62852131bc429a335ed3bd100cc7575b7842c9e5590a9d86e", "normalizedScope": { "portfolioId": "c0-portfolio", "tenantId": "c0-tenant", diff --git a/tests/fixtures/c0-qualification/result-manifest.json b/tests/fixtures/c0-qualification/result-manifest.json index ccb8f85..69907d2 100644 --- a/tests/fixtures/c0-qualification/result-manifest.json +++ b/tests/fixtures/c0-qualification/result-manifest.json @@ -3,8 +3,8 @@ "registryVersion": "c0-qualification/v2", "fixtureId": "gks-c0.4-golden", "recordedAt": "2026-09-27", - "productSha": "0344f33ebf5f0ad5cc7807ffd5ec18c9da83d6f8+working-tree", - "corpusSha": "0344f33ebf5f0ad5cc7807ffd5ec18c9da83d6f8+working-tree", + "productSha": "333fa12d311cb5e5a22c6d80dddc3cef35c9c15a+working-tree", + "corpusSha": "333fa12d311cb5e5a22c6d80dddc3cef35c9c15a+working-tree", "runtime": { "node": "24.19.0", "platform": "win32",