From fc81ccc32e4e0299e3187f3cf0a60c68da0301df Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E9=83=91=E8=BE=BE=E5=A5=87?= <> Date: Mon, 27 Jul 2026 09:48:11 +0800 Subject: [PATCH 1/4] fix(legal): validate source repair preparation --- .../legal/plugins/legal-coverage/hook.mjs | 7 ++++ .../scripts/lib/legal-coverage.mjs | 33 ++++++++++++++++--- 2 files changed, 35 insertions(+), 5 deletions(-) diff --git a/products/legal/plugins/legal-coverage/hook.mjs b/products/legal/plugins/legal-coverage/hook.mjs index 06b33556..199f3a35 100644 --- a/products/legal/plugins/legal-coverage/hook.mjs +++ b/products/legal/plugins/legal-coverage/hook.mjs @@ -547,6 +547,13 @@ async function advanceRepairPreparation(workspaceRoot, sessionState, toolName, t if (!info.isFile() || info.size <= 0 || info.size > 24576) { return { ordinal, seenCheckpointDigests, changed: false }; } + const result = await validateWorkspace({ workspaceRoot, writeProof: false }); + const workItems = await dynamicWorkItems(workspaceRoot, result); + if (workItems?.group !== "source-fragment-repair-apply" + || workItems.repair?.validated !== true + || workItems.repair.path !== target.path) { + return { ordinal, seenCheckpointDigests, changed: false }; + } } const preparationDigest = checkpointDigest({ kind: `repair-target-${toolName === "write_file" ? "write" : "read"}`, diff --git a/products/legal/plugins/legal-coverage/scripts/lib/legal-coverage.mjs b/products/legal/plugins/legal-coverage/scripts/lib/legal-coverage.mjs index 86fa8e6b..7be0328c 100644 --- a/products/legal/plugins/legal-coverage/scripts/lib/legal-coverage.mjs +++ b/products/legal/plugins/legal-coverage/scripts/lib/legal-coverage.mjs @@ -649,6 +649,7 @@ function sourceMergeRepairPlanFor(rejectedPlan) { ].join("\0")).slice(0, 12); const repairPath = `${STATE_DIRECTORY}/fragments/source-repair-${digest}.json`; const appliedReceiptPath = `${STATE_DIRECTORY}/fragments/source-repair-applied-${digest}.json`; + const sourceContext = Array.isArray(repairSlice.sourceContext) ? repairSlice.sourceContext : []; const { preparedSlice: _preparedSlice, ...boundedPlan } = rejectedPlan; const proposal = { ...boundedPlan.proposal }; delete proposal.template; @@ -681,11 +682,7 @@ function sourceMergeRepairPlanFor(rejectedPlan) { proposalPath: rejectedPlan.proposal.path, proposalSha256: repairSlice.proposal.sha256, diagnosticSha256, - operations: rejectedFacts.map((item) => ({ - factNumber: item.factNumber, - action: "replace", - fact: item.fact, - })), + operations: rejectedFacts.map((item) => sourceMergeRepairTemplateOperationFor(item, sourceContext)), }, repairSlice: { schemaVersion: 1, @@ -697,6 +694,29 @@ function sourceMergeRepairPlanFor(rejectedPlan) { }; } +function sourceMergeRepairTemplateOperationFor(item, sourceContext) { + let fact = item.fact; + if (item.diagnosticCodes.includes("source_merge_fact_evidence_mismatch") + && isRecord(fact) && Array.isArray(fact.sourceRefs)) { + const evidenceBySourceId = new Map(sourceContext + .filter((source) => isRecord(source) && nonEmpty(source.sourceId) + && EVIDENCE_CLASSES.has(source.evidenceClass)) + .map((source) => [source.sourceId, source.evidenceClass])); + const allowedEvidenceClasses = [...new Set(fact.sourceRefs + .filter((reference) => isRecord(reference) && nonEmpty(reference.sourceId)) + .map((reference) => evidenceBySourceId.get(reference.sourceId)) + .filter((evidenceClass) => EVIDENCE_CLASSES.has(evidenceClass)))]; + if (allowedEvidenceClasses.length === 1) { + fact = { ...fact, evidenceClass: allowedEvidenceClasses[0] }; + } + } + return { + factNumber: item.factNumber, + action: "replace", + fact, + }; +} + function sourceMergeRepairApplyPlanFor(repairPlan, repairReceipt) { const { preparedSlice: _preparedSlice, ...boundedPlan } = repairPlan; const proposal = { ...boundedPlan.proposal }; @@ -971,6 +991,7 @@ function sourceMergeRepairSliceFor(patch, bytes, plan, receipt, validationDiagno if (!row) return []; return [{ sourceId, + evidenceClass: row.evidenceClass, allowedFragmentFacts: (Array.isArray(row.facts) ? row.facts : []) .filter((fact) => isRecord(fact) && nonEmpty(fact.locator) && nonEmpty(fact.statement)) .map((fact) => ({ locator: fact.locator, statement: fact.statement })) @@ -3922,6 +3943,8 @@ function nextActionFor( } return `Write exactly one new immutable source repair transaction to ${JSON.stringify(workItems.repair.path)} as the next tool call. ` + `Use workItems.repair.template and workItems.repair.repairSlice; cover every rejected fact exactly once with action replace or remove. ` + + `The template already applies validator-derived fields when the referenced fragment sources permit exactly one value; preserve those fields unless another listed diagnostic requires changing them. ` + + `For evidence-class diagnostics, use only the exact sourceContext evidenceClass for a referenced source instead of inferring a document type. ` + `For replace, provide the complete corrected fact. For remove, provide a specific reason and rely on the unchanged full validator to enforce source disposition. ` + `Do not read or overwrite the rejected proposal, readiness checkpoint, canonical ledgers, fragment, or raw sources. ` + `The Legal Plugin will combine the repair with every unchanged fact and validate it before exposing an apply command.`; From 54f4852f46d40c4ce6c6128b4851ce45f498f626 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E9=83=91=E8=BE=BE=E5=A5=87?= <> Date: Mon, 27 Jul 2026 09:48:11 +0800 Subject: [PATCH 2/4] test(legal): cover validated repair contract --- tests/products/legal-coverage.spec.ts | 36 +++++++++++++++++++++------ 1 file changed, 28 insertions(+), 8 deletions(-) diff --git a/tests/products/legal-coverage.spec.ts b/tests/products/legal-coverage.spec.ts index 8a58e66e..5a7b2638 100644 --- a/tests/products/legal-coverage.spec.ts +++ b/tests/products/legal-coverage.spec.ts @@ -594,7 +594,7 @@ test("legal coverage injects deterministic disjoint worker batches for large pen const invalidProposalBody = { ...proposalBase, facts: [ - { ...proposalBase.facts[0], sourceRefs: [{ sourceId: proposal.sourceIds[0], locator: "invented:99" }] }, + { ...proposalBase.facts[0], evidenceClass: "official-record" }, { ...proposalBase.facts[1], dateOrPeriod: "2026", @@ -623,7 +623,7 @@ test("legal coverage injects deterministic disjoint worker batches for large pen const invalidProposalContext = invalidProposal.hookSpecificOutput.additionalContext ?? ""; assert.match(invalidProposalContext, /"group": "source-fragment-repair"/u); assert.match(invalidProposalContext, /"mode": "main-agent-repair"/u); - assert.match(invalidProposalContext, /source_merge_fact_locator_unverified/u); + assert.match(invalidProposalContext, /source_merge_fact_evidence_mismatch/u); assert.match(invalidProposalContext, /source_merge_fact_time_invalid/u); const invalidProposalEnvelope = JSON.parse(invalidProposalContext .replace(/^\n/u, "") @@ -666,6 +666,7 @@ test("legal coverage injects deterministic disjoint worker batches for large pen }>; sourceContext: Array<{ sourceId: string; + evidenceClass: string; allowedFragmentFacts: Array<{ locator: string; statement: string }>; conflicts: string[]; unresolvedItems: string[]; @@ -681,7 +682,7 @@ test("legal coverage injects deterministic disjoint worker batches for large pen assert.deepEqual( immutableRepair.repairSlice.diagnostics.items.map((item) => item.code), [ - "source_merge_fact_locator_unverified", + "source_merge_fact_evidence_mismatch", "source_merge_fact_time_invalid", "source_merge_threshold_invalid", ], @@ -690,11 +691,13 @@ test("legal coverage injects deterministic disjoint worker batches for large pen immutableRepair.repairSlice.diagnostics.items.map((item) => item.factNumber), [1, 2, 2], ); - assert.match(immutableRepair.repairSlice.diagnostics.items[0]?.message ?? "", /Proposal fact 1 locator/u); + assert.match(immutableRepair.repairSlice.diagnostics.items[0]?.message ?? "", /Proposal fact 1 evidenceClass/u); assert.match(immutableRepair.repairSlice.diagnostics.items[1]?.message ?? "", /Proposal fact 2 requires exactly one/u); assert.match(immutableRepair.repairSlice.diagnostics.items[2]?.message ?? "", /Proposal fact 2 has an invalid thresholdAssessment/u); assert.match(invalidProposalEnvelope.nextAction, /Write exactly one new immutable source repair transaction/u); assert.match(invalidProposalEnvelope.nextAction, /Do not read or overwrite the rejected proposal/u); + assert.match(invalidProposalEnvelope.nextAction, /template already applies validator-derived fields/u); + assert.match(invalidProposalEnvelope.nextAction, /exact sourceContext evidenceClass/u); const invalidProposalBytes = await readFile(proposalPath); assert.equal(immutableRepair.proposalPath, proposal.path); assert.equal(immutableRepair.proposalSha256, sha256(invalidProposalBytes)); @@ -704,14 +707,16 @@ test("legal coverage injects deterministic disjoint worker batches for large pen assert.equal(immutableRepair.limits.maxSerializedBytes, 24576); assert.deepEqual(immutableRepair.repairSlice.rejectedFacts.map((item) => item.factNumber), [1, 2]); assert.deepEqual(immutableRepair.repairSlice.rejectedFacts[0]?.fact, invalidProposalBody.facts[0]); - assert.deepEqual(immutableRepair.repairSlice.rejectedFacts[0]?.diagnosticCodes, ["source_merge_fact_locator_unverified"]); + assert.deepEqual(immutableRepair.repairSlice.rejectedFacts[0]?.diagnosticCodes, ["source_merge_fact_evidence_mismatch"]); const firstRepairSource = immutableRepair.repairSlice.sourceContext .find((source) => source.sourceId === proposal.sourceIds[0]); + assert.equal(firstRepairSource?.evidenceClass, "other"); assert.deepEqual(firstRepairSource?.allowedFragmentFacts, [ { locator: "converted.txt:1", statement: `Reviewed ${proposal.sourceIds[0]}.` }, ]); assert.deepEqual(firstRepairSource?.conflicts, ["Synthetic conflict is preserved on the source ledger."]); assert.deepEqual(firstRepairSource?.unresolvedItems, ["Synthetic unresolved item is preserved on the source ledger."]); + assert.equal(immutableRepair.template.operations[0]?.fact.evidenceClass, "other"); assert.doesNotMatch(invalidProposalContext, /"currentProposal":/u); assert.doesNotMatch(invalidProposalContext, /"preparedSlice":/u); assert.doesNotMatch(invalidProposalContext, /"sourceFragmentCommand":/u); @@ -739,7 +744,7 @@ test("legal coverage injects deterministic disjoint worker batches for large pen "--proposal-sha256", invalidProposalHash, ); assert.equal(rejectedDirectApply.exitCode, 1); - assert.match(rejectedDirectApply.stderr, /source_merge_fact_locator_unverified/u); + assert.match(rejectedDirectApply.stderr, /source_merge_fact_evidence_mismatch/u); assert.doesNotMatch(rejectedDirectApply.stderr, /source_merge_threshold_invalid/u); const wrongRepairPath = join(workspace, STATE_ROOT, "fragments", "wrong-repair.json"); @@ -817,8 +822,7 @@ test("legal coverage injects deterministic disjoint worker batches for large pen factNumber: 1, action: "replace", fact: { - ...invalidProposalBody.facts[0], - sourceRefs: [{ sourceId: proposal.sourceIds[0], locator: "converted.txt:1" }], + ...immutableRepair.template.operations[0]?.fact, }, }, { @@ -937,6 +941,15 @@ test("legal coverage injects deterministic disjoint worker batches for large pen ]; for (const invalidRepair of invalidRepairs) { await writeJson(repairPath, invalidRepair.body); + await runHook({ + hookEventName: "PostToolUse", + sessionId: "large-pending-source-plan", + transcriptPath: "", + cwd: workspace, + toolName: "write_file", + toolInput: { file_path: immutableRepair.path }, + toolUseId: `invalid-repair-${invalidRepair.name}`, + }); const rejectedRepair = await runHook({ hookEventName: "PreModelRequest", sessionId: "large-pending-source-plan", @@ -946,6 +959,13 @@ test("legal coverage injects deterministic disjoint worker batches for large pen const rejectedRepairContext = rejectedRepair.hookSpecificOutput.additionalContext ?? ""; assert.match(rejectedRepairContext, invalidRepair.errorCode, invalidRepair.name); assert.doesNotMatch(rejectedRepairContext, /"sourceMergeRepairApplyCommand":/u, invalidRepair.name); + assert.equal( + (rejectedRepair.hookSpecificOutput.modelRequestPatch?.metadata?.pilotdeckConvergence as { + repairPreparationOrdinal: number; + }).repairPreparationOrdinal, + 0, + invalidRepair.name, + ); assert.deepEqual( await readFile(join(workspace, STATE_ROOT, "sources.json")), canonicalSourcesBeforeRepair, From 83b009e0405121fb866e40f608f8c745eb0bcc4c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E9=83=91=E8=BE=BE=E5=A5=87?= <> Date: Mon, 27 Jul 2026 09:48:11 +0800 Subject: [PATCH 3/4] docs(convergence): define V24R10 repair contract --- ...RGENCE_V24R10_VALIDATED_REPAIR_CONTRACT.md | 101 ++++++++++++++++++ 1 file changed, 101 insertions(+) create mode 100644 docs/PILOTDECK_CONVERGENCE_V24R10_VALIDATED_REPAIR_CONTRACT.md diff --git a/docs/PILOTDECK_CONVERGENCE_V24R10_VALIDATED_REPAIR_CONTRACT.md b/docs/PILOTDECK_CONVERGENCE_V24R10_VALIDATED_REPAIR_CONTRACT.md new file mode 100644 index 00000000..3d03f9be --- /dev/null +++ b/docs/PILOTDECK_CONVERGENCE_V24R10_VALIDATED_REPAIR_CONTRACT.md @@ -0,0 +1,101 @@ +# PilotDeck Convergence V24R10: Validated Repair Contract + +## Decision + +V24R10 fixes the V24R9 Case 09 product-gate failure before the ordinary source +apply stage. It changes only the Legal Coverage repair contract and repair +preparation accounting. It does not change Agent Core, O1, validator acceptance, +Progress Lease thresholds `8/2`, Router, Memory, the model, the corpus, source +apply receipts, matrix selection, authority closure, or completion authority. + +The failed run wrote the correct deterministic immutable repair path but the +transaction itself was invalid. A replacement fact inferred a plausible +document type instead of using the exact `evidenceClass` already validated on +the referenced worker fragment. The PostToolUse hook then counted any non-empty +file at the repair path as prepared, so an invalid transaction consumed the one +`repair_preparation_grace` even though no apply command existed. + +## Contract + +The source repair slice now exposes the exact `evidenceClass` from each +validated fragment source next to its existing exact locators, statements, +conflicts, and unresolved items. + +When a rejected fact has `source_merge_fact_evidence_mismatch`, the repair +template derives a corrected `evidenceClass` only if all referenced fragment +rows resolve to exactly one allowed class. This is a mechanical projection of +validated receipt metadata, not a new legal inference. If references permit +multiple classes or no validated class, the template preserves the rejected +fact and the Agent must decide from the bounded repair slice. + +The dynamic next action tells the Agent to preserve validator-derived template +fields and to use exact source-context classifications rather than infer a +document type. Existing diagnostics remain authoritative for every field that +cannot be derived uniquely, including semantic locator selection, time, +threshold, materiality, and removal. + +## Preparation Boundary + +A `write_file` event at the deterministic repair path is no longer sufficient +to advance `repairPreparationOrdinal`. + +After the write, Legal Coverage revalidates the current workspace and derives +the current bounded work item. Preparation advances only when all of the +following hold: + +- the file is the exact state-bound repair target; +- the file is non-empty and within the existing 24 KiB limit; +- the unchanged full repair validator accepts the complete transaction; +- the derived work item is `source-fragment-repair-apply`; +- its repair receipt is marked validated; and +- its path exactly matches the expected repair target. + +Only that state exposes `sourceMergeRepairApplyCommand` and earns the existing +one `repair_preparation_grace`. Invalid content, wrong paths, missing files, +overflow, reads, replay, and rejected repairs advance neither preparation nor +semantic progress. + +## Boundaries + +- Validator acceptance is unchanged. The template cannot make an invalid + transaction valid unless its projected field already comes from validated + fragment metadata. +- No canonical ledger is mutated during derivation or preparation. +- No extra repair attempt, grace kind, stagnant observation, or retry loop is + introduced. An invalid immutable repair still fails closed. +- Source-context additions remain inside the existing bounded repair slice and + 24 KiB transaction limits. +- Core continues to receive only opaque ordinals, counts, and hashes. It does + not learn legal source fields or transaction rules. + +## Counterexamples + +Tests must prove that: + +- a unique referenced fragment class is projected into the repair template; +- ambiguous or missing classes are not guessed; +- rejected facts and the original proposal remain unchanged; +- wrong-path, missing, empty, oversized, malformed, stale, duplicate, + out-of-scope, placeholder, invalid-locator, and otherwise invalid repair + writes do not advance `repairPreparationOrdinal`; +- a valid state-bound repair advances preparation exactly once and exposes the + exact apply command; +- apply writes the existing verified receipt, advances semantic progress once, + and replay advances zero; and +- source apply receipts, matrix/authority protocols, Lease behavior, O1, and + ordinary legal-task non-activation remain unchanged. + +## Verification Gate + +1. Run the focused Legal Coverage, real-Gateway, Progress Lease, Agent runtime, + and O1 suites. +2. Replay the exact preserved V24R9 Case 09 source failure in a disposable + workspace and prove validator-derived repair template, valid-only + preparation, exact apply, verified receipt, and one semantic renewal. +3. Run the complete repository suite and patch hygiene checks. +4. Record reviewer-readable QA evidence on disk, commit, push, and open a + stacked PR on V24R9. +5. Assemble a new immutable campaign and repeat Gate 0, paired smoke, Case 05, + and the complete Case 09 product gate. V25 and the 85-case campaign remain + blocked until Case 09 completes with validator success and a non-skeleton + report. From a61e360ef78637ce202027fd61b29e8efb9a8983 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E9=83=91=E8=BE=BE=E5=A5=87?= <> Date: Mon, 27 Jul 2026 09:48:11 +0800 Subject: [PATCH 4/4] test(legal): record V24R10 QA evidence --- .../QA.md | 129 ++ .../case09-replay-result.json | 21 + .../focused-legal-gateway.log | 521 +++++ .../full-suite.log | 1899 +++++++++++++++++ .../replay-preserved-case09.mjs | 201 ++ 5 files changed, 2771 insertions(+) create mode 100644 .omo/evidence/20260727-v24r10-validated-repair-contract/QA.md create mode 100644 .omo/evidence/20260727-v24r10-validated-repair-contract/case09-replay-result.json create mode 100644 .omo/evidence/20260727-v24r10-validated-repair-contract/focused-legal-gateway.log create mode 100644 .omo/evidence/20260727-v24r10-validated-repair-contract/full-suite.log create mode 100644 .omo/evidence/20260727-v24r10-validated-repair-contract/replay-preserved-case09.mjs diff --git a/.omo/evidence/20260727-v24r10-validated-repair-contract/QA.md b/.omo/evidence/20260727-v24r10-validated-repair-contract/QA.md new file mode 100644 index 00000000..931f9886 --- /dev/null +++ b/.omo/evidence/20260727-v24r10-validated-repair-contract/QA.md @@ -0,0 +1,129 @@ +# V24R10 Validated Repair Contract QA + +## Scope and boundary + +V24R10 changes only Legal Coverage source-repair guidance and preparation +accounting. It exposes validated fragment `evidenceClass` metadata, derives a +template correction only when the referenced sources permit exactly one value, +and requires a fully validated repair/apply work item before advancing +`repairPreparationOrdinal`. + +It does not change Agent Core, O1, validator acceptance, Progress Lease `8/2`, +Router, Memory, model configuration, corpus, ordinary source apply receipts, +matrix selection, authority closure, or completion authority. + +## Static and patch verification + +Commands: + +```sh +node --check products/legal/plugins/legal-coverage/hook.mjs +node --check products/legal/plugins/legal-coverage/scripts/legal-coverage.mjs +node --check products/legal/plugins/legal-coverage/scripts/lib/legal-coverage.mjs +node --check .omo/evidence/20260727-v24r10-validated-repair-contract/replay-preserved-case09.mjs +git diff --check +``` + +Observed: every command exited `0`; `git diff --check` emitted no diagnostics. + +Why enough: these checks cover parse validity and patch hygiene, but do not +substitute for behavioral tests. + +## Focused Legal Coverage and real Gateway suite + +Command: + +```sh +node --test --test-force-exit --test-timeout 60000 \ + dist/tests/products/legal-coverage.spec.js \ + dist/tests/agent/legal-coverage-plugin-runtime.spec.js \ + dist/tests/agent/progress-lease.spec.js \ + dist/tests/agent/agent-loop-runtime-controls.spec.js \ + dist/tests/observability/recorder.spec.js \ + dist/tests/observability/local-gateway-progress-handoff.spec.js +``` + +Observed: `84/84` passed with no failures, cancellations, skips, or todos. +Artifact: `focused-legal-gateway.log`, SHA-256 +`4841ba259bf33fb21261eda4a598207d18c0d5496fda3da919d4756af83756e1`. + +The Legal Coverage regression proves that a unique validated fragment class is +projected into the repair template, every tested invalid repair write keeps +`repairPreparationOrdinal` at zero, and a valid repair advances preparation +once. Existing counterexamples continue to cover missing/duplicate/unknown +operations, extra valid-fact changes, unsupported actions, placeholders, +missing removal reasons, changed diagnostic identity, invalid locators, +overflow, stale state, changed proposals, traversal, symlink ancestors, and +replay. + +The four real local Gateway cases cover actual hook subscription and tool +execution, immutable source repair through verified progress, authority +closure, Stop failure, Router/Memory-disabled isolation, and complete O1 +pairing with zero drops in the source-repair fixture. + +## Complete repository suite + +Command: + +```sh +npm test +``` + +Observed: build plus `308/308` tests passed with no failures, cancellations, +skips, or todos. Artifact: `full-suite.log`, SHA-256 +`6a66ff058ef158a5fc47ac66e5b42ae7b3a5db814529586f7c47e0f0fb37f613`. + +Why enough: the complete clean run covers every repository component against +the candidate, including Core Lease and compaction, Gateway, O1, ordinary +source receipts, Legal Coverage, matrices, authorities, and unrelated product +behavior. No non-Legal implementation file changed. + +## Preserved Case 09 failure replay + +The reviewer-readable driver `replay-preserved-case09.mjs` copies the exact +V24R9 failed execution workspace to a disposable directory and installs the +V24R10 Legal Coverage plugin. It never mutates the original campaign or +preserved workspace. + +It first replays PostToolUse against the original invalid immutable repair and +proves preparation remains zero. It then removes only that disposable copy, +derives the new state-bound repair template, preserves its mechanically +corrected exact fragment classifications, selects an exact locator from the +bounded repair context, writes the transaction, and drives the real hook and +CLI through apply and replay. + +Observed: + +```text +rejected repair: source-repair-43668ad6d287.json +applied receipt: source-repair-applied-43668ad6d287.json +invalid preparation: 0 +valid preparation: 1 +progressOrdinal: 0 -> 1 -> 1 +canonical mutation: 4 sources, 16 facts +post-apply source state: 4 reviewed, 20 pending +next work group: source-fragment-merge +``` + +Artifact: `case09-replay-result.json`, SHA-256 +`1f211ea08ae4cf2cdae7436778e407d020a1eb93f61a33df5cfbb106f76b499c`. + +Why enough: this is the exact proposal, repair identity, fragment metadata, and +canonical state from the failed product run. It proves the bug was corrected at +the real dynamic prompt/hook/CLI boundary: invalid bytes do not earn grace; +valid receipt-bound bytes expose the exact apply command; apply writes the +verified receipt and renews progress once; replay renews zero. + +## Omitted and residual risk + +No API key, provider URL, authorization header, environment dump, private +source content, report text, prompt text, raw repair content, or model reasoning +is included in this evidence directory. Logs contain only local deterministic +test output. + +The production-model Case 09 has not yet run on V24R10. Therefore this change +is code-, contract-, real-Gateway-, O1-, full-suite-, and exact-failure-replay +verified, but it is not yet a product Gate pass. V25 and the 85-case campaign +remain blocked until a fresh immutable campaign passes Gate 0, paired smoke, +Case 05, and the complete Case 09 gate with validator success, a current +completion proof, and a non-skeleton report. diff --git a/.omo/evidence/20260727-v24r10-validated-repair-contract/case09-replay-result.json b/.omo/evidence/20260727-v24r10-validated-repair-contract/case09-replay-result.json new file mode 100644 index 00000000..18090c09 --- /dev/null +++ b/.omo/evidence/20260727-v24r10-validated-repair-contract/case09-replay-result.json @@ -0,0 +1,21 @@ +{ + "passed": true, + "fixture": "preserved-v24r9-case09-invalid-repair", + "rejectedRepair": "source-repair-43668ad6d287.json", + "appliedReceipt": "source-repair-applied-43668ad6d287.json", + "invalidRepairPreparationOrdinal": 0, + "validRepairPreparationOrdinal": 1, + "progressOrdinal": [ + 0, + 1, + 1 + ], + "appliedSourceCount": 4, + "appliedFactCount": 16, + "reviewedSources": 4, + "pendingSources": 20, + "totalFacts": 16, + "stateHashAfter": "e6d480e622d25dad082ad758a9676220b091d79aba5c44e6e840eba2705f5039", + "repairSha256": "1ba2abb4f789eff5841fe7dc345ad5c22506a61c30ddfc57d697396ececb7fd7", + "nextGroup": "source-fragment-merge" +} diff --git a/.omo/evidence/20260727-v24r10-validated-repair-contract/focused-legal-gateway.log b/.omo/evidence/20260727-v24r10-validated-repair-contract/focused-legal-gateway.log new file mode 100644 index 00000000..89eaae56 --- /dev/null +++ b/.omo/evidence/20260727-v24r10-validated-repair-contract/focused-legal-gateway.log @@ -0,0 +1,521 @@ +TAP version 13 +# (node:6507) ExperimentalWarning: SQLite is an experimental feature and might change at any time +# (Use `node --trace-warnings ...` to show where the warning was created) +# Subtest: PreModelRequest mutations survive a post-routing request rebuild and remain model-only +ok 1 - PreModelRequest mutations survive a post-routing request rebuild and remain model-only + --- + duration_ms: 13.94375 + type: 'test' + ... +# Subtest: AgentLoop exposes explicit subagent identity to lifecycle hooks +ok 2 - AgentLoop exposes explicit subagent identity to lifecycle hooks + --- + duration_ms: 0.393333 + type: 'test' + ... +# Subtest: evaluation progress lease stops before a third unchanged model request when full compaction is rejected +ok 3 - evaluation progress lease stops before a third unchanged model request when full compaction is rejected + --- + duration_ms: 2.079791 + type: 'test' + ... +# Subtest: AgentLoop delivers newly surfaced repair feedback once after an applied boundary +ok 4 - AgentLoop delivers newly surfaced repair feedback once after an applied boundary + --- + duration_ms: 2.730042 + type: 'test' + ... +# Subtest: AgentLoop permits one prepared-target request and then requires genuine progress +ok 5 - AgentLoop permits one prepared-target request and then requires genuine progress + --- + duration_ms: 3.447917 + type: 'test' + ... +# Subtest: AgentLoop carries a bounded handoff through the required boundary to genuine progress +ok 6 - AgentLoop carries a bounded handoff through the required boundary to genuine progress + --- + duration_ms: 1.849125 + type: 'test' + ... +# Subtest: artifact failure injects one bounded correction turn and succeeds after validation +ok 7 - artifact failure injects one bounded correction turn and succeeds after validation + --- + duration_ms: 6.667834 + type: 'test' + ... +# Subtest: required artifact failure wins over max-turn completion after a final tool call +ok 8 - required artifact failure wins over max-turn completion after a final tool call + --- + duration_ms: 1.138 + type: 'test' + ... +# (node:6508) ExperimentalWarning: SQLite is an experimental feature and might change at any time +# (Use `node --trace-warnings ...` to show where the warning was created) +# Subtest: real gateway drives legal plugin milestones through bounded artifact correction +ok 9 - real gateway drives legal plugin milestones through bounded artifact correction + --- + duration_ms: 846.141459 + type: 'test' + ... +# Subtest: real gateway blocks completion when the legal Stop hook cannot read session state +ok 10 - real gateway blocks completion when the legal Stop hook cannot read session state + --- + duration_ms: 496.709542 + type: 'test' + ... +# Subtest: real gateway executes the state-bound legal authority closure with complete O1 evidence +ok 11 - real gateway executes the state-bound legal authority closure with complete O1 evidence + --- + duration_ms: 567.518791 + type: 'test' + ... +# Subtest: real gateway applies an immutable source repair before renewing legal progress +ok 12 - real gateway applies an immutable source repair before renewing legal progress + --- + duration_ms: 764.821458 + type: 'test' + ... +# Subtest: progress lease stays inert unless explicitly enabled +ok 13 - progress lease stays inert unless explicitly enabled + --- + duration_ms: 0.663333 + type: 'test' + ... +# Subtest: opaque hash churn is stagnant while a smaller remaining count renews the lease +ok 14 - opaque hash churn is stagnant while a smaller remaining count renews the lease + --- + duration_ms: 0.135541 + type: 'test' + ... +# Subtest: only a strictly increasing domain progress ordinal renews the lease +ok 15 - only a strictly increasing domain progress ordinal renews the lease + --- + duration_ms: 0.220166 + type: 'test' + ... +# Subtest: a lower remaining count cannot roll the stored progress ordinal backward +ok 16 - a lower remaining count cannot roll the stored progress ordinal backward + --- + duration_ms: 0.20975 + type: 'test' + ... +# Subtest: two stagnant observations require a boundary and allow exactly one post-boundary turn +ok 17 - two stagnant observations require a boundary and allow exactly one post-boundary turn + --- + duration_ms: 0.084959 + type: 'test' + ... +# Subtest: new repair feedback after a boundary gets one delivery turn without renewing progress +ok 18 - new repair feedback after a boundary gets one delivery turn without renewing progress + --- + duration_ms: 0.190042 + type: 'test' + ... +# Subtest: genuine progress after repair feedback renews the lease +ok 19 - genuine progress after repair feedback renews the lease + --- + duration_ms: 0.050375 + type: 'test' + ... +# Subtest: a newly prepared repair target gets one non-progress request after feedback +ok 20 - a newly prepared repair target gets one non-progress request after feedback + --- + duration_ms: 0.054625 + type: 'test' + ... +# Subtest: genuine progress immediately after repair preparation renews the lease +ok 21 - genuine progress immediately after repair preparation renews the lease + --- + duration_ms: 0.179 + type: 'test' + ... +# Subtest: preparation observed before feedback cannot be replayed after the boundary +ok 22 - preparation observed before feedback cannot be replayed after the boundary + --- + duration_ms: 0.236125 + type: 'test' + ... +# Subtest: a second repair revision cannot replace missing progress after feedback +ok 23 - a second repair revision cannot replace missing progress after feedback + --- + duration_ms: 0.075208 + type: 'test' + ... +# Subtest: repair feedback already delivered before a boundary cannot be replayed as grace +ok 24 - repair feedback already delivered before a boundary cannot be replayed as grace + --- + duration_ms: 0.033917 + type: 'test' + ... +# Subtest: a new operational handoff gets one bounded request without renewing progress +ok 25 - a new operational handoff gets one bounded request without renewing progress + --- + duration_ms: 0.052875 + type: 'test' + ... +# Subtest: a post-boundary handoff is bounded and its replay still fails closed +ok 26 - a post-boundary handoff is bounded and its replay still fails closed + --- + duration_ms: 0.03075 + type: 'test' + ... +# Subtest: simultaneous repair and handoff revisions cannot be redeemed on separate turns +ok 27 - simultaneous repair and handoff revisions cannot be redeemed on separate turns + --- + duration_ms: 0.035125 + type: 'test' + ... +# Subtest: a lower handoff ordinal cannot be replayed as grace +ok 28 - a lower handoff ordinal cannot be replayed as grace + --- + duration_ms: 0.02375 + type: 'test' + ... +# Subtest: handoff allowance has a hard per-progress-epoch limit and resets only on progress +ok 29 - handoff allowance has a hard per-progress-epoch limit and resets only on progress + --- + duration_ms: 0.031583 + type: 'test' + ... +# Subtest: handoff cannot bypass an unavailable required boundary +ok 30 - handoff cannot bypass an unavailable required boundary + --- + duration_ms: 0.025708 + type: 'test' + ... +# Subtest: sanitized Case 09 matrix handoff trajectory reaches the next semantic checkpoint +ok 31 - sanitized Case 09 matrix handoff trajectory reaches the next semantic checkpoint + --- + duration_ms: 0.801917 + type: 'test' + ... +# Subtest: cold-start allowance expires after the first explicit domain progress +ok 32 - cold-start allowance expires after the first explicit domain progress + --- + duration_ms: 0.045208 + type: 'test' + ... +# Subtest: evaluation mode fails closed when the required boundary is rejected or unavailable +ok 33 - evaluation mode fails closed when the required boundary is rejected or unavailable + --- + duration_ms: 0.034042 + type: 'test' + ... +# Subtest: a completed report releases the tracked scope +ok 34 - a completed report releases the tracked scope + --- + duration_ms: 0.028625 + type: 'test' + ... +# Subtest: convergence metadata parser rejects malformed and oversized reports +ok 35 - convergence metadata parser rejects malformed and oversized reports + --- + duration_ms: 0.09125 + type: 'test' + ... +# (node:6510) ExperimentalWarning: SQLite is an experimental feature and might change at any time +# (Use `node --trace-warnings ...` to show where the warning was created) +# [session-title] generation skipped (invalid_json): Unexpected token 'B', "Bounded co"... is not valid JSON +# Subtest: local Gateway records a bounded handoff through boundary to progress +ok 36 - local Gateway records a bounded handoff through boundary to progress + --- + duration_ms: 764.074709 + type: 'test' + ... +# Subtest: subagent tool detection and result pair at the model projection layer +ok 37 - subagent tool detection and result pair at the model projection layer + --- + duration_ms: 11.180875 + type: 'test' + ... +# Subtest: main tool detection and result produce one exact O1 pair +ok 38 - main tool detection and result produce one exact O1 pair + --- + duration_ms: 0.605291 + type: 'test' + ... +# Subtest: repair preparation grace is observable without becoming progress +ok 39 - repair preparation grace is observable without becoming progress + --- + duration_ms: 0.076334 + type: 'test' + ... +# Subtest: recorder finalizes a complete hash-only trajectory +ok 40 - recorder finalizes a complete hash-only trajectory + --- + duration_ms: 8.613875 + type: 'test' + ... +# Subtest: queue overflow is explicit and makes integrity partial +ok 41 - queue overflow is explicit and makes integrity partial + --- + duration_ms: 5.330166 + type: 'test' + ... +# Subtest: verifier rejects reused model request identity and duplicate terminals +ok 42 - verifier rejects reused model request identity and duplicate terminals + --- + duration_ms: 2.886459 + type: 'test' + ... +# Subtest: verifier rejects duplicate identity and secret-bearing keys +ok 43 - verifier rejects duplicate identity and secret-bearing keys + --- + duration_ms: 0.273584 + type: 'test' + ... +# Subtest: legal coverage validator creates a current proof and removes it when the deliverable changes +ok 44 - legal coverage validator creates a current proof and removes it when the deliverable changes + --- + duration_ms: 320.306708 + type: 'test' + ... +# Subtest: legal coverage initializer creates a text skeleton before source review and remains idempotent +ok 45 - legal coverage initializer creates a text skeleton before source review and remains idempotent + --- + duration_ms: 235.411833 + type: 'test' + ... +# Subtest: legal coverage initializer binds trusted manifests to original inputs +ok 46 - legal coverage initializer binds trusted manifests to original inputs + --- + duration_ms: 278.616625 + type: 'test' + ... +# Subtest: legal coverage source bootstrap creates only deterministic pending manifest rows +ok 47 - legal coverage source bootstrap creates only deterministic pending manifest rows + --- + duration_ms: 359.460709 + type: 'test' + ... +# Subtest: legal coverage injects deterministic disjoint worker batches for large pending source rooms +ok 48 - legal coverage injects deterministic disjoint worker batches for large pending source rooms + --- + duration_ms: 4562.413542 + type: 'test' + ... +# Subtest: legal coverage CLI exposes bundled guidance through stable named references +ok 49 - legal coverage CLI exposes bundled guidance through stable named references + --- + duration_ms: 144.824792 + type: 'test' + ... +# Subtest: legal coverage initializer creates only explicit text formats and preserves existing content +ok 50 - legal coverage initializer creates only explicit text formats and preserves existing content + --- + duration_ms: 55.316708 + type: 'test' + ... +# Subtest: legal coverage initializer rejects traversal and symlink ancestors without external writes +ok 51 - legal coverage initializer rejects traversal and symlink ancestors without external writes + --- + duration_ms: 152.623708 + type: 'test' + ... +# Subtest: legal coverage validator binds runner originals and derivations into its proof +ok 52 - legal coverage validator binds runner originals and derivations into its proof + --- + duration_ms: 213.998667 + type: 'test' + ... +# Subtest: legal coverage validator rejects missing lineage and mutable or derived input roots +ok 53 - legal coverage validator rejects missing lineage and mutable or derived input roots + --- + duration_ms: 272.209792 + type: 'test' + ... +# Subtest: legal coverage next-batch exposes one bounded deterministic repair slice +ok 54 - legal coverage next-batch exposes one bounded deterministic repair slice + --- + duration_ms: 249.976416 + type: 'test' + ... +# Subtest: legal coverage apply-batch atomically updates only the current bounded slice +ok 55 - legal coverage apply-batch atomically updates only the current bounded slice + --- + duration_ms: 316.603709 + type: 'test' + ... +# Subtest: legal coverage apply-batch rejects record and byte limit violations before writing +ok 56 - legal coverage apply-batch rejects record and byte limit violations before writing + --- + duration_ms: 202.115125 + type: 'test' + ... +# Subtest: legal coverage validator rejects orphaned conflicts and incomplete final disclosure +ok 57 - legal coverage validator rejects orphaned conflicts and incomplete final disclosure + --- + duration_ms: 103.645625 + type: 'test' + ... +# Subtest: legal coverage validator detects un-inventoried sources and cross-fact timeline collisions +ok 58 - legal coverage validator detects un-inventoried sources and cross-fact timeline collisions + --- + duration_ms: 112.754209 + type: 'test' + ... +# Subtest: legal coverage validator rejects reused generic quotes and unsupported fact coverage +ok 59 - legal coverage validator rejects reused generic quotes and unsupported fact coverage + --- + duration_ms: 109.987917 + type: 'test' + ... +# Subtest: legal coverage validator requires authority links for critical issues and legal-authority matrices +ok 60 - legal coverage validator requires authority links for critical issues and legal-authority matrices + --- + duration_ms: 108.556458 + type: 'test' + ... +# Subtest: legal coverage validator rejects non-object canonical JSON documents +ok 61 - legal coverage validator rejects non-object canonical JSON documents + --- + duration_ms: 714.131042 + type: 'test' + ... +# Subtest: legal coverage validator binds reviewed source bytes to the ledger and state hash +ok 62 - legal coverage validator binds reviewed source bytes to the ledger and state hash + --- + duration_ms: 210.066125 + type: 'test' + ... +# Subtest: legal coverage validator rejects ancestor symlinks and a symlinked proof +ok 63 - legal coverage validator rejects ancestor symlinks and a symlinked proof + --- + duration_ms: 157.719209 + type: 'test' + ... +# Subtest: legal coverage validator does not read or write through a symlinked state ancestor +ok 64 - legal coverage validator does not read or write through a symlinked state ancestor + --- + duration_ms: 102.250042 + type: 'test' + ... +# Subtest: legal coverage validator requires unresolved disclosure for unverified material facts +ok 65 - legal coverage validator requires unresolved disclosure for unverified material facts + --- + duration_ms: 105.497166 + type: 'test' + ... +# Subtest: legal coverage validator rejects unique but irrelevant issue and authority quotes +ok 66 - legal coverage validator rejects unique but irrelevant issue and authority quotes + --- + duration_ms: 109.053917 + type: 'test' + ... +# Subtest: legal coverage validator enforces reciprocal and same-entry ledger relationships +ok 67 - legal coverage validator enforces reciprocal and same-entry ledger relationships + --- + duration_ms: 107.266917 + type: 'test' + ... +# Subtest: legal coverage validator accepts null for an optional threshold assessment +ok 68 - legal coverage validator accepts null for an optional threshold assessment + --- + duration_ms: 145.611334 + type: 'test' + ... +# Subtest: legal coverage validator uses locators without quotes for binary deliverables +ok 69 - legal coverage validator uses locators without quotes for binary deliverables + --- + duration_ms: 119.5355 + type: 'test' + ... +# Subtest: legal coverage validator rejects empty-fact shortcuts and material facts outside matrices +ok 70 - legal coverage validator rejects empty-fact shortcuts and material facts outside matrices + --- + duration_ms: 210.681 + type: 'test' + ... +# Subtest: legal coverage hook activates only legal work and injects one observable milestone +ok 71 - legal coverage hook activates only legal work and injects one observable milestone + --- + duration_ms: 540.437583 + type: 'test' + ... +# Subtest: legal coverage hook groups repeated validator errors into one bounded milestone +ok 72 - legal coverage hook groups repeated validator errors into one bounded milestone + --- + duration_ms: 534.876792 + type: 'test' + ... +# Subtest: legal coverage pending-matrix selection is bounded across pages and invalid revisions cannot manufacture progress +ok 73 - legal coverage pending-matrix selection is bounded across pages and invalid revisions cannot manufacture progress + --- + duration_ms: 981.863 + type: 'test' + ... +# Subtest: legal coverage progress ordinal advances once for a new legal phase +ok 74 - legal coverage progress ordinal advances once for a new legal phase + --- + duration_ms: 217.120375 + type: 'test' + ... +# Subtest: legal coverage permits not-applicable only after exhaustive selection and rejects stale or changed matrix proposals +ok 75 - legal coverage permits not-applicable only after exhaustive selection and rejects stale or changed matrix proposals + --- + duration_ms: 480.385333 + type: 'test' + ... +# Subtest: legal coverage rejects oversized matrix evidence before making a selection receipt immutable +ok 76 - legal coverage rejects oversized matrix evidence before making a selection receipt immutable + --- + duration_ms: 167.615375 + type: 'test' + ... +# Subtest: legal coverage fails closed when one matrix index item exceeds the bounded page +ok 77 - legal coverage fails closed when one matrix index item exceeds the bounded page + --- + duration_ms: 165.253666 + type: 'test' + ... +# Subtest: legal coverage injects a bounded deterministic matrix relation-closure batch +ok 78 - legal coverage injects a bounded deterministic matrix relation-closure batch + --- + duration_ms: 222.000458 + type: 'test' + ... +# Subtest: legal coverage authority closure uses one bounded state-bound transaction +ok 79 - legal coverage authority closure uses one bounded state-bound transaction + --- + duration_ms: 635.979541 + type: 'test' + ... +# Subtest: legal coverage rejects changed and symlinked authority closure proposals without ledger mutation +ok 80 - legal coverage rejects changed and symlinked authority closure proposals without ledger mutation + --- + duration_ms: 261.14425 + type: 'test' + ... +# Subtest: legal coverage milestone digest ignores opaque incomplete-state hash churn +ok 81 - legal coverage milestone digest ignores opaque incomplete-state hash churn + --- + duration_ms: 0.242375 + type: 'test' + ... +# Subtest: legal coverage hook activates a malformed configured workspace so the agent can repair it +ok 82 - legal coverage hook activates a malformed configured workspace so the agent can repair it + --- + duration_ms: 107.915458 + type: 'test' + ... +# Subtest: legal coverage hook rejects a symlinked session-state ancestor without writing outside the workspace +ok 83 - legal coverage hook rejects a symlinked session-state ancestor without writing outside the workspace + --- + duration_ms: 55.397875 + type: 'test' + ... +# Subtest: legal product plugin loads one skill and contains no benchmark-specific controls +ok 84 - legal product plugin loads one skill and contains no benchmark-specific controls + --- + duration_ms: 3.07575 + type: 'test' + ... +1..84 +# tests 84 +# suites 0 +# pass 84 +# fail 0 +# cancelled 0 +# skipped 0 +# todo 0 +# duration_ms 14247.322167 diff --git a/.omo/evidence/20260727-v24r10-validated-repair-contract/full-suite.log b/.omo/evidence/20260727-v24r10-validated-repair-contract/full-suite.log new file mode 100644 index 00000000..5761e331 --- /dev/null +++ b/.omo/evidence/20260727-v24r10-validated-repair-contract/full-suite.log @@ -0,0 +1,1899 @@ + +> pilotdeck@0.1.0 test +> npm run build && node --test --test-force-exit --test-timeout 60000 "dist/tests/**/*.test.js" "dist/tests/**/*.spec.js" + + +> pilotdeck@0.1.0 prebuild +> npm run check:runtime && node scripts/bootstrap-pilotdeck-config.mjs && cd src/context/memory/edgeclaw-memory-core && npm run build + + +> pilotdeck@0.1.0 check:runtime +> node scripts/check-node-runtime.mjs + + +> edgeclaw-memory-core@0.0.0 build +> node -e "require('fs').rmSync('lib',{recursive:true,force:true})" && tsc -p tsconfig.json + + +> pilotdeck@0.1.0 build +> node -e "require('fs').rmSync('dist',{recursive:true,force:true})" && tsc -p tsconfig.json && node -e "require('fs').cpSync('src/extension/plugins/builtin','dist/src/extension/plugins/builtin',{recursive:true})" + +TAP version 13 +# Subtest: Feishu handles permission replies before the active chat drain finishes +ok 1 - Feishu handles permission replies before the active chat drain finishes + --- + duration_ms: 1.362125 + type: 'test' + ... +# Subtest: ImPermissionHelper resolves all pending permission requests for a chat +ok 2 - ImPermissionHelper resolves all pending permission requests for a chat + --- + duration_ms: 4.20575 + type: 'test' + ... +# Subtest: ImPermissionHelper keeps pending requests when the reply is invalid +ok 3 - ImPermissionHelper keeps pending requests when the reply is invalid + --- + duration_ms: 1.651334 + type: 'test' + ... +# (node:7163) ExperimentalWarning: SQLite is an experimental feature and might change at any time +# (Use `node --trace-warnings ...` to show where the warning was created) +# Subtest: PreModelRequest mutations survive a post-routing request rebuild and remain model-only +ok 4 - PreModelRequest mutations survive a post-routing request rebuild and remain model-only + --- + duration_ms: 8.87425 + type: 'test' + ... +# Subtest: AgentLoop exposes explicit subagent identity to lifecycle hooks +ok 5 - AgentLoop exposes explicit subagent identity to lifecycle hooks + --- + duration_ms: 0.738084 + type: 'test' + ... +# Subtest: evaluation progress lease stops before a third unchanged model request when full compaction is rejected +ok 6 - evaluation progress lease stops before a third unchanged model request when full compaction is rejected + --- + duration_ms: 4.597875 + type: 'test' + ... +# Subtest: AgentLoop delivers newly surfaced repair feedback once after an applied boundary +ok 7 - AgentLoop delivers newly surfaced repair feedback once after an applied boundary + --- + duration_ms: 5.319417 + type: 'test' + ... +# Subtest: AgentLoop permits one prepared-target request and then requires genuine progress +ok 8 - AgentLoop permits one prepared-target request and then requires genuine progress + --- + duration_ms: 6.213292 + type: 'test' + ... +# Subtest: AgentLoop carries a bounded handoff through the required boundary to genuine progress +ok 9 - AgentLoop carries a bounded handoff through the required boundary to genuine progress + --- + duration_ms: 2.68325 + type: 'test' + ... +# Subtest: artifact failure injects one bounded correction turn and succeeds after validation +ok 10 - artifact failure injects one bounded correction turn and succeeds after validation + --- + duration_ms: 17.887583 + type: 'test' + ... +# Subtest: required artifact failure wins over max-turn completion after a final tool call +ok 11 - required artifact failure wins over max-turn completion after a final tool call + --- + duration_ms: 4.799375 + type: 'test' + ... +# Subtest: AgentSession restores terminal state and dispatches SessionEnd when the runner throws +ok 12 - AgentSession restores terminal state and dispatches SessionEnd when the runner throws + --- + duration_ms: 3.3405 + type: 'test' + ... +# Subtest: AgentSession cleanup runs when a consumer stops reading early +ok 13 - AgentSession cleanup runs when a consumer stops reading early + --- + duration_ms: 0.242583 + type: 'test' + ... +# (node:7165) ExperimentalWarning: SQLite is an experimental feature and might change at any time +# (Use `node --trace-warnings ...` to show where the warning was created) +# Subtest: real gateway drives legal plugin milestones through bounded artifact correction +ok 14 - real gateway drives legal plugin milestones through bounded artifact correction + --- + duration_ms: 1528.214 + type: 'test' + ... +# Subtest: real gateway blocks completion when the legal Stop hook cannot read session state +ok 15 - real gateway blocks completion when the legal Stop hook cannot read session state + --- + duration_ms: 855.972666 + type: 'test' + ... +# Subtest: real gateway executes the state-bound legal authority closure with complete O1 evidence +ok 16 - real gateway executes the state-bound legal authority closure with complete O1 evidence + --- + duration_ms: 794.029625 + type: 'test' + ... +# Subtest: real gateway applies an immutable source repair before renewing legal progress +ok 17 - real gateway applies an immutable source repair before renewing legal progress + --- + duration_ms: 888.399959 + type: 'test' + ... +# (node:7166) ExperimentalWarning: SQLite is an experimental feature and might change at any time +# (Use `node --trace-warnings ...` to show where the warning was created) +# Subtest: local gateway applies project hook context and enforces artifact correction +ok 18 - local gateway applies project hook context and enforces artifact correction + --- + duration_ms: 1050.790416 + type: 'test' + ... +# Subtest: ordinary edit failures after a successful write do not enter large-file repair +ok 19 - ordinary edit failures after a successful write do not enter large-file repair + --- + duration_ms: 0.813417 + type: 'test' + ... +# Subtest: an explicit post-draft large-file failure starts bounded recovery +ok 20 - an explicit post-draft large-file failure starts bounded recovery + --- + duration_ms: 0.185 + type: 'test' + ... +# Subtest: successful focused write clears an active large-file repair episode +ok 21 - successful focused write clears an active large-file repair episode + --- + duration_ms: 0.14675 + type: 'test' + ... +# Subtest: permission failures are never reclassified as large-file recovery +ok 22 - permission failures are never reclassified as large-file recovery + --- + duration_ms: 0.045708 + type: 'test' + ... +# Subtest: progress lease stays inert unless explicitly enabled +ok 23 - progress lease stays inert unless explicitly enabled + --- + duration_ms: 1.376208 + type: 'test' + ... +# Subtest: opaque hash churn is stagnant while a smaller remaining count renews the lease +ok 24 - opaque hash churn is stagnant while a smaller remaining count renews the lease + --- + duration_ms: 0.483167 + type: 'test' + ... +# Subtest: only a strictly increasing domain progress ordinal renews the lease +ok 25 - only a strictly increasing domain progress ordinal renews the lease + --- + duration_ms: 0.085459 + type: 'test' + ... +# Subtest: a lower remaining count cannot roll the stored progress ordinal backward +ok 26 - a lower remaining count cannot roll the stored progress ordinal backward + --- + duration_ms: 0.099083 + type: 'test' + ... +# Subtest: two stagnant observations require a boundary and allow exactly one post-boundary turn +ok 27 - two stagnant observations require a boundary and allow exactly one post-boundary turn + --- + duration_ms: 0.060625 + type: 'test' + ... +# Subtest: new repair feedback after a boundary gets one delivery turn without renewing progress +ok 28 - new repair feedback after a boundary gets one delivery turn without renewing progress + --- + duration_ms: 0.15775 + type: 'test' + ... +# Subtest: genuine progress after repair feedback renews the lease +ok 29 - genuine progress after repair feedback renews the lease + --- + duration_ms: 0.048041 + type: 'test' + ... +# Subtest: a newly prepared repair target gets one non-progress request after feedback +ok 30 - a newly prepared repair target gets one non-progress request after feedback + --- + duration_ms: 0.051583 + type: 'test' + ... +# Subtest: genuine progress immediately after repair preparation renews the lease +ok 31 - genuine progress immediately after repair preparation renews the lease + --- + duration_ms: 0.169375 + type: 'test' + ... +# Subtest: preparation observed before feedback cannot be replayed after the boundary +ok 32 - preparation observed before feedback cannot be replayed after the boundary + --- + duration_ms: 0.247791 + type: 'test' + ... +# Subtest: a second repair revision cannot replace missing progress after feedback +ok 33 - a second repair revision cannot replace missing progress after feedback + --- + duration_ms: 0.074333 + type: 'test' + ... +# Subtest: repair feedback already delivered before a boundary cannot be replayed as grace +ok 34 - repair feedback already delivered before a boundary cannot be replayed as grace + --- + duration_ms: 0.033833 + type: 'test' + ... +# Subtest: a new operational handoff gets one bounded request without renewing progress +ok 35 - a new operational handoff gets one bounded request without renewing progress + --- + duration_ms: 0.052792 + type: 'test' + ... +# Subtest: a post-boundary handoff is bounded and its replay still fails closed +ok 36 - a post-boundary handoff is bounded and its replay still fails closed + --- + duration_ms: 0.030834 + type: 'test' + ... +# Subtest: simultaneous repair and handoff revisions cannot be redeemed on separate turns +ok 37 - simultaneous repair and handoff revisions cannot be redeemed on separate turns + --- + duration_ms: 0.03475 + type: 'test' + ... +# Subtest: a lower handoff ordinal cannot be replayed as grace +ok 38 - a lower handoff ordinal cannot be replayed as grace + --- + duration_ms: 0.023084 + type: 'test' + ... +# Subtest: handoff allowance has a hard per-progress-epoch limit and resets only on progress +ok 39 - handoff allowance has a hard per-progress-epoch limit and resets only on progress + --- + duration_ms: 0.030791 + type: 'test' + ... +# Subtest: handoff cannot bypass an unavailable required boundary +ok 40 - handoff cannot bypass an unavailable required boundary + --- + duration_ms: 0.024083 + type: 'test' + ... +# Subtest: sanitized Case 09 matrix handoff trajectory reaches the next semantic checkpoint +ok 41 - sanitized Case 09 matrix handoff trajectory reaches the next semantic checkpoint + --- + duration_ms: 0.861833 + type: 'test' + ... +# Subtest: cold-start allowance expires after the first explicit domain progress +ok 42 - cold-start allowance expires after the first explicit domain progress + --- + duration_ms: 0.063459 + type: 'test' + ... +# Subtest: evaluation mode fails closed when the required boundary is rejected or unavailable +ok 43 - evaluation mode fails closed when the required boundary is rejected or unavailable + --- + duration_ms: 0.046958 + type: 'test' + ... +# Subtest: a completed report releases the tracked scope +ok 44 - a completed report releases the tracked scope + --- + duration_ms: 0.036208 + type: 'test' + ... +# Subtest: convergence metadata parser rejects malformed and oversized reports +ok 45 - convergence metadata parser rejects malformed and oversized reports + --- + duration_ms: 0.096791 + type: 'test' + ... +# Subtest: explore subagent does not probe tool safety before execution +ok 46 - explore subagent does not probe tool safety before execution + --- + duration_ms: 15.516334 + type: 'test' + ... +# Subtest: explore registry ignores an unallowed dynamic execute_code tool without probing it +ok 47 - explore registry ignores an unallowed dynamic execute_code tool without probing it + --- + duration_ms: 0.428542 + type: 'test' + ... +# Subtest: read-only subagent evaluates bash safety from the real command +ok 48 - read-only subagent evaluates bash safety from the real command + --- + duration_ms: 3.776333 + type: 'test' + ... +# Subtest: read-only execute_code checks the real code instead of crashing on registry setup +ok 49 - read-only execute_code checks the real code instead of crashing on registry setup + --- + duration_ms: 0.469959 + type: 'test' + ... +# Subtest: subagent budget defaults to ten minutes without a parent deadline +ok 50 - subagent budget defaults to ten minutes without a parent deadline + --- + duration_ms: 2.702417 + type: 'test' + ... +# Subtest: subagent budget preserves the parent handoff window +ok 51 - subagent budget preserves the parent handoff window + --- + duration_ms: 0.236375 + type: 'test' + ... +# Subtest: subagent budget directive exposes the effective bound and diminishing-return rule +ok 52 - subagent budget directive exposes the effective bound and diminishing-return rule + --- + duration_ms: 0.177333 + type: 'test' + ... +# Subtest: composed subagent timeout aborts with a typed reason +ok 53 - composed subagent timeout aborts with a typed reason + --- + duration_ms: 11.074042 + type: 'test' + ... +# Subtest: subagent operation returns control even when the callee ignores abort +ok 54 - subagent operation returns control even when the callee ignores abort + --- + duration_ms: 9.244083 + type: 'test' + ... +# Subtest: parent abort wins without being misclassified as a child timeout +ok 55 - parent abort wins without being misclassified as a child timeout + --- + duration_ms: 0.426084 + type: 'test' + ... +# Subtest: filterIncompleteToolCalls tolerates malformed messages without content +ok 56 - filterIncompleteToolCalls tolerates malformed messages without content + --- + duration_ms: 1.916917 + type: 'test' + ... +# Subtest: fork timeout closes child lifecycle and lets the parent finish +ok 57 - fork timeout closes child lifecycle and lets the parent finish + --- + duration_ms: 36.5445 + type: 'test' + ... +# Subtest: turn environment provides an isolated PilotDeck-owned work directory +ok 58 - turn environment provides an isolated PilotDeck-owned work directory + --- + duration_ms: 1.372125 + type: 'test' + ... +# Subtest: turn environment inherits the process environment when no override is configured +ok 59 - turn environment inherits the process environment when no override is configured + --- + duration_ms: 0.313208 + type: 'test' + ... +# Subtest: validates a required artifact and keeps contracts isolated by session +ok 60 - validates a required artifact and keeps contracts isolated by session + --- + duration_ms: 22.150458 + type: 'test' + ... +# Subtest: missing or wrong-format artifacts fail with reviewer-readable issues +ok 61 - missing or wrong-format artifacts fail with reviewer-readable issues + --- + duration_ms: 1.935 + type: 'test' + ... +# Subtest: empty required artifacts fail validation +ok 62 - empty required artifacts fail validation + --- + duration_ms: 7.275833 + type: 'test' + ... +# Subtest: rejects traversal and symlink escapes before a domain validator runs +ok 63 - rejects traversal and symlink escapes before a domain validator runs + --- + duration_ms: 7.922916 + type: 'test' + ... +# Subtest: legal-specific validator data stays in the plugin validator +ok 64 - legal-specific validator data stays in the plugin validator + --- + duration_ms: 1.837125 + type: 'test' + ... +# Subtest: validator exceptions become structured failures instead of escaping the runtime +ok 65 - validator exceptions become structured failures instead of escaping the runtime + --- + duration_ms: 3.049541 + type: 'test' + ... +# Subtest: duplicate validator ids cannot override an existing validator +ok 66 - duplicate validator ids cannot override an existing validator + --- + duration_ms: 1.438416 + type: 'test' + ... +# Subtest: contract registration is atomic when one contract is invalid +ok 67 - contract registration is atomic when one contract is invalid + --- + duration_ms: 0.596834 + type: 'test' + ... +# Subtest: extension watcher ignores generated Python and Office runtime files +ok 68 - extension watcher ignores generated Python and Office runtime files + --- + duration_ms: 0.523375 + type: 'test' + ... +# Subtest: config bootstrap does not copy bundled skills into user storage +ok 69 - config bootstrap does not copy bundled skills into user storage + --- + duration_ms: 72.345583 + type: 'test' + ... +# Subtest: Office attachments are reported unsupported before size checks +ok 70 - Office attachments are reported unsupported before size checks + --- + duration_ms: 5.847709 + type: 'test' + ... +# Subtest: auto compaction reports summary success separately from rejected oversized output +ok 71 - auto compaction reports summary success separately from rejected oversized output + --- + duration_ms: 2.684708 + type: 'test' + ... +# Subtest: auto compaction marks a budget-clearing summary as applied +ok 72 - auto compaction marks a budget-clearing summary as applied + --- + duration_ms: 0.084041 + type: 'test' + ... +# Subtest: progress policy can force a full boundary while the token budget is still healthy +ok 73 - progress policy can force a full boundary while the token budget is still healthy + --- + duration_ms: 0.091125 + type: 'test' + ... +# Subtest: full compaction distinguishes a protected prefix with no summarizable messages from model failure +ok 74 - full compaction distinguishes a protected prefix with no summarizable messages from model failure + --- + duration_ms: 499.474291 + type: 'test' + ... +# Subtest: auto compaction propagates no-summarizable as a stable rejection reason without claiming a summary attempt +ok 75 - auto compaction propagates no-summarizable as a stable rejection reason without claiming a summary attempt + --- + duration_ms: 0.194 + type: 'test' + ... +# Subtest: bounded protected-prefix retention summarizes old agent turns and preserves every kept tool pair +ok 76 - bounded protected-prefix retention summarizes old agent turns and preserves every kept tool pair + --- + duration_ms: 1.243584 + type: 'test' + ... +# Subtest: full compaction summarizes a real-shaped single-prompt Agent trajectory +ok 77 - full compaction summarizes a real-shaped single-prompt Agent trajectory + --- + duration_ms: 0.719333 + type: 'test' + ... +# Subtest: full compaction aligns a message-count tail boundary to complete tool turns +ok 78 - full compaction aligns a message-count tail boundary to complete tool turns + --- + duration_ms: 0.313167 + type: 'test' + ... +# Subtest: sanitized Case 09 replay retains enough evidence to detect rejected blocking compactions +ok 79 - sanitized Case 09 replay retains enough evidence to detect rejected blocking compactions + --- + duration_ms: 3.645167 + type: 'test' + ... +# Subtest: lifecycle context is injected as a transient model-only message +ok 80 - lifecycle context is injected as a transient model-only message + --- + duration_ms: 4.099458 + type: 'test' + ... +# Subtest: lifecycle without a dynamic store preserves legacy hook messages +ok 81 - lifecycle without a dynamic store preserves legacy hook messages + --- + duration_ms: 0.160666 + type: 'test' + ... +# Subtest: SessionEnd clears session-scoped runtime state even when a hook throws +ok 82 - SessionEnd clears session-scoped runtime state even when a hook throws + --- + duration_ms: 0.69975 + type: 'test' + ... +# Subtest: merges pending context by priority and consumes it once +ok 83 - merges pending context by priority and consumes it once + --- + duration_ms: 2.031458 + type: 'test' + ... +# Subtest: re-registering the same source and id replaces stale content without changing order +ok 84 - re-registering the same source and id replaces stale content without changing order + --- + duration_ms: 0.20475 + type: 'test' + ... +# Subtest: expired context is pruned without affecting another session +ok 85 - expired context is pruned without affecting another session + --- + duration_ms: 0.09825 + type: 'test' + ... +# Subtest: blank context is ignored +ok 86 - blank context is ignored + --- + duration_ms: 0.069459 + type: 'test' + ... +# Subtest: clearing a session does not disturb another session with a shared prefix +ok 87 - clearing a session does not disturb another session with a shared prefix + --- + duration_ms: 0.167209 + type: 'test' + ... +# Subtest: context budgets retain higher-priority entries and cap merged prompt size +ok 88 - context budgets retain higher-priority entries and cap merged prompt size + --- + duration_ms: 0.969625 + type: 'test' + ... +# Subtest: source and id tuples cannot collide through delimiter characters +ok 89 - source and id tuples cannot collide through delimiter characters + --- + duration_ms: 0.15375 + type: 'test' + ... +# Subtest: default prompt does not require Markdown links for generated files +ok 90 - default prompt does not require Markdown links for generated files + --- + duration_ms: 2.889875 + type: 'test' + ... +# Subtest: default prompt does not advertise web search when the tool is unavailable +ok 91 - default prompt does not advertise web search when the tool is unavailable + --- + duration_ms: 0.116125 + type: 'test' + ... +# Subtest: available skills include the resolved SKILL.md path and bounded lookup guidance +ok 92 - available skills include the resolved SKILL.md path and bounded lookup guidance + --- + duration_ms: 2.299041 + type: 'test' + ... +# Subtest: budget snapshot uses conservative budget tokens for ratio while preserving real tokens +ok 93 - budget snapshot uses conservative budget tokens for ratio while preserving real tokens + --- + duration_ms: 1.120792 + type: 'test' + ... +# Subtest: tool text under token budget remains inline even when over legacy byte threshold +ok 94 - tool text under token budget remains inline even when over legacy byte threshold + --- + duration_ms: 6050.086709 + type: 'test' + ... +# Subtest: tool text over token budget is persisted with expanded grep-first preview +ok 95 - tool text over token budget is persisted with expanded grep-first preview + --- + duration_ms: 7.836584 + type: 'test' + ... +# Subtest: large tool error references preserve error semantics for model replay +ok 96 - large tool error references preserve error semantics for model replay + --- + duration_ms: 2.233083 + type: 'test' + ... +# Subtest: multibyte truncated tool result references advertise read_file access +ok 97 - multibyte truncated tool result references advertise read_file access + --- + duration_ms: 1.862417 + type: 'test' + ... +# Subtest: parses declarative artifact contracts without domain-specific knowledge +ok 98 - parses declarative artifact contracts without domain-specific knowledge + --- + duration_ms: 1.08025 + type: 'test' + ... +# (node:7241) ExperimentalWarning: SQLite is an experimental feature and might change at any time +# (Use `node --trace-warnings ...` to show where the warning was created) +# Subtest: an active domain plugin contributes only generic runtime contracts +ok 99 - an active domain plugin contributes only generic runtime contracts + --- + duration_ms: 2.093291 + type: 'test' + ... +# Subtest: internal prompts do not reactivate domain detection +ok 100 - internal prompts do not reactivate domain detection + --- + duration_ms: 0.36475 + type: 'test' + ... +# Subtest: parses only the supported model request patch fields +ok 101 - parses only the supported model request patch fields + --- + duration_ms: 2.27 + type: 'test' + ... +# Subtest: parses bounded dynamic context controls for hook-driven injection +ok 102 - parses bounded dynamic context controls for hook-driven injection + --- + duration_ms: 0.291209 + type: 'test' + ... +# Subtest: standalone skills expose only their slug without a parent-directory namespace +ok 103 - standalone skills expose only their slug without a parent-directory namespace + --- + duration_ms: 13.943417 + type: 'test' + ... +# Subtest: a plugin skill directory used as the configured base never derives a parent namespace +ok 104 - a plugin skill directory used as the configured base never derives a parent namespace + --- + duration_ms: 0.272833 + type: 'test' + ... +# Subtest: standalone skill precedence is project > user > builtin without legacy aliases +ok 105 - standalone skill precedence is project > user > builtin without legacy aliases + --- + duration_ms: 32.866125 + type: 'test' + ... +# Subtest: legacy bundled migration backs up only byte-identical bootstrap copies +ok 106 - legacy bundled migration backs up only byte-identical bootstrap copies + --- + duration_ms: 46.342958 + type: 'test' + ... +# Subtest: legacy bundled migration leaves user skills untouched when the release bundle is missing +ok 107 - legacy bundled migration leaves user skills untouched when the release bundle is missing + --- + duration_ms: 10.48925 + type: 'test' + ... +# Subtest: SkillManager lists built-ins separately and describes override relationships +ok 108 - SkillManager lists built-ins separately and describes override relationships + --- + duration_ms: 39.640166 + type: 'test' + ... +# Subtest: SkillManager permits reading but rejects mutations of built-in skills +ok 109 - SkillManager permits reading but rejects mutations of built-in skills + --- + duration_ms: 3.737958 + type: 'test' + ... +# Subtest: registered plain-text attachments with non-whitelisted names are described as read_file inspectable +ok 110 - registered plain-text attachments with non-whitelisted names are described as read_file inspectable + --- + duration_ms: 10.168625 + type: 'test' + ... +# Subtest: registered Office attachments are still described as not directly inspectable +ok 111 - registered Office attachments are still described as not directly inspectable + --- + duration_ms: 2.836833 + type: 'test' + ... +# Subtest: startPilotDeckServer listens before a background channel finishes starting +ok 112 - startPilotDeckServer listens before a background channel finishes starting + --- + duration_ms: 24.994666 + type: 'test' + ... +# (node:7250) ExperimentalWarning: SQLite is an experimental feature and might change at any time +# (Use `node --trace-warnings ...` to show where the warning was created) +# Subtest: browser-use args do not inherit generic proxy env by default +ok 113 - browser-use args do not inherit generic proxy env by default + --- + duration_ms: 1.74075 + type: 'test' + ... +# Subtest: browser-use args use explicit browser proxy server +ok 114 - browser-use args use explicit browser proxy server + --- + duration_ms: 0.099375 + type: 'test' + ... +# Subtest: browser-use args only inherit generic proxy env when opted in +ok 115 - browser-use args only inherit generic proxy env when opted in + --- + duration_ms: 0.069625 + type: 'test' + ... +# Subtest: browser-use args use config proxy when browser env proxy is absent +ok 116 - browser-use args use config proxy when browser env proxy is absent + --- + duration_ms: 0.082791 + type: 'test' + ... +# Subtest: browser-use args allow explicit direct mode to disable config proxy +ok 117 - browser-use args allow explicit direct mode to disable config proxy + --- + duration_ms: 0.053 + type: 'test' + ... +# Subtest: internal prompt dispatch waits for the user turn and fully drains the synthetic turn +ok 118 - internal prompt dispatch waits for the user turn and fully drains the synthetic turn + --- + duration_ms: 3.174209 + type: 'test' + ... +# Subtest: mapAgentEvent propagates runId to streaming lifecycle boundaries +ok 119 - mapAgentEvent propagates runId to streaming lifecycle boundaries + --- + duration_ms: 1.471292 + type: 'test' + ... +# Subtest: mapAgentEvent projects every convergence ordinal +ok 120 - mapAgentEvent projects every convergence ordinal + --- + duration_ms: 1.437584 + type: 'test' + ... +# Subtest: defer returns immediately while a user turn is busy +ok 121 - defer returns immediately while a user turn is busy + --- + duration_ms: 2.055583 + type: 'test' + ... +# Subtest: enqueue waits for idle and dispatches exactly once +ok 122 - enqueue waits for idle and dispatches exactly once + --- + duration_ms: 3.359792 + type: 'test' + ... +# Subtest: same semantic key coalesces in-flight and dedupes after completion +ok 123 - same semantic key coalesces in-flight and dedupes after completion + --- + duration_ms: 0.188542 + type: 'test' + ... +# Subtest: an idle-settle race retries when the gateway reports busy +ok 124 - an idle-settle race retries when the gateway reports busy + --- + duration_ms: 0.167542 + type: 'test' + ... +# Subtest: abort while queued does not run an internal turn +ok 125 - abort while queued does not run an internal turn + --- + duration_ms: 0.109208 + type: 'test' + ... +# Subtest: turn timeouts are reported distinctly from generic execution failures +ok 126 - turn timeouts are reported distinctly from generic execution failures + --- + duration_ms: 0.049416 + type: 'test' + ... +# Subtest: a zero recent-dedupe window disables completed-result deduplication +ok 127 - a zero recent-dedupe window disables completed-result deduplication + --- + duration_ms: 0.157334 + type: 'test' + ... +# Subtest: dispose prevents a queued prompt from starting after shutdown +ok 128 - dispose prevents a queued prompt from starting after shutdown + --- + duration_ms: 0.119 + type: 'test' + ... +# Subtest: the public WebSocket boundary strips trusted in-process turn controls +ok 129 - the public WebSocket boundary strips trusted in-process turn controls + --- + duration_ms: 1.807209 + type: 'test' + ... +# Subtest: waitForIdle resolves only after the matching turn slot is released +ok 130 - waitForIdle resolves only after the matching turn slot is released + --- + duration_ms: 3.413166 + type: 'test' + ... +# Subtest: waitForIdle honors AbortSignal without releasing the turn +ok 131 - waitForIdle honors AbortSignal without releasing the turn + --- + duration_ms: 0.601667 + type: 'test' + ... +# Subtest: mapAgentEvent bounds live tool result previews at the gateway +ok 132 - mapAgentEvent bounds live tool result previews at the gateway + --- + duration_ms: 1.674208 + type: 'test' + ... +# Subtest: mapAgentEvent bounds large strings inside successful tool data +ok 133 - mapAgentEvent bounds large strings inside successful tool data + --- + duration_ms: 0.67225 + type: 'test' + ... +# Subtest: mapAgentEvent bounds subagent tool result content and preview +ok 134 - mapAgentEvent bounds subagent tool result content and preview + --- + duration_ms: 0.3115 + type: 'test' + ... +# Subtest: Gateway propagates its absolute turn deadline into the Agent session +ok 135 - Gateway propagates its absolute turn deadline into the Agent session + --- + duration_ms: 3.492375 + type: 'test' + ... +# Subtest: WeixinChannel.start returns while QR login is waiting +ok 136 - WeixinChannel.start returns while QR login is waiting + --- + duration_ms: 15.417083 + type: 'test' + ... +# Subtest: channel runtime status reporter persists the latest channel state +ok 137 - channel runtime status reporter persists the latest channel state + --- + duration_ms: 6.133917 + type: 'test' + ... +# Subtest: UI weixin QR route only reads runtime status +ok 138 - UI weixin QR route only reads runtime status + --- + duration_ms: 1.403959 + type: 'test' + ... +# Subtest: UI weixin QR begin route delegates to gateway prepare RPC +ok 139 - UI weixin QR begin route delegates to gateway prepare RPC + --- + duration_ms: 0.174709 + type: 'test' + ... +# Subtest: Gateway settings keeps existing status rendered during silent refresh +ok 140 - Gateway settings keeps existing status rendered during silent refresh + --- + duration_ms: 0.368292 + type: 'test' + ... +# Subtest: Gateway settings starts weixin QR by begin route and ignores stale runtime errors +ok 141 - Gateway settings starts weixin QR by begin route and ignores stale runtime errors + --- + duration_ms: 0.341166 + type: 'test' + ... +# Subtest: Gateway protocol exposes prepare_weixin_login RPC +ok 142 - Gateway protocol exposes prepare_weixin_login RPC + --- + duration_ms: 0.443666 + type: 'test' + ... +# Subtest: applies context, system messages, and restricted model request patches +ok 143 - applies context, system messages, and restricted model request patches + --- + duration_ms: 1.000334 + type: 'test' + ... +# Subtest: blocks before any request mutation is used +ok 144 - blocks before any request mutation is used + --- + duration_ms: 0.055083 + type: 'test' + ... +# Subtest: McpClient keeps stdio clients idle before connection +ok 145 - McpClient keeps stdio clients idle before connection + --- + duration_ms: 0.521334 + type: 'test' + ... +# Subtest: McpClient constructs streamable_http transport without requiring stdio fields +ok 146 - McpClient constructs streamable_http transport without requiring stdio fields + --- + duration_ms: 0.045958 + type: 'test' + ... +# Subtest: McpClient routes streamable_http fetches with bounded timeouts +ok 147 - McpClient routes streamable_http fetches with bounded timeouts + --- + duration_ms: 0.717041 + type: 'test' + ... +# Subtest: expandMcpString expands ${env:*} placeholders +ok 148 - expandMcpString expands ${env:*} placeholders + --- + duration_ms: 0.645667 + type: 'test' + ... +# Subtest: expandMcpString expands ${userHome} placeholder +ok 149 - expandMcpString expands ${userHome} placeholder + --- + duration_ms: 0.0985 + type: 'test' + ... +# Subtest: expandMcpString expands ~ prefix +ok 150 - expandMcpString expands ~ prefix + --- + duration_ms: 0.042166 + type: 'test' + ... +# Subtest: expandMcpString leaves unknown env vars as empty string +ok 151 - expandMcpString leaves unknown env vars as empty string + --- + duration_ms: 0.034 + type: 'test' + ... +# Subtest: expandMcpString handles combined placeholders +ok 152 - expandMcpString handles combined placeholders + --- + duration_ms: 0.042166 + type: 'test' + ... +# Subtest: expandMcpConfig recursively expands objects and arrays +ok 153 - expandMcpConfig recursively expands objects and arrays + --- + duration_ms: 0.531083 + type: 'test' + ... +# Subtest: parsePluginMcpServers expands ${env:*} in stdio env +ok 154 - parsePluginMcpServers expands ${env:*} in stdio env + --- + duration_ms: 0.14375 + type: 'test' + ... +# Subtest: parsePluginMcpServers expands ${env:*} in stdio command before transport detection +ok 155 - parsePluginMcpServers expands ${env:*} in stdio command before transport detection + --- + duration_ms: 0.042666 + type: 'test' + ... +# Subtest: parsePluginMcpServers drops empty expanded stdio command +ok 156 - parsePluginMcpServers drops empty expanded stdio command + --- + duration_ms: 0.190041 + type: 'test' + ... +# Subtest: parsePluginMcpServers expands ${userHome} in stdio cwd +ok 157 - parsePluginMcpServers expands ${userHome} in stdio cwd + --- + duration_ms: 0.228208 + type: 'test' + ... +# Subtest: parsePluginMcpServers expands ~ in stdio args (backward compat) +ok 158 - parsePluginMcpServers expands ~ in stdio args (backward compat) + --- + duration_ms: 0.107916 + type: 'test' + ... +# Subtest: parsePluginMcpServers expands ${env:*} in streamable_http url +ok 159 - parsePluginMcpServers expands ${env:*} in streamable_http url + --- + duration_ms: 0.041875 + type: 'test' + ... +# Subtest: parsePluginMcpServers expands ${env:*} in streamable_http headers +ok 160 - parsePluginMcpServers expands ${env:*} in streamable_http headers + --- + duration_ms: 0.036625 + type: 'test' + ... +# Subtest: user config and plugin config resolve same placeholders consistently +ok 161 - user config and plugin config resolve same placeholders consistently + --- + duration_ms: 0.03775 + type: 'test' + ... +# Subtest: Anthropic-compatible terminated SSE errors remain retryable +ok 162 - Anthropic-compatible terminated SSE errors remain retryable + --- + duration_ms: 4.865 + type: 'test' + ... +# Subtest: catalog provider resolves api key from default env var when apiKey is omitted +ok 163 - catalog provider resolves api key from default env var when apiKey is omitted + --- + duration_ms: 1.552291 + type: 'test' + ... +# Subtest: catalog provider resolves api key from default env var when apiKey is blank +ok 164 - catalog provider resolves api key from default env var when apiKey is blank + --- + duration_ms: 0.122333 + type: 'test' + ... +# Subtest: normalizeModelError classifies common network failures +ok 165 - normalizeModelError classifies common network failures + --- + duration_ms: 2.603083 + type: 'test' + ... +# Subtest: model request failure preserves provider raw message before action guidance +ok 166 - model request failure preserves provider raw message before action guidance + --- + duration_ms: 3.790333 + type: 'test' + ... +# Subtest: model_not_found guidance points users to local model settings +ok 167 - model_not_found guidance points users to local model settings + --- + duration_ms: 1.911 + type: 'test' + ... +# Subtest: stream idle timeout is classified as timeout with network and timeoutMs guidance +ok 168 - stream idle timeout is classified as timeout with network and timeoutMs guidance + --- + duration_ms: 0.544167 + type: 'test' + ... +# Subtest: provider terminated stream is classified as a retryable connection reset +ok 169 - provider terminated stream is classified as a retryable connection reset + --- + duration_ms: 0.55875 + type: 'test' + ... +# Subtest: billing and rate limit guidance distinguish provider-side fixes +ok 170 - billing and rate limit guidance distinguish provider-side fixes + --- + duration_ms: 0.20675 + type: 'test' + ... +# Subtest: unknown provider errors still give actionable settings and provider checks +ok 171 - unknown provider errors still give actionable settings and provider checks + --- + duration_ms: 0.082584 + type: 'test' + ... +# Subtest: OpenAI Responses usage reads cached tokens from input token details +ok 172 - OpenAI Responses usage reads cached tokens from input token details + --- + duration_ms: 1.034375 + type: 'test' + ... +# Subtest: Gemini usage counts thoughts tokens as output consumption +ok 173 - Gemini usage counts thoughts tokens as output consumption + --- + duration_ms: 0.166083 + type: 'test' + ... +# Subtest: Ollama provider uses catalog defaults and does not require apiKey +ok 174 - Ollama provider uses catalog defaults and does not require apiKey + --- + duration_ms: 2.945959 + type: 'test' + ... +# Subtest: Ollama provider builds OpenAI-compatible chat completions body +ok 175 - Ollama provider builds OpenAI-compatible chat completions body + --- + duration_ms: 0.902458 + type: 'test' + ... +# Subtest: model request builders tolerate malformed messages with missing content +ok 176 - model request builders tolerate malformed messages with missing content + --- + duration_ms: 1.8735 + type: 'test' + ... +# Subtest: cloneMessages normalizes malformed messages with missing content +ok 177 - cloneMessages normalizes malformed messages with missing content + --- + duration_ms: 1.017875 + type: 'test' + ... +# Subtest: streamModel retries Anthropic-compatible terminated SSE errors +ok 178 - streamModel retries Anthropic-compatible terminated SSE errors + --- + duration_ms: 8.588 + type: 'test' + ... +# Subtest: networkFetch retries retryable status responses and then succeeds +ok 179 - networkFetch retries retryable status responses and then succeeds + --- + duration_ms: 2.551459 + type: 'test' + ... +# Subtest: networkFetch uses retry-after when calculating retry delay +ok 180 - networkFetch uses retry-after when calculating retry delay + --- + duration_ms: 0.066291 + type: 'test' + ... +# Subtest: networkFetch caps retry-after delays with maxDelayMs +ok 181 - networkFetch caps retry-after delays with maxDelayMs + --- + duration_ms: 0.033334 + type: 'test' + ... +# Subtest: networkFetch normalizes DNS and reset errors +ok 182 - networkFetch normalizes DNS and reset errors + --- + duration_ms: 0.084167 + type: 'test' + ... +# Subtest: networkFetch times out requests +ok 183 - networkFetch times out requests + --- + duration_ms: 3.467959 + type: 'test' + ... +# Subtest: networkFetch honors init.signal abort reasons without options.signal +ok 184 - networkFetch honors init.signal abort reasons without options.signal + --- + duration_ms: 1.569333 + type: 'test' + ... +# Subtest: networkFetch preserves parent NetworkFetchError reasons passed through options.signal +ok 185 - networkFetch preserves parent NetworkFetchError reasons passed through options.signal + --- + duration_ms: 0.238209 + type: 'test' + ... +# (node:7291) ExperimentalWarning: SQLite is an experimental feature and might change at any time +# (Use `node --trace-warnings ...` to show where the warning was created) +# [session-title] generation skipped (invalid_json): Unexpected token 'Q', "QA" is not valid JSON +# Subtest: local gateway writes a complete shadow bundle linked to the final request +ok 186 - local gateway writes a complete shadow bundle linked to the final request + --- + duration_ms: 760.220917 + type: 'test' + ... +# [session-title] generation skipped (invalid_json): Unexpected token 'Q', "QA" is not valid JSON +# [session-title] generation skipped (invalid_json): Unexpected token 'Q', "QA" is not valid JSON +# Subtest: enabling O1 does not change Agent-visible model input +ok 187 - enabling O1 does not change Agent-visible model input + --- + duration_ms: 438.028792 + type: 'test' + ... +# (node:7292) ExperimentalWarning: SQLite is an experimental feature and might change at any time +# (Use `node --trace-warnings ...` to show where the warning was created) +# [session-title] generation skipped (invalid_json): Unexpected token 'B', "Bounded co"... is not valid JSON +# Subtest: local Gateway records a bounded handoff through boundary to progress +ok 188 - local Gateway records a bounded handoff through boundary to progress + --- + duration_ms: 1213.915875 + type: 'test' + ... +# (node:7293) ExperimentalWarning: SQLite is an experimental feature and might change at any time +# (Use `node --trace-warnings ...` to show where the warning was created) +# Subtest: local Gateway records a complete O1 trajectory when a bounded child times out +ok 189 - local Gateway records a complete O1 trajectory when a bounded child times out + --- + duration_ms: 1623.113416 + type: 'test' + ... +# Subtest: subagent tool detection and result pair at the model projection layer +ok 190 - subagent tool detection and result pair at the model projection layer + --- + duration_ms: 8.832708 + type: 'test' + ... +# Subtest: main tool detection and result produce one exact O1 pair +ok 191 - main tool detection and result produce one exact O1 pair + --- + duration_ms: 0.835458 + type: 'test' + ... +# Subtest: repair preparation grace is observable without becoming progress +ok 192 - repair preparation grace is observable without becoming progress + --- + duration_ms: 0.078125 + type: 'test' + ... +# Subtest: recorder finalizes a complete hash-only trajectory +ok 193 - recorder finalizes a complete hash-only trajectory + --- + duration_ms: 14.286792 + type: 'test' + ... +# Subtest: queue overflow is explicit and makes integrity partial +ok 194 - queue overflow is explicit and makes integrity partial + --- + duration_ms: 33.39175 + type: 'test' + ... +# Subtest: verifier rejects reused model request identity and duplicate terminals +ok 195 - verifier rejects reused model request identity and duplicate terminals + --- + duration_ms: 22.828083 + type: 'test' + ... +# Subtest: verifier rejects duplicate identity and secret-bearing keys +ok 196 - verifier rejects duplicate identity and secret-bearing keys + --- + duration_ms: 0.143542 + type: 'test' + ... +# [PilotDeck] transientRetry: rate_limit (attempt 1/1, delay=1ms) +# Subtest: router observation closes every fallback and retry attempt +ok 197 - router observation closes every fallback and retry attempt + --- + duration_ms: 21.635875 + type: 'test' + ... +# Subtest: router request identity separates sessions sharing a turn id +ok 198 - router request identity separates sessions sharing a turn id + --- + duration_ms: 4.938834 + type: 'test' + ... +# Subtest: router request identity advances across sequential calls in one turn +ok 199 - router request identity advances across sequential calls in one turn + --- + duration_ms: 3.51175 + type: 'test' + ... +# Subtest: observability is disabled by default and preserves the O1 diagnostic profile +ok 200 - observability is disabled by default and preserves the O1 diagnostic profile + --- + duration_ms: 36.201167 + type: 'test' + ... +# Subtest: O1 rejects unsupported profiles and unsafe labels +ok 201 - O1 rejects unsupported profiles and unsafe labels + --- + duration_ms: 4.4895 + type: 'test' + ... +# Subtest: web search can be explicitly disabled without discarding provider config +ok 202 - web search can be explicitly disabled without discarding provider config + --- + duration_ms: 2.035125 + type: 'test' + ... +# Subtest: web search enabled remains optional for backwards compatibility +ok 203 - web search enabled remains optional for backwards compatibility + --- + duration_ms: 0.110666 + type: 'test' + ... +# Subtest: web search enabled must be a boolean +ok 204 - web search enabled must be a boolean + --- + duration_ms: 0.111416 + type: 'test' + ... +# Subtest: progress lease is disabled by default and opt-in evaluation config is preserved +ok 205 - progress lease is disabled by default and opt-in evaluation config is preserved + --- + duration_ms: 21.079208 + type: 'test' + ... +# Subtest: progress lease preserves an explicit cold-start allowance +ok 206 - progress lease preserves an explicit cold-start allowance + --- + duration_ms: 3.111875 + type: 'test' + ... +# Subtest: progress lease rejects non-evaluation modes +ok 207 - progress lease rejects non-evaluation modes + --- + duration_ms: 3.648375 + type: 'test' + ... +# Subtest: progress lease rejects a cold-start allowance below the steady-state limit +ok 208 - progress lease rejects a cold-start allowance below the steady-state limit + --- + duration_ms: 3.9965 + type: 'test' + ... +# Subtest: legal coverage validator creates a current proof and removes it when the deliverable changes +ok 209 - legal coverage validator creates a current proof and removes it when the deliverable changes + --- + duration_ms: 309.771209 + type: 'test' + ... +# Subtest: legal coverage initializer creates a text skeleton before source review and remains idempotent +ok 210 - legal coverage initializer creates a text skeleton before source review and remains idempotent + --- + duration_ms: 354.010792 + type: 'test' + ... +# Subtest: legal coverage initializer binds trusted manifests to original inputs +ok 211 - legal coverage initializer binds trusted manifests to original inputs + --- + duration_ms: 422.073208 + type: 'test' + ... +# Subtest: legal coverage source bootstrap creates only deterministic pending manifest rows +ok 212 - legal coverage source bootstrap creates only deterministic pending manifest rows + --- + duration_ms: 419.02025 + type: 'test' + ... +# Subtest: legal coverage injects deterministic disjoint worker batches for large pending source rooms +ok 213 - legal coverage injects deterministic disjoint worker batches for large pending source rooms + --- + duration_ms: 5019.701583 + type: 'test' + ... +# Subtest: legal coverage CLI exposes bundled guidance through stable named references +ok 214 - legal coverage CLI exposes bundled guidance through stable named references + --- + duration_ms: 145.230167 + type: 'test' + ... +# Subtest: legal coverage initializer creates only explicit text formats and preserves existing content +ok 215 - legal coverage initializer creates only explicit text formats and preserves existing content + --- + duration_ms: 57.391 + type: 'test' + ... +# Subtest: legal coverage initializer rejects traversal and symlink ancestors without external writes +ok 216 - legal coverage initializer rejects traversal and symlink ancestors without external writes + --- + duration_ms: 156.389125 + type: 'test' + ... +# Subtest: legal coverage validator binds runner originals and derivations into its proof +ok 217 - legal coverage validator binds runner originals and derivations into its proof + --- + duration_ms: 226.286792 + type: 'test' + ... +# Subtest: legal coverage validator rejects missing lineage and mutable or derived input roots +ok 218 - legal coverage validator rejects missing lineage and mutable or derived input roots + --- + duration_ms: 281.516084 + type: 'test' + ... +# Subtest: legal coverage next-batch exposes one bounded deterministic repair slice +ok 219 - legal coverage next-batch exposes one bounded deterministic repair slice + --- + duration_ms: 225.475875 + type: 'test' + ... +# Subtest: legal coverage apply-batch atomically updates only the current bounded slice +ok 220 - legal coverage apply-batch atomically updates only the current bounded slice + --- + duration_ms: 331.688875 + type: 'test' + ... +# Subtest: legal coverage apply-batch rejects record and byte limit violations before writing +ok 221 - legal coverage apply-batch rejects record and byte limit violations before writing + --- + duration_ms: 214.679083 + type: 'test' + ... +# Subtest: legal coverage validator rejects orphaned conflicts and incomplete final disclosure +ok 222 - legal coverage validator rejects orphaned conflicts and incomplete final disclosure + --- + duration_ms: 110.311917 + type: 'test' + ... +# Subtest: legal coverage validator detects un-inventoried sources and cross-fact timeline collisions +ok 223 - legal coverage validator detects un-inventoried sources and cross-fact timeline collisions + --- + duration_ms: 118.153083 + type: 'test' + ... +# Subtest: legal coverage validator rejects reused generic quotes and unsupported fact coverage +ok 224 - legal coverage validator rejects reused generic quotes and unsupported fact coverage + --- + duration_ms: 139.316292 + type: 'test' + ... +# Subtest: legal coverage validator requires authority links for critical issues and legal-authority matrices +ok 225 - legal coverage validator requires authority links for critical issues and legal-authority matrices + --- + duration_ms: 136.954083 + type: 'test' + ... +# Subtest: legal coverage validator rejects non-object canonical JSON documents +ok 226 - legal coverage validator rejects non-object canonical JSON documents + --- + duration_ms: 758.886208 + type: 'test' + ... +# Subtest: legal coverage validator binds reviewed source bytes to the ledger and state hash +ok 227 - legal coverage validator binds reviewed source bytes to the ledger and state hash + --- + duration_ms: 223.867917 + type: 'test' + ... +# Subtest: legal coverage validator rejects ancestor symlinks and a symlinked proof +ok 228 - legal coverage validator rejects ancestor symlinks and a symlinked proof + --- + duration_ms: 170.070833 + type: 'test' + ... +# Subtest: legal coverage validator does not read or write through a symlinked state ancestor +ok 229 - legal coverage validator does not read or write through a symlinked state ancestor + --- + duration_ms: 107.945125 + type: 'test' + ... +# Subtest: legal coverage validator requires unresolved disclosure for unverified material facts +ok 230 - legal coverage validator requires unresolved disclosure for unverified material facts + --- + duration_ms: 113.256958 + type: 'test' + ... +# Subtest: legal coverage validator rejects unique but irrelevant issue and authority quotes +ok 231 - legal coverage validator rejects unique but irrelevant issue and authority quotes + --- + duration_ms: 115.672416 + type: 'test' + ... +# Subtest: legal coverage validator enforces reciprocal and same-entry ledger relationships +ok 232 - legal coverage validator enforces reciprocal and same-entry ledger relationships + --- + duration_ms: 113.9885 + type: 'test' + ... +# Subtest: legal coverage validator accepts null for an optional threshold assessment +ok 233 - legal coverage validator accepts null for an optional threshold assessment + --- + duration_ms: 114.511375 + type: 'test' + ... +# Subtest: legal coverage validator uses locators without quotes for binary deliverables +ok 234 - legal coverage validator uses locators without quotes for binary deliverables + --- + duration_ms: 112.544917 + type: 'test' + ... +# Subtest: legal coverage validator rejects empty-fact shortcuts and material facts outside matrices +ok 235 - legal coverage validator rejects empty-fact shortcuts and material facts outside matrices + --- + duration_ms: 221.027291 + type: 'test' + ... +# Subtest: legal coverage hook activates only legal work and injects one observable milestone +ok 236 - legal coverage hook activates only legal work and injects one observable milestone + --- + duration_ms: 576.368458 + type: 'test' + ... +# Subtest: legal coverage hook groups repeated validator errors into one bounded milestone +ok 237 - legal coverage hook groups repeated validator errors into one bounded milestone + --- + duration_ms: 622.175666 + type: 'test' + ... +# Subtest: legal coverage pending-matrix selection is bounded across pages and invalid revisions cannot manufacture progress +ok 238 - legal coverage pending-matrix selection is bounded across pages and invalid revisions cannot manufacture progress + --- + duration_ms: 1062.665167 + type: 'test' + ... +# Subtest: legal coverage progress ordinal advances once for a new legal phase +ok 239 - legal coverage progress ordinal advances once for a new legal phase + --- + duration_ms: 230.389833 + type: 'test' + ... +# Subtest: legal coverage permits not-applicable only after exhaustive selection and rejects stale or changed matrix proposals +ok 240 - legal coverage permits not-applicable only after exhaustive selection and rejects stale or changed matrix proposals + --- + duration_ms: 512.68775 + type: 'test' + ... +# Subtest: legal coverage rejects oversized matrix evidence before making a selection receipt immutable +ok 241 - legal coverage rejects oversized matrix evidence before making a selection receipt immutable + --- + duration_ms: 178.235125 + type: 'test' + ... +# Subtest: legal coverage fails closed when one matrix index item exceeds the bounded page +ok 242 - legal coverage fails closed when one matrix index item exceeds the bounded page + --- + duration_ms: 174.959417 + type: 'test' + ... +# Subtest: legal coverage injects a bounded deterministic matrix relation-closure batch +ok 243 - legal coverage injects a bounded deterministic matrix relation-closure batch + --- + duration_ms: 237.833125 + type: 'test' + ... +# Subtest: legal coverage authority closure uses one bounded state-bound transaction +ok 244 - legal coverage authority closure uses one bounded state-bound transaction + --- + duration_ms: 638.00525 + type: 'test' + ... +# Subtest: legal coverage rejects changed and symlinked authority closure proposals without ledger mutation +ok 245 - legal coverage rejects changed and symlinked authority closure proposals without ledger mutation + --- + duration_ms: 272.83425 + type: 'test' + ... +# Subtest: legal coverage milestone digest ignores opaque incomplete-state hash churn +ok 246 - legal coverage milestone digest ignores opaque incomplete-state hash churn + --- + duration_ms: 0.273667 + type: 'test' + ... +# Subtest: legal coverage hook activates a malformed configured workspace so the agent can repair it +ok 247 - legal coverage hook activates a malformed configured workspace so the agent can repair it + --- + duration_ms: 110.331375 + type: 'test' + ... +# Subtest: legal coverage hook rejects a symlinked session-state ancestor without writing outside the workspace +ok 248 - legal coverage hook rejects a symlinked session-state ancestor without writing outside the workspace + --- + duration_ms: 57.214666 + type: 'test' + ... +# Subtest: legal product plugin loads one skill and contains no benchmark-specific controls +ok 249 - legal product plugin loads one skill and contains no benchmark-specific controls + --- + duration_ms: 3.418167 + type: 'test' + ... +# Subtest: file artifacts include every meaningful workspace change without an extension allowlist +ok 250 - file artifacts include every meaningful workspace change without an extension allowlist + --- + duration_ms: 30.1795 + type: 'test' + ... +# Subtest: file artifact fingerprints are reused for unchanged files across scans and turns +ok 251 - file artifact fingerprints are reused for unchanged files across scans and turns + --- + duration_ms: 15.934958 + type: 'test' + ... +# Subtest: TurnRunner emits and persists file artifacts before completing the turn +ok 252 - TurnRunner emits and persists file artifacts before completing the turn + --- + duration_ms: 20.09775 + type: 'test' + ... +# Subtest: TurnRunner does not collect generated files when artifacts are disabled +ok 253 - TurnRunner does not collect generated files when artifacts are disabled + --- + duration_ms: 2.752583 + type: 'test' + ... +# Subtest: bash success result is formatted with assertions, stdout, and stderr +ok 254 - bash success result is formatted with assertions, stdout, and stderr + --- + duration_ms: 8.600375 + type: 'test' + ... +# Subtest: bash allows hyphenated commands whose names merely start with next +ok 255 - bash allows hyphenated commands whose names merely start with next + --- + duration_ms: 3.976875 + type: 'test' + ... +# Subtest: bash still rejects exact framework CLI tokens that start long-lived processes +ok 256 - bash still rejects exact framework CLI tokens that start long-lived processes + --- + duration_ms: 2.24175 + type: 'test' + ... +# Subtest: bash failure tool result includes raw stdout and stderr tail for UI and model +ok 257 - bash failure tool result includes raw stdout and stderr tail for UI and model + --- + duration_ms: 19.858708 + type: 'test' + ... +# Subtest: bash failure diagnostic extracts Python traceback location and exception +ok 258 - bash failure diagnostic extracts Python traceback location and exception + --- + duration_ms: 1.239667 + type: 'test' + ... +# Subtest: bash success keeps full output for ToolResultBudget persistence +ok 259 - bash success keeps full output for ToolResultBudget persistence + --- + duration_ms: 7.446417 + type: 'test' + ... +# Subtest: agent tool accepts explorer as an alias for explore +ok 260 - agent tool accepts explorer as an alias for explore + --- + duration_ms: 2.957125 + type: 'test' + ... +# Subtest: agent tool defaults general-purpose to explore in ask mode +ok 261 - agent tool defaults general-purpose to explore in ask mode + --- + duration_ms: 0.130791 + type: 'test' + ... +# Subtest: agent tool injects and forwards the parent-bounded timeout +ok 262 - agent tool injects and forwards the parent-bounded timeout + --- + duration_ms: 0.182958 + type: 'test' + ... +# Subtest: agent tool rejects a launch that would consume the parent handoff window +ok 263 - agent tool rejects a launch that would consume the parent handoff window + --- + duration_ms: 0.25275 + type: 'test' + ... +# Subtest: agent fallback path enforces the same timeout +ok 264 - agent fallback path enforces the same timeout + --- + duration_ms: 5.87025 + type: 'test' + ... +# Subtest: agent tool preserves unknown custom fallback subagent names +ok 265 - agent tool preserves unknown custom fallback subagent names + --- + duration_ms: 1.067708 + type: 'test' + ... +# Subtest: execute_code read-only probe handles missing input +ok 266 - execute_code read-only probe handles missing input + --- + duration_ms: 0.691959 + type: 'test' + ... +# Subtest: disabling web search removes it from the registry but keeps web fetch +ok 267 - disabling web search removes it from the registry but keeps web fetch + --- + duration_ms: 1.153792 + type: 'test' + ... +# Subtest: execute_code rejects nested web search calls when web search is disabled +ok 268 - execute_code rejects nested web search calls when web search is disabled + --- + duration_ms: 0.13 + type: 'test' + ... +# Subtest: read_skill returns the resolved SKILL.md path with the skill body +ok 269 - read_skill returns the resolved SKILL.md path with the skill body + --- + duration_ms: 0.590083 + type: 'test' + ... +# Subtest: read_skill preserves legacy content-only loading when metadata is unavailable +ok 270 - read_skill preserves legacy content-only loading when metadata is unavailable + --- + duration_ms: 0.064125 + type: 'test' + ... +# Subtest: web_search retries transient provider failures +ok 271 - web_search retries transient provider failures + --- + duration_ms: 583.647666 + type: 'test' + ... +# Subtest: web_search turns request timeout into tool_timeout +ok 272 - web_search turns request timeout into tool_timeout + --- + duration_ms: 2.785667 + type: 'test' + ... +# Subtest: web_search turns network timeout errors into tool_timeout +ok 273 - web_search turns network timeout errors into tool_timeout + --- + duration_ms: 1.924666 + type: 'test' + ... +# Subtest: write_file freshness error tells the model to read the file first +ok 274 - write_file freshness error tells the model to read the file first + --- + duration_ms: 0.937708 + type: 'test' + ... +# Subtest: write_file missing content points at the missing field and chunked recovery +ok 275 - write_file missing content points at the missing field and chunked recovery + --- + duration_ms: 0.29125 + type: 'test' + ... +# Subtest: invalid tool input keeps a bounded original error block +ok 276 - invalid tool input keeps a bounded original error block + --- + duration_ms: 0.207625 + type: 'test' + ... +# Subtest: edit_file old_string miss tells the model to reread and copy exact text +ok 277 - edit_file old_string miss tells the model to reread and copy exact text + --- + duration_ms: 0.061042 + type: 'test' + ... +# Subtest: read_file invalid range preserves the concrete issue +ok 278 - read_file invalid range preserves the concrete issue + --- + duration_ms: 0.105292 + type: 'test' + ... +# Subtest: bash missing command reports the required command parameter +ok 279 - bash missing command reports the required command parameter + --- + duration_ms: 0.099584 + type: 'test' + ... +# Subtest: bash timeout over max keeps foreground and task_wait guidance +ok 280 - bash timeout over max keeps foreground and task_wait guidance + --- + duration_ms: 0.129625 + type: 'test' + ... +# Subtest: bash background rejection keeps background-specific guidance +ok 281 - bash background rejection keeps background-specific guidance + --- + duration_ms: 0.076375 + type: 'test' + ... +# Subtest: write_file can create a new file without a prior read_file call +ok 282 - write_file can create a new file without a prior read_file call + --- + duration_ms: 8.270083 + type: 'test' + ... +# Subtest: edit_file can create a new file with empty old_string without a prior read_file call +ok 283 - edit_file can create a new file with empty old_string without a prior read_file call + --- + duration_ms: 2.161541 + type: 'test' + ... +# Subtest: read_file auto-pages large text files instead of failing +ok 284 - read_file auto-pages large text files instead of failing + --- + duration_ms: 2405.288125 + type: 'test' + ... +# Subtest: read_file rejects Office container files during validation +ok 285 - read_file rejects Office container files during validation + --- + duration_ms: 0.8445 + type: 'test' + ... +# Subtest: read_file explicit limit reads a large file range without auto paging +ok 286 - read_file explicit limit reads a large file range without auto paging + --- + duration_ms: 5.25675 + type: 'test' + ... +# Subtest: read_file auto-shrinks oversized persisted tool-result ref ranges +ok 287 - read_file auto-shrinks oversized persisted tool-result ref ranges + --- + duration_ms: 23629.287417 + type: 'test' + ... +# Subtest: read_file keeps explicit oversized ordinary file ranges strict +ok 288 - read_file keeps explicit oversized ordinary file ranges strict + --- + duration_ms: 2.455417 + type: 'test' + ... +# Subtest: read_file explicit limit records a ranged snapshot for follow-up edits +ok 289 - read_file explicit limit records a ranged snapshot for follow-up edits + --- + duration_ms: 1.649875 + type: 'test' + ... +# Subtest: read_file auto-paged large files record a ranged snapshot for follow-up edits +ok 290 - read_file auto-paged large files record a ranged snapshot for follow-up edits + --- + duration_ms: 1629.285875 + type: 'test' + ... +# Subtest: read_file returns a head-tail preview for a single oversized line +ok 291 - read_file returns a head-tail preview for a single oversized line + --- + duration_ms: 2.384333 + type: 'test' + ... +# Subtest: read_file classifies common Office formats as binary +ok 292 - read_file classifies common Office formats as binary + --- + duration_ms: 0.21825 + type: 'test' + ... +# Subtest: read_file rejects ranged binary input instead of hanging +ok 293 - read_file rejects ranged binary input instead of hanging + --- + duration_ms: 1.0455 + type: 'test' + ... +# Subtest: read_file range honors an aborted signal +ok 294 - read_file range honors an aborted signal + --- + duration_ms: 0.781584 + type: 'test' + ... +# Subtest: glob result text tells the model when more files are available +ok 295 - glob result text tells the model when more files are available + --- + duration_ms: 32.066292 + type: 'test' + ... +# Subtest: grep result text includes next offset when paginated +ok 296 - grep result text includes next offset when paginated + --- + duration_ms: 16.48375 + type: 'test' + ... +# Subtest: todo_write returns the actual todo list in model-visible content +ok 297 - todo_write returns the actual todo list in model-visible content + --- + duration_ms: 0.652333 + type: 'test' + ... +# Subtest: task_list returns model-visible status and next action hints +ok 298 - task_list returns model-visible status and next action hints + --- + duration_ms: 3.904083 + type: 'test' + ... +# Subtest: applyResultSizeLimit keeps both head and tail when truncating text output +ok 299 - applyResultSizeLimit keeps both head and tail when truncating text output + --- + duration_ms: 0.805125 + type: 'test' + ... +# Subtest: large tool results are persisted under workspace .pilotdeck and readable by read_file +ok 300 - large tool results are persisted under workspace .pilotdeck and readable by read_file + --- + duration_ms: 12.205375 + type: 'test' + ... +# Subtest: large tool result read_file aliases are short and sequential +ok 301 - large tool result read_file aliases are short and sequential + --- + duration_ms: 6.46325 + type: 'test' + ... +# Subtest: history replay restores structured agent file artifacts +ok 302 - history replay restores structured agent file artifacts + --- + duration_ms: 10.11675 + type: 'test' + ... +# Subtest: history replay hides Agent file artifacts in general conversations +ok 303 - history replay hides Agent file artifacts in general conversations + --- + duration_ms: 3.607458 + type: 'test' + ... +# Subtest: history replay preserves agent status i18n metadata and user hint +ok 304 - history replay preserves agent status i18n metadata and user hint + --- + duration_ms: 11.991084 + type: 'test' + ... +# Subtest: history token usage restores latest non-empty turn past latest empty turn result +ok 305 - history token usage restores latest non-empty turn past latest empty turn result + --- + duration_ms: 4.12375 + type: 'test' + ... +# Subtest: history token usage prefers persisted context budget snapshot +ok 306 - history token usage prefers persisted context budget snapshot + --- + duration_ms: 3.816333 + type: 'test' + ... +# Subtest: web reducer merges persisted tool result detail path into existing tool result +ok 307 - web reducer merges persisted tool result detail path into existing tool result + --- + duration_ms: 1.401459 + type: 'test' + ... +# Subtest: web reducer bounds huge live tool result previews +ok 308 - web reducer bounds huge live tool result previews + --- + duration_ms: 0.190666 + type: 'test' + ... +1..308 +# tests 308 +# suites 0 +# pass 308 +# fail 0 +# cancelled 0 +# skipped 0 +# todo 0 +# duration_ms 30367.696792 diff --git a/.omo/evidence/20260727-v24r10-validated-repair-contract/replay-preserved-case09.mjs b/.omo/evidence/20260727-v24r10-validated-repair-contract/replay-preserved-case09.mjs new file mode 100644 index 00000000..b8402df6 --- /dev/null +++ b/.omo/evidence/20260727-v24r10-validated-repair-contract/replay-preserved-case09.mjs @@ -0,0 +1,201 @@ +import assert from "node:assert/strict"; +import { createHash } from "node:crypto"; +import { execFile, spawn } from "node:child_process"; +import { cp, mkdir, mkdtemp, readFile, readdir, rm, writeFile } from "node:fs/promises"; +import { tmpdir } from "node:os"; +import { basename, join, resolve } from "node:path"; +import { promisify } from "node:util"; + +const execFileAsync = promisify(execFile); +const sourceRun = resolve(process.argv[2] ?? ""); +const candidateRoot = resolve(process.argv[3] ?? ""); +const outputPath = resolve(process.argv[4] ?? ""); + +assert.ok(process.argv[2] && process.argv[3] && process.argv[4], + "usage: replay-preserved-case09.mjs "); + +const workspace = await mkdtemp(join(tmpdir(), "pilotdeck-v24r10-case09-replay-")); +const stateRoot = join(workspace, ".pilotdeck", "work", "legal-coverage"); +const fragmentRoot = join(stateRoot, "fragments"); +const pluginRoot = join(workspace, ".pilotdeck", "plugins", "legal-coverage"); +const candidatePlugin = join(candidateRoot, "products", "legal", "plugins", "legal-coverage"); +const cli = join(pluginRoot, "scripts", "legal-coverage.mjs"); +const hook = join(pluginRoot, "hook.mjs"); + +try { + await cp(sourceRun, workspace, { recursive: true }); + await rm(pluginRoot, { recursive: true, force: true }); + await cp(candidatePlugin, pluginRoot, { recursive: true }); + + const repairName = (await readdir(fragmentRoot)) + .find((name) => /^source-repair-[a-f0-9]{12}\.json$/u.test(name)); + assert.ok(repairName, "preserved failed run must contain the rejected immutable repair"); + const repairRelativePath = `.pilotdeck/work/legal-coverage/fragments/${repairName}`; + const repairPath = join(fragmentRoot, repairName); + + const invalidSessionId = "v24r10-case09-invalid-repair-replay"; + await runHook({ + hookEventName: "PreModelRequest", + sessionId: invalidSessionId, + transcriptPath: "", + cwd: workspace, + }); + await runHook({ + hookEventName: "PostToolUse", + sessionId: invalidSessionId, + transcriptPath: "", + cwd: workspace, + toolName: "write_file", + toolInput: { file_path: repairRelativePath }, + toolUseId: "preserved-invalid-repair-write", + }); + const afterInvalidWrite = await runHook({ + hookEventName: "PreModelRequest", + sessionId: invalidSessionId, + transcriptPath: "", + cwd: workspace, + }); + assert.equal(convergence(afterInvalidWrite).repairPreparationOrdinal, 0, + "invalid preserved repair must not advance preparation"); + + await rm(repairPath); + const sessionId = "v24r10-case09-valid-repair-replay"; + const rejected = await runHook({ + hookEventName: "PreModelRequest", + sessionId, + transcriptPath: "", + cwd: workspace, + }); + const repairPlan = legalEnvelope(rejected).workItems.repair; + const template = structuredClone(repairPlan.template); + const evidenceOperation = template.operations.find((operation) => operation.factNumber === 5); + const expenseOperation = template.operations.find((operation) => operation.factNumber === 11); + assert.equal(evidenceOperation?.fact?.evidenceClass, "official-record"); + assert.equal(expenseOperation?.fact?.evidenceClass, "company-disclosure"); + + const locatorOperation = template.operations.find((operation) => operation.factNumber === 9); + assert.ok(locatorOperation?.fact?.sourceRefs?.[0]); + const sourceContext = repairPlan.repairSlice.sourceContext.find( + (source) => source.sourceId === locatorOperation.fact.sourceRefs[0].sourceId, + ); + const exactLocator = sourceContext?.allowedFragmentFacts.find( + (fact) => fact.locator.includes("line 9-10"), + )?.locator; + assert.ok(exactLocator, "preserved repair context must include the exact bounded locator"); + locatorOperation.fact.sourceRefs[0].locator = exactLocator; + await writeJson(repairPath, template); + + await runHook({ + hookEventName: "PostToolUse", + sessionId, + transcriptPath: "", + cwd: workspace, + toolName: "write_file", + toolInput: { file_path: repairRelativePath }, + toolUseId: "preserved-valid-repair-write", + }); + const applyReady = await runHook({ + hookEventName: "PreModelRequest", + sessionId, + transcriptPath: "", + cwd: workspace, + }); + const applyEnvelope = legalEnvelope(applyReady); + const beforeProgress = convergence(applyReady).progressOrdinal; + assert.equal(convergence(applyReady).repairPreparationOrdinal, 1); + assert.equal(applyEnvelope.workItems.group, "source-fragment-repair-apply"); + assert.equal(applyEnvelope.workItems.repair.validated, true); + assert.match(applyEnvelope.sourceMergeRepairApplyCommand, /source-repair-apply/u); + + const applied = await execFileAsync(process.execPath, [ + cli, + "source-repair-apply", + "--workspace", workspace, + "--input-file", applyEnvelope.workItems.repair.path, + "--repair-sha256", applyEnvelope.workItems.repair.repairSha256, + ], { encoding: "utf8" }); + const appliedResult = JSON.parse(applied.stdout); + assert.equal(appliedResult.applied, true); + + const afterApply = await runHook({ + hookEventName: "PreModelRequest", + sessionId, + transcriptPath: "", + cwd: workspace, + }); + const afterEnvelope = legalEnvelope(afterApply); + const afterProgress = convergence(afterApply).progressOrdinal; + assert.ok(afterEnvelope.workItems.appliedRepair); + assert.equal(afterProgress, beforeProgress + 1); + + const replay = await runHook({ + hookEventName: "PreModelRequest", + sessionId, + transcriptPath: "", + cwd: workspace, + }); + const replayProgress = convergence(replay).progressOrdinal; + assert.equal(replayProgress, afterProgress); + + const sources = JSON.parse(await readFile(join(stateRoot, "sources.json"), "utf8")); + const facts = JSON.parse(await readFile(join(stateRoot, "facts.json"), "utf8")); + const appliedReceipt = afterEnvelope.workItems.appliedRepair.path; + const result = { + passed: true, + fixture: "preserved-v24r9-case09-invalid-repair", + rejectedRepair: basename(repairName), + appliedReceipt: basename(appliedReceipt), + invalidRepairPreparationOrdinal: convergence(afterInvalidWrite).repairPreparationOrdinal, + validRepairPreparationOrdinal: convergence(applyReady).repairPreparationOrdinal, + progressOrdinal: [beforeProgress, afterProgress, replayProgress], + appliedSourceCount: appliedResult.sourceCount, + appliedFactCount: appliedResult.factCount, + reviewedSources: sources.sources.filter((source) => source.status === "reviewed").length, + pendingSources: sources.sources.filter((source) => source.status === "pending").length, + totalFacts: facts.facts.length, + stateHashAfter: appliedResult.stateHash, + repairSha256: sha256(await readFile(repairPath)), + nextGroup: afterEnvelope.workItems.group, + }; + await mkdir(resolve(outputPath, ".."), { recursive: true }); + await writeJson(outputPath, result); + process.stdout.write(`${JSON.stringify(result, null, 2)}\n`); +} finally { + await rm(workspace, { recursive: true, force: true }); +} + +function sha256(bytes) { + return createHash("sha256").update(bytes).digest("hex"); +} + +function legalEnvelope(output) { + return JSON.parse((output.hookSpecificOutput.additionalContext ?? "") + .replace(/^\n/u, "") + .replace(/\n<\/legal_coverage_state>$/u, "")); +} + +function convergence(output) { + return output.hookSpecificOutput.modelRequestPatch.metadata.pilotdeckConvergence; +} + +async function runHook(input) { + return new Promise((resolvePromise, reject) => { + const child = spawn(process.execPath, [hook], { stdio: ["pipe", "pipe", "pipe"] }); + let stdout = ""; + let stderr = ""; + child.stdout.setEncoding("utf8"); + child.stderr.setEncoding("utf8"); + child.stdout.on("data", (chunk) => { stdout += chunk; }); + child.stderr.on("data", (chunk) => { stderr += chunk; }); + child.on("error", reject); + child.on("close", (code) => { + if (code !== 0) reject(new Error(stderr || `hook exited with code ${code}`)); + else resolvePromise(JSON.parse(stdout)); + }); + child.stdin.end(JSON.stringify(input)); + }); +} + +async function writeJson(path, value) { + await writeFile(path, `${JSON.stringify(value, null, 2)}\n`); +}