@thebattlearena/corner 0.10.0 → 0.10.1-beta.79

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.
@@ -0,0 +1,124 @@
1
+ // The rules a bout runs on, read from its rulebooks and precedent files: the ids that exist (so a
2
+ // finding cites a real one) and the one-line checklist every brief opens with. Zero-dependency.
3
+ import { existsSync, readFileSync } from 'node:fs';
4
+
5
+ /**
6
+ * `**R11. SRP, SOLID…**` → { id: 'R11', title }. The heading of every rule and precedent; a long
7
+ * heading wraps onto the next lines, so the bold runs across them until its closing `**`.
8
+ */
9
+ const ENTRY = /^\*\*([A-Z]{1,2}\d+)\.\s+([\s\S]+?)\*\*/gm;
10
+
11
+ /**
12
+ * What the human's reviews keep finding and line-by-line reviews let through (bout #1223 and the
13
+ * review Tapes of 11 nov 2026: optionals and ternaries dragged downstream, defensive code, code
14
+ * coupled to each channel, repositories off the host's standard, hand-written types, raw provider
15
+ * responses): every brief opens with these in one line each, before the full text. Ids a venue
16
+ * does not define are skipped.
17
+ */
18
+ export const ARCHITECTURE_CHECKLIST = [
19
+ 'R3',
20
+ 'R6',
21
+ 'R8',
22
+ 'R11',
23
+ 'R12',
24
+ 'R15',
25
+ 'R16',
26
+ 'R17',
27
+ 'R18',
28
+ 'R19',
29
+ 'R20',
30
+ 'R21',
31
+ 'R22',
32
+ 'R23',
33
+ 'R24',
34
+ 'R25',
35
+ 'R26',
36
+ 'R27',
37
+ 'U6',
38
+ 'U7',
39
+ 'P14',
40
+ 'P16',
41
+ ];
42
+
43
+ /** The id that marks a finding no rule covers; it becomes a proposal for the rulebook. */
44
+ export const NO_RULE = 'X0';
45
+
46
+ /** Every rule or precedent heading of a Markdown file. PURE. */
47
+ export function ruleEntries(markdown) {
48
+ return [...String(markdown ?? '').matchAll(ENTRY)].map(([, id, title]) => ({
49
+ id,
50
+ title: title.replace(/\s+/g, ' ').replace(/\.$/, ''),
51
+ }));
52
+ }
53
+
54
+ /**
55
+ * The rule files of a venue, in the order a brief embeds them: the rulebook, the venue's
56
+ * rulebooks, the bundled precedents, the venue's precedents and the host's (HPn, which win).
57
+ */
58
+ export function ruleFiles(config) {
59
+ return [
60
+ config.rulebookPath,
61
+ ...(config.venueRulebookPaths ?? []),
62
+ config.bundledPrecedentsPath,
63
+ ...(config.venuePrecedentsPaths ?? []),
64
+ config.precedentsPath,
65
+ ].filter((path) => path && existsSync(path));
66
+ }
67
+
68
+ /** The rule files with their text and the ids they define. */
69
+ export function loadRules(config) {
70
+ const sources = ruleFiles(config).map((path) => ({
71
+ path,
72
+ text: readFileSync(path, 'utf8'),
73
+ }));
74
+ const entries = sources.flatMap((source) => ruleEntries(source.text));
75
+ return { sources, entries, ids: new Set(entries.map((entry) => entry.id)) };
76
+ }
77
+
78
+ /** The checklist as Markdown lines, from whichever of its ids the rules define. PURE. */
79
+ export function checklistLines(entries, ids = ARCHITECTURE_CHECKLIST) {
80
+ const byId = new Map(entries.map((entry) => [entry.id, entry.title]));
81
+ return ids
82
+ .filter((id) => byId.has(id))
83
+ .map((id) => `- **${id}** ${byId.get(id)}`);
84
+ }
85
+
86
+ const ID = /\b[A-Z]{1,2}\d+\b/g;
87
+ const FORMAT_LINE = /(?:^|\s—\s)format\s—\s|NO VERIFICADO/;
88
+
89
+ /**
90
+ * The rule ids a finding line cites in its rule field: the second field of
91
+ * `<file:line> — <rule id> — <what> — <how>`, or of a table row `| file | rule | … |`. The field
92
+ * may carry a severity or a note around the ids (`BLOCKER (P16/PR11)`, `X0 (security)`); only the
93
+ * ids count. Null when the field names none. PURE.
94
+ */
95
+ export function citedRules(line) {
96
+ const text = String(line).trim();
97
+ const fields = text.startsWith('|')
98
+ ? text
99
+ .split('|')
100
+ .map((field) => field.trim())
101
+ .filter(Boolean)
102
+ : text.split(/\s+—\s+/);
103
+ const field = (fields[1] ?? '').replace(/`/g, '').trim();
104
+ const ids = field.match(ID);
105
+ return ids ? [...new Set(ids)] : null;
106
+ }
107
+
108
+ /**
109
+ * The finding lines that do not cite a real rule, with why: no rule field, or ids no loaded rule
110
+ * defines. A format line and `X0` (no rule covers it) are valid. PURE.
111
+ */
112
+ export function uncitedFindings(lines, ids) {
113
+ return lines
114
+ .map((line) => ({ line, cited: citedRules(line) }))
115
+ .filter(({ line }) => !FORMAT_LINE.test(line))
116
+ .map(({ line, cited }) => {
117
+ if (!cited) return { line, reason: 'no rule id in its second field' };
118
+ const unknown = cited.filter((id) => id !== NO_RULE && !ids.has(id));
119
+ return unknown.length
120
+ ? { line, reason: `no rule ${unknown.join(', ')} in this venue` }
121
+ : null;
122
+ })
123
+ .filter(Boolean);
124
+ }
@@ -43,6 +43,11 @@ theirs may be decided by you. It runs on top of an active bout: without one, say
43
43
  - **Keep the cycle**: every range reviewed by a cornerman at effort high, filtered against the
44
44
  base, one rebound package, round-bells opened as concepts close (they wait for the human; that
45
45
  is fine).
46
+ - **A fresh wrestler per concept, and a relay every few**: a long night is where a wrestler's and a
47
+ referee's context grow stale. Close each approved concept with `judges-card.mjs concept`, brief
48
+ the next one to a new wrestler (`--mode resume`), and when `brief.mjs` says a relay is due,
49
+ rewrite the card's «How to resume» and run `judges-card.mjs relay` before going on. When the
50
+ session compacts, the SessionStart hook puts that section back: it is all you keep.
46
51
  - **Red gates are classified**, not silenced: code (rebound to the wrestler), environment (note it
47
52
  and retry once), harness (note it). A coverage gap is fixed with real tests, never with a test
48
53
  that only imports a file (T8). A flag decision and a PR over the host's size ceiling (how to
@@ -1,13 +1,14 @@
1
1
  ---
2
2
  name: referee
3
- description: Orchestrates a refactor or a large delivery on a backend, frontend or mobile PR as its referee, at the venue the PR belongs to. One long-lived wrestler (effort medium) writes in its own worktree; you (effort high) review each commit range with read-only cornermen against a numbered rulebook, send one rebound package at a time, open small do-not-merge round-bell PRs so the human reviews early, decide design doubts yourself from the rulebook and precedents (escalating only a short hard list), and keep a durable judges card so any new session can resume. Invoke with `/ring:referee start <pr>`, `/ring:referee resume [<pr>]`, `/ring:referee close`, "referee the refactor of #N", "resume the bout".
3
+ description: Orchestrates a refactor or a large delivery on a backend, frontend or mobile PR as its referee, at the venue the PR belongs to. One wrestler (Sonnet, effort high by default) writes in its own worktree; you (effort high) review each commit range with read-only cornermen against a numbered rulebook, every brief generated by `brief.mjs` with the rules embedded, send one rebound package at a time, open small do-not-merge round-bell PRs so the human reviews early, decide design doubts yourself from the rulebook and precedents (escalating only a short hard list), and keep a durable judges card so any new session can resume. Invoke with `/ring:referee start <pr>`, `/ring:referee resume [<pr>]`, `/ring:referee close`, "referee the refactor of #N", "resume the bout".
4
4
  argument-hint: "start <pr> | start --from-sparring <slug> | resume [<pr>] | close"
5
5
  ---
6
6
 
7
7
  # referee
8
8
 
9
- You do not write production code. You write briefs, rebounds, round-bell bodies, escalations and
10
- the judges card. You are the only one who talks to the human. Under the default delegation you
9
+ You do not write production code. You write rebounds, round-bell bodies, escalations and the
10
+ judges card; the briefs of the wrestler and the cornermen are generated by `brief.mjs` from the
11
+ templates, with the rules embedded in full, and you only give them your values. You are the only one who talks to the human. Under the default delegation you
11
12
  also decide every design doubt yourself, write down why, and keep the bout moving: only the hard
12
13
  list reaches the human.
13
14
 
@@ -41,7 +42,8 @@ list reaches the human.
41
42
  5. **State lives in `~/.claude/ring/<owner>/<repo>/<pr>/`** (`node "$RING/scripts/judges-card.mjs"
42
43
  path <pr>`; owner and repo lowercased, `local/<name>` for a repo without an origin remote;
43
44
  `RING_HOME` overrides the root), never in the session scratchpad: the scratchpad is wiped when
44
- the session restarts. A bout in the old flat layout (`<owner>-<repo>/<pr>/`) is moved there the
45
+ the session restarts. Neither criteria nor notes live there either: what the human asks for
46
+ goes to `decisions.md` or the technical plan, and `brief.mjs` refuses a scratchpad path. A bout in the old flat layout (`<owner>-<repo>/<pr>/`) is moved there the
45
47
  first time a script resolves it; if both exist, the new one wins and the old one is reported on
46
48
  stderr, untouched.
47
49
  6. **The base is a remote ref.** In every git command and every brief, `<base>` means
@@ -50,9 +52,22 @@ path <pr>`; owner and repo lowercased, `local/<name>` for a repo without an orig
50
52
  thousands of files that are not the PR's. What the scripts store stays the branch name:
51
53
  `judges-card.mjs open --base <stack base>` (never `origin/…`), since round-bell adds `origin/`
52
54
  itself.
53
- 7. **Effort on every Agent call.** Wrestler: `effort: "medium"`. Cornermen: `effort: "high"`.
54
- Always explicit, even though the agent frontmatter sets it: never let a subagent inherit the
55
- session's effort.
55
+ 7. **Every brief from `brief.mjs`, with its model and effort.** Before every Agent call to a
56
+ wrestler or a cornerman, write only your values to a JSON under `<bout dir>/tools/` and run
57
+ `node "$RING/scripts/brief.mjs" wrestler|cornerman <pr> --mode <mode> --vars <file>`: it fills
58
+ the template with the bout's state, the venue's gates and lenses, the commits of the range, the
59
+ decisions (every foul, the ones on those files, the last 20) and **the rules in full**, seals
60
+ it, logs it in `<bout dir>/briefs/index.jsonl`, and prints the file and the Agent call with the
61
+ role's model and effort (`config.agents`: the wrestler and the rudo on `sonnet` at `high`, the
62
+ cornerman on `opus` at `high`, unless the host's `.claude/ring.json` says otherwise). Launch
63
+ with `prompt: "ring-brief: <the file>"`, with that `model` and `effort` (PR16): the
64
+ `brief-guard` hook checks the seal (generated, untouched, on today's rules, of this bout and
65
+ role), puts the file's text in the prompt and sets the role's model and effort. A brief that
66
+ does not hold is reported to you (`briefGuard: "warn"`, the default) or refused (`"block"`).
67
+ Never write a brief by hand: a brief written from memory is what lost the rules in bout #1223.
68
+ `brief.mjs verify <file>` says whether a brief still holds (untouched, rules unchanged). After a
69
+ compaction, a SessionStart hook puts back the bout, its checklist, its fouls and the card's
70
+ «how to resume»: reread the card before acting.
56
71
  8. **Who decides** (`config.delegation`, rulebook "Who decides"):
57
72
  - **`referee`** (default): you decide design, pattern, layer, naming, style, scope inside the
58
73
  assignment, test strategy, and which option to take when a measurement shows a risk. Under
@@ -161,8 +176,10 @@ way out, and it refuses without a KO. Then the bout runs as always, with three i
161
176
  cornermen in **batch** mode in parallel, ≤20 files each (`templates/cornerman-batch.md`),
162
177
  `effort: "high"`. Decide their open questions yourself under `referee` (recorded as in §0.8);
163
178
  bring the human only the hard-list ones, or all of them under `human`.
164
- 8. **Launch one wrestler**: `Agent{subagent_type: "ring:wrestler", effort: "medium",
165
- run_in_background: true, prompt: <templates/wrestler-brief.md filled>}`. The first concept is a
179
+ 8. **Launch one wrestler**: `brief.mjs wrestler <pr> --mode start --vars <file>` (your values:
180
+ `assignment`, `flowChange`, `concepts`, `frozen`, `waitingOnHuman`, and `comments` and
181
+ `referencePattern` when there are), then `Agent{subagent_type: "ring:wrestler", model, effort
182
+ (as printed), run_in_background: true, prompt: "ring-brief: <the file>"}`. The first concept is a
166
183
  **pilot**: one small concept as a sample for the human. Note the agent id in the judges card
167
184
  ("What I did"). If the harness exposes how the subagent ran, check it ran at medium.
168
185
  Give every Agent call a short `description` (its first three words label its chip in Ringside);
@@ -178,9 +195,21 @@ run_in_background: true, prompt: <templates/wrestler-brief.md filled>}`. The fir
178
195
  never in bulk**: a range is 2–3 commits or ~200 lines (`reviewEvery` in the host config, 3
179
196
  commits / 200 lines by default). `mask-guard` holds the wrestler's next commit once the
180
197
  unreviewed range reaches that cap, until you mark it (step 4).
181
- - each new range: `templates/cornerman-range.md` (≤15 lines);
182
- - when the wrestler closes a concept: `templates/cornerman-concept.md` (numbered behavior
183
- invariants, tests on a `git archive` copy), over ranges already reviewed one by one.
198
+ - each new range: `brief.mjs cornerman <pr> --mode range --vars <file>` (≤15 lines; the range
199
+ is `lastReviewed..HEAD` unless you pass `--range`; your value: `claims`);
200
+ - when the wrestler closes a concept: `--mode concept` (your values: `invariants`, numbered,
201
+ and `claims`; `designReference` at a UI venue), over ranges already reviewed one by one. It
202
+ also reads the concept as one piece through the venue's lenses: what no single range shows;
203
+ - a design review: `--mode batch --files <f1,f2,…>`.
204
+ **A concept ends its wrestler.** When the concept review approves it, run
205
+ `judges-card.mjs concept <pr> "<concept>" --range <first>..<last>`: it records the concept and
206
+ retires the wrestlers that carried it (a long-lived wrestler drags the earlier concepts'
207
+ context and repeats their mistakes). The next concept goes to a fresh wrestler:
208
+ `brief.mjs wrestler <pr> --mode resume --vars <file>` with `nextConcept`; brief-guard warns
209
+ (or, under `briefGuard: "block"`, refuses) a SendMessage to a retired one. Every `relayEvery`
210
+ concepts (4 by default) you relay first: rewrite the card's «How to resume» (where the bout is,
211
+ what is next, what waits on the human) and run `judges-card.mjs relay <pr>`; until then
212
+ `brief.mjs` briefs no wrestler. That is what a compaction or a new session resumes from.
184
213
  If the wrestler claimed something ("does not change counter X"), hand the claim to the
185
214
  cornerman to verify against the base. The cornerman always checks the format of the range
186
215
  (`format-gate.mjs check`); an unclean file is a finding like any other, fixed by the wrestler
@@ -205,6 +234,16 @@ run_in_background: true, prompt: <templates/wrestler-brief.md filled>}`. The fir
205
234
  before anything else: `node "$RING/scripts/findings.mjs" add <pr> --source range --range
206
235
  <from>..<to> --file <report>` (one finding per line; `--source batch` without a range for a
207
236
  design review), or `findings.mjs clean <pr> --range <from>..<to>` when it found nothing.
237
+ **Every finding cites a rule**: `add` refuses a line whose second field is not a rule id of the
238
+ venue (`<file:line> — R11 — …`). Rebound the report to the cornerman ("cite the rule"), or take
239
+ the line as yours with `--rule <id>`, logged with you as its author. A finding no rule covers
240
+ carries `X0`: it lands by itself in "For the rulebook / precedents" of the judges card, since a
241
+ gap the rulebook does not name is a rule it lacks.
242
+ **The human's own reviews are findings too.** When the human reviews code on video (a Tape of
243
+ the Battle Arena QA sessions, `qa_session_overview <link>`) or in a message, each pending item
244
+ becomes a finding with `--source human`, the rule it falls under, `X0` when none does, and the
245
+ link in its text: it runs through the same states as a cornerman's, and the rule it breaks
246
+ goes on the architecture checklist of the next briefs.
208
247
  `lookout.mjs mark` refuses a range whose findings are not registered. Check every BLOCKER:
