devrites 2.2.0 → 2.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 (50) hide show
  1. package/CHANGELOG.md +18 -0
  2. package/README.md +30 -17
  3. package/docs/architecture.md +30 -24
  4. package/docs/command-map.md +15 -6
  5. package/docs/flow.md +19 -5
  6. package/docs/orchestration.md +17 -7
  7. package/docs/skills.md +32 -8
  8. package/pack/.claude/agents/devrites-code-reviewer.md +6 -1
  9. package/pack/.claude/agents/devrites-devex-reviewer.md +94 -0
  10. package/pack/.claude/agents/devrites-forge-judge.md +92 -0
  11. package/pack/.claude/agents/devrites-frontend-reviewer.md +8 -0
  12. package/pack/.claude/agents/devrites-retrospector.md +91 -0
  13. package/pack/.claude/agents/devrites-slice-wright.md +18 -5
  14. package/pack/.claude/rules/README.md +5 -2
  15. package/pack/.claude/rules/agents.md +30 -2
  16. package/pack/.claude/rules/anti-patterns.md +11 -0
  17. package/pack/.claude/rules/code-review.md +16 -6
  18. package/pack/.claude/rules/core.md +13 -4
  19. package/pack/.claude/rules/developer-experience.md +119 -0
  20. package/pack/.claude/rules/principles.md +158 -0
  21. package/pack/.claude/rules/spec-grammar.md +106 -0
  22. package/pack/.claude/skills/devrites-browser-proof/SKILL.md +42 -3
  23. package/pack/.claude/skills/devrites-lib/SKILL.md +9 -2
  24. package/pack/.claude/skills/devrites-lib/scripts/devrites.sh +8 -0
  25. package/pack/.claude/skills/devrites-lib/scripts/spec-validate.sh +130 -0
  26. package/pack/.claude/skills/rite-adopt/SKILL.md +13 -1
  27. package/pack/.claude/skills/rite-autocomplete/SKILL.md +3 -1
  28. package/pack/.claude/skills/rite-build/SKILL.md +36 -1
  29. package/pack/.claude/skills/rite-build/reference/anti-patterns.md +12 -0
  30. package/pack/.claude/skills/rite-build/reference/forge.md +171 -0
  31. package/pack/.claude/skills/rite-build/reference/one-slice-cycle.md +2 -0
  32. package/pack/.claude/skills/rite-build/reference/wright-dispatch.md +22 -2
  33. package/pack/.claude/skills/rite-define/SKILL.md +14 -1
  34. package/pack/.claude/skills/rite-frame/SKILL.md +7 -2
  35. package/pack/.claude/skills/rite-learn/SKILL.md +17 -4
  36. package/pack/.claude/skills/rite-polish/SKILL.md +3 -1
  37. package/pack/.claude/skills/rite-prove/SKILL.md +30 -1
  38. package/pack/.claude/skills/rite-quick/SKILL.md +9 -4
  39. package/pack/.claude/skills/rite-review/SKILL.md +5 -2
  40. package/pack/.claude/skills/rite-seal/SKILL.md +22 -4
  41. package/pack/.claude/skills/rite-ship/SKILL.md +21 -0
  42. package/pack/.claude/skills/rite-spec/SKILL.md +29 -5
  43. package/pack/.claude/skills/rite-spec/reference/spec-template.md +11 -1
  44. package/pack/.claude/skills/rite-status/SKILL.md +3 -1
  45. package/pack/.claude/skills/rite-vet/SKILL.md +46 -14
  46. package/package.json +1 -1
  47. package/scripts/check-cross-refs.py +4 -0
  48. package/scripts/run-behavioral-evals.sh +170 -0
  49. package/scripts/run-evals.sh +3 -0
  50. package/scripts/validate.sh +1 -1
@@ -22,7 +22,8 @@ lifecycle (`/rite-temper` → `/rite-define` → `/rite-build` …) takes over.
22
22
 
23
23
  ## Rules consulted (read on demand from `.claude/rules/`)
24
24
  **Step 0:** Read `.claude/rules/core.md` first. Pull `documentation.md` when recording
25
- the adoption decisions (why-not-what) in `decisions.md`.
25
+ the adoption decisions (why-not-what) in `decisions.md`; pull `principles.md` when the code
26
+ upholds invariants worth proposing as project principles (step 4a).
26
27
 
27
28
  ## Operating rules (DevRites core)
28
29
  - No silent assumptions · prefer the project's existing conventions (you are *documenting*
@@ -61,6 +62,16 @@ the adoption decisions (why-not-what) in `decisions.md`.
61
62
  evidence-gated promotion: the seeds start at the base band and are provenance-tagged as
62
63
  onboarding observations, not sealed-slice proofs, so real slices later corroborate or
63
64
  (fresh-wins) contradict them.
