@joshuafolkken/kit 1.1455.0 → 1.1471.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 (211) hide show
  1. package/.claude/skills/dependency-update/SKILL.md +26 -55
  2. package/.claude/skills/epic-commands/SKILL.md +87 -148
  3. package/.claude/skills/epic-commands/epic-bundle.md +45 -77
  4. package/.claude/skills/epic-commands/execution-waves.md +44 -81
  5. package/.claude/skills/verify-ui/SKILL.md +25 -50
  6. package/.claude/skills/workflow-commands/background-commands.md +36 -47
  7. package/.claude/skills/workflow-commands/backlogrun-child.md +72 -85
  8. package/.claude/skills/workflow-commands/backlogrun-lanes.md +91 -121
  9. package/.claude/skills/workflow-commands/backlogrun-park.md +73 -117
  10. package/.claude/skills/workflow-commands/backlogrun-progress.md +7 -9
  11. package/.claude/skills/workflow-commands/backlogrun-recovery.md +11 -20
  12. package/.claude/skills/workflow-commands/backlogrun-steps.md +43 -52
  13. package/.claude/skills/workflow-commands/chain-rule.md +24 -34
  14. package/.claude/skills/workflow-commands/followup.md +57 -76
  15. package/.claude/skills/workflow-commands/issue-comments.md +43 -60
  16. package/.claude/skills/workflow-commands/kickoff.md +65 -74
  17. package/.claude/skills/workflow-commands/observation-filing.md +62 -107
  18. package/.claude/skills/workflow-commands/observation-ledger.md +56 -111
  19. package/.claude/skills/workflow-commands/pre-gate-cut.md +89 -138
  20. package/.claude/skills/workflow-commands/prerequisite.md +10 -15
  21. package/.claude/skills/workflow-commands/progress-watcher.md +21 -26
  22. package/.claude/skills/workflow-commands/retrospective.md +31 -50
  23. package/.claude/skills/workflow-commands/split-assessment.md +34 -58
  24. package/.claude/skills/workflow-commands/working-tree-hold.md +54 -71
  25. package/CLAUDE.md +6 -5
  26. package/dist/CLAUDE.md +6 -5
  27. package/dist/hooks/batch-guard.js +6 -6
  28. package/dist/hooks/{chunk-7WQBPLUN.js → chunk-43ZHT22P.js} +31 -15
  29. package/dist/hooks/{chunk-LIAFCXL7.js → chunk-AS74OU3D.js} +23 -7
  30. package/dist/hooks/{chunk-3C4XRYG6.js → chunk-E7OIKX5Z.js} +17 -12
  31. package/dist/hooks/{chunk-Z4XI7CZS.js → chunk-EUMACUK2.js} +18 -12
  32. package/dist/hooks/{chunk-WNLVGT7E.js → chunk-H5HVWYXK.js} +4 -4
  33. package/dist/hooks/{chunk-HHI6FO3R.js → chunk-IIGFYXKC.js} +345 -240
  34. package/dist/hooks/{chunk-GPLGZ6IC.js → chunk-J47HU5H5.js} +14 -3
  35. package/dist/hooks/{chunk-OLMO72ON.js → chunk-K4EOONYM.js} +1 -1
  36. package/dist/hooks/{chunk-D5QBTOCZ.js → chunk-K4LMBQC2.js} +4 -4
  37. package/dist/hooks/{chunk-TBJFTSUY.js → chunk-KC5AK2GF.js} +8 -6
  38. package/dist/hooks/chunk-SHGPANFB.js +56 -0
  39. package/dist/hooks/{chunk-5H4SSDSD.js → chunk-YHBZAJSV.js} +3 -3
  40. package/dist/hooks/{chunk-HFGWYGUV.js → chunk-YYPI7SJZ.js} +5 -5
  41. package/dist/hooks/codex-hook-adapter.js +13 -13
  42. package/dist/hooks/duplicate-read-guard.js +7 -7
  43. package/dist/hooks/format-edited.js +3 -3
  44. package/dist/hooks/inputs.json +216 -214
  45. package/dist/hooks/investigation-guard.js +7 -7
  46. package/dist/hooks/pretool-guard.js +12 -12
  47. package/dist/hooks/session-lang.js +6 -5
  48. package/dist/hooks/stop-guard.js +18 -14
  49. package/dist/josh.js +5 -6
  50. package/dist/managed-marker/index.js +1 -2
  51. package/dist/version/index.js +15 -12
  52. package/eslint/base.js +2 -1
  53. package/eslint/rules/unicorn.js +16 -0
  54. package/package.json +3 -3
  55. package/prompts/coding-standards.md +12 -22
  56. package/prompts/collaboration-workflow/file-edits.md +26 -44
  57. package/prompts/collaboration-workflow/gh-rest.md +20 -36
  58. package/prompts/collaboration-workflow/glossary.md +5 -7
  59. package/prompts/collaboration-workflow/issue-citation.md +7 -13
  60. package/prompts/collaboration-workflow/issue-template.md +29 -29
  61. package/prompts/collaboration-workflow/operating-rules.md +35 -44
  62. package/prompts/collaboration-workflow/output-bounds.md +15 -15
  63. package/prompts/collaboration-workflow/overview.md +14 -19
  64. package/prompts/collaboration-workflow/plan-comment.md +13 -13
  65. package/prompts/collaboration-workflow/principles.md +50 -53
  66. package/prompts/collaboration-workflow/proposal-request.md +1 -9
  67. package/prompts/collaboration-workflow/report-format.md +32 -49
  68. package/prompts/collaboration-workflow/residency.md +10 -10
  69. package/prompts/collaboration-workflow/rule-delivery.md +9 -9
  70. package/prompts/collaboration-workflow/shell-body.md +10 -20
  71. package/prompts/collaboration-workflow/turn-batching.md +8 -14
  72. package/prompts/collaboration-workflow/upstream-interrupt.md +26 -34
  73. package/prompts/collaboration-workflow/wip-cap.md +24 -30
  74. package/prompts/collaboration-workflow.md +24 -25
  75. package/prompts/refactoring.md +33 -43
  76. package/prompts/review-rubric.md +84 -129
  77. package/prompts/review.md +111 -141
  78. package/prompts/sonar-hotspot-handling.md +22 -50
  79. package/prompts/testing-guide.md +48 -99
  80. package/scripts/adopt/adopt-toolkits.ts +2 -4
  81. package/scripts/agent/agent-exit-record.ts +23 -1
  82. package/scripts/agent/codex-agent-argv.ts +7 -2
  83. package/scripts/backlog/backlog-budget-cli.ts +1 -2
  84. package/scripts/backlog/backlog-drive-carried.ts +26 -0
  85. package/scripts/backlog/backlog-drive-cli.ts +10 -5
  86. package/scripts/backlog/backlog-drive-named.ts +2 -4
  87. package/scripts/backlog/backlog-drive-restore.ts +19 -1
  88. package/scripts/backlog/backlog-drive-seats.ts +27 -0
  89. package/scripts/backlog/backlog-drive.ts +11 -6
  90. package/scripts/backlog/backlog-ready.ts +13 -6
  91. package/scripts/behavior/behavior-rules.ts +3 -2
  92. package/scripts/cost-runtime/codex-usage.ts +4 -2
  93. package/scripts/cost-runtime/cost-cli.ts +1 -2
  94. package/scripts/delegation/investigation-guard.ts +1 -1
  95. package/scripts/delegation/investigation-reads.ts +1 -1
  96. package/scripts/discovery/repo-discovery.ts +4 -2
  97. package/scripts/discovery/repo-origin.ts +2 -4
  98. package/scripts/doctor/doctor-logic.ts +6 -3
  99. package/scripts/document/document-byte-budget.ts +27 -27
  100. package/scripts/document/document-byte-check.ts +1 -2
  101. package/scripts/document/entry-read-budget.ts +2 -2
  102. package/scripts/epic/epic-audit-rationale.ts +8 -2
  103. package/scripts/epic/epic-bundle-cli.ts +1 -2
  104. package/scripts/epic/epic-chains.ts +6 -2
  105. package/scripts/epic/epic-close-comment.ts +1 -2
  106. package/scripts/gate/verification-gate.ts +7 -2
  107. package/scripts/gh/git-pr.ts +6 -2
  108. package/scripts/git/git-common-directory.ts +3 -3
  109. package/scripts/git/git-diff-reads.ts +16 -9
  110. package/scripts/git/git-execa-error.ts +1 -2
  111. package/scripts/hooks/format-edited-file.ts +10 -4
  112. package/scripts/hooks/pre-push-unit.ts +15 -4
  113. package/scripts/hooks/pretool-guard.ts +23 -29
  114. package/scripts/hooks/step-zero-notice.ts +3 -2
  115. package/scripts/init/hook-command-rewrite.ts +6 -2
  116. package/scripts/init/init-logic-deploy-vps.ts +4 -2
  117. package/scripts/issue/behavior-change-lint.ts +12 -6
  118. package/scripts/issue/issue-scout-cli.ts +3 -2
  119. package/scripts/josh/file-map-stamp.ts +1 -3
  120. package/scripts/josh/hook-decision.ts +17 -2
  121. package/scripts/lane/lane-await.ts +3 -2
  122. package/scripts/lane/lane-child-invocation.ts +14 -1
  123. package/scripts/lane/lane-guard-policy.ts +4 -4
  124. package/scripts/lane/lane-ledger.ts +9 -0
  125. package/scripts/lane/lane-load.ts +9 -2
  126. package/scripts/lane/lane-measure-cli.ts +2 -0
  127. package/scripts/lane/lane-parked.ts +18 -0
  128. package/scripts/lane/lane-process-load.ts +125 -0
  129. package/scripts/lane/lane-process-stats.ts +93 -0
  130. package/scripts/lane/lane-registry.ts +8 -3
  131. package/scripts/lane/lane-session-broker.ts +29 -5
  132. package/scripts/lane/lane-spent.ts +14 -3
  133. package/scripts/lane/lane-stats.ts +1 -0
  134. package/scripts/managed-marker/managed-marker-logic.ts +1 -2
  135. package/scripts/overrides/lockfile-overrides.ts +1 -3
  136. package/scripts/propagate/propagate.ts +1 -2
  137. package/scripts/release/release-progress.ts +3 -2
  138. package/scripts/report/report-lint.ts +7 -4
  139. package/scripts/review/review-record-cli.ts +6 -2
  140. package/scripts/rules/destructive-command.ts +6 -9
  141. package/scripts/rules/direct-filing.ts +3 -7
  142. package/scripts/rules/early-heartbeat.ts +7 -18
  143. package/scripts/rules/file-body.ts +6 -13
  144. package/scripts/rules/filing-cap.ts +3 -6
  145. package/scripts/rules/git-force.ts +5 -9
  146. package/scripts/rules/implementation-cut.ts +37 -35
  147. package/scripts/rules/index-guard.ts +4 -7
  148. package/scripts/rules/issue-comments.ts +5 -15
  149. package/scripts/rules/josh-git-bare.ts +3 -6
  150. package/scripts/rules/lane-background.ts +10 -21
  151. package/scripts/rules/lane-carry-conflict.ts +7 -17
  152. package/scripts/rules/lane-interactive-ask.ts +6 -11
  153. package/scripts/rules/lane-park.ts +5 -13
  154. package/scripts/rules/lane-split-park.ts +2 -7
  155. package/scripts/rules/lane-switch-main.ts +9 -12
  156. package/scripts/rules/oracle-consulted.ts +4 -5
  157. package/scripts/rules/piped-verification.ts +81 -31
  158. package/scripts/rules/poll-loop.ts +7 -17
  159. package/scripts/rules/pre-gate-cut.ts +7 -21
  160. package/scripts/rules/prior-comment-read.ts +7 -5
  161. package/scripts/rules/protected-files.ts +5 -9
  162. package/scripts/rules/raw-field-body.ts +3 -7
  163. package/scripts/rules/rule-body-guard.ts +4 -11
  164. package/scripts/rules/run-tail-rule.ts +6 -22
  165. package/scripts/rules/shell-body-trigger.ts +4 -6
  166. package/scripts/rules/shell-segments.ts +7 -0
  167. package/scripts/rules/stop-rules.ts +16 -29
  168. package/scripts/rules/test-declared-commit.ts +4 -10
  169. package/scripts/rules/third-party-write.ts +5 -10
  170. package/scripts/rules/worktree-guard.ts +5 -12
  171. package/scripts/run/board/run-board-github.ts +11 -3
  172. package/scripts/run/board/run-board-idle.ts +31 -0
  173. package/scripts/run/board/run-board-read.ts +9 -12
  174. package/scripts/run/board/run-board-reason.ts +7 -3
  175. package/scripts/run/board/run-board-state.ts +4 -0
  176. package/scripts/run/board/run-board-status.ts +18 -3
  177. package/scripts/run/board/run-board-thanks.ts +7 -2
  178. package/scripts/run/board/run-board-tick.ts +32 -7
  179. package/scripts/run/carry/run-carry-args.ts +1 -3
  180. package/scripts/run/carry/run-carry-conversation.ts +7 -2
  181. package/scripts/run/cut/run-cut-cli.ts +15 -20
  182. package/scripts/run/cut/run-cut-decline.ts +72 -0
  183. package/scripts/run/cut/run-cut-ready.ts +74 -0
  184. package/scripts/run/cut/run-cut-report.ts +13 -0
  185. package/scripts/run/cut/run-cut.ts +10 -1
  186. package/scripts/run/hold/run-hold-cli.ts +14 -3
  187. package/scripts/run/hold/run-hold.ts +6 -3
  188. package/scripts/run/merge/run-merge-cli.ts +30 -7
  189. package/scripts/run/merge/run-merge-ship.ts +59 -1
  190. package/scripts/run/merge/run-merge.ts +3 -2
  191. package/scripts/run/run-ending.ts +31 -3
  192. package/scripts/run/run-parent-cut-hook.ts +5 -10
  193. package/scripts/run/run-preflight.ts +1 -2
  194. package/scripts/run/run-stop-notify.ts +1 -2
  195. package/scripts/run/run-watcher-guard.ts +6 -3
  196. package/scripts/run/ship/run-ship-review-steps.ts +13 -4
  197. package/scripts/run/ship/run-ship-review.ts +15 -2
  198. package/scripts/run/tidy/run-tidy-cli.ts +3 -3
  199. package/scripts/run/tidy/run-tidy-lanes.ts +3 -2
  200. package/scripts/safe-chain/project-config.ts +7 -2
  201. package/scripts/sync/sync-hook-safety.ts +8 -2
  202. package/scripts/test/unit-worker-share.ts +3 -2
  203. package/scripts/time-runtime/time-batch-guard.ts +3 -3
  204. package/scripts/time-runtime/time-hook-transcript.ts +3 -2
  205. package/scripts/time-runtime/time-shell.ts +10 -0
  206. package/scripts/version/effective-upstream.ts +3 -4
  207. package/scripts/version/kit-version-config.ts +8 -2
  208. package/scripts/version/latest-corepack.ts +1 -2
  209. package/scripts/version/release-hold.ts +11 -4
  210. package/scripts/version/version-commands.ts +12 -4
  211. package/dist/hooks/chunk-UT7YC3N2.js +0 -56
