@sun-asterisk/sungen 3.2.34 → 3.3.0-alpha.2
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/commands/audit.d.ts.map +1 -1
- package/dist/cli/commands/audit.js +10 -5
- package/dist/cli/commands/audit.js.map +1 -1
- package/dist/cli/commands/capability.d.ts.map +1 -1
- package/dist/cli/commands/capability.js +6 -2
- package/dist/cli/commands/capability.js.map +1 -1
- package/dist/cli/commands/contract-derive-command.d.ts +11 -0
- package/dist/cli/commands/contract-derive-command.d.ts.map +1 -0
- package/dist/cli/commands/contract-derive-command.js +99 -0
- package/dist/cli/commands/contract-derive-command.js.map +1 -0
- package/dist/cli/commands/contract.d.ts +17 -0
- package/dist/cli/commands/contract.d.ts.map +1 -0
- package/dist/cli/commands/contract.js +162 -0
- package/dist/cli/commands/contract.js.map +1 -0
- package/dist/cli/commands/defect.d.ts +7 -0
- package/dist/cli/commands/defect.d.ts.map +1 -0
- package/dist/cli/commands/defect.js +68 -0
- package/dist/cli/commands/defect.js.map +1 -0
- package/dist/cli/commands/delivery.d.ts.map +1 -1
- package/dist/cli/commands/delivery.js +96 -12
- package/dist/cli/commands/delivery.js.map +1 -1
- package/dist/cli/commands/generate.d.ts.map +1 -1
- package/dist/cli/commands/generate.js +3 -1
- package/dist/cli/commands/generate.js.map +1 -1
- package/dist/cli/commands/journey.d.ts.map +1 -1
- package/dist/cli/commands/journey.js +43 -15
- package/dist/cli/commands/journey.js.map +1 -1
- package/dist/cli/commands/next.d.ts +8 -0
- package/dist/cli/commands/next.d.ts.map +1 -1
- package/dist/cli/commands/next.js +68 -8
- package/dist/cli/commands/next.js.map +1 -1
- package/dist/cli/commands/triage.d.ts +7 -0
- package/dist/cli/commands/triage.d.ts.map +1 -0
- package/dist/cli/commands/triage.js +98 -0
- package/dist/cli/commands/triage.js.map +1 -0
- package/dist/cli/commands/update.d.ts +11 -0
- package/dist/cli/commands/update.d.ts.map +1 -1
- package/dist/cli/commands/update.js +31 -6
- package/dist/cli/commands/update.js.map +1 -1
- package/dist/cli/index.js +24 -33
- package/dist/cli/index.js.map +1 -1
- package/dist/cli/project-sungen-invocation.d.ts +7 -0
- package/dist/cli/project-sungen-invocation.d.ts.map +1 -0
- package/dist/cli/project-sungen-invocation.js +76 -0
- package/dist/cli/project-sungen-invocation.js.map +1 -0
- package/dist/contract/approve-contract.d.ts +31 -0
- package/dist/contract/approve-contract.d.ts.map +1 -0
- package/dist/contract/approve-contract.js +87 -0
- package/dist/contract/approve-contract.js.map +1 -0
- package/dist/contract/assertion-matcher-vocabulary.d.ts +24 -0
- package/dist/contract/assertion-matcher-vocabulary.d.ts.map +1 -0
- package/dist/contract/assertion-matcher-vocabulary.js +100 -0
- package/dist/contract/assertion-matcher-vocabulary.js.map +1 -0
- package/dist/contract/build-contract-verdict.d.ts +32 -0
- package/dist/contract/build-contract-verdict.d.ts.map +1 -0
- package/dist/contract/build-contract-verdict.js +123 -0
- package/dist/contract/build-contract-verdict.js.map +1 -0
- package/dist/contract/build-trace-conformance.d.ts +18 -0
- package/dist/contract/build-trace-conformance.d.ts.map +1 -0
- package/dist/contract/build-trace-conformance.js +153 -0
- package/dist/contract/build-trace-conformance.js.map +1 -0
- package/dist/contract/classify-contract-change.d.ts +23 -0
- package/dist/contract/classify-contract-change.d.ts.map +1 -0
- package/dist/contract/classify-contract-change.js +98 -0
- package/dist/contract/classify-contract-change.js.map +1 -0
- package/dist/contract/classify-contract-unit.d.ts +18 -0
- package/dist/contract/classify-contract-unit.d.ts.map +1 -0
- package/dist/contract/classify-contract-unit.js +49 -0
- package/dist/contract/classify-contract-unit.js.map +1 -0
- package/dist/contract/classify-trace-conformance.d.ts +32 -0
- package/dist/contract/classify-trace-conformance.d.ts.map +1 -0
- package/dist/contract/classify-trace-conformance.js +117 -0
- package/dist/contract/classify-trace-conformance.js.map +1 -0
- package/dist/contract/contract-git.d.ts +24 -0
- package/dist/contract/contract-git.d.ts.map +1 -0
- package/dist/contract/contract-git.js +144 -0
- package/dist/contract/contract-git.js.map +1 -0
- package/dist/contract/contract-hash.d.ts +7 -0
- package/dist/contract/contract-hash.d.ts.map +1 -0
- package/dist/contract/contract-hash.js +38 -0
- package/dist/contract/contract-hash.js.map +1 -0
- package/dist/contract/contract-report-file.d.ts +4 -0
- package/dist/contract/contract-report-file.d.ts.map +1 -0
- package/dist/contract/contract-report-file.js +60 -0
- package/dist/contract/contract-report-file.js.map +1 -0
- package/dist/contract/contract-schema.d.ts +71 -0
- package/dist/contract/contract-schema.d.ts.map +1 -0
- package/dist/contract/contract-schema.js +102 -0
- package/dist/contract/contract-schema.js.map +1 -0
- package/dist/contract/contract-units.d.ts +12 -0
- package/dist/contract/contract-units.d.ts.map +1 -0
- package/dist/contract/contract-units.js +96 -0
- package/dist/contract/contract-units.js.map +1 -0
- package/dist/contract/contracts-lock-file.d.ts +24 -0
- package/dist/contract/contracts-lock-file.d.ts.map +1 -0
- package/dist/contract/contracts-lock-file.js +140 -0
- package/dist/contract/contracts-lock-file.js.map +1 -0
- package/dist/contract/data-normalize.d.ts +10 -0
- package/dist/contract/data-normalize.d.ts.map +1 -0
- package/dist/contract/data-normalize.js +110 -0
- package/dist/contract/data-normalize.js.map +1 -0
- package/dist/contract/derive-free-feature.d.ts +38 -0
- package/dist/contract/derive-free-feature.d.ts.map +1 -0
- package/dist/contract/derive-free-feature.js +154 -0
- package/dist/contract/derive-free-feature.js.map +1 -0
- package/dist/contract/field-semantic-redaction.d.ts +21 -0
- package/dist/contract/field-semantic-redaction.d.ts.map +1 -0
- package/dist/contract/field-semantic-redaction.js +118 -0
- package/dist/contract/field-semantic-redaction.js.map +1 -0
- package/dist/contract/free-feature-blocks.d.ts +10 -0
- package/dist/contract/free-feature-blocks.d.ts.map +1 -0
- package/dist/contract/free-feature-blocks.js +94 -0
- package/dist/contract/free-feature-blocks.js.map +1 -0
- package/dist/contract/lock-secret-guard.d.ts +22 -0
- package/dist/contract/lock-secret-guard.d.ts.map +1 -0
- package/dist/contract/lock-secret-guard.js +134 -0
- package/dist/contract/lock-secret-guard.js.map +1 -0
- package/dist/contract/lock-vs-git-head.d.ts +26 -0
- package/dist/contract/lock-vs-git-head.d.ts.map +1 -0
- package/dist/contract/lock-vs-git-head.js +28 -0
- package/dist/contract/lock-vs-git-head.js.map +1 -0
- package/dist/contract/normalize-contract-step.d.ts +21 -0
- package/dist/contract/normalize-contract-step.d.ts.map +1 -0
- package/dist/contract/normalize-contract-step.js +70 -0
- package/dist/contract/normalize-contract-step.js.map +1 -0
- package/dist/contract/normalize-feature-steps.d.ts +7 -0
- package/dist/contract/normalize-feature-steps.d.ts.map +1 -0
- package/dist/contract/normalize-feature-steps.js +127 -0
- package/dist/contract/normalize-feature-steps.js.map +1 -0
- package/dist/contract/oracle-strength.d.ts +12 -0
- package/dist/contract/oracle-strength.d.ts.map +1 -0
- package/dist/contract/oracle-strength.js +88 -0
- package/dist/contract/oracle-strength.js.map +1 -0
- package/dist/contract/project-contract-blockers.d.ts +10 -0
- package/dist/contract/project-contract-blockers.d.ts.map +1 -0
- package/dist/contract/project-contract-blockers.js +73 -0
- package/dist/contract/project-contract-blockers.js.map +1 -0
- package/dist/contract/record-contract-approval.d.ts +21 -0
- package/dist/contract/record-contract-approval.d.ts.map +1 -0
- package/dist/contract/record-contract-approval.js +98 -0
- package/dist/contract/record-contract-approval.js.map +1 -0
- package/dist/contract/render-free-gherkin.d.ts +40 -0
- package/dist/contract/render-free-gherkin.d.ts.map +1 -0
- package/dist/contract/render-free-gherkin.js +141 -0
- package/dist/contract/render-free-gherkin.js.map +1 -0
- package/dist/contract/render-trace-sentence.d.ts +20 -0
- package/dist/contract/render-trace-sentence.d.ts.map +1 -0
- package/dist/contract/render-trace-sentence.js +109 -0
- package/dist/contract/render-trace-sentence.js.map +1 -0
- package/dist/contract/render-trace-target.d.ts +14 -0
- package/dist/contract/render-trace-target.d.ts.map +1 -0
- package/dist/contract/render-trace-target.js +70 -0
- package/dist/contract/render-trace-target.js.map +1 -0
- package/dist/contract/resolve-unit-trace-identities.d.ts +8 -0
- package/dist/contract/resolve-unit-trace-identities.d.ts.map +1 -0
- package/dist/contract/resolve-unit-trace-identities.js +78 -0
- package/dist/contract/resolve-unit-trace-identities.js.map +1 -0
- package/dist/contract/trace/collect-trace-evidence.d.ts +33 -0
- package/dist/contract/trace/collect-trace-evidence.d.ts.map +1 -0
- package/dist/contract/trace/collect-trace-evidence.js +161 -0
- package/dist/contract/trace/collect-trace-evidence.js.map +1 -0
- package/dist/contract/trace/identity-cache.d.ts +19 -0
- package/dist/contract/trace/identity-cache.d.ts.map +1 -0
- package/dist/contract/trace/identity-cache.js +104 -0
- package/dist/contract/trace/identity-cache.js.map +1 -0
- package/dist/contract/trace/select-final-attempt.d.ts +27 -0
- package/dist/contract/trace/select-final-attempt.d.ts.map +1 -0
- package/dist/contract/trace/select-final-attempt.js +116 -0
- package/dist/contract/trace/select-final-attempt.js.map +1 -0
- package/dist/contract/trace/snapshot-html-renderer.d.ts +31 -0
- package/dist/contract/trace/snapshot-html-renderer.d.ts.map +1 -0
- package/dist/contract/trace/snapshot-html-renderer.js +172 -0
- package/dist/contract/trace/snapshot-html-renderer.js.map +1 -0
- package/dist/contract/trace/snapshot-resolver.d.ts +21 -0
- package/dist/contract/trace/snapshot-resolver.d.ts.map +1 -0
- package/dist/contract/trace/snapshot-resolver.js +65 -0
- package/dist/contract/trace/snapshot-resolver.js.map +1 -0
- package/dist/contract/trace/snapshot-store.d.ts +66 -0
- package/dist/contract/trace/snapshot-store.d.ts.map +1 -0
- package/dist/contract/trace/snapshot-store.js +173 -0
- package/dist/contract/trace/snapshot-store.js.map +1 -0
- package/dist/contract/trace/target-identity.d.ts +74 -0
- package/dist/contract/trace/target-identity.d.ts.map +1 -0
- package/dist/contract/trace/target-identity.js +68 -0
- package/dist/contract/trace/target-identity.js.map +1 -0
- package/dist/contract/trace/test-step-join.d.ts +65 -0
- package/dist/contract/trace/test-step-join.d.ts.map +1 -0
- package/dist/contract/trace/test-step-join.js +80 -0
- package/dist/contract/trace/test-step-join.js.map +1 -0
- package/dist/contract/trace/trace-binding.d.ts +21 -0
- package/dist/contract/trace/trace-binding.d.ts.map +1 -0
- package/dist/contract/trace/trace-binding.js +88 -0
- package/dist/contract/trace/trace-binding.js.map +1 -0
- package/dist/contract/trace/trace-file-reader.d.ts +41 -0
- package/dist/contract/trace/trace-file-reader.d.ts.map +1 -0
- package/dist/contract/trace/trace-file-reader.js +161 -0
- package/dist/contract/trace/trace-file-reader.js.map +1 -0
- package/dist/contract/trace/trace-fingerprint.d.ts +22 -0
- package/dist/contract/trace/trace-fingerprint.d.ts.map +1 -0
- package/dist/contract/trace/trace-fingerprint.js +96 -0
- package/dist/contract/trace/trace-fingerprint.js.map +1 -0
- package/dist/contract/trace/trace-identity-resolution.d.ts +26 -0
- package/dist/contract/trace/trace-identity-resolution.d.ts.map +1 -0
- package/dist/contract/trace/trace-identity-resolution.js +84 -0
- package/dist/contract/trace/trace-identity-resolution.js.map +1 -0
- package/dist/contract/trace/trace-run-record.d.ts +30 -0
- package/dist/contract/trace/trace-run-record.d.ts.map +1 -0
- package/dist/contract/trace/trace-run-record.js +121 -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 +104 -0
- package/dist/contract/verify-approval-decisions.js.map +1 -0
- package/dist/decisions/append-decision.d.ts +12 -0
- package/dist/decisions/append-decision.d.ts.map +1 -0
- package/dist/decisions/append-decision.js +73 -0
- package/dist/decisions/append-decision.js.map +1 -0
- package/dist/decisions/confirmed-decision-commands.d.ts +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 +42 -0
- package/dist/decisions/decision-schema.d.ts.map +1 -0
- package/dist/decisions/decision-schema.js +75 -0
- package/dist/decisions/decision-schema.js.map +1 -0
- package/dist/decisions/decision-source.d.ts +2 -0
- package/dist/decisions/decision-source.d.ts.map +1 -0
- package/dist/decisions/decision-source.js +18 -0
- package/dist/decisions/decision-source.js.map +1 -0
- package/dist/decisions/exclusive-file-create.d.ts +3 -0
- package/dist/decisions/exclusive-file-create.d.ts.map +1 -0
- package/dist/decisions/exclusive-file-create.js +84 -0
- package/dist/decisions/exclusive-file-create.js.map +1 -0
- package/dist/decisions/git-head-trust.d.ts +7 -0
- package/dist/decisions/git-head-trust.d.ts.map +1 -0
- package/dist/decisions/git-head-trust.js +76 -0
- package/dist/decisions/git-head-trust.js.map +1 -0
- package/dist/decisions/human-confirmation.d.ts +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 +31 -0
- package/dist/decisions/legacy-waiver-migration.d.ts.map +1 -0
- package/dist/decisions/legacy-waiver-migration.js +170 -0
- package/dist/decisions/legacy-waiver-migration.js.map +1 -0
- package/dist/decisions/read-decisions.d.ts +19 -0
- package/dist/decisions/read-decisions.d.ts.map +1 -0
- package/dist/decisions/read-decisions.js +90 -0
- package/dist/decisions/read-decisions.js.map +1 -0
- package/dist/decisions/viewpoint-baseline-adapter.d.ts +7 -0
- package/dist/decisions/viewpoint-baseline-adapter.d.ts.map +1 -0
- package/dist/decisions/viewpoint-baseline-adapter.js +109 -0
- package/dist/decisions/viewpoint-baseline-adapter.js.map +1 -0
- package/dist/decisions/waiver-decisions.d.ts +24 -0
- package/dist/decisions/waiver-decisions.d.ts.map +1 -0
- package/dist/decisions/waiver-decisions.js +46 -0
- package/dist/decisions/waiver-decisions.js.map +1 -0
- package/dist/exporters/csv-exporter.d.ts +4 -0
- package/dist/exporters/csv-exporter.d.ts.map +1 -1
- package/dist/exporters/csv-exporter.js +1 -1
- package/dist/exporters/csv-exporter.js.map +1 -1
- package/dist/exporters/feature-parser.d.ts +8 -0
- package/dist/exporters/feature-parser.d.ts.map +1 -1
- package/dist/exporters/feature-parser.js +12 -0
- package/dist/exporters/feature-parser.js.map +1 -1
- package/dist/exporters/matrix/build.d.ts +3 -0
- package/dist/exporters/matrix/build.d.ts.map +1 -1
- package/dist/exporters/matrix/build.js +59 -19
- package/dist/exporters/matrix/build.js.map +1 -1
- package/dist/exporters/matrix/carry-manual-results.d.ts +19 -0
- package/dist/exporters/matrix/carry-manual-results.d.ts.map +1 -1
- package/dist/exporters/matrix/carry-manual-results.js +76 -6
- package/dist/exporters/matrix/carry-manual-results.js.map +1 -1
- package/dist/exporters/matrix/export.d.ts +8 -2
- package/dist/exporters/matrix/export.d.ts.map +1 -1
- package/dist/exporters/matrix/export.js +16 -3
- package/dist/exporters/matrix/export.js.map +1 -1
- package/dist/exporters/matrix/gates.d.ts +6 -1
- package/dist/exporters/matrix/gates.d.ts.map +1 -1
- package/dist/exporters/matrix/gates.js +57 -128
- package/dist/exporters/matrix/gates.js.map +1 -1
- package/dist/exporters/matrix/render-csv.d.ts.map +1 -1
- package/dist/exporters/matrix/render-csv.js +4 -1
- package/dist/exporters/matrix/render-csv.js.map +1 -1
- package/dist/exporters/matrix/render-gate-overrides-sheet.d.ts +13 -0
- package/dist/exporters/matrix/render-gate-overrides-sheet.d.ts.map +1 -0
- package/dist/exporters/matrix/render-gate-overrides-sheet.js +44 -0
- package/dist/exporters/matrix/render-gate-overrides-sheet.js.map +1 -0
- package/dist/exporters/matrix/render-xlsx.d.ts +1 -0
- package/dist/exporters/matrix/render-xlsx.d.ts.map +1 -1
- package/dist/exporters/matrix/render-xlsx.js +6 -0
- package/dist/exporters/matrix/render-xlsx.js.map +1 -1
- package/dist/exporters/matrix/types.d.ts +9 -1
- package/dist/exporters/matrix/types.d.ts.map +1 -1
- package/dist/exporters/matrix/types.js.map +1 -1
- package/dist/exporters/matrix/xlsx-cells.js +1 -1
- package/dist/exporters/matrix/xlsx-cells.js.map +1 -1
- package/dist/exporters/playwright-report-parser.d.ts.map +1 -1
- package/dist/exporters/playwright-report-parser.js +1 -0
- package/dist/exporters/playwright-report-parser.js.map +1 -1
- package/dist/exporters/scenario-merger.d.ts.map +1 -1
- package/dist/exporters/scenario-merger.js +2 -0
- package/dist/exporters/scenario-merger.js.map +1 -1
- package/dist/exporters/types.d.ts +5 -0
- package/dist/exporters/types.d.ts.map +1 -1
- package/dist/generators/test-generator/code-generator.d.ts.map +1 -1
- package/dist/generators/test-generator/code-generator.js +13 -0
- package/dist/generators/test-generator/code-generator.js.map +1 -1
- package/dist/graph/evaluate-graph.d.ts +23 -0
- package/dist/graph/evaluate-graph.d.ts.map +1 -0
- package/dist/graph/evaluate-graph.js +52 -0
- package/dist/graph/evaluate-graph.js.map +1 -0
- package/dist/graph/locate-finding.d.ts +20 -0
- package/dist/graph/locate-finding.d.ts.map +1 -0
- package/dist/graph/locate-finding.js +137 -0
- package/dist/graph/locate-finding.js.map +1 -0
- package/dist/graph/node-decl.d.ts +66 -0
- package/dist/graph/node-decl.d.ts.map +1 -0
- package/dist/graph/node-decl.js +8 -0
- package/dist/graph/node-decl.js.map +1 -0
- package/dist/graph/nodes/core-node-catalog.d.ts +7 -0
- package/dist/graph/nodes/core-node-catalog.d.ts.map +1 -0
- package/dist/graph/nodes/core-node-catalog.js +13 -0
- package/dist/graph/nodes/core-node-catalog.js.map +1 -0
- package/dist/graph/nodes/test-case-contract-nodes.d.ts +21 -0
- package/dist/graph/nodes/test-case-contract-nodes.d.ts.map +1 -0
- package/dist/graph/nodes/test-case-contract-nodes.js +59 -0
- package/dist/graph/nodes/test-case-contract-nodes.js.map +1 -0
- package/dist/graph/nodes/test-case-nodes.d.ts +26 -0
- package/dist/graph/nodes/test-case-nodes.d.ts.map +1 -0
- package/dist/graph/nodes/test-case-nodes.js +66 -0
- package/dist/graph/nodes/test-case-nodes.js.map +1 -0
- package/dist/graph/nodes/unit-audit-nodes.d.ts +18 -0
- package/dist/graph/nodes/unit-audit-nodes.d.ts.map +1 -0
- package/dist/graph/nodes/unit-audit-nodes.js +95 -0
- package/dist/graph/nodes/unit-audit-nodes.js.map +1 -0
- package/dist/graph/nodes/unit-contract-node.d.ts +21 -0
- package/dist/graph/nodes/unit-contract-node.d.ts.map +1 -0
- package/dist/graph/nodes/unit-contract-node.js +88 -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 +11 -0
- package/dist/graph/test-case-results.d.ts.map +1 -0
- package/dist/graph/test-case-results.js +137 -0
- package/dist/graph/test-case-results.js.map +1 -0
- package/dist/graph/test-case-satisfied.d.ts +25 -0
- package/dist/graph/test-case-satisfied.d.ts.map +1 -0
- package/dist/graph/test-case-satisfied.js +51 -0
- package/dist/graph/test-case-satisfied.js.map +1 -0
- package/dist/graph/test-case-state-shape.d.ts +67 -0
- package/dist/graph/test-case-state-shape.d.ts.map +1 -0
- package/dist/graph/test-case-state-shape.js +3 -0
- package/dist/graph/test-case-state-shape.js.map +1 -0
- package/dist/graph/test-case-states.d.ts +9 -0
- package/dist/graph/test-case-states.d.ts.map +1 -0
- package/dist/graph/test-case-states.js +116 -0
- package/dist/graph/test-case-states.js.map +1 -0
- package/dist/graph/unit-artifact-probes.d.ts +19 -0
- package/dist/graph/unit-artifact-probes.d.ts.map +1 -0
- package/dist/graph/unit-artifact-probes.js +133 -0
- package/dist/graph/unit-artifact-probes.js.map +1 -0
- package/dist/graph/unit-state-facts.d.ts +43 -0
- package/dist/graph/unit-state-facts.d.ts.map +1 -0
- package/dist/graph/unit-state-facts.js +87 -0
- package/dist/graph/unit-state-facts.js.map +1 -0
- package/dist/harness/audit.d.ts +11 -0
- package/dist/harness/audit.d.ts.map +1 -1
- package/dist/harness/audit.js +34 -9
- package/dist/harness/audit.js.map +1 -1
- package/dist/harness/catalog/gate-rules.yaml +647 -0
- package/dist/harness/counted-assertions.d.ts +2 -0
- package/dist/harness/counted-assertions.d.ts.map +1 -0
- package/dist/harness/counted-assertions.js +38 -0
- package/dist/harness/counted-assertions.js.map +1 -0
- package/dist/harness/defect-draft.d.ts +14 -0
- package/dist/harness/defect-draft.d.ts.map +1 -0
- package/dist/harness/defect-draft.js +143 -0
- package/dist/harness/defect-draft.js.map +1 -0
- package/dist/harness/defects.d.ts +24 -0
- package/dist/harness/defects.d.ts.map +1 -0
- package/dist/harness/defects.js +118 -0
- package/dist/harness/defects.js.map +1 -0
- package/dist/harness/gates/audit-gate-terms.d.ts +41 -0
- package/dist/harness/gates/audit-gate-terms.d.ts.map +1 -0
- package/dist/harness/gates/audit-gate-terms.js +72 -0
- package/dist/harness/gates/audit-gate-terms.js.map +1 -0
- package/dist/harness/gates/audit-gate-verdict.d.ts +31 -0
- package/dist/harness/gates/audit-gate-verdict.d.ts.map +1 -0
- package/dist/harness/gates/audit-gate-verdict.js +101 -0
- package/dist/harness/gates/audit-gate-verdict.js.map +1 -0
- package/dist/harness/gates/audit-input-hash.d.ts +4 -0
- package/dist/harness/gates/audit-input-hash.d.ts.map +1 -0
- package/dist/harness/gates/audit-input-hash.js +96 -0
- package/dist/harness/gates/audit-input-hash.js.map +1 -0
- package/dist/harness/gates/audit-report-verification.d.ts +41 -0
- package/dist/harness/gates/audit-report-verification.d.ts.map +1 -0
- package/dist/harness/gates/audit-report-verification.js +236 -0
- package/dist/harness/gates/audit-report-verification.js.map +1 -0
- package/dist/harness/gates/decisions-hash.d.ts +13 -0
- package/dist/harness/gates/decisions-hash.d.ts.map +1 -0
- package/dist/harness/gates/decisions-hash.js +72 -0
- package/dist/harness/gates/decisions-hash.js.map +1 -0
- package/dist/harness/gates/delivery-checklist.d.ts +21 -0
- package/dist/harness/gates/delivery-checklist.d.ts.map +1 -0
- package/dist/harness/gates/delivery-checklist.js +128 -0
- package/dist/harness/gates/delivery-checklist.js.map +1 -0
- package/dist/harness/gates/delivery-decision-gate.d.ts +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 +138 -0
- package/dist/harness/gates/in-run-decision-check.js.map +1 -0
- package/dist/harness/gates/journey-gate-inputs.d.ts +37 -0
- package/dist/harness/gates/journey-gate-inputs.d.ts.map +1 -0
- package/dist/harness/gates/journey-gate-inputs.js +81 -0
- package/dist/harness/gates/journey-gate-inputs.js.map +1 -0
- package/dist/harness/gates/migrated-waiver-review.d.ts +6 -0
- package/dist/harness/gates/migrated-waiver-review.d.ts.map +1 -0
- package/dist/harness/gates/migrated-waiver-review.js +116 -0
- package/dist/harness/gates/migrated-waiver-review.js.map +1 -0
- package/dist/harness/gates/resolve-rule.d.ts +59 -0
- package/dist/harness/gates/resolve-rule.d.ts.map +1 -0
- package/dist/harness/gates/resolve-rule.js +41 -0
- package/dist/harness/gates/resolve-rule.js.map +1 -0
- package/dist/harness/gates/unit-gate-overrides.d.ts +25 -0
- package/dist/harness/gates/unit-gate-overrides.d.ts.map +1 -0
- package/dist/harness/gates/unit-gate-overrides.js +122 -0
- package/dist/harness/gates/unit-gate-overrides.js.map +1 -0
- package/dist/harness/journey.d.ts +43 -1
- package/dist/harness/journey.d.ts.map +1 -1
- package/dist/harness/journey.js +207 -33
- package/dist/harness/journey.js.map +1 -1
- package/dist/harness/manifest.d.ts +12 -0
- package/dist/harness/manifest.d.ts.map +1 -1
- package/dist/harness/manifest.js +73 -45
- package/dist/harness/manifest.js.map +1 -1
- package/dist/harness/next-step.d.ts +37 -6
- package/dist/harness/next-step.d.ts.map +1 -1
- package/dist/harness/next-step.js +34 -174
- package/dist/harness/next-step.js.map +1 -1
- package/dist/harness/parse.d.ts +25 -0
- package/dist/harness/parse.d.ts.map +1 -1
- package/dist/harness/parse.js +38 -13
- package/dist/harness/parse.js.map +1 -1
- package/dist/harness/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 +29 -6
- package/dist/harness/script-check.js.map +1 -1
- package/dist/harness/sensors.d.ts +14 -3
- package/dist/harness/sensors.d.ts.map +1 -1
- package/dist/harness/sensors.js +64 -44
- package/dist/harness/sensors.js.map +1 -1
- package/dist/harness/triage-signals.d.ts +20 -0
- package/dist/harness/triage-signals.d.ts.map +1 -0
- package/dist/harness/triage-signals.js +30 -0
- package/dist/harness/triage-signals.js.map +1 -0
- package/dist/harness/triage-unit.d.ts +23 -0
- package/dist/harness/triage-unit.d.ts.map +1 -0
- package/dist/harness/triage-unit.js +142 -0
- package/dist/harness/triage-unit.js.map +1 -0
- package/dist/harness/triage.d.ts +28 -0
- package/dist/harness/triage.d.ts.map +1 -0
- package/dist/harness/triage.js +99 -0
- package/dist/harness/triage.js.map +1 -0
- package/dist/harness/viewpoint-ledger.d.ts.map +1 -1
- package/dist/harness/viewpoint-ledger.js +2 -1
- package/dist/harness/viewpoint-ledger.js.map +1 -1
- package/dist/hooks/bash-write-targets.d.ts +15 -0
- package/dist/hooks/bash-write-targets.d.ts.map +1 -0
- package/dist/hooks/bash-write-targets.js +173 -0
- package/dist/hooks/bash-write-targets.js.map +1 -0
- package/dist/hooks/claude-hook-entry.d.ts +11 -0
- package/dist/hooks/claude-hook-entry.d.ts.map +1 -0
- package/dist/hooks/claude-hook-entry.js +137 -0
- package/dist/hooks/claude-hook-entry.js.map +1 -0
- package/dist/hooks/claude-transcript-turn.d.ts +11 -0
- package/dist/hooks/claude-transcript-turn.d.ts.map +1 -0
- package/dist/hooks/claude-transcript-turn.js +143 -0
- package/dist/hooks/claude-transcript-turn.js.map +1 -0
- package/dist/hooks/guard-delivery-contract-rule.d.ts +10 -0
- package/dist/hooks/guard-delivery-contract-rule.d.ts.map +1 -0
- package/dist/hooks/guard-delivery-contract-rule.js +152 -0
- package/dist/hooks/guard-delivery-contract-rule.js.map +1 -0
- package/dist/hooks/guard-protected-paths.d.ts +9 -0
- package/dist/hooks/guard-protected-paths.d.ts.map +1 -0
- package/dist/hooks/guard-protected-paths.js +107 -0
- package/dist/hooks/guard-protected-paths.js.map +1 -0
- package/dist/hooks/guard-rules.d.ts +37 -0
- package/dist/hooks/guard-rules.d.ts.map +1 -0
- package/dist/hooks/guard-rules.js +61 -0
- package/dist/hooks/guard-rules.js.map +1 -0
- package/dist/hooks/guard-tty-spoof-rule.d.ts +15 -0
- package/dist/hooks/guard-tty-spoof-rule.d.ts.map +1 -0
- package/dist/hooks/guard-tty-spoof-rule.js +21 -0
- package/dist/hooks/guard-tty-spoof-rule.js.map +1 -0
- package/dist/hooks/hand-back-core.d.ts +39 -0
- package/dist/hooks/hand-back-core.d.ts.map +1 -0
- package/dist/hooks/hand-back-core.js +122 -0
- package/dist/hooks/hand-back-core.js.map +1 -0
- package/dist/hooks/hook-cli-command.d.ts +9 -0
- package/dist/hooks/hook-cli-command.d.ts.map +1 -0
- package/dist/hooks/hook-cli-command.js +46 -0
- package/dist/hooks/hook-cli-command.js.map +1 -0
- package/dist/hooks/hook-registry.d.ts +10 -0
- package/dist/hooks/hook-registry.d.ts.map +1 -0
- package/dist/hooks/hook-registry.js +24 -0
- package/dist/hooks/hook-registry.js.map +1 -0
- package/dist/hooks/hook-result-contract.d.ts +50 -0
- package/dist/hooks/hook-result-contract.d.ts.map +1 -0
- package/dist/hooks/hook-result-contract.js +80 -0
- package/dist/hooks/hook-result-contract.js.map +1 -0
- package/dist/hooks/hook-toggles.d.ts +10 -0
- package/dist/hooks/hook-toggles.d.ts.map +1 -0
- package/dist/hooks/hook-toggles.js +107 -0
- package/dist/hooks/hook-toggles.js.map +1 -0
- package/dist/hooks/pre-tool-guard-hook.d.ts +10 -0
- package/dist/hooks/pre-tool-guard-hook.d.ts.map +1 -0
- package/dist/hooks/pre-tool-guard-hook.js +105 -0
- package/dist/hooks/pre-tool-guard-hook.js.map +1 -0
- package/dist/hooks/session-start-unit-state-hook.d.ts +13 -0
- package/dist/hooks/session-start-unit-state-hook.d.ts.map +1 -0
- package/dist/hooks/session-start-unit-state-hook.js +57 -0
- package/dist/hooks/session-start-unit-state-hook.js.map +1 -0
- package/dist/hooks/stop-next-step-hook.d.ts +16 -0
- package/dist/hooks/stop-next-step-hook.d.ts.map +1 -0
- package/dist/hooks/stop-next-step-hook.js +42 -0
- package/dist/hooks/stop-next-step-hook.js.map +1 -0
- package/dist/hooks/sungen-project-root.d.ts +19 -0
- package/dist/hooks/sungen-project-root.d.ts.map +1 -0
- package/dist/hooks/sungen-project-root.js +130 -0
- package/dist/hooks/sungen-project-root.js.map +1 -0
- package/dist/orchestrator/ai-skills/command-partial-includes.d.ts +15 -0
- package/dist/orchestrator/ai-skills/command-partial-includes.d.ts.map +1 -0
- package/dist/orchestrator/ai-skills/command-partial-includes.js +106 -0
- package/dist/orchestrator/ai-skills/command-partial-includes.js.map +1 -0
- package/dist/orchestrator/ai-skills/commands.d.ts +5 -1
- package/dist/orchestrator/ai-skills/commands.d.ts.map +1 -1
- package/dist/orchestrator/ai-skills/commands.js +16 -6
- package/dist/orchestrator/ai-skills/commands.js.map +1 -1
- package/dist/orchestrator/ai-skills/golden-skills.test.js +29 -0
- package/dist/orchestrator/ai-skills/golden-skills.test.js.map +1 -1
- package/dist/orchestrator/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/screen-manager.d.ts.map +1 -1
- package/dist/orchestrator/screen-manager.js +4 -2
- package/dist/orchestrator/screen-manager.js.map +1 -1
- package/dist/orchestrator/templates/ai-src/commands/add-api.md +2 -0
- package/dist/orchestrator/templates/ai-src/commands/add-flow.md +2 -0
- package/dist/orchestrator/templates/ai-src/commands/add-screen.md +2 -0
- package/dist/orchestrator/templates/ai-src/commands/create-data-test.md +1 -0
- package/dist/orchestrator/templates/ai-src/commands/create-test.md +14 -16
- package/dist/orchestrator/templates/ai-src/commands/dashboard.md +2 -0
- package/dist/orchestrator/templates/ai-src/commands/delivery.md +2 -0
- package/dist/orchestrator/templates/ai-src/commands/design.md +2 -0
- package/dist/orchestrator/templates/ai-src/commands/feedback.md +2 -0
- package/dist/orchestrator/templates/ai-src/commands/locale.md +2 -0
- package/dist/orchestrator/templates/ai-src/commands/review.md +2 -0
- package/dist/orchestrator/templates/ai-src/commands/run-test.md +14 -10
- package/dist/orchestrator/templates/ai-src/config/claude.md +20 -7
- package/dist/orchestrator/templates/ai-src/config/copilot.md +20 -7
- package/dist/orchestrator/templates/ai-src/partials/hand-back-rule.claude.md +20 -0
- package/dist/orchestrator/templates/ai-src/partials/hand-back-rule.codex.md +20 -0
- package/dist/orchestrator/templates/ai-src/partials/hand-back-rule.copilot.md +20 -0
- package/dist/orchestrator/templates/ai-src/skills/sungen-api-coverage-model/SKILL.md +1 -1
- package/dist/orchestrator/templates/ai-src/skills/sungen-capture-mobile/SKILL.md +3 -3
- package/dist/orchestrator/templates/ai-src/skills/sungen-data-factory/SKILL.md +9 -0
- package/dist/orchestrator/templates/ai-src/skills/sungen-error-mapping/SKILL.md +2 -0
- package/dist/orchestrator/templates/ai-src/skills/sungen-gherkin-syntax/SKILL.md +1 -1
- package/dist/orchestrator/templates/ai-src/skills/sungen-harness-audit/SKILL.md +2 -0
- package/dist/orchestrator/templates/claude-hooks/sungen-hook.cjs +64 -0
- package/dist/utils/safe-yaml-parse.d.ts +8 -0
- package/dist/utils/safe-yaml-parse.d.ts.map +1 -0
- package/dist/utils/safe-yaml-parse.js +51 -0
- package/dist/utils/safe-yaml-parse.js.map +1 -0
- package/package.json +3 -3
- package/src/basis/anchor-grammar.ts +83 -0
- package/src/basis/anchor-hash.ts +27 -0
- package/src/basis/bm51-tables.ts +65 -0
- package/src/basis/extract-spec-anchors.ts +95 -0
- package/src/basis/migration-sentinel.ts +175 -0
- package/src/basis/spec-frontmatter.ts +68 -0
- package/src/basis/spec-sections.ts +35 -0
- package/src/basis/test-basis-schema.ts +81 -0
- package/src/capabilities/discover.ts +15 -2
- package/src/capabilities/registry.ts +16 -0
- package/src/cli/commands/audit.ts +10 -5
- package/src/cli/commands/capability.ts +4 -2
- package/src/cli/commands/contract-derive-command.ts +61 -0
- package/src/cli/commands/contract.ts +130 -0
- package/src/cli/commands/defect.ts +49 -0
- package/src/cli/commands/delivery.ts +92 -15
- package/src/cli/commands/generate.ts +3 -1
- package/src/cli/commands/journey.ts +41 -16
- package/src/cli/commands/next.ts +64 -10
- package/src/cli/commands/triage.ts +57 -0
- package/src/cli/commands/update.ts +35 -8
- package/src/cli/index.ts +24 -30
- package/src/cli/project-sungen-invocation.ts +39 -0
- package/src/contract/approve-contract.ts +94 -0
- package/src/contract/assertion-matcher-vocabulary.ts +93 -0
- package/src/contract/build-contract-verdict.ts +108 -0
- package/src/contract/build-trace-conformance.ts +100 -0
- package/src/contract/classify-contract-change.ts +92 -0
- package/src/contract/classify-contract-unit.ts +47 -0
- package/src/contract/classify-trace-conformance.ts +116 -0
- package/src/contract/contract-git.ts +106 -0
- package/src/contract/contract-hash.ts +34 -0
- package/src/contract/contract-report-file.ts +25 -0
- package/src/contract/contract-schema.ts +137 -0
- package/src/contract/contract-units.ts +52 -0
- package/src/contract/contracts-lock-file.ts +97 -0
- package/src/contract/data-normalize.ts +66 -0
- package/src/contract/derive-free-feature.ts +120 -0
- package/src/contract/field-semantic-redaction.ts +80 -0
- package/src/contract/free-feature-blocks.ts +56 -0
- package/src/contract/lock-secret-guard.ts +95 -0
- package/src/contract/lock-vs-git-head.ts +38 -0
- package/src/contract/normalize-contract-step.ts +74 -0
- package/src/contract/normalize-feature-steps.ts +93 -0
- package/src/contract/oracle-strength.ts +80 -0
- package/src/contract/project-contract-blockers.ts +36 -0
- package/src/contract/record-contract-approval.ts +70 -0
- package/src/contract/render-free-gherkin.ts +113 -0
- package/src/contract/render-trace-sentence.ts +98 -0
- package/src/contract/render-trace-target.ts +68 -0
- package/src/contract/resolve-unit-trace-identities.ts +35 -0
- package/src/contract/trace/collect-trace-evidence.ts +109 -0
- package/src/contract/trace/identity-cache.ts +61 -0
- package/src/contract/trace/select-final-attempt.ts +70 -0
- package/src/contract/trace/snapshot-html-renderer.ts +136 -0
- package/src/contract/trace/snapshot-resolver.ts +66 -0
- package/src/contract/trace/snapshot-store.ts +151 -0
- package/src/contract/trace/target-identity.ts +104 -0
- package/src/contract/trace/test-step-join.ts +104 -0
- package/src/contract/trace/trace-binding.ts +56 -0
- package/src/contract/trace/trace-file-reader.ts +133 -0
- package/src/contract/trace/trace-fingerprint.ts +68 -0
- package/src/contract/trace/trace-identity-resolution.ts +74 -0
- package/src/contract/trace/trace-run-record.ts +80 -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 +60 -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 +76 -0
- package/src/decisions/decision-source.ts +14 -0
- package/src/decisions/exclusive-file-create.ts +42 -0
- package/src/decisions/git-head-trust.ts +32 -0
- package/src/decisions/human-confirmation.ts +119 -0
- package/src/decisions/legacy-waiver-migration.ts +135 -0
- package/src/decisions/read-decisions.ts +61 -0
- package/src/decisions/viewpoint-baseline-adapter.ts +74 -0
- package/src/decisions/waiver-decisions.ts +56 -0
- package/src/exporters/csv-exporter.ts +5 -1
- package/src/exporters/feature-parser.ts +12 -0
- package/src/exporters/matrix/build.ts +62 -18
- package/src/exporters/matrix/carry-manual-results.ts +70 -7
- package/src/exporters/matrix/export.ts +17 -2
- package/src/exporters/matrix/gates.ts +62 -129
- package/src/exporters/matrix/render-csv.ts +4 -1
- package/src/exporters/matrix/render-gate-overrides-sheet.ts +50 -0
- package/src/exporters/matrix/render-xlsx.ts +6 -0
- package/src/exporters/matrix/types.ts +9 -1
- package/src/exporters/matrix/xlsx-cells.ts +1 -1
- package/src/exporters/playwright-report-parser.ts +1 -0
- package/src/exporters/scenario-merger.ts +2 -1
- package/src/exporters/types.ts +5 -0
- package/src/generators/test-generator/code-generator.ts +13 -0
- package/src/graph/evaluate-graph.ts +66 -0
- package/src/graph/locate-finding.ts +93 -0
- package/src/graph/node-decl.ts +72 -0
- package/src/graph/nodes/core-node-catalog.ts +15 -0
- package/src/graph/nodes/test-case-contract-nodes.ts +74 -0
- package/src/graph/nodes/test-case-nodes.ts +68 -0
- package/src/graph/nodes/unit-audit-nodes.ts +95 -0
- package/src/graph/nodes/unit-contract-node.ts +89 -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 +90 -0
- package/src/graph/test-case-satisfied.ts +55 -0
- package/src/graph/test-case-state-shape.ts +62 -0
- package/src/graph/test-case-states.ts +84 -0
- package/src/graph/unit-artifact-probes.ts +77 -0
- package/src/graph/unit-state-facts.ts +112 -0
- package/src/harness/audit.ts +46 -11
- package/src/harness/catalog/gate-rules.yaml +647 -0
- package/src/harness/counted-assertions.ts +33 -0
- package/src/harness/defect-draft.ts +115 -0
- package/src/harness/defects.ts +94 -0
- package/src/harness/gates/audit-gate-terms.ts +80 -0
- package/src/harness/gates/audit-gate-verdict.ts +78 -0
- package/src/harness/gates/audit-input-hash.ts +55 -0
- package/src/harness/gates/audit-report-verification.ts +201 -0
- package/src/harness/gates/decisions-hash.ts +42 -0
- package/src/harness/gates/delivery-checklist.ts +120 -0
- package/src/harness/gates/delivery-decision-gate.ts +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 +99 -0
- package/src/harness/gates/journey-gate-inputs.ts +89 -0
- package/src/harness/gates/migrated-waiver-review.ts +68 -0
- package/src/harness/gates/resolve-rule.ts +94 -0
- package/src/harness/gates/unit-gate-overrides.ts +99 -0
- package/src/harness/journey.ts +215 -38
- package/src/harness/manifest.ts +69 -45
- package/src/harness/next-step.ts +60 -170
- package/src/harness/parse.ts +40 -13
- package/src/harness/provenance.ts +99 -11
- package/src/harness/script-check.ts +27 -6
- package/src/harness/sensors.ts +73 -47
- package/src/harness/triage-signals.ts +46 -0
- package/src/harness/triage-unit.ts +113 -0
- package/src/harness/triage.ts +127 -0
- package/src/harness/viewpoint-ledger.ts +2 -2
- package/src/hooks/bash-write-targets.ts +109 -0
- package/src/hooks/claude-hook-entry.ts +85 -0
- package/src/hooks/claude-transcript-turn.ts +96 -0
- package/src/hooks/guard-delivery-contract-rule.ts +110 -0
- package/src/hooks/guard-protected-paths.ts +71 -0
- package/src/hooks/guard-rules.ts +98 -0
- package/src/hooks/guard-tty-spoof-rule.ts +31 -0
- package/src/hooks/hand-back-core.ts +112 -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 +56 -0
- package/src/hooks/stop-next-step-hook.ts +37 -0
- package/src/hooks/sungen-project-root.ts +80 -0
- package/src/orchestrator/ai-skills/command-partial-includes.ts +72 -0
- package/src/orchestrator/ai-skills/commands.ts +22 -8
- package/src/orchestrator/ai-skills/golden-skills.test.ts +23 -0
- package/src/orchestrator/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/screen-manager.ts +3 -1
- package/src/orchestrator/templates/ai-src/commands/add-api.md +2 -0
- package/src/orchestrator/templates/ai-src/commands/add-flow.md +2 -0
- package/src/orchestrator/templates/ai-src/commands/add-screen.md +2 -0
- package/src/orchestrator/templates/ai-src/commands/create-data-test.md +1 -0
- package/src/orchestrator/templates/ai-src/commands/create-test.md +14 -16
- package/src/orchestrator/templates/ai-src/commands/dashboard.md +2 -0
- package/src/orchestrator/templates/ai-src/commands/delivery.md +2 -0
- package/src/orchestrator/templates/ai-src/commands/design.md +2 -0
- package/src/orchestrator/templates/ai-src/commands/feedback.md +2 -0
- package/src/orchestrator/templates/ai-src/commands/locale.md +2 -0
- package/src/orchestrator/templates/ai-src/commands/review.md +2 -0
- package/src/orchestrator/templates/ai-src/commands/run-test.md +14 -10
- package/src/orchestrator/templates/ai-src/config/claude.md +20 -7
- package/src/orchestrator/templates/ai-src/config/copilot.md +20 -7
- package/src/orchestrator/templates/ai-src/partials/hand-back-rule.claude.md +20 -0
- package/src/orchestrator/templates/ai-src/partials/hand-back-rule.codex.md +20 -0
- package/src/orchestrator/templates/ai-src/partials/hand-back-rule.copilot.md +20 -0
- package/src/orchestrator/templates/ai-src/skills/sungen-api-coverage-model/SKILL.md +1 -1
- package/src/orchestrator/templates/ai-src/skills/sungen-capture-mobile/SKILL.md +3 -3
- package/src/orchestrator/templates/ai-src/skills/sungen-data-factory/SKILL.md +9 -0
- package/src/orchestrator/templates/ai-src/skills/sungen-error-mapping/SKILL.md +2 -0
- package/src/orchestrator/templates/ai-src/skills/sungen-gherkin-syntax/SKILL.md +1 -1
- package/src/orchestrator/templates/ai-src/skills/sungen-harness-audit/SKILL.md +2 -0
- package/src/orchestrator/templates/claude-hooks/sungen-hook.cjs +64 -0
- package/src/utils/safe-yaml-parse.ts +44 -0
|
@@ -0,0 +1,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);
|
|
@@ -0,0 +1,68 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* How a trace target reads in Gherkin: `[<accessible name>] <element type>` plus an optional
|
|
3
|
+
* `in [<scope name>] <container>` (DOS-14.5, G3). The words are the documented sungen forms, so a
|
|
4
|
+
* derived `.free.feature` normalizes back to exactly the contract steps the trace produced.
|
|
5
|
+
*
|
|
6
|
+
* name the accessible name, dynamic parts normalized, private-use glyphs (icon fonts, R4)
|
|
7
|
+
* stripped, brackets and quotes made safe for the bracket grammar
|
|
8
|
+
* fallback no usable name (a `getByText` on a <p>, a missing target): the text the test
|
|
9
|
+
* itself located by, else the element's text, else the role — always `low` (R3)
|
|
10
|
+
*/
|
|
11
|
+
import { normalizeShownText } from './data-normalize';
|
|
12
|
+
import type { TargetFacts, TargetIdentity } from './trace/target-identity';
|
|
13
|
+
|
|
14
|
+
const TYPE_OF: Record<string, string> = {
|
|
15
|
+
button: 'button', link: 'link', textbox: 'field', searchbox: 'search', combobox: 'dropdown', listbox: 'dropdown',
|
|
16
|
+
checkbox: 'checkbox', radio: 'radio', switch: 'toggle', heading: 'heading', spinbutton: 'field', slider: 'slider',
|
|
17
|
+
tab: 'tab', img: 'image', dialog: 'dialog', alertdialog: 'dialog', alert: 'alert', row: 'row', cell: 'text',
|
|
18
|
+
listitem: 'item', option: 'option', menuitem: 'menu-item', status: 'message', table: 'table', list: 'list',
|
|
19
|
+
navigation: 'region', region: 'region', form: 'form', article: 'card', progressbar: 'progressbar', tooltip: 'tooltip',
|
|
20
|
+
};
|
|
21
|
+
const CONTAINER_OF: Record<string, string> = {
|
|
22
|
+
row: 'row', listitem: 'list-item', list: 'list', table: 'table', grid: 'table', treegrid: 'table', dialog: 'dialog',
|
|
23
|
+
alertdialog: 'dialog', form: 'form', group: 'group', radiogroup: 'group', article: 'card', tabpanel: 'panel',
|
|
24
|
+
menu: 'menu', menubar: 'menu',
|
|
25
|
+
};
|
|
26
|
+
|
|
27
|
+
/** Private-use code points (icon-font glyphs) are part of the identity but not of what a reader sees. */
|
|
28
|
+
const PRIVATE_USE = /[-]/g;
|
|
29
|
+
|
|
30
|
+
export function safeRefText(text: string): string {
|
|
31
|
+
return normalizeShownText(text.replace(PRIVATE_USE, '').replace(/\s+/g, ' ').trim())
|
|
32
|
+
.replace(/\[/g, '(').replace(/\]/g, ')').replace(/"/g, "'");
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
export const elementType = (role: string): string => TYPE_OF[role] ?? 'text';
|
|
36
|
+
|
|
37
|
+
/** The text a locator itself pins (`internal:text="Hi"`, `internal:label="Email"i`, a test id), if any. */
|
|
38
|
+
export function refFromSelector(selector: string | undefined): string {
|
|
39
|
+
if (!selector) return '';
|
|
40
|
+
const tail = selector.split(' >> ').filter((p) => !/^(nth=|visible=|internal:control=enter-frame)/.test(p)).pop() ?? '';
|
|
41
|
+
const quoted = /^internal:(?:text|label|has-text)="((?:[^"\\]|\\.)*)"[is]?$/.exec(tail);
|
|
42
|
+
if (quoted) return quoted[1].replace(/\\"/g, '"');
|
|
43
|
+
const attr = /^internal:(?:testid|attr)=\[[\w-]+="((?:[^"\\]|\\.)*)"[is]?\]$/.exec(tail);
|
|
44
|
+
if (attr) return attr[1];
|
|
45
|
+
const role = /^internal:role=([a-z]+)(?:\[name="((?:[^"\\]|\\.)*)"[is]?\])?/.exec(tail);
|
|
46
|
+
if (role) return role[2] ?? role[1];
|
|
47
|
+
return tail.replace(/^css=/, '');
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
export interface RenderedTarget { text: string; low?: string }
|
|
51
|
+
|
|
52
|
+
/** `[Name] type in [Scope] container`, and why it is low when it is. */
|
|
53
|
+
export function renderTarget(identity: TargetIdentity | undefined, facts: TargetFacts | undefined, selector: string | undefined): RenderedTarget {
|
|
54
|
+
const role = identity?.role ?? 'generic';
|
|
55
|
+
let name = safeRefText(identity?.name ?? '');
|
|
56
|
+
let low = identity?.identityConfidence === 'high' ? undefined : (identity?.reasons.join(', ') || 'no target identity');
|
|
57
|
+
if (!name) {
|
|
58
|
+
name = safeRefText(refFromSelector(selector) || facts?.text?.slice(0, 60) || role);
|
|
59
|
+
low = low ?? 'no accessible name';
|
|
60
|
+
}
|
|
61
|
+
const scope = identity?.scope && identity.identityConfidence === 'high'
|
|
62
|
+
? ` in [${safeRefText(identity.scope.name)}] ${CONTAINER_OF[identity.scope.role] ?? 'region'}` : '';
|
|
63
|
+
return { text: `[${name}] ${elementType(role)}${scope}`, ...(low ? { low } : {}) };
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
/** Two identities name the same element (used to drop a focus click before a fill). */
|
|
67
|
+
export const sameTarget = (a: TargetIdentity | undefined, b: TargetIdentity | undefined): boolean =>
|
|
68
|
+
!!a && !!b && a.role === b.role && a.name === b.name && JSON.stringify(a.scope ?? null) === JSON.stringify(b.scope ?? null);
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* `contract check --trace`: before the verdict is built, ask the registered `traceIdentity`
|
|
3
|
+
* resolver (driver-ui — a real browser) about every target of the unit's admissible traces and
|
|
4
|
+
* cache the answers (DOS-14.5). The verdict itself never calls a browser; it reads the cache, so
|
|
5
|
+
* the CLI and the Claude Code guard keep building the same verdict from the same files.
|
|
6
|
+
*/
|
|
7
|
+
import * as fs from 'fs';
|
|
8
|
+
import { capabilityRegistry } from '../capabilities/registry';
|
|
9
|
+
import { readCapabilities } from '../harness/capability';
|
|
10
|
+
import { MOBILE_NOT_SUPPORTED } from './build-trace-conformance';
|
|
11
|
+
import { lockPathFor } from './contracts-lock-file';
|
|
12
|
+
import { contractFeatureFiles, loadContractScenarios } from './normalize-feature-steps';
|
|
13
|
+
import { collectScenarioEvidence } from './trace/collect-trace-evidence';
|
|
14
|
+
import type { TraceIdentityResolver } from './trace/target-identity';
|
|
15
|
+
import { resolveWithDriver } from './trace/trace-identity-resolution';
|
|
16
|
+
|
|
17
|
+
const mtimeMs = (f: string): number => { try { return fs.statSync(f).mtimeMs; } catch { return 0; } };
|
|
18
|
+
|
|
19
|
+
/** The first registered capability that can resolve trace identities (UI: driver-ui), if any. */
|
|
20
|
+
export function registeredTraceResolver(): TraceIdentityResolver | undefined {
|
|
21
|
+
return capabilityRegistry.all().find((c) => typeof c.traceIdentity === 'function')?.traceIdentity;
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
export async function resolveUnitTraceIdentities(projectRoot: string, unit: { name: string; dir: string }, resolver = registeredTraceResolver()): Promise<string[]> {
|
|
25
|
+
const traced = loadContractScenarios(unit.dir, unit.name).filter((s) => s.implementation !== 'compiled');
|
|
26
|
+
if (!traced.length) return [];
|
|
27
|
+
if ((readCapabilities(projectRoot).platform ?? '').toLowerCase() === 'mobile') return [MOBILE_NOT_SUPPORTED];
|
|
28
|
+
if (!resolver) return ['no trace identity resolver is registered (install @sungen/driver-ui) — every target identity stays low, so nothing can match'];
|
|
29
|
+
const notBefore = Math.max(mtimeMs(lockPathFor(unit.dir)), ...contractFeatureFiles(unit.dir, unit.name).map(mtimeMs));
|
|
30
|
+
const evidence = collectScenarioEvidence(projectRoot, new Set(traced.map((s) => s.id)), notBefore);
|
|
31
|
+
const traces = [...evidence.runs.values()].flat().filter((r) => r.status === 'admissible' && r.trace).map((r) => r.trace!);
|
|
32
|
+
if (!traces.length) return [];
|
|
33
|
+
const r = await resolveWithDriver(projectRoot, traces, resolver);
|
|
34
|
+
return [`trace identity: ${r.asked} target(s) resolved by ${r.resolver || 'the cache'}${r.blockedRequests ? `, ${r.blockedRequests} replay request(s) blocked` : ''}`];
|
|
35
|
+
}
|
|
@@ -0,0 +1,109 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Which recorded traces speak for which scenario (DOS-14.6): run records → their JSON reports →
|
|
3
|
+
* each test's final attempt → its trace (zip limits + version gate) → the binding checks.
|
|
4
|
+
*
|
|
5
|
+
* admissible final attempt passed, trace read, every binding link holds
|
|
6
|
+
* inconclusive final attempt did not pass (warning)
|
|
7
|
+
* unproven no run record, no test, no trace (hard for free / impl:code)
|
|
8
|
+
* refused the trace exists but may not speak (swapped, stale, rejected zip…)
|
|
9
|
+
* unsupported the trace's format version is unknown (exit ≠ 0)
|
|
10
|
+
*
|
|
11
|
+
* Only traces of the scenario ids asked for are opened, so a unit pays for its own tests only.
|
|
12
|
+
*/
|
|
13
|
+
import * as fs from 'fs';
|
|
14
|
+
import * as path from 'path';
|
|
15
|
+
import { bindTrace } from './trace-binding';
|
|
16
|
+
import { readTraceFile, ParsedTrace } from './trace-file-reader';
|
|
17
|
+
import { TraceRejected } from './trace-zip-limits';
|
|
18
|
+
import { finalAttempt, readPlaywrightReport, ReportResult, ReportTest, scenarioIdOfTest } from './select-final-attempt';
|
|
19
|
+
import { findRunRecords, readRunRecord, RunRecord } from './trace-run-record';
|
|
20
|
+
|
|
21
|
+
export type RunStatus = 'admissible' | 'inconclusive' | 'unproven' | 'refused' | 'unsupported';
|
|
22
|
+
export interface TraceRun {
|
|
23
|
+
scenarioId: string;
|
|
24
|
+
status: RunStatus;
|
|
25
|
+
reason?: string;
|
|
26
|
+
test?: ReportTest;
|
|
27
|
+
result?: ReportResult;
|
|
28
|
+
record?: RunRecord;
|
|
29
|
+
trace?: ParsedTrace;
|
|
30
|
+
/** Project-relative test file (from the run record), once bound. */
|
|
31
|
+
specFile?: string;
|
|
32
|
+
}
|
|
33
|
+
export interface ScenarioEvidence { runs: Map<string, TraceRun[]>; problems: string[]; inputs: string[] }
|
|
34
|
+
|
|
35
|
+
/** The reason a run is not judged once the verdict's deadline has passed (fail closed). */
|
|
36
|
+
export const OVER_BUDGET = 'verdict budget exceeded; run `sungen contract check`';
|
|
37
|
+
const NO_RECORD = "no sungen run record under test-results-free/ (or test-results/) — run the free tests with their own config: outputDir 'test-results-free', trace: 'on' and the sungen run-record reporter";
|
|
38
|
+
|
|
39
|
+
/** The real path of `file` when it is a regular file INSIDE the project (symlinks resolved); else null. */
|
|
40
|
+
export function insideProject(projectRoot: string, file: string): string | null {
|
|
41
|
+
try {
|
|
42
|
+
const root = fs.realpathSync(projectRoot);
|
|
43
|
+
const real = fs.realpathSync(file);
|
|
44
|
+
return real.startsWith(root + path.sep) && fs.statSync(real).isFile() ? real : null;
|
|
45
|
+
} catch { return null; }
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
function resolveAttachment(projectRoot: string, report: string, p: string): { file?: string; outside?: boolean } {
|
|
49
|
+
for (const cand of path.isAbsolute(p) ? [p] : [path.resolve(projectRoot, p), path.resolve(path.dirname(report), p)]) {
|
|
50
|
+
if (!fs.existsSync(cand)) continue;
|
|
51
|
+
const real = insideProject(projectRoot, cand);
|
|
52
|
+
return real ? { file: real } : { outside: true };
|
|
53
|
+
}
|
|
54
|
+
return {};
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
function judge(projectRoot: string, record: RunRecord, test: ReportTest, notBefore: number): TraceRun {
|
|
58
|
+
const scenarioId = scenarioIdOfTest(test.title, test.tags);
|
|
59
|
+
const base = { scenarioId, test, record };
|
|
60
|
+
const fin = finalAttempt(test);
|
|
61
|
+
if (fin.admissibility !== 'admissible') return { ...base, result: fin.result, status: fin.admissibility, reason: fin.reason };
|
|
62
|
+
const { file, outside } = resolveAttachment(projectRoot, test.report, fin.result!.tracePath!);
|
|
63
|
+
if (outside) return { ...base, result: fin.result, status: 'unproven', reason: `the trace ${path.basename(fin.result!.tracePath!)} is outside the project (or not a file) — not read` };
|
|
64
|
+
if (!file) return { ...base, result: fin.result, status: 'unproven', reason: `no trace at ${fin.result!.tracePath} (the report points at a missing file)` };
|
|
65
|
+
let trace: ParsedTrace;
|
|
66
|
+
try { trace = readTraceFile(file); } catch (e) {
|
|
67
|
+
const unsupported = e instanceof TraceRejected && e.code === 'trace.version-unsupported';
|
|
68
|
+
return { ...base, result: fin.result, status: unsupported ? 'unsupported' : 'refused', reason: `trace refused: ${(e as Error).message}` };
|
|
69
|
+
}
|
|
70
|
+
const bound = bindTrace({ projectRoot, test, result: fin.result!, record, trace, notBefore });
|
|
71
|
+
if (bound.ok === false) return { ...base, result: fin.result, trace, status: 'refused', reason: `trace refused: ${bound.reason}` };
|
|
72
|
+
return { ...base, result: fin.result, trace, status: 'admissible', specFile: bound.specFile };
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
/** Every judged run of the tests `want` accepts (derive: a unit's tests; check: by scenario id). */
|
|
76
|
+
export function collectRuns(
|
|
77
|
+
projectRoot: string, want: (id: string, test: ReportTest) => boolean, notBefore: number, deadline = Number.POSITIVE_INFINITY,
|
|
78
|
+
): { runs: TraceRun[]; problems: string[]; inputs: string[]; records: number } {
|
|
79
|
+
const runs: TraceRun[] = [];
|
|
80
|
+
const problems: string[] = [];
|
|
81
|
+
const inputs: string[] = [];
|
|
82
|
+
const records = findRunRecords(projectRoot);
|
|
83
|
+
for (const file of records) {
|
|
84
|
+
let record: RunRecord;
|
|
85
|
+
if (!insideProject(projectRoot, file)) { problems.push(`${path.basename(file)} is outside the project (a symlink?) — not read`); continue; }
|
|
86
|
+
try { record = readRunRecord(file); } catch (e) { problems.push(`${path.basename(file)} is not trusted: ${(e as Error).message}`); continue; }
|
|
87
|
+
if (!record.report) { problems.push(`${path.basename(file)} names no JSON report — add the json reporter to the free-test config`); continue; }
|
|
88
|
+
const reportFile = insideProject(projectRoot, path.resolve(projectRoot, record.report));
|
|
89
|
+
if (!reportFile) { problems.push(`${path.basename(file)}: the report ${path.basename(record.report)} is missing or outside the project — not read`); continue; }
|
|
90
|
+
let tests: ReportTest[];
|
|
91
|
+
try { tests = readPlaywrightReport(reportFile); } catch (e) { problems.push((e as Error).message); continue; }
|
|
92
|
+
inputs.push(`${record.runId}:${record.report}`);
|
|
93
|
+
for (const test of tests) {
|
|
94
|
+
const id = scenarioIdOfTest(test.title, test.tags);
|
|
95
|
+
if (!want(id, test)) continue;
|
|
96
|
+
runs.push(Date.now() > deadline ? { scenarioId: id, test, record, status: 'unproven', reason: OVER_BUDGET } : judge(projectRoot, record, test, notBefore));
|
|
97
|
+
}
|
|
98
|
+
}
|
|
99
|
+
return { runs, problems, inputs, records: records.length };
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
export function collectScenarioEvidence(projectRoot: string, ids: ReadonlySet<string>, notBefore: number, deadline?: number): ScenarioEvidence {
|
|
103
|
+
const found = collectRuns(projectRoot, (id) => ids.has(id), notBefore, deadline);
|
|
104
|
+
const runs = new Map<string, TraceRun[]>([...ids].map((id) => [id, found.runs.filter((r) => r.scenarioId === id)]));
|
|
105
|
+
for (const [id, list] of runs) {
|
|
106
|
+
if (!list.length) list.push({ scenarioId: id, status: 'unproven', reason: found.records ? `no test for ${id} in the recorded run(s)` : NO_RECORD });
|
|
107
|
+
}
|
|
108
|
+
return { runs, problems: found.problems, inputs: found.inputs };
|
|
109
|
+
}
|
|
@@ -0,0 +1,61 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The driver's identity answers, memoized per replayed snapshot: `.sungen/trace-identity/identities.json`.
|
|
3
|
+
*
|
|
4
|
+
* Core cannot run a browser, but the contract verdict (and the Claude Code guard that rebuilds it)
|
|
5
|
+
* must be a synchronous function of what is on disk. So `sungen contract check --trace` /
|
|
6
|
+
* `contract derive` ask `@sungen/driver-ui` once and keep each answer under the sha256 of the EXACT
|
|
7
|
+
* HTML it judged (target marker included). On read:
|
|
8
|
+
*
|
|
9
|
+
* - a malformed file, an unknown key, an entry that is not well-formed → the whole file is ignored
|
|
10
|
+
* - an entry is only ever looked up by the hash of HTML core rendered itself from the trace, so a
|
|
11
|
+
* changed snapshot can never inherit an old answer
|
|
12
|
+
* - anything not found → core's heuristic, which is `low`, which is never `match`
|
|
13
|
+
*
|
|
14
|
+
* This file is derived data, like `.sungen/reports`: deleting it costs a re-resolve, never a pass.
|
|
15
|
+
*/
|
|
16
|
+
import * as fs from 'fs';
|
|
17
|
+
import * as path from 'path';
|
|
18
|
+
import { TRUSTED_RESOLVER, validIdentityResult, TraceIdentityResult } from './target-identity';
|
|
19
|
+
|
|
20
|
+
export const IDENTITY_CACHE_SCHEMA = 1;
|
|
21
|
+
/** A driver answer plus the browser build that gave it. */
|
|
22
|
+
export type CachedIdentity = Omit<TraceIdentityResult, 'key'> & { browser: string };
|
|
23
|
+
export interface IdentityCache { resolver: string; entries: Map<string, CachedIdentity> }
|
|
24
|
+
|
|
25
|
+
const FILE_KEYS = new Set(['schema', 'resolver', 'entries']);
|
|
26
|
+
const SHA256 = /^[0-9a-f]{64}$/;
|
|
27
|
+
|
|
28
|
+
export const identityCachePath = (projectRoot: string): string => path.join(projectRoot, '.sungen', 'trace-identity', 'identities.json');
|
|
29
|
+
|
|
30
|
+
/** The cache, or an empty one with a reason when there is nothing trustworthy to read. */
|
|
31
|
+
export function readIdentityCache(projectRoot: string): { cache: IdentityCache; problem?: string } {
|
|
32
|
+
const empty: IdentityCache = { resolver: '', entries: new Map() };
|
|
33
|
+
const file = identityCachePath(projectRoot);
|
|
34
|
+
if (!fs.existsSync(file)) return { cache: empty };
|
|
35
|
+
let raw: unknown;
|
|
36
|
+
try { raw = JSON.parse(fs.readFileSync(file, 'utf8')); } catch { return { cache: empty, problem: `${path.basename(file)} is not JSON — ignored` }; }
|
|
37
|
+
const bad = (why: string) => ({ cache: empty, problem: `${path.basename(file)} ${why} — ignored` });
|
|
38
|
+
if (!raw || typeof raw !== 'object' || Array.isArray(raw)) return bad('is not an object');
|
|
39
|
+
const doc = raw as Record<string, unknown>;
|
|
40
|
+
if (Object.keys(doc).some((k) => !FILE_KEYS.has(k))) return bad('has an unknown key');
|
|
41
|
+
if (doc.schema !== IDENTITY_CACHE_SCHEMA || typeof doc.resolver !== 'string' || !doc.entries || typeof doc.entries !== 'object') return bad('is not schema 1');
|
|
42
|
+
if (!TRUSTED_RESOLVER.test(doc.resolver)) return bad(`was not written by the driver-ui resolver ("${doc.resolver.slice(0, 40)}")`);
|
|
43
|
+
const entries = new Map<string, CachedIdentity>();
|
|
44
|
+
for (const [sha, value] of Object.entries(doc.entries as Record<string, unknown>)) {
|
|
45
|
+
const { browser, ...answer } = (value && typeof value === 'object' ? value : {}) as Record<string, unknown>;
|
|
46
|
+
const ok = SHA256.test(sha) && typeof browser === 'string' && browser.length > 0 && browser.length <= 100 ? validIdentityResult({ ...answer, key: sha }) : null;
|
|
47
|
+
if (!ok) return bad(`has a malformed entry (${sha.slice(0, 12)})`);
|
|
48
|
+
const { key: _key, ...rest } = ok;
|
|
49
|
+
entries.set(sha, { ...rest, browser: browser as string });
|
|
50
|
+
}
|
|
51
|
+
return { cache: { resolver: doc.resolver, entries } };
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
/** Write the cache with exactly `entries` (callers merge; answers are keyed by the HTML they judged). */
|
|
55
|
+
export function writeIdentityCache(projectRoot: string, resolver: string, entries: Map<string, CachedIdentity>): string {
|
|
56
|
+
const file = identityCachePath(projectRoot);
|
|
57
|
+
fs.mkdirSync(path.dirname(file), { recursive: true });
|
|
58
|
+
const sorted = Object.fromEntries([...entries.entries()].sort(([a], [b]) => a.localeCompare(b)));
|
|
59
|
+
fs.writeFileSync(file, `${JSON.stringify({ schema: IDENTITY_CACHE_SCHEMA, resolver, entries: sorted }, null, 2)}\n`);
|
|
60
|
+
return file;
|
|
61
|
+
}
|
|
@@ -0,0 +1,70 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Playwright's JSON report → each test's attempts, and which one may speak (DOS-14.6; phase 07
|
|
3
|
+
* criteria 3–5). Traces are mapped THROUGH the report — `merge-reports` renames trace files to
|
|
4
|
+
* `resources/<sha1>.zip`, so a directory name is never trusted to say which test a trace is.
|
|
5
|
+
*
|
|
6
|
+
* the final attempt (highest retry) passed, with a trace admissible (binding still to check)
|
|
7
|
+
* the final attempt did not pass inconclusive
|
|
8
|
+
* the final attempt has no trace attachment unproven
|
|
9
|
+
*
|
|
10
|
+
* A test's scenario id is its first tag that reads as an id (`@VP-LOGIN-001`), else the title's
|
|
11
|
+
* leading id, else the whole title — the same rule the contract uses for a scenario name.
|
|
12
|
+
*/
|
|
13
|
+
import * as fs from 'fs';
|
|
14
|
+
import { isIdLike } from '../../harness/parse';
|
|
15
|
+
|
|
16
|
+
export interface ReportResult { retry: number; status: string; startTime: string; duration: number; tracePath: string | null }
|
|
17
|
+
export interface ReportTest { testId: string; title: string; file: string; line: number; tags: string[]; project: string; results: ReportResult[]; report: string }
|
|
18
|
+
export type Admissibility = 'admissible' | 'inconclusive' | 'unproven';
|
|
19
|
+
|
|
20
|
+
const isObj = (v: unknown): v is Record<string, unknown> => !!v && typeof v === 'object' && !Array.isArray(v);
|
|
21
|
+
const arr = (v: unknown): unknown[] => (Array.isArray(v) ? v : []);
|
|
22
|
+
|
|
23
|
+
function* specsOf(suite: unknown): Generator<Record<string, unknown>> {
|
|
24
|
+
if (!isObj(suite)) return;
|
|
25
|
+
for (const s of arr(suite.specs)) if (isObj(s)) yield s;
|
|
26
|
+
for (const c of arr(suite.suites)) yield* specsOf(c);
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
function resultOf(r: unknown): ReportResult | null {
|
|
30
|
+
if (!isObj(r) || !Number.isInteger(r.retry) || typeof r.status !== 'string') return null;
|
|
31
|
+
const trace = arr(r.attachments).find((a) => isObj(a) && a.name === 'trace' && typeof a.path === 'string') as { path: string } | undefined;
|
|
32
|
+
return { retry: r.retry as number, status: r.status, startTime: String(r.startTime ?? ''), duration: Number(r.duration) || 0, tracePath: trace?.path ?? null };
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
/** Every test of a report; throws when the file is not a Playwright JSON report at all. */
|
|
36
|
+
export function readPlaywrightReport(file: string): ReportTest[] {
|
|
37
|
+
let doc: unknown;
|
|
38
|
+
try { doc = JSON.parse(fs.readFileSync(file, 'utf8')); } catch (e) { throw new Error(`the JSON report ${file} is not readable (${(e as Error).message.split('\n')[0]})`); }
|
|
39
|
+
if (!isObj(doc) || !Array.isArray(doc.suites)) throw new Error(`${file} is not a Playwright JSON report (no suites)`);
|
|
40
|
+
const out: ReportTest[] = [];
|
|
41
|
+
for (const top of doc.suites) {
|
|
42
|
+
for (const spec of specsOf(top)) {
|
|
43
|
+
if (typeof spec.id !== 'string' || typeof spec.title !== 'string' || typeof spec.file !== 'string') continue;
|
|
44
|
+
for (const t of arr(spec.tests)) {
|
|
45
|
+
if (!isObj(t)) continue;
|
|
46
|
+
const results = arr(t.results).map(resultOf).filter((r): r is ReportResult => !!r).sort((a, b) => a.retry - b.retry);
|
|
47
|
+
out.push({
|
|
48
|
+
testId: spec.id, title: spec.title, file: spec.file, line: Number(spec.line) || 0,
|
|
49
|
+
tags: arr(spec.tags).map(String), project: String(t.projectName ?? ''), results, report: file,
|
|
50
|
+
});
|
|
51
|
+
}
|
|
52
|
+
}
|
|
53
|
+
}
|
|
54
|
+
return out;
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
export function scenarioIdOfTest(title: string, tags: string[]): string {
|
|
58
|
+
const tag = tags.map((t) => t.replace(/^@/, '')).find(isIdLike);
|
|
59
|
+
if (tag) return tag.toUpperCase();
|
|
60
|
+
const lead = title.match(/^\s*([A-Za-z][A-Za-z0-9.-]*)/)?.[1];
|
|
61
|
+
return lead && isIdLike(lead) ? lead.toUpperCase() : title.replace(/\s+/g, ' ').trim();
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
export function finalAttempt(t: ReportTest): { result?: ReportResult; admissibility: Admissibility; reason?: string } {
|
|
65
|
+
const result = t.results[t.results.length - 1];
|
|
66
|
+
if (!result) return { admissibility: 'unproven', reason: 'the report has no attempt for this test' };
|
|
67
|
+
if (result.status !== 'passed') return { result, admissibility: 'inconclusive', reason: `the final attempt (retry ${result.retry}) did not pass (${result.status})` };
|
|
68
|
+
if (!result.tracePath) return { result, admissibility: 'unproven', reason: `no trace for the final attempt (retry ${result.retry}) — free / impl:code tests need trace: 'on'` };
|
|
69
|
+
return { result, admissibility: 'admissible' };
|
|
70
|
+
}
|
|
@@ -0,0 +1,136 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* A frame snapshot → static, INERT HTML for identity replay (DOS-14.5, DOS-14.7). Port of the
|
|
3
|
+
* phase 07 spike renderer (refs resolved per frame; 1684 refs, 0 broken on the real app).
|
|
4
|
+
*
|
|
5
|
+
* state what the viewer's in-page script restores is applied statically (JS is off in the
|
|
6
|
+
* replay): input values, checked / selected, open dialogs and popovers
|
|
7
|
+
* CSS stylesheets are INLINED from the trace's own resources (G1). A sheet that cannot be
|
|
8
|
+
* found is counted: without the page's CSS, accessible names built from `display`
|
|
9
|
+
* spacing or `::before` glyphs come out wrong (3/89 and 9/27 in the spike), so a
|
|
10
|
+
* missing sheet caps identity at `low`
|
|
11
|
+
* inert no scripts, no `on*` handlers, no URL-bearing attribute, CSS `url()` / `@import`
|
|
12
|
+
* stripped, a CSP meta that forbids every fetch, `<base>` / refresh dropped
|
|
13
|
+
*/
|
|
14
|
+
import { attrsOf, FrameSnapshot, isEl, isRef, kidsOf, RefStats, SnapNode, SnapshotStore } from './snapshot-store';
|
|
15
|
+
|
|
16
|
+
export interface RenderStats extends RefStats { stylesheets: number; stylesheetsMissing: number }
|
|
17
|
+
export interface RenderedSnapshot { html: string; stats: RenderStats }
|
|
18
|
+
|
|
19
|
+
const AUTO_CLOSING = new Set(['AREA', 'BASE', 'BR', 'COL', 'COMMAND', 'EMBED', 'HR', 'IMG', 'INPUT', 'KEYGEN', 'LINK', 'MENUITEM', 'META', 'PARAM', 'SOURCE', 'TRACK', 'WBR']);
|
|
20
|
+
const URL_ATTRS = new Set(['src', 'srcset', 'href', 'action', 'formaction', 'poster', 'data', 'background', 'xlink:href', 'ping', 'longdesc', 'cite', 'manifest', 'codebase', 'archive', 'lowsrc', 'dynsrc', 'srcdoc', 'imagesrcset']);
|
|
21
|
+
export const REPLAY_CSP = "default-src 'none'; style-src 'unsafe-inline'; img-src 'none'; font-src 'none'; media-src 'none'; frame-src 'none'; child-src 'none'; object-src 'none'; form-action 'none'; base-uri 'none'; connect-src 'none'; script-src 'none'";
|
|
22
|
+
|
|
23
|
+
const escapeHtml = (s: string): string => s.replace(/&/g, '&').replace(/</g, '<').replace(/>/g, '>');
|
|
24
|
+
const escapeAttr = (s: string): string => String(s).replace(/&/g, '&').replace(/"/g, '"').replace(/</g, '<').replace(/>/g, '>');
|
|
25
|
+
/** A tag name that is only a name — anything else in a trace is markup smuggled into the name. */
|
|
26
|
+
const TAG_NAME = /^[A-Za-z][A-Za-z0-9-]*$/;
|
|
27
|
+
|
|
28
|
+
/** CSS escapes decoded (`\75 ` → `u`, `\r` → `r`) and comments removed — what the CSS parser sees. */
|
|
29
|
+
const decodeCss = (css: string): string => css
|
|
30
|
+
.replace(/\\([0-9a-fA-F]{1,6})[ \t\n\r\f]?/g, (_m, hex: string) => {
|
|
31
|
+
const cp = parseInt(hex, 16);
|
|
32
|
+
return cp > 0 && cp <= 0x10ffff ? String.fromCodePoint(cp) : '�';
|
|
33
|
+
})
|
|
34
|
+
.replace(/\\([^\n0-9a-fA-F])/g, '$1')
|
|
35
|
+
.replace(/\/\*[\s\S]*?\*\//g, '');
|
|
36
|
+
const FETCHING = /url\s*\(|@import|image-set\s*\(/i;
|
|
37
|
+
|
|
38
|
+
const stripFetches = (css: string): string => css
|
|
39
|
+
.replace(/@import[^;]*;?/gi, '')
|
|
40
|
+
.replace(/(-webkit-)?image-set\((?:[^()]|\([^)]*\))*\)/gi, 'none')
|
|
41
|
+
.replace(/url\(\s*(?:"[^"]*"|'[^']*'|[^)]*)\s*\)/gi, 'none');
|
|
42
|
+
|
|
43
|
+
/**
|
|
44
|
+
* CSS with every fetch removed. A URL hidden behind escapes or comments (`u\72l(`, `@\69mport`) is
|
|
45
|
+
* caught on the decoded text, and then the DECODED text is what is kept; CSS that hides nothing
|
|
46
|
+
* stays byte-identical (icon-font `content: "\f101"` escapes matter for accessible names, R4).
|
|
47
|
+
*/
|
|
48
|
+
export function sanitizeCss(css: string): string {
|
|
49
|
+
const decoded = css.includes('\\') || css.includes('/*') ? decodeCss(css) : css;
|
|
50
|
+
const base = decoded !== css && FETCHING.test(decoded) ? decoded : css;
|
|
51
|
+
return stripFetches(base).replace(/<\/style/gi, '<\\/style');
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
/** Static equivalents of the viewer's in-page state restoration, applied to a copy of the attrs. */
|
|
55
|
+
function restoredState(name: string, raw: Record<string, string>): { attrs: Record<string, string>; textarea?: string } {
|
|
56
|
+
const attrs = { ...raw };
|
|
57
|
+
let textarea: string | undefined;
|
|
58
|
+
if (attrs.__playwright_value_ !== undefined) {
|
|
59
|
+
if (name === 'INPUT' && !/^(checkbox|radio|file)$/i.test(attrs.type || '')) attrs.value = attrs.__playwright_value_;
|
|
60
|
+
if (name === 'TEXTAREA') textarea = attrs.__playwright_value_;
|
|
61
|
+
}
|
|
62
|
+
if (attrs.__playwright_checked_ !== undefined) { if (attrs.__playwright_checked_ === 'true') attrs.checked = ''; else delete attrs.checked; }
|
|
63
|
+
if (attrs.__playwright_selected_ !== undefined) { if (attrs.__playwright_selected_ === 'true') attrs.selected = ''; else delete attrs.selected; }
|
|
64
|
+
if (attrs.__playwright_popover_open_ !== undefined) delete attrs.popover;
|
|
65
|
+
if (name === 'DIALOG' && attrs.__playwright_dialog_open_ !== undefined) attrs.open = '';
|
|
66
|
+
return { attrs, textarea };
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
function attrText(name: string, attrs: Record<string, string>): string {
|
|
70
|
+
const out: string[] = [];
|
|
71
|
+
for (const [k, v] of Object.entries(attrs)) {
|
|
72
|
+
const lk = k.toLowerCase();
|
|
73
|
+
if (lk.startsWith('__playwright_') && lk !== '__playwright_target__') continue;
|
|
74
|
+
if (lk.startsWith('on') || !/^[A-Za-z_:][-A-Za-z0-9_:.]*$/.test(k)) continue;
|
|
75
|
+
if (URL_ATTRS.has(lk)) {
|
|
76
|
+
// A link without href loses its role; keep the attribute, drop the URL.
|
|
77
|
+
if ((name === 'A' || name === 'AREA') && lk === 'href') out.push(' href="#"');
|
|
78
|
+
continue;
|
|
79
|
+
}
|
|
80
|
+
out.push(' ', k, '="', escapeAttr(lk === 'style' ? sanitizeCss(String(v)) : String(v)), '"');
|
|
81
|
+
}
|
|
82
|
+
return out.join('');
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
export function renderSnapshot(store: SnapshotStore, snapshot: FrameSnapshot, readResource: (sha1: string) => string | null): RenderedSnapshot {
|
|
86
|
+
const stats: RenderStats = { refs: 0, brokenRefs: 0, stylesheets: 0, stylesheetsMissing: 0 };
|
|
87
|
+
const out: string[] = [];
|
|
88
|
+
const sheets: Array<{ snap: FrameSnapshot; href: string; media?: string }> = [];
|
|
89
|
+
let headSeen = false;
|
|
90
|
+
const visit = (n: SnapNode, snap: FrameSnapshot, parent: string | undefined): void => {
|
|
91
|
+
if (isRef(n)) { const d = store.deref(n, snap, stats); if (d) visit(d.n, d.snap, parent); return; }
|
|
92
|
+
if (typeof n === 'string') { out.push(parent === 'STYLE' ? sanitizeCss(n) : escapeHtml(n)); return; }
|
|
93
|
+
if (!isEl(n) || !TAG_NAME.test(n[0])) return;
|
|
94
|
+
const name = n[0].toUpperCase();
|
|
95
|
+
const raw = attrsOf(n);
|
|
96
|
+
if (name === 'SCRIPT' || name === 'NOSCRIPT' || name === 'BASE' || (name === 'META' && raw['http-equiv'] !== undefined)) return;
|
|
97
|
+
if (name === 'LINK') {
|
|
98
|
+
if ((raw.rel || '').toLowerCase().split(/\s+/).includes('stylesheet') && raw.href) {
|
|
99
|
+
out.push(`<!--SUNGEN_SHEET_${sheets.length}-->`);
|
|
100
|
+
sheets.push({ snap, href: raw.href, media: raw.media });
|
|
101
|
+
}
|
|
102
|
+
return;
|
|
103
|
+
}
|
|
104
|
+
if (name === 'TEMPLATE' && raw.__playwright_style_sheet_ !== undefined) { out.push('<style>', sanitizeCss(raw.__playwright_style_sheet_), '</style>'); return; }
|
|
105
|
+
if (name === 'TEMPLATE' && raw.__playwright_shadow_root_ !== undefined) {
|
|
106
|
+
out.push('<template shadowrootmode="open">');
|
|
107
|
+
kidsOf(n).forEach((k) => visit(k, snap, 'TEMPLATE'));
|
|
108
|
+
out.push('</template>');
|
|
109
|
+
return;
|
|
110
|
+
}
|
|
111
|
+
const { attrs, textarea } = restoredState(name, raw);
|
|
112
|
+
// Only THIS call's target keeps the marker, as `__playwright_target__="<callId>"` (v9 writes "").
|
|
113
|
+
const mark = attrs.__playwright_target__;
|
|
114
|
+
if (mark !== undefined) {
|
|
115
|
+
if (mark === snapshot.callId || (mark === '' && snap.callId === snapshot.callId)) attrs.__playwright_target__ = snapshot.callId;
|
|
116
|
+
else delete attrs.__playwright_target__;
|
|
117
|
+
}
|
|
118
|
+
out.push('<', name, attrText(name, attrs), '>');
|
|
119
|
+
if (name === 'HEAD' && !headSeen) { headSeen = true; out.push(`<meta http-equiv="Content-Security-Policy" content="${REPLAY_CSP}">`); }
|
|
120
|
+
if (textarea !== undefined) out.push(escapeHtml(textarea));
|
|
121
|
+
else kidsOf(n).forEach((k) => visit(k, snap, name));
|
|
122
|
+
if (!AUTO_CLOSING.has(name)) out.push('</', name, '>');
|
|
123
|
+
};
|
|
124
|
+
visit(snapshot.html, snapshot, undefined);
|
|
125
|
+
let html = out.join('');
|
|
126
|
+
sheets.forEach((s, i) => {
|
|
127
|
+
const sha1 = store.stylesheetSha1(s.snap, s.href);
|
|
128
|
+
const css = sha1 ? readResource(sha1) : null;
|
|
129
|
+
if (css === null) stats.stylesheetsMissing++; else stats.stylesheets++;
|
|
130
|
+
const media = s.media ? ` media="${escapeAttr(s.media)}"` : '';
|
|
131
|
+
html = html.replace(`<!--SUNGEN_SHEET_${i}-->`, () => (css === null ? '' : `<style${media}>${sanitizeCss(css)}</style>`));
|
|
132
|
+
});
|
|
133
|
+
const csp = headSeen ? '' : `<meta http-equiv="Content-Security-Policy" content="${REPLAY_CSP}">`;
|
|
134
|
+
const doctype = snapshot.doctype ? `<!DOCTYPE ${escapeHtml(snapshot.doctype)}>` : '';
|
|
135
|
+
return { html: doctype + csp + html, stats };
|
|
136
|
+
}
|