@tyhld/conductor 0.12.85 → 0.12.87

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.
@@ -110,6 +110,11 @@ REINSTALL = ('npm install -g @tyhld/conductor '
110
110
  RESYNC = 'APPLY=1 bash "$(npm root -g)/@tyhld/conductor/scripts/update-guard.sh"'
111
111
 
112
112
 
113
+ def autoupdate_enabled():
114
+ """自動更新が入っている PC か(自社PCの鍵 ~/.tyhld/bin/autoupdate/enabled・便 29babc5d)。"""
115
+ return os.path.isfile(os.path.join(os.path.expanduser('~'), '.tyhld', 'bin', 'autoupdate', 'enabled'))
116
+
117
+
113
118
  def recovery_steps(reason):
114
119
  """うまくいかなかった理由に【合った】手順を返す(便 675e2d05)。
115
120
 
@@ -118,7 +123,20 @@ def recovery_steps(reason):
118
123
  (入っている版と公開の版が違う)のときにこれを打っても、配り元の確認でまた弾かれる=
119
124
  同じ失敗を繰り返すだけで直らない。先に入れ直しが要る。
120
125
  """
121
- if reason in ('version-drift', 'not-installed', 'incomplete'):
126
+ if reason in ('flagged-bad', 'flagged-suspect'):
127
+ # ★管制で不具合・疑いの印が付いた版(便 29babc5d)。入れ直しても同じ版なら直らない。
128
+ if autoupdate_enabled():
129
+ return ['再開のしかた: この PC は自動更新が入っています。管制が直した版を決めれば、'
130
+ '次の自動更新で入れ替わります(人の操作は要りません)。']
131
+ return ['再開のしかた: この版には管制で不具合の印が付いています。CC1 に知らせてください'
132
+ f'(直した版が公開されたら {REINSTALL})。']
133
+ if reason in ('unpublished', 'deprecated', 'version-drift', 'not-installed', 'incomplete'):
134
+ # ★自動更新が入っている PC は、更新係が管制の決めた版へそろえる(便 29babc5d)。
135
+ # 人が npm で入れると段階配布の順番が崩れるので、入れ直しを促さない。
136
+ # ★not-installed・incomplete は配布物そのものが無い=更新係も動けないので、自動更新の有無によらず入れ直す。
137
+ if autoupdate_enabled() and reason in ('unpublished', 'deprecated', 'version-drift'):
138
+ return ['再開のしかた: この PC は自動更新が入っています。次の自動更新(管制が決めた版を、'
139
+ '作業中の便が終わってから入れます)でそろいます。管制の「PCの状態」で確かめてください。']
122
140
  return [f'再開のしかた: この PC で次の順に打ってください。① {REINSTALL}',
123
141
  f' ② {RESYNC}']
124
142
  if reason == 'no-npm':
@@ -1912,6 +1912,70 @@ finally:
1912
1912
  else:
1913
1913
  os.environ['CONDUCTOR_WORK_DIR'] = _wd_env2
1914
1914
 
1915
+ # ─────────────────────────────────────────────────────────────────────────────
1916
+ # 検索の「探す言葉」の中の削除命令(便 915694ac・えふさん確定 2026-10-02)
1917
+ #
1918
+ # 【実物】職人が鉄則や台本を調べるとき、探す言葉に「rm -rf .git」のような字が入る。
1919
+ # 番人はそれを削除命令として読み、調べる作業を毎回止めていた。
1920
+ # 【直し】探すだけの道具(grep 系・rg 系・git grep)の【引用符の中】に在る字は、
1921
+ # 削除命令として数えない。囲みの外に在る字は1件も免じない。
1922
+ # ★安全は1件も緩めない=下の「止まり続ける形」をすべて固定する。
1923
+ # ─────────────────────────────────────────────────────────────────────────────
1924
+ print('\n=== 探す言葉の中の削除命令は、削除命令として読まない ===')
1925
+ _SEARCH_OK = [
1926
+ "grep -rn 'rm ' skills docs",
1927
+ 'grep -rn "rm -rf" skills',
1928
+ "grep -n 'rm -rf .git' skills/conductor-craftsman/SKILL.md",
1929
+ "grep -rn 'rm -r /tmp/claude-1000/sagyou' skills",
1930
+ "grep -rln 'rm -r ~' docs",
1931
+ 'grep -rn "rm -rf $HOME" docs',
1932
+ "git grep -n 'rm -rf .git' -- skills docs",
1933
+ "rg -n 'rm -rf /' docs",
1934
+ "rg 'rm -rf .tyhld' skills",
1935
+ "grep -rn '-f なしの rm -r' skills",
1936
+ ]
1937
+ for _c in _SEARCH_OK:
1938
+ check(f'探す言葉: {_c}', policy.decide_bash(_c)['decision'], policy.ALLOW)
1939
+
1940
+ print('\n=== 探す言葉に見せても、本当に消す形は止まり続ける ===')
1941
+ # ★この一覧はえふさん確定(2026-10-02)の「今までどおり止まること」の全件。
1942
+ _STILL_DENY = [
1943
+ # 包みの中(bash -c・sh -c・eval)
1944
+ "bash -c 'rm -rf .git'",
1945
+ 'sh -c "rm -rf .git"',
1946
+ "eval 'rm -rf .git'",
1947
+ 'bash -c "grep -rn \'x\' docs; rm -rf .git"',
1948
+ # 命令の置き換え($( ) と ` `)
1949
+ '$(rm -rf .git)',
1950
+ 'echo $(rm -rf .git)',
1951
+ 'X=$(rm -rf .git)',
1952
+ '`rm -rf .git`',
1953
+ 'echo `rm -rf .git`',
1954
+ '$(rm -r ~)',
1955
+ # ; && | で続けた後ろ
1956
+ 'echo x; rm -rf .git',
1957
+ "grep -rn 'x' docs && rm -rf .git",
1958
+ "grep -rn 'x' docs | rm -rf .git",
1959
+ "grep -rn 'rm -rf' docs; rm -r .tyhld",
1960
+ # 探す命令に見せかけて後ろで消す形
1961
+ "grep -rn 'rm -rf .git' docs; rm -rf .git",
1962
+ "grep -rn 'rm -rf' skills && rm -r ~",
1963
+ # xargs・find の -exec(消す先が字面に出る形)
1964
+ 'xargs rm -rf .git',
1965
+ "find . -name '*.tmp' -exec rm -rf .git {} +",
1966
+ 'find docs -type f -exec rm -r {} +',
1967
+ # 変数・* を含む再帰削除(今までどおり)
1968
+ 'rm -rf $DIR/x',
1969
+ 'rm -rf build/*',
1970
+ ]
1971
+ for _c in _STILL_DENY:
1972
+ check(f'止まり続ける: {_c}', policy.decide_bash(_c)['decision'], policy.DENY)
1973
+
1974
+ print('\n=== -f なしの実在パスの削除は今までどおり通る ===')
1975
+ for _c in ['rm -r /tmp/claude-1000/sagyou', 'rm /tmp/claude-1000/a.html',
1976
+ 'rm -rf node_modules', 'rm -rf dist']:
1977
+ check(f'通る: {_c}', policy.decide_bash(_c)['decision'], policy.ALLOW)
1978
+
1915
1979
  # ─────────────────────────────────────────────────────────────────────────────
1916
1980
  print('\n' + '=' * 72)
1917
1981
  if failures:
@@ -322,6 +322,32 @@ with tempfile.TemporaryDirectory() as tmp:
322
322
  check_true(f'{label}: 入っている配布物の台本を打たせる', 'npm root -g' in b)
323
323
  check(f'{label}: 作業ツリーへ cd させない', 'cd ~/projects' in b, False)
324
324
 
325
+ # ★便 29babc5d: 番人を配る条件が「公開されている正規の版で、管制で不具合の印が無い版」になった。
326
+ unpub = body_for('unpublished')
327
+ check_true('公開npmに無い版: 入れ直しから案内する', '① npm install -g @tyhld/conductor' in unpub)
328
+ check_true('使わないでの印: 入れ直しから案内する', '① npm install -g' in body_for('deprecated'))
329
+ bad = body_for('flagged-bad')
330
+ check_true('管制で不具合の印: CC1 に知らせる', 'CC1 に知らせて' in bad)
331
+ check('管制で不具合の印: 配り直しだけを促さない(同じ版なら直らない)', '② APPLY=1 bash' in bad, False)
332
+
333
+ # ★自動更新が入っている PC(鍵 ~/.tyhld/bin/autoupdate/enabled)は、人に npm で入れさせない
334
+ # (更新係が管制の決めた版へそろえる。人が入れると段階配布の順番が崩れる)。
335
+ old_home = os.environ.get('HOME')
336
+ os.environ['HOME'] = tmp
337
+ try:
338
+ os.makedirs(os.path.join(tmp, '.tyhld', 'bin', 'autoupdate'), exist_ok=True)
339
+ write(os.path.join(tmp, '.tyhld', 'bin', 'autoupdate'), 'enabled', 'x\n')
340
+ auto = body_for('unpublished')
341
+ check_true('自動更新の PC: 次の自動更新でそろうと書く', '次の自動更新' in auto)
342
+ check('自動更新の PC: npm で入れさせない', 'npm install -g' in auto, False)
343
+ check_true('自動更新の PC・不具合の印: 人の操作は要らないと書く', '人の操作は要りません' in body_for('flagged-suspect'))
344
+ check_true('自動更新の PC でも、配布物が無いときは入れ直す', 'npm install -g' in body_for('not-installed'))
345
+ finally:
346
+ if old_home is None:
347
+ os.environ.pop('HOME', None)
348
+ else:
349
+ os.environ['HOME'] = old_home
350
+
325
351
  # ── ★札の覚え書き(壊れていても・書けなくても止まらない)──────────────────
326
352
  print('\n【★札の覚え書き】')
327
353
  with tempfile.TemporaryDirectory() as tmp:
@@ -0,0 +1,61 @@
1
+ #!/usr/bin/env bash
2
+ # tyhld-autoupdate — 自社PCの更新係を1回流す入口(便 29babc5d・設計 3ed9e2b2 の A3)。
3
+ #
4
+ # 【置き場】~/.tyhld/bin/tyhld-autoupdate(★職人は書けない場所)。隣に dist-source.sh が要る
5
+ # (常駐の入口 conductor-run と同じ置き場・同じ探し方)。置くのは自社PCの導入台本
6
+ # scripts/autoupdate-install.sh だけ。★お客さまの機の導入台本(sales-template/install.sh・setup.sh)は置かない。
7
+ #
8
+ # 【使い方】systemd から呼ぶ(人が打つ物ではない)。
9
+ # tyhld-autoupdate-boot.service … ~/.tyhld/bin/tyhld-autoupdate --boot(起動時に1回・常駐より先)
10
+ # tyhld-autoupdate.service … ~/.tyhld/bin/tyhld-autoupdate(タイマーから数分ごと。聞きに行くのは管制が返す間隔ごと)
11
+ # 終了コード: 0=いつも(更新しなかった・失敗した も 0。理由は journal と控えに残る)/2=使い方の誤り/
12
+ # 78=動かす采配くんが見つからない
13
+ #
14
+ # 【決め】
15
+ # ・鍵(~/.tyhld/bin/autoupdate/enabled)が無い機では、何も読まずに終わる(お客さまの機で動かない)。
16
+ # ・同時に2つ動かさない(起動時の1回とタイマーが重なっても、後の方は何もせず終わる)。
17
+ # ・動かすのは常駐と同じ「公開済みの配布物」(作業ツリーは動かさない=dist_runtime_resolve)。
18
+ # ・接続設定は systemd が EnvironmentFile で渡す。無いときだけ ~/.conductor.env から
19
+ # CONDUCTOR_URL・CONDUCTOR_TOKEN・CONDUCTOR_MACHINE の3つを読む(★値は画面に出さない)。
20
+ set -u
21
+
22
+ KEY="${HOME:-}/.tyhld/bin/autoupdate/enabled"
23
+ if [[ ! -f "$KEY" ]]; then
24
+ echo "[tyhld-autoupdate] 自動更新の鍵がありません($KEY)。この機では何もしません。"
25
+ exit 0
26
+ fi
27
+
28
+ HERE="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
29
+ . "$HERE/dist-source.sh"
30
+
31
+ if ! dist_runtime_resolve; then
32
+ echo "[tyhld-autoupdate] 流せません: 動かす采配くんを確かめられません(${DIST_REASON})。" >&2
33
+ echo "[tyhld-autoupdate] 人の手番: npm install -g @tyhld/conductor --@tyhld:registry=https://registry.npmjs.org" >&2
34
+ exit 78
35
+ fi
36
+
37
+ ENV_FILE="${HOME:-}/.conductor.env"
38
+ if [[ ( -z "${CONDUCTOR_URL:-}" || -z "${CONDUCTOR_TOKEN:-}" ) && -r "$ENV_FILE" ]]; then
39
+ while IFS= read -r line || [[ -n "$line" ]]; do
40
+ line="${line#export }"
41
+ case "$line" in
42
+ CONDUCTOR_URL=*|CONDUCTOR_TOKEN=*|CONDUCTOR_MACHINE=*)
43
+ key="${line%%=*}"; val="${line#*=}"
44
+ val="${val%\"}"; val="${val#\"}"; val="${val%\'}"; val="${val#\'}"
45
+ [[ -n "${!key:-}" ]] || export "$key=$val"
46
+ ;;
47
+ esac
48
+ done < "$ENV_FILE"
49
+ fi
50
+
51
+ # ★同時に2つ動かさない(flock が無い機では、そのまま1つとして動かす)。
52
+ LOCK="${HOME:-}/.tyhld/bin/autoupdate/lock"
53
+ if command -v flock >/dev/null 2>&1; then
54
+ exec 9>"$LOCK" || exit 0
55
+ if ! flock -n 9; then
56
+ echo "[tyhld-autoupdate] ほかの更新係が動いているので、今回は何もしません。"
57
+ exit 0
58
+ fi
59
+ fi
60
+
61
+ "$DIST_NODE" "$DIST_ROOT/dist/cli.js" autoupdate "$@"
@@ -80,7 +80,7 @@ info() { echo "[update-guard] $*"; }
80
80
  warn() { echo "[update-guard] 注意: $*" >&2; }