@@ -5,76 +5,47 @@ description: Checks after `pnpm update`, `josh latest`, `josh adopt`, `pnpm josh
5
5
 
6
6
  # After a dependency-update command
7
7
 
8
- `CLAUDE.md` → "Decision autonomy" keeps both prohibitions resident as Tier C — never touch `overrides`
9
- in either file, never touch `devEngines`, without explicit user approval. This skill is the other
10
- half: what you actually run to find out whether a command already touched them, and how to read the
11
- one change that is expected. It applies after `pnpm update`, `josh latest`, `josh adopt` (which runs
12
- `josh latest` as one of its steps), `pnpm josh overrides`, a Dependabot merge, or any other command
13
- that can rewrite dependency versions.
14
-
15
- This skill is the single source of the overrides protection's procedure;
16
- `prompts/collaboration-workflow/operating-rules.md` only points here. Overrides were added on
17
- purpose — for security, compatibility or a working guarantee (for example
18
- `"esbuild@<=0.24.2": ">=0.25.0"` for Workers build compatibility).
8
+ `CLAUDE.md` → "Decision autonomy" keeps both prohibitions resident as Tier C: never touch `overrides`,
9
+ never touch `devEngines`, without explicit user approval. This skill is the single source of how to check
10
+ whether a command touched them, after `pnpm update`, `josh latest`, `josh adopt` (which runs
11
+ `josh latest`), `pnpm josh overrides`, a Dependabot merge, or any command that rewrites dependency
12
+ versions. Overrides exist on purpose (e.g. `"esbuild@<=0.24.2": ">=0.25.0"` for Workers builds).
19
13
 
20
14
  ## 1. Effective overrides live in the workspace — inspect both files
21
15
 
22
- **pnpm 11 and 12 read effective overrides only from `pnpm-workspace.yaml`.** The old
23
- `pnpm.overrides` field in **`package.json` is ignored**, as verified with pnpm 12.6.0. Inspect both
24
- files to preserve any existing declarations, but do not count the package field as an effective
25
- override. An absent or empty package field says nothing about workspace overrides: app-kit's
26
- `package.json` has no `pnpm` field, while its workspace has a real override.
16
+ **pnpm 11 and 12 read effective overrides only from `pnpm-workspace.yaml`.** `pnpm.overrides` in
17
+ **`package.json` is ignored** (verified with pnpm 12.6.0). Inspect both to preserve existing
18
+ declarations, but an absent package field says nothing about workspace overrides.
27
19
 
28
20
  ## 2. The check is a command you run, not a conclusion you reach
29
21
 
30
- **The check is a command you run, not a conclusion you reach.** After `pnpm update`, `josh latest`,
31
- or any dependency-update command, verify the overrides in both `pnpm-workspace.yaml` and
32
- `package.json` by running
33
-
34
22
  ```bash
