devrites 5.7.2 → 5.9.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.
Files changed (137) hide show
  1. package/CHANGELOG.md +16 -0
  2. package/README.md +2 -2
  3. package/docs/architecture.md +4 -4
  4. package/docs/command-map.md +3 -3
  5. package/docs/engine/commands.md +1 -0
  6. package/docs/engine/workspace-schema.md +1 -1
  7. package/docs/flow.md +4 -2
  8. package/docs/orchestration.md +2 -2
  9. package/docs/skills.md +1 -1
  10. package/docs/usage.md +11 -10
  11. package/engine/internal/parallel/cli.go +69 -0
  12. package/engine/internal/parallel/git.go +8 -0
  13. package/engine/internal/parallel/ops.go +40 -12
  14. package/engine/internal/parallel/ops_test.go +29 -1
  15. package/engine/internal/parallel/parallel_test.go +26 -14
  16. package/engine/internal/parallel/select.go +76 -0
  17. package/engine/internal/parallel/select_test.go +95 -0
  18. package/pack/.claude/agents/devrites-slice-wright.md +18 -9
  19. package/pack/.claude/settings.json +1 -0
  20. package/pack/.claude/skills/devrites-lib/reference/standards/afk-hitl.md +4 -3
  21. package/pack/.claude/skills/rite-autocomplete/SKILL.md +18 -18
  22. package/pack/.claude/skills/rite-autocomplete/reference/decision-policy.md +2 -3
  23. package/pack/.claude/skills/rite-autocomplete/reference/loop.md +18 -25
  24. package/pack/.claude/skills/rite-autocomplete/reference/stop-conditions.md +10 -11
  25. package/pack/.claude/skills/rite-build/SKILL.md +13 -8
  26. package/pack/.claude/skills/rite-build/reference/afk-discipline.md +3 -1
  27. package/pack/.claude/skills/rite-build/reference/anti-patterns.md +4 -0
  28. package/pack/.claude/skills/rite-build/reference/checkpoint.md +33 -21
  29. package/pack/.claude/skills/rite-build/reference/one-slice-cycle.md +5 -4
  30. package/pack/.claude/skills/rite-build/reference/parallel-batch.md +30 -23
  31. package/pack/.claude/skills/rite-build/reference/phase-contract.md +4 -2
  32. package/pack/.claude/skills/rite-build/reference/wright-dispatch.md +16 -9
  33. package/pack/.claude/skills/rite-plan/reference/slicing.md +4 -3
  34. package/pack/.claude/skills/rite-polish/SKILL.md +3 -1
  35. package/pack/.claude/skills/rite-prove/SKILL.md +3 -0
  36. package/pack/.claude/skills/rite-review/SKILL.md +3 -1
  37. package/pack/.claude/skills/rite-temper/SKILL.md +6 -6
  38. package/pack/.claude/skills/rite-temper/reference/significance.md +3 -3
  39. package/pack/generated/claude/agents/devrites-slice-wright.md +18 -9
  40. package/pack/generated/claude/settings.json +1 -0
  41. package/pack/generated/claude/skills/devrites-lib/reference/standards/afk-hitl.md +4 -3
  42. package/pack/generated/claude/skills/rite-autocomplete/SKILL.md +18 -18
  43. package/pack/generated/claude/skills/rite-autocomplete/reference/decision-policy.md +2 -3
  44. package/pack/generated/claude/skills/rite-autocomplete/reference/loop.md +18 -25
  45. package/pack/generated/claude/skills/rite-autocomplete/reference/stop-conditions.md +10 -11
  46. package/pack/generated/claude/skills/rite-build/SKILL.md +13 -8
  47. package/pack/generated/claude/skills/rite-build/reference/afk-discipline.md +3 -1
  48. package/pack/generated/claude/skills/rite-build/reference/anti-patterns.md +4 -0
  49. package/pack/generated/claude/skills/rite-build/reference/checkpoint.md +33 -21
  50. package/pack/generated/claude/skills/rite-build/reference/one-slice-cycle.md +5 -4
  51. package/pack/generated/claude/skills/rite-build/reference/parallel-batch.md +30 -23
  52. package/pack/generated/claude/skills/rite-build/reference/phase-contract.md +4 -2
  53. package/pack/generated/claude/skills/rite-build/reference/wright-dispatch.md +16 -9
  54. package/pack/generated/claude/skills/rite-plan/reference/slicing.md +4 -3
  55. package/pack/generated/claude/skills/rite-polish/SKILL.md +3 -1
  56. package/pack/generated/claude/skills/rite-prove/SKILL.md +3 -0
  57. package/pack/generated/claude/skills/rite-review/SKILL.md +3 -1
  58. package/pack/generated/claude/skills/rite-temper/SKILL.md +6 -6
  59. package/pack/generated/claude/skills/rite-temper/reference/significance.md +3 -3
  60. package/pack/generated/codex/agents/devrites-slice-wright.toml +18 -9
  61. package/pack/generated/codex/skills/devrites-lib/reference/standards/afk-hitl.md +4 -3
  62. package/pack/generated/codex/skills/rite-autocomplete/SKILL.md +18 -18
  63. package/pack/generated/codex/skills/rite-autocomplete/reference/decision-policy.md +2 -3
  64. package/pack/generated/codex/skills/rite-autocomplete/reference/loop.md +18 -25
  65. package/pack/generated/codex/skills/rite-autocomplete/reference/stop-conditions.md +10 -11
  66. package/pack/generated/codex/skills/rite-build/SKILL.md +13 -8
  67. package/pack/generated/codex/skills/rite-build/reference/afk-discipline.md +3 -1
  68. package/pack/generated/codex/skills/rite-build/reference/anti-patterns.md +4 -0
  69. package/pack/generated/codex/skills/rite-build/reference/checkpoint.md +33 -21
  70. package/pack/generated/codex/skills/rite-build/reference/one-slice-cycle.md +5 -4
  71. package/pack/generated/codex/skills/rite-build/reference/parallel-batch.md +30 -23
  72. package/pack/generated/codex/skills/rite-build/reference/phase-contract.md +4 -2
  73. package/pack/generated/codex/skills/rite-build/reference/wright-dispatch.md +16 -9
  74. package/pack/generated/codex/skills/rite-plan/reference/slicing.md +4 -3
  75. package/pack/generated/codex/skills/rite-polish/SKILL.md +3 -1
  76. package/pack/generated/codex/skills/rite-prove/SKILL.md +3 -0
  77. package/pack/generated/codex/skills/rite-review/SKILL.md +3 -1
  78. package/pack/generated/codex/skills/rite-temper/SKILL.md +6 -6
  79. package/pack/generated/codex/skills/rite-temper/reference/significance.md +3 -3
  80. package/pack/generated/omp/agents/devrites-slice-wright.md +18 -9
  81. package/pack/generated/omp/skills/devrites-lib/reference/standards/afk-hitl.md +4 -3
  82. package/pack/generated/omp/skills/rite-autocomplete/SKILL.md +18 -18
  83. package/pack/generated/omp/skills/rite-autocomplete/reference/decision-policy.md +2 -3
  84. package/pack/generated/omp/skills/rite-autocomplete/reference/loop.md +18 -25
  85. package/pack/generated/omp/skills/rite-autocomplete/reference/stop-conditions.md +10 -11
  86. package/pack/generated/omp/skills/rite-build/SKILL.md +13 -8
  87. package/pack/generated/omp/skills/rite-build/reference/afk-discipline.md +3 -1
  88. package/pack/generated/omp/skills/rite-build/reference/anti-patterns.md +4 -0
  89. package/pack/generated/omp/skills/rite-build/reference/checkpoint.md +33 -21
  90. package/pack/generated/omp/skills/rite-build/reference/one-slice-cycle.md +5 -4
  91. package/pack/generated/omp/skills/rite-build/reference/parallel-batch.md +30 -23
  92. package/pack/generated/omp/skills/rite-build/reference/phase-contract.md +4 -2
  93. package/pack/generated/omp/skills/rite-build/reference/wright-dispatch.md +16 -9
  94. package/pack/generated/omp/skills/rite-plan/reference/slicing.md +4 -3
  95. package/pack/generated/omp/skills/rite-polish/SKILL.md +3 -1
  96. package/pack/generated/omp/skills/rite-prove/SKILL.md +3 -0
  97. package/pack/generated/omp/skills/rite-review/SKILL.md +3 -1
  98. package/pack/generated/omp/skills/rite-temper/SKILL.md +6 -6
  99. package/pack/generated/omp/skills/rite-temper/reference/significance.md +3 -3
  100. package/pack/generated/pi/agents/devrites-code-reviewer.md +1 -1
  101. package/pack/generated/pi/agents/devrites-devex-reviewer.md +1 -1
  102. package/pack/generated/pi/agents/devrites-doubt-reviewer.md +1 -1
  103. package/pack/generated/pi/agents/devrites-evidence-scout.md +1 -1
  104. package/pack/generated/pi/agents/devrites-frontend-reviewer.md +1 -1
  105. package/pack/generated/pi/agents/devrites-performance-reviewer.md +1 -1
  106. package/pack/generated/pi/agents/devrites-plan-drafter.md +1 -1
  107. package/pack/generated/pi/agents/devrites-plan-reviewer.md +1 -1
  108. package/pack/generated/pi/agents/devrites-proof-runner.md +1 -1
  109. package/pack/generated/pi/agents/devrites-retrospector.md +1 -1
  110. package/pack/generated/pi/agents/devrites-security-auditor.md +1 -1
  111. package/pack/generated/pi/agents/devrites-simplifier-reviewer.md +1 -1
  112. package/pack/generated/pi/agents/devrites-slice-wright.md +19 -10
  113. package/pack/generated/pi/agents/devrites-spec-reviewer.md +1 -1
  114. package/pack/generated/pi/agents/devrites-strategy-reviewer.md +1 -1
  115. package/pack/generated/pi/agents/devrites-test-analyst.md +1 -1
  116. package/pack/generated/pi/agents/devrites-upgrade-planner.md +1 -1
  117. package/pack/generated/pi/skills/devrites-lib/reference/standards/afk-hitl.md +4 -3
  118. package/pack/generated/pi/skills/rite-autocomplete/SKILL.md +18 -18
  119. package/pack/generated/pi/skills/rite-autocomplete/reference/decision-policy.md +2 -3
  120. package/pack/generated/pi/skills/rite-autocomplete/reference/loop.md +18 -25
  121. package/pack/generated/pi/skills/rite-autocomplete/reference/stop-conditions.md +10 -11
  122. package/pack/generated/pi/skills/rite-build/SKILL.md +13 -8
  123. package/pack/generated/pi/skills/rite-build/reference/afk-discipline.md +3 -1
  124. package/pack/generated/pi/skills/rite-build/reference/anti-patterns.md +4 -0
  125. package/pack/generated/pi/skills/rite-build/reference/checkpoint.md +33 -21
  126. package/pack/generated/pi/skills/rite-build/reference/one-slice-cycle.md +5 -4
  127. package/pack/generated/pi/skills/rite-build/reference/parallel-batch.md +30 -23
  128. package/pack/generated/pi/skills/rite-build/reference/phase-contract.md +4 -2
  129. package/pack/generated/pi/skills/rite-build/reference/wright-dispatch.md +16 -9
  130. package/pack/generated/pi/skills/rite-plan/reference/slicing.md +4 -3
  131. package/pack/generated/pi/skills/rite-polish/SKILL.md +3 -1
  132. package/pack/generated/pi/skills/rite-prove/SKILL.md +3 -0
  133. package/pack/generated/pi/skills/rite-review/SKILL.md +3 -1
  134. package/pack/generated/pi/skills/rite-temper/SKILL.md +6 -6
  135. package/pack/generated/pi/skills/rite-temper/reference/significance.md +3 -3
  136. package/package.json +1 -1
  137. package/scripts/pi-generate.sh +52 -1
