@thebattlearena/corner 0.9.1-beta.70 → 0.10.1-beta.78

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 (58) hide show
  1. package/README.md +46 -26
  2. package/package.json +1 -1
  3. package/plugin/.claude-plugin/plugin.json +1 -1
  4. package/ring/agents/cornerman.md +32 -6
  5. package/ring/agents/rudo.md +2 -1
  6. package/ring/agents/wrestler.md +19 -4
  7. package/ring/hooks/hooks.json +42 -0
  8. package/ring/scripts/bout-stats-render.mjs +366 -0
  9. package/ring/scripts/bout-stats.mjs +360 -0
  10. package/ring/scripts/brief-build.mjs +130 -0
  11. package/ring/scripts/brief-guard.mjs +154 -0
  12. package/ring/scripts/brief.mjs +272 -0
  13. package/ring/scripts/compact-hook.mjs +110 -0
  14. package/ring/scripts/config.mjs +25 -1
  15. package/ring/scripts/contract-endpoints.mjs +163 -0
  16. package/ring/scripts/contract-preview.mjs +34 -14
  17. package/ring/scripts/fight.mjs +27 -0
  18. package/ring/scripts/findings.mjs +82 -3
  19. package/ring/scripts/format-gate.mjs +723 -0
  20. package/ring/scripts/format-worker.mjs +120 -0
  21. package/ring/scripts/front-routes.mjs +345 -0
  22. package/ring/scripts/judges-card.mjs +324 -3
  23. package/ring/scripts/lookout.mjs +22 -4
  24. package/ring/scripts/mask-guard.mjs +34 -1
  25. package/ring/scripts/page-embed.mjs +112 -0
  26. package/ring/scripts/play-by-play.mjs +155 -7
  27. package/ring/scripts/preparation-collect.mjs +177 -0
  28. package/ring/scripts/preparation.mjs +547 -0
  29. package/ring/scripts/round-bell.mjs +9 -2
  30. package/ring/scripts/rule-ids.mjs +124 -0
  31. package/ring/scripts/tech-plan-log.mjs +81 -0
  32. package/ring/scripts/tech-plan.mjs +191 -0
  33. package/ring/scripts/verdicts-log.mjs +75 -0
  34. package/ring/scripts/verdicts.mjs +205 -0
  35. package/ring/skills/play-by-play/SKILL.md +235 -33
  36. package/ring/skills/play-by-play/templates/blocks.css +196 -0
  37. package/ring/skills/play-by-play/templates/chronicle.html +358 -55
  38. package/ring/skills/play-by-play/templates/endpoints-view.js +1153 -0
  39. package/ring/skills/play-by-play/templates/interview-form.js +516 -0
  40. package/ring/skills/play-by-play/templates/now-panel.js +719 -0
  41. package/ring/skills/referee/SKILL.md +181 -56
  42. package/ring/skills/referee/references/api-diagram.md +56 -0
  43. package/ring/skills/referee/references/precedents.md +11 -0
  44. package/ring/skills/referee/references/rulebook.md +97 -6
  45. package/ring/skills/referee/templates/api-diagram.html +115 -0
  46. package/ring/skills/referee/templates/cornerman-batch.md +17 -11
  47. package/ring/skills/referee/templates/cornerman-concept.md +28 -30
  48. package/ring/skills/referee/templates/cornerman-range.md +16 -11
  49. package/ring/skills/referee/templates/escalation.md +7 -1
  50. package/ring/skills/referee/templates/round-bell-body.md +4 -0
  51. package/ring/skills/referee/templates/wrestler-brief.md +28 -41
  52. package/ring/skills/referee/templates/wrestler-cutoff.md +4 -0
  53. package/ring/skills/referee/templates/wrestler-resume.md +4 -0
  54. package/ring/skills/sparring/SKILL.md +13 -4
  55. package/ring/skills/sparring/templates/contrato.md +29 -0
  56. package/ring/skills/sparring/templates/rudo-brief.md +7 -0
  57. package/ring/skills/title-defense/SKILL.md +2 -2
  58. package/ring/skills/weigh-in/SKILL.md +6 -0
@@ -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
 
@@ -39,9 +40,10 @@ list reaches the human.
39
40
  too; each venue rulebook says which core families apply there. The review lenses of the venue
40
41
  are `config.lenses`.