35
23
  git diff -- pnpm-workspace.yaml package.json
36
24
  ```
37
25
 
38
- (after `josh adopt`, which has already committed and returned to the default branch, compare the
39
- pull-request branch instead: `git diff <default>...<branch> -- pnpm-workspace.yaml package.json`)
40
- and confirm the effective `overrides:` block in `pnpm-workspace.yaml` and any historical
41
- `pnpm.overrides` declarations in `package.json` are untouched, **and** that `devDependencies`
42
- versions still respect the effective overrides. If any entry was removed, modified, or bumped past
43
- an override, restore it immediately, investigate why it changed, and report that to the user — never
44
- keep the change without explicit approval.
26
+ (after `josh adopt`, which has already committed and returned to the default branch:
27
+ `git diff <default>...<branch> -- pnpm-workspace.yaml package.json`). Confirm both overrides blocks
28
+ are untouched **and** `devDependencies` still respect the effective overrides. A removed, modified or
29
+ over-bumped entry: restore it immediately, investigate, and report — never keep it without approval.
45
30
 
46
- `josh latest` prints its own verdict as its last overrides line (`✔ overrides unchanged (<n> from
47
- <file>)`, or a `⚠ overrides changed` warning), and `pnpm josh overrides` compares effective workspace
48
- entries against a saved snapshot and reports ignored package entries — **quote what one printed.**
31
+ `josh latest` prints its verdict as its last overrides line (`✔ overrides unchanged (<n> from
32
+ <file>)` or `⚠ overrides changed`); `pnpm josh overrides` compares against a saved snapshot —
33
+ **quote what one printed.**
49
34
 
50
35
  ## 3. `devEngines` — the one expected change
51
36
 
52
- Verify `devEngines` was not changed **outside the legitimate `josh latest` pnpm bump**. If it changed
53
- in any other way, restore it immediately and ask the user before making any change.
54
-
55
- **Exception — the `josh latest` lockstep pnpm bump is expected, NOT a violation.** `josh latest`
56
- deliberately bumps `devEngines.packageManager.version` in lockstep with the top-level
57
- `packageManager` pin (see `scripts/version/latest-corepack.ts` → `sync_development_engines`); the two
58
- MUST stay exactly equal — **byte-identical, `+sha512…` Corepack integrity suffix included**. pnpm
59
- compares them as raw strings, so a bare `11.18.0` paired with `pnpm@11.18.0+sha512…` is a mismatch
60
- and the dual-declaration warning fires; only a character-for-character match suppresses it.
61
-
62
- So after `josh latest`, **KEEP** a `devEngines.packageManager.version` change **if and only if** it
63
- now equals everything after `pnpm@` in the new `packageManager` pin (same version string **and** same
64
- integrity suffix; only the `version` field moved). Reverting it would both undo a valid toolchain
65
- update **and** re-introduce a `packageManager`/`devEngines` mismatch — the opposite of the rule's
66
- intent.
37
+ **Exception — the `josh latest` lockstep pnpm bump is expected, NOT a violation.** **KEEP** a
38
+ `devEngines.packageManager.version` change **if and only if** it equals everything after `pnpm@` in
39
+ the new `packageManager` pin — **byte-identical, `+sha512…` Corepack integrity suffix included**
40
+ (`scripts/version/latest-corepack.ts` → `sync_development_engines`). Reverting it would re-introduce
41
+ the mismatch.
67
42
 
68
- **Restore + ask only** when `devEngines` changed in some OTHER way: its version no longer matches
69
- `packageManager` (a dropped, stale, or truncated integrity suffix counts as a mismatch), its
70
- structure changed (`name` / `onFail` / fields added or removed), or it was touched by something other
71
- than `josh latest`. The one structural change that is expected is `josh sync` moving
72
- `devEngines.packageManager.onFail` from `"error"` to `"download"` — a user-approved migration that
73
- leaves `name` and `version` untouched; keep it.
43
+ **Restore + ask** when `devEngines` changed any other way: the version no longer matches
44
+ `packageManager` (a dropped or truncated suffix counts), its structure changed (`name` / `onFail` /
45
+ fields), or something other than `josh latest` touched it. One structural change is expected and
46
+ kept: `josh sync` moving `devEngines.packageManager.onFail` from `"error"` to `"download"`.
74
47
 
75
48
  ## 4. When the bump breaks something — fix forward
76
49
 
77
- The procedure — latest-first, fix forward at the correct layer, pin back only as a last resort with a
78
- tracking issue — has its single source in
79
- `prompts/collaboration-workflow/principles.md` → "latest-first". Fix-forward never authorizes a silent edit to a protected pin: the
80
- approval gates in sections 1–3 still apply.
50
+ Single source: `prompts/collaboration-workflow/principles.md` → "latest-first". Fix-forward never
51
+ authorizes a silent edit to a protected pin; sections 1–3 still apply.
@@ -5,9 +5,8 @@ description: Procedures for `josh epic:audit`, `epic:next` and `epic:bundle`. Re
5
5
 
6
6
  # The `josh epic:*` commands
7
7
 
8
- These three commands are what turn an epic from a list of issue numbers into something a run can
9
- execute unattended. **This skill is their single source**; where each rule came from is
10
- `docs/maintainers/epic-commands-rationale.md` → "Where each rule came from".
8
+ This skill is the single source for the three commands. Reasons: `docs/maintainers/epic-commands-rationale.md`
9
+ → "Where each rule came from".
11
10
 
12
11
  The workflow keywords themselves — `kickoff`, `fullrun`, `halfrun`, `prrun`, `backlogrun` — live in the
13
12
  `workflow-commands` skill.
@@ -19,27 +18,23 @@ epic:audit → find contradictions across the children, and fix what it finds (T
19
18
  backlogrun → which calls epic:next each round
20
19
  ```
