@thebattlearena/corner 0.9.1-beta.70 → 0.10.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 (45) hide show
  1. package/package.json +1 -1
  2. package/plugin/.claude-plugin/plugin.json +1 -1
  3. package/ring/agents/cornerman.md +20 -3
  4. package/ring/agents/wrestler.md +11 -0
  5. package/ring/hooks/hooks.json +20 -0
  6. package/ring/scripts/bout-stats-render.mjs +366 -0
  7. package/ring/scripts/bout-stats.mjs +360 -0
  8. package/ring/scripts/config.mjs +1 -1
  9. package/ring/scripts/contract-endpoints.mjs +163 -0
  10. package/ring/scripts/contract-preview.mjs +34 -14
  11. package/ring/scripts/fight.mjs +27 -0
  12. package/ring/scripts/findings.mjs +3 -1
  13. package/ring/scripts/format-gate.mjs +723 -0
  14. package/ring/scripts/format-worker.mjs +120 -0
  15. package/ring/scripts/front-routes.mjs +345 -0
  16. package/ring/scripts/judges-card.mjs +324 -3
  17. package/ring/scripts/lookout.mjs +22 -4
  18. package/ring/scripts/mask-guard.mjs +34 -1
  19. package/ring/scripts/page-embed.mjs +112 -0
  20. package/ring/scripts/play-by-play.mjs +155 -7
  21. package/ring/scripts/preparation-collect.mjs +177 -0
  22. package/ring/scripts/preparation.mjs +547 -0
  23. package/ring/scripts/round-bell.mjs +9 -2
  24. package/ring/scripts/tech-plan-log.mjs +81 -0
  25. package/ring/scripts/tech-plan.mjs +191 -0
  26. package/ring/scripts/verdicts-log.mjs +75 -0
  27. package/ring/scripts/verdicts.mjs +205 -0
  28. package/ring/skills/play-by-play/SKILL.md +235 -33
  29. package/ring/skills/play-by-play/templates/blocks.css +196 -0
  30. package/ring/skills/play-by-play/templates/chronicle.html +358 -55
  31. package/ring/skills/play-by-play/templates/endpoints-view.js +1153 -0
  32. package/ring/skills/play-by-play/templates/interview-form.js +516 -0
  33. package/ring/skills/play-by-play/templates/now-panel.js +719 -0
  34. package/ring/skills/referee/SKILL.md +136 -42
  35. package/ring/skills/referee/references/api-diagram.md +56 -0
  36. package/ring/skills/referee/references/precedents.md +11 -0
  37. package/ring/skills/referee/references/rulebook.md +65 -4
  38. package/ring/skills/referee/templates/api-diagram.html +115 -0
  39. package/ring/skills/referee/templates/escalation.md +7 -1
  40. package/ring/skills/referee/templates/round-bell-body.md +4 -0
  41. package/ring/skills/referee/templates/wrestler-brief.md +3 -0
  42. package/ring/skills/sparring/SKILL.md +10 -2
  43. package/ring/skills/sparring/templates/contrato.md +29 -0
  44. package/ring/skills/sparring/templates/rudo-brief.md +7 -0
  45. package/ring/skills/weigh-in/SKILL.md +6 -0
@@ -39,7 +39,7 @@ list reaches the human.
39
39
  too; each venue rulebook says which core families apply there. The review lenses of the venue
40
40
  are `config.lenses`.
41
41
  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;
42
+ path <pr>`; owner and repo lowercased, `local/<name>` for a repo without an origin remote;
43
43
  `RING_HOME` overrides the root), never in the session scratchpad: the scratchpad is wiped when
44
44
  the session restarts. A bout in the old flat layout (`<owner>-<repo>/<pr>/`) is moved there the
45
45
  first time a script resolves it; if both exist, the new one wins and the old one is reported on
@@ -59,7 +59,7 @@ list reaches the human.
59
59
  doubt, prefer the option that preserves current behavior and wire contracts (PR11) and ticket
60
60
  the rest. Record each decision with
61
61
  `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
62
+ · discarded <alternative> · affects <area>"`, list it in the judges card under "Decisions I
63
63
  took for you", and keep going.
64
64
  - **`human`**: escalate both lists, as the rulebook describes.