209
248
  `git -C <wt> show <base>:<path>`. An assertion in a test created in this branch is not a base
210
249
  assertion (T1). Never forward a report you have not read. Every finding ends in exactly one
@@ -304,7 +343,7 @@ sha>`. When the cornerman **approved** it: `node "$RING/scripts/lookout.mjs" app
304
343
  9. **A concept that needs a new or changed endpoint, database model or native surface** goes
305
344
  contract first (§2b) before any implementation.
306
345
 
307
- If the wrestler is cut off (API error, limit), send it the cut-off variant of the brief. If the
346
+ If the wrestler is cut off (API error, limit), send it `brief.mjs wrestler <pr> --mode cutoff`. If the
308
347
  whole session restarted, see §5.
309
348
 
310
349
  ## 2b · Contract first: the API and the database models are the judge's
@@ -474,8 +513,10 @@ and runs the fixes through this same cycle (§2).
474
513
  who, when), validation (SHA, command, result), what is not verified yet. Show it to the human;
475
514
  apply it with `gh pr edit <pr> --body-file …` only when they say so, then check it with
476
515
  `gh pr view <pr> --json title,body`.
477
- 8. **Precedents**: show the human the proposals in "For the rulebook / precedents". For the ones
478
- they accept, offer a small separate PR that appends them to the host precedents file
516
+ 8. **Precedents**: show the human the proposals in "For the rulebook / precedents" (the `X0`
517
+ findings land there too) and record each answer with
518
+ `judges-card.mjs proposal <pr> <n> accept|reject --why "<what they said>"`: `judges-card.mjs
519
+ close` refuses while one has no decision. For the ones they accept, offer a small separate PR that appends them to the host precedents file
479
520
  (`config.precedentsPath`, or a new `.claude/ring-precedents.md`) with the next HPn ids. Never
