DecisionCase lifecycle
- Status
- contract (production hardness)
- Conceptual layer
- ④ Decision
- Repo layer
- L4
knowledge-reasoning - Source
- architecture section 3.6.4, section 5.3
- ADR
- 027
- Siblings
07-finding-runtime.md·12-trace-and-eval.md·25-work-queue-and-concurrency.md·27-ports-and-reliability.md·00-kernel.md
A DecisionCaseOne run unit: intake + snapshot + obligations + candidates + terminal is a durable unit of work, not a single request. Models, L3, and memory calls fail. Processes restart. Without explicit states, leases, timeouts, and resume rules, you get orphan traces and double emits. This doc is the lifecycle contract.
States#
How to read it.
- Cases move from the plant work queue through lease, running, and optional port waits before
terminalizing. - Semantic terminals only after the stage graph finishes with a frozen ledger;
failed_infraandtimed_outare ops paths, not withholds. heldstays in the L4 store; Prescription delivery to L5 waits on portfolio release.
Build now: states above + lease heartbeat. Later: paused state via ADR if needed.
View Mermaid source
flowchart TB
%% house-style: decision-case-lifecycle
subgraph run["Active case"]
direction LR
q["queued"] --> ls["leased"]
ls --> rn["running"]
rn --> ap["awaiting_ports"]
ap --> rn
ap --> rw["retry_wait"]
rw --> ls
rn --> tz["terminalizing"]
end
gate{{"Kernel re-check / CardSink"}}
subgraph term["Terminals"]
direction LR
ok["terminal:<br/>emit / supersede / withhold / abstain"]
hd["held"]
cx["cancelled"]
to["timed_out"]
fi["failed_infra"]
end
back(["↩ Opportunity ledger on semantic block"])
tz --> gate
gate --> ok
gate --> back
tz --> hd
q & ls & rn --> cx
rn & rw --> to
ap & rw --> fi
classDef govc fill:#fff4d6,stroke:#c99a2e,color:#000
classDef agentc fill:#e8f0ff,stroke:#5b7bd5,color:#000
classDef loopc fill:#eef7ee,stroke:#4f9a4f,color:#000
class gate govc
class back loopc
| State | Meaning |
|---|---|
queued | On plant work queue |
leased | Worker claimed; lease heartbeat required |
running | Inside stage graph |
awaiting_ports | Blocked on ModelSlot / L3 / memory / builder |
retry_wait | Scheduled retry after retryable port failure |
terminalizing | Kernel re-check / CardSink |
terminal | emit | supersede | withhold | abstain recorded |
held | Portfolio hold — L4 store, not L5 |
cancelled | Explicit cancel; trace closed with reason |
timed_out | Wall or lease timeout; trace closed; opportunity ledger if a candidate existed |
failed_infra | Non-retryable infra abort — not a semantic withhold; ops alert |
Rule: failed_infra and timed_out are not customer withholds. They do not teach soft gates. They page on-call. Semantic withhold / abstain only after the stage graph could finish with a frozen ledger.
Identity and durability#
| Field | Role |
|---|---|
decision_case_id | Stable UUID |
plant_id | Tenancy |
lockfile_id | Pin for the whole case — never mid-case pin flip |
correlation_id | From work item; joins logs/metrics/trace |
condition_key | When known |
lease_owner / lease_until | Crash recovery |
attempt | Retry count |
created_at / updated_at | Recorded time |
Case row + append-only stage events live in the L4 operational store. Crash mid-stage: resume from last completed stage checkpoint if ledger frozen for that stage; otherwise restart from last safe checkpoint (never re-emit without idempotency key — 27).
Timeouts (registry defaults — lock in ops)#
| Timeout | Applies to | On fire |
|---|---|---|
case_wall_clock | Entire case | timed_out |
lease_heartbeat | Worker lease | Another worker may reclaim if lease expired |
stage_budget | Single stage | Fail stage → retry policy or timed_out |
port_deadline | Each port call | See 27 |
Exception-tier cases get tighter wall clocks than energy/cost investigative lanes (latency_tier registry).
Cancel#
Who may cancel: on-call (ops), plant owner (plant-scoped), system on kill-switch (28-commissioning-and-controls.md).
Cancel writes a DecisionTraceAlways-on record: observed, context, action, policy, approval, outcome (and seam decisions) with terminal_reason=cancelled (or closes as failed_infra if no ledger). Never leaves a half-sent CardSink without compensating idempotent check.
Crash resume algorithm (normative intent)#
- Worker starts → claim next
queued/ reclaim expiredleased. - Load case + last completed stage id + frozen sub-ledger.
- If CardSink already succeeded for this
decision_case_id+emit_idempotency_key→ markterminalemit/supersede; do not call models again. - Else continue from next stage under the same lockfile and as-known-at snapshot id.
- Do not refresh PSMPlant Situation Model to “now” on resume — that breaks replay. New evidence requires a new case or explicit
recheckwork item.
Token budget failure#
If a stage cannot fit required proof rows (proof floorMinimum evidence/structure required before emit (asset bound, verification path, L3 condition test for discoveries), intersecting hard constraints, calculator refs for priced claims) inside the token budget after zoom policy:
→ withhold or abstain with gate_id=token_budget_required_proof (hard-adjacent: not soft-tunable to “drop proof”).
Never silently truncate required measured rows to force a draft. Optional advisory / OE chunks truncate first (05-context-engineering.md).
Rejected alternatives#
| Alternative | Why |
|---|---|
| Stateless request/response only | Cannot survive restart or multi-port calls |
| Auto-refresh PSM on resume | Breaks as-known-at / pass^k |
| Counting infra timeouts as soft-gate blocks | Poisons calibration |
| Mid-case lockfile upgrade | Non-reproducible terminal |
What would change this#
- Wall clocks too tight for enveloped L3 sims → raise stage budgets by latency tier after measured p95.
- Need human “pause case” without cancel → add
pausedstate via ADR.
v1 slice vs later#
| v1 | Later |
|---|---|
| States above; lease + wall timeout; resume from stage checkpoint | Multi-worker HA with fencing tokens |
| Infra fail vs semantic withhold split | Same |
| Manual cancel via ops tooling | L6 plant-owner cancel for plant-scoped cases |
Change class#
Timeouts and caps: data. New states that change terminal semantics: ADR + kernel/lifecycle bump.
Page history: last 4 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): agentic decision architecture, ADRs, and production hardness
8275e7c