@sun-asterisk/sungen 3.3.0-alpha.1 → 3.3.0-alpha.10
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/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/cli/case-triage-hint.d.ts +14 -0
- package/dist/cli/case-triage-hint.d.ts.map +1 -0
- package/dist/cli/case-triage-hint.js +23 -0
- package/dist/cli/case-triage-hint.js.map +1 -0
- package/dist/cli/commands/audit.js +1 -1
- package/dist/cli/commands/audit.js.map +1 -1
- package/dist/cli/commands/contract-derive-command.d.ts +4 -2
- package/dist/cli/commands/contract-derive-command.d.ts.map +1 -1
- package/dist/cli/commands/contract-derive-command.js +19 -5
- package/dist/cli/commands/contract-derive-command.js.map +1 -1
- package/dist/cli/commands/contract.d.ts +3 -0
- package/dist/cli/commands/contract.d.ts.map +1 -1
- package/dist/cli/commands/contract.js +40 -7
- package/dist/cli/commands/contract.js.map +1 -1
- package/dist/cli/commands/delivery.d.ts.map +1 -1
- package/dist/cli/commands/delivery.js +59 -18
- package/dist/cli/commands/delivery.js.map +1 -1
- package/dist/cli/commands/gate.d.ts.map +1 -1
- package/dist/cli/commands/gate.js +2 -1
- package/dist/cli/commands/gate.js.map +1 -1
- package/dist/cli/commands/generate.d.ts.map +1 -1
- package/dist/cli/commands/generate.js +14 -2
- 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 +6 -4
- package/dist/cli/commands/journey.js.map +1 -1
- package/dist/cli/commands/next.d.ts +7 -2
- package/dist/cli/commands/next.d.ts.map +1 -1
- package/dist/cli/commands/next.js +41 -10
- package/dist/cli/commands/next.js.map +1 -1
- package/dist/cli/commands/triage.d.ts +2 -1
- package/dist/cli/commands/triage.d.ts.map +1 -1
- package/dist/cli/commands/triage.js +45 -3
- package/dist/cli/commands/triage.js.map +1 -1
- package/dist/cli/commands/update.d.ts.map +1 -1
- package/dist/cli/commands/update.js +6 -2
- package/dist/cli/commands/update.js.map +1 -1
- package/dist/cli/index.js +5 -1
- package/dist/cli/index.js.map +1 -1
- package/dist/cli/json-error-on-exit.d.ts +34 -0
- package/dist/cli/json-error-on-exit.d.ts.map +1 -0
- package/dist/cli/json-error-on-exit.js +63 -0
- package/dist/cli/json-error-on-exit.js.map +1 -0
- package/dist/cli/project-sungen-invocation.d.ts +13 -0
- package/dist/cli/project-sungen-invocation.d.ts.map +1 -0
- package/dist/cli/project-sungen-invocation.js +95 -0
- package/dist/cli/project-sungen-invocation.js.map +1 -0
- package/dist/cli/run-command-hints.d.ts +9 -0
- package/dist/cli/run-command-hints.d.ts.map +1 -0
- package/dist/cli/run-command-hints.js +84 -0
- package/dist/cli/run-command-hints.js.map +1 -0
- package/dist/cli/waiting-for-you-block.d.ts +18 -0
- package/dist/cli/waiting-for-you-block.d.ts.map +1 -0
- package/dist/cli/waiting-for-you-block.js +22 -0
- package/dist/cli/waiting-for-you-block.js.map +1 -0
- package/dist/contract/accept-opaque-steps.d.ts +3 -0
- package/dist/contract/accept-opaque-steps.d.ts.map +1 -0
- package/dist/contract/accept-opaque-steps.js +114 -0
- package/dist/contract/accept-opaque-steps.js.map +1 -0
- package/dist/contract/approve-contract.d.ts +8 -1
- package/dist/contract/approve-contract.d.ts.map +1 -1
- package/dist/contract/approve-contract.js +6 -0
- package/dist/contract/approve-contract.js.map +1 -1
- package/dist/contract/build-contract-verdict.js +2 -2
- package/dist/contract/build-contract-verdict.js.map +1 -1
- package/dist/contract/build-trace-conformance.d.ts +5 -1
- package/dist/contract/build-trace-conformance.d.ts.map +1 -1
- package/dist/contract/build-trace-conformance.js +12 -14
- package/dist/contract/build-trace-conformance.js.map +1 -1
- package/dist/contract/classify-contract-change.d.ts +3 -2
- package/dist/contract/classify-contract-change.d.ts.map +1 -1
- package/dist/contract/classify-contract-change.js.map +1 -1
- package/dist/contract/classify-trace-conformance.d.ts +18 -3
- package/dist/contract/classify-trace-conformance.d.ts.map +1 -1
- package/dist/contract/classify-trace-conformance.js +64 -17
- package/dist/contract/classify-trace-conformance.js.map +1 -1
- package/dist/contract/contract-schema.d.ts +21 -0
- package/dist/contract/contract-schema.d.ts.map +1 -1
- package/dist/contract/contract-schema.js +25 -3
- package/dist/contract/contract-schema.js.map +1 -1
- package/dist/contract/contracts-lock-file.d.ts.map +1 -1
- package/dist/contract/contracts-lock-file.js +1 -0
- package/dist/contract/contracts-lock-file.js.map +1 -1
- package/dist/contract/derive-free-feature.d.ts +19 -0
- package/dist/contract/derive-free-feature.d.ts.map +1 -1
- package/dist/contract/derive-free-feature.js +40 -2
- package/dist/contract/derive-free-feature.js.map +1 -1
- package/dist/contract/project-contract-blockers.d.ts.map +1 -1
- package/dist/contract/project-contract-blockers.js +4 -2
- package/dist/contract/project-contract-blockers.js.map +1 -1
- package/dist/contract/record-contract-approval.d.ts +16 -1
- package/dist/contract/record-contract-approval.d.ts.map +1 -1
- package/dist/contract/record-contract-approval.js +29 -1
- package/dist/contract/record-contract-approval.js.map +1 -1
- package/dist/contract/render-free-gherkin.d.ts +2 -0
- package/dist/contract/render-free-gherkin.d.ts.map +1 -1
- package/dist/contract/render-free-gherkin.js +6 -0
- package/dist/contract/render-free-gherkin.js.map +1 -1
- package/dist/contract/resolve-unit-trace-identities.d.ts +12 -0
- package/dist/contract/resolve-unit-trace-identities.d.ts.map +1 -1
- package/dist/contract/resolve-unit-trace-identities.js +33 -49
- package/dist/contract/resolve-unit-trace-identities.js.map +1 -1
- package/dist/contract/trace/collect-trace-evidence.d.ts +16 -5
- package/dist/contract/trace/collect-trace-evidence.d.ts.map +1 -1
- package/dist/contract/trace/collect-trace-evidence.js +101 -43
- package/dist/contract/trace/collect-trace-evidence.js.map +1 -1
- package/dist/contract/trace/read-file-once.d.ts +20 -0
- package/dist/contract/trace/read-file-once.d.ts.map +1 -0
- package/dist/contract/trace/read-file-once.js +135 -0
- package/dist/contract/trace/read-file-once.js.map +1 -0
- package/dist/contract/trace/scenario-id-claims.d.ts +11 -0
- package/dist/contract/trace/scenario-id-claims.d.ts.map +1 -0
- package/dist/contract/trace/scenario-id-claims.js +89 -0
- package/dist/contract/trace/scenario-id-claims.js.map +1 -0
- package/dist/contract/trace/select-final-attempt.d.ts +4 -2
- package/dist/contract/trace/select-final-attempt.d.ts.map +1 -1
- package/dist/contract/trace/select-final-attempt.js +7 -4
- package/dist/contract/trace/select-final-attempt.js.map +1 -1
- package/dist/contract/trace/test-step-join.d.ts +3 -3
- package/dist/contract/trace/test-step-join.d.ts.map +1 -1
- package/dist/contract/trace/test-step-join.js +2 -2
- package/dist/contract/trace/test-step-join.js.map +1 -1
- package/dist/contract/trace/trace-binding.d.ts +3 -2
- package/dist/contract/trace/trace-binding.d.ts.map +1 -1
- package/dist/contract/trace/trace-binding.js +6 -3
- package/dist/contract/trace/trace-binding.js.map +1 -1
- package/dist/contract/trace/trace-file-reader.d.ts +14 -2
- package/dist/contract/trace/trace-file-reader.d.ts.map +1 -1
- package/dist/contract/trace/trace-file-reader.js +27 -45
- package/dist/contract/trace/trace-file-reader.js.map +1 -1
- package/dist/contract/trace/trace-freshness.d.ts +27 -0
- package/dist/contract/trace/trace-freshness.d.ts.map +1 -0
- package/dist/contract/trace/trace-freshness.js +146 -0
- package/dist/contract/trace/trace-freshness.js.map +1 -0
- package/dist/contract/trace/trace-identity-resolution.d.ts +2 -0
- package/dist/contract/trace/trace-identity-resolution.d.ts.map +1 -1
- package/dist/contract/trace/trace-identity-resolution.js +6 -3
- package/dist/contract/trace/trace-identity-resolution.js.map +1 -1
- package/dist/contract/trace/trace-run-record.d.ts +8 -3
- package/dist/contract/trace/trace-run-record.d.ts.map +1 -1
- package/dist/contract/trace/trace-run-record.js +42 -22
- package/dist/contract/trace/trace-run-record.js.map +1 -1
- package/dist/contract/trace/trace-steps.js +1 -1
- package/dist/contract/trace/trace-steps.js.map +1 -1
- package/dist/contract/trace/trace-zip-limits.d.ts +5 -1
- package/dist/contract/trace/trace-zip-limits.d.ts.map +1 -1
- package/dist/contract/trace/trace-zip-limits.js +6 -2
- package/dist/contract/trace/trace-zip-limits.js.map +1 -1
- package/dist/contract/verify-approval-decisions.d.ts.map +1 -1
- package/dist/contract/verify-approval-decisions.js +9 -4
- package/dist/contract/verify-approval-decisions.js.map +1 -1
- package/dist/decisions/confirmed-decision-commands.d.ts +11 -0
- package/dist/decisions/confirmed-decision-commands.d.ts.map +1 -1
- package/dist/decisions/confirmed-decision-commands.js +21 -3
- package/dist/decisions/confirmed-decision-commands.js.map +1 -1
- package/dist/decisions/decision-schema.d.ts +2 -1
- package/dist/decisions/decision-schema.d.ts.map +1 -1
- package/dist/decisions/decision-schema.js +2 -1
- package/dist/decisions/decision-schema.js.map +1 -1
- package/dist/decisions/git-head-trust.d.ts +12 -0
- package/dist/decisions/git-head-trust.d.ts.map +1 -1
- package/dist/decisions/git-head-trust.js +52 -0
- package/dist/decisions/git-head-trust.js.map +1 -1
- package/dist/decisions/human-confirmation.d.ts +2 -0
- package/dist/decisions/human-confirmation.d.ts.map +1 -1
- package/dist/decisions/human-confirmation.js +6 -0
- package/dist/decisions/human-confirmation.js.map +1 -1
- package/dist/decisions/legacy-waiver-migration.d.ts +8 -1
- package/dist/decisions/legacy-waiver-migration.d.ts.map +1 -1
- package/dist/decisions/legacy-waiver-migration.js +13 -4
- package/dist/decisions/legacy-waiver-migration.js.map +1 -1
- package/dist/exporters/csv-exporter.d.ts +11 -2
- package/dist/exporters/csv-exporter.d.ts.map +1 -1
- package/dist/exporters/csv-exporter.js +8 -4
- package/dist/exporters/csv-exporter.js.map +1 -1
- package/dist/exporters/matrix/export.d.ts +6 -1
- package/dist/exporters/matrix/export.d.ts.map +1 -1
- package/dist/exporters/matrix/export.js +11 -4
- package/dist/exporters/matrix/export.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/xlsx-exporter.d.ts +3 -2
- package/dist/exporters/xlsx-exporter.d.ts.map +1 -1
- package/dist/exporters/xlsx-exporter.js +4 -4
- package/dist/exporters/xlsx-exporter.js.map +1 -1
- package/dist/graph/board-human-actions.d.ts +5 -0
- package/dist/graph/board-human-actions.d.ts.map +1 -0
- package/dist/graph/board-human-actions.js +83 -0
- package/dist/graph/board-human-actions.js.map +1 -0
- package/dist/graph/evaluate-graph.d.ts +21 -1
- package/dist/graph/evaluate-graph.d.ts.map +1 -1
- package/dist/graph/evaluate-graph.js +37 -9
- package/dist/graph/evaluate-graph.js.map +1 -1
- package/dist/graph/human-action.d.ts +21 -0
- package/dist/graph/human-action.d.ts.map +1 -0
- package/dist/graph/human-action.js +3 -0
- package/dist/graph/human-action.js.map +1 -0
- package/dist/graph/manual-triage-decisions.d.ts +19 -0
- package/dist/graph/manual-triage-decisions.d.ts.map +1 -0
- package/dist/graph/manual-triage-decisions.js +53 -0
- package/dist/graph/manual-triage-decisions.js.map +1 -0
- package/dist/graph/node-decl.d.ts +16 -0
- package/dist/graph/node-decl.d.ts.map +1 -1
- package/dist/graph/node-decl.js +4 -1
- package/dist/graph/node-decl.js.map +1 -1
- package/dist/graph/nodes/test-case-contract-nodes.d.ts +3 -1
- package/dist/graph/nodes/test-case-contract-nodes.d.ts.map +1 -1
- package/dist/graph/nodes/test-case-contract-nodes.js +22 -6
- package/dist/graph/nodes/test-case-contract-nodes.js.map +1 -1
- package/dist/graph/nodes/test-case-nodes.d.ts +3 -0
- package/dist/graph/nodes/test-case-nodes.d.ts.map +1 -1
- package/dist/graph/nodes/test-case-nodes.js +33 -4
- package/dist/graph/nodes/test-case-nodes.js.map +1 -1
- package/dist/graph/nodes/unit-audit-nodes.d.ts.map +1 -1
- package/dist/graph/nodes/unit-audit-nodes.js +1 -0
- package/dist/graph/nodes/unit-audit-nodes.js.map +1 -1
- package/dist/graph/nodes/unit-contract-node.d.ts +2 -1
- package/dist/graph/nodes/unit-contract-node.d.ts.map +1 -1
- package/dist/graph/nodes/unit-contract-node.js +16 -1
- package/dist/graph/nodes/unit-contract-node.js.map +1 -1
- package/dist/graph/nodes/unit-design-nodes.d.ts.map +1 -1
- package/dist/graph/nodes/unit-design-nodes.js +2 -1
- package/dist/graph/nodes/unit-design-nodes.js.map +1 -1
- package/dist/graph/test-case-results.d.ts +1 -0
- package/dist/graph/test-case-results.d.ts.map +1 -1
- package/dist/graph/test-case-results.js +27 -3
- package/dist/graph/test-case-results.js.map +1 -1
- package/dist/graph/test-case-satisfied.d.ts +5 -4
- package/dist/graph/test-case-satisfied.d.ts.map +1 -1
- package/dist/graph/test-case-satisfied.js +4 -0
- package/dist/graph/test-case-satisfied.js.map +1 -1
- package/dist/graph/test-case-state-shape.d.ts +7 -1
- package/dist/graph/test-case-state-shape.d.ts.map +1 -1
- package/dist/graph/unit-artifact-probes.d.ts.map +1 -1
- package/dist/graph/unit-artifact-probes.js +3 -1
- package/dist/graph/unit-artifact-probes.js.map +1 -1
- package/dist/graph/unit-results-freshness.d.ts +29 -0
- package/dist/graph/unit-results-freshness.d.ts.map +1 -0
- package/dist/graph/unit-results-freshness.js +213 -0
- package/dist/graph/unit-results-freshness.js.map +1 -0
- package/dist/harness/audit.d.ts +3 -2
- package/dist/harness/audit.d.ts.map +1 -1
- package/dist/harness/audit.js +4 -17
- package/dist/harness/audit.js.map +1 -1
- package/dist/harness/catalog/gate-rules.yaml +3 -3
- package/dist/harness/defect-draft.d.ts.map +1 -1
- package/dist/harness/defect-draft.js +12 -2
- package/dist/harness/defect-draft.js.map +1 -1
- package/dist/harness/defects.d.ts +14 -0
- package/dist/harness/defects.d.ts.map +1 -1
- package/dist/harness/defects.js +14 -10
- package/dist/harness/defects.js.map +1 -1
- package/dist/harness/flow-check.d.ts +9 -0
- package/dist/harness/flow-check.d.ts.map +1 -1
- package/dist/harness/flow-check.js +11 -0
- package/dist/harness/flow-check.js.map +1 -1
- package/dist/harness/gates/audit-gate-verdict.d.ts.map +1 -1
- package/dist/harness/gates/audit-gate-verdict.js +2 -1
- package/dist/harness/gates/audit-gate-verdict.js.map +1 -1
- package/dist/harness/gates/audit-input-hash.d.ts +2 -0
- package/dist/harness/gates/audit-input-hash.d.ts.map +1 -1
- package/dist/harness/gates/audit-input-hash.js +30 -2
- package/dist/harness/gates/audit-input-hash.js.map +1 -1
- package/dist/harness/gates/audit-report-verification.d.ts +17 -6
- package/dist/harness/gates/audit-report-verification.d.ts.map +1 -1
- package/dist/harness/gates/audit-report-verification.js +55 -21
- package/dist/harness/gates/audit-report-verification.js.map +1 -1
- package/dist/harness/gates/delivery-checklist.d.ts +3 -0
- package/dist/harness/gates/delivery-checklist.d.ts.map +1 -1
- package/dist/harness/gates/delivery-checklist.js +39 -6
- package/dist/harness/gates/delivery-checklist.js.map +1 -1
- package/dist/harness/gates/delivery-decision-gate.d.ts +4 -0
- package/dist/harness/gates/delivery-decision-gate.d.ts.map +1 -1
- package/dist/harness/gates/delivery-decision-gate.js +26 -1
- package/dist/harness/gates/delivery-decision-gate.js.map +1 -1
- package/dist/harness/gates/delivery-decision-step.d.ts +4 -1
- package/dist/harness/gates/delivery-decision-step.d.ts.map +1 -1
- package/dist/harness/gates/delivery-decision-step.js +17 -2
- package/dist/harness/gates/delivery-decision-step.js.map +1 -1
- package/dist/harness/gates/in-run-decision-check.d.ts.map +1 -1
- package/dist/harness/gates/in-run-decision-check.js +40 -12
- package/dist/harness/gates/in-run-decision-check.js.map +1 -1
- package/dist/harness/gates/migrated-waiver-import-bounds.d.ts +13 -0
- package/dist/harness/gates/migrated-waiver-import-bounds.d.ts.map +1 -0
- package/dist/harness/gates/migrated-waiver-import-bounds.js +141 -0
- package/dist/harness/gates/migrated-waiver-import-bounds.js.map +1 -0
- package/dist/harness/gates/migrated-waiver-review.d.ts +14 -4
- package/dist/harness/gates/migrated-waiver-review.d.ts.map +1 -1
- package/dist/harness/gates/migrated-waiver-review.js +78 -80
- package/dist/harness/gates/migrated-waiver-review.js.map +1 -1
- package/dist/harness/journey.d.ts +40 -0
- package/dist/harness/journey.d.ts.map +1 -1
- package/dist/harness/journey.js +155 -22
- package/dist/harness/journey.js.map +1 -1
- package/dist/harness/next-step.d.ts +19 -0
- package/dist/harness/next-step.d.ts.map +1 -1
- package/dist/harness/next-step.js +21 -0
- package/dist/harness/next-step.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/triage-manual-classification.d.ts +32 -0
- package/dist/harness/triage-manual-classification.d.ts.map +1 -0
- package/dist/harness/triage-manual-classification.js +56 -0
- package/dist/harness/triage-manual-classification.js.map +1 -0
- package/dist/harness/unit-paths.d.ts +8 -0
- package/dist/harness/unit-paths.d.ts.map +1 -1
- package/dist/harness/unit-paths.js +23 -1
- package/dist/harness/unit-paths.js.map +1 -1
- package/dist/hooks/bash-read-only-segment.d.ts +9 -0
- package/dist/hooks/bash-read-only-segment.d.ts.map +1 -0
- package/dist/hooks/bash-read-only-segment.js +168 -0
- package/dist/hooks/bash-read-only-segment.js.map +1 -0
- package/dist/hooks/bash-write-targets.d.ts +12 -2
- package/dist/hooks/bash-write-targets.d.ts.map +1 -1
- package/dist/hooks/bash-write-targets.js +122 -29
- package/dist/hooks/bash-write-targets.js.map +1 -1
- package/dist/hooks/guard-protected-paths.d.ts +4 -0
- package/dist/hooks/guard-protected-paths.d.ts.map +1 -1
- package/dist/hooks/guard-protected-paths.js +13 -3
- package/dist/hooks/guard-protected-paths.js.map +1 -1
- package/dist/hooks/guard-rules.d.ts +0 -17
- package/dist/hooks/guard-rules.d.ts.map +1 -1
- package/dist/hooks/guard-rules.js +79 -10
- package/dist/hooks/guard-rules.js.map +1 -1
- package/dist/hooks/guard-tty-spoof-rule.d.ts +3 -1
- package/dist/hooks/guard-tty-spoof-rule.d.ts.map +1 -1
- package/dist/hooks/guard-tty-spoof-rule.js +3 -2
- package/dist/hooks/guard-tty-spoof-rule.js.map +1 -1
- package/dist/hooks/hand-back-core.d.ts +9 -4
- package/dist/hooks/hand-back-core.d.ts.map +1 -1
- package/dist/hooks/hand-back-core.js +24 -11
- package/dist/hooks/hand-back-core.js.map +1 -1
- package/dist/hooks/session-start-unit-state-hook.d.ts +2 -1
- package/dist/hooks/session-start-unit-state-hook.d.ts.map +1 -1
- package/dist/hooks/session-start-unit-state-hook.js +9 -4
- package/dist/hooks/session-start-unit-state-hook.js.map +1 -1
- package/dist/hooks/stop-next-step-hook.d.ts.map +1 -1
- package/dist/hooks/stop-next-step-hook.js +1 -0
- package/dist/hooks/stop-next-step-hook.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/project-initializer.d.ts.map +1 -1
- package/dist/orchestrator/project-initializer.js +1 -0
- package/dist/orchestrator/project-initializer.js.map +1 -1
- package/dist/orchestrator/templates/ai-src/commands/create-test.md +6 -3
- package/dist/orchestrator/templates/ai-src/commands/delivery.md +2 -0
- package/dist/orchestrator/templates/ai-src/commands/review.md +2 -0
- package/dist/orchestrator/templates/ai-src/commands/run-test.md +1 -1
- package/dist/orchestrator/templates/ai-src/partials/waiting-for-you.claude.md +11 -0
- package/dist/orchestrator/templates/ai-src/partials/waiting-for-you.codex.md +11 -0
- package/dist/orchestrator/templates/ai-src/partials/waiting-for-you.copilot.md +11 -0
- package/package.json +3 -3
- package/src/capabilities/discover.ts +15 -2
- package/src/cli/case-triage-hint.ts +31 -0
- package/src/cli/commands/audit.ts +1 -1
- package/src/cli/commands/contract-derive-command.ts +20 -8
- package/src/cli/commands/contract.ts +41 -7
- package/src/cli/commands/delivery.ts +56 -18
- package/src/cli/commands/gate.ts +2 -1
- package/src/cli/commands/generate.ts +13 -2
- package/src/cli/commands/journey.ts +8 -6
- package/src/cli/commands/next.ts +41 -12
- package/src/cli/commands/triage.ts +41 -3
- package/src/cli/commands/update.ts +6 -2
- package/src/cli/index.ts +5 -1
- package/src/cli/json-error-on-exit.ts +72 -0
- package/src/cli/project-sungen-invocation.ts +59 -0
- package/src/cli/run-command-hints.ts +47 -0
- package/src/cli/waiting-for-you-block.ts +27 -0
- package/src/contract/accept-opaque-steps.ts +114 -0
- package/src/contract/approve-contract.ts +10 -3
- package/src/contract/build-contract-verdict.ts +2 -2
- package/src/contract/build-trace-conformance.ts +11 -8
- package/src/contract/classify-contract-change.ts +3 -3
- package/src/contract/classify-trace-conformance.ts +76 -22
- package/src/contract/contract-schema.ts +31 -2
- package/src/contract/contracts-lock-file.ts +1 -0
- package/src/contract/derive-free-feature.ts +46 -3
- package/src/contract/project-contract-blockers.ts +4 -2
- package/src/contract/record-contract-approval.ts +41 -1
- package/src/contract/render-free-gherkin.ts +5 -0
- package/src/contract/resolve-unit-trace-identities.ts +35 -12
- package/src/contract/trace/collect-trace-evidence.ts +105 -36
- package/src/contract/trace/read-file-once.ts +95 -0
- package/src/contract/trace/scenario-id-claims.ts +60 -0
- package/src/contract/trace/select-final-attempt.ts +12 -5
- package/src/contract/trace/test-step-join.ts +6 -5
- package/src/contract/trace/trace-binding.ts +7 -4
- package/src/contract/trace/trace-file-reader.ts +34 -13
- package/src/contract/trace/trace-freshness.ts +110 -0
- package/src/contract/trace/trace-identity-resolution.ts +7 -4
- package/src/contract/trace/trace-run-record.ts +37 -15
- package/src/contract/trace/trace-steps.ts +1 -1
- package/src/contract/trace/trace-zip-limits.ts +6 -2
- package/src/contract/verify-approval-decisions.ts +9 -5
- package/src/decisions/confirmed-decision-commands.ts +25 -4
- package/src/decisions/decision-schema.ts +2 -1
- package/src/decisions/git-head-trust.ts +44 -0
- package/src/decisions/human-confirmation.ts +5 -0
- package/src/decisions/legacy-waiver-migration.ts +14 -3
- package/src/exporters/csv-exporter.ts +12 -4
- package/src/exporters/matrix/export.ts +11 -4
- package/src/exporters/matrix/xlsx-cells.ts +1 -1
- package/src/exporters/xlsx-exporter.ts +5 -4
- package/src/graph/board-human-actions.ts +80 -0
- package/src/graph/evaluate-graph.ts +45 -10
- package/src/graph/human-action.ts +33 -0
- package/src/graph/manual-triage-decisions.ts +55 -0
- package/src/graph/node-decl.ts +18 -0
- package/src/graph/nodes/test-case-contract-nodes.ts +28 -7
- package/src/graph/nodes/test-case-nodes.ts +37 -5
- package/src/graph/nodes/unit-audit-nodes.ts +2 -1
- package/src/graph/nodes/unit-contract-node.ts +20 -2
- package/src/graph/nodes/unit-design-nodes.ts +2 -1
- package/src/graph/test-case-results.ts +25 -4
- package/src/graph/test-case-satisfied.ts +7 -4
- package/src/graph/test-case-state-shape.ts +7 -2
- package/src/graph/unit-artifact-probes.ts +3 -1
- package/src/graph/unit-results-freshness.ts +165 -0
- package/src/harness/audit.ts +4 -13
- package/src/harness/catalog/gate-rules.yaml +3 -3
- package/src/harness/defect-draft.ts +13 -2
- package/src/harness/defects.ts +22 -9
- package/src/harness/flow-check.ts +11 -0
- package/src/harness/gates/audit-gate-verdict.ts +2 -1
- package/src/harness/gates/audit-input-hash.ts +27 -3
- package/src/harness/gates/audit-report-verification.ts +57 -24
- package/src/harness/gates/delivery-checklist.ts +39 -7
- package/src/harness/gates/delivery-decision-gate.ts +29 -1
- package/src/harness/gates/delivery-decision-step.ts +19 -3
- package/src/harness/gates/in-run-decision-check.ts +38 -13
- package/src/harness/gates/migrated-waiver-import-bounds.ts +103 -0
- package/src/harness/gates/migrated-waiver-review.ts +90 -37
- package/src/harness/journey.ts +174 -21
- package/src/harness/next-step.ts +34 -1
- package/src/harness/provenance.ts +99 -11
- package/src/harness/triage-manual-classification.ts +76 -0
- package/src/harness/unit-paths.ts +19 -0
- package/src/hooks/bash-read-only-segment.ts +130 -0
- package/src/hooks/bash-write-targets.ts +109 -23
- package/src/hooks/guard-protected-paths.ts +13 -3
- package/src/hooks/guard-rules.ts +37 -18
- package/src/hooks/guard-tty-spoof-rule.ts +6 -3
- package/src/hooks/hand-back-core.ts +24 -12
- package/src/hooks/session-start-unit-state-hook.ts +9 -4
- package/src/hooks/stop-next-step-hook.ts +1 -0
- package/src/orchestrator/assets-drift.ts +12 -5
- package/src/orchestrator/project-initializer.ts +1 -0
- package/src/orchestrator/templates/ai-src/commands/create-test.md +6 -3
- package/src/orchestrator/templates/ai-src/commands/delivery.md +2 -0
- package/src/orchestrator/templates/ai-src/commands/review.md +2 -0
- package/src/orchestrator/templates/ai-src/commands/run-test.md +1 -1
- package/src/orchestrator/templates/ai-src/partials/waiting-for-you.claude.md +11 -0
- package/src/orchestrator/templates/ai-src/partials/waiting-for-you.codex.md +11 -0
- package/src/orchestrator/templates/ai-src/partials/waiting-for-you.copilot.md +11 -0
|
@@ -30,7 +30,7 @@ function render(r: AuditReport): void {
|
|
|
30
30
|
L('');
|
|
31
31
|
L(` Quality score (business-weighted): ${r.score.overall}/10 [Viewpoint Gate: ${r.gateStatus}]`);
|
|
32
32
|
L(` Intent: focus=${r.intent.focus} · depth threshold ${r.depth.threshold.toFixed(2)} (${r.intent.source})`);
|
|
33
|
-
L(` Provenance: sungen ${r.provenance.sungenVersion} · catalog ${r.provenance.catalogHash} (same
|
|
33
|
+
L(` Provenance: sungen ${r.provenance.sungenVersion} · catalog ${r.provenance.catalogHash} · rules ${r.provenance.rulesHash} (same rules on every machine → same score)`);
|
|
34
34
|
// Print the axes the score was ACTUALLY built from — a fixed five-line block omitted a flow's
|
|
35
35
|
// `flowCoverage`, `specFR` and `atomicLedger` while showing `balance`, which a contract flow
|
|
36
36
|
// does not score on.
|
|
@@ -2,14 +2,17 @@
|
|
|
2
2
|
* `sungen contract derive --screen <u> [--grep <re>] [--json]` (DOS-14.8, phase 08): free tests'
|
|
3
3
|
* traces → `features/<u>.free.feature`, every scenario `@free @proposed`, deterministic, no LLM.
|
|
4
4
|
*
|
|
5
|
-
* exit 0 written, unchanged,
|
|
6
|
-
* exit 1
|
|
5
|
+
* exit 0 written, unchanged, every scenario already approved, or a mobile project (not supported yet)
|
|
6
|
+
* exit 1 nothing to derive (no admissible trace — each skipped test says why, #932), refused (a
|
|
7
|
+
* secret or a machine path would have been written — nothing was), refused because two
|
|
8
|
+
* recorded tests claim one id (#962 — both named, nothing written), or an error
|
|
7
9
|
*/
|
|
8
10
|
import type { Command } from 'commander';
|
|
9
11
|
import * as path from 'path';
|
|
10
|
-
import { deriveFreeFeature, DeriveResult } from '../../contract/derive-free-feature';
|
|
12
|
+
import { deriveFreeFeature, DeriveResult, nothingDerived } from '../../contract/derive-free-feature';
|
|
11
13
|
import { registeredTraceResolver } from '../../contract/resolve-unit-trace-identities';
|
|
12
14
|
import { resolveContractUnit } from '../../contract/contract-units';
|
|
15
|
+
import { withProjectInvocation } from '../project-sungen-invocation';
|
|
13
16
|
|
|
14
17
|
function print(r: DeriveResult, unit: string): void {
|
|
15
18
|
const L = console.log;
|
|
@@ -18,20 +21,29 @@ function print(r: DeriveResult, unit: string): void {
|
|
|
18
21
|
L(`━━━ Contract derive: ${unit} — free tests' traces → ${rel} ━━━`);
|
|
19
22
|
L('');
|
|
20
23
|
if (r.note) L(` ⚠ ${r.note}`);
|
|
24
|
+
for (const w of r.warnings ?? []) L(` ⚠ ${w}`);
|
|
21
25
|
for (const s of r.scenarios) L(` + ${s.title} (${s.steps} steps${s.low ? `, ${s.low} low identity` : ''}${s.opaque ? `, ${s.opaque} opaque` : ''})`);
|
|
22
26
|
for (const s of r.skipped) L(` - ${s.id} skipped: ${s.reason}`);
|
|
23
|
-
if (r.
|
|
27
|
+
if (r.duplicateIds?.length) {
|
|
28
|
+
L(" ✗ Refused — nothing was written. One ID names one test: keeping one would silently drop the other's contract.");
|
|
29
|
+
for (const d of r.duplicateIds) {
|
|
30
|
+
L(` ${d.tests.length} recorded tests claim ${d.id}:`);
|
|
31
|
+
for (const t of d.tests) L(` ${t}`);
|
|
32
|
+
if (d.staleRecords?.length) L(` If that test is gone, delete ${d.staleRecords.join(', ')} — an older run's record (free tests now record under test-results-free/).`);
|
|
33
|
+
}
|
|
34
|
+
L(" Give each test its own ID (its title's leading ID, or its @tag), run the free tests again with trace on, then derive.");
|
|
35
|
+
} else if (r.refused?.length) {
|
|
24
36
|
L(` ✗ Refused — nothing was written. ${r.refused.length} likely secret(s) / machine path(s):`);
|
|
25
37
|
for (const h of r.refused) L(` ${h.where} — ${h.reason}`);
|
|
26
38
|
L(' Move the value into test-data (or an env overlay) and use it from there, then derive again.');
|
|
27
39
|
} else if (r.written) {
|
|
28
40
|
L(` ✓ Wrote ${rel}. Review the steps (they ARE the contract), then QA approves on a terminal:`);
|
|
29
|
-
L(` sungen contract approve --screen ${unit}`);
|
|
41
|
+
L(` ${withProjectInvocation(`sungen contract approve --screen ${unit}`, process.cwd())}`);
|
|
30
42
|
L(' After approval, re-run the free tests (trace: "on") — a trace older than the contract proves nothing.');
|
|
31
43
|
} else if (r.scenarios.length) {
|
|
32
44
|
L(` = ${rel} is already up to date.`);
|
|
33
|
-
} else if (
|
|
34
|
-
L(`
|
|
45
|
+
} else if (nothingDerived(r)) {
|
|
46
|
+
L(` ✗ Nothing to derive: no admissible trace of a test for ${unit}${r.skipped.length ? ' (each skipped test above says why)' : ' (try --grep)'}.`);
|
|
35
47
|
}
|
|
36
48
|
L('');
|
|
37
49
|
}
|
|
@@ -51,7 +63,7 @@ export function registerDeriveCommand(contract: Command): void {
|
|
|
51
63
|
projectRoot: process.cwd(), unit, grep: options.grep ? String(options.grep) : undefined, resolver: registeredTraceResolver() ?? null,
|
|
52
64
|
});
|
|
53
65
|
if (options.json) console.log(JSON.stringify(r, null, 2)); else print(r, unit.name);
|
|
54
|
-
process.exit(r.refused?.length ? 1 : 0);
|
|
66
|
+
process.exit(r.refused?.length || r.duplicateIds?.length || nothingDerived(r) ? 1 : 0);
|
|
55
67
|
} catch (error) {
|
|
56
68
|
console.error('Error:', error instanceof Error ? error.message : error);
|
|
57
69
|
process.exit(1);
|
|
@@ -4,6 +4,9 @@
|
|
|
4
4
|
* approve --screen <u> [--scenario <id>] [--reason <text>]
|
|
5
5
|
* a human types a confirmation on a TTY; writes contracts.lock.yaml + a contract-approval
|
|
6
6
|
* decision. No TTY → refused with the exact command (exit 1).
|
|
7
|
+
* approve --screen <u> --scenario <id> --accept-opaque [--reason <text>]
|
|
8
|
+
* the same human act for the reviewed opaque trace steps of one approved free / impl:code
|
|
9
|
+
* scenario (#923): bound to each step as it reads now (accept-opaque-steps.ts).
|
|
7
10
|
* check --screen <u> [--trace] [--json]
|
|
8
11
|
* rebuilds the verdict from .feature + lock + git HEAD + recorded traces (never from a report),
|
|
9
12
|
* writes .sungen/reports/<slug>-contract.json as output (secret-scanned first). Exit 2 on a hard
|
|
@@ -17,6 +20,7 @@ import * as fs from 'fs';
|
|
|
17
20
|
import * as path from 'path';
|
|
18
21
|
import { approveContract } from '../../contract/approve-contract';
|
|
19
22
|
import { buildContractVerdict, ContractVerdict } from '../../contract/build-contract-verdict';
|
|
23
|
+
import { LOW_IDENTITY_FINDING } from '../../contract/classify-trace-conformance';
|
|
20
24
|
import { contractReportPath, writeContractReport } from '../../contract/contract-report-file';
|
|
21
25
|
import { HARD_CLASSES, REVIEW_CLASSES } from '../../contract/contract-schema';
|
|
22
26
|
import { processIO } from '../../decisions/human-confirmation';
|
|
@@ -24,6 +28,7 @@ import { resolveContractUnit } from '../../contract/contract-units';
|
|
|
24
28
|
import { secretScanHits } from '../../contract/field-semantic-redaction';
|
|
25
29
|
import { resolveUnitTraceIdentities } from '../../contract/resolve-unit-trace-identities';
|
|
26
30
|
import { registerDeriveCommand } from './contract-derive-command';
|
|
31
|
+
import { withProjectInvocationInText } from '../project-sungen-invocation';
|
|
27
32
|
|
|
28
33
|
const MARK: Record<string, string> = {
|
|
29
34
|
match: '✓', strengthened: '↑', proposed: '+', 'hash-changed': '⚖', 'oracle-weakened': '✗', 'target-changed': '✗', 'assertion-lost': '✗',
|
|
@@ -31,10 +36,17 @@ const MARK: Record<string, string> = {
|
|
|
31
36
|
};
|
|
32
37
|
/** `contract check` judges traces with a generous deadline (the guard path keeps ~3 s). */
|
|
33
38
|
const CHECK_TRACE_BUDGET_MS = 120_000;
|
|
34
|
-
const UNPROVEN_HINT = " → free / impl:code tests need trace: 'on'
|
|
39
|
+
const UNPROVEN_HINT = " → free / impl:code tests need their own config (outputDir 'test-results-free', trace: 'on', the sungen run-record reporter), then a fresh run (see `sungen contract derive`).";
|
|
35
40
|
|
|
36
|
-
|
|
37
|
-
|
|
41
|
+
/** The command that recompiles a unit's spec — `sungen generate --screen|--flow|--api <unit>`. */
|
|
42
|
+
function generateCommand(unit: { name: string; dir: string }): string {
|
|
43
|
+
const kind = path.relative(path.join(process.cwd(), 'qa'), unit.dir).split(path.sep)[0];
|
|
44
|
+
return `sungen generate --${kind === 'flows' ? 'flow' : kind === 'api' ? 'api' : 'screen'} ${unit.name}`;
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
function printVerdict(v: ContractVerdict, reportFile: string, generate: string): void {
|
|
48
|
+
// The approve / --accept-opaque / generate lines as this project runs sungen (#963).
|
|
49
|
+
const L = (line: string): void => console.log(withProjectInvocationInText(line, process.cwd()));
|
|
38
50
|
const head = v.verdict === 'pass' ? '✓ PASS' : v.verdict === 'review'
|
|
39
51
|
? '⚖ REVIEW — changed, not weaker. Exit 0 here, but review blocks `sungen delivery` until QA re-approves' : '✗ FAIL';
|
|
40
52
|
L('');
|
|
@@ -64,7 +76,19 @@ function printVerdict(v: ContractVerdict, reportFile: string): void {
|
|
|
64
76
|
: onlyUnproven ? " → Re-run the free / impl:code tests with trace: 'on', then check again."
|
|
65
77
|
: ' → Restore the approved checks in the .feature — or, if the change is intended, QA approves it on a terminal:');
|
|
66
78
|
}
|
|
67
|
-
|
|
79
|
+
// An opaque step is not cleared by re-approving (#923): QA reviews it, then accepts it as it reads now.
|
|
80
|
+
const acceptable = blocking.filter((s) => s.class === 'opaque' && s.trace?.opaque?.some((o) => !o.accepted)
|
|
81
|
+
&& !s.findings.some((f) => f.detail.startsWith(LOW_IDENTITY_FINDING)));
|
|
82
|
+
if (v.verdict !== 'pass' && !v.uncommitted.length && !onlyUnproven && acceptable.length < blocking.length) L(` sungen contract approve --screen ${v.unit}`);
|
|
83
|
+
for (const s of acceptable) {
|
|
84
|
+
L(` → ${s.id}: QA reviews each opaque step above (verbatim, file:line); to accept them as they read now, on a terminal:`);
|
|
85
|
+
L(` sungen contract approve --screen ${v.unit} --scenario ${s.id} --accept-opaque`);
|
|
86
|
+
}
|
|
87
|
+
// A weakened compiled scenario also leaves its spec out of step (#943): name the command that fixes the drift.
|
|
88
|
+
// (A free / impl:code scenario carries a trace summary; its test is hand-written, never regenerated.)
|
|
89
|
+
if (v.verdict === 'fail' && !v.uncommitted.length && v.scenarios.some((s) => !s.trace && HARD_CLASSES.has(s.class))) {
|
|
90
|
+
L(` → After fixing the scenario, run \`${generate}\`: until the compiled spec is regenerated, \`sungen script-check\` reports drift.`);
|
|
91
|
+
}
|
|
68
92
|
L(` Report: ${path.relative(process.cwd(), reportFile)}`);
|
|
69
93
|
L('');
|
|
70
94
|
}
|
|
@@ -85,15 +109,25 @@ export function registerContractCommand(program: Command): void {
|
|
|
85
109
|
.option('-s, --screen <name>', 'Screen / flow / api unit name')
|
|
86
110
|
.option('--scenario <id>', 'Approve one scenario only (its ID, e.g. VP-LOGIN-001)')
|
|
87
111
|
.option('--reason <text>', 'Why (recorded in the contract-approval decision)')
|
|
112
|
+
.option('--accept-opaque', 'With --scenario: accept the reviewed opaque trace step(s) of one approved free / impl:code scenario')
|
|
88
113
|
.action(async (options) => {
|
|
89
114
|
try {
|
|
90
115
|
const unit = unitOrThrow(options.screen);
|
|
91
|
-
const r = await approveContract({
|
|
116
|
+
const r = await approveContract({
|
|
117
|
+
projectRoot: process.cwd(), unit: unit.name, scenario: options.scenario, reason: options.reason, acceptOpaque: options.acceptOpaque === true, io: processIO(),
|
|
118
|
+
});
|
|
92
119
|
if (r.status === 'nothing-to-approve') { console.log(`\n= Nothing to approve: every chosen scenario of ${unit.name} already matches its approved lock entry.\n`); return; }
|
|
120
|
+
if (r.status === 'nothing-to-accept') { console.log(`\n= Nothing to accept: every opaque step of ${options.scenario} is already accepted as it reads now.\n`); return; }
|
|
121
|
+
if (r.status === 'accepted') {
|
|
122
|
+
console.log(`\n✓ Accepted ${r.accepted?.length ?? 0} opaque step(s) of ${options.scenario} → ${path.relative(process.cwd(), r.lockPath)} (decision ${r.decisionId})`);
|
|
123
|
+
console.log(' Commit the lock and the decision through review, then re-run the free test with its trace: a trace older than the lock proves nothing.\n');
|
|
124
|
+
return;
|
|
125
|
+
}
|
|
93
126
|
console.log(`\n✓ Approved ${r.approved.length} scenario(s)${r.removed.length ? `, removed ${r.removed.length}` : ''} → ${path.relative(process.cwd(), r.lockPath)} (decision ${r.decisionId})`);
|
|
94
127
|
console.log(' Commit the lock and the decision through review: until they are in git HEAD, `sungen contract check` reports contract.lock-uncommitted.\n');
|
|
95
128
|
} catch (error) {
|
|
96
|
-
|
|
129
|
+
// A refusal names the command a human runs instead: as this project runs sungen (#963).
|
|
130
|
+
console.error('Error:', error instanceof Error ? withProjectInvocationInText(error.message, process.cwd()) : error);
|
|
97
131
|
process.exit(1);
|
|
98
132
|
}
|
|
99
133
|
});
|
|
@@ -116,7 +150,7 @@ export function registerContractCommand(program: Command): void {
|
|
|
116
150
|
if (hits.length) throw new Error(`Refused to write the contract report — likely secret(s): ${hits.map((h) => `${h.where} ${h.reason}`).join('; ')}`);
|
|
117
151
|
const file = writeContractReport(process.cwd(), verdict);
|
|
118
152
|
if (options.json) console.log(JSON.stringify(verdict, null, 2));
|
|
119
|
-
else printVerdict(verdict, file);
|
|
153
|
+
else printVerdict(verdict, file, generateCommand(unit));
|
|
120
154
|
process.exit(verdict.exitCode);
|
|
121
155
|
} catch (error) {
|
|
122
156
|
// Unreadable = not run (DOS-14.0): an earlier report must not outlive the inputs it judged.
|
|
@@ -17,6 +17,7 @@ import { mergeFeatureAndSpec } from '../../exporters/scenario-merger';
|
|
|
17
17
|
import {
|
|
18
18
|
buildTestCaseRows,
|
|
19
19
|
buildSummary,
|
|
20
|
+
deliverableBasename,
|
|
20
21
|
renderCsv,
|
|
21
22
|
writeCsv,
|
|
22
23
|
} from '../../exporters/csv-exporter';
|
|
@@ -41,11 +42,16 @@ import { MatrixFinding } from '../../exporters/matrix/types';
|
|
|
41
42
|
import { applyManualCarry, ManualCarryReport, PriorManualResults, readPriorManualResults } from '../../exporters/matrix/carry-manual-results';
|
|
42
43
|
import { formatTestOutcome, resultTotals } from '../../exporters/matrix/result-totals';
|
|
43
44
|
import { readCapabilities } from '../../harness/capability';
|
|
45
|
+
import { splitFeatureBlocks } from '../../contract/free-feature-blocks';
|
|
44
46
|
import { processIO } from '../../decisions/human-confirmation';
|
|
45
47
|
import { DeliveryDecisionGate } from '../../harness/gates/delivery-decision-gate';
|
|
46
48
|
import { runDeliveryDecisionStep } from '../../harness/gates/delivery-decision-step';
|
|
47
49
|
import { deliverableGateOverrides, InRunAcceptance } from '../../harness/gates/gate-override-report';
|
|
48
50
|
import { ChecklistUnit, renderChecklist, unitChecklist } from '../../harness/gates/delivery-checklist';
|
|
51
|
+
import { withProjectInvocationInText } from '../project-sungen-invocation';
|
|
52
|
+
import { freeRunCommand, freeRunHasResults, generatedSubdir, plainRunCommand } from '../run-command-hints';
|
|
53
|
+
import { isHandWritten } from '../../harness/parse';
|
|
54
|
+
import { FREE_RUN_DIR } from '../../contract/trace/trace-run-record';
|
|
49
55
|
|
|
50
56
|
const COLOR = {
|
|
51
57
|
reset: '\x1b[0m',
|
|
@@ -101,14 +107,30 @@ function makeTarget(screen: string, featureBaseName: string, kind: UnitKind): De
|
|
|
101
107
|
}
|
|
102
108
|
|
|
103
109
|
/**
|
|
104
|
-
*
|
|
105
|
-
*
|
|
110
|
+
* A derived `*.free.feature` none of whose scenarios QA has approved yet (every one still `@proposed`):
|
|
111
|
+
* a proposal, not a delivery unit — counted as one, it ended in "Not exported" (#939). Approving a
|
|
112
|
+
* scenario drops its tag (`contract approve`), and from then on the file is a unit like any other.
|
|
113
|
+
*/
|
|
114
|
+
function proposalOnly(featureFile: string): boolean {
|
|
115
|
+
if (!featureFile.endsWith('.free.feature')) return false;
|
|
116
|
+
try {
|
|
117
|
+
// No scenario block at all (empty, or not Gherkin) is not a proposal: it stays listed and the preflight names it.
|
|
118
|
+
const blocks = splitFeatureBlocks(fs.readFileSync(featureFile, 'utf8'));
|
|
119
|
+
return blocks.length > 0 && blocks.every((b) => b.proposed);
|
|
120
|
+
} catch {
|
|
121
|
+
return false; // unreadable: listed, so the preflight names the problem
|
|
122
|
+
}
|
|
123
|
+
}
|
|
124
|
+
|
|
125
|
+
/**
|
|
126
|
+
* List all `.feature` files inside a screen/flow/api unit as separate targets — a proposal-only
|
|
127
|
+
* free feature excepted. Returns empty array when the directory has no features yet.
|
|
106
128
|
*/
|
|
107
129
|
function listFeatureTargets(cwd: string, screen: string, kind: UnitKind): DeliveryTarget[] {
|
|
108
130
|
const featuresDir = path.join(cwd, 'qa', qaParent(kind), screen, 'features');
|
|
109
131
|
if (!fs.existsSync(featuresDir)) return [];
|
|
110
132
|
return fs.readdirSync(featuresDir)
|
|
111
|
-
.filter((f) => f.endsWith('.feature'))
|
|
133
|
+
.filter((f) => f.endsWith('.feature') && !proposalOnly(path.join(featuresDir, f)))
|
|
112
134
|
.map((f) => makeTarget(screen, f.slice(0, -'.feature'.length), kind))
|
|
113
135
|
.sort((a, b) => a.featureBaseName.localeCompare(b.featureBaseName));
|
|
114
136
|
}
|
|
@@ -169,8 +191,7 @@ function qaDir(cwd: string, target: DeliveryTarget): string {
|
|
|
169
191
|
// dashboard snapshot builder loads the identical contract (shared + unit merge).
|
|
170
192
|
|
|
171
193
|
function generatedDir(cwd: string, target: DeliveryTarget): string {
|
|
172
|
-
|
|
173
|
-
return path.join(cwd, 'specs', 'generated', sub);
|
|
194
|
+
return path.join(cwd, generatedSubdir(target.kind, target.screen));
|
|
174
195
|
}
|
|
175
196
|
|
|
176
197
|
// ----------------------------------------------------------------------------
|
|
@@ -233,6 +254,9 @@ export function runPreflight(cwd: string, target: DeliveryTarget): PreflightChec
|
|
|
233
254
|
const selectorsFile = path.join(base, 'selectors', `${target.featureBaseName}.yaml`);
|
|
234
255
|
const specFile = path.join(genBase, `${target.featureBaseName}.spec.ts`);
|
|
235
256
|
const resultsFile = resolveResultsPath(cwd, target);
|
|
257
|
+
// A derived `*.free.feature` is implemented by hand-written tests (DOS-14.8): no selectors, never compiled,
|
|
258
|
+
// run with the project's free-test config into its own outputDir — so its hints are that run's (#964).
|
|
259
|
+
const free = isHandWritten([], featureFile);
|
|
236
260
|
|
|
237
261
|
const featureOk = checkFeatureReal(featureFile);
|
|
238
262
|
// test-data is only REQUIRED when the feature actually references a
|
|
@@ -243,9 +267,9 @@ export function runPreflight(cwd: string, target: DeliveryTarget): PreflightChec
|
|
|
243
267
|
? checkTestDataHasVars(testDataFile)
|
|
244
268
|
: true;
|
|
245
269
|
// API units have no DOM → no selectors; the catalog (apis.yaml) is the contract. N/A, not missing.
|
|
246
|
-
const selectorsOk = target.kind === 'api' ? true : checkSelectorsHasEntries(selectorsFile, target.featureBaseName);
|
|
247
|
-
const specOk = fs.existsSync(specFile);
|
|
248
|
-
const resultsOk = resultsFile !== null;
|
|
270
|
+
const selectorsOk = target.kind === 'api' || free ? true : checkSelectorsHasEntries(selectorsFile, target.featureBaseName);
|
|
271
|
+
const specOk = free || fs.existsSync(specFile);
|
|
272
|
+
const resultsOk = free ? freeRunHasResults(cwd) : resultsFile !== null;
|
|
249
273
|
|
|
250
274
|
const label = target.isFlow ? `flow/${target.featureBaseName}` : target.featureBaseName;
|
|
251
275
|
const missing: string[] = [];
|
|
@@ -267,13 +291,15 @@ export function runPreflight(cwd: string, target: DeliveryTarget): PreflightChec
|
|
|
267
291
|
missing.push(`compiled .spec.ts missing (optional — needed to run): ${path.relative(cwd, specFile)}`);
|
|
268
292
|
suggestions.push(`sungen generate --${target.kind === 'flow' ? 'flow' : target.kind === 'api' ? 'api' : 'screen'} ${target.screen}`);
|
|
269
293
|
}
|
|
270
|
-
if (!resultsOk) {
|
|
294
|
+
if (!resultsOk && free) {
|
|
295
|
+
missing.push(`free-test results missing (optional): ${FREE_RUN_DIR}/`);
|
|
296
|
+
suggestions.push(freeRunCommand(cwd));
|
|
297
|
+
} else if (!resultsOk) {
|
|
271
298
|
const env = process.env.SUNGEN_ENV;
|
|
272
299
|
const envSuffix = env ? `.${env}` : '';
|
|
273
300
|
missing.push(`test-result${envSuffix}.json missing (optional)`);
|
|
274
301
|
const genRel = path.relative(cwd, genBase);
|
|
275
|
-
|
|
276
|
-
suggestions.push(`${envPrefix}PLAYWRIGHT_JSON_OUTPUT_NAME=${genRel}/${target.featureBaseName}-test-result.json npx playwright test ${genRel}/${target.featureBaseName}.spec.ts`);
|
|
302
|
+
suggestions.push(plainRunCommand(genRel, target.featureBaseName, env));
|
|
277
303
|
}
|
|
278
304
|
|
|
279
305
|
return {
|
|
@@ -711,6 +737,8 @@ async function exportMatrixTarget(
|
|
|
711
737
|
}
|
|
712
738
|
|
|
713
739
|
const { model, mapMissing, mapErrors } = loadMatrixModel(paths);
|
|
740
|
+
// An audit that is untrusted or FAILs never yields an official deliverable: --preview renders a DRAFT (#935).
|
|
741
|
+
if (model && decisions?.auditDraft(qaDir(cwd, target))) model.manifest.approvalState = 'draft';
|
|
714
742
|
|
|
715
743
|
if (mapMissing) {
|
|
716
744
|
log(`${COLOR.yellow}${paths.label}: no delivery map${COLOR.reset} (${path.relative(cwd, paths.mapFile)})`);
|
|
@@ -729,11 +757,13 @@ async function exportMatrixTarget(
|
|
|
729
757
|
// counted, so this line, the workbook's totals and the summary table all agree (#891). A file
|
|
730
758
|
// that cannot be read is reported where it matters — when it would be overwritten.
|
|
731
759
|
const format = opts.format ?? 'xlsx';
|
|
760
|
+
// A draft (DRAFT banner) is written to its own `…-testcases.DRAFT.<ext>`, never onto the official file (#965).
|
|
761
|
+
const draft = model.manifest.approvalState === 'draft';
|
|
732
762
|
let prior: PriorManualResults | null = null;
|
|
733
763
|
let priorError: Error | null = null;
|
|
734
764
|
try { prior = await readPriorManualResults(deliverableXlsxPath(cwd, paths.unit)); }
|
|
735
|
-
// A CSV-only export overwrites no workbook, so an unreadable one only means "nothing to carry".
|
|
736
|
-
catch (e) { if (format !== 'csv') priorError = e as Error; }
|
|
765
|
+
// A CSV-only export or a draft overwrites no official workbook, so an unreadable one only means "nothing to carry".
|
|
766
|
+
catch (e) { if (format !== 'csv' && !draft) priorError = e as Error; }
|
|
737
767
|
applyManualCarry(model, prior);
|
|
738
768
|
model.gateOverrides = deliverableGateOverrides(decisions?.overridesFor(qaDir(cwd, target)) ?? [], hasReview(model.findings), decisions?.acceptedInRun);
|
|
739
769
|
|
|
@@ -783,6 +813,9 @@ async function exportMatrixTarget(
|
|
|
783
813
|
const failed = model.items.reduce((a, i) => a + i.resultCounts.failed, 0);
|
|
784
814
|
const written = [xlsxPath, csvPath].filter((p): p is string => !!p).map((p) => path.relative(cwd, p));
|
|
785
815
|
for (const rel of written) log(` ${COLOR.green}→ ${rel}${COLOR.reset}`);
|
|
816
|
+
if (draft) {
|
|
817
|
+
log(` ${COLOR.yellow}draft only — not a deliverable:${COLOR.reset} it carries the DRAFT banner, and the official ${deliverableBasename(paths.unit)} file was neither written nor changed.`);
|
|
818
|
+
}
|
|
786
819
|
if (manualCarry) printManualCarry(manualCarry);
|
|
787
820
|
return {
|
|
788
821
|
label: paths.label,
|
|
@@ -910,7 +943,8 @@ async function printDeliveryChecklist(cwd: string, targets: DeliveryTarget[], ac
|
|
|
910
943
|
const rows: Array<{ label: string; boxes: Awaited<ReturnType<typeof unitChecklist>> }> = [];
|
|
911
944
|
for (const u of units.values()) rows.push({ label: u.label, boxes: await unitChecklist(cwd, u, accepted) });
|
|
912
945
|
log('');
|
|
913
|
-
|
|
946
|
+
// Each box's fix command as this project runs sungen (#963).
|
|
947
|
+
for (const line of renderChecklist(rows)) log(withProjectInvocationInText(line, cwd));
|
|
914
948
|
}
|
|
915
949
|
|
|
916
950
|
// ----------------------------------------------------------------------------
|
|
@@ -929,7 +963,7 @@ export function registerDeliveryCommand(program: Command): void {
|
|
|
929
963
|
.option('--full', 'Alias of --legacy')
|
|
930
964
|
.option('--check', 'Matrix gates only — validate the delivery map, write nothing')
|
|
931
965
|
.option('--approve [ids]', 'Approve proposed groups (all, or a comma-separated id list) + stamp fingerprints')
|
|
932
|
-
.option('--preview', 'Render the matrix despite review-required findings (DRAFT
|
|
966
|
+
.option('--preview', 'Render the matrix despite review-required findings — a DRAFT (watermarked) written to <unit>-testcases.DRAFT.*, never onto the official file')
|
|
933
967
|
.option('--format <format>', 'Matrix output format: xlsx (default) | csv | both', 'xlsx')
|
|
934
968
|
.option('--accept-in-run', 'Deliver despite gate-overrides/waivers/approvals recorded during the run (decision.in-run) — a human confirms on a terminal')
|
|
935
969
|
.action(async (names: string[], options: {
|
|
@@ -1023,7 +1057,8 @@ export function registerDeliveryCommand(program: Command): void {
|
|
|
1023
1057
|
}
|
|
1024
1058
|
if (options.approve === undefined) {
|
|
1025
1059
|
const gate = await runDeliveryDecisionStep({
|
|
1026
|
-
|
|
1060
|
+
// The accept-in-run command and its refusal as this project runs sungen (#963).
|
|
1061
|
+
projectRoot: cwd, acceptInRun: !!options.acceptInRun, io: processIO(), log: (line) => log(withProjectInvocationInText(line, cwd)),
|
|
1027
1062
|
// --preview / --check soften only the matrix export; --legacy has no draft mode, so it always needs one.
|
|
1028
1063
|
requireTrustedAudit: !!(options.legacy || options.full) || (!options.preview && !options.check),
|
|
1029
1064
|
refs: toExport.map((t) => ({ label: t.isFlow ? `flow/${t.featureBaseName}` : t.featureBaseName, unitName: t.screen, unitDir: qaDir(cwd, t), resultsPath: resolveResultsPath(cwd, t) })),
|
|
@@ -1095,9 +1130,12 @@ export function registerDeliveryCommand(program: Command): void {
|
|
|
1095
1130
|
// an approval are the normal state of a live project. Preflight skips are
|
|
1096
1131
|
// not counted here: reaching this point in an explicit scope means
|
|
1097
1132
|
// --continue-on-missing was passed, which IS the opt-in to tolerate them.
|
|
1098
|
-
|
|
1133
|
+
// --check reports like its checklist: an audit box that does not hold is a non-zero exit (#935).
|
|
1134
|
+
const auditFails = (decisions?.untrustedAudits.length ?? 0) + (decisions?.failingAudits.length ?? 0);
|
|
1135
|
+
if (options.check && auditFails) log(`${COLOR.red}✗ --check: ${auditFails} unit(s) whose audit does not pass — see the audit box above.${COLOR.reset}`);
|
|
1136
|
+
const failed = (explicitScope
|
|
1099
1137
|
? notExported.length > 0
|
|
1100
|
-
: matrixSummaries.length === 0;
|
|
1138
|
+
: matrixSummaries.length === 0) || (!!options.check && auditFails > 0);
|
|
1101
1139
|
if (failed) process.exit(1);
|
|
1102
1140
|
return;
|
|
1103
1141
|
}
|
package/src/cli/commands/gate.ts
CHANGED
|
@@ -3,6 +3,7 @@ import * as path from 'path';
|
|
|
3
3
|
import * as fs from 'fs';
|
|
4
4
|
import { runGate, renderGate, GatePhase } from '../../harness/journey';
|
|
5
5
|
import { findUnitDir } from '../resolve-unit';
|
|
6
|
+
import { withProjectInvocationInText } from '../project-sungen-invocation';
|
|
6
7
|
|
|
7
8
|
|
|
8
9
|
const PHASES: GatePhase[] = ['create', 'run', 'deliver'];
|
|
@@ -24,7 +25,7 @@ export function registerGateCommand(program: Command): void {
|
|
|
24
25
|
|
|
25
26
|
const verdict = runGate(process.cwd(), name, phase);
|
|
26
27
|
if (options.json) console.log(JSON.stringify(verdict, null, 2));
|
|
27
|
-
else console.log(renderGate(verdict));
|
|
28
|
+
else console.log(withProjectInvocationInText(renderGate(verdict), process.cwd())); // waive / sign-off hints as the project runs sungen (#963)
|
|
28
29
|
|
|
29
30
|
// Exit 2 on HALT — usable in CI / the orchestration loop to block the next phase.
|
|
30
31
|
process.exit(verdict.status === 'halt' ? 2 : 0);
|
|
@@ -8,6 +8,7 @@ import { capabilityRegistry } from '../../capabilities/registry';
|
|
|
8
8
|
import { discoverAndRegisterCapabilities } from '../../capabilities/discover';
|
|
9
9
|
import { readCapabilities, writeCapabilities, driverMeta, loadDriverCatalog } from '../../harness/capability';
|
|
10
10
|
import { isHandWritten } from '../../harness/parse';
|
|
11
|
+
import { generatedSubdir, plainRunCommand } from '../run-command-hints';
|
|
11
12
|
|
|
12
13
|
/**
|
|
13
14
|
* Find .feature files recursively in a directory
|
|
@@ -255,8 +256,18 @@ export function registerGenerateCommand(program: Command): void {
|
|
|
255
256
|
if (shown.length < ddWarnings.length) console.log(` … and ${ddWarnings.length - shown.length} more (run with -v to list all)`);
|
|
256
257
|
}
|
|
257
258
|
|
|
258
|
-
|
|
259
|
-
|
|
259
|
+
// Never the interactive `--ui` (#939): an agent cannot drive it, and run-test is what records the results.
|
|
260
|
+
const unit = screenName || flowName || apiName;
|
|
261
|
+
const nextStep = platform === 'mobile' ? 'npm run test:mobile' : unit ? `/sungen:run-test ${unit}` : 'npx playwright test';
|
|
262
|
+
console.log(`Next step: ${nextStep}`);
|
|
263
|
+
// The plain run writes each feature's results where `sungen next` and `sungen delivery` read them (#964) —
|
|
264
|
+
// a bare `npx playwright test` wrote them to the config's default file, which neither reads.
|
|
265
|
+
if (platform !== 'mobile' && unit) {
|
|
266
|
+
const genRel = generatedSubdir(apiName ? 'api' : flowName ? 'flow' : 'screen', unit);
|
|
267
|
+
console.log(' or the plain run — it writes the results file `sungen next` and `sungen delivery` read:');
|
|
268
|
+
for (const f of featureFiles) console.log(` ${plainRunCommand(genRel, path.basename(f, '.feature'))}`);
|
|
269
|
+
}
|
|
270
|
+
console.log('');
|
|
260
271
|
} catch (error) {
|
|
261
272
|
console.error('Error:', error instanceof Error ? error.message : error);
|
|
262
273
|
process.exit(1);
|
|
@@ -1,10 +1,11 @@
|
|
|
1
1
|
import { Command } from 'commander';
|
|
2
2
|
import * as path from 'path';
|
|
3
|
-
import { runJourney,
|
|
3
|
+
import { runJourney, renderJourneyBoard, rollupJourney, renderRollupBoard, writeJourneyFileBestEffort } from '../../harness/journey';
|
|
4
4
|
import { reportSlug } from '../../harness/unit-paths';
|
|
5
5
|
import { findUnitDir } from '../resolve-unit';
|
|
6
|
-
import { answerPendingMigration, decideWithConfirmation, DECIDE_KINDS, waiveWithConfirmation } from '../../decisions/confirmed-decision-commands';
|
|
6
|
+
import { answerPendingMigration, decideWithConfirmation, DECIDE_KINDS, signoffWithConfirmation, waiveWithConfirmation } from '../../decisions/confirmed-decision-commands';
|
|
7
7
|
import { processIO } from '../../decisions/human-confirmation';
|
|
8
|
+
import { withProjectInvocationInText } from '../project-sungen-invocation';
|
|
8
9
|
|
|
9
10
|
const collect = (value: string, previous: string[]): string[] => [...previous, value];
|
|
10
11
|
|
|
@@ -22,7 +23,7 @@ export function registerJourneyCommand(program: Command): void {
|
|
|
22
23
|
.option('--payload <json>', 'Kind-specific fields as a JSON object, e.g. a gate-override\'s {"rule","severity","scope"} (with --decide)')
|
|
23
24
|
.option('--visibility <v>', 'internal (default, never exported) | client (with --decide)')
|
|
24
25
|
.option('--supersedes <id>', 'The decision this one replaces — the old entry is never edited (with --decide)')
|
|
25
|
-
.option('--signoff', 'Sign off the review queue — the single human gate (allowed only when every other obligation is satisfied/waived)')
|
|
26
|
+
.option('--signoff', 'Sign off the review queue — the single human gate (allowed only when every other obligation is satisfied/waived); a human types a confirmation on a TTY')
|
|
26
27
|
.option('--by <name>', 'Who is signing off (recorded with --signoff)')
|
|
27
28
|
.option('--json', 'Output the raw JSON report')
|
|
28
29
|
.action(async (options) => {
|
|
@@ -63,7 +64,7 @@ export function registerJourneyCommand(program: Command): void {
|
|
|
63
64
|
const report = options.waive
|
|
64
65
|
? await waiveWithConfirmation({ projectRoot: process.cwd(), unit: name, obligation: options.waive, reason: options.reason || '', io })
|
|
65
66
|
: options.signoff
|
|
66
|
-
?
|
|
67
|
+
? await signoffWithConfirmation({ projectRoot: process.cwd(), unit: name, by: options.by, io })
|
|
67
68
|
: runJourney(process.cwd(), name);
|
|
68
69
|
|
|
69
70
|
const outDir = path.join(process.cwd(), '.sungen', 'journey');
|
|
@@ -77,12 +78,13 @@ export function registerJourneyCommand(program: Command): void {
|
|
|
77
78
|
console.log(JSON.stringify(report, null, 2));
|
|
78
79
|
} else {
|
|
79
80
|
console.log('');
|
|
80
|
-
console.log(board);
|
|
81
|
+
console.log(withProjectInvocationInText(board, process.cwd()));
|
|
81
82
|
console.log(` Board: ${written ? path.relative(process.cwd(), written) : '(not written — read-only checkout)'}`);
|
|
82
83
|
console.log('');
|
|
83
84
|
}
|
|
84
85
|
} catch (error) {
|
|
85
|
-
|
|
86
|
+
// A refusal names the command a human runs instead: as this project runs sungen (#963).
|
|
87
|
+
console.error('Error:', error instanceof Error ? withProjectInvocationInText(error.message, process.cwd()) : error);
|
|
86
88
|
process.exit(1);
|
|
87
89
|
}
|
|
88
90
|
});
|
package/src/cli/commands/next.ts
CHANGED
|
@@ -9,8 +9,13 @@
|
|
|
9
9
|
* sungen next <unit> --case <id> one test case: its state, and its steps after the blocking
|
|
10
10
|
* unit steps before them (OES-10.3)
|
|
11
11
|
* --json the same, machine-readable; steps add nodeId, granularity,
|
|
12
|
-
* testCase?, gates?;
|
|
13
|
-
*
|
|
12
|
+
* testCase?, gates?; the unit adds `results` (#942) and
|
|
13
|
+
* `humanActions: [{command, reason, kind, rendered}]` (#954) —
|
|
14
|
+
* `command` canonical, `rendered` as this project runs it (#963),
|
|
15
|
+
* and `humanActionsHeldBy[Command]` when a blocking step holds them (#966);
|
|
16
|
+
* every older field is kept. stdout is the payload alone (#660, #836)
|
|
17
|
+
* The text names the run the results come from and warns when they are older than the suite (#942),
|
|
18
|
+
* and ends with "Waiting for you": every pending human-only command, with why (#954).
|
|
14
19
|
*/
|
|
15
20
|
import { Command } from 'commander';
|
|
16
21
|
import * as fs from 'fs';
|
|
@@ -18,8 +23,13 @@ import * as path from 'path';
|
|
|
18
23
|
import { CLI_GIT_TIMEOUT_MS, headSnapshot } from '../../contract/contract-git';
|
|
19
24
|
import { testCaseStatusLabel } from '../../graph/test-case-satisfied';
|
|
20
25
|
import { testCaseStates } from '../../graph/test-case-states';
|
|
26
|
+
import { describeResultsRun } from '../../graph/unit-results-freshness';
|
|
21
27
|
import { deriveSteps, NextStep, readUnitState, UnitState } from '../../harness/next-step';
|
|
22
28
|
import { resolveUnit, resolveUnitCandidates, AmbiguousUnitError } from '../resolve-unit';
|
|
29
|
+
import { withProjectInvocation, withProjectInvocationInText } from '../project-sungen-invocation';
|
|
30
|
+
import { waitingForYouLines } from '../waiting-for-you-block';
|
|
31
|
+
import { caseTriageLines } from '../case-triage-hint';
|
|
32
|
+
import { testCaseNeed } from '../../graph/test-case-satisfied';
|
|
23
33
|
|
|
24
34
|
/** Every unit the project holds, so `sungen next` with no argument still answers. */
|
|
25
35
|
function allUnits(cwd: string): Array<{ name: string; kind: UnitState['kind']; dir: string }> {
|
|
@@ -35,15 +45,25 @@ function allUnits(cwd: string): Array<{ name: string; kind: UnitState['kind']; d
|
|
|
35
45
|
return out;
|
|
36
46
|
}
|
|
37
47
|
|
|
38
|
-
|
|
48
|
+
/** Steps as text, each `sungen …` command — the reason's too (#963) — as this project runs it (#939); `--json` keeps them canonical. */
|
|
49
|
+
function renderSteps(steps: NextStep[], cwd: string): void {
|
|
39
50
|
for (const st of steps) {
|
|
40
51
|
const mark = st.kind === 'blocked' ? '⛔' : st.kind === 'optional' ? '○' : st.kind === 'decide' ? '⚖' : '→';
|
|
41
|
-
console.log(` ${mark} ${st.command}${st.kind === 'decide' ? ' (a QA decision — ask, do not run)' : ''}`);
|
|
42
|
-
console.log(` ${st.because}`);
|
|
52
|
+
console.log(` ${mark} ${withProjectInvocationInText(st.command, cwd)}${st.kind === 'decide' ? ' (a QA decision — ask, do not run)' : ''}`);
|
|
53
|
+
console.log(` ${withProjectInvocationInText(st.because, cwd)}`);
|
|
43
54
|
}
|
|
44
55
|
}
|
|
45
56
|
|
|
46
|
-
|
|
57
|
+
/**
|
|
58
|
+
* `--json`: each human action keeps its canonical `command` and adds `rendered` — the line a human runs in
|
|
59
|
+
* this project (`npx sungen …` with a local install; an instruction unchanged), as the text shows it (#963).
|
|
60
|
+
*/
|
|
61
|
+
function withRenderedHumanActions(s: UnitState, cwd: string): UnitState {
|
|
62
|
+
if (!s.humanActions) return s;
|
|
63
|
+
return { ...s, humanActions: s.humanActions.map((a) => ({ ...a, rendered: a.instruction ? a.command : withProjectInvocation(a.command, cwd) })) };
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
function render(s: UnitState, cwd: string): void {
|
|
47
67
|
const L = console.log;
|
|
48
68
|
L('');
|
|
49
69
|
L(`━━━ Next steps: ${s.unit} (${s.kind}) ━━━`);
|
|
@@ -57,10 +77,16 @@ function render(s: UnitState): void {
|
|
|
57
77
|
].filter(Boolean);
|
|
58
78
|
L(` state: ${held.length ? held.join(' · ') : '(nothing yet)'}`);
|
|
59
79
|
if (s.audit) L(` audit: ${s.audit.overall}/10 [${s.audit.gateStatus}] · ${s.audit.findings.length} finding(s)`);
|
|
60
|
-
else if (s.auditNotCounted) L(` audit: ${s.auditNotCounted.state === 'stale' ? 'stale' : 'not trusted'} — ${s.auditNotCounted.reason}`);
|
|
80
|
+
else if (s.auditNotCounted) L(` audit: ${s.auditNotCounted.state === 'stale' ? 'stale' : 'not trusted'} — ${withProjectInvocationInText(s.auditNotCounted.reason, cwd)}`);
|
|
81
|
+
if (s.results) {
|
|
82
|
+
L(` results: ${s.results.files.length} file${s.results.files.length === 1 ? '' : 's'}, ${describeResultsRun(s.results)}`);
|
|
83
|
+
if (s.results.warning) L(` ⚠ ${s.results.warning}`);
|
|
84
|
+
}
|
|
61
85
|
L('');
|
|
62
|
-
renderSteps(s.steps);
|
|
86
|
+
renderSteps(s.steps, cwd);
|
|
63
87
|
L('');
|
|
88
|
+
const waiting = waitingForYouLines(s.humanActions, cwd, s.humanActionsHeldBy, s.humanActionsHeldByCommand);
|
|
89
|
+
if (waiting.length) { for (const line of waiting) L(line); L(''); }
|
|
64
90
|
}
|
|
65
91
|
|
|
66
92
|
/** `--case <id>`: the case's state and its frontier. Exits 1 when the case cannot be found. */
|
|
@@ -84,13 +110,15 @@ function caseView(s: UnitState, cwd: string, unitDir: string, id: string, json:
|
|
|
84
110
|
L('');
|
|
85
111
|
L(` test case: ${tc.title}${tc.unstableId ? ' (no VP-id — this id changes when the title does)' : ''}`);
|
|
86
112
|
L(` where: ${tc.file}#L${tc.lines[0]}-L${tc.lines[1]}`);
|
|
87
|
-
L(` status: ${testCaseStatusLabel(tc)} · result ${tc.result}${tc.mocked && tc.result === 'passed' ? ' (mocked)' : ''}${tc.stale ? ` · stale: ${tc.stale}` : ''}${tc.triage ? ` · triage ${tc.triage.class}` : ''}`);
|
|
113
|
+
L(` status: ${testCaseStatusLabel(tc)} · result ${tc.result}${tc.mocked && tc.result === 'passed' ? ' (mocked)' : ''}${tc.stale ? ` · stale: ${tc.stale}` : ''}${tc.triage ? ` · triage ${tc.triage.class}${tc.triage.ruleId === 'manual' ? ` (by hand, ${tc.triage.decision})` : ''}` : ''}`);
|
|
88
114
|
if (tc.contract) L(` contract: ${tc.contract.class}${tc.contract.uncommitted ? ' · lock entry not committed' : ''}${tc.contract.lowIdentity ? ' · low target identity' : ''}`);
|
|
89
115
|
if (tc.anchors.length) L(` anchors: ${tc.anchors.map((a) => `${a.anchor} (${a.change})`).join(', ')}`);
|
|
90
116
|
for (const f of tc.findings) L(` finding: ${f.code} [${f.confidence.label} ${f.confidence.score}] ${f.citation}`);
|
|
91
117
|
L('');
|
|
92
|
-
if (steps.length) renderSteps(steps);
|
|
118
|
+
if (steps.length) renderSteps(steps, cwd);
|
|
93
119
|
else L(` ✓ nothing to do for this test case — ${testCaseStatusLabel(tc)}`);
|
|
120
|
+
// "needs triage" names how to classify the red, even while a unit step holds its own step back (#966).
|
|
121
|
+
if (testCaseNeed(tc) === 'triage') for (const line of caseTriageLines(s, tc, steps, cwd)) L(line);
|
|
94
122
|
L('');
|
|
95
123
|
}
|
|
96
124
|
|
|
@@ -128,10 +156,11 @@ export function registerNextCommand(program: Command): void {
|
|
|
128
156
|
});
|
|
129
157
|
}
|
|
130
158
|
if (o.json) {
|
|
131
|
-
|
|
159
|
+
const out = states.map((s) => withRenderedHumanActions(s, cwd));
|
|
160
|
+
console.log(JSON.stringify(out.length === 1 ? out[0] : out, null, 2));
|
|
132
161
|
return;
|
|
133
162
|
}
|
|
134
|
-
for (const s of states) render(s);
|
|
163
|
+
for (const s of states) render(s, cwd);
|
|
135
164
|
} catch (e) {
|
|
136
165
|
console.error(`Error: ${e instanceof Error ? e.message : e}`);
|
|
137
166
|
process.exit(1);
|