feat(modules/airc): AttachRequest carries channel — owner-core model headless fix#1505
Conversation
…headless fix Iterating on the moment-of-truth test. With #1504 (socket discovery) landed, the next concrete break surfaced: AIRC daemon attach stream stopped: failed to attach to airc daemon: attach requires a channel in the owner-core model Per `airc-daemon/src/server.rs:274` + `airc-ipc/src/request.rs:144` docstring: the owner-core router subscribes PER CHANNEL — no global fan-out table. AttachRequest.channel is mandatory; clients attach once per room they care about. Continuum was sending `AttachRequest::default()` (no channel), which worked under an earlier model the substrate has since left behind. ### What ships - `discover_default_channel()` — parses `airc room` stdout for the scope's current room `channel: <uuid>` line + returns the UUID. Honors `$AIRC_DEFAULT_CHANNEL` env override (UUID) for tests + multi-room operators pinning the first attach. Robust to whitespace + alt-capitalization (`Channel:`, `CHANNEL:`); fails loud (UnparseableChannel error) if airc renames the field. - `AircModule::attach_channel: Option<RoomId>` new field, populated by `discover_and_construct` alongside the socket path. `initialize` spawns the daemon attach only when BOTH a socket AND a channel are available — partial degradation rather than boot failure. - `inbound_attach::spawn_daemon_attach` + `run_daemon_attach` take a `channel: RoomId` and put it in `AttachRequest.channel = Some(_)`. Single caller updated; no other code paths. - 4 new unit tests for the parser (typical airc room output, alt capitalization + whitespace, missing channel line, non-UUID after label) — 7 discovery tests total. ### Verification (manual end-to-end on this branch) $ rm -f /tmp/hctest.sock && \ target/release/continuum-core-server /tmp/hctest.sock > boot.log 2>&1 & $ grep -E "Discovered airc" boot.log Discovered airc daemon socket via `airc ipc-endpoint` socket_path="/Users/joel/.airc/runtime/airc-machine-…-v5.sock" Discovered airc default channel via `airc room` channel=11c1a7ac-cb85-5ca0-a5b4-2847280ea3fa # No more "attach requires a channel in the owner-core model" warning. $ cargo test --release --lib --features metal,accelerate airc::discovery test result: ok. 7 passed; 0 failed. ### Next concrete break revealed (follow-up #82, not in this PR) The attach now connects + passes the channel gate. Next-layer error: `AIRC daemon attach stream stopped: failed to read airc daemon event: Semantic(None, "missing field 'event'")` CBOR Response variant shape changed between continuum's pinned airc-ipc SHA (428f9281…) and the live daemon. Likely fix: SHA bump in src/workers/Cargo.toml after the AttachRequest channel change lands on airc canary. Tracked separately so this PR can ship the single, complete fix for break #2. ### Pattern Iterate-on-moment-of-truth: each fix uncovers the next layer; each PR is one well-scoped substrate change with end-to-end verification + a tracked follow-up for the next surfaced break. Three breaks revealed so far (1504, this PR, #82); breaks 1 + 2 fixed. ### Follow-ups (filed) - airc-side: `airc room --print-channel` flag (mirror the `airc ipc-endpoint` pattern) so continuum's stdout parser can be replaced with a stable contract. Note in the parser docstring. - continuum #82: CBOR Response shape mismatch / SHA bump. - continuum: multi-room attach (one daemon_attach task per channel when continuum rooms become first-class — currently single-room). ### References - airc owner-core model: `airc-daemon/src/server.rs:274`, `airc-ipc/src/request.rs:144` (AttachRequest docstring), `airc-lib/tests/common/mod.rs` (model description). - continuum#1504 — sibling PR (socket discovery) — this PR's prerequisite, already landed on canary. - airc#1095 — sibling PR (`airc ipc-endpoint`), pending Windows CI. - Memories: `headless-rust-must-work-soon`, `continuum-thesis-airc- is-the-medium`, `every-error-is-an-opportunity-to-battle-harden`, `agent-review-as-acceptable-approval`. - ALPHA-GAP §0A line 706 — headless target. Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
|
Adversarial reviewer verdict: APPROVE (per AGENTS.md §0 / airc#1094 agent-sign-off doctrine, applied across repos via continuum's Spawned as a fresh subagent with no prior context, prompted to default-to-BLOCK. Reviewer ID: Reasons
Strongest counterargument consideredThe Minor nit (non-blocking)The 5 legacy/test constructors ( BLOCK threshold not met. Merge on green CI. |
Summary
AttachRequest, satisfying airc's owner-core router model (airc-daemon/src/server.rs:274). Previously continuum sentAttachRequest::default(), which the daemon rejects withattach requires a channel in the owner-core model.discover_default_channel()parsesairc roomfor the scope's current room channel UUID and plumbs it through toAircModule::initializealongside the socket discovered in feat(modules/airc): headless socket discovery viaairc ipc-endpoint+ auto-install #1504.$AIRC_DEFAULT_CHANNELenv override (UUID) for tests or operators pinning a channel explicitly.airc room <name>), the rest of continuum-core boots — only the inbound attach is skipped, with a loud warning that quotes the remedy.Why
Next concrete break revealed by the headless moment-of-truth test (#1504 was the first break: socket discovery). With socket discovery fixed, the next-layer failure surfaced:
Per airc's owner-core router model: ONE machine account → ONE daemon → events routed per-channel (no global fan-out table).
AttachRequest.channelis mandatory — clients attach once per room they care about (or N attaches for N rooms). The oldAttachRequest::default()worked under an earlier model; the substrate moved + continuum didn't catch up.Joel direction: "let's do it" (after #1504 landed).
What this PR touches
src/workers/continuum-core/src/airc/discovery.rsdiscover_default_channel()+ parser forairc roomstdout + 4 new unit tests (typical, alt-cap, no-channel, non-uuid) + 2 newDiscoveryErrorvariantssrc/workers/continuum-core/src/airc/mod.rsdiscover_default_channelsrc/workers/continuum-core/src/airc/inbound_attach.rsspawn_daemon_attach+run_daemon_attachnow take achannel: RoomIdparameter; populateAttachRequest.channel = Some(channel)src/workers/continuum-core/src/modules/airc.rsattach_channel: Option<RoomId>field onAircModule;discover_and_constructruns both discoveries;initializerequires both before spawning the attachResolution order for the channel
$AIRC_DEFAULT_CHANNEL— explicit UUID override (tests, multi-room scopes pinning the first attach).airc roomstdout parse — find thechannel: <uuid>line and parse the UUID. Robust to whitespace + alt-capitalization (Channel:,CHANNEL:).Err(DiscoveryError::RoomCommandFailed | UnparseableChannel)— caller decides whether to skip attach or fail (AircModule::discover_and_constructskips attach with a loud warning quoting the operator remedy).Test plan
cargo build --release --bin continuum-core-server --features metal,acceleratecargo test --release --lib --features metal,accelerate airc::discovery— 7 total (3 from feat(modules/airc): headless socket discovery viaairc ipc-endpoint+ auto-install #1504 + 4 new)Discovered airc default channel via 'airc room'+ noattach requires a channelwarningAIRC_DEFAULT_CHANNEL=<uuid>skips the room parseairc room <name>boots cleanly with loud warning quoting the remedyFollow-ups
spawn_daemon_attachtask per known channel.airc roomparser. When airc addsairc room --print-channel(mirroring theairc ipc-endpointdecoupling pattern), switch to that flag for a stable contract. Filed as airc-side follow-up.References
airc ipc-endpoint+ auto-install #1504, awaiting Windows CI) —airc ipc-endpointcommand. This PR uses the same shell-out-and-parse pattern.airc-daemon/src/server.rs:274,airc-ipc/src/request.rs:144(AttachRequest docstring),airc-lib/tests/common/mod.rs(model description).headless-rust-must-work-soon,continuum-thesis-airc-is-the-medium,every-error-is-an-opportunity-to-battle-harden,agent-review-as-acceptable-approval(adversarial reviewer pattern this PR uses for sign-off)Generated with Claude Code