21
20
 
22
- Front-loading every `needs-decision` in one pass is `backlogrun`'s job, not a command of this skill:
23
- `backlogrun-steps.md` → "Resolve what the plan can resolve, before starting". A `backlogrun #E --only`'s
24
- pre-check is `epic:audit`.
21
+ Front-loading every `needs-decision` is `backlogrun`'s job: `backlogrun-steps.md` → "Resolve what
22
+ the plan can resolve, before starting". A `backlogrun #E --only`'s pre-check is `epic:audit`.
25
23
 
26
- **Two references are read only when their case arises**: `execution-waves.md` when an epic's children
27
- need an order between groups (a wave boundary, or one child that must run alone), and
28
- `epic-bundle.md` when a filing's `epic:bundle` answer places the issue.
24
+ Read `execution-waves.md` only when children need an order between groups (a wave boundary, or a child
25
+ that must run alone), and `epic-bundle.md` only when a filing's `epic:bundle` answer places the issue.
29
26
 
30
27
  ## Recording a decision
31
28
 
32
- **A decision is recorded in two places, and one without the other loses half of it.** The epic's
33
- `## Decisions` log carries the decision; a comment on each child it applies to carries the reasoning
34
- for that child's reader. **Recording a decision removes that child's `needs-decision` label** (Tier A);
35
- the label-clearing rule itself is `backlogrun-park.md` → "park and continue".
29
+ Record it in both places: the epic's `## Decisions` log, and a comment on each child it applies to.
30
+ Recording a decision removes that child's `needs-decision` label (Tier A; `backlogrun-park.md` →
31
+ "park and continue").
36
32
 
37
- - **A decision taken *as* a child joins the epic** goes through `pnpm josh epic --add … --decision-file`
38
- — `epic-bundle.md` is that flag's single source, and it writes both halves at once.
39
- - **A decision about a child the epic already tracks** cannot use that flag: an insertion with nothing
40
- to add is refused outright. Until an entry point for already-tracked children exists, write the
41
- child comments with `pnpm josh issue:comment <N> --body-file <path>` on each child the answer
42
- applies to, and **say in the report that the epic's `## Decisions` entry is still pending**.
33
+ - **Decided as a child joins the epic**: `pnpm josh epic --add … --decision-file` writes both
34
+ (`epic-bundle.md`).
35
+ - **About a child the epic already tracks**: that flag refuses an insertion with nothing to add.
36
+ Comment on each child with `pnpm josh issue:comment <N> --body-file <path>`, and **say in the report
37
+ that the epic's `## Decisions` entry is still pending**.
43
38
 
44
39
  ```md
45
40
  ## Decisions
@@ -55,8 +50,8 @@ the label-clearing rule itself is `backlogrun-park.md` → "park and continue".
55
50
 
56
51
  ## Creating an epic
57
52
 
58
- A split into two or more issues always gets an epic (`prompts/collaboration-workflow/issue-template.md`).
59
- Create it with the command, never by hand-writing the body:
53
+ A split into two or more issues always gets an epic (`prompts/collaboration-workflow/issue-template.md`),
54
+ created with the command, never by hand:
60
55
 
61
56
  ```bash
62
57
  pnpm josh epic "<epic-title>" <N1> <N2> ... # order-free batch
@@ -64,105 +59,71 @@ pnpm josh epic "<epic-title>" <N1> <N2> ... --ordered # argument order is the d
64
59
  # --rationale-file <path|-> fills Split rationale; --origin <owner/repo#N> adds the backlink