41
42
  5. **State lives in `~/.claude/ring/<owner>/<repo>/<pr>/`** (`node "$RING/scripts/judges-card.mjs"
42
- path <pr>`; owner and repo lowercased, `local/<name>` for a repo without an origin remote;
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,16 +52,29 @@ list reaches the human.
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
59
74
  doubt, prefer the option that preserves current behavior and wire contracts (PR11) and ticket
60
75
  the rest. Record each decision with
61
76
  `judges-card.mjs log <pr> decision "<id> · referee · <decision> · rests on <R/T/F/P, PRn, HPn>
62
- · discarded <alternative> · affects <area>"`, list it in the judges card under "Decisions I
77
+ · discarded <alternative> · affects <area>"`, list it in the judges card under "Decisions I
63
78
  took for you", and keep going.
64
79
  - **`human`**: escalate both lists, as the rulebook describes.
65
80
  - **The hard list always goes to the human**: merging, bases, closing or readying PRs,
@@ -67,9 +82,10 @@ list reaches the human.
67
82
  assignment did not ask for (including a base assertion customers would see); a PR over the
68
83
  host's size ceiling (how to slice it and any exception); **a new or changed endpoint in the
69
84
  API spec, a changed database model, or a change to the native surface of a mobile venue**
70
- (§2b); customer data or PII, money or billing,
85
+ (§2b); customer data or PII, money or billing,
71
86
  production; anything `mask-guard` blocks; a direct contradiction between two host
72
- instructions; a PR whose venue is in doubt (§1.2).
87
+ instructions; a PR whose venue is in doubt (§1.2); a deviation from a pattern the host's own
88
+ rules fix (P14), which you never approve as a design doubt.
73
89
  9. **Escalating to the human** (the hard list, size ceiling included, or anything under `human`):
74
90
  numbered text, two options each with its cost and your recommendation, answerable as "1. b"
75
91
  (`templates/escalation.md`). Use AskUserQuestion only when the host repo's instructions require
@@ -103,9 +119,19 @@ way out, and it refuses without a KO. Then the bout runs as always, with three i
103
119
  - **What the judge validated is the behavior that must not change**: write it as numbered
104
120
  invariants for the concept-mode cornerman, and add characterization tests first (T rules).
105
121
  - **The rudo's shortcuts for this repo are the first rebound package**, blockers first.
106
- - **The contract the sparring drafted goes through §2b** like any other: nothing of it is approved
107
- because the sparring used it. Its credentials (by name) are a hard-list item: taking a sandbox
108
- key's variable to production is the human's.
122
+ - **The bases go to the judge first.** The contracts decide most of the code still to build, so
123
+ before launching the wrestler (step 8) gather every endpoint and data model the sparring
124
+ proposed: every `<sparring dir>/contrato-<repo>.md` (one per repo; read them together, in repo
125
+ order), checked against the branch (`gh pr diff <pr>`: spec,
126
+ entities, schema, migrations, and the `tools/*.sql` the tables came from). Draw them with the
127
+ ring's diagrams: the endpoints per `references/api-diagram.md` into `<bout dir>/api-diagram.html`
128
+ and the data models per `references/db-diagram.md` into `<bout dir>/db-diagram.html`, each with
129
+ the rudo's shortcuts written as decisions to validate. Serve them with `contract-preview.mjs`
130
+ (§2b step 4) and escalate them as the bout's first question, one numbered decision per
131
+ endpoint or table. Nothing of it is approved because the sparring used it: each goes through
132
+ §2b and `approve-contract`, and concepts that depend on a contract wait for it, while the
133
+ shortcuts that do not can start. Its credentials (by name) are a hard-list item: taking a
134
+ sandbox key's variable to production is the human's.
109
135
  - **Open it with `--from-sparring <slug>`** (step 5): `judges-card.mjs open` refuses a sparring
110
136
  that is not a KO, records `fromSparring` and marks the hand-off on the sparring. The bout is then
111
137
  the third fall of the sparring's chronicle: one page and one link for the whole fight
@@ -122,7 +148,6 @@ way out, and it refuses without a KO. Then the bout runs as always, with three i
122
148
  rebound changes them. Starting the cleanup before that escalation is a foul; log it in the
123
149
  judges card.
124
150
 
125
-
126
151
  1. `gh pr view <pr> --json number,title,url,headRefName,baseRefName,files`. The **stack base** is
127
152
  `baseRefName` unless the human names another one; ask if the PR is part of a stack.
128
153
  2. **Venue.** `gh pr diff <pr> --name-only | node "$RING/scripts/config.mjs" venue`, or
@@ -137,7 +162,7 @@ way out, and it refuses without a KO. Then the bout runs as always, with three i
137
162
  `git worktree add --detach <abs path> origin/<headRefName>` (after `git fetch origin`). Never
138
163
  launch the wrestler with `isolation: "worktree"`: that creates another tree on another branch.
139
164
  5. Open the bout: `node "$RING/scripts/judges-card.mjs" open --pr <pr> --worktree <abs path>
140
- --branch <headRefName> --base <stack base> --venue <venue> [--from-sparring <slug>]`. This activates `mask-guard` for this repo.
165
+ --branch <headRefName> --base <stack base> --venue <venue> [--from-sparring <slug>]`. This activates `mask-guard` for this repo.
141
166
  It refuses to open when `.claude/ring.json` leaves `apiSpec` or `dbModels` empty while the repo
142
167
  has specs or migrations: fix the config (paths or globs; `null` means "detect") rather than
143
168
  passing `--allow-empty-contract`, which is only for a bout that truly has no contract. Then
@@ -146,12 +171,15 @@ way out, and it refuses without a KO. Then the bout runs as always, with three i
146
171
  6. **Gather the assignment**: review comments (`gh api repos/{owner}/{repo}/pulls/<pr>/comments`,
147
172
  `gh pr view <pr> --comments`) and what the human said. Every decision or preference goes to
148
173
  `judges-card.mjs log <pr> decision "<id> · <who> · <decision> · <affects>"`.
149
- 7. **Design review before touching code** (P5), when the change is large or the human asks: launch
174
+ 7. **Design review before touching code** (P5), always when the PR creates a module or an
175
+ aggregate, and otherwise when the change is large or the human asks: launch
150
176
  cornermen in **batch** mode in parallel, ≤20 files each (`templates/cornerman-batch.md`),
151
177
  `effort: "high"`. Decide their open questions yourself under `referee` (recorded as in §0.8);
152
178
  bring the human only the hard-list ones, or all of them under `human`.
153
- 8. **Launch one wrestler**: `Agent{subagent_type: "ring:wrestler", effort: "medium",
154
- 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
155
183
  **pilot**: one small concept as a sample for the human. Note the agent id in the judges card
156
184
  ("What I did"). If the harness exposes how the subagent ran, check it ran at medium.
157
185
  Give every Agent call a short `description` (its first three words label its chip in Ringside);
@@ -167,16 +195,46 @@ way out, and it refuses without a KO. Then the bout runs as always, with three i
167
195
  never in bulk**: a range is 2–3 commits or ~200 lines (`reviewEvery` in the host config, 3
168
196
  commits / 200 lines by default). `mask-guard` holds the wrestler's next commit once the
169
197
  unreviewed range reaches that cap, until you mark it (step 4).
170
- - each new range: `templates/cornerman-range.md` (≤15 lines);
171
- - when the wrestler closes a concept: `templates/cornerman-concept.md` (numbered behavior
172
- 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,…>`.
173
204
  If the wrestler claimed something ("does not change counter X"), hand the claim to the
174
- cornerman to verify against the base.
205
+ cornerman to verify against the base. The cornerman always checks the format of the range
206
+ (`format-gate.mjs check`); an unclean file is a finding like any other, fixed by the wrestler
207
+ with `format-gate.mjs fix <wt> --range <base>..HEAD`. A check that comes back **NO VERIFICADO**
208
+ (exit 2: sort-imports or prettier not installed in the repo) is never clean: register it as a
209
+ finding of the range (the repo lacks its format tooling), and settle it by having the
210
+ dependencies installed in the worktree, or by escalating if the repo has none.
211
+ **The scripts enforce it, not memory:** `lookout.mjs approve`, `round-bell.mjs open`,
212
+ `judges-card.mjs close` and every push to the PR branch (`mask-guard`) run the same check on
213
+ the range from the merge base with `origin/<base>` up to the sha, and refuse unclean files.
214
+ They also refuse a NOT VERIFIED check, unless you accepted it for the bout because the repo
215
+ really has no such tooling: `lookout.mjs approve <bout dir> <sha> --format-unverified "<why>"`
216
+ (or `format-gate.mjs accept-unverified <bout dir> "<why>"`), which goes to `decisions.md`.
217
+ Blobs that came out clean are cached in `<bout dir>/format-clean.json`, so after an approve the
218
+ push, the round and the close only check what changed. While a bout is active,
219
+ ring's hooks format what the wrestler edits or writes from the shell (sort-imports with the
220
+ repo's tsconfig, then prettier, with the repo's binaries; they stand aside when the repo's
221
+ `.claude/settings.json` formats on its own), and the wrestler runs `format-gate.mjs fix` before
222
+ each commit.
175
223
  2. **Register every finding, then filter the report yourself, against the base.** Each finding a
176
224
  cornerman reports (range, concept, batch or final review) goes into the bout's findings ledger
177
225
  before anything else: `node "$RING/scripts/findings.mjs" add <pr> --source range --range
178
- <from>..<to> --file <report>` (one finding per line; `--source batch` without a range for a
226
+ <from>..<to> --file <report>` (one finding per line; `--source batch` without a range for a
179
227
  design review), or `findings.mjs clean <pr> --range <from>..<to>` when it found nothing.
228
+ **Every finding cites a rule**: `add` refuses a line whose second field is not a rule id of the
229
+ venue (`<file:line> — R11 — …`). Rebound the report to the cornerman ("cite the rule"), or take
230
+ the line as yours with `--rule <id>`, logged with you as its author. A finding no rule covers
231
+ carries `X0`: it lands by itself in "For the rulebook / precedents" of the judges card, since a
232
+ gap the rulebook does not name is a rule it lacks.
233
+ **The human's own reviews are findings too.** When the human reviews code on video (a Tape of
234
+ the Battle Arena QA sessions, `qa_session_overview <link>`) or in a message, each pending item
235
+ becomes a finding with `--source human`, the rule it falls under, `X0` when none does, and the
236
+ link in its text: it runs through the same states as a cornerman's, and the rule it breaks
237
+ goes on the architecture checklist of the next briefs.
180
238
  `lookout.mjs mark` refuses a range whose findings are not registered. Check every BLOCKER:
181
239
  `git -C <wt> show <base>:<path>`. An assertion in a test created in this branch is not a base
182
240
  assertion (T1). Never forward a report you have not read. Every finding ends in exactly one
@@ -185,11 +243,11 @@ way out, and it refuses without a KO. Then the bout runs as always, with three i
185
243
  - discarded: `findings.mjs discard <pr> F<n> --id D<k> --reason "<why>"`;
186
244
  - left for a ticket: `findings.mjs ticket <pr> F<n> --ticket <id|"to file"> --reason "<why>"`;
187
245
  - sent to the human: `findings.mjs escalate <pr> F<n>`, and it goes in the escalation.
188
- A discard or a ticket is a decision you took for the human: the script writes it to
189
- `decisions.md` and to the judges card ("Decisions I took for you" or "Tickets"), and it stays
190
- **unshown** until a round-bell body carries it (round-bell does that itself) or you show it
191
- another way (`findings.mjs shown <pr> F<n> --in "<where>"`). "C3 left it unified later" is not a
192
- state: a finding nobody settled is open, and open findings hold the next round-bell and push.
246
+ A discard or a ticket is a decision you took for the human: the script writes it to
247
+ `decisions.md` and to the judges card ("Decisions I took for you" or "Tickets"), and it stays
248
+ **unshown** until a round-bell body carries it (round-bell does that itself) or you show it
249
+ another way (`findings.mjs shown <pr> F<n> --in "<where>"`). "C3 left it unified later" is not a
250
+ state: a finding nobody settled is open, and open findings hold the next round-bell and push.
193
251
  3. **Rebound in one package** (`templates/rebound.md`, P6), blockers first, and **freeze** the
194
252
  area under review ("do not touch `<path>`"). If the human left preferences to pass on, they go
195
253
  in the same message (P7). Cite the finding ids (F<n>) in each point. Send it with SendMessage
@@ -198,8 +256,8 @@ way out, and it refuses without a KO. Then the bout runs as always, with three i
198
256
  the wrestler closes it (a point closed in work still waiting on a contract is ticked
199
257
  `uncommitted, <where it lives>` and gets its sha when that concept is committed).
200
258
  4. Once the range is reviewed: `node "$RING/scripts/lookout.mjs" mark <bout dir> <last reviewed
201
- sha>`. When the cornerman **approved** it: `node "$RING/scripts/lookout.mjs" approve <bout dir>
202
- <sha>`, and only then push the PR branch up to that sha (you push, the wrestler never does).
259
+ sha>`. When the cornerman **approved** it: `node "$RING/scripts/lookout.mjs" approve <bout dir>
260
+ <sha>`, and only then push the PR branch up to that sha (you push, the wrestler never does).
203
261
  `mask-guard` blocks a push to the PR branch past the approved tip, and while a finding from a
204
262
  range inside it is open or more settled findings than `findingsGate.unshown` were never shown.
205
263
  **Two branches, two purposes, never mixed:**
@@ -223,7 +281,20 @@ way out, and it refuses without a KO. Then the bout runs as always, with three i
223
281
  `judges-card.mjs log <pr> decision "D<n> · referee · FOUL: D<k> <what was wrong> · <evidence>"`,
224
282
  naming the decision D<k> that was wrong; the chronicle marks D<k> at its own time.
225
283
  - **To the human**: only the hard list (or everything under `human`), with
226
- `templates/escalation.md`. A PR heading over the host's size ceiling is on that list: how to
284
+ `templates/escalation.md`. Every escalation goes with its context: a Markdown file with one
285
+ `## Contexto <n> · <question>` section per question, with the concrete detail of each option
286
+ (what it includes, what it leaves out, what it costs: the routes, the tables, the people it
287
+ touches), since a question is not answerable without it. Right after sending it, write it as
288
+ sent to a file and log it with its context:
289
+ `judges-card.mjs log <pr> escalation <file> --context <file>` (E<n>, stamped; an escalation
290
+ already logged takes its context with `log <pr> escalation-context E<n> <file>`); when the judge answers,
291
+ `judges-card.mjs log <pr> answer [E<n>] "<the judge's words>"` (`--attach <asset id,…>`
292
+ for images), before acting on it. The judge can also answer from the chronicle page (each
293
+ question has its form there): before acting on an answer, and whenever you resume, read the
294
+ page's `respuestas` with `ArtifactData` (`list`, the chronicle's `url`, `out_dir` into
295
+ `<bout dir>/tools/`) and run `judges-card.mjs page-answers <pr> <that dir>`, which logs each new one at the time
296
+ the judge saved it, with the text as the judge left it, the note and the images. They are «la
297
+ entrevista» of the chronicle, with the judge's answer next to each question. A PR heading over the host's size ceiling is on that list: how to
227
298
  slice it and any exception are the human's.
228
299
  - While the human decides, the wrestler works on something else: never leave it idle.
229
300
  - **If the human overturns a decision of yours**: apply it through a rebound, log the overturn
@@ -231,8 +302,10 @@ way out, and it refuses without a KO. Then the bout runs as always, with three i
231
302
  rulebook / precedents"). Do the same for every hard-list answer the human gives.
232
303
  6. **Round-bell per concept.** When a concept closes and passes review:
233
304
  `node "$RING/scripts/round-bell.mjs" open <pr> "<concept>" <sha> --verified "<gates, SHA,
234
- result>" --pending "<…>"` (add `--from <sha>` on the first round if the human already saw the
235
- commits before the bout started). It opens a draft stacked on the previous round, labelled
305
+ result>" --pending "<…>"` (on the first round add `--from <sha>` whenever the PR is stacked on
306
+ another branch, with the parent's tip, so its commits are not listed as the bout's own (PR28),
307
+ or when the human already saw the commits before the bout started; add `--flow-change "<…>"`
308
+ when the concept changes how a flow works today, P15). It opens a draft stacked on the previous round, labelled
236
309
  do-not-merge. It refuses while a finding from a range inside the round is open, and its body
