feat(daemon): Phase D — GEM300 in-the-loop (jobs, recipes, EC changes)
Semantics settled and documented: v1 is observe-and-report. The engine keeps acking S16/S3/S7/S2F15 from its FSM tables — exactly the behaviour both reference implementations validated — while the tool observes lifecycle events on the Subscribe stream and reports physical progress back. Gating stays the documented v2 deferred-reply item. Engine: two new store observers (HandlerSlot pattern) — RecipeStore fires (ppid, body) after an add (S7F3 downloads), EquipmentConstantStore fires (id, value) on ACCEPTED S2F15 writes only. Unit-tested. Daemon: the service registers PJ/recipe/EC observers (io thread; add_ observers coexist with register_default_handlers' primaries) and fans the new HostRequest variants out via push_request (fire-and-forget, no- buffering contract). ProcessJob carries action (Start->START, Resume-> RESUME, Paused->PAUSE, Stopping->STOP, Aborting->ABORT) + recipe + material bindings read store-side on the io thread. ReportProcessJob maps SETTING_UP ->SetupComplete, COMPLETE->ProcessComplete, ABORTED->AbortComplete via read_sync; PROCESSING is informational; unknown job => INVALID_OBJECT, table-rejected transition => CANNOT_DO_NOW. Carriers deferred (CarrierStore has no observer machinery; ReportCarrier stays UNIMPLEMENTED) — roadmap. Python client: on_process_job / on_recipe / on_constant_change decorators + report_job(job_id, state); ProcessJob dataclass exported. Tests: daemon suite 141 -> 175 assertions — the full in-process loop (S16F11 create -> tool setup -> S16F5 PJSTART -> stream ProcessJob with recipe+carriers -> ReportProcessJob(COMPLETE) -> FSM at ProcessComplete), rejection paths, S7F3 -> ProcessProgram, S2F15 -> ConstantChange with the configured name. Core 475/3097 (observer units). Live regression: daemon interop 20 checks + pyclient 13 checks still green against the running daemon. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
This commit is contained in:
+23
-6
@@ -202,12 +202,29 @@ debts tax every later phase, and the most valuable tests aren't automated.
|
||||
`cpp_mini_tool` is the worked example.
|
||||
|
||||
### Phase D — GEM300 in-the-loop (process/carrier tools)
|
||||
10. ⬜ Settle job/carrier semantics (who acks S16F5/S3F17, gate vs observe —
|
||||
see proto comments), then wire `ProcessJob`/`CarrierAction` onto the
|
||||
stream + `ReportProcessJob`/`ReportCarrier` into the PJ/CJ/carrier stores.
|
||||
11. ⬜ Recipe download (`ProcessProgram` on the stream when S7F3 lands) and
|
||||
EC-change notification (`ConstantChange` when S2F15 lands).
|
||||
12. ⬜ Interop scenarios for jobs/carriers vs secsgem-py + secs4j.
|
||||
10. 🚧 Semantics SETTLED: v1 is **observe-and-report** — the engine keeps
|
||||
acking S16/S3/S7/S2F15 from its FSM tables (what both references
|
||||
validated); the tool observes lifecycle events on the stream and reports
|
||||
physical progress back. Gating (tool decides the ack) = the documented
|
||||
v2 deferred-reply item. ✅ `ProcessJob` on the stream (PJ store observer:
|
||||
→Processing/Start=START, /Resume=RESUME, →Paused=PAUSE, →Stopping=STOP,
|
||||
→Aborting=ABORT; carries recipe + material bindings) +
|
||||
`ReportProcessJob` (SETTING_UP→SetupComplete, COMPLETE→ProcessComplete,
|
||||
ABORTED→AbortComplete; PROCESSING informational; INVALID_OBJECT /
|
||||
CANNOT_DO_NOW on unknown job / illegal transition). ⬜ Carriers deferred:
|
||||
`CarrierStore` has no observer machinery yet — apply the HandlerSlot
|
||||
pattern, then mirror the job wiring; `ReportCarrier` stays gRPC
|
||||
UNIMPLEMENTED (the stub default) until then.
|
||||
11. ✅ `ProcessProgram` on the stream when S7F3 lands (new RecipeStore
|
||||
added-observer) and `ConstantChange` when S2F15 is ACCEPTED (new
|
||||
EquipmentConstantStore changed-observer; rejected writes never fire).
|
||||
Python client: `on_process_job`/`on_recipe`/`on_constant_change` +
|
||||
`report_job`.
|
||||
12. 🚧 In-process E2E covers the full job loop (S16F11→S16F5→stream→report→
|
||||
FSM, 175-assertion daemon suite); secs4j's 55 checks run against the
|
||||
daemon (declarative path). ⬜ A live host-driven job loop with a
|
||||
subscribed tool needs raw S16 frames host-side (secsgem-py 0.3.0 lacks
|
||||
S16 builders — see interop/raw_gem300_harness.py for the frame source).
|
||||
|
||||
### Phase E — hardening & operations
|
||||
13. ⬜ gRPC exposure: default to localhost + document UDS; optional TLS creds.
|
||||
|
||||
Reference in New Issue
Block a user