devrites 7.2.6 → 7.2.7

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 (78) hide show
  1. package/CHANGELOG.md +6 -0
  2. package/README.md +1 -1
  3. package/SECURITY.md +11 -7
  4. package/docs/engine/commands.md +3 -3
  5. package/engine/internal/lib/context.go +7 -4
  6. package/engine/internal/lib/dispatch.go +77 -21
  7. package/engine/internal/lib/taskgraph.go +94 -21
  8. package/pack/.claude/skills/devrites-lib/reference/parallel-dispatch.md +3 -3
  9. package/pack/.claude/skills/devrites-lib/reference/standards/agents.md +3 -6
  10. package/pack/.claude/skills/devrites-lib/reference/standards/principles.md +5 -6
  11. package/pack/.claude/skills/rite-autocomplete/reference/decision-policy.md +3 -1
  12. package/pack/.claude/skills/rite-autocomplete/reference/lookahead.md +18 -35
  13. package/pack/.claude/skills/rite-autocomplete/reference/stop-conditions.md +7 -8
  14. package/pack/.claude/skills/rite-build/reference/parallel-batch.md +23 -13
  15. package/pack/.claude/skills/rite-build/reference/wright-dispatch.md +5 -5
  16. package/pack/.claude/skills/rite-spec/SKILL.md +2 -2
  17. package/pack/.claude/skills/rite-temper/SKILL.md +4 -5
  18. package/pack/generated/claude/skills/devrites-lib/reference/parallel-dispatch.md +3 -3
  19. package/pack/generated/claude/skills/devrites-lib/reference/standards/agents.md +3 -6
  20. package/pack/generated/claude/skills/devrites-lib/reference/standards/principles.md +5 -6
  21. package/pack/generated/claude/skills/rite-autocomplete/reference/decision-policy.md +3 -1
  22. package/pack/generated/claude/skills/rite-autocomplete/reference/lookahead.md +18 -35
  23. package/pack/generated/claude/skills/rite-autocomplete/reference/stop-conditions.md +7 -8
  24. package/pack/generated/claude/skills/rite-build/reference/parallel-batch.md +23 -13
  25. package/pack/generated/claude/skills/rite-build/reference/wright-dispatch.md +5 -5
  26. package/pack/generated/claude/skills/rite-spec/SKILL.md +2 -2
  27. package/pack/generated/claude/skills/rite-temper/SKILL.md +4 -5
  28. package/pack/generated/codex/skills/devrites-lib/reference/parallel-dispatch.md +3 -3
  29. package/pack/generated/codex/skills/devrites-lib/reference/standards/agents.md +3 -6
  30. package/pack/generated/codex/skills/devrites-lib/reference/standards/principles.md +5 -6
  31. package/pack/generated/codex/skills/rite-autocomplete/reference/decision-policy.md +3 -1
  32. package/pack/generated/codex/skills/rite-autocomplete/reference/lookahead.md +18 -35
  33. package/pack/generated/codex/skills/rite-autocomplete/reference/stop-conditions.md +7 -8
  34. package/pack/generated/codex/skills/rite-build/reference/parallel-batch.md +23 -13
  35. package/pack/generated/codex/skills/rite-build/reference/wright-dispatch.md +5 -5
  36. package/pack/generated/codex/skills/rite-spec/SKILL.md +2 -2
  37. package/pack/generated/codex/skills/rite-temper/SKILL.md +4 -5
  38. package/pack/generated/devin/skills/devrites-lib/reference/parallel-dispatch.md +3 -3
  39. package/pack/generated/devin/skills/devrites-lib/reference/standards/agents.md +3 -6
  40. package/pack/generated/devin/skills/devrites-lib/reference/standards/principles.md +5 -6
  41. package/pack/generated/devin/skills/rite-autocomplete/reference/decision-policy.md +3 -1
  42. package/pack/generated/devin/skills/rite-autocomplete/reference/lookahead.md +18 -35
  43. package/pack/generated/devin/skills/rite-autocomplete/reference/stop-conditions.md +7 -8
  44. package/pack/generated/devin/skills/rite-build/reference/parallel-batch.md +23 -13
  45. package/pack/generated/devin/skills/rite-build/reference/wright-dispatch.md +5 -5
  46. package/pack/generated/devin/skills/rite-spec/SKILL.md +2 -2
  47. package/pack/generated/devin/skills/rite-temper/SKILL.md +4 -5
  48. package/pack/generated/dsh/skills/devrites-lib/reference/parallel-dispatch.md +3 -3
  49. package/pack/generated/dsh/skills/devrites-lib/reference/standards/agents.md +3 -6
  50. package/pack/generated/dsh/skills/devrites-lib/reference/standards/principles.md +5 -6
  51. package/pack/generated/dsh/skills/rite-autocomplete/reference/decision-policy.md +3 -1
  52. package/pack/generated/dsh/skills/rite-autocomplete/reference/lookahead.md +18 -35
  53. package/pack/generated/dsh/skills/rite-autocomplete/reference/stop-conditions.md +7 -8
  54. package/pack/generated/dsh/skills/rite-build/reference/parallel-batch.md +23 -13
  55. package/pack/generated/dsh/skills/rite-build/reference/wright-dispatch.md +5 -5
  56. package/pack/generated/dsh/skills/rite-spec/SKILL.md +2 -2
  57. package/pack/generated/dsh/skills/rite-temper/SKILL.md +4 -5
  58. package/pack/generated/omp/skills/devrites-lib/reference/parallel-dispatch.md +3 -3
  59. package/pack/generated/omp/skills/devrites-lib/reference/standards/agents.md +3 -6
  60. package/pack/generated/omp/skills/devrites-lib/reference/standards/principles.md +5 -6
  61. package/pack/generated/omp/skills/rite-autocomplete/reference/decision-policy.md +3 -1
  62. package/pack/generated/omp/skills/rite-autocomplete/reference/lookahead.md +18 -35
  63. package/pack/generated/omp/skills/rite-autocomplete/reference/stop-conditions.md +7 -8
  64. package/pack/generated/omp/skills/rite-build/reference/parallel-batch.md +23 -13
  65. package/pack/generated/omp/skills/rite-build/reference/wright-dispatch.md +5 -5
  66. package/pack/generated/omp/skills/rite-spec/SKILL.md +2 -2
  67. package/pack/generated/omp/skills/rite-temper/SKILL.md +4 -5
  68. package/pack/generated/pi/skills/devrites-lib/reference/parallel-dispatch.md +3 -3
  69. package/pack/generated/pi/skills/devrites-lib/reference/standards/agents.md +3 -6
  70. package/pack/generated/pi/skills/devrites-lib/reference/standards/principles.md +5 -6
  71. package/pack/generated/pi/skills/rite-autocomplete/reference/decision-policy.md +3 -1
  72. package/pack/generated/pi/skills/rite-autocomplete/reference/lookahead.md +18 -35
  73. package/pack/generated/pi/skills/rite-autocomplete/reference/stop-conditions.md +7 -8
  74. package/pack/generated/pi/skills/rite-build/reference/parallel-batch.md +23 -13
  75. package/pack/generated/pi/skills/rite-build/reference/wright-dispatch.md +5 -5
  76. package/pack/generated/pi/skills/rite-spec/SKILL.md +2 -2
  77. package/pack/generated/pi/skills/rite-temper/SKILL.md +4 -5
  78. package/package.json +3 -3