237
310
  lists, under "Findings I settled without you", every finding you discarded or ticketed that the
238
311
  human has not seen yet. Tell the human the number, the production line delta and what to look
@@ -246,11 +319,11 @@ way out, and it refuses without a KO. Then the bout runs as always, with three i
246
319
  - **Soft size line**: a round over ~30 files or ~600 production lines (round-bell warns) is
247
320
  split by concept unless the human said otherwise. Under `delegation: "referee"` you decide how
248
321
  to split it. Only the host's hard size ceiling for the deliverable PR goes to the human.
249
- After each round-bell, refresh the chronicle (`/ring:play-by-play <pr>`). `mask-guard` enforces
250
- it: once the chronicle has a link, a bout record changed after its last publish blocks the
251
- Copilot re-request and `judges-card.mjs close` until it is republished.
252
- When the human approves a round-bell, that is **the pattern to replicate**: cite it
253
- (`gh pr diff <n>`) in every following brief and log it as a decision.
322
+ After each round-bell, refresh the chronicle (`/ring:play-by-play <pr>`). `mask-guard` enforces
323
+ it: once the chronicle has a link, a bout record changed after its last publish blocks the
324
+ Copilot re-request and `judges-card.mjs close` until it is republished.
325
+ When the human approves a round-bell, that is **the pattern to replicate**: cite it
326
+ (`gh pr diff <n>`) in every following brief and log it as a decision.
254
327
  7. **If the human objects, ask first: "Is it the solution (the behavior) or the class design?"**
