devrites 5.14.4 → 6.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 +17 -0
- package/NOTICE.md +6 -4
- package/README.md +77 -16
- package/SECURITY.md +25 -11
- package/bin/devrites.mjs +49 -15
- package/docs/adr/0001-go-engine-as-control-plane.md +6 -3
- package/docs/adr/0029-v5-workspace-schema-and-native-migration.md +9 -0
- package/docs/adr/README.md +3 -2
- package/docs/agents/issue-tracker.md +1 -1
- package/docs/architecture.md +6 -3
- package/docs/cli.md +32 -9
- package/docs/command-map.md +5 -3
- package/docs/engine/state-schema.md +1 -1
- package/docs/flow.md +1 -1
- package/docs/harness-compliance.md +2 -2
- package/docs/release.md +15 -11
- package/docs/usage.md +1 -1
- package/engine/commands.go +1 -1
- package/engine/go.mod +1 -1
- package/engine/help.go +41 -15
- package/engine/help_test.go +134 -0
- package/engine/internal/acceptance/acceptance_test.go +298 -7
- package/engine/internal/acceptance/approve.go +7 -2
- package/engine/internal/acceptance/cli.go +3 -0
- package/engine/internal/acceptance/evidence.go +69 -5
- package/engine/internal/acceptance/lint.go +1 -1
- package/engine/internal/acceptance/run.go +5 -3
- package/engine/internal/acceptance/scaffold.go +5 -16
- package/engine/internal/devritespaths/paths.go +7 -6
- package/engine/internal/devritespaths/paths_test.go +18 -0
- package/engine/internal/fsutil/copy.go +83 -49
- package/engine/internal/fsutil/copy_test.go +70 -109
- package/engine/internal/gate/gate.go +89 -16
- package/engine/internal/gate/gate_test.go +101 -0
- package/engine/internal/hostpack/hostpack.go +52 -2
- package/engine/internal/hostpack/hostpack_test.go +83 -2
- package/engine/internal/install/apply.go +59 -16
- package/engine/internal/install/binary.go +10 -1
- package/engine/internal/install/install_test.go +428 -0
- package/engine/internal/install/merge.go +69 -38
- package/engine/internal/install/preflight.go +7 -0
- package/engine/internal/install/uninstall.go +43 -4
- package/engine/internal/lib/claims.go +25 -4
- package/engine/internal/lib/claims_test.go +57 -0
- package/engine/internal/lib/closeout.go +41 -3
- package/engine/internal/lib/closeout_test.go +144 -0
- package/engine/internal/lib/context.go +8 -4
- package/engine/internal/lib/context_next_test.go +100 -0
- package/engine/internal/lib/detect.go +1 -1
- package/engine/internal/lib/detect_test.go +13 -0
- package/engine/internal/lib/diffscope.go +19 -9
- package/engine/internal/lib/dispatch.go +7 -0
- package/engine/internal/lib/dup.go +40 -37
- package/engine/internal/lib/dup_test.go +178 -0
- package/engine/internal/lib/manifest_union.go +4 -1
- package/engine/internal/lib/manifest_union_test.go +37 -0
- package/engine/internal/lib/metrics.go +4 -0
- package/engine/internal/lib/metrics_test.go +28 -0
- package/engine/internal/lib/migrate_test.go +59 -0
- package/engine/internal/lib/observe_budgets.go +25 -9
- package/engine/internal/lib/observe_budgets_test.go +254 -0
- package/engine/internal/lib/observe_summary.go +23 -2
- package/engine/internal/lib/open_visual.go +4 -2
- package/engine/internal/lib/open_visual_test.go +11 -0
- package/engine/internal/lib/regression.go +62 -23
- package/engine/internal/lib/regression_test.go +103 -0
- package/engine/internal/lib/resolve.go +36 -17
- package/engine/internal/lib/resolve_remediation_test.go +132 -0
- package/engine/internal/lib/skilltrust.go +0 -11
- package/engine/internal/lib/slicecheck.go +7 -5
- package/engine/internal/lib/slicecheck_test.go +18 -0
- package/engine/internal/lib/triggers.go +9 -3
- package/engine/internal/lib/triggers_test.go +23 -0
- package/engine/internal/lib/util.go +17 -3
- package/engine/internal/lib/util_test.go +104 -0
- package/engine/internal/lib/visual_outline.go +7 -3
- package/engine/internal/lib/visual_outline_test.go +28 -0
- package/engine/internal/lib/windows.go +13 -9
- package/engine/internal/lib/windows_test.go +98 -0
- package/engine/internal/lib/workspace_read.go +2 -11
- package/engine/internal/lib/workspace_read_test.go +102 -0
- package/engine/internal/markdowntext/markdowntext.go +194 -11
- package/engine/internal/markdowntext/testdata/structural.json +250 -0
- package/engine/internal/notes/cli.go +30 -12
- package/engine/internal/notes/notes.go +242 -39
- package/engine/internal/notes/notes_test.go +372 -0
- package/engine/internal/overhaul/admit/packet.go +89 -0
- package/engine/internal/overhaul/admit/packet_test.go +223 -0
- package/engine/internal/overhaul/admit/run.go +4 -0
- package/engine/internal/overhaul/bench/run.go +6 -0
- package/engine/internal/overhaul/bench/run_test.go +46 -0
- package/engine/internal/overhaul/overhaul.go +6 -1
- package/engine/internal/overhaul/ovio/ovio.go +10 -3
- package/engine/internal/overhaul/ovio/ovio_test.go +75 -0
- package/engine/internal/overhaul/packet/packet.go +208 -0
- package/engine/internal/overhaul/packet/packet_test.go +263 -0
- package/engine/internal/overhaul/records/check.go +6 -0
- package/engine/internal/overhaul/records/init.go +16 -5
- package/engine/internal/overhaul/records/records_test.go +4 -0
- package/engine/internal/overhaul/records/run.go +66 -0
- package/engine/internal/overhaul/records/run_test.go +77 -0
- package/engine/internal/overhaul/render/run.go +15 -11
- package/engine/internal/overhaul/render/run_test.go +463 -1
- package/engine/internal/overhaul/render/visual.go +95 -33
- package/engine/internal/overhaul/score/run.go +20 -0
- package/engine/internal/overhaul/score/run_test.go +156 -1
- package/engine/internal/overhaul/snapshot/run.go +78 -21
- package/engine/internal/overhaul/snapshot/run_test.go +344 -8
- package/engine/internal/parallel/cli.go +33 -20
- package/engine/internal/parallel/cli_test.go +59 -0
- package/engine/internal/parallel/git.go +39 -1
- package/engine/internal/parallel/ops.go +101 -11
- package/engine/internal/parallel/ops_test.go +552 -69
- package/engine/internal/parallel/parallel_test.go +31 -1
- package/engine/internal/parallel/pathdisjoint.go +19 -1
- package/engine/internal/parallel/pathdisjoint_test.go +72 -0
- package/engine/internal/parallel/select.go +6 -2
- package/engine/internal/release/release.go +13 -3
- package/engine/internal/release/release_test.go +208 -3
- package/engine/internal/rootfacts/facts.go +9 -0
- package/engine/internal/rootfacts/facts_test.go +38 -0
- package/engine/internal/state/acceptance.go +7 -1
- package/engine/internal/state/budgets.go +11 -10
- package/engine/internal/state/budgets_test.go +93 -0
- package/engine/internal/state/cursor.go +19 -8
- package/engine/internal/state/cursor_test.go +120 -0
- package/engine/internal/state/lock_unix.go +7 -1
- package/engine/internal/state/lock_windows.go +7 -1
- package/engine/internal/state/state_test.go +49 -18
- package/engine/internal/state/taskgraph.go +18 -4
- package/engine/internal/state/taskgraph_test.go +43 -0
- package/engine/internal/state/workspaceschema.go +16 -2
- package/engine/internal/state/workspaceschema_test.go +34 -0
- package/engine/internal/state/write.go +67 -6
- package/engine/internal/state/write_test.go +136 -0
- package/engine/internal/version/version_test.go +2 -0
- package/engine/main.go +8 -1
- package/engine/observation_contract_test.go +1 -1
- package/engine/root_routing_test.go +23 -1
- package/install.sh +132 -12
- package/pack/.claude/agents/devrites-spec-reviewer.md +2 -2
- package/pack/.claude/skills/devrites-browser-proof/SKILL.md +2 -2
- package/pack/.claude/skills/devrites-frontend-craft/reference/design-references.md +18 -18
- package/pack/.claude/skills/devrites-frontend-craft/reference/quality-standards.md +6 -4
- package/pack/.claude/skills/devrites-interview/SKILL.md +4 -4
- package/pack/.claude/skills/devrites-lib/reference/parallel-dispatch.md +1 -1
- package/pack/.claude/skills/devrites-lib/reference/standards/afk-hitl.md +9 -1
- package/pack/.claude/skills/devrites-lib/reference/standards/agents.md +3 -2
- package/pack/.claude/skills/devrites-lib/reference/standards/gates.md +6 -5
- package/pack/.claude/skills/devrites-lib/reference/standards/spec-grammar.md +1 -1
- package/pack/.claude/skills/devrites-lib/reference/standards/verification-methods.md +3 -3
- package/pack/.claude/skills/devrites-lib/reference/standards/windows.md +3 -3
- package/pack/.claude/skills/devrites-lib/reference/standards/workflow-artifacts.md +2 -2
- package/pack/.claude/skills/devrites-lib/reference/visual-playbooks/outline-template.md +1 -1
- package/pack/.claude/skills/devrites-lib/reference/workspace-artifact-schema.md +4 -4
- package/pack/.claude/skills/devrites-ux-shape/SKILL.md +11 -12
- package/pack/.claude/skills/devrites-ux-shape/reference/visual-direction-probe.md +3 -1
- package/pack/.claude/skills/overhaul/SKILL.md +1 -1
- package/pack/.claude/skills/overhaul/references/orchestration.md +5 -3
- package/pack/.claude/skills/overhaul/references/records.md +39 -0
- package/pack/.claude/skills/overhaul/references/scoring.md +14 -4
- package/pack/.claude/skills/rite/reference/menu.md +1 -1
- package/pack/.claude/skills/rite-adopt/SKILL.md +8 -2
- package/pack/.claude/skills/rite-autocomplete/SKILL.md +11 -6
- package/pack/.claude/skills/rite-autocomplete/reference/decision-policy.md +3 -2
- package/pack/.claude/skills/rite-autocomplete/reference/loop.md +1 -1
- package/pack/.claude/skills/rite-autocomplete/reference/stop-conditions.md +6 -3
- package/pack/.claude/skills/rite-build/SKILL.md +2 -1
- package/pack/.claude/skills/rite-build/reference/afk-discipline.md +1 -1
- package/pack/.claude/skills/rite-build/reference/checkpoint-protocol.md +1 -1
- package/pack/.claude/skills/rite-build/reference/checkpoint.md +5 -0
- package/pack/.claude/skills/rite-build/reference/parallel-batch.md +2 -2
- package/pack/.claude/skills/rite-build/reference/wright-dispatch.md +3 -2
- package/pack/.claude/skills/rite-clarify/SKILL.md +5 -4
- package/pack/.claude/skills/rite-clarify/reference/anti-patterns.md +2 -2
- package/pack/.claude/skills/rite-clarify/reference/decision-coverage.md +3 -2
- package/pack/.claude/skills/rite-converge/SKILL.md +2 -0
- package/pack/.claude/skills/rite-customize/SKILL.md +6 -4
- package/pack/.claude/skills/rite-define/SKILL.md +2 -0
- package/pack/.claude/skills/rite-define/reference/gates.md +8 -7
- package/pack/.claude/skills/rite-explain/SKILL.md +1 -1
- package/pack/.claude/skills/rite-explain/reference/intake.md +1 -1
- package/pack/.claude/skills/rite-fast/SKILL.md +1 -1
- package/pack/.claude/skills/rite-plan/SKILL.md +4 -3
- package/pack/.claude/skills/rite-plan/reference/replan-and-repair.md +4 -4
- package/pack/.claude/skills/rite-polish/reference/anti-ai-slop.md +1 -1
- package/pack/.claude/skills/rite-prove/reference/browser-proof.md +5 -5
- package/pack/.claude/skills/rite-resolve/SKILL.md +3 -3
- package/pack/.claude/skills/rite-seal/SKILL.md +4 -3
- package/pack/.claude/skills/rite-seal/reference/phase-contract.md +3 -2
- package/pack/.claude/skills/rite-seal/reference/seal-template.md +2 -0
- package/pack/.claude/skills/rite-ship/SKILL.md +4 -2
- package/pack/.claude/skills/rite-ship/reference/git-ship.md +8 -2
- package/pack/.claude/skills/rite-ship/reference/ship-template.md +2 -1
- package/pack/.claude/skills/rite-spec/SKILL.md +2 -2
- package/pack/.claude/skills/rite-spec/reference/spec-checklists.md +1 -1
- package/pack/.claude/skills/rite-spec/reference/spec-template.md +7 -3
- package/pack/.claude/skills/rite-spec/reference/state-workspace.md +2 -0
- package/pack/.claude/skills/rite-temper/SKILL.md +12 -9
- package/pack/.claude/skills/rite-temper/reference/scope-modes.md +1 -1
- package/pack/.claude/skills/rite-temper/reference/significance.md +10 -3
- package/pack/.claude/skills/rite-temper/reference/strategy-template.md +13 -2
- package/pack/.claude/skills/rite-vet/SKILL.md +4 -3
- package/pack/.claude/skills/rite-vet/reference/artifacts.md +1 -1
- package/pack/.claude/workflows/devrites-readonly-review.js +5 -4
- package/pack/generated/claude/agents/devrites-spec-reviewer.md +2 -2
- package/pack/generated/claude/skills/devrites-browser-proof/SKILL.md +2 -2
- package/pack/generated/claude/skills/devrites-frontend-craft/reference/design-references.md +18 -18
- package/pack/generated/claude/skills/devrites-frontend-craft/reference/quality-standards.md +6 -4
- package/pack/generated/claude/skills/devrites-interview/SKILL.md +4 -4
- package/pack/generated/claude/skills/devrites-lib/reference/parallel-dispatch.md +1 -1
- package/pack/generated/claude/skills/devrites-lib/reference/standards/afk-hitl.md +9 -1
- package/pack/generated/claude/skills/devrites-lib/reference/standards/agents.md +3 -2
- package/pack/generated/claude/skills/devrites-lib/reference/standards/gates.md +6 -5
- package/pack/generated/claude/skills/devrites-lib/reference/standards/spec-grammar.md +1 -1
- package/pack/generated/claude/skills/devrites-lib/reference/standards/verification-methods.md +3 -3
- package/pack/generated/claude/skills/devrites-lib/reference/standards/windows.md +3 -3
- package/pack/generated/claude/skills/devrites-lib/reference/standards/workflow-artifacts.md +2 -2
- package/pack/generated/claude/skills/devrites-lib/reference/visual-playbooks/outline-template.md +1 -1
- package/pack/generated/claude/skills/devrites-lib/reference/workspace-artifact-schema.md +4 -4
- package/pack/generated/claude/skills/devrites-ux-shape/SKILL.md +11 -12
- package/pack/generated/claude/skills/devrites-ux-shape/reference/visual-direction-probe.md +3 -1
- package/pack/generated/claude/skills/overhaul/SKILL.md +1 -1
- package/pack/generated/claude/skills/overhaul/references/orchestration.md +5 -3
- package/pack/generated/claude/skills/overhaul/references/records.md +39 -0
- package/pack/generated/claude/skills/overhaul/references/scoring.md +14 -4
- package/pack/generated/claude/skills/rite/reference/menu.md +1 -1
- package/pack/generated/claude/skills/rite-adopt/SKILL.md +8 -2
- package/pack/generated/claude/skills/rite-autocomplete/SKILL.md +11 -6
- package/pack/generated/claude/skills/rite-autocomplete/reference/decision-policy.md +3 -2
- package/pack/generated/claude/skills/rite-autocomplete/reference/loop.md +1 -1
- package/pack/generated/claude/skills/rite-autocomplete/reference/stop-conditions.md +6 -3
- package/pack/generated/claude/skills/rite-build/SKILL.md +2 -1
- package/pack/generated/claude/skills/rite-build/reference/afk-discipline.md +1 -1
- package/pack/generated/claude/skills/rite-build/reference/checkpoint-protocol.md +1 -1
- package/pack/generated/claude/skills/rite-build/reference/checkpoint.md +5 -0
- package/pack/generated/claude/skills/rite-build/reference/parallel-batch.md +2 -2
- package/pack/generated/claude/skills/rite-build/reference/wright-dispatch.md +3 -2
- package/pack/generated/claude/skills/rite-clarify/SKILL.md +5 -4
- package/pack/generated/claude/skills/rite-clarify/reference/anti-patterns.md +2 -2
- package/pack/generated/claude/skills/rite-clarify/reference/decision-coverage.md +3 -2
- package/pack/generated/claude/skills/rite-converge/SKILL.md +2 -0
- package/pack/generated/claude/skills/rite-customize/SKILL.md +6 -4
- package/pack/generated/claude/skills/rite-define/SKILL.md +2 -0
- package/pack/generated/claude/skills/rite-define/reference/gates.md +8 -7
- package/pack/generated/claude/skills/rite-explain/SKILL.md +1 -1
- package/pack/generated/claude/skills/rite-explain/reference/intake.md +1 -1
- package/pack/generated/claude/skills/rite-fast/SKILL.md +1 -1
- package/pack/generated/claude/skills/rite-plan/SKILL.md +4 -3
- package/pack/generated/claude/skills/rite-plan/reference/replan-and-repair.md +4 -4
- package/pack/generated/claude/skills/rite-polish/reference/anti-ai-slop.md +1 -1
- package/pack/generated/claude/skills/rite-prove/reference/browser-proof.md +5 -5
- package/pack/generated/claude/skills/rite-resolve/SKILL.md +3 -3
- package/pack/generated/claude/skills/rite-seal/SKILL.md +4 -3
- package/pack/generated/claude/skills/rite-seal/reference/phase-contract.md +3 -2
- package/pack/generated/claude/skills/rite-seal/reference/seal-template.md +2 -0
- package/pack/generated/claude/skills/rite-ship/SKILL.md +4 -2
- package/pack/generated/claude/skills/rite-ship/reference/git-ship.md +8 -2
- package/pack/generated/claude/skills/rite-ship/reference/ship-template.md +2 -1
- package/pack/generated/claude/skills/rite-spec/SKILL.md +2 -2
- package/pack/generated/claude/skills/rite-spec/reference/spec-checklists.md +1 -1
- package/pack/generated/claude/skills/rite-spec/reference/spec-template.md +7 -3
- package/pack/generated/claude/skills/rite-spec/reference/state-workspace.md +2 -0
- package/pack/generated/claude/skills/rite-temper/SKILL.md +12 -9
- package/pack/generated/claude/skills/rite-temper/reference/scope-modes.md +1 -1
- package/pack/generated/claude/skills/rite-temper/reference/significance.md +10 -3
- package/pack/generated/claude/skills/rite-temper/reference/strategy-template.md +13 -2
- package/pack/generated/claude/skills/rite-vet/SKILL.md +4 -3
- package/pack/generated/claude/skills/rite-vet/reference/artifacts.md +1 -1
- package/pack/generated/claude/workflows/devrites-readonly-review.js +5 -4
- package/pack/generated/codex/AGENTS.md +1 -1
- package/pack/generated/codex/agents/devrites-spec-reviewer.toml +2 -2
- package/pack/generated/codex/skills/devrites-browser-proof/SKILL.md +2 -2
- package/pack/generated/codex/skills/devrites-frontend-craft/reference/design-references.md +18 -18
- package/pack/generated/codex/skills/devrites-frontend-craft/reference/quality-standards.md +6 -4
- package/pack/generated/codex/skills/devrites-interview/SKILL.md +4 -4
- package/pack/generated/codex/skills/devrites-lib/reference/parallel-dispatch.md +1 -1
- package/pack/generated/codex/skills/devrites-lib/reference/standards/afk-hitl.md +9 -1
- package/pack/generated/codex/skills/devrites-lib/reference/standards/agents.md +3 -2
- package/pack/generated/codex/skills/devrites-lib/reference/standards/gates.md +6 -5
- package/pack/generated/codex/skills/devrites-lib/reference/standards/spec-grammar.md +1 -1
- package/pack/generated/codex/skills/devrites-lib/reference/standards/verification-methods.md +3 -3
- package/pack/generated/codex/skills/devrites-lib/reference/standards/windows.md +3 -3
- package/pack/generated/codex/skills/devrites-lib/reference/standards/workflow-artifacts.md +2 -2
- package/pack/generated/codex/skills/devrites-lib/reference/visual-playbooks/outline-template.md +1 -1
- package/pack/generated/codex/skills/devrites-lib/reference/workspace-artifact-schema.md +4 -4
- package/pack/generated/codex/skills/devrites-ux-shape/SKILL.md +11 -12
- package/pack/generated/codex/skills/devrites-ux-shape/reference/visual-direction-probe.md +3 -1
- package/pack/generated/codex/skills/overhaul/SKILL.md +1 -1
- package/pack/generated/codex/skills/overhaul/references/orchestration.md +5 -3
- package/pack/generated/codex/skills/overhaul/references/records.md +39 -0
- package/pack/generated/codex/skills/overhaul/references/scoring.md +14 -4
- package/pack/generated/codex/skills/rite/reference/menu.md +1 -1
- package/pack/generated/codex/skills/rite-adopt/SKILL.md +8 -2
- package/pack/generated/codex/skills/rite-autocomplete/SKILL.md +11 -6
- package/pack/generated/codex/skills/rite-autocomplete/reference/decision-policy.md +3 -2
- package/pack/generated/codex/skills/rite-autocomplete/reference/loop.md +1 -1
- package/pack/generated/codex/skills/rite-autocomplete/reference/stop-conditions.md +6 -3
- package/pack/generated/codex/skills/rite-build/SKILL.md +2 -1
- package/pack/generated/codex/skills/rite-build/reference/afk-discipline.md +1 -1
- package/pack/generated/codex/skills/rite-build/reference/checkpoint-protocol.md +1 -1
- package/pack/generated/codex/skills/rite-build/reference/checkpoint.md +5 -0
- package/pack/generated/codex/skills/rite-build/reference/parallel-batch.md +2 -2
- package/pack/generated/codex/skills/rite-build/reference/wright-dispatch.md +3 -2
- package/pack/generated/codex/skills/rite-clarify/SKILL.md +5 -4
- package/pack/generated/codex/skills/rite-clarify/reference/anti-patterns.md +2 -2
- package/pack/generated/codex/skills/rite-clarify/reference/decision-coverage.md +3 -2
- package/pack/generated/codex/skills/rite-converge/SKILL.md +2 -0
- package/pack/generated/codex/skills/rite-customize/SKILL.md +6 -4
- package/pack/generated/codex/skills/rite-define/SKILL.md +2 -0
- package/pack/generated/codex/skills/rite-define/reference/gates.md +8 -7
- package/pack/generated/codex/skills/rite-explain/SKILL.md +1 -1
- package/pack/generated/codex/skills/rite-explain/reference/intake.md +1 -1
- package/pack/generated/codex/skills/rite-fast/SKILL.md +1 -1
- package/pack/generated/codex/skills/rite-plan/SKILL.md +4 -3
- package/pack/generated/codex/skills/rite-plan/reference/replan-and-repair.md +4 -4
- package/pack/generated/codex/skills/rite-polish/reference/anti-ai-slop.md +1 -1
- package/pack/generated/codex/skills/rite-prove/reference/browser-proof.md +5 -5
- package/pack/generated/codex/skills/rite-resolve/SKILL.md +3 -3
- package/pack/generated/codex/skills/rite-seal/SKILL.md +4 -3
- package/pack/generated/codex/skills/rite-seal/reference/phase-contract.md +3 -2
- package/pack/generated/codex/skills/rite-seal/reference/seal-template.md +2 -0
- package/pack/generated/codex/skills/rite-ship/SKILL.md +4 -2
- package/pack/generated/codex/skills/rite-ship/reference/git-ship.md +8 -2
- package/pack/generated/codex/skills/rite-ship/reference/ship-template.md +2 -1
- package/pack/generated/codex/skills/rite-spec/SKILL.md +2 -2
- package/pack/generated/codex/skills/rite-spec/reference/spec-checklists.md +1 -1
- package/pack/generated/codex/skills/rite-spec/reference/spec-template.md +7 -3
- package/pack/generated/codex/skills/rite-spec/reference/state-workspace.md +2 -0
- package/pack/generated/codex/skills/rite-temper/SKILL.md +12 -9
- package/pack/generated/codex/skills/rite-temper/reference/scope-modes.md +1 -1
- package/pack/generated/codex/skills/rite-temper/reference/significance.md +10 -3
- package/pack/generated/codex/skills/rite-temper/reference/strategy-template.md +13 -2
- package/pack/generated/codex/skills/rite-vet/SKILL.md +4 -3
- package/pack/generated/codex/skills/rite-vet/reference/artifacts.md +1 -1
- package/pack/generated/devin/AGENTS.md +1 -1
- package/pack/generated/devin/agents/devrites-spec-reviewer.md +2 -2
- package/pack/generated/devin/skills/devrites-browser-proof/SKILL.md +2 -2
- package/pack/generated/devin/skills/devrites-frontend-craft/reference/design-references.md +18 -18
- package/pack/generated/devin/skills/devrites-frontend-craft/reference/quality-standards.md +6 -4
- package/pack/generated/devin/skills/devrites-interview/SKILL.md +4 -4
- package/pack/generated/devin/skills/devrites-lib/reference/parallel-dispatch.md +1 -1
- package/pack/generated/devin/skills/devrites-lib/reference/standards/afk-hitl.md +9 -1
- package/pack/generated/devin/skills/devrites-lib/reference/standards/agents.md +3 -2
- package/pack/generated/devin/skills/devrites-lib/reference/standards/gates.md +6 -5
- package/pack/generated/devin/skills/devrites-lib/reference/standards/spec-grammar.md +1 -1
- package/pack/generated/devin/skills/devrites-lib/reference/standards/verification-methods.md +3 -3
- package/pack/generated/devin/skills/devrites-lib/reference/standards/windows.md +3 -3
- package/pack/generated/devin/skills/devrites-lib/reference/standards/workflow-artifacts.md +2 -2
- package/pack/generated/devin/skills/devrites-lib/reference/visual-playbooks/outline-template.md +1 -1
- package/pack/generated/devin/skills/devrites-lib/reference/workspace-artifact-schema.md +4 -4
- package/pack/generated/devin/skills/devrites-ux-shape/SKILL.md +11 -12
- package/pack/generated/devin/skills/devrites-ux-shape/reference/visual-direction-probe.md +3 -1
- package/pack/generated/devin/skills/overhaul/SKILL.md +1 -1
- package/pack/generated/devin/skills/overhaul/references/orchestration.md +5 -3
- package/pack/generated/devin/skills/overhaul/references/records.md +39 -0
- package/pack/generated/devin/skills/overhaul/references/scoring.md +14 -4
- package/pack/generated/devin/skills/rite/reference/menu.md +1 -1
- package/pack/generated/devin/skills/rite-adopt/SKILL.md +8 -2
- package/pack/generated/devin/skills/rite-autocomplete/SKILL.md +11 -6
- package/pack/generated/devin/skills/rite-autocomplete/reference/decision-policy.md +3 -2
- package/pack/generated/devin/skills/rite-autocomplete/reference/loop.md +1 -1
- package/pack/generated/devin/skills/rite-autocomplete/reference/stop-conditions.md +6 -3
- package/pack/generated/devin/skills/rite-build/SKILL.md +2 -1
- package/pack/generated/devin/skills/rite-build/reference/afk-discipline.md +1 -1
- package/pack/generated/devin/skills/rite-build/reference/checkpoint-protocol.md +1 -1
- package/pack/generated/devin/skills/rite-build/reference/checkpoint.md +5 -0
- package/pack/generated/devin/skills/rite-build/reference/parallel-batch.md +2 -2
- package/pack/generated/devin/skills/rite-build/reference/wright-dispatch.md +3 -2
- package/pack/generated/devin/skills/rite-clarify/SKILL.md +5 -4
- package/pack/generated/devin/skills/rite-clarify/reference/anti-patterns.md +2 -2
- package/pack/generated/devin/skills/rite-clarify/reference/decision-coverage.md +3 -2
- package/pack/generated/devin/skills/rite-converge/SKILL.md +2 -0
- package/pack/generated/devin/skills/rite-customize/SKILL.md +6 -4
- package/pack/generated/devin/skills/rite-define/SKILL.md +2 -0
- package/pack/generated/devin/skills/rite-define/reference/gates.md +8 -7
- package/pack/generated/devin/skills/rite-explain/SKILL.md +1 -1
- package/pack/generated/devin/skills/rite-explain/reference/intake.md +1 -1
- package/pack/generated/devin/skills/rite-fast/SKILL.md +1 -1
- package/pack/generated/devin/skills/rite-plan/SKILL.md +4 -3
- package/pack/generated/devin/skills/rite-plan/reference/replan-and-repair.md +4 -4
- package/pack/generated/devin/skills/rite-polish/reference/anti-ai-slop.md +1 -1
- package/pack/generated/devin/skills/rite-prove/reference/browser-proof.md +5 -5
- package/pack/generated/devin/skills/rite-resolve/SKILL.md +3 -3
- package/pack/generated/devin/skills/rite-seal/SKILL.md +4 -3
- package/pack/generated/devin/skills/rite-seal/reference/phase-contract.md +3 -2
- package/pack/generated/devin/skills/rite-seal/reference/seal-template.md +2 -0
- package/pack/generated/devin/skills/rite-ship/SKILL.md +4 -2
- package/pack/generated/devin/skills/rite-ship/reference/git-ship.md +8 -2
- package/pack/generated/devin/skills/rite-ship/reference/ship-template.md +2 -1
- package/pack/generated/devin/skills/rite-spec/SKILL.md +2 -2
- package/pack/generated/devin/skills/rite-spec/reference/spec-checklists.md +1 -1
- package/pack/generated/devin/skills/rite-spec/reference/spec-template.md +7 -3
- package/pack/generated/devin/skills/rite-spec/reference/state-workspace.md +2 -0
- package/pack/generated/devin/skills/rite-temper/SKILL.md +12 -9
- package/pack/generated/devin/skills/rite-temper/reference/scope-modes.md +1 -1
- package/pack/generated/devin/skills/rite-temper/reference/significance.md +10 -3
- package/pack/generated/devin/skills/rite-temper/reference/strategy-template.md +13 -2
- package/pack/generated/devin/skills/rite-vet/SKILL.md +4 -3
- package/pack/generated/devin/skills/rite-vet/reference/artifacts.md +1 -1
- package/pack/generated/omp/agents/devrites-spec-reviewer.md +2 -2
- package/pack/generated/omp/commands/rite-define.md +1 -1
- package/pack/generated/omp/commands/rite-handoff.md +1 -1
- package/pack/generated/omp/commands/rite-plan.md +1 -1
- package/pack/generated/omp/commands/rite-pressure-test.md +1 -1
- package/pack/generated/omp/commands/rite-quick.md +1 -1
- package/pack/generated/omp/commands/rite-status.md +1 -1
- package/pack/generated/omp/commands/rite-temper.md +1 -1
- package/pack/generated/omp/commands/rite-zoom-out.md +1 -1
- package/pack/generated/omp/skills/devrites-browser-proof/SKILL.md +2 -2
- package/pack/generated/omp/skills/devrites-frontend-craft/reference/design-references.md +18 -18
- package/pack/generated/omp/skills/devrites-frontend-craft/reference/quality-standards.md +6 -4
- package/pack/generated/omp/skills/devrites-interview/SKILL.md +4 -4
- package/pack/generated/omp/skills/devrites-lib/reference/parallel-dispatch.md +1 -1
- package/pack/generated/omp/skills/devrites-lib/reference/standards/afk-hitl.md +9 -1
- package/pack/generated/omp/skills/devrites-lib/reference/standards/agents.md +3 -2
- package/pack/generated/omp/skills/devrites-lib/reference/standards/gates.md +6 -5
- package/pack/generated/omp/skills/devrites-lib/reference/standards/spec-grammar.md +1 -1
- package/pack/generated/omp/skills/devrites-lib/reference/standards/verification-methods.md +3 -3
- package/pack/generated/omp/skills/devrites-lib/reference/standards/windows.md +3 -3
- package/pack/generated/omp/skills/devrites-lib/reference/standards/workflow-artifacts.md +2 -2
- package/pack/generated/omp/skills/devrites-lib/reference/visual-playbooks/outline-template.md +1 -1
- package/pack/generated/omp/skills/devrites-lib/reference/workspace-artifact-schema.md +4 -4
- package/pack/generated/omp/skills/devrites-ux-shape/SKILL.md +11 -12
- package/pack/generated/omp/skills/devrites-ux-shape/reference/visual-direction-probe.md +3 -1
- package/pack/generated/omp/skills/overhaul/SKILL.md +1 -1
- package/pack/generated/omp/skills/overhaul/references/orchestration.md +5 -3
- package/pack/generated/omp/skills/overhaul/references/records.md +39 -0
- package/pack/generated/omp/skills/overhaul/references/scoring.md +14 -4
- package/pack/generated/omp/skills/rite/reference/menu.md +1 -1
- package/pack/generated/omp/skills/rite-adopt/SKILL.md +8 -2
- package/pack/generated/omp/skills/rite-autocomplete/SKILL.md +11 -6
- package/pack/generated/omp/skills/rite-autocomplete/reference/decision-policy.md +3 -2
- package/pack/generated/omp/skills/rite-autocomplete/reference/loop.md +1 -1
- package/pack/generated/omp/skills/rite-autocomplete/reference/stop-conditions.md +6 -3
- package/pack/generated/omp/skills/rite-build/SKILL.md +2 -1
- package/pack/generated/omp/skills/rite-build/reference/afk-discipline.md +1 -1
- package/pack/generated/omp/skills/rite-build/reference/checkpoint-protocol.md +1 -1
- package/pack/generated/omp/skills/rite-build/reference/checkpoint.md +5 -0
- package/pack/generated/omp/skills/rite-build/reference/parallel-batch.md +2 -2
- package/pack/generated/omp/skills/rite-build/reference/wright-dispatch.md +3 -2
- package/pack/generated/omp/skills/rite-clarify/SKILL.md +5 -4
- package/pack/generated/omp/skills/rite-clarify/reference/anti-patterns.md +2 -2
- package/pack/generated/omp/skills/rite-clarify/reference/decision-coverage.md +3 -2
- package/pack/generated/omp/skills/rite-converge/SKILL.md +2 -0
- package/pack/generated/omp/skills/rite-customize/SKILL.md +6 -4
- package/pack/generated/omp/skills/rite-define/SKILL.md +2 -0
- package/pack/generated/omp/skills/rite-define/reference/gates.md +8 -7
- package/pack/generated/omp/skills/rite-explain/SKILL.md +1 -1
- package/pack/generated/omp/skills/rite-explain/reference/intake.md +1 -1
- package/pack/generated/omp/skills/rite-fast/SKILL.md +1 -1
- package/pack/generated/omp/skills/rite-plan/SKILL.md +4 -3
- package/pack/generated/omp/skills/rite-plan/reference/replan-and-repair.md +4 -4
- package/pack/generated/omp/skills/rite-polish/reference/anti-ai-slop.md +1 -1
- package/pack/generated/omp/skills/rite-prove/reference/browser-proof.md +5 -5
- package/pack/generated/omp/skills/rite-resolve/SKILL.md +3 -3
- package/pack/generated/omp/skills/rite-seal/SKILL.md +4 -3
- package/pack/generated/omp/skills/rite-seal/reference/phase-contract.md +3 -2
- package/pack/generated/omp/skills/rite-seal/reference/seal-template.md +2 -0
- package/pack/generated/omp/skills/rite-ship/SKILL.md +4 -2
- package/pack/generated/omp/skills/rite-ship/reference/git-ship.md +8 -2
- package/pack/generated/omp/skills/rite-ship/reference/ship-template.md +2 -1
- package/pack/generated/omp/skills/rite-spec/SKILL.md +2 -2
- package/pack/generated/omp/skills/rite-spec/reference/spec-checklists.md +1 -1
- package/pack/generated/omp/skills/rite-spec/reference/spec-template.md +7 -3
- package/pack/generated/omp/skills/rite-spec/reference/state-workspace.md +2 -0
- package/pack/generated/omp/skills/rite-temper/SKILL.md +12 -9
- package/pack/generated/omp/skills/rite-temper/reference/scope-modes.md +1 -1
- package/pack/generated/omp/skills/rite-temper/reference/significance.md +10 -3
- package/pack/generated/omp/skills/rite-temper/reference/strategy-template.md +13 -2
- package/pack/generated/omp/skills/rite-vet/SKILL.md +4 -3
- package/pack/generated/omp/skills/rite-vet/reference/artifacts.md +1 -1
- package/pack/generated/pi/AGENTS.md +1 -1
- package/pack/generated/pi/agents/devrites-spec-reviewer.md +2 -2
- package/pack/generated/pi/prompts/rite-define.md +1 -1
- package/pack/generated/pi/prompts/rite-handoff.md +1 -1
- package/pack/generated/pi/prompts/rite-plan.md +1 -1
- package/pack/generated/pi/prompts/rite-pressure-test.md +1 -1
- package/pack/generated/pi/prompts/rite-quick.md +1 -1
- package/pack/generated/pi/prompts/rite-status.md +1 -1
- package/pack/generated/pi/prompts/rite-temper.md +1 -1
- package/pack/generated/pi/prompts/rite-zoom-out.md +1 -1
- package/pack/generated/pi/skills/devrites-browser-proof/SKILL.md +2 -2
- package/pack/generated/pi/skills/devrites-frontend-craft/reference/design-references.md +18 -18
- package/pack/generated/pi/skills/devrites-frontend-craft/reference/quality-standards.md +6 -4
- package/pack/generated/pi/skills/devrites-interview/SKILL.md +4 -4
- package/pack/generated/pi/skills/devrites-lib/reference/parallel-dispatch.md +1 -1
- package/pack/generated/pi/skills/devrites-lib/reference/standards/afk-hitl.md +9 -1
- package/pack/generated/pi/skills/devrites-lib/reference/standards/agents.md +3 -2
- package/pack/generated/pi/skills/devrites-lib/reference/standards/gates.md +6 -5
- package/pack/generated/pi/skills/devrites-lib/reference/standards/spec-grammar.md +1 -1
- package/pack/generated/pi/skills/devrites-lib/reference/standards/verification-methods.md +3 -3
- package/pack/generated/pi/skills/devrites-lib/reference/standards/windows.md +3 -3
- package/pack/generated/pi/skills/devrites-lib/reference/standards/workflow-artifacts.md +2 -2
- package/pack/generated/pi/skills/devrites-lib/reference/visual-playbooks/outline-template.md +1 -1
- package/pack/generated/pi/skills/devrites-lib/reference/workspace-artifact-schema.md +4 -4
- package/pack/generated/pi/skills/devrites-ux-shape/SKILL.md +11 -12
- package/pack/generated/pi/skills/devrites-ux-shape/reference/visual-direction-probe.md +3 -1
- package/pack/generated/pi/skills/overhaul/SKILL.md +1 -1
- package/pack/generated/pi/skills/overhaul/references/orchestration.md +5 -3
- package/pack/generated/pi/skills/overhaul/references/records.md +39 -0
- package/pack/generated/pi/skills/overhaul/references/scoring.md +14 -4
- package/pack/generated/pi/skills/rite/reference/menu.md +1 -1
- package/pack/generated/pi/skills/rite-adopt/SKILL.md +8 -2
- package/pack/generated/pi/skills/rite-autocomplete/SKILL.md +11 -6
- package/pack/generated/pi/skills/rite-autocomplete/reference/decision-policy.md +3 -2
- package/pack/generated/pi/skills/rite-autocomplete/reference/loop.md +1 -1
- package/pack/generated/pi/skills/rite-autocomplete/reference/stop-conditions.md +6 -3
- package/pack/generated/pi/skills/rite-build/SKILL.md +2 -1
- package/pack/generated/pi/skills/rite-build/reference/afk-discipline.md +1 -1
- package/pack/generated/pi/skills/rite-build/reference/checkpoint-protocol.md +1 -1
- package/pack/generated/pi/skills/rite-build/reference/checkpoint.md +5 -0
- package/pack/generated/pi/skills/rite-build/reference/parallel-batch.md +2 -2
- package/pack/generated/pi/skills/rite-build/reference/wright-dispatch.md +3 -2
- package/pack/generated/pi/skills/rite-clarify/SKILL.md +5 -4
- package/pack/generated/pi/skills/rite-clarify/reference/anti-patterns.md +2 -2
- package/pack/generated/pi/skills/rite-clarify/reference/decision-coverage.md +3 -2
- package/pack/generated/pi/skills/rite-converge/SKILL.md +2 -0
- package/pack/generated/pi/skills/rite-customize/SKILL.md +6 -4
- package/pack/generated/pi/skills/rite-define/SKILL.md +2 -0
- package/pack/generated/pi/skills/rite-define/reference/gates.md +8 -7
- package/pack/generated/pi/skills/rite-explain/SKILL.md +1 -1
- package/pack/generated/pi/skills/rite-explain/reference/intake.md +1 -1
- package/pack/generated/pi/skills/rite-fast/SKILL.md +1 -1
- package/pack/generated/pi/skills/rite-plan/SKILL.md +4 -3
- package/pack/generated/pi/skills/rite-plan/reference/replan-and-repair.md +4 -4
- package/pack/generated/pi/skills/rite-polish/reference/anti-ai-slop.md +1 -1
- package/pack/generated/pi/skills/rite-prove/reference/browser-proof.md +5 -5
- package/pack/generated/pi/skills/rite-resolve/SKILL.md +3 -3
- package/pack/generated/pi/skills/rite-seal/SKILL.md +4 -3
- package/pack/generated/pi/skills/rite-seal/reference/phase-contract.md +3 -2
- package/pack/generated/pi/skills/rite-seal/reference/seal-template.md +2 -0
- package/pack/generated/pi/skills/rite-ship/SKILL.md +4 -2
- package/pack/generated/pi/skills/rite-ship/reference/git-ship.md +8 -2
- package/pack/generated/pi/skills/rite-ship/reference/ship-template.md +2 -1
- package/pack/generated/pi/skills/rite-spec/SKILL.md +2 -2
- package/pack/generated/pi/skills/rite-spec/reference/spec-checklists.md +1 -1
- package/pack/generated/pi/skills/rite-spec/reference/spec-template.md +7 -3
- package/pack/generated/pi/skills/rite-spec/reference/state-workspace.md +2 -0
- package/pack/generated/pi/skills/rite-temper/SKILL.md +12 -9
- package/pack/generated/pi/skills/rite-temper/reference/scope-modes.md +1 -1
- package/pack/generated/pi/skills/rite-temper/reference/significance.md +10 -3
- package/pack/generated/pi/skills/rite-temper/reference/strategy-template.md +13 -2
- package/pack/generated/pi/skills/rite-vet/SKILL.md +4 -3
- package/pack/generated/pi/skills/rite-vet/reference/artifacts.md +1 -1
- package/package.json +5 -4
- package/scripts/build-host-artifacts.sh +15 -0
- package/scripts/codex-generate.sh +1 -1
- package/scripts/devin-generate.sh +1 -1
- package/scripts/expand-includes.py +46 -0
- package/scripts/install-lib.sh +42 -7
- package/scripts/omp-generate.sh +11 -0
- package/scripts/pi-generate.sh +12 -1
- package/update.sh +1 -2
- package/engine/testdata/fixtures/basic/devrites-root/work/auth-tokens/README.md +0 -7
- package/engine/testdata/fixtures/basic/devrites-root/work/auth-tokens/architecture.md +0 -3
- package/engine/testdata/fixtures/basic/devrites-root/work/auth-tokens/assumptions.md +0 -3
- package/engine/testdata/fixtures/basic/devrites-root/work/auth-tokens/brief.md +0 -3
- package/engine/testdata/fixtures/basic/devrites-root/work/auth-tokens/decision-coverage.md +0 -3
- package/engine/testdata/fixtures/basic/devrites-root/work/auth-tokens/decisions.md +0 -4
- package/engine/testdata/fixtures/basic/devrites-root/work/auth-tokens/eng-review.md +0 -3
- package/engine/testdata/fixtures/basic/devrites-root/work/auth-tokens/gates.md +0 -4
- package/engine/testdata/fixtures/basic/devrites-root/work/auth-tokens/plan.md +0 -5
- package/engine/testdata/fixtures/basic/devrites-root/work/auth-tokens/questions.md +0 -3
- package/engine/testdata/fixtures/basic/devrites-root/work/auth-tokens/spec.md +0 -6
- package/engine/testdata/fixtures/basic/devrites-root/work/auth-tokens/state.md +0 -8
- package/engine/testdata/fixtures/basic/devrites-root/work/auth-tokens/tasks.md +0 -1
- package/engine/testdata/fixtures/basic/devrites-root/work/auth-tokens/test-plan.md +0 -3
- package/engine/testdata/fixtures/basic/devrites-root/work/auth-tokens/traceability.md +0 -3
- package/engine/testdata/fixtures/basic/devrites-root/work/search-ranking/README.md +0 -7
- package/engine/testdata/fixtures/basic/devrites-root/work/search-ranking/assumptions.md +0 -3
- package/engine/testdata/fixtures/basic/devrites-root/work/search-ranking/brief.md +0 -3
- package/engine/testdata/fixtures/basic/devrites-root/work/search-ranking/decisions.md +0 -3
- package/engine/testdata/fixtures/basic/devrites-root/work/search-ranking/questions.md +0 -3
- package/engine/testdata/fixtures/basic/devrites-root/work/search-ranking/spec.md +0 -6
- package/engine/testdata/fixtures/basic/devrites-root/work/search-ranking/state.md +0 -3
- package/engine/testdata/golden/TestParityCloseOut/active_pointed_elsewhere.golden +0 -2
- package/engine/testdata/golden/TestParityCloseOut/active_slug_cleared.golden +0 -2
- package/engine/testdata/golden/TestParityCloseOut/archive_clobber.golden +0 -1
- package/engine/testdata/golden/TestParityCloseOut/missing_slug.golden +0 -1
- package/engine/testdata/golden/TestParityCloseOut/missing_workspace.golden +0 -1
- package/engine/testdata/golden/TestParityLearnings/add.golden +0 -2
- package/engine/testdata/golden/TestParityLearnings/list.golden +0 -5
- package/engine/testdata/golden/TestParityLearnings/mine-no-archive.golden +0 -2
- package/engine/testdata/golden/TestParityLearnings/mine.golden +0 -5
- package/engine/testdata/golden/TestParityLearnings/nudge.golden +0 -2
- package/engine/testdata/golden/TestParityLearnings/unknown.golden +0 -1
- package/engine/testdata/golden/TestParityPackageExistence/ghost-workspace.golden +0 -1
- package/engine/testdata/golden/TestParityPackageExistence/no-manifest.golden +0 -1
- package/engine/testdata/golden/TestParityPackageExistence/not-git.golden +0 -1
- package/engine/testdata/golden/TestParityPackageExistence/undeclared.golden +0 -1
- package/engine/testdata/golden/TestParityPreamble/afk/a.golden +0 -14
- package/engine/testdata/golden/TestParityPreamble/hitl/active.golden +0 -18
- package/engine/testdata/golden/TestParityPreamble/hitl/bare.golden +0 -10
- package/engine/testdata/golden/TestParityPreamble/hitl/full.golden +0 -18
- package/engine/testdata/golden/TestParityPreamble/hitl/ghost.golden +0 -2
- package/engine/testdata/golden/TestParityPreamble/hitl/nonl.golden +0 -11
- package/engine/testdata/golden/TestParityPreamble/hitl/openend.golden +0 -13
- package/engine/testdata/golden/TestParityProgress/arg=allbuilt.golden +0 -4
- package/engine/testdata/golden/TestParityProgress/arg=done.golden +0 -3
- package/engine/testdata/golden/TestParityProgress/arg=ghost.golden +0 -1
- package/engine/testdata/golden/TestParityProgress/arg=mid.golden +0 -4
- package/engine/testdata/golden/TestParityProgress/arg=nophase.golden +0 -3
- package/engine/testdata/golden/TestParityProgress/arg=noslice.golden +0 -3
- package/engine/testdata/golden/TestParityProgress/arg=plan.golden +0 -4
- package/engine/testdata/golden/TestParityProgress/arg=seal.golden +0 -3
- package/engine/testdata/golden/TestParityResolve/answer/questions.md.golden +0 -9
- package/engine/testdata/golden/TestParityResolve/answer/state.md.golden +0 -7
- package/engine/testdata/golden/TestParityResolve/answer.golden +0 -4
- package/engine/testdata/golden/TestParityResolve/batch/questions.md.golden +0 -9
- package/engine/testdata/golden/TestParityResolve/batch-unterminated/questions.md.golden +0 -9
- package/engine/testdata/golden/TestParityResolve/batch-unterminated.golden +0 -2
- package/engine/testdata/golden/TestParityResolve/batch.golden +0 -3
- package/engine/testdata/golden/TestParityResolve/drop/questions.md.golden +0 -9
- package/engine/testdata/golden/TestParityResolve/drop/state.md.golden +0 -9
- package/engine/testdata/golden/TestParityResolve/drop.golden +0 -4
- package/engine/testdata/golden/TestParityResolve/missing-answer.golden +0 -1
- package/engine/testdata/golden/TestParityResolve/no-workspace.golden +0 -1
- package/engine/testdata/golden/TestParityResolve/not-found.golden +0 -1
- package/engine/testdata/golden/TestParityResolve/not-open.golden +0 -1
- package/engine/testdata/golden/TestStatusLiveWorkspace.golden +0 -10
- package/engine/testdata/golden/status/auth-tokens.txt +0 -9
- package/engine/testdata/golden/status/search-ranking.txt +0 -9
- package/engine/testdata/visual/open-visual-smoke.html +0 -675
- package/engine/testdata/visual/open-visual-smoke.outline.md +0 -54
- package/engine/tests/adr_0003_gate_exit_code_test.go +0 -32
- package/engine/tests/adr_0004_required_by_phase_test.go +0 -54
- package/engine/tests/adr_0011_define_plan_test.go +0 -32
- package/engine/tests/adr_0027_readiness_binding_test.go +0 -107
- package/engine/tests/cli_test.go +0 -126
- package/engine/tests/gate_test.go +0 -669
- package/engine/tests/meta_test.go +0 -37
- package/engine/tests/parity_closeout_test.go +0 -105
- package/engine/tests/parity_githelpers_test.go +0 -41
- package/engine/tests/parity_resolve_test.go +0 -136
- package/engine/tests/parity_test.go +0 -86
- package/engine/tests/workspace_observation_migration_test.go +0 -1069
|
@@ -190,9 +190,16 @@ func (r *runner) recheckPath(rel string) error {
|
|
|
190
190
|
if now != before {
|
|
191
191
|
return fmt.Errorf("refusing to change %s: file changed after preflight; retry the operation", rel)
|
|
192
192
|
}
|
|
193
|
+
if afterRecheckPath != nil {
|
|
194
|
+
afterRecheckPath(rel)
|
|
195
|
+
}
|
|
193
196
|
return nil
|
|
194
197
|
}
|
|
195
198
|
|
|
199
|
+
// afterRecheckPath lets tests swap a path component in the window between the
|
|
200
|
+
// recheck and the write that follows it.
|
|
201
|
+
var afterRecheckPath func(rel string)
|
|
202
|
+
|
|
196
203
|
func inspectManagedPath(target, rel string) (pathSnapshot, error) {
|
|
197
204
|
native := filepath.FromSlash(rel)
|
|
198
205
|
if !filepath.IsLocal(native) {
|
|
@@ -4,6 +4,7 @@ import (
|
|
|
4
4
|
"fmt"
|
|
5
5
|
"os"
|
|
6
6
|
"path/filepath"
|
|
7
|
+
"strings"
|
|
7
8
|
|
|
8
9
|
"github.com/devrites/devrites/internal/hostpack"
|
|
9
10
|
)
|
|
@@ -28,12 +29,18 @@ func (r *runner) uninstall() error {
|
|
|
28
29
|
return err
|
|
29
30
|
}
|
|
30
31
|
|
|
32
|
+
// Per-path would-be content for a dry run; a nil value means the file
|
|
33
|
+
// would be removed.
|
|
34
|
+
dryTree := map[string][]byte{}
|
|
31
35
|
for _, rel := range entries {
|
|
32
36
|
merge, ok := hostpack.ManagedMergeForMarker(rel)
|
|
33
37
|
if !ok {
|
|
34
38
|
continue
|
|
35
39
|
}
|
|
36
40
|
if r.opts.DryRun {
|
|
41
|
+
if err := dryRunStripMarker(dryTree, r.target, merge); err != nil {
|
|
42
|
+
return err
|
|
43
|
+
}
|
|
37
44
|
fmt.Fprintf(r.opts.Stdout, " [merge-remove] %s\n", merge.DryRun)
|
|
38
45
|
continue
|
|
39
46
|
}
|
|
@@ -44,7 +51,7 @@ func (r *runner) uninstall() error {
|
|
|
44
51
|
return err
|
|
45
52
|
}
|
|
46
53
|
if merge.MarkerRel == hostpack.LegacyCodexHooksMerge.MarkerRel {
|
|
47
|
-
if err :=
|
|
54
|
+
if err := r.stripHooksConfined(filepath.Join(r.target, filepath.FromSlash(merge.TargetRel))); err != nil {
|
|
48
55
|
return fmt.Errorf("strip hooks from %s: %w", merge.TargetRel, err)
|
|
49
56
|
}
|
|
50
57
|
continue
|
|
@@ -55,7 +62,7 @@ func (r *runner) uninstall() error {
|
|
|
55
62
|
}
|
|
56
63
|
continue
|
|
57
64
|
}
|
|
58
|
-
if err :=
|
|
65
|
+
if err := r.stripMarkerConfined(filepath.Join(r.target, filepath.FromSlash(merge.TargetRel)), merge.Begin, merge.End); err != nil {
|
|
59
66
|
return fmt.Errorf("strip marker block from %s: %w", merge.TargetRel, err)
|
|
60
67
|
}
|
|
61
68
|
// The Codex and pi AGENTS.md blocks share one target; record the strip
|
|
@@ -82,7 +89,7 @@ func (r *runner) uninstall() error {
|
|
|
82
89
|
if err := r.recheckPath(rel); err != nil {
|
|
83
90
|
return err
|
|
84
91
|
}
|
|
85
|
-
if err :=
|
|
92
|
+
if err := r.removeConfined(dest); err != nil && !os.IsNotExist(err) {
|
|
86
93
|
return fmt.Errorf("remove %s: %w", rel, err)
|
|
87
94
|
}
|
|
88
95
|
}
|
|
@@ -98,7 +105,7 @@ func (r *runner) uninstall() error {
|
|
|
98
105
|
if err := r.recheckPath(ManifestName); err != nil {
|
|
99
106
|
return err
|
|
100
107
|
}
|
|
101
|
-
_ =
|
|
108
|
+
_ = r.removeConfined(mf)
|
|
102
109
|
dirs = append(dirs, filepath.Dir(mf))
|
|
103
110
|
for _, d := range dirs {
|
|
104
111
|
pruneEmptyDirs(d, r.target)
|
|
@@ -125,3 +132,35 @@ func (r *runner) uninstall() error {
|
|
|
125
132
|
}
|
|
126
133
|
return nil
|
|
127
134
|
}
|
|
135
|
+
|
|
136
|
+
// dryRunStripMarker runs the real marker strip against an in-memory view of
|
|
137
|
+
// the target (tree), so a dry run follows the same sequence as the real
|
|
138
|
+
// uninstall and reports the same errors without writing anything.
|
|
139
|
+
func dryRunStripMarker(tree map[string][]byte, target string, merge hostpack.ManagedMerge) error {
|
|
140
|
+
if merge.MarkerRel == hostpack.LegacyCodexHooksMerge.MarkerRel || merge.TargetRel == hostpack.ClaudeSettingsMerge.TargetRel {
|
|
141
|
+
return nil
|
|
142
|
+
}
|
|
143
|
+
path := filepath.Join(target, filepath.FromSlash(merge.TargetRel))
|
|
144
|
+
data, seen := tree[path]
|
|
145
|
+
if !seen {
|
|
146
|
+
var err error
|
|
147
|
+
// #nosec G304 -- managed file path from the install manifest
|
|
148
|
+
if data, err = os.ReadFile(path); err != nil {
|
|
149
|
+
if os.IsNotExist(err) {
|
|
150
|
+
return nil
|
|
151
|
+
}
|
|
152
|
+
return fmt.Errorf("strip marker block from %s: read %s: %w", merge.TargetRel, path, err)
|
|
153
|
+
}
|
|
154
|
+
} else if data == nil {
|
|
155
|
+
return nil
|
|
156
|
+
}
|
|
157
|
+
next, err := hostpack.StripMarkerBlock(data, merge.Begin, merge.End)
|
|
158
|
+
if err != nil {
|
|
159
|
+
return fmt.Errorf("strip marker block from %s: %w in %s; fix it and rerun", merge.TargetRel, err, path)
|
|
160
|
+
}
|
|
161
|
+
if strings.TrimSpace(string(next)) == "" {
|
|
162
|
+
next = nil
|
|
163
|
+
}
|
|
164
|
+
tree[path] = next
|
|
165
|
+
return nil
|
|
166
|
+
}
|
|
@@ -29,6 +29,9 @@ const (
|
|
|
29
29
|
claimDefaultTTL = 30
|
|
30
30
|
claimMinTTL = 1
|
|
31
31
|
claimMaxTTL = 240
|
|
32
|
+
// claimMaxLine bounds one ledger line; the reader's scanner cannot
|
|
33
|
+
// return a longer one.
|
|
34
|
+
claimMaxLine = 1 << 20
|
|
32
35
|
)
|
|
33
36
|
|
|
34
37
|
const claimUsage = `usage: devrites-engine claim <add|release|list|check> ...
|
|
@@ -86,7 +89,7 @@ func readClaims(path string) (latest map[string]*claimRecord, order []string, ba
|
|
|
86
89
|
}
|
|
87
90
|
defer func() { _ = f.Close() }()
|
|
88
91
|
scanner := bufio.NewScanner(f)
|
|
89
|
-
scanner.Buffer(make([]byte, 64<<10),
|
|
92
|
+
scanner.Buffer(make([]byte, 64<<10), claimMaxLine)
|
|
90
93
|
for scanner.Scan() {
|
|
91
94
|
line := strings.TrimSpace(scanner.Text())
|
|
92
95
|
if line == "" {
|
|
@@ -103,7 +106,10 @@ func readClaims(path string) (latest map[string]*claimRecord, order []string, ba
|
|
|
103
106
|
r := rec
|
|
104
107
|
latest[rec.ID] = &r
|
|
105
108
|
}
|
|
106
|
-
|
|
109
|
+
if err := scanner.Err(); err != nil {
|
|
110
|
+
return nil, nil, 0, fmt.Errorf("read %s: %w (a line may exceed %d bytes; repair or remove the oversized line by hand)", path, err, claimMaxLine)
|
|
111
|
+
}
|
|
112
|
+
return latest, order, bad, nil
|
|
107
113
|
}
|
|
108
114
|
|
|
109
115
|
func liveClaims(latest map[string]*claimRecord, order []string, now time.Time) []*claimRecord {
|
|
@@ -127,6 +133,9 @@ func appendClaim(path string, rec *claimRecord) error {
|
|
|
127
133
|
if err != nil {
|
|
128
134
|
return err
|
|
129
135
|
}
|
|
136
|
+
if len(line) >= claimMaxLine {
|
|
137
|
+
return fmt.Errorf("claim record is %d bytes once encoded, limit is %d; shorten --reason", len(line), claimMaxLine)
|
|
138
|
+
}
|
|
130
139
|
f, err := os.OpenFile(path, os.O_APPEND|os.O_CREATE|os.O_WRONLY, 0o600) // #nosec G304 -- path is the validated project claims ledger
|
|
131
140
|
if err != nil {
|
|
132
141
|
return err
|
|
@@ -198,6 +207,10 @@ func claimConflicts(live []*claimRecord, session string, paths []string) []*clai
|
|
|
198
207
|
return out
|
|
199
208
|
}
|
|
200
209
|
|
|
210
|
+
// An unreadable line may be a live claim, so the conflict gates refuse to
|
|
211
|
+
// answer rather than report a path free.
|
|
212
|
+
const unparsedLedgerMsg = "claim: %d unparsed ledger line(s); cannot rule out a conflict, repair the claims ledger\n"
|
|
213
|
+
|
|
201
214
|
func printConflicts(w io.Writer, conflicts []*claimRecord) {
|
|
202
215
|
for _, rec := range conflicts {
|
|
203
216
|
fmt.Fprintf(w, "claim: BLOCKED: %s held by session %q until %s", strings.Join(rec.Paths, ","), rec.Session, rec.ExpiresAt)
|
|
@@ -264,11 +277,15 @@ func claimAdd(root string, args []string, stdout, stderr io.Writer) int {
|
|
|
264
277
|
}
|
|
265
278
|
|
|
266
279
|
func claimAddLocked(path, session, reason string, ttl int, paths []string, stdout, stderr io.Writer) int {
|
|
267
|
-
latest, order,
|
|
280
|
+
latest, order, bad, err := readClaims(path)
|
|
268
281
|
if err != nil {
|
|
269
282
|
fmt.Fprintf(stderr, "claim: %v\n", err)
|
|
270
283
|
return 2
|
|
271
284
|
}
|
|
285
|
+
if bad > 0 {
|
|
286
|
+
fmt.Fprintf(stderr, unparsedLedgerMsg, bad)
|
|
287
|
+
return 2
|
|
288
|
+
}
|
|
272
289
|
now := time.Now().UTC()
|
|
273
290
|
conflicts := claimConflicts(liveClaims(latest, order, now), session, paths)
|
|
274
291
|
if len(conflicts) > 0 {
|
|
@@ -411,11 +428,15 @@ func claimCheck(root string, args []string, stdout, stderr io.Writer) int {
|
|
|
411
428
|
fmt.Fprintf(stderr, "claim: %v\n", err)
|
|
412
429
|
return 2
|
|
413
430
|
}
|
|
414
|
-
latest, order,
|
|
431
|
+
latest, order, bad, err := readClaims(claimsPath(root))
|
|
415
432
|
if err != nil {
|
|
416
433
|
fmt.Fprintf(stderr, "claim: %v\n", err)
|
|
417
434
|
return 2
|
|
418
435
|
}
|
|
436
|
+
if bad > 0 {
|
|
437
|
+
fmt.Fprintf(stderr, unparsedLedgerMsg, bad)
|
|
438
|
+
return 2
|
|
439
|
+
}
|
|
419
440
|
conflicts := claimConflicts(liveClaims(latest, order, time.Now().UTC()), session, paths)
|
|
420
441
|
if len(conflicts) > 0 {
|
|
421
442
|
printConflicts(stdout, conflicts)
|
|
@@ -183,6 +183,35 @@ func TestClaimLedgerToleratesMalformedLines(t *testing.T) {
|
|
|
183
183
|
}
|
|
184
184
|
}
|
|
185
185
|
|
|
186
|
+
func TestClaimCheckFailsClosedOnUnparsedLine(t *testing.T) {
|
|
187
|
+
root := claimTestRoot(t)
|
|
188
|
+
path := filepath.Join(root, ClaimsFile)
|
|
189
|
+
full, err := json.Marshal(&claimRecord{
|
|
190
|
+
ID: "cl-1", Session: "s-1", Paths: []string{"b.txt"},
|
|
191
|
+
ClaimedAt: "2026-01-01T00:00:00Z",
|
|
192
|
+
ExpiresAt: time.Now().Add(time.Hour).UTC().Format(time.RFC3339),
|
|
193
|
+
})
|
|
194
|
+
if err != nil {
|
|
195
|
+
t.Fatal(err)
|
|
196
|
+
}
|
|
197
|
+
truncated := string(full[:len(full)-12]) + "\n"
|
|
198
|
+
if err := os.WriteFile(path, []byte(truncated), 0o600); err != nil {
|
|
199
|
+
t.Fatal(err)
|
|
200
|
+
}
|
|
201
|
+
latest, order, bad, err := readClaims(path)
|
|
202
|
+
if err != nil || bad != 1 || len(liveClaims(latest, order, time.Now())) != 0 {
|
|
203
|
+
t.Fatalf("premise: want 1 unparsed line and no live claims, got bad=%d live=%d err=%v", bad, len(liveClaims(latest, order, time.Now())), err)
|
|
204
|
+
}
|
|
205
|
+
code, out, errOut := runClaim(t, root, "check", "--session", "s-2", "b.txt")
|
|
206
|
+
if code == 0 || !strings.Contains(out+errOut, "unparsed") {
|
|
207
|
+
t.Fatalf("check over unreadable ledger must fail closed: code=%d out=%q err=%q", code, out, errOut)
|
|
208
|
+
}
|
|
209
|
+
code, out, errOut = runClaim(t, root, "add", "--session", "s-2", "b.txt")
|
|
210
|
+
if code == 0 || !strings.Contains(out+errOut, "unparsed") {
|
|
211
|
+
t.Fatalf("add over unreadable ledger must fail closed: code=%d out=%q err=%q", code, out, errOut)
|
|
212
|
+
}
|
|
213
|
+
}
|
|
214
|
+
|
|
186
215
|
func TestClaimAddConcurrentOverlapOneWins(t *testing.T) {
|
|
187
216
|
root := claimTestRoot(t)
|
|
188
217
|
const n = 8
|
|
@@ -208,3 +237,31 @@ func TestClaimAddConcurrentOverlapOneWins(t *testing.T) {
|
|
|
208
237
|
t.Fatalf("overlapping concurrent claims: %d succeeded, want exactly 1", wins)
|
|
209
238
|
}
|
|
210
239
|
}
|
|
240
|
+
|
|
241
|
+
func TestClaimAddRejectsOversizedRecord(t *testing.T) {
|
|
242
|
+
root := claimTestRoot(t)
|
|
243
|
+
ledger := filepath.Join(root, ClaimsFile)
|
|
244
|
+
reason := strings.Repeat("<", 190000)
|
|
245
|
+
code, _, errOut := runClaim(t, root, "add", "--session", "s1", "--reason", reason, "a.txt")
|
|
246
|
+
if code != 2 {
|
|
247
|
+
t.Fatalf("oversized add should exit 2, got %d stderr=%.200s", code, errOut)
|
|
248
|
+
}
|
|
249
|
+
if info, err := os.Stat(ledger); err == nil && info.Size() != 0 {
|
|
250
|
+
t.Fatalf("oversized add wrote %d bytes to the ledger", info.Size())
|
|
251
|
+
}
|
|
252
|
+
if code, _, e := runClaim(t, root, "list"); code != 0 {
|
|
253
|
+
t.Fatalf("list after refused add: %d %s", code, e)
|
|
254
|
+
}
|
|
255
|
+
}
|
|
256
|
+
|
|
257
|
+
func TestClaimReadNamesLedgerOnOversizedLine(t *testing.T) {
|
|
258
|
+
root := claimTestRoot(t)
|
|
259
|
+
ledger := filepath.Join(root, ClaimsFile)
|
|
260
|
+
if err := os.WriteFile(ledger, []byte(strings.Repeat("x", (1<<20)+10)+"\n"), 0o600); err != nil {
|
|
261
|
+
t.Fatal(err)
|
|
262
|
+
}
|
|
263
|
+
code, _, errOut := runClaim(t, root, "list")
|
|
264
|
+
if code != 2 || !strings.Contains(errOut, ledger) || !strings.Contains(errOut, "repair") {
|
|
265
|
+
t.Fatalf("list: code=%d stderr=%q", code, errOut)
|
|
266
|
+
}
|
|
267
|
+
}
|
|
@@ -1,15 +1,27 @@
|
|
|
1
1
|
package lib
|
|
2
2
|
|
|
3
3
|
import (
|
|
4
|
+
"errors"
|
|
4
5
|
"fmt"
|
|
5
6
|
"io"
|
|
7
|
+
"io/fs"
|
|
6
8
|
"os"
|
|
7
9
|
"path/filepath"
|
|
10
|
+
"runtime"
|
|
8
11
|
|
|
9
12
|
"github.com/devrites/devrites/internal/devritespaths"
|
|
10
13
|
"github.com/devrites/devrites/internal/state"
|
|
11
14
|
)
|
|
12
15
|
|
|
16
|
+
// releaseLockBeforeRename drops the feature lock before the workspace rename on
|
|
17
|
+
// Windows, which refuses to rename a directory while the lock file inside it is
|
|
18
|
+
// open. renameWorkspace is the rename; both are variables so tests can inject the
|
|
19
|
+
// platform decision and observe the lock at the moment of the rename.
|
|
20
|
+
var (
|
|
21
|
+
releaseLockBeforeRename = runtime.GOOS == "windows"
|
|
22
|
+
renameWorkspace = os.Rename
|
|
23
|
+
)
|
|
24
|
+
|
|
13
25
|
// CloseOut retires a shipped feature: it moves the feature directory into the
|
|
14
26
|
// archive and, when the feature is still the active one, clears the ACTIVE cursor
|
|
15
27
|
// so the next /rite-spec starts clean. The workspace is relocated, never deleted:
|
|
@@ -30,8 +42,8 @@ func CloseOut(root string, args []string, stdout, stderr io.Writer) int {
|
|
|
30
42
|
|
|
31
43
|
work, err := devritespaths.ExistingFeatureDirChecked(dv, slug)
|
|
32
44
|
if err != nil {
|
|
33
|
-
if
|
|
34
|
-
fmt.Fprintf(stderr, "state close: no workspace for %s\n", slug)
|
|
45
|
+
if errors.Is(err, fs.ErrNotExist) {
|
|
46
|
+
fmt.Fprintf(stderr, "state close: no workspace for %s at %s\n", slug, filepath.Join(dv, "work", slug))
|
|
35
47
|
return 4
|
|
36
48
|
}
|
|
37
49
|
fmt.Fprintf(stderr, "state close: invalid workspace for %s: %v\n", slug, err)
|
|
@@ -62,13 +74,39 @@ func CloseOut(root string, args []string, stdout, stderr io.Writer) int {
|
|
|
62
74
|
return 5
|
|
63
75
|
}
|
|
64
76
|
|
|
77
|
+
// Hold the per-feature lock across the rename and the ACTIVE clear, as
|
|
78
|
+
// dispatch and the parallel lease ops do across their load-modify-save. A
|
|
79
|
+
// writer already holding the lock finishes its save before the rename, so
|
|
80
|
+
// that save is archived. On unix a writer queued behind this close-out
|
|
81
|
+
// acquires a lock file that now lives in the archive; state.WithFeatureLock
|
|
82
|
+
// reports state.ErrWorkspaceMoved and the writer does not run, so it cannot
|
|
83
|
+
// re-create work/<slug>. Windows cannot rename a directory that holds an
|
|
84
|
+
// open file, so there the lock is released just before the rename.
|
|
85
|
+
code := 1
|
|
86
|
+
if err := state.WithFeatureLockRelease(dv, slug, func(release func()) error {
|
|
87
|
+
code = closeOutLocked(release, work, arch, archiveDir, archDisplay, active, activeSlug, slug, stdout, stderr)
|
|
88
|
+
return nil
|
|
89
|
+
}); err != nil {
|
|
90
|
+
fmt.Fprintf(stderr, "state close: cannot lock feature %s: %v\n", slug, err)
|
|
91
|
+
return 1
|
|
92
|
+
}
|
|
93
|
+
return code
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
// closeOutLocked performs the rename and the conditional ACTIVE clear. It runs
|
|
97
|
+
// under the feature lock held by CloseOut and reports an exit code rather than
|
|
98
|
+
// an error, preserving CloseOut's documented exit codes.
|
|
99
|
+
func closeOutLocked(release func(), work, arch, archiveDir, archDisplay, active, activeSlug, slug string, stdout, stderr io.Writer) int {
|
|
65
100
|
// Fail closed: if the move does not happen, leave the cursor alone and do not
|
|
66
101
|
// claim success: a lost audit trail must never look like a clean close-out.
|
|
67
102
|
if err := os.MkdirAll(archiveDir, 0o755); err != nil {
|
|
68
103
|
fmt.Fprintf(stderr, "state close: cannot create archive dir: %v\n", err)
|
|
69
104
|
return 1
|
|
70
105
|
}
|
|
71
|
-
if
|
|
106
|
+
if releaseLockBeforeRename {
|
|
107
|
+
release()
|
|
108
|
+
}
|
|
109
|
+
if err := renameWorkspace(work, arch); err != nil {
|
|
72
110
|
fmt.Fprintf(stderr, "state close: cannot archive %s -> %s: %v\n", work, arch, err)
|
|
73
111
|
return 1
|
|
74
112
|
}
|
|
@@ -2,11 +2,15 @@ package lib
|
|
|
2
2
|
|
|
3
3
|
import (
|
|
4
4
|
"bytes"
|
|
5
|
+
"io"
|
|
5
6
|
"os"
|
|
6
7
|
"path/filepath"
|
|
7
8
|
"runtime"
|
|
8
9
|
"strings"
|
|
9
10
|
"testing"
|
|
11
|
+
"time"
|
|
12
|
+
|
|
13
|
+
"github.com/devrites/devrites/internal/state"
|
|
10
14
|
)
|
|
11
15
|
|
|
12
16
|
func writeFile(t *testing.T, path, body string) {
|
|
@@ -120,3 +124,143 @@ func TestCloseOutRollsBackArchiveWhenActiveCannotBeCleared(t *testing.T) {
|
|
|
120
124
|
t.Fatalf("ACTIVE changed after rollback: %q, %v", data, err)
|
|
121
125
|
}
|
|
122
126
|
}
|
|
127
|
+
|
|
128
|
+
// TestCloseOutSerializesRenameBehindFeatureLock holds the per-feature lock in a
|
|
129
|
+
// concurrent writer and requires close-out to wait for it. Without the lock the
|
|
130
|
+
// rename lands while the writer is mid-save, and the writer's MkdirAll re-creates
|
|
131
|
+
// work/<slug> after the workspace has been archived.
|
|
132
|
+
//
|
|
133
|
+
// The barrier is channel-based: `locked` proves the writer owns the lock before
|
|
134
|
+
// close-out starts, and `release` hands the workspace back only after close-out
|
|
135
|
+
// has stayed blocked for the bounded wait below. That wait never signals
|
|
136
|
+
// success: success is proven by the archive carrying the writer's file and
|
|
137
|
+
// work/<slug> being gone. It only bounds how long an unsynchronized rename may
|
|
138
|
+
// take to appear, so a missing lock fails fast instead of hanging.
|
|
139
|
+
func TestCloseOutSerializesRenameBehindFeatureLock(t *testing.T) {
|
|
140
|
+
root := t.TempDir()
|
|
141
|
+
writeFile(t, filepath.Join(root, "work", "feat", "state.md"), "inside\n- Schema: 4\n")
|
|
142
|
+
writeFile(t, filepath.Join(root, "ACTIVE"), "feat\n")
|
|
143
|
+
|
|
144
|
+
// dispatch.json is what a concurrent `dispatch` mid-save writes; it is
|
|
145
|
+
// written the way saveDispatch writes it, MkdirAll included, so the
|
|
146
|
+
// resurrection path is exercised rather than simulated.
|
|
147
|
+
dispatchPath := filepath.Join(root, "work", "feat", "dispatch.json")
|
|
148
|
+
locked := make(chan struct{})
|
|
149
|
+
release := make(chan struct{})
|
|
150
|
+
wrote := make(chan error, 1)
|
|
151
|
+
writerDone := make(chan error, 1)
|
|
152
|
+
go func() {
|
|
153
|
+
writerDone <- state.WithFeatureLock(root, "feat", func() error {
|
|
154
|
+
close(locked)
|
|
155
|
+
<-release
|
|
156
|
+
// Still holding the lock, as dispatch does across its load-modify-save.
|
|
157
|
+
if err := os.MkdirAll(filepath.Dir(dispatchPath), 0o755); err != nil {
|
|
158
|
+
wrote <- err
|
|
159
|
+
return err
|
|
160
|
+
}
|
|
161
|
+
wrote <- state.AtomicWrite(dispatchPath, []byte(`{"phase":"start"}`), 0o600)
|
|
162
|
+
return nil
|
|
163
|
+
})
|
|
164
|
+
}()
|
|
165
|
+
<-locked
|
|
166
|
+
|
|
167
|
+
closeDone := make(chan int, 1)
|
|
168
|
+
go func() { closeDone <- CloseOut(root, []string{"feat"}, io.Discard, io.Discard) }()
|
|
169
|
+
|
|
170
|
+
select {
|
|
171
|
+
case code := <-closeDone:
|
|
172
|
+
t.Fatalf("state close returned %d while an in-process writer held %s: "+
|
|
173
|
+
"the rename is not serialized behind the feature lock, so a concurrent "+
|
|
174
|
+
"writer can re-create work/feat after the workspace has been archived",
|
|
175
|
+
code, filepath.Join("work", "feat", ".lock"))
|
|
176
|
+
case <-time.After(750 * time.Millisecond):
|
|
177
|
+
// Still blocked on the lock: correct.
|
|
178
|
+
}
|
|
179
|
+
|
|
180
|
+
// Release the writer only once close-out is demonstrably parked, then let the
|
|
181
|
+
// writer's save land. With the lock held across the rename, the rename cannot
|
|
182
|
+
// have happened yet, so this file is archived rather than left behind.
|
|
183
|
+
close(release)
|
|
184
|
+
if err := <-wrote; err != nil {
|
|
185
|
+
t.Fatalf("concurrent dispatch save: %v", err)
|
|
186
|
+
}
|
|
187
|
+
if err := <-writerDone; err != nil {
|
|
188
|
+
t.Fatalf("concurrent writer lock: %v", err)
|
|
189
|
+
}
|
|
190
|
+
|
|
191
|
+
var code int
|
|
192
|
+
select {
|
|
193
|
+
case code = <-closeDone:
|
|
194
|
+
case <-time.After(10 * time.Second):
|
|
195
|
+
t.Fatal("state close did not finish after the feature lock was released")
|
|
196
|
+
}
|
|
197
|
+
if code != 0 {
|
|
198
|
+
t.Fatalf("state close = %d, want 0 after the concurrent writer released the lock\n", code)
|
|
199
|
+
}
|
|
200
|
+
if !isFile(filepath.Join(root, "archive", "feat", "dispatch.json")) {
|
|
201
|
+
t.Fatal("concurrent dispatch.json was not archived: the rename did not run after the writer's save")
|
|
202
|
+
}
|
|
203
|
+
if _, err := os.Lstat(filepath.Join(root, "work", "feat")); !os.IsNotExist(err) {
|
|
204
|
+
t.Fatalf("work/feat was re-created by the concurrent writer after close-out: %v", err)
|
|
205
|
+
}
|
|
206
|
+
if data, err := os.ReadFile(filepath.Join(root, "ACTIVE")); err != nil || len(data) != 0 {
|
|
207
|
+
t.Fatalf("ACTIVE = %q (%v), want cleared", data, err)
|
|
208
|
+
}
|
|
209
|
+
}
|
|
210
|
+
|
|
211
|
+
// TestCloseOutReleasesFeatureLockBeforeRenameWhenPlatformRequires injects the
|
|
212
|
+
// Windows decision and checks, from inside the rename, that the feature lock is
|
|
213
|
+
// free: another locker acquires it while the workspace still exists. With the
|
|
214
|
+
// lock still held at the rename that locker stays blocked.
|
|
215
|
+
func TestCloseOutReleasesFeatureLockBeforeRenameWhenPlatformRequires(t *testing.T) {
|
|
216
|
+
root := t.TempDir()
|
|
217
|
+
writeFile(t, filepath.Join(root, "work", "feat", "state.md"), "inside\n- Schema: 4\n")
|
|
218
|
+
writeFile(t, filepath.Join(root, "ACTIVE"), "feat\n")
|
|
219
|
+
|
|
220
|
+
origRelease, origRename := releaseLockBeforeRename, renameWorkspace
|
|
221
|
+
t.Cleanup(func() { releaseLockBeforeRename, renameWorkspace = origRelease, origRename })
|
|
222
|
+
releaseLockBeforeRename = true
|
|
223
|
+
|
|
224
|
+
var lockFreeAtRename bool
|
|
225
|
+
renameWorkspace = func(from, to string) error {
|
|
226
|
+
acquired := make(chan struct{})
|
|
227
|
+
done := make(chan error, 1)
|
|
228
|
+
go func() {
|
|
229
|
+
done <- state.WithFeatureLock(root, "feat", func() error {
|
|
230
|
+
close(acquired)
|
|
231
|
+
return nil
|
|
232
|
+
})
|
|
233
|
+
}()
|
|
234
|
+
select {
|
|
235
|
+
case <-acquired:
|
|
236
|
+
lockFreeAtRename = true
|
|
237
|
+
<-done
|
|
238
|
+
case <-time.After(750 * time.Millisecond):
|
|
239
|
+
// Still blocked: the rename is about to run with the lock held.
|
|
240
|
+
// The locker is drained after CloseOut returns.
|
|
241
|
+
t.Cleanup(func() { <-done })
|
|
242
|
+
}
|
|
243
|
+
return os.Rename(from, to)
|
|
244
|
+
}
|
|
245
|
+
|
|
246
|
+
code, out := runCloseOut(t, root, "feat")
|
|
247
|
+
if code != 0 {
|
|
248
|
+
t.Fatalf("state close = %d, want 0\n%s", code, out)
|
|
249
|
+
}
|
|
250
|
+
if !lockFreeAtRename {
|
|
251
|
+
t.Fatal("the feature lock was still held when the workspace was renamed")
|
|
252
|
+
}
|
|
253
|
+
if !isFile(filepath.Join(root, "archive", "feat", "state.md")) {
|
|
254
|
+
t.Fatal("workspace was not archived")
|
|
255
|
+
}
|
|
256
|
+
}
|
|
257
|
+
|
|
258
|
+
func TestCloseOutMissingWorkspaceNamesResolvedPath(t *testing.T) {
|
|
259
|
+
root := t.TempDir()
|
|
260
|
+
|
|
261
|
+
code, out := runCloseOut(t, root, "ghost")
|
|
262
|
+
want := filepath.Join(root, "work", "ghost")
|
|
263
|
+
if code != 4 || !strings.Contains(out, "ghost") || !strings.Contains(out, want) {
|
|
264
|
+
t.Fatalf("state close missing workspace = %d, want exit 4 naming %q\n%s", code, want, out)
|
|
265
|
+
}
|
|
266
|
+
}
|
|
@@ -152,7 +152,7 @@ func RunContext(root string, args []string, stdout, stderr io.Writer) int {
|
|
|
152
152
|
}
|
|
153
153
|
for _, rel := range workspaceList {
|
|
154
154
|
if writeContextEntry(&bundle, filepath.Join(featureDir, filepath.FromSlash(rel)),
|
|
155
|
-
"workspace/"+rel, seen) {
|
|
155
|
+
"workspace/"+rel, seen, false) {
|
|
156
156
|
loaded++
|
|
157
157
|
}
|
|
158
158
|
}
|
|
@@ -162,7 +162,7 @@ func RunContext(root string, args []string, stdout, stderr io.Writer) int {
|
|
|
162
162
|
if opts.role != "" {
|
|
163
163
|
rel := roleAgentPath(manifest, opts.role)
|
|
164
164
|
if writeContextEntry(&bundle, filepath.Join(skillsRoot, "..", "agents", filepath.FromSlash(rel)),
|
|
165
|
-
"agents/"+rel, seen) {
|
|
165
|
+
"agents/"+rel, seen, true) {
|
|
166
166
|
loaded++
|
|
167
167
|
} else {
|
|
168
168
|
// The agent contract is a pack file, not a workspace artifact: a
|
|
@@ -280,7 +280,7 @@ func expandIncludes(abs string, data []byte) []byte {
|
|
|
280
280
|
})
|
|
281
281
|
}
|
|
282
282
|
|
|
283
|
-
func writeContextEntry(bundle *strings.Builder, abs, label string, seen map[string]bool) bool {
|
|
283
|
+
func writeContextEntry(bundle *strings.Builder, abs, label string, seen map[string]bool, expand bool) bool {
|
|
284
284
|
if seen[abs] {
|
|
285
285
|
return false
|
|
286
286
|
}
|
|
@@ -291,7 +291,11 @@ func writeContextEntry(bundle *strings.Builder, abs, label string, seen map[stri
|
|
|
291
291
|
fmt.Fprintf(bundle, "MISSING: %s\n\n", label)
|
|
292
292
|
return false
|
|
293
293
|
}
|
|
294
|
-
|
|
294
|
+
if expand {
|
|
295
|
+
bundle.Write(expandIncludes(abs, data))
|
|
296
|
+
} else {
|
|
297
|
+
bundle.Write(data)
|
|
298
|
+
}
|
|
295
299
|
if len(data) > 0 && data[len(data)-1] != '\n' {
|
|
296
300
|
bundle.WriteByte('\n')
|
|
297
301
|
}
|
|
@@ -4,6 +4,7 @@ import (
|
|
|
4
4
|
"bytes"
|
|
5
5
|
"os"
|
|
6
6
|
"path/filepath"
|
|
7
|
+
"runtime"
|
|
7
8
|
"strings"
|
|
8
9
|
"sync"
|
|
9
10
|
"testing"
|
|
@@ -143,6 +144,9 @@ func TestDispatchWaveBarrier(t *testing.T) {
|
|
|
143
144
|
if c, _, _ := run("feat", "open", "--phase", "review", "--wave", "w1", "--role", "a", "--role", "b"); c != 0 {
|
|
144
145
|
t.Fatal("open failed")
|
|
145
146
|
}
|
|
147
|
+
if c, _, e := run("feat", "status"); c == 0 {
|
|
148
|
+
t.Fatalf("bare status should fail while a wave is open: %s", e)
|
|
149
|
+
}
|
|
146
150
|
if c, _, e := run("feat", "seal", "--wave", "w1"); c == 0 {
|
|
147
151
|
t.Fatalf("seal before all starts should fail: %s", e)
|
|
148
152
|
}
|
|
@@ -173,6 +177,9 @@ func TestDispatchWaveBarrier(t *testing.T) {
|
|
|
173
177
|
if c, o, _ := run("feat", "status", "--wave", "w1"); c != 0 || !strings.Contains(o.String(), "complete") {
|
|
174
178
|
t.Fatalf("status should report complete: %d %s", c, o)
|
|
175
179
|
}
|
|
180
|
+
if c, _, e := run("feat", "status"); c != 0 {
|
|
181
|
+
t.Fatalf("bare status should pass once every wave is complete: %s", e)
|
|
182
|
+
}
|
|
176
183
|
// metrics auto-recorded for start+return
|
|
177
184
|
stdout, stderr := &bytes.Buffer{}, &bytes.Buffer{}
|
|
178
185
|
if code := RunMetrics(root, []string{"summary", "feat"}, stdout, stderr); code != 0 ||
|
|
@@ -519,6 +526,75 @@ func TestDiffScopeAllowlist(t *testing.T) {
|
|
|
519
526
|
if code != 0 || !strings.Contains(stdout.String(), "2 changed") {
|
|
520
527
|
t.Fatalf("code=%d\n%s", code, stdout.String())
|
|
521
528
|
}
|
|
529
|
+
|
|
530
|
+
// A changed path whose own name contains the porcelain rename separator
|
|
531
|
+
// is checked as that full path, never as a fragment of it. Windows forbids
|
|
532
|
+
// '>' in file names, so the case cannot exist there.
|
|
533
|
+
if runtime.GOOS != "windows" {
|
|
534
|
+
sepRepo := t.TempDir()
|
|
535
|
+
initGitRepository(t, sepRepo)
|
|
536
|
+
runDiffScopeGit(t, sepRepo, "config", "user.email", "tests@example.invalid")
|
|
537
|
+
runDiffScopeGit(t, sepRepo, "config", "user.name", "DevRites Tests")
|
|
538
|
+
writeFile(t, filepath.Join(sepRepo, "docs", "secret -> notes.md"), "v1\n")
|
|
539
|
+
writeFile(t, filepath.Join(sepRepo, "docs", "secret"), "v1\n")
|
|
540
|
+
runDiffScopeGit(t, sepRepo, "add", "-A")
|
|
541
|
+
runDiffScopeGit(t, sepRepo, "commit", "-qm", "base")
|
|
542
|
+
writeFile(t, filepath.Join(sepRepo, "docs", "secret -> notes.md"), "changed out of contract\n")
|
|
543
|
+
|
|
544
|
+
stdout.Reset()
|
|
545
|
+
stderr.Reset()
|
|
546
|
+
code = RunCheckDiffScope(root, []string{"feat", "--allow", "notes.md", "--cwd", sepRepo}, stdout, stderr)
|
|
547
|
+
if code != 3 || !strings.Contains(stdout.String(), "BLOCKED: docs/secret -> notes.md outside allowlist") {
|
|
548
|
+
t.Fatalf("separator-named path checked as its tail: code=%d want 3 with the full path\n%s", code, stdout.String())
|
|
549
|
+
}
|
|
550
|
+
stdout.Reset()
|
|
551
|
+
stderr.Reset()
|
|
552
|
+
code = RunCheckDiffScope(root, []string{"feat", "--allow", "docs/secret -> notes.md", "--cwd", sepRepo}, stdout, stderr)
|
|
553
|
+
if code != 0 {
|
|
554
|
+
t.Fatalf("declared path rejected: code=%d\n%s", code, stdout.String())
|
|
555
|
+
}
|
|
556
|
+
|
|
557
|
+
// A rename reports its new name and a separator-named file reports its
|
|
558
|
+
// full name, even when both end in the same tail.
|
|
559
|
+
runDiffScopeGit(t, sepRepo, "mv", "docs/secret", "notes.md")
|
|
560
|
+
stdout.Reset()
|
|
561
|
+
stderr.Reset()
|
|
562
|
+
code = RunCheckDiffScope(root, []string{"feat", "--allow", "notes.md", "--cwd", sepRepo}, stdout, stderr)
|
|
563
|
+
if code != 3 || !strings.Contains(stdout.String(), "BLOCKED: docs/secret -> notes.md outside allowlist") ||
|
|
564
|
+
!strings.Contains(stdout.String(), "BLOCKED: docs/secret outside allowlist") {
|
|
565
|
+
t.Fatalf("rename and separator-named file not distinguishable: code=%d\n%s", code, stdout.String())
|
|
566
|
+
}
|
|
567
|
+
}
|
|
568
|
+
|
|
569
|
+
// An ordinary rename is reported by its new name, and its old name is
|
|
570
|
+
// checked against the allowlist too.
|
|
571
|
+
renameRepo := t.TempDir()
|
|
572
|
+
initGitRepository(t, renameRepo)
|
|
573
|
+
runDiffScopeGit(t, renameRepo, "config", "user.email", "tests@example.invalid")
|
|
574
|
+
runDiffScopeGit(t, renameRepo, "config", "user.name", "DevRites Tests")
|
|
575
|
+
writeFile(t, filepath.Join(renameRepo, "old.go"), "package old\n")
|
|
576
|
+
runDiffScopeGit(t, renameRepo, "add", "-A")
|
|
577
|
+
runDiffScopeGit(t, renameRepo, "commit", "-qm", "base")
|
|
578
|
+
runDiffScopeGit(t, renameRepo, "mv", "old.go", "new.go")
|
|
579
|
+
stdout.Reset()
|
|
580
|
+
stderr.Reset()
|
|
581
|
+
code = RunCheckDiffScope(root, []string{"feat", "--allow", "old.go", "--cwd", renameRepo}, stdout, stderr)
|
|
582
|
+
if code != 3 || !strings.Contains(stdout.String(), "BLOCKED: new.go outside allowlist") {
|
|
583
|
+
t.Fatalf("rename not reported by its new name: code=%d\n%s", code, stdout.String())
|
|
584
|
+
}
|
|
585
|
+
stdout.Reset()
|
|
586
|
+
stderr.Reset()
|
|
587
|
+
code = RunCheckDiffScope(root, []string{"feat", "--allow", "new.go", "--cwd", renameRepo}, stdout, stderr)
|
|
588
|
+
if code != 3 || !strings.Contains(stdout.String(), "BLOCKED: old.go outside allowlist") {
|
|
589
|
+
t.Fatalf("rename old name not checked: code=%d\n%s", code, stdout.String())
|
|
590
|
+
}
|
|
591
|
+
}
|
|
592
|
+
|
|
593
|
+
func runDiffScopeGit(t *testing.T, repo string, args ...string) {
|
|
594
|
+
t.Helper()
|
|
595
|
+
if out, err := runGitCommand(repo, nil, args...); err != nil {
|
|
596
|
+
t.Fatalf("git %v: %v\n%s", args, err, out)
|
|
597
|
+
}
|
|
522
598
|
}
|
|
523
599
|
|
|
524
600
|
func TestDispatchConcurrentReturnsCompleteWave(t *testing.T) {
|
|
@@ -572,3 +648,27 @@ func TestDispatchAbandonRecordsReason(t *testing.T) {
|
|
|
572
648
|
t.Fatalf("abandon reason not persisted: %v\n%s", err, data)
|
|
573
649
|
}
|
|
574
650
|
}
|
|
651
|
+
|
|
652
|
+
func TestContextWorkspaceIncludeMarkerStaysLiteral(t *testing.T) {
|
|
653
|
+
root, featureDir := newWorkspace(t, "build")
|
|
654
|
+
marker := "<!-- include:../../outside.txt -->"
|
|
655
|
+
writeFile(t, filepath.Join(featureDir, "spec.md"), marker+"\n")
|
|
656
|
+
writeFile(t, filepath.Join(root, "outside.txt"), "SENTINEL-OUTSIDE\n")
|
|
657
|
+
skills := newSkillsRoot(t)
|
|
658
|
+
stdout, stderr := &bytes.Buffer{}, &bytes.Buffer{}
|
|
659
|
+
code := RunContext(root, []string{"feat", "--phase", "build", "--skills-root", skills}, stdout, stderr)
|
|
660
|
+
if code != 0 {
|
|
661
|
+
t.Fatalf("code=%d\n%s%s", code, stdout, stderr)
|
|
662
|
+
}
|
|
663
|
+
bundle, err := os.ReadFile(filepath.Join(featureDir, "ctx", "build.bundle.md"))
|
|
664
|
+
if err != nil {
|
|
665
|
+
t.Fatal(err)
|
|
666
|
+
}
|
|
667
|
+
text := string(bundle)
|
|
668
|
+
if strings.Contains(text, "SENTINEL-OUTSIDE") {
|
|
669
|
+
t.Fatalf("workspace include marker was expanded:\n%s", text)
|
|
670
|
+
}
|
|
671
|
+
if !strings.Contains(text, marker) {
|
|
672
|
+
t.Fatalf("workspace include marker not kept literal:\n%s", text)
|
|
673
|
+
}
|
|
674
|
+
}
|