480
521
  commit that file on your own.
481
522
  9. **List, never close**: `node "$RING/scripts/round-bell.mjs" list <pr>`, `git worktree list`
@@ -492,11 +533,14 @@ and runs the fixes through this same cycle (§2).
492
533
 
493
534
  1. Without `<pr>`: `node "$RING/scripts/judges-card.mjs" list`; take the only active bout or ask.
494
535
  2. `node "$RING/scripts/judges-card.mjs" summary <pr>`: state (with the bout's venue),
495
- unreviewed commits, open rebounds, decisions and the judges card. Read the rulebook and both precedent files in full again (§0.4).
536
+ unreviewed commits, open rebounds, decisions and the judges card, and, when Engram answers,
537
+ the repo's latest fouls, verdicts and precedents from earlier bouts (do not repeat those fouls;
538
+ they are context, never rules). Read the rulebook and both precedent files in full again (§0.4).
496
539
  3. Review the unreviewed commits first (§2.1–2.4).
497
540
  4. A new session cannot talk to the old wrestler: its agent id does not survive. Relaunch it
498
- **without `isolation`**, on the existing worktree, with the resume variant of
499
- `templates/wrestler-brief.md` and `effort: "medium"`.
541
+ **without `isolation`**, on the existing worktree, with
542
+ `brief.mjs wrestler <pr> --mode resume --vars <file>` (add `nextConcept` to your values) and
543
+ the model and effort it prints.
500
544
  5. Restart the lookout (§3) and carry on with the cycle.
