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.
- package/CHANGELOG.md +16 -0
- package/README.md +2 -2
- package/docs/architecture.md +4 -4
- package/docs/command-map.md +3 -3
- package/docs/engine/commands.md +1 -0
- package/docs/engine/workspace-schema.md +1 -1
- package/docs/flow.md +4 -2
- package/docs/orchestration.md +2 -2
- package/docs/skills.md +1 -1
- package/docs/usage.md +11 -10
- package/engine/internal/parallel/cli.go +69 -0
- package/engine/internal/parallel/git.go +8 -0
- package/engine/internal/parallel/ops.go +40 -12
- package/engine/internal/parallel/ops_test.go +29 -1
- package/engine/internal/parallel/parallel_test.go +26 -14
- package/engine/internal/parallel/select.go +76 -0
- package/engine/internal/parallel/select_test.go +95 -0
- package/pack/.claude/agents/devrites-slice-wright.md +18 -9
- package/pack/.claude/settings.json +1 -0
- package/pack/.claude/skills/devrites-lib/reference/standards/afk-hitl.md +4 -3
- package/pack/.claude/skills/rite-autocomplete/SKILL.md +18 -18
- package/pack/.claude/skills/rite-autocomplete/reference/decision-policy.md +2 -3
- package/pack/.claude/skills/rite-autocomplete/reference/loop.md +18 -25
- package/pack/.claude/skills/rite-autocomplete/reference/stop-conditions.md +10 -11
- package/pack/.claude/skills/rite-build/SKILL.md +13 -8
- package/pack/.claude/skills/rite-build/reference/afk-discipline.md +3 -1
- package/pack/.claude/skills/rite-build/reference/anti-patterns.md +4 -0
- package/pack/.claude/skills/rite-build/reference/checkpoint.md +33 -21
- package/pack/.claude/skills/rite-build/reference/one-slice-cycle.md +5 -4
- package/pack/.claude/skills/rite-build/reference/parallel-batch.md +30 -23
- package/pack/.claude/skills/rite-build/reference/phase-contract.md +4 -2
- package/pack/.claude/skills/rite-build/reference/wright-dispatch.md +16 -9
- package/pack/.claude/skills/rite-plan/reference/slicing.md +4 -3
- package/pack/.claude/skills/rite-polish/SKILL.md +3 -1
- package/pack/.claude/skills/rite-prove/SKILL.md +3 -0
- package/pack/.claude/skills/rite-review/SKILL.md +3 -1
- package/pack/.claude/skills/rite-temper/SKILL.md +6 -6
- package/pack/.claude/skills/rite-temper/reference/significance.md +3 -3
- package/pack/generated/claude/agents/devrites-slice-wright.md +18 -9
- package/pack/generated/claude/settings.json +1 -0
- package/pack/generated/claude/skills/devrites-lib/reference/standards/afk-hitl.md +4 -3
- package/pack/generated/claude/skills/rite-autocomplete/SKILL.md +18 -18
- package/pack/generated/claude/skills/rite-autocomplete/reference/decision-policy.md +2 -3
- package/pack/generated/claude/skills/rite-autocomplete/reference/loop.md +18 -25
- package/pack/generated/claude/skills/rite-autocomplete/reference/stop-conditions.md +10 -11
- package/pack/generated/claude/skills/rite-build/SKILL.md +13 -8
- package/pack/generated/claude/skills/rite-build/reference/afk-discipline.md +3 -1
- package/pack/generated/claude/skills/rite-build/reference/anti-patterns.md +4 -0
- package/pack/generated/claude/skills/rite-build/reference/checkpoint.md +33 -21
- package/pack/generated/claude/skills/rite-build/reference/one-slice-cycle.md +5 -4
- package/pack/generated/claude/skills/rite-build/reference/parallel-batch.md +30 -23
- package/pack/generated/claude/skills/rite-build/reference/phase-contract.md +4 -2
- package/pack/generated/claude/skills/rite-build/reference/wright-dispatch.md +16 -9
- package/pack/generated/claude/skills/rite-plan/reference/slicing.md +4 -3
- package/pack/generated/claude/skills/rite-polish/SKILL.md +3 -1
- package/pack/generated/claude/skills/rite-prove/SKILL.md +3 -0
- package/pack/generated/claude/skills/rite-review/SKILL.md +3 -1
- package/pack/generated/claude/skills/rite-temper/SKILL.md +6 -6
- package/pack/generated/claude/skills/rite-temper/reference/significance.md +3 -3
- package/pack/generated/codex/agents/devrites-slice-wright.toml +18 -9
- package/pack/generated/codex/skills/devrites-lib/reference/standards/afk-hitl.md +4 -3
- package/pack/generated/codex/skills/rite-autocomplete/SKILL.md +18 -18
- package/pack/generated/codex/skills/rite-autocomplete/reference/decision-policy.md +2 -3
- package/pack/generated/codex/skills/rite-autocomplete/reference/loop.md +18 -25
- package/pack/generated/codex/skills/rite-autocomplete/reference/stop-conditions.md +10 -11
- package/pack/generated/codex/skills/rite-build/SKILL.md +13 -8
- package/pack/generated/codex/skills/rite-build/reference/afk-discipline.md +3 -1
- package/pack/generated/codex/skills/rite-build/reference/anti-patterns.md +4 -0
- package/pack/generated/codex/skills/rite-build/reference/checkpoint.md +33 -21
- package/pack/generated/codex/skills/rite-build/reference/one-slice-cycle.md +5 -4
- package/pack/generated/codex/skills/rite-build/reference/parallel-batch.md +30 -23
- package/pack/generated/codex/skills/rite-build/reference/phase-contract.md +4 -2
- package/pack/generated/codex/skills/rite-build/reference/wright-dispatch.md +16 -9
- package/pack/generated/codex/skills/rite-plan/reference/slicing.md +4 -3
- package/pack/generated/codex/skills/rite-polish/SKILL.md +3 -1
- package/pack/generated/codex/skills/rite-prove/SKILL.md +3 -0
- package/pack/generated/codex/skills/rite-review/SKILL.md +3 -1
- package/pack/generated/codex/skills/rite-temper/SKILL.md +6 -6
- package/pack/generated/codex/skills/rite-temper/reference/significance.md +3 -3
- package/pack/generated/omp/agents/devrites-slice-wright.md +18 -9
- package/pack/generated/omp/skills/devrites-lib/reference/standards/afk-hitl.md +4 -3
- package/pack/generated/omp/skills/rite-autocomplete/SKILL.md +18 -18
- package/pack/generated/omp/skills/rite-autocomplete/reference/decision-policy.md +2 -3
- package/pack/generated/omp/skills/rite-autocomplete/reference/loop.md +18 -25
- package/pack/generated/omp/skills/rite-autocomplete/reference/stop-conditions.md +10 -11
- package/pack/generated/omp/skills/rite-build/SKILL.md +13 -8
- package/pack/generated/omp/skills/rite-build/reference/afk-discipline.md +3 -1
- package/pack/generated/omp/skills/rite-build/reference/anti-patterns.md +4 -0
- package/pack/generated/omp/skills/rite-build/reference/checkpoint.md +33 -21
- package/pack/generated/omp/skills/rite-build/reference/one-slice-cycle.md +5 -4
- package/pack/generated/omp/skills/rite-build/reference/parallel-batch.md +30 -23
- package/pack/generated/omp/skills/rite-build/reference/phase-contract.md +4 -2
- package/pack/generated/omp/skills/rite-build/reference/wright-dispatch.md +16 -9
- package/pack/generated/omp/skills/rite-plan/reference/slicing.md +4 -3
- package/pack/generated/omp/skills/rite-polish/SKILL.md +3 -1
- package/pack/generated/omp/skills/rite-prove/SKILL.md +3 -0
- package/pack/generated/omp/skills/rite-review/SKILL.md +3 -1
- package/pack/generated/omp/skills/rite-temper/SKILL.md +6 -6
- package/pack/generated/omp/skills/rite-temper/reference/significance.md +3 -3
- package/pack/generated/pi/agents/devrites-code-reviewer.md +1 -1
- package/pack/generated/pi/agents/devrites-devex-reviewer.md +1 -1
- package/pack/generated/pi/agents/devrites-doubt-reviewer.md +1 -1
- package/pack/generated/pi/agents/devrites-evidence-scout.md +1 -1
- package/pack/generated/pi/agents/devrites-frontend-reviewer.md +1 -1
- package/pack/generated/pi/agents/devrites-performance-reviewer.md +1 -1
- package/pack/generated/pi/agents/devrites-plan-drafter.md +1 -1
- package/pack/generated/pi/agents/devrites-plan-reviewer.md +1 -1
- package/pack/generated/pi/agents/devrites-proof-runner.md +1 -1
- package/pack/generated/pi/agents/devrites-retrospector.md +1 -1
- package/pack/generated/pi/agents/devrites-security-auditor.md +1 -1
- package/pack/generated/pi/agents/devrites-simplifier-reviewer.md +1 -1
- package/pack/generated/pi/agents/devrites-slice-wright.md +19 -10
- package/pack/generated/pi/agents/devrites-spec-reviewer.md +1 -1
- package/pack/generated/pi/agents/devrites-strategy-reviewer.md +1 -1
- package/pack/generated/pi/agents/devrites-test-analyst.md +1 -1
- package/pack/generated/pi/agents/devrites-upgrade-planner.md +1 -1
- package/pack/generated/pi/skills/devrites-lib/reference/standards/afk-hitl.md +4 -3
- package/pack/generated/pi/skills/rite-autocomplete/SKILL.md +18 -18
- package/pack/generated/pi/skills/rite-autocomplete/reference/decision-policy.md +2 -3
- package/pack/generated/pi/skills/rite-autocomplete/reference/loop.md +18 -25
- package/pack/generated/pi/skills/rite-autocomplete/reference/stop-conditions.md +10 -11
- package/pack/generated/pi/skills/rite-build/SKILL.md +13 -8
- package/pack/generated/pi/skills/rite-build/reference/afk-discipline.md +3 -1
- package/pack/generated/pi/skills/rite-build/reference/anti-patterns.md +4 -0
- package/pack/generated/pi/skills/rite-build/reference/checkpoint.md +33 -21
- package/pack/generated/pi/skills/rite-build/reference/one-slice-cycle.md +5 -4
- package/pack/generated/pi/skills/rite-build/reference/parallel-batch.md +30 -23
- package/pack/generated/pi/skills/rite-build/reference/phase-contract.md +4 -2
- package/pack/generated/pi/skills/rite-build/reference/wright-dispatch.md +16 -9
- package/pack/generated/pi/skills/rite-plan/reference/slicing.md +4 -3
- package/pack/generated/pi/skills/rite-polish/SKILL.md +3 -1
- package/pack/generated/pi/skills/rite-prove/SKILL.md +3 -0
- package/pack/generated/pi/skills/rite-review/SKILL.md +3 -1
- package/pack/generated/pi/skills/rite-temper/SKILL.md +6 -6
- package/pack/generated/pi/skills/rite-temper/reference/significance.md +3 -3
- package/package.json +1 -1
- 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.
|
|
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 #
|
|
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>/
|
package/docs/architecture.md
CHANGED
|
@@ -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
|
|
340
|
-
|
|
341
|
-
|
|
342
|
-
|
|
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
|
package/docs/command-map.md
CHANGED
|
@@ -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
|
|
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
|
|
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 |
|
package/docs/engine/commands.md
CHANGED
|
@@ -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 #
|
|
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
|
|
308
|
-
|
|
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
|
package/docs/orchestration.md
CHANGED
|
@@ -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
|
|
134
|
-
slice remains;
|
|
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
|
|
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` |
|
|
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"
|
|
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
|
|
388
|
-
|
|
389
|
-
|
|
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
|
|
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
|
|
396
|
-
pending-slice count
|
|
397
|
-
|
|
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("
|
|
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
|
-
|
|
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
|
|
418
|
-
//
|
|
419
|
-
// non-forced cleanup from discarding the refs that hold
|
|
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
|
|
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", "
|
|
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", "
|
|
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", "
|
|
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 != "
|
|
410
|
-
t.Fatalf("control should gain
|
|
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
|
-
|
|
413
|
-
|
|
414
|
-
|
|
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
|
|
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
|
|
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
|
|
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
|
-
//
|
|
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
|
|
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 != "
|
|
510
|
-
t.Fatalf("expected one
|
|
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
|
|