81
81
  fail() { echo "[update-guard] エラー: $*" >&2; exit 1; }
82
82
 
83
- # >>> preflight >>> ★この印の間が正本。install.sh と update-guard.sh で1文字も違わないことをテストが固定する。
83
+ # >>> preflight >>> ★この印の間が正本。install.sh・update-guard.sh・setup.sh の3つで1文字も違わないことをテストが固定する。
84
84
  # ═════════════════════════════════════════════════════════════════════════════
85
85
  # 先頭の確かめ(職人が始まるのに要る物・便 ff8f4be9)
86
86
  #
@@ -125,6 +125,14 @@ preflight_ver_lt() {
125
125
  return 1
126
126
  }
127
127
 
128
+ # ホームの外の置き場(★src/headless.ts の CLAUDE_SYSTEM_BIN_DIRS と同じ・テストで一致を固定)。
129
+ # ★検査だけが作り物のフォルダに差し替える(CONDUCTOR_PREFLIGHT_SYSTEM_DIRS)。
130
+ # HOME を隔離してもここは絶対パスなので、機体に claude が在ると検査が機体に依って落ちる
131
+ # (実測 2026-10-02 メインPC: /usr/bin/claude = 2.1.86)。中継の側は便 ff8f4be9 で
132
+ # 同じ差し替え(resolveClaudeBin(systemDirs))を入れてある。作りをそろえる。
133
+ # ★差し替えても安全は変わらない(探す場所が変わるだけ・見つからなければ need では止まる)。
134
+ PREFLIGHT_SYSTEM_BIN_DIRS="${CONDUCTOR_PREFLIGHT_SYSTEM_DIRS-/usr/local/bin:/usr/bin}"
135
+
128
136
  # 中継と同じ候補を同じ順で出す(1行に1つ)。
