@tyhld/conductor 0.12.7 → 0.12.8
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 +3 -1
- package/sales-template/setup.sh +28 -6
- package/scripts/dist-source.sh +168 -0
- package/scripts/hooks/skill_sync_notify.py +30 -6
- package/scripts/hooks/test_policy.py +11 -2
- package/scripts/hooks/test_skill_sync_notify.py +27 -0
- package/scripts/update-guard.sh +1014 -0
- package/systemd/guard-sync-install.sh +22 -4
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@tyhld/conductor",
|
|
3
|
-
"version": "0.12.
|
|
3
|
+
"version": "0.12.8",
|
|
4
4
|
"description": "采配くん管制の見守りアプリ(conductor-agent)。各PCで常駐し、中央(devlog-tracker)へ定期的に生存報告(heartbeat)を送る常駐CLI。",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"bin": {
|
|
@@ -19,6 +19,8 @@
|
|
|
19
19
|
"sales-template",
|
|
20
20
|
"scripts/hooks",
|
|
21
21
|
"!scripts/hooks/__pycache__",
|
|
22
|
+
"scripts/update-guard.sh",
|
|
23
|
+
"scripts/dist-source.sh",
|
|
22
24
|
"scripts/codex",
|
|
23
25
|
"!scripts/codex/__pycache__",
|
|
24
26
|
"skills",
|
package/sales-template/setup.sh
CHANGED
|
@@ -571,7 +571,29 @@ mkdir -p "$CLAUDE_DIR"
|
|
|
571
571
|
# Notification を足しても【新規に設置したお客さまの機体には入らなかった】。
|
|
572
572
|
# 下の ⑤-0 で見本と配る中身を機械照合し、外れていれば設置を止める=足し忘れが起こせない。
|
|
573
573
|
GUARD_HOME="$HOME/.tyhld/hooks"
|
|
574
|
-
|
|
574
|
+
|
|
575
|
+
# ─── ★配り元を決める(便 4061dd71) ─────────────────────────────────────────
|
|
576
|
+
# 【なぜ要るか】この台本は「setup.sh から見て .. 」を配り元にしている。お客さまの機では
|
|
577
|
+
# それが公開済みの配布物なので正しい。ところが開発機でリポジトリの sales-template/setup.sh を
|
|
578
|
+
# 打つと、配り元は★職人の作業ツリーになり、作りかけの番人がその機の全現場へ配られる。
|
|
579
|
+
# 【どう直したか】呼び出し元が git の作業場所かどうかで分ける(dist-source.sh)。
|
|
580
|
+
# ・git の作業場所でない=配布物そのもの(★お客さまの機はいつもこちら)。
|
|
581
|
+
# 今までどおりそのまま使う。公開 npm への問い合わせもしない=回線が無くても設置できる。
|
|
582
|
+
# ・git の作業場所=職人の作業ツリー。公開済みの正規版(この機に入っている配布物で、版が
|
|
583
|
+
# 公開 npm のいまの版と一致するもの)へ差し替える。確かめられなければ配らずに止まる。
|
|
584
|
+
. "$SCRIPT_DIR/../scripts/dist-source.sh"
|
|
585
|
+
if ! dist_source_for_setup "$(cd "$SCRIPT_DIR/.." && pwd)"; then
|
|
586
|
+
fail "配り元を確かめられません(${DIST_REASON})。★作業ツリーからは配りません。
|
|
587
|
+
人の手番: npm install -g @tyhld/conductor --@tyhld:registry=https://registry.npmjs.org
|
|
588
|
+
そのうえで、入れた配布物の setup.sh を打ってください。"
|
|
589
|
+
fi
|
|
590
|
+
SETUP_DIST_ROOT="$DIST_ROOT"
|
|
591
|
+
if [[ "$SETUP_DIST_ROOT" != "$(cd "$SCRIPT_DIR/.." && pwd)" ]]; then
|
|
592
|
+
echo "[setup] 配り元: $SETUP_DIST_ROOT(版 ${DIST_VERSION:-不明}=公開 npm のいまの版)"
|
|
593
|
+
echo "[setup] ★この台本の置き場(作業ツリー)からは配りません。"
|
|
594
|
+
fi
|
|
595
|
+
|
|
596
|
+
GUARD_SAMPLE="$SETUP_DIST_ROOT/scripts/hooks/settings.sample.json"
|
|
575
597
|
|
|
576
598
|
# --- 番人一式(正本1本)を固定パスへ配置 ---
|
|
577
599
|
# ★配布元は「正本1本」だけ。以前は sales-template/ に複製を置いていたため、正本が更新されても
|
|
@@ -579,7 +601,7 @@ GUARD_SAMPLE="$SCRIPT_DIR/../scripts/hooks/settings.sample.json"
|
|
|
579
601
|
#
|
|
580
602
|
# 正本の在り処は「setup.sh から見て ../scripts/hooks」。npm 配布物でもリポジトリでも
|
|
581
603
|
# 同じ相対位置になる(package.json の files に scripts/hooks を含めてある)。
|
|
582
|
-
GUARD_SRC_DIR="$
|
|
604
|
+
GUARD_SRC_DIR="$SETUP_DIST_ROOT/scripts/hooks"
|
|
583
605
|
|
|
584
606
|
# ★配る番人一式(段2の3フック+その依存)。scripts/update-guard.sh と同じ集合にする。
|
|
585
607
|
# 1本でも欠けると差し替えた現場の番人が読めず止まる(勤務用コピーの考え方=ADR-002)。
|
|
@@ -761,7 +783,7 @@ echo "[setup] 番人フック($registered_events)を $HOME_SETTINGS に登録し
|
|
|
761
783
|
# --- Codex の職人にも番人を登録する(★ADR-022・Codex を使う機体だけ) -------
|
|
762
784
|
# ★同じ処理を2か所に書かない: 稼働機の更新(update-guard.sh ①-e2)と同じスクリプトを呼ぶ。
|
|
763
785
|
# Codex を使っていない機体では中で何もしない(~/.codex を勝手に作らない)。
|
|
764
|
-
CODEX_HOOK_INSTALL="$
|
|
786
|
+
CODEX_HOOK_INSTALL="$SETUP_DIST_ROOT/scripts/hooks/install-codex-hooks.sh"
|
|
765
787
|
if [[ -f "$CODEX_HOOK_INSTALL" ]]; then
|
|
766
788
|
APPLY=1 GUARD_HOME="$GUARD_HOME" bash "$CODEX_HOOK_INSTALL" \
|
|
767
789
|
|| warn "Codex への番人登録に失敗しました(Claude 側の設置は完了しています)"
|
|
@@ -777,9 +799,9 @@ fi
|
|
|
777
799
|
# 締め出す物の正本は @tyhld/conductor の src/proto-profile.ts(=dist/proto-profile.js)1本で、
|
|
778
800
|
# deny も deniedDomains もそこから生成される。ここに一覧を写さない(写しを2か所に持たない=
|
|
779
801
|
# 片方に足してもう片方を忘れる事故を構造的に起こせなくする)。
|
|
780
|
-
SETTINGS_SRC="$
|
|
802
|
+
SETTINGS_SRC="$SETUP_DIST_ROOT/sales-template/settings.json"
|
|
781
803
|
SETTINGS_DST="$CLAUDE_DIR/settings.json"
|
|
782
|
-
PROTO_GEN="$
|
|
804
|
+
PROTO_GEN="$SETUP_DIST_ROOT/dist/proto-profile.js"
|
|
783
805
|
if [[ "$PROFILE" == "proto" ]]; then
|
|
784
806
|
[[ -f "$PROTO_GEN" ]] || fail "試作の型の正本が見つかりません: $PROTO_GEN
|
|
785
807
|
配布物が壊れている可能性があります。次の1行で入れ直してください:
|
|
@@ -882,7 +904,7 @@ fi
|
|
|
882
904
|
# ★番人と違い、これは安全境界ではなく作業規律なので、見つからないときは中断せず警告して進む
|
|
883
905
|
# (スキルが無くても番人は効いており、安全に穴は空かない)。
|
|
884
906
|
CRAFTSMAN_SKILL_NAME="conductor-craftsman"
|
|
885
|
-
CRAFTSMAN_SKILL_SRC="$
|
|
907
|
+
CRAFTSMAN_SKILL_SRC="$SETUP_DIST_ROOT/skills/$CRAFTSMAN_SKILL_NAME"
|
|
886
908
|
CRAFTSMAN_SKILL_DST="$HOME/.claude/skills/$CRAFTSMAN_SKILL_NAME"
|
|
887
909
|
|
|
888
910
|
if [[ -f "$CRAFTSMAN_SKILL_SRC/SKILL.md" ]]; then
|
|
@@ -0,0 +1,168 @@
|
|
|
1
|
+
#!/usr/bin/env bash
|
|
2
|
+
# shellcheck disable=SC2034
|
|
3
|
+
# ★上の指示(SC2034=使っていない変数)は、このファイル全体に効かせるため最初のコマンドより前に置く。
|
|
4
|
+
# この部品の「返り値」は DIST_ROOT / DIST_VERSION / DIST_PUBLISHED / DIST_REASON の4つで、
|
|
5
|
+
# 読むのは呼び出し元(update-guard.sh / sales-template/setup.sh / systemd/guard-sync-install.sh)。
|
|
6
|
+
# このファイルの中では使い切らないので、指摘だけが出てしまう。
|
|
7
|
+
#
|
|
8
|
+
# dist-source.sh — 「全現場へ配ってよい中身は、どこにあるか」を決める1か所(★便 4061dd71)。
|
|
9
|
+
#
|
|
10
|
+
# 【なぜ要るか(実測 2026-09-16・開発履歴 6b405ae6)】
|
|
11
|
+
# 1時間ごとの番人の自動同期(tyhld-guard-sync.timer)の本体が
|
|
12
|
+
# /home/fshim/projects/conductor/scripts/update-guard.sh
|
|
13
|
+
# =★conductor 職人の【作業ツリー】を指していた。職人が番人を直しかけのブランチに居るあいだに
|
|
14
|
+
# 同期が走ると、作りかけの番人がその機の【全現場】へ配られる。当日は main で変更なしだったため
|
|
15
|
+
# 無事だった(配った番人7本が公開版 0.12.5 と cmp で一致)。無事だったのは偶然である。
|
|
16
|
+
#
|
|
17
|
+
# 【えふさん確定(2026-09-16)】
|
|
18
|
+
# 「① 番人の1時間自動同期 承認 29612ff1後に実施。ただし『公開済みの正規版』を唯一の配布元にする。
|
|
19
|
+
# 作業treeから全現場へ配らない」
|
|
20
|
+
#
|
|
21
|
+
# 【この部品が決めること】★これだけ
|
|
22
|
+
# ・配ってよい中身の置き場(DIST_ROOT)=この機に入っている @tyhld/conductor の配布物
|
|
23
|
+
# ・それが「公開済みの正規版」か(入っている版 == 公開 npm のいまの版)
|
|
24
|
+
# ・違う/確かめられないときは【配らない】と言い切ること
|
|
25
|
+
#
|
|
26
|
+
# 【この部品が決めないこと】
|
|
27
|
+
# ・番人の判定・禁止の中身・鉄則の中身(1文字も触らない)
|
|
28
|
+
# ・配った結果の記録と知らせ方(呼び出し側が便 29612ff1 の仕組みに載せる)
|
|
29
|
+
#
|
|
30
|
+
# 【使い方】
|
|
31
|
+
# . "$(dirname "$0")/dist-source.sh"
|
|
32
|
+
# if dist_source_resolve; then …DIST_ROOT を配り元にしてよい…
|
|
33
|
+
# else …DIST_REASON を記録して配らない…
|
|
34
|
+
# fi
|
|
35
|
+
#
|
|
36
|
+
# 【試験用の入口】★安全境界ではない(環境変数を置ける人は cp も打てる)。
|
|
37
|
+
# CONDUCTOR_DIST_ROOT … 配布物の置き場を名指しする(試験で偽の配布物を指すため)
|
|
38
|
+
# CONDUCTOR_DIST_PUBLISHED … 公開 npm への問い合わせの代わりに使う値(試験で網を切らないため)
|
|
39
|
+
|
|
40
|
+
# ★取得先の宣言は install.sh / setup.sh の「registry-pin」正本と同じ形にする。
|
|
41
|
+
# (公開 npm 1本。手元の設定が何であってもここだけは公開 npm を見る=便 9858450b)
|
|
42
|
+
CONDUCTOR_PKG="${CONDUCTOR_PKG:-@tyhld/conductor}"
|
|
43
|
+
CONDUCTOR_REGISTRY="${CONDUCTOR_REGISTRY:-https://registry.npmjs.org}"
|
|
44
|
+
CONDUCTOR_REGISTRY_ARG="--@tyhld:registry=${CONDUCTOR_REGISTRY}"
|
|
45
|
+
|
|
46
|
+
#: 解決の結果。呼び出し側はこの3つだけ見ればよい。
|
|
47
|
+
DIST_ROOT='' # 配ってよい中身の置き場(確かめられたときだけ入る)
|
|
48
|
+
DIST_VERSION='' # そこに入っている版
|
|
49
|
+
DIST_PUBLISHED='' # 公開 npm のいまの版
|
|
50
|
+
DIST_REASON='' # 配れないときの理由(1語・記録にそのまま載る)
|
|
51
|
+
|
|
52
|
+
# この機に入っている配布物の置き場を探す。見つからなければ空。
|
|
53
|
+
# ★node の版が変わると /home/…/.nvm/versions/node/<版>/… の <版> が変わる。
|
|
54
|
+
# だから固定のパスを覚えない。いま PATH に居る conductor から辿る(版が変わっても付いていく)。
|
|
55
|
+
dist_pkg_root() {
|
|
56
|
+
if [[ -n "${CONDUCTOR_DIST_ROOT:-}" ]]; then
|
|
57
|
+
printf '%s' "$CONDUCTOR_DIST_ROOT"
|
|
58
|
+
return 0
|
|
59
|
+
fi
|
|
60
|
+
local bin real root
|
|
61
|
+
# ★bash で動く書き方(command -v -a は zsh 専用。便 9858450b で踏んだ)。
|
|
62
|
+
bin="$(type -a -p conductor 2>/dev/null | head -n 1)"
|
|
63
|
+
if [[ -n "$bin" ]]; then
|
|
64
|
+
real="$(readlink -f "$bin" 2>/dev/null || printf '%s' "$bin")"
|
|
65
|
+
# <置き場>/dist/cli.js から2つ上がると配布物の根になる。
|
|
66
|
+
root="$(cd "$(dirname "$real")/.." 2>/dev/null && pwd)" || root=''
|
|
67
|
+
if [[ -n "$root" && -f "$root/package.json" ]]; then
|
|
68
|
+
printf '%s' "$root"
|
|
69
|
+
return 0
|
|
70
|
+
fi
|
|
71
|
+
fi
|
|
72
|
+
# 予備=npm が言う全体の置き場から辿る。
|
|
73
|
+
local groot
|
|
74
|
+
groot="$(npm root -g 2>/dev/null)"
|
|
75
|
+
if [[ -n "$groot" && -f "$groot/$CONDUCTOR_PKG/package.json" ]]; then
|
|
76
|
+
printf '%s' "$groot/$CONDUCTOR_PKG"
|
|
77
|
+
return 0
|
|
78
|
+
fi
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
# 指定の置き場に入っている版(package.json)。読めなければ空。
|
|
82
|
+
dist_installed_version() {
|
|
83
|
+
local pj="$1/package.json"
|
|
84
|
+
[[ -f "$pj" ]] || return 0
|
|
85
|
+
python3 -c 'import json,sys
|
|
86
|
+
try:
|
|
87
|
+
print(json.load(open(sys.argv[1], encoding="utf-8")).get("version") or "")
|
|
88
|
+
except Exception:
|
|
89
|
+
pass' "$pj" 2>/dev/null
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
# 公開 npm が「いま配っている版」(取れなければ空)。★固定の下限は使わない(便 9858450b)。
|
|
93
|
+
dist_published_version() {
|
|
94
|
+
# ★検査用の入口。空文字を入れれば「問い合わせられなかった」を作れる(網を切らずに確かめる)。
|
|
95
|
+
if [[ -n "${CONDUCTOR_DIST_PUBLISHED+x}" ]]; then
|
|
96
|
+
printf '%s' "$CONDUCTOR_DIST_PUBLISHED"
|
|
97
|
+
return 0
|
|
98
|
+
fi
|
|
99
|
+
local v
|
|
100
|
+
v="$(npm view "$CONDUCTOR_PKG" version "$CONDUCTOR_REGISTRY_ARG" 2>/dev/null | tr -d '[:space:]')"
|
|
101
|
+
if [[ "$v" =~ ^[0-9]+\.[0-9]+\.[0-9]+ ]]; then
|
|
102
|
+
printf '%s' "${BASH_REMATCH[0]}"
|
|
103
|
+
fi
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
# 配ってよい中身の置き場を決める。
|
|
107
|
+
# 返り 0 … DIST_ROOT を配り元にしてよい
|
|
108
|
+
# 返り 1 … 配ってはいけない(DIST_REASON に理由)
|
|
109
|
+
# 理由の語(記録にそのまま載る)
|
|
110
|
+
# not-installed … 配布物がこの機に入っていない
|
|
111
|
+
# no-version … 入っている配布物の版が読めない
|
|
112
|
+
# unverifiable … 公開 npm に問い合わせられない(回線・取得先の不調)
|
|
113
|
+
# version-drift … 入っている版が公開 npm のいまの版と違う
|
|
114
|
+
dist_source_resolve() {
|
|
115
|
+
DIST_ROOT=''; DIST_VERSION=''; DIST_PUBLISHED=''; DIST_REASON=''
|
|
116
|
+
local root
|
|
117
|
+
root="$(dist_pkg_root)"
|
|
118
|
+
if [[ -z "$root" ]]; then
|
|
119
|
+
DIST_REASON='not-installed'
|
|
120
|
+
return 1
|
|
121
|
+
fi
|
|
122
|
+
DIST_VERSION="$(dist_installed_version "$root")"
|
|
123
|
+
if [[ -z "$DIST_VERSION" ]]; then
|
|
124
|
+
DIST_REASON='no-version'
|
|
125
|
+
return 1
|
|
126
|
+
fi
|
|
127
|
+
DIST_PUBLISHED="$(dist_published_version)"
|
|
128
|
+
if [[ -z "$DIST_PUBLISHED" ]]; then
|
|
129
|
+
# ★ここで「まあいいか」と配らない。問い合わせられない=正規版かどうか分からない、である。
|
|
130
|
+
DIST_REASON='unverifiable'
|
|
131
|
+
return 1
|
|
132
|
+
fi
|
|
133
|
+
if [[ "$DIST_VERSION" != "$DIST_PUBLISHED" ]]; then
|
|
134
|
+
DIST_REASON='version-drift'
|
|
135
|
+
return 1
|
|
136
|
+
fi
|
|
137
|
+
DIST_ROOT="$root"
|
|
138
|
+
return 0
|
|
139
|
+
}
|
|
140
|
+
|
|
141
|
+
# 配布物として体裁が揃っているか(配る中身が欠けた置き場を掴まない)。
|
|
142
|
+
# ★ここで見るのは「采配くんの配布物であること」を決める最小の3つだけ。
|
|
143
|
+
# 職人の鉄則(skills/)は安全境界ではなく作業規律で、欠けたときの扱いは ①-d が持っている
|
|
144
|
+
# (便 29612ff1=成功と偽らず控えに残して管制へ知らせる)。ここで落とすと、その扱いが働かない。
|
|
145
|
+
dist_source_complete() {
|
|
146
|
+
local root="$1"
|
|
147
|
+
[[ -f "$root/package.json" ]] || return 1
|
|
148
|
+
[[ -f "$root/scripts/hooks/cc2_guard.py" ]] || return 1
|
|
149
|
+
[[ -f "$root/sales-template/settings.json" ]] || return 1
|
|
150
|
+
return 0
|
|
151
|
+
}
|
|
152
|
+
|
|
153
|
+
# 設置台本(setup.sh)のための配り元。★お客さまの機の流れを1ミリも変えないための分岐。
|
|
154
|
+
# ・呼び出し元が【git の作業場所でない】=配布物そのもの(お客さまの機はいつもこちら)。
|
|
155
|
+
# そのまま使う。公開 npm への問い合わせもしない=今までどおり・回線が無くても設置できる。
|
|
156
|
+
# ・呼び出し元が【git の作業場所】=職人の作業ツリー。ここから全現場へ配らせない。
|
|
157
|
+
# 公開済みの正規版へ差し替える(確かめられなければ 1 を返して呼び出し元を止める)。
|
|
158
|
+
# 返り 0 … DIST_ROOT を配り元にしてよい/返り 1 … 配ってはいけない(DIST_REASON に理由)
|
|
159
|
+
dist_source_for_setup() {
|
|
160
|
+
local caller_root="$1"
|
|
161
|
+
DIST_ROOT=''; DIST_VERSION=''; DIST_PUBLISHED=''; DIST_REASON=''
|
|
162
|
+
if ! git -C "$caller_root" rev-parse --git-dir >/dev/null 2>&1; then
|
|
163
|
+
DIST_ROOT="$caller_root"
|
|
164
|
+
DIST_VERSION="$(dist_installed_version "$caller_root")"
|
|
165
|
+
return 0
|
|
166
|
+
fi
|
|
167
|
+
dist_source_resolve
|
|
168
|
+
}
|
|
@@ -45,11 +45,35 @@ import hook_datalink # noqa: E402
|
|
|
45
45
|
#: ★1時間ごとの同期と同じ幅にしてあるので、直らない限りカードは途切れない。
|
|
46
46
|
NOTIFY_WAIT_MS = 60 * 60 * 1000
|
|
47
47
|
|
|
48
|
-
#:
|
|
48
|
+
#: 同じカードを積み上げないための鍵の頭。★機体ごと・種類ごとに1枚だけにする。
|
|
49
49
|
#: 管制は同じ鍵の再登録を【寿命の延長】として扱う(upsertHookRequest)。だから
|
|
50
50
|
#: 毎時の失敗でカードが増えず、直ればその回で登録が止まって数分後に自然に閉じる。
|
|
51
51
|
REQUEST_KEY_PREFIX = 'craftsman-skill-stale'
|
|
52
52
|
|
|
53
|
+
#: 知らせの種類ごとの文面(★便 4061dd71 で2種類目を足した)。
|
|
54
|
+
#: 控えの `what=` で選ぶ。無ければ従来どおり鉄則の配り損ね(便 29612ff1)。
|
|
55
|
+
#: ★新しい知らせ方(別の口)は作らない。ここに1行足すだけで同じ確認カードに載る。
|
|
56
|
+
NOTICE_KINDS = {
|
|
57
|
+
'craftsman-skill': {
|
|
58
|
+
'key': 'craftsman-skill-stale',
|
|
59
|
+
'summary': '職人の決まりが古いままです',
|
|
60
|
+
'stopped': '職人の決まり(鉄則)が、新しい版に入れ替わっていません。'
|
|
61
|
+
'職人は古い決まりのまま動きます。',
|
|
62
|
+
},
|
|
63
|
+
'dist-source': {
|
|
64
|
+
'key': 'dist-source-unverified',
|
|
65
|
+
'summary': '安全装置を配れていません',
|
|
66
|
+
'stopped': '配り元(この PC に入っている采配くん本体)が公開済みの正規版だと'
|
|
67
|
+
'確かめられないため、番人・職人の決まり・現場の権限ルールを'
|
|
68
|
+
'1件も配っていません。現場は前に配られたままの状態で動きます。',
|
|
69
|
+
},
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
|
|
73
|
+
def notice_kind(rec):
|
|
74
|
+
"""控えの what= から文面を選ぶ。知らない種類なら鉄則の配り損ねとして扱う。"""
|
|
75
|
+
return NOTICE_KINDS.get(rec.get('what') or 'craftsman-skill', NOTICE_KINDS['craftsman-skill'])
|
|
76
|
+
|
|
53
77
|
|
|
54
78
|
def parse_last(path):
|
|
55
79
|
"""直近の試みの控えを {鍵: 値} に読む。読めなければ空。
|
|
@@ -77,9 +101,9 @@ def build_payload(rec, env=None):
|
|
|
77
101
|
env = os.environ if env is None else env
|
|
78
102
|
machine = env.get('CONDUCTOR_MACHINE') or os.uname().nodename
|
|
79
103
|
reason = rec.get('reason', '不明')
|
|
104
|
+
kind = notice_kind(rec)
|
|
80
105
|
body = '\n'.join([
|
|
81
|
-
f'いま止まっているもの: この PC({machine}
|
|
82
|
-
'新しい版に入れ替わっていません。職人は古い決まりのまま動きます。',
|
|
106
|
+
f'いま止まっているもの: この PC({machine})で、{kind["stopped"]}',
|
|
83
107
|
'再開のしかた: この PC で次の1行を打ってください。'
|
|
84
108
|
' cd ~/projects/conductor そのあと APPLY=1 bash scripts/update-guard.sh',
|
|
85
109
|
'★このカードは管制からは押せません(この PC での操作が要ります)。',
|
|
@@ -94,10 +118,10 @@ def build_payload(rec, env=None):
|
|
|
94
118
|
'sessionId': hook_datalink.stable_session_id('', machine, 'guard-sync'),
|
|
95
119
|
'kind': 'message',
|
|
96
120
|
'question': body,
|
|
97
|
-
'summary': f'
|
|
121
|
+
'summary': f'{kind["summary"]}({machine})',
|
|
98
122
|
'waitMs': NOTIFY_WAIT_MS,
|
|
99
|
-
#
|
|
100
|
-
'requestKey': f'{
|
|
123
|
+
# ★機体ごと・種類ごとに固定の鍵。毎時の失敗が同じ1枚に集約される(増殖しない)。
|
|
124
|
+
'requestKey': f'{kind["key"]}:{machine}',
|
|
101
125
|
}
|
|
102
126
|
|
|
103
127
|
|
|
@@ -974,8 +974,17 @@ check('同期モードは権限ルールの整理より前で終わる',
|
|
|
974
974
|
# 人が流し忘れても、すでに配ってしまった現場の二重登録が1時間以内にそろう。
|
|
975
975
|
check('同期モードは現場の二重登録外しまでは行う',
|
|
976
976
|
_ug.index('③ 現場の二重登録を外す') < _ug.index('同期のみ完了しました'), True)
|
|
977
|
-
|
|
978
|
-
|
|
977
|
+
# ★2026-09-16(便 4061dd71)に守り方を移した。旧=「この作業ツリーに未コミットの変更が
|
|
978
|
+
# 無ければ配ってよい」(=コミットさえすれば作りかけでも配れた・そもそも作業ツリーが配り元だった)。
|
|
979
|
+
# 新=配り元を「この機に入っている配布物で、版が公開 npm のいまの版と一致するもの」だけに限る。
|
|
980
|
+
# 中身の検査は test/dist-source-published-only.test.ts。
|
|
981
|
+
check('配り元を決める部品を通している', 'dist-source.sh' in _ug, True)
|
|
982
|
+
check('配り元は公開済みの正規版だけ', 'dist_source_resolve' in _ug, True)
|
|
983
|
+
check('配る中身はすべて配り元から取る',
|
|
984
|
+
all(x in _ug for x in ('GUARD_SRC_DIR="$DIST_ROOT/scripts/hooks"',
|
|
985
|
+
'SETTINGS_SRC="$DIST_ROOT/sales-template/settings.json"',
|
|
986
|
+
'CRAFTSMAN_SKILL_SRC="$DIST_ROOT/skills/conductor-craftsman/SKILL.md"')), True)
|
|
987
|
+
check('確かめられないときは配らずに終わる', 'dist_record failed' in _ug, True)
|
|
979
988
|
|
|
980
989
|
_svc = os.path.join(_conductor, 'systemd', 'tyhld-guard-sync.service')
|
|
981
990
|
_tmr = os.path.join(_conductor, 'systemd', 'tyhld-guard-sync.timer')
|
|
@@ -124,6 +124,33 @@ with tempfile.TemporaryDirectory() as tmp:
|
|
|
124
124
|
# ★控えの中身(パス)以外の余計な物を載せない=秘密は1文字も入らない。
|
|
125
125
|
check('本文にトークンは入らない', CFG['token'] in q, False)
|
|
126
126
|
|
|
127
|
+
# ── ★2種類目=配り元が正規版でない(便 4061dd71)────────────────────────────
|
|
128
|
+
print('\n【★2種類目=配り元の配り損ね(便 4061dd71)】')
|
|
129
|
+
DIST_NG = ('2026-09-16T12:00:00+0900 status=failed reason=version-drift what=dist-source '
|
|
130
|
+
'version=0.12.6 published=0.12.8 root=/n/@tyhld/conductor')
|
|
131
|
+
with tempfile.TemporaryDirectory() as tmp:
|
|
132
|
+
link = FakeLink()
|
|
133
|
+
sn.run(write(tmp, 'dist.last', DIST_NG + '\n'), datalink=link)
|
|
134
|
+
p = link.sent[0]
|
|
135
|
+
q = p['question']
|
|
136
|
+
check_true('見出しが配り元の話になっている', '安全装置を配れていません' in p['summary'])
|
|
137
|
+
check_true('本文に「いま止まっているもの」がある', 'いま止まっているもの:' in q)
|
|
138
|
+
check_true('本文に「再開のしかた」がある', '再開のしかた:' in q)
|
|
139
|
+
check_true('何を配れていないかが書いてある', '番人' in q and '権限ルール' in q)
|
|
140
|
+
check_true('うまくいかなかった理由が載る', 'version-drift' in q)
|
|
141
|
+
check_true('鍵が種類ごとに分かれている', p['requestKey'].startswith('dist-source-unverified:'))
|
|
142
|
+
# ★鉄則の配り損ねとは別の鍵=2種類が同じ1枚に潰れない。
|
|
143
|
+
link2 = FakeLink()
|
|
144
|
+
sn.run(write(tmp, 'skill.last', NG_LINE + '\n'), datalink=link2)
|
|
145
|
+
check('鉄則の配り損ねとは別の鍵', p['requestKey'] == link2.sent[0]['requestKey'], False)
|
|
146
|
+
# ★知らない種類は、従来どおり鉄則の配り損ねとして扱う(増やしても壊れない)。
|
|
147
|
+
link3 = FakeLink()
|
|
148
|
+
unknown = DIST_NG.replace('what=dist-source', 'what=まだ無い種類')
|
|
149
|
+
sn.run(write(tmp, 'unknown.last', unknown + '\n'), datalink=link3)
|
|
150
|
+
check_true('知らない種類でも落ちずにカードは出る', bool(link3.sent))
|
|
151
|
+
check_true('知らない種類は従来の文面へ倒れる',
|
|
152
|
+
'職人の決まりが古いままです' in link3.sent[0]['summary'])
|
|
153
|
+
|
|
127
154
|
# ── (f) 知らせに失敗しても同期は止めない ────────────────────────────────────
|
|
128
155
|
print('\n【(f) 知らせに失敗しても同期を止めない】')
|
|
129
156
|
with tempfile.TemporaryDirectory() as tmp:
|