peertable 0.6.1 → 0.7.1
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/LICENSE +21 -21
- package/README.ja.md +126 -122
- package/README.md +162 -158
- package/package.json +49 -49
- package/room/Dockerfile +7 -7
- package/room/client.mjs +53 -8
- package/room/server.mjs +188 -12
- package/skill/02_models.snapshot.md +103 -103
- package/skill/SKILL.md +264 -259
- package/skill/scripts/alarm-set.sh +0 -0
- package/skill/scripts/archive-room-log.py +0 -0
- package/skill/scripts/bridge-record-live.mjs +0 -0
- package/skill/scripts/change-effort.sh +0 -0
- package/skill/scripts/change-seat.sh +0 -0
- package/skill/scripts/codex-parent-watch.sh +0 -0
- package/skill/scripts/doctor.sh +0 -0
- package/skill/scripts/ensure-bridge.sh +0 -0
- package/skill/scripts/ensure-codex-room-mcp.mjs +0 -0
- package/skill/scripts/ensure-room-mcp.mjs +3 -7
- package/skill/scripts/external-pane.mjs +0 -0
- package/skill/scripts/kickoff-gate.mjs +82 -0
- package/skill/scripts/launch-seat.sh +0 -0
- package/skill/scripts/leave-seat.sh +0 -0
- package/skill/scripts/make-plan-input.mjs +0 -0
- package/skill/scripts/parent-join.sh +0 -0
- package/skill/scripts/remove-managed-room-mcp.mjs +70 -0
- package/skill/scripts/resolve-seat-placement.mjs +0 -0
- package/skill/scripts/resume.sh +234 -0
- package/skill/scripts/room-mcp-config.mjs +24 -0
- package/skill/scripts/seat-status-bridge.mjs +27 -0
- package/skill/scripts/set-mission.sh +0 -0
- package/skill/scripts/setup.sh +0 -0
- package/skill/scripts/teardown.sh +7 -2
- package/skill/scripts/tmux-at.bash +10 -10
- package/skill/scripts/upgrade-team-assets.sh +59 -7
- package/skill/scripts/wakeup-bridge.mjs +71 -4
- package/skill/templates/charter.md +20 -20
- package/skill/templates/mcp.json +5 -5
- package/skill/templates/member-standalone.md +60 -58
- package/skill/templates/member.md +149 -147
- package/skill/templates/parent.md +143 -142
- package/skill/templates/tasks.md +8 -8
package/room/server.mjs
CHANGED
|
@@ -53,6 +53,20 @@ db.exec(`
|
|
|
53
53
|
)
|
|
54
54
|
`)
|
|
55
55
|
|
|
56
|
+
// bridge 台帳と配送 receipt 台帳(決定102・103)。bridge の心拍と wakeup-bridge の TUI 投入成立だけが書く。
|
|
57
|
+
db.exec(`
|
|
58
|
+
CREATE TABLE IF NOT EXISTS bridges (
|
|
59
|
+
room TEXT NOT NULL, kind TEXT NOT NULL,
|
|
60
|
+
pid INTEGER, state TEXT, detail TEXT, beat_at TEXT NOT NULL,
|
|
61
|
+
PRIMARY KEY (room, kind)
|
|
62
|
+
);
|
|
63
|
+
CREATE TABLE IF NOT EXISTS deliveries (
|
|
64
|
+
room TEXT NOT NULL, seq INTEGER NOT NULL, recipient TEXT NOT NULL,
|
|
65
|
+
result TEXT NOT NULL, reason TEXT, at TEXT NOT NULL,
|
|
66
|
+
PRIMARY KEY (room, seq, recipient)
|
|
67
|
+
)
|
|
68
|
+
`)
|
|
69
|
+
|
|
56
70
|
// 既存 DB(旧 vendor 列世代)は一度だけ harness 列を足して値を引き継ぐ。旧列は rollback 用に残す。
|
|
57
71
|
{
|
|
58
72
|
const columns = db.prepare('PRAGMA table_info(members)').all().map((c) => c.name)
|
|
@@ -156,6 +170,104 @@ const recipientError = (code, message) => ({
|
|
|
156
170
|
schema: 'peertable.error.v1', code, message,
|
|
157
171
|
})
|
|
158
172
|
|
|
173
|
+
// ---- 実効状態の生成(決定101〜103)------------------------------------------------
|
|
174
|
+
// 実効稼働状態・bridge 健全性の判定は server がここで一度だけ行い、Web UI / MCP / script は
|
|
175
|
+
// 生成された欄を読むだけにする。閲覧面ごとの独自判定は同じ誤認(保存=配達、静的一覧=稼働)を再生産する。
|
|
176
|
+
// 既定 90 秒(bridge 心拍 30 秒の3倍)。env は repro が時間を縮めて実測するための入口(experiments/ 参照)
|
|
177
|
+
const STATUS_STALE_MS = Number(process.env.PEERTABLE_STATUS_STALE_MS ?? 90_000)
|
|
178
|
+
const AUTH_FAIL_FRESH_MS = 600_000 // 403 観測の表示保持。以後の正常心拍で即座に消える
|
|
179
|
+
|
|
180
|
+
// 書込 403 の観測記録。403 を返した server だけが「bridge が認証で書けていない」を確実に知る
|
|
181
|
+
// (bridge 側は書けないので自己申告できない)。`${room}/${kind}` -> 最終観測 epoch ms。
|
|
182
|
+
const authFailures = new Map()
|
|
183
|
+
|
|
184
|
+
// 403 になった書込がどの bridge のものかを request の形から分類する。分類できない 403 は記録しない
|
|
185
|
+
// (member 以外の一般書込の 403 を bridge 障害として表示しない)。
|
|
186
|
+
function classifyDeniedWrite(rest, body) {
|
|
187
|
+
if (rest === 'bridges') { try { return JSON.parse(body).kind ?? null } catch { return null } }
|
|
188
|
+
if (rest === 'deliveries') return 'wakeup'
|
|
189
|
+
if (rest === 'members') {
|
|
190
|
+
try { return JSON.parse(body).usage_source === 'pane_status' ? 'seat_status' : null } catch { return null }
|
|
191
|
+
}
|
|
192
|
+
return null
|
|
193
|
+
}
|
|
194
|
+
|
|
195
|
+
const BRIDGE_KINDS = ['seat_status', 'wakeup']
|
|
196
|
+
// 表示語彙はオーナー報告の障害名(status_bridge_down / wakeup_bridge_down / bridge_auth_failed)
|
|
197
|
+
const BRIDGE_LABEL = { seat_status: 'status', wakeup: 'wakeup' }
|
|
198
|
+
function bridgeHealth(roomName, now = Date.now()) {
|
|
199
|
+
const out = {}
|
|
200
|
+
for (const kind of BRIDGE_KINDS) {
|
|
201
|
+
const row = db.prepare('SELECT * FROM bridges WHERE room = ? AND kind = ?').get(roomName, kind)
|
|
202
|
+
const beat = row ? Date.parse(row.beat_at) : null
|
|
203
|
+
const authAt = authFailures.get(`${roomName}/${kind}`)
|
|
204
|
+
if (authAt !== undefined && now - authAt < AUTH_FAIL_FRESH_MS && (beat === null || authAt > beat)) {
|
|
205
|
+
out[kind] = { state: 'bridge_auth_failed', beat_at: row?.beat_at ?? null }
|
|
206
|
+
continue
|
|
207
|
+
}
|
|
208
|
+
if (!row) { out[kind] = { state: `${BRIDGE_LABEL[kind]}_bridge_unreported`, beat_at: null }; continue }
|
|
209
|
+
out[kind] = now - beat < STATUS_STALE_MS
|
|
210
|
+
? { state: 'up', beat_at: row.beat_at, pid: row.pid }
|
|
211
|
+
: { state: `${BRIDGE_LABEL[kind]}_bridge_down`, beat_at: row.beat_at }
|
|
212
|
+
}
|
|
213
|
+
return out
|
|
214
|
+
}
|
|
215
|
+
|
|
216
|
+
// member 1件の実効稼働状態。報告が新しい時だけ status を採り、途絶・未報告は unknown へ落として
|
|
217
|
+
// 理由(bridge 停止 / 認証失敗 / 心拍途絶)を必ず併記する——状態点が黙って消えるだけの面を作らない。
|
|
218
|
+
function effectiveStatus(member, bridges, now = Date.now()) {
|
|
219
|
+
const statusBridge = bridges.seat_status.state
|
|
220
|
+
if (!member.status_at) {
|
|
221
|
+
return {
|
|
222
|
+
status_effective: 'unknown',
|
|
223
|
+
status_reason: statusBridge === 'up' ? 'status_unreported' : statusBridge,
|
|
224
|
+
status_age_ms: null,
|
|
225
|
+
}
|
|
226
|
+
}
|
|
227
|
+
const age = now - Date.parse(member.status_at)
|
|
228
|
+
if (age < STATUS_STALE_MS) return { status_effective: member.status, status_reason: 'fresh', status_age_ms: age }
|
|
229
|
+
return {
|
|
230
|
+
status_effective: 'unknown',
|
|
231
|
+
status_reason: statusBridge === 'up' ? 'status_heartbeat_stale' : statusBridge,
|
|
232
|
+
status_age_ms: age,
|
|
233
|
+
}
|
|
234
|
+
}
|
|
235
|
+
|
|
236
|
+
// ---- 配送状態の導出(決定102)-----------------------------------------------------
|
|
237
|
+
// receipt(wakeup-bridge の実投入記録)が正。無い宛先は member 台帳と bridge 台帳から
|
|
238
|
+
// pending / seat_unavailable / bridge_unavailable を導出する。room_saved だけでは配達と言わない。
|
|
239
|
+
const isParentMember = member => member?.delivery?.kind === 'parent_watch'
|
|
240
|
+
// wakeup-bridge の配送対象判定(skill/scripts/wakeup-delivery.mjs の isWakeupBridgeTarget と同じ規則)
|
|
241
|
+
function isTuiDeliveryTarget(member) {
|
|
242
|
+
if (!member || isParentMember(member)) return false
|
|
243
|
+
const hasPane = typeof member.observe?.tmux_target === 'string' && member.observe.tmux_target.length > 0
|
|
244
|
+
const harness = member.harness ?? member.vendor
|
|
245
|
+
return hasPane || harness === 'codex' || harness === 'grok'
|
|
246
|
+
}
|
|
247
|
+
|
|
248
|
+
function deliveryPlanFor(roomName, msg, bridges) {
|
|
249
|
+
const members = new Map(listMembers(roomName).map(m => [m.name, m]))
|
|
250
|
+
const recipients = Array.isArray(msg.to_names) ? msg.to_names
|
|
251
|
+
: msg.to === 'all' ? [...members.keys()].filter(n => n !== msg.from)
|
|
252
|
+
: typeof msg.to === 'string' && msg.to.length > 0 ? [msg.to] : []
|
|
253
|
+
const out = {}
|
|
254
|
+
for (const name of recipients) {
|
|
255
|
+
const receipt = db.prepare('SELECT * FROM deliveries WHERE room = ? AND seq = ? AND recipient = ?')
|
|
256
|
+
.get(roomName, msg.seq, name)
|
|
257
|
+
if (receipt) {
|
|
258
|
+
out[name] = { state: receipt.result, ...(receipt.reason ? { reason: receipt.reason } : {}), at: receipt.at }
|
|
259
|
+
continue
|
|
260
|
+
}
|
|
261
|
+
const member = members.get(name)
|
|
262
|
+
if (!member) { out[name] = { state: 'seat_unavailable', reason: 'member_not_found' }; continue }
|
|
263
|
+
if (isParentMember(member)) { out[name] = { state: 'pending', reason: 'parent_watch経由' }; continue }
|
|
264
|
+
if (!isTuiDeliveryTarget(member)) { out[name] = { state: 'seat_unavailable', reason: 'no_delivery_route' }; continue }
|
|
265
|
+
if (bridges.wakeup.state !== 'up') { out[name] = { state: 'bridge_unavailable', reason: bridges.wakeup.state }; continue }
|
|
266
|
+
out[name] = { state: 'pending' }
|
|
267
|
+
}
|
|
268
|
+
return out
|
|
269
|
+
}
|
|
270
|
+
|
|
159
271
|
const WAITING_WORDS = ['待機する', '待機します', '待機。']
|
|
160
272
|
|
|
161
273
|
function parentName(room) {
|
|
@@ -236,11 +348,30 @@ http.createServer(async (req, res) => {
|
|
|
236
348
|
if (req.method === 'GET' && rest === 'messages')
|
|
237
349
|
return json(res, 200, { messages: readMessages(room, Number(url.searchParams.get('since') ?? 0)) }, CORS)
|
|
238
350
|
|
|
239
|
-
if (req.method === 'GET' && rest === 'members')
|
|
351
|
+
if (req.method === 'GET' && rest === 'members') {
|
|
352
|
+
const now = Date.now()
|
|
353
|
+
const bridges = bridgeHealth(room.name, now)
|
|
354
|
+
return json(res, 200, {
|
|
355
|
+
members: listMembers(room.name).map(m => ({ ...m, ...effectiveStatus(m, bridges, now) })),
|
|
356
|
+
bridges,
|
|
357
|
+
capabilities: {
|
|
358
|
+
member_observation_v1: true, member_ledger_v1: true,
|
|
359
|
+
effective_status_v1: true, delivery_receipt_v1: true,
|
|
360
|
+
},
|
|
361
|
+
}, CORS)
|
|
362
|
+
}
|
|
363
|
+
|
|
364
|
+
// 配送状態の照会(決定102)。receipt が正・無い宛先は台帳から導出
|
|
365
|
+
if (req.method === 'GET' && rest === 'deliveries') {
|
|
366
|
+
const seqParam = Number(url.searchParams.get('seq'))
|
|
367
|
+
if (!Number.isSafeInteger(seqParam) || seqParam <= 0) return json(res, 400, { error: 'seq_required' }, CORS)
|
|
368
|
+
const msg = readMessages(room, seqParam - 1).find(m => m.seq === seqParam)
|
|
369
|
+
if (!msg) return json(res, 404, { error: 'message_not_found' }, CORS)
|
|
240
370
|
return json(res, 200, {
|
|
241
|
-
|
|
242
|
-
|
|
371
|
+
schema: 'peertable.delivery.v1', seq: seqParam, room_saved: true,
|
|
372
|
+
delivery: deliveryPlanFor(room.name, msg, bridgeHealth(room.name)),
|
|
243
373
|
}, CORS)
|
|
374
|
+
}
|
|
244
375
|
|
|
245
376
|
// 単独 member の読み出し。席の attach 入力・診断はここから取る(席fileは廃止・台帳が唯一の正)
|
|
246
377
|
if (req.method === 'GET' && seg[2] === 'members' && seg[3]) {
|
|
@@ -267,8 +398,13 @@ http.createServer(async (req, res) => {
|
|
|
267
398
|
|
|
268
399
|
// 書込系。TOKEN 設定時は一致を要求する(外部公開設置のための最小ゲート)
|
|
269
400
|
const body = await readBody(req)
|
|
270
|
-
if (TOKEN !== null && (req.headers['x-peertable-token'] ?? url.searchParams.get('token')) !== TOKEN)
|
|
401
|
+
if (TOKEN !== null && (req.headers['x-peertable-token'] ?? url.searchParams.get('token')) !== TOKEN) {
|
|
402
|
+
// bridge 由来と分類できる 403 は観測として残し、実効表示 bridge_auth_failed の根拠にする(決定103)。
|
|
403
|
+
// 403 を返す server だけが「bridge が認証で書けていない」を確実に知る——状態点が黙って消える形にしない
|
|
404
|
+
const deniedKind = classifyDeniedWrite(rest, body)
|
|
405
|
+
if (deniedKind !== null && BRIDGE_KINDS.includes(deniedKind)) authFailures.set(`${room.name}/${deniedKind}`, Date.now())
|
|
271
406
|
return json(res, 403, { error: 'token_required' })
|
|
407
|
+
}
|
|
272
408
|
|
|
273
409
|
if (req.method === 'POST' && rest === 'messages') {
|
|
274
410
|
const { from, to, to_names: toNames, body: text } = JSON.parse(body)
|
|
@@ -279,7 +415,31 @@ http.createServer(async (req, res) => {
|
|
|
279
415
|
const waiting = WAITING_WORDS.some(word => text.includes(word))
|
|
280
416
|
const audience = normalizeAudience(waiting ? parentName(room) : to, waiting ? null : toNames)
|
|
281
417
|
if (audience.error) return json(res, 400, audience.error)
|
|
282
|
-
|
|
418
|
+
const saved = post(room, from, audience.to, text, audience.to_names)
|
|
419
|
+
// 保存と配達は別の事実(決定102)。応答は room_saved と宛先別 delivery の二段で返し、
|
|
420
|
+
// delivered は wakeup-bridge の投入成立 receipt だけが作る。sent の一語で両者を混ぜない
|
|
421
|
+
return json(res, 200, { ...saved, room_saved: true, delivery: deliveryPlanFor(room.name, saved, bridgeHealth(room.name)) })
|
|
422
|
+
}
|
|
423
|
+
// bridge 心拍(決定103)。seat-status / wakeup が 30 秒ごとに送る。正常心拍は 403 観測を消す
|
|
424
|
+
if (req.method === 'POST' && rest === 'bridges') {
|
|
425
|
+
const { kind, pid, state, detail } = JSON.parse(body)
|
|
426
|
+
if (typeof kind !== 'string' || kind.length === 0) return json(res, 400, { error: 'kind_required' })
|
|
427
|
+
db.prepare(`INSERT OR REPLACE INTO bridges (room, kind, pid, state, detail, beat_at)
|
|
428
|
+
VALUES (?, ?, ?, ?, ?, ?)`).run(room.name, kind, pid ?? null, state ?? 'running', detail ?? null, new Date().toISOString())
|
|
429
|
+
authFailures.delete(`${room.name}/${kind}`)
|
|
430
|
+
return json(res, 200, { ok: true })
|
|
431
|
+
}
|
|
432
|
+
// 配送 receipt(決定102)。書き手は wakeup-bridge だけ。同じ (seq, recipient) は最新で上書き
|
|
433
|
+
// (seat_unavailable → 再試行成功 delivered の遷移を残すため)
|
|
434
|
+
if (req.method === 'POST' && rest === 'deliveries') {
|
|
435
|
+
const { seq: recSeq, recipient, result, reason } = JSON.parse(body)
|
|
436
|
+
if (!Number.isSafeInteger(recSeq) || recSeq <= 0) return json(res, 400, { error: 'seq_required' })
|
|
437
|
+
if (typeof recipient !== 'string' || recipient.length === 0) return json(res, 400, { error: 'recipient_required' })
|
|
438
|
+
if (!['delivered', 'pending', 'seat_unavailable', 'bridge_unavailable', 'failed'].includes(result))
|
|
439
|
+
return json(res, 400, { error: 'result_invalid' })
|
|
440
|
+
db.prepare(`INSERT OR REPLACE INTO deliveries (room, seq, recipient, result, reason, at)
|
|
441
|
+
VALUES (?, ?, ?, ?, ?, ?)`).run(room.name, recSeq, recipient, result, reason ?? null, new Date().toISOString())
|
|
442
|
+
return json(res, 200, { ok: true })
|
|
283
443
|
}
|
|
284
444
|
if (req.method === 'POST' && rest === 'members') {
|
|
285
445
|
const { name, ...meta } = JSON.parse(body)
|
|
@@ -310,6 +470,8 @@ http.createServer(async (req, res) => {
|
|
|
310
470
|
for (const s of room.streams) s.end()
|
|
311
471
|
rooms.delete(room.name); rmSync(room.dir, { recursive: true, force: true })
|
|
312
472
|
db.prepare('DELETE FROM members WHERE room = ?').run(room.name)
|
|
473
|
+
db.prepare('DELETE FROM bridges WHERE room = ?').run(room.name)
|
|
474
|
+
db.prepare('DELETE FROM deliveries WHERE room = ?').run(room.name)
|
|
313
475
|
return json(res, 200, { ok: true })
|
|
314
476
|
}
|
|
315
477
|
return json(res, 404, { error: 'not_found' })
|
|
@@ -353,6 +515,8 @@ const UI = room => `<!doctype html><html lang="ja"><head><meta charset="utf-8"><
|
|
|
353
515
|
.top{position:sticky;top:0;z-index:2;background:var(--bg);border-bottom:1px solid var(--line);padding:12px 16px 0}
|
|
354
516
|
.top>div{max-width:760px;margin:0 auto}
|
|
355
517
|
.members{display:flex;gap:6px;overflow-x:auto;padding:10px 0;scrollbar-width:thin}
|
|
518
|
+
.bridgewarn{color:var(--busy);font-size:12px;font-weight:650;padding:0 0 8px}
|
|
519
|
+
.bridgewarn:empty{display:none}
|
|
356
520
|
.chip.has-meta{cursor:pointer}
|
|
357
521
|
/* 稼働状態の点。報告が途絶えたら unknown(中空)へ落として、古い状態を出し続けない */
|
|
358
522
|
.chip .nm{display:inline-flex;align-items:center;gap:5px}
|
|
@@ -419,7 +583,7 @@ const UI = room => `<!doctype html><html lang="ja"><head><meta charset="utf-8"><
|
|
|
419
583
|
.to-bottom[hidden]{display:none}
|
|
420
584
|
.sys .body{padding:2px 12px;border-radius:999px;background:var(--surface);border:1px solid var(--line)}
|
|
421
585
|
</style></head><body>
|
|
422
|
-
<header class="top"><div class="brand">${MARK}${esc(room)} <small>· Peertable</small></div><div class="members" id="members"></div></header>
|
|
586
|
+
<header class="top"><div class="brand">${MARK}${esc(room)} <small>· Peertable</small></div><div class="members" id="members"></div><div class="bridgewarn" id="bridges"></div></header>
|
|
423
587
|
<main class="log" id="log"></main>
|
|
424
588
|
<button class="to-bottom" id="to-bottom" type="button" hidden aria-label="最新の発言へ" title="最新の発言へ">↓</button>
|
|
425
589
|
<script>
|
|
@@ -552,8 +716,21 @@ function render(m){
|
|
|
552
716
|
body.appendChild(meta);body.appendChild(bub);body.appendChild(seq())
|
|
553
717
|
d.appendChild(body);logEl.appendChild(d);last=m;return d
|
|
554
718
|
}
|
|
719
|
+
// bridge 障害の帯。unreported(bridge を立てない部屋・archive 部屋)は常時警告で汚さないため出さない
|
|
720
|
+
const bridgesEl=document.getElementById('bridges')
|
|
721
|
+
function renderBridges(bridges){
|
|
722
|
+
const label={seat_status:'稼働状態bridge',wakeup:'配達bridge'}
|
|
723
|
+
const warn=[]
|
|
724
|
+
for(const k of ['seat_status','wakeup']){
|
|
725
|
+
const s=bridges&&bridges[k]&&bridges[k].state
|
|
726
|
+
if(!s||s==='up'||s.endsWith('_unreported'))continue
|
|
727
|
+
warn.push(label[k]+':'+(s==='bridge_auth_failed'?'認証失敗(403・bridge_auth_failed)':'心拍途絶(停止)'))
|
|
728
|
+
}
|
|
729
|
+
bridgesEl.textContent=warn.length?'⚠ '+warn.join(' / '):''
|
|
730
|
+
}
|
|
555
731
|
async function refreshMembers(){
|
|
556
732
|
const r=await(await fetch(api('members'))).json()
|
|
733
|
+
renderBridges(r.bridges)
|
|
557
734
|
syncAvatarAssignments(r.members.map(m=>m.name))
|
|
558
735
|
membersEl.textContent=''
|
|
559
736
|
if(!r.members.length){membersEl.appendChild(el('span','empty','(まだ誰も居ない)'));return}
|
|
@@ -564,12 +741,12 @@ async function refreshMembers(){
|
|
|
564
741
|
const rolesText=Array.isArray(m.roles)&&m.roles.length?m.roles.join('・'):''
|
|
565
742
|
const settingsText=[m.model,m.effort].filter(Boolean).join('×')
|
|
566
743
|
const meta=[rolesText,settingsText,m.mission].filter(Boolean)
|
|
567
|
-
//
|
|
568
|
-
//
|
|
569
|
-
const
|
|
570
|
-
const st=(m.status&&age<STATUS_STALE_MS)?m.status:(m.status?'unknown':null)
|
|
744
|
+
// 稼働状態は server 生成の実効状態(status_effective / status_reason)だけを読む(決定101)。
|
|
745
|
+
// 閲覧面ごとに独自の鮮度判定を持つと、MCP と Web UI で違う判定になり誤認が再発する
|
|
746
|
+
const st=m.status_effective??null
|
|
571
747
|
if(st)c.classList.add('is-'+st)
|
|
572
|
-
|
|
748
|
+
const reason={status_unreported:'未報告(seat-status bridge が動いていない)',status_heartbeat_stale:'状態heartbeatが途絶えている',status_bridge_down:'状態bridgeが停止(status_bridge_down)',status_bridge_unreported:'状態bridgeが未登録',bridge_auth_failed:'bridge認証失敗(403)'}[m.status_reason]
|
|
749
|
+
meta.push(st?'状態 '+({busy:'作業中',idle:'待機',dead:'停止',blocked:'承認待ち',unknown:'不明'}[st]??st)+(reason?'('+reason+')':''):'状態 未取得(旧serverは実効状態を返さない)')
|
|
573
750
|
const usage=[]
|
|
574
751
|
const busyAge=m.busy_since?Date.now()-Date.parse(m.busy_since):NaN
|
|
575
752
|
if((st==='busy'||st==='blocked')&&Number.isFinite(busyAge)&&busyAge>=0)usage.push('継続 '+elapsed(busyAge))
|
|
@@ -633,7 +810,6 @@ function celebrate(name){
|
|
|
633
810
|
document.body.appendChild(v);setTimeout(()=>v.remove(),1600)
|
|
634
811
|
}
|
|
635
812
|
const BEAT=${HEARTBEAT_MS}
|
|
636
|
-
const STATUS_STALE_MS=90000 // 稼働状態の鮮度。これを過ぎた報告は unknown(bridge 心拍30秒の3倍)
|
|
637
813
|
let lastSeq=0,lastBeat=Date.now(),es=null,emptyEl=null,firstLoad=true,catching=false,memberDebounce=null
|
|
638
814
|
// seq で二重描画を弾く。張り直し後の追いつきと SSE の新着が重なっても同じ発言は1回しか出ない
|
|
639
815
|
function apply(m,live=false){
|
|
@@ -1,103 +1,103 @@
|
|
|
1
|
-
# 02_models — 役割→モデル×エフォート順位表(唯一の参照点)
|
|
2
|
-
|
|
3
|
-
<!-- 前提: 2026-08-19 更新(Claude 5 / GPT-5.6 / Grok 4.6 世代)。バージョン固定禁止(PLAN 原則9)。モデル名をこの表以外+公認例外(codex/agents/*.toml・.codex-sidecar.yml)に書き散らさない -->
|
|
4
|
-
|
|
5
|
-
方針: skill・agents・委譲契約・スクリプトは**役割名**でモデルを指し、具体名への解決はこの表だけが担う。本書は役割を先に定義し、役割ごとに適したモデル×effortを1位〜3位で与える。ベンダー別レーンの表構造は撤廃した(オーナー裁定 2026-08-19)。世代交代時は**この1枚+公認例外2種を更新して push すれば全端末が追従**する。更新トリガーはオーナーの宣言(PLAN 原則6)。
|
|
6
|
-
|
|
7
|
-
背骨: **役割が要求する能力で選び、順位は実測で昇降格する。ただし、現状維持に候補側だけの立証責任を負わせない。** 新しい有力候補は代表実務へ期限・範囲を切って投入し、成功率・手戻り・監査工数・総token・所要時間・quotaで現役と比較する。未検証の現役を「安全」、未検証の新顔を「危険」とは扱わない。
|
|
8
|
-
|
|
9
|
-
本書の根拠は次の4種を混ぜない。
|
|
10
|
-
|
|
11
|
-
1. **一次事実**: model提供元公式の価格、context、対応effort、live catalog。
|
|
12
|
-
2. **外部観測**: benchmark、Xの利用報告、独立評価。harness・標本数・再現条件を併記する。
|
|
13
|
-
3. **dotagents実測**: 当工場のtask、監査通過率、配線実測。一般性能へ拡張しない。
|
|
14
|
-
4. **運用判断**: 上記を踏まえた配置。客観的に確定した事実を「オーナー裁定」と呼ばない。
|
|
15
|
-
|
|
16
|
-
**親のモデル×effortはオーナーの領分**であり、規範・AIはピンを打ち替えない。親候補と根拠は提示し、子の配置はこの表で解決する。
|
|
17
|
-
|
|
18
|
-
## 役割の定義(9つ)
|
|
19
|
-
|
|
20
|
-
1. **統括** — campaign/会話の主体。裁定・契約クリティカルな受入・commit
|
|
21
|
-
2. **反証** — 実ファイルを読んで主張を殺しにかかる
|
|
22
|
-
3. **監査・発見** — 自成果物(repo・設計・文書)の欠陥出し。誤検知は後段が裁く(第三者reviewを含む)
|
|
23
|
-
4. **設計** — 実装前の構造・境界・停止判断の案出し
|
|
24
|
-
5. **相談** — 実読不要の純推論second opinion
|
|
25
|
-
6. **実装** — 仕様固定済みのまとまった実装・テスト・移設
|
|
26
|
-
7. **局所コーディング** — focused test付きの狭い修正・実装
|
|
27
|
-
8. **軽作業** — 分類・抽出・字面回収
|
|
28
|
-
9. **調査** — 外部事実の回収〜統合(浅↔深はeffortで刻む)
|
|
29
|
-
|
|
30
|
-
## 順位表(役割→1位〜3位)
|
|
31
|
-
|
|
32
|
-
「実測」= dotagents 2026-08-19 役割配置実験([実験記録](../rag/models/role-placement-experiment-20260819.md)、各セルn=1)。
|
|
33
|
-
|
|
34
|
-
| 役割 | 1位 | 2位 | 3位 |
|
|
35
|
-
|---|---|---|---|
|
|
36
|
-
| 統括 | **オーナー指定**(参考候補: Opus 5×high、Grok 4.6×high、Fable 5×highスポット。順位は参考情報でpinはオーナー) | — | — |
|
|
37
|
-
| 反証 | Grok 4.6×high(実測14/14) | Sol×high(実測14/14。同着はGrok優先=オーナー裁定 2026-08-19) | Opus 5×high(実測13/14。唯一の誤りは真だが無害な指摘を殺した方向) |
|
|
38
|
-
| 監査・発見 | Grok 4.6×medium(実測recall 7/10・FP 0) | Sonnet 5×medium(実測6/10+partial 1・FP 0) | Terra×medium(実測6/10・FP 0) |
|
|
39
|
-
| 設計 | Opus 5×high(GDPval-AA・APEX首位 2026-08-19時点) | Grok 4.6×high(独立視点) | Sol×medium(過剰設計報告に注意) |
|
|
40
|
-
| 相談 | ChatGPT(gpt-connector・別quota。[06_gpt-connector.md](06_gpt-connector.md)) | Grok 4.6×medium | Fable 5×highスポット(契約critical) |
|
|
41
|
-
| 実装 | Terra×high(Terminal-Bench 2.1 78.4%) | Sonnet 5×medium〜high | Grok 4.6×medium(repo横断・長時間はhigh) |
|
|
42
|
-
| 局所コーディング | Luna×medium(実測: maxと品質差なし・14/14) | Sonnet 5×medium | Grok 4.6×medium |
|
|
43
|
-
| 軽作業 | Luna×low〜medium | Haiku(Claude枠で閉じる時。effortなし) | Grok 4.6×low |
|
|
44
|
-
| 調査 | Grok 4.6×low(字面)〜medium(統合)。X直結は唯一 | Sonnet 5×medium+Web | Sol/Terraで独立確認 |
|
|
45
|
-
|
|
46
|
-
### 構造規則(3つ)
|
|
47
|
-
|
|
48
|
-
1. **反証の1位は、成果を作ったモデルと別ベンダーから選ぶ。**
|
|
49
|
-
2. **順位は既定であって拘束ではない。** quota逼迫・入口障害・catalog不在時は次順位へ落とし、落とした事実を報告する。存在しないmodel/effortへはfallbackせず明示エラーにする。
|
|
50
|
-
3. **見逃し対策は2段構造で行う。** finderの増席でなく「疑いを列挙→反証役に裁かせる」工程を挟む(実測: finder 3席が全員見逃した欠陥3件を反証工程が回収した)。
|
|
51
|
-
|
|
52
|
-
## モデル台帳(slug・価格の解決はここだけ)
|
|
53
|
-
|
|
54
|
-
| モデル | slug | API定価(入力/出力 per Mtok) | context | effort段階 |
|
|
55
|
-
|---|---|---|---|---|
|
|
56
|
-
| Claude Fable 5 | alias `fable` | $10/$50 | 1M | low〜xhigh/max |
|
|
57
|
-
| Claude Opus 5 | alias `opus` | $5/$25 | 1M | low〜xhigh/max |
|
|
58
|
-
| Claude Sonnet 5 | alias `sonnet` | **$2/$10(恒久価格)** | 1M | low〜xhigh/max |
|
|
59
|
-
| Claude Haiku 4.5 | alias `haiku` | $1/$5 | 200K | **effortなし** |
|
|
60
|
-
| GPT-5.6 Sol | `gpt-5.6-sol` | $5/$30(長contextは$10/$45) | 1.05M | none〜max(Codex CLIはultraあり) |
|
|
61
|
-
| GPT-5.6 Terra | `gpt-5.6-terra` | $2/$12(長contextは$4/$18) | 1.05M | 同上 |
|
|
62
|
-
| GPT-5.6 Luna | `gpt-5.6-luna` | $0.20/$1.20(長contextは$0.40/$1.80) | 1.05M | none〜max(ultraなし) |
|
|
63
|
-
| Grok 4.6 | `grok-4.6` | $2/$6(200K超は$4/$12) | 500K | low/medium/high(既定)/xhigh |
|
|
64
|
-
|
|
65
|
-
価格は標準API定価であり、Claude Code・Codex・Grok Buildのsubscription quotaとは別物。OpenAIの長context課金は階層制(境界と現行値は公式pricingを実行時に確認)。Grok Buildのcatalogに`grok-build-0.1`(Composer 2.5系)の記載が公式docsにあるが、端末live catalogでの実在は未検証——Composer入口はcatalog確認まで`unsupported`のまま、Grokへfallbackしない。根拠は [GPT-5.6](../rag/models/gpt-5.6-family.md)、[Claude 5](../rag/models/claude-5-family.md)、[Grok 4.6](../rag/models/xai-grok46.md)。
|
|
66
|
-
|
|
67
|
-
### 消費枠(quotaの独立勘定。規則2のfallback判断に使う)
|
|
68
|
-
|
|
69
|
-
Anthropic(Claude Code本体・Agent/Workflow)/OpenAI Codex(Codex CLI・codex-sidecar・aiterm codex_agent)/OpenAI ChatGPT(gpt-connector・API fallback禁止)/xAI(Grok Build・aiterm grok_agent)の4枠は別勘定。同役割の次順位が別枠なら、quota逼迫時のfallbackはコスト増でなく枠の移動になる。
|
|
70
|
-
|
|
71
|
-
## 入口と使い分け
|
|
72
|
-
|
|
73
|
-
- **Codex親の三入口を分ける**: ① native subagent=repo密結合、② external execution=codex-sidecar/aiterm、③ consultation=gpt-connector。Grok/ComposerはAitermの別harness入口であり、Codex→Codexの入口判断とは別契約。
|
|
74
|
-
- Aitermの`codex_agent`/`grok_agent`/`claude_agent`はmodelとeffortを毎回明示する。live catalog不在・effort非対応は明示エラーにし、別modelへfallbackしない。
|
|
75
|
-
- **委譲の安全・回収・受入契約は[委譲契約](../shared/orchestrate/delegation-contract.md)が正本**。Aitermの運用型は[aiterm-dispatch](../shared/orchestrate/aiterm-dispatch.md)を正とする。external writerはinstalled→registered→verified→execution-verifiedの最終段だけに置く。
|
|
76
|
-
- codex-sidecarはmodel/effortを毎回明示するか`.codex-sidecar.yml` defaultsへ置く。現行schemaはlow〜xhighでmaxを渡せない。
|
|
77
|
-
- **役割と配置関係の機械可読な対応**は`lib/orchestrate/placement-policy.mjs` v1が固定する。自動ConsultationはAnthropic/OpenAIだけだが、これは現行配線のclosed enumであり、xAIの能力評価ではない。
|
|
78
|
-
|
|
79
|
-
## effortの規範
|
|
80
|
-
|
|
81
|
-
effortは単調に品質を上げない。まず役割に合うpresetへ置き、同じ代表taskで隣接levelを比べる。失敗後に変えるのはmodel tierかeffortの片方だけにする。
|
|
82
|
-
|
|
83
|
-
| model | 出発点 | 上げ下げの規律 |
|
|
84
|
-
|---|---|---|
|
|
85
|
-
| Opus 5 | **medium**(工程限定の実装・review)、**high**(長期agent・複雑設計) | xhigh/maxは長時間の知識仕事で測定差が出た時だけ。権限境界が緩い工程へ高effortで置かない |
|
|
86
|
-
| Fable 5 | high | 契約criticalのスポット。常用親と同義になる使い方はしない |
|
|
87
|
-
| Sonnet 5 | medium | finderの字面回収だけlow。高effortは代表taskで差が出た時 |
|
|
88
|
-
| Haiku 4.5 | effortなし | 存在しない`haiku×low`を指定しない |
|
|
89
|
-
| Sol/Terra | medium | high/xhighは測定可能な品質差がある時。maxは最難関quality-first。ultraはCodex harnessのmax+自動fan-outであり明示要求時だけ |
|
|
90
|
-
| Luna | **分解済み・仕様固定・focused test付きはmedium**(2026-08-19実測: medium=max同格・14/14) | 分解が甘い仕事はLunaのeffortを上げて救わず別モデルを選ぶ(旧「maxのみ」実測の教訓を適用範囲限定で継承) |
|
|
91
|
-
| Grok 4.6 | low=X/字面回収、medium=統合調査・監査finder、high=統括・反証・長時間agent | xhighは思考loopと遅延の外部報告がある。代表taskでhighを上回った時だけ使う |
|
|
92
|
-
|
|
93
|
-
## Grok 4.6をどう読むか
|
|
94
|
-
|
|
95
|
-
xAI公式値ではoffice/agentic系で最前線級、DeepSWE・TerminalBenchでは比較対象を下回る。Xには統括・review・長時間実装の成功例と、security誤判定・部分読み完了誤認・思考loopの失敗例が併存する。dotagents実測(2026-08-19)では監査finderのrecall首位(7/10・FP 0)と反証満点(14/14)で、監査・反証・調査の第一候補として実戦配置する。
|
|
96
|
-
|
|
97
|
-
## 指定と世代交代時の更新手順
|
|
98
|
-
|
|
99
|
-
- Claude Code内はfloating alias(`fable`/`opus`/`sonnet`/`haiku`)だけを使う。Agent/Workflowのmodelとeffortは対応する場合に毎回明示し、Haikuへeffortを付けない。
|
|
100
|
-
- Codexにfloating aliasがないため、`codex/agents/*.toml`と`.codex-sidecar.yml`は具体slugを持つ公認例外。世代交代時は本書と同一commitで更新する。
|
|
101
|
-
- 外部CLIはpinせず`agents-update`でlatest追従する。model live catalogは実行直前に見る。
|
|
102
|
-
- オーナーの世代交代宣言後、`grep -rn "前提:"`で影響面を列挙し、本書、公認例外、focused fixture、RAGを同時更新する。新規候補は小さな実戦比較を行い、失敗も次の配置判断へ残す。
|
|
103
|
-
- 順位の変更は実測(役割配置実験の再走または実戦の成功率・手戻り)を根拠にし、実験記録をRAGへ残してから表を書き換える。
|
|
1
|
+
# 02_models — 役割→モデル×エフォート順位表(唯一の参照点)
|
|
2
|
+
|
|
3
|
+
<!-- 前提: 2026-08-19 更新(Claude 5 / GPT-5.6 / Grok 4.6 世代)。バージョン固定禁止(PLAN 原則9)。モデル名をこの表以外+公認例外(codex/agents/*.toml・.codex-sidecar.yml)に書き散らさない -->
|
|
4
|
+
|
|
5
|
+
方針: skill・agents・委譲契約・スクリプトは**役割名**でモデルを指し、具体名への解決はこの表だけが担う。本書は役割を先に定義し、役割ごとに適したモデル×effortを1位〜3位で与える。ベンダー別レーンの表構造は撤廃した(オーナー裁定 2026-08-19)。世代交代時は**この1枚+公認例外2種を更新して push すれば全端末が追従**する。更新トリガーはオーナーの宣言(PLAN 原則6)。
|
|
6
|
+
|
|
7
|
+
背骨: **役割が要求する能力で選び、順位は実測で昇降格する。ただし、現状維持に候補側だけの立証責任を負わせない。** 新しい有力候補は代表実務へ期限・範囲を切って投入し、成功率・手戻り・監査工数・総token・所要時間・quotaで現役と比較する。未検証の現役を「安全」、未検証の新顔を「危険」とは扱わない。
|
|
8
|
+
|
|
9
|
+
本書の根拠は次の4種を混ぜない。
|
|
10
|
+
|
|
11
|
+
1. **一次事実**: model提供元公式の価格、context、対応effort、live catalog。
|
|
12
|
+
2. **外部観測**: benchmark、Xの利用報告、独立評価。harness・標本数・再現条件を併記する。
|
|
13
|
+
3. **dotagents実測**: 当工場のtask、監査通過率、配線実測。一般性能へ拡張しない。
|
|
14
|
+
4. **運用判断**: 上記を踏まえた配置。客観的に確定した事実を「オーナー裁定」と呼ばない。
|
|
15
|
+
|
|
16
|
+
**親のモデル×effortはオーナーの領分**であり、規範・AIはピンを打ち替えない。親候補と根拠は提示し、子の配置はこの表で解決する。
|
|
17
|
+
|
|
18
|
+
## 役割の定義(9つ)
|
|
19
|
+
|
|
20
|
+
1. **統括** — campaign/会話の主体。裁定・契約クリティカルな受入・commit
|
|
21
|
+
2. **反証** — 実ファイルを読んで主張を殺しにかかる
|
|
22
|
+
3. **監査・発見** — 自成果物(repo・設計・文書)の欠陥出し。誤検知は後段が裁く(第三者reviewを含む)
|
|
23
|
+
4. **設計** — 実装前の構造・境界・停止判断の案出し
|
|
24
|
+
5. **相談** — 実読不要の純推論second opinion
|
|
25
|
+
6. **実装** — 仕様固定済みのまとまった実装・テスト・移設
|
|
26
|
+
7. **局所コーディング** — focused test付きの狭い修正・実装
|
|
27
|
+
8. **軽作業** — 分類・抽出・字面回収
|
|
28
|
+
9. **調査** — 外部事実の回収〜統合(浅↔深はeffortで刻む)
|
|
29
|
+
|
|
30
|
+
## 順位表(役割→1位〜3位)
|
|
31
|
+
|
|
32
|
+
「実測」= dotagents 2026-08-19 役割配置実験([実験記録](../rag/models/role-placement-experiment-20260819.md)、各セルn=1)。
|
|
33
|
+
|
|
34
|
+
| 役割 | 1位 | 2位 | 3位 |
|
|
35
|
+
|---|---|---|---|
|
|
36
|
+
| 統括 | **オーナー指定**(参考候補: Opus 5×high、Grok 4.6×high、Fable 5×highスポット。順位は参考情報でpinはオーナー) | — | — |
|
|
37
|
+
| 反証 | Grok 4.6×high(実測14/14) | Sol×high(実測14/14。同着はGrok優先=オーナー裁定 2026-08-19) | Opus 5×high(実測13/14。唯一の誤りは真だが無害な指摘を殺した方向) |
|
|
38
|
+
| 監査・発見 | Grok 4.6×medium(実測recall 7/10・FP 0) | Sonnet 5×medium(実測6/10+partial 1・FP 0) | Terra×medium(実測6/10・FP 0) |
|
|
39
|
+
| 設計 | Opus 5×high(GDPval-AA・APEX首位 2026-08-19時点) | Grok 4.6×high(独立視点) | Sol×medium(過剰設計報告に注意) |
|
|
40
|
+
| 相談 | ChatGPT(gpt-connector・別quota。[06_gpt-connector.md](06_gpt-connector.md)) | Grok 4.6×medium | Fable 5×highスポット(契約critical) |
|
|
41
|
+
| 実装 | Terra×high(Terminal-Bench 2.1 78.4%) | Sonnet 5×medium〜high | Grok 4.6×medium(repo横断・長時間はhigh) |
|
|
42
|
+
| 局所コーディング | Luna×medium(実測: maxと品質差なし・14/14) | Sonnet 5×medium | Grok 4.6×medium |
|
|
43
|
+
| 軽作業 | Luna×low〜medium | Haiku(Claude枠で閉じる時。effortなし) | Grok 4.6×low |
|
|
44
|
+
| 調査 | Grok 4.6×low(字面)〜medium(統合)。X直結は唯一 | Sonnet 5×medium+Web | Sol/Terraで独立確認 |
|
|
45
|
+
|
|
46
|
+
### 構造規則(3つ)
|
|
47
|
+
|
|
48
|
+
1. **反証の1位は、成果を作ったモデルと別ベンダーから選ぶ。**
|
|
49
|
+
2. **順位は既定であって拘束ではない。** quota逼迫・入口障害・catalog不在時は次順位へ落とし、落とした事実を報告する。存在しないmodel/effortへはfallbackせず明示エラーにする。
|
|
50
|
+
3. **見逃し対策は2段構造で行う。** finderの増席でなく「疑いを列挙→反証役に裁かせる」工程を挟む(実測: finder 3席が全員見逃した欠陥3件を反証工程が回収した)。
|
|
51
|
+
|
|
52
|
+
## モデル台帳(slug・価格の解決はここだけ)
|
|
53
|
+
|
|
54
|
+
| モデル | slug | API定価(入力/出力 per Mtok) | context | effort段階 |
|
|
55
|
+
|---|---|---|---|---|
|
|
56
|
+
| Claude Fable 5 | alias `fable` | $10/$50 | 1M | low〜xhigh/max |
|
|
57
|
+
| Claude Opus 5 | alias `opus` | $5/$25 | 1M | low〜xhigh/max |
|
|
58
|
+
| Claude Sonnet 5 | alias `sonnet` | **$2/$10(恒久価格)** | 1M | low〜xhigh/max |
|
|
59
|
+
| Claude Haiku 4.5 | alias `haiku` | $1/$5 | 200K | **effortなし** |
|
|
60
|
+
| GPT-5.6 Sol | `gpt-5.6-sol` | $5/$30(長contextは$10/$45) | 1.05M | none〜max(Codex CLIはultraあり) |
|
|
61
|
+
| GPT-5.6 Terra | `gpt-5.6-terra` | $2/$12(長contextは$4/$18) | 1.05M | 同上 |
|
|
62
|
+
| GPT-5.6 Luna | `gpt-5.6-luna` | $0.20/$1.20(長contextは$0.40/$1.80) | 1.05M | none〜max(ultraなし) |
|
|
63
|
+
| Grok 4.6 | `grok-4.6` | $2/$6(200K超は$4/$12) | 500K | low/medium/high(既定)/xhigh |
|
|
64
|
+
|
|
65
|
+
価格は標準API定価であり、Claude Code・Codex・Grok Buildのsubscription quotaとは別物。OpenAIの長context課金は階層制(境界と現行値は公式pricingを実行時に確認)。Grok Buildのcatalogに`grok-build-0.1`(Composer 2.5系)の記載が公式docsにあるが、端末live catalogでの実在は未検証——Composer入口はcatalog確認まで`unsupported`のまま、Grokへfallbackしない。根拠は [GPT-5.6](../rag/models/gpt-5.6-family.md)、[Claude 5](../rag/models/claude-5-family.md)、[Grok 4.6](../rag/models/xai-grok46.md)。
|
|
66
|
+
|
|
67
|
+
### 消費枠(quotaの独立勘定。規則2のfallback判断に使う)
|
|
68
|
+
|
|
69
|
+
Anthropic(Claude Code本体・Agent/Workflow)/OpenAI Codex(Codex CLI・codex-sidecar・aiterm codex_agent)/OpenAI ChatGPT(gpt-connector・API fallback禁止)/xAI(Grok Build・aiterm grok_agent)の4枠は別勘定。同役割の次順位が別枠なら、quota逼迫時のfallbackはコスト増でなく枠の移動になる。
|
|
70
|
+
|
|
71
|
+
## 入口と使い分け
|
|
72
|
+
|
|
73
|
+
- **Codex親の三入口を分ける**: ① native subagent=repo密結合、② external execution=codex-sidecar/aiterm、③ consultation=gpt-connector。Grok/ComposerはAitermの別harness入口であり、Codex→Codexの入口判断とは別契約。
|
|
74
|
+
- Aitermの`codex_agent`/`grok_agent`/`claude_agent`はmodelとeffortを毎回明示する。live catalog不在・effort非対応は明示エラーにし、別modelへfallbackしない。
|
|
75
|
+
- **委譲の安全・回収・受入契約は[委譲契約](../shared/orchestrate/delegation-contract.md)が正本**。Aitermの運用型は[aiterm-dispatch](../shared/orchestrate/aiterm-dispatch.md)を正とする。external writerはinstalled→registered→verified→execution-verifiedの最終段だけに置く。
|
|
76
|
+
- codex-sidecarはmodel/effortを毎回明示するか`.codex-sidecar.yml` defaultsへ置く。現行schemaはlow〜xhighでmaxを渡せない。
|
|
77
|
+
- **役割と配置関係の機械可読な対応**は`lib/orchestrate/placement-policy.mjs` v1が固定する。自動ConsultationはAnthropic/OpenAIだけだが、これは現行配線のclosed enumであり、xAIの能力評価ではない。
|
|
78
|
+
|
|
79
|
+
## effortの規範
|
|
80
|
+
|
|
81
|
+
effortは単調に品質を上げない。まず役割に合うpresetへ置き、同じ代表taskで隣接levelを比べる。失敗後に変えるのはmodel tierかeffortの片方だけにする。
|
|
82
|
+
|
|
83
|
+
| model | 出発点 | 上げ下げの規律 |
|
|
84
|
+
|---|---|---|
|
|
85
|
+
| Opus 5 | **medium**(工程限定の実装・review)、**high**(長期agent・複雑設計) | xhigh/maxは長時間の知識仕事で測定差が出た時だけ。権限境界が緩い工程へ高effortで置かない |
|
|
86
|
+
| Fable 5 | high | 契約criticalのスポット。常用親と同義になる使い方はしない |
|
|
87
|
+
| Sonnet 5 | medium | finderの字面回収だけlow。高effortは代表taskで差が出た時 |
|
|
88
|
+
| Haiku 4.5 | effortなし | 存在しない`haiku×low`を指定しない |
|
|
89
|
+
| Sol/Terra | medium | high/xhighは測定可能な品質差がある時。maxは最難関quality-first。ultraはCodex harnessのmax+自動fan-outであり明示要求時だけ |
|
|
90
|
+
| Luna | **分解済み・仕様固定・focused test付きはmedium**(2026-08-19実測: medium=max同格・14/14) | 分解が甘い仕事はLunaのeffortを上げて救わず別モデルを選ぶ(旧「maxのみ」実測の教訓を適用範囲限定で継承) |
|
|
91
|
+
| Grok 4.6 | low=X/字面回収、medium=統合調査・監査finder、high=統括・反証・長時間agent | xhighは思考loopと遅延の外部報告がある。代表taskでhighを上回った時だけ使う |
|
|
92
|
+
|
|
93
|
+
## Grok 4.6をどう読むか
|
|
94
|
+
|
|
95
|
+
xAI公式値ではoffice/agentic系で最前線級、DeepSWE・TerminalBenchでは比較対象を下回る。Xには統括・review・長時間実装の成功例と、security誤判定・部分読み完了誤認・思考loopの失敗例が併存する。dotagents実測(2026-08-19)では監査finderのrecall首位(7/10・FP 0)と反証満点(14/14)で、監査・反証・調査の第一候補として実戦配置する。
|
|
96
|
+
|
|
97
|
+
## 指定と世代交代時の更新手順
|
|
98
|
+
|
|
99
|
+
- Claude Code内はfloating alias(`fable`/`opus`/`sonnet`/`haiku`)だけを使う。Agent/Workflowのmodelとeffortは対応する場合に毎回明示し、Haikuへeffortを付けない。
|
|
100
|
+
- Codexにfloating aliasがないため、`codex/agents/*.toml`と`.codex-sidecar.yml`は具体slugを持つ公認例外。世代交代時は本書と同一commitで更新する。
|
|
101
|
+
- 外部CLIはpinせず`agents-update`でlatest追従する。model live catalogは実行直前に見る。
|
|
102
|
+
- オーナーの世代交代宣言後、`grep -rn "前提:"`で影響面を列挙し、本書、公認例外、focused fixture、RAGを同時更新する。新規候補は小さな実戦比較を行い、失敗も次の配置判断へ残す。
|
|
103
|
+
- 順位の変更は実測(役割配置実験の再走または実戦の成功率・手戻り)を根拠にし、実験記録をRAGへ残してから表を書き換える。
|