peertable 0.4.4 → 0.4.6
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 +1 -1
- package/room/client.mjs +42 -5
- package/skill/SKILL.md +3 -3
- package/skill/scripts/aiterm-configure.mjs +1 -1
- package/skill/scripts/aiterm-launch.mjs +1 -1
- package/skill/scripts/launch-seat.sh +9 -7
- package/skill/templates/done.sh +16 -27
- package/skill/templates/member.md +4 -5
package/README.ja.md
CHANGED
|
@@ -107,7 +107,7 @@ Codex では、スキルが所有する room MCP block をプロジェクトの
|
|
|
107
107
|
|
|
108
108
|
動いており、**自分自身の開発に使っている**。2026-08-08 に end-to-end 検証済み——オーケストレーターなしの完全な一周(2 メンバーが相談し、claim し、インターフェースを交渉し、見つけた罠を共有して小さなプロジェクトを出荷)を**外部介入ゼロ**で完走。2026-08-13の実席ライフサイクルでは、作業席が親を通じてsession contextを保ったままmodel / effortを変更し、再起動後はroomと工程正本から再着任した。2026-08-14にはGrok 4.6席の着席、room参加、同一sessionの4.6↔4.5変更、DM起床を実機で確認した。2026-08-17にGrok席はidle待ち、broadcastは本文を残し、tmuxの無い親でbridge cursorが止まらないよう直した。
|
|
109
109
|
|
|
110
|
-
現在のnpm releaseは **peertable 0.4.
|
|
110
|
+
現在のnpm releaseは **peertable 0.4.6**。
|
|
111
111
|
|
|
112
112
|
設計文書と決定履歴(**87 決定**)は [docs/plan.md](docs/plan.md)。
|
|
113
113
|
|
package/README.md
CHANGED
|
@@ -143,7 +143,7 @@ It interviews you, names the members, scaffolds `.team/` (charter + roles, isola
|
|
|
143
143
|
|
|
144
144
|
Working, and used to build itself. First verified end-to-end on 2026-08-08 with a full no-orchestrator loop: two members consulted, claimed, negotiated an interface, shared a discovered pitfall, and shipped a small project with **zero external intervention**. A 2026-08-13 real-seat lifecycle verified in-place model/effort changes and restart recovery. On 2026-08-14, a Grok 4.6 seat joined the room, changed 4.6↔4.5 in the same session, and woke on a direct message in a live acceptance run. On 2026-08-17 the wake-up path was corrected so Grok seats wait for idle, broadcasts keep their body, and a parent without a tmux seat cannot stall the bridge cursor.
|
|
145
145
|
|
|
146
|
-
The current npm release is **peertable 0.4.
|
|
146
|
+
The current npm release is **peertable 0.4.6**.
|
|
147
147
|
|
|
148
148
|
The design document and decision log (**87 decisions**, in Japanese) live in [docs/plan.md](docs/plan.md).
|
|
149
149
|
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "peertable",
|
|
3
|
-
"version": "0.4.
|
|
3
|
+
"version": "0.4.6",
|
|
4
4
|
"description": "A round table of peer agents. No orchestrator at the head. Turn Claude Code, Codex, and Grok sessions into a team of equal, long-lived peers.",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"license": "MIT",
|
package/room/client.mjs
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
|
-
// Peertable セッション側クライアント。room
|
|
2
|
+
// Peertable セッション側クライアント。channel(新着の一行通知)と room ツールを 1 プロセスに統合する。
|
|
3
3
|
// .mcp.json 登録: {"command":"node","args":["client.mjs"],"env":{"PEERTABLE_URL":"http://192.168.1.2:8790","PEERTABLE_ROOM":"myproject","PEERTABLE_MEMBER":"hinata"}}
|
|
4
4
|
import { Server } from '@modelcontextprotocol/sdk/server/index.js'
|
|
5
5
|
import { StdioServerTransport } from '@modelcontextprotocol/sdk/server/stdio.js'
|
|
@@ -11,7 +11,7 @@ import { fileURLToPath } from 'node:url'
|
|
|
11
11
|
|
|
12
12
|
// client.mjs 側のハードコード版数。package.json の version と一致していることを
|
|
13
13
|
// diagnostics の version_consistency が見る(2 つの版数源の drift 検出。決定45)
|
|
14
|
-
const MCP_VERSION = '0.4.
|
|
14
|
+
const MCP_VERSION = '0.4.6'
|
|
15
15
|
const PKG_ROOT = join(dirname(fileURLToPath(import.meta.url)), '..')
|
|
16
16
|
|
|
17
17
|
const USAGE = `usage:
|
|
@@ -56,12 +56,10 @@ let cursor = 0 // read_unread 用。参加時点から数える
|
|
|
56
56
|
const mcp = new Server(
|
|
57
57
|
{ name: 'room', version: MCP_VERSION },
|
|
58
58
|
{
|
|
59
|
-
// Claude起動時の既存channel選択契約は維持するが、通知は送らない。実配送はwakeup-bridge一系統。
|
|
60
59
|
capabilities: { experimental: { 'claude/channel': {} }, tools: {} },
|
|
61
60
|
instructions:
|
|
62
61
|
`あなたは Peertable room「${ROOM}」のメンバー「${ME}」である。` +
|
|
63
|
-
'
|
|
64
|
-
'read_unread は任意の履歴確認に使う。' +
|
|
62
|
+
'<channel source="room"> の通知は「新着あり」の合図であり、本文は read_unread ツールで読む。' +
|
|
65
63
|
'発言は post ツールを使う。to: "all" はroom全体、メンバー名はDM、配列は複数人宛である。',
|
|
66
64
|
},
|
|
67
65
|
)
|
|
@@ -174,6 +172,45 @@ const IDENTITY = Object.fromEntries(Object.entries({
|
|
|
174
172
|
}).filter(([, v]) => v))
|
|
175
173
|
await fetch(api('members'), { method: 'POST', headers, body: JSON.stringify({ name: ME, ...IDENTITY }) })
|
|
176
174
|
|
|
175
|
+
// SSE 購読 → 自分に関係する新着だけ一行通知へ変換。切断は外部障害なので再接続する。
|
|
176
|
+
const actionHint = 'read_unread で本文を読み、本文に具体的な依頼・行動要求があればそれをこのturnで実行し、完了または具体的なblockerをroomへ報告してから入力待ちに戻ること。情報通知だけなら追加の外部行動を起こさず読了で戻ること。'
|
|
177
|
+
async function subscribe() {
|
|
178
|
+
for (;;) {
|
|
179
|
+
try {
|
|
180
|
+
const res = await fetch(api(`events`))
|
|
181
|
+
let buf = ''
|
|
182
|
+
for await (const chunk of res.body) {
|
|
183
|
+
buf += Buffer.from(chunk).toString('utf8')
|
|
184
|
+
let i
|
|
185
|
+
while ((i = buf.indexOf('\n\n')) >= 0) {
|
|
186
|
+
const frame = buf.slice(0, i)
|
|
187
|
+
buf = buf.slice(i + 2)
|
|
188
|
+
const lines = frame.split('\n')
|
|
189
|
+
// 名前付きイベント(ping/member等)はチャット発言ではない。明示的に読み飛ばす——
|
|
190
|
+
// 現状は relevant() が宛先無しの payload を弾いて偶然安全なだけで、将来のイベント追加が
|
|
191
|
+
// 席を起こす経路を open のままにしない
|
|
192
|
+
if (lines.some(l => l.startsWith('event: '))) continue
|
|
193
|
+
const data = lines.filter(l => l.startsWith('data: ')).map(l => l.slice(6)).join('')
|
|
194
|
+
if (!data) continue
|
|
195
|
+
const m = JSON.parse(data)
|
|
196
|
+
if (!relevant(m)) continue
|
|
197
|
+
await mcp.notification({
|
|
198
|
+
method: 'notifications/claude/channel',
|
|
199
|
+
params: {
|
|
200
|
+
content: `room に新着あり(${m.from} → ${Array.isArray(m.to_names) ? m.to_names.join(', ') : m.to})。${actionHint}`,
|
|
201
|
+
meta: { from: m.from, to: Array.isArray(m.to_names) ? m.to_names.join(',') : m.to, seq: String(m.seq) },
|
|
202
|
+
},
|
|
203
|
+
})
|
|
204
|
+
}
|
|
205
|
+
}
|
|
206
|
+
} catch {
|
|
207
|
+
// サーバー断。少し待って再接続(会話は止まるが工程はローカル Lattice で続く)
|
|
208
|
+
}
|
|
209
|
+
await new Promise(r => setTimeout(r, 3000))
|
|
210
|
+
}
|
|
211
|
+
}
|
|
212
|
+
subscribe()
|
|
213
|
+
|
|
177
214
|
// --- diagnostics(決定45 の契約。read-only。呼ばれた時だけ走る)-------------------------
|
|
178
215
|
// 関数宣言なので巻き上げられ、ファイル冒頭のサブコマンド分岐から呼べる。
|
|
179
216
|
// checks の値は契約どおり状態そのもの(pass / fail / not_applicable / unverified)。
|
package/skill/SKILL.md
CHANGED
|
@@ -65,7 +65,7 @@ script は内部で Aiterm の公開 `claude_agent` / `codex_agent` / `grok_agen
|
|
|
65
65
|
- 起動前に `pty_list` で既存の `peer-*` 席を確認する(前の卓の残骸を99席実測したことがある)。同名の席は launch-seat.sh が落としてから立て直す
|
|
66
66
|
- 着任指示を第6引数に渡すと着席後に送る。文面: 「あなたは「<日本語名>」。.team/roles/member.md を読んで着任し、作業ループを開始せよ。全タスク完了の宣言まで自律的に続けること。」
|
|
67
67
|
- 席が読む env は script が組み立てる(`PEERTABLE_URL` / `PEERTABLE_ROOM` / `PEERTABLE_MEMBER` / `PEERTABLE_CREDENTIAL_FILE`、Lattice 併用なら `PEERTABLE_PLAN` と actor 3点)。token値は席別`0600` fileにだけ置き、pathだけを席へ渡す。**channels は `--mcp-config` の MCP server を解決しない**(実測 2026-08-08・Claude Code v2.1.226・決定44)ため、room の MCP 定義は setup.sh が project root へ置く `.mcp.json` が正。Peertable管理下fileはlaunch時にもcurrent-tree clientへ同期する。project に既存 `.mcp.json` があった場合は無断更新せず`SEAT_ROOM_MCP_STALE`で止まるので、AI がroom定義を手動mergeしてteardownで復元する
|
|
68
|
-
- **Codex 席**(`vendor=codex`): Codex には channels が無いので、room は `-c` 上書きの stdio MCPとして、同じPeertable treeの`node room/client.mjs`を差す。**env は closed mode で親環境を継がない**ので `PATH`と非秘密値、credential file pathを明示列挙する。effortはmember metadataだけでなく`model_reasoning_effort`へも同じ値を渡す。モデル名は ChatGPT アカウントで使える slug を渡す(`~/.codex/config.toml` の `model` が既定値の参考。使えない slug は起動後の最初のターンで 400 になって初めて分かる)。**起床ブリッジは
|
|
68
|
+
- **Codex 席**(`vendor=codex`): Codex には channels が無いので、room は `-c` 上書きの stdio MCPとして、同じPeertable treeの`node room/client.mjs`を差す。**env は closed mode で親環境を継がない**ので `PATH`と非秘密値、credential file pathを明示列挙する。effortはmember metadataだけでなく`model_reasoning_effort`へも同じ値を渡す。モデル名は ChatGPT アカウントで使える slug を渡す(`~/.codex/config.toml` の `model` が既定値の参考。使えない slug は起動後の最初のターンで 400 になって初めて分かる)。**起床ブリッジは Codex / Grok だけ**(下記)——Claude 席は room client が `notifications/claude/channel` を送る(8/12 に送信ループを削った回帰を 0.4.5 で戻した)
|
|
69
69
|
- **Grok 席**(`vendor=grok`): project rootの`.mcp.json`をGrok Buildが読み、同じroom clientと席固有envをAitermが渡す。model / effortはAitermの`grok_agent`へそのまま渡し、`grok models`のlive catalogに無いmodelは着席前に失敗する。初めて開くtreeの既知workspace trustは着席処理が通す。channelsは無いので起床ブリッジを使う。Grok TUIの既定はターン中の素送信を今の仕事へ混ぜず入力キューへ積むので、ブリッジはGrok席がidleになるまで送らない
|
|
70
70
|
6. **起床ブリッジ(Codex / Grok 席がある時)**: `scripts/ensure-bridge.sh <project> wakeup <席名>…`。room の SSE を購読し、明示的にその席宛の新着(自分の発言は除く)だけを tmux へ素送信して起こす。ensure は専用 tmux session で起動し、bridge の `ready_at` を待つ。確認できなければログ末尾を出して非ゼロで終わる。Codexは即送信、Grokはidle待ち。親(`parent_watch` / `observe: null`)は対象にしない。停止は `node scripts/wakeup-bridge.mjs <project> --stop`(teardown.sh が自動で行う)
|
|
71
71
|
- **黙って止まらないための三段**(決定58 の受信側の作法): ①75秒なにも届かなければ自分から切って繋ぎ直す ②繋ぎ直したら `?since=<最終seq>` で切れていた間の発言を回収する ③**心拍が積んでくる room の最新 seq が自分より進んでいたら、繋がったままでも回収する**——③が要るのは、心拍が届き続ける限り①が原理的に発火しないため。**server 側の心拍(`event: ping`・25秒周期)が前提**なので、古い room サーバーへ繋ぐと①だけが効く形になる
|
|
@@ -108,10 +108,10 @@ script は内部で Aiterm の公開 `claude_agent` / `codex_agent` / `grok_agen
|
|
|
108
108
|
- **id に plan key だけの固定値を使わない。** `close` しても run directory は残り `run list` は closed を返さないので、**「無いのに `RUN_EXISTS` で作れない」**袋小路に入る(2026-08-09 実測)
|
|
109
109
|
- `RUN_EXISTS` が返ったら**相手の run を推定せず**、再 list → active があれば共有、無ければ別の一意 id で作り直す
|
|
110
110
|
- **席は自分で `todo start` してから `run intake` する。** intake が返すのは隔離 worktree と `intervention`(`none` か `hold`)で、**許可証ではない**。`hold` は「他の着手済み task と競合しているから留まれ」という装置の指示である
|
|
111
|
-
- **`todo done`
|
|
111
|
+
- **`todo done` は監査担当が打つ。** canonical の cwd/store へ打ち、証跡は worktree から渡す。`done.sh <task> --evidence-from <worktree>/evidence/<plan>/<task>.md`。cwd 1つで兼ねると必ずどちらかが外れる(canonical では証跡が読めず、worktree では accept が見ない store を書く)。**canonical へ証跡を複製して通すのは偽装**——linked worktree は object DB を共有するので複製は要らない。**accept は intake 席が、todo done の後に打つ**(engine は done 前の accept を拒否する。done.sh が accept を先に要求すると循環する)
|
|
112
112
|
- **worktree と lease は設備の供給である。** 席が要求すれば出てくるもので、出してもらうものではない
|
|
113
113
|
- **席と spool は接触しない。** 席が触るのは room と worktree だけで、`.lattice/` の直読み・直書き禁止の契約はそのまま
|
|
114
|
-
- **席の作法の正本は `templates/member.md` の「Lattice の実行層へ自分の着手を載せる」節**(intake→intervention 判定→attach
|
|
114
|
+
- **席の作法の正本は `templates/member.md` の「Lattice の実行層へ自分の着手を載せる」節**(intake→intervention 判定→attach→作業→監査担当が `done.sh`→intake 席が accept の順・1席1 intake・禁止操作・検証の回し方・成果の正本)。ここに二重化しない
|
|
115
115
|
- **席は自分の pid を装置へ渡す(attach)。** その pid は `.team/seats/<名前>.json`(`launch-seat.sh` が着席直後に書く)が持ち、席は読んで `schema` を足すだけで attach input になる。**raw argv を保存しない**——token値をargvから除いた後も、将来の引数を無条件に複製しない。持つのはdigestだけ
|
|
116
116
|
- **run-bridge は可視化の中継であって、必須経路ではない**(決定63)。落ちていても席は自分で intake / attach / accept を打てるし、介入も `lattice run intake intervention --run <ref> --task <id>` で自分で読める。**見えなくなるだけ**である
|
|
117
117
|
- **ブリッジの起動**: `scripts/ensure-bridge.sh <project> run [--lattice <path>]`。停止は `node scripts/run-bridge.mjs <project> --stop`(**teardown.sh が自動で行う**)。**spool dir も席名も取らない**(配車をしないので要らない。渡すと typed error で落ちる)。ADR 0157 の作法(pid 記録・起動時に前の記録を掃除・SIGTERM→SIGKILL)はそのまま
|
|
@@ -16,7 +16,7 @@ if (!session_id || (!model && !reasoning_effort)) {
|
|
|
16
16
|
// Aiterm のmanaged session metadataは実行時state root(macOSではTMPDIR)にある。
|
|
17
17
|
// stdio transportの既定環境は親のTMPDIRを落とすため、同じ公開serverへ明示継承する。
|
|
18
18
|
const transport = new StdioClientTransport({ command: 'aiterm-mcp', env: process.env })
|
|
19
|
-
const client = new Client({ name: 'peertable-seat-configure', version: '0.4.
|
|
19
|
+
const client = new Client({ name: 'peertable-seat-configure', version: '0.4.6' })
|
|
20
20
|
await client.connect(transport)
|
|
21
21
|
const result = await client.callTool({
|
|
22
22
|
name: 'agent_configure',
|
|
@@ -8,7 +8,7 @@ if (!session_name || !['claude', 'codex', 'grok'].includes(vendor) || !model ||
|
|
|
8
8
|
throw new Error('SEAT_AITERM_LAUNCH_ARGS_INVALID')
|
|
9
9
|
}
|
|
10
10
|
|
|
11
|
-
const client = new Client({ name: 'peertable-seat-launch', version: '0.4.
|
|
11
|
+
const client = new Client({ name: 'peertable-seat-launch', version: '0.4.6' })
|
|
12
12
|
await client.connect(new StdioClientTransport({ command: 'aiterm-mcp', env: process.env }))
|
|
13
13
|
const env_vars = [
|
|
14
14
|
'PEERTABLE_URL', 'PEERTABLE_ROOM', 'PEERTABLE_MEMBER', 'PEERTABLE_CREDENTIAL_FILE',
|
|
@@ -566,13 +566,15 @@ else
|
|
|
566
566
|
echo "seat-status-bridge の起動確認に失敗した(席は着席済み)" >&2
|
|
567
567
|
fi
|
|
568
568
|
|
|
569
|
-
# Claude
|
|
570
|
-
# Codex / Grok
|
|
571
|
-
if
|
|
572
|
-
|
|
573
|
-
|
|
574
|
-
|
|
575
|
-
|
|
569
|
+
# Claude 席の起床は room client の notifications/claude/channel。ブリッジは channels を持たない
|
|
570
|
+
# Codex / Grok 席だけ。Claude に立てると channel と二重配達になる。
|
|
571
|
+
if [ "$vendor" = codex ] || [ "$vendor" = grok ]; then
|
|
572
|
+
if PEERTABLE_CREDENTIAL_FILE="$credential_file" "$(dirname "$0")/ensure-bridge.sh" "$proj" wakeup; then
|
|
573
|
+
echo "wakeup-bridge: 起動確認済み"
|
|
574
|
+
else
|
|
575
|
+
echo "SEAT_WAKEUP_BRIDGE_NOT_READY: Codex/Grok席の起床bridgeを準備できない" >&2
|
|
576
|
+
exit 1
|
|
577
|
+
fi
|
|
576
578
|
fi
|
|
577
579
|
|
|
578
580
|
if [ -z "$brief" ]; then brief_completed=true; fi
|
package/skill/templates/done.sh
CHANGED
|
@@ -35,7 +35,8 @@ usage: done.sh <task_id> [--plan <plan_key>] [--evidence-from <隔離worktreeの
|
|
|
35
35
|
evidence/<plan>/<task>.md を commit 済みにして done.sh <task_id> を実行する。
|
|
36
36
|
wrapper が証跡から記述子を生成し、lattice todo doneで同じ本文を最終試験結果として canonical store へ記録する。
|
|
37
37
|
pull run の worktree で作業した場合だけ --evidence-from に同じrepoの絶対pathを渡す。
|
|
38
|
-
|
|
38
|
+
この script は監査担当が打つ。accept は intake 席が todo done の後に打つ(engine 正順)。
|
|
39
|
+
未accept・未着地は警告だけで、done を止めない。
|
|
39
40
|
USAGE
|
|
40
41
|
}
|
|
41
42
|
|
|
@@ -177,10 +178,9 @@ while [ "$#" -gt 0 ]; do
|
|
|
177
178
|
done
|
|
178
179
|
[ -n "$plan" ] || { echo "ERROR: 完了処理を続けられない: --plan または PEERTABLE_PLAN が必要" >&2; exit 1; }
|
|
179
180
|
if [ -n "$evidence_from" ]; then
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
esac
|
|
181
|
+
if ! python3 -c 'import os,sys; sys.exit(0 if os.path.isabs(sys.argv[1]) else 1)' "$evidence_from"; then
|
|
182
|
+
echo "ERROR: --evidence-from は絶対pathでなければならない: $evidence_from" >&2; exit 1
|
|
183
|
+
fi
|
|
184
184
|
# **黙って canonical の証跡へ落ちない。** 落ちると「worktree の成果を done した」と見えるのに
|
|
185
185
|
# 実際は別の file を hash することになり、受理された内容と成果物が食い違う
|
|
186
186
|
[ -f "$evidence_from" ] || { echo "ERROR: --evidence-from の証跡が存在しない: $evidence_from" >&2; exit 1; }
|
|
@@ -240,11 +240,9 @@ case "$task_status" in
|
|
|
240
240
|
*) echo "ERROR: 完了処理を続けられない: ToDoが完了可能状態でない: $task_status" >&2; exit 1 ;;
|
|
241
241
|
esac
|
|
242
242
|
|
|
243
|
-
# **
|
|
244
|
-
#
|
|
245
|
-
#
|
|
246
|
-
# 「ToDo は完了・成果はどこにも着地していない」状態が親の事後照合まで誰にも見えなかった
|
|
247
|
-
# (そして landing-only mode は receipt が無ければ 0 本=無言になる)。
|
|
243
|
+
# **done と accept は別軸。** engine は todo done の後にだけ accept できる。
|
|
244
|
+
# 監査担当が done.sh で閉じ、intake 席が accept する。未 accept を done の拒否条件にすると循環する。
|
|
245
|
+
# 読めない状態は成功へ倒さない。未accept・未着地は警告(未push と同じ)。
|
|
248
246
|
# **実行層に載っていない task は素通しする**——pull run の利用は任意で、載っていない卓を止めない。
|
|
249
247
|
gate_runs=$("$done_gate_cli" run list --json 2>&1) || {
|
|
250
248
|
echo "ERROR: receipt の状態を読めない(run list が失敗): $gate_runs" >&2; exit 1;
|
|
@@ -322,12 +320,6 @@ for index, intake in enumerate(intakes):
|
|
|
322
320
|
entry = intake
|
|
323
321
|
print("absent" if entry is None else ("accepted" if entry.get("accepted_head_sha") else "pending"))
|
|
324
322
|
' "$t") || { echo "ERROR: receipt の状態を読めない: $gate_state" >&2; exit 1; }
|
|
325
|
-
if [ "$gate_state" = pending ]; then
|
|
326
|
-
echo "ERROR: receipt が未acceptのまま done は打てない: ${t} @ ${gate_ref}" >&2
|
|
327
|
-
echo " 先に受理させる: ${done_gate_cli} run intake accept --run ${gate_ref} --task ${t}" >&2
|
|
328
|
-
echo " (accept が hold で止まる場合、その理由の解消が完了条件であって、done は迂回路ではない)" >&2
|
|
329
|
-
exit 1
|
|
330
|
-
fi
|
|
331
323
|
done
|
|
332
324
|
|
|
333
325
|
# descriptor の path は repo 内の相対(repo 外の絶対 path は --evidence が INVALID_ARGUMENTS で弾く)。
|
|
@@ -378,9 +370,7 @@ if [ -n "$upstream_ref" ]; then
|
|
|
378
370
|
fi
|
|
379
371
|
fi
|
|
380
372
|
|
|
381
|
-
# pull run
|
|
382
|
-
# canonical branch へ着地したことまで確認する。別 task の未着地だけでは
|
|
383
|
-
# この task の completed を止めない。
|
|
373
|
+
# pull run に載った task は、accept / 着地を警告として出す。done 自体は止めない。
|
|
384
374
|
for gate_ref in $gate_refs; do
|
|
385
375
|
gate_obs=""
|
|
386
376
|
gate_obs=$("$done_gate_cli" run observe --run "$gate_ref" 2>&1) || {
|
|
@@ -420,14 +410,14 @@ else:
|
|
|
420
410
|
exit 1
|
|
421
411
|
}
|
|
422
412
|
[ "$gate_state" = absent ] && continue
|
|
423
|
-
[ "$gate_state"
|
|
424
|
-
echo "
|
|
425
|
-
|
|
426
|
-
|
|
413
|
+
if [ "$gate_state" != accepted ]; then
|
|
414
|
+
echo "未accept: ${t} @ ${gate_ref}(intake 席が accept する。done は記録済み)" >&2
|
|
415
|
+
continue
|
|
416
|
+
fi
|
|
427
417
|
landing_report=""
|
|
428
418
|
landing_report=$("$done_gate_cli" run landing --run "$gate_ref" 2>&1) || {
|
|
429
|
-
echo "
|
|
430
|
-
|
|
419
|
+
echo "着地状態を読めない: run landing が失敗: $gate_ref: $landing_report" >&2
|
|
420
|
+
continue
|
|
431
421
|
}
|
|
432
422
|
landing_task=$(printf '%s' "$landing_report" | python3 -c '
|
|
433
423
|
import json, sys
|
|
@@ -459,8 +449,7 @@ else:
|
|
|
459
449
|
exit 1
|
|
460
450
|
}
|
|
461
451
|
[ "$landing_task" = landed ] || {
|
|
462
|
-
echo "
|
|
463
|
-
exit 1
|
|
452
|
+
echo "未着地: ${t} @ ${gate_ref}(task receipt が canonical へ未着地)" >&2
|
|
464
453
|
}
|
|
465
454
|
done
|
|
466
455
|
|
|
@@ -90,19 +90,18 @@ lattice run intake --run .lattice/runs/<run-id> --task <id>
|
|
|
90
90
|
- **依存は install しなくても解決する。** worktree は repo 配下(`<repo>/.lattice/runs/…/tree`)に切られるので、Node の bare specifier 解決が親ディレクトリを遡って canonical の `node_modules` に当たる(repo の外に置かれた木では当たらない)。これは現在の worktree 配置がもたらしている便益であって、どこでも成り立つ性質ではない
|
|
91
91
|
- **当たるのは canonical に入っている版である。** worktree の `package.json` が要求する版とは限らないので、**lockfile や依存を動かす task では、検証結果を「解決された版のずれ」ごと疑う**
|
|
92
92
|
- それでも回らない検証は、無理に回さず room で言う。**動かないからといって canonical tree で回さない**——それは測りたい木ではない
|
|
93
|
-
9. **自己試験と自己監査を完了し、最終試験結果を監査担当へ渡す。**
|
|
93
|
+
9. **自己試験と自己監査を完了し、最終試験結果を監査担当へ渡す。** `todo done` / `done.sh <task>` は監査担当が打つ(憲章11・12)。監査担当が工程をクローズした後、intake した席だけが accept できる(装置が actor で束縛しているので、監査担当や親が代わりに打てない)。
|
|
94
94
|
```
|
|
95
|
-
# canonical repo の cwd
|
|
95
|
+
# 監査担当のクローズ後。canonical repo の cwd から打つ
|
|
96
96
|
cd <canonical repo>
|
|
97
|
-
PEERTABLE_PLAN={{PLAN_KEY}} .team/scripts/done.sh <id> --evidence-from <worktree>/evidence/{{PLAN_KEY}}/<id>.md
|
|
98
97
|
lattice run intake accept --run <ref> --task <id>
|
|
99
98
|
.team/scripts/done.sh --landing-run <ref>
|
|
100
99
|
```
|
|
101
|
-
|
|
100
|
+
証跡は worktree に commit したものを監査担当が `--evidence-from` で hash する。
|
|
102
101
|
**canonical へ証跡を書き写して通すのも禁止**——「worktree の中だけを触る」契約を破りながら
|
|
103
102
|
green にする偽装になる。linked worktree は canonical と object DB を共有するので、
|
|
104
103
|
**worktree に commit した証跡は canonical から読める**(複製は要らない)。
|
|
105
|
-
装置が worktree の base→HEAD
|
|
104
|
+
装置が worktree の base→HEAD を独立に観測して受理する。
|
|
106
105
|
最後の landing-only 呼び出しは、accept 済み receipt が canonical default branch へ未着地なら
|
|
107
106
|
`未着地 N本`を出す。警告だけで処理は止めないが、`未push` と別の完了軸なので読み飛ばさない。
|
|
108
107
|
|