L4 — As-built decision runtime
- Status
- as-built
- Conceptual layer
- ④ Decision
- Repo layer
- L4
knowledge-reasoning(stamped_l4) - Source
- architecture section 2.2 KR, section 3.6.4, section 5.3
- Normative contract
README.md·00-kernel.md· ADRs 020–027
Maps what is implemented today under src/stamped_l4/runtime/. Prefer numbered contract docs (00–29) for design rules; use this page for package paths and live compile behavior. Ask on main since knowledge-reasoning PR #24; Ask→runtime discovery in PR #26.
Live compile path#
Evidence envelope (emitted ∧ l4) or enqueue/sweep — as built: Finding
→ PlantWorkQueue
→ DecisionRuntime.process_one() (worker/decision_runner.py)
→ stages: candidates → constraints → portfolio → minimizer → kernel_recheck → terminal
→ DecisionTrace (always)
→ CardSink.deliver only if PlantFlags.can_emit()
Semantic terminals: emit | supersede | withhold | abstain.
Not the compile path: legacy LangGraph quality / Lane A graphs under graph/ remain in the tree (analyst and history) but are not invoked from worker/runner.py for inbox/preview compile. Preview responses map terminals to legacy status strings; prescription body is null on the runtime path — card-proposal is the customer-facing proposal object (direction: full PrescriptionWhat to do, why, who, check plan (direction; as built: prescription.json 1.0.0 / card-proposal) section 5.3).
Safe local default: emit_enabled=false, shadow_only=true (traces without sink delivery).
Package map (stamped_l4.runtime)#
Table: 17 rows by package
| Package | Responsibility |
|---|---|
kernel | Pure terminals, hard/soft gates, evidence tiers — no I/O |
contracts | Pydantic models vs external/contracts/schemas/intelligence/* |
registry | Versioned registries + default stage sequence |
psm | Plant Situation Model snapshots / digest |
constraints | Typed predicates + evaluator |
seams | Dual-family model slots, budget, economy cache |
stages | Intake floor, condition key, proof, stage executor |
portfolio | Dedupe, conflict, supersede, attention, hold |
ledger | Opportunity ledger + owner backlog |
discovery | Scanners, shift sweeps, hypothesis lane hooks |
queue | Plant work queue |
lifecycle | DecisionCase states, leases, resume |
ports | Protocols + reliability middleware |
control | Flags, safe-start, kill switch |
sinks | StubCardSink (file) · HttpCardSink |
trace | DecisionTrace builder, replay |
obs | In-process metrics |
Trust: kernel never imports adapters. CardSink is the only outbound side-effect for customer-facing proposals. failed_infra is ops/lifecycle, not a semantic withhold.
Control flags (defaults)#
| Flag | Default | Meaning |
|---|---|---|
emit_enabled | false | Allow sink when not shadowing |
shadow_only | true | Full path, block sink |
kill_switch | false | Engaged via kill API/CLI |
sweep_enabled | true | Shift/sweep work |
discovery_shift_sweep_enabled | false | Discovery scanners on sweep |
hypothesis_enabled | false | Hypothesis lane |
can_emit() = emit on and not shadow and not kill.
Surfaces#
| Surface | Notes |
|---|---|
| HTTP inbox / preview | Runtime-backed; legacy path names kept |
/v1/runtime/* | Cases, traces, ledger, backlog, flags, enqueue, kill |
CLI stamped-l4 | enqueue, sweep, flags, kill, trace, replay, bench |
Ask /v1/chat/sessions/* | Live read-only question answering over SSE v2; never emits a card. L4_ASK_ENABLED=0 returns 503 ASK_MOVED |
Ask (package stamped_l4.ask)#
Design rules: 14-ask.md. Consumer detail: docs/ask/ASK_PATH.md in knowledge-reasoning.
L6 BFF → AskHub (turn lock, step log, SSE v2 replay)
→ hard-stop gate (refuse before any model call)
→ intent pre-route + recipe → clarify (rules; seam recorded)
→ ReAct loop (≤ 6 model turns) over 35 read tools via one Dispatcher
→ verifier per claim (numbers, money, direction, citations vs the turn ledger)
→ token · citation · done events; thread digest retained after done
| Piece | As built |
|---|---|
| Retrieval | kb_search missions: plant/asset wall, graph-first ontology expansion, BM25 + dense fused by RRF, rerank; vector-first fallback with a caveat |
| Memory | Hindsight plant bank + per-thread bank; off unless L4_MEMORY=hindsight. Plant bank written only by closed outcomes and human-confirmed promotions |
| Seams | ask_intent_routing, ask_recipe_select, ask_clarify_need, context_zoom, oe_retrieval_mission, ask_sub_investigate_gate, ask_thread_retain, ask_discovery_request, ask_answer_framing; decisions recorded for Jev |
| Discovery | request_discovery enqueues ask_sweep (P4) for one asset; the queue dedupes while one is queued or running. The worker runs it through the sweep path under the plant's sweep flags and keeps only hits on that asset |
| Sub-investigation | Private runtime with DryRunCardSink; nothing leaves L4 |
Known gaps: Ask sends no scanner_context, so an Ask sweep runs but finds nothing yet. Cards and PSMPlant Situation Model for Ask come from fixture ports. Ask and the runtime keep separate hard-stop lists.
Storage note#
Alembic migration 0003 creates durable runtime tables (cases, traces, queue, ledger, flags, …). With a session factory, DecisionRuntime uses SQL for queue, cases, flags and traces; the opportunity ledgerStore of every blocked candidate with gate id and later outcome if known and hold store stay in memory. The API always builds a factory (local SQLite without L4_DATABASE_URL); the worker only does with L4_DATABASE_URL and warns otherwise. API and worker must share L4_DATABASE_URL, or Ask discovery requests never reach the worker.
Related#
- L3 EvidenceLayer contract for detector output (direction; as built: Finding finding.json 1.2.0) emit:
../l3/04-finding-contract.md - Handoff:
../../handoff/l4/stamped-l4-architecture-handoff.md
Page history: last 5 changes
- docs(technical): rewrite l4 21-30, glossary and README; reconcile architecture gaps
e7fead7 - docs(decisions): add ADR-033..038 (twin runtime, fast read path, plant-side writer, message classes, alerts and quality-to-lot link, part-keyed parameters), fast-loop technical set, rebuilt index with renumbering map; fix bare-number link text and ranges
22e2872 - docs(decisions): renumber live ADRs 001-032 in order, mark withdrawn refs ADR-W###, repoint withdrawn links to archive, note partial supersessions
36c944e - docs(l4): record live Ask, ask_sweep bridge and SQL stores in as-built
2e73007 - docs(l4): align the handoff with the decision runtime
a94c9f4