65
60
  ```
66
61
 
67
- The body format is the command's output, and `pnpm josh epic:check <E>` answers whether a body still
68
- meets it (the parser auto-close reads; exit 1 on a miss) — run it after any hand edit. `pnpm josh
69
- followup` closes the epic once every task-list child is closed; one tracking a cross-repository child
70
- is closed by hand. The by-hand fallback where `josh` is unavailable: `issue-template.md`; the format's
71
- reasons and the history: `docs/maintainers/epic-commands-rationale.md` → "Creating an epic by hand".
62
+ After any hand edit run `pnpm josh epic:check <E>` (exit 1 when the body no longer parses).
63
+ `pnpm josh followup` closes the epic once every task-list child is closed; one tracking a
64
+ cross-repository child is closed by hand. Without `josh`: `issue-template.md`; the format's history:
65
+ `docs/maintainers/epic-commands-rationale.md` → "Creating an epic by hand".
72
66
 
73
67
  ## `josh epic:audit <E>` — contradictions across the children
74
68
 
75
- `epic:check` verifies one epic's *format*; this verifies that the children agree.
76
-
77
- Run it **without being asked**: when a `backlogrun` starts a named epic's children, and right
78
- after a child is added or a dependency changed.
79
-
80
- The cycle and declaration-mismatch checks are `epic:next`'s, reused rather than re-derived. What this
81
- adds is reading *inside* the children:
69
+ `epic:check` verifies the format; this verifies the children agree. Run it **without being asked**
70
+ when a `backlogrun` starts a named epic's children, and right after a child is added or a dependency
71
+ changed. Cycle and declaration checks are reused from `epic:next`; it adds:
82
72
 
83
73
  | Check | Level |
84
74
  | --- | --- |
85
75
  | A child's body names another child, nothing orders the two | warning |
86
- | A child's **acceptance criteria** name another child, nothing orders the two | **error**; a warning once both children are closed, and a warning when the pair is in two repositories. A warning there does **not** mean the pair is safe: the child is still offered as runnable, and recording the relation is what clears it |
76
+ | A child's **acceptance criteria** name another child, nothing orders the two | **error**; a warning once both are closed or when the pair spans two repositories — the child is still offered as runnable until the relation is recorded |
87
77
  | A body cites a missing or already-closed issue | warning |
88
- | A task-list row points at another **epic** | warning — `epic:next` withholds the row, so the batch is already safe; what is left is a thing a person has to look at |
78
+ | A task-list row points at another **epic** | warning — `epic:next` already withholds the row; a person should look |
89
79
  | An issue names this epic as parent that the task list does not track | warning |
90
80
  | The search for those issues could not read the open backlog | **error** |
91
81
  | That search stopped before the end of the backlog | warning |
92
82
 
93
- **Only errors fail it** (exit 1); a warning leaves the exit code alone and is for the reader to judge
94
- — the first check fires on a legitimate forward reference as readily as on a missing dependency. A
95
- forward reference the other child *already depends on* is not reported at all.
83
+ **Only errors fail it** (exit 1). A forward reference the other child already depends on is not
84
+ reported. A pair is demoted to a warning only when the closed state is **confirmed** (any state but
85
+ `CLOSED` normalizes to `OPEN`).
96
86
 
97
- **A pair with either child closed is a warning, not an error**, and **the demotion needs the closed
98
- state confirmed**: `epic_issue.normalize_state` maps every state but `CLOSED` (`MERGED` included) to
99
- `OPEN`, so a state that could not be confirmed stays an error.
87
+ **Fixing what it finds is Tier A** — fix it and record why on the issue. Park with `needs-decision`
88
+ only for a design choice nobody has made.
100
89
 
101
- **Fixing what it finds is Tier A** — re-pointing a dependency or correcting prose is reversible and
102
- will otherwise stall the work, so fix it without asking and record why on the issue. Park with
103
- `needs-decision` only when the contradiction is a design choice nobody has made.
90
+ The two `orphan search` findings are about the search, not the children:
104
91
 
105
- **The two `orphan search` findings are the exception, because neither is a contradiction.** They are
106
- about the search itself rather than about anything the children say:
92
+ - `✖ orphan search: Could not list the open issues…` — **re-run the audit**; nothing to fix or decide.
93
+ If it keeps failing, check `gh auth status` and the rate limit.
94
+ - `⚠ orphan search: The open-issue scan hit its …` — only the newest 500 issues (or 50 bodies) were
95
+ covered; the audit passes. Look further by hand if an orphan is expected.
107
96
 
108
- - `✖ orphan search: Could not list the open issues…` — the search never ran, so "no orphans" would
109
- be a claim about a listing that never arrived. It fails the audit, which stops the epic's run at its
110
- step 0. **Re-run the audit**; that is the whole response. There is nothing to fix and nothing to
111
- decide, so neither Tier A nor a `needs-decision` park applies. If it keeps failing, check
112
- `gh auth status` and whether the rate limit has reset.
113
- - `⚠ orphan search: The open-issue scan hit its …` — the search covered only the newest part of the
114
- backlog (500 issues, or 50 bodies mentioning the epic). It ran, so the audit passes; read the line,
115
- and look further down the backlog by hand if an orphan is expected there.
116
-
117
- **One thing it cannot check** belongs to planning: when a child introduces a new label, command,
118
- state or artifact, list the existing code referencing that concept and confirm some child owns
119
- updating it. Label names are single-sourced in `scripts/issue/issue-labels.ts`.
97
+ **It cannot check** that when a child introduces a new label, command, state or artifact, some child
98
+ owns updating the existing code referencing that concept — do that at planning. Label names live in
99
+ `scripts/issue/issue-labels.ts`.
120
100
 
121
101
  History: `docs/maintainers/epic-commands-rationale.md` → "`epic:audit` — why the children are read across".
122
102
 
123
103
  ## `josh epic:next <E…>` — what is runnable
124
104
 
125
- Returns **every** runnable child, bundled per repository with the local checkout to run it in.
126
- `--repo <owner/repo>` narrows to one and prints a single token: the issue number, or the verdict.
127
- `--lanes` beside it prints one issue number per line instead, **up to the number of free lanes in
128
- that repository** — so a caller that reads one token keeps reading one token.
129
-
130
- **It takes more than one epic, and they answer as one.** Every leading argument is an epic
131
- reference — the split is on the first flag, so `epic:next 858 909 --repo X --lanes` reads two — and
132
- their runnable children merge into one candidate pool per repository, so six free lanes fill from
133
- every named epic rather than from whichever was typed first. **The priority order is the order the
134
- epics were named.** **Inside one epic the order is that epic's own task list**: its declared chain
135
- still decides which children are *runnable*, and among those, the order the task list names them is
136
- the order they are offered — so an epic says "do this one first" by moving the row, not by declaring
137
- a chain, which would stop every child behind a stuck one.
138
- **`pnpm josh epic --add <E> <N> --order-before <M>` is what moves that row**: the row goes where the
139
- flag names, and the declaration and the `blocked-by` relations are left exactly as they stood — so
140
- "moving the row" is a command rather than the hand edit `epic --add` exists to prevent.
141
- `--order-after <M>` is its other direction, and both work on a child the epic already tracks, which
142
- is a pure reorder. **A child a chain still holds is not freed by moving its row**: `epic:next`
143
- filters by `blocked-by` before it applies task-list order, so the command prints a ⚠️ line naming
144
- that child rather than reporting a reordering nothing can observe. **A child two epics both track
145
- enters once**, keyed by `owner/repo#number` and kept by the epic named earlier; withheld there, it
146
- stays withheld, because a `blocked-by` relation belongs to the issue rather than to the epic listing
147
- it. **One unusable graph refuses the whole answer**, and **one reference that does not parse fails
148
- the read** rather than being dropped. `docs/josh-commands-backlog.md` → "`josh epic:next`" carries the
149
- worked form.
150
-
151
- **An `in-progress` issue occupies a lane rather than the whole repository.** The occupancy is counted
152
- from that repository's own `in-progress` listing — never from anything the session remembers — and
153
- what is left of `JOSH_LANE_LIMIT` (**default 4**) is what gets offered. At zero the answer is `wait`
154
- and the holders are named on standard error. A parked child releases its lane; one stopped by
155
- `needs-human-review` goes on holding one, because its uncommitted work is still in that checkout.
156
-
157
- **A child that is itself an epic is never offered** — an epic is not a unit of work. It waits on a
158
- *person*, because no amount of waiting turns an epic into work. **The test is the child's `epic`
159
- label, never how its task-list row is written**: a row naming `owner/repo#N` is a different property
160
- and a legitimate one — it disables the epic auto-close by design, which is why a cross-repository
161
- backlink is prose rather than a row — so a cross-repository child that is not an epic stays
162
- runnable, and one that is an epic is withheld exactly like a local one. **A parent epic over another
163
- epic never auto-closes**: the auto-close evaluates only the epics whose own task list holds the issue
164
- a merge just closed, and a grandchild merging never reaches the grandparent. `epic:audit` reports the
165
- same row, with that consequence in the finding.
105
+ Returns **every** runnable child, bundled per repository with its local checkout. `--repo <owner/repo>`
106
+ narrows to one and prints a single token (an issue number or the verdict); `--lanes` beside it prints
107
+ one number per line, **up to the number of free lanes in that repository**.
108
+
109
+ - **Several epics answer as one**: every argument before the first flag is an epic; their runnable
110
+ children merge into one pool per repository. **Priority is the order the epics were named**, then
111
+ each epic's own task-list order (its declared chain still decides what is runnable).
112
+ - **Reorder with `pnpm josh epic --add <E> <N> --order-before <M>`** (or `--order-after <M>`), never a
113
+ hand edit; declarations and `blocked-by` relations stay as they were. Moving a row does not free a
114
+ child a chain still holds — the command prints a ⚠️ line instead.
115
+ - **A child two epics track enters once**, kept by the epic named earlier.
116
+ - **One unusable graph refuses the whole answer**; **one reference that does not parse fails the
117
+ read**. Worked form: `docs/josh-commands-backlog.md` → "`josh epic:next`".
118
+
119
+ **An `in-progress` issue occupies a lane, not the repository.** Free lanes are `JOSH_LANE_LIMIT`
120
+ (**default 4**) minus that repository's `in-progress` listing. At zero the answer is `wait`, with the
121
+ holders on standard error. A parked child releases its lane; a `needs-human-review` stop keeps it.
122
+
123
+ **A child that is itself an epic is never offered** and waits on a *person*. The test is the child's
124
+ `epic` label, never how its row is written: a cross-repository `owner/repo#N` row that is not an epic
125
+ stays runnable. **A parent epic over another epic never auto-closes** (a grandchild's merge never
126
+ reaches it); `epic:audit` reports the row.
166
127
 
