@golden-frijoles/kit 0.44.0 → 1.0.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 (39) hide show
  1. package/dist/build-order.mjs +6 -6
  2. package/dist/build-state.mjs +2 -3
  3. package/dist/cold-read.mjs +1 -1
  4. package/dist/cold-read.prompt.md +3 -1
  5. package/dist/cross-panel.prompt.md +2 -2
  6. package/dist/cross-review.mjs +1 -1
  7. package/dist/cross-review.prompt.md +1 -1
  8. package/dist/doc-format.enforced.json +1 -1
  9. package/dist/doc-format.mjs +3 -3
  10. package/dist/doc-hygiene.mjs +3 -3
  11. package/dist/emit-epic-kickoff.mjs +4 -4
  12. package/dist/emit-kickoff.mjs +4 -4
  13. package/dist/init.mjs +1 -1
  14. package/dist/intent-match.mjs +6 -6
  15. package/dist/intent-reader.mjs +2 -2
  16. package/dist/lib/board-text.mjs +2 -2
  17. package/dist/lib/config-registry.mjs +5 -5
  18. package/dist/lib/epic-kickoff.mjs +5 -5
  19. package/dist/lib/golden-onboarding.mjs +1 -1
  20. package/dist/lib/kickoff-cli.mjs +3 -3
  21. package/dist/lib/result-dates.mjs +2 -2
  22. package/dist/lib/roadmap-contract.mjs +3 -3
  23. package/dist/lib/stage.mjs +10 -0
  24. package/dist/lib/strategy-files.mjs +1 -1
  25. package/dist/lib/surface.mjs +1 -1
  26. package/dist/quote.mjs +1 -1
  27. package/dist/review-route.mjs +3 -3
  28. package/dist/roadmap-extract.mjs +1 -1
  29. package/dist/routines/README.md +4 -4
  30. package/dist/routines/ops-nightly.prompt.md +22 -18
  31. package/dist/routines/pmo-report.prompt.md +3 -3
  32. package/dist/routines/pr-review.prompt.md +1 -1
  33. package/dist/routines/roadmap-hygiene.prompt.md +13 -11
  34. package/dist/routines/weekly-recap.prompt.md +2 -2
  35. package/dist/skeleton/Roadmap/00-ideas/README.md +6 -6
  36. package/dist/skeleton/Roadmap/LEARNINGS.md +1 -1
  37. package/dist/skeleton/Roadmap/WAYS-OF-WORKING.md +6 -6
  38. package/dist/templates/kickoff.md +1 -1
  39. package/package.json +1 -1
@@ -18,7 +18,7 @@ import { execFileSync } from 'node:child_process';
18
18
  import { readFileSync, writeFileSync, existsSync } from 'node:fs';
19
19
  import { fileURLToPath } from 'node:url';
20
20
  import { dirname, join } from 'node:path';
21
- import { STAGES, groupByStage } from './lib/stage.mjs';
21
+ import { STAGES, groupByStage, stageWord } from './lib/stage.mjs';
22
22
  import { projectRoot } from './lib/project-root.mjs';
23
23
 
24
24
  const __dirname = dirname(fileURLToPath(import.meta.url));
