@joshuafolkken/kit 1.972.0 → 1.983.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.claude/skills/epic-commands/SKILL.md +6 -5
- package/.claude/skills/workflow-commands/SKILL.md +9 -9
- package/.claude/skills/workflow-commands/background-commands.md +1 -1
- package/.claude/skills/workflow-commands/fullrun-steps.md +8 -8
- package/.claude/skills/workflow-commands/fullrun.md +1 -0
- package/.claude/skills/workflow-commands/halfrun.md +16 -12
- package/.claude/skills/workflow-commands/issue-comments.md +1 -1
- package/.claude/skills/workflow-commands/issue-scout.md +21 -22
- package/.claude/skills/workflow-commands/kickoff.md +8 -9
- package/.claude/skills/workflow-commands/observation-filing.md +9 -10
- package/.claude/skills/workflow-commands/prerequisite.md +7 -6
- package/.claude/skills/workflow-commands/retrospective.md +5 -5
- package/.claude/skills/workflow-commands/rule-residency.md +2 -2
- package/.claude/skills/workflow-commands/split-assessment.md +4 -3
- package/.claude/skills/workflow-commands/target-repository.md +7 -6
- package/.claude/skills/workflow-commands/working-tree-hold.md +16 -1
- package/README.md +24 -20
- package/dist/hooks/batch-guard.js +4 -4
- package/dist/hooks/{chunk-ZPLXLJ7O.js → chunk-47SPMGOD.js} +16 -1
- package/dist/hooks/{chunk-BVMSPG3M.js → chunk-5JX6HXDX.js} +3 -3
- package/dist/hooks/{chunk-KQG4776P.js → chunk-APVOACVT.js} +1 -1
- package/dist/hooks/{chunk-2K52BRWV.js → chunk-ASUZGZFT.js} +2 -2
- package/dist/hooks/{chunk-CRGJTD6E.js → chunk-ERXMJJOV.js} +1 -1
- package/dist/hooks/{chunk-WWTMHHZU.js → chunk-IM3KM25Y.js} +61 -30
- package/dist/hooks/{chunk-BNWIOJFU.js → chunk-LBXQHXMC.js} +138 -358
- package/dist/hooks/{chunk-NXL3SBXY.js → chunk-P3UX7SFN.js} +2 -2
- package/dist/hooks/{chunk-F6OM6SIG.js → chunk-RFPHKU7C.js} +12 -12
- package/dist/hooks/{chunk-ZYYSBNR6.js → chunk-ZT65LR3U.js} +4 -4
- package/dist/hooks/codex-hook-adapter.js +10 -10
- package/dist/hooks/duplicate-read-guard.js +5 -5
- package/dist/hooks/format-edited.js +2 -2
- package/dist/hooks/investigation-guard.js +5 -5
- package/dist/hooks/pretool-guard.js +9 -9
- package/dist/hooks/stop-guard.js +4 -4
- package/dist/josh.js +16 -1
- package/package.json +2 -2
- package/prompts/collaboration-workflow/epic-bundle.md +8 -8
- package/prompts/collaboration-workflow/issue-template.md +5 -5
- package/prompts/collaboration-workflow/rule-delivery.md +43 -30
- package/prompts/collaboration-workflow/upstream-interrupt.md +1 -1
- package/prompts/collaboration-workflow/wip-cap.md +3 -3
- package/prompts/review.md +11 -9
- package/scripts/ci/pr-classification.ts +13 -12
- package/scripts/delegation/delegation-policy.ts +9 -9
- package/scripts/document/document-byte-budget.ts +1 -1
- package/scripts/git/issue-labels.ts +52 -5
- package/scripts/init/init-bootstrap.ts +92 -0
- package/scripts/init/init-install.ts +4 -1
- package/scripts/init/init-logic-workspace.ts +1 -1
- package/scripts/init/init.ts +23 -5
- package/scripts/init/start-plan.ts +1 -1
- package/scripts/init/start-steps.ts +5 -28
- package/scripts/issue/issue-file-cli.ts +188 -0
- package/scripts/issue/issue-file.ts +192 -0
- package/scripts/issue/issue-lint-cli.ts +1 -1
- package/scripts/issue/issue-scout-cli.ts +41 -97
- package/scripts/issue/issue-scout-closed.ts +102 -0
- package/scripts/josh/__snapshots__/josh-command-map.test.ts.snap +9 -1
- package/scripts/josh/josh-command-map.ts +1 -0
- package/scripts/josh/josh-commands-issue.ts +11 -0
- package/scripts/josh/josh-commands-project.ts +5 -1
- package/scripts/propagate/propagate-run.ts +1 -0
- package/scripts/propagate/propagate-select.ts +72 -0
- package/scripts/propagate/propagate-targets.ts +5 -3
- package/scripts/propagate/propagate.ts +86 -24
- package/scripts/repo/repository-labels.ts +91 -0
- package/scripts/rules/bash-triggers.ts +19 -6
- package/scripts/rules/delivered-rules.ts +28 -32
- package/scripts/rules/direct-filing.ts +38 -0
- package/scripts/rules/filing-cap.ts +11 -8
- package/scripts/rules/issue-fold.ts +4 -4
- package/scripts/rules/oracle-consulted.ts +4 -4
- package/scripts/rules/oracle-firing.ts +13 -16
- package/scripts/rules/rule-body-guard.ts +6 -9
- package/scripts/rules/stop-rules.ts +3 -2
- package/scripts/run/run-entry-cli.ts +34 -1
- package/scripts/run/run-halfrun-resume.ts +104 -0
- package/scripts/run/run-hold-cli.ts +27 -4
- package/scripts/run/run-hold.ts +11 -0
- package/scripts/security/security-audit.ts +12 -1
- package/scripts/sync/sync.ts +6 -0
- package/scripts/test/pilot-files.ts +0 -1
- package/scripts/version/fresh-metadata-cache.ts +36 -0
- package/scripts/version/latest-update.ts +2 -1
- package/scripts/rules/issue-bug-label.ts +0 -85
- package/scripts/rules/issue-filing-args.ts +0 -50
- package/scripts/rules/issue-scout.ts +0 -64
|
@@ -53,33 +53,33 @@
|
|
|
53
53
|
|
|
54
54
|
## 配送されている規則
|
|
55
55
|
|
|
56
|
-
| 規則 | 引き金
|
|
57
|
-
| ---------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
|
|
58
|
-
| **ターン内バッチング**(`turn-batching.md`) | `pnpm josh batch:guard` — 単発呼び出しのターンが 3 つ続いた次の `Bash` / `Edit` / `Read`
|
|
59
|
-
| **調査の委譲しきい値**(`SKILL.md` → §2b) | `pnpm josh investigation:guard` — 編集しないファイルの読み取りが 3 件目に達した `Read` / `Bash`
|
|
60
|
-
| **バックログ WIP 上限**(`wip-cap.md`) | `pnpm josh rule:guard` — Issue
|
|
61
|
-
|
|
|
62
|
-
| **1 ラン 10 件の起票上限**(`.claude/skills/workflow-commands/SKILL.md` → §2d、`observation-filing.md`) | `pnpm josh rule:guard` — Issue
|
|
63
|
-
| **Issue コメントの読み取り**(`SKILL.md` → §2g) | `pnpm josh rule:guard` — Issue 本文だけを読む `Bash`(`gh issue view <N>`、または `…/issues/<N>` で終わる GET)
|
|
64
|
-
| **本文をシェルに載せない**(`shell-body.md`) | `pnpm josh rule:guard` — 二重引用符の本文値(`-f body="…"` / `-f "body=…"` / `--body "…"` / `--notify-message "…"`)にバッククォートか `$` を含む `Bash`
|
|
65
|
-
| **@path をリテラル投稿する誤射**(`shell-body.md`、joshuafolkken/kit#2304) | `pnpm josh rule:guard` — 生フィールド(`-f` / `--raw-field`)で `body=@<path>` を渡した `Bash`。`@` で始まる値をそのまま送るため `@<path>` がコメントとして投稿される。**毎回発火する**
|
|
66
|
-
| **検証コマンドのパイプ**(`output-bounds.md`) | `pnpm josh rule:guard` — 合否を意味する josh のチェック(`gate` / `check` / `lint*` / `cspell*` / `test*` / `eval` / `overrides` / `ranges`)がパイプの手前に立った `Bash`
|
|
67
|
-
| **早すぎる進捗報告**(`.claude/skills/workflow-commands/backlogrun-progress.md` → 「Progress while the run is quiet」) | `pnpm josh rule:guard` — 待つことだけが目的の `Bash`(全区間が `sleep`、または `sleep` に `echo` / `:` / `date` が並ぶだけ)。既に生きているタイマーがある回、またはその報告が間隔前に着地する回に拒否する
|
|
68
|
-
| **run 末尾の空転**(`background-commands.md`) | `pnpm josh rule:guard` — commit・push・PR 作成をまとめる段(`pnpm josh git -y` / 別名 `josh g`、`--yes` も同じ)を**前景**で出した `Bash`。`run_in_background` が付いていれば引き金に当たらない
|
|
69
|
-
| **gate 手前の cut**(`.claude/skills/workflow-commands/pre-gate-cut.md`) | `pnpm josh rule:guard` — レーンの作業ツリーに居て、目印 `JOSH_LANE_CHILD` がその Issue を指し、cut 記録がまだ無いまま `pnpm josh gate`(別名 `josh ga` も同じ)を走らせる `Bash`
|
|
70
|
-
| **実装フェーズの cut**(`.claude/skills/workflow-commands/pre-gate-cut.md` → 「It is a guard, fired at the edit that crosses the threshold」、joshuafolkken/kit#2310) | `pnpm josh rule:guard` — 派遣されたレーンの子(目印 `JOSH_LANE_CHILD` がその Issue を指す)で cut 記録がまだ無く、直近文脈のコスト(`pnpm josh cost --cut` と同一の統計・同一のしきい値 `CONTEXT_CUT_THRESHOLD`、測定不能は `!== UNDER` で安全側に発火)が超過しているときに出した `Edit` / `Write`。編集の直前で拒否し `pnpm josh run:cut --impl <N> --handoff <path>` を返す。**しきい値を跨ぐたびに発火する**(`decide`、joshuafolkken/kit#2385)。直後の同じ編集の出し直しは通すので `busy` / `failed` でも空回りしない
|
|
71
|
-
| **レーンの子の保留**(`.claude/skills/workflow-commands/pre-gate-cut.md`) | `pnpm josh rule:guard` — 派遣されたレーンの子(目印 `JOSH_LANE_CHILD` がその Issue を指す)が、判断待ちの停止通知 `pnpm josh notify --task-type confirmation` を出した `Bash`
|
|
72
|
-
| **レーンの子の対話質問**(`.claude/skills/workflow-commands/pre-gate-cut.md` → 「The interactive ask is refused one call earlier」) | `pnpm josh rule:guard` — 派遣されたレーンの子(目印 `JOSH_LANE_CHILD` がその Issue を指す)が `AskUserQuestion` を呼んだ呼び出し。停止通知の 1 つ手前で拒否し park 手順を返す。ハーネスは対話質問をターン終了で拒否するので、この 1 つ前が正しい引き金である。**毎回発火する**
|
|
73
|
-
| **テストの宣言**(`CLAUDE.md` → Code Change Rules) | `pnpm josh rule:guard` — commit・push・PR 段(`pnpm josh git -y` / 別名 `josh g`)を出した瞬間に `pnpm josh test:declared` が作業ツリーを `required`(テストでも機械的免除パスでもない変更ファイルがある)と読む `Bash`。`run_tail` の commit 段照合を再利用し、新しい配送経路は作らない
|
|
74
|
-
| **force push / ブランチ削除**(`operating-rules.md`、joshuafolkken/kit#2120) | `pnpm josh rule:guard` — `git push` / `git branch` を argv として解析し、force(`--force` / `-f` / 結合クラスタ `-uf`)・削除(`--delete` / `-d` / `-D` / コロン形 `:branch`)・`git -C` 前置を綴りによらず判定した `Bash`。**毎回発火する**
|
|
75
|
-
| **認可外の作業ツリー変更**(`operating-rules.md`、joshuafolkken/kit#2120) | `pnpm josh rule:guard` — `git checkout -- <path>` / `git restore <path>`(worktree)/ 認可外の `git stash`(bare・メッセージ無し push・位置指定 pop 等)を出した `Bash`。**毎回発火する**
|
|
76
|
-
| **ファイル本文をシェルに載せない**(`file-edits.md`、joshuafolkken/kit#2120) | `pnpm josh rule:guard` — 既存ファイルを対象としたヒアドキュメント書き込み・書き込みを伴う `node -e` / インタプリタ heredoc・in-place `perl -0pi -e` を出した `Bash`。既存判定は `stat` 1 回。**毎回発火する**
|
|
77
|
-
| **停止時の通知**(`CLAUDE.md` →「Mid-workflow stop notification」、joshuafolkken/kit#2121) | `pnpm josh stop:guard` — `Stop` フックで、作業ツリーを押さえたまま(`run:hold` 記録あり)その turn に `confirmation` 通知を出さずに止まろうとする。**停止をブロックする**
|
|
78
|
-
| **hold の解放**(`.claude/skills/workflow-commands/SKILL.md` → §2f、joshuafolkken/kit#2121) | `pnpm josh stop:guard` — `Stop` フックで、ツリーが綺麗(`git status --porcelain` が空)なのに `run:hold` 記録が残ったまま止まる。**停止をブロックする**
|
|
79
|
-
| **Issue 引用の書式**(`prompts/collaboration-workflow/issue-citation.md`、joshuafolkken/kit#2121・joshuafolkken/kit#2247) | `pnpm josh stop:guard` — `Stop` フックで、`last_assistant_message` の地の文に裸の `#N` を含む。**停止をブロックする**(モデルに差し戻し、引用を直して返信を出し直させる)
|
|
80
|
-
| **起票の申し出**(`.claude/skills/workflow-commands/SKILL.md` → §2i、joshuafolkken/kit#2422) | `pnpm josh stop:guard` — `Stop` フックで、`last_assistant_message` の地の文が起票を申し出る(「起票してよければ」等)のに、transcript 末尾に起票(`filing_cap` の数え方)が無い。**停止をブロックする**
|
|
81
|
-
| **規則本文を散文に書き足す前の第 0 問・順序の問い**(`residency.md`、joshuafolkken/kit#2272・joshuafolkken/kit#2324) | `pnpm josh rule:guard` — `CLAUDE.md` / `prompts/**/*.md` / `.claude/skills/**/*.md` を対象とする `Edit` / `Write` で、リンク・URL を除いた正味の追記が閾値以上(規則本文の追記)。誤字・リンク張り替え・削除は当たらない。第 0 問(`oracle:list`)と順序の問い(`run:step
|
|
82
|
-
| **オラクル未参照の行為**(`decision-oracle.ts`、joshuafolkken/kit#2324) | `pnpm josh rule:guard` — 発火点を宣言したオラクルが支配する `Bash`(`pkg:scout` = `pnpm add
|
|
56
|
+
| 規則 | 引き金 | 引き金が発火しないターンでは |
|
|
57
|
+
| ---------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
|
|
58
|
+
| **ターン内バッチング**(`turn-batching.md`) | `pnpm josh batch:guard` — 単発呼び出しのターンが 3 つ続いた次の `Bash` / `Edit` / `Read` | 往復あたりの呼び出し数が下限を上回っている = 規則は既に守られている |
|
|
59
|
+
| **調査の委譲しきい値**(`SKILL.md` → §2b) | `pnpm josh investigation:guard` — 編集しないファイルの読み取りが 3 件目に達した `Read` / `Bash` | 読み取りがしきい値未満 = 委譲する対象がまだ無い |
|
|
60
|
+
| **バックログ WIP 上限**(`wip-cap.md`) | `pnpm josh rule:guard` — Issue を起票する `Bash`(`pnpm josh issue:file`) | Issue が起票されていない = 上限に触れる行為が無い |
|
|
61
|
+
| **直接起票の禁止**(`docs/josh-commands.md` → `josh issue:file`、joshuafolkken/kit#2808) | `pnpm josh rule:guard` — `josh issue:file` を通さずに Issue を作成する `Bash`(`gh issue create`、または `…/issues` への `title` 付き POST)。拒否文は `pnpm josh issue:file` の書式を渡す。重複探し・本文の検査と分類ラベル・`## Origin` の確認・`epic:bundle` はこのコマンドが実行するので、それぞれを別の行で見張らない。**毎回発火する** | `pnpm josh issue:file` で起票している = 起票の全段がそろっている |
|
|
62
|
+
| **1 ラン 10 件の起票上限**(`.claude/skills/workflow-commands/SKILL.md` → §2d、`observation-filing.md`) | `pnpm josh rule:guard` — Issue を起票する `Bash`(`pnpm josh issue:file`)。そのランで**拒否されていない**起票が既に 10 件あるとき、11 件目を拒否する。終了コードが 0 でない `issue:file` 呼び出しは起票に数えない。`is_issue_filing` を再利用し、新しい述語は作らない | 拒否されていない起票が 10 件未満 = 上限に触れていない。**繰り返す行為を止める行なので毎回発火する**(下の「例外」に従う) |
|
|
63
|
+
| **Issue コメントの読み取り**(`SKILL.md` → §2g) | `pnpm josh rule:guard` — Issue 本文だけを読む `Bash`(`gh issue view <N>`、または `…/issues/<N>` で終わる GET) | Issue を読んでいない、または最初から `--comments` 付きで読んでいる |
|
|
64
|
+
| **本文をシェルに載せない**(`shell-body.md`) | `pnpm josh rule:guard` — 二重引用符の本文値(`-f body="…"` / `-f "body=…"` / `--body "…"` / `--notify-message "…"`)にバッククォートか `$` を含む `Bash` | 本文がシェルに評価されない = 規則は既に守られている |
|
|
65
|
+
| **@path をリテラル投稿する誤射**(`shell-body.md`、joshuafolkken/kit#2304) | `pnpm josh rule:guard` — 生フィールド(`-f` / `--raw-field`)で `body=@<path>` を渡した `Bash`。`@` で始まる値をそのまま送るため `@<path>` がコメントとして投稿される。**毎回発火する** | `-F` / `--field body=@`(ファイルを読む正しい綴り)・`@` を含まない本文・`pnpm josh issue:comment` = リテラルの `@path` が送出されない |
|
|
66
|
+
| **検証コマンドのパイプ**(`output-bounds.md`) | `pnpm josh rule:guard` — 合否を意味する josh のチェック(`gate` / `check` / `lint*` / `cspell*` / `test*` / `eval` / `overrides` / `ranges`)がパイプの手前に立った `Bash` | 検証の終了コードが握りつぶされていない = 失敗が失敗として伝わる |
|
|
67
|
+
| **早すぎる進捗報告**(`.claude/skills/workflow-commands/backlogrun-progress.md` → 「Progress while the run is quiet」) | `pnpm josh rule:guard` — 待つことだけが目的の `Bash`(全区間が `sleep`、または `sleep` に `echo` / `:` / `date` が並ぶだけ)。既に生きているタイマーがある回、またはその報告が間隔前に着地する回に拒否する | 待機タイマーを自前で張っていない = 時計は `run:progress` が 1 本だけ持っている |
|
|
68
|
+
| **run 末尾の空転**(`background-commands.md`) | `pnpm josh rule:guard` — commit・push・PR 作成をまとめる段(`pnpm josh git -y` / 別名 `josh g`、`--yes` も同じ)を**前景**で出した `Bash`。`run_in_background` が付いていれば引き金に当たらない | 背景で発行している = 完了通知が run を再開させ、push とマージのあいだが空かない |
|
|
69
|
+
| **gate 手前の cut**(`.claude/skills/workflow-commands/pre-gate-cut.md`) | `pnpm josh rule:guard` — レーンの作業ツリーに居て、目印 `JOSH_LANE_CHILD` がその Issue を指し、cut 記録がまだ無いまま `pnpm josh gate`(別名 `josh ga` も同じ)を走らせる `Bash` | レーン以外の checkout に居る、目印が無い(レーン内の人)、または既に cut 済み = 規則が守られている状態 |
|
|
70
|
+
| **実装フェーズの cut**(`.claude/skills/workflow-commands/pre-gate-cut.md` → 「It is a guard, fired at the edit that crosses the threshold」、joshuafolkken/kit#2310) | `pnpm josh rule:guard` — 派遣されたレーンの子(目印 `JOSH_LANE_CHILD` がその Issue を指す)で cut 記録がまだ無く、直近文脈のコスト(`pnpm josh cost --cut` と同一の統計・同一のしきい値 `CONTEXT_CUT_THRESHOLD`、測定不能は `!== UNDER` で安全側に発火)が超過しているときに出した `Edit` / `Write`。編集の直前で拒否し `pnpm josh run:cut --impl <N> --handoff <path>` を返す。**しきい値を跨ぐたびに発火する**(`decide`、joshuafolkken/kit#2385)。直後の同じ編集の出し直しは通すので `busy` / `failed` でも空回りしない | レーンの子でない(レーン外・目印なし・レーン内の人)、非編集ツール、既に cut 済み、またはコストがしきい値未満 = 打ち切りが要らない状態 |
|
|
71
|
+
| **レーンの子の保留**(`.claude/skills/workflow-commands/pre-gate-cut.md`) | `pnpm josh rule:guard` — 派遣されたレーンの子(目印 `JOSH_LANE_CHILD` がその Issue を指す)が、判断待ちの停止通知 `pnpm josh notify --task-type confirmation` を出した `Bash` | レーンの子でない(レーン外・目印なし・レーン内の人)、または停止通知でない = 保留を記録すべき停止が無い |
|
|
72
|
+
| **レーンの子の対話質問**(`.claude/skills/workflow-commands/pre-gate-cut.md` → 「The interactive ask is refused one call earlier」) | `pnpm josh rule:guard` — 派遣されたレーンの子(目印 `JOSH_LANE_CHILD` がその Issue を指す)が `AskUserQuestion` を呼んだ呼び出し。停止通知の 1 つ手前で拒否し park 手順を返す。ハーネスは対話質問をターン終了で拒否するので、この 1 つ前が正しい引き金である。**毎回発火する** | レーンの子でない(レーン外・目印なし・レーン内の人)、または対話ツールでない = 握りつぶされる質問が無い |
|
|
73
|
+
| **テストの宣言**(`CLAUDE.md` → Code Change Rules) | `pnpm josh rule:guard` — commit・push・PR 段(`pnpm josh git -y` / 別名 `josh g`)を出した瞬間に `pnpm josh test:declared` が作業ツリーを `required`(テストでも機械的免除パスでもない変更ファイルがある)と読む `Bash`。`run_tail` の commit 段照合を再利用し、新しい配送経路は作らない | 差分が `exempt`(`*.md` 等の非ランタイム)または `satisfied`(`*.test.ts` / `*.e2e.ts` を含む)= テストが要らない、または既に伴っている。免除は人が Step 0 で宣言するため 1 ラン 1 回だけ配送する |
|
|
74
|
+
| **force push / ブランチ削除**(`operating-rules.md`、joshuafolkken/kit#2120) | `pnpm josh rule:guard` — `git push` / `git branch` を argv として解析し、force(`--force` / `-f` / 結合クラスタ `-uf`)・削除(`--delete` / `-d` / `-D` / コロン形 `:branch`)・`git -C` 前置を綴りによらず判定した `Bash`。**毎回発火する** | force / 削除でない通常の `git push` / `git branch` = 破壊的操作が無い |
|
|
75
|
+
| **認可外の作業ツリー変更**(`operating-rules.md`、joshuafolkken/kit#2120) | `pnpm josh rule:guard` — `git checkout -- <path>` / `git restore <path>`(worktree)/ 認可外の `git stash`(bare・メッセージ無し push・位置指定 pop 等)を出した `Bash`。**毎回発火する** | メッセージ付き `git stash push -m` ・`git stash list` ・`pnpm josh git` ・`pnpm josh stash:pop` = 認可された退避 |
|
|
76
|
+
| **ファイル本文をシェルに載せない**(`file-edits.md`、joshuafolkken/kit#2120) | `pnpm josh rule:guard` — 既存ファイルを対象としたヒアドキュメント書き込み・書き込みを伴う `node -e` / インタプリタ heredoc・in-place `perl -0pi -e` を出した `Bash`。既存判定は `stat` 1 回。**毎回発火する** | 新規ファイル作成・読み取り専用のヒアドキュメント・短い `sed -i` = 本文を丸ごと運んでいない |
|
|
77
|
+
| **停止時の通知**(`CLAUDE.md` →「Mid-workflow stop notification」、joshuafolkken/kit#2121) | `pnpm josh stop:guard` — `Stop` フックで、作業ツリーを押さえたまま(`run:hold` 記録あり)その turn に `confirmation` 通知を出さずに止まろうとする。**停止をブロックする** | 押さえを持っていない、`confirmation` 通知が末尾にある、または pre-gate cut を取った回(レーンの子の自動継続)= 人待ちの停止ではない(`stop_hook_active` が無限ループの歯止め) |
|
|
78
|
+
| **hold の解放**(`.claude/skills/workflow-commands/SKILL.md` → §2f、joshuafolkken/kit#2121) | `pnpm josh stop:guard` — `Stop` フックで、ツリーが綺麗(`git status --porcelain` が空)なのに `run:hold` 記録が残ったまま止まる。**停止をブロックする** | ツリーが汚れている(`halfrun` の commit 前停止・`needs-human-review` 停止)、または既に解放済み = 次のランが踏まない |
|
|
79
|
+
| **Issue 引用の書式**(`prompts/collaboration-workflow/issue-citation.md`、joshuafolkken/kit#2121・joshuafolkken/kit#2247) | `pnpm josh stop:guard` — `Stop` フックで、`last_assistant_message` の地の文に裸の `#N` を含む。**停止をブロックする**(モデルに差し戻し、引用を直して返信を出し直させる) | リンク形式で引用している、コードフェンス/インラインコード/引用行の中、`PR` / `pull request` 直後の `#N`、または GitHub 向け成果物(`gh` 引数として渡る)、あるいは `stop_hook_active` が立っている = 差し戻す裸の番号が無い |
|
|
80
|
+
| **起票の申し出**(`.claude/skills/workflow-commands/SKILL.md` → §2i、joshuafolkken/kit#2422) | `pnpm josh stop:guard` — `Stop` フックで、`last_assistant_message` の地の文が起票を申し出る(「起票してよければ」等)のに、transcript 末尾に起票(`filing_cap` の数え方)が無い。**停止をブロックする** | 起票済み、フェンス/引用行の中、第三者の `owner/repo` を名指す(`repo_party.classify`)、owner 不明、または `stop_hook_active` = 保留された Tier A 起票が無い |
|
|
81
|
+
| **規則本文を散文に書き足す前の第 0 問・順序の問い**(`residency.md`、joshuafolkken/kit#2272・joshuafolkken/kit#2324) | `pnpm josh rule:guard` — `CLAUDE.md` / `prompts/**/*.md` / `.claude/skills/**/*.md` を対象とする `Edit` / `Write` で、リンク・URL を除いた正味の追記が閾値以上(規則本文の追記)。誤字・リンク張り替え・削除は当たらない。第 0 問(`oracle:list`)と順序の問い(`run:step`)を突きつける。前提を満たすまで毎回拒否する(下の「例外」)。**#2324 で stand-down** — `oracle:list` と `run:step` の両方を走らせた記録が末尾にあれば通す(リマインダではなく「問いに答えたか」で通す) | 規則ドキュメントを編集していない、追記が閾値未満、または両方の配置判定コマンドを実行済み = 散文の規則が増えていない、または配置の問いに答え済み |
|
|
82
|
+
| **オラクル未参照の行為**(`decision-oracle.ts`、joshuafolkken/kit#2324) | `pnpm josh rule:guard` — 発火点を宣言したオラクルが支配する `Bash`(`pkg:scout` = `pnpm add`)を、そのオラクルの `pnpm josh <command>` 無しで出したとき拒否。拒否文は登録簿(判断・コマンド・回答語彙・単一ソース)から組み立て手書きしない。実行するまで毎回拒否し、実行済みで stand-down。発火点を名指しできないオラクルは理由を宣言し**可視化された未強制**として残る(`oracle:list` が発火点/理由を印字)。`release:scope` は理由側で、`pnpm josh followup` のマージ**後**に owe されるリリースを読むためマージを gate せず trail する(joshuafolkken/kit#2334) | 支配下の行為を出していない、またはそのオラクルを実行済み = 規則は既に守られている |
|
|
83
83
|
|
|
84
84
|
**引き金はシェルのコマンド文字列しか見えない。** したがって node の中から REST で Issue を作る経路(`pnpm josh propagate` など)は上の正規表現に掛からない。同じ理由で、本文をファイルで渡す `gh api --input <file>` もタイトルが文字列に現れないため掛からない(`-f` / `-F` / `--field` / `--raw-field` の 4 綴りは覆う)。**これが、規則本文を配送に移したうえで常駐側にトリガ 1 行を残す理由の 1 つである** — 常駐の 1 行は経路によらず効き、配送はそれを効く瞬間に補強する。引き金つきの配送だけにすると、正規表現が知っている綴りだけが規則の適用範囲になる。
|
|
85
85
|
|
|
@@ -95,7 +95,7 @@ WIP 上限の引き金は Issue の**作成**だけを見る。`…/issues/<N>/c
|
|
|
95
95
|
|
|
96
96
|
**拒否文は「コメントも読め」という要求ではなく、読み直しのコマンドそのものを渡す。** 散文の要求は joshuafolkken/kit#1344 が 3 ラン連続で「数値がまったく動かなかった」と実測した形式であり、この行がそれを避けるのは、本文だけの読み取りが**通らない**ことによる。ランの次の一手はコメント込みの読み取りになり、コメントは「読むべきもの」ではなく実際に手元にある。ただし `PreToolUse` の拒否が運べるのは文字列 1 本だけなので、**フックがコメント本文を注入するわけではない** — 1 往復ぶん遅れて、ランが自分で取りに行く。
|
|
97
97
|
|
|
98
|
-
**この行は `batch:guard` と引き金が重なる唯一の行である。** `time_batch_guard.is_guarded_call` は `gh issue view` を候補として扱う(`gh issue create`
|
|
98
|
+
**この行は `batch:guard` と引き金が重なる唯一の行である。** `time_batch_guard.is_guarded_call` は `gh issue view` を候補として扱う(`gh issue create` は扱わない)。ただしこの行は前提を満たすまで毎回拒否するので(下の「例外」)、`is_first_delivery` の待避分岐を通らず、バッチングの拒否が出るターンでも拒否する。`delivered-rules.test.ts` がこれを固定している。
|
|
99
99
|
|
|
100
100
|
**判定はコマンド 1 件ごとに、行頭に錨を打って行う。** シェル 1 行は複数のコマンドを運ぶため、`&&` / `||` / `;` / 改行で区切った各区間をそれぞれ判定する(`|` は区切りに使わない — `--jq` の中に現れる方がはるかに多い)。これがないと 2 つの誤りが同時に出る。引用符の中に `gh issue view` を含む書き込み(`gh issue comment <N> -b "…"`)が読み取りと誤認され、逆に `gh issue view <N> && grep -c x` のように無関係な `-c` を含む行が「コメント込み」と誤認されて配送が消える。
|
|
101
101
|
|
|
@@ -117,6 +117,19 @@ WIP 上限の引き金は Issue の**作成**だけを見る。`…/issues/<N>/c
|
|
|
117
117
|
|
|
118
118
|
**`batch:guard` との待避分岐は、そういう行では守る対象が変わる。** 1 ラン 1 回の行では**記録**を守っていた — レースに負ければ、そのランに 1 度しかない配送を無駄打ちするからである。毎回発火する行はその形では消えない。一方で待機タイマーを張る呼び出しは定義上「単発の `Bash`」であり、まさにバッチングガードも候補として扱う形なので、**拒否まで待避させると、10 秒の窓とその中の再発行で規則がまるごと黙る**。したがって**拒否は無条件に行い、待避は「記録してよいか」として行へ渡す** — 他のフックが止めうる呼び出しでしてはならないのは、走らなかったタイマーを生きていると書き残すことだからである。配送文には「これは毎回発火する」と明記する(1 回きりだと読まれると、再発行すれば通ると誤解される)。
|
|
119
119
|
|
|
120
|
+
### 例外 — 前提を求める行は、前提が満たされるまで毎回拒否する(joshuafolkken/kit#2807)
|
|
121
|
+
|
|
122
|
+
**`already_satisfied`(前提の行為がトランスクリプト末尾にあるか)を持つ行は、1 ラン 1 回の対象から外れる。** 前提が末尾に現れるまで、毎回拒否する。1 ラン 1 回のままだと、前提を飛ばしたランの再発行がそのまま通る。手順を読み飛ばしたランほどガードを素通りしてしまい、ガードの意味がない。対象は `issue-fold`・`issue-comments`・`rule-body`・`oracle-consulted:*` である。
|
|
123
|
+
|
|
124
|
+
従うランが詰まることはない。前提の行為を済ませれば、stand-down が先に答えて通すからである。記録を使い切る心配もないので、`batch:guard` との待避もしない。待避すると、10 秒の窓の中の再発行が前提を満たさないまま通ってしまう。配送文には「前提が末尾に現れるまで拒否する」と明記する。
|
|
125
|
+
|
|
126
|
+
**前提の証拠を読めないときは、拒否を基本とする。** たとえば前提の行為を確かめるための記録が読めないときがこれに当たる。意図して通す例外は、理由をコードのコメントに残す。現在の例外は次のとおりである。
|
|
127
|
+
|
|
128
|
+
- トランスクリプト自体を読めないとき(`hook-decision.ts`)
|
|
129
|
+
- 初回の起票で、畳み込む相手がないとき(`issue-fold.ts`)
|
|
130
|
+
- 進捗の記録がないとき(`early-heartbeat.ts`)
|
|
131
|
+
- git の読み取りに失敗して、変更なしとして扱うとき(`delivered-rules.ts` の `test-declared` 行)
|
|
132
|
+
|
|
120
133
|
## 本文のシェル評価は同じ機構の 1 行で覆った(joshuafolkken/kit#1198)
|
|
121
134
|
|
|
122
135
|
コメント本文をシェルに載せるとバッククォートがコマンドとして実行される問題は、**この機構の追加 1 行で覆った**。起票時の見込みどおり新しい機構は要らず、`delivered-rules.ts` の `shell-body` 行と、その発火・非発火を固定するテストで足りている(実装先として書いていた joshuafolkken/kit#1542 は #1198 の重複として閉じられたため、実装は #1198 側で行った)。
|
|
@@ -136,11 +149,11 @@ WIP 上限の引き金は Issue の**作成**だけを見る。`…/issues/<N>/c
|
|
|
136
149
|
- `scripts/rules/piped-verification-rule.test.ts` — 検証コマンドのパイプが `output-bounds.md` に単一ソースとして存在し、配送文が仕組み・逃げ道・境界の 3 つを運び、読み取り専用の一覧を巻き込んでいないこと
|
|
137
150
|
- `scripts/rules/pre-gate-cut-rule.test.ts` — gate 手前の cut の手順が `.claude/skills/workflow-commands/pre-gate-cut.md` に単一ソースとして存在し、この文書と `docs/josh-commands.md` の双方が引き金を書いていること。発火・非発火(レーンかつ目印ありかつ未 cut でのみ拒否し、目印の無い人・レーン外・cut 済みでは無言)は `scripts/rules/pre-gate-cut.test.ts` が固定する
|
|
138
151
|
- `scripts/rules/implementation-cut-rule.test.ts` — 実装フェーズの cut の手順が `.claude/skills/workflow-commands/pre-gate-cut.md` →「It is a guard, fired at the edit that crosses the threshold」に単一ソースとして存在し、配送文がその節・命令・再武装の契約(再開で記録を消して両ガードを再武装させること、joshuafolkken/kit#2310)を運ぶこと。発火・非発火(レーンの子かつ目印ありかつ未 cut かつしきい値超過の `Edit` / `Write` でのみ拒否し、レーン外・目印なし・非編集・cut 済み・しきい値未満では無言)は `scripts/rules/implementation-cut.test.ts` が固定する
|
|
139
|
-
- `scripts/rules/
|
|
152
|
+
- `scripts/rules/delivered-rules-filing.test.ts` — 直接起票(`gh issue create`、`…/issues` への `title` 付き POST)が毎回拒否されて `pnpm josh issue:file` を案内され、`issue:file` の呼び出し自体は拒否されないこと。起票上限と畳み込みの行が `issue:file` の呼び出しで発火し、失敗した `issue:file` 呼び出しを起票に数えないことも同じスイートが固定する
|
|
140
153
|
- `scripts/rules/filing-cap.test.ts` — 起票回数の計数(拒否された起票を数に入れないこと)を固定する。発火・非発火(10 件目まで無言・11 件目で拒否・毎回発火する)は `scripts/rules/delivered-rules.test.ts` が固定する
|
|
141
154
|
- `scripts/rules/lane-park-rule.test.ts` — レーンの子の保留の手順が `.claude/skills/workflow-commands/pre-gate-cut.md` に単一ソースとして存在し、配送文がその節を指すこと。発火・非発火(レーンの子かつ停止通知でのみ拒否し、レーン外・目印なし・停止通知でないときは無言)は `scripts/rules/lane-park.test.ts` が固定する
|
|
142
155
|
- `scripts/rules/lane-interactive-ask-rule.test.ts` — レーンの子の対話質問の手順が `.claude/skills/workflow-commands/pre-gate-cut.md` → 「The interactive ask is refused one call earlier」に単一ソースとして存在し、配送文がその節を指すこと(joshuafolkken/kit#2201)。発火・非発火(レーンの子の `AskUserQuestion` でのみ拒否し、レーン外・目印なし・対話ツールでないときは無言)と毎回発火は `scripts/rules/lane-interactive-ask.test.ts` が、終了記録からの質問・選択肢の取り出しは `scripts/agent/interactive-ask.test.ts` が固定する
|
|
143
156
|
- `scripts/rules/lane-switch-main.test.ts` — レーンの子の `git switch main`(joshuafolkken/kit#2313)を、失敗する前に拒否する述語(`switch_target` / `is_switch_away_from_lane`)と配送文の文言を固定する。手順の単一ソースは `.claude/skills/workflow-commands/backlogrun-lanes.md` / `backlogrun-child.md`。発火・非発火(レーンの子が自分のレーンブランチ以外へ切り替えるときのみ拒否し、自ブランチ・作成/デタッチ・レーン外・主チェックアウトでは無言)と毎回発火、「1 コマンドを 1 行だけが主張する」ことは同スイートが固定する
|
|
144
157
|
- `scripts/rules/git-argv.test.ts` / `scripts/rules/git-force.test.ts` / `scripts/rules/worktree-guard.test.ts` / `scripts/rules/file-body.test.ts` — Bash 文字列の 3 群(joshuafolkken/kit#2120)の述語と配送文の文言を固定する。実際の配送経路での発火・無言と「1 コマンドを 1 行だけが主張する」ことは `scripts/rules/delivered-rules-bash.test.ts` が固定する
|
|
145
|
-
- `scripts/rules/rule-body-guard.test.ts` — 規則本文を散文に書き足す `Edit` / `Write`(joshuafolkken/kit#2272)の述語・配送文・列挙表掲載を固定する。発火(規則ドキュメントへの追記)と無言(誤字・リンク張り替え・削除・非規則ファイル・非 Edit/Write
|
|
158
|
+
- `scripts/rules/rule-body-guard.test.ts` — 規則本文を散文に書き足す `Edit` / `Write`(joshuafolkken/kit#2272)の述語・配送文・列挙表掲載を固定する。発火(規則ドキュメントへの追記)と無言(誤字・リンク張り替え・削除・非規則ファイル・非 Edit/Write)、および実際の配送経路での「2 つのコマンドを実行するまで毎回拒否」はこの 1 スイートが固定する。配送文が第 0 問(`oracle:list`)と順序の問い(`run:step`)を運び、単一ソースが `residency.md` であることも同じスイートが押さえる
|
|
146
159
|
- `scripts/rules/stop-rules-rule.test.ts` — 停止時 4 規則の単一ソース(`CLAUDE.md` の停止通知・`SKILL.md` → §2f・`SKILL.md` → §2i・`issue-citation.md`)と `rule-delivery.md` の列挙表掲載を固定する。発火・無言(押さえ×未通知でブロック/綺麗×押さえでブロック/汚れ・通知済みで無言/`stop_hook_active` で無言/起票の申し出×未起票でブロック・起票済み/第三者リポジトリ/owner 不明で無言/裸 `#N` でブロック・リンク形式やコードフェンス/インラインコード/引用行/PR 参照の中で無言)は `scripts/rules/stop-rules.test.ts`・`scripts/rules/filing-offer.test.ts`・`scripts/rules/issue-citation.test.ts` が、`Stop` フックの配線は `scripts/claude/claude-settings-hooks.test.ts` が固定する
|
|
@@ -16,7 +16,7 @@
|
|
|
16
16
|
|
|
17
17
|
1. **現在の作業を退避する**: `git stash push -u -m "upstream-interrupt: paused #<N>"`(WIP コミットでも可)。退避したことを忘れないよう、この時点で stash を作る。メッセージ付きで積むのは、復元をメッセージで特定するため(下記ステップ 5)
|
|
18
18
|
2. **進行中の Issue に状況を明記する**: `pnpm josh issue:comment <N> --body-file <path>` で、(a) 作業を stash したこと、(b) 中断理由(どの別パッケージの・どんな問題で中断したか)、(c) 対象パッケージに作成した新 Issue へのリンクを `## Upstream issues` 見出しの下に `owner/repo#N` 形式で、を記載する。これにより「なぜこの Issue が一時停止しているか」と「何を待っているのか」が後から監査できる(→「起票元へのバックリンク」)
|
|
19
|
-
3. **対象パッケージのリポジトリに新しい Issue を作成する(確認なし)**: `
|
|
19
|
+
3. **対象パッケージのリポジトリに新しい Issue を作成する(確認なし)**: `pnpm josh issue:file "<root-cause title>" --body-file <body-file> --depth <n> --route tier-a --repo <owner>/<repo>`(`route:tier-a` は実装中の Tier A 起票を経路として集計するためのラベル — joshuafolkken/kit#1083。付与するのは first-party の場合だけで、third-party はコマンド自身が拒否し起票しない)。本文の検査、本文が宣言する分類ラベルの付与、`## Origin` 節の確認、起票先での重複探し、`epic:bundle` はこのコマンドが実行する(`docs/josh-commands.md` → `josh issue:file`)。分類の単一ソースは `prompts/collaboration-workflow/issue-template.md`。根本原因・再現・期待結果を Step 1 のテンプレに沿って記載し、**本文に `## Origin` 節を置いて起票元 Issue を `owner/repo#N` 形式で書く**(→「起票元へのバックリンク」)。上流 Issue は欠陥を、起票元 Issue は証拠を持つため、リンクがないと上流 Issue は後から解釈できなくなる。ここで「起票してよいか」を尋ねて停止してはならない。**ただし確認なしで起票してよいのは対象が first-party のときだけ**で、third-party(owner が自リポジトリと一致しないリポジトリ)なら起票せずに停止する(→「第三者リポジトリへの書き込みは Tier C(明示指示が必要)」)
|
|
20
20
|
4. **`confirmation` Telegram を送って停止する**: 上流 Issue の URL と、何が止まっているかを本文に書く(→「確認待ちで停止するときの Telegram 通知(`confirmation`)」)。無人実行でも画面外でユーザーが気付ける。停止は Issue が既に存在する状態で行うので、ユーザーは「待つ / 先送りする」を Issue を見ながら 1 語で答えられる
|
|
21
21
|
5. **元の作業を再開する**: 上流の修正がマージされた、または**ユーザーが先送りを明示判断した**後に `pnpm josh stash:pop "upstream-interrupt: paused #<N>"`(位置指定や引数なしの `git stash pop` は使わない — stash は全 work tree が共有する 1 本のスタックで、別のレーンの stash を取り込む恐れがある)して、退避していた元タスクを続行する。上流 Issue を割り込みで実装するかどうかもユーザーの判断(上流パッケージの実装・PR・マージはそれぞれのワークフロー規則に従う)
|
|
22
22
|
|
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
|
|
3
3
|
**単一ソースはこのファイルである。**
|
|
4
4
|
|
|
5
|
-
**この規則は常駐していない — 引き金つき配送に移った**(joshuafolkken/kit#1524)。`pnpm josh rule:guard` が Issue を作成する `Bash` 呼び出し(`gh issue create
|
|
5
|
+
**この規則は常駐していない — 引き金つき配送に移った**(joshuafolkken/kit#1524)。`pnpm josh rule:guard` が Issue を作成する `Bash` 呼び出し(`pnpm josh issue:file`。`gh issue create` や `…/issues` への `title` 付き POST による直接起票は `direct-filing` が毎回拒否する)を拒否し、そこで数え方・拒否・2 つの免除・免除を決める 3 条件を突きつける。起票が無いターンでは何も起きず、それは上限に触れる行為が無いということである。配送は**ラン 1 回につき 1 度**なので、数えたうえで同じ呼び出しをもう一度出せばよい。機構と列挙表は `rule-delivery.md`、配送文の実体は `scripts/rules/delivered-rules.ts` の `WIP_CAP_REASON` にあり、`scripts/backlog/backlog-manufacturing-rule.test.ts` がその中身を固定している。
|
|
6
6
|
|
|
7
7
|
## 規則
|
|
8
8
|
|
|
@@ -69,10 +69,10 @@ gh api "repos/{owner}/{repo}/issues?state=open&per_page=100" --paginate \
|
|
|
69
69
|
|
|
70
70
|
#### 該当したときの手順
|
|
71
71
|
|
|
72
|
-
1. **上限に関係なく起票する。** 1
|
|
72
|
+
1. **上限に関係なく起票する。** 1 件閉じることを条件にしない。起票は `pnpm josh issue:file` で行う。本文の検査、本文が宣言する分類ラベルの付与、重複探し、`epic:bundle` はこのコマンドが実行する(`docs/josh-commands.md` → `josh issue:file`)。
|
|
73
73
|
|
|
74
74
|
```bash
|
|
75
|
-
|
|
75
|
+
pnpm josh issue:file "<title>" --body-file <body-file> --depth <n> --route interrupt
|
|
76
76
|
```
|
|
77
77
|
|
|
78
78
|
2. **超過している事実と、当たった条件の番号を本文に 1 行書く**(例:「オープン 37 件で起票。3 条件のうち 2(文書化された手順が完了できない)に該当」)。**どちらか一方では足りない** — 件数だけでは判定が再現できず、条件だけでは上限の可視化が効かない。
|
package/prompts/review.md
CHANGED
|
@@ -183,7 +183,7 @@ Only branch 2 files an Issue. What follows applies to that branch.
|
|
|
183
183
|
than being deferred — see below.)
|
|
184
184
|
- **Filing does not end at the Issue.** `epic:next` only ever offers a child the task list of an epic
|
|
185
185
|
names, so an Issue in no epic is never handed to a running `backlogrun` — the deferred finding is parked
|
|
186
|
-
forever, which reads the same from the backlog. **The
|
|
186
|
+
forever, which reads the same from the backlog. **The steps run inside the CI wait, not before
|
|
187
187
|
the commit.** Where the run opens a pull request — `fullrun`, `backlogrun` — they go after
|
|
188
188
|
`pnpm josh git -y` and before `pnpm josh followup`; where it does not — `halfrun`, or a standalone
|
|
189
189
|
pre-commit self-review — they run as soon as the disposition is decided. **The chain may run in a
|
|
@@ -191,14 +191,16 @@ Only branch 2 files an Issue. What follows applies to that branch.
|
|
|
191
191
|
- **When the round files a second follow-up, run `pnpm josh issue:fold` first.** Several findings from
|
|
192
192
|
one review fold into one Issue by default — the filing-time counterpart to the split assessment,
|
|
193
193
|
reading the same two questions (`.claude/skills/workflow-commands/split-assessment.md` → "The same
|
|
194
|
-
two questions decide the filing-time fold"). `pnpm josh rule:guard` refuses the second
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
1. File the follow-up Issue referencing the current one, tagged `route:review-cap
|
|
198
|
-
`
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
194
|
+
two questions decide the filing-time fold"). `pnpm josh rule:guard` refuses the second
|
|
195
|
+
`pnpm josh issue:file` call until it is folded; the first filing asks nothing.
|
|
196
|
+
|
|
197
|
+
1. File the follow-up Issue referencing the current one, tagged `route:review-cap` — **before the current Issue closes.**
|
|
198
|
+
`pnpm josh issue:file "<title>" --body-file <body-file> --depth <n> --route review-cap`.
|
|
199
|
+
The command lints the body, applies the classification labels it declares, runs the duplicate scan
|
|
200
|
+
and then `epic:bundle` on the new Issue (`docs/josh-commands.md` → `josh issue:file`). The
|
|
201
|
+
`epic:bundle` candidate search reads open issues only, so once the parent has closed it answers
|
|
202
|
+
`none` permanently.
|
|
203
|
+
2. Act on the `epic:bundle` answer it prints. **`epic:bundle` recommends and writes nothing**, so acting means running the
|
|
202
204
|
write command yourself — never a hand edit of the epic body, which leaves the task list and the
|
|
203
205
|
`blocked-by` relations disagreeing and `epic:next` returning `error`.
|
|
204
206
|
|
|
@@ -1,18 +1,15 @@
|
|
|
1
1
|
import { readFileSync } from 'node:fs'
|
|
2
2
|
import { fileURLToPath } from 'node:url'
|
|
3
|
+
import {
|
|
4
|
+
BREAKING_CHANGE_LABEL as BREAKING,
|
|
5
|
+
BUGFIX_LABEL as BUGFIX,
|
|
6
|
+
RELEASE_CLASSIFICATION_NAMES as CLASSIFICATION_LABELS,
|
|
7
|
+
ENHANCEMENT_LABEL,
|
|
8
|
+
type ReleaseClassification,
|
|
9
|
+
} from '#scripts/git/issue-labels'
|
|
3
10
|
import { execaSync } from 'execa'
|
|
4
11
|
import { z } from 'zod'
|
|
5
12
|
|
|
6
|
-
const BUGFIX = 'bugfix'
|
|
7
|
-
const BREAKING = 'breaking-change'
|
|
8
|
-
const CLASSIFICATION_LABELS = [
|
|
9
|
-
BREAKING,
|
|
10
|
-
'enhancement',
|
|
11
|
-
BUGFIX,
|
|
12
|
-
'other-change',
|
|
13
|
-
'ignore-for-release',
|
|
14
|
-
] as const
|
|
15
|
-
type ReleaseClassification = (typeof CLASSIFICATION_LABELS)[number]
|
|
16
13
|
const CLASSIFICATION_SET: ReadonlySet<string> = new Set(CLASSIFICATION_LABELS)
|
|
17
14
|
const ISSUE_BUG_LABEL = 'bug'
|
|
18
15
|
const DECLARATION = /^- リリース分類: (.+)$/gmu
|
|
@@ -50,7 +47,7 @@ function release_candidates(
|
|
|
50
47
|
): ReadonlyArray<ReleaseClassification> {
|
|
51
48
|
if (!candidates.includes(BREAKING)) return candidates
|
|
52
49
|
|
|
53
|
-
return candidates.filter((candidate) => candidate !==
|
|
50
|
+
return candidates.filter((candidate) => candidate !== ENHANCEMENT_LABEL)
|
|
54
51
|
}
|
|
55
52
|
|
|
56
53
|
function issue_candidates(issue_json: string): ReadonlyArray<ReleaseClassification> {
|
|
@@ -132,4 +129,8 @@ const pr_classification = {
|
|
|
132
129
|
select_issue_classification,
|
|
133
130
|
}
|
|
134
131
|
|
|
135
|
-
export { pr_classification
|
|
132
|
+
export { pr_classification }
|
|
133
|
+
export {
|
|
134
|
+
RELEASE_CLASSIFICATION_NAMES as CLASSIFICATION_LABELS,
|
|
135
|
+
type ReleaseClassification,
|
|
136
|
+
} from '#scripts/git/issue-labels'
|
|
@@ -53,16 +53,16 @@ const DELEGATABLE_STEPS: ReadonlyArray<DelegatableStep> = [
|
|
|
53
53
|
},
|
|
54
54
|
{
|
|
55
55
|
name: 'followup-filing',
|
|
56
|
-
// The late-run follow-up filing chain — issue:
|
|
57
|
-
// — is a dependency chain `batch:guard`
|
|
58
|
-
//
|
|
59
|
-
// (joshuafolkken/kit#1892). A fresh unit runs the same
|
|
60
|
-
// round cap already composed the finding text, so what
|
|
61
|
-
//
|
|
62
|
-
//
|
|
63
|
-
does: 'run the late-run follow-up filing chain in an isolated unit — issue:scout,
|
|
56
|
+
// The late-run follow-up filing chain — `issue:file` (which scouts, lints, labels and runs
|
|
57
|
+
// epic:bundle itself, joshuafolkken/kit#2808) → epic --add — is a dependency chain `batch:guard`
|
|
58
|
+
// correctly keeps one call per step, and what makes it expensive is the ~340k context it runs in
|
|
59
|
+
// at the tail of a run, not the step count (joshuafolkken/kit#1892). A fresh unit runs the same
|
|
60
|
+
// chain at a small context, and the review round cap already composed the finding text, so what
|
|
61
|
+
// the unit is handed is mechanical: run the commands and act on their deterministic answers. This
|
|
62
|
+
// is not `batch:guard`'s business — delegation only moves the chain to a cheaper context.
|
|
63
|
+
does: 'run the late-run follow-up filing chain in an isolated unit — issue:file (scout, lint, labels, epic:bundle), then epic --add — given the finding text the parent already composed, and return the new Issue number and the epic:bundle answer',
|
|
64
64
|
verifier:
|
|
65
|
-
"the parent reads the filed Issue from GitHub with `pnpm josh issue:state <new>`, not the unit's summary; a follow-up reported filed but not created is still absent, the way `epic-child`'s reported-done-but-not-merged child is still open. The finding text came from the parent and `issue:
|
|
65
|
+
"the parent reads the filed Issue from GitHub with `pnpm josh issue:state <new>`, not the unit's summary; a follow-up reported filed but not created is still absent, the way `epic-child`'s reported-done-but-not-merged child is still open. The finding text came from the parent and `issue:file`'s steps are deterministic, so the unit's work is the mechanical execution `issue:state` confirms",
|
|
66
66
|
},
|
|
67
67
|
{
|
|
68
68
|
name: 'implementation-unit',
|
|
@@ -82,7 +82,7 @@ const DOCUMENT_BYTE_BUDGET: ReadonlyArray<DocumentBudget> = [
|
|
|
82
82
|
{ path: '.claude/skills/workflow-commands/rule-residency.md', bytes: 24_576 },
|
|
83
83
|
{ path: '.claude/skills/workflow-commands/target-repository.md', bytes: 8192 },
|
|
84
84
|
{ path: '.claude/skills/workflow-commands/working-tree-hold.md', bytes: 8192 },
|
|
85
|
-
{ path: 'docs/josh-commands.md', bytes:
|
|
85
|
+
{ path: 'docs/josh-commands.md', bytes: 176_128 },
|
|
86
86
|
{ path: 'prompts/agent-rules.md', bytes: 4096 },
|
|
87
87
|
{ path: 'prompts/coding-standards.md', bytes: 16_384 },
|
|
88
88
|
{ path: 'prompts/collaboration-workflow.md', bytes: 8192 },
|
|
@@ -186,14 +186,16 @@ const DEPTH_LABELS: ReadonlyArray<{
|
|
|
186
186
|
},
|
|
187
187
|
]
|
|
188
188
|
|
|
189
|
-
|
|
190
|
-
// applies with its intended color (joshuafolkken/kit#2197). The `epic` and `in-progress` metadata is
|
|
191
|
-
// the one the workflow documents create them with; the labels only a person applies are left out.
|
|
192
|
-
const WORKFLOW_LABELS: ReadonlyArray<{
|
|
189
|
+
interface LabelDefinition {
|
|
193
190
|
name: string
|
|
194
191
|
color: string
|
|
195
192
|
description: string
|
|
196
|
-
}
|
|
193
|
+
}
|
|
194
|
+
|
|
195
|
+
// What `josh start` provisions on a new repository so the first `kickoff` finds every label a run
|
|
196
|
+
// applies with its intended color (joshuafolkken/kit#2197). The `epic` and `in-progress` metadata is
|
|
197
|
+
// the one the workflow documents create them with; the labels only a person applies are left out.
|
|
198
|
+
const WORKFLOW_LABELS: ReadonlyArray<LabelDefinition> = [
|
|
197
199
|
{
|
|
198
200
|
name: EPIC_LABEL,
|
|
199
201
|
color: '5319e7',
|
|
@@ -214,6 +216,41 @@ const WORKFLOW_LABELS: ReadonlyArray<{
|
|
|
214
216
|
...DEPTH_LABELS,
|
|
215
217
|
]
|
|
216
218
|
|
|
219
|
+
// The pull request release classifications `pr-classification.yml` requires exactly one of
|
|
220
|
+
// (joshuafolkken/kit#2797). The check is distributed by `josh sync`, so the labels it asks for are
|
|
221
|
+
// provisioned from this same list — a repository that never had them would otherwise fail the check
|
|
222
|
+
// on every pull request that is not an `enhancement`.
|
|
223
|
+
const BUGFIX_LABEL = 'bugfix'
|
|
224
|
+
const OTHER_CHANGE_LABEL = 'other-change'
|
|
225
|
+
const IGNORE_FOR_RELEASE_LABEL = 'ignore-for-release'
|
|
226
|
+
const RELEASE_CLASSIFICATION_NAMES = [
|
|
227
|
+
BREAKING_CHANGE_LABEL,
|
|
228
|
+
ENHANCEMENT_LABEL,
|
|
229
|
+
BUGFIX_LABEL,
|
|
230
|
+
OTHER_CHANGE_LABEL,
|
|
231
|
+
IGNORE_FOR_RELEASE_LABEL,
|
|
232
|
+
] as const
|
|
233
|
+
type ReleaseClassification = (typeof RELEASE_CLASSIFICATION_NAMES)[number]
|
|
234
|
+
|
|
235
|
+
const RELEASE_CLASSIFICATION_METADATA: Readonly<
|
|
236
|
+
Record<ReleaseClassification, Omit<LabelDefinition, 'name'>>
|
|
237
|
+
> = {
|
|
238
|
+
[BREAKING_CHANGE_LABEL]: { color: 'd93f0b', description: 'Release: a breaking change (major)' },
|
|
239
|
+
[ENHANCEMENT_LABEL]: { color: '84b6eb', description: 'Release: a new feature (minor)' },
|
|
240
|
+
[BUGFIX_LABEL]: { color: 'd73a4a', description: 'Release: a bug fix (patch)' },
|
|
241
|
+
[OTHER_CHANGE_LABEL]: { color: 'c2e0c6', description: 'Release: any other change (patch)' },
|
|
242
|
+
[IGNORE_FOR_RELEASE_LABEL]: { color: 'cfd3d7', description: 'Release: left out of the notes' },
|
|
243
|
+
}
|
|
244
|
+
|
|
245
|
+
const RELEASE_CLASSIFICATION_LABELS: ReadonlyArray<LabelDefinition> =
|
|
246
|
+
RELEASE_CLASSIFICATION_NAMES.map((name) => ({ name, ...RELEASE_CLASSIFICATION_METADATA[name] }))
|
|
247
|
+
|
|
248
|
+
// Every label kit provisions on a repository — `josh start` on a new one, `josh sync` on every run.
|
|
249
|
+
const REPOSITORY_LABELS: ReadonlyArray<LabelDefinition> = [
|
|
250
|
+
...WORKFLOW_LABELS,
|
|
251
|
+
...RELEASE_CLASSIFICATION_LABELS,
|
|
252
|
+
]
|
|
253
|
+
|
|
217
254
|
// The shape `gh issue list --json labels` returns; narrowed here so the predicate below takes any
|
|
218
255
|
// listing row without importing a schema.
|
|
219
256
|
interface LabelReference {
|
|
@@ -295,13 +332,18 @@ const ALL_LABELS: ReadonlySet<string> = new Set([
|
|
|
295
332
|
DEPTH_0_LABEL,
|
|
296
333
|
DEPTH_1_LABEL,
|
|
297
334
|
DEPTH_2_LABEL,
|
|
335
|
+
BUGFIX_LABEL,
|
|
336
|
+
OTHER_CHANGE_LABEL,
|
|
337
|
+
IGNORE_FOR_RELEASE_LABEL,
|
|
298
338
|
])
|
|
299
339
|
|
|
340
|
+
export type { LabelDefinition, ReleaseClassification }
|
|
300
341
|
export {
|
|
301
342
|
ALL_LABELS,
|
|
302
343
|
ALREADY_DONE_LABEL,
|
|
303
344
|
AUTO_OK_LABEL,
|
|
304
345
|
BUG_LABEL,
|
|
346
|
+
BUGFIX_LABEL,
|
|
305
347
|
BREAKING_CHANGE_LABEL,
|
|
306
348
|
depth_label_of,
|
|
307
349
|
DEPTH_0_LABEL,
|
|
@@ -314,12 +356,17 @@ export {
|
|
|
314
356
|
FILING_ROUTE_LABELS,
|
|
315
357
|
has_any_label,
|
|
316
358
|
has_label_name,
|
|
359
|
+
IGNORE_FOR_RELEASE_LABEL,
|
|
317
360
|
IN_PROGRESS_LABEL,
|
|
318
361
|
INTERRUPT_ROUTE_LABEL,
|
|
319
362
|
label_name_of,
|
|
320
363
|
NEEDS_DECISION_LABEL,
|
|
321
364
|
NEEDS_HUMAN_REVIEW_LABEL,
|
|
322
365
|
NOT_DIRECTLY_RUNNABLE_LABELS,
|
|
366
|
+
OTHER_CHANGE_LABEL,
|
|
367
|
+
RELEASE_CLASSIFICATION_LABELS,
|
|
368
|
+
RELEASE_CLASSIFICATION_NAMES,
|
|
369
|
+
REPOSITORY_LABELS,
|
|
323
370
|
REVIEW_CAP_ROUTE_LABEL,
|
|
324
371
|
REVIEW_ROUND2_SKIPPED_LABEL,
|
|
325
372
|
RUN_LANE_LABEL,
|
|
@@ -0,0 +1,92 @@
|
|
|
1
|
+
import { existsSync, readFileSync, realpathSync } from 'node:fs'
|
|
2
|
+
import path from 'node:path'
|
|
3
|
+
import { z } from 'zod'
|
|
4
|
+
import { init_install, type InstallStep } from './init-install'
|
|
5
|
+
import { init_logic_workspace } from './init-logic-workspace'
|
|
6
|
+
|
|
7
|
+
const KIT_PACKAGE_NAME = '@joshuafolkken/kit'
|
|
8
|
+
const NODE_MODULES = 'node_modules'
|
|
9
|
+
const PACKAGE_JSON = 'package.json'
|
|
10
|
+
// The static template lists exactly the build scripts kit's own dependency tree carries, which is
|
|
11
|
+
// what installing kit alone needs approved (joshuafolkken/kit#2785).
|
|
12
|
+
const KIT_BUILDS_TEMPLATE = 'templates/pnpm-workspace.static.yaml'
|
|
13
|
+
const HANDOFF_FAILURE = 'josh init from the project-installed kit failed — see the output above'
|
|
14
|
+
// Set on the handed-off run, so a kit that still does not find itself installed in the project — pnpm
|
|
15
|
+
// put it somewhere else — stops instead of installing and handing off again without end.
|
|
16
|
+
const HANDOFF_ENV = 'JOSH_INIT_HANDED_OFF'
|
|
17
|
+
const REPEATED_HANDOFF = `the kit this project installed is not at ${NODE_MODULES}/${KIT_PACKAGE_NAME} — run \`pnpm exec josh init\` where it is`
|
|
18
|
+
|
|
19
|
+
const dependency_fields_schema = z.looseObject({
|
|
20
|
+
dependencies: z.record(z.string(), z.string()).optional(),
|
|
21
|
+
devDependencies: z.record(z.string(), z.string()).optional(),
|
|
22
|
+
})
|
|
23
|
+
|
|
24
|
+
function real_directory(directory: string): string | undefined {
|
|
25
|
+
return existsSync(directory) ? realpathSync(directory) : undefined
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
// A `pnpm dlx` (or global) run executes whatever kit its cache held, which may be days behind the
|
|
29
|
+
// registry — and `init` pins the kit it runs as, so a stale cache used to leave its version and its
|
|
30
|
+
// templates in the project for good (joshuafolkken/kit#2794). Only the kit the project itself installs
|
|
31
|
+
// does the setup. Compared by real path, so a global virtual store shared with the project still
|
|
32
|
+
// reads as the project's own kit.
|
|
33
|
+
function is_project_kit(package_directory: string, project_root: string): boolean {
|
|
34
|
+
const installed = real_directory(path.join(project_root, NODE_MODULES, KIT_PACKAGE_NAME))
|
|
35
|
+
|
|
36
|
+
return installed !== undefined && installed === real_directory(package_directory)
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
function kit_build_flags(template: string): Array<string> {
|
|
40
|
+
return [...init_logic_workspace.template_build_values(template)]
|
|
41
|
+
.filter(([, value]) => value === 'true')
|
|
42
|
+
.map(([name]) => `--allow-build=${name}`)
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
function has_kit_dependency(project_root: string): boolean {
|
|
46
|
+
const manifest_path = path.join(project_root, PACKAGE_JSON)
|
|
47
|
+
if (!existsSync(manifest_path)) return false
|
|
48
|
+
const parsed: unknown = JSON.parse(readFileSync(manifest_path, 'utf8'))
|
|
49
|
+
const manifest = dependency_fields_schema.parse(parsed)
|
|
50
|
+
const names = Object.keys({ ...manifest.dependencies, ...manifest.devDependencies })
|
|
51
|
+
|
|
52
|
+
return names.includes(KIT_PACKAGE_NAME)
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
// A project that already lists kit keeps the version it chose; otherwise pnpm picks it, under the
|
|
56
|
+
// project's own `minimumReleaseAge`, so the version installed is never the cache's decision.
|
|
57
|
+
function kit_install_step(has_kit: boolean, template: string): InstallStep {
|
|
58
|
+
if (has_kit) return { label: 'pnpm install', command: 'pnpm', args: ['install'] }
|
|
59
|
+
const args = ['add', '-D', ...kit_build_flags(template), KIT_PACKAGE_NAME]
|
|
60
|
+
|
|
61
|
+
return { label: `pnpm ${args.join(' ')}`, command: 'pnpm', args }
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
function handoff_step(args: ReadonlyArray<string>): InstallStep {
|
|
65
|
+
const handoff_args = ['exec', 'josh', 'init', ...args]
|
|
66
|
+
|
|
67
|
+
return {
|
|
68
|
+
label: `pnpm ${handoff_args.join(' ')}`,
|
|
69
|
+
command: 'pnpm',
|
|
70
|
+
args: handoff_args,
|
|
71
|
+
env: { [HANDOFF_ENV]: '1' },
|
|
72
|
+
}
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
// Returns the failure to report, or undefined once the project-installed kit finished the setup.
|
|
76
|
+
function hand_off(
|
|
77
|
+
package_directory: string,
|
|
78
|
+
project_root: string,
|
|
79
|
+
args: ReadonlyArray<string>,
|
|
80
|
+
): string | undefined {
|
|
81
|
+
if (process.env[HANDOFF_ENV] !== undefined) return REPEATED_HANDOFF
|
|
82
|
+
console.info('\n🚀 Installing @joshuafolkken/kit into the project, then running its josh init')
|
|
83
|
+
const template = readFileSync(path.join(package_directory, KIT_BUILDS_TEMPLATE), 'utf8')
|
|
84
|
+
const install = kit_install_step(has_kit_dependency(project_root), template)
|
|
85
|
+
if (!init_install.did_step_succeed(install, project_root)) return `${install.label} failed`
|
|
86
|
+
if (!init_install.did_step_succeed(handoff_step(args), project_root)) return HANDOFF_FAILURE
|
|
87
|
+
|
|
88
|
+
return undefined
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
const init_bootstrap = { is_project_kit, kit_build_flags, hand_off }
|
|
92
|
+
export { init_bootstrap }
|