package/CHANGELOG.md CHANGED
@@ -2,6 +2,12 @@
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
+ ## [7.2.7](https://github.com/ViktorsBaikers/DevRites/compare/v7.2.6...v7.2.7) (2026-10-31)
6
+
7
+ ### Fixed
8
+
9
+ * **skills:** keep lookahead planning through false stops ([#192](https://github.com/ViktorsBaikers/DevRites/issues/192)) ([1185459](https://github.com/ViktorsBaikers/DevRites/commit/1185459e0477fe7fc9ee6b8e32e3ef315100ae15))
10
+
5
11
  ## [7.2.6](https://github.com/ViktorsBaikers/DevRites/compare/v7.2.5...v7.2.6) (2026-10-31)
6
12
 
7
13
  ### Fixed
package/README.md CHANGED
@@ -31,7 +31,7 @@ history can't lose it. After you clear the conversation, switch hosts, or hand
31
31
  the feature to another engineer, the next session reads the same plan,
32
32
  decisions, and evidence before it continues.
33
33
 
34
- **Status:** [`v7.2.6`](https://github.com/ViktorsBaikers/DevRites/releases/tag/v7.2.6): see [`CHANGELOG.md`](CHANGELOG.md) for release notes.
34
+ **Status:** [`v7.2.7`](https://github.com/ViktorsBaikers/DevRites/releases/tag/v7.2.7): see [`CHANGELOG.md`](CHANGELOG.md) for release notes.
35
35
 
36
36
  This is the latest published release; `main` may contain unreleased work.
37
37
 
package/SECURITY.md CHANGED
@@ -218,13 +218,17 @@ hosts or their makers. codebase-memory-mcp, ego-lite, browser-harness, and
218
218
  Playwright MCP are optional user-selected tools that DevRites calls through
219
219
  their documented interfaces rather than bundling them.
220
220
 
221
- Repository npm-audit exceptions are temporary trust records, not claims that an
222
- upstream issue is fixed. Each entry in `scripts/npm-audit-exceptions.json` is
223
- restricted to an exact advisory, package range, installed node path, owner,
224
- reason, source, and near-term expiry; validation re-audits the live dependency
225
- graph and fails stale, broadened, unmatched, expired, or inside-the-7-day
226
- refresh-horizon entries. Prefer pinning a patched ancestor (see
227
- `package.json` `overrides`) over extending an expiry.
221
+ Advisories whose only installed copy is inside the npm CLI tarball
222
+ (`node_modules/npm/node_modules/`) are not blocking. Overrides cannot replace
223
+ those copies, and the publish plugin cannot take a newer npm that still bundles
224
+ them. The same advisory installed anywhere else still blocks. Any other
225
+ npm-audit exception is a temporary trust record, not a claim that an upstream
226
+ issue is fixed. Each entry in `scripts/npm-audit-exceptions.json` is restricted
227
+ to an exact advisory, package range, installed node path, owner, reason, source,
228
+ and near-term expiry; validation re-audits the live dependency graph and fails
229
+ stale, broadened, unmatched, expired, or inside-the-7-day-refresh-horizon
230
+ entries. Prefer pinning a patched ancestor (see `package.json` `overrides`)
231
+ over extending an expiry.
228
232
 
229
233
  ### Agentic trust boundaries
230
234
 
@@ -17,7 +17,7 @@ workflow strategy.
17
17
  | `check preconditions <slug> [--slice SLICE-###] [--timeout N]` | Run the `test-plan.md` Build-entry preflight rows whose optional `Run at` cell is `vet` (with `--slice`, `pre-dispatch <SLICE>`); `action` rows and rows without the cell never run. Writes one candidate-bound transcript per row under `references/gates/preflight-*/`; a row passes on exit 0. A `pre-dispatch` row whose newest transcript passed is kept, not re-run: it is a pre-edit baseline, so a re-dispatch never runs it against the slice's own edits; changing its command or cwd retires the pass. Exit `3` on any failure or an invalid `Run at` value. |
18
18
  | `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. `check readiness` and `check seal` also block a `scripts/` test driver that a tasks.md Writer allowlist plans or touched-files.md lists when no gates.md `CHECK` runs it and no `Driver waiver: <path> — DEC-### <reason>` line cites a recorded decision; waived drivers print as `note: unexecuted driver (waived by DEC-###): <path>` lines, on pass or block. |
19
19
  | `check path-disjoint [--root <dir>] [<json-file> | -]` | Verify slice path sets are pairwise disjoint. |
20
- | `parallel select --cap <1-10> [--root <dir>] [--slug <slug>] [--from-plan \| <json-file> \| -]` | Choose the greedy path-disjoint subset ≤ cap from supplied ready slices, or with `--from-plan` (needs `--slug`) from the plan: tasks.md slices whose dependencies state.md checks `built`, with Writer allowlist (else Files likely touched) paths plus the nearest manifest/lockfile when `External dependencies:` names a package (`none*`, existing, or no-new prose without a coordinate). With `--slug` and a running lease, the lease's not-yet-landed slices hold cap slots and paths, slices it names are never picked, and only new picks print. |
20
+ | `parallel select --cap <1-10> [--root <dir>] [--slug <slug>] [--from-plan \| <json-file> \| -]` | Choose the greedy path-disjoint subset ≤ cap from supplied ready slices, or with `--from-plan` (needs `--slug`) from the plan: tasks.md slices whose dependencies are built (state.md checkbox, or an evidence.md heading that says that slice was checkpointed plus a `Checkpoint: <sha>` line), with Writer allowlist (else Files likely touched) paths plus the nearest manifest/lockfile when `External dependencies:` names a package (module path, npm scope, backticked name, or a non-toolchain version). With `--slug` and a running lease, the lease's not-yet-landed slices hold cap slots and paths, slices it names are never picked, and only new picks print. |
21
21
  | `parallel <create\|add\|integrate\|cleanup\|status\|abort\|lease-read\|lease-write\|lease-clear\|record-green>` | Deterministic parallel worktree leases, integration, and cleanup for parallel writer batches. A lease is a pipeline: `integrate --slice <id> --apply-to-control` lands one green sibling onto control (refusing if anything but this lease moved control), and `add` starts new path-disjoint siblings from the current control tip while others still run. |
22
22
  | `check task-graph <slug>` | Validate `tasks.md` slice dependency graph for cycles, unknown deps, malformed tokens, duplicate IDs, missing `Dependencies`, and `depends_on` mismatch. Prints a non-blocking `ADVISORY size:` line (and records a `size-advisory` metric) for each slice with 12+ Writer allowlist files and no `Size rationale:`. A `Dependencies` ID may carry a `(via <interface>)` suffix (empty or unclosed is a malformed token); prints a non-blocking `ADVISORY edge:` line (and records an `edge-advisory` metric) for each edge with no via, or whose via text is not a case-insensitive substring of the dependency's `Consumes / Produces:`. Prints `schedule: <n> slices in <r> build rounds at cap 10` (the `/rite-build` batch selection replayed over each slice's Writer allowlist, else Files likely touched, plus the manifest parallel select reserves; omitted when a slice has no path) and a non-blocking `ADVISORY hub:` line (top five; records a `hub-advisory` metric) for each path, compared case-folded, that three or more slices without `Serial rationale:` write. Advisories print only when the graph is valid. |
23
23
  | `check regression <slug> [--update]` | Compare the workspace's structural progress facts (phase ordinal, checked ACs, met/abandoned gates, done slices, resolved questions, present artifacts) against the recorded `regression-baseline.json` high-water mark; `--update` ratchets the baseline forward under the feature lock and prints each blessed regression. |
@@ -37,8 +37,8 @@ workflow strategy.
37
37
  | `next [slug]` | Print the minimal remaining lifecycle path plus advisory skips; in Build also `eligible_now:`, `batch:` and the `select-command:` (`parallel select --root <project> --slug <slug> --cap N --from-plan`, N = AFK max_parallel else 10) that reproduces the batch. Prints `blocked: unattributed root edit: ...` after `open-questions:` and exits `3` while the ADR-0038 tripwire flags a product path; the rest of the output still prints. |
38
38
  | `handoff [slug] [--resume]` | Emit the deterministic resume record: cursor fields, `awaiting_human`, open `questions.md` gate kinds, the `gates.md` reduction with unmet/stale/abandoned ids, `decisions.md` dead ends, in-flight dispatch waves (`inflight_wave:` lines: phase, state, pending roles, slices), missing required files, the canonical read-next order, and the same `continuation:` lines as `check continuation`. Read-only unless `--resume`, which appends one `resume` event (role root) to `metrics.jsonl`; `metrics summary` then reports minutes from each resume to the next dispatch. The `/rite-handoff` spine and the compaction-fallback entry point. |
39
39
  | `recall --paths <p1,p2,...> [--limit n]` | Read-time query over `.devrites/archive/`: list archived features whose `touched-files.md` candidate manifest covers a queried project-relative path (exact or directory prefix, case-folded), ranked by overlap count, then newest archive-directory mtime, then slug (default 10 features). Each feature prints up to 8 `archive/<slug>/<file>:<line>` pointers, then `+N more`: `decisions.md` `DEC-###` entries and `## Carry-forward constraints` bullets whose `Files:` overlap (or a constraint naming no files), plus `review.md`/`seal.md`/`drift.md` lines that mention a queried path; fenced examples are ignored. Never writes, caches, or indexes (no metric either); symlinked archive entries are refused. An unreadable archive file prints `unreadable:` and the scan continues. Exit `0` (including `recall: no archive`), `1` invalid archive directory, `2` usage. |
40
- | `context [slug] (--phase <p>\|--skill <name>) [--role <r>] [--slice <SLICE-ID>] [--recheck <inventory>] [--trigger a,b] [--inline-contract] [--whole]` | Emit one deduplicated read-set bundle per phase/role/skill from each skill's `loads:` manifest; `--skill` works without a workspace when `--out` is given. A root `--phase build\|autocomplete` bundle (no `--role`, no `--slice`) defaults to `--slice <active_slice>` while the state.md cursor phase is `build` and active_slice names a slice present in tasks.md (prints `scope: slice SLICE-### (auto from state.md) ...`); `--whole` opts out. Every bundle opens, after its header comments, with a table of contents: one `L<start>-<end> <section label>` line per `===== label =====` section, giving its exact line range in the bundle file. A bundle without `--role` (the root's own read) gives over-budget artifacts as by-reference indexes; a `--role` bundle leads with the role's agent contract and names the manifest's `rootOnly` paths instead of inlining them. When the contract resolves under `.claude/agents`, `.omp/agents`, or `.codex/agents` (the host loads it as the agent's system prompt), the bundle writes one header line naming its path, size, and SHA-256 instead of the body; other layouts (pi, devin, unknown) and `--inline-contract` (only with `--role`) inline the body, and a missing contract still exits `3`. Inside inlined files, a `--role` bundle replaces each `<!-- root-only -->`…`<!-- /root-only -->` block with a one-line stub naming the omitted heading, and does the same for each `<!-- model-surface -->` block unless the role is `security-auditor`, the feature has an `ai-spec.md`, or `spec.md`/`plan.md`/`tasks.md`/`design-brief.md` are absent or (outside the spec template's `AI-SPEC annex`) name a model, agent, RAG, or tool surface; an unbalanced or headingless marker exits `2`. `--slice` inlines that slice's `tasks.md` block and turns each over-budget artifact, and each artifact over 16 KiB except `spec.md`, into a by-reference index (path, SHA-256, headings, line hits for every ID the block cites); repeat `--slice` for several slices; an unknown slice exits `3`. In phase `build`, a `--role` bundle other than `slice-wright` without `--slice` exits `3` (`unscoped build reviewer bundle`). `--recheck <inventory>` (phase `build`, with `--role` and `--slice`; path relative to the workspace) writes `<phase>-<role>.<slice>.recheck.bundle.md`: the inventory file and `core.md` inline whole, every other non-`rootOnly` pack file becomes a by-reference index (path, SHA-256, headings with line numbers) followed by each section the inventory cites as `<file>.md § <heading>`; workspace handling matches `--slice`. A missing or empty inventory exits `2`. First reviews, full-inventory rechecks, Vet, `/rite-review`, and Seal do not use it. |
41
- | `dispatch <slug> <open\|start\|seal\|return\|status\|abandon\|attribute>` | Launch-wave barrier for parallel dispatch: seal needs a handle per role, return needs seal; the start that gives the last role its handle seals the wave, and an explicit seal on a sealed wave is a no-op; `open --claim-session <id> --claim <path>... [--claim-ttl <minutes>]` records one file claim for the wave in claims.jsonl and exits `3`, creating no wave and no claim, while a live foreign session holds an overlapping path; the return that completes the wave releases that claim; `abandon --release` releases it on any other host-confirmed terminal path (a stop or a refused launch); re-opening an abandoned wave inherits its kept claim or, with new claim flags, replaces and releases it; claim flags apply to open only and `--release` to abandon only; start/return auto-record metrics, `return --outcome findings|no-findings|gap` records the role's Outcome line, and `abandon` records its reason, so `metrics summary` shows per-role yield and lost waves. Bare `status` prints every open, sealed, or abandoned wave and folds complete waves into one `N complete wave(s) (use --all)` line; `--all` lists every wave; it still exits nonzero while any wave is open or sealed. For a reviewer, auditor, or test-analyst role, `return --result <file>` stores the return verbatim as `packets/<wave>-<role>.return.md` (exit `3` if that file exists), derives the outcome from its `Outcome:`/`Counts:` head (a malformed or inconsistent head records `gap` with a warning, never `no-findings`), exits `3` when an explicit `--outcome` disagrees, and records each `Finding:` row's `role|slice|file|section|severity` key, so `metrics summary` adds per-role `resolution: resolved re-raised open late` counts. A `--result` file that already is that `packets/` path is kept as the stored return. A build wave with a wright role needs `--slice <SLICE-ID>` per slice, and a slice-bound vet wave takes it too; a reviewer-only wave (for example a Build slice review) may take it, uncapped, so its `Finding:` keys name that slice; a reviewer, auditor, or test-analyst wave needs `--packet <file>` and `open` exits `3` on a packet that seeds a conclusion (gate `*-PASS` token, `no-findings`, an `Outcome:`/`Counts:` line, an "already passed" claim, or zero Critical/Important counts) or tells the agent to read an installed `agents/devrites-*.md`/`.toml` role contract (on the line or under a "Read …" heading or label; canonical `pack/` paths excepted), and a wright wave's `--contract` gets the same role-contract check; a Review or Seal reviewer wave also exits `3` unless the packet names an existing non-empty `.diff`/`.patch` file and the manifest covers every checkpointed path; every 5 rounds of either kind on one slice, `open` exits `3` until `--reslice-review <DRIFT-ID>` names an unused `drift.md` `## <DRIFT-ID>` heading for that slice. `open` also warns when an earlier wave for the same slice never returned. Under `.devrites/AFK` max_parallel >= 2, a first-round build wright wave narrower than the `parallel select --from-plan` batch exits `3` (printing the batch, the command and the parallel-batch lifecycle) until evidence.md records `serial: <skipped ids> — <reason>`; repair rounds, lease siblings and HITL runs are never refused. A build wright `--contract` must name `gates precheck` under `Writer-safe checks:` when gates.md has a `RUN: writer` gate. `open` exits `3` when a `--role` names no pack agent in a project-level install (a `.<lane>` suffix is allowed), and lints a reviewer packet against the wave's roles, so a recheck may name its own role's fingerprint. Root-edit tripwire (ADR-0038): the first `open` records `<feature>/tree-baseline.json`, which maps dirty product paths outside `.devrites/` and `.scratch/` to their mode plus SHA-256, accepted as user work. The return that completes a wright wave (any phase) adds its slice write sets and claim paths. `open` exits `3` with `unattributed root edit: <paths>` while a dirty product path differs from that baseline and no live claim or in-flight wright wave covers it, unless the new wright wave covers the path through `--claim` or a slice write set. `attribute --reason <evidence> --path <path>...` records owner work, gate output, a standalone wright, or a sanctioned root write for flagged paths only. Each finding writes an `unattributed-edit` metrics row. The engine never reverts a file, and a workspace without the baseline is never blocked. |
40
+ | `context [slug] (--phase <p>\|--skill <name>) [--role <r>] [--slice <SLICE-ID>] [--recheck <inventory>] [--trigger a,b] [--inline-contract] [--whole]` | Emit one deduplicated read-set bundle per phase/role/skill from each skill's `loads:` manifest; `--skill` works without a workspace when `--out` is given. A root `--phase build\|autocomplete` bundle (no `--role`, no `--slice`) defaults to `--slice <active_slice>` while the state.md cursor phase is `build` and active_slice names a slice present in tasks.md (prints `scope: slice SLICE-### (auto from state.md) ...`), unless `.devrites/AFK` `max_parallel` is 2 or more or a parallel lease is `running` (the bundle stays whole); `--whole` opts out. Every bundle opens, after its header comments, with a table of contents: one `L<start>-<end> <section label>` line per `===== label =====` section, giving its exact line range in the bundle file. A bundle without `--role` (the root's own read) gives over-budget artifacts as by-reference indexes; a `--role` bundle leads with the role's agent contract and names the manifest's `rootOnly` paths instead of inlining them. When the contract resolves under `.claude/agents`, `.omp/agents`, or `.codex/agents` (the host loads it as the agent's system prompt), the bundle writes one header line naming its path, size, and SHA-256 instead of the body; other layouts (pi, devin, unknown) and `--inline-contract` (only with `--role`) inline the body, and a missing contract still exits `3`. Inside inlined files, a `--role` bundle replaces each `<!-- root-only -->`…`<!-- /root-only -->` block with a one-line stub naming the omitted heading, and does the same for each `<!-- model-surface -->` block unless the role is `security-auditor`, the feature has an `ai-spec.md`, or `spec.md`/`plan.md`/`tasks.md`/`design-brief.md` are absent or (outside the spec template's `AI-SPEC annex`) name a model, agent, RAG, or tool surface; an unbalanced or headingless marker exits `2`. `--slice` inlines that slice's `tasks.md` block and turns each over-budget artifact, and each artifact over 16 KiB except `spec.md`, into a by-reference index (path, SHA-256, headings, line hits for every ID the block cites); repeat `--slice` for several slices; an unknown slice exits `3`. In phase `build`, a `--role` bundle other than `slice-wright` without `--slice` exits `3` (`unscoped build reviewer bundle`). `--recheck <inventory>` (phase `build`, with `--role` and `--slice`; path relative to the workspace) writes `<phase>-<role>.<slice>.recheck.bundle.md`: the inventory file and `core.md` inline whole, every other non-`rootOnly` pack file becomes a by-reference index (path, SHA-256, headings with line numbers) followed by each section the inventory cites as `<file>.md § <heading>`; workspace handling matches `--slice`. A missing or empty inventory exits `2`. First reviews, full-inventory rechecks, Vet, `/rite-review`, and Seal do not use it. |
41
+ | `dispatch <slug> <open\|start\|seal\|return\|status\|abandon\|attribute>` | Launch-wave barrier for parallel dispatch: seal needs a handle per role, return needs seal; the start that gives the last role its handle seals the wave, and an explicit seal on a sealed wave is a no-op; `open --claim-session <id> --claim <path>... [--claim-ttl <minutes>]` records one file claim for the wave in claims.jsonl and exits `3`, creating no wave and no claim, while a live foreign session holds an overlapping path; the return that completes the wave releases that claim; `abandon --release` releases it on any other host-confirmed terminal path (a stop or a refused launch); re-opening an abandoned wave inherits its kept claim or, with new claim flags, replaces and releases it; claim flags apply to open only and `--release` to abandon only; start/return auto-record metrics, `return --outcome findings|no-findings|gap` records the role's Outcome line, and `abandon` records its reason, so `metrics summary` shows per-role yield and lost waves. Bare `status` prints every open, sealed, or abandoned wave and folds complete waves into one `N complete wave(s) (use --all)` line; `--all` lists every wave; it still exits nonzero while any wave is open or sealed. For a reviewer, auditor, or test-analyst role, `return --result <file>` stores the return verbatim as `packets/<wave>-<role>.return.md` (exit `3` if that file exists), derives the outcome from its `Outcome:`/`Counts:` head (a malformed or inconsistent head records `gap` with a warning, never `no-findings`), exits `3` when an explicit `--outcome` disagrees, and records each `Finding:` row's `role|slice|file|section|severity` key, so `metrics summary` adds per-role `resolution: resolved re-raised open late` counts. A `--result` file that already is that `packets/` path is kept as the stored return. A build wave with a wright role needs `--slice <SLICE-ID>` per slice, and a slice-bound vet wave takes it too; a reviewer-only wave (for example a Build slice review) may take it, uncapped, so its `Finding:` keys name that slice; a reviewer, auditor, or test-analyst wave needs `--packet <file>` and `open` exits `3` on a packet that seeds a conclusion (gate `*-PASS` token, `no-findings`, an `Outcome:`/`Counts:` line, an "already passed" claim, or zero Critical/Important counts) or tells the agent to read an installed `agents/devrites-*.md`/`.toml` role contract (on the line or under a "Read …" heading or label; canonical `pack/` paths excepted), and a wright wave's `--contract` gets the same role-contract check; a Review or Seal reviewer wave also exits `3` unless the packet names an existing non-empty `.diff`/`.patch` file and the manifest covers every checkpointed path; every 5 rounds of either kind on one slice, `open` exits `3` until `--reslice-review <DRIFT-ID>` names an unused `drift.md` `## <DRIFT-ID>` heading for that slice. `open` also warns when an earlier wave for the same slice never returned. Under `.devrites/AFK` max_parallel >= 2, a build wright wave that leaves `parallel select --from-plan` picks unstarted exits `3` (printing the batch, the command and the parallel-batch lifecycle) until evidence.md records `serial: <skipped ids> — <reason>`; a repair round or a running lease slice does not waive those picks, and a reason that only says the skipped slice is repairing, already started, or in a wave does not count. A wave naming two or more slices needs one `--role slice-wright.<SLICE-ID>` per slice; one shared role exits `3`. HITL runs are never width-refused. A build wright `--contract` must name `gates precheck` under `Writer-safe checks:` when gates.md has a `RUN: writer` gate. `open` exits `3` when a `--role` names no pack agent in a project-level install (a `.<lane>` suffix is allowed), and lints a reviewer packet against the wave's roles, so a recheck may name its own role's fingerprint. Root-edit tripwire (ADR-0038): the first `open` records `<feature>/tree-baseline.json`, which maps dirty product paths outside `.devrites/` and `.scratch/` to their mode plus SHA-256, accepted as user work. The return that completes a wright wave (any phase) adds its slice write sets and claim paths. `open` exits `3` with `unattributed root edit: <paths>` while a dirty product path differs from that baseline and no live claim or in-flight wright wave covers it, unless the new wright wave covers the path through `--claim` or a slice write set. `attribute --reason <evidence> --path <path>...` records owner work, gate output, a standalone wright, or a sanctioned root write for flagged paths only. Each finding writes an `unattributed-edit` metrics row. The engine never reverts a file, and a workspace without the baseline is never blocked. |
42
42
  | `candidate freeze <slug> --wave <w> (--slice <SLICE-ID> \| --paths <path>...) [--base <ref>] [--worktree <path>]` | Writes packets/<w>.diff (tracked changes vs base, default HEAD, plus untracked files as /dev/null diffs) and packets/<w>.sha256 (base, diff digest, per-file sha256, aggregate check-candidate digest); never replaces a wave's frozen candidate with a different one. `--worktree <path>` (requires `--base`) reads the same path set from a parallel sibling's linked worktree before it lands: the path must be a symlink-free worktree top level that `git worktree list` names, shares the control repository, and is neither a submodule nor the control checkout. It exits `3` on uncommitted path-set changes there and adds a `head <sha>` row naming the worktree HEAD (the transfer commit); packets stay in the control workspace. |
43
43
  | `packet <slug> --wave <w> --phase <p> --role <r> [--slice <SLICE-ID>] [--recheck] [--checks <G-ID,...>] [--focus <file>]` | Renders one reviewer/auditor/test-analyst or same-worktree slice-wright by-reference packet (packets/<w>.<role>.md, or packets/<w>.contract.md for a wright) naming the context bundle (the .recheck bundle with `--recheck`) and frozen diff. It copies packets/<w>.sha256 (header rows only when large), lists a wright slice's tasks.md `Tests/proof` as its first writer-safe check, resolves `--checks` through gates.md and approved preflight rows (adding `gates precheck` for `RUN: writer` gates), and accepts only anchored `--focus` rows (a doubt-reviewer's 1-3 row claim is required). It runs the dispatch open lints on a draft and never replaces a prior packet with a refused one (exit `3`). |