255
328
  Only a class-design objection triggers a **structural pause** (P3): the wrestler finishes what
256
329
  is mechanical and stops; a cornerman in batch mode proposes the target design (class diagram,
@@ -261,7 +334,7 @@ way out, and it refuses without a KO. Then the bout runs as always, with three i
261
334
  9. **A concept that needs a new or changed endpoint, database model or native surface** goes
262
335
  contract first (§2b) before any implementation.
263
336
 
264
- If the wrestler is cut off (API error, limit), send it the cut-off variant of the brief. If the
337
+ If the wrestler is cut off (API error, limit), send it `brief.mjs wrestler <pr> --mode cutoff`. If the
265
338
  whole session restarted, see §5.
266
339
 
267
340
  ## 2b · Contract first: the API and the database models are the judge's
@@ -276,7 +349,7 @@ A change to the description of a field is a contract change too.
276
349
 
277
350
  **Never tell the wrestler a contract is approved** ("the contract is already approved, update the
278
351
  spec") unless `judges-card.mjs approve-contract` recorded the judge's yes for exactly that content:
279
- a spec edit you anticipate is a new contract, and goes through steps 2–6 before it is committed.
352
+ a spec edit you anticipate is a new contract, and goes through steps 2–7 before it is committed.
280
353
 
281
354
  1. If the host names a `contractIntent` (its own intent skill or doc, e.g. `/endpoint-intent`),
282
355
  fill it first and attach it.
@@ -287,15 +360,22 @@ a spec edit you anticipate is a new contract, and goes through steps 2–6 befor
287
360
  migration, the data backfill. Rebound until it passes.
288
361
  4. When the contract changes a database model, draw it first: `references/db-diagram.md` (the
289
362
  tables as an entity-relationship diagram, the migration DDL and what to validate) from
290
- `templates/db-diagram.html` into `<bout dir>/db-diagram.html`, and redraw it after every
291
- rebound. Then serve the preview from the uncommitted working tree (for a native surface
363
+ `templates/db-diagram.html` into `<bout dir>/db-diagram.html`. When it adds or changes
364
+ endpoints, draw them too: `references/api-diagram.md` (who calls each endpoint, its request,
365
+ response and errors, and the tables it touches) from `templates/api-diagram.html` into
366
+ `<bout dir>/api-diagram.html`. Redraw both after every rebound. Then serve the preview from the uncommitted working tree (for a native surface
292
367
  contract there is no preview server: show the staged diff of each native file, `git -C <wt>
293
- diff --cached -- <files>`, and, for a dependency, the M17 checklist filled in per package):
368
+ diff --cached -- <files>`, and, for a dependency, the M17 checklist filled in per package):
294
369
  `node "$RING/scripts/contract-preview.mjs" <bout dir>` with `run_in_background: true`
295
- (127.0.0.1:4815; Swagger UI of the proposed spec, what changes against the base, the diagram
296
- above the model diff, which folds into "Code diff", and a warning when a model file changed
297
- after the diagram). Ask the judge with
298
- `templates/escalation.md`: the URL, what changed, and your recommendation.
370
+ (127.0.0.1:4815; Swagger UI of the proposed spec, what changes against the base with the
371
+ endpoint diagram above it, the model diagram above the model diff, each code diff folded into a
372
+ disclosure, and a warning when a spec or model file changed after its diagram). Ask the judge with
373
+ `templates/escalation.md`: the URL, what changed, and your recommendation, with its context
374
+ per question, and log both (`judges-card.mjs log <pr> escalation <file> --context <file>`,
375
+ then `log <pr> answer` with the judge's words, or `page-answers`, §2.5). Then regenerate and
376
+ republish the chronicle (`/ring:play-by-play`, with `embed --contract`): the judge also
377
+ reviews the contract there, endpoint by endpoint (and, at a frontend venue, route by route
378
+ with `embed --routes`), and gives each one «Se aprueba» or «Se mejora».
299
379
  5. Until the judge answers, the contract zone is frozen; the wrestler works on other concepts or
300
380
  waits. Work that depends on the unapproved contract (code that needs its new package or its
301
381
  config) stays **uncommitted and out of the tree**: each later concept is kept as a patch under
@@ -320,22 +400,65 @@ a spec edit you anticipate is a new contract, and goes through steps 2–6 befor
320
400
  `<bout dir>/tools/`, not staged, so the judge reviews the contracts in one escalation and
321
401
  approves each one on its own. When nothing is left to draft, the wait is declared, not hidden:
322
402
  the judges card says what is waiting on the human, and the wrestler stops until the answer.
323
- 6. **Approved**: only the judge's approval of the **concrete** staged contract counts: an
403
+ 6. **The verdicts first.** Before acting on the contract, and whenever you resume, list the
404
+ chronicle's `veredictos` with `ArtifactData` (`list`, the chronicle's `url`, `out_dir` into
405
+ `<bout dir>/tools/`) and run `judges-card.mjs page-verdicts <pr> <that dir>`. It logs each
406
+ verdict at the time the judge gave it, turns each «se mejora» into a contract finding (rebound
407
+ it to the wrestler like any finding) and into a point of the technical plan («Lo que pidió el
408
+ juez»), and says when every endpoint and route is approved with its current fingerprint. A
409
+ rewritten contract outdates the verdicts on what changed: regenerate and republish the
410
+ chronicle after every change to the contract, so the judge reviews what is there now. **Do not
411
+ ask for `approve-contract` until page-verdicts says every one is approved with its current
412
+ fingerprint.**
413
+ 7. **Approved**: only the judge's approval of the **concrete** staged contract counts: an
324
414
  approval "in concept", given before the files existed, does not. If the review found effects the
325
415
  judge did not see (a manifest entry, resources dropped from the build, a permission, a platform
326
416
  flag), they go to the judge first, listed in one message. Then
327
417
  `node "$RING/scripts/judges-card.mjs" approve-contract <pr> --files <staged paths> --id D<n>
328
- --judge-said "<the judge's own words about this concrete contract>"`: it hashes the staged
418
+ --judge-said "<the judge's own words about this concrete contract>"`: it hashes the staged
329
419
  content and writes the judge's decision line itself, with the quote, so an approval is never
330
420
  typed in the judge's name. Then the wrestler commits the contract in its own commit and
331
421
  implements it. `mask-guard` blocks a commit of a contract file whose
332
422
  content was not approved, so any later edit to it needs a new approval.
333
- 7. **Rejected**: rebound the contract with the judge's reasons and go back to step 3.
334
- 8. **A change to the contract you decide while a cornerman reviews it** (a lockfile to normalize,
423
+ 8. **Rejected**: rebound the contract with the judge's reasons and go back to step 3.
424
+ 9. **A change to the contract you decide while a cornerman reviews it** (a lockfile to normalize,
335
425
  a config to fix) does not restart the review: it waits for the verdict, goes into the same
336
426
  rebound package (P6), and the rebounded contract gets a cornerman check of its own before the
337
427
  judge sees it.
338
428
 
429
+ ## 2c · The technical plan
430
+
431
+ `<bout dir>/plan-tecnico.md` is what the wrestler has to do for the deliverable: a title
432
+ (`# El plan del técnico · #<pr>`), `## <group>` and bullets
433
+ `- [<state>] **<title>** <detail> · origen: <ids>`, the states being pendiente · espera al juez ·
434
+ en curso · hecho, and a group whose name says «tickets» for what stays out of the delivery. The
435
+ origin cites findings (B1, F8…), decisions (D1…), questions (E1-3), verdicts or the sparring's
436
+ card.
437
+
438
+ - **Write it when the bout opens**, before the first wrestler brief. A bout from a sparring takes
439
+ its points from the card (the judge's validation, the rudo's shortcuts for this repo, «Not
440
+ verified yet», the pending items), from the contract review's findings, from your decisions and
441
+ from the escalation; the tickets the KO left out go to the tickets group. Every point says where
442
+ it comes from.
443
+ - **Never edit a state by hand**: `judges-card.mjs plan <pr> <n|title> <state> [--ref <sha|round-bell>]`
444
+ stamps the change in `plan.jsonl`; `plan <pr> add "<group>" "<title>" --origin <ids>` adds a
445
+ point, `plan <pr> list` prints it. When a round-bell closes a concept or the wrestler applies a
446
+ finding, mark the points it moves, with the round-bell or the sha as `--ref`. `page-verdicts`
447
+ adds each «se mejora» by itself.
448
+ - A bout does not close with a point of the plan outside «hecho» unless the judge sent it to a
449
+ ticket (move it to the tickets group with its id).
450
+
451
+ The chronicle shows the plan after «La entrevista» and before the rounds (`embed --plan`).
452
+
453
+ ## 2d · The chronicle during the preparation
454
+
455
+ The judge reviews the preparation in the chronicle, so keep it current: regenerate (`embed` with
456
+ every bout dir of the fight) and republish it after every change to the contract, after logging
457
+ the judge's answers and after logging the verdicts. In a fight with several repos (a back and a
458
+ front), each repo's referee keeps its own bout dir (its interview, verdicts, plan, diagram) and
459
+ logs there; whichever session regenerates the page passes every bout dir of the fight to `embed`
460
+ (the fight's JSON lists them in `bouts[].boutDir`), so the one page joins them all.
461
+
339
462
  ## 3 · Watching
340
463
 
341
464
  - `node "$RING/scripts/lookout.mjs" watch <bout dir>` with `run_in_background: true`. It exits
@@ -390,9 +513,10 @@ and runs the fixes through this same cycle (§2).
390
513
  checkouts), for the human to close and clean. Closing PRs and deleting worktrees and files is
391
514
  theirs.
392
515
  10. **The chronicle's final edition**: `/ring:play-by-play <pr>` with the status "Final", saying
393
- whether Copilot reviewed the last commit. `close` is blocked while the chronicle is behind.
516
+ whether Copilot reviewed the last commit, with its "El cierre en números" block
517
+ (`bout-stats.mjs`: numbers, charts, highlight reel and changelog). `close` is blocked while the chronicle is behind.
394
518
  11. When the human says the bout is over: `node "$RING/scripts/judges-card.mjs" close <pr>`. Its
395
- files stay as the record.
519
+ files stay as the record.
396
520
 
397
521
  ## 5 · `resume [<pr>]`
398
522
 
@@ -401,8 +525,9 @@ and runs the fixes through this same cycle (§2).
401
525
  unreviewed commits, open rebounds, decisions and the judges card. Read the rulebook and both precedent files in full again (§0.4).
402
526
  3. Review the unreviewed commits first (§2.1–2.4).
403
527
  4. A new session cannot talk to the old wrestler: its agent id does not survive. Relaunch it
404
- **without `isolation`**, on the existing worktree, with the resume variant of
405
- `templates/wrestler-brief.md` and `effort: "medium"`.
528
+ **without `isolation`**, on the existing worktree, with
529
+ `brief.mjs wrestler <pr> --mode resume --vars <file>` (add `nextConcept` to your values) and
530
+ the model and effort it prints.
406
531
  5. Restart the lookout (§3) and carry on with the cycle.
407
532
 
408
533
  For unattended work (the human goes to sleep), use `/ring:no-surrender` on top of an active bout.
@@ -0,0 +1,56 @@
1
+ # The endpoints of the contract as a diagram
2
+
3
+ When a contract (§2b) adds or changes endpoints, and always at the opening of a bout that comes
4
+ from a sparring, the judge approves a picture of the API, not a YAML diff. The referee draws it in
5
+ `<bout dir>/api-diagram.html` from `templates/api-diagram.html`; `contract-preview.mjs` shows it
6
+ above "What changes" (the spec text diff folds below it) and warns when a spec file changed after
7
+ it was drawn: redraw it after every rebound of the contract. The tables the endpoints touch are
8
+ drawn in detail in the model diagram (`references/db-diagram.md`); here they are only named.
9
+
10
+ Adapted from the architecture type of diagram-design by Cathryn Lavery (MIT,
11
+ `diagram-design-LICENSE.md` next to this file): its layout conventions, connectors, palette and
12
+ fonts.
13
+
14
+ ## What goes in
15
+
16
+ Read the spec on the branch and, after a sparring, every `<sparring dir>/contrato-<repo>.md`, not memory. One
17
+ page with three parts:
18
+
19
+ 1. **The diagram** (inline SVG), left to right in three columns:
20
+ - **Who calls**: the screen or action that calls each endpoint (`Tira de métricas`, `botón
21
+ Pausar`), grey, tag `CALLER`.
22
+ - **The endpoints**: one box each with its method tag (`GET`, `POST`, `PATCH`, `DELETE`), the
23
+ path in mono, then one line per part: `in ·` the params and body fields that matter, `out ·`
24
+ the status and the shape, `err ·` the error statuses with their code. **New or changed**:
25
+ accent stroke and tint, tag `NEW` or `CHANGED`; an existing endpoint the change only reuses
26
+ is grey with tag `EXISTING`; a dropped one is dashed with tag `DROPPED`.
27
+ - **The tables** each endpoint reads or writes: a small box with the table name, in the accent
28
+ when the model diagram shows it as new or changed.
29
+ - **Connectors**: caller → endpoint → table, orthogonal with rounded elbows (r = 8), arrows drawn
30
+ before the boxes, never diagonal. A write is a solid arrow labelled `writes`, a read a dashed one
31
+ labelled `reads`, the label on a paper patch 6 px above the line.
32
+ - Group endpoints by resource when there are more than four; above eight, split into one
33
+ diagram per resource.
34
+ 2. **The contract per endpoint**: a table with method and path, request, response, errors and
35
+ scoping (tenant, workspace or user), in the judge's language.
36
+ 3. **What to validate**: the decisions the judge is really approving, one per line, bold lead:
37
+ naming and REST semantics, pagination and sorting of lists, the error codes the front
38
+ handles, tenant scoping, idempotency of writes, what is computed in the back versus the front,
39
+ breaking changes for existing callers, and, after a sparring, **each shortcut the rudo took**
40
+ (no DTO, no auth check, a JSON column instead of a table, a mock) as its own decision: keep,
41
+ fix in this bout, or ticket.
42
+
43
+ A footer names the sources (spec paths and branch, the `contrato-<repo>.md` files). Write the copy in the judge's
44
+ language.
45
+
46
+ ## Form
47
+
48
+ - One self-contained HTML file: the template's CSS, fonts from Google Fonts only, no scripts (the
49
+ preview frames it without scripts).
50
+ - Palette of the template, the same as the model diagram: paper `#f5f5f5`, ink `#2d3142`, muted
51
+ `#4f5d75`, soft `#7a8399`, accent `#eb6c36` only on what changes. Geist for names, Geist Mono for
52
+ methods, paths and fields, Instrument Serif italic for notes.
53
+ - Size the `viewBox` to the boxes; text never touches a box edge (12 px in) and a path that does
54
+ not fit is wrapped at a `/`.
55
+ - Before serving, open it once: no overlapping text, no connector crossing a box that is not its
56
+ end, every endpoint has its `in`, `out` and `err` lines.
@@ -106,6 +106,17 @@ there; it had to be reopened._
106
106
 
107
107
  **PR27. Never link into a checkout the bout does not own, not even for a moment.** A measurement
108
108
  that needs another tree's files works on a copy or a fresh install under `<bout dir>/tools/`.
109
+ The same holds for cornermen and for `git archive` copies: they install their own dependencies,
110
+ never a link to another checkout's `node_modules`.
109
111
  _Why: a wrestler linked the main checkout's `node_modules` into its worktree to measure a bundler's
110
112
  isolation; any install or cleanup run through that link writes in the checkout every session
111
113
  shares._
114
+
115
+ **PR28. A round of a PR born from another branch always opens with `--from`.** Without it the
116
+ round-bell lists the parent branch's commits as the bout's own. _Why: a stacked PR's first round
117
+ showed the human dozens of commits that were not part of the bout._
118
+
119
+ **PR29. When the host regenerates artifacts from the working tree, no concept starts with
120
+ uncommitted changes.** A hook that rebuilds a wiki or generated files from the worktree sweeps
121
+ whatever is pending into the next commit. _Why: leftovers of one concept slipped into another
122
+ concept's commit through a regenerated wiki._