package/CHANGELOG.md CHANGED
@@ -2,6 +2,22 @@
2
2
 
3
3
  All notable changes to DevRites are documented here. The format follows [Keep a Changelog](https://keepachangelog.com/en/1.1.0/) and DevRites adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html). Releases are generated automatically by [semantic-release](https://semantic-release.gitbook.io/) from Conventional Commits on `main`.
4
4
 
5
+ ## [5.9.0](https://github.com/ViktorsBaikers/DevRites/compare/v5.8.0...v5.9.0) (2026-09-15)
6
+
7
+ ### Added
8
+
9
+ * **rite:** land proven WIP commits and keep autocomplete running ([#75](https://github.com/ViktorsBaikers/DevRites/issues/75)) ([73bf932](https://github.com/ViktorsBaikers/DevRites/commit/73bf932568d7bae34ea20ce6476c33842b123467))
10
+
11
+ ## [5.8.0](https://github.com/ViktorsBaikers/DevRites/compare/v5.7.2...v5.8.0) (2026-09-14)
12
+
13
+ ### Added
14
+
15
+ * **agents:** allowlist lean-ctx and lens tools on pi agents ([#74](https://github.com/ViktorsBaikers/DevRites/issues/74)) ([08390d3](https://github.com/ViktorsBaikers/DevRites/commit/08390d3bf3ccffec0353803e910ee7a7227ff6ba))
16
+
17
+ ### Fixed
18
+
19
+ * **rite:** re-select parallel width after a serial round ([#73](https://github.com/ViktorsBaikers/DevRites/issues/73)) ([ac8f073](https://github.com/ViktorsBaikers/DevRites/commit/ac8f073c0640ab0350b92a87a86193de39dedd57))
20
+
5
21
  ## [5.7.2](https://github.com/ViktorsBaikers/DevRites/compare/v5.7.1...v5.7.2) (2026-09-14)
6
22
 
7
23
  ### Fixed
package/README.md CHANGED
@@ -33,7 +33,7 @@ project-conventional push, tag, or PR action, and archive the workspace.
33
33
  Unattended runs may create local WIP checkpoint commits along the way, but they
34
34
  remain local unless Ship's disclosed plan includes an approved remote action.
35
35
 
36
- **Status:** [`v5.7.2`](https://github.com/ViktorsBaikers/DevRites/releases/tag/v5.7.2): see [`CHANGELOG.md`](CHANGELOG.md) for release notes.
36
+ **Status:** [`v5.9.0`](https://github.com/ViktorsBaikers/DevRites/releases/tag/v5.9.0): see [`CHANGELOG.md`](CHANGELOG.md) for release notes.
37
37
 
38
38
  This is the latest published release; `main` may contain unreleased work.
39
39
 
@@ -152,7 +152,7 @@ the original decisions and proof.
152
152
  .devrites/
153
153
  ACTIVE # active feature slug
154
154
  AFK # optional unattended-mode configuration
155
- CHECKPOINT # optional local WIP checkpoint mode
155
+ CHECKPOINT # reserved path; not a commit gate
156
156
  principles.md # project rules that gate the workflow
157
157
  specs/ # living structured capabilities
158
158
  work/<slug>/
@@ -336,10 +336,10 @@ workflow. It carries state through workspace files rather than chat. A vague
336
336
  prompt starts `devrites-interview`; `/rite-spec` and `/rite-clarify` then
337
337
  finish the only interactive window. After decision coverage is CLEAR it runs unattended,
338
338
  choosing the recommended option at each soft gate and recording the rationale
339
- in `decisions.md`. It does **not** weaken the safety gates: genuine
340
- product/scope/policy decisions, irreversible risk, human-only access/actions,
341
- an open human-owned gate, a NO-GO, exhausted `max_slices`, or low confidence
342
- still pause. Agents use bounded recovery for red tests, runtime failures, and
339
+ in `decisions.md`. It does **not** weaken blocking/escalating or irreversible-risk
340
+ gates: human-only access/actions, those gates, a NO-GO, or low confidence still
341
+ pause. Temper expand, validating gates, and default slice/agent/time/review-queue
342
+ caps do not. `--max-slices` is ignored the same way. Agents use bounded recovery for red tests, runtime failures, and
343
343
  missing technical coverage. By default the workflow stops at Seal GO with `/rite-ship` as the next step.
344
344
  The `--ship` flag (alias `--yolo`) continues through Ship preflight, discloses
345
345
  the exact Git plan, and still stops for a fresh literal `GO` plus native host
@@ -40,7 +40,7 @@ The Go surface is intentionally closed and deterministic:
40
40
 
41
41
  | Lane | Commands | Owner |
42
42
  |---|---|---|
43
- | Candidate and deterministic checks | `check candidate`, `check readiness [--emit-binding]`, `check seal`, `check task-graph`, `check path-disjoint`, `check skill-trust`, `check indexes` | Candidate validates/hashes the strict project manifest; readiness checks phase files, the required `tasks.md` slice graph, or emits the vetted Build-input binding; seal checks final files/open gates, that graph and binding, then exact candidate bindings. `check indexes` reports install manifest and optional code-index directory presence as JSON. |
43
+ | Candidate and deterministic checks | `check candidate`, `check readiness [--emit-binding]`, `check seal`, `check task-graph`, `check path-disjoint`, `parallel select`, `check skill-trust`, `check indexes` | Candidate validates/hashes the strict project manifest; readiness checks phase files, the required `tasks.md` slice graph, or emits the vetted Build-input binding; seal checks final files/open gates, that graph and binding, then exact candidate bindings. `check indexes` reports install manifest and optional code-index directory presence as JSON. `parallel select` returns the greedy path-disjoint subset ≤ cap from the supplied ready slices. |
44
44
  | Workspace observation | `observe summary`, `orient`, `observe slice <slug> <SLICE-ID>` | JSON workspace snapshot for agents (cursor, task graph, `artifact_budgets`, `bulk_files`); `orient` is an alias for `observe summary`. `observe slice` prints one `SLICE-###` section of `tasks.md` so Build reads a slice, not the file. |
45
45
  | Atomic workspace state | `state resolve`, `state merge-manifest`, `state close`, `migrate [--dry-run] [--answer id=choice]` | Go owns answer/drop/batch resolution, the recorded predecessor-chain manifest union for a release milestone, transactional close, and fail-closed v5 schema normalization. |
46
46
  | Security | `secret-scan` | `/rite-ship`, safe hooks, or an operator scans staged blobs, stdin, or touched files. |
@@ -91,7 +91,7 @@ See [`cli.md`](cli.md).
91
91
  | [`/rite-define`](../pack/.claude/skills/rite-define/SKILL.md) | define → plan | `[slug]` | Authors architecture, plan, vertical `SLICE-###` tasks, and traceability. A changed provider/consumer boundary names one canonical shared contract plus consuming provider/consumer tests; no change uses a justified no-impact statement. | `spec.md` + `decision-coverage.md` (+ `strategy.md`) + references | `architecture.md`, `plan.md`, `tasks.md`, `traceability.md`, `state.md`, `decisions.md` |
92
92
  | [`/rite-vet`](../pack/.claude/skills/rite-vet/SKILL.md) | vet | `[slug] [--cross-model] [--full]` | **Before build: every feature.** Engineering review of the defined plan: scope challenge (reuse / minimum-diff / complexity smell) + architecture / plan code-quality / test-coverage design / performance, confidence-banded with a quote-the-source verification gate; failure-mode and dependency-safety review. Hardens `plan.md` / `tasks.md` in place; writes the build-readable `test-plan.md` and a semantic `READY` verdict reconciled by the exact native reviewer; acceptance-changing deltas route via the Spec Drift Guard. Runs on every plan: depth scales to stakes (light pass on simple plans, full on big/risky), never skipped; **always in `/rite-autocomplete`**. Reviewer: `devrites-plan-reviewer` (+ optional `--cross-model`). | `plan.md` + `tasks.md` + `spec.md` (+ `strategy.md`) | `eng-review.md`, `test-plan.md`, `plan.md`, `tasks.md`, `decisions.md`, `state.md` |
93
93
  | [`/rite-plan`](../pack/.claude/skills/rite-plan/SKILL.md) | repair → plan | `[mode]` | Reslice / repair / re-order / split / unblock an active plan and return to the `plan` checkpoint; `revise` is artifact-only and `/rite-vet` is the normal resume. | spec/plan/tasks/state/drift + diff | `plan.md`, `tasks.md`, `state.md`, `decisions.md` |
94
- | [`/rite-build`](../pack/.claude/skills/rite-build/SKILL.md) | build | `[slice]` | In HITL, orchestrates one vertical slice through the sole wright; an explicit `.devrites/AFK` sentinel may chain bounded low-risk slices. Every task states exact project-relative paths; after return, the root rejects extra paths or weakened tests, runs repository proof, and maintains the strict candidate manifest. | workspace + diff + `.devrites/CHECKPOINT` | code + `state.md`, `evidence.md`, `traceability.md`, `touched-files.md` (+ local `WIP(<slug>)` commit in checkpoint mode) |
94
+ | [`/rite-build`](../pack/.claude/skills/rite-build/SKILL.md) | build | `[slice]` | In HITL, orchestrates one vertical slice through the sole wright; an explicit `.devrites/AFK` sentinel may chain bounded low-risk slices. Every task states exact project-relative paths; after return, the root rejects extra paths or weakened tests, runs independent review and repository proof, and maintains the strict candidate manifest. Lands a local `WIP(<slug>):` commit only after that check loop is green (never on wright return). | workspace + diff | code + `state.md`, `evidence.md`, `traceability.md`, `touched-files.md` (+ local `WIP(<slug>):` after checks; never a slice id) |
95
95
  | [`/rite-converge`](../pack/.claude/skills/rite-converge/SKILL.md) | converge | `[slug]` | **Recovery.** Compare live code with clarified intent, append each unmet piece as a traceable `SLICE-###`, and invalidate the old vet verdict so changed work returns through `/rite-vet`. `tasks.md` stays byte-identical when already converged. | clarified spec + plan/tasks + principles + live code | `tasks.md` (appended), `traceability.md`, `state.md`, `eng-review.md` (invalidated), `decisions.md` |
96
96
  | [`/rite-upgrade`](../pack/.claude/skills/rite-upgrade/SKILL.md) | compatibility | `[slug]` | **Conditional recovery.** Audit an older released workspace against current contracts. Only cited defects route through Clarify, Plan repair, Converge, Vet, Prove, Polish, Review, or Seal. Ambiguous candidate scope is a gap; age/cursor form alone is never a defect, and old passes are never synthesized. | active workspace + named current contracts | admitted unfinished artifacts via their owning rite |
97
97
  | [`/rite-prove`](../pack/.claude/skills/rite-prove/SKILL.md) | prove | `[feature-slug]` | Positive, discriminating tests + build/runtime/browser proof; checks the same candidate before/after commands and binds evidence to its digest. | `traceability.md` + workspace + candidate | `evidence.md`, `browser-evidence.md`, `traceability.md`, `state.md` |
@@ -99,7 +99,7 @@ See [`cli.md`](cli.md).
99
99
  | [`/rite-review`](../pack/.claude/skills/rite-review/SKILL.md) | review | `[scope: slice N \| feature] [--full]` | Feature-scoped parallel Spec + Standards review of the closed candidate; binds `review.md` to its digest. | workspace + candidate | `review.md`, `evidence.md`, `state.md` |
100
100
  | [`/rite-seal`](../pack/.claude/skills/rite-seal/SKILL.md) | seal | `[feature-slug] [--full]` | Candidate-bound GO / NO-GO **decision**. Rechecks exact proof/review bindings and runs no git; on GO sets `Next step: /rite-ship`. | all artifacts + candidate | `seal.md`, `state.md` |
101
101
  | [`/rite-ship`](../pack/.claude/skills/rite-ship/SKILL.md) | ship | `[slug]` | Starts with a read-only Seal/candidate check and exact plan disclosure, then requires fresh literal `GO`. Only then may it collapse checkpoints, stage exact candidate paths, validate staged scope/bytes/bindings/secrets, commit and reverify, perform optional separately approved push/tag/PR actions, and archive/close. | `seal.md` + candidate + Git index | `ship.md`, `state.md`, archive |
102
- | [`/rite-autocomplete`](../pack/.claude/skills/rite-autocomplete/SKILL.md) | (orchestrator) | `[idea] [--ship\|--yolo] [--max-slices N] [--full] [--cross-model]` | Full lifecycle (spec → clarify → … → seal → ship). Spec + clarify form the one interactive window; AFK/checkpoint mode arms only after decision coverage is CLEAR. Pauses for genuine human-owned decisions/actions, NO-GO, or budget exhaustion; objective red checks use bounded technical recovery. Default stops at Seal GO; `--ship` (`--yolo`) reaches Ship preflight but still requires fresh literal `GO` and native approval; `--full` selects the Full profile and `--cross-model` arms Vet's second opinion. | idea + workspace | whole workspace (drives every phase) |
102
+ | [`/rite-autocomplete`](../pack/.claude/skills/rite-autocomplete/SKILL.md) | (orchestrator) | `[idea] [--ship\|--yolo] [--max-slices N] [--full] [--cross-model]` | Full lifecycle (spec → clarify → … → seal → ship). Spec + clarify form the one interactive window; AFK/checkpoint mode arms only after decision coverage is CLEAR. Pauses for irreversible risk, blocking/escalating, human-only access, or NO-GO — not temper expand, validating gates, `--max-slices`, or default resource caps. Objective red checks use bounded technical recovery. Default stops at Seal GO; `--ship` (`--yolo`) reaches Ship preflight but still requires fresh literal `GO` and native approval; `--full` selects the Full profile and `--cross-model` arms Vet's second opinion. | idea + workspace | whole workspace (drives every phase) |
103
103
  | [`/rite-quick`](../pack/.claude/skills/rite-quick/SKILL.md) | (express) | `<change>` | Express lane for a **small, reversible, unambiguous** change: one-line contract → TDD build → scoped prove → review-lite → ship, no full artifact tree. **Significance gate first**: auth / migration / public-API / destructive / multi-slice / ambiguous → escalates to `/rite-spec`. Triggers: "quick fix", "small change", "tiny tweak", "just do X". | the change + codebase | code + commit (optional `brief.md` / `evidence.md`) |
104
104
  | [`/rite-frame`](../pack/.claude/skills/rite-frame/SKILL.md) | lens | `[ask \| diff]` | Pre-flight + self-audit lens for ad-hoc work the lifecycle gates never see: **FRAME** turns an imperative ask into a falsifiable success criterion + verify command before code; **AUDIT** checks a raw diff against the four LLM coding failure modes (silent assumption / overcomplication / out-of-scope edit / unverifiable goal). Top of `/rite-quick` or before a plain "just do X". | the ask / a raw diff | success criterion + verify command (inline) |
105
105
  | [`/rite-adopt`](../pack/.claude/skills/rite-adopt/SKILL.md) | onboard | `[path or area to adopt] [+ what you want to build next]` | Reverse-derive the existing baseline and next objective; propose any durable project guidance in the nearest native instruction file. | codebase + project instructions | workspace spec/state artifacts + optional guidance proposal |
@@ -16,6 +16,7 @@ workflow strategy.
16
16
  | `check readiness --emit-binding <slug>` | Render the exact stable Build-input binding for Vet to record after review. |
17
17
  | `check seal <slug>` | Check files required by target Phase `seal`, open human gates, the `tasks.md` slice graph, canonical `AC-###` presence when `tasks.md`/`test-plan.md` are required, the stable Build-input binding, and exact candidate bindings. |
18
18
  | `check path-disjoint [--root <dir>] [<json-file> | -]` | Verify slice path sets are pairwise disjoint. |
19
+ | `parallel select --cap <1-10> [--root <dir>] [<json-file> | -]` | Choose the greedy path-disjoint subset ≤ cap from supplied ready slices. |
19
20
  | `check task-graph <slug>` | Validate `tasks.md` slice dependency graph for cycles, unknown deps, malformed tokens, duplicate IDs, missing `Dependencies`, and `depends_on` mismatch. |
20
21
  | `check skill-trust <path>` | Scan one skill/agent Markdown file for structural trust violations. |
21
22
  | `observe summary <slug>` | Emit sanitized JSON workspace summary from one retained observation. `task_graph.ok` is true iff `task_graph.problems` is empty; `problems` lists cycle, unknown-dep, malformed-token, duplicate-id, and missing-`Dependencies` blockers. |
@@ -21,7 +21,7 @@ budgets, IDs, and read order live in
21
21
  .devrites/
22
22
  ACTIVE
23
23
  AFK # optional run-mode sentinel
24
- CHECKPOINT # optional local WIP checkpoint sentinel
24
+ CHECKPOINT # reserved path; not a commit gate
25
25
  principles.md # ratified project invariants
26
26
  specs/ # living capability Markdown, maintained by skills
27
27
  work/
package/docs/flow.md CHANGED
@@ -304,8 +304,10 @@ Each workspace-operating lifecycle skill reads durable state from
304
304
  `.devrites/work/<feature-slug>/` before acting and reports the owning create or
305
305
  selection command when no workspace is active. Utility and on-ramp skills use
306
306
  their own stated preconditions. The optional `.devrites/AFK` sentinel sits
307
- beside `ACTIVE` and toggles the session-level run mode; `.devrites/CHECKPOINT`
308
- separately enables eligible local WIP checkpoint commits.
307
+ beside `ACTIVE` and toggles the session-level run mode. Local `WIP(<slug>):`
308
+ checkpoints land after independent review and fail-on-red proof are green, not
309
+ when a wright returns; `.devrites/CHECKPOINT` is a reserved
310
+ path, not a gate.
309
311
 
310
312
  ```mermaid
311
313
  erDiagram
@@ -130,8 +130,8 @@ Isolation does not authorize same-worktree throughput. Parallel writers are allo
130
130
  **only** under the `/rite-build` batch path when path-disjoint eligibility, abort-batch,
131
131
  and a control `parallel-lease.md` apply. `--parallel N` (or the AFK sentinel
132
132
  `max_parallel`, default cap 10 under `/rite-autocomplete`) is a **cap**: each round runs
133
- the largest eligible set, recomputes after every integrate, and repeats until no pending
134
- slice remains; fewer than two eligible slices fall back to the serial cycle (see
133
+ the largest eligible set, recomputes after every completed round, and repeats until no
134
+ pending slice remains; a one-slice round is serial for that round only (see
135
135
  [`parallel-batch.md`](../pack/.claude/skills/rite-build/reference/parallel-batch.md)).
136
136
  Same-worktree multi-writer and root-emulated worktrees remain forbidden; default
137
137
  `/rite-build` stays one writer across linked worktrees.
package/docs/skills.md CHANGED
@@ -222,7 +222,7 @@ and `Shipped`. Utility commands keep the same compact labels and one-next-action
222
222
 
223
223
  | Skill | What It Does | Use When |
224
224
  |---|---|---|
225
- | [`rite-autocomplete`](../pack/.claude/skills/rite-autocomplete/SKILL.md) | Runs the lifecycle unattended (spec → clarify → … → seal → ship), choosing the recommended option at each soft gate and recording the rationale in `decisions.md`.<br>A vague prompt gets one up-front spec/clarify window; after CLEAR it does not iterate per phase.<br>It pauses only for genuine product/scope/policy decisions, irreversible risk, human-only access, NO-GO, or budget exhaustion.<br>Objective red checks use bounded technical recovery, including cold-resume from retained one-shot evidence and one-time migration of stale missing-writer stops when an upgraded contract supplies the first supported writer. That migration applies once; the first real materializer failure is attempt one under normal bounded recovery. Internal `NEEDS_REPLAN` and new agent-owned review findings stay caller-owned backward edges, not user-facing stops. Spending an action authorization blocks another real execution, not offline repair.<br>Default stops at Seal GO. `--ship` (alias `--yolo`) reaches Ship preflight but still requires a fresh literal `GO` and native approval. | "Autocomplete", "do the whole thing", "run the full cycle", "one-shot this feature". |
225
+ | [`rite-autocomplete`](../pack/.claude/skills/rite-autocomplete/SKILL.md) | Runs the lifecycle unattended (spec → clarify → … → seal → ship), choosing the recommended option at each soft gate and recording the rationale in `decisions.md`.<br>A vague prompt gets one up-front spec/clarify window; after CLEAR it does not iterate per phase.<br>It pauses only for irreversible risk, blocking/escalating gates, human-only access, or NO-GO — not temper expand, validating gates, or default slice/agent/time/review-queue caps.<br>Objective red checks use bounded technical recovery, including cold-resume from retained one-shot evidence and one-time migration of stale missing-writer stops when an upgraded contract supplies the first supported writer. That migration applies once; the first real materializer failure is attempt one under normal bounded recovery. Internal `NEEDS_REPLAN` and new agent-owned review findings stay caller-owned backward edges, not user-facing stops. Spending an action authorization blocks another real execution, not offline repair.<br>Default stops at Seal GO. `--ship` (alias `--yolo`) reaches Ship preflight but still requires a fresh literal `GO` and native approval. | "Autocomplete", "do the whole thing", "run the full cycle", "one-shot this feature". |
226
226
  | [`rite-zoom-out`](../pack/.claude/skills/rite-zoom-out/SKILL.md) | Map the modules, callers, callees, and decisions in an unfamiliar area using the project's domain glossary. | Explicit-only: `/rite-zoom-out` / `/rite zoom-out`. |
227
227
  | [`rite-prototype`](../pack/.claude/skills/rite-prototype/SKILL.md) | Throwaway code answering ONE design question: logic harness OR 2 to 4 UI variations on one route. | Explicit-only: `/rite-prototype` / `/rite prototype`. |
228
228
  | [`rite-handoff`](../pack/.claude/skills/rite-handoff/SKILL.md) | Compact chat session → handoff doc. References existing `.devrites/work/<slug>/` artifacts by path. | Explicit-only: `/rite-handoff` / `/rite handoff`. |
package/docs/usage.md CHANGED
@@ -69,7 +69,7 @@ Project-root sentinel (outside the workspace):
69
69
  | File | Created by | Holds |
70
70
  |---|---|---|
71
71
  | `.devrites/AFK` | you (presence = AFK mode active) | optional YAML: `max_slices`, `notify`, `allow_gates`. Empty file = AFK with defaults. See [`pack/.claude/skills/devrites-lib/reference/standards/afk-hitl.md`](../pack/.claude/skills/devrites-lib/reference/standards/afk-hitl.md). |
72
- | `.devrites/CHECKPOINT` | you or `/rite-autocomplete` (presence = checkpoint mode) | empty sentinel. When set, `/rite-build` commits each proven slice local-only as `WIP(<slug>)` so a crash mid-build loses neither source nor reasoning; after literal `GO`, `/rite-ship` collapses an eligible disclosed WIP run into the feature commit. See [`pack/.claude/skills/rite-build/reference/checkpoint.md`](../pack/.claude/skills/rite-build/reference/checkpoint.md). |
72
+ | `.devrites/CHECKPOINT` | unused as a gate | reserved path (not a candidate). After Independent Build review and fail-on-red proof are green (not when a wright returns), `/rite-build` / `/rite-prove` / `/rite-polish` / `/rite-review` land a local `WIP(<slug>): <imperative summary>` commit (serial: checkpoint git recipe or no-op if nothing staged; parallel: `devrites-engine parallel integrate`); `/rite-ship` later collapses an eligible WIP run. See [`pack/.claude/skills/rite-build/reference/checkpoint.md`](../pack/.claude/skills/rite-build/reference/checkpoint.md). |
73
73
 
74
74
  The shape of this directory is also documented in
75
75
  [`flow.md` § Workspace state model](flow.md#7-workspace-state-model).
@@ -368,7 +368,7 @@ full list.
368
368
  ## 11) Full unattended lifecycle: `/rite-autocomplete`
369
369
 
370
370
  ```text
371
- /rite-autocomplete "add CSV export for admins" --max-slices 8
371
+ /rite-autocomplete "add CSV export for admins"
372
372
  → vague idea → runs devrites-interview, then /rite-spec + /rite-clarify in
373
373
  the only interactive window
374
374
  → Decision coverage: CLEAR → arms AFK, then drives: /rite-temper →
@@ -384,17 +384,18 @@ You: GO # → commit, optional approved push/tag/PR, then
384
384
 
385
385
  Add `--ship` (alias `--yolo`) to continue through `/rite-ship` preflight.
386
386
  Autocomplete discloses the exact Git plan, then still stops for a fresh literal
387
- `GO` and native host approval. It also pauses for genuine product/scope/policy
388
- decisions, irreversible risk, human-only access/actions, a NO-GO, exhausted
389
- `max_slices`, or low confidence. Objective red checks use bounded recovery
390
- instead. Args:
387
+ `GO` and native host approval. It also pauses for irreversible risk, human-only access/actions, blocking or
388
+ escalating gates, a NO-GO, or low confidence — not for temper expand, validating
389
+ gates, `--max-slices`, or default slice/agent/time/review-queue caps.
390
+ Objective red checks use bounded recovery instead. Args:
391
391
  `[idea] [--ship|--yolo] [--max-slices N] [--full] [--cross-model]`.
392
392
  Optional flags are inactive unless their exact token occurs in the invocation;
393
- `--max-slices` requires one positive base-10 integer. `--full` selects the Full
393
+ `--max-slices` requires one positive base-10 integer and does not cap this run.
394
+ `--full` selects the Full
394
395
  execution profile, and `--cross-model` arms Vet's second opinion.
395
- Autocomplete never rewrites an existing `.devrites/AFK`. After Vet fixes the
396
- pending-slice count, it stores the lower effective budget in `state.md`; an
397
- existing remaining counter can only stay the same or decrease.
396
+ Autocomplete never rewrites an existing `.devrites/AFK`. After Vet it seeds
397
+ remaining from the pending-slice count and ignores leftover remaining and
398
+ `--max-slices`.
398
399
 
399
400
  ## Checking in
400
401
 
@@ -5,6 +5,7 @@ import (
5
5
  "fmt"
6
6
  "io"
7
7
  "os"
8
+ "strconv"
8
9
  "strings"
9
10
  )
10
11
 
@@ -71,6 +72,71 @@ func runPathDisjoint(args []string, stdin io.Reader, stdout, stderr io.Writer) i
71
72
  return ExitOK
72
73
  }
73
74
 
75
+ func cmdSelect(args []string, stdin io.Reader, stdout, stderr io.Writer) int {
76
+ const usage = "usage: parallel select --cap <1-10> [--root <dir>] [<json-file>|-]"
77
+ root := ""
78
+ jsonPath := "-"
79
+ capN := 0
80
+ sawCap := false
81
+ for i := 0; i < len(args); i++ {
82
+ switch args[i] {
83
+ case "--root":
84
+ if i+1 >= len(args) {
85
+ fmt.Fprintln(stderr, usage)
86
+ return ExitUsage
87
+ }
88
+ i++
89
+ root = args[i]
90
+ case "--cap":
91
+ if i+1 >= len(args) {
92
+ fmt.Fprintln(stderr, usage)
93
+ return ExitUsage
94
+ }
95
+ i++
96
+ n, err := strconv.Atoi(args[i])
97
+ if err != nil {
98
+ fmt.Fprintf(stderr, "parallel select: --cap must be an integer\n")
99
+ return ExitUsage
100
+ }
101
+ capN = n
102
+ sawCap = true
103
+ case "-h", "--help":
104
+ fmt.Fprintln(stdout, usage)
105
+ return ExitOK
106
+ default:
107
+ if args[i] != "-" && strings.HasPrefix(args[i], "-") {
108
+ fmt.Fprintf(stderr, "parallel select: unknown flag %s\n", args[i])
109
+ return ExitUsage
110
+ }
111
+ jsonPath = args[i]
112
+ }
113
+ }
114
+ if !sawCap {
115
+ fmt.Fprintln(stderr, usage)
116
+ return ExitUsage
117
+ }
118
+ data, err := readJSONInput(jsonPath, stdin)
119
+ if err != nil {
120
+ fmt.Fprintf(stderr, "parallel select: %v\n", err)
121
+ return ExitBlocked
122
+ }
123
+ slices, err := ParseSlicesJSON(data)
124
+ if err != nil {
125
+ fmt.Fprintf(stderr, "parallel select: %v\n", err)
126
+ return ExitBlocked
127
+ }
128
+ selected, err := SelectGreedy(capN, slices, root)
129
+ if err != nil {
130
+ fmt.Fprintf(stderr, "parallel select: %v\n", err)
131
+ if strings.Contains(err.Error(), "cap must be") {
132
+ return ExitUsage
133
+ }
134
+ return ExitBlocked
135
+ }
136
+ fmt.Fprintln(stdout, formatSelect(selected))
137
+ return ExitOK
138
+ }
139
+
74
140
  func runParallel(args []string, stdin io.Reader, stdout, stderr io.Writer) int {
75
141
  if len(args) == 0 {
76
142
  fmt.Fprintln(stderr, parallelUsage())
@@ -98,6 +164,8 @@ func runParallel(args []string, stdin io.Reader, stdout, stderr io.Writer) int {
98
164
  return cmdLeaseClear(rest, stdout, stderr)
99
165
  case "check-disjoint", "path-disjoint":
100
166
  return runPathDisjoint(rest, stdin, stdout, stderr)
167
+ case "select":
168
+ return cmdSelect(rest, stdin, stdout, stderr)
101
169
  case "-h", "--help", "help":
102
170
  fmt.Fprintln(stdout, parallelUsage())
103
171
  return ExitOK
@@ -121,6 +189,7 @@ Subcommands:
121
189
  lease-read --root --slug [--field name]
122
190
  lease-clear --root --slug
123
191
  check-disjoint [--root] [<json-file>|-]
192
+ select --cap <1-10> [--root] [<json-file>|-]
124
193
 
125
194
  Exit codes: 0 ok, 2 usage, 3 blocked`)
126
195
  }
@@ -211,6 +211,10 @@ func mergeFFOnly(repo, commit string) error {
211
211
  return err
212
212
  }
213
213
 
214
+ func commitSubject(repo, rev string) (string, error) {
215
+ return git(repo, "log", "-1", "--format=%s", rev)
216
+ }
217
+
214
218
  func cherryPickNoCommit(repo, fromExclusive, toInclusive string) error {
215
219
  _, err := git(repo, "cherry-pick", "-n", fromExclusive+".."+toInclusive)
216
220
  return err
@@ -219,3 +223,7 @@ func cherryPickNoCommit(repo, fromExclusive, toInclusive string) error {
219
223
  func cherryPickAbort(repo string) {
220
224
  _, _ = git(repo, "cherry-pick", "--abort")
221
225
  }
226
+
227
+ func cherryPickQuit(repo string) {
228
+ _, _ = git(repo, "cherry-pick", "--quit")
229
+ }
@@ -5,6 +5,7 @@ import (
5
5
  "fmt"
6
6
  "os"
7
7
  "path/filepath"
8
+ "regexp"
8
9
  "sort"
9
10
  "strings"
10
11
 
@@ -285,10 +286,8 @@ func integrateLocked(opts IntegrateOpts) (tip string, lease *Lease, err error) {
285
286
  }
286
287
 
287
288
  union := make([]string, 0, len(lease.Slices))
288
- ids := make([]string, 0, len(lease.Slices))
289
289
  for _, sl := range lease.Slices {
290
290
  union = append(union, sl.Paths...)
291
- ids = append(ids, sl.ID)
292
291
  }
293
292
  if opts.ApplyToControl {
294
293
  // FF into control must not silently absorb or clobber the user's
@@ -381,14 +380,19 @@ func integrateLocked(opts IntegrateOpts) (tip string, lease *Lease, err error) {
381
380
 
382
381
  if err := cherryPickNoCommit(stageWT, base, tc); err != nil {
383
382
  cherryPickAbort(stageWT)
384
- return fail(fmt.Errorf("squash apply failed for slice %s: %w", sl.ID, err))
383
+ return fail(fmt.Errorf("apply failed for slice %s: %w", sl.ID, err))
385
384
  }
385
+ subj, err := commitSubject(opts.Root, tc)
386
+ if err != nil {
387
+ return fail(fmt.Errorf("transfer subject for %s: %w", sl.ID, err))
388
+ }
389
+ if _, err = stagePathsAndCommit(stageWT, controlCommitMessage(opts.Slug, subj), sl.Paths); err != nil {
390
+ return fail(fmt.Errorf("control commit for %s: %w", sl.ID, err))
391
+ }
392
+ cherryPickQuit(stageWT)
386
393
  }
387
-
388
- msg := fmt.Sprintf("WIP(%s): parallel batch %s (%s)\n\n[devrites-context]\nslices: %s\nbase: %s",
389
- opts.Slug, lease.BatchID, strings.Join(ids, ", "), strings.Join(ids, ", "), base)
390
- if tip, err = stagePathsAndCommit(stageWT, msg, union); err != nil {
391
- return fail(fmt.Errorf("squash commit: %w", err))
394
+ if tip, err = headSHA(stageWT); err != nil {
395
+ return fail(fmt.Errorf("integrate tip: %w", err))
392
396
  }
393
397
  if err := removeWorktree(opts.Root, stageWT); err != nil {
394
398
  if _, statErr := os.Stat(stageWT); statErr == nil {
@@ -414,12 +418,36 @@ func integrateLocked(opts IntegrateOpts) (tip string, lease *Lease, err error) {
414
418
  } else if err := ensureControlAtBase(opts.Root, base); err != nil {
415
419
  return fail(err)
416
420
  }
417
- // Without --apply-to-control the lease stays running: the squash commit
418
- // lives only on the integrate branch, and a running lease keeps a
419
- // non-forced cleanup from discarding the refs that hold it.
421
+ // Without --apply-to-control the lease stays running: the integrate
422
+ // commits live only on the integrate branch, and a running lease keeps a
423
+ // non-forced cleanup from discarding the refs that hold them.
420
424
  return tip, lease, nil
421
425
  }
422
426
 
427
+ var (
428
+ sliceTaskID = regexp.MustCompile(`(?i)\bSLICE-\d+\b`)
429
+ sliceProcessMark = regexp.MustCompile(`(?i)\bthis slice\b|\bslice\s+\d+\b`)
430
+ existingWIPPrefix = regexp.MustCompile(`(?i)^WIP\([^)]+\):\s*`)
431
+ )
432
+
433
+ // controlCommitMessage is the local unpushed subject/body landed on control.
434
+ // Ship collapse still keys off the WIP(<slug>): prefix; the rest is a human
435
+ // summary with DevRites process marks stripped (SLICE-###, "this slice",
436
+ // "slice N"). Ordinary English such as "array slice" is left intact.
437
+ func controlCommitMessage(slug, transferSubject string) string {
438
+ prefix := fmt.Sprintf("WIP(%s): ", slug)
439
+ line := strings.TrimSpace(strings.SplitN(transferSubject, "\n", 2)[0])
440
+ line = existingWIPPrefix.ReplaceAllString(line, "")
441
+ line = sliceTaskID.ReplaceAllString(line, "")
442
+ line = sliceProcessMark.ReplaceAllString(line, "")
443
+ line = strings.TrimSpace(strings.Trim(line, ":-"))
444
+ line = strings.Join(strings.Fields(line), " ")
445
+ if line == "" {
446
+ line = "land proven work"
447
+ }
448
+ return prefix + line + "\n\n[devrites-context]\n"
449
+ }
450
+
423
451
  func pathListsEqual(a, b []string) bool {
424
452
  if len(a) != len(b) {
425
453
  return false
@@ -470,7 +498,7 @@ func salvageSlice(sl LeaseSlice, batchID string) (string, error) {
470
498
  return "", nil
471
499
  }
472
500
  return stagePathsAndCommit(sl.WorktreePath,
473
- fmt.Sprintf("devrites: salvage %s WIP (%s)", sl.ID, batchID), sl.Paths)
501
+ fmt.Sprintf("devrites: salvage WIP (%s)", batchID), sl.Paths)
474
502
  }
475
503
 
476
504
  // Cleanup removes worker worktrees and clears the lease. A complete batch
@@ -19,7 +19,7 @@ func commitIn(t *testing.T, wt, file, marker string) string {
19
19
  t.Fatal(err)
20
20
  }
21
21
  gitOk(t, wt, "add", file)
22
- gitOk(t, wt, "commit", "-m", "slice "+marker)
22
+ gitOk(t, wt, "commit", "-m", "WIP(demo-feature): add "+marker+" helper")
23
23
  return gitOk(t, wt, "rev-parse", "HEAD")
24
24
  }
25
25
 
@@ -426,3 +426,31 @@ func TestCleanupWarnsOnFailedBranchCleanup(t *testing.T) {
426
426
  t.Fatalf("cleanup should still clear the lease, got %v", err)
427
427
  }
428
428
  }
429
+
430
+ func TestControlCommitMessage(t *testing.T) {
431
+ t.Parallel()
432
+ got := controlCommitMessage("admin-report", "WIP(admin-report): SLICE-003 add CSV export")
433
+ wantPrefix := "WIP(admin-report): add CSV export\n"
434
+ if !strings.HasPrefix(got, wantPrefix) {
435
+ t.Fatalf("subject %q want prefix %q", got, wantPrefix)
436
+ }
437
+ if strings.Contains(got, "SLICE-") || strings.Contains(strings.ToLower(got), "slice-003") {
438
+ t.Fatalf("message still names a slice id: %q", got)
439
+ }
440
+ got = controlCommitMessage("admin-report", "WIP(other): complete slice 3")
441
+ if !strings.HasPrefix(got, "WIP(admin-report): complete\n") {
442
+ t.Fatalf("foreign WIP prefix / slice N: %q", got)
443
+ }
444
+ got = controlCommitMessage("admin-report", "add this slice CSV")
445
+ if !strings.HasPrefix(got, "WIP(admin-report): add CSV\n") {
446
+ t.Fatalf("this slice: %q", got)
447
+ }
448
+ got = controlCommitMessage("admin-report", "keep array slice helper")
449
+ if !strings.HasPrefix(got, "WIP(admin-report): keep array slice helper\n") {
450
+ t.Fatalf("ordinary English stripped: %q", got)
451
+ }
452
+ got = controlCommitMessage("admin-report", "SLICE-001")
453
+ if !strings.HasPrefix(got, "WIP(admin-report): land proven work\n") {
454
+ t.Fatalf("empty summary after strip: %q", got)
455
+ }
456
+ }
@@ -216,6 +216,13 @@ func TestCleanupSalvagesAbortedWork(t *testing.T) {
216
216
  if body := gitOk(t, repo, "show", sb.Commit+":src/b_new.go"); !strings.Contains(body, "Bnew") {
217
217
  t.Fatalf("salvaged file content missing: %q", body)
218
218
  }
219
+ subj := gitOk(t, repo, "log", "-1", "--format=%s", sb.Commit)
220
+ if strings.Contains(subj, "slice-b") || strings.Contains(subj, "SLICE-") {
221
+ t.Fatalf("salvage subject names a slice id: %q", subj)
222
+ }
223
+ if !strings.HasPrefix(subj, "devrites: salvage WIP (") {
224
+ t.Fatalf("salvage subject %q", subj)
225
+ }
219
226
 
220
227
  // Worktrees removed, lease cleared, kept branches still resolve.
221
228
  for _, wt := range []string{wtA, wtB, wtC} {
@@ -375,14 +382,14 @@ func TestIntegrateDivergentSiblings(t *testing.T) {
375
382
  t.Fatal(err)
376
383
  }
377
384
  gitOk(t, wtA, "add", "src/a.go")
378
- gitOk(t, wtA, "commit", "-m", "slice-a green")
385
+ gitOk(t, wtA, "commit", "-m", "WIP(demo-feature): add A helper")
379
386
  tcA := gitOk(t, wtA, "rev-parse", "HEAD")
380
387
 
381
388
  if err := os.WriteFile(filepath.Join(wtB, "src", "b.go"), []byte("package main\n\nfunc B() {}\n"), 0o644); err != nil {
382
389
  t.Fatal(err)
383
390
  }
384
391
  gitOk(t, wtB, "add", "src/b.go")
385
- gitOk(t, wtB, "commit", "-m", "slice-b green")
392
+ gitOk(t, wtB, "commit", "-m", "WIP(demo-feature): add B helper")
386
393
  tcB := gitOk(t, wtB, "rev-parse", "HEAD")
387
394
 
388
395
  if _, err := RecordGreen(repo, slug, "slice-a", tcA); err != nil {
@@ -406,12 +413,17 @@ func TestIntegrateDivergentSiblings(t *testing.T) {
406
413
  if head != tip {
407
414
  t.Fatalf("control head %s want tip %s", head, tip)
408
415
  }
409
- if n := gitOk(t, repo, "rev-list", "--count", base+"..HEAD"); n != "1" {
410
- t.Fatalf("control should gain exactly one squash commit, got %s", n)
416
+ if n := gitOk(t, repo, "rev-list", "--count", base+"..HEAD"); n != "2" {
417
+ t.Fatalf("control should gain one commit per sibling, got %s", n)
411
418
  }
412
- subj := gitOk(t, repo, "log", "-1", "--format=%s")
413
- if !strings.HasPrefix(subj, "WIP(demo-feature):") {
414
- t.Fatalf("squash subject %q missing WIP(demo-feature): prefix", subj)
419
+ log := gitOk(t, repo, "log", "--format=%s", base+"..HEAD")
420
+ for _, subj := range strings.Split(log, "\n") {
421
+ if !strings.HasPrefix(subj, "WIP(demo-feature):") {
422
+ t.Fatalf("control subject %q missing WIP(demo-feature): prefix", subj)
423
+ }
424
+ if strings.Contains(strings.ToUpper(subj), "SLICE-") {
425
+ t.Fatalf("control subject %q still names a slice id", subj)
426
+ }
415
427
  }
416
428
  a := mustRead(t, filepath.Join(repo, "src", "a.go"))
417
429
  b := mustRead(t, filepath.Join(repo, "src", "b.go"))
@@ -428,7 +440,7 @@ func TestIntegrateDivergentSiblings(t *testing.T) {
428
440
  }
429
441
 
430
442
  // TestIntegrateWithoutApplyKeepsLeaseRunning proves a flag-less integrate
431
- // stages the squash commit on the integrate branch but leaves the lease
443
+ // stages the per-sibling commits on the integrate branch but leaves the lease
432
444
  // running and control at base, so a later non-forced cleanup cannot discard
433
445
  // the only refs holding the integrated tree.
434
446
  func TestIntegrateWithoutApplyKeepsLeaseRunning(t *testing.T) {
@@ -441,7 +453,7 @@ func TestIntegrateWithoutApplyKeepsLeaseRunning(t *testing.T) {
441
453
  t.Fatal(err)
442
454
  }
443
455
  if tip == "" || tip == base {
444
- t.Fatalf("expected staged squash tip, got %s", tip)
456
+ t.Fatalf("expected staged integrate tip, got %s", tip)
445
457
  }
446
458
  if lease.Status != StatusRunning {
447
459
  t.Fatalf("status=%s want running without --apply-to-control", lease.Status)
@@ -451,13 +463,13 @@ func TestIntegrateWithoutApplyKeepsLeaseRunning(t *testing.T) {
451
463
  }
452
464
  ibranch := IntegrateBranchName(slug, batch)
453
465
  if got := gitOk(t, repo, "rev-parse", ibranch); got != tip {
454
- t.Fatalf("integrate branch %s want squash tip %s", got, tip)
466
+ t.Fatalf("integrate branch %s want integrate tip %s", got, tip)
455
467
  }
456
468
  if _, err := Cleanup(repo, slug, false); err == nil {
457
469
  t.Fatal("cleanup on running lease must refuse without --force")
458
470
  }
459
471
  // Forced cleanup salvages then removes; the integrate branch holds the
460
- // squash tip, so it is kept and reported rather than deleted.
472
+ // integrate tip, so it is kept and reported rather than deleted.
461
473
  if _, err := Cleanup(repo, slug, true); err != nil {
462
474
  t.Fatal(err)
463
475
  }
@@ -468,7 +480,7 @@ func TestIntegrateWithoutApplyKeepsLeaseRunning(t *testing.T) {
468
480
 
469
481
  // TestIntegrateFailedLeaseRepairsAndRetries proves integrate-failed is not a
470
482
  // dead end: a repaired transfer re-records on the failed lease and a retried
471
- // integrate completes into one squash commit on control.
483
+ // integrate completes into one control commit per sibling.
472
484
  func TestIntegrateFailedLeaseRepairsAndRetries(t *testing.T) {
473
485
  repo, base := setupRepo(t)
474
486
  slug, batch := "demo-feature", "batch1"
@@ -506,8 +518,8 @@ func TestIntegrateFailedLeaseRepairsAndRetries(t *testing.T) {
506
518
  } else if lease.Status != StatusComplete {
507
519
  t.Fatalf("status=%s want complete", lease.Status)
508
520
  }
509
- if n := gitOk(t, repo, "rev-list", "--count", base+"..HEAD"); n != "1" {
510
- t.Fatalf("expected one squash commit on control, got %s", n)
521
+ if n := gitOk(t, repo, "rev-list", "--count", base+"..HEAD"); n != "2" {
522
+ t.Fatalf("expected one control commit per sibling, got %s", n)
511
523
  }
512
524
  }
513
525