65
65
  - **The hard list always goes to the human**: merging, bases, closing or readying PRs,
@@ -67,9 +67,10 @@ list reaches the human.
67
67
  assignment did not ask for (including a base assertion customers would see); a PR over the
68
68
  host's size ceiling (how to slice it and any exception); **a new or changed endpoint in the
69
69
  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,
70
+ (§2b); customer data or PII, money or billing,
71
71
  production; anything `mask-guard` blocks; a direct contradiction between two host
72
- instructions; a PR whose venue is in doubt (§1.2).
72
+ instructions; a PR whose venue is in doubt (§1.2); a deviation from a pattern the host's own
73
+ rules fix (P14), which you never approve as a design doubt.
73
74
  9. **Escalating to the human** (the hard list, size ceiling included, or anything under `human`):
74
75
  numbered text, two options each with its cost and your recommendation, answerable as "1. b"
75
76
  (`templates/escalation.md`). Use AskUserQuestion only when the host repo's instructions require
@@ -103,9 +104,19 @@ way out, and it refuses without a KO. Then the bout runs as always, with three i
103
104
  - **What the judge validated is the behavior that must not change**: write it as numbered
104
105
  invariants for the concept-mode cornerman, and add characterization tests first (T rules).
105
106
  - **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.
107
+ - **The bases go to the judge first.** The contracts decide most of the code still to build, so
108
+ before launching the wrestler (step 8) gather every endpoint and data model the sparring
109
+ proposed: every `<sparring dir>/contrato-<repo>.md` (one per repo; read them together, in repo
110
+ order), checked against the branch (`gh pr diff <pr>`: spec,
111
+ entities, schema, migrations, and the `tools/*.sql` the tables came from). Draw them with the
112
+ ring's diagrams: the endpoints per `references/api-diagram.md` into `<bout dir>/api-diagram.html`
113
+ and the data models per `references/db-diagram.md` into `<bout dir>/db-diagram.html`, each with
114
+ the rudo's shortcuts written as decisions to validate. Serve them with `contract-preview.mjs`
115
+ (§2b step 4) and escalate them as the bout's first question, one numbered decision per
116
+ endpoint or table. Nothing of it is approved because the sparring used it: each goes through
117
+ §2b and `approve-contract`, and concepts that depend on a contract wait for it, while the
118
+ shortcuts that do not can start. Its credentials (by name) are a hard-list item: taking a
119
+ sandbox key's variable to production is the human's.
109
120
  - **Open it with `--from-sparring <slug>`** (step 5): `judges-card.mjs open` refuses a sparring
110
121
  that is not a KO, records `fromSparring` and marks the hand-off on the sparring. The bout is then
111
122
  the third fall of the sparring's chronicle: one page and one link for the whole fight
@@ -122,7 +133,6 @@ way out, and it refuses without a KO. Then the bout runs as always, with three i
122
133
  rebound changes them. Starting the cleanup before that escalation is a foul; log it in the
123
134
  judges card.
124
135
 
125
-
126
136
  1. `gh pr view <pr> --json number,title,url,headRefName,baseRefName,files`. The **stack base** is
127
137
  `baseRefName` unless the human names another one; ask if the PR is part of a stack.
128
138
  2. **Venue.** `gh pr diff <pr> --name-only | node "$RING/scripts/config.mjs" venue`, or
@@ -137,7 +147,7 @@ way out, and it refuses without a KO. Then the bout runs as always, with three i
137
147
  `git worktree add --detach <abs path> origin/<headRefName>` (after `git fetch origin`). Never
138
148
  launch the wrestler with `isolation: "worktree"`: that creates another tree on another branch.
139
149
  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.
150
+ --branch <headRefName> --base <stack base> --venue <venue> [--from-sparring <slug>]`. This activates `mask-guard` for this repo.
141
151
  It refuses to open when `.claude/ring.json` leaves `apiSpec` or `dbModels` empty while the repo
142
152
  has specs or migrations: fix the config (paths or globs; `null` means "detect") rather than
143
153
  passing `--allow-empty-contract`, which is only for a bout that truly has no contract. Then
@@ -146,12 +156,13 @@ way out, and it refuses without a KO. Then the bout runs as always, with three i
146
156
  6. **Gather the assignment**: review comments (`gh api repos/{owner}/{repo}/pulls/<pr>/comments`,
147
157
  `gh pr view <pr> --comments`) and what the human said. Every decision or preference goes to
148
158
  `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