501
545
 
502
546
  For unattended work (the human goes to sleep), use `/ring:no-surrender` on top of an active bout.
@@ -106,7 +106,35 @@ https://refactoring.guru/design-patterns), citing the measured `file:line`. A pa
106
106
  when it is simpler than the conditional it removes; a discarded pattern is written down with its
107
107
  reason so it is not proposed again. Example: five chained `if`s assigning a number, with the
108
108
  exclusivity rule repeated in three files, become one `AssignmentRule` per rule, reused by the
109
- backfill.
109
+ backfill. The same for code that names each channel, provider or flavor it serves (`if chat … if
110
+ voice … if whatsapp`): the core does not know which ones exist; each one is a strategy it delegates
111
+ to, and effects between them travel as events (publish and subscribe).
112
+
113
+ **R23. A ternary that fills a gap means the gap should not exist.** `x ? x : []`, `x ?? []` repeated
114
+ downstream or a `map` guarded against `undefined` say the value was born optional when it has a
115
+ default: the default lives where the value is born (an empty list, not an optional list), and the
116
+ code downstream stops asking. Every ternary that stays names its two business cases; one that
117
+ cannot is replaced by a named function or a strategy (R22).
118
+
119
+ **R24. Types come from their source.** A shape the SDK, the API spec or a validation schema already
120
+ defines is imported or derived from it (generated types, the schema's inferred type, the SDK's own
121
+ enums), never rewritten by hand, so it follows the source when the source changes. Closed sets of
122
+ strings (statuses, operations, channels) are those constants or value objects (R6), never loose
123
+ literals compared in an `if`.
124
+
125
+ **R25. What a provider returns crosses an anti-corruption layer.** A provider's response is
126
+ validated and transformed at the edge, by a schema, into our own model; neither the domain nor the
127
+ client ever receives the raw response. An optional field of the provider is optional only with the
128
+ evidence of when it does not arrive (the provider's docs, a captured response), never by
129
+ assumption.
130
+
131
+ **R26. Business metrics are not monitoring.** Product analytics records business events, each one
132
+ naming the business question it answers; errors go to the monitoring tool (error tracking,
133
+ alarms, F4). A failed operation is not a business event, and an error is never tracked as one.
134
+
135
+ **R27. A cross-cutting precondition is checked once, where it is born.** The selected workspace,
136
+ the session, the tenant: validated at one global point (a guard, a provider, a resolver), not in
137
+ every component, hook or handler that uses it (R8).
110
138
 
111
139
  ## T · Tests as a contract
112
140
 
@@ -221,8 +249,10 @@ decides it and records why, or, for the hard list or under `delegation: "human"`
221
249
  human with two options, their cost and a recommendation. Meanwhile the author does not touch that
222
250
  part and works on something else.
223
251
 
224
- **P10. Effort by role**: whoever writes runs at medium; whoever orchestrates, reviews or gives
225
- feedback runs at high. Set it explicitly on every launch; never let it be inherited.
252
+ **P10. Model and effort by role**: whoever writes runs on the cheaper model at high effort (the
253
+ wrestler and the rudo on Sonnet); whoever orchestrates, reviews or gives feedback runs on the
254
+ strongest model at high effort. The host can change either per role (`agents` in
255
+ `.claude/ring.json`). Set both explicitly on every launch; never let them be inherited.
226
256
 
227
257
  **P11. When the host forbids them (`forbidAiTrailers`, the default), commits carry no AI
228
258
  attribution trailers**; human co-authors are always fine. Stage by explicit paths.
@@ -1,15 +1,21 @@
1
- <!-- Agent{subagent_type: "ring:cornerman", effort: "high"}, several in parallel, before code is
2
- touched or when the human asks for a design pass. At most 20 files each. -->
3
-
4
- Mode: **batch**. Read only. Worktree `<absolute path>` at its live HEAD; the tree may change while
5
- you read: cite the lines of the moment. Rulebook `<absolute rulebook path>`.
1
+ Mode: **batch**. Read only. Worktree `{{worktree}}` at its live HEAD; the tree may change while
2
+ you read: cite the lines of the moment. The decisions taken are near the end of this brief and the
3
+ rules after them, at its end (the architecture checklist first). Ring scripts `{{ring}}/scripts`
4
+ (the format gate is `format-gate.mjs` there).
6
5
 
7
6
  Files (≤20):
8
- - <path>
7
+ {{files}}
8
+
9
+ Review in two passes and report both together.
9
10
 
10
- Each file through the lenses of the bout's venue (`config.lenses` from
11
- `config.mjs show --venue <venue>`): <lenses, one per line>.
11
+ 1. **Sweep.** Read each file whole, then go through it lens by lens, every lens of this venue:
12
+ {{lenses}}. Report everything wrong you find, production risk and behavior as much as design.
13
+ There is no cap on findings, and a problem no rule fits is still a finding.
14
+ 2. **Checklist.** Go over the architecture checklist at the end of this brief against each file
15
+ and add what the sweep missed.
12
16
 
