@joshuafolkken/kit 1.953.0 → 1.972.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (100) hide show
  1. package/.claude/skills/workflow-commands/background-commands.md +3 -0
  2. package/.claude/skills/workflow-commands/backlogrun-child.md +75 -120
  3. package/.claude/skills/workflow-commands/backlogrun-lanes.md +78 -115
  4. package/.claude/skills/workflow-commands/backlogrun-progress.md +82 -146
  5. package/.claude/skills/workflow-commands/backlogrun-steps.md +173 -229
  6. package/.claude/skills/workflow-commands/backlogrun.md +3 -1
  7. package/.claude/skills/workflow-commands/followup.md +3 -0
  8. package/.claude/skills/workflow-commands/fullrun.md +3 -4
  9. package/.claude/skills/workflow-commands/observation-filing.md +31 -25
  10. package/.claude/skills/workflow-commands/pre-gate-cut.md +96 -161
  11. package/.github/workflows/pr-classification.yml +12 -1
  12. package/CLAUDE.md +3 -3
  13. package/README.md +5 -3
  14. package/dist/CLAUDE.md +3 -3
  15. package/dist/hooks/batch-guard.js +4 -4
  16. package/dist/hooks/{chunk-OM5VDM6F.js → chunk-2K52BRWV.js} +2 -2
  17. package/dist/hooks/{chunk-Z5TVZ4AP.js → chunk-BNWIOJFU.js} +29 -16
  18. package/dist/hooks/{chunk-NKVLBB65.js → chunk-BVMSPG3M.js} +3 -3
  19. package/dist/hooks/{chunk-IA7WC4NS.js → chunk-CRGJTD6E.js} +1 -1
  20. package/dist/hooks/{chunk-M4U4YNRE.js → chunk-F6OM6SIG.js} +3 -3
  21. package/dist/hooks/{chunk-O3PEDF3M.js → chunk-KQG4776P.js} +1 -1
  22. package/dist/hooks/{chunk-V7RH56HU.js → chunk-NXL3SBXY.js} +2 -2
  23. package/dist/hooks/{chunk-452OKRQX.js → chunk-WWTMHHZU.js} +120 -18
  24. package/dist/hooks/{chunk-EYFJ74MJ.js → chunk-ZPLXLJ7O.js} +21 -7
  25. package/dist/hooks/{chunk-IBFC25IX.js → chunk-ZYYSBNR6.js} +4 -4
  26. package/dist/hooks/codex-hook-adapter.js +10 -10
  27. package/dist/hooks/duplicate-read-guard.js +5 -5
  28. package/dist/hooks/format-edited.js +2 -2
  29. package/dist/hooks/investigation-guard.js +5 -5
  30. package/dist/hooks/pretool-guard.js +9 -9
  31. package/dist/hooks/stop-guard.js +206 -176
  32. package/dist/josh.js +14 -6
  33. package/package.json +3 -3
  34. package/prompts/collaboration-workflow/wip-cap.md +1 -1
  35. package/scripts/backlog/backlog-arrival.ts +11 -1
  36. package/scripts/backlog/backlog-budget.ts +25 -8
  37. package/scripts/backlog/backlog-drive-cli.ts +5 -1
  38. package/scripts/backlog/backlog-drive-retrospective.ts +20 -0
  39. package/scripts/backlog/backlog-drive.ts +7 -3
  40. package/scripts/backlog/backlog-next.ts +45 -2
  41. package/scripts/backlog/backlog-offer.ts +1 -0
  42. package/scripts/backlog/backlog-plan-cli.ts +66 -27
  43. package/scripts/backlog/backlog-plan.ts +30 -2
  44. package/scripts/backlog/backlog-stalled.ts +9 -0
  45. package/scripts/backlog/backlog-waves.ts +169 -0
  46. package/scripts/document/document-byte-budget.ts +1 -1
  47. package/scripts/document/entry-read-budget.ts +7 -5
  48. package/scripts/epic/epic-lane-offer.ts +69 -4
  49. package/scripts/epic/epic-next.ts +2 -2
  50. package/scripts/epic/epic-solo.ts +147 -0
  51. package/scripts/epic/epic-triage.ts +47 -0
  52. package/scripts/gate/hook-gate-reuse.ts +7 -8
  53. package/scripts/git/git-closes-keyword.ts +15 -0
  54. package/scripts/git/git-followup-flush.ts +5 -6
  55. package/scripts/git/git-followup-issue-close.ts +75 -0
  56. package/scripts/git/git-pr-followup-wrapup.ts +36 -6
  57. package/scripts/git/git-pr-followup.ts +9 -16
  58. package/scripts/git/git-staging.ts +33 -20
  59. package/scripts/git/issue-labels.ts +42 -0
  60. package/scripts/hooks/stop-guard.ts +1 -0
  61. package/scripts/init/init-install.ts +64 -0
  62. package/scripts/init/init.ts +38 -11
  63. package/scripts/init/project-profile.ts +9 -5
  64. package/scripts/init/start-plan.ts +145 -0
  65. package/scripts/init/start-prompt.ts +55 -0
  66. package/scripts/init/start-steps.ts +167 -0
  67. package/scripts/init/start.ts +92 -16
  68. package/scripts/issue/issue-closing-pr.ts +93 -0
  69. package/scripts/josh/__snapshots__/josh-command-map.test.ts.snap +4 -2
  70. package/scripts/josh/josh-commands-project.ts +11 -3
  71. package/scripts/josh/josh-commands-workflow.ts +3 -3
  72. package/scripts/observations/observation-ledger-prepare.ts +31 -0
  73. package/scripts/observations/observation-ledger.ts +5 -5
  74. package/scripts/observations/observations-flush.ts +11 -30
  75. package/scripts/rules/implementation-cut.ts +49 -16
  76. package/scripts/rules/lane-background.ts +25 -5
  77. package/scripts/rules/stop-rules.ts +16 -4
  78. package/scripts/run/run-carry-cli.ts +21 -0
  79. package/scripts/run/run-cut-cli.ts +92 -110
  80. package/scripts/run/run-cut-report.ts +167 -0
  81. package/scripts/run/run-cut.ts +23 -7
  82. package/scripts/run/run-entry-cli.ts +36 -2
  83. package/scripts/run/run-headless.ts +1 -0
  84. package/scripts/run/run-hold-cli.ts +36 -15
  85. package/scripts/run/run-hold.ts +15 -4
  86. package/scripts/run/run-merge-cli.ts +28 -14
  87. package/scripts/run/run-merge-steps.ts +38 -2
  88. package/scripts/run/run-merge.ts +41 -3
  89. package/scripts/run/run-prep-cli.ts +8 -2
  90. package/scripts/run/run-prep-locate.ts +165 -0
  91. package/scripts/run/run-prep.ts +12 -1
  92. package/scripts/run/run-retrospective.ts +34 -0
  93. package/scripts/run/run-step-cli.ts +19 -5
  94. package/scripts/run/run-step.ts +31 -19
  95. package/scripts/run/run-tail-cli.ts +4 -2
  96. package/scripts/run/run-wake-work.ts +4 -0
  97. package/scripts/safe-chain/ci-installer-pin.ts +8 -4
  98. package/scripts/time-runtime/time-background.ts +11 -0
  99. package/scripts/time-runtime/time-transcript-line.ts +4 -0
  100. package/templates/pnpm-workspace.static.yaml +2 -0
