@sun-asterisk/sungen 3.2.34 → 3.3.0-alpha.1
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/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/commands/audit.d.ts.map +1 -1
- package/dist/cli/commands/audit.js +9 -4
- package/dist/cli/commands/audit.js.map +1 -1
- package/dist/cli/commands/capability.d.ts.map +1 -1
- package/dist/cli/commands/capability.js +6 -2
- package/dist/cli/commands/capability.js.map +1 -1
- package/dist/cli/commands/contract-derive-command.d.ts +10 -0
- package/dist/cli/commands/contract-derive-command.d.ts.map +1 -0
- package/dist/cli/commands/contract-derive-command.js +99 -0
- package/dist/cli/commands/contract-derive-command.js.map +1 -0
- package/dist/cli/commands/contract.d.ts +17 -0
- package/dist/cli/commands/contract.d.ts.map +1 -0
- package/dist/cli/commands/contract.js +162 -0
- package/dist/cli/commands/contract.js.map +1 -0
- package/dist/cli/commands/defect.d.ts +7 -0
- package/dist/cli/commands/defect.d.ts.map +1 -0
- package/dist/cli/commands/defect.js +68 -0
- package/dist/cli/commands/defect.js.map +1 -0
- package/dist/cli/commands/delivery.d.ts.map +1 -1
- package/dist/cli/commands/delivery.js +87 -10
- package/dist/cli/commands/delivery.js.map +1 -1
- package/dist/cli/commands/generate.d.ts.map +1 -1
- package/dist/cli/commands/generate.js +3 -1
- 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 +41 -13
- package/dist/cli/commands/journey.js.map +1 -1
- package/dist/cli/commands/next.d.ts +8 -0
- package/dist/cli/commands/next.d.ts.map +1 -1
- package/dist/cli/commands/next.js +64 -6
- package/dist/cli/commands/next.js.map +1 -1
- package/dist/cli/commands/triage.d.ts +7 -0
- package/dist/cli/commands/triage.d.ts.map +1 -0
- package/dist/cli/commands/triage.js +98 -0
- package/dist/cli/commands/triage.js.map +1 -0
- 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 +31 -6
- 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/contract/approve-contract.d.ts +31 -0
- package/dist/contract/approve-contract.d.ts.map +1 -0
- package/dist/contract/approve-contract.js +87 -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 +18 -0
- package/dist/contract/build-trace-conformance.d.ts.map +1 -0
- package/dist/contract/build-trace-conformance.js +153 -0
- package/dist/contract/build-trace-conformance.js.map +1 -0
- package/dist/contract/classify-contract-change.d.ts +23 -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 +32 -0
- package/dist/contract/classify-trace-conformance.d.ts.map +1 -0
- package/dist/contract/classify-trace-conformance.js +117 -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 +71 -0
- package/dist/contract/contract-schema.d.ts.map +1 -0
- package/dist/contract/contract-schema.js +102 -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 +140 -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 +30 -0
- package/dist/contract/derive-free-feature.d.ts.map +1 -0
- package/dist/contract/derive-free-feature.js +145 -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 +73 -0
- package/dist/contract/project-contract-blockers.js.map +1 -0
- package/dist/contract/record-contract-approval.d.ts +21 -0
- package/dist/contract/record-contract-approval.d.ts.map +1 -0
- package/dist/contract/record-contract-approval.js +98 -0
- package/dist/contract/record-contract-approval.js.map +1 -0
- package/dist/contract/render-free-gherkin.d.ts +40 -0
- package/dist/contract/render-free-gherkin.d.ts.map +1 -0
- package/dist/contract/render-free-gherkin.js +141 -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 +8 -0
- package/dist/contract/resolve-unit-trace-identities.d.ts.map +1 -0
- package/dist/contract/resolve-unit-trace-identities.js +78 -0
- package/dist/contract/resolve-unit-trace-identities.js.map +1 -0
- package/dist/contract/trace/collect-trace-evidence.d.ts +33 -0
- package/dist/contract/trace/collect-trace-evidence.d.ts.map +1 -0
- package/dist/contract/trace/collect-trace-evidence.js +161 -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/select-final-attempt.d.ts +27 -0
- package/dist/contract/trace/select-final-attempt.d.ts.map +1 -0
- package/dist/contract/trace/select-final-attempt.js +116 -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 +21 -0
- package/dist/contract/trace/trace-binding.d.ts.map +1 -0
- package/dist/contract/trace/trace-binding.js +88 -0
- package/dist/contract/trace/trace-binding.js.map +1 -0
- package/dist/contract/trace/trace-file-reader.d.ts +35 -0
- package/dist/contract/trace/trace-file-reader.d.ts.map +1 -0
- package/dist/contract/trace/trace-file-reader.js +155 -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-identity-resolution.d.ts +26 -0
- package/dist/contract/trace/trace-identity-resolution.d.ts.map +1 -0
- package/dist/contract/trace/trace-identity-resolution.js +84 -0
- package/dist/contract/trace/trace-identity-resolution.js.map +1 -0
- package/dist/contract/trace/trace-run-record.d.ts +27 -0
- package/dist/contract/trace/trace-run-record.d.ts.map +1 -0
- package/dist/contract/trace/trace-run-record.js +115 -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 +41 -0
- package/dist/contract/trace/trace-zip-limits.d.ts.map +1 -0
- package/dist/contract/trace/trace-zip-limits.js +48 -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 +104 -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 +30 -0
- package/dist/decisions/confirmed-decision-commands.d.ts.map +1 -0
- package/dist/decisions/confirmed-decision-commands.js +172 -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 +42 -0
- package/dist/decisions/decision-schema.d.ts.map +1 -0
- package/dist/decisions/decision-schema.js +75 -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 +7 -0
- package/dist/decisions/git-head-trust.d.ts.map +1 -0
- package/dist/decisions/git-head-trust.js +76 -0
- package/dist/decisions/git-head-trust.js.map +1 -0
- package/dist/decisions/human-confirmation.d.ts +33 -0
- package/dist/decisions/human-confirmation.d.ts.map +1 -0
- package/dist/decisions/human-confirmation.js +155 -0
- package/dist/decisions/human-confirmation.js.map +1 -0
- package/dist/decisions/legacy-waiver-migration.d.ts +31 -0
- package/dist/decisions/legacy-waiver-migration.d.ts.map +1 -0
- package/dist/decisions/legacy-waiver-migration.js +170 -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 +4 -0
- package/dist/exporters/csv-exporter.d.ts.map +1 -1
- package/dist/exporters/csv-exporter.js +1 -1
- package/dist/exporters/csv-exporter.js.map +1 -1
- package/dist/exporters/feature-parser.d.ts +8 -0
- package/dist/exporters/feature-parser.d.ts.map +1 -1
- package/dist/exporters/feature-parser.js +12 -0
- package/dist/exporters/feature-parser.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 +59 -19
- package/dist/exporters/matrix/build.js.map +1 -1
- package/dist/exporters/matrix/carry-manual-results.d.ts +19 -0
- package/dist/exporters/matrix/carry-manual-results.d.ts.map +1 -1
- package/dist/exporters/matrix/carry-manual-results.js +76 -6
- package/dist/exporters/matrix/carry-manual-results.js.map +1 -1
- package/dist/exporters/matrix/export.d.ts +8 -2
- package/dist/exporters/matrix/export.d.ts.map +1 -1
- package/dist/exporters/matrix/export.js +16 -3
- 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 +4 -1
- 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 +6 -0
- package/dist/exporters/matrix/render-xlsx.js.map +1 -1
- package/dist/exporters/matrix/types.d.ts +9 -1
- package/dist/exporters/matrix/types.d.ts.map +1 -1
- package/dist/exporters/matrix/types.js.map +1 -1
- package/dist/exporters/playwright-report-parser.d.ts.map +1 -1
- package/dist/exporters/playwright-report-parser.js +1 -0
- package/dist/exporters/playwright-report-parser.js.map +1 -1
- package/dist/exporters/scenario-merger.d.ts.map +1 -1
- package/dist/exporters/scenario-merger.js +2 -0
- package/dist/exporters/scenario-merger.js.map +1 -1
- package/dist/exporters/types.d.ts +5 -0
- package/dist/exporters/types.d.ts.map +1 -1
- package/dist/generators/test-generator/code-generator.d.ts.map +1 -1
- package/dist/generators/test-generator/code-generator.js +13 -0
- package/dist/generators/test-generator/code-generator.js.map +1 -1
- package/dist/graph/evaluate-graph.d.ts +21 -0
- package/dist/graph/evaluate-graph.d.ts.map +1 -0
- package/dist/graph/evaluate-graph.js +49 -0
- package/dist/graph/evaluate-graph.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/node-decl.d.ts +59 -0
- package/dist/graph/node-decl.d.ts.map +1 -0
- package/dist/graph/node-decl.js +8 -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 +19 -0
- package/dist/graph/nodes/test-case-contract-nodes.d.ts.map +1 -0
- package/dist/graph/nodes/test-case-contract-nodes.js +56 -0
- package/dist/graph/nodes/test-case-contract-nodes.js.map +1 -0
- package/dist/graph/nodes/test-case-nodes.d.ts +26 -0
- package/dist/graph/nodes/test-case-nodes.d.ts.map +1 -0
- package/dist/graph/nodes/test-case-nodes.js +66 -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 +95 -0
- package/dist/graph/nodes/unit-audit-nodes.js.map +1 -0
- package/dist/graph/nodes/unit-contract-node.d.ts +20 -0
- package/dist/graph/nodes/unit-contract-node.d.ts.map +1 -0
- package/dist/graph/nodes/unit-contract-node.js +81 -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 +90 -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 +11 -0
- package/dist/graph/test-case-results.d.ts.map +1 -0
- package/dist/graph/test-case-results.js +137 -0
- package/dist/graph/test-case-results.js.map +1 -0
- package/dist/graph/test-case-satisfied.d.ts +25 -0
- package/dist/graph/test-case-satisfied.d.ts.map +1 -0
- package/dist/graph/test-case-satisfied.js +51 -0
- package/dist/graph/test-case-satisfied.js.map +1 -0
- package/dist/graph/test-case-state-shape.d.ts +67 -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 +133 -0
- package/dist/graph/unit-artifact-probes.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 +11 -0
- package/dist/harness/audit.d.ts.map +1 -1
- package/dist/harness/audit.js +34 -9
- package/dist/harness/audit.js.map +1 -1
- package/dist/harness/catalog/gate-rules.yaml +647 -0
- package/dist/harness/counted-assertions.d.ts +2 -0
- package/dist/harness/counted-assertions.d.ts.map +1 -0
- package/dist/harness/counted-assertions.js +38 -0
- package/dist/harness/counted-assertions.js.map +1 -0
- package/dist/harness/defect-draft.d.ts +14 -0
- package/dist/harness/defect-draft.d.ts.map +1 -0
- package/dist/harness/defect-draft.js +143 -0
- package/dist/harness/defect-draft.js.map +1 -0
- package/dist/harness/defects.d.ts +24 -0
- package/dist/harness/defects.d.ts.map +1 -0
- package/dist/harness/defects.js +118 -0
- package/dist/harness/defects.js.map +1 -0
- 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 +101 -0
- package/dist/harness/gates/audit-gate-verdict.js.map +1 -0
- package/dist/harness/gates/audit-input-hash.d.ts +4 -0
- package/dist/harness/gates/audit-input-hash.d.ts.map +1 -0
- package/dist/harness/gates/audit-input-hash.js +96 -0
- package/dist/harness/gates/audit-input-hash.js.map +1 -0
- package/dist/harness/gates/audit-report-verification.d.ts +32 -0
- package/dist/harness/gates/audit-report-verification.d.ts.map +1 -0
- package/dist/harness/gates/audit-report-verification.js +207 -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 +21 -0
- package/dist/harness/gates/delivery-checklist.d.ts.map +1 -0
- package/dist/harness/gates/delivery-checklist.js +128 -0
- package/dist/harness/gates/delivery-checklist.js.map +1 -0
- package/dist/harness/gates/delivery-decision-gate.d.ts +29 -0
- package/dist/harness/gates/delivery-decision-gate.d.ts.map +1 -0
- package/dist/harness/gates/delivery-decision-gate.js +91 -0
- package/dist/harness/gates/delivery-decision-gate.js.map +1 -0
- package/dist/harness/gates/delivery-decision-step.d.ts +16 -0
- package/dist/harness/gates/delivery-decision-step.d.ts.map +1 -0
- package/dist/harness/gates/delivery-decision-step.js +71 -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 +138 -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-review.d.ts +6 -0
- package/dist/harness/gates/migrated-waiver-review.d.ts.map +1 -0
- package/dist/harness/gates/migrated-waiver-review.js +116 -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 +24 -1
- package/dist/harness/journey.d.ts.map +1 -1
- package/dist/harness/journey.js +114 -23
- 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 +73 -45
- package/dist/harness/manifest.js.map +1 -1
- package/dist/harness/next-step.d.ts +37 -6
- package/dist/harness/next-step.d.ts.map +1 -1
- package/dist/harness/next-step.js +34 -174
- package/dist/harness/next-step.js.map +1 -1
- package/dist/harness/parse.d.ts +25 -0
- package/dist/harness/parse.d.ts.map +1 -1
- package/dist/harness/parse.js +38 -13
- package/dist/harness/parse.js.map +1 -1
- package/dist/harness/script-check.d.ts.map +1 -1
- package/dist/harness/script-check.js +29 -6
- 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 +64 -44
- package/dist/harness/sensors.js.map +1 -1
- package/dist/harness/triage-signals.d.ts +20 -0
- package/dist/harness/triage-signals.d.ts.map +1 -0
- package/dist/harness/triage-signals.js +30 -0
- package/dist/harness/triage-signals.js.map +1 -0
- package/dist/harness/triage-unit.d.ts +23 -0
- package/dist/harness/triage-unit.d.ts.map +1 -0
- package/dist/harness/triage-unit.js +142 -0
- package/dist/harness/triage-unit.js.map +1 -0
- package/dist/harness/triage.d.ts +28 -0
- package/dist/harness/triage.d.ts.map +1 -0
- package/dist/harness/triage.js +99 -0
- package/dist/harness/triage.js.map +1 -0
- package/dist/harness/viewpoint-ledger.d.ts.map +1 -1
- package/dist/harness/viewpoint-ledger.js +2 -1
- package/dist/harness/viewpoint-ledger.js.map +1 -1
- package/dist/hooks/bash-write-targets.d.ts +15 -0
- package/dist/hooks/bash-write-targets.d.ts.map +1 -0
- package/dist/hooks/bash-write-targets.js +173 -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 +9 -0
- package/dist/hooks/guard-protected-paths.d.ts.map +1 -0
- package/dist/hooks/guard-protected-paths.js +106 -0
- package/dist/hooks/guard-protected-paths.js.map +1 -0
- package/dist/hooks/guard-rules.d.ts +37 -0
- package/dist/hooks/guard-rules.d.ts.map +1 -0
- package/dist/hooks/guard-rules.js +61 -0
- package/dist/hooks/guard-rules.js.map +1 -0
- package/dist/hooks/guard-tty-spoof-rule.d.ts +15 -0
- package/dist/hooks/guard-tty-spoof-rule.d.ts.map +1 -0
- package/dist/hooks/guard-tty-spoof-rule.js +21 -0
- package/dist/hooks/guard-tty-spoof-rule.js.map +1 -0
- package/dist/hooks/hand-back-core.d.ts +37 -0
- package/dist/hooks/hand-back-core.d.ts.map +1 -0
- package/dist/hooks/hand-back-core.js +115 -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 +13 -0
- package/dist/hooks/session-start-unit-state-hook.d.ts.map +1 -0
- package/dist/hooks/session-start-unit-state-hook.js +55 -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 +42 -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/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 +16 -6
- package/dist/orchestrator/ai-skills/commands.js.map +1 -1
- package/dist/orchestrator/ai-skills/golden-skills.test.js +29 -0
- package/dist/orchestrator/ai-skills/golden-skills.test.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 +23 -0
- package/dist/orchestrator/project-initializer.js.map +1 -1
- package/dist/orchestrator/screen-manager.d.ts.map +1 -1
- package/dist/orchestrator/screen-manager.js +4 -2
- package/dist/orchestrator/screen-manager.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 +2 -0
- 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 +14 -16
- package/dist/orchestrator/templates/ai-src/commands/dashboard.md +2 -0
- package/dist/orchestrator/templates/ai-src/commands/delivery.md +2 -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 +2 -0
- package/dist/orchestrator/templates/ai-src/commands/run-test.md +14 -10
- package/dist/orchestrator/templates/ai-src/config/claude.md +20 -7
- package/dist/orchestrator/templates/ai-src/config/copilot.md +20 -7
- 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/skills/sungen-api-coverage-model/SKILL.md +1 -1
- package/dist/orchestrator/templates/ai-src/skills/sungen-capture-mobile/SKILL.md +3 -3
- package/dist/orchestrator/templates/ai-src/skills/sungen-data-factory/SKILL.md +9 -0
- package/dist/orchestrator/templates/ai-src/skills/sungen-error-mapping/SKILL.md +2 -0
- package/dist/orchestrator/templates/ai-src/skills/sungen-gherkin-syntax/SKILL.md +1 -1
- package/dist/orchestrator/templates/ai-src/skills/sungen-harness-audit/SKILL.md +2 -0
- package/dist/orchestrator/templates/claude-hooks/sungen-hook.cjs +64 -0
- 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/registry.ts +16 -0
- package/src/cli/commands/audit.ts +9 -4
- package/src/cli/commands/capability.ts +4 -2
- package/src/cli/commands/contract-derive-command.ts +60 -0
- package/src/cli/commands/contract.ts +130 -0
- package/src/cli/commands/defect.ts +49 -0
- package/src/cli/commands/delivery.ts +85 -13
- package/src/cli/commands/generate.ts +3 -1
- package/src/cli/commands/journey.ts +39 -14
- package/src/cli/commands/next.ts +60 -8
- package/src/cli/commands/triage.ts +57 -0
- package/src/cli/commands/update.ts +35 -8
- package/src/cli/index.ts +24 -30
- package/src/contract/approve-contract.ts +94 -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 +100 -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 +116 -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 +137 -0
- package/src/contract/contract-units.ts +52 -0
- package/src/contract/contracts-lock-file.ts +97 -0
- package/src/contract/data-normalize.ts +66 -0
- package/src/contract/derive-free-feature.ts +110 -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 +36 -0
- package/src/contract/record-contract-approval.ts +70 -0
- package/src/contract/render-free-gherkin.ts +113 -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 +35 -0
- package/src/contract/trace/collect-trace-evidence.ts +109 -0
- package/src/contract/trace/identity-cache.ts +61 -0
- package/src/contract/trace/select-final-attempt.ts +70 -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 +103 -0
- package/src/contract/trace/trace-binding.ts +56 -0
- package/src/contract/trace/trace-file-reader.ts +123 -0
- package/src/contract/trace/trace-fingerprint.ts +68 -0
- package/src/contract/trace/trace-identity-resolution.ts +74 -0
- package/src/contract/trace/trace-run-record.ts +74 -0
- package/src/contract/trace/trace-steps.ts +88 -0
- package/src/contract/trace/trace-zip-limits.ts +73 -0
- package/src/contract/trace/trace-zip-reader.ts +127 -0
- package/src/contract/verify-approval-decisions.ts +60 -0
- package/src/decisions/append-decision.ts +35 -0
- package/src/decisions/confirmed-decision-commands.ts +128 -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 +76 -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 +32 -0
- package/src/decisions/human-confirmation.ts +114 -0
- package/src/decisions/legacy-waiver-migration.ts +135 -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 +5 -1
- package/src/exporters/feature-parser.ts +12 -0
- package/src/exporters/matrix/build.ts +62 -18
- package/src/exporters/matrix/carry-manual-results.ts +70 -7
- package/src/exporters/matrix/export.ts +17 -2
- package/src/exporters/matrix/gates.ts +62 -129
- package/src/exporters/matrix/render-csv.ts +4 -1
- package/src/exporters/matrix/render-gate-overrides-sheet.ts +50 -0
- package/src/exporters/matrix/render-xlsx.ts +6 -0
- package/src/exporters/matrix/types.ts +9 -1
- package/src/exporters/playwright-report-parser.ts +1 -0
- package/src/exporters/scenario-merger.ts +2 -1
- package/src/exporters/types.ts +5 -0
- package/src/generators/test-generator/code-generator.ts +13 -0
- package/src/graph/evaluate-graph.ts +62 -0
- package/src/graph/locate-finding.ts +93 -0
- package/src/graph/node-decl.ts +65 -0
- package/src/graph/nodes/core-node-catalog.ts +15 -0
- package/src/graph/nodes/test-case-contract-nodes.ts +69 -0
- package/src/graph/nodes/test-case-nodes.ts +68 -0
- package/src/graph/nodes/unit-audit-nodes.ts +95 -0
- package/src/graph/nodes/unit-contract-node.ts +82 -0
- package/src/graph/nodes/unit-design-nodes.ts +88 -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 +90 -0
- package/src/graph/test-case-satisfied.ts +55 -0
- package/src/graph/test-case-state-shape.ts +62 -0
- package/src/graph/test-case-states.ts +84 -0
- package/src/graph/unit-artifact-probes.ts +77 -0
- package/src/graph/unit-state-facts.ts +112 -0
- package/src/harness/audit.ts +46 -11
- package/src/harness/catalog/gate-rules.yaml +647 -0
- package/src/harness/counted-assertions.ts +33 -0
- package/src/harness/defect-draft.ts +115 -0
- package/src/harness/defects.ts +94 -0
- package/src/harness/gates/audit-gate-terms.ts +80 -0
- package/src/harness/gates/audit-gate-verdict.ts +78 -0
- package/src/harness/gates/audit-input-hash.ts +55 -0
- package/src/harness/gates/audit-report-verification.ts +173 -0
- package/src/harness/gates/decisions-hash.ts +42 -0
- package/src/harness/gates/delivery-checklist.ts +120 -0
- package/src/harness/gates/delivery-decision-gate.ts +111 -0
- package/src/harness/gates/delivery-decision-step.ts +73 -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 +99 -0
- package/src/harness/gates/journey-gate-inputs.ts +89 -0
- package/src/harness/gates/migrated-waiver-review.ts +68 -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 +122 -27
- package/src/harness/manifest.ts +69 -45
- package/src/harness/next-step.ts +60 -170
- package/src/harness/parse.ts +40 -13
- package/src/harness/script-check.ts +27 -6
- package/src/harness/sensors.ts +73 -47
- package/src/harness/triage-signals.ts +46 -0
- package/src/harness/triage-unit.ts +113 -0
- package/src/harness/triage.ts +127 -0
- package/src/harness/viewpoint-ledger.ts +2 -2
- package/src/hooks/bash-write-targets.ts +109 -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 +70 -0
- package/src/hooks/guard-rules.ts +98 -0
- package/src/hooks/guard-tty-spoof-rule.ts +31 -0
- package/src/hooks/hand-back-core.ts +106 -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 +54 -0
- package/src/hooks/stop-next-step-hook.ts +37 -0
- package/src/hooks/sungen-project-root.ts +80 -0
- package/src/orchestrator/ai-skills/command-partial-includes.ts +72 -0
- package/src/orchestrator/ai-skills/commands.ts +22 -8
- package/src/orchestrator/ai-skills/golden-skills.test.ts +23 -0
- package/src/orchestrator/claude-hooks-install.ts +168 -0
- package/src/orchestrator/lockstep-version.ts +28 -15
- package/src/orchestrator/project-initializer.ts +21 -0
- package/src/orchestrator/screen-manager.ts +3 -1
- package/src/orchestrator/templates/ai-src/commands/add-api.md +2 -0
- package/src/orchestrator/templates/ai-src/commands/add-flow.md +2 -0
- 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 +14 -16
- package/src/orchestrator/templates/ai-src/commands/dashboard.md +2 -0
- package/src/orchestrator/templates/ai-src/commands/delivery.md +2 -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 +2 -0
- package/src/orchestrator/templates/ai-src/commands/run-test.md +14 -10
- package/src/orchestrator/templates/ai-src/config/claude.md +20 -7
- package/src/orchestrator/templates/ai-src/config/copilot.md +20 -7
- 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/skills/sungen-api-coverage-model/SKILL.md +1 -1
- package/src/orchestrator/templates/ai-src/skills/sungen-capture-mobile/SKILL.md +3 -3
- package/src/orchestrator/templates/ai-src/skills/sungen-data-factory/SKILL.md +9 -0
- package/src/orchestrator/templates/ai-src/skills/sungen-error-mapping/SKILL.md +2 -0
- package/src/orchestrator/templates/ai-src/skills/sungen-gherkin-syntax/SKILL.md +1 -1
- package/src/orchestrator/templates/ai-src/skills/sungen-harness-audit/SKILL.md +2 -0
- package/src/orchestrator/templates/claude-hooks/sungen-hook.cjs +64 -0
- package/src/utils/safe-yaml-parse.ts +44 -0
|
@@ -0,0 +1,115 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* `sungen defect draft` — turn each `product-candidate` row of a unit's last triage into a record
|
|
3
|
+
* `qa/defects/DEF-NNN-<slug>.md` (see defects.ts for the link checks). Deterministic: Steps come
|
|
4
|
+
* from the scenario's Gherkin with test-data resolved, Expected/Actual from the assertion, and the
|
|
5
|
+
* severity is PROPOSED by rule until QA confirms it. Never overwrites a file, and never drafts a
|
|
6
|
+
* scenario an existing record already names.
|
|
7
|
+
*/
|
|
8
|
+
import * as fs from 'fs';
|
|
9
|
+
import * as path from 'path';
|
|
10
|
+
import { stringify as toYaml } from 'yaml';
|
|
11
|
+
import type { ResolvedUnit } from '../cli/resolve-unit';
|
|
12
|
+
import { redactSecrets } from '../exporters/api-testcase-formatter';
|
|
13
|
+
import { getEnvironment } from '../exporters/run-environment';
|
|
14
|
+
import { parseFeatureMetadata } from '../exporters/feature-parser';
|
|
15
|
+
import { mergeFeatureAndSpec, MergedScenario } from '../exporters/scenario-merger';
|
|
16
|
+
import { maskCredentialValue } from '../exporters/sensitive-key';
|
|
17
|
+
import { parseSpecFile } from '../exporters/spec-parser';
|
|
18
|
+
import { loadTestData, substituteTestDataVars } from '../exporters/test-data-resolver';
|
|
19
|
+
import { readCapabilities } from './capability';
|
|
20
|
+
import { defectsDir, nextDefectId, readDefects } from './defects';
|
|
21
|
+
import type { TriageRow } from './triage';
|
|
22
|
+
import { readTriageReport, unitSecrets } from './triage-unit';
|
|
23
|
+
|
|
24
|
+
export interface DraftResult {
|
|
25
|
+
/** `sungen triage` has not run for this unit. */
|
|
26
|
+
missingTriage?: boolean;
|
|
27
|
+
written: string[];
|
|
28
|
+
skipped: Array<{ scenario: string; id: string }>;
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
/** Blocking actions: a @high scenario that fails on one of these is `major`, otherwise `minor`. */
|
|
32
|
+
const BLOCKING = /\b(?:submit|save|apply|log ?in|sign ?in|sign ?up|register|checkout|pay|confirm|send|navigate|redirect)\b/i;
|
|
33
|
+
|
|
34
|
+
const slug = (s: string): string =>
|
|
35
|
+
s.replace(/^\s*(?:VP|FL)-[A-Z0-9-]+\s+/i, '').toLowerCase().replace(/[^a-z0-9]+/g, '-').replace(/^-|-$/g, '').slice(0, 50).replace(/-$/, '');
|
|
36
|
+
|
|
37
|
+
interface UnitScenario { merged: MergedScenario; testData: Record<string, string>; secrets: string[] }
|
|
38
|
+
|
|
39
|
+
/** Scenario title → its merged Gherkin, test-data (credentials masked) and secret values. */
|
|
40
|
+
function unitScenarios(cwd: string, unit: ResolvedUnit): Map<string, UnitScenario> {
|
|
41
|
+
const out = new Map<string, UnitScenario>();
|
|
42
|
+
const featuresDir = path.join(unit.dir, 'features');
|
|
43
|
+
if (!fs.existsSync(featuresDir)) return out;
|
|
44
|
+
for (const f of fs.readdirSync(featuresDir).filter((x) => x.endsWith('.feature'))) {
|
|
45
|
+
const base = f.replace(/\.feature$/, '');
|
|
46
|
+
const spec = parseSpecFile(path.join(cwd, 'specs', 'generated', unit.unitId, `${base}.spec.ts`));
|
|
47
|
+
const dataFile = path.join(unit.dir, 'test-data', `${base}.yaml`);
|
|
48
|
+
const raw = fs.existsSync(dataFile) ? loadTestData(dataFile) : {};
|
|
49
|
+
const testData = Object.fromEntries(Object.entries(raw).map(([k, v]) => [k, maskCredentialValue(k, String(v))]));
|
|
50
|
+
const secrets = unitSecrets(raw, cwd);
|
|
51
|
+
for (const m of mergeFeatureAndSpec(parseFeatureMetadata(path.join(featuresDir, f)), spec)) {
|
|
52
|
+
out.set(m.feature.name, { merged: m, testData, secrets });
|
|
53
|
+
}
|
|
54
|
+
}
|
|
55
|
+
return out;
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
function render(cwd: string, unit: ResolvedUnit, id: string, row: TriageRow, sc: UnitScenario | undefined): string {
|
|
59
|
+
const tags = sc?.merged.feature.tags ?? [];
|
|
60
|
+
const resolve = (t: string): string => (sc ? substituteTestDataVars(t, sc.testData) : t);
|
|
61
|
+
const steps = (sc?.merged.resolvedSteps ?? []).map((s) => resolve(s.text));
|
|
62
|
+
const thens = (sc?.merged.resolvedExpected ?? []).map((s) => resolve(s.text));
|
|
63
|
+
const high = tags.some((t) => t === '@high' || t === '@critical');
|
|
64
|
+
const severity = high && BLOCKING.test([row.scenario, ...steps].join(' ')) ? 'major' : 'minor';
|
|
65
|
+
const specFile = path.join(unit.dir, 'requirements', 'spec.md');
|
|
66
|
+
const env = getEnvironment(cwd);
|
|
67
|
+
const front = {
|
|
68
|
+
id, title: row.scenario, severity, severity_source: 'proposed', status: 'open',
|
|
69
|
+
units: [unit.unitId], scenarios: [row.scenario], claims: row.ids, legacy: row.legacy,
|
|
70
|
+
spec: fs.existsSync(specFile) ? path.relative(cwd, specFile) : '',
|
|
71
|
+
found: {
|
|
72
|
+
date: new Date().toISOString().slice(0, 10),
|
|
73
|
+
env: process.env.SUNGEN_ENV || 'default',
|
|
74
|
+
base_url: env.baseURL,
|
|
75
|
+
platform: row.variant || readCapabilities(cwd).platform || 'web',
|
|
76
|
+
},
|
|
77
|
+
tracker: '',
|
|
78
|
+
};
|
|
79
|
+
const body = [
|
|
80
|
+
'---', toYaml(front).trimEnd(), '---', '',
|
|
81
|
+
`# ${id}: ${row.scenario}`, '',
|
|
82
|
+
'## Summary', '', '(to fill: what is wrong, where, and who it affects)', '',
|
|
83
|
+
'## Steps to reproduce', '', ...(steps.length ? steps.map((s, i) => `${i + 1}. ${s}`) : ['(scenario steps not found — see the feature)']), '',
|
|
84
|
+
'## Expected', '', ...thens.map((t) => `- ${t}`), ...(row.expected ? [`- Asserted value: ${row.expected}`] : []), '',
|
|
85
|
+
'## Actual', '', `- ${row.received ? `Received: ${row.received}` : row.evidence}`, '',
|
|
86
|
+
'## Evidence', '', `- Error: ${row.evidence}`, ...(row.tracePath ? [`- Trace: ${row.tracePath}`] : []), '',
|
|
87
|
+
'## Notes', '', `- Triage: ${row.class} (${row.ruleId})${row.variant ? ` on ${row.variant}` : ''}`, '',
|
|
88
|
+
].join('\n');
|
|
89
|
+
return redactSecrets(body, sc?.secrets ?? []);
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
export function draftDefects(cwd: string, unit: ResolvedUnit, opts: { scenario?: string } = {}): DraftResult {
|
|
93
|
+
const triage = readTriageReport(cwd, unit.unitId);
|
|
94
|
+
if (!triage) return { missingTriage: true, written: [], skipped: [] };
|
|
95
|
+
const recorded = new Map<string, string>();
|
|
96
|
+
for (const r of readDefects(cwd)) if (r.units.includes(unit.unitId)) for (const s of r.scenarios) recorded.set(s, r.id);
|
|
97
|
+
|
|
98
|
+
const wanted = triage.rows.filter((r) => r.class === 'product-candidate'
|
|
99
|
+
&& (!opts.scenario || r.scenario.includes(opts.scenario) || r.ids.includes(opts.scenario)));
|
|
100
|
+
const scenarios = unitScenarios(cwd, unit);
|
|
101
|
+
const result: DraftResult = { written: [], skipped: [] };
|
|
102
|
+
const seen = new Set<string>();
|
|
103
|
+
for (const row of wanted) {
|
|
104
|
+
if (seen.has(row.scenario)) continue; // one record per scenario, even across OS variants
|
|
105
|
+
seen.add(row.scenario);
|
|
106
|
+
const existing = recorded.get(row.scenario);
|
|
107
|
+
if (existing) { result.skipped.push({ scenario: row.scenario, id: existing }); continue; }
|
|
108
|
+
const id = nextDefectId(cwd);
|
|
109
|
+
const file = path.join(defectsDir(cwd), `${id}-${slug(row.scenario) || 'defect'}.md`);
|
|
110
|
+
fs.mkdirSync(path.dirname(file), { recursive: true });
|
|
111
|
+
fs.writeFileSync(file, render(cwd, unit, id, row, scenarios.get(row.scenario)), { encoding: 'utf-8', flag: 'wx' });
|
|
112
|
+
result.written.push(path.relative(cwd, file));
|
|
113
|
+
}
|
|
114
|
+
return result;
|
|
115
|
+
}
|
|
@@ -0,0 +1,94 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Defect records — `qa/defects/DEF-NNN-<slug>.md`, one project-wide folder. A scenario points at a
|
|
3
|
+
* record with `@known-defect:DEF-NNN`; the record points back at units, scenarios, claims and legacy
|
|
4
|
+
* TCs in its frontmatter. This module reads the records and checks both directions of that link.
|
|
5
|
+
* Drafting a record from a triage row lives in defect-draft.ts.
|
|
6
|
+
*/
|
|
7
|
+
import * as fs from 'fs';
|
|
8
|
+
import * as path from 'path';
|
|
9
|
+
import { parse as parseYaml } from 'yaml';
|
|
10
|
+
|
|
11
|
+
export const DEFECT_STATUSES = ['open', 'fixed', 'verified', 'closed', 'wont-fix'] as const;
|
|
12
|
+
export type DefectStatus = typeof DEFECT_STATUSES[number];
|
|
13
|
+
|
|
14
|
+
export interface DefectRecord {
|
|
15
|
+
id: string;
|
|
16
|
+
title: string;
|
|
17
|
+
severity: string;
|
|
18
|
+
status: string;
|
|
19
|
+
units: string[];
|
|
20
|
+
scenarios: string[];
|
|
21
|
+
/** Project-relative path of the record file. */
|
|
22
|
+
file: string;
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
const DEF_FILE = /^(DEF-(\d+))-.*\.md$/;
|
|
26
|
+
const KNOWN_DEFECT_TAG = /@known-defect:(DEF-\d+)\b/g;
|
|
27
|
+
|
|
28
|
+
export function defectsDir(cwd: string): string {
|
|
29
|
+
return path.join(cwd, 'qa', 'defects');
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
function defectFiles(cwd: string): string[] {
|
|
33
|
+
const dir = defectsDir(cwd);
|
|
34
|
+
return fs.existsSync(dir) ? fs.readdirSync(dir).filter((f) => DEF_FILE.test(f)).sort() : [];
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
/** The next free id: one above the highest number on disk, zero-padded to 3. */
|
|
38
|
+
export function nextDefectId(cwd: string): string {
|
|
39
|
+
const max = defectFiles(cwd).reduce((m, f) => Math.max(m, Number(f.match(DEF_FILE)![2])), 0);
|
|
40
|
+
return `DEF-${String(max + 1).padStart(3, '0')}`;
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
const list = (v: unknown): string[] => (Array.isArray(v) ? v.map(String) : []);
|
|
44
|
+
|
|
45
|
+
/** Every record whose frontmatter parses; a malformed file is skipped, never thrown on. */
|
|
46
|
+
export function readDefects(cwd: string): DefectRecord[] {
|
|
47
|
+
const out: DefectRecord[] = [];
|
|
48
|
+
for (const f of defectFiles(cwd)) {
|
|
49
|
+
const file = path.join(defectsDir(cwd), f);
|
|
50
|
+
try {
|
|
51
|
+
const fm = parseYaml(fs.readFileSync(file, 'utf-8').split(/^---\s*$/m)[1] ?? '') as Record<string, unknown> | null;
|
|
52
|
+
if (!fm || typeof fm !== 'object') continue;
|
|
53
|
+
out.push({
|
|
54
|
+
id: String(fm.id ?? f.match(DEF_FILE)![1]),
|
|
55
|
+
title: String(fm.title ?? ''),
|
|
56
|
+
severity: String(fm.severity ?? ''),
|
|
57
|
+
status: String(fm.status ?? ''),
|
|
58
|
+
units: list(fm.units),
|
|
59
|
+
scenarios: list(fm.scenarios),
|
|
60
|
+
file: path.relative(cwd, file),
|
|
61
|
+
});
|
|
62
|
+
} catch {
|
|
63
|
+
// unreadable frontmatter: not a record we can reason about
|
|
64
|
+
}
|
|
65
|
+
}
|
|
66
|
+
return out;
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
/**
|
|
70
|
+
* Advisory audit findings for one unit (never scored):
|
|
71
|
+
* DEFECT-REF-MISSING a `@known-defect:DEF-NNN` tag with no record file.
|
|
72
|
+
* DEFECT-ORPHAN (info) an OPEN record of this unit names a scenario the unit no longer has.
|
|
73
|
+
*/
|
|
74
|
+
export function defectLinkFindings(cwd: string, unitId: string, featureText: string, scenarioNames: string[]): string[] {
|
|
75
|
+
const records = readDefects(cwd);
|
|
76
|
+
const findings: string[] = [];
|
|
77
|
+
const known = new Set(records.map((r) => r.id));
|
|
78
|
+
const tagged = new Set([...featureText.matchAll(KNOWN_DEFECT_TAG)].map((m) => m[1]));
|
|
79
|
+
for (const id of tagged) {
|
|
80
|
+
if (!known.has(id)) {
|
|
81
|
+
findings.push(`DEFECT-REF-MISSING: \`@known-defect:${id}\` has no record in qa/defects/ — draft it with \`sungen defect draft --screen ${unitId}\`, or correct the id.`);
|
|
82
|
+
}
|
|
83
|
+
}
|
|
84
|
+
const names = new Set(scenarioNames);
|
|
85
|
+
for (const r of records) {
|
|
86
|
+
if (r.status !== 'open' || !r.units.includes(unitId)) continue;
|
|
87
|
+
for (const s of r.scenarios) {
|
|
88
|
+
if (!names.has(s)) {
|
|
89
|
+
findings.push(`DEFECT-ORPHAN (info): ${r.id} is open and names scenario "${s}", which ${unitId} no longer has — update the record's scenarios, or close it.`);
|
|
90
|
+
}
|
|
91
|
+
}
|
|
92
|
+
}
|
|
93
|
+
return findings;
|
|
94
|
+
}
|
|
@@ -0,0 +1,80 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The audit's gateStatus conjunction, in ONE place (OES-11.2): `runAudit` evaluates it to set
|
|
3
|
+
* gateStatus, and `verifyAuditReport` re-evaluates it from the report's own fields to catch a stored
|
|
4
|
+
* PASS the report cannot reproduce (a hand-flipped gateStatus + exitCode — tester finding m1).
|
|
5
|
+
*
|
|
6
|
+
* Each term names its catalog rule; `resolveRule` decides whether a violated one blocks. The decision
|
|
7
|
+
* layer adds three hard terms: a rejected override, a decision file that does not verify (id / hash).
|
|
8
|
+
* Re-evaluating catches casual tampering only: a forger who also rewrites every finding, gap and
|
|
9
|
+
* verdict in the report is out of scope here, as for inputsHash (anti-staleness, not anti-forgery).
|
|
10
|
+
*/
|
|
11
|
+
import { evaluateGateTerms, GateTerm } from './gate-terms';
|
|
12
|
+
import { trackGateRules } from './resolve-rule';
|
|
13
|
+
import { UnitGateOverrides } from './unit-gate-overrides';
|
|
14
|
+
|
|
15
|
+
/** The report fields the conjunction reads — present in every report runAudit writes. */
|
|
16
|
+
export interface AuditTermSources {
|
|
17
|
+
gate: { gaps: unknown[] };
|
|
18
|
+
depth: { verdict: string };
|
|
19
|
+
claim: { verdict: string };
|
|
20
|
+
spec: { verdict: string };
|
|
21
|
+
taxonomyMismatch: boolean;
|
|
22
|
+
gateSensorError: boolean;
|
|
23
|
+
reusedScenarioIds: unknown[];
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
export function auditGateTerms(r: AuditTermSources): GateTerm[] {
|
|
27
|
+
return [
|
|
28
|
+
{ rule: 'audit.viewpoint-coverage', holds: r.gate.gaps.length === 0 },
|
|
29
|
+
{ rule: 'audit.assertion-depth', holds: r.depth.verdict !== 'fail' },
|
|
30
|
+
{ rule: 'audit.claim-proof', holds: r.claim.verdict !== 'fail' },
|
|
31
|
+
{ rule: 'audit.spec-coverage', holds: r.spec.verdict !== 'fail' },
|
|
32
|
+
{ rule: 'audit.taxonomy-mismatch', holds: !r.taxonomyMismatch },
|
|
33
|
+
// An 'error' finding from a capability gate sensor (api, db, …) — the driver's own verdict.
|
|
34
|
+
{ rule: 'audit.gate-sensor-error', holds: !r.gateSensorError },
|
|
35
|
+
// #694 — a reused id reaches the deliverable as two rows under one testcase id.
|
|
36
|
+
{ rule: 'audit.scenario-id-reuse', holds: r.reusedScenarioIds.length === 0 },
|
|
37
|
+
];
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
export function decisionTerms(o: UnitGateOverrides): GateTerm[] {
|
|
41
|
+
return [
|
|
42
|
+
{ rule: 'gate.override-rejected', holds: o.rejected.length === 0 },
|
|
43
|
+
{ rule: 'decision.id-reuse', holds: !o.problems.some((p) => p.code === 'id-mismatch') },
|
|
44
|
+
{ rule: 'decision.mutated', holds: !o.problems.some((p) => p.code !== 'id-mismatch') },
|
|
45
|
+
];
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
/** The gateStatus a report's own terms produce under the unit's overrides — what the audit computed. */
|
|
49
|
+
export function recomputeAuditGateStatus(sources: AuditTermSources, overrides: UnitGateOverrides): 'PASS' | 'FAIL' {
|
|
50
|
+
const outcome = evaluateGateTerms([...auditGateTerms(sources), ...decisionTerms(overrides)], trackGateRules(overrides.context));
|
|
51
|
+
return outcome.pass ? 'PASS' : 'FAIL';
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
const isObj = (v: unknown): v is Record<string, unknown> => !!v && typeof v === 'object' && !Array.isArray(v);
|
|
55
|
+
|
|
56
|
+
/**
|
|
57
|
+
* The term sources of a parsed audit.json. `strict` (a current report): every field must be there with
|
|
58
|
+
* its type, else null. Not strict (a legacy report, written before some fields): a missing field reads
|
|
59
|
+
* as "holds", so only a stored PASS that a present field contradicts can be caught.
|
|
60
|
+
*/
|
|
61
|
+
export function termSourcesOf(r: Record<string, unknown>, strict: boolean): AuditTermSources | null {
|
|
62
|
+
const verdict = (k: string): string | null | undefined => {
|
|
63
|
+
const v = r[k];
|
|
64
|
+
if (!isObj(v)) return strict ? null : undefined;
|
|
65
|
+
return typeof v.verdict === 'string' ? v.verdict : strict ? null : undefined;
|
|
66
|
+
};
|
|
67
|
+
const gaps = isObj(r.gate) && Array.isArray(r.gate.gaps) ? r.gate.gaps : strict ? null : [];
|
|
68
|
+
const depth = verdict('depth');
|
|
69
|
+
const claim = verdict('claim');
|
|
70
|
+
const spec = verdict('spec');
|
|
71
|
+
const flag = (k: string): boolean | null => (typeof r[k] === 'boolean' ? r[k] as boolean : strict ? null : false);
|
|
72
|
+
const taxonomyMismatch = flag('taxonomyMismatch');
|
|
73
|
+
const gateSensorError = flag('gateSensorError');
|
|
74
|
+
const reused = Array.isArray(r.reusedScenarioIds) ? r.reusedScenarioIds : strict ? null : [];
|
|
75
|
+
if ([gaps, depth, claim, spec, taxonomyMismatch, gateSensorError, reused].some((x) => x === null)) return null;
|
|
76
|
+
return {
|
|
77
|
+
gate: { gaps: gaps as unknown[] }, depth: { verdict: depth ?? 'pass' }, claim: { verdict: claim ?? 'pass' }, spec: { verdict: spec ?? 'pass' },
|
|
78
|
+
taxonomyMismatch: taxonomyMismatch as boolean, gateSensorError: gateSensorError as boolean, reusedScenarioIds: reused as unknown[],
|
|
79
|
+
};
|
|
80
|
+
}
|
|
@@ -0,0 +1,78 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The audit's side of the gate registry (OES-11.2/11.4): load the unit's gate overrides, hand the
|
|
3
|
+
* tracked rules to the sensors, evaluate the gateStatus conjunction, and produce the fields the
|
|
4
|
+
* report records so readers can verify it (takumi T3):
|
|
5
|
+
*
|
|
6
|
+
* gateOverrides[] every override touching the unit, applied / ineffective / rejected
|
|
7
|
+
* decisionsHash the overrides it was judged under (stale when they change)
|
|
8
|
+
* inputsHash .feature + spec + viewpoint + decisionsHash
|
|
9
|
+
* exitCode 0 on PASS, 2 on FAIL — what `sungen audit` exits with
|
|
10
|
+
*
|
|
11
|
+
* The decision layer adds three hard terms: a rejected override (gate.override-rejected) and a
|
|
12
|
+
* decision file in the unit's reach that does not verify (decision.id-reuse / decision.mutated).
|
|
13
|
+
* With no decision at all none of this changes an outcome.
|
|
14
|
+
*/
|
|
15
|
+
import * as path from 'path';
|
|
16
|
+
import { projectRootFromScreenDir } from '../intent';
|
|
17
|
+
import { auditInputsHash } from './audit-input-hash';
|
|
18
|
+
import { evaluateGateTerms, ruleVerdict, SensorVerdict } from './gate-terms';
|
|
19
|
+
import { auditGateTerms, AuditTermSources, decisionTerms } from './audit-gate-terms';
|
|
20
|
+
import { GateOverrideReport, gateOverrideReports } from './gate-override-report';
|
|
21
|
+
import { GateRules, TrackedGateRules, trackGateRules } from './resolve-rule';
|
|
22
|
+
import { loadUnitGateOverrides, unitScopeOfDir, UnitGateOverrides } from './unit-gate-overrides';
|
|
23
|
+
|
|
24
|
+
export const AUDIT_EXIT_CODE = { PASS: 0, FAIL: 2 } as const;
|
|
25
|
+
|
|
26
|
+
export interface AuditGateContext {
|
|
27
|
+
projectRoot: string;
|
|
28
|
+
overrides: UnitGateOverrides;
|
|
29
|
+
rules: TrackedGateRules;
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
export function auditGateContext(unitDir: string): AuditGateContext {
|
|
33
|
+
const projectRoot = projectRootFromScreenDir(unitDir);
|
|
34
|
+
const overrides = loadUnitGateOverrides(projectRoot, unitScopeOfDir(projectRoot, unitDir));
|
|
35
|
+
return { projectRoot, overrides, rules: trackGateRules(overrides.context) };
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
/** A depth verdict a capability computed with the catalog alone, re-levelled for this unit's rules. */
|
|
39
|
+
export function relevelProvidedDepth<T extends { verdict: SensorVerdict }>(depth: T, rules: GateRules): T {
|
|
40
|
+
if (depth.verdict !== 'fail') return depth;
|
|
41
|
+
const verdict = ruleVerdict(rules, 'audit.assertion-depth', true, false);
|
|
42
|
+
return verdict === 'fail' ? depth : { ...depth, verdict };
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
/** Findings for the decision layer — empty when the unit has no override and no bad decision file. */
|
|
46
|
+
export function decisionFindings(ctx: AuditGateContext): string[] {
|
|
47
|
+
const o = ctx.overrides;
|
|
48
|
+
const rel = (p: string): string => path.relative(ctx.projectRoot, p).split(path.sep).join('/');
|
|
49
|
+
return [
|
|
50
|
+
...o.rejected.map((r) => `GATE-OVERRIDE-REJECTED (gate.override-rejected): ${r.decisionId} in ${r.path} cannot apply — ${r.reason}. Record a valid override that supersedes it.`),
|
|
51
|
+
...o.ineffective.map((r) => `GATE-OVERRIDE-INEFFECTIVE: ${r.decisionId} (${r.rule ?? '?'}) in ${r.path} relaxes nothing — ${r.reason}. Record it with \`sungen journey --decide gate-override\` on a terminal.`),
|
|
52
|
+
...o.problems.map((p) => `${p.code === 'id-mismatch' ? 'DECISION-ID-REUSED (decision.id-reuse)' : 'DECISION-UNVERIFIABLE (decision.mutated)'}: ${rel(p.path)} — ${p.message}. Restore it from git; change a decision by recording a superseding one.`),
|
|
53
|
+
...o.inForce.map((x) => `GATE-OVERRIDDEN: ${x.rule} is ${x.severity} for ${x.scope} (catalog level lowered by ${x.decisionId}, recorded by ${x.by}) — ${x.reason}`),
|
|
54
|
+
// Review H1: it counts here so its author sees the effect; release refuses it until reviewed.
|
|
55
|
+
...o.inForce.filter((x) => x.committed === false).map((x) => `GATE-OVERRIDE-UNCOMMITTED: ${x.decisionId} (${x.rule}) is not in git HEAD — it counts in this audit, but \`sungen gate --phase deliver\` and \`sungen delivery\` refuse it (decision.in-run) until it is committed through review.`),
|
|
56
|
+
];
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
export interface AuditGateVerdict {
|
|
60
|
+
gateStatus: 'PASS' | 'FAIL';
|
|
61
|
+
gateOverrides: GateOverrideReport[];
|
|
62
|
+
decisionsHash: string;
|
|
63
|
+
inputsHash: string;
|
|
64
|
+
exitCode: number;
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
/** Evaluate the gateStatus conjunction (audit-gate-terms.ts, shared with the reader) and the verifiable fields. */
|
|
68
|
+
export function auditGateVerdict(ctx: AuditGateContext, sources: AuditTermSources, unitDir: string, unitName: string): AuditGateVerdict {
|
|
69
|
+
const outcome = evaluateGateTerms([...auditGateTerms(sources), ...decisionTerms(ctx.overrides)], ctx.rules);
|
|
70
|
+
const gateStatus = outcome.pass ? 'PASS' : 'FAIL';
|
|
71
|
+
return {
|
|
72
|
+
gateStatus,
|
|
73
|
+
gateOverrides: gateOverrideReports(ctx.overrides, ctx.rules.triggered()),
|
|
74
|
+
decisionsHash: ctx.overrides.decisionsHash,
|
|
75
|
+
inputsHash: auditInputsHash(unitDir, unitName, ctx.overrides.decisionsHash),
|
|
76
|
+
exitCode: AUDIT_EXIT_CODE[gateStatus],
|
|
77
|
+
};
|
|
78
|
+
}
|
|
@@ -0,0 +1,55 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* `inputsHash` of an audit (OES-11.4, takumi T3): the hash of what the audit judged plus the
|
|
3
|
+
* `decisionsHash` it was judged under. A reader recomputes it; any difference means the report no
|
|
4
|
+
* longer describes the unit, so it counts as never audited.
|
|
5
|
+
*
|
|
6
|
+
* It is ANTI-STALENESS, NOT ANTI-FORGERY: anyone who can write audit.json can also compute this hash
|
|
7
|
+
* (the function is public). It catches a report that outlived its inputs; verdicts built by code and
|
|
8
|
+
* the git-HEAD checks on decisions are what stand against a forger.
|
|
9
|
+
*
|
|
10
|
+
* Inputs — every file whose change can move a verdict:
|
|
11
|
+
* the unit's .feature files · requirements/spec.md · requirements/test-viewpoint.md ·
|
|
12
|
+
* requirements/flow-contract.yaml · requirements/spec_figma.md · requirements/ui-inventory.md ·
|
|
13
|
+
* requirements/ui/* · qa/context.md (intent focus → thresholds) · qa/capabilities.yaml (platform,
|
|
14
|
+
* enabled drivers → gate sensors)
|
|
15
|
+
* Not yet (phase 05 follow-ups): qa/defects/ and the sibling flows credited to a screen.
|
|
16
|
+
*/
|
|
17
|
+
import { createHash } from 'crypto';
|
|
18
|
+
import * as fs from 'fs';
|
|
19
|
+
import * as path from 'path';
|
|
20
|
+
import { canonicalJson } from '../../decisions/decision-identity';
|
|
21
|
+
import { projectRootFromScreenDir } from '../intent';
|
|
22
|
+
import { featureFilesFor } from '../unit-paths';
|
|
23
|
+
|
|
24
|
+
const sha256 = (s: string | Buffer): string => createHash('sha256').update(s).digest('hex');
|
|
25
|
+
|
|
26
|
+
function fileSha(file: string): string | null {
|
|
27
|
+
try { return sha256(fs.readFileSync(file)); } catch { return null; }
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
function filesIn(dir: string): string[] {
|
|
31
|
+
try {
|
|
32
|
+
return fs.readdirSync(dir, { withFileTypes: true }).filter((e) => e.isFile()).map((e) => path.join(dir, e.name)).sort();
|
|
33
|
+
} catch { return []; }
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
/** The audit's input files that exist, in a stable order. */
|
|
37
|
+
export function auditInputFiles(unitDir: string, unitName: string): string[] {
|
|
38
|
+
const req = path.join(unitDir, 'requirements');
|
|
39
|
+
const qa = path.join(projectRootFromScreenDir(unitDir), 'qa');
|
|
40
|
+
const candidates = [
|
|
41
|
+
...featureFilesFor(unitDir, unitName),
|
|
42
|
+
...['spec.md', 'test-viewpoint.md', 'flow-contract.yaml', 'spec_figma.md', 'ui-inventory.md'].map((f) => path.join(req, f)),
|
|
43
|
+
...filesIn(path.join(req, 'ui')),
|
|
44
|
+
path.join(qa, 'context.md'),
|
|
45
|
+
path.join(qa, 'capabilities.yaml'),
|
|
46
|
+
];
|
|
47
|
+
return candidates.filter((f) => fs.existsSync(f));
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
export function auditInputsHash(unitDir: string, unitName: string, decisionsHash: string): string {
|
|
51
|
+
const root = projectRootFromScreenDir(unitDir);
|
|
52
|
+
const inputs = auditInputFiles(unitDir, unitName)
|
|
53
|
+
.map((f) => ({ file: path.relative(root, f).split(path.sep).join('/'), sha: fileSha(f) }));
|
|
54
|
+
return sha256(canonicalJson({ schema: 2, inputs, decisionsHash }));
|
|
55
|
+
}
|
|
@@ -0,0 +1,173 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Reading `audit.json` back without trusting it (takumi T3, DOS-14.0, OES-11.4). The report is an
|
|
3
|
+
* OUTPUT of `sungen audit`; every reader (journey/gate, delivery, and `next` via this module) checks:
|
|
4
|
+
*
|
|
5
|
+
* schema a JSON object; only the keys the audit writes; known keys have their type
|
|
6
|
+
* status `gateStatus` is PASS|FAIL and agrees with the integer `exitCode` (0 | 2)
|
|
7
|
+
* inputs `inputsHash` equals the hash of today's inputs + decisions (audit-input-hash.ts)
|
|
8
|
+
* decisions `decisionsHash` equals today's — otherwise the audit is stale: re-audit
|
|
9
|
+
* verdict gateStatus re-evaluated from the report's own terms (audit-gate-terms.ts) under the
|
|
10
|
+
* overrides in force — a flipped gateStatus + exitCode is `verdict-mismatch` (tester m1)
|
|
11
|
+
* Anything else counts as NEVER AUDITED, not as clean; only an explicit `gateStatus: PASS` passes.
|
|
12
|
+
* These checks are anti-STALENESS and anti-casual-tamper, NOT anti-forgery: a writer of audit.json can
|
|
13
|
+
* recompute the hashes, and one who rewrites every finding, gap and verdict is out of scope here.
|
|
14
|
+
*
|
|
15
|
+
* Legacy (DOS-11.4): a report written before these fields existed carries none of exitCode /
|
|
16
|
+
* inputsHash / decisionsHash / gateOverrides. It is still read — with the note "legacy audit, inputs
|
|
17
|
+
* unverified" — while no gate-override can apply to the unit and no input file is newer than it
|
|
18
|
+
* (mtime, review M1); otherwise it is stale. A report carrying SOME of the fields is malformed.
|
|
19
|
+
*/
|
|
20
|
+
import * as fs from 'fs';
|
|
21
|
+
import * as path from 'path';
|
|
22
|
+
import { resolveUnitCandidates } from '../../cli/resolve-unit';
|
|
23
|
+
import { reportSlug } from '../unit-paths';
|
|
24
|
+
import { auditInputFiles, auditInputsHash } from './audit-input-hash';
|
|
25
|
+
import { AUDIT_EXIT_CODE } from './audit-gate-verdict';
|
|
26
|
+
import { recomputeAuditGateStatus, termSourcesOf } from './audit-gate-terms';
|
|
27
|
+
import { EMPTY_DECISIONS_HASH } from './decisions-hash';
|
|
28
|
+
import { loadUnitGateOverrides, unitScopeOfDir, UnitGateOverrides } from './unit-gate-overrides';
|
|
29
|
+
|
|
30
|
+
export type AuditState = 'absent' | 'verified' | 'legacy' | 'stale' | 'invalid';
|
|
31
|
+
export type AuditProblemCode = 'unreadable' | 'unknown-key' | 'bad-type' | 'partial-fields' | 'status-exit-mismatch'
|
|
32
|
+
| 'unit-unresolved' | 'decisions-changed' | 'inputs-changed' | 'legacy-with-overrides' | 'legacy-inputs-newer' | 'verdict-mismatch';
|
|
33
|
+
|
|
34
|
+
export interface AuditVerification {
|
|
35
|
+
state: AuditState;
|
|
36
|
+
problems: Array<{ code: AuditProblemCode; message: string }>;
|
|
37
|
+
/** Caveats on a report that counts (a legacy report's inputs are unverified). */
|
|
38
|
+
notes: string[];
|
|
39
|
+
/** The parsed report — only when it counts (verified or legacy). */
|
|
40
|
+
report: Record<string, unknown> | null;
|
|
41
|
+
path: string;
|
|
42
|
+
/** Today's decisionsHash for the unit (what a fresh audit would record). */
|
|
43
|
+
currentDecisionsHash: string;
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
export const LEGACY_AUDIT_NOTE = 'legacy audit, inputs unverified — written before inputsHash existed; re-run `sungen audit` to record it';
|
|
47
|
+
|
|
48
|
+
/** A legacy report is stale once an input it judged is newer than the report itself (mtime). */
|
|
49
|
+
function newerLegacyInput(reportFile: string, unitDir: string | null, unit: string): string | null {
|
|
50
|
+
if (!unitDir) return null;
|
|
51
|
+
const at = fs.statSync(reportFile).mtimeMs;
|
|
52
|
+
return auditInputFiles(unitDir, unit).find((f) => fs.statSync(f).mtimeMs > at) ?? null;
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
type JsonKind = 'string' | 'number' | 'boolean' | 'object' | 'array';
|
|
56
|
+
const OBJ = 'object' as const;
|
|
57
|
+
/** Every top-level key `runAudit` writes (tests/gates/audit-report-trust.run.ts keeps this in step). */
|
|
58
|
+
export const AUDIT_REPORT_KEYS: Record<string, JsonKind> = {
|
|
59
|
+
screen: 'string', scenarioCount: 'number', gate: OBJ, depth: OBJ, claim: OBJ, taxonomy: OBJ, balance: OBJ,
|
|
60
|
+
duplicates: OBJ, trace: OBJ, taxonomyMismatch: 'boolean', reusedScenarioIds: 'array', downstream: OBJ,
|
|
61
|
+
manualOracle: OBJ, automatableManual: OBJ, flowDepth: OBJ, oracle: OBJ, ledger: OBJ, viewpointBaseline: OBJ,
|
|
62
|
+
flowQuality: OBJ, flowInventory: OBJ, uiInventory: OBJ, calibration: OBJ, score: OBJ, gateStatus: 'string',
|
|
63
|
+
findings: 'array', intent: OBJ, provenance: OBJ, spec: OBJ,
|
|
64
|
+
gateOverrides: 'array', decisionsHash: 'string', inputsHash: 'string', exitCode: 'number', gateSensorError: 'boolean',
|
|
65
|
+
};
|
|
66
|
+
const P0_KEYS = ['exitCode', 'inputsHash', 'decisionsHash', 'gateOverrides', 'gateSensorError'];
|
|
67
|
+
const HASH_RE = /^[0-9a-f]{64}$/;
|
|
68
|
+
|
|
69
|
+
const kindOf = (v: unknown): JsonKind | 'null' => (v === null ? 'null' : Array.isArray(v) ? 'array' : typeof v as JsonKind);
|
|
70
|
+
|
|
71
|
+
/** Structural problems of a parsed report, independent of the unit's current inputs. */
|
|
72
|
+
function schemaProblems(r: Record<string, unknown>): AuditVerification['problems'] {
|
|
73
|
+
const out: AuditVerification['problems'] = [];
|
|
74
|
+
for (const [k, v] of Object.entries(r)) {
|
|
75
|
+
const want = AUDIT_REPORT_KEYS[k];
|
|
76
|
+
if (!want) out.push({ code: 'unknown-key', message: `unknown key "${k}" — the audit never writes it` });
|
|
77
|
+
else if (kindOf(v) !== want) out.push({ code: 'bad-type', message: `"${k}" must be ${want}, got ${kindOf(v)}` });
|
|
78
|
+
}
|
|
79
|
+
if (r.gateStatus !== 'PASS' && r.gateStatus !== 'FAIL') out.push({ code: 'bad-type', message: 'gateStatus must be PASS or FAIL' });
|
|
80
|
+
if (Array.isArray(r.findings) && !r.findings.every((f) => typeof f === 'string')) out.push({ code: 'bad-type', message: 'findings must be strings' });
|
|
81
|
+
const present = P0_KEYS.filter((k) => k in r);
|
|
82
|
+
if (present.length > 0 && present.length < P0_KEYS.length) {
|
|
83
|
+
out.push({ code: 'partial-fields', message: `carries ${present.join(', ')} but not ${P0_KEYS.filter((k) => !(k in r)).join(', ')}` });
|
|
84
|
+
}
|
|
85
|
+
if (present.length === P0_KEYS.length) {
|
|
86
|
+
if (!Number.isInteger(r.exitCode)) out.push({ code: 'bad-type', message: 'exitCode must be an integer' });
|
|
87
|
+
else if (r.gateStatus === 'PASS' || r.gateStatus === 'FAIL') {
|
|
88
|
+
if (r.exitCode !== AUDIT_EXIT_CODE[r.gateStatus]) out.push({ code: 'status-exit-mismatch', message: `gateStatus ${r.gateStatus} with exitCode ${r.exitCode} — a ${r.gateStatus} audit exits ${AUDIT_EXIT_CODE[r.gateStatus]}` });
|
|
89
|
+
}
|
|
90
|
+
for (const k of ['inputsHash', 'decisionsHash']) if (typeof r[k] !== 'string' || !HASH_RE.test(r[k] as string)) out.push({ code: 'bad-type', message: `${k} must be 64 hex characters` });
|
|
91
|
+
if (Array.isArray(r.gateOverrides) && !r.gateOverrides.every((g) => kindOf(g) === 'object')) out.push({ code: 'bad-type', message: 'gateOverrides must be objects' });
|
|
92
|
+
}
|
|
93
|
+
return out;
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
export function auditReportPath(projectRoot: string, unit: string): string {
|
|
97
|
+
return path.join(projectRoot, '.sungen', 'reports', `${reportSlug(unit)}-audit.json`);
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
/** Today's decisionsHash for a unit name — what `next` compares an audit with (phase 05). */
|
|
101
|
+
export function currentDecisionsHash(projectRoot: string, unit: string): string {
|
|
102
|
+
const found = resolveUnitCandidates(unit, projectRoot);
|
|
103
|
+
const scope = found.length === 1 ? unitScopeOfDir(projectRoot, found[0].dir) : null;
|
|
104
|
+
return loadUnitGateOverrides(projectRoot, scope).decisionsHash;
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
export function verifyAuditReport(projectRoot: string, unit: string): AuditVerification {
|
|
108
|
+
const file = auditReportPath(projectRoot, unit);
|
|
109
|
+
const found = resolveUnitCandidates(unit, projectRoot);
|
|
110
|
+
const unitDir = found.length === 1 ? found[0].dir : null;
|
|
111
|
+
const overrides = loadUnitGateOverrides(projectRoot, unitDir ? unitScopeOfDir(projectRoot, unitDir) : null);
|
|
112
|
+
const current = overrides.decisionsHash;
|
|
113
|
+
const result = (state: AuditState, problems: AuditVerification['problems'], report: Record<string, unknown> | null = null, notes: string[] = []): AuditVerification =>
|
|
114
|
+
({ state, problems, notes, report, path: file, currentDecisionsHash: current });
|
|
115
|
+
|
|
116
|
+
if (!fs.existsSync(file)) return result('absent', []);
|
|
117
|
+
let parsed: unknown;
|
|
118
|
+
try { parsed = JSON.parse(fs.readFileSync(file, 'utf8').replace(/^/, '')); } catch (err) {
|
|
119
|
+
return result('invalid', [{ code: 'unreadable', message: err instanceof Error ? err.message : String(err) }]);
|
|
120
|
+
}
|
|
121
|
+
if (kindOf(parsed) !== 'object') return result('invalid', [{ code: 'unreadable', message: 'not a JSON object' }]);
|
|
122
|
+
const r = parsed as Record<string, unknown>;
|
|
123
|
+
const schema = schemaProblems(r);
|
|
124
|
+
if (schema.length) return result('invalid', schema);
|
|
125
|
+
|
|
126
|
+
if (!P0_KEYS.some((k) => k in r)) {
|
|
127
|
+
if (current !== EMPTY_DECISIONS_HASH) {
|
|
128
|
+
return result('stale', [{ code: 'legacy-with-overrides', message: 'written by an earlier sungen (no decisionsHash) and a gate-override now applies to this unit' }]);
|
|
129
|
+
}
|
|
130
|
+
const newer = newerLegacyInput(file, unitDir, unit);
|
|
131
|
+
if (newer) return result('stale', [{ code: 'legacy-inputs-newer', message: `written by an earlier sungen and ${path.basename(newer)} changed after it` }]);
|
|
132
|
+
// A legacy report predates gateSensorError, so only a stored PASS its own fields contradict is caught.
|
|
133
|
+
const sources = termSourcesOf(r, false);
|
|
134
|
+
if (r.gateStatus === 'PASS' && sources && recomputeAuditGateStatus(sources, overrides) === 'FAIL') return result('invalid', [verdictMismatch(r)]);
|
|
135
|
+
return result('legacy', [], r, [LEGACY_AUDIT_NOTE]);
|
|
136
|
+
}
|
|
137
|
+
const stale: AuditVerification['problems'] = [];
|
|
138
|
+
if (r.decisionsHash !== current) stale.push({ code: 'decisions-changed', message: 'the gate-override decisions changed since this audit' });
|
|
139
|
+
if (!unitDir) return result('invalid', [{ code: 'unit-unresolved', message: `"${unit}" does not name exactly one unit, so the audit's inputs cannot be checked` }]);
|
|
140
|
+
if (r.inputsHash !== auditInputsHash(unitDir, unit, r.decisionsHash as string)) {
|
|
141
|
+
stale.push({ code: 'inputs-changed', message: 'an input the audit judged (.feature, spec, viewpoint, context, design) changed since this audit' });
|
|
142
|
+
}
|
|
143
|
+
if (stale.length) return result('stale', stale);
|
|
144
|
+
const tampered = verdictProblem(r, overrides);
|
|
145
|
+
return tampered ? result('invalid', [tampered]) : result('verified', [], r);
|
|
146
|
+
}
|
|
147
|
+
|
|
148
|
+
const verdictMismatch = (r: Record<string, unknown>): AuditVerification['problems'][number] => ({
|
|
149
|
+
code: 'verdict-mismatch',
|
|
150
|
+
message: `verdict mismatch — gateStatus ${String(r.gateStatus)} is not what the report's own findings produce under today's overrides (edited by hand)`,
|
|
151
|
+
});
|
|
152
|
+
|
|
153
|
+
/** A current report re-evaluated from its own terms (audit-gate-terms.ts): missing terms or another verdict = not trusted. */
|
|
154
|
+
function verdictProblem(r: Record<string, unknown>, overrides: UnitGateOverrides): AuditVerification['problems'][number] | null {
|
|
155
|
+
const sources = termSourcesOf(r, true);
|
|
156
|
+
if (!sources) return { code: 'bad-type', message: 'the gate terms (gate.gaps, depth/claim/spec verdicts, taxonomyMismatch, gateSensorError, reusedScenarioIds) are incomplete' };
|
|
157
|
+
return recomputeAuditGateStatus(sources, overrides) === r.gateStatus ? null : verdictMismatch(r);
|
|
158
|
+
}
|
|
159
|
+
|
|
160
|
+
/** Whether the report may be used at all (verified, or a legacy report no override touches). */
|
|
161
|
+
export const auditCounts = (v: AuditVerification): boolean => v.state === 'verified' || v.state === 'legacy';
|
|
162
|
+
|
|
163
|
+
/** Only an explicit `gateStatus: PASS` on a report that counts passes; a score never does. */
|
|
164
|
+
export const auditPassed = (v: AuditVerification): boolean => auditCounts(v) && v.report?.gateStatus === 'PASS';
|
|
165
|
+
|
|
166
|
+
/** One line saying why the report does not count, for a needs-you item or a blocker. */
|
|
167
|
+
export function auditNotCountedReason(v: AuditVerification, unit: string): string {
|
|
168
|
+
const why = v.problems.map((p) => p.message).join('; ');
|
|
169
|
+
const rerun = `re-run \`sungen audit --screen ${unit}\``;
|
|
170
|
+
if (v.state === 'stale') return `Audit report is stale (${why}) — ${rerun}.`;
|
|
171
|
+
if (v.state === 'invalid') return `Audit report is not trusted (${why}) — treated as never audited; ${rerun}.`;
|
|
172
|
+
return `No audit report yet — ${rerun}.`;
|
|
173
|
+
}
|
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* `decisionsHash` (OES-11.4, red-team #12): a fingerprint of the decisions that can change a unit's
|
|
3
|
+
* gate outcome. The audit records it; `next`, `gate` and `delivery` recompute it — a mismatch means
|
|
4
|
+
* the audit was measured under other decisions: stale, re-audit.
|
|
5
|
+
*
|
|
6
|
+
* What it covers, for one unit — the project's `qa/decisions/` plus the unit's own directory:
|
|
7
|
+
* - every `gate-override` entry that can apply to the unit (accepted, ineffective, rejected,
|
|
8
|
+
* superseded alike), by path and entry_hash
|
|
9
|
+
* - every file there that does not verify (unreadable, invalid, id or hash mismatch), by path,
|
|
10
|
+
* problem and content — it may be an override nobody can read
|
|
11
|
+
* What it does NOT cover: waivers. A waiver is bound to the audit by `evidence_hash = hash of the
|
|
12
|
+
* audit report`; folding it in here would make every waiver stale the audit that it waives.
|
|
13
|
+
*/
|
|
14
|
+
import { createHash } from 'crypto';
|
|
15
|
+
import * as fs from 'fs';
|
|
16
|
+
import * as path from 'path';
|
|
17
|
+
import { canonicalJson } from '../../decisions/decision-identity';
|
|
18
|
+
import { DecisionProblem } from '../../decisions/decision-file';
|
|
19
|
+
|
|
20
|
+
export interface DecisionsHashItem {
|
|
21
|
+
path: string;
|
|
22
|
+
entryHash?: string;
|
|
23
|
+
problem?: string;
|
|
24
|
+
contentSha?: string;
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
const sha256 = (s: string | Buffer): string => createHash('sha256').update(s).digest('hex');
|
|
28
|
+
|
|
29
|
+
export function computeDecisionsHash(items: DecisionsHashItem[]): string {
|
|
30
|
+
const sorted = [...items].sort((a, b) => (a.path < b.path ? -1 : a.path > b.path ? 1 : 0));
|
|
31
|
+
return sha256(canonicalJson({ schema: 1, decisions: sorted }));
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
/** The hash of a unit with no decision that can touch its gates. */
|
|
35
|
+
export const EMPTY_DECISIONS_HASH = computeDecisionsHash([]);
|
|
36
|
+
|
|
37
|
+
/** A problem file as a hash item: its content counts, so editing it re-stales the audit. */
|
|
38
|
+
export function problemHashItem(projectRoot: string, p: DecisionProblem): DecisionsHashItem {
|
|
39
|
+
let contentSha = 'unreadable';
|
|
40
|
+
try { contentSha = sha256(fs.readFileSync(p.path)); } catch { /* gone or unreadable: the marker stands */ }
|
|
41
|
+
return { path: path.relative(projectRoot, p.path).split(path.sep).join('/'), problem: p.code, contentSha };
|
|
42
|
+
}
|