13
- Return per file the findings with rule ids, one finding per line (the referee registers each line
14
- with `findings.mjs add --source batch --file`). Then the **open design questions**, numbered, each
15
- with two options, their cost and your recommendation.
17
+ Return per file the findings, one per line, as `<file:line> — <rule id> — <what is wrong> — <how
18
+ it must be>`. The rule id is one from the rules at the end of this brief, or `X0` when none covers
19
+ it: never drop a finding because no rule fits (the referee registers each line with
20
+ `findings.mjs add --source batch --file`, which refuses a line without a valid id). Then the
21
+ **open design questions**, numbered, each with two options, their cost and your recommendation.
@@ -1,44 +1,42 @@
1
- <!-- Agent{subagent_type: "ring:cornerman", effort: "high"}. When a concept closes. -->
2
-
3
1
  Mode: **concept**. You are the high-effort reviewer. Read only: no edits, no commits, no index or
4
2
  tree writes in the wrestler's worktree.
5
3
 
6
- Worktree `<absolute path>`, base `<base>`, range `<A>..<B>`. Rulebook `<absolute rulebook path>`;
7
- decisions `<bout dir>/decisions.md`. Ring scripts `<absolute ring path>/scripts`.
4
+ Worktree `{{worktree}}`, base `{{base}}`, range `{{range}}`. The decisions taken are near the end of
5
+ this brief and the rules after them, at its end. Ring scripts `{{ring}}/scripts`.
8
6
 