65
+ 4a. **Propose candidate principles** (human-ratified; optional). Where the investigation found an
66
+ invariant the code *consistently and deliberately* upholds — money always in integer cents, PII
67
+ always redacted from logs, every v1 endpoint preserved — surface it as a **candidate
68
+ principle**, not a seeded convention. Principles are prescriptive and gating, so they are
69
+ **ratified by the human, never auto-seeded** the way conventions are: present the candidates via
70
+ `AskUserQuestion` with the evidence (where the code upholds it), and write the ones the human
71
+ ratifies to `.devrites/principles.md` with a dated Governance entry
72
+ ([`principles.md`](../../rules/principles.md)). Propose, don't impose — an unratified candidate
73
+ stays a convention, not a gate. Skip cleanly when nothing rises to an invariant (common — a
74
+ fresh adopt may declare zero principles, and that's valid).
64
75
  5. **Hand off.** The project is now in the lifecycle with a spec and a head-start ledger.
65
76
  Point the user at `/rite-temper` (big/risky) or `/rite-define` (straightforward) — do not
66
77
  plan or build here.
@@ -78,6 +89,7 @@ Adopted: <slug>
78
89
  Baseline: <one-line summary of current behavior> Placement: <where it lives>
79
90
  Next objective: <what we'll build on top>
80
91
  Conventions seeded: <n> (commands · idioms · placement · gotchas)
92
+ Principles proposed: <n ratified → .devrites/principles.md | none rose to an invariant>
81
93
  Next: big / risky? → /rite-temper · straightforward? → /rite-define
82
94
  ↻ Hygiene: /clear before the next phase (spec.md + decisions.md + the seeded ledger captured). See rules/context-hygiene.md.
83
95
  ```
@@ -22,7 +22,9 @@ blocking / escalating gates, and any NO-GO still pause.
22
22
  - **Safety gates are not bypassable.** AFK never auto-passes destructive migration /
23
23
  auth-authz change / public-API break / external-contract change / red tests; blocking
24
24
  and escalating gates and any open `gate: validating` always pause. `--ship` auto-confirms
25
- the **final** type-GO only — nothing else.
25
+ the **final** type-GO only — nothing else. A change that violates a declared project
26
+ principle (`.devrites/principles.md`) with no recorded exception pauses too — autocomplete
27
+ never grants a principle exception on its own (`principles.md`: that's a human decision).
26
28
  - **Loop budget = the plan's own slice count, not a fixed number.** After `/rite-vet`
27
29
  (not `/rite-define` — vet may split or add slices, so the count isn't final until then),
28
30
  set the AFK budget to however many slices the plan has, so the loop builds exactly the
@@ -24,6 +24,7 @@ writes; read them yourself for the doubt/record gates or in the inline fallback:
24
24
  - `error-handling.md` — fail fast, no silent catches, fail closed.
25
25
  - `testing.md` — pyramid, behaviour over implementation, see-it-fail-first.
26
26
  - `patterns.md` — composition over inheritance, avoid premature abstraction.
27
+ - `principles.md` — the project invariants (`.devrites/principles.md`) the slice must honor; the wright reads them as **binding**, not priors.
27
28
  - `security.md` — when the slice touches user input, auth, data, or external integrations.
28
29
 
29
30
  ## Operating rules
@@ -40,11 +41,23 @@ writes; read them yourself for the doubt/record gates or in the inline fallback:
40
41
  The **prose you write yourself** — `evidence.md`, `decisions.md`, the slice report — follows
41
42
  the human-voice charter (`.claude/rules/prose-style.md`; depth in `devrites-prose-craft`): no
42
43
  filler openers, no marketing adjectives, exact commands and identifiers kept verbatim.
44
+ - **Honor declared project principles.** The wright reads `.devrites/principles.md` and treats
45
+ each invariant as **binding** (not a prior to weigh like a convention) — a slice it cannot build
46
+ without breaking one is an **Escalation**, not a silent violation. On return **you verify no
47
+ principle was broken**; a fresh violation is handled like any irreversible-risk item — a
48
+ human-approved, scoped exception in the register or a stop, never folded into the slice. No
49
+ `.devrites/principles.md` → none declared → nothing to honor.
43
50
  - **You never edit source — the wright is the only writer of code + tests.** You write only
44
51
  `.devrites/` bookkeeping. On any red gate, doubt finding, or coverage gap your only remedies
45
52
  are **continue the same wright once** (it fixes in its own context) or **stop + escalate** —
46
53
  never patch the code yourself. The `reconcile.sh` gate (step 6) enforces this by exit code:
47
54
  any source file changed outside the wright's claimed set is a hard STOP.
55
+ - **A `Forge: yes` slice competes candidates — one author still lands.** When `/rite-vet`
56
+ flagged the slice a genuine architecture fork, step 3 runs K=2–3 candidate wrights in
57
+ **isolated worktrees** and lands exactly one winner's diff; the single-writer invariant holds
58
+ because no tree ever has two authors and only the winner reaches the working tree. You still
59
+ never edit source, and reconcile runs against the winner's claimed set. The default slice is
60
+ single-path — forge is the rare exception ([`reference/forge.md`](reference/forge.md)).
48
61
 
49
62
  ## Workflow ([one-slice-cycle](reference/one-slice-cycle.md))
50
63
  0. **Rules + AFK + readiness check.** Read `.claude/rules/core.md` first. Then **run the
@@ -111,10 +124,25 @@ writes; read them yourself for the doubt/record gates or in the inline fallback:
111
124
  [ -f "$ST" ] || ST=pack/.claude/skills/devrites-lib/scripts/stuck.sh
112
125
  [ -f "$ST" ] && bash "$ST" log "$(cat .devrites/ACTIVE 2>/dev/null)" dispatch "<slice id>" || true
113
126
  ```
127
+ **Forge branch — only if the selected slice is `Forge: yes`.** Instead of the single dispatch
128
+ below, run the competitive build per [`reference/forge.md`](reference/forge.md): K=2–3 candidate
129
+ wrights, each in an **isolated git worktree** on the distinct strategy `/rite-vet` named, then a
130
+ fresh-context [`devrites-forge-judge`](../../agents/devrites-forge-judge.md) scores them against
131
+ acceptance + `test-plan.md` + `.devrites/principles.md` + the anti-slop charter, and you land
132
+ exactly **one** winner's diff in the working tree, graft any cheap runner-up improvement by
133
+ continuing the winning wright once, and write `forge-report.md`. Forge returns the **same shape**
134
+ a single wright does (one structured artifact, for the winner), so steps 4–7 (doubt, fail-on-red,
135
+ reconcile against the winner's claimed set, record, stop) run **unchanged**. If you cannot give
136
+ the judge an objective scorecard (the slice lacks acceptance / `test-plan.md` coverage) or cannot
137
+ name two genuinely different strategies, the flag is stale — clear it and build single-path. The
138
+ single-writer invariant is intact: each candidate owns its own tree, exactly one author's diff
139
+ lands. Then **stop** here for this slice; the steps below are the default single-path dispatch.
140
+
114
141
  Then assemble the slice contract and send it per
115
142
  [`reference/wright-dispatch.md`](reference/wright-dispatch.md): the slice goal, acceptance
116
143
  criteria, and **scope boundary**; the paths it may touch (`touched-files.md`); the context
117
- paths to read (`spec.md`, `plan.md`, `decisions.md`, `assumptions.md`, plus `test-plan.md`
144
+ paths to read (`spec.md`, `plan.md`, `decisions.md`, `assumptions.md`, `.devrites/principles.md`
145
+ when present — the binding invariants the slice must honor — plus `test-plan.md`
118
146
  when present — its per-gap test requirements + regression-criticals for this slice are the
119
147
  coverage the wright must write — and `design-brief.md`
120
148
  when the slice touches UI per [frontend-trigger](reference/frontend-trigger.md)); and the
@@ -146,6 +174,10 @@ writes; read them yourself for the doubt/record gates or in the inline fallback:
146
174
  blocking protocol violation — pause and re-dispatch with the item flagged out-of-bounds, do
147
175
  **not** doubt-and-accept it. (The wright's return is the not-yet-load-bearing moment — the
148
176
  slice isn't `built` or merged yet — so this post-return doubt is still pre-commit.)
177
+ **Principle check (same standing):** a wright return that breaks a declared principle
178
+ (reported in its `Principles` field, or that you detect against `.devrites/principles.md`) is
179
+ handled here like an irreversible-risk item — block, route to a human-approved scoped
180
+ exception in the register or stop; never doubt-and-accept a principle violation into the slice.
149
181
  5. **Fail-on-red.** If the wright's `Gates` were red (targeted tests / types / lint) or it
150
182
  couldn't verify: do **not** mark the slice `built`, and **do not fix the code yourself**.
151
183
  First remedy — **continue the same wright once** (`SendMessage` to it, carrying the failing
@@ -258,4 +290,7 @@ Next ▸ /rite-prove (prove the completed feature)
258
290
  Keep fact lines terse — one `key value` per fact, `·` between, no prose. The meter, the
259
291
  `✅ ALL BUILT` marker, and the ribbon carry the progress; don't restate them in words.
260
292
 
293
+ For a **forged slice**, the `Built` line names the competition and points at the record, e.g.
294
+ `Built slice 3 — csv-streaming (forged: 3 candidates → winner B; forge-report.md)`.
295
+
261
296
  **DO NOT continue to the next slice automatically** — even at `✅ ALL BUILT`, `/rite-prove` is the user's call.
@@ -15,6 +15,16 @@ generic AI naming, drive-by refactors): see
15
15
  | "Slices 1 and 2 are related; let me do both." | One slice, then stop. Full stop. The user asks for the next. |
16
16
  | "It's faster to skip `devrites-doubt` for this small change." | Doubt is cheapest *before* the decision is standing. After is debugging. |
17
17
 
18
+ ## Forge ([forge.md](forge.md))
19
+
20
+ | Excuse | Rebuttal |
21
+ |---|---|
22
+ | "This slice is hard — I'll forge it." | Hard ≠ forkable. Forge competes ≥2 *genuinely-different* approaches with no clear winner. One hard approach is single-path; building it K times wastes K× for one answer. |
23
+ | "I'm not sure which approach is right, so let me compete them and decide." | Forge is for a real fork the plan couldn't settle, not for dodging a decision you can make now. Decide from the code/docs first; forge only when the alternatives are genuinely undecidable on paper. |
24
+ | "Candidate A's data layer plus B's UI would be the best of both." | Don't hand-merge candidates — two authors in one tree is the incoherence the single-writer rule exists to prevent. Land one winner whole; graft a *specific* runner-up idea only by continuing the winning wright once. |
25
+ | "The judge already scored these, so I can skip the post-return doubt." | The judge picks *between* candidates; it doesn't replace doubting the winner's standing decisions. Steps 4–7 run on the winner unchanged. |
26
+ | "A fourth candidate might be even better." | K is capped at 3. A fourth rarely moves the winner and multiplies cost; if none of three is right, the plan is wrong — `/rite-plan repair`, don't add candidates. |
27
+
18
28
  ## Red Flags
19
29
 
20
30
  - About to start slice N+1 without the user asking.
@@ -23,3 +33,5 @@ generic AI naming, drive-by refactors): see
23
33
  - Writing a `try/catch` block wider than what you actually handle.
24
34
  - A comment that restates what the next line does.
25
35
  - Touching files that aren't in `touched-files.md` for "tidiness".
36
+ - Forging a slice with no acceptance / `test-plan.md` coverage for the judge to score against.
37
+ - Landing more than one candidate's code, or keeping a losing worktree/branch around after F7.
@@ -0,0 +1,171 @@
1
+ # Forge — competing candidate builds for one slice
2
+
3
+ How `/rite-build` builds a slice that `/rite-vet` flagged `Forge: yes`: instead of one wright,
4
+ **K = 2–3 candidate wrights** each build the slice a genuinely different way in **isolation**, a
5
+ read-only judge scores them, and exactly **one** winner's diff lands in the working tree. Loaded
6
+ on demand by `/rite-build` step 3; the sibling of single-path
7
+ [`wright-dispatch.md`](wright-dispatch.md).
8
+
9
+ Forge is the **rare** path. Most slices are single-path (cheaper, and the default). A slice
10
+ forges only when the work is a genuine architecture fork — two or three approaches that actually
11
+ differ, no clear winner on paper, at high enough stakes that building the wrong one costs more
12
+ than building all of them. That judgment is made at `/rite-vet` and recorded as the slice's
13
+ `Forge:` field; build acts on the flag, it does not invent it.
14
+
15
+ ## Why this doesn't break the single-writer invariant
16
+
17
+ DevRites forbids a parallel fan-out of writers **sharing one tree** — concurrent writers on one
18
+ working tree make conflicting implicit decisions and produce incoherent code
19
+ ([`wright-dispatch.md`](wright-dispatch.md)). Forge keeps that invariant intact by **isolation**:
20
+ every candidate writes in its own worktree (or its own throwaway branch), never touching another
21
+ candidate's tree, and the orchestrator lands exactly one. No tree ever has two authors; exactly
22
+ one author's work ships. "N isolated complete attempts, winner-takes-all" is not "N writers on
23
+ one slice."
24
+
25
+ ## Trust the flag, but clear a stale one
26
+
27
+ Before competing anything, confirm the flag still earns its cost — `/rite-vet` set it, but the
28
+ plan may have moved:
29
+
30
+ - **No objective scorecard** — the slice has no acceptance criteria and no `test-plan.md`
31
+ coverage the judge can score against → clear `Forge` to `no`, build single-path. A competition
32
+ with no rubric is a coin toss with K× the cost.
33
+ - **Can't name two genuinely different strategies** — if the candidates would be variations of
34
+ one approach, there is nothing to compete → single-path.
35
+ - **Slice shrank below the bar** (now Complexity ≤3, or a dependency landed that picks the
36
+ approach) → single-path.
37
+
38
+ A cleared flag is recorded in `decisions.md` (one line: why forge was dropped). Never forge a
39
+ slice you can't score, and never forge to avoid making a decision the plan already made.
40
+
41
+ ## Mechanics
42
+
43
+ The orchestrator runs F1–F7. Steps 4–7 of the [one-slice-cycle](one-slice-cycle.md) (doubt,
44
+ fail-on-red, reconcile, record, stop) then run **unchanged** on the winner.
45
+
46
+ ### F1 — Confirm the K strategies
47
+ Take the 2–3 candidate strategies `/rite-vet` named (in the slice brief). Each must be a
48
+ **distinct, complete approach** to the same slice contract — a different seam, data shape,
49
+ reuse-vs-build call, or algorithm — not a tweak of one. Name them `A`, `B`, (`C`) with a
50
+ one-line description each. K is capped at 3: a fourth candidate rarely changes the winner and
51
+ multiplies cost.
52
+
53
+ ### F2 — Snapshot, then isolate each candidate
54
+ Snapshot the working tree first (the winner's landing is reconciled against it later), then give
55
+ each candidate its own isolated tree. **Prefer parallel isolated worktrees** when the harness can
56
+ dispatch a sub-agent with worktree isolation; the **always-available** path is one throwaway git
57
+ branch per candidate, built sequentially (slower, universally works — the
58
+ [`tooling.md`](../../../rules/tooling.md) "fallback is first-class" discipline):
59
+
60
+ ```bash
61
+ SLUG="$(cat .devrites/ACTIVE 2>/dev/null)"
62
+ git rev-parse --is-inside-work-tree >/dev/null 2>&1 || echo "(not a git repo — forge needs git for isolation; degrade to single-path)"
63
+ BASE="$(git rev-parse --abbrev-ref HEAD)"
64
+ # one isolated worktree per candidate (parallel-capable); falls back to branches if worktrees are unavailable
65
+ for C in A B; do
66
+ git worktree add ".devrites/work/$SLUG/forge/cand-$C" -b "forge/$SLUG/cand-$C" 2>/dev/null \
67
+ || git branch "forge/$SLUG/cand-$C" "$BASE"
68
+ done
69
+ ```
70
+
71
+ ### F3 — Dispatch K candidate wrights
72
+ Send **each** candidate the identical single-path slice contract from
73
+ [`wright-dispatch.md`](wright-dispatch.md), with **one** line added naming its assigned strategy
74
+ and its isolated tree:
75
+
76
+ ```
77
+ Forge candidate <A|B|C> of <K>. Build this slice using ONLY this approach:
78
+ Strategy: <the distinct approach, one or two sentences — the seam / data shape / reuse call>
79
+ Work in your isolated tree: <worktree path | branch name>. Do not consult or merge another
80
+ candidate's work. Same discipline as always — orient → RED → implement smallest complete →
81
+ verify → return your structured artifact. Code + tests only.
82
+ ```
83
+
84
+ Each is still **one wright, one tree** — the invariant holds per candidate. Parallel where the
85
+ harness isolates them; otherwise build candidate A to green, capture its diff, reset to `BASE`,
86
+ then candidate B. A candidate that hits an irreversible-risk item escalates exactly as a
87
+ single-path wright does — **forge never bypasses a gate** (see AFK below).
88
+
89
+ ### F4 — Judge (read-only, fresh context)
90
+ Dispatch [`devrites-forge-judge`](../../../agents/devrites-forge-judge.md) with the K finished
91
+ candidate diffs and the scorecard inputs (slice acceptance, `test-plan.md`, `.devrites/principles.md`,
92
+ the anti-slop charter). It scores each candidate, ranks them, names the **winner** and the
93
+ specific runner-up ideas worth grafting, and returns the structured verdict. The judge **never
94
+ writes code** — it reads the diffs and returns findings, like every reviewer agent. If sub-agent
95
+ dispatch is unavailable, do the judge's rubric pass yourself in a fresh read, discarding your
96
+ authoring reasoning (a flagged fallback, not an independent judgment).
97
+
98
+ ### F5 — Land the winner, graft sparingly
99
+ Apply **only** the winner's diff to the working tree (merge its worktree / cherry-pick its
100
+ branch). If the judge named a cheap, specific improvement in a runner-up, graft it by
101
+ **continuing the winning wright once** with that instruction — never hand-merge two candidates'
102
+ code (that re-creates the incoherent-tree failure the invariant exists to prevent). The landed
103
+ tree has exactly one author: the winner (plus its own grafted follow-up).
104
+
105
+ ### F6 — Write `forge-report.md`
106
+ The durable record of the competition (template below). One per forged slice; archived with the
107
+ feature.
108
+
109
+ ### F7 — Clean up, then return to the cycle
110
+ Remove the losing worktrees / delete the candidate branches:
111
+
112
+ ```bash
113
+ SLUG="$(cat .devrites/ACTIVE 2>/dev/null)"
114
+ for C in A B; do
115
+ git worktree remove ".devrites/work/$SLUG/forge/cand-$C" --force 2>/dev/null || true
116
+ git branch -D "forge/$SLUG/cand-$C" 2>/dev/null || true
117
+ done
118
+ ```
119
+
120
+ Then hand the **winner's** structured artifact to [one-slice-cycle](one-slice-cycle.md) step 4 as
121
+ if a single wright produced it. Doubt, fail-on-red, reconcile (against the F2 snapshot, claimed =
122
+ the winner's files), record, and stop all run unchanged.
123
+
124
+ ## `forge-report.md` template
125
+
126
+ ```markdown
127
+ # Forge report: <slice id — name>
128
+ Forged on <iso>. Reason: <the architecture fork /rite-vet flagged>.
129
+
130
+ ## Candidates
131
+ | # | Strategy | Gates | Score | Notes |
132
+ |---|---|---|---|---|
133
+ | A | <approach> | green/red | <judge score> | <one line> |
134
+ | B | <approach> | green/red | <judge score> | <one line> |
135
+
136
+ ## Verdict
137
+ Winner: <A|B|C> — <why it won, in the judge's terms: acceptance coverage, simplicity, principle
138
+ fit, anti-slop, reuse>.
139
+ Grafted from runner-up: <specific idea + which candidate | none>.
140
+ Discarded: <the losing approaches + the one-line reason each lost — load-bearing for a later
141
+ slice that might be tempted to retry one>.
142
+ ```
143
+
144
+ The **Discarded** section matters: it is a dead-end record at the slice level, the same role
145
+ `decisions.md` "Dead ends" plays for the feature — a later slice shouldn't re-litigate an
146
+ approach forge already rejected.
147
+
148
+ ## AFK & budget
149
+
150
+ - **Cost.** Forge multiplies the build by K. Under `.devrites/AFK`, each candidate counts against
151
+ the slice budget (`tick-afk.sh` once per candidate dispatched), so a forge slice can exhaust the
152
+ cap faster — that is intended back-pressure, not a bug.
153
+ - **Gates are unchanged.** Forge changes *how many candidates build*, never *what pauses*. An
154
+ irreversible-risk item, a blocking/escalating gate, or a red-on-completion in **any** candidate
155
+ pauses per [`afk-hitl.md`](../../../rules/afk-hitl.md) exactly as single-path. AFK widens what is
156
+ automatic, never what is irreversible — forge included.
157
+ - **Stuck loop still applies.** Re-dispatching the same candidate without progress trips
158
+ `stuck.sh` the same as a single wright.
159
+
160
+ ## When isolation is impossible
161
+
162
+ No git, or neither worktrees nor throwaway branches are usable → **degrade to single-path** and
163
+ say so. Forge is an accelerator for choosing between real alternatives; it is never a requirement
164
+ for building the slice. A slice always has a single-path build available — the competition is the
165
+ optional part.
166
+
167
+ ## Anti-patterns
168
+
169
+ See [`anti-patterns.md`](anti-patterns.md) § Forge — the short version: don't forge a decided or
170
+ trivial slice, don't forge to dodge a decision, don't hand-merge candidates, don't exceed K=3,
171
+ and don't let a forged slice skip the post-return doubt because "the judge already looked."
@@ -9,6 +9,8 @@ The orchestrator (`/rite-build`) gates and records; the **wright** writes. See
9
9
  ```
10
10
  SELECT → orchestrator: restate slice goal + acceptance + scope boundary; HITL gate (pause pre-code)
11
11
  (SHAPE) → orchestrator: if UI and no design-brief.md, shape it (devrites-ux-shape) before dispatch
12
+ (FORGE) → orchestrator: if slice is Forge: yes, compete K=2–3 isolated candidate wrights →
13
+ devrites-forge-judge → land one winner → forge-report.md, then continue at DOUBT (forge.md)
12
14
  DISPATCH → hand the slice contract to devrites-slice-wright (fresh context). Inside the wright:
13
15
  ORIENT → load only the files this slice touches; learn the project's idiom; reuse-first
14
16
  (RED) → if behavior change: write the failing test first
@@ -16,8 +16,11 @@ The orchestrator's window is full of spec investigation, planning, prior slices,
16
16
  output — the exact "lost-in-the-middle" load that degrades instruction-following and pulls the
17
17
  model toward generic code. The wright starts clean and sees only the contract, so it holds the
18
18
  slice boundary strictly and writes to the *project's* idiom instead of drifting. Single-threaded
19
- by design: **one** wright per slice, never a parallel fan-out of writers concurrent writers
20
- make conflicting implicit decisions and produce incoherent code.
19
+ by design: **one** wright per slice, never a parallel fan-out of writers *sharing a tree* —
20
+ concurrent writers on one working tree make conflicting implicit decisions and produce incoherent
21
+ code. The one sanctioned exception is a **forge** slice, which competes K candidates in *isolated*
22
+ worktrees and lands exactly one — no tree ever has two authors, so the invariant holds (see
23
+ [Forge](#forge--competing-candidates-the-deliberate-exception)).
21
24
 
22
25
  ## The contract `/rite-build` sends
23
26
  One `Task` call to `devrites-slice-wright` carrying everything the writer needs and nothing it
@@ -91,6 +94,23 @@ set.
91
94
  [`evidence-standard.md`](evidence-standard.md). Evidence is the wright's real command output,
92
95
  not its say-so. Then tick AFK if `.devrites/AFK` is present (`tick-afk.sh`; exit 3 → STOP).
93
96
 
97
+ ## Forge — competing candidates (the deliberate exception)
98
+
99
+ The single-writer rule forbids parallel writers **sharing one tree**. A `Forge: yes` slice
100
+ (flagged by `/rite-vet` as a genuine architecture fork at Complexity ≥4) is the one sanctioned
101
+ fan-out, and it keeps the rule intact by **isolation**: each candidate wright works in its own
102
+ `git worktree`, sees the identical slice contract plus one **distinct strategy**, and never
103
+ touches another candidate's tree. A read-only [`devrites-forge-judge`](../../../agents/devrites-forge-judge.md)
104
+ then scores the finished candidates against acceptance + `test-plan.md` + `.devrites/principles.md`
105
+ + the anti-slop charter, and the orchestrator lands **exactly one** winner's diff in the working
106
+ tree. No tree ever has two authors; exactly one author's work ships. Everything downstream (doubt,
107
+ fail-on-red, reconcile against the winner's claimed set, record) runs on the winner as if a single
108
+ wright had built it.
109
+
110
+ Full mechanics — strategy derivation, worktree setup, the judge contract, landing + grafting the
111
+ winner, `forge-report.md`, AFK budgeting, and the worktree-unavailable fallback — live in
112
+ [`forge.md`](forge.md).
113
+
94
114
  ## Fallback
95
115
  If the `Task` tool / sub-agent dispatch is unavailable, `/rite-build` runs the wright's
96
116
  discipline **inline** in its own context and flags it as a fallback (no clean-context benefit).
@@ -17,6 +17,7 @@ nothing gets missed in one batch. **No code here.**
17
17
  as their first step; the other rule files load on demand. Pull these via `Read` when shaping
18
18
  the plan:
19
19
  - `development-workflow.md` — small batches, trunk-always-green, definition of done.
20
+ - `principles.md` — the project invariants (`.devrites/principles.md`) the chosen approach must conform to.
20
21
  - `documentation.md` — record plan-time decisions and rationale.
21
22
 
22
23
  ## Operating rules
@@ -90,6 +91,12 @@ the plan:
90
91
  ```
91
92
  5. **Complexity & deviations gate** — justify anything off DevRites defaults (new dep,
92
93
  extra abstraction, second design system) in the plan; if you can't justify it, simplify.
94
+ **Principles conformance:** read `.devrites/principles.md` (if present) and confirm the
95
+ approach honors every declared invariant. A plan that conflicts with one is not "a deviation
96
+ to justify away" — either reshape the approach to conform, or, when the conflict is genuine and
97
+ intended, route it through the Spec Drift Guard plus a recorded decision and a scoped principle
98
+ exception a human approves. Never ready a plan that silently violates an invariant. (Re-scored
99
+ as a blocking gate at `/rite-vet`; no file → none declared → nothing to check.)
93
100
  6. **Write** `plan.md` + `tasks.md`; update `state.md` (phase: plan → next `/rite-build`).
94
101
  7. **Readiness gate** (bottom of plan-template): every acceptance criterion covered by a
95
102
  slice, dependency order acyclic + risk-first, no unjustified deviation, rollback for
@@ -105,6 +112,9 @@ Goal:
105
112
  Satisfies: AC-n[, AC-m] # reverse traceability — which spec acceptance criteria this slice satisfies
106
113
  Acceptance criteria: # which spec FR/criteria this satisfies
107
114
  Complexity: N/5 — <reason> # 1=trivial … 5=hairy; >3 triggers a reslice unless the reason justifies it
115
+ Forge: no | yes — <reason> # default no. Propose yes ONLY when Complexity ≥4 AND the slice has ≥2 genuinely-viable
116
+ # approaches with no clear winner (an architecture fork, not just "hard"). /rite-vet confirms
117
+ # or clears it; /rite-build then competes K isolated candidates and keeps one. See rite-build/reference/forge.md.
108
118
  Mode: AFK | HITL # AFK = implementable + mergeable without human gating;
109
119
  # HITL = needs a human decision mid-slice (design call,
110
120
  # architectural choice, destructive migration sign-off).
@@ -139,7 +149,10 @@ Evidence required:
139
149
  > coverage. Keep `Blocked by` cycle-free. `depends_on` is the machine-readable mirror tools read
140
150
  > to pick the next *buildable* slice; `Complexity` (>3 → reslice) sizes it; `Satisfies` +
141
151
  > `Consumes/Produces` + `Known-Gotchas` + `Validation commands` make each slice a self-contained,
142
- > one-pass-implementable brief (the PRP target `/rite-vet` checks).
152
+ > one-pass-implementable brief (the PRP target `/rite-vet` checks). `Forge` flags the rare slice
153
+ > worth *competing* — a genuine architecture fork at high complexity, not a slice that is merely
154
+ > hard. Define only proposes it; `/rite-vet` confirms or clears it and `/rite-build` acts on it.
155
+ > The bulk of slices stay `no` (single-path is cheaper and the default).
143
156
 
144
157
  > **Mid-flight discipline.** When tempted to skip vertical slicing, coverage mapping, or dependency-order discipline — see [`anti-patterns`](reference/anti-patterns.md) (Common Rationalizations + Red Flags). Load it the moment you reach for the excuse.
145
158
 
@@ -77,6 +77,9 @@ map + worked examples: [`reference/failure-modes.md`](reference/failure-modes.md
77
77
  - [ ] **4 · Unverifiable goal** — is there a command that proves this, run, with output? Or am
78
78
  I asserting "it works"? Tautological test that can't fail? → run the FRAME verify command;
79
79
  record command + output (`testing.md`, evidence-over-confidence).
80
+ - [ ] **Principle check** — if `.devrites/principles.md` exists, does the change break a declared
81
+ invariant? A violation with no recorded, human-approved exception is a **Critical** — the
82
+ express lane is not a way around a project gate; escalate it, don't ship it (`principles.md`).
80
83
 
81
84
  The test for each changed line: **it traces directly to the criterion, and the criterion can
82
85
  be proven false.** A line that fails either is a finding.
@@ -85,8 +88,10 @@ be proven false.** A line that fails either is a finding.
85
88
 
86
89
  If FRAME can't produce a falsifiable criterion, or AUDIT surfaces a mode-1 / mode-3 issue that
87
90
  is actually a hidden design decision (new dependency, data model, second design system, an
88
- auth/migration/public-API touch) **STOP and route to `/rite-spec`**. Same drift guard the
89
- express lane enforces don't quietly grow an unframed ask into unreviewed work.
91
+ auth/migration/public-API touch), or a **declared-principle violation** with no recorded
92
+ exception **STOP and route to `/rite-spec`** (a needed exception is a human-approved decision,
93
+ not an inline call). Same drift guard the express lane enforces — don't quietly grow an unframed
94
+ ask into unreviewed work.
90
95
 
91
96
  ## Rules
92
97
  - Self-applied and inline. No subagent, no `.devrites/` workspace required. For an adversarial
@@ -1,6 +1,6 @@
1
1
  ---
2
2
  name: rite-learn
3
- description: Review the auto-captured learning ledger and promote recurring lessons to project rules — the human-gated half of the cross-feature learning loop. Capture is automatic (`/rite-seal` appends dismissed-finding classes + dead-ends to `.devrites/learnings.md` on every GO; the review skills load it before a fan-out), so the system learns without a command; this skill mines that ledger across features and decides which recurring lessons graduate into a rule (propose, don't impose). Use when the user says "what have we learned", "rite learn", "harvest lessons", "promote our learnings", or after several features ship. Not for the install (`/rite-doctor`), feature status (`/rite-status`), onboarding (`/rite-adopt`), or a diff review (`/rite-review`).
3
+ description: Review the auto-captured learning ledger and promote recurring lessons to project rules or principles — the human-gated half of the cross-feature learning loop. Capture is automatic (`/rite-seal` appends dismissed-finding classes + dead-ends to `.devrites/learnings.md` on every GO; the review skills load it before a fan-out), so the system learns without a command; this skill mines that ledger across features and decides which recurring lessons graduate into a rule (propose, don't impose). Use when the user says "what have we learned", "rite learn", "harvest lessons", "promote our learnings", or after several features ship. Not for the install (`/rite-doctor`), feature status (`/rite-status`), onboarding (`/rite-adopt`), or a diff review (`/rite-review`).
4
4
  argument-hint: "[--mine | \"<lesson to record>\"]"
5
5
  user-invocable: true
6
6
  disable-model-invocation: true
@@ -45,6 +45,11 @@ it never edits source or rule files on its own.
45
45
  you could swap onto any project says nothing).
46
46
  3. **Classify each candidate** into its durable home:
47
47
  - **project rule** — a craft/standard that belongs in a `.claude/rules/*` file or `CLAUDE.md`.
48
+ - **project principle** — a recurring correction that is really a *non-negotiable invariant*
49
+ (not just an idiom or a craft standard): graduate it to `.devrites/principles.md`
50
+ ([`principles.md`](../../rules/principles.md)). This is the **trusted, gating** layer — higher
51
+ stakes than a rule — so `/rite-learn` **drafts** the principle + a dated Governance entry for
52
+ the human to confirm; it never writes a principle silently.
48
53
  - **conventions-ledger entry** — a proven project idiom for `.devrites/conventions.md`.
49
54
  - **dismissed-finding class** — a pattern reviewers keep flagging that is *intentional here*;
50
55
  recording it stops the recurring false positive (`learnings.md`, loaded pre-fan-out).
@@ -53,9 +58,12 @@ it never edits source or rule files on its own.
53
58
  times, the proposed home) via `AskUserQuestion` — the human picks which to promote. Never
54
59
  promote a lesson to a rule silently; an unproven "lesson" hardened into a rule is its own slop.
55
60
  5. **Record the accepted.** For each the user accepts, append it with `learnings.sh add <slug>
56
- "<lesson>" <tag>` (`tag` ∈ `rule | convention | dismiss`). If the user approves a **rule** or
57
- **ledger** promotion, draft the exact edit and let the user confirm it through the normal flow
58
- — `/rite-learn` writes the ledger, not the rule files. Then `touch .devrites/.learnings-reviewed`
61
+ "<lesson>" <tag>` (`tag` ∈ `rule | convention | dismiss`). If the user approves a **rule**,
62
+ **principle**, or **ledger** promotion, draft the exact edit and let the user confirm it through
63
+ the normal flow — `/rite-learn` writes the ledger, not the rule or principle files. A
64
+ **principle** promotion is the highest-stakes of these: draft the `.devrites/principles.md` entry
65
+ **plus its dated Governance line**, and let the human confirm before it lands — a principle is a
66
+ gate, so it is amended deliberately, never auto-written. Then `touch .devrites/.learnings-reviewed`
59
67
  so the SessionStart learnings nudge snoozes until new signal accumulates.
60
68
 
61
69
  ## How the ledger is used
@@ -66,6 +74,11 @@ convention** raises the bar. The ledger is an **untrusted prior** — a fresh ob
66
74
  live code always overrides a ledger entry (see `.claude/rules/security.md`). Confidence in a
67
75
  recorded lesson never raises its authority.
68
76
 
77
+ A **project principle** (`.devrites/principles.md`) is the opposite layer — prescriptive,
78
+ trusted, and **gating**. Promoting a lesson there is a deliberate amendment, not a prior the next
79
+ fan-out can override: a violation becomes a blocking finding, not a suppressed false positive.
80
+ That asymmetry is why principle promotion is human-confirmed and dated, never auto-written.
81
+
69
82
  ## Gotchas
70
83
  - Evidence first: a lesson without ≥2 real occurrences is speculation. Cite the features.
71
84
  - Don't pad the ledger. Five real lessons that change behaviour beat thirty rubber-stamped rows.
@@ -49,7 +49,9 @@ don't load both up front.
49
49
  4. **If UI scope detected** read [`reference/ui.md`](reference/ui.md), and read
50
50
  `design-brief.md` if present (the UX/UI contract `devrites-ux-shape` shaped at spec and
51
51
  `devrites-frontend-craft` refined while building) so the polish honors the agreed
52
- direction + states. Then run
52
+ direction + states. **Read the `## Visual Verdict` table in `browser-evidence.md` if present:
53
+ its `FAIL` and `PARTIAL` rows are the normalize/quality-bar worklist** — fix the root cause of
54
+ each (a missing state, an off-token CTA, an anti-slop hit), don't decorate around it. Then run
53
55
  **Phase 3 (normalize)** → **Phase 4 (UI polish)**. Honor argument modes:
54
56
  - `bolder | quieter | distill | harden` — passed to Phase 4 as the
55
57
  emphasis dial.
@@ -36,9 +36,14 @@ the affected criteria/routes to refresh proof before `/rite-seal`.
36
36
  **Step 0:** Read `.claude/rules/core.md` first. The other rule files load on demand;
37
37
  pull these via `Read` when relevant:
38
38
  - `testing.md` — pyramid, determinism, no-flake discipline.
39
+ - `spec-grammar.md` — when the spec uses structured `### Requirement:` / `#### Scenario:`
40
+ blocks, each scenario (WHEN/THEN) is one observable behavior to walk and prove.
39
41
  - `performance.md` — measure first when perf is in scope.
40
42
  - `observability.md` — when the change has a runtime surface (endpoint, job, integration,
41
43
  user flow): telemetry must be present **and observed to emit**, not assumed.
44
+ - `developer-experience.md` — when the change ships a developer-facing surface (API / CLI / SDK /
45
+ webhook / config / error messages / getting-started): **measure** the DX scorecard (run the flow,
46
+ time time-to-hello-world, capture the verbatim error text), don't assert it.
42
47
 
43
48
  ## Operating rules
44
49
  - Evidence over confidence. Feature scope only — fix within the feature or record a
@@ -71,7 +76,19 @@ pull these via `Read` when relevant:
71
76
  (`devrites-browser-proof`): routes, viewports, screenshots (opened + described),
72
77
  console, network, interaction paths, and design-reference match if references exist.
73
78
  5. **Map results to acceptance** — walk `spec.md` acceptance criteria; note which are now
74
- proven and which aren't. If `test-plan.md` exists, also walk its acceptance→test map and
79
+ proven and which aren't. **If the spec uses the structured grammar** (`### Requirement:` /
80
+ `#### Scenario:` blocks — `spec-grammar.md`), walk it **per scenario**: each `#### Scenario:`
81
+ WHEN/THEN is one observable behavior that needs a passing asserting test (the WHEN is the
82
+ arrange, the THEN the assert). A scenario with no covering result is an unproven gap =
83
+ blocker, the same standing as an uncovered acceptance criterion. Re-run the grammar gate
84
+ first so a requirement hand-edited to malformed since `/rite-spec` can't masquerade as proven:
85
+ ```bash
86
+ SV=.claude/skills/devrites-lib/scripts/spec-validate.sh
87
+ [ -f "$SV" ] || SV="${CLAUDE_SKILL_DIR:-}/../devrites-lib/scripts/spec-validate.sh"
88
+ [ -f "$SV" ] || SV=pack/.claude/skills/devrites-lib/scripts/spec-validate.sh
89
+ [ -f "$SV" ] && { bash "$SV" ".devrites/work/<slug>"; echo "spec-validate rc=$?"; } || true
90
+ ```
91
+ If `test-plan.md` exists, also walk its acceptance→test map and
75
92
  per-gap requirements — a planned test (especially a regression-Critical) with no covering
76
93
  result is an unproven gap, not a pass. **Also walk the test-plan interaction inventory**
77
94
  (every interactive element + user flow): each must have a passing asserting test. An
@@ -107,6 +124,16 @@ pull these via `Read` when relevant:
107
124
  span actually emits, and record that observation in `evidence.md`. Instrumentation never seen
108
125
  emitting is unproven, not done. Skip entirely for pure-internal / docs / config / type-only
109
126
  changes — don't instrument a typo fix.
127
+ 5c. **Developer-experience measure (developer-facing surface only).** If the feature ships a public
128
+ API, CLI, SDK/library, webhook, config/env contract, error/exit path, or the getting-started flow
129
+ (`developer-experience.md`), **exercise it** rather than reading it: run the getting-started steps
130
+ on a clean state and **time time-to-hello-world**; invoke the CLI `--help` / call the endpoint /
131
+ import the package; trigger the failure path and capture the **verbatim** error text. For a docs or
132
+ quickstart page, capture it through the browser-proof ladder (`devrites-browser-proof`) and describe
133
+ the screenshot. Write the **measured** scorecard to `devex.md` (beside the `/rite-vet` prediction the
134
+ boomerang reconciles at `/rite-seal`) and the headline numbers + error strings to `evidence.md`. A
135
+ scorecard from "the code looks fine" is Source mode, not proof. Skip entirely when no developer-facing
136
+ surface is in scope — don't DX-measure an internal refactor.
110
137
  6. **On failure** → [failure-triage](reference/failure-triage.md) +
111
138
  `devrites-debug-recovery`. Reproduce → isolate → fix within scope → re-run; if a fix
112
139
  would exceed scope, record a blocker.
@@ -120,9 +147,11 @@ pull these via `Read` when relevant:
120
147
  ```
121
148
  Proved: <feature>
122
149
  Acceptance criteria proven: <n / total>
150
+ Scenarios proven: <n / total | n/a (flat acceptance)>
123
151
  Tests: <cmd → pass/fail (counts)>
124
152
  Build: <cmd → pass/fail> Lint: <cmd → pass/fail>
125
153
  Browser: <ladder rung used + summary | n/a>
154
+ DevEx: <measured TTHW + getting-started/error-message verdict → devex.md | n/a (no dev-facing surface)>
126
155
  Unresolved failures / blockers: <none | list>
127
156
  Next: /rite-polish (finish the feature → /rite-review → /rite-seal)
128
157
  ↻ Hygiene: /clear before /rite-polish (evidence.md + browser-evidence.md captured; debug trails noisy). See rules/context-hygiene.md.
@@ -23,6 +23,9 @@ express lane:**
23
23
  - **Ambiguous scope** — you'd have to guess what "done" means, or the ask hides a design
24
24
  decision (data model, new dependency, second design system).
25
25
  - Security-sensitive input handling, or a measurable performance-critical path.
26
+ - Would **break a declared project principle** (`.devrites/principles.md`) with no recorded,
27
+ human-approved exception — the express lane never relaxes a project gate; a needed exception is
28
+ a deliberate human decision, so route it to `/rite-spec`.
26
29
 
27
30
  If none hold, the change is small + reversible + unambiguous → proceed. **When in doubt,
28
31
  escalate** — the cost of the full lifecycle on a small change is minutes; the cost of the
@@ -34,6 +37,7 @@ Read `.claude/rules/core.md` first. Then the small set this lane actually needs:
34
37
  - `testing.md` — TDD, **completeness** (every touched behavior/element asserted) +
35
38
  **assertion strength** (no tautological tests; see it fail first), scaled to the change.
36
39
  - `error-handling.md` / `security.md` — only if the change touches input/errors.
40
+ - `principles.md` — when `.devrites/principles.md` exists; a change that breaks an invariant is a gate, not a quick fix.
37
41
 
38
42
  ## Workflow
39
43
  0. **Orient.** Read `core.md`. If a `.devrites/` workspace is active, run the preamble to
@@ -53,10 +57,11 @@ Read `.claude/rules/core.md` first. Then the small set this lane actually needs:
53
57
  4. **Prove (scoped).** Run the **targeted** tests + typecheck / lint for what changed (not
54
58
  the whole suite) → green. Record the command + output. A tautological test that can't
55
59
  fail is not proof.
56
- 5. **Review-lite + ship.** Self-review the diff (correctness, scope, idiom — one pass, no
57
- subagent fan-out). Show the diff, then on the user's confirm commit it (Conventional
58
- Commits, atomic) or hand to `/rite-ship` if a workspace is active. **Never push without
59
- the user asking.**
60
+ 5. **Review-lite + ship.** Self-review the diff (correctness, scope, idiom, and if
61
+ `.devrites/principles.md` exists no declared invariant broken; this is `/rite-frame`'s AUDIT
62
+ passone pass, no subagent fan-out). Show the diff, then on the user's confirm commit it
63
+ (Conventional Commits, atomic) — or hand to `/rite-ship` if a workspace is active. **Never push
64
+ without the user asking.**
60
65
 
61
66
  ## Escalation (mid-flight) — the Spec Drift Guard still applies
62
67
  If the "small" change turns out to be not small — a second slice appears, a real design