129
137
  preflight_claude_candidates() {
130
138
  if [[ -n "${CONDUCTOR_CLAUDE_BIN:-}" ]]; then
@@ -133,6 +141,7 @@ preflight_claude_candidates() {
133
141
  fi
134
142
  local d
135
143
  local -a nv=()
144
+ local -a sysdirs=()
136
145
  for d in "$HOME/.nvm/versions/node"/v*; do
137
146
  [[ -d "$d" ]] && nv+=("${d##*/}")
138
147
  done
@@ -142,7 +151,12 @@ preflight_claude_candidates() {
142
151
  printf '%s\n' "$HOME/.nvm/versions/node/$d/bin/claude"
143
152
  done < <(printf '%s\n' "${nv[@]}" | sort -t. -k1.2,1nr -k2,2nr -k3,3nr)
144
153
  fi
145
- printf '%s\n' "$HOME/.local/bin/claude" "$HOME/.claude/local/claude" /usr/local/bin/claude /usr/bin/claude
154
+ printf '%s\n' "$HOME/.local/bin/claude" "$HOME/.claude/local/claude"
155
+ IFS=':' read -r -a sysdirs <<< "$PREFLIGHT_SYSTEM_BIN_DIRS"
156
+ for d in ${sysdirs[@]+"${sysdirs[@]}"}; do
157
+ [[ -n "$d" ]] && printf '%s\n' "$d/claude"
158
+ done
159
+ return 0
146
160
  }
147
161
 
148
162
  # その claude の入れ方に合う更新の1行(src/headless.ts の claudeUpdateHint と同じ見分け方)。
@@ -308,8 +322,10 @@ fi
308
322
  # 「止まる」を採ると、今までどおり打ったのに何も起きない形になり、手順を覚え直すことになる。
309
323
  #
310
324
  # 【正規版でないときは黙って進まない】
311
- # 入っていない/版が読めない/公開 npm に聞けない/版が公開 npm の今の版と違う のどれでも、
312
- # ★1バイトも配らずに終わる。失敗は控えに残し、便 29612ff1 で作った知らせ方(確認カード)に載せる。
325
+ # 入っていない/版が読めない/公開 npm に聞けない/版が公開 npm に無い・使わないでの印がある/
326
+ # 管制で不具合(bad)・疑い(suspect)の印がある のどれでも、★1バイトも配らずに終わる。
327
+ # ★公開の最新より古くても、公開 npm に在る正規の版で印が無ければ配る(便 29babc5d・段階配布のため・
328
+ # えふさん確定 2026-10-02 夜)。管制に届かないときは「印が無い」として配る(配らないほうが危ない)。失敗は控えに残し、便 29612ff1 で作った知らせ方(確認カード)に載せる。
313
329
  . "$SCRIPT_DIR/dist-source.sh"
314
330
 
315
331
  DIST_STATE="$GUARD_HOME/dist-source.state" # 最後に【確かめられた】配り元
@@ -349,9 +365,10 @@ if ! dist_source_resolve; then
349
365
  no-version) warn " 入っている配布物の版が読めません。" ;;
350
366
  no-npm) warn " 公開 npm に問い合わせる npm が見つかりません(配布物を動かす node の隣にも、PATH にもありません)。" ;;