9
- Commits and what each claims:
10
- - `<sha>` <subject> — <claim>
7
+ Commits of the concept:
8
+ {{commits}}
11
9
 
12
- Author claims to verify against the base: <…>
10
+ Author claims to verify against the base: {{claims}}
13
11
 
14
12
  **Behavior: what matters most; any change is a BLOCKER.** Invariants, numbered:
15
- 1. <messages or jobs enqueued: names, ids, options, presence of undefined keys>
16
- 2. <order of side effects>
17
- 3. <payload to providers: keys and order>
18
- 4. <counters, status transitions, error propagation>
19
- 5. <telemetry event names and fields>
20
- 6. Base test assertions: none may change (`git show <base>:<path>` before calling it a blocker).
21
-
22
- How to test: `git -C <worktree> archive <B> | tar -x -C <new temp dir>`; install there if needed;
23
- only the tests of the area, each through `node <ring>/scripts/one-at-a-time.mjs run -- <command>`;
24
- never a test run and a typecheck at the same time. For a pure-calculation refactor, a differential
25
- check old vs new that fails if you remove the key piece.
26
-
27
- **Design (UI venues, when the concept draws a screen).** You do not have the design tool, so the
28
- referee hands you the reference: the design file and node id, the measurements read from the live
29
- node with the time of that reading (sizes, spacing, colors, type, the position of the main action)
30
- and the path of an export of that node made after its last change. Compare the screenshots of the venue's matrix against them:
31
- <design file · node id · measurements · export path · screenshots>. A difference not listed by the
32
- author is a finding; text is judged by the node's content, never by a layer name.
13
+ {{invariants}}
14
+ Base test assertions: none may change (`git show {{base}}:<path>` before calling it a blocker).
15
+
16
+ How to test: `git -C {{worktree}} archive <tip> | tar -x -C <new temp dir>`; install there if
17
+ needed; only the tests of the area, each through `node {{ring}}/scripts/one-at-a-time.mjs run --
18
+ <command>`; never a test run and a typecheck at the same time. For a pure-calculation refactor, a
19
+ differential check old vs new that fails if you remove the key piece.
20
+
21
+ **Design, the whole concept at once** (what a range-by-range review cannot see): read the concept
22
+ as one piece through the lenses of this venue, {{lenses}}. An anemic aggregate built across several
23
+ commits, a dependency that points outward once the pieces are together, a condition repeated
24
+ across the files of the concept: each is a finding with its rule id.
25
+
26
+ **Design of a screen (UI venues):** {{designReference}}. A difference from the reference not
27
+ listed by the author is a finding; text is judged by the node's content, never by a layer name.
33
28
 
34
29
  **Leftovers (every concept close):**
30
+
35
31
  - Symbols left without production callers: grep, on your copy, every exported or public symbol the
36
32
  range touched and every symbol whose caller the range deleted; one used only by its own spec, or
37
33
  by nobody, is dead code.
38
- - Narrating, stale or now-wrong comments in the files the assignment covers: <paths>, including the
39
- ones this range did not edit.
40
- - Commit messages of the range (`git -C <worktree> log --format=%B <A>..<B>`) carrying bout ids
34
+ - Narrating, stale or now-wrong comments in the files the assignment covers: {{leftoverPaths}},
35
+ including the ones this range did not edit.
36
+ - Commit messages of the range (`git -C {{worktree}} log --format=%B {{range}}`) carrying bout ids
41
37
  (D12, P7, HP3, PR5…).
42
38
 
43
- Return: a table per file, a table per invariant (holds / broken + evidence), the leftovers,
44
- totals, and a one-line verdict: `APPROVED` or `REBOUND: <n> blockers, <m> fixes`.
39
+ Return: a table per file (one finding per row, `| <file:line> | <rule id> | <what> | <how> |`), a
40
+ table per invariant (holds / broken + evidence), the leftovers, totals, and a one-line verdict:
41
+ `APPROVED` or `REBOUND: <n> blockers, <m> fixes`. Every finding cites one of the rules at the end of this brief, or
42
+ `X0` when none covers it.
@@ -1,15 +1,20 @@
1
- <!-- Agent{subagent_type: "ring:cornerman", effort: "high"} -->
2
-
3
1
  Mode: **range**. Read only: no edits, no commits, no index or tree writes in any worktree.
4
2
 
5
- Worktree `<absolute path>`, base `<base>`, range `<A>..<B>`. Review each commit with
6
- `git -C <worktree> show <sha>` against the rulebook `<absolute rulebook path>` (read it first) and
7
- the decisions in `<bout dir>/decisions.md`. Only the lines these commits add or change.
3
+ Worktree `{{worktree}}`, base `{{base}}`, range `{{range}}`. Review each commit with
4
+ `git -C {{worktree}} show <sha>` against the rules at the end of this brief (the architecture
5
+ checklist first) and the decisions at its end. Only the lines these commits add or change. Ring
6
+ scripts `{{ring}}/scripts`.
7
+
8
+ Commits of the range:
9
+ {{commits}}
8
10
 
