@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
package/src/harness/sensors.ts
CHANGED
|
@@ -12,6 +12,9 @@ import { parse as parseYaml } from 'yaml';
|
|
|
12
12
|
import { ScenarioInfo, ViewpointEntry, idPrefix } from './parse';
|
|
13
13
|
import { readTextFile } from './read-text';
|
|
14
14
|
import { keywordHit, hasKeyword } from './keyword-match';
|
|
15
|
+
import { countedAssertions } from './counted-assertions';
|
|
16
|
+
import { CATALOG_GATE_RULES, GateRules } from './gates/resolve-rule';
|
|
17
|
+
import { ruleVerdict, sensorSeverityOf } from './gates/gate-terms';
|
|
15
18
|
|
|
16
19
|
// Business-critical category keywords (matched by CONTAINMENT against the VP category, so a
|
|
17
20
|
// compound category like LIST-DISPLAY / ADD-TO-CART / PRODUCT-DISCOVERY classifies correctly).
|
|
@@ -275,6 +278,8 @@ export function assertionDepth(
|
|
|
275
278
|
scenarios: ScenarioInfo[],
|
|
276
279
|
dataThemes: CatalogTheme[] = [],
|
|
277
280
|
focus = 'functional',
|
|
281
|
+
/** The unit's gate rules; the catalog alone (today's levels) by default. */
|
|
282
|
+
rules: GateRules = CATALOG_GATE_RULES,
|
|
278
283
|
): DepthResult {
|
|
279
284
|
const nonManual = scenarios.filter((s) => !s.manual);
|
|
280
285
|
const shallow = nonManual.filter((s) => s.shallow);
|
|
@@ -298,8 +303,7 @@ export function assertionDepth(
|
|
|
298
303
|
const ratio = required.length ? 1 - reqShallow.length / required.length : 1;
|
|
299
304
|
|
|
300
305
|
const threshold = DEPTH_THRESHOLDS[focus] ?? DEPTH_THRESHOLDS.functional;
|
|
301
|
-
|
|
302
|
-
if (ratio < threshold) verdict = WARN_ONLY_FOCUS.has(focus) ? 'warn' : 'fail';
|
|
306
|
+
const verdict = ruleVerdict(rules, 'audit.assertion-depth', ratio < threshold, WARN_ONLY_FOCUS.has(focus));
|
|
303
307
|
|
|
304
308
|
return {
|
|
305
309
|
total: nonManual.length,
|
|
@@ -710,7 +714,10 @@ export interface ClaimUnproven {
|
|
|
710
714
|
claim: string; // the claim keyword matched in the title
|
|
711
715
|
need: string; // the assertion shape the steps must contain
|
|
712
716
|
hint: string; // how to fix
|
|
713
|
-
|
|
717
|
+
/** Resolved from the row's catalog rule (fail = blocks when under the ratio; info only via an override). */
|
|
718
|
+
severity: 'fail' | 'warn' | 'info';
|
|
719
|
+
/** The gate rule that decided `severity` (`claim.*`, or `claim.partial-proof`). */
|
|
720
|
+
rule: string;
|
|
714
721
|
}
|
|
715
722
|
|
|
716
723
|
export interface ClaimProofResult {
|
|
@@ -725,16 +732,17 @@ export interface ClaimProofResult {
|
|
|
725
732
|
}
|
|
726
733
|
|
|
727
734
|
interface ClaimRule {
|
|
735
|
+
/** The catalog rule (`claim.*`) whose resolved severity a missing proof gets (OES-11.2). */
|
|
736
|
+
rule: string;
|
|
728
737
|
claim: string;
|
|
729
738
|
title: RegExp; // claim keyword(s) in the title
|
|
730
739
|
proof: RegExp; // required assertion shape in the steps
|
|
731
740
|
need: string;
|
|
732
741
|
hint: string;
|
|
733
|
-
severity: 'fail' | 'warn';
|
|
734
742
|
// Optional: even when `proof` matches, if `underproof` also matches the proof is
|
|
735
|
-
// PARTIAL (the right shape exists but on the wrong/another element) → a
|
|
736
|
-
//
|
|
737
|
-
// but asserts the actions with singular presence.
|
|
743
|
+
// PARTIAL (the right shape exists but on the wrong/another element) → a finding at the
|
|
744
|
+
// `claim.partial-proof` rule's level. e.g. an "each card exposes <action>" claim that has
|
|
745
|
+
// a see-all on price but asserts the actions with singular presence.
|
|
738
746
|
underproof?: RegExp;
|
|
739
747
|
underNeed?: string;
|
|
740
748
|
underHint?: string;
|
|
@@ -748,29 +756,30 @@ const CLAIM_RULES: ClaimRule[] = [
|
|
|
748
756
|
// catch, so proving it with a substring proves the opposite of what the title says. Found on
|
|
749
757
|
// a real basic flow whose seven confirmation rows all used `contains` while claiming the six
|
|
750
758
|
// values "survive unchanged" (#599).
|
|
759
|
+
rule: 'claim.unchanged-preserved',
|
|
751
760
|
claim: 'unchanged/preserved',
|
|
752
761
|
title: /\b(unchanged|unmodified|un-?edited|identical|preserved|intact|verbatim|as entered|without (?:any )?(?:change|modification))\b/,
|
|
753
762
|
proof: /^(?![\s\S]*\bcontains \{\{)/,
|
|
754
763
|
need: 'exact-value assertions (`see [X] text with {{v}}` / `field with {{v}}`) for the values the title says are unchanged',
|
|
755
764
|
hint: 'swap `contains {{v}}` for `with {{v}}` on the continuity rows. A substring match cannot distinguish "unchanged" from "changed by adding a prefix" — and if the screen genuinely renders extra text around the value, say so in the title instead of claiming exactness.',
|
|
756
|
-
severity: 'warn',
|
|
757
765
|
},
|
|
758
766
|
{
|
|
759
767
|
// An ORDER claim ("sorted by date descending", "ties broken by last-updated") is invisible to
|
|
760
768
|
// a presence check: `see [List] table` passes whatever order the rows are in. It sits ahead of
|
|
761
769
|
// the generic rules so "all results sorted …" is judged as an ordering claim, not an all-claim.
|
|
762
770
|
// A tiebreaker is a Tier-1 guard in the tc-generation skill, yet no rule recognised it (#810).
|
|
771
|
+
rule: 'claim.ordering-sort',
|
|
763
772
|
claim: 'ordering/sort',
|
|
764
773
|
title: /\b(ascending|descending|sorted|sorts|sort order|ordered by|order by|in (?:chronological|alphabetical|reverse|numeric) order|(?:newest|oldest|latest|earliest|highest|lowest) first|tie-?break(?:s|er|ers)?|ties?)\b|降順|昇順|並び順|giảm dần|tăng dần|sắp xếp/,
|
|
765
774
|
proof: /match data|is sorted|sorted (?:ascending|descending)|\brow \d+\b|\.first\.|\.last\.|\[\d+\]\.|order by/,
|
|
766
775
|
need: 'an order-sensitive assertion (`table match data:` rows in the expected order, `is sorted ascending|descending`, or positional `row N` checks)',
|
|
767
776
|
hint: 'a presence check passes in any order — assert the rows in their expected sequence (`User see [T] table match data:`), or the column sort state; a tiebreak needs two rows sharing the primary key.',
|
|
768
|
-
severity: 'fail',
|
|
769
777
|
},
|
|
770
778
|
{
|
|
771
779
|
// A LIMIT claim ("never exceeds 10", "at most 5 files") is a count claim in disguise.
|
|
772
780
|
// Ahead of 'no-side-effect', whose negation+effect pattern would otherwise swallow
|
|
773
781
|
// "never exceeds 10 records" and ask for a duplicate-count proof.
|
|
782
|
+
rule: 'claim.limit-cap',
|
|
774
783
|
claim: 'limit/cap',
|
|
775
784
|
// The quantity must be ≥2 ("at most ONE order" is a no-duplicate claim — left to the rules
|
|
776
785
|
// below) and not a duration ("up to 30 seconds" is a timing claim, not a cap).
|
|
@@ -778,51 +787,50 @@ const CLAIM_RULES: ClaimRule[] = [
|
|
|
778
787
|
proof: /\bcount\b|tohavecount|table with|row with \{\{|match data|is at most|\bexactly\b|field with \{\{|\berror\b|invalid|is disabled/,
|
|
779
788
|
need: 'a count/boundary assertion (count is N, `is at most`, the N+1th input rejected or truncated)',
|
|
780
789
|
hint: 'assert the boundary itself — the count at the cap, or the value/error when one more is attempted — not just that the list or field is present.',
|
|
781
|
-
severity: 'fail',
|
|
782
790
|
},
|
|
783
791
|
{
|
|
792
|
+
rule: 'claim.all-every-each',
|
|
784
793
|
claim: 'all/every/each',
|
|
785
794
|
title: /\b(all|every|each)\b/,
|
|
786
795
|
proof: /\bsee all\b/,
|
|
787
796
|
need: 'a `User see all …` assertion (every element, not one)',
|
|
788
797
|
hint: 'assert across all matched elements — for per-card structure use the all-card DSL (roadmap Q2).',
|
|
789
|
-
severity: 'fail',
|
|
790
798
|
// singular action presence under an "each/every" claim = partial proof
|
|
791
799
|
underproof: /\bsee \[[^\]]+\] (link|button)\b/,
|
|
792
800
|
underNeed: 'an all-card assertion for the claimed action (not single presence)',
|
|
793
801
|
underHint: 'the actions are asserted once, not per-card — wrap them with the all-card DSL `User see all [Card] contain [Action]` (roadmap Q2).',
|
|
794
802
|
},
|
|
795
803
|
{
|
|
804
|
+
rule: 'claim.only-belong-to',
|
|
796
805
|
claim: 'only/belong-to',
|
|
797
806
|
title: /\b(only|belongs? to|exclusively)\b/,
|
|
798
807
|
proof: /\bsee all\b[^.]*\bcontain/,
|
|
799
808
|
need: 'all-match (`User see all … contains …`)',
|
|
800
809
|
hint: 'all displayed items must match the facet; "no items OUTSIDE" is UI-limited → verify via hybrid API/DB or mark @manual with reason.',
|
|
801
|
-
severity: 'warn',
|
|
802
810
|
},
|
|
803
811
|
{
|
|
812
|
+
rule: 'claim.single-exactly-one',
|
|
804
813
|
claim: 'single/exactly-one',
|
|
805
814
|
title: /\b(a single|single|exactly one|just one|only one)\b/,
|
|
806
815
|
proof: /\bcount\b|\btable with\b|\bexactly\b|tohavecount/,
|
|
807
816
|
need: 'a count assertion (e.g. count is 1)',
|
|
808
817
|
hint: 'assert the quantity, not just presence.',
|
|
809
|
-
severity: 'fail',
|
|
810
818
|
},
|
|
811
819
|
{
|
|
820
|
+
rule: 'claim.correct-matching-identity',
|
|
812
821
|
claim: 'correct/matching/identity',
|
|
813
822
|
title: /\b(correct|matching|the same|same (product|name|price|value|item)|consistency|identity|lands? in|is the (product|item))\b/,
|
|
814
823
|
proof: /\bremember\b/,
|
|
815
824
|
need: 'capture + compare (`User remember … as {{v}}` then assert {{v}})',
|
|
816
825
|
hint: 'remember the value on the source, then assert it on the target (cross-screen → defer to a flow as @manual).',
|
|
817
|
-
severity: 'fail',
|
|
818
826
|
},
|
|
819
827
|
{
|
|
828
|
+
rule: 'claim.changes-toggles',
|
|
820
829
|
claim: 'changes/toggles',
|
|
821
830
|
title: /\b(changes?|toggles?|updates?|reflects?|reopens?)\b/,
|
|
822
831
|
proof: /\bremember\b|\bis hidden\b/,
|
|
823
832
|
need: 'a state delta (before ≠ after, or a visible→hidden contrast)',
|
|
824
833
|
hint: 'capture the before-state and assert the after-state differs, or assert the visible/hidden transition.',
|
|
825
|
-
severity: 'warn',
|
|
826
834
|
},
|
|
827
835
|
{
|
|
828
836
|
// GENERAL — mutation-absence. A title asserts that a STATE-CHANGING action does NOT
|
|
@@ -833,6 +841,7 @@ const CLAIM_RULES: ClaimRule[] = [
|
|
|
833
841
|
// count/contrast (record count unchanged) or defer to @manual. This is the general
|
|
834
842
|
// category behind "browser back does not re-submit", "does not re-charge the card",
|
|
835
843
|
// "double-click does not create two orders" — not a per-feature keyword.
|
|
844
|
+
rule: 'claim.no-side-effect',
|
|
836
845
|
claim: 'no-side-effect/no-duplicate',
|
|
837
846
|
// The negation must attach to an EFFECT — something created, sent, stored, charged, fired
|
|
838
847
|
// twice — not to the INPUT being refused. "duplicate address is rejected" is an observable
|
|
@@ -850,33 +859,33 @@ const CLAIM_RULES: ClaimRule[] = [
|
|
|
850
859
|
proof: /\bcount\b|ok_count|status_counts|row with \{\{|table with|tohavecount|is hidden|are hidden|is disabled|are disabled|not complete|no longer/,
|
|
851
860
|
need: 'a record/request-count proof (count stays at one, e.g. `User see [Table] row with {{count}}`, an API `{{name.ok_count}}` invariant, or a `@query` DB count) or @manual with a request-count oracle',
|
|
852
861
|
hint: 'a "does-not-happen / does-not-repeat" claim about a state-changing action is NOT proven by a terminal `see [...] page` — that page is identical whether or not the action (re-)fired. Prove the side-effect count is unchanged, or mark @manual with a setup→action→assert-no-duplicate oracle.',
|
|
853
|
-
severity: 'fail',
|
|
854
862
|
},
|
|
855
863
|
{
|
|
864
|
+
rule: 'claim.hidden-rejected',
|
|
856
865
|
claim: 'hidden/rejected/not-complete',
|
|
857
866
|
title: /\b(hidden|closed|dismiss(es|ed)?|not complete|rejected|inert)\b/,
|
|
858
867
|
proof: /\bis hidden\b|\bare hidden\b|message is hidden|not complete|\bhidden\b/,
|
|
859
868
|
need: 'a negative / hidden assertion (`… is hidden`)',
|
|
860
869
|
hint: 'assert the absence/hidden state that the title claims, not just an unrelated visible element.',
|
|
861
|
-
severity: 'fail',
|
|
862
870
|
},
|
|
863
871
|
{
|
|
872
|
+
rule: 'claim.cleared-emptied',
|
|
864
873
|
claim: 'cleared/emptied',
|
|
865
874
|
title: /\b(cleared|clears|emptied|empties|reset to empty|wiped)\b/,
|
|
866
875
|
proof: /\bis empty\b|with \{\{empty|with ['"]?['"]?\s*$|\bempty\b/,
|
|
867
876
|
need: 'an empty/cleared assertion after the action (e.g. `field with {{empty_value}}` / `is empty`)',
|
|
868
877
|
hint: 'prove the value is actually gone — return to the screen and assert the field is empty, not just that the action ran.',
|
|
869
|
-
severity: 'fail',
|
|
870
878
|
},
|
|
871
879
|
{
|
|
880
|
+
rule: 'claim.restored-preserved',
|
|
872
881
|
claim: 'restored/preserved',
|
|
873
882
|
title: /\b(restored|preserved|persists?|retained|remembered|kept)\b/,
|
|
874
883
|
proof: /\bremember\b|with \{\{|field with/,
|
|
875
884
|
need: 'the value re-asserted after the transition (capture or `field with {{v}}` after returning)',
|
|
876
885
|
hint: 'prove the value survives — assert the field still holds the typed value after the reload/return, not just that it was typed.',
|
|
877
|
-
severity: 'warn',
|
|
878
886
|
},
|
|
879
887
|
{
|
|
888
|
+
rule: 'claim.independent-separate',
|
|
880
889
|
claim: 'independent/separate',
|
|
881
890
|
title: /\b(independent|separate|isolat(ed|es)|per[- ]tab|two tabs|each tab)\b/,
|
|
882
891
|
// A storage `does not exist` check on the OTHER tenant's key proves isolation
|
|
@@ -885,23 +894,22 @@ const CLAIM_RULES: ClaimRule[] = [
|
|
|
885
894
|
proof: /\bcontext\b|tab a|tab b|second (tab|context)|does not exist/,
|
|
886
895
|
need: 'a multi-context proof (tab A vs tab B)',
|
|
887
896
|
hint: 'independence across tabs/contexts is rarely DSL-expressible — mark @manual with a clear setup/action/oracle.',
|
|
888
|
-
severity: 'warn',
|
|
889
897
|
},
|
|
890
898
|
{
|
|
899
|
+
rule: 'claim.sanitized-inert',
|
|
891
900
|
claim: 'sanitized/inert',
|
|
892
901
|
title: /\b(sanitized|sanitised|escaped|inert|not executed|not rendered|stripped)\b/,
|
|
893
902
|
proof: /field with \{\{|payload|inert|toContainText|is hidden/,
|
|
894
903
|
need: 'the payload echoed as inert text (`field with {{payload}}`) + no execution',
|
|
895
904
|
hint: 'prove the payload round-trips as literal text and triggers nothing — assert the field value and the absence of any effect.',
|
|
896
|
-
severity: 'warn',
|
|
897
905
|
},
|
|
898
906
|
{
|
|
907
|
+
rule: 'claim.announces-aria',
|
|
899
908
|
claim: 'announces/aria',
|
|
900
909
|
title: /\b(announce[sd]?|aria|screen[- ]reader|programmatically associated)\b/,
|
|
901
910
|
proof: /aria|role|@manual|describedby|is focused|tab order/,
|
|
902
911
|
need: 'an aria/role assertion (or @manual with a screen-reader oracle)',
|
|
903
912
|
hint: 'ARIA announcement is usually not DSL-expressible — assert aria attributes if possible, else @manual with an NVDA/VoiceOver oracle.',
|
|
904
|
-
severity: 'warn',
|
|
905
913
|
},
|
|
906
914
|
{
|
|
907
915
|
// GENERAL CATCH-ALL (last) — any negative/absence title not handled by a specific
|
|
@@ -911,6 +919,7 @@ const CLAIM_RULES: ClaimRule[] = [
|
|
|
911
919
|
// only a positive presence. WARN, because a positive proxy is sometimes a valid
|
|
912
920
|
// negative proof (e.g. "stayed on the login page"); the semantic reviewer is the
|
|
913
921
|
// authoritative recall layer for the residue this can't judge structurally.
|
|
922
|
+
rule: 'claim.negative-absence',
|
|
914
923
|
claim: 'negative-claim/absence',
|
|
915
924
|
title: /(\bno\b|\bnot\b|n['’]t\b|\bnever\b|\bwithout\b|\bcannot\b|prevent|block|avoid|reject|disabl|\bdeny\b|denies|\bkhông\b|\bchưa\b)/i,
|
|
916
925
|
// `does not exist` covers the storage-absence shape — the broad "any storage
|
|
@@ -920,7 +929,6 @@ const CLAIM_RULES: ClaimRule[] = [
|
|
|
920
929
|
proof: /is hidden|are hidden|is empty|no longer|not complete|disabl|invalid|rejected|\berror\b|\bcount\b|row with \{\{|table with|\bremember\b|\bexactly\b|tohavecount|does not exist|tab order/i,
|
|
921
930
|
need: 'a proof of the ABSENCE — a contrast/empty/hidden/error/count assertion, or @manual with an oracle',
|
|
922
931
|
hint: 'a negative claim ("no / not / không …") is not proven by a positive `see [X]` that looks the same whether or not the claim holds. Assert the contrast (state hidden/empty, error shown, count unchanged), or mark @manual.',
|
|
923
|
-
severity: 'warn',
|
|
924
932
|
},
|
|
925
933
|
];
|
|
926
934
|
|
|
@@ -947,7 +955,14 @@ export interface TaxonomyResult {
|
|
|
947
955
|
|
|
948
956
|
const TAXONOMY_KNOWN = new Set(['LIST', 'PRODUCT', 'CART', 'FILTER', 'NAV', 'VAL', 'SEC']);
|
|
949
957
|
|
|
950
|
-
|
|
958
|
+
/**
|
|
959
|
+
* Words of ACCESS — a title about who may reach something. They make the robustness/routing words
|
|
960
|
+
* below security evidence ("deep link without authentication"); alone those words describe routing.
|
|
961
|
+
*/
|
|
962
|
+
const ACCESS_CONTEXT = /\b(xss|sql|injection|without authentication|unauthenticated|unauthori[sz]ed|without an? (?:authenticated|valid|active) \w+|without (?:permission|login|logging in|signing in|an? account|access)|not (?:logged|signed) in|logged[- ]out|direct(?:ly)? access\w*|auth[- ]?guard|forbidden|\b403\b|(?:no|insufficient|missing) permissions?|permission required|session expired|expired session|tamper\w*)\b/;
|
|
963
|
+
|
|
964
|
+
/** `requires`: the signal counts only when the title ALSO matches it (#888). */
|
|
965
|
+
const CATEGORY_SIGNALS: { cat: string; re: RegExp; requires?: RegExp }[] = [
|
|
951
966
|
{ cat: 'FILTER', re: /\b(categor(y|ies)|sub-?categor\w*|expand\w*|collaps\w*|\bfacet\b|filter\w*|belongs? to)\b/ },
|
|
952
967
|
{ cat: 'CART', re: /\b(add(ed|ing)? to cart|added (dialog|modal|message|confirmation)|cart (page|table|line|item)|view cart|continue shopping|quantit)\b/ },
|
|
953
968
|
{ cat: 'PRODUCT', re: /\b(product detail|product-detail|view product|product page|same product|product name and price|product identity|detail product)\b/ },
|
|
@@ -959,9 +974,16 @@ const CATEGORY_SIGNALS: { cat: string; re: RegExp }[] = [
|
|
|
959
974
|
// SEC, so the linter told QA to re-tag genuine auth-gate tests as navigation — diluting the
|
|
960
975
|
// visibility of the security suite (QA field report, 3.2.23). The guard vocabulary below is
|
|
961
976
|
// what makes the INTENT matchable, and GUARD_INTENT then wins over a mere observable.
|
|
962
|
-
{ cat: 'SEC', re: /\b(xss|sql|injection|payload|without authentication|unauthenticated|without an? (?:authenticated|valid|active) \w+|direct(?:ly)? access\w*|
|
|
977
|
+
{ cat: 'SEC', re: /\b(xss|sql|injection|payload|without authentication|unauthenticated|without an? (?:authenticated|valid|active) \w+|direct(?:ly)? access\w*|auth[- ]?guard|not skippable|without having (?:completed|submitted|logged)|remains? unauthenticated|\binert\b|tamper\w*|malformed|special character|abuse)\b/ },
|
|
978
|
+
// Robustness and routing words are SEC evidence only beside an ACCESS word. Alone they describe
|
|
979
|
+
// routing — "Unknown route shows the 404 page", "Deep link opens the item", "the tab from the url
|
|
980
|
+
// parameter" — and the linter told QA to re-tag those navigation tests as VP-SEC (#888).
|
|
981
|
+
{ cat: 'SEC', re: /\b(deep[- ]link\w*|url parameter|query param\w*|falls? back|gracefull?y|without a crash|not[- ]found|\b404\b)\b/, requires: ACCESS_CONTEXT },
|
|
963
982
|
];
|
|
964
983
|
|
|
984
|
+
const signalMatches = (c: { re: RegExp; requires?: RegExp }, title: string): boolean =>
|
|
985
|
+
c.re.test(title) && (!c.requires || c.requires.test(title));
|
|
986
|
+
|
|
965
987
|
/**
|
|
966
988
|
* Categories whose signal describes the test's INTENT/risk rather than the observable action.
|
|
967
989
|
* When one of these matches, a competing observable-only category (NAV: "redirects", "navigates")
|
|
@@ -979,14 +1001,19 @@ export function taxonomyLint(scenarios: ScenarioInfo[]): TaxonomyResult {
|
|
|
979
1001
|
if (!cat || !TAXONOMY_KNOWN.has(cat)) continue;
|
|
980
1002
|
checked++;
|
|
981
1003
|
const title = s.name.toLowerCase();
|
|
982
|
-
const matched = [...new Set(CATEGORY_SIGNALS.filter((c) => c
|
|
1004
|
+
const matched = [...new Set(CATEGORY_SIGNALS.filter((c) => signalMatches(c, title)).map((c) => c.cat))];
|
|
983
1005
|
if (matched.includes(cat)) continue; // title supports its own category → fine
|
|
1006
|
+
// A gate (`requires`) only stops a word from SUGGESTING its category. An author who already
|
|
1007
|
+
// chose that category is supported by the word alone: a VP-SEC "Deep link to the filtered
|
|
1008
|
+
// list …" stays SEC instead of being pushed toward FILTER (#888 review).
|
|
1009
|
+
if (CATEGORY_SIGNALS.some((c) => c.cat === cat && c.requires && c.re.test(title))) continue;
|
|
984
1010
|
// The declared category may be an INTENT category whose signal this title spells
|
|
985
1011
|
// differently; never "correct" it toward a category that only names the observable.
|
|
986
1012
|
const suppressed = INTENT_OVER_OBSERVABLE[cat];
|
|
987
1013
|
const others = matched.filter((c) => c !== cat && !(suppressed && suppressed.has(c)));
|
|
988
1014
|
if (others.length === 1) { // unambiguous mismatch
|
|
989
|
-
|
|
1015
|
+
// The entry that MATCHED — a category can have several (SEC: guard words, gated robustness words).
|
|
1016
|
+
const sig = CATEGORY_SIGNALS.find((c) => c.cat === others[0] && signalMatches(c, title))!;
|
|
990
1017
|
const m = title.match(sig.re);
|
|
991
1018
|
mislabeled.push({ name: s.name, current: cat, suggested: others[0], signal: m ? m[0] : others[0] });
|
|
992
1019
|
}
|
|
@@ -994,7 +1021,12 @@ export function taxonomyLint(scenarios: ScenarioInfo[]): TaxonomyResult {
|
|
|
994
1021
|
return { total: scenarios.length, checked, mislabeled };
|
|
995
1022
|
}
|
|
996
1023
|
|
|
997
|
-
|
|
1024
|
+
/**
|
|
1025
|
+
* `rules` resolves each row's catalog rule for the unit being audited (the catalog alone by default —
|
|
1026
|
+
* today's levels). A rule switched `off` by a gate-override is not measured: its unproven claim is
|
|
1027
|
+
* neither counted nor reported (a partial-proof switched off leaves the claim proven).
|
|
1028
|
+
*/
|
|
1029
|
+
export function claimProof(scenarios: ScenarioInfo[], focus = 'functional', rules: GateRules = CATALOG_GATE_RULES): ClaimProofResult {
|
|
998
1030
|
const measured = scenarios.filter((s) => !s.manual);
|
|
999
1031
|
const unproven: ClaimUnproven[] = [];
|
|
1000
1032
|
let withClaims = 0;
|
|
@@ -1003,19 +1035,17 @@ export function claimProof(scenarios: ScenarioInfo[], focus = 'functional'): Cla
|
|
|
1003
1035
|
const title = s.name.toLowerCase();
|
|
1004
1036
|
const rule = CLAIM_RULES.find((r) => r.title.test(title));
|
|
1005
1037
|
if (!rule) continue; // no claim → not measured
|
|
1038
|
+
const miss = !rule.proof.test(s.stepsText)
|
|
1039
|
+
// proof shape entirely absent → the claim's own rule decides the level
|
|
1040
|
+
? { rule: rule.rule, need: rule.need, hint: rule.hint }
|
|
1041
|
+
: rule.underproof && rule.underproof.test(s.stepsText)
|
|
1042
|
+
// right shape exists but only as a partial proof
|
|
1043
|
+
? { rule: 'claim.partial-proof', need: rule.underNeed ?? rule.need, hint: rule.underHint ?? rule.hint }
|
|
1044
|
+
: null;
|
|
1045
|
+
const severity = miss ? sensorSeverityOf(rules.resolve(miss.rule)) : undefined;
|
|
1046
|
+
if (miss && severity === null && miss.rule === rule.rule) continue; // the claim's rule is off for this unit
|
|
1006
1047
|
withClaims++;
|
|
1007
|
-
if (
|
|
1008
|
-
// proof shape entirely absent → fail at the rule's severity
|
|
1009
|
-
unproven.push({ name: s.name, claim: rule.claim, need: rule.need, hint: rule.hint, severity: rule.severity });
|
|
1010
|
-
} else if (rule.underproof && rule.underproof.test(s.stepsText)) {
|
|
1011
|
-
// right shape exists but only as a partial proof → warn
|
|
1012
|
-
unproven.push({
|
|
1013
|
-
name: s.name, claim: rule.claim,
|
|
1014
|
-
need: rule.underNeed ?? rule.need,
|
|
1015
|
-
hint: rule.underHint ?? rule.hint,
|
|
1016
|
-
severity: 'warn',
|
|
1017
|
-
});
|
|
1018
|
-
}
|
|
1048
|
+
if (miss && severity) unproven.push({ name: s.name, claim: rule.claim, need: miss.need, hint: miss.hint, severity, rule: miss.rule });
|
|
1019
1049
|
}
|
|
1020
1050
|
|
|
1021
1051
|
const proven = withClaims - unproven.length;
|
|
@@ -1023,10 +1053,7 @@ export function claimProof(scenarios: ScenarioInfo[], focus = 'functional'): Cla
|
|
|
1023
1053
|
const threshold = DEPTH_THRESHOLDS[focus] ?? DEPTH_THRESHOLDS.functional;
|
|
1024
1054
|
|
|
1025
1055
|
const hasFail = unproven.some((u) => u.severity === 'fail');
|
|
1026
|
-
|
|
1027
|
-
if (ratio < threshold) {
|
|
1028
|
-
verdict = WARN_ONLY_FOCUS.has(focus) || !hasFail ? 'warn' : 'fail';
|
|
1029
|
-
}
|
|
1056
|
+
const verdict = ruleVerdict(rules, 'audit.claim-proof', ratio < threshold, WARN_ONLY_FOCUS.has(focus) || !hasFail);
|
|
1030
1057
|
|
|
1031
1058
|
return { total: measured.length, withClaims, proven, unproven, ratio, focus, threshold, verdict };
|
|
1032
1059
|
}
|
|
@@ -1058,9 +1085,8 @@ export function numericClaimGaps(scenarios: ScenarioInfo[]): Array<{ name: strin
|
|
|
1058
1085
|
if (!m) continue;
|
|
1059
1086
|
const claimed = NUMBER_WORDS[m[1]] ?? Number(m[1]);
|
|
1060
1087
|
if (!Number.isFinite(claimed) || claimed < 2) continue;
|
|
1061
|
-
//
|
|
1062
|
-
|
|
1063
|
-
const asserted = (s.stepsText.match(/\bsee \[[^\]]+\][^|]*?\b(?:with|contains) \{\{/g) ?? []).length;
|
|
1088
|
+
// Values, positive states and indicator presence count; plain text visibility does not (#866).
|
|
1089
|
+
const asserted = countedAssertions(s.stepsText);
|
|
1064
1090
|
if (asserted > 0 && asserted < claimed) out.push({ name: s.name, claimed, asserted, noun: m[2] });
|
|
1065
1091
|
}
|
|
1066
1092
|
return out;
|
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Triage signal library — the ordered message patterns `sungen triage` matches a red scenario's
|
|
3
|
+
* error against. Seeded from the repair rules (`harness/repair.ts`, driver-mobile `mobile-repair.ts`);
|
|
4
|
+
* the patterns are COPIED, not imported, because core never loads a driver at runtime. The parity
|
|
5
|
+
* check in tests/harness/triage-classification.run.ts keeps the environment rules in step.
|
|
6
|
+
*
|
|
7
|
+
* Order matters and is the classification order: environment first (nothing about the test or the
|
|
8
|
+
* app can be judged when the session never started), then test defects (the test never reached the
|
|
9
|
+
* app). Assertions are decided in triage.ts from the parsed Expected/Received, not here.
|
|
10
|
+
*/
|
|
11
|
+
export type SignalClass = 'environment' | 'test-defect';
|
|
12
|
+
|
|
13
|
+
export interface TriageSignal {
|
|
14
|
+
id: string;
|
|
15
|
+
class: SignalClass;
|
|
16
|
+
match: RegExp;
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
export const TRIAGE_SIGNALS: TriageSignal[] = [
|
|
20
|
+
// --- environment: the run could not reach a usable app/session -----------------------------------
|
|
21
|
+
{ id: 'env-network', class: 'environment', match: /net::ERR_[A-Z_]+|\bECONNREFUSED\b|\bENOTFOUND\b|\bEAI_AGAIN\b|\bETIMEDOUT\b/ },
|
|
22
|
+
// mirrors mobile-session-not-created
|
|
23
|
+
{ id: 'env-session-not-created', class: 'environment', match: /A new session could not be created|session not created/i },
|
|
24
|
+
// mirrors mobile-driver-proxy-lost
|
|
25
|
+
{ id: 'env-driver-proxy-lost', class: 'environment', match: /Could not proxy command to (?:the )?remote server|socket hang up|ECONNRESET/ },
|
|
26
|
+
// mirrors mobile-instrumentation-failed
|
|
27
|
+
{ id: 'env-instrumentation-failed', class: 'environment', match: /instrumentation process (?:cannot|could not) be (?:initialized|started)|Cannot start the instrumentation process/ },
|
|
28
|
+
// mirrors mobile-chromedriver-mismatch
|
|
29
|
+
{ id: 'env-chromedriver-mismatch', class: 'environment', match: /No Chromedriver found|chromedriver.*(?:version|mismatch)|Chrome version must be/i },
|
|
30
|
+
// mirrors mobile-webview-context-absent
|
|
31
|
+
{ id: 'env-webview-context-absent', class: 'environment', match: /no WEBVIEW\/CHROMIUM context appeared within/ },
|
|
32
|
+
{ id: 'env-auth-state-missing', class: 'environment', match: /storageState|\.auth\/[\w.-]+\.json|ENOENT[^\n]*\.auth/ },
|
|
33
|
+
{ id: 'env-base-url-unresolved', class: 'environment', match: /Cannot navigate to invalid URL|baseURL (?:is )?(?:not set|undefined)|Invalid URL/ },
|
|
34
|
+
{ id: 'env-browser-closed', class: 'environment', match: /Target page, context or browser has been closed|browserType\.launch/ },
|
|
35
|
+
|
|
36
|
+
// --- test defect: the test could not reach the element or its data -------------------------------
|
|
37
|
+
{ id: 'test-strict-mode', class: 'test-defect', match: /strict mode violation/ },
|
|
38
|
+
{ id: 'test-locator-unresolved', class: 'test-defect', match: /locator\.\w+: [^\n]*(?:Timeout|timeout)[^\n]*exceeded[\s\S]*waiting for |resolved to 0 elements/ },
|
|
39
|
+
{ id: 'test-element-absent', class: 'test-defect', match: /no such element|Element not found:|An element could not be located/ },
|
|
40
|
+
{ id: 'test-data-missing', class: 'test-defect', match: /Test data key not found:|@cases dataset "[^"]+" not found/ },
|
|
41
|
+
{ id: 'test-step-unmatched', class: 'test-defect', match: /\[sungen\] step "[^"]*" matched no pattern|\bSG\d{3}\b/ },
|
|
42
|
+
{ id: 'test-tap-intercepted', class: 'test-defect', match: /element click intercepted|is not clickable at point|element is obscured|intercepts pointer events/i },
|
|
43
|
+
];
|
|
44
|
+
|
|
45
|
+
/** A failure that is only Playwright's test-level timeout — no locator, no assertion detail (→ unclassified). */
|
|
46
|
+
export const BARE_TIMEOUT = /^(?:Error:\s*)?Test timeout of \d+ms exceeded\.?$/;
|
|
@@ -0,0 +1,113 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* `sungen triage` IO: find a unit's feature(s) and result file(s), join them on the same seam
|
|
3
|
+
* delivery and the dashboard use (`mergeFeatureAndSpec` + `resolveResultVariants`), classify each
|
|
4
|
+
* scenario, and persist `.sungen/triage/<unit>.json`. Read-only apart from that one report file.
|
|
5
|
+
*/
|
|
6
|
+
import * as fs from 'fs';
|
|
7
|
+
import * as path from 'path';
|
|
8
|
+
import { parseFeatureMetadata } from '../exporters/feature-parser';
|
|
9
|
+
import { loadPlaywrightReport, resolveResultsPath } from '../exporters/playwright-report-parser';
|
|
10
|
+
import { collectQaEnvSecretDenylist } from '../exporters/qa-env-secret-denylist';
|
|
11
|
+
import { isSensitiveKey } from '../exporters/sensitive-key';
|
|
12
|
+
import { resolveResultVariants } from '../exporters/result-variants';
|
|
13
|
+
import { mergeFeatureAndSpec } from '../exporters/scenario-merger';
|
|
14
|
+
import { parseSpecFile } from '../exporters/spec-parser';
|
|
15
|
+
import { loadTestData } from '../exporters/test-data-resolver';
|
|
16
|
+
import type { ResolvedUnit } from '../cli/resolve-unit';
|
|
17
|
+
import { classifyScenario, TriageClass, TriageRow } from './triage';
|
|
18
|
+
|
|
19
|
+
export interface TriageReport {
|
|
20
|
+
unit: string;
|
|
21
|
+
generatedAt: string;
|
|
22
|
+
/** Result files read, relative to the project root. Empty → nothing has run yet. */
|
|
23
|
+
resultFiles: string[];
|
|
24
|
+
counts: Partial<Record<TriageClass, number>>;
|
|
25
|
+
rows: TriageRow[];
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
/**
|
|
29
|
+
* Values that must never reach a triage evidence line or a defect record: the resolved `${QA_*}`
|
|
30
|
+
* secrets AND the literal values of credential-named test-data keys — an assertion on a password
|
|
31
|
+
* field prints that value in its Expected/Received.
|
|
32
|
+
*/
|
|
33
|
+
export function unitSecrets(testData: Record<string, string>, cwd: string): string[] {
|
|
34
|
+
const literal = Object.entries(testData)
|
|
35
|
+
.filter(([k, v]) => isSensitiveKey(k) && typeof v === 'string' && v.length >= 3 && !v.includes('${'))
|
|
36
|
+
.map(([, v]) => String(v));
|
|
37
|
+
return [...new Set([...collectQaEnvSecretDenylist(testData, cwd).values, ...literal])].sort((a, b) => b.length - a.length);
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
function generatedDir(cwd: string, u: ResolvedUnit): string {
|
|
41
|
+
return path.join(cwd, 'specs', 'generated', u.unitId);
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
/**
|
|
45
|
+
* Every result file of one feature, labelled by variant: `<base>-test-result.json` (no label),
|
|
46
|
+
* `<base>-test-result.<os|env>[.<env>].json` (label = the middle part). A mobile unit has only
|
|
47
|
+
* per-OS files, so all of them are read rather than the first one found. Falls back to the shared
|
|
48
|
+
* lookup (incl. `test-results/results.json`) when the generated dir holds none.
|
|
49
|
+
*/
|
|
50
|
+
function resultFilesFor(cwd: string, genDir: string, base: string): Array<{ file: string; variant?: string }> {
|
|
51
|
+
const prefix = `${base}-test-result`;
|
|
52
|
+
const own = fs.existsSync(genDir)
|
|
53
|
+
? fs.readdirSync(genDir).filter((f) => f.startsWith(prefix) && f.endsWith('.json')).sort()
|
|
54
|
+
: [];
|
|
55
|
+
if (own.length > 0) {
|
|
56
|
+
return own.map((f) => {
|
|
57
|
+
const variant = f.slice(prefix.length, -'.json'.length).replace(/^\./, '');
|
|
58
|
+
return { file: path.join(genDir, f), ...(variant ? { variant } : {}) };
|
|
59
|
+
});
|
|
60
|
+
}
|
|
61
|
+
const shared = resolveResultsPath(cwd, genDir, base);
|
|
62
|
+
return shared ? [{ file: shared }] : [];
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
export function triageUnit(cwd: string, u: ResolvedUnit): TriageReport {
|
|
66
|
+
const genDir = generatedDir(cwd, u);
|
|
67
|
+
const featuresDir = path.join(u.dir, 'features');
|
|
68
|
+
const features = fs.existsSync(featuresDir) ? fs.readdirSync(featuresDir).filter((f) => f.endsWith('.feature')).sort() : [];
|
|
69
|
+
const rows: TriageRow[] = [];
|
|
70
|
+
const resultFiles: string[] = [];
|
|
71
|
+
|
|
72
|
+
for (const f of features) {
|
|
73
|
+
const base = f.replace(/\.feature$/, '');
|
|
74
|
+
const files = resultFilesFor(cwd, genDir, base);
|
|
75
|
+
if (files.length === 0) continue;
|
|
76
|
+
const merged = mergeFeatureAndSpec(parseFeatureMetadata(path.join(featuresDir, f)), parseSpecFile(path.join(genDir, `${base}.spec.ts`)));
|
|
77
|
+
const dataFile = path.join(u.dir, 'test-data', `${base}.yaml`);
|
|
78
|
+
const testData = fs.existsSync(dataFile) ? loadTestData(dataFile) : {};
|
|
79
|
+
const secrets = unitSecrets(testData, cwd);
|
|
80
|
+
|
|
81
|
+
for (const { file, variant } of files) {
|
|
82
|
+
resultFiles.push(path.relative(cwd, file));
|
|
83
|
+
const results = loadPlaywrightReport(file);
|
|
84
|
+
for (const m of merged) {
|
|
85
|
+
for (const v of resolveResultVariants(m, results, testData)) {
|
|
86
|
+
const r = classifyScenario({ unit: u.unitId, title: `${m.feature.name}${v.nameSuffix}`, tags: m.feature.tags, result: v.result, variant }, secrets);
|
|
87
|
+
if (r) rows.push(r);
|
|
88
|
+
}
|
|
89
|
+
}
|
|
90
|
+
}
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
const counts: Partial<Record<TriageClass, number>> = {};
|
|
94
|
+
for (const r of rows) counts[r.class] = (counts[r.class] ?? 0) + 1;
|
|
95
|
+
return { unit: u.unitId, generatedAt: new Date().toISOString(), resultFiles, counts, rows };
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
export function triageReportPath(cwd: string, unitId: string): string {
|
|
99
|
+
return path.join(cwd, '.sungen', 'triage', `${unitId.replace(/\//g, '-')}.json`);
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
/** The last triage of a unit, or null when `sungen triage` has not run for it. */
|
|
103
|
+
export function readTriageReport(cwd: string, unitId: string): TriageReport | null {
|
|
104
|
+
try { return JSON.parse(fs.readFileSync(triageReportPath(cwd, unitId), 'utf-8')) as TriageReport; } catch { return null; }
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
/** Persist under `.sungen/triage/<unit>.json` (a flow `flows/x` → `flows-x.json`); returns the path. */
|
|
108
|
+
export function writeTriageReport(cwd: string, report: TriageReport): string {
|
|
109
|
+
const out = triageReportPath(cwd, report.unit);
|
|
110
|
+
fs.mkdirSync(path.dirname(out), { recursive: true });
|
|
111
|
+
fs.writeFileSync(out, `${JSON.stringify(report, null, 2)}\n`, 'utf-8');
|
|
112
|
+
return out;
|
|
113
|
+
}
|
|
@@ -0,0 +1,127 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* `sungen triage` — classify each red or flaky scenario of a run into exactly one class, with the
|
|
3
|
+
* rule that fired and one evidence line. Deterministic and read-only; it reports, it never gates.
|
|
4
|
+
*
|
|
5
|
+
* Order (first match wins):
|
|
6
|
+
* 1. known-defect `@known-defect` / `@known-defect:<id>` — an expected red.
|
|
7
|
+
* 2. blocked-capability `@manual:*` / `@requires:*`, or skipped by design.
|
|
8
|
+
* 3. environment the session or network never gave the test a usable app.
|
|
9
|
+
* 4. test-defect the test could not reach its element or data.
|
|
10
|
+
* 5. product-candidate an assertion whose Expected and Received both exist and differ.
|
|
11
|
+
* 6. unclassified anything else — left for the agent or QA, with the raw first message.
|
|
12
|
+
* A `passed` scenario that needed a retry is `flaky` (green, listed separately).
|
|
13
|
+
*
|
|
14
|
+
* 5 is tested before 3 and 4 in code: a parsed Expected/Received pair that differs means the app
|
|
15
|
+
* answered, which outranks any wrapper text (a test timeout around the assertion included).
|
|
16
|
+
*
|
|
17
|
+
* A bare test timeout (no locator, no assertion) is `unclassified` too: a slow environment, a page
|
|
18
|
+
* that hangs on a product bug and a test waiting on the wrong thing all print the same line.
|
|
19
|
+
*
|
|
20
|
+
* An assertion on an element that never appeared (`Received: <element(s) not found>`) is NOT a test
|
|
21
|
+
* defect: a wrong selector and a missing message look identical, so it is left for judgment rather
|
|
22
|
+
* than hiding a possible product bug behind "fix the selector".
|
|
23
|
+
*/
|
|
24
|
+
import { redactSecrets } from '../exporters/api-testcase-formatter';
|
|
25
|
+
import { parseExpectedReceived } from '../exporters/playwright-report-parser';
|
|
26
|
+
import type { PlaywrightResult } from '../exporters/types';
|
|
27
|
+
import { BARE_TIMEOUT, TRIAGE_SIGNALS } from './triage-signals';
|
|
28
|
+
|
|
29
|
+
export type TriageClass =
|
|
30
|
+
| 'known-defect' | 'blocked-capability' | 'environment' | 'test-defect' | 'product-candidate' | 'unclassified' | 'flaky';
|
|
31
|
+
|
|
32
|
+
export interface TriageInput {
|
|
33
|
+
unit: string;
|
|
34
|
+
title: string;
|
|
35
|
+
tags: string[];
|
|
36
|
+
result?: PlaywrightResult;
|
|
37
|
+
/** Result-file variant (`android`, `ios`, an env name) when the unit has several. */
|
|
38
|
+
variant?: string;
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
export interface TriageRow {
|
|
42
|
+
unit: string;
|
|
43
|
+
scenario: string;
|
|
44
|
+
variant?: string;
|
|
45
|
+
class: TriageClass;
|
|
46
|
+
ruleId: string;
|
|
47
|
+
evidence: string;
|
|
48
|
+
ids: string[];
|
|
49
|
+
legacy: string[];
|
|
50
|
+
defectId?: string;
|
|
51
|
+
tracePath?: string;
|
|
52
|
+
/** product-candidate only: the two halves of the assertion, for a defect draft. */
|
|
53
|
+
expected?: string;
|
|
54
|
+
received?: string;
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
const EVIDENCE_MAX = 200;
|
|
58
|
+
const VIEWPOINT_ID = /\b(?:VP|FL)-[A-Z0-9]+(?:-[A-Z0-9]+)*\b/gi;
|
|
59
|
+
const ELEMENT_ABSENT = /Received:\s*<element\(s\) not found>/;
|
|
60
|
+
|
|
61
|
+
/** The part of a message the run itself wrote: before `Call log:`, without `Locator:` echo lines. */
|
|
62
|
+
const runHead = (m: string): string => m.split(/\n\s*Call log:/)[0].replace(/^\s*Locator:.*$/gm, '');
|
|
63
|
+
|
|
64
|
+
/** Every failure message, stack frames and a leading `Error:` dropped. */
|
|
65
|
+
function messagesOf(result: PlaywrightResult): string[] {
|
|
66
|
+
const raw = result.errorMessages?.length ? result.errorMessages : [result.error ?? ''];
|
|
67
|
+
return raw.map((m) => m.split(/\n\s*at\s/)[0].replace(/^Error:\s*/i, '').trim()).filter(Boolean);
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
function evidenceLine(text: string, secrets: string[]): string {
|
|
71
|
+
const flat = redactSecrets(text, secrets).replace(/\s+/g, ' ').trim();
|
|
72
|
+
return flat.length > EVIDENCE_MAX ? `${flat.slice(0, EVIDENCE_MAX - 1)}…` : flat;
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
/** Classify one scenario. Null when it needs no triage (clean pass, or never ran). */
|
|
76
|
+
export function classifyScenario(input: TriageInput, secrets: string[] = []): TriageRow | null {
|
|
77
|
+
const { result, tags } = input;
|
|
78
|
+
if (!result) return null;
|
|
79
|
+
const base = {
|
|
80
|
+
unit: input.unit,
|
|
81
|
+
scenario: input.title,
|
|
82
|
+
...(input.variant ? { variant: input.variant } : {}),
|
|
83
|
+
ids: [...new Set([input.title, ...tags].join(' ').match(VIEWPOINT_ID) ?? [])],
|
|
84
|
+
legacy: tags.filter((t) => t.startsWith('@legacy:')).map((t) => t.slice('@legacy:'.length)),
|
|
85
|
+
...(result.tracePath ? { tracePath: result.tracePath } : {}),
|
|
86
|
+
};
|
|
87
|
+
const row = (cls: TriageClass, ruleId: string, evidence: string): TriageRow =>
|
|
88
|
+
({ ...base, class: cls, ruleId, evidence: evidenceLine(evidence, secrets) });
|
|
89
|
+
|
|
90
|
+
if (result.status === 'passed') {
|
|
91
|
+
return (result.attempts ?? 1) > 1 ? row('flaky', 'passed-after-retry', `passed on attempt ${result.attempts}`) : null;
|
|
92
|
+
}
|
|
93
|
+
const messages = messagesOf(result);
|
|
94
|
+
const first = messages[0] ?? result.status;
|
|
95
|
+
|
|
96
|
+
const known = tags.find((t) => t === '@known-defect' || t.startsWith('@known-defect:'));
|
|
97
|
+
if (known) {
|
|
98
|
+
const r = row('known-defect', 'tag-known-defect', first);
|
|
99
|
+
const id = known.split(':')[1];
|
|
100
|
+
return id ? { ...r, defectId: id } : r;
|
|
101
|
+
}
|
|
102
|
+
if (tags.some((t) => t.startsWith('@manual'))) return row('blocked-capability', 'tag-manual', `${tags.find((t) => t.startsWith('@manual'))} — ${first}`);
|
|
103
|
+
if (tags.some((t) => t.startsWith('@requires:'))) return row('blocked-capability', 'tag-requires', `${tags.find((t) => t.startsWith('@requires:'))} — ${first}`);
|
|
104
|
+
if (result.status === 'skipped') return row('blocked-capability', 'status-skipped', 'skipped');
|
|
105
|
+
|
|
106
|
+
// An assertion with both halves decides the case even when a timeout wraps it.
|
|
107
|
+
for (const m of messages) {
|
|
108
|
+
const { expected, received } = parseExpectedReceived(m);
|
|
109
|
+
if (expected && received && !ELEMENT_ABSENT.test(m) && expected !== received) {
|
|
110
|
+
return { ...row('product-candidate', 'assertion-mismatch', `Expected ${expected} · Received ${received}`),
|
|
111
|
+
expected: evidenceLine(expected, secrets), received: evidenceLine(received, secrets) };
|
|
112
|
+
}
|
|
113
|
+
}
|
|
114
|
+
for (const s of TRIAGE_SIGNALS) {
|
|
115
|
+
// Environment signals read only what the RUN said, never the test's own selector text that a
|
|
116
|
+
// message echoes in `Locator:` / `Call log:` — `getByText('Invalid URL')` is a missing message,
|
|
117
|
+
// not a bad base URL. Test-defect signals need the call log (`waiting for …`), so they read it all.
|
|
118
|
+
const hit = messages.find((m) => s.match.test(s.class === 'environment' ? runHead(m) : m));
|
|
119
|
+
if (hit) return row(s.class, s.id, hit);
|
|
120
|
+
}
|
|
121
|
+
const absent = messages.find((m) => ELEMENT_ABSENT.test(m));
|
|
122
|
+
if (absent) return row('unclassified', 'assertion-element-absent', absent);
|
|
123
|
+
if (messages.length > 0 && messages.every((m) => BARE_TIMEOUT.test(m))) {
|
|
124
|
+
return row('unclassified', 'bare-timeout', `${first} No locator or assertion detail: a slow environment, a hanging page, or a wait in the test. Re-run once, then read the trace's last action.`);
|
|
125
|
+
}
|
|
126
|
+
return row('unclassified', 'no-signal', first);
|
|
127
|
+
}
|