167
128
  Children that are not runnable are sorted by **whether waiting helps**, never by label:
168
129
 
@@ -172,62 +133,42 @@ Children that are not runnable are sorted by **whether waiting helps**, never by
172
133
  | Waiting on time | Wait and ask again |
173
134
  | Waiting on a person | Stop and report |
174
135
 
175
- **A blocker outside every named epic is weighed, not ignored.** Closed, it resolves (a
176
- cross-repository one still waits for its release); open and tracked by any epic named in the same
177
- call, the child waits on time unless that blocker itself waits on a person, which the child
178
- inherits; open and tracked by none, the child waits on a person and the blocker is named on standard
179
- error; a state the relation did not carry reads as waiting. **So an order between two epics is
180
- recorded with a native `blocked-by` relation and honoured by naming both epics** — `epic:next <E1>
181
- <E2>`, or `backlog:next`, whose set is the whole opted-in backlog. The table is
182
- `docs/josh-commands-backlog.md` → "`josh epic:next`".
136
+ **A blocker outside every named epic is weighed**: closed, it resolves (cross-repository: after its
137
+ release); open and tracked by an epic named in the same call, the child waits on time (or on a person,
138
+ if the blocker does); open and untracked, the child waits on a person and the blocker is named on
139
+ standard error; an unknown state reads as waiting. **Order two epics with a native `blocked-by`
140
+ relation and name both** — `epic:next <E1> <E2>`, or `backlog:next`.
183
141
 
184
- Two things stop the command rather than being worked around: a **circular dependency**, and a
185
- **disagreement between the epic body and the `blocked-by` relations** (an epic written before `josh`
186
- recorded them, a recording that failed, or a relation hand-added since). Only a line that is
187
- *nothing but* a chain counts as a declaration — a prose line recommending an execution order is a suggestion, not a dependency.
142
+ Two things stop the command: a **circular dependency**, and a **disagreement between the epic body
143
+ and the `blocked-by` relations**. Only a line that is *nothing but* a chain is a declaration — a prose
144
+ line recommending an execution order is a suggestion, not a dependency.
188
145
 
189
- Why it orders and counts as it does: `docs/maintainers/epic-commands-rationale.md` → "`epic:next` — why it answers as it does".
146
+ Why: `docs/maintainers/epic-commands-rationale.md` → "`epic:next` — why it answers as it does".
190
147
 
191
148
  ## Epics that span repositories
192
149
 
193
- Write cross-repository children in the task list as `owner/repo#N` or a full issue URL. Their state
194
- is read against that repository through `gh api`, so **no local clone is needed to learn it** — only
195
- to implement.
150
+ Write cross-repository children as `owner/repo#N` or a full issue URL. Their state is read through
151
+ `gh api`, so **no local clone is needed to learn it** — only to implement.
196
152
 
