@warren-dean/code-kit 0.8.0 → 0.8.1

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.
@@ -9,7 +9,7 @@
9
9
  "name": "code-kit",
10
10
  "source": "./",
11
11
  "description": "Keeps AI coding agents inside your project's rules, from idea to merged code: spec design, lanes, layer rules, protected paths with an approval log, spec-check and design gates, proof-before-finish checks, dispatch, review, build status and CI verification, configured per project from its docs",
12
- "version": "0.8.0",
12
+ "version": "0.8.1",
13
13
  "category": "development",
14
14
  "tags": ["guardrails", "architecture", "lanes", "layers", "spec-driven", "ci", "multi-agent"],
15
15
  "license": "MIT"
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "code-kit",
3
- "version": "0.8.0",
3
+ "version": "0.8.1",
4
4
  "description": "Keeps AI coding agents inside your project's rules, from idea to merged code: spec design, lanes, layer rules, protected paths with an approval log, spec-check and design gates, proof-before-finish checks, dispatch, review, build status and CI verification, configured per project from its docs",
5
5
  "author": {
6
6
  "name": "Warren Langeveldt"
package/CHANGELOG.md CHANGED
@@ -2,6 +2,14 @@
2
2
 
3
3
  What changed in each release of code-kit, newest first. Versions follow the plugin manifest and the npm package `@warren-dean/code-kit`. Each release is also on [GitHub Releases](https://github.com/warrendeanlangeveldt/code-kit/releases).
4
4
 
5
+ ## 0.8.1 (2026-10-11)
6
+
7
+ Fixes for projects set up before 0.8.0.
8
+
9
+ - **No first merge for you here either.** A project set up before 0.8.0 has no code-kit rules on `main`, so its first merge still waited on you. Now the lead puts the rules you approved onto `main` itself (`code-kit queue rules`, a step the loop takes), without switching branches, and then merges the queue under them.
10
+ - **Waiting on the merge queue isn't a broken plan.** Stories that depend on branches waiting to merge were reported as a dependency cycle, sending the build back to spec design. `next` now says they wait for the merge.
11
+ - The band's key for decisions reads "d: Why, and what each choice does", not "d: d Why".
12
+
5
13
  ## 0.8.0 (2026-10-11)
6
14
 
7
15
  You decide what's built and the rules; the lead delivers it. Only real decisions reach you, each with the story behind it, and the whole build reads as one story from your request to now.
package/README.md CHANGED
@@ -274,7 +274,7 @@ With Claude Code 2.1.287 or later, code-kit's mod shows the project in the sessi
274
274
 
275
275
  - **The band** sits above the prompt. While the build is moving (the lead mid-turn, an agent at work, the loop started) it opens with the mission: **goal**, the milestone being built and how many of its stories are done; **now**, what the lead is doing and the loop's state, with **Pause** or **Resume**; **next**, the step the loop takes next; and **agents**, a line per agent at work with its current tool and how long since. With Context Graph installed, a line says when a lane edited files without understanding them, and only then. Beneath, labelled **you**, what only you can decide, each with the story it's for ("for ST-4 Booking form (BOOK-1)"), and **d** ("Why, and what each choice does") opens it as a card in the Decisions tab; usage and the plan's pause are labelled **note**. While the loop runs, a lane's finished work is the lead's to review and merge, so it never waits here; with the loop off, Review and Merge are offered to you. With nothing happening and nothing waiting, the band is gone. It shows:
276
276
  - an agent's call held for you: installing a new package, writing or committing a protected file, or a lane changing the kit. Rather than stopping the agent, the call waits while the band asks ("web wants to install dayjs · 1:53"). **Approve** opens the confirmation and, once you confirm, the same call runs; **Refuse**, or no answer within `harness.hold.minutes` (2 unless set), refuses it as before. Under `autonomy: autonomous`, a call within `approvals.delegate` is approved as delegated without asking. Nothing is held under `claude -p` or the SDK, where nobody can answer;
277
- - the lead loop, under `harness.autonomy`. `/code-kit:init` asks you how hands-off you want it. Until you've chosen, the loop's first step waits in the band ("code-kit can keep the build moving between your prompts") with **Keep going**, **Ask each time** and **Off**, and your answer is recorded as your setting. Once the lead has finished a turn, the loop keeps the build moving: with the lead idle and nothing typed in the prompt, it asks `code-kit next` and prompts the lead to dispatch the ready stories, review a finished one, or build its own, never the same step twice while nothing has changed. A step the lead was prompted with twice in a row and that is still next goes to you instead, in the band: "Merge lead/st-1: still next after 2 prompts. Ask the lead again?" with **Go**. **Pause** and **Resume** stop and start it. Under `propose`, each step waits in the band for **Go**; under `off`, the loop does nothing. A lane agent with no tool call for `harness.stall.nudgeMinutes` (5) is messaged, unless its work is done (it handed back, or its branch is in review or the merge queue); at `stall.restartMinutes` (10) the lead is asked to stop it and dispatch its story again, at most `stall.maxRestarts` (2) times, and then the band flags the lane. When every story is done, the band says so and the loop stops. With `harness.agents.reviewer.on`, a finished branch is first reviewed in the background by code-kit's reviewer agent, which the lead starts: it runs `verify` and the review checklist, read-only, and grades each finding blocker, concern or nit. The lead's review prompt then carries the findings, and a blocker means the branch is sent back. The pane shows each review, running with its clock or what it found. If the reviewer hasn't reported within 15 minutes, or the plan's 5-hour use is past `harness.background.pauseAtPercent`, the lead reviews without it. A branch the lead's review passes joins the merge queue (`code-kit queue`). Branches merge one at a time, in the order they passed, each verified against the base as it is then: the lead merges where `approvals.delegate.merge` is on under `autonomous`, otherwise the band shows the head with **Merge** for you. Who may merge is read from the config committed on the base, so a branch can't grant itself merges. There's no first merge for you: setting up commits the rules you approved straight onto the base. A merged lane's worktree is removed when it holds nothing uncommitted. A head that conflicts or fails verify goes back to its lane. The pane lists the queue, and lanes whose branch waits in it read `queued`;
277
+ - the lead loop, under `harness.autonomy`. `/code-kit:init` asks you how hands-off you want it. Until you've chosen, the loop's first step waits in the band ("code-kit can keep the build moving between your prompts") with **Keep going**, **Ask each time** and **Off**, and your answer is recorded as your setting. Once the lead has finished a turn, the loop keeps the build moving: with the lead idle and nothing typed in the prompt, it asks `code-kit next` and prompts the lead to dispatch the ready stories, review a finished one, or build its own, never the same step twice while nothing has changed. A step the lead was prompted with twice in a row and that is still next goes to you instead, in the band: "Merge lead/st-1: still next after 2 prompts. Ask the lead again?" with **Go**. **Pause** and **Resume** stop and start it. Under `propose`, each step waits in the band for **Go**; under `off`, the loop does nothing. A lane agent with no tool call for `harness.stall.nudgeMinutes` (5) is messaged, unless its work is done (it handed back, or its branch is in review or the merge queue); at `stall.restartMinutes` (10) the lead is asked to stop it and dispatch its story again, at most `stall.maxRestarts` (2) times, and then the band flags the lane. When every story is done, the band says so and the loop stops. With `harness.agents.reviewer.on`, a finished branch is first reviewed in the background by code-kit's reviewer agent, which the lead starts: it runs `verify` and the review checklist, read-only, and grades each finding blocker, concern or nit. The lead's review prompt then carries the findings, and a blocker means the branch is sent back. The pane shows each review, running with its clock or what it found. If the reviewer hasn't reported within 15 minutes, or the plan's 5-hour use is past `harness.background.pauseAtPercent`, the lead reviews without it. A branch the lead's review passes joins the merge queue (`code-kit queue`). Branches merge one at a time, in the order they passed, each verified against the base as it is then: the lead merges where `approvals.delegate.merge` is on under `autonomous`, otherwise the band shows the head with **Merge** for you. Who may merge is read from the config committed on the base, so a branch can't grant itself merges. There's no first merge for you: setting up commits the rules you approved straight onto the base, and in a project set up before that, the lead puts them there itself (`code-kit queue rules`) before its first merge. A merged lane's worktree is removed when it holds nothing uncommitted. A head that conflicts or fails verify goes back to its lane. The pane lists the queue, and lanes whose branch waits in it read `queued`;
278
278
  - approvals agents have asked for: **Approve…** opens a confirmation with a reason prefilled from the request, which you keep or rewrite;
279
279
  - stories finished and waiting for review: **Review** asks the lead to run `/code-kit:review` on the branch, and **Merge** runs `verify`, checks included, then merges if it passes;
280
280
  - a finish check failing in this session: **Lanes** opens the pane;
@@ -346,6 +346,7 @@ code-kit settings [--json] # the harness setti
346
346
  code-kit settings set <key> <value> --reason "…" # change one, as the person: written to the config and logged as a kit approval
347
347
  code-kit queue [--json] # the merge queue: branches whose review passed, in order
348
348
  code-kit queue add|drop <branch> # the lead queues a branch its review passed, or takes it out
349
+ code-kit queue rules # the lead puts the approved rules onto a base that has none yet (a project set up before 0.8.0)
349
350
  code-kit queue merge --delegated|--person # merge the head, verified against the base as it is now; a conflict or failed verify sends it back to its lane
350
351
  code-kit loop [--json] # whether the person paused the lead loop; other tools' background work pauses with it
351
352
  code-kit loop pause|resume # the person's act, as Pause and Resume in the band
package/bin/code-kit.mjs CHANGED
@@ -336,9 +336,82 @@ function configAt(ref) {
336
336
  return config ?? null;
337
337
  }
338
338
 
339
+ /**
340
+ * SETUP-2: a project set up before setup committed its rules onto the base has none there, so nothing
341
+ * could be merged by the lead. `queue rules` commits the rules as committed on this branch (the ones the
342
+ * person approved) onto the base, as setup now does, without checking the base out: a commit of the base
343
+ * with those files changed, the base moved to it. Only while the base has no rules, and only the lead.
344
+ */
345
+ function rulesOntoBase(root, config) {
346
+ const into = intoName ?? config.branches.protected[0];
347
+ const git = (args, opts = {}) =>
348
+ execFileSync('git', ['-C', root, ...args], { encoding: 'utf8', ...opts }).trim();
349
+ const head = git(['rev-parse', '--abbrev-ref', 'HEAD']);
350
+ if (head === into)
351
+ die(
352
+ `You're on ${into}: commit ${file} here as setup does (\`git add ${file} && git commit\`), which the hooks allow while ${into} has no rules.`,
353
+ );
354
+ if (configAt(into))
355
+ return out(
356
+ `${into} already has code-kit's rules committed; the merge queue merges under them.`,
357
+ );
358
+ if (!configAt('HEAD'))
359
+ die(
360
+ `${head} has no ${file} committed. Commit the rules the person approved first, then run this again.`,
361
+ );
362
+ const used = git(['worktree', 'list', '--porcelain'])
363
+ .split('\n')
364
+ .some((l) => l === `branch refs/heads/${into}`);
365
+ if (used)
366
+ die(
367
+ `${into} is checked out in a worktree, so moving it here would leave that checkout behind. Commit ${file} in that checkout instead (the hooks allow it while ${into} has no rules).`,
368
+ );
369
+ // The rules, and the baseline of existing violations they were approved with.
370
+ const files = [file, BASELINE_FILE].filter((f) => {
371
+ try {
372
+ execFileSync('git', ['-C', root, 'cat-file', '-e', `HEAD:${f}`], { stdio: 'ignore' });
373
+ return true;
374
+ } catch {
375
+ return false;
376
+ }
377
+ });
378
+ const index = join(tmpdir(), `code-kit-rules-${process.pid}`);
379
+ const env = { ...process.env, GIT_INDEX_FILE: index };
380
+ try {
381
+ git(['read-tree', into], { env });
382
+ for (const f of files)
383
+ git(
384
+ ['update-index', '--add', '--cacheinfo', `100644,${git(['rev-parse', `HEAD:${f}`])},${f}`],
385
+ {
386
+ env,
387
+ },
388
+ );
389
+ const tree = git(['write-tree'], { env });
390
+ const before = git(['rev-parse', into]);
391
+ const commit = git(
392
+ [
393
+ 'commit-tree',
394
+ tree,
395
+ '-p',
396
+ before,
397
+ '-m',
398
+ `code-kit's rules onto ${into}, as the person approved them on ${head}\n\nFrom now on ${into}'s committed rules decide who merges into it.`,
399
+ ],
400
+ { env },
401
+ );
402
+ git(['update-ref', `refs/heads/${into}`, commit, before]);
403
+ } finally {
404
+ rmSync(index, { force: true });
405
+ }
406
+ const now = configAt(into);
407
+ out(
408
+ `Committed ${files.join(' and ')} onto ${into}, from ${head}. ${now?.approvals.delegate?.merge ? 'The lead merges the queue from now on (code-kit queue merge --delegated).' : `${into}'s rules don't delegate merges, so the person merges the queue.`}`,
409
+ );
410
+ }
411
+
339
412
  /** Why the first merge into `into`, which has no config committed yet, is the person's. */
340
413
  const firstMerge = (into, branch) =>
341
- `${into} has no ${file} committed yet, so it delegates nothing: the first merge, which brings code-kit's rules onto ${into}, is the person's. Ask them to merge ${branch} from the code-kit band (Merge), or to run \`! node "${process.argv[1]}" queue merge --person\`. From then on, ${into}'s committed rules decide who merges.`;
414
+ `${into} has no ${file} committed yet, so it delegates nothing. Put the approved rules onto it first: \`node "${process.argv[1]}" queue rules\`. Then merge ${branch} again; from then on ${into}'s committed rules decide who merges.`;
342
415
 
343
416
  /** Why a merge didn't happen: `refused` (the rules), `verify` (problems) or `conflict`. */
344
417
  class MergeStop extends Error {
@@ -527,6 +600,7 @@ function queue() {
527
600
  const place = waiting(readQueue(root)).length;
528
601
  return out(`Queued ${branch} to merge${place > 1 ? `, ${place - 1} ahead of it` : ', next'}.`);
529
602
  }
603
+ if (sub === 'rules') return rulesOntoBase(root, config);
530
604
  if (sub === 'merge') {
531
605
  const byPerson = mergeMode();
532
606
  const entries = readQueue(root);
@@ -561,7 +635,7 @@ function queue() {
561
635
  }
562
636
  if (sub !== undefined)
563
637
  die(
564
- 'code-kit queue [--json] | queue add <branch> | queue drop <branch> | queue merge --delegated|--person',
638
+ 'code-kit queue [--json] | queue add <branch> | queue drop <branch> | queue rules | queue merge --delegated|--person',
565
639
  );
566
640
  const entries = readQueue(root);
567
641
  if (flag('--json')) return out(JSON.stringify(entries, null, 2));
@@ -1194,7 +1268,7 @@ function usage() {
1194
1268
  ' | stops [--session id] [--json] | sent-back <branch> [--reason "…"]\n' +
1195
1269
  ' | settings [--json] | settings set <key> <value> --reason "…" [--via pane]\n' +
1196
1270
  ' | hold <id> [--minutes N] | hold <id> --answer approved|refused\n' +
1197
- ' | queue [--json] | queue add|drop <branch> | queue merge --delegated|--person\n' +
1271
+ ' | queue [--json] | queue add|drop <branch> | queue rules | queue merge --delegated|--person\n' +
1198
1272
  ' | loop [--json] | loop pause|resume\n' +
1199
1273
  ' | map <story> [--json] | trail [--json] | journal [--json]\n' +
1200
1274
  ' | story <id> [--json] | timeline [--json]',
@@ -74,6 +74,7 @@ const packages = addedPackages(cmd);
74
74
  const approves = runs(cmd, new RegExp(String.raw`^${CODE_KIT}\s+approve\b`));
75
75
  const delegatedMerge = runs(cmd, new RegExp(String.raw`^${CODE_KIT}\s+(?:queue\s+)?merge\b`));
76
76
  const queues = runs(cmd, new RegExp(String.raw`^${CODE_KIT}\s+queue\s+(?:add|drop)\b`));
77
+ const rulesOntoBase = runs(cmd, new RegExp(String.raw`^${CODE_KIT}\s+queue\s+rules\b`));
77
78
  const commits = runs(cmd, new RegExp(String.raw`^${GIT}commit\b`));
78
79
  if (error) {
79
80
  if (commits) block(`Blocked: ${error}\nFix the config before committing.`);
@@ -174,6 +175,10 @@ if (queues && actor.kind !== 'lead')
174
175
  block(
175
176
  `Blocked: only the lead puts branches in the merge queue, after its review passes them. Finish your story and commit it on your branch; the lead reviews it.\nCommand: ${cmd}`,
176
177
  );
178
+ if (rulesOntoBase && actor.kind !== 'lead')
179
+ block(
180
+ `Blocked: only the lead puts code-kit's rules onto the protected branch. Finish your story and commit it on your branch; the lead takes it from there.\nCommand: ${cmd}`,
181
+ );
177
182
  if (delegatedMerge && actor.kind !== 'lead')
178
183
  block(
179
184
  `Blocked: only the lead merges into a protected branch. Finish your story and commit it on your branch; the lead reviews and merges it.\nCommand: ${cmd}`,
@@ -180,11 +180,15 @@ export function nextStep(root, base) {
180
180
  // the person does, and always the first merge, which brings code-kit's rules onto the base.
181
181
  const delegation = queued.length ? mergeDelegation(root, config) : { delegated: false };
182
182
  const leadMerges = delegation.delegated && config.harness.autonomy === 'autonomous';
183
+ // SETUP-2: a base with no rules committed (a project set up before setup committed them there): the
184
+ // lead puts the approved rules onto it, and the queue merges under them from then on.
183
185
  if (queued.length && delegation.first)
184
- attention.push(
185
- `${queued[0]} passed review and is next to merge. ${delegation.into} has no code-kit config committed yet, so this first merge, which brings the rules onto it, is the person's (code-kit queue merge --person, or Merge in the code-kit band); after it, the lead merges.`,
186
+ return result(
187
+ 'rules',
188
+ `${queued[0]} passed review and is next to merge, but ${delegation.into} has no code-kit rules committed yet. Put the rules the person approved onto it (code-kit queue rules); then the queue merges under them.`,
189
+ { args: delegation.into },
186
190
  );
187
- else if (queued.length && !leadMerges)
191
+ if (queued.length && !leadMerges)
188
192
  attention.push(
189
193
  `${queued.join(', ')} passed review and ${queued.length === 1 ? 'waits' : 'wait'} for the person to merge (code-kit queue merge --person, or Merge in the code-kit band).`,
190
194
  );
@@ -227,6 +231,12 @@ export function nextStep(root, base) {
227
231
  'wait',
228
232
  `${building.join(', ')} ${building.length === 1 ? 'is' : 'are'} being built; nothing else can start until one finishes. A lane that stopped without finishing is dispatched again.`,
229
233
  );
234
+ // Stories waiting on branches in the merge queue wait for the merge, not on a broken plan.
235
+ if (queued.length)
236
+ return result(
237
+ 'wait',
238
+ `${queued.join(', ')} passed review and ${queued.length === 1 ? 'waits' : 'wait'} to be merged; the stories that depend on ${queued.length === 1 ? 'it' : 'them'} start after.`,
239
+ );
230
240
  if (unbuildable.length)
231
241
  return result(
232
242
  'spec-design',
@@ -50,7 +50,7 @@ function whyOf(line, check) {
50
50
  if (line.kind === 'proposal') return 'You asked to approve each step before the lead takes it.';
51
51
  if (line.kind === 'queue')
52
52
  return check?.firstMerge
53
- ? 'main has no rules committed yet, so this first merge is yours; after it, the lead merges.'
53
+ ? 'The loop is off, so merging is yours. main has no rules committed yet: code-kit queue rules puts them there.'
54
54
  : "Your rules don't let the lead merge into main, so merges are yours.";
55
55
  if (line.kind === 'review') return 'The loop is off, so reviewing finished work is yours.';
56
56
  return null;
@@ -3,7 +3,7 @@
3
3
  // existing skills, and the hooks judge every call as before.
4
4
 
5
5
  /** The steps the loop hands the lead; the rest (spec-design, init, check) wait for the person. */
6
- const LEAD_STEPS = new Set(['dispatch', 'review', 'lead', 'merge']);
6
+ const LEAD_STEPS = new Set(['dispatch', 'review', 'lead', 'merge', 'rules']);
7
7
 
8
8
  /**
9
9
  * The prompt for a step from `code-kit next --json` (LOOP-1), or null when it isn't the lead's to
@@ -19,6 +19,9 @@ export function leadPrompt(step, { cli = 'code-kit' } = {}) {
19
19
  return ids.length
20
20
  ? `Merge ${ids[0]}, next in the merge queue: run node "${cli}" queue merge --delegated. It verifies the branch against the base as it is now, and sends it back to its lane if it conflicts or fails.`
21
21
  : null;
22
+ // SETUP-2: the base has no rules yet; the lead puts the approved ones there, then merges as usual.
23
+ if (step.step === 'rules')
24
+ return `Put code-kit's approved rules onto ${ids[0] ?? 'the base'}: run node "${cli}" queue rules. It commits the rules as committed on this branch, without switching branches; after it, merge the queue as usual.`;
22
25
  if (step.step === 'dispatch')
23
26
  return ids.length
24
27
  ? `Dispatch ${ids.join(', ')}: run /code-kit:dispatch ${ids.join(' ')}.`
@@ -40,8 +43,9 @@ export const stepKey = (step) => `${step.step}:${step.args ?? ''}`;
40
43
  export function stepLabel(step) {
41
44
  const ids = (step.args ?? '').split(/\s+/).filter(Boolean).join(', ');
42
45
  const verb =
43
- { dispatch: 'Dispatch', review: 'Review', lead: 'Build', merge: 'Merge' }[step.step] ??
44
- step.step;
46
+ { dispatch: 'Dispatch', review: 'Review', lead: 'Build', merge: 'Merge', rules: 'Rules onto' }[
47
+ step.step
48
+ ] ?? step.step;
45
49
  return ids ? `${verb} ${ids}` : verb;
46
50
  }
47
51
 
@@ -2066,18 +2066,31 @@ test('MQ-2 where the person merges, the head of the queue waits in the band for
2066
2066
  expect(w.opened).toContain(RESULT);
2067
2067
  });
2068
2068
 
2069
- test('MQ-2 the first merge, before the base has code-kit rules committed, is offered to the person', async ($, on) => {
2069
+ test("SETUP-2 with no rules on the base yet, the lead puts them there under autonomy: the band doesn't ask the person to merge", async ($, on) => {
2070
2070
  const w = project();
2071
2071
  (w.check as any).firstMerge = true;
2072
2072
  w.queue = [{ branch: 'lead/st-1', passed: '2026-10-10T00:00:00Z', state: 'waiting' }];
2073
+ w.next = { step: 'rules', args: 'main', then: [] };
2073
2074
  await start($, on, w);
2074
2075
  const ui = await bandUi($);
2075
- expect(
2076
- await ui.find({
2077
- type: 'Text',
2078
- text: 'lead/st-1 is next to merge: the first merge is yours, then the lead merges',
2079
- }),
2080
- ).toBeDefined();
2076
+ expect(await ui.find({ type: 'Text', text: /next to merge/ })).toBeUndefined();
2077
+ await ui.unmount();
2078
+ });
2079
+
2080
+ test("SETUP-2 with the loop off, the merge is the person's, and its card says how the rules get onto main", async ($, on) => {
2081
+ const w = project();
2082
+ (w.check as any).firstMerge = true;
2083
+ (w.check as any).harness = { autonomy: 'off' };
2084
+ w.queue = [{ branch: 'lead/st-1', passed: '2026-10-10T00:00:00Z', state: 'waiting' }];
2085
+ await start($, on, w);
2086
+ const band = await bandUi($);
2087
+ expect(await band.find({ type: 'Text', text: 'lead/st-1 is next to merge' })).toBeDefined();
2088
+ await press($, 'band-decide');
2089
+ await band.unmount();
2090
+ const ui = await pane($, PANE);
2091
+ expect((await ui.find({ key: 'decision-card' }))?.text).toMatch(
2092
+ /code-kit queue rules puts them there/,
2093
+ );
2081
2094
  await ui.unmount();
2082
2095
  });
2083
2096
 
@@ -798,15 +798,13 @@ export function bandLines({
798
798
  });
799
799
  // MQ-2: where the lead doesn't merge, the head of the queue waits for the person's Merge.
800
800
  const next = queue.find((e) => ['waiting', 'merging'].includes(e.state));
801
- const leadMerges =
802
- check?.delegatesMerge && (check?.harness?.autonomy ?? 'autonomous') === 'autonomous';
801
+ const autonomous = (check?.harness?.autonomy ?? 'autonomous') === 'autonomous';
802
+ // SETUP-2: with no rules on the base yet, the lead puts them there first and then merges.
803
+ const leadMerges = autonomous && (check?.delegatesMerge || check?.firstMerge);
803
804
  if (next && !leadMerges)
804
805
  lines.push({
805
806
  kind: 'queue',
806
- // The first merge brings code-kit's rules onto the base, so it's the person's even under autonomy.
807
- text: check?.firstMerge
808
- ? `${next.branch} is next to merge: the first merge is yours, then the lead merges`
809
- : `${next.branch} is next to merge`,
807
+ text: `${next.branch} is next to merge`,
810
808
  branch: next.branch,
811
809
  actions: [{ id: 'mergeQueue', label: 'Merge' }],
812
810
  });
@@ -931,7 +929,7 @@ export function band(lines, { Box, Text, Button }, onAction) {
931
929
  Text({ children: [''.padEnd(LABEL)] }),
932
930
  Button({
933
931
  key: 'band-decide',
934
- label: 'd Why, and what each choice does',
932
+ label: 'Why, and what each choice does',
935
933
  hotkey: 'd',
936
934
  plain: true,
937
935
  onPress: () => onAction('decide', first),
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@warren-dean/code-kit",
3
- "version": "0.8.0",
3
+ "version": "0.8.1",
4
4
  "description": "Keeps AI coding agents inside your project's rules: lanes, layer rules, protected paths and proof before finish, enforced by Claude Code hooks and in CI",
5
5
  "license": "MIT",
6
6
  "author": "Warren Langeveldt",