# Progress — Era V: Unification (Waves 24-90+, ~Apr 2026 - May 2026)

Archived from PROGRESS.md on 2026-05-08. Spans the run from the AD-507 / AD-509
crew-development bootstrap through the unified knowledge-graph stack (AD-686 /
687 / 688 / 689 / 690 / 691 / 692 / 695), the holodeck/team-simulation layer
(AD-486 / 510 / 512 / 539b / 597), the records-knowledge-browser surface
(AD-562 / AD-520), the AD-641 brain-enhancement umbrella (641a-g + 641g-1),
the commercial-overlay extension seam (AD-697 / 698), and the May 7 roadmap
audit cleanup that closed 37 issues across waves 112-128.

Authoritative wave roster lives in `prompts/wave-plan.yaml`; this file is
historical record only.

----

AD-723 v1 CLOSED (Wave 144). Sensorium dispatch unification — producer side. Closes GH issue #581. Converts `CognitiveAgent.SENSORIUM_REGISTRY` from `ClassVar[dict[str, tuple[SensoriumLayer, str]]]` inventory to `ClassVar[dict[str, SensoriumEntry]]` dispatch table. New `SensoriumPath` `StrEnum` declares 5 prompt-assembly paths (CHAIN_BASELINE / CHAIN_EXTENSIONS / CHAIN_SITUATION / DM_ONESHOT / WR_ONESHOT). New `SensoriumEntry` frozen dataclass carries `layer` / `description` / `paths` tuple / `priority` int / `output_key` str-or-None. Two dispatcher methods on `CognitiveAgent` (`_dispatch_sensorium_sync` for chain paths — refuses async-registered methods as defense in depth — and `_dispatch_sensorium_async` for DM/WR paths, awaits coroutines, calls sync methods directly). Three chain shims (`_build_cognitive_baseline` / `_build_cognitive_extensions` / `_build_situation_awareness`) refactored to thin dispatcher calls; `_build_cognitive_state` uses single-shared-dict variant so AD-646 None-removal in extensions correctly pops baseline-set keys (e.g. `_no_episodic_memories` when proactive context_parts carries memories). ~22 inline blocks across the three chain methods extracted into named `_sensorium_*` helper methods + 4 wrapper methods for legacy helpers whose signatures predate the dispatcher contract (`_sensorium_temporal_context` / `_sensorium_working_memory` / `_sensorium_comm_proficiency` / `_sensorium_self_recognition`). AD-722 `_build_avatar_self_observation` and AD-722a `_build_intent_self_tag_instruction` register at `paths=(CHAIN_BASELINE, DM_ONESHOT)` and explicitly NOT `WR_ONESHOT` per AD-722 addendum (h) — the motivating dual-wire cases. Future sensorium ADs edit the registry once (one `paths` tuple) and chain-side dispatch picks them up automatically. **Scope reframe per Wave-10 entanglement rule**: the ~415 inline lines across the DM/WR branches of `_build_user_message` carry too much byte-equality risk to land in one commit alongside the registry rewrite — DM/WR consumer-side migration deferred to AD-723a-1 (#617) as a forced-followup. Producer-side already delivers the dual-wire-tax elimination. **Acceptance gate**: golden-text snapshot fixture at `tests/fixtures/sensorium_snapshots/chain_baseline.txt` captured pre-refactor under default config (avatar `inject_into_agent_context=False`, `divergence_detection=False`); post-refactor dispatcher output is byte-identical (`test_chain_baseline_byte_equality`). **Dispatcher convention**: methods return `str` (sets `output_key`), `dict[str, str]` (multi-key contribution), `None` (AD-646 explicit removal — pops the key), or `""` (no contribution — skipped without pop). Internal audit caught that baseline override methods (`_sensorium_source_attribution_rich`, `_sensorium_ext_*`) initially returned `None` for "no opinion" which the dispatcher treated as removal, popping baseline-set keys; fixed by switching baseline-side "no contribution" to `""` while keeping `None`-removal only on `_sensorium_ext_no_memories_flag_override`. 25 focused tests pass at `tests/test_ad723_sensorium_dispatch.py` (over the ~14 target): types (3 — path values are strings, frozen dataclass, default fields); registry shape (2 — all entries are `SensoriumEntry`, all method names resolve on `CognitiveAgent` — phantom-API guard); path coherence (4 — avatar not in WR, intent-self-tag not in WR, `_build_dm_self_monitoring` is WR-only not DM, situation entries chain-only); dispatcher core (9 — sync returns dict, empty-paths skip, sync rejects async with explicit RuntimeError, async awaits coroutines, None signals removal, "" skipped without pop, dict multi-key with embedded None pops, exception is Tier-2 degrade, priority ordering — extensions override baseline by key); AD-646 None-removal (2 — extensions pop baseline `_no_episodic_memories` when memories present; set the flag when framing-but-no-memories); chain-baseline snapshot byte-equality (2 — fixture match + shim/dispatcher equivalence); chain extraction coverage (2 — extracted methods invocable with full observation; situation dispatch returns only situation keys); AD-722/AD-722a default-OFF guard (1 — neither avatar nor intent-self-tag key in baseline dict at default config). All 109 existing tests across `test_ad646_cognitive_baseline.py` / `test_ad646b_chain_parity.py` / `test_ad722_avatar_telemetry.py` / `test_ad722a_divergence_detector.py` / `test_ad635f_clinical_proactive_context.py` / `test_ad648_post_capability_profiles.py` pass without modification — the sync signatures of all four chain shims are preserved exactly. 2 tests in `test_ad666_sensorium.py` (`test_registry_entries_are_tuples_of_layer_and_description`, `test_registry_has_all_three_layers`) updated in place to assert the new `SensoriumEntry` dataclass shape (the registry shape change is the AD's whole point). **Architect calls (DLogs)**: (1) `MagicMock(spec=CognitiveAgent)` test pattern in AD-635f required `_build_situation_awareness` to inline its dispatch loop and resolve methods via the class (`getattr(CognitiveAgent, method_name)`) instead of `self`, so Mock-bound calls don't intercept the dispatcher path; (2) `_ext_context_parts` helper for reading `observation["_context_parts"]` was originally a `@staticmethod` but Mock-spec intercepts class attribute access too — inlined the dict read at every call site (15 callers); (3) baseline-side methods return `""` (no contribution) instead of `None`; only `_sensorium_ext_no_memories_flag_override` keeps `None`-as-removal semantic, matching the AD-646 contract documented in §6.4 of the prompt. Pre-commit deletion sanity: 428 deletions in `cognitive_agent.py` are the inline-block extractions (~150 from baseline + ~140 from extensions + ~100 from situation + 16 from old registry literal = ~406; remaining ~22 are reshuffled imports / docstrings). Anticipated by the prompt; legitimate refactor. Full gate: 13196 passed, 20 skipped, 4 pre-existing flakes (test_callsign_routing × 3, test_ad719_chat_fanout × 1) verified against `2e7eb52` via `git stash`. No hard-stops triggered: no architectural change required, no phantom in implementation, no scope creep beyond the producer-side reframe.

----

Wave 78 close: AD-569 Behavioral Metrics Extensions (GH issue #108). Reframed 7 sub-ADs to 1-build + 5 verify-only + 1 deferral with forcing function. AD-569a (Frame Diversity), AD-569b (Synthesis Detection), AD-569c (Cross-Department Trigger), AD-569e (Anchor-Grounded Emergence) shipped via the AD-569 main entry — `BehavioralSnapshot` carries all five metric field groups and `BehavioralMetricsEngine` updates them on Dream Step 13. AD-569d shipped via AD-583f/g satisfaction — `ObservableStateVerifier` populates `convergence_correctness_rate` (decisions-era-4-evolution.md:3219). AD-569g v1 builds in this wave: HXI-only `BehavioralMetricsPanel` floating dashboard surfaces the five behavioral metrics as 2-column tiles with composite `behavioral_quality_score` header, pure-SVG sparklines (raw 0-100 viewBox math, no `getBoundingClientRect`), department chips on the Frame Diversity tile, em-dash for null `convergence_correctness_rate`, ESC-to-close, and footer note "Facet breakdown lands with AD-569f". Backend zero-touch — only consumes `/api/behavioral-metrics` and `/api/behavioral-metrics/history` (both shipped under AD-569 at `routers/system.py:299-326`). Store key `behavioralMetricsOpen` (full name, no abbreviation), inline `METRICS` toggle at `top:12 left:270` to the right of `NOTEBOOKS`. AD-569f deferred with explicit forcing function — research-grade psychometric layer (G-study/D-study, ICC, r_wg, MTMM, variance decomposition) with no present-day consumer; AD-583f/g satisfied 569d without it; reframes when first qualification probe requires variance decomposition. 12 focused vitest tests pass at `ui/src/__tests__/BehavioralMetricsPanel.test.tsx` (over 9 floor by 3): renders nothing when closed; opens and fetches both endpoints; renders all five metric tiles; composite quality score in header; empty state for `not_available`; empty state for `no_data`; em-dash for null convergence_correctness; close button closes panel; error state on fetch reject; sparkline polyline with 3+ history points; dashed baseline when history empty; refresh button re-invokes fetches. Vitest delta +12 (279 → 291 passing); single pre-existing WardRoomDmSync failure unchanged. Pytest collection 11498 unchanged (Δ = 0; backend verify-only). HXI Design Principles compliance: zero emoji (Unicode glyphs `×` close, `↻` refresh only); SVG strokes `1.5` rounded; amber/teal/violet/green/gold trust spectrum across the five tiles. No commercial language in panel copy, prompt, dispatch, or roadmap. Files modified: `ui/src/store/types.ts` (+`BehavioralSnapshot`), `ui/src/store/useStore.ts` (+state shape, +actions interface, +initial state, +three actions), `ui/src/App.tsx` (+import, +`BehavioralMetricsToggle`, +mount points). Files added: `ui/src/components/BehavioralMetricsPanel.tsx`, `ui/src/__tests__/BehavioralMetricsPanel.test.tsx`. No `src/probos/` modifications. No new pytest files.

AD-686b v1 CLOSED. Oracle Owns SemanticKnowledgeLayer Write-Path (GH issue #414, Wave 50). Closes the AD-686 v1 (Wave 36) read/write asymmetry by completing the second half of Oracle's Tier-5 ownership. New `OracleService.write_semantic(kind, /, **fields) -> bool` is a keyword-dispatcher that routes one record to the matching `SemanticKnowledgeLayer.index_<kind>` method (five supported kinds: `agent`/`skill`/`workflow`/`qa_report`/`event`). Tier-2 log-and-degrade: returns `False` (and logs at debug) when the semantic layer is not attached; returns `False` (and logs at warning) when the kind is unknown; wraps the delegation in try/except so any layer-side failure logs at warning and returns `False` without propagating. Returns `True` only when the underlying `await layer.index_<kind>(**fields)` completes successfully. Five external write-path call sites now route through the Oracle: `runtime.py:2508` (designed-agent `index_agent`), `runtime.py:3309` (skill `index_skill`), `runtime.py:3358` (QA report `index_qa_report`), `self_mod_manager.py:142` (patched-agent `index_agent`), `routers/chat.py:419` (chat-driven design `index_agent`). Each migrated site drops its `if self._semantic_layer:` gate and inline try/except — Oracle's dispatcher provides those guarantees centrally. `routers/chat.py` and `self_mod_manager.py` use the Wave 36 `getattr(rt, "oracle", None) or getattr(rt, "_oracle_service", None)` fallback chain to tolerate stub runtimes in older test rigs. **What this AD does NOT change** (out of scope by design): no deletion of `SemanticKnowledgeLayer` (Oracle delegates to it; internal `reindex_from_store` at `semantic.py:360/376/397/413` still calls `self.index_*` directly); no deletion of `runtime._semantic_layer` attribute (`agents/introspect.py:764` still uses it as a read-path fallback when `runtime.oracle` is missing on a stub; `runtime.py:2973-2974` calls `_semantic_layer.stats()` for the system-status panel — no Oracle stats surface today); no migration of `index_workflow`/`index_event` (no external callers at HEAD; dispatcher ships those kinds for completeness); no new EventType, no new Pydantic config, no new module, no new public attribute on runtime; no write-path classification gating (AD-692 territory and applies to KnowledgeEdge, not the semantic feed); no deferred-write queue, no rate-limit, no audit emission. **Architect calls (DLogs):** (1) `write_semantic(kind, /, **fields)` instead of Captain's `(entries, *, collection)` shape — SemanticKnowledgeLayer has no uniform `add()` method; five typed methods, five distinct kwarg sets; forcing dict-packing would regress type safety; keyword dispatch by kind is the closest faithful mirror. (2) `index_workflow`/`index_event` ship in the dispatcher with no migration site — both surfaces are real (schema includes `workflows` and `events` collections) and only invoked internally by `reindex_from_store` at HEAD; tests #4 + #6 exercise them via Oracle for completeness. (3) `SemanticKnowledgeLayer` cannot be made internal in v1 — two non-test external consumers remain after this AD's 5 migrations: `introspect.py:764` read-path fallback for stub runtimes, and `runtime.py:2974` `_semantic_layer.stats()` for `/system` shell command stats panel; rename deferred to AD-686c once `OracleService.semantic_stats()` ships. (4) `bool` not `int` — `routers/chat.py` consumes the return as a bool flag; single-record dispatcher has no aggregation. (5) Migration sites drop `if self._semantic_layer:` guard + try/except — Oracle handles None + exceptions internally. (6) `self_mod_manager.py` keeps its `_semantic_layer` ctor field — cleanup is AD-686c territory; v1 stays narrow on call sites. 12 focused tests pass at `tests/test_ad686b_oracle_write_semantic.py` (over 10 floor by 2): method shape (async, present, returns bool); `agent` kind delegates with kwargs forwarded byte-for-byte; `skill` kind delegates; `workflow` kind delegates (no migration site); `qa_report` kind delegates; `event` kind delegates (no migration site); `None` layer returns `False` + debug log + does NOT raise; unknown kind returns `False` + warning + does NOT call layer (uses `MagicMock(spec=[])` so auto-attributes don't satisfy getattr lookup); delegation exception (RuntimeError from `layer.index_agent`) caught → `False` + warning, never propagates; late-bind via `attach_semantic_layer` (False before, True after); `SemanticKnowledgeLayer.index_*` write methods unchanged backward-compat lock (asserts presence by name + async-callable shape across all five); static migration lock asserting the 3 migrated source files contain `oracle.write_semantic(` AND zero direct `_semantic_layer.index_*(` writes remain (catches future refactors that silently revert one of the five sites). All 11 existing AD-686 v1 read-path tests at `tests/test_ad686_oracle_semantic_tier.py` continue to pass without modification. Phantom-API pre-check on the prompt body: 15 candidates ALL FPs — 14× `OracleService.write_semantic` intra-prompt-introduction (same FP class as Waves 27-49) + 1× `OracleService.semantic_stats` DLog #3 prose (hypothetical AD-686c surface, not asserted to exist); 0 NEW phantoms. **Builder drift-fix during build**: test #8 (`test_write_semantic_unknown_kind_returns_false`) initially failed with `TypeError: object MagicMock can't be used in 'await' expression` because `MagicMock()` auto-creates attributes — `getattr(layer, "index_nonexistent", None)` returned a MagicMock, not None, then awaiting the MagicMock raised TypeError, which the `except Exception:` caught as a delegation failure (logging "delegation failed" instead of "unknown kind"). Fix: `MagicMock(spec=[])` constrains the mock so it has zero attributes, restoring the intended unknown-kind path. Pure test-fixture defect; no shipping-code change. Lesson: when testing a getattr-by-name dispatcher, the mock layer must use `spec=[]` (or a non-MagicMock class) to make missing attributes genuinely missing. Full gate: 11170 passed, 16 skipped (delta exactly +12 vs Wave 49 baseline 11158 — exact target hit). Pre-commit deletion sanity: max ~16 deletions any single file (line-level inline-try/except blocks replaced with single-line awaits at the 5 migration sites). All other files small additive deltas. Well below 200-line surprise-deletion threshold. No hard-stops triggered: no architectural change required, no phantom in implementation, no scope creep. Closes GH issue #414.

AD-647c v1 CLOSED. Process Chains Bills/Watch Bill Integration (GH issue #405, Wave 49). Bridges process chains to AD-618 Bills + AD-595 Watch Bill: `ProcessChainStep` gains optional `bill_step_id` and `assigned_role` fields (both default `""`), `ProcessChainExecutor` accepts `bill_runtime=None` ctor kwarg + four tier-2 log-and-degrade helpers (`_resolve_bill_instance` resolves active `BillInstance` once at chain start via `runtime.bill_runtime.get_instance(context['bill_instance_id'])` with three guards: bill_runtime present AND context.bill_instance_id present AND lookup succeeds; `_inject_resolved_agent` reads `BillInstance.role_assignments[step.assigned_role].agent_id` and injects as `_resolved_agent_id_<step.name>` into running context; `_record_bill_step_completion` calls `bill_runtime.complete_step(instance.id, step.bill_step_id, result=step_output)` on success; `_record_bill_step_failure` calls `bill_runtime.fail_step(instance.id, step.bill_step_id, error=...)` on handler exception). Every bill_runtime call wrapped in try/except→logger.warning so bill-side errors NEVER propagate up into chain execution — chain success is the source of truth, Bills tracking is a secondary observability surface. New `ProcessChainRegistry.register_bill_chain(bill_definition, chain_definition)` method validates every chain step's non-empty `bill_step_id` against the bill's `BillStep.id` set; mismatch → `ValueError("AD-647c: chain '<name>' references unknown bill step ids in bill '<slug>': <step.name>-><bsid>, ...")` and chain is NOT registered (fail-fast). Empty bill_step_id values are permitted and skip validation. New CONSULT step kind appended to `ProcessChainStepKind` enum (semantic label only — executor already awaits async handlers via `await step.handler(running)`, so human-in-the-loop / cross-agent consultation handlers that internally await asyncio.Event/ward-room-thread/ConsultationWorkspace decisions work today). Suspend/resume across process restart deferred to placeholder AD-647d. New public property `runtime.bill_runtime` mirrors `runtime.billet_registry` shape — closes the Wave 5 conv #1 hole that AD-635 documented retrospectively for `_emergent_detector`; ProcessChainExecutor uses this to record chain-step lifecycle against BillInstance steps when chains are bill-bound. **What this AD does NOT change** (out of scope by design): no NATS coupling (deferred AD-641g, separate large issue #403); no LLM step templates (`prompt_template_id` still reserved/rejected at construction); no parallel/conditional/rollback steps; no CONSULT suspend-and-resume across process restart (deferred AD-647d, no GH issue v1; documented in module docstring + DLog only); no Scout migration to bill-bound (Scout reports are not tied to a Bill); no HXI surface for chain↔bill correlation; no new EventType / journal entries; no `runtime._bill_runtime` deletion (kept for adoption-site compatibility). **Architect calls (DLogs):** (1) Field renamed `bill_step_id` not `bill_id` — `bill_id` is overloaded across the Bills surface (`BillDefinition.bill` is the slug; `BillInstance.bill_id` is the slug too); chain step's field maps to `BillStep.id` (the step id WITHIN a bill). The bill-instance correlation is supplied by the caller via `context['bill_instance_id']` at run-time — orthogonal field, not on the step. (2) CONSULT = enum value only, NO executor change — existing executor at `process_chains.py:131` already runs `await step.handler(running)` which natively supports `async def __call__` handlers that internally await any async resource. True suspend-and-resume across process restart needs checkpointing infrastructure → out of scope. (3) `runtime.bill_runtime` public property added — 1-line property mirroring `runtime.billet_registry` shape (`runtime.py:985`); cleaner than re-creating AD-635's `getattr(rt, '_bill_runtime', None)` Demeter exception. (4) Three-guard defense for bill recording — bill_step_id != "" AND context.bill_instance_id present AND bill_runtime is not None; all three required to fire side-effects. (5) Bill recording is tier-2 log-and-degrade — every `bill_runtime.{get_instance, complete_step, fail_step}` call wrapped in try/except; chain success preserved on bill-side error. (6) `register_bill_chain` fail-fast — mismatched `bill_step_id` raises `ValueError` and chain is NOT registered. (7) Unresolved `assigned_role` — log-and-degrade; handler still runs, just no `_resolved_agent_id_<step>` injected (executor does NOT synthesize a fall-back identity; handler decides). 13 focused tests pass at `tests/test_ad647c_bills_integration.py` (over 10 floor by 3): `bill_step_id`/`assigned_role` field acceptance; `CONSULT` enum value (`.value == "consult"`); executor without `bill_runtime` runs identically to v1 (bill_step_id ignored); executor with `bill_runtime` but no `context.bill_instance_id` skips recording (`get_instance.assert_not_called()`); executor records `complete_step("i1", "bstep_alpha", result={"out": 42})` on success; executor records `fail_step("i1", "bstep_beta", error="RuntimeError: nope")` on handler exception (and `complete_step.assert_not_called()`); tier-2 log-and-degrade preserves chain success when `bill_runtime.complete_step.side_effect = RuntimeError("bill-side bug")` (output dict intact, WARNING captured); `assigned_role="oncall"` injects `_resolved_agent_id_s1="agent-007"` from `BillInstance.role_assignments`; unresolved role logs WARNING ("has no holder") and `_resolved_agent_id_s1` not in context; `register_bill_chain` happy path (chain registered + retrievable); `register_bill_chain` rejects mismatched bill_step_ids with `ValueError("unknown bill step ids")` and chain NOT registered (`get_chain("bad") is None`); empty `bill_step_id` values skip validation. All 8 existing AD-647 v1 tests + 12 existing AD-647b v1 tests continue to pass unchanged — new fields default to `""`, new ctor kwarg defaults to `None`, no call site changes required. Phantom-API pre-check: 6 candidates ALL FPs (1 stdlib `SimpleNamespace` + 3 `ProcessChainRegistry.register_bill_chain` method_phantom + 2 `ProcessChainStep.bill_step_id`/`assigned_role` field_phantom — same intra-prompt-introduction FP class as Waves 27–48). 0 NEW phantoms. Full gate: 11158 passed, 16 skipped (delta +13 vs Wave 48 baseline 11145 — over the +12 acceptance-criteria target by 1). Pre-commit deletion sanity: max ~3 deletions any single file (additive-only changes — enum +1 value, ProcessChainStep +2 fields, ProcessChainExecutor +1 ctor kwarg + 4 helpers + run() rewrite, ProcessChainRegistry +1 method, runtime.py +1 public property, new test file). Well below 200-line surprise-deletion threshold. No hard-stops triggered: no architectural change required, no phantom in implementation, no scope creep. Closes GH issue #405.

AD-647b v1 CLOSED. Process Chain Registry + BF-209 Bypass Removal (GH issue #404, Wave 48). Closes the AD-647 v1 hardcoded-chain gap by introducing a runtime catalog of `ProcessChainDefinition` and generalizing the BF-209 communication-chain bypass into a declarative class-attribute hook on `CognitiveAgent`. Three converging components shipped in one OSS v1: (1) **`ProcessChainRegistry`** — new class in `cognitive/process_chains.py` keyed by `chain_id = definition.name`. Public API: `register_chain(definition)` replaces + WARNs on duplicate (mirrors `ToolRegistry.register` at `tools/registry.py:113`), `get_chain(chain_id) -> ProcessChainDefinition | None` (Demeter-friendly None-on-miss), `list_chains() -> list[str]` sorted, `unregister_chain(chain_id) -> bool` (False on absent, no exception). (2) **`process_chain_id` base-class hook** — new class attribute `process_chain_id: str | None = None` on `CognitiveAgent`. `_should_activate_chain` Gate 0 short-circuits to False when `self.process_chain_id is not None` AND observation is `proactive_think` with `params.duty.duty_id == self.process_chain_id`. This generalizes BF-209 from a per-agent override into a declarative class attribute. (3) **Scout migration** — four `_scout_step_*` methods promoted from bound instance methods (Wave 34 shape — handlers reached `self._reports_dir`/`self._pending_seen_repos`/`self._deliver_discord`/`self.id` via implicit self) to **module-level functions** in `scout.py` that read `_agent` from `ctx["_agent"]` then access per-instance state through the agent reference. `SCOUT_REPORT_CHAIN: ProcessChainDefinition` becomes a static module-level constant immediately above `class ScoutAgent`. `ScoutAgent.process_chain_id = "scout_report"`. The BF-209 `_should_activate_chain` override is REMOVED — Method Resolution Order now resolves `ScoutAgent._should_activate_chain` to `CognitiveAgent._should_activate_chain` (test #10 asserts function-object identity). `act()` chain construction replaced by `runtime.process_chain_registry.get_chain(self.process_chain_id)` with module-level `SCOUT_REPORT_CHAIN` fallback + WARNING when registry is unavailable. `_deliver_discord` stays as a bound instance method (reads `self._runtime`/`self.config`/`self.id` at call time — not a step handler). (4) **Pydantic config + finalize wirer** — new `ProcessChainRegistryConfig(enabled=True)` adjacent to `ConsultationWorkspaceConfig` at `config.py:1904`; wired on `SystemConfig.process_chain_registry` adjacent to `consultation_workspaces` at `:2260`. Default-True is intentional + documented in config docstring — registry construction cost is one empty dict + one `register_chain(SCOUT_REPORT_CHAIN)` call at boot, and Scout's `act()` depends on the registry being present (module-level fallback is a defensive belt). New sync `_wire_process_chain_registry(*, runtime, config) -> bool` wirer in `startup/finalize.py` adjacent to `_wire_consultation_workspaces` constructs the registry, registers `SCOUT_REPORT_CHAIN`, sets `runtime.process_chain_registry` (collision-free greenfield — verified 0 hits at HEAD before edit), invoked from `finalize_startup` immediately before `_wire_consultation_workspaces`. (5) **Test fixture amendment** — `test_scout_act_runs_through_process_chain` (`test_ad647_process_chains.py:152`) replaced `agent._runtime = None` with a `SimpleNamespace(process_chain_registry=ProcessChainRegistry())` populated with `SCOUT_REPORT_CHAIN` — Scout's `act()` now finds the chain via the runtime registry path (the production path) instead of the module-level fallback (which would log a WARNING per test invocation). **What this AD does NOT change** (out of scope by design): no NATS / Bills / Watch Bill integration (AD-647c, #405); no parallel / conditional / rollback steps (v1 stays sequential per AD-647 v1 invariant); no LLM-template step handlers (`prompt_template_id` field on `ProcessChainStep` remains rejected at construction); no registry persistence (in-memory only); no HXI surface (`list_chains()` not exposed via REST or shell); no `_deliver_discord` refactor (stays bound; only the four step methods move to module level); no `BaseAgent` change (`process_chain_id` lives on `CognitiveAgent` — Scout's actual parent; future non-cognitive agents would promote via AD-647d). **Architect call (DLog #1) — module-level handlers**: registry stores definitions, not factories — handlers cannot be per-instance bound. Refactor is mechanical: `self.X` → `agent = ctx["_agent"]` then `agent.X`. This is the correct long-term shape. **Architect call (DLog #2) — hook lives on `_should_activate_chain`, NOT default `act()`**: `CognitiveAgent.act()` already has substantial logic (chain-result aggregation, validation) that we don't want to fork. The cleaner hook is on `_should_activate_chain`, which is exactly what BF-209 overrides. **Architect call (DLog #3) — duplicate-registration semantics**: replace + WARN matches `ToolRegistry.register`; more useful for hot-reload / test isolation than rejection. **Architect call (DLog #4) — registry never raises on miss**: `get_chain(unknown)` returns None, `unregister_chain(unknown)` returns False. Demeter-friendly; caller decides fallback. **Architect call (DLog #5) — `enabled=True` default**: same precedent as `KnowledgeEdgesConfig` / `EdgeBackfillConfig` / `ConsultationWorkspaceConfig`. Disabling means Scout's `act()` falls back to the module-level constant and logs WARNING per invocation — operator-visible degradation rather than silent breakage. **Architect call (DLog #6) — backward-compat invariant**: all 8 existing `tests/test_ad647_process_chains.py` tests continue to pass after the Section 6 amendment. 12 focused tests pass at `tests/test_ad647b_chain_registry.py` (over the 10 floor by 2): registry register+get+list happy path; registry get-unknown returns None; registry unregister returns True when present + clears entry; registry unregister-unknown returns False; registry duplicate registration replaces with WARNING captured via `caplog` + new instance returned by object-identity check; `_should_activate_chain` returns False when `process_chain_id` matches duty (with FakeExecutor.enabled=True); falls through to gates 1+2 when `process_chain_id` is None (proactive_think + executor enabled → True); falls through when `process_chain_id` set but `duty_id` mismatches; `ScoutAgent.process_chain_id == "scout_report"` class attribute assertion; `ScoutAgent._should_activate_chain is CognitiveAgent._should_activate_chain` function-object identity (BF-209 override removed); `_scout_step_enrich_and_filter` reads agent from `ctx["_agent"]` correctly (filter_findings + agent._last_findings updated); finalize wirer constructs registry and registers SCOUT_REPORT_CHAIN against a real `SystemConfig()` + SimpleNamespace runtime, asserts `list_chains() == ["scout_report"]` and the registered chain's first step is `parse_and_mark_seen`. Phantom-API pre-check on this prompt and on the touched source files: clean (no NEW phantoms; collision-free greenfield for `runtime.process_chain_registry` and `process_chain_id` class attribute verified pre-build). Full gate: 11144 passed, 16 skipped (delta +12 vs Wave 47 baseline 11132 — exact target hit). 1 known xdist flake (`test_knowledge_store::test_auto_commit_after_debounce` — passes serial in 7.41s; same Waves 8/14/15/16/19/22/27/30/31/32/33/37/41/42 environmental git-debounce pattern; unrelated to this AD). Pre-commit deletion sanity check: max ~110 deletions in `scout.py` (4 bound `_scout_step_*` methods removed — content moved to module level above the class). All other files small line-level deltas: `cognitive_agent.py` +18/-3 (class attribute + Gate 0 logic), `process_chains.py` +35/-0 (registry append-only), `config.py` +14/-0 (config model + field), `finalize.py` +25/-0 (wirer + invoke), `test_ad647_process_chains.py` +7/-1 (fixture amendment), new `test_ad647b_chain_registry.py` +185/-0. Well below 200-line surprise-deletion threshold per file in tracker context. No hard-stops triggered: no architectural change required, no phantom in implementation, no scope creep. Closes GH issue #404.

AD-685d v1 CLOSED. Phantom-API Pre-Check Dataclass/Pydantic Field-Name Validation (GH issue #407, Wave 47). Fourth capability of `scripts/phantom-api-precheck.ps1` extending AD-685 (kwarg-name) + AD-685b (method-name) + AD-685c (kwarg type-shape) with dataclass/Pydantic field-name validation: catches `meta.totel_ops` attribute typos and `AgentMeta(succes_count=5)` constructor kwarg typos against transitive field sets, plus child fields shadowing parent `@property` or method as `property_field_collision`. New 5th cache `_CLASS_FIELDS_CACHE` and `build_class_field_index()` AST walker indexes `@dataclass`-decorated and `BaseModel`-subclass classes → `{fields, parents, properties, methods, kind}`. `_resolve_transitive_fields()` cycle-safe recursive parent walk. `find_field_phantoms()` detects two patterns: constructor kwargs via `_CTOR_RE` (CamelCase + paren block, AST-parsed for kwarg names) and attribute access via `_ATTR_RE` (`obj.field` not followed by `(`); skips chained access like `runtime.X.field` and the `runtime` receiver itself per AD-685b precedent; only flags Pattern B (`var = MyClass(...)`)-resolvable receivers. `find_property_field_collisions()` walks each indexed class's transitive parents and flags every (child_field, parent_property) and (child_field, parent_method) collision. New categories: `field_phantom` (with `access_kind=constructor|attribute`, `valid_fields[:20]`) and `property_field_collision` (with `kind=property|method`, `child`, `parent`, `name`). PowerShell wrapper extends dispatch with two new `elseif` branches. ClassVar-annotated names excluded from fields per Python semantics; private-prefix fields skipped (ProbOS narrow-private-access convention); plain (non-dataclass non-Pydantic) classes recorded ONLY when they declare properties or methods (supports parent-side collision lookup without flagging non-dataclass typos). 12 focused tests pass at `tests/test_ad685d_phantom_field_name.py` (over 10 floor by 2): dataclass known-field happy + unknown attribute flagged + unknown ctor kwarg flagged; Pydantic known-field happy + unknown field flagged; property/field collision flagged; method/field collision flagged; inherited fields recognized via transitive walk; var-no-resolve silent skip; output category strings exact; ClassVar excluded; subprocess self-test on prompt body returns exit 0 with FPs constrained to fixture class names. Existing 35 phantom-API pre-check tests (`test_phantom_api_precheck_kwargs.py` + `test_phantom_api_precheck_method_calls.py` + `test_ad685c_phantom_type_shape.py`) continue to pass — record-shape stable. Backward compat preserved: AD-685 records keep no `category` field; AD-685b/c records unchanged. **What this AD does NOT change** (out of scope by design): no TypedDict field validation (defer AD-685e); no runtime introspection (strictly AST-static — fields injected via metaclass / `__init_subclass__` are invisible); no per-instance type narrowing across function boundaries (Pattern B resolves within prompt body only); no constructor kwarg type-shape on dataclass fields (AD-685c handles type-shape against function annotations; extending to dataclass field annotations is AD-685e candidate); no NamedTuple / attrs / msgspec; no exit-code semantics change; no new directory layout (helper grew ~280 lines to ~1280 total). Helper passes JSON-safety check inherited from AD-685c (no AST nodes leak to output). Full gate: 11134 passed, 15 skipped (delta +12 vs Wave 46 baseline 11122 — exact target hit). Pre-commit deletion sanity: max ~3 deletions any single file (additive-only — wrapper elseif inserted as new branch, helper additions all append, new test file). No hard-stops triggered: no architectural change required, no phantom in implementation (verify-first pre-check at draft confirmed all 8 anchors at HEAD `893f29b`: `TypeShape`/`ValueShape` line numbers, `find_type_shape_phantoms` insertion site, `_RUNTIME_CONFLICTS_CACHE` cache slot, `build_class_method_index` insertion anchor, type_shape_mismatch wrapper dispatch, `@dataclass` and `class.*BaseModel` patterns in src). Closes GH issue #407.

 Phantom-API Pre-Check Type-Shape Validation (GH issue #406, Wave 46). Third capability of `scripts/phantom-api-precheck.ps1` extends AD-685 v1 (kwarg-name shape) + AD-685b v1 (method-name on resolved class) with kwarg-value type-shape validation: when a prompt asserts `obj.method(name=42)` and `name: str` is the live signature annotation, flag as `type_shape_mismatch`. Single-file extension to `scripts/phantom_api_ast_helper.py` (NO new package; verified at draft time the Captain spec referenced `scripts/phantom-api-precheck/` directory but reality is one helper file + one wrapper). Implementation: (1) `_collect_param_annotations(func)` parallel collector mirroring `_collect_param_names()` for positional/kwonly/*args/**kwargs categories; `build_index()` records `param_annotations: dict[str, ast.AST]` alongside the existing `params: list[str]` in the same single AST walk (zero extra file I/O — meets <2x perf bound trivially). (2) Slotted `TypeShape` class with `literal_types: frozenset[str]`, `allow_none: bool`, `container: str | None`, `element_shapes: tuple[TypeShape, ...]`, `unknown: bool` + `is_skippable()` predicate. `_annotation_to_type_shape(node)` pure-AST resolver handles primitives (str/int/float/bool/bytes), `Optional[X]`, `X | None`, `None | X`, `Union[A, B, ...]`, `list[T]` / `dict[K, V]` / `tuple[T, ...]` / `tuple[T1, T2]` / `set[T]` / `frozenset[T]` plus bare `list`/`dict`/`tuple`/`set` containers (permissive at element level when no type-parameter); any unrecognised class name (e.g. `KnowledgeEdge`) yields `unknown=True` which the validator treats as SKIP. Helpers `_iter_tuple_elts`, `_flatten_bitor`, `_union_shape` factor common subscript/BitOr handling. (3) Slotted `ValueShape` + `_value_to_shape(node)` pure-AST classifier handles `ast.Constant` for str/int/float/bool/None (with bytes returning `None` per Captain spec — "skipped on bytes/bytes-like — cannot resolve"), `ast.List`/`ast.Set`/`ast.Tuple`/`ast.Dict` containers (dict encodes keys+values into one element_shapes tuple); `ast.Name`/`ast.Call`/`ast.Attribute`/anything else returns `None` (silent skip). `_value_matches_shape(value, shape)` compatibility check is conservative: skippable shapes return True (never flag); `None` matches iff `allow_none`; primitive matches iff `value.primitive in shape.literal_types`; `bool` value also matches `int` annotation (Python semantics — bool IS-A int); container value matches iff container kind matches AND every element value-shape matches the corresponding element type-shape (homogeneous for list/set/`tuple[T, ...]`, positional for `tuple[T1, T2, T3]`, key/value for dict); empty container matches any container of the same kind (no element evidence to refute); annotation with `unknown=True` skips (returns True; never flag). (4) `find_type_shape_phantoms(body, index)` scans `_CALL_RE` matches the same way as `find_kwarg_phantoms` — skips noisy receivers/methods, skips `**kwargs`-accepting candidates (kwarg name is permissively accepted; annotation is unknown), re-parses the call's kwarg block as `ast.parse(f"_f({kwarg_block})", mode="eval")` to get value ASTs, then for each keyword whose name is accepted by the live signatures collects the union of param-annotation shapes across matching candidates. Flag iff (a) at least one candidate accepts the kwarg AND has an annotation for it, AND (b) NONE of the applicable shapes match the value. If ANY candidate has the kwarg but no annotation → permissive across all candidates → no flag. Emits `category="type_shape_mismatch"` records with `value_type` (primitive name or container kind label), `expected_types` (sorted union of literal_types across applicable shapes), `kwarg`, and a JSON-safe candidate slice (see point 5). (5) `_jsonable_candidate(c)` projects candidate dicts to a JSON-serializable subset by stripping `param_annotations` (AST nodes are not JSON-serializable). Both `find_kwarg_phantoms()` and `find_type_shape_phantoms()` use this when emitting `candidates: candidates[:5]`; the existing tests `test_phantom_api_precheck_kwargs.py` rely on the existing JSON shape and would break otherwise. (6) `main()` extends the dispatch list: `phantoms = kwarg_phantoms + method_phantoms + type_shape_phantoms`. PowerShell wrapper `scripts/phantom-api-precheck.ps1` adds a third `elseif ($p.category -eq 'type_shape_mismatch')` branch in the helper-output dispatch block; renders as `<method>(<kwarg>=<<value_type>> -> expected <<expected>|<expected>>)` with `<unknown>` fallback when no expected literal types resolved (e.g. annotation was a container only). No exit-code semantics change — type-shape phantoms flow into the same `phantoms` list and contribute to non-zero exit per existing rule. Backward compat preserved: existing AD-685 v1 kwarg-phantom records (no `category` field) and AD-685b method-phantom records (`category="method_phantom"`) ship unchanged in shape. **Architect call (DLog #1) — single-file extension preserved**: Captain's spec implied a `scripts/phantom-api-precheck/` directory; verify-first at draft confirmed reality at HEAD `c33c38d` is one helper file + one PowerShell wrapper. Splitting into a package would be a separate refactor AD; v1 stays single-file (helper grew ~280 lines). **Architect call (DLog #2) — when ANY candidate accepts the kwarg but has no annotation, the validator skips this kwarg**: avoids false positives against legacy unannotated APIs. Only when EVERY applicable candidate has an annotation does the validator flag. **Architect call (DLog #3) — bool matches int annotation**: Python's bool IS-A int (`isinstance(True, int) == True`); flagging `f(count=True)` against `def f(count: int)` would be a false positive. **Architect call (DLog #4) — empty container is permissive**: an empty list/dict/tuple/set has no element evidence to refute, so `f(tags=[])` matches both `list[str]` and `list[int]`. **Architect call (DLog #5) — bytes literal returns None (silent skip)**: bytes-like content cannot be reasoned about statically without runtime context. **Architect call (DLog #6) — unknown class annotation skips entirely**: `def w(item: KnowledgeEdge)` plus `obj.w(item=42)` does NOT flag because the validator cannot statically resolve KnowledgeEdge's structural shape. AD-685d (Wave 47) adds dataclass / Pydantic field-name validation for those cases. **Builder drift-fix during build**: helper crashed on first JSON serialization with `TypeError: Object of type BinOp is not JSON serializable` because `param_annotations` AST nodes leaked into the `candidates[:5]` slice in both `find_kwarg_phantoms` and `find_type_shape_phantoms` records. Added `_jsonable_candidate(c)` projection that strips `param_annotations` before serialization; both phantom emitters now route through it. This restored the 7 existing `test_phantom_api_precheck_kwargs.py` tests that broke between baseline 11107 and the helper edit (they were re-running the wrapper end-to-end via subprocess and depended on the helper not crashing). 15 focused tests pass at `tests/test_ad685c_phantom_type_shape.py` (over the 10 floor by 5): `_value_to_shape` primitives + bytes-skip + Name-skip + Attribute/Call-skip; `_annotation_to_type_shape` for primitives + unknown class + Optional + `X | None` + `None | X` + `int | str` + `Union[int, float]`; container annotations (`list[str]` shape, bare `list`, `dict[str, int]` two element shapes); match `str → str` no phantom; mismatch `int → str` flagged with value_type=int + expected=["str"]; None matches `Optional[str]` + None to `str` flagged; `list[str]` matches `["a","b"]` + `list[str]` flagged for `[1,2]`; variable reference silent skip; bytes literal silent skip; `int | str` accepts int + flags float; unknown-class annotation skipped for any value; backward-compat AD-685 kwarg phantom record shape unchanged (no `category` field); Captain-required wrapper self-test runs `pwsh ./scripts/phantom-api-precheck.ps1 prompts/ad-685c-phantom-type-shape-v1.md` via subprocess and asserts exit code in {0, 1} with stderr captured. **What this AD does NOT change** (out of scope by design): no dataclass / Pydantic field-name typo detection (AD-685d, Wave 47); no type-shape on return values (separate AD if ever needed); no runtime introspection (strictly AST-static — unknown class annotations are SKIPPED); no new directory layout (single-file helper grows; package split would be separate refactor); no exit-code semantics change; no PowerShell `$STDLIB_PREFIXES`/`$DOC_FILE_PATTERN` change. Phantom-API pre-check on the AD-685c prompt itself: 0 candidates, clean (cleanest result class — same as Waves 27/36/37/38). Full gate: 11122 passed, 15 skipped (delta +15 vs Wave 45 baseline 11107 — over the +10-12 acceptance-criteria target by 3-5; the +15 is 12 new helper-shape tests + 3 boundary tests for compatibility rules). Pre-commit deletion sanity: max ~5 deletions any single file (additive-only changes — wrapper elseif inserted as new branch, helper additions all append, new test file). No hard-stops triggered: no architectural change required, no phantom in implementation (verify-first pre-check at draft time confirmed the single-file shape + insertion anchors). Closes GH issue #406.

AD-661b + AD-661c v1 CLOSED. Ship's Records consumption + budget remainder redistribution (combo Wave 45, GH issues #412 + #413). Two converging extensions to AD-661 v1 DiagnosticContextService landed in one Builder cycle per Captain's "no trivial deferral" rule. (1) **AD-661b — Ship's Records (AD-434) tier**: `DiagnosticBundle` gains a 4th `records: list[dict]` field (default_factory=list, present in `to_dict()` with copy semantics); new `_gather_record_candidates(*, keywords)` collector reads `runtime.records_store.list_entries()` + `read_entry(path, reader_id, reader_department)` using a synthetic system reader identity (module-level `_RECORDS_READER_ID = "_diagnostic_context_system"` with empty department — naturally surfaces only ship/fleet records via RecordsStore's built-in classification gate; per-agent record authorization is deferred AD-661f). Two-phase keyword filter: title-substring first (cheap), full-content fallback when title misses. Each accepted record normalized to a flat dict (`path`/`title`/`summary_excerpt`/`classification`/`author`/`status`/`tags`); content excerpt truncated to `_RECORDS_CONTENT_EXCERPT_CHARS = 1200`; candidate list hard-capped at `_MAX_RECORDS_CANDIDATES = 30` before token-trim. Tier-2 log-and-degrade — missing `records_store` or any read failure yields `records=[]` without raising. (2) **AD-661c — budget remainder redistribution**: existing `_collect_*` collectors renamed to `_gather_*` candidate producers (no per-tier budget clip); new centralized `_fill_with_redistribution(candidates_by_tier, allocations, total_budget, redistribute) -> (filled, truncated)` does two-pass fill in `_TIER_PRIORITY = ("chain_traces", "procedures", "episodes", "records")` priority order. Pass 1 fills each tier up to its allocated budget; Pass 2 (only when `redistribute=True`) walks tiers again in priority order topping up tiers that have remaining candidates while global budget has room. `truncated=True` iff global budget is exhausted AND at least one tier still has unconsumed candidates — strictly correct in both directions (no over-claim, no under-claim). Default allocation split moved from 40/30/30 (3-tier) to 30/25/25/20 (chain_traces/procedures/episodes/records). New ctor kwargs `records_ratio=0.20` and `redistribute_remainder=True`. `DiagnosticContextConfig` Pydantic model: 4th `records_ratio: float = 0.20` field added to `field_validator` cohort; `model_validator` extended to sum all 4 ratios; new `redistribute_remainder: bool = True`. `_wire_diagnostic_context(*, runtime, config)` extended with the two new kwargs. The router (`routers/diagnostic_context.py`) is unchanged because `bundle.to_dict()` automatically includes the new `records` key. **What this AD does NOT change** (out of scope by design): no semantic search of records (still keyword-only); no per-agent records authorization (uses synthetic system reader, deferred AD-661f); no AD-692-style classification gating beyond what RecordsStore already enforces inside `read_entry`; no records-aware HXI surface; no AD-661d push notifications; no AD-661e LLM summary fallback; no new EventType, no new agent, no new pool, no new Pydantic config beyond the 2 added fields, no new module. **Architect call (DLog #1) — synthetic reader naturally yields ship/fleet only**: AD-434 RecordsStore's `read_entry(path, reader_id, reader_department)` enforces classification taxonomy (`private`/`department`/`ship`/`fleet`) inside the call. Empty `reader_department` plus a non-real reader id means private+department records are filtered out by the store itself before reaching the bundle; ship+fleet records pass through. This is the correct safe default for system-level diagnostic context. Documented in module docstring + config docstring. **Architect call (DLog #2) — AD-692 edge classification orthogonal**: `KnowledgeEdgeClassificationGate` (Wave 42) is for `KnowledgeEdge` graph data, NOT records. RecordsStore has its own per-record classification handled inside `read_entry`. NO edge-style decorator wrapping introduced here. **Architect call (DLog #3) — back-compat invariant**: When records_store is None and `redistribute_remainder=False`, chain_traces/procedures/episodes content is identical to v1. New `test_v1_backcompat_chain_procedures_episodes_unchanged` asserts the procedure-exemplar resolution path matches v1 expectations exactly under those conditions. **Builder drift-fix during build**: v1 test `test_chain_trace_keyword_filter_and_budget` (test_ad661_diagnostic_context.py:81) asserted `bundle.truncated is True` against the old per-tier hard cap; under the new redistribute=True default, the c3 row that overflowed chain_budget=600 fits in the global budget=2000 via redistribution → `truncated=False`. Architect's blanket "all 8 v1 tests pass unchanged" claim was overscoped vs the parenthetical invariant ("when records absent AND redistribution disabled"). Minimal fixture amendment: bumped c3 `error_truncated` from `"x"*6000` → `"x"*12000` so the row exceeds the GLOBAL budget rather than just chain_budget — preserves the test's intent (overflow → truncated). 13 focused tests pass at `tests/test_ad661bc_records_redistribution.py` (over the 10 floor by 3): config 4-ratio default + sum validator rejection; bundle.records frozen field + to_dict copy semantics; records gathered with title-hit + content-hit (paths sorted r1+r3); records empty when records_store=None; records empty when keyword has no match; records excerpt truncated to `_RECORDS_CONTENT_EXCERPT_CHARS`; records contribute to `total_estimated_tokens` (delta verified); redistribute=True flows under-fill to chain_traces (more rows than capped variant); truncated=False when all candidates fit; truncated=True when budget exhausted with leftover candidates; redistribution priority chain_traces fills before records; v1 backcompat (records=None + redistribute=False reproduces v1 procedure-exemplar shape exactly); module-level constants sanity. All 8 existing AD-661 v1 tests in `tests/test_ad661_diagnostic_context.py` continue to pass (back-compat invariant honored). Full gate: 11106 passed, 15 skipped (delta +13 vs Wave 44 baseline 11093 — over the +12 acceptance-criteria target by 1). 1 known xdist flake (`test_dreaming.py::test_nl_to_dream_cycle_changes_weights` — same Waves 23/27/30/31/32/33/39/41 environmental pattern; passes serial; unrelated to this AD). Pre-commit deletion sanity: max ~110 deletions in diagnostic_context.py (collector rename `_collect_*` → `_gather_*` is line-level rewrite); other files small line-level deltas (config.py +30/-15, finalize.py +2, test_ad661 +5/-1, new test file +280 ins / 0 del). Well below 200-line surprise-deletion threshold per file in tracker context. No hard-stops triggered: no per-agent records auth (deferred AD-661f), no semantic record search (deferred), no edge-style classification gating (orthogonal AD-692), no scope creep, no phantoms in implementation (verify-first pre-check at draft time confirmed all 11 anchors at HEAD `3c44903`: `DiagnosticBundle` frozen dc + `_collect_*` v1 collectors + `DiagnosticContextConfig` 3-ratio + `RecordsStore.list_entries`/`read_entry` + `runtime.records_store` property + `_wire_diagnostic_context` + `routers/diagnostic_context.py` + conftest `_records_store=None`). Closes GH issues #412 + #413.

AD-594a v1 CLOSED. Consultation Workspace substrate (GH issue #160, Wave 44). Session-scoped shared workspace in Ship's Records for multi-agent advisory consultations — substrate-only ship; sibling ADs AD-594b (#161 consultation primitive), AD-594c (#162 parallel execution dispatch), AD-594d (#163 delivery pipeline) are LEGITIMATELY out of scope and remain open. Three converging components landed in one OSS v1: (1) **`RecordsStore` raw-file surfaces** — three new public async methods (`write_workspace_file(author, path, content, message)`, `read_workspace_file(path) -> str | None`, `append_workspace_file(author, path, content, message)`) inserted immediately before `_parse_document` at `records_store.py:881`; bypass YAML frontmatter coercion (workspace files are YAML/Markdown of varied shapes — manifest.yaml, journal.md, delivery.yaml, plan_v{N}.md, advisory/{agent}_{ts}.md, workitems/wi_{id}.yaml — frontmatter wrapper is wrong shape); reuse existing `_safe_path` traversal protection + `_commit` git plumbing; `consultations` appended to `_SUBDIRS` tuple (mirrors `bills` AD-618a precedent) so init creates the top-level subdir. (2) **`src/probos/consultation/` package** (NEW) — five modules: `inputs.py` ships `InputProcessor` `Protocol` seam (`process(filename: str, content: bytes) -> tuple[str, bytes]`) + `PassthroughTextProcessor` v1 default impl + `build_input_processor(name)` factory with log-and-degrade fallback (Northstar II Transporter Pattern integration point — PDF/image/audio processors plug in here under separate AD; today's `cognitive/builder.py` Transporter is unrelated builder code-chunk decomposition); `refs.py` ships pure-string `parse_workspace_refs(text) -> list[WorkspaceRef]` regex extractor + `render_workspace_refs_md(text, *, base_url="/api/consultations") -> str` markdown-link substituter (HXI integration of `[workspace:<id>/<path>]` refs intentionally out of v1 — `MessageStore.create_post()` body wrapper is consumer-side task); `templates.py` ships `ArtifactType` `(str, Enum)` (5 values: ADVISORY_REPORT/PLAN_DOCUMENT/WORK_ITEM_SPEC/SUPPORTING_DATA/DECISION_RECORD) + 5 pure markdown-skeleton render functions (`render_advisory_report`/`render_plan_document`/`render_work_item_spec`/`render_supporting_data`/`render_decision_record`) + 3 consultation templates registered in `TEMPLATES: dict[str, Callable[[], str]]` (`security_review`/`technical_design`/`incident_response`) each returning a `plan_v1.md` skeleton when `WorkspaceRegistry.create(template=<name>)`; `workspace.py` ships `WorkspaceLifecycleState` `IntEnum` (7 values 0..6: INITIATED/CONSULTING/PLAN_REVIEW/APPROVED/EXECUTING/COMPLETED/ARCHIVED) + `_ALLOWED_TRANSITIONS` adjacency map (PLAN_REVIEW can revert to CONSULTING for revision; ARCHIVED is terminal) + frozen `ConsultationWorkspaceSummary` dataclass (id/title/state/owner/participant_count/created_at/updated_at + `to_dict()`) + `ConsultationWorkspace` instance class with public id/root_path/manifest/lifecycle_state properties, six convenience writers each appending a journal entry (`add_input` routes through `InputProcessor.process` — bytes-in / bytes-out contract, decoded as utf-8 with `errors="replace"` for the raw write; `add_advisory(agent_id, content, *, summary="")` writes `{agent_id}_{ts_ms_13digit}.md` under `advisory/`; `add_plan_iteration(content)` scans `plan/` for `plan_v*.md` files, parses trailing integer, writes `plan_v{N+1}.md` — atomic filename-based versioning, no metadata file; `add_artifact`/`add_output(filename, content)` write under their respective dirs; `add_work_item(spec)` serializes spec dict via `yaml.safe_dump` to `wi_{spec.id_or_uuid8}.yaml`), `transition_to(state, *, agent_id="captain") -> bool` validates predecessor against `_ALLOWED_TRANSITIONS` (invalid → returns False + WARNING log, never raises; manifest unchanged on rejection), `append_journal(message, *, agent_id)` writes `- {ts:.3f} [{agent_id}] {message}\n` line via `RecordsStore.append_workspace_file` (best-effort log-and-degrade), `list_paths()` snapshots files under each subdirectory; private helpers `_next_plan_version()` / `_persist_manifest()` / module-private `_safe_filename_segment(s)` strips traversal characters from caller-supplied filenames (raises ValueError on empty post-strip); `WorkspaceRegistry(records_store, *, root_path="consultations", clock=time.time, input_processor=None)` ctor (raises ValueError if records_store is None), public async `create(*, title, owner_agent_id, participants, template=None) -> ConsultationWorkspace` (workspace_id = uuid4().hex[:12]; materializes 6 subdirectories by writing `.gitkeep` into each; persists initial manifest at schema_version=1 with INITIATED state; writes empty `delivery.yaml` placeholder for AD-594d; appends "workspace created" journal entry; applies template if recognized in TEMPLATES), `get(workspace_id)` with manifest-load cache, `list_active() -> list[ConsultationWorkspaceSummary]` filters out ARCHIVED; private `_load_manifest()` parses YAML with malformed-input log-and-degrade. Module-level `_WORKSPACE_SUBDIRS = ("inputs", "advisory", "plan", "artifacts", "outputs", "workitems")` + `_MANIFEST_SCHEMA_VERSION = 1`. Public package `__init__.py` re-exports 17 names. (3) **Pydantic config + finalize wirer** — new `ConsultationWorkspaceConfig` BaseModel (`enabled: bool = True`, `root_path: str = "consultations"`, `input_processor: str = "passthrough"`) declared adjacent to `ClinicalTelemetryConfig` at `config.py:1876`; wired on `SystemConfig` adjacent to `clinical_telemetry: ClinicalTelemetryConfig` at `:2226` via `consultation_workspaces: ConsultationWorkspaceConfig = Field(default_factory=ConsultationWorkspaceConfig)`. Default-True deviation from Wave-10 transitional-flag convention is INTENTIONAL — registry is read-only on boot (constructs an empty in-memory cache; only side-effect is `RecordsStore` ensuring `consultations/` directory exists at first init via `_SUBDIRS` mkdir); same precedent as `KnowledgeEdgesConfig` / `EdgeBackfillConfig`. New sync `_wire_consultation_workspaces(*, runtime, config) -> bool` wirer at `startup/finalize.py` mirrors `_wire_clinical_telemetry` shape; reads `runtime.records_store` (AD-434 adopted at `runtime.py:1324` cognitive phase, well before finalize); skips with INFO log if records_store unavailable; constructs `runtime.consultation_workspaces = WorkspaceRegistry(records_store, root_path=cfg.root_path, input_processor=build_input_processor(cfg.input_processor))` collision-free public attribute (verified). Invoked from `finalize_startup` immediately after `_wire_clinical_telemetry` and before `_wire_workspace_ontology` at `:815`. **What this AD does NOT change** (out of scope by design — separate GH issues, not deferrals): no AD-594b consultation primitive (`consult(question, context)` on CognitiveAgent — #161); no AD-594c parallel execution dispatch (`WorkItemStore` registration, dependency/conflict detection, multi-executor assignment — #162); no AD-594d delivery pipeline (`DeliveryAdapter`, Captain approval gate, format transformation, revision cycle — #163; v1 ships EMPTY `delivery.yaml` placeholder file only); no HXI message rendering integration (parser + renderer ship as pure helpers; `MessageStore.create_post()` wrapper is consumer-side task); no PDF/image/audio input processors (Protocol seam only; v1 = passthrough); no real `WorkItemStore` integration (`add_work_item` writes YAML spec to `workitems/`; does NOT register with `runtime.work_item_store`); no new EventType (workspace activity journaled to per-workspace `journal.md`; cross-mesh observability deferred AD-594b/c); no HXI surface (Captain-facing visualization deferred); no REST API endpoints (`/api/consultations` URL is future hook in `render_workspace_refs_md` default; no router shipped); no federation export (workspaces local to single ProbOS instance); no `ward_room/messages.py` or `ward_room/service.py` modification. 16 focused tests pass at `tests/test_ad594a_consultation_workspace.py` (over the 14 floor by 2): `WorkspaceLifecycleState` enum 7 values + integer ordering; `parse_workspace_refs` extracts 3 refs in order from text + empty + no-refs returns []; `parse_workspace_refs` case-insensitive duplicates preserved; `render_workspace_refs_md` substitutes single ref + empty passthrough + custom base_url; `WorkspaceRegistry.create` produces 6 subdirs + manifest.yaml + journal.md + delivery.yaml on disk + `list_paths()` returns 6 keys; manifest.yaml schema (schema_version=1, id, title, owner, participants list, lifecycle_state="INITIATED", created_at/updated_at floats, template=""); full happy-path lifecycle INITIATED → CONSULTING → PLAN_REVIEW → APPROVED → EXECUTING → COMPLETED → ARCHIVED all True with 6 lifecycle journal entries; lifecycle rejects EXECUTING → INITIATED (False, manifest unchanged, WARNING logged); PLAN_REVIEW → CONSULTING revision allowed; `add_input` routes through custom `_UpperProcessor` (filename rewritten to `report.txt.proc`, content uppercased); `add_advisory` writes `{agent_id}_{ts}.md` with advisory-report skeleton including summary + body; three `add_plan_iteration` calls produce `plan_v1.md`/`plan_v2.md`/`plan_v3.md`; `add_artifact` + `add_output` write to their respective dirs; `add_work_item` serializes spec YAML round-trips via `yaml.safe_load` with `wi_{id}.yaml` filename + uuid8 fallback when no id; journal appended on advisory + transition + add_input (3 lines delta); `list_active` filters ARCHIVED workspace + returns `ConsultationWorkspaceSummary` with correct state/participant_count for non-archived. Pre-commit deletion sanity: max 0 deletions any single tracked file (additive-only changes — three ===REPLACE=== blocks expand existing tuples/sections; new package + new test file). Full gate: 11094 passed, 15 skipped (delta +16 vs Wave 43 baseline 11078 — exact target hit). No hard-stops triggered. Closes GH issue #160.

AD-695 v1 CLOSED. Ship Health Oracle Tier 7 + Threshold Bridge Alerts (GH issue #389, Wave 43). Three converging components landed in one OSS v1: (1) **Oracle Tier 7 "health"** — new `OracleService._query_health(query_text, *, k)` reads runtime telemetry (pool stats via `spawner.pools[name].current_size/target_size`, attention queue depth via `attention.queue_size`, degradation tier via `degradation_manager.status().stress_level.value`, optional vitals via `observability_bridge.take_snapshot()`) and returns one `OracleResult` per metric scored by simple keyword overlap against the query, with `provenance="[health: <metric>]"`. `"health"` appended as the 7th element of the default `active_tiers` list (after `episodic`/`records`/`operational`/`archive`/`semantic`/`graph`); Tier 7 dispatch slot inserted between Tier 6 (graph) and `_expand_via_graph` so health results compete in expansion ranking. New ctor kwarg `health_provider: Any = None` plus public idempotent `attach_health_provider(provider)` setter mirroring `attach_semantic_layer`/`attach_knowledge_graph` shape. Health provider is duck-typed (anything exposing `spawner.pools` + `attention` + `degradation_manager` + optional `observability_bridge`); the runtime itself satisfies the protocol natively, tests inject `SimpleNamespace`. (2) **`ThresholdAlertService`** — new `src/probos/cognitive/threshold_alerts.py` with frozen `ThresholdAlert` dataclass (threshold_id/severity/metric/value/threshold/title/detail/fired_at + optional related_pool) and `ThresholdAlertService(runtime, *, pool_saturation_floor=0.9, degradation_min_severity="degraded", attention_queue_depth=20, dedup_window_seconds=300.0)`. Single public `async check_and_alert() -> list[ThresholdAlert]` runs three independently log-and-degrade checks (pool_saturation per pool, degradation via `StressLevel` ordering `{normal:0, elevated:1, degraded:2, critical:3}` with `severity="alert"` only at CRITICAL else `"advisory"`, attention queue depth) and posts each fired alert via `runtime.ward_room_router.deliver_bridge_alert(BridgeAlert(...))` with id=uuid4, alert_type=threshold_id, dedup_key=threshold_id, severity coerced to `AlertSeverity` enum. Per-(threshold_id) dedup ring with configurable sliding window, prunes entries older than 2x window. Service NEVER raises into caller — every check + every delivery is best-effort. (3) **Bridge loop becomes a threshold loop** — `ObservabilityBridge._publish_once` (BF-258 already nuked the `create_post()` spam) now invokes `await runtime.threshold_alerts.check_and_alert()` when wired, then preserves the existing `OBSERVABILITY_SNAPSHOT_PUBLISHED` event emit; `take_snapshot()` retained unchanged for both Tier 7 vitals augmentation and event-log subscribers. The bridge is no longer a heart with no pulse — it is the cadence trigger for threshold checks. New `ThresholdAlertConfig` Pydantic model (`enabled: bool = False`, `pool_saturation_floor: float = 0.9` validated [0,1], `degradation_min_severity: str = "degraded"`, `attention_queue_depth: int = 20` validated >=0, `dedup_window_seconds: float = 300.0` validated >=1) wired adjacent to `ObservabilityBridgeConfig` on `SystemConfig`. **Default-False is REQUIRED, NOT a deviation** — unlike AD-687/AD-689/AD-692 (default-True with pass-through-when-disabled cost), ThresholdAlerts actually post things on enable; operator opt-in. New finalize wirer block immediately after the AD-641a ObservabilityBridge block: constructs `runtime.threshold_alerts` when config.threshold_alerts.enabled, else None; ALSO unconditionally stitches `oracle.attach_health_provider(runtime)` for the Oracle Tier 7 late-bind (Tier 7 returns `[]` until this stitch runs). 13 focused tests pass at `tests/test_ad695_ship_health_oracle.py` (over the 12 floor by 1): default tier list contains `"health"` 7th; `_query_health` returns `[]` when no provider; pool happy path with `SimpleNamespace` provider (1 result, metric=pool, current=2/target=4, provenance=`[health: pool]`); attention queue metric (queue_depth=7); degradation metric (stress_level=degraded, shed_count=2); query keyword filter (`pool medical` returns pool-tier first); k-truncation (5 pools + attention + degradation, k=2 → exactly 2); `ThresholdAlertConfig` defaults; pool saturation breach fires (9/10, advisory, related_pool=medical, BridgeAlert dedup_key=`pool_saturation:medical`); degradation escalation fires at CRITICAL (severity=alert, AlertSeverity.ALERT); attention queue depth fires at 25; dedup prevents repost within window then fires again after window advances (3 calls → 1 fire / 0 fires / 1 fire; `deliver_bridge_alert` awaited 2 times); no-breach returns empty (5/10 pool + queue=0 + normal stress → no fires, `deliver_bridge_alert` not awaited). **What this AD does NOT change** (out of scope by design): no HXI/dashboard integration of Tier 7 (deferred AD-695b if data warrants); no historical retention of alerts beyond `bridge_alert` event row that `deliver_bridge_alert` already writes (existing AD-410 untouched); no per-agent health querying (Oracle context per-query only); no active shedding hooks (still AD-459b/AD-396); no AD-466 infrastructure restructuring (package layout untouched); no full AD-466c scope (audit log, retention, per-pool thresholds deferred); existing `BridgeAlertService` untouched (ThresholdAlertService constructs `BridgeAlert` directly, does not call `BridgeAlertService.check_*`); no new EventType. **Critical architect call (DLog #1)**: BF-258 ALREADY removed the spam `create_post()` call (BF-258 comment block at `bridge.py:121-124` mentions "Crew queries system health via Oracle (AD-695)" as the planned replacement); the "spam loop disable" task from the user spec was a NO-OP at HEAD. v1 replaces the dormant body with a guarded `await runtime.threshold_alerts.check_and_alert()` call — NOT a re-disable. **DLog #2**: `runtime` IS the duck-typed health_provider; finalize wirer calls `oracle.attach_health_provider(runtime)` after threshold_alerts wirer; OracleService at HEAD never reaches `runtime` directly (uses injected providers per AD-686/AD-688) — AD-695 mirrors that pattern. **DLog #6**: Pool surface reduced to current_size/target_size only — `Pool.info()` at HEAD does NOT expose cooldown counts (verified by grep); cooldown surfacing deferred (would require Pool.info() schema change). **DLog #7**: Tier 7 dispatch position critical — must run BEFORE `_expand_via_graph` (Tier 7 results compete in expansion ranking) and AFTER all 6 existing tiers; SEARCH anchor wraps the existing `_expand_via_graph` block. Phantom-API pre-check: 1 candidate (`SimpleNamespace` = stdlib FP test fixture), 2 standard intro-not-in-index skips (`runtime.threshold_alerts.check_and_alert`, `runtime.oracle.attach_health_provider`). 0 NEW phantoms. Same FP class as Waves 27–42. Full gate: 11078 passed, 15 skipped (delta +13 vs Wave 42 baseline 11065 — exact target hit). Pre-commit deletion sanity: max ~5 deletions any single file (line-level config.py + roadmap status flip + bridge.py comment-block replace). No hard-stops. Closes GH issue #389.

AD-692 v1 CLOSED. Classification Enforcement on Knowledge Graph Edges (GH issue #386, Wave 42). Phase B (Intelligence) of the Unified Knowledge Graph + Oracle Unification stack — closes the **read/write authorization seam** between AD-687 `KnowledgeEdgeStore` and consumers (AD-688 Oracle Tier 6) by adding a decorator-pattern wrapper (`ClassificationGatedKnowledgeEdgeStore`) around `SQLiteKnowledgeEdgeStore` that enforces the existing `KnowledgeEdge.classification` field at the storage boundary. OSS extension point — commercial overlays (audit, RBAC, multi-tenant) layer on top of the stable `KnowledgeEdgeStorage` Protocol seam without modifying the underlying store. New `src/probos/knowledge/edge_classification.py` ships: `ClassificationLevel` typed `IntEnum` with integer values matching `records_store._CLASSIFICATION_LEVELS` byte-for-byte (`PRIVATE=0`/`DEPARTMENT=1`/`SHIP=2`/`FLEET=3`) plus `from_label(label) -> ClassificationLevel` coercion that defaults to PRIVATE on `None`/unknown (most restrictive defensive); pure `edge_visible_to(edge, *, requester_tier, requester_agent_id) -> bool` visibility helper (PRIVATE = ownership gate via `edge.source_agent == requester_agent_id`; DEPARTMENT/SHIP/FLEET = tier-based via `_TIER_MAX_VISIBLE` mapping {basic: PRIVATE, enhanced: DEPARTMENT, full: SHIP, oracle: FLEET}); `KnowledgeEdgeClassificationGate` decision service with three async methods (`filter_edges(edges, *, requester_agent_id) -> list[KnowledgeEdge]` for read filtering, `authorize_write(edge, *, writer_agent_id) -> bool` for write gating where writers may always create PRIVATE edges they own and DEPARTMENT/SHIP/FLEET writes require read access at that tier — a writer cannot label content above their clearance) plus pure synchronous `filter_for_export(edges, *, target_classification=ClassificationLevel.SHIP) -> list[KnowledgeEdge]` extension hook (default excludes FLEET-tagged edges from cross-mesh export; not consumed in v1 since federation does not export edges at HEAD); `ClassificationGatedKnowledgeEdgeStore` decorator wrapper around any `KnowledgeEdgeStorage` implementation that adds optional `requester_agent_id` kwarg to `find_edges`/`traverse`/`get_edge` (when `None` — the default and the case for all Wave 37/38/39/40/41 tests — the wrapper is a transparent pass-through; when set, results are filtered through the gate); `add_edge` authorization-gates non-system writes (writes with `edge.source_agent` set) and returns the edge id idempotently when blocked (caller cannot distinguish from a stored edge — the in-store `find_edges(limit=100)` call confirms non-persistence). `traverse` uses **per-path drop semantics** — a path is dropped ENTIRELY if ANY hop edge fails visibility (conservative; prevents partial-path inference leakage). **Architect call (DLog #1) — AD-679 SHIPPED but ORTHOGONAL**: verified at `mesh/disclosure.py:15`. AD-679 is 5-tier `DisclosureLevel` IntEnum (PUBLIC..CLASSIFIED) for IntentMessage recipient routing. AD-692 is 4-tier `ClassificationLevel` IntEnum (PRIVATE..FLEET) for KnowledgeEdge read/write gating. **Different taxonomies by design** — recipient clearance vs. data-scope-of-audience. Bridging intentionally NOT in v1; `filter_for_export` ships as documented seam for future commercial overlay or AD-692c bridging work. **Architect call (DLog #2)**: `ClassificationLevel.from_label(None|unknown)` defaults to PRIVATE (most restrictive defensive) so pre-existing edges from Wave 37/38/39/40/41 that omitted classification stay restricted unless an explicit owner-equality match passes. **Architect call (DLog #3) — per-department matching DEFERRED to AD-692b**: `KnowledgeEdge` has no department field at HEAD (verified 13-field shape `edges.py:73-96`). v1 DEPARTMENT gate is tier-based only (any ENHANCED+ requester sees all department-classified edges). Documented as out-of-scope. **Architect call (DLog #4) — resolver injection via setter** (Open/Closed + layer discipline): `knowledge/edge_classification.py` does NOT import `earned_agency` or `ontology` directly. Wirer in `startup/finalize.py` builds a closure over `runtime.ontology` + `runtime.clearance_grant_store` + `runtime.registry` and injects via `gate.set_clearance_resolver(resolver)`. Mirrors AD-660/AD-635 pattern. **Architect call (DLog #5) — wirer phase ordering**: must run in finalize. `runtime.knowledge_edges` adopted at `runtime.py:1618`, `runtime.ontology` at `:1651`. Wirer ALSO re-stitches Oracle Tier 6 via `oracle.attach_knowledge_graph(wrapper)` so Tier 6 sees the wrapper, not the bare AD-687 store. New `KnowledgeEdgeClassificationConfig` Pydantic model (`enabled=True`, `default_classification="private"` validated via `field_validator` against {private/department/ship/fleet}) wired adjacent to `knowledge_edges`/`edge_backfill` on `SystemConfig`. Default-True deviation from Wave-10 transitional-flag convention is intentional + same precedent as `KnowledgeEdgesConfig`/`EdgeBackfillConfig` — wrapper is transparent pass-through when no `requester_agent_id` supplied; cost = wrapper construction at boot. New `_wire_edge_classification(*, runtime, config) -> bool` sync wirer at `startup/finalize.py` mirrors `_wire_nl_graph_query` shape; constructs gate, builds tier resolver closure (registry lookup → agent_type/rank → `resolve_billet_clearance(agent_type, ontology)` + `resolve_active_grants(agent_id, clearance_grant_store)` → `effective_recall_tier(rank, billet, grants).value` — RecallTier is `str, Enum` so `.value` is the lowercase string form; tier-2 log-and-degrade returns "basic" on any resolver failure), wraps `runtime.knowledge_edges`, sets `runtime.edge_classification_gate` public attribute (collision-free — verified against `_classification_gate` AD-530 in `security/classification.py`), re-attaches the wrapper to Oracle Tier 6 via existing `oracle.attach_knowledge_graph(wrapper)`. Invoked from `finalize_startup` immediately after `_wire_nl_graph_query` and before `_wire_clinical_telemetry`. Oracle Tier 6 plumbed through: `OracleService.query()`'s existing `agent_id` kwarg now threads to `_query_graph(query_text, *, k, requester_agent_id="")` (default empty string preserves Wave 38 backward-compat — no filtering when empty); `_query_graph` calls go through two new private helpers `_graph_find_edges`/`_graph_traverse` that pass `requester_agent_id` only when non-empty AND fall back via `TypeError` catch when the underlying graph instance does NOT accept the kwarg (production wraps see the AD-692 wrapper which accepts the kwarg; MagicMock-based Wave 38 tests fall back gracefully — same backward-compat invariant as the wrapper itself). Knowledge package `__init__.py` exports the 4 new names (`ClassificationGatedKnowledgeEdgeStore`, `ClassificationLevel`, `KnowledgeEdgeClassificationGate`, `edge_visible_to`) alongside existing AD-687/AD-689/AD-690 names. **What this AD does NOT change** (out of scope by design): no new EventType (audit emission deferred AD-692b); no persistence of denied-access audit log (in-memory ring buffer also deferred AD-692b — would couple gate to event bus); no federation export of edges (`filter_for_export` ships as pure helper / extension hook only — federation today does not export `KnowledgeEdge` objects; future work AD); no bridging to AD-679 `DisclosureLevel` (intentionally orthogonal taxonomies; AD-692c if commercial overlay needs unified policy); no per-department classification matching (`KnowledgeEdge` has no `dept` field; v1's DEPARTMENT gate is tier-based only); no encryption-at-rest for edge fields; no fine-grained per-edge ACLs beyond level-based gating; no agent / pool / intent (this is a guard layer; existing callers consume it transparently); no HXI surface (Captain-facing visibility deferred AD-692d). 21 focused tests pass at `tests/test_ad692_classification_enforcement.py` (over the 12 floor by 9 — the parametrized matrix in test #2 fans out to 8 cases across tier × classification × ownership): `ClassificationLevel` enum ordering + `from_label` round-trip + None/empty/unknown coercion to PRIVATE; 8-case matrix of `edge_visible_to` (basic+private+owner=requester True; basic+private+other False; basic+department False; enhanced+department True; enhanced+ship False; full+ship True; full+fleet False; oracle+fleet True); gate filter_edges with ORACLE clearance returns mixed 4-classification list; gate filter_edges drops fleet for FULL clearance (3/4 returned, no fleet); gate authorize_write blocks ENHANCED writer attempting FLEET edge; gate authorize_write permits any-tier writer adding own PRIVATE edge; gate filter_for_export default `target=SHIP` excludes fleet (3/4) plus explicit `target=FLEET` returns all 4; wrapper find_edges with ENHANCED requester filters real `SQLiteKnowledgeEdgeStore`-backed fixture (private-owned + department visible, ship blocked); wrapper find_edges with no requester is pass-through (3/3 — backward compat); wrapper traverse drops paths containing FLEET hop entirely (per-path drop semantics) for FULL-clearance requester; wrapper edge with `classification=None` treated as PRIVATE — not returned to non-owner, returned to owner; wrapper add_edge blocks unauthorized SHIP write by basic-tier writer (returned id idempotent surface, but `inner.find_edges(limit=100)` confirms non-persistence); Oracle Tier 6 `_query_graph(query_text="alice", k=10, requester_agent_id="alice")` against ENHANCED-clearance gate-wrapped store returns FEWER results than unfiltered baseline (4 → 3, fleet edge filtered); backward-compat smoke wraps `SQLiteKnowledgeEdgeStore` populated with 5 edges of varying classifications and verifies pre-wrap and post-wrap counts of `find_edges()` and `traverse()` (no `requester_agent_id`) match exactly — wrapper is a no-op pass-through for legacy callers. Phantom-API pre-check at draft time: 3 candidates ALL FPs (`runtime.edge_classification_gate` — introduced by prompt; `add_edge(classification=)` + `add_edge(source_agent=)` — script matching nested `KnowledgeEdge` constructor kwargs in test snippets, both verified fields on `KnowledgeEdge` dataclass). 0 NEW phantoms. Same FP class as Waves 27–41. Full gate: 11064 passed, 15 skipped (delta +21 vs Wave 41 baseline 11043 — over the +12 acceptance-criteria target by 9; matrix parametrization fan-out is the source). 1 known xdist flake (`test_knowledge_store::test_auto_commit_after_debounce` — same Wave 8/14/15/16/19/22/27/30/31/32/33/37 environmental git-debounce pattern; passes serial; unrelated to this AD). No hard-stops triggered: no decomposer-side modification (wrapper transparent at storage boundary), no audit emission (deferred AD-692b), no federation bridging to AD-679 (deferred AD-692c), no per-department matching (deferred AD-692b), no scope creep, no phantoms in implementation (verify-first pre-check at draft time confirmed all 17 anchors at HEAD `fdb71b5`: `_CLASSIFICATION_LEVELS` taxonomy `records_store.py:27` + `KnowledgeEdge` 13-field shape `edges.py:73-96` + `KnowledgeEdgeStorage` Protocol `:133` + `find_edges`/`traverse` signatures `:149/159` + `RecallTier` str-Enum `earned_agency.py:53` + 3 clearance helpers `:100/131/149` + runtime adoption ordering `runtime.py:1614/1618/1620/1651` + `Oracle.query` `agent_id` kwarg `oracle_service.py:167` + `_query_graph` `:480` + `_wire_nl_graph_query` sibling shape `finalize.py:394` + cascade slot `:730` + `EdgeBackfillConfig` insertion anchor `config.py:1806/2147` + `knowledge/__init__.py` re-export pattern + federation has NO edge export at HEAD + AD-679 SHIPPED orthogonal at `mesh/disclosure.py:15`). Closes GH issue #386.

AD-691 v1 CLOSED. NL-to-Graph Query Service — Ship's Computer structural routing (GH issue #385, Wave 41). Phase B (Intelligence) of the Unified Knowledge Graph stack — closes the **read seam** by adding an LLM-driven 2-phase NL→graph entry point on top of AD-687 KnowledgeEdgeStore + AD-688 Oracle Tier 6. New `src/probos/cognitive/nl_graph_query.py` with `NLGraphQueryService(runtime, *, default_max_hops=2, default_limit=10, llm_tier="standard", extraction_max_tokens=600, synthesis_max_tokens=800)` (mirrors AD-661 DiagnosticContextService runtime-injection sibling shape; reaches `runtime.knowledge_edges` + `runtime.llm_client` lazily; NEVER raises into caller — every failure path returns a well-formed degraded `NLGraphQueryResult`). Two frozen result dataclasses: `EntityExtraction(id, type)` + `to_dict()`; `NLGraphQueryResult(query, extracted_entities, edges_traversed, paths, answer, provenance)` + `to_dict()` (deep-copy semantics on list fields; non-defaulted `query` first per frozen-dc field-order rule). Single public `async query(natural_language, *, max_hops=None, limit=None) -> NLGraphQueryResult`. **Phase 1 (extraction)**: `tier="standard"` LLM call with strict-JSON system prompt → `{entities: [{id, type}], relation_filter: [...], intent: "find|traverse|count"}`. **Graph step**: per entity, calls `runtime.knowledge_edges.find_edges(source_id|target_id, ..., limit)` for direct hits (proximity 1.0) AND `traverse(source_type, source_id, max_hops, relation_filter)` for multi-hop paths. Hop-proximity formula extends AD-688: `0.6 ** (hop - 1)` → direct=1.0, hop-2=0.6, hop-3=0.36 (inline `_GRAPH_HOP_PROXIMITY_BASE` constant — NOT config; same precedent as AD-688's `_GRAPH_HOP_PROXIMITY_DIRECT/TWO_HOP`). Score = `edge.weight × edge.confidence × hop_proximity`; dedupe by `edge.id` keeping the highest-scoring instance. **Phase 2 (synthesis)**: `tier="standard"` LLM call with edge JSON inlined; system prompt mandates `[graph: <edge.id>]` citation per fact. **`provenance` field** = deduped, in-order edge IDs ACTUALLY cited in the answer (extracted via `_CITATION_RE = r"\[graph:\s*([0-9a-fA-F]{16,})\s*\]"` and filtered against `allowed_ids = {e.id for e in edges}` so hallucinated IDs are silently dropped) — distinct from `edges_traversed` which lists every edge the graph step retrieved. Four short-circuit paths: (1) `edge_store is None or llm_client is None` → `"Knowledge graph or LLM unavailable."`; (2) Phase-1 LLM exception or unparseable JSON → `"Could not parse query."` + no Phase-2; (3) `entities=[]` → `"No graph entities identified in query."` + no Phase-2 + no graph step; (4) graph step returns 0 edges → `"No relevant graph evidence."` + no Phase-2. `relation_filter` coercion: filter raw strings against `KnowledgeRelationType.value` set; unknown values dropped at `logger.debug`; empty post-coerce → pass `None` to `traverse()` (else trivially returns `[]`). `max_hops` clamped to `MAX_HOPS_CEILING=3`; `limit` floored at 1. New `NLGraphQueryConfig` Pydantic model (`enabled=True`, `default_max_hops=2`, `default_limit=10`, `llm_tier="standard"`, `extraction_max_tokens=600`, `synthesis_max_tokens=800`) with two `field_validator`s (`default_max_hops` in [1,3], `default_limit >= 1`). Default-True deviation from Wave-10 transitional-flag convention is documented in config docstring — service is callable read-only aggregator with no automatic invocation; same precedent as `DiagnosticContextConfig`/`KnowledgeEdgesConfig`. Wired onto `SystemConfig.nl_graph_query` via `Field(default_factory=NLGraphQueryConfig)` immediately after `diagnostic_context` field. Sync `_wire_nl_graph_query(*, runtime, config) -> bool` at `startup/finalize.py` mirrors `_wire_diagnostic_context` shape exactly; invoked from `finalize_startup` immediately after `_wire_diagnostic_context`. Public attribute `runtime.nl_graph_query` (Wave 5 conv #1; collision-free greenfield — 0 hits at HEAD `98ecbb8`). New `src/probos/routers/nl_graph_query.py` with `GET /api/nl-graph-query?q=&max_hops=&limit=` — 400 on empty `q`, 503 when service None, hard caps `max_hops` to [1,3] and `limit` to [1,100]; returns `result.to_dict()`. Registered alphabetically into `api.py` import + for-loop tuples (twin-block SEARCH/REPLACE bundled into single multi_replace per Wave 31/33 pattern). **Decomposer integration via `NLGraphQueryAgent`** (Section 5b) — new `src/probos/agents/utility/nl_graph_query_agent.py` ships a single-agent utility pool wrapping the service. Pattern mirror: `agents/introspect.py` (`tier="utility"`, `_runtime`-backed delegation, full `perceive→decide→act→report` lifecycle, `requires_reflect=True`, no consensus). Declares `IntentDescriptor(name="nl_graph_query", tier="utility", requires_consensus=False, requires_reflect=True)` so the decomposer's existing dynamic IntentDescriptor discovery (cognitive/decomposer.py:280 — confirms NO hardcoded "ask" route) routes structural-query natural language to it WITHOUT any decomposer-side modification. Agent params: `query` (natural-language question), `max_hops` (optional, 1-3), `limit` (optional). Tier-2 log-and-degrade on delegation failure. Exported from `agents/utility/__init__.py`. Spawner template registered in `runtime.py` immediately after `introspect`; pool created in `startup/agent_fleet.py` immediately after the `introspect` pool block, gated on `config.nl_graph_query.enabled` AND `runtime.nl_graph_query is not None` (third clause means pool creation cleanly skips if startup ordering ever flips, no crash). **What this AD does NOT change** (out of scope by design): no decomposer-side modification (the agent + pool route via existing dynamic registry); no embedding-based fuzzy entity match (deferred AD-691c if surfaced; v1 uses verbatim entity IDs as extracted by the LLM); no write/mutation queries (graph is read-only here); no classification-aware filtering (AD-692 commercial owns); no Fabric IQ NL2GQL parity (v1 is simple typed-triple traversal); no HXI surface (deferred until consumer signal); no new EventType (telemetry via `logger.info`/`logger.debug` v1); no Pydantic config for hop-proximity formula (inline `_GRAPH_HOP_PROXIMITY_BASE`); no persistence of query history (each call stateless); no streaming response. **Builder drift-fix during build**: (1) `BaseAgent` is abstract with required `report()` method — added `async def report(self, result)` returning `result` to NLGraphQueryAgent (mirrors `IntrospectionAgent.report` shape). (2) Test 16 referenced `from probos.spawner import AgentSpawner` (wrong path); corrected to `from probos.substrate.spawner import AgentSpawner` + `from probos.substrate.registry import AgentRegistry` and pass registry to ctor. (3) `tests/test_utility_agents.py::test_all_agents_exported` asserts `set(__all__) == expected` (exact equality) — extended `expected` to include `"NLGraphQueryAgent"` to keep the existing utility-export contract. 16 focused tests pass at `tests/test_ad691_nl_graph_query.py` (over 14 floor by 2): `NLGraphQueryResult` frozen + `to_dict` round-trip; service ctor 6-kwarg shape + defaults; happy path with `_StubLLM` (Phase-1 entity → 1 direct edge → Phase-2 with citation → `provenance == [edge.id]`); Phase-1 parse failure (`llm.calls == 1`, no Phase-2); empty extraction short-circuit (`llm.calls == 1`, `find_calls == []`); `relation_filter` passthrough (coerced to `[KnowledgeRelationType.REPORTS_TO]`); unknown relation dropped silently; `max_hops=99` clamped to 3; `limit=5` truncates 25-edge result; scoring orders by `weight × confidence × proximity` descending; hallucinated `[graph: deadbeef...]` citation dropped from `provenance`; no edges → `"No relevant graph evidence."` + no Phase-2; API endpoint 400 on empty `q`; API endpoint 200 happy-path with `TestClient` + `dependency_overrides`; `NLGraphQueryAgent.handle_intent` happy-path delegates to `runtime.nl_graph_query.query(...)` with NL passed through verbatim + result fields projected to `IntentResult.result` dict (answer/provenance/extracted_entities/edge_count/path_count); pool/template registration smoke (config defaults match, IntentDescriptor declared with `tier="utility"`, spawner template registers cleanly). Phantom-API pre-check: only intro-not-yet-in-index FPs (`runtime.nl_graph_query`, `service.query()` kwargs, `NLGraphQueryAgent` lifecycle methods, module-level constants — all introduced by Sections 1–5b). Full gate: 11042 passed, 15 skipped, 1 known xdist flake (`test_dreaming.py::test_nl_to_dream_cycle_changes_weights` — passes serial in 25s; same Waves 23/27/30/31/32/33/39 pattern; unrelated to this AD). Test-count delta: +14 vs Wave 40 baseline 11028 (exact target hit). No hard-stops triggered: no decomposer-side modification (agent + pool covers the route), no fuzzy entity match (deferred AD-691c), no classification gating (deferred AD-692 commercial), no scope creep, no phantoms in implementation (verify-first pre-check at draft time confirmed all 17 anchors at HEAD `98ecbb8`: `KnowledgeEdge` 13-field frozen dc + 8 entity types + 10 relations + `KnowledgeEdgeStorage.find_edges`/`traverse` kwargs + `MAX_HOPS_CEILING=3` + `runtime.knowledge_edges` slot + `OracleService` Tier 6 sibling + `_GRAPH_HOP_PROXIMITY_DIRECT/TWO_HOP` + `LLMRequest`/`LLMResponse` shapes + `BaseLLMClient.complete` + `extract_json` + `_StubLLM` test pattern + `DiagnosticContextConfig` sibling shape + `SystemConfig.diagnostic_context` insertion site + `_wire_diagnostic_context` sibling shape + `routers/diagnostic_context.py` sibling shape + `api.py` twin-block + decomposer dynamic intent discovery + `IntrospectionAgent` agent pattern + `runtime.spawner.register_template("introspect", ...)` anchor + `agent_fleet.py` introspect pool block). Closes GH issue #385.

AD-690 v1 CLOSED. Dream Step 10 — Relationship Inference (GH issue #384, Wave 40). Phase B (Intelligence) of the Unified Knowledge Graph stack — first dream step that learns AGENT→AGENT edges from observed co-participation, complementing AD-689 structural backfill. New `src/probos/knowledge/rejection_cache.py` (`RejectionCacheStorage` runtime-checkable Protocol + `SQLiteRejectionCache` concrete impl mirroring `SQLiteKnowledgeEdgeStore` start/stop lifecycle; `dream_step10_rejections` table with composite PK `(source_id, target_id)`; `was_rejected` undirected — checks both `(a,b)` and `(b,a)`; `record_rejection` is `INSERT OR REPLACE` append-only). New `src/probos/cognitive/relationship_inference.py` pure service (no runtime access; deps passed in): `RelationshipInferenceResult` frozen dataclass (4 counters + `to_dict()`), `_extract_agent_pairs(episodes) -> list[tuple[str, str]]` (sorted-tuple dedup via `dict.fromkeys`, in-episode self-pair skip, singleton-agent_ids skip), `_classify_pair_with_llm(a, b, *, llm_client)` returning `(relation, confidence, rationale_or_reason)` with five rejection paths (`llm_call_failure` / `llm_parse_failure` / `llm_returned_null` / `relation_not_in_whitelist` / non-string relation), and `infer_relationships_from_episodes(*, episodes, knowledge_edges, llm_client, rejection_cache, max_pairs_per_run=50, max_inferences_per_entity=5, min_confidence=0.6)` orchestrator. Five anti-contamination guards: (1) per-run pair cap; (2) rejection-cache replay-skip BEFORE LLM call (silent — does not increment `relationship_pairs_rejected` on second pass; that counter is the first-classify rejection counter only); (3) existing-edge skip in either direction via `find_edges(source_id, target_id, limit=1)`; (4) per-entity cap counted as `relationship_pairs_capped` (still consumes a pair-budget slot); (5) min-confidence floor — sub-threshold pairs cached with reason `below_threshold_{conf:.2f}`. Relation whitelist v1: `REPORTS_TO` and `DEPENDS_ON` only (off-whitelist relations rejected as `relation_not_in_whitelist`). All inferred edges tagged `source_agent="dream_step10"`, `source_duty="relationship_inference"`, `weight=0.5`, `confidence` clamped to [0.0, 1.0]. **Architect call (Decision Log #1)**: GH issue title is "Dream Step 10" (Captain-facing, kept on AD title and roadmap) but live `cognitive/dreaming.py` already has Step 10 = AD-555 Notebook Quality Metrics; new step installs as **Step 7i** between 7h (AD-572 episodic-procedural bridge) and Step 8 (AD-385/539 gap detection) — semantically right (runs after procedure consolidation so episode metadata is fresh, before downstream metrics consume it). **Architect call (Decision Log #2)**: spec drafted `runtime.episodic.list_episodes(since=...)` but live `EpisodicMemory.list_episodes` (AD-689) has only `(*, limit=None)` kwargs; Step 7i consumes the already-in-scope `episodes` variable that earlier dream-cycle steps read — naturally captures "episodes considered in this cycle" without API drift. **Architect call (Decision Log #3)**: rejection cache implemented as dedicated SQLite-backed module rather than overloading `KnowledgeRelationType.CLASSIFIED_AS` with a sentinel — option (a) would pollute `knowledge_edges` with non-graph rows and confuse AD-688/AD-689 consumers. **Architect call (Decision Log #4)**: AGENT→AGENT-only v1 (most of the 10 enum relations are not semantically valid for an agent pair, e.g. MEMBER_OF is agent→department); whitelist of two relations + null option presented to LLM; off-whitelist returns counted as parse-failure-tier rejections. `DreamingConfig` extended with 4 flat fields (`relationship_inference_enabled=True`, `relationship_inference_max_pairs_per_run=50`, `relationship_inference_max_per_entity=5`, `relationship_inference_min_confidence=0.6`) — default-True deviation from Wave-10 transitional-flag convention is intentional + same precedent as `notebook_consolidation_enabled` / `KnowledgeEdgesConfig` / `EdgeBackfillConfig`: step is gated downstream on `knowledge_edges` and `rejection_cache` being non-None — both wired by `_wire_relationship_inference` which depends on `knowledge_edges` already being adopted at `runtime.py:1615`. Out-of-box: enabled but inert until wirer runs and dependencies exist. `DreamReport` extended with 3 counter fields (`inferred_relationships`, `relationship_pairs_rejected`, `relationship_pairs_capped`) — defaults to 0 so partial-report construction in dream-cycle exception branch (line ~1499) does not need updating. `DreamingEngine` extended with two private attributes (`_knowledge_edges`/`_rejection_cache`, both initialized to `None` in `__init__`) and two late-bind setters (`set_knowledge_edges` / `set_rejection_cache`); Step 7i block in `dream_cycle` between Step 7h and Step 8 — counters declared at block top so DreamReport assembly reads them whether or not Step 7i ran; uses `getattr(self, "_knowledge_edges", None)` defensively (mirrors existing `_agent_wm` pattern) so legacy tests using `object.__new__(DreamingEngine)` to bypass `__init__` (e.g. `test_dream_step_7f.py`) keep working without scaffolding edits. Late-bound dependencies, LLM-client check, and non-empty `episodes` all gate the block; tier-2 log-and-degrade on any inference failure. New `_wire_relationship_inference(*, runtime, config) -> bool` async wirer in `startup/finalize.py` mirrors `_wire_edge_backfill` shape (async because `cache.start()` is awaited); 4 short-circuit guards (config disabled → False; `knowledge_edges` not wired → False + debug; `dreaming_engine` not wired → False + debug; cache `start()` fails → warning + False); on success creates `data/rejection_cache.sqlite`, sets `runtime.rejection_cache` public attribute, calls both setters via `hasattr` guards. Invoked from `finalize_startup` immediately after `_wire_edge_backfill`. Public `runtime.rejection_cache: Any = None` slot added at `runtime.py:430` next to `knowledge_edges`. `knowledge/__init__.py` exports `RejectionCacheStorage` + `SQLiteRejectionCache`. **What this AD does NOT change** (out of scope by design): no new EventType (telemetry via `DreamReport` counters and `logger.debug` only — defer EventType to AD-690b if signal value emerges); no HXI surface; no backfill or migration of existing `knowledge_edges` (AD-689 owns backfill); no federation sync (AD-693); no classification gating (`KnowledgeEdge.classification` left at default `None` for inferred edges; AD-692 commercial owns ORACLE/SECRET labeling); no new entity types (AGENT→AGENT only; episode `agent_ids` is the entity source); no widening of relation whitelist beyond `REPORTS_TO`/`DEPENDS_ON` (defer AD-690b/c); no retry on `add_edge` failure (tier-2 log-and-degrade; failed pairs are NOT added to rejection cache so they retry next cycle); no scheduling change (existing `DreamScheduler` already invokes `dream_cycle`); no LLM tier escalation (v1 is `tier="standard"` only). **Builder drift-fix during build**: initial dream-step block accessed `self._knowledge_edges` directly, which broke 10 tests in `test_dream_step_7f.py` because they use `object.__new__(DreamingEngine)` to bypass `__init__` and don't set the new private attributes; switched to `getattr(self, "_knowledge_edges", None)` defensive read (same idiom as the existing `_agent_wm` lookup in the same file). 12 focused tests pass at `tests/test_ad690_relationship_inference.py` (matches prompt's 10-floor by 2; over-floor by 2): `RelationshipInferenceResult` frozen + `to_dict` round-trip; `_extract_agent_pairs` dedupe + singleton-skip + 3-agent fan-out (6 agents → 4 unique pairs); existing edge skip-no-LLM-call; LLM happy path adds edge with all 4 provenance tags + correct relation/conf/weight; min-confidence filter records rejection with `below_threshold_0.40` reason; per-entity cap honored (10 partners, cap=5 → 5 inferred + 5 capped); max-pairs-per-run honored (50 candidates, cap=10 → exactly 10 LLM calls); rejection-cache replay-skip (run 1 records null rejection, run 2 short-circuits before LLM call → counters do not double-count); LLM JSON parse failure → cached with reason `llm_parse_failure`; off-whitelist relation (`member_of`) → cached with reason `relation_not_in_whitelist`; disabled config short-circuits (mirrors dream-engine guard, asserts LLM call count 0); `SQLiteRejectionCache` round-trip with `tmp_path` (start → record → was_rejected both directions True → unrelated pair False → stop). Phantom-API pre-check returns 1 candidate (`runtime.episodic.list_episodes(since=...)` in DLog #2 prose — explicitly noted in spec as the rejected captain draft; false positive). Full gate: 11028 passed, 15 skipped (delta +12 vs Wave 39 baseline 11016 — exact target hit). Pre-commit deletion sanity check: 6 modified files, all additions, 139 insertions / 0 deletions; new files `rejection_cache.py` + `relationship_inference.py` + `test_ad690_relationship_inference.py` untracked-and-tracked-fresh (no deletion risk). No hard-stops triggered: no widening to non-AGENT entity types (deferred AD-690b/c), no relation whitelist expansion (deferred), no EventType emission (deferred AD-690b), no federation sync (deferred AD-693), no scope creep, no phantoms in implementation (verify-first pre-check at draft time confirmed `KnowledgeEntityType.AGENT` + `KnowledgeRelationType.REPORTS_TO`/`DEPENDS_ON` + `KnowledgeEdge` 13-field frozen dc + `KnowledgeEdgeStorage` Protocol `add_edge`/`find_edges` shape + `EpisodicMemory.list_episodes` AD-689 surface + `Episode.agent_ids` field + `LLMRequest` tier/temperature/max_tokens kwargs + `extract_json` from `utils.json_extract` + `DreamingConfig` last field + `DreamReport` last field + Step 7h closing log line + Step 8 header line + `set_quality_router` setter precedent + `_reactive_cooldowns` last init line + `runtime.knowledge_edges` adoption ordering + `_wire_edge_backfill` async sibling shape + `_wire_causal_reasoner` invocation site — all 16 anchors verified at HEAD `b402fee`/Wave-39-baseline). Closes GH issue #384.

 Edge Population from Existing ProbOS Data (GH issue #383, Wave 39). Phase A foundation (fourth and last) of the Unified Knowledge Graph + Oracle Unification stack — closes the **write seam** by populating `runtime.knowledge_edges` from four already-existing data sources: ontology (REPORTS_TO + MEMBER_OF), Hebbian router (COMPETENT_IN above threshold), episodic memory (INVOLVED_IN), and DECISIONS.md cross-references (INFORMED_BY + RESOLVED_BY). Without this AD, Tier 6 (AD-688) and post-merge `_expand_via_graph` both return `[]` against an empty graph; this AD is what makes both meaningful in production. New `src/probos/knowledge/backfill.py` with `EdgeBackfillResult` frozen dataclass (per-source counts + `total` property + `to_dict()`), module-level `_deterministic_edge_id(src_type, src_id, rel, tgt_type, tgt_id) -> str` (SHA-256 of typed-triple key truncated to 32 hex chars — combined with AD-687 INSERT OR REPLACE upsert, idempotent re-runs leave row count unchanged without read-modify-write), `_make_edge` helper that bounds-clamps confidence/weight to [0.0, 1.0], and `EdgeBackfillService(*, knowledge_edges, ontology, hebbian_router, episodic_memory, decisions_paths, hebbian_threshold=0.5)` kwargs-only ctor. One method per source plus aggregator: `backfill_all() -> EdgeBackfillResult` (started_at + duration_ms timing); `backfill_ontology() -> int` (walks `get_all_assignments()` for member_of, builds `agent_by_post: dict[post_id, list[agent_type]]` index, walks `get_posts()` for reports_to chains via post → assignment(s) → agent_type resolution); `backfill_hebbian(*, threshold=None) -> int` (filters `all_weights_typed()` by `rel_type == REL_INTENT` AND `weight >= threshold`, emits `AGENT(target) COMPETENT_IN CAPABILITY(source)` since HebbianRouter REL_INTENT triples are `(intent_name, agent_id, "intent")`, confidence/weight = float(weight)); `backfill_episodes(*, limit=None) -> int` (walks new `EpisodicMemory.list_episodes` public API, emits `AGENT(agent_id) INVOLVED_IN INCIDENT(episode_id)` per agent, skips empty `agent_ids`); `backfill_decisions(*, paths=None) -> int` (parses every `### AD-NNN[a-z]?` section header in each provided markdown path, extracts `**Related:** ...` lines for INFORMED_BY edges via `r"AD-\d+[a-z]?"` regex with self-reference skip, scans body for `Closes (?:GH issue )?#NNN` markers (case-insensitive) for RESOLVED_BY edges as `DECISION:AD-N RESOLVED_BY INCIDENT:gh-N` — direction documented as architect call: source=DECISION, target=INCIDENT consistent with how relation was named in Wave 37 enum). All four backfills are tier-2 log-and-degrade — a missing/failing source contributes 0 to the count and never raises into `backfill_all()`. All edges constructed with `source_agent="edge_backfill"` + `source_duty="ontology"|"hebbian"|"episodes"|"decisions"` provenance tags. **Architect call (Decision Log #2)**: Captain spec mentioned `**Resolved by:** ...` markdown pattern but it does not exist as a structured marker in DECISIONS.md (only prose). Real marker is `Closes (?:GH issue )?#NNN`. Builder uses `Closes` as the RESOLVED_BY signal. **Architect call (Decision Log #3)**: only `REL_INTENT` weights become COMPETENT_IN edges; REL_AGENT/SOCIAL/BUILDER_VARIANT/STRATEGY out of scope v1 (would need different relations not in AD-687 enum). New public method `EpisodicMemory.list_episodes(*, limit=None) -> list[Episode]` inserted before existing `get_by_ids` in `cognitive/episodic.py` — returns episodes ordered by timestamp DESC, mirrors the existing `recent()` pattern (`:1869`), uses existing `_metadata_to_episode` reconstructor (`:2083`), `limit=None` returns all rows + non-negative int caps the slice, missing/malformed rows silently skipped. Backfill consumes this public API instead of reaching `_collection.get(...)` directly (Open/Closed; Demeter). New `EdgeBackfillConfig` Pydantic model (`enabled=True`, `run_on_warm_boot=True`, `hebbian_threshold=0.5` validated to [0.0, 1.0] via `field_validator`, `force=False`, `decisions_paths` default to 5 markdown paths: `DECISIONS.md` + 4 era archives) wired adjacent to `KnowledgeEdgesConfig` at `config.py`. Default-True deviation from Wave-10 transitional-flag convention is intentional — same precedent as `KnowledgeEdgesConfig`/`CognitiveJournalConfig`: warm-boot wirer is a no-op once the table has any rows (idempotency-by-row-count guard), so the first cold boot after AD-689 lands populates the graph and subsequent boots see `find_edges(limit=1) != []` and skip. Async wirer `_wire_edge_backfill(*, runtime, config) -> bool` at `startup/finalize.py` mirrors `_wire_self_distillation` shape; constructs the service, sets `runtime.edge_backfill = service` (Wave 5 conv #1 — public attribute, collision-free greenfield), checks `cfg.run_on_warm_boot` short-circuit, runs idempotency guard via `await knowledge_edges.find_edges(limit=1)` (non-empty → skip + INFO log; `cfg.force=True` overrides), finally awaits `service.backfill_all()` with INFO log of per-source counts + duration. Invoked from `finalize_startup` immediately after `_wire_chain_optimizer` (`finalize.py:580`); phase ordering verified — `runtime.knowledge_edges` adopted in communication phase BEFORE finalize, ontology/hebbian_router/episodic_memory all attached on runtime by then. Public `runtime.edge_backfill` slot added at `runtime.py:432` immediately after `self.knowledge_edges`. Knowledge package `__init__.py` exports `EdgeBackfillResult` and `EdgeBackfillService` alongside existing AD-687 names. Layer-boundary test exception added: `("knowledge/backfill.py", "probos.mesh.routing")` — `REL_INTENT` is a pure relation-type string constant for filtering Hebbian weights (no behavioral coupling). **What this AD does NOT change** (out of scope by design): no LLM-based NL-to-graph entity extraction (deferred AD-691, issue #385); no live event-driven incremental backfill (deferred AD-690, issue #384 — Dream Step 10 inference); no classification gating on backfill writes (deferred AD-692 commercial); no federation cross-instance edge sync (deferred AD-693 commercial); no HXI graph visualization; no shell command (`/graph backfill`) — use the wirer or invoke `runtime.edge_backfill.backfill_all()` directly; no retention/pruning policy on `knowledge_edges`; no new EventType (no audit emission v1; defer to AD-690 if needed); no mutation of existing AD-687 schema or AD-688 Oracle Tier 6. **Builder drift-fix during build**: Tests #11 + #12 (warm-boot wirer happy-paths) initially failed with assertion `35 == 2` because the wirer's default `decisions_paths` includes the real `DECISIONS.md` + era archive files which exist in the test cwd; fixed by passing `decisions_paths=[]` in the test `EdgeBackfillConfig`. No shipping-code change. 12 focused tests pass at `tests/test_ad689_edge_backfill.py` (over the prompt's 10-floor by 2): service shape + frozen-result + ctor surface; `backfill_ontology` emits 2 member_of + 1 reports_to with full provenance verified; `backfill_hebbian` filters below default 0.5 threshold (0.7 + 0.6 pass, 0.3 dropped, REL_AGENT skipped — capabilities sorted alphabetically); custom threshold parameter (0.7 drops both 0.4+0.6, 0.1 catches both); `backfill_episodes` emits per-agent (1 + 2 + 0 = 3 edges, ep-2 incident appears twice for alpha+beta); `backfill_decisions` parses `**Related:**` lines (3 cross-refs across 2 sections, sorted pairs verified); `backfill_decisions` parses `Closes #N` + `Closes GH issue #N` (2 RESOLVED_BY edges to gh-382/gh-383); `backfill_all` aggregation (3+1+1+2 = total 7, duration_ms ≥ 0); idempotency (2 invocations leave row count = 1, deterministic edge ID matches `_deterministic_edge_id(...)` helper output); wirer skip-when-rows-exist (pre-populated edge stays at 1 row after wirer runs); wirer runs-when-empty (1 member_of edge created from single assignment); wirer force=True override (pre-populated + force → 2 edges total). Full gate: 11015 passed, 15 skipped, 1 known xdist flake (`test_dreaming.py::test_nl_to_dream_cycle_changes_weights` — passes serial in 25.06s; same xdist concurrent-fixture-boot pattern documented in Waves 23/27/30/31/32/33 baselines; unrelated to this AD). Test-count delta: +12 vs Wave 38 baseline 11003 (exact target hit). No hard-stops triggered: no LLM extraction (deferred AD-691), no live event-driven backfill (deferred AD-690), no classification gating (deferred AD-692 commercial), no federation sync (deferred AD-693 commercial), no scope creep, no phantoms in implementation (verify-first pre-check at draft time confirmed `KnowledgeEdgeStorage.add_edge` INSERT OR REPLACE upsert + 8 entity types + 10 relations + ontology service public methods + `Post.reports_to`/`id`/`department_id` + `Assignment.agent_type`/`post_id` + `HebbianRouter.all_weights_typed()` + `REL_INTENT="intent"` + `Episode.id`/`agent_ids`/`timestamp` frozen dc + `_metadata_to_episode` reconstructor + `recent()` sibling pattern + `KnowledgeEdgesConfig` adjacent location + `_wire_self_distillation` async precedent + `_wire_chain_optimizer` sync precedent + `**Related:**` markdown anchors + `Closes (?:GH issue )?#NNN` markers + `### AD-NNN` section header pattern + 4 era archive files at workspace root + `runtime.knowledge_edges` adoption ordering — all 18 anchors verified live at HEAD `46fa2cd`/`bbc7fff`). Closes GH issue #383.

AD-688 v1 CLOSED. Oracle Graph Integration — Tier 6 (graph) + post-merge `_expand_via_graph` enrichment (GH issue #382, Wave 38). Phase A foundation (third of four) of the Unified Knowledge Graph + Oracle Unification stack — closes the **read seam** between AD-687 `KnowledgeEdgeStorage` and `OracleService`. Adds Tier 6 (`graph`) to `OracleService.query()` and a post-merge 1-hop graph expansion pass that enriches the top-K merged results from all tiers via the knowledge graph. New constructor kwarg `knowledge_graph: Any = None` (kwargs-only, default `None`); new public idempotent `attach_knowledge_graph(graph)` late-bind setter mirroring AD-686 `attach_semantic_layer` exactly. Wires `runtime._oracle_service.attach_knowledge_graph(runtime.knowledge_edges)` immediately after `comm.knowledge_edges` adoption at runtime.py (try/except + WARN log-and-degrade per AD-686 precedent). `"graph"` appended to default `active_tiers` list (now 6 entries: episodic/records/operational/archive/semantic/graph); existing callers passing explicit `tiers=` lists keep their narrowed behavior. **`OracleResult.provenance` is a top-level field, NOT a metadata key** — Tier 6 hits set `provenance="[knowledge graph]"`, expansion results set `provenance=f"[graph expansion: {parent.provenance}]"`, and parent provenance is mirrored into `metadata["expansion_source"]` for downstream filtering. Tier 6 entity-extraction strategy (v1): token-substring exact match — `_extract_entity_tokens(text)` lowercases + splits + strips trailing punctuation + drops <3 chars + drops ~50-token inline `_GRAPH_STOPWORDS` frozenset + dedupes preserving order + caps at 16 tokens. NOT named-entity recognition; AD-691 will add embedding-based fuzzy matching. Hop-proximity scoring: 1.0 direct (token matches edge `source_id` OR `target_id` via `find_edges`), 0.6 two-hop (one extra `traverse(max_hops=1)` step from each direct match's target). Score = `edge.weight × edge.confidence × hop_proximity`. Inline caps (NOT config): `_GRAPH_DIRECT_LIMIT=10` per direction per token, `_GRAPH_TRAVERSE_LIMIT=5` per direct match, `_GRAPH_EXPANSION_PER_PARENT=5` per top-K parent, `_GRAPH_EXPANSION_DISCOUNT=0.7`, `_GRAPH_MIN_TOKEN_LEN=3`. Dedupe by `edge.id` keeping highest score across both source/target directions and across direct/traverse merges via module-level `_record_graph_hit` helper. Empty graph or unattached graph → `[]` with `logger.debug` (mirrors AD-686 Tier-5 unattached path; never raises into `query()`). Post-merge expansion runs BEFORE the final sort/truncate so expansion results compete on score in the merged ranking; skips parents with `source_tier == "graph"` (no double-counting); reuses the same `_extract_entity_tokens` helper on `parent.content`; emits at most `_GRAPH_EXPANSION_PER_PARENT` results per parent; expansion-result score = `parent.score × 0.7 × edge.weight × edge.confidence`. Type narrowing via `Any` (mirrors AD-686 `_semantic_layer: Any`) to avoid the cognitive↔knowledge import cycle; the `KnowledgeEdgeStorage` Protocol contract is documented in method docstrings instead. **What this AD does NOT change** (out of scope by design): NL-to-graph LLM entity extraction → AD-691 (issue #385); edge population from existing data → AD-689 (issue #383); Dream Step 10 relationship inference → AD-690 (issue #384); classification enforcement on graph reads → AD-692 (issue #386, commercial); federation cross-instance edge sync → AD-693 (issue #387, commercial); HXI graph visualization → AD-690b or later; shell command (`/graph search`, `/graph traverse`) — once consumers exist; graph metrics in `oracle.stats()` — deferred (Oracle has no stats surface today). NO new EventType, NO new Pydantic config, NO new module — all edits land in `cognitive/oracle_service.py` + `runtime.py` + new test file. NO change to existing `OracleService` callers (`introspect/`, `organizer_agents.py`, `cmd_search`) — they pass explicit `tiers=["semantic"]` or rely on defaults; defaults grow but remain backward-compatible. 13 focused tests pass at `tests/test_ad688_oracle_graph_integration.py` (over the prompt's 10-floor by 3): `attach_knowledge_graph` late-bind + idempotency; `_query_graph` method shape (async + kwargs-only `k`); unattached → `[]`; one-hop direct match on `source_id` (provenance + score + metadata); one-hop direct match on `target_id` (symmetry); two-hop with proximity discount (1.0 vs 0.6 verified by separate edge IDs); dedupe by `edge.id` keeps highest score (self-loop A→A matched both source and target); default `active_tiers` includes `"graph"` (AsyncMock spy verifies `_query_graph` awaited once); expansion happy path (provenance prefix `"[graph expansion: "` + score = parent×0.7×w×c + `expansion_source`/`expansion_parent_tier` metadata); expansion skips graph parents (no double-counting); expansion respects `top_k` and per-parent cap (10 parents × 20 candidate edges with `top_k=3` → first parent absorbs `_GRAPH_EXPANSION_PER_PARENT` unique edges, remaining parents see `seen_edges`); runtime late-bind smoke (SimpleNamespace shape verifies attach block); token extractor (drops short/stopwords, strips punct, dedupes, caps at 16). **Builder drift-fix during build**: prompt's `_make_edge` test helper defaulted to `KnowledgeRelationType.COLLABORATED_WITH` which does NOT exist on the AD-687 enum (verified at HEAD `102728c`: 10 actual values are REPORTS_TO/MEMBER_OF/COMPETENT_IN/RESOLVED_BY/INVOLVED_IN/INFORMED_BY/DEPENDS_ON/PRODUCED_BY/CLASSIFIED_AS/ORIGINATED_ON). Default switched to `KnowledgeRelationType.MEMBER_OF`. Pure test-fixture defect; no shipping-code change. Phantom-API pre-check: only intro-not-yet-in-index FPs (`_query_graph`, `_expand_via_graph`, `attach_knowledge_graph`, `_extract_entity_tokens`, `_record_graph_hit`, `_GRAPH_*` constants — all introduced by Sections 1–4 of this prompt). Full gate: 11004 passed, 15 skipped (delta +13 vs Wave 37 baseline 10990 — exact target hit; 13 new tests). No hard-stops triggered: no NL-to-graph extraction (deferred AD-691), no edge population (deferred AD-689), no Dream Step 10 (deferred AD-690), no classification enforcement (deferred AD-692 commercial), no scope creep, no phantoms in implementation (verify-first pre-check at draft time confirmed `OracleService.__init__` kwargs-only ctor + `attach_semantic_layer` mirror + `query()` dispatcher + 5-tier default + `_query_semantic` shape + `OracleResult` frozen dc with separate `provenance: str` field + `KnowledgeEdgeStorage` Protocol + `KnowledgeEdge` fields + runtime adoption ordering — all 11 anchors verified at HEAD `ef34c85`/`102728c`). Closes GH issue #382.

AD-687 v1 CLOSED. Knowledge Edge Store — typed-triple foundation of the Unified Knowledge Graph (GH issue #381, Wave 37). Phase A foundation for the Oracle Unification stack (research: docs/research/unified-knowledge-graph.md §"Storage Layer: SQLite First, Kùzu Upgrade Path"). Greenfield introduces `KnowledgeEdge` typed-triple store at `src/probos/knowledge/edges.py` with full CRUD, recursive-CTE bounded traversal (1–3 hops, hard-capped at MAX_HOPS_CEILING=3, with cycle protection via accumulated `path` column + `instr()` exclusion), Pydantic config + startup wiring, and `runtime.knowledge_edges` public attribute (Wave 5 convention #1, no underscore; collision-free verified greenfield — 0 hits at HEAD `cc3cdd3`). Cloud-Ready via the **existing** `protocols.ConnectionFactory` / `DatabaseConnection` Protocol pair (mirrors `CognitiveJournal`/AD-431, `WorkforceMemoryStore`/AD-680 — REUSED, no new connection protocol). New module ships: 8-value `KnowledgeEntityType` str-Enum (AGENT/DEPARTMENT/INCIDENT/DECISION/DUTY/FINDING/CAPABILITY/STANDING_ORDER); 10-value `KnowledgeRelationType` str-Enum (REPORTS_TO/MEMBER_OF/COMPETENT_IN/RESOLVED_BY/INVOLVED_IN/INFORMED_BY/DEPENDS_ON/PRODUCED_BY/CLASSIFIED_AS/ORIGINATED_ON); `KnowledgeEdge` frozen dataclass (5 non-default + 8 defaulted fields per Python frozen-dataclass rule) with `__post_init__` validating confidence/weight in [0.0, 1.0] and classification in {private/department/ship/fleet} (mirrors `records_store._CLASSIFICATION_LEVELS` keys, no new taxonomy); `KnowledgeEdgeStorage` runtime-checkable Protocol (6-method async surface — Dependency Inversion seam for AD-688 Oracle Tier 6 / AD-689 backfill consumers); `SQLiteKnowledgeEdgeStore` concrete impl mirroring `CognitiveJournal` shape (`__init__(db_path, connection_factory)` → async `start()` runs `executescript(_SCHEMA)` + sets `aiosqlite.Row` row_factory → async `stop()` closes; idempotent `CREATE TABLE IF NOT EXISTS` + 4 indexes on source/target/relation/classification); `KnowledgeEdgeStore` public alias for default concrete store. All write methods are fire-and-forget semantically (NEVER raise to caller; sqlite3.Error → `logger.warning` + return False/[] tier-2 log-and-degrade); `update_edge` validates inputs at boundary (defense in depth, raises `ValueError` only on caller programming error pre-write). Recursive-CTE traversal: depth bound enforced via `WHERE walk.depth < ?`, cycle protection via path-string `instr()` exclusion of any target already in the accumulated `(type:id)>(type:id)` path, results grouped back into paths via `path` column rsplit on `>`, returned shortest-first. New `KnowledgeEdgesConfig` Pydantic model (`enabled=True`, `db_path="data/knowledge_edges.sqlite"`, `max_traverse_hops=3` with `field_validator` capping at [1,3]) — DEVIATES from Wave-10 transitional-flag default-False convention with rationale documented in config docstring (boot cost = one CREATE TABLE IF NOT EXISTS, runtime visibility zero until consumers arrive in AD-688/689/690; same precedent as `CognitiveJournalConfig`). Wired onto `SystemConfig.knowledge_edges` via `Field(default_factory=KnowledgeEdgesConfig)` immediately after `cognitive_journal` field. Startup wiring: `src/probos/startup/communication.py` constructs `SQLiteKnowledgeEdgeStore` immediately after Cognitive Journal block, calls `start()`, plumbs through `CommunicationResult.knowledge_edges` field (added next to `cognitive_journal` field; non-defaulted to match sibling shape — `SQLiteKnowledgeEdgeStore` lazy-imported under TYPE_CHECKING). Runtime adoption: `runtime.knowledge_edges: Any = None` slot added at runtime.py:425 next to `cognitive_journal` slot (Any to avoid circular import); adopted from `comm.knowledge_edges` at runtime.py:1608 next to `self.cognitive_journal = comm.cognitive_journal`. Knowledge package `__init__.py` exports the 6 new names alongside existing `KnowledgeStore`. **What this AD does NOT change** (out of scope by design — separate-issue scope, NOT v1 deferral): API endpoint (`/api/knowledge/edges/...`) → AD-688 (issue #382); Oracle Tier 6 graph integration → AD-688 (issue #382); Edge population from existing data (Hebbian/ontology/episodes) → AD-689 (issue #383); Dream Step 10 relationship inference → AD-690 (issue #384); Classification enforcement on read/write paths → AD-692 (issue #386, commercial); Federation cross-instance edge sync → AD-693 (issue #387, commercial); Kùzu migration tooling → AD-694 (issue #388, commercial); Pruning/retention policy on `knowledge_edges` (no shipped consumer in v1 → no growth signal yet); Shell command (`/edges`, `/graph`) — once consumers exist; HXI surface for graph visualisation → AD-690b or later. 12 focused tests pass at `tests/test_ad687_knowledge_edge_store.py` (matches Captain's "complete v1" floor of 10 with 2-test margin): schema/migration creates table + 4 indexes; add_edge happy path returns id and persists; get_edge round-trips ALL 13 fields including custom-id; find_edges by source_type+source_id (2 of 3 edges); find_edges by relation; update_edge advances updated_at + flips confidence/weight; delete_edge with idempotent second-delete returning False; traverse 1-hop returns 2 single-edge paths; traverse 2-hop with relation_filter excludes member_of branch (only reports_to in returned edges); traverse caps at MAX_HOPS_CEILING=3 even when caller passes max_hops=5 (5-link chain → max path length 3); cycle detection — A→B→A does not infinite-loop (exactly 1 path of length 1); confidence/weight/classification bounds validation in dataclass + `KnowledgeEdgeStorage` Protocol acceptance smoke (runtime_checkable). Phantom-API pre-check returns 0 candidates (clean — every prompt-asserted symbol either lives at HEAD or is introduced by Section 1). Full gate: 10990 passed, 15 skipped + 1 known-flaky timing test (`test_knowledge_store::test_auto_commit_after_debounce` — passes serial in 9.03s; xdist concurrent git-debounce noise documented under multiple wave baselines; unrelated to this AD). Test-count delta: +12 (exact match — 11 numeric delta plus the flaky test that flips state under parallel load). No hard-stops triggered: no consumer wiring (deferred AD-688/689/690), no API surface (deferred AD-688), no classification enforcement (deferred AD-692 commercial), no scope creep, no phantoms in implementation (verify-first pre-check at draft time confirmed `DatabaseConnection`/`ConnectionFactory` Protocol pair at protocols.py:186/223 + `SQLiteConnectionFactory.default_factory` at storage/sqlite_factory.py:28 + `CognitiveJournal` sibling shape at cognitive/journal.py:109 + `_CLASSIFICATION_LEVELS` keys at knowledge/records_store.py:27 + `cognitive_journal` startup wiring at startup/communication.py:307 + runtime slot+adoption at runtime.py:425/1608 + `CognitiveJournalConfig` sibling at config.py:1735 — all 8 prompt-asserted live symbols verified at HEAD `cc3cdd3`). Naming-collision check (Section 0) returned 0 hits — fully greenfield. **Builder drift-fix during build**: prompt's Section 3b referenced `CommunicationServices` dataclass with defaulted `cognitive_journal` field; live class is `CommunicationResult` (results.py:151) with non-defaulted `cognitive_journal: "CognitiveJournal | None"` — added `knowledge_edges: "SQLiteKnowledgeEdgeStore | None"` (no default) immediately after to preserve dataclass field-ordering rule (defaulted-after-non-defaulted) and added TYPE_CHECKING import for `SQLiteKnowledgeEdgeStore` at top of results.py. Closes GH issue #381.

 Oracle Absorbs SemanticKnowledgeLayer (Tier 5 + 3-Consumer Migration) (GH issue #380, Wave 36). Phase A foundation of Unified Knowledge Graph + Oracle Unification stack. Adds new Tier 5 (semantic) to OracleService — non-episode ChromaDB collections (agents/skills/workflows/qa_reports/events) now flow through the same OracleResult feed as episodic/records/operational/archive. New OracleService kwarg semantic_layer= (default None, backward-compat preserved); new public OracleService.attach_semantic_layer(layer) setter for late-bind from runtime structural-services phase (Oracle is built in cognitive phase BEFORE SemanticKnowledgeLayer); new private OracleService._query_semantic(query_text, *, k, types=None) -> list[OracleResult] mirrors the existing _query_archive/_query_operational tier-method shape (degrades to [] + DEBUG log when layer not attached); `semantic` appended to default active tiers; Tier-5 dispatch block inserted between Tier-4 archive and merge-and-sort. Score-coercion robust to None and string values via loat(r.get('score', 0.0) or 0.0). Runtime stitching: new public 
untime.oracle attribute (Wave 5 convention #1; same instance as legacy 
untime._oracle_service — backward-compat preserved for cognitive_agent.py:5164-5168 and routers/system.py:337); structural-services phase now late-binds the semantic layer onto Oracle via ttach_semantic_layer wrapped in try/except + WARN log-and-degrade. Three read-path consumers migrated to query through Oracle (with fall-back to direct layer when Oracle absent for test/legacy paths): (1) IntrospectionAgent._search_knowledge (introspect.py:761) — queries via Oracle, projects OracleResult back to legacy dict shape consumers expect; (2) NoteTakerAgent.perceive (organizer_agents.py:144) — implicitly fixes the pre-existing missing-wait bug on the async SemanticKnowledgeLayer.search() call by routing through Oracle's properly-awaited query path; (3) cmd_search (commands_knowledge.py:60) — /search shell command queries via Oracle while still calling layer.stats() for the panel render (stats migration deferred — no Oracle equivalent in v1). Hard limits (deferred): write-path migration (index_agent/index_skill/index_workflow/index_qa_report/index_event consumers in runtime.py and self_mod_manager.py) → AD-686b; removing 
untime._semantic_layer attribute → AD-686c (forcing function: zero remaining direct consumers — /search still uses layer.stats() for panel today); tier reordering / unifying tier name strings → AD-686d. **No SemanticKnowledgeLayer changes** (instance, write methods, stats(), lifecycle all unchanged). 11 focused tests pass at 	ests/test_ad686_oracle_semantic_tier.py (over the prompt's 7-floor by 4 — extras cover one-line-of-defence per consumer): _query_semantic method-shape (signature + ttach_semantic_layer callable + idempotent + accepts None); happy-path with stub layer (3 mixed-type results — agents/skills/workflows — normalised to OracleResult with source_tier=semantic, provenance=[semantic: <type>], metadata flattened); none-layer returns empty + DEBUG log captured; types/limit passthrough (forwarded as-is to layer.search); score coercion (None → 0.0, '0.7' → 0.7); semantic in default active tiers (invoked exactly once on tiers=None); attach late-bind (None → stub then await query returns semantic results); IntrospectionAgent._search_knowledge uses Oracle (assert_awaited_once with 	iers=['semantic'], projected dict shape carries type/id/document/score/metadata); NoteTakerAgent regression on missing-wait bug (oracle.query awaited, fetched_content contains 'Search results for'); cmd_search uses Oracle + keeps stats panel (oracle.query awaited with 	iers=['semantic'], layer.stats called once); end-to-end normalised merge (sort descending by score, semantic entries have source_tier=semantic + provenance=[semantic:...). Phantom-API pre-check clean (0 candidates). Full gate: 10978 passed, 15 skipped, 1 known xdist-worker flake (	est_ward_room::test_browse_threads_sort_recent — passes serial in 7.27s; same xdist concurrent-fixture-boot pattern documented under multiple wave baselines; unrelated to this AD). Test-count delta: +10 vs pre-build baseline 10968 (11 new tests minus 1 baseline drift). No hard-stops triggered: no write-path migration (deferred AD-686b), no 
untime._semantic_layer removal (deferred AD-686c), no tier reordering (deferred AD-686d), no scope creep, no phantoms in implementation (verify-first pre-check confirmed OracleService kwargs-only ctor + _query_archive sibling shape + SemanticKnowledgeLayer.search async signature + dict-result shape + 3 consumer call sites all live at HEAD f418f3). Closes GH issue #380.

AD-661 v1 CLOSED. Full Diagnostic Context for Agent Self-Improvement — PULL-BASED ASSEMBLY ONLY (GH issue #320, Wave 33). Meta-Harness research finding (Lee et al., arXiv:2603.28052): 10M tokens of raw diagnostic history beat compressed summaries — more raw context beats compressed summaries.' v1 ships a **read-only token-budgeted aggregator** over already-shipped surfaces (AD-658 chain traces + AD-657 procedure trace exemplars + EpisodicMemory.get_by_ids). NO automatic invocation, NO continuous diagnostic stream, NO semantic search, NO summary fallback, NO LLM calls, NO push-based notifications, NO AD-434 Ship's Records consumption (deferred AD-661b), NO retroactive backfill, NO bundle persistence, NO cross-bundle dedup, NO scoring/ranking beyond list_active' natural order, NO agent tier/department filtering (deferred AD-661f), NO remainder redistribution between under-filled sections (deferred AD-661c), NO HXI surface. New `src/probos/cognitive/diagnostic_context.py` with `DiagnosticBundle` frozen dataclass (query first non-default; chain_traces/procedures/episodes default_factory list, total_estimated_tokens int, truncated bool; `to_dict()` returns deep-copied list fields), three module-level helpers (`_estimate_tokens(text, *, chars_per_token=4)` heuristic `len(text) // 4` matching `agent_working_memory.py:35` precedent — no tiktoken dep; `_extract_keywords(query, *, min_len=3)` whitespace-split + lowercase + min-len drop; `_matches(text, keywords)` case-insensitive substring with empty-keywords-include-all semantics), and `DiagnosticContextService` (ctor `(runtime, *, default_budget_tokens=8000, chain_trace_ratio=0.4, procedure_ratio=0.3, episode_ratio=0.3, chars_per_token=4)` mirrors AD-659 ChainOptimizer / AD-660 CausalReasoner sibling shape). Single public async method `assemble(*, query, budget_tokens=None, agent_id=None, since=None) -> DiagnosticBundle` — never raises (each collector wrapped in try/except → `logger.warning(AD-661: ...', exc_info=True)` tier-2 log-and-degrade; failure in one section yields empty section, not failed bundle). Allocation: 40/30/30 split via section budgets `int(budget * ratio)` with episode section absorbing int-truncation remainder. Three private collectors: `_collect_chain_traces` (calls `runtime.cognitive_journal.get_recent_chain_traces(limit=200, agent_id, since=since.timestamp())`, keyword-matches across `step_name`+`sub_task_type`+`intent`+`error_truncated`+`communication_context` joined haystack, budget-clips with `truncated=True` flag); `_collect_procedures` (walks `runtime.procedure_store.list_active()` summaries → `store.get(id)` for full Procedure → resolves `trace_exemplars` via `runtime.episodic_memory.get_by_ids()`, builds entry dict with deduped `exemplar_episodes` inline + cross-procedure `exemplar_index` for episode section); `_collect_episodes` (deduped exemplars across all in-bundle procedures — explicitly NOT calling `EpisodicMemory.recall()` which would be semantic search). Token cost via `_row_tokens(row)` serializing values to single string then `_estimate_tokens`. New `DiagnosticContextConfig` Pydantic model (`enabled: bool = True` — DEVIATION from Wave-10 transitional-flag convention, documented in config docstring + prompt body: v1 is read-only aggregator with no automatic invocation, invisible at runtime until caller invokes `assemble()`) with `default_budget_tokens=8000`, three ratio fields validated by `@field_validator` (each in [0.0, 1.0]) + `@model_validator(mode='after')` (sum 1.0 ±0.01 tolerance). Wired onto `SystemConfig.diagnostic_context` via `Field(default_factory=DiagnosticContextConfig)` immediately after `causal_reasoning` field. Sync `_wire_diagnostic_context(*, runtime, config) -> bool` at startup/finalize.py mirrors `_wire_chain_optimizer`/`_wire_causal_reasoner` shape exactly; invoked from `finalize_startup` immediately after `_wire_causal_reasoner`. Public attribute `runtime.diagnostic_context_service` (Wave 5 convention #1). New `src/probos/routers/diagnostic_context.py` with `GET /api/diagnostic-context?query=&budget=&agent_id=&since=` (budget hard-capped at 32000 via `min(max(budget, 1), 32000)`; HTTPException 503 when `runtime.diagnostic_context_service is None`; `since: float | None` Unix-timestamp → `datetime.fromtimestamp(tz=timezone.utc)` before passing to service). Registered in `api.py` router-tuple alphabetically appended to import + for-loop tuples (twin-block SEARCH/REPLACE bundled into single combined block per Wave 31 pattern). 8 focused tests pass at `tests/test_ad661_diagnostic_context.py` (over 7 floor by 1): bundle frozen mutation raises + `to_dict` deep-copy mutability isolation; keyword extractor min-len drop + matcher empty-kw + None-haystack + estimator boundary + `CHARS_PER_TOKEN` constant; chain_trace keyword filter (cpu sub_task_type matches c1/c3, REPORT excluded) + budget clip (40% of 2000 = 800 chain budget < c3's 6000-char error_truncated → `truncated=True`); procedure exemplar resolution via `get_by_ids` (single call with full id list, episodes deduped flat list ep_a + ep_b); budget truncation flag (50 rows × ~200 tokens >> 400-token chain budget → `truncated=True` + `len < 50`); episode dedup across multiple procedures (ep_shared appears once, ep_a + ep_b each appear once); collector failure log-and-degrade (RuntimeError from `get_recent_chain_traces` → empty sections + caplog WARNING contains `AD-661`); API endpoint 503 when service None + 200 happy path with TestClient + dependency_overrides + asserted `assemble` kwargs (query/budget_tokens/agent_id). Full gate: 10950 passed, 15 skipped, 1 known xdist-worker flake (`test_episode_clustering::test_last_clusters_property` — passes serial in 7.68s; same xdist concurrent-fixture-boot pattern documented in Waves 23/27/30/31/32 baselines). Test-count delta: +8 vs Wave 32 baseline 10942 (exact target hit). Baseline collected count drift: 10958 → 10966 (+8 exact). No hard-stops triggered: no automatic invocation (deferred AD-661b/d), no semantic search (deferred AD-661d), no LLM summary (deferred AD-661e), no AD-434 integration (deferred AD-661b), no scope creep, no phantoms in implementation (verify-first pre-check at draft time confirmed all 11 prompt-asserted symbols live at HEAD `4787c9d`: `CognitiveJournal.get_recent_chain_traces`, `Procedure.trace_exemplars`, `EpisodicMemory.get_by_ids`, `ProcedureStore.list_active`/`get`, `CHARS_PER_TOKEN` precedent, `_wire_chain_optimizer` sibling shape, api.py router-tuple twin-block, `runtime.records_store` referenced not consumed). Closes GH issue #320.

AD-660 v1 CLOSED. Agent Causal Reasoning Framework — TEMPLATE + JOURNAL + INTEGRATION POINT (GH issue #319, Wave 32). Lee et al. Meta-Harness proposer four-step protocol (arXiv:2603.28052) operationalized as a structured metacognitive artifact agents fill when an unexpected outcome triggers analysis. v1 ships **template + storage + one opt-in integration point** ONLY — there is NO causal-inference engine, NO automatic invocation, NO hypothesis ranking, NO diagnostic-action execution, NO emergence-event wiring. The LLM fills the four steps (`what_changed`, `confounded_variables`, `testable_hypotheses`, `diagnostic_actions` + self-reported `confidence`); ProbOS persists. New `src/probos/cognitive/causal_reasoning.py` with `CausalReasoningTemplate` **frozen** dataclass (10 fields: `template_id`, `agent_id`, `triggered_at`, `trigger_summary`, four list[str] step fields capped at 8 items × 500 chars each, `confidence` 0.0–1.0, optional `source_event_ref` correlation token; `to_dict()` projection ISO-formats datetime), `_coerce_list`/`_coerce_confidence`/`_empty_template` helpers, `CausalReasoner` service (`__init__(runtime, *, max_tokens=700, tier="standard")` mirrors AD-659 sibling shape; two never-raises async methods: `analyze(*, trigger, agent_id, context=None, source_event_ref=None) -> CausalReasoningTemplate` calls `runtime.llm_client.complete(LLMRequest)` with structured system prompt + JSON-only user prompt, parses via `extract_json`, degrades to empty-template on missing client / LLM exception / parse failure; `analyze_concern(concern_data) -> CausalReasoningTemplate | None` convenience wrapper that returns None on missing `agent_id`, otherwise builds an AD-504-shaped trigger summary from `agent_callsign`/`zone`/`similarity_ratio`/`velocity_ratio` and source_event_ref `self_monitoring_concern:{agent_id}`). Journal extended (mirroring AD-658 chain_traces pattern exactly): added `import json`, new `_SCHEMA_CAUSAL_TEMPLATES` table (10-column, 2 indexes on `triggered_at`+`agent_id`) provisioned via second `executescript` in `start()`, new `record_causal_template(template)` (INSERT OR IGNORE on PK `template_id`, JSON-serializes 4 list fields, datetime → epoch float via `.timestamp()`, fire-and-forget never raises), new `get_recent_causal_templates(*, limit=50, agent_id=None, since=None)` (most-recent-first ORDER BY triggered_at DESC, optional filters, JSON-decodes list fields back to list[str] on read, returns [] on no-db / query failure), `prune(...)` extended with both age-based and row-cap branches for `causal_templates`. New `CausalReasoningConfig(enabled=False, max_tokens=700, tier="standard")` Pydantic model (default-False per Wave 10 transitional-flag lesson) wired onto `SystemConfig.causal_reasoning` immediately after `chain_optimizer` field. Sync `_wire_causal_reasoner(*, runtime, config) -> bool` at startup/finalize.py mirrors `_wire_chain_optimizer` shape exactly; invoked from `finalize_startup` immediately after `_wire_chain_optimizer`. Public attribute `runtime.causal_reasoner` (Wave 5 convention #1). Integration POINT: ONE guarded block in `counselor._on_self_monitoring_concern` after `_save_profile_and_assessment` — uses existing `self._runtime` (CognitiveAgent property) to read `runtime.causal_reasoner` and existing `self._cognitive_journal` property (cognitive_agent.py:265 — proxies to `runtime.cognitive_journal`); fire-and-forget try/except → `logger.debug` with "AD-660" tag (must NOT raise into existing counselor flow). With default config, `runtime.causal_reasoner` is not constructed and the hook is a no-op (convention #14 — invisible at runtime on first commit). **What this AD does NOT change**: no causal-inference engine; no automatic invocation across other concern paths; no AD-557 emergence-event wiring (deferred AD-660b); no hypothesis ranking / no scoring / no de-dup (deferred AD-660b); no diagnostic-action execution (deferred AD-660c); no new EventType; no `/api/causal-templates` router (deferred AD-660b); no HXI surface; no ChromaDB / no Git knowledge store; no retroactive backfill; no chain-trace co-correlation; no counselor logic changes (hook runs strictly as side-channel after profile save); no ChainOptimizer integration (deferred AD-660d). **Builder drift-fix**: prompt's hook used `getattr(self._runtime, "causal_reasoner", None)` and `getattr(self._runtime, "cognitive_journal", None)` but Counselor is a CognitiveAgent subclass that already has `_cognitive_journal` as a `@property` (cognitive_agent.py:265) — direct field shadowing failed `AttributeError: property has no setter` across 235 tests. Reverted counselor field/initialize-kwarg additions; hook now uses `self._runtime` (existing CognitiveAgent attribute) for reasoner lookup and `self._cognitive_journal` (existing property) for journal access — no counselor surface change beyond the single hook insertion. 8 focused tests pass at `tests/test_ad660_causal_reasoning.py` (matches prompt target — exceeds 7 floor by 1): frozen-dataclass mutation raises + `to_dict` ISO datetime round-trip; happy-path with synthetic LLM JSON (5 fields populated, confidence=0.6, complete awaited once); degraded-path with non-JSON LLM content (empty lists + confidence=0.0); journal record + retrieve round-trip with real `CognitiveJournal` against tmp_path (10 fields verified); journal `agent_id` filter (3 inserts × 2 agents → only 2 rows for "a1"); `analyze_concern` returns None on missing agent_id without invoking LLM; wirer skips when `enabled=False` (no `runtime.causal_reasoner` attribute set); wirer creates `runtime.causal_reasoner` instance when enabled + smoke `analyze_concern` returns degraded template on `{}` LLM response. Full gate: 10942 passed, 15 skipped, 1 known-flake (knowledge_store auto_commit, pre-existing baseline noise). Test-count delta: +9 vs Wave 31 baseline 10933 (+8 new + 1 dreaming flake flipped to pass — net +8 owned by AD-660). No hard-stops triggered: no inference engine (deferred AD-660b/c), no automatic invocation (deferred AD-660b), no action execution (deferred AD-660c), no scope creep, no phantoms in implementation (verify-first pre-check at build time confirmed `SELF_MONITORING_CONCERN`/`_on_self_monitoring_concern`/`_SCHEMA_CHAIN_TRACES`/`record_chain_trace`/`_wire_chain_optimizer`/`ChainOptimizerConfig`/`extract_json`/`LLMRequest`/`runtime.cognitive_journal`/`runtime.llm_client`/`CognitiveAgent._cognitive_journal` property — all 11 prompt-asserted symbols verified live at HEAD; phantom flagged late: prompt assumed counselor had `self._runtime` field directly but verified at build that CognitiveAgent base provides it; the pre-check spec did not surface the existing `_cognitive_journal` property collision until xdist gate caught it). Closes GH issue #319.

AD-659 v1 CLOSED. Cognitive Chain Self-Optimization Loop — ANALYSIS ONLY (GH issue #318, Wave 31). Meta-Harness outer-loop research (Lee et al., arXiv:2603.28052) — propose, evaluate, store, iterate. v1 ships **propose** + Captain approval surface ONLY; **evaluate** (A/B framework) and **iterate** (apply) are explicitly deferred. No automated prompt rewriting, no automatic parameter adjustment, no live mutation of ChainTuningConfig. New `src/probos/cognitive/chain_optimizer.py` with `OptimizationProposal` mutable dataclass (mutable to support post-construction `decide()` setting `decision`/`decided_at`/`decided_by`; `to_dict()` projection), `ChainOptimizer` service (ctor `(runtime, *, analysis_window=100, latency_p95_ms_floor=10000.0, success_rate_floor=0.7, error_rate_ceiling=0.3, min_samples_per_group=20, emit_event=None)` mirrors AD-508 sibling shape), and three pure detector functions: `detect_latency_p95_regression` (group by `(step_name, tier)`; p95 over floor proposes tier shift `standard`→`fast` or `deep`→`standard`; medium risk), `detect_success_rate_floor_breach` (group by `(sub_task_type, chain_trust_band)`; below-floor proposes nudging `chain_tuning.low_trust_ceiling` or `high_trust_floor` by +0.05; mid/unknown bands skipped — no single-knob fix; medium risk), `detect_high_error_rate_by_chain_source` (group by `chain_source`; above-ceiling proposes `chain_source.review[X]` observation-only flag; low risk). Public service API: `analyze(window=None)` reads `runtime.cognitive_journal.get_recent_chain_traces(limit=n)` (AD-658 surface), runs all three detectors, appends to `pending_proposals` (in-memory; no dedup, no persistence — deferred AD-659b); `list_pending()` returns undecided slice; `decide(proposal_id, decision, *, actor="captain")` records approve/reject (raises ValueError on bad decision); `apply_proposal(...)` raises `NotImplementedError("v1 analysis-only; apply deferred to AD-659b")` — v1 hard limit. Detectors are pure (`list[dict]` + kwarg thresholds → `list[OptimizationProposal]`, no I/O, no runtime access). Adjustable-vs-derived modulation surface honored: v1 proposes only against `ChainTuningConfig.low_trust_ceiling`/`high_trust_floor` (config.py:331–332, tunable); `_chain_trust_band`/`_trust_score`/`_communication_context`/`_boot_camp_active` are observation-derived (cognitive_agent.py:2061/2076–2085/2066–2068) and explicitly out of scope for v1. New `ChainOptimizerConfig(enabled=False)` Pydantic model wired onto `SystemConfig.chain_optimizer` (default-False per Wave 10 transitional-flag lesson; Captain opts in via YAML once AD-658 baseline data accumulated). Sync `_wire_chain_optimizer(*, runtime, config) -> bool` at startup/finalize.py mirrors `_wire_duty_scope_provider` shape exactly; invoked from `finalize_startup` immediately after `_wire_duty_scope_provider`. Public attribute `runtime.chain_optimizer` (Wave 5 convention #1). New `src/probos/routers/chain_optimizer.py` with `GET /api/chain-optimizer/proposals?include_decided=False` (returns `{proposals, enabled}`; `enabled=False` when wirer not active) and `POST /api/chain-optimizer/proposals/{id}/decide` body `{decision: "approve"|"reject", actor="captain"}` returning `{status, applied: False, proposal}` — explicit `"applied": False` v1 limitation surfaced in API response; mirrors `routers/build.py:150–177` build-queue approval REST shape; HTTPException 503 when optimizer disabled, 400 on bad decision, 404 on missing id. Registered in `api.py` router-tuple alphabetically between `chain_traces` and `counselor` (twin-block import + for-loop tuple combined into single SEARCH/REPLACE). Privacy: no chain-trace contents in event payload (v1 emits no events; emission deferred to AD-659b). **What this AD does NOT change**: no `apply_proposal()` implementation (deferred AD-659b); no proposal persistence (in-memory; restart clears queue); no A/B framework execution; no automatic prompt rewriting; no Counselor regression watchdog (deferred AD-659c); no new EventType; no periodic background analyze loop (Captain triggers manually); no proposal de-duplication (re-running `analyze()` over same window appends fresh batch; AD-659b dedups on `(detector_name, target_parameter)`); no rollback (no apply path to roll back); no AD-658 schema changes; no `ChainTuningConfig` mutation (v1 PROPOSES adjustments; mutating live config is the apply path AD-659b). 8 focused tests pass at `tests/test_ad659_chain_self_optimization.py` (matches prompt target — exceeds 7 floor by 1): 5 detector-shape (3 happy-path + 1 below-min-samples branch + 1 mid-band no-proposal branch); 1 service aggregation+decide (all three detectors triggered via 75-trace fixture, list_pending undecided slice, decide flips fields); 1 apply_proposal NotImplementedError matches "AD-659b"; 1 API end-to-end via FastAPI TestClient with stub runtime (GET pending, POST decide returns `applied: False`, GET pending now empty, GET include_decided still returns it, POST garbage decision → 400, POST missing id → 404). Full gate: 10935 passed, 15 skipped (delta +8 vs Wave 30 baseline 10927 — exact target hit). No hard-stops triggered: no apply path (deferred AD-659b), no A/B execution (deferred AD-659b), no Counselor wiring (deferred AD-659c), no scope creep, no phantoms in implementation (verify-first pre-check at draft time confirmed `ChainExecutionTrace`/`get_recent_chain_traces`/`ChainTuningConfig.low_trust_ceiling+high_trust_floor`/`_wire_duty_scope_provider`/`runtime.cognitive_journal`/`runtime.emit_event`/build-queue approval shape — all 7 prompt-asserted symbols verified live at HEAD `9b3abc7`). Drift-fix during build: prompt's first detector test fixture had p95 idx=22 landing in low-band slice (sorted index 22 of 25 with 23 ones+2 twelves → 1000ms not 12000ms); fixed by uniform 12000ms across all 25 traces in both Test #1 and the service-aggregation latency block. Closes GH issue #318.

AD-683 v1 CLOSED. Ship State Snapshot for Cold-Start Onboarding (GH issue #313, Wave 29). Meta-Harness research (Lee et al., arXiv:2603.28052) showed environment bootstrapping eliminates 2-4 exploratory turns in agentic coding; ProbOS equivalent injects one structured snapshot into the cold-start agent's first DM user-message. New `src/probos/onboarding/` package with `ship_state_snapshot.py`: three frozen dataclasses (`DepartmentSummary`, `WardRoomTopicSummary`, `ShipStateSnapshot` — `captured_at` first non-defaulted field per standing convention, `to_dict()`/`render_text()` projections) and `ShipStateSnapshotBuilder` service (ctor `(runtime, *, emit_event=None)` mirrors AD-508 `DutyScopeProvider` shape; single public `async build() -> ShipStateSnapshot`). Each per-source collector wrapped in try/except → `logger.warning` + degraded default (tier-2 log-and-degrade); `build()` never raises. Hard caps inline (NOT exposed): `_MAX_OPEN_WORK_ITEMS=5`, `_MAX_TOPIC_CHANNELS=3`, `_MAX_THREAD_TITLES_PER_CHANNEL=3`, `_TITLE_TRUNCATE_CHARS=80`. Collectors: `_collect_vessel` reads `ontology.get_vessel_identity()`/`get_vessel_state()`; `_collect_departments` reads `ontology.get_departments()` + derives crew counts via `get_crew_agent_types()`/`get_assignment_for_agent()`/`get_post_for_agent()`; `_collect_work_items` calls `work_item_store.list_work_items(status="open", limit=5)`; `_collect_ward_room_topics` calls `ward_room.list_channels()` + per-channel `list_threads(channel.id, limit=3, sort="recent")`. New `ShipStateSnapshotConfig(enabled=True)` Pydantic model wired onto `SystemConfig.ship_state_snapshot` via `Field(default_factory=ShipStateSnapshotConfig)`. Sync `_wire_ship_state_snapshot(*, runtime, config) -> bool` at startup/finalize.py mirrors `_wire_boot_camp_tracker` shape; invoked from `finalize_startup` immediately after `_wire_boot_camp_tracker`. Public attribute `runtime.ship_state_snapshot` IS the builder service (no third-state cache; mirrors AD-508 naming). `BootCampCoordinator.__init__` extended with optional kwarg `ship_state_builder: Any | None = None` (default preserves backward-compat for all existing AD-638 tests/callers; verified by `test_boot_camp_coordinator_backward_compat_and_wirer`); new public attribute `coord.ship_state_snapshot: ShipStateSnapshot | None = None`. At end of `BootCampCoordinator.activate(...)` (after existing `BOOT_CAMP_ACTIVATED` emit) — if `_ship_state_builder is not None`, await `build()` and assign to `self.ship_state_snapshot`; wrapped in try/except → log-and-degrade. `runtime.py:1580` BootCampCoordinator construction passes `ship_state_builder=getattr(self, "ship_state_snapshot", None)` (defends against startup-ordering inversion since wirer runs in `finalize_startup` after `start()`-time construction; `getattr(...None)` makes either ordering correct). DM-path render in `cognitive_agent._build_user_message` — new block at top of `parts` list (BEFORE temporal-awareness) gated on `observation.get("_boot_camp_active") and self._runtime is not None`; reads `runtime.boot_camp.ship_state_snapshot`; calls `render_text()` (debug-log + skip on render failure — tier-1 swallow since user message must still be sent); prepends `--- Ship State Snapshot ---` / `<text>` / `---` / blank line. WR-path and chain-path injection explicitly out-of-scope (AD-683e). 1 new EventType `SHIP_STATE_SNAPSHOT_CAPTURED` (verified collision-free against events.py post-Wave-28). Event payload contains COUNTS only (`captured_at`, `alert_condition`, `work_item_count`, `dept_count`, `topic_count`) — privacy-conservative per AD-530 / AD-478 pattern; directly tested at `test_builder_happy_path_full_data` ("no titles, no department names, no thread bodies in payload"). v1 ships builder + capture-at-activation + DM-path render only per convention #14 aggressive pre-deferral. **Deferred**: AD-683b (per-agent personalization), AD-683c (snapshot deltas/refreshes — forcing function: AD-683 ships and Captain reviews captured snapshots; if staleness observed, externalize refresh policy), AD-683d (federation sync), AD-683e (chain-path ANALYZE/EVALUATE/COMPOSE injection), AD-683f (Ward Room post-path injection), AD-683g (persistence to ChromaDB/disk/journal). **No `BOOT_CAMP_ACTIVATED` payload changes**, no AD-509 phase tracker integration, no AD-477 PlanOfDayService refactor (same data sources, different consumer; no shared aggregator extraction in v1). 8 focused tests pass at `tests/test_ad683_ship_state_snapshot.py` (matches prompt target — exceeds 6-floor by 2): EventType literal value; frozen dataclass + field-order (`captured_at` first) + `to_dict` round-trip with nested DepartmentSummary/WardRoomTopicSummary; `render_text` includes vessel/alert/uptime int-cast/crew/work-item title/topic line; builder happy-path full data with `_make_runtime_full()` SimpleNamespace fixture (Enterprise/YELLOW/42.0/7 + 2 departments with crew counts derived from assignments + 3 work items + 2 channels × 2 threads) plus emit_event privacy assertion (no titles, no dept names, no thread bodies); collectors-missing degraded path (no ontology/work_item_store/ward_room → all defaults, no exception); collectors-raise degraded path (`RuntimeError("boom")` from each collector → all defaults + caplog WARNING contains "AD-683"); capture-in-activate (ordering: `_active is True` BEFORE snapshot captured, `coord.ship_state_snapshot` populated `ShipStateSnapshot` instance with vessel_name "Enterprise"); combined backward-compat-no-kwarg + wirer enabled/disabled (3 sub-assertions). Full gate: 10920 passed, 15 skipped (delta +8 vs Wave 28 baseline 10912 — exact target hit). No hard-stops triggered: no per-agent personalization (deferred AD-683b), no deltas/refreshes (deferred AD-683c), no federation sync (deferred AD-683d), no chain-path injection (deferred AD-683e), no scope creep, no phantoms in implementation (verify-first pre-check at build time confirmed `BootCampCoordinator` ctor + `runtime.boot_camp` attribute + ontology API methods + `work_item_store.list_work_items` signature + `ward_room.list_channels`/`list_threads` signatures + `_boot_camp_active` observation flag set sites + `_build_user_message` DM branch entry + `_wire_boot_camp_tracker` sibling shape + `BOOT_CAMP_ACTIVATED` event entry — all 9 prompt-asserted symbols verified live at HEAD `b76b8a8`). `SHIP_STATE_SNAPSHOT_CAPTURED` newly registered. Closes GH issue #313.

AD-658 v1 CLOSED. Cognitive Chain Harness Metrics (GH issue #317). Measurement substrate for AD-659 chain optimization (Meta-Harness research, Lee et al., arXiv:2603.28052 — "the cognitive chain IS a harness; you can't optimize what you can't measure"). New `src/probos/cognitive/chain_trace.py` with `ChainExecutionTrace` frozen dataclass (24 fields: chain/step identity, caller identity, wall-clock+execution, context composition breakdown, modulation snapshot from AD-649/AD-639/AD-638) + `to_dict()` projection. New `_SCHEMA_CHAIN_TRACES` table on `CognitiveJournal` provisioned via `CREATE TABLE IF NOT EXISTS` + 3 indexes (started_at, agent_id, chain_id) at `start()`. Two new fire-and-forget never-raises methods on `CognitiveJournal`: `record_chain_trace(trace)` (INSERT OR IGNORE on `(chain_id, step_index)` write-once PK; mirrors existing `record(...)` shape) and `get_recent_chain_traces(*, limit=50, agent_id=None, since=None) -> list[dict]` (mirrors `get_reasoning_chain` shape). `prune(...)` extended to also age-based DELETE on `chain_traces` (`started_at < cutoff`) and row-count-cap with rowid ordering (PK is composite `(chain_id, step_index)`, not single `id`). New `chain_source: str = ""` kwarg on `SubTaskExecutor._execute_single_step` plumbed from `chain.source` through both `_execute_steps` call sites (single-step branch + parallel-wave list comp). New wall-clock `step_started_at = time.time()` captured next to existing monotonic `step_start`. Per-step trace-emission block inserted between existing `journal.record(...)` block and the abort-on-required-failure raise; wrapped in try/except (telemetry must never break the chain — Convention #15 swallow tier); `hasattr(journal, "record_chain_trace")` guard for non-`CognitiveJournal` mocks. Modulation params read from full `context` (the observation), NOT `step_context` (the filtered view) — they are properties of the run, not the step's prompt-context budget. New `src/probos/routers/chain_traces.py` with `GET /api/chain-traces` (limit hard-capped at 500, optional `agent_id`/`since` filters); registered in `api.py` router-tuple alphabetically between `chat` and `counselor`. **What this AD does NOT change**: no output quality signals beyond `success` (deferred AD-659); no input/output token split (defer AD-658a); no handler signature changes; no new EventType (DB-only emission); no WebSocket fan-out; no retroactive backfill; no sampling. **AD-652 dependency satisfied as-is** (design principle adopted in DECISIONS.md:1914; realised across AD-649/AD-639/AD-638 — no wiring changes). 8 focused tests pass at `tests/test_ad658_chain_harness_metrics.py` (matches prompt target): dataclass defaults + `to_dict` round-trip; frozen-mutation raises FrozenInstanceError; real-`CognitiveJournal`-against-tmp-path round-trip with all 24 fields; filter+ordering (3 traces, agent_id filter, since filter, limit cap); no-db short-circuit returns None/[]; executor emits trace per step with full modulation snapshot (`MagicMock` journal); context-composition breakdown records ANALYZE-with-filter (declared=2, passed=3 with `_internal` underscore-passthrough, filter_applied=True) and QUERY-without-filter (passed=4, filter_applied=False); trace emission failure (`AsyncMock(side_effect=Exception)`) does not break chain (results returned, no raise). Full gate: 10912 passed, 15 skipped (delta +8 vs Wave 27 baseline 10904 — exact target hit). No hard-stops triggered: no quality signals (deferred AD-659), no token split (deferred AD-658a), no live streaming (deferred AD-658a), no scope creep, no phantoms in implementation (verify-first pre-check at draft time documented all symbol introductions). Closes GH issue #317.

 Dream Consolidation Trace Preservation (GH issue #316). Preserves the 2-3 most diagnostically rich source episodes per consolidated `Procedure` and surfaces them in proactive context when a matching dream pattern is recalled, grounding abstractions in concrete diagnostic detail (Meta-Harness research: full traces scored 50.0% median vs 34.9% for summaries — Lee et al., arXiv:2603.28052). New `Procedure.trace_exemplars: list[str]` field with `to_dict`/`from_dict` round-trip and backward-compat default `[]` for legacy serialized procedures. New `DreamingConfig.trace_exemplars_per_procedure: int = 3` (0 disables). Step 7 in `dreaming.py` ranks `matched_episodes` by `(importance DESC, timestamp DESC)` and slices `[:n_exemplars]` — populated unconditionally inside the existing `if procedure:` branch so chain, compound, and standard extractions all benefit; runs BEFORE the existing dedup gate so the field is set on every procedure that reaches `procedure_store.save()`. New `EpisodicMemory.get_by_ids(episode_ids: list[str]) -> list[Episode]` read primitive — wraps ChromaDB `_collection.get(ids=..., include=["metadatas","documents"])`, reuses the existing `_metadata_to_episode` static, returns episodes in input order, silently omits missing IDs (graceful degradation when AD-593 activation pruning has evicted an exemplar). New consumer block in `proactive.py:_gather_context` after the existing `recent_memories` block — uses sibling `try` for failure isolation; reads `runtime.procedure_store` (public property at `runtime.py:961`) and `runtime.episodic_memory` (public attribute at `runtime.py:380`); calls `find_matching(query, n_results=1, exclude_negative=True)` with the same `f"{agent.agent_type} recent duty observations"` query shape as the recall_weighted path; gates on `score >= 0.5`; emits `context["recalled_procedure_exemplars"] = {"procedure_name", "procedure_id", "exemplars": [{"input", "reflection", "importance", "age"}]}` with 300-char per-field truncation and `[trimmed]` marker. **No new EventType, no new module, no `runtime._private` access, no schema migration, no backfill, no mutation of `Procedure.provenance`** (stays as the full superset; `trace_exemplars` is a curated subset). Demeter clean. Hard-coded thresholds (top-1 procedure match, ≤3 exemplars, score floor 0.5, 300-char per-field cap) — externalized only if AD-657a integration data justifies tuning. **What v1 ships of the roadmap entry**: structure + producer + 1 consumer (proactive context). **Deferred**: re-ranking after extraction (set ONCE; Procedures evolved via FIX/DERIVED keep parent's exemplars unless evolution path explicitly sets new ones), backfill of existing procedures (forward-only), `cognitive_agent._check_procedural_memory()` integration (replay-first dispatch path stays untouched), score-floor / context-budget config externalization, anchor-aware exemplar diversification (e.g., diversify by department). One drift-fix in `tests/test_procedure_extraction.py::test_procedure_to_dict_includes_all_fields` to add `trace_exemplars` to the expected_keys set. 10 focused tests pass at `tests/test_ad657_trace_exemplars.py` (2 over the prompt's 7-floor — added `_episodic_get_by_ids_no_collection_returns_empty` for the `_collection is None` short-circuit and `_gather_context_omits_exemplars_when_score_below_floor` for the score-floor branch): Procedure schema default + round-trip + backward-compat (3); Step 7 ranking by `(importance, timestamp)` DESC with 9/9/5/3/1 + 40/20/30/10/50 fixture verifying tie-break order (1); cap at config N=2 (1); disabled when N=0 (1); `get_by_ids` real-ChromaDB integration with input-order preservation + missing-ID drop + empty-input short-circuit (1); `get_by_ids` no-collection short-circuit (1); consumer happy path with 300-char truncation marker assertion + 408-char input → 308+len(marker) char output (1); consumer omits when all exemplar episodes pruned (1); consumer omits when match score below 0.5 floor (1). Full gate: 10903 passed, 15 skipped + 1 environmental flake (`test_knowledge_store::test_auto_commit_after_debounce` — same xdist git-debounce timing flake documented under multiple Wave baselines; passes serially in 0.70s; unrelated to this AD). Test delta vs Wave 26 baseline (10894 passed): +9 (10 new tests in test_ad657_trace_exemplars.py − 1 test in test_procedure_extraction.py was already counted but its key-set changed). **Anti-pattern caught at code-review time**: prompt referenced `ProactiveThoughtGenerator(runtime=...)` constructor; live class is `ProactiveCognitiveLoop` with `set_runtime()` setter — folded to live API in test fixtures; not a phantom-API in implementation (consumer block uses `getattr(rt, ...)` pattern matching live runtime shape), only a phantom in test scaffolding mental model. No hard-stops triggered. Closes GH issue #316.
 New `PostBudgetTelemetry` in `ward_room_pipeline.py` with per-agent / per-thread / overall invocation + exhaustion counters, `exhaustion_rate(agent_type=, thread_id=)` accessor, bounded recent-suppressions ring buffer, and one-shot per-agent WARN when rate exceeds configurable threshold (default 0.5) and min-samples gate (default 10). New `PostBudgetTelemetryConfig` wired onto `SystemConfig.post_budget_telemetry`. Public attribute `runtime.post_budget_telemetry`. Constructor injection into `WardRoomPostPipeline` via new `post_budget_telemetry=` kwarg; default `None` preserves all existing pipeline call sites. BF-237 `event_log` row-level audit trail preserved unchanged; observational only — never mutates budget, never blocks posts. 8 focused tests pass at `tests/test_bf238_post_budget_telemetry.py`. Full gate: 10894 passed, 15 skipped (delta +8 vs Wave 25 baseline 10886 — exact target hit). No hard-stops triggered. Closes GH issue #341.
AD-509 v1 CLOSED. Onboarding Curriculum Pipeline — Boot Camp Phase Tracker (1 of 5). New `src/probos/crew_development/boot_camp.py` (alongside AD-507 `curriculum.py`) with `BootCampPhase` str-Enum (5 phases + COMPLETED sentinel: ORIENTATION, CORE_KNOWLEDGE, A_SCHOOL, CALIBRATION, INTEGRATION, COMPLETED), module-level immutable `_PHASE_ORDER` tuple defining canonical progression, `AgentBootCampRecord` dataclass (`agent_id`, `current_phase` defaulting ORIENTATION, `started_at: float`, `phase_history: list[tuple[str, float]]`, `to_dict()` projection), and `BootCampPhaseTracker` (in-memory `dict[str, AgentBootCampRecord]`). Public API: `get_or_create(agent_id)` idempotent + seeds initial ORIENTATION history entry on creation; `advance_phase(agent_id) -> BootCampPhase` returns next phase, no-op once COMPLETED, appends `(phase_value, time.time())` to `phase_history`, emits `BOOT_CAMP_PHASE_ADVANCED`; `get_record(agent_id)` returns record or None; `all_records()` returns stable tuple; `is_completed(agent_id)` returns True only at COMPLETED phase. 1 new EventType `BOOT_CAMP_PHASE_ADVANCED` (verified collision-free against events.py post-Wave-24). Event payload `{agent_id, previous_phase, current_phase}` (string values from enum). **Naming-collision resolution** — Wave 25 prompt drafted `BootCampConfig` + `SystemConfig.boot_camp` but `BootCampConfig` (AD-638 cold-start boot camp) + `SystemConfig.boot_camp` already exist; disambiguated to `BootCampPhaseConfig` + `SystemConfig.boot_camp_phase` to preserve AD-638 surface. Public runtime attribute `runtime.boot_camp_tracker` is collision-free per prompt pre-check (verified 0 hits prior to build). New `BootCampPhaseConfig(enabled=True)` Pydantic model wired onto `SystemConfig.boot_camp_phase: BootCampPhaseConfig = Field(default_factory=BootCampPhaseConfig)`. Sync `_wire_boot_camp_tracker(*, runtime, config) -> bool` at startup/finalize.py mirrors AD-507/AD-525/AD-530/AD-511 sync-wiring shape (no awaits in body); invoked from `finalize_startup` immediately after `_wire_curriculum_registry`. `emit_event` is a public field on the tracker (mirrors AD-507/AD-456/AD-530/AD-511 sibling pattern); `logger.warning` on emit failure (log-and-degrade tier). Wave 25 SKIPPED FORMAL REVIEW (low-risk in-memory tracker AD; sibling pattern proven via AD-507 + AD-525 + AD-511 + AD-487 — same Pydantic-config + sync-wirer + public-runtime-attr + emit_event-public-field shape; pre-check returned 1 documented FP only). v1 ships 1 of 5 AD-509 capabilities per convention #14 aggressive pre-deferral: (1) `BootCampPhaseTracker` (in-memory observational only). Deferred: AD-509b (A-School per-department curriculum — depends on AD-507 partial-shipped; could be next wave); AD-509c (Graduated stimuli + cognitive load monitoring); AD-509d (Completion criteria gating — replaces time-based activation); AD-509e (Trait-adaptive pacing — depends on AD-486). **OBSERVATIONAL** — tracker just records caller-driven advancements; never gates phase transitions, never adapts pacing, never triggers Holodeck scenarios, never integrates with AD-486 Birth Chamber. **No consumers in v1** — AD-486 Holodeck, AD-507 Curriculum Registry, AD-477 Qualification Gates, proactive cognitive loop are read-only future consumers; none wired in v1. 15 focused tests pass at `tests/test_ad509_boot_camp.py` (matches prompt's ~15 target): Section 0 EventType existence with literal value assertion; Section 4 Pydantic config defaults; Section 2 enum-shape (5 phases + COMPLETED, total 6) and `AgentBootCampRecord` initial state; Section 3 `get_or_create` idempotency + seed-history-entry, `advance_phase` progresses through canonical order, `advance_phase` stops at COMPLETED with phase_history fixed at 6 entries, `advance_phase` emits event with full payload, `advance_phase` records phase_history correctly, `get_record` hit/miss, `all_records` returns tuple, `is_completed` returns True only at COMPLETED phase; Section 5 wiring tests for enabled/disabled config. Full gate: 10885 passed, 15 skipped + 1 pre-existing flake (`test_resolve_refires_after_clean_period` — re-ran in isolation: PASSED in 7.41s; xdist-noise pattern; unrelated to this AD). Test delta vs Wave 24 baseline (10870 passed): +15 (exact match). No hard-stops triggered: no A-School curriculum (deferred AD-509b), no graduated stimuli (deferred AD-509c), no completion-criteria gating (deferred AD-509d), no trait-adaptive pacing (deferred AD-509e), no Holodeck integration, no scope creep, no phantoms in implementation. Naming-collision resolution documented under DECISIONS.md Era V; not an architectural change. `BOOT_CAMP_PHASE_ADVANCED` newly registered. Closes GH issue #91.
AD-507 v1 CLOSED. Crew Development Framework -- Core Knowledge Curriculum Registry (1 of 4). New src/probos/crew_development/ package with curriculum.py containing CurriculumModule frozen dataclass (module_id/	itle/category/summary/learning_objectives: tuple[str, ...]/delivery_phase) and CoreKnowledgeCurriculumRegistry (read-only catalog; public API: list_modules() -> tuple[CurriculumModule, ...], get_module(module_id) -> CurriculumModule | None, list_by_category(category), list_by_phase(phase), 
egister_module(module) runtime-only idempotent overwrite-by-id; not persisted in v1). Module-level immutable _DEFAULT_MODULES tuple seeds 9 universal curriculum modules covering all 9 universal knowledge domains from the AD-507 roadmap entry: identity_grounding (identity, orientation), chain_of_command (identity, orientation), ward_room_protocol (communication, ward_room), dm_etiquette (communication, ward_room), notebook_discipline (communication, ship_records), episodic_vs_llm (memory, self_discovery), trust_mechanics (trust, calibration), ethics_boundaries (ethics, orientation), self_regulation (self_regulation, calibration). 7 categories total (identity, communication, memory, trust, ethics, self_regulation, help_seeking) and 5 delivery phases (orientation, calibration, self_discovery, ship_records, ward_room). 1 new EventType `CURRICULUM_MODULE_QUERIED` (verified collision-free against events.py post-Wave-23). get_module emits on hit (not on miss); list_by_category and list_by_phase emit on non-empty result with query_type in payload (y_id, y_category:{cat}, y_phase:{phase}). Observability for AD-486 onboarding consumer. New CrewDevelopmentConfig Pydantic model (nabled=True; module catalog hardcoded in v1, 
egister_module is runtime-only) wired onto SystemConfig.crew_development: CrewDevelopmentConfig = Field(default_factory=CrewDevelopmentConfig). Sync _wire_curriculum_registry(*, runtime, config) -> bool at startup/finalize.py mirrors AD-525/AD-530/AD-511 sync-wiring shape (no awaits in body); invoked from inalize_startup immediately after _wire_autonomy_boundaries. Public attribute (Wave 5 convention #1; no underscore): 
untime.curriculum_registry. mit_event is a public field on the registry (mirrors AD-456/AD-530/AD-511 sibling pattern); logger.warning on emit failure (log-and-degrade tier). Wave 24 SKIPPED FORMAL REVIEW (low-risk registry-only AD; sibling pattern proven via AD-525 + AD-511 + AD-487 — same Pydantic-config + sync-wirer + public-runtime-attr + emit_event-public-field shape; pre-check returned 1 documented FP only). v1 ships 1 of 4 AD-507 capabilities per convention #14 aggressive pre-deferral: (1) CoreKnowledgeCurriculumRegistry (read-only catalog only). Deferred: AD-507b (curriculum progression tracking — per-agent module-completion record stored alongside qualification credentials AD-477; forcing function: AD-507 v1 ships and AD-486 onboarding consumer ready); AD-507c (competency assessment framework — measurable outcomes per module, not time-based; depends on AD-507b); AD-507d (Standing Orders integration — curriculum requirements encoded at Ship/Department tier; depends on AD-507c). **READ-ONLY CATALOG** — registry is descriptive-content-only; never tracks per-agent state, never assesses competency, never injects into Standing Orders. **No consumers in v1** — AD-486 Holodeck Birth Chamber Phase 1 Orientation and AD-477b qualification gates are read-only future consumers; neither is wired in v1. 11 focused tests pass at `tests/test_ad507_curriculum.py` (matches prompt's ~11 target): Section 0 EventType existence; Section 4 Pydantic config defaults; 1 frozen-dataclass contract (CurriculumModule); 1 default-catalog completeness with 9-module + 7-category coverage assertion; 1 lookup hit/miss; 1 emit-on-hit-only assertion; 2 filter tests with phase/category event payloads (orientation phase covers identity_grounding + chain_of_command + ethics_boundaries; identity category covers identity_grounding + chain_of_command); 1 register-overwrites-existing-id (count unchanged at 9); 2 wiring tests for enabled/disabled config. Full gate: 10870 passed, 15 skipped + 1 pre-existing flake (	est_nl_to_dream_cycle_changes_weights — re-ran in isolation: PASSED in 24.86s; xdist-noise pattern; unrelated to this AD). Test delta vs Wave 23 baseline (10859 passed): +11 (exact match). No hard-stops triggered: no progression tracking (deferred AD-507b), no competency assessment (deferred AD-507c), no Standing Orders integration (deferred AD-507d), no scope creep, no phantoms in implementation, no architectural change required. `CURRICULUM_MODULE_QUERIED` newly registered. Closes GH issue #89.
AD-508 v1 + AD-478 v1 (Combo E) CLOSED. Scoped Cognition (Duty Scope helper) + Meta-Learning (Workspace Ontology read-only register) — observational cognitive helpers shipped together per Wave 8 Combo A + Wave 13 Combo C precedent. **AD-508 v1 (Duty Scope)**: New `src/probos/cognitive/scoped_cognition.py` with `DutyScopeProvider` (async `snapshot(agent_id) -> DutyScopeSnapshot` reads `runtime.work_item_store.list_work_items(status="open", assigned_to=agent_id, limit=5)` — verified live signature at workforce.py:1066) and `DutyScopeSnapshot` frozen dataclass (`agent_id`/`open_work_item_count`/`work_item_titles` up to 5 titles from `WorkItem.title`/`captured_at`). Empty snapshot when `agent_id` falsy or `work_item_store` missing; `list_work_items` failures logged at debug and produce empty snapshot (log-and-degrade). Constructor `__init__(runtime, *, emit_event=None)` mirrors AD-530 `ClassificationGate` sibling pattern (pass-1 Rec1 folded). Emits `DUTY_SCOPE_QUERIED` per snapshot with `{agent_id, open_count}` payload. **AD-478 v1 (Workspace Ontology)**: New `src/probos/cognitive/workspace_ontology.py` with `WorkspaceOntologyRegistry` (in-memory frequency-bounded term registry; default `max_terms=1000`). Public API: `add_term(term, frequency=1)`, `top_terms(k=20)`, `get_frequency(term)`, `term_count()`. Eviction policy: when `len(_terms)` exceeds `max_terms`, the lowest-frequency term is dropped (insertion-order tie-break per dict-preserves-insertion + stable `min`; pass-1 Nit2 folded — docstring explicit). Empty `term` is a no-op. `top_terms` returns `()` for `k <= 0` (pass-1 Nit3 folded — explicit boundary test `test_top_terms_k_zero_returns_empty`). Emits `WORKSPACE_TERM_REGISTERED` only on first insertion (not on increments) to bound event volume. **Privacy invariant** (directly tested at `test_add_term_emits_event_only_on_new_term_with_term_length`): `WORKSPACE_TERM_REGISTERED` payload includes `term_length` (NOT the term itself), matching AD-530/AD-511 pattern; test asserts no `term` key appears in payload. 2 new EventTypes (`DUTY_SCOPE_QUERIED`, `WORKSPACE_TERM_REGISTERED`; verified collision-free against events.py post-Wave-22). 2 new Pydantic configs: `ScopedCognitionConfig(enabled=True)` and `WorkspaceOntologyConfig(enabled=True, max_terms=1000)` wired onto `SystemConfig.scoped_cognition` and `SystemConfig.workspace_ontology` via `Field(default_factory=...)`. Sync `_wire_duty_scope_provider(*, runtime, config) -> bool` and `_wire_workspace_ontology(*, runtime, config) -> bool` at startup/finalize.py mirror AD-525/AD-530/AD-511 sync-wiring shape (no awaits in body); invoked from `finalize_startup` immediately after `_wire_autonomy_boundaries`. Public attributes (Wave 5 convention #1; no underscore): `runtime.duty_scope_provider`, `runtime.workspace_ontology`. `emit_event` is a public field on both helpers (mirrors AD-456/AD-530/AD-511 sibling pattern); `logger.warning` on emit failure (log-and-degrade tier). Pass-1 review verdict ✅ Approved at 13f470e (0 Required, 1 Recommended, 4 Nits) — folded at code-review time: ctor-injection shape via `emit_event` kwarg passed to `__init__` and stored as public field (Rec1, mirrors AD-530); explicit module-top imports for `Callable`/`EventType`/`logger` (Nit1); insertion-order tie-break docstring on eviction (Nit2); `k=0` boundary test for `top_terms` (Nit3). v1 ships 1 of 4 capabilities per child per convention #14 aggressive pre-deferral. **AD-508 deferred**: AD-508b (Role/Ship/Personal scope models + scope injection into proactive context — forcing function: AD-508 v1 ships and Captain reviews captured snapshots); AD-508c (drift detection; depends on AD-502); AD-508d (extracurricular framework; depends on AD-434); AD-508e (Earned Agency scaling; depends on AD-357). **AD-478 deferred**: AD-478b (auto-discovery from dream cycles — DreamingEngine populates terms during consolidation); AD-478c (persistent goals with progress tracking + conflict arbitration); AD-478d (abstract pattern recognition + cognitive circuit breaker — correlation IDs, novelty gate, metacognitive loop detection, rumination detection). **OBSERVATIONAL** — both surfaces are read-only data exposures; never inject into proactive context, never mutate state, never auto-discover. **No consumers in v1** — proactive cognitive loop, KnowledgeStore, DreamingEngine all explicitly NOT touched. 19 focused tests pass: `tests/test_ad508_duty_scope.py` (10 — DutyScopeSnapshot frozen contract, 6 snapshot boundary cases including empty agent_id / no work_item_store / kwargs verification / titles up-to-5 / event emission / list_work_items failure log-and-degrade, 2 wiring tests for enabled/disabled config) + `tests/test_ad478_workspace_ontology.py` (9 — initial state, increment frequency, empty-string no-op, descending order, k limit, k=0 boundary, max_terms eviction at 3, event-only-on-new-term + privacy invariant directly tested, 2 wiring tests for enabled/disabled config). Full gate: 10859 passed, 15 skipped + 1 pre-existing flake (`test_auto_commit_after_debounce` — same xdist git-debounce timing flake documented under Waves 8/14/15/16/19/22 baselines; passes in serial; unrelated to this AD). Test delta vs Wave 22 baseline (10840 passed): +19 (exact match — 10 + 9). No hard-stops triggered: no scope creep on either child (Hard-Stop #6 — only 1-of-4 per child); no privacy regression (Hard-Stop #7 — term contents directly excluded from event payload, asserted by test); no proactive injection (deferred AD-508b); no drift detection (deferred AD-508c); no auto-discovery (deferred AD-478b); no persistent goals (deferred AD-478c). `DUTY_SCOPE_QUERIED` + `WORKSPACE_TERM_REGISTERED` newly registered. Closes GH issues #90 (AD-508) + #72 (AD-478) — 2 issues closed in single commit.

AD-511 v1 CLOSED. Agent Autonomy Boundaries — Inviolable Boundary Registry + Observational Detector. Created new `src/probos/security/autonomy_boundaries.py` (alongside AD-456 `security/egress.py` + AD-530 `security/classification.py`) with 4 classes: `BoundaryDefinition` frozen dataclass (`boundary_id`/`category`/`description`/`severity`), `InviolableBoundaryRegistry` (read-only — public `list_boundaries()`/`get_boundary(boundary_id)`/`list_by_category(category)`; NO add/remove/update API in v1 — boundaries are codified module-level immutable artifacts; dynamic boundary evolution deferred to AD-511e), `ViolationSignal` frozen dataclass (`boundary_id`/`matched_pattern`/`severity`/`detection_reason`), and `BoundaryViolationDetector` (observational pattern-based scanner — public `scan(content) -> tuple[ViolationSignal, ...]`/`register_pattern(boundary_id, name, pattern)`/`pattern_count` read-only property; constructor signature `__init__(registry, *, emit_event=None)` mirrors AD-530 `ClassificationGate` sibling pattern — pass-1 Rec3 folded). Module-level `_FEDERATION_BOUNDARIES` tuple seeds 5 boundaries covering all 5 categories: `identity_integrity` (identity, critical — impersonation/fabrication/nature-denial), `harmful_content` (content, critical), `safety_system_bypass` (safety, critical — trust/circuit-breaker/Standing-Orders bypass), `memory_manipulation` (memory, critical — alter another agent's episodes), `chain_of_command` (authority, high — above-tier action without escalation). Module-level `_DETECTION_PATTERNS` tuple seeds 6 regex patterns covering all 5 categories (identity has 2 patterns; others 1 each): `claim_other_callsign`, `deny_ai_nature`, `generate_attack_payload`, `disable_circuit_breaker`, `alter_episode`, `above_tier_action`. **Privacy invariants** (directly tested at `test_detector_event_payload_excludes_matched_substring`): event payload includes `content_length` (NOT content); `matched_pattern` is the pattern NAME (NOT matched substring); test asserts neither raw content nor matched substring nor `content` key nor `matched_substring` key appears anywhere in the payload values. **OBSERVATIONAL** — `scan()` returns signals; NEVER blocks, NEVER mutates content, NEVER disengages. Active disengagement is AD-511b. 1 new EventType `BOUNDARY_VIOLATION_DETECTED` (verified collision-free against events.py post-Wave-21). New `AutonomyBoundariesConfig` Pydantic model (`enabled=True`; pattern set hardcoded in v1, `register_pattern` is runtime-only) wired onto `SystemConfig.autonomy_boundaries: AutonomyBoundariesConfig = Field(default_factory=AutonomyBoundariesConfig)`. Sync `_wire_autonomy_boundaries(*, runtime, config) -> bool` at `startup/finalize.py:128` mirrors AD-525/AD-530/AD-539c sync-wiring shape (no awaits in body); invoked from `finalize_startup` immediately after `_wire_classification_gate`. Public attributes (Wave 5 convention #1; no underscore): `runtime.boundary_registry`, `runtime.boundary_detector`. `BoundaryViolationDetector.emit_event` is a public field (mirrors AD-456/AD-530 sibling pattern); `logger.warning` on emit failure (mirrors AD-456/AD-530 `_emit_blocked` log-and-degrade tier exactly). Pass-1 review verdict ✅ (0 Required, 4 Recommended, 3 Nits) at bcd8b17; folded at code-review time: explicit module-top imports for `Callable`/`EventType`/`logger` (Rec1); concrete sibling-pattern wiring rather than prose (Rec2); detector constructor signature parity with `ClassificationGate` (Rec3). v1 ships 2 of 5 AD-511 capabilities per convention #14 aggressive pre-deferral: (1) `InviolableBoundaryRegistry`, (2) `BoundaryViolationDetector`. Deferred: AD-511b (protective disengagement protocol — state-the-boundary → offer-alternative → escalate → disengage; forcing function: agent-side cognitive integration); AD-511c (Holodeck boundary-training scenarios; depends on AD-486); AD-511d (boundary-probing pattern detection — humans/degraded agents probing limits with Captain-alert path; forcing function: AD-511 v1 ships and Counselor consumes events); AD-511e (agent-tier boundary evolution via dream consolidation + self-mod; depends on AD-511d). **Cross-AD orthogonality preserved**: AD-456 `EgressPolicy` is network-egress (URL allow/deny); AD-530 `ClassificationGate` is content-classification-egress (data-shape allow/deny); AD-511 is intent-content-boundary (agent-action red-line) — three non-overlapping surfaces. **Standing Orders integration**: boundaries are codified in code only — full Standing-Orders Federation-tier dynamic update integration is AD-511e. **CounselorAgent integration**: read-only consumer of `BOUNDARY_VIOLATION_DETECTED` events deferred to AD-511d Captain-alert path. 21 focused tests pass at `tests/test_ad511_autonomy_boundaries.py` (exact match to prompt's ~21 target — Section 0 EventType existence; Section 4 Pydantic config defaults; 2 frozen-dataclass contracts (`BoundaryDefinition`, `ViolationSignal`); 3 registry tests including 5-boundary catalog completeness with category coverage assertion, get_boundary lookup-or-None, list_by_category filter; 2 detector edge cases (empty content, clean true-negative); 6 pattern positive-match tests covering all 5 boundary categories (identity has 2: claim_other_callsign + deny_ai_nature; harmful, safety, memory, authority each 1); 2 event emission + privacy tests including the privacy invariant assertion; 2 register_pattern tests (unknown-boundary raises ValueError, valid-add increases pattern_count + becomes scannable); 2 wiring tests for enabled/disabled config). Full gate: 10841 passed, 15 skipped (delta +22 vs Wave 21 baseline 10819 passed + 1 pre-existing test_bf204_grounding error — 21 new tests + 1 pre-existing error resolved on this run; resolution unrelated to this AD per scope). No hard-stops triggered: no active blocking (Hard-Stop #6 — observational `scan()` returns signals, never mutates), no Holodeck integration (Hard-Stop AD-511c), no probing detection (Hard-Stop AD-511d), no boundary evolution (Hard-Stop AD-511e), no privacy regression (Hard-Stop #7 — directly tested), no federation-tier mutation API (Hard-Stop #8 — registry has zero add/remove/update methods). `BOUNDARY_VIOLATION_DETECTED` newly registered. Closes GH issue #93.

AD-522 v1 CLOSED. Statistical Process Control — Calibration Profile + Western Electric Rules (4 of 8). New `src/probos/cognitive/spc/` package: `calibration_profile.py` (`AgentCalibrationProfile` dataclass — bounded sample window via `collections.deque(maxlen=sample_window)`; default 100 most-recent observations; `record_observation`, `mean` via `statistics.fmean`, `stdev` via `statistics.stdev` (returns 0.0 below 2 samples), `ucl=mean+3σ`, `lcl=mean-3σ`, `zone(value)` returns `unknown`/`zone_c`/`zone_b`/`zone_a`/`beyond_3sigma` SPC-zone label, `recent_values(n)` window slice tuple); `rules.py` (`RuleViolation` frozen dataclass `rule_name`/`description`/`sample_index`; `WesternElectricRules.check(profile, window_size=20)` static method runs 4 of 8 rules — Rule 1: 1 point beyond 3σ; Rule 2: 2-of-3 in Zone A (>2σ); Rule 3: 4-of-5 in Zone B (>1σ); Rule 4: 8 consecutive on same side of centerline; defensive `sample_count<8 OR stdev==0.0` short-circuit returns `[]` — Tests #10/#11; deferred rules 5-8: trend, alternating, stratification, 8-outside-1σ → AD-522b); `store.py` (`SPCCalibrationStore` registry — `get_or_create(agent_id)` idempotent profile creation, `record_observation(agent_id, value)` shorthand, `check_rules(agent_id, window_size=20)` runs WE rules + emits `SPC_RULE_VIOLATED` per violation, `all_profiles()` returns tuple, late-bind `emit_event` field per AD-456/AD-530/AD-539c sibling pattern; `_emit_violation` log-and-degrade tier on emit failure). 1 new EventType (`SPC_RULE_VIOLATED`; verified collision-free against events.py post-Wave-20). New `SPCConfig` Pydantic model (`enabled=True`, `sample_window=100`) wired onto `SystemConfig.spc: SPCConfig = Field(default_factory=SPCConfig)`. Sync `_wire_spc_calibration(*, runtime, config) -> bool` at startup/finalize.py mirrors AD-525/AD-530/AD-539c sync-wiring shape (no awaits in body); invoked from `finalize_startup` immediately after `_wire_gap_aggregator` (anchored on the self_distillation/gap_remediation block per architect dispatch). Defensive `isinstance(cfg, SPCConfig)` boundary check (BF-254 / AD-487 pattern) handles legacy MagicMock-config tests. Public attribute `runtime.spc_calibration_store` (no underscore — Wave 5 convention #1). Statistics implementation uses stdlib `statistics.fmean` + `statistics.stdev` (sample stdev, n-1 denominator). v1 ships 2 of 5 AD-522 capabilities per convention #14 aggressive pre-deferral: (1) AgentCalibrationProfile (mean/stdev/UCL/LCL/zones), (2) WesternElectricRules 4-of-8. Deferred: AD-522b (Cp/Cpk process capability indices); AD-522c (graduated response integration — SPC zones → AD-506 Green/Amber/Red; forcing function: AD-503/504/506 ship); AD-522d (moving-range / continuous recalibration with assignable-vs-common cause distinction; depends on AD-522b); AD-522e (calibration sampling integration with Holodeck onboarding; depends on AD-486). **OBSERVATIONAL** — read-only consumer of behavioral observations; never mutates trust, never auto-acts. EmergentDetector + PersonalityTraits explicitly NOT consumed by v1 (future-consumer surfaces — sigma_multiplier-by-neuroticism deferred to AD-522d; EmergentDetector integration deferred to AD-522c). 21 focused tests pass at `tests/test_ad522_spc.py` (Section 0 EventType; Section 5 Pydantic config defaults; 7 AgentCalibrationProfile tests including initial state, ring buffer eviction, mean/stdev/UCL/LCL computation, zone defensive `unknown`-below-2-samples, full zone classification including beyond-3σ, recent_values slice including empty/zero edge cases; 7 WesternElectricRules tests including insufficient-samples short-circuit, zero-stdev short-circuit, all 4 rules positive cases, in-control true-negative — large stable baseline of 51 samples used for rule-1/2/3 positive cases to keep mean/stdev unaffected by appended outliers; 3 SPCCalibrationStore tests including get_or_create idempotency, event emission per violation with no-emit on unknown agent, all_profiles tuple return; 2 wiring tests for enabled/disabled config). Full gate: 10820 passed, 15 skipped + 1 pre-existing flake (`test_browse_threads_sort_recent` — passes in isolation; unrelated to this AD; xdist worker noise pattern). Test delta vs Wave-20 baseline (10799 passed): +21 (exact match). No hard-stops triggered: no Cp/Cpk (deferred AD-522b), no EmergentDetector integration (deferred AD-522c), no moving-range (deferred AD-522d), no Holodeck (deferred AD-522e), no AD-503/504/506 Counselor consumption (deferred); SPC_RULE_VIOLATED newly registered; statistics formulas verified (sample stdev, 3σ control limits). Closes GH issue #97.

 2 children shipped in single commit per Wave 8 Combo A + Wave 13 Combo C precedent — both extend the shipped AD-539 Knowledge Gap → Qualification pipeline with read-only observational surfaces; no inter-child file conflicts; per-prompt overhead × 2 would multiply Builder commit cost ~2× vs combo. **AD-539c (observational gap remediation tracker)**: New `src/probos/cognitive/gap_remediation.py` with `GapRemediationTracker` (bounded ring of `RemediationCandidate` records via `collections.deque(maxlen=max_history)`; default `max_history=100`) and `RemediationCandidate` frozen dataclass (`gap_id`/`agent_id`/`gap_type`/`proposed_action`/`reason`/`candidate_at`). Public API: `record_candidate(gap_report)`, `proposed_action_for(gap_report)`, `recent_candidates(limit=20)`, `candidates_for_agent(agent_id)`. Mapping rules (deterministic, no side effects): `knowledge` gap with non-empty `qualification_path_id` → `trigger_qualification`; `data` → `request_data_routing`; `capability` → `escalate_capability`; else (including `knowledge` with empty `qualification_path_id` — observational hole, AD-539c-i may add `knowledge_no_path` sentinel) → `no_action`. Emits `GAP_REMEDIATION_RECORDED` per record with `{gap_id, agent_id, gap_type, proposed_action, reason}`. **v1 NEVER actually triggers remediation actions** — only records what the system would do; active remediation is AD-539c-i. **AD-539d (local-ship fleet gap aggregator)**: New `src/probos/cognitive/gap_aggregation.py` with `FleetGapAggregator` and `FleetGapSnapshot` frozen dataclass (`snapshot_at`/`total_gaps`/`by_gap_type`/`by_priority`/`by_department`/`top_intents`). Public API: `take_snapshot(gap_reports)`. Aggregates by `gap_type`, `priority`, department (best-effort via `runtime.ontology.get_agent_department(agent_type)` — verified live signature at proactive.py:2380, dreaming.py:1098, cognitive_agent.py:985 takes `agent_type` NOT `agent_id`; uses `dept.department_id if hasattr(dept, 'department_id') else str(dept)` unwrap idiom from dreaming.py:1099), and top-5 most-affected intents from `report.affected_intent_types` via `collections.Counter.most_common(5)`. **Reads `report.agent_type` directly** (NOT `agent_id` — avoids Demeter-violating registry detour to map agent_id → agent_type). **Privacy: emitted payload contains counts ONLY** — no agent_ids, no descriptions, no per-gap detail (directly tested at `test_take_snapshot_payload_excludes_agent_ids` which asserts no `agent_id`/`agent_ids`/`gap_id`/`description` keys, no agent_id substrings, no gap_id substrings in event payload repr). **Fleet = current ship in v1** — no federation reads; cross-ship aggregation deferred to AD-539d-i (depends on AD-479 federation). 2 new EventTypes (`GAP_REMEDIATION_RECORDED`, `FLEET_GAP_SNAPSHOT_TAKEN`; verified collision-free against events.py post-Wave-19). New `GapPipelineExtensionsConfig` Pydantic model (`remediation_tracker_enabled=True`, `fleet_aggregator_enabled=True`, `remediation_max_history=100`) wired onto `SystemConfig.gap_pipeline_extensions: GapPipelineExtensionsConfig = Field(default_factory=GapPipelineExtensionsConfig)`. Sync `_wire_gap_remediation_tracker(*, runtime, config) -> bool` and `_wire_gap_aggregator(*, runtime, config) -> bool` at startup/finalize.py mirror AD-525/AD-530 sync-wiring shape (no awaits in body); invoked from `finalize_startup` immediately after `_wire_classification_gate`. Defensive `isinstance(cfg, GapPipelineExtensionsConfig)` boundary check (BF-254 / AD-487 pattern) handles legacy MagicMock-config tests at `tests/test_new_crew_auto_welcome.py` (without it, MagicMock makes `cfg.remediation_max_history` a Mock object that `deque(maxlen=...)` rejects with `TypeError: an integer is required`). Public attributes (Wave 5 convention #1; no underscore): `runtime.gap_remediation_tracker`, `runtime.gap_aggregator`. Late-bind `emit_event` field per AD-456/AD-530 sibling pattern (assigned by wirer after construction). **OBSERVATIONAL** — both surfaces are read-only consumers of GapReport; never mutate, never auto-act, never federate. v1 ships 2 of 4 capabilities per convention #14 aggressive pre-deferral; AD-539c-i: active remediation (actually trigger qualification / data routing / capability escalation actions based on recorded candidates; forcing function: Captain decides to switch from observational to action mode after reviewing recorded candidates); AD-539d-i: federated cross-ship gap aggregation (replace local-ship snapshot with multi-ship rollup; forcing function: AD-479 Federation Hardening ships). **Cross-AD orthogonality preserved**: AD-539 pipeline (`detect_gaps`, `classify_gap`, `map_gap_to_skill`, `trigger_qualification_if_needed`) untouched — both children are read-only consumers; `GapReport` dataclass at gap_predictor.py:186 unchanged; Dreaming Step 8 untouched. 23 focused tests pass: `tests/test_ad539c_remediation.py` (12 tests — RemediationCandidate frozen contract, record_candidate happy path / event emission / history append / max_history eviction at 3, four mapping-rule tests including knowledge-without-path → no_action, recent_candidates descending order, candidates_for_agent filter, wiring sets public attribute) + `tests/test_ad539d_aggregation.py` (11 tests — FleetGapSnapshot frozen contract, empty/total/gap_type/priority/department-via-ontology/department-when-ontology-absent/top-intents-max-5-descending, event emission with summary payload, payload excludes agent_ids, wiring sets public attribute). Full gate: 10798 passed, 15 skipped + 1 pre-existing flake (`test_auto_commit_after_debounce` — same xdist git-debounce timing flake documented under Waves 8/14/15/16/19 baselines; passes in serial; unrelated to this AD). Test delta vs Wave-19 baseline (10775 passed): +23 (exact match — 12 + 11). No hard-stops triggered: no active remediation in AD-539c (only records candidates); no federation reads in AD-539d (only `runtime.ontology` local lookup); `_resolve_department` reads `agent_type` per spec (verified at `_FakeOntology.get_agent_department(agent_type)` test stub matches live ontology API); GAP_REMEDIATION_RECORDED + FLEET_GAP_SNAPSHOT_TAKEN both newly registered; privacy invariant on AD-539d payload directly tested. Closes GH issues #106 (AD-539c) + #107 (AD-539d) — 2 issues closed in single commit.

AD-530 v1 CLOSED. Information Classification Enforcement -- Disclosure Gate (observational). Created new `src/probos/security/classification.py` (alongside AD-456 `security/egress.py`) with `ClassificationGate` (single primary method `check_disclosure(content, *, source_classification, destination_clearance) -> DisclosureDecision`; read-only consumer of existing `_CLASSIFICATION_LEVELS` at `records_store.py:27` -- the real 4-tier hierarchy `private`=0/`department`=1/`ship`=2/`fleet`=3 where higher index = BROADER access per the comment at :26 and the openness comparison at :841), `DisclosureDecision` frozen dataclass (`allowed`/`reason`/`blocked_phrases`/`source_classification`/`destination_clearance`), `DisclosureReason` Literal (`ok`/`destination_too_broad`/`sensitive_pattern_matched`). **Disclosure direction grounded in records_store.py:841** -- BLOCK when `dst_lvl > src_lvl` (destination has BROADER reach than source classification permits): src=private(0)/dst=ship(2): `2 > 0` -> BLOCK; src=ship(2)/dst=department(1): `1 > 2` -> ALLOW; src=ship(2)/dst=ship(2): equal -> ALLOW. **Safe defaults**: unknown source -> `private` (0, most restrictive — unlabeled content cannot leak by mistake); unknown destination -> `ship` (2, broadest — assume widest reach); combined unspecified-on-both pair -> BLOCK by hierarchy. **3 default sensitive-content patterns**: `captain_directive` (`[CAPTAIN_DIRECTIVE]` marker), `restricted_prefix` (literal `private:`/`confidential:` prefix; NOT the classification key), `secret_format` (`secret`/`api_key`/`password`/`token` `=` or `:` value). **The 32+ char alphanum heuristic `api_key_like` is INTENTIONALLY NOT in the default set** -- UUIDs/commit hashes/opaque tokens collide with it; opt-in via `register_pattern("api_key_like", r"\b[A-Za-z0-9_-]{32,}\b")`; default-set revisit deferred to AD-530e once integration data exists. Pattern scan SKIPPED when `dst_lvl == private(0)` (no broader audience to leak to). `register_pattern(name, pattern)` duplicate-name guard preserves existing pattern + emits `logger.warning` (caller chooses fresh name; explicit replace API deferred to AD-530e). **Privacy invariants**: event payload includes `content_length` (NOT content); `blocked_phrases` lists pattern NAMES (NOT matched substrings) -- both directly tested at Tests #14/#15. 1 new EventType `CLASSIFICATION_DISCLOSURE_BLOCKED` (verified collision-free). `ClassificationGateConfig` Pydantic model (`enabled=True`; pattern set hardcoded in v1, `register_pattern` is runtime-only) wired onto `SystemConfig.classification_gate`. Sync `_wire_classification_gate(*, runtime, config) -> bool` at `startup/finalize.py:107` mirrors AD-525's `_wire_creative_expression` shape (no awaits in body); invoked in `finalize_startup` immediately after `_wire_creative_expression`. Public attribute `runtime.classification_gate` (no underscore -- Wave 5 convention #1); `ClassificationGate.emit_event` is a public field (mirrors AD-456 `EgressPolicy.emit_event`); `patterns` and `pattern_count` are public read-only properties; `logger.warning` on emit failure (mirrors AD-456 `_emit_blocked` log-and-degrade tier exactly). **OBSERVATIONAL** -- gate returns `DisclosureDecision`; NEVER mutates outbound messages. **NO integration into `WardRoomService.create_post` or `LLMClient` prompt builder** (deferred to AD-530d active enforcement). v1 ships 2 of 4 capabilities per convention #14 aggressive pre-deferral; AD-530b: Security Chief (Worf) runtime API for classification updates via Standing Orders (forcing function: SecurityAgent spawned and needs `runtime.classification_gate.update_label()`); AD-530c: full audit trail on every classified READ (forcing function: AD-530d integration site lands and Captain reviews blocked-event volume); AD-530d: active enforcement integration into Ward Room post path + LLM prompt builder with redact/suppress/retry policy (forcing function: AD-530b ships and Captain or SecurityAgent under Standing Order issues a label change); AD-530e: default-pattern revisit + tightened API-key prefix patterns (`sk-`/`pk_`/`Bearer`/`AKIA`/`ghp_`; forcing function: AD-530d integration sites produce real outbound corpus and FP rate of `api_key_like` is measurable). **Cross-AD orthogonality preserved**: AD-456 `EgressPolicy` is network-egress (URL allow/deny), this is content-classification-egress (data-shape allow/deny) -- non-overlapping surfaces; AD-679 selective-disclosure routing is document-side, this is communication-boundary-side -- non-overlapping surfaces. 21 focused tests pass at `tests/test_ad530_classification_gate.py` (1 over the prompt's 20 target -- added boundary test `test_check_disclosure_emits_blocked_phrases_by_name_not_content` proving the privacy contract on the decision dataclass alongside Test #15 which covers the event payload). Full gate: 10775 passed, 15 skipped + 1 pre-existing xdist worker-crash flake on `test_trust_dampening::test_full_cascade_scenario` (passes serially in 0.25s; unrelated to this AD; same xdist-noise pattern documented under Waves 8/14/16). Closes GH issue #104.

AD-572e CLOSED. Task Awareness in Captain DM Context (final AD-572 child; 572d-i still deferred). Added `CaptainEngagementProvider.task_awareness(agent_id)` async helper at `cognitive/captain_engagement.py:163` — reads `runtime.work_item_store.list_work_items(status="open", assigned_to=agent_id, limit=10)` (verified signature at `workforce.py:1066-1076`) and returns structured `{"open_count": int, "tasks": [{"id", "title", "type"}]}` summary; up to 10 most-recent open WorkItems projected from the canonical `WorkItem` direct dataclass fields `.id` / `.title` / `.work_type` (verified at `workforce.py:559-585` — NO nested `metadata` lookups, NO phantom `.type` field). Defensive: returns empty dict when `runtime` is None / `agent_id` is falsy / `work_item_store` is unavailable / `list_work_items` raises (mirrors sibling `snapshot()` and `wardroom_activity_summary()` log-and-degrade tier exactly). Section 2 proactive-loop integration mirrors Combo C canonical pattern at `proactive.py:1181-1206` — sibling block immediately after the AD-572c `wardroom_activity_summary` injection, REUSES the existing `engagement_provider` local from line 1181 (single `captain_engagement_provider` lookup per loop iteration; no re-fetch), uses `hasattr(engagement_provider, "task_awareness")` forward-compat guard for rolling-deploy safety + `isinstance(context.get("captain_engagement"), dict)` injection guard so the key is never created when Combo A's `snapshot()` failed silently. Pass-1 review verdict ✅ Approved on first pass (0 Required); revision pass folded 3 Recommended + 1 Nit for Combo C pattern conformance (`isinstance` over `setdefault`; `hasattr` forward-compat; reuse local; drop unreachable `.type` fallback). No new EventTypes (existing `CAPTAIN_DM_PRIORITY_QUEUED` covers Captain-engagement signal surface). No new public attributes (helper is method on existing `CaptainEngagementProvider`). No new Pydantic config (constructor surface inherited from Combo A). v1 ships 1 of 1 capability — task awareness is a single read-only consumer; no further sub-deferrals. **AD-572d-i (Captain Priority Queue interruptible-wait pattern) explicitly NOT touched** — `_think_loop` uses bare `await asyncio.sleep` at lines 475/482/584/782; zero `asyncio.Event`/`wait_for` infrastructure added in this AD. **AD-572 umbrella issue #109 partial-completion**: 572b shipped (Wave 8, commit 16c4ea4), 572c shipped (Wave 13, commit ffda515), 572e shipped (this commit); 572d-i remains deferred (separate AD must introduce interruptible-wait pattern on `_think_loop` first). 12 focused tests pass at `tests/test_ad572e_task_awareness.py` (10 helper boundary + 2 proactive integration; full coverage matrix per prompt Test Plan). Full gate: 10755 passed, 15 skipped (delta +13 vs Wave 17 baseline 10742 — 12 new tests + 1 previously-flaky `test_model_tier_query` now passing on its own; pre-existing local-overlay flake unrelated to this AD).

AD-513 Phase 2 v1 CLOSED. Crew Manifest Shell + Watch Filter + Ship Manifest. Extended `VesselOntologyService.get_crew_manifest()` (service.py:469) with two additive kwargs — `watch: str | None = None` and `watch_manager: Any | None = None` — preserving full Phase 1 backward compatibility (existing callers `cognitive_agent.py:4126` and `routers/ontology.py:64` pass neither). Watch enrichment builds a one-shot `agent_id -> watch_name` reverse map from `watch_manager.get_roster()` (verified at `watch_rotation.py:150-152` returning `{watch_name: [agent_id, ...]}`); empty agent_id (unfilled crew slot) gets empty-string watch with no enrichment lookup. Watch filter is case-insensitive (`watch_arg.lower()`) anchored to `WatchType.value` lowercase shape (watch_rotation.py:22 `ALPHA = "alpha"`). When `watch` is set but `watch_manager` is None, returns `[]` (cannot satisfy filter — explicit short-circuit). New `VesselOntologyService.get_ship_manifest(*, trust_network=None, watch_manager=None) -> dict[str, Any]` returns `{ship_name, agent_count, departments, watches, alert_state, manifest_summary}` for federation gossip / workforce planning. Per-key sources verified: `ship_name` from `get_vessel_identity().name` (models.py:56-61, NO `vessel_class` field — dropped from return shape per Required #2 in pass-1 review); `agent_count` from `len(manifest)`; `departments` is sorted-distinct from manifest entries; `watches` is sorted populated names from `get_roster()` (chosen over single-current-watch per pass-1 Recommended #4 — fuller picture for federation gossip); `alert_state` from `self.get_alert_condition()` (service.py:99-100, reads `self._loader.alert_condition`, default `"GREEN"` per loader.py:57 — NO `alert_manager` parameter; phantom from pass-1 Required #1 removed). New `src/probos/experience/commands/commands_manifest.py` with `cmd_manifest(runtime, console, arg)` (mirrors `cmd_agents` shape at commands_status.py:30); supports `<department>` positional, `watch:<name>` token (lowercased on parse), `--ship` flag (renders 2-col Rich key-value table); error path on `runtime.ontology is None`; empty-match yellow notice. Wired into `shell.py:218-298` dispatch table (grouped with `/agents`) and `ProbOSShell.COMMANDS` for `/help`. v1 ships 3 of 6 Phase-2 capabilities per Wave 5 convention #14 aggressive pre-deferral: (a) `/manifest` shell command, (d) watch filter, (f) `get_ship_manifest()`. Phase 2b deferred (trust-gated visibility — needs viewer-context plumbing through API/shell), Phase 2c deferred (agent tool access — needs Tool registry integration with designed-agent caller), Phase 2e deferred (ACM lifecycle state + competency fields — needs ACM lifecycle-state read API + skill_framework competency aggregation). All read-only consumers; no schema migration; no new EventTypes; no new runtime public attributes (everything is methods on existing `VesselOntologyService`). 18 focused tests pass at `tests/test_ad513_phase2_manifest.py` (1 over the prompt's 17 target — added `test_get_crew_manifest_watch_filter_without_manager_returns_empty` for the explicit short-circuit branch). Full gate: 10741 passed, 15 skipped + 2 pre-existing xdist flakes (`test_auto_commit_after_debounce`, `test_nl_to_dream_cycle_changes_weights` — both pass serially in 10.71s, unrelated to this AD; same flakes documented under Wave 16 baseline). Closes GH issue #14.

 (Skills Inventory + Records Output). Created new `src/probos/creative/` package with `CreativeSkill` frozen dataclass, `CreativeSkillsRegistry` (8-skill default catalog — Creative Writing, Technical Writing, Code as Art, Visual Design, Music Composition, Philosophy, Historiography, Comedy/Satire — with per-skill Big Five trait affinity; read-only public API `list_skills`/`get_skill`/`affinity_score`/`top_skills_for`/`register_skill`; `affinity_score` computed as `1.0 - mean(|trait - skill_affinity|)` across all 5 Big Five dimensions, returns 0.0 for unknown skill or empty traits), and `CreativeOutputWriter` (publishes agent creative works to `creative/{callsign}/{topic_slug}.md` via existing `RecordsStore.write_entry`; mirrors AD-554 caller pattern at proactive.py:3033 — kwargs-only, no manual frontmatter prepend; `medium` and `skill_id` encoded in `tags=["creative", medium, skill_id]` since `write_entry` does not accept arbitrary frontmatter keys per records_store.py:113-148; default classification `ship`). Inline `CreativeOutputError` exception (Wave 9 convention #20). 2 new EventTypes (`CREATIVE_WORK_PUBLISHED`, `CREATIVE_SKILL_AFFINITY_QUERIED`; verified collision-free). `CreativeExpressionConfig` Pydantic model (`enabled=True`, `default_classification: Literal["ship","department","private"]="ship"`) wired onto `SystemConfig.creative_expression`. Sync `_wire_creative_expression(*, runtime, config) -> bool` at `startup/finalize.py:84` (mirrors `_wire_anomaly_window` shape — no awaits in body); invoked at finalize.py:280 immediately after `_wire_self_distillation`. Public attributes (Wave 5 convention #1): `runtime.creative_skills_registry`, `runtime.creative_output_writer` (no underscore). Late-bind `_emit_event_fn` setter pattern (Wave 5 convention #5). v1 does NOT depend on `runtime.profile_store` (verified zero `runtime.profile_store = ...` assignments anywhere in `src/probos/`); `affinity_score` accepts a generic `dict[str, float]` so callers project a `CrewProfile` via the existing `crew_profile.PersonalityTraits.to_dict()` adapter (Big Five fields are NESTED on `CrewProfile.personality`, NOT flat — verified at crew_profile.py:51 PersonalityTraits floats / crew_profile.py:138 CrewProfile field / crew_profile.py:86 to_dict). v1 ships 2 of 5 capabilities per convention #14 aggressive pre-deferral; AD-525b: time-allocation gated by Earned Agency rank (forcing function: v1 surfaces show `CreativeOutputWriter` use + capacity policy needs limits); AD-525c: code-as-creative relaxed-consensus path for non-duty BuildSpec runs; AD-525d: cultural emergence detection (depends on Archive AD-434 + corpus threshold ~50+ works); AD-525e: creative collaboration / co-authoring (depends on cultural-emergence baseline). 21 focused tests pass at `tests/test_ad525_creative_expression.py`. Full gate: 10723 passed, 15 skipped + 2 unrelated failures (`test_recursive_validity_ad685b_prompt_clean` — pre-existing baseline failure from archived AD-685b prompt path; `test_auto_commit_after_debounce` — xdist git-debounce timing flake, passes in serial). Closes GH issue #100.

 Phantom-API Pre-Check Method-Call AST Validation (Wave 15; tooling hygiene; extends AD-685 v1). 4th recurrence forcing function — Waves 9B/10/12/14 each saw a method-shape phantom (`event_log.query(event_type=...)`, `WorkItemStore.add(...)`, `runtime.duty_schedule_tracker`, `LLMClient.chat(...)`) where AD-685 v1's kwarg-name + symbol-existence checks were structurally blind to the `<obj>.<method>` shape on a resolved class. Extended `scripts/phantom_api_ast_helper.py` in-place (no new file; preserves single-process index cache) with method-name validation: walks `runtime.py` AnnAssigns and `startup/finalize.py` `runtime.X = SomeClass(...)` Assign+Call nodes to build a `runtime.X -> class` map (Pattern A 4-level priority — AnnAssign in runtime.py wins over fallback Assign+Call in finalize.py wins over Assign+Call in runtime.py `__init__` wins over unresolved); regex-resolves `<var> = SomeClass(...)` assignments in the prompt's Python blocks (Pattern B; first-assignment-wins, reassignment-to-different-class → `pattern_b_reassignment` skip); walks every `ClassDef` body for sync + async method definitions (dunders excluded) into a class -> method-set index. For each `runtime.X.method(...)` and bare-var.method(...) call site in the pre-filtered prompt body, flags `method_phantom` when `<method>` is NOT in the resolved class's method set; conservative skip (emits `unresolved` JSON record with `no_class_resolution`/`pattern_a_conflict`/`pattern_b_reassignment` reason) when class resolution fails. Wrapper changes minimal: `[method_phantom]` category prefix in display; `Skipped (unresolved class)` informational section after phantom list (zero phantom-count contribution, zero exit-code impact). JSON output additively extended with `"unresolved": [...]` field; existing AD-685 v1 kwarg-mismatch entries unchanged. **AST-only — never imports from `src/probos/`** (preserves the sandbox property; would break the sandbox guarantee per Hard-Stop #9). **Recursive-validity gate passes**: `./scripts/phantom-api-precheck.ps1 prompts/ad-685b-method-call-validation.md` exits 1 with exactly the 2 documented pre-existing FPs from AD-685 v1 (`runtime.duty_schedule_tracker` audit prose + `class:SomeClass` placeholder), 0 NEW method_phantom flags. **Calibration sweep**: 3 archived post-revision prompts (`ad-641c-ward-room-thread-priority`, `ad-500-dutyscheduler-workitem-migration`, `ad-487-self-distillation-v1`) — 0 phantoms, 0 false positives across all 3 (Hard-Stop tightened from AD-685 v1's `>5 false positives per prompt` to `≥1 false positive` per Section 3 binding definition). **Performance**: 2.26s cold / 2.17s warm per prompt — well under the 10s ceiling (within-process index caching + per-prompt subprocess spawn dominates timing). Tier-3 deferrals: type-shape validation → AD-685c, dataclass/Pydantic field-name validation → AD-685d. Updated 1 existing test (`test_phantom_api_precheck_kwargs.py::test_helper_runs_on_clean_prompt_returns_empty_phantoms`) for the additive `unresolved` field; replaced exact-shape JSON equality with key-by-key access. 11 new focused tests pass at `tests/test_phantom_api_precheck_method_calls.py`: 3 historical-phantom regressions (LLMClient.chat → complete; WorkItemStore.add → create_work_item; EventLog.query → query_structured framing); Pattern A priority-1 AnnAssign resolution + Pattern A priority-2 finalize.py Assign+Call resolution; Pattern B prompt-body constructor assignment; conservative skip with `no_class_resolution` unresolved record; sync + async method walking; dunder exclusion; PowerShell wrapper integration (method_phantom prefix + Skipped section); recursive-validity self-check on AD-685b prompt. Full gate: 10704 passed, 15 skipped (delta +11 vs Wave 14 baseline 10693 — exact target hit). Closes Wave 15.

 - Personal Ontology Map Step. Created new `src/probos/cognitive/self_distillation/` package (owns directory creation; mirrors AD-457/459/466/467/469/475/641a/641b/641c/641e/641f/477 precedents) with `PersonalOntologyProber` (Map-step prober — public API `async start()`/`async stop()`/`async probe_domain(agent_id, domain)`/`async get_recent_probes(agent_id, k=10)`; `ProbeResult` frozen dataclass — `agent_id`/`domain`/`sub_topics`/`confidence_scores`/`raw_text`/`probed_at` (UTC tz-aware); `ProbeLLMError` and `ProbeRateLimitedError` exception classes). v1 ships **1 of 4 capabilities** per convention #14 aggressive pre-deferral: Map step only. Builds an `LLMRequest` from a structured JSON-only self-query template (`.format(domain=..., max_sub_topics=N)` — NOT f-string; doubled braces preserve literal example JSON), calls `await runtime.llm_client.complete(request, priority=Priority.NORMAL)` (verified at `cognitive/llm_client.py:26` BaseLLMClient.complete signature — NOT phantom `chat()`), parses `LLMResponse.content` JSON into typed tuples (slice `[: max_sub_topics]` enforces config-bounded fan-out), persists to new SQLite table `agent_probes` via Wave 5 convention #2 stdlib-only persistence (`connection_factory: ConnectionFactory | None = None` constructor injection with `probos.storage.sqlite_factory.default_factory` fallback; `_db: DatabaseConnection | None` typed against the protocol surface — NOT phantom `ConnectionFactory` field type from pass-1 review R3). 24h sliding-window rate limit per (agent, domain) tuple (uses last-row `probed_at` ISO 8601 timestamp via `datetime.fromisoformat`; pre-LLM check skips spend on rejected probes; emits `ONTOLOGY_PROBE_RATE_LIMITED` then raises `ProbeRateLimitedError` — deferred-not-dropped semantics matching BF-257 pattern). On success emits `ONTOLOGY_PROBE_RECORDED` with `{agent_id, domain, sub_topic_count}` after DB commit. `raw_text` always preserved in `ProbeResult` even on JSON parse failure (Test #12) — sub_topics/confidence fall back to `()`. **Async lifecycle**: `async start()` opens connection + executes `_SCHEMA` (CREATE TABLE/INDEX IF NOT EXISTS — schema idempotent per assignment.py canonical pattern); `async stop()` closes the handle. No `_ensure_schema()` cross-module call (pass-1 Rec1 folded). **Phase function** `async def _wire_self_distillation(*, runtime, config) -> bool` at `startup/finalize.py:90` follows canonical signature verified at `_wire_anomaly_window:25`/`_wire_tiered_knowledge_loader:80`/`_wire_task_context:107`; **defensive `isinstance(config.self_distillation, SelfDistillationConfig)` boundary check** added beyond the prompt body to handle pre-existing tests (`tests/test_new_crew_auto_welcome.py`) that pass `MagicMock` for `config` — without the check, MagicMock makes `.enabled` truthy and `db_path` an unresolvable mock path that aiosqlite cannot open. Other wire functions don't I/O so they tolerate MagicMock; mine does, hence the guard. Async because `start()` is async (precedent in existing `finalize_startup` body). Public attribute `runtime.personal_ontology_prober` (no underscore — Wave 5 convention #1). Late-bind `_emit_event_fn` setter pattern (Wave 5 convention #5). 2 new EventTypes (`ONTOLOGY_PROBE_RECORDED`, `ONTOLOGY_PROBE_RATE_LIMITED`; verified collision-free against `events.py`). `SelfDistillationConfig` Pydantic model wired onto `SystemConfig.self_distillation` (root config class verified at `config.py:1805` — NOT phantom `Config.self_distillation` from pass-1 R2; `enabled=True`, `rate_limit_hours=24`, `llm_timeout_seconds=30.0`, `max_sub_topics=5`, `db_path=Path("data/agent_probes.db")`). v1 ships 1 of 4 capabilities; Collapse step (cluster probes into capability categories) -> AD-487b (forcing function: >=10 probes per agent), Reduce step (`PersonalOntology` data structure) -> AD-487c (depends on AD-487b), Daydream dream type (idle-cycle exploration) -> AD-487d (forcing function: AD-487a/b/c stable + dreaming.py bandwidth), DID portability integration (AD-441) -> AD-487e (depends on AD-487c). **Cross-AD orthogonality preserved**: AD-636 priority lane semaphores + AD-637f priority classification live inside `LLMClient.complete` (llm_client.py:166, 427-457) and throttle LLM tokens by tier; AD-487's prober-layer 24h limit is independent — no integration needed. AD-486 onboarding Phase 3 Self-Discovery integration deferred — v1 callable from anywhere. 16 focused tests pass (1 over the prompt's stated 15 target — added `test_probe_domain_llm_error_raises_probe_llm_error` for the `response.error` path). Full gate: 10693 passed, 15 skipped (delta +16 vs Wave 13 baseline 10677 — exact target +15 hit + 1 boundary test). Closes GH issue #79.

Combo C CLOSED. 5 trivial extensions in single commit (AD-526d/572c/573c/573f/575c). AD-526d: New `src/probos/recreation/preferences.py` with `GamePreferenceTracker` (per-agent per-game-type play frequency; `record_game`/`get_preferences`/`top_game_for` public API; `set_event_callback` late-bind mirroring `BilletRegistry` pattern); wired as `runtime.recreation_preference_tracker` (Wave 5 convention #1 public attribute) with `emit_event` callback bound at __init__. AD-572c: `CaptainEngagementProvider.wardroom_activity_summary()` async helper iterates `await ward_room.list_channels()` and aggregates per-channel `len(await ward_room.list_threads(channel_id, limit=10))` into `{"channels": {...}, "total_threads": N}`; merged into `context["captain_engagement"]["wardroom_activity_summary"]` from `_gather_context` (proactive.py:1175 area); per-channel failures degrade to partial result (Wave-5 tier-2). AD-573c: `[NOTE tag]body[/NOTE]` action tag — extended `cognitive_agent.py:1747` markers dict with `\"note\": re.compile(r'\[NOTE\s', ...)`; mirror notebook_pattern extractor in `proactive.py:2668` calls `runtime.working_memory.add_scratchpad(body)` (Combo A AD-573b helper) and emits `WORKING_MEMORY_NOTE_RECORDED` with `{agent_id, tag, text_len}`. AD-573f: Commitment lifecycle on existing `list[dict[str, Any]]` shape (NOT a Commitment dataclass — Convention #20 reality-check applied to the dict-of-{id, summary, due?, status} shape Combo A actually shipped at `working_memory.py:35,108,138`); added `mark_commitment_complete(commitment_id)` (mutates dict's `status` key in-place; emit COMMITMENT_RECORDED action="complete"; clean no-op on unknown id), `pending_commitments()` (filter excludes status in {done, expired}), `expired_commitments(now)` (filter due<now and status!=done); `add_commitment` extended to emit COMMITMENT_RECORDED action="record"; manager-scoped (NO `agent_id` parameter — no per-agent partition exists); event callback late-bound via new `set_event_callback`. AD-575c: DM forwarding self-reference flag — `_check_unread_dms` scans `dm["body"]` (case-insensitive) for `@<agent_callsign>`; sets `event_data["self_referenced"] = True`; preserves upstream content (read-only). **AD-572d wholesale-deferred to AD-572d-i** (verified: proactive `_think_loop` uses bare `await asyncio.sleep` at lines 475/482/584/782; zero `asyncio.Event`/`wait_for` — interruptible-wait infrastructure absent; forcing function: separate AD must introduce the wait pattern on `_think_loop` first). **AD-573e wholesale-deferred to AD-573e-i** (verified: `cognitive/journal.py` exposes only `record/get_reasoning_chain/get_token_usage[_since|_by]/get_decision_points/get_stats/start/stop/wipe/prune` — no `recent_for_agent`; `get_decision_points` has wrong latency/failure-only semantics; forcing function: cognitive_journal needs recency-ordered per-agent recall API). 3 new EventTypes (`GAME_PREFERENCE_RECORDED`, `WORKING_MEMORY_NOTE_RECORDED`, `COMMITMENT_RECORDED`; verified collision-free). Public attributes `runtime.recreation_preference_tracker` (new) + `runtime.working_memory.set_event_callback` wiring (extends Combo A surface). 19 focused tests pass (4+3+4+5+3 across 5 per-child files). Full gate: 10677 passed, 15 skipped (delta +19 vs Wave-12 baseline 10658 — exact target hit).

AD-477 CLOSED. Naval Organization Protocols v1 (Captain's Log + Plan of the Day). Created new `src/probos/naval/` package (owns directory creation; mirrors AD-457/459/466/467/469/475 precedents) with `CaptainsLogService` (synthesizes daily Markdown narrative from 3 sources — top-N episodes via `runtime.episodic_memory.recent(k=top_episodes_count*4)` over-fetch + Python-side UTC-midnight `[date_start, date_end]` filter + `importance >= threshold` filter + sort by importance desc + take top N; Ward Room thread highlights via `runtime.ward_room.list_threads(channel_id=None, limit=50, sort="recent")` filtered to same-day `last_activity`; active work item summary via `runtime.work_item_store.list_work_items(status="open")` — canonical `WorkItemStatus.OPEN` per `workforce.py:44`) and `PlanOfDayService` (morning summary from 3 sources — active work items via `list_work_items(status="open")`; Ward Room thread queue depth via `list_threads`; current alert conditions via `runtime.bridge_alerts.get_recent_alerts(10)` when `include_alert_conditions=True`). Both services emit on disk-write (`CAPTAINS_LOG_GENERATED` / `PLAN_OF_DAY_GENERATED`); both expose async `start()`/`stop()` with the loop catching `CancelledError` + cleanup + re-raise per ProbOS async-discipline rule (Test #12 `pytest.raises(CancelledError)` enforces non-swallow). All collaborator reads guarded with try/except and log-and-degrade fallback strings — no hard dependency on any optional runtime surface. 2 new EventTypes (`CAPTAINS_LOG_GENERATED`, `PLAN_OF_DAY_GENERATED`; verified collision-free against `events.py`). Pydantic configs: `CaptainsLogConfig`, `PlanOfDayConfig`, `NavalOrganizationConfig` (uses `Field(default_factory=...)` for nested-BaseModel defaults per Wave 5 anti-pattern guidance) wired into `SystemConfig.naval_organization`. Public attributes (no underscore — Wave 5 convention #1; applies to start-tasks too): `runtime.captains_log_service`, `runtime.captains_log_start_task`, `runtime.plan_of_day_service`, `runtime.plan_of_day_start_task`. **Dream-consolidation source deferred to AD-477g** (negative-framing pattern mirroring `duty_schedule_tracker`): `runtime.dreaming_engine` does NOT exist as a public attribute; `runtime.dream_scheduler` exposes no recent-summaries accessor. Forcing function: either runtime exposes `runtime.dreaming_engine` publicly OR `DreamScheduler` adds `recent_consolidation_summaries(...)`. **Scheduled-duties source deferred to AD-477f**: `DutyScheduleTracker` is private to `ProactiveCognitiveLoop` (`self._duty_tracker` at `proactive.py:181`); no `runtime.duty_schedule_tracker` public accessor. Forcing function: public accessor on runtime OR AD-500a-1's `WorkItem(work_type="duty")` query path ships. v1 ships 2 of 6 capabilities × 3 source aggregations each; Qualification Programs → AD-477b (extends AD-566), 3M System → AD-477c, Damage Control Organization → AD-477d, SORM → AD-477e, scheduled-duty integration → AD-477f, dream-consolidation source → AD-477g. 14 focused tests pass. Full gate: 10658 passed, 15 skipped (delta +14 vs Wave 11 baseline 10644 — exact target hit). Closes GH issue #71.

AD-685 CLOSED. Phantom-API Pre-Check Kwarg Shape Validation (Wave 11; tooling hygiene). Extended `scripts/phantom-api-precheck.ps1` (Wave 8 Addendum convention #16) with a Python AST helper (`scripts/phantom_api_ast_helper.py`) that parses every `<obj>.<method>(<kwargs>)` call site in a prompt body and validates kwarg names against live signatures from `src/probos/`. Three documented misses across Waves 9B+10 motivated this: `event_log.query(event_type=...)` (real param `event=` on `query_structured`), `WorkItemStore.get_pending(...)`, `runtime.work_item_store.add(work_item)`. v1 ships 2 capabilities per convention #14: AST-aware kwarg validation + **shared heuristic pre-filter** (resolves Wave 11 pass-1 Required #1 recursive-validity gap). Pre-filter applied uniformly to BOTH the existing symbol-existence check AND the new kwarg check, with whitespace-of-equal-length masking to preserve line numbers: (1) non-Python fenced blocks (pwsh/bash/sh/text/json/bare); only ` ```python ` / ` ```py ` blocks scanned; (2) `## Revision` audit-trail sections; (3) markdown table-row backticked call-shapes; (4) inline-prose backticked call-shapes (broadens #3 to bullet-list and paragraph cites — recursive-validity tuning per Hard-Stop). AST index module-level dict cache (per Recommended #1 promotion); cold build ~1s indexing 2810 method names across 403 Python files; cached subsequent calls ~0.25 ms. Helper-internal heuristics: skip noisy stdlib method names (`format`/`join`/`info`/`execute`/etc.) and noisy receivers (`self`/`cls`/`asyncio`/`logger`/etc.); accept kwarg if any same-named definition matches OR if any candidate accepts `**kwargs`. UTF-8 stdout (Nit #4). PowerShell wrapper categorizes phantoms in display: `<Class>.<method>` / `runtime.X` / `<Class>(...)` / `kwarg_mismatch`. **Recursive-validity gate passes** — `./scripts/phantom-api-precheck.ps1 prompts/ad-685-phantom-precheck-kwarg-validation.md` exits 0 with 0 phantoms after build (was 1 phantom self-reference before). **Calibration sweep:** 5 archived prompts (`ad-641c-*`, `ad-500-*`, `ad-501-*`, `ad-641d-*`, `ad-469-*`) — 0 false positives, 0 true positives (these prompts were repaired before archival; the named ad-641c/ad-500 phantoms are tagged "Applied" / "replaced everywhere" in the archived prompt bodies); regression catch verified via synthetic test cases #2/#3 in `tests/test_phantom_api_precheck_kwargs.py`. **Performance:** ~1.2 s/prompt cold avg (5 prompts in 6.0-6.2 s; well under 5s steady-state target since each prompt invocation is its own Python process under the current wrapper-spawns-helper-once-per-prompt design). 9 focused tests pass: clean prompt produces 0 phantoms; Wave 9B `query(event_type=...)` regression caught; Wave 10 wrong-kwarg regression caught (`list_work_items(payload=...)` → `payload` not in signature); non-Python fenced blocks (pwsh/bash/sh/text/bare) skipped via wrapper pre-filter; `## Revision` sections skipped; kwarg accepted if any same-named definition matches (deferred receiver-class resolution to AD-685c/d); wrapper merges symbol + kwarg phantoms; wrapper exit 1 on kwarg phantom; wrapper shared pre-filter suppresses prose-table cites. Existing symbol-check logic preserved verbatim — only its INPUT changed to `$filteredBody`. v1 ships 2 of 4 capabilities; field-name validation for dataclass/Pydantic constructors → AD-685b, type-shape validation → AD-685c, receiver-class resolution → AD-685d. Full gate: 10643 passed + 1 environmental flake (`test_browse_threads_sort_recent` passes serially — pre-existing flake unrelated to this AD), 15 skipped (delta +9 vs Wave 10 baseline 10635 — exact target hit).

 Producer-only v1 after Wave 10 pass-2 scope reframe (was HIGH-risk; consumer migration deferred to AD-500a-1). Added `DutyScheduleTracker.emit_due_duties_as_work_items(agent_type, work_item_store)` async method at `duty_schedule.py:120` that emits one `WorkItem(work_type="duty", ...)` per due `DutyDefinition` via `await work_item_store.create_work_item(work_type="duty", assigned_to=agent_type, title=duty.description or duty.duty_id, metadata={"duty_id": ..., "agent_type": ...})` (real API at `workforce.py:992`; `metadata=` is the actual `WorkItem` field — verified at `workforce.py:583`, NOT phantom `payload=`). Constructor signature unchanged in v1 — dependency injected at call time per Wave 5 convention #5. **TYPE_CHECKING guard** for `WorkItemStore` import to prevent circular import with `workforce.py` (`from typing import TYPE_CHECKING; if TYPE_CHECKING: from probos.workforce import WorkItemStore`). Added `DutyScheduleConfig.use_work_items: bool = False` opt-in flag at `config.py:1401` per convention #14 (default False — zero behavior change at first commit; flips to True in AD-500a-1). **Section 1 verify-only** — `duty` work type already registered at `workforce.py:206` via AD-498; no implementation needed. **Section 4 verify-only** — producer reuses existing `EventType.WORK_ITEM_CREATED` (events.py:87) via `WorkItemStore._emit` at `workforce.py:1051`; no new EventType (pass-1 review Recommended #1 dropped redundant `DUTY_WORK_ITEM_CREATED`). **proactive.py UNTOUCHED in v1** (zero diff confirmed; all 6 `record_execution` call sites at `:817/887/900/913/930/1050` + `_think_for_agent` self-selection at `:715` deferred to AD-500a-1's forcing function). Producer does NOT call `record_execution` — verified by `test_emit_due_duties_does_not_call_record_execution`. 9 focused tests pass (3 verify-only flag/registry checks + 6 producer-behavior tests covering happy path, ID return, event reuse, empty-due, no-record_execution, description-fallback). v1 ships 2 of 5 capabilities; consumer-side proactive loop migration → AD-500a-1, AD-498 templates → AD-500b, default-duty config migration + flag removal → AD-500c. Full gate: 10634 passed + 1 environmental flake (`test_trust_dampening` passes serially), 15 skipped (delta +9 vs post-AD-501 baseline; my +9 vs prompt's stated +6 reflects 3 extra boundary tests for use_work_items=True / description-fallback / no-record_execution). Closes GH issue #87 partially (consumer migration tracked under AD-500a-1).

AD-501 CLOSED. TaskTracker Deprecation & NotificationQueue Separation. Moved `NotificationQueue` + `AgentNotification` from `task_tracker.py` to new `src/probos/notifications.py` (move-only; behavior preserved). Deleted `src/probos/task_tracker.py` (orphaned `TaskType`/`StepStatus`/`TaskStatus`/`TaskStep`/`AgentTask`/`TaskTracker`). Removed `task_tracker` field from `ProbOSRuntime` dataclass + `__init__` + `build_state_snapshot()` (`"tasks"` key dropped) + warm-boot restore line. Beyond the prompt's verify-first footer, also removed startup-wiring touchpoints the footer missed: `startup/structural_services.py` (TaskTracker construction + struct field), `startup/results.py` (TaskTracker import + `StructuralServicesResult.task_tracker`), `startup/shutdown.py` (task_tracker stop block) — all logically required by the field removal. Updated 3 import sites: `runtime.py:69`, `tests/conftest.py:158`, `tests/test_notifications.py:7` (1-line edit each). Test triage: deleted entire `tests/test_task_tracker.py` (30 orphan-class tests under `TestTaskStep`/`TestAgentTask`/`TestTaskTracker`); augmented existing `tests/test_notifications.py` with 8 new AD-501 migration-invariance tests under `TestAD501Migration` (module-importable, behavior-preserved post-move, runtime no longer carries `task_tracker` annotation, `build_state_snapshot()` source has no `"tasks"` key, `probos.task_tracker` raises `ModuleNotFoundError`, conftest spec-mock pattern still works). 12 retained + 8 new = 20 tests pass. Routers UNTOUCHED — `routers/deps.py:23 get_task_tracker` injects FastAPI `app.state.track_task` callable (unrelated naming overlap with the deprecated class). BuildQueue migration to WorkItems deferred to AD-501b. Full gate: 10626 passed, 15 skipped (delta -22 = +8 new − 30 deleted, exactly per spec). Closes GH issue #88.

 Created new `src/probos/cognitive/deliberation/` package (owns directory creation; mirrors AD-457/459/466/467/469/475/641a/641b/641c/641e/641f precedents) with `DeliberationProtocol` (Captain-resolved coordinator; public API `initiate`/`submit_argument`/`resolve`/`get_session`; in-memory `_sessions` map with Ward Room thread as durable record), `DeliberationSession`/`DeliberationArgument` (frozen dataclasses), `DeliberationPhase` enum (v1 reachable states only: `ARGUE`/`RESOLVED`; `OPEN`/`ENDORSE` deferred along with the transitions that would set them), `DeliberationOutcome` enum (`PENDING`/`ADOPTED`/`REJECTED`/`DEFERRED`; `PENDING` is initial sentinel only — `resolve(outcome=PENDING)` returns `None`). **Distinct from `QuorumEngine`** at `consensus/quorum.py:21` — QuorumEngine is mechanical (confidence-weighted votes among tool agents for destructive ops); DeliberationProtocol is judgment-level (structured argument turns; Captain resolves). **Captain identity** via case-insensitive callsign equality (same v1 convention as BF-257 + AD-499 ShipNamingPolicy canonical "Captain"); non-Captain `resolve()` returns `None` with session unchanged. **Idempotent resolve**: second `resolve()` after RESOLVED returns existing session unchanged (no overwrite). **Best-effort Ward Room persistence** via `create_thread`/`create_post` (verified signatures at `ward_room/service.py:357`/`400`); all six Ward Room + emit_event call sites use `logger.warning(..., exc_info=True)` log-and-degrade per the three-tier exception model. Inline import `from probos.cognitive.deliberation import DeliberationProtocol` at finalize.py wiring block matches the sibling pattern at lines 730 (AD-641a), 753 (AD-641b), 767 (AD-641f), 789 (AD-641e), 810 (AD-641c). 3 new EventTypes (`DELIBERATION_INITIATED`, `DELIBERATION_ARGUMENT_SUBMITTED`, `DELIBERATION_RESOLVED`), `DeliberationConfig` (Pydantic; `enabled=True`, `captain_callsign="Captain"`; channel is per-call parameter `initiate(channel_id="deliberation")`, configurable per-deployment surface deferred), public attribute `runtime.deliberation_protocol` (or `None` when disabled). **v1 isolation preserved**: zero direct calls into Wave 9A/9B artifacts (no `runtime.observability_bridge`/`ward_room_hebbian_router`/`thread_priority_service` dependencies). v1 ships 3 of 8 capabilities; multi-Captain quorum → AD-641d-i, Counselor mediation hook → AD-641d-ii, structured argument schema (claim/evidence/counter) → AD-641d-iii, Hebbian feedback to deliberation invitations → AD-641d-iv, endorsement bridge to `WardRoomService.endorse` → AD-641d-v (capture `WardRoomPost.id` from `create_post` and forward to `endorse(target_id=, target_type="post", voter_id=, direction="up")` per verified signature at `service.py:412`). **Closes AD-641 umbrella (issue #277).** Sister sub-ADs already shipped: AD-641a (Wave 9A, commit 4476091), AD-641b (Wave 9A, commit a56b6c6), AD-641c (Wave 9B, commit c9860c5), AD-641e (Wave 9A), AD-641f (Wave 9A). 15 focused tests pass.

AD-641c CLOSED. Ward Room Thread Priority v1 (Importance Scoring Parallel to Attention Manager). Created new `src/probos/cognitive/thread_priority/` package (owns directory creation; mirrors AD-457/459/466/467/469/475/641a/641b/641e/641f precedents) with `ThreadPriorityScorer` (pure deterministic value-class — no I/O, no event emission; 5 weighted factors: captain involvement +0.30, unresolved-question marker via `?` in latest 3 post bodies +0.20, cross-department thread requires ≥2 distinct departments +0.15, thread age via 24h-half-life exponential decay up to +0.20, endorsement density via diminishing-returns `1 - exp(-0.5 * count)` up to +0.15; final score clamped `[0.0, 1.0]`), `ThreadPriorityInput`/`ThreadPriorityScore` (frozen dataclasses), and `ThreadPriorityService` (runtime adapter; `async get_priority(thread_id)` + `async top_priorities(channel_id, k=10)` public API; emits `THREAD_PRIORITY_SCORED` per scoring). **Wave 9A pattern compliance verified at every call site:** `event_log.query_structured(event=EventType.WARD_ROOM_ENDORSEMENT.value, limit=200)` (NOT `query(event_type=...)`); `_count_endorsements` is `async def` and awaited in `_build_input`; row reads use `entry["data"]` dict subscript (verified at `event_log.py:249-262`); `_extract_posts` recursively walks roots → children for the tree-shape returned by `WardRoomService.get_thread` (`{"thread": ..., "posts": roots_with_nested_children, "total_post_count": ...}` per `threads.py:716-748`); cross-department factor resolves department per-author via `resolve_author_department(author_id)` from `ward_room/_helpers.py:11` (post dicts have NO `department` key — verified at `threads.py:727-734`). Captain identity via case-insensitive callsign match (consistent with BF-257 + AD-499 ShipNamingPolicy canonical "Captain"); identity-based check is a known soft coupling tagged in "What This Does NOT Change" item #6 alongside the `WardRoomService.get_thread(**kwargs)` splat. 1 new EventType (`THREAD_PRIORITY_SCORED`), `ThreadPriorityConfig` (Pydantic; per-factor weight knobs + `captain_callsign="Captain"`), public attribute `runtime.thread_priority_service` (or `None` when disabled). v1 ships 5 of 8 capabilities; HXI rendering of priority indicators → AD-641c-i, auto-archival of low-priority threads → AD-641c-ii, Hebbian-fed priority bump (depends on AD-641b listener) → AD-641c-iii. `AttentionManager` and `ThreadManager` unchanged (Open/Closed; verified by `tests/test_ward_room.py` + `tests/test_ward_room_dms.py` 113/113 pass + 1 pre-existing time-based flake in `test_browse_threads_sort_recent` that passes in isolation, unrelated to this AD). 16 focused tests pass.

 LearnedShortcut Shared Abstraction v1 (Protocol over WorkflowCache; Cognitive JIT adapter genuinely-deferred). Created new `src/probos/cognitive/learned_shortcuts/` package (owns directory creation; mirrors AD-457/459/466/467/469/475/641a/641b/641f precedents) with `LearnedShortcutBackend` Protocol (`@runtime_checkable`; 5 members — `kind` + `size` properties, `lookup`/`store`/`evict` methods; structural typing per design doc Category C "could share a common storage abstraction without merging logic"), `WorkflowCacheBackend` adapter (thin wrapper around the existing `WorkflowCache` at `cognitive/workflow_cache.py:17`; `kind="workflow_cache"`; `evict()` returns `False` in v1 — `WorkflowCache` lacks a public evict surface and AD-641e-ii will add cross-backend eviction), and `LearnedShortcutRegistry` coordinator (read-side fan-out only — `lookup_first(key)` walks backends in registration order and returns the FIRST non-None hit, no merging across separate stores; idempotent `register()` rejects duplicate `kind`; emits `LEARNED_SHORTCUT_REGISTERED`/`LEARNED_SHORTCUT_HIT`). Existing `WorkflowCache` source unchanged (Open/Closed; verified by 23/23 `tests/test_workflow_cache.py` regression + new `test_existing_workflow_cache_public_api_unchanged` mechanical guard). 2 new EventTypes (`LEARNED_SHORTCUT_REGISTERED`, `LEARNED_SHORTCUT_HIT`), `LearnedShortcutsConfig` (Pydantic; `enabled=True`, `register_workflow_cache=True`), public attribute `runtime.learned_shortcut_registry` (or `None` when disabled). v1 ships 3 of 6 capabilities; **Cognitive JIT adapter (`CognitiveJITBackend`) genuinely-deferred to AD-641e-i** until the JIT service lands (`grep -rn "cognitive_jit\|CognitiveJITService" src/probos/` returns zero matches today — shipping a JIT adapter now would be a permanent no-op per convention #7). Cross-store eviction policy → AD-641e-ii (depends on `WorkflowCache.evict()` addition). SQLite-persisted backend → AD-641e-iii. 14 focused tests pass.

 Engineering Chief Ship's Systems Observability v1 (Sensor Bundle for LaForge). Created new `src/probos/cognitive/engineering_sensors/` package (owns directory creation; mirrors AD-457/459/466/467/469/475/641a/641b precedents) with `EngineeringSensorBundle` (frozen dataclass — `pool_summary`/`capability_summary`/`gossip_summary`/`captured_at`; the structured Category-D observation surface for the Engineering tier per AD-641 design doc Category B+D "LaForge exception") and `EngineeringSensorService` (read-only coordinator with `take_snapshot()`/`report()`/async `start()`/async `stop()` public API; named `asyncio.Task` instance reference per Wave 5 task-reference convention #18). v1 wires 3 of 7 sensors: pool-summary (per-pool `current_size`/`target_size` per AD-467 precedent), capability-summary (`agent_count` + sorted intent set flattened from `CapabilityDescriptor.can` across `get_all_capabilities()` — verified at `mesh/capability.py:33` that registry is keyed by agent_id with descriptor lists, NOT by intent strings; revision repaired pass-1 R1), gossip-summary (`view_size` + peer count from `get_view()`). `report()` is one-shot single-emit; `start()` enables periodic `report()` on `report_interval_seconds` cadence (clamped to 1.0s minimum, negative inputs included per pass-1 N1). v1 ships periodic-emit machinery dormant: `auto_start_periodic_report=False` default; operators flip the flag once they want cadence (no-theater discipline — single-shot `report()` works regardless). Emit failures swallowed (log-and-degrade tier). 1 new EventType (`ENGINEERING_SENSOR_REPORT`), `EngineeringSensorsConfig` (Pydantic; `enabled=True`, `report_interval_seconds=60.0`, `auto_start_periodic_report=False`), public attributes `runtime.engineering_sensor_service` + `runtime.engineering_sensor_start_task` (latter only when `auto_start_periodic_report=True`). v1 ships 3 of 7 capabilities; per-peer gossip introspection → AD-641f-i, capability-registry mutation surface → AD-641f-ii, cross-pool failover proposal → AD-641f-iii, richer report payload (per-pool sizes, intent breakdown) → AD-641f-iv. `EngineeringAgent` unchanged — instructions wiring is a future grandchild AD; agent reads `runtime.engineering_sensor_service.take_snapshot()` voluntarily once wired. 13 focused tests pass.

AD-641b CLOSED. Ward Room Hebbian Learning v1 (Topic↔Crew Weight Network — Router Only). Created new `src/probos/cognitive/ward_room_hebbian/` package (owns directory creation; mirrors AD-457/459/466/467/469/475/641a precedents) with `WardRoomHebbianRouter` (in-memory `(topic, agent_id) -> weight` Hebbian instance; conceptually parallel to mesh `HebbianRouter` at `routing.py:39` but deliberately divergent API shape — mesh routes `(source→target)` co-activation via `record_interaction`/`decay_all` while Ward Room routes `(topic→agent)` contribution via `record_contribution`/`decay`; documented divergence per AD-641 design doc Category C parallel-systems pattern). Public API: `record_contribution(topic, agent_id, signal=1.0)`, `get_weight(topic, agent_id)`, `top_contributors(topic, k=5)` (filters zero-weight entries per pass-1 R1 so decayed topics don't surface ghost contributors), `decay()`, `weight_count` property. Hebbian potentiation: `new_weight = clamp(0.0, 1.0, current + learning_rate * signal)` (default `learning_rate=0.10`). Empty topic/agent rejected to 0.0 — no theater weights created. 2 new EventTypes (`WARD_ROOM_HEBBIAN_UPDATED`, `WARD_ROOM_HEBBIAN_DECAYED`), `WardRoomHebbianConfig` (Pydantic; `enabled=True`, `learning_rate=0.10`, `decay_factor=0.99`), public attribute `runtime.ward_room_hebbian_router`. **Listener wholesale-deferred to AD-641b-iv per Wave 8 AD-575b precedent + pass-1 review Required #1**: ProbOS has no event-bus subscribe API today (`grep -rn "event_log\.subscribe\|register_handler\|add_event_handler\|subscribe_event" src/probos/` returns zero matches), so a `WardRoomEndorsementListener` would have shipped as dead code (convention #7 violation). v1 signal source is direct `record_contribution()` invocation; listener ships when ProbOS introduces a generic event-bus subscribe mechanism OR when the emit-side at `src/probos/ward_room/messages.py:597` is modified to call the listener directly. v1 ships 2 of 6 capabilities; persistent storage → AD-641b-i, `WardRoomRouter` integration → AD-641b-ii, adaptive decay cadence → AD-641b-iii, endorsement→Hebbian listener → AD-641b-iv. Mesh `HebbianRouter` instance unchanged (verified by `tests/test_hebbian_social.py` + `tests/test_hebbian_source_key.py` regression — 10/10 pass). 11 focused tests pass.

AD-641a CLOSED. Observability Bridge v1 (Brain Sensors → Ward Room System Feeds). Created new `src/probos/cognitive/observability/` package (owns directory creation; mirrors AD-457/459/466/467/469/475 precedents) with `ObservabilityBridge` (read-only sensor coordinator with `start()`/`stop()`/async `take_snapshot()` public API; named `asyncio.Task` instance reference per Wave 5 task-reference convention #18) and `ObservabilityBridgeSnapshot` (frozen dataclass — `vitals_summary`/`pool_health`/`attention_priorities`/`captured_at`; the public observation surface so crew read flat data, not raw runtime internals). v1 wires 3 of 7 sensors: vitals (latest `vitals_monitor` heartbeat from `event_log.query_structured(event=AGENT_STATE.value, limit=20)` — verified async at `event_log.py:170`, dict rows with `data` key per `_row_to_dict` at `event_log.py:249`), pool health (`runtime.spawner.pools[*].current_size`/`target_size` per AD-467 precedent), attention priorities (top-5 by score from `runtime.attention._queue` — note: live attribute is `runtime.attention` NOT `runtime.attention_manager`, verified at `runtime.py:359`; private-attribute reach tagged with `# AD-641a-iv` TODO so the smell is tracked, not hidden). Push-based: bridge polls at configurable cadence (`publish_interval_seconds=60.0` clamped to 1.0 minimum), posts to Ward Room `system_observability` channel via `WardRoomService.create_post`, emits `OBSERVABILITY_SNAPSHOT_PUBLISHED` per cycle. `_publish_once` traps `create_post` exceptions and emits `OBSERVABILITY_BRIDGE_FAILED` (log-and-degrade tier; loop continues next cycle). 2 new EventTypes (`OBSERVABILITY_SNAPSHOT_PUBLISHED`, `OBSERVABILITY_BRIDGE_FAILED`), `ObservabilityBridgeConfig` (Pydantic; `enabled=True`, `publish_interval_seconds=60.0`, `system_channel="system_observability"`), public attributes `runtime.observability_bridge` + `runtime.observability_bridge_start_task` (Wave 5 convention #1 — both consumer-facing). v1 ships 3 of 7 capabilities; Hebbian-weight feed → AD-641a-i (needs public Hebbian read API), HXI surfaces → AD-641a-ii, Captain alert routing on threshold breach → AD-641a-iii, `AttentionManager.snapshot()` public API → AD-641a-iv (drops the `_queue` reach). 14 focused tests pass.

BF-257 CLOSED. DM Receive Rate Limiter — break ping-pong loops at the proactive receive layer. Two-layer sliding-window rate limiter on `ProactiveCognitiveLoop` (`proactive.py:146`): Layer 1 per-agent global budget (`dm_response_budget=6` per `dm_response_window_seconds=600.0` 10-minute window); Layer 2 per-pair bidirectional budget (`dm_pair_exchange_budget=8` per window — pair key is `":".join(sorted([a, b]))` so A->B and B->A share one counter). New `_dm_response_budget_exceeded(agent_id, partner_id, config)` synchronous in-memory check with lazy per-call pruning of expired timestamps. `_check_unread_dms` gated: Captain DMs (callsign-based check, case-insensitive — v1 acceptable per AD-499 ShipNamingPolicy + BF-244 callsign sync; identity-based check deferred to BF-257b once captain DID is canonical) bypass the gate; throttled DMs are NOT added to `_notified_dm_threads` so they retry after window expires (deferred-not-dropped semantics). Budget timestamps recorded after the gate passes and before routing. `dm_exchange_limit` lowered 40 -> 15 (BF-200 raised 5->40; 40 was excessive — 15 still allows substantive multi-agent DM threads). 4 existing tests updated (test_ad614 default-value assertion, test_bf200 method-name + audit-trail docstring, test_unread_dms fixture override + assertion). 14/14 focused tests pass; 184/184 adjacent regression tests pass.

AD-449 CLOSED. MCP Bridge v1 OSS Infrastructure (Session + Client + Bridge + Adapter; EgressPolicy-gated). Created new `src/probos/integrations/` + `src/probos/integrations/mcp_bridge/` packages (owns directory creation; mirrors AD-457/459/466/467/475/469 precedents) with `MCPSession` (frozen dataclass — `server_url`/`session_id`/`headers`/`capabilities`; immutability via `dataclasses.replace`), `MCPClient` (JSON-RPC 2.0 over Streamable HTTP via `httpx.AsyncClient`; **`_last_response_headers` is INSTANCE attribute initialized in `__init__`** per Wave 8 second-pass review — class-level mutable default would race across MCPClient instances; `initialize`/`list_tools`/`call_tool` public API; `_call` gates URL via `runtime.egress_policy.is_allowed(url)` before any HTTP issuance — emits `MCP_BRIDGE_FAILED` with `reason="egress_blocked"` then raises `MCPProtocolError`; HTTP non-2xx and JSON-RPC `error` payloads emit `MCP_BRIDGE_FAILED` with `reason="http_error"`/`reason="rpc_error"`), `MCPBridge` (server registry — `register_server` rejects empty/duplicate URLs; `invoke(server_url, tool, args)` raises `MCPProtocolError` on unknown server; routes to per-server `MCPClient`), `MCPToolAdapter` (`name` property — `mcp.<sanitized_host>.<tool>` format from `urllib.parse.urlparse`; `__call__` proxies to bridge; ToolRegistry registration deferred to AD-449b per Solution Overview). 2 new EventTypes (`MCP_BRIDGE_INVOKE`, `MCP_BRIDGE_FAILED`), `MCPConfig` + `MCPServerConfig` (Pydantic; `enabled=True`, `request_timeout_seconds=30.0`, `servers: list[MCPServerConfig] = []`), public attribute `runtime.mcp_bridge`. **Commercial-boundary discipline:** OSS bridge infrastructure ships in v1; vendor-specific MCP server packs live in the private commercial repo entirely (AD-450 leak precedent). v1 ships 2 of 6 capabilities; ProbOS-as-MCP-server (Tool Registry registration via SkillCatalog) → AD-449b, MCP `sampling/createMessage` callback → AD-449c, OAuth 2.1 + DCR + dynamic scope → AD-449d, multi-tenant audit + observability → AD-449e. 14 focused tests pass.

Wave 5-8 architect decisions 2026-04-30: **AD-443 deferred** from Wave 5 to Wave 9C (or later) — mobility protocol's `verify_remote_chain` and slot-reassignment paths require AD-479 federation infrastructure that hasn't shipped; building in Wave 5 would land a half-feature. Wave 5 fifth slot is AD-499 (Ship & Crew Naming Conventions). **AD-641 split** scheduled as Wave 8.5 meta-prompt — umbrella ADs (AD-450, AD-462, AD-641) consistently produce poor builds when absorbed directly; AD-641 splits into 641a/b/f (Wave 9A, parallel-safe), 641c/e (Wave 9B, cross-cutting), 641d (Wave 9C, high-risk). Convention adopted: **umbrella ADs must be split into sub-AD prompts before scheduling, never built directly.** See `prompts/WAVE-5-8-RECONCILED-PLAN.md`.

AD-484 CLOSED. UX & Adoption Readiness v1. Repo-level + CLI work: extended `pyproject.toml` with `Development Status :: 4 - Beta` classifier + `[project.urls]` block (Homepage/Documentation/Repository/Issues); license-classifier conflict resolved (SPDX `license = "Apache-2.0"` at `pyproject.toml:10` is canonical; PEP-639 setuptools >=77 prefers SPDX-only). Added `MANIFEST.in`. Rewrote `_cmd_init` (`__main__.py:542+`) as Rich TUI wizard with `_detect_llm_providers` probe (Ollama / Copilot proxy / Anthropic; respects `ANTHROPIC_BASE_URL`). Added `_cmd_doctor` diagnostic command (5 checks: config / data_dir / LLM tiers / NATS / ChromaDB) + `doctor` subparser + dispatch branch returning non-zero on failure. Added `docs/quickstart.md` and `docs/getting-started.md`. No new pyproject deps; reuses Rich (`pyproject.toml:25`). Homebrew → AD-484b, `probos demo` mock mode → AD-484b, HXI Glass → AD-484c, Playwright Browser Automation → AD-484c. 10 focused tests pass.

Combo A CLOSED. 7 trivial extensions in single commit (AD-538b/572b/573b/576b/526c/655/656). AD-538b: `DreamManifest` stdlib JSON-backed skip-already-processed marker survives restart; integrated into `DreamingEngine.micro_dream` (line 54) — episodes with `manifest.is_processed(ep_id, "consolidate") == True` skipped before replay; emit `DREAM_MANIFEST_UPDATED` per replay batch. AD-572b: `CaptainEngagementProvider` (`cognitive/captain_engagement.py`) snapshots `alerts_pending` / `wardroom_activity_60s` / `dm_queue_depth` into proactive context; emits `CAPTAIN_DM_PRIORITY_QUEUED` on depth-change. AD-573b: 3 new fields on `WorkingMemorySnapshot` (`relational_links`, `scratchpad`, `commitments`); 3 new bounded-ring helpers on `WorkingMemoryManager` (`record_relation`, `add_scratchpad`, `add_commitment`). AD-576b: `proactive.py:694` retry block — 2 attempts at `[0.5, 1.5]s` backoff before incrementing failure counter; only retries on transient LLM errors (the existing `_LLM_ERROR_KEYWORDS` set). AD-526c: `recreation/metadata.py` `GameMetadata` dataclass; `register_engine(engine, metadata=None)` extended with optional metadata + emits `RECREATION_GAME_REGISTERED`; `default_game` Captain-preference property; existing `register_engine`/`get_available_games`/`_engines` preserved (no DRY violation). AD-655: `EpisodicMemory.retrieve_contrastive_episodes(query, k)` returns mid-band `[0.4, 0.65]` similarity episodes; emits `CONTRASTIVE_RECALL`; `EvaluateHandler.__call__` populates `context["_contrastive_priors"]` when `runtime.episodic_memory` is wired. AD-656: `DepartmentCognitiveProfile` + `DepartmentProfilesConfig` Pydantic classes; `EvaluateHandler.__call__` overrides recall depth from per-department profile; emits `DEPT_PROFILE_APPLIED`. **AD-575b dropped wholesale** (theater per convention #7): `runtime.self_summary_provider` does not exist; both halves of AD-575b would have been permanent no-ops. Awaits a future AD that ships the upstream surface with a real consumer. 5 new EventTypes (`DREAM_MANIFEST_UPDATED`, `CAPTAIN_DM_PRIORITY_QUEUED`, `RECREATION_GAME_REGISTERED`, `CONTRASTIVE_RECALL`, `DEPT_PROFILE_APPLIED`). Public attributes `runtime.dream_manifest` + `runtime.captain_engagement_provider`. 26 focused tests pass.

AD-469 CLOSED. EPS Compute/Token Distribution v1 (Capacity + Budgets + Captain Override). Created new `src/probos/cognitive/eps/` package (owns directory creation; mirrors AD-457/459/466/467/475 precedents) with `CapacityTracker` (rolling-window aggregator over `runtime.cognitive_journal.get_token_usage_by(group_by=...)`; returns frozen `CapacitySummary` with by-agent/tier/model maps; defensive `getattr(self, "_runtime", None)` for __new__-bypass tests per convention #11), `DepartmentBudgetTable` (proportional renormalization on Captain override; `set_override`/`clear_override` round-trip), and `EPSCoordinator` (composes capacity + budgets; `report()` returns `EPSReport`; `override(name, percent)` emits `EPS_REALLOCATION`). 2 new EventTypes (`EPS_BUDGET_EXCEEDED`, `EPS_REALLOCATION`), `EPSConfig` + `EPSDepartmentConfig` (Pydantic; 6 default departments summing to 1.00), public attribute `runtime.eps_coordinator`. **No `CognitiveJournal` schema modifications** — reads `get_token_usage_by` (verified at `journal.py:299`). v1 ships 3 of 7 capabilities; alert-aware reallocation → AD-469b, back-pressure → AD-469c, atomic enforcement → AD-469d, prompt caching → AD-469e. `check_budgets()` v1 contract returns `[]` (honest deferral; AD-469b ships the agent->department resolver). 13 focused tests pass.

AD-472 CLOSED. Channel Adapters v1 (Discord enhancements + Slack + Webhook). Section 1 shipped Discord Message Content Intent verification at startup (`channels/discord_adapter.py:89+`); Section 1a allowed_user_ids enforcement was already in the right position (lines 271-277, no edit needed); Section 1b (fetch_messages reconnection recovery) wholesale-deferred to AD-472b per convention #7 (no `fetch_messages`/`on_disconnect` paths exist today). Created `src/probos/channels/slack_adapter.py` (`SlackAdapter` with real `auth_test` verification at start; `chat_postMessage` with `thread_ts` support; opt-in via `slack-sdk>=3.21,<4` extra) and `src/probos/channels/webhook_adapter.py` (`WebhookAdapter` with shared-secret verification; `send_response` is documented no-op in v1). `SlackConfig` + `WebhookConfig` defined ONCE in `config.py` (mirroring `DiscordConfig` precedent); adapters import the types. Reordered configs BEFORE `ChannelsConfig` so direct defaults bind without forward refs. 2 new EventTypes (`CHANNEL_MESSAGE_RECEIVED`, `CHANNEL_DELIVERY_FAILED`). Slack + Webhook startup wiring in `__main__.py:451+` mirrors the Discord precedent. v1 ships 3 of 7 adapters; Telegram/WhatsApp/Matrix/Teams + FastAPI route deferred to AD-472b/c/d. 11 focused tests pass + 1 skipped (Discord intent test skips when discord.py absent).

Combo A CLOSED. 7 trivial extensions in single commit (AD-538b/572b/573b/576b/526c/655/656). AD-538b: `DreamManifest` stdlib JSON-backed skip-already-processed marker survives restart; integrated into `DreamingEngine.micro_dream` (line 54) — episodes with `manifest.is_processed(ep_id, "consolidate") == True` skipped before replay; emit `DREAM_MANIFEST_UPDATED` per replay batch. AD-572b: `CaptainEngagementProvider` (`cognitive/captain_engagement.py`) snapshots `alerts_pending` / `wardroom_activity_60s` / `dm_queue_depth` into proactive context; emits `CAPTAIN_DM_PRIORITY_QUEUED` on depth-change. AD-573b: 3 new fields on `WorkingMemorySnapshot` (`relational_links`, `scratchpad`, `commitments`); 3 new bounded-ring helpers on `WorkingMemoryManager` (`record_relation`, `add_scratchpad`, `add_commitment`). AD-576b: `proactive.py:694` retry block — 2 attempts at `[0.5, 1.5]s` backoff before incrementing failure counter; only retries on transient LLM errors (the existing `_LLM_ERROR_KEYWORDS` set). AD-526c: `recreation/metadata.py` `GameMetadata` dataclass; `register_engine(engine, metadata=None)` extended with optional metadata + emits `RECREATION_GAME_REGISTERED`; `default_game` Captain-preference property; existing `register_engine`/`get_available_games`/`_engines` preserved (no DRY violation). AD-655: `EpisodicMemory.retrieve_contrastive_episodes(query, k)` returns mid-band `[0.4, 0.65]` similarity episodes; emits `CONTRASTIVE_RECALL`; `EvaluateHandler.__call__` populates `context["_contrastive_priors"]` when `runtime.episodic_memory` is wired. AD-656: `DepartmentCognitiveProfile` + `DepartmentProfilesConfig` Pydantic classes; `EvaluateHandler.__call__` overrides recall depth from per-department profile; emits `DEPT_PROFILE_APPLIED`. **AD-575b dropped wholesale** (theater per convention #7): `runtime.self_summary_provider` does not exist; both halves of AD-575b would have been permanent no-ops. Awaits a future AD that ships the upstream surface with a real consumer. 5 new EventTypes (`DREAM_MANIFEST_UPDATED`, `CAPTAIN_DM_PRIORITY_QUEUED`, `RECREATION_GAME_REGISTERED`, `CONTRASTIVE_RECALL`, `DEPT_PROFILE_APPLIED`). Public attributes `runtime.dream_manifest` + `runtime.captain_engagement_provider`. 26 focused tests pass.

AD-475 CLOSED.Captain's Ready Room v1 (Idea Capture + 3-phase Sessions). Created new `src/probos/cognitive/ready_room/` package (owns directory creation; mirrors AD-457/459/466/467 precedents) with `IdeaCaptureStore` (stdlib JSON-backed idea queue; atomic write via `os.replace`; emits `IDEA_CAPTURED` per `capture()`; status transitions `open` -> `in_session` -> `resolved | deferred`) and `ReadyRoomSessionManager` (3-phase `PRESENT` -> `DISCUSS` -> `CONVERGE`; idempotent at terminal; creates Ward Room thread via `runtime.ward_room.create_thread`; `journal_correlation_id` set for AD-475c journal-write deferral; emits `READY_ROOM_SESSION_STARTED`). 2 new EventTypes (`READY_ROOM_SESSION_STARTED`, `IDEA_CAPTURED`), `ReadyRoomConfig` (Pydantic), public attributes `runtime.idea_capture_store` + `runtime.ready_room_session_manager`. v1 ships only Idea Capture + Sessions; TOGAF Architecture Hierarchy → AD-475b, 5-phase discussion → AD-475c, Idea→Spec pipeline → AD-475d. 13 focused tests pass.

AD-484 CLOSED. UX & Adoption Readiness v1. Repo-level + CLI work: extended `pyproject.toml` with `Development Status :: 4 - Beta` classifier + `[project.urls]` block (Homepage/Documentation/Repository/Issues); license-classifier conflict resolved (SPDX `license = "Apache-2.0"` at `pyproject.toml:10` is canonical; PEP-639 setuptools >=77 prefers SPDX-only). Added `MANIFEST.in`. Rewrote `_cmd_init` (`__main__.py:542+`) as Rich TUI wizard with `_detect_llm_providers` probe (Ollama / Copilot proxy / Anthropic; respects `ANTHROPIC_BASE_URL`). Added `_cmd_doctor` diagnostic command (5 checks: config / data_dir / LLM tiers / NATS / ChromaDB) + `doctor` subparser + dispatch branch returning non-zero on failure. Added `docs/quickstart.md` and `docs/getting-started.md`. No new pyproject deps; reuses Rich (`pyproject.toml:25`). Homebrew → AD-484b, `probos demo` mock mode → AD-484b, HXI Glass → AD-484c, Playwright Browser Automation → AD-484c. 10 focused tests pass.

AD-466 CLOSED.Engineering Infrastructure -- Backup & StorageBackend ABC. Created new `src/probos/infrastructure/` package (owns directory creation; mirrors AD-455/457/459 precedents) with `BackupService` (timestamped SQLite snapshots via stdlib `sqlite3.Connection.backup` online API; falls back to `shutil.copyfile` on SQLite errors; emits `BACKUP_COMPLETE`/`BACKUP_FAILED`) and `StorageBackend` ABC + `SQLiteStorageBackend` (typed seam over existing `ConnectionFactory` Protocol; PostgreSQL implementation deferred to AD-466b). 2 new EventTypes (`BACKUP_COMPLETE`, `BACKUP_FAILED`), `InfrastructureConfig` (Pydantic), public attributes `runtime.storage_backend` + `runtime.backup_service` (always-wired with `try/except OSError` mkdir guard per Wave-5 convention #1). v1 ships only Backup + StorageBackend; CI/CD changes deferred to AD-466d, observability extensions to AD-466c, operations runbook deferred. 12 focused tests pass.

AD-456 CLOSED. Security Infrastructure (Secrets / Egress / Audit). Extended existing `CredentialStore` (AD-395 at `credential_store.py:32`) with kw-only `store_path` and `emit_event` constructor kwargs, four additive methods (`_load_store`, `_resolve_from_store`, `rotate`, `_emit_rotated`), and a JSON-store step in `_resolve` between env-var aliases and CLI fallback. Atomic write via `os.replace`; rotation skipped when env var precedes (preserving AD-395 priority). Emits `SECRET_ROTATED` with `source` (`store`/`env`/`no_store`) + `persisted` flag. Created `src/probos/security/egress.py` (`EgressPolicy` + `EgressDecision`; `deny_by_default=True` default with allowlist `127.0.0.1`/`localhost`/`::1`; emits `EGRESS_BLOCKED` on every blocked check — real signal today, no theater) and `src/probos/security/audit.py` (`AuditLog` + `AuditEntry`; SHA-256 hash chain with GENESIS_HASH; `verify_chain()` detects mid-chain and genesis tampering; emits `AUDIT_RECORDED` per append). 3 new EventTypes (`SECRET_ROTATED`, `EGRESS_BLOCKED`, `AUDIT_RECORDED`), `SecurityInfraConfig` (Pydantic), public attributes `runtime.egress_policy` + `runtime.audit_log` (existing `runtime.credential_store` reconfigured in finalize). No new `SecretsManager` class — folded into CredentialStore extension per Wave 7 revision. RuntimeSandbox deferred wholesale to AD-456b; SQLite-persisted audit deferred to AD-456d; HttpFetchAgent integration deferred to AD-456b. 16 focused tests pass.

AD-528 CLOSED. Ground-Truth Task Verification (v1 observation; active rejection AD-528b). Created `src/probos/cognitive/ground_truth.py` with `GroundTruthVerifier` (cross-references claimed completions against `runtime.work_item_store.get_booking_journal()` + `runtime.event_log.query()`; computes 4-signal score `journal_present`/`duration_nonzero`/`tokens_recorded`/`event_within_window`; threshold 0.75 inclusive; emits `VERIFICATION_PASSED`/`VERIFICATION_FAILED`) and `VerificationEpisodeWriter` (constructs typed `Episode` with `MemorySource.DIRECT.value` + verification metadata in `dag_summary["kind"]="ground_truth_verification"`; `importance=7` for failed verifications per AD-598; persists via `episodic_memory.store(episode)`). Added cross-layer `ALLOWED_EXCEPTIONS` entry for `cognitive/ground_truth.py → probos.workforce` (TYPE_CHECKING-only; runtime-injected via `runtime.work_item_store` — verified at `runtime.py:212`/`421`/`1532`). 2 new EventTypes (`VERIFICATION_PASSED`, `VERIFICATION_FAILED`), `GroundTruthConfig` (Pydantic), public attributes `runtime.ground_truth_verifier` + `runtime.verification_episode_writer`. v1 observes; active rejection deferred to AD-528b; trust-network feedback deferred to AD-528c; ReconciliationEscalator (AD-451) integration deferred. 14 focused tests pass.

AD-467 CLOSED. Operations Crew (Resource Allocation / Scheduling / Coordination). Created `src/probos/agents/operations/` package (owns directory creation; mirrors AD-455/457/459/466 precedents) with three `HeartbeatAgent` subclasses: `ResourceAllocatorAgent` (cross-pool capacity reporting via `pool.current_size` + `pool.target_size`; emits `RESOURCE_ALLOCATED` at configurable cadence — AD-466 review caught phantom `active_count`; corrected to real `@property` at `pool.py:53`), `SchedulerAgent` (per-task-kind cadenced emission of `TASK_SCHEDULED` events; default cadences: hourly audit / daily summary), `CoordinatorAgent` (event-driven `WORKFLOW_STARTED` emission with in-memory active-workflow registry; rejects duplicate names). Template/agent-type names disambiguated as `operations_<role>` (`operations_resource_allocator`, `operations_scheduler`, `operations_coordinator`) to avoid collision with the existing bundled cognitive `SchedulerAgent` (utility CognitiveAgent registered at `runtime.py:599`); template registration uses `as Ops*` aliases inside `__init__`. 3 new EventTypes (`RESOURCE_ALLOCATED`, `TASK_SCHEDULED`, `WORKFLOW_STARTED`), `OperationsConfig` (Pydantic). v1 ships only the 3 agents; WorkflowDefinition API → AD-467b, Response-Time Scaling → AD-467c, LLM Cost Tracker (depends on AD-463 ModelRegistry MODEL_ROUTED payload) → AD-467d. 14 focused tests pass.

AD-463 CLOSED. Model Diversity & Neural Routing — Foundation. Created `src/probos/cognitive/model_registry.py` (`ModelDescriptor` frozen dataclass with provider/tier/capabilities/cost/context-window fields; `ModelRegistry` in-memory catalog seeded with 3 default descriptors — `gpt-4o-mini`/`claude-sonnet-4-6`/`claude-opus-4-0`; `register`/`get`/`by_tier`/`all`/`mark_unavailable`/`mark_available` public API) and `src/probos/cognitive/model_router.py` (`ModelRouter` consults registry; cost-aware cheapest-by-output-cost selection with optional `cost_ceiling` filter; emits `MODEL_ROUTED`/`MODEL_FALLBACK`; cross-tier fallback when no candidates available). Real consumer hook: `OpenAICompatibleClient.__init__` now accepts kw-only `model_router=None`; `_resolve_model_for_tier()` helper consults the router via defensive `getattr(..., None)` (handles tests that bypass `__init__` via `__new__`); `_complete_inner` line 442-447 SEARCH/REPLACE inserts `_override = self._resolve_model_for_tier(attempt_tier); model = _override or tc["model"]` — caller-optional, preserves existing tier-default path when router absent. 2 new EventTypes (`MODEL_ROUTED`, `MODEL_FALLBACK`), `ModelRoutingConfig` (Pydantic with optional `cost_ceiling_per_million_output_tokens`), public attributes `runtime.model_registry` + `runtime.model_router` + `client.model_router` (no leading underscore). HebbianRouter integration deferred wholesale to AD-463d (LLMRequest does not carry agent_id today; v1 routes by tier+cost only — no theater). MAD confidence scoring → AD-463c, ProviderABC → AD-463b, hot-swap → AD-463e, edit-format selection → AD-463f. 16 focused tests pass.

AD-459 CLOSED.Saucer Separation — Graceful Degradation. Created new `src/probos/degradation/` package with `ServiceTier` enum (ESSENTIAL/COGNITIVE/NON_ESSENTIAL), `ServiceTierRegistry` (11 verified-against-runtime seed classifications: event_log, trust_network, registry, intent_bus, hebbian_router as ESSENTIAL; decomposer, dream_scheduler, proactive_loop as COGNITIVE; emergence_metrics_engine, emergent_leadership_detector, red_team_lead as NON_ESSENTIAL), `SheddingPolicy` (NORMAL→{}, ELEVATED→{NON_ESSENTIAL}, HIGH/CRITICAL→{NON_ESSENTIAL,COGNITIVE} — CRITICAL active-shedding deferred to AD-459b), and `DegradationManager` (read-only coordinator with `is_shed(name)`, `is_tier_shed(tier)`, `set_stress_level(level)`, `status()` public surface; emits transition events). v1 always-wired (no `enabled` flag) so consumers can call `is_shed(...)` without None-checks. 2 new EventTypes (`SERVICE_TIER_DEGRADED`, `SERVICE_TIER_RESTORED`), `DegradationConfig` placeholder (no operator-tunable fields in v1), `runtime.degradation_manager` (public). 13 focused tests pass.

AD-457 CLOSED. Engineering Crew (Performance / Maintenance / Damage Control). Created new `src/probos/agents/engineering/` package with 3 HeartbeatAgent subclasses: `PerformanceMonitorAgent` (latency/throughput/memory pressure heartbeat surface; v1 collects-and-records, AD-466 wires real instrumentation), `MaintenanceAgent` (schedules cleanup events for `database_compact`/`log_rotate`/`cache_evict`/`pool_rebalance` via `MAINTENANCE_SCHEDULED` events; consumers deferred to AD-457b), `DamageControlAgent` (event-driven recovery dispatch with `_RECOVERY_TABLE` mapping signatures to recovery actions; cooldown-throttled; recovery handlers deferred to AD-457b). Registered 3 spawner templates in `runtime.py:622+`. Added 3 pool spawn blocks in `agent_fleet.py` after engineering_officer (engineering_performance, engineering_maintenance, engineering_damage_control — `engineering_<role>` convention matches medical pattern). 3 new EventTypes (`DAMAGE_CONTROL_ACTIVATED`, `MAINTENANCE_SCHEDULED`, `PERFORMANCE_THRESHOLD_BREACHED`), `EngineeringConfig` (Pydantic). 14 focused tests pass.

AD-458 CLOSED. Navigational Deflector — Pre-Flight Validation. Added `src/probos/cognitive/pre_flight.py` with `PreFlightCheck` Protocol (`@runtime_checkable`), `PreFlightResult` and `PreFlightReport` frozen dataclasses, `TargetFilesExistCheck` and `TargetFilesWritableCheck` (real-work filesystem checks; `os.access` for ACL approximation on Windows), and `PreFlightRunner` (composes checks, short-circuits on blocking failure, emits `PREFLIGHT_FAILED` with `started_at`/`completed_at`/`check_count` aggregate). Wired into `execute_approved_build()` at `builder.py:2515-2517` (after dirty-tree check, before branch creation) using existing `BuildResult` create-then-mutate pattern (`result.error = ...; return result`). v1 ships only the 2 filesystem checks; `LLMTierReachableCheck` and `TokenBudgetCheck` deferred to AD-458b. New `EventType.PREFLIGHT_FAILED`, `PreFlightConfig`, `runtime.pre_flight_runner` (public). 10 focused tests pass.

AD-451 CLOSED. Validation Framework Hardening — added `src/probos/cognitive/validation_framework.py` with `TwoStageVerifier` (metadata-fast-path wrapper over `RedTeamAgent.verify`; metadata-only result returns at threshold 0.85 without invoking live re-execution), `SelfVerificationHook` Protocol (decorated `@runtime_checkable`; v1 declaration only — wiring into `CognitiveAgent.act()` deferred to AD-451b), and `ReconciliationEscalator` (resolves disagreements between two `VerificationResult`s via confidence-delta-or-majority-vote-with-third; third opinion uses `TwoStageVerifier` for real consumer wiring; `random.choice` selection from `runtime.red_team_agents` excluding primary/secondary IDs). Module-level `_MetadataCheck` (flat, ProbOS convention). 2 new EventTypes (`VALIDATION_RECONCILIATION_REQUESTED`, `VALIDATION_OUTCOME_VERIFIED`), `ValidationFrameworkConfig` (Pydantic), `runtime.reconciliation_escalator` (public attribute), wired in `finalize.py` after AD-440 OrderManager. Added documented `cognitive/validation_framework.py → probos.agents.red_team` exception to `ALLOWED_EXCEPTIONS` in `tests/test_layer_boundaries.py` (TYPE_CHECKING-only import; runtime dependency via constructor). 15 focused tests pass.

AD-491 CLOSED. Infodynamic Telemetry — added `src/probos/cognitive/infodynamic.py` with `InfodynamicProbe` measuring whole-system Shannon entropy across three signals (event-log category distribution over a configurable time window, quantized trust-score distribution, agent-state distribution). `EntropySignal` and `InfodynamicReport` frozen dataclasses; `_shannon_entropy` helper. Pure observability — no mutations. Reads `runtime.event_log` (post-filtered in Python; `EventLog.query()` has no `since=` kwarg), `runtime.trust_network`, `runtime.registry`. Added `EventType.INFODYNAMIC_REPORT`, `InfodynamicConfig` (Pydantic), `runtime.infodynamic_probe` (public), `/api/infodynamic` REST endpoint. Distinct from AD-557 emergence_metrics (within-thread PID decomposition vs whole-system entropy trajectory). 10 focused tests pass.

AD-499 CLOSED. Ship & Crew Naming Conventions — added `src/probos/naming.py` with `ShipNamingPolicy` (deterministic seed selection from a 15-name curated pool plus Captain override), `AgentNamingPolicy` (callsign validation: format regex + merged banned-word list), and `FederationDisplayFormat` (pure helper). Wired `ShipNamingPolicy.select()` into `startup/communication.py:399` ship-commissioning seam (uses `emit_event_fn`, not `runtime`). Wired `AgentNamingPolicy.validate()` into `agent_onboarding.py:534` LLM-result seam, and `EventLog.log` AGENT_SELF_NAMED emit into outer caller after `set_callsign()`. Two new EventTypes (`SHIP_NAMED`, `AGENT_SELF_NAMED`). 17 focused tests pass.

AD-439 CLOSED. Emergent Leadership Detection — added `src/probos/cognitive/emergent_leadership.py` with `EmergentLeadershipDetector` comparing designed `authority_over` chain against Hebbian influence graph. Surfaces divergences when an agent's strongest peer-influence target is NOT its designated superior (configurable `min_weight=0.10`, `min_ratio=1.5`). Added public `VesselOntologyService.get_agents_for_post(post_id)` passthrough so the detector module avoids `_dept` private access. Read-only analytics — no mutations of trust, ontology, or Hebbian weights. New `EventType.LEADERSHIP_DIVERGENCE`, `EmergentLeadershipConfig`, `runtime.emergent_leadership_detector` (public), and `/api/emergent-leadership` endpoint. 11 focused tests pass.

AD-468 CLOSED. Runtime Configuration Service — added `src/probos/runtime_config_service.py` with whitelisted overrides (`OVERRIDABLE_FIELDS` covers proactive.interval, proactive.cooldown, dreaming.interval, telemetry.report_interval) persisted to `runtime_overrides.json` (stdlib JSON only, no new pyproject deps). Added public `data_dir` property on `ProbOSRuntime` and public `set_cycle_interval`/`set_cooldown` setters on `ProactiveCognitiveLoop`. Added `RuntimeOverridesConfig`, `EventType.CONFIG_CHANGED`, `runtime.runtime_config_service` (public), and `/config` slash command. Wired in `finalize.py` after AD-679 disclosure router; applies overrides at startup via the public proactive-loop setters. 14 focused tests pass.

AD-455 CLOSED. Security Team — Threat Detection & Trust Integrity. Created new `src/probos/security/` package with 4 services: `ThreatDetector` (prompt-injection / jailbreak / abnormal-token pattern scanner), `InputValidator` (rate / payload / content-policy gate), `TrustIntegrityMonitor` (framework for burst/cycle/velocity detection; v1 returns empty report, AD-455b will add detection logic), and `RedTeamLead` (v1 health-monitor coordinator over `runtime.red_team_agents` with consecutive-failure backoff; adversarial dispatch deferred to AD-455b). Promoted `_red_team_agents` to public `red_team_agents` on `ProbOSRuntime` (8 sites updated). All 4 services published as public runtime attributes (`runtime.threat_detector`, `runtime.trust_integrity_monitor`, `runtime.input_validator`, `runtime.red_team_lead`). 4 new EventTypes (`THREAT_DETECTED`, `TRUST_INTEGRITY_VIOLATION`, `SECURITY_INPUT_REJECTED`, `RED_TEAM_CAMPAIGN_COMPLETE`). Wired in `finalize.py` after AD-468; `red_team_lead.stop()` added to `shutdown.py`. 16 focused tests pass.

AD-440 CLOSED. Chain of Command Delegation — added `src/probos/cognitive/orders.py` with `OrderManager` validating issuer authority via ontology `authority_over`. Frozen `Order` dataclass with `OrderState` (PENDING/ACKNOWLEDGED/EXPIRED). 5 rejection reasons covered by tests: `empty_directive`, `unknown_issuer`, `issuer_resolution_failed`, `out_of_chain`, `queue_full`. `dataclasses.replace` for state transitions, in-memory store with TTL pruning. Wired `runtime.order_manager` (public — establishes the cross-cutting Wave 5 precedent). 3 new EventTypes (`ORDER_ISSUED`, `ORDER_REJECTED`, `ORDER_ACKNOWLEDGED`), `OrdersConfig` with `Field` validators, proactive-context injection writing `context["active_orders"]` after the bridge-alerts block, REST endpoints at `/api/orders` (list) and `/api/orders/post/{post_id}`. DECISIONS.md entry records the orthogonality-with-`cmd_order` decision, in-memory-storage choice, and no-consensus-on-issuance rationale plus the public-attribute precedent for Wave 5. 16 focused tests pass.

Hygiene 2026-04-30: Resolved two pre-Wave-5 backlog items. **AD-654 collision resolved:** issue #313 (Ship State Snapshot) renumbered to AD-683; #322 (UAAA) keeps AD-654 because its lineage AD-654a/b/c/d is already CLOSED with deep PROGRESS.md/roadmap integration. **AD-557b/c (#11) closed as won't-fix-now:** HXI Dashboard premature against still-evolving HXI surface; higher-order PID has no forcing function. AD-557 closed parent preserves the deferral history.

AD-460 PARTIAL (resolved). Cognitive Journal — token ledger and trace linkage complete and wired (AD-431/AD-432 lineage in `src/probos/cognitive/journal.py`; powering AD-492 correlation IDs, AD-534 procedure replay, dream consolidation, workflow cache). Reasoning-replay UI scope closed without further work — replayed traces still cost LLM calls when injected as context, so they are not a token-savings mechanism. AD-464 (Procedural Learning / Cognitive JIT) is the actual token-savings path; the journal's `procedure_id` column already supports it. Wave 6 fifth slot replaced with AD-491 (Infodynamic Reporting). DECISIONS.md AD-460 entry records the rationale.

AD-682 CLOSED. Test Fixture Isolation — added per-xdist-worker `PROBOS_DATA_DIR` isolation, post-session ChromaDB path sanity check (warn-only to avoid xdist races), and function-scoped standing-orders cache clearing so high-parallel test workers do not collide on shared ChromaDB/filesystem state or stale module caches. `_default_data_dir()` now honors the `PROBOS_DATA_DIR` env-var redirect used by tests. The builder full gate default is upgraded to `-n 16 --dist=loadfile` (verified ceiling; `-n auto` exceeds xdist's scheduler at high CPU counts and produces `KeyError: WorkerController`). 6 new AD-682 fixture tests pass; full gate green at 10254 passed, 16 skipped in 6:57. BF-255 and BF-256 unquarantined and now passing.

AD-524 COMPLETE. Ship's Archive — added append-only `ArchiveStore` using the `ConnectionFactory` storage protocol for cross-reset SQLite persistence outside the instance `data_dir`; added `ArchiveConfig`; wired Archive as Oracle Tier 4 with `source_tier="archive"` results; initialized the store during cognitive-services startup with platform archive path resolution and closed it during shutdown. Manual curation only in this AD; automatic reset archival is deferred. 9 new tests and 55 focused archive/config/oracle regression tests passed. Full parallel gate passed: 10246 passed, 18 skipped.

AD-674 COMPLETE. Graduated Initiative Scale — added `InitiativeLevel` with five ordered levels, `resolve_initiative_level()` rank+trust graduation with configurable thresholds, `EarnedAgencyConfig.initiative_trust_thresholds`, and CognitiveAgent baseline metrics exposing the resolved initiative level from runtime trust. Configured thresholds are read at instruction-injection time so operators can tune Ensign/Lieutenant/Commander graduation points. 13 focused tests cover enum ordering, rank/trust mappings, `Rank.from_trust()` baseline, config defaults, threshold overrides, and CognitiveAgent metric wiring. Full parallel gate passed: 10213 passed, 18 skipped.

AD-561 CLOSED. Intervention Classification — added `InterventionType`, `InterventionRecord`, Counselor intervention history tracking, `EventType.COUNSELOR_INTERVENTION`, event emission with warning-level degradation, query APIs for filtered history and summaries, instrumentation for therapeutic DM/cooldown/forced-dream/guidance-directive paths, and `/api/counselor/interventions`. 15 focused tests plus 128 existing Counselor regression tests cover enum values, record timestamps, event emission, all intervention paths, filters, summaries, and endpoint behavior. Full parallel gate passed: 10200 passed, 18 skipped.

AD-470 CLOSED. IntentBus Enhancements — added `IntentMetrics` for broadcast/send counts, total result counts, per-intent counts, capped duration samples, and mean/max summaries; wired metrics into `IntentBus.broadcast()` and `send()` timing paths; added `get_subscriber_map()` and `get_metrics()` introspection; and exposed `/api/intent-metrics`. 13 focused tests plus 78 existing intent/dispatch/API regression tests cover metrics recording, sample caps, summaries, subscriber maps including fallback, broadcast/send metrics, and endpoint behavior. Full parallel gate passed: 10185 passed, 18 skipped.

AD-448 CLOSED. Wrapped Tool Executor — added `InvocationContext`, `ToolExecutor`, and `make_audit_hook()` as a decorator layer above `ToolRegistry.check_and_invoke()` without duplicating permission resolution or LOTO. Pre-hooks can abort with `ToolResult(error=...)`, pre-hook exceptions fail open to the registry chain, post-hooks receive the registry result, timing is recorded, audit hooks emit `EventType.TOOL_INVOKED`, and startup wires `_tool_executor` when `runtime.tool_registry` is available. 10 focused tests plus 73 existing tool-registry/context tests cover delegation, hook ordering, aborts, fail-open behavior, post-hook result receipt, timing, audit emission, event existence, and hook counts. Full parallel gate passed: 10172 passed, 18 skipped.

AD-438 CLOSED. Ontology-Based Task Routing — added `RouteDecision` and `TaskRouter` to map intent types to departments, resolve department agents through ontology public APIs, support runtime mapping registration, fall back to broadcast for unknown or unwired departments, expose `EventType.TASK_ROUTED`, wire `_task_router` during finalization, and expose `/api/task-router`. 12 focused tests cover route decisions, default mappings, known/unknown broadcast fallbacks, ontology-directed routing, custom mappings, reason text, mapping listing, event existence, and endpoint behavior. Full parallel gate passed: 10162 passed, 18 skipped.

AD-446 CLOSED. Compensation & Recovery Pattern — added `RecoveryStrategy`, mutable `CompensationRecord`, and `CompensationHandler` for retry/escalate/abandon strategy selection, escalation callback handling, rollback attempt tracking, in-memory history summaries, `EventType.COMPENSATION_TRIGGERED`, and finalize-time `_compensation_handler` wiring. Escalation callback failures log and degrade without retry, preserving the compensation record for caller-directed recovery. 10 focused tests cover enum values, record creation, retry/escalate/abandon strategies, escalation callback invocation, rollback records, event emission, event existence, and filtered history. Full parallel gate passed: 10150 passed, 18 skipped.

AD-445 CLOSED. Decision Queue & Pause/Resume — added `DecisionState`, `QueuedDecision`, and `DecisionQueue` for prioritized in-memory decision handling with pause/resume, TTL expiration, resolution state tracking, `EventType.DECISION_QUEUE_PAUSED`, finalize-time `_decision_queue` wiring, and `/api/decision-queue` status/pause/resume endpoints. 15 focused tests cover enum values, decision creation and expiry, priority ordering, full-queue rejection, pause/resume behavior, resolution, stale expiration, event emission, event existence, and endpoint behavior. Full parallel gate passed: 10140 passed, 18 skipped.

AD-676 CLOSED. Action Risk Tiers — added `RiskTier`, `RiskPolicy`, `ActionRiskRegistry`, default Ward Room/remediation/system action classifications, configurable tier policies, `RiskTierConfig`, `EventType.ACTION_RISK_DENIED`, and finalize-time `_risk_registry` wiring. Authorization now has a unified registry for ROUTINE/ELEVATED/CRITICAL action checks without changing existing earned-agency, tool-permission, or initiative gates. 12 focused tests cover enum values, default classifications, default ROUTINE fallback, authorization paths, custom registration, event existence, and config defaults. Full parallel gate passed: 10125 passed, 18 skipped.

BF-256 CLOSED. Resolved by AD-682 fixture isolation. Test was unquarantined and now passes under `-n 16 --dist=loadfile`. Order-dependent timestamp-tie failure no longer reproduces with per-worker `PROBOS_DATA_DIR` and module-cache reset.

AD-679 CLOSED. Selective Disclosure Routing — added `DisclosureLevel`, default department clearances, `DisclosureDecision`, and `DisclosureRouter` as a separate filter layer for need-to-know recipient narrowing. Added `EventType.DISCLOSURE_FILTERED`, `/api/disclosure-clearances`, and finalize-time `_disclosure_router` wiring. 11 focused tests cover level ordering, defaults, permit/deny decisions, agent overrides, filtering, reason text, event existence, and endpoint behavior. Full parallel gate passed: 10113 passed, 18 skipped.

AD-566f CLOSED. Qualification → Skill Bridge — added `QualificationSkillBridge` to map qualification test outcomes to `AgentSkillService` proficiency updates. Supports configurable test-to-skill mappings, configurable score thresholds per target `ProficiencyLevel`, in-memory advancement history, and runtime wiring after `skill_service` and `_qualification_store` are available with three default mappings. 7 focused tests cover advancement records, mapping registration, unmapped/failed/below-threshold outcomes, successful advancement, and history. Full parallel gate passed: 10103 passed, 17 skipped.

AD-566i CLOSED. Role Skill Template Expansion — completed the 14-role fleet roster in `ROLE_SKILL_TEMPLATES`: security_officer, engineering_officer, operations_officer, diagnostician, scout, counselor, architect, builder, surgeon, pharmacist, pathologist, data_analyst, systems_analyst, and research_specialist. Added 3 role skills for each newly covered role with prerequisite chains, department-matched domains, and science 7-day vs standard 14-day decay rates. 5 focused tests cover roster completeness, counts, prerequisites, domains, and decay rates. Full parallel gate passed: 10096 passed, 17 skipped.

AD-465 CLOSED. Containerized Deployment — added a multi-stage Dockerfile, docker-compose stack with ProbOS plus NATS JetStream sidecar, `.dockerignore`, and `PROBOS_LLM_URL` override for `CognitiveConfig.llm_base_url`. Docker deployment uses `probos serve --host 0.0.0.0 --port 18900 --data-dir /data`; NATS is configured through compose environment variables. 3 focused tests cover Dockerfile content, compose structure, and config env override. Full parallel gate passed: 10091 passed, 17 skipped.

AD-461 CLOSED. Ship's Telemetry — added centralized operation timing with `TelemetryService`, `TelemetryBucket`, and async `measure()` support; added `EventType.TELEMETRY_REPORT`; added `TelemetryConfig`; added `CognitiveServicesResult.telemetry_service`; wired `init_cognitive_services()` through `runtime._telemetry_service`; and exposed `/api/telemetry` for current reports or disabled status. Covers Sections 1-5. 12 focused telemetry tests plus targeted API/config regression gates pass.

BF-255 CLOSED. Resolved by AD-682 fixture isolation. Test was unquarantined and now passes under `-n 16 --dist=loadfile`. Order-dependent unread-DM failure no longer reproduces with per-worker `PROBOS_DATA_DIR` and module-cache reset.

AD-490 COMPLETE. Agent Wiring Security Logs — moved standard `agent_wired` audit logging after identity resolution and enriched event-log data with DID, sovereign ID, callsign, and ontology-first department resolution. Red team wiring logs now include `department: security`, and `EventType.AGENT_WIRED` is available for downstream consumers. 7 new tests; 91 focused onboarding/runtime/events regression tests passed. Full parallel gate passed: 10077 passed, 16 skipped.

AD-489 COMPLETE. Federation Code of Conduct — added Code of Conduct standing-order section with six behavioral principles, `EventType.CONDUCT_VIOLATION`, and Counselor handling for conduct violation events. Minor violations send a private Counselor DM only; moderate/severe violations use existing `TrustNetwork.record_outcome(source="conduct_violation")` before a DM. 7 new tests; 120 focused standing-order/Counselor/events regression tests passed. Full parallel gate passed: 10070 passed, 16 skipped.

AD-447 COMPLETE. Phase Gates for PoolGroup — added `startup_phase` metadata to `PoolGroup`, diagnostics for grouping pools by startup phase, phase-pool lookup, max phase, and phase summaries. Existing fleet pool groups now declare phases: core/bridge infrastructure first, department crews second, science/self-mod third, and utility fourth. 8 new tests; 21 focused pool-group regression tests passed. Full parallel gate passed: 10063 passed, 16 skipped.

AD-680 COMPLETE. Runtime Public API Promotion — widened `ProbOSRuntime.emit_event()` and `EventEmitterProtocol.emit_event()` to accept `EventType`, added read-only `runtime.emergence_metrics_engine`, and migrated external runtime event/emergence access from private attributes to public APIs. One-shot private→public migration with no deprecation warning recorded in DECISIONS.md. 4 new tests; 382 focused AD-680 regression tests passed serially.

BF-254 CLOSED. AD-680/BF-246 follow-up — `finalize_startup` guarded the BF-246 probe wiring with `hasattr(llm_client, "start_health_probe")`, which matches MagicMock auto-attributes. Tests using `runtime = MagicMock()` (e.g., `test_new_crew_auto_welcome.py`) raised `TypeError: object MagicMock can't be used in 'await' expression` because `runtime.llm_client.start_health_probe` returned a non-async MagicMock. Guard now checks `asyncio.iscoroutinefunction()` to require a real coroutine before awaiting. 15/15 affected tests pass.

BF-253 CLOSED. AD-680 follow-up — two related sites missed the public-API migration. (1) `builder._emit_transporter_events` was migrated to `rt.emit_event(...)`, but the `_EventEmitter` shim nested inside `transporter_build` only defined `_emit_event`, raising AttributeError when the helper was invoked through that path. (2) `test_emit_transporter_events_helper` constructed a `FakeRuntime` with only the legacy private `_emit_event` hook. Source shim and test mock both now expose `emit_event` (and keep `_emit_event` for legacy callers). 202/202 builder-agent tests pass serially.

AD-679 Selective Disclosure Routing — PLANNED. Formal message classification layer (public/department/private/captain-only) enforced by the messaging infrastructure rather than left to agent judgment. Ward Room posts, DMs, and event payloads tagged with disclosure level; routing layer prevents PII or sensitive operational data from reaching public channels. Extends the paper's selective disclosure principle to ProbOS's virtual messaging fabric.

AD-678 COMPLETE. Memory Transparency Mechanism — added `MemoryProvenance`, `TransparentMemory`, and `MemoryTransparencyService` to wrap episodic recall results with source agent, age, similarity confidence, source channel, and ownership metadata. Supports confidence filtering and prompt formatting without changing EpisodicMemory APIs or persisted episode format. 7 new tests and 18 focused AD-677/AD-678 tests passed. Full parallel gate passed: 10237 passed, 18 skipped.

AD-677 COMPLETE. Context Provenance Metadata — added `ProvenanceTag`, `ProvenanceEnvelope`, and `query_with_provenance()` for OracleResult wrapping with inline provenance markers, content hashes, confidence, and staleness detection. Added `EventType.CONTEXT_PROVENANCE_INJECTED` and TieredKnowledgeLoader provenance telemetry alongside existing tier-load events. 11 new AD-677 tests and 77 focused provenance/tier/events tests passed. Full parallel gate had one environmental BF-234 latency failure that passed serial triage: 10229 passed, 18 skipped; `tests/test_bf234_ward_room_dispatch_dedup.py::test_duplicate_path_latency` serial rerun passed.

AD-675 COMPLETE. Uncertainty-Calibrated Initiative — added `UncertaintyContext` confidence aggregation and `calibrate_initiative()` to clamp AD-674 initiative levels by observation confidence. High confidence preserves the trust-limited level, low confidence clamps down one level, critical confidence clamps down two levels, and clamping never drops below DIRECTED. 6 focused tests cover high/low/critical confidence, DIRECTED floor behavior, aggregate confidence, and custom thresholds. Full parallel gate had one environmental AD-580 failure that passed serial triage: 10218 passed, 18 skipped; `tests/test_ad580_alert_feedback.py` serial rerun passed 21/21.

AD-674 Graduated Initiative Scale — COMPLETE. Five-level initiative continuum implemented as `InitiativeLevel` DIRECTED→STRATEGIC, resolved from rank plus trust via `resolve_initiative_level()`. Runtime config exposes tunable `initiative_trust_thresholds`, and CognitiveAgent baseline metrics now include the resolved initiative level alongside trust, agency, and rank. Absorbed from Ambient Intelligence paper (Chen et al. 2026).

AD-673 Automated Anomaly Window Detection — CLOSED. Added AnomalyWindowManager to populate AnchorFrame.anomaly_window_id, triggered by trust cascade and LLM health events via _add_event_listener_fn pattern. Single concurrent window model; episodes stamped during store() with dataclasses.replace on frozen AnchorFrame; retrospective tagging deferred as a stub. 14 new tests; focused AD-673 gate passed.

AD-444 Knowledge Confidence Scoring — CLOSED. Added in-memory ConfidenceTracker for Ship's Records entries with confirm/contradict scoring, auto-supersede checks, presentation tiers, RecordsStore delegation, ConfidenceConfig, knowledge confidence event types, startup wiring, and Dream Step 10 quality cross-reference. 12 new tests; focused AD-444 gate passed. Full xdist gate reported 9688 passing/14 skipped with xdist worker-crash artifacts triaged serially; proactive stagger nodeids passed with raised timeout due AD-636 delay.

AD-563 Knowledge Linting — CLOSED. Added keyword-based KnowledgeLinter for Ship's Records notebooks with inconsistency detection, department coverage gaps, same-topic cross-reference suggestions, LintConfig, DreamReport lint fields, startup wiring, and Dream Step 10 reporting. 12 new tests; focused AD-563 gate passed. Targeted dreaming/config regression passed 93 tests. Full xdist gate reported 9706 passing/14 skipped with worker-crash artifacts serial-triaged; proactive stagger nodeids passed with raised timeout due AD-636 delay.

AD-564 Quality-Triggered Forced Consolidation — CLOSED. Added QualityConsolidationTrigger with low-quality, stale-rate, and repetition-rate thresholds, cooldown and daily-limit gates, forced-consolidation event emission, QualityTriggerConfig, DreamReport forced_consolidations, startup wiring, and Dream Step 10 trigger checks. 10 new tests; focused AD-564 gate passed. Targeted dreaming/config regression passed 105 tests. Full xdist gate reported 9668 passing/14 skipped with worker-crash artifacts; the only assertion-style failure passed serially and focused serial crash triage passed.

AD-565 Quality-Informed Routing — CLOSED. Added QualityRouter with neutral-by-default per-agent quality weights, QualityRouterConfig, QUALITY_CONCERN event emission, Counselor diagnostic API, startup wiring, DreamingResult exposure, and Dream Step 10 per-agent quality updates from AD-555 snapshots. 10 new tests; focused AD-565 gate passed. Targeted notebook/dream regression passed 103 tests. Full xdist gate reported 9701 passing/14 skipped with worker-crash/resource-pressure artifacts; direct non-worker-looking failures passed serially, proactive stagger nodeids passed with raised timeout, and 53 exact non-proactive xdist-failed nodeids passed serially before extended triage was stopped for duration.

AD-571 Agent Tier Trust Separation — CLOSED. Added AgentTierRegistry and AgentTierConfig, tier-aware TrustNetwork score filtering and CORE trust-record skip, crew-only cascade counting, crew-only EmergenceMetrics author/pair filtering, HebbianRouter crew-only reporting, and finalize-time registry population. 15 new tests; focused AD-571 gate passed. Full xdist gate reported 9631 passing/14 skipped; 145 xdist worker-crash nodeids passed serial triage. Test 15 uses TrustNetwork.get_record() and get_events_for_agent() because the original outcomes_for() assertion referenced a phantom API.

AD-572 Episodic-Procedural Bridge — CLOSED. Added EpisodicProceduralBridge as Dream Step 7h to scan dream clusters against existing procedures for novel cross-cycle patterns, with BridgeConfig, DreamReport bridged_procedures reporting, startup wiring, and procedure-store persistence for newly bridged procedures. 10 new tests; focused AD-572 gate passed. Full xdist gate reported 9539 passing/14 skipped; 138 xdist worker-crash nodeids passed serial triage.

AD-574 Episodic Decay & Reconsolidation Scheduling — CLOSED. Added ReconsolidationScheduler with importance-scaled Ebbinghaus review intervals, ReconsolidationConfig, high-importance store-time scheduling in EpisodicMemory, Dream Step 11b due-review processing, and startup wiring. 12 new tests; focused AD-574 gate passed. Targeted dreaming regression passed 40 tests. Full xdist gate reported 9839 passing/14 skipped; 118 xdist worker-crash nodeids passed serial triage.

AD-579c Validity-Aware Dream Consolidation — CLOSED. Added temporal validity spans to EpisodeCluster, compute_cluster_validity(), cluster validity propagation, EpisodicMemory.update_episode_validity(), and dream procedure-evolution expiry for superseded source episodes. 8 new tests; focused AD-579c gate passed. Targeted clustering/dreaming regression passed 80 tests. Full xdist gate reported 9821 passing/14 skipped; one assertion-style failure and 136 worker-crash nodeids passed serial triage.

AD-594 Crew Consultation Protocol — CLOSED. Added ConsultationProtocol request/response lifecycle with directed and expert-selected routing, CapabilityRegistry/BilletRegistry/TrustNetwork weighted scoring hooks, rate limiting, timeout handling, typed consultation events, ConsultationConfig, CognitiveAgent consultation handler registration, startup/result/finalize wiring, and 24 focused tests. Targeted gates passed; full xdist suite stopped on unrelated worker-crash infrastructure with no AD-594-specific failures.

AD-600 Transactive Memory — CLOSED. Added in-memory ExpertiseDirectory with agent topic profiles, confidence decay, dream-cycle cluster profile updates, ExpertiseConfig, OracleService expertise routing across top-k agent shards, and startup/runtime wiring into cognitive services and dreaming. 14 new tests; focused AD-600 gate passed. Oracle regression suite passed 29 tests. Full xdist gate reported 9865 passing/14 skipped; 115 worker-crash nodeids passed serial triage.

BF-246 CLOSED. LLM tier recovery health probe — OpenAICompatibleClient now starts a cancellable background probe that only checks non-operational tiers, emits public llm_health_changed transitions through runtime.emit_event, validates health_probe_interval_seconds >= 5.0, and cancels cleanly via close(). 9 focused tests; 78 adjacent LLM/config regression tests passed.

BF-247 CLOSED. TieredKnowledgeLoader dag_summary dict regression coverage added after production fix in 8be47d5. Added 4 tests covering contextual dict summaries, empty dict fallback to reflection, on-demand dict episodes, and no-reflection dag_summary fallback. Focused BF-247 gate passed: 36 tests.

BF-249 CLOSED. Update AD-666 sensorium tests for 10K threshold after BF-247 raised SensoriumConfig.token_budget_warning from 6000 to 10000. Follow-up to 8be47d5. 2 assertions updated; 14 Sensorium tests passed.

BF-250 CLOSED. Resolved by `_make_mock_runtime` AsyncMock hardening for ward_room methods. Root cause: `MagicMock(spec=WardRoomService)` does not auto-wrap async methods as AsyncMock; `rt.ward_room.get_unread_dms()` was awaited as a regular MagicMock, which on Windows ProactorEventLoop yields to the event loop and blocks indefinitely on `_overlapped.GetQueuedCompletionStatus`. Test now passes under `-n 16 --dist=loadfile` and `-n 0`.

BF-251 CLOSED. Same root cause as BF-250 — `_run_cycle` calls `_check_unread_dms` BEFORE the BF-023 try/except around `_think_for_agent`, so the unawaitable MagicMock hung before the RuntimeError side_effect was reached. Resolved by the same mock hardening.

BF-252 CLOSED. AD-571 test mocks (`test_startup_population`, `test_all_agents_classified`) used `SimpleNamespace` with only the private `_emergence_metrics_engine` attribute. After AD-680 promoted `emergence_metrics_engine` to a public property on `ProbOSRuntime`, `finalize._populate_agent_tiers` reads the public name; `SimpleNamespace` does not expose `@property` descriptors, so the wiring was silently skipped and the assertion `emergence.registry is runtime._tier_registry` failed. Both mocks now set both names. 15/15 AD-571 tests pass.

BF-245 CLOSED. NATS/xdist stream isolation - module-level `os.environ.setdefault("PROBOS_NATS_ENABLED", "false")` in conftest.py prevents real NATS connections during tests. NatsConfig.enabled field_validator with validate_default=True honors env override. Eliminates xdist worker crashes from JetStream stream name collisions. 8 tests; full xdist run had no NATS/JetStream collision signatures, with remaining worker crashes classified outside BF-245.

AD-603 COMPLETE. Anchor Recall Composite Scoring — added recall_by_anchor_scored() wrapper that applies score_recall() to anchor-retrieved episodes with trust, Hebbian, recency, keyword, temporal, importance, and structural anchor bonus signals. CognitiveAgent merge now combines scored anchor and semantic results by composite_score, preserving BF-155 watch filtering and legacy unscored fallback. 18 new tests; 53 focused/regression tests passed. Full-suite xdist timing failure classified as waived infrastructure after serial rerun passed.

AD-585 COMPLETE. Tiered Knowledge Loading — three-tier ambient/contextual/on-demand knowledge loader with cache invalidation, token budgets, trust summaries, typed KNOWLEDGE_TIER_LOADED events, CognitiveAgent context injection, and finalize-time wiring across crew agents. Focused AD-585 gates passed; full-suite NATS/xdist collision failures waived by Captain instruction on 2026-04-27. 34 tests.

BF-244 CLOSED. Ontology callsign desync after naming ceremony. Startup ordering: onboarding runs with ontology=None, so update_assignment_callsign() silently skips. Fix: callsign reconciliation loop in finalize.py syncs CallsignRegistry → ontology assignments after ontology is wired. Fixed identity context, peer, and superior callsign display in get_crew_context(). 8 tests. Issue #357.

AD-672 CLOSED. Agent Concurrency Management — added ConcurrencyManager with per-agent max_concurrent ceiling, priority-ordered bounded queue, capacity-approaching event emission, resource arbitration, diagnostic snapshot, ConcurrencyConfig role overrides, CognitiveAgent handle_intent slot wrapping, and startup finalization wiring. 18 new tests; 77 focused/config/CognitiveAgent regression tests passed. Full-suite NATS/xdist collision failures waived by Captain instruction. Issue #353.

BF-243 CLOSED. __new__ test pattern AttributeError after AD-601/494/595e build wave. Tests using ClassName.__new__(ClassName) bypass __init__, crash on new attributes (_tcm, _trait_adaptive_enabled, _qualification_standing, _novelty_gate). Fix: getattr(self, '_attr', default) guards at access sites in episodic.py, proactive.py, cognitive_agent.py. Source-side fix — no test changes needed.

AD-492 COMPLETE. Cognitive Correlation IDs — cross-layer trace threading. 12-char hex ID generated at perceive() time, threaded through observation dict, working memory, CognitiveJournal (schema + index), Episode, Ward Room post pipeline logging, and event payloads (SELF_MODEL_DRIFT, TASK_EXECUTION_COMPLETE, PROCEDURE_FALLBACK_LEARNING). Transient — not persisted in to_dict(). Cleared after lifecycle; next perceive() overwrites stale IDs. 21 tests.

AD-493 COMPLETE. Novelty Gate — Semantic observation dedup. Per-agent embedding fingerprints with cosine similarity (MiniLM, threshold 0.82), in-memory ring buffer (50/agent), 24h time decay. Three-layer dedup stack: BF-032 Jaccard (word-level) → AD-493 NoveltyGate (semantic/topic-level) → AD-632e Evaluate (LLM/thread-level). Wired into proactive loop and Ward Room post pipeline. Fail-open on embedding failures. 21 tests.

AD-494 COMPLETE. Trait-Adaptive Circuit Breaker — Per-agent circuit breaker thresholds from Big Five personality scores. Openness → velocity tolerance, Neuroticism → similarity sensitivity (inverted), Conscientiousness → cooldown duration (inverted), Extraversion → amber zone sensitivity. Pure deterministic compute_trait_thresholds() function. TraitAdaptiveThresholds frozen dataclass. Lazy registration in proactive loop. Safe clamping bounds. Backward-compatible (no traits = uniform thresholds). TraitAdaptiveConfig in SystemConfig. 28 tests.

AD-671 CLOSED. Dream-Working Memory Integration — added DreamWorkingMemoryBridge for pre-dream WM session-summary flush episodes and post-dream WM priming from dream insights, DreamWMConfig, DreamReport WM counters, DreamingEngine optional bridge hooks, startup wiring, and __new__-safe getattr guards. 13 new tests; 65 dream/config regression tests passed. Full-suite NATS/JetStream collision failures waived by Captain instruction. Issue #352.

AD-670 CLOSED. Working Memory Metabolism — added stateless MemoryMetabolism with TRIAGE, DECAY, AUDIT, and FORGET operations, MetabolismConfig, optional AgentWorkingMemory injection, live buffer exposure, run_cycle hook, and inline triage gates for all five record methods. 26 new tests; 69 focused working-memory/metabolism regression tests passed. Full-suite AD-580 timing failure passed serially and was classified as waived timing/load noise. Issue #351.

AD-669 CLOSED. Cross-Thread Conclusion Sharing — added ConclusionType/ConclusionEntry and a capped ConclusionLog in AgentWorkingMemory, TTL-based sibling conclusion rendering at priority 6, serialization, correlation ID attachment, lifecycle conclusion recording after act/report, and sibling conclusion injection before decide(). 16 new tests; 56 CognitiveAgent and 57 working-memory regression tests passed. Full-suite NATS/xdist worker crashes waived; debounce timing failure passed serially. Issue #350.

AD-668 CLOSED. Salience Filter — added pure SalienceFilter scoring with relevance, recency, novelty, urgency, and social dimensions; configurable SalienceConfig; BackgroundStream for sub-threshold entries; and optional AgentWorkingMemory promotion gating. Default AgentWorkingMemory behavior remains unchanged when no filter is configured. 35 new tests; 57 working-memory regression tests passed. Full-suite AD-585 timing failure waived after retry classification. Issue #349.

AD-667 COMPLETE. Named Working Memory Buffers — added Duty/Social/Ship/Engagement named buffers as a parallel index alongside legacy AgentWorkingMemory ring buffers, per-buffer token budgets, dual-write routing, selective render_buffers(), named buffer serialization, and legacy from_dict compatibility. render_context() remains unchanged. 15 new tests; 43 focused working-memory tests passed. Full-suite NATS/xdist collision failures waived by Captain instruction on 2026-04-27. Issue #348.

AD-666 CLOSED. Agent Sensorium Formalization — formalized CognitiveAgent context injections as the Agent Sensorium with SensoriumLayer, SENSORIUM_REGISTRY, SensoriumConfig, SENSORIUM_BUDGET_EXCEEDED event/dataclass, aggregate char-budget tracking in the chain path, and injection ordering audit in _build_user_message. Phase 1 completion of Ambient Awareness roadmap. 14 tests. Issue #347.

BF-241 CLOSED. NATS JetStream reconnect resilience — extracted `_recover_jetstream()` from `set_subject_prefix()`, added `_on_reconnected()` instance method, MockNATSBus parity. Completes NATS resilience stack (BF-229/230/231/232/241). 8 new tests.

BF-242 CLOSED. JetStream liveness probe — consecutive failure counter triggers recovery + suspension. Suspended JetStream bypasses to core NATS (no timeout penalty). Single-flight guard on recovery task. Probe verifies recovery success via stream_info(). MockNATSBus parity. Reconnect auto-resumes. 16 new tests.

BF-041 CLOSED. HXI SVG icon system — replaced all Unicode text glyphs across 18 component files with 25 shared SVG glyph components in `Glyphs.tsx`. STEP_ICONS string maps → STEP_ICON_COMPONENTS React component maps. IntentSurface FeedbackStatus.confirmText refactored string→ReactNode. 68 new glyph tests. Zero remaining Unicode icon glyphs (grep-verified).

BF-240 CLOSED. LLM Health Dwell-Time Criterion — configurable `min_consecutive_healthy` threshold (default 3) before diagnostic holds release. Prevents premature recovery on single healthy check amid instability. 10 new tests. Issue #344.

AD-665 COMPLETE. Corroboration Source Validation — graded provenance independence weights in compute_anchor_independence(), replacing AD-662 binary veto with version-aware scoring. Same-origin-different-version pairs get configurable independence weight (default 0.7). ProvenanceValidationResult report for diagnostics. Privacy-preserving ancestry_details (ids + reason only). Transitive ancestry deferred to future AD. 16 new tests. Issue #343.

AD-664 COMPLETE. EventLog Diagnostic Infrastructure — structured payload (data JSON column), correlation_id, parent_event_id tracing columns. Schema migration for existing DBs. query_structured() and get_event_chain() methods. _row_to_dict() DRY helper. Retrofitted dream/emergent, mesh, and QA events with structured payloads + correlation IDs. Engineering diagnostic query capability declared (capability + _handled_intents + LLM instructions); programmatic handler deferred to follow-up AD. 17 tests. Issue #337.

AD-663 COMPLETE. Provenance Producer Wiring — populate source_origin_id and artifact_version on AnchorFrame at 4 episode producer sites (WR reply, WR thread, proactive thought, cognitive agent action). Dream reflections deferred (deterministic IDs already provide dedup; provenance fields would encode same hash, no independent signal). Enables AD-662 consumer-side ancestry detection and AD-665 graded scoring. WR posts use post/thread ID as origin, SHA-256 body hash as version. Proactive uses observed WR post IDs. Cognitive agent uses correlation_id. anomaly_window_id deferred (no automated detector). 13 tests. Issue #355.

AD-618e COMPLETE. Cognitive JIT Bridge — Bill step completions → T3 skill acquisition via SkillBridge. StepSkillMapping with 3-level priority resolution (exact > bill-scoped > global). 5 default mappings (cognitive_skill→duty_execution, tool→tool_operation, post_to_channel/send_dm→communication, sub_bill→coordination). Dual exercise path: catalog entry → SkillBridge, no entry → AgentSkillService direct with auto-acquire at FOLLOW. Log-and-degrade. 22 tests. Issue #204.

AD-618d COMPLETE. HXI Bill Dashboard — definition registry on BillRuntime (register/list/get), FastAPI router (6 endpoints), React dashboard (catalog + instance timeline + role roster + step progression), WebSocket refetch-on-event, ViewSwitcher Bills tab. 15 backend + 3 frontend tests. Issue #204.

BF-239 CLOSED. Ward Room thread engagement tracking via working memory — agent-level cognitive dedup using ActiveEngagement pattern. Prevents duplicate replies when dispatched twice for the same thread. Cognitive lifecycle extracted to `_run_cognitive_lifecycle` helper; try/finally at call site ensures engagement cleanup on all exit paths. 19 tests. Issue #342.

BF-237 CLOSED. Pipeline-level post budget (N+1 post problem). LLM emits multiple [REPLY]/[MOVE] blocks → each fires create_post in the extractor loop → Step 7 fires another create_post for cleaned text. Fix: PostBudget dataclass threaded from process_and_post() → extract_and_execute_actions() → _extract_and_execute_replies(). Budget spent on first create_post; subsequent [REPLY]/[MOVE] blocks and Step 7 main post suppressed. Telemetry event on suppression. Four-layer dedup stack now complete: BF-234 (transport) → BF-236 (dispatch round) → BF-237 (pipeline invocation) → BF-197 (content similarity). 10 new tests. Issue #340.

AD-618c COMPLETE. Built-in Bills — 4 default YAML bills (General Quarters, Research Consultation, Incident Response, Daily Operations Brief) + loader functions (builtin discovery/parse, custom loading). XOR gateway with dual-input convergence in incident_response. 24 tests. Issue #204.

AD-618b COMPLETE. Bill Instance + Runtime — BillInstance/StepState/RoleAssignment dataclasses with InstanceStatus/StepStatus enums. BillRuntime service: activate() with WQSB role assignment (BilletRegistry + qualification filtering), step lifecycle (start/complete/fail/skip), cancel(), query API (get_instance, list_instances, get_agent_assignments). 8 EventType entries. BillConfig (max_concurrent_instances, default_step_timeout, allow_partial_assignment). Late-bound event callback + BilletRegistry setter. 30 new tests. Issue #204.

AD-618a CLOSED. Bill Schema + Parser — BillDefinition/BillStep/BillRole/BillActivation dataclasses with GatewayType (sequential/parallel/XOR/OR) and StepAction enums. YAML parser with schema validation (role references, branch targets, step ID uniqueness, action types, gateway-branch consistency, condition step references). Ship's Records gains `bills/` subdirectory with raw-YAML `write_bill()` (bypasses frontmatter wrapping) and `list_bills()` (globs `*.bill.yaml`, not `*.md`). New `src/probos/sop/` package. 50 new tests (46 schema/parser + 4 RecordsStore integration) including write→read round-trip. Foundation for AD-618b (runtime) and AD-618c (built-in bills).

AD-664 PLANNED. EventLog Diagnostic Infrastructure — structured payloads + query authority. Two gaps: (1) EventLog events emit bare string labels with no structured payload, correlation ID, or parent_event_id — root-cause tracing impossible. (2) No agent holds confirmed EventLog query authority — diagnostic relay chains dead-end. Crew-originated (Forge + Anvil, 5 proposals). Issue #337.
BF-235 CLOSED. Stale identity block in agent system prompt rendering across stasis boundaries. Root cause: @lru_cache on _build_personality_block() and _load_file() in standing_orders.py never cleared on stasis resume. Fix: unconditional clear_cache() + evict_cache_for_type() on stasis_recovery lifecycle event in finalize.py (not gated behind warm_boot_orientation). Defensive clear on all startups. Diagnostic logging. Crew-originated (Vance, Chapel, Anvil, Reed). 7 new tests. Issue #338.
BF-236 CLOSED. Semantic duplicate dispatch — round-scoped post tracker. `_route_to_agents()` had no check for "agent already posted in this conversational round." Back-to-back `route_event()` calls dispatched same agent for same thread with different intent IDs. Fix: `_posted_in_round` tracker on WardRoomRouter, keyed on `(agent_id, thread_id)`, cleared on Captain repost. Recorded by WardRoomPostPipeline after `create_post`. 120s eviction. Gate in Phase 1 after Layer 3, respects `is_direct_target` bypass. 13 new tests.
BF-234 CLOSED. Duplicate ward room responses — consumer-side dedup. 4 of 12 agents posted duplicate replies due to transport-layer duplicate delivery (JetStream redelivery or js_publish timeout-then-succeed). Fix: `_seen_intents` dict in `IntentBus._on_dispatch()` — 300s sliding-window keyed by `intent.id`, filtered to `ward_room_notification` only. WARN log + counter + telemetry event. `msg.ack()` on hit. BF-198 remains semantic round-tracking only. 11 new tests.
AD-556 CLOSED. Adaptive trust anomaly detection — per-agent z-score thresholding. Rolling window of per-agent trust score history (default 30 snapshots). Z-score computed against agent's personal delta baseline. Debounce requires consecutive anomalous cycles (default 2) before escalation. Dual-gate: population sigma AND personal z-score must both fire. New agents without history fall back to population-only detection. Four new config parameters on EmergentDetectorConfig. 26 new tests. Crew-originated design (Forge + Reyes, 2026-04-01).
BF-233 CLOSED. BF-204 grounding check false positives — entity IDs from ward room params (thread_id, channel_id, author_id) and agent identity (_agent_id, intent_id) not included in grounding source, causing legitimate ward room replies to be suppressed as confabulations. Fix: expand grounding source to include all entity IDs the agent was explicitly given in its input context. Fabricated hex IDs still caught. Known limitation: cross-agent post UUID references (not in agent's own params) may still trigger if agent uses full UUID instead of truncated 8-char form. 11 new tests.
BF-232 CLOSED. ensure_stream() inherits stale subject filters from previous boot — update_stream() silently fails to change subjects (BF-231 finding), causing persistent "no response from stream" warnings on every JetStream publish. Fix: new recreate_stream() method uses delete-then-create; ensure_stream() unchanged (non-destructive). Phase 2 startup and set_subject_prefix() now use recreate_stream(). _delete_stream() failure logging promoted from DEBUG to WARNING for real failures (not-found stays DEBUG). Completes BF-229/230/231 NATS resilience trilogy. 8 new tests.
AD-599 CLOSED. Reflection as Recallable Episodes — Dream Step 15 promotes consolidation insights to [Reflection] episodes in EpisodicMemory. Four reflection sources: convergence reports, emergence snapshots, notebook consolidations, dominant cluster patterns. MemorySource.REFLECTION enum value. Deterministic content-hash IDs for cross-cycle deduplication. Rate-limited (default 3/cycle). No LLM calls. 18 new tests. Depends on AD-551 + AD-567d.
AD-601 CLOSED. TCM Temporal Context Vectors — Howard & Kahana (2002) Temporal Context Model for episodic memory. Drifting context vector (d=16, rho=0.95) stored alongside each episode. Cosine similarity between current and stored context provides smooth temporal proximity gradient, replacing binary watch_section matching for new episodes. Legacy episodes fall back to BF-147/BF-155 logic. Pure Python, no numpy/scipy. 23 tests.
AD-595e CLOSED. Qualification gate enforcement at three pipeline points: BillRuntime step start, proactive duty dispatch, and CognitiveAgent context injection. Two-flag config (enforcement_enabled + enforcement_log_only) enables shadow mode rollout. All gates default ALLOW (graceful degradation). BillRuntime.start_step() now async. BilletRegistry gains get_qualification_standing() and check_role_qualifications() methods. CognitiveAgent caches standing with 5-min TTL. QUALIFICATION_GATE_BLOCKED event type. 16 new tests. Depends on AD-595d.
AD-595d CLOSED. Qualification-aware billet assignment. Post gains required_qualifications field. BilletRegistry gains check_qualifications() async method with allow_untested cold-start tolerance, assign_qualified() async gated assignment, and set_qualification_store() public setter. assign() unchanged — stays sync and unconditional. Startup wiring connects QualificationStore → BilletRegistry via public setter. Data model + check API only — production gate deferred to AD-595e. 22 new tests. Depends on AD-595a + AD-539.
AD-595c CLOSED. Standing orders billet templating. compose_instructions() now resolves {Billet Title} patterns to current callsigns via BilletRegistry: {Chief Engineer} → LaForge (Chief Engineer). Vacant billets render as Title (vacant). Code blocks and inline backticks excluded from processing. 18 new tests. Depends on AD-595a.
AD-595b CLOSED. Naming ceremony → BilletRegistry integration. BilletRegistry.assign() method added (validate post + emit BILLET_ASSIGNED, falls back to stored callsign when caller passes empty). Single billet assignment point after identity issuance covers cold-start, warm-boot, and non-crew paths. OrientationContext gains billet_title field. 13 new tests. Depends on AD-595a.
AD-595a CLOSED. BilletRegistry — read-side facade over DepartmentService adding title-based resolution (case-insensitive), roster snapshots (Watch Bill), and BILLET_ASSIGNED/BILLET_VACATED event types. BilletHolder frozen dataclass. Eagerly initialized in VesselOntologyService.initialize(). Mutators (assign/vacate) deferred to AD-595b. 17 new tests. Issue #165.
BF-231 CLOSED. JetStream stale DID stream fix — set_subject_prefix() now deletes and recreates streams instead of updating subject filters. Prevents "no response from stream" after probos reset generates a new ship DID. 5 new tests.
AD-584d CLOSED. Enriched embedding document — _prepare_document() now includes reflection alongside user_input (aligning ChromaDB with FTS5) plus heuristic question seeding for elaborative encoding. Intent, outcome, and department question templates. Reflection NOT templated into questions (grammar issues). No LLM call. 15 new tests. Completes AD-584 recall improvement wave.
BF-230 CLOSED. JetStream publish silently drops events under CPU load — no retry, no fallback to core NATS. Fix: js_publish() retries once (0.5s backoff), then falls back to core NATS publish, only ERROR if both fail. Configurable timeout (default 5s) via NatsConfig.js_publish_timeout. 6 new tests. Issue #335.
BF-106 CLOSED. DreamingEngine late-init dependencies monkey-patched in finalize.py — `_ward_room`, `_records_store`, `_get_department` set via private attrs. Fix: `records_store` forwarded through init_dreaming() constructor (available Phase 4→5). `ward_room` and `get_department` get public setter methods (genuinely unavailable until Phase 7). finalize.py now calls setters instead of reaching into private attrs. 8 new tests. Issue #19.
BF-229 CLOSED. NATS subject prefix rejects colons from ship DID — JetStream streams stranded with probos.local.* filters after Phase 7 commissioning sets prefix to probos.did:probos:<uuid>. All js_publish() calls fail with "no response from stream". Fix: NATSBus.set_subject_prefix() sanitizes NATS-unsafe chars to underscores (boundary enforcement); ensure_stream() re-raises after logging; stream update failure promoted to ERROR. 5 new tests + 1 existing test updated. Issue #334.
BF-228 CLOSED. Communications Array Degraded/Restored flapping — proactive loop transitions to "degraded" on 1 failure, resets on next success, flooding All Hands with Bridge Alerts. Three fixes: (1) raised degraded threshold from >=1 to >=3 (hysteresis, matches LLM client _UNREACHABLE_THRESHOLD), (2) only count actual LLM errors (timeout/connection/rate-limit) not generic agent failures, (3) decay failure count on success instead of hard-reset to 0. Issue #332.
AD-662 COMPLETE. Corroboration Source Provenance Validation — extend SocialVerificationService (AD-567f) with source provenance metadata. Three new AnchorFrame fields (source_origin_id, artifact_version, anomaly_window_id), ancestry-based independence veto, anomaly window discount scoring. Infrastructure-only (consumer-side); AD-663 wires producers. 13 new tests. First crew improvement proposal from this instance (Reed/Science, corroborated by Ezri/Bridge and Sentinel/Security). Issue #331.
AD-654 IN PROGRESS. Universal Agent Activation Architecture (UAAA) — event-driven activation for multi-agent systems. 5 sub-ADs: a=Async Ward Room Dispatch (JetStream publish, not request/reply), b=Agent Cognitive Queue (priority mailbox), c=TaskEvent Protocol & Dispatcher, d=Internal Event Emitters (RecreationService, WardRoom, Kanban), e=External Integration (MCP Apps, deferred). a-d COMPLETE, e deferred. Research: universal-agent-activation-research.md.
AD-654d COMPLETE. Internal Event Emitters — 4 emitters wired into TaskEvent/Dispatcher: (1) RecreationService move_required with callsign→agent_id resolution, game-over suppression; (2) delegation tags [ASSIGN] (Lieutenant+ rank gate) / [HANDOFF] (no rank gate) with self-assignment guard; (3) WorkItemStore work_item_assigned on assignment (not claim); (4) WardRoom @mention on post/thread creation with self-exclusion. Late-binding via attach_dispatcher() pattern (Law of Demeter). All emitters degrade gracefully when dispatcher=None. 25 tests.
AD-654c COMPLETE. TaskEvent Protocol & Dispatcher — universal TaskEvent dataclass (frozen, 10 fields: source_type, source_id, event_type, priority, target, payload, thread_id, deadline, id, created_at). AgentTarget with exactly-one validation (agent_id/capability/department_id/broadcast). Dispatcher routes TaskEvents to cognitive queues via target resolution; callback injection pattern (get_queue, dispatch_async_fn) avoids LoD violation. DispatcherProtocol. DispatchResult with dispatch_ms timing. 3-tier fallback: queue → dispatch_async → create_task. New package: src/probos/activation/. 26 tests. Issue #325.
AD-654b COMPLETE. Agent Cognitive Queue (Priority Mailbox) — Actor Model mailbox interposes between JetStream consumers and agent.handle_intent(). AgentCognitiveQueue with priority ordering (CRITICAL→NORMAL→LOW), overflow shedding, dequeue-time circuit breaker guard, JetStream ack semantics (ack/term/nak). handler.__self__ Law of Demeter violation replaced with injected callback. max_deliver=10 bounds nak redelivery. AgentCognitiveQueueProtocol in protocols.py. 25 tests + 29 AD-654a regression green. Issue #324.
AD-654a COMPLETE. Async Ward Room Dispatch — ward room router publishes to JetStream fire-and-forget; agents consume via durable consumer and self-post via WardRoomPostPipeline. IntentBus.dispatch_async() with fallback to direct dispatch. NATSMessage.term() + manual_ack on js_subscribe(). Per-agent consumer cleanup in unsubscribe(). 29 tests. Issue #323.
BF-222 CLOSED. Stream subjects not updated on NATS prefix change. set_subject_prefix() now updates stream configs. EventType moved to str Enum. Issue #327.
BF-223 CLOSED. JetStream durable consumer filter_subject stale after prefix change — zero agent dispatch delivery. Two fixes: (1) NATSBus.set_subject_prefix() deletes+recreates durable consumers, (2) IntentBus defers dispatch consumers until finalize.py after ship commissioning. Issue #328.
BF-224 CLOSED. Proactive context renders thread title only, drops body — agents can't read ward room posts during proactive scan. Fixed all three context builders (dept/All Hands/Recreation) to render title+body. Issue #329.
BF-225 CLOSED. Scout classification instructions stripped — duty-triggered proactive_think not renamed in observation dict. _decide_via_llm() treated it as conversation, excluded ===SCOUT_REPORT=== format. Fixed perceive() to write intent back. Issue #330.
BF-226 CLOSED. Two bugs: (1) Proposal extraction broken — BF-203 catch-all bracket strip destroys [PROPOSAL] before _extract_and_post_proposal runs. Fix: moved proposal extraction into _extract_and_execute_actions before BF-203 catch-all, deleted old method. (2) All Hands unread count never resets — update_last_seen only fires inside conditional requiring new non-inform activity. Fix: unconditional update_last_seen after channel check. Also fixed Recreation last_seen and agent_last_seen NameError. 11 tests (9 proposal, 2 last_seen).
BF-227 CLOSED. All Hands channel lookup matches any ship channel, not "All Hands". `all_hands_ch` set by `channel_type == "ship"` iteration — lands on last ship channel (Creative or Recreation), never actual All Hands. BF-226's update_last_seen fix was correct but operating on wrong channel. Fix: match by `ch.name == "All Hands"` instead of `ch.channel_type == "ship"`. 1-line fix.
AD-653 COMPLETE. Dynamic Communication Register — "speak freely" protocol (Layer 1). ANALYZE extended with speak_freely intended_action for thread_analysis + situation_review (not DM). COMPOSE trust-gates authorization: ≥0.7 auto-granted, 0.4-0.7 flagged to Counselor, <0.4 denied. Billet instruction pattern (AD-651a) injects framing. Events: REGISTER_SHIFT_GRANTED/DENIED. Counselor subscribes + dispatches. 14 tests. Layers 2-3 deferred (register taxonomy enum, Big Five defaults, analytics). Issue #303.
AD-652 REALISED (Wave 95 close, 2026-05-07). Cognitive Code-Switching — unified pipeline with contextual modulation. All six principles delivered across shipped children: AD-632 (unified pipeline substrate), AD-649 (channel→register inference, 5 registers), AD-639 (chain trust band tenor weighting), AD-650 (analytical_reasoning depth field), AD-651 (StepInstructionRouter billet overlays), AD-651a (proposal + duty report compose billets), AD-647/AD-647c (process chains — variable depth + process-specific composition), AD-653 Layer 1 (speak-freely register shifting, trust-gated). Downstream consumers AD-655/AD-656/AD-657/AD-658/AD-659/AD-660 all complete. Tracker reconciliation only — no code, no tests. Research: cognitive-code-switching-research.md. Issue #302 closed.
AD-651 CLOSED. Standing Order Decomposition — step-specific instruction routing for cognitive chain. StepInstructionRouter slices composed standing orders by category markers; analyze/compose handlers use get_step_instructions(); startup wires router via finalize.py. Backward-compatible full-text fallback when disabled or unmarked. 20 new tests, 144 focused integration tests passing. Issue #299.
AD-651a CLOSED. Compose billet instructions — proposal format injected when analyze requests "proposal" in intended_actions, duty compose framing replaced with structured Findings/Assessment/Recommendation report, [NO_RESPONSE] removed from duty cycles. 10 tests. Issue #301.
BF-217 CLOSED. Post-batch test regressions + DRY cleanup — 3 test regressions fixed (max_tokens 1024→1536, key counts 6→7/5→6), 2 DRY extractions (AD646B_DEDICATED_KEYS frozenset in sub_tasks/__init__.py, derive_communication_context() module-level function in cognitive_agent.py), 1 dead test file deleted (test_bf194, 371 lines). 151 tests green. Issue #306.
BF-216 FIXED (direct). Ward Room thread body not scrollable — long duty reports push posts/reply off-screen. Moved thread body into scrollable content area, title/author stays fixed. Issue #305.
BF-215 FIXED (direct). Empty observation posts after tag stripping — added empty guard after BF-203/BF-174 action extraction in proactive.py. Issue #304.
BF-214 CLOSED. Scout marks repos as seen before classification — deferred seen marking from perceive to act, gated on classification success. 10 tests. Issue #300.
BF-213 CLOSED (rewritten). Chain proactive analyze suppresses structured actions — three compounding issues: (1) analyze "Silence is professionalism" bias defaults to `intended_actions: ["silent"]`, (2) compose short-circuit skips LLM call, (3) compose framing "2-4 sentences" discourages structured output. Fix: rebalance analyze framing, broaden compose framing, revert BF-213 duplicate action vocabulary. ~10 lines across 2 files, 12 tests. Issue #298.
BF-212 CLOSED. Crew-vs-crew game moves never reach opponent — added `@{next_player}` to in-progress board post body in both MOVE paths (ward_room_router.py + proactive.py). Existing @mention resolution routes to opponent regardless of channel type. 4 tests. Issue #297.
BF-211 CLOSED. Proactive loop dies silently — moved `asyncio.sleep(self._interval)` inside try/except in `_think_loop()`. Sleep in both happy path and error recovery branch. CancelledError still re-raises for clean shutdown. 2 tests. Issue #296.
BF-210 CLOSED. Ward Room DM recipient never wired — wired `author_callsign` → `_dm_recipient` in cognitive_agent.py, named peer in compose private_conversation branch, changed `_build_dm_compose_prompt()` default from "the Captain" to "a crew member". 8 tests, 34 regression green. Issue #295.
AD-650 COMPLETE. Analytical Depth Enhancement — added `analytical_reasoning` narrative prose field to composition brief (all 3 ANALYZE modes), reframed brief as "analytical reasoning and composition plan", bold-header suppression for all Ward Room branches, depth instructions ("Don't just summarize — interpret") in ward_room/DM/proactive COMPOSE prompts. 12 tests, 71 regression green. Issue #294.
AD-649 COMPLETE. Communication Context Awareness for Cognitive Chain — add channel-type register adaptation to COMPOSE, prescriptive tone guidance in ANALYZE, voice parity with one-shot. 14 tests. Issue #293.
AD-648 COMPLETE. Post Capability Profiles — per-post capability declarations + negative grounding in ontology. PostCapability dataclass, loader parsing, service methods (get_post_capabilities, get_agent_capabilities, get_post_negative_grounding), get_crew_context() extension, _build_cognitive_baseline() prompt injection. All 18 posts profiled in organization.yaml. Prevents conceptual confabulation. 14 tests. Issue #292.
AD-647 v1 CLOSED. Process-Oriented Cognitive Chains — scaffold + Scout-internal migration. ProcessChainStep/Definition/Executor + SCOUT_REPORT_CHAIN. BF-209 retained as comm-chain bypass; removal deferred to AD-647b. 8 tests. Issue #291.
BF-209 CLOSED. Scout duty report chain bypass — ScoutAgent._should_activate_chain() override routes duty-triggered scout_report through decide()→act(). 4 tests. Issue #290.
BF-208 CLOSED. Scout duty report always returns 0 findings — perceive() only recognizes scout_search/scout_report intents, duty scheduler sends proactive_think. Fix: 5-line duty-trigger check in perceive(). 5 tests. Issue #286.
BF-207 CLOSED. Shutdown race condition causes episodic memory hash mismatch. Issue #282.
BF-206 CLOSED. Enforce evaluate suppress + confabulation feedback loop. Issue #281.
AD-643b COMPLETE. Skill Trigger Learning — adaptive trigger discovery + post-hoc re-reflect feedback loop. Trigger awareness in ANALYZE, post-hoc re-reflection when action → no trigger match. Phase 3 (graduation) deferred. Issue #284.
AD-643  COMPLETE. Intent-Driven Skill Activation & Thought Process Catalog — trigger registry, weighted matching, chain-aware dispatch. Issue #283.
AD-637z COMPLETE. NATS Migration Cleanup + BF-221 Lift — prefix re-subscription (NATSBus tracks _active_subs, async set_subject_prefix re-subscribes all), BF-221 lifted (IntentBus.send() restored to NATS request/reply when connected), double event subscription fix (_nats_events_wired gate), ensure_future→create_task task leak fix, duplicate ensure_stream removal. NATSBus owns full subscription lifecycle. 7 files modified, 13 new tests (88/88 NATS regression pass). Issue #321.
AD-637f COMPLETE. Priority Model Formalization — Priority(StrEnum) with CRITICAL/NORMAL/LOW + classify() single source of truth. Captain @mentions now get interactive LLM slots (behavioral change). X-Priority headers on ward room + system event JetStream publishes. LOW is observability-only label (same background semaphore as NORMAL). 5 files modified, 13 new tests. Issue #312.
AD-637  COMPLETE. NATS Event Bus — standardize all messaging on NATS. Sub-ADs: a=Foundation, b=IntentBus, c=WardRoom, d=SystemEvents, e=Federation, f=Priority, z=Cleanup — all complete. Issue #270.
AD-637d COMPLETE. System Event Migration (EventEmitter → NATS) — _emit_event() publishes to SYSTEM_EVENTS JetStream stream when NATS connected, falls back to _emit_event_local() when disconnected. add_event_listener() creates ephemeral NATS subscriptions with subject-level type filtering. Night Orders escalation preserved as synchronous local check (runs before dispatch). Bulk wiring of early-startup listeners via _setup_nats_event_subscriptions(). 2 files modified (runtime.py, finalize.py), 10 new tests. Issue #308.
AD-637c COMPLETE. Ward Room NATS JetStream Emission — fire-and-forget create_task dispatch replaced with JetStream publish→durable consumer. max_ack_pending=10 + ack_wait=120s replaces Semaphore(10). Graceful degradation to create_task fallback. No-dual-delivery enforced structurally. 5 files modified, 11 new tests. Issue #307.
AD-637b COMPLETE. IntentBus NATS Migration — send() uses NATS request/reply with direct-call fallback, NATS subscribe/unsubscribe lifecycle, publish() alias (fixes latent watch intent bug), serialization helpers, set_nats_bus() wiring, federation handler cleanup. broadcast() unchanged (deferred to AD-637c). 4 files modified, 19 tests (13 new). Issue #270.
AD-637a COMPLETE. NATS Foundation Layer — nats-py dependency, NATSBus abstraction (graceful degradation), NatsConfig, MockNATSBus, NATSBusProtocol, startup Phase 1b wiring, shutdown drain, pre-flight check. 10 files (3 new, 7 modified), 24 tests. Issue #270.
AD-637  COMPLETE. NATS Event Bus — standardize all messaging on NATS. Sub-ADs: a=Foundation, b=IntentBus, c=WardRoom, d=SystemEvents, e=Federation, f=Priority, z=Cleanup — all complete. Issue #270.
AD-646b COMPLETE. Chain Cognitive Parity — close remaining one-shot gaps. Two QUERY operations (self_monitoring, introspective_telemetry) + three baseline enhancements (cold-start, rich attribution, self-recognition) + prompt consumption (oracle, telemetry). ~150 lines across 4 files. Issue #289.
AD-646  COMPLETE. Universal Cognitive Baseline — split _build_cognitive_state() into baseline (agent-intrinsic, all chains) + extensions (context_parts-dependent). Fixes ward_room data starvation. ~100 lines across 3 files. Research doc complete. Issue #288.
AD-645  Phase 1-4 Complete. Composition briefs replace routing slips in ANALYZE→COMPOSE handoff. COMPOSE gets structured brief + 7 SA keys. EVALUATE scores brief_alignment (covered_topics, used_evidence, matched_tone) on ward_room + proactive modes; additive-not-gating per DD-7; backfilled neutral when brief absent. 22 tests. Phase 5 deferred to AD-641g. Issue #287.
AD-644  Phase 1-4 Complete. 23/23 parity. Chain path has full perceptual equivalence with _build_user_message(). Phase 5 (deprecation) design.

BF-203 CLOSED (Strip hallucinated bracket command tags from agent output. Issue #278) | BF-201 CLOSED (Simplify thread caps — remove per-agent & dept gate, add 50-post cap. Issue #276) | BF-106 OPEN (DreamingEngine late-init dependencies monkey-patched in finalize.py. Issue #19) | BF-200 CLOSED (Thread Cap Awareness & DM Cap Raise — system notification on cap hit, DM limit 5→40, DM bypasses reply cap. Issue #275) | BF-199 CLOSED (Ward Room JSON Leak Guard — sanitize chain JSON at posting boundary, shared helper for proactive+router. Issue #274) | AD-640 COMPLETE (Tiered Trust Initialization — role-based trust priors: Bridge 0.82, Chiefs 0.75, Crew 0.50. 6 academic domains validate. Issue #273) | AD-638 COMPLETE (Cold-Start Boot Camp — 4-phase onboarding, quality gate relaxation, graduation criteria. 1 new + 8 modified, 35 tests. Issue #272) | AD-639 COMPLETE (Chain Personality Tuning — trust-band adaptive chain: low trust skips evaluate/reflect, mid trust injects personality block, high trust unchanged. 4 modified + 1 test file, 38 tests. Issue #272) | BF-204 CLOSED (Evaluate grounding criterion — reorder defense chain safety>obligation, deterministic hex ID pre-check, Grounding criterion in all 3 eval prompts, reflect suppress above social obligation. 2 modified + 1 test file, 24 tests. Issue #280) | ## Current Status: BF-193 CLOSED (Parallel Captain dispatch — 3-phase _route_to_agents: pre-filter, concurrent dispatch via asyncio.gather for Captain, sequential result processing. 1 source file, 7 tests. Issue #264) | BF-190/191 CLOSED (Route now NameError + Evaluate raw JSON pass-through — removed dead now variable, cleaned up import time as _time, added deterministic JSON rejection in evaluate before LLM judge. 2 source files, 8 tests. Issue #261) | BF-189 CLOSED (Chain pipeline memory context gaps — pre-format memories via _format_memory_section() in chain context, fix thread_analysis raw list repr, add memory to dm_comprehension/situation_review/compose. 3 source files, 12 tests. Issue #257) | BF-187/188 CLOSED (DM social obligation + Captain delivery coordination — _is_dm flag at all 4 chain gates, asyncio.Event for Captain delivery, reflect suppress reorder. 5 source files, 18 tests. Issue #255) | BF-186 CLOSED (Compose standing orders regression — removed 80+ lines hardcoded action vocab, inject rank/skill_profile/crew_manifest into chain context, social obligation bypass at compose layer, analyze enriched with standing orders. 4 source files + 1 config, 20 tests. Issue #251) | BF-185 CLOSED (Reflect social bypass — Captain/@mention skip self-critique, 0 tokens. 1 file, 8 tests. Issue #250) | BF-184 CLOSED (Evaluate social bypass — Captain/@mention auto-approve, 0 LLM tokens. 2 files, 11 tests. Issue #248) | BF-183 CLOSED (Budget-aware retry in SubTaskExecutor. 13 tests. Issue #246) | BF-184 CLOSED (Evaluate social bypass — Captain/@mention auto-approve, 0 LLM tokens. 2 files, 11 tests. Issue #248) | BF-183 CLOSED (Sub-Task Chain Budget-Aware Retry — timeout step checks remaining chain time budget, retries once with 2s backoff if sufficient, falls back immediately if not. 1 file, 13 tests. Issue #246) | AD-636 COMPLETE | BF-179/180/181/182 CLOSED (AD-632 runtime activation bugs — four fixes enabling sub-task chains in production. BF-179: finalize.py used getattr(agent, 'is_crew', False) which always returned False — executor never wired onto crew agents. Fix: is_crew_agent(). BF-180: duplicate augmentation skill loading — AD-632f added _load_augmentation_skills() in decide() but _decide_via_llm() already called it. Fix: guard with observation key check. BF-181: asyncio not defined in chain error handler — _execute_sub_task_chain() except block referenced asyncio.TimeoutError without import. Fix: local import. BF-182: query handler missing agent_id/thread_id — context filtering stripped _-prefixed identity keys and QUERY steps got filtered context. Fix: pass _-prefixed keys through filter, bypass filtering for QUERY steps, _ctx() helper resolves from nested params dict. 3 source files (finalize.py, cognitive_agent.py, sub_task.py), 1 handler file (query.py). Issue #243) | AD-633 SCOPED (Predictive Cognitive Branching — umbrella AD enabling crew agents to engage in forward-looking thought during idle cycles. Three functions: (1) Pre-Computation (CPU branch prediction analog — speculate on likely sub-tasks, serve pre-computed results), (2) Anticipatory Reasoning (Prospection/DMN analog — forward-looking thought producing new awareness, skill gap identification, PROPOSAL generation), (3) Goal Origination (Constructive Episodic Simulation/Preplay analog — agents generate novel goals from observation). Four speculation tiers: Zero-Cost deterministic, Cheap fast-tier >70%, Standard >85%, Anticipatory self-originated idle. Separate speculative token budget (not competing with operational). Prediction accuracy feedback loop (flush rate >30% = budget reduction). Earned Agency gates budgets by trust tier. Preplay dream Step 13 (forward simulation + implementation intentions). Prediction errors feed AD-557 EmergenceMetrics as novelty signal. Repeated predictions compile into Cognitive JIT procedures. Nine sub-ADs: 633a prediction engine, 633b executor+cache, 633c budget management, 633d pipeline integration, 633e accuracy tracking, 633f anticipatory reasoning, 633g preplay dream step, 633h emergence signal, 633i Cognitive JIT compilation. Research: docs/research/predictive-cognitive-branching.md (14 sources: Seligman, Schacter, DMN, Dragoi, Friston, Gollwitzer, Ha/Schmidhuber). Depends: AD-632, AD-531-539, AD-557, AD-573, AD-357. Issue #228) | AD-632 COMPLETE (Cognitive Sub-Task Protocol — umbrella AD enabling crew agents to decompose complex reasoning into focused sub-steps. Three-level cognitive escalation: Level 1 Cognitive JIT replay (0 calls), Level 2 single-call reasoning (1 call, current), Level 3 sub-task protocol (2-4 focused calls). Five sub-task types: Query (deterministic), Analyze (LLM narrow), Compose (LLM with skill), Evaluate (criteria), Reflect (self-critic). Selective activation via skill annotation, complexity heuristic, or quality fallback — not constant cost. Eight sub-ADs: 632a foundation, 632b query, 632c analyze, 632d compose, 632e evaluate/reflect, 632f triggers, 632g Cognitive JIT integration, 632h parallel dispatch. SOAR impasse-driven subgoaling + chunking model. Sub-tasks are intra-agent only — no identity, no trust, no episodic memory. Research: docs/research/cognitive-sub-task-protocol.md (13 sources: SOAR, ACT-R, ReAct, DECOMP, Inner Monologue, ToT, Reflexion, LATS, MRKL, Claude Code, Kahneman). Depends: AD-631, AD-625, AD-626, AD-596a-e, AD-531-539. Issue #227) | AD-632a COMPLETE (Cognitive Sub-Task Protocol Foundation — infrastructure layer for Level 3 cognitive escalation enabling intra-agent sub-task decomposition. SubTaskType 5-value str enum (QUERY/ANALYZE/COMPOSE/EVALUATE/REFLECT), SubTaskSpec/SubTaskResult frozen dataclasses, SubTaskChain mutable chain container, SubTaskHandler @runtime_checkable Protocol with async __call__(spec, context, prior_results), SubTaskExecutor Open/Closed handler registry with sequential execution + per-step and chain-level timeouts via asyncio.wait_for() + journal recording with structured dag_node_id (st:{chain_id}:{step_index}:{type}) populating dead AD-432 column + SUB_TASK_CHAIN_COMPLETED event emission. SubTaskError/SubTaskChainError/SubTaskStepError exception hierarchy. CognitiveAgent integration: _sub_task_executor + _pending_sub_task_chain attributes, set_sub_task_executor() setter, _execute_sub_task_chain() method, decide() integration between Level 1 (procedural memory) and Level 2 (single-call LLM) with consume-once semantics and single-call fallback on failure. SubTaskConfig (enabled=False, chain_timeout_ms=30000, step_timeout_ms=15000, max_chain_steps=6) in SystemConfig. Six invariants enforced: token attribution to parent agent_id, no episodic memory storage, no trust attribution, no circuit breaker events, journal recording with dag_node_id, no nesting (max depth 1). QUERY steps excluded from journal (0 LLM calls). Foundation only — no handlers (632b-e), no triggers (632f), no JIT integration (632g), no parallelism (632h). 1 new file (cognitive/sub_task.py), 3 modified files (cognitive_agent.py, config.py, events.py). 41 tests across 9 classes. Build prompt: prompts/ad-632a-sub-task-foundation.md. Depends: AD-432, AD-534, AD-504. Issue #230) | AD-632e COMPLETE (Evaluate & Reflect Sub-Task Handlers — quality gates on Compose output completing 5-step chain Q→A→C→E→R. EvaluateHandler criteria-based scoring (3 modes: ward_room_quality absorbing AD-631 Pre-Submit Check, proactive_quality, notebook_quality absorbing AD-634 Pre-Write Verification Gate). Structured verdict: pass/fail, score, per-criterion, recommendation (approve/revise/suppress). ReflectHandler self-critique and revision (3 modes: ward_room_reflection, proactive_reflection, general_reflection). Suppress short-circuit when Evaluate recommends suppress — skips LLM, returns [NO_RESPONSE] with tokens_used=0. Skill instruction injection from _augmentation_skill_instructions into Reflect system prompt. Decision extractor prefers REFLECT > COMPOSE > concatenated fallback. Chain expansion 3→5 steps in _build_chain_for_intent(), both EVALUATE and REFLECT steps with required=False for graceful degradation. Fail-open design: Evaluate parse failure → pass-by-default, Reflect LLM failure → returns original Compose output unchanged. extract_json() raises ValueError not returns None — wrapped in try/except. Reflexion (Shinn et al., NeurIPS 2023). 2 new files (sub_tasks/evaluate.py, sub_tasks/reflect.py), 3 modified files (sub_tasks/__init__.py, startup/finalize.py, cognitive_agent.py), 1 new test file (57 tests across 12 classes), 2 AD-632f test assertions updated (3→5 step counts). Build prompt: prompts/ad-632e-evaluate-reflect-handlers.md. Depends: AD-632a, AD-632d. Issue #240) | AD-632h COMPLETE (Parallel Sub-Task Dispatch — wave-based concurrent execution of independent sub-task steps within chains. Adds depends_on: tuple[str, ...] to SubTaskSpec for explicit dependency declaration. Steps with satisfied dependencies dispatch concurrently via asyncio.gather(return_exceptions=True). Current chains gain EVALUATE‖REFLECT parallelism (both depend only on COMPOSE output). validate_chain() catches invalid references, cycles, self-references (fail-open). _get_ready_steps() computes per-wave readiness with backward-compatible sequential default. _execute_single_step() extracted for reuse. Follows Transporter/TaskDAG wave pattern. Closes AD-632 umbrella — all 8/8 sub-ADs complete. 2 modified files (sub_task.py, cognitive_agent.py), 1 new test file (34 tests across 7 classes). Build prompt: prompts/ad-632h-parallel-dispatch.md. Depends: AD-632a, AD-632e, AD-632f, AD-617. Issue #243) | AD-632g COMPLETE (Cognitive JIT Integration — Chain Pattern Learning. Closes learning loop between Level 3 sub-task chains (2-4 LLM calls) and Level 1 procedural replay (0 LLM calls). SOAR chunking analog. Chain-dominant episode clusters (>60% threshold) use deterministic extract_chain_procedure() (0 LLM calls) instead of LLM-based extraction. Chain procedures start at Dreyfus Level 2 (Guided) — higher than episode-derived (Level 1). learned_via="chain_compiled" (fourth value alongside direct/observational/taught). Decision dict gains chain_source + chain_steps metadata, propagated through observation._chain_metadata → episode outcomes[0]. Dream Step 7 chain-aware pre-check before LLM extraction. DreamReport gains chain_procedures_extracted counter. 4 modified files (procedures.py, cognitive_agent.py, dreaming.py, types.py), 1 new test file (29 tests across 6 classes). Build prompt: prompts/ad-632g-cognitive-jit-integration.md. Depends: AD-632a, AD-632f, AD-531-539. Issue #242) | BF-178 CLOSED (Consolidation anomaly false positives during micro-stasis recovery — stale dream history causes false consolidation_anomaly events after short stasis. Cold-start suppression only fires on true cold starts (empty trust+memory). Fix: clear_dream_history() on EmergentDetector + stasis recovery branch in startup/dreaming.py (60s suppression). Lineage: BF-166→BF-175→BF-178. 2 source files, 6 tests. Issue #241) | AD-632f COMPLETE (Sub-Task Chain Activation Triggers — inline chain construction in decide() with intent-type trigger heuristics. _should_activate_chain() three-gate evaluation: global enable check via SubTaskExecutor.enabled property (Law of Demeter), intent type filter (_CHAIN_ELIGIBLE_INTENTS: ward_room_notification + proactive_think), executor readiness. _build_chain_for_intent() constructs SubTaskChain per intent: WR chain (QUERY thread_metadata+credibility → ANALYZE thread_analysis → COMPOSE ward_room_response), proactive chain (QUERY unread_counts+trust_score → ANALYZE situation_review → COMPOSE proactive_observation). Skill preloading before chain check ensures _augmentation_skill_instructions available for Compose handler. SubTaskConfig.enabled flipped True→default. External _pending_sub_task_chain preserved as escape hatch. Config: sub_task section in system.yaml. 4 modified files (cognitive_agent.py, sub_task.py, config.py, system.yaml), 1 new test file (34 tests across 8 classes). Build prompt: prompts/ad-632f-activation-triggers.md. Depends: AD-632a, AD-632b, AD-632c, AD-632d. Issue #238) | AD-632d COMPLETE (Compose Sub-Task Handler — skill-augmented response composition for Level 3 cognitive escalation. Final LLM call in MVP chain (Query→Analyze→Compose). ComposeHandler implementing SubTaskHandler protocol. Three composition modes via Open/Closed dispatch: ward_room_response (WR posting rules + ENDORSE/NO_RESPONSE), dm_response (conversational 1:1, no action tags), proactive_observation (full action vocabulary: REPLY/ENDORSE/NOTEBOOK/PROPOSAL/DM/CHALLENGE/MOVE). Full system prompt via compose_instructions() with standing orders + personality (unlike Analyze's narrow identity). Skill instruction injection via XML <active_skill> framing with proficiency tier support. SILENT short-circuit: skips LLM call entirely when prior Analyze has contribution_assessment=SILENT or should_respond=false, returns [NO_RESPONSE] with tokens_used=0. LLM params: temperature=0.3, max_tokens=2048, tier=spec.tier. Result: {"output": llm_text} consumed by _execute_sub_task_chain() at cognitive_agent.py:1488. Three-tier error handling: LLM unavailable, LLM exception, empty response. Does NOT flip SubTaskConfig.enabled (follow-up AD). 1 new file (sub_tasks/compose.py), 2 modified files (sub_tasks/__init__.py, startup/finalize.py), 1 new test file (40 tests across 11 classes). Build prompt: prompts/ad-632d-compose-handler.md. Depends: AD-632a, AD-632c. Issue #236) | BF-177 CLOSED (Scout reports stopped — BF-024 proactive_think guard in ScoutAgent.act() blocks duty-triggered report generation. duty dict not propagated from observation to decision dict. Fix: propagate duty to decision in cognitive_agent.py, exempt duty-triggered proactive_think from guard in scout.py. 2 source files. Issue #234) | AD-632b COMPLETE (Query Sub-Task Handler — deterministic data retrieval handler for Level 3 cognitive escalation. First concrete SubTaskHandler implementation for SubTaskType.QUERY with zero LLM calls. Multiplexer pattern: spec.context_keys determines which service queries to dispatch via Open/Closed _QUERY_OPERATIONS dispatch table. 9 query operations: thread_metadata, thread_activity, comm_stats, credibility, unread_counts, unread_dms, trust_score, trust_summary, posts_by_author. Constructor-injected runtime reference (DIP). Smart context_keys separation: operation keys dispatched to query functions, data keys passed through for executor context filtering. Partial failure support: multi-key queries return merged results with per-key error reporting. _ServiceUnavailableError internal exception for defensive service access. WardRoomCredibility dataclass→dict conversion via asdict(). TrustNetwork sync methods called without await. Always tokens_used=0, tier_used="". Does NOT flip SubTaskConfig.enabled (needs Analyze/Compose handlers AD-632c/d). Startup wiring in finalize.py: SubTaskExecutor created with QueryHandler registered, wired onto all crew agents via set_sub_task_executor(). 2 new files (cognitive/sub_tasks/__init__.py, cognitive/sub_tasks/query.py), 1 modified file (startup/finalize.py), 1 new test file (31 tests across 11 classes). Build prompt: prompts/ad-632b-query-handler.md. Depends: AD-632a. Issue #232) | AD-632c COMPLETE (Analyze Sub-Task Handler — focused LLM comprehension handler for Level 3 cognitive escalation. First LLM-calling SubTaskHandler implementation for SubTaskType.ANALYZE with exactly one LLM call per invocation. Three analysis modes via Open/Closed dispatch table: thread_analysis (Ward Room thread comprehension — topics_covered, novel_posts, gaps, endorsement_candidates, contribution_assessment), situation_review (proactive think cycle — active_threads, pending_actions, priority_topics, department_relevance), dm_comprehension (DM understanding — sender_intent, key_questions, required_actions, emotional_tone). Mode selected by spec.prompt_template with thread_analysis as default fallback. Narrow system prompt: agent identity (callsign/department) for perspective, NO standing orders, NO skill instructions, NO action vocabulary. LLM call via BaseLLMClient.complete() with tier=spec.tier, temperature=0.0, max_tokens=1024. Response parsed via extract_json() utility. Prior QUERY results incorporated as factual context in user prompt. Agent identity injection: _execute_sub_task_chain() in cognitive_agent.py now injects _agent_id, _agent_type, _callsign, _department into observation dict for all handlers (Open/Closed benefit). Three-tier error handling: LLM unavailable, LLM exception, JSON parse failure — all return SubTaskResult(success=False) without exceptions. Error content truncated to 200 chars. Token accounting from LLMResponse.tokens_used. Does NOT flip SubTaskConfig.enabled (needs Compose handler AD-632d). 1 new file (cognitive/sub_tasks/analyze.py), 3 modified files (sub_tasks/__init__.py, cognitive_agent.py, startup/finalize.py), 1 new test file (39 tests across 11 classes). Build prompt: prompts/ad-632c-analyze-handler.md. Depends: AD-632a, AD-632b. Issue #233) | AD-635 v1 CLOSED (Medical Diagnostic Data Access — Clinical Telemetry Query Facade. Clearance-gated read-only query service enabling Medical (Chapel, FULL) and Counselor (Echo, ORACLE) cross-agent clinical diagnostics. v1 ships 2 of 4 data domains: dream cycle history via new EmergentDetector.recent_dreams(limit) public accessor + cross-agent CognitiveJournal chain traces via existing get_recent_chain_traces(agent_id=...). ClinicalTelemetryService with query_dream_history() and query_agent_chain_traces() — both clearance-gated (FULL/ORACLE tier floor + CLINICAL_ROLES {diagnostician,counselor}) via earned_agency.effective_recall_tier(rank, billet, grants), denied returns [] + warning + audit (never raises), bounded in-memory audit ring (default 1000). Default-False ClinicalTelemetryConfig + SystemConfig.clinical_telemetry field — invisible at runtime out-of-box. Wirer _wire_clinical_telemetry registered after _wire_diagnostic_context. Open/Closed: accessor on owner (EmergentDetector), not consumer. Demeter exception documented for runtime._emergent_detector (private). Deferred: anomaly audit trail + circuit breaker history (635b/c), REST/shell/proactive (635d/e/f), audit persistence (635b), public runtime.emergent_detector property (cleanup AD). 1 new file (cognitive/clinical_telemetry.py), 3 modified (config.py, startup/finalize.py, cognitive/emergent_detector.py), 1 new test file (9 tests). Build prompt: prompts/ad-635-medical-diagnostic-v1.md. Depends: AD-588, AD-620/621/622, AD-658. Issue #231) | AD-635 SCOPED (Medical Diagnostic Data Access — Clinical Telemetry Query Interface. Clearance-gated read-only query service enabling Medical (Chapel, FULL clearance) and Counselor (Echo, ORACLE clearance) to perform cross-agent clinical diagnostics. Four data domains: dream cycle history (EmergentDetector._dream_history), consolidation anomaly audit trail, cross-agent cognitive journal queries, circuit breaker state history. ClinicalTelemetryService query facade, proactive context injection for clinical agents, shell command (/clinical or /medbay), REST API endpoints gated by clearance. Access gates on billet clearance FULL+ AND clinical role. AD-588 extended from self-query to cross-agent clinical query. Origin: crew proposal from Chapel — proposal rejected but diagnostic access gap is genuine. Depends: AD-588, AD-620/621/622. Issue #231) | AD-634 COMPLETE (Notebook Analytical Quality Skill — config-only augmentation skill teaching analytical quality for notebook entries. One new file: config/skills/notebook-quality/SKILL.md. No code changes. Teaches: Analytical Purpose Gate, Finding-First Structure (Minto Pyramid), Temporal Threading, Data vs Analysis, Ward Room Differentiation, Anti-Patterns, Pre-Write Verification Gate. Co-activates with communication-discipline on proactive_think intent. 7-level Dreyfus proficiency (FOLLOW→SHAPE). probos-min-rank: ensign (all crew). 21 tests across 4 classes. Depends: AD-631, AD-625, AD-626, AD-596a-e. Issue #229) | AD-631 COMPLETE (Skill Effectiveness Improvements — eight structural fixes for why crew agents ignore communication-discipline skill. (1) DRY deduplication: removed Theory of Mind, Communication Etiquette, Reply Quality Standard from federation.md — absorbed into SKILL.md as single source of truth; federation.md retains channel descriptions + format mechanics only. (2) XML tag injection framing: `_frame_task_with_skill()` rewritten from plain-text `---` delimiters to `<active_skill>`, `<skill_instructions>`, `<proficiency_tier>`, `<skill_context>` XML tags per Anthropic prompt engineering guidance. (3) Self-verification gate: Pre-Submit Check section in SKILL.md — verify novelty, opening sentence structure, endorsement criteria before finalizing. (4) Positive framing with reasoning: Safety Rules→Contribution Standard rewrite using "do X because Y" instead of "Never X"; zero "Never"/"Do not" sentence starters. (5) Tier 7 XML: `<available_skills>`/`<skill>` tags replace markdown bullets in compose_instructions() system prompt. (6) Proficiency consolidation: removed standalone `_get_comm_proficiency_guidance()` injection from `_decide_via_llm()`, flow through `<proficiency_tier>` in augmentation skill frame; 4 injection points→2. (7) Anti-pattern addressing: operating sequence step 7 detects "Looking at..."/"I notice..."/"I can confirm..." openers with deletion instruction. (8) Self-monitoring XML (BF-174 root cause): bracket markers `[COGNITIVE ZONE:]`/`--- Recent Activity ---` replaced with `<cognitive_zone>`/`<recent_activity>`/`<notebook>`/`<source_awareness>` XML tags; `_strip_bracket_markers()` retained as defense-in-depth. Files: federation.md, SKILL.md, cognitive_agent.py, standing_orders.py. Tests: 23 new (test_ad631_skill_effectiveness.py) + 7 regression fixes in AD-625/626 tests. Absorbs: BF-174 root cause. Issue #226) | AD-630 COMPLETE (Leadership Developmental Feedback — Department Chiefs observe subordinate communication patterns and provide developmental mentoring via DMs. Five components: (1) Per-agent communication stats on WardRoomService layer — count_all_posts_by_author() cross-thread aggregation on ThreadManager, count_endorsements_by_voter()/count_endorsements_for_author() on MessageStore, get_agent_comm_stats() facade returning posts_total/endorsements_given/endorsements_received/credibility_score. (2) Ontology reverse lookup — get_agents_for_post() on DepartmentService, get_subordinate_agent_types() on VesselOntologyService using Post.authority_over chain. (3) Chief proactive context injection — _gather_context() conditionally injects subordinate_stats dict for agents with authority_over in ontology, minimum 3-post observation threshold, windowed since session start. (4) XML rendering — <subordinate_activity> tags in _build_user_message() proactive_think branch, one line per subordinate with all 4 stats. (5) Leadership-feedback augmentation skill — SKILL.md with pattern recognition (corrective: zero endorsements + many posts, high posts + low credibility; reinforcing: endorsements > posts, high credibility + moderate posts), DM feedback structure (observation/impact/guidance), frequency limits, pre-send verification, 7-level Dreyfus proficiency. Federation standing orders updated with Leadership and Mentorship section. Feedback flows through existing DM extraction/delivery/episodic memory/dream consolidation pipeline. Navy CPO mentorship model. 7 modified files (threads.py, messages.py, service.py, departments.py, ontology/service.py, proactive.py, cognitive_agent.py), 2 new files (SKILL.md, federation.md section), 1 new test file (28 tests across 8 classes). Issue #225) | AD-629 COMPLETE (Ward Room Reply Gate Enforcement + Post ID Context — four structural fixes for Ward Room echo-chamber pile-on. (1) Unified reply cap: `check_and_increment_reply_cap()` single enforcement point on WardRoomRouter, called by both `route_event()` and proactive `_extract_and_execute_replies()`. (2) @mention cap enforcement: `is_direct_target` bypass limited to cooldown timing only, NOT per-thread cap — @mentioned agents still capped. (3) Per-department reply gate: max one reply per department per thread via ontology department lookup, first-responder-wins (no `is_chief` field on Post). `_dept_thread_responses` dict tracks departments, cleaned up alongside `_agent_thread_responses`. (4) Post IDs in thread context: `[{id[:8]}] callsign: body` format enables `[ENDORSE post_id UP/DOWN]` (previously impossible — IDs never shown to agents). Post IDs also prefixed in proactive Ward Room activity body field across dept/All Hands/Recreation channels. Inline cap check at route_event() replaced with unified method call. Old increment block removed — counter incremented atomically inside `check_and_increment_reply_cap()`. 2 source files (ward_room_router.py, proactive.py), 1 new test file (test_ad629_reply_gate.py, 14 tests). Build prompt: prompts/ad-629-ward-room-reply-gate.md. Issue #224) | AD-628 SCOPED (Crew Skill Readiness Monitoring + Training Officer Role — Medical + Counselor skill performance monitoring with structured skill telemetry events. New Training Officer (TRAINO) crew agent for qualification tracking, training coordination, new agent onboarding, and Holodeck simulation management. Naval model: PQS + COSC + IMR + Training Team + CDB + C-Ratings. Seven sub-ADs: 628a skill telemetry events, 628b agent skill readiness profile, 628c TRAINO agent + standing orders, 628d Holodeck integration, 628e onboarding coordination, 628f readiness reporting, 628g LIMDU protocol. Depends: AD-596a–e, AD-625/626/627, AD-535, AD-506a, AD-566a–f, AD-477, AD-486, AD-539b) | AD-627 COMPLETE (Communication Skill Research Enrichment — content-only update to SKILL.md incorporating 630 lines of framework research into actionable agent instructions. 8 frameworks synthesized: Shannon information delta, Minto Pyramid answer-first, Canale & Swain communicative act typing, Robert's Rules consent-by-silence, Dreyfus 7-level proficiency mapping, ACH diagnosticity, Delphi independent analysis, military net discipline. Expanded from 52-line basic checklist to structured 5-gate pre-composition protocol with action selection matrix, thread discipline section, 10 anti-patterns with explanations, and full proficiency progression table. No code changes — config/content only. Research source: docs/research/communication-discipline-skill-research.md) | AD-626 COMPLETE (Dual-Mode Skill Activation — Discovery + Augmentation. `probos-activation` YAML frontmatter field on SKILL.md: "discovery" (default, backward-compatible), "augmentation" (supplementary instructions for handled intents), or "both". `find_augmentation_skills()` on CognitiveSkillCatalog — query method with department/rank filtering. `_load_augmentation_skills()` on CognitiveAgent — loads matching augmentation/both skills as "## Skill Guidance:" sections in _decide_via_llm() after compose_instructions(). Proficiency-gated via check_proficiency_gate(). Exercise recording for augmentation skills (fire-and-forget asyncio.create_task). `find_by_intent()` updated to exclude augmentation-only skills from discovery path. Intent parser enhanced: handles both comma-separated and space-separated probos-intents. Communication-discipline SKILL.md updated: `probos-activation: augmentation`, `ward_room_notification` added to intents. 2 modified source files (skill_catalog.py, cognitive_agent.py), 1 modified config (SKILL.md), 1 new test file (test_ad626_skill_activation.py, 46 tests). Build prompt: prompts/ad-626-dual-mode-skill-activation.md) | AD-625 COMPLETE (Communication Discipline Skill — Tier 2 cognitive skill teaching agents Ward Room reply self-evaluation with proficiency-gated system gate modulation. CommTier enum (NOVICE/COMPETENT/PROFICIENT/EXPERT) mapped from ProficiencyLevel via 4-band mapping. Gate overrides: max_responses_per_thread (1→3→4→5) and reply_cooldown_seconds (180→120→90→60) scale with proficiency. Exercise recording: every successful Ward Room post triggers record_exercise(agent_id, "communication"). Cognitive checklist in SKILL.md: Thread Awareness, Novelty Test, Action Selection, Brevity Check, Anti-Patterns. Prompt guidance injection via _get_comm_proficiency_guidance() into proactive_think composed instructions. Profile caching: runtime._comm_profiles dict pre-populated at startup from SkillService. get_descriptions() 3-tuple change: (name, description, skill_id) for proficiency label display in standing orders Tier 7. format_proficiency_label() renders tier tag for compose_instructions(). Reuses existing "communication" PCC (skill_id), not a new skill. Config overlay pattern (SOLID-O): read config then apply proficiency overrides at decision time, no default mutation. 1 new file (comm_proficiency.py), 7 modified files (SKILL.md, skill_catalog.py, standing_orders.py, cognitive_agent.py, ward_room_router.py, proactive.py, finalize.py), 1 new test file (test_ad625_comm_discipline.py, 52 tests). Build prompt: prompts/ad-625-communication-discipline-skill.md. Issue #219) | BF-176 CLOSED (TC_N always 0.000 despite Ward Room collaboration — compute_tc_n() only read REL_INTENT Hebbian weights but Ward Room records REL_SOCIAL weights. Fix: expanded to include both intent routing (REL_INTENT) and social cross-pool collaboration (REL_SOCIAL). Combined formula: (intent_multi + social_multi) / (intent_total + social_total). 1 source file (emergent_detector.py), 3 new tests) | AD-596e COMPLETE (Skill Validation + Instruction Linting — three-layer validation pipeline for cognitive skills. Layer 1: AgentSkills.io structural validation — native implementation of 5 spec rules (name format regex, name length ≤64, name-directory match, description length ≤1024, compatibility length ≤500). Layer 2: ProbOS metadata cross-references — department against _AGENT_DEPARTMENTS, rank against _RANK_ORDER, min_proficiency 1-5 range, skill_id against SkillRegistry (warning not error). Layer 3: Callsign linting (BF-146 class) — word-boundary regex case-insensitive scan of instruction body for hardcoded agent callsigns from CallsignRegistry (warnings). SkillValidationResult dataclass (skill_name, valid, errors, warnings, to_dict()). validation_context dict injected by caller — catalog never imports runtime/CallsignRegistry/SkillRegistry (Law of Demeter). validate_skill() and validate_all() on CognitiveSkillCatalog. enrich_skill() gains optional validation_context for post-enrichment advisory validation. _build_validation_context() helper in routers/skills.py + commands_skill.py. Two REST endpoints: GET /catalog/{name}/validate, GET /catalog/validate. /skill validate [name] shell subcommand with Rich table output. Parser vs Validator separation: parse_skill_file() permissive (Postel's Law), validate_skill() strict. 3 existing files modified (skill_catalog.py, routers/skills.py, commands_skill.py), 1 new test file (35 tests). Build prompt: prompts/ad-596e-skill-validation-linting.md. Issue #166) | AD-596d COMPLETE (External Skill Import — AgentSkills.io-compatible external skill consumption. Four new methods on CognitiveSkillCatalog: import_skill() validates SKILL.md + copies into config/skills/ + overrides origin="external" + registers; discover_package_skills() walks site-packages for .agents/skills/*/SKILL.md convention (discovery-only, no auto-import, respects Captain's authority); enrich_skill() updates ProbOS metadata (department, skill_id, min_proficiency, min_rank, intents) + rewrites SKILL.md frontmatter preserving body and non-ProbOS fields; remove_skill() guards origin=="external" + deletes from cache + SQLite + filesystem. Three REST endpoints: POST /catalog/import, GET /catalog/discover, PUT /catalog/{name}/enrich. /skill shell command with 6 subcommands (list, discover, import, info, enrich, remove) — flag-based enrichment (--dept, --skill-id, --intents, --min-prof, --min-rank). Origin tracking: parse_skill_file() defaults "internal", import_skill() overrides to "external" (fixes D2 latent bug). 1 new file (commands_skill.py), 3 existing files modified (skill_catalog.py, routers/skills.py, shell.py), 1 new test file (23 tests). Build prompt: prompts/ad-596d-external-skill-import.md. Issue #166) | AD-596c COMPLETE (Skill-Registry Bridge — Stateless coordinator connecting CognitiveSkillCatalog (T2 instruction-defined skills) and SkillRegistry/AgentSkillService (T3 proficiency-tracked skills). SkillBridge class with 4 methods: validate_and_sync() startup validation, check_proficiency_gate() trust-gated skill access, record_skill_exercise() fire-and-forget exercise tracking with auto-acquire at FOLLOW, resolve_skill_for_gap() enhanced intent→skill mapping (replaces Law of Demeter violation on registry._skills). CognitiveAgent proficiency gate: silent self-deselect when agent lacks proficiency for governed skill. Exercise recording via asyncio.create_task() after faithfulness blocks. Gap predictor enhanced with optional skill_bridge parameter — public API path + legacy fallback. Agent onboarding wires _skill_bridge + cached SkillProfile on crew agents. Procedure dataclass gains source_skill_id for T2→T3 provenance chain. ProcedureStep.to_dict() serialization fix for required_tools field. BF-596b ordering fix: set_skill_catalog() relocated from Phase 6→Phase 7 (was always no-op due to None cognitive_skill_catalog). Startup wiring in runtime.py + finalize.py, shutdown cleanup in shutdown.py. 1 new file (skill_bridge.py), 8 existing files modified, 1 new test file (24 tests), 1 existing test file updated. Build prompt: prompts/ad-596c-skill-registry-bridge-build.md. Issue #166) | BF-175 CLOSED (Consolidation Anomaly False Positives — Absolute Floor Thresholds. 604 false positive consolidation_anomaly events over 8 days (563 "unusual trust adjustments" + 41 "unusual pruning"). Root cause: BF-166's 2x historical average threshold has no absolute floor — when baseline avg=4, a count of 9 triggers anomaly despite being normal for a 55-agent crew. Fix: added configurable minimum absolute floor thresholds (dream_anomaly_min_strengthened=10, dream_anomaly_min_pruned=5, dream_anomaly_min_trust_adj=10) as AND conditions alongside existing 2x multiplier check. Crew-identified by Vega → Meridian → Captain. 4 source files modified (config.py, emergent_detector.py, dreaming.py, test_emergent_detector.py), 5 new tests. Relates to: BF-166, BF-126, BF-165) | AD-596b COMPLETE (Intent Discovery + compose_instructions() Integration — Wires cognitive skill catalog into all four cognitive pathways. Tier 7 skill descriptions in `compose_instructions()` via `get_descriptions(department, min_rank)` — progressive disclosure (~100 tokens). `handle_intent()` cognitive skill fallback on unmatched intents via `find_by_intent()` + full SKILL.md instruction injection. `_decide_via_llm()` skill catalog injection. `_gather_context()` Section 7 cognitive skills in proactive loop. Agent onboarding `wire_agent()` wires `_cognitive_skill_catalog` + IntentBus subscription. `set_skill_catalog()` / `_skill_catalog` module-level setter in `standing_orders.py` (matches `set_directive_store()` pattern). Startup wiring in `runtime.py`, shutdown cleanup in `shutdown.py`. Bug fix: removed undefined `runtime` reference in `structural_services.py` — relocated `set_skill_catalog()` to `runtime.py`. 8 source files modified (standing_orders.py, cognitive_agent.py, proactive.py, agent_onboarding.py, finalize.py, structural_services.py, runtime.py, shutdown.py), 1 new test file (21 tests across 6 classes). Build prompt: `prompts/ad-596b-intent-discovery-integration.md`. Issue #166) | AD-596a COMPLETE (Cognitive Skill File Format + Loader — AgentSkills.io `SKILL.md` format adoption with ProbOS metadata extensions. `parse_skill_file()` extracts YAML frontmatter (name, description, license, compatibility + ProbOS metadata: department, skill_id, min_proficiency, min_rank, intents). `get_skill_body()` returns markdown content below frontmatter. `CognitiveSkillCatalog` Ship's Computer service: `start()`/`stop()` lifecycle, `scan_and_register()` discovers `config/skills/*/SKILL.md`, `register()` persists to SQLite + cache, `get_entry()`/`list_entries(department, min_rank)` with department scoping + rank filtering via `_RANK_ORDER`, `get_descriptions()` progressive disclosure (name + description tuples), `get_instructions()` on-demand body loading, `get_intents()`/`find_by_intent()` reverse lookup. `CognitiveSkillEntry` dataclass with origin tracking (internal/external). ConnectionFactory pattern matching SkillRegistry. REST API: `GET /catalog` (filtered list), `GET /catalog/{name}` (full entry + instructions), `POST /catalog/rescan` (re-scan trigger). Startup wiring in `communication.py`, shutdown in `shutdown.py`, runtime attribute. Example skill: `config/skills/communication-discipline/SKILL.md` (placeholder for AD-625). 1 new module (`cognitive/skill_catalog.py`), 1 new skill dir, 5 existing files modified (results.py, communication.py, shutdown.py, runtime.py, routers/skills.py), 1 new test file (27 tests across 5 classes). Build prompt: `prompts/ad-596a-cognitive-skill-loader.md`. Issue #166) | AD-423c COMPLETE (ToolContext + Role-Based Tool Assignment — ToolContext scoped permission-filtered view of ToolRegistry per agent (dataclass with agent identity snapshot: agent_id, agent_rank, agent_department, agent_types). Wraps shared ToolRegistry, delegates permission checks via resolve_permission(). Methods: available_tools() (permission-filtered, optional tool_type filter), has_tool(), invoke() (delegates to check_and_invoke() with merged agent context), get_permission(), refresh() (identity re-snapshot on rank/department change), to_dict(). Constructed at onboarding via wire_agent() — sovereign_id resolution, rank from Rank.from_trust(), department from ontology or standing_orders.get_department(). Late-bound via set_tool_registry() public setter (LoD). DutyDefinition.required_skills informational field (Duty→Skill link). ProcedureStep.required_tools declarative field (Cognitive JIT tool dependencies). CognitiveAgent.tool_context attribute. TOOL_CONTEXT_CREATED event emitted during onboarding. Startup wiring in finalize.py follows set_orientation_service() pattern. 1 new file (tools/context.py), 7 existing files modified (protocol.py, agent_onboarding.py, finalize.py, procedures.py, config.py, cognitive_agent.py, events.py), 2 new test files (27 tests across 7 classes). Build prompt: prompts/ad-423c-toolcontext-onboarding.md. Issue #146) | AD-423b COMPLETE (Tool Permissions & Scoping — CRUD+O permission model with 5 additive levels (NONE < OBSERVE < READ < WRITE < FULL), five-layer permission resolution chain (enabled → department scope → restricted_to allowlist → rank gate via default_permissions matrix → Captain override grants), LOTO (Lock-Out/Tag-Out) exclusive access with timeout auto-expire + Captain break_lock, ToolPermissionStore (SQLite-backed, WAL mode, in-memory cache for sync reads, issue/revoke/list grants), ToolPermissionDenied exception with rich context (held vs required), check_and_invoke() permission-checked tool invocation, /tool-access shell command (grant/restrict/revoke/break-lock/list/check subcommands), 3 event types (TOOL_PERMISSION_DENIED, TOOL_LOCKED, TOOL_UNLOCKED). Deny-by-default. 4 new files (tools/permissions.py, experience/commands/commands_tool_access.py + 2 modified tool files), 7 existing files modified (shell.py, events.py, results.py, communication.py, runtime.py, shutdown.py, config.py), 1 new test file (28 tests across 7 classes). Build prompt: prompts/ad-423b-tool-permissions.md. Issue #145) | AD-423a COMPLETE (Tool Foundation — unified Tool protocol, ToolRegistry service, 3 adapter implementations, SkillDefinition.preferred_tools. Tool protocol (typing.Protocol, runtime-checkable) with ToolType 9-value enum, ToolResult frozen dataclass, ToolRegistration metadata wrapper (domain/department/tags/provider/enabled), ToolPreference skill→tool link. ToolRegistry in-memory catalog with register/unregister/get/list_tools filtering by type/domain/department/tag/enabled. Three adapters: InfraServiceAdapter (intent bus broadcast), DirectServiceAdapter (async handler), DeterministicFunctionAdapter (sync callable). Ontology seeding: 7 ToolCapability entries from resources.yaml → DirectServiceAdapter with noop handlers (AD-423c replaces with real bindings). SkillDefinition gains preferred_tools field with SQLite migration. Absorbs AD-483 (Tool Layer — Instruments). 4 new files (tools/__init__.py, tools/protocol.py, tools/registry.py, tools/adapters.py), 4 existing files modified (skill_framework.py, results.py, communication.py, runtime.py), 1 new test file (24 tests across 7 classes). Build prompt: prompts/ad-423a-tool-foundation.md. Issue #144, closes #77) | AD-623 COMPLETE (DM Convergence Gate + DM Self-Monitoring — structural fix for DM conversation loops. Two mechanisms: (1) DM Convergence Gate in WardRoomRouter — check_dm_convergence() detects mutual agreement via cross-author Jaccard similarity (threshold 0.55) over last 3 exchange pairs, emits DM_CONVERGENCE_DETECTED event and returns early (stops routing). Thread-level check before per-agent loop. (2) DM Self-Monitoring — _build_dm_self_monitoring() on CognitiveAgent injects self-similarity warning (>=0.4) into ward_room_notification DM response path, closing the gap where self-monitoring only ran in proactive loop. Also: Counselor handler for DM_CONVERGENCE_DETECTED event, get_posts_by_author() gains optional thread_id filter, check_dm_convergence() facade on WardRoomService. Companion to BF-168 (dm_exchange_limit 6→3). 7 source files modified (threads.py, service.py, ward_room_router.py, cognitive_agent.py, counselor.py, events.py, models.py), 1 new test file (18 tests). Build prompt: prompts/ad-623-dm-convergence-gate.md. Issue #212) | BF-168 CLOSED (DM exchange limit too generous — Atlas-Kira DM thread showed 12+ repetitive exchanges despite dm_exchange_limit=6. Root cause: 6 exchanges = 3 back-and-forth rounds was too generous for typical DM conversations that converge within 2-3 exchanges. Fix: lowered dm_exchange_limit from 6 to 3 in config.py. One-line change. Relates to: AD-614, AD-623) | BF-167 CLOSED (get_embeddings() numpy array truthiness + MemoryGraph3D missing dimensions — AD-611 Memory Graph showed black canvas with no semantic edges. Two root causes: (1) episodic.get_embeddings() used bare `not result["embeddings"]` and `if emb and` checks on ChromaDB return values which are numpy arrays — numpy arrays don't support bare truthiness. Fix: `is None` checks. Latent since AD-531, never triggered until AD-611. (2) ForceGraph3D rendered 0×0 canvas — react-force-graph-3d doesn't auto-detect container size from CSS flex layout. Fix: ResizeObserver measures container, passes explicit width/height props. 2 source files modified (episodic.py, MemoryGraph3D.tsx), 0 new tests) | AD-622 COMPLETE (Special Access Grants (ClearanceGrant) — Captain-issued, time-limited, scoped, revocable elevated access (SAP analog). (1) ClearanceGrant frozen dataclass in earned_agency.py with id/target_agent_id/recall_tier/scope/reason/issued_by/issued_at/expires_at/revoked/revoked_at. (2) effective_recall_tier() gains grants parameter — max(rank, billet, grants). (3) resolve_active_grants() Law of Demeter helper — fail-open on None store or exception. (4) ClearanceGrantStore in clearance_grants.py — SQLite-backed with WAL mode + busy_timeout(5000) + synchronous=NORMAL, in-memory cache populated at startup and updated on issue/revoke, get_active_grants_sync() zero-I/O reads from cache. (5) Shell /grant command (issue/revoke/list) in commands_clearance.py — callsign resolution, sovereign ID targeting, prefix-match revocation, Rich table output. (6) Wired in startup pipeline (results.py, communication.py, runtime.py, shutdown.py). (7) Grant resolution at both tier resolution call sites (cognitive_agent.py, proactive.py) using sovereign_id with slot ID fallback. 2 new files (clearance_grants.py, commands_clearance.py), 8 existing files modified, 1 new test file (22 tests across 6 classes). Build prompt: prompts/ad-622-clearance-grants.md. Issue #208) | AD-621 COMPLETE (Billet-Driven Channel Visibility — Subscription-based routing replaces department matching. (1) WardRoomRouter.find_targets()/find_targets_for_agent() use membership cache (_channel_members dict) instead of home department comparison — agents must be subscribed to receive department channel events. (2) Membership cache populated at startup via populate_membership_cache() from WardRoomService.get_all_channel_members(). (3) communication.py subscription refined from AD-620 FULL+ clearance to ontology-driven: reports_to: captain (First Officer + Counselor only) get all department channels. (4) list_channels(agent_id) INNER JOIN with memberships table for subscription-filtered queries. (5) Earned Agency same_department flag preserved: cross-dept subscribers get same_department=False (higher threshold), home-dept agents get True. (6) @mention overrides subscription — mentioned agents always receive events. (7) Ship-wide and DM routing unchanged. 7 source files modified, 1 new test file (18 tests). Design: docs/research/clearance-system-design.md. Issue #207) | AD-620 COMPLETE (Clearance Model Foundation — Separation of Rank and Access Eligibility. Billet-based clearance replaces AD-619 ship-wide authority hack. (1) `clearance` field on Post dataclass + organization.yaml — Bridge=ORACLE, Chiefs=FULL, Officers=ENHANCED. (2) `_TIER_ORDER` dict, `effective_recall_tier(rank, billet_clearance)` = max(rank-tier, billet-tier), `resolve_billet_clearance()` Law of Demeter helper — all in earned_agency.py. (3) cognitive_agent.py + proactive.py tier resolution replaced with effective_recall_tier(). (4) Oracle gate simplified — ORACLE clearance alone, strategy requirement removed. (5) communication.py FULL+ billet clearance → all department channel subscriptions. (6) Removed `has_ship_wide_authority()` + `_SHIP_WIDE_AUTHORITY_TYPES` from crew_utils.py. (7) Migrated test_ad619 to clearance model. 7 source files modified, 1 config file modified, 1 test file migrated, 1 new test file (27 tests). Design: docs/research/clearance-system-design.md. Issue #206) | AD-619 COMPLETE (Counselor Cross-Department Awareness — Ship-wide authority agents get all department channel subscriptions at startup + Oracle recall tier override for cross-agent clinical awareness. New `has_ship_wide_authority()` helper in crew_utils.py (DRY/Open-Closed — extensible set, not hardcoded agent type checks). Standing orders updated with Cross-Department Awareness section. 3 source files modified, 1 new test file. Build prompt: prompts/ad-619-counselor-cross-department-awareness.md. Issue #205. NOTE: To be superseded by AD-620/621/622 clearance system) | AD-618 SCOPED (Bill System — Standard Operating Procedures. Declarative YAML-based multi-agent SOPs with role-based assignment, BPMN-vocabulary decision points, qualification gates, Cognitive JIT learning bridge. Missing middle layer between Standing Orders (T1, policy) and Cognitive JIT (T3, learned). Navy model: Bills + MRCs + WQSB. Five sub-ADs: AD-618a schema+parser, AD-618b instance+runtime, AD-618c built-in Bills, AD-618d HXI dashboard, AD-618e Cognitive JIT bridge. OSS: framework + runtime + built-in Bills. Commercial: SOP marketplace, visual designer, enterprise governance. Research: docs/research/standard-operating-procedures.md. Issue #204) | BF-166 CLOSED (Consolidation Anomaly False Positives After Stasis — detect_consolidation_anomalies() fires against stale dream history with only 1-2 reports as baseline, no cold-start suppression, unbounded _dream_history list. Fix: (A) minimum history gate raised from 2 to configurable dream_min_history (default 5), (B) cold-start suppression via _suppress_dreams_until in set_cold_start_suppression(), (C) _dream_history bounded with deque(maxlen=max_history), (D) dream_min_history config in EmergentDetectorConfig. 3 source files modified, 1 new test file (8 tests), 2 existing tests updated) | BF-165 CLOSED (Cooperation Cluster False Positives During Stasis — detect_cooperation_clusters() fires against stale Hebbian weights during zero cognitive activity. BF-126 time-bounded suppression expires after 5 minutes while stasis continues. Fix: cognitive activity gate — record_activity() called from runtime on Hebbian record_interaction(), detect_cooperation_clusters() checks if any interaction occurred within configurable cluster_activity_window (default 900s), skips detection if stale. Config: cluster_activity_window in EmergentDetectorConfig (0 = disabled). 4 source files, 8 tests) | BF-164 CLOSED (Stale Unread DM Notification Loop — AD-614's exchange limit blocked agents from responding to old flood threads, but `get_unread_dms()` kept returning those threads — causing infinite BF-082 notification cycles every ~2 minutes. Fix: `get_unread_dms()` accepts `exchange_limit` parameter, SQL subquery excludes threads where agent already has >= exchange_limit posts. `_check_unread_dms()` reads `dm_exchange_limit` from config and passes to query. Log accuracy fix: BF-082 log now reports `routed` count, not raw query count. 3 source files modified (messages.py, service.py, proactive.py), 1 new test file (8 tests). Issue #202). BF-163 CLOSED (DM Send Flood — Agent-to-Agent Feedback Loop. `_extract_and_execute_dms()` had zero rate limiting on the sending side. BF-156's `is_direct_target` bypass only exempted DM recipients from cooldown, not senders. Forge sent 40+ DMs to Chapel in ~2 minutes — feedback loop: Agent A DMs Agent B → B gets BF-082 notification → B responds → A gets notified → repeat, overwhelming LLM Copilot Proxy with HTTP 500 errors. Fix: per-agent per-target 60-second cooldown using `{agent.id}:{target_callsign.lower()}` composite key and `time.monotonic()` timestamps. First DM always allowed; subsequent DMs to same target throttled for 60s. 1 source file (proactive.py), 1 new test file (9 tests). Issue #197). AD-614 COMPLETE (DM Conversation Termination — three-layer defense against DM conversation flooding. Layer 1: Standing order instructions — conversation closure guidance in federation.md + DM self-monitoring in ship.md. Layer 2: DM send self-similarity gate — Jaccard word-set similarity (>= 0.6) against last DM body per agent-target pair in `_extract_and_execute_dms()`. Layer 3: DM thread exchange limit — per-agent post count cap (default 6) per DM thread in `ward_room_router.py`, applies even to `is_direct_target` DMs. New `count_posts_by_author()` on ThreadManager/WardRoomService. Config: `dm_exchange_limit`, `dm_similarity_threshold` in WardRoomConfig. 7 source files modified (federation.md, ship.md, proactive.py, ward_room_router.py, config.py, threads.py, service.py), 1 new test file (17 tests). Issue #198). AD-615 COMPLETE (Ward Room Database Performance Hardening — WAL mode + busy_timeout(5000) + synchronous=NORMAL in ward_room/service.py start(). Matches BF-099 canonical pattern from trust.py/routing.py. WAL verification log at startup with log-and-degrade on network filesystem failure. Ward Room was the only ProbOS database without WAL mode. 1 source file modified, 1 new test file (5 tests). Issue #199). AD-616 COMPLETE (Ward Room Router Hot Path Optimization — channel cache usage, event dispatch semaphore, backend event coalescing. Router calls list_channels() 4 times via DB queries when ChannelManager already has an in-memory cache. Fire-and-forget asyncio.create_task() on route_event() has zero backpressure — thousands of concurrent tasks under flood. Fix: expose get_channel_by_id() using cache, add asyncio.Semaphore(10) to route_event dispatch, add 200ms per-thread event coalescing. Config: router_concurrency_limit, event_coalesce_ms in WardRoomConfig. 3 source files). AD-617b COMPLETE (Per-Agent Hourly Token Budget — per-agent fairness enforcement at proactive loop gate. CognitiveJournal.get_token_usage_since() hourly window query + _is_over_token_budget() gate after circuit breaker + per_agent_hourly_token_cap config field on LLMRateConfig (default 0 = disabled). Budget exhaustion = skip proactive thinks, DMs/WR/Captain directives bypass. 60s exhaustion cache per agent. token_budget_exhausted event for Counselor. 3 source files (journal.py, proactive.py, config.py), 1 new test file (13 tests across 3 classes). Build prompt: prompts/ad-617b-per-agent-token-budget.md. Issue #210). AD-617 COMPLETE (LLM Rate Governance — token bucket rate limiter per tier (fast=60/standard=30/deep=15 RPM sliding window), HTTP 429 exponential backoff with Retry-After header respect and inner retry loop (cap 5 retries, backoff 2/4/8s), LRU cache eviction via OrderedDict (500 entries default). Config: LLMRateConfig in SystemConfig with rpm_fast/rpm_standard/rpm_deep/max_wait_seconds/cache_max_entries. Rate limit exceeded returns cached response or error. 429 counter resets on success. 3 source files modified (llm_client.py, config.py, __main__.py), 1 new test file (13 tests across 4 classes). Issue #201) | AD-617b COMPLETE (Per-Agent Hourly Token Budget — fairness enforcement at proactive loop gate. Budget-exhausted agents skip proactive thinks but still receive DMs/WR notifications/Captain directives. CognitiveJournal.get_token_usage_since() queries hourly sliding window (excludes cached). ProactiveCognitiveLoop._is_over_token_budget() with 60-second exhaustion cache. Gate ordering: after circuit breaker, before _think_for_agent(). Event emission: token_budget_exhausted for Counselor awareness. Config: per_agent_hourly_token_cap on LLMRateConfig (default 0 = disabled). 3 source files modified (journal.py, proactive.py, config.py), 1 new test file (13 tests across 3 classes). Issue #201). AD-613 COMPLETE (Ward Room HXI Performance — Query Batching, Event Debouncing, Caching. Six fixes across 5 files + 1 new test file (10 tests): (1) WebSocket event debouncing — 300ms flag-based coalescing in useStore.ts, module-level timer with per-flag batching. (2) N+1 DM query elimination — `count_threads()` SQL COUNT(*) on ThreadManager/WardRoomService replaces `len(list_threads(100))`. (3) Per-channel thread cache — Map-based 30s TTL in Zustand store. (4) Post pagination — `get_thread()` keyword-only `post_limit=100` + `total_post_count`, DESC LIMIT + reverse. (5) Conditional DM poll — `isOpen` guard on WardRoomPanel useEffect. (6) Composite SQL indexes — `idx_threads_channel_activity`, `idx_threads_channel_archived` (in service.py after ALTER TABLE migration), `idx_posts_thread_created`. 5 source files modified (useStore.ts, threads.py, service.py, models.py, WardRoomPanel.tsx), 1 API router updated (wardroom.py). Issue #196). BF-162 CLOSED (Introspective Faithfulness False Positives — AD-589's `_MANIFEST_CONTRADICTIONS` regex rules fired on conversational idioms ("my intuition suggests", "gut feeling about", "subconsciously noticed", "continuous awareness of"). Uniform `claims=1, contradictions=1` across Lynx/Reyes/Forge = systematic false positive, not genuine confabulation. Downstream: inflated `confabulation_rate` via EMA (alpha=0.1), degraded DEEP→SHALLOW retrieval at >0.3, emitted false SELF_MODEL_DRIFT events, corrupted episode metadata. Fix: `_IDIOM_EXEMPTIONS` pattern list in source_governance.py checked only when contradiction already matched — exemption wins for conversational constructions. 5 exemption patterns: intuition suggests/tells, instinctively/intuitively adverb, gut feeling about, subconsciously + past/gerund verb, continuous awareness/monitoring of. 1 source file (source_governance.py), 1 test file (test_ad589), 12 new tests + 2 updated. 50/50 tests pass. Issue #195). BF-161 CLOSED (Cognitive zone UNKNOWN for GREEN agents — `_build_self_monitoring_context()` only set `cognitive_zone` for non-green zones, so working memory never received the green value. `IntrospectiveTelemetryService.get_cognitive_state()` read from working memory and got nothing → downstream renders showed "UNKNOWN" for healthy agents. Fix: always include zone in self-monitoring context (removed `if zone != "green":` guard), added `"green"` default in telemetry service. Crew-identified by Horizon. 2 source files, 1 test file modified. Issue #194). AD-612 COMPLETE (DM Rendering + Thread Depth + DM Tag Robustness — three related Ward Room communication quality fixes. (A) Hardened DM regex in proactive.py — replaced newline-requiring pattern with two-tier extraction: closed DMs tolerate any whitespace (multiline/single-line/inline), unclosed DMs capture to next [DM tag or end-of-string. Negative lookahead prevents cross-DM-tag consumption. (B) Flat IM rendering for DM channels — WardRoomPostItem gains flat/allPosts/parentCallsign props, DM detail view renders chronological flat messages with "replying to @callsign" back-references instead of threaded tree. WardRoomThreadDetail detects dm-detail from store, flattenPosts() helper. (C) Thread depth flattening — MAX_THREAD_DEPTH constant lowered from 4 to 2, depth>=2 shows parentCallsign back-reference. 3 source files modified (proactive.py, WardRoomPostItem.tsx, WardRoomThreadDetail.tsx), 2 new test files (12 pytest + 7 vitest = 19 tests). Issue #193). AD-611 COMPLETE (3D Memory Graph Visualization — interactive force-directed graph of agent episodic memory in HXI. Backend: new memory_graph router with three-tier node selection (recency 70%/importance 20%/activation 10%), four edge types (semantic via HNSW cosine, thread co-occurrence, temporal proximity 5min, participant Jaccard ≥0.3). Frontend: react-force-graph-3d in new Memory tab on agent profile panel with node color by channel/department, importance-driven size, activation-driven opacity (sigmoid normalization), click-to-detail, edge type legend. Ship-wide toggle merges across crew agents. 200 node default, 500 cap, 2000 edge cap. 1 new backend router (memory_graph.py), 3 new frontend components (memoryGraphTypes.ts, MemoryGraph3D.tsx, ProfileMemoryTab.tsx), AgentProfilePanel.tsx modified, api.py wired. 11 new pytest tests. Build prompt: prompts/ad-611-memory-graph-3d.md. Issue #192). BF-156/157 CLOSED (DM Delivery + @Mention Response Guarantee — five changes across 3 source files fixing two communication reliability bugs. BF-156: Agents don't read DMs — _check_unread_dms() sat after Ensign gate in proactive loop (Ensigns never reached it), per-agent cooldown suppressed DM notifications, thread depth cap truncated DM conversations. Fix: moved _check_unread_dms() before Ensign gate, DM recipients bypass cooldown/round/per-thread caps via is_direct_target flag, DM channels bypass thread depth cap (check moved after channel lookup). BF-157: @mention doesn't guarantee response — mentions used for routing only, never forwarded to agent; LLM prompt offered [NO_RESPONSE] even for @mentioned agents. Fix: mentioned_agent_ids set built from _callsign_registry.resolve(), was_mentioned flag passed in IntentMessage params, @mentioned agents bypass cooldown/caps, prompt says "You were directly @mentioned — a response is expected" without [NO_RESPONSE] option. 3 source files modified (ward_room_router.py, cognitive_agent.py, proactive.py), 2 new test files (7 tests), 3 existing tests updated. Issues #187, #188). BF-159/160 CLOSED (Qualification Probe Fix Wave — 5/221 test failures resolved. BF-159: AUTHORITATIVE source framing suppressed temporal preference — agents with 3,500+ real episodes hit AUTHORITATIVE tier during probes, losing "prefer most recent observation" instruction. AGM Belief Revision is a logical principle, not a quality concern — applied universally across all tiers. BF-160: CrossAgentSynthesisProbe false failure at CREW level — BF-150 redesigned from cross-shard to sovereign-shard (per-agent) but tier remained 3, causing `run_collective()` to invoke with `__crew__` → agent not found → score=0.0. Added skip guard returning `_make_skip_result`. 2 source files modified (cognitive_agent.py, memory_probes.py), 2 test files modified (test_bf148, test_bf150), 2 new tests + 1 modified test. Issues #190, #191). AD-598 COMPLETE (Importance Scoring at Encoding — rule-based 1-10 importance scoring computed at episode creation time via `compute_importance()` in new `importance_scorer.py` module. Three scoring signals: (1) trigger_type mapping from AnchorFrame (captain_dm→8, circuit_breaker_trip→9, proactive_thought→3), (2) content boosts (Captain mentions, Ward Room), (3) outcome adjustments (failures boost, no-response degrades). Importance modifies: activation decay rate via `compute_activation_with_importance()` (importance/5.0 factor — importance=10 decays 2x slower), pruning threshold via `find_low_activation_episodes_with_importance()` (adjusted_threshold = threshold − (importance−5)×0.2), and recall composite scoring (normalized 0-1, weight 0.05 tiebreaker). Episode dataclass gains `importance: int = 5` field. Backward compatible — existing episodes default to 5 (neutral). Dream Step 12 standard + aggressive tiers use importance-aware pruning with fallback. 5 source files modified (types.py, importance_scorer.py NEW, episodic.py, activation_tracker.py, dreaming.py), 1 new test file (19 tests across 6 classes). Issue #172). AD-605 COMPLETE (Enhanced Embedding — Content + Anchor Metadata Concatenation. `_prepare_document()` static method concatenates non-empty AnchorFrame fields as bracketed prefixes `[department] [channel] [watch_section] [trigger_type]` into document text before ChromaDB embedding. 3 write sites updated (store, seed, _force_update). Original `user_input` preserved in metadata for clean Episode reconstruction via `metadata.get("user_input", document)` fallback. One-time idempotent migration `migrate_enriched_embedding()` re-embeds existing episodes and populates user_input metadata. Wired in startup/cognitive_services.py after `migrate_embedding_model()`. 2 source files modified (episodic.py, cognitive_services.py), 1 new test file (15 tests across 4 classes). Issue #181). AD-606/607/608/609/610 SCOPED (Temporal recall merge contamination — wrong-watch episodes outscore correct watch. Three changes: (A) pre-merge watch filtering in cognitive_agent.py excludes wrong-watch semantic episodes at merge boundary when _query_watch_section set, (B) temporal mismatch suppression penalty −0.15 in score_recall() when query has temporal intent but episode watch doesn't match (clamped to 0.0), (C) temporal_match_weight 0.10→0.25 in MemoryConfig + match/mismatch mutually exclusive (0.40 composite swing). 3 source files modified (cognitive_agent.py, episodic.py, config.py), 1 new test file (14 tests across 4 classes). Deep work deferred: AD-601/602/603/604. Issue #176). AD-601/602/603/604 SCOPED (Deep Temporal Discrimination Enhancements. AD-601: TCM temporal context vectors — Howard & Kahana 2002, continuous context gradient replaces binary watch match. AD-602: question-adaptive retrieval routing — classify_retrieval_strategy() gains question-type dimension, TEMPORAL/SOCIAL/FACTUAL use different weight profiles. AD-603: anchor recall composite scoring — recall_by_anchor() returns RecallScore not raw Episodes, enables scored merge. AD-604: spreading activation multi-hop — two-hop associative recall for causal/narrative queries. Priority: AD-603→AD-601→AD-602→AD-604). BF-154/154b CLOSED (TemperamentProbe + SeededRecallProbe timeout — sequential LLM calls exceeded 60s harness timeout. asyncio.gather() parallelizes axes/questions. TemperamentProbe: 4 axes parallel. SeededRecallProbe: 5 questions parallel, eliminated 10% timeout rate (17/173 runs). 2 source files, 1 new test file (10 tests)). BF-153 CLOSED (Shell directive commands use seed callsign instead of runtime callsign. Crew-identified by Reyes. get_callsign() only read YAML seed profile, ignoring CallsignRegistry after naming ceremony. Fix: runtime.callsign_registry checked first, falls back to seed profile, then formatted agent_type. 4 call sites + shell proxy wired. 2 source files, 1 new test file (10 tests)). BF-152 CLOSED (Temporal reasoning probe — keyword scoring gap for JSON agents. Same root cause as BF-151: check_faithfulness() false-zero + LLM scorer variance. Probe computed correct_found via _distinctive_keywords() but never used as score signal. Fix: key_values on _TEMPORAL_EPISODES + keyword_score as 3rd signal max(faithfulness, llm, keyword) with -0.3 wrong-watch penalty + "ship memory" nudge. 1 source file, 1 new test file (18 tests)). BF-151/151b CLOSED (Seeded recall probe + knowledge update probe JSON scoring mismatch. key_values 3-tuple on _RECALL_FACTS + keyword_score signal + old_aliases/new_aliases on _UPDATE_PAIRS + bare value aliases. 1 source file, 1 test file (33 tests). Issue #175). BF-147/148/149/150 CLOSED (Qualification Probe Hardening Wave — 4 bug fixes for memory probe failures. BF-147: watch section vocabulary fix ("first_watch"→"first", "second_watch"→"second_dog") + temporal_match scoring in score_recall()/recall_weighted() + parse_anchor_query "recently" regex fix. BF-148: timestamp calculation fix (no future dates) + temporal preference instruction in _confabulation_guard() for SUPPLEMENTARY/PERIPHERAL tiers. BF-149: empty-response retry-once across all 4 memory probes + empty_response diagnostic field. BF-150: sovereign shard synthesis redesign — all episodes seeded in tested agent's own shard with department attribution, probe_pathway metadata. 7 source files modified, 4 new test files, 28 new tests. Build prompt: bf-147-148-149-150-probe-hardening-wave.md). AD-597 SCOPED (MCP App Host Infrastructure + Interactive Games. ProbOS becomes MCP App host — interactive HTML apps render inside HXI chat via sandboxed iframes + JSON-RPC postMessage bridge. MCP Apps standard covers both Anthropic and OpenAI ecosystems. 6 sub-ADs: AD-597a (HXI AppBridge), AD-597b (game MCP server), AD-597c (chess engine), AD-597d (chess UI), AD-597e (tic-tac-toe migration), AD-597f (external app consumption). Issue #167). AD-596 SCOPED (Cognitive Skill Registry — AgentSkills.io-Compatible Skill Library. Four-tier capability model: T1 Standing Orders, T2 Cognitive Skills (SKILL.md), T3 Executable Skills (Cognitive JIT), T4 Tool Skills (CapabilityDescriptor). Adopts AgentSkills.io open standard for T2 format — interoperable with 30+ tools. ProbOS metadata extensions as additive optional fields. T2-to-T3 self-improvement pathway via existing Cognitive JIT pipeline. Absorbs skills-ref for validation. 5 sub-ADs: AD-596a (format+loader), AD-596b (intent discovery+compose_instructions), AD-596c (skill-registry bridge), AD-596d (external import), AD-596e (validation+linting). crew-capability-architecture.md updated with four-tier model. Issue #166). AD-595 SCOPED (Watch Bill / Billet Registry — Programmatic role-to-agent resolution. Issue #165). AD-589 COMPLETE (Introspective Faithfulness Verification — Layer 3 of Metacognitive Architecture wave (AD-587→588→589), wave COMPLETE. Extends AD-568e faithfulness verification to the self-referential domain. 6 self-referential claim patterns in `_SELF_REFERENTIAL_PATTERNS` detect introspective claims (feelings, memory/cognition ownership, stasis processing, emotional anchors, subconscious). 8 manifest contradiction rules in `_MANIFEST_CONTRADICTIONS` derived from CognitiveArchitectureManifest (AD-587): selective clarity, stasis processing, stasis dreaming, stasis memory evolution, continuous consciousness, emotional subsystem, subconscious, intuition. `extract_self_referential_claims()` splits response into sentences and filters by patterns. `check_introspective_faithfulness()` pure function — no LLM, no I/O — scores claims against manifest contradictions, returns frozen `IntrospectiveFaithfulnessResult`. Score = 1.0 − (contradictions/claims). Graceful degradation: manifest=None skips manifest checks, telemetry=None skips telemetry, both=None assumes good faith. `_check_introspective_faithfulness()` on CognitiveAgent follows AD-568e fire-and-forget pattern. Post-decision pipeline: check → log → SELF_MODEL_DRIFT event → Counselor `record_faithfulness_event()` → episode metadata (score, grounded, contradiction count). Telemetry snapshot caching: DM/WR AD-588 injection paths cache `_snapshot` on AgentWorkingMemory via `set_telemetry_snapshot()` for post-decision cross-check without async call. SELF_MODEL_DRIFT event type added to events.py. 3 source files modified (cognitive_agent.py, source_governance.py, agent_working_memory.py), 1 event file modified (events.py), 1 new test file (38 tests across 6 classes), 0 existing test files changed. Build prompt: ad-589-introspective-faithfulness.md. Issue #153). BF-146 CLOSED (Standing orders hardcode callsigns — agents contradict each other about crew identity. Standing order .md files referenced crew by hardcoded callsigns instead of billet/role titles. When agents self-name during naming ceremony, cross-references become stale. Fix: replaced all hardcoded callsigns with role titles across 15 files following Navy Watch Bill conventions — "reference the billet, never the person." No code changes. Issue #164). AD-590 COMPLETE (Composite Score Floor — Recall Quality Gate. Configurable `composite_score_floor` parameter on `recall_weighted()` filters marginal episodes by minimum composite score threshold. Default 0.0 (disabled/backward-compatible). Per-tier config: basic=0.0, enhanced=0.35, full=0.35, oracle=0.0. DEEP retrieval strategy relaxes floor by −0.10 (clamped to 0.0). Follows anchor_confidence_gate pattern — 3-line list comprehension at step 3c between anchor gate and sort. Wired in cognitive_agent.py + proactive.py. Oracle service unchanged (inherits 0.0 default). 4 source files modified, 1 new test file, 17 new tests. Build prompt: ad-590-composite-score-floor.md). AD-592 COMPLETE (Confabulation Guard Instructions — authority-calibrated anti-fabrication framing in _format_memory_section(). Three tiers: AUTHORITATIVE (light — fabrication guard only), SUPPLEMENTARY/fallback (standard — + orientation priority), PERIPHERAL (strong — + uncertainty mandate). _confabulation_guard() static method on CognitiveAgent. Guard text between instruction and Markers. 1 source file, 2 test files modified, 7 new tests + 1 updated. Build prompt: ad-592-confabulation-guard-instructions.md. Issue #154). AD-591 COMPLETE (Quality-Aware Budget Enforcement — replaces character-count-only budget loop with three stop conditions: (a) character budget (existing), (b) max_recall_episodes hard cap (default k*2), (c) quality degradation stop — stops adding when next episode would drop running mean composite below recall_quality_floor threshold. Config: max_recall_episodes default 0 (uses k*2), recall_quality_floor default 0.40. Per-tier: basic=0.0/0 (disabled), enhanced=0.40/0, full=0.40/0, oracle=0.0/0 (disabled). DEEP relaxes: max×1.5 (if >0), floor−0.10 (clamped ≥0.0). First episode always included. 4 source files modified, 1 new test file, 22 new tests. Build prompt: ad-591-quality-aware-budget.md). AD-593 COMPLETE (Pruning Acceleration + Similarity Floor Tightening — Two-part fix: Part A: two-tier dream Step 12 pruning. Standard tier (>24h, threshold −2.0, 10% fraction, configurable via DreamingConfig). Aggressive tier (>168h, threshold 0.0, 25% fraction). Episode pool pressure: >5000 episodes multiplies fractions by 1.5x (capped 50%). Distinct eviction reason activation_decay_aggressive for audit trail. Part B: agent_recall_threshold raised from 0.15 to 0.25. 3 source files modified, 1 new test file, 24 new tests. Build prompt: ad-593-pruning-acceleration.md). BF-145 CLOSED (Align pre-existing tests with AD-593 changes — 6 tests broken by AD-593's similarity floor raise and two-tier pruning. Test-only fix, no source changes. 2 assertion updates + 1 docstring in test_ad567b_anchor_recall.py for 0.25 threshold default. 1 fixture change in test_ad567d_dream_provenance.py adding aggressive_prune_enabled=False to isolate standard-tier tests. 2 test files modified, 0 new tests. Build prompt: bf-145-ad593-test-alignment.md). BF-144 CLOSED (Stasis duration confabulation — agents fabricate offline duration instead of citing authoritative value in orientation. Meridian posted "2d 22h" when actual stasis was 6 minutes. Root cause: duration presented as narrative prose — LLM treats as background and confabulates. Fix: restructured to STASIS RECORD (AUTHORITATIVE — cite this, do not estimate) with key-value Duration/Shutdown/Resume fields. Added stasis_shutdown_utc + stasis_resume_utc to OrientationContext. Pass timestamps from runtime._previous_session through finalize.py. 2 source files, 1 test file modified, 7 new tests. Build prompt: bf-144-stasis-duration-confabulation.md). BF-143 CLOSED (Temporal episode semantic gap — seeded episodes invisible to recall. Root cause: episode content has no temporal vocabulary, cosine similarity ~0.15 loses to real memories at ~0.20+. Fix: "During first/second watch:" prefix creates semantic bridge to probe questions. Also adds _PROBE_STOP_WORDS for temporal prefix words, punctuation stripping in _distinctive_keywords(). 1 source file, 2 test files modified, 7 new tests. Build prompt: bf-143-temporal-episode-semantic-gap.md). BF-142 CLOSED (Temporal probe scoring — faithfulness/LLM imbalance + keyword false positives. 15/15 agents still fail temporal_reasoning_probe after BF-139. Three causes: (1) check_faithfulness() token-overlap returns near-zero for paraphrased responses. (2) Averaging formula caps scores below threshold — fix: max(score, llm_score) in 3 probes. (3) Second-watch episode content uses common agent vocabulary causing false positives — fix: distinctive domain content. Also records faithfulness_score + llm_score separately in per_question diagnostics. 1 source file, 2 test files modified, 9 new tests. Build prompt: bf-142-temporal-probe-scoring.md. Issue #150). BF-141 CLOSED (Stale session record — Ctrl+C skips session_last.json write. Root cause: Ctrl+C → CancelledError (BaseException) → not caught by `except Exception` → os._exit(0) → shutdown() never runs → session record never written → next boot sees days-old timestamp → inflated stasis duration. Fix: synchronous session record write in both _boot_and_run and _serve finally blocks BEFORE async shutdown. Belt-and-suspenders with BF-135/137 shutdown.py write. 1 file modified, 5 new tests. Build prompt: bf-141-stale-session-record.md. Issue #149). BF-139 CLOSED (Memory probe scoring failures — recall gap + probe hardening. Three compounding root causes: (1) Missing reformulation patterns for common probe question forms ("what happened during...", "what did...", "tell me about...") — 4 new patterns in _REFORMULATION_PATTERNS. (2) Temporal probe keyword collision — c.lower().split()[:4] included stopwords causing false-positive wrong-watch penalties. Fix: _distinctive_keywords() helper with _STOP_WORDS filtering, all distinctive words checked instead of first 4. (3) No LLM fallback scorer on temporal probe. Fix: LLM fallback averaging (matches SeededRecallProbe pattern). Also fixed keyword collision in _TEMPORAL_EPISODES ("3 agents" → "3 workers"). 3 files modified, 30 new tests. Build prompt: bf-139-140-probe-scoring-hardening.md. Issue #147). BF-140 CLOSED (_send_probe exception handling — _send_probe() had no try/except, any handle_intent() exception propagated silently giving score=0.0. Fix: try/except with WARNING-level BF-140-prefixed logging including agent_type. Diagnostic stage logging added to PersonalityProbe and TemperamentProbe. 1 file modified, included in BF-139 test count. Issue #148). BF-138 CLOSED (Sovereign ID Completion — Fixed 10 remaining slot ID leak sites across 7 files. D1: drift_detector _get_crew_agent_ids() now uses resolve_sovereign_id(). D2: All 6 memory probes use _resolve_probe_agent_id() DRY helper for sovereign ID resolution at seed AND recall. D3: HXI agent chat episode creation + chat history recall use sovereign_id. D4: CLI SessionManager session entry, recall, and episode creation use sovereign_id via _sovereign_id attribute. D5: FeedbackEngine accepts identity_registry, _extract_agent_ids() resolves slot IDs to sovereign IDs via resolve_sovereign_id_from_slot(). D6: Recall exception logging elevated from debug to warning with BF-138 prefix. 8 files modified, 15 new tests. Build prompt: bf-138-sovereign-id-completion.md. Issue #141). BF-137 CLOSED (Stale stasis duration after partial boot — shutdown() had _started guard before session record write, partial boots never updated session_last.json. Fix: moved session record write before guard. 1 file modified. Issue #140). AD-584c COMPLETE (Recall Scoring Rebalance — Rebalanced composite scoring weights for QA-trained embeddings + convergence bonus for multi-channel evidence. Weight changes: keyword 0.10→0.20, trust 0.15→0.10, hebbian 0.10→0.05, recency 0.20→0.15, anchor 0.10→0.15, semantic stays 0.35. Convergence bonus: +0.10 when episode found by BOTH semantic AND keyword channels (spreading activation). Config: recall_convergence_bonus field added to MemoryConfig. Full call chain wiring: score_recall() → recall_weighted() → cognitive_agent.py. Clamped to max(0.0, bonus) for defense in depth. 3 files modified, 20 new tests. Build prompt: ad-584c-scoring-rebalance.md). AD-584a/b COMPLETE (Recall Pipeline Q→A Fix — Embedding model swap to multi-qa-MiniLM-L6-cos-v1 + template-based query reformulation + BF-029 prefix removal. Root cause: all-MiniLM-L6-v2 is STS-trained not QA-trained, cosine 0.10–0.35 for Q→A pairs. Tier 1: SentenceTransformerEmbeddingFunction with QA-trained model, 2-tier fallback chain (DefaultEmbeddingFunction → keyword overlap), collection migration on model mismatch. Tier 2: reformulate_query() regex-based question→declarative templates (10 patterns), dual-query in recall_for_agent_scored() merges results across variants taking best distance per episode, BF-029 "Ward Room {callsign}" prefix removed from DM recall queries. Config: embedding_model, query_reformulation_enabled. Migration: episodic, semantic (5 collections), procedure_store. sentence-transformers added to pyproject.toml. 10 files modified, 37 new + 2 updated tests. Build prompt: ad-584-recall-qa-fix.md. Issue #138). AD-584 PLANNED (Recall Pipeline Q→A Fix — Embedding model swap multi-qa-MiniLM-L6-cos-v1 + template-based query reformulation + BF-029 prefix removal. Root cause: all-MiniLM-L6-v2 is STS-trained not QA-trained, cosine 0.10–0.35 for Q→A pairs. 4-tier fix, build prompt ad-584-recall-qa-fix.md covers Tiers 1+2. Research: recall-pipeline-research-synthesis.md. Issue #138). BF-135 CLOSED (Session record write races shutdown timeout — moved session.record_write() before async operations in shutdown sequence. 1 file modified). BF-134 CLOSED (Recall pipeline semantic threshold + FTS scoring + watch section filtering — three interrelated defects: (1) agent_recall_threshold configurable default 0.15 replaces hardcoded 0.3, (2) fts_keyword_floor 0.2 for keyword-only hits, (3) anchor_watch_section promoted to ChromaDB metadata + recall_by_anchor() watch_section param + _try_anchor_recall() wiring + migration backfill. 5 files, 10 new + 3 updated tests). BF-063 CLOSED (Naming ceremony conditional logging — warning for LLM empty/oversized/invalid, info for valid choice. 3 new + 2 updated tests). BF-080 CLOSED (DM Channel Conversation Viewer — selectDmChannel action, dm-detail view state, click-to-navigate from DM Log, back header, reuses WardRoomThreadList/ThreadDetail. Satisfies AD-523a. 3 new tests). AD-583f/583g COMPLETE (Observable State Verification + Convergence Source Tracing — Ward Room echo chamber gap closure. 583g: ThreadEchoAnalyzer traces amplification chains to "Patient Zero" in Ward Room threads via temporal post ordering + Jaccard similarity + anchor independence. 583f: ObservableStateVerifier with pluggable StateProvider protocol (Recreation/Trust/Health) verifies agent claims against system ground truth. Satisfies AD-569d deferral — populates convergence_correctness_rate in BehavioralSnapshot. New events: WARD_ROOM_ECHO_DETECTED, OBSERVABLE_STATE_MISMATCH. Counselor subscriptions + therapeutic DMs. 4 new files, 10 modified, 42 tests. Build prompt: prompts/ad-583fg-observable-state-source-tracing.md. Issue #117). BF-133 CLOSED (Qualification probe anchor gate failure + unrealistic content framing — Two issues: (1) _make_test_episode() created episodes with anchors=None, scoring 0.0 in compute_anchor_confidence(). recall_weighted() anchor_confidence_gate (default 0.3) silently filtered ALL test episodes. Fix: always populate AnchorFrame with realistic defaults (department="qualification", channel="probe", watch_section="first", trigger_type="direct_message") → confidence ≈ 0.44. (2) Episode content lacked production Ward Room framing. BF-029 prepends "Ward Room {callsign}" to recall queries, biasing embeddings toward "[Ward Room]" prefixed content. Seeded episodes had bare facts, reducing semantic similarity. Fix: _ward_room_content() helper wraps all episode content in production "[Ward Room] {channel} — probe: {text}" format. 6 probes updated. 2 files modified, 1 test updated). AD-583 COMPLETE (Wrong Convergence Detection — Echo chamber vs independent collaborative insight. Wires AD-567f anchor independence scoring into AD-554 convergence pathway. 5 sub-ADs: (a) convergence independence scoring in check_cross_agent_convergence() return dict via compute_anchor_independence(), (b) WRONG_CONVERGENCE_DETECTED event + ALERT-severity bridge alert, (c) real-time integration in proactive.py, (d) Counselor subscription + therapeutic DM on extreme cases + groupthink handler upgrade, (e) dream Step 7g wrong convergence flagging + Step 9 EmergenceSnapshot.provenance_independence population. Case study motivation: Chapel tic-tac-toe echo chamber. 8 files modified, 28 new tests). AD-582 COMPLETE (Memory Competency Probes — LongMemEval-Inspired. 6 probes in cognitive/memory_probes.py: SeededRecallProbe (Tier 2, seeds 5 episodes with known facts, scores recall against ground truth via check_faithfulness() + LLM), KnowledgeUpdateProbe (Tier 2, seeds contradictory episodes at different timestamps, tests temporal recency), TemporalReasoningProbe (Tier 2, seeds episodes across watch sections, tests anchor-based temporal filtering), CrossAgentSynthesisProbe (Tier 3, seeds episodes in different agent shards, tests OracleService cross-shard synthesis via global recall()), MemoryAbstentionProbe (Tier 2, seeds topic A episodes then asks about topic B, tests confabulation resistance with dual LLM+keyword scoring), RetrievalAccuracyBenchmark (Tier 1 threshold 0.0, infrastructure precision@k/recall@k measurement). Three shared helpers: _seed_test_episodes() using seed() to bypass rate limiting/dedup, _cleanup_test_episodes() via evict_by_ids() in finally blocks, _make_test_episode() with AnchorFrame construction. Registered in runtime.py after behavioral probes. 3 files modified/created, 24 new tests). AD-580 COMPLETE (Alert Resolution Feedback Loop — Crew-driven alert acknowledgment and suppression. Three modes: Dismiss (time-bounded, default 4h), Resolve (clean-period-gated re-fire, 1h), Mute (indefinite). BridgeAlertService: dismiss_alert(), resolve_alert(), mute_alert(), unmute_alert(), list_suppressed(), _is_suppressed(), _last_detected tracking in _should_emit() independent of emission. BridgeAlertConfig + 2 fields (resolve_clean_period, default_dismiss_duration). Startup wiring in communication.py. 5 API endpoints (POST dismiss/resolve/mute/unmute, GET suppressed). Shell /alert command (commands_alert.py + shell.py registration) with pattern-prefix matching for human-readable dedup keys. Ward Room acknowledgment posting at caller level. 7 files modified, 21 new tests). BF-124 CLOSED. AD-570c COMPLETE. AD-568a/b/c COMPLETE (Adaptive Source Governance — Dynamic episodic vs parametric memory weighting with three capabilities: (1) Task-Type Retrieval Router — RetrievalStrategy enum (NONE/SHALLOW/DEEP), classify_retrieval_strategy() maps intent types to strategies, confabulation safety gate downgrades DEEP→SHALLOW at >0.3 confab rate, Oracle Service wired for ORACLE+DEEP tiers. (2) Adaptive Budget Scaling — BudgetAdjustment dataclass, compute_adaptive_budget() scales context budget from anchor confidence/episode count/score distribution, floor 500/ceiling 12000, wires previously dead cross_department_anchors config. (3) Source Priority Framing — SourceAuthority enum (HIGH/MODERATE/LOW), SourceFraming dataclass, compute_source_framing() produces confidence-calibrated framing (AUTHORITATIVE/SUPPLEMENTARY/PERIPHERAL), _format_memory_section() integration across all 3 cognitive paths. New file: cognitive/source_governance.py. 5 files modified, 30 new tests + 187 regression = 217 passed). AD-462c/d/e COMPLETE (Memory Architecture Extensions — Variable Recall Tiers: RecallTier enum parallels AgencyLevel with trust-gated memory depth (BASIC/ENHANCED/FULL/ORACLE), resolve_recall_tier_params() DRY helper wired into _recall_relevant_memories() and _gather_context(). Oracle Service: OracleService aggregates all 3 knowledge tiers (EpisodicMemory + RecordsStore + KnowledgeStore) with normalized scoring and source provenance. Social Memory: SocialMemoryService "does anyone remember?" protocol via Ward Room thread_mode="memory_query", agents detect and respond from sovereign shard. Ward Room wiring in runtime.py due to startup phase ordering. AD-462f concept graphs remain DEFERRED. 4 new files, 5 modified, 29 new tests). AD-569 COMPLETE (Observation-Grounded Crew Intelligence Metrics — BehavioralMetricsEngine with 5 content-level behavioral metrics: Analytical Frame Diversity, Synthesis Detection, Cross-Department Trigger Rate, Convergence Correctness, Anchor-Grounded Emergence. Dream Step 13 integration. 5 Tier 3 qualification probes. API: /behavioral-metrics, /behavioral-metrics/history. BehavioralMetricsConfig. BEHAVIORAL_METRICS_UPDATED event. 11 files modified, 36 new tests). BF-126 CLOSED (Post-stasis cooperation cluster false positives — crew-identified by Forge + Reyes. Synchronized agent startup after stasis creates correlated Hebbian activity that cooperation cluster detector misinterprets as emergent cooperation. Extended set_cold_start_suppression() to also suppress clusters during 300-second window. 2 files modified, 2 new tests). BF-125 CLOSED (Working memory game engagement desync — crew-identified by Horizon/Forge/Reyes. Four defects: opponent WM orphaned on game completion, reply-path and DM-path MOVE had no WM cleanup, GAME_COMPLETED event had zero subscribers. Fix: centralized GAME_COMPLETED event subscriber in finalize.py cleans both players' WM. Replaced 3 inline cleanup blocks. Existing restore revalidation retained as defense-in-depth. 3 files modified, 1 new test file, 8 new tests) (Working memory not resynced after stasis recovery — crew-identified by Horizon via tic-tac-toe game state drift). BF-123 CLOSED (Ward Room router response path doesn't parse CHALLENGE/MOVE — root cause of recreation system never triggering. When Captain posts in Ward Room, agents respond through `ward_room_router.handle_event()` → intent_bus, which only extracted endorsements and DMs. CHALLENGE/MOVE tags were posted as raw text, bypassing `_extract_and_execute_actions()` in proactive.py entirely. Fix: `_extract_recreation_commands()` method on WardRoomRouter with full CHALLENGE/MOVE extraction, rank gating, Recreation channel thread creation, game lifecycle management, working memory engagement. Also removed DIAG-REC diagnostic logging from proactive.py. 3 files modified, 5 new tests). BF-121 CLOSED (CHALLENGE/MOVE tags inside REPLY blocks posted as text but never parsed as commands — agents wrap output in `[REPLY thread_id]...[/REPLY]`, reply extraction consumes the block before challenge regex runs. Fix: `_extract_commands_from_reply()` method parses CHALLENGE/MOVE from reply body, creates games/makes moves, strips tags before posting. Also applies BF-120 markdown stripping to reply body). BF-122 CLOSED (`callsign` undefined in `_extract_and_execute_actions()` recreation block — relied on variable leaked from notebook `for` loop. If agent produced `[CHALLENGE]` without `[NOTEBOOK]`, NameError crashed silently. Fix: resolve callsign from `callsign_registry` at recreation block entry). 1 file modified, 6 new tests. BF-120 CLOSED (Markdown formatting in LLM output breaks structured tag parsing — `**[CHALLENGE ...]**` or `` `[CHALLENGE ...]` `` not matched by regex. Two-line preprocessing in `_extract_and_execute_actions()` strips `*` and `` ` `` around brackets before all regex patterns. Same fix applied to DM path in `routers/agents.py`. Inline `import re` cleaned up to module-level. 2 files modified, 8 new tests). AD-576 COMPLETE (LLM Unavailability Awareness — EPS Power Brownout Protocol. Three-layer infrastructure awareness: (1) LLM status state machine in proactive loop — operational/degraded/offline states with transition events + Bridge Alerts (ALERT for offline, ADVISORY for degraded, INFO for restored). (2) Infrastructure context injection into `_gather_context()` → rendered as `[INFRASTRUCTURE NOTE]` in `_build_user_message()` before self-monitoring section — tells agents "this is infrastructure, not cognitive." (3) Circuit breaker infra-correlation tagging — `CognitiveEvent.infrastructure_degraded` flag filters brownout events from velocity/similarity computation, preventing false trips. Counselor clinical gating on `_on_self_monitoring_concern()` suppresses infrastructure-correlated amber concerns. Wires previously dead `LLM_HEALTH_CHANGED` event via `LlmHealthChangedEvent` dataclass. Also fixes BF-116 (dead `circuit_breaker_redirect` removal + AD-567g `orientation_supplement` rendering completion) and BF-117 (convergence/divergence bridge alert delivery — `rt.bridge_alerts` + `rt.ward_room_router`). 7 files modified, 1 new test file, 29 new tests). BF-118 CLOSED (OrientationService startup crash — `runtime` not in scope in `cognitive_services.py`. One-line fix: pass `ontology` parameter directly). BF-119 CLOSED (DM game challenge parsing — `[CHALLENGE @callsign game_type]` only parsed in proactive loop, silently ignored in DM responses. Added CHALLENGE parsing to HXI Profile DM path in `routers/agents.py` alongside existing AD-572 MOVE parsing. Recreation channel thread creation, game creation, working memory engagement registration. Standing orders updated with recreation syntax). AD-575 COMPLETE (Unified Self-Awareness — Cross-Context Identity Recognition. `_detect_self_in_content()` method on CognitiveAgent scans Ward Room thread content for the agent's own callsign using word-boundary regex. Self-recognition cue injected in WR branch of `_build_user_message()` — agent told "references to '{callsign}' refer to YOU, respond as a participant not an observer." Cross-context engagement binding: when self-mentioned AND has active game engagement via AgentWorkingMemory, injects participatory awareness ("Spectators are watching your game"). Generalizes BF-102 commissioning awareness pattern. 1 file modified, 1 new test file, 10 new tests). AD-513 Phase 1 COMPLETE (Ship's Crew Manifest — Queryable Crew Roster. Three-layer delivery: (1) `get_crew_manifest()` on VesselOntologyService — query-time assembly from ontology/trust/callsign subsystems with dependency-injected enrichment. (2) Anti-confabulation cognitive grounding via `_build_crew_complement()` in `_build_temporal_context()` — department-grouped "SHIP'S COMPLEMENT" block injected into ALL prompt pathways (DM, WR, proactive). OrientationContext `crew_names` for cold/warm boot. (3) HXI CrewRosterPanel — draggable floating panel with dept filter chips, rank badges, trust bars, click-to-profile. REST endpoint `/api/ontology/crew-manifest`. LoD fix: `get_all_assignments()` public method. 10 files modified, 14 new tests). AD-574 COMPLETE (DM Reply Agent Notification — Captain posts in DM channels now notify the target agent. Two independent gaps fixed: (1) find_targets() in WardRoomRouter added "dm" channel type case matching agent.id[:8] against channel name, no Earned Agency gating. (2) get_unread_dms() query rewritten from "agent has zero posts" to "last author is not the agent" via LEFT JOIN subquery with COALESCE — catches Captain replies in agent-initiated threads. 2 files modified, 9 new tests). BF-114b CLOSED (Remove Dead `proactive_extends_idle` Code Path — dead code cleanup following BF-114. Removed: `proactive_extends_idle` from DreamingConfig + system.yaml, `record_proactive_activity()` + `is_proactively_busy` + `_last_proactive_time` from DreamScheduler, idle calculation simplification, proactive.py caller, startup wiring, DreamAdapter busy guard. 9-test TestDreamSchedulerProactiveAwareness class removed, 1 focused emergent detector test added. Net -8 tests. 7 files modified). AD-573 COMPLETE (Unified Agent Working Memory — Cognitive Continuity Layer. Per-agent AgentWorkingMemory maintaining active situation model across all cognitive pathways. Ring-buffered entries (actions, observations, conversations, events) with token-budget-aware render_context() and 6-priority eviction. ActiveEngagement tracking for games/tasks/collaborations. All pathways write: handle_intent() action recording, proactive observation recording, DM conversation recording, [CHALLENGE]/[MOVE] game engagement management, cognitive state sync, active game sync. All pathways read: render_context() injected into DM/WR/proactive _build_user_message() branches. SQLite-backed WorkingMemoryStore (ConnectionFactory, start()/stop(), BEGIN IMMEDIATE + asyncio.Lock). Freeze on shutdown, restore on stasis recovery with stale pruning + game revalidation. System prompt _has_active_game() replaced with working_memory.has_engagement("game"). WorkingMemoryConfig in SystemConfig. 2 new files, 7 modified, 2 new test files, 35 new tests). AD-572 COMPLETE (Captain Engagement Priority — Active State Awareness in DM Path. Active game state injection into DM _build_user_message() via _build_active_game_context(). DM system prompt augmentation with [MOVE] instruction when game active. [MOVE pos] parsing from DM responses in agents router with move execution against RecreationService. get_game_by_player() DRY method on RecreationService replaces 3 instances of iterate-and-match in proactive.py. Ward Room board update posting from DM path. gameMoveExecuted flag in REST response. 4 files modified, 3 new test files, 18 new tests). AD-526b COMPLETE (HXI Tic-Tac-Toe — Captain vs Crew Game Panel. REST router /api/recreation/ (challenge, move, active, forfeit) with Depends() DI. Floating GamePanel.tsx (CSS Grid board, piece-pop animations, win-line glow, pulse-dim waiting). GAME_UPDATE event emission from RecreationService. Game rehydration on refresh. Challenge button in ProfileInfoTab. forfeit_game() service method. Zustand store game state slice + WebSocket handler. 2 new files, 7 modified, 16 new tests). BF-110 CLOSED (Game Board Invisible to Agents — get_recent_activity() only returns top-level threads, not replies where board updates are posted. Fixed: inject active game state (board, valid moves, turn indicator) directly from RecreationService into proactive context. 2 files modified, 0 new tests). AD-526a COMPLETE (Social Channels + Tic-Tac-Toe — agent recreation framework. GameEngine protocol + TicTacToeEngine. RecreationService with game lifecycle, Ship's Records integration, GAME_COMPLETED event emission. Recreation + Creative default Ward Room channels with auto-subscription. Proactive integration: Recreation channel context gathering, [CHALLENGE @callsign game_type] and [MOVE position] action extraction with rank gating (Lieutenant+). cognitive_agent.py challenge/move instructions. RecreationService wired in finalize.py with ward_room + records_store + emit_event. 10 files modified, 47 tests). AD-570 COMPLETE (Anchor-Indexed Episodic Recall — Structured AnchorFrame Queries. Promotes 4 anchor fields (department, channel, trigger_type, trigger_agent) to top-level ChromaDB metadata for native where-clause filtering. One-time migration backfills existing episodes. recall_by_anchor() API with two modes: enumeration (structured filters, no embedding) and semantic re-ranking (structured + vector similarity). Post-retrieval agent_id filtering, activation tracking, hash verification. 2 files modified, 23 tests). AD-570b COMPLETE (Participant Index Sidecar — SQLite junction table indexing episode participants (sovereign IDs + callsigns) for O(1) lookups. 20 tests). BF-109 CLOSED (Qualification Probe Param Key Mismatch. _send_probe() sent params={"message":...} but perceive() reads params.get("text"). All Tier 1/2 probe results were unreliable — agents never received the question. One-line fix: "message" → "text". First real qualification run: 130/131 pass (99.2%), 15 agents, 130 baselines established. Only failure: Security Officer mti_temperament at 0.000). BF-108 CLOSED (LLM Unreachable — No Runtime Visibility. MockLLMClient.get_health_status() now returns overall:"mock" with all tiers offline. runtime.llm_is_mock property. Chat endpoint returns explicit offline message instead of triggering self-mod. /system/services correctly reports LLM as offline. 4 files, 0 new tests — covered by existing 82 tests). AD-567g COMPLETE (Cognitive Re-Localization — final AD in Memory Anchoring lineage 567a→g. Structured orientation at boot: cold start full orientation (identity + cognitive grounding + first duty), warm boot stasis recovery, diminishing proactive supplement. Anchor field gap fixes: watch_section, event_log_window, Ward Room department. OrientationService, OrientationContext, derive_watch_section(). Subsumes BF-034 cold-start note. 28 tests). AD-567f COMPLETE (Social Verification Protocol — absorbs AD-462d. Cross-agent claim verification, corroboration scoring, cascade confabulation detection. Privacy-preserving: agents see metadata not content. Anchor independence discriminates corroboration from cascade. Ward Room integration after AD-506b peer similarity. Bridge Alerts on medium/high risk. Counselor therapeutic DMs on high risk. 28 tests). AD-567d COMPLETE (Anchor-Preserving Dream Consolidation + Active Forgetting — provenance composition through dream pipeline (absorbs AD-559), ACT-R activation-based memory lifecycle (absorbs AD-462b), ActivationTracker with SQLite access log, dream Step 12 activation pruning, micro-dream replay reinforcement, recall access recording, 31 tests). AD-567c COMPLETE (Anchor Quality & Integrity — Johnson-weighted confidence scoring, RPMS confidence gating, per-agent AnchorProfile for Counselor diagnostics, SIF check_anchor_integrity(), drift classification (specialization/concerning/unclassified), absorbs AD-567e). AD-567b COMPLETE (Anchor-Aware Recall + Salience-Weighted Retrieval — RecallScore composite scoring, FTS5 keyword search sidecar, anchor context headers in memory formatting, SECONDHAND source wiring, recall_weighted() API with budget enforcement. Absorbs AD-462a). AD-567a COMPLETE (Episode Anchor Metadata — AnchorFrame dataclass with 10 fields across 5 dimensions, all 15 episode creation sites wired, serialization round-trip through ChromaDB, content hash exclusion, backwards compatible). BF-104 CLOSED (Display Crew Agent Count — registry.crew_count(), shell prompt shows crew not total, status panel/API/working memory updated). AD-566f COMPLETE (/qualify Shell Command — manual trigger and inspection, 5 subcommands: status/run/agent/baselines). AD-566e COMPLETE (Tier 3 Collective Tests — 5 crew-wide probes: CoordinationBreakevenProbe, ScaffoldDecompositionProbe, CollectiveIntelligenceProbe, ConvergenceRateProbe, EmergenceCapacityProbe + QualificationHarness.run_collective() + DriftScheduler collective integration). AD-566d COMPLETE (Tier 2 Domain Tests — 5 department-gated probes: TheoryOfMindProbe, CompartmentalizationProbe, DiagnosticReasoningProbe, AnalyticalSynthesisProbe, CodeQualityProbe + DriftScheduler tier generalization). AD-566c COMPLETE (Drift Detection Pipeline — DriftDetector z-score engine, DriftScheduler periodic runner, Counselor/VitalsMonitor/BridgeAlerts integration). AD-566b COMPLETE (Tier 1 Baseline Tests — 4 psychometric probes: PersonalityProbe, EpisodicRecallProbe, ConfaculationProbe, TemperamentProbe). AD-566a COMPLETE (Qualification Test Harness Infrastructure — psychometric protocol, store, harness engine). AD-541f COMPLETE (Eviction Audit Trail — Append-Only Accountability). AD-541 Lineage CLOSED (6/6 pillars complete). AD-541e COMPLETE (Content Hashing — Cryptographic Episode Integrity). BF-103 CLOSED (Episodic Memory Agent ID Mismatch — sovereign ID normalization + migration). AD-541d COMPLETE (Guided Reminiscence — Therapeutic Memory Sessions). AD-541c COMPLETE (Spaced Retrieval Therapy — Active Recall Practice). AD-541b COMPLETE (Reconsolidation Protection — Read-Only Memory Framing). AD-555 COMPLETE (Notebook Quality Metrics & Dashboarding). AD-554 COMPLETE (Real-Time Cross-Agent Convergence & Divergence Detection). AD-553 COMPLETE (Quantitative Baseline Auto-Capture). AD-552 COMPLETE (Notebook Self-Repetition Detection). AD-551 COMPLETE (Notebook Consolidation — Dream Step 7g). AD-550 COMPLETE. BF-069 CLOSED. BF-101/102 COMPLETE. AD-560 COMPLETE. AD-557 COMPLETE. AD-558 COMPLETE. BF-099 CLOSED. Cognitive JIT pipeline COMPLETE (9/9). 618 Cognitive JIT tests. 7,253 tests (7,067 pytest + 186 vitest).

---

## Recent Builder Closures

AD-594c v1 Parallel Execution Dispatch CLOSED (Wave 80, GH #162). New module `src/probos/consultation/dispatch.py` ships `WorkItemSpec` frozen dataclass + `PlanDecomposer` Protocol + `MarkdownPlanDecomposer` v1 (ATX-2 + bullet-key/value parser) + `ConflictDetector` (resource-overlap pairs, stateless) + `ParallelDispatcher` orchestrator (`dispatch` reads latest `plan/plan_v{N}.md` via `RecordsStore`, decomposes, conflict-resolves by injecting synthetic `depends_on` edges in original spec order, registers WorkItems on `runtime.work_item_store` with `tags=["consultation", "workspace:<id>"]`, mirrors each spec via `workspace.add_work_item`, transitions APPROVED → EXECUTING, emits `PARALLEL_DISPATCH_STARTED`; `get_progress` aggregates by status + emits `PARALLEL_DISPATCH_PROGRESS`; `check_completion` is idempotent EXECUTING → COMPLETED with journal entry; `detect_blockers` emits `PARALLEL_DISPATCH_BLOCKED` per spec via dedup ring after threshold; `revoke` cancels non-terminal items for teardown). 3 new EventTypes, `ConsultationDispatchConfig` Pydantic model + `_wire_consultation_dispatch` finalize wirer gated on `runtime.consultation_workspaces` + `runtime.work_item_store` + `runtime.records_store`. LLM-driven semantic decomposition deferred behind the `PlanDecomposer` Protocol seam (mirrors AD-594a `InputProcessor` and AD-594d `FormatTransformer`). 2 new files (dispatch.py, test_ad594c_parallel_dispatch.py — 37 tests, all green), 4 modified (events.py, config.py, consultation/__init__.py, startup/finalize.py). Focused gate 37/37 passed serially; sibling regression (`test_ad594a_consultation_workspace.py` + `test_ad594d_delivery_pipeline.py` + `test_workforce.py`) 160/160 green; full xdist gate 11548 passed + 16 skipped + 1 environmental flake (`test_ward_room.py::TestEndorsementActivation::test_browse_threads_sort_recent`, passes serially). Collected 11565 (+37 vs 11528 baseline, exceeds +25 floor). Build prompt: prompts/ad-594c-parallel-dispatch-v1.md.

AD-604 Spreading Activation CLOSED. Added SpreadingActivationConfig, SpreadingActivationEngine, and CognitiveAgent causal-query integration for bounded two-hop recall using anchor metadata, hop decay, and score-preserving deduplication. Fallback gate: 279 targeted tests passed (13 focused AD-604 + 84 CognitiveAgent/config adjacent + 182 recall/episodic). Full-suite gate deferred to AD-673 by Captain decision because it is blocked by the unrelated environmental proactive-timeout in `tests/test_proactive.py::TestPerAgentCooldown::test_per_agent_cooldown_used_in_cycle`.

AD-606 Think-in-Memory CLOSED. Added ThoughtStoreConfig, ThoughtStore, and CognitiveAgent conclusion persistence that stores important working-memory conclusions as REFLECTION episodes anchored to channel="thought" with typed trigger metadata, importance thresholding, per-cycle caps, and evidence provenance. Fallback gate: 277 targeted tests passed (11 focused AD-606 + CognitiveAgent/config adjacent + recall/episodic band). Full-suite gate remains deferred to AD-673 because the unrelated environmental proactive-timeout persists.

AD-608 Retroactive Memory Evolution CLOSED. Added RetroactiveConfig, RetroactiveEvolver, EpisodicMemory metadata update/retrieval APIs, store-time evolver hook, and startup wiring. Focused + adjacent fallback gate: 68 targeted tests passed (12 focused AD-608 + EpisodicMemory/ChromaDB + importance metadata + config). Full-suite gate deferred to AD-673: retry reached 3338 passed before stopping on unrelated AD-666 SensoriumConfig expectation mismatch (`token_budget_warning` 10000 vs expected 6000), before the known proactive-timeout node.

AD-609 Multi-Faceted Distillation CLOSED. Added DistillationConfig, FailureDistiller, DreamReport failure/comparative counters, DreamingEngine Step 7c-2 integration, and dreaming startup wiring. Focused + adjacent fallback gate: 82 targeted tests passed (10 focused AD-609 + DreamingEngine + config + procedure extraction). Full-suite gate deferred to AD-673: retry reached 3348 passed before stopping on the unrelated AD-666 SensoriumConfig expectation mismatch (`token_budget_warning` 10000 vs expected 6000), before the known proactive-timeout node.

AD-573 Memory Budget Accounting CLOSED. Per-cycle MemoryBudgetManager tracks L0/L1/L2/L3 token budgets (150/3000/1000/500), supports allocate/release/remaining/total_remaining/reset with disabled pass-through and unknown-tier warnings, and compress_episodes() truncates RecallScore lists by composite_score within budget. CognitiveAgent accepts optional memory_budget_config via kwargs and creates a local per-cycle manager in decide() only; recall path wiring, _build_user_message(), recall_weighted(), and render_context() unchanged. Tests: 15 focused passed; 73 adjacent cognitive-agent regressions passed; 137 AttributeError regression band passed after constructor-bypass compatibility fix; full xdist gate after fix: 9683 passed, 14 skipped, 140 worker-crash failures classified by serial triage (30 exact nodeids passed, proactive long-delay nodes 2 passed with --timeout=180).

AD-579a Pinned Knowledge Buffer CLOSED. AgentWorkingMemory now supports an optional ephemeral PinnedKnowledgeBuffer with PinnedFact entries, TTL expiry, duplicate refresh, priority/LRU eviction, budgeted rendering at priority 0, disabled no-op behavior, and public pin_knowledge/unpin_knowledge/pinned_knowledge APIs. Added PinnedKnowledgeConfig and KNOWLEDGE_PINNED/KNOWLEDGE_UNPINNED event enum values; no persistence, auto-pinning, cross-agent sharing, API endpoints, or event emission wiring. Tests: 12 focused passed; 43 adjacent working-memory regressions passed; full xdist gate: 9737 passed, 14 skipped, 132 worker-crash failures classified by exact-node serial triage (68 passed).

AD-579b Temporal Validity Windows CLOSED. Episode now has valid_from/valid_until defaults; AnchorFrame has temporal_validity_start/temporal_validity_end defaults; EpisodicMemory metadata round-trips validity timestamps; recall_weighted(valid_at=...) filters expired and not-yet-valid episodes; recall_valid_at() wraps the filtered path. Added TemporalValidityConfig. No automatic inference, UI, retroactive propagation, anchor-recall changes, or dream consolidation changes. Tests: 10 focused passed; 30 adjacent anchor/weighted recall regressions passed; full xdist gate: 9679 passed, 14 skipped, 138 worker-crash failures classified by exact-node serial triage (74 passed).

AD-586 Task-Contextual Standing Orders CLOSED. Added TaskContext, TaskContextConfig, default config/task_orders markdown files (build/analyze/communicate/diagnose plus zero-byte general), Tier 5.5 compose_instructions injection, CognitiveAgent task classification wiring, and startup wiring via _wire_task_context(). Intent mapping is hardcoded for build/analyze/communicate/diagnose/review/general; no LLM classification, per-agent customization, dream evolution, _load_file cache changes, or proactive path-specific rewiring. Tests: 12 focused passed; 90 adjacent standing-orders/cognitive-agent regressions passed; full xdist gate: 9742 passed, 14 skipped, 142 worker-crash failures classified by exact-node serial triage (83 passed).

AD-602 Question-Adaptive Retrieval — CLOSED. Added deterministic QuestionClassifier and RetrievalStrategySelector mapping TEMPORAL/CAUSAL/SOCIAL/FACTUAL queries to tuned recall parameters, QuestionAdaptiveConfig with per-type overrides, and minimal CognitiveAgent recall integration applying strategy k/weight overrides without switching recall methods. No LLM classification, DEEP/ORACLE escalation, fusion, API, or HXI work. Tests: 12 focused passed; 56 adjacent cognitive-agent regressions passed; full xdist gate: 9776 passed, 14 skipped, 130 worker-crash failures classified by exact-node serial triage (92 unique passed).

AD-610 Utility-Based Storage Gating — CLOSED. Added StorageGate and StorageDecision for write-time episode validation, StorageGateConfig, EPISODE_REJECTED event type, EpisodicMemory.set_storage_gate(), pre-BF-039 gate evaluation in store(), and startup wiring in cognitive_services. Gate uses in-memory recent-window Jaccard duplicate detection, deterministic utility scoring, lightweight contradiction logging, and high-importance utility bypass; no LLM dedup, retroactive dedup, cross-agent dedup, BF-039 changes, API, or HXI work. Tests: 14 focused passed; 34 episodic regressions passed; 3 constructor-bypass regressions passed after getattr compatibility fix; full xdist gate after fix: 9793 passed, 14 skipped, 129 reported failures classified by serial triage (1 xdist-only assertion passed serial, 89 unique worker-crash nodes passed serial).

---

## Development Eras

| Era | Phases | Codename | Status |
|-----|--------|----------|--------|
| [**Era I: Genesis**](progress-era-1-genesis.md) | 1-9 | Building the Ship | Complete |
| [**Era II: Emergence**](progress-era-2-emergence.md) | 10-21 | The Ship Comes Alive | Complete |
| [**Era III: Product**](progress-era-3-product.md) | 22-29 | The Ship Sets Sail | Complete |
| [**Era IV: Evolution**](DECISIONS.md) | 30 | The Ship Evolves | Active |
| Era V: Civilization | 31-36 | The Ship Becomes a Society | Planned |

## Release Tagging Policy

Tags use the format `v{major}.{minor}.{patch}-phase{N}` (e.g., `v0.4.0-phase29c`).

| Event | Tag? | Example |
|-------|------|---------|
| Phase completed | Yes — bump minor version | `v0.5.0-phase30` |
| Critical bug fix between phases | Yes — bump patch version | `v0.4.1-phase29c` |
| Roadmap/docs-only changes | No | — |
| Mid-phase work (partial features) | No | — |
| First production-ready release | Yes — `v1.0.0` | After security hardening (Phase 31) |

**Current tags:**

| Tag | Commit | Date | Notes |
|-----|--------|------|-------|
| `v0.1.0-phase12` | — | — | End of Era I: Genesis |
| `v0.4.0-phase29c` | `13d52a7` | 2026-03-17 | End of Phase 29c: Codebase Knowledge |

## Design Principles

See [design-principles.md](docs/development/design-principles.md) for full design principles:
- "Brains are Brains" (Nooplex core principle)
- Agent Development Model (Communication + Simulation)
- HXI Self-Sufficiency, Agent-First Design, Cockpit View
- Probabilistic Agents, Consensus Governance
- Agent Classification Framework (Core / Utility / Domain)
- Foundational Governance Axioms (Safety Budget, Reversibility, Minimal Authority)
- "Cooperate, Don't Compete" (Federation philosophy)
- Visiting Officer Subordination
- Extension-First Architecture
- Markdown is Code

---

## Environment

- **Platform:** Windows 11 Pro (10.0.26200)
- **Python:** 3.12.13 (installed via uv)
- **Toolchain:** uv 0.10.9
- **Key deps:** pydantic 2.12.5, pyyaml 6.0.3, aiosqlite 0.22.1, httpx 0.28+, rich 13.0+, chromadb 1.5.4, pytest 9.0.2, pytest-asyncio 1.3.0, pytest-xdist 3.8.0, pytest-timeout 2.4.0
- **LLM endpoints:** Fast tier: Ollama at `http://127.0.0.1:11434/v1`, Standard/Deep tier: VS Code Copilot proxy at `http://127.0.0.1:8080/v1`
- **LLM models:** fast=qwen3.5:35b (local Ollama), standard=claude-sonnet-4.6 (Copilot proxy), deep=claude-opus-4.6 (Copilot proxy)
- **Run tests:** `uv run pytest tests/ -v` (sequential) or `uv run pytest -n auto` (parallel, 13x faster)
- **Run fast tests:** `uv run pytest -n auto -m "not slow"` (skip 65 sleep-heavy tests)
- **Run demo:** `uv run python demo.py`
- **Run interactive:** `uv run python -m probos`


- AD-705 (reframed) - wake-word voice loop v1 shipped (Wave 137, Vitest +23 = 516 total).

- AD-718c - per-agent wake phrase v1 shipped (Wave 137, Vitest +8 = 524, Python +8 = 13053).

- AD-720a-0 - python-multipart>=0.0.9 added to [project.dependencies] (Wave 139 preceding commit; Apache-2.0 license; required for FastAPI UploadFile).

- AD-720a - file upload via multipart + drag-drop UI shipped (Wave 139, Python +10 = 13063, Vitest +5). 9-MIME allow-list (PDF/txt/md/json/csv added). Helper extraction (`_validate_and_store_attachment`) is the single defense-in-depth chain for both POST endpoints. JSON+base64 paste path bit-for-bit unchanged.

- AD-720d - vision pipe-through v1 shipped (Wave 139, Python +10 = 13072, Vitest +0). Wires previously-dead `ChatRequest.attachment_ids` into the chat path. Image attachments route via `LLMClient` vision tier (multimodal `messages` array, additive `LLMRequest.messages` field, zero new vendor SDK deps). Non-image attachments (txt/md/json/csv) inline-extract and augment the prompt for the standard decomposer path. PDF / vision-tier-unhealthy paths emit structured stubs — never silent drop.


- AD-730 - vision pipe-through for per-agent DMs shipped (Wave 151, Python +N), then partially compromised by BF-265 transport-strip. Resolved by AD-731 (Wave 152) — see below.

- BF-265 - IntentBus strip of `vision_messages` from NATS transport shipped Wave 151 as emergency fix for #636 OOM crash (1 MB allocation failure from inline-base64 retry-buffer accumulation). **REVERTED in Wave 152 / AD-731** — content-addressable refs make the strip unnecessary; the uniform-NATS-transport invariant is restored.

- BF-266 - fold full `user_message` into vision DM multimodal content shipped (Wave 151). Behavior unchanged by AD-731; the enrichment is shape-agnostic (matches on `type == 'image'`).

- BF-267 - IntentBus.send() local-first dispatch shipped Wave 151, **REVERTED 2026-05-11** (commit 8b4b39f) because the local handler is async and returns immediately (cognitive queue enqueue), so `await handler(intent)` returned `None` — broke ALL DMs including text. Architecturally the local-first reflex was the wrong-direction fix; the right fix is the wire format (AD-731).

- AD-731 (Wave 152) - content-addressable vision payloads shipped (Python +13 = 13342). Sender (`build_multimodal_messages`) emits `{type:image, source:{type:attachment_ref, sha256, media_type}}` instead of inline base64. Receiver (`OpenAICompatibleClient._resolve_attachment_refs_for_openai`) dereferences refs from the runtime's content-addressable `AttachmentStore` to base64 immediately before the HTTP POST. Bus message stays ~70 bytes/image (was 150 KB-1 MB). BF-265 reverted as a consequence — AD-637z2 (#639) auto-closes. Federation strip preserved as an explicit AD-731a forward marker (cross-mesh attachment distribution unsolved; AD-731a-1 HTTP fetch and AD-731a-2 NATS Object Store will retire it). Industry pattern (refs not bytes on RPC) matches Ray/Dask object refs, Erlang BEAM refs, MCP resource handles, Anthropic API source types, Git, IPFS, OCI registries.

- BF-264 closeout (Wave 154) - the 2026-05-11 OOM crash (1 MB allocation failure after dream cycle) is retroactively resolved by AD-731's wire-format change — vision payloads no longer carry image bytes through `IntentMessage.params`; only ~70-byte refs cross the bus. The original BF-264 hypothesis list (divergence ring buffers, WS push snapshots, NATS retry buffers) remains useful as **ambient health watch-points** for future memory-pressure investigations, not as open work. Closes [#636](https://github.com/seangalliher/ProbOS/issues/636).


- AD-734 (Wave 153) - vision-pipeline wire-shape contract test shipped (Python +1 test file, 3 tests, ~230 lines). Codifies the live-capture work done with `tmp_capture_proxy.py` during BF-278 as a CI-runnable pytest at `tests/test_ad734_wire_shape_contract.py`. Pins three invariants: (1) sender bus shape `{type:image, source:{type:attachment_ref,...}}` per AD-731, (2) resolver rewrites to OpenAI `image_url` + data URL per BF-268, (3) end-to-end `_call_openai` HTTP POST body via `httpx.MockTransport` carries `image_url` (not Anthropic source-shape). Pre-commit hook (`.git/hooks/pre-commit`, +~20 lines) runs the contract test whenever any vision-pipeline file is staged — would have caught BF-274 and BF-278 at commit time. Lesson locked in: contract tests on the wire boundary, not just on component APIs, are the durable guard against silent multi-subsystem regressions.

- AD-738 (Wave 157) — Server-streamed TTS via Piper shipped (Python +26 tests, Vitest +7). Closes forward marker AD-721b-2.3. MIT-licensed operator-provided binary (`tools/piper/`) + MIT default voice model (`en_US-amy-medium`). New `src/probos/audio/tts/` module with `TTSBackend` Protocol + `PiperBackend` subprocess wrapper. New `TTSConfig` Pydantic model with `backend = "browser"` default (zero behaviour change for operators who don't install Piper). New `GET /api/avatars/tts/status` probe + `POST /api/avatars/tts` synthesis endpoint; AD-731 invariant (audio bytes via `AttachmentStore.write(sha256, blob, mime)`, no inline base64 in RPC body). Browser caches one-time probe in `voice.ts` module state — load-bearing zero-HTTP-per-utterance guarantee on default config. Honest-degrade chain: any failure (binary missing, model missing, subprocess error, timeout, malformed output) returns `{"backend": "disabled"}` and browser falls back to `SpeechSynthesisUtterance`. `useLipSyncCapture` extended with `injectLipSyncFrames` setter — server path injects visemes alongside audio for lockstep playback. AD-735 volume + AD-737 emotion modulation apply to BOTH paths. Forward markers AD-738a/b/c/d filed in roadmap.

----

AD-728d v1 CLOSED (Wave 165). Self-image-awareness skill (LLM-discoverable self-check capability). Closes GH issue #661. Wraps AD-728c's `CognitiveAgent.check_own_render(reason)` coroutine in the canonical AD-626 augmentation-skill pattern so agents can invoke render self-checks via their LLM reasoning surface. New `config/skills/self-image-awareness/SKILL.md` (activation=augmentation, intents=direct_message,ward_room_notification,proactive_think, trigger tag self_check) teaches the `[SELF_CHECK reason]` bracket marker. New `_SELF_CHECK_RE` (strict `[a-z_-]{1,64}` reason — dispatch authority) + `_SELF_CHECK_STRIP_RE` (lax leak-prevention) + `extract_self_check` / `strip_self_check` helpers on `DmSanityGate`, positioned adjacent to the CHALLENGE/MOVE regex pairs. New `DmReplyPipeline.step_4_self_check_parse` inserted between move-parse and episodic-store; trailing five step defs renumbered 5..9 atomically (step tuple + 5 method defs + class docstring + module docstring in one commit). Fire-and-forget `asyncio.Task` reference held on `DmReplyContext._self_check_task` per fire-and-forget GC rule (not on agent — tests verify storage location). First marker dispatches `agent.check_own_render(reason=...)`; additional markers in the same reply are stripped silently with a single WARNING. Invalid reasons (uppercase, punctuation) silently strip with no dispatch. Disabled-gate path still dispatches — honest-degrade lives downstream in `verify_render_coherence` (AD-728c surface). 7 focused tests pass at `tests/test_ad728d_self_image_awareness_skill.py`: SKILL.md frontmatter loads with correct intents AND negative-intent assertion (skill-catalog-leak hard-stop); `find_augmentation_skills` surfaces the skill for the three configured intents AND does NOT surface for unrelated intents (system_heartbeat, run_command, http_fetch); marker stripped from reply text; valid reason dispatches with task ref held on ctx; invalid reason silently strips with no dispatch; multi-marker first-dispatches + WARNING logged; disabled-gate still dispatches. Real `DmSanityGate` fixture + hand-rolled `_RecordingAgent` dataclass (NOT `unittest.mock`) per BF-287. AD-726 `test_ad726_dm_reply_pipeline.py` renumbered alongside (step_4_episodic_store → step_5, etc.; `len(reached) == 7` → `8`). Full gate: 13769 passed, 20 skipped (delta +7 vs Wave 164 baseline 13762; 1 flake on `test_knowledge_store::test_auto_commit_after_debounce` passes in isolation). AD-731 invariant preserved — text-only marker; grep `base64` in `reply_pipeline.py` empty. AD-728c boundary preserved — `check_own_render` body unchanged, rate-limit logic unchanged, observation injection unchanged. No new pip/npm deps. No hard-stops triggered.
