devrites 7.2.7 → 7.3.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 +40 -0
- package/README.md +6 -1
- package/docs/candidate-integrity.md +10 -0
- package/docs/cli.md +7 -1
- package/docs/command-map.md +9 -2
- package/docs/engine/commands.md +50 -4
- package/docs/orchestration.md +35 -13
- package/docs/skills.md +5 -0
- package/engine/internal/acceptance/cli.go +35 -30
- package/engine/internal/acceptance/preconditions.go +1 -1
- package/engine/internal/acceptance/run.go +138 -53
- package/engine/internal/acceptance/transcript.go +236 -45
- package/engine/internal/devritespaths/paths.go +23 -5
- package/engine/internal/gate/gate.go +6 -0
- package/engine/internal/install/apply.go +5 -0
- package/engine/internal/install/merge.go +48 -6
- package/engine/internal/install/preflight.go +21 -3
- package/engine/internal/install/uninstall.go +9 -10
- package/engine/internal/lib/apply_bundle.go +7 -1
- package/engine/internal/lib/candidate.go +20 -7
- package/engine/internal/lib/candidate_coverage.go +2 -19
- package/engine/internal/lib/closeout.go +22 -10
- package/engine/internal/lib/context.go +67 -47
- package/engine/internal/lib/context_io.go +171 -0
- package/engine/internal/lib/detect.go +8 -8
- package/engine/internal/lib/dispatch.go +6 -0
- package/engine/internal/lib/packet.go +37 -7
- package/engine/internal/lib/packet_freeze.go +60 -12
- package/engine/internal/lib/packet_integrity.go +179 -0
- package/engine/internal/lib/perturb.go +3 -3
- package/engine/internal/lib/resolve.go +10 -5
- package/engine/internal/lib/slicecheck.go +7 -1
- package/engine/internal/lib/state_edit.go +9 -2
- package/engine/internal/lib/taskgraph.go +20 -36
- package/engine/internal/lib/triggers.go +30 -31
- package/engine/internal/lib/workspace_pool.go +330 -0
- package/engine/internal/parallel/cli.go +190 -49
- package/engine/internal/parallel/git.go +47 -14
- package/engine/internal/parallel/landing.go +232 -0
- package/engine/internal/parallel/lease.go +97 -18
- package/engine/internal/parallel/ops.go +312 -155
- package/engine/internal/parallel/pathdisjoint.go +48 -4
- package/engine/internal/parallel/prune.go +133 -0
- package/engine/internal/parallel/repository.go +239 -0
- package/engine/internal/parallel/select.go +64 -42
- package/engine/internal/state/cursor.go +5 -5
- package/engine/main.go +2 -1
- package/pack/.claude/agents/devrites-proof-runner.md +16 -9
- package/pack/.claude/skills/devrites-lib/SKILL.md +2 -0
- package/pack/.claude/skills/devrites-lib/reference/candidate-integrity.md +7 -3
- package/pack/.claude/skills/devrites-lib/reference/parallel-dispatch.md +3 -0
- package/pack/.claude/skills/devrites-lib/reference/parallel-workspaces.md +202 -0
- package/pack/.claude/skills/devrites-lib/reference/standards/afk-hitl.md +9 -12
- package/pack/.claude/skills/devrites-lib/reference/standards/agents.md +7 -3
- package/pack/.claude/skills/devrites-lib/reference/standards/context-hygiene.md +1 -1
- package/pack/.claude/skills/devrites-lib/reference/standards/gates.md +18 -9
- package/pack/.claude/skills/devrites-lib/reference/workspace-artifact-schema.md +10 -0
- package/pack/.claude/skills/rite-autocomplete/SKILL.md +13 -10
- package/pack/.claude/skills/rite-autocomplete/reference/lookahead.md +4 -4
- package/pack/.claude/skills/rite-autocomplete/reference/loop.md +13 -9
- package/pack/.claude/skills/rite-build/SKILL.md +13 -10
- package/pack/.claude/skills/rite-build/reference/afk-discipline.md +1 -1
- package/pack/.claude/skills/rite-build/reference/checkpoint.md +1 -1
- package/pack/.claude/skills/rite-build/reference/parallel-batch.md +60 -39
- package/pack/.claude/skills/rite-build/reference/phase-contract.md +8 -5
- package/pack/.claude/skills/rite-build/reference/wright-dispatch.md +7 -7
- package/pack/.claude/skills/rite-clarify/SKILL.md +2 -2
- package/pack/.claude/skills/rite-converge/SKILL.md +4 -4
- package/pack/.claude/skills/rite-define/SKILL.md +7 -3
- package/pack/.claude/skills/rite-define/reference/plan-template.md +4 -2
- package/pack/.claude/skills/rite-plan/SKILL.md +11 -8
- package/pack/.claude/skills/rite-plan/reference/dependency-graph.md +9 -2
- package/pack/.claude/skills/rite-plan/reference/slicing.md +2 -0
- package/pack/.claude/skills/rite-polish/SKILL.md +3 -3
- package/pack/.claude/skills/rite-prove/SKILL.md +9 -5
- package/pack/.claude/skills/rite-resolve/SKILL.md +3 -3
- package/pack/.claude/skills/rite-resolve/reference/answer-protocol.md +2 -0
- package/pack/.claude/skills/rite-review/SKILL.md +4 -3
- package/pack/.claude/skills/rite-review/reference/feature-scoped-review.md +2 -2
- package/pack/.claude/skills/rite-seal/SKILL.md +1 -1
- package/pack/.claude/skills/rite-seal/reference/phase-contract.md +1 -1
- package/pack/.claude/skills/rite-ship/SKILL.md +2 -2
- package/pack/.claude/skills/rite-ship/reference/git-ship.md +2 -2
- package/pack/.claude/skills/rite-spec/SKILL.md +8 -5
- package/pack/.claude/skills/rite-spec/reference/state-workspace.md +5 -0
- package/pack/.claude/skills/rite-temper/SKILL.md +3 -3
- package/pack/.claude/skills/rite-vet/SKILL.md +9 -5
- package/pack/.claude/skills/rite-vet/reference/review-axes.md +12 -5
- package/pack/generated/claude/agents/devrites-proof-runner.md +16 -9
- package/pack/generated/claude/skills/devrites-lib/SKILL.md +2 -0
- package/pack/generated/claude/skills/devrites-lib/reference/candidate-integrity.md +7 -3
- package/pack/generated/claude/skills/devrites-lib/reference/parallel-dispatch.md +3 -0
- package/pack/generated/claude/skills/devrites-lib/reference/parallel-workspaces.md +202 -0
- package/pack/generated/claude/skills/devrites-lib/reference/standards/afk-hitl.md +9 -12
- package/pack/generated/claude/skills/devrites-lib/reference/standards/agents.md +7 -3
- package/pack/generated/claude/skills/devrites-lib/reference/standards/context-hygiene.md +1 -1
- package/pack/generated/claude/skills/devrites-lib/reference/standards/gates.md +18 -9
- package/pack/generated/claude/skills/devrites-lib/reference/workspace-artifact-schema.md +10 -0
- package/pack/generated/claude/skills/rite-autocomplete/SKILL.md +13 -10
- package/pack/generated/claude/skills/rite-autocomplete/reference/lookahead.md +4 -4
- package/pack/generated/claude/skills/rite-autocomplete/reference/loop.md +13 -9
- package/pack/generated/claude/skills/rite-build/SKILL.md +13 -10
- package/pack/generated/claude/skills/rite-build/reference/afk-discipline.md +1 -1
- package/pack/generated/claude/skills/rite-build/reference/checkpoint.md +1 -1
- package/pack/generated/claude/skills/rite-build/reference/parallel-batch.md +60 -39
- package/pack/generated/claude/skills/rite-build/reference/phase-contract.md +8 -5
- package/pack/generated/claude/skills/rite-build/reference/wright-dispatch.md +7 -7
- package/pack/generated/claude/skills/rite-clarify/SKILL.md +2 -2
- package/pack/generated/claude/skills/rite-converge/SKILL.md +4 -4
- package/pack/generated/claude/skills/rite-define/SKILL.md +7 -3
- package/pack/generated/claude/skills/rite-define/reference/plan-template.md +4 -2
- package/pack/generated/claude/skills/rite-plan/SKILL.md +11 -8
- package/pack/generated/claude/skills/rite-plan/reference/dependency-graph.md +9 -2
- package/pack/generated/claude/skills/rite-plan/reference/slicing.md +2 -0
- package/pack/generated/claude/skills/rite-polish/SKILL.md +3 -3
- package/pack/generated/claude/skills/rite-prove/SKILL.md +9 -5
- package/pack/generated/claude/skills/rite-resolve/SKILL.md +3 -3
- package/pack/generated/claude/skills/rite-resolve/reference/answer-protocol.md +2 -0
- package/pack/generated/claude/skills/rite-review/SKILL.md +4 -3
- package/pack/generated/claude/skills/rite-review/reference/feature-scoped-review.md +2 -2
- package/pack/generated/claude/skills/rite-seal/SKILL.md +1 -1
- package/pack/generated/claude/skills/rite-seal/reference/phase-contract.md +1 -1
- package/pack/generated/claude/skills/rite-ship/SKILL.md +2 -2
- package/pack/generated/claude/skills/rite-ship/reference/git-ship.md +2 -2
- package/pack/generated/claude/skills/rite-spec/SKILL.md +8 -5
- package/pack/generated/claude/skills/rite-spec/reference/state-workspace.md +5 -0
- package/pack/generated/claude/skills/rite-temper/SKILL.md +3 -3
- package/pack/generated/claude/skills/rite-vet/SKILL.md +9 -5
- package/pack/generated/claude/skills/rite-vet/reference/review-axes.md +12 -5
- package/pack/generated/codex/agents/devrites-proof-runner.toml +16 -9
- package/pack/generated/codex/skills/devrites-lib/SKILL.md +2 -0
- package/pack/generated/codex/skills/devrites-lib/reference/candidate-integrity.md +7 -3
- package/pack/generated/codex/skills/devrites-lib/reference/parallel-dispatch.md +3 -0
- package/pack/generated/codex/skills/devrites-lib/reference/parallel-workspaces.md +202 -0
- package/pack/generated/codex/skills/devrites-lib/reference/standards/afk-hitl.md +9 -12
- package/pack/generated/codex/skills/devrites-lib/reference/standards/agents.md +7 -3
- package/pack/generated/codex/skills/devrites-lib/reference/standards/context-hygiene.md +1 -1
- package/pack/generated/codex/skills/devrites-lib/reference/standards/gates.md +18 -9
- package/pack/generated/codex/skills/devrites-lib/reference/workspace-artifact-schema.md +10 -0
- package/pack/generated/codex/skills/rite-autocomplete/SKILL.md +13 -10
- package/pack/generated/codex/skills/rite-autocomplete/reference/lookahead.md +4 -4
- package/pack/generated/codex/skills/rite-autocomplete/reference/loop.md +13 -9
- package/pack/generated/codex/skills/rite-build/SKILL.md +13 -10
- package/pack/generated/codex/skills/rite-build/reference/afk-discipline.md +1 -1
- package/pack/generated/codex/skills/rite-build/reference/checkpoint.md +1 -1
- package/pack/generated/codex/skills/rite-build/reference/parallel-batch.md +60 -39
- package/pack/generated/codex/skills/rite-build/reference/phase-contract.md +8 -5
- package/pack/generated/codex/skills/rite-build/reference/wright-dispatch.md +7 -7
- package/pack/generated/codex/skills/rite-clarify/SKILL.md +2 -2
- package/pack/generated/codex/skills/rite-converge/SKILL.md +4 -4
- package/pack/generated/codex/skills/rite-define/SKILL.md +7 -3
- package/pack/generated/codex/skills/rite-define/reference/plan-template.md +4 -2
- package/pack/generated/codex/skills/rite-plan/SKILL.md +11 -8
- package/pack/generated/codex/skills/rite-plan/reference/dependency-graph.md +9 -2
- package/pack/generated/codex/skills/rite-plan/reference/slicing.md +2 -0
- package/pack/generated/codex/skills/rite-polish/SKILL.md +3 -3
- package/pack/generated/codex/skills/rite-prove/SKILL.md +9 -5
- package/pack/generated/codex/skills/rite-resolve/SKILL.md +3 -3
- package/pack/generated/codex/skills/rite-resolve/reference/answer-protocol.md +2 -0
- package/pack/generated/codex/skills/rite-review/SKILL.md +4 -3
- package/pack/generated/codex/skills/rite-review/reference/feature-scoped-review.md +2 -2
- package/pack/generated/codex/skills/rite-seal/SKILL.md +1 -1
- package/pack/generated/codex/skills/rite-seal/reference/phase-contract.md +1 -1
- package/pack/generated/codex/skills/rite-ship/SKILL.md +2 -2
- package/pack/generated/codex/skills/rite-ship/reference/git-ship.md +2 -2
- package/pack/generated/codex/skills/rite-spec/SKILL.md +8 -5
- package/pack/generated/codex/skills/rite-spec/reference/state-workspace.md +5 -0
- package/pack/generated/codex/skills/rite-temper/SKILL.md +3 -3
- package/pack/generated/codex/skills/rite-vet/SKILL.md +9 -5
- package/pack/generated/codex/skills/rite-vet/reference/review-axes.md +12 -5
- package/pack/generated/devin/agents/devrites-proof-runner.md +16 -9
- package/pack/generated/devin/skills/devrites-lib/SKILL.md +2 -0
- package/pack/generated/devin/skills/devrites-lib/reference/candidate-integrity.md +7 -3
- package/pack/generated/devin/skills/devrites-lib/reference/parallel-dispatch.md +3 -0
- package/pack/generated/devin/skills/devrites-lib/reference/parallel-workspaces.md +202 -0
- package/pack/generated/devin/skills/devrites-lib/reference/standards/afk-hitl.md +9 -12
- package/pack/generated/devin/skills/devrites-lib/reference/standards/agents.md +7 -3
- package/pack/generated/devin/skills/devrites-lib/reference/standards/context-hygiene.md +1 -1
- package/pack/generated/devin/skills/devrites-lib/reference/standards/gates.md +18 -9
- package/pack/generated/devin/skills/devrites-lib/reference/workspace-artifact-schema.md +10 -0
- package/pack/generated/devin/skills/rite-autocomplete/SKILL.md +13 -10
- package/pack/generated/devin/skills/rite-autocomplete/reference/lookahead.md +4 -4
- package/pack/generated/devin/skills/rite-autocomplete/reference/loop.md +13 -9
- package/pack/generated/devin/skills/rite-build/SKILL.md +13 -10
- package/pack/generated/devin/skills/rite-build/reference/afk-discipline.md +1 -1
- package/pack/generated/devin/skills/rite-build/reference/checkpoint.md +1 -1
- package/pack/generated/devin/skills/rite-build/reference/parallel-batch.md +60 -39
- package/pack/generated/devin/skills/rite-build/reference/phase-contract.md +8 -5
- package/pack/generated/devin/skills/rite-build/reference/wright-dispatch.md +7 -7
- package/pack/generated/devin/skills/rite-clarify/SKILL.md +2 -2
- package/pack/generated/devin/skills/rite-converge/SKILL.md +4 -4
- package/pack/generated/devin/skills/rite-define/SKILL.md +7 -3
- package/pack/generated/devin/skills/rite-define/reference/plan-template.md +4 -2
- package/pack/generated/devin/skills/rite-plan/SKILL.md +11 -8
- package/pack/generated/devin/skills/rite-plan/reference/dependency-graph.md +9 -2
- package/pack/generated/devin/skills/rite-plan/reference/slicing.md +2 -0
- package/pack/generated/devin/skills/rite-polish/SKILL.md +3 -3
- package/pack/generated/devin/skills/rite-prove/SKILL.md +9 -5
- package/pack/generated/devin/skills/rite-resolve/SKILL.md +3 -3
- package/pack/generated/devin/skills/rite-resolve/reference/answer-protocol.md +2 -0
- package/pack/generated/devin/skills/rite-review/SKILL.md +4 -3
- package/pack/generated/devin/skills/rite-review/reference/feature-scoped-review.md +2 -2
- package/pack/generated/devin/skills/rite-seal/SKILL.md +1 -1
- package/pack/generated/devin/skills/rite-seal/reference/phase-contract.md +1 -1
- package/pack/generated/devin/skills/rite-ship/SKILL.md +2 -2
- package/pack/generated/devin/skills/rite-ship/reference/git-ship.md +2 -2
- package/pack/generated/devin/skills/rite-spec/SKILL.md +8 -5
- package/pack/generated/devin/skills/rite-spec/reference/state-workspace.md +5 -0
- package/pack/generated/devin/skills/rite-temper/SKILL.md +3 -3
- package/pack/generated/devin/skills/rite-vet/SKILL.md +9 -5
- package/pack/generated/devin/skills/rite-vet/reference/review-axes.md +12 -5
- package/pack/generated/dsh/agents/devrites-proof-runner.md +16 -9
- package/pack/generated/dsh/runtime/cordis.patch.yml +1 -1
- package/pack/generated/dsh/skills/devrites-lib/SKILL.md +2 -0
- package/pack/generated/dsh/skills/devrites-lib/reference/candidate-integrity.md +7 -3
- package/pack/generated/dsh/skills/devrites-lib/reference/parallel-dispatch.md +3 -0
- package/pack/generated/dsh/skills/devrites-lib/reference/parallel-workspaces.md +202 -0
- package/pack/generated/dsh/skills/devrites-lib/reference/standards/afk-hitl.md +9 -12
- package/pack/generated/dsh/skills/devrites-lib/reference/standards/agents.md +7 -3
- package/pack/generated/dsh/skills/devrites-lib/reference/standards/context-hygiene.md +1 -1
- package/pack/generated/dsh/skills/devrites-lib/reference/standards/gates.md +18 -9
- package/pack/generated/dsh/skills/devrites-lib/reference/workspace-artifact-schema.md +10 -0
- package/pack/generated/dsh/skills/rite-autocomplete/SKILL.md +13 -10
- package/pack/generated/dsh/skills/rite-autocomplete/reference/lookahead.md +4 -4
- package/pack/generated/dsh/skills/rite-autocomplete/reference/loop.md +13 -9
- package/pack/generated/dsh/skills/rite-build/SKILL.md +13 -10
- package/pack/generated/dsh/skills/rite-build/reference/afk-discipline.md +1 -1
- package/pack/generated/dsh/skills/rite-build/reference/checkpoint.md +1 -1
- package/pack/generated/dsh/skills/rite-build/reference/parallel-batch.md +60 -39
- package/pack/generated/dsh/skills/rite-build/reference/phase-contract.md +8 -5
- package/pack/generated/dsh/skills/rite-build/reference/wright-dispatch.md +7 -7
- package/pack/generated/dsh/skills/rite-clarify/SKILL.md +2 -2
- package/pack/generated/dsh/skills/rite-converge/SKILL.md +4 -4
- package/pack/generated/dsh/skills/rite-define/SKILL.md +7 -3
- package/pack/generated/dsh/skills/rite-define/reference/plan-template.md +4 -2
- package/pack/generated/dsh/skills/rite-plan/SKILL.md +11 -8
- package/pack/generated/dsh/skills/rite-plan/reference/dependency-graph.md +9 -2
- package/pack/generated/dsh/skills/rite-plan/reference/slicing.md +2 -0
- package/pack/generated/dsh/skills/rite-polish/SKILL.md +3 -3
- package/pack/generated/dsh/skills/rite-prove/SKILL.md +9 -5
- package/pack/generated/dsh/skills/rite-resolve/SKILL.md +3 -3
- package/pack/generated/dsh/skills/rite-resolve/reference/answer-protocol.md +2 -0
- package/pack/generated/dsh/skills/rite-review/SKILL.md +4 -3
- package/pack/generated/dsh/skills/rite-review/reference/feature-scoped-review.md +2 -2
- package/pack/generated/dsh/skills/rite-seal/SKILL.md +1 -1
- package/pack/generated/dsh/skills/rite-seal/reference/phase-contract.md +1 -1
- package/pack/generated/dsh/skills/rite-ship/SKILL.md +2 -2
- package/pack/generated/dsh/skills/rite-ship/reference/git-ship.md +2 -2
- package/pack/generated/dsh/skills/rite-spec/SKILL.md +8 -5
- package/pack/generated/dsh/skills/rite-spec/reference/state-workspace.md +5 -0
- package/pack/generated/dsh/skills/rite-temper/SKILL.md +3 -3
- package/pack/generated/dsh/skills/rite-vet/SKILL.md +9 -5
- package/pack/generated/dsh/skills/rite-vet/reference/review-axes.md +12 -5
- package/pack/generated/omp/agents/devrites-proof-runner.md +16 -9
- package/pack/generated/omp/commands/rite-build.md +1 -1
- package/pack/generated/omp/skills/devrites-lib/SKILL.md +2 -0
- package/pack/generated/omp/skills/devrites-lib/reference/candidate-integrity.md +7 -3
- package/pack/generated/omp/skills/devrites-lib/reference/parallel-dispatch.md +3 -0
- package/pack/generated/omp/skills/devrites-lib/reference/parallel-workspaces.md +202 -0
- package/pack/generated/omp/skills/devrites-lib/reference/standards/afk-hitl.md +9 -12
- package/pack/generated/omp/skills/devrites-lib/reference/standards/agents.md +11 -3
- package/pack/generated/omp/skills/devrites-lib/reference/standards/context-hygiene.md +1 -1
- package/pack/generated/omp/skills/devrites-lib/reference/standards/gates.md +18 -9
- package/pack/generated/omp/skills/devrites-lib/reference/workspace-artifact-schema.md +10 -0
- package/pack/generated/omp/skills/rite-autocomplete/SKILL.md +13 -10
- package/pack/generated/omp/skills/rite-autocomplete/reference/lookahead.md +4 -4
- package/pack/generated/omp/skills/rite-autocomplete/reference/loop.md +13 -9
- package/pack/generated/omp/skills/rite-build/SKILL.md +13 -10
- package/pack/generated/omp/skills/rite-build/reference/afk-discipline.md +1 -1
- package/pack/generated/omp/skills/rite-build/reference/checkpoint.md +1 -1
- package/pack/generated/omp/skills/rite-build/reference/parallel-batch.md +60 -39
- package/pack/generated/omp/skills/rite-build/reference/phase-contract.md +8 -5
- package/pack/generated/omp/skills/rite-build/reference/wright-dispatch.md +7 -7
- package/pack/generated/omp/skills/rite-clarify/SKILL.md +2 -2
- package/pack/generated/omp/skills/rite-converge/SKILL.md +4 -4
- package/pack/generated/omp/skills/rite-define/SKILL.md +7 -3
- package/pack/generated/omp/skills/rite-define/reference/plan-template.md +4 -2
- package/pack/generated/omp/skills/rite-plan/SKILL.md +11 -8
- package/pack/generated/omp/skills/rite-plan/reference/dependency-graph.md +9 -2
- package/pack/generated/omp/skills/rite-plan/reference/slicing.md +2 -0
- package/pack/generated/omp/skills/rite-polish/SKILL.md +3 -3
- package/pack/generated/omp/skills/rite-prove/SKILL.md +9 -5
- package/pack/generated/omp/skills/rite-resolve/SKILL.md +3 -3
- package/pack/generated/omp/skills/rite-resolve/reference/answer-protocol.md +2 -0
- package/pack/generated/omp/skills/rite-review/SKILL.md +4 -3
- package/pack/generated/omp/skills/rite-review/reference/feature-scoped-review.md +2 -2
- package/pack/generated/omp/skills/rite-seal/SKILL.md +1 -1
- package/pack/generated/omp/skills/rite-seal/reference/phase-contract.md +1 -1
- package/pack/generated/omp/skills/rite-ship/SKILL.md +2 -2
- package/pack/generated/omp/skills/rite-ship/reference/git-ship.md +2 -2
- package/pack/generated/omp/skills/rite-spec/SKILL.md +8 -5
- package/pack/generated/omp/skills/rite-spec/reference/state-workspace.md +5 -0
- package/pack/generated/omp/skills/rite-temper/SKILL.md +3 -3
- package/pack/generated/omp/skills/rite-vet/SKILL.md +9 -5
- package/pack/generated/omp/skills/rite-vet/reference/review-axes.md +12 -5
- package/pack/generated/pi/agents/devrites-proof-runner.md +16 -9
- package/pack/generated/pi/prompts/rite-build.md +1 -1
- package/pack/generated/pi/skills/devrites-lib/SKILL.md +2 -0
- package/pack/generated/pi/skills/devrites-lib/reference/candidate-integrity.md +7 -3
- package/pack/generated/pi/skills/devrites-lib/reference/parallel-dispatch.md +3 -0
- package/pack/generated/pi/skills/devrites-lib/reference/parallel-workspaces.md +202 -0
- package/pack/generated/pi/skills/devrites-lib/reference/standards/afk-hitl.md +9 -12
- package/pack/generated/pi/skills/devrites-lib/reference/standards/agents.md +7 -3
- package/pack/generated/pi/skills/devrites-lib/reference/standards/context-hygiene.md +1 -1
- package/pack/generated/pi/skills/devrites-lib/reference/standards/gates.md +18 -9
- package/pack/generated/pi/skills/devrites-lib/reference/workspace-artifact-schema.md +10 -0
- package/pack/generated/pi/skills/rite-autocomplete/SKILL.md +13 -10
- package/pack/generated/pi/skills/rite-autocomplete/reference/lookahead.md +4 -4
- package/pack/generated/pi/skills/rite-autocomplete/reference/loop.md +13 -9
- package/pack/generated/pi/skills/rite-build/SKILL.md +13 -10
- package/pack/generated/pi/skills/rite-build/reference/afk-discipline.md +1 -1
- package/pack/generated/pi/skills/rite-build/reference/checkpoint.md +1 -1
- package/pack/generated/pi/skills/rite-build/reference/parallel-batch.md +60 -39
- package/pack/generated/pi/skills/rite-build/reference/phase-contract.md +8 -5
- package/pack/generated/pi/skills/rite-build/reference/wright-dispatch.md +7 -7
- package/pack/generated/pi/skills/rite-clarify/SKILL.md +2 -2
- package/pack/generated/pi/skills/rite-converge/SKILL.md +4 -4
- package/pack/generated/pi/skills/rite-define/SKILL.md +7 -3
- package/pack/generated/pi/skills/rite-define/reference/plan-template.md +4 -2
- package/pack/generated/pi/skills/rite-plan/SKILL.md +11 -8
- package/pack/generated/pi/skills/rite-plan/reference/dependency-graph.md +9 -2
- package/pack/generated/pi/skills/rite-plan/reference/slicing.md +2 -0
- package/pack/generated/pi/skills/rite-polish/SKILL.md +3 -3
- package/pack/generated/pi/skills/rite-prove/SKILL.md +9 -5
- package/pack/generated/pi/skills/rite-resolve/SKILL.md +3 -3
- package/pack/generated/pi/skills/rite-resolve/reference/answer-protocol.md +2 -0
- package/pack/generated/pi/skills/rite-review/SKILL.md +4 -3
- package/pack/generated/pi/skills/rite-review/reference/feature-scoped-review.md +2 -2
- package/pack/generated/pi/skills/rite-seal/SKILL.md +1 -1
- package/pack/generated/pi/skills/rite-seal/reference/phase-contract.md +1 -1
- package/pack/generated/pi/skills/rite-ship/SKILL.md +2 -2
- package/pack/generated/pi/skills/rite-ship/reference/git-ship.md +2 -2
- package/pack/generated/pi/skills/rite-spec/SKILL.md +8 -5
- package/pack/generated/pi/skills/rite-spec/reference/state-workspace.md +5 -0
- package/pack/generated/pi/skills/rite-temper/SKILL.md +3 -3
- package/pack/generated/pi/skills/rite-vet/SKILL.md +9 -5
- package/pack/generated/pi/skills/rite-vet/reference/review-axes.md +12 -5
- package/package.json +1 -1
- package/scripts/build-host-artifacts.sh +5 -6
- package/scripts/omp-generate.sh +24 -14
|
@@ -17,11 +17,18 @@ import (
|
|
|
17
17
|
)
|
|
18
18
|
|
|
19
19
|
func (r *runner) installClaudeSettingsMarker(state claudeSettingsMerge) error {
|
|
20
|
+
return r.installMarker(hostpack.ClaudeSettingsMerge.MarkerRel, claudeSettingsMarkerText(state))
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
func claudeSettingsMarkerText(state claudeSettingsMerge) string {
|
|
20
24
|
var b strings.Builder
|
|
21
25
|
b.WriteString(hostpack.ClaudeSettingsMerge.MarkerText + "\n" + claudeRulesAddedLine)
|
|
22
26
|
if state.created {
|
|
23
27
|
b.WriteString("\n" + claudeSettingsCreatedLine)
|
|
24
28
|
}
|
|
29
|
+
if state.defaultModeOwned {
|
|
30
|
+
b.WriteString("\ndefault-mode=added")
|
|
31
|
+
}
|
|
25
32
|
rules := make([]string, 0, len(state.added))
|
|
26
33
|
for rule := range state.added {
|
|
27
34
|
rules = append(rules, rule)
|
|
@@ -30,7 +37,7 @@ func (r *runner) installClaudeSettingsMarker(state claudeSettingsMerge) error {
|
|
|
30
37
|
for _, rule := range rules {
|
|
31
38
|
b.WriteString("\nrule:" + rule)
|
|
32
39
|
}
|
|
33
|
-
return
|
|
40
|
+
return b.String()
|
|
34
41
|
}
|
|
35
42
|
|
|
36
43
|
const (
|
|
@@ -364,16 +371,45 @@ func (r *runner) mergeClaudeSettings(merge hostpack.JSONMerge) error {
|
|
|
364
371
|
return fmt.Errorf("encode merged Claude settings: %w", err)
|
|
365
372
|
}
|
|
366
373
|
data = append(data, '\n')
|
|
374
|
+
// Publish ownership before grants; keep retired ownership until settings land.
|
|
375
|
+
pending := r.pendingClaudeSettingsOwnership(state)
|
|
376
|
+
if err := r.installClaudeSettingsMarker(pending); err != nil {
|
|
377
|
+
return err
|
|
378
|
+
}
|
|
379
|
+
if r.records[merge.MarkerRel] == "" {
|
|
380
|
+
return fmt.Errorf("cannot merge %s without owning %s; use --force to replace the marker", merge.TargetRel, merge.MarkerRel)
|
|
381
|
+
}
|
|
382
|
+
if err := r.recheckPath(merge.TargetRel); err != nil {
|
|
383
|
+
return err
|
|
384
|
+
}
|
|
367
385
|
if err := r.writeConfined(dest, data, 0o644); err != nil {
|
|
368
386
|
return fmt.Errorf("cannot write %s: %w", merge.TargetRel, err)
|
|
369
387
|
}
|
|
370
|
-
|
|
388
|
+
if pending.defaultModeOwned || len(pending.added) != len(state.added) {
|
|
389
|
+
if err := r.refreshPreflight(merge.MarkerRel); err != nil {
|
|
390
|
+
return err
|
|
391
|
+
}
|
|
392
|
+
return r.installClaudeSettingsMarker(state)
|
|
393
|
+
}
|
|
394
|
+
return nil
|
|
371
395
|
}
|
|
372
396
|
|
|
373
397
|
// claudeSettingsMerge is what the settings marker records about a merge.
|
|
374
398
|
type claudeSettingsMerge struct {
|
|
375
|
-
created
|
|
376
|
-
added
|
|
399
|
+
created bool
|
|
400
|
+
added map[string]bool
|
|
401
|
+
defaultModeOwned bool
|
|
402
|
+
}
|
|
403
|
+
|
|
404
|
+
func (r *runner) pendingClaudeSettingsOwnership(next claudeSettingsMerge) claudeSettingsMerge {
|
|
405
|
+
pending := claudeSettingsMerge{
|
|
406
|
+
created: next.created, added: r.managedClaudePermissionRules(),
|
|
407
|
+
defaultModeOwned: r.claudeDefaultModeOwned(),
|
|
408
|
+
}
|
|
409
|
+
for rule := range next.added {
|
|
410
|
+
pending.added[rule] = true
|
|
411
|
+
}
|
|
412
|
+
return pending
|
|
377
413
|
}
|
|
378
414
|
|
|
379
415
|
// claudeSettingsNext returns the merged .claude/settings.json without
|
|
@@ -433,6 +469,12 @@ func stripHooksWith(path string, write func([]byte) error, remove func() error)
|
|
|
433
469
|
}
|
|
434
470
|
|
|
435
471
|
func (r *runner) stripClaudeSettings(path string) error {
|
|
472
|
+
return r.stripClaudeSettingsWith(path, func(data []byte) error {
|
|
473
|
+
return r.writeConfined(path, data, 0o644)
|
|
474
|
+
}, func() error { return r.removeConfined(path) })
|
|
475
|
+
}
|
|
476
|
+
|
|
477
|
+
func (r *runner) stripClaudeSettingsWith(path string, write func([]byte) error, remove func() error) error {
|
|
436
478
|
current, err := readJSON(path)
|
|
437
479
|
if err != nil {
|
|
438
480
|
if errors.Is(err, fs.ErrNotExist) {
|
|
@@ -442,14 +484,14 @@ func (r *runner) stripClaudeSettings(path string) error {
|
|
|
442
484
|
}
|
|
443
485
|
next := stripDevritesSettings(current, r.claudeDefaultModeOwned(), r.managedClaudePermissionRules())
|
|
444
486
|
if len(next) == 0 && r.claudeSettingsCreated() {
|
|
445
|
-
return
|
|
487
|
+
return remove()
|
|
446
488
|
}
|
|
447
489
|
data, err := json.MarshalIndent(next, "", " ")
|
|
448
490
|
if err != nil {
|
|
449
491
|
return fmt.Errorf("encode Claude settings: %w", err)
|
|
450
492
|
}
|
|
451
493
|
data = append(data, '\n')
|
|
452
|
-
return
|
|
494
|
+
return write(data)
|
|
453
495
|
}
|
|
454
496
|
|
|
455
497
|
func (r *runner) stripMarkerConfined(path, begin, end string) error {
|
|
@@ -6,6 +6,7 @@ import (
|
|
|
6
6
|
"fmt"
|
|
7
7
|
"io"
|
|
8
8
|
"io/fs"
|
|
9
|
+
"maps"
|
|
9
10
|
"os"
|
|
10
11
|
"path/filepath"
|
|
11
12
|
"slices"
|
|
@@ -22,6 +23,7 @@ func (r *runner) preflightInstall() error {
|
|
|
22
23
|
return fmt.Errorf("preflight install paths: %w", err)
|
|
23
24
|
}
|
|
24
25
|
var conflicts []string
|
|
26
|
+
dryTree := map[string][]byte{}
|
|
25
27
|
for rel := range desired {
|
|
26
28
|
snapshot, err := r.rememberPath(rel)
|
|
27
29
|
if err != nil {
|
|
@@ -34,7 +36,8 @@ func (r *runner) preflightInstall() error {
|
|
|
34
36
|
}
|
|
35
37
|
}
|
|
36
38
|
}
|
|
37
|
-
for
|
|
39
|
+
for _, rel := range slices.Sorted(maps.Keys(r.prev)) {
|
|
40
|
+
record := r.prev[rel]
|
|
38
41
|
if desired[rel] || hostpack.PreserveOnPrune(rel) {
|
|
39
42
|
continue
|
|
40
43
|
}
|
|
@@ -58,7 +61,7 @@ func (r *runner) preflightInstall() error {
|
|
|
58
61
|
if _, err := r.rememberPath(merge.TargetRel); err != nil {
|
|
59
62
|
return err
|
|
60
63
|
}
|
|
61
|
-
if err := dryRunStripMarker(
|
|
64
|
+
if err := r.dryRunStripMarker(dryTree, merge); err != nil {
|
|
62
65
|
return err
|
|
63
66
|
}
|
|
64
67
|
}
|
|
@@ -104,19 +107,34 @@ func (r *runner) preflightMerges() error {
|
|
|
104
107
|
return fmt.Errorf("merge %s: %w", hostpack.CodexConfigMerge.TargetRel, err)
|
|
105
108
|
}
|
|
106
109
|
}
|
|
107
|
-
|
|
110
|
+
_, settings, err := r.claudeSettingsNext()
|
|
111
|
+
if err != nil {
|
|
108
112
|
return fmt.Errorf("seed claude settings: %w", err)
|
|
109
113
|
}
|
|
114
|
+
marker := hostpack.ClaudeSettingsMerge.MarkerRel
|
|
115
|
+
if snapshot := r.preflight[marker]; !snapshot.missing && !r.opts.Force {
|
|
116
|
+
if _, managed := r.prev[marker]; !managed {
|
|
117
|
+
text := claudeSettingsMarkerText(r.pendingClaudeSettingsOwnership(settings)) + "\n"
|
|
118
|
+
sum := sha256.Sum256([]byte(text))
|
|
119
|
+
if snapshot.hash != fmt.Sprintf("sha256:%x", sum[:]) {
|
|
120
|
+
return fmt.Errorf("cannot merge %s without owning %s; use --force to replace the marker", hostpack.ClaudeSettingsMerge.TargetRel, marker)
|
|
121
|
+
}
|
|
122
|
+
}
|
|
123
|
+
}
|
|
110
124
|
return nil
|
|
111
125
|
}
|
|
112
126
|
|
|
113
127
|
func (r *runner) preflightUninstall(entries []string) error {
|
|
114
128
|
var conflicts []string
|
|
129
|
+
dryTree := map[string][]byte{}
|
|
115
130
|
for _, rel := range entries {
|
|
116
131
|
if merge, ok := hostpack.ManagedMergeForMarker(rel); ok {
|
|
117
132
|
if _, err := r.rememberPath(merge.TargetRel); err != nil {
|
|
118
133
|
return err
|
|
119
134
|
}
|
|
135
|
+
if err := r.dryRunStripMarker(dryTree, merge); err != nil {
|
|
136
|
+
return err
|
|
137
|
+
}
|
|
120
138
|
}
|
|
121
139
|
if !hostpack.ShouldRemoveOnUninstall(rel, entries) {
|
|
122
140
|
continue
|
|
@@ -35,18 +35,12 @@ func (r *runner) uninstall() error {
|
|
|
35
35
|
return err
|
|
36
36
|
}
|
|
37
37
|
|
|
38
|
-
// Per-path would-be content for a dry run; a nil value means the file
|
|
39
|
-
// would be removed.
|
|
40
|
-
dryTree := map[string][]byte{}
|
|
41
38
|
for _, rel := range entries {
|
|
42
39
|
merge, ok := hostpack.ManagedMergeForMarker(rel)
|
|
43
40
|
if !ok {
|
|
44
41
|
continue
|
|
45
42
|
}
|
|
46
43
|
if r.opts.DryRun {
|
|
47
|
-
if err := dryRunStripMarker(dryTree, r.target, merge); err != nil {
|
|
48
|
-
return err
|
|
49
|
-
}
|
|
50
44
|
fmt.Fprintf(r.opts.Stdout, " [merge-remove] %s\n", merge.DryRun)
|
|
51
45
|
continue
|
|
52
46
|
}
|
|
@@ -142,11 +136,16 @@ func (r *runner) uninstall() error {
|
|
|
142
136
|
// dryRunStripMarker runs the real marker strip against an in-memory view of
|
|
143
137
|
// the target (tree), so a dry run follows the same sequence as the real
|
|
144
138
|
// uninstall and reports the same errors without writing anything.
|
|
145
|
-
func dryRunStripMarker(tree map[string][]byte,
|
|
146
|
-
|
|
147
|
-
|
|
139
|
+
func (r *runner) dryRunStripMarker(tree map[string][]byte, merge hostpack.ManagedMerge) error {
|
|
140
|
+
path := filepath.Join(r.target, filepath.FromSlash(merge.TargetRel))
|
|
141
|
+
write := func(data []byte) error { tree[path] = data; return nil }
|
|
142
|
+
remove := func() error { tree[path] = nil; return nil }
|
|
143
|
+
if merge.MarkerRel == hostpack.LegacyCodexHooksMerge.MarkerRel {
|
|
144
|
+
return stripHooksWith(path, write, remove)
|
|
145
|
+
}
|
|
146
|
+
if merge.TargetRel == hostpack.ClaudeSettingsMerge.TargetRel {
|
|
147
|
+
return r.stripClaudeSettingsWith(path, write, remove)
|
|
148
148
|
}
|
|
149
|
-
path := filepath.Join(target, filepath.FromSlash(merge.TargetRel))
|
|
150
149
|
data, seen := tree[path]
|
|
151
150
|
if !seen {
|
|
152
151
|
var err error
|
|
@@ -210,7 +210,12 @@ func collectBundleTargets(slug string, bundles []candidateBundle) ([]*bundleTarg
|
|
|
210
210
|
}
|
|
211
211
|
pins := map[string]string{}
|
|
212
212
|
for path, hash := range bundle.BaseHashes {
|
|
213
|
-
|
|
213
|
+
rel := filepath.ToSlash(filepath.Clean(strings.TrimPrefix(filepath.ToSlash(path), prefix)))
|
|
214
|
+
pin := strings.ToLower(strings.TrimPrefix(strings.TrimSpace(hash), "sha256:"))
|
|
215
|
+
if previous, ok := pins[rel]; ok && previous != pin {
|
|
216
|
+
problems = append(problems, fmt.Sprintf("bundle %d %s: conflicting base hashes for the same file", bi+1, path))
|
|
217
|
+
}
|
|
218
|
+
pins[rel] = pin
|
|
214
219
|
}
|
|
215
220
|
for _, file := range bundle.CandidateFiles {
|
|
216
221
|
rel := strings.TrimPrefix(filepath.ToSlash(file.Path), prefix)
|
|
@@ -219,6 +224,7 @@ func collectBundleTargets(slug string, bundles []candidateBundle) ([]*bundleTarg
|
|
|
219
224
|
problems = append(problems, where+": path must be a Markdown file inside the "+slug+" workspace")
|
|
220
225
|
continue
|
|
221
226
|
}
|
|
227
|
+
rel = filepath.ToSlash(filepath.Clean(rel))
|
|
222
228
|
op := strings.ToLower(strings.TrimSpace(file.Operation))
|
|
223
229
|
switch op {
|
|
224
230
|
case "create", "replace":
|
|
@@ -38,6 +38,7 @@ type candidateRow struct {
|
|
|
38
38
|
executable bool
|
|
39
39
|
size int64
|
|
40
40
|
info os.FileInfo
|
|
41
|
+
fileDigest string
|
|
41
42
|
}
|
|
42
43
|
|
|
43
44
|
type candidateIdentity struct {
|
|
@@ -72,7 +73,7 @@ func loadCandidateIdentity(root, slug string) (candidateIdentity, error) {
|
|
|
72
73
|
if err := validateCandidateRows(project, rows); err != nil {
|
|
73
74
|
return candidateIdentity{}, err
|
|
74
75
|
}
|
|
75
|
-
digest, err := hashCandidate(project, rows)
|
|
76
|
+
digest, err := hashCandidate(project, rows, false)
|
|
76
77
|
if err != nil {
|
|
77
78
|
return candidateIdentity{}, err
|
|
78
79
|
}
|
|
@@ -407,15 +408,18 @@ func candidatePathInfo(project, name string) (os.FileInfo, bool, error) {
|
|
|
407
408
|
return nil, false, errors.New("path is empty")
|
|
408
409
|
}
|
|
409
410
|
|
|
410
|
-
func hashCandidate(project string, rows []candidateRow) (string, error) {
|
|
411
|
+
func hashCandidate(project string, rows []candidateRow, perFile bool) (string, error) {
|
|
411
412
|
digest := sha256.New()
|
|
412
413
|
writeCandidateField(digest, candidateDigestDomain)
|
|
413
414
|
writeCandidateField(digest, candidateDigestVersion)
|
|
414
415
|
writeCandidateField(digest, fmt.Sprintf("%d", len(rows)))
|
|
415
|
-
for
|
|
416
|
+
for i, row := range rows {
|
|
416
417
|
writeCandidateField(digest, row.state)
|
|
417
418
|
writeCandidateField(digest, row.path)
|
|
418
419
|
if row.state == "deleted" {
|
|
420
|
+
if err := validateCandidateRow(project, &row); err != nil {
|
|
421
|
+
return "", fmt.Errorf("candidate %q: %w", row.path, err)
|
|
422
|
+
}
|
|
419
423
|
writeCandidateField(digest, "absent")
|
|
420
424
|
writeCandidateField(digest, "0")
|
|
421
425
|
writeCandidateLength(digest, 0)
|
|
@@ -428,9 +432,18 @@ func hashCandidate(project string, rows []candidateRow) (string, error) {
|
|
|
428
432
|
writeCandidateField(digest, "0")
|
|
429
433
|
}
|
|
430
434
|
writeCandidateLength(digest, uint64(row.size)) // #nosec G115 -- validateCandidateRow bounds size to 0..64 MiB
|
|
431
|
-
|
|
435
|
+
var writer io.Writer = digest
|
|
436
|
+
var fileDigest hash.Hash
|
|
437
|
+
if perFile {
|
|
438
|
+
fileDigest = sha256.New()
|
|
439
|
+
writer = io.MultiWriter(digest, fileDigest)
|
|
440
|
+
}
|
|
441
|
+
if err := hashCandidateFile(writer, project, row); err != nil {
|
|
432
442
|
return "", fmt.Errorf("candidate %q: %w", row.path, err)
|
|
433
443
|
}
|
|
444
|
+
if perFile {
|
|
445
|
+
rows[i].fileDigest = hex.EncodeToString(fileDigest.Sum(nil))
|
|
446
|
+
}
|
|
434
447
|
}
|
|
435
448
|
return hex.EncodeToString(digest.Sum(nil)), nil
|
|
436
449
|
}
|
|
@@ -446,7 +459,7 @@ func writeCandidateLength(writer hash.Hash, size uint64) {
|
|
|
446
459
|
_, _ = writer.Write(encoded[:])
|
|
447
460
|
}
|
|
448
461
|
|
|
449
|
-
func hashCandidateFile(digest
|
|
462
|
+
func hashCandidateFile(digest io.Writer, project string, row candidateRow) error {
|
|
450
463
|
if _, exists, err := candidatePathInfo(project, row.path); err != nil || !exists {
|
|
451
464
|
if err != nil {
|
|
452
465
|
return err
|
|
@@ -464,7 +477,7 @@ func hashCandidateFile(digest hash.Hash, project string, row candidateRow) error
|
|
|
464
477
|
if err != nil {
|
|
465
478
|
return err
|
|
466
479
|
}
|
|
467
|
-
if !opened.Mode().IsRegular() || opened.Size() != row.size || (opened.Mode().Perm()&0o111 != 0) != row.executable || !os.SameFile(row.info, opened) {
|
|
480
|
+
if !opened.Mode().IsRegular() || opened.Size() != row.size || (opened.Mode().Perm()&0o111 != 0) != row.executable || !os.SameFile(row.info, opened) || !row.info.ModTime().Equal(opened.ModTime()) {
|
|
468
481
|
return errors.New("file changed size or type before reading")
|
|
469
482
|
}
|
|
470
483
|
written, err := io.CopyN(digest, file, row.size)
|
|
@@ -483,7 +496,7 @@ func hashCandidateFile(digest hash.Hash, project string, row candidateRow) error
|
|
|
483
496
|
if err != nil {
|
|
484
497
|
return err
|
|
485
498
|
}
|
|
486
|
-
if !exists || !after.Mode().IsRegular() || after.Size() != row.size || (after.Mode().Perm()&0o111 != 0) != row.executable || !os.SameFile(opened, after) || !os.SameFile(opened, current) {
|
|
499
|
+
if !exists || !after.Mode().IsRegular() || after.Size() != row.size || current.Size() != row.size || (after.Mode().Perm()&0o111 != 0) != row.executable || (current.Mode().Perm()&0o111 != 0) != row.executable || !os.SameFile(opened, after) || !os.SameFile(opened, current) || !opened.ModTime().Equal(after.ModTime()) || !opened.ModTime().Equal(current.ModTime()) {
|
|
487
500
|
return errors.New("file changed size or type while reading")
|
|
488
501
|
}
|
|
489
502
|
return nil
|
|
@@ -1,9 +1,7 @@
|
|
|
1
1
|
package lib
|
|
2
2
|
|
|
3
3
|
import (
|
|
4
|
-
"errors"
|
|
5
4
|
"fmt"
|
|
6
|
-
"os"
|
|
7
5
|
"path/filepath"
|
|
8
6
|
"regexp"
|
|
9
7
|
"sort"
|
|
@@ -118,7 +116,7 @@ func omissionError(omitted []string) error {
|
|
|
118
116
|
len(omitted), strings.Join(shown, ", "), more)
|
|
119
117
|
}
|
|
120
118
|
|
|
121
|
-
var diffPathRE = regexp.MustCompile(`[A-Za-z0-9_./@+-]+\.(?:diff|patch)\b`)
|
|
119
|
+
var diffPathRE = regexp.MustCompile(`(?:[A-Za-z]:/)?[A-Za-z0-9_./@+-]+\.(?:diff|patch)\b`)
|
|
122
120
|
|
|
123
121
|
// lintReviewCandidate gates a Review or Seal reviewer wave: the candidate
|
|
124
122
|
// manifest must cover every path the feature's checkpoints changed, and the
|
|
@@ -128,20 +126,5 @@ func lintReviewCandidate(root, slug, packet string) error {
|
|
|
128
126
|
if err := CheckCandidateCoverage(root, slug); err != nil {
|
|
129
127
|
return err
|
|
130
128
|
}
|
|
131
|
-
|
|
132
|
-
if err != nil {
|
|
133
|
-
return fmt.Errorf("reviewer packet %s: %w", packet, err)
|
|
134
|
-
}
|
|
135
|
-
for _, name := range diffPathRE.FindAllString(string(raw), -1) {
|
|
136
|
-
for _, dir := range []string{projectDir(root), featureDir(root, slug), ""} {
|
|
137
|
-
candidate := name
|
|
138
|
-
if dir != "" {
|
|
139
|
-
candidate = filepath.Join(dir, filepath.FromSlash(name))
|
|
140
|
-
}
|
|
141
|
-
if info, err := os.Stat(candidate); err == nil && info.Mode().IsRegular() && info.Size() > 0 {
|
|
142
|
-
return nil
|
|
143
|
-
}
|
|
144
|
-
}
|
|
145
|
-
}
|
|
146
|
-
return errors.New("a Review or Seal reviewer packet must name the frozen candidate diff (an existing non-empty .diff or .patch file); reviewers cannot judge a digest alone")
|
|
129
|
+
return lintPacketDiffs(root, slug, packet, true)
|
|
147
130
|
}
|
|
@@ -10,6 +10,7 @@ import (
|
|
|
10
10
|
"runtime"
|
|
11
11
|
|
|
12
12
|
"github.com/devrites/devrites/internal/devritespaths"
|
|
13
|
+
"github.com/devrites/devrites/internal/parallel"
|
|
13
14
|
"github.com/devrites/devrites/internal/state"
|
|
14
15
|
)
|
|
15
16
|
|
|
@@ -62,11 +63,6 @@ func CloseOut(root string, args []string, stdout, stderr io.Writer) int {
|
|
|
62
63
|
archDisplay := filepath.ToSlash(filepath.Join("archive", slug))
|
|
63
64
|
active := filepath.Join(dv, "ACTIVE")
|
|
64
65
|
|
|
65
|
-
activeSlug, err := devritespaths.ActiveSlug(dv)
|
|
66
|
-
if err != nil {
|
|
67
|
-
fmt.Fprintf(stderr, "state close: invalid ACTIVE cursor: %v\n", err)
|
|
68
|
-
return 1
|
|
69
|
-
}
|
|
70
66
|
// A broken symlink at the destination still counts as "already there", so use
|
|
71
67
|
// Lstat rather than Stat to avoid silently overwriting it.
|
|
72
68
|
if _, err := os.Lstat(arch); err == nil {
|
|
@@ -83,11 +79,22 @@ func CloseOut(root string, args []string, stdout, stderr io.Writer) int {
|
|
|
83
79
|
// re-create work/<slug>. Windows cannot rename a directory that holds an
|
|
84
80
|
// open file, so there the lock is released just before the rename.
|
|
85
81
|
code := 1
|
|
86
|
-
if err :=
|
|
87
|
-
|
|
88
|
-
|
|
82
|
+
if err := parallel.WithControlLock(dv, func() error {
|
|
83
|
+
leased, err := parallel.WorkspaceLeased(dv, slug)
|
|
84
|
+
if err != nil {
|
|
85
|
+
return err
|
|
86
|
+
}
|
|
87
|
+
if leased {
|
|
88
|
+
fmt.Fprintf(stderr, "state close: BLOCKED: %s is named by a retained parallel lease; finish and clean up the controller lease before archiving\n", slug)
|
|
89
|
+
code = 3
|
|
90
|
+
return nil
|
|
91
|
+
}
|
|
92
|
+
return state.WithFeatureLockRelease(dv, slug, func(release func()) error {
|
|
93
|
+
code = closeOutLocked(release, work, arch, archiveDir, archDisplay, active, slug, stdout, stderr)
|
|
94
|
+
return nil
|
|
95
|
+
})
|
|
89
96
|
}); err != nil {
|
|
90
|
-
fmt.Fprintf(stderr, "state close:
|
|
97
|
+
fmt.Fprintf(stderr, "state close: %v\n", err)
|
|
91
98
|
return 1
|
|
92
99
|
}
|
|
93
100
|
return code
|
|
@@ -96,7 +103,12 @@ func CloseOut(root string, args []string, stdout, stderr io.Writer) int {
|
|
|
96
103
|
// closeOutLocked performs the rename and the conditional ACTIVE clear. It runs
|
|
97
104
|
// under the feature lock held by CloseOut and reports an exit code rather than
|
|
98
105
|
// an error, preserving CloseOut's documented exit codes.
|
|
99
|
-
func closeOutLocked(release func(), work, arch, archiveDir, archDisplay, active,
|
|
106
|
+
func closeOutLocked(release func(), work, arch, archiveDir, archDisplay, active, slug string, stdout, stderr io.Writer) int {
|
|
107
|
+
activeSlug, err := devritespaths.PhysicalActiveSlug(filepath.Dir(active))
|
|
108
|
+
if err != nil {
|
|
109
|
+
fmt.Fprintf(stderr, "state close: invalid ACTIVE cursor: %v\n", err)
|
|
110
|
+
return 1
|
|
111
|
+
}
|
|
100
112
|
// Fail closed: if the move does not happen, leave the cursor alone and do not
|
|
101
113
|
// claim success: a lost audit trail must never look like a clean close-out.
|
|
102
114
|
if err := os.MkdirAll(archiveDir, 0o755); err != nil {
|
|
@@ -4,6 +4,7 @@ import (
|
|
|
4
4
|
"bytes"
|
|
5
5
|
"crypto/sha256"
|
|
6
6
|
"encoding/json"
|
|
7
|
+
"errors"
|
|
7
8
|
"fmt"
|
|
8
9
|
"io"
|
|
9
10
|
"os"
|
|
@@ -80,7 +81,7 @@ func RunContext(root string, args []string, stdout, stderr io.Writer) int {
|
|
|
80
81
|
}
|
|
81
82
|
// A Build reviewer judges one slice; an unscoped bundle inlines whole
|
|
82
83
|
// plan/tasks/architecture into every dispatch.
|
|
83
|
-
if opts.phase == "build" && opts.role != "" && opts.role != "slice-wright" && opts.slice == "" {
|
|
84
|
+
if opts.phase == "build" && opts.role != "" && roleBase(opts.role) != "slice-wright" && opts.slice == "" {
|
|
84
85
|
fmt.Fprintf(stderr, "context: BLOCKED: unscoped build reviewer bundle: --role %s in phase build needs --slice <SLICE-ID>\n", opts.role)
|
|
85
86
|
return 3
|
|
86
87
|
}
|
|
@@ -93,12 +94,18 @@ func RunContext(root string, args []string, stdout, stderr io.Writer) int {
|
|
|
93
94
|
return 2
|
|
94
95
|
}
|
|
95
96
|
}
|
|
97
|
+
sources := contextSources{dir: featureDir}
|
|
96
98
|
// One active slice per bundle, except a parallel batch: the root has to
|
|
97
99
|
// see the other slices to start them. Whole tasks/plan/decisions ride
|
|
98
100
|
// that batch's turns; serial runs stay scoped.
|
|
99
101
|
autoScope := false
|
|
100
102
|
if featureDir != "" && opts.role == "" && opts.slice == "" && !opts.whole && (opts.phase == "build" || opts.phase == "autocomplete") && !parallelPlanVisible(root, opts.slug) {
|
|
101
|
-
|
|
103
|
+
id, err := activeSliceScope(&sources)
|
|
104
|
+
if err != nil {
|
|
105
|
+
fmt.Fprintf(stderr, "context: %v\n", err)
|
|
106
|
+
return 2
|
|
107
|
+
}
|
|
108
|
+
if id != "" {
|
|
102
109
|
opts.slices, opts.slice, autoScope = []string{id}, id, true
|
|
103
110
|
}
|
|
104
111
|
}
|
|
@@ -110,10 +117,21 @@ func RunContext(root string, args []string, stdout, stderr io.Writer) int {
|
|
|
110
117
|
var inventory []byte
|
|
111
118
|
recheckPath := opts.recheck
|
|
112
119
|
if recheckPath != "" {
|
|
113
|
-
if
|
|
120
|
+
if filepath.IsAbs(recheckPath) {
|
|
121
|
+
absoluteFeature, absErr := filepath.Abs(featureDir)
|
|
122
|
+
if absErr != nil {
|
|
123
|
+
fmt.Fprintf(stderr, "context: --recheck: %v\n", absErr)
|
|
124
|
+
return 2
|
|
125
|
+
}
|
|
126
|
+
if rel, relErr := filepath.Rel(absoluteFeature, recheckPath); relErr == nil && filepath.IsLocal(rel) {
|
|
127
|
+
inventory, err = sources.read(rel)
|
|
128
|
+
} else {
|
|
129
|
+
inventory, err = readBoundedRegularFile(recheckPath, workspaceArtifactLimit)
|
|
130
|
+
}
|
|
131
|
+
} else {
|
|
132
|
+
inventory, err = sources.read(recheckPath)
|
|
114
133
|
recheckPath = filepath.Join(featureDir, filepath.FromSlash(recheckPath))
|
|
115
134
|
}
|
|
116
|
-
inventory, err = os.ReadFile(recheckPath) // #nosec G304 -- the root names its own recheck inventory file
|
|
117
135
|
if err == nil && len(bytes.TrimSpace(inventory)) == 0 {
|
|
118
136
|
err = fmt.Errorf("%s is empty", opts.recheck)
|
|
119
137
|
}
|
|
@@ -141,7 +159,7 @@ func RunContext(root string, args []string, stdout, stderr io.Writer) int {
|
|
|
141
159
|
var sliceBlock string
|
|
142
160
|
var sliceIDs []citedID
|
|
143
161
|
if opts.slice != "" {
|
|
144
|
-
raw, err :=
|
|
162
|
+
raw, err := sources.read("tasks.md")
|
|
145
163
|
if err != nil {
|
|
146
164
|
fmt.Fprintf(stderr, "context: %v\n", err)
|
|
147
165
|
return 2
|
|
@@ -238,12 +256,17 @@ func RunContext(root string, args []string, stdout, stderr io.Writer) int {
|
|
|
238
256
|
}
|
|
239
257
|
// The open findings lead a recheck bundle, before any reference text.
|
|
240
258
|
if inventory != nil {
|
|
241
|
-
|
|
259
|
+
writeContextData(&bundle, recheckPath, "recheck inventory: "+opts.recheck, inventory, nil, seen, false)
|
|
242
260
|
loaded++
|
|
243
261
|
}
|
|
244
262
|
var rootOnly []string
|
|
245
263
|
citedSections, packByReference := 0, 0
|
|
246
|
-
|
|
264
|
+
facts, err := gatherTriggerFacts(root, opts.role, &sources)
|
|
265
|
+
if err != nil {
|
|
266
|
+
fmt.Fprintf(stderr, "context: %v\n", err)
|
|
267
|
+
return 2
|
|
268
|
+
}
|
|
269
|
+
keepModel := opts.role == "" || keepModelSurface(opts.role, facts)
|
|
247
270
|
for _, rel := range files {
|
|
248
271
|
abs := filepath.Join(skillsRoot, filepath.FromSlash(rel))
|
|
249
272
|
if seen[abs] {
|
|
@@ -297,16 +320,21 @@ func RunContext(root string, args []string, stdout, stderr io.Writer) int {
|
|
|
297
320
|
}
|
|
298
321
|
for _, rel := range workspaceList {
|
|
299
322
|
abs := filepath.Join(featureDir, filepath.FromSlash(rel))
|
|
323
|
+
data, err := sources.read(rel)
|
|
324
|
+
if err != nil && !errors.Is(err, os.ErrNotExist) {
|
|
325
|
+
fmt.Fprintf(stderr, "context: %v\n", err)
|
|
326
|
+
return 2
|
|
327
|
+
}
|
|
300
328
|
// Role bundles without --slice (full Vet, Review, Seal) read
|
|
301
329
|
// everything; root and --slice bundles take over-budget
|
|
302
330
|
// artifacts by anchor.
|
|
303
331
|
if opts.slice == "" && opts.role != "" {
|
|
304
|
-
if
|
|
332
|
+
if writeContextData(&bundle, abs, "workspace/"+rel, data, err, seen, false) {
|
|
305
333
|
loaded++
|
|
306
334
|
}
|
|
307
335
|
continue
|
|
308
336
|
}
|
|
309
|
-
ok, byRef := writeSliceEntry(&bundle, filepath.Dir(root), abs, "workspace/"+rel, opts.slice, sliceBlock, sliceIDs, seen)
|
|
337
|
+
ok, byRef := writeSliceEntry(&bundle, filepath.Dir(root), abs, "workspace/"+rel, data, err, opts.slice, sliceBlock, sliceIDs, seen)
|
|
310
338
|
if ok {
|
|
311
339
|
loaded++
|
|
312
340
|
}
|
|
@@ -322,34 +350,11 @@ func RunContext(root string, args []string, stdout, stderr io.Writer) int {
|
|
|
322
350
|
withTOC := insertSectionTOC(bundle.String())
|
|
323
351
|
bundle.Reset()
|
|
324
352
|
bundle.WriteString(withTOC)
|
|
325
|
-
out := opts.
|
|
326
|
-
if
|
|
327
|
-
out = filepath.Join(featureDir, "ctx", bundleName(opts))
|
|
328
|
-
} else if !filepath.IsAbs(out) && featureDir != "" {
|
|
329
|
-
out = filepath.Join(featureDir, filepath.FromSlash(out))
|
|
330
|
-
}
|
|
331
|
-
if err := os.MkdirAll(filepath.Dir(out), 0o755); err != nil {
|
|
353
|
+
out, unchanged, err := publishContext(featureDir, opts, []byte(bundle.String()))
|
|
354
|
+
if err != nil {
|
|
332
355
|
fmt.Fprintf(stderr, "context: %v\n", err)
|
|
333
356
|
return 2
|
|
334
357
|
}
|
|
335
|
-
digestPath := out + ".sha256"
|
|
336
|
-
digest := fmt.Sprintf("%x", sha256.Sum256([]byte(bundle.String())))
|
|
337
|
-
unchanged := false
|
|
338
|
-
if prev, err := os.ReadFile(digestPath); err == nil && strings.TrimSpace(string(prev)) == digest { // #nosec G304 -- digestPath is generated under the feature workspace
|
|
339
|
-
if _, err := os.Stat(out); err == nil {
|
|
340
|
-
unchanged = true
|
|
341
|
-
}
|
|
342
|
-
}
|
|
343
|
-
if !unchanged {
|
|
344
|
-
if err := os.WriteFile(out, []byte(bundle.String()), 0o600); err != nil {
|
|
345
|
-
fmt.Fprintf(stderr, "context: %v\n", err)
|
|
346
|
-
return 2
|
|
347
|
-
}
|
|
348
|
-
if err := os.WriteFile(digestPath, []byte(digest+"\n"), 0o600); err != nil {
|
|
349
|
-
fmt.Fprintf(stderr, "context: %v\n", err)
|
|
350
|
-
return 2
|
|
351
|
-
}
|
|
352
|
-
}
|
|
353
358
|
if opts.slug != "" {
|
|
354
359
|
phaseLabel := opts.phase
|
|
355
360
|
if phaseLabel == "" {
|
|
@@ -396,7 +401,7 @@ func RunContext(root string, args []string, stdout, stderr io.Writer) int {
|
|
|
396
401
|
fmt.Fprintf(stdout, "triggers: auto=[%s] (fired by --role dispatch)\n",
|
|
397
402
|
strings.Join(autoTriggers, ","))
|
|
398
403
|
}
|
|
399
|
-
if suggested := suggestTriggers(manifest,
|
|
404
|
+
if suggested := suggestTriggers(manifest, opts, facts); len(suggested) > 0 {
|
|
400
405
|
fmt.Fprintf(stdout, "triggers: suggested=[%s] (advisory — confirm each before use)\n",
|
|
401
406
|
strings.Join(suggested, ","))
|
|
402
407
|
// The advisory list is only actionable if the agent is told how to
|
|
@@ -455,7 +460,7 @@ func workspaceReadSet(manifest loadsManifest, role string) ([]string, bool) {
|
|
|
455
460
|
if role == "" {
|
|
456
461
|
return manifest.Workspace, false
|
|
457
462
|
}
|
|
458
|
-
if list, ok := manifest.WorkspaceByRole[role]; ok {
|
|
463
|
+
if list, ok := manifest.WorkspaceByRole[roleBase(role)]; ok {
|
|
459
464
|
return list, true
|
|
460
465
|
}
|
|
461
466
|
return manifest.Workspace, false
|
|
@@ -558,11 +563,10 @@ const sliceInlineMaxBytes = 16 << 10
|
|
|
558
563
|
// other artifact but spec.md over sliceInlineMaxBytes as a by-reference index;
|
|
559
564
|
// any canonical artifact over its line/byte budget as a by-reference index;
|
|
560
565
|
// everything else whole.
|
|
561
|
-
func writeSliceEntry(bundle *strings.Builder, project, abs, label, slice, block string, ids []citedID, seen map[string]bool) (loaded, byRef bool) {
|
|
566
|
+
func writeSliceEntry(bundle *strings.Builder, project, abs, label string, data []byte, err error, slice, block string, ids []citedID, seen map[string]bool) (loaded, byRef bool) {
|
|
562
567
|
if seen[abs] {
|
|
563
568
|
return false, false
|
|
564
569
|
}
|
|
565
|
-
data, err := os.ReadFile(abs) // #nosec G304 -- abs is resolved from the validated feature workspace
|
|
566
570
|
name := filepath.Base(abs)
|
|
567
571
|
if err == nil && name == "tasks.md" && slice != "" {
|
|
568
572
|
seen[abs] = true
|
|
@@ -570,7 +574,7 @@ func writeSliceEntry(bundle *strings.Builder, project, abs, label, slice, block
|
|
|
570
574
|
} else if err == nil && slice != "" && name != "spec.md" && len(data) > sliceInlineMaxBytes {
|
|
571
575
|
seen[abs] = true
|
|
572
576
|
} else if status, ok := state.ArtifactBudget(name, data); err != nil || !ok || !status.Over {
|
|
573
|
-
return
|
|
577
|
+
return writeContextData(bundle, abs, label, data, err, seen, false), false
|
|
574
578
|
} else {
|
|
575
579
|
seen[abs] = true
|
|
576
580
|
}
|
|
@@ -726,12 +730,19 @@ func clipRunes(s string, n int) string {
|
|
|
726
730
|
}
|
|
727
731
|
|
|
728
732
|
func writeContextEntry(bundle *strings.Builder, abs, label string, seen map[string]bool, expand bool) bool {
|
|
733
|
+
if seen[abs] {
|
|
734
|
+
return false
|
|
735
|
+
}
|
|
736
|
+
data, err := os.ReadFile(abs) // #nosec G304 -- abs is resolved from the validated context source set
|
|
737
|
+
return writeContextData(bundle, abs, label, data, err, seen, expand)
|
|
738
|
+
}
|
|
739
|
+
|
|
740
|
+
func writeContextData(bundle *strings.Builder, abs, label string, data []byte, err error, seen map[string]bool, expand bool) bool {
|
|
729
741
|
if seen[abs] {
|
|
730
742
|
return false
|
|
731
743
|
}
|
|
732
744
|
seen[abs] = true
|
|
733
745
|
fmt.Fprintf(bundle, "===== %s =====\n", label)
|
|
734
|
-
data, err := os.ReadFile(abs) // #nosec G304 -- abs is resolved from the validated context source set
|
|
735
746
|
if err != nil {
|
|
736
747
|
fmt.Fprintf(bundle, "MISSING: %s\n\n", label)
|
|
737
748
|
return false
|
|
@@ -915,6 +926,7 @@ func parseLoadsManifest(text string) (loadsManifest, bool, error) {
|
|
|
915
926
|
}
|
|
916
927
|
|
|
917
928
|
func roleAgentPath(manifest loadsManifest, role string) string {
|
|
929
|
+
role = roleBase(role)
|
|
918
930
|
if manifest.Agents != nil {
|
|
919
931
|
if rel, ok := manifest.Agents[role]; ok {
|
|
920
932
|
return rel
|
|
@@ -925,20 +937,28 @@ func roleAgentPath(manifest loadsManifest, role string) string {
|
|
|
925
937
|
|
|
926
938
|
// activeSliceScope is state.md's active_slice when it names a slice block in
|
|
927
939
|
// tasks.md, else "" (unset, "none", or stale cursors keep the whole bundle).
|
|
928
|
-
func activeSliceScope(
|
|
929
|
-
|
|
940
|
+
func activeSliceScope(sources *contextSources) (string, error) {
|
|
941
|
+
raw, err := sources.read("state.md")
|
|
942
|
+
if err != nil && !errors.Is(err, os.ErrNotExist) {
|
|
943
|
+
return "", err
|
|
944
|
+
}
|
|
945
|
+
lines := strings.Split(string(raw), "\n")
|
|
930
946
|
// active_slice outlives Build; after it the root's autocomplete bundle stays whole.
|
|
931
947
|
if phase, _ := state.CursorField(lines, state.CursorPhase); strings.TrimSpace(phase) != "build" {
|
|
932
|
-
return ""
|
|
948
|
+
return "", nil
|
|
933
949
|
}
|
|
934
950
|
id, _ := state.CursorField(lines, state.CursorActiveSlice)
|
|
935
951
|
if id = strings.TrimSpace(id); !state.ValidSliceID(id) {
|
|
936
|
-
return ""
|
|
952
|
+
return "", nil
|
|
953
|
+
}
|
|
954
|
+
tasks, err := sources.read("tasks.md")
|
|
955
|
+
if err != nil && !errors.Is(err, os.ErrNotExist) {
|
|
956
|
+
return "", err
|
|
937
957
|
}
|
|
938
|
-
if _, ok := state.ExtractTaskSlice(
|
|
939
|
-
return ""
|
|
958
|
+
if _, ok := state.ExtractTaskSlice(tasks, id); !ok {
|
|
959
|
+
return "", nil
|
|
940
960
|
}
|
|
941
|
-
return id
|
|
961
|
+
return id, nil
|
|
942
962
|
}
|
|
943
963
|
|
|
944
964
|
func bundleName(opts contextOpts) string {
|