Three independent hygiene fixes named in the 2026-05-19 design review.
All small, all observable, none architectural.
1. ``RuntimeConfig`` flag drop on pack_id / frame_pack override
chat/runtime.py:306-320 used to enumerate fields by hand when
reconstructing RuntimeConfig under the pack_id / frame_pack
override path. The list stopped at ``admissibility_margin`` and
silently dropped FIVE newer flags: identity_pack, ethics_pack,
forward_graph_constraint, composed_surface, thread_anaphora.
Caller side-effect:
ChatRuntime(pack_id="x", config=RuntimeConfig(composed_surface=True))
.config.composed_surface == False # silently lost
Fix: ``dataclasses.replace(config, input_packs=..., frame_pack=...)``.
Every field on the dataclass survives by construction; future
additions never need a synchronized edit on this path.
2. Stale CAUSE / VERIFICATION docstring
tests/test_intent_classification_extensions.py described a sixth
runtime-side fix (pack_grounded_surface fallback for
CAUSE/VERIFICATION) that was considered, reverted, and the file's
own test classes pin the opposite contract. Docstring now states
the doctrine correctly: no fallback, deliberately, so the discovery
layer can log the teaching-gap signal.
3. Thin convenience wrappers: respond / achat / arespond
tests/test_achat.py and tests/test_language_pack_runtime.py
referenced these public methods since 2026-05-14, but they were
never implemented on ChatRuntime — those 12 tests had been red on
every full-lane run since the rebase. Added as thin wrappers:
respond(text) -> ChatResponse.surface
achat(text) -> async wrapper around chat()
arespond(text)-> async wrapper around respond()
The async wrappers are deliberately NOT genuinely non-blocking —
the underlying CPU-bound walk/recall/composition remains sync.
Docstrings say so explicitly. Callers needing real concurrency
should wrap in asyncio.to_thread at the call site; promoting the
wrappers to true async event-loop integration is a future change
gated by an actual concurrent caller.
Regression coverage:
tests/test_runtime_config_passthrough.py — 4 tests
- all 19 RuntimeConfig fields survive a pack_id override
- all five newer flags survive a frame_pack override
- no-override path preserves caller config by identity (no rebuild)
- the four public methods exist and are callable
Verification:
44/44 affected tests green (was 12 red pre-fix).
Cognition eval byte-identical on both splits.
No surface-format change; this commit is pure plumbing.