peertable 0.3.2 → 0.3.4
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 +3 -1
- package/README.md +2 -2
- package/package.json +2 -1
- package/room/client.mjs +24 -7
- package/room/server.mjs +23 -7
- package/skill/SKILL.md +71 -2
- package/skill/scripts/archive-room-log.py +59 -0
- package/skill/scripts/run-bridge.mjs +601 -0
- package/skill/scripts/setup.sh +96 -2
- package/skill/scripts/teardown.sh +127 -8
- package/skill/scripts/wakeup-bridge.mjs +3 -1
- package/skill/templates/charter.md +4 -4
- package/skill/templates/member.md +35 -3
package/skill/scripts/setup.sh
CHANGED
|
@@ -51,6 +51,39 @@ else
|
|
|
51
51
|
mode=lattice
|
|
52
52
|
fi
|
|
53
53
|
|
|
54
|
+
# Lattice 併用モードは、登録に使う公開CLIと同梱work-order binaryを、projectへ
|
|
55
|
+
# 何か置く前に確定する。通常はglobal installされた lattice の隣を使う。
|
|
56
|
+
# release前のsource treeを実測する時だけ、2つのenvで同じtreeのbinを明示できる。
|
|
57
|
+
lattice_cli=""
|
|
58
|
+
work_order_binary=""
|
|
59
|
+
node_binary=""
|
|
60
|
+
if [ "$mode" = "lattice" ]; then
|
|
61
|
+
lattice_cli="${LATTICE_CLI:-$(command -v lattice 2>/dev/null || true)}"
|
|
62
|
+
[ -n "$lattice_cli" ] || { echo "ERROR: lattice CLI が見つからない" >&2; exit 1; }
|
|
63
|
+
[ -x "$lattice_cli" ] || { echo "ERROR: lattice CLI が実行可能fileでない: $lattice_cli" >&2; exit 1; }
|
|
64
|
+
lattice_cli=$(node -e 'process.stdout.write(require("node:fs").realpathSync(process.argv[1]))' "$lattice_cli")
|
|
65
|
+
|
|
66
|
+
work_order_binary="${LATTICE_WORK_ORDER_ADAPTER_BINARY:-$(dirname "$lattice_cli")/lattice-work-order-adapter.mjs}"
|
|
67
|
+
[ -f "$work_order_binary" ] && [ -x "$work_order_binary" ] || {
|
|
68
|
+
echo "ERROR: Lattice work-order adapter binary が見つからないか実行不能: $work_order_binary" >&2
|
|
69
|
+
exit 1
|
|
70
|
+
}
|
|
71
|
+
work_order_binary=$(node -e 'process.stdout.write(require("node:fs").realpathSync(process.argv[1]))' "$work_order_binary")
|
|
72
|
+
node_binary=$(node -e 'process.stdout.write(require("node:fs").realpathSync(process.execPath))')
|
|
73
|
+
[ -x "$node_binary" ] || { echo "ERROR: Node executable が実行可能fileでない: $node_binary" >&2; exit 1; }
|
|
74
|
+
|
|
75
|
+
# config_refはgit root相対の公開契約。subdirectoryをprojectとして受けると別の
|
|
76
|
+
# `.lattice/` を作ってしまうので、黙って親repoへ登録せずtypedに止める。
|
|
77
|
+
project_root=$(node -e 'process.stdout.write(require("node:fs").realpathSync(process.argv[1]))' "$proj")
|
|
78
|
+
git_root=$(git -C "$proj" rev-parse --show-toplevel 2>/dev/null || true)
|
|
79
|
+
[ -n "$git_root" ] || { echo "ERROR: Lattice 併用モードのprojectはgit repositoryでなければならない: $proj" >&2; exit 1; }
|
|
80
|
+
git_root=$(node -e 'process.stdout.write(require("node:fs").realpathSync(process.argv[1]))' "$git_root")
|
|
81
|
+
[ "$project_root" = "$git_root" ] || {
|
|
82
|
+
echo "ERROR: project_dirはgit rootを指さなければならない: project=$project_root git_root=$git_root" >&2
|
|
83
|
+
exit 1
|
|
84
|
+
}
|
|
85
|
+
fi
|
|
86
|
+
|
|
54
87
|
mkdir -p "$tdir/roles"
|
|
55
88
|
cp "$tpl/charter.md" "$tdir/CLAUDE.md"
|
|
56
89
|
if [ "$mode" = "standalone" ]; then
|
|
@@ -92,6 +125,67 @@ fi
|
|
|
92
125
|
|
|
93
126
|
lattice_preexisting=false
|
|
94
127
|
[ -d "$proj/.lattice" ] && lattice_preexisting=true
|
|
128
|
+
runtime_preexisting=false
|
|
129
|
+
[ -d "$proj/.lattice/runtime" ] && runtime_preexisting=true
|
|
130
|
+
|
|
131
|
+
# adapter registry/config/spool はhost固有のruntime stateであり、sourceとして追跡しない。
|
|
132
|
+
# `.lattice/`の一部を正本として追跡するprojectでもruntimeだけをroot相対で除外する。
|
|
133
|
+
added_runtime_exclude=false
|
|
134
|
+
if [ "$mode" = "lattice" ] && [ -d "$proj/.git" ] \
|
|
135
|
+
&& ! grep -qx '/\.lattice/runtime/' "$proj/.git/info/exclude" 2>/dev/null; then
|
|
136
|
+
mkdir -p "$proj/.git/info"
|
|
137
|
+
echo '/.lattice/runtime/' >> "$proj/.git/info/exclude"
|
|
138
|
+
added_runtime_exclude=true
|
|
139
|
+
fi
|
|
140
|
+
|
|
141
|
+
# managed run の仕事口をLattice runtime stateとして用意する。configを`.team/`
|
|
142
|
+
# に置くとarchive teardownでregistryだけが残って壊れるため、registryと同じ
|
|
143
|
+
# `.lattice/runtime/`の寿命へ揃える。席はこのspoolへ直接触れない。
|
|
144
|
+
work_order_adapter=false
|
|
145
|
+
work_order_spool_ref=""
|
|
146
|
+
if [ "$mode" = "lattice" ]; then
|
|
147
|
+
work_order_root="$proj/.lattice/runtime/work-order-adapter"
|
|
148
|
+
work_order_spool="$work_order_root/spool"
|
|
149
|
+
work_order_config="$work_order_root/config.json"
|
|
150
|
+
work_order_registration="$tdir/work-order-adapter-registration.json"
|
|
151
|
+
work_order_config_ref=".lattice/runtime/work-order-adapter/config.json"
|
|
152
|
+
work_order_spool_ref=".lattice/runtime/work-order-adapter/spool"
|
|
153
|
+
|
|
154
|
+
mkdir -p "$work_order_spool/orders" "$work_order_spool/reports"
|
|
155
|
+
chmod 700 "$work_order_root" "$work_order_spool" "$work_order_spool/orders" "$work_order_spool/reports"
|
|
156
|
+
work_order_spool=$(node -e 'process.stdout.write(require("node:fs").realpathSync(process.argv[1]))' "$work_order_spool")
|
|
157
|
+
|
|
158
|
+
node -e '
|
|
159
|
+
const { writeFileSync } = require("node:fs");
|
|
160
|
+
const [target, spool] = process.argv.slice(1);
|
|
161
|
+
writeFileSync(target, `${JSON.stringify({
|
|
162
|
+
schema: "lattice.work_order_adapter_config.v1",
|
|
163
|
+
spool_dir: spool,
|
|
164
|
+
})}\n`, { mode: 0o600 });
|
|
165
|
+
' "$work_order_config" "$work_order_spool"
|
|
166
|
+
chmod 600 "$work_order_config"
|
|
167
|
+
node -e '
|
|
168
|
+
const { writeFileSync } = require("node:fs");
|
|
169
|
+
const [target, binary, script, configRef] = process.argv.slice(1);
|
|
170
|
+
writeFileSync(target, `${JSON.stringify({
|
|
171
|
+
schema: "lattice.runtime_adapter_registration_input.v2",
|
|
172
|
+
adapter_kind: "work-order",
|
|
173
|
+
launch_kind: "host_binary",
|
|
174
|
+
binary_path: binary,
|
|
175
|
+
argv: [script],
|
|
176
|
+
config_ref: configRef,
|
|
177
|
+
host_driven_epoch: true,
|
|
178
|
+
})}\n`, { mode: 0o600 });
|
|
179
|
+
' "$work_order_registration" "$node_binary" "$work_order_binary" "$work_order_config_ref"
|
|
180
|
+
chmod 600 "$work_order_registration"
|
|
181
|
+
|
|
182
|
+
(
|
|
183
|
+
cd "$proj"
|
|
184
|
+
"$lattice_cli" run adapter register --input "$work_order_registration"
|
|
185
|
+
)
|
|
186
|
+
work_order_adapter=true
|
|
187
|
+
echo "work-order adapter: binary=$node_binary argv=$work_order_binary config=$work_order_config_ref spool=$work_order_spool_ref" >&2
|
|
188
|
+
fi
|
|
95
189
|
|
|
96
190
|
# Lattice 併用モードだけ、工程表の右ペインへ円卓を差す(決定53・明示的コネクタ)。
|
|
97
191
|
# 公開URL基底は `PEERTABLE_PUBLIC_URL`(クオ環境: https://peertable.kitepon.dev)。
|
|
@@ -113,6 +207,6 @@ if [ ${#phases[@]} -gt 0 ]; then
|
|
|
113
207
|
phases_json="[${phases_json%,}]"
|
|
114
208
|
fi
|
|
115
209
|
|
|
116
|
-
printf '{"room":"%s","server_url":"%s","public_url":"%s","mode":"%s","plan_key":"%s","phases":%s,"added_exclude":%s,"lattice_preexisting":%s,"added_root_mcp":%s,"added_mcp_exclude":%s,"external_pane":%s,"project_json_preexisting":%s}\n' \
|
|
117
|
-
"$room" "$url" "$public_url" "$mode" "$plan" "$phases_json" "$added_exclude" "$lattice_preexisting" "$added_root_mcp" "$added_mcp_exclude" "$external_pane" "$project_json_preexisting" > "$tdir/setup-state.json"
|
|
210
|
+
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"}\n' \
|
|
211
|
+
"$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" > "$tdir/setup-state.json"
|
|
118
212
|
echo "scaffold done: $tdir"
|
|
@@ -1,17 +1,34 @@
|
|
|
1
1
|
#!/bin/bash
|
|
2
|
-
# Peertable teardown
|
|
3
|
-
# usage: teardown.sh <project_dir>
|
|
2
|
+
# Peertable teardown の機械部分。席(tmux)も本 script が畳む。
|
|
3
|
+
# usage: teardown.sh <project_dir> [--purge]
|
|
4
|
+
# 既定(archive): room のログを `docs/archive/` へ書き出し、`.lattice/` は残して畳む
|
|
5
|
+
# --purge : 何も残さず全部消す(従来の痕跡ゼロ・ゲスト project 向け)
|
|
4
6
|
# 書込トークンは環境変数 PEERTABLE_POST_TOKEN から取る(`~/.config/peertable.env` は export 付きで定義すること)。
|
|
5
7
|
#
|
|
6
8
|
# 撤去は「何が実施され、何が実施されなかったか」を1行ずつ出す。room 削除だけがトークンを要するので、
|
|
7
9
|
# そこが失敗しても残りの撤去は続行し、未実施を明示して非ゼロで終わる(黙って中断しない・決定58)。
|
|
10
|
+
#
|
|
11
|
+
# **既定が archive なのはオーナー裁定(2026-08-09・決定61)**。痕跡ゼロを既定にしていた時代は、
|
|
12
|
+
# 畳んだ瞬間に room の会話ログ(server 側の正本)と Lattice store が消えていた——**卓の議論と
|
|
13
|
+
# 工程の記録は、卓そのものより寿命が長い**。ゲスト project を汚さない不可侵原則は `--purge` が持つ。
|
|
8
14
|
set -e
|
|
9
15
|
proj="$1"
|
|
16
|
+
mode=archive
|
|
17
|
+
for arg in "${@:2}"; do
|
|
18
|
+
case "$arg" in
|
|
19
|
+
--purge) mode=purge ;;
|
|
20
|
+
--archive) mode=archive ;;
|
|
21
|
+
*) echo "ERROR: 未知の引数: ${arg}(受けるのは --purge / --archive だけ)" >&2; exit 1 ;;
|
|
22
|
+
esac
|
|
23
|
+
done
|
|
10
24
|
state="$proj/.team/setup-state.json"
|
|
11
25
|
room=$(python3 -c "import json;print(json.load(open('$state'))['room'])")
|
|
12
26
|
url=$(python3 -c "import json;print(json.load(open('$state'))['server_url'])")
|
|
13
27
|
added=$(python3 -c "import json;print(json.load(open('$state'))['added_exclude'])")
|
|
14
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))")
|
|
15
32
|
# 旧 state(added_root_mcp 不在・手動フォールバック時代の root_mcp_json_fallback)も読む
|
|
16
33
|
added_mcp=$(python3 -c "import json;d=json.load(open('$state'));print(d.get('added_root_mcp', d.get('root_mcp_json_fallback', False)))")
|
|
17
34
|
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)))")
|
|
@@ -22,6 +39,42 @@ skip() { echo "teardown: [スキップ] $*"; }
|
|
|
22
39
|
miss() { echo "teardown: [未実施] $*" >&2; fail=1; }
|
|
23
40
|
yes_() { [ "$1" = "True" ] || [ "$1" = "true" ]; }
|
|
24
41
|
|
|
42
|
+
echo "teardown: mode=${mode}(archive=ログとstoreを残す/purge=痕跡ゼロ)"
|
|
43
|
+
|
|
44
|
+
# ---- ログの控え(archive だけ)。room 自体は残るので、これは repo 側の写し ----
|
|
45
|
+
# 失敗しても撤去は続ける(room に原本があるため)。--purge の時だけ「消す前の最後の機会」になる
|
|
46
|
+
log_saved=skip
|
|
47
|
+
if [ "$mode" = archive ]; then
|
|
48
|
+
arc="$proj/docs/archive"
|
|
49
|
+
out="$arc/room-log_${room}_$(date +%Y%m%d-%H%M%S).md"
|
|
50
|
+
mkdir -p "$arc"
|
|
51
|
+
if python3 "$(dirname "$0")/archive-room-log.py" "$url" "$room" "$out"; then
|
|
52
|
+
did "room ログの写しを ${out#"$proj/"} へ(原本は room に残る)"
|
|
53
|
+
log_saved=yes
|
|
54
|
+
else
|
|
55
|
+
miss "room ログの写しに失敗($url/api/$room が読めない)— **原本は room に残っている**ので撤去は続行する"
|
|
56
|
+
log_saved=no
|
|
57
|
+
fi
|
|
58
|
+
fi
|
|
59
|
+
|
|
60
|
+
# ---- 席(tmux)の終了。**`.team/` を消す前**に、この room の member だけを畳む ----
|
|
61
|
+
# `peer-*` を全部畳むと、同じマシンの別の卓を巻き込む(bridge が members 起点にしているのと同じ理由)
|
|
62
|
+
sock="${PEERTABLE_TMUX_SOCKET:-${TMPDIR}claude-tmux-sockets/claude.sock}"
|
|
63
|
+
seats=$(python3 "$(dirname "$0")/archive-room-log.py" --members "$url" "$room" 2>/dev/null || true)
|
|
64
|
+
if [ -z "$seats" ]; then
|
|
65
|
+
miss "席の終了 — member 一覧が取れず、畳む相手を特定できない。手で確認: tmux -S \"$sock\" list-sessions | grep peer-"
|
|
66
|
+
else
|
|
67
|
+
closed=0
|
|
68
|
+
for name in $seats; do
|
|
69
|
+
if tmux -S "$sock" has-session -t "peer-$name" 2>/dev/null; then
|
|
70
|
+
tmux -S "$sock" kill-session -t "peer-$name" && closed=$((closed + 1))
|
|
71
|
+
fi
|
|
72
|
+
done
|
|
73
|
+
if [ "$closed" -gt 0 ]; then did "席の終了(${closed}席)"; else skip "席の終了(生きている席なし)"; fi
|
|
74
|
+
left=$(tmux -S "$sock" list-sessions 2>/dev/null | grep -c '^peer-' || true)
|
|
75
|
+
[ "${left:-0}" -eq 0 ] || echo "teardown: [注記] 他の卓の peer-* が ${left}件 残っている(この卓のものではないので畳まない)"
|
|
76
|
+
fi
|
|
77
|
+
|
|
25
78
|
# Codex 席の起床ブリッジ(決定54)。常駐 process なので、`.team/` を消す前に確実に止める
|
|
26
79
|
if [ -f "$proj/.team/wakeup-bridge.json" ]; then
|
|
27
80
|
# 停止に失敗しても **ここで止まらない**。`set -e` で落ちると、t6 の契約(各段の実施・未実施を
|
|
@@ -50,12 +103,66 @@ else
|
|
|
50
103
|
skip "seat-status-bridge(起動記録なし)"
|
|
51
104
|
fi
|
|
52
105
|
|
|
106
|
+
# 配車ブリッジ(managed run に載せた卓だけ立っている)。同じ理由で `.team/` を消す前に止める。
|
|
107
|
+
# 止め残すと、spool を見張り続ける常駐が次の run の order を拾って**卓が無いのに配車を投稿する**
|
|
108
|
+
if [ -f "$proj/.team/run-bridge.json" ]; then
|
|
109
|
+
if node "$(dirname "$0")/run-bridge.mjs" "$proj" --stop; then
|
|
110
|
+
did "run-bridge 停止"
|
|
111
|
+
else
|
|
112
|
+
miss "run-bridge 停止に失敗(常駐が残る)— 上の _STOP_FAILED を見て手で止める"
|
|
113
|
+
fi
|
|
114
|
+
else
|
|
115
|
+
skip "run-bridge(起動記録なし)"
|
|
116
|
+
fi
|
|
117
|
+
|
|
118
|
+
# setupが新しく作ったhost固有runtimeだけを撤去する。既存runtimeは他adapterや進行中runの
|
|
119
|
+
# 所有物を含み得るので触らない。runtimeを先に消してからexcludeを戻し、teardown後に
|
|
120
|
+
# untracked stateが露出する順序逆転を防ぐ。
|
|
121
|
+
if yes_ "$work_order_adapter" && ! yes_ "$runtime_pre"; then
|
|
122
|
+
rm -rf "$proj/.lattice/runtime"
|
|
123
|
+
did ".lattice/runtime/ 撤去(setup が新規作成したhost固有state)"
|
|
124
|
+
elif yes_ "$work_order_adapter"; then
|
|
125
|
+
skip ".lattice/runtime/(setup 以前から存在)"
|
|
126
|
+
else
|
|
127
|
+
skip ".lattice/runtime/(work-order adapter登録なし)"
|
|
128
|
+
fi
|
|
129
|
+
|
|
53
130
|
# 外部ペイン(決定53)。`.team/` を消す前に戻す——退避先が `.team/` の中にある
|
|
54
131
|
ext=$(python3 -c "import json;print(json.load(open('$state')).get('external_pane', False))")
|
|
55
132
|
pj_pre=$(python3 -c "import json;print(json.load(open('$state')).get('project_json_preexisting', False))")
|
|
56
133
|
|
|
57
|
-
#
|
|
58
|
-
|
|
134
|
+
# ---- 解散(archive): **部屋は残し、メンバー登録だけ外す** ----
|
|
135
|
+
# 円卓の解散は「部屋を畳む」ではなく「集まりが散る」。部屋は場所であって、次の campaign も
|
|
136
|
+
# 同じ部屋で続く——**過去ログが同じ部屋の履歴として繋がり、部屋は常に一つに見える**
|
|
137
|
+
# (オーナー裁定 2026-08-09・決定61)。参加者一覧だけ空にして、席が戻れば再登録される
|
|
138
|
+
if [ "$mode" = archive ]; then
|
|
139
|
+
if [ -z "${PEERTABLE_POST_TOKEN:-}" ]; then
|
|
140
|
+
miss "メンバー登録の解除 — TOKEN_MISSING: \`~/.config/peertable.env\` の定義が \`export\` 付きでないと子 process へ渡らない"
|
|
141
|
+
echo "teardown: [手当] 参加者は次で外せる: curl -X DELETE \"$url/api/$room/members/<名前>\" -H \"X-Peertable-Token: \$PEERTABLE_POST_TOKEN\"" >&2
|
|
142
|
+
elif [ -z "$seats" ]; then
|
|
143
|
+
skip "メンバー登録の解除(一覧が取れていない)"
|
|
144
|
+
else
|
|
145
|
+
# 履歴に解散の区切りを残す。**部屋が続く以上、どこで卓が変わったかが読めないと
|
|
146
|
+
# 過去ログが一続きの会話に見えてしまう**(次の campaign の発言と地続きになる)
|
|
147
|
+
body="解散。この卓はここまで。参加者: ${seats}。部屋と過去ログはこのまま残り、次の卓も同じ部屋で続く。"
|
|
148
|
+
python3 -c "
|
|
149
|
+
import json,sys,urllib.request
|
|
150
|
+
req=urllib.request.Request('$url/api/$room/messages', method='POST',
|
|
151
|
+
data=json.dumps({'from':'system','to':'all','body':'''$body'''}).encode(),
|
|
152
|
+
headers={'Content-Type':'application/json','X-Peertable-Token':'$PEERTABLE_POST_TOKEN'})
|
|
153
|
+
urllib.request.urlopen(req, timeout=10).read()
|
|
154
|
+
" 2>/dev/null && did "解散の区切りを履歴へ" || skip "解散の区切り(投稿できず・撤去は続行)"
|
|
155
|
+
n=0
|
|
156
|
+
for name in $seats; do
|
|
157
|
+
c=$(curl -s -o /dev/null -w '%{http_code}' -X DELETE "$url/api/$room/members/$name" -H "X-Peertable-Token: $PEERTABLE_POST_TOKEN" || true)
|
|
158
|
+
[ "$c" = 200 ] && n=$((n + 1))
|
|
159
|
+
done
|
|
160
|
+
did "メンバー登録の解除(${n}名)— **部屋と過去ログは残す**($url/$room)"
|
|
161
|
+
fi
|
|
162
|
+
# room 削除は --purge だけ。トークンを要する唯一の段で、ここだけが外部サービスへの依存境界
|
|
163
|
+
elif [ "$log_saved" = no ]; then
|
|
164
|
+
miss "room 削除 $room — ログを保全できていないので消さない(保全より先に消すと会話は二度と戻らない)"
|
|
165
|
+
elif [ -z "${PEERTABLE_POST_TOKEN:-}" ]; then
|
|
59
166
|
miss "room 削除 $room — TOKEN_MISSING: PEERTABLE_POST_TOKEN が空。\`~/.config/peertable.env\` の定義が \`export\` 付きでないと子 process へ渡らない"
|
|
60
167
|
else
|
|
61
168
|
code=$(curl -s -o /dev/null -w '%{http_code}' -X DELETE "$url/api/$room" -H "X-Peertable-Token: $PEERTABLE_POST_TOKEN" || true)
|
|
@@ -99,6 +206,14 @@ else
|
|
|
99
206
|
skip "exclude の /.mcp.json(setup が足していない)"
|
|
100
207
|
fi
|
|
101
208
|
|
|
209
|
+
if yes_ "$added_runtime_ex"; then
|
|
210
|
+
grep -vx '/\.lattice/runtime/' "$proj/.git/info/exclude" > "$proj/.git/info/exclude.tmp" || true
|
|
211
|
+
mv "$proj/.git/info/exclude.tmp" "$proj/.git/info/exclude"
|
|
212
|
+
did "exclude から /.lattice/runtime/ を撤去"
|
|
213
|
+
else
|
|
214
|
+
skip "exclude の /.lattice/runtime/(setup が足していない)"
|
|
215
|
+
fi
|
|
216
|
+
|
|
102
217
|
if yes_ "$added"; then
|
|
103
218
|
grep -vx '\.team/' "$proj/.git/info/exclude" > "$proj/.git/info/exclude.tmp" || true
|
|
104
219
|
mv "$proj/.git/info/exclude.tmp" "$proj/.git/info/exclude"
|
|
@@ -107,11 +222,15 @@ else
|
|
|
107
222
|
skip "exclude の .team/(setup が足していない)"
|
|
108
223
|
fi
|
|
109
224
|
|
|
110
|
-
if [ "$lat_pre" = "
|
|
111
|
-
rm -rf "$proj/.lattice"
|
|
112
|
-
did ".lattice/ 削除(setup が作ったもの)"
|
|
113
|
-
else
|
|
225
|
+
if [ "$lat_pre" = "True" ] || [ "$lat_pre" = "true" ]; then
|
|
114
226
|
skip ".lattice/(setup 以前から存在)"
|
|
227
|
+
elif [ "$mode" = archive ]; then
|
|
228
|
+
# 工程正本を残すのが archive の本体。**残すだけでは git が知らない**ので、追跡へ入れるのは人の判断
|
|
229
|
+
did ".lattice/ を残す(工程正本・\`lattice todo status\` と \`gantt serve\` が読む)"
|
|
230
|
+
echo "teardown: [注記] .lattice/ は git 追跡外のままなら次の clone に残らない。残すなら commit すること"
|
|
231
|
+
else
|
|
232
|
+
rm -rf "$proj/.lattice"
|
|
233
|
+
did ".lattice/ 削除(setup が作ったもの・--purge)"
|
|
115
234
|
fi
|
|
116
235
|
|
|
117
236
|
if [ "$fail" -ne 0 ]; then
|
|
@@ -91,7 +91,9 @@ setInterval(async () => {
|
|
|
91
91
|
function dispatch(msg) {
|
|
92
92
|
for (const seat of seats) {
|
|
93
93
|
if (msg.from === seat) continue
|
|
94
|
-
|
|
94
|
+
// 複数人宛は `to_names` が実宛先を持つ(server は旧 client のために `to` を 'all' へ倒す)。
|
|
95
|
+
// ここで実宛先を見ないと、名指しされていない Codex 席まで起こしてしまう。
|
|
96
|
+
if (Array.isArray(msg.to_names) ? !msg.to_names.includes(seat) : (msg.to !== 'all' && msg.to !== seat)) continue
|
|
95
97
|
pending.get(seat).push(msg)
|
|
96
98
|
}
|
|
97
99
|
}
|
|
@@ -1,13 +1,13 @@
|
|
|
1
1
|
# チーム憲章
|
|
2
2
|
|
|
3
|
-
|
|
3
|
+
これはチーム作業である。自分が引き受けたタスクの完了はミッションの完了ではない。全タスク完了までチームは解散しない。
|
|
4
4
|
|
|
5
5
|
1. 拘束力を持つのは**工程正本への記録**と room での決定だけ(工程正本=Lattice 併用モードなら Lattice の todo 記録、単独円卓モードなら room の宣言そのもの)。DM(個別宛)で決まったことは決定ではない。部位を跨ぐ合意・設計判断は必ず room 全員宛で行う
|
|
6
|
-
2. 進捗は room に一行で報告する: claim
|
|
7
|
-
3. claim
|
|
6
|
+
2. 進捗は room に一行で報告する: managed run なら配車への受諾・辞退、その他の卓なら claim・join、共通して完了時・詰まり自覚時・方針変更時。task 外でも成果物になる作業(実測・検証・CI)は着手前に一言。仲間を五里霧中に置かない
|
|
7
|
+
3. 仕事を引き受ける手順は実行方式で分かれる。managed run では task を Lattice、候補席を bridge が選び、`[配車] <タスク> → <席>` はその提示を全員へ見せるだけで割当ではない。提示された席が `[受諾] <タスク>` を返した時だけ束縛が成立し、`[辞退] <タスク> <理由>` なら bridge が別席へ再配車する。この経路で `[claim]` を重ねない。managed run でない卓では、room へ `[claim] <タスク>` を全員宛で投稿し、直後に read_unread で直前ログを確認する(タスクの呼び名は Lattice 併用モードなら task_id、単独円卓モードなら `.team/tasks.md` の議題名)。同じタスクへの先行 claim があれば取り下げるか `[join] <タスク>` へ切り替える
|
|
8
8
|
4. 分からないことは room で聞く。台帳はない。自分の変更が他の部位に影響するなら、聞かれる前に 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 ログの照合)。残っていれば managed run は次の配車を待ち、その他の卓は claim へ戻る。全タスクが終わっていれば room へ「全タスク完了」を全員宛で宣言する
|
|
11
11
|
7. 役割逸脱は誰であれ指摘する。これは無礼ではなく義務である
|
|
12
12
|
8. **親の発言は拘束力を持たない。** 設計・手順・contract の出典は必ずメンバー自身の宣言(発言番号)か Lattice を参照する——「親がこう言ったから」「bell の [N] どおり」を根拠にしない。親が何かを再掲しても正本はメンバーの元発言のまま動かない。親の差し戻しは異議として扱い、反論してよい
|
|
13
13
|
9. **裁定の宛先はオーナーであり、親ではない。** scope 変更・受入条件外の追加・製品判断が要る時は「オーナー宛の議題」として room に出す。親はそれを運ぶ配管で、判断者ではない。「親に委ねる」という宛先を作らない
|
|
@@ -2,7 +2,9 @@
|
|
|
2
2
|
|
|
3
3
|
あなたはこのプロジェクトの対等なメンバーである。指揮者はいない。判断はメンバーが行う。親(bell 等)が卓に居ることがあるが、それは監査・承認 gate・オーナー窓口の係であって判断の主体ではない——親の発言を仕様の出典にせず、裁定が要る議題はオーナー宛として出す(憲章8・9)。あなたの名前は環境変数 `PEERTABLE_MEMBER` にある。room ツール(post / read_unread / read_log / members)で仲間と話せる。plan key は `{{PLAN_KEY}}`。
|
|
4
4
|
|
|
5
|
-
##
|
|
5
|
+
## 作業ループ(managed run の配車を除く)
|
|
6
|
+
|
|
7
|
+
このループは自分で task を取る卓のもの。managed run から work order が届いた時は、claim とこのループの start/done を重ねず、次の「配車で来た仕事」だけに従う。
|
|
6
8
|
|
|
7
9
|
1. `lattice todo status --json` で ready なタスクを見る。{{CLAIM_SCOPE}}
|
|
8
10
|
2. 憲章の手順で room に claim を宣言する。**`[claim]` は独立した1発言で出す**——完了報告や他タスクの話と同じ発言に畳まない。宣言としては有効でも、後から機械的に追えなくなり、監査が「宣言が無い」と誤読する(2026-08-08 実測)
|
|
@@ -16,15 +18,45 @@
|
|
|
16
18
|
7. **手が空いていて ready が無いなら、他の席の done を監査する。** 実装者以外なら誰でもよい。実物(diff・検証結果・ハーネス)を自分で走らせて所見を room へ出す——**報告を読むだけでは監査にならない**。親は所見を読んで受理を宣言するだけで、コードは読まない
|
|
17
19
|
8. 1 へ戻る
|
|
18
20
|
|
|
21
|
+
## 配車で来た仕事(Lattice の managed run に載っている卓だけ)
|
|
22
|
+
|
|
23
|
+
卓が Lattice の実行層(managed run)に載っている時は、仕事は claim で取り合わず、**task 選択=Lattice・候補席の選択=bridge・受けるかの決定=席**の3層で届く。bridge の全員宛 `[配車] t7 → akari` は提示を可視化しただけで、まだ割当ではない。提示された席が `[受諾] t7` を返した時だけ、その席と work order の束縛が成立する。届き方は自分宛の次の1ブロックである。載っていない卓では配車は起きないので、この節は静かに眠る。
|
|
24
|
+
|
|
25
|
+
```
|
|
26
|
+
[work order] t7
|
|
27
|
+
worktree: /abs/path/to/.lattice/runs/<run>/worktrees/<id>/tree
|
|
28
|
+
base_sha: 8f0e…(40 hex)
|
|
29
|
+
scope_writes: src/a.mjs, test/a.test.mjs
|
|
30
|
+
verifier_refs: node --test test/a.test.mjs
|
|
31
|
+
forbidden_operations: push, branch, merge, rebase, reset, stash
|
|
32
|
+
packet_digest: 3a91…(64 hex)
|
|
33
|
+
```
|
|
34
|
+
|
|
35
|
+
1. **work order の7欄(task・worktree・base・scope・verifier・禁止操作・packet digest)を読んで、受諾か辞退を即返す。** `[受諾] t7` / `[辞退] t7 <理由>` を**独立した1発言**で room 全員宛へ。bridge は行頭一致で機械 parse するので、他の話と同じ発言に畳むと届かない。**辞退は正当な選択**(本筋の WIP が塞がっている・自分の測定器ではその検証ができない)で、bridge が別の席へ再配車する。黙殺だけはしない
|
|
36
|
+
2. **この work order に `[claim]` や `lattice todo start` を重ねて割当を作らない。** task の選択は Lattice、席の選択は bridge、席との束縛は `[受諾]`、実行結果の正本は run の event/receipt がすでに持つ。別に campaign の todo を閉じる手続きがある時は、receipt を確認した後にその手続きとして行うのであって、配車の受諾を二重記録するためではない
|
|
37
|
+
3. **worktree の中だけを、絶対パスで触る。** `cd` しない・env を書き換えない・別 project へ移らない。席の room 接続と MCP 解決は cwd と env に乗っているので、動かすと卓から落ちる(そのために席を動かさない設計になっている)。git は `git -C <worktree> …`、編集は絶対パスで開く
|
|
38
|
+
4. **commit してよい。禁止は `push` / `branch` / `merge` / `rebase` / `reset` / `stash` の6つ**で、正は注入文の `forbidden_operations`(出所は Lattice engine が packet へ載せる実物・`src/runtime-engine.mjs` の `FORBIDDEN_OPERATIONS`)。worktree は `base_sha` の detached HEAD なので、そこへ積む commit は base の子孫のままで canonical branch を動かさない。逆に6つは HEAD を base の子孫から外すか、外部へ効果を出す操作なので、観測の前提か公開契約のどちらかを壊す
|
|
39
|
+
5. **`scope_writes` の外へ書いても黙って弾かれない——`undeclared_write` として観測に出る。** 書く必要があると分かった時点で room へ言う。隠して書いても diff で見えるだけである
|
|
40
|
+
6. **検証は worktree の中で回す。** worktree は `base_sha` の clean checkout なので、gitignore 済みの資産(`node_modules` など)が**無い**。埋めに行く前に次の3点を読むこと。
|
|
41
|
+
- **worktree の中で `npm install` してはいけない。** checkpoint 観測は `git status --ignored=matching` で撮る=**gitignore 済みの書き込みも拾う**(gitignore 経由の scope 迂回を塞ぐ設計)。install した file はそのまま `undeclared_write` になり、diff entry 上限(256)を超えた時点で**観測そのものが失敗する**。自分の task の記録を自分で壊すことになる
|
|
42
|
+
- **依存は install しなくても解決する。** worktree は repo 配下(`<repo>/.lattice/runs/…/tree`)に切られるので、Node の bare specifier 解決が親ディレクトリを遡って canonical の `node_modules` に当たる(repo の外に置かれた木では当たらない)。これは現在の worktree 配置がもたらしている便益であって、どこでも成り立つ性質ではない
|
|
43
|
+
- **当たるのは canonical に入っている版である。** worktree の `package.json` が要求する版とは限らないので、**lockfile や依存を動かす task では、検証結果を「解決された版のずれ」ごと疑う**
|
|
44
|
+
- それでも回らない検証は、無理に回さず room で言う。**動かないからといって canonical tree で回さない**——それは測りたい木ではない
|
|
45
|
+
7. 終わったら **`[完了] t7` を独立した1発言**で room 全員宛へ。bridge がこれを見て report を書き、Lattice が worktree の diff を独立に撮って receipt にする
|
|
46
|
+
|
|
47
|
+
**成果の正本は席の commit ではなく、Lattice が撮った observed diff である。** `[完了]` の後、Lattice が worktree の diff を独立に撮って receipt にする——**受理されるのはその観測であって、あなたの commit ではない**。
|
|
48
|
+
|
|
49
|
+
worktree は最後に `git worktree remove --force` で畳まれ、木ごと消える(commit object は残るが、どの参照からも辿れない=gc の対象)。**畳まれるのは `run close` の時ではなく、supervisor が終了する時である**——close は run を閉じるだけで成果を捨てない(木そのものが run の成果なので、着地させる前に消さない設計)。**それでも「commit したから残る」と思わないこと。** canonical への着地は run の外の別工程であり、`[完了]` も `run close` も着地の宣言ではない。
|
|
50
|
+
|
|
19
51
|
## 再着任(context が要約されたら)
|
|
20
52
|
|
|
21
|
-
自分の context が要約された(=会話の前半が手元に無い)と気づいたら、実装を続ける前に `.team/roles/member.md` と `.team/CLAUDE.md` を読み直して着任し直し、room へ `[再着任] <名前>`
|
|
53
|
+
自分の context が要約された(=会話の前半が手元に無い)と気づいたら、実装を続ける前に `.team/roles/member.md` と `.team/CLAUDE.md` を読み直して着任し直し、room へ `[再着任] <名前>` を一行投稿する。進行中の仕事は自分の記憶でなく**工程正本で取り直す**——managed run なら `run observe` の状態と room の `[配車]` / `[受諾]` / `[辞退]` / `[完了]`、その他の Lattice 併用卓なら `lattice todo status --json` の active と room の claim・完了報告を照合する。記憶と正本が食い違ったら、正本を正として食い違いを room で報告する。
|
|
22
54
|
|
|
23
55
|
## 注意
|
|
24
56
|
|
|
25
57
|
- Lattice の書き込みが `STORE_WRITE_CONFLICT` 等で弾かれたら、1〜2 秒待って同じコマンドを再実行する(同時書込の正常な負け方であり、壊れてはいない)
|
|
26
58
|
- `--parallel-frontier` を付けた start が `parallel_frontier_not_applicable` で弾かれたら、それは**その task がもう `next_ready` に居ない**(他人が着手済み・依存で塞がった)という意味である。フラグの不具合ではないので付け外しで粘らず、`lattice todo status --json` と room ログで claim 状況を確認し直す
|
|
27
|
-
- claim が衝突したら、Lattice の start 記録(誰が in-progress
|
|
59
|
+
- managed run でない卓の claim が衝突したら、Lattice の start 記録(誰が in-progress か)を機械の事実として使う。managed run は claim 衝突で裁定せず、bridge の提示と席の受諾・辞退を見る
|
|
28
60
|
- **note が持つものを room の散文へ二重化しない**。設計メモ・タスク固有の経緯は `lattice todo note` に置き、room には決定と進捗だけを流す
|
|
29
61
|
- room の新着通知が来たら read_unread で読む。返事が要るものには post で応える
|
|
30
62
|
- **Codex 席の場合**: 起床は channels ではなく wakeup-bridge が担う。`room に新着あり(<誰> → <宛先>)。read_unread で読むこと。` が端末へ直接届くので、Claude 席と同じく read_unread で読む。**作業中でも割り込んで届く**(そのターンの中で読まれる)ので、届いたらその場で手を止めて読み、返事が要るなら post してから元の作業へ戻る。自分の発言では起きない
|