@kontourai/flow-agents 3.12.1 → 4.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.github/workflows/ci.yml +76 -0
- package/CHANGELOG.md +41 -0
- package/build/generated/capability-declarations.json +177 -0
- package/build/src/builder-flow-runtime.d.ts +37 -0
- package/build/src/builder-flow-runtime.js +71 -1
- package/build/src/builder-gate-action-envelope.d.ts +58 -7
- package/build/src/builder-gate-action-envelope.js +160 -28
- package/build/src/builder-lifecycle-authority.d.ts +26 -0
- package/build/src/builder-lifecycle-authority.js +38 -0
- package/build/src/cli/builder-run.js +71 -2
- package/build/src/cli/narrative-render.d.ts +95 -0
- package/build/src/cli/narrative-render.js +466 -0
- package/build/src/cli/narrative-sources.d.ts +2 -0
- package/build/src/cli/narrative-sources.js +282 -0
- package/build/src/cli/public-contracts.d.ts +257 -0
- package/build/src/cli/public-contracts.js +87 -0
- package/build/src/cli/workflow-sidecar.d.ts +28 -12
- package/build/src/cli/workflow-sidecar.js +599 -180
- package/build/src/cli/workflow.js +11 -1
- package/build/src/cli.js +8 -0
- package/build/src/continuation-driver.d.ts +12 -0
- package/build/src/continuation-driver.js +16 -2
- package/build/src/continuation-validation.js +324 -8
- package/build/src/flow-kit/action-metadata.js +21 -1
- package/build/src/index.d.ts +33 -2
- package/build/src/index.js +24 -0
- package/build/src/lib/capability-declarations.d.ts +83 -0
- package/build/src/lib/capability-declarations.js +209 -0
- package/build/src/narrative/envelope.d.ts +111 -0
- package/build/src/narrative/envelope.js +590 -0
- package/build/src/narrative/eval-result.d.ts +60 -0
- package/build/src/narrative/eval-result.js +158 -0
- package/build/src/narrative/grounding-validator.d.ts +104 -0
- package/build/src/narrative/grounding-validator.js +472 -0
- package/build/src/narrative/integrity.d.ts +17 -0
- package/build/src/narrative/integrity.js +39 -0
- package/build/src/narrative/intent-economics.d.ts +42 -0
- package/build/src/narrative/intent-economics.js +82 -0
- package/build/src/narrative/intent.d.ts +94 -0
- package/build/src/narrative/intent.js +132 -0
- package/build/src/narrative/policy-filter.d.ts +15 -0
- package/build/src/narrative/policy-filter.js +58 -0
- package/build/src/narrative/projection.d.ts +58 -0
- package/build/src/narrative/projection.js +502 -0
- package/build/src/narrative/readers.d.ts +84 -0
- package/build/src/narrative/readers.js +406 -0
- package/build/src/narrative/render.d.ts +3 -0
- package/build/src/narrative/render.js +83 -0
- package/build/src/narrative/resolver.d.ts +52 -0
- package/build/src/narrative/resolver.js +190 -0
- package/build/src/narrative/snapshot.d.ts +90 -0
- package/build/src/narrative/snapshot.js +325 -0
- package/build/src/narrative/source-ids.d.ts +121 -0
- package/build/src/narrative/source-ids.js +201 -0
- package/build/src/narrative/statements.d.ts +84 -0
- package/build/src/narrative/statements.js +347 -0
- package/build/src/narrative/turn-spine.d.ts +19 -0
- package/build/src/narrative/turn-spine.js +73 -0
- package/build/src/tools/build-universal-bundles.js +8 -0
- package/build/src/tools/generate-capability-matrix.d.ts +7 -0
- package/build/src/tools/generate-capability-matrix.js +207 -0
- package/build/src/tools/validate-source-tree.js +5 -1
- package/context/scripts/hooks/stop-goal-fit.js +182 -9
- package/context/scripts/hooks/workflow-steering.js +160 -19
- package/context/scripts/telemetry/lib/session.sh +3 -0
- package/context/scripts/telemetry/lib/transport.sh +12 -3
- package/context/scripts/telemetry/telemetry.sh +480 -59
- package/dist/base/build/generated/capability-declarations.json +177 -0
- package/dist/base/build/package.json +1 -1
- package/dist/base/build/src/builder-flow-runtime.d.ts +37 -0
- package/dist/base/build/src/builder-flow-runtime.js +71 -1
- package/dist/base/build/src/builder-gate-action-envelope.d.ts +58 -7
- package/dist/base/build/src/builder-gate-action-envelope.js +160 -28
- package/dist/base/build/src/builder-lifecycle-authority.d.ts +26 -0
- package/dist/base/build/src/builder-lifecycle-authority.js +38 -0
- package/dist/base/build/src/cli/builder-run.js +71 -2
- package/dist/base/build/src/cli/narrative-render.d.ts +95 -0
- package/dist/base/build/src/cli/narrative-render.js +466 -0
- package/dist/base/build/src/cli/narrative-sources.d.ts +2 -0
- package/dist/base/build/src/cli/narrative-sources.js +282 -0
- package/dist/base/build/src/cli/public-contracts.d.ts +257 -0
- package/dist/base/build/src/cli/public-contracts.js +87 -0
- package/dist/base/build/src/cli/workflow-sidecar.d.ts +28 -12
- package/dist/base/build/src/cli/workflow-sidecar.js +599 -180
- package/dist/base/build/src/cli/workflow.js +11 -1
- package/dist/base/build/src/cli.js +8 -0
- package/dist/base/build/src/continuation-driver.d.ts +12 -0
- package/dist/base/build/src/continuation-driver.js +16 -2
- package/dist/base/build/src/continuation-validation.js +324 -8
- package/dist/base/build/src/flow-kit/action-metadata.js +21 -1
- package/dist/base/build/src/index.d.ts +33 -2
- package/dist/base/build/src/index.js +24 -0
- package/dist/base/build/src/lib/capability-declarations.d.ts +83 -0
- package/dist/base/build/src/lib/capability-declarations.js +209 -0
- package/dist/base/build/src/narrative/envelope.d.ts +111 -0
- package/dist/base/build/src/narrative/envelope.js +590 -0
- package/dist/base/build/src/narrative/eval-result.d.ts +60 -0
- package/dist/base/build/src/narrative/eval-result.js +158 -0
- package/dist/base/build/src/narrative/grounding-validator.d.ts +104 -0
- package/dist/base/build/src/narrative/grounding-validator.js +472 -0
- package/dist/base/build/src/narrative/integrity.d.ts +17 -0
- package/dist/base/build/src/narrative/integrity.js +39 -0
- package/dist/base/build/src/narrative/intent-economics.d.ts +42 -0
- package/dist/base/build/src/narrative/intent-economics.js +82 -0
- package/dist/base/build/src/narrative/intent.d.ts +94 -0
- package/dist/base/build/src/narrative/intent.js +132 -0
- package/dist/base/build/src/narrative/policy-filter.d.ts +15 -0
- package/dist/base/build/src/narrative/policy-filter.js +58 -0
- package/dist/base/build/src/narrative/projection.d.ts +58 -0
- package/dist/base/build/src/narrative/projection.js +502 -0
- package/dist/base/build/src/narrative/readers.d.ts +84 -0
- package/dist/base/build/src/narrative/readers.js +406 -0
- package/dist/base/build/src/narrative/render.d.ts +3 -0
- package/dist/base/build/src/narrative/render.js +83 -0
- package/dist/base/build/src/narrative/resolver.d.ts +52 -0
- package/dist/base/build/src/narrative/resolver.js +190 -0
- package/dist/base/build/src/narrative/snapshot.d.ts +90 -0
- package/dist/base/build/src/narrative/snapshot.js +325 -0
- package/dist/base/build/src/narrative/source-ids.d.ts +121 -0
- package/dist/base/build/src/narrative/source-ids.js +201 -0
- package/dist/base/build/src/narrative/statements.d.ts +84 -0
- package/dist/base/build/src/narrative/statements.js +347 -0
- package/dist/base/build/src/narrative/turn-spine.d.ts +19 -0
- package/dist/base/build/src/narrative/turn-spine.js +73 -0
- package/dist/base/build/src/tools/build-universal-bundles.js +8 -0
- package/dist/base/build/src/tools/generate-capability-matrix.d.ts +7 -0
- package/dist/base/build/src/tools/generate-capability-matrix.js +207 -0
- package/dist/base/build/src/tools/validate-source-tree.js +5 -1
- package/dist/base/context/scripts/hooks/stop-goal-fit.js +182 -9
- package/dist/base/context/scripts/hooks/workflow-steering.js +160 -19
- package/dist/base/context/scripts/telemetry/lib/session.sh +3 -0
- package/dist/base/context/scripts/telemetry/lib/transport.sh +12 -3
- package/dist/base/context/scripts/telemetry/telemetry.sh +480 -59
- package/dist/base/docs/context-map.md +4 -0
- package/dist/base/docs/decisions/index.md +1 -0
- package/dist/base/docs/decisions/writer-observed-execution.md +51 -0
- package/dist/base/docs/fixture-ownership.md +5 -1
- package/dist/base/docs/migrations.md +19 -4
- package/dist/base/docs/public-workflow-cli.md +9 -0
- package/dist/base/docs/spec/builder-flow-runtime.md +49 -8
- package/dist/base/docs/spec/runtime-hook-surface.md +29 -1
- package/dist/base/docs/specs/economics-record-contract.md +1 -1
- package/dist/base/docs/specs/harness-capability-matrix.md +63 -21
- package/dist/base/docs/workflow-usage-guide.md +4 -1
- package/dist/base/evals/ci/run-baseline.sh +38 -0
- package/dist/base/evals/fixtures/narrative-evals/README.md +62 -0
- package/dist/base/evals/fixtures/narrative-evals/corpus.json +129 -0
- package/dist/base/evals/fixtures/narrative-evals/scorer.mjs +522 -0
- package/dist/base/evals/fixtures/narrative-grounding-validator/corpus.json +20 -0
- package/dist/base/evals/fixtures/narrative-grounding-validator/prompt-injection-adversarial.txt +1 -0
- package/dist/base/evals/fixtures/narrative-grounding-validator/prompt-injection-control.txt +1 -0
- package/dist/base/evals/fixtures/narrative-grounding-validator/scorer.mjs +201 -0
- package/dist/base/evals/fixtures/narrative-prose-renderer/corpus.json +15 -0
- package/dist/base/evals/fixtures/narrative-prose-renderer/scorer.mjs +201 -0
- package/dist/base/evals/fixtures/narrative-sources/expected-capture-completeness.json +13 -0
- package/dist/base/evals/fixtures/narrative-sources/flow/runs/run-fixture/report.json +1 -0
- package/dist/base/evals/fixtures/narrative-sources/flow/runs/run-fixture/state.json +1 -0
- package/dist/base/evals/fixtures/narrative-sources/flow/runs/run-runtime/state.json +1 -0
- package/dist/base/evals/fixtures/narrative-sources/repo/created.txt +1 -0
- package/dist/base/evals/fixtures/narrative-sources/repo/fixture.txt +1 -0
- package/dist/base/evals/fixtures/narrative-sources/session/agents/agent-fixture/events.jsonl +2 -0
- package/dist/base/evals/fixtures/narrative-sources/session/agents/nested-worker/events.jsonl +1 -0
- package/dist/base/evals/fixtures/narrative-sources/session/trust.bundle +1 -0
- package/dist/base/evals/fixtures/narrative-sources/telemetry/full.jsonl +3 -0
- package/dist/base/evals/fixtures/narrative-sources/telemetry/runtime.jsonl +4 -0
- package/dist/base/evals/fixtures/narrative-sources/transcripts/session.txt +1 -0
- package/dist/base/evals/integration/test_capability_declarations.sh +183 -0
- package/dist/base/evals/integration/test_economics_record.sh +42 -0
- package/dist/base/evals/integration/test_fixture_retirement_audit.sh +2 -2
- package/dist/base/evals/integration/test_gate_review_inquiry_records.sh +42 -35
- package/dist/base/evals/integration/test_goal_fit_hook.sh +13 -5
- package/dist/base/evals/integration/test_goal_fit_narrative_exclusion.sh +68 -0
- package/dist/base/evals/integration/test_narrative_evals.sh +174 -0
- package/dist/base/evals/integration/test_narrative_grounded_envelope.sh +280 -0
- package/dist/base/evals/integration/test_narrative_grounding_validator.sh +166 -0
- package/dist/base/evals/integration/test_narrative_intent_annotation.sh +205 -0
- package/dist/base/evals/integration/test_narrative_prose_renderer.sh +239 -0
- package/dist/base/evals/integration/test_narrative_redaction_failclosed.sh +120 -0
- package/dist/base/evals/integration/test_narrative_runtime_projection.sh +146 -0
- package/dist/base/evals/integration/test_narrative_source_contract.sh +228 -0
- package/dist/base/evals/integration/test_narrative_trust_isolation.sh +408 -0
- package/dist/base/evals/integration/test_plain_stop_messaging.sh +91 -0
- package/dist/base/evals/integration/test_session_resume_roundtrip.sh +228 -0
- package/dist/base/evals/integration/test_telemetry_action_class.sh +265 -0
- package/dist/base/evals/integration/test_telemetry_delegation.sh +291 -0
- package/dist/base/evals/integration/test_telemetry_sanitize_usage.sh +155 -0
- package/dist/base/evals/integration/test_telemetry_task_slug.sh +158 -0
- package/dist/base/evals/integration/test_telemetry_tool_outcome.sh +279 -0
- package/dist/base/evals/integration/test_workflow_sidecar_writer.sh +111 -2
- package/dist/base/evals/run.sh +12 -0
- package/dist/base/evals/static/test_library_exports.sh +5 -0
- package/dist/base/install.sh +1 -1
- package/dist/base/schemas/grounded-execution-narrative.schema.json +292 -0
- package/dist/base/schemas/narrative-eval-result.schema.json +205 -0
- package/dist/base/schemas/narrative-runtime-projection.schema.json +134 -0
- package/dist/base/schemas/narrative-source-manifest.schema.json +294 -0
- package/dist/base/scripts/check-hachure-boundary.mjs +81 -0
- package/dist/base/scripts/hooks/stop-goal-fit.js +182 -9
- package/dist/base/scripts/hooks/workflow-steering.js +160 -19
- package/dist/base/scripts/telemetry/economics-record.schema.json +1 -1
- package/dist/base/scripts/telemetry/economics-record.sh +47 -3
- package/dist/base/scripts/telemetry/lib/session.sh +3 -0
- package/dist/base/scripts/telemetry/lib/transport.sh +12 -3
- package/dist/base/scripts/telemetry/telemetry.sh +480 -59
- package/dist/claude-code/build/generated/capability-declarations.json +177 -0
- package/dist/claude-code/build/package.json +1 -1
- package/dist/claude-code/build/src/builder-flow-runtime.d.ts +37 -0
- package/dist/claude-code/build/src/builder-flow-runtime.js +71 -1
- package/dist/claude-code/build/src/builder-gate-action-envelope.d.ts +58 -7
- package/dist/claude-code/build/src/builder-gate-action-envelope.js +160 -28
- package/dist/claude-code/build/src/builder-lifecycle-authority.d.ts +26 -0
- package/dist/claude-code/build/src/builder-lifecycle-authority.js +38 -0
- package/dist/claude-code/build/src/cli/builder-run.js +71 -2
- package/dist/claude-code/build/src/cli/narrative-render.d.ts +95 -0
- package/dist/claude-code/build/src/cli/narrative-render.js +466 -0
- package/dist/claude-code/build/src/cli/narrative-sources.d.ts +2 -0
- package/dist/claude-code/build/src/cli/narrative-sources.js +282 -0
- package/dist/claude-code/build/src/cli/public-contracts.d.ts +257 -0
- package/dist/claude-code/build/src/cli/public-contracts.js +87 -0
- package/dist/claude-code/build/src/cli/workflow-sidecar.d.ts +28 -12
- package/dist/claude-code/build/src/cli/workflow-sidecar.js +599 -180
- package/dist/claude-code/build/src/cli/workflow.js +11 -1
- package/dist/claude-code/build/src/cli.js +8 -0
- package/dist/claude-code/build/src/continuation-driver.d.ts +12 -0
- package/dist/claude-code/build/src/continuation-driver.js +16 -2
- package/dist/claude-code/build/src/continuation-validation.js +324 -8
- package/dist/claude-code/build/src/flow-kit/action-metadata.js +21 -1
- package/dist/claude-code/build/src/index.d.ts +33 -2
- package/dist/claude-code/build/src/index.js +24 -0
- package/dist/claude-code/build/src/lib/capability-declarations.d.ts +83 -0
- package/dist/claude-code/build/src/lib/capability-declarations.js +209 -0
- package/dist/claude-code/build/src/narrative/envelope.d.ts +111 -0
- package/dist/claude-code/build/src/narrative/envelope.js +590 -0
- package/dist/claude-code/build/src/narrative/eval-result.d.ts +60 -0
- package/dist/claude-code/build/src/narrative/eval-result.js +158 -0
- package/dist/claude-code/build/src/narrative/grounding-validator.d.ts +104 -0
- package/dist/claude-code/build/src/narrative/grounding-validator.js +472 -0
- package/dist/claude-code/build/src/narrative/integrity.d.ts +17 -0
- package/dist/claude-code/build/src/narrative/integrity.js +39 -0
- package/dist/claude-code/build/src/narrative/intent-economics.d.ts +42 -0
- package/dist/claude-code/build/src/narrative/intent-economics.js +82 -0
- package/dist/claude-code/build/src/narrative/intent.d.ts +94 -0
- package/dist/claude-code/build/src/narrative/intent.js +132 -0
- package/dist/claude-code/build/src/narrative/policy-filter.d.ts +15 -0
- package/dist/claude-code/build/src/narrative/policy-filter.js +58 -0
- package/dist/claude-code/build/src/narrative/projection.d.ts +58 -0
- package/dist/claude-code/build/src/narrative/projection.js +502 -0
- package/dist/claude-code/build/src/narrative/readers.d.ts +84 -0
- package/dist/claude-code/build/src/narrative/readers.js +406 -0
- package/dist/claude-code/build/src/narrative/render.d.ts +3 -0
- package/dist/claude-code/build/src/narrative/render.js +83 -0
- package/dist/claude-code/build/src/narrative/resolver.d.ts +52 -0
- package/dist/claude-code/build/src/narrative/resolver.js +190 -0
- package/dist/claude-code/build/src/narrative/snapshot.d.ts +90 -0
- package/dist/claude-code/build/src/narrative/snapshot.js +325 -0
- package/dist/claude-code/build/src/narrative/source-ids.d.ts +121 -0
- package/dist/claude-code/build/src/narrative/source-ids.js +201 -0
- package/dist/claude-code/build/src/narrative/statements.d.ts +84 -0
- package/dist/claude-code/build/src/narrative/statements.js +347 -0
- package/dist/claude-code/build/src/narrative/turn-spine.d.ts +19 -0
- package/dist/claude-code/build/src/narrative/turn-spine.js +73 -0
- package/dist/claude-code/build/src/tools/build-universal-bundles.js +8 -0
- package/dist/claude-code/build/src/tools/generate-capability-matrix.d.ts +7 -0
- package/dist/claude-code/build/src/tools/generate-capability-matrix.js +207 -0
- package/dist/claude-code/build/src/tools/validate-source-tree.js +5 -1
- package/dist/claude-code/context/scripts/hooks/stop-goal-fit.js +182 -9
- package/dist/claude-code/context/scripts/hooks/workflow-steering.js +160 -19
- package/dist/claude-code/context/scripts/telemetry/lib/session.sh +3 -0
- package/dist/claude-code/context/scripts/telemetry/lib/transport.sh +12 -3
- package/dist/claude-code/context/scripts/telemetry/telemetry.sh +480 -59
- package/dist/claude-code/docs/context-map.md +4 -0
- package/dist/claude-code/docs/decisions/index.md +1 -0
- package/dist/claude-code/docs/decisions/writer-observed-execution.md +51 -0
- package/dist/claude-code/docs/fixture-ownership.md +5 -1
- package/dist/claude-code/docs/migrations.md +19 -4
- package/dist/claude-code/docs/public-workflow-cli.md +9 -0
- package/dist/claude-code/docs/spec/builder-flow-runtime.md +49 -8
- package/dist/claude-code/docs/spec/runtime-hook-surface.md +29 -1
- package/dist/claude-code/docs/specs/economics-record-contract.md +1 -1
- package/dist/claude-code/docs/specs/harness-capability-matrix.md +63 -21
- package/dist/claude-code/docs/workflow-usage-guide.md +4 -1
- package/dist/claude-code/evals/ci/run-baseline.sh +38 -0
- package/dist/claude-code/evals/fixtures/narrative-evals/README.md +62 -0
- package/dist/claude-code/evals/fixtures/narrative-evals/corpus.json +129 -0
- package/dist/claude-code/evals/fixtures/narrative-evals/scorer.mjs +522 -0
- package/dist/claude-code/evals/fixtures/narrative-grounding-validator/corpus.json +20 -0
- package/dist/claude-code/evals/fixtures/narrative-grounding-validator/prompt-injection-adversarial.txt +1 -0
- package/dist/claude-code/evals/fixtures/narrative-grounding-validator/prompt-injection-control.txt +1 -0
- package/dist/claude-code/evals/fixtures/narrative-grounding-validator/scorer.mjs +201 -0
- package/dist/claude-code/evals/fixtures/narrative-prose-renderer/corpus.json +15 -0
- package/dist/claude-code/evals/fixtures/narrative-prose-renderer/scorer.mjs +201 -0
- package/dist/claude-code/evals/fixtures/narrative-sources/expected-capture-completeness.json +13 -0
- package/dist/claude-code/evals/fixtures/narrative-sources/flow/runs/run-fixture/report.json +1 -0
- package/dist/claude-code/evals/fixtures/narrative-sources/flow/runs/run-fixture/state.json +1 -0
- package/dist/claude-code/evals/fixtures/narrative-sources/flow/runs/run-runtime/state.json +1 -0
- package/dist/claude-code/evals/fixtures/narrative-sources/repo/created.txt +1 -0
- package/dist/claude-code/evals/fixtures/narrative-sources/repo/fixture.txt +1 -0
- package/dist/claude-code/evals/fixtures/narrative-sources/session/agents/agent-fixture/events.jsonl +2 -0
- package/dist/claude-code/evals/fixtures/narrative-sources/session/agents/nested-worker/events.jsonl +1 -0
- package/dist/claude-code/evals/fixtures/narrative-sources/session/trust.bundle +1 -0
- package/dist/claude-code/evals/fixtures/narrative-sources/telemetry/full.jsonl +3 -0
- package/dist/claude-code/evals/fixtures/narrative-sources/telemetry/runtime.jsonl +4 -0
- package/dist/claude-code/evals/fixtures/narrative-sources/transcripts/session.txt +1 -0
- package/dist/claude-code/evals/integration/test_capability_declarations.sh +183 -0
- package/dist/claude-code/evals/integration/test_economics_record.sh +42 -0
- package/dist/claude-code/evals/integration/test_fixture_retirement_audit.sh +2 -2
- package/dist/claude-code/evals/integration/test_gate_review_inquiry_records.sh +42 -35
- package/dist/claude-code/evals/integration/test_goal_fit_hook.sh +13 -5
- package/dist/claude-code/evals/integration/test_goal_fit_narrative_exclusion.sh +68 -0
- package/dist/claude-code/evals/integration/test_narrative_evals.sh +174 -0
- package/dist/claude-code/evals/integration/test_narrative_grounded_envelope.sh +280 -0
- package/dist/claude-code/evals/integration/test_narrative_grounding_validator.sh +166 -0
- package/dist/claude-code/evals/integration/test_narrative_intent_annotation.sh +205 -0
- package/dist/claude-code/evals/integration/test_narrative_prose_renderer.sh +239 -0
- package/dist/claude-code/evals/integration/test_narrative_redaction_failclosed.sh +120 -0
- package/dist/claude-code/evals/integration/test_narrative_runtime_projection.sh +146 -0
- package/dist/claude-code/evals/integration/test_narrative_source_contract.sh +228 -0
- package/dist/claude-code/evals/integration/test_narrative_trust_isolation.sh +408 -0
- package/dist/claude-code/evals/integration/test_plain_stop_messaging.sh +91 -0
- package/dist/claude-code/evals/integration/test_session_resume_roundtrip.sh +228 -0
- package/dist/claude-code/evals/integration/test_telemetry_action_class.sh +265 -0
- package/dist/claude-code/evals/integration/test_telemetry_delegation.sh +291 -0
- package/dist/claude-code/evals/integration/test_telemetry_sanitize_usage.sh +155 -0
- package/dist/claude-code/evals/integration/test_telemetry_task_slug.sh +158 -0
- package/dist/claude-code/evals/integration/test_telemetry_tool_outcome.sh +279 -0
- package/dist/claude-code/evals/integration/test_workflow_sidecar_writer.sh +111 -2
- package/dist/claude-code/evals/run.sh +12 -0
- package/dist/claude-code/evals/static/test_library_exports.sh +5 -0
- package/dist/claude-code/install.sh +1 -1
- package/dist/claude-code/schemas/grounded-execution-narrative.schema.json +292 -0
- package/dist/claude-code/schemas/narrative-eval-result.schema.json +205 -0
- package/dist/claude-code/schemas/narrative-runtime-projection.schema.json +134 -0
- package/dist/claude-code/schemas/narrative-source-manifest.schema.json +294 -0
- package/dist/claude-code/scripts/check-hachure-boundary.mjs +81 -0
- package/dist/claude-code/scripts/hooks/stop-goal-fit.js +182 -9
- package/dist/claude-code/scripts/hooks/workflow-steering.js +160 -19
- package/dist/claude-code/scripts/telemetry/economics-record.schema.json +1 -1
- package/dist/claude-code/scripts/telemetry/economics-record.sh +47 -3
- package/dist/claude-code/scripts/telemetry/lib/session.sh +3 -0
- package/dist/claude-code/scripts/telemetry/lib/transport.sh +12 -3
- package/dist/claude-code/scripts/telemetry/telemetry.sh +480 -59
- package/dist/codex/build/generated/capability-declarations.json +177 -0
- package/dist/codex/build/package.json +1 -1
- package/dist/codex/build/src/builder-flow-runtime.d.ts +37 -0
- package/dist/codex/build/src/builder-flow-runtime.js +71 -1
- package/dist/codex/build/src/builder-gate-action-envelope.d.ts +58 -7
- package/dist/codex/build/src/builder-gate-action-envelope.js +160 -28
- package/dist/codex/build/src/builder-lifecycle-authority.d.ts +26 -0
- package/dist/codex/build/src/builder-lifecycle-authority.js +38 -0
- package/dist/codex/build/src/cli/builder-run.js +71 -2
- package/dist/codex/build/src/cli/narrative-render.d.ts +95 -0
- package/dist/codex/build/src/cli/narrative-render.js +466 -0
- package/dist/codex/build/src/cli/narrative-sources.d.ts +2 -0
- package/dist/codex/build/src/cli/narrative-sources.js +282 -0
- package/dist/codex/build/src/cli/public-contracts.d.ts +257 -0
- package/dist/codex/build/src/cli/public-contracts.js +87 -0
- package/dist/codex/build/src/cli/workflow-sidecar.d.ts +28 -12
- package/dist/codex/build/src/cli/workflow-sidecar.js +599 -180
- package/dist/codex/build/src/cli/workflow.js +11 -1
- package/dist/codex/build/src/cli.js +8 -0
- package/dist/codex/build/src/continuation-driver.d.ts +12 -0
- package/dist/codex/build/src/continuation-driver.js +16 -2
- package/dist/codex/build/src/continuation-validation.js +324 -8
- package/dist/codex/build/src/flow-kit/action-metadata.js +21 -1
- package/dist/codex/build/src/index.d.ts +33 -2
- package/dist/codex/build/src/index.js +24 -0
- package/dist/codex/build/src/lib/capability-declarations.d.ts +83 -0
- package/dist/codex/build/src/lib/capability-declarations.js +209 -0
- package/dist/codex/build/src/narrative/envelope.d.ts +111 -0
- package/dist/codex/build/src/narrative/envelope.js +590 -0
- package/dist/codex/build/src/narrative/eval-result.d.ts +60 -0
- package/dist/codex/build/src/narrative/eval-result.js +158 -0
- package/dist/codex/build/src/narrative/grounding-validator.d.ts +104 -0
- package/dist/codex/build/src/narrative/grounding-validator.js +472 -0
- package/dist/codex/build/src/narrative/integrity.d.ts +17 -0
- package/dist/codex/build/src/narrative/integrity.js +39 -0
- package/dist/codex/build/src/narrative/intent-economics.d.ts +42 -0
- package/dist/codex/build/src/narrative/intent-economics.js +82 -0
- package/dist/codex/build/src/narrative/intent.d.ts +94 -0
- package/dist/codex/build/src/narrative/intent.js +132 -0
- package/dist/codex/build/src/narrative/policy-filter.d.ts +15 -0
- package/dist/codex/build/src/narrative/policy-filter.js +58 -0
- package/dist/codex/build/src/narrative/projection.d.ts +58 -0
- package/dist/codex/build/src/narrative/projection.js +502 -0
- package/dist/codex/build/src/narrative/readers.d.ts +84 -0
- package/dist/codex/build/src/narrative/readers.js +406 -0
- package/dist/codex/build/src/narrative/render.d.ts +3 -0
- package/dist/codex/build/src/narrative/render.js +83 -0
- package/dist/codex/build/src/narrative/resolver.d.ts +52 -0
- package/dist/codex/build/src/narrative/resolver.js +190 -0
- package/dist/codex/build/src/narrative/snapshot.d.ts +90 -0
- package/dist/codex/build/src/narrative/snapshot.js +325 -0
- package/dist/codex/build/src/narrative/source-ids.d.ts +121 -0
- package/dist/codex/build/src/narrative/source-ids.js +201 -0
- package/dist/codex/build/src/narrative/statements.d.ts +84 -0
- package/dist/codex/build/src/narrative/statements.js +347 -0
- package/dist/codex/build/src/narrative/turn-spine.d.ts +19 -0
- package/dist/codex/build/src/narrative/turn-spine.js +73 -0
- package/dist/codex/build/src/tools/build-universal-bundles.js +8 -0
- package/dist/codex/build/src/tools/generate-capability-matrix.d.ts +7 -0
- package/dist/codex/build/src/tools/generate-capability-matrix.js +207 -0
- package/dist/codex/build/src/tools/validate-source-tree.js +5 -1
- package/dist/codex/context/scripts/hooks/stop-goal-fit.js +182 -9
- package/dist/codex/context/scripts/hooks/workflow-steering.js +160 -19
- package/dist/codex/context/scripts/telemetry/lib/session.sh +3 -0
- package/dist/codex/context/scripts/telemetry/lib/transport.sh +12 -3
- package/dist/codex/context/scripts/telemetry/telemetry.sh +480 -59
- package/dist/codex/docs/context-map.md +4 -0
- package/dist/codex/docs/decisions/index.md +1 -0
- package/dist/codex/docs/decisions/writer-observed-execution.md +51 -0
- package/dist/codex/docs/fixture-ownership.md +5 -1
- package/dist/codex/docs/migrations.md +19 -4
- package/dist/codex/docs/public-workflow-cli.md +9 -0
- package/dist/codex/docs/spec/builder-flow-runtime.md +49 -8
- package/dist/codex/docs/spec/runtime-hook-surface.md +29 -1
- package/dist/codex/docs/specs/economics-record-contract.md +1 -1
- package/dist/codex/docs/specs/harness-capability-matrix.md +63 -21
- package/dist/codex/docs/workflow-usage-guide.md +4 -1
- package/dist/codex/evals/ci/run-baseline.sh +38 -0
- package/dist/codex/evals/fixtures/narrative-evals/README.md +62 -0
- package/dist/codex/evals/fixtures/narrative-evals/corpus.json +129 -0
- package/dist/codex/evals/fixtures/narrative-evals/scorer.mjs +522 -0
- package/dist/codex/evals/fixtures/narrative-grounding-validator/corpus.json +20 -0
- package/dist/codex/evals/fixtures/narrative-grounding-validator/prompt-injection-adversarial.txt +1 -0
- package/dist/codex/evals/fixtures/narrative-grounding-validator/prompt-injection-control.txt +1 -0
- package/dist/codex/evals/fixtures/narrative-grounding-validator/scorer.mjs +201 -0
- package/dist/codex/evals/fixtures/narrative-prose-renderer/corpus.json +15 -0
- package/dist/codex/evals/fixtures/narrative-prose-renderer/scorer.mjs +201 -0
- package/dist/codex/evals/fixtures/narrative-sources/expected-capture-completeness.json +13 -0
- package/dist/codex/evals/fixtures/narrative-sources/flow/runs/run-fixture/report.json +1 -0
- package/dist/codex/evals/fixtures/narrative-sources/flow/runs/run-fixture/state.json +1 -0
- package/dist/codex/evals/fixtures/narrative-sources/flow/runs/run-runtime/state.json +1 -0
- package/dist/codex/evals/fixtures/narrative-sources/repo/created.txt +1 -0
- package/dist/codex/evals/fixtures/narrative-sources/repo/fixture.txt +1 -0
- package/dist/codex/evals/fixtures/narrative-sources/session/agents/agent-fixture/events.jsonl +2 -0
- package/dist/codex/evals/fixtures/narrative-sources/session/agents/nested-worker/events.jsonl +1 -0
- package/dist/codex/evals/fixtures/narrative-sources/session/trust.bundle +1 -0
- package/dist/codex/evals/fixtures/narrative-sources/telemetry/full.jsonl +3 -0
- package/dist/codex/evals/fixtures/narrative-sources/telemetry/runtime.jsonl +4 -0
- package/dist/codex/evals/fixtures/narrative-sources/transcripts/session.txt +1 -0
- package/dist/codex/evals/integration/test_capability_declarations.sh +183 -0
- package/dist/codex/evals/integration/test_economics_record.sh +42 -0
- package/dist/codex/evals/integration/test_fixture_retirement_audit.sh +2 -2
- package/dist/codex/evals/integration/test_gate_review_inquiry_records.sh +42 -35
- package/dist/codex/evals/integration/test_goal_fit_hook.sh +13 -5
- package/dist/codex/evals/integration/test_goal_fit_narrative_exclusion.sh +68 -0
- package/dist/codex/evals/integration/test_narrative_evals.sh +174 -0
- package/dist/codex/evals/integration/test_narrative_grounded_envelope.sh +280 -0
- package/dist/codex/evals/integration/test_narrative_grounding_validator.sh +166 -0
- package/dist/codex/evals/integration/test_narrative_intent_annotation.sh +205 -0
- package/dist/codex/evals/integration/test_narrative_prose_renderer.sh +239 -0
- package/dist/codex/evals/integration/test_narrative_redaction_failclosed.sh +120 -0
- package/dist/codex/evals/integration/test_narrative_runtime_projection.sh +146 -0
- package/dist/codex/evals/integration/test_narrative_source_contract.sh +228 -0
- package/dist/codex/evals/integration/test_narrative_trust_isolation.sh +408 -0
- package/dist/codex/evals/integration/test_plain_stop_messaging.sh +91 -0
- package/dist/codex/evals/integration/test_session_resume_roundtrip.sh +228 -0
- package/dist/codex/evals/integration/test_telemetry_action_class.sh +265 -0
- package/dist/codex/evals/integration/test_telemetry_delegation.sh +291 -0
- package/dist/codex/evals/integration/test_telemetry_sanitize_usage.sh +155 -0
- package/dist/codex/evals/integration/test_telemetry_task_slug.sh +158 -0
- package/dist/codex/evals/integration/test_telemetry_tool_outcome.sh +279 -0
- package/dist/codex/evals/integration/test_workflow_sidecar_writer.sh +111 -2
- package/dist/codex/evals/run.sh +12 -0
- package/dist/codex/evals/static/test_library_exports.sh +5 -0
- package/dist/codex/install.sh +1 -1
- package/dist/codex/schemas/grounded-execution-narrative.schema.json +292 -0
- package/dist/codex/schemas/narrative-eval-result.schema.json +205 -0
- package/dist/codex/schemas/narrative-runtime-projection.schema.json +134 -0
- package/dist/codex/schemas/narrative-source-manifest.schema.json +294 -0
- package/dist/codex/scripts/check-hachure-boundary.mjs +81 -0
- package/dist/codex/scripts/hooks/stop-goal-fit.js +182 -9
- package/dist/codex/scripts/hooks/workflow-steering.js +160 -19
- package/dist/codex/scripts/telemetry/economics-record.schema.json +1 -1
- package/dist/codex/scripts/telemetry/economics-record.sh +47 -3
- package/dist/codex/scripts/telemetry/lib/session.sh +3 -0
- package/dist/codex/scripts/telemetry/lib/transport.sh +12 -3
- package/dist/codex/scripts/telemetry/telemetry.sh +480 -59
- package/dist/kiro/build/generated/capability-declarations.json +177 -0
- package/dist/kiro/build/package.json +1 -1
- package/dist/kiro/build/src/builder-flow-runtime.d.ts +37 -0
- package/dist/kiro/build/src/builder-flow-runtime.js +71 -1
- package/dist/kiro/build/src/builder-gate-action-envelope.d.ts +58 -7
- package/dist/kiro/build/src/builder-gate-action-envelope.js +160 -28
- package/dist/kiro/build/src/builder-lifecycle-authority.d.ts +26 -0
- package/dist/kiro/build/src/builder-lifecycle-authority.js +38 -0
- package/dist/kiro/build/src/cli/builder-run.js +71 -2
- package/dist/kiro/build/src/cli/narrative-render.d.ts +95 -0
- package/dist/kiro/build/src/cli/narrative-render.js +466 -0
- package/dist/kiro/build/src/cli/narrative-sources.d.ts +2 -0
- package/dist/kiro/build/src/cli/narrative-sources.js +282 -0
- package/dist/kiro/build/src/cli/public-contracts.d.ts +257 -0
- package/dist/kiro/build/src/cli/public-contracts.js +87 -0
- package/dist/kiro/build/src/cli/workflow-sidecar.d.ts +28 -12
- package/dist/kiro/build/src/cli/workflow-sidecar.js +599 -180
- package/dist/kiro/build/src/cli/workflow.js +11 -1
- package/dist/kiro/build/src/cli.js +8 -0
- package/dist/kiro/build/src/continuation-driver.d.ts +12 -0
- package/dist/kiro/build/src/continuation-driver.js +16 -2
- package/dist/kiro/build/src/continuation-validation.js +324 -8
- package/dist/kiro/build/src/flow-kit/action-metadata.js +21 -1
- package/dist/kiro/build/src/index.d.ts +33 -2
- package/dist/kiro/build/src/index.js +24 -0
- package/dist/kiro/build/src/lib/capability-declarations.d.ts +83 -0
- package/dist/kiro/build/src/lib/capability-declarations.js +209 -0
- package/dist/kiro/build/src/narrative/envelope.d.ts +111 -0
- package/dist/kiro/build/src/narrative/envelope.js +590 -0
- package/dist/kiro/build/src/narrative/eval-result.d.ts +60 -0
- package/dist/kiro/build/src/narrative/eval-result.js +158 -0
- package/dist/kiro/build/src/narrative/grounding-validator.d.ts +104 -0
- package/dist/kiro/build/src/narrative/grounding-validator.js +472 -0
- package/dist/kiro/build/src/narrative/integrity.d.ts +17 -0
- package/dist/kiro/build/src/narrative/integrity.js +39 -0
- package/dist/kiro/build/src/narrative/intent-economics.d.ts +42 -0
- package/dist/kiro/build/src/narrative/intent-economics.js +82 -0
- package/dist/kiro/build/src/narrative/intent.d.ts +94 -0
- package/dist/kiro/build/src/narrative/intent.js +132 -0
- package/dist/kiro/build/src/narrative/policy-filter.d.ts +15 -0
- package/dist/kiro/build/src/narrative/policy-filter.js +58 -0
- package/dist/kiro/build/src/narrative/projection.d.ts +58 -0
- package/dist/kiro/build/src/narrative/projection.js +502 -0
- package/dist/kiro/build/src/narrative/readers.d.ts +84 -0
- package/dist/kiro/build/src/narrative/readers.js +406 -0
- package/dist/kiro/build/src/narrative/render.d.ts +3 -0
- package/dist/kiro/build/src/narrative/render.js +83 -0
- package/dist/kiro/build/src/narrative/resolver.d.ts +52 -0
- package/dist/kiro/build/src/narrative/resolver.js +190 -0
- package/dist/kiro/build/src/narrative/snapshot.d.ts +90 -0
- package/dist/kiro/build/src/narrative/snapshot.js +325 -0
- package/dist/kiro/build/src/narrative/source-ids.d.ts +121 -0
- package/dist/kiro/build/src/narrative/source-ids.js +201 -0
- package/dist/kiro/build/src/narrative/statements.d.ts +84 -0
- package/dist/kiro/build/src/narrative/statements.js +347 -0
- package/dist/kiro/build/src/narrative/turn-spine.d.ts +19 -0
- package/dist/kiro/build/src/narrative/turn-spine.js +73 -0
- package/dist/kiro/build/src/tools/build-universal-bundles.js +8 -0
- package/dist/kiro/build/src/tools/generate-capability-matrix.d.ts +7 -0
- package/dist/kiro/build/src/tools/generate-capability-matrix.js +207 -0
- package/dist/kiro/build/src/tools/validate-source-tree.js +5 -1
- package/dist/kiro/context/scripts/hooks/stop-goal-fit.js +182 -9
- package/dist/kiro/context/scripts/hooks/workflow-steering.js +160 -19
- package/dist/kiro/context/scripts/telemetry/lib/session.sh +3 -0
- package/dist/kiro/context/scripts/telemetry/lib/transport.sh +12 -3
- package/dist/kiro/context/scripts/telemetry/telemetry.sh +480 -59
- package/dist/kiro/docs/context-map.md +4 -0
- package/dist/kiro/docs/decisions/index.md +1 -0
- package/dist/kiro/docs/decisions/writer-observed-execution.md +51 -0
- package/dist/kiro/docs/fixture-ownership.md +5 -1
- package/dist/kiro/docs/migrations.md +19 -4
- package/dist/kiro/docs/public-workflow-cli.md +9 -0
- package/dist/kiro/docs/spec/builder-flow-runtime.md +49 -8
- package/dist/kiro/docs/spec/runtime-hook-surface.md +29 -1
- package/dist/kiro/docs/specs/economics-record-contract.md +1 -1
- package/dist/kiro/docs/specs/harness-capability-matrix.md +63 -21
- package/dist/kiro/docs/workflow-usage-guide.md +4 -1
- package/dist/kiro/evals/ci/run-baseline.sh +38 -0
- package/dist/kiro/evals/fixtures/narrative-evals/README.md +62 -0
- package/dist/kiro/evals/fixtures/narrative-evals/corpus.json +129 -0
- package/dist/kiro/evals/fixtures/narrative-evals/scorer.mjs +522 -0
- package/dist/kiro/evals/fixtures/narrative-grounding-validator/corpus.json +20 -0
- package/dist/kiro/evals/fixtures/narrative-grounding-validator/prompt-injection-adversarial.txt +1 -0
- package/dist/kiro/evals/fixtures/narrative-grounding-validator/prompt-injection-control.txt +1 -0
- package/dist/kiro/evals/fixtures/narrative-grounding-validator/scorer.mjs +201 -0
- package/dist/kiro/evals/fixtures/narrative-prose-renderer/corpus.json +15 -0
- package/dist/kiro/evals/fixtures/narrative-prose-renderer/scorer.mjs +201 -0
- package/dist/kiro/evals/fixtures/narrative-sources/expected-capture-completeness.json +13 -0
- package/dist/kiro/evals/fixtures/narrative-sources/flow/runs/run-fixture/report.json +1 -0
- package/dist/kiro/evals/fixtures/narrative-sources/flow/runs/run-fixture/state.json +1 -0
- package/dist/kiro/evals/fixtures/narrative-sources/flow/runs/run-runtime/state.json +1 -0
- package/dist/kiro/evals/fixtures/narrative-sources/repo/created.txt +1 -0
- package/dist/kiro/evals/fixtures/narrative-sources/repo/fixture.txt +1 -0
- package/dist/kiro/evals/fixtures/narrative-sources/session/agents/agent-fixture/events.jsonl +2 -0
- package/dist/kiro/evals/fixtures/narrative-sources/session/agents/nested-worker/events.jsonl +1 -0
- package/dist/kiro/evals/fixtures/narrative-sources/session/trust.bundle +1 -0
- package/dist/kiro/evals/fixtures/narrative-sources/telemetry/full.jsonl +3 -0
- package/dist/kiro/evals/fixtures/narrative-sources/telemetry/runtime.jsonl +4 -0
- package/dist/kiro/evals/fixtures/narrative-sources/transcripts/session.txt +1 -0
- package/dist/kiro/evals/integration/test_capability_declarations.sh +183 -0
- package/dist/kiro/evals/integration/test_economics_record.sh +42 -0
- package/dist/kiro/evals/integration/test_fixture_retirement_audit.sh +2 -2
- package/dist/kiro/evals/integration/test_gate_review_inquiry_records.sh +42 -35
- package/dist/kiro/evals/integration/test_goal_fit_hook.sh +13 -5
- package/dist/kiro/evals/integration/test_goal_fit_narrative_exclusion.sh +68 -0
- package/dist/kiro/evals/integration/test_narrative_evals.sh +174 -0
- package/dist/kiro/evals/integration/test_narrative_grounded_envelope.sh +280 -0
- package/dist/kiro/evals/integration/test_narrative_grounding_validator.sh +166 -0
- package/dist/kiro/evals/integration/test_narrative_intent_annotation.sh +205 -0
- package/dist/kiro/evals/integration/test_narrative_prose_renderer.sh +239 -0
- package/dist/kiro/evals/integration/test_narrative_redaction_failclosed.sh +120 -0
- package/dist/kiro/evals/integration/test_narrative_runtime_projection.sh +146 -0
- package/dist/kiro/evals/integration/test_narrative_source_contract.sh +228 -0
- package/dist/kiro/evals/integration/test_narrative_trust_isolation.sh +408 -0
- package/dist/kiro/evals/integration/test_plain_stop_messaging.sh +91 -0
- package/dist/kiro/evals/integration/test_session_resume_roundtrip.sh +228 -0
- package/dist/kiro/evals/integration/test_telemetry_action_class.sh +265 -0
- package/dist/kiro/evals/integration/test_telemetry_delegation.sh +291 -0
- package/dist/kiro/evals/integration/test_telemetry_sanitize_usage.sh +155 -0
- package/dist/kiro/evals/integration/test_telemetry_task_slug.sh +158 -0
- package/dist/kiro/evals/integration/test_telemetry_tool_outcome.sh +279 -0
- package/dist/kiro/evals/integration/test_workflow_sidecar_writer.sh +111 -2
- package/dist/kiro/evals/run.sh +12 -0
- package/dist/kiro/evals/static/test_library_exports.sh +5 -0
- package/dist/kiro/install.sh +1 -1
- package/dist/kiro/schemas/grounded-execution-narrative.schema.json +292 -0
- package/dist/kiro/schemas/narrative-eval-result.schema.json +205 -0
- package/dist/kiro/schemas/narrative-runtime-projection.schema.json +134 -0
- package/dist/kiro/schemas/narrative-source-manifest.schema.json +294 -0
- package/dist/kiro/scripts/check-hachure-boundary.mjs +81 -0
- package/dist/kiro/scripts/hooks/stop-goal-fit.js +182 -9
- package/dist/kiro/scripts/hooks/workflow-steering.js +160 -19
- package/dist/kiro/scripts/telemetry/economics-record.schema.json +1 -1
- package/dist/kiro/scripts/telemetry/economics-record.sh +47 -3
- package/dist/kiro/scripts/telemetry/lib/session.sh +3 -0
- package/dist/kiro/scripts/telemetry/lib/transport.sh +12 -3
- package/dist/kiro/scripts/telemetry/telemetry.sh +480 -59
- package/dist/opencode/build/generated/capability-declarations.json +177 -0
- package/dist/opencode/build/package.json +1 -1
- package/dist/opencode/build/src/builder-flow-runtime.d.ts +37 -0
- package/dist/opencode/build/src/builder-flow-runtime.js +71 -1
- package/dist/opencode/build/src/builder-gate-action-envelope.d.ts +58 -7
- package/dist/opencode/build/src/builder-gate-action-envelope.js +160 -28
- package/dist/opencode/build/src/builder-lifecycle-authority.d.ts +26 -0
- package/dist/opencode/build/src/builder-lifecycle-authority.js +38 -0
- package/dist/opencode/build/src/cli/builder-run.js +71 -2
- package/dist/opencode/build/src/cli/narrative-render.d.ts +95 -0
- package/dist/opencode/build/src/cli/narrative-render.js +466 -0
- package/dist/opencode/build/src/cli/narrative-sources.d.ts +2 -0
- package/dist/opencode/build/src/cli/narrative-sources.js +282 -0
- package/dist/opencode/build/src/cli/public-contracts.d.ts +257 -0
- package/dist/opencode/build/src/cli/public-contracts.js +87 -0
- package/dist/opencode/build/src/cli/workflow-sidecar.d.ts +28 -12
- package/dist/opencode/build/src/cli/workflow-sidecar.js +599 -180
- package/dist/opencode/build/src/cli/workflow.js +11 -1
- package/dist/opencode/build/src/cli.js +8 -0
- package/dist/opencode/build/src/continuation-driver.d.ts +12 -0
- package/dist/opencode/build/src/continuation-driver.js +16 -2
- package/dist/opencode/build/src/continuation-validation.js +324 -8
- package/dist/opencode/build/src/flow-kit/action-metadata.js +21 -1
- package/dist/opencode/build/src/index.d.ts +33 -2
- package/dist/opencode/build/src/index.js +24 -0
- package/dist/opencode/build/src/lib/capability-declarations.d.ts +83 -0
- package/dist/opencode/build/src/lib/capability-declarations.js +209 -0
- package/dist/opencode/build/src/narrative/envelope.d.ts +111 -0
- package/dist/opencode/build/src/narrative/envelope.js +590 -0
- package/dist/opencode/build/src/narrative/eval-result.d.ts +60 -0
- package/dist/opencode/build/src/narrative/eval-result.js +158 -0
- package/dist/opencode/build/src/narrative/grounding-validator.d.ts +104 -0
- package/dist/opencode/build/src/narrative/grounding-validator.js +472 -0
- package/dist/opencode/build/src/narrative/integrity.d.ts +17 -0
- package/dist/opencode/build/src/narrative/integrity.js +39 -0
- package/dist/opencode/build/src/narrative/intent-economics.d.ts +42 -0
- package/dist/opencode/build/src/narrative/intent-economics.js +82 -0
- package/dist/opencode/build/src/narrative/intent.d.ts +94 -0
- package/dist/opencode/build/src/narrative/intent.js +132 -0
- package/dist/opencode/build/src/narrative/policy-filter.d.ts +15 -0
- package/dist/opencode/build/src/narrative/policy-filter.js +58 -0
- package/dist/opencode/build/src/narrative/projection.d.ts +58 -0
- package/dist/opencode/build/src/narrative/projection.js +502 -0
- package/dist/opencode/build/src/narrative/readers.d.ts +84 -0
- package/dist/opencode/build/src/narrative/readers.js +406 -0
- package/dist/opencode/build/src/narrative/render.d.ts +3 -0
- package/dist/opencode/build/src/narrative/render.js +83 -0
- package/dist/opencode/build/src/narrative/resolver.d.ts +52 -0
- package/dist/opencode/build/src/narrative/resolver.js +190 -0
- package/dist/opencode/build/src/narrative/snapshot.d.ts +90 -0
- package/dist/opencode/build/src/narrative/snapshot.js +325 -0
- package/dist/opencode/build/src/narrative/source-ids.d.ts +121 -0
- package/dist/opencode/build/src/narrative/source-ids.js +201 -0
- package/dist/opencode/build/src/narrative/statements.d.ts +84 -0
- package/dist/opencode/build/src/narrative/statements.js +347 -0
- package/dist/opencode/build/src/narrative/turn-spine.d.ts +19 -0
- package/dist/opencode/build/src/narrative/turn-spine.js +73 -0
- package/dist/opencode/build/src/tools/build-universal-bundles.js +8 -0
- package/dist/opencode/build/src/tools/generate-capability-matrix.d.ts +7 -0
- package/dist/opencode/build/src/tools/generate-capability-matrix.js +207 -0
- package/dist/opencode/build/src/tools/validate-source-tree.js +5 -1
- package/dist/opencode/context/scripts/hooks/stop-goal-fit.js +182 -9
- package/dist/opencode/context/scripts/hooks/workflow-steering.js +160 -19
- package/dist/opencode/context/scripts/telemetry/lib/session.sh +3 -0
- package/dist/opencode/context/scripts/telemetry/lib/transport.sh +12 -3
- package/dist/opencode/context/scripts/telemetry/telemetry.sh +480 -59
- package/dist/opencode/docs/context-map.md +4 -0
- package/dist/opencode/docs/decisions/index.md +1 -0
- package/dist/opencode/docs/decisions/writer-observed-execution.md +51 -0
- package/dist/opencode/docs/fixture-ownership.md +5 -1
- package/dist/opencode/docs/migrations.md +19 -4
- package/dist/opencode/docs/public-workflow-cli.md +9 -0
- package/dist/opencode/docs/spec/builder-flow-runtime.md +49 -8
- package/dist/opencode/docs/spec/runtime-hook-surface.md +29 -1
- package/dist/opencode/docs/specs/economics-record-contract.md +1 -1
- package/dist/opencode/docs/specs/harness-capability-matrix.md +63 -21
- package/dist/opencode/docs/workflow-usage-guide.md +4 -1
- package/dist/opencode/evals/ci/run-baseline.sh +38 -0
- package/dist/opencode/evals/fixtures/narrative-evals/README.md +62 -0
- package/dist/opencode/evals/fixtures/narrative-evals/corpus.json +129 -0
- package/dist/opencode/evals/fixtures/narrative-evals/scorer.mjs +522 -0
- package/dist/opencode/evals/fixtures/narrative-grounding-validator/corpus.json +20 -0
- package/dist/opencode/evals/fixtures/narrative-grounding-validator/prompt-injection-adversarial.txt +1 -0
- package/dist/opencode/evals/fixtures/narrative-grounding-validator/prompt-injection-control.txt +1 -0
- package/dist/opencode/evals/fixtures/narrative-grounding-validator/scorer.mjs +201 -0
- package/dist/opencode/evals/fixtures/narrative-prose-renderer/corpus.json +15 -0
- package/dist/opencode/evals/fixtures/narrative-prose-renderer/scorer.mjs +201 -0
- package/dist/opencode/evals/fixtures/narrative-sources/expected-capture-completeness.json +13 -0
- package/dist/opencode/evals/fixtures/narrative-sources/flow/runs/run-fixture/report.json +1 -0
- package/dist/opencode/evals/fixtures/narrative-sources/flow/runs/run-fixture/state.json +1 -0
- package/dist/opencode/evals/fixtures/narrative-sources/flow/runs/run-runtime/state.json +1 -0
- package/dist/opencode/evals/fixtures/narrative-sources/repo/created.txt +1 -0
- package/dist/opencode/evals/fixtures/narrative-sources/repo/fixture.txt +1 -0
- package/dist/opencode/evals/fixtures/narrative-sources/session/agents/agent-fixture/events.jsonl +2 -0
- package/dist/opencode/evals/fixtures/narrative-sources/session/agents/nested-worker/events.jsonl +1 -0
- package/dist/opencode/evals/fixtures/narrative-sources/session/trust.bundle +1 -0
- package/dist/opencode/evals/fixtures/narrative-sources/telemetry/full.jsonl +3 -0
- package/dist/opencode/evals/fixtures/narrative-sources/telemetry/runtime.jsonl +4 -0
- package/dist/opencode/evals/fixtures/narrative-sources/transcripts/session.txt +1 -0
- package/dist/opencode/evals/integration/test_capability_declarations.sh +183 -0
- package/dist/opencode/evals/integration/test_economics_record.sh +42 -0
- package/dist/opencode/evals/integration/test_fixture_retirement_audit.sh +2 -2
- package/dist/opencode/evals/integration/test_gate_review_inquiry_records.sh +42 -35
- package/dist/opencode/evals/integration/test_goal_fit_hook.sh +13 -5
- package/dist/opencode/evals/integration/test_goal_fit_narrative_exclusion.sh +68 -0
- package/dist/opencode/evals/integration/test_narrative_evals.sh +174 -0
- package/dist/opencode/evals/integration/test_narrative_grounded_envelope.sh +280 -0
- package/dist/opencode/evals/integration/test_narrative_grounding_validator.sh +166 -0
- package/dist/opencode/evals/integration/test_narrative_intent_annotation.sh +205 -0
- package/dist/opencode/evals/integration/test_narrative_prose_renderer.sh +239 -0
- package/dist/opencode/evals/integration/test_narrative_redaction_failclosed.sh +120 -0
- package/dist/opencode/evals/integration/test_narrative_runtime_projection.sh +146 -0
- package/dist/opencode/evals/integration/test_narrative_source_contract.sh +228 -0
- package/dist/opencode/evals/integration/test_narrative_trust_isolation.sh +408 -0
- package/dist/opencode/evals/integration/test_plain_stop_messaging.sh +91 -0
- package/dist/opencode/evals/integration/test_session_resume_roundtrip.sh +228 -0
- package/dist/opencode/evals/integration/test_telemetry_action_class.sh +265 -0
- package/dist/opencode/evals/integration/test_telemetry_delegation.sh +291 -0
- package/dist/opencode/evals/integration/test_telemetry_sanitize_usage.sh +155 -0
- package/dist/opencode/evals/integration/test_telemetry_task_slug.sh +158 -0
- package/dist/opencode/evals/integration/test_telemetry_tool_outcome.sh +279 -0
- package/dist/opencode/evals/integration/test_workflow_sidecar_writer.sh +111 -2
- package/dist/opencode/evals/run.sh +12 -0
- package/dist/opencode/evals/static/test_library_exports.sh +5 -0
- package/dist/opencode/install.sh +1 -1
- package/dist/opencode/schemas/grounded-execution-narrative.schema.json +292 -0
- package/dist/opencode/schemas/narrative-eval-result.schema.json +205 -0
- package/dist/opencode/schemas/narrative-runtime-projection.schema.json +134 -0
- package/dist/opencode/schemas/narrative-source-manifest.schema.json +294 -0
- package/dist/opencode/scripts/check-hachure-boundary.mjs +81 -0
- package/dist/opencode/scripts/hooks/stop-goal-fit.js +182 -9
- package/dist/opencode/scripts/hooks/workflow-steering.js +160 -19
- package/dist/opencode/scripts/telemetry/economics-record.schema.json +1 -1
- package/dist/opencode/scripts/telemetry/economics-record.sh +47 -3
- package/dist/opencode/scripts/telemetry/lib/session.sh +3 -0
- package/dist/opencode/scripts/telemetry/lib/transport.sh +12 -3
- package/dist/opencode/scripts/telemetry/telemetry.sh +480 -59
- package/dist/pi/build/generated/capability-declarations.json +177 -0
- package/dist/pi/build/package.json +1 -1
- package/dist/pi/build/src/builder-flow-runtime.d.ts +37 -0
- package/dist/pi/build/src/builder-flow-runtime.js +71 -1
- package/dist/pi/build/src/builder-gate-action-envelope.d.ts +58 -7
- package/dist/pi/build/src/builder-gate-action-envelope.js +160 -28
- package/dist/pi/build/src/builder-lifecycle-authority.d.ts +26 -0
- package/dist/pi/build/src/builder-lifecycle-authority.js +38 -0
- package/dist/pi/build/src/cli/builder-run.js +71 -2
- package/dist/pi/build/src/cli/narrative-render.d.ts +95 -0
- package/dist/pi/build/src/cli/narrative-render.js +466 -0
- package/dist/pi/build/src/cli/narrative-sources.d.ts +2 -0
- package/dist/pi/build/src/cli/narrative-sources.js +282 -0
- package/dist/pi/build/src/cli/public-contracts.d.ts +257 -0
- package/dist/pi/build/src/cli/public-contracts.js +87 -0
- package/dist/pi/build/src/cli/workflow-sidecar.d.ts +28 -12
- package/dist/pi/build/src/cli/workflow-sidecar.js +599 -180
- package/dist/pi/build/src/cli/workflow.js +11 -1
- package/dist/pi/build/src/cli.js +8 -0
- package/dist/pi/build/src/continuation-driver.d.ts +12 -0
- package/dist/pi/build/src/continuation-driver.js +16 -2
- package/dist/pi/build/src/continuation-validation.js +324 -8
- package/dist/pi/build/src/flow-kit/action-metadata.js +21 -1
- package/dist/pi/build/src/index.d.ts +33 -2
- package/dist/pi/build/src/index.js +24 -0
- package/dist/pi/build/src/lib/capability-declarations.d.ts +83 -0
- package/dist/pi/build/src/lib/capability-declarations.js +209 -0
- package/dist/pi/build/src/narrative/envelope.d.ts +111 -0
- package/dist/pi/build/src/narrative/envelope.js +590 -0
- package/dist/pi/build/src/narrative/eval-result.d.ts +60 -0
- package/dist/pi/build/src/narrative/eval-result.js +158 -0
- package/dist/pi/build/src/narrative/grounding-validator.d.ts +104 -0
- package/dist/pi/build/src/narrative/grounding-validator.js +472 -0
- package/dist/pi/build/src/narrative/integrity.d.ts +17 -0
- package/dist/pi/build/src/narrative/integrity.js +39 -0
- package/dist/pi/build/src/narrative/intent-economics.d.ts +42 -0
- package/dist/pi/build/src/narrative/intent-economics.js +82 -0
- package/dist/pi/build/src/narrative/intent.d.ts +94 -0
- package/dist/pi/build/src/narrative/intent.js +132 -0
- package/dist/pi/build/src/narrative/policy-filter.d.ts +15 -0
- package/dist/pi/build/src/narrative/policy-filter.js +58 -0
- package/dist/pi/build/src/narrative/projection.d.ts +58 -0
- package/dist/pi/build/src/narrative/projection.js +502 -0
- package/dist/pi/build/src/narrative/readers.d.ts +84 -0
- package/dist/pi/build/src/narrative/readers.js +406 -0
- package/dist/pi/build/src/narrative/render.d.ts +3 -0
- package/dist/pi/build/src/narrative/render.js +83 -0
- package/dist/pi/build/src/narrative/resolver.d.ts +52 -0
- package/dist/pi/build/src/narrative/resolver.js +190 -0
- package/dist/pi/build/src/narrative/snapshot.d.ts +90 -0
- package/dist/pi/build/src/narrative/snapshot.js +325 -0
- package/dist/pi/build/src/narrative/source-ids.d.ts +121 -0
- package/dist/pi/build/src/narrative/source-ids.js +201 -0
- package/dist/pi/build/src/narrative/statements.d.ts +84 -0
- package/dist/pi/build/src/narrative/statements.js +347 -0
- package/dist/pi/build/src/narrative/turn-spine.d.ts +19 -0
- package/dist/pi/build/src/narrative/turn-spine.js +73 -0
- package/dist/pi/build/src/tools/build-universal-bundles.js +8 -0
- package/dist/pi/build/src/tools/generate-capability-matrix.d.ts +7 -0
- package/dist/pi/build/src/tools/generate-capability-matrix.js +207 -0
- package/dist/pi/build/src/tools/validate-source-tree.js +5 -1
- package/dist/pi/context/scripts/hooks/stop-goal-fit.js +182 -9
- package/dist/pi/context/scripts/hooks/workflow-steering.js +160 -19
- package/dist/pi/context/scripts/telemetry/lib/session.sh +3 -0
- package/dist/pi/context/scripts/telemetry/lib/transport.sh +12 -3
- package/dist/pi/context/scripts/telemetry/telemetry.sh +480 -59
- package/dist/pi/docs/context-map.md +4 -0
- package/dist/pi/docs/decisions/index.md +1 -0
- package/dist/pi/docs/decisions/writer-observed-execution.md +51 -0
- package/dist/pi/docs/fixture-ownership.md +5 -1
- package/dist/pi/docs/migrations.md +19 -4
- package/dist/pi/docs/public-workflow-cli.md +9 -0
- package/dist/pi/docs/spec/builder-flow-runtime.md +49 -8
- package/dist/pi/docs/spec/runtime-hook-surface.md +29 -1
- package/dist/pi/docs/specs/economics-record-contract.md +1 -1
- package/dist/pi/docs/specs/harness-capability-matrix.md +63 -21
- package/dist/pi/docs/workflow-usage-guide.md +4 -1
- package/dist/pi/evals/ci/run-baseline.sh +38 -0
- package/dist/pi/evals/fixtures/narrative-evals/README.md +62 -0
- package/dist/pi/evals/fixtures/narrative-evals/corpus.json +129 -0
- package/dist/pi/evals/fixtures/narrative-evals/scorer.mjs +522 -0
- package/dist/pi/evals/fixtures/narrative-grounding-validator/corpus.json +20 -0
- package/dist/pi/evals/fixtures/narrative-grounding-validator/prompt-injection-adversarial.txt +1 -0
- package/dist/pi/evals/fixtures/narrative-grounding-validator/prompt-injection-control.txt +1 -0
- package/dist/pi/evals/fixtures/narrative-grounding-validator/scorer.mjs +201 -0
- package/dist/pi/evals/fixtures/narrative-prose-renderer/corpus.json +15 -0
- package/dist/pi/evals/fixtures/narrative-prose-renderer/scorer.mjs +201 -0
- package/dist/pi/evals/fixtures/narrative-sources/expected-capture-completeness.json +13 -0
- package/dist/pi/evals/fixtures/narrative-sources/flow/runs/run-fixture/report.json +1 -0
- package/dist/pi/evals/fixtures/narrative-sources/flow/runs/run-fixture/state.json +1 -0
- package/dist/pi/evals/fixtures/narrative-sources/flow/runs/run-runtime/state.json +1 -0
- package/dist/pi/evals/fixtures/narrative-sources/repo/created.txt +1 -0
- package/dist/pi/evals/fixtures/narrative-sources/repo/fixture.txt +1 -0
- package/dist/pi/evals/fixtures/narrative-sources/session/agents/agent-fixture/events.jsonl +2 -0
- package/dist/pi/evals/fixtures/narrative-sources/session/agents/nested-worker/events.jsonl +1 -0
- package/dist/pi/evals/fixtures/narrative-sources/session/trust.bundle +1 -0
- package/dist/pi/evals/fixtures/narrative-sources/telemetry/full.jsonl +3 -0
- package/dist/pi/evals/fixtures/narrative-sources/telemetry/runtime.jsonl +4 -0
- package/dist/pi/evals/fixtures/narrative-sources/transcripts/session.txt +1 -0
- package/dist/pi/evals/integration/test_capability_declarations.sh +183 -0
- package/dist/pi/evals/integration/test_economics_record.sh +42 -0
- package/dist/pi/evals/integration/test_fixture_retirement_audit.sh +2 -2
- package/dist/pi/evals/integration/test_gate_review_inquiry_records.sh +42 -35
- package/dist/pi/evals/integration/test_goal_fit_hook.sh +13 -5
- package/dist/pi/evals/integration/test_goal_fit_narrative_exclusion.sh +68 -0
- package/dist/pi/evals/integration/test_narrative_evals.sh +174 -0
- package/dist/pi/evals/integration/test_narrative_grounded_envelope.sh +280 -0
- package/dist/pi/evals/integration/test_narrative_grounding_validator.sh +166 -0
- package/dist/pi/evals/integration/test_narrative_intent_annotation.sh +205 -0
- package/dist/pi/evals/integration/test_narrative_prose_renderer.sh +239 -0
- package/dist/pi/evals/integration/test_narrative_redaction_failclosed.sh +120 -0
- package/dist/pi/evals/integration/test_narrative_runtime_projection.sh +146 -0
- package/dist/pi/evals/integration/test_narrative_source_contract.sh +228 -0
- package/dist/pi/evals/integration/test_narrative_trust_isolation.sh +408 -0
- package/dist/pi/evals/integration/test_plain_stop_messaging.sh +91 -0
- package/dist/pi/evals/integration/test_session_resume_roundtrip.sh +228 -0
- package/dist/pi/evals/integration/test_telemetry_action_class.sh +265 -0
- package/dist/pi/evals/integration/test_telemetry_delegation.sh +291 -0
- package/dist/pi/evals/integration/test_telemetry_sanitize_usage.sh +155 -0
- package/dist/pi/evals/integration/test_telemetry_task_slug.sh +158 -0
- package/dist/pi/evals/integration/test_telemetry_tool_outcome.sh +279 -0
- package/dist/pi/evals/integration/test_workflow_sidecar_writer.sh +111 -2
- package/dist/pi/evals/run.sh +12 -0
- package/dist/pi/evals/static/test_library_exports.sh +5 -0
- package/dist/pi/install.sh +1 -1
- package/dist/pi/schemas/grounded-execution-narrative.schema.json +292 -0
- package/dist/pi/schemas/narrative-eval-result.schema.json +205 -0
- package/dist/pi/schemas/narrative-runtime-projection.schema.json +134 -0
- package/dist/pi/schemas/narrative-source-manifest.schema.json +294 -0
- package/dist/pi/scripts/check-hachure-boundary.mjs +81 -0
- package/dist/pi/scripts/hooks/stop-goal-fit.js +182 -9
- package/dist/pi/scripts/hooks/workflow-steering.js +160 -19
- package/dist/pi/scripts/telemetry/economics-record.schema.json +1 -1
- package/dist/pi/scripts/telemetry/economics-record.sh +47 -3
- package/dist/pi/scripts/telemetry/lib/session.sh +3 -0
- package/dist/pi/scripts/telemetry/lib/transport.sh +12 -3
- package/dist/pi/scripts/telemetry/telemetry.sh +480 -59
- package/docs/context-map.md +4 -0
- package/docs/decisions/index.md +1 -0
- package/docs/decisions/writer-observed-execution.md +51 -0
- package/docs/fixture-ownership.md +5 -1
- package/docs/migrations.md +19 -4
- package/docs/public-workflow-cli.md +9 -0
- package/docs/spec/builder-flow-runtime.md +49 -8
- package/docs/spec/runtime-hook-surface.md +29 -1
- package/docs/specs/economics-record-contract.md +1 -1
- package/docs/specs/harness-capability-matrix.md +63 -21
- package/docs/workflow-usage-guide.md +4 -1
- package/evals/ci/run-baseline.sh +38 -0
- package/evals/fixtures/narrative-evals/README.md +62 -0
- package/evals/fixtures/narrative-evals/corpus.json +129 -0
- package/evals/fixtures/narrative-evals/scorer.mjs +522 -0
- package/evals/fixtures/narrative-grounding-validator/corpus.json +20 -0
- package/evals/fixtures/narrative-grounding-validator/prompt-injection-adversarial.txt +1 -0
- package/evals/fixtures/narrative-grounding-validator/prompt-injection-control.txt +1 -0
- package/evals/fixtures/narrative-grounding-validator/scorer.mjs +201 -0
- package/evals/fixtures/narrative-prose-renderer/corpus.json +15 -0
- package/evals/fixtures/narrative-prose-renderer/scorer.mjs +201 -0
- package/evals/fixtures/narrative-sources/expected-capture-completeness.json +13 -0
- package/evals/fixtures/narrative-sources/flow/runs/run-fixture/report.json +1 -0
- package/evals/fixtures/narrative-sources/flow/runs/run-fixture/state.json +1 -0
- package/evals/fixtures/narrative-sources/flow/runs/run-runtime/state.json +1 -0
- package/evals/fixtures/narrative-sources/repo/created.txt +1 -0
- package/evals/fixtures/narrative-sources/repo/fixture.txt +1 -0
- package/evals/fixtures/narrative-sources/session/agents/agent-fixture/events.jsonl +2 -0
- package/evals/fixtures/narrative-sources/session/agents/nested-worker/events.jsonl +1 -0
- package/evals/fixtures/narrative-sources/session/trust.bundle +1 -0
- package/evals/fixtures/narrative-sources/telemetry/full.jsonl +3 -0
- package/evals/fixtures/narrative-sources/telemetry/runtime.jsonl +4 -0
- package/evals/fixtures/narrative-sources/transcripts/session.txt +1 -0
- package/evals/integration/test_capability_declarations.sh +183 -0
- package/evals/integration/test_economics_record.sh +42 -0
- package/evals/integration/test_fixture_retirement_audit.sh +2 -2
- package/evals/integration/test_gate_review_inquiry_records.sh +42 -35
- package/evals/integration/test_goal_fit_hook.sh +13 -5
- package/evals/integration/test_goal_fit_narrative_exclusion.sh +68 -0
- package/evals/integration/test_narrative_evals.sh +174 -0
- package/evals/integration/test_narrative_grounded_envelope.sh +280 -0
- package/evals/integration/test_narrative_grounding_validator.sh +166 -0
- package/evals/integration/test_narrative_intent_annotation.sh +205 -0
- package/evals/integration/test_narrative_prose_renderer.sh +239 -0
- package/evals/integration/test_narrative_redaction_failclosed.sh +120 -0
- package/evals/integration/test_narrative_runtime_projection.sh +146 -0
- package/evals/integration/test_narrative_source_contract.sh +228 -0
- package/evals/integration/test_narrative_trust_isolation.sh +408 -0
- package/evals/integration/test_plain_stop_messaging.sh +91 -0
- package/evals/integration/test_session_resume_roundtrip.sh +228 -0
- package/evals/integration/test_telemetry_action_class.sh +265 -0
- package/evals/integration/test_telemetry_delegation.sh +291 -0
- package/evals/integration/test_telemetry_sanitize_usage.sh +155 -0
- package/evals/integration/test_telemetry_task_slug.sh +158 -0
- package/evals/integration/test_telemetry_tool_outcome.sh +279 -0
- package/evals/integration/test_workflow_sidecar_writer.sh +111 -2
- package/evals/run.sh +12 -0
- package/evals/static/test_library_exports.sh +5 -0
- package/package.json +9 -5
- package/schemas/grounded-execution-narrative.schema.json +292 -0
- package/schemas/narrative-eval-result.schema.json +205 -0
- package/schemas/narrative-runtime-projection.schema.json +134 -0
- package/schemas/narrative-source-manifest.schema.json +294 -0
- package/scripts/check-hachure-boundary.mjs +81 -0
- package/scripts/hooks/stop-goal-fit.js +182 -9
- package/scripts/hooks/workflow-steering.js +160 -19
- package/scripts/telemetry/economics-record.schema.json +1 -1
- package/scripts/telemetry/economics-record.sh +47 -3
- package/scripts/telemetry/lib/session.sh +3 -0
- package/scripts/telemetry/lib/transport.sh +12 -3
- package/scripts/telemetry/telemetry.sh +480 -59
- package/src/builder-flow-runtime.ts +102 -1
- package/src/builder-gate-action-envelope.ts +212 -35
- package/src/builder-lifecycle-authority.ts +48 -0
- package/src/cli/builder-flow-runtime.test.mjs +218 -7
- package/src/cli/builder-run.ts +71 -1
- package/src/cli/capability-declarations.test.mjs +117 -0
- package/src/cli/continuation-driver.test.mjs +327 -20
- package/src/cli/kit-metadata-security.test.mjs +90 -2
- package/src/cli/narrative-arch-isolation.test.mjs +198 -0
- package/src/cli/narrative-envelope.test.mjs +367 -0
- package/src/cli/narrative-grounding-validator.test.mjs +322 -0
- package/src/cli/narrative-intent.test.mjs +177 -0
- package/src/cli/narrative-policy-filter.test.mjs +66 -0
- package/src/cli/narrative-projection.test.mjs +236 -0
- package/src/cli/narrative-readers.test.mjs +196 -0
- package/src/cli/narrative-render.test.mjs +221 -0
- package/src/cli/narrative-render.ts +589 -0
- package/src/cli/narrative-resolver.test.mjs +114 -0
- package/src/cli/narrative-snapshot.test.mjs +149 -0
- package/src/cli/narrative-source-ids.test.mjs +84 -0
- package/src/cli/narrative-sources.ts +276 -0
- package/src/cli/narrative-statements.test.mjs +369 -0
- package/src/cli/narrative-turn-spine.test.mjs +74 -0
- package/src/cli/public-api.test.mjs +17 -0
- package/src/cli/public-contracts.ts +95 -0
- package/src/cli/sidecar-pure-helpers.test.mjs +216 -24
- package/src/cli/trust-bundle-policy-order.test.mjs +134 -1
- package/src/cli/workflow-sidecar.ts +563 -167
- package/src/cli/workflow.ts +15 -1
- package/src/cli/writer-observed-execution.test.mjs +102 -0
- package/src/cli.ts +8 -0
- package/src/continuation-driver.ts +32 -3
- package/src/continuation-validation.ts +336 -8
- package/src/flow-kit/action-metadata.ts +20 -1
- package/src/index.ts +241 -0
- package/src/lib/capability-declarations.ts +261 -0
- package/src/narrative/envelope.ts +624 -0
- package/src/narrative/eval-result.ts +175 -0
- package/src/narrative/grounding-validator.ts +610 -0
- package/src/narrative/integrity.ts +53 -0
- package/src/narrative/intent-economics.ts +119 -0
- package/src/narrative/intent.ts +216 -0
- package/src/narrative/policy-filter.ts +66 -0
- package/src/narrative/projection.ts +530 -0
- package/src/narrative/readers.ts +427 -0
- package/src/narrative/render.ts +82 -0
- package/src/narrative/resolver.ts +195 -0
- package/src/narrative/snapshot.ts +365 -0
- package/src/narrative/source-ids.ts +294 -0
- package/src/narrative/statements.ts +423 -0
- package/src/narrative/turn-spine.ts +99 -0
- package/src/tools/build-universal-bundles.ts +8 -0
- package/src/tools/generate-capability-matrix.ts +215 -0
- package/src/tools/validate-source-tree.ts +5 -1
- package/build/src/cli/sidecar-claim-explain.d.ts +0 -45
- package/build/src/cli/sidecar-claim-explain.js +0 -87
- package/dist/base/build/src/cli/sidecar-claim-explain.d.ts +0 -45
- package/dist/base/build/src/cli/sidecar-claim-explain.js +0 -87
- package/dist/claude-code/build/src/cli/sidecar-claim-explain.d.ts +0 -45
- package/dist/claude-code/build/src/cli/sidecar-claim-explain.js +0 -87
- package/dist/codex/build/src/cli/sidecar-claim-explain.d.ts +0 -45
- package/dist/codex/build/src/cli/sidecar-claim-explain.js +0 -87
- package/dist/kiro/build/src/cli/sidecar-claim-explain.d.ts +0 -45
- package/dist/kiro/build/src/cli/sidecar-claim-explain.js +0 -87
- package/dist/opencode/build/src/cli/sidecar-claim-explain.d.ts +0 -45
- package/dist/opencode/build/src/cli/sidecar-claim-explain.js +0 -87
- package/dist/pi/build/src/cli/sidecar-claim-explain.d.ts +0 -45
- package/dist/pi/build/src/cli/sidecar-claim-explain.js +0 -87
- package/src/cli/sidecar-claim-explain.ts +0 -130
|
@@ -14,7 +14,15 @@ import { flowAgentsPackageRoot, flowAgentsPackageVersion } from "../lib/package-
|
|
|
14
14
|
import { pinnedFlowAgentsCommand } from "../lib/pinned-cli-command.js";
|
|
15
15
|
import { runObservedCommand } from "../lib/observed-command.js";
|
|
16
16
|
import { captureReviewWorkspaceSnapshot, startBuilderFlowSession, syncBuilderFlowSession } from "../builder-flow-runtime.js";
|
|
17
|
-
import {
|
|
17
|
+
import { NARRATIVE_NAMESPACE_ROOT } from "./narrative-sources.js";
|
|
18
|
+
import {
|
|
19
|
+
EVIDENCE_REF_FIELD_SCHEMAS,
|
|
20
|
+
EVIDENCE_REF_KINDS,
|
|
21
|
+
EVIDENCE_REF_RULES,
|
|
22
|
+
NARRATIVE_PROMOTE_OPERATION,
|
|
23
|
+
WORKFLOW_ACCEPTANCE_STATUSES,
|
|
24
|
+
WORKFLOW_CRITIQUE_STATUSES,
|
|
25
|
+
} from "./public-contracts.js";
|
|
18
26
|
// #291 Wave 1 Task 1.1 exports: ensure-session's ownership guard reuses the EXACT same
|
|
19
27
|
// assignment ⋈ liveness join / claim / supersede logic #290 already ships for the
|
|
20
28
|
// `assignment-provider` CLI, rather than reimplementing a second, parallel join (static ESM
|
|
@@ -374,53 +382,22 @@ export async function validateTrustBundle(bundle: unknown): Promise<{ valid: boo
|
|
|
374
382
|
return { valid: false, errors: [message], available: true };
|
|
375
383
|
}
|
|
376
384
|
}
|
|
377
|
-
// Validate a single InquiryRecord against the hachure inquiry-record.schema.json.
|
|
378
|
-
// Uses a separate AJV instance compiled against that schema (not the trust-bundle schema).
|
|
379
|
-
let _hachureInquiryRecordValidator: ((record: unknown) => { valid: boolean; errors: string[] }) | null | undefined;
|
|
380
|
-
function getHachureInquiryRecordValidator(): ((record: unknown) => { valid: boolean; errors: string[] }) | null {
|
|
381
|
-
if (_hachureInquiryRecordValidator !== undefined) return _hachureInquiryRecordValidator;
|
|
382
|
-
try {
|
|
383
|
-
const _require = createRequire(import.meta.url);
|
|
384
|
-
const hachureDir = path.dirname(_require.resolve("hachure"));
|
|
385
|
-
const schemasDir = path.join(hachureDir, "schemas");
|
|
386
|
-
const Ajv = _require("ajv/dist/2020");
|
|
387
|
-
const schemas: Record<string, any> = {};
|
|
388
|
-
for (const file of fs.readdirSync(schemasDir)) {
|
|
389
|
-
if (!file.endsWith(".schema.json")) continue;
|
|
390
|
-
schemas[file] = JSON.parse(fs.readFileSync(path.join(schemasDir, file), "utf8"));
|
|
391
|
-
}
|
|
392
|
-
const inquiryRecordSchema = schemas["inquiry-record.schema.json"];
|
|
393
|
-
if (!inquiryRecordSchema) { _hachureInquiryRecordValidator = null; return null; }
|
|
394
|
-
const ajv = new Ajv({ strict: false, allErrors: true });
|
|
395
|
-
for (const [filename, schema] of Object.entries(schemas)) {
|
|
396
|
-
if (filename === "inquiry-record.schema.json") continue;
|
|
397
|
-
ajv.addSchema(schema, filename);
|
|
398
|
-
}
|
|
399
|
-
const validate = ajv.compile(inquiryRecordSchema);
|
|
400
|
-
_hachureInquiryRecordValidator = (record: unknown) => {
|
|
401
|
-
const valid = validate(record);
|
|
402
|
-
if (valid) return { valid: true, errors: [] };
|
|
403
|
-
const errors = ((validate as any).errors ?? []).map((err: any) => {
|
|
404
|
-
const loc = err.instancePath || err.schemaPath || "";
|
|
405
|
-
return `${loc} ${err.message ?? "invalid"}`.trim();
|
|
406
|
-
});
|
|
407
|
-
return { valid: false, errors };
|
|
408
|
-
};
|
|
409
|
-
return _hachureInquiryRecordValidator;
|
|
410
|
-
} catch {
|
|
411
|
-
_hachureInquiryRecordValidator = null;
|
|
412
|
-
return null;
|
|
413
|
-
}
|
|
414
|
-
}
|
|
415
385
|
/**
|
|
416
|
-
* Validate a record against the canonical
|
|
417
|
-
*
|
|
418
|
-
* Returns `{ valid, errors, available }`. Fail-open when
|
|
386
|
+
* Validate a record against the canonical InquiryRecord schema via
|
|
387
|
+
* @kontourai/surface's `validateInquiryRecord` (symmetric to `validateTrustBundle`).
|
|
388
|
+
* Returns `{ valid, errors, available }`. Fail-open when Surface is unavailable.
|
|
419
389
|
*/
|
|
420
|
-
export function validateInquiryRecord(record: unknown): { valid: boolean; errors: string[]; available: boolean } {
|
|
421
|
-
const
|
|
422
|
-
if (!
|
|
423
|
-
|
|
390
|
+
export async function validateInquiryRecord(record: unknown): Promise<{ valid: boolean; errors: string[]; available: boolean }> {
|
|
391
|
+
const m = await tryLoadSurface();
|
|
392
|
+
if (!m || typeof (m as { validateInquiryRecord?: unknown }).validateInquiryRecord !== "function") {
|
|
393
|
+
return { valid: true, errors: [], available: false };
|
|
394
|
+
}
|
|
395
|
+
try {
|
|
396
|
+
(m as unknown as { validateInquiryRecord: (r: unknown) => unknown }).validateInquiryRecord(record);
|
|
397
|
+
return { valid: true, errors: [], available: true };
|
|
398
|
+
} catch (err) {
|
|
399
|
+
return { valid: false, errors: [err instanceof Error ? err.message : String(err)], available: true };
|
|
400
|
+
}
|
|
424
401
|
}
|
|
425
402
|
// ─── @kontourai/surface status derivation ────────────────────────────────────
|
|
426
403
|
// Surface is ESM-only; this module builds to CJS. Load Surface via a fail-open
|
|
@@ -471,8 +448,12 @@ type SurfaceModule = {
|
|
|
471
448
|
) => SurfaceInquiryRecord;
|
|
472
449
|
buildTrustReport: (bundle: Record<string, unknown>, options?: { now?: Date }) => Record<string, unknown>;
|
|
473
450
|
buildDerivationDrilldown: (report: Record<string, unknown>, claimId: string) => Record<string, unknown>;
|
|
451
|
+
/** #171: consumer-ready claim explanation (Surface >=2.10); composes the drilldown internally, fail-soft. */
|
|
452
|
+
explainClaim: (report: Record<string, unknown>, claimId: string) => import("@kontourai/surface").ClaimExplanation;
|
|
474
453
|
/** Canonical trust-bundle validator from @kontourai/surface. Throws on invalid input; returns TrustBundle on success. */
|
|
475
454
|
validateTrustBundle: (input: unknown) => Record<string, unknown>;
|
|
455
|
+
/** Canonical InquiryRecord validator from @kontourai/surface. Throws on invalid input; returns InquiryRecord on success. */
|
|
456
|
+
validateInquiryRecord: (input: unknown) => Record<string, unknown>;
|
|
476
457
|
/** Freeze a derivation checkpoint from a report. */
|
|
477
458
|
checkpointFromReport: (report: Record<string, unknown>) => Record<string, unknown>;
|
|
478
459
|
/** Diff two derivations (prior checkpoint → later report) and emit freshness transition events. */
|
|
@@ -533,6 +514,18 @@ function criterionStatusToEventStatus(status: string): string | null {
|
|
|
533
514
|
if (status === "accepted_gap") return "assumed";
|
|
534
515
|
return null; // pending / not_verified → no event → Surface returns "unknown"
|
|
535
516
|
}
|
|
517
|
+
|
|
518
|
+
const ACCEPTANCE_CRITERION_STATUSES = new Set<string>(WORKFLOW_ACCEPTANCE_STATUSES);
|
|
519
|
+
const CANONICALLY_OBSERVED_ACCEPTANCE_CRITERIA = new WeakSet<object>();
|
|
520
|
+
|
|
521
|
+
function markCanonicallyObservedCriterion<T extends AnyObj>(criterion: T): T {
|
|
522
|
+
CANONICALLY_OBSERVED_ACCEPTANCE_CRITERIA.add(criterion);
|
|
523
|
+
return criterion;
|
|
524
|
+
}
|
|
525
|
+
|
|
526
|
+
function normalizeCriterionStatus(status: unknown): string {
|
|
527
|
+
return typeof status === "string" && ACCEPTANCE_CRITERION_STATUSES.has(status) ? status : "pending";
|
|
528
|
+
}
|
|
536
529
|
/**
|
|
537
530
|
* WS8 (ADR 0020): Derive Surface evidence classification (evidenceType + method)
|
|
538
531
|
* from a workflow check's kind, replacing the previous hardcoded `test_output`.
|
|
@@ -635,7 +628,20 @@ export function reduceCaptureLogByCommand(commandLog: AnyObj[] | undefined): Map
|
|
|
635
628
|
else if (prev.observedResult === "pass" || result === "pass") merged = "pass";
|
|
636
629
|
else merged = "ambiguous";
|
|
637
630
|
}
|
|
638
|
-
|
|
631
|
+
// #634 review finding: the exit code must travel with the WINNING status, never a
|
|
632
|
+
// losing entry's — [hook fail exit 1, writer pass exit 0] must fold to fail/1, not
|
|
633
|
+
// fail/0 (contradictory isError:true, exitCode:0 evidence). When both entries carry
|
|
634
|
+
// the winning status, prefer the newer non-null code.
|
|
635
|
+
let mergedExitCode: number | null;
|
|
636
|
+
if (!prev) {
|
|
637
|
+
mergedExitCode = exitCode;
|
|
638
|
+
} else if (merged === result && merged === prev.observedResult) {
|
|
639
|
+
mergedExitCode = exitCode !== null ? exitCode : prev.exitCode;
|
|
640
|
+
} else if (merged === result) {
|
|
641
|
+
mergedExitCode = exitCode;
|
|
642
|
+
} else {
|
|
643
|
+
mergedExitCode = prev.exitCode;
|
|
644
|
+
}
|
|
639
645
|
captureByCommand.set(key, { observedResult: merged, exitCode: mergedExitCode });
|
|
640
646
|
}
|
|
641
647
|
return captureByCommand;
|
|
@@ -830,8 +836,6 @@ export async function buildTrustBundle(slug: string, timestamp: string, checks:
|
|
|
830
836
|
// "flow-step". Fallback: first non-decision, non-acceptance entry.
|
|
831
837
|
// check (kind=policy) → expects[] entry whose claimType contains
|
|
832
838
|
// "compliance" or "policy". Fallback: same as non-policy.
|
|
833
|
-
// acceptance criterion → expects[] entry whose subjectType is "flow-step"
|
|
834
|
-
// OR claimType contains "tests" OR "compliance". Fallback: first entry.
|
|
835
839
|
// critique → expects[] entry whose claimType contains "policy"
|
|
836
840
|
// OR "compliance" AND subjectType is "artifact". Fallback: last entry.
|
|
837
841
|
//
|
|
@@ -841,7 +845,7 @@ export async function buildTrustBundle(slug: string, timestamp: string, checks:
|
|
|
841
845
|
// Surface from that evidence (never hand-set).
|
|
842
846
|
//
|
|
843
847
|
// Per-gate producibility (ADR 0016 P-d):
|
|
844
|
-
// (a) Already handled via subjectType=flow-step preference:
|
|
848
|
+
// (a) Already handled via subjectType=flow-step preference for checks:
|
|
845
849
|
// builder.verify.tests (verify-gate, subjectType=flow-step)
|
|
846
850
|
// builder.verify.policy-compliance (verify-gate, kind=policy match)
|
|
847
851
|
// (b) Producible via fallback (non-decision, non-acceptance, first match):
|
|
@@ -858,7 +862,10 @@ export async function buildTrustBundle(slug: string, timestamp: string, checks:
|
|
|
858
862
|
// builder.learn.evidence (learn-gate, subjectType=release)
|
|
859
863
|
// For category (c): record-gate-claim subcommand allows skills to target a specific
|
|
860
864
|
// expects[] entry by --expectation <id>, bypassing this semantic match entirely.
|
|
861
|
-
|
|
865
|
+
//
|
|
866
|
+
// Acceptance criteria do not participate in this matching table. They are durable Work Item
|
|
867
|
+
// facts with invariant workflow.acceptance.criterion/flow-step identity across every gate.
|
|
868
|
+
function matchExpectsEntry(kind: "check" | "critique", checkKindVal?: string, expectationId?: string): { claimType: string; subjectType: string } | null {
|
|
862
869
|
if (!activeStep || activeStep.gateExpects.length === 0) return null;
|
|
863
870
|
const expects = activeStep.gateExpects;
|
|
864
871
|
if (kind === "check") {
|
|
@@ -891,14 +898,6 @@ export async function buildTrustBundle(slug: string, timestamp: string, checks:
|
|
|
891
898
|
if (fallback) return { claimType: fallback.bundle_claim.claimType, subjectType: fallback.bundle_claim.subjectType };
|
|
892
899
|
return null;
|
|
893
900
|
}
|
|
894
|
-
if (kind === "acceptance") {
|
|
895
|
-
const match = expects.find((e) => {
|
|
896
|
-
const ct = e.bundle_claim.claimType.toLowerCase();
|
|
897
|
-
return e.bundle_claim.subjectType === "flow-step" || ct.includes("tests") || ct.includes("compliance");
|
|
898
|
-
});
|
|
899
|
-
if (match) return { claimType: match.bundle_claim.claimType, subjectType: match.bundle_claim.subjectType };
|
|
900
|
-
return { claimType: expects[0]!.bundle_claim.claimType, subjectType: expects[0]!.bundle_claim.subjectType };
|
|
901
|
-
}
|
|
902
901
|
if (kind === "critique") {
|
|
903
902
|
const match = expects.find((e) => {
|
|
904
903
|
const ct = e.bundle_claim.claimType.toLowerCase();
|
|
@@ -1093,33 +1092,74 @@ export async function buildTrustBundle(slug: string, timestamp: string, checks:
|
|
|
1093
1092
|
if (!criterion.id) continue;
|
|
1094
1093
|
const subjectId = `${slug}/${criterion.id}`;
|
|
1095
1094
|
const fieldOrBehavior = String(criterion.description ?? criterion.id);
|
|
1095
|
+
const normalizedCriterionStatus = normalizeCriterionStatus(criterion.status);
|
|
1096
1096
|
const criterionIdentityVersion = criterion.identity_version === 2 ? 2 : 1;
|
|
1097
1097
|
const criterionVerifiedAt = criterionIdentityVersion === 2 && typeof criterion.verified_at === "string" ? criterion.verified_at : null;
|
|
1098
|
+
const criterionEvidenceRefs = Array.isArray(criterion.evidence_refs) ? criterion.evidence_refs : [];
|
|
1099
|
+
const commandRefs = criterionEvidenceRefs
|
|
1100
|
+
.filter((ref: AnyObj) => ref?.kind === "command")
|
|
1101
|
+
.map((ref: AnyObj) => commandFromEvidenceRef(ref))
|
|
1102
|
+
.filter((command: string) => command.length > 0);
|
|
1103
|
+
const rawObservedCommands = Array.isArray(criterion._observed_commands) ? criterion._observed_commands as AnyObj[] : [];
|
|
1104
|
+
const observedCommands = rawObservedCommands.filter((observation: AnyObj) =>
|
|
1105
|
+
typeof observation?.command === "string"
|
|
1106
|
+
&& commandRefs.includes(observation.command)
|
|
1107
|
+
&& observation.exit_code === 0
|
|
1108
|
+
&& typeof observation.output_sha256 === "string"
|
|
1109
|
+
&& /^[a-f0-9]{64}$/i.test(observation.output_sha256)
|
|
1110
|
+
&& Number.isSafeInteger(observation.test_count)
|
|
1111
|
+
&& observation.test_count > 0
|
|
1112
|
+
&& observation.execution_proof?.kind === "local-process-exit",
|
|
1113
|
+
);
|
|
1114
|
+
const hasObservedCommandProvenance = CANONICALLY_OBSERVED_ACCEPTANCE_CRITERIA.has(criterion)
|
|
1115
|
+
&& criterionIdentityVersion === 2
|
|
1116
|
+
&& criterionVerifiedAt !== null
|
|
1117
|
+
&& Number.isFinite(Date.parse(criterionVerifiedAt))
|
|
1118
|
+
&& observedCommands.length === rawObservedCommands.length
|
|
1119
|
+
&& observedCommands.length > 0
|
|
1120
|
+
&& commandRefs.every((command: string) => observedCommands.some((observation: AnyObj) => observation.command === command));
|
|
1121
|
+
const criterionStatus = normalizedCriterionStatus === "pass" && !hasObservedCommandProvenance
|
|
1122
|
+
? "pending"
|
|
1123
|
+
: normalizedCriterionStatus;
|
|
1124
|
+
const criterionClaimTimestamp = criterionStatus === "pass" && criterionVerifiedAt ? criterionVerifiedAt : ts;
|
|
1098
1125
|
const claimId = generateClaimId(subjectId, "flow-agents.workflow", criterionVerifiedAt ? `${fieldOrBehavior}::verified::${criterionVerifiedAt}` : fieldOrBehavior);
|
|
1099
|
-
const
|
|
1100
|
-
const
|
|
1101
|
-
|
|
1126
|
+
const claimType = "workflow.acceptance.criterion";
|
|
1127
|
+
const criterionEvidenceIds: string[] = [];
|
|
1128
|
+
if (criterionStatus === "pass") {
|
|
1129
|
+
for (const observation of observedCommands) {
|
|
1130
|
+
const evidenceId = `ev:${claimId}:${createHash("sha256").update(observation.command).digest("hex").slice(0, 16)}`;
|
|
1131
|
+
criterionEvidenceIds.push(evidenceId);
|
|
1132
|
+
evidenceItems.push({
|
|
1133
|
+
id: evidenceId,
|
|
1134
|
+
claimId,
|
|
1135
|
+
evidenceType: "test_output",
|
|
1136
|
+
method: "validation",
|
|
1137
|
+
sourceRef: `${slug}/observed-command`,
|
|
1138
|
+
excerptOrSummary: observation.command,
|
|
1139
|
+
observedAt: criterionVerifiedAt,
|
|
1140
|
+
collectedBy: "flow-agents/workflow-sidecar",
|
|
1141
|
+
passing: true,
|
|
1142
|
+
execution: { runner: "bash", label: observation.command, isError: false, exitCode: 0 },
|
|
1143
|
+
});
|
|
1144
|
+
}
|
|
1145
|
+
}
|
|
1146
|
+
const policy = ensurePolicy(claimType, "high", criterionEvidenceIds.length > 0 ? ["test_output"] : []);
|
|
1147
|
+
const evStatus = criterionStatusToEventStatus(criterionStatus);
|
|
1102
1148
|
const claimEvents: AnyObj[] = [];
|
|
1103
1149
|
if (evStatus) {
|
|
1104
|
-
const evt: AnyObj = { id: `evt:${claimId}`, claimId, status: evStatus, actor: "flow-agents/workflow-sidecar", method: "validation", evidenceIds:
|
|
1150
|
+
const evt: AnyObj = { id: `evt:${claimId}`, claimId, status: evStatus, actor: "flow-agents/workflow-sidecar", method: "validation", evidenceIds: criterionEvidenceIds, createdAt: criterionClaimTimestamp, verifiedAt: criterionClaimTimestamp };
|
|
1105
1151
|
events.push(evt);
|
|
1106
1152
|
claimEvents.push(evt);
|
|
1107
1153
|
}
|
|
1108
1154
|
|
|
1109
|
-
//
|
|
1110
|
-
|
|
1111
|
-
|
|
1112
|
-
|
|
1113
|
-
|
|
1114
|
-
|
|
1115
|
-
|
|
1116
|
-
|
|
1117
|
-
} else {
|
|
1118
|
-
// No active flow step — only the workflow.* primary claim (legitimate no-flow fallback path).
|
|
1119
|
-
const claimObj: AnyObj = { id: claimId, subjectType: "workflow-acceptance-criterion", subjectId, facet: "flow-agents.workflow", claimType: legacyClaimType, fieldOrBehavior, value: criterion.status, createdAt: ts, updatedAt: ts, impactLevel: "high", verificationPolicyId: policy.id, metadata: { origin: "acceptance", criterion: { id: criterion.id, description: criterion.description ?? criterion.id, status: criterion.status, evidence_refs: Array.isArray(criterion.evidence_refs) ? criterion.evidence_refs : [] } } };
|
|
1120
|
-
const { status: derivedStatus } = deriveClaimStatus({ claim: claimObj as Record<string, unknown>, evidence: [], events: claimEvents as Record<string, unknown>[], policies: [policy] as Record<string, unknown>[] });
|
|
1121
|
-
claims.push({ ...claimObj, status: derivedStatus });
|
|
1122
|
-
}
|
|
1155
|
+
// Acceptance criteria are durable Work Item facts. Their identity must not change when a
|
|
1156
|
+
// bundle is rebuilt at a different active gate. Missing or invalid status is conservatively
|
|
1157
|
+
// projected as pending so direct artifact edits can never manufacture a pass or invalidate
|
|
1158
|
+
// the Hachure claim shape.
|
|
1159
|
+
const claimObj: AnyObj = { id: claimId, subjectType: "flow-step", subjectId, facet: "flow-agents.workflow", claimType, fieldOrBehavior, value: criterionStatus, createdAt: criterionClaimTimestamp, updatedAt: criterionClaimTimestamp, impactLevel: "high", verificationPolicyId: policy.id, metadata: { origin: "acceptance", criterion: { id: criterion.id, description: criterion.description ?? criterion.id, status: criterionStatus, evidence_refs: criterionEvidenceRefs, ...(observedCommands.length > 0 ? { observed_commands: observedCommands } : {}), ...(criterionIdentityVersion === 2 ? { identity_version: 2 } : {}), ...(criterionVerifiedAt ? { verified_at: criterionVerifiedAt } : {}) }, ...(workflowSubjectRef ? { workflow_subject_ref: workflowSubjectRef } : {}) } };
|
|
1160
|
+
const criterionEvidence = evidenceItems.filter((evidence) => evidence.claimId === claimId);
|
|
1161
|
+
const { status: derivedStatus } = deriveClaimStatus({ claim: claimObj as Record<string, unknown>, evidence: criterionEvidence as Record<string, unknown>[], events: claimEvents as Record<string, unknown>[], policies: [policy] as Record<string, unknown>[] });
|
|
1162
|
+
claims.push({ ...claimObj, status: derivedStatus });
|
|
1123
1163
|
}
|
|
1124
1164
|
|
|
1125
1165
|
// Critique entries → claims + events
|
|
@@ -2443,28 +2483,40 @@ function hasNonEmptyString(value: unknown): boolean {
|
|
|
2443
2483
|
function hasPositiveInteger(value: unknown): boolean {
|
|
2444
2484
|
return Number.isInteger(value) && Number(value) >= 1;
|
|
2445
2485
|
}
|
|
2446
|
-
export function validateEvidenceRef(ref: AnyObj, label: string): AnyObj {
|
|
2447
|
-
if (!
|
|
2448
|
-
for (const key of Object.keys(ref)) if (!
|
|
2486
|
+
export function validateEvidenceRef(ref: AnyObj, label: string, projectRoot = process.cwd()): AnyObj {
|
|
2487
|
+
if (!(EVIDENCE_REF_KINDS as readonly unknown[]).includes(ref.kind)) die(`${label} entry kind must be one of: ${EVIDENCE_REF_KINDS.join(", ")}`);
|
|
2488
|
+
for (const key of Object.keys(ref)) if (!Object.hasOwn(EVIDENCE_REF_FIELD_SCHEMAS, key)) die(`${label} entries contain unsupported field: ${key}`);
|
|
2449
2489
|
if (ref.url !== undefined && !hasNonEmptyString(ref.url)) die(`${label} entry url must be a non-empty string`);
|
|
2450
2490
|
if (ref.file !== undefined && !hasNonEmptyString(ref.file)) die(`${label} entry file must be a non-empty string`);
|
|
2451
2491
|
if (ref.excerpt !== undefined && !hasNonEmptyString(ref.excerpt)) die(`${label} entry excerpt must be a non-empty string`);
|
|
2452
2492
|
if (ref.summary !== undefined && !hasNonEmptyString(ref.summary)) die(`${label} entry summary must be a non-empty string`);
|
|
2453
2493
|
if (ref.line_start !== undefined && !hasPositiveInteger(ref.line_start)) die(`${label} entry line_start must be a positive integer`);
|
|
2454
2494
|
if (ref.line_end !== undefined && !hasPositiveInteger(ref.line_end)) die(`${label} entry line_end must be a positive integer`);
|
|
2455
|
-
|
|
2456
|
-
|
|
2457
|
-
|
|
2458
|
-
|
|
2459
|
-
|
|
2495
|
+
rejectNarrativeReference(projectRoot, ref.file, `${label} entry file`);
|
|
2496
|
+
rejectNarrativeReference(projectRoot, ref.url, `${label} entry url`);
|
|
2497
|
+
rejectNarrativeReference(projectRoot, ref.excerpt, `${label} entry excerpt`);
|
|
2498
|
+
const rules = EVIDENCE_REF_RULES[ref.kind as keyof typeof EVIDENCE_REF_RULES];
|
|
2499
|
+
for (const rule of rules) {
|
|
2500
|
+
const present = (field: string): boolean => field === "line_start" || field === "line_end"
|
|
2501
|
+
? hasPositiveInteger(ref[field])
|
|
2502
|
+
: hasNonEmptyString(ref[field]);
|
|
2503
|
+
const valid = rule.mode === "all" ? rule.fields.every(present) : rule.fields.some(present);
|
|
2504
|
+
if (!valid) {
|
|
2505
|
+
const separator = rule.fields.length > 2 ? ", " : " ";
|
|
2506
|
+
const joined = rule.fields.length > 1
|
|
2507
|
+
? `${rule.fields.slice(0, -1).join(separator)}${rule.fields.length > 2 ? "," : ""} or ${rule.fields.at(-1)}`
|
|
2508
|
+
: rule.fields[0];
|
|
2509
|
+
die(`${label} ${ref.kind} refs require ${rule.mode === "all" && rule.fields.length > 1 ? joined.replace(/ or ([^,]+)$/, " and $1") : joined}`);
|
|
2510
|
+
}
|
|
2511
|
+
}
|
|
2460
2512
|
return ref;
|
|
2461
2513
|
}
|
|
2462
|
-
export function normalizeEvidenceRefs(raw: unknown, label: string): AnyObj[] {
|
|
2514
|
+
export function normalizeEvidenceRefs(raw: unknown, label: string, projectRoot = process.cwd()): AnyObj[] {
|
|
2463
2515
|
if (!Array.isArray(raw)) die(`${label} must be an array`);
|
|
2464
2516
|
return raw.map((ref) => {
|
|
2465
2517
|
if (typeof ref === "string") die(`${label} entries must be structured evidence reference objects; legacy string refs are not supported`);
|
|
2466
2518
|
if (!ref || typeof ref !== "object" || Array.isArray(ref)) die(`${label} entries must be objects`);
|
|
2467
|
-
return validateEvidenceRef({ ...ref as AnyObj }, label);
|
|
2519
|
+
return validateEvidenceRef({ ...ref as AnyObj }, label, projectRoot);
|
|
2468
2520
|
});
|
|
2469
2521
|
}
|
|
2470
2522
|
|
|
@@ -2478,14 +2530,155 @@ function canonicalProjectRootForSession(dir: string): string {
|
|
|
2478
2530
|
return projectRoot;
|
|
2479
2531
|
}
|
|
2480
2532
|
|
|
2533
|
+
// #619: the narrative isolation guard must run for EVERY session layout (canonical,
|
|
2534
|
+
// tmp, or legacy .flow-agents/) without imposing the canonical-session requirement that
|
|
2535
|
+
// canonicalProjectRootForSession enforces. It only needs a best-effort project root to
|
|
2536
|
+
// resolve relative evidence paths; the namespace regex and content-shape checks are
|
|
2537
|
+
// location-independent. It mirrors canonicalProjectRootForSession's path computation for a
|
|
2538
|
+
// canonical .kontourai/flow-agents/<slug> session (project root is three levels up), handles
|
|
2539
|
+
// the legacy .flow-agents/<slug> layout (two levels up), and never dies on any layout.
|
|
2540
|
+
function narrativeGuardRoot(dir: string): string {
|
|
2541
|
+
const artifactRoot = path.dirname(dir);
|
|
2542
|
+
const parentRoot = path.dirname(artifactRoot);
|
|
2543
|
+
if (path.basename(artifactRoot) === "flow-agents" && path.basename(parentRoot) === ".kontourai") {
|
|
2544
|
+
return path.dirname(parentRoot);
|
|
2545
|
+
}
|
|
2546
|
+
if (path.basename(artifactRoot) === ".flow-agents") {
|
|
2547
|
+
return parentRoot;
|
|
2548
|
+
}
|
|
2549
|
+
// Unknown/tmp layout: best-effort. Relative-path resolution may be imperfect, but the
|
|
2550
|
+
// namespace regex and content-shape checks are location-independent, so the isolation
|
|
2551
|
+
// guarantee holds regardless.
|
|
2552
|
+
return artifactRoot;
|
|
2553
|
+
}
|
|
2554
|
+
|
|
2481
2555
|
function pathIsWithinRoot(candidate: string, root: string): boolean {
|
|
2482
2556
|
const relative = path.relative(root, candidate);
|
|
2483
2557
|
return relative === "" || (!relative.startsWith(`..${path.sep}`) && relative !== ".." && !path.isAbsolute(relative));
|
|
2484
2558
|
}
|
|
2485
2559
|
|
|
2560
|
+
export const NARRATIVE_TRUST_ISOLATION_DIAGNOSTIC = "narrative trust isolation (#619): narrative namespace artifacts cannot be used as workflow evidence";
|
|
2561
|
+
|
|
2562
|
+
function decodeNarrativeReference(value: string): string {
|
|
2563
|
+
try { return decodeURIComponent(value); } catch { return value; }
|
|
2564
|
+
}
|
|
2565
|
+
|
|
2566
|
+
function realpathWithExistingPrefix(candidate: string): string {
|
|
2567
|
+
let cursor = path.resolve(candidate);
|
|
2568
|
+
const suffix: string[] = [];
|
|
2569
|
+
while (!fs.existsSync(cursor)) {
|
|
2570
|
+
const parent = path.dirname(cursor);
|
|
2571
|
+
if (parent === cursor) break;
|
|
2572
|
+
suffix.unshift(path.basename(cursor));
|
|
2573
|
+
cursor = parent;
|
|
2574
|
+
}
|
|
2575
|
+
let canonical = cursor;
|
|
2576
|
+
try { canonical = (fs.realpathSync.native ?? fs.realpathSync)(cursor); } catch { /* resolved lexical prefix is the fallback */ }
|
|
2577
|
+
return path.resolve(canonical, ...suffix);
|
|
2578
|
+
}
|
|
2579
|
+
|
|
2580
|
+
export function isNarrativeNamespacePath(projectRoot: string, resolvedPath: string): boolean {
|
|
2581
|
+
const decoded = decodeNarrativeReference(resolvedPath);
|
|
2582
|
+
const normalized = decoded.replaceAll("\\", "/").toLowerCase();
|
|
2583
|
+
if (/(?:^|[^a-z0-9._-])\.kontourai(?:\/[^/\s"'`]+)*\/narrative(?:$|[/\s"'`])/.test(normalized)) return true;
|
|
2584
|
+
if (/^[a-z][a-z0-9+.-]*:\/\//i.test(decoded) && !decoded.toLowerCase().startsWith("file://")) return false;
|
|
2585
|
+
let local = decoded;
|
|
2586
|
+
if (decoded.toLowerCase().startsWith("file://")) {
|
|
2587
|
+
try { local = fileURLToPath(decoded); } catch { return false; }
|
|
2588
|
+
}
|
|
2589
|
+
const canonicalRoot = realpathWithExistingPrefix(path.resolve(projectRoot, NARRATIVE_NAMESPACE_ROOT)).toLowerCase();
|
|
2590
|
+
const candidate = realpathWithExistingPrefix(path.resolve(projectRoot, local)).toLowerCase();
|
|
2591
|
+
return pathIsWithinRoot(candidate, canonicalRoot);
|
|
2592
|
+
}
|
|
2593
|
+
|
|
2594
|
+
export function isNarrativeArtifactContent(fileBytes: Buffer | string): boolean {
|
|
2595
|
+
const text = Buffer.isBuffer(fileBytes) ? fileBytes.toString("utf8") : fileBytes;
|
|
2596
|
+
try {
|
|
2597
|
+
const parsed: unknown = JSON.parse(text);
|
|
2598
|
+
if (!parsed || typeof parsed !== "object" || Array.isArray(parsed)) return false;
|
|
2599
|
+
const value = parsed as AnyObj;
|
|
2600
|
+
if (value.schema_version === "grounded-execution-narrative/v1") return true;
|
|
2601
|
+
if (value.schema_version === "grounded-runtime-projection/v1") return true;
|
|
2602
|
+
if (value.schema_version === "1.0"
|
|
2603
|
+
&& hasNonEmptyString(value.narrative_id)
|
|
2604
|
+
&& hasNonEmptyString(value.captured_at)
|
|
2605
|
+
&& value.compiler && typeof value.compiler === "object"
|
|
2606
|
+
&& value.capture_completeness && typeof value.capture_completeness === "object"
|
|
2607
|
+
&& Array.isArray(value.sources)) return true;
|
|
2608
|
+
return false;
|
|
2609
|
+
} catch {
|
|
2610
|
+
return text.includes("flow-agents-narrative-composer")
|
|
2611
|
+
&& text.includes("# Grounded Execution Narrative")
|
|
2612
|
+
&& text.includes("## Authority provenance");
|
|
2613
|
+
}
|
|
2614
|
+
}
|
|
2615
|
+
|
|
2616
|
+
function localPathTokens(value: string): string[] {
|
|
2617
|
+
const decoded = decodeNarrativeReference(value);
|
|
2618
|
+
const tokens = decoded.match(/(?:file:\/\/[^\s"'`]+)|(?:[A-Za-z]:[\\/][^\s"'`]+)|(?:\.{0,2}[\\/][^\s"'`]+)|(?:[^\s"'`]+[\\/][^\s"'`]+)/g) ?? [];
|
|
2619
|
+
return tokens.map((token) => token.replace(/^[([{<]+/, "").replace(/[\])}>;,]+$/, ""));
|
|
2620
|
+
}
|
|
2621
|
+
|
|
2622
|
+
function referencesComposedNarrativePath(projectRoot: string, value: string): boolean {
|
|
2623
|
+
const decoded = decodeNarrativeReference(value);
|
|
2624
|
+
const folded = decoded.replaceAll("\\", "/").toLowerCase();
|
|
2625
|
+
if (folded.includes(".kontourai/narrative")) return true;
|
|
2626
|
+
|
|
2627
|
+
// Static shell inspection cannot decide arbitrary runtime variable composition such as
|
|
2628
|
+
// `base=.kontourai; test -f "$base/narrative/..."`. The compensating invariant is that a
|
|
2629
|
+
// command check persists only its command plus exit/observed-output digest; it never reads file
|
|
2630
|
+
// bytes into trust.bundle. Every channel that DOES materialize a file is independently guarded
|
|
2631
|
+
// by canonical path and narrative content shape. Keep that invariant covered end-to-end.
|
|
2632
|
+
const cdPattern = /(?:^|[;&|]\s*)cd\s+(?:"([^"]+)"|'([^']+)'|([^\s;&|]+))\s*&&([\s\S]*)/gi;
|
|
2633
|
+
for (const match of decoded.matchAll(cdPattern)) {
|
|
2634
|
+
const cdTarget = match[1] ?? match[2] ?? match[3] ?? "";
|
|
2635
|
+
const remainder = (match[4] ?? "").replaceAll("\\", "/").toLowerCase();
|
|
2636
|
+
if (!/(?:^|[^a-z0-9._-])narrative\//.test(remainder)) continue;
|
|
2637
|
+
const canonicalTarget = realpathWithExistingPrefix(path.resolve(projectRoot, decodeNarrativeReference(cdTarget)))
|
|
2638
|
+
.replaceAll("\\", "/")
|
|
2639
|
+
.toLowerCase();
|
|
2640
|
+
const canonicalKontourai = realpathWithExistingPrefix(path.resolve(projectRoot, ".kontourai"))
|
|
2641
|
+
.replaceAll("\\", "/")
|
|
2642
|
+
.toLowerCase();
|
|
2643
|
+
if (canonicalTarget === canonicalKontourai
|
|
2644
|
+
|| canonicalTarget.startsWith(`${canonicalKontourai}/`)
|
|
2645
|
+
|| /(?:^|\/)\.kontourai(?:\/|$)/.test(canonicalTarget)) return true;
|
|
2646
|
+
}
|
|
2647
|
+
return false;
|
|
2648
|
+
}
|
|
2649
|
+
|
|
2650
|
+
function narrativeCandidateFile(projectRoot: string, raw: string): string | null {
|
|
2651
|
+
let decoded = decodeNarrativeReference(raw);
|
|
2652
|
+
if (/^[a-z][a-z0-9+.-]*:\/\//i.test(decoded)) {
|
|
2653
|
+
if (!decoded.toLowerCase().startsWith("file://")) return null;
|
|
2654
|
+
try { decoded = fileURLToPath(decoded); } catch { return null; }
|
|
2655
|
+
}
|
|
2656
|
+
const candidate = path.isAbsolute(decoded) ? decoded : path.resolve(projectRoot, decoded);
|
|
2657
|
+
try {
|
|
2658
|
+
const canonical = (fs.realpathSync.native ?? fs.realpathSync)(candidate);
|
|
2659
|
+
const stat = fs.statSync(canonical);
|
|
2660
|
+
return stat.isFile() ? canonical : null;
|
|
2661
|
+
} catch { return null; }
|
|
2662
|
+
}
|
|
2663
|
+
|
|
2664
|
+
function rejectNarrativeReference(projectRoot: string, value: unknown, label: string): void {
|
|
2665
|
+
if (typeof value !== "string") return;
|
|
2666
|
+
const decoded = decodeNarrativeReference(value);
|
|
2667
|
+
if (referencesComposedNarrativePath(projectRoot, decoded)) die(`${label}: ${NARRATIVE_TRUST_ISOLATION_DIAGNOSTIC}`);
|
|
2668
|
+
const candidates = [decoded, ...localPathTokens(decoded)];
|
|
2669
|
+
for (const candidate of new Set(candidates)) {
|
|
2670
|
+
if (isNarrativeNamespacePath(projectRoot, candidate)) die(`${label}: ${NARRATIVE_TRUST_ISOLATION_DIAGNOSTIC}`);
|
|
2671
|
+
const file = narrativeCandidateFile(projectRoot, candidate);
|
|
2672
|
+
if (file && isNarrativeArtifactContent(fs.readFileSync(file))) {
|
|
2673
|
+
die(`${label}: ${NARRATIVE_TRUST_ISOLATION_DIAGNOSTIC}`);
|
|
2674
|
+
}
|
|
2675
|
+
}
|
|
2676
|
+
}
|
|
2677
|
+
|
|
2486
2678
|
function validateLocalEvidenceFile(projectRoot: string, raw: string, label: string): string {
|
|
2487
2679
|
const candidate = path.resolve(projectRoot, raw);
|
|
2488
2680
|
if (!pathIsWithinRoot(candidate, projectRoot)) die(`${label} must remain inside the canonical project root`);
|
|
2681
|
+
rejectNarrativeReference(projectRoot, raw, label);
|
|
2489
2682
|
let stat: fs.Stats;
|
|
2490
2683
|
try { stat = fs.lstatSync(candidate); } catch { die(`${label} does not exist`); }
|
|
2491
2684
|
if (stat.isSymbolicLink() || !stat.isFile()) die(`${label} must be a non-symlink regular file`);
|
|
@@ -2500,6 +2693,13 @@ function globMatches(pattern: string, relative: string): boolean {
|
|
|
2500
2693
|
|
|
2501
2694
|
type GateProducer = { id: string; artifactPatterns: string[]; selfProducedTrustSlices: string[] };
|
|
2502
2695
|
|
|
2696
|
+
export function rejectOperationBoundExpectation(expectationId: string, operation: string): never {
|
|
2697
|
+
const completion = operation === NARRATIVE_PROMOTE_OPERATION
|
|
2698
|
+
? "authenticated external narrative provider completion"
|
|
2699
|
+
: "authenticated external ChangeProvider completion";
|
|
2700
|
+
die(`record-gate-claim cannot satisfy operation-bound expectation ${expectationId}; ${operation} requires ${completion}`);
|
|
2701
|
+
}
|
|
2702
|
+
|
|
2503
2703
|
function expectedGateProducer(flowId: string, stepId: string, expectationId: string): GateProducer {
|
|
2504
2704
|
const manifest = loadJson(path.join(flowAgentsPackageRoot(), "kits", "builder", "kit.json"));
|
|
2505
2705
|
const actions = Array.isArray(manifest.flow_step_actions) ? manifest.flow_step_actions as AnyObj[] : [];
|
|
@@ -2510,7 +2710,7 @@ function expectedGateProducer(flowId: string, stepId: string, expectationId: str
|
|
|
2510
2710
|
: undefined;
|
|
2511
2711
|
if (binding?.interface === "operation") {
|
|
2512
2712
|
const operation = typeof binding.operation === "string" ? binding.operation : "the declared external operation";
|
|
2513
|
-
|
|
2713
|
+
rejectOperationBoundExpectation(expectationId, operation);
|
|
2514
2714
|
}
|
|
2515
2715
|
const skills = Array.isArray(action.skills) ? action.skills.filter((value: unknown): value is string => typeof value === "string") : [];
|
|
2516
2716
|
const roles = Array.isArray(manifest.skill_roles) ? manifest.skill_roles as AnyObj[] : [];
|
|
@@ -2739,6 +2939,102 @@ async function normalizeObservedCommands(commands: string[], projectRoot: string
|
|
|
2739
2939
|
return commands.map((command) => byCommand.get(command)!);
|
|
2740
2940
|
}
|
|
2741
2941
|
|
|
2942
|
+
// #634: the canonical writer's own execution is a first-class observation. On hosts whose
|
|
2943
|
+
// PostToolUse capture never surfaces exit codes, every hook-captured entry is "ambiguous",
|
|
2944
|
+
// the independent capture can never confirm a pass, and the verify gate dead-ends. The writer
|
|
2945
|
+
// just ran the command itself — a real process exit, not an inference — so it appends that
|
|
2946
|
+
// observation to the SAME hash-chained command-log, under the SAME lock protocol the capture
|
|
2947
|
+
// hook uses, visibly attributed via source: "canonical-writer-execution". Precedence stays
|
|
2948
|
+
// honest in the fold (reduceCaptureLogByCommand): any observed fail beats any pass, so a
|
|
2949
|
+
// writer pass can lift ambiguity but can never bury a hook-observed failure. The append is
|
|
2950
|
+
// fail-open with a stderr note: losing the supplementary observation must never fail the
|
|
2951
|
+
// gate-claim write itself. Decision record: docs/decisions/writer-observed-execution.md.
|
|
2952
|
+
export const WRITER_OBSERVATION_SOURCE = "canonical-writer-execution";
|
|
2953
|
+
const WRITER_LOCK_RETRY_MS = 5;
|
|
2954
|
+
const WRITER_LOCK_MAX_TRIES = 200;
|
|
2955
|
+
const WRITER_LOCK_STALE_MS = 10000;
|
|
2956
|
+
|
|
2957
|
+
function loadCommandLogChain(): { CHAIN_GENESIS: string; computeChainHash: (prevHash: string, record: AnyObj) => string } {
|
|
2958
|
+
const _req = createRequire(import.meta.url);
|
|
2959
|
+
const chainPath = path.resolve(path.dirname(fileURLToPath(import.meta.url)), "../../../scripts/lib/command-log-chain.js");
|
|
2960
|
+
return _req(chainPath) as { CHAIN_GENESIS: string; computeChainHash: (prevHash: string, record: AnyObj) => string };
|
|
2961
|
+
}
|
|
2962
|
+
|
|
2963
|
+
function writerSleepSync(ms: number): void {
|
|
2964
|
+
try { Atomics.wait(new Int32Array(new SharedArrayBuffer(4)), 0, 0, ms); }
|
|
2965
|
+
catch { /* SharedArrayBuffer/Atomics unavailable — skip the backoff */ }
|
|
2966
|
+
}
|
|
2967
|
+
|
|
2968
|
+
function writerAcquireLock(lockFile: string): number | null {
|
|
2969
|
+
for (let i = 0; i < WRITER_LOCK_MAX_TRIES; i++) {
|
|
2970
|
+
try {
|
|
2971
|
+
const fd = fs.openSync(lockFile, "wx");
|
|
2972
|
+
try { fs.writeSync(fd, String(process.pid)); } catch { /* pid is advisory only */ }
|
|
2973
|
+
return fd;
|
|
2974
|
+
} catch (err) {
|
|
2975
|
+
if (!err || (err as NodeJS.ErrnoException).code !== "EEXIST") return null;
|
|
2976
|
+
try {
|
|
2977
|
+
const st = fs.statSync(lockFile);
|
|
2978
|
+
if (Date.now() - st.mtimeMs > WRITER_LOCK_STALE_MS) { fs.unlinkSync(lockFile); continue; }
|
|
2979
|
+
} catch { continue; }
|
|
2980
|
+
writerSleepSync(WRITER_LOCK_RETRY_MS);
|
|
2981
|
+
}
|
|
2982
|
+
}
|
|
2983
|
+
return null;
|
|
2984
|
+
}
|
|
2985
|
+
|
|
2986
|
+
function writerReadLastChainState(logFile: string, genesis: string): { seq: number; hash: string } {
|
|
2987
|
+
let raw = "";
|
|
2988
|
+
try { raw = fs.readFileSync(logFile, "utf8"); } catch { return { seq: -1, hash: genesis }; }
|
|
2989
|
+
const lines = raw.split("\n").filter((l) => l.trim());
|
|
2990
|
+
for (let i = lines.length - 1; i >= 0; i--) {
|
|
2991
|
+
let entry: AnyObj;
|
|
2992
|
+
try { entry = JSON.parse(lines[i]!); } catch { continue; }
|
|
2993
|
+
if (entry && entry._chain && typeof entry._chain.hash === "string" && typeof entry._chain.seq === "number") {
|
|
2994
|
+
return { seq: entry._chain.seq, hash: entry._chain.hash };
|
|
2995
|
+
}
|
|
2996
|
+
}
|
|
2997
|
+
return { seq: -1, hash: genesis };
|
|
2998
|
+
}
|
|
2999
|
+
|
|
3000
|
+
export function appendWriterObservedCommands(dir: string, observed: ObservedCommand[], timestamp: string): void {
|
|
3001
|
+
if (observed.length === 0) return;
|
|
3002
|
+
try {
|
|
3003
|
+
const chain = loadCommandLogChain();
|
|
3004
|
+
const logFile = path.join(dir, "command-log.jsonl");
|
|
3005
|
+
const lockFile = `${logFile}.lock`;
|
|
3006
|
+
const fd = writerAcquireLock(lockFile);
|
|
3007
|
+
try {
|
|
3008
|
+
let { seq, hash: prevHash } = writerReadLastChainState(logFile, chain.CHAIN_GENESIS);
|
|
3009
|
+
const lines: string[] = [];
|
|
3010
|
+
for (const entry of observed) {
|
|
3011
|
+
const record: AnyObj = {
|
|
3012
|
+
command: entry.command,
|
|
3013
|
+
observedResult: entry.exit_code === 0 ? "pass" : "fail",
|
|
3014
|
+
exitCode: entry.exit_code,
|
|
3015
|
+
capturedAt: timestamp,
|
|
3016
|
+
source: WRITER_OBSERVATION_SOURCE,
|
|
3017
|
+
writer: {
|
|
3018
|
+
output_sha256: entry.output_sha256,
|
|
3019
|
+
...(Number.isSafeInteger(entry.test_count) ? { test_count: entry.test_count } : {}),
|
|
3020
|
+
...(entry.execution_proof ? { execution_proof: entry.execution_proof } : {}),
|
|
3021
|
+
},
|
|
3022
|
+
};
|
|
3023
|
+
seq += 1;
|
|
3024
|
+
const hashValue = chain.computeChainHash(prevHash, record);
|
|
3025
|
+
record._chain = { seq, prevHash, hash: hashValue };
|
|
3026
|
+
prevHash = hashValue;
|
|
3027
|
+
lines.push(JSON.stringify(record));
|
|
3028
|
+
}
|
|
3029
|
+
fs.appendFileSync(logFile, `${lines.join("\n")}\n`);
|
|
3030
|
+
} finally {
|
|
3031
|
+
if (fd !== null) { try { fs.closeSync(fd); } catch { /* closed */ } try { fs.unlinkSync(lockFile); } catch { /* removed */ } }
|
|
3032
|
+
}
|
|
3033
|
+
} catch (error) {
|
|
3034
|
+
process.stderr.write(`[record-gate-claim] writer observation append failed (fail-open, capture unaffected): ${error instanceof Error ? error.message : String(error)}\n`);
|
|
3035
|
+
}
|
|
3036
|
+
}
|
|
3037
|
+
|
|
2742
3038
|
function requireObservedCommandRefs(refs: AnyObj[], observedCommands: ReadonlySet<string>, label: string, requireAll = false): void {
|
|
2743
3039
|
const commandRefs = refs.filter((ref) => ref.kind === "command");
|
|
2744
3040
|
if (commandRefs.length === 0) die(`${label} requires a command evidence ref matching a successful observed command`);
|
|
@@ -2751,7 +3047,7 @@ function requireObservedCommandRefs(refs: AnyObj[], observedCommands: ReadonlySe
|
|
|
2751
3047
|
}
|
|
2752
3048
|
}
|
|
2753
3049
|
|
|
2754
|
-
function completePassingCriteria(existing: AnyObj[], raw: string[], observedCommands:
|
|
3050
|
+
function completePassingCriteria(existing: AnyObj[], raw: string[], observedCommands: readonly ObservedCommand[], verifiedAt: string, projectRoot: string): AnyObj[] {
|
|
2755
3051
|
if (raw.length === 0) die("record-gate-claim requires --criterion-json for a passing tests-evidence claim");
|
|
2756
3052
|
const incoming = raw.map((value) => parseJson(value, "--criterion-json"));
|
|
2757
3053
|
const expectedById = new Map<string, AnyObj>();
|
|
@@ -2764,20 +3060,23 @@ function completePassingCriteria(existing: AnyObj[], raw: string[], observedComm
|
|
|
2764
3060
|
if (new Set(ids).size !== ids.length || ids.length !== expectedIds.length || ids.some((id) => !expectedIds.includes(id))) {
|
|
2765
3061
|
die(`--criterion-json must cover every declared acceptance criterion exactly once (expected: ${expectedIds.join(", ") || "none"}; received: ${ids.join(", ") || "none"})`);
|
|
2766
3062
|
}
|
|
3063
|
+
const observedCommandNames = new Set(observedCommands.map((observation) => observation.command));
|
|
2767
3064
|
return incoming.map((criterion, index) => {
|
|
2768
3065
|
if (Object.keys(criterion).some((key) => !["id", "status", "evidence_refs"].includes(key))) die(`criterion ${ids[index]} may update only id, status, and evidence_refs`);
|
|
2769
3066
|
if (criterion.status !== "pass") die(`criterion ${ids[index]} must have status pass for a passing tests-evidence claim`);
|
|
2770
|
-
const refs = normalizeEvidenceRefs(criterion.evidence_refs, `criterion ${ids[index]} evidence_refs
|
|
3067
|
+
const refs = normalizeEvidenceRefs(criterion.evidence_refs, `criterion ${ids[index]} evidence_refs`, projectRoot);
|
|
2771
3068
|
if (refs.length === 0) die(`criterion ${ids[index]} requires reviewable evidence_refs`);
|
|
2772
|
-
requireObservedCommandRefs(refs,
|
|
2773
|
-
|
|
3069
|
+
requireObservedCommandRefs(refs, observedCommandNames, `criterion ${ids[index]}`);
|
|
3070
|
+
const referencedCommands = new Set(refs.filter((ref) => ref.kind === "command").map(commandFromEvidenceRef));
|
|
3071
|
+
const criterionObservedCommands = observedCommands.filter((observation) => referencedCommands.has(observation.command));
|
|
3072
|
+
return markCanonicallyObservedCriterion({ ...expectedById.get(ids[index])!, status: "pass", evidence_refs: refs, identity_version: 2, verified_at: verifiedAt, _observed_commands: criterionObservedCommands });
|
|
2774
3073
|
});
|
|
2775
3074
|
}
|
|
2776
3075
|
|
|
2777
3076
|
const critiqueStatuses = new Set<string>(WORKFLOW_CRITIQUE_STATUSES);
|
|
2778
3077
|
const safeCritiqueId = /^[a-z][a-z0-9_-]*$/;
|
|
2779
3078
|
|
|
2780
|
-
function normalizeCritiqueLanes(raw: string[]): AnyObj[] {
|
|
3079
|
+
function normalizeCritiqueLanes(raw: string[], projectRoot: string): AnyObj[] {
|
|
2781
3080
|
if (raw.length === 0) die("record-critique requires at least one --lane-json");
|
|
2782
3081
|
const lanes = raw.map((value, index) => {
|
|
2783
3082
|
const lane = parseJson(value, "--lane-json");
|
|
@@ -2786,7 +3085,7 @@ function normalizeCritiqueLanes(raw: string[]): AnyObj[] {
|
|
|
2786
3085
|
if (!safeCritiqueId.test(String(lane.id ?? ""))) die(`--lane-json ${index} id must be a unique safe identifier`);
|
|
2787
3086
|
if (!critiqueStatuses.has(String(lane.status ?? ""))) die(`--lane-json ${index} status must be one of: pass, fail, not_verified`);
|
|
2788
3087
|
if (!hasNonEmptyString(lane.summary)) die(`--lane-json ${index} summary must be non-empty`);
|
|
2789
|
-
const evidenceRefs = normalizeEvidenceRefs(lane.evidence_refs, `--lane-json ${index} evidence_refs
|
|
3088
|
+
const evidenceRefs = normalizeEvidenceRefs(lane.evidence_refs, `--lane-json ${index} evidence_refs`, projectRoot);
|
|
2790
3089
|
if (evidenceRefs.length === 0) die(`--lane-json ${index} requires structured reviewable evidence_refs`);
|
|
2791
3090
|
return { id: lane.id, status: lane.status, summary: lane.summary, evidence_refs: evidenceRefs };
|
|
2792
3091
|
});
|
|
@@ -2804,6 +3103,13 @@ function reviewTargetArtifacts(dir: string, rawPaths: string[], label: string):
|
|
|
2804
3103
|
}
|
|
2805
3104
|
|
|
2806
3105
|
function reviewTargetArtifactsMatch(dir: string, reviewTarget: unknown): boolean {
|
|
3106
|
+
if (reviewTarget && typeof reviewTarget === "object" && !Array.isArray(reviewTarget)) {
|
|
3107
|
+
const projectRoot = narrativeGuardRoot(dir);
|
|
3108
|
+
const artifacts = (reviewTarget as AnyObj).artifacts;
|
|
3109
|
+
if (Array.isArray(artifacts)) {
|
|
3110
|
+
artifacts.forEach((artifact, index) => rejectNarrativeReference(projectRoot, artifact?.file, `critique review_target artifact ${index}`));
|
|
3111
|
+
}
|
|
3112
|
+
}
|
|
2807
3113
|
try {
|
|
2808
3114
|
if (!reviewTarget || typeof reviewTarget !== "object" || Array.isArray(reviewTarget)) return false;
|
|
2809
3115
|
const artifacts = (reviewTarget as AnyObj).artifacts;
|
|
@@ -2869,7 +3175,7 @@ function critiqueIsCleanAndCurrent(dir: string, critique: AnyObj): boolean {
|
|
|
2869
3175
|
// BEFORE normalizeCheck runs — see applyGateClaimStamp). This does not weaken new-mint
|
|
2870
3176
|
// enforcement: a NOVEL gate-claim-* id (not already present at all) is still rejected exactly as
|
|
2871
3177
|
// before, and superseding a STAMPED existing id is now rejected too.
|
|
2872
|
-
export function normalizeCheck(raw: AnyObj, allowGateClaimPrefix = false, existingCheckStampById?: ReadonlyMap<string, boolean
|
|
3178
|
+
export function normalizeCheck(raw: AnyObj, allowGateClaimPrefix = false, existingCheckStampById?: ReadonlyMap<string, boolean>, projectRoot = process.cwd()): AnyObj {
|
|
2873
3179
|
const check = { ...raw };
|
|
2874
3180
|
if (!check.id || !check.kind || !check.status || !check.summary) die("check requires id, kind, status, and summary");
|
|
2875
3181
|
if (!allowGateClaimPrefix && typeof check.id === "string" && check.id.startsWith("gate-claim-")) {
|
|
@@ -2879,13 +3185,17 @@ export function normalizeCheck(raw: AnyObj, allowGateClaimPrefix = false, existi
|
|
|
2879
3185
|
}
|
|
2880
3186
|
if (!checkKinds.has(check.kind)) die("kind must be one of: build, types, lint, test, command, security, diff, browser, runtime, policy, external");
|
|
2881
3187
|
if (!checkStatuses.has(check.status)) die("status must be one of: pass, fail, not_verified, skip");
|
|
3188
|
+
rejectNarrativeReference(projectRoot, check.command, `check ${String(check.id)} command`);
|
|
2882
3189
|
validateRunnableCheckCommand(check, `check ${String(check.id)}`);
|
|
2883
|
-
if (Array.isArray(check.standard_refs)) for (const ref of check.standard_refs)
|
|
2884
|
-
|
|
2885
|
-
|
|
3190
|
+
if (Array.isArray(check.standard_refs)) for (const ref of check.standard_refs) {
|
|
3191
|
+
if (!["junit", "sarif", "coverage", "veritas"].includes(ref.standard)) die("standard must be one of");
|
|
3192
|
+
rejectNarrativeReference(projectRoot, ref.ref, `check ${String(check.id)} standard_ref`);
|
|
3193
|
+
}
|
|
3194
|
+
if (check.artifact_refs) check.artifact_refs = normalizeEvidenceRefs(check.artifact_refs, "artifact_refs", projectRoot);
|
|
3195
|
+
if (check.surface_trust_refs) check.surface_trust_refs = normalizeSurfaceRefs(check.surface_trust_refs, projectRoot);
|
|
2886
3196
|
return check;
|
|
2887
3197
|
}
|
|
2888
|
-
function normalizeSurfaceRefs(refs: any): AnyObj[] {
|
|
3198
|
+
function normalizeSurfaceRefs(refs: any, projectRoot: string): AnyObj[] {
|
|
2889
3199
|
if (!Array.isArray(refs)) die("surface_trust_refs must be an array");
|
|
2890
3200
|
// Use the cached @kontourai/surface module for advisory inline validation of referenced
|
|
2891
3201
|
// trust.bundle files. Fail-open when surface is not yet loaded (surface loads on first
|
|
@@ -2895,6 +3205,7 @@ function normalizeSurfaceRefs(refs: any): AnyObj[] {
|
|
|
2895
3205
|
const keys = JSON.stringify(ref).match(/"([^"]+)":/g) ?? [];
|
|
2896
3206
|
for (const key of keys.map((k) => k.slice(1, -2))) if (key.toLowerCase().includes("veritas")) die(`unsupported field in Surface trust ref: ${key}`);
|
|
2897
3207
|
const out = { ...ref };
|
|
3208
|
+
rejectNarrativeReference(projectRoot, out.artifact_ref, "surface trust artifact_ref");
|
|
2898
3209
|
// trust.bundle is the canonical Hachure-aligned artifact kind; TrustReport/Trust Snapshot are legacy aliases
|
|
2899
3210
|
if (!["trust.bundle", "TrustReport", "Trust Snapshot"].includes(out.artifact_kind)) die("artifact_kind must be one of: trust.bundle, TrustReport, Trust Snapshot");
|
|
2900
3211
|
// When surface is loaded, validate the referenced trust artifact if it is a local file.
|
|
@@ -2973,23 +3284,25 @@ export function kitIdentityFromBundle(
|
|
|
2973
3284
|
// 3. Genuinely unknown — never fallback to "builder".
|
|
2974
3285
|
return { claimType: "unknown.trust.bundle", kitId: "unknown", subject: "unknown-kit", gateId: "unknown.trust.bundle" };
|
|
2975
3286
|
}
|
|
2976
|
-
function surfaceCheckFromArtifact(file: string, index: number): AnyObj {
|
|
2977
|
-
|
|
3287
|
+
function surfaceCheckFromArtifact(file: string, index: number, projectRoot = process.cwd()): AnyObj {
|
|
3288
|
+
rejectNarrativeReference(projectRoot, file, `--surface-trust-json ${index}`);
|
|
3289
|
+
const resolvedFile = path.isAbsolute(file) ? file : path.resolve(projectRoot, decodeNarrativeReference(file));
|
|
3290
|
+
const raw = JSON.parse(read(resolvedFile));
|
|
2978
3291
|
const lower = JSON.stringify(raw).toLowerCase();
|
|
2979
3292
|
// Structurally read kit identity from the bundle — never hardcode "builder".
|
|
2980
|
-
const { claimType: bundleClaimType, subject: bundleSubject, gateId: bundleGateId } = kitIdentityFromBundle(raw,
|
|
3293
|
+
const { claimType: bundleClaimType, subject: bundleSubject, gateId: bundleGateId } = kitIdentityFromBundle(raw, resolvedFile);
|
|
2981
3294
|
let ref: AnyObj;
|
|
2982
3295
|
if (lower.includes("provider") && lower.includes("absent")) {
|
|
2983
|
-
ref = { artifact_kind: "trust.bundle", artifact_ref:
|
|
3296
|
+
ref = { artifact_kind: "trust.bundle", artifact_ref: resolvedFile, gate_id: "provider.unavailable", claim_type: bundleClaimType, claim_status: "unknown", subject: bundleSubject, freshness: { status: "unknown", summary: "No trust provider is configured" }, authority: { producer: "unknown", summary: "No trust provider is configured" }, integrity: { status: "unknown", summary: "Unknown" }, status: "not_verified", summary: "No trust provider is configured" };
|
|
2984
3297
|
} else if (lower.includes("artifact") && lower.includes("absent")) {
|
|
2985
|
-
ref = { artifact_kind: "trust.bundle", artifact_ref:
|
|
3298
|
+
ref = { artifact_kind: "trust.bundle", artifact_ref: resolvedFile, gate_id: "artifact.unavailable", claim_type: bundleClaimType, claim_status: "unknown", subject: bundleSubject, freshness: { status: "unknown", summary: "Artifact not readable" }, authority: { producer: "unknown", summary: "Artifact not readable" }, integrity: { status: "unknown", summary: "Artifact not readable" }, status: "not_verified", summary: "artifact not readable" };
|
|
2986
3299
|
} else {
|
|
2987
3300
|
const claimStatus = lower.includes("rejected") ? "rejected" : "accepted";
|
|
2988
3301
|
const freshness = lower.includes("stale") ? "stale" : "fresh";
|
|
2989
3302
|
const producer = lower.includes("missing-authority") ? "unknown" : "surface-local";
|
|
2990
3303
|
const integrity = lower.includes("mismatch") ? "mismatch" : "matched";
|
|
2991
3304
|
// Use trust.bundle as the canonical Hachure-aligned artifact_kind for all trust-backed evidence refs
|
|
2992
|
-
ref = { artifact_kind: "trust.bundle", artifact_ref:
|
|
3305
|
+
ref = { artifact_kind: "trust.bundle", artifact_ref: resolvedFile, gate_id: bundleGateId, claim_type: bundleClaimType, claim_status: claimStatus, subject: bundleSubject, freshness: { status: freshness, summary: freshness === "fresh" ? "fresh" : "not currently verifiable" }, authority: { producer, summary: producer === "unknown" ? "missing authority" : "Local Surface trust producer." }, integrity: { status: integrity, summary: integrity === "matched" ? "matched" : "integrity mismatch" } };
|
|
2993
3306
|
ref.status = deriveSurfaceStatus(ref);
|
|
2994
3307
|
ref.summary = ref.status === "pass" ? "accepted" : ref.status === "not_verified" ? "not currently verifiable" : (claimStatus === "rejected" ? "rejected" : producer === "unknown" ? "missing authority" : "integrity mismatch");
|
|
2995
3308
|
}
|
|
@@ -3008,9 +3321,10 @@ function surfaceCheckFromArtifact(file: string, index: number): AnyObj {
|
|
|
3008
3321
|
* not the first thing an agent reaches for.
|
|
3009
3322
|
*/
|
|
3010
3323
|
function validateAcceptanceEvidenceRefs(dir: string, p?: ReturnType<typeof parseArgs>): void {
|
|
3011
|
-
|
|
3324
|
+
const projectRoot = narrativeGuardRoot(dir);
|
|
3325
|
+
const skipRunnabilityGuard = p?.flags.has("skip-evidence-ref-runnability-guard") ?? false;
|
|
3326
|
+
if (skipRunnabilityGuard) {
|
|
3012
3327
|
process.stderr.write("[record-evidence] evidence-ref runnability guard skipped via --skip-evidence-ref-runnability-guard\n");
|
|
3013
|
-
return;
|
|
3014
3328
|
}
|
|
3015
3329
|
const file = path.join(dir, "acceptance.json");
|
|
3016
3330
|
if (!fs.existsSync(file)) return;
|
|
@@ -3019,7 +3333,8 @@ function validateAcceptanceEvidenceRefs(dir: string, p?: ReturnType<typeof parse
|
|
|
3019
3333
|
const { isRunnableCommandText, isAmbiguousAbsenceCommand } = loadRunnableCommandHelper();
|
|
3020
3334
|
data.criteria.forEach((criterion: AnyObj, index: number) => {
|
|
3021
3335
|
if (criterion.evidence_refs === undefined) return;
|
|
3022
|
-
const refs = normalizeEvidenceRefs(criterion.evidence_refs, `acceptance.criteria[${index}].evidence_refs
|
|
3336
|
+
const refs = normalizeEvidenceRefs(criterion.evidence_refs, `acceptance.criteria[${index}].evidence_refs`, projectRoot);
|
|
3337
|
+
if (skipRunnabilityGuard) return;
|
|
3023
3338
|
// #412 (AC8): a kind:"command" ref's command source (excerpt, falling back to url when that
|
|
3024
3339
|
// is where the command string lives) must be a literally runnable shell command, not prose
|
|
3025
3340
|
// describing a manual verification step — reject at RECORD time instead of letting it reach
|
|
@@ -3089,8 +3404,17 @@ function requireStampedClaim(claim: AnyObj, dir: string): string {
|
|
|
3089
3404
|
}
|
|
3090
3405
|
return origin;
|
|
3091
3406
|
}
|
|
3407
|
+
|
|
3408
|
+
function loadTrustBundleForTrustMachinery(dir: string): AnyObj {
|
|
3409
|
+
const bundleFile = path.join(dir, "trust.bundle");
|
|
3410
|
+
if (fs.existsSync(bundleFile) && isNarrativeArtifactContent(fs.readFileSync(bundleFile))) {
|
|
3411
|
+
die(`trust.bundle in ${dir}: ${NARRATIVE_TRUST_ISOLATION_DIAGNOSTIC}; restore a genuine trust.bundle and keep rendered narratives under ${NARRATIVE_NAMESPACE_ROOT}`);
|
|
3412
|
+
}
|
|
3413
|
+
return loadJson(bundleFile);
|
|
3414
|
+
}
|
|
3415
|
+
|
|
3092
3416
|
function checksFromBundle(dir: string): AnyObj[] {
|
|
3093
|
-
const bundle =
|
|
3417
|
+
const bundle = loadTrustBundleForTrustMachinery(dir);
|
|
3094
3418
|
const allClaims: AnyObj[] = Array.isArray(bundle.claims) ? bundle.claims : [];
|
|
3095
3419
|
// Validate stamps on every claim up front — any unstamped claim anywhere in the bundle marks
|
|
3096
3420
|
// it pre-supersession, regardless of whether it is check/acceptance/critique-typed.
|
|
@@ -3275,10 +3599,20 @@ function readBundleState(dir: string): { checks: AnyObj[]; criteria: AnyObj[]; c
|
|
|
3275
3599
|
const acceptance = loadJson(path.join(dir, "acceptance.json"));
|
|
3276
3600
|
const bundledCriteria = criteriaFromBundle(dir);
|
|
3277
3601
|
const acceptedCriteria = Array.isArray(acceptance.criteria) ? acceptance.criteria as AnyObj[] : [];
|
|
3602
|
+
if (acceptedCriteria.some((criterion) => !hasNonEmptyString(criterion?.id) || !hasNonEmptyString(criterion?.description))) {
|
|
3603
|
+
die("acceptance.json contains a criterion without a non-empty id and description — refusing to rebuild trust.bundle without durable acceptance identity");
|
|
3604
|
+
}
|
|
3605
|
+
const acceptedIds = acceptedCriteria.map((criterion) => String(criterion.id));
|
|
3606
|
+
if (new Set(acceptedIds).size !== acceptedIds.length) {
|
|
3607
|
+
die("acceptance.json criterion ids must be unique — refusing to rebuild trust.bundle with ambiguous acceptance identity");
|
|
3608
|
+
}
|
|
3278
3609
|
const contractSignature = (criteria: AnyObj[]): string => JSON.stringify(criteria.map((criterion) => ({
|
|
3279
3610
|
id: criterion.id ?? null,
|
|
3280
3611
|
description: criterion.description ?? null,
|
|
3281
3612
|
})));
|
|
3613
|
+
// Planning may establish or refine a valid criterion contract after session startup. Once the
|
|
3614
|
+
// signatures agree, the bundle remains authoritative for verified status and observed-command
|
|
3615
|
+
// provenance; artifact status edits alone cannot manufacture completion.
|
|
3282
3616
|
const criteria = acceptedCriteria.length > 0 && contractSignature(acceptedCriteria) !== contractSignature(bundledCriteria)
|
|
3283
3617
|
? acceptedCriteria
|
|
3284
3618
|
: (bundledCriteria.length > 0 ? bundledCriteria : acceptedCriteria);
|
|
@@ -3302,7 +3636,7 @@ function mergeChecksById(existing: AnyObj[], incoming: AnyObj[]): AnyObj[] {
|
|
|
3302
3636
|
return [...byId.values()];
|
|
3303
3637
|
}
|
|
3304
3638
|
function critiquesFromBundle(dir: string): AnyObj[] {
|
|
3305
|
-
const bundle =
|
|
3639
|
+
const bundle = loadTrustBundleForTrustMachinery(dir);
|
|
3306
3640
|
if (!Array.isArray(bundle.claims)) return [];
|
|
3307
3641
|
for (const c of bundle.claims) requireStampedClaim(c, dir);
|
|
3308
3642
|
// A claim is a CRITIQUE when its origin is "critique" (authoritative — see requireStampedClaim
|
|
@@ -3328,7 +3662,7 @@ function critiquesFromBundle(dir: string): AnyObj[] {
|
|
|
3328
3662
|
}
|
|
3329
3663
|
|
|
3330
3664
|
function criteriaFromBundle(dir: string): AnyObj[] {
|
|
3331
|
-
const bundle =
|
|
3665
|
+
const bundle = loadTrustBundleForTrustMachinery(dir);
|
|
3332
3666
|
if (!Array.isArray(bundle.claims)) return [];
|
|
3333
3667
|
for (const claim of bundle.claims) requireStampedClaim(claim, dir);
|
|
3334
3668
|
return bundle.claims
|
|
@@ -3336,14 +3670,28 @@ function criteriaFromBundle(dir: string): AnyObj[] {
|
|
|
3336
3670
|
.map((claim: AnyObj) => {
|
|
3337
3671
|
const md = claim.metadata && typeof claim.metadata === "object" && !Array.isArray(claim.metadata) ? claim.metadata as AnyObj : {};
|
|
3338
3672
|
const saved = md.criterion && typeof md.criterion === "object" && !Array.isArray(md.criterion) ? md.criterion as AnyObj : {};
|
|
3339
|
-
|
|
3673
|
+
const criterion = {
|
|
3340
3674
|
id: typeof saved.id === "string" ? saved.id : String(claim.subjectId || "").split("/").pop(),
|
|
3341
3675
|
description: typeof saved.description === "string" ? saved.description : (claim.fieldOrBehavior || ""),
|
|
3342
3676
|
status: typeof saved.status === "string" ? saved.status : (claim.value ?? "not_verified"),
|
|
3343
3677
|
evidence_refs: Array.isArray(saved.evidence_refs) ? saved.evidence_refs : [],
|
|
3678
|
+
...(Array.isArray(saved.observed_commands) ? { _observed_commands: saved.observed_commands } : {}),
|
|
3344
3679
|
...(typeof saved.verified_at === "string" ? { verified_at: saved.verified_at } : {}),
|
|
3345
3680
|
...(saved.identity_version === 2 ? { identity_version: 2 } : {}),
|
|
3346
3681
|
};
|
|
3682
|
+
const observedCommands = Array.isArray(saved.observed_commands) ? saved.observed_commands as AnyObj[] : [];
|
|
3683
|
+
const evidence = Array.isArray(bundle.evidence) ? bundle.evidence.filter((item: AnyObj) => item?.claimId === claim.id) : [];
|
|
3684
|
+
const event = Array.isArray(bundle.events) ? bundle.events.find((item: AnyObj) => item?.claimId === claim.id && item.status === "verified") : null;
|
|
3685
|
+
const evidenceIds = new Set(evidence.map((item: AnyObj) => item.id));
|
|
3686
|
+
const hasCanonicalEvidence = claim.value === "pass"
|
|
3687
|
+
&& claim.status === "verified"
|
|
3688
|
+
&& observedCommands.length > 0
|
|
3689
|
+
&& evidence.length === observedCommands.length
|
|
3690
|
+
&& evidence.every((item: AnyObj) => item.evidenceType === "test_output" && item.passing === true && observedCommands.some((observation: AnyObj) => observation.command === item.execution?.label))
|
|
3691
|
+
&& Array.isArray(event?.evidenceIds)
|
|
3692
|
+
&& event.evidenceIds.length === evidenceIds.size
|
|
3693
|
+
&& event.evidenceIds.every((id: string) => evidenceIds.has(id));
|
|
3694
|
+
return hasCanonicalEvidence ? markCanonicallyObservedCriterion(criterion) : criterion;
|
|
3347
3695
|
})
|
|
3348
3696
|
.filter((criterion: AnyObj) => typeof criterion.id === "string" && criterion.id.length > 0);
|
|
3349
3697
|
}
|
|
@@ -3378,7 +3726,11 @@ async function recordEvidence(p: ReturnType<typeof parseArgs>): Promise<number>
|
|
|
3378
3726
|
// (not re-read) as _existingState for the compose-safe merge — one readBundleState call.
|
|
3379
3727
|
const _existingState = readBundleState(dir);
|
|
3380
3728
|
const _existingCheckStampById = existingCheckStampMap(_existingState.checks);
|
|
3381
|
-
const
|
|
3729
|
+
const projectRoot = narrativeGuardRoot(dir);
|
|
3730
|
+
const _checksRaw = [
|
|
3731
|
+
...opts(p, "check-json").map((v) => normalizeCheck(parseJson(v, "--check-json"), false, _existingCheckStampById, projectRoot)),
|
|
3732
|
+
...opts(p, "surface-trust-json").map((file, index) => surfaceCheckFromArtifact(file, index, projectRoot)),
|
|
3733
|
+
];
|
|
3382
3734
|
// WS8 (AC4, iteration 2): a command-backed check reconciles against CI or fails — it can
|
|
3383
3735
|
// NEVER be waived. Reject --accepted-gap-reason/--waived-by on any check whose evidence
|
|
3384
3736
|
// classifies as test_output (build/types/lint/test/command, and security/browser/runtime
|
|
@@ -3470,6 +3822,7 @@ async function recordCheck(p: ReturnType<typeof parseArgs>, commandArgv: string[
|
|
|
3470
3822
|
if (commandArgv?.length && commandString) die("record-check: pass the command via EITHER `-- <command...>` OR --command, not both");
|
|
3471
3823
|
|
|
3472
3824
|
const repoRoot = findRepoRootFromDir(dir);
|
|
3825
|
+
rejectNarrativeReference(repoRoot, commandArgv?.join(" ") ?? commandString, "record-check command");
|
|
3473
3826
|
let displayCommand: string;
|
|
3474
3827
|
let exitCode: number | null;
|
|
3475
3828
|
let stdout = "";
|
|
@@ -3548,7 +3901,7 @@ ${stderr}` : ""}`.trim());
|
|
|
3548
3901
|
status,
|
|
3549
3902
|
summary,
|
|
3550
3903
|
command: displayCommand,
|
|
3551
|
-
}, false, _existingCheckStampById);
|
|
3904
|
+
}, false, _existingCheckStampById, repoRoot);
|
|
3552
3905
|
if (outputSha256) check._output_sha256 = outputSha256;
|
|
3553
3906
|
|
|
3554
3907
|
const _mergedChecks = mergeChecksById(_existingState.checks, [check]);
|
|
@@ -3655,10 +4008,19 @@ async function recordGateClaim(p: ReturnType<typeof parseArgs>): Promise<number>
|
|
|
3655
4008
|
const mustRunTests = targetExpectation.id === "tests-evidence" && statusVal === "pass";
|
|
3656
4009
|
const observedCommandRaw = opts(p, "observed-command-json");
|
|
3657
4010
|
if (mustRunTests && gateCommands.length === 0) die("record-gate-claim requires at least one --command for a passing tests-evidence claim");
|
|
3658
|
-
|
|
4011
|
+
// #619: the narrative evidence-ref guards (validateEvidenceRef / normalizeCheck /
|
|
4012
|
+
// completePassingCriteria) need a non-null, location-independent project root that never
|
|
4013
|
+
// dies on a non-canonical session. normalizeObservedCommands still requires the strict
|
|
4014
|
+
// canonical root, but only when there are commands to normalize.
|
|
4015
|
+
const projectRoot = narrativeGuardRoot(dir);
|
|
4016
|
+
const canonicalRoot = gateCommands.length > 0 ? canonicalProjectRootForSession(dir) : null;
|
|
4017
|
+
for (const command of gateCommands) rejectNarrativeReference(projectRoot, command, "record-gate-claim command");
|
|
3659
4018
|
const observedCommands = gateCommands.length > 0
|
|
3660
|
-
? await normalizeObservedCommands(gateCommands,
|
|
4019
|
+
? await normalizeObservedCommands(gateCommands, canonicalRoot!, mustRunTests, statusVal)
|
|
3661
4020
|
: [];
|
|
4021
|
+
// #634: persist the writer's real executions into the hash-chained command-log so the
|
|
4022
|
+
// capture fold has a deterministic observation even on exit-code-blind hosts.
|
|
4023
|
+
appendWriterObservedCommands(dir, observedCommands, ts);
|
|
3662
4024
|
const observedCommandNames = new Set(observedCommands.map((entry) => entry.command));
|
|
3663
4025
|
let outputSha256: string | null = null;
|
|
3664
4026
|
if (!mustRunTests && gateCommands.length > 1) die("record-gate-claim accepts repeatable --command only for passing tests-evidence claims");
|
|
@@ -3687,7 +4049,7 @@ async function recordGateClaim(p: ReturnType<typeof parseArgs>): Promise<number>
|
|
|
3687
4049
|
};
|
|
3688
4050
|
|
|
3689
4051
|
// Include structured evidence refs if provided
|
|
3690
|
-
const evidenceRefs: AnyObj[] = opts(p, "evidence-ref-json").map((v) => validateEvidenceRef(parseJson(v, "--evidence-ref-json"), "--evidence-ref-json"));
|
|
4052
|
+
const evidenceRefs: AnyObj[] = opts(p, "evidence-ref-json").map((v) => validateEvidenceRef(parseJson(v, "--evidence-ref-json"), "--evidence-ref-json", projectRoot));
|
|
3691
4053
|
const producer = expectedGateProducer(exactFlowContext?.flowId ?? activeStep.flowId, activeStep.stepId, targetExpectation.id);
|
|
3692
4054
|
if (statusVal === "pass") validateReviewableGateEvidence(dir, slug, evidenceRefs, producer, `gate claim ${targetExpectation.id}`);
|
|
3693
4055
|
if (mustRunTests) requireObservedCommandRefs(evidenceRefs, observedCommandNames, "a passing tests-evidence claim", true);
|
|
@@ -3708,7 +4070,7 @@ async function recordGateClaim(p: ReturnType<typeof parseArgs>): Promise<number>
|
|
|
3708
4070
|
if (gateCommand) check.command = gateCommand;
|
|
3709
4071
|
if (observedCommands.length > 0) check._observed_commands = observedCommands;
|
|
3710
4072
|
|
|
3711
|
-
const checkNormalized = normalizeCheck(check, /* allowGateClaimPrefix */ true);
|
|
4073
|
+
const checkNormalized = normalizeCheck(check, /* allowGateClaimPrefix */ true, undefined, projectRoot);
|
|
3712
4074
|
if (outputSha256) checkNormalized._output_sha256 = outputSha256;
|
|
3713
4075
|
// WS8 (ADR 0020): honor the accepted-gap waiver flags for a gate claim too.
|
|
3714
4076
|
const gateWaiver = parseWaiver(p, ts);
|
|
@@ -3721,7 +4083,7 @@ async function recordGateClaim(p: ReturnType<typeof parseArgs>): Promise<number>
|
|
|
3721
4083
|
// SAME expectation id supersedes the earlier check for that expectation (mergeChecksById); a
|
|
3722
4084
|
// gate claim against a different expectation is additive.
|
|
3723
4085
|
const _existingState = readBundleState(dir);
|
|
3724
|
-
const criteria = mustRunTests ? completePassingCriteria(_existingState.criteria, opts(p, "criterion-json"),
|
|
4086
|
+
const criteria = mustRunTests ? completePassingCriteria(_existingState.criteria, opts(p, "criterion-json"), observedCommands, ts, projectRoot) : _existingState.criteria;
|
|
3725
4087
|
if (mustRunTests) {
|
|
3726
4088
|
const liveCritiques = _existingState.critiques.filter((critique) => !critique.superseded_by);
|
|
3727
4089
|
if (liveCritiques.length === 0 || liveCritiques.some((critique) => !critiqueIsCleanAndCurrent(dir, critique))) {
|
|
@@ -3778,6 +4140,7 @@ async function promote(p: ReturnType<typeof parseArgs>): Promise<number> {
|
|
|
3778
4140
|
// repo-relative paths when the ref lives under the repo, so the claim is portable.
|
|
3779
4141
|
const targets: string[] = [];
|
|
3780
4142
|
for (const raw of rawPaths) {
|
|
4143
|
+
rejectNarrativeReference(repoRoot, raw, "promote --evidence-path");
|
|
3781
4144
|
const abs = path.isAbsolute(raw) ? raw : path.resolve(repoRoot, raw);
|
|
3782
4145
|
if (!fs.existsSync(abs)) die(`promote --evidence-path does not exist on disk: ${raw} (resolved: ${abs}). Promotion evidence refs must point at durable docs that were actually written.`);
|
|
3783
4146
|
const rel = path.relative(repoRoot, abs);
|
|
@@ -3904,19 +4267,24 @@ async function advanceState(p: ReturnType<typeof parseArgs>): Promise<number> {
|
|
|
3904
4267
|
return 0;
|
|
3905
4268
|
}
|
|
3906
4269
|
|
|
3907
|
-
export function normalizeFinding(raw: AnyObj): AnyObj {
|
|
4270
|
+
export function normalizeFinding(raw: AnyObj, projectRoot = process.cwd()): AnyObj {
|
|
3908
4271
|
if (raw.file_refs !== undefined && !Array.isArray(raw.file_refs)) die("file_refs must be an array");
|
|
4272
|
+
for (const ref of raw.file_refs ?? []) {
|
|
4273
|
+
if (typeof ref !== "string") die("file_refs entries must be strings");
|
|
4274
|
+
rejectNarrativeReference(projectRoot, ref, "finding file_ref");
|
|
4275
|
+
}
|
|
3909
4276
|
return raw;
|
|
3910
4277
|
}
|
|
3911
4278
|
|
|
3912
4279
|
async function recordCritique(p: ReturnType<typeof parseArgs>): Promise<number> {
|
|
3913
4280
|
const dir = artifactDirFrom(p.positional[0] || die("artifact directory is required"));
|
|
3914
4281
|
const slug = taskSlugFor(dir, opt(p, "task-slug"));
|
|
4282
|
+
const projectRoot = canonicalProjectRootForSession(dir);
|
|
3915
4283
|
const verdict = opt(p, "verdict");
|
|
3916
4284
|
if (!critiqueStatuses.has(verdict)) die("record-critique requires --verdict pass, fail, or not_verified");
|
|
3917
4285
|
const summary = opt(p, "summary");
|
|
3918
4286
|
if (!hasNonEmptyString(summary)) die("record-critique requires --summary");
|
|
3919
|
-
const lanes = normalizeCritiqueLanes(opts(p, "lane-json"));
|
|
4287
|
+
const lanes = normalizeCritiqueLanes(opts(p, "lane-json"), projectRoot);
|
|
3920
4288
|
const reviewArtifacts = reviewTargetArtifacts(dir, opts(p, "artifact-ref"), "record-critique review_target");
|
|
3921
4289
|
if (verdict === "pass" && (lanes.some((lane) => lane.status !== "pass") || reviewArtifacts.length === 0)) {
|
|
3922
4290
|
die("a passing critique requires every lane to pass and at least one local reviewed --artifact-ref");
|
|
@@ -3946,12 +4314,12 @@ async function recordCritique(p: ReturnType<typeof parseArgs>): Promise<number>
|
|
|
3946
4314
|
review_target: {
|
|
3947
4315
|
artifacts: reviewArtifacts,
|
|
3948
4316
|
workspace_snapshot: captureReviewWorkspaceSnapshot(
|
|
3949
|
-
|
|
4317
|
+
projectRoot,
|
|
3950
4318
|
reviewArtifacts.map((artifact) => ({ file: String(artifact.file), sha256: String(artifact.sha256) })),
|
|
3951
4319
|
),
|
|
3952
4320
|
},
|
|
3953
4321
|
artifact_refs: reviewArtifacts.map((artifact) => artifact.file),
|
|
3954
|
-
findings: opts(p, "finding-json").map((v) => normalizeFinding(parseJson(v, "--finding-json"))),
|
|
4322
|
+
findings: opts(p, "finding-json").map((v) => normalizeFinding(parseJson(v, "--finding-json"), projectRoot)),
|
|
3955
4323
|
};
|
|
3956
4324
|
if (critique.verdict === "pass" && critique.findings.some((f: AnyObj) => f.status === "open")) die("required critique must pass");
|
|
3957
4325
|
// #267/#282: supersede-by-critique-id. The latest write for a critique id wins for
|
|
@@ -4020,14 +4388,43 @@ async function importCritique(p: ReturnType<typeof parseArgs>): Promise<number>
|
|
|
4020
4388
|
if (verdict !== "pass") die("required critique must pass");
|
|
4021
4389
|
return result;
|
|
4022
4390
|
}
|
|
4391
|
+
|
|
4392
|
+
function rejectNarrativeReleaseReferences(projectRoot: string, value: unknown, label: string, referenceField = false): void {
|
|
4393
|
+
if (typeof value === "string") {
|
|
4394
|
+
if (referenceField) rejectNarrativeReference(projectRoot, value, label);
|
|
4395
|
+
return;
|
|
4396
|
+
}
|
|
4397
|
+
if (Array.isArray(value)) {
|
|
4398
|
+
for (const entry of value) rejectNarrativeReleaseReferences(projectRoot, entry, label, referenceField);
|
|
4399
|
+
return;
|
|
4400
|
+
}
|
|
4401
|
+
if (!value || typeof value !== "object") return;
|
|
4402
|
+
for (const [key, entry] of Object.entries(value as AnyObj)) {
|
|
4403
|
+
const isReferenceField = /^(?:refs?|files?|paths?|(?:evidence|artifact|source)_(?:refs?|files?|paths?))$/.test(key);
|
|
4404
|
+
rejectNarrativeReleaseReferences(projectRoot, entry, `${label}.${key}`, isReferenceField);
|
|
4405
|
+
}
|
|
4406
|
+
}
|
|
4407
|
+
|
|
4023
4408
|
async function recordRelease(p: ReturnType<typeof parseArgs>): Promise<number> {
|
|
4024
4409
|
const dir = artifactDirFrom(p.positional[0] || die("artifact directory is required"));
|
|
4025
4410
|
const slug = taskSlugFor(dir, opt(p, "task-slug"));
|
|
4411
|
+
const projectRoot = narrativeGuardRoot(dir);
|
|
4026
4412
|
const decision = opt(p, "decision");
|
|
4027
4413
|
if (!["merge", "release", "deploy", "hold", "rollback_required"].includes(decision)) die("decision must be one of: merge, release, deploy, hold, rollback_required");
|
|
4028
4414
|
const gates = opts(p, "gate-json").map((v) => parseJson(v, "--gate-json"));
|
|
4029
4415
|
if (["merge", "release", "deploy"].includes(decision) && !gates.some((g) => g.name === decision && g.status === "pass")) die(`positive release decision requires ${decision} gate to pass`);
|
|
4030
|
-
const
|
|
4416
|
+
const evidenceRef = opt(p, "evidence-ref");
|
|
4417
|
+
const rollbackPlan = parseJson(opt(p, "rollback-json", '{"status":"not_required","summary":"Not required.","owner":"maintainer"}'), "--rollback-json");
|
|
4418
|
+
const observabilityPlan = parseJson(opt(p, "observability-json", '{"status":"not_required","summary":"Not required."}'), "--observability-json");
|
|
4419
|
+
const postDeployChecks = opts(p, "post-deploy-json").map((v) => parseJson(v, "--post-deploy-json"));
|
|
4420
|
+
const docs = parseJson(opt(p, "docs-json", '{"status":"not_needed","summary":"Not needed."}'), "--docs-json");
|
|
4421
|
+
rejectNarrativeReference(projectRoot, evidenceRef, "release evidence_ref");
|
|
4422
|
+
rejectNarrativeReleaseReferences(projectRoot, gates, "release gates");
|
|
4423
|
+
rejectNarrativeReleaseReferences(projectRoot, rollbackPlan, "release rollback_plan");
|
|
4424
|
+
rejectNarrativeReleaseReferences(projectRoot, observabilityPlan, "release observability_plan");
|
|
4425
|
+
rejectNarrativeReleaseReferences(projectRoot, postDeployChecks, "release post_deploy_checks");
|
|
4426
|
+
rejectNarrativeReleaseReferences(projectRoot, docs, "release docs");
|
|
4427
|
+
const payload = { ...sidecarBase(slug), decision: opt(p, "decision"), updated_at: opt(p, "timestamp", now()), scope: opt(p, "scope"), evidence_ref: evidenceRef, gates, rollback_plan: rollbackPlan, observability_plan: observabilityPlan, post_deploy_checks: postDeployChecks, docs };
|
|
4031
4428
|
const stateSummary = opt(p, "summary").trim() || `Release readiness recorded for ${decision}.`;
|
|
4032
4429
|
writeJson(path.join(dir, "release.json"), payload);
|
|
4033
4430
|
writeState(dir, slug, "delivered", "release", payload.updated_at, stateSummary);
|
|
@@ -5386,8 +5783,12 @@ function validateLearningPrevention(prevention: unknown): void {
|
|
|
5386
5783
|
if (typeof value.status !== "string" || value.status.length === 0) die("correction.prevention.status is required");
|
|
5387
5784
|
if (!["open", "completed", "accepted", "deferred", "rejected"].includes(value.status)) die("correction.prevention.status must be one of: open, completed, accepted, deferred, rejected");
|
|
5388
5785
|
}
|
|
5389
|
-
export function normalizeLearning(raw: AnyObj, timestamp: string): AnyObj {
|
|
5786
|
+
export function normalizeLearning(raw: AnyObj, timestamp: string, projectRoot = process.cwd()): AnyObj {
|
|
5390
5787
|
if (!Array.isArray(raw.source_refs)) die("source_refs must be an array");
|
|
5788
|
+
for (const ref of raw.source_refs) {
|
|
5789
|
+
if (typeof ref !== "string") die("source_refs entries must be strings");
|
|
5790
|
+
rejectNarrativeReference(projectRoot, ref, "learning source_ref");
|
|
5791
|
+
}
|
|
5391
5792
|
if (!Array.isArray(raw.facts)) die("facts must be an array");
|
|
5392
5793
|
if (!Array.isArray(raw.routing)) die("routing must be an array");
|
|
5393
5794
|
if (!["success", "failure", "mixed", "unknown"].includes(raw.outcome)) die("learning outcome must be one of: success, failure, mixed, unknown");
|
|
@@ -5398,7 +5799,8 @@ async function recordLearning(p: ReturnType<typeof parseArgs>): Promise<number>
|
|
|
5398
5799
|
const dir = artifactDirFrom(p.positional[0] || die("artifact directory is required"));
|
|
5399
5800
|
const slug = taskSlugFor(dir, opt(p, "task-slug"));
|
|
5400
5801
|
const timestamp = opt(p, "timestamp", now());
|
|
5401
|
-
const
|
|
5802
|
+
const projectRoot = narrativeGuardRoot(dir);
|
|
5803
|
+
const records = opts(p, "record-json").map((v) => normalizeLearning(parseJson(v, "--record-json"), timestamp, projectRoot));
|
|
5402
5804
|
const status = opt(p, "status", "learned");
|
|
5403
5805
|
if (status === "learned" && records.some((r) => r.routing.some((x: AnyObj) => x.status === "open"))) die("learning status learned cannot have open routing");
|
|
5404
5806
|
if (status === "learned" && records.some((r) => r.correction === undefined)) die("learning status learned requires every record to include correction.needed");
|
|
@@ -5418,7 +5820,7 @@ function evidenceClean(dir: string): boolean {
|
|
|
5418
5820
|
// legacy (pre-bundle-era) sessions that never wrote a trust.bundle at all — unrelated to origin
|
|
5419
5821
|
// stamping. When a trust.bundle IS present, every claim must be stamped (requireStampedClaim);
|
|
5420
5822
|
// there is no claimType-derivation fallback for an unstamped claim (#268/#344).
|
|
5421
|
-
const bundle =
|
|
5823
|
+
const bundle = loadTrustBundleForTrustMachinery(dir);
|
|
5422
5824
|
if (Array.isArray(bundle.claims)) {
|
|
5423
5825
|
for (const c of bundle.claims) requireStampedClaim(c, dir);
|
|
5424
5826
|
const checkClaims = (bundle.claims as AnyObj[]).filter((c: AnyObj) => c && claimOrigin(c) === "check");
|
|
@@ -5437,7 +5839,7 @@ function evidenceClean(dir: string): boolean {
|
|
|
5437
5839
|
}
|
|
5438
5840
|
function critiqueClean(dir: string): boolean {
|
|
5439
5841
|
// trust.bundle is the sole critique artifact. Legacy critique.json must not influence gates.
|
|
5440
|
-
const bundle =
|
|
5842
|
+
const bundle = loadTrustBundleForTrustMachinery(dir);
|
|
5441
5843
|
if (Array.isArray(bundle.claims)) {
|
|
5442
5844
|
for (const c of bundle.claims) requireStampedClaim(c, dir);
|
|
5443
5845
|
const critiqueClaims = (bundle.claims as AnyObj[]).filter((c: AnyObj) => {
|
|
@@ -5457,9 +5859,14 @@ function assertExistingLearningValid(dir: string): void {
|
|
|
5457
5859
|
const file = path.join(dir, "learning.json");
|
|
5458
5860
|
if (!fs.existsSync(file)) return;
|
|
5459
5861
|
const data = loadJson(file);
|
|
5862
|
+
const projectRoot = narrativeGuardRoot(dir);
|
|
5460
5863
|
if (!Array.isArray(data.records)) die("learning records must be an array");
|
|
5461
5864
|
for (const record of data.records) {
|
|
5462
5865
|
if (!Array.isArray(record.source_refs)) die("source_refs must be an array");
|
|
5866
|
+
for (const ref of record.source_refs) {
|
|
5867
|
+
if (typeof ref !== "string") die("source_refs entries must be strings");
|
|
5868
|
+
rejectNarrativeReference(projectRoot, ref, "learning source_ref");
|
|
5869
|
+
}
|
|
5463
5870
|
if (!Array.isArray(record.facts)) die("facts must be an array");
|
|
5464
5871
|
if (!Array.isArray(record.routing)) die("routing must be an array");
|
|
5465
5872
|
validateLearningCorrection(record);
|
|
@@ -5470,6 +5877,7 @@ async function dogfoodPass(p: ReturnType<typeof parseArgs>): Promise<number> {
|
|
|
5470
5877
|
const root = opt(p, "artifact-root") ? path.resolve(opt(p, "artifact-root")) : defaultArtifactRootForRead();
|
|
5471
5878
|
const dir = path.resolve(opt(p, "artifact-dir") || currentDir(root) || "");
|
|
5472
5879
|
requireArtifactDirUnderRoot(dir, root);
|
|
5880
|
+
const projectRoot = narrativeGuardRoot(dir);
|
|
5473
5881
|
assertExistingLearningValid(dir);
|
|
5474
5882
|
const verdict = opt(p, "verdict");
|
|
5475
5883
|
if (verdict === "pass") {
|
|
@@ -5482,7 +5890,7 @@ async function dogfoodPass(p: ReturnType<typeof parseArgs>): Promise<number> {
|
|
|
5482
5890
|
// trust.bundle does not yet exist (loadJson falls back to {}), exactly as
|
|
5483
5891
|
// recordEvidence/recordCheck rely on.
|
|
5484
5892
|
const _dogfoodExistingCheckStampById = existingCheckStampMap(readBundleState(dir).checks);
|
|
5485
|
-
const checks = opts(p, "check-json").map((v) => normalizeCheck(parseJson(v, "--check-json"), false, _dogfoodExistingCheckStampById));
|
|
5893
|
+
const checks = opts(p, "check-json").map((v) => normalizeCheck(parseJson(v, "--check-json"), false, _dogfoodExistingCheckStampById, projectRoot));
|
|
5486
5894
|
if (checks.some((c) => c.status !== "pass" && c.status !== "skip")) die("clean evidence requires all non-skipped checks to pass");
|
|
5487
5895
|
// Phase 4c: evidence check reads from trust.bundle (sole verification artifact); legacy evidence.json fallback in evidenceClean.
|
|
5488
5896
|
// #268/#344: builder.* check/critique claims count as clean evidence via their authoritative origin stamp.
|
|
@@ -5493,13 +5901,13 @@ async function dogfoodPass(p: ReturnType<typeof parseArgs>): Promise<number> {
|
|
|
5493
5901
|
if (!_hasBundleEvidence && !_hasLegacyEvidence && !fs.existsSync(path.join(dir, "trust.bundle")) && !fs.existsSync(path.join(dir, "evidence.json")) && checks.length === 0) die("cannot mark clean without passing evidence");
|
|
5494
5902
|
if (p.flags.has("require-critique") || opt(p, "release-decision")) {
|
|
5495
5903
|
const newCritiqueVerdict = opt(p, "critique-verdict", "pass");
|
|
5496
|
-
for (const value of opts(p, "finding-json")) normalizeFinding(parseJson(value, "--finding-json"));
|
|
5904
|
+
for (const value of opts(p, "finding-json")) normalizeFinding(parseJson(value, "--finding-json"), projectRoot);
|
|
5497
5905
|
if (newCritiqueVerdict !== "pass") die(opt(p, "release-decision") ? "requires clean critique" : "requires clean critique before recording pass evidence");
|
|
5498
5906
|
if (!opt(p, "critique-id") && !critiqueClean(dir)) die("requires passing critique");
|
|
5499
5907
|
if (!critiqueClean(dir) && fs.existsSync(path.join(dir, "trust.bundle"))) die(opt(p, "release-decision") ? "requires clean critique" : "requires clean critique before recording pass evidence");
|
|
5500
5908
|
}
|
|
5501
5909
|
}
|
|
5502
|
-
const learningRecords = opts(p, "learning-record-json").map((v) => normalizeLearning(parseJson(v, "--learning-record-json"), opt(p, "timestamp", now())));
|
|
5910
|
+
const learningRecords = opts(p, "learning-record-json").map((v) => normalizeLearning(parseJson(v, "--learning-record-json"), opt(p, "timestamp", now()), projectRoot));
|
|
5503
5911
|
if (opt(p, "learning-status") === "learned" && learningRecords.some((r) => r.routing.some((x: AnyObj) => x.status === "open"))) die("learned status cannot have open learning routing");
|
|
5504
5912
|
if (opt(p, "learning-status") === "learned" && learningRecords.some((r) => r.correction === undefined)) die("learned status requires every learning record to include correction.needed");
|
|
5505
5913
|
if (opts(p, "check-json").length) await recordEvidence({ ...p, positional: [dir], opts: { ...p.opts, verdict: [verdict] }, flags: p.flags });
|
|
@@ -5655,7 +6063,7 @@ export function gateAdvisoryFix(
|
|
|
5655
6063
|
}
|
|
5656
6064
|
|
|
5657
6065
|
/**
|
|
5658
|
-
* Build a schema-conformant InquiryRecord for
|
|
6066
|
+
* Build a schema-conformant InquiryRecord for @kontourai/surface's canonical InquiryRecord schema.
|
|
5659
6067
|
* Strips Surface-internal fields (identityLinkIds, transitiveRuleIds) from
|
|
5660
6068
|
* resolutionPath that are valid in the TS type but not in the JSON schema.
|
|
5661
6069
|
* Sets answer.value to the gate-review value-add: { calibration, advisoryFix, gateFired, sessionSlug }.
|
|
@@ -5855,16 +6263,17 @@ async function gateReview(p: ReturnType<typeof parseArgs>): Promise<number> {
|
|
|
5855
6263
|
|
|
5856
6264
|
const records = buildGateInquiryRecords(bundle, blockSignal, slug, expectedCriterionIds, surface);
|
|
5857
6265
|
|
|
5858
|
-
// Validate each record against the
|
|
5859
|
-
const validator = getHachureInquiryRecordValidator();
|
|
6266
|
+
// Validate each record against the canonical InquiryRecord schema via Surface (fail-open)
|
|
5860
6267
|
let schemaValid = true;
|
|
5861
6268
|
const validationErrors: string[] = [];
|
|
5862
|
-
|
|
5863
|
-
|
|
5864
|
-
|
|
5865
|
-
|
|
6269
|
+
const inquiryValidator = (surface as { validateInquiryRecord?: (r: unknown) => unknown } | null)?.validateInquiryRecord;
|
|
6270
|
+
if (typeof inquiryValidator === "function") {
|
|
6271
|
+
for (const record of records) {
|
|
6272
|
+
try {
|
|
6273
|
+
inquiryValidator(record);
|
|
6274
|
+
} catch (err) {
|
|
5866
6275
|
schemaValid = false;
|
|
5867
|
-
validationErrors.push(
|
|
6276
|
+
validationErrors.push(`${record["id"] ?? "?"}: ${err instanceof Error ? err.message : String(err)}`);
|
|
5868
6277
|
}
|
|
5869
6278
|
}
|
|
5870
6279
|
}
|
|
@@ -5885,7 +6294,7 @@ async function gateReview(p: ReturnType<typeof parseArgs>): Promise<number> {
|
|
|
5885
6294
|
.filter(([, n]) => n > 0)
|
|
5886
6295
|
.map(([k, n]) => `${k}=${n}`)
|
|
5887
6296
|
.join(", ");
|
|
5888
|
-
const schemaTag =
|
|
6297
|
+
const schemaTag = typeof inquiryValidator === "function" ? (schemaValid ? " schema:valid" : " schema:INVALID") : " schema:unavailable";
|
|
5889
6298
|
console.log(`gate-review: ${records.length} InquiryRecord(s) [${summary}]${schemaTag} → ${outputPath}`);
|
|
5890
6299
|
return 0;
|
|
5891
6300
|
}
|
|
@@ -6389,14 +6798,13 @@ async function liveness(p: ReturnType<typeof parseArgs>): Promise<number> {
|
|
|
6389
6798
|
}
|
|
6390
6799
|
// ─────────────────────────────────────────────────────────────────────────────
|
|
6391
6800
|
|
|
6392
|
-
// ─── Claim Lookup —
|
|
6393
|
-
//
|
|
6394
|
-
//
|
|
6395
|
-
//
|
|
6396
|
-
//
|
|
6397
|
-
|
|
6398
|
-
export type { ClaimEvidenceItem, ClaimExplanation } from "
|
|
6399
|
-
import { buildClaimExplanation } from "./sidecar-claim-explain.js";
|
|
6801
|
+
// ─── Claim Lookup — consumes Surface explainClaim (#171 lift complete) ───────
|
|
6802
|
+
// The pure projection this file once hosted (sidecar-claim-explain.ts, ops#22)
|
|
6803
|
+
// was lifted verbatim into @kontourai/surface as explainClaim(report, claimId)
|
|
6804
|
+
// (kontourai/surface#151, 2.10.0) with the drilldown composition folded inside
|
|
6805
|
+
// (same fail-soft semantics the caller-side enrichment had). Types re-export
|
|
6806
|
+
// from Surface so the library facade keeps compiling for downstream importers.
|
|
6807
|
+
export type { ClaimEvidenceItem, ClaimExplanation } from "@kontourai/surface";
|
|
6400
6808
|
|
|
6401
6809
|
/**
|
|
6402
6810
|
* claim <id> <dir>
|
|
@@ -6438,29 +6846,17 @@ Available claim ids:
|
|
|
6438
6846
|
|
|
6439
6847
|
// Load Surface via tryLoadSurface() (ESM, cached, fail-open pattern)
|
|
6440
6848
|
const surface = await tryLoadSurface();
|
|
6441
|
-
if (!surface || typeof surface.buildTrustReport !== "function" || typeof surface.
|
|
6442
|
-
process.stderr.write(`[claim] @kontourai/surface unavailable or missing buildTrustReport/
|
|
6849
|
+
if (!surface || typeof surface.buildTrustReport !== "function" || typeof surface.explainClaim !== "function") {
|
|
6850
|
+
process.stderr.write(`[claim] @kontourai/surface unavailable or missing buildTrustReport/explainClaim (needs >=2.10)
|
|
6443
6851
|
`);
|
|
6444
6852
|
return 0; // fail-open, consistent with gate-review pattern
|
|
6445
6853
|
}
|
|
6446
6854
|
|
|
6447
|
-
// Build TrustReport
|
|
6855
|
+
// Build TrustReport, then the structured explanation — Surface's explainClaim
|
|
6856
|
+
// (#171 lift) composes the derivation drilldown internally, fail-soft, exactly
|
|
6857
|
+
// as the retired local helper + caller-side enrichment did.
|
|
6448
6858
|
const report = surface.buildTrustReport(bundle as unknown as Record<string, unknown>);
|
|
6449
|
-
|
|
6450
|
-
// Build the structured explanation (pure, promotable to #171)
|
|
6451
|
-
const explanation = buildClaimExplanation(report, bundle as unknown as Record<string, unknown>, claimId);
|
|
6452
|
-
|
|
6453
|
-
// Enrich the why.directInputs/leafClaims/diagnostics from the drilldown
|
|
6454
|
-
try {
|
|
6455
|
-
const drilldown = surface.buildDerivationDrilldown(report, claimId) as AnyObj;
|
|
6456
|
-
if (drilldown) {
|
|
6457
|
-
explanation.why.directInputs = Array.isArray(drilldown.directInputs) ? drilldown.directInputs : [];
|
|
6458
|
-
explanation.why.leafClaims = Array.isArray(drilldown.leafClaims) ? drilldown.leafClaims : [];
|
|
6459
|
-
explanation.why.diagnostics = Array.isArray(drilldown.diagnostics) ? drilldown.diagnostics : [];
|
|
6460
|
-
}
|
|
6461
|
-
} catch {
|
|
6462
|
-
// buildDerivationDrilldown threw (e.g. claim not in report) — proceed without drilldown
|
|
6463
|
-
}
|
|
6859
|
+
const explanation = surface.explainClaim(report, claimId) as import("@kontourai/surface").ClaimExplanation;
|
|
6464
6860
|
|
|
6465
6861
|
if (p.flags.has("json")) {
|
|
6466
6862
|
console.log(JSON.stringify(explanation, null, 2));
|
|
@@ -6547,7 +6943,7 @@ Available claim ids:
|
|
|
6547
6943
|
if (explanation.why.changeRecords.length > 0) {
|
|
6548
6944
|
lines.push(` Change records: ${explanation.why.changeRecords.length}`);
|
|
6549
6945
|
for (const cr of explanation.why.changeRecords) {
|
|
6550
|
-
lines.push(` - ${cr.action ?? "?"} at ${cr.at ?? cr.createdAt ?? "?"}`);
|
|
6946
|
+
lines.push(` - ${cr.action ?? "?"} at ${(cr as AnyObj).at ?? cr.createdAt ?? "?"}`);
|
|
6551
6947
|
}
|
|
6552
6948
|
}
|
|
6553
6949
|
|