Skip to content

fix(boot): typed AircDiscovery + BootMode + conditional manifest (Slice A.2.1) - #1528

Merged
joelteply merged 2 commits into
canaryfrom
4075b9a4/slice-a2-substrate-boot-contract
Jun 7, 2026
Merged

fix(boot): typed AircDiscovery + BootMode + conditional manifest (Slice A.2.1)#1528
joelteply merged 2 commits into
canaryfrom
4075b9a4/slice-a2-substrate-boot-contract

Conversation

@joelteply

Copy link
Copy Markdown
Contributor

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)> + flat EXPECTED_MODULES const + 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-9033e2213dff
Supersedes: PR #1527

Closes (with structural fixes, not patches)

R1#1 (BLOCKING) — EXPECTED_MODULES contradicted --mode=inference-only

verify_registration(&discovery, mode) now 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 true by construction.

R2#1 (BLOCKING) — AIRC_DAEMON_SOCKET env var bypassed liveness

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

Explicit --mode=<full-citizen|inference-only|fail-fast> flag (default FullCitizen). The operator states intent; the substrate doesn't guess from personas/ directory contents.

R2#3 + R3#3 (BLOCKING) — hand-maintained manifest drift

ALL_KNOWN_MODULES = MODULES_CORE ∪ MODULES_PERSONA_HOSTING is 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 of runtime.registry().module_names() on top.

R2 NIT — A2 panic filter mutes only signal

A2 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 🔊 ready / 🔇 unavailable status 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)> tuple
  • EXPECTED_MODULES: &[&str] flat const
  • inline seed-presence heuristic in ipc/mod.rs

Each 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/Unreachable is 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)

Module Tests What it pins
airc::discovery_state 4 Healthy/Degraded/Unreachable transitions; StaleSocket carries path AND underlying error
runtime::boot_mode 12 Canonical/alias/case-insensitive parsing; requires_persona_hosting/requires_voice queries; extract_boot_mode against equals-form/space-form/absent/dangling argv
runtime::runtime::conditional_modules_tests 7 required_modules dispatch 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 + tempdir continuum_root + start_server end-to-end across the mode × discovery × seed matrix).

End-to-end (release binary)

Will be exercised after CI green:

  • --mode=full-citizen + AIRC healthy → All N required modules registered, Paige hosts, airc msg round-trip
  • --mode=full-citizen + AIRC unreachable → exit 1, typed reason from Unreachable.reason()
  • --mode=full-citizen + AIRC_DAEMON_SOCKET=<stale.sock> → exit 1 with StaleSocket { path, underlying }
  • --mode=inference-only + AIRC unreachable → boots cleanly, INFO line names operator intent
  • --mode=fail-fast + libonnxruntime missing → exit 1

Out of scope (folded into A.2.2 + B')

A.2.2:

  • Context trait discovery() + boot_mode() accessors
  • PersonaContext / AgentContext / StubContext impl
  • tests/substrate_boot_contract.rs integration matrix
  • tests/ort_panic_filter.rs with serial_test::serial
  • tests/expected_modules_snapshot.rs insta snapshot
  • 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 all read/change runtime mode through the same primitive. Same for runtime/discovery/get. The compression extends to the control surface: one typed value, one command pair, every actor uses the same primitive.

B':

  • Generalization of AircDiscovery's three-variant pattern to RendererHealth / InferenceHealth / VoiceHealth
  • CategorySidecar trait + uniform health() / allocate_lane() / shutdown()
  • Sidecar process pattern (renderer/voice/inference/foundry as separate binaries)
  • Ares-the-persona dispatcher (the master control citizen)

Doctrine alignment (substantive, 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
  • [[substrate-is-a-good-citizen-on-the-host]] — boot banner names the mode; degraded states have human-actionable typed reasons
  • [[host-the-seemingly-impossible]] — substrate publishes what it CAN do honestly; --mode=full-citizen is a promise, not a hope

References

🤖 Generated with Claude Code

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>
joelteply and others added 2 commits June 7, 2026 10:52
…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
joelteply force-pushed the 4075b9a4/slice-a2-substrate-boot-contract branch from 43795e3 to 8e82cdc Compare June 7, 2026 15:59
@joelteply
joelteply merged commit 04a4b08 into canary Jun 7, 2026
3 checks passed
@joelteply
joelteply deleted the 4075b9a4/slice-a2-substrate-boot-contract branch June 7, 2026 16:17
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant