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.
- package/CHANGELOG.md +6 -0
- package/README.md +1 -1
- package/SECURITY.md +11 -7
- package/docs/engine/commands.md +3 -3
- package/engine/internal/lib/context.go +7 -4
- package/engine/internal/lib/dispatch.go +77 -21
- package/engine/internal/lib/taskgraph.go +94 -21
- package/pack/.claude/skills/devrites-lib/reference/parallel-dispatch.md +3 -3
- package/pack/.claude/skills/devrites-lib/reference/standards/agents.md +3 -6
- package/pack/.claude/skills/devrites-lib/reference/standards/principles.md +5 -6
- package/pack/.claude/skills/rite-autocomplete/reference/decision-policy.md +3 -1
- package/pack/.claude/skills/rite-autocomplete/reference/lookahead.md +18 -35
- package/pack/.claude/skills/rite-autocomplete/reference/stop-conditions.md +7 -8
- package/pack/.claude/skills/rite-build/reference/parallel-batch.md +23 -13
- package/pack/.claude/skills/rite-build/reference/wright-dispatch.md +5 -5
- package/pack/.claude/skills/rite-spec/SKILL.md +2 -2
- package/pack/.claude/skills/rite-temper/SKILL.md +4 -5
- package/pack/generated/claude/skills/devrites-lib/reference/parallel-dispatch.md +3 -3
- package/pack/generated/claude/skills/devrites-lib/reference/standards/agents.md +3 -6
- package/pack/generated/claude/skills/devrites-lib/reference/standards/principles.md +5 -6
- package/pack/generated/claude/skills/rite-autocomplete/reference/decision-policy.md +3 -1
- package/pack/generated/claude/skills/rite-autocomplete/reference/lookahead.md +18 -35
- package/pack/generated/claude/skills/rite-autocomplete/reference/stop-conditions.md +7 -8
- package/pack/generated/claude/skills/rite-build/reference/parallel-batch.md +23 -13
- package/pack/generated/claude/skills/rite-build/reference/wright-dispatch.md +5 -5
- package/pack/generated/claude/skills/rite-spec/SKILL.md +2 -2
- package/pack/generated/claude/skills/rite-temper/SKILL.md +4 -5
- package/pack/generated/codex/skills/devrites-lib/reference/parallel-dispatch.md +3 -3
- package/pack/generated/codex/skills/devrites-lib/reference/standards/agents.md +3 -6
- package/pack/generated/codex/skills/devrites-lib/reference/standards/principles.md +5 -6
- package/pack/generated/codex/skills/rite-autocomplete/reference/decision-policy.md +3 -1
- package/pack/generated/codex/skills/rite-autocomplete/reference/lookahead.md +18 -35
- package/pack/generated/codex/skills/rite-autocomplete/reference/stop-conditions.md +7 -8
- package/pack/generated/codex/skills/rite-build/reference/parallel-batch.md +23 -13
- package/pack/generated/codex/skills/rite-build/reference/wright-dispatch.md +5 -5
- package/pack/generated/codex/skills/rite-spec/SKILL.md +2 -2
- package/pack/generated/codex/skills/rite-temper/SKILL.md +4 -5
- package/pack/generated/devin/skills/devrites-lib/reference/parallel-dispatch.md +3 -3
- package/pack/generated/devin/skills/devrites-lib/reference/standards/agents.md +3 -6
- package/pack/generated/devin/skills/devrites-lib/reference/standards/principles.md +5 -6
- package/pack/generated/devin/skills/rite-autocomplete/reference/decision-policy.md +3 -1
- package/pack/generated/devin/skills/rite-autocomplete/reference/lookahead.md +18 -35
- package/pack/generated/devin/skills/rite-autocomplete/reference/stop-conditions.md +7 -8
- package/pack/generated/devin/skills/rite-build/reference/parallel-batch.md +23 -13
- package/pack/generated/devin/skills/rite-build/reference/wright-dispatch.md +5 -5
- package/pack/generated/devin/skills/rite-spec/SKILL.md +2 -2
- package/pack/generated/devin/skills/rite-temper/SKILL.md +4 -5
- package/pack/generated/dsh/skills/devrites-lib/reference/parallel-dispatch.md +3 -3
- package/pack/generated/dsh/skills/devrites-lib/reference/standards/agents.md +3 -6
- package/pack/generated/dsh/skills/devrites-lib/reference/standards/principles.md +5 -6
- package/pack/generated/dsh/skills/rite-autocomplete/reference/decision-policy.md +3 -1
- package/pack/generated/dsh/skills/rite-autocomplete/reference/lookahead.md +18 -35
- package/pack/generated/dsh/skills/rite-autocomplete/reference/stop-conditions.md +7 -8
- package/pack/generated/dsh/skills/rite-build/reference/parallel-batch.md +23 -13
- package/pack/generated/dsh/skills/rite-build/reference/wright-dispatch.md +5 -5
- package/pack/generated/dsh/skills/rite-spec/SKILL.md +2 -2
- package/pack/generated/dsh/skills/rite-temper/SKILL.md +4 -5
- package/pack/generated/omp/skills/devrites-lib/reference/parallel-dispatch.md +3 -3
- package/pack/generated/omp/skills/devrites-lib/reference/standards/agents.md +3 -6
- package/pack/generated/omp/skills/devrites-lib/reference/standards/principles.md +5 -6
- package/pack/generated/omp/skills/rite-autocomplete/reference/decision-policy.md +3 -1
- package/pack/generated/omp/skills/rite-autocomplete/reference/lookahead.md +18 -35
- package/pack/generated/omp/skills/rite-autocomplete/reference/stop-conditions.md +7 -8
- package/pack/generated/omp/skills/rite-build/reference/parallel-batch.md +23 -13
- package/pack/generated/omp/skills/rite-build/reference/wright-dispatch.md +5 -5
- package/pack/generated/omp/skills/rite-spec/SKILL.md +2 -2
- package/pack/generated/omp/skills/rite-temper/SKILL.md +4 -5
- package/pack/generated/pi/skills/devrites-lib/reference/parallel-dispatch.md +3 -3
- package/pack/generated/pi/skills/devrites-lib/reference/standards/agents.md +3 -6
- package/pack/generated/pi/skills/devrites-lib/reference/standards/principles.md +5 -6
- package/pack/generated/pi/skills/rite-autocomplete/reference/decision-policy.md +3 -1
- package/pack/generated/pi/skills/rite-autocomplete/reference/lookahead.md +18 -35
- package/pack/generated/pi/skills/rite-autocomplete/reference/stop-conditions.md +7 -8
- package/pack/generated/pi/skills/rite-build/reference/parallel-batch.md +23 -13
- package/pack/generated/pi/skills/rite-build/reference/wright-dispatch.md +5 -5
- package/pack/generated/pi/skills/rite-spec/SKILL.md +2 -2
- package/pack/generated/pi/skills/rite-temper/SKILL.md +4 -5
- 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.
|
|
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
|
-
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
|
|
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
|
|
package/docs/engine/commands.md
CHANGED
|
@@ -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
|
|
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
|
|
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
|
|
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
|
-
//
|
|
95
|
-
//
|
|
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
|
|
61
|
-
|
|
62
|
-
|
|
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"
|
|
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)
|
|
820
|
-
|
|
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
|
-
//
|
|
825
|
-
//
|
|
826
|
-
//
|
|
827
|
-
|
|
828
|
-
|
|
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
|
-
|
|
835
|
-
if err != nil || len(batch)
|
|
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);
|
|
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
|
|
199
|
-
//
|
|
200
|
-
//
|
|
201
|
-
//
|
|
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
|
|
237
|
-
// built — <sha>`, or `- [x] Slice 4:
|
|
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
|
-
|
|
260
|
-
|
|
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
|
|
265
|
-
modulePathRe
|
|
266
|
-
npmScopeRe
|
|
267
|
-
versionPinRe
|
|
268
|
-
|
|
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.
|
|
274
|
-
//
|
|
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
|
-
|
|
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
|
|
173
|
-
counting children any leaf spawned
|
|
174
|
-
Refuse
|
|
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 —
|
|
149
|
-
|
|
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
|
|
51
|
-
|
|
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
|
|
55
|
-
|
|
56
|
-
|
|
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
|
-
|
|
6
|
-
|
|
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
|
|
13
|
-
|
|
14
|
-
|
|
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
|
|
16
|
+
Before any write:
|
|
23
17
|
`devrites-engine claim add --session lookahead-<target> --ttl 240 --workspace <target> --reason lookahead`.
|
|
24
|
-
Exit 3
|
|
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
|
|
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
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
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.
|