159
+ 7. **Design review before touching code** (P5), always when the PR creates a module or an
160
+ aggregate, and otherwise when the change is large or the human asks: launch
150
161
  cornermen in **batch** mode in parallel, ≤20 files each (`templates/cornerman-batch.md`),
151
162
  `effort: "high"`. Decide their open questions yourself under `referee` (recorded as in §0.8);
152
163
  bring the human only the hard-list ones, or all of them under `human`.
153
164
  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
165
+ run_in_background: true, prompt: <templates/wrestler-brief.md filled>}`. The first concept is a
155
166
  **pilot**: one small concept as a sample for the human. Note the agent id in the judges card
156
167
  ("What I did"). If the harness exposes how the subagent ran, check it ran at medium.
157
168
  Give every Agent call a short `description` (its first three words label its chip in Ringside);
@@ -171,11 +182,28 @@ way out, and it refuses without a KO. Then the bout runs as always, with three i
171
182
  - when the wrestler closes a concept: `templates/cornerman-concept.md` (numbered behavior
172
183
  invariants, tests on a `git archive` copy), over ranges already reviewed one by one.
173
184
  If the wrestler claimed something ("does not change counter X"), hand the claim to the
174
- cornerman to verify against the base.
185
+ cornerman to verify against the base. The cornerman always checks the format of the range
186
+ (`format-gate.mjs check`); an unclean file is a finding like any other, fixed by the wrestler
187
+ with `format-gate.mjs fix <wt> --range <base>..HEAD`. A check that comes back **NO VERIFICADO**
188
+ (exit 2: sort-imports or prettier not installed in the repo) is never clean: register it as a
189
+ finding of the range (the repo lacks its format tooling), and settle it by having the
190
+ dependencies installed in the worktree, or by escalating if the repo has none.
191
+ **The scripts enforce it, not memory:** `lookout.mjs approve`, `round-bell.mjs open`,
192
+ `judges-card.mjs close` and every push to the PR branch (`mask-guard`) run the same check on
193
+ the range from the merge base with `origin/<base>` up to the sha, and refuse unclean files.
194
+ They also refuse a NOT VERIFIED check, unless you accepted it for the bout because the repo
195
+ really has no such tooling: `lookout.mjs approve <bout dir> <sha> --format-unverified "<why>"`
196
+ (or `format-gate.mjs accept-unverified <bout dir> "<why>"`), which goes to `decisions.md`.
197
+ Blobs that came out clean are cached in `<bout dir>/format-clean.json`, so after an approve the
198
+ push, the round and the close only check what changed. While a bout is active,
199
+ ring's hooks format what the wrestler edits or writes from the shell (sort-imports with the
200
+ repo's tsconfig, then prettier, with the repo's binaries; they stand aside when the repo's
201
+ `.claude/settings.json` formats on its own), and the wrestler runs `format-gate.mjs fix` before
202
+ each commit.
175
203
  2. **Register every finding, then filter the report yourself, against the base.** Each finding a
176
204
  cornerman reports (range, concept, batch or final review) goes into the bout's findings ledger
177
205
  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
206
+ <from>..<to> --file <report>` (one finding per line; `--source batch` without a range for a
179
207
  design review), or `findings.mjs clean <pr> --range <from>..<to>` when it found nothing.
180
208
  `lookout.mjs mark` refuses a range whose findings are not registered. Check every BLOCKER:
181
209
  `git -C <wt> show <base>:<path>`. An assertion in a test created in this branch is not a base
@@ -185,11 +213,11 @@ way out, and it refuses without a KO. Then the bout runs as always, with three i
185
213
  - discarded: `findings.mjs discard <pr> F<n> --id D<k> --reason "<why>"`;
186
214
  - left for a ticket: `findings.mjs ticket <pr> F<n> --ticket <id|"to file"> --reason "<why>"`;
187
215
  - 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.
