STEP 1579 (b) — Plan Runner Msg (arc 3/3 完)
目的
STEP 1569 Ophyd 読解 top 3 の (b):
Msg indirection layer: rei-aios MCP tool 群を 「plan は tool call を yield → SafetyGate dispatch」 の 2 層化 — introspection / dry-run / 安全停止を得る
Bluesky の RunEngine は 10 年運用実績のある pattern:
# Bluesky
def my_plan():
yield Msg('set', motor, 5)
yield Msg('trigger', detector)
yield Msg('read', detector)
RE(my_plan()) # RunEngine が dispatch
これを Rei stack (TypeScript / MCP tool 群 / D-FUMT₈ verdict) 向けに portable な形で minimum viable 実装。
5 Msg types
| Msg type | 用途 | Runner action |
|---|---|---|
call |
tool 呼出 (toolName + args) | toolHandler 経由 dispatch、 result 記録、 NoData 検出 |
check |
SafetyGate approval (hazardLevel + reason) | SafetyGate handler 評価、 拒否 → throw |
dry_run_hint |
plan 意図の annotation | trace 記録のみ |
wait |
N ms 待機 (settle_time 等) | dry-run 時は skip、 実行時は setTimeout |
checkpoint |
resumable point label (data 付) | trace 記録 (将来 pause/resume 起点) |
PlanRunner (dispatcher)
const runner = new PlanRunner({
dryRun: false, // true = tool 呼出 skip、 introspection のみ
toolHandler: (name, args) => ..., // 実 dispatch (MCP integration 点)
safetyGate: (checkMsg) => ..., // STEP 1345 SafetyGate 統合点
waitFn: (ms) => new Promise(...), // 差替可 (test 時 mock 化)
});
const trace: TraceEntry[] = await runner.run(myPlan());
// trace = 各 Msg の timestamp / result / noDataDetected を記録
const summary = summarizeTrace(trace);
// { totalMsgs, byType, noDataHits, safetyChecks, approvedChecks }
Pilot plan: diskDiagnosticPlan
disk_health_verdict (STEP 1567、 Layer 1 sensor tool) を Msg indirection 越しに呼出:
function* diskDiagnosticPlan(input): Plan {
yield dryRunHintMsg('disk-diagnostic plan v0.1');
yield checkMsg('disk_health_verdict', input, 'caution',
'SMART attribute read requires privileged access');
const r = yield callMsg('disk_health_verdict', input, 'primary-disk-scan');
yield checkpointMsg('post-primary-scan', {
verdict: r.verdict, reason: r.reason
});
// 条件分岐 follow-up
if (r.verdict === 'ZERO' && r.reason === 'no_data') {
yield callMsg('log_and_retry', {...}, 'no-data-retry');
} else if (r.verdict === 'FALSE') {
yield callMsg('escalate_backup_verify', {...}, 'critical-escalation');
} else {
yield callMsg('log_healthy', {...}, 'log-only');
}
}
実行 3 パターン確認済:
- Healthy disk (reallocatedSectors=0, temperature=40): verdict=TRUE → log_healthy 分岐
- NoData (empty input): verdict=ZERO, reason=no_data、
noDataDetected=trueflag → log_and_retry 分岐 - Dry-run: 全 tool 呼出 skip、 introspection のみ (verdict undefined → else branch)
STEP 1569 top 3 統合
| top 3 | STEP | PlanRunner での消費 |
|---|---|---|
| (a) NoDataResult 型化 | STEP 1571 | call Msg dispatch 後、 isNoDataResult() で自動判定、 noDataDetected flag として trace に記録。 pilot plan は no-data path で log_and_retry に routing |
| (c) Protocol interface | STEP 1576 | toolHandler 出力が Verdicted<V> shape を持てば PlanRunner は verdict/reason を trace に立てられる。 conformance 済 tool は自動対応 |
| (b) Msg indirection | STEP 1579 (本) | plan を Msg generator として書き、 PlanRunner が dispatch。 dry-run / SafetyGate / trace / conditional branching / composability を得る |
Test — 48/48 PASS
- Msg constructors (5 types)
- PlanRunner basic run — 1 checkpoint
- toolHandler dispatch
- SafetyGate rejection (block → throw)
- Dry-run mode — 0 tool invocations
- NoDataResult 自動検出 (noDataDetected flag)
- Trace summarization (byType / noDataHits / safetyChecks)
- Pilot plan full 5-Msg sequence
- Pilot plan dry-run introspection
- Pilot plan no-data path routing
Ophyd / Bluesky 対応
| Bluesky/Ophyd | Rei (STEP 1579) |
|---|---|
| Msg (15 types) | Msg (5 types v0.1: call / check / dry_run_hint / wait / checkpoint) |
| plan = generator yielding Msg | Plan = Generator<Msg, unknown, MsgResult> |
| RunEngine (RE) | PlanRunner |
| RE dry-run | PlanRunner dryRun: true |
| Status object | MsgResult tagged union (call_result / check_verdict / dry_run_ack / wait_completed / checkpoint_ack) |
| hardware dispatch (Signal.put) | toolHandler(toolName, args) |
| safety guards | SafetyGate handler (STEP 1345 統合点) |
| NotConnectedError | NoDataResult (STEP 1571) — call_result の noDataDetected flag |
Honest scope
- v0.1 = 5 Msg type + 1 pilot plan + 48 test。 MCP server 全 tool の Msg wrapping は別 STEP (逐次 migration、 本 STEP は pattern 実証まで)
- Bluesky の 15 Msg (open_run / close_run / subscribe / monitor / kickoff / complete 等) は Rei で対応する semantics が明確になり次第追加。 v0.1 は verdict tool dispatch に必要な最小 subset
- Downstream tool (log_and_retry / escalate_backup_verify / log_healthy) は pilot 内で stub 返却。 実 MCP tool 化は別 STEP
- Wait Msg の time-precision は setTimeout 依存 (数 ms 精度)。 hardware settling で必要な μs 精度は別 handler 差替で対応
- PlanRunner は状態 stateless。 checkpoint からの resume は将来 v0.2 (persistent trace / replay engine)