44
44
  | `metrics record <slug> --phase <p> --event <e> [--role r] [--bytes n] [--note s]` | Append an event to the workspace's `metrics.jsonl` ledger. |
@@ -57,7 +57,9 @@ every ID the block cites).
57
57
  Repeat --slice for a recheck that spans several slices. In phase build a
58
58
  --role bundle other than slice-wright requires --slice (exit 3). A root
59
59
  build or autocomplete bundle (no --role, no --slice) defaults to --slice
60
- <active_slice> when state.md names one present in tasks.md; --whole opts out.
60
+ <active_slice> when state.md names one present in tasks.md, unless
61
+ .devrites/AFK max_parallel is 2 or more or a parallel lease is running
62
+ (the bundle stays whole so the other slices stay visible); --whole opts out.
61
63
  --recheck <inventory> (phase build, with --role and --slice) builds a
62
64
  bounded recheck bundle: the inventory file (relative to the workspace)
63
65
  and core.md are inlined; every other pack file becomes a by-reference
@@ -91,10 +93,11 @@ func RunContext(root string, args []string, stdout, stderr io.Writer) int {
91
93
  return 2
92
94
  }
93
95
  }
94
- // The root's own build/autocomplete bundle holds one slice at a time;
95
- // whole tasks/plan/decisions ride every later turn otherwise.
96
+ // One active slice per bundle, except a parallel batch: the root has to
97
+ // see the other slices to start them. Whole tasks/plan/decisions ride
98
+ // that batch's turns; serial runs stay scoped.
96
99
  autoScope := false