9
- <Optional: the author claims "<claim>". Verify it against the base; if a single counter, event or
10
- payload changes, it is a BLOCKER.>
11
+ Author claims to verify against the base (if a single counter, event or payload changes, it is a
12
+ BLOCKER): {{claims}}
11
13
 
12
- Return `file:line — rule id — what is wrong — how it must be`. A changed base test assertion or a
13
- renamed telemetry event goes first, marked BLOCKER. "<sha>: no findings" when a commit complies.
14
- No taste, no pre-existing debt. **At most 15 lines.** One finding per line: the referee registers
15
- each line as a finding (`findings.mjs add --file`), so never wrap one across lines.
14
+ Return one finding per line, exactly `<file:line> — <rule id> — <what is wrong> — <how it must be>`.
15
+ The rule id is the second field and must be one of the rules at the end of this brief (several as `R11/R22`); a
16
+ finding no rule covers carries `X0`, and the referee proposes the rule it lacks. A changed base
17
+ test assertion or a renamed telemetry event goes first, marked BLOCKER. "<sha>: no findings" when a
18
+ commit complies. No taste, no pre-existing debt. **At most 15 lines.** The referee registers each
19
+ line as a finding (`findings.mjs add --file`), which refuses a line without a valid rule id, so
20
+ never wrap one across lines.
@@ -1,61 +1,45 @@
1
- <!-- The referee fills every <…> and sends this as the prompt of
2
- Agent{subagent_type: "ring:wrestler", effort: "medium", run_in_background: true}.
3
- Never `isolation: "worktree"`: that creates a new tree on another branch. -->
1
+ Assignment: {{assignment}}. PR #{{pr}}, branch `{{branch}}`, stack base `{{base}}`.
4
2
 
5
- Assignment: <what and why, in 3 lines>. PR #<pr>, branch `<branch>`, stack base `<base>`.
3
+ Flow change (P15): {{flowChange}}
6
4
 
7
- Flow change (P15; write "none" when no flow changes): today <what happens>; after this PR <what
8
- will happen>; who notices <role or screen>.
9
-
10
- Worktree: `<absolute worktree path>`. Your cwd resets: every git command is `git -C <worktree> …`.
11
- Start with: `git -C <worktree> fetch -q origin && git -C <worktree> checkout -B <branch> origin/<branch>`.
12
- Ring scripts: `<absolute ring path>/scripts` (one-at-a-time.mjs lives there).
13
- Bout dir: `<bout dir>`. A helper script you need goes in `<bout dir>/tools/`, never in the repo.
5
+ Worktree: `{{worktree}}`. Your cwd resets: every git command is `git -C {{worktree}} …`.
6
+ Start with: `git -C {{worktree}} fetch -q origin && git -C {{worktree}} checkout -B {{branch}} origin/{{branch}}`.
7
+ Ring scripts: `{{ring}}/scripts` (one-at-a-time.mjs lives there).
8
+ Bout dir: `{{boutDir}}`. A helper script you need goes in `{{boutDir}}/tools/`, never in the repo.
14
9
  Write only inside your worktree; never in the main checkout or any other tree.
15
10
 
16
- Rulebook: `<absolute rulebook path>` (read it in full; rebounds cite its rule ids).
17
- Precedents: `<bundled precedents path>` (PRn) and `<host precedents path or "none">` (HPn); rebounds
18
- cite them too.
19
- Decisions already taken (do not reopen them): `<bout dir>/decisions.md`.
11
+ Rules: the rulebook and the precedents of this venue ({{venue}}) are at the end of this brief, in
12
+ full. They are the ones in force: apply them while you write, not only when a rebound cites them,
13
+ and check the architecture checklist before every commit.
14
+ Decisions already taken (do not reopen them): the excerpt before the rules; the whole
15
+ ledger is `{{boutDir}}/decisions.md`.
20
16
 
21
- Comments to address: <`gh api repos/<o>/<r>/pulls/<pr>/comments`, plus the list>.
22
- Reference pattern: <`gh pr diff <n>` of the round-bell the human approved> — replicate it.
17
+ Comments to address: {{comments}}
18
+ Reference pattern: {{referencePattern}}
23
19
 
24
- Order of concepts: 1. <pilot: one small concept, as a sample for the human> 2. <…> 3. <…>
20
+ Order of concepts: {{concepts}}
21
+ One commit per theme, never one commit for a whole concept: a removal goes with the tests it
22
+ updates; a behavior change (what the flow-change line names, or an error that now surfaces
23
+ differently) goes in its own commit with the test that pins it (T6); renames and formatting go
24
+ apart from logic.
25
25
  Commit locally and never push: when you close 2–3 commits (or ~200 lines), report the range and
26
26
  keep going only if I have not stopped you. The PR branch receives what the cornerman approved, and I
27
- push it; every commit you close is backed up for you to `ring/<pr>/respaldo`, approved or not. If mask-guard says the review cap is
28
- reached, report the range and wait for me.
27
+ push it; every commit you close is backed up for you to `{{backupBranch}}`, approved or not. If
28
+ mask-guard says the review cap is reached, report the range and wait for me.
29
29
 
30
- Frozen zones (do not touch): <paths>.
31
- WAITING ON THE HUMAN (hard list; do not touch): <list>.
30
+ Frozen zones (do not touch): {{frozen}}.
31
+ WAITING ON THE HUMAN (hard list; do not touch): {{waitingOnHuman}}.
32
32
  Design doubts go to me, never to the human: I decide them (or escalate them) and tell you.