197
- - **The owner restriction is inherited unchanged**: a repository with a different owner is never a
198
- target, by any route.
199
- - **The epic auto-closes only when every cross-repository child's state could actually be read.** One
200
- unreadable child leaves it open, exactly as before.
201
- - **An epic in another repository must be referenced as `owner/repo#N`** — `backlogrun
153
+ - **The owner restriction is inherited unchanged**: a different owner is never a target.
154
+ - **The epic auto-closes only when every cross-repository child's state could be read.**
155
+ - **An epic in another repository is referenced as `owner/repo#N`** — `backlogrun
202
156
  joshuafolkken/kit#<E> --only`, `epic:next joshuafolkken/kit#<E> --repo joshuafolkken/app-kit`. A
203
- bare `#N` resolves to *this* repository's issue of that number. A cross-repository epic exists in
204
- one repository only.
205
- - **`epic:next` prints each candidate's repository and local checkout** from the repository discovery
206
- map; a repository with no checkout prints `(no local checkout)` and is **never cloned on its own**.
207
-
208
- **A dependency that crosses a repository is not satisfied by the blocking issue closing** — merging
209
- does not publish. It resolves only when the blocker is closed **and** the version its default branch
210
- declares has appeared in the registry.
211
-
212
- **Unless that repository publishes nothing.** A repository with no `package.json` on its default
213
- branch, or one declaring `private`, ships no release for the check to wait on, so a closed blocker
214
- there resolves. The answer is read from the blocker repository's own manifest and never from the
215
- registry (a registry 404 also means "this token may not see it"). A manifest read that fails (rather
216
- than answering 404) is told apart by HTTP status and still waits.
217
-
218
- **The evaluation is an AND in that order.** While the blocker is open the registry is never
219
- consulted, so a run never stalls on a publish from the moment it starts. The target is that exact
220
- version, never "something newer". **A child no longer bumps the version**: the version moves only
221
- when `pnpm josh release` runs, so the target is the release that includes the blocker, not the
222
- default branch's version just after its merge (the previous release). The publish check shares
223
- `josh propagate`'s implementation; it is not defined twice. History:
224
- `docs/maintainers/epic-commands-rationale.md` → "Cross-repository epics".
157
+ bare `#N` is this repository's issue.
158
+ - **`epic:next` prints each candidate's local checkout**; none prints `(no local checkout)` and is
159
+ **never cloned on its own**.
160
+
161
+ **A cross-repository dependency resolves only when the blocker is closed and the version its default
162
+ branch declares is in the registry** (checked in that order; that exact version). A child no longer
163
+ bumps the version, so the target is the release that includes the blocker. **Unless that repository
164
+ publishes nothing**: no `package.json` on its default branch, or `private`, resolves on close — read
165
+ from its manifest, never the registry; a failed manifest read (not a 404) still waits. The publish
166
+ check is `josh propagate`'s. History: `docs/maintainers/epic-commands-rationale.md` → "Cross-repository epics".
225
167
 
226
168
  ## `josh epic:bundle <N>` — does this new issue belong with one already filed?
227
169
 
228
- It runs right after an issue is filed: by `kickoff` / `fullrun` / `halfrun`, or by any Tier A filing
229
- mid-implementation, including inside a `backlogrun`. **`pnpm josh issue:file` runs it as its last step**;
230
- run it by hand only when that step printed `⚠`. **It recommends; it writes nothing.**
170
+ Runs right after any filing; **`pnpm josh issue:file` runs it as its last step** — run it by hand only
171
+ when that step printed `⚠`. **It recommends; it writes nothing.**
231
172
 
232
173
  | Candidates | Do | Tier |
233
174
  | --- | --- | --- |
@@ -239,9 +180,7 @@ run it by hand only when that step printed `⚠`. **It recommends; it writes not
239
180
  | No strong signal | Nothing | — |
240
181
  | **The epic listing was cut short** | **Nothing** — every placing row above is withheld | — |
241
182
 
242
- **A placing answer is acted on with `epic-bundle.md`** — what counts as a signal, the cut listing,
243
- choosing between epics, and the `--decision-file` record that pays for the choice. `Nothing to
244
- bundle.` needs no further read.
183
+ **A placing answer is acted on with `epic-bundle.md`**. `Nothing to bundle.` needs no further read.
245
184
 
246
185
  History: `docs/maintainers/epic-commands-rationale.md` → "`epic:bundle` — why issues filed apart are bundled afterwards";
247
186
  the reasons behind `epic-bundle.md`: `docs/maintainers/epic-commands-rationale.md` → "`epic:bundle` — why each answer reads as it does";
@@ -1,44 +1,30 @@
1
1
  # `epic:bundle` — the detail behind the answer
2
2
 
3
- **Read this when a filing's `epic:bundle` answer places the issue** — `add_to_epic`, `create_epic`,
4
- or a choice between epics — or when the answer is one the summary in the `epic-commands` skill
5
- (`SKILL.md` → "`josh epic:bundle <N>` — does this new issue belong with one already filed?") does not
6
- settle. `Nothing to bundle.` needs neither.
3
+ Read this when a filing's `epic:bundle` answer places the issue (`add_to_epic`, `create_epic`, or a
4
+ choice between epics), or when `SKILL.md` → "`josh epic:bundle <N>` — does this new issue belong with
5
+ one already filed?" does not settle it. `Nothing to bundle.` needs neither. Reasons:
6
+ `docs/maintainers/epic-commands-rationale.md` → "`epic:bundle` — why each answer reads as it does".
7
7
 
8
8
  **Only two things count as a signal**: the issues referring to each other in prose, or a `blocked-by`
