@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,74 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* One parsed Gherkin step → one contract step (DOS-14.1). The step was already parsed by the
|
|
3
|
+
* existing GherkinParser (ParsedStep: parent scope, state clause, data table, nth); this re-reads
|
|
4
|
+
* its sentence with references and values masked, as the StepMapper's matcher does
|
|
5
|
+
* (`maskRefsForMatching`) so a NAME can never read as grammar — and keeps what it masked:
|
|
6
|
+
*
|
|
7
|
+
* `User see [Status] text with {{s}} in [Job Row] row with {{t}}`
|
|
8
|
+
* → {kind: assert, verb: 'see [] text with {}', target: {ref: 'Status', parent: 'row [Job Row] with {{t}}'},
|
|
9
|
+
* matcher: 'equals', expected: '{{s}}'}
|
|
10
|
+
*
|
|
11
|
+
* The verb keeps every word and number of the sentence, so two steps with equal contract steps are
|
|
12
|
+
* the same sentence up to case and spacing: an exact match is never a guess. `{{var}}` stays a
|
|
13
|
+
* variable (never resolved), a literal loses only its quotes.
|
|
14
|
+
*/
|
|
15
|
+
import type { ParsedStep } from '../generators/gherkin-parser';
|
|
16
|
+
import { extractParentScopeClause } from '../generators/gherkin-parser/parent-scope-clause';
|
|
17
|
+
import type { ContractStep, ContractTarget } from './contract-schema';
|
|
18
|
+
import { assertShapeOf } from './assertion-matcher-vocabulary';
|
|
19
|
+
|
|
20
|
+
export type StepBucket = 'given' | 'when' | 'then';
|
|
21
|
+
|
|
22
|
+
/** Quoted literal · `{{var}}` · `<outline placeholder>` · `[Reference]`, left to right. */
|
|
23
|
+
const TOKEN = /"[^"]*"|\{\{[^}]*\}\}|<[^>\s][^>]*>|\[[^\]]*\]/g;
|
|
24
|
+
|
|
25
|
+
function parentOf(text: string): { parent?: string; body: string } {
|
|
26
|
+
const { clause } = extractParentScopeClause(text);
|
|
27
|
+
if (!clause) return { body: text };
|
|
28
|
+
const tail = clause.valueRef !== undefined ? ` with {{${clause.valueRef}}}` : clause.valueText !== undefined ? ` with "${clause.valueText}"` : '';
|
|
29
|
+
return { parent: `${clause.type} [${clause.ref.trim()}]${tail}`, body: clause.remainingText };
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
export function normalizeContractStep(step: ParsedStep, bucket: StepBucket): ContractStep {
|
|
33
|
+
const { parent, body } = parentOf(step.text);
|
|
34
|
+
const refs: string[] = [];
|
|
35
|
+
const values: string[] = [];
|
|
36
|
+
const masked = body.replace(TOKEN, (tok) => {
|
|
37
|
+
if (tok.startsWith('[')) { refs.push(tok.slice(1, -1).trim()); return '[]'; }
|
|
38
|
+
values.push(tok.startsWith('"') ? tok.slice(1, -1) : tok);
|
|
39
|
+
return '{}';
|
|
40
|
+
});
|
|
41
|
+
const verb = masked.replace(/^\s*user\s+/i, '').replace(/\s+/g, ' ').replace(/\s*[.:]\s*$/, '').trim().toLowerCase();
|
|
42
|
+
const kind = bucket === 'then' ? 'assert' : 'action';
|
|
43
|
+
|
|
44
|
+
let target: ContractTarget | undefined;
|
|
45
|
+
if (refs.length) target = { ref: refs[0], ...(refs.length > 1 ? { also: refs.slice(1) } : {}) };
|
|
46
|
+
else if (kind === 'assert' && /^expect\b/.test(verb) && values.length) target = { ref: values.shift()! };
|
|
47
|
+
if (target && parent) target.parent = parent;
|
|
48
|
+
if (target && step.nth && step.nth > 0) target.nth = step.nth;
|
|
49
|
+
|
|
50
|
+
const joined = values.length ? values.join(' | ') : undefined;
|
|
51
|
+
const table = step.dataTable ? [step.dataTable.headers, ...step.dataTable.rows.map((r) => r.cells)] : undefined;
|
|
52
|
+
const out: ContractStep = { kind, verb };
|
|
53
|
+
if (target) out.target = target;
|
|
54
|
+
if (kind === 'action') {
|
|
55
|
+
if (joined !== undefined) out.value = joined;
|
|
56
|
+
} else {
|
|
57
|
+
const shape = assertShapeOf(verb, step.state);
|
|
58
|
+
out.matcher = shape.matcher;
|
|
59
|
+
if (joined !== undefined) out.expected = joined;
|
|
60
|
+
if (shape.negated) out.negated = true;
|
|
61
|
+
}
|
|
62
|
+
if (table) out.table = table;
|
|
63
|
+
return out;
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
/** Given/When/Then for every step of one block, And/But inheriting the last primary keyword. */
|
|
67
|
+
export function bucketsOf(steps: ParsedStep[]): StepBucket[] {
|
|
68
|
+
let last: StepBucket = 'given';
|
|
69
|
+
return steps.map((s) => {
|
|
70
|
+
const kw = s.keyword.trim().toLowerCase();
|
|
71
|
+
if (kw === 'given' || kw === 'when' || kw === 'then') last = kw;
|
|
72
|
+
return last;
|
|
73
|
+
});
|
|
74
|
+
}
|
|
@@ -0,0 +1,93 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* A unit's `.feature` files → its contract scenarios (DOS-14.1, 14.2), through the existing
|
|
3
|
+
* GherkinParser — no second parser.
|
|
4
|
+
*
|
|
5
|
+
* which scenarios every automated one: not `@manual` (scenario or feature), not `@deferred:flow`,
|
|
6
|
+
* not a `@steps:` block or a `@beforeAll/@afterEach/@afterAll` hook
|
|
7
|
+
* steps Background + the `@extend:` base's Given/When (the compiler's own rule) + its own
|
|
8
|
+
* id the title's leading ID (`VP-LOGIN-001`), as the harness reads it; a title with no
|
|
9
|
+
* ID is locked by its name, so renaming it reads as removing it
|
|
10
|
+
* implementation `@free` → free, `@impl:code` → impl:code, otherwise compiled — scenario or feature tag
|
|
11
|
+
* hash contractHash(implementation, steps); the scenario name is not in it
|
|
12
|
+
*
|
|
13
|
+
* A file that does not parse throws: an unreadable contract is never an empty one.
|
|
14
|
+
*/
|
|
15
|
+
import * as fs from 'fs';
|
|
16
|
+
import * as path from 'path';
|
|
17
|
+
import { GherkinParser, ParsedScenario, ParsedStep } from '../generators/gherkin-parser';
|
|
18
|
+
import { isIdLike } from '../harness/parse';
|
|
19
|
+
import { featureFilesFor } from '../harness/unit-paths';
|
|
20
|
+
import { contractHash } from './contract-hash';
|
|
21
|
+
import type { ContractScenario, ContractStep, Implementation } from './contract-schema';
|
|
22
|
+
import { bucketsOf, normalizeContractStep } from './normalize-contract-step';
|
|
23
|
+
|
|
24
|
+
const EXCLUDED = (tags: string[]): boolean => tags.some((t) => /^@manual\b/i.test(t) || /^@deferred:flow$/i.test(t));
|
|
25
|
+
|
|
26
|
+
function implementationOf(tags: string[]): Implementation {
|
|
27
|
+
if (tags.some((t) => /^@free$/i.test(t))) return 'free';
|
|
28
|
+
if (tags.some((t) => /^@impl:code$/i.test(t))) return 'impl:code';
|
|
29
|
+
return 'compiled';
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
function idOf(name: string): { id: string; hasId: boolean } {
|
|
33
|
+
const lead = name.match(/^\s*([A-Za-z][A-Za-z0-9.-]*)/)?.[1];
|
|
34
|
+
return lead && isIdLike(lead) ? { id: lead.toUpperCase(), hasId: true } : { id: name.replace(/\s+/g, ' ').trim(), hasId: false };
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
/** Only Given→When of an `@extend` base: its Then (and the And/But after it) stay with the base. */
|
|
38
|
+
const extendPart = (steps: ParsedStep[]): ParsedStep[] => steps.filter((_, i) => bucketsOf(steps)[i] !== 'then');
|
|
39
|
+
|
|
40
|
+
const normalizeBlock = (steps: ParsedStep[]): ContractStep[] => {
|
|
41
|
+
const buckets = bucketsOf(steps);
|
|
42
|
+
return steps.map((s, i) => normalizeContractStep(s, buckets[i]));
|
|
43
|
+
};
|
|
44
|
+
|
|
45
|
+
/** 1-based line of each step, searched in order from line index `from` — for messages only (0 = not found). */
|
|
46
|
+
function stepLines(lines: string[], from: number, steps: ParsedStep[]): number[] {
|
|
47
|
+
let at = Math.max(from, 0);
|
|
48
|
+
return steps.map((s) => {
|
|
49
|
+
const i = lines.findIndex((l, n) => n >= at && l.trim().endsWith(s.text.trim()));
|
|
50
|
+
if (i >= 0) at = i + 1;
|
|
51
|
+
return i + 1;
|
|
52
|
+
});
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
const HEADER = /^\s*(?:Scenario|Scenario Outline|Scenario Template|Example):/;
|
|
56
|
+
const OUTLINE = /^\s*Scenario (?:Outline|Template):/;
|
|
57
|
+
const headerLine = (lines: string[], name: string): number => lines.findIndex((l) => HEADER.test(l) && l.includes(name.trim()));
|
|
58
|
+
|
|
59
|
+
export function scenariosOfFeatureFile(file: string): ContractScenario[] {
|
|
60
|
+
const feature = new GherkinParser().parseFeatureFile(file);
|
|
61
|
+
const lines = fs.readFileSync(file, 'utf8').split(/\r?\n/);
|
|
62
|
+
const base = new Map<string, ParsedScenario>();
|
|
63
|
+
for (const sc of feature.scenarios) if (sc.stepsName) base.set(sc.stepsName, sc);
|
|
64
|
+
const background = feature.background?.steps ?? [];
|
|
65
|
+
const out: ContractScenario[] = [];
|
|
66
|
+
for (const sc of feature.scenarios) {
|
|
67
|
+
const tags = [...(feature.tags ?? []), ...(sc.tags ?? [])];
|
|
68
|
+
if (sc.stepsName || sc.hookType || EXCLUDED(tags)) continue;
|
|
69
|
+
const baseScenario = sc.extendsName ? base.get(sc.extendsName) : undefined;
|
|
70
|
+
const extended = extendPart(baseScenario?.steps ?? []);
|
|
71
|
+
const steps = [...normalizeBlock(background), ...normalizeBlock(extended), ...normalizeBlock(sc.steps)];
|
|
72
|
+
const implementation = implementationOf(tags);
|
|
73
|
+
const { id, hasId } = idOf(sc.name);
|
|
74
|
+
const header = headerLine(lines, sc.name);
|
|
75
|
+
const sourceLines = [
|
|
76
|
+
...stepLines(lines, 0, background),
|
|
77
|
+
...stepLines(lines, baseScenario ? headerLine(lines, baseScenario.name) : 0, extended),
|
|
78
|
+
...stepLines(lines, header, sc.steps),
|
|
79
|
+
];
|
|
80
|
+
const outline = header >= 0 && OUTLINE.test(lines[header]);
|
|
81
|
+
out.push({ id, implementation, steps, hash: contractHash(implementation, steps), source: { file, name: sc.name, hasId, lines: sourceLines, outline } });
|
|
82
|
+
}
|
|
83
|
+
return out;
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
/** Every contract scenario of a unit, in `featureFilesFor` order. Missing feature files contribute nothing. */
|
|
87
|
+
export function loadContractScenarios(unitDir: string, unit: string): ContractScenario[] {
|
|
88
|
+
return featureFilesFor(unitDir, unit).filter((f) => fs.existsSync(f)).flatMap(scenariosOfFeatureFile);
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
/** Feature files that make up a unit's contract input (for the inputs hash). */
|
|
92
|
+
export const contractFeatureFiles = (unitDir: string, unit: string): string[] =>
|
|
93
|
+
featureFilesFor(unitDir, unit).filter((f) => fs.existsSync(f)).map((f) => path.resolve(f));
|
|
@@ -0,0 +1,80 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* How a changed assertion compares to the approved one on the same target (DOS-14.4).
|
|
3
|
+
*
|
|
4
|
+
* Every rule is written for POSITIVE checks, from the approved check A to the new check B:
|
|
5
|
+
* weakened expected value dropped · a data-table row removed or a cell changed · a shorter
|
|
6
|
+
* literal under `contains` · a lower rung on the ladder (conservative)
|
|
7
|
+
* strengthened B implies A, with the same expected value, on the same element (IMPLICATION, never
|
|
8
|
+
* rung): equals ⇒ contains · all:X ⇒ X · url ⇒ url-contains · title ⇒ title-contains
|
|
9
|
+
* review anything else — it blocks delivery until QA re-approves it
|
|
10
|
+
* Nothing implies `visible`: a text or count check passes on a hidden element, and a state (enabled,
|
|
11
|
+
* checked, empty, focused, selected, loading, sorted…) says nothing about visibility either.
|
|
12
|
+
*
|
|
13
|
+
* Under a NEGATION the direction reverses — `not P` proves `not Q` exactly when Q ⇒ P — so the same
|
|
14
|
+
* rules run with A and B swapped: `does not see [Toast]` → `does not see [Toast] with text {{x}}` is
|
|
15
|
+
* a weakening (a visible toast with other text now passes). A negation added or removed is a flip.
|
|
16
|
+
*/
|
|
17
|
+
import { sameJson } from './contract-hash';
|
|
18
|
+
import type { ContractStep } from './contract-schema';
|
|
19
|
+
import { matcherStrength } from './assertion-matcher-vocabulary';
|
|
20
|
+
|
|
21
|
+
export { assertShapeOf, matcherStrength } from './assertion-matcher-vocabulary';
|
|
22
|
+
export type { AssertShape } from './assertion-matcher-vocabulary';
|
|
23
|
+
|
|
24
|
+
const IMPLIES: Record<string, readonly string[]> = { equals: ['contains'], url: ['url-contains'], title: ['title-contains'] };
|
|
25
|
+
|
|
26
|
+
/** Does a passing `now` check prove the `was` check (both positive)? */
|
|
27
|
+
export function impliesMatcher(now: string | undefined, was: string | undefined): boolean {
|
|
28
|
+
if (!now || !was || now === was) return false;
|
|
29
|
+
if (was.startsWith('all:') && !now.startsWith('all:')) return false;
|
|
30
|
+
const n = now.replace(/^all:/, '');
|
|
31
|
+
const w = was.replace(/^all:/, '');
|
|
32
|
+
return n === w || (IMPLIES[n] ?? []).includes(w);
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
const CONNECTORS = new Set(['with', 'has', 'have', 'contains', 'contain', 'includes', 'include', 'is', 'matches', 'in', 'to', 'and']);
|
|
36
|
+
const elementType = (verb: string): string => {
|
|
37
|
+
const m = verb.match(/\[\] ([\w-]+)/);
|
|
38
|
+
return m && !CONNECTORS.has(m[1]) ? m[1] : '';
|
|
39
|
+
};
|
|
40
|
+
const isLiteral = (v: string | undefined): v is string => v !== undefined && !/\{\{|<[^>]+>/.test(v);
|
|
41
|
+
const baseOf = (m: string | undefined): string => (m ?? '').replace(/^all:/, '');
|
|
42
|
+
const rowsOf = (t: string[][] | undefined): string[] => (t ?? []).map((r) => JSON.stringify(r));
|
|
43
|
+
const TEXT = new Set(['equals', 'contains']);
|
|
44
|
+
|
|
45
|
+
/** x ⇒ y for two positive LITERAL text checks (equals / contains); null when they are not both that. */
|
|
46
|
+
function literalImplies(x: ContractStep, y: ContractStep): boolean | null {
|
|
47
|
+
const bx = baseOf(x.matcher);
|
|
48
|
+
const by = baseOf(y.matcher);
|
|
49
|
+
if (!isLiteral(x.expected) || !isLiteral(y.expected) || !TEXT.has(bx) || !TEXT.has(by)) return null;
|
|
50
|
+
return by === 'equals' ? bx === 'equals' && x.expected === y.expected : x.expected.includes(y.expected);
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
export type AssertComparison = { class: 'strengthened' | 'review' | 'oracle-weakened'; detail: string };
|
|
54
|
+
|
|
55
|
+
const show = (s: ContractStep): string => `${s.negated ? 'not ' : ''}${s.matcher ?? '?'}${s.expected !== undefined ? ` "${s.expected}"` : ''}`;
|
|
56
|
+
|
|
57
|
+
/** Two assertions on the same target that are NOT identical. */
|
|
58
|
+
export function compareAsserts(locked: ContractStep, current: ContractStep): AssertComparison {
|
|
59
|
+
const change = `${show(locked)} → ${show(current)}`;
|
|
60
|
+
const weak = (why: string): AssertComparison => ({ class: 'oracle-weakened', detail: `${why}: ${change}` });
|
|
61
|
+
const review = (why: string): AssertComparison => ({ class: 'review', detail: `${why}: ${change}` });
|
|
62
|
+
if (!!locked.negated !== !!current.negated) return weak('negation flipped');
|
|
63
|
+
const neg = locked.negated === true;
|
|
64
|
+
const [a, b] = neg ? [current, locked] : [locked, current];
|
|
65
|
+
const sa = matcherStrength(a.matcher);
|
|
66
|
+
const sb = matcherStrength(b.matcher);
|
|
67
|
+
if (sa === null || sb === null) return review(`the ladder cannot judge "${locked.verb}" → "${current.verb}"`);
|
|
68
|
+
if (a.expected !== undefined && b.expected === undefined) return weak(neg ? 'a value was added to a negated check' : 'expected value dropped');
|
|
69
|
+
if (a.table) {
|
|
70
|
+
const kept = new Set(rowsOf(b.table));
|
|
71
|
+
const lost = rowsOf(a.table).filter((r) => !kept.has(r)).length;
|
|
72
|
+
if (lost) return weak(`${lost} data-table row(s) ${neg ? 'added to a negated check' : 'removed or changed'}`);
|
|
73
|
+
}
|
|
74
|
+
if (literalImplies(a, b) === true && literalImplies(b, a) === false) return weak(neg ? 'a longer literal under a negated check' : 'a shorter literal under contains');
|
|
75
|
+
if (sb < sa) return weak('weaker check');
|
|
76
|
+
if (a.expected !== undefined && b.expected !== a.expected) return review('expected value changed');
|
|
77
|
+
const sameElement = sameJson(a.target, b.target) && elementType(a.verb) === elementType(b.verb) && rowsOf(a.table).length === rowsOf(b.table).length;
|
|
78
|
+
if (sameElement && impliesMatcher(b.matcher, a.matcher)) return { class: 'strengthened', detail: `stronger check: ${change}` };
|
|
79
|
+
return review('changed, and the new check does not prove the approved one');
|
|
80
|
+
}
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Which units' contracts hold a delivery back — for the Claude Code guard (OES-12.3, DOS-14.4).
|
|
3
|
+
*
|
|
4
|
+
* HEAD is read once (one `git ls-tree`, bounded by the caller's timeout). A unit with no lock in the
|
|
5
|
+
* working tree AND none in HEAD has no contract to break and is skipped without parsing anything.
|
|
6
|
+
* Every other unit gets its verdict rebuilt by buildContractVerdict — the same function `contract
|
|
7
|
+
* check` runs — and anything but `pass` blocks. Its trace part gets the guard's ~3 s at most:
|
|
8
|
+
* SUNGEN_TRACE_VERDICT_BUDGET_MS can shorten that here, never lengthen it (#926). A lock deleted from the working tree still counts
|
|
9
|
+
* while HEAD holds it. If git FAILS in a checkout that has .git while a delivered unit has a lock,
|
|
10
|
+
* this throws (GitUnavailableError): the guard asks rather than calling an unverifiable lock clean.
|
|
11
|
+
*/
|
|
12
|
+
import * as fs from 'fs';
|
|
13
|
+
import { buildContractVerdict, ContractUnit, VerdictValue } from './build-contract-verdict';
|
|
14
|
+
import { guardTraceBudgetMs } from './build-trace-conformance';
|
|
15
|
+
import { CLI_GIT_TIMEOUT_MS, GitUnavailableError, headSnapshot, posixRel } from './contract-git';
|
|
16
|
+
import { HARD_CLASSES } from './contract-schema';
|
|
17
|
+
import { lockPathFor } from './contracts-lock-file';
|
|
18
|
+
|
|
19
|
+
export interface ContractBlocker { unit: string; verdict: Exclude<VerdictValue, 'pass'>; headline: string }
|
|
20
|
+
|
|
21
|
+
export function contractBlockers(projectRoot: string, units: ContractUnit[], opts: { gitTimeoutMs?: number } = {}): ContractBlocker[] {
|
|
22
|
+
const unique = [...new Map(units.map((u) => [u.dir, u])).values()];
|
|
23
|
+
const snap = headSnapshot(projectRoot, opts.gitTimeoutMs ?? CLI_GIT_TIMEOUT_MS);
|
|
24
|
+
const hasWorkingLock = (u: ContractUnit): boolean => fs.existsSync(lockPathFor(u.dir));
|
|
25
|
+
if (snap.error && unique.some(hasWorkingLock)) throw new GitUnavailableError(snap.error);
|
|
26
|
+
const out: ContractBlocker[] = [];
|
|
27
|
+
for (const unit of unique) {
|
|
28
|
+
if (!hasWorkingLock(unit) && !snap.blobs?.has(posixRel(projectRoot, lockPathFor(unit.dir)))) continue;
|
|
29
|
+
const v = buildContractVerdict(projectRoot, unit, { snapshot: snap, traceBudgetMs: guardTraceBudgetMs() });
|
|
30
|
+
if (v.verdict === 'pass') continue;
|
|
31
|
+
const worst = v.scenarios.find((s) => HARD_CLASSES.has(s.class)) ?? v.scenarios.find((s) => s.class === 'hash-changed');
|
|
32
|
+
const headline = worst
|
|
33
|
+
? `${worst.id} ${worst.class}${worst.findings[0] ? ` (${worst.findings[0].detail})` : ''}`
|
|
34
|
+
: `${v.uncommitted.length} lock entr${v.uncommitted.length === 1 ? 'y is' : 'ies are'} not what QA approved and committed (${v.uncommitted[0]?.id} ${v.uncommitted[0]?.change})`;
|
|
35
|
+
out.push({ unit: unit.name, verdict: v.verdict, headline });
|
|
36
|
+
}
|
|
37
|
+
return out;
|
|
38
|
+
}
|
|
@@ -0,0 +1,110 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* What an approval writes, once a human has confirmed it (DOS-14.3, DOS-14.4, takumi T5):
|
|
3
|
+
*
|
|
4
|
+
* 1. the secret guard over the exact lock text — a hit writes nothing at all
|
|
5
|
+
* 2. a `contract-approval` decision listing every approved scenario id + hash (and the removals)
|
|
6
|
+
* 3. the lock: each approved entry `humanApproved: true`, `decisionId` = that decision, and
|
|
7
|
+
* `approvedAt` = its `valid_from` — the pair `contract check` cross-checks (M1)
|
|
8
|
+
*
|
|
9
|
+
* The decision goes first: a crash between the two leaves a record without a lock entry, never an
|
|
10
|
+
* entry pointing at nothing. Only `sungen contract approve` calls this after its TTY confirmation;
|
|
11
|
+
* the confirmation itself lives there (approve-contract.ts).
|
|
12
|
+
*
|
|
13
|
+
* `--accept-opaque` (#923, accept-opaque-steps.ts) writes the same kind of decision for ONE scenario,
|
|
14
|
+
* listing it at its hash WITH the opaque steps QA accepted (step + sha); its lock entry is rewritten to
|
|
15
|
+
* point at that decision and carries the same list, which verify-approval-decisions.ts cross-checks.
|
|
16
|
+
*/
|
|
17
|
+
import * as path from 'path';
|
|
18
|
+
import { appendDecision } from '../decisions/append-decision';
|
|
19
|
+
import { isValidScope } from '../decisions/decision-file';
|
|
20
|
+
import type { JsonValue } from '../decisions/decision-schema';
|
|
21
|
+
import { gitUserName } from '../decisions/decision-source';
|
|
22
|
+
import { AcceptedOpaqueStep, ContractLock, ContractScenario, LOCK_SCHEMA } from './contract-schema';
|
|
23
|
+
import { lockEntryFor, lockPathFor, serializeLock, writeLockFile } from './contracts-lock-file';
|
|
24
|
+
import { assertNoLockSecrets } from './lock-secret-guard';
|
|
25
|
+
|
|
26
|
+
export interface ApprovalWrite {
|
|
27
|
+
projectRoot: string;
|
|
28
|
+
unitDir: string;
|
|
29
|
+
unit: string;
|
|
30
|
+
approve: ContractScenario[];
|
|
31
|
+
removed: string[];
|
|
32
|
+
reason?: string;
|
|
33
|
+
lock: ContractLock | null;
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
export const DEFAULT_APPROVAL_REASON = 'QA reviewed the normalized Gherkin contract';
|
|
37
|
+
export const DEFAULT_OPAQUE_REASON = 'QA reviewed the opaque step(s) sungen cannot compare';
|
|
38
|
+
|
|
39
|
+
export interface OpaqueAcceptanceWrite {
|
|
40
|
+
projectRoot: string;
|
|
41
|
+
unitDir: string;
|
|
42
|
+
unit: string;
|
|
43
|
+
/** The scenario as it reads now — its hash equals its approved lock entry's. */
|
|
44
|
+
scenario: ContractScenario;
|
|
45
|
+
accepted: AcceptedOpaqueStep[];
|
|
46
|
+
reason?: string;
|
|
47
|
+
lock: ContractLock;
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
/** The decision scope of a unit directory (`screens/login`, `api/flows/pay`…). */
|
|
51
|
+
export function scopeOfUnitDir(projectRoot: string, unitDir: string): string {
|
|
52
|
+
const scope = path.relative(path.join(projectRoot, 'qa'), unitDir).split(path.sep).join('/');
|
|
53
|
+
if (!isValidScope(scope)) throw new Error(`${unitDir} is not a unit directory under qa/ (screens|flows|api/<unit>, api/flows/<flow>)`);
|
|
54
|
+
return scope;
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
export function nextLock(w: ApprovalWrite, decisionId: string, approvedAt: string): ContractLock {
|
|
58
|
+
const scenarios = { ...(w.lock?.scenarios ?? {}) };
|
|
59
|
+
for (const id of w.removed) delete scenarios[id];
|
|
60
|
+
for (const sc of w.approve) scenarios[sc.id] = lockEntryFor(sc, decisionId, approvedAt);
|
|
61
|
+
return { schema: LOCK_SCHEMA, scenarios };
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
/** Throws LockSecretError when the lock this approval would write carries a sensitive value. */
|
|
65
|
+
export function checkApprovalSecrets(w: ApprovalWrite): void {
|
|
66
|
+
assertNoLockSecrets(w.approve, serializeLock(nextLock(w, 'D-0000000000000000', new Date().toISOString())));
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
export function recordContractApproval(w: ApprovalWrite): { decisionId: string; lock: ContractLock } {
|
|
70
|
+
const scope = scopeOfUnitDir(w.projectRoot, w.unitDir);
|
|
71
|
+
checkApprovalSecrets(w);
|
|
72
|
+
const payload: Record<string, JsonValue> = {
|
|
73
|
+
unit: w.unit,
|
|
74
|
+
scenarios: w.approve.map((s) => ({ id: s.id, hash: s.hash, implementation: s.implementation })),
|
|
75
|
+
removed: w.removed,
|
|
76
|
+
};
|
|
77
|
+
const decision = appendDecision(w.projectRoot, scope, {
|
|
78
|
+
kind: 'contract-approval', anchors: [], visibility: 'internal', valid_from: new Date().toISOString(), payload,
|
|
79
|
+
text: `Approved the Test Contract of ${w.unit}: ${[...w.approve.map((s) => s.id), ...w.removed.map((id) => `-${id}`)].join(', ')}`,
|
|
80
|
+
reason: w.reason?.trim() || DEFAULT_APPROVAL_REASON,
|
|
81
|
+
source: { by: gitUserName(w.projectRoot), where: 'cli' },
|
|
82
|
+
});
|
|
83
|
+
const lock = nextLock(w, decision.entry.id, decision.entry.valid_from);
|
|
84
|
+
writeLockFile(lockPathFor(w.unitDir), lock);
|
|
85
|
+
return { decisionId: decision.entry.id, lock };
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
function lockWithAcceptance(w: OpaqueAcceptanceWrite, decisionId: string, approvedAt: string): ContractLock {
|
|
89
|
+
const entry = { ...lockEntryFor(w.scenario, decisionId, approvedAt), acceptedOpaque: w.accepted.map((a) => ({ step: a.step, sha: a.sha })) };
|
|
90
|
+
return { schema: LOCK_SCHEMA, scenarios: { ...w.lock.scenarios, [w.scenario.id]: entry } };
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
export function recordOpaqueAcceptance(w: OpaqueAcceptanceWrite): { decisionId: string; lock: ContractLock } {
|
|
94
|
+
const scope = scopeOfUnitDir(w.projectRoot, w.unitDir);
|
|
95
|
+
assertNoLockSecrets([w.scenario], serializeLock(lockWithAcceptance(w, 'D-0000000000000000', new Date().toISOString())));
|
|
96
|
+
const sc = w.scenario;
|
|
97
|
+
const accepted = w.accepted.map((a) => ({ step: a.step, sha: a.sha }));
|
|
98
|
+
const payload: Record<string, JsonValue> = {
|
|
99
|
+
unit: w.unit, scenarios: [{ id: sc.id, hash: sc.hash, implementation: sc.implementation, acceptedOpaque: accepted }], removed: [],
|
|
100
|
+
};
|
|
101
|
+
const decision = appendDecision(w.projectRoot, scope, {
|
|
102
|
+
kind: 'contract-approval', anchors: [], visibility: 'internal', valid_from: new Date().toISOString(), payload,
|
|
103
|
+
text: `Accepted the reviewed opaque step(s) ${[...new Set(accepted.map((a) => a.step))].join(', ')} of ${sc.id} (${w.unit})`,
|
|
104
|
+
reason: w.reason?.trim() || DEFAULT_OPAQUE_REASON,
|
|
105
|
+
source: { by: gitUserName(w.projectRoot), where: 'cli' },
|
|
106
|
+
});
|
|
107
|
+
const lock = lockWithAcceptance(w, decision.entry.id, decision.entry.valid_from);
|
|
108
|
+
writeLockFile(lockPathFor(w.unitDir), lock);
|
|
109
|
+
return { decisionId: decision.entry.id, lock };
|
|
110
|
+
}
|
|
@@ -0,0 +1,118 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* A trace's intent steps → Gherkin steps, deterministically (DOS-14.8): the same trace and the same
|
|
3
|
+
* identities give the same text, byte for byte. No LLM is involved — naming a scenario or suggesting
|
|
4
|
+
* a VP-id is left to the user (or an agent) and never touches the steps or the hash.
|
|
5
|
+
*
|
|
6
|
+
* keywords first step `Given`; the next action `When`, later actions `And`; a check after an
|
|
7
|
+
* action `Then`, after a check `And`; an action after a check `When`
|
|
8
|
+
* drops a click on an element immediately followed by a fill of the SAME element (a focus click)
|
|
9
|
+
* opaque `User see opaque check "<source line>"` / `User do opaque step "<GET /api/x>"`, with a
|
|
10
|
+
* `# opaque at <file>:<line>` comment — kept verbatim, always reviewed by a human
|
|
11
|
+
* low `# identity low: <why>` above any step whose target is not proven
|
|
12
|
+
* safety secrets typed into credential fields are redacted everywhere; machine paths scrubbed
|
|
13
|
+
*/
|
|
14
|
+
import * as fs from 'fs';
|
|
15
|
+
import * as path from 'path';
|
|
16
|
+
import { displayPath, scrubKnownSecrets, scrubMachinePaths } from './field-semantic-redaction';
|
|
17
|
+
import { normalizeShownText, TestDataIndex } from './data-normalize';
|
|
18
|
+
import { renderTarget, sameTarget } from './render-trace-target';
|
|
19
|
+
import { actionSentence, assertSentence, sensitiveStep } from './render-trace-sentence';
|
|
20
|
+
import type { TargetFacts, TargetIdentity } from './trace/target-identity';
|
|
21
|
+
import { isPageLevel } from './trace/trace-identity-resolution';
|
|
22
|
+
import type { IntentStep } from './trace/trace-steps';
|
|
23
|
+
|
|
24
|
+
export const CONTROL_CHARS = /[\u0000-\u001f\u007f\u2028\u2029]/g;
|
|
25
|
+
/** Text from a trace or report shown in one line of a finding: control characters dropped, at most `max` characters. */
|
|
26
|
+
export function clip(s: string, max = 120): string {
|
|
27
|
+
const t = s.replace(CONTROL_CHARS, '');
|
|
28
|
+
return t.length > max ? `${t.slice(0, max - 1)}\u2026` : t;
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
export interface ResolvedTarget { identity: TargetIdentity; facts?: TargetFacts }
|
|
32
|
+
export interface RenderedStep {
|
|
33
|
+
keyword: 'Given' | 'When' | 'Then' | 'And';
|
|
34
|
+
sentence: string;
|
|
35
|
+
kind: 'action' | 'assert';
|
|
36
|
+
/** The outermost `test.step` title (impl:code compares group by group). */
|
|
37
|
+
group?: string;
|
|
38
|
+
low?: string;
|
|
39
|
+
opaque?: { verbatim: string; where?: string };
|
|
40
|
+
}
|
|
41
|
+
export interface RenderContext { index: TestDataIndex; projectRoot: string; specFile?: string; secrets: Set<string> }
|
|
42
|
+
|
|
43
|
+
function whereOf(step: IntentStep, ctx: RenderContext): { where?: string; line?: string } {
|
|
44
|
+
const loc = step.location;
|
|
45
|
+
if (!loc?.file) return {};
|
|
46
|
+
const own = ctx.specFile && (loc.file === ctx.specFile || loc.file.endsWith(`/${ctx.specFile}`) || path.basename(loc.file) === path.basename(ctx.specFile));
|
|
47
|
+
const file = own ? ctx.specFile! : displayPath(loc.file, ctx.projectRoot);
|
|
48
|
+
let line: string | undefined;
|
|
49
|
+
if (own) {
|
|
50
|
+
try { line = fs.readFileSync(path.join(ctx.projectRoot, ctx.specFile!), 'utf8').split(/\r?\n/)[loc.line - 1]?.trim(); } catch { line = undefined; }
|
|
51
|
+
}
|
|
52
|
+
return { where: `${file}:${loc.line}`, line };
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
const quoteVerbatim = (s: string): string => `"${normalizeShownText(s).replace(/"/g, "'").replace(/\s+/g, ' ').trim()}"`;
|
|
56
|
+
|
|
57
|
+
function keywordFor(kind: 'action' | 'assert', prev: RenderedStep[]): RenderedStep['keyword'] {
|
|
58
|
+
const last = prev[prev.length - 1];
|
|
59
|
+
if (!last) return 'Given';
|
|
60
|
+
if (kind === 'assert') return last.kind === 'assert' ? 'And' : 'Then';
|
|
61
|
+
if (last.kind === 'assert') return 'When';
|
|
62
|
+
return prev.length === 1 && last.keyword === 'Given' ? 'When' : 'And';
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
export function renderTraceSteps(steps: IntentStep[], targets: Map<string, ResolvedTarget>, ctx: RenderContext): RenderedStep[] {
|
|
66
|
+
const out: RenderedStep[] = [];
|
|
67
|
+
steps.forEach((step, i) => {
|
|
68
|
+
const t = step.callId ? targets.get(step.callId) : undefined;
|
|
69
|
+
const next = steps[i + 1];
|
|
70
|
+
if (step.method === 'click' && next && (next.method === 'fill' || next.method === 'type') && sameTarget(t?.identity, next.callId ? targets.get(next.callId)?.identity : undefined)) return;
|
|
71
|
+
const group = step.group[0];
|
|
72
|
+
if (step.kind === 'opaque-assert' || step.kind === 'opaque-action') {
|
|
73
|
+
const { where, line } = whereOf(step, ctx);
|
|
74
|
+
const verbatim = step.kind === 'opaque-action' && /^[A-Z]+ /.test(step.verbatim ?? '') ? step.verbatim! : line || step.verbatim || step.method;
|
|
75
|
+
const kind = step.kind === 'opaque-assert' ? 'assert' : 'action';
|
|
76
|
+
const sentence = kind === 'assert' ? `User see opaque check ${quoteVerbatim(verbatim)}` : `User do opaque step ${quoteVerbatim(verbatim)}`;
|
|
77
|
+
out.push({ keyword: keywordFor(kind, out), sentence, kind, ...(group ? { group } : {}), opaque: { verbatim, ...(where ? { where } : {}) } });
|
|
78
|
+
return;
|
|
79
|
+
}
|
|
80
|
+
const target = isPageLevel(step) ? { text: '' } : renderTarget(t?.identity, t?.facts, typeof step.params.selector === 'string' ? step.params.selector : undefined);
|
|
81
|
+
const kind = step.kind === 'assert' ? 'assert' : 'action';
|
|
82
|
+
const sentence = kind === 'assert' ? assertSentence(step, target.text) : actionSentence(step, target.text, ctx, sensitiveStep(step, t?.facts, t?.identity.name));
|
|
83
|
+
out.push({ keyword: keywordFor(kind, out), sentence, kind, ...(group ? { group } : {}), ...(target.low ? { low: target.low } : {}) });
|
|
84
|
+
});
|
|
85
|
+
// Control characters (terminal escapes, BEL, line separators) would corrupt or spoof a Gherkin line.
|
|
86
|
+
const clean = (s: string): string => scrubMachinePaths(scrubKnownSecrets(s, ctx.secrets), ctx.projectRoot).replace(CONTROL_CHARS, '');
|
|
87
|
+
return out.map((s) => ({
|
|
88
|
+
...s, sentence: clean(s.sentence), ...(s.low ? { low: clean(s.low) } : {}),
|
|
89
|
+
...(s.opaque ? { opaque: { verbatim: clean(s.opaque.verbatim), ...(s.opaque.where ? { where: clean(s.opaque.where) } : {}) } } : {}),
|
|
90
|
+
}));
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
/** The Gherkin lines of one scenario's steps, with their comments. */
|
|
94
|
+
export function stepLines(steps: RenderedStep[]): string[] {
|
|
95
|
+
const lines: string[] = [];
|
|
96
|
+
for (const s of steps) {
|
|
97
|
+
if (s.opaque) lines.push(` # opaque${s.opaque.where ? ` at ${s.opaque.where}` : ''} — compared by a human, never automatically`);
|
|
98
|
+
if (s.low) lines.push(` # identity low: ${s.low}`);
|
|
99
|
+
lines.push(` ${s.keyword} ${s.sentence}`);
|
|
100
|
+
}
|
|
101
|
+
return lines;
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
export interface FreeScenarioBlock { id: string; title: string; tags: string[]; comments: string[]; steps: RenderedStep[] }
|
|
105
|
+
|
|
106
|
+
export function scenarioBlockText(b: FreeScenarioBlock): string {
|
|
107
|
+
return [` ${b.tags.join(' ')}`, ` Scenario: ${b.title}`, ...b.comments.map((c) => ` # ${c}`), ...stepLines(b.steps)].join('\n');
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
export const FREE_FEATURE_HEADER = [
|
|
111
|
+
'# Derived by `sungen contract derive` from Playwright traces — deterministic, no LLM.',
|
|
112
|
+
'# These steps are the contract; the Playwright test is the implementation. Each scenario stays a',
|
|
113
|
+
'# proposal (not counted by the audit) until QA approves it: sungen contract approve --screen <unit>',
|
|
114
|
+
];
|
|
115
|
+
|
|
116
|
+
export function freeFeatureText(unit: string, blocks: string[]): string {
|
|
117
|
+
return [...FREE_FEATURE_HEADER.map((l) => l.replace('<unit>', unit)), `Feature: ${unit} (free tests)`, '', ...blocks.flatMap((b) => [b, ''])].join('\n');
|
|
118
|
+
}
|
|
@@ -0,0 +1,98 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* One trace intent step → one sungen Gherkin sentence (without its keyword), in the documented
|
|
3
|
+
* forms the contract normalizer reads (DOS-14.1): `fill [T] field with {{v}}`, `see [T] heading
|
|
4
|
+
* with text "x"`, `see the url matches "re"`, `do not see [T] dialog`, `see [T] checkbox is checked`…
|
|
5
|
+
* A check outside that vocabulary still renders (verbatim expression words) and normalizes to the
|
|
6
|
+
* `unknown` matcher — which the ladder never calls `match` unless the sentence is identical.
|
|
7
|
+
*/
|
|
8
|
+
import { normalizeShownText, normalizeValue, TestDataIndex } from './data-normalize';
|
|
9
|
+
import { isSensitiveTarget, REDACTED } from './field-semantic-redaction';
|
|
10
|
+
import type { IntentStep } from './trace/trace-steps';
|
|
11
|
+
|
|
12
|
+
export interface SentenceContext {
|
|
13
|
+
index: TestDataIndex;
|
|
14
|
+
/** Raw values typed into credential fields — scrubbed from everything written. */
|
|
15
|
+
secrets: Set<string>;
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
const str = (v: unknown): string => (typeof v === 'string' ? v : v === undefined || v === null ? '' : String(v));
|
|
19
|
+
const quote = (v: string): string => `"${v.replace(/"/g, "'").replace(/\r?\n/g, '\\n')}"`;
|
|
20
|
+
|
|
21
|
+
/** A value as the step writes it: a whole `{{ref}}` bare, anything else quoted. */
|
|
22
|
+
function valueText(raw: string, ctx: SentenceContext, sensitive: boolean): string {
|
|
23
|
+
const ref = ctx.index.byValue.get(raw);
|
|
24
|
+
if (ref) return `{{${ref}}}`;
|
|
25
|
+
if (sensitive) { if (raw) ctx.secrets.add(raw); return REDACTED; }
|
|
26
|
+
const v = normalizeValue(raw, ctx.index);
|
|
27
|
+
return /^\{\{[^}]+\}\}$/.test(v) ? v : quote(v);
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
function pathOf(url: string): string {
|
|
31
|
+
try { const u = new URL(url); return /^https?:$/.test(u.protocol) ? `${u.pathname}${u.search}` : url; } catch { return url; }
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
function selectValues(params: Record<string, unknown>): string {
|
|
35
|
+
const opts = Array.isArray(params.options) ? params.options : [];
|
|
36
|
+
return opts.map((o) => (o && typeof o === 'object' ? str((o as Record<string, unknown>).valueOrLabel ?? (o as Record<string, unknown>).value ?? (o as Record<string, unknown>).label ?? (o as Record<string, unknown>).index) : str(o))).join(' | ');
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
export function actionSentence(step: IntentStep, target: string, ctx: SentenceContext, sensitive: boolean): string {
|
|
40
|
+
const p = step.params;
|
|
41
|
+
switch (step.method) {
|
|
42
|
+
case 'goto': return `User is on [${pathOf(normalizeShownText(str(p.url)))}] page`;
|
|
43
|
+
case 'fill': return `User fill ${target} with ${valueText(str(p.value), ctx, sensitive)}`;
|
|
44
|
+
case 'type': return `User fill ${target} with ${valueText(str(p.text), ctx, sensitive)}`;
|
|
45
|
+
case 'click': case 'tap': return `User click ${target}`;
|
|
46
|
+
case 'dblclick': return `User double click ${target}`;
|
|
47
|
+
case 'check': return `User check ${target}`;
|
|
48
|
+
case 'uncheck': return `User uncheck ${target}`;
|
|
49
|
+
case 'hover': return `User hover ${target}`;
|
|
50
|
+
case 'selectOption': return `User select ${target} with ${valueText(selectValues(p), ctx, false)}`;
|
|
51
|
+
case 'press': return `User press ${quote(str(p.key))} on ${target}`;
|
|
52
|
+
case 'setInputFiles': {
|
|
53
|
+
const files = Array.isArray(p.payloads) ? p.payloads.map((x) => str((x as Record<string, unknown>)?.name)) : Array.isArray(p.localPaths) ? p.localPaths.map((x) => str(x).split(/[\\/]/).pop() ?? '') : [];
|
|
54
|
+
return `User fill ${target.replace(/\] \S+/, '] uploader')} with ${quote(files.join(' | '))}`;
|
|
55
|
+
}
|
|
56
|
+
case 'keyboardPress': return `User press ${quote(str(p.key))} key`;
|
|
57
|
+
default: return `User type ${valueText(str(p.text), ctx, false)} on the keyboard`;
|
|
58
|
+
}
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
interface Expected { kind: 'text' | 'regex' | 'none'; value: string }
|
|
62
|
+
function expectedOf(p: Record<string, unknown>): Expected {
|
|
63
|
+
const list = Array.isArray(p.expectedText) ? (p.expectedText as Array<Record<string, unknown>>) : [];
|
|
64
|
+
if (list.length && list.every((e) => typeof e?.regexSource === 'string')) return { kind: 'regex', value: list.map((e) => str(e.regexSource)).join(' | ') };
|
|
65
|
+
if (list.length) return { kind: 'text', value: list.map((e) => str(e.string ?? e.regexSource)).join(' | ') };
|
|
66
|
+
if (typeof p.expectedNumber === 'number') return { kind: 'text', value: String(p.expectedNumber) };
|
|
67
|
+
return { kind: 'none', value: '' };
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
const STATES: Record<string, string> = { 'to.be.checked': 'checked', 'to.be.enabled': 'enabled', 'to.be.disabled': 'disabled', 'to.be.focused': 'focused', 'to.be.empty': 'empty', 'to.be.editable': 'editable', 'to.be.attached': 'attached' };
|
|
71
|
+
|
|
72
|
+
export function assertSentence(step: IntentStep, target: string): string {
|
|
73
|
+
const p = step.params;
|
|
74
|
+
const expr = str(p.expression);
|
|
75
|
+
const not = p.isNot === true;
|
|
76
|
+
const e = expectedOf(p);
|
|
77
|
+
const shown = (v: string): string => quote(normalizeShownText(v));
|
|
78
|
+
if (expr === 'to.have.url' || expr === 'to.have.title') {
|
|
79
|
+
const subject = expr === 'to.have.url' ? 'url' : 'title';
|
|
80
|
+
return `User see the ${subject} ${e.kind === 'regex' ? (not ? 'does not match' : 'matches') : not ? 'is not' : 'is'} ${shown(e.value)}`;
|
|
81
|
+
}
|
|
82
|
+
if (expr === 'to.be.visible') return not ? `User do not see ${target}` : `User see ${target}`;
|
|
83
|
+
if (expr === 'to.be.hidden') return not ? `User see ${target}` : `User see ${target} is hidden`;
|
|
84
|
+
if (STATES[expr]) return `User see ${target} is ${not ? 'not ' : ''}${STATES[expr]}`;
|
|
85
|
+
if (expr === 'to.have.text' || expr === 'to.contain.text') {
|
|
86
|
+
if (e.kind === 'regex') return `User see ${target} ${not ? 'does not match' : 'matches'} ${shown(e.value)}`;
|
|
87
|
+
if (expr === 'to.contain.text') return `User see ${target} ${not ? 'does not contain' : 'contains'} ${shown(e.value)}`;
|
|
88
|
+
return `User see ${target} ${not ? 'does not have text' : 'with text'} ${shown(e.value)}`;
|
|
89
|
+
}
|
|
90
|
+
if (expr === 'to.have.value' || expr === 'to.have.values') return `User see ${target} ${not ? 'does not have value' : 'has value'} ${shown(e.value)}`;
|
|
91
|
+
if (expr === 'to.have.count') return `User see ${target} ${not ? 'does not have' : 'has'} ${shown(e.value)} items`;
|
|
92
|
+
const words = expr.replace(/^to\./, '').replace(/\./g, ' ').replace(/^have /, 'has ').replace(/^be /, 'is ');
|
|
93
|
+
return `User see ${target} ${not ? 'not ' : ''}${words}${e.kind === 'none' ? '' : ` ${shown(e.value)}`}`;
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
/** The value typed into a credential field is sensitive even when the field's name is not. */
|
|
97
|
+
export const sensitiveStep = (step: IntentStep, facts: Parameters<typeof isSensitiveTarget>[0], name?: string): boolean =>
|
|
98
|
+
(step.method === 'fill' || step.method === 'type') && isSensitiveTarget(facts, name);
|