@sun-asterisk/sungen 3.2.35 → 3.3.0-alpha.10
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/dist/basis/anchor-grammar.d.ts +32 -0
- package/dist/basis/anchor-grammar.d.ts.map +1 -0
- package/dist/basis/anchor-grammar.js +82 -0
- package/dist/basis/anchor-grammar.js.map +1 -0
- package/dist/basis/anchor-hash.d.ts +6 -0
- package/dist/basis/anchor-hash.d.ts.map +1 -0
- package/dist/basis/anchor-hash.js +28 -0
- package/dist/basis/anchor-hash.js.map +1 -0
- package/dist/basis/bm51-tables.d.ts +20 -0
- package/dist/basis/bm51-tables.d.ts.map +1 -0
- package/dist/basis/bm51-tables.js +71 -0
- package/dist/basis/bm51-tables.js.map +1 -0
- package/dist/basis/extract-spec-anchors.d.ts +39 -0
- package/dist/basis/extract-spec-anchors.d.ts.map +1 -0
- package/dist/basis/extract-spec-anchors.js +81 -0
- package/dist/basis/extract-spec-anchors.js.map +1 -0
- package/dist/basis/migration-sentinel.d.ts +47 -0
- package/dist/basis/migration-sentinel.d.ts.map +1 -0
- package/dist/basis/migration-sentinel.js +219 -0
- package/dist/basis/migration-sentinel.js.map +1 -0
- package/dist/basis/spec-frontmatter.d.ts +18 -0
- package/dist/basis/spec-frontmatter.d.ts.map +1 -0
- package/dist/basis/spec-frontmatter.js +55 -0
- package/dist/basis/spec-frontmatter.js.map +1 -0
- package/dist/basis/spec-sections.d.ts +18 -0
- package/dist/basis/spec-sections.d.ts.map +1 -0
- package/dist/basis/spec-sections.js +27 -0
- package/dist/basis/spec-sections.js.map +1 -0
- package/dist/basis/test-basis-schema.d.ts +28 -0
- package/dist/basis/test-basis-schema.d.ts.map +1 -0
- package/dist/basis/test-basis-schema.js +141 -0
- package/dist/basis/test-basis-schema.js.map +1 -0
- package/dist/capabilities/discover.d.ts +4 -0
- package/dist/capabilities/discover.d.ts.map +1 -1
- package/dist/capabilities/discover.js +16 -2
- package/dist/capabilities/discover.js.map +1 -1
- package/dist/capabilities/registry.d.ts +15 -0
- package/dist/capabilities/registry.d.ts.map +1 -1
- package/dist/capabilities/registry.js.map +1 -1
- package/dist/cli/case-triage-hint.d.ts +14 -0
- package/dist/cli/case-triage-hint.d.ts.map +1 -0
- package/dist/cli/case-triage-hint.js +23 -0
- package/dist/cli/case-triage-hint.js.map +1 -0
- package/dist/cli/commands/audit.js +1 -1
- package/dist/cli/commands/audit.js.map +1 -1
- package/dist/cli/commands/capability.js +1 -1
- package/dist/cli/commands/capability.js.map +1 -1
- package/dist/cli/commands/contract-derive-command.d.ts +12 -0
- package/dist/cli/commands/contract-derive-command.d.ts.map +1 -0
- package/dist/cli/commands/contract-derive-command.js +113 -0
- package/dist/cli/commands/contract-derive-command.js.map +1 -0
- package/dist/cli/commands/contract.d.ts +20 -0
- package/dist/cli/commands/contract.d.ts.map +1 -0
- package/dist/cli/commands/contract.js +195 -0
- package/dist/cli/commands/contract.js.map +1 -0
- package/dist/cli/commands/delivery.d.ts.map +1 -1
- package/dist/cli/commands/delivery.js +109 -19
- package/dist/cli/commands/delivery.js.map +1 -1
- package/dist/cli/commands/gate.d.ts.map +1 -1
- package/dist/cli/commands/gate.js +2 -1
- package/dist/cli/commands/gate.js.map +1 -1
- package/dist/cli/commands/generate.d.ts.map +1 -1
- package/dist/cli/commands/generate.js +17 -3
- package/dist/cli/commands/generate.js.map +1 -1
- package/dist/cli/commands/journey.d.ts.map +1 -1
- package/dist/cli/commands/journey.js +47 -17
- package/dist/cli/commands/journey.js.map +1 -1
- package/dist/cli/commands/next.d.ts +13 -0
- package/dist/cli/commands/next.d.ts.map +1 -1
- package/dist/cli/commands/next.js +98 -9
- package/dist/cli/commands/next.js.map +1 -1
- package/dist/cli/commands/triage.d.ts +2 -1
- package/dist/cli/commands/triage.d.ts.map +1 -1
- package/dist/cli/commands/triage.js +45 -3
- package/dist/cli/commands/triage.js.map +1 -1
- package/dist/cli/commands/update.d.ts +11 -0
- package/dist/cli/commands/update.d.ts.map +1 -1
- package/dist/cli/commands/update.js +37 -8
- package/dist/cli/commands/update.js.map +1 -1
- package/dist/cli/index.js +24 -33
- package/dist/cli/index.js.map +1 -1
- package/dist/cli/json-error-on-exit.d.ts +34 -0
- package/dist/cli/json-error-on-exit.d.ts.map +1 -0
- package/dist/cli/json-error-on-exit.js +63 -0
- package/dist/cli/json-error-on-exit.js.map +1 -0
- package/dist/cli/project-sungen-invocation.d.ts +13 -0
- package/dist/cli/project-sungen-invocation.d.ts.map +1 -0
- package/dist/cli/project-sungen-invocation.js +95 -0
- package/dist/cli/project-sungen-invocation.js.map +1 -0
- package/dist/cli/run-command-hints.d.ts +9 -0
- package/dist/cli/run-command-hints.d.ts.map +1 -0
- package/dist/cli/run-command-hints.js +84 -0
- package/dist/cli/run-command-hints.js.map +1 -0
- package/dist/cli/waiting-for-you-block.d.ts +18 -0
- package/dist/cli/waiting-for-you-block.d.ts.map +1 -0
- package/dist/cli/waiting-for-you-block.js +22 -0
- package/dist/cli/waiting-for-you-block.js.map +1 -0
- package/dist/contract/accept-opaque-steps.d.ts +3 -0
- package/dist/contract/accept-opaque-steps.d.ts.map +1 -0
- package/dist/contract/accept-opaque-steps.js +114 -0
- package/dist/contract/accept-opaque-steps.js.map +1 -0
- package/dist/contract/approve-contract.d.ts +38 -0
- package/dist/contract/approve-contract.d.ts.map +1 -0
- package/dist/contract/approve-contract.js +93 -0
- package/dist/contract/approve-contract.js.map +1 -0
- package/dist/contract/assertion-matcher-vocabulary.d.ts +24 -0
- package/dist/contract/assertion-matcher-vocabulary.d.ts.map +1 -0
- package/dist/contract/assertion-matcher-vocabulary.js +100 -0
- package/dist/contract/assertion-matcher-vocabulary.js.map +1 -0
- package/dist/contract/build-contract-verdict.d.ts +32 -0
- package/dist/contract/build-contract-verdict.d.ts.map +1 -0
- package/dist/contract/build-contract-verdict.js +123 -0
- package/dist/contract/build-contract-verdict.js.map +1 -0
- package/dist/contract/build-trace-conformance.d.ts +22 -0
- package/dist/contract/build-trace-conformance.d.ts.map +1 -0
- package/dist/contract/build-trace-conformance.js +151 -0
- package/dist/contract/build-trace-conformance.js.map +1 -0
- package/dist/contract/classify-contract-change.d.ts +24 -0
- package/dist/contract/classify-contract-change.d.ts.map +1 -0
- package/dist/contract/classify-contract-change.js +98 -0
- package/dist/contract/classify-contract-change.js.map +1 -0
- package/dist/contract/classify-contract-unit.d.ts +18 -0
- package/dist/contract/classify-contract-unit.d.ts.map +1 -0
- package/dist/contract/classify-contract-unit.js +49 -0
- package/dist/contract/classify-contract-unit.js.map +1 -0
- package/dist/contract/classify-trace-conformance.d.ts +47 -0
- package/dist/contract/classify-trace-conformance.d.ts.map +1 -0
- package/dist/contract/classify-trace-conformance.js +164 -0
- package/dist/contract/classify-trace-conformance.js.map +1 -0
- package/dist/contract/contract-git.d.ts +24 -0
- package/dist/contract/contract-git.d.ts.map +1 -0
- package/dist/contract/contract-git.js +144 -0
- package/dist/contract/contract-git.js.map +1 -0
- package/dist/contract/contract-hash.d.ts +7 -0
- package/dist/contract/contract-hash.d.ts.map +1 -0
- package/dist/contract/contract-hash.js +38 -0
- package/dist/contract/contract-hash.js.map +1 -0
- package/dist/contract/contract-report-file.d.ts +4 -0
- package/dist/contract/contract-report-file.d.ts.map +1 -0
- package/dist/contract/contract-report-file.js +60 -0
- package/dist/contract/contract-report-file.js.map +1 -0
- package/dist/contract/contract-schema.d.ts +92 -0
- package/dist/contract/contract-schema.d.ts.map +1 -0
- package/dist/contract/contract-schema.js +124 -0
- package/dist/contract/contract-schema.js.map +1 -0
- package/dist/contract/contract-units.d.ts +12 -0
- package/dist/contract/contract-units.d.ts.map +1 -0
- package/dist/contract/contract-units.js +96 -0
- package/dist/contract/contract-units.js.map +1 -0
- package/dist/contract/contracts-lock-file.d.ts +24 -0
- package/dist/contract/contracts-lock-file.d.ts.map +1 -0
- package/dist/contract/contracts-lock-file.js +141 -0
- package/dist/contract/contracts-lock-file.js.map +1 -0
- package/dist/contract/data-normalize.d.ts +10 -0
- package/dist/contract/data-normalize.d.ts.map +1 -0
- package/dist/contract/data-normalize.js +110 -0
- package/dist/contract/data-normalize.js.map +1 -0
- package/dist/contract/derive-free-feature.d.ts +49 -0
- package/dist/contract/derive-free-feature.d.ts.map +1 -0
- package/dist/contract/derive-free-feature.js +183 -0
- package/dist/contract/derive-free-feature.js.map +1 -0
- package/dist/contract/field-semantic-redaction.d.ts +21 -0
- package/dist/contract/field-semantic-redaction.d.ts.map +1 -0
- package/dist/contract/field-semantic-redaction.js +118 -0
- package/dist/contract/field-semantic-redaction.js.map +1 -0
- package/dist/contract/free-feature-blocks.d.ts +10 -0
- package/dist/contract/free-feature-blocks.d.ts.map +1 -0
- package/dist/contract/free-feature-blocks.js +94 -0
- package/dist/contract/free-feature-blocks.js.map +1 -0
- package/dist/contract/lock-secret-guard.d.ts +22 -0
- package/dist/contract/lock-secret-guard.d.ts.map +1 -0
- package/dist/contract/lock-secret-guard.js +134 -0
- package/dist/contract/lock-secret-guard.js.map +1 -0
- package/dist/contract/lock-vs-git-head.d.ts +26 -0
- package/dist/contract/lock-vs-git-head.d.ts.map +1 -0
- package/dist/contract/lock-vs-git-head.js +28 -0
- package/dist/contract/lock-vs-git-head.js.map +1 -0
- package/dist/contract/normalize-contract-step.d.ts +21 -0
- package/dist/contract/normalize-contract-step.d.ts.map +1 -0
- package/dist/contract/normalize-contract-step.js +70 -0
- package/dist/contract/normalize-contract-step.js.map +1 -0
- package/dist/contract/normalize-feature-steps.d.ts +7 -0
- package/dist/contract/normalize-feature-steps.d.ts.map +1 -0
- package/dist/contract/normalize-feature-steps.js +127 -0
- package/dist/contract/normalize-feature-steps.js.map +1 -0
- package/dist/contract/oracle-strength.d.ts +12 -0
- package/dist/contract/oracle-strength.d.ts.map +1 -0
- package/dist/contract/oracle-strength.js +88 -0
- package/dist/contract/oracle-strength.js.map +1 -0
- package/dist/contract/project-contract-blockers.d.ts +10 -0
- package/dist/contract/project-contract-blockers.d.ts.map +1 -0
- package/dist/contract/project-contract-blockers.js +75 -0
- package/dist/contract/project-contract-blockers.js.map +1 -0
- package/dist/contract/record-contract-approval.d.ts +36 -0
- package/dist/contract/record-contract-approval.d.ts.map +1 -0
- package/dist/contract/record-contract-approval.js +126 -0
- package/dist/contract/record-contract-approval.js.map +1 -0
- package/dist/contract/render-free-gherkin.d.ts +42 -0
- package/dist/contract/render-free-gherkin.d.ts.map +1 -0
- package/dist/contract/render-free-gherkin.js +147 -0
- package/dist/contract/render-free-gherkin.js.map +1 -0
- package/dist/contract/render-trace-sentence.d.ts +20 -0
- package/dist/contract/render-trace-sentence.d.ts.map +1 -0
- package/dist/contract/render-trace-sentence.js +109 -0
- package/dist/contract/render-trace-sentence.js.map +1 -0
- package/dist/contract/render-trace-target.d.ts +14 -0
- package/dist/contract/render-trace-target.d.ts.map +1 -0
- package/dist/contract/render-trace-target.js +70 -0
- package/dist/contract/render-trace-target.js.map +1 -0
- package/dist/contract/resolve-unit-trace-identities.d.ts +20 -0
- package/dist/contract/resolve-unit-trace-identities.d.ts.map +1 -0
- package/dist/contract/resolve-unit-trace-identities.js +62 -0
- package/dist/contract/resolve-unit-trace-identities.js.map +1 -0
- package/dist/contract/trace/collect-trace-evidence.d.ts +44 -0
- package/dist/contract/trace/collect-trace-evidence.d.ts.map +1 -0
- package/dist/contract/trace/collect-trace-evidence.js +219 -0
- package/dist/contract/trace/collect-trace-evidence.js.map +1 -0
- package/dist/contract/trace/identity-cache.d.ts +19 -0
- package/dist/contract/trace/identity-cache.d.ts.map +1 -0
- package/dist/contract/trace/identity-cache.js +104 -0
- package/dist/contract/trace/identity-cache.js.map +1 -0
- package/dist/contract/trace/read-file-once.d.ts +20 -0
- package/dist/contract/trace/read-file-once.d.ts.map +1 -0
- package/dist/contract/trace/read-file-once.js +135 -0
- package/dist/contract/trace/read-file-once.js.map +1 -0
- package/dist/contract/trace/scenario-id-claims.d.ts +11 -0
- package/dist/contract/trace/scenario-id-claims.d.ts.map +1 -0
- package/dist/contract/trace/scenario-id-claims.js +89 -0
- package/dist/contract/trace/scenario-id-claims.js.map +1 -0
- package/dist/contract/trace/select-final-attempt.d.ts +29 -0
- package/dist/contract/trace/select-final-attempt.d.ts.map +1 -0
- package/dist/contract/trace/select-final-attempt.js +119 -0
- package/dist/contract/trace/select-final-attempt.js.map +1 -0
- package/dist/contract/trace/snapshot-html-renderer.d.ts +31 -0
- package/dist/contract/trace/snapshot-html-renderer.d.ts.map +1 -0
- package/dist/contract/trace/snapshot-html-renderer.js +172 -0
- package/dist/contract/trace/snapshot-html-renderer.js.map +1 -0
- package/dist/contract/trace/snapshot-resolver.d.ts +21 -0
- package/dist/contract/trace/snapshot-resolver.d.ts.map +1 -0
- package/dist/contract/trace/snapshot-resolver.js +65 -0
- package/dist/contract/trace/snapshot-resolver.js.map +1 -0
- package/dist/contract/trace/snapshot-store.d.ts +66 -0
- package/dist/contract/trace/snapshot-store.d.ts.map +1 -0
- package/dist/contract/trace/snapshot-store.js +173 -0
- package/dist/contract/trace/snapshot-store.js.map +1 -0
- package/dist/contract/trace/target-identity.d.ts +74 -0
- package/dist/contract/trace/target-identity.d.ts.map +1 -0
- package/dist/contract/trace/target-identity.js +68 -0
- package/dist/contract/trace/target-identity.js.map +1 -0
- package/dist/contract/trace/test-step-join.d.ts +65 -0
- package/dist/contract/trace/test-step-join.d.ts.map +1 -0
- package/dist/contract/trace/test-step-join.js +80 -0
- package/dist/contract/trace/test-step-join.js.map +1 -0
- package/dist/contract/trace/trace-binding.d.ts +22 -0
- package/dist/contract/trace/trace-binding.d.ts.map +1 -0
- package/dist/contract/trace/trace-binding.js +91 -0
- package/dist/contract/trace/trace-binding.js.map +1 -0
- package/dist/contract/trace/trace-file-reader.d.ts +47 -0
- package/dist/contract/trace/trace-file-reader.d.ts.map +1 -0
- package/dist/contract/trace/trace-file-reader.js +137 -0
- package/dist/contract/trace/trace-file-reader.js.map +1 -0
- package/dist/contract/trace/trace-fingerprint.d.ts +22 -0
- package/dist/contract/trace/trace-fingerprint.d.ts.map +1 -0
- package/dist/contract/trace/trace-fingerprint.js +96 -0
- package/dist/contract/trace/trace-fingerprint.js.map +1 -0
- package/dist/contract/trace/trace-freshness.d.ts +27 -0
- package/dist/contract/trace/trace-freshness.d.ts.map +1 -0
- package/dist/contract/trace/trace-freshness.js +146 -0
- package/dist/contract/trace/trace-freshness.js.map +1 -0
- package/dist/contract/trace/trace-identity-resolution.d.ts +28 -0
- package/dist/contract/trace/trace-identity-resolution.d.ts.map +1 -0
- package/dist/contract/trace/trace-identity-resolution.js +87 -0
- package/dist/contract/trace/trace-identity-resolution.js.map +1 -0
- package/dist/contract/trace/trace-run-record.d.ts +32 -0
- package/dist/contract/trace/trace-run-record.d.ts.map +1 -0
- package/dist/contract/trace/trace-run-record.js +135 -0
- package/dist/contract/trace/trace-run-record.js.map +1 -0
- package/dist/contract/trace/trace-steps.d.ts +19 -0
- package/dist/contract/trace/trace-steps.d.ts.map +1 -0
- package/dist/contract/trace/trace-steps.js +79 -0
- package/dist/contract/trace/trace-steps.js.map +1 -0
- package/dist/contract/trace/trace-zip-limits.d.ts +45 -0
- package/dist/contract/trace/trace-zip-limits.d.ts.map +1 -0
- package/dist/contract/trace/trace-zip-limits.js +52 -0
- package/dist/contract/trace/trace-zip-limits.js.map +1 -0
- package/dist/contract/trace/trace-zip-reader.d.ts +10 -0
- package/dist/contract/trace/trace-zip-reader.d.ts.map +1 -0
- package/dist/contract/trace/trace-zip-reader.js +186 -0
- package/dist/contract/trace/trace-zip-reader.js.map +1 -0
- package/dist/contract/verify-approval-decisions.d.ts +5 -0
- package/dist/contract/verify-approval-decisions.d.ts.map +1 -0
- package/dist/contract/verify-approval-decisions.js +109 -0
- package/dist/contract/verify-approval-decisions.js.map +1 -0
- package/dist/decisions/append-decision.d.ts +12 -0
- package/dist/decisions/append-decision.d.ts.map +1 -0
- package/dist/decisions/append-decision.js +73 -0
- package/dist/decisions/append-decision.js.map +1 -0
- package/dist/decisions/confirmed-decision-commands.d.ts +41 -0
- package/dist/decisions/confirmed-decision-commands.d.ts.map +1 -0
- package/dist/decisions/confirmed-decision-commands.js +190 -0
- package/dist/decisions/confirmed-decision-commands.js.map +1 -0
- package/dist/decisions/decision-file.d.ts +31 -0
- package/dist/decisions/decision-file.d.ts.map +1 -0
- package/dist/decisions/decision-file.js +139 -0
- package/dist/decisions/decision-file.js.map +1 -0
- package/dist/decisions/decision-identity.d.ts +12 -0
- package/dist/decisions/decision-identity.d.ts.map +1 -0
- package/dist/decisions/decision-identity.js +78 -0
- package/dist/decisions/decision-identity.js.map +1 -0
- package/dist/decisions/decision-integrity-check.d.ts +12 -0
- package/dist/decisions/decision-integrity-check.d.ts.map +1 -0
- package/dist/decisions/decision-integrity-check.js +102 -0
- package/dist/decisions/decision-integrity-check.js.map +1 -0
- package/dist/decisions/decision-schema.d.ts +43 -0
- package/dist/decisions/decision-schema.d.ts.map +1 -0
- package/dist/decisions/decision-schema.js +76 -0
- package/dist/decisions/decision-schema.js.map +1 -0
- package/dist/decisions/decision-source.d.ts +2 -0
- package/dist/decisions/decision-source.d.ts.map +1 -0
- package/dist/decisions/decision-source.js +18 -0
- package/dist/decisions/decision-source.js.map +1 -0
- package/dist/decisions/exclusive-file-create.d.ts +3 -0
- package/dist/decisions/exclusive-file-create.d.ts.map +1 -0
- package/dist/decisions/exclusive-file-create.js +84 -0
- package/dist/decisions/exclusive-file-create.js.map +1 -0
- package/dist/decisions/git-head-trust.d.ts +19 -0
- package/dist/decisions/git-head-trust.d.ts.map +1 -0
- package/dist/decisions/git-head-trust.js +128 -0
- package/dist/decisions/git-head-trust.js.map +1 -0
- package/dist/decisions/human-confirmation.d.ts +35 -0
- package/dist/decisions/human-confirmation.d.ts.map +1 -0
- package/dist/decisions/human-confirmation.js +161 -0
- package/dist/decisions/human-confirmation.js.map +1 -0
- package/dist/decisions/legacy-waiver-migration.d.ts +38 -0
- package/dist/decisions/legacy-waiver-migration.d.ts.map +1 -0
- package/dist/decisions/legacy-waiver-migration.js +179 -0
- package/dist/decisions/legacy-waiver-migration.js.map +1 -0
- package/dist/decisions/read-decisions.d.ts +19 -0
- package/dist/decisions/read-decisions.d.ts.map +1 -0
- package/dist/decisions/read-decisions.js +90 -0
- package/dist/decisions/read-decisions.js.map +1 -0
- package/dist/decisions/viewpoint-baseline-adapter.d.ts +7 -0
- package/dist/decisions/viewpoint-baseline-adapter.d.ts.map +1 -0
- package/dist/decisions/viewpoint-baseline-adapter.js +109 -0
- package/dist/decisions/viewpoint-baseline-adapter.js.map +1 -0
- package/dist/decisions/waiver-decisions.d.ts +24 -0
- package/dist/decisions/waiver-decisions.d.ts.map +1 -0
- package/dist/decisions/waiver-decisions.js +46 -0
- package/dist/decisions/waiver-decisions.js.map +1 -0
- package/dist/exporters/csv-exporter.d.ts +11 -2
- package/dist/exporters/csv-exporter.d.ts.map +1 -1
- package/dist/exporters/csv-exporter.js +8 -4
- package/dist/exporters/csv-exporter.js.map +1 -1
- package/dist/exporters/matrix/build.d.ts +3 -0
- package/dist/exporters/matrix/build.d.ts.map +1 -1
- package/dist/exporters/matrix/build.js +10 -9
- package/dist/exporters/matrix/build.js.map +1 -1
- package/dist/exporters/matrix/export.d.ts +9 -1
- package/dist/exporters/matrix/export.d.ts.map +1 -1
- package/dist/exporters/matrix/export.js +13 -4
- package/dist/exporters/matrix/export.js.map +1 -1
- package/dist/exporters/matrix/gates.d.ts +6 -1
- package/dist/exporters/matrix/gates.d.ts.map +1 -1
- package/dist/exporters/matrix/gates.js +57 -128
- package/dist/exporters/matrix/gates.js.map +1 -1
- package/dist/exporters/matrix/render-csv.d.ts.map +1 -1
- package/dist/exporters/matrix/render-csv.js +3 -0
- package/dist/exporters/matrix/render-csv.js.map +1 -1
- package/dist/exporters/matrix/render-gate-overrides-sheet.d.ts +13 -0
- package/dist/exporters/matrix/render-gate-overrides-sheet.d.ts.map +1 -0
- package/dist/exporters/matrix/render-gate-overrides-sheet.js +44 -0
- package/dist/exporters/matrix/render-gate-overrides-sheet.js.map +1 -0
- package/dist/exporters/matrix/render-xlsx.d.ts +1 -0
- package/dist/exporters/matrix/render-xlsx.d.ts.map +1 -1
- package/dist/exporters/matrix/render-xlsx.js +3 -0
- package/dist/exporters/matrix/render-xlsx.js.map +1 -1
- package/dist/exporters/matrix/types.d.ts +4 -1
- package/dist/exporters/matrix/types.d.ts.map +1 -1
- package/dist/exporters/matrix/types.js.map +1 -1
- package/dist/exporters/matrix/xlsx-cells.js +1 -1
- package/dist/exporters/matrix/xlsx-cells.js.map +1 -1
- package/dist/exporters/test-data-resolver.d.ts.map +1 -1
- package/dist/exporters/test-data-resolver.js +2 -12
- package/dist/exporters/test-data-resolver.js.map +1 -1
- package/dist/exporters/xlsx-exporter.d.ts +3 -2
- package/dist/exporters/xlsx-exporter.d.ts.map +1 -1
- package/dist/exporters/xlsx-exporter.js +4 -4
- package/dist/exporters/xlsx-exporter.js.map +1 -1
- package/dist/generators/gherkin-parser/index.d.ts.map +1 -1
- package/dist/generators/gherkin-parser/index.js +0 -1
- package/dist/generators/gherkin-parser/index.js.map +1 -1
- package/dist/generators/test-generator/adapters/appium/templates/steps/assertions/contain-text-assertion.hbs +1 -2
- package/dist/generators/test-generator/adapters/playwright/templates/steps/actions/fill-action.hbs +1 -13
- package/dist/generators/test-generator/adapters/playwright/templates/steps/assertions/contain-text-assertion.hbs +1 -9
- package/dist/generators/test-generator/code-generator.d.ts.map +1 -1
- package/dist/generators/test-generator/code-generator.js +18 -4
- package/dist/generators/test-generator/code-generator.js.map +1 -1
- package/dist/generators/test-generator/diagnostics.d.ts +3 -9
- package/dist/generators/test-generator/diagnostics.d.ts.map +1 -1
- package/dist/generators/test-generator/diagnostics.js +3 -9
- package/dist/generators/test-generator/diagnostics.js.map +1 -1
- package/dist/generators/test-generator/patterns/types.d.ts +1 -1
- package/dist/generators/test-generator/patterns/types.d.ts.map +1 -1
- package/dist/generators/test-generator/step-mapper.d.ts +0 -8
- package/dist/generators/test-generator/step-mapper.d.ts.map +1 -1
- package/dist/generators/test-generator/step-mapper.js +8 -52
- package/dist/generators/test-generator/step-mapper.js.map +1 -1
- package/dist/generators/test-generator/utils/data-resolver.d.ts +0 -16
- package/dist/generators/test-generator/utils/data-resolver.d.ts.map +1 -1
- package/dist/generators/test-generator/utils/data-resolver.js +0 -59
- package/dist/generators/test-generator/utils/data-resolver.js.map +1 -1
- package/dist/generators/test-generator/utils/selector-resolver.d.ts.map +1 -1
- package/dist/generators/test-generator/utils/selector-resolver.js +2 -10
- package/dist/generators/test-generator/utils/selector-resolver.js.map +1 -1
- package/dist/graph/board-human-actions.d.ts +5 -0
- package/dist/graph/board-human-actions.d.ts.map +1 -0
- package/dist/graph/board-human-actions.js +83 -0
- package/dist/graph/board-human-actions.js.map +1 -0
- package/dist/graph/evaluate-graph.d.ts +41 -0
- package/dist/graph/evaluate-graph.d.ts.map +1 -0
- package/dist/graph/evaluate-graph.js +77 -0
- package/dist/graph/evaluate-graph.js.map +1 -0
- package/dist/graph/human-action.d.ts +21 -0
- package/dist/graph/human-action.d.ts.map +1 -0
- package/dist/graph/human-action.js +3 -0
- package/dist/graph/human-action.js.map +1 -0
- package/dist/graph/locate-finding.d.ts +20 -0
- package/dist/graph/locate-finding.d.ts.map +1 -0
- package/dist/graph/locate-finding.js +137 -0
- package/dist/graph/locate-finding.js.map +1 -0
- package/dist/graph/manual-triage-decisions.d.ts +19 -0
- package/dist/graph/manual-triage-decisions.d.ts.map +1 -0
- package/dist/graph/manual-triage-decisions.js +53 -0
- package/dist/graph/manual-triage-decisions.js.map +1 -0
- package/dist/graph/node-decl.d.ts +75 -0
- package/dist/graph/node-decl.d.ts.map +1 -0
- package/dist/graph/node-decl.js +11 -0
- package/dist/graph/node-decl.js.map +1 -0
- package/dist/graph/nodes/core-node-catalog.d.ts +7 -0
- package/dist/graph/nodes/core-node-catalog.d.ts.map +1 -0
- package/dist/graph/nodes/core-node-catalog.js +13 -0
- package/dist/graph/nodes/core-node-catalog.js.map +1 -0
- package/dist/graph/nodes/test-case-contract-nodes.d.ts +21 -0
- package/dist/graph/nodes/test-case-contract-nodes.d.ts.map +1 -0
- package/dist/graph/nodes/test-case-contract-nodes.js +72 -0
- package/dist/graph/nodes/test-case-contract-nodes.js.map +1 -0
- package/dist/graph/nodes/test-case-nodes.d.ts +29 -0
- package/dist/graph/nodes/test-case-nodes.d.ts.map +1 -0
- package/dist/graph/nodes/test-case-nodes.js +95 -0
- package/dist/graph/nodes/test-case-nodes.js.map +1 -0
- package/dist/graph/nodes/unit-audit-nodes.d.ts +18 -0
- package/dist/graph/nodes/unit-audit-nodes.d.ts.map +1 -0
- package/dist/graph/nodes/unit-audit-nodes.js +96 -0
- package/dist/graph/nodes/unit-audit-nodes.js.map +1 -0
- package/dist/graph/nodes/unit-contract-node.d.ts +21 -0
- package/dist/graph/nodes/unit-contract-node.d.ts.map +1 -0
- package/dist/graph/nodes/unit-contract-node.js +96 -0
- package/dist/graph/nodes/unit-contract-node.js.map +1 -0
- package/dist/graph/nodes/unit-design-nodes.d.ts +14 -0
- package/dist/graph/nodes/unit-design-nodes.d.ts.map +1 -0
- package/dist/graph/nodes/unit-design-nodes.js +91 -0
- package/dist/graph/nodes/unit-design-nodes.js.map +1 -0
- package/dist/graph/nodes/unit-execution-nodes.d.ts +16 -0
- package/dist/graph/nodes/unit-execution-nodes.d.ts.map +1 -0
- package/dist/graph/nodes/unit-execution-nodes.js +79 -0
- package/dist/graph/nodes/unit-execution-nodes.js.map +1 -0
- package/dist/graph/test-case-identity.d.ts +13 -0
- package/dist/graph/test-case-identity.d.ts.map +1 -0
- package/dist/graph/test-case-identity.js +106 -0
- package/dist/graph/test-case-identity.js.map +1 -0
- package/dist/graph/test-case-results.d.ts +12 -0
- package/dist/graph/test-case-results.d.ts.map +1 -0
- package/dist/graph/test-case-results.js +161 -0
- package/dist/graph/test-case-results.js.map +1 -0
- package/dist/graph/test-case-satisfied.d.ts +26 -0
- package/dist/graph/test-case-satisfied.d.ts.map +1 -0
- package/dist/graph/test-case-satisfied.js +55 -0
- package/dist/graph/test-case-satisfied.js.map +1 -0
- package/dist/graph/test-case-state-shape.d.ts +73 -0
- package/dist/graph/test-case-state-shape.d.ts.map +1 -0
- package/dist/graph/test-case-state-shape.js +3 -0
- package/dist/graph/test-case-state-shape.js.map +1 -0
- package/dist/graph/test-case-states.d.ts +9 -0
- package/dist/graph/test-case-states.d.ts.map +1 -0
- package/dist/graph/test-case-states.js +116 -0
- package/dist/graph/test-case-states.js.map +1 -0
- package/dist/graph/unit-artifact-probes.d.ts +19 -0
- package/dist/graph/unit-artifact-probes.d.ts.map +1 -0
- package/dist/graph/unit-artifact-probes.js +135 -0
- package/dist/graph/unit-artifact-probes.js.map +1 -0
- package/dist/graph/unit-results-freshness.d.ts +29 -0
- package/dist/graph/unit-results-freshness.d.ts.map +1 -0
- package/dist/graph/unit-results-freshness.js +213 -0
- package/dist/graph/unit-results-freshness.js.map +1 -0
- package/dist/graph/unit-state-facts.d.ts +43 -0
- package/dist/graph/unit-state-facts.d.ts.map +1 -0
- package/dist/graph/unit-state-facts.js +87 -0
- package/dist/graph/unit-state-facts.js.map +1 -0
- package/dist/harness/audit.d.ts +14 -2
- package/dist/harness/audit.d.ts.map +1 -1
- package/dist/harness/audit.js +20 -21
- package/dist/harness/audit.js.map +1 -1
- package/dist/harness/catalog/gate-rules.yaml +647 -0
- package/dist/harness/defect-draft.d.ts.map +1 -1
- package/dist/harness/defect-draft.js +12 -2
- package/dist/harness/defect-draft.js.map +1 -1
- package/dist/harness/defects.d.ts +14 -0
- package/dist/harness/defects.d.ts.map +1 -1
- package/dist/harness/defects.js +14 -10
- package/dist/harness/defects.js.map +1 -1
- package/dist/harness/flow-check.d.ts +9 -0
- package/dist/harness/flow-check.d.ts.map +1 -1
- package/dist/harness/flow-check.js +11 -0
- package/dist/harness/flow-check.js.map +1 -1
- package/dist/harness/gates/audit-gate-terms.d.ts +41 -0
- package/dist/harness/gates/audit-gate-terms.d.ts.map +1 -0
- package/dist/harness/gates/audit-gate-terms.js +72 -0
- package/dist/harness/gates/audit-gate-terms.js.map +1 -0
- package/dist/harness/gates/audit-gate-verdict.d.ts +31 -0
- package/dist/harness/gates/audit-gate-verdict.d.ts.map +1 -0
- package/dist/harness/gates/audit-gate-verdict.js +102 -0
- package/dist/harness/gates/audit-gate-verdict.js.map +1 -0
- package/dist/harness/gates/audit-input-hash.d.ts +6 -0
- package/dist/harness/gates/audit-input-hash.d.ts.map +1 -0
- package/dist/harness/gates/audit-input-hash.js +124 -0
- package/dist/harness/gates/audit-input-hash.js.map +1 -0
- package/dist/harness/gates/audit-report-verification.d.ts +43 -0
- package/dist/harness/gates/audit-report-verification.d.ts.map +1 -0
- package/dist/harness/gates/audit-report-verification.js +241 -0
- package/dist/harness/gates/audit-report-verification.js.map +1 -0
- package/dist/harness/gates/decisions-hash.d.ts +13 -0
- package/dist/harness/gates/decisions-hash.d.ts.map +1 -0
- package/dist/harness/gates/decisions-hash.js +72 -0
- package/dist/harness/gates/decisions-hash.js.map +1 -0
- package/dist/harness/gates/delivery-checklist.d.ts +24 -0
- package/dist/harness/gates/delivery-checklist.d.ts.map +1 -0
- package/dist/harness/gates/delivery-checklist.js +161 -0
- package/dist/harness/gates/delivery-checklist.js.map +1 -0
- package/dist/harness/gates/delivery-decision-gate.d.ts +33 -0
- package/dist/harness/gates/delivery-decision-gate.d.ts.map +1 -0
- package/dist/harness/gates/delivery-decision-gate.js +116 -0
- package/dist/harness/gates/delivery-decision-gate.js.map +1 -0
- package/dist/harness/gates/delivery-decision-step.d.ts +19 -0
- package/dist/harness/gates/delivery-decision-step.d.ts.map +1 -0
- package/dist/harness/gates/delivery-decision-step.js +86 -0
- package/dist/harness/gates/delivery-decision-step.js.map +1 -0
- package/dist/harness/gates/gate-override-report.d.ts +59 -0
- package/dist/harness/gates/gate-override-report.d.ts.map +1 -0
- package/dist/harness/gates/gate-override-report.js +86 -0
- package/dist/harness/gates/gate-override-report.js.map +1 -0
- package/dist/harness/gates/gate-override-scope.d.ts +18 -0
- package/dist/harness/gates/gate-override-scope.d.ts.map +1 -0
- package/dist/harness/gates/gate-override-scope.js +98 -0
- package/dist/harness/gates/gate-override-scope.js.map +1 -0
- package/dist/harness/gates/gate-override-validation.d.ts +25 -0
- package/dist/harness/gates/gate-override-validation.d.ts.map +1 -0
- package/dist/harness/gates/gate-override-validation.js +81 -0
- package/dist/harness/gates/gate-override-validation.js.map +1 -0
- package/dist/harness/gates/gate-rule-catalog.d.ts +24 -0
- package/dist/harness/gates/gate-rule-catalog.d.ts.map +1 -0
- package/dist/harness/gates/gate-rule-catalog.js +112 -0
- package/dist/harness/gates/gate-rule-catalog.js.map +1 -0
- package/dist/harness/gates/gate-terms.d.ts +33 -0
- package/dist/harness/gates/gate-terms.d.ts.map +1 -0
- package/dist/harness/gates/gate-terms.js +41 -0
- package/dist/harness/gates/gate-terms.js.map +1 -0
- package/dist/harness/gates/in-run-decision-check.d.ts +21 -0
- package/dist/harness/gates/in-run-decision-check.d.ts.map +1 -0
- package/dist/harness/gates/in-run-decision-check.js +166 -0
- package/dist/harness/gates/in-run-decision-check.js.map +1 -0
- package/dist/harness/gates/journey-gate-inputs.d.ts +37 -0
- package/dist/harness/gates/journey-gate-inputs.d.ts.map +1 -0
- package/dist/harness/gates/journey-gate-inputs.js +81 -0
- package/dist/harness/gates/journey-gate-inputs.js.map +1 -0
- package/dist/harness/gates/migrated-waiver-import-bounds.d.ts +13 -0
- package/dist/harness/gates/migrated-waiver-import-bounds.d.ts.map +1 -0
- package/dist/harness/gates/migrated-waiver-import-bounds.js +141 -0
- package/dist/harness/gates/migrated-waiver-import-bounds.js.map +1 -0
- package/dist/harness/gates/migrated-waiver-review.d.ts +16 -0
- package/dist/harness/gates/migrated-waiver-review.d.ts.map +1 -0
- package/dist/harness/gates/migrated-waiver-review.js +114 -0
- package/dist/harness/gates/migrated-waiver-review.js.map +1 -0
- package/dist/harness/gates/resolve-rule.d.ts +59 -0
- package/dist/harness/gates/resolve-rule.d.ts.map +1 -0
- package/dist/harness/gates/resolve-rule.js +41 -0
- package/dist/harness/gates/resolve-rule.js.map +1 -0
- package/dist/harness/gates/unit-gate-overrides.d.ts +25 -0
- package/dist/harness/gates/unit-gate-overrides.d.ts.map +1 -0
- package/dist/harness/gates/unit-gate-overrides.js +122 -0
- package/dist/harness/gates/unit-gate-overrides.js.map +1 -0
- package/dist/harness/journey.d.ts +64 -1
- package/dist/harness/journey.d.ts.map +1 -1
- package/dist/harness/journey.js +258 -34
- package/dist/harness/journey.js.map +1 -1
- package/dist/harness/manifest.d.ts +12 -0
- package/dist/harness/manifest.d.ts.map +1 -1
- package/dist/harness/manifest.js +71 -43
- package/dist/harness/manifest.js.map +1 -1
- package/dist/harness/next-step.d.ts +58 -4
- package/dist/harness/next-step.d.ts.map +1 -1
- package/dist/harness/next-step.js +55 -204
- package/dist/harness/next-step.js.map +1 -1
- package/dist/harness/parse.d.ts +7 -0
- package/dist/harness/parse.d.ts.map +1 -1
- package/dist/harness/parse.js +18 -2
- package/dist/harness/parse.js.map +1 -1
- package/dist/harness/provenance.d.ts +29 -0
- package/dist/harness/provenance.d.ts.map +1 -1
- package/dist/harness/provenance.js +109 -9
- package/dist/harness/provenance.js.map +1 -1
- package/dist/harness/script-check.d.ts.map +1 -1
- package/dist/harness/script-check.js +22 -5
- package/dist/harness/script-check.js.map +1 -1
- package/dist/harness/sensors.d.ts +14 -3
- package/dist/harness/sensors.d.ts.map +1 -1
- package/dist/harness/sensors.js +41 -38
- package/dist/harness/sensors.js.map +1 -1
- package/dist/harness/triage-manual-classification.d.ts +32 -0
- package/dist/harness/triage-manual-classification.d.ts.map +1 -0
- package/dist/harness/triage-manual-classification.js +56 -0
- package/dist/harness/triage-manual-classification.js.map +1 -0
- package/dist/harness/unit-paths.d.ts +8 -0
- package/dist/harness/unit-paths.d.ts.map +1 -1
- package/dist/harness/unit-paths.js +23 -1
- package/dist/harness/unit-paths.js.map +1 -1
- package/dist/hooks/bash-read-only-segment.d.ts +9 -0
- package/dist/hooks/bash-read-only-segment.d.ts.map +1 -0
- package/dist/hooks/bash-read-only-segment.js +168 -0
- package/dist/hooks/bash-read-only-segment.js.map +1 -0
- package/dist/hooks/bash-write-targets.d.ts +25 -0
- package/dist/hooks/bash-write-targets.d.ts.map +1 -0
- package/dist/hooks/bash-write-targets.js +266 -0
- package/dist/hooks/bash-write-targets.js.map +1 -0
- package/dist/hooks/claude-hook-entry.d.ts +11 -0
- package/dist/hooks/claude-hook-entry.d.ts.map +1 -0
- package/dist/hooks/claude-hook-entry.js +137 -0
- package/dist/hooks/claude-hook-entry.js.map +1 -0
- package/dist/hooks/claude-transcript-turn.d.ts +11 -0
- package/dist/hooks/claude-transcript-turn.d.ts.map +1 -0
- package/dist/hooks/claude-transcript-turn.js +143 -0
- package/dist/hooks/claude-transcript-turn.js.map +1 -0
- package/dist/hooks/guard-delivery-contract-rule.d.ts +10 -0
- package/dist/hooks/guard-delivery-contract-rule.d.ts.map +1 -0
- package/dist/hooks/guard-delivery-contract-rule.js +152 -0
- package/dist/hooks/guard-delivery-contract-rule.js.map +1 -0
- package/dist/hooks/guard-protected-paths.d.ts +13 -0
- package/dist/hooks/guard-protected-paths.d.ts.map +1 -0
- package/dist/hooks/guard-protected-paths.js +116 -0
- package/dist/hooks/guard-protected-paths.js.map +1 -0
- package/dist/hooks/guard-rules.d.ts +20 -0
- package/dist/hooks/guard-rules.d.ts.map +1 -0
- package/dist/hooks/guard-rules.js +130 -0
- package/dist/hooks/guard-rules.js.map +1 -0
- package/dist/hooks/guard-tty-spoof-rule.d.ts +17 -0
- package/dist/hooks/guard-tty-spoof-rule.d.ts.map +1 -0
- package/dist/hooks/guard-tty-spoof-rule.js +22 -0
- package/dist/hooks/guard-tty-spoof-rule.js.map +1 -0
- package/dist/hooks/hand-back-core.d.ts +42 -0
- package/dist/hooks/hand-back-core.d.ts.map +1 -0
- package/dist/hooks/hand-back-core.js +128 -0
- package/dist/hooks/hand-back-core.js.map +1 -0
- package/dist/hooks/hook-cli-command.d.ts +9 -0
- package/dist/hooks/hook-cli-command.d.ts.map +1 -0
- package/dist/hooks/hook-cli-command.js +46 -0
- package/dist/hooks/hook-cli-command.js.map +1 -0
- package/dist/hooks/hook-registry.d.ts +10 -0
- package/dist/hooks/hook-registry.d.ts.map +1 -0
- package/dist/hooks/hook-registry.js +24 -0
- package/dist/hooks/hook-registry.js.map +1 -0
- package/dist/hooks/hook-result-contract.d.ts +50 -0
- package/dist/hooks/hook-result-contract.d.ts.map +1 -0
- package/dist/hooks/hook-result-contract.js +80 -0
- package/dist/hooks/hook-result-contract.js.map +1 -0
- package/dist/hooks/hook-toggles.d.ts +10 -0
- package/dist/hooks/hook-toggles.d.ts.map +1 -0
- package/dist/hooks/hook-toggles.js +107 -0
- package/dist/hooks/hook-toggles.js.map +1 -0
- package/dist/hooks/pre-tool-guard-hook.d.ts +10 -0
- package/dist/hooks/pre-tool-guard-hook.d.ts.map +1 -0
- package/dist/hooks/pre-tool-guard-hook.js +105 -0
- package/dist/hooks/pre-tool-guard-hook.js.map +1 -0
- package/dist/hooks/session-start-unit-state-hook.d.ts +14 -0
- package/dist/hooks/session-start-unit-state-hook.d.ts.map +1 -0
- package/dist/hooks/session-start-unit-state-hook.js +60 -0
- package/dist/hooks/session-start-unit-state-hook.js.map +1 -0
- package/dist/hooks/stop-next-step-hook.d.ts +16 -0
- package/dist/hooks/stop-next-step-hook.d.ts.map +1 -0
- package/dist/hooks/stop-next-step-hook.js +43 -0
- package/dist/hooks/stop-next-step-hook.js.map +1 -0
- package/dist/hooks/sungen-project-root.d.ts +19 -0
- package/dist/hooks/sungen-project-root.d.ts.map +1 -0
- package/dist/hooks/sungen-project-root.js +130 -0
- package/dist/hooks/sungen-project-root.js.map +1 -0
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +1 -2
- package/dist/index.js.map +1 -1
- package/dist/orchestrator/ai-skills/command-partial-includes.d.ts +15 -0
- package/dist/orchestrator/ai-skills/command-partial-includes.d.ts.map +1 -0
- package/dist/orchestrator/ai-skills/command-partial-includes.js +106 -0
- package/dist/orchestrator/ai-skills/command-partial-includes.js.map +1 -0
- package/dist/orchestrator/ai-skills/commands.d.ts +5 -1
- package/dist/orchestrator/ai-skills/commands.d.ts.map +1 -1
- package/dist/orchestrator/ai-skills/commands.js +11 -3
- package/dist/orchestrator/ai-skills/commands.js.map +1 -1
- package/dist/orchestrator/assets-drift.d.ts +5 -0
- package/dist/orchestrator/assets-drift.d.ts.map +1 -1
- package/dist/orchestrator/assets-drift.js +12 -3
- package/dist/orchestrator/assets-drift.js.map +1 -1
- package/dist/orchestrator/claude-hooks-install.d.ts +27 -0
- package/dist/orchestrator/claude-hooks-install.d.ts.map +1 -0
- package/dist/orchestrator/claude-hooks-install.js +203 -0
- package/dist/orchestrator/claude-hooks-install.js.map +1 -0
- package/dist/orchestrator/lockstep-version.d.ts +15 -5
- package/dist/orchestrator/lockstep-version.d.ts.map +1 -1
- package/dist/orchestrator/lockstep-version.js +30 -17
- package/dist/orchestrator/lockstep-version.js.map +1 -1
- package/dist/orchestrator/project-initializer.d.ts +6 -0
- package/dist/orchestrator/project-initializer.d.ts.map +1 -1
- package/dist/orchestrator/project-initializer.js +24 -0
- package/dist/orchestrator/project-initializer.js.map +1 -1
- package/dist/orchestrator/templates/ai-src/commands/add-api.md +2 -0
- package/dist/orchestrator/templates/ai-src/commands/add-flow.md +6 -26
- package/dist/orchestrator/templates/ai-src/commands/add-screen.md +2 -0
- package/dist/orchestrator/templates/ai-src/commands/create-data-test.md +1 -0
- package/dist/orchestrator/templates/ai-src/commands/create-test.md +17 -16
- package/dist/orchestrator/templates/ai-src/commands/dashboard.md +2 -0
- package/dist/orchestrator/templates/ai-src/commands/delivery.md +4 -0
- package/dist/orchestrator/templates/ai-src/commands/design.md +2 -0
- package/dist/orchestrator/templates/ai-src/commands/feedback.md +2 -0
- package/dist/orchestrator/templates/ai-src/commands/locale.md +2 -0
- package/dist/orchestrator/templates/ai-src/commands/review.md +4 -0
- package/dist/orchestrator/templates/ai-src/commands/run-test.md +6 -8
- package/dist/orchestrator/templates/ai-src/partials/hand-back-rule.claude.md +20 -0
- package/dist/orchestrator/templates/ai-src/partials/hand-back-rule.codex.md +20 -0
- package/dist/orchestrator/templates/ai-src/partials/hand-back-rule.copilot.md +20 -0
- package/dist/orchestrator/templates/ai-src/partials/waiting-for-you.claude.md +11 -0
- package/dist/orchestrator/templates/ai-src/partials/waiting-for-you.codex.md +11 -0
- package/dist/orchestrator/templates/ai-src/partials/waiting-for-you.copilot.md +11 -0
- package/dist/orchestrator/templates/ai-src/skills/sungen-delivery/SKILL.md +1 -2
- package/dist/orchestrator/templates/ai-src/skills/sungen-error-mapping/SKILL.md +4 -5
- package/dist/orchestrator/templates/ai-src/skills/sungen-gherkin-syntax/SKILL.md +8 -24
- package/dist/orchestrator/templates/ai-src/skills/sungen-tc-generation/SKILL.md +3 -4
- package/dist/orchestrator/templates/claude-hooks/sungen-hook.cjs +64 -0
- package/dist/orchestrator/templates/specs-test-data.ts +4 -22
- package/dist/utils/safe-yaml-parse.d.ts +8 -0
- package/dist/utils/safe-yaml-parse.d.ts.map +1 -0
- package/dist/utils/safe-yaml-parse.js +51 -0
- package/dist/utils/safe-yaml-parse.js.map +1 -0
- package/package.json +3 -3
- package/src/basis/anchor-grammar.ts +83 -0
- package/src/basis/anchor-hash.ts +27 -0
- package/src/basis/bm51-tables.ts +65 -0
- package/src/basis/extract-spec-anchors.ts +95 -0
- package/src/basis/migration-sentinel.ts +175 -0
- package/src/basis/spec-frontmatter.ts +68 -0
- package/src/basis/spec-sections.ts +35 -0
- package/src/basis/test-basis-schema.ts +81 -0
- package/src/capabilities/discover.ts +15 -2
- package/src/capabilities/registry.ts +16 -0
- package/src/cli/case-triage-hint.ts +31 -0
- package/src/cli/commands/audit.ts +1 -1
- package/src/cli/commands/capability.ts +1 -1
- package/src/cli/commands/contract-derive-command.ts +72 -0
- package/src/cli/commands/contract.ts +164 -0
- package/src/cli/commands/delivery.ts +106 -19
- package/src/cli/commands/gate.ts +2 -1
- package/src/cli/commands/generate.ts +16 -3
- package/src/cli/commands/journey.ts +45 -18
- package/src/cli/commands/next.ts +92 -11
- package/src/cli/commands/triage.ts +41 -3
- package/src/cli/commands/update.ts +41 -10
- package/src/cli/index.ts +24 -30
- package/src/cli/json-error-on-exit.ts +72 -0
- package/src/cli/project-sungen-invocation.ts +59 -0
- package/src/cli/run-command-hints.ts +47 -0
- package/src/cli/waiting-for-you-block.ts +27 -0
- package/src/contract/accept-opaque-steps.ts +114 -0
- package/src/contract/approve-contract.ts +101 -0
- package/src/contract/assertion-matcher-vocabulary.ts +93 -0
- package/src/contract/build-contract-verdict.ts +108 -0
- package/src/contract/build-trace-conformance.ts +103 -0
- package/src/contract/classify-contract-change.ts +92 -0
- package/src/contract/classify-contract-unit.ts +47 -0
- package/src/contract/classify-trace-conformance.ts +170 -0
- package/src/contract/contract-git.ts +106 -0
- package/src/contract/contract-hash.ts +34 -0
- package/src/contract/contract-report-file.ts +25 -0
- package/src/contract/contract-schema.ts +166 -0
- package/src/contract/contract-units.ts +52 -0
- package/src/contract/contracts-lock-file.ts +98 -0
- package/src/contract/data-normalize.ts +66 -0
- package/src/contract/derive-free-feature.ts +153 -0
- package/src/contract/field-semantic-redaction.ts +80 -0
- package/src/contract/free-feature-blocks.ts +56 -0
- package/src/contract/lock-secret-guard.ts +95 -0
- package/src/contract/lock-vs-git-head.ts +38 -0
- package/src/contract/normalize-contract-step.ts +74 -0
- package/src/contract/normalize-feature-steps.ts +93 -0
- package/src/contract/oracle-strength.ts +80 -0
- package/src/contract/project-contract-blockers.ts +38 -0
- package/src/contract/record-contract-approval.ts +110 -0
- package/src/contract/render-free-gherkin.ts +118 -0
- package/src/contract/render-trace-sentence.ts +98 -0
- package/src/contract/render-trace-target.ts +68 -0
- package/src/contract/resolve-unit-trace-identities.ts +58 -0
- package/src/contract/trace/collect-trace-evidence.ts +178 -0
- package/src/contract/trace/identity-cache.ts +61 -0
- package/src/contract/trace/read-file-once.ts +95 -0
- package/src/contract/trace/scenario-id-claims.ts +60 -0
- package/src/contract/trace/select-final-attempt.ts +77 -0
- package/src/contract/trace/snapshot-html-renderer.ts +136 -0
- package/src/contract/trace/snapshot-resolver.ts +66 -0
- package/src/contract/trace/snapshot-store.ts +151 -0
- package/src/contract/trace/target-identity.ts +104 -0
- package/src/contract/trace/test-step-join.ts +104 -0
- package/src/contract/trace/trace-binding.ts +59 -0
- package/src/contract/trace/trace-file-reader.ts +144 -0
- package/src/contract/trace/trace-fingerprint.ts +68 -0
- package/src/contract/trace/trace-freshness.ts +110 -0
- package/src/contract/trace/trace-identity-resolution.ts +77 -0
- package/src/contract/trace/trace-run-record.ts +96 -0
- package/src/contract/trace/trace-steps.ts +88 -0
- package/src/contract/trace/trace-zip-limits.ts +77 -0
- package/src/contract/trace/trace-zip-reader.ts +127 -0
- package/src/contract/verify-approval-decisions.ts +64 -0
- package/src/decisions/append-decision.ts +35 -0
- package/src/decisions/confirmed-decision-commands.ts +149 -0
- package/src/decisions/decision-file.ts +93 -0
- package/src/decisions/decision-identity.ts +73 -0
- package/src/decisions/decision-integrity-check.ts +61 -0
- package/src/decisions/decision-schema.ts +77 -0
- package/src/decisions/decision-source.ts +14 -0
- package/src/decisions/exclusive-file-create.ts +42 -0
- package/src/decisions/git-head-trust.ts +76 -0
- package/src/decisions/human-confirmation.ts +119 -0
- package/src/decisions/legacy-waiver-migration.ts +146 -0
- package/src/decisions/read-decisions.ts +61 -0
- package/src/decisions/viewpoint-baseline-adapter.ts +74 -0
- package/src/decisions/waiver-decisions.ts +56 -0
- package/src/exporters/csv-exporter.ts +12 -4
- package/src/exporters/matrix/build.ts +11 -9
- package/src/exporters/matrix/export.ts +16 -4
- package/src/exporters/matrix/gates.ts +62 -129
- package/src/exporters/matrix/render-csv.ts +3 -0
- package/src/exporters/matrix/render-gate-overrides-sheet.ts +50 -0
- package/src/exporters/matrix/render-xlsx.ts +3 -0
- package/src/exporters/matrix/types.ts +4 -1
- package/src/exporters/matrix/xlsx-cells.ts +1 -1
- package/src/exporters/test-data-resolver.ts +2 -10
- package/src/exporters/xlsx-exporter.ts +5 -4
- package/src/generators/gherkin-parser/index.ts +0 -1
- package/src/generators/test-generator/adapters/appium/templates/steps/assertions/contain-text-assertion.hbs +1 -2
- package/src/generators/test-generator/adapters/playwright/templates/steps/actions/fill-action.hbs +1 -13
- package/src/generators/test-generator/adapters/playwright/templates/steps/assertions/contain-text-assertion.hbs +1 -9
- package/src/generators/test-generator/code-generator.ts +17 -4
- package/src/generators/test-generator/diagnostics.ts +3 -9
- package/src/generators/test-generator/patterns/types.ts +0 -2
- package/src/generators/test-generator/step-mapper.ts +9 -56
- package/src/generators/test-generator/utils/data-resolver.ts +0 -53
- package/src/generators/test-generator/utils/selector-resolver.ts +2 -10
- package/src/graph/board-human-actions.ts +80 -0
- package/src/graph/evaluate-graph.ts +97 -0
- package/src/graph/human-action.ts +33 -0
- package/src/graph/locate-finding.ts +93 -0
- package/src/graph/manual-triage-decisions.ts +55 -0
- package/src/graph/node-decl.ts +83 -0
- package/src/graph/nodes/core-node-catalog.ts +15 -0
- package/src/graph/nodes/test-case-contract-nodes.ts +90 -0
- package/src/graph/nodes/test-case-nodes.ts +100 -0
- package/src/graph/nodes/unit-audit-nodes.ts +96 -0
- package/src/graph/nodes/unit-contract-node.ts +100 -0
- package/src/graph/nodes/unit-design-nodes.ts +89 -0
- package/src/graph/nodes/unit-execution-nodes.ts +77 -0
- package/src/graph/test-case-identity.ts +75 -0
- package/src/graph/test-case-results.ts +111 -0
- package/src/graph/test-case-satisfied.ts +58 -0
- package/src/graph/test-case-state-shape.ts +67 -0
- package/src/graph/test-case-states.ts +84 -0
- package/src/graph/unit-artifact-probes.ts +79 -0
- package/src/graph/unit-results-freshness.ts +165 -0
- package/src/graph/unit-state-facts.ts +112 -0
- package/src/harness/audit.ts +31 -18
- package/src/harness/catalog/gate-rules.yaml +647 -0
- package/src/harness/defect-draft.ts +13 -2
- package/src/harness/defects.ts +22 -9
- package/src/harness/flow-check.ts +11 -0
- package/src/harness/gates/audit-gate-terms.ts +80 -0
- package/src/harness/gates/audit-gate-verdict.ts +79 -0
- package/src/harness/gates/audit-input-hash.ts +79 -0
- package/src/harness/gates/audit-report-verification.ts +206 -0
- package/src/harness/gates/decisions-hash.ts +42 -0
- package/src/harness/gates/delivery-checklist.ts +152 -0
- package/src/harness/gates/delivery-decision-gate.ts +139 -0
- package/src/harness/gates/delivery-decision-step.ts +89 -0
- package/src/harness/gates/gate-override-report.ts +114 -0
- package/src/harness/gates/gate-override-scope.ts +66 -0
- package/src/harness/gates/gate-override-validation.ts +75 -0
- package/src/harness/gates/gate-rule-catalog.ts +82 -0
- package/src/harness/gates/gate-terms.ts +59 -0
- package/src/harness/gates/in-run-decision-check.ts +124 -0
- package/src/harness/gates/journey-gate-inputs.ts +89 -0
- package/src/harness/gates/migrated-waiver-import-bounds.ts +103 -0
- package/src/harness/gates/migrated-waiver-review.ts +121 -0
- package/src/harness/gates/resolve-rule.ts +94 -0
- package/src/harness/gates/unit-gate-overrides.ts +99 -0
- package/src/harness/journey.ts +287 -39
- package/src/harness/manifest.ts +67 -43
- package/src/harness/next-step.ts +91 -193
- package/src/harness/parse.ts +20 -2
- package/src/harness/provenance.ts +99 -11
- package/src/harness/script-check.ts +20 -5
- package/src/harness/sensors.ts +47 -40
- package/src/harness/triage-manual-classification.ts +76 -0
- package/src/harness/unit-paths.ts +19 -0
- package/src/hooks/bash-read-only-segment.ts +130 -0
- package/src/hooks/bash-write-targets.ts +195 -0
- package/src/hooks/claude-hook-entry.ts +85 -0
- package/src/hooks/claude-transcript-turn.ts +96 -0
- package/src/hooks/guard-delivery-contract-rule.ts +110 -0
- package/src/hooks/guard-protected-paths.ts +80 -0
- package/src/hooks/guard-rules.ts +117 -0
- package/src/hooks/guard-tty-spoof-rule.ts +34 -0
- package/src/hooks/hand-back-core.ts +118 -0
- package/src/hooks/hook-cli-command.ts +17 -0
- package/src/hooks/hook-registry.ts +24 -0
- package/src/hooks/hook-result-contract.ts +114 -0
- package/src/hooks/hook-toggles.ts +66 -0
- package/src/hooks/pre-tool-guard-hook.ts +67 -0
- package/src/hooks/session-start-unit-state-hook.ts +59 -0
- package/src/hooks/stop-next-step-hook.ts +38 -0
- package/src/hooks/sungen-project-root.ts +80 -0
- package/src/index.ts +1 -2
- package/src/orchestrator/ai-skills/command-partial-includes.ts +72 -0
- package/src/orchestrator/ai-skills/commands.ts +17 -5
- package/src/orchestrator/assets-drift.ts +12 -5
- package/src/orchestrator/claude-hooks-install.ts +168 -0
- package/src/orchestrator/lockstep-version.ts +28 -15
- package/src/orchestrator/project-initializer.ts +22 -0
- package/src/orchestrator/templates/ai-src/commands/add-api.md +2 -0
- package/src/orchestrator/templates/ai-src/commands/add-flow.md +6 -26
- package/src/orchestrator/templates/ai-src/commands/add-screen.md +2 -0
- package/src/orchestrator/templates/ai-src/commands/create-data-test.md +1 -0
- package/src/orchestrator/templates/ai-src/commands/create-test.md +17 -16
- package/src/orchestrator/templates/ai-src/commands/dashboard.md +2 -0
- package/src/orchestrator/templates/ai-src/commands/delivery.md +4 -0
- package/src/orchestrator/templates/ai-src/commands/design.md +2 -0
- package/src/orchestrator/templates/ai-src/commands/feedback.md +2 -0
- package/src/orchestrator/templates/ai-src/commands/locale.md +2 -0
- package/src/orchestrator/templates/ai-src/commands/review.md +4 -0
- package/src/orchestrator/templates/ai-src/commands/run-test.md +6 -8
- package/src/orchestrator/templates/ai-src/partials/hand-back-rule.claude.md +20 -0
- package/src/orchestrator/templates/ai-src/partials/hand-back-rule.codex.md +20 -0
- package/src/orchestrator/templates/ai-src/partials/hand-back-rule.copilot.md +20 -0
- package/src/orchestrator/templates/ai-src/partials/waiting-for-you.claude.md +11 -0
- package/src/orchestrator/templates/ai-src/partials/waiting-for-you.codex.md +11 -0
- package/src/orchestrator/templates/ai-src/partials/waiting-for-you.copilot.md +11 -0
- package/src/orchestrator/templates/ai-src/skills/sungen-delivery/SKILL.md +1 -2
- package/src/orchestrator/templates/ai-src/skills/sungen-error-mapping/SKILL.md +4 -5
- package/src/orchestrator/templates/ai-src/skills/sungen-gherkin-syntax/SKILL.md +8 -24
- package/src/orchestrator/templates/ai-src/skills/sungen-tc-generation/SKILL.md +3 -4
- package/src/orchestrator/templates/claude-hooks/sungen-hook.cjs +64 -0
- package/src/orchestrator/templates/specs-test-data.ts +4 -22
- package/src/utils/safe-yaml-parse.ts +44 -0
- package/dist/generators/test-generator/adapters/appium/templates/steps/assertions/expanded-assertion.hbs +0 -2
- package/dist/generators/test-generator/adapters/playwright/templates/steps/actions/date-picker-unsupported.hbs +0 -5
- package/dist/generators/test-generator/adapters/playwright/templates/steps/assertions/expanded-assertion.hbs +0 -12
- package/src/generators/test-generator/adapters/appium/templates/steps/assertions/expanded-assertion.hbs +0 -2
- package/src/generators/test-generator/adapters/playwright/templates/steps/actions/date-picker-unsupported.hbs +0 -5
- package/src/generators/test-generator/adapters/playwright/templates/steps/assertions/expanded-assertion.hbs +0 -12
|
@@ -0,0 +1,61 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Append-only integrity of the decision layer (DOS-13.3). Emits the catalog's hard rules — it does
|
|
3
|
+
* not define severity; phase 04's resolver decides what a finding blocks.
|
|
4
|
+
*
|
|
5
|
+
* decision.id-reuse stored id ≠ id of the content (edited text, renamed file, moved to another scope)
|
|
6
|
+
* decision.mutated entry_hash ≠ entry, an unreadable entry, or an entry in `git show HEAD:` that
|
|
7
|
+
* was edited or deleted in the working tree — the only check a careful forger
|
|
8
|
+
* who recomputes both hashes cannot pass
|
|
9
|
+
*
|
|
10
|
+
* Outside git (or with no commit yet) the HEAD comparison is skipped; it never throws.
|
|
11
|
+
*/
|
|
12
|
+
import * as fs from 'fs';
|
|
13
|
+
import * as path from 'path';
|
|
14
|
+
import { safeYamlParse } from '../utils/safe-yaml-parse';
|
|
15
|
+
import { canonicalJson } from './decision-identity';
|
|
16
|
+
import { DECISION_FILE_PATH, listDecisionDirs, verifyDecisionFile } from './decision-file';
|
|
17
|
+
import { gitOutput as git } from './git-head-trust';
|
|
18
|
+
|
|
19
|
+
export const DECISION_INTEGRITY_RULES = { idReuse: 'decision.id-reuse', mutated: 'decision.mutated' } as const;
|
|
20
|
+
export type DecisionIntegrityRule = typeof DECISION_INTEGRITY_RULES[keyof typeof DECISION_INTEGRITY_RULES];
|
|
21
|
+
export interface DecisionFinding { rule: DecisionIntegrityRule; path: string; message: string }
|
|
22
|
+
|
|
23
|
+
|
|
24
|
+
/** Same meaning, whatever the line endings or YAML layout: compare the parsed entries canonically. */
|
|
25
|
+
function sameEntry(a: string, b: string): boolean {
|
|
26
|
+
try {
|
|
27
|
+
return canonicalJson(safeYamlParse(a)) === canonicalJson(safeYamlParse(b));
|
|
28
|
+
} catch {
|
|
29
|
+
return a.replace(/\r\n?/g, '\n') === b.replace(/\r\n?/g, '\n');
|
|
30
|
+
}
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
export function checkDecisionIntegrity(projectRoot: string): DecisionFinding[] {
|
|
34
|
+
const out = new Map<string, DecisionFinding>();
|
|
35
|
+
const add = (rule: DecisionIntegrityRule, abs: string, message: string): void => {
|
|
36
|
+
const rel = path.relative(projectRoot, abs).split(path.sep).join('/');
|
|
37
|
+
if (!out.has(`${rule} ${rel}`)) out.set(`${rule} ${rel}`, { rule, path: rel, message });
|
|
38
|
+
};
|
|
39
|
+
|
|
40
|
+
for (const { scope, dir } of listDecisionDirs(projectRoot)) {
|
|
41
|
+
let names: string[] = [];
|
|
42
|
+
try { names = fs.readdirSync(dir).filter((f) => f.endsWith('.yaml')); } catch { continue; }
|
|
43
|
+
for (const name of names) {
|
|
44
|
+
const file = path.join(dir, name);
|
|
45
|
+
const v = verifyDecisionFile(file, scope);
|
|
46
|
+
if (!v.problem) continue;
|
|
47
|
+
add(v.problem.code === 'id-mismatch' ? DECISION_INTEGRITY_RULES.idReuse : DECISION_INTEGRITY_RULES.mutated, file, v.problem.message);
|
|
48
|
+
}
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
const listed = git(projectRoot, ['ls-tree', '-r', '-z', '--name-only', 'HEAD', '--', 'qa']);
|
|
52
|
+
for (const rel of (listed ?? '').split('\0').filter((p) => DECISION_FILE_PATH.test(p))) {
|
|
53
|
+
const abs = path.join(projectRoot, ...rel.split('/'));
|
|
54
|
+
if (!fs.existsSync(abs)) { add(DECISION_INTEGRITY_RULES.mutated, abs, 'committed entry was deleted (append-only: supersede it instead)'); continue; }
|
|
55
|
+
const head = git(projectRoot, ['show', `HEAD:./${rel}`]);
|
|
56
|
+
if (head !== null && !sameEntry(head, fs.readFileSync(abs, 'utf8'))) {
|
|
57
|
+
add(DECISION_INTEGRITY_RULES.mutated, abs, 'committed entry was edited (append-only: record a superseding entry instead)');
|
|
58
|
+
}
|
|
59
|
+
}
|
|
60
|
+
return [...out.values()];
|
|
61
|
+
}
|
|
@@ -0,0 +1,77 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Decision entry schema (DOS-13.2).
|
|
3
|
+
*
|
|
4
|
+
* `{id, kind, anchors[], text, reason, payload?, source:{by, where}, valid_from, supersedes?,
|
|
5
|
+
* visibility: internal|client, evidence_hash?, entry_hash}`
|
|
6
|
+
*
|
|
7
|
+
* Validation is strict both ways: a draft is checked before it is written, and a stored entry is
|
|
8
|
+
* checked when read — an unknown key (a stored `superseded_by`, say) makes the entry invalid, because
|
|
9
|
+
* a field outside the id's content could carry meaning the id does not vouch for.
|
|
10
|
+
*/
|
|
11
|
+
/** `triage-classification`: a red classified by hand, `sungen triage --case --as` (#928, graph/manual-triage-decisions.ts). */
|
|
12
|
+
export const DECISION_KINDS = ['waiver', 'gate-override', 'contract-approval', 'viewpoint-acceptance', 'clarification', 'triage-classification'] as const;
|
|
13
|
+
export type DecisionKind = typeof DECISION_KINDS[number];
|
|
14
|
+
export type DecisionVisibility = 'internal' | 'client';
|
|
15
|
+
export type JsonValue = string | number | boolean | null | JsonValue[] | { [key: string]: JsonValue };
|
|
16
|
+
|
|
17
|
+
export interface DecisionSource { by: string; where: string }
|
|
18
|
+
|
|
19
|
+
export interface DecisionDraft {
|
|
20
|
+
kind: DecisionKind;
|
|
21
|
+
anchors: string[];
|
|
22
|
+
text: string;
|
|
23
|
+
reason: string;
|
|
24
|
+
payload?: Record<string, JsonValue>;
|
|
25
|
+
source: DecisionSource;
|
|
26
|
+
valid_from: string;
|
|
27
|
+
supersedes?: string;
|
|
28
|
+
visibility: DecisionVisibility;
|
|
29
|
+
evidence_hash?: string;
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
export interface DecisionEntry extends DecisionDraft { id: string; entry_hash: string }
|
|
33
|
+
|
|
34
|
+
export const DECISION_ID_RE = /^D-[0-9a-f]{16}$/;
|
|
35
|
+
const ENTRY_HASH_RE = /^[0-9a-f]{64}$/;
|
|
36
|
+
const ISO_RE = /^\d{4}-\d{2}-\d{2}T\d{2}:\d{2}(:\d{2}(\.\d+)?)?(Z|[+-]\d{2}:\d{2})$/;
|
|
37
|
+
const STORED_KEYS = new Set(['id', 'kind', 'anchors', 'text', 'reason', 'payload', 'source', 'valid_from', 'supersedes', 'visibility', 'evidence_hash', 'entry_hash']);
|
|
38
|
+
|
|
39
|
+
const isObject = (v: unknown): v is Record<string, unknown> => typeof v === 'object' && v !== null && !Array.isArray(v);
|
|
40
|
+
const nonEmpty = (v: unknown): boolean => typeof v === 'string' && v.trim().length > 0;
|
|
41
|
+
|
|
42
|
+
function isJson(v: unknown, depth = 0): boolean {
|
|
43
|
+
if (depth > 20) return false;
|
|
44
|
+
if (v === null || typeof v === 'string' || typeof v === 'boolean') return true;
|
|
45
|
+
if (typeof v === 'number') return Number.isFinite(v);
|
|
46
|
+
if (Array.isArray(v)) return v.every((x) => isJson(x, depth + 1));
|
|
47
|
+
return isObject(v) && Object.values(v).every((x) => isJson(x, depth + 1));
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
/** Errors in a draft (empty = valid). Shared by the write boundary and the stored-entry check. */
|
|
51
|
+
export function draftErrors(d: Record<string, unknown>): string[] {
|
|
52
|
+
const e: string[] = [];
|
|
53
|
+
if (!DECISION_KINDS.includes(d.kind as DecisionKind)) e.push(`kind must be one of ${DECISION_KINDS.join('|')}`);
|
|
54
|
+
if (!Array.isArray(d.anchors) || !d.anchors.every(nonEmpty)) e.push('anchors must be a list of non-empty strings');
|
|
55
|
+
if (!nonEmpty(d.text)) e.push('text is required');
|
|
56
|
+
if (!nonEmpty(d.reason)) e.push('reason is required');
|
|
57
|
+
if (d.payload !== undefined && !(isObject(d.payload) && isJson(d.payload))) e.push('payload must be a JSON object');
|
|
58
|
+
const src = d.source;
|
|
59
|
+
if (!isObject(src) || typeof src.by !== 'string' || !nonEmpty(src.where) || Object.keys(src).some((k) => k !== 'by' && k !== 'where')) {
|
|
60
|
+
e.push('source must be {by, where}');
|
|
61
|
+
}
|
|
62
|
+
if (typeof d.valid_from !== 'string' || !ISO_RE.test(d.valid_from) || Number.isNaN(Date.parse(d.valid_from))) e.push('valid_from must be an ISO-8601 timestamp');
|
|
63
|
+
if (d.supersedes !== undefined && (typeof d.supersedes !== 'string' || !DECISION_ID_RE.test(d.supersedes))) e.push('supersedes must be a decision id D-<16 hex>');
|
|
64
|
+
if (d.visibility !== 'internal' && d.visibility !== 'client') e.push('visibility must be internal|client');
|
|
65
|
+
if (d.evidence_hash !== undefined && typeof d.evidence_hash !== 'string') e.push('evidence_hash must be a string');
|
|
66
|
+
return e;
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
/** A stored entry: the draft rules plus id, entry_hash and no unknown keys. */
|
|
70
|
+
export function storedEntryErrors(raw: unknown): string[] {
|
|
71
|
+
if (!isObject(raw)) return ['not a mapping'];
|
|
72
|
+
const e = draftErrors(raw);
|
|
73
|
+
for (const k of Object.keys(raw)) if (!STORED_KEYS.has(k)) e.push(`unknown key "${k}"`);
|
|
74
|
+
if (typeof raw.id !== 'string' || !DECISION_ID_RE.test(raw.id)) e.push('id must be D-<16 hex>');
|
|
75
|
+
if (typeof raw.entry_hash !== 'string' || !ENTRY_HASH_RE.test(raw.entry_hash)) e.push('entry_hash must be 64 hex');
|
|
76
|
+
return e;
|
|
77
|
+
}
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Who recorded a decision: `git config user.name` — a name, never the email (DOS-13.2, security
|
|
3
|
+
* considerations). Best-effort: no git, no name → 'unknown'.
|
|
4
|
+
*/
|
|
5
|
+
import { execFileSync } from 'child_process';
|
|
6
|
+
|
|
7
|
+
export function gitUserName(cwd: string): string {
|
|
8
|
+
try {
|
|
9
|
+
const name = execFileSync('git', ['config', 'user.name'], { cwd, stdio: ['ignore', 'pipe', 'ignore'], timeout: 5000 }).toString('utf8').trim();
|
|
10
|
+
return name || 'unknown';
|
|
11
|
+
} catch {
|
|
12
|
+
return 'unknown';
|
|
13
|
+
}
|
|
14
|
+
}
|
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Create a file only if its name is free, and never leave it half-written (red-team #13).
|
|
3
|
+
*
|
|
4
|
+
* The bytes go to a private tmp file opened with O_EXCL, are fsynced, then hard-linked to the final
|
|
5
|
+
* name. `link` fails with EEXIST when the name is taken, so the final file appears complete or not at
|
|
6
|
+
* all. On a filesystem without hard links an O_EXCL create of the final name keeps the exclusivity.
|
|
7
|
+
* Used for decision entries (same content → same name → "already recorded") and for the migration
|
|
8
|
+
* sentinel (two migrations of one unit cannot both believe they hold it).
|
|
9
|
+
*/
|
|
10
|
+
import { randomBytes } from 'crypto';
|
|
11
|
+
import * as fs from 'fs';
|
|
12
|
+
|
|
13
|
+
const NO_HARD_LINKS = new Set(['EPERM', 'ENOTSUP', 'EOPNOTSUPP', 'ENOSYS', 'EXDEV']);
|
|
14
|
+
|
|
15
|
+
/** true = created, false = the name was already taken. Any other failure throws. */
|
|
16
|
+
export function createFileExclusive(file: string, text: string): boolean {
|
|
17
|
+
const tmp = `${file}.tmp-${process.pid}-${randomBytes(6).toString('hex')}`;
|
|
18
|
+
const fd = fs.openSync(tmp, 'wx');
|
|
19
|
+
try {
|
|
20
|
+
fs.writeFileSync(fd, text);
|
|
21
|
+
fs.fsyncSync(fd);
|
|
22
|
+
} finally {
|
|
23
|
+
fs.closeSync(fd);
|
|
24
|
+
}
|
|
25
|
+
try {
|
|
26
|
+
fs.linkSync(tmp, file);
|
|
27
|
+
return true;
|
|
28
|
+
} catch (err) {
|
|
29
|
+
const code = (err as NodeJS.ErrnoException).code ?? '';
|
|
30
|
+
if (code === 'EEXIST') return false;
|
|
31
|
+
if (!NO_HARD_LINKS.has(code)) throw err;
|
|
32
|
+
try {
|
|
33
|
+
fs.writeFileSync(file, text, { flag: 'wx' });
|
|
34
|
+
return true;
|
|
35
|
+
} catch (e) {
|
|
36
|
+
if ((e as NodeJS.ErrnoException).code === 'EEXIST') return false;
|
|
37
|
+
throw e;
|
|
38
|
+
}
|
|
39
|
+
} finally {
|
|
40
|
+
fs.rmSync(tmp, { force: true });
|
|
41
|
+
}
|
|
42
|
+
}
|
|
@@ -0,0 +1,76 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* What git HEAD says — the trust anchor for agent-writable state (red-team #3, review H1).
|
|
3
|
+
*
|
|
4
|
+
* `.sungen/` files are written by the CLI and, without the guard, by any agent. A record in them is
|
|
5
|
+
* trusted only where it equals its committed version: a commit went through review, a local edit
|
|
6
|
+
* did not. Outside git, or for a file git does not track, nothing is trusted. Never throws.
|
|
7
|
+
*/
|
|
8
|
+
import { execFileSync } from 'child_process';
|
|
9
|
+
import * as fs from 'fs';
|
|
10
|
+
import * as path from 'path';
|
|
11
|
+
|
|
12
|
+
/** stdout of a git command run in `cwd`, or null when git fails (no repo, no HEAD, untracked path…). */
|
|
13
|
+
export function gitOutput(cwd: string, args: string[]): string | null {
|
|
14
|
+
try {
|
|
15
|
+
return execFileSync('git', args, { cwd, stdio: ['ignore', 'pipe', 'ignore'], maxBuffer: 64 * 1024 * 1024, timeout: 30000 }).toString('utf8');
|
|
16
|
+
} catch {
|
|
17
|
+
return null;
|
|
18
|
+
}
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
/**
|
|
22
|
+
* The text of each `<rev>:<path>` spec, read by ONE `git cat-file --batch` in `cwd` (`./path` is relative
|
|
23
|
+
* to it): null where git holds no such file; null for the whole call when git fails.
|
|
24
|
+
*/
|
|
25
|
+
export function gitFileTexts(cwd: string, specs: string[]): Array<string | null> | null {
|
|
26
|
+
if (specs.length === 0) return [];
|
|
27
|
+
if (specs.some((s) => /[\r\n]/.test(s))) return null;
|
|
28
|
+
let out: Buffer;
|
|
29
|
+
try {
|
|
30
|
+
out = execFileSync('git', ['cat-file', '--batch'], { cwd, input: `${specs.join('\n')}\n`, stdio: ['pipe', 'pipe', 'ignore'], maxBuffer: 256 * 1024 * 1024, timeout: 60000 });
|
|
31
|
+
} catch {
|
|
32
|
+
return null;
|
|
33
|
+
}
|
|
34
|
+
// Each answer: `<oid> <type> <size>\n<content>\n`, or `<spec> missing\n` (also `ambiguous`).
|
|
35
|
+
const texts: Array<string | null> = [];
|
|
36
|
+
let at = 0;
|
|
37
|
+
for (let i = 0; i < specs.length; i++) {
|
|
38
|
+
const eol = out.indexOf(0x0a, at);
|
|
39
|
+
if (eol < 0) return null;
|
|
40
|
+
const header = out.subarray(at, eol).toString('utf8').split(' ');
|
|
41
|
+
at = eol + 1;
|
|
42
|
+
if (header.length !== 3 || !/^\d+$/.test(header[2])) { texts.push(null); continue; }
|
|
43
|
+
const size = Number(header[2]);
|
|
44
|
+
texts.push(header[1] === 'blob' ? out.subarray(at, at + size).toString('utf8') : null);
|
|
45
|
+
at += size + 1;
|
|
46
|
+
}
|
|
47
|
+
return texts;
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
/**
|
|
51
|
+
* Whether `cwd` is in a shallow clone. git < 2.15 does not know `--is-shallow-repository` and echoes it
|
|
52
|
+
* back, so only an exact `true` / `false` is an answer; otherwise the `shallow` file in the git directory
|
|
53
|
+
* (common dir, or the git dir) decides, and when not even that can be found the clone is taken as shallow
|
|
54
|
+
* — its history is then unknown, which fails closed for the callers that ask.
|
|
55
|
+
*/
|
|
56
|
+
export function isShallowRepository(cwd: string): boolean {
|
|
57
|
+
const answer = gitOutput(cwd, ['rev-parse', '--is-shallow-repository'])?.trim();
|
|
58
|
+
if (answer === 'true' || answer === 'false') return answer === 'true';
|
|
59
|
+
const dirs = ['--git-common-dir', '--git-dir'].map((flag) => gitOutput(cwd, ['rev-parse', flag])?.trim())
|
|
60
|
+
.filter((d): d is string => !!d && !d.startsWith('-'));
|
|
61
|
+
return dirs.length === 0 || dirs.some((d) => fs.existsSync(path.join(path.resolve(cwd, d), 'shallow')));
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
/** The committed text of `abs` (a file inside `projectRoot`), or null when git HEAD has no such file. */
|
|
65
|
+
export function committedText(projectRoot: string, abs: string): string | null {
|
|
66
|
+
const rel = path.relative(projectRoot, abs).split(path.sep).join('/');
|
|
67
|
+
if (!rel || rel.startsWith('..') || path.isAbsolute(rel)) return null;
|
|
68
|
+
return gitOutput(projectRoot, ['show', `HEAD:./${rel}`]);
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
/** The committed JSON of `abs`, or null (absent from HEAD, or not JSON). */
|
|
72
|
+
export function committedJson(projectRoot: string, abs: string): unknown {
|
|
73
|
+
const text = committedText(projectRoot, abs);
|
|
74
|
+
if (text === null) return null;
|
|
75
|
+
try { return JSON.parse(text.replace(/^/, '')); } catch { return null; }
|
|
76
|
+
}
|
|
@@ -0,0 +1,119 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Typed human confirmation (DOS-13.4, red-team #3; review phase 03 M5).
|
|
3
|
+
*
|
|
4
|
+
* `journey --waive`, `journey --decide`, `delivery --accept-in-run` record or accept decisions that
|
|
5
|
+
* relax a gate, so a person must type a confirmation on a terminal:
|
|
6
|
+
* - no TTY (an agent's shell, a pipe, CI) → refuse and print the exact command for a human to run;
|
|
7
|
+
* piped stdin is not a TTY, so piping the phrase in never counts
|
|
8
|
+
* - the prompt and the answer go through the controlling terminal (`/dev/tty`), not stdin/stderr,
|
|
9
|
+
* so a redirected or wrapped stream neither sees nor answers it
|
|
10
|
+
* - the phrase carries a NONCE drawn per prompt, so an answer cannot be scripted from the refusal
|
|
11
|
+
* message: the printed command never contains it (it does not exist until the prompt is shown)
|
|
12
|
+
* This is a hard floor under Claude Code (the PreToolUse guard also denies agent writes to
|
|
13
|
+
* qa/**\/decisions/**) and a soft barrier elsewhere; git-HEAD comparison and the in-run rule remain.
|
|
14
|
+
*/
|
|
15
|
+
import { randomBytes } from 'crypto';
|
|
16
|
+
import * as fs from 'fs';
|
|
17
|
+
import * as readline from 'readline';
|
|
18
|
+
import * as tty from 'tty';
|
|
19
|
+
|
|
20
|
+
export interface ConfirmationIO {
|
|
21
|
+
input: NodeJS.ReadableStream & { isTTY?: boolean };
|
|
22
|
+
output: NodeJS.WritableStream;
|
|
23
|
+
/** The CLI's real terminal: prompt on `/dev/tty` (opened only when a confirmation is asked). */
|
|
24
|
+
controllingTerminal?: boolean;
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
export class HumanConfirmationRefused extends Error {
|
|
28
|
+
constructor(message: string, public readonly command?: string) {
|
|
29
|
+
super(message);
|
|
30
|
+
this.name = 'HumanConfirmationRefused';
|
|
31
|
+
}
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
/** The CLI's streams: stdin decides whether a human is there; the prompt itself uses /dev/tty. */
|
|
35
|
+
export const processIO = (): ConfirmationIO => ({ input: process.stdin, output: process.stderr, controllingTerminal: true });
|
|
36
|
+
export const isInteractive = (io: ConfirmationIO): boolean => io.input.isTTY === true;
|
|
37
|
+
|
|
38
|
+
export function shellQuote(arg: string): string {
|
|
39
|
+
return /^[A-Za-z0-9_./:=@%+-]+$/.test(arg) ? arg : `'${arg.replace(/'/g, `'\\''`)}'`;
|
|
40
|
+
}
|
|
41
|
+
export const formatCommand = (argv: string[]): string => argv.map(shellQuote).join(' ');
|
|
42
|
+
|
|
43
|
+
interface Terminal { input: NodeJS.ReadableStream; output: NodeJS.WritableStream; close(): void }
|
|
44
|
+
|
|
45
|
+
/** The terminal to talk to a human on, or null when there is none. */
|
|
46
|
+
function openTerminal(io: ConfirmationIO): Terminal | null {
|
|
47
|
+
if (!isInteractive(io)) return null;
|
|
48
|
+
if (!io.controllingTerminal || process.platform === 'win32') return { input: io.input, output: io.output, close: () => undefined };
|
|
49
|
+
let rfd = -1;
|
|
50
|
+
let wfd = -1;
|
|
51
|
+
try {
|
|
52
|
+
rfd = fs.openSync('/dev/tty', 'r');
|
|
53
|
+
wfd = fs.openSync('/dev/tty', 'w');
|
|
54
|
+
const input = new tty.ReadStream(rfd);
|
|
55
|
+
const output = new tty.WriteStream(wfd);
|
|
56
|
+
return { input, output, close: () => { input.destroy(); output.destroy(); } };
|
|
57
|
+
} catch {
|
|
58
|
+
for (const fd of [rfd, wfd]) if (fd >= 0) { try { fs.closeSync(fd); } catch { /* already closed */ } }
|
|
59
|
+
return null; // no controlling terminal: not a human at a keyboard
|
|
60
|
+
}
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
/** One line from the human, or null on EOF. */
|
|
64
|
+
function askLine(t: Terminal, prompt: string): Promise<string | null> {
|
|
65
|
+
return new Promise((resolve) => {
|
|
66
|
+
const rl = readline.createInterface({ input: t.input, output: t.output, terminal: false });
|
|
67
|
+
let settled = false;
|
|
68
|
+
const done = (v: string | null): void => { if (!settled) { settled = true; resolve(v); rl.close(); } };
|
|
69
|
+
rl.on('close', () => done(null));
|
|
70
|
+
rl.question(prompt, (answer) => done(answer));
|
|
71
|
+
});
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
function refuseWithoutTty(what: string, command: string): HumanConfirmationRefused {
|
|
75
|
+
return new HumanConfirmationRefused(
|
|
76
|
+
`Refused: ${what} needs a human at a terminal to type a confirmation, and this run has no TTY.\n`
|
|
77
|
+
+ ` Nothing was recorded. Run this yourself in a terminal:\n ${command}`,
|
|
78
|
+
command,
|
|
79
|
+
);
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
/** Refuse up front, before any state is read or written, when no human is at a terminal. */
|
|
83
|
+
export function requireInteractive(io: ConfirmationIO, what: string, command: string): void {
|
|
84
|
+
if (!isInteractive(io)) throw refuseWithoutTty(what, command);
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
/** A fresh, hard-to-guess suffix for one prompt. */
|
|
88
|
+
export const confirmationNonce = (): string => randomBytes(3).toString('hex').toUpperCase();
|
|
89
|
+
|
|
90
|
+
export async function requireTypedConfirmation(opts: {
|
|
91
|
+
what: string; phrase: string; command: string; summary: string[]; io: ConfirmationIO;
|
|
92
|
+
}): Promise<void> {
|
|
93
|
+
const t = openTerminal(opts.io);
|
|
94
|
+
if (!t) throw refuseWithoutTty(opts.what, opts.command);
|
|
95
|
+
try {
|
|
96
|
+
const expected = `${opts.phrase} ${confirmationNonce()}`;
|
|
97
|
+
t.output.write(`\nAbout to ${opts.what}:\n${opts.summary.map((l) => ` ${l}`).join('\n')}\n`);
|
|
98
|
+
const answer = await askLine(t, `Type "${expected}" to confirm (anything else cancels): `);
|
|
99
|
+
if ((answer ?? '').trim() !== expected) {
|
|
100
|
+
throw new HumanConfirmationRefused(`Cancelled: the confirmation did not match the phrase shown. Nothing was recorded.`, opts.command);
|
|
101
|
+
}
|
|
102
|
+
} finally {
|
|
103
|
+
t.close();
|
|
104
|
+
}
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
/** A leftover write-ahead sentinel: the human answers REVERT or KEEP. Never answered for them. */
|
|
108
|
+
export async function askMigrationChoice(opts: { detail: string; command: string; io: ConfirmationIO }): Promise<'REVERT' | 'KEEP'> {
|
|
109
|
+
const t = openTerminal(opts.io);
|
|
110
|
+
if (!t) throw refuseWithoutTty('answering an interrupted migration (REVERT or KEEP)', opts.command);
|
|
111
|
+
try {
|
|
112
|
+
t.output.write(`\n${opts.detail}\n`);
|
|
113
|
+
const answer = ((await askLine(t, 'Type REVERT (restore every file it lists) or KEEP (keep the new files): ')) ?? '').trim().toUpperCase();
|
|
114
|
+
if (answer === 'REVERT' || answer === 'KEEP') return answer;
|
|
115
|
+
throw new HumanConfirmationRefused('Cancelled: answer REVERT or KEEP. Nothing was changed.', opts.command);
|
|
116
|
+
} finally {
|
|
117
|
+
t.close();
|
|
118
|
+
}
|
|
119
|
+
}
|
|
@@ -0,0 +1,146 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Waivers move from `.sungen/journey/<slug>.state.json` into the decision layer (DOS-13.5, red-team
|
|
3
|
+
* #2) — without laundering an agent-forged waiver into a hash-valid decision (review H1).
|
|
4
|
+
*
|
|
5
|
+
* The state file is agent-writable, so a legacy waiver is TRUSTED only when its record equals the
|
|
6
|
+
* one committed in git HEAD (a commit went through review; a local edit did not). Per obligation,
|
|
7
|
+
* with L = the legacy waiver and D = the decision in force:
|
|
8
|
+
* L same as D → nothing to do (same reason against the same evidence)
|
|
9
|
+
* L untrusted → not imported, not counted; needs-you says UNTRUSTED and how to confirm it
|
|
10
|
+
* L trusted, no D → import L (`where: migrated`, idempotent by content id)
|
|
11
|
+
* L trusted, newer than D → import L superseding D — an `at` in the future never counts as newer
|
|
12
|
+
* L trusted, not newer than D → D wins; the dual-write corrects the legacy file
|
|
13
|
+
* The import runs ONCE per unit: when nothing untrusted remains, a marker is written and from then on
|
|
14
|
+
* the legacy file is only a dual-write target (so the previous CLI still reads the waivers), never a source.
|
|
15
|
+
*
|
|
16
|
+
* plan is read-only, so the board is the same whether or not the writes succeed. commit writes under
|
|
17
|
+
* the write-ahead sentinel; only an unwritable checkout is tolerated silently — a conflict is raised.
|
|
18
|
+
*/
|
|
19
|
+
import * as fs from 'fs';
|
|
20
|
+
import * as path from 'path';
|
|
21
|
+
import { withMigrationSentinel } from '../basis/migration-sentinel';
|
|
22
|
+
import { appendDecision, AppendResult } from './append-decision';
|
|
23
|
+
import { canonicalJson, computeDecisionId } from './decision-identity';
|
|
24
|
+
import { decisionsDirFor, unitDecisionScope } from './decision-file';
|
|
25
|
+
import { DecisionDraft } from './decision-schema';
|
|
26
|
+
import { gitUserName } from './decision-source';
|
|
27
|
+
import { committedJson } from './git-head-trust';
|
|
28
|
+
import { formatCommand } from './human-confirmation';
|
|
29
|
+
import { readDecisions } from './read-decisions';
|
|
30
|
+
import { asLegacyWaiver, effectiveWaiverEntries, isLegacyWaiver, LegacyWaiver, sameWaiver, waiverDraft } from './waiver-decisions';
|
|
31
|
+
|
|
32
|
+
export interface UntrustedWaiver { obligation: string; waiver: LegacyWaiver }
|
|
33
|
+
|
|
34
|
+
export interface WaiverSyncPlan {
|
|
35
|
+
scope: string | null;
|
|
36
|
+
/** obligation → waiver in force (legacy shape): what the board counts. */
|
|
37
|
+
waivers: Record<string, LegacyWaiver>;
|
|
38
|
+
untrusted: UntrustedWaiver[];
|
|
39
|
+
/** What the dual-write puts back in the legacy file: legacy key order, unknown records kept verbatim. */
|
|
40
|
+
stateWaivers: Record<string, unknown>;
|
|
41
|
+
imports: DecisionDraft[];
|
|
42
|
+
/** Write the import marker on commit: nothing untrusted is left to decide. */
|
|
43
|
+
markDone: boolean;
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
export const importMarkerPath = (projectRoot: string, slug: string): string =>
|
|
47
|
+
path.join(projectRoot, '.sungen', 'migrations', `${slug}.waivers-imported.json`);
|
|
48
|
+
|
|
49
|
+
const asRecord = (v: unknown): Record<string, unknown> => (v && typeof v === 'object' && !Array.isArray(v) ? v as Record<string, unknown> : {});
|
|
50
|
+
const newer = (w: LegacyWaiver, validFrom: string): boolean => {
|
|
51
|
+
const at = Date.parse(w.at);
|
|
52
|
+
return at > Date.parse(validFrom) && at <= Date.now();
|
|
53
|
+
};
|
|
54
|
+
|
|
55
|
+
export function planWaiverSync(projectRoot: string, unit: string, slug: string, statePath: string, legacy: unknown): WaiverSyncPlan {
|
|
56
|
+
const legacyWaivers = asRecord(legacy);
|
|
57
|
+
const scope = unitDecisionScope(projectRoot, unit);
|
|
58
|
+
const inForce = scope ? effectiveWaiverEntries(readDecisions(projectRoot, scope, { kind: 'waiver' }).entries) : new Map();
|
|
59
|
+
const stateWaivers: Record<string, unknown> = { ...legacyWaivers };
|
|
60
|
+
const waivers: Record<string, LegacyWaiver> = {};
|
|
61
|
+
const untrusted: UntrustedWaiver[] = [];
|
|
62
|
+
const imports: DecisionDraft[] = [];
|
|
63
|
+
|
|
64
|
+
if (scope && fs.existsSync(importMarkerPath(projectRoot, slug))) {
|
|
65
|
+
for (const [ob, d] of inForce) stateWaivers[ob] = waivers[ob] = asLegacyWaiver(d);
|
|
66
|
+
return { scope, waivers, untrusted, stateWaivers, imports, markDone: false };
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
const head = asRecord(asRecord(committedJson(projectRoot, statePath)).waivers);
|
|
70
|
+
for (const [ob, w] of Object.entries(legacyWaivers)) {
|
|
71
|
+
if (!isLegacyWaiver(w)) continue; // unreadable record: kept in the file, never counted
|
|
72
|
+
const d = inForce.get(ob);
|
|
73
|
+
if (d && sameWaiver(asLegacyWaiver(d), w)) { waivers[ob] = w; continue; }
|
|
74
|
+
if (!(ob in head) || canonicalJson(head[ob]) !== canonicalJson(w)) { untrusted.push({ obligation: ob, waiver: w }); continue; }
|
|
75
|
+
if (d && !newer(w, d.valid_from)) { stateWaivers[ob] = waivers[ob] = asLegacyWaiver(d); continue; }
|
|
76
|
+
if (scope) imports.push(waiverDraft(ob, w, { by: 'unknown', where: 'migrated' }, d?.id));
|
|
77
|
+
waivers[ob] = w;
|
|
78
|
+
}
|
|
79
|
+
for (const [ob, d] of inForce) if (!(ob in waivers)) stateWaivers[ob] = waivers[ob] = asLegacyWaiver(d);
|
|
80
|
+
return { scope, waivers, untrusted, stateWaivers, imports, markDone: !!scope && untrusted.length === 0 };
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
/** The command a human types to give (or confirm) a waiver — the board's line and `sungen next`'s "Waiting for you" (#954). */
|
|
84
|
+
export const waiveCommand = (unit: string, obligation: string, reason: string): string =>
|
|
85
|
+
formatCommand(['sungen', 'journey', '--screen', unit, '--waive', obligation, '--reason', reason]);
|
|
86
|
+
|
|
87
|
+
/** The needs-you line for a waiver that is not in git HEAD: not counted, and how a human confirms it. */
|
|
88
|
+
export function untrustedWaiverText(unit: string, slug: string, title: string, u: UntrustedWaiver): string {
|
|
89
|
+
const confirm = waiveCommand(unit, u.obligation, u.waiver.reason);
|
|
90
|
+
return `⚠️ Waiver on "${title}" is UNTRUSTED — not counted (decision.in-run: it is in .sungen/journey/${slug}.state.json `
|
|
91
|
+
+ `but not in git HEAD). If it is yours, confirm it on a terminal: ${confirm}`;
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
const UNWRITABLE = new Set(['EACCES', 'EROFS', 'ENOSPC', 'EPERM']);
|
|
95
|
+
|
|
96
|
+
function writeFile(file: string, text: string): void {
|
|
97
|
+
fs.mkdirSync(path.dirname(file), { recursive: true });
|
|
98
|
+
fs.writeFileSync(file, text, 'utf-8');
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
/**
|
|
102
|
+
* Persist a plan: imports, the dual-written legacy file, the import marker. Returns false when the
|
|
103
|
+
* checkout cannot be written (read-only, full disk); any other failure — a decision that does not
|
|
104
|
+
* verify, a migration in progress or interrupted — is raised, never swallowed.
|
|
105
|
+
*/
|
|
106
|
+
export function commitWaiverSync(projectRoot: string, unit: string, slug: string, plan: WaiverSyncPlan, statePath: string, stateText: string): boolean {
|
|
107
|
+
const marker = importMarkerPath(projectRoot, slug);
|
|
108
|
+
const writeMarker = plan.markDone && !fs.existsSync(marker);
|
|
109
|
+
const markerText = (ids: string[]) => `${JSON.stringify({ schema: 1, unit, importedAt: new Date().toISOString(), imported: ids }, null, 2)}\n`;
|
|
110
|
+
try {
|
|
111
|
+
if (!plan.scope || plan.imports.length === 0) {
|
|
112
|
+
writeFile(statePath, stateText);
|
|
113
|
+
if (writeMarker) writeFile(marker, markerText([]));
|
|
114
|
+
return true;
|
|
115
|
+
}
|
|
116
|
+
const scope = plan.scope;
|
|
117
|
+
const dir = decisionsDirFor(projectRoot, scope);
|
|
118
|
+
const targets = plan.imports.map((d) => path.join(dir, `${computeDecisionId(d, scope)}.yaml`));
|
|
119
|
+
withMigrationSentinel(projectRoot, unit, slug, `import ${plan.imports.length} legacy waiver(s) into the decision layer`,
|
|
120
|
+
[statePath, ...(writeMarker ? [marker] : []), ...targets], () => {
|
|
121
|
+
const ids = plan.imports.map((d) => appendDecision(projectRoot, scope, d).entry.id);
|
|
122
|
+
writeFile(statePath, stateText);
|
|
123
|
+
if (writeMarker) writeFile(marker, markerText(ids));
|
|
124
|
+
});
|
|
125
|
+
return true;
|
|
126
|
+
} catch (err) {
|
|
127
|
+
if (UNWRITABLE.has((err as NodeJS.ErrnoException)?.code ?? '')) return false;
|
|
128
|
+
throw err;
|
|
129
|
+
}
|
|
130
|
+
}
|
|
131
|
+
|
|
132
|
+
/**
|
|
133
|
+
* Record a waiver given now on the CLI; supersedes the waiver in force for that obligation, if any.
|
|
134
|
+
* The same waiver again records nothing — unless the one in force was imported (`where: migrated`):
|
|
135
|
+
* re-confirming it on a terminal is how a human vouches for a migrated waiver its legacy record cannot
|
|
136
|
+
* (decision.in-run), so it always writes the `cli` decision that supersedes it (#925).
|
|
137
|
+
*/
|
|
138
|
+
export function recordWaiver(projectRoot: string, unit: string, obligation: string, w: LegacyWaiver): AppendResult {
|
|
139
|
+
const scope = unitDecisionScope(projectRoot, unit);
|
|
140
|
+
if (!scope) throw new Error(`Cannot record a waiver: "${unit}" does not name exactly one unit under qa/screens, qa/flows or qa/api.`);
|
|
141
|
+
const current = effectiveWaiverEntries(readDecisions(projectRoot, scope, { kind: 'waiver' }).entries).get(obligation);
|
|
142
|
+
if (current && current.source?.where !== 'migrated' && sameWaiver(asLegacyWaiver(current), w)) {
|
|
143
|
+
return { status: 'already-recorded', entry: current, path: current.path };
|
|
144
|
+
}
|
|
145
|
+
return appendDecision(projectRoot, scope, waiverDraft(obligation, w, { by: gitUserName(projectRoot), where: 'cli' }, current?.id));
|
|
146
|
+
}
|
|
@@ -0,0 +1,61 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Read the decisions of one scope (DOS-13.1 – 13.3). Best-effort by contract: a missing directory
|
|
3
|
+
* reads as empty, a bad file becomes a `problem` — a read-only or half-broken checkout never throws.
|
|
4
|
+
*
|
|
5
|
+
* Only entries that verify against themselves take effect; an entry whose content no longer matches
|
|
6
|
+
* its id, or whose entry_hash no longer matches the entry, is reported and ignored (trust nothing
|
|
7
|
+
* written). Computed on read, never stored:
|
|
8
|
+
* - `superseded_by` — the latest visible entry that names this one in `supersedes`
|
|
9
|
+
* - `stale` — with `auditHash` given: `evidence_hash ≠ auditHash` (the waiver rule, DOS-13.5)
|
|
10
|
+
* `asOf` hides entries whose `valid_from` is later, so a later superseder does not supersede yet.
|
|
11
|
+
*/
|
|
12
|
+
import * as fs from 'fs';
|
|
13
|
+
import * as path from 'path';
|
|
14
|
+
import { DecisionEntry, DecisionKind } from './decision-schema';
|
|
15
|
+
import { DecisionProblem, decisionsDirFor, verifyDecisionFile } from './decision-file';
|
|
16
|
+
|
|
17
|
+
export interface ReadDecision extends DecisionEntry {
|
|
18
|
+
scope: string;
|
|
19
|
+
path: string;
|
|
20
|
+
superseded_by?: string;
|
|
21
|
+
stale?: boolean;
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
export interface ReadDecisionsOptions { asOf?: string; auditHash?: string; kind?: DecisionKind }
|
|
25
|
+
export interface ReadDecisionsResult { entries: ReadDecision[]; problems: DecisionProblem[] }
|
|
26
|
+
|
|
27
|
+
const byTime = (a: DecisionEntry, b: DecisionEntry): number =>
|
|
28
|
+
Date.parse(a.valid_from) - Date.parse(b.valid_from) || (a.id < b.id ? -1 : a.id > b.id ? 1 : 0);
|
|
29
|
+
|
|
30
|
+
export function readDecisions(projectRoot: string, scope: string, opts: ReadDecisionsOptions = {}): ReadDecisionsResult {
|
|
31
|
+
const problems: DecisionProblem[] = [];
|
|
32
|
+
let dir: string;
|
|
33
|
+
let names: string[];
|
|
34
|
+
try {
|
|
35
|
+
dir = decisionsDirFor(projectRoot, scope);
|
|
36
|
+
names = fs.readdirSync(dir).filter((f) => f.endsWith('.yaml')).sort();
|
|
37
|
+
} catch {
|
|
38
|
+
return { entries: [], problems };
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
const cutoff = opts.asOf ? Date.parse(opts.asOf) : Infinity;
|
|
42
|
+
const entries: ReadDecision[] = [];
|
|
43
|
+
for (const name of names) {
|
|
44
|
+
const file = path.join(dir, name);
|
|
45
|
+
const v = verifyDecisionFile(file, scope);
|
|
46
|
+
if (v.problem) { problems.push(v.problem); continue; }
|
|
47
|
+
if (Date.parse(v.entry.valid_from) > cutoff) continue;
|
|
48
|
+
entries.push({ ...v.entry, scope, path: file });
|
|
49
|
+
}
|
|
50
|
+
entries.sort(byTime);
|
|
51
|
+
|
|
52
|
+
const byId = new Map(entries.map((e) => [e.id, e]));
|
|
53
|
+
for (const e of entries) {
|
|
54
|
+
const target = e.supersedes ? byId.get(e.supersedes) : undefined;
|
|
55
|
+
if (target) target.superseded_by = e.id; // ascending time: the latest superseder wins
|
|
56
|
+
}
|
|
57
|
+
if (opts.auditHash !== undefined) {
|
|
58
|
+
for (const e of entries) if (e.evidence_hash !== undefined) e.stale = e.evidence_hash !== opts.auditHash;
|
|
59
|
+
}
|
|
60
|
+
return { entries: opts.kind ? entries.filter((e) => e.kind === opts.kind) : entries, problems };
|
|
61
|
+
}
|