peertable 0.3.3 → 0.3.5
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/README.ja.md +1 -1
- package/README.md +1 -1
- package/package.json +2 -1
- package/room/client.mjs +23 -9
- package/room/server.mjs +93 -18
- package/skill/SKILL.md +89 -15
- package/skill/scripts/archive-room-log.py +3 -1
- package/skill/scripts/change-effort.sh +139 -0
- package/skill/scripts/launch-seat.sh +80 -0
- package/skill/scripts/parent-join.sh +4 -19
- package/skill/scripts/run-bridge.mjs +259 -0
- package/skill/scripts/seat-status-bridge.mjs +34 -14
- package/skill/scripts/seat-usage.mjs +26 -0
- package/skill/scripts/setup.sh +49 -2
- package/skill/scripts/teardown.sh +139 -1
- package/skill/scripts/wakeup-bridge.mjs +5 -4
- package/skill/templates/charter.md +6 -6
- package/skill/templates/done.sh +114 -5
- package/skill/templates/member-standalone.md +7 -3
- package/skill/templates/member.md +100 -6
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
const TOKEN_HINT = /[↓↑]\s*([0-9]+(?:\.[0-9]+)?)\s*([kKmM]?)\s*tokens\b/gu
|
|
2
|
+
|
|
3
|
+
const TOKEN_MULTIPLIER = Object.freeze({
|
|
4
|
+
'': 1,
|
|
5
|
+
k: 1_000,
|
|
6
|
+
m: 1_000_000,
|
|
7
|
+
})
|
|
8
|
+
|
|
9
|
+
export function supportsMemberObservation(payload) {
|
|
10
|
+
return payload?.capabilities?.member_observation_v1 === true
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
/**
|
|
14
|
+
* paneのstatus行が公開しているtoken値だけを読む。
|
|
15
|
+
* vendor固有のログや課金単価は推測せず、表示が無い席はnullのままにする。
|
|
16
|
+
*/
|
|
17
|
+
export function parsePaneTokenHint(pane) {
|
|
18
|
+
if (typeof pane !== 'string') return null
|
|
19
|
+
let latest = null
|
|
20
|
+
for (const match of pane.matchAll(TOKEN_HINT)) {
|
|
21
|
+
const multiplier = TOKEN_MULTIPLIER[match[2].toLowerCase()]
|
|
22
|
+
const value = Math.round(Number(match[1]) * multiplier)
|
|
23
|
+
if (Number.isSafeInteger(value) && value >= 0) latest = value
|
|
24
|
+
}
|
|
25
|
+
return latest
|
|
26
|
+
}
|
package/skill/scripts/setup.sh
CHANGED
|
@@ -51,6 +51,29 @@ else
|
|
|
51
51
|
mode=lattice
|
|
52
52
|
fi
|
|
53
53
|
|
|
54
|
+
# Lattice 併用モードは、公開CLIをprojectへ何か置く前に確定する。通常はglobal installされた
|
|
55
|
+
# lattice を使い、release前のsource treeを実測する時だけ `LATTICE_CLI` で同じtreeのbinを明示する。
|
|
56
|
+
# **work-order adapter binary はもう要らない**(配車を撤去したので登録しない)。ここで見るのは
|
|
57
|
+
# CLI の存在だけで、席が `todo status` / `run intake` を叩ける前提の確認である。
|
|
58
|
+
lattice_cli=""
|
|
59
|
+
if [ "$mode" = "lattice" ]; then
|
|
60
|
+
lattice_cli="${LATTICE_CLI:-$(command -v lattice 2>/dev/null || true)}"
|
|
61
|
+
[ -n "$lattice_cli" ] || { echo "ERROR: lattice CLI が見つからない" >&2; exit 1; }
|
|
62
|
+
[ -x "$lattice_cli" ] || { echo "ERROR: lattice CLI が実行可能fileでない: $lattice_cli" >&2; exit 1; }
|
|
63
|
+
lattice_cli=$(node -e 'process.stdout.write(require("node:fs").realpathSync(process.argv[1]))' "$lattice_cli")
|
|
64
|
+
|
|
65
|
+
# config_refはgit root相対の公開契約。subdirectoryをprojectとして受けると別の
|
|
66
|
+
# `.lattice/` を作ってしまうので、黙って親repoへ登録せずtypedに止める。
|
|
67
|
+
project_root=$(node -e 'process.stdout.write(require("node:fs").realpathSync(process.argv[1]))' "$proj")
|
|
68
|
+
git_root=$(git -C "$proj" rev-parse --show-toplevel 2>/dev/null || true)
|
|
69
|
+
[ -n "$git_root" ] || { echo "ERROR: Lattice 併用モードのprojectはgit repositoryでなければならない: $proj" >&2; exit 1; }
|
|
70
|
+
git_root=$(node -e 'process.stdout.write(require("node:fs").realpathSync(process.argv[1]))' "$git_root")
|
|
71
|
+
[ "$project_root" = "$git_root" ] || {
|
|
72
|
+
echo "ERROR: project_dirはgit rootを指さなければならない: project=$project_root git_root=$git_root" >&2
|
|
73
|
+
exit 1
|
|
74
|
+
}
|
|
75
|
+
fi
|
|
76
|
+
|
|
54
77
|
mkdir -p "$tdir/roles"
|
|
55
78
|
cp "$tpl/charter.md" "$tdir/CLAUDE.md"
|
|
56
79
|
if [ "$mode" = "standalone" ]; then
|
|
@@ -92,6 +115,27 @@ fi
|
|
|
92
115
|
|
|
93
116
|
lattice_preexisting=false
|
|
94
117
|
[ -d "$proj/.lattice" ] && lattice_preexisting=true
|
|
118
|
+
runtime_preexisting=false
|
|
119
|
+
[ -d "$proj/.lattice/runtime" ] && runtime_preexisting=true
|
|
120
|
+
|
|
121
|
+
# adapter registry/config/spool はhost固有のruntime stateであり、sourceとして追跡しない。
|
|
122
|
+
# `.lattice/`の一部を正本として追跡するprojectでもruntimeだけをroot相対で除外する。
|
|
123
|
+
added_runtime_exclude=false
|
|
124
|
+
if [ "$mode" = "lattice" ] && [ -d "$proj/.git" ] \
|
|
125
|
+
&& ! grep -qx '/\.lattice/runtime/' "$proj/.git/info/exclude" 2>/dev/null; then
|
|
126
|
+
mkdir -p "$proj/.git/info"
|
|
127
|
+
echo '/.lattice/runtime/' >> "$proj/.git/info/exclude"
|
|
128
|
+
added_runtime_exclude=true
|
|
129
|
+
fi
|
|
130
|
+
|
|
131
|
+
# **機械配車の口はもう作らない。** 2026-08-09 のオーナー裁定(改・裁定1)で、Lattice が席へ
|
|
132
|
+
# 仕事を配る向きは撤回された。席は自分で `todo start` してから `run intake` するので、
|
|
133
|
+
# work-order adapter の登録も spool(orders/reports)も要らない。**setup がここで adapter を
|
|
134
|
+
# 必須化していると、adapter binary が無い環境で新しい卓が立たなくなる**(配車をしないのに)。
|
|
135
|
+
# 既存 project に前の卓が作った registry が残っていても触らない——他 adapter や進行中 run の
|
|
136
|
+
# 所有物を含みうるので、setup が消す対象ではない。
|
|
137
|
+
work_order_adapter=false
|
|
138
|
+
work_order_spool_ref=""
|
|
95
139
|
|
|
96
140
|
# Lattice 併用モードだけ、工程表の右ペインへ円卓を差す(決定53・明示的コネクタ)。
|
|
97
141
|
# 公開URL基底は `PEERTABLE_PUBLIC_URL`(クオ環境: https://peertable.kitepon.dev)。
|
|
@@ -113,6 +157,9 @@ if [ ${#phases[@]} -gt 0 ]; then
|
|
|
113
157
|
phases_json="[${phases_json%,}]"
|
|
114
158
|
fi
|
|
115
159
|
|
|
116
|
-
|
|
117
|
-
|
|
160
|
+
# **解決した CLI の実 path を残す。** 残さないと、席も teardown も PATH の `lattice` へ逸れる。
|
|
161
|
+
# release 前の source tree を実測する卓では、それは**pull 系 command を持たない古い install**で、
|
|
162
|
+
# 手順どおり打っても届かない(suzune の監査で実測・room [1037])。
|
|
163
|
+
printf '{"room":"%s","server_url":"%s","public_url":"%s","mode":"%s","plan_key":"%s","phases":%s,"added_exclude":%s,"lattice_preexisting":%s,"runtime_preexisting":%s,"added_runtime_exclude":%s,"added_root_mcp":%s,"added_mcp_exclude":%s,"external_pane":%s,"project_json_preexisting":%s,"work_order_adapter":%s,"work_order_spool_ref":"%s","lattice_cli":"%s"}\n' \
|
|
164
|
+
"$room" "$url" "$public_url" "$mode" "$plan" "$phases_json" "$added_exclude" "$lattice_preexisting" "$runtime_preexisting" "$added_runtime_exclude" "$added_root_mcp" "$added_mcp_exclude" "$external_pane" "$project_json_preexisting" "$work_order_adapter" "$work_order_spool_ref" "$lattice_cli" > "$tdir/setup-state.json"
|
|
118
165
|
echo "scaffold done: $tdir"
|
|
@@ -26,6 +26,10 @@ room=$(python3 -c "import json;print(json.load(open('$state'))['room'])")
|
|
|
26
26
|
url=$(python3 -c "import json;print(json.load(open('$state'))['server_url'])")
|
|
27
27
|
added=$(python3 -c "import json;print(json.load(open('$state'))['added_exclude'])")
|
|
28
28
|
lat_pre=$(python3 -c "import json;print(json.load(open('$state'))['lattice_preexisting'])")
|
|
29
|
+
runtime_pre=$(python3 -c "import json;d=json.load(open('$state'));print(d.get('runtime_preexisting', True))")
|
|
30
|
+
added_runtime_ex=$(python3 -c "import json;d=json.load(open('$state'));print(d.get('added_runtime_exclude', False))")
|
|
31
|
+
work_order_adapter=$(python3 -c "import json;d=json.load(open('$state'));print(d.get('work_order_adapter', False))")
|
|
32
|
+
work_order_spool_ref=$(python3 -c "import json;d=json.load(open('$state'));print(d.get('work_order_spool_ref', ''))")
|
|
29
33
|
# 旧 state(added_root_mcp 不在・手動フォールバック時代の root_mcp_json_fallback)も読む
|
|
30
34
|
added_mcp=$(python3 -c "import json;d=json.load(open('$state'));print(d.get('added_root_mcp', d.get('root_mcp_json_fallback', False)))")
|
|
31
35
|
added_mcp_ex=$(python3 -c "import json;d=json.load(open('$state'));print(d.get('added_mcp_exclude', d.get('root_mcp_json_fallback', False)))")
|
|
@@ -100,6 +104,132 @@ else
|
|
|
100
104
|
skip "seat-status-bridge(起動記録なし)"
|
|
101
105
|
fi
|
|
102
106
|
|
|
107
|
+
# run 可視化ブリッジ(Lattice の実行層を使う卓だけ立っている)。同じ理由で `.team/` を消す前に止める。
|
|
108
|
+
# 止め残すと、卓が無いのに run の進行と介入を投稿し続ける常駐が残る
|
|
109
|
+
if [ -f "$proj/.team/run-bridge.json" ]; then
|
|
110
|
+
if node "$(dirname "$0")/run-bridge.mjs" "$proj" --stop; then
|
|
111
|
+
did "run-bridge 停止"
|
|
112
|
+
else
|
|
113
|
+
miss "run-bridge 停止に失敗(常駐が残る)— 上の _STOP_FAILED を見て手で止める"
|
|
114
|
+
fi
|
|
115
|
+
else
|
|
116
|
+
skip "run-bridge(起動記録なし)"
|
|
117
|
+
fi
|
|
118
|
+
|
|
119
|
+
# run の close は成果が既定branchへ着地した証拠ではない。bridgeを止めてから、`run list`が挙げる
|
|
120
|
+
# **active run** の landing report を読む。**旧版は spool の work order から run を逆算していた**が、
|
|
121
|
+
# 配車が無くなって order が出ないので、装置に直接聞く形へ変えた(改・裁定1)。
|
|
122
|
+
# **`run list` は closed run を返さない**(実装で除外される)ので、ここで見えるのは未 close の分だけ
|
|
123
|
+
# である。closed 済み run の着地は `run close` の返値が landing report を含むので**その時点で読む**。
|
|
124
|
+
# `.lattice/runs` を直に走査して補わない——consumer contract 違反で、旧 orders の保持は配車の復活になる。
|
|
125
|
+
# 全 closed の再列挙が要るなら Lattice 側の公開面を足す別課題であって、ここで迂回実装しない。
|
|
126
|
+
# 未着地・未pushは判断結果なので `lattice run landing` 自体はexit 0を返し、teardownも止めない。
|
|
127
|
+
# **CLI の出力と exit code を先に単独で取る。** `cmd | python3` にすると pipeline の rc は
|
|
128
|
+
# python のものになり、**CLI が rc≠0 で typed error JSON を返しても parser が `active_runs` 欠落を
|
|
129
|
+
# 空配列として飲んで「active run なし」に化ける**(suzune の監査で実測・room [948])。
|
|
130
|
+
# 沈黙する偽 green は、着地の見落としをそのまま「確認済み」に見せる——いちばん避けたい壊れ方である。
|
|
131
|
+
lattice_cli="${LATTICE_CLI:-$(command -v lattice 2>/dev/null || true)}"
|
|
132
|
+
run_list_json=""
|
|
133
|
+
run_list_rc=0
|
|
134
|
+
if [ -n "$lattice_cli" ] && [ -x "$lattice_cli" ]; then
|
|
135
|
+
run_list_json=$(cd "$proj" && "$lattice_cli" run list --json 2>&1) || run_list_rc=$?
|
|
136
|
+
fi
|
|
137
|
+
if [ -z "$lattice_cli" ] || [ ! -x "$lattice_cli" ]; then
|
|
138
|
+
miss "run landing — LATTICE_CLIが実行可能fileを指さず、着地状態を読めない: ${lattice_cli:-未設定}"
|
|
139
|
+
elif [ "$run_list_rc" != "0" ]; then
|
|
140
|
+
miss "run landing — run list が rc=${run_list_rc} で失敗: $(printf '%s' "$run_list_json" | head -c 200)"
|
|
141
|
+
elif ! run_refs=$(printf '%s' "$run_list_json" | python3 -c '
|
|
142
|
+
import json, sys
|
|
143
|
+
|
|
144
|
+
# schema と active_runs を要求する。**typed error JSON も別 schema も黙って空扱いにしない。**
|
|
145
|
+
# f-string の中でバックスラッシュ付きの引用符を使わない——shell の single-quoted `python3 -c`
|
|
146
|
+
# へ `\"` がそのまま届き、**Python が SyntaxError で落ちる**(kanade の監査で実測・room [957])。
|
|
147
|
+
# 値は先に変数へ取り出して、f-string には名前だけを置く。
|
|
148
|
+
raw = sys.stdin.read()
|
|
149
|
+
try:
|
|
150
|
+
listed = json.loads(raw)
|
|
151
|
+
except json.JSONDecodeError as error:
|
|
152
|
+
sys.exit(f"run list がJSONでない: {error}")
|
|
153
|
+
if not isinstance(listed, dict):
|
|
154
|
+
sys.exit(f"run list がobjectでない: {type(listed).__name__}")
|
|
155
|
+
actual_schema = listed.get("schema")
|
|
156
|
+
if actual_schema != "lattice.run_list.v1":
|
|
157
|
+
sys.exit(f"run list の schema が違う: {actual_schema}")
|
|
158
|
+
active = listed.get("active_runs")
|
|
159
|
+
if not isinstance(active, list):
|
|
160
|
+
sys.exit(f"run list に active_runs 配列が無い: {type(active).__name__}")
|
|
161
|
+
# **entry を filter で捨てない。** 1件でも読めない entry があれば、active な run を落として
|
|
162
|
+
# 「なし」に見せることになる(suzune の監査で実測・room [956])。全件を要求して、
|
|
163
|
+
# 満たさなければ非ゼロで落ちる。外部 versioned JSON の境界で fallback しない。
|
|
164
|
+
refs = []
|
|
165
|
+
for index, entry in enumerate(active):
|
|
166
|
+
if not isinstance(entry, dict):
|
|
167
|
+
sys.exit(f"active_runs[{index}] がobjectでない: {type(entry).__name__}")
|
|
168
|
+
ref = entry.get("run_ref")
|
|
169
|
+
if not isinstance(ref, str) or not ref:
|
|
170
|
+
sys.exit(f"active_runs[{index}] に run_ref 文字列が無い")
|
|
171
|
+
refs.append(ref)
|
|
172
|
+
print("\n".join(sorted(refs)))
|
|
173
|
+
' 2>&1); then
|
|
174
|
+
miss "run landing — run listを解釈できない: $(printf '%s' "$run_refs" | head -c 200)"
|
|
175
|
+
elif [ -z "$run_refs" ]; then
|
|
176
|
+
skip "run landing(active runなし)"
|
|
177
|
+
else
|
|
178
|
+
while IFS= read -r run_ref; do
|
|
179
|
+
[ -n "$run_ref" ] || continue
|
|
180
|
+
if landing_report=$(cd "$proj" && "$lattice_cli" run landing --run "$run_ref" 2>&1); then
|
|
181
|
+
unlanded_count=""
|
|
182
|
+
if unlanded_count=$(printf '%s' "$landing_report" | python3 -c '
|
|
183
|
+
import json, sys
|
|
184
|
+
|
|
185
|
+
raw = sys.stdin.read()
|
|
186
|
+
try:
|
|
187
|
+
report = json.loads(raw)
|
|
188
|
+
except json.JSONDecodeError as error:
|
|
189
|
+
sys.exit(f"run landing がJSONでない: {error}")
|
|
190
|
+
if not isinstance(report, dict):
|
|
191
|
+
sys.exit(f"run landing がobjectでない: {type(report).__name__}")
|
|
192
|
+
actual_schema = report.get("schema")
|
|
193
|
+
if actual_schema != "lattice.run_landing_report.v1":
|
|
194
|
+
sys.exit(f"run landing の schema が違う: {actual_schema}")
|
|
195
|
+
receipts = report.get("accepted_receipts")
|
|
196
|
+
if not isinstance(receipts, list):
|
|
197
|
+
sys.exit("run landing に accepted_receipts 配列が無い")
|
|
198
|
+
for index, receipt in enumerate(receipts):
|
|
199
|
+
if not isinstance(receipt, dict) or not isinstance(receipt.get("landed"), bool):
|
|
200
|
+
sys.exit(f"accepted_receipts[{index}] の landed が真偽値でない")
|
|
201
|
+
print(sum(1 for receipt in receipts if not receipt["landed"]))
|
|
202
|
+
' 2>&1); then
|
|
203
|
+
did "run landing ${landing_report}"
|
|
204
|
+
if [ "$unlanded_count" != 0 ]; then
|
|
205
|
+
echo "未着地 ${unlanded_count}本: run ${run_ref} の受理済み成果が canonical default branch へ着地していない" >&2
|
|
206
|
+
fi
|
|
207
|
+
else
|
|
208
|
+
miss "run landing $run_ref — reportを解釈できない: ${unlanded_count}"
|
|
209
|
+
fi
|
|
210
|
+
else
|
|
211
|
+
miss "run landing $run_ref — ${landing_report}"
|
|
212
|
+
fi
|
|
213
|
+
done <<EOF
|
|
214
|
+
$run_refs
|
|
215
|
+
EOF
|
|
216
|
+
fi
|
|
217
|
+
|
|
218
|
+
# setupが新しく作ったhost固有runtimeだけを撤去する。既存runtimeは他adapterや進行中runの
|
|
219
|
+
# 所有物を含み得るので触らない。runtimeを先に消してからexcludeを戻し、teardown後に
|
|
220
|
+
# untracked stateが露出する順序逆転を防ぐ。
|
|
221
|
+
# **配車を撤去した後の setup は `.lattice/runtime/` を作らない**ので、新しい卓ではここは常に
|
|
222
|
+
# skip になる。判定を残してあるのは、**配車時代に立てた卓を畳む時**にだけ効くからである
|
|
223
|
+
# (その卓の setup-state は `work_order_adapter: true` を持つ)。
|
|
224
|
+
if yes_ "$work_order_adapter" && ! yes_ "$runtime_pre"; then
|
|
225
|
+
rm -rf "$proj/.lattice/runtime"
|
|
226
|
+
did ".lattice/runtime/ 撤去(配車時代の setup が新規作成したhost固有state)"
|
|
227
|
+
elif yes_ "$work_order_adapter"; then
|
|
228
|
+
skip ".lattice/runtime/(setup 以前から存在)"
|
|
229
|
+
else
|
|
230
|
+
skip ".lattice/runtime/(setup は runtime state を作っていない)"
|
|
231
|
+
fi
|
|
232
|
+
|
|
103
233
|
# 外部ペイン(決定53)。`.team/` を消す前に戻す——退避先が `.team/` の中にある
|
|
104
234
|
ext=$(python3 -c "import json;print(json.load(open('$state')).get('external_pane', False))")
|
|
105
235
|
pj_pre=$(python3 -c "import json;print(json.load(open('$state')).get('project_json_preexisting', False))")
|
|
@@ -121,7 +251,7 @@ if [ "$mode" = archive ]; then
|
|
|
121
251
|
python3 -c "
|
|
122
252
|
import json,sys,urllib.request
|
|
123
253
|
req=urllib.request.Request('$url/api/$room/messages', method='POST',
|
|
124
|
-
data=json.dumps({'from':'system','to':'
|
|
254
|
+
data=json.dumps({'from':'system','to':'system','body':'''$body'''}).encode(),
|
|
125
255
|
headers={'Content-Type':'application/json','X-Peertable-Token':'$PEERTABLE_POST_TOKEN'})
|
|
126
256
|
urllib.request.urlopen(req, timeout=10).read()
|
|
127
257
|
" 2>/dev/null && did "解散の区切りを履歴へ" || skip "解散の区切り(投稿できず・撤去は続行)"
|
|
@@ -179,6 +309,14 @@ else
|
|
|
179
309
|
skip "exclude の /.mcp.json(setup が足していない)"
|
|
180
310
|
fi
|
|
181
311
|
|
|
312
|
+
if yes_ "$added_runtime_ex"; then
|
|
313
|
+
grep -vx '/\.lattice/runtime/' "$proj/.git/info/exclude" > "$proj/.git/info/exclude.tmp" || true
|
|
314
|
+
mv "$proj/.git/info/exclude.tmp" "$proj/.git/info/exclude"
|
|
315
|
+
did "exclude から /.lattice/runtime/ を撤去"
|
|
316
|
+
else
|
|
317
|
+
skip "exclude の /.lattice/runtime/(setup が足していない)"
|
|
318
|
+
fi
|
|
319
|
+
|
|
182
320
|
if yes_ "$added"; then
|
|
183
321
|
grep -vx '\.team/' "$proj/.git/info/exclude" > "$proj/.git/info/exclude.tmp" || true
|
|
184
322
|
mv "$proj/.git/info/exclude.tmp" "$proj/.git/info/exclude"
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
|
-
// Codex 席の起床ブリッジ。room の SSE
|
|
2
|
+
// Codex 席の起床ブリッジ。room の SSE を購読し、明示的にその席宛の新着が来たら
|
|
3
3
|
// tmux の席へ素送信して起こす。Claude 席は channels が同じ役をするので対象外。
|
|
4
4
|
//
|
|
5
5
|
// usage: wakeup-bridge.mjs <project_dir> <seat> [seat...] 起動(前面。nohup で常駐させる)
|
|
@@ -66,9 +66,10 @@ const pending = new Map(seats.map(s => [s, []]))
|
|
|
66
66
|
|
|
67
67
|
async function wake(seat, msgs) {
|
|
68
68
|
const last = msgs[msgs.length - 1]
|
|
69
|
+
const audience = Array.isArray(last.to_names) ? last.to_names.join(', ') : last.to
|
|
69
70
|
const text = msgs.length === 1
|
|
70
|
-
? `room に新着あり(${last.from} → ${
|
|
71
|
-
: `room に新着 ${msgs.length} 件(最新: ${last.from} → ${
|
|
71
|
+
? `room に新着あり(${last.from} → ${audience})。read_unread で読むこと。`
|
|
72
|
+
: `room に新着 ${msgs.length} 件(最新: ${last.from} → ${audience})。read_unread で読むこと。`
|
|
72
73
|
try {
|
|
73
74
|
await run('tmux', ['-S', sock, 'send-keys', '-t', `peer-${seat}`, text])
|
|
74
75
|
await sleep(400)
|
|
@@ -91,7 +92,7 @@ setInterval(async () => {
|
|
|
91
92
|
function dispatch(msg) {
|
|
92
93
|
for (const seat of seats) {
|
|
93
94
|
if (msg.from === seat) continue
|
|
94
|
-
if (msg.
|
|
95
|
+
if (Array.isArray(msg.to_names) ? !msg.to_names.includes(seat) : msg.to !== seat) continue
|
|
95
96
|
pending.get(seat).push(msg)
|
|
96
97
|
}
|
|
97
98
|
}
|
|
@@ -1,13 +1,13 @@
|
|
|
1
1
|
# チーム憲章
|
|
2
2
|
|
|
3
|
-
|
|
3
|
+
これはチーム作業である。自分が引き受けたタスクの完了はミッションの完了ではない。全タスク完了までチームは解散しない。
|
|
4
4
|
|
|
5
|
-
1. 拘束力を持つのは**工程正本への記録**と room
|
|
6
|
-
2. 進捗は room に一行で報告する: claim
|
|
7
|
-
3. claim
|
|
8
|
-
4. 分からないことは room
|
|
5
|
+
1. 拘束力を持つのは**工程正本への記録**と room ログに残った決定だけ(工程正本=Lattice 併用モードなら Lattice の todo 記録、単独円卓モードなら room の宣言そのもの)。宛先は影響を受けるメンバーだけを明示する。room ログは全員がpullで読めるので、broadcastによる周知を決定の成立条件にしない
|
|
6
|
+
2. 進捗は room に一行で報告する: claim・join、完了時・詰まり自覚時・方針変更時。task 外でも成果物になる作業(実測・検証・CI)は着手前に一言。仲間を五里霧中に置かない
|
|
7
|
+
3. **仕事を選ぶのも始めるのも自分である。** どのモードでも、`read_log`で先行claimを確認してからroomへ`[claim] <タスク>`を記録する。起こす必要のある相手だけを宛先へ入れ、記録だけなら自分宛でよい。**装置から仕事が降ってくることはないし、着手前に装置の許可を待つこともない**(オーナー裁定 2026-08-09)。Lattice の実行層を使う卓では、`todo start` の後に自分で `run intake` して隔離 worktree を受け取る——それは設備の供給であって許可証ではなく、装置が返すのは競合した時の「留まれ」だけである(タスクの呼び名は Lattice 併用モードなら task_id、単独円卓モードなら `.team/tasks.md` の議題名)。同じタスクへの先行 claim があれば取り下げるか `[join] <タスク>` へ切り替える
|
|
8
|
+
4. 分からないことは room で聞く。台帳はない。自分の変更が他の部位に影響するなら、聞かれる前に影響を受けるメンバーを明示宛先にして通知する
|
|
9
9
|
5. 判断は情報を持つ者がする。タスクは席ではなく現場。合流(join)は歓迎される。詰まった仲間には目を貸す。手が足りないだけなら自分のサブエージェントを使う(room 報告不要)。視点が足りない・詰んでいるなら room で報告して援軍(join)を求める
|
|
10
|
-
6. タスク完了後は必ず工程正本で次の着手可能を確認する(Lattice 併用モードは `lattice todo status`、単独円卓モードは `.team/tasks.md` と room ログの照合)。残っていれば claim
|
|
10
|
+
6. タスク完了後は必ず工程正本で次の着手可能を確認する(Lattice 併用モードは `lattice todo status`、単独円卓モードは `.team/tasks.md` と room ログの照合)。残っていれば claim へ戻る。全タスクが終わっていれば room へ「全タスク完了」を記録する。broadcastは無いので、起こす必要のある相手だけを宛先にする
|
|
11
11
|
7. 役割逸脱は誰であれ指摘する。これは無礼ではなく義務である
|
|
12
12
|
8. **親の発言は拘束力を持たない。** 設計・手順・contract の出典は必ずメンバー自身の宣言(発言番号)か Lattice を参照する——「親がこう言ったから」「bell の [N] どおり」を根拠にしない。親が何かを再掲しても正本はメンバーの元発言のまま動かない。親の差し戻しは異議として扱い、反論してよい
|
|
13
13
|
9. **裁定の宛先はオーナーであり、親ではない。** scope 変更・受入条件外の追加・製品判断が要る時は「オーナー宛の議題」として room に出す。親はそれを運ぶ配管で、判断者ではない。「親に委ねる」という宛先を作らない
|
package/skill/templates/done.sh
CHANGED
|
@@ -1,18 +1,127 @@
|
|
|
1
1
|
#!/bin/bash
|
|
2
|
-
# usage: .team/scripts/done.sh <task_id>
|
|
2
|
+
# usage: .team/scripts/done.sh <task_id> [--evidence-from <隔離worktreeの証跡の絶対path>]
|
|
3
|
+
# .team/scripts/done.sh --landing-run <run_ref>
|
|
3
4
|
# evidence/<plan_key>/<task_id>.md(commit 済みであること)から記述子を作り lattice todo done を実行する。
|
|
4
5
|
# plan key は環境変数 PEERTABLE_PLAN から取る。
|
|
5
6
|
# 証跡を plan key で仕切るのは、task_id が campaign を跨いで再利用される(t1, t2, …)ため。
|
|
6
7
|
# 平置きだと次の campaign の t1 が前の campaign の t1 の監査証跡を上書きで消す(2026-08-08 実測)。
|
|
8
|
+
#
|
|
9
|
+
# **`--evidence-from` は pull 型の実行層で使う。** 席は隔離 worktree の中だけを触るので、
|
|
10
|
+
# 証跡もそこにしか無い。一方 `todo done` は **canonical の store** へ打たないと、run の accept が
|
|
11
|
+
# その done を見ない。cwd 1つで両方を兼ねると必ずどちらかが外れる(mio の監査で実測・room [1012]):
|
|
12
|
+
# canonical で打つ → worktree にしか無い証跡を読めない
|
|
13
|
+
# worktree で打つ → worktree 側の `.lattice/todo` を書き、canonical の accept が見ない
|
|
14
|
+
# なので **証跡の blob/digest は worktree の file から、`todo done` は canonical の cwd/store へ**、と
|
|
15
|
+
# 明示的に分ける。**canonical へ証跡を別書きして通すのは禁止**——「worktree の中だけ」の契約を
|
|
16
|
+
# 破りながら green にする偽装になる。
|
|
17
|
+
#
|
|
18
|
+
# 成立する理由: linked worktree は canonical と object DB を共有するので、canonical の cwd から
|
|
19
|
+
# `git hash-object -w <worktree の絶対path>` で書いた blob はそのまま canonical で読める。
|
|
20
|
+
# evidence verifier は descriptor.path の working tree 実在を見ず、object DB の blob と digest、
|
|
21
|
+
# 読み出し時の `rev-list --all` 到達性を見る(mio が実 repo で確認・room [1016])。
|
|
7
22
|
set -e
|
|
23
|
+
# `todo done` と run receipt の accept は別の正本を持つ。landing-only mode は accept の直後に
|
|
24
|
+
# 同じ run ref を受け取り、受理済み receipt の着地だけを表示する。accept 自体はここへ吸収しない。
|
|
25
|
+
if [ "${1:-}" = "--landing-run" ]; then
|
|
26
|
+
[ "$#" = 2 ] || {
|
|
27
|
+
echo "ERROR: --landing-run には run ref を1つ渡すこと(usage: done.sh --landing-run <run_ref>)" >&2
|
|
28
|
+
exit 1
|
|
29
|
+
}
|
|
30
|
+
run_ref="$2"
|
|
31
|
+
[ -n "$run_ref" ] || { echo "ERROR: --landing-run には run ref を渡すこと" >&2; exit 1; }
|
|
32
|
+
lattice_cli="${LATTICE_CLI:-$(command -v lattice 2>/dev/null || true)}"
|
|
33
|
+
if [ -z "$lattice_cli" ] || [ ! -x "$lattice_cli" ]; then
|
|
34
|
+
echo "着地状態を読めない: LATTICE_CLIが実行可能fileを指さない(${lattice_cli:-未設定})" >&2
|
|
35
|
+
exit 0
|
|
36
|
+
fi
|
|
37
|
+
landing_report=""
|
|
38
|
+
landing_rc=0
|
|
39
|
+
landing_report=$("$lattice_cli" run landing --run "$run_ref" 2>&1) || landing_rc=$?
|
|
40
|
+
if [ "$landing_rc" != 0 ]; then
|
|
41
|
+
echo "着地状態を読めない: run landing が rc=${landing_rc} で失敗: ${landing_report}" >&2
|
|
42
|
+
exit 0
|
|
43
|
+
fi
|
|
44
|
+
unlanded_count=""
|
|
45
|
+
if ! unlanded_count=$(printf '%s' "$landing_report" | python3 -c '
|
|
46
|
+
import json, sys
|
|
47
|
+
raw = sys.stdin.read()
|
|
48
|
+
try:
|
|
49
|
+
report = json.loads(raw)
|
|
50
|
+
except json.JSONDecodeError as error:
|
|
51
|
+
sys.exit(f"run landing がJSONでない: {error}")
|
|
52
|
+
if not isinstance(report, dict):
|
|
53
|
+
sys.exit(f"run landing がobjectでない: {type(report).__name__}")
|
|
54
|
+
actual_schema = report.get("schema")
|
|
55
|
+
if actual_schema != "lattice.run_landing_report.v1":
|
|
56
|
+
sys.exit(f"run landing の schema が違う: {actual_schema}")
|
|
57
|
+
receipts = report.get("accepted_receipts")
|
|
58
|
+
if not isinstance(receipts, list):
|
|
59
|
+
sys.exit("run landing に accepted_receipts 配列が無い")
|
|
60
|
+
for index, receipt in enumerate(receipts):
|
|
61
|
+
if not isinstance(receipt, dict) or not isinstance(receipt.get("landed"), bool):
|
|
62
|
+
sys.exit(f"accepted_receipts[{index}] の landed が真偽値でない")
|
|
63
|
+
print(sum(1 for receipt in receipts if not receipt["landed"]))
|
|
64
|
+
' 2>&1); then
|
|
65
|
+
echo "着地状態を読めない: ${unlanded_count}" >&2
|
|
66
|
+
exit 0
|
|
67
|
+
fi
|
|
68
|
+
if [ "$unlanded_count" != 0 ]; then
|
|
69
|
+
echo "未着地 ${unlanded_count}本: run ${run_ref} の受理済み成果が canonical default branch へ着地していない" >&2
|
|
70
|
+
fi
|
|
71
|
+
exit 0
|
|
72
|
+
fi
|
|
73
|
+
# **引数の形を exact に要求する。** 緩く受けると、`--evidnce-from` のような typo が
|
|
74
|
+
# 「option 無し」として通り、**canonical 側の同名証跡を黙って hash する**——別 file を
|
|
75
|
+
# 受理させておいて green に見える(kanade の監査で実測・room [1029])。
|
|
76
|
+
# 1引数(既定経路)か、3引数(`<task> --evidence-from <絶対path>`)だけを許す。
|
|
77
|
+
case $# in
|
|
78
|
+
1) ;;
|
|
79
|
+
3) [ "$2" = "--evidence-from" ] || {
|
|
80
|
+
echo "ERROR: 未知のoption: $2(使えるのは --evidence-from だけ)" >&2; exit 1; } ;;
|
|
81
|
+
*) echo "ERROR: 引数の形が違う(usage: done.sh <task_id> [--evidence-from <絶対path>] | done.sh --landing-run <run_ref>)" >&2; exit 1 ;;
|
|
82
|
+
esac
|
|
8
83
|
t="$1"
|
|
84
|
+
[ -n "$t" ] || { echo "ERROR: task_id が空" >&2; exit 1; }
|
|
85
|
+
evidence_from=""
|
|
86
|
+
if [ "$#" = 3 ]; then
|
|
87
|
+
evidence_from="$3"
|
|
88
|
+
[ -n "$evidence_from" ] || { echo "ERROR: --evidence-from には証跡fileの絶対pathを渡すこと" >&2; exit 1; }
|
|
89
|
+
case "$evidence_from" in
|
|
90
|
+
/*) ;;
|
|
91
|
+
*) echo "ERROR: --evidence-from は絶対pathでなければならない: $evidence_from" >&2; exit 1 ;;
|
|
92
|
+
esac
|
|
93
|
+
# **黙って canonical の証跡へ落ちない。** 落ちると「worktree の成果を done した」と見えるのに
|
|
94
|
+
# 実際は別の file を hash することになり、受理された内容と成果物が食い違う
|
|
95
|
+
[ -f "$evidence_from" ] || { echo "ERROR: --evidence-from の証跡が存在しない: $evidence_from" >&2; exit 1; }
|
|
96
|
+
# **object DB を共有していない木の file は hash-object できても意味が無い。** 別 repo の
|
|
97
|
+
# 証跡を渡された時に「書けたから成立した」と読まないよう、common git dir の一致を要求する
|
|
98
|
+
# (kanade の設計指摘・room [1018])。linked worktree なら両者は同じ絶対 path を指す。
|
|
99
|
+
here_common=$(git rev-parse --path-format=absolute --git-common-dir)
|
|
100
|
+
from_common=$(git -C "$(dirname "$evidence_from")" rev-parse --path-format=absolute --git-common-dir 2>/dev/null || true)
|
|
101
|
+
[ -n "$from_common" ] && [ "$here_common" = "$from_common" ] || {
|
|
102
|
+
echo "ERROR: --evidence-from が同じrepoのworktreeでない(object DBを共有していない)" >&2
|
|
103
|
+
echo " canonical: ${here_common}" >&2
|
|
104
|
+
echo " evidence : ${from_common:-(git worktree ではない)}" >&2
|
|
105
|
+
exit 1
|
|
106
|
+
}
|
|
107
|
+
fi
|
|
108
|
+
|
|
109
|
+
# descriptor の path は repo 内の相対(repo 外の絶対 path は --evidence が INVALID_ARGUMENTS で弾く)。
|
|
110
|
+
# worktree でも canonical でも同じ相対 path に置く規約なので、この値は両者で一致する。
|
|
9
111
|
f="evidence/$PEERTABLE_PLAN/$t.md"
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
112
|
+
src="${evidence_from:-$f}"
|
|
113
|
+
[ -f "$src" ] || { echo "ERROR: 証跡が見つからない: $src" >&2; exit 1; }
|
|
114
|
+
oid=$(git hash-object -w "$src")
|
|
115
|
+
digest=$(shasum -a 256 "$src" | cut -d' ' -f1)
|
|
13
116
|
tmp=".ev-$t.json"
|
|
117
|
+
# **失敗しても記述子を残さない。** `set -e` の下で `todo done` が落ちると、後段の `rm` へ
|
|
118
|
+
# 到達せず repo に `.ev-<task>.json` が残る(自分の負側 test で実測。TASK_NOT_FOUND の後に
|
|
119
|
+
# untracked file が残った)。次に `git status` を撮った人が、それを誰かの作業中変更と読む。
|
|
120
|
+
trap 'rm -f "$tmp"' EXIT
|
|
14
121
|
printf '{"evidence_id":"ev-%s","repo_id":"self","path":"%s","git_blob_oid":"%s","content_digest":"%s","media_type":"text/markdown","anchor_digest":null}\n' "$t" "$f" "$oid" "$digest" > "$tmp"
|
|
15
|
-
lattice
|
|
122
|
+
# **PATH の `lattice` へ黙って逸れない。** setup が解決した CLI を席 env の `LATTICE_CLI` で受け、
|
|
123
|
+
# 無い時だけ PATH を使う(bridge の `--lattice` / teardown の `LATTICE_CLI` と同じ選択規律)。
|
|
124
|
+
"${LATTICE_CLI:-lattice}" todo done --plan "$PEERTABLE_PLAN" --task "$t" --evidence "$tmp"
|
|
16
125
|
rm -f "$tmp"
|
|
17
126
|
|
|
18
127
|
# 完了の定義は「repo 内の変更は push まで」。done を打つ瞬間はそれが成り立っていなければ
|
|
@@ -7,14 +7,18 @@
|
|
|
7
7
|
## 作業ループ
|
|
8
8
|
|
|
9
9
|
1. `.team/tasks.md` で議題を見る。`read_log` で既存の claim と完了報告を照合し、まだ誰も持っていないものを選ぶ
|
|
10
|
-
2.
|
|
11
|
-
3.
|
|
10
|
+
2. 憲章の手順で`read_log`から先行claimを確認し、roomに`[claim] <タスク>`を記録する。起こす必要のある相手だけを宛先へ入れ、記録だけなら自分宛でよい。**`[claim]` は独立した1発言で出す**——完了報告や他タスクの話と同じ発言に畳まない。宣言としては有効でも、後から機械的に追えなくなり、監査が「宣言が無い」と誤読する(2026-08-08 実測)。単独モードでは room ログが唯一の正本なので、より効く
|
|
11
|
+
3. 実装する。インターフェースなど他タスクに影響する決定は、影響を受けるメンバーを明示宛先にして一行で共有する
|
|
12
12
|
4. 完了手順:
|
|
13
13
|
- 何を作り、どう確認したかを自分で確かめる(テスト・実行・実測。「たぶん動く」で閉じない)
|
|
14
14
|
- 変更ファイルを `git add` して commit する(メッセージは日本語一行。対象ファイルを明示して他人の作業中変更を巻き込まない)
|
|
15
|
-
5. room へ `[done] <タスク>`
|
|
15
|
+
5. room へ `[done] <タスク>` を記録する(何を作り、どう確認したかを一行で添える)。監査を頼む相手だけを宛先にし、相手が未定なら自分宛でよい。**この報告が完了の唯一の記録である**——書かなければ完了していない
|
|
16
16
|
6. 1 へ戻る
|
|
17
17
|
|
|
18
|
+
## effortを変更してほしい時
|
|
19
|
+
|
|
20
|
+
作業を安全に中断できる状態にしてから、親だけへ`[effort変更依頼] <level>`を明示DMする。親が変更すると席は再起動し、会話contextは引き継がれない。再起動後は下の再着任手順でrole・`.team/tasks.md`・roomログから現在地を取り直す。自分でCLI設定を変えたり、broadcastで依頼したりしない。
|
|
21
|
+
|
|
18
22
|
## 再着任(context が要約されたら)
|
|
19
23
|
|
|
20
24
|
自分の context が要約された(=会話の前半が手元に無い)と気づいたら、実装を続ける前に `.team/roles/member.md` と `.team/CLAUDE.md` を読み直して着任し直し、room へ `[再着任] <名前>` を一行投稿する。進行中 claim の状態は自分の記憶でなく**工程正本で取り直す**——この卓の工程正本は room の宣言だけなので、`read_log` で自分の claim・他人の claim・完了報告を全部照合する(機械に問い合わせる先は無い)。記憶と正本が食い違ったら、正本を正として食い違いを room で報告する。
|