Stop-and-document outcome for issue #680's final runtime wiring. The serving turn has no lawful in-turn ContemplationResult or provider: the sole QUESTION_NEEDED producer (generate.contemplation.contemplate) is an off-serving growth organ that runtime is forbidden to call, and no plumbing carries its artifact into the turn. Wiring is deferred to a lawful acquisition slice; chat/runtime.py is left untouched.
8.2 KiB
ASK Runtime Wiring — The Missing Boundary (Stop-and-Document)
Date: 2026-06-10
Branch: feat/ask-runtime-wire
Issue: #680 (final chat/runtime.py wiring)
Status: STOPPED before wiring. No chat/runtime.py edit was made.
1. Why this is a stop-and-document, not a wiring PR
The task to wire chat.ask_runtime.maybe_apply_served_ask into chat/runtime.py
carried an explicit pre-condition:
If no lawful
ContemplationResultor provider exists at the boundary, do not invent one. Stop and document the exact missing boundary.
That pre-condition is met. A QUESTION_NEEDED ContemplationResult cannot be
obtained at the runtime fallback boundary without either (a) calling the
off-serving contemplation producer — which the task's hard constraints forbid —
or (b) fabricating a result, which the "do not invent one" clause forbids. So the
wiring is stopped here and the gap is documented for review.
2. The boundary that wiring would target
The smallest legal fallback boundary in the serving turn is the universal-disclosure fallback: the point where the turn has decided it cannot ground the input and falls back to the "I don't know" disclosure. That is exactly the missing-information condition a served ASK is meant to convert into a question.
- Surface constant:
chat/runtime.py:172_UNKNOWN_DOMAIN_SURFACE = "I don't know — insufficient grounding for that yet." - Main-path fallback assignment:
chat/runtime.py:2582(response_surface = _UNKNOWN_DOMAIN_SURFACE),selected_source = "universal_disclosure"at:2586. - Cold/stub-path fallback assignment:
chat/runtime.py:2060,:2295. - Final surface finalize (post-governance) before the
TurnEventis built:shape_surface(...)atchat/runtime.py:2746,TurnEvent(...)at:2753.
A wiring would call, at that fallback (post-shape_surface, pre-TurnEvent):
response_surface = maybe_apply_served_ask(self.config, response_surface, provider=<provider>)
Under default config (ask_serving_enabled absent/False) this is a pure no-op —
acquire_served_ask_candidate never calls the provider while the gate is dark.
The unresolved part is <provider>: what lawfully produces the candidate.
3. Producer / consumer topology (why no lawful provider exists)
graph LR
subgraph OFF-SERVING (growth organ)
CT["generate.contemplation.contemplate<br/>(pass_manager.py)"] -->|ContemplationResult<br/>terminal=QUESTION_NEEDED<br/>question_path=teaching/questions/*| ART[(teaching/questions/ artifact)]
RUN["core/contemplation/runner.py::run_contemplation<br/>(offline loop)"] --> CT
end
subgraph SERVING TURN (chat/runtime.py)
TURN["ChatRuntime.chat()"] --> FB["_UNKNOWN_DOMAIN_SURFACE fallback"]
FB -. "needs a ContemplationResult here" .-> GAP{{"no in-turn producer<br/>and no carried artifact handle"}}
end
ADAPTER["chat.ask_runtime.maybe_apply_served_ask<br/>→ acquire_served_ask_candidate<br/>→ evaluate_served_ask"]
GAP -. consumes .-> ADAPTER
ART -. "not carried into the turn" .-> GAP
The consumer side is fully built and merged:
core/epistemic_disclosure/ask_serving.py::evaluate_served_ask— validates the Q1-DDeliveredQuestionartifact off aContemplationResultand returns aServedAskDecision(#677).core/epistemic_disclosure/ask_acquisition.py::acquire_served_ask_candidate— gate-first seam that accepts either acontemplation_resultor aContemplationProvidercallable (#678).chat/ask_runtime.py::maybe_apply_served_ask— runtime-facing helper that returns the served surface or the unchanged fallback (#681).
The producer side is off-serving by construction:
- The only code that produces a
ContemplationResultwithterminal == QUESTION_NEEDEDand aquestion_pathisgenerate/contemplation/pass_manager.py(contemplate(...), ASK delivery at_handle_ask_delivery, lines ~103–121). - Its package docstring (
generate/contemplation/__init__.py) declares it explicitly off-serving:"Contemplation v0 (N6) — a single bounded read -> classify -> terminal -> maybe-emit pass. Off-serving growth organ ... No loops, no daemon, no L10 runtime, no self-modification."
- Its only non-test caller is the offline loop
core/contemplation/runner.py::run_contemplation— not the serving turn.
No plumbing connects the two. Confirmed by source sweep:
chat/runtime.pyhas zero references topass_manager,ComprehensionAttempt,ContemplationResult,QUESTION_NEEDED,Terminal,question_path,proposal_path,deliver_ask, orDeliveredQuestion. (Itscontemplation/discourse_contemplationsymbols are the unrelatedteaching.contemplationdiscovery/plan path, producingContemplationFinding, not aQUESTION_NEEDEDContemplationResult.)- The only ASK reference anywhere under
chat/ischat/ask_runtime.pyitself. tests/test_ask_serving_integration.pystates the position in its own header:"These tests intentionally avoid
chat.runtime. This slice is adapter-only: [it does not] wire runtime acquisition ofContemplationResult."
4. Why neither escape is lawful under the constraints
| Way to get a candidate at the boundary | Verdict |
|---|---|
Runtime calls generate.contemplation.contemplate / pass_manager per turn |
Forbidden — "Do not call pass_manager directly from runtime"; also it is an off-serving organ ("no L10 runtime"), so calling it in the serving turn contradicts its design. |
Runtime calls deliver_ask / render_question to build a question in-turn |
Forbidden — "Do not import/call render_question"; also re-renders prose the serving layer must only consume. |
Add an instance hook (self._served_ask_provider = None) that only a test populates with a DummyResult |
Inventing one. No production code path would ever set it. The required test "runtime returns artifact text" would then prove only that a back-door hook is reachable from a fabricated double — decoration, not proof (CLAUDE.md, "Schema-Defined Proof Obligations"). |
Read an already-written teaching/questions/ artifact from the turn |
No lawful handle. evaluate_served_ask consumes a ContemplationResult object (it reads question_path off it); nothing keys an off-serving artifact to the current turn, and scanning the sink would be new, unspecified acquisition logic — not "wiring an existing helper." |
Because every route is either forbidden or an invention, the required test "gate enabled + valid QUESTION_NEEDED artifact: runtime path returns artifact question text" cannot be satisfied honestly through the serving turn today. That is the precise blocker.
5. What is genuinely missing (the lawful next slice)
A real, load-bearing boundary needs an in-turn acquisition path that does not call the off-serving producer. Options, for review:
- Carried-handle acquisition (recommended). Define a serving-safe lookup
that, given the turn's already-computed limitation/assessment, resolves a
pre-delivered
teaching/questions/artifact into aContemplationResult-shaped handle — reading an existing off-serving artifact, never producing one. This givesmaybe_apply_served_aska lawfulproviderwhose source exists in production. It is a new acquisition seam (its own reviewed PR), not a one-line runtime hook. - Explicit producer→serving plumbing. A separate slice that runs the off-serving contemplation pass out-of-band and persists a turn-addressable handle the serving turn can resolve. Larger; needs its own ADR and trust boundary (it bridges the off-serving organ to serving).
Either way, the runtime wiring is a follow-on to one of these — not this PR.
6. What was (and was not) changed here
- Changed: this document only.
- Not changed:
chat/runtime.py, the publicchat(...)signature,ChatResponse,TurnEvent, telemetry schema,Q1B_ASK_CARVE_OUT,proposal_allowed, VERIFIED serving,CLAIMS.md, metrics. No call topass_managerorrender_questionwas added. - Base state: branch is cut from
main@32f8c2ff; the focused ASK suite was green (434/434) earlier this session at that commit, and no edits were made to any code under test.