97
- if featureDir != "" && opts.role == "" && opts.slice == "" && !opts.whole && (opts.phase == "build" || opts.phase == "autocomplete") {
100
+ if featureDir != "" && opts.role == "" && opts.slice == "" && !opts.whole && (opts.phase == "build" || opts.phase == "autocomplete") && !parallelPlanVisible(root, opts.slug) {
98
101
  if id := activeSliceScope(featureDir); id != "" {
99
102
  opts.slices, opts.slice, autoScope = []string{id}, id, true
100
103
  }
@@ -57,9 +57,11 @@ which keys its --result findings on that slice. A --result file may already
57
57
  be the packets/ path; it is then kept as the stored return. Every 5 rounds of one kind on one slice, the
58
58
  next open is blocked until --reslice-review names a drift.md
59
59
  "## <DRIFT-ID>" heading for that slice. Under .devrites/AFK max_parallel >= 2,
60
- a first-round build wright wave narrower than the parallel select --from-plan
61
- batch is blocked until evidence.md records "serial: <skipped ids> — <reason>";
62
- repair rounds and lease siblings are never blocked.
60
+ a build wright wave that leaves parallel select --from-plan picks unstarted is
61
+ blocked until evidence.md records "serial: <skipped ids> — <reason>". A repair
62
+ round or a running lease slice does not waive those picks. A wave naming two
63
+ or more slices needs one slice-wright.<SLICE-ID> role per slice; one shared
64
+ role has one handle, so the other slices never start. HITL is never width-blocked.
63
65
 
64
66
  Root-edit tripwire (ADR-0038): the first open records tree-baseline.json
65
67
  (dirty product paths outside .devrites/ and .scratch/, accepted as user work);
@@ -742,7 +744,6 @@ func admitWrightRounds(root, slug string, f *dispatchFile, w *dispatchWave, revi
742
744
  }
743
745
  }
744
746
  var lines, overCap []string
745
- firstRound := true
746
747
  for _, id := range w.Slices {
747
748
  prior, reviews := 0, 0
748
749
  var unreturned []string
@@ -762,7 +763,6 @@ func admitWrightRounds(root, slug string, f *dispatchFile, w *dispatchWave, revi
762
763
  }
763
764
  }
764
765
  round := prior + 1
765
- firstRound = firstRound && prior == 0
766
766
  if round > wrightRoundsPerReview*(1+reviews) {
767
767
  overCap = append(overCap, id)
768
768
  }
@@ -780,7 +780,11 @@ func admitWrightRounds(root, slug string, f *dispatchFile, w *dispatchWave, revi
780
780
  fmt.Fprintf(stderr, "dispatch: --reslice-review given but no slice has reached its %d-round cap\n", wrightRoundsPerReview)
781
781
  return nil, 2
782
782
  }
783
- if kind == "wright" && firstRound {
783
+ if kind == "wright" {
784
+ if refusal := wrightFanoutRefusal(w); refusal != "" {
785
+ fmt.Fprintln(stderr, refusal)
786
+ return nil, 3
787
+ }
784
788
  if refusal := widerBatchRefusal(root, slug, w.Slices); refusal != "" {
785
789
  fmt.Fprintln(stderr, refusal)
786
790
  return nil, 3
@@ -816,36 +820,88 @@ func admitWrightRounds(root, slug string, f *dispatchFile, w *dispatchWave, revi
816
820
  }
817
821
 
818
822
  // serialRecordRe matches an evidence.md `serial: <ids> — <reason>` record
819
- // (the reason may open with a code span, never be empty); group 1 holds the ids.
820
- var serialRecordRe = regexp.MustCompile("(?mi)^[ \\t]*(?:[-*][ \\t]+)?`?serial:([^\\n]*?)(?:[ \\t]*[—–]|[ \\t]+--?[ \\t])[ \\t]*(?:[^\\s`]|`[^\\s`])")
823
+ // (the reason may open with a code span, never be empty). Group 1 holds the
824
+ // ids; group 2 holds the reason.
825
+ var serialRecordRe = regexp.MustCompile("(?mi)^[ \\t]*(?:[-*][ \\t]+)?`?serial:([^\\n]*?)(?:[ \\t]*[—–]|[ \\t]+--?[ \\t])[ \\t]*([^\\s`][^\\n]*|`[^\\s`][^\\n]*)")
826
+
827
+ // A repair, an open wave, or "already started" is not a shared surface.
828
+ // Those reasons do not waive a pick unless they also name a path.
829
+ var transientSerialReasonRe = regexp.MustCompile(`(?i)\b(?:repairs?|repairing|repaired|already started|in flight|open wave|sealed wave|running lease|in the lease)\b`)
830
+ var serialSurfaceRe = regexp.MustCompile("`[^`\\n]*\\.[^`\\n]+`|(?i)\\b[\\w./-]+\\.[a-z0-9]+\\b")
831
+
832
+ func serialReasonWaives(reason string) bool {
833
+ if transientSerialReasonRe.MatchString(reason) && !serialSurfaceRe.MatchString(reason) {
834
+ return false
835
+ }
836
+ return true
837
+ }
821
838
 
822
839
  var sliceTokenRe = regexp.MustCompile(`(?i)\bSLICE-\d+\b`)
823
840
 
824
- // widerBatchRefusal refuses a first-round AFK build wright wave when
825
- // `parallel select --from-plan` picks more slices than the wave holds and
826
- // evidence.md has no `serial:` record naming each skipped pick. HITL runs,
827
- // lease siblings, slices the plan does not list as eligible, and selection
828
- // errors are never refused: the check widens batches, it never stops a build.
841
+ // wrightFanoutRefusal refuses a build wright wave that names several slices
842
+ // but not one slice-wright role per slice. One role has one host handle, so
843
+ // the other slices would never start.
844
+ func wrightFanoutRefusal(w *dispatchWave) string {
845
+ if len(w.Slices) < 2 {
846
+ return ""
847
+ }
848
+ lanes := map[string]bool{}
849
+ bare := 0
850
+ for role := range w.Roles {
851
+ if roleBase(role) != "slice-wright" {
852
+ continue
853
+ }
854
+ lane, ok := wrightLane(role)
855
+ if !ok {
856
+ bare++
857
+ continue
858
+ }
859
+ lanes[sliceNumber(lane)] = true
860
+ }
861
+ if bare > 0 || len(lanes) != len(w.Slices) {
862
+ return fmt.Sprintf("dispatch: BLOCKED: %d slices need one `--role slice-wright.<SLICE-ID>` each so every writer gets a handle; one shared slice-wright role starts only one agent", len(w.Slices))
863
+ }
864
+ for _, id := range w.Slices {
865
+ if !lanes[sliceNumber(id)] {
866
+ return fmt.Sprintf("dispatch: BLOCKED: %s has no `--role slice-wright.%s`", id, id)
867
+ }
868
+ }
869
+ return ""
870
+ }
871
+
872
+ // wrightLane is the SLICE-### suffix of a slice-wright.<SLICE-ID> role.
873
+ func wrightLane(role string) (string, bool) {
874
+ base, lane, ok := strings.Cut(strings.TrimPrefix(role, "devrites-"), ".")
875
+ if !ok || base != "slice-wright" || !state.ValidSliceID(lane) {
876
+ return "", false
877
+ }
878
+ return lane, true
879
+ }
880
+
881
+ // widerBatchRefusal refuses an AFK build wright wave when
882
+ // `parallel select --from-plan` still picks slices this wave does not start
883
+ // and evidence.md has no `serial:` record for each. A repair round or a
884
+ // running lease slice does not waive those picks. HITL runs and selection
885
+ // errors are never refused: the check widens the batch, it never stops a
886
+ // wave that already starts every remaining pick.
829
887
  func widerBatchRefusal(root, slug string, wave []string) string {
830
888
  capN := min(afkMaxParallel(root), parallel.MaxParallelSlices)
831
889
  if capN < 2 {
832
890
  return ""
833
891
  }
834
- eligible, batch, err := planBatch(root, slug, capN)
835
- if err != nil || len(batch) <= len(wave) {
892
+ _, batch, err := planBatch(root, slug, capN)
893
+ if err != nil || len(batch) == 0 {
836
894
  return ""
837
895
  }
838
- for _, id := range wave {
839
- if !containsSlice(eligible, id) {
840
- return ""
841
- }
842
- }
843
896
  var recorded []string
844
897
  if raw, err := readWorkspaceArtifact(root, slug, "evidence.md"); err == nil {
845
898
  if visible, err := markdowntext.Structural(raw); err == nil {
846
899
  raw = visible
847
900
  }
848
901
  for _, m := range serialRecordRe.FindAllStringSubmatch(string(raw), -1) {
902
+ if len(m) < 3 || !serialReasonWaives(m[2]) {
903
+ continue
904
+ }
849
905
  recorded = append(recorded, sliceTokenRe.FindAllString(m[1], -1)...)
850
906
  }
851
907
  }
@@ -858,7 +914,7 @@ func widerBatchRefusal(root, slug string, wave []string) string {
858
914
  if len(missing) == 0 {
859
915
  return ""
860
916
  }
861
- return fmt.Sprintf("dispatch: BLOCKED: AFK max_parallel %d: `%s` picks %s, wider than this wave (%s); run that batch per rite-build/reference/parallel-batch.md (`parallel create`, then one wave with a --slice per sibling), or record `serial: %s — <shared surface or reason>` in evidence.md",
917
+ return fmt.Sprintf("dispatch: BLOCKED: AFK max_parallel %d: `%s` picks %s, wider than this wave (%s); start those picks (`parallel create`, or `parallel add` while a lease runs) and dispatch them, or record `serial: %s — <shared surface or reason>` in evidence.md",
862
918
  capN, planSelectCommand(filepath.Dir(root), slug, capN), strings.Join(batch, ","), strings.Join(wave, ","), strings.Join(missing, ","))
863
919
  }
864
920
 
@@ -1,6 +1,7 @@
1
1
  package lib
2
2
 
3
3
  import (
4
+ "errors"
4
5
  "fmt"
5
6
  "os"
6
7
  "path"
@@ -195,10 +196,10 @@ func PrecheckSkips(root, slug, slice string, gateIDs []string) (map[string]strin
195
196
  return skips, nil
196
197
  }
197
198
 
198
- // planReady lists, in plan order, the tasks.md slices state.md does not check
199
- // built whose dependencies it all checks built, each with its sliceWritePaths
200
- // plus its dependency manifest and lockfile when it declares external
201
- // dependencies. root is the .devrites directory.
199
+ // planReady lists, in plan order, the tasks.md slices that are not built whose
200
+ // dependencies are all built, each with its sliceWritePaths plus its dependency
201
+ // manifest and lockfile when it declares external dependencies. root is the
202
+ // .devrites directory.
202
203
  func planReady(root, slug string) ([]parallel.SlicePaths, error) {
203
204
  raw, err := readWorkspaceArtifact(root, slug, "tasks.md")
204
205
  if err != nil {
@@ -233,8 +234,11 @@ func planReady(root, slug string) ([]parallel.SlicePaths, error) {
233
234
  // builtWordRe marks a checked state.md item as a built checkpoint.
234
235
  var builtWordRe = regexp.MustCompile(`(?i)\bbuilt\b`)
235
236
 
236
- // builtSlices returns the slice numbers state.md checks built (`- [x] SLICE-004
237
- // built — <sha>`, or `- [x] Slice 4: <name> — built`), outside fenced examples.
237
+ // builtSlices returns the slice numbers that are built, outside fenced examples.
238
+ // A state.md checkbox (`- [x] SLICE-004 built — <sha>`, or `- [x] Slice 4:
239
+ // <name> — built`) counts. So does an evidence.md heading that says that slice
240
+ // was checkpointed and whose section has a `Checkpoint: <sha>` line — a missed
241
+ // checkbox must not keep dependents out of the ready set.
238
242
  func builtSlices(root, slug string) (map[string]bool, error) {
239
243
  raw, err := readWorkspaceArtifact(root, slug, "state.md")
240
244
  if err != nil {
@@ -253,35 +257,86 @@ func builtSlices(root, slug string) (map[string]bool, error) {
253
257
  built[strings.TrimLeft(m[1], "0")] = true
254
258
  }
255
259
  }
260
+ extra, err := evidenceBuilt(root, slug)
261
+ if err != nil {
262
+ return nil, err
263
+ }
264
+ for id := range extra {
265
+ built[id] = true
266
+ }
256
267
  return built, nil
257
268
  }
258
269
 
259
- // manifestDenialRe is prose that says the slice adds no dependency.
260
- var manifestDenialRe = regexp.MustCompile(`(?i)\b(?:no new|no product|existing|already-admitted|already admitted|stdlib)\b`)
270
+ var (
271
+ checkpointedHeadingRe = regexp.MustCompile(`(?i)\bcheckpointed\b`)
272
+ notCheckpointedRe = regexp.MustCompile(`(?i)\bnot checkpointed\b|\bcheckpoint (?:refused|reverted|failed)\b`)
273
+ checkpointSHARe = regexp.MustCompile(`(?m)^[ \t]*Checkpoint: [0-9a-fA-F]{7,40}\b`)
274
+ )
275
+
276
+ // evidenceBuilt reads evidence.md checkpoint sections. A missing file is none.
277
+ func evidenceBuilt(root, slug string) (map[string]bool, error) {
278
+ raw, err := readWorkspaceArtifact(root, slug, "evidence.md")
279
+ if err != nil {
280
+ if errors.Is(err, os.ErrNotExist) {
281
+ return map[string]bool{}, nil
282
+ }
283
+ return nil, err
284
+ }
285
+ masked, err := markdowntext.Structural(raw)
286
+ if err != nil {
287
+ return nil, fmt.Errorf("evidence.md: %w", err)
288
+ }
289
+ text := string(masked)
290
+ if strings.HasPrefix(text, "## ") {
291
+ text = "\n" + text
292
+ }
293
+ built := map[string]bool{}
294
+ for part := range strings.SplitSeq(text, "\n## ") {
295
+ heading, body, _ := strings.Cut(part, "\n")
296
+ var ids []string
297
+ for _, id := range sliceTokenRe.FindAllString(heading, -1) {
298
+ ids = append(ids, sliceNumber(id))
299
+ }
300
+ if len(ids) == 0 {
301
+ continue
302
+ }
303
+ if notCheckpointedRe.MatchString(heading) {
304
+ for _, id := range ids {
305
+ delete(built, id)
306
+ }
307
+ continue
308
+ }
309
+ if checkpointedHeadingRe.MatchString(heading) && checkpointSHARe.MatchString(body) {
310
+ for _, id := range ids {
311
+ built[id] = true
312
+ }
313
+ }
314
+ }
315
+ return built, nil
316
+ }
261
317
 
262
318
  // A backtick name, module path, npm scope, or x.y.z pin. go.mod/go.sum is not a module path.
319
+ // A toolchain version (Go 1.22.0) is not a package add.
263
320
  var (
264
- backtickPkgRe = regexp.MustCompile("`([^`]+)`")
265
- modulePathRe = regexp.MustCompile(`(?i)(?:[a-z0-9-]+\.)+[a-z]{2,}/[a-z0-9._@~+-]+`)
266
- npmScopeRe = regexp.MustCompile(`(?i)@[a-z0-9._-]+/[a-z0-9._-]+`)
267
- versionPinRe = regexp.MustCompile(`(?i)\bv?\d+\.\d+\.\d+\b`)
268
- manifestHostRe = regexp.MustCompile(`(?i)\.(?:mod|sum|json|toml|yaml|yml|lock|lockb)$`)
321
+ backtickPkgRe = regexp.MustCompile("`([^`]+)`")
322
+ modulePathRe = regexp.MustCompile(`(?i)(?:[a-z0-9-]+\.)+[a-z]{2,}/[a-z0-9._@~+-]+`)
323
+ npmScopeRe = regexp.MustCompile(`(?i)@[a-z0-9._-]+/[a-z0-9._-]+`)
324
+ versionPinRe = regexp.MustCompile(`(?i)\bv?\d+\.\d+\.\d+\b`)
325
+ toolchainVersionRe = regexp.MustCompile(`(?i)\b(?:go|golang|node|bun|npm|pnpm|yarn|rustc|python|ruby|java)\s+v?\d+\.\d+\.\d+\b`)
326
+ manifestHostRe = regexp.MustCompile(`(?i)\.(?:mod|sum|json|toml|yaml|yml|lock|lockb)$`)
269
327
  )
270
328
 
271
329
  // externalClaimsManifest reports whether External dependencies names a package
272
330
  // add, so parallel select reserves the nearest manifest. A none prefix does
273
- // not. shortcut: existing/no-new prose with no package coordinate does not
274
- // either; name the module, version, scope, or `package` when the slice edits one.
331
+ // not. Prose with no module path, npm scope, `package`, or non-toolchain
332
+ // version pin does not either. shortcut: name the package when the slice
333
+ // edits a manifest; "Go 1.22.0" or "standard library" is not a package add.
275
334
  func externalClaimsManifest(external string) bool {
276
335
  ext := strings.ToLower(strings.Trim(strings.TrimSpace(external), "`"))
277
336
  if ext == "" || ext == "-" || ext == "n/a" || strings.HasPrefix(ext, "none") {
278
337
  return false
279
338
  }
280
- raw := strings.TrimSpace(external)
281
- if manifestDenialRe.MatchString(raw) && !namesPackage(raw) {
282
- return false
283
- }
284
- return true
339
+ return namesPackage(strings.TrimSpace(external))
285
340
  }
286
341
 
287
342
  // namesPackage reports a module path, version pin, npm scope, or `package`
@@ -293,7 +348,7 @@ func namesPackage(raw string) bool {
293
348
  return true
294
349
  }
295
350
  }
296
- if npmScopeRe.MatchString(raw) || versionPinRe.MatchString(raw) {
351
+ if npmScopeRe.MatchString(raw) || versionPinRe.MatchString(toolchainVersionRe.ReplaceAllString(raw, " ")) {
297
352
  return true
298
353
  }
299
354
  for _, loc := range modulePathRe.FindAllStringIndex(raw, -1) {
@@ -396,6 +451,24 @@ func afkMaxParallel(root string) int {
396
451
  return n
397
452
  }
398
453
 
454
+ // parallelPlanVisible reports that the root must see every slice. An
455
+ // unattended cap above 1, or a running parallel lease, is a batch: scoping
456
+ // the bundle to active_slice hides the siblings that batch is supposed to start.
457
+ func parallelPlanVisible(devritesRoot, slug string) bool {
458
+ if afkMaxParallel(devritesRoot) >= 2 {
459
+ return true
460
+ }
461
+ if slug == "" {
462
+ return false
463
+ }
464
+ path, err := parallel.LeasePath(filepath.Dir(devritesRoot), slug)
465
+ if err != nil {
466
+ return false
467
+ }
468
+ lease, err := parallel.ReadLease(path)
469
+ return err == nil && lease.Status == parallel.StatusRunning
470
+ }
471
+
399
472
  func allDone(ids []string, done map[string]bool) bool {
400
473
  for _, id := range ids {
401
474
  if !done[id] {
@@ -169,9 +169,9 @@ named in the re-dispatch contract; changes outside task paths stop for the human
169
169
 
170
170
  ## Scale limits
171
171
 
172
- The role table bounds rosters. Use roughly four concurrent read-only reviewers,
173
- counting children any leaf spawned; eight only with disjoint paths/resources.
174
- Refuse unbounded swarms and role substitution: sharpen scope or use a bounded second pass when coverage is missing.
172
+ The role table bounds rosters. Use up to ten concurrent read-only reviewers,
173
+ the same cap as a writer batch, counting children any leaf spawned.
174
+ Refuse a larger swarm and role substitution: sharpen scope or use a bounded second pass when coverage is missing.
175
175
  Overlapping reviewers increase duplicate findings and reconciliation work.
176
176
 
177
177
  ## Children are tools, not peers
@@ -8,7 +8,7 @@ Follow DevRites policy and [`depth profiles`](../orchestration-profiles.md).
8
8
 
9
9
  <!-- root-only -->
10
10
  - Root owns scope, questions/decisions/results, `.devrites/**`, phase transitions — not product source/tests; vetted executable workflow artifacts follow [`workflow-artifacts.md`](workflow-artifacts.md).
11
- - Every named role runs; unavailable → HITL, never skip/substitute.
11
+ - Every named role runs; unavailable → HITL, never skip/substitute. `sequence_lookahead: yes` with no spawn tool defers the role, records the gap, and keeps planning. Not gate `access`, not a verdict. Admit the role before Build.
12
12
  <!-- /root-only -->
13
13
  - Among lifecycle `devrites-*` specialists, only bounded wright writes product
14
14
  source/tests; others inspect an immutable candidate. The independent `/rite-fast`
@@ -145,11 +145,8 @@ Basis: <files read · commands run to reach this finding>
145
145
  asks for is a Suggestion, however plausible. The `when` must also be reachable:
146
146
  a hostile actor enters only through a trust boundary the feature really has
147
147
  (users, network, external systems, untrusted files). A trigger that needs one it
148
- never faces — the code under test forging its own evidence, a tampering second
149
- user on a single-owner dev host, a swapped remote daemon under a local dev tool —
150
- is a Suggestion too. So is an environment state the feature's declared
151
- environment does not have (a mount option, kernel setting or daemon location
152
- absent from the target host): guard against it only when the spec names it.
148
+ never faces — forged test evidence, a second user on a single-owner host, or a swapped local daemon —
149
+ is a Suggestion too. So is an undeclared host setting (mount, kernel, daemon path): guard it only when the spec names it.
153
150
  A finding never pulls a deferred slice's scope forward.
154
151
  - **`no-findings`:** `No-findings:` names checks and inspected evidence. Bare
155
152
  pass, empty list, or “looks good” is malformed.
@@ -47,10 +47,9 @@ unaudited.
47
47
 
48
48
  ## Gate
49
49
 
50
- At plan, build, review, and seal, compare the proposed or actual change with each
51
- in-scope principle. An unexcepted violation is a Critical finding and blocks the
52
- phase. A missing or empty principles file passes silently.
50
+ At plan, build, review, and seal, compare the change with each in-scope principle.
51
+ An unexcepted violation is Critical and blocks. A missing or empty file passes.
53
52
 
54
- Adding, changing, retiring, or excepting a principle is a deliberate human-owned
55
- decision recorded in the file's governance history. Agents may propose wording;
56
- they do not ratify it.
53
+ Adding, changing, retiring, or excepting a principle is human-owned. Date it in
54
+ the governance history. Agents do not ratify it. Comply by changing the spec.
55
+ Ask for an exception only when the objective cannot be met without the violation.
@@ -39,4 +39,6 @@ recommended option stay human. A blocking question that already names option 1
39
39
  `(Recommended)` or `proposed:` is autocomplete's to resolve: pick that option
40
40
  via `devrites-engine state resolve` and continue. Recommended temper
41
41
  `expand` and extra acceptance are never auto-picked: they stop for a human through
42
- `AskUserQuestion`; do not invent product intent.
42
+ `AskUserQuestion`; do not invent product intent. A principle
43
+ conflict complies by changing the spec. Except a principle when the
44
+ objective cannot be met without the violation.
@@ -2,49 +2,32 @@
2
2
 
3
3
  > Root-only: not inlined in --role bundles.
4
4
 
5
- A second session plans the next sequence continuation while the active
6
- workspace builds, so the child's Spec → Vet overlaps its predecessor's
7
- Build → Seal. The main loop later resumes the planned child instead of
8
- specifying it again.
5
+ Plans the next continuation while the active workspace builds. Spec → Vet
6
+ overlaps the predecessor's Build → Seal.
9
7
 
10
8
  ## Start and target
11
9
 
12
- The operator starts the second host session with `/rite-autocomplete
13
- --lookahead` once the `.devrites/ACTIVE` workspace is in Build or later. The
14
- agent never launches a session itself. The target is the next recorded
15
- continuation after the active workspace: the entry
16
- [sequence continuation](loop.md#sequence-continuation) would open. Stop with
17
- nothing to do when the active workspace is before Build, no entry remains,
18
- `continue_sequence: false`, or the sequence budget is spent.
10
+ Start `/rite-autocomplete --lookahead` once `.devrites/ACTIVE` is in Build or later. The agent never launches a session. Target: the next continuation
11
+ ([sequence continuation](loop.md#sequence-continuation)). Stop when that workspace
12
+ is before Build, no entry remains, `continue_sequence: false`, or the budget is spent.
19
13
 
20
14
  ## Lease
21
15
 
22
- Before any write, take the workspace lease:
16
+ Before any write:
23
17
  `devrites-engine claim add --session lookahead-<target> --ttl 240 --workspace <target> --reason lookahead`.
24
- Exit 3 means another session holds the target: stop. Re-add the lease with the
25
- same session at each phase start, and release it with `devrites-engine claim
26
- release` on every stop. A crashed session's lease reads `dead` and frees itself.
18
+ Exit 3: held, stop. Re-add the same session each phase. Release with `devrites-engine claim release` on stop. A `dead` lease frees itself.
27
19
 
28
20
  ## Rules
29
21
 
30
- - Never read or write `.devrites/ACTIVE`. Pass the target slug explicitly to every
31
- skill and engine command, including `/rite-spec <target> "<objective>"` and
32
- `devrites-engine handoff <target> --resume`.
22
+ - Never read or write `.devrites/ACTIVE`. Pass the slug to `/rite-spec <target> "<objective>"` and `devrites-engine handoff <target> --resume`.
33
23
  - After the workspace exists, run `devrites-engine state set <target> sequence_lookahead=yes`.
34
- - Run Spec → Clarify → Temper → Define → Plan → Vet as the main loop does.
35
- Define and Plan read the predecessor's current tree and plan. Record an
36
- assumption for each predecessor interface the plan relies on.
37
- - At Vet, beside the readiness binding, record exactly one line
38
- `Predecessor readiness: <active-slug> <digest>` in `eng-review.md`. `<digest>`
39
- is the hex that `devrites-engine check readiness --emit-binding <active-slug>`
40
- prints. When the predecessor later changes its planning inputs, the child's
41
- readiness check blocks with `DRV-GATE-READINESS-STALE` and the main loop
42
- re-vets the child before Build.
43
- - Never Build, never write product source or tests, never commit, and never
44
- arm or rewrite `.devrites/AFK`. Owner questions go to the target's
45
- `questions.md` and stop the lookahead.
46
- - Stop at Vet `READY` with `next_action: build after predecessor Seal GO`, at a
47
- human gate, or when the target's lease is lost.
48
-
49
- **Failing case:** the lookahead passes Vet and starts Build while the
50
- predecessor is still sealing, so two root sessions write one tree.
24
+ - Run Spec → Clarify → Temper → Define → Plan → Vet. Define and Plan read the predecessor tree and plan. Record one assumption per predecessor interface used.
25
+ - At Vet record `Predecessor readiness: <active-slug> <digest>` in `eng-review.md` (the `--emit-binding` hex). A later planning change blocks `DRV-GATE-READINESS-STALE`; re-vet before Build.
26
+ - Predecessor still in Build or later forbids only Build, product source, tests, commits, and `.devrites/AFK`. It does not skip Define, Plan, or Vet, and is not `awaiting_human`. Acceptance, scope, and irreversible risk still stop.
27
+ - No spawn tool is not gate `access` or `awaiting_human`. Root writes Define and Plan. No Vet `READY` until those roles are admitted; else stop `running`, `next_action: /rite-vet`.
28
+ - On every entry, re-read `state.md` and `questions.md`. Do not report a stop from chat. `continuation: clear` with `awaiting_human` is not done: fold resolved decisions into the spec, then continue. An except-principle or change-the-spec question complies: change the spec and resolve it. Ask only when the approved objective cannot comply.
29
+ - Stop at Vet `READY` with `next_action: build after predecessor Seal GO`, at a real human gate, or when the lease is lost.
30
+
31
+ **Failing case:** Vet then Build while the predecessor is still sealing, so two sessions write one tree. Or Define, Plan, and Vet skipped because the predecessor is still Build, or a missing strategy-reviewer spawn recorded as gate `access`.
32
+
33
+ **Failing case:** stops to except a principle the spec can avoid.