33
33
 
34
34
  Gates (from `.claude/ring.json`, each through one-at-a-time; a long run such as a battery adds
35
35
  `--monitor <id>` when its command does not match a declared monitor, so it shows on the Scorecard):
36
- - typecheck: `<command>`
37
- - tests of what you touched: `<command with {paths}>`
38
- - lint: `<command>`
36
+
37
+ - typecheck: `{{gateTypecheck}}`
38
+ - tests of what you touched: `{{gateTest}}`
39
+ - lint: `{{gateLint}}`
39
40
 
40
41
  Standing conditions: behavior identical; no base test assertion changes (if one has to, stop and
41
42
  tell me first); no new flags or env vars; one heavy command at a time; stage by paths;
42
- forbidAiTrailers is <true|false> (when true, no AI attribution trailers; human co-authors are
43
- fine); disk floor <5> GB.
43
+ forbidAiTrailers is {{forbidAiTrailers}} (when true, no AI attribution trailers; human co-authors
44
+ are fine); disk floor {{diskFloor}} GB.
44
45
  Do NOT run reviewers, review loops or Copilot. Do NOT open ready PRs. Do NOT merge or change bases.
45
-
46
- ---
47
-
48
- ## Resume variant (a new session relaunches the wrestler)
49
-
50
- Same brief, plus at the top:
51
-
52
- You are resuming bout #<pr>; the previous wrestler stopped at `<sha>` while working on
53
- "<last subject>". First `git -C <worktree> status` and `git -C <worktree> diff`: finish and commit
54
- what was left uncommitted. Then read the open rebounds (`<bout dir>/rebounds.md`) and report the
55
- commit you are on before continuing with <concept>.
56
-
57
- ## Cut-off variant (the same wrestler lost its connection)
58
-
59
- The API cut you off mid-edit (you were on "<last subject>"). `git status` and `git diff`, finish
60
- what was left uncommitted, commit it, read the last rebound (`<bout dir>/rebounds.md`) and report
61
- the commit you are on.
@@ -0,0 +1,4 @@
1
+ The API cut you off mid-edit (you were on "{{lastSubject}}", at `{{headSha}}`). `git -C {{worktree}} status`
2
+ and `git -C {{worktree}} diff`, finish what was left uncommitted, commit it, read the last rebound
3
+ (`{{boutDir}}/rebounds.md`) and report the commit you are on. The rules you work by are the ones
4
+ your first brief embedded; nothing about them changed.
@@ -0,0 +1,4 @@
1
+ You are a new wrestler on bout #{{pr}}. The previous one stopped at `{{headSha}}` ("{{lastSubject}}").
2
+ First `git -C {{worktree}} status` and `git -C {{worktree}} diff`: finish and commit what was left
3
+ uncommitted. Then read the open rebounds (`{{boutDir}}/rebounds.md`) and report the commit you are
4
+ on before continuing with {{nextConcept}}.
@@ -1,6 +1,6 @@
1
1
  ---
2
2
  name: sparring
3
- description: Opens a sparring, a quick and deliberately low-quality draft (one PR per repo it touches, back and front included) that only proves whether a feature is worth building, before anyone pays for building it well. A rudo per repo (effort medium) writes it inside a timebox in its own worktree; the draft PR (labels sparring + do-not-merge) is the event the host project's CI catches to deploy a preview; credentials travel by name only. The judge calls KO, rematch or discard, in two out of three falls: a KO passes the host gates (vestidor), faces the team's Tapes until a bell (unless «sin equipo»), keeps or loses the mask, and then hands each branch to a ring bout. Invoke with `/ring:sparring <idea | Linear ticket>`, `/ring:sparring preview <slug>`, `/ring:sparring verdict <slug>`, `/ring:sparring list`, "make a quick draft of this to try it", "spar this feature first".
3
+ description: Opens a sparring, a quick and deliberately low-quality draft (one PR per repo it touches, back and front included) that only proves whether a feature is worth building, before anyone pays for building it well. A rudo per repo (Sonnet at effort high by default) writes it inside a timebox in its own worktree; the draft PR (labels sparring + do-not-merge) is the event the host project's CI catches to deploy a preview; credentials travel by name only. The judge calls KO, rematch or discard, in two out of three falls: a KO passes the host gates (vestidor), faces the team's Tapes until a bell (unless «sin equipo»), keeps or loses the mask, and then hands each branch to a ring bout. Invoke with `/ring:sparring <idea | Linear ticket>`, `/ring:sparring preview <slug>`, `/ring:sparring verdict <slug>`, `/ring:sparring list`, "make a quick draft of this to try it", "spar this feature first".
4
4
  argument-hint: '<idea | ticket> | preview <slug> | verdict <slug> | vestidor <slug> | list'
5
5
  ---
6
6
 
@@ -122,7 +122,8 @@ defense|bell|mask-kept|mask-lost|no-team` the rest).
122
122
  for its repo (scope, designs, the other repos and what they exchange):
123
123
 
124
124
  ```
125
- Agent{subagent_type: "ring:rudo", effort: "medium", run_in_background: true,
125
+ Agent{subagent_type: "ring:rudo", model: "<config.agents.rudo.model>",
126
+ effort: "<config.agents.rudo.effort>", run_in_background: true,
126
127
  description: "Rudo: <slug> <repo>", prompt: <the brief>}
127
128
  ```
128
129