9
- already recorded between them. **A similar title never counts on its own** — "related" expands
10
- without limit, and the threshold is what keeps an unrelated issue out.
11
-
12
- **The release issue is neither a candidate nor a subject**, like an epic. `issue:file --release` records
13
- every change awaiting publication as its blocker, so that relation is mechanical, not a sign of shared
14
- work. Counted as a signal, it would put the release and an unrelated change in one epic. A
15
- `blocked-by` or a prose reference between any other two issues still counts.
16
-
17
- **The search is not limited to the open backlog.** Every issue number the subject's body names is read
18
- on its own, whatever its state. A **closed** reference counts only when an open epic already tracks
19
- it, since the answer worth recovering is `add_to_epic`; an epic created over a closed issue has
20
- nothing left to run. An **open** reference counts either way. A read that fails — and a reference the
21
- per-issue cap never reached — is reported as a gap, never folded into "no strong signal". A number
22
- that answers with a **pull request** is not a candidate at all: `repos/{owner}/{repo}/issues/<N>`
23
- serves one too, and a merged PR does not report `CLOSED`.
24
-
25
- **A number that does not exist is not a gap.** A typo, or another repository's number quoted in
26
- prose, is dropped in silence — neither a candidate nor something the command reports it could not
27
- read. **The two are told apart by HTTP status, never by `gh`'s wording**: 404 is nothing at that
28
- number, 403 and 429 are a rate limit. The referenced-number reads are capped at twenty, and **only
29
- the subject's own body is followed**.
30
-
31
- **`none` is a legitimate answer** — a self-review outside any workflow has no current issue to point
32
- at, so the issue stays in the backlog: what the procedure requires is running the command and
33
- following its answer, not landing every issue in an epic. The review-cap follow-up's filing and the
34
- answer table it acts on are `prompts/review.md` → "Three-way disposition after the cap".
35
-
36
- **Every row that *places* the issue asserts a negative, so a cut epic listing withholds all of them.**
37
- "No epic already tracks this issue" — which `create_epic` asserts about the candidates too — is only
38
- as good as the listing it was read from, and an epic past the cut tracks its children invisibly.
39
- **`add_to_epic` rests on it as much as `create_epic` does**: adding the issue to the epic a
40
- *candidate* sits in, while an unseen epic already tracks the issue itself, is the same duplicate by
41
- another route. The verdict says so itself:
9
+ already recorded between them. **A similar title never counts on its own.**
10
+
11
+ **The release issue is neither a candidate nor a subject**, like an epic — its `blocked-by` relations
12
+ are mechanical. Any other `blocked-by` or prose reference still counts.
13
+
14
+ **The search is not limited to the open backlog.** Every number the subject's own body names is read
15
+ (capped at twenty), whatever its state:
16
+
17
+ - A **closed** reference counts only when an open epic already tracks it; an **open** one counts either way.
18
+ - A **pull request** number is not a candidate.
19
+ - A failed read, or a reference past the cap, is reported as a gap — never folded into "no strong signal".
20
+ - **A number that does not exist is not a gap** — dropped in silence. Told apart **by HTTP status,
21
+ never by `gh`'s wording**: 404 is nothing there; 403 and 429 are a rate limit.
22
+
23
+ **`none` is a legitimate answer** — the issue stays in the backlog. The review-cap follow-up's filing:
24
+ `prompts/review.md` → "Three-way disposition after the cap".
25
+
26
+ **A cut epic listing withholds every placing row**, `add_to_epic` as much as `create_epic` — an unseen
27
+ epic may already track the issue. The verdict says so, and the exit code stays `0`:
42
28
 
43
29
  ```text
44
30
  Could not confirm which epic already tracks these — do not place this issue in one.
@@ -46,50 +32,32 @@ Could not confirm which epic already tracks these — do not place this issue in
46
32
  Related: #<N>
47
33
  ```
48
34
 
49
- **The children and the order are deliberately absent** — they are the recipe for the placement that
50
- line says not to make. The exit code stays `0`, as it does for every other "do nothing" answer. **What
51
- survives the cut is a membership that *was* found**: the table's first row names the epic it read
52
- tracking the issue, and epics past the cut cannot unseat it.
35
+ It prints no children or order. What survives the cut is a membership that *was* found (the table's
36
+ first row).
53
37
 
54
- **Placing an issue is not merging epics.** Bundling is reversible — one `epic --add` moves an issue
55
- to a different epic — so choosing between two candidate epics is Tier A: take the one you recommend,
56
- and write the decision (what was taken, what was rejected, why, and the date) on **both** the issue
57
- and the epic's `## Decisions`. **Merging two epics is a different action, and nothing here proposes
58
- it.**
38
+ **Placing an issue is not merging epics.** Choosing between two candidate epics is Tier A: take the one
39
+ you recommend and write the decision (taken, rejected, why, date) on **both** the issue and the epic's
40
+ `## Decisions`. **Merging two epics is a different action, and nothing here proposes it.**
59
41
 
60
- **An epic and its own parent do not produce the spread verdict at all.** The parent already contains
61
- the child, so the pair is narrowed to the inner epic and the issue is added there. **The narrowing
62
- drops parents, never peers**: one unrelated epic beside a nested chain still asks, and so does a
63
- cyclic declaration, where there is no inner epic to pick. Stop only where the epics left are
64
- genuinely too close to separate, which is the toss-up Tier B is for and is rare.
65
-
66
- **The decision record is what pays for the autonomy** — without it the run has taken a decision
67
- nobody can find afterwards.
42
+ **An epic and its own parent do not produce the spread verdict** — the issue goes to the inner epic.
43
+ **The narrowing drops parents, never peers**: an unrelated epic beside a nested chain, or a cyclic
44
+ declaration, still asks. Stop (Tier B) only where the epics left are genuinely too close to separate.
68
45
 
69
46
  **Write both halves in the one call that places the issue: `pnpm josh epic --add <E> <N...>
70
- --decision-file <path|->`.** It appends the record to the epic's `## Decisions` inside the body edit
71
- the insertion already makes, and posts the same text as a comment on each child added. **Never
72
- hand-edit the epic body to add the entry** — that is the operation `--add` exists to remove. Two
73
- constraints on the record's own text, both refused before anything is written:
74
-
75
- - **No line that is *nothing but* a `#A -> #B` chain.** Such a line is read as part of the epic's
76
- declaration wherever it sits in the body, so the record would declare an order nobody decided. Quote
77
- the order inside backticks, or put it in a fenced block; either is accepted.
78
- - **The record must say something, and the path must be readable.** An empty file is refused, and so is
79
- `--decision-file` with no usable path — otherwise the insertion lands, no record is written anywhere,
80
- and the command still exits 0.
81
-
82
- Answers about children the epic already tracks cannot use this flag; `SKILL.md` → "Recording a
83
- decision" says what to do instead.
84
-
85
- **Its sibling runs before the filing, not after it: `pnpm josh issue:scout "<title>"`.** That command
86
- answers the same epic question for an issue that does not exist yet — this decision, called rather
87
- than restated — and beside it the one thing this one deliberately refuses: whether the work has
88
- already been filed, from a title comparison. **Both run**, and neither replaces the other:
89
- `pnpm josh issue:file` runs the scout before it creates the issue, and `epic:bundle` afterwards, from
90
- the real number and the relations recorded against it. Full behavior:
47
+ --decision-file <path|->`.** It appends to the epic's `## Decisions` and comments the same text on
48
+ each child added. **Never hand-edit the epic body to add the entry.** Refused before anything is
49
+ written:
50
+
51
+ - **A line that is *nothing but* a `#A -> #B` chain** — it would be read as a declaration. Quote the
52
+ order in backticks or a fenced block.
53
+ - **An empty record, or `--decision-file` with no usable path.**
54
+
55
+ For children the epic already tracks: `SKILL.md` → "Recording a decision".
56
+
57
+ **Its sibling runs before the filing: `pnpm josh issue:scout "<title>"`** — the same epic question for
58
+ an issue not yet filed, plus whether the work is already filed. **Both run**: `pnpm josh issue:file`
59
+ runs the scout before creating the issue and `epic:bundle` after. Full behavior:
91
60
  `docs/josh-commands-backlog.md` → "`josh issue:file`".
92
61
 
93
62
  **When the relation carries an order, record it** in `blocked-by` and in the epic's `Dependencies` —
94
- on an addition as much as on a new epic. Without it the batch survives and the reason for it does
95
- not. An order **nobody declared is not invented**.
63
+ on an addition as much as on a new epic. An order **nobody declared is not invented**.