@@ -56,7 +56,9 @@ Each step is a terse trigger and a pointer; the procedure is read on demand from
56
56
  opted-in pool, a run's own filings once bundled, and the brake (`--max`, ten filings, the WIP cap)
57
57
  that bounds the amount. `auto-ok` is a person's alone.
58
58
  4. **Report the plan before the first child** — `pnpm josh backlog:plan`, then resolve every
59
- `needs-decision` issue decidable from its body in one pass. `backlogrun-steps.md` → "The plan,
59
+ `needs-decision` issue decidable from its body in one pass. In the same pass, record `blocked-by`
60
+ between issues that must land in order, and apply `run:solo` where the verification-path
61
+ enumeration holds, `run:lane` elsewhere. `backlogrun-steps.md` → "The plan,
60
62
  before the first child starts" and its "Resolve what the plan can resolve, before starting".
61
63
  5. **Named issues run first, in order** — `backlogrun-steps.md` → "Named issues run first, in order":
62
64
  the supervisor's driver dispatches each as a delegated `fullrun`, one at a time, then drains the
@@ -58,6 +58,9 @@ What one invocation does, in order:
58
58
  A run that stopped at the merge therefore has no Issue comment, and the missing comment is what a
59
59
  failed merge looks like from GitHub.
60
60
  - **Closes the epics the Issue completes**, on a merged run only.