216
+ A discard or a ticket is a decision you took for the human: the script writes it to
217
+ `decisions.md` and to the judges card ("Decisions I took for you" or "Tickets"), and it stays
218
+ **unshown** until a round-bell body carries it (round-bell does that itself) or you show it
219
+ another way (`findings.mjs shown <pr> F<n> --in "<where>"`). "C3 left it unified later" is not a
220
+ state: a finding nobody settled is open, and open findings hold the next round-bell and push.
193
221
  3. **Rebound in one package** (`templates/rebound.md`, P6), blockers first, and **freeze** the
194
222
  area under review ("do not touch `<path>`"). If the human left preferences to pass on, they go
195
223
  in the same message (P7). Cite the finding ids (F<n>) in each point. Send it with SendMessage
@@ -198,8 +226,8 @@ way out, and it refuses without a KO. Then the bout runs as always, with three i
198
226
  the wrestler closes it (a point closed in work still waiting on a contract is ticked
199
227
  `uncommitted, <where it lives>` and gets its sha when that concept is committed).
200
228
  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).
229
+ sha>`. When the cornerman **approved** it: `node "$RING/scripts/lookout.mjs" approve <bout dir>
230
+ <sha>`, and only then push the PR branch up to that sha (you push, the wrestler never does).
203
231
  `mask-guard` blocks a push to the PR branch past the approved tip, and while a finding from a
204
232
  range inside it is open or more settled findings than `findingsGate.unshown` were never shown.
205
233
  **Two branches, two purposes, never mixed:**
@@ -223,7 +251,20 @@ way out, and it refuses without a KO. Then the bout runs as always, with three i
223
251
  `judges-card.mjs log <pr> decision "D<n> · referee · FOUL: D<k> <what was wrong> · <evidence>"`,
224
252
  naming the decision D<k> that was wrong; the chronicle marks D<k> at its own time.
225
253
  - **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
254
+ `templates/escalation.md`. Every escalation goes with its context: a Markdown file with one
255
+ `## Contexto <n> · <question>` section per question, with the concrete detail of each option
256
+ (what it includes, what it leaves out, what it costs: the routes, the tables, the people it
257
+ touches), since a question is not answerable without it. Right after sending it, write it as
258
+ sent to a file and log it with its context:
259
+ `judges-card.mjs log <pr> escalation <file> --context <file>` (E<n>, stamped; an escalation
260
+ already logged takes its context with `log <pr> escalation-context E<n> <file>`); when the judge answers,
261
+ `judges-card.mjs log <pr> answer [E<n>] "<the judge's words>"` (`--attach <asset id,…>`
262
+ for images), before acting on it. The judge can also answer from the chronicle page (each
263
+ question has its form there): before acting on an answer, and whenever you resume, read the
264
+ page's `respuestas` with `ArtifactData` (`list`, the chronicle's `url`, `out_dir` into
265
+ `<bout dir>/tools/`) and run `judges-card.mjs page-answers <pr> <that dir>`, which logs each new one at the time
266
+ the judge saved it, with the text as the judge left it, the note and the images. They are «la
267
+ 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
268
  slice it and any exception are the human's.
228
269
  - While the human decides, the wrestler works on something else: never leave it idle.
229
270
  - **If the human overturns a decision of yours**: apply it through a rebound, log the overturn
@@ -231,8 +272,10 @@ way out, and it refuses without a KO. Then the bout runs as always, with three i
231
272
  rulebook / precedents"). Do the same for every hard-list answer the human gives.
232
273
  6. **Round-bell per concept.** When a concept closes and passes review:
233
274
  `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
275
+ result>" --pending "<…>"` (on the first round add `--from <sha>` whenever the PR is stacked on
276
+ another branch, with the parent's tip, so its commits are not listed as the bout's own (PR28),
277
+ or when the human already saw the commits before the bout started; add `--flow-change "<…>"`
278
+ when the concept changes how a flow works today, P15). It opens a draft stacked on the previous round, labelled
236
279
  do-not-merge. It refuses while a finding from a range inside the round is open, and its body
237
280
  lists, under "Findings I settled without you", every finding you discarded or ticketed that the
238
281
  human has not seen yet. Tell the human the number, the production line delta and what to look
@@ -246,11 +289,11 @@ way out, and it refuses without a KO. Then the bout runs as always, with three i
246
289
  - **Soft size line**: a round over ~30 files or ~600 production lines (round-bell warns) is