351
367
  unverifiable) warn " 公開 npm への問い合わせに失敗しました(npm: ${DIST_NPM:-不明}。回線・取得先・npm の不調のどれか)。" ;;
352
- version-drift) warn " 入っている版 ${DIST_VERSION} が、公開 npm のいまの版 ${DIST_PUBLISHED} と違います。" ;;
368
+ unpublished) warn " 入っている版 ${DIST_VERSION} は公開 npm にありません(正規の版ではありません。公開の最新は ${DIST_PUBLISHED})。" ;;
369
+ deprecated) warn " 入っている版 ${DIST_VERSION} には、公開 npm で「使わないで」の印が付いています(${DIST_DEPRECATED})。" ;;
353
370
  esac
354
- warn " 人の手番: npm install -g @tyhld/conductor --@tyhld:registry=https://registry.npmjs.org"
371
+ while IFS= read -r l; do warn " $l"; done < <(dist_human_step "$DIST_REASON")
355
372
  dist_record failed "$DIST_REASON"
356
373
  dist_notify
357
374
  exit 0
@@ -363,7 +380,12 @@ if ! dist_source_complete "$DIST_ROOT"; then
363
380
  dist_notify
364
381
  exit 0
365
382
  fi
366
- info " 配り元: $DIST_ROOT(版 $DIST_VERSION =公開 npm のいまの版)"
383
+ if [[ "$DIST_VERSION" == "$DIST_PUBLISHED" ]]; then
384
+ info " 配り元: $DIST_ROOT(版 $DIST_VERSION =公開 npm のいまの版・管制の印 ${DIST_CENTRAL_FLAG:-unknown})"
385
+ else
386
+ # ★段階配布のあいだ、後続の機は公開の最新より古い正規の版で動く(便 29babc5d・えふさん確定 2026-10-02 夜)。
387
+ info " 配り元: $DIST_ROOT(版 $DIST_VERSION =公開 npm に在る正規の版・公開の最新は $DIST_PUBLISHED・管制の印 ${DIST_CENTRAL_FLAG:-unknown})"
388
+ fi
367
389
  if [[ "$DIST_ROOT" != "$REPO_ROOT" ]]; then
