devrites 6.1.5 → 6.3.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (76) hide show
  1. package/CHANGELOG.md +12 -0
  2. package/README.md +1 -1
  3. package/docs/engine/commands.md +2 -2
  4. package/engine/help.go +1 -1
  5. package/engine/internal/lib/dispatch.go +12 -5
  6. package/engine/internal/lib/dispatch_contract.go +51 -0
  7. package/engine/internal/lib/drift.go +14 -2
  8. package/engine/internal/lib/proportion.go +130 -0
  9. package/engine/internal/lib/slicecheck.go +1 -0
  10. package/pack/.claude/agents/devrites-code-reviewer.md +1 -1
  11. package/pack/.claude/agents/devrites-plan-drafter.md +3 -1
  12. package/pack/.claude/agents/devrites-plan-reviewer.md +4 -1
  13. package/pack/.claude/skills/devrites-lib/reference/standards/agents.md +8 -1
  14. package/pack/.claude/skills/devrites-lib/reference/standards/loop-operations.md +4 -0
  15. package/pack/.claude/skills/rite-build/SKILL.md +2 -2
  16. package/pack/.claude/skills/rite-build/reference/one-slice-cycle.md +3 -1
  17. package/pack/.claude/skills/rite-build/reference/wright-dispatch.md +8 -4
  18. package/pack/.claude/skills/rite-define/SKILL.md +3 -1
  19. package/pack/.claude/skills/rite-vet/SKILL.md +3 -1
  20. package/pack/.claude/skills/rite-vet/reference/artifacts.md +14 -3
  21. package/pack/generated/claude/agents/devrites-code-reviewer.md +1 -1
  22. package/pack/generated/claude/agents/devrites-plan-drafter.md +3 -1
  23. package/pack/generated/claude/agents/devrites-plan-reviewer.md +4 -1
  24. package/pack/generated/claude/skills/devrites-lib/reference/standards/agents.md +8 -1
  25. package/pack/generated/claude/skills/devrites-lib/reference/standards/loop-operations.md +4 -0
  26. package/pack/generated/claude/skills/rite-build/SKILL.md +2 -2
  27. package/pack/generated/claude/skills/rite-build/reference/one-slice-cycle.md +3 -1
  28. package/pack/generated/claude/skills/rite-build/reference/wright-dispatch.md +8 -4
  29. package/pack/generated/claude/skills/rite-define/SKILL.md +3 -1
  30. package/pack/generated/claude/skills/rite-vet/SKILL.md +3 -1
  31. package/pack/generated/claude/skills/rite-vet/reference/artifacts.md +14 -3
  32. package/pack/generated/codex/agents/devrites-code-reviewer.toml +1 -1
  33. package/pack/generated/codex/agents/devrites-plan-drafter.toml +3 -1
  34. package/pack/generated/codex/agents/devrites-plan-reviewer.toml +4 -1
  35. package/pack/generated/codex/skills/devrites-lib/reference/standards/agents.md +8 -1
  36. package/pack/generated/codex/skills/devrites-lib/reference/standards/loop-operations.md +4 -0
  37. package/pack/generated/codex/skills/rite-build/SKILL.md +2 -2
  38. package/pack/generated/codex/skills/rite-build/reference/one-slice-cycle.md +3 -1
  39. package/pack/generated/codex/skills/rite-build/reference/wright-dispatch.md +8 -4
  40. package/pack/generated/codex/skills/rite-define/SKILL.md +3 -1
  41. package/pack/generated/codex/skills/rite-vet/SKILL.md +3 -1
  42. package/pack/generated/codex/skills/rite-vet/reference/artifacts.md +14 -3
  43. package/pack/generated/devin/agents/devrites-code-reviewer.md +1 -1
  44. package/pack/generated/devin/agents/devrites-plan-drafter.md +3 -1
  45. package/pack/generated/devin/agents/devrites-plan-reviewer.md +4 -1
  46. package/pack/generated/devin/skills/devrites-lib/reference/standards/agents.md +8 -1
  47. package/pack/generated/devin/skills/devrites-lib/reference/standards/loop-operations.md +4 -0
  48. package/pack/generated/devin/skills/rite-build/SKILL.md +2 -2
  49. package/pack/generated/devin/skills/rite-build/reference/one-slice-cycle.md +3 -1
  50. package/pack/generated/devin/skills/rite-build/reference/wright-dispatch.md +8 -4
  51. package/pack/generated/devin/skills/rite-define/SKILL.md +3 -1
  52. package/pack/generated/devin/skills/rite-vet/SKILL.md +3 -1
  53. package/pack/generated/devin/skills/rite-vet/reference/artifacts.md +14 -3
  54. package/pack/generated/omp/agents/devrites-code-reviewer.md +1 -1
  55. package/pack/generated/omp/agents/devrites-plan-drafter.md +3 -1
  56. package/pack/generated/omp/agents/devrites-plan-reviewer.md +4 -1
  57. package/pack/generated/omp/skills/devrites-lib/reference/standards/agents.md +8 -1
  58. package/pack/generated/omp/skills/devrites-lib/reference/standards/loop-operations.md +4 -0
  59. package/pack/generated/omp/skills/rite-build/SKILL.md +2 -2
  60. package/pack/generated/omp/skills/rite-build/reference/one-slice-cycle.md +3 -1
  61. package/pack/generated/omp/skills/rite-build/reference/wright-dispatch.md +8 -4
  62. package/pack/generated/omp/skills/rite-define/SKILL.md +3 -1
  63. package/pack/generated/omp/skills/rite-vet/SKILL.md +3 -1
  64. package/pack/generated/omp/skills/rite-vet/reference/artifacts.md +14 -3
  65. package/pack/generated/pi/agents/devrites-code-reviewer.md +1 -1
  66. package/pack/generated/pi/agents/devrites-plan-drafter.md +3 -1
  67. package/pack/generated/pi/agents/devrites-plan-reviewer.md +4 -1
  68. package/pack/generated/pi/skills/devrites-lib/reference/standards/agents.md +8 -1
  69. package/pack/generated/pi/skills/devrites-lib/reference/standards/loop-operations.md +4 -0
  70. package/pack/generated/pi/skills/rite-build/SKILL.md +2 -2
  71. package/pack/generated/pi/skills/rite-build/reference/one-slice-cycle.md +3 -1
  72. package/pack/generated/pi/skills/rite-build/reference/wright-dispatch.md +8 -4
  73. package/pack/generated/pi/skills/rite-define/SKILL.md +3 -1
  74. package/pack/generated/pi/skills/rite-vet/SKILL.md +3 -1
  75. package/pack/generated/pi/skills/rite-vet/reference/artifacts.md +14 -3
  76. package/package.json +1 -1