247
290
  split by concept unless the human said otherwise. Under `delegation: "referee"` you decide how
248
291
  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.
292
+ After each round-bell, refresh the chronicle (`/ring:play-by-play <pr>`). `mask-guard` enforces
293
+ it: once the chronicle has a link, a bout record changed after its last publish blocks the
294
+ Copilot re-request and `judges-card.mjs close` until it is republished.
295
+ When the human approves a round-bell, that is **the pattern to replicate**: cite it
296
+ (`gh pr diff <n>`) in every following brief and log it as a decision.
254
297
  7. **If the human objects, ask first: "Is it the solution (the behavior) or the class design?"**
255
298
  Only a class-design objection triggers a **structural pause** (P3): the wrestler finishes what
256
299
  is mechanical and stops; a cornerman in batch mode proposes the target design (class diagram,
@@ -276,7 +319,7 @@ A change to the description of a field is a contract change too.
276
319
 
277
320
  **Never tell the wrestler a contract is approved** ("the contract is already approved, update the
278
321
  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.
322
+ a spec edit you anticipate is a new contract, and goes through steps 2–7 before it is committed.
280
323
 
281
324
  1. If the host names a `contractIntent` (its own intent skill or doc, e.g. `/endpoint-intent`),
282
325
  fill it first and attach it.
@@ -287,15 +330,22 @@ a spec edit you anticipate is a new contract, and goes through steps 2–6 befor
287
330
  migration, the data backfill. Rebound until it passes.
288
331
  4. When the contract changes a database model, draw it first: `references/db-diagram.md` (the
289
332
  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
333
+ `templates/db-diagram.html` into `<bout dir>/db-diagram.html`. When it adds or changes
334
+ endpoints, draw them too: `references/api-diagram.md` (who calls each endpoint, its request,
335
+ response and errors, and the tables it touches) from `templates/api-diagram.html` into
336
+ `<bout dir>/api-diagram.html`. Redraw both after every rebound. Then serve the preview from the uncommitted working tree (for a native surface
292
337
  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):
338
+ diff --cached -- <files>`, and, for a dependency, the M17 checklist filled in per package):
294
339
  `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.
340
+ (127.0.0.1:4815; Swagger UI of the proposed spec, what changes against the base with the
341
+ endpoint diagram above it, the model diagram above the model diff, each code diff folded into a
342
+ disclosure, and a warning when a spec or model file changed after its diagram). Ask the judge with
343
+ `templates/escalation.md`: the URL, what changed, and your recommendation, with its context
344
+ per question, and log both (`judges-card.mjs log <pr> escalation <file> --context <file>`,
345
+ then `log <pr> answer` with the judge's words, or `page-answers`, §2.5). Then regenerate and
346
+ republish the chronicle (`/ring:play-by-play`, with `embed --contract`): the judge also
347
+ reviews the contract there, endpoint by endpoint (and, at a frontend venue, route by route
348
+ with `embed --routes`), and gives each one «Se aprueba» or «Se mejora».
299
349
  5. Until the judge answers, the contract zone is frozen; the wrestler works on other concepts or
300
350
  waits. Work that depends on the unapproved contract (code that needs its new package or its
301
351
  config) stays **uncommitted and out of the tree**: each later concept is kept as a patch under
@@ -320,22 +370,65 @@ a spec edit you anticipate is a new contract, and goes through steps 2–6 befor
320
370
  `<bout dir>/tools/`, not staged, so the judge reviews the contracts in one escalation and
321
371
  approves each one on its own. When nothing is left to draft, the wait is declared, not hidden:
322
372
  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
373
+ 6. **The verdicts first.** Before acting on the contract, and whenever you resume, list the
374
+ chronicle's `veredictos` with `ArtifactData` (`list`, the chronicle's `url`, `out_dir` into
375
+ `<bout dir>/tools/`) and run `judges-card.mjs page-verdicts <pr> <that dir>`. It logs each
376
+ verdict at the time the judge gave it, turns each «se mejora» into a contract finding (rebound
377
+ it to the wrestler like any finding) and into a point of the technical plan («Lo que pidió el
378
+ juez»), and says when every endpoint and route is approved with its current fingerprint. A
379
+ rewritten contract outdates the verdicts on what changed: regenerate and republish the
380
+ chronicle after every change to the contract, so the judge reviews what is there now. **Do not
381
+ ask for `approve-contract` until page-verdicts says every one is approved with its current
382
+ fingerprint.**
383
+ 7. **Approved**: only the judge's approval of the **concrete** staged contract counts: an
324
384
  approval "in concept", given before the files existed, does not. If the review found effects the
325
385
  judge did not see (a manifest entry, resources dropped from the build, a permission, a platform
326
386
  flag), they go to the judge first, listed in one message. Then
327
387
  `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
388
+ --judge-said "<the judge's own words about this concrete contract>"`: it hashes the staged
329
389
  content and writes the judge's decision line itself, with the quote, so an approval is never
330
390
  typed in the judge's name. Then the wrestler commits the contract in its own commit and
331
391
  implements it. `mask-guard` blocks a commit of a contract file whose
332
392
  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,
393
+ 8. **Rejected**: rebound the contract with the judge's reasons and go back to step 3.
394
+ 9. **A change to the contract you decide while a cornerman reviews it** (a lockfile to normalize,
335
395
  a config to fix) does not restart the review: it waits for the verdict, goes into the same
336
396
  rebound package (P6), and the rebounded contract gets a cornerman check of its own before the
337
397
  judge sees it.
338
398
 
399
+ ## 2c · The technical plan
400
+
401
+ `<bout dir>/plan-tecnico.md` is what the wrestler has to do for the deliverable: a title
402
+ (`# El plan del técnico · #<pr>`), `## <group>` and bullets
403
+ `- [<state>] **<title>** <detail> · origen: <ids>`, the states being pendiente · espera al juez ·
404
+ en curso · hecho, and a group whose name says «tickets» for what stays out of the delivery. The
405
+ origin cites findings (B1, F8…), decisions (D1…), questions (E1-3), verdicts or the sparring's
406
+ card.
407
+
408
+ - **Write it when the bout opens**, before the first wrestler brief. A bout from a sparring takes
409
+ its points from the card (the judge's validation, the rudo's shortcuts for this repo, «Not
410
+ verified yet», the pending items), from the contract review's findings, from your decisions and
411
+ from the escalation; the tickets the KO left out go to the tickets group. Every point says where
412
+ it comes from.
413
+ - **Never edit a state by hand**: `judges-card.mjs plan <pr> <n|title> <state> [--ref <sha|round-bell>]`
414
+ stamps the change in `plan.jsonl`; `plan <pr> add "<group>" "<title>" --origin <ids>` adds a
415
+ point, `plan <pr> list` prints it. When a round-bell closes a concept or the wrestler applies a
416
+ finding, mark the points it moves, with the round-bell or the sha as `--ref`. `page-verdicts`
417
+ adds each «se mejora» by itself.
418
+ - A bout does not close with a point of the plan outside «hecho» unless the judge sent it to a
419
+ ticket (move it to the tickets group with its id).
420
+
421
+ The chronicle shows the plan after «La entrevista» and before the rounds (`embed --plan`).
422
+
423
+ ## 2d · The chronicle during the preparation
424
+
425
+ The judge reviews the preparation in the chronicle, so keep it current: regenerate (`embed` with
426
+ every bout dir of the fight) and republish it after every change to the contract, after logging
427
+ the judge's answers and after logging the verdicts. In a fight with several repos (a back and a
428
+ front), each repo's referee keeps its own bout dir (its interview, verdicts, plan, diagram) and
429
+ logs there; whichever session regenerates the page passes every bout dir of the fight to `embed`
430
+ (the fight's JSON lists them in `bouts[].boutDir`), so the one page joins them all.
431
+
339
432
  ## 3 · Watching
340
433
 
341
434
  - `node "$RING/scripts/lookout.mjs" watch <bout dir>` with `run_in_background: true`. It exits
@@ -390,9 +483,10 @@ and runs the fixes through this same cycle (§2).
390
483
  checkouts), for the human to close and clean. Closing PRs and deleting worktrees and files is
391
484
  theirs.
392
485
  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.
486
+ whether Copilot reviewed the last commit, with its "El cierre en números" block
487
+ (`bout-stats.mjs`: numbers, charts, highlight reel and changelog). `close` is blocked while the chronicle is behind.
394
488
  11. When the human says the bout is over: `node "$RING/scripts/judges-card.mjs" close <pr>`. Its
395
- files stay as the record.
489
+ files stay as the record.
396
490
 
397
491
  ## 5 · `resume [<pr>]`
398
492
 
@@ -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._
@@ -22,7 +22,7 @@ expression.
22
22
  `if (existing) { …; return } create(...)`. Lookups that may legitimately find nothing return a
23
23
  list or a null object, not a hidden null from a finder that should have failed.
24
24
 
25
- **R4. Very few comments.** Only a non-obvious *why*, in one line. Remove comments that narrate
25
+ **R4. Very few comments.** Only a non-obvious _why_, in one line. Remove comments that narrate
26
26
  what or how, repeat the name, tell the history of the refactor, or describe another layer. Keep the
27
27
  comment that guards a measured incident ("a 400 from the provider does not burn the key, measured
28
28
  on <date>"): deleting it invites the bug back.
@@ -57,7 +57,8 @@ a large block becomes a helper or its own function.
57
57
  identical methods (merge them unless the difference means something to the caller). Dependencies
58
58
  point inward: the domain imports nothing from application or infrastructure; adapters implement
59
59
  the ports the consumer owns, translate foreign errors and types at the border, and live by
60
- technology.
60
+ technology. The same condition repeated across files is a finding even when every piece works on
61
+ its own: it lives in one place (a value object, a rule, a specification) and the rest call it.
61
62
 
62
63
  **R12. Every value object is a class**, with a private constructor, named static factories and
63
64
  methods with its behavior; never an interface plus a frozen object plus loose functions. A plain
@@ -71,6 +72,42 @@ existing value object when there is one. Constructors used for dependency inject
71
72
  methods, a named option, a value object or polymorphism. The same for optional callbacks whose mere
72
73
  presence changes the semantics: use a discriminated result.
73
74
 
75
+ **R15. Every new file earns its place with one business sentence**: in the language of the
76
+ domain, which fact of the customer it serves and what it will be used for next. A file without
77
+ that sentence does not enter.
78
+
79
+ **R16. The backend follows the business narrative, not the screen.** A field, filter or query that
80
+ exists only to paint a view, or only to validate before another action (a "preview"), is a
81
+ finding: the view composes what the business already exposes.
82
+
83
+ **R17. Every optional carries its reason.** Each new `?`, nullable or optional field in a DTO,
84
+ command, event or entity is justified in one sentence; otherwise it is required or has a default.
85
+ A union like `X | null` where null means "a different operation" becomes two commands.
86
+
87
+ **R18. An aggregate is born valid.** What gives it meaning exists from its factory; no aggregate
88
+ is created empty and completed later by setters or a second call.
89
+
90
+ **R19. Primitives do not cross boundaries.** Commands, events and ports carry value objects
91
+ (R12), not `string | null` or loose ids; the primitive is parsed once at the edge.
92
+
93
+ **R20. Before a new endpoint or command, list the existing ones that could do it** and why they do
94
+ not serve. A new route that duplicates an existing one under another verb (a PATCH where
95
+ `POST`/`DELETE` on the same resource already exists) is a finding.
96
+
97
+ **R21. Names come from the ubiquitous language and do not collide with another context.** Before
98
+ naming a module, aggregate or resource, search the repo: a name another bounded context already
99
+ owns (a `catalog` next to an existing `catalogs`) is renamed.
100
+
101
+ **R22. Design patterns where they fit, and only where they fit.** Conditionals repeated by type,
102
+ state or provider are replaced by the pattern that removes them (a chain of rules or a composed
103
+ specification, Strategy, polymorphism in the value object, Observer for effects between modules,
104
+ Factory Method for creation invariants, polymorphic errors instead of a map of codes; catalog:
105
+ https://refactoring.guru/design-patterns), citing the measured `file:line`. A pattern enters only
106
+ when it is simpler than the conditional it removes; a discarded pattern is written down with its
107
+ reason so it is not proposed again. Example: five chained `if`s assigning a number, with the
108
+ exclusivity rule repeated in three files, become one `AssignmentRule` per rule, reused by the
109
+ backfill.
110
+
74
111
  ## T · Tests as a contract
75
112
 
76
113
  **T1. In a refactor, no assertion of a test that exists in the base changes.** Moving or renaming a
@@ -142,6 +179,12 @@ purpose, short and clear, and are listed in the PR.
142
179
  **F12. A quiet linter is not an approval.** No orphan entries in baselines or allowlists, and the
143
180
  gates run clean against the stack base and against the default branch.
144
181
 
182
+ **F13. A provider's hard limits count against the base, not the diff.** On a new infrastructure
183
+ resource (metric filters, rules, indexes, queues, alarms), count how many the base already has
184
+ against the provider's quota before approving. _Example: AWS allows 100 metric filters per log
185
+ group; main already had 100 and the PR added 2, so the alarms would not have been created in
186
+ production._
187
+
145
188
  ## P · Review process
146
189
 
147
190
  **P1. One PR per category.** Business logic and its tests apart from harness, mocks, tooling,
@@ -151,7 +194,7 @@ skills and docs. The deliverable PR carries only code and tests.
151
194
  practice is stopped in time, with the approved pattern already applied.
152
195
 
153
196
  **P3. If the human dislikes the class design, a structural pause.** First ask whether the
154
- objection is to the *solution* (the behavior) or to the *class design*; a validated solution is not
197
+ objection is to the _solution_ (the behavior) or to the _class design_; a validated solution is not
155
198
  redesigned. On a design objection, finish what is mechanical, agree a target architecture with the
156
199
  human and resume with a single sample concept.
157
200
 
@@ -160,7 +203,10 @@ per concept by a read-only reviewer on a fixed range of commits.
160
203
 
161
204
  **P5. File-by-file design review in batches** of at most 20 files through 8 lenses: layer and
162
205
  dependencies, coupling, duplication, SOLID, domain model, errors (fail open or closed),
163
- expressiveness, production risk.
206
+ expressiveness, production risk. Each new file brings its business sentence (R15) and repeated
207
+ conditionals bring their pattern proposal or the reason none fits (R22). **It is mandatory before
208
+ the first commit when the PR creates a module or an aggregate**, not only when the change is
209
+ large or the human asks: a design review that arrives after the cleanup started arrives late.
164
210
 
165
211
  **P6. One rebound package per review**: `file:line — rule — what is wrong — how it must be`,
166
212
  blockers first (base assertion changed, event renamed, behavior changed). What is not applied is
@@ -189,6 +235,20 @@ battery runs once when everything converged.** The targeted reproduction (a dire
189
235
  one e2e spec, minutes) must be green on the exact tip that ships before the finding's thread is
190
236
  replied to as fixed or resolved; the full validation (hours) runs once, on the final tip.
191
237
 
238
+ **P14. The host's rules are not the referee's decisions.** When the host fixes a pattern (a
239
+ repository only through specifications, a layer that may not import another), a deviation is a
240
+ cornerman BLOCKER or goes to the hard list; under `delegation: "referee"` the referee may not
241
+ approve it as a design doubt.
242
+
243
+ **P15. A PR that changes how a flow works today explains it in plain words before the code**:
244
+ what happens today, what will happen, and who notices. It goes in the wrestler's brief ("Flow
245
+ change" line of `templates/wrestler-brief.md`) and in the round-bell body (`round-bell.mjs open …
246
+ --flow-change "<today …; after …; who notices …>"`, rendered under "What changes for whom").
247
+
248
+ **P16. Read the execution path before claiming "today it goes through X".** Where a value is
249
+ stored does not say which path uses it; a claim about current behavior cites the call chain that
250
+ runs, not the column or the config that exists.
251
+
192
252
  ## Who decides
193
253
 
194
254
  The host's `delegation` (`.claude/ring.json`) sets who decides design doubts. With `"referee"`
@@ -208,6 +268,7 @@ bout moving; with `"human"` the referee escalates both lists, as before.
208
268
  writes, data deletion).
209
269
  - Anything `mask-guard` blocks.
210
270
  - A direct contradiction between two instructions of the host repo: it is written down and waits.
271
+ - A deviation from a pattern the host's own rules fix (P14).
211
272
 
212
273
  ### The referee's under `delegation: "referee"`
213
274