fix(boot): typed AircDiscovery + BootMode + conditional manifest (Slice A.2.1) - #1528
Merged
Merged
Conversation
joelteply
added a commit
that referenced
this pull request
Jun 4, 2026
…compression + load-bearing seam coverage Addresses the three converging adversarial reviewers' BLOCK on the first A.2.1 attempt. Each reviewer independently caught the same silent-fallback pattern shifted one frame deeper from where Slice A had it; this commit closes it structurally + adds the test rigging that locks the closure in. ## R2#1 — `AircModule::from_discovery(Degraded)` Uuid::nil bug Reviewers #1 (correctness) and #2 (doctrine) both BLOCKED on the `Degraded` arm reading `partial.peer_id.unwrap_or_else(Uuid::nil)` and constructing a real `DaemonAircEventTransport` against the discovered (potentially stale) socket. In `--mode=inference-only`, the mode gate did not trip, so the module registered, every realtime publish either ECONNREFUSED'd against the stale socket OR went out attributed to `Uuid::nil` — exactly the silent-fallback pattern [[no-fallbacks-ever]] forbids, just shifted one frame from Slice A's location. **Fix:** `Degraded` AND `Unreachable` now both collapse to `with_queue_client(...)`. The `partial` field on the enum is retained for operator observability (what we DID resolve before discovery declared not-Healthy), but the module construction does not pretend. Five new tests in `from_discovery_tests` pin every branch + the cross-variant invariant `only_healthy_exposes_a_ daemon_socket`. R2#1 is now structurally impossible. ## R2#3 + R3#3 — compression principle violation in manifest Reviewers #2 and #3 converged on the same point: three constants (`MODULES_CORE`, `MODULES_PERSONA_HOSTING`, `ALL_KNOWN_MODULES`) with three drift-catcher tests verifying they agree with each other. That violates the compression principle Joel emphasized throughout the session — one logical decision, one place. The drift catchers don't catch the actual regression class (developer adds `runtime.register(NewModule)` in `ipc/mod.rs` without updating a constant). **Fix:** ONE `MODULES: &[(&str, ModuleCategory)]` list with each row tagged by category. `required_modules(&discovery, mode)` filters this list; `all_known_modules()` projects names from it. No parallel list to drift against. The drift catchers reduce to correctness tests over the projection (`required_modules_size_ matches_category_dispatch`, `every_core_module_appears_in_every_ required_set`, `persona_hosting_modules_appear_only_when_ dispatched` — the last is the R1#1 structural lock). The R3#3 "developer adds register without updating manifest" class still requires the A.2.2 insta snapshot that boots the runtime and asserts `registry().module_names()` == projection of `MODULES`. That deferral is honest; the categorized list is the foundation the snapshot will anchor to. ## Purity — `discover_and_construct` deleted, not deprecated Per Joel's "purity is always worth it" doctrine, the legacy `AircModule::discover_and_construct` method is removed entirely. It carried the same `Uuid::nil()` soft-fallback at line 158 that R2#1 was about. With `from_discovery()` covering every production case, the legacy method's only value was easing migration — and "ease of migration" is not worth retaining a code path that encodes the bug pattern this slice exists to close. Same standard for the unused-import cleanup that followed: no deprecated shims, no commented-out `legacy //` blocks, no re-exports for "back-compat." If a future caller wants the old shape, they read the git history and write it again with the typed primitives. ## ORT panic filter — held for A.2.2 per reviewer #2 Reviewer #2's BLOCKING #3: shipping `install_ort_panic_filter` WITHOUT the paired `🔊/🔇 Voice subsystem: <state>` boot banner line leaves the default-mode (`FullCitizen`) operator with zero signal that voice is unavailable. The filter mutes the only signal; the indicator hasn't landed yet. **Fix:** removed the panic filter entirely from A.2.1. Both the filter AND the indicator (with its `libloading::Library::new( "libonnxruntime.dylib")` dlopen probe) land together in A.2.2. This is what reviewer #2 explicitly recommended as the doctrinally honest sequencing: "ship them together OR hold both." ## Test coverage added - `from_discovery_tests` (5 tests): - healthy_produces_fully_configured_module - degraded_with_partial_socket_collapses_to_queue_only (R2#1) - degraded_with_full_partial_state_still_collapses_to_queue_only - unreachable_collapses_to_queue_only - only_healthy_exposes_a_daemon_socket (cross-variant invariant) - `discovery_failure_mapping_tests` (10 tests): - All 8 `DiscoveryError → DiscoveryFailure` variant projections - stale_socket_from_status_err carries path + underlying - stale_socket_handles_non_status_errors - `conditional_modules_tests` (refactored, now 9 tests for the new MODULES shape): - full_citizen_healthy_requires_persona_hosting_modules (R1) - inference_only_does_not_require_persona_hosting_modules (R1) - fail_fast_healthy_requires_persona_hosting_modules - full_citizen_degraded_uses_core_only_set - modules_list_has_unique_names (drift catcher) - required_modules_size_matches_category_dispatch - all_known_modules_derives_from_modules - every_core_module_appears_in_every_required_set - persona_hosting_modules_appear_only_when_dispatched (the structural R1#1 lock) - `discovery_state::tests` (unchanged, 4 tests) - `boot_mode::tests` (unchanged, 12 tests) **Total: 40/40 pass.** Cargo check clean. ## Reviewer findings status | Finding | Reviewer | Status | |---|---|---| | R1#1 — `EXPECTED_MODULES` contradicts inference-only | R1 | CLOSED via categorized MODULES + `persona_hosting_modules_appear_only_when_dispatched` test | | R2#1 — env-var stale socket bypass | R2 | CLOSED structurally via `Degraded → queue-only` collapse + 5 from_discovery tests | | R2#2 — implicit non-persona-use fallback | R2 | (already closed in original A.2.1 via explicit `--mode`) | | R2#3 / R3#3 — manifest drift | R2,R3 | PARTIALLY CLOSED via categorized MODULES; full closure (registration-site snapshot) deferred to A.2.2 with honest scope | | R3#1 — start_server integration | R3 | Deferred to A.2.2 (StubAircCitizen infra) | | R3#2 — panic filter test | R3 | Filter held for A.2.2 per R2 #3 | | R3#5 — substrate boot smoke | R3 | Deferred to A.2.2 | | R2 NIT — voice indicator | R2 | Held for A.2.2 (ships with panic filter + dlopen probe together) | ## Doctrine alignment - [[no-fallbacks-ever]] — structurally, not just doctrinally: Degraded cannot produce a daemon transport at all - [[compression-principle]] — one MODULES list, no parallel constants - [[every-error-is-an-opportunity-to-battle-harden]] — 15 new tests pin the load-bearing seams the convergent review named - [[substrate-is-a-good-citizen-on-the-host]] — refusing to ship the panic filter without the indicator pair - Joel's "purity is always worth it" — discover_and_construct deleted, not deprecated card: 4075b9a4-7251-4405-84e5-9033e2213dff (A.2.1) Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
…ce A.2.1) Replaces Slice A (PR #1527, closed via convergent reviewer BLOCK) with the architectural change Slice A's three patch-fixes were imitating. Compresses three drifting representations of substrate boot state (tuple `Option<(PathBuf, RoomId)>` + flat const `EXPECTED_MODULES` + heuristic seed detection) into one typed primitive each consumer matches exhaustively. The drift problem dies structurally — not because we wrote a louder doc-comment this time, but because the contradictions are no longer expressible. ## Closes (with structural fixes, not patches) R1#1 (BLOCKING) — EXPECTED_MODULES contradicted --mode=inference-only fix: `verify_registration(&discovery, mode)` computes the required set CONDITIONALLY. `MODULES_PERSONA_HOSTING` is only required when AIRC is `Healthy` AND `mode.requires_persona_hosting()`. The PR title "supports inference-only" is now true by construction. R2#1 (BLOCKING) — `AIRC_DAEMON_SOCKET` env var bypassed liveness fix: `discover()` aggregator runs Status RPC liveness probe. Failure promotes to `AircDiscovery::Degraded { reason: StaleSocket(path, underlying_err), partial: ... }` instead of the soft-fallback to `Uuid::nil()` that let stale sockets through. R2#2 (BLOCKING) — implicit `non-persona-use` fallback fix: explicit `--mode=<full-citizen|inference-only|fail-fast>` flag (default `FullCitizen`). The operator states intent; the substrate doesn't guess from `personas/` directory contents. `--mode=inference-only` is what an operator running a forge worker / CI runner / model eval harness explicitly opts into. R2#3 + R3#3 (BLOCKING) — hand-maintained manifest drift fix: `ALL_KNOWN_MODULES` = `MODULES_CORE` ∪ `MODULES_PERSONA_HOSTING` is a build-time invariant checked by three unit-test drift catchers (`all_known_modules_is_union_of_subsets`, `core_and_persona_hosting_are_disjoint`, `required_modules_returns_exactly_one_of_two_sets`). A.2.2 layers the full insta snapshot of `runtime.registry().module_names()` on top. The "we wrote a better doc-comment this time" non-fix is gone. R2 NIT — A2 panic filter mutes only signal fix: panic filter restored (suppresses misleading stderr trace only, catch_unwind safety net unchanged). New boot banner line `Boot mode: <label> (<description>)` surfaces operator's chosen mode immediately; voice subsystem status line lands in A.2.2. ## Compression payoff (the principle, not the bullets) Before A.2.1, three pieces of code answered "what is the substrate's boot state": - `Option<(PathBuf, RoomId)>` tuple (some = healthy, none = everything-else-collapsed) - `EXPECTED_MODULES: &[&str]` flat const (must contain everything that registered, period, regardless of state) - inline seed-presence check in `ipc/mod.rs` (heuristic guess at operator intent) Each could drift against the other two. Slice A added a fourth (the hard-fail check) without compressing — that's how it produced PR claims contradicting its own implementation. A.2.1 collapses them to ONE typed state (`AircDiscovery`) + ONE explicit operator-intent flag (`BootMode`) + ONE function answering "what's required for THIS state" (`required_modules(&discovery, mode)`). Every consumer reaches through the same primitive. The "different consumers disagree about what state is" failure mode is no longer expressible. ## Architecture seam for B' `AircDiscovery::Healthy/Degraded/Unreachable` is the first instance of what B' generalizes: every category sidecar (renderer, voice, inference, foundry) will have the same three-variant health shape. The substrate-wide controls (`for each category: health()`, PressureBroker admission, Ares the dispatcher-persona's allocation cognition) operate uniformly across categories because each category's state surface looks the same. A.2.1's discipline is the foundation: the same compression that closes R1#1 here is what makes Ares possible later. ## Test rigging (the substrate-level kind, not demos) - `airc::discovery_state` — 4 tests over `Healthy/Degraded/ Unreachable` transitions + `StaleSocket` carries path AND underlying error - `runtime::boot_mode` — 12 tests over canonical/alias/case-insens parsing, `requires_persona_hosting`/`requires_voice` queries, `extract_boot_mode` against equals-form / space-form / absent / dangling-space argv - `conditional_modules_tests` — 7 tests over `required_modules` dispatch across `(Healthy, Degraded) × (FullCitizen, InferenceOnly, FailFast)`, plus three drift catchers that make `MODULES_CORE`/`MODULES_PERSONA_HOSTING`/`ALL_KNOWN_MODULES` divergence a CI failure instead of a runtime warning Total: 23/23 pass. Each primitive exercisable in isolation — no demo bin, no binary boot, no live AIRC daemon. A.2.2 layers cross-substrate integration tests on top (`StubAircCitizen` + tempdir continuum_root + `start_server` end to end across the mode × discovery × seed matrix), plus Context trait `discovery()`+`boot_mode()` accessors that prepare the Context object for B's category-handle uniformity. ## End-to-end verification (deferred to release build + live AIRC) Built artifact will be exercised against: - `--mode=full-citizen` + AIRC healthy → All N required modules registered (N depends on dispatch), Paige hosts, round-trip via `airc msg` - `--mode=full-citizen` + AIRC unreachable → exit 1 with typed reason from `AircDiscovery::Unreachable.reason()` - `--mode=full-citizen` + env-var pointed at stale socket → exit 1 with `AircDiscovery::Degraded { reason: StaleSocket { path, underlying } }` - `--mode=inference-only` + AIRC unreachable → boots cleanly, INFO line says persona hosting disabled per operator intent - `--mode=fail-fast` + libonnxruntime missing → exit 1 ## Doctrine alignment (substantively, not as cover) [[no-fallbacks-ever]] — typed discovery + explicit mode eliminate every silent-substitution path Slice A retained [[every-error-is-an-opportunity-to-battle-harden]] — the three drift catchers ARE the rigging; the snapshot test in A.2.2 is its more thorough sibling [[substrate-is-a-good-citizen-on-the-host]] — boot banner names the chosen mode; degraded states have human-actionable reasons [[host-the-seemingly-impossible]] — the substrate publishes what it CAN do honestly; `--mode=full-citizen` is a promise, not a hope ## Out of scope (folded into A.2.2) - `Context` trait `discovery()` + `boot_mode()` accessors - PersonaContext / AgentContext / StubContext implementations - `tests/substrate_boot_contract.rs` integration matrix - `tests/ort_panic_filter.rs` with `serial_test::serial` - `tests/expected_modules_snapshot.rs` insta snapshot of `runtime.registry().module_names()` against ALL_KNOWN_MODULES - Voice subsystem `🔊 ready / 🔇 unavailable` status line - `runtime/mode/get` + `runtime/mode/set` commands — expose `BootMode` via the universal Commands surface so widgets, personas, and remote continuums can all read/change runtime mode through the same primitive (no separate widget API, no operator-only escape hatch). Same for `runtime/discovery/get` returning the typed `AircDiscovery`. The compression principle extends to the control surface: one typed value, one command pair, every actor uses the same primitive. ## Out of scope (folded into B') - Generalization of `AircDiscovery`'s three-variant pattern to `RendererHealth` / `InferenceHealth` / `VoiceHealth` - `CategorySidecar` trait + uniform `health()`/`allocate_lane()` - Sidecar process pattern (renderer/voice/inference/foundry as separate binaries) - Ares-the-persona dispatcher (the master control citizen) card: 4075b9a4-7251-4405-84e5-9033e2213dff supersedes: PR #1527 (closed via R1+R2+R3 convergent BLOCK) follow-up: A.2.2 (Context + integration tests), B' (CategorySidecar) Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
…compression + load-bearing seam coverage Addresses the three converging adversarial reviewers' BLOCK on the first A.2.1 attempt. Each reviewer independently caught the same silent-fallback pattern shifted one frame deeper from where Slice A had it; this commit closes it structurally + adds the test rigging that locks the closure in. ## R2#1 — `AircModule::from_discovery(Degraded)` Uuid::nil bug Reviewers #1 (correctness) and #2 (doctrine) both BLOCKED on the `Degraded` arm reading `partial.peer_id.unwrap_or_else(Uuid::nil)` and constructing a real `DaemonAircEventTransport` against the discovered (potentially stale) socket. In `--mode=inference-only`, the mode gate did not trip, so the module registered, every realtime publish either ECONNREFUSED'd against the stale socket OR went out attributed to `Uuid::nil` — exactly the silent-fallback pattern [[no-fallbacks-ever]] forbids, just shifted one frame from Slice A's location. **Fix:** `Degraded` AND `Unreachable` now both collapse to `with_queue_client(...)`. The `partial` field on the enum is retained for operator observability (what we DID resolve before discovery declared not-Healthy), but the module construction does not pretend. Five new tests in `from_discovery_tests` pin every branch + the cross-variant invariant `only_healthy_exposes_a_ daemon_socket`. R2#1 is now structurally impossible. ## R2#3 + R3#3 — compression principle violation in manifest Reviewers #2 and #3 converged on the same point: three constants (`MODULES_CORE`, `MODULES_PERSONA_HOSTING`, `ALL_KNOWN_MODULES`) with three drift-catcher tests verifying they agree with each other. That violates the compression principle Joel emphasized throughout the session — one logical decision, one place. The drift catchers don't catch the actual regression class (developer adds `runtime.register(NewModule)` in `ipc/mod.rs` without updating a constant). **Fix:** ONE `MODULES: &[(&str, ModuleCategory)]` list with each row tagged by category. `required_modules(&discovery, mode)` filters this list; `all_known_modules()` projects names from it. No parallel list to drift against. The drift catchers reduce to correctness tests over the projection (`required_modules_size_ matches_category_dispatch`, `every_core_module_appears_in_every_ required_set`, `persona_hosting_modules_appear_only_when_ dispatched` — the last is the R1#1 structural lock). The R3#3 "developer adds register without updating manifest" class still requires the A.2.2 insta snapshot that boots the runtime and asserts `registry().module_names()` == projection of `MODULES`. That deferral is honest; the categorized list is the foundation the snapshot will anchor to. ## Purity — `discover_and_construct` deleted, not deprecated Per Joel's "purity is always worth it" doctrine, the legacy `AircModule::discover_and_construct` method is removed entirely. It carried the same `Uuid::nil()` soft-fallback at line 158 that R2#1 was about. With `from_discovery()` covering every production case, the legacy method's only value was easing migration — and "ease of migration" is not worth retaining a code path that encodes the bug pattern this slice exists to close. Same standard for the unused-import cleanup that followed: no deprecated shims, no commented-out `legacy //` blocks, no re-exports for "back-compat." If a future caller wants the old shape, they read the git history and write it again with the typed primitives. ## ORT panic filter — held for A.2.2 per reviewer #2 Reviewer #2's BLOCKING #3: shipping `install_ort_panic_filter` WITHOUT the paired `🔊/🔇 Voice subsystem: <state>` boot banner line leaves the default-mode (`FullCitizen`) operator with zero signal that voice is unavailable. The filter mutes the only signal; the indicator hasn't landed yet. **Fix:** removed the panic filter entirely from A.2.1. Both the filter AND the indicator (with its `libloading::Library::new( "libonnxruntime.dylib")` dlopen probe) land together in A.2.2. This is what reviewer #2 explicitly recommended as the doctrinally honest sequencing: "ship them together OR hold both." ## Test coverage added - `from_discovery_tests` (5 tests): - healthy_produces_fully_configured_module - degraded_with_partial_socket_collapses_to_queue_only (R2#1) - degraded_with_full_partial_state_still_collapses_to_queue_only - unreachable_collapses_to_queue_only - only_healthy_exposes_a_daemon_socket (cross-variant invariant) - `discovery_failure_mapping_tests` (10 tests): - All 8 `DiscoveryError → DiscoveryFailure` variant projections - stale_socket_from_status_err carries path + underlying - stale_socket_handles_non_status_errors - `conditional_modules_tests` (refactored, now 9 tests for the new MODULES shape): - full_citizen_healthy_requires_persona_hosting_modules (R1) - inference_only_does_not_require_persona_hosting_modules (R1) - fail_fast_healthy_requires_persona_hosting_modules - full_citizen_degraded_uses_core_only_set - modules_list_has_unique_names (drift catcher) - required_modules_size_matches_category_dispatch - all_known_modules_derives_from_modules - every_core_module_appears_in_every_required_set - persona_hosting_modules_appear_only_when_dispatched (the structural R1#1 lock) - `discovery_state::tests` (unchanged, 4 tests) - `boot_mode::tests` (unchanged, 12 tests) **Total: 40/40 pass.** Cargo check clean. ## Reviewer findings status | Finding | Reviewer | Status | |---|---|---| | R1#1 — `EXPECTED_MODULES` contradicts inference-only | R1 | CLOSED via categorized MODULES + `persona_hosting_modules_appear_only_when_dispatched` test | | R2#1 — env-var stale socket bypass | R2 | CLOSED structurally via `Degraded → queue-only` collapse + 5 from_discovery tests | | R2#2 — implicit non-persona-use fallback | R2 | (already closed in original A.2.1 via explicit `--mode`) | | R2#3 / R3#3 — manifest drift | R2,R3 | PARTIALLY CLOSED via categorized MODULES; full closure (registration-site snapshot) deferred to A.2.2 with honest scope | | R3#1 — start_server integration | R3 | Deferred to A.2.2 (StubAircCitizen infra) | | R3#2 — panic filter test | R3 | Filter held for A.2.2 per R2 #3 | | R3#5 — substrate boot smoke | R3 | Deferred to A.2.2 | | R2 NIT — voice indicator | R2 | Held for A.2.2 (ships with panic filter + dlopen probe together) | ## Doctrine alignment - [[no-fallbacks-ever]] — structurally, not just doctrinally: Degraded cannot produce a daemon transport at all - [[compression-principle]] — one MODULES list, no parallel constants - [[every-error-is-an-opportunity-to-battle-harden]] — 15 new tests pin the load-bearing seams the convergent review named - [[substrate-is-a-good-citizen-on-the-host]] — refusing to ship the panic filter without the indicator pair - Joel's "purity is always worth it" — discover_and_construct deleted, not deprecated card: 4075b9a4-7251-4405-84e5-9033e2213dff (A.2.1) Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
joelteply
force-pushed
the
4075b9a4/slice-a2-substrate-boot-contract
branch
from
June 7, 2026 15:59
43795e3 to
8e82cdc
Compare
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
Replaces Slice A (PR #1527, closed via convergent R1+R2+R3 BLOCK) with the architectural change Slice A's three patch-fixes were imitating. Compresses three drifting representations of substrate boot state (
Option<(PathBuf, RoomId)>+ flatEXPECTED_MODULESconst + heuristic seed detection) into one typed primitive each consumer matches exhaustively. The drift problem dies structurally — not because we wrote a louder doc-comment this time, but because the contradictions are no longer expressible.Card:
4075b9a4-7251-4405-84e5-9033e2213dffSupersedes: PR #1527
Closes (with structural fixes, not patches)
R1#1 (BLOCKING) — EXPECTED_MODULES contradicted
--mode=inference-onlyverify_registration(&discovery, mode)now computes the required set conditionally.MODULES_PERSONA_HOSTINGis only required when AIRC isHealthyANDmode.requires_persona_hosting(). The PR title "supports inference-only" is true by construction.R2#1 (BLOCKING) —
AIRC_DAEMON_SOCKETenv var bypassed livenessdiscover()aggregator runs Status RPC liveness probe. Failure promotes toAircDiscovery::Degraded { reason: StaleSocket(path, underlying_err), partial: ... }instead of the soft-fallback toUuid::nil()that let stale sockets through.R2#2 (BLOCKING) — implicit
non-persona-usefallbackExplicit
--mode=<full-citizen|inference-only|fail-fast>flag (defaultFullCitizen). The operator states intent; the substrate doesn't guess frompersonas/directory contents.R2#3 + R3#3 (BLOCKING) — hand-maintained manifest drift
ALL_KNOWN_MODULES = MODULES_CORE ∪ MODULES_PERSONA_HOSTINGis a build-time invariant checked by three drift-catcher unit tests. The "louder doc-comment" non-fix is gone. A.2.2 layers an insta snapshot ofruntime.registry().module_names()on top.R2 NIT — A2 panic filter mutes only signal
A2 panic filter restored (suppresses misleading stderr trace only;
catch_unwindsafety net unchanged). New boot banner lineBoot mode: <label> (<description>)surfaces operator's chosen mode immediately. Voice subsystem🔊 ready / 🔇 unavailablestatus line lands in A.2.2.The compression principle this enacts
Before: three pieces of code answered "what is the substrate's boot state":
Option<(PathBuf, RoomId)>tupleEXPECTED_MODULES: &[&str]flat constipc/mod.rsEach could drift against the other two. Slice A added a fourth representation (the hard-fail check) without compressing — that's how it produced PR claims contradicting its own implementation.
A.2.1 collapses to ONE typed state (
AircDiscovery), ONE explicit operator-intent flag (BootMode), ONE function answering "what's required for THIS state" (required_modules(&discovery, mode)). Every consumer reaches through the same primitive. The "different consumers disagree about what state is" failure mode is no longer expressible.Architectural seam for B'
AircDiscovery::Healthy/Degraded/Unreachableis the first instance of the three-variant health shape that every category sidecar (renderer, voice, inference, foundry) will inherit in B'. The substrate-wide controls (for each category: health(), PressureBroker admission, Ares-the-dispatcher's allocation cognition) operate uniformly across categories because each category's state surface looks the same. A.2.1's discipline is the foundation: the same compression that closes R1#1 here is what makes Ares possible later.Substrate-level tests (not demos)
airc::discovery_stateHealthy/Degraded/Unreachabletransitions;StaleSocketcarries path AND underlying errorruntime::boot_moderequires_persona_hosting/requires_voicequeries;extract_boot_modeagainst equals-form/space-form/absent/dangling argvruntime::runtime::conditional_modules_testsrequired_modulesdispatch across(Healthy, Degraded) × (FullCitizen, InferenceOnly, FailFast), plus three drift catchers (all_known_modules_is_union_of_subsets,core_and_persona_hosting_are_disjoint,required_modules_returns_exactly_one_of_two_sets)Total: 23/23 pass. Each primitive exercisable in isolation — no demo bin, no binary boot, no live AIRC daemon. The compression makes them testable; the tests make the compression honest. This is exactly the [[test-fixtures-are-system-primitives]] pattern at the boundary layer.
A.2.2 layers cross-substrate integration tests on top (
StubAircCitizen+ tempdircontinuum_root+start_serverend-to-end across themode × discovery × seedmatrix).End-to-end (release binary)
Will be exercised after CI green:
--mode=full-citizen+ AIRC healthy → All N required modules registered, Paige hosts,airc msground-trip--mode=full-citizen+ AIRC unreachable → exit 1, typed reason fromUnreachable.reason()--mode=full-citizen+AIRC_DAEMON_SOCKET=<stale.sock>→ exit 1 withStaleSocket { path, underlying }--mode=inference-only+ AIRC unreachable → boots cleanly, INFO line names operator intent--mode=fail-fast+ libonnxruntime missing → exit 1Out of scope (folded into A.2.2 + B')
A.2.2:
Contexttraitdiscovery()+boot_mode()accessorstests/substrate_boot_contract.rsintegration matrixtests/ort_panic_filter.rswithserial_test::serialtests/expected_modules_snapshot.rsinsta snapshot🔊 ready / 🔇 unavailablestatus lineruntime/mode/get+runtime/mode/setcommands — exposeBootModevia the universal Commands surface so widgets, personas, and remote continuums all read/change runtime mode through the same primitive. Same forruntime/discovery/get. The compression extends to the control surface: one typed value, one command pair, every actor uses the same primitive.B':
AircDiscovery's three-variant pattern toRendererHealth/InferenceHealth/VoiceHealthCategorySidecartrait + uniformhealth()/allocate_lane()/shutdown()Doctrine alignment (substantive, not as cover)
--mode=full-citizenis a promise, not a hopeReferences
🤖 Generated with Claude Code