package/CHANGELOG.md CHANGED
@@ -2,6 +2,18 @@
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
+ ## [6.3.0](https://github.com/ViktorsBaikers/DevRites/compare/v6.2.0...v6.3.0) (2026-10-16)
6
+
7
+ ### Added
8
+
9
+ * **devrites:** guard proof proportionality across phases ([#113](https://github.com/ViktorsBaikers/DevRites/issues/113)) ([2222b60](https://github.com/ViktorsBaikers/DevRites/commit/2222b60563bf28202d163406d27cf7182c6431f0))
10
+
11
+ ## [6.2.0](https://github.com/ViktorsBaikers/DevRites/compare/v6.1.5...v6.2.0) (2026-10-16)
12
+
13
+ ### Added
14
+
15
+ * **devrites:** lint wright contracts at dispatch open ([#112](https://github.com/ViktorsBaikers/DevRites/issues/112)) ([391a931](https://github.com/ViktorsBaikers/DevRites/commit/391a9317dba9f00941ed29c48cc1f3f2ae8551c4))
16
+
5
17
  ## [6.1.5](https://github.com/ViktorsBaikers/DevRites/compare/v6.1.4...v6.1.5) (2026-10-16)
6
18
 
7
19
  ### 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:** [`v6.1.5`](https://github.com/ViktorsBaikers/DevRites/releases/tag/v6.1.5): see [`CHANGELOG.md`](CHANGELOG.md) for release notes.
34
+ **Status:** [`v6.3.0`](https://github.com/ViktorsBaikers/DevRites/releases/tag/v6.3.0): 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
 
@@ -20,10 +20,10 @@ workflow strategy.
20
20
  | `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. |
21
21
  | `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; 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. |
22
22
  | `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. |
23
- | `check drift <slug> [--record]` | Attribute readiness-input changes to the exact artifact. `--record` writes the per-input `readiness-inputs.json` digest baseline under the feature lock; without it the workspace diffs current inputs against that baseline (or falls back to the aggregate binding recorded in `eng-review.md`). Advisory: exit `0` on drift; a corrupt baseline exits `3`. A `test-plan.md` change limited to exact `Workflow Artifact reviewed identity: <64hex>` lines is reported as `reviewed-identity-only` (ADR-0034). |
23
+ | `check drift <slug> [--record]` | Attribute readiness-input changes to the exact artifact. `--record` writes the per-input `readiness-inputs.json` digest baseline under the feature lock; without it the workspace diffs current inputs against that baseline (or falls back to the aggregate binding recorded in `eng-review.md`). Advisory: exit `0` on drift; a corrupt baseline exits `3`. A `test-plan.md` change limited to exact `Workflow Artifact reviewed identity: <64hex>` lines is reported as `reviewed-identity-only` (ADR-0034). `--record` also stores each slice's proof bytes (its `tasks.md` block plus the `test-plan.md` lines naming it); both modes print `ADVISORY proportion:` for a slice whose proof grew over 2x since that baseline or carries over 3x the median proof per AC. |
24
24
  | `check windows <slug> [--worktree\|--staged\|--base <ref>]` | Fail on deferral and bar-lowering markers the change introduces without a waiver row in the workspace's `windows.md`: added TODO/FIXME/XXX/HACK, skipped/focused-test and stub idioms, checker suppressions, one-line empty catch; removed `deleted-test` files and net `removed-assertion` lines in remaining test files. |
25
25
  | `check dup [slug] [--all\|--worktree\|--staged\|--base <ref>] [--min-lines n] [--threshold f] [--ignore-file <path>] [--limit n] [--exclude <csv>]` | Report near-duplicate code clusters that survive comment stripping, literal collapsing, and identifier renaming — token-shingle matches chained into segments, clustered transitively, ranked with bounded path/line-distance boosts. Diff modes mark units overlapping changed hunks with `*`; stable content-derived cluster hashes let `.devrites/dup-ignore` dismissals survive line moves but resurface on structural edits. Staged mode reads index blobs and excludes untracked files. Bounded heuristic: successful scans/leads exit `0`; usage, unavailable input, and partial scans exit `2`. |
26
- | `check slice <slug> <SLICE-ID>` | Pre-dispatch lint of one slice's wright contract: contract present, exact Writer allowlist paths, resolvable `Satisfies` AC ids, bounded scope. Same non-blocking size advisory as `check task-graph`. |
26
+ | `check slice <slug> <SLICE-ID>` | Pre-dispatch lint of one slice's wright contract: contract present, exact Writer allowlist paths, resolvable `Satisfies` AC ids, bounded scope. Same non-blocking size advisory as `check task-graph`, plus the `check drift` proportion advisory for that slice. |
27
27
  | `check diff-scope <slug> --allow <csv>\|--allow-file <path> [--worktree\|--staged\|--base <ref>]` | Verify the changed-path set stays inside the declared allowlist; the mechanical gate run before reviewer dispatch. |
28
28
  | `check skill-trust <path>` | Scan one skill/agent Markdown file for structural trust violations. |
29
29
  | `check indexes [--root <dir>]` | Report install manifest and code-index presence as JSON. |
package/engine/help.go CHANGED
@@ -58,7 +58,7 @@ const (
58
58
  metrics record <slug> --phase <p> --event <e> [--role r] [--bytes n] [--note s]
59
59
  metrics summary [slug] [--by-slice]
60
60
  `
61
- dispatchUsage = `usage: devrites-engine dispatch <slug> <open|start|seal|return|status|abandon> [--phase p] [--wave w] [--role r] [--handle h] [--reason s] [--slice SLICE-###] [--reslice-review <DRIFT-ID>]`
61
+ dispatchUsage = `usage: devrites-engine dispatch <slug> <open|start|seal|return|status|abandon> [--phase p] [--wave w] [--role r] [--handle h] [--reason s] [--slice SLICE-###] [--reslice-review <DRIFT-ID>] [--contract <file>]`
62
62
  claimUsageText = `usage: devrites-engine claim <add|release|list|check> ...
63
63
 
64
64
  claim add --session <id> [--ttl <minutes>] [--reason <s>] <path>...
@@ -21,9 +21,9 @@ import (
21
21
  // fan-out cannot silently degrade to serial (open -> every role started ->
22
22
  // seal -> returns -> complete). start/return also auto-record metrics events.
23
23
  //
24
- // devrites-engine dispatch <slug> <sub> [--phase p] [--wave w] [--role r] [--handle h] [--reason s] [--slice SLICE-###] [--reslice-review <DRIFT-ID>]
24
+ // devrites-engine dispatch <slug> <sub> [--phase p] [--wave w] [--role r] [--handle h] [--reason s] [--slice SLICE-###] [--reslice-review <DRIFT-ID>] [--contract <file>]
25
25
 
26
- const dispatchUsage = `usage: devrites-engine dispatch <slug> <open|start|seal|return|status|abandon> [--phase p] [--wave w] [--role r] [--handle h] [--reason s] [--slice SLICE-###] [--reslice-review <DRIFT-ID>]
26
+ const dispatchUsage = `usage: devrites-engine dispatch <slug> <open|start|seal|return|status|abandon> [--phase p] [--wave w] [--role r] [--handle h] [--reason s] [--slice SLICE-###] [--reslice-review <DRIFT-ID>] [--contract <file>]
27
27
 
28
28
  Wave barrier for parallel agent dispatch: seal fails until every declared
29
29
  role has a distinct handle; return fails before seal. status --wave exits
@@ -117,7 +117,7 @@ func nowStamp() string { return time.Now().UTC().Format(time.RFC3339) }
117
117
 
118
118
  // RunDispatch implements the dispatch wave state machine.
119
119
  func RunDispatch(root string, args []string, stdout, stderr io.Writer) int {
120
- var slug, sub, phase, wave, role, handle, reason, review string
120
+ var slug, sub, phase, wave, role, handle, reason, review, contract string
121
121
  var roles, sliceIDs []string
122
122
  var positional []string
123
123
  for i := 0; i < len(args); i++ {
@@ -138,6 +138,8 @@ func RunDispatch(root string, args []string, stdout, stderr io.Writer) int {
138
138
  sliceIDs = append(sliceIDs, value)
139
139
  case "--reslice-review":
140
140
  review = value
141
+ case "--contract":
142
+ contract = value
141
143
  default:
142
144
  if strings.HasPrefix(args[i], "-") {
143
145
  fmt.Fprintf(stderr, "dispatch: unknown flag %q\n", args[i])
@@ -161,7 +163,7 @@ func RunDispatch(root string, args []string, stdout, stderr io.Writer) int {
161
163
  // an update and strand the wave short of complete.
162
164
  code := 0
163
165
  if err := state.WithFeatureLock(root, slug, func() error {
164
- code = dispatchLocked(root, slug, sub, phase, wave, role, handle, reason, roles, sliceIDs, review, stdout, stderr)
166
+ code = dispatchLocked(root, slug, sub, phase, wave, role, handle, reason, roles, sliceIDs, review, contract, stdout, stderr)
165
167
  return nil
166
168
  }); err != nil {
167
169
  fmt.Fprintf(stderr, "dispatch: %v\n", err)
@@ -170,7 +172,7 @@ func RunDispatch(root string, args []string, stdout, stderr io.Writer) int {
170
172
  return code
171
173
  }
172
174
 
173
- func dispatchLocked(root, slug, sub, phase, wave, role, handle, reason string, roles, sliceIDs []string, review string, stdout, stderr io.Writer) int {
175
+ func dispatchLocked(root, slug, sub, phase, wave, role, handle, reason string, roles, sliceIDs []string, review, contract string, stdout, stderr io.Writer) int {
174
176
  f, path, err := loadDispatch(root, slug)
175
177
  if err != nil {
176
178
  fmt.Fprintf(stderr, "dispatch: %v\n", err)
@@ -209,6 +211,11 @@ func dispatchLocked(root, slug, sub, phase, wave, role, handle, reason string, r
209
211
  if code != 0 {
210
212
  return code
211
213
  }
214
+ if isBuildWrightWave(w) {
215
+ if err := lintWrightContract(contract); err != nil {
216
+ return fail("BLOCKED: %v", err)
217
+ }
218
+ }
212
219
  f.Waves[wave] = w
213
220
  if err := saveDispatch(path, f); err != nil {
214
221
  return fail("%v", err)
@@ -0,0 +1,51 @@
1
+ package lib
2
+
3
+ import (
4
+ "fmt"
5
+ "regexp"
6
+ "strings"
7
+ )
8
+
9
+ // maxContractBytes bounds a wright contract read; packets stay <= 64 KiB.
10
+ const maxContractBytes = 64 << 10
11
+
12
+ var (
13
+ // writerSafeLineRE is the contract line that names the checks the wright
14
+ // itself runs before returning.
15
+ writerSafeLineRE = regexp.MustCompile(`(?im)^[\s>*#-]*writer-safe checks\s*:\s*\S`)
16
+ // forbidExecutionRE catches contract text that leaves the wright blind:
17
+ // "skip tests", "no tests/build", "do not run any checks", "run nothing".
18
+ forbidExecutionRE = regexp.MustCompile(`(?i)\bskip(?:s|ping)?\s+(?:all\s+|the\s+)?(?:builds?|lint|tests?|testing|checks?|formatters?)\b` +
19
+ `|\bno\s+(?:leaf\s+)?(?:tests?|test\s+commands?|builds?|lint|execution|checks?)\b` +
20
+ `|\b(?:do\s+not|don't|never)\s+run\s+(?:any\s+)?(?:tests?|checks?|builds?|lint|commands?)\b` +
21
+ `|\brun\s+nothing\b`)
22
+ // allowedAfterRE marks a forbid match that is about not weakening tests,
23
+ // not about skipping their execution ("no tests weakened or deleted").
24
+ allowedAfterRE = regexp.MustCompile(`(?i)^[\s/,a-z-]{0,24}\b(?:weaken|delet|remov|disabl|skipp|xfail|loosen)`)
25
+ )
26
+
27
+ // lintWrightContract refuses a build wright contract that forbids the wright's
28
+ // own writer-safe verification or names none, so a blind round is rejected
29
+ // before launch rather than discovered after the wright returns.
30
+ func lintWrightContract(path string) error {
31
+ if path == "" {
32
+ return fmt.Errorf("a build wright wave needs --contract <file> holding the exact wright contract (with a `Writer-safe checks:` line)")
33
+ }
34
+ raw, err := readBoundedRegularFile(path, maxContractBytes)
35
+ if err != nil {
36
+ return fmt.Errorf("wright contract %s: %w", path, err)
37
+ }
38
+ for i, line := range strings.Split(string(raw), "\n") {
39
+ for _, loc := range forbidExecutionRE.FindAllStringIndex(line, -1) {
40
+ if allowedAfterRE.MatchString(line[loc[1]:]) {
41
+ continue
42
+ }
43
+ return fmt.Errorf("wright contract %s:%d forbids writer-safe execution (%q); the wright runs syntax/type/lint, unit and self-check suites and a narrow local smoke itself — list them under `Writer-safe checks:` and keep only artifact-producing, consumptive, or full canonical gates root-owned",
44
+ path, i+1, strings.TrimSpace(line[loc[0]:loc[1]]))
45
+ }
46
+ }
47
+ if !writerSafeLineRE.Match(raw) {
48
+ return fmt.Errorf("wright contract %s names no `Writer-safe checks:` line; list the syntax/type/lint, unit/self-check and narrow smoke commands the wright runs before returning", path)
49
+ }
50
+ return nil
51
+ }
@@ -44,6 +44,8 @@ type driftBaseline struct {
44
44
  RecordedAt string `json:"recorded_at"`
45
45
  Binding string `json:"binding"`
46
46
  Inputs []gate.ReadinessInputDigest `json:"inputs"`
47
+ // ProofBytes is each slice's proof size at Vet, for the growth advisory.
48
+ ProofBytes map[string]int `json:"proof_bytes,omitempty"`
47
49
  }
48
50
 
49
51
  // driftDiff is one input whose state differs from the baseline.
@@ -78,10 +80,16 @@ func RunCheckDrift(root string, args []string, stdout, stderr io.Writer) int {
78
80
  return 2
79
81
  }
80
82
  baselinePath := filepath.Join(workspace, driftBaselineFile)
83
+ code := 0
81
84
  if record {
82
- return recordDriftBaseline(root, slug, baselinePath, digests, binding, stdout, stderr)
85
+ code = recordDriftBaseline(root, slug, baselinePath, digests, binding, stdout, stderr)
86
+ } else {
87
+ code = compareDrift(workspace, baselinePath, digests, binding, stdout, stderr)
83
88
  }
84
- return compareDrift(workspace, baselinePath, digests, binding, stdout, stderr)
89
+ if code == 0 {
90
+ printProportion(root, slug, "", stdout)
91
+ }
92
+ return code
85
93
  }
86
94
 
87
95
  // recordDriftBaseline writes the current per-input digests as the baseline.
@@ -91,6 +99,10 @@ func recordDriftBaseline(root, slug, baselinePath string, digests []gate.Readine
91
99
  RecordedAt: time.Now().UTC().Format(time.RFC3339),
92
100
  Binding: binding,
93
101
  Inputs: digests,
102
+ ProofBytes: map[string]int{},
103
+ }
104
+ for id, w := range workspaceProofWeights(root, slug) {
105
+ baseline.ProofBytes[id] = w.Bytes
94
106
  }
95
107
  payload, err := json.MarshalIndent(baseline, "", " ")
96
108
  if err != nil {
@@ -0,0 +1,130 @@
1
+ package lib
2
+
3
+ import (
4
+ "fmt"
5
+ "io"
6
+ "path/filepath"
7
+ "regexp"
8
+ "sort"
9
+ "strings"
10
+
11
+ "github.com/devrites/devrites/internal/devritespaths"
12
+ "github.com/devrites/devrites/internal/state"
13
+ )
14
+
15
+ // proportion.go: proof proportionality advisories. A slice whose proof keeps
16
+ // growing after Vet, or that carries far more proof per acceptance criterion
17
+ // than its siblings, is usually absorbing hardening no spec line asked for —
18
+ // the cause of long vet↔plan and review↔repair loops. The advisory names the
19
+ // slice so the root moves uncited rows into an optional slice instead of
20
+ // repairing them; it never blocks.
21
+
22
+ const (
23
+ // proofGrowthLimit is the growth factor since the Vet baseline that warns.
24
+ proofGrowthLimit = 2
25
+ // proofRatioLimit is how many times the median per-AC proof weight warns.
26
+ proofRatioLimit = 3
27
+ // proofRatioMinSlices is the sibling count below which a median means little.
28
+ proofRatioMinSlices = 3
29
+ )
30
+
31
+ // sliceWeight is one slice's proof size: its tasks.md block plus every
32
+ // test-plan.md line that names it, and the distinct AC IDs its block cites.
33
+ type sliceWeight struct {
34
+ Bytes int
35
+ ACs int
36
+ }
37
+
38
+ // proofWeights measures every SLICE-### in tasks.md; a missing test-plan.md
39
+ // counts as empty.
40
+ func proofWeights(tasks, testPlan []byte) map[string]sliceWeight {
41
+ graph := ParseTaskGraph(tasks)
42
+ planLines := strings.Split(string(testPlan), "\n")
43
+ weights := make(map[string]sliceWeight, len(graph.Slices))
44
+ for _, s := range graph.Slices {
45
+ block, _ := state.ExtractTaskSlice(tasks, s.ID)
46
+ w := sliceWeight{Bytes: len(block)}
47
+ seen := map[string]bool{}
48
+ for _, ac := range sliceACRe.FindAllString(block, -1) {
49
+ seen[ac] = true
50
+ }
51
+ w.ACs = len(seen)
52
+ idRE := regexp.MustCompile(`\b` + regexp.QuoteMeta(s.ID) + `\b`)
53
+ for _, line := range planLines {
54
+ if idRE.MatchString(line) {
55
+ w.Bytes += len(line) + 1
56
+ }
57
+ }
58
+ weights[s.ID] = w
59
+ }
60
+ return weights
61
+ }
62
+
63
+ // proportionAdvisories compares current weights against the Vet baseline
64
+ // (growth) and against sibling slices (per-AC ratio). only limits output to
65
+ // one slice; empty means all.
66
+ func proportionAdvisories(weights map[string]sliceWeight, baseline map[string]int, only string) []string {
67
+ var perAC []float64
68
+ for _, w := range weights {
69
+ if w.ACs > 0 {
70
+ perAC = append(perAC, float64(w.Bytes)/float64(w.ACs))
71
+ }
72
+ }
73
+ sort.Float64s(perAC)
74
+ median := 0.0
75
+ if len(perAC) >= proofRatioMinSlices {
76
+ median = perAC[len(perAC)/2]
77
+ }
78
+ ids := make([]string, 0, len(weights))
79
+ for id := range weights {
80
+ if only == "" || id == only {
81
+ ids = append(ids, id)
82
+ }
83
+ }
84
+ sort.Strings(ids)
85
+ const fix = "move proof rows that quote no AC/REQ/INV/PROH/DEC/EDGE line into an optional slice instead of repairing them"
86
+ var lines []string
87
+ for _, id := range ids {
88
+ w := weights[id]
89
+ if base := baseline[id]; base > 0 && w.Bytes > proofGrowthLimit*base {
90
+ lines = append(lines, fmt.Sprintf("ADVISORY proportion: %s proof grew %.1fx since the Vet baseline (%d -> %d bytes); %s",
91
+ id, float64(w.Bytes)/float64(base), base, w.Bytes, fix))
92
+ }
93
+ if median > 0 && w.ACs > 0 {
94
+ if ratio := float64(w.Bytes) / float64(w.ACs) / median; ratio > proofRatioLimit {
95
+ lines = append(lines, fmt.Sprintf("ADVISORY proportion: %s carries %d bytes of proof for %d AC(s), %.1fx the median slice; %s",
96
+ id, w.Bytes, w.ACs, ratio, fix))
97
+ }
98
+ }
99
+ }
100
+ return lines
101
+ }
102
+
103
+ // workspaceProofWeights reads tasks.md and test-plan.md for slug; nil when
104
+ // tasks.md is unreadable, so the advisory stays silent rather than wrong.
105
+ func workspaceProofWeights(root, slug string) map[string]sliceWeight {
106
+ tasks, err := readWorkspaceArtifact(root, slug, "tasks.md")
107
+ if err != nil {
108
+ return nil
109
+ }
110
+ testPlan, _ := readWorkspaceArtifact(root, slug, "test-plan.md")
111
+ return proofWeights(tasks, testPlan)
112
+ }
113
+
114
+ // printProportion writes the advisories for slug (one slice when only is set)
115
+ // against the drift baseline's recorded proof sizes, when one exists.
116
+ func printProportion(root, slug, only string, stdout io.Writer) {
117
+ weights := workspaceProofWeights(root, slug)
118
+ if weights == nil {
119
+ return
120
+ }
121
+ var recorded map[string]int
122
+ if workspace, err := devritespaths.ExistingFeatureDirChecked(root, slug); err == nil {
123
+ if base, ok, err := readDriftBaseline(filepath.Join(workspace, driftBaselineFile)); err == nil && ok {
124
+ recorded = base.ProofBytes
125
+ }
126
+ }
127
+ for _, line := range proportionAdvisories(weights, recorded, only) {
128
+ fmt.Fprintln(stdout, line)
129
+ }
130
+ }
@@ -188,6 +188,7 @@ func RunCheckSlice(root string, args []string, stdout, stderr io.Writer) int {
188
188
  if n, over := sliceSizeAdvisory(block); over {
189
189
  fmt.Fprintf(stdout, "check slice: ADVISORY size: %s has %d Writer allowlist files (>= %d); consider an acceptance-preserving split\n", sliceID, n, sliceFileAdvisory)
190
190
  }
191
+ printProportion(root, slug, sliceID, stdout)
191
192
  if len(problems) > 0 {
192
193
  fmt.Fprintf(stdout, "check slice: %s BLOCKED\n", sliceID)
193
194
  for _, p := range problems {
@@ -155,7 +155,7 @@ Outcome: <findings | no-findings | gap>
155
155
  Counts: <n per severity or kind used in the rows below>
156
156
  Account: <admitted findings | No-findings | Gap per Result admission>
157
157
  Coverage: <files read fully | hunk-level | skipped: name — reason>
158
- Finding: <severity> conf <n> | <file:line> | <observed> | when <input/state/sequence> → <effect> | <minimum fix>
158
+ Finding: <severity> conf <n> | <file:line> | <observed> | when <input/state/sequence> → <effect> | breaks <ID or standard §> "<quoted line>" | <minimum fix>
159
159
  Basis: <files read · commands run to reach this finding>
160
160
  Contract: <applicable | not-applicable> — <basis: both sides read · crossing test>
161
161
  Tests: <adequate? gaps>
@@ -23,7 +23,9 @@ seams supplied by the orchestrator.
23
23
  Reuse existing seams and dependencies before proposing new ones. Build vertical
24
24
  slices in dependency order; put a hub file's shared edit in one seam slice
25
25
  ([slicing.md](../skills/rite-plan/reference/slicing.md#slice-independence)). Map every REQ and AC to a slice and executable proof,
26
- name rollback and failure paths; keep acceptance-changing proposals separate. Durable
26
+ name rollback and failure paths; keep acceptance-changing proposals separate. Each proof
27
+ row quotes the spec line or standard rule it proves (`Proves`); proof that proves none
28
+ goes to `test-plan.md` `## Deferred hardening`, never into a slice. Durable
27
29
  proof commands must be portable repository commands, never host-local wrappers,
28
30
  user-specific absolute paths, or temporary proof trees.
29
31
  Reconcile `spec.md`'s applicability map against live evidence. When triggered, apply
@@ -112,7 +112,10 @@ criterion, slice, or proof.
112
112
  `convergence_pressure` eligibility mechanical. Tag every Critical/Important
113
113
  `kind: contract` (an implementer could build it differently and still pass the
114
114
  planned tests) or `kind: mechanism` (a named test can discriminate the wrong
115
- implementation; propose that case as the fix, not new plan prose).
115
+ implementation; propose that case as the fix, not new plan prose). Each
116
+ Critical/Important also quotes the spec line (`AC`/`REQ`/`INV`/`PROH`/`DEC`/`EDGE`)
117
+ or standard rule it breaks; hardening no spec line asks for is a Suggestion
118
+ ([agents.md](../skills/devrites-lib/reference/standards/agents.md) § Result admission, Proportionality).
116
119
  - In a recheck, your verdict covers the listed fingerprints, the correction diff, and
117
120
  the dependents of each changed clause. Return an explicit correction-created
118
121
  regression verdict over the touched invariants and those dependents as
@@ -98,7 +98,7 @@ Each required reviewer/analyst/auditor starts with exactly one:
98
98
  ```text
99
99
  Outcome: <findings | no-findings | gap>
100
100
  Counts: <n per severity or kind used in the rows below>
101
- Finding: <severity> conf <n> | <file:line or artifact section> | <observed quote/result> | when <input/state/sequence> → <effect> | <minimum fix>
101
+ Finding: <severity> conf <n> | <file:line or artifact section> | <observed quote/result> | when <input/state/sequence> → <effect> | breaks <ID or standard §> "<quoted line>" | <minimum fix>
102
102
  Basis: <files read · commands run to reach this finding>
103
103
  ```
104
104
 
@@ -108,6 +108,13 @@ Basis: <files read · commands run to reach this finding>
108
108
  A Critical/Important row carries `conf <n>` in its severity cell and writes impact
109
109
  as `when <input/state/sequence> → <effect>`; missing either, root admits it as
110
110
  `Unverified: <claim> — missing trigger` (stays `gap`).
111
+ - **Proportionality:** a Critical/Important row's `breaks` quotes the line it violates:
112
+ a spec ID (`AC`/`REQ`/`INV`/`PROH`/`DEC`/`EDGE`) or a shipped standard rule
113
+ (`<standard>.md § <section>`: security, regression, principles). Root anchors the
114
+ quote like the `file:line` quote. No anchor, or one the defect does not break,
115
+ admits the row as Suggestion logged `Deferred: <claim> — no spec anchor` for an
116
+ optional follow-up slice; it never drives a repair round. Hardening no spec line
117
+ asks for is a Suggestion, however plausible.
111
118
  - **`no-findings`:** `No-findings:` names checks and inspected evidence. Bare
112
119
  pass, empty list, or “looks good” is malformed.
113
120
  - **`gap`:** names missing/unreadable/stale input; skipped/failed required check;
@@ -112,6 +112,10 @@ armed AFK workspace whose exact scope, gates, and budgets permit it.
112
112
  run `devrites-engine check drift <slug>` before re-vetting: it names the
113
113
  exact input artifact that changed (or `missing`/`added`) since the recorded
114
114
  baseline, so the Spec Drift Guard or `/rite-vet` starts at the right file.
115
+ - `check drift` and `check slice` print `ADVISORY proportion:` when a slice's proof
116
+ grew over 2x since the Vet baseline or carries over 3x the median proof per AC.
117
+ Repair by moving rows without a `Proves` spec line to `Deferred hardening`
118
+ ([agents.md](agents.md) § Result admission, Proportionality), not by adding proof.
115
119
  - Native notifications fire only after evidence and stop state are written.
116
120
 
117
121
  Use [`afk-hitl.md`](afk-hitl.md) for unattended authority and resource budgets,
@@ -29,8 +29,8 @@ Execute [`reference/phase-contract.md`](reference/phase-contract.md)
29
29
  [`reference/wright-dispatch.md`](reference/wright-dispatch.md).
30
30
  Every wright contract names the writer-safe checks the wright runs (syntax/type/lint,
31
31
  unit and self-check suites, a narrow local smoke) and never forbids them; only
32
- artifact-producing, consumptive, or full canonical gates stay root-owned.
33
- Prior contracts in `evidence.md` are not a template for this. **Failing case:** the
32
+ artifact-producing, consumptive, or full canonical gates stay root-owned;
33
+ `dispatch open --contract` refuses a contract that breaks this. Prior contracts in `evidence.md` are not a template for this. **Failing case:** the
34
34
  contract says "skip tests mid-flight; root runs them after return", and the wright
35
35
  returns three unit failures that a 20 s `go test` would have caught in-round.
36
36
 
@@ -23,7 +23,9 @@ DISPATCH → hand the slice contract to devrites-slice-wright (fresh context).
23
23
  (independent claims, fresh contexts); wright fixes stay serial — writers share files
24
24
  PROVE → root, concurrent with the reviews on the same frozen candidate:
25
25
  required build/browser/E2E (narrow self-check/smoke first, full gate only
26
- after it is green, long runs in the background), then returned-diff/path recheck; fail-on-red
26
+ after it is green, long runs in the background; cancel one still running once
27
+ review or proof sends the candidate back, keeping its partial output as
28
+ diagnostics), then returned-diff/path recheck; fail-on-red
27
29
  (red/gap → repair wright, then re-review and re-prove; do not commit yet)
28
30
  RECORD → orchestrator: after green verification, update state.md/evidence.md and
29
31
  upsert the authoritative candidate manifest from the actual scoped diff
@@ -94,10 +94,14 @@ claim add --session <id> <task-paths>`; retain its returned `<claim-id>`, then r
94
94
  live foreign session releases the retained claim and stops the dispatch, per
95
95
  [`agents.md`](../../devrites-lib/reference/standards/agents.md). `.devrites/**`
96
96
  is never a task path; see [Workflow Artifact admission](../../devrites-lib/reference/standards/workflow-artifacts.md).
97
- Ask the host
98
- for the exact writer in fresh context and wait. Track it through the
99
- launch barrier — `devrites-engine dispatch <slug> open --phase build --wave
100
- <slice-id> --role slice-wright --slice <SLICE-ID>`, then `start` with the host handle, `seal`, and
97
+ Write the exact contract, with its `Writer-safe checks:` line, to
98
+ `packets/<wave>.contract.md` and hand the wright that text verbatim. Ask the host
99
+ for the exact writer in fresh context; a repair round on the same slice whose
100
+ returned wright the host can still continue goes to that wright on its existing
101
+ handle instead, so it does not re-read the slice from scratch. Wait, and track it
102
+ through the launch barrier — `devrites-engine dispatch <slug> open --phase build --wave
103
+ <slice-id> --role slice-wright --slice <SLICE-ID> --contract <file>` (it refuses a
104
+ contract that forbids writer-safe checks or names none), then `start` with the host handle, `seal`, and
101
105
  `return` on completion (auto-records dispatch/return metrics). Every 5 wright
102
106
  rounds on one slice, `open` blocks for a reslice review: propose an
103
107
  acceptance-preserving split (`root_no_change_analysis` → `FOLD` per
@@ -131,7 +131,9 @@ Pull these via `Read` when shaping the plan:
131
131
  expand this into repository-wide coverage.
132
132
  4. **Map coverage and wiring:** every `AC-###` spec acceptance criterion maps to ≥1 `SLICE-###`
133
133
  ([`acceptance-criteria.md`](../rite-spec/reference/acceptance-criteria.md)); no orphaned criteria, no slice without a
134
- criterion. Lift covered/backstop `Edge Coverage` rows and resolved `Prohibitions (must-NOT)`
134
+ criterion. Every proof row quotes the spec line it proves (`Proves`); proof that proves
135
+ none goes to `test-plan.md` `## Deferred hardening`, never into a slice
136
+ ([artifacts.md](../rite-vet/reference/artifacts.md)). Lift covered/backstop `Edge Coverage` rows and resolved `Prohibitions (must-NOT)`
135
137
  rows into `traceability.md` and `test-plan.md`; unresolved rows get a gate/owner. Each
136
138
  `Existing behavior to preserve` row becomes a regression row whose current evidence is
137
139
  observed green on the unchanged baseline as a prerequisite of the first slice touching that
@@ -207,7 +207,9 @@ plan declares a root-authored executable workflow file, read
207
207
  `devrites-engine check readiness --emit-binding <slug>`; normal readiness check
208
208
  must pass. Record the per-input drift baseline alongside it:
209
209
  `devrites-engine check drift <slug> --record` — a later stale binding then
210
- names the exact artifact that moved instead of "something changed". Technical failure records reproduction, not qid. Human gap awaits
210
+ names the exact artifact that moved instead of "something changed". An `ADVISORY
211
+ proportion:` line names a slice whose proof outgrew its ACs: move its rows without a
212
+ `Proves` spec line to `Deferred hardening` before READY, never deepen them. Technical failure records reproduction, not qid. Human gap awaits
211
213
  Clarify. Optional cross-model follows `reference/cross-model.md` (trigger `cross-model`).
212
214
 
213
215
  With READY, no pending remediation, and a valid technical return cursor,
@@ -126,9 +126,20 @@ prove only their named static criterion.
126
126
  <the ASCII code-paths + user-flows diagram from review-axes.md, with COVERAGE / GAPS / REGRESSIONS line>
127
127
 
128
128
  ## Per-gap test requirements
129
- | ID | Path / flow | Test file (match conventions) | Asserts (input → expected) | Kind | Slice | Priority |
130
- |---|---|---|---|---|---|---|
131
- | T1 | <path> | <path/to.test> | <empty list → 200 + []> | unit / E2E / eval | <slice> | P1 / **Regression-Critical** |
129
+ | ID | Path / flow | Test file (match conventions) | Asserts (input → expected) | Kind | Slice | Priority | Proves |
130
+ |---|---|---|---|---|---|---|---|
131
+ | T1 | <path> | <path/to.test> | <empty list → 200 + []> | unit / E2E / eval | <slice> | P1 / **Regression-Critical** | <AC/REQ/INV/PROH/DEC/EDGE ID or standard § + quoted line> |
132
+
133
+ Every row's `Proves` quotes the spec line or standard rule it proves. A row that proves
134
+ none is plan-invented hardening: move it to `Deferred hardening`.
135
+
136
+ ## Deferred hardening
137
+ | ID | Hardening | Why no spec line needs it | Promote by |
138
+ |---|---|---|---|
139
+ | D1 | <proof or mechanism> | <reason> | <the spec line an owner would add> |
140
+
141
+ Deferred rows are an optional follow-up slice: not built, proven, or gating in this
142
+ feature; this table is their follow-up record. Nothing is deleted.
132
143
 
133
144
  ## Interaction inventory (UI slices — every element + flow gets an asserting test)
134
145
  Enumerate every interactive element and user flow the feature exposes; one row each, none
@@ -161,7 +161,7 @@ Outcome: <findings | no-findings | gap>
161
161
  Counts: <n per severity or kind used in the rows below>
162
162
  Account: <admitted findings | No-findings | Gap per Result admission>
163
163
  Coverage: <files read fully | hunk-level | skipped: name — reason>
164
- Finding: <severity> conf <n> | <file:line> | <observed> | when <input/state/sequence> → <effect> | <minimum fix>
164
+ Finding: <severity> conf <n> | <file:line> | <observed> | when <input/state/sequence> → <effect> | breaks <ID or standard §> "<quoted line>" | <minimum fix>
165
165
  Basis: <files read · commands run to reach this finding>
166
166
  Contract: <applicable | not-applicable> — <basis: both sides read · crossing test>
167
167
  Tests: <adequate? gaps>
@@ -23,7 +23,9 @@ seams supplied by the orchestrator.
23
23
  Reuse existing seams and dependencies before proposing new ones. Build vertical
24
24
  slices in dependency order; put a hub file's shared edit in one seam slice
25
25
  ([slicing.md](../skills/rite-plan/reference/slicing.md#slice-independence)). Map every REQ and AC to a slice and executable proof,
26
- name rollback and failure paths; keep acceptance-changing proposals separate. Durable
26
+ name rollback and failure paths; keep acceptance-changing proposals separate. Each proof
27
+ row quotes the spec line or standard rule it proves (`Proves`); proof that proves none
28
+ goes to `test-plan.md` `## Deferred hardening`, never into a slice. Durable
27
29
  proof commands must be portable repository commands, never host-local wrappers,
28
30
  user-specific absolute paths, or temporary proof trees.
29
31
  Reconcile `spec.md`'s applicability map against live evidence. When triggered, apply
@@ -118,7 +118,10 @@ criterion, slice, or proof.
118
118
  `convergence_pressure` eligibility mechanical. Tag every Critical/Important
119
119
  `kind: contract` (an implementer could build it differently and still pass the
120
120
  planned tests) or `kind: mechanism` (a named test can discriminate the wrong
121
- implementation; propose that case as the fix, not new plan prose).
121
+ implementation; propose that case as the fix, not new plan prose). Each
122
+ Critical/Important also quotes the spec line (`AC`/`REQ`/`INV`/`PROH`/`DEC`/`EDGE`)
123
+ or standard rule it breaks; hardening no spec line asks for is a Suggestion
124
+ ([agents.md](../skills/devrites-lib/reference/standards/agents.md) § Result admission, Proportionality).
122
125
  - In a recheck, your verdict covers the listed fingerprints, the correction diff, and
123
126
  the dependents of each changed clause. Return an explicit correction-created
124
127
  regression verdict over the touched invariants and those dependents as
@@ -98,7 +98,7 @@ Each required reviewer/analyst/auditor starts with exactly one:
98
98
  ```text
99
99
  Outcome: <findings | no-findings | gap>
100
100
  Counts: <n per severity or kind used in the rows below>
101
- Finding: <severity> conf <n> | <file:line or artifact section> | <observed quote/result> | when <input/state/sequence> → <effect> | <minimum fix>
101
+ Finding: <severity> conf <n> | <file:line or artifact section> | <observed quote/result> | when <input/state/sequence> → <effect> | breaks <ID or standard §> "<quoted line>" | <minimum fix>
102
102
  Basis: <files read · commands run to reach this finding>
103
103
  ```
104
104
 
@@ -108,6 +108,13 @@ Basis: <files read · commands run to reach this finding>
108
108
  A Critical/Important row carries `conf <n>` in its severity cell and writes impact
109
109
  as `when <input/state/sequence> → <effect>`; missing either, root admits it as
110
110
  `Unverified: <claim> — missing trigger` (stays `gap`).
111
+ - **Proportionality:** a Critical/Important row's `breaks` quotes the line it violates:
112
+ a spec ID (`AC`/`REQ`/`INV`/`PROH`/`DEC`/`EDGE`) or a shipped standard rule
113
+ (`<standard>.md § <section>`: security, regression, principles). Root anchors the
114
+ quote like the `file:line` quote. No anchor, or one the defect does not break,
115
+ admits the row as Suggestion logged `Deferred: <claim> — no spec anchor` for an
116
+ optional follow-up slice; it never drives a repair round. Hardening no spec line
117
+ asks for is a Suggestion, however plausible.
111
118
  - **`no-findings`:** `No-findings:` names checks and inspected evidence. Bare
112
119
  pass, empty list, or “looks good” is malformed.
113
120
  - **`gap`:** names missing/unreadable/stale input; skipped/failed required check;
@@ -112,6 +112,10 @@ armed AFK workspace whose exact scope, gates, and budgets permit it.
112
112
  run `devrites-engine check drift <slug>` before re-vetting: it names the
113
113
  exact input artifact that changed (or `missing`/`added`) since the recorded
114
114
  baseline, so the Spec Drift Guard or `/rite-vet` starts at the right file.
115
+ - `check drift` and `check slice` print `ADVISORY proportion:` when a slice's proof
116
+ grew over 2x since the Vet baseline or carries over 3x the median proof per AC.
117
+ Repair by moving rows without a `Proves` spec line to `Deferred hardening`
118
+ ([agents.md](agents.md) § Result admission, Proportionality), not by adding proof.
115
119
  - Native notifications fire only after evidence and stop state are written.
116
120
 
117
121
  Use [`afk-hitl.md`](afk-hitl.md) for unattended authority and resource budgets,
@@ -29,8 +29,8 @@ Execute [`reference/phase-contract.md`](reference/phase-contract.md)
29
29
  [`reference/wright-dispatch.md`](reference/wright-dispatch.md).
30
30
  Every wright contract names the writer-safe checks the wright runs (syntax/type/lint,
31
31
  unit and self-check suites, a narrow local smoke) and never forbids them; only
32
- artifact-producing, consumptive, or full canonical gates stay root-owned.
33
- Prior contracts in `evidence.md` are not a template for this. **Failing case:** the
32
+ artifact-producing, consumptive, or full canonical gates stay root-owned;
33
+ `dispatch open --contract` refuses a contract that breaks this. Prior contracts in `evidence.md` are not a template for this. **Failing case:** the
34
34
  contract says "skip tests mid-flight; root runs them after return", and the wright
35
35
  returns three unit failures that a 20 s `go test` would have caught in-round.
36
36
 
@@ -23,7 +23,9 @@ DISPATCH → hand the slice contract to devrites-slice-wright (fresh context).
23
23
  (independent claims, fresh contexts); wright fixes stay serial — writers share files
24
24
  PROVE → root, concurrent with the reviews on the same frozen candidate:
25
25
  required build/browser/E2E (narrow self-check/smoke first, full gate only
26
- after it is green, long runs in the background), then returned-diff/path recheck; fail-on-red
26
+ after it is green, long runs in the background; cancel one still running once
27
+ review or proof sends the candidate back, keeping its partial output as
28
+ diagnostics), then returned-diff/path recheck; fail-on-red
27
29
  (red/gap → repair wright, then re-review and re-prove; do not commit yet)
28
30
  RECORD → orchestrator: after green verification, update state.md/evidence.md and
29
31
  upsert the authoritative candidate manifest from the actual scoped diff