@skyf0xx/hedgehog 6.3.4 → 6.4.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/bin/cli.mjs CHANGED
@@ -14,6 +14,7 @@
14
14
  // npx @skyf0xx/hedgehog --help
15
15
 
16
16
  import { cp, mkdir, access, readdir, stat, rm, readFile, writeFile, mkdtemp } from 'node:fs/promises';
17
+ import { Buffer } from 'node:buffer';
17
18
  import { constants, existsSync, realpathSync } from 'node:fs';
18
19
  import { fileURLToPath } from 'node:url';
19
20
  import { dirname, join, relative, resolve } from 'node:path';
@@ -104,7 +105,7 @@ import { NOOP_DIR } from '../src/db/noop.mjs';
104
105
  import { runFastpath, loadFastpaths, orphanedFastpathTasks, FASTPATH_DIR } from '../src/db/fastpath.mjs';
105
106
  import { HOSTS, HOST_FLAGS, DEFAULT_HOST, availableHosts } from '../src/hosts/index.mjs';
106
107
  import { recordHosts, installedHosts } from '../src/hosts/installed.mjs';
107
- import { wrapSection } from '../src/hosts/claude-md-merge.mjs';
108
+ import { wrapSection, stripPhaseBlocks } from '../src/hosts/claude-md-merge.mjs';
108
109
  import {
109
110
  recordVersion,
110
111
  checkForUpdate,
@@ -372,6 +373,11 @@ function corePayload(core, h, { hostOnly = false } = {}) {
372
373
  // re-vendor, per each shelf's ATTRIBUTION.md) — none of those belong in
373
374
  // an update, so a core's `workspace` and `vendor_skills` are left alone
374
375
  // here while its agents and skills are refreshed.
376
+ // The bootstrap file is excluded here on purpose: `hedgehog shed` (and
377
+ // writePlannedFile's own self-heal) is the only thing allowed to remove
378
+ // content from it, and an update pass that also touched it would race
379
+ // that removal against whatever bootstrap-only content a fresh shell
380
+ // still carries.
375
381
  function updatePlan(host = DEFAULT_HOST, core = null) {
376
382
  const h = HOSTS[host];
377
383
  return [
@@ -478,6 +484,98 @@ function warnOrphanedNotes({ orphanedNotes }) {
478
484
  console.log('');
479
485
  }
480
486
 
487
+ const PHASE_NAME = 'bootstrap-only';
488
+
489
+ // Engine-generic conditions for "this project's build graph is past
490
+ // bootstrap" — never core-specific facts like nx.json or
491
+ // astro.config.mjs, which the engine has no business knowing about. Both
492
+ // must hold: a build graph exists, and at least one task has been
493
+ // compiled into it. The bootstrap file's own {{PROJECT_SUMMARY}}
494
+ // placeholder is the third condition (see shedCommand and
495
+ // writePlannedFile's self-heal) but is checked per-file, since a
496
+ // multi-host project can have several bootstrap files to test.
497
+ async function graphPastBootstrap() {
498
+ if (!(await exists(DB_PATH))) return false;
499
+ const db = openDb();
500
+ try {
501
+ return db.prepare('SELECT 1 FROM tasks LIMIT 1').get() !== undefined;
502
+ } finally {
503
+ db.close();
504
+ }
505
+ }
506
+
507
+ async function canShed(bootstrapContent) {
508
+ if (!(await graphPastBootstrap())) return false;
509
+ return !bootstrapContent.includes('{{PROJECT_SUMMARY}}');
510
+ }
511
+
512
+ // `hedgehog shed` — strips bootstrap-only content from every host
513
+ // bootstrap file actually on disk, once the project has provably moved
514
+ // past bootstrap. Guard conditions are engine-generic only (see
515
+ // graphPastBootstrap/canShed above); this command names which one failed
516
+ // rather than a single generic refusal, in the style of `hedgehog
517
+ // boundary`.
518
+ async function shedCommand() {
519
+ if (!(await exists(DB_PATH))) {
520
+ console.error(
521
+ `${red('No build graph found.')} ${bold(dbAbsPath())} does not exist — run ${bold('hedgehog init')} or ${bold('hedgehog db init')} first.\n`,
522
+ );
523
+ process.exitCode = 1;
524
+ return;
525
+ }
526
+ if (!(await graphPastBootstrap())) {
527
+ console.error(
528
+ `${red('No task has been compiled into the build graph yet.')} Run ${bold('hedgehog plan')} first.\n`,
529
+ );
530
+ process.exitCode = 1;
531
+ return;
532
+ }
533
+
534
+ const hosts = await installedHosts(DEST_ROOT);
535
+ const bootstrapFiles = [
536
+ ...new Set(hosts.map((name) => HOSTS[name].bootstrapFile).filter(Boolean)),
537
+ ];
538
+
539
+ const present = [];
540
+ for (const rel of bootstrapFiles) {
541
+ const abs = join(DEST_ROOT, rel);
542
+ if (await exists(abs)) present.push({ rel, abs });
543
+ }
544
+ if (present.length === 0) {
545
+ console.error(`${red('No bootstrap file found.')} Expected one of: ${bootstrapFiles.join(', ')}\n`);
546
+ process.exitCode = 1;
547
+ return;
548
+ }
549
+
550
+ const unfilled = [];
551
+ for (const { rel, abs } of present) {
552
+ const content = await readFile(abs, 'utf8');
553
+ if (content.includes('{{PROJECT_SUMMARY}}')) unfilled.push(rel);
554
+ }
555
+ if (unfilled.length > 0) {
556
+ console.error(
557
+ `${red('{{PROJECT_SUMMARY}} is still unfilled in:')} ${unfilled.join(', ')} — nothing has been built yet.\n`,
558
+ );
559
+ process.exitCode = 1;
560
+ return;
561
+ }
562
+
563
+ let anyStripped = false;
564
+ for (const { rel, abs } of present) {
565
+ const before = await readFile(abs, 'utf8');
566
+ const after = stripPhaseBlocks(before, PHASE_NAME);
567
+ if (after === before) continue;
568
+ anyStripped = true;
569
+ await writeFile(abs, after);
570
+ const beforeBytes = Buffer.byteLength(before, 'utf8').toLocaleString('en-US');
571
+ const afterBytes = Buffer.byteLength(after, 'utf8').toLocaleString('en-US');
572
+ console.log(`${rel} ${beforeBytes} → ${afterBytes} B`);
573
+ }
574
+ if (!anyStripped) {
575
+ console.log('nothing to shed');
576
+ }
577
+ }
578
+
481
579
  // Writes one planned file to disk — a straight copy, or for a `merge`
482
580
  // entry, the shell template with {{CORE_SECTION}} replaced by the
483
581
  // chosen core's include.
@@ -528,7 +626,12 @@ async function writePlannedFile(f) {
528
626
  out = out.replaceAll('{{CORE_SECTION}}', wrapSection(section));
529
627
  }
530
628
  const dispatch = await readFile(join(PKG_ROOT, f.merge.dispatch), 'utf8');
531
- await writeFile(f.dest, out.replaceAll('{{HOST_DISPATCH}}', dispatch.trimEnd()));
629
+ out = out.replaceAll('{{HOST_DISPATCH}}', dispatch.trimEnd());
630
+ // Self-heal: a project past bootstrap that re-runs `init --force`
631
+ // must not silently regain bootstrap-only content just because this
632
+ // write started from the pristine shell again.
633
+ if (await canShed(out)) out = stripPhaseBlocks(out, PHASE_NAME);
634
+ await writeFile(f.dest, out);
532
635
  return;
533
636
  }
534
637
  // Rendered from the payload rather than copied from it — the routing
@@ -675,6 +778,8 @@ ${bold('Usage')}
675
778
  npx @skyf0xx/hedgehog debt resolve <debt-id> --reason "<why>" mark a debt note resolved
676
779
  npx @skyf0xx/hedgehog decision add <task-id> "<note>" declare a decision that lands in dependent tasks' packets
677
780
  npx @skyf0xx/hedgehog decision list [<task-id>] list declared decisions, oldest first
781
+ npx @skyf0xx/hedgehog shed strip bootstrap-only content from the installed
782
+ bootstrap file(s), once the project is past bootstrap
678
783
  npx @skyf0xx/hedgehog db migrate bring the graph's schema up to the latest version
679
784
  npx @skyf0xx/hedgehog community star --answer <a> record the star prompt's answer
680
785
  npx @skyf0xx/hedgehog community showcase --repo <url> [--description <text>]
@@ -4547,6 +4652,11 @@ async function main() {
4547
4652
  return;
4548
4653
  }
4549
4654
 
4655
+ if (cmd === 'shed') {
4656
+ await shedCommand();
4657
+ return;
4658
+ }
4659
+
4550
4660
  console.error(`${red('Unknown command:')} ${cmd}\n`);
4551
4661
  await help();
4552
4662
  process.exitCode = 1;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@skyf0xx/hedgehog",
3
- "version": "6.3.4",
3
+ "version": "6.4.0",
4
4
  "description": "Install the Hedgehog build discipline (agents + skills) into a repo, for Claude Code, Cursor, or Gemini CLI.",
5
5
  "type": "module",
6
6
  "repository": {
@@ -61,9 +61,11 @@ workspace is scaffolded (see `planner.md`'s Workflow step 7 and step 9).
61
61
  This compiles those intents into tasks so the core's loop skill has
62
62
  something to pick up from `hedgehog next`. Then run `hedgehog graph` to
63
63
  start (or reuse) the live graph server and open it, so the build graph is
64
- on screen before the first build step starts. Then state plainly that
65
- Bootstrap is closed and name the loop skill that owns everything from
66
- here. Don't hand off to another instance of yourself.
64
+ on screen before the first build step starts. Run `hedgehog shed` to
65
+ strip bootstrap-only content from the bootstrap file now that it no
66
+ longer applies. Then state plainly that Bootstrap is closed and name the
67
+ loop skill that owns everything from here. Don't hand off to another
68
+ instance of yourself.
67
69
 
68
70
 
69
71
  ## Constraints
package/src/db/ready.mjs CHANGED
@@ -7,6 +7,7 @@
7
7
 
8
8
  import { findClaimableTasks, findInFlightTasks } from './claim.mjs';
9
9
  import { conflicts, verifyRadius } from './conflict.mjs';
10
+ import { ORCHESTRATING_FOOTER } from './status.mjs';
10
11
 
11
12
  // Walks the same candidates claimTasks would, in the same priority/id
12
13
  // order, greedily sorting each into CLAIMABLE (doesn't conflict with
@@ -92,5 +93,8 @@ export function formatReady({ claimable, heldBack }) {
92
93
  }
93
94
  }
94
95
 
96
+ lines.push('');
97
+ lines.push(ORCHESTRATING_FOOTER);
98
+
95
99
  return lines.join('\n');
96
100
  }
package/src/db/status.mjs CHANGED
@@ -274,6 +274,12 @@ export async function graphWorktreeStatus(db, opts) {
274
274
  return worktreeStatus(db, opts);
275
275
  }
276
276
 
277
+ // Printed at the bottom of both `hedgehog status` and `hedgehog ready` —
278
+ // the two commands every session provably runs — since no static check
279
+ // can verify an orchestrating session actually reads the skill this
280
+ // names.
281
+ export const ORCHESTRATING_FOOTER = 'See the hedgehog-orchestrating skill for the claim → dispatch → verify cycle.';
282
+
277
283
  const BLOCKED_REASON_LABELS = {
278
284
  verification_failed: 'verification failed',
279
285
  scope_violation: 'scope violation',
@@ -470,5 +476,8 @@ export function formatStatus({
470
476
  lines.push(' See: hedgehog reconcile list');
471
477
  }
472
478
 
479
+ lines.push('');
480
+ lines.push(ORCHESTRATING_FOOTER);
481
+
473
482
  return lines.join('\n');
474
483
  }
@@ -6,14 +6,5 @@ context with its own tool grant. The skills in `.claude/skills/` are
6
6
  available the same way; invoke one by name rather than reimplementing what
7
7
  it describes.
8
8
 
9
- Claude Code reads that registration once, at session start — an agent or
10
- skill file written mid-session (by `hedgehog init` or `hedgehog update`
11
- just now) is not yet dispatchable by name in this session. If a name-based
12
- dispatch reports it as not found, read the file directly from
13
- `.claude/agents/` or `.claude/skills/` and follow it inline instead of
14
- retrying the dispatch; it becomes dispatchable by name after a session
15
- restart or a fresh context.
16
-
17
- Clear context with `/clear` at the unit boundaries described above —
18
- `hedgehog boundary` tells you whether you're at one (exit 0), and
19
- `hedgehog boundary --handoff` is what the next session starts from.
9
+ Clear context with `/clear` at the unit boundaries the
10
+ `hedgehog-orchestrating` skill states.
@@ -64,6 +64,74 @@ export function appendCoreSection(existingContent, section) {
64
64
  return `${existingContent.trimEnd()}\n\n${block}\n`;
65
65
  }
66
66
 
67
+ // One phase name only, `bootstrap-only` — there is exactly one
68
+ // transition in a project's life (still bootstrapping vs. not), so a
69
+ // predicate grammar for a one-bit state would be a maintenance burden
70
+ // with no second case to justify it.
71
+ const PHASE_MARKER_START = (phase) => `<!-- hedgehog:${phase} start -->`;
72
+ const PHASE_MARKER_END = (phase) => `<!-- hedgehog:${phase} end -->`;
73
+
74
+ // Removes every `<!-- hedgehog:<phase> start -->...end -->` block from
75
+ // `content`, along with the blank lines immediately surrounding each
76
+ // removed block, so stripping never leaves a double blank line behind.
77
+ // Byte-for-byte identity when no markers are present is the contract
78
+ // every core package not yet using markers relies on.
79
+ //
80
+ // A block is self-contained by convention (no section outside it may
81
+ // reference into it), so removal never leaves a dangling reference for
82
+ // this function to worry about — that's enforced by review of what gets
83
+ // marked, not by this code.
84
+ export function stripPhaseBlocks(content, phase) {
85
+ const start = PHASE_MARKER_START(phase);
86
+ const end = PHASE_MARKER_END(phase);
87
+ const startCount = countOccurrences(content, start);
88
+ const endCount = countOccurrences(content, end);
89
+ if (startCount === 0 && endCount === 0) return content;
90
+ if (startCount !== endCount) {
91
+ throw new Error(
92
+ `stripPhaseBlocks: ${startCount} "${start}" marker(s) but ${endCount} "${end}" marker(s) — unterminated or orphaned`,
93
+ );
94
+ }
95
+
96
+ // Flat, non-nesting blocks only: a start found before the matching end
97
+ // of the previous block indicates nesting, which this vocabulary
98
+ // deliberately does not support.
99
+ const blockRe = new RegExp(`${escapeRe(start)}[\\s\\S]*?${escapeRe(end)}`, 'g');
100
+ // Not a valid marker (the phase name can't contain whitespace), and not
101
+ // realistic page content either, so it can't collide with anything
102
+ // already in `content` — used as a removal placeholder instead of a
103
+ // control character, which linting disallows anywhere a regex pattern
104
+ // could embed it.
105
+ const PLACEHOLDER = '⁣hedgehog-stripped-block⁣';
106
+ let matchCount = 0;
107
+ let stripped = content.replace(blockRe, () => {
108
+ matchCount++;
109
+ return PLACEHOLDER;
110
+ });
111
+ if (matchCount !== startCount) {
112
+ throw new Error(
113
+ `stripPhaseBlocks: found ${startCount} "${start}" marker(s) but only ${matchCount} well-formed block(s) — check for nesting or an orphaned marker`,
114
+ );
115
+ }
116
+
117
+ // Each removed block leaves a placeholder; collapse it and the blank
118
+ // lines around it so no double blank line remains.
119
+ const placeholderRe = new RegExp(`[ \\t]*\\n?[ \\t]*${escapeRe(PLACEHOLDER.trim())}[ \\t]*\\n?[ \\t]*\\n?`, 'g');
120
+ stripped = stripped.replace(placeholderRe, '\n');
121
+ stripped = stripped.replace(/\n{3,}/g, '\n\n');
122
+ return stripped;
123
+ }
124
+
125
+ function countOccurrences(haystack, needle) {
126
+ let count = 0;
127
+ let i = 0;
128
+ while ((i = haystack.indexOf(needle, i)) !== -1) {
129
+ count++;
130
+ i += needle.length;
131
+ }
132
+ return count;
133
+ }
134
+
67
135
  function escapeRe(s) {
68
136
  return s.replace(/[.*+?^${}()|[\]\\]/g, '\\$&');
69
137
  }
@@ -16,6 +16,5 @@ gates the commit either way.
16
16
  The skills in `.cursor/skills/` are procedures to follow — read the one
17
17
  whose situation applies rather than improvising the steps.
18
18
 
19
- Clear the conversation at the unit boundaries described above —
20
- `hedgehog boundary` tells you whether you're at one (exit 0), and
21
- `hedgehog boundary --handoff` is what the next session starts from.
19
+ Clear the conversation at the unit boundaries the `hedgehog-orchestrating`
20
+ skill states.
@@ -22,8 +22,7 @@ commit lands.
22
22
  The skills in `.gemini/skills/` are procedures to follow — read the one
23
23
  whose situation applies rather than improvising the steps.
24
24
 
25
- Clear the conversation at the unit boundaries described above —
26
- `hedgehog boundary` tells you whether you're at one (exit 0), and
27
- `hedgehog boundary --handoff` is what the next session starts from.
25
+ Clear the conversation at the unit boundaries the `hedgehog-orchestrating`
26
+ skill states.
28
27
 
29
28
  @./AGENTS.md
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "hedgehog",
3
- "version": "6.3.4",
3
+ "version": "6.4.0",
4
4
  "description": "Hedgehog build discipline: ordered, tested, verified build steps.",
5
5
  "contextFileName": "GEMINI.md"
6
6
  }
@@ -0,0 +1,154 @@
1
+ ---
2
+ name: hedgehog-orchestrating
3
+ description: Use at the start of any session on a project with a build graph (`.hedgehog/hedgehog.db`), and again at every claim/dispatch/verify cycle within it. Owns consuming the build graph — claiming task packets, delegating each to the layer's agent, running `hedgehog verify` to gate the commit, recording debt and decisions, the intent check at an intent's last layer, and when to clear context. Not for a build agent handed an already-claimed packet — that agent never claims, dispatches, or verifies; this skill is the orchestrating session's own.
4
+ ---
5
+
6
+ # Orchestrating the build
7
+
8
+ `.hedgehog/hedgehog.db` is the source of truth for what's next — never
9
+ re-derive build state from prose. To work from it:
10
+
11
+ 1. Run `hedgehog claim --count N --owner <owner>`. It's atomic and
12
+ lease-based, and returns up to N tasks (each with its own full
13
+ STATUS/INTENT/RELEVANT RULES/INHERITED DEBT/INHERITED DECISIONS/WHY
14
+ NOW/BLOCKED DOWNSTREAM/ALLOWED SCOPE/VERIFICATION packet) that the
15
+ scheduler has already verified are safe to run together right now —
16
+ scope and verify-radius disjoint. `--count` is a maximum, not a
17
+ promise: a call may return fewer than N, or zero. `hedgehog ready` is
18
+ a read-only preview of the claimable/held-back split (and why a task
19
+ is held back — conflict with another claimable task, or exclusivity)
20
+ before you claim.
21
+ 2. Delegate each claimed packet to this core's loop skill (named in the
22
+ project's core section), one dispatch per packet, running concurrently.
23
+ 3. As each agent reports its packet done, run `hedgehog verify
24
+ <task-id> --owner <owner>` **serially** — one at a time, even though
25
+ the building happened concurrently, because verify writes git commits
26
+ and those must land one at a time. It checks the touched files
27
+ against the packet's ALLOWED SCOPE, runs the verification command,
28
+ and on a pass writes the commit and unlocks whatever the task was
29
+ blocking. An agent reporting success never moves the task — only a
30
+ passing `hedgehog verify` exit code does.
31
+
32
+ `hedgehog claim` hands out only tasks safe to run together. Never run
33
+ two tasks it didn't hand you together.
34
+
35
+ The packet's **INTENT** block names the goal and outcome of the *whole*
36
+ intent, not just this layer. A layer's own verify command runs the tests
37
+ that layer wrote, so it measures internal consistency and never coverage
38
+ of what was asked — a layer that builds half the intent and tests that
39
+ half exhaustively is green. Build the layer's share of the goal, and say
40
+ so when the packet doesn't account for something the goal asks for.
41
+ When `hedgehog verify` closes the **last** layer of an intent it prints
42
+ that goal and outcome back as an **INTENT CHECK**: read the built work
43
+ against it there, because nothing else in the build does.
44
+
45
+ A layer that discovers a limitation the next layer has to compensate for
46
+ records it with `hedgehog debt add <task-id> "<note>"` — it lands in the
47
+ **INHERITED DEBT** section of every packet that depends on that task. A
48
+ comment in a source file is not a mechanism; nothing reads it.
49
+
50
+ A layer that makes a choice a dependent layer needs to know about — a
51
+ pattern, a library, a trade-off, anything the next task should follow
52
+ rather than reinvent or contradict — records it with `hedgehog decision
53
+ add <task-id> "<note>"`, landing in the **INHERITED DECISIONS** section
54
+ the same way. Debt is what's still wrong with a task; a decision is why
55
+ it was built the way it was.
56
+
57
+ `planner` owns writing intents (`hedgehog intent add`) at planning
58
+ intake; `hedgehog plan` compiles them into the task graph the loop
59
+ consumes. Nothing checks a box — there is no checklist, only queryable
60
+ state.
61
+
62
+ **When the build is done:** once `hedgehog status` shows every task
63
+ `complete` and `hedgehog boundary` exits 0 (see **Managing context**
64
+ below — it checks nothing-in-flight, a clean tree, and a closed intent
65
+ together), the build session is complete. The permanent record is the
66
+ committed intents (`.hedgehog/intents/*.json`), the friction log
67
+ (`.hedgehog/friction/*.md`), the core definition (root `core.yaml` for a
68
+ shipped core, `.hedgehog/core.yaml` for an authored one), and the git
69
+ commit history itself — not the database. `.hedgehog/hedgehog.db` is
70
+ gitignored: a derived index, rebuildable at any time via `hedgehog db
71
+ rebuild`, which replays those committed sources against git history.
72
+ That rebuild also runs automatically on a fresh clone when the DB is
73
+ missing but `.hedgehog/intents/` exists. That's what makes every later
74
+ session cheap.
75
+
76
+ A completed build is **extendable, not sealed**. Offer the user a
77
+ fresh-context handoff, and name both ways forward:
78
+
79
+ - **Adjustments to what's built** → the `tweaker` agent, from a *new*
80
+ chat window, not a subagent call inside this one — this session's
81
+ context has been building the whole project and is exactly what
82
+ "clearing context now costs nothing" (below) means to discard. Tell
83
+ the user plainly: close this chat window and open a new one, then
84
+ paste this to start it:
85
+
86
+ > The build for {{PROJECT_NAME}} is complete. Use the tweaker agent:
87
+ > first review the friction log and ask me for feedback on the build,
88
+ > then take my tweak requests one at a time.
89
+
90
+ In the new window, `tweaker` starts clean, once reviews the friction
91
+ log (`hedgehog friction list`) for possible discipline-improvement
92
+ issues and separately asks the user directly for feedback on the
93
+ build, filing each real pattern or piece of feedback as its own GitHub
94
+ issue against the Hedgehog repo itself, never this project's repo
95
+ (friction as `bug`/`help wanted`, feedback as `suggestion`, each only
96
+ after showing the exact content and getting explicit approval), then
97
+ takes any tweak requests one at a time.
98
+ - **New scope** — a new module or feature, anything beyond adjusting what
99
+ exists → on a core with a module axis, the `planner` agent, which runs
100
+ `hedgehog-planning-intake`'s **Re-entry pass**. It reads the existing
101
+ planning archive as context and elicits only what's new, then adds
102
+ intents and runs `hedgehog plan`. This is append-only: `plan` skips
103
+ intents already compiled, so every `complete` task keeps its status and
104
+ its commits, and `hedgehog claim` resumes at the first tasks of the new
105
+ work. Planning is not re-run from scratch, and the workspace is not
106
+ re-scaffolded. (This core's own section states where new scope goes if
107
+ this core has no module axis to add an intent to.)
108
+
109
+ If a request turns out to be structural rather than either of those —
110
+ something already built is wrong at its source — that's the Correction
111
+ Protocol's post-build entry, in this core's own loop skill.
112
+
113
+ ## Managing context
114
+
115
+ Hedgehog is designed so the conversation is disposable. Keep the working
116
+ context small:
117
+
118
+ - **Clear context at natural boundaries** — a module's Phase A, a
119
+ landing page section, whatever this core's own unit boundary is — once
120
+ that unit is done and committed. Ask `hedgehog boundary` rather than
121
+ judging it: it exits 0 only when all three of nothing-in-flight, a
122
+ clean working tree, and a last closed task that completed its intent
123
+ hold, and names which one failed otherwise. Clear the conversation and
124
+ start fresh, then run `hedgehog status`/`hedgehog claim` and continue.
125
+ Nothing is lost, because the build graph, commits, and code hold all
126
+ the state. Prefer this over letting one session accumulate the entire
127
+ project.
128
+ - **`hedgehog quiesce` and `hedgehog boundary` answer different
129
+ questions.** `quiesce` reports whether anything is still in flight —
130
+ necessary before clearing (clearing while a lease is outstanding
131
+ orphans that lease until it expires), but not sufficient: a graph can
132
+ be perfectly settled halfway through an intent, with a dirty working
133
+ tree. `boundary` is the whole question — is this a moment to throw the
134
+ conversation away — and it includes the `quiesce` check as its first
135
+ condition. Use `quiesce` when you're waiting for dispatched work to
136
+ land (the Correction Protocol), `boundary` when you're deciding whether
137
+ to clear.
138
+ - **A cleared or new session recovers by running `hedgehog status` and
139
+ reading the commit log**, never by needing the prior conversation.
140
+ `hedgehog boundary --handoff` prints that recovery block directly —
141
+ where the build is, what's next and why, what's in flight, what's
142
+ blocked — derived from the graph, so no session hands a summary to the
143
+ next one.
144
+ - **Delegate heavy work to agents.** Scaffolding and every build step
145
+ run in their own isolated context, so work doesn't pile up in the
146
+ main thread. Planning intake's BMAD Phase 0 is the exception — the
147
+ project's own instructions file states it — and stays in that session
148
+ through Confirm & Lock; the mining, `bootstrap`, and per-module steps
149
+ after it delegate as usual.
150
+ - **Don't paste large context back in.** If you find yourself
151
+ re-explaining the architecture, stop — it's fixed and stated in the
152
+ project's core section, not something to reconstruct. If you need a
153
+ project specific, read it from the code. That's the self-documenting
154
+ design working as intended.
@@ -1,3 +1,4 @@
1
+ <!-- hedgehog:bootstrap-only start -->
1
2
  <!--
2
3
  Hedgehog project CLAUDE.md template.
3
4
 
@@ -11,6 +12,7 @@
11
12
 
12
13
  Delete this comment block after the placeholders are filled in.
13
14
  -->
15
+ <!-- hedgehog:bootstrap-only end -->
14
16
 
15
17
  # {{PROJECT_NAME}}
16
18
 
@@ -24,6 +26,7 @@ This project is built with **Hedgehog**: a one-step-at-a-time build
24
26
  discipline. The rules below aren't project preferences — they're how the
25
27
  build stays mechanically correct. Follow them exactly.
26
28
 
29
+ <!-- hedgehog:bootstrap-only start -->
27
30
  ## First message in a fresh install
28
31
 
29
32
  If `{{PROJECT_SUMMARY}}` above is still an unfilled placeholder, this is a
@@ -51,6 +54,7 @@ read the file and carry on.
51
54
  Don't re-explain the discipline or summarize this file; the greeting is
52
55
  one line, not a tour. Skip this entirely once the placeholder is filled
53
56
  in — every later session starts with `hedgehog status`, not a greeting.
57
+ <!-- hedgehog:bootstrap-only end -->
54
58
 
55
59
  ## How to work here
56
60
 
@@ -74,8 +78,8 @@ whole plan in context — the plan lives in the structure:
74
78
 
75
79
  Because state lives in those places and not in the conversation, a fresh
76
80
  context loses nothing: the architecture is known a priori, and the
77
- project's specifics are re-read on demand. Use that (see **Managing
78
- context** below).
81
+ project's specifics are re-read on demand. The `hedgehog-orchestrating`
82
+ skill (see **Running the build** below) is what uses that.
79
83
 
80
84
  **Use only the skills and agents this repo provides**, including its
81
85
  vendored BMAD shelf — never a general-purpose build-tool skill pack
@@ -97,154 +101,12 @@ taking the faster path.
97
101
 
98
102
  {{CORE_SECTION}}
99
103
 
100
- ## Consuming the graph
101
-
102
- `.hedgehog/hedgehog.db` is the source of truth for what's next — never
103
- re-derive build state from prose. To work from it:
104
-
105
- 1. Run `hedgehog claim --count N --owner <owner>`. It's atomic and
106
- lease-based, and returns up to N tasks (each with its own full
107
- STATUS/INTENT/RELEVANT RULES/INHERITED DEBT/INHERITED DECISIONS/WHY
108
- NOW/BLOCKED DOWNSTREAM/ALLOWED SCOPE/VERIFICATION packet) that the
109
- scheduler has already verified are safe to run together right now —
110
- scope and verify-radius disjoint. `--count` is a maximum, not a
111
- promise: a call may return fewer than N, or zero. `hedgehog ready` is
112
- a read-only preview of the claimable/held-back split (and why a task
113
- is held back — conflict with another claimable task, or exclusivity)
114
- before you claim.
115
- 2. Delegate each claimed packet to this core's loop skill (named in the
116
- section above), one dispatch per packet, running concurrently.
117
- 3. As each agent reports its packet done, run `hedgehog verify
118
- <task-id> --owner <owner>` **serially** — one at a time, even though
119
- the building happened concurrently, because verify writes git commits
120
- and those must land one at a time. It checks the touched files
121
- against the packet's ALLOWED SCOPE, runs the verification command,
122
- and on a pass writes the commit and unlocks whatever the task was
123
- blocking. An agent reporting success never moves the task — only a
124
- passing `hedgehog verify` exit code does.
125
-
126
- `hedgehog claim` hands out only tasks safe to run together. Never run
127
- two tasks it didn't hand you together.
128
-
129
- The packet's **INTENT** block names the goal and outcome of the *whole*
130
- intent, not just this layer. A layer's own verify command runs the tests
131
- that layer wrote, so it measures internal consistency and never coverage
132
- of what was asked — a layer that builds half the intent and tests that
133
- half exhaustively is green. Build the layer's share of the goal, and say
134
- so when the packet doesn't account for something the goal asks for.
135
- When `hedgehog verify` closes the **last** layer of an intent it prints
136
- that goal and outcome back as an **INTENT CHECK**: read the built work
137
- against it there, because nothing else in the build does.
138
-
139
- A layer that discovers a limitation the next layer has to compensate for
140
- records it with `hedgehog debt add <task-id> "<note>"` — it lands in the
141
- **INHERITED DEBT** section of every packet that depends on that task. A
142
- comment in a source file is not a mechanism; nothing reads it.
143
-
144
- A layer that makes a choice a dependent layer needs to know about — a
145
- pattern, a library, a trade-off, anything the next task should follow
146
- rather than reinvent or contradict — records it with `hedgehog decision
147
- add <task-id> "<note>"`, landing in the **INHERITED DECISIONS** section
148
- the same way. Debt is what's still wrong with a task; a decision is why
149
- it was built the way it was.
150
-
151
- `planner` owns writing intents (`hedgehog intent add`) at planning
152
- intake; `hedgehog plan` compiles them into the task graph the loop
153
- consumes. Nothing checks a box — there is no checklist, only queryable
154
- state.
155
-
156
- **When the build is done:** once `hedgehog status` shows every task
157
- `complete` and `hedgehog boundary` exits 0 (see **Managing context**
158
- below — it checks nothing-in-flight, a clean tree, and a closed intent
159
- together), the build session is complete. The permanent record is the committed
160
- intents (`.hedgehog/intents/*.json`), the friction log
161
- (`.hedgehog/friction/*.md`), the core definition (root `core.yaml` for a
162
- shipped core, `.hedgehog/core.yaml` for an authored one), and the git
163
- commit history itself — not the database. `.hedgehog/hedgehog.db` is gitignored: a
164
- derived index, rebuildable at any time via `hedgehog db rebuild`, which
165
- replays those committed sources against git history. That rebuild also
166
- runs automatically on a fresh clone when the DB is missing but
167
- `.hedgehog/intents/` exists. That's what makes every later session
168
- cheap.
169
-
170
- A completed build is **extendable, not sealed**. Offer the user a
171
- fresh-context handoff, and name both ways forward:
172
-
173
- - **Adjustments to what's built** → the `tweaker` agent, from a *new*
174
- chat window, not a subagent call inside this one — this session's
175
- context has been building the whole project and is exactly what
176
- "clearing context now costs nothing" (above) means to discard. Tell
177
- the user plainly: close this chat window and open a new one, then
178
- paste this to start it:
179
-
180
- > The build for {{PROJECT_NAME}} is complete. Use the tweaker agent:
181
- > first review the friction log and ask me for feedback on the build,
182
- > then take my tweak requests one at a time.
183
-
184
- In the new window, `tweaker` starts clean, once reviews the friction
185
- log (`hedgehog friction list`) for possible discipline-improvement
186
- issues and separately asks the user directly for feedback on the
187
- build, filing each real pattern or piece of feedback as its own GitHub
188
- issue against the Hedgehog repo itself, never this project's repo
189
- (friction as `bug`/`help wanted`, feedback as `suggestion`, each only
190
- after showing the exact content and getting explicit approval), then
191
- takes any tweak requests one at a time.
192
- - **New scope** — a new module or feature, anything beyond adjusting what
193
- exists → on a core with a module axis, the `planner` agent, which runs
194
- `hedgehog-planning-intake`'s **Re-entry pass**. It reads the existing
195
- planning archive as context and elicits only what's new, then adds
196
- intents and runs `hedgehog plan`. This is append-only: `plan` skips
197
- intents already compiled, so every `complete` task keeps its status and
198
- its commits, and `hedgehog claim` resumes at the first tasks of the new
199
- work. Planning is not re-run from scratch, and the workspace is not
200
- re-scaffolded. (This core's own section above states where new scope
201
- goes if this core has no module axis to add an intent to.)
202
-
203
- If a request turns out to be structural rather than either of those —
204
- something already built is wrong at its source — that's the Correction
205
- Protocol's post-build entry, in this core's own loop skill.
206
-
207
- ## Managing context
208
-
209
- Hedgehog is designed so the conversation is disposable. Keep the working
210
- context small:
211
-
212
- - **Clear context at natural boundaries** — a module's Phase A, a
213
- landing page section, whatever this core's own unit boundary is — once
214
- that unit is done and committed. Ask `hedgehog boundary` rather than
215
- judging it: it exits 0 only when all three of nothing-in-flight, a
216
- clean working tree, and a last closed task that completed its intent
217
- hold, and names which one failed otherwise. Clear the conversation and
218
- start fresh, then run `hedgehog status`/`hedgehog claim` and continue.
219
- Nothing is lost, because the build graph, commits, and code hold all
220
- the state. Prefer this over letting one session accumulate the entire
221
- project.
222
- - **`hedgehog quiesce` and `hedgehog boundary` answer different
223
- questions.** `quiesce` reports whether anything is still in flight —
224
- necessary before clearing (clearing while a lease is outstanding
225
- orphans that lease until it expires), but not sufficient: a graph can
226
- be perfectly settled halfway through an intent, with a dirty working
227
- tree. `boundary` is the whole question — is this a moment to throw the
228
- conversation away — and it includes the `quiesce` check as its first
229
- condition. Use `quiesce` when you're waiting for dispatched work to
230
- land (the Correction Protocol), `boundary` when you're deciding whether
231
- to clear.
232
- - **A cleared or new session recovers by running `hedgehog status` and
233
- reading the commit log**, never by needing the prior conversation.
234
- `hedgehog boundary --handoff` prints that recovery block directly —
235
- where the build is, what's next and why, what's in flight, what's
236
- blocked — derived from the graph, so no session hands a summary to the
237
- next one.
238
- - **Delegate heavy work to agents.** Scaffolding and every build step
239
- run in their own isolated context, so work doesn't pile up in the
240
- main thread. Planning intake's BMAD Phase 0 is the exception — see
241
- **First message in a fresh install** above — and stays here through
242
- Confirm & Lock; the mining, `bootstrap`, and per-module steps after it
243
- delegate as usual.
244
- - **Don't paste large context back in.** If you find yourself
245
- re-explaining the architecture, stop — it's fixed and stated in this
246
- file's core section, not something to reconstruct. If you need a
247
- project specific, read it from the code. That's the self-documenting
248
- design working as intended.
104
+ ## Running the build
105
+
106
+ The build graph is the source of truth for what's next — never re-derive
107
+ build state from prose. The `hedgehog-orchestrating` skill owns the claim →
108
+ dispatch → verify cycle, the intent check, debt and decision recording, the
109
+ context boundaries to clear at, and the post-build handoff. Read it at the
110
+ start of every session and follow it.
249
111
 
250
112
  {{HOST_DISPATCH}}