STEP 1579 (b) — Plan Runner Msg (arc 3/3 完)

STEP 1579 · STEP 1569 top 3 の (b) 実装 · Bluesky RunEngine + Msg pattern の Rei 版最小実装 · STEP 1569 arc 全 3/3 完了 · Rei-AIOS · 2026-08-30

目的

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 パターン確認済:

  1. Healthy disk (reallocatedSectors=0, temperature=40): verdict=TRUE → log_healthy 分岐
  2. NoData (empty input): verdict=ZERO, reason=no_data、 noDataDetected=true flag → log_and_retry 分岐
  3. Dry-run: 全 tool 呼出 skip、 introspection のみ (verdict undefined → else branch)

STEP 1569 top 3 統合

top 3STEPPlanRunner での消費
(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

  1. Msg constructors (5 types)
  2. PlanRunner basic run — 1 checkpoint
  3. toolHandler dispatch
  4. SafetyGate rejection (block → throw)
  5. Dry-run mode — 0 tool invocations
  6. NoDataResult 自動検出 (noDataDetected flag)
  7. Trace summarization (byType / noDataHits / safetyChecks)
  8. Pilot plan full 5-Msg sequence
  9. Pilot plan dry-run introspection
  10. Pilot plan no-data path routing

Ophyd / Bluesky 対応

Bluesky/OphydRei (STEP 1579)
Msg (15 types)Msg (5 types v0.1: call / check / dry_run_hint / wait / checkpoint)
plan = generator yielding MsgPlan = Generator<Msg, unknown, MsgResult>
RunEngine (RE)PlanRunner
RE dry-runPlanRunner dryRun: true
Status objectMsgResult tagged union (call_result / check_verdict / dry_run_ack / wait_completed / checkpoint_ack)
hardware dispatch (Signal.put)toolHandler(toolName, args)
safety guardsSafetyGate handler (STEP 1345 統合点)
NotConnectedErrorNoDataResult (STEP 1571) — call_result の noDataDetected flag

Honest scope