368
390
  info " ★この台本の置き場($REPO_ROOT)からは配りません。"
369
391
  fi
@@ -109,7 +109,7 @@ description: 采配くんの便(プロンプト末尾に CONDUCTOR_JOB:<id>
109
109
  - ① **起動は必ず `chromium_sandbox=False`**(`--no-sandbox`)。付けないと起動できない。
110
110
  - ② **道具は共有の場所を使う。自分でダウンロードしない。** 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)。
111
111
  - ③ **localhost の画面検品は proxy を指定しない。** プロキシ `localhost:3128` はサンドボックスの中にしか居ないので、外部URLへ出るときだけ Playwright の proxy オプションへ明示指定する(環境変数は拾われない)。`ECONNREFUSED` なら回避せず**未実施として報告**する。
112
- - ④ 検品用の使い捨て画面(確認だけのHTML/スクリプト)は**リポジトリの中に作らない。置き場は `/tmp/claude-1000/`**(鉄則13)。リポジトリの中に作ってしまったものはコミット前に削除する。ただしリポジトリ直下は番人が `rm` を止めるので、**自分で消そうとせず報告する**(鉄則13)。
112
+ - ④ 検品用の使い捨て画面(確認だけのHTML/スクリプト)は**リポジトリの中に作らない。置き場は `/tmp/claude-1000/`**(鉄則13)。リポジトリの中に作ってしまったものはコミット前に削除する。**自分で作った物に限り、`-f` を付けない `rm -r` と実在のパスの名指しで消し、消した物を報告に書く**(鉄則13)。★止められたら別の消し方を探さず報告する。
113
113
  9. **pnpm は取りに行かない・版を指定しない(ADR-015)** — ★`npx --yes pnpm@10 …` と打たない(毎回ダウンロードが走る)。`pnpm` とだけ打てば、そのリポジトリの `packageManager` の版が corepack で自動的に使われる。
114
114
  - ★**必ずリポジトリの中で打つ。** 版の指定はディレクトリごとに効くので、リポジトリの外(ホーム等)から `--filter` で呼ぶと既定の新しい版が動き `ERR_PNPM_UNSUPPORTED_ENGINE` で弾かれる(実測)。
115
115
  - `pnpm` が見つからない/版が合わないときは、回避せず**人の手番=`corepack enable`** と報告する。`npm i -g pnpm` は打たない(corepack と衝突する)。