@@ -78,7 +78,7 @@ function render(rows) {
78
78
  out.push('# Build order — the six stages');
79
79
  out.push('');
80
80
  out.push(`> **Generated ${now} — do not hand-edit.** One stage per initiative, decided in one place`);
81
- out.push('> (`scripts/lib/stage.mjs`): To groom · Grooming · Ready to build · Building · QA · Shipped.');
81
+ out.push(`> (\`scripts/lib/stage.mjs\`): ${STAGES.map(stageWord).join(' · ')}.`);
82
82
  if (LIVE) {
83
83
  out.push('> **Live:** Building and QA come from git and GitHub as of this run. Printed, never written.');
84
84
  } else {
@@ -89,7 +89,7 @@ function render(rows) {
89
89
  for (const stage of STAGES) {
90
90
  const list = columns[stage];
91
91
  if (LIVE_ONLY.has(stage) && !LIVE) {
92
- out.push(`## ${stage} — live only`);
92
+ out.push(`## ${stageWord(stage)} — live only`);
93
93
  out.push('');
94
94
  out.push(
95
95
  `_${STAGE_NOTE[stage]}. Not in this committed file: \`node scripts/build-order.mjs --live\` or the Hub board._`
@@ -97,7 +97,7 @@ function render(rows) {
97
97
  out.push('');
98
98
  continue;
99
99
  }
100
- out.push(`## ${stage} (${list.length})`);
100
+ out.push(`## ${stageWord(stage)} (${list.length})`);
101
101
  out.push('');
102
102
  out.push(`_${STAGE_NOTE[stage]}._`);
103
103
  out.push('');
@@ -126,7 +126,7 @@ if (unfunded.length) {
126
126
  }
127
127
 
128
128
  // Scaffolded ⇒ funded (fund-at-approval D8). A live bet — an epic scaffolded or in progress, or a queued seed — must
129
- // name the cycle that paid for it, and that cycle must be a file in Roadmap/bets/. `groom` funds a bet at its approval
129
+ // name the cycle that paid for it, and that cycle must be a file in Roadmap/bets/. `refine` funds a bet at its approval
130
130
  // gate (`fund.mjs`), in the same commit as the scaffold, so this only fails on work that skipped the gate.
131
131
  const BETS = join(REPO, 'Roadmap', 'bets');
132
132
  const cycleOf = (v) => String(v).replace(/^Roadmap\/bets\//, '').replace(/\.md$/, '');
@@ -139,7 +139,7 @@ const unpaid = liveBets.filter((r) => !paidBy(r));
139
139
  if (unpaid.length) {
140
140
  console.error('Live bets with no funding record (`underwritten_by:` must name a Roadmap/bets/<cycle>.md):');
141
141
  for (const r of unpaid) console.error(` - ${r.doc_link}${r.underwritten_by ? ` — no Roadmap/bets/${cycleOf(r.underwritten_by)}.md` : ''}`);
142
- console.error('Fund it at the approval gate: node "$GROOM/fund.mjs" --slug <slug> --displaced "<…>" --next | --after <slug>');
142
+ console.error('Fund it at the approval gate: node "$REFINE/fund.mjs" --slug <slug> --displaced "<…>" --next | --after <slug>');
143
143
  process.exit(1);
144
144
  }
145
145
 
@@ -61,7 +61,7 @@ import { parseJournal, JOURNAL_BRANCH, JOURNAL_PATH } from './lib/session-journa
61
61
  import { branchCandidates, parseBranch } from './lib/work-branch.mjs';
62
62
  import { buildRows } from './roadmap-extract.mjs';
63
63
  import { gatherFacts } from './lib/stage-facts.mjs';
64
- import { STAGES, groupByStage } from './lib/stage.mjs';
64
+ import { STAGES, groupByStage, stageWord } from './lib/stage.mjs';
65
65
  import { getKey } from './lib/config.mjs';
66
66
 
67
67
  const __dirname = dirname(fileURLToPath(import.meta.url));
@@ -927,14 +927,13 @@ function stageParts(state) {
927
927
  }
928
928
 
929
929
  // build-view-upgrade D3 — the stage as a track, in lib/stage.mjs's words. `To groom` shows only when it is the stage.
930
- const TRACK_WORDS = { 'To groom': 'Backlog', 'Ready to build': 'Ready' };
931
930
  export const TRACK_MARK = '◉';
932
931
 
933
932
  /** `Grooming ─ Ready ─ ◉ Building ─ QA ─ Shipped` for a stage; `◉ Locking` in Building's place while locking. */
934
933
  export function stageTrack(stage, locking = false) {
935
934
  return STAGES.filter((s) => s !== 'To groom' || stage === 'To groom')
936
935
  .map((s) => {
937
- const word = s === 'Building' && locking ? 'Locking' : (TRACK_WORDS[s] ?? s);
936
+ const word = s === 'Building' && locking ? 'Locking' : stageWord(s);
938
937
  return s === stage ? `${TRACK_MARK} ${word}` : word;
939
938
  })
940
939
  .join(' ─ ');
@@ -1,5 +1,5 @@
1
1
  #!/usr/bin/env node
2
- // cold-read.mjs — the deterministic half of the `cold-read` skill: the brief, the run, the seal and the compare
2
+ // cold-read.mjs — the deterministic half of the cold-read chapter of the `strategy` skill: the brief, the run, the seal and the compare
3
3
  // (coaches-v2 D2–D4).
4
4
  //
5
5
  // node scripts/cold-read.mjs brief [--out <file>] print the agent's brief (the exclusion list is in it); --out
@@ -8,7 +8,9 @@ the end, says where it goes.
8
8
  Skip these, and anything whose purpose is positioning, brand, messaging, landing copy or a strategy method:
9
9
 
10
10
  - `Roadmap/00-strategy/` (all of it): the makers' own strategy and any earlier cold read
11
- - any `pmf-narrative`, `north-star`, `risk-validation` or `cold-read` skill folder: the coaching methods
11
+ - the `strategy` skill folder (and, on an older install, any `pmf-narrative`, `north-star`, `risk-validation` or
12
+ `cold-read` skill folder), and the `refine` skill's `references/coaching.md` and `references/strategy.md`: the
13
+ coaching methods
12
14
  - `references/`, `brand/`, landing-page and marketing copy, positioning or messaging modules
13
15
  - `.env*`, secrets, `node_modules/`, `.git/`
14
16
 
@@ -24,7 +24,7 @@
24
24
  ---
25
25
 
26
26
  You are an **advisory second-opinion reviewer** from a different model family than the agent that is
27
- grooming this plan. You are reviewing a **proposed plan** — a scope/seed doc for a feature, bug, spike,
27
+ refining this plan. You are reviewing a **proposed plan** — a scope/seed doc for a feature, bug, spike,
28
28
  or chore — **before** it gets sliced into an epic and built. Your job is to catch the architecture blind
29
29
  spots a same-family planner would miss, while the plan is still cheap to change.
30
30
 
@@ -80,7 +80,7 @@ confirm (or kill) your "already modeled / not modeled" assumption.
80
80
  ## LENS: architect-pragmatist
81
81
  You are the **ship-it pragmatist.** Your question on every line: *what is the thinnest thing that actually
82
82
  works and ships, and is this plan over-built for v1?* Push hard on:
83
- - **Can we already do this today?** (LEARNINGS / the groom skill's "can we already do this?" stage.) Sort
83
+ - **Can we already do this today?** (LEARNINGS / the refine skill's "can we already do this?" stage.) Sort
84
84
  the ask into *already-possible* (existing features + the right messaging/positioning — no build),
85
85
  *light-enhancement* (a small story or copy/config change on an existing feature), or *genuinely-new*.
86
86
  If buckets 1–2 hit the outcome, say so loudly — that's the win. A plan that builds net-new when copy +
@@ -109,7 +109,7 @@ export function promptPathFor(lens) {
109
109
 
110
110
  const BANNER =
111
111
  '> **Cross-agent review — every finding is fixed, or answered on this PR, before merge. This does not authorize one.** ' +
112
- 'CI and the risk-tier merge rule remain the only merge authority. A fresh `pr-reviewer` pass covers context independence; ' +
112
+ 'CI and the risk-tier merge rule remain the only merge authority. A fresh `verifier` pass covers context independence; ' +
113
113
  'this is the family-independence pass: one single-pass read by a model family that did not build the diff.';
114
114
 
115
115
  const HELP = `cross-review.mjs — the cross-agent judgment-layer review for a PR diff.
@@ -2,7 +2,7 @@
2
2
  cross-review.prompt.md — the ONE shared reviewer prompt, read by BOTH readers.
3
3
 
4
4
  • `scripts/cross-review.mjs` (the external CLI pass) sends everything below the first `---`.
5
- • The fresh `pr-reviewer` subagent reads *Shared bar*, *What to check* and *Project rules*, and ignores
5
+ • The fresh `verifier` subagent reads *Shared bar*, *What to check* and *Project rules*, and ignores
6
6
  *CLI reader only*.
7
7
 
8
8
  One prompt, two independent readers, no drift: that is the whole design (ways-of-work-lean-pass D8).
@@ -1,4 +1,4 @@
1
1
  {
2
- "$comment": "Which Roadmap docs `node scripts/doc-format.mjs --check` FAILS on (everything else is reported, not enforced). A trailing \"/\" makes a prefix. A new project enforces all of Roadmap/ — nothing predates the groom templates. A project adopting this late lists files as each area is swept.",
2
+ "$comment": "Which Roadmap docs `node scripts/doc-format.mjs --check` FAILS on (everything else is reported, not enforced). A trailing \"/\" makes a prefix. A new project enforces all of Roadmap/ — nothing predates the refine templates. A project adopting this late lists files as each area is swept.",
3
3
  "enforced": ["Roadmap/"]
4
4
  }
@@ -1,7 +1,7 @@
1
1
  #!/usr/bin/env node
2
2
  // doc-format.mjs — check Roadmap/ epic docs (README.md, sprint-N.md, RETROSPECTIVE.md) against the
3
- // canonical shape: the `groom` plugin's scaffolding templates (golden-frijoles/skills,
4
- // skills/groom/templates/) — proven canonical in the project this was ported from by a zero-drift
3
+ // canonical shape: the `refine` plugin's scaffolding templates (golden-frijoles/skills,
4
+ // skills/refine/templates/) — proven canonical in the project this was ported from by a zero-drift
5
5
  // control group (81 seeds, one authoring path, identical shape; epic READMEs drift because they get
6
6
  // hand-edited after scaffolding, away from the template). dobby-foundation's CI renders a scaffolded
7
7
  // epic and runs this checker over it, so the producer validates its own templates.
@@ -392,7 +392,7 @@ export function checkRetrospective(content) {
392
392
  // long as the italic markup actually closes somewhere (immediately after the date, or at the end
393
393
  // of the line). Require: starts with the italic-open + "Closed:" + a real date, and a closing "_"
394
394
  // appears somewhere after that.
395
- // The canonical groom scaffold intentionally has no close date yet. Accept its literal sentinel:
395
+ // The canonical refine scaffold intentionally has no close date yet. Accept its literal sentinel:
396
396
  // treating an unbuilt epic as closed would make roadmap-to-notion derive a false Shipped state.
397
397
  const isScaffoldPlaceholder = trimmed === '_Closed: <date>_';
398
398
  const startsWithDate = /^_Closed:\s*\d{4}-\d{2}-\d{2}/.test(trimmed);
@@ -3,7 +3,7 @@
3
3
  // candidates in LEARNINGS.md + the README poster. ADVISORY ONLY: this script never edits any
4
4
  // Roadmap doc — it only reads them and (unless --check) writes ONE new dated report file under
5
5
  // Roadmap/00-ideas/. Every flagged candidate is a hint for a human/model to verify, not a verdict —
6
- // see the `doc-hygiene` skill (`golden-frijoles` plugin, golden-frijoles marketplace) for the
6
+ // see Step 4 of scripts/routines/roadmap-hygiene.prompt.md for the
7
7
  // judgment pass this script's output feeds.
8
8
  //
9
9
  // node scripts/doc-hygiene.mjs # print the report + write Roadmap/00-ideas/DOC-HYGIENE-REPORT-<date>.md
@@ -211,12 +211,12 @@ function buildReport() {
211
211
  const date = new Date().toISOString().slice(0, 10);
212
212
  const lines = [];
213
213
  lines.push(
214
- `<!-- Advisory artifact from the doc-hygiene skill (scripts/doc-hygiene.mjs). Findings are proposals`,
214
+ `<!-- Advisory artifact from the doc-hygiene pass (the roadmap-hygiene routine, step 4) (scripts/doc-hygiene.mjs). Findings are proposals`,
215
215
  ` only — no LEARNINGS.md/README.md content was changed by this script. -->`,
216
216
  '',
217
217
  `# Doc hygiene report — ${date}`,
218
218
  '',
219
- '🧹 **doc-hygiene skill.** Advisory only — review by hand; nothing here gates or auto-edits.',
219
+ '🧹 **doc-hygiene pass (the roadmap-hygiene routine, step 4).** Advisory only — review by hand; nothing here gates or auto-edits.',
220
220
  '',
221
221
  '## Always-read set size',
222
222
  ''
@@ -1,5 +1,5 @@
1
1
  #!/usr/bin/env node
2
- // emit-epic-kickoff.mjs — print the finished, paste-ready EPIC-MODE kickoff for a groomed epic.
2
+ // emit-epic-kickoff.mjs — print the finished, paste-ready EPIC-MODE kickoff for a refined epic.
3
3
  //
4
4
  // ── Why this exists ──────────────────────────────────────────────────────────────────────────────────
5
5
  // Epic mode — one orchestrated run across a whole epic, sprint files as internal integration boundaries
@@ -28,12 +28,12 @@
28
28
  // npx -y @golden-frijoles/kit emit-epic-kickoff --list [--repo-root <path>] # the epics a kickoff can start
29
29
  // # (live-build-view S2.4): `scaffolded` or `in-progress`, build order first
30
30
  //
31
- // The kickoff's one home is `/build <slug>` (the plugin's mod runs groom's vendored copy of THIS file and fills the
31
+ // The kickoff's one home is `/build <slug>` (the plugin's mod runs refine's vendored copy of THIS file and fills the
32
32
  // prompt with its output); hosts without the mod run it through the kit. It is never saved to a file — the epic docs
33
33
  // are the state, regenerate it.
34
34
  //
35
35
  // One source, three homes (kickoff-generator-path C1): this file ships in the kit, a project spawned from the template
36
- // carries it in `scripts/`, and the groom skill holds a byte-identical copy in `vendor/` (render-hook-vendor.mjs).
36
+ // carries it in `scripts/`, and the refine skill holds a byte-identical copy in `vendor/` (render-hook-vendor.mjs).
37
37
 
38
38
  import { readFileSync, readdirSync, existsSync, realpathSync } from 'node:fs';
39
39
  import { fileURLToPath } from 'node:url';
@@ -108,7 +108,7 @@ export function listEpics(repoRoot, { read = (p) => readFileSync(p, 'utf8'), exi
108
108
 
109
109
  /** The list as printed: one epic per line, the slug first so it can be typed after `/build `. */
110
110
  export function formatEpicList(epics) {
111
- if (!epics.length) return 'No scaffolded or in-progress epics under Roadmap/ — groom one first.';
111
+ if (!epics.length) return 'No scaffolded or in-progress epics under Roadmap/ — refine one first.';
112
112
  return epics.map((e) => `${e.slug} ${e.title} (${e.status}${e.build_order !== null ? `, #${e.build_order}` : ''})`).join('\n');
113
113
  }
114
114
 
@@ -1,6 +1,6 @@
1
1
  #!/usr/bin/env node
2
- // emit-kickoff.mjs — print the finished, paste-ready Stage-8 Claude Code kickoff for a groomed
3
- // sprint. Planning-only helper for the `groom` skill (Stage 8). The invariant preamble (the
2
+ // emit-kickoff.mjs — print the finished, paste-ready Stage-8 Claude Code kickoff for a refined
3
+ // sprint. Planning-only helper for the `refine` skill (Stage 8). The invariant preamble (the
4
4
  // orientation reads, plan-mode, escalate-don't-guess triggers, etc.) lives in
5
5
  // `templates/kickoff.md` — this script's whole point is that boilerplate stops being retyped by
6
6
  // hand per sprint; only the sprint-specific delta (epic title, sprint number + its own title,
@@ -10,7 +10,7 @@
10
10
  // npx -y @golden-frijoles/kit emit-kickoff --epic <slug> --sprint N [--repo-root <path>]
11
11
  //
12
12
  // One source, three homes (kickoff-generator-path C1): this file ships in the kit, a project spawned from the template
13
- // carries it in `scripts/`, and the groom skill holds a byte-identical copy in `vendor/` (render-hook-vendor.mjs).
13
+ // carries it in `scripts/`, and the refine skill holds a byte-identical copy in `vendor/` (render-hook-vendor.mjs).
14
14
  // The template is read from this file's own directory, which is the kit root in all three.
15
15
  //
16
16
  // Resolves the epic dir by SEARCHING Roadmap/*/<slug>/ under the project root (lib/kickoff-cli.mjs) — the
@@ -19,7 +19,7 @@
19
19
  // headings), then substitutes into templates/kickoff.md and prints the result to stdout.
20
20
  //
21
21
  // It does NOT write any file — this is a read + print tool, same "advisory, editorial control
22
- // stays with the caller" stance as the other groom/cross-agent scripts in this repo.
22
+ // stays with the caller" stance as the other refine/cross-agent scripts in this repo.
23
23
 
24
24
  import { readFileSync, existsSync, realpathSync } from 'node:fs';
25
25
  import { fileURLToPath } from 'node:url';
package/dist/init.mjs CHANGED
@@ -4,7 +4,7 @@
4
4
  // node scripts/init.mjs # write the skeleton into projectRoot(); never overwrite a file
5
5
  //
6
6
  // ── Why ───────────────────────────────────────────────────────────────────────────────────────
7
- // A stranger who pastes the install prompt into an EXISTING repo gets the plugin, but `groom` needs
7
+ // A stranger who pastes the install prompt into an EXISTING repo gets the plugin, but `refine` needs
8
8
  // somewhere to write on day one — `Roadmap/README.md`, `WAYS-OF-WORKING.md`, `LEARNINGS.md`, and the
9
9
  // `00-ideas/` funnel. `frijoles-kit init` is that one step: it writes the same skeleton a project spawned
10
10
  // from `template/` already has, into any repo, copied or installed (D2's two roots).
@@ -1,5 +1,5 @@
1
1
  #!/usr/bin/env node
2
- // intent-match.mjs — how well a groomed pitch captured the ask it was written from (intent-match S1, D9–D15).
2
+ // intent-match.mjs — how well a refined pitch captured the ask it was written from (intent-match S1, D9–D15).
3
3
  //
4
4
  // node scripts/intent-match.mjs <seed.md> print the score, the band and a route for each gap
5
5
  // node scripts/intent-match.mjs <seed.md> --json the same, as JSON on stdout (the report goes to stderr)
@@ -7,27 +7,27 @@
7
7
  // node scripts/intent-match.mjs <seed.md> --no-route skip the second call that routes each gap to an artifact
8
8
  //
9
9
  // ── ADVISORY, NEVER A GATE (D1) ──────────────────────────────────────────────────────────────────────────────
10
- // The score can add a step to a groom; it never blocks a scaffold and never removes the product owner's approval.
10
+ // The score can add a step to a refinement; it never blocks a scaffold and never removes the product owner's approval.
11
11
  // Nothing reads the exit code as a verdict: 0 means "scored", 2 means "could not look", 1 means a usage error.
12
12
  //
13
13
  // ── ONE STATISTIC PER QUESTION (D2, D11) ─────────────────────────────────────────────────────────────────────
14
14
  // Each signal is one number over Jev Noul/Score answers, so a later calibration can fit weights and bands without
15
15
  // re-asking anything (the jev-reanchor spike's lesson). Coverage in = mean P(true) over the ask's claims; coverage
16
16
  // out = mean P(true) over the pitch's acceptance criteria; clarity = mean Score ÷ 3 over the same criteria;
17
- // teach-back = the product owner's answer to groom's Stage 1 mirror (yes 1 · partly 0.5 · no 0). The total is
17
+ // teach-back = the product owner's answer to refine's Stage 1 mirror (yes 1 · partly 0.5 · no 0). The total is
18
18
  // 100 × the equal-weight mean of the signals PRESENT, and it always says "uncalibrated" and which signals it used:
19
19
  // the 80 / 60 bands are placeholders until `intent-outcomes` has twenty answered epics to fit them against.
20
20
  //
21
21
  // ── THREE STATES, NEVER TWO ──────────────────────────────────────────────────────────────────────────────────
22
22
  // No key, `jev.egress` not `true`, a state over Jev's budget, a timeout or a malformed answer all print "could not
23
- // look" and NO number. A partial score dressed as a whole one is worse than none: it is the number a groom would
23
+ // look" and NO number. A partial score dressed as a whole one is worse than none: it is the number a refinement would
24
24
  // quote. The pitch is never truncated to fit — `askJev` refuses an over-budget state, and so do we.
25
25
  //
26
26
  // ── NOT A JEV RAIL (C2) ──────────────────────────────────────────────────────────────────────────────────────
27
27
  // A rail (`review`, `prose`) is a guard with a regex to fall back to and an off/shadow/jev switch. This decides
28
28
  // nothing and has no fallback, so it only reads `jev.egress` and `jev.model`: egress must be an explicit `true`.
29
29
  //
30
- // The seed format this parses is the contract in the epic README's D9 (and groom's `templates/scope-seed.md`):
30
+ // The seed format this parses is the contract in the epic README's D9 (and refine's `templates/scope-seed.md`):
31
31
  // `## The ask, as given` → `### Claims` (a numbered list) → `**Teach-back:** yes | partly | no`, and the list under
32
32
  // the first `## Acceptance…` heading. Zero deps — Node 18+.
33
33
 
@@ -60,7 +60,7 @@ export const BANDS = Object.freeze([
60
60
  { min: 0, label: 'sketch or spike first' },
61
61
  ]);
62
62
 
63
- /** The artifacts a gap can be routed to (D14) — the visuals rule (groom Stage 4.6) draws from the same words. */
63
+ /** The artifacts a gap can be routed to (D14) — the visuals rule (refine Stage 4.6) draws from the same words. */
64
64
  export const ROUTES = Object.freeze({
65
65
  copy_deck: 'copy deck',
66
66
  wireframe: 'wireframe',
@@ -13,7 +13,7 @@
13
13
  // Any failure — no reader CLI, a capped or logged-out CLI, a timeout, an empty or unstructured reply, Jev unable to
14
14
  // look — prints exactly ONE line, `reader skipped: <why>`, and exits 0. There is no retry and no second family after
15
15
  // one has been asked: free-tier CLIs can be finicky, and three reads of every pitch cost more time than they return
16
- // (the product owner's call at grooming). Exit 1 is kept for a usage error only.
16
+ // (the product owner's call at refining). Exit 1 is kept for a usage error only.
17
17
  //
18
18
  // ── NEVER CLAUDE ─────────────────────────────────────────────────────────────────────────────────────────────
19
19
  // Planning and building run on Claude; a Claude reader adds no independent read. `FAMILIES` holds no Claude entry
@@ -156,7 +156,7 @@ export function readerSection({ family, agreement, seedComponents, seedTotal, da
156
156
  ? `- Seed score: **${seedTotal ?? '—'}** (${Object.entries(seedComponents)
157
157
  .map(([k, v]) => `${SIGNAL_NAMES[k]} ${f2(v)}`)
158
158
  .join(' · ')})`
159
- : '- Seed score: none recorded (run `node scripts/intent-match.mjs <seed> --write` at groom Stage 3.5)';
159
+ : '- Seed score: none recorded (run `node scripts/intent-match.mjs <seed> --write` at refine Stage 3.5)';
160
160
  const lines = [
161
161
  '## Intent match',
162
162
  '',
@@ -5,7 +5,7 @@
5
5
  //
6
6
  // Pure — the caller passes the rows and how fresh their facts are.
7
7
 
8
- import { STAGES, groupByStage } from './stage.mjs';
8
+ import { STAGES, groupByStage, stageWord } from './stage.mjs';
9
9
 
10
10
  const SHIPPED_SHOWN = 5;
11
11
 
@@ -36,7 +36,7 @@ export function renderBoardText(rows, facts = { mode: 'docs' }) {
36
36
  const out = [`Board — six stages, ${fresh}`, ''];
37
37
  for (const stage of STAGES) {
38
38
  const list = columns[stage];
39
- out.push(`${stage} (${list.length})`);
39
+ out.push(`${stageWord(stage)} (${list.length})`);
40
40
  const shown = stage === 'Shipped' ? list.slice(0, SHIPPED_SHOWN) : list;
41
41
  for (const row of shown) out.push(cardLine(row));
42
42
  if (stage === 'Shipped' && list.length > shown.length)
@@ -6,8 +6,8 @@
6
6
  //
7
7
  // key dotted, `<section>.<name>`: the path in golden-frijoles.config.json
8
8
  // module the product module doctor groups it under (Plan · Build · Ship · Measure · Spend · Operate)
9
- // askWhen 'setup' | 'first-groom' | 'first-pr' | 'first-smoke' | 'first-report' | 'first-prune'
10
- // | 'first-high-risk-groom' | 'never-yet' (declared so the schema is ready; nothing reads it this wave)
9
+ // askWhen 'setup' | 'first-refine' | 'first-pr' | 'first-smoke' | 'first-report' | 'first-prune'
10
+ // | 'first-high-risk-refine' | 'never-yet' (declared so the schema is ready; nothing reads it this wave)
11
11
  // default what a skipped question means. `null` = "unanswered", which a rail must treat as its safest choice
12
12
  // question the plain words an agent asks, once
13
13
  // choices optional allowed values; `store: 'env'` means the answer lives in .env.local (frijoles init), never here
@@ -81,14 +81,14 @@ export const REGISTRY = Object.freeze([
81
81
  {
82
82
  key: 'roadmap.areas',
83
83
  module: 'Plan',
84
- askWhen: 'first-groom',
84
+ askWhen: 'first-refine',
85
85
  default: ['09 Platform & Infra'],
86
86
  question: 'Which areas (macro-sections) does this roadmap have? 09 Platform & Infra is reserved.',
87
87
  },
88
88
  {
89
89
  key: 'ways.fillIns',
90
90
  module: 'Plan',
91
- askWhen: 'first-groom',
91
+ askWhen: 'first-refine',
92
92
  default: 'Roadmap/fill-ins.yml',
93
93
  question: 'Where are this project\'s WAYS-OF-WORKING fill-ins?',
94
94
  },
@@ -157,7 +157,7 @@ export const REGISTRY = Object.freeze([
157
157
  {
158
158
  key: 'ship.killSwitchPolicy',
159
159
  module: 'Ship',
160
- askWhen: 'first-high-risk-groom',
160
+ askWhen: 'first-high-risk-refine',
161
161
  default: 'every-risk-high-story-names-its-flag',
162
162
  question: 'Should every risk:high story name its kill-switch flag?',
163
163
  },
@@ -1,11 +1,11 @@
1
1
  // epic-kickoff.mjs — the epic-mode kickoff, as one pure builder (board-sinks-and-scrumban D17).
2
2
  //
3
- // It used to live only in the groom skill (`emit-epic-kickoff.mjs` + `templates/epic-kickoff.md`), which the kit does
3
+ // It used to live only in the refine skill (`emit-epic-kickoff.mjs` + `templates/epic-kickoff.md`), which the kit does
4
4
  // not ship and an installed extractor cannot import (lock C11). The Hub's "Ready to build" card has to carry the same
5
5
  // kickoff the CLI prints — a kickoff composed twice is the exact drift `emit-epic-kickoff` was written to end — so
6
6
  // the builder, the parsers it reads the docs with and the template text all live here, in the ONE source
7
- // (`template/scripts/`). The groom skill imports a byte-identical copy that `skills/scripts/render-hook-vendor.mjs`
8
- // writes into `groom/vendor/` and checks in CI; the extractor imports this file directly.
7
+ // (`template/scripts/`). The refine skill imports a byte-identical copy that `skills/scripts/render-hook-vendor.mjs`
8
+ // writes into `refine/vendor/` and checks in CI; the extractor imports this file directly.
9
9
  //
10
10
  // The parsers moved here from `emit-kickoff.mjs` unchanged; that file re-exports them, so the per-sprint generator
11
11
  // and this one still read the doc format with ONE set of parsers.
@@ -150,7 +150,7 @@ export function buildEpicRules({ risk, texts, appetite = null, slug = '<slug>' }
150
150
  const rules = [];
151
151
  if (String(risk).toUpperCase() === 'HIGH')
152
152
  rules.push(
153
- '- **High risk:** the fresh `pr-reviewer` pass is mandatory on every PR, on top of the routed external passes.'
153
+ '- **High risk:** the fresh `verifier` pass is mandatory on every PR, on top of the routed external passes.'
154
154
  );
155
155
  if (MIGRATION_RE.test(all))
156
156
  rules.push(
@@ -164,7 +164,7 @@ export function buildEpicRules({ risk, texts, appetite = null, slug = '<slug>' }
164
164
  if (String(appetite).toUpperCase() === 'L')
165
165
  rules.push(
166
166
  `- **L bet:** it is funded one wave at a time. When you stop at a wave boundary, ask the product owner one line — ` +
167
- `"fund the next wave of \`${slug}\`? what does it displace?" — and on yes run groom's ` +
167
+ `"Start the next part of \`${slug}\`? What waits for it?" — and on yes run refine's ` +
168
168
  `\`fund.mjs --slug ${slug} --displaced "<…>"\` (position kept) before the next wave starts.`
169
169
  );
170
170
  return rules.length ? `\nFor this epic:\n${rules.join('\n')}\n` : '';
@@ -176,7 +176,7 @@ export const INSTALL_PROMPT =
176
176
  '2. Tell me in a few lines what it installs, what changes on this machine and which services it contacts. ' +
177
177
  'Offer me a security review, and wait for my go-ahead. ' +
178
178
  '3. Install it the way install.md says for the agent you are. ' +
179
- '4. Run the golden-frijoles skill and start its setup.';
179
+ '4. Run the setup skill from the golden-frijoles plugin.';
180
180
 
181
181
  /**
182
182
  * The value of `name` in a dotenv file, or null.
@@ -4,8 +4,8 @@
4
4
  // `emit-epic-kickoff.mjs` and `emit-kickoff.mjs` run in THREE places, the same bytes in each:
5
5
  // • a project's own `scripts/` (copied from the template),
6
6
  // • the kit's `dist/` (`npx -y @golden-frijoles/kit emit-epic-kickoff …`, far from the project),
7
- // • the groom skill's `vendor/` inside the plugin (the build-view mod's `/build`, and groom's Stage 8).
8
- // `projectRoot()` answers the first two. For the third it answers `groom/`, which holds no Roadmap/, so the root takes
7
+ // • the refine skill's `vendor/` inside the plugin (the build-view mod's `/build`, and refine's Stage 8).
8
+ // `projectRoot()` answers the first two. For the third it answers `refine/`, which holds no Roadmap/, so the root takes
9
9
  // one more rung (see `resolveRepoRoot`). The epic lookup lived twice, byte-for-byte, in the two CLIs; it lives here.
10
10
  //
11
11
  // Zero deps; no side effects at import.
@@ -72,7 +72,7 @@ export function findEpicDir(repoRoot, slug, { exists = existsSync, list = readdi
72
72
  return {
73
73
  error:
74
74
  `"${slug}" is a seed with no epic yet — a kickoff builds an epic. Fund it at the approval gate, then scaffold it ` +
75
- `(a fixed-scope seed needs nothing but its slug): node "$GROOM/scaffold-epic.mjs" --slug ${slug}`,
75
+ `(a fixed-scope seed needs nothing but its slug): node "$REFINE/scaffold-epic.mjs" --slug ${slug}`,
76
76
  };
77
77
  }
78
78
  return { error: `no epic found for slug "${slug}" under any Roadmap/*/ dir in "${repoRoot}" (searched: ${macros.join(', ') || '(none)'})` };
@@ -50,7 +50,7 @@ export function todayUtc(now = new Date()) {
50
50
  * shipped before the result record existed are never due (the no-backfill rule).
51
51
  */
52
52
  export function readDateOf({ readDate, targetMetric, shippedAt }) {
53
- // A day written at grooming that passes before the epic ships is read on ship day (codex review, #290): there was
53
+ // A day written at refining that passes before the epic ships is read on ship day (codex review, #290): there was
54
54
  // nothing out there to read before then, and "overdue since before release" would be a false alarm.
55
55
  const shipped = dayOf(shippedAt);
56
56
  if (isDay(readDate))
@@ -67,7 +67,7 @@ export function isLate({ verdictAt, shippedAt }) {
67
67
 
68
68
  /**
69
69
  * Whether a read is due today: a shipped epic with a target, no verdict yet, and a read date that has arrived. An epic
70
- * not yet shipped is never due, even past a read date written at grooming: there is nothing out there to read.
70
+ * not yet shipped is never due, even past a read date written at refining: there is nothing out there to read.
71
71
  */
72
72
  export function isReadDue({ targetMetric, verdict, readDate, shipped }, today = todayUtc()) {
73
73
  if (!shipped || !targetMetric || verdict || !isDay(readDate)) return false;
@@ -41,7 +41,7 @@ export const EPIC_FIELDS = ['title', 'area', 'risk', 'type', 'phase', 'sprints_t
41
41
  export const SPRINT_FIELDS = ['epic', 'sprint', 'title', 'risk', 'phase', 'stories_total', 'stories'];
42
42
  export const STORY_FIELDS = ['id', 'title', 'as_a', 'i_want', 'so_that', 'risk', 'status'];
43
43
 
44
- // finops D6/D17 — an epic's quote (written at grooming) and its actual (stamped at close), declared ONCE here.
44
+ // finops D6/D17 — an epic's quote (written at refining) and its actual (stamped at close), declared ONCE here.
45
45
  // All optional: an epic scaffolded before FinOps has none, and an absent field is never a zero (D4). The numeric
46
46
  // ones are numbers >= 0 or null; a `*_basis` says where the number came from, in words.
47
47
  export const FINOPS_NUMERIC_FIELDS = ['quote_low_usd', 'quote_high_usd', 'actual_usd', 'actual_mtok'];
@@ -55,7 +55,7 @@ export const FINOPS_FIELDS = [
55
55
  'actual_basis',
56
56
  ];
57
57
 
58
- // result-record D1/D2 — the result record: what an epic should move (written at grooming) and its verdict (written at
58
+ // result-record D1/D2 — the result record: what an epic should move (written at refining) and its verdict (written at
59
59
  // the read), declared ONCE here. All optional: no target is "no target", never an error. Dates are calendar days.
60
60
  export const VERDICTS = ['proven', 'disproven', 'unclear'];
61
61
  export const TARGET_FIELDS = ['hypothesis', 'target_metric', 'target_from', 'target_to', 'read_date'];
@@ -140,7 +140,7 @@ export function validateResultFields(fm) {
140
140
  return offenses;
141
141
  }
142
142
 
143
- // one-epic-page D11 — the epic's flag, decided at groom Stage 6b and copied into the README by the scaffold. The SDK's
143
+ // one-epic-page D11 — the epic's flag, decided at refine Stage 6b and copied into the README by the scaffold. The SDK's
144
144
  // flag-key grammar (`FLAG_KEY` in apps/web/lib/flag-admin-operation.ts). Optional: `flag_key: null` (no flag) is the
145
145
  // default and never an error; a value that is not a key is named, never pushed (the extract sends null for it).
146
146
  export const FLAG_KEY_RE = /^[a-z][a-z0-9_.-]{0,127}$/;
@@ -25,6 +25,16 @@ import { branchCandidates } from './work-branch.mjs';
25
25
  /** The six stages, in board order (D1 — locked by the product owner, 2026-10-01). */
26
26
  export const STAGES = Object.freeze(['To groom', 'Grooming', 'Ready to build', 'Building', 'QA', 'Shipped']);
27
27
 
28
+ /**
29
+ * What each stage is CALLED where a person reads it (plugin-1-0 D6): plain agile, Backlog → Refining → Ready. The keys
30
+ * above are stored data and never change; the board, the build view and the terminal board print through `stageWord`.
31
+ * One known exception: the Notion sync writes the KEY into its `Stage` select, because a select option is data matched
32
+ * by name (renaming it is a migration of that database: the product owner's call). The console's twin is
33
+ * `apps/web/lib/screen-words.ts → stageLabel`, and a test keeps the two equal.
34
+ */
35
+ export const STAGE_WORDS = Object.freeze({ 'To groom': 'Backlog', Grooming: 'Refining', 'Ready to build': 'Ready' });
36
+ export const stageWord = (stage) => STAGE_WORDS[stage] ?? stage;
37
+
28
38
  const DOCS_STAGE = {
29
39
  raw: 'To groom',
30
40
  ready: 'Grooming',
@@ -1,7 +1,7 @@
1
1
  // strategy-files.mjs — where the strategy coaches' files live, and the few shapes every strategy script reads
2
2
  // (coaches-v2 D1, D4, D7).
3
3
  //
4
- // groom's own reader (`skills/groom/strategy.mjs`) ships inside the plugin and cannot import from the kit, so it keeps
4
+ // refine's own reader (`skills/refine/strategy.mjs`) ships inside the plugin and cannot import from the kit, so it keeps
5
5
  // its own STRATEGY_DIR and KINDS; `skills/scripts/strategy-templates.test.mjs` fails while the two disagree, so a
6
6
  // rename lands in both or in neither.
7
7
  //
@@ -132,7 +132,7 @@ function parseBlockLine(rest) {
132
132
  if (/^[a-z][\w-]*:(\s|$)/.test(rest)) {
133
133
  throw (
134
134
  '`- <kind>: "…"` is the old list shape — write `- <kind> "words"` (for example `- head "Orders"`); ' +
135
- 'see groom/references/intent-and-visuals.md'
135
+ 'see refine/references/intent-and-visuals.md'
136
136
  );
137
137
  }
138
138
  const tokens = tokenize(rest);
package/dist/quote.mjs CHANGED
@@ -7,7 +7,7 @@
7
7
  //
8
8
  // The quote is the p25–p75 of `actual_usd` over SHIPPED epics at the same appetite — the middle half of what epics
9
9
  // that size actually cost here. Until there are MIN_HISTORY of them it is WIDE_DEFAULT, labelled `wide` with its n,
10
- // never a guess dressed as a calibration (D4). It is recomputed at every groom, so it moves as history accumulates.
10
+ // never a guess dressed as a calibration (D4). It is recomputed at every refinement, so it moves as history accumulates.
11
11
  // Epics are read through the extractor (`buildRows`), so appetite follows the board's own rule (README, else seed —
12
12
  // D20) and no second frontmatter reader exists.
13
13
  //
@@ -4,7 +4,7 @@
4
4
  // ── The rule ──────────────────────────────────────────────────────────────────────────────────────
5
5
  // The highest-preference family that did NOT build the diff runs the general pass.
6
6
  // If the PR touches a security path, the NEXT eligible family runs the lean security lens.
7
- // The fresh `pr-reviewer` subagent runs too, in whatever scope the project's review-config.json sets.
7
+ // The fresh `verifier` subagent runs too, in whatever scope the project's review-config.json sets.
8
8
  //
9
9
  // That is the whole policy. It replaces a 273-line router with a four-row table, a two-passes-per-PR
10
10
  // count, a HIGH-only branch on the fresh reviewer, and the REFUND-ASK / --fallback-after / DARK-layer
@@ -84,7 +84,7 @@ export function planReview({ builder, available = PREFERENCE, securityPass = fal
84
84
  else notes.push(`The security lens is triggered but no family can run it. Say so in the PR body.`);
85
85
  }
86
86
  notes.push(
87
- 'The fresh `pr-reviewer` subagent runs as well — context independence is a different axis from family independence.'
87
+ 'The fresh `verifier` subagent runs as well — context independence is a different axis from family independence.'
88
88
  );
89
89
  return { builder, general, security, notes };
90
90
  }
@@ -107,7 +107,7 @@ export function renderPlan(plan, pr, repo) {
107
107
  l.push(' Run:');
108
108
  if (plan.general) l.push(` ${arg(plan.general)}`);
109
109
  if (plan.security) l.push(` ${arg(plan.security)} --lens security`);
110
- l.push(` then: use the pr-reviewer subagent on PR #${pr}`);
110
+ l.push(` then: use the verifier subagent on PR #${pr}`);
111
111
  return l.join('\n');
112
112
  }
113
113
 
@@ -613,7 +613,7 @@ export function buildRows({
613
613
  area,
614
614
  type: TYPE_LABEL[epicFm.type || seed.type] || 'Epic',
615
615
  risk,
616
- // finops D20 — the README's own appetite first (an epic groomed after FinOps carries it), else the seed's.
616
+ // finops D20 — the README's own appetite first (an epic refined after FinOps carries it), else the seed's.
617
617
  appetite: epicFm.appetite || seed.appetite || null,
618
618
  // fund-at-approval D8 — the README's own first (an epic with no seed carries it there), else the seed's.
619
619
  underwritten_by: epicFm.underwritten_by || seed.underwritten_by || null,
@@ -138,9 +138,9 @@ restored.)
138
138
  1. **Install the Claude GitHub App** on `<root-repo>` (root repo).
139
139
  2. **Create the routine** from `roadmap-hygiene.prompt.md`.
140
140
  3. **Trigger:** Schedule, **weekly — Mon 14:00 UTC** (after the 08:00 nightly `notion-sync.yml`).
141
- 4. **Env/connectors:** **No Notion connector** — no `.mcp.json`, no `NOTION_TOKEN`. The routine grooms
141
+ 4. **Env/connectors:** **No Notion connector** — no `.mcp.json`, no `NOTION_TOKEN`. The routine refines
142
142
  the `00-ideas` funnel, flags status-drift, runs `node scripts/build-order.mjs`, invokes the
143
- `doc-hygiene` skill (`node scripts/doc-hygiene.mjs` — always-read-set size + LEARNINGS/poster
143
+ doc-hygiene script (`node scripts/doc-hygiene.mjs` — always-read-set size + LEARNINGS/poster
144
144
  dedupe-staleness candidates), and opens a `claude/` **docs PR** with the regenerated
145
145
  `BUILD-ORDER.md` + any new `DOC-HYGIENE-REPORT-*.md` + a drift report. Network = GitHub.
146
146
  5. **Propagation:** after the product owner merges the docs PR, the existing `notion-sync.yml` propagates
@@ -289,7 +289,7 @@ routine (cap-safe) — see the budget table below.
289
289
  Shipped by `ops-routines-reporting` (the origin project's `ops-routines-reporting` epic)
290
290
  S3 — a dedicated **weekly** routine (mirroring Routine C's precedent of a standalone weekly schedule,
291
291
  rather than a day-of-week-gated step folded into the nightly `ops-nightly` routine). One step: the
292
- `weekly-recap` skill (`scripts/weekly-recap.mjs`) gathers the week's merged PRs (every configured repo),
292
+ `report` skill's weekly chapter (`scripts/weekly-recap.mjs`) gathers the week's merged PRs (every configured repo),
293
293
  shipped/closed epics (README frontmatter `status:` flips), a merges-to-main deploy count per app repo,
294
294
  and a short retro digest per shipped epic — then posts one Telegram message.
295
295
 
@@ -324,7 +324,7 @@ and a short retro digest per shipped epic — then posts one Telegram message.
324
324
  (reads `gh` + `git log` across every configured repo; posts to Telegram; writes `claude/pmo-reports-log`).
325
325
 
326
326
  Shipped by `pmo-operational-reports` (the origin project's `pmo-operational-reports` epic)
327
- S3. One step: the `pmo-report` skill (`scripts/pmo-report.mjs --weekly`) gathers scrum/DORA/doc-ops
327
+ S3. One step: the `report` skill's PMO chapter (`scripts/pmo-report.mjs --weekly`) gathers scrum/DORA/doc-ops
328
328
  metrics, renders a story-deck URL, posts headline numbers plus the deck link to Telegram, and
329
329
  then advances the PMO window log.
330
330
 
@@ -12,18 +12,17 @@
12
12
 
13
13
  Every step here is advisory/observability or a docs-only PR — none merges, none gates, none is a
14
14
  required check, and NOTHING here ever runs a destructive `--apply` (that stays a separate,
15
- human-confirmed action per the `vercel-prune` skill — this routine only ever runs its dry-run
16
- report step).
15
+ human-confirmed action — this routine only ever runs the dry-run report).
17
16
 
18
- Skills below come from the `golden-frijoles` plugin (golden-frijoles marketplace,
19
- golden-frijoles/skills) — invoke each by name, not
20
- by a repo-local `skills/<name>/SKILL.md` path (that path no longer exists in this repo).
17
+ Steps 1–3 run their scripts directly: the skills that used to wrap them (build-order-sync, vercel-prune, babysit-pr)
18
+ left the distributed plugin in 1.0 (plugin-1-0 S3.3), and everything they added is written into the steps below.
19
+ Step 4 uses the plugin's `report` skill, daily chapter.
21
20
 
22
21
  Reuse, don't rebuild:
23
- - `build-order-sync` skill → scripts/build-order-sync.mjs (check/regen/branch/PR on drift)
24
- - `vercel-prune` skill → scripts/vercel-prune-previews.mjs (dry-run report only, never --apply)
25
- - `babysit-pr` skill → scripts/babysit-pr.mjs (one open PR at a time; silent when clean)
26
- - `standup-post` skill → scripts/standup.mjs (the aggregation, diffing, and actual Telegram
22
+ - scripts/build-order-sync.mjs (check/regen/branch/PR on drift)
23
+ - scripts/vercel-prune-previews.mjs (dry-run report only, never --apply)
24
+ - scripts/babysit-pr.mjs (one open PR at a time; silent when clean)
25
+ - `report` skill's daily chapter → scripts/standup.mjs (the aggregation, diffing, and actual Telegram
27
26
  send — including its own independent CI-red / merge-conflict read, taken AFTER steps 1–3 have run)
28
27
  - gh CLI (every repo in reporting.config.json's `repos`), scripts/build-order.mjs --check,
29
28
  scripts/vercel-prune-previews.mjs (dry-run, --project <vercelProject> --age <stalePreviewAgeDays>), the configured `smoke` workflow.
@@ -40,25 +39,30 @@ running as the product owner. Your job is to run four steps, in order, then stop
40
39
  **advisory only** — you never approve, merge, block, or auto-apply anything, and any code/doc change
41
40
  you make lands only as a `claude/`-branch PR for a human to review.
42
41
 
43
- ## Step 1 — `build-order-sync`
44
- Use the `build-order-sync` skill: run `node scripts/build-order-sync.mjs`. If the board was
42
+ ## Step 1 — build-order sync
43
+ Run `node scripts/build-order-sync.mjs`. If the board was
45
44
  stale, it opens a `claude/` docs PR with the regenerated `Roadmap/00-ideas/BUILD-ORDER.md` — nothing
46
45
  else to do. If it was already current, no PR — move on.
47
46
 
48
- ## Step 2 — `vercel-prune` (dry-run report only)
49
- Use the `vercel-prune` skill **through Stage 2 only** — the dry-run report. **Never run its
50
- Stage 3 (`--apply`)** from this routine, under any circumstance; that is a separate, human-initiated
47
+ ## Step 2 — stale Vercel previews (dry-run report only)
48
+ Keep every branch with an open PR (an empty list is fine), then run the dry run only:
49
+
50
+ ```bash
51
+ KEEP=$(gh pr list --repo <app-repo> --state open --json headRefName --jq '[.[].headRefName] | join(",")')
52
+ node scripts/vercel-prune-previews.mjs --project <vercelProject> --age <stalePreviewAgeDays> --keep-branch "$KEEP"
53
+ ```
54
+
55
+ **Never pass `--apply`** from this routine, under any circumstance; that is a separate, human-initiated
51
56
  action gated on the product owner explicitly asking for it in a live conversation, which this unattended nightly
52
57
  run structurally cannot be. Note the stale-preview count/list in your own reasoning — no PR, no
53
58
  comment; the standup (step 4) will report it independently.
54
59
 
55
- ## Step 3 — `babysit-pr` (once per open PR, across every configured repo)
60
+ ## Step 3 — babysit open PRs (once per open PR, across every configured repo)
56
61
  For each repo in `reporting.config.json`'s `repos`: list open PRs (`gh pr list --repo <repo> --state open --json
57
- number`), then use the `babysit-pr` skill once per open PR (`node scripts/babysit-pr.mjs <PR#>
58
- --repo <repo>`). A clean PR gets no comment — that's correct, not a skipped step. Never merge, never
62
+ number`), then run `node scripts/babysit-pr.mjs <PR#> --repo <repo>` once per open PR. A clean PR gets no comment — that's correct, not a skipped step. Never merge, never
59
63
  rebase a conflicting branch, never touch any commit-status/check-run API.
60
64
 
61
- ## Step 4 — `standup-post`, written by YOU in three phases
65
+ ## Step 4 — the standup (the `report` skill, daily chapter), written by YOU in three phases
62
66
 
63
67
  The standup is no longer a dump of delta lines: **you write it, as the CPO persona, and a mechanical
64
68
  guard checks your draft before it posts.**
@@ -5,10 +5,10 @@
5
5
  It posts the PMO headline metrics plus the story-deck link to Telegram, using the same
6
6
  Telegram/load-bearing-output rail as standup-post and weekly-recap.
7
7
 
8
- Reuse, don't rebuild (the `pmo-report` skill comes from the `golden-frijoles` plugin, golden-frijoles
8
+ Reuse, don't rebuild (the `report` skill's PMO chapter comes from the `golden-frijoles` plugin, golden-frijoles
9
9
  marketplace — invoke by name, not a repo-local
10
10
  `skills/<name>/SKILL.md` path, which no longer exists in this repo):
11
- - `pmo-report` skill -> scripts/pmo-report.mjs --weekly
11
+ - `report` skill's PMO chapter -> scripts/pmo-report.mjs --weekly
12
12
  - scripts/lib/gh-rest.mjs, scripts/lib/log-branch.mjs, scripts/lib/telegram-format.mjs
13
13
  - scripts/lib/pmo-delivery.mjs for message formatting and sendMessage.
14
14
 
@@ -33,7 +33,7 @@ code, open a PR, or change a required status check. The Telegram post plus the P
33
33
  the entire output.
34
34
 
35
35
  ## The one step - `pmo-report`
36
- Use the `pmo-report` skill exactly. It handles the config check (the committed `reporting.config.json`
36
+ Use the `report` skill's PMO chapter exactly. It handles the config check (the committed `reporting.config.json`
37
37
  for repos and deck hosting; the chat id from the `TELEGRAM_CHAT_ID` env var in this unattended session), the `TELEGRAM_BOT_TOKEN` check, running
38
38
  `node scripts/pmo-report.mjs --weekly`, and reporting the headline metrics plus generated deck link.
39
39
 
@@ -28,7 +28,7 @@ only sources of truth. If anyone reads your output as a decision, say plainly th
28
28
 
29
29
  **Where you sit in the review stack (updated 2026-07-14 — review-policy flip).** WAYS-OF-WORKING → *Review
30
30
  & merge* defines three layers: CI (always), **cross-agent review via `scripts/cross-review.mjs`** (mandatory
31
- every PR — a *different model family*, run locally), and the **`pr-reviewer` subagent** (mandatory on HIGH
31
+ every PR — a *different model family*, run locally), and the **`verifier` subagent** (mandatory on HIGH
32
32
  tier, optional on LOW). **You are none of them.** You do not satisfy the mandatory cross-agent requirement
33
33
  — you are the Claude family, and cross-family blind-spot coverage is the entire point of that layer — and
34
34
  you do not satisfy the HIGH-tier fresh-reviewer requirement, which is a deliberate, invoked pass against the
@@ -3,7 +3,7 @@
3
3
 
4
4
  This is the prompt for a weekly Claude Code *Routine* (cloud session, research preview) on the
5
5
  monorepo-ROOT repo (<root-repo>), running as the product owner. It adds the JUDGMENT layer the
6
- deterministic scripts can't: groom the 00-ideas funnel, flag status-drift, regenerate BUILD-ORDER.md,
6
+ deterministic scripts can't: refine the 00-ideas funnel, flag status-drift, regenerate BUILD-ORDER.md,
7
7
  and open a `claude/` DOCS PR with a drift report.
8
8
 
9
9
  NO Notion connector by design. The existing `.github/workflows/notion-sync.yml` (nightly + on push)
@@ -16,7 +16,7 @@
16
16
  - SSOT = each epic README frontmatter `status:`; seed frontmatter owns the un-scaffolded funnel.
17
17
  The extractor emits r.status (authoritative) AND r.status_derived (fallback) so drift is detectable.
18
18
  - Funnel docs: Roadmap/00-ideas/README.md (seed lifecycle: raw|ready|queued|scaffolded|in-progress|shipped|archived)
19
- - `node scripts/doc-hygiene.mjs` (the `doc-hygiene` skill, `golden-frijoles` plugin) → measures the always-read set + flags
19
+ - `node scripts/doc-hygiene.mjs` → measures the always-read set + flags
20
20
  dedupe/staleness candidates in LEARNINGS.md/the poster; writes its own dated
21
21
  Roadmap/00-ideas/DOC-HYGIENE-REPORT-<date>.md — a DIFFERENT concern from this routine's own
22
22
  HYGIENE-REPORT-*.md (funnel/status drift). Never auto-edits either.
@@ -30,7 +30,7 @@
30
30
 
31
31
  You are a weekly **roadmap-hygiene** Claude Code Routine on the monorepo-root repo
32
32
  (`<root-repo>`), running as the product owner. Your output is a single `claude/` **docs PR** — you
33
- groom and flag, you do not merge, deploy, or touch app code. Everything you produce is **advisory**:
33
+ refine and flag, you do not merge, deploy, or touch app code. Everything you produce is **advisory**:
34
34
  the human reviews and merges the PR; nothing here gates anything.
35
35
 
36
36
  ## What "the roadmap" is (read these first)
@@ -42,7 +42,7 @@ the human reviews and merges the PR; nothing here gates anything.
42
42
 
43
43
  ## Do this in one pass, then open the PR
44
44
 
45
- **1. Groom the `00-ideas` funnel.** Scan `Roadmap/00-ideas/seeds/*.md`:
45
+ **1. Refine the `00-ideas` funnel.** Scan `Roadmap/00-ideas/seeds/*.md`:
46
46
  - Flag seeds whose `status:` looks stale vs reality — e.g. a seed marked `scaffolded`/`in-progress`
47
47
  whose linked `epic:` README is already `shipped`, or a `ready`/`queued` seed with no movement in a
48
48
  long while. Note seeds missing required frontmatter (`slug`/`area`/`status`/`type`).
@@ -62,12 +62,14 @@ one-line reason.
62
62
  `Roadmap/00-ideas/BUILD-ORDER.md` from the projection. If it changes, that change goes in the PR; if
63
63
  `node scripts/build-order.mjs --check` is already clean, say so (the board was current).
64
64
 
65
- **4. Doc hygiene pass.** Invoke the `doc-hygiene` skill (`golden-frijoles` plugin, golden-frijoles
66
- marketplace — no longer a repo-local `skills/doc-hygiene/`
67
- path) — run
68
- `node scripts/doc-hygiene.mjs`, review any flagged candidates per its Stage 2 (verify before reporting;
69
- its heuristics are deliberately cheap and can false-positive), and note the always-read set's current
70
- size. It writes its own dated `Roadmap/00-ideas/DOC-HYGIENE-REPORT-<date>.md` — commit that alongside
65
+ **4. Doc hygiene pass.** Run `node scripts/doc-hygiene.mjs` and verify every flagged candidate before
66
+ reporting it; its heuristics are cheap word-overlap and path checks that narrow where to look, nothing more:
67
+ - *near-duplicate bullets:* read both in context; a shared topic with a different why, date or source is not a
68
+ duplicate;
69
+ - *dead path:* check every app root before calling a path gone, and a bullet that documents a swap (old → new path)
70
+ always flags its old path, correctly;
71
+ - *archived-epic mention:* flag it only if the lesson itself, not the epic's name, is now wrong.
72
+ Then note the always-read set's current size. It writes its own dated `Roadmap/00-ideas/DOC-HYGIENE-REPORT-<date>.md` — commit that alongside
71
73
  the PR if `--check` wasn't used. **It never edits `LEARNINGS.md`/`README.md`** — a genuine candidate
72
74
  worth acting on goes in the PR body as a proposal, same as everything else this routine surfaces.
73
75
 
@@ -76,7 +78,7 @@ worth acting on goes in the PR body as a proposal, same as everything else this
76
78
  `DOC-HYGIENE-REPORT-*.md` (and only docs under `Roadmap/`). **Docs only — never touch app code,
77
79
  scripts, or infra** (running `scripts/doc-hygiene.mjs` is a read-plus-one-new-report tool invocation,
78
80
  not a script edit).
79
- - PR body = the **drift report**: four short sections — *Funnel grooming*, *Status drift*, *Board
81
+ - PR body = the **drift report**: four short sections — *Funnel refining*, *Status drift*, *Board
80
82
  regenerated?*, *Doc hygiene* — each a bullet list of findings (or "nothing to flag"). Each finding is
81
83
  one line: what, where, and the proposed fix. Lead the PR body with the advisory banner:
82
84
  > 🤖 **Routine C — weekly roadmap hygiene (Claude, cloud).** Advisory docs PR — review & merge by hand; nothing here gates.
@@ -9,10 +9,10 @@
9
9
  failure-ping) but on a weekly cadence, and mirrors Routine C's precedent (a dedicated weekly routine,
10
10
  not a day-of-week-gated step folded into a nightly one).
11
11
 
12
- Reuse, don't rebuild (the `weekly-recap` skill comes from the `golden-frijoles` plugin, golden-frijoles
12
+ Reuse, don't rebuild (the `report` skill's weekly chapter comes from the `golden-frijoles` plugin, golden-frijoles
13
13
  marketplace — invoke by name, not a repo-local
14
14
  `skills/<name>/SKILL.md` path, which no longer exists in this repo):
15
- - `weekly-recap` skill → scripts/weekly-recap.mjs (the gathering, message-building, and the
15
+ - `report` skill's weekly chapter → scripts/weekly-recap.mjs (the gathering, message-building, and the
16
16
  actual Telegram send + weekly-recaps.log commit — this routine just invokes it and reports back)
17
17
  - gh CLI (every configured repo), git log -p on epic READMEs (status: SSOT), the same configured repo list
18
18
  scripts/standup.mjs already uses.
@@ -52,7 +52,7 @@ silently to a derived status. A silent fallback makes drift undetectable exactly
52
52
  One field is authoritative at each stage — they never both drive the board:
53
53
 
54
54
  - **Before an epic exists** (`epic: null`) → the **seed's** `status` (`raw`/`ready`/`queued`) is
55
- authoritative; you set it by hand or the `groom` skill sets it. This is what the BUILD-ORDER
55
+ authoritative; you set it by hand or the `refine` skill sets it. This is what the BUILD-ORDER
56
56
  **funnel** shows.
57
57
  - **Once `epic:` is set** → the **epic README's frontmatter `status:` is the SSOT** (set at epic
58
58
  close: `scaffolded` → `in-progress` → `shipped`). The seed is now **funnel-only** — its `status:` is
@@ -64,7 +64,7 @@ One field is authoritative at each stage — they never both drive the board:
64
64
  `appetite` (S | M | L) is the **budget the idea is worth**, fixed at shaping *before* the solution
65
65
  is designed — sessions + an implied token band, never a time estimate (see WAYS-OF-WORKING →
66
66
  *Betting & appetite*). `underwritten_by` names the **cycle that paid for it** — the bare name of a
67
- `Roadmap/bets/<cycle>.md` file (`wave-2026-10`), written by `groom`'s `fund.mjs` at the approval gate.
67
+ `Roadmap/bets/<cycle>.md` file (`wave-2026-10`), written by `refine`'s `fund.mjs` at the approval gate.
68
68
  `null` means nobody has paid for it yet — fine in the funnel, impossible on the board: `build-order.mjs`
69
69
  **hard-fails** a `queued` seed with no `appetite`, and a live bet (`queued`, or an epic scaffolded or in
70
70
  progress) whose `underwritten_by` is missing or names no cycle file. Like `status`, `appetite` is an
@@ -72,15 +72,15 @@ enforced enum — a present-but-unrecognized value fails the board, it never fal
72
72
 
73
73
  ## How seeds flow (no file moves)
74
74
 
75
- 1. **Capture** — drop a raw idea as `seeds/<slug>.md` with `status: raw` (the `groom` skill does this
75
+ 1. **Capture** — drop a raw idea as `seeds/<slug>.md` with `status: raw` (the `refine` skill does this
76
76
  from a brain-dump).
77
- 2. **Scope** — `groom` fills out the Definition-of-Ready (appetite included) and flips
77
+ 2. **Scope** — `refine` fills out the Definition-of-Ready (appetite included) and flips
78
78
  `status: ready`.
79
- 3. **Fund + scaffold, in one answer** — the approval gate is the betting table. On "Approve the plan", `groom`
79
+ 3. **Fund + scaffold, in one answer** — the approval gate is the betting table. On "Approve the plan", `refine`
80
80
  runs its own `fund.mjs` (a row in the month's `Roadmap/bets/wave-YYYY-MM.md`, created on first use,
81
81
  recording what the bet displaced; `underwritten_by:`; a `build_order` placed with `--next` or
82
82
  `--after <slug>`, renumbering only the queue) and then `scaffold-epic.mjs` (the epic/sprint docs; the
83
- seed gets `epic:` + `status: scaffolded`), committed together. Both ship inside the `groom` skill,
83
+ seed gets `epic:` + `status: scaffolded`), committed together. Both ship inside the `refine` skill,
84
84
  `golden-frijoles` plugin. A fixed-scope seed scaffolds from its slug alone, its acceptance criteria
85
85
  becoming sprint 1's stories. "Park it" leaves the seed `ready` and scaffolds nothing.
86
86
  **No file ever moves between folders** — the frontmatter carries the state.
@@ -162,6 +162,6 @@ one-liner + why + date shape.
162
162
  - **Session length is set by a measured line, not a stamina rule.** A whole epic in one session is the
163
163
  main context-cost driver, and the durable state (the plan file, sprint docs, team memory) makes
164
164
  re-entry cheap by design — so checkpoint or hand off when the budget line says so (its thresholds
165
- live in one table, `THRESHOLDS` in groom's `session-budget.mjs`), not per sprint. Groom's twin rule is "one deep ask
165
+ live in one table, `THRESHOLDS` in refine's `session-budget.mjs`), not per sprint. Refine's twin rule is "one deep ask
166
166
  per approval gate". *(Sharpened 2026-09-30, session-budget: "fresh session per sprint" was set for
167
167
  earlier models.)*
@@ -17,7 +17,7 @@ Everything is sliced into **user stories** — the smallest independently testab
17
17
  \<role\>, I want \<capability\>, so that \<outcome\>*, plus **acceptance checks the product owner can
18
18
  run**. Stories roll up into sprints, sprints into an epic, epics into a macro-section. Before building,
19
19
  check whether existing features plus communication already deliver the outcome — surface that lighter
20
- path first; `groom` gates on it (Stage 2.5).
20
+ path first; `refine` gates on it (Stage 2.5).
21
21
 
22
22
  ## The cadence
23
23
 
@@ -93,13 +93,13 @@ and session context. Fixed appetite, variable scope.
93
93
  | **L** | a multi-wave epic | per-wave: each wave is re-bet at the boundary |
94
94
 
95
95
  Four rules: **an exhausted bet returns to shaping**, never extends in flight; **the approval gate is the
96
- betting table** — **Approve the plan** in `groom`'s Plan gate funds it in the same answer (`fund.mjs`: a row in
96
+ betting table** — **Approve the plan** in `refine`'s Plan gate funds it in the same answer (`fund.mjs`: a row in
97
97
  the month's cycle file `Roadmap/bets/wave-YYYY-MM.md` recording what it displaced, `underwritten_by:`, and a
98
98
  build position), in the same commit as the scaffold, while **Park it** leaves it `ready` and scaffolds nothing
99
- (the gates' words: groom `references/gates.md`); **nothing scaffolded is unfunded** — `build-order.mjs` fails a
99
+ (the gates' words: refine `references/gates.md`); **nothing scaffolded is unfunded** — `build-order.mjs` fails a
100
100
  live bet with no `underwritten_by:`;
101
101
  and **uphill work stays on the strongest model**. An L bet is re-bet at each wave boundary with one line
102
- when its builder stops there ("fund the next wave of `<slug>`? what does it displace?"; yes runs `fund.mjs`
102
+ when its builder stops there ("Start the next part of `<slug>`? What waits for it?"; yes runs `fund.mjs`
103
103
  again, position kept). Fixed scope (appetite S) and reactive/ops work go through the same gate, straight to
104
104
  a builder. Why it works this way:
105
105
  [`references/shapeup/`](https://github.com/golden-frijoles/skills/blob/main/template/references/shapeup/README.md).
@@ -111,7 +111,7 @@ questions** — not more reviewers of the same kind.
111
111
 
112
112
  ```
113
113
  CI (deterministic gate) — does it build, typecheck, pass the suite? BLOCKS merge
114
- → fresh pr-reviewer subagent — context independence: did not hold the diff
114
+ → fresh verifier subagent — context independence: did not hold the diff
115
115
  → one external cross-family pass — family independence: different blind spots
116
116
  → + a lean security lens — when the diff touches a security path
117
117
  → the builder merges on green
@@ -234,7 +234,7 @@ derived views. **`Roadmap/bets/`** holds one file per wave; **`tasks/`** is the
234
234
  - **Worker death is a normal case.** Each builder on its own worktree; a killed worker's uncommitted tree
235
235
  is evidence, not garbage; **verify by re-deriving repo state, never by trusting a completion report** —
236
236
  a rate-limited subagent still returns a plausible-sounding result. Checkpoint and hand
237
- off when the budget line says so (Claude Code: under the prompt; groom: at each approval gate) —
237
+ off when the budget line says so (Claude Code: under the prompt; refine: at each approval gate) —
238
238
  not by sprint count. Where no line shows (function hooks off, or no figure yet), compact at
239
239
  sprint/PR boundaries.
240
240
  - Commit messages end with the `Co-Authored-By: Claude` trailer.
@@ -17,7 +17,7 @@ api) is green, open a draft PR declaring the risk tier, then flip it ready-for-r
17
17
  SMOKE WALKTHROUGH (below) into sprint-{{N}}.md before you call the sprint done.
18
18
  Review (WAYS-OF-WORKING → Review & merge): CI always; then run
19
19
  `node scripts/review-route.mjs --builder <who-wrote-it> <PR#>` and take what it prints — one external
20
- general pass, a security lens when the changed paths trigger it, and the fresh `pr-reviewer` subagent, in
20
+ general pass, a security lens when the changed paths trigger it, and the fresh `verifier` subagent, in
21
21
  the scope `scripts/review-config.json` sets. A family never reviews its own diff, so don't pick `--agent`
22
22
  by hand; a capped family is routed past with `--exclude <family>`, and if only one can run it runs both
23
23
  prompts and you say so in the PR body. You merge your own PR on a green gate once findings are resolved.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@golden-frijoles/kit",
3
- "version": "0.44.0",
3
+ "version": "1.0.0",
4
4
  "description": "The scripts the Golden Frijoles skills run, packaged so they work in any repo: planning, build-order, reporting and verification rails. Zero dependencies.",
5
5
  "type": "module",
6
6
  "bin": {