@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.
- package/dist/build-order.mjs +6 -6
- package/dist/build-state.mjs +2 -3
- package/dist/cold-read.mjs +1 -1
- package/dist/cold-read.prompt.md +3 -1
- package/dist/cross-panel.prompt.md +2 -2
- package/dist/cross-review.mjs +1 -1
- package/dist/cross-review.prompt.md +1 -1
- package/dist/doc-format.enforced.json +1 -1
- package/dist/doc-format.mjs +3 -3
- package/dist/doc-hygiene.mjs +3 -3
- package/dist/emit-epic-kickoff.mjs +4 -4
- package/dist/emit-kickoff.mjs +4 -4
- package/dist/init.mjs +1 -1
- package/dist/intent-match.mjs +6 -6
- package/dist/intent-reader.mjs +2 -2
- package/dist/lib/board-text.mjs +2 -2
- package/dist/lib/config-registry.mjs +5 -5
- package/dist/lib/epic-kickoff.mjs +5 -5
- package/dist/lib/golden-onboarding.mjs +1 -1
- package/dist/lib/kickoff-cli.mjs +3 -3
- package/dist/lib/result-dates.mjs +2 -2
- package/dist/lib/roadmap-contract.mjs +3 -3
- package/dist/lib/stage.mjs +10 -0
- package/dist/lib/strategy-files.mjs +1 -1
- package/dist/lib/surface.mjs +1 -1
- package/dist/quote.mjs +1 -1
- package/dist/review-route.mjs +3 -3
- package/dist/roadmap-extract.mjs +1 -1
- package/dist/routines/README.md +4 -4
- package/dist/routines/ops-nightly.prompt.md +22 -18
- package/dist/routines/pmo-report.prompt.md +3 -3
- package/dist/routines/pr-review.prompt.md +1 -1
- package/dist/routines/roadmap-hygiene.prompt.md +13 -11
- package/dist/routines/weekly-recap.prompt.md +2 -2
- package/dist/skeleton/Roadmap/00-ideas/README.md +6 -6
- package/dist/skeleton/Roadmap/LEARNINGS.md +1 -1
- package/dist/skeleton/Roadmap/WAYS-OF-WORKING.md +6 -6
- package/dist/templates/kickoff.md +1 -1
- package/package.json +1 -1
package/dist/build-order.mjs
CHANGED
|
@@ -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(
|
|
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/. `
|
|
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 "$
|
|
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
|
|
package/dist/build-state.mjs
CHANGED
|
@@ -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' : (
|
|
936
|
+
const word = s === 'Building' && locking ? 'Locking' : stageWord(s);
|
|
938
937
|
return s === stage ? `${TRACK_MARK} ${word}` : word;
|
|
939
938
|
})
|
|
940
939
|
.join(' ─ ');
|
package/dist/cold-read.mjs
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
|
-
// cold-read.mjs — the deterministic half of the
|
|
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
|
package/dist/cold-read.prompt.md
CHANGED
|
@@ -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
|
|
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
|
-
|
|
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
|
|
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 +
|
package/dist/cross-review.mjs
CHANGED
|
@@ -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 `
|
|
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 `
|
|
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
|
|
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
|
}
|
package/dist/doc-format.mjs
CHANGED
|
@@ -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 `
|
|
4
|
-
// skills/
|
|
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
|
|
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);
|
package/dist/doc-hygiene.mjs
CHANGED
|
@@ -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
|
|
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
|
|
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
|
|
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
|
|
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
|
|
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
|
|
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/ —
|
|
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
|
|
package/dist/emit-kickoff.mjs
CHANGED
|
@@ -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
|
|
3
|
-
// sprint. Planning-only helper for 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
|
|
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
|
|
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 `
|
|
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).
|
package/dist/intent-match.mjs
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
|
-
// intent-match.mjs — how well a
|
|
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
|
|
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
|
|
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
|
|
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
|
|
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 (
|
|
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',
|
package/dist/intent-reader.mjs
CHANGED
|
@@ -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
|
|
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
|
|
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
|
'',
|
package/dist/lib/board-text.mjs
CHANGED
|
@@ -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-
|
|
10
|
-
// | 'first-high-risk-
|
|
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-
|
|
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-
|
|
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-
|
|
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
|
|
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
|
|
8
|
-
// writes into `
|
|
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 `
|
|
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
|
-
`"
|
|
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
|
|
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.
|
package/dist/lib/kickoff-cli.mjs
CHANGED
|
@@ -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
|
|
8
|
-
// `projectRoot()` answers the first two. For the third it answers `
|
|
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 "$
|
|
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
|
|
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
|
|
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
|
|
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
|
|
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
|
|
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}$/;
|
package/dist/lib/stage.mjs
CHANGED
|
@@ -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
|
-
//
|
|
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
|
//
|
package/dist/lib/surface.mjs
CHANGED
|
@@ -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
|
|
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
|
|
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
|
//
|
package/dist/review-route.mjs
CHANGED
|
@@ -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 `
|
|
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 `
|
|
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
|
|
110
|
+
l.push(` then: use the verifier subagent on PR #${pr}`);
|
|
111
111
|
return l.join('\n');
|
|
112
112
|
}
|
|
113
113
|
|
package/dist/roadmap-extract.mjs
CHANGED
|
@@ -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
|
|
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,
|
package/dist/routines/README.md
CHANGED
|
@@ -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
|
|
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
|
-
|
|
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
|
-
`
|
|
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 `
|
|
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
|
|
16
|
-
report step).
|
|
15
|
+
human-confirmed action — this routine only ever runs the dry-run report).
|
|
17
16
|
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
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
|
-
-
|
|
24
|
-
-
|
|
25
|
-
-
|
|
26
|
-
- `
|
|
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 —
|
|
44
|
-
|
|
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 —
|
|
49
|
-
|
|
50
|
-
|
|
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 —
|
|
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
|
|
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 —
|
|
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 `
|
|
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
|
-
- `
|
|
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 `
|
|
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 **`
|
|
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:
|
|
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`
|
|
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
|
-
|
|
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.
|
|
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.**
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
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
|
|
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 `
|
|
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
|
-
- `
|
|
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 `
|
|
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 `
|
|
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 `
|
|
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** — `
|
|
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", `
|
|
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 `
|
|
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
|
|
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; `
|
|
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 `
|
|
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:
|
|
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 ("
|
|
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
|
|
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;
|
|
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 `
|
|
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.
|
|
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": {
|