devrites 3.2.27 → 4.0.0
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/CHANGELOG.md +22 -144
- package/NOTICE.md +6 -5
- package/README.md +187 -95
- package/SECURITY.md +103 -79
- package/bin/devrites.mjs +140 -24
- package/docs/adr/0003-gate-model-hitl-pause.md +1 -1
- package/docs/adr/0013-native-codex-agent-orchestration.md +74 -0
- package/docs/adr/0014-native-host-hook-boundary.md +64 -0
- package/docs/adr/0015-read-only-root-native-orchestration.md +96 -0
- package/docs/adr/0016-codex-writer-fail-closed.md +53 -0
- package/docs/adr/0017-native-codex-writer-agent.md +63 -0
- package/docs/adr/0018-native-sandbox-instruction-writer-boundary.md +66 -0
- package/docs/adr/0019-native-boundary-with-deterministic-gates.md +48 -0
- package/docs/adr/0020-thin-engine-native-orchestration-boundary.md +54 -0
- package/docs/adr/0021-observable-workspace-compatibility.md +49 -0
- package/docs/adr/0022-native-orchestration-thin-engine.md +90 -0
- package/docs/adr/0023-native-workspace-reads-line-output.md +71 -0
- package/docs/adr/0024-native-policy-offline-installer-boundary.md +111 -0
- package/docs/adr/0025-evidence-gated-workspace-upgrades.md +64 -0
- package/docs/adr/0026-content-bound-proof-and-bounded-inputs.md +169 -0
- package/docs/adr/0027-content-bound-build-readiness.md +77 -0
- package/docs/adr/README.md +26 -11
- package/docs/agents/domain.md +2 -1
- package/docs/agents/issue-driven-rites.md +1 -1
- package/docs/architecture.md +191 -103
- package/docs/candidate-integrity.md +138 -0
- package/docs/capability-surface-selection.md +15 -5
- package/docs/cli.md +139 -92
- package/docs/command-map.md +88 -63
- package/docs/engine/commands.md +115 -507
- package/docs/engine/state-schema.md +94 -113
- package/docs/engine/workspace-schema.md +169 -229
- package/docs/flow.md +113 -68
- package/docs/harness-compliance.md +29 -56
- package/docs/markdown-instruction-upgrade-2026-08-02.md +302 -0
- package/docs/orchestration.md +136 -149
- package/docs/porting-to-a-new-harness.md +22 -7
- package/docs/quick-reference.md +21 -8
- package/docs/release.md +47 -4
- package/docs/skills.md +111 -72
- package/docs/templates/CRITIC_REVIEW_PACKET_TEMPLATE.md +2 -0
- package/docs/upstream-workflow-benchmark-2026-08-01.md +655 -0
- package/docs/usage.md +99 -65
- package/engine/Makefile +1 -2
- package/engine/cmd/releasepack/main.go +45 -25
- package/engine/cmd/releasepack/main_test.go +69 -0
- package/engine/commands.go +8 -258
- package/engine/harnessmatrix_test.go +28 -41
- package/engine/internal/devritespaths/paths.go +12 -28
- package/engine/internal/fsutil/copy.go +8 -0
- package/engine/internal/gate/gate.go +104 -105
- package/engine/internal/gate/gate_test.go +94 -84
- package/engine/internal/gate/readiness_binding.go +203 -0
- package/engine/internal/gate/readiness_binding_test.go +271 -0
- package/engine/internal/gitenv/env.go +46 -0
- package/engine/internal/gitenv/env_test.go +48 -0
- package/engine/internal/hostpack/hostpack.go +16 -20
- package/engine/internal/hostpack/hostpack_test.go +6 -6
- package/engine/internal/install/install.go +289 -511
- package/engine/internal/install/install_test.go +218 -306
- package/engine/internal/lib/candidate.go +475 -0
- package/engine/internal/lib/candidate_test.go +503 -0
- package/engine/internal/lib/closeout.go +17 -21
- package/engine/internal/lib/closeout_test.go +23 -0
- package/engine/internal/lib/cursor_compat_test.go +12 -75
- package/engine/internal/lib/evidencefresh.go +58 -47
- package/engine/internal/lib/evidencefresh_test.go +120 -0
- package/engine/internal/lib/resolve.go +13 -35
- package/engine/internal/lib/resolve_remediation_test.go +5 -17
- package/engine/internal/lib/secretscan.go +432 -0
- package/engine/internal/lib/secretscan_test.go +555 -0
- package/engine/internal/lib/util.go +24 -65
- package/engine/internal/lib/util_test.go +44 -0
- package/engine/internal/markdowntext/markdowntext.go +108 -0
- package/engine/internal/markdowntext/markdowntext_test.go +58 -0
- package/engine/internal/markdowntext/testdata/structural.json +27 -0
- package/engine/internal/reason/reason.go +2 -123
- package/engine/internal/reason/reason_test.go +4 -2
- package/engine/internal/rootfacts/facts.go +6 -8
- package/engine/internal/rootfacts/facts_test.go +20 -0
- package/engine/internal/state/cmd/workflowmanifest/main.go +10 -12
- package/engine/internal/state/cursor.go +53 -14
- package/engine/internal/state/cursor_test.go +57 -0
- package/engine/internal/state/feature.go +64 -154
- package/engine/internal/state/schema.go +24 -50
- package/engine/internal/state/state_test.go +151 -140
- package/engine/internal/state/status.go +4 -17
- package/engine/internal/state/workflow_manifest.json +9 -89
- package/engine/main.go +73 -502
- package/engine/root_routing_test.go +183 -212
- package/engine/testdata/fixtures/basic/devrites-root/{features/auth-tokens/feature.md → work/auth-tokens/README.md} +0 -1
- package/engine/testdata/fixtures/basic/devrites-root/work/auth-tokens/architecture.md +3 -0
- package/engine/testdata/fixtures/basic/devrites-root/work/auth-tokens/assumptions.md +3 -0
- package/engine/testdata/fixtures/basic/devrites-root/work/auth-tokens/brief.md +3 -0
- package/engine/testdata/fixtures/basic/devrites-root/work/auth-tokens/decision-coverage.md +3 -0
- package/engine/testdata/fixtures/basic/devrites-root/work/auth-tokens/eng-review.md +3 -0
- package/engine/testdata/fixtures/basic/devrites-root/work/auth-tokens/questions.md +3 -0
- package/engine/testdata/fixtures/basic/devrites-root/{features/auth-tokens/status.md → work/auth-tokens/state.md} +3 -0
- package/engine/testdata/fixtures/basic/devrites-root/work/auth-tokens/test-plan.md +3 -0
- package/engine/testdata/fixtures/basic/devrites-root/work/auth-tokens/traceability.md +3 -0
- package/engine/testdata/fixtures/basic/devrites-root/{features/search-ranking/feature.md → work/search-ranking/README.md} +0 -1
- package/engine/testdata/fixtures/basic/devrites-root/work/search-ranking/assumptions.md +3 -0
- package/engine/testdata/fixtures/basic/devrites-root/work/search-ranking/brief.md +3 -0
- package/engine/testdata/fixtures/basic/devrites-root/work/search-ranking/decisions.md +3 -0
- package/engine/testdata/fixtures/basic/devrites-root/work/search-ranking/questions.md +3 -0
- package/engine/testdata/fixtures/basic/devrites-root/work/search-ranking/state.md +2 -0
- package/engine/testdata/golden/TestParityCloseOut/active_pointed_elsewhere.golden +1 -1
- package/engine/testdata/golden/TestParityCloseOut/active_slug_cleared.golden +1 -1
- package/engine/tests/adr_0003_gate_exit_code_test.go +2 -2
- package/engine/tests/adr_0011_define_plan_test.go +2 -2
- package/engine/tests/adr_0027_readiness_binding_test.go +106 -0
- package/engine/tests/cli_test.go +24 -107
- package/engine/tests/gate_test.go +177 -200
- package/engine/tests/meta_test.go +5 -35
- package/engine/tests/parity_closeout_test.go +12 -12
- package/engine/tests/parity_resolve_test.go +6 -16
- package/engine/tests/parity_test.go +8 -302
- package/install.sh +201 -34
- package/pack/.claude/agents/devrites-code-reviewer.md +9 -18
- package/pack/.claude/agents/devrites-devex-reviewer.md +10 -18
- package/pack/.claude/agents/devrites-doubt-reviewer.md +10 -20
- package/pack/.claude/agents/devrites-evidence-scout.md +14 -26
- package/pack/.claude/agents/devrites-frontend-reviewer.md +9 -15
- package/pack/.claude/agents/devrites-performance-reviewer.md +13 -21
- package/pack/.claude/agents/devrites-plan-drafter.md +24 -28
- package/pack/.claude/agents/devrites-plan-reviewer.md +30 -47
- package/pack/.claude/agents/devrites-proof-runner.md +41 -47
- package/pack/.claude/agents/devrites-retrospector.md +34 -79
- package/pack/.claude/agents/devrites-security-auditor.md +9 -17
- package/pack/.claude/agents/devrites-simplifier-reviewer.md +9 -18
- package/pack/.claude/agents/devrites-slice-wright.md +44 -62
- package/pack/.claude/agents/devrites-spec-reviewer.md +38 -50
- package/pack/.claude/agents/devrites-strategy-reviewer.md +10 -19
- package/pack/.claude/agents/devrites-test-analyst.md +22 -21
- package/pack/.claude/agents/devrites-upgrade-planner.md +52 -71
- package/pack/.claude/settings.json +12 -100
- package/pack/.claude/skills/devrites-api-interface/SKILL.md +1 -2
- package/pack/.claude/skills/devrites-audit/SKILL.md +6 -9
- package/pack/.claude/skills/devrites-browser-proof/SKILL.md +0 -1
- package/pack/.claude/skills/devrites-debug-recovery/SKILL.md +21 -27
- package/pack/.claude/skills/devrites-debug-recovery/reference/cleanup-and-classify.md +22 -33
- package/pack/.claude/skills/devrites-doubt/SKILL.md +4 -6
- package/pack/.claude/skills/devrites-frontend-craft/SKILL.md +1 -2
- package/pack/.claude/skills/devrites-frontend-craft/reference/design-references.md +2 -1
- package/pack/.claude/skills/devrites-interview/SKILL.md +31 -36
- package/pack/.claude/skills/devrites-lib/SKILL.md +37 -66
- package/pack/.claude/skills/devrites-lib/reference/candidate-integrity.md +51 -0
- package/pack/.claude/skills/devrites-lib/reference/intent-map.md +20 -12
- package/pack/.claude/skills/devrites-lib/reference/orchestration-profiles.md +22 -0
- package/pack/.claude/skills/devrites-lib/reference/parallel-dispatch.md +35 -110
- package/pack/.claude/skills/devrites-lib/reference/reply-contract.md +26 -93
- package/pack/.claude/skills/devrites-lib/reference/standards/README.md +6 -3
- package/pack/.claude/skills/devrites-lib/reference/standards/afk-hitl.md +43 -34
- package/pack/.claude/skills/devrites-lib/reference/standards/agents.md +90 -216
- package/pack/.claude/skills/devrites-lib/reference/standards/anti-patterns.md +1 -1
- package/pack/.claude/skills/devrites-lib/reference/standards/code-review.md +9 -28
- package/pack/.claude/skills/devrites-lib/reference/standards/context-hygiene.md +13 -11
- package/pack/.claude/skills/devrites-lib/reference/standards/core.md +59 -54
- package/pack/.claude/skills/devrites-lib/reference/standards/hooks.md +3 -5
- package/pack/.claude/skills/devrites-lib/reference/standards/principles.md +29 -143
- package/pack/.claude/skills/devrites-lib/reference/standards/release/ship-checklist.md +5 -6
- package/pack/.claude/skills/devrites-lib/reference/standards/security.md +13 -31
- package/pack/.claude/skills/devrites-lib/reference/standards/skill-authoring.md +112 -82
- package/pack/.claude/skills/devrites-lib/reference/standards/spec-grammar.md +71 -45
- package/pack/.claude/skills/devrites-lib/reference/standards/testing.md +22 -11
- package/pack/.claude/skills/devrites-lib/reference/standards/tooling.md +15 -24
- package/pack/.claude/skills/devrites-lib/reference/workspace-artifact-schema.md +106 -37
- package/pack/.claude/skills/devrites-prose-craft/SKILL.md +3 -4
- package/pack/.claude/skills/devrites-source-driven/SKILL.md +3 -4
- package/pack/.claude/skills/devrites-ux-shape/SKILL.md +3 -3
- package/pack/.claude/skills/rite/SKILL.md +12 -13
- package/pack/.claude/skills/rite/reference/menu.md +11 -11
- package/pack/.claude/skills/rite-adopt/SKILL.md +29 -84
- package/pack/.claude/skills/rite-adopt/reference/adoption.md +16 -50
- package/pack/.claude/skills/rite-autocomplete/SKILL.md +65 -54
- package/pack/.claude/skills/rite-autocomplete/reference/loop.md +24 -17
- package/pack/.claude/skills/rite-autocomplete/reference/stop-conditions.md +18 -21
- package/pack/.claude/skills/rite-build/SKILL.md +28 -34
- package/pack/.claude/skills/rite-build/reference/afk-discipline.md +36 -34
- package/pack/.claude/skills/rite-build/reference/anti-patterns.md +5 -19
- package/pack/.claude/skills/rite-build/reference/checkpoint-protocol.md +10 -9
- package/pack/.claude/skills/rite-build/reference/checkpoint.md +17 -17
- package/pack/.claude/skills/rite-build/reference/frontend-trigger.md +3 -3
- package/pack/.claude/skills/rite-build/reference/one-slice-cycle.md +11 -14
- package/pack/.claude/skills/rite-build/reference/output.md +14 -24
- package/pack/.claude/skills/rite-build/reference/phase-contract.md +49 -188
- package/pack/.claude/skills/rite-build/reference/spec-drift-guard.md +1 -1
- package/pack/.claude/skills/rite-build/reference/wright-dispatch.md +43 -133
- package/pack/.claude/skills/rite-clarify/SKILL.md +46 -40
- package/pack/.claude/skills/rite-clarify/reference/decision-coverage.md +4 -10
- package/pack/.claude/skills/rite-converge/SKILL.md +9 -33
- package/pack/.claude/skills/rite-converge/reference/convergence-assessment.md +2 -2
- package/pack/.claude/skills/rite-customize/SKILL.md +34 -50
- package/pack/.claude/skills/rite-define/SKILL.md +34 -58
- package/pack/.claude/skills/rite-define/reference/plan-template.md +15 -0
- package/pack/.claude/skills/rite-doctor/SKILL.md +52 -64
- package/pack/.claude/skills/rite-dogfood/SKILL.md +1 -2
- package/pack/.claude/skills/rite-explain/SKILL.md +1 -20
- package/pack/.claude/skills/rite-frame/SKILL.md +1 -7
- package/pack/.claude/skills/rite-frame/reference/failure-modes.md +1 -1
- package/pack/.claude/skills/rite-handoff/SKILL.md +1 -2
- package/pack/.claude/skills/rite-handoff/reference/handoff-template.md +1 -1
- package/pack/.claude/skills/rite-learn/SKILL.md +44 -104
- package/pack/.claude/skills/rite-plan/SKILL.md +14 -26
- package/pack/.claude/skills/rite-plan/reference/task-breakdown.md +4 -1
- package/pack/.claude/skills/rite-polish/SKILL.md +20 -28
- package/pack/.claude/skills/rite-polish/reference/adr-promotion.md +11 -0
- package/pack/{generated/claude/skills/rite-ship → .claude/skills/rite-polish}/reference/design-memory.md +23 -21
- package/pack/.claude/skills/rite-polish/reference/ledger.md +62 -0
- package/pack/.claude/skills/rite-pov/SKILL.md +28 -32
- package/pack/.claude/skills/rite-pr-feedback/SKILL.md +0 -1
- package/pack/.claude/skills/rite-pressure-test/SKILL.md +6 -13
- package/pack/.claude/skills/rite-prototype/SKILL.md +0 -6
- package/pack/.claude/skills/rite-prove/SKILL.md +73 -68
- package/pack/.claude/skills/rite-prove/reference/acceptance-proof.md +34 -30
- package/pack/.claude/skills/rite-prove/reference/test-command-discovery.md +15 -14
- package/pack/.claude/skills/rite-quick/SKILL.md +5 -19
- package/pack/.claude/skills/rite-resolve/SKILL.md +4 -21
- package/pack/.claude/skills/rite-resolve/reference/answer-protocol.md +2 -2
- package/pack/.claude/skills/rite-review/SKILL.md +34 -57
- package/pack/.claude/skills/rite-seal/SKILL.md +54 -87
- package/pack/.claude/skills/rite-seal/reference/anti-patterns.md +2 -2
- package/pack/.claude/skills/rite-seal/reference/final-evidence.md +31 -56
- package/pack/.claude/skills/rite-seal/reference/go-no-go.md +27 -51
- package/pack/.claude/skills/rite-seal/reference/output.md +2 -4
- package/pack/.claude/skills/rite-seal/reference/phase-contract.md +42 -140
- package/pack/.claude/skills/rite-seal/reference/risk-and-rollback.md +2 -2
- package/pack/.claude/skills/rite-seal/reference/seal-template.md +17 -28
- package/pack/.claude/skills/rite-ship/SKILL.md +65 -93
- package/pack/.claude/skills/rite-ship/reference/anti-patterns.md +4 -1
- package/pack/.claude/skills/rite-ship/reference/close-out.md +4 -12
- package/pack/.claude/skills/rite-ship/reference/git-ship.md +86 -48
- package/pack/.claude/skills/rite-ship/reference/ship-template.md +22 -16
- package/pack/.claude/skills/rite-spec/SKILL.md +62 -95
- package/pack/.claude/skills/rite-spec/reference/investigation.md +1 -4
- package/pack/.claude/skills/rite-spec/reference/question-protocol.md +21 -20
- package/pack/.claude/skills/rite-spec/reference/spec-checklists.md +16 -17
- package/pack/.claude/skills/rite-spec/reference/spec-template.md +47 -49
- package/pack/.claude/skills/rite-spec/reference/state-workspace.md +28 -13
- package/pack/.claude/skills/rite-status/SKILL.md +30 -88
- package/pack/.claude/skills/rite-temper/SKILL.md +12 -31
- package/pack/.claude/skills/rite-upgrade/SKILL.md +86 -101
- package/pack/.claude/skills/rite-vet/SKILL.md +88 -117
- package/pack/.claude/skills/rite-vet/reference/artifacts.md +56 -50
- package/pack/.claude/skills/rite-vet/reference/cross-model.md +15 -39
- package/pack/.claude/skills/rite-vet/reference/depth.md +2 -2
- package/pack/.claude/skills/rite-vet/reference/review-axes.md +3 -6
- package/pack/.claude/skills/rite-zoom-out/SKILL.md +0 -6
- package/pack/generated/README.md +2 -2
- package/pack/generated/claude/agents/devrites-code-reviewer.md +9 -18
- package/pack/generated/claude/agents/devrites-devex-reviewer.md +10 -18
- package/pack/generated/claude/agents/devrites-doubt-reviewer.md +10 -20
- package/pack/generated/claude/agents/devrites-evidence-scout.md +14 -26
- package/pack/generated/claude/agents/devrites-frontend-reviewer.md +9 -15
- package/pack/generated/claude/agents/devrites-performance-reviewer.md +13 -21
- package/pack/generated/claude/agents/devrites-plan-drafter.md +24 -28
- package/pack/generated/claude/agents/devrites-plan-reviewer.md +30 -47
- package/pack/generated/claude/agents/devrites-proof-runner.md +41 -47
- package/pack/generated/claude/agents/devrites-retrospector.md +34 -79
- package/pack/generated/claude/agents/devrites-security-auditor.md +9 -17
- package/pack/generated/claude/agents/devrites-simplifier-reviewer.md +9 -18
- package/pack/generated/claude/agents/devrites-slice-wright.md +44 -62
- package/pack/generated/claude/agents/devrites-spec-reviewer.md +38 -50
- package/pack/generated/claude/agents/devrites-strategy-reviewer.md +10 -19
- package/pack/generated/claude/agents/devrites-test-analyst.md +22 -21
- package/pack/generated/claude/agents/devrites-upgrade-planner.md +52 -71
- package/pack/generated/claude/settings.json +12 -100
- package/pack/generated/claude/skills/devrites-api-interface/SKILL.md +1 -2
- package/pack/generated/claude/skills/devrites-audit/SKILL.md +6 -9
- package/pack/generated/claude/skills/devrites-browser-proof/SKILL.md +0 -1
- package/pack/generated/claude/skills/devrites-debug-recovery/SKILL.md +21 -27
- package/pack/generated/claude/skills/devrites-debug-recovery/reference/cleanup-and-classify.md +22 -33
- package/pack/generated/claude/skills/devrites-doubt/SKILL.md +4 -6
- package/pack/generated/claude/skills/devrites-frontend-craft/SKILL.md +1 -2
- package/pack/generated/claude/skills/devrites-frontend-craft/reference/design-references.md +2 -1
- package/pack/generated/claude/skills/devrites-interview/SKILL.md +31 -36
- package/pack/generated/claude/skills/devrites-lib/SKILL.md +37 -66
- package/pack/generated/claude/skills/devrites-lib/reference/candidate-integrity.md +51 -0
- package/pack/generated/claude/skills/devrites-lib/reference/intent-map.md +20 -12
- package/pack/generated/claude/skills/devrites-lib/reference/orchestration-profiles.md +22 -0
- package/pack/generated/claude/skills/devrites-lib/reference/parallel-dispatch.md +35 -110
- package/pack/generated/claude/skills/devrites-lib/reference/reply-contract.md +26 -93
- package/pack/generated/claude/skills/devrites-lib/reference/standards/README.md +6 -3
- package/pack/generated/claude/skills/devrites-lib/reference/standards/afk-hitl.md +43 -34
- package/pack/generated/claude/skills/devrites-lib/reference/standards/agents.md +90 -216
- package/pack/generated/claude/skills/devrites-lib/reference/standards/anti-patterns.md +1 -1
- package/pack/generated/claude/skills/devrites-lib/reference/standards/code-review.md +9 -28
- package/pack/generated/claude/skills/devrites-lib/reference/standards/context-hygiene.md +13 -11
- package/pack/generated/claude/skills/devrites-lib/reference/standards/core.md +59 -54
- package/pack/generated/claude/skills/devrites-lib/reference/standards/hooks.md +3 -5
- package/pack/generated/claude/skills/devrites-lib/reference/standards/principles.md +29 -143
- package/pack/generated/claude/skills/devrites-lib/reference/standards/release/ship-checklist.md +5 -6
- package/pack/generated/claude/skills/devrites-lib/reference/standards/security.md +13 -31
- package/pack/generated/claude/skills/devrites-lib/reference/standards/skill-authoring.md +112 -82
- package/pack/generated/claude/skills/devrites-lib/reference/standards/spec-grammar.md +71 -45
- package/pack/generated/claude/skills/devrites-lib/reference/standards/testing.md +22 -11
- package/pack/generated/claude/skills/devrites-lib/reference/standards/tooling.md +15 -24
- package/pack/generated/claude/skills/devrites-lib/reference/workspace-artifact-schema.md +106 -37
- package/pack/generated/claude/skills/devrites-prose-craft/SKILL.md +3 -4
- package/pack/generated/claude/skills/devrites-source-driven/SKILL.md +3 -4
- package/pack/generated/claude/skills/devrites-ux-shape/SKILL.md +3 -3
- package/pack/generated/claude/skills/rite/SKILL.md +12 -13
- package/pack/generated/claude/skills/rite/reference/menu.md +11 -11
- package/pack/generated/claude/skills/rite-adopt/SKILL.md +29 -84
- package/pack/generated/claude/skills/rite-adopt/reference/adoption.md +16 -50
- package/pack/generated/claude/skills/rite-autocomplete/SKILL.md +65 -54
- package/pack/generated/claude/skills/rite-autocomplete/reference/loop.md +24 -17
- package/pack/generated/claude/skills/rite-autocomplete/reference/stop-conditions.md +18 -21
- package/pack/generated/claude/skills/rite-build/SKILL.md +28 -34
- package/pack/generated/claude/skills/rite-build/reference/afk-discipline.md +36 -34
- package/pack/generated/claude/skills/rite-build/reference/anti-patterns.md +5 -19
- package/pack/generated/claude/skills/rite-build/reference/checkpoint-protocol.md +10 -9
- package/pack/generated/claude/skills/rite-build/reference/checkpoint.md +17 -17
- package/pack/generated/claude/skills/rite-build/reference/frontend-trigger.md +3 -3
- package/pack/generated/claude/skills/rite-build/reference/one-slice-cycle.md +11 -14
- package/pack/generated/claude/skills/rite-build/reference/output.md +14 -24
- package/pack/generated/claude/skills/rite-build/reference/phase-contract.md +49 -188
- package/pack/generated/claude/skills/rite-build/reference/spec-drift-guard.md +1 -1
- package/pack/generated/claude/skills/rite-build/reference/wright-dispatch.md +43 -133
- package/pack/generated/claude/skills/rite-clarify/SKILL.md +46 -40
- package/pack/generated/claude/skills/rite-clarify/reference/decision-coverage.md +4 -10
- package/pack/generated/claude/skills/rite-converge/SKILL.md +9 -33
- package/pack/generated/claude/skills/rite-converge/reference/convergence-assessment.md +2 -2
- package/pack/generated/claude/skills/rite-customize/SKILL.md +34 -50
- package/pack/generated/claude/skills/rite-define/SKILL.md +34 -58
- package/pack/generated/claude/skills/rite-define/reference/plan-template.md +15 -0
- package/pack/generated/claude/skills/rite-doctor/SKILL.md +52 -64
- package/pack/generated/claude/skills/rite-dogfood/SKILL.md +1 -2
- package/pack/generated/claude/skills/rite-explain/SKILL.md +1 -20
- package/pack/generated/claude/skills/rite-frame/SKILL.md +1 -7
- package/pack/generated/claude/skills/rite-frame/reference/failure-modes.md +1 -1
- package/pack/generated/claude/skills/rite-handoff/SKILL.md +1 -2
- package/pack/generated/claude/skills/rite-handoff/reference/handoff-template.md +1 -1
- package/pack/generated/claude/skills/rite-learn/SKILL.md +44 -104
- package/pack/generated/claude/skills/rite-plan/SKILL.md +14 -26
- package/pack/generated/claude/skills/rite-plan/reference/task-breakdown.md +4 -1
- package/pack/generated/claude/skills/rite-polish/SKILL.md +20 -28
- package/pack/generated/claude/skills/rite-polish/reference/adr-promotion.md +11 -0
- package/pack/generated/{codex/skills/rite-ship → claude/skills/rite-polish}/reference/design-memory.md +23 -21
- package/pack/generated/claude/skills/rite-polish/reference/ledger.md +62 -0
- package/pack/generated/claude/skills/rite-pov/SKILL.md +28 -32
- package/pack/generated/claude/skills/rite-pr-feedback/SKILL.md +0 -1
- package/pack/generated/claude/skills/rite-pressure-test/SKILL.md +6 -13
- package/pack/generated/claude/skills/rite-prototype/SKILL.md +0 -6
- package/pack/generated/claude/skills/rite-prove/SKILL.md +73 -68
- package/pack/generated/claude/skills/rite-prove/reference/acceptance-proof.md +34 -30
- package/pack/generated/claude/skills/rite-prove/reference/test-command-discovery.md +15 -14
- package/pack/generated/claude/skills/rite-quick/SKILL.md +5 -19
- package/pack/generated/claude/skills/rite-resolve/SKILL.md +4 -21
- package/pack/generated/claude/skills/rite-resolve/reference/answer-protocol.md +2 -2
- package/pack/generated/claude/skills/rite-review/SKILL.md +34 -57
- package/pack/generated/claude/skills/rite-seal/SKILL.md +54 -87
- package/pack/generated/claude/skills/rite-seal/reference/anti-patterns.md +2 -2
- package/pack/generated/claude/skills/rite-seal/reference/final-evidence.md +31 -56
- package/pack/generated/claude/skills/rite-seal/reference/go-no-go.md +27 -51
- package/pack/generated/claude/skills/rite-seal/reference/output.md +2 -4
- package/pack/generated/claude/skills/rite-seal/reference/phase-contract.md +42 -140
- package/pack/generated/claude/skills/rite-seal/reference/risk-and-rollback.md +2 -2
- package/pack/generated/claude/skills/rite-seal/reference/seal-template.md +17 -28
- package/pack/generated/claude/skills/rite-ship/SKILL.md +65 -93
- package/pack/generated/claude/skills/rite-ship/reference/anti-patterns.md +4 -1
- package/pack/generated/claude/skills/rite-ship/reference/close-out.md +4 -12
- package/pack/generated/claude/skills/rite-ship/reference/git-ship.md +86 -48
- package/pack/generated/claude/skills/rite-ship/reference/ship-template.md +22 -16
- package/pack/generated/claude/skills/rite-spec/SKILL.md +62 -95
- package/pack/generated/claude/skills/rite-spec/reference/investigation.md +1 -4
- package/pack/generated/claude/skills/rite-spec/reference/question-protocol.md +21 -20
- package/pack/generated/claude/skills/rite-spec/reference/spec-checklists.md +16 -17
- package/pack/generated/claude/skills/rite-spec/reference/spec-template.md +47 -49
- package/pack/generated/claude/skills/rite-spec/reference/state-workspace.md +28 -13
- package/pack/generated/claude/skills/rite-status/SKILL.md +30 -88
- package/pack/generated/claude/skills/rite-temper/SKILL.md +12 -31
- package/pack/generated/claude/skills/rite-upgrade/SKILL.md +86 -101
- package/pack/generated/claude/skills/rite-vet/SKILL.md +88 -117
- package/pack/generated/claude/skills/rite-vet/reference/artifacts.md +56 -50
- package/pack/generated/claude/skills/rite-vet/reference/cross-model.md +15 -39
- package/pack/generated/claude/skills/rite-vet/reference/depth.md +2 -2
- package/pack/generated/claude/skills/rite-vet/reference/review-axes.md +3 -6
- package/pack/generated/claude/skills/rite-zoom-out/SKILL.md +0 -6
- package/pack/generated/codex/AGENTS.md +5 -4
- package/pack/generated/codex/agents/devrites-code-reviewer.toml +8 -27
- package/pack/generated/codex/agents/devrites-devex-reviewer.toml +9 -27
- package/pack/generated/codex/agents/devrites-doubt-reviewer.toml +9 -29
- package/pack/generated/codex/agents/devrites-evidence-scout.toml +14 -36
- package/pack/generated/codex/agents/devrites-frontend-reviewer.toml +8 -24
- package/pack/generated/codex/agents/devrites-performance-reviewer.toml +12 -30
- package/pack/generated/codex/agents/devrites-plan-drafter.toml +24 -38
- package/pack/generated/codex/agents/devrites-plan-reviewer.toml +30 -57
- package/pack/generated/codex/agents/devrites-proof-runner.toml +41 -57
- package/pack/generated/codex/agents/devrites-retrospector.toml +34 -89
- package/pack/generated/codex/agents/devrites-security-auditor.toml +8 -26
- package/pack/generated/codex/agents/devrites-simplifier-reviewer.toml +8 -27
- package/pack/generated/codex/agents/devrites-slice-wright.toml +44 -71
- package/pack/generated/codex/agents/devrites-spec-reviewer.toml +38 -60
- package/pack/generated/codex/agents/devrites-strategy-reviewer.toml +9 -28
- package/pack/generated/codex/agents/devrites-test-analyst.toml +21 -30
- package/pack/generated/codex/agents/devrites-upgrade-planner.toml +52 -81
- package/pack/generated/codex/config.toml +7 -0
- package/pack/generated/codex/skills/devrites-api-interface/SKILL.md +1 -21
- package/pack/generated/codex/skills/devrites-audit/SKILL.md +6 -28
- package/pack/generated/codex/skills/devrites-browser-proof/SKILL.md +1 -21
- package/pack/generated/codex/skills/devrites-debug-recovery/SKILL.md +21 -46
- package/pack/generated/codex/skills/devrites-debug-recovery/reference/cleanup-and-classify.md +22 -33
- package/pack/generated/codex/skills/devrites-doubt/SKILL.md +4 -25
- package/pack/generated/codex/skills/devrites-frontend-craft/SKILL.md +1 -21
- package/pack/generated/codex/skills/devrites-frontend-craft/reference/design-references.md +2 -1
- package/pack/generated/codex/skills/devrites-interview/SKILL.md +32 -56
- package/pack/generated/codex/skills/devrites-lib/SKILL.md +38 -86
- package/pack/generated/codex/skills/devrites-lib/reference/candidate-integrity.md +51 -0
- package/pack/generated/codex/skills/devrites-lib/reference/intent-map.md +20 -12
- package/pack/generated/codex/skills/devrites-lib/reference/orchestration-profiles.md +22 -0
- package/pack/generated/codex/skills/devrites-lib/reference/parallel-dispatch.md +35 -110
- package/pack/generated/codex/skills/devrites-lib/reference/reply-contract.md +26 -93
- package/pack/generated/codex/skills/devrites-lib/reference/standards/README.md +6 -3
- package/pack/generated/codex/skills/devrites-lib/reference/standards/afk-hitl.md +43 -34
- package/pack/generated/codex/skills/devrites-lib/reference/standards/agents.md +90 -216
- package/pack/generated/codex/skills/devrites-lib/reference/standards/anti-patterns.md +1 -1
- package/pack/generated/codex/skills/devrites-lib/reference/standards/code-review.md +9 -28
- package/pack/generated/codex/skills/devrites-lib/reference/standards/context-hygiene.md +13 -11
- package/pack/generated/codex/skills/devrites-lib/reference/standards/core.md +59 -54
- package/pack/generated/codex/skills/devrites-lib/reference/standards/hooks.md +3 -5
- package/pack/generated/codex/skills/devrites-lib/reference/standards/principles.md +29 -143
- package/pack/generated/codex/skills/devrites-lib/reference/standards/release/ship-checklist.md +5 -6
- package/pack/generated/codex/skills/devrites-lib/reference/standards/security.md +13 -31
- package/pack/generated/codex/skills/devrites-lib/reference/standards/skill-authoring.md +112 -82
- package/pack/generated/codex/skills/devrites-lib/reference/standards/spec-grammar.md +71 -45
- package/pack/generated/codex/skills/devrites-lib/reference/standards/testing.md +22 -11
- package/pack/generated/codex/skills/devrites-lib/reference/standards/tooling.md +15 -24
- package/pack/generated/codex/skills/devrites-lib/reference/workspace-artifact-schema.md +106 -37
- package/pack/generated/codex/skills/devrites-prose-craft/SKILL.md +3 -23
- package/pack/generated/codex/skills/devrites-source-driven/SKILL.md +3 -23
- package/pack/generated/codex/skills/devrites-ux-shape/SKILL.md +3 -22
- package/pack/generated/codex/skills/rite/SKILL.md +12 -32
- package/pack/generated/codex/skills/rite/reference/menu.md +11 -11
- package/pack/generated/codex/skills/rite-adopt/SKILL.md +29 -103
- package/pack/generated/codex/skills/rite-adopt/reference/adoption.md +16 -50
- package/pack/generated/codex/skills/rite-autocomplete/SKILL.md +65 -73
- package/pack/generated/codex/skills/rite-autocomplete/reference/loop.md +24 -17
- package/pack/generated/codex/skills/rite-autocomplete/reference/stop-conditions.md +18 -21
- package/pack/generated/codex/skills/rite-build/SKILL.md +28 -53
- package/pack/generated/codex/skills/rite-build/reference/afk-discipline.md +36 -34
- package/pack/generated/codex/skills/rite-build/reference/anti-patterns.md +5 -19
- package/pack/generated/codex/skills/rite-build/reference/checkpoint-protocol.md +10 -9
- package/pack/generated/codex/skills/rite-build/reference/checkpoint.md +17 -17
- package/pack/generated/codex/skills/rite-build/reference/frontend-trigger.md +3 -3
- package/pack/generated/codex/skills/rite-build/reference/one-slice-cycle.md +11 -14
- package/pack/generated/codex/skills/rite-build/reference/output.md +14 -24
- package/pack/generated/codex/skills/rite-build/reference/phase-contract.md +49 -188
- package/pack/generated/codex/skills/rite-build/reference/spec-drift-guard.md +1 -1
- package/pack/generated/codex/skills/rite-build/reference/wright-dispatch.md +43 -133
- package/pack/generated/codex/skills/rite-clarify/SKILL.md +46 -59
- package/pack/generated/codex/skills/rite-clarify/reference/decision-coverage.md +4 -10
- package/pack/generated/codex/skills/rite-converge/SKILL.md +9 -52
- package/pack/generated/codex/skills/rite-converge/reference/convergence-assessment.md +2 -2
- package/pack/generated/codex/skills/rite-customize/SKILL.md +32 -67
- package/pack/generated/codex/skills/rite-define/SKILL.md +34 -77
- package/pack/generated/codex/skills/rite-define/reference/plan-template.md +15 -0
- package/pack/generated/codex/skills/rite-doctor/SKILL.md +52 -83
- package/pack/generated/codex/skills/rite-dogfood/SKILL.md +1 -21
- package/pack/generated/codex/skills/rite-explain/SKILL.md +1 -39
- package/pack/generated/codex/skills/rite-frame/SKILL.md +1 -26
- package/pack/generated/codex/skills/rite-frame/reference/failure-modes.md +1 -1
- package/pack/generated/codex/skills/rite-handoff/SKILL.md +1 -21
- package/pack/generated/codex/skills/rite-handoff/reference/handoff-template.md +1 -1
- package/pack/generated/codex/skills/rite-learn/SKILL.md +44 -123
- package/pack/generated/codex/skills/rite-plan/SKILL.md +14 -45
- package/pack/generated/codex/skills/rite-plan/reference/task-breakdown.md +4 -1
- package/pack/generated/codex/skills/rite-polish/SKILL.md +20 -47
- package/pack/generated/codex/skills/rite-polish/reference/adr-promotion.md +11 -0
- package/pack/{.claude/skills/rite-ship → generated/codex/skills/rite-polish}/reference/design-memory.md +23 -21
- package/pack/generated/codex/skills/rite-polish/reference/ledger.md +62 -0
- package/pack/generated/codex/skills/rite-pov/SKILL.md +28 -51
- package/pack/generated/codex/skills/rite-pr-feedback/SKILL.md +0 -20
- package/pack/generated/codex/skills/rite-pressure-test/SKILL.md +6 -32
- package/pack/generated/codex/skills/rite-prototype/SKILL.md +0 -25
- package/pack/generated/codex/skills/rite-prove/SKILL.md +73 -87
- package/pack/generated/codex/skills/rite-prove/reference/acceptance-proof.md +34 -30
- package/pack/generated/codex/skills/rite-prove/reference/test-command-discovery.md +15 -14
- package/pack/generated/codex/skills/rite-quick/SKILL.md +5 -38
- package/pack/generated/codex/skills/rite-resolve/SKILL.md +4 -40
- package/pack/generated/codex/skills/rite-resolve/reference/answer-protocol.md +2 -2
- package/pack/generated/codex/skills/rite-review/SKILL.md +34 -76
- package/pack/generated/codex/skills/rite-seal/SKILL.md +51 -103
- package/pack/generated/codex/skills/rite-seal/reference/anti-patterns.md +2 -2
- package/pack/generated/codex/skills/rite-seal/reference/final-evidence.md +31 -56
- package/pack/generated/codex/skills/rite-seal/reference/go-no-go.md +27 -51
- package/pack/generated/codex/skills/rite-seal/reference/output.md +2 -4
- package/pack/generated/codex/skills/rite-seal/reference/phase-contract.md +42 -140
- package/pack/generated/codex/skills/rite-seal/reference/risk-and-rollback.md +2 -2
- package/pack/generated/codex/skills/rite-seal/reference/seal-template.md +17 -28
- package/pack/generated/codex/skills/rite-ship/SKILL.md +65 -112
- package/pack/generated/codex/skills/rite-ship/reference/anti-patterns.md +4 -1
- package/pack/generated/codex/skills/rite-ship/reference/close-out.md +4 -12
- package/pack/generated/codex/skills/rite-ship/reference/git-ship.md +86 -48
- package/pack/generated/codex/skills/rite-ship/reference/ship-template.md +22 -16
- package/pack/generated/codex/skills/rite-spec/SKILL.md +62 -114
- package/pack/generated/codex/skills/rite-spec/reference/investigation.md +1 -4
- package/pack/generated/codex/skills/rite-spec/reference/question-protocol.md +21 -20
- package/pack/generated/codex/skills/rite-spec/reference/spec-checklists.md +16 -17
- package/pack/generated/codex/skills/rite-spec/reference/spec-template.md +47 -49
- package/pack/generated/codex/skills/rite-spec/reference/state-workspace.md +28 -13
- package/pack/generated/codex/skills/rite-status/SKILL.md +30 -107
- package/pack/generated/codex/skills/rite-temper/SKILL.md +12 -50
- package/pack/generated/codex/skills/rite-upgrade/SKILL.md +86 -120
- package/pack/generated/codex/skills/rite-vet/SKILL.md +88 -136
- package/pack/generated/codex/skills/rite-vet/reference/artifacts.md +56 -50
- package/pack/generated/codex/skills/rite-vet/reference/cross-model.md +15 -39
- package/pack/generated/codex/skills/rite-vet/reference/depth.md +2 -2
- package/pack/generated/codex/skills/rite-vet/reference/review-axes.md +3 -6
- package/pack/generated/codex/skills/rite-zoom-out/SKILL.md +0 -25
- package/package.json +6 -5
- package/scripts/build-host-artifacts.sh +5 -9
- package/scripts/codex-generate.sh +18 -278
- package/scripts/install-lib.sh +121 -34
- package/uninstall.sh +19 -17
- package/update.sh +26 -18
- package/docs/engine/agent-contract.md +0 -172
- package/docs/extensions.md +0 -178
- package/engine/git_guard.go +0 -187
- package/engine/git_guard_test.go +0 -283
- package/engine/harnessmatrix.go +0 -60
- package/engine/hookpolicy.go +0 -113
- package/engine/hookpolicy_test.go +0 -160
- package/engine/hooks.go +0 -677
- package/engine/hooks_agent_dispatch.go +0 -1637
- package/engine/hooks_events_test.go +0 -431
- package/engine/hooks_workspace.go +0 -1196
- package/engine/internal/doctor/doctor.go +0 -306
- package/engine/internal/doctor/doctor_test.go +0 -252
- package/engine/internal/forge/forge.go +0 -938
- package/engine/internal/forge/forge_test.go +0 -576
- package/engine/internal/forge/git.go +0 -245
- package/engine/internal/forge/liveness_unix.go +0 -48
- package/engine/internal/forge/liveness_windows.go +0 -67
- package/engine/internal/forge/manifest.go +0 -402
- package/engine/internal/harness/compliance.go +0 -96
- package/engine/internal/harness/harness.go +0 -301
- package/engine/internal/harness/harness_test.go +0 -124
- package/engine/internal/iohooks/detach_other.go +0 -11
- package/engine/internal/iohooks/detach_unix.go +0 -11
- package/engine/internal/iohooks/iohooks.go +0 -839
- package/engine/internal/iohooks/iohooks_test.go +0 -561
- package/engine/internal/lib/adoption.go +0 -659
- package/engine/internal/lib/adoption_test.go +0 -121
- package/engine/internal/lib/analyze.go +0 -232
- package/engine/internal/lib/archivesearch.go +0 -125
- package/engine/internal/lib/archivesearch_test.go +0 -138
- package/engine/internal/lib/budget.go +0 -172
- package/engine/internal/lib/buildreadiness.go +0 -111
- package/engine/internal/lib/checkacceptance.go +0 -176
- package/engine/internal/lib/clarifyreturn.go +0 -91
- package/engine/internal/lib/clarifyreturn_test.go +0 -123
- package/engine/internal/lib/command_other.go +0 -7
- package/engine/internal/lib/command_unix.go +0 -24
- package/engine/internal/lib/context.go +0 -280
- package/engine/internal/lib/conventions.go +0 -449
- package/engine/internal/lib/coverage.go +0 -145
- package/engine/internal/lib/dispatchwaive.go +0 -31
- package/engine/internal/lib/dispatchwaive_test.go +0 -23
- package/engine/internal/lib/doubtcoverage.go +0 -78
- package/engine/internal/lib/extensions.go +0 -622
- package/engine/internal/lib/extensions_test.go +0 -305
- package/engine/internal/lib/footprint.go +0 -239
- package/engine/internal/lib/gitauthority.go +0 -601
- package/engine/internal/lib/gitauthority_test.go +0 -346
- package/engine/internal/lib/jsonout.go +0 -129
- package/engine/internal/lib/jsonout_test.go +0 -73
- package/engine/internal/lib/lanes.go +0 -112
- package/engine/internal/lib/learnings.go +0 -397
- package/engine/internal/lib/learnings_inject_test.go +0 -77
- package/engine/internal/lib/learnings_remediation_test.go +0 -24
- package/engine/internal/lib/ledger.go +0 -311
- package/engine/internal/lib/ledger_test.go +0 -186
- package/engine/internal/lib/mutationgate.go +0 -89
- package/engine/internal/lib/observability_test.go +0 -358
- package/engine/internal/lib/overrides.go +0 -164
- package/engine/internal/lib/overrides_test.go +0 -112
- package/engine/internal/lib/preamble.go +0 -159
- package/engine/internal/lib/preamble_questions_test.go +0 -10
- package/engine/internal/lib/progress.go +0 -210
- package/engine/internal/lib/provenance.go +0 -462
- package/engine/internal/lib/provenance_test.go +0 -154
- package/engine/internal/lib/readiness_contract.json +0 -118
- package/engine/internal/lib/readinessartifact.go +0 -533
- package/engine/internal/lib/readinessartifact_test.go +0 -422
- package/engine/internal/lib/reconcile.go +0 -1496
- package/engine/internal/lib/reconcile_test.go +0 -1071
- package/engine/internal/lib/recoveryattempts.go +0 -298
- package/engine/internal/lib/recoveryattempts_test.go +0 -215
- package/engine/internal/lib/reviewers.go +0 -143
- package/engine/internal/lib/reviewers_test.go +0 -45
- package/engine/internal/lib/reviewerstats.go +0 -199
- package/engine/internal/lib/reviewerstats_test.go +0 -104
- package/engine/internal/lib/reviewintegrity.go +0 -143
- package/engine/internal/lib/reviewintegrity_test.go +0 -103
- package/engine/internal/lib/runbook.go +0 -295
- package/engine/internal/lib/runbook_context_test.go +0 -182
- package/engine/internal/lib/session.go +0 -1020
- package/engine/internal/lib/session_test.go +0 -177
- package/engine/internal/lib/specparse.go +0 -125
- package/engine/internal/lib/specskeleton.go +0 -79
- package/engine/internal/lib/specvalidate.go +0 -391
- package/engine/internal/lib/specvalidate_test.go +0 -185
- package/engine/internal/lib/stuck.go +0 -206
- package/engine/internal/lib/templates/conventions_drift.tmpl +0 -6
- package/engine/internal/lib/templates/conventions_ledger_header.md +0 -6
- package/engine/internal/lib/templates/conventions_orient_header.txt +0 -1
- package/engine/internal/lib/testintegrity.go +0 -199
- package/engine/internal/lib/testintegrity_test.go +0 -149
- package/engine/internal/lib/tickafk.go +0 -86
- package/engine/internal/migrate/migrate.go +0 -279
- package/engine/internal/migrate/migrate_test.go +0 -157
- package/engine/internal/orient/firstrun.go +0 -50
- package/engine/internal/orient/firstrun_test.go +0 -72
- package/engine/internal/orient/orient.go +0 -69
- package/engine/internal/orient/orient_test.go +0 -25
- package/engine/internal/profile/profile.go +0 -284
- package/engine/internal/profile/profile_test.go +0 -13
- package/engine/internal/state/clarify_transition.go +0 -165
- package/engine/internal/state/clarify_transition_test.go +0 -130
- package/engine/internal/state/snapshot.go +0 -457
- package/engine/internal/toolpolicy/classifier.go +0 -533
- package/engine/internal/toolpolicy/classifier_test.go +0 -424
- package/engine/internal/toolpolicy/git.go +0 -616
- package/engine/internal/toolpolicy/scanner.go +0 -382
- package/engine/observability_cli_test.go +0 -52
- package/engine/profile_test.go +0 -76
- package/engine/subagent_orient_context.txt +0 -8
- package/engine/testdata/golden/TestParityA1Guard/devrites-edit-allowed.golden +0 -1
- package/engine/testdata/golden/TestParityA1Guard/no-window-silent.golden +0 -1
- package/engine/testdata/golden/TestParityA1Guard/non-edit-tool.golden +0 -1
- package/engine/testdata/golden/TestParityA1Guard/observe-mode-silent.golden +0 -1
- package/engine/testdata/golden/TestParityA1Guard/source-edit-enforce-denies.golden +0 -2
- package/engine/testdata/golden/TestParityA1Guard/subagent-allowed.golden +0 -1
- package/engine/testdata/golden/TestParityAnalyze/noactive.golden +0 -1
- package/engine/testdata/golden/TestParityAnalyze/slug=covered.golden +0 -15
- package/engine/testdata/golden/TestParityAnalyze/slug=dangling.golden +0 -16
- package/engine/testdata/golden/TestParityAnalyze/slug=ghost.golden +0 -1
- package/engine/testdata/golden/TestParityAnalyze/slug=noac.golden +0 -15
- package/engine/testdata/golden/TestParityAnalyze/slug=order.golden +0 -17
- package/engine/testdata/golden/TestParityAnalyze/slug=orphan.golden +0 -16
- package/engine/testdata/golden/TestParityAnalyze/slug=uncovered.golden +0 -16
- package/engine/testdata/golden/TestParityBudget/default-active.golden +0 -4
- package/engine/testdata/golden/TestParityBudget/extra-md.golden +0 -5
- package/engine/testdata/golden/TestParityBudget/no-active.golden +0 -1
- package/engine/testdata/golden/TestParityBudget/no-feature.golden +0 -1
- package/engine/testdata/golden/TestParityBudget/over-advisory.golden +0 -6
- package/engine/testdata/golden/TestParityBudget/over-strict.golden +0 -6
- package/engine/testdata/golden/TestParityBudget/within.golden +0 -6
- package/engine/testdata/golden/TestParityBuildReadiness/arg=approved.golden +0 -2
- package/engine/testdata/golden/TestParityBuildReadiness/arg=approvenone.golden +0 -1
- package/engine/testdata/golden/TestParityBuildReadiness/arg=await.golden +0 -1
- package/engine/testdata/golden/TestParityBuildReadiness/arg=blocked.golden +0 -1
- package/engine/testdata/golden/TestParityBuildReadiness/arg=clarifyopen.golden +0 -1
- package/engine/testdata/golden/TestParityBuildReadiness/arg=emptystatus.golden +0 -2
- package/engine/testdata/golden/TestParityBuildReadiness/arg=ghost.golden +0 -1
- package/engine/testdata/golden/TestParityBuildReadiness/arg=legacycontract.golden +0 -1
- package/engine/testdata/golden/TestParityBuildReadiness/arg=noapprove.golden +0 -1
- package/engine/testdata/golden/TestParityBuildReadiness/arg=noclarify.golden +0 -1
- package/engine/testdata/golden/TestParityBuildReadiness/arg=novet.golden +0 -1
- package/engine/testdata/golden/TestParityBuildReadiness/arg=stalevet.golden +0 -1
- package/engine/testdata/golden/TestParityBuildReadiness/arg=trailhash.golden +0 -2
- package/engine/testdata/golden/TestParityBuildReadiness/arg=trailpipe.golden +0 -2
- package/engine/testdata/golden/TestParityBuildReadiness/arg=vetnotready.golden +0 -1
- package/engine/testdata/golden/TestParityCheckAcceptance/arg=blank-section.golden +0 -1
- package/engine/testdata/golden/TestParityCheckAcceptance/arg=empty.golden +0 -1
- package/engine/testdata/golden/TestParityCheckAcceptance/arg=flat-dropped.golden +0 -1
- package/engine/testdata/golden/TestParityCheckAcceptance/arg=flat-ok.golden +0 -2
- package/engine/testdata/golden/TestParityCheckAcceptance/arg=flat-unchecked.golden +0 -1
- package/engine/testdata/golden/TestParityCheckAcceptance/arg=id-gap.golden +0 -1
- package/engine/testdata/golden/TestParityCheckAcceptance/arg=id-ok.golden +0 -2
- package/engine/testdata/golden/TestParityCheckAcceptance/arg=no-seal.golden +0 -1
- package/engine/testdata/golden/TestParityCheckAcceptance/arg=no-section.golden +0 -1
- package/engine/testdata/golden/TestParityCheckAcceptance/arg=nonexistent.golden +0 -1
- package/engine/testdata/golden/TestParityCheckAcceptance.golden +0 -1
- package/engine/testdata/golden/TestParityCoverage/arg=flat.golden +0 -7
- package/engine/testdata/golden/TestParityCoverage/arg=full.golden +0 -10
- package/engine/testdata/golden/TestParityCoverage/arg=nonexistent.golden +0 -1
- package/engine/testdata/golden/TestParityCoverage/arg=nospec.golden +0 -1
- package/engine/testdata/golden/TestParityCoverage/arg=speconly.golden +0 -9
- package/engine/testdata/golden/TestParityCoverage.golden +0 -1
- package/engine/testdata/golden/TestParityCursor/bare.golden +0 -3
- package/engine/testdata/golden/TestParityCursor/full-afk.golden +0 -6
- package/engine/testdata/golden/TestParityCursor/full-hitl.golden +0 -5
- package/engine/testdata/golden/TestParityCursor/no-active.golden +0 -1
- package/engine/testdata/golden/TestParityCursor/red.golden +0 -5
- package/engine/testdata/golden/TestParityCursor/zerogates.golden +0 -5
- package/engine/testdata/golden/TestParityDoubtCoverage/arg=doubted.golden +0 -2
- package/engine/testdata/golden/TestParityDoubtCoverage/arg=ghost.golden +0 -2
- package/engine/testdata/golden/TestParityDoubtCoverage/arg=missing.golden +0 -3
- package/engine/testdata/golden/TestParityDoubtCoverage/arg=nodoubt.golden +0 -4
- package/engine/testdata/golden/TestParityDoubtCoverage/arg=nolog.golden +0 -2
- package/engine/testdata/golden/TestParityDoubtCoverage/arg=nowright.golden +0 -2
- package/engine/testdata/golden/TestParityDoubtCoverage.golden +0 -1
- package/engine/testdata/golden/TestParityMutationGate/active-slug.golden +0 -3
- package/engine/testdata/golden/TestParityMutationGate/explicit-slug.golden +0 -3
- package/engine/testdata/golden/TestParityMutationGate/ghost.golden +0 -1
- package/engine/testdata/golden/TestParityOrientSilentPath.golden +0 -1
- package/engine/testdata/golden/TestParityReconcile/bad-mode.golden +0 -1
- package/engine/testdata/golden/TestParityReconcile/check-clean/snapshot.golden +0 -2
- package/engine/testdata/golden/TestParityReconcile/check-clean.golden +0 -2
- package/engine/testdata/golden/TestParityReconcile/check-no-base.golden +0 -1
- package/engine/testdata/golden/TestParityReconcile/check-no-claimed/snapshot.golden +0 -2
- package/engine/testdata/golden/TestParityReconcile/check-violation/snapshot.golden +0 -2
- package/engine/testdata/golden/TestParityReconcile/check-violation.golden +0 -1
- package/engine/testdata/golden/TestParityReconcile/close-window/snapshot.golden +0 -2
- package/engine/testdata/golden/TestParityReconcile/close-window.golden +0 -3
- package/engine/testdata/golden/TestParityReconcile/not-git.golden +0 -1
- package/engine/testdata/golden/TestParityReconcile/snapshot/snapshot.golden +0 -2
- package/engine/testdata/golden/TestParityReconcile/snapshot-no-allowlist.golden +0 -1
- package/engine/testdata/golden/TestParityReconcile/snapshot.golden +0 -2
- package/engine/testdata/golden/TestParityRedwatch/fail-writes-red.golden +0 -2
- package/engine/testdata/golden/TestParityRedwatch/non-bash-tool.golden +0 -1
- package/engine/testdata/golden/TestParityRedwatch/non-test-command.golden +0 -1
- package/engine/testdata/golden/TestParityRedwatch/pass-clears-red.golden +0 -1
- package/engine/testdata/golden/TestParityResolve/next-qid.golden +0 -2
- package/engine/testdata/golden/TestParityReviewerReadonly/agent-required-devrites-agent-denies.golden +0 -2
- package/engine/testdata/golden/TestParityReviewerReadonly/agent-required-non-devrites-allows.golden +0 -1
- package/engine/testdata/golden/TestParityReviewerReadonly/agent-required-subagent-type-only-allows.golden +0 -1
- package/engine/testdata/golden/TestParityReviewerReadonly/enforce-deny-git-commit.golden +0 -2
- package/engine/testdata/golden/TestParityReviewerReadonly/enforce-deny-mutating.golden +0 -2
- package/engine/testdata/golden/TestParityReviewerReadonly/non-bash-tool.golden +0 -1
- package/engine/testdata/golden/TestParityReviewerReadonly/non-tool-input.golden +0 -1
- package/engine/testdata/golden/TestParityReviewerReadonly/observe-mutating-silent.golden +0 -1
- package/engine/testdata/golden/TestParityReviewerReadonly/safe-bash-command.golden +0 -1
- package/engine/testdata/golden/TestParitySpecValidate/arg=dup.golden +0 -1
- package/engine/testdata/golden/TestParitySpecValidate/arg=empty.golden +0 -1
- package/engine/testdata/golden/TestParitySpecValidate/arg=flat.golden +0 -2
- package/engine/testdata/golden/TestParitySpecValidate/arg=nonexistent.golden +0 -1
- package/engine/testdata/golden/TestParitySpecValidate/arg=noscenario.golden +0 -1
- package/engine/testdata/golden/TestParitySpecValidate/arg=noshall.golden +0 -1
- package/engine/testdata/golden/TestParitySpecValidate/arg=nothen.golden +0 -1
- package/engine/testdata/golden/TestParitySpecValidate/arg=valid/spec.md.golden +0 -2
- package/engine/testdata/golden/TestParitySpecValidate/arg=valid.golden +0 -2
- package/engine/testdata/golden/TestParitySpecValidate.golden +0 -1
- package/engine/testdata/golden/TestParityStatusline/full-afk.golden +0 -2
- package/engine/testdata/golden/TestParityStatusline/full-hitl.golden +0 -2
- package/engine/testdata/golden/TestParityStatusline/no-active.golden +0 -1
- package/engine/testdata/golden/TestParityStatusline/nophase.golden +0 -2
- package/engine/testdata/golden/TestParityStatusline/red.golden +0 -2
- package/engine/testdata/golden/TestParityStuckCheck/bad-cmd.golden +0 -1
- package/engine/testdata/golden/TestParityStuckCheck/custom-window-2.golden +0 -2
- package/engine/testdata/golden/TestParityStuckCheck/dominant.golden +0 -2
- package/engine/testdata/golden/TestParityStuckCheck/few.golden +0 -2
- package/engine/testdata/golden/TestParityStuckCheck/ghost.golden +0 -2
- package/engine/testdata/golden/TestParityStuckCheck/identical.golden +0 -2
- package/engine/testdata/golden/TestParityStuckCheck/no-cmd.golden +0 -1
- package/engine/testdata/golden/TestParityStuckCheck/no-slug.golden +0 -1
- package/engine/testdata/golden/TestParityStuckCheck/nolog.golden +0 -2
- package/engine/testdata/golden/TestParityStuckCheck/pingpong.golden +0 -2
- package/engine/testdata/golden/TestParityStuckCheck/varied.golden +0 -2
- package/engine/testdata/golden/TestParityStuckCheck/window-04-identical.golden +0 -2
- package/engine/testdata/golden/TestParityStuckCheck/window-2.5-varied4.golden +0 -2
- package/engine/testdata/golden/TestParityStuckCheck/window-4x-varied4.golden +0 -2
- package/engine/testdata/golden/TestParityStuckCheck/window-leadspace-varied4.golden +0 -2
- package/engine/testdata/golden/TestParityStuckCheck/window-plus4-identical.golden +0 -2
- package/engine/testdata/golden/TestParitySubagentOrient/devrites-agent-subagent-type-key.golden +0 -2
- package/engine/testdata/golden/TestParitySubagentOrient/devrites-agent.golden +0 -2
- package/engine/testdata/golden/TestParitySubagentOrient/garbage-input.golden +0 -1
- package/engine/testdata/golden/TestParitySubagentOrient/non-devrites-agent.golden +0 -1
- package/engine/testdata/golden/TestParityTestIntegrity/assertion-dropped.golden +0 -1
- package/engine/testdata/golden/TestParityTestIntegrity/clean.golden +0 -2
- package/engine/testdata/golden/TestParityTestIntegrity/deleted.golden +0 -1
- package/engine/testdata/golden/TestParityTestIntegrity/ghost-workspace.golden +0 -1
- package/engine/testdata/golden/TestParityTestIntegrity/not-git.golden +0 -1
- package/engine/testdata/golden/TestParityTestIntegrity/skip-added.golden +0 -1
- package/engine/testdata/golden/TestParityTickAfk/budget1/state.md.golden +0 -1
- package/engine/testdata/golden/TestParityTickAfk/budget1.golden +0 -2
- package/engine/testdata/golden/TestParityTickAfk/budget3/state.md.golden +0 -5
- package/engine/testdata/golden/TestParityTickAfk/budget3.golden +0 -2
- package/engine/testdata/golden/TestParityTickAfk/bulletPipe/state.md.golden +0 -1
- package/engine/testdata/golden/TestParityTickAfk/bulletPipe.golden +0 -2
- package/engine/testdata/golden/TestParityTickAfk/missingField/state.md.golden +0 -3
- package/engine/testdata/golden/TestParityTickAfk/missingField.golden +0 -2
- package/engine/testdata/golden/TestParityTickAfk/missingFile.golden +0 -1
- package/engine/testdata/golden/TestParityTickAfk/nonNumeric/state.md.golden +0 -1
- package/engine/testdata/golden/TestParityTickAfk/nonNumeric.golden +0 -1
- package/engine/testdata/golden/TestParityTickAfk/none/state.md.golden +0 -1
- package/engine/testdata/golden/TestParityTickAfk/none.golden +0 -2
- package/engine/testdata/golden/TestParityWrightScope/agent-required-non-wright.golden +0 -1
- package/engine/testdata/golden/TestParityWrightScope/devrites-edit-denied.golden +0 -2
- package/engine/testdata/golden/TestParityWrightScope/in-scope-allowed.golden +0 -1
- package/engine/testdata/golden/TestParityWrightScope/non-edit-tool.golden +0 -1
- package/engine/testdata/golden/TestParityWrightScope/out-of-scope-enforce-denies.golden +0 -2
- package/engine/testdata/golden/TestParityWrightScope/out-of-scope-observe-silent.golden +0 -1
- package/engine/testdata/golden/TestSpecSkeleton/missing-some.golden +0 -2
- package/engine/testdata/golden/TestSpecSkeleton/present-all.golden +0 -2
- package/engine/testdata/golden/TestStuckLogHashParity.golden +0 -1
- package/engine/tests/adr_0006_clock_seam_test.go +0 -36
- package/engine/tests/budget_test.go +0 -88
- package/engine/tests/canonical_ac_ids_test.go +0 -44
- package/engine/tests/concurrency_cli_test.go +0 -131
- package/engine/tests/conventions_test.go +0 -106
- package/engine/tests/doctor_cli_test.go +0 -223
- package/engine/tests/forge_cli_test.go +0 -463
- package/engine/tests/hook_test.go +0 -2732
- package/engine/tests/hooks_io_test.go +0 -309
- package/engine/tests/json_contract_test.go +0 -224
- package/engine/tests/lib_parity_test.go +0 -930
- package/engine/tests/migrate_cli_test.go +0 -195
- package/engine/tests/parity_analyze_test.go +0 -55
- package/engine/tests/parity_buildreadiness_test.go +0 -199
- package/engine/tests/parity_learnings_test.go +0 -89
- package/engine/tests/parity_mutationgate_test.go +0 -36
- package/engine/tests/parity_reconcile_test.go +0 -94
- package/engine/tests/parity_testintegrity_test.go +0 -65
- package/engine/tests/parity_tickafk_test.go +0 -83
- package/engine/validatepack.go +0 -192
- package/engine/validatepack_test.go +0 -79
- package/engine/validatepackskills.go +0 -181
- package/engine/validatepackskills_test.go +0 -171
- package/pack/.claude/agents/devrites-forge-judge.md +0 -118
- package/pack/.claude/skills/devrites-lib/reference/model-tiers.md +0 -44
- package/pack/.claude/skills/devrites-refresh-indexes/SKILL.md +0 -52
- package/pack/.claude/skills/rite-build/reference/evidence-standard.md +0 -45
- package/pack/.claude/skills/rite-build/reference/forge.md +0 -210
- package/pack/.claude/skills/rite-seal/reference/conventions-ledger.md +0 -60
- package/pack/.claude/skills/rite-ship/reference/adr-promotion.md +0 -9
- package/pack/.claude/skills/rite-ship/reference/ledger.md +0 -56
- package/pack/generated/claude/agents/devrites-forge-judge.md +0 -118
- package/pack/generated/claude/skills/devrites-lib/reference/model-tiers.md +0 -44
- package/pack/generated/claude/skills/devrites-refresh-indexes/SKILL.md +0 -52
- package/pack/generated/claude/skills/rite-build/reference/evidence-standard.md +0 -45
- package/pack/generated/claude/skills/rite-build/reference/forge.md +0 -210
- package/pack/generated/claude/skills/rite-seal/reference/conventions-ledger.md +0 -60
- package/pack/generated/claude/skills/rite-ship/reference/adr-promotion.md +0 -9
- package/pack/generated/claude/skills/rite-ship/reference/ledger.md +0 -56
- package/pack/generated/codex/agents/devrites-forge-judge.toml +0 -128
- package/pack/generated/codex/hooks.json +0 -185
- package/pack/generated/codex/skills/devrites-api-interface/agents/openai.yaml +0 -2
- package/pack/generated/codex/skills/devrites-audit/agents/openai.yaml +0 -2
- package/pack/generated/codex/skills/devrites-browser-proof/agents/openai.yaml +0 -2
- package/pack/generated/codex/skills/devrites-debug-recovery/agents/openai.yaml +0 -2
- package/pack/generated/codex/skills/devrites-doubt/agents/openai.yaml +0 -2
- package/pack/generated/codex/skills/devrites-frontend-craft/agents/openai.yaml +0 -2
- package/pack/generated/codex/skills/devrites-interview/agents/openai.yaml +0 -2
- package/pack/generated/codex/skills/devrites-lib/reference/model-tiers.md +0 -44
- package/pack/generated/codex/skills/devrites-prose-craft/agents/openai.yaml +0 -2
- package/pack/generated/codex/skills/devrites-refresh-indexes/SKILL.md +0 -71
- package/pack/generated/codex/skills/devrites-refresh-indexes/agents/openai.yaml +0 -2
- package/pack/generated/codex/skills/devrites-source-driven/agents/openai.yaml +0 -2
- package/pack/generated/codex/skills/devrites-ux-shape/agents/openai.yaml +0 -2
- package/pack/generated/codex/skills/rite-build/reference/evidence-standard.md +0 -45
- package/pack/generated/codex/skills/rite-build/reference/forge.md +0 -210
- package/pack/generated/codex/skills/rite-seal/reference/conventions-ledger.md +0 -60
- package/pack/generated/codex/skills/rite-ship/reference/adr-promotion.md +0 -9
- package/pack/generated/codex/skills/rite-ship/reference/ledger.md +0 -56
- package/scripts/build-binaries.sh +0 -75
- package/scripts/build-release-tarball.sh +0 -105
- package/scripts/check-authority-drift.py +0 -125
- package/scripts/check-cross-refs.py +0 -139
- package/scripts/check-generated-skill-budget.mjs +0 -42
- package/scripts/check-instruction-size-baseline.mjs +0 -88
- package/scripts/check-invocation-integrity.py +0 -115
- package/scripts/check-no-global-writes.sh +0 -66
- package/scripts/check-no-personal-paths.py +0 -50
- package/scripts/check-npm-audit.mjs +0 -110
- package/scripts/check-reference-governance.mjs +0 -83
- package/scripts/check-reply-contract.sh +0 -64
- package/scripts/check-rule-uniqueness.sh +0 -45
- package/scripts/devrites-detect.sh +0 -176
- package/scripts/grade-feature.sh +0 -204
- package/scripts/live-hosts/agent-result.schema.json +0 -231
- package/scripts/live-hosts/claude.sh +0 -87
- package/scripts/live-hosts/codex.sh +0 -85
- package/scripts/live-hosts/common.sh +0 -113
- package/scripts/live-hosts/fake-host.py +0 -257
- package/scripts/live-hosts/host-transport.py +0 -286
- package/scripts/npm-audit-exceptions.json +0 -26
- package/scripts/pin.sh +0 -249
- package/scripts/release-check.sh +0 -75
- package/scripts/run-agent-contract-evals.py +0 -1380
- package/scripts/run-behavioral-evals.sh +0 -196
- package/scripts/run-evals.sh +0 -132
- package/scripts/run-live-behavioral-evals.py +0 -1310
- package/scripts/run-outcome-evals.sh +0 -454
- package/scripts/run-routing-evals.py +0 -342
- package/scripts/run-tests.mjs +0 -232
- package/scripts/scan-pack-security.py +0 -209
- package/scripts/scan-supply-chain-iocs.py +0 -127
- package/scripts/skill-pruning-audit.mjs +0 -121
- package/scripts/skills-inventory.mjs +0 -125
- package/scripts/supply-chain-iocs.json +0 -11
- package/scripts/sync-version.sh +0 -54
- package/scripts/validate-agent-composition.py +0 -57
- package/scripts/validate-command-parity.py +0 -70
- package/scripts/validate-frontmatter.py +0 -218
- package/scripts/validate-skill-anatomy.py +0 -139
- package/scripts/validate-workflow-security.py +0 -184
- package/scripts/validate-workspace-schema.py +0 -800
- package/scripts/validate.sh +0 -354
- package/scripts/workflow_schema.py +0 -69
- /package/engine/testdata/fixtures/basic/devrites-root/{features → work}/auth-tokens/decisions.md +0 -0
- /package/engine/testdata/fixtures/basic/devrites-root/{features → work}/auth-tokens/plan.md +0 -0
- /package/engine/testdata/fixtures/basic/devrites-root/{features → work}/auth-tokens/spec.md +0 -0
- /package/engine/testdata/fixtures/basic/devrites-root/{features → work}/auth-tokens/tasks.md +0 -0
- /package/engine/testdata/fixtures/basic/devrites-root/{features → work}/search-ranking/spec.md +0 -0
|
@@ -1,930 +0,0 @@
|
|
|
1
|
-
package main_test
|
|
2
|
-
|
|
3
|
-
// Golden + diagnostic tests for the ported devrites-lib commands (issue 08).
|
|
4
|
-
//
|
|
5
|
-
// Golden: each command is run through the Go binary against a fixture, and its
|
|
6
|
-
// stdout + exit code are checked against a recorded golden snapshot (stderr is
|
|
7
|
-
// diagnostic, not contract). Snapshots are regenerated with UPDATE_GOLDEN=1.
|
|
8
|
-
//
|
|
9
|
-
// Diagnostic: a second pass asserts the Go stderr carries the same actionable
|
|
10
|
-
// finding substrings a consumer greps for, so the port keeps its diagnostic
|
|
11
|
-
// quality even though stderr is excluded from the golden check.
|
|
12
|
-
|
|
13
|
-
import (
|
|
14
|
-
"bytes"
|
|
15
|
-
"errors"
|
|
16
|
-
"os"
|
|
17
|
-
"os/exec"
|
|
18
|
-
"path/filepath"
|
|
19
|
-
"testing"
|
|
20
|
-
"time"
|
|
21
|
-
|
|
22
|
-
"github.com/devrites/devrites/internal/testutil"
|
|
23
|
-
)
|
|
24
|
-
|
|
25
|
-
// libEnv forces byte-order collation so `sort` in the bash script and
|
|
26
|
-
// sort.Strings in Go agree (matters for check-acceptance's id ordering).
|
|
27
|
-
var libEnv = []string{"LC_ALL=C", "DEVRITES_ROOT="}
|
|
28
|
-
|
|
29
|
-
// libRootEnv points DEVRITES_ROOT at the workspace's .devrites directory so the
|
|
30
|
-
// command reads the fixture under <root>/features/<slug>. LC_ALL=C pins byte-order
|
|
31
|
-
// collation so id ordering is deterministic.
|
|
32
|
-
func libRootEnv(work string) []string {
|
|
33
|
-
return []string{"LC_ALL=C", "DEVRITES_ROOT=" + filepath.Join(work, ".devrites")}
|
|
34
|
-
}
|
|
35
|
-
|
|
36
|
-
// writeFeatureFile writes fixture content to <work>/.devrites/features/<slug>/<name>.
|
|
37
|
-
func writeFeatureFile(t *testing.T, work, slug, name, content string) {
|
|
38
|
-
t.Helper()
|
|
39
|
-
writeFile(t, work, filepath.Join(".devrites/features", slug, name), content)
|
|
40
|
-
}
|
|
41
|
-
|
|
42
|
-
// makeFeatureDir creates an empty feature directory in the workspace.
|
|
43
|
-
func makeFeatureDir(t *testing.T, work, slug string) {
|
|
44
|
-
t.Helper()
|
|
45
|
-
mkdirAllT(t, work, filepath.Join(".devrites/features", slug))
|
|
46
|
-
}
|
|
47
|
-
|
|
48
|
-
// writeSpec writes content to <workdir>/<name>/spec.md and returns the subdir name.
|
|
49
|
-
func writeSpec(t *testing.T, workdir, name, content string) string {
|
|
50
|
-
t.Helper()
|
|
51
|
-
dir := filepath.Join(workdir, name)
|
|
52
|
-
if err := os.MkdirAll(dir, 0o755); err != nil {
|
|
53
|
-
t.Fatal(err)
|
|
54
|
-
}
|
|
55
|
-
if err := os.WriteFile(filepath.Join(dir, "spec.md"), []byte(content), 0o644); err != nil {
|
|
56
|
-
t.Fatal(err)
|
|
57
|
-
}
|
|
58
|
-
return name
|
|
59
|
-
}
|
|
60
|
-
|
|
61
|
-
func writeFile(t *testing.T, workdir, rel, content string) {
|
|
62
|
-
t.Helper()
|
|
63
|
-
testutil.WriteFile(t, filepath.Join(workdir, rel), content)
|
|
64
|
-
}
|
|
65
|
-
|
|
66
|
-
// runGo runs the built binary in workdir with libEnv, returning stdout, stderr, code.
|
|
67
|
-
func runGo(t *testing.T, workdir string, args ...string) (stdout, stderr string, code int) {
|
|
68
|
-
t.Helper()
|
|
69
|
-
cmd := exec.Command(binPath, args...)
|
|
70
|
-
cmd.Dir = workdir
|
|
71
|
-
cmd.Env = append(os.Environ(), libEnv...)
|
|
72
|
-
var out, errBuf bytes.Buffer
|
|
73
|
-
cmd.Stdout = &out
|
|
74
|
-
cmd.Stderr = &errBuf
|
|
75
|
-
if err := cmd.Run(); err != nil {
|
|
76
|
-
var ee *exec.ExitError
|
|
77
|
-
if !errors.As(err, &ee) {
|
|
78
|
-
t.Fatalf("running %s %v: %v", binPath, args, err)
|
|
79
|
-
}
|
|
80
|
-
code = ee.ExitCode()
|
|
81
|
-
}
|
|
82
|
-
return out.String(), errBuf.String(), code
|
|
83
|
-
}
|
|
84
|
-
|
|
85
|
-
// ---- spec-validate --------------------------------------------------------
|
|
86
|
-
|
|
87
|
-
// Spec fixtures cover the well-formed spec plus each malformed shape so the
|
|
88
|
-
// golden check exercises every spec-validate verdict.
|
|
89
|
-
const (
|
|
90
|
-
specValid = `# Spec
|
|
91
|
-
## Acceptance criteria
|
|
92
|
-
### Requirement: Tokens expire after inactivity
|
|
93
|
-
The system SHALL reject a session token older than 15 minutes.
|
|
94
|
-
#### Scenario: expired token
|
|
95
|
-
- [ ] [AC1] **WHEN** a token aged > 15m is presented **THEN** respond 401
|
|
96
|
-
#### Scenario: fresh token
|
|
97
|
-
- [ ] [AC2] **WHEN** a token aged < 15m is presented **THEN** allow
|
|
98
|
-
### Requirement: Logout revokes the token
|
|
99
|
-
Logout MUST invalidate the token server-side.
|
|
100
|
-
#### Scenario: replay after logout
|
|
101
|
-
- [ ] [AC3] **WHEN** a logged-out token is replayed **THEN** respond 401
|
|
102
|
-
`
|
|
103
|
-
specFlat = `# Spec
|
|
104
|
-
## Acceptance criteria
|
|
105
|
-
- [ ] [AC1] export returns a CSV with a header row
|
|
106
|
-
- [ ] [AC2] an empty dataset returns 204
|
|
107
|
-
`
|
|
108
|
-
specNoShall = `### Requirement: Export behaves nicely
|
|
109
|
-
The export produces a file and emails the user.
|
|
110
|
-
#### Scenario: happy path
|
|
111
|
-
- **WHEN** a user requests an export **THEN** a file is produced
|
|
112
|
-
`
|
|
113
|
-
specNoScenario = `### Requirement: It works
|
|
114
|
-
The system SHALL do the thing.
|
|
115
|
-
`
|
|
116
|
-
specNoThen = `### Requirement: It works
|
|
117
|
-
The system SHALL do the thing.
|
|
118
|
-
#### Scenario: half a scenario
|
|
119
|
-
- **WHEN** something happens
|
|
120
|
-
`
|
|
121
|
-
specDup = `### Requirement: Same name
|
|
122
|
-
The system SHALL alpha.
|
|
123
|
-
#### Scenario: a
|
|
124
|
-
- **WHEN** a **THEN** b
|
|
125
|
-
### Requirement: Same name
|
|
126
|
-
The system SHALL beta.
|
|
127
|
-
#### Scenario: c
|
|
128
|
-
- **WHEN** c **THEN** d
|
|
129
|
-
`
|
|
130
|
-
)
|
|
131
|
-
|
|
132
|
-
func TestParitySpecValidate(t *testing.T) {
|
|
133
|
-
work := t.TempDir()
|
|
134
|
-
|
|
135
|
-
writeSpec(t, work, "valid", specValid)
|
|
136
|
-
writeSpec(t, work, "flat", specFlat)
|
|
137
|
-
writeSpec(t, work, "noshall", specNoShall)
|
|
138
|
-
writeSpec(t, work, "noscenario", specNoScenario)
|
|
139
|
-
writeSpec(t, work, "nothen", specNoThen)
|
|
140
|
-
writeSpec(t, work, "dup", specDup)
|
|
141
|
-
if err := os.MkdirAll(filepath.Join(work, "empty"), 0o755); err != nil {
|
|
142
|
-
t.Fatal(err)
|
|
143
|
-
}
|
|
144
|
-
|
|
145
|
-
// arg is the positional argument passed to both implementations, relative to work.
|
|
146
|
-
for _, arg := range []string{
|
|
147
|
-
"valid", // well-formed structured spec → exit 0
|
|
148
|
-
"flat", // flat-bullet form → exit 0 (no-op)
|
|
149
|
-
"noshall", // missing SHALL/MUST → exit 1
|
|
150
|
-
"noscenario", // requirement with no scenario → exit 1
|
|
151
|
-
"nothen", // scenario missing THEN → exit 1
|
|
152
|
-
"dup", // duplicate headers → exit 1
|
|
153
|
-
"valid/spec.md", // direct file path → exit 0
|
|
154
|
-
"empty", // dir without spec.md → exit 5
|
|
155
|
-
"nonexistent", // no such workspace or file → exit 2
|
|
156
|
-
} {
|
|
157
|
-
c := parityCase{
|
|
158
|
-
workdir: work,
|
|
159
|
-
env: libEnv,
|
|
160
|
-
goArgs: []string{"spec-validate", arg},
|
|
161
|
-
}
|
|
162
|
-
t.Run("arg="+arg, func(t *testing.T) { c.assertEqual(t) })
|
|
163
|
-
}
|
|
164
|
-
|
|
165
|
-
// No-argument usage path (exit 2): asserted separately since arg is absent.
|
|
166
|
-
(parityCase{
|
|
167
|
-
workdir: work,
|
|
168
|
-
env: libEnv,
|
|
169
|
-
goArgs: []string{"spec-validate"},
|
|
170
|
-
}).assertEqual(t)
|
|
171
|
-
}
|
|
172
|
-
|
|
173
|
-
func TestSpecSkeleton(t *testing.T) {
|
|
174
|
-
work := t.TempDir()
|
|
175
|
-
|
|
176
|
-
writeSpec(t, work, "present", `# Contract
|
|
177
|
-
|
|
178
|
-
## Problem
|
|
179
|
-
done
|
|
180
|
-
## Goal
|
|
181
|
-
done
|
|
182
|
-
## Non-goals
|
|
183
|
-
done
|
|
184
|
-
## Users / actors
|
|
185
|
-
done
|
|
186
|
-
## Requirements
|
|
187
|
-
done
|
|
188
|
-
## Acceptance criteria
|
|
189
|
-
done
|
|
190
|
-
## Edge Coverage
|
|
191
|
-
done
|
|
192
|
-
## Prohibitions (must-NOT)
|
|
193
|
-
done
|
|
194
|
-
## Edge cases
|
|
195
|
-
done
|
|
196
|
-
## Measurable success
|
|
197
|
-
done
|
|
198
|
-
## Scope boundaries
|
|
199
|
-
done
|
|
200
|
-
`)
|
|
201
|
-
writeSpec(t, work, "missing", `# Contract
|
|
202
|
-
|
|
203
|
-
## Problem
|
|
204
|
-
done
|
|
205
|
-
## Goal
|
|
206
|
-
done
|
|
207
|
-
## Requirements
|
|
208
|
-
done
|
|
209
|
-
`)
|
|
210
|
-
|
|
211
|
-
for _, tc := range []struct {
|
|
212
|
-
name string
|
|
213
|
-
arg string
|
|
214
|
-
}{
|
|
215
|
-
{"present-all", "present"},
|
|
216
|
-
{"missing-some", "missing/spec.md"},
|
|
217
|
-
} {
|
|
218
|
-
tc := tc
|
|
219
|
-
t.Run(tc.name, func(t *testing.T) {
|
|
220
|
-
(parityCase{
|
|
221
|
-
workdir: work,
|
|
222
|
-
env: libEnv,
|
|
223
|
-
goArgs: []string{"spec-skeleton", tc.arg},
|
|
224
|
-
}).assertEqual(t)
|
|
225
|
-
})
|
|
226
|
-
}
|
|
227
|
-
}
|
|
228
|
-
|
|
229
|
-
// TestSpecValidateDiagnostics asserts the Go stderr carries the same finding
|
|
230
|
-
// substrings the legacy spec-validate-test.sh greps for.
|
|
231
|
-
func TestSpecValidateDiagnostics(t *testing.T) {
|
|
232
|
-
work := t.TempDir()
|
|
233
|
-
cases := []struct {
|
|
234
|
-
name, content, wantExit1 string
|
|
235
|
-
}{
|
|
236
|
-
{"noshall", specNoShall, "no SHALL/MUST"},
|
|
237
|
-
{"noscenario", specNoScenario, `no "#### Scenario:"`},
|
|
238
|
-
{"nothen", specNoThen, "no THEN line"},
|
|
239
|
-
{"dup", specDup, "duplicate Requirement header"},
|
|
240
|
-
}
|
|
241
|
-
for _, tc := range cases {
|
|
242
|
-
writeSpec(t, work, tc.name, tc.content)
|
|
243
|
-
_, stderr, code := runGo(t, work, "spec-validate", tc.name)
|
|
244
|
-
if code != 1 {
|
|
245
|
-
t.Errorf("%s: exit=%d, want 1", tc.name, code)
|
|
246
|
-
}
|
|
247
|
-
if !bytes.Contains([]byte(stderr), []byte(tc.wantExit1)) {
|
|
248
|
-
t.Errorf("%s: stderr %q missing %q", tc.name, stderr, tc.wantExit1)
|
|
249
|
-
}
|
|
250
|
-
}
|
|
251
|
-
}
|
|
252
|
-
|
|
253
|
-
// ---- check-acceptance -----------------------------------------------------
|
|
254
|
-
|
|
255
|
-
func TestParityCheckAcceptance(t *testing.T) {
|
|
256
|
-
work := t.TempDir()
|
|
257
|
-
|
|
258
|
-
// id mode, all proven → exit 0
|
|
259
|
-
writeFile(t, work, "id-ok/spec.md", "## Acceptance criteria\n- [ ] [AC1] a\n- [ ] [AC2] b\n- [ ] [AC3] c\n")
|
|
260
|
-
writeFile(t, work, "id-ok/seal.md", "## Acceptance Criteria\n- [x] [AC1] a: evidence t1\n- [x] [AC2] b: evidence t2\n- [x] [AC3] c: evidence t3\n")
|
|
261
|
-
|
|
262
|
-
// id mode, one unproven → exit 1
|
|
263
|
-
writeFile(t, work, "id-gap/spec.md", "## Acceptance criteria\n- [ ] [AC1] a\n- [ ] [AC2] b\n- [ ] [AC3] c\n")
|
|
264
|
-
writeFile(t, work, "id-gap/seal.md", "## Acceptance Criteria\n- [x] [AC1] a: evidence t1\n- [ ] [AC2] b\n- [ ] [AC3] c\n")
|
|
265
|
-
|
|
266
|
-
// fallback (no ids), all checked → exit 0
|
|
267
|
-
writeFile(t, work, "flat-ok/spec.md", "## Acceptance criteria\n- export returns CSV\n- empty dataset returns 204\n")
|
|
268
|
-
writeFile(t, work, "flat-ok/seal.md", "## Acceptance Criteria\n- [x] export returns CSV\n- [x] empty dataset returns 204\n")
|
|
269
|
-
|
|
270
|
-
// fallback, one unchecked → exit 1
|
|
271
|
-
writeFile(t, work, "flat-unchecked/spec.md", "## Acceptance criteria\n- export returns CSV\n- empty dataset returns 204\n")
|
|
272
|
-
writeFile(t, work, "flat-unchecked/seal.md", "## Acceptance Criteria\n- [x] export returns CSV\n- [ ] empty dataset returns 204\n")
|
|
273
|
-
|
|
274
|
-
// fallback, seal drops a criterion → exit 1
|
|
275
|
-
writeFile(t, work, "flat-dropped/spec.md", "## Acceptance criteria\n- a\n- b\n- c\n")
|
|
276
|
-
writeFile(t, work, "flat-dropped/seal.md", "## Acceptance Criteria\n- [x] a\n- [x] b\n")
|
|
277
|
-
|
|
278
|
-
// spec has no acceptance section → exit 1
|
|
279
|
-
writeFile(t, work, "no-section/spec.md", "# Spec\nsome prose, no criteria heading\n")
|
|
280
|
-
writeFile(t, work, "no-section/seal.md", "## Acceptance Criteria\n- [x] a\n")
|
|
281
|
-
|
|
282
|
-
// spec has an acceptance heading but only blank lines under it → still
|
|
283
|
-
// "nothing to grade" (bash `[ -z ]` after trailing-newline strip), exit 1.
|
|
284
|
-
writeFile(t, work, "blank-section/spec.md", "## Acceptance criteria\n\n\n## Notes\nprose\n")
|
|
285
|
-
writeFile(t, work, "blank-section/seal.md", "## Acceptance Criteria\n- [x] a\n")
|
|
286
|
-
|
|
287
|
-
// missing seal.md → exit 5
|
|
288
|
-
writeFile(t, work, "no-seal/spec.md", "## Acceptance criteria\n- [ ] [AC1] a\n")
|
|
289
|
-
|
|
290
|
-
// empty dir (missing spec.md) → exit 5
|
|
291
|
-
if err := os.MkdirAll(filepath.Join(work, "empty"), 0o755); err != nil {
|
|
292
|
-
t.Fatal(err)
|
|
293
|
-
}
|
|
294
|
-
|
|
295
|
-
for _, arg := range []string{
|
|
296
|
-
"id-ok", "id-gap", "flat-ok", "flat-unchecked", "flat-dropped",
|
|
297
|
-
"no-section", "blank-section", "no-seal", "empty", "nonexistent",
|
|
298
|
-
} {
|
|
299
|
-
c := parityCase{
|
|
300
|
-
workdir: work,
|
|
301
|
-
env: libEnv,
|
|
302
|
-
goArgs: []string{"check-acceptance", arg},
|
|
303
|
-
}
|
|
304
|
-
t.Run("arg="+arg, func(t *testing.T) { c.assertEqual(t) })
|
|
305
|
-
}
|
|
306
|
-
|
|
307
|
-
// No-argument usage path (exit 2).
|
|
308
|
-
(parityCase{
|
|
309
|
-
workdir: work,
|
|
310
|
-
env: libEnv,
|
|
311
|
-
goArgs: []string{"check-acceptance"},
|
|
312
|
-
}).assertEqual(t)
|
|
313
|
-
}
|
|
314
|
-
|
|
315
|
-
// ---- footprint ------------------------------------------------------------
|
|
316
|
-
//
|
|
317
|
-
// Golden tests: the expected stdout + exit values are asserted inline under
|
|
318
|
-
// LC_ALL=C, so the feature stands alone. The invariants a future edit must
|
|
319
|
-
// preserve: skip records never count, active-minutes = int((max-min)/60), the
|
|
320
|
-
// wright · reviewer · audit · doubt kind order with correct singular/plural
|
|
321
|
-
// forms, and the roster 0/1/3 exit taxonomy.
|
|
322
|
-
|
|
323
|
-
// goldenCase asserts the built binary's stdout + exit code for one invocation.
|
|
324
|
-
type goldenCase struct {
|
|
325
|
-
name string
|
|
326
|
-
args []string
|
|
327
|
-
wantOut string
|
|
328
|
-
wantCode int
|
|
329
|
-
}
|
|
330
|
-
|
|
331
|
-
func (gc goldenCase) assert(t *testing.T, work string) {
|
|
332
|
-
t.Helper()
|
|
333
|
-
out, _, code := runGo(t, work, gc.args...)
|
|
334
|
-
if out != gc.wantOut || code != gc.wantCode {
|
|
335
|
-
t.Errorf("%s: got exit=%d out=%q\n want exit=%d out=%q",
|
|
336
|
-
gc.name, code, out, gc.wantCode, gc.wantOut)
|
|
337
|
-
}
|
|
338
|
-
}
|
|
339
|
-
|
|
340
|
-
func TestFootprintGolden(t *testing.T) {
|
|
341
|
-
work := t.TempDir()
|
|
342
|
-
|
|
343
|
-
// render: the deterministic fixed-epoch log from tests/footprint-test.sh.
|
|
344
|
-
writeFile(t, work, ".devrites/features/rendered/footprint.log",
|
|
345
|
-
"1000 wright 01-list\n1100 wright 02-detail\n"+
|
|
346
|
-
"1200 reviewer devrites-code-reviewer\n1200 reviewer devrites-security-auditor\n"+
|
|
347
|
-
"1300 reviewer devrites-test-analyst\n")
|
|
348
|
-
// single reviewer → the singular "1 subagent (1 reviewer)" + 0-slice path.
|
|
349
|
-
writeFile(t, work, ".devrites/features/one/footprint.log", "1000 reviewer spec-reviewer\n")
|
|
350
|
-
// audit + doubt → those kinds' pluralization and order.
|
|
351
|
-
writeFile(t, work, ".devrites/features/ad/footprint.log",
|
|
352
|
-
"1000 wright a\n1000 audit x\n1000 audit y\n1000 doubt z\n")
|
|
353
|
-
mkdirAllT(t, work, ".devrites/features/nolog")
|
|
354
|
-
writeFile(t, work, ".devrites/features/rc-complete/footprint.log",
|
|
355
|
-
"1000 reviewer spec-reviewer\n1000 reviewer code-reviewer\n1000 reviewer test-analyst\n"+
|
|
356
|
-
"1000 reviewer frontend-reviewer\n1000 skip security-auditor\n"+
|
|
357
|
-
"1000 skip performance-reviewer\n1000 skip devex-reviewer\n")
|
|
358
|
-
writeFile(t, work, ".devrites/features/rc-unacc/footprint.log",
|
|
359
|
-
"1000 reviewer spec-reviewer\n1000 reviewer code-reviewer\n")
|
|
360
|
-
writeFile(t, work, ".devrites/features/rc-skip/footprint.log",
|
|
361
|
-
"1000 reviewer spec-reviewer\n1000 reviewer code-reviewer\n1000 skip test-analyst\n"+
|
|
362
|
-
"1000 skip frontend-reviewer\n1000 skip security-auditor\n"+
|
|
363
|
-
"1000 skip performance-reviewer\n1000 skip devex-reviewer\n")
|
|
364
|
-
mkdirAllT(t, work, ".devrites/features/live")
|
|
365
|
-
|
|
366
|
-
rosterComplete := " spec-reviewer: dispatched\n code-reviewer: dispatched\n" +
|
|
367
|
-
" test-analyst: dispatched\n frontend-reviewer: dispatched\n" +
|
|
368
|
-
" security-auditor: skipped\n performance-reviewer: skipped\n" +
|
|
369
|
-
" devex-reviewer: skipped\nroster: complete (every reviewer accounted for)\n"
|
|
370
|
-
rosterUnacc := " spec-reviewer: dispatched\n code-reviewer: dispatched\n" +
|
|
371
|
-
" test-analyst: UNACCOUNTED\n frontend-reviewer: UNACCOUNTED\n" +
|
|
372
|
-
" security-auditor: UNACCOUNTED\n performance-reviewer: UNACCOUNTED\n" +
|
|
373
|
-
" devex-reviewer: UNACCOUNTED\n" +
|
|
374
|
-
"roster: UNACCOUNTED: test-analyst frontend-reviewer security-auditor performance-reviewer devex-reviewer\n"
|
|
375
|
-
rosterSkip := " spec-reviewer: dispatched\n code-reviewer: dispatched\n" +
|
|
376
|
-
" test-analyst: skipped (always-on: verify carry-forward)\n" +
|
|
377
|
-
" frontend-reviewer: skipped\n security-auditor: skipped\n" +
|
|
378
|
-
" performance-reviewer: skipped\n devex-reviewer: skipped\n" +
|
|
379
|
-
"roster: always-on skipped: test-analyst\n"
|
|
380
|
-
|
|
381
|
-
for _, gc := range []goldenCase{
|
|
382
|
-
{"render", []string{"footprint", "render", "rendered"},
|
|
383
|
-
"Footprint: 5 subagents (2 wright · 3 reviewers) · 2 slices · ~5m active\n", 0},
|
|
384
|
-
{"render-singular", []string{"footprint", "render", "one"},
|
|
385
|
-
"Footprint: 1 subagent (1 reviewer) · 0 slices · ~0m active\n", 0},
|
|
386
|
-
{"render-audit-doubt", []string{"footprint", "render", "ad"},
|
|
387
|
-
"Footprint: 4 subagents (1 wright · 2 audits · 1 doubt) · 1 slice · ~0m active\n", 0},
|
|
388
|
-
{"render-nolog", []string{"footprint", "render", "nolog"},
|
|
389
|
-
"Footprint: n/a (no dispatch records)\n", 0},
|
|
390
|
-
{"render-missing-ws", []string{"footprint", "render", "ghost"},
|
|
391
|
-
"Footprint: n/a (no dispatch records)\n", 0},
|
|
392
|
-
{"roster-complete", []string{"footprint", "roster", "rc-complete"}, rosterComplete, 0},
|
|
393
|
-
{"roster-unaccounted", []string{"footprint", "roster", "rc-unacc"}, rosterUnacc, 3},
|
|
394
|
-
{"roster-skip-alwayson", []string{"footprint", "roster", "rc-skip"}, rosterSkip, 1},
|
|
395
|
-
{"roster-nolog", []string{"footprint", "roster", "nolog"},
|
|
396
|
-
"roster: n/a (no dispatch records: fan-out did not run)\n", 0},
|
|
397
|
-
{"log-missing-ws", []string{"footprint", "log", "ghost", "wright", "x"}, "", 0},
|
|
398
|
-
{"log-usage-no-kind", []string{"footprint", "log", "live"}, "", 2},
|
|
399
|
-
{"unknown-subcommand", []string{"footprint", "bogus", "live"}, "", 2},
|
|
400
|
-
{"missing-slug", []string{"footprint", "render"}, "", 2},
|
|
401
|
-
{"no-args", []string{"footprint"}, "", 2},
|
|
402
|
-
} {
|
|
403
|
-
t.Run(gc.name, func(t *testing.T) { gc.assert(t, work) })
|
|
404
|
-
}
|
|
405
|
-
|
|
406
|
-
// The footprint never emits a token/dollar/cost figure: DevRites cannot
|
|
407
|
-
// truthfully source one (the same invariant footprint-test.sh guards).
|
|
408
|
-
out, _, _ := runGo(t, work, "footprint", "render", "rendered")
|
|
409
|
-
for _, banned := range []string{"token", "$", "cost", "usd", "USD"} {
|
|
410
|
-
if bytes.Contains([]byte(out), []byte(banned)) {
|
|
411
|
-
t.Errorf("render leaked a %q figure: %q", banned, out)
|
|
412
|
-
}
|
|
413
|
-
}
|
|
414
|
-
}
|
|
415
|
-
|
|
416
|
-
// TestFootprintLogAppends asserts the side-effect the parity oracle can't (it
|
|
417
|
-
// compares only stdout+exit): `log` appends exactly one record and a missing
|
|
418
|
-
// workspace is a silent no-op.
|
|
419
|
-
func TestFootprintLogAppends(t *testing.T) {
|
|
420
|
-
work := t.TempDir()
|
|
421
|
-
mkdirAllT(t, work, ".devrites/features/feat")
|
|
422
|
-
logp := filepath.Join(work, ".devrites", "features", "feat", "footprint.log")
|
|
423
|
-
|
|
424
|
-
if _, _, code := runGo(t, work, "footprint", "log", "feat", "audit", "security"); code != 0 {
|
|
425
|
-
t.Fatalf("log exit=%d, want 0", code)
|
|
426
|
-
}
|
|
427
|
-
if n := fileLines(t, logp); n != 1 {
|
|
428
|
-
t.Errorf("after one log: %d lines, want 1", n)
|
|
429
|
-
}
|
|
430
|
-
runGo(t, work, "footprint", "log", "feat", "wright", "01")
|
|
431
|
-
if n := fileLines(t, logp); n != 2 {
|
|
432
|
-
t.Errorf("after two logs: %d lines, want 2", n)
|
|
433
|
-
}
|
|
434
|
-
|
|
435
|
-
// An empty label still writes its trailing space: byte-parity with
|
|
436
|
-
// footprint.sh's `printf '%s %s %s\n'`, which always emits three fields.
|
|
437
|
-
runGo(t, work, "footprint", "log", "feat", "wright")
|
|
438
|
-
if last := lastLine(t, logp); !bytes.HasSuffix([]byte(last), []byte(" wright ")) {
|
|
439
|
-
t.Errorf("empty-label record = %q, want a trailing space (`<epoch> wright `)", last)
|
|
440
|
-
}
|
|
441
|
-
|
|
442
|
-
if _, _, code := runGo(t, work, "footprint", "log", "ghost", "wright", "x"); code != 0 {
|
|
443
|
-
t.Errorf("log on a missing workspace: exit=%d, want 0 (never fail the caller)", code)
|
|
444
|
-
}
|
|
445
|
-
if _, err := os.Stat(filepath.Join(work, ".devrites", "features", "ghost", "footprint.log")); err == nil {
|
|
446
|
-
t.Error("log on a missing workspace created a log; it must be a no-op")
|
|
447
|
-
}
|
|
448
|
-
}
|
|
449
|
-
|
|
450
|
-
// ---- evidence-fresh -------------------------------------------------------
|
|
451
|
-
//
|
|
452
|
-
// Golden tests: expected stdout + exit are asserted inline. File mtimes are set
|
|
453
|
-
// explicitly so the freshness comparison is deterministic and independent of
|
|
454
|
-
// when the fixtures were written.
|
|
455
|
-
|
|
456
|
-
func TestEvidenceFreshGolden(t *testing.T) {
|
|
457
|
-
work := t.TempDir()
|
|
458
|
-
|
|
459
|
-
old := time.Date(2026, 1, 1, 0, 0, 0, 0, time.UTC)
|
|
460
|
-
mid := time.Date(2026, 1, 2, 0, 0, 0, 0, time.UTC)
|
|
461
|
-
newer := time.Date(2026, 1, 3, 0, 0, 0, 0, time.UTC)
|
|
462
|
-
|
|
463
|
-
const okMsg = "evidence-fresh: OK: evidence post-dates every touched file.\n"
|
|
464
|
-
|
|
465
|
-
// Primary workspace: evidence + one touched file.
|
|
466
|
-
ws := ".devrites/features/ftest"
|
|
467
|
-
writeFile(t, work, ws+"/touched-files.md", "# touched\n- `src/app.js`\n")
|
|
468
|
-
writeFile(t, work, ws+"/evidence.md", "# evidence\nproof\n")
|
|
469
|
-
writeFile(t, work, "src/app.js", "code\n")
|
|
470
|
-
|
|
471
|
-
// STALE: the touched file is newer than the proof → exit 3.
|
|
472
|
-
chtimeT(t, work, ws+"/evidence.md", old)
|
|
473
|
-
chtimeT(t, work, "src/app.js", mid)
|
|
474
|
-
(goldenCase{"stale", []string{"evidence-fresh", "ftest"}, "", 3}).assert(t, work)
|
|
475
|
-
|
|
476
|
-
// FRESH: the proof now post-dates the code → exit 0.
|
|
477
|
-
chtimeT(t, work, ws+"/evidence.md", newer)
|
|
478
|
-
(goldenCase{"fresh", []string{"evidence-fresh", "ftest"}, okMsg, 0}).assert(t, work)
|
|
479
|
-
|
|
480
|
-
// proof.md is the supported alias for evidence.md. A proof-only workspace
|
|
481
|
-
// must pass/fail freshness by the same mtime rule.
|
|
482
|
-
writeFile(t, work, ".devrites/features/proofonly/touched-files.md", "# touched\n- `src/proofonly.js`\n")
|
|
483
|
-
writeFile(t, work, ".devrites/features/proofonly/proof.md", "# proof\n")
|
|
484
|
-
writeFile(t, work, "src/proofonly.js", "x\n")
|
|
485
|
-
chtimeT(t, work, "src/proofonly.js", old)
|
|
486
|
-
chtimeT(t, work, ".devrites/features/proofonly/proof.md", newer)
|
|
487
|
-
(goldenCase{"proof-only-fresh", []string{"evidence-fresh", "proofonly"}, okMsg, 0}).assert(t, work)
|
|
488
|
-
|
|
489
|
-
writeFile(t, work, ".devrites/features/proofstale/touched-files.md", "# touched\n- `src/proofstale.js`\n")
|
|
490
|
-
writeFile(t, work, ".devrites/features/proofstale/proof.md", "# proof\n")
|
|
491
|
-
writeFile(t, work, "src/proofstale.js", "x\n")
|
|
492
|
-
chtimeT(t, work, ".devrites/features/proofstale/proof.md", old)
|
|
493
|
-
chtimeT(t, work, "src/proofstale.js", newer)
|
|
494
|
-
(goldenCase{"proof-only-stale", []string{"evidence-fresh", "proofstale"}, "", 3}).assert(t, work)
|
|
495
|
-
|
|
496
|
-
// Default slug resolved from .devrites/ACTIVE, fresh → exit 0.
|
|
497
|
-
writeFile(t, work, ".devrites/features/defslug/touched-files.md", "# touched\n- `src/other.js`\n")
|
|
498
|
-
writeFile(t, work, ".devrites/features/defslug/evidence.md", "# evidence\n")
|
|
499
|
-
writeFile(t, work, "src/other.js", "x\n")
|
|
500
|
-
writeFile(t, work, ".devrites/ACTIVE", "defslug\n")
|
|
501
|
-
chtimeT(t, work, "src/other.js", old)
|
|
502
|
-
chtimeT(t, work, ".devrites/features/defslug/evidence.md", newer)
|
|
503
|
-
(goldenCase{"default-slug", []string{"evidence-fresh"}, okMsg, 0}).assert(t, work)
|
|
504
|
-
|
|
505
|
-
// An absolute touched path newer than the proof → STALE (exercises the
|
|
506
|
-
// absolute-path branch, not just repo-relative resolution).
|
|
507
|
-
absPath := filepath.Join(work, "absnew.js")
|
|
508
|
-
writeFile(t, work, "absnew.js", "x\n")
|
|
509
|
-
writeFile(t, work, ".devrites/features/abs/touched-files.md", "# touched\n- `"+absPath+"`\n")
|
|
510
|
-
writeFile(t, work, ".devrites/features/abs/evidence.md", "# evidence\n")
|
|
511
|
-
chtimeT(t, work, ".devrites/features/abs/evidence.md", old)
|
|
512
|
-
chtimeT(t, work, "absnew.js", newer)
|
|
513
|
-
(goldenCase{"absolute-path-stale", []string{"evidence-fresh", "abs"}, "", 3}).assert(t, work)
|
|
514
|
-
|
|
515
|
-
// No workspace directory → exit 5.
|
|
516
|
-
(goldenCase{"no-workspace", []string{"evidence-fresh", "ghost"}, "", 5}).assert(t, work)
|
|
517
|
-
|
|
518
|
-
// Workspace present but no evidence → exit 5.
|
|
519
|
-
mkdirAllT(t, work, ".devrites/features/noev")
|
|
520
|
-
(goldenCase{"no-evidence", []string{"evidence-fresh", "noev"}, "", 5}).assert(t, work)
|
|
521
|
-
|
|
522
|
-
// Proof present, no touched-files.md → nothing to compare, exit 0.
|
|
523
|
-
writeFile(t, work, ".devrites/features/notf/evidence.md", "# evidence\n")
|
|
524
|
-
(goldenCase{"no-touched-files", []string{"evidence-fresh", "notf"},
|
|
525
|
-
"evidence-fresh: no touched-files.md: nothing to compare (treating as fresh).\n", 0}).assert(t, work)
|
|
526
|
-
}
|
|
527
|
-
|
|
528
|
-
// mkdirAllT creates an (empty) directory under workdir.
|
|
529
|
-
func mkdirAllT(t *testing.T, workdir, rel string) {
|
|
530
|
-
t.Helper()
|
|
531
|
-
if err := os.MkdirAll(filepath.Join(workdir, rel), 0o755); err != nil {
|
|
532
|
-
t.Fatal(err)
|
|
533
|
-
}
|
|
534
|
-
}
|
|
535
|
-
|
|
536
|
-
// chtimeT sets a file's mtime (and atime) so freshness comparisons are
|
|
537
|
-
// deterministic and identical for both the bash and Go readers.
|
|
538
|
-
func chtimeT(t *testing.T, workdir, rel string, mtime time.Time) {
|
|
539
|
-
t.Helper()
|
|
540
|
-
if err := os.Chtimes(filepath.Join(workdir, rel), mtime, mtime); err != nil {
|
|
541
|
-
t.Fatal(err)
|
|
542
|
-
}
|
|
543
|
-
}
|
|
544
|
-
|
|
545
|
-
// fileLines counts newline-terminated lines in a file.
|
|
546
|
-
func fileLines(t *testing.T, path string) int {
|
|
547
|
-
t.Helper()
|
|
548
|
-
b, err := os.ReadFile(path)
|
|
549
|
-
if err != nil {
|
|
550
|
-
t.Fatal(err)
|
|
551
|
-
}
|
|
552
|
-
return bytes.Count(b, []byte("\n"))
|
|
553
|
-
}
|
|
554
|
-
|
|
555
|
-
// lastLine returns the final non-empty line of a file (its trailing newline
|
|
556
|
-
// stripped), so a caller can assert the exact bytes of the most recent record.
|
|
557
|
-
func lastLine(t *testing.T, path string) string {
|
|
558
|
-
t.Helper()
|
|
559
|
-
b, err := os.ReadFile(path)
|
|
560
|
-
if err != nil {
|
|
561
|
-
t.Fatal(err)
|
|
562
|
-
}
|
|
563
|
-
lines := bytes.Split(bytes.TrimRight(b, "\n"), []byte("\n"))
|
|
564
|
-
return string(lines[len(lines)-1])
|
|
565
|
-
}
|
|
566
|
-
|
|
567
|
-
// ---- coverage -------------------------------------------------------------
|
|
568
|
-
//
|
|
569
|
-
// Golden: the matrix, the AC id sort, the ";"-joined slices (including the awk
|
|
570
|
-
// substring quirk), the UNCOVERED / pending fallbacks, and the exit taxonomy
|
|
571
|
-
// are checked against golden snapshots.
|
|
572
|
-
|
|
573
|
-
func TestParityCoverage(t *testing.T) {
|
|
574
|
-
work := t.TempDir()
|
|
575
|
-
|
|
576
|
-
// Mixed coverage + proof, and C-locale AC ordering (AC1, AC10, AC2). The
|
|
577
|
-
// "AC1" row also picks up the AC10 slice: the awk match is a substring test
|
|
578
|
-
// and the port reproduces it.
|
|
579
|
-
writeFeatureFile(t, work, "full", "spec.md",
|
|
580
|
-
"## Acceptance criteria\n- [ ] [AC1] alpha\n- [ ] [AC2] beta\n- [ ] [AC10] dec\n")
|
|
581
|
-
writeFeatureFile(t, work, "full", "tasks.md",
|
|
582
|
-
"## Slice 1: list\n Satisfies: AC1\n## Slice 2: detail\n Satisfies: AC2, AC10\n")
|
|
583
|
-
writeFeatureFile(t, work, "full", "seal.md",
|
|
584
|
-
"## Acceptance Criteria\n- [x] [AC1] alpha: evidence t1\n- [ ] [AC2] beta\n- [x] [AC10] dec: evidence t3\n")
|
|
585
|
-
|
|
586
|
-
// spec only → every AC UNCOVERED + pending.
|
|
587
|
-
writeFeatureFile(t, work, "speconly", "spec.md",
|
|
588
|
-
"## Acceptance criteria\n- [ ] [AC1] a\n- [ ] [AC2] b\n")
|
|
589
|
-
|
|
590
|
-
// flat spec, no [ACn] ids → header + footer, zero rows.
|
|
591
|
-
writeFeatureFile(t, work, "flat", "spec.md",
|
|
592
|
-
"## Acceptance criteria\n- export returns CSV\n- empty dataset returns 204\n")
|
|
593
|
-
|
|
594
|
-
// workspace dir present but no spec.md → exit 2.
|
|
595
|
-
makeFeatureDir(t, work, "nospec")
|
|
596
|
-
|
|
597
|
-
for _, arg := range []string{
|
|
598
|
-
"full", // matrix with mixed proof + the substring quirk
|
|
599
|
-
"speconly", // all UNCOVERED / pending
|
|
600
|
-
"flat", // no ids → empty matrix body
|
|
601
|
-
"nospec", // dir, no spec.md → exit 2
|
|
602
|
-
"nonexistent", // no workspace dir → exit 2
|
|
603
|
-
} {
|
|
604
|
-
c := parityCase{
|
|
605
|
-
workdir: work,
|
|
606
|
-
env: libRootEnv(work),
|
|
607
|
-
goArgs: []string{"coverage", arg},
|
|
608
|
-
}
|
|
609
|
-
t.Run("arg="+arg, func(t *testing.T) { c.assertEqual(t) })
|
|
610
|
-
}
|
|
611
|
-
|
|
612
|
-
// No-argument path with no active feature → "no active workspace", exit 2.
|
|
613
|
-
(parityCase{
|
|
614
|
-
workdir: work,
|
|
615
|
-
env: libRootEnv(work),
|
|
616
|
-
goArgs: []string{"coverage"},
|
|
617
|
-
}).assertEqual(t)
|
|
618
|
-
}
|
|
619
|
-
|
|
620
|
-
// TestCoverageGolden asserts the exact matrix bytes with an inline expectation
|
|
621
|
-
// (fixtures chosen without an AC1/AC10 collision so the golden reads naturally;
|
|
622
|
-
// the substring quirk is exercised by TestParityCoverage's "full" fixture).
|
|
623
|
-
func TestCoverageGolden(t *testing.T) {
|
|
624
|
-
work := t.TempDir()
|
|
625
|
-
const base = ".devrites/features/"
|
|
626
|
-
|
|
627
|
-
writeFile(t, work, base+"g/spec.md",
|
|
628
|
-
"## Acceptance criteria\n- [ ] [AC1] a\n- [ ] [AC2] b\n- [ ] [AC3] c\n")
|
|
629
|
-
writeFile(t, work, base+"g/tasks.md",
|
|
630
|
-
"## Slice 1: list\n Satisfies: AC1\n## Slice 2: detail\n Satisfies: AC2, AC3\n")
|
|
631
|
-
writeFile(t, work, base+"g/seal.md",
|
|
632
|
-
"## Acceptance Criteria\n- [x] [AC1] a: evidence t1\n- [x] [AC2] b: evidence t2\n- [ ] [AC3] c\n")
|
|
633
|
-
|
|
634
|
-
const wantG = "# Coverage matrix: g\n\n" +
|
|
635
|
-
"| AC | Slice(s) | Proven in seal? |\n" +
|
|
636
|
-
"|----|----------|-----------------|\n" +
|
|
637
|
-
"| AC1 | Slice 1: list | yes |\n" +
|
|
638
|
-
"| AC2 | Slice 2: detail | yes |\n" +
|
|
639
|
-
"| AC3 | Slice 2: detail | pending |\n\n" +
|
|
640
|
-
"_Generated by devrites-engine coverage from spec.md / tasks.md / seal.md. UNCOVERED rows block the analyze gate in /rite-vet (Claude) / $rite-vet (Codex)._\n"
|
|
641
|
-
(goldenCase{"full-matrix", []string{"coverage", "g"}, wantG, 0}).assert(t, work)
|
|
642
|
-
|
|
643
|
-
// spec only → UNCOVERED / pending fallbacks.
|
|
644
|
-
writeFile(t, work, base+"u/spec.md", "## Acceptance criteria\n- [ ] [AC1] a\n")
|
|
645
|
-
const wantU = "# Coverage matrix: u\n\n" +
|
|
646
|
-
"| AC | Slice(s) | Proven in seal? |\n" +
|
|
647
|
-
"|----|----------|-----------------|\n" +
|
|
648
|
-
"| AC1 | (UNCOVERED) | pending |\n\n" +
|
|
649
|
-
"_Generated by devrites-engine coverage from spec.md / tasks.md / seal.md. UNCOVERED rows block the analyze gate in /rite-vet (Claude) / $rite-vet (Codex)._\n"
|
|
650
|
-
(goldenCase{"uncovered", []string{"coverage", "u"}, wantU, 0}).assert(t, work)
|
|
651
|
-
}
|
|
652
|
-
|
|
653
|
-
// ---- doubt-coverage -------------------------------------------------------
|
|
654
|
-
//
|
|
655
|
-
// Golden: the decisions.md `doubt: MISSING` short-circuit (checked first), the
|
|
656
|
-
// no-log pass, the wright/doubt count line, and the 0/1/2/3 exit taxonomy are
|
|
657
|
-
// checked against golden snapshots.
|
|
658
|
-
|
|
659
|
-
func TestParityDoubtCoverage(t *testing.T) {
|
|
660
|
-
work := t.TempDir()
|
|
661
|
-
|
|
662
|
-
// decisions.md MISSING → exit 3, and it wins even over the footprint log.
|
|
663
|
-
writeFeatureFile(t, work, "missing", "decisions.md",
|
|
664
|
-
"## Decisions stood\n- use flock — doubt: MISSING\n")
|
|
665
|
-
writeFeatureFile(t, work, "missing", "footprint.log", "1000 wright 01\n")
|
|
666
|
-
|
|
667
|
-
// dir exists, no log / decisions → "no footprint log" pass.
|
|
668
|
-
makeFeatureDir(t, work, "nolog")
|
|
669
|
-
|
|
670
|
-
// wright dispatch(es), zero doubt → exit 1.
|
|
671
|
-
writeFeatureFile(t, work, "nodoubt", "footprint.log",
|
|
672
|
-
"1000 wright 01\n1000 reviewer code-reviewer\n1000 wright 02\n")
|
|
673
|
-
|
|
674
|
-
// wright + doubt → exit 0.
|
|
675
|
-
writeFeatureFile(t, work, "doubted", "footprint.log", "1000 wright 01\n1000 doubt d1\n")
|
|
676
|
-
|
|
677
|
-
// zero wright (only reviewers) → exit 0, count line "0 wright · N doubt".
|
|
678
|
-
writeFeatureFile(t, work, "nowright", "footprint.log",
|
|
679
|
-
"1000 reviewer code-reviewer\n1000 doubt d1\n")
|
|
680
|
-
|
|
681
|
-
for _, arg := range []string{
|
|
682
|
-
"missing", "nolog",
|
|
683
|
-
"nodoubt", "doubted", "nowright",
|
|
684
|
-
"ghost", // no workspace at all → "no footprint log" pass
|
|
685
|
-
} {
|
|
686
|
-
c := parityCase{
|
|
687
|
-
workdir: work,
|
|
688
|
-
env: libRootEnv(work),
|
|
689
|
-
goArgs: []string{"doubt-coverage", arg},
|
|
690
|
-
}
|
|
691
|
-
t.Run("arg="+arg, func(t *testing.T) { c.assertEqual(t) })
|
|
692
|
-
}
|
|
693
|
-
|
|
694
|
-
// No-slug usage path → exit 2.
|
|
695
|
-
(parityCase{
|
|
696
|
-
workdir: work,
|
|
697
|
-
env: libRootEnv(work),
|
|
698
|
-
goArgs: []string{"doubt-coverage"},
|
|
699
|
-
}).assertEqual(t)
|
|
700
|
-
}
|
|
701
|
-
|
|
702
|
-
// TestDoubtCoverageGolden asserts each verdict's exact stdout inline: the
|
|
703
|
-
// SKIPPED / no-log messages and the "no doubt ran" prompt.
|
|
704
|
-
func TestDoubtCoverageGolden(t *testing.T) {
|
|
705
|
-
work := t.TempDir()
|
|
706
|
-
const base = ".devrites/features/"
|
|
707
|
-
|
|
708
|
-
writeFile(t, work, base+"miss/decisions.md", "- x — doubt: MISSING\n")
|
|
709
|
-
mkdirAllT(t, work, base+"empty")
|
|
710
|
-
writeFile(t, work, base+"nd/footprint.log", "1000 wright 01\n1000 wright 02\n")
|
|
711
|
-
writeFile(t, work, base+"ok/footprint.log", "1000 wright 01\n1000 doubt d1\n")
|
|
712
|
-
|
|
713
|
-
for _, gc := range []goldenCase{
|
|
714
|
-
{"skipped", []string{"doubt-coverage", "miss"},
|
|
715
|
-
"doubt-coverage: SKIPPED: a '## Decisions stood' entry in decisions.md has doubt: MISSING.\n" +
|
|
716
|
-
"A decision was stood and recorded but never doubted. Re-dispatch doubt or escalate.\n", 3},
|
|
717
|
-
{"no-log", []string{"doubt-coverage", "empty"},
|
|
718
|
-
"doubt-coverage: no footprint log: nothing to assess (pass)\n", 0},
|
|
719
|
-
{"no-doubt-ran", []string{"doubt-coverage", "nd"},
|
|
720
|
-
"doubt-coverage: 2 wright · 0 doubt\n" +
|
|
721
|
-
"doubt-coverage: no doubt dispatched across 2 wright run(s). Either every slice's\n" +
|
|
722
|
-
"'Decisions stood' was genuinely empty, or doubt was skipped: verify against decisions.md.\n", 1},
|
|
723
|
-
{"doubted", []string{"doubt-coverage", "ok"},
|
|
724
|
-
"doubt-coverage: 1 wright · 1 doubt\n", 0},
|
|
725
|
-
} {
|
|
726
|
-
t.Run(gc.name, func(t *testing.T) { gc.assert(t, work) })
|
|
727
|
-
}
|
|
728
|
-
}
|
|
729
|
-
|
|
730
|
-
// ---- preamble -------------------------------------------------------------
|
|
731
|
-
//
|
|
732
|
-
// Golden: the workspace-state orientation: the header, verbatim state.md (with
|
|
733
|
-
// and without a trailing newline), the fixed artifact-present order, the AFK/HITL
|
|
734
|
-
// run mode with verbatim config lines, and the questions-by-gate tally: are
|
|
735
|
-
// checked against golden snapshots.
|
|
736
|
-
|
|
737
|
-
func TestParityPreamble(t *testing.T) {
|
|
738
|
-
// A HITL workspace (no .devrites/AFK) exercising most branches.
|
|
739
|
-
hitl := t.TempDir()
|
|
740
|
-
writeFeatureFile(t, hitl, "full", "state.md", "- Phase: build\n- Status: running\n- Next step: implement slice 3\n")
|
|
741
|
-
writeFeatureFile(t, hitl, "full", "spec.md", "# spec\n")
|
|
742
|
-
writeFeatureFile(t, hitl, "full", "plan.md", "# plan\n")
|
|
743
|
-
writeFeatureFile(t, hitl, "full", "tasks.md", "# tasks\n")
|
|
744
|
-
writeFeatureFile(t, hitl, "full", "questions.md",
|
|
745
|
-
"## q-1\nstatus: open\ngate: blocking\n\n"+
|
|
746
|
-
"## q-2\nstatus: resolved\ngate: validating\n\n"+
|
|
747
|
-
"## q-3\nstatus: open\ngate: advisory\n\n"+
|
|
748
|
-
"## q-4\nstatus: open\ngate: blocking\n## Notes\nprose\n")
|
|
749
|
-
// questions.md ending on an OPEN q- block (no closing heading): exercises the
|
|
750
|
-
// awk END-finalize path.
|
|
751
|
-
writeFeatureFile(t, hitl, "openend", "state.md", "- Phase: spec\n")
|
|
752
|
-
writeFeatureFile(t, hitl, "openend", "questions.md",
|
|
753
|
-
"## q-1\nstatus: resolved\ngate: blocking\n\n## q-2\nstatus: open\ngate: escalating\n")
|
|
754
|
-
// state.md with NO trailing newline: `cat` must not add one before the blank line.
|
|
755
|
-
writeFeatureFile(t, hitl, "nonl", "state.md", "- Phase: spec")
|
|
756
|
-
// A workspace directory with no state.md / questions.md.
|
|
757
|
-
makeFeatureDir(t, hitl, "bare")
|
|
758
|
-
writeFile(t, hitl, ".devrites/ACTIVE", "full\n")
|
|
759
|
-
|
|
760
|
-
for _, arg := range []string{"full", "openend", "nonl", "bare", "ghost"} {
|
|
761
|
-
c := parityCase{
|
|
762
|
-
workdir: hitl,
|
|
763
|
-
env: libRootEnv(hitl),
|
|
764
|
-
goArgs: []string{"preamble", arg},
|
|
765
|
-
}
|
|
766
|
-
t.Run("hitl/"+arg, func(t *testing.T) { c.assertEqual(t) })
|
|
767
|
-
}
|
|
768
|
-
// No-arg path resolves the slug from ACTIVE.
|
|
769
|
-
t.Run("hitl/active", func(t *testing.T) {
|
|
770
|
-
(parityCase{workdir: hitl, env: libRootEnv(hitl), goArgs: []string{"preamble"}}).assertEqual(t)
|
|
771
|
-
})
|
|
772
|
-
|
|
773
|
-
// An AFK workspace: the sentinel plus verbatim (non-comment) config lines.
|
|
774
|
-
afk := t.TempDir()
|
|
775
|
-
writeFeatureFile(t, afk, "a", "state.md", "- Phase: prove\n")
|
|
776
|
-
writeFile(t, afk, ".devrites/AFK", "# afk config\nmax-slices: 3\n\nescalate: true\n")
|
|
777
|
-
t.Run("afk/a", func(t *testing.T) {
|
|
778
|
-
(parityCase{workdir: afk, env: libRootEnv(afk), goArgs: []string{"preamble", "a"}}).assertEqual(t)
|
|
779
|
-
})
|
|
780
|
-
}
|
|
781
|
-
|
|
782
|
-
// ---- progress -------------------------------------------------------------
|
|
783
|
-
//
|
|
784
|
-
// Golden: the header rule width, the slice meter (round-half math, ✅ ALL BUILT
|
|
785
|
-
// vs the last-built tail), and the flow ribbon (conditional temper/vet, the
|
|
786
|
-
// explicit plan and terminal done cursor rules) are checked against golden snapshots.
|
|
787
|
-
|
|
788
|
-
func TestParityProgress(t *testing.T) {
|
|
789
|
-
work := t.TempDir()
|
|
790
|
-
|
|
791
|
-
sliceBlock := "\n## Slice progress\n" +
|
|
792
|
-
"- [x] Slice 1: list · built\n" +
|
|
793
|
-
"- [x] Slice 2: detail · built\n" +
|
|
794
|
-
"- [ ] Slice 3: polish · pending\n" +
|
|
795
|
-
"## Next\n- something\n"
|
|
796
|
-
|
|
797
|
-
// build phase, 2/3 slices, temper (strategy.md) + vet (eng-review.md) present.
|
|
798
|
-
writeFeatureFile(t, work, "mid", "state.md", "- Phase: build"+sliceBlock)
|
|
799
|
-
writeFeatureFile(t, work, "mid", "strategy.md", "# strategy\n")
|
|
800
|
-
writeFeatureFile(t, work, "mid", "eng-review.md", "# eng-review\n")
|
|
801
|
-
|
|
802
|
-
// all built → ✅ ALL BUILT; no temper/vet artifacts → shorter ribbon.
|
|
803
|
-
writeFeatureFile(t, work, "allbuilt", "state.md",
|
|
804
|
-
"- Phase: build\n## Slice progress\n- [x] Slice 1: a · built\n- [x] Slice 2: b · built\n")
|
|
805
|
-
|
|
806
|
-
// no slices at all → meter skipped entirely.
|
|
807
|
-
writeFeatureFile(t, work, "noslice", "state.md", "- Phase: spec\n")
|
|
808
|
-
|
|
809
|
-
// phase variants exercising the cursor rules.
|
|
810
|
-
writeFeatureFile(t, work, "done", "state.md", "- Phase: done\n")
|
|
811
|
-
writeFeatureFile(t, work, "plan", "state.md", "- Phase: plan"+sliceBlock)
|
|
812
|
-
writeFeatureFile(t, work, "seal", "state.md", "- Phase: seal\n")
|
|
813
|
-
// missing Phase → defaults to "spec".
|
|
814
|
-
writeFeatureFile(t, work, "nophase", "state.md", "- Status: running\n")
|
|
815
|
-
|
|
816
|
-
for _, arg := range []string{"mid", "allbuilt", "noslice", "done", "plan", "seal", "nophase", "ghost"} {
|
|
817
|
-
c := parityCase{
|
|
818
|
-
workdir: work,
|
|
819
|
-
env: libRootEnv(work),
|
|
820
|
-
goArgs: []string{"progress", arg},
|
|
821
|
-
}
|
|
822
|
-
t.Run("arg="+arg, func(t *testing.T) { c.assertEqual(t) })
|
|
823
|
-
}
|
|
824
|
-
}
|
|
825
|
-
|
|
826
|
-
// ---- stuck ----------------------------------------------------------------
|
|
827
|
-
//
|
|
828
|
-
// Golden: `check` reads only the sha1 column, so a fixture log with controlled
|
|
829
|
-
// hash tokens drives every verdict (too-few / identical / ping-pong / dominant /
|
|
830
|
-
// varied / custom window) and the 0/3 exit split, checked against golden
|
|
831
|
-
// snapshots. `log` is exercised separately (its stdout is empty; its side effect
|
|
832
|
-
// carries a nondeterministic timestamp, so only the sha1 column is snapshotted).
|
|
833
|
-
|
|
834
|
-
func TestParityStuckCheck(t *testing.T) {
|
|
835
|
-
work := t.TempDir()
|
|
836
|
-
|
|
837
|
-
// Each record is "<epoch> <tool> <hash>"; check only reads the hash ($3).
|
|
838
|
-
rec := func(h string) string { return "1700000000 Bash " + h + "\n" }
|
|
839
|
-
writeFeatureFile(t, work, "few", "action.log", rec("a")+rec("a")) // NR < win(4)
|
|
840
|
-
writeFeatureFile(t, work, "identical", "action.log", rec("a")+rec("a")+rec("a")+rec("a")) // last 4 identical
|
|
841
|
-
writeFeatureFile(t, work, "pingpong", "action.log", rec("a")+rec("b")+rec("a")+rec("b")+rec("a")+rec("b"))
|
|
842
|
-
writeFeatureFile(t, work, "dominant", "action.log", rec("a")+rec("a")+rec("a")+rec("a")+rec("a")+rec("a")+rec("b")+rec("c"))
|
|
843
|
-
writeFeatureFile(t, work, "varied", "action.log", rec("a")+rec("b")+rec("c")+rec("d")+rec("e"))
|
|
844
|
-
writeFeatureFile(t, work, "varied4", "action.log", rec("a")+rec("b")+rec("c")+rec("d")) // 4 distinct
|
|
845
|
-
makeFeatureDir(t, work, "nolog")
|
|
846
|
-
|
|
847
|
-
type cse struct {
|
|
848
|
-
name string
|
|
849
|
-
goArgs []string
|
|
850
|
-
}
|
|
851
|
-
for _, tc := range []cse{
|
|
852
|
-
{"few", []string{"check", "few"}},
|
|
853
|
-
{"identical", []string{"check", "identical"}},
|
|
854
|
-
{"pingpong", []string{"check", "pingpong"}},
|
|
855
|
-
{"dominant", []string{"check", "dominant"}},
|
|
856
|
-
{"varied", []string{"check", "varied"}},
|
|
857
|
-
{"nolog", []string{"check", "nolog"}},
|
|
858
|
-
{"ghost", []string{"check", "ghost"}},
|
|
859
|
-
{"custom-window-2", []string{"check", "few", "2"}}, // win=2 → last 2 identical → STUCK
|
|
860
|
-
// Pathological window tokens: the numeric-string vs string comparison and
|
|
861
|
-
// the verbatim `win` echo: edge cases the port must reproduce.
|
|
862
|
-
{"window-4x-varied4", []string{"check", "varied4", "4x"}},
|
|
863
|
-
{"window-2.5-varied4", []string{"check", "varied4", "2.5"}},
|
|
864
|
-
{"window-leadspace-varied4", []string{"check", "varied4", " 4"}},
|
|
865
|
-
{"window-04-identical", []string{"check", "identical", "04"}},
|
|
866
|
-
{"window-plus4-identical", []string{"check", "identical", "+4"}},
|
|
867
|
-
{"no-cmd", nil},
|
|
868
|
-
{"no-slug", []string{"check"}},
|
|
869
|
-
{"bad-cmd", []string{"bogus", "few"}},
|
|
870
|
-
} {
|
|
871
|
-
c := parityCase{workdir: work, env: libRootEnv(work), goArgs: append([]string{"stuck"}, tc.goArgs...)}
|
|
872
|
-
t.Run(tc.name, func(t *testing.T) { c.assertEqual(t) })
|
|
873
|
-
}
|
|
874
|
-
}
|
|
875
|
-
|
|
876
|
-
// TestStuckLogHashParity proves the `log` side effect: it appends a record whose
|
|
877
|
-
// sha1 column is deterministic given the logged target, so that column is
|
|
878
|
-
// snapshotted (the epoch column is nondeterministic and excluded by reading only
|
|
879
|
-
// field 3). A second log appends a second record, and logging to a missing
|
|
880
|
-
// workspace is a silent no-op.
|
|
881
|
-
func TestStuckLogHashParity(t *testing.T) {
|
|
882
|
-
field3 := func(t *testing.T, path string) string {
|
|
883
|
-
t.Helper()
|
|
884
|
-
fields := bytes.Fields(bytes.TrimRight(readFileT(t, path), "\n"))
|
|
885
|
-
if len(fields) < 3 {
|
|
886
|
-
t.Fatalf("log line has <3 fields: %q", string(readFileT(t, path)))
|
|
887
|
-
}
|
|
888
|
-
return string(fields[2])
|
|
889
|
-
}
|
|
890
|
-
|
|
891
|
-
// The new-schema features layout under DEVRITES_ROOT.
|
|
892
|
-
gw := t.TempDir()
|
|
893
|
-
mkdirAllT(t, gw, ".devrites/features/feat")
|
|
894
|
-
goLog := filepath.Join(gw, ".devrites/features/feat/action.log")
|
|
895
|
-
runArgv(t, gw, libRootEnv(gw), "", binPath, "stuck", "log", "feat", "Bash", "src/a.js")
|
|
896
|
-
goHash := field3(t, goLog)
|
|
897
|
-
|
|
898
|
-
// Snapshot the sha1 column (the epoch column is masked by reading only field 3).
|
|
899
|
-
assertGoldenKey(t, t.Name(), goHash)
|
|
900
|
-
if len(goHash) != 40 {
|
|
901
|
-
t.Errorf("sha1 hex = %q, want 40 chars", goHash)
|
|
902
|
-
}
|
|
903
|
-
|
|
904
|
-
// A second log appends a second record.
|
|
905
|
-
runArgv(t, gw, libRootEnv(gw), "", binPath, "stuck", "log", "feat", "Edit", "src/b.js")
|
|
906
|
-
if n := fileLines(t, goLog); n != 2 {
|
|
907
|
-
t.Errorf("after two logs: %d lines, want 2", n)
|
|
908
|
-
}
|
|
909
|
-
// Logging to a missing workspace is a no-op (never fails the caller).
|
|
910
|
-
if _, code := runArgv(t, gw, libRootEnv(gw), "", binPath, "stuck", "log", "ghost", "Bash", "x"); code != 0 {
|
|
911
|
-
t.Errorf("log on missing workspace: exit=%d, want 0", code)
|
|
912
|
-
}
|
|
913
|
-
if isFileT(filepath.Join(gw, ".devrites/features/ghost/action.log")) {
|
|
914
|
-
t.Error("log on missing workspace created a file; must be a no-op")
|
|
915
|
-
}
|
|
916
|
-
}
|
|
917
|
-
|
|
918
|
-
func readFileT(t *testing.T, path string) []byte {
|
|
919
|
-
t.Helper()
|
|
920
|
-
b, err := os.ReadFile(path)
|
|
921
|
-
if err != nil {
|
|
922
|
-
t.Fatal(err)
|
|
923
|
-
}
|
|
924
|
-
return b
|
|
925
|
-
}
|
|
926
|
-
|
|
927
|
-
func isFileT(path string) bool {
|
|
928
|
-
fi, err := os.Stat(path)
|
|
929
|
-
return err == nil && !fi.IsDir()
|
|
930
|
-
}
|