@tyhld/conductor 0.12.49 → 0.12.51

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/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@tyhld/conductor",
3
- "version": "0.12.49",
3
+ "version": "0.12.51",
4
4
  "description": "采配くん管制の見守りアプリ(conductor-agent)。各PCで常駐し、中央(devlog-tracker)へ定期的に生存報告(heartbeat)を送る常駐CLI。",
5
5
  "type": "module",
6
6
  "bin": {
@@ -678,9 +678,13 @@ if [[ ${#missing_guard[@]} -gt 0 ]]; then
678
678
  fi
679
679
 
680
680
  # ★壊れた Python を固定パスへ置くと全現場が同時に止まる。置く前に構文を確認する。
681
- if ! python3 -m py_compile "${GUARD_FILES[@]/#/$GUARD_SRC_DIR/}"; then
681
+ # ★.pyc は配り元へ書かない=一時置き場へ向ける(update-guard.sh ①と同じ作法・便 07c14f64)。
682
+ pyc_tmp="$(mktemp -d)"
683
+ if ! PYTHONPYCACHEPREFIX="$pyc_tmp" python3 -m py_compile "${GUARD_FILES[@]/#/$GUARD_SRC_DIR/}"; then
684
+ rm -rf "$pyc_tmp"
682
685
  fail "番人一式の構文確認に失敗しました。固定パスは変更していません。"
683
686
  fi
687
+ rm -rf "$pyc_tmp"
684
688
 
685
689
  mkdir -p "$GUARD_HOME"
686
690
  for f in "${GUARD_FILES[@]}"; do
@@ -144,12 +144,16 @@ CODEX_NOTIFY_DST="$GUARD_HOME/codex-notify.py"
144
144
  if [[ -f "$CODEX_NOTIFY_SRC" ]]; then
145
145
  if [[ "$APPLY" == "1" ]]; then
146
146
  # 壊れた Python を固定パスへ置かない(番人一式と同じ作法)。
147
- if python3 -m py_compile "$CODEX_NOTIFY_SRC"; then
147
+ # ★.pyc は配り元へ書かない=一時置き場へ向ける(update-guard.sh ①と同じ・便 07c14f64)。
148
+ pyc_tmp="$(mktemp -d)"
149
+ if PYTHONPYCACHEPREFIX="$pyc_tmp" python3 -m py_compile "$CODEX_NOTIFY_SRC"; then
150
+ rm -rf "$pyc_tmp"
148
151
  mkdir -p "$GUARD_HOME"
149
152
  cp -f "$CODEX_NOTIFY_SRC" "$CODEX_NOTIFY_DST"
150
153
  chmod +x "$CODEX_NOTIFY_DST"
151
154
  info "完了の受け口を設置: $CODEX_NOTIFY_DST"
152
155
  else
156
+ rm -rf "$pyc_tmp"
153
157
  warn "完了の受け口の構文確認に失敗しました(設置をスキップ): $CODEX_NOTIFY_SRC"
154
158
  fi
155
159
  else
@@ -126,7 +126,8 @@ dist_record() {
126
126
  dist_notify() {
127
127
  [[ "$APPLY" == "1" ]] || return 0
128
128
  [[ -f "$DIST_NOTIFY" ]] || return 0
129
- python3 "$DIST_NOTIFY" "$DIST_LAST" >/dev/null 2>&1 || true
129
+ # ★配り元の隣の部品を import するので、配り元に .pyc を書かせない(①の構文確認と同じ理由)。
130
+ PYTHONDONTWRITEBYTECODE=1 python3 "$DIST_NOTIFY" "$DIST_LAST" >/dev/null 2>&1 || true
130
131
  }
131
132
 
132
133
  info "①-src 配り元の確認(公開済みの正規版だけを配ります)"
@@ -264,9 +265,15 @@ fi
264
265
  info "① 番人一式を設置: $GUARD_HOME"
265
266
  if [[ "$APPLY" == "1" ]]; then
266
267
  # 置く前に構文を確認する。壊れた Python を固定パスへ置くと全現場が同時に止まるため。
267
- if ! python3 -m py_compile "${INSTALL_FILES[@]/#/$GUARD_SRC_DIR/}"; then
268
+ # ★.pyc は配り元へ書かない=一時置き場へ向ける(PYTHONDONTWRITEBYTECODE は py_compile に効かない)。
269
+ # 配り元の __pycache__ に書くと、同じ配り元を並べて読む者(試験の写し・もう1本の設置)と
270
+ # 作り消しでぶつかり、壊れていないのに「構文確認に失敗」で止まる(便 07c14f64 で再現)。
271
+ pyc_tmp="$(mktemp -d)"
272
+ if ! PYTHONPYCACHEPREFIX="$pyc_tmp" python3 -m py_compile "${INSTALL_FILES[@]/#/$GUARD_SRC_DIR/}"; then
273
+ rm -rf "$pyc_tmp"
268
274
  fail "番人一式の構文確認に失敗しました。固定パスは変更していません。"
269
275
  fi
276
+ rm -rf "$pyc_tmp"
270
277
  mkdir -p "$GUARD_HOME"
271
278
  for f in "${INSTALL_FILES[@]}"; do
272
279
  cp -f "$GUARD_SRC_DIR/$f" "$GUARD_HOME/$f"
@@ -379,7 +386,7 @@ craftsman_backup() {
379
386
  # 知らせる側が控えの status を見て決める=ここに判断を二重に書かない。
380
387
  craftsman_notify() {
381
388
  [[ -f "$CRAFTSMAN_NOTIFY" ]] || return 0
382
- python3 "$CRAFTSMAN_NOTIFY" "$CRAFTSMAN_LAST" >/dev/null 2>&1 || true
389
+ PYTHONDONTWRITEBYTECODE=1 python3 "$CRAFTSMAN_NOTIFY" "$CRAFTSMAN_LAST" >/dev/null 2>&1 || true
383
390
  }
384
391
 
385
392
  # 直った回だけの呼び口。★確認のみ(APPLY!=1)のときは管制の札に触らない。
@@ -44,6 +44,66 @@ description: 采配くんの便(プロンプト末尾に CONDUCTOR_JOB:<id>
44
44
  - ★**合流したかの確かめ方(題名では判定しない・えふさん確定 2026-09-18)。** 合流完了の判定はコミット題名では行わない。GitHub上で対象PRが MERGED であること、merge先が意図したbase branchであること、merge後のbase branchに対象PRの変更内容が含まれることを確認する。(squash merge / merge commit / rebase merge の方式に依存しない確認にする)
45
45
  - ★これは共通正本 `claude-code-operations.md §11.3` と、この鉄則7 の2か所を正本にする(各リポの CLAUDE.md へは複製しない)。
46
46
  - 確かめ方の例(値は実際の番号・ブランチ名に置き換える):`gh pr view 番号 --json state,baseRefName,mergeCommit` で、`state` が `MERGED`、`baseRefName` が意図した base branch(ふつうは `main`)であることを見る。`git fetch origin` のあと `git merge-base --is-ancestor mergeCommitのoid origin/base` が成功する(=合流でできたコミットが base に含まれる)ことを見る。`gh pr diff 番号` の変更が、`git diff` や `git show origin/base:ファイル` で base branch に入っていることを、変更したファイルごとに見比べる。
47
+ - ★**上の4つ(保護の確認・CI・合流・合流後の確かめ)を1回で流す形**(便 03bef270)。★**確かめは1つも省いていない**:段の名前(`protect` / `ci` / `merge` / `verify`)付きで結果を出し、**落ちた段で止まる**。`--admin` は使わない。確かめを減らしたいときにこの塊を削らない。
48
+ そのままコピーして、1回の実行として流す(`PR=<PR番号>`)。手で1つずつ打つ従来の手順も、そのまま有効。
49
+ ```bash
50
+ #!/usr/bin/env bash
51
+ # 鉄則7 の4つの確かめ(保護 → CI → 合流 → 合流後)を1回で流す。確かめは1つも省かない。
52
+ # 使い方: PR=<PR番号> bash <この塊> 落ちた段は NG[段名] で分かる。★--admin は使わない。
53
+ set -uo pipefail
54
+ PR="${PR:?PR=<PR番号> が要る}"
55
+ ng() { printf 'NG[%s] %s\n' "$1" "$2" >&2; exit 1; }
56
+ ok() { printf 'OK[%s] %s\n' "$1" "$2"; }
57
+ V() { gh pr view "$PR" --json "$1" --jq "$2" 2>/dev/null; }
58
+
59
+ # ① 保護 … 必須の検査が1件以上のときだけ「保護あり」。0・404・403・失敗は「保護なし」へ倒す。
60
+ BASE="$(V baseRefName .baseRefName)"; [ -n "$BASE" ] || ng protect '合流先を読めない'
61
+ SLUG="$(gh repo view --json nameWithOwner --jq .nameWithOwner 2>/dev/null)"
62
+ [ -n "$SLUG" ] || ng protect 'リポジトリを読めない'
63
+ N="$(gh api "repos/$SLUG/branches/$BASE/protection/required_status_checks" --jq '.contexts | length' 2>/dev/null)"
64
+ case "$N" in ''|*[!0-9]*) N=0 ;; esac
65
+ if [ "$N" -ge 1 ]; then PROT=yes; else PROT=no; fi
66
+ ok protect "base=$BASE 保護=$PROT(必須の検査 $N 件)"
67
+
68
+ # ② CI … リンクが /actions/runs/ の行だけを CI と数える(Vercel は数えない)。
69
+ ROLL="$(V statusCheckRollup '[.statusCheckRollup[]? | {u:(.detailsUrl//.targetUrl//""), s:(.status//""), c:((.conclusion//.state//"")|ascii_upcase)} | select(.u|test("/actions/runs/"))]')"
70
+ [ -n "$ROLL" ] || ng ci 'CI の一覧を読めない'
71
+ read -r TOTAL RUNNING RED NOTPASS <<<"$(printf '%s' "$ROLL" | jq -r '[length,
72
+ ([.[]|select(.s!="" and .s!="COMPLETED")]|length),
73
+ ([.[]|select(.s=="COMPLETED" or .s=="")|select(.c|test("^(SUCCESS|NEUTRAL|SKIPPED)$")|not)]|length),
74
+ ([.[]|select(.c|test("^(SUCCESS|NEUTRAL|SKIPPED)$")|not)]|length)] | @tsv')"
75
+ [ "${TOTAL:-0}" -ge 1 ] || ng ci 'このリポに CI が無い(鉄則7-④: 手元で検査を流し、人の判断で合流する)'
76
+ [ "$RED" -eq 0 ] || ng ci "CI が赤($RED 件)"
77
+ if [ "$PROT" = no ]; then
78
+ [ "$RUNNING" -eq 0 ] || ng ci "CI が実行中($RUNNING 件・終わってから確かめ直す)"
79
+ [ "$NOTPASS" -eq 0 ] || ng ci "pass でない CI がある($NOTPASS 件)"
80
+ fi
81
+ ok ci "CI $TOTAL 件・赤 0 件(保護=$PROT)"
82
+
83
+ # ③ 合流 … 保護あり=予約(緑になり次第 GitHub が合流)/保護なし=緑を確かめたうえで合流。
84
+ if [ "$PROT" = yes ]; then
85
+ gh pr merge "$PR" --auto --merge --delete-branch || ng merge '合流の予約に失敗(鉄則7-② で確かめ直す。旗を足さない)'
86
+ ok merge '保護あり=合流を予約した'
87
+ else
88
+ gh pr merge "$PR" --merge --delete-branch || ng merge '合流に失敗'
89
+ ok merge '保護なし=CI の緑を確かめてから合流した'
90
+ fi
91
+
92
+ # ④ 合流後 … MERGED・base が意図どおり・変更が base に入っている(題名では判定しない)。
93
+ END=$(( $(date +%s) + ${WAIT:-600} ))
94
+ while :; do
95
+ ST="$(V state .state)"
96
+ [ "$ST" = MERGED ] && break
97
+ [ "$(date +%s)" -lt "$END" ] || ng verify "まだ MERGED でない(state=$ST)"
98
+ sleep 15
99
+ done
100
+ GOT="$(V baseRefName .baseRefName)"
101
+ [ "$GOT" = "$BASE" ] || ng verify "合流先が違う($GOT ≠ $BASE)"
102
+ OID="$(V mergeCommit .mergeCommit.oid)"; [ -n "$OID" ] || ng verify '合流コミットを読めない'
103
+ git fetch origin --quiet || ng verify 'fetch に失敗'
104
+ git merge-base --is-ancestor "$OID" "origin/$BASE" || ng verify "変更が origin/$BASE に入っていない"
105
+ ok verify "MERGED / base=$BASE / $OID が origin/$BASE に入っている"
106
+ ```
47
107
  8. **ブラウザ検品(Playwright)の作法** — ★取りに行かない。PCに1回だけ入れた共有の道具を指すだけ(ADR-010)。
48
108
  - ① **起動は必ず `chromium_sandbox=False`**(`--no-sandbox`)。付けないと起動できない。
49
109
  - ② **道具は共有の場所を使う。自分でダウンロードしない。** python は `$HOME/.tyhld/pw-venv/bin/python`、`PLAYWRIGHT_BROWSERS_PATH` は `$HOME/.tyhld/pw-browsers`。**無ければそこで止めて「人の手番=`APPLY=1 bash scripts/pw-browser-install.sh`」と報告する。** `playwright install` も `pip install playwright` も職人は実行しない(毎便まるごとダウンロードが走り、便が遅くなる。通信自体は鉄則12のとおり許可済みだが、取りに行かないのが本筋=ADR-010)。
@@ -69,6 +129,15 @@ description: 采配くんの便(プロンプト末尾に CONDUCTOR_JOB:<id>
69
129
  - **雛形(`.env.example` など)を直すときの正しい形=「案を作る→人が1回で反映」。** 職人は変更後の中身を**リポジトリの外(`/tmp/claude-1000/` の下)**に作り、報告に次の2つを載せる。① 変更の**差分**(何を足す/消すか)。② 人が**素のターミナルで1回貼るだけで反映できる1つのコマンド**(例:`cp /tmp/claude-1000/<下書き> <リポ>/.env.example`)。★このコマンドは**人が自分のターミナルで**打つ(職人の番人は人の端末には掛からない)。職人が `.env.example` を直接書き換えようとしても番人が止めるので、**回避せず、この形で案を出す**。
70
130
  - **本物の `.env`・`.env.local` は、これまでどおり中身も書き込みも扱わない**(秘密の値は1文字も読まない・書かない=鉄則12の金庫)。
71
131
 
132
+ ## 番人に止められる常連(こう書くと止まる → 代わりにこう書く)
133
+
134
+ 止まる書き方を最初から使わなければ、打ち直しの往復が要らない。★番人の判定は変えない(弱めない)。
135
+
136
+ - **cd を `&&` でつなぐ**(`cd 先 && 何か`・cd とリダイレクトを同じ行に混ぜる)→ 止まる。**cd は作業のはじめに1回だけ単独で**打ち、以後は絶対パスか `git -C <実際のパス>`(鉄則2)。
137
+ - **`.npmrc` を読む**(`cat ~/.npmrc` など)→ 止まる。**中身は見に行かない。** 差分は対象を名指しで `git diff -- src docs`、全体で見たいときは除いて `git diff -- . ':(exclude).npmrc'`(鉄則1)。
138
+ - **中身が変わる場所を指す再帰の `rm`**(`rm -rf "$DIR/x"`・`rm -rf build/*` のように変数や `*` を含む)→ 止まる。**消す先を実在のパスで名指しする**(例 `rm -rf /tmp/claude-1000/作業名`・鉄則13)。
139
+ - **作業場所が変数のままの `git push`**(`git -C "$DIR" push`・行き先のブランチを確かめられない形)→ 止まる。**リポの中で、ブランチを名指しして** `git push -u origin <作業ブランチ名>`。
140
+
72
141
  ## 便の終え方
73
142
 
74
143
  便の最後には、配達役が付けた1行「【便の終え方】…報告の置き場: ~/最終行: DONE:<現場> とだけ出力/完了の知らせ: conductor_complete を commandId="<指示ID>" で1回だけ」と、管理用ジョブ印 `CONDUCTOR_JOB:<指示ID>` がある。作業がすべて終わったら、この節のとおりに次の順で終える(便 a5306df2:以前は便ごとに付いていた全文を、ここへ寄せた)。