61
+ - **Confirms the Issue itself closed**, on a merged run only. GitHub has left merged runs' Issues
62
+ open despite `closes #N` (joshuafolkken/kit#2770), so an Issue still open a few seconds after the
63
+ merge is closed with a comment saying why, and the repair is printed as a warning.
61
64
  - **Takes the `in-progress` label back off the Issue**, on a merged run only. A run that merged nothing
62
65
  keeps it. The removal reads the Issue's labels and sends back the spelling GitHub stored, so an
63
66
  `In-Progress` repository is not missed, and an Issue that never carried the label is never written to.
@@ -58,11 +58,10 @@ call reports `hold`.
58
58
  ambient surface is the run's event stream, followed identically before and after a cut, so nothing
59
59
  about the reporter moves when execution hands off (`backlogrun-progress.md` → "The invariant is a
60
60
  tier, not a mechanism").
61
- - **A dispatched lane child asks whether it is a resume first — `pnpm josh run:cut --resume <N>`**; on
62
- `resume` it skips the title, plan, hold and implementation and goes to the gate. At the pre-gate
61
+ - **A lane child asks whether it is a resume first — `pnpm josh run:cut --resume <N>`** (`run:entry` asks it
62
+ before the hold, joshuafolkken/kit#2760); on `resume` it skips the title, plan, hold and implementation and goes to the gate. At the pre-gate
63
63
  boundary — immediately after `pnpm josh main:merge`, before the gate — it takes the cut with
64
- `pnpm josh run:cut <N>` (the ordered step in `chain-rule.md`, so the cut is taken before the gate
65
- rather than after a refusal), and it records any park on the Issue before the stop notify.
64
+ `pnpm josh run:cut <N>` (the ordered step in `chain-rule.md`), and it records any park on the Issue before the stop notify.
66
65
  `pre-gate-cut.md` is the single source of both.
67
66
 
68
67
  ## The stop branches
@@ -254,37 +254,43 @@ it — and seven lines had never left a working tree. **So the repeat count belo
254
254
  that is empty on every other machine**, and every sighting was a first one, which is the state
255
255
  joshuafolkken/kit#1728 created the ledger to end.
256
256
 
257
- - **An ordinary run never commits the ledger, and that is enforced rather than remembered.**
258
- `pnpm josh git` stages with `docs/maintainers/observations.md` excluded, in the one staging step every entry
259
- point goes through (`scripts/git/git-staging.ts`), so a `fullrun` in the primary checkout **cannot**
260
- carry a ledger line into an Issue that has nothing to do with it. It is not a rule a run has to
261
- remember at the commit — a run that had to would be the run that forgets.
262
- - **The parent flushes the ledger as a pull request of its own** — `pnpm josh observations:flush`. It
263
- stages that one path and nothing else, commits it on a branch of its own, opens a docs-only pull
264
- request, waits on the same required checks every other pull request waits on, merges it and returns
265
- the checkout to the default branch. **Nothing is committed to the default branch directly**, which
266
- is the constraint this route had to satisfy.
267
- - **`pnpm josh followup` runs the flush itself, so no run has to remember to** (joshuafolkken/kit#1810).
268
- The flush is the ledger's only commit path, and until it was wired into `followup` nothing called
269
- it — an appended line stayed in the working tree until a person ran the command by hand, the same
270
- defect the staging exclusion above was built to avoid. After the merge and before it releases the working-tree
271
- hold, `followup` reads the tree, and **only when the ledger holds a pending append** returns the
272
- checkout to the default branch (`pnpm josh ms`) and flushes. A run that appended nothing pays
273
- nothing, and a lane skips it (next bullet). A flush that fails is reported and does not take the merge, the
274
- epic close or the hold release down with it. **A side effect: on a run that did flush, `followup`
275
- ends on the default branch.**
257
+ - **A run's appended lines ride its own commit** (joshuafolkken/kit#2763). `pnpm josh git` stages
258
+ `docs/maintainers/observations.md` with the run's other changes, in the one staging step every entry
259
+ point goes through (`scripts/git/git-staging.ts`), so the lines are reviewed and merged with the
260
+ pull request of the run that recorded them, and the run's CI is the only wait they cost. Until
261
+ #2763 the ledger was excluded there (joshuafolkken/kit#1756) and every run that appended a line
262
+ paid a second branch, CI wait and merge after its own — 144 ledger-only pull requests in one month.
263
+ **Record before the commit, not after the merge:** a finding recorded with `pnpm josh review:record`
264
+ before `pnpm josh git -y` is carried; the time while CI runs is for the records that need no CI
265
+ result — filing an observation Issue, drafting the completion report.
266
+ - **A line that breaks the grammar is not carried** (joshuafolkken/kit#2123). The staging step parses
267
+ the ledger first — after moving lines still on the old path (joshuafolkken/kit#2724) — and on a
268
+ broken line leaves the ledger out of the commit and says so; the commit itself goes ahead.
269
+ - **What no run's commit carried is flushed as a pull request of its own** — `pnpm josh
270
+ observations:flush`: a lane's lines (next bullets) and a line appended after the commit, such as a
271
+ second review round's record. It stages that one path and nothing else, commits it on a branch of
272
+ its own, opens a docs-only pull request, waits on the same required checks every other pull request
273
+ waits on, merges it and returns the checkout to the default branch. **Nothing is committed to the
274
+ default branch directly.**
275
+ - **`pnpm josh followup` runs that flush itself, so no run has to remember to** (joshuafolkken/kit#1810).
276
+ After the merge and before it releases the working-tree hold, `followup` reads the tree, and **only
277
+ when the ledger still holds a pending append** returns the checkout to the default branch
278
+ (`pnpm josh ms`) and flushes. A run whose lines rode its commit pays nothing, and a lane skips it
279
+ (next bullet). A flush that fails is reported and does not take the merge, the epic close or the
280
+ hold release down with it. **A side effect: on a run that did flush, `followup` ends on the default
281
+ branch.**
276
282
  - **A lane's writers use the primary checkout's ledger** (joshuafolkken/kit#2419): a lane refuses
277
283
  `pnpm josh ms`, so its own copy had no way out. `pnpm josh observations:flush` acts on the primary
278
284
  checkout from a lane. **A dispatched lane child does not flush at all** (joshuafolkken/kit#2492): its
279
285
  `run:tail` skips the step, and the `backlogrun` flushes every lane's lines once, in one pull request,
280
286
  at `pnpm josh run:carry --end`.
281
- - **Mixing the lines into a child's pull request was considered and is refused.** That is the
282
- contamination the exclusion above exists to end, and adopting it would turn the defect into the
283
- specification: a ledger line in an unrelated diff is a line no reviewer of that diff has a reason
284
- to question.
287
+ - **A lane child's pull request does not carry the lines, and that is deliberate.** Lanes run side by
288
+ side, and two pull requests that each append at the ledger's tail conflict with each other the
289
+ moment the first merges — a conflict ends the other's CI wait. The batch's single flush at
290
+ `run:carry --end` is already one pull request per batch, not one per child.
285
291
  - **Run by hand it goes in the primary checkout, once per cycle rather than once per observation** —
286
- but with `pnpm josh followup` now flushing automatically (above), a hand run is the exception rather
287
- than the rule. It refuses off the default branch and refuses a working tree holding anything besides
292
+ but with the lines riding the run's commit and `pnpm josh followup` flushing the rest (above), a hand
293
+ run is the exception rather than the rule. It refuses off the default branch and refuses a working tree holding anything besides
288
294
  the ledger, so a run in progress cannot be flushed out from under, and a lane's checkout is never
289
295
  the one it acts on.
290
296
  - **Nothing to flush is an answer, not a failure.** With the ledger matching the commit it sits on
@@ -1,32 +1,29 @@
1
1
  # The pre-gate cut — a lane child ends its turn before the gate
2
2
 
3
3
  A lane child is a detached `fullrun #<N>` process, and the thinking it accumulates while implementing
4
- rides on every later API call in the same session (measured at **176K of 204K output tokens** on
5
- joshuafolkken/kit#1837). The **pre-gate cut** drops that accumulation: the child ends its process the
6
- moment implementation is done, and a **fresh** process resumes the same lane from the gate onward.
4
+ rides on every later API call in the same session. The **pre-gate cut** drops that accumulation: the
5
+ child ends its process the moment implementation is done, and a **fresh** process resumes the same lane
6
+ from the gate onward. Rationale: `docs/maintainers/pre-gate-cut-rationale.md` → "Why the cut exists".
7
7
 
8
8
  This file is the single source of the boundary. `run:cut` takes it, and `run:cut --resume` is the
9
- check a fresh process makes at its own entry. The rule is reached only **during** a lane child's run,
10
- after the workflow skill has been read, so it is not an entry read — `fullrun.md`, `chain-rule.md` and
11
- `backlogrun.md` name it with a plain mention and route here. **A second boundary is added below** — the
9
+ check a fresh process makes at its own entry. **A second boundary is added below** — the
12
10
  implementation-phase cut (joshuafolkken/kit#1933), at a context threshold _during_ implementation.
13
11
  Everything through "What is carried" describes the pre-gate cut; the later section states only what
14
12
  differs.
15
13
 
16
14
  ## Where the boundary is
17
15
 
18
- **After implementation and the refactor, before `pnpm josh gate`** — the same boundary
19
- joshuafolkken/kit#1837 puts the `origin/main` merge at: implementation complete, nothing committed,
20
- the gate not started. Everything before it produced the thinking to be dropped; everything after it
21
- (gate → review → commit → push → merge) is verification the fresh process runs on the tree.
16
+ **After implementation and the refactor, before `pnpm josh gate`**: implementation complete, nothing
17
+ committed, the gate not started. Everything after it (gate → review → commit → push → merge) is
18
+ verification the fresh process runs on the tree.
22
19
 
23
20
  **A lane child first hands it to a detached supervisor** (`chain-rule.md` step 0); the cut is the fallback.
24
21
 
25
22
  ## It applies to a dispatched lane child, and to nothing else
26
23
 
27
- The cut is for a **detached** `fullrun` a lane dispatched. A person's interactive `fullrun` must not
28
- relaunch itself, so a dispatched child is told apart **mechanically, by a mark the dispatch sets** —
29
- never by the model reading the shape of its own prompt (joshuafolkken/kit#1904).
24
+ The cut is for a **detached** `fullrun` a lane dispatched; a person's interactive `fullrun` never
25
+ relaunches itself. A dispatched child is told apart **mechanically, by a mark the dispatch sets** — never
26
+ by the model reading the shape of its own prompt (joshuafolkken/kit#1904).
30
27
 
31
28
  ### The dispatch mark
32
29
 
@@ -34,8 +31,7 @@ never by the model reading the shape of its own prompt (joshuafolkken/kit#1904).
34
31
  to the lane's issue number. `scripts/lane/lane-child-marker.ts` is its one definition.
35
32
 
36
33
  - **Meaning**: "this session was launched for issue `<N>`, not typed by a person."
37
- - **Lifetime**: the child process — set at launch, read at the gate, never written by the run itself (a
38
- mark a run could set for itself would be no mark at all).
34
+ - **Lifetime**: the child process — set at launch, read at the gate, never written by the run itself.
39
35
  - **Trust**: only where its value equals the lane's own issue. A mark that leaked in from a parent
40
36
  session — naming another issue, or none — is read as a person, so a leak cannot make the guard fire
41
37
  on a checkout it does not belong to.
@@ -48,13 +44,11 @@ checkout, a cut in a lane.
48
44
 
49
45
  ## Taking the cut
50
46
 
51
- **The order is fixed in the chain, not left to the refusal to enforce** (joshuafolkken/kit#2177).
52
- `chain-rule.md` step 1 issues `pnpm josh run:cut <N>` after `pnpm josh main:merge`, before the scoped
53
- pair and the gate, so the default path takes the cut before anything reads the tree — removing the
54
- wasted round trip joshuafolkken/kit#2160 measured (an uncut gate refused, its batched `review:brief`
55
- cancelled, then cut and resume). **The command is still issued unconditionally**; `run:cut` itself
56
- decides whether the cut is warranted (joshuafolkken/kit#2312), so a short lane with nothing to drop is
57
- answered `under-threshold` and walks on to the gate rather than relaunching.
47
+ **The order is fixed in the chain** (joshuafolkken/kit#2177): `chain-rule.md` step 1 issues
48
+ `pnpm josh run:cut <N>` after `pnpm josh main:merge`, before the scoped pair and the gate. **The command
49
+ is issued unconditionally**; `run:cut` itself decides whether the cut is warranted
50
+ (joshuafolkken/kit#2312), so a short lane with nothing to drop is answered `under-threshold` and walks on
51
+ to the gate rather than relaunching.
58
52
 
59
53
  At the pre-gate boundary, issue:
60
54
 
@@ -65,9 +59,8 @@ pnpm josh run:cut <N> # alias: josh rct
65
59
  - `cut` (0) — record written and handed to the lane's launch owner (OpenAI supervisor starts the
66
60
  successor after this process exits; Anthropic relaunches directly). **End the turn immediately** —
67
61
  the fresh process owns the run.
68
- - `under-threshold` (0) — the recent-window per-request cost is under the shared `CONTEXT_CUT_THRESHOLD`
69
- (joshuafolkken/kit#2312), so the accumulation a cut would drop is not worth a resume's cost. Nothing
70
- was cut; **continue to the gate.**
62
+ - `under-threshold` (0) — the recent-window per-request cost is under the shared `CONTEXT_CUT_THRESHOLD`,
63
+ so nothing was cut; **continue to the gate.**
71
64
  - `not-a-lane` (0) — no open lane for this issue, so not a dispatched child. **Continue to the gate.**
72
65
  - `unready` (1) — the tree is clean or on the default branch, nothing to carry. Continue to the gate.
73
66
  - `busy` (1) — a cut is already in flight for this tree (the double-cut guard). Do not relaunch.
@@ -79,29 +72,22 @@ the current process to carry the run on itself.
79
72
 
80
73
  ### The gate refuses until the cut has been taken (joshuafolkken/kit#1864)
81
74
 
82
- **This step was carried as prose and fired exactly never.** joshuafolkken/kit#1850 measured six lane
83
- children and the cut was taken **0 times**: four issued the *entry* check
84
- `pnpm josh run:cut --resume <N>` and were answered `fresh`, not one issued the call above. A step a run
85
- is free to skip is the step skipped under time pressure — so `pnpm josh rule:guard` **refuses
86
- `pnpm josh gate`** while this checkout is a lane and no cut record is carried, handing back the command
87
- above. With the ordered step above (joshuafolkken/kit#2177) the refusal is insurance, not the primary
88
- trigger.
89
-
90
- - **It fires for a marked child and nowhere else** (joshuafolkken/kit#1904). The `PreToolUse` hook
91
- reads two facts: the working directory is a lane — `<lane root>/<issue number>` — **and**
92
- `JOSH_LANE_CHILD` names that same issue. An interactive `fullrun` carries no mark and runs its gate
93
- untouched. `run:cut` draws its own line its own way — an open-lane lookup — which is why the guard and
94
- the command need not agree byte for byte; the mark makes the guard's half mechanical.
75
+ **This step was carried as prose and fired exactly never.** Across six measured lane children the cut
76
+ was taken **0 times**. So `pnpm josh rule:guard` **refuses `pnpm josh gate`** while this checkout is a
77
+ lane and no cut record is carried, handing back the command above. With the ordered step above the
78
+ refusal is insurance, not the primary trigger. Rationale:
79
+ `docs/maintainers/pre-gate-cut-rationale.md` → "Why the rules are guards, not prose".
80
+
81
+ - **It fires for a marked child and nowhere else**: the working directory is a lane —
82
+ `<lane root>/<issue number>` — **and** `JOSH_LANE_CHILD` names that same issue. An interactive
83
+ `fullrun` carries no mark and runs its gate untouched. `run:cut` uses an open-lane lookup instead, so
84
+ the guard and the command need not agree byte for byte.
95
85
  - **It fires only when the recent-window context warrants a cut** (joshuafolkken/kit#2312). The guard
96
86
  reads the same `cost_cli.session_verdict` `run:cut` reads, so an `under`-threshold lane is let through
97
- to the gate rather than refused — the guard and the command never disagree about whether a cut was
98
- due. The context read is lazy, after the command and lane checks, so an ordinary run pays nothing for
99
- it.
87
+ to the gate rather than refused.
100
88
  - **It is silent once the cut is carried**, so the resumed process goes straight to the gate.
101
- - **It fires once per run.** Five of the six verdicts leave this process holding the run, each needing
102
- the reissued gate call to pass — a refusal that repeated would wedge exactly the runs that obeyed.
103
- - **`--resume`, `--end` and `--json` do not count as taking the cut**, because they ask about one
104
- rather than take it — counting them would have credited four of the six measured children.
89
+ - **It fires once per run.** A refusal that repeated would wedge the runs that obeyed.
90
+ - **`--resume`, `--end` and `--json` do not count as taking the cut.**
105
91
 
106
92
  The row, its trigger and the enumeration it joins are
107
93
  `prompts/collaboration-workflow/rule-delivery.md`; `scripts/rules/pre-gate-cut.test.ts` pins it.
@@ -131,34 +117,34 @@ pnpm josh run:cut --resume <N>
131
117
  the wrong tree.
132
118
  - `busy` (1) — another process already owns the resume. Send a `confirmation` Telegram and stop.
133
119
 
134
- **A resume is never reported as a success it did not earn.** `stale` and `busy` stop the run rather
135
- than gating, so a cut that lost its work, or one two processes reached at once, is caught.
120
+ **A resume is never reported as a success it did not earn**: `stale` and `busy` stop the run rather
121
+ than gating.
136
122
 
137
123
  ### The stage is passed to the resumed child, not reconstructed by it
138
124
 
139
- (joshuafolkken/kit#1904) A resumed child arrives with `JOSH_LANE_CHILD` still set and `run:cut
140
- --resume` reads the record the cut left, so it learns *where to resume from* at its entry. **Skip the
141
- entry it has already done**: do not re-read the workflow skill, repeat the split assessment or
142
- re-normalize the title; the plan and decisions are read back from the issue. This removes the
143
- reconstruction a stopped child redid on every resume (2.6 minutes / ~150K tokens, joshuafolkken/kit#1876).
125
+ A resumed child arrives with `JOSH_LANE_CHILD` still set and `run:cut --resume` reads the record the cut
126
+ left, so it learns _where to resume from_ at its entry. **Skip the entry it has already done**: do not
127
+ re-read the workflow skill, repeat the split assessment or re-normalize the title; the plan and
128
+ decisions are read back from the issue. Rationale:
129
+ `docs/maintainers/pre-gate-cut-rationale.md` → "Why the resume carries what it carries".
144
130
 
145
131
  ## What is carried, and what is not
146
132
 
147
133
  - **The working tree is not in the record, because it never left the disk.** The record carries only
148
134
  what a fresh process cannot recover — the issue, the branch, the invocation, and that a **declared
149
- cut** put it here. **The conversation is not carried** (joshuafolkken/kit#1567 found compaction does
150
- not reduce billing); the fresh process reads the plan and auto-decisions back off GitHub.
151
- - **The hold is kept, not released** — the uncommitted work is what a second run would trample, so the
152
- resumed process adopts the `run:hold` record and `pnpm josh followup` releases it at the merge.
153
- - **The dispatch mark is re-applied, not inherited** (joshuafolkken/kit#1904): the relaunch strips the
154
- parent's environment and sets `JOSH_LANE_CHILD` afresh from the lane's issue.
135
+ cut** put it here. **The conversation is not carried**; the fresh process reads the plan and
136
+ auto-decisions back off GitHub.
137
+ - **The hold is kept, not released** — the resumed process adopts the `run:hold` record and
138
+ `pnpm josh followup` releases it at the merge.
139
+ - **The dispatch mark is re-applied, not inherited**: the relaunch strips the parent's environment and
140
+ sets `JOSH_LANE_CHILD` afresh from the lane's issue.
155
141
 
156
142
  ## Verification, uniqueness and double-launch
157
143
 
158
144
  - **The resume verifies the tree against the record**: the branch matches, the tree is dirty, the
159
145
  issue matches, and the tree is still under the cutting run's hold. Any mismatch is `stale`.
160
- - **No owner is recorded on the cut.** OpenAI keeps a separate per-worktree supervisor owner and
161
- generation state; the cut is the stage hand-off alone.
146
+ - **No owner is recorded on the cut** — the cut is the stage hand-off alone (OpenAI keeps its
147
+ supervisor owner separately).
162
148
  - **The cut is exclusive** — `run:cut` writes the record with an exclusive create, so a second cut on
163
149
  the same tree is refused `busy`.
164
150
  - **The resume is unique.** Taking the hand-off over spends it — a second `run:cut --resume` reads a
@@ -168,11 +154,8 @@ reconstruction a stopped child redid on every resume (2.6 minutes / ~150K tokens
168
154
 
169
155
  ## The implementation-phase cut — a lane child cuts before the gate too
170
156
 
171
- **The pre-gate cut drops the thinking accumulated _before_ the gate; it does nothing about the thinking
172
- accumulated _during_ implementation** (joshuafolkken/kit#1933). A lane child re-reads its whole
173
- conversation on every request, so a long implementation is billed like a long `backlogrun` parent (which
174
- on 2026-09-13 measured **208k–386k** median context per request). The **implementation-phase cut** caps
175
- that growth — the child ends mid-implementation and a fresh one resumes the lane **back into
157
+ The **implementation-phase cut** (joshuafolkken/kit#1933) caps the context accumulated _during_
158
+ implementation — the child ends mid-implementation and a fresh one resumes the lane **back into
176
159
  implementation**.
177
160
 
178
161
  ### The measurement is the parent hand-off's, never a second one
@@ -186,106 +169,67 @@ built for the lane child.
186
169
 
187
170
  ### It is a guard, fired at the edit that crosses the threshold (joshuafolkken/kit#2310)
188
171
 
189
- **This step was carried as prose and fired exactly never** — the fate the pre-gate cut met before
190
- joshuafolkken/kit#1864. joshuafolkken/kit#1933 reasoned it *could not* be a guard, because the
191
- per-request cost was read **asynchronously**; but `cost_cli.session_verdict` is synchronous and is the
192
- exact verdict `pnpm josh cost --cut` prints. joshuafolkken/kit#2310 measured the child-side check across
193
- five lanes: the verdict was read once each at session entry, before the context grew, so the cut fired
194
- **0 times** while 33.9% of their requests ran past 200,000 tokens.
172
+ **This step was carried as prose and fired exactly never** — measured across five lanes, the cut fired
173
+ **0 times** while 33.9% of their requests ran past 200,000 tokens. Rationale:
174
+ `docs/maintainers/pre-gate-cut-rationale.md` → "Why the rules are guards, not prose".
195
175
 
196
176
  So `pnpm josh rule:guard` **refuses an `Edit` / `Write`** while this checkout is a dispatched lane child
197
- whose recent-context cost is over threshold, handing back `pnpm josh run:cut --impl <N> --handoff <path>` — a
198
- `PreToolUse` refusal lands *before* the edit, so the tree is at the state the previous edit left it. `cut`
199
- ends the turn, the rest leave this process implementing; a fresh process's `pnpm josh run:cut --resume
200
- <N>` then answers **`resume-impl`**, so it **skips the title, plan, hold claim and split assessment** and
201
- **continues implementation** rather than gating. Unlike the pre-gate cut, **the implementation resume
202
- clears the record** (joshuafolkken/kit#2310): the pre-gate cut fires once per lane so its record must
203
- survive to answer a second resume `handed-off` (joshuafolkken/kit#1935), but this one may fire again, so
204
- its record is removed on resume. A double cut stays impossible — `begin_cut`'s exclusive create prevents
205
- it — so a lane crosses this boundary several times, each with exactly one successor.
206
-
207
- - **It fires for a marked child and nowhere else** — the dispatch mark names this lane's own issue, so a
208
- person's own `fullrun` carries no mark and sees no refusal.
209
- - **It fires on every threshold crossing, not once per run** (joshuafolkken/kit#2385) — once per run
210
- silenced the row after its first refusal, so a `busy` / `failed` / `unready` verdict, or an edit
211
- reissued unchanged, left the context to grow unwatched (joshuafolkken/kit#2382 ran to 282,747 tokens,
212
- the cut never taken). The row now carries `decide` and re-asks on each over-threshold edit.
213
- - **An edit reissued right after a refusal passes** (joshuafolkken/kit#2385) — so a `busy` / `failed` cut
214
- cannot wedge the run: the reissue lands inside a short window and goes through, a genuinely new crossing
215
- past it refuses again.
216
- - **An unmeasurable session warrants the cut** (joshuafolkken/kit#2385) — the `verdict !== UNDER_VERDICT`
217
- reading matches the pre-gate cut's `warrants_the_cut`, so they never disagree.
177
+ whose recent-context cost is over threshold, handing back `pnpm josh run:cut --impl <N> --handoff <path>` — the
178
+ refusal lands _before_ the edit. `cut` ends the turn, the rest leave this process implementing; a fresh
179
+ process's `pnpm josh run:cut --resume <N>` then answers **`resume-impl`**, so it **skips the title, plan,
180
+ hold claim and split assessment** and **continues implementation** rather than gating. Unlike the
181
+ pre-gate cut, **the implementation resume clears the record** — it may fire again, unlike the
182
+ once-per-lane pre-gate cut (joshuafolkken/kit#1935); `begin_cut`'s exclusive create still gives each
183
+ crossing one successor.
184
+
185
+ - **It fires for a marked child, or a run held outside a lane on a measured `over`**
186
+ (joshuafolkken/kit#2760); a person's tree holds no run. Outside a lane `cut` relaunches nothing: keep
187
+ the hold, send a `confirmation` Telegram naming `fullrun #<N>`, end the turn.
188
+ - **It fires on every threshold crossing, not once per run** (joshuafolkken/kit#2385) — it re-asks on
189
+ each over-threshold edit, including after a `busy` / `failed` / `unready` verdict.
190
+ - **An edit reissued right after a refusal passes** — so a `busy` / `failed` cut cannot wedge the run:
191
+ the reissue lands inside a short window and goes through, a genuinely new crossing past it refuses
192
+ again.
193
+ - **An unmeasurable session warrants the cut** in a lane, as the pre-gate cut's does.
218
194
  - **It is silent between a cut and its resume** — a carried record naming this issue keeps it quiet; the
219
195
  resume clears it, and the fresh process reads its own transcript under threshold.
220
- - **The verdict read is reused over a short window** (joshuafolkken/kit#2385) — the whole-transcript
221
- price is a candidate on every edit now, so it is cached per checkout for a few seconds: a turn's burst
222
- of edits shares one read.
196
+ - **The verdict read is reused over a short window** — cached per checkout for a few seconds, so a
197
+ turn's burst of edits shares one read.
223
198
 
224
199
  `scripts/rules/implementation-cut.ts` implements the trigger, `scripts/rules/implementation-cut.test.ts`
225
200
  pins it, and the row joins `prompts/collaboration-workflow/rule-delivery.md`. **A child that ended
226
201
  without cutting is still detected after the fact by `pnpm josh run:ending <N> --output <path>`**
227
202
  (joshuafolkken/kit#2139). **A single check (`lint:related` / `test:related`) is run after a batch of
228
- edits, not after each one** — every check is a request too (joshuafolkken/kit#1383).
203
+ edits, not after each one.**
229
204
 
230
205
  ## The threshold, and the statistic it is measured against
231
206
 
232
- **The `CONTEXT_CUT_THRESHOLD` (135_000) is one value; what joshuafolkken/kit#2295 changed is the
233
- statistic — `cost_verdict.per_request_cost` now averages the most recent `RECENT_REQUEST_WINDOW` (10)
234
- requests rather than the whole session.** It is still *one* measurement both read. This supersedes
235
- joshuafolkken/kit#2282, which kept the whole-session average and rejected this option on the batch it
236
- measured — short-lived lane children with no parent session, for which the two averages nearly coincide.
237
-
238
- - **The safety net now fires.** Built (joshuafolkken/kit#1933) for a pathological regime a typical lane
239
- never enters, 135_000 sits below the 208k floor of that regime, so a runaway implementation still trips
240
- it — and joshuafolkken/kit#2310 made the guard trip actively at the crossing edit.
241
- - **The recent window catches the long parent the whole-session average missed.** The 2026-09-21
242
- parent `567f8eac` (186 requests, 7 hours) first crossed 200k at request 65, but its whole-session
243
- average did not until request 150 — **85 requests (~4 hours) later**, ~$7.88 supervising an oversized
244
- parent past the point a cut was due. The recent-10 average removes that lag.
245
- - **It is not the forbidden second measurement.** joshuafolkken/kit#1933 forbids the child pricing its
246
- cut on a statistic the parent does not share; #2295 keeps one statistic both read and redefines it.
247
-
248
- ### The value is a break-even, not a chosen number (joshuafolkken/kit#2406)
249
-
250
- **The threshold is derived.** joshuafolkken/kit#2374 set 150_000; joshuafolkken/kit#2406 replaced the
251
- hand-picked number with the arithmetic behind it. A cut costs one preamble rewrite
252
- (`POST_CUT_CONTEXT`, ~60_000 tokens) and saves the dropped context's cache read on every later request,
253
- so it turns on a rate, not a ceiling: `context-cut-payback.ts` computes the per-request context at which a
254
- cut pays back over the expected remaining requests, from the cache multipliers in `cost-pricing.ts` (no
255
- second price list). At a 10-request horizon that is **135_000**, in #2374's band; the horizon is the knob
256
- a before/after lane measurement tunes (#2406 PR).
257
-
258
- ### The cut is conditional, and the resume side is why (joshuafolkken/kit#2312)
259
-
260
- **Until joshuafolkken/kit#2312 the pre-gate cut was _unconditional_, and its case recorded only the
261
- gain** — the context reduction it carries (~130k → ~60k per request). **The resume side was never
262
- priced.** A cut relaunches a fresh process that re-reads the plan and decisions off GitHub before the
263
- gate — a fixed ramp of ~12 requests per session. That ramp was **$81.01 of $435.09 (19%)**
264
- across the 32 lanes of 2026-09-21, falling hardest on the short lanes with the least to drop: #2282 spent
265
- **58%** of its billing on it at 60k median context, #2295 **39%** at 74k, #2289 **37%** at 81k — each
266
- with **0%** of its requests over 200k.
267
-
268
- **So the cut is now taken only when the recent-window per-request cost is over `CONTEXT_CUT_THRESHOLD`**
269
- — the same statistic and threshold the implementation-phase cut reads, so **no second threshold is
270
- introduced**. Below it `run:cut` answers `under-threshold` and the gate runs uncut; the guard reads the
271
- same verdict and stays silent. A lane like #2298 (31% of requests over 200k) still cuts as before, and an
272
- **unmeasurable** session keeps the old unconditional cut so it is never left uncut.
207
+ **`CONTEXT_CUT_THRESHOLD` (135_000) is one value, and `cost_verdict.per_request_cost` — the one
208
+ statistic the parent and the lane child both read — averages the most recent `RECENT_REQUEST_WINDOW`
209
+ (10) requests, not the whole session** (joshuafolkken/kit#2295). The value is a derived break-even, not a
210
+ chosen number (joshuafolkken/kit#2406): `scripts/cost-runtime/context-cut-payback.ts` computes it from a
211
+ cut's one-time preamble rewrite (`POST_CUT_CONTEXT`, ~60_000 tokens — a cut carries a request from ~130k
212
+ down to ~60k), the cache multipliers in `cost-pricing.ts`, and a 10-request horizon.
213
+
214
+ **The cut is conditional** (joshuafolkken/kit#2312): taken only when the recent-window per-request cost
215
+ is over `CONTEXT_CUT_THRESHOLD` — the same statistic and threshold the implementation-phase cut reads, so
216
+ no second threshold exists. Below it `run:cut` answers `under-threshold` and the gate runs uncut; the
217
+ guard reads the same verdict and stays silent. An **unmeasurable** session is always cut.
218
+ Rationale: `docs/maintainers/pre-gate-cut-rationale.md` → "Why the threshold is a break-even".
273
219
 
274
220
  ## Consistency with the chain rule
275
221
 
276
222
  The chain forbids ending a turn at the push, with CI in flight and nothing set to resume. **The pre-gate
277
223
  cut and the detached hand-off end it _before_ the gate** and start what carries the run on in the same
278
224
  act — a fresh process, or the supervisor — so the push is never a turn boundary. The implementation-phase
279
- cut is the same pattern one boundary earlier (joshuafolkken/kit#1933).
225
+ cut is the same pattern one boundary earlier.
280
226
 
281
227
  ## A lane child records its park before it stops
282
228
 
283
229
  **A dispatched lane child records its park on the Issue before it stops for a decision**
284
- (joshuafolkken/kit#2034). A headless child's only route to ask a person anything is to park the Issue —
285
- `needs-decision` and a comment carrying the question. Measured twice on 2026-09-14: **#2012**'s child had
286
- its `AskUserQuestion` refused and **#2011** wrote a Tier B decision, both only to their final message, so
287
- the parent found an OPEN Issue with `in-progress` and no question. The child is the only process that
288
- knows the question, so it records the park itself rather than leaving the parent to reconstruct it.
230
+ (joshuafolkken/kit#2034). A headless child's only route to ask a person is to park the Issue — measured
231
+ on #2012 and #2011, whose questions reached only their final message. Rationale:
232
+ `docs/maintainers/pre-gate-cut-rationale.md` → "Why the rules are guards, not prose".
289
233
 
290
234
  **The record, before the Telegram, is the park procedure exactly** — `needs-decision` plus a comment
291
235
  carrying the question, the options, and whether work was stashed. Its single source is
@@ -299,18 +243,16 @@ would otherwise leave the Issue bare.
299
243
 
300
244
  ### The stop notify refuses until the park is recorded
301
245
 
302
- **This was prose that would have fired never**: a step a child is free to skip under time pressure is
303
- the step it skips. So `pnpm josh rule:guard` **refuses `pnpm josh notify --task-type confirmation`**
304
- while this checkout is a lane child — the mark `JOSH_LANE_CHILD` names this lane's own issue — handing
305
- back the park commands.
246
+ `pnpm josh rule:guard` **refuses `pnpm josh notify --task-type confirmation`** while this checkout is a
247
+ lane child — the mark `JOSH_LANE_CHILD` names this lane's own issue — handing back the park commands.
306
248
 
307
249
  - **It fires for a marked child and nowhere else.** A person's own `fullrun` carries no mark and sends
308
250
  its stop notify untouched.
309
251
  - **It fires once per run.** The child records the park and reissues the same notify; a
310
252
  `needs-human-review` or `already-done` stop reissues after the one delivery, its label already on the
311
253
  Issue.
312
- - **The park state is not readable synchronously.** A `PreToolUse` guard makes no network call and the
313
- park is a GitHub label, so the delivery is a checklist — one wasted reissue on the compliant path is safe.
254
+ - **The park state is not readable synchronously**, so the delivery is a checklist — one wasted reissue
255
+ on the compliant path is safe.
314
256
 
315
257
  This section is the single source of the lane-child park rule; `scripts/rules/lane-park.ts` implements
316
258
  the trigger, `scripts/rules/lane-park.test.ts` pins it, and the row joins the enumeration in
@@ -318,18 +260,11 @@ the trigger, `scripts/rules/lane-park.test.ts` pins it, and the row joins the en
318
260
 
319
261
  ### The interactive ask is refused one call earlier
320
262
 
321
- **The stop-notify guard fired one tool-call too late** (joshuafolkken/kit#2201). A child that reaches
322
- for `AskUserQuestion` never reaches the notify: the harness refuses an interactive ask in a headless
323
- session by **ending the turn**, so the notify guard never fires and the question dies in the exit
324
- record's `permission_denials`. Measured on 2026-09-20 inside `backlogrun #2163 --only`: **#2178**'s
325
- child hit a Tier B branch, called `AskUserQuestion`, was refused, and left an OPEN Issue with
326
- `in-progress` and no question — the failure joshuafolkken/kit#2034 set out to prevent, one call
327
- upstream.
328
-
329
- So `pnpm josh rule:guard` **refuses `AskUserQuestion` for a marked lane child**, one call before the
330
- stop it would have reached. A hook deny returns to the model rather than ending the turn, so the
331
- refusal becomes an instruction — park the question. It fires on **every occurrence** and stays silent
332
- for a person working in a lane.
263
+ (joshuafolkken/kit#2201) The harness refuses an interactive ask in a headless session by **ending the
264
+ turn**, so the notify guard never fires — #2178's child did exactly that, the failure
265
+ joshuafolkken/kit#2034 set out to prevent, one call upstream. So `pnpm josh rule:guard` **refuses
266
+ `AskUserQuestion` for a marked lane child**; the deny returns to the model, and the instruction is to
267
+ park the question. It fires on **every occurrence** and stays silent for a person working in a lane.
333
268
 
334
269
  **A child that slips past it is still recoverable** — `pnpm josh run:ending` lifts the refused ask out
335
270
  of the exit record's `permission_denials` into the `abandoned` verdict's park basis, so the parent puts
@@ -15,6 +15,13 @@ on:
15
15
  # nothing here needs it: every run reads the pull request's labels as they stand when it runs, not as
16
16
  # its event carried them, so whichever suite GitHub counts as newest holds a current verdict.
17
17
 
18
+ # The same safe-chain installer pin as ci.yml — the release and its install-safe-chain.sh SHA-256.
19
+ # `pnpm dlx … setup-ci` left only shims, so the install below ran without a scan (joshuafolkken/kit#2765).
20
+ # `josh latest` moves both together, in this file and in ci.yml.
21
+ env:
22
+ SAFE_CHAIN_INSTALLER_VERSION: 1.5.20
23
+ SAFE_CHAIN_INSTALLER_SHA256: 0ad25efe15d1fa56105157a454d647223e78eb0c53d1f85e3d10afcd722e7bfd
24
+
18
25
  jobs:
19
26
  classification:
20
27
  name: Release classification
@@ -63,7 +70,11 @@ jobs:
63
70
  ${{ runner.os }}-pnpm-store-
64
71
 
65
72
  - name: Setup safe-chain
66
- run: pnpm dlx @aikidosec/safe-chain@1.5.20 setup-ci
73
+ run: |
74
+ installer="$RUNNER_TEMP/install-safe-chain.sh"
75
+ curl --proto '=https' -fsSL "https://github.com/AikidoSec/safe-chain/releases/download/$SAFE_CHAIN_INSTALLER_VERSION/install-safe-chain.sh" -o "$installer"
76
+ echo "$SAFE_CHAIN_INSTALLER_SHA256 $installer" | sha256sum -c -
77
+ sh "$installer" --ci
67
78
 
68
79
  - name: Install dependencies
69
80
  env:
package/CLAUDE.md CHANGED
@@ -89,7 +89,7 @@ Read from a `.env` file at the project root by the AI scripts, `josh port` and `
89
89
 
90
90
  ### Dependency overrides (`pnpm-workspace.yaml` / `package.json`)
91
91
 
92
- - **Effective overrides live in `pnpm-workspace.yaml`.** pnpm 11 and 12 ignore `pnpm.overrides` in `package.json` (verified with pnpm 12.6.0). Check both files for existing declarations, but never count the ignored package field as an effective override. **An absent or empty `pnpm.overrides` is not evidence that the project has no overrides.**
92
+ - **Effective overrides live in `pnpm-workspace.yaml`.** pnpm 11 and 12 ignore `pnpm.overrides` in `package.json`. Check both files for existing declarations, but never count the ignored package field as an effective override. **An absent or empty `pnpm.overrides` is not evidence that the project has no overrides.**
93
93
  - **NEVER** remove or modify entries in **either** location without explicit user approval.
94
94
  - **NEVER** modify the `devEngines` field in `package.json` without explicit user confirmation. It pins the development toolchain (e.g. pnpm version); silently changing it can break CI or other contributors' environments.
95
95
  - **The check is a command you run, not a conclusion you reach.** After `pnpm update`, `josh latest`, or any dependency-update command, **load the `dependency-update` skill** and follow its procedure before reporting anything about the pins.
@@ -144,7 +144,7 @@ Before every `git commit` (follow-up commits included), self-review against `pro
144
144
 
145
145
  ## Doc Sync Rules
146
146
 
147
- **`CLAUDE.md` is the single source for every agent rule.** `AGENTS.md` and `GEMINI.md` carry no rules — a rule addition, spec change or wording fix is written **once**, here (joshuafolkken/kit#963). The structural test [scripts/document/ai-document-pointers.test.ts](https://github.com/joshuafolkken/kit/blob/main/scripts/document/ai-document-pointers.test.ts) fails if a rule body reappears in either, or if a pointer loses the sentence that sends an agent here.
147
+ **`CLAUDE.md` is the single source for every agent rule.** `AGENTS.md` and `GEMINI.md` carry no rules — a rule addition, spec change or wording fix is written **once**, here (joshuafolkken/kit#963).
148
148
 
149
149
  **docs/ must stay in sync with the package.** When `josh bump` changes the version, review `docs/` and update any section describing changed behavior (new/renamed commands, `josh init` / `josh sync` behavior, new config files) before committing.
150
150
 
@@ -166,7 +166,7 @@ Before every `git commit` (follow-up commits included), self-review against `pro
166
166
 
167
167
  `kickoff`, `fullrun`, `halfrun` and `backlogrun` are the Issue-driven shorthand commands. **Their procedures are not resident** — they live in the `workflow-commands` skill. **What stays here is decided by one question: must the rule fire on a turn where no skill was loaded?** Explicit invocation, the mid-workflow stop notification, the `overrides` / `devEngines` prohibitions, the UI-verification gate and the three `epic:*` rules below all do; everything a run reaches only after reading the skill is routed to, never restated (`SKILL.md` → "What stays resident, and what is read from here").
168
168
 
169
- **Read the skill before running any part of a command — including the first `gh` call.** Acting from the table below alone is not enough: the table says which command was typed, not how to run it.
169
+ **Read the skill before running any part of a command — including the first `gh` call.**
170
170
 
171
171
  | Typed keyword | What it does | Read first |
172
172
  | ------------------------ | --------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ---------------------------------------------------------------- |