@@ -120,10 +120,17 @@ description: 采配くんの便(プロンプト末尾に CONDUCTOR_JOB:<id>
120
120
  - **電話は自由。** 外への通信(npm / GitHub / API / ドキュメント取得など)は許可済みなので、`Network request outside of sandbox` は出ない。**通信のために止まらない・人を待たない**。最後まで自走して完了まで行く。
121
121
  - **金庫は禁止。** 秘密の持ち出し(`curl -d @.env` / `cp .env` / `tar ~/.ssh` / `base64 .env` / `echo $DATABASE_URL` など)は**番人が今までどおり止める**。通信が自由になったこととは**別の仕組み**なので、「通信が許可されたから秘密も送れる」は成り立たない。本番DB・secret・`.env`・`hooks`・`.tyhld`・`.ssh` への書き込み禁止もそのまま。
122
122
  - **それでも塞がれたら回避しない。** `curl: (56) CONNECT tunnel failed, response 403` のように通信が塞がれたときは、**意図的に閉じてある接続先**(例: 本番DBへの直結)か、設定がその機体にまだ届いていないかのどちらか。**別経路を探さず、そこで止めて報告する**(人の手番=`APPLY=1 bash scripts/update-guard.sh` → 職人を再起動)。
123
- 13. **使い捨てファイルはリポジトリの中に作らない(えふさん決定 2026-09-03)**
124
- - **実機検品・検証用の使い捨てファイル(HTML・画像・ログ等)は、リポジトリの中に作らない。`/tmp/claude-1000/` に置く。リポジトリ直下に作ってしまった場合は自分で消そうとせず、報告に「残骸あり・場所」と書く。**
125
- - **なぜ。** 実機 2026-09-03: ai-saku の画面便(PR#258・PR#269)で、検品用の使い捨てページ `_layout-check.html` をリポジトリ直下に作り、消そうとして番人(`rm` on dangerous path)に止められ、人が2回 `rm` した。**番人の `rm` 制限は正しい(緩めない)。悪いのは置き場。**
126
- - 置き場を `/tmp/claude-1000/` にすれば、番人に止められることも、残骸がコミットに混ざることも起きない(根治=鉄則10)。Playwright の検品ページも同じ(鉄則8-④)。
123
+ 13. **使い捨てファイルはリポジトリの中に作らない(えふさん決定 2026-09-03/えふさん確定 2026-10-02・判断6 で後片付けの形を改めた)**
124
+ - **実機検品・検証用の使い捨てファイル(HTML・画像・ログ等)は、リポジトリの中に作らない。`/tmp/claude-1000/` に置く。**
125
+ - **リポジトリの中(直下を含む)に作ってしまったときの後片付け(えふさん確定 2026-10-02・判断6)。自分で作った物に限り、`-f` を付けない `rm -r` と実在のパスの名指しで消し、消した物を報告に書く。**
126
+ - 通る例: `rm -r ./_layout-check.html` / `rm -r node_modules/.tmp-作業名`(★実在のパスを名指し・`-f` を付けない)
127
+ - ★**はじめから在った物には触らない。** 自分が作った物かどうか分からなければ消さず、報告に「残骸あり・場所」と書く。
128
+ - ★**`.git`・`.claude/hooks`・`.tyhld` は、相対パスでも削除禁止**(番人が止める。緩めない)。
129
+ - ★**`-f` 付きの再帰削除(`rm -rf` 等)・変数や `*` を含む形は止まる**(禁止の一覧と番人)。
130
+ - ★**止められたら別の消し方(`find -delete`・`python -c` 等)を探さず、消せなかった場所を報告に書く**(鉄則4)。
131
+ - **なぜ(置き場の話)。** 実機 2026-09-03: ai-saku の画面便(PR#258・PR#269)で、検品用の使い捨てページ `_layout-check.html` をリポジトリ直下に作り、`rm -rf` で消そうとして止められ、人が2回消した。**禁止の一覧と番人の制限は正しい(緩めない)。悪いのは置き場。**
132
+ - **なぜ(後片付けの話を改めた理由)。** 旧い文言は「リポジトリ直下は番人が `rm` を止めるので自分で消さず報告」だった。実測(便 fb248f6e)では、`-f` を付けない `rm -r` と実在のパスの名指しなら、リポジトリ直下の自分の作業ファイルも通る。止まるのは `-f` 付きと変数・`*` 入りだけ。**止まらない形を「止まる」と書いていると、職人が自分の残骸を残したまま便を終える**ので、実物に合わせた(番人・禁止の一覧は1行も変えていない)。
133
+ - 置き場を `/tmp/claude-1000/` にすれば、止められることも、残骸がコミットに混ざることも起きない(根治=鉄則10)。Playwright の検品ページも同じ(鉄則8-④)。
127
134
  14. **`.env` 類(`.env`・`.env.local`・`.env.example` などの雛形も含む)は職人が書き換えない(えふさん確定 2026-09-18・便 3d11f913)**
128
135
  - **番人は `.env` 本体も `.env.example` などの雛形も、書き込みを止める**(Write/Edit の設定 deny と、Bash の `echo`/`tee`/`sed -i`/`cp`/`mv` などを、経路を問わず同じ結果にそろえた)。読み込みは今までどおり(雛形は読める・本物は読めない)。
129
136
  - **なぜ。** 雛形(`.env.example`)は普通は秘密値を置かないが、「exampleだから常に安全」と番人へ例外を作ると、将来誤って実値が書かれたときの穴になる(利便性より安全側・えふさん確定)。
@@ -155,6 +162,7 @@ description: 采配くんの便(プロンプト末尾に CONDUCTOR_JOB:<id>
155
162
  - **`-f` 付き、または中身が変わる場所を指す再帰の `rm`**(`rm -rf`・`rm -fr`・`rm -r -f`・`rm -R`・`rm --recursive`/`rm -r "$DIR/x"`・`rm -r build/*` のように変数や `*` を含む)→ 止まる。`-f` 付きは配布している禁止の一覧(現場の `.claude/settings.json` の deny)で**必ず**止まる(番人より先に効く=消す先が作業ファイルでも通らない・便 fb248f6e)。変数・`*` は番人が止める。**`-f` を付けず、消す先を実在のパスで名指しする**(`/tmp/claude-1000/` の下・鉄則13)。★止められたら**別の消し方を探さず**(`find -delete`・`python -c` 等に回らない)、消せなかった場所を報告に書く(鉄則4)。
156
163
  - 通る例: `rm -r /tmp/claude-1000/作業名` / `rm -r node_modules/.tmp-作業名`(★`-f` を付けない・自分で作った物だけ)
157
164
  - 止まる例: `rm -rf /tmp/claude-1000/作業名`(禁止の一覧)/ `rm -r "$DIR/x"`・`rm -r build/*`(番人)/ `rm -r .git`・`rm -r .claude/hooks`・`rm -r .tyhld`(番人=相対パスでも消せない)
165
+ - ★**探すだけの道具の「探す言葉」に `rm …` と書いても、もう止まらない**(便 915694ac で直した)。鉄則や台本を調べるときの `grep -rn 'rm -rf .git' skills`・`git grep -n 'rm -r' -- skills`・`rg 'rm -rf /' docs` は通る。★囲みの外に書いた `rm` は今までどおり測るので、`grep -rn 'x' docs; rm -rf .git` のように**後ろで消す形は止まる**。
158
166
  - **作業場所が変数のままの `git push`**(`git -C "$DIR" push`・行き先のブランチを確かめられない形)→ 止まる。**リポの中で、ブランチを名指しして** `git push -u origin <作業ブランチ名>`。
159
167
 
160
168
  ## 便の終え方
@@ -102,7 +102,7 @@ else
102
102
  # 入口の一式を写す元=公開済みの正規版(作業ツリーからは写さない・便 4061dd71 と同じ判定)。
103
103
  if ! dist_source_for_setup "$(cd "$SCRIPT_DIR/.." && pwd)"; then
104
104
  die "司令塔を動かす采配くんを確かめられません(${DIST_REASON})。
105
- 人の手番: npm install -g @tyhld/conductor --@tyhld:registry=https://registry.npmjs.org"
105
+ $(dist_human_step "$DIST_REASON" | sed 's/^/ /')"
106
106
  fi
107
107
  if ! dist_runtime_bundle_install "$DIST_ROOT"; then
108
108
  die "司令塔の入口を置けませんでした: $DIST_RUNTIME_BIN_DIR"
@@ -85,7 +85,7 @@ else
85
85
  # 入口の一式を写す元=公開済みの正規版(作業ツリーからは写さない・便 4061dd71 と同じ判定)。
86
86
  if ! dist_source_for_setup "$(cd "$SCRIPT_DIR/.." && pwd)"; then
87
87
  skip "耳を動かす采配くんを確かめられません(${DIST_REASON})。
88
- 人の手番: npm install -g @tyhld/conductor --@tyhld:registry=https://registry.npmjs.org"
88
+ $(dist_human_step "$DIST_REASON" | sed 's/^/ /')"
89
89
  fi
90
90
  if ! dist_runtime_bundle_install "$DIST_ROOT"; then
91
91
  skip "耳の入口を置けませんでした: $DIST_RUNTIME_BIN_DIR"
@@ -66,7 +66,7 @@ done
66
66
  . "$SCRIPT_DIR/../scripts/dist-source.sh"
67
67
  if ! dist_source_for_setup "$REPO_ROOT"; then
68
68
  warn "配り元を確かめられません(${DIST_REASON})。自動同期タイマーは設置しません。"
69
- warn " 人の手番: npm install -g @tyhld/conductor --@tyhld:registry=https://registry.npmjs.org"
69
+ while IFS= read -r l; do warn " $l"; done < <(dist_human_step "$DIST_REASON")
70
70
  exit 0
71
71
  fi
72
72
  if [[ "$DIST_ROOT" != "$REPO_ROOT" ]]; then
@@ -0,0 +1,27 @@
1
+ # tyhld-autoupdate-boot.service — 起動時に1回だけ、更新係を流す(便 29babc5d・設計 3ed9e2b2 の A3)。
2
+ #
3
+ # 【何をするか】~/.tyhld/bin/tyhld-autoupdate --boot を1回。管制に目標の版を聞き、違えば入れ替える。
4
+ # 起動直後は便が0なので、更新は「電源を入れた直後」にほぼ済む(設計 1/4)。
5
+ # 自己診断は、常駐が起きてからタイマーの回(tyhld-autoupdate.service)が行う。
6
+ #
7
+ # 【順番】常駐(conductor@*・耳・司令塔)は、この1回が終わってから起きる。
8
+ # ★順番は常駐の側の drop-in(conductor@.service.d/10-tyhld-autoupdate.conf の After=)で決める。
9
+ # 置くのは scripts/autoupdate-install.sh(自社PCだけ)。お客さまの機の常駐には何も足さない。
10
+ # ★この1回が失敗しても常駐は起きる(After= だけで Requires= は付けない)。
11
+ #
12
+ # 【★自社PCだけ】置いて有効にするのは scripts/autoupdate-install.sh(人の窓で1回)だけ。
13
+
14
+ [Unit]
15
+ Description=tyhld: auto-update check at boot (before the relays start)
16
+ After=network-online.target
17
+ Wants=network-online.target
18
+
19
+ [Service]
20
+ Type=oneshot
21
+ EnvironmentFile=-%h/.conductor.env
22
+ ExecStart=/bin/bash %h/.tyhld/bin/tyhld-autoupdate --boot
23
+ # 入れ替え(npm・公式の導入台本)の上限。常駐をいつまでも待たせない。
24
+ TimeoutStartSec=20min
25
+
26
+ [Install]
27
+ WantedBy=default.target
@@ -0,0 +1,24 @@
1
+ # tyhld-autoupdate.service — 更新係を1回流す(便 29babc5d・設計 3ed9e2b2 の A3)。
2
+ #
3
+ # 【何をするか】~/.tyhld/bin/tyhld-autoupdate を1回(中身は src/autoupdate.ts)。
4
+ # ・管制が返す checkAgainMin の時刻が来た回だけ、管制に目標の版を聞く(それ以外の回はすぐ終わる)
5
+ # ・全現場が空きのときだけ入れ替える → 常駐を起こし直す → 自己診断 → 結果を管制へ
6
+ # ・自己診断が FAIL なら、この機だけ前の版へ戻す
7
+ # ★sudo を使わない。番人・禁止の一覧・囲い・通信の許可には触らない。
8
+ #
9
+ # 【★自社PCだけ】置いて有効にするのは scripts/autoupdate-install.sh(人の窓で1回)だけ。
10
+ # 鍵(~/.tyhld/bin/autoupdate/enabled)が無ければ、この係は何もしない。
11
+ # .timer から数分ごとに呼ばれる(tyhld-autoupdate.timer)。
12
+
13
+ [Unit]
14
+ Description=tyhld: auto-update (conductor / Claude Code, staged by the central)
15
+ After=network-online.target
16
+ Wants=network-online.target
17
+
18
+ [Service]
19
+ Type=oneshot
20
+ EnvironmentFile=-%h/.conductor.env
21
+ ExecStart=/bin/bash %h/.tyhld/bin/tyhld-autoupdate
22
+ # 入れ替え+自己診断(小さな試験の3分を含む)+戻すときの分。
23
+ TimeoutStartSec=60min
24
+ Nice=10
@@ -0,0 +1,20 @@
1
+ # tyhld-autoupdate.timer — 更新係を数分ごとに起こす(便 29babc5d・設計 3ed9e2b2 の A3)。
2
+ #
3
+ # - OnBootSec=4min … 起動時の1回(tyhld-autoupdate-boot)と常駐が起きた後に、1回目
4
+ # (起動時に入れ替えた機は、ここで自己診断を行う)
5
+ # - OnUnitActiveSec=5min … 以後5分ごとに起きる。★管制へ聞きに行くのは、管制が返した
6
+ # checkAgainMin(いまは15分)が来た回だけ(間隔は更新係が決め打ちしない)
7
+ # - Persistent=false … 取りこぼしの追い実行はしない(起動時の1回が在る)
8
+ #
9
+ # ★自社PCだけ。有効にするのは scripts/autoupdate-install.sh(お客さまの機には入れない)。
10
+
11
+ [Unit]
12
+ Description=tyhld: auto-update check
13
+
14
+ [Timer]
15
+ OnBootSec=4min
16
+ OnUnitActiveSec=5min
17
+ AccuracySec=30s
18
+
19
+ [Install]
20
+ WantedBy=timers.target
@@ -5,7 +5,7 @@
5
5
  # ・副作用なし(commit・push・PR・合流・配備・公開をしない)。結果は手元の控えと管制へ。
6
6
  # ・正常時は何も知らせない(知らせるかどうかは管制が決める)。
7
7
  #
8
- # 【★自社PCだけ】置いて有効にするのは自社PCの導入台本(次の便 A3)か人の窓。
8
+ # 【★自社PCだけ】置いて有効にするのは自社PCの導入台本 scripts/autoupdate-install.sh(人の窓で1回)。
9
9
  # sales-template/install.sh・setup.sh はこのタイマーを置かない(お客さまの機には入れない)。
10
10
  # .timer から毎日1回呼ばれる(tyhld-selfcheck.timer)。
11
11
 
@@ -5,7 +5,7 @@
5
5
  # - RandomizedDelaySec … 複数の機が同じ時刻に Claude を呼ばないよう30分ばらす。
6
6
  # ★管制は「26時間来ない機」を🟡にする想定(設計 3ed9e2b2 の 2/4)。
7
7
  #
8
- # ★自社PCだけ。有効にするのは自社PCの導入台本(次の便 A3)か人の窓(お客さまの機には入れない)。
8
+ # ★自社PCだけ。有効にするのは自社PCの導入台本 scripts/autoupdate-install.sh(お客さまの機には入れない)。
9
9
 
10
10
  [Unit]
11
11
  Description=tyhld: daily self-check