@thebattlearena/corner 0.10.1-beta.81 → 0.10.1-beta.82

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.
package/README.md CHANGED
@@ -439,7 +439,7 @@ accept|reject --why "<lo que dijo el humano>"`.
439
439
 
440
440
  **Revisión humana por bloques (PR Review Lab)**: cada punto del plan técnico es un bloque (`B<n>` es
441
441
  el punto n). `judges-card.mjs plan <pr> estimate <n> --minutes <m> [--priority 1-4]` le pone estimado y
442
- prioridad. Al pasar a «en curso» se anotan el inicio y el autor (el `user.email` de git del worktree),
442
+ prioridad (sin ellos un bloque no arranca; también van en el mismo comando: `plan <pr> <n> "en curso" --minutes <m> --priority <p>`). Al pasar a «en curso» se anotan el inicio y el autor (el `user.email` de git del worktree),
443
443
  y al pasar a «espera al juez» el fin y el rango (`--range A..B` lo fija a mano); todo va en
444
444
  `plan.jsonl`, que es lo que lee el modo «Pelea del ring» de PR Review Lab. Cuando `humanReviewEvery`
445
445
  bloques (2 por default, `null` lo apaga) esperan al juez, suena la campana en PR Review Lab y
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@thebattlearena/corner",
3
- "version": "0.10.1-beta.81",
3
+ "version": "0.10.1-beta.82",
4
4
  "description": "Corner: tu esquina de Battle Arena en Claude Code (el plugin `battlearena`). Hooks de tracking, objetivo del turno, plan de batalla /battlearena:conquer, Ringside (el panel de tu día), el ring y la radio. Zero-dependency.",
5
5
  "type": "module",
6
6
  "bin": {
@@ -610,10 +610,25 @@ function plan([pr, target, ...rest]) {
610
610
  const [state] = rest;
611
611
  if (!target || !state)
612
612
  throw new Error(
613
- 'usage: plan <pr> <n|title> <pendiente|espera al juez|en curso|hecho> [--ref <sha|round-bell>] [--range A..B] | estimate <n> --minutes <m> [--priority 1-4] | add … | list',
613
+ 'usage: plan <pr> <n|title> <pendiente|espera al juez|en curso|hecho> [--ref <sha|round-bell>] [--range A..B] [--minutes <m> --priority <1-4>] | estimate <n> --minutes <m> [--priority 1-4] | add … | list',
614
614
  );
615
+ const { config } = loadConfig(projectDir(), {
616
+ contract: false,
617
+ venue: readState(dir).venue ?? DEFAULT_VENUE,
618
+ });
619
+ const inline = [value('--minutes'), value('--priority')].some(
620
+ (given) => given != null,
621
+ );
615
622
  const changed = setPlanState(dir, target, state, value('--ref'), undefined, {
616
623
  range: value('--range'),
624
+ estimate: inline
625
+ ? estimateFacts({
626
+ priority: value('--priority'),
627
+ minutes: value('--minutes'),
628
+ })
629
+ : null,
630
+ // Under the human review by blocks a block starts with its estimate and priority.
631
+ requireEstimate: Boolean(config.humanReviewEvery),
617
632
  });
618
633
  console.log(
619
634
  `${changed.n}. ${changed.title}: ${changed.from} → ${changed.to} at ${localStamp(new Date())}${changed.facts.range && changed.to === 'espera al juez' ? ` · range ${changed.facts.range}` : ''}`,
@@ -70,6 +70,21 @@ export function factsForState(previous, to, { at, head, email, range }) {
70
70
  return {};
71
71
  }
72
72
 
73
+ /**
74
+ * Why a block may not start: under the human review by blocks, a block enters «en curso» only with
75
+ * its estimate and priority (PR Review Lab orders by one and calibrates the other against the real
76
+ * time per person). Null when it may. PURE.
77
+ */
78
+ export function startRefusal(facts, to, n) {
79
+ if (to !== 'en curso') return null;
80
+ const missing = [
81
+ facts.estimateMin == null && '--minutes <m>',
82
+ facts.priority == null && '--priority <1-4>',
83
+ ].filter(Boolean);
84
+ if (!missing.length) return null;
85
+ return `B${n} has no ${facts.estimateMin == null ? 'estimate' : 'priority'} yet: a block starts with its estimate and priority (the human review by blocks). Add ${missing.join(' ')} to this command, or run plan <pr> estimate ${n} ${missing.join(' ')} first`;
86
+ }
87
+
73
88
  /** Checks an estimate: priority 1–4 and whole minutes. PURE. */
74
89
  export function estimateFacts({ priority, minutes }) {
75
90
  const facts = {};
@@ -12,7 +12,12 @@ import { join } from 'node:path';
12
12
 
13
13
  import { readState } from './config.mjs';
14
14
  import { scriptJson } from './contract-endpoints.mjs';
15
- import { authorEmail, blockFacts, factsForState } from './review-blocks.mjs';
15
+ import {
16
+ authorEmail,
17
+ blockFacts,
18
+ factsForState,
19
+ startRefusal,
20
+ } from './review-blocks.mjs';
16
21
  import {
17
22
  parsePlan,
18
23
  parsePlanLog,
@@ -34,7 +39,7 @@ export function setPlanState(
34
39
  state,
35
40
  ref = null,
36
41
  at = new Date().toISOString(),
37
- { range = null, estimate = null } = {},
42
+ { range = null, estimate = null, requireEstimate = false } = {},
38
43
  ) {
39
44
  const file = join(dir, PLAN);
40
45
  const markdown = read(file);
@@ -58,6 +63,11 @@ export function setPlanState(
58
63
  range,
59
64
  })),
60
65
  };
66
+ const refusal =
67
+ requireEstimate && next !== item.state
68
+ ? startRefusal(facts, next, item.n)
69
+ : null;
70
+ if (refusal) throw new Error(refusal);
61
71
  writeFileSync(file, withPlanState(markdown, item, next));
62
72
  appendFileSync(
63
73
  join(dir, PLAN_LOG),
@@ -2,6 +2,7 @@
2
2
  name: referee
3
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
+ effort: high
5
6
  ---
6
7
 
7
8
  # referee
@@ -212,7 +213,8 @@ way out, and it refuses without a KO. Then the bout runs as always, with three i
212
213
  `brief.mjs` briefs no wrestler. That is what a compaction or a new session resumes from.
213
214
  **Human review by blocks**: each point of the technical plan is a block. Give each its estimate
214
215
  and priority when the plan is made (`judges-card.mjs plan <pr> estimate <n> --minutes <m>
215
- --priority <1-4>`), move it to «en curso» when its wrestler starts and to «espera al juez» when
216
+ --priority <1-4>`; the script refuses to start a block without both, or pass them with the
217
+ start: `plan <pr> <n> "en curso" --minutes <m> --priority <p>`), move it to «en curso» when its wrestler starts and to «espera al juez» when
216
218
  its review passes (that stamps the author, the times and the range PR Review Lab reviews). Every
217
219
  `humanReviewEvery` blocks waiting, brief.mjs stops: the human reviews that cut in PR Review Lab
218
220
  (bout mode) and signs a verdict with a Tape. The next brief applies it: a pass closes the
@@ -2,6 +2,7 @@
2
2
  name: sparring
3
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 bell <slug>`, `/ring:sparring mask <slug> [kept|lost]`, `/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> | bell <slug> | mask <slug> [kept|lost] | list'
5
+ effort: high
5
6
  ---
6
7
 
7
8
  # sparring