Skip to content

refactor(decision-engine): strip Part 2 — remove legacy decision data and compatibility - #205

Merged
g1e2x87 merged 33 commits into
mainfrom
feat/decision-engine-strip-p2
Nov 14, 2026
Merged

g1e2x87 merged 33 commits into
mainfrom
feat/decision-engine-strip-p2

Conversation

@g1e2x87

@g1e2x87 g1e2x87 commented Nov 14, 2026

Copy link
Copy Markdown
Member

Decision-engine strip, Part 2: legacy decision data and compatibility removed

Part 1 made the decision engine advisory-only and independent. Part 2 deletes what the old gating decision plane left behind. Development phase: no migration, compatibility shims or importers.

What changes

  • Deleted: src/causal_tree/, the decision store and journal, legacy decision recovery, the [causal_tree] config section, decision-bound approval grants and /approvals/{decision_id}/resume, process relaunch, and the scanners that guarded the deleted code. Retired routes answer 410.
  • Rehomed: the turn service and context moved off the engine types. The runtime key moved out of the decision store. Cost is projected from the terminal's retained settlement evidence through the recovery outbox.
  • Child sessions: one ordinary/operator issuance path. Recursive delegation follows the root's gates and is bounded by depth, budget and integrity. Supervised children use the production run approval resolver. Segment terminals carry the parent session key.
  • Engine administration: replacement CLI/HTTP config surface with an exact key allowlist. A backend selection whose latency budget is below the catalog recommendation is refused on every entrypoint. prx init --decision-enable turns on the advisory engine.
  • Restart: every terminal verification failure is TerminalNotReplayable. A terminal that fails verification is quarantined instead of blocking startup.
  • Kept: the skills feature in full, including lifecycle actions, tables and review.

Specs and audits

  • Requirements: REQ-DECISION-ENGINE-STRIP-P2.md (+ G5B addendum, controller rulings), design DESIGN-P-DECISION-ENGINE-PART2-MIGRATION.md (both Codex-audited).
  • Code audit: Codex round 1 FAIL 0/6/3 → fixed; round 2 FAIL 0/1/3 → fixed.

Verification (before this PR)

Full CI gate set mirrored on the qaa build host at effee62: same rustc 1.99.0, nextest 0.9.151 profile ci, test_home_guard and shard filter.

  • quick, fmt, clippy (default and all features), machete, architecture_boundaries, doc-tests: PASS.
  • Tests: 11065 run, 11065 passed. The same mirror on main f4cc5ad passes 11393/11393; the lower count is the deleted decision-store tests.

After landing

A full prx chat session with a real provider, built from main, is the real test. It covers tools, approval, engine on/off, child and process-mode child, restart, retired surfaces, cost, the network guard and a gateway webhook turn.

…ey, persisted claim semantic, cost projector

Part 2 branches from landed main f4cc5ad (Part 1; 6676a46 is an ancestor).

S2 re-measure on f4cc5ad vs the spec's 2318d71 column (lines/files):
A1 20/9 = ; A2 102/21 = ; A3 139/32 vs 148/31 (05a4 moved approval wiring to
runtime/turn_publication{,/approval}.rs; PR #204 CI 9135849 deleted
historical_resume_drift.rs); A4 188/50 vs 187/50 (05a4 IssuedChildGrant moves to
terminal/binding.rs and turn_publication/builder.rs; PR #204 CI child_process.rs +1);
A5a 1578/253 vs 1635/253 (05a4 rehome and PR #204 CI test rewrites); A5b 845/177
vs 852/178 (05a4 decision_source -> turn_source moves, f4cc5ad delegate split,
PR #204 CI fixture rewrites); A5c 63 vs 65 (05a4 moved build_error.rs and
execution/operator_process.rs to turn_publication/); A5d 4/3, A5e 18/8, A5f 8,
A6 12/3, A7 13/8 unchanged.

- P2-R1: session_worker::transport_key::TransportSecret {for_launch, existing};
  both session_worker_signing_key copies, existing_session_worker_signing_key and
  the process-local fallback deleted; SESSION_WORKER_SECRET must be 32 bytes.
- P2-R2: runtime key at <ws>/self/runtime/runtime.key; state_key is the runtime
  key always (no plane gate, no ephemeral fallback).
- P2-R3: claim payload always carries the semantic; restart verification of
  D4.4 steps 1-7 (turn_terminal::restart_verify); canonical key_id enforced.
- P2-R4: golden vectors E2-E7b, K_child, K_batch, batch and segment seals,
  constraints and effective-config payload literals; restart tests per status
  with negative cells.
- P2-R5: runtime::cost_recovery::CostOutboxProjector; scan_recovery_costs with a
  raw cursor and exhaustion flag replaces pending_recovery_costs.
- P2-R6: warn_legacy_files_once.
- P2-R7: surface and oversized-reply model id from the pinned TrustedParts.

Tests deleted: transport_key::tests::{an_existing_publicly_readable_valid_key_is_
tightened_without_rotation (OV-6), failed_key_publication_logs_the_cause_and_
returns_the_fallback_error (fallback removed), concurrent_transport_key_creators_
return_the_same_persisted_material (replaced), an_invalid_existing_transport_key_
is_never_replaced (replaced)}; runtime_key::tests::ephemeral_keys_need_no_
workspace_and_do_not_share_fingerprints (no ephemeral fallback).
Tests retargeted: auth_02_3_tests restart scan -> cost_recovery::run_pass;
sqlite recovery_authority tests -> scan_recovery_costs; identity and H2 review
key-path tests -> self/runtime/runtime.key.

Acceptance: A1 20/9 -> 0 (exit 1).
…e, /api/decision-engine, fresh telemetry store

P2-R8: `prx decision-engine {status, backends, config set <key> <value>}` in
the new `decision_engine_cli.rs`; `config set` goes through
`config/manage/mutation/decision_engine.rs` (renamed from `causal.rs`) with
the allowlist (`EngineSetting`, `engine_setting`, typed `EngineKeyError`).
`prx causal …` prints one deprecation line and exits 0. `causal_cli.rs`,
`causal_cli/backend_transport_tests.rs` and every user deleted; `DecisionSetup`,
`BackendKindArg`, `hosted_owner`, `hosted_setup_steps` moved for `prx init`
and onboarding. The onboarding local backend test is replaced by a status
line (its `DecisionAdmin` is deleted).
P2-R9: the ten `/api/causal/*` routes answer 410 `{"error":"gone","see":
"/api/decision-engine"}`; new `gateway/api/decision_engine.rs` (status,
backends, analyses/outcomes with `limit` 1..=100 default 20, config plan/apply
with allowlisted keys, write-only credential -> 204, typed 400 body).
P2-R10: `decision-engine.sqlite` is fresh-schema v2 (`engine_analyses`,
`engine_outcomes` with the spec DDL; other `user_version` dropped and
recreated); analyses recorded once per turn with the redacted brief sent;
`TurnOutcome.result` (`TurnResultClass`) replaces `succeeded`, plus
`cost_micro_usd` (same priced usage record as the `turn:<id>:usage`
settlement) and `duration_ms`.
P2-R11: `causal_tree::{admin, evaluate, report_catalog, v1_compat}`,
`FactoryDeps::load_key`, `CausalMutation::ApplyCalibration`, `read_proposal`,
`StoredReport` deleted; `config set apply-calibration` is one typed error.
`KeyDomain::ReportOwnership` renamed `SkillRecordSeal` with its label
unchanged (live skill lesson-delivery/study seals; bytes identical).
Admin-only decision-store readers deleted with them (`list`, `inspection_*`,
`evaluation_rows`, `import_progress`, `mark_imported`, `record_decision`,
writer `Close`/`Imported`, `LocalOperator`, `TimeWindow`, `EvaluationRows`,
`DecisionId::legacy`, `detach_last`, `all_stored`); store tests keep
`counts`/`close`/`path` as helpers in the store's `testing` module.
Ruling: `DecisionKey::ephemeral` -> `security::runtime_key::test_support::ephemeral_key` (13 fixture files).
Scanners: `decision_seals.rs` admin entry, `causal_domain.rs` admin entry,
`scanner_probes.rs` admin probe, `panic_policy.rs` causal_cli entry removed;
`causal_policy.rs` writer path renamed.

Tests deleted (reason): causal_cli unit tests and backend_transport_tests
(module deleted); gateway causal tests (routes retired); tests/cdp_cli.rs
`prx causal` read/test/config cells (command retired); mutation
`apply_calibration_needs_a_report_and_a_pin` (ApplyCalibration deleted);
factory `a_disabled_plane_creates_no_key` and the load_key assertion
(load_key deleted); `engine_outcomes_gains_a_scope_column_once` (fresh
schema); `decision_id::a_legacy_id_is_stable_and_round_trips` (legacy
deleted); manage_cli `a_decision_secret_is_read_only_after_confirmation`
(credentials only via PUT /api/decision-engine/credential); store tests that
read deleted admin readers or import progress (files_and_recovery:
a_read_only_store_creates_nothing, a_read_only_open_of_an_existing_store_changes_no_file,
the_workspace_id_is_stable_across_a_move, import_progress_needs_every_decision_stored,
a_read_only_open_sees_rows_in_a_live_wal; tests/mod.rs:
late_outcome_joins_once_and_a_conflicting_settlement_is_refused,
partial_outcome_at_provider_retention_boundary_reopens_and_replays,
a_conflicting_decision_is_refused_not_overwritten,
decision_debug_omits_private_endpoint_projection_and_lineage,
decision_chain_transaction_rolls_back_on_conflict_and_emits_commit_after_storage,
inspect_other_owner_decision_denied, a_newer_schema_opens_read_only;
batching: the_real_writer_stops_batches_at_drain_and_capacity_before_notifying);
agent.rs decision-receipt inspection block of custom_memory_loader_fixture.
New tests: tests/cdp_cli.rs {decision_engine_status_reports_disabled_by_default,
causal_command_prints_one_deprecation_line, decision_engine_config_set_rejects_unknown_and_secret_keys};
gateway::api::decision_engine::tests::{legacy_causal_routes_return_gone,
analyses_are_bounded_and_redacted, config_apply_rejects_secret_and_unknown_keys};
decision_engine::tests::{telemetry_store_holds_only_analyses_and_outcomes,
turn_outcome_records_cost_duration_and_result_class};
mutation `engine_setting_accepts_only_the_allowlist`.

A2: 102 lines / 21 files (f4cc5ad) -> exit 1.
…ger only, decision-bound grants refused

P2-R12 approval ledger ownership
- Deleted security/approval_ledger/{historical, resume, resolution, grant, pending}.rs,
  ApprovalLedger, ApprovalRuntime, GrantScope, keys::{DecisionAuthority,
  decision_resolution_key}, review::valid_projection and every impl ApprovalLedger.
- DecisionApprovalView and scope_from_source moved unchanged to
  security/approval_ledger/view.rs (OV-12); review.rs keeps only ApprovalReviewAdapter.
- ApprovalAuthority keeps the run-keyed key bytes; the decision-only grant-id/op-id
  parameters and resume_key are removed (no other implementor).
- The decision store refuses historical collect_approval_arguments evidence instead of
  validating it with valid_projection (store deleted in G5).

P2-R13 claimed-decision and resume plumbing
- Deleted security/decision_approval.rs, tools/capability/resume_evidence.rs,
  DecisionResumeAdapter/DecisionResumePort, ToolExecutionContext.decision_resume,
  TurnRuntimeContext.{resume, resume_evidence}, resume_tool_name,
  decision_advisory::resume_gate_maps, the resume parameter of
  PublicationRules::{surface, restricts, capability} and TurnSurfacePort::fresh_resume_evidence,
  TurnSurface::{restricted_to, single_published}, turn_publication::open_resume_turn and
  turn_publication/approval.rs, runtime/turn_decision/pending_approval{,/h2,/sealed_call}.rs,
  bind_resume_execution/execute_agent_resume, collected/claimed-resume context state,
  RunAllowances::{inherit_resume, retire_pending} and the unfunded allowance state.
- Gateway: the /approvals/{id}/resume route, run_console_resume and ConsoleTurnMode are
  deleted; /sessions/{id}/approvals/{id} GET/POST/DELETE serve run-keyed calls only and the
  inbox lists run-keyed calls only (run-call routes kept).
- Chat: /resume-decision, /approval --decision, /revoke --decision, /approve|/deny --decision,
  decision display aliases and the foreground resume close are deleted.
- Controlled H2 gateway: the local-owner review (LocalOwnerReview/Stamp, attest, the
  show/approve/deny/revoke reader verbs) is deleted; the local control reader keeps `exit`.
  Gateway lifecycle actions that required a local owner review are refused.

P2-R14 ApprovalGrantV2.decision_binding
- Field and DecisionApprovalBinding kept (wire). verify_for_decision and
  issue_decision_one_shot deleted. New typed GrantRefused::DecisionBound via
  refuse_decision_binding(); verify_for_operation(_bound) and SecurityPolicy v2 verification
  refuse any grant with decision_binding: Some.
- New test acl::approval_grant::tests::grant_with_decision_binding_is_refused.

Tests deleted (reason: they exercise deleted historical decision-approval code):
- security::approval_ledger::tests: decision_keys_are_byte_identical_to_pre_extraction,
  pending_and_review_payloads_keep_pre_extraction_bytes, consumed_grant_claim_keeps_pre_extraction_bytes,
  revoked_grant_claim_keeps_pre_extraction_bytes, signed_resume_intent_keeps_pre_extraction_bytes,
  claim_and_revoke_race_has_exactly_one_winner, consumed_grant_cannot_be_revoked_or_reclaimed_after_restart,
  revoked_grant_cannot_be_claimed_after_restart, wrong_owner_or_session_cannot_review_or_claim,
  run_keyed_and_decision_keyed_listings_do_not_mix, decision_claim_checks_stored_grant_owner,
  pending_approval_loads_from_the_journal_with_a_dropped_store_projection,
  pending_approval_with_a_missing_journal_row_is_refused,
  pending_approval_with_a_tampered_journal_row_is_refused,
  legacy_pending_approval_without_journal_rows_must_be_requested_again,
  and the whole historical_exact_call_approval module
- runtime::turn_decision::tests::historical_support (module),
  recursive_budget::authenticated_resume_without_original_allowance_cannot_mint_a_child_slot
- tools::skills tests::a_decision_bound_grant_works_only_inside_the_claimed_scope (with_claimed_decision)
- chat::binding::tests::{resume_admission_infrastructure_failure_offers_retry_without_error_details,
  resume_admission_authority_failures_share_an_opaque_refusal,
  enabled_fresh_plan_resume_without_approved_grant_is_rejected}; resume=true cells removed
- chat::input_queue::refusal_tests (only helper was the resume-failure path)
- runtime::h2_entry::review::tests (module; local owner review deleted),
  origin::tests::explicit_resume_normalization_... (replaced by revoked_profile_refuses_new_terminal_input)
- tools::execution::tests::admission_matrix::{budget::resume_context_carries_budget,
  surface::console_resume_replays_model_turn_with_surface,
  surface::console_and_chat_resume_go_through_model_turn_context} (model_turn_context deleted)
- tests/chat_pty_e2e/decision_resume.rs
- architecture scanners: tool_tests::resume_sites_go_through_model_turn_context + its mutation
  probe, the execute_agent_resume entries in tool_tests/tool_tables, causal_domain
  valid_projection entry
Retargeted: recursive_allowance::tests::context_drop_removes_every_run_alias (denied-pending half removed),
gateway sessions targeting test (no pending decision count).

Acceptance A3: 139 lines / 32 files (cc50935) -> exit 1.
…ycle actions

G3 removed the controlled-Gateway local-owner review together with the
decision approvals it was attached to, so the Gateway lifecycle actions
publish, annotate, rollback and retire always failed closed. Restore the
review, decoupled from decision approvals:

- EntryInvocation::from_verified is async; a Gateway lifecycle mutation
  posts a review request to the original local terminal (in-process
  broker on ControlledH2Profile, bounded to 16 waiting reviews, 300 s
  window) and admits only after the local owner approves that exact call
  (`approve REVIEW CALL_DIGEST`). `deny REVIEW`, timeout, reader exit
  or a revoked launch refuses it. The admitted invocation carries the
  LocalOwnerReview, which is re-verified (origin, args, call digest,
  capability and catalog generations) at consume; consume is single-use.
- The local control reader accepts approve/deny/exit and ends every
  waiting review when it exits.
- No decision store, decision id or key is involved.

Tests: runtime::h2_entry::review::tests::{local_owner_review_gates_publish,
local_owner_review_gates_annotate, local_owner_review_gates_rollback,
local_owner_review_gates_retire} (approved -> performed once; denied ->
not performed) and reader_exit_ends_waiting_review_without_performing.
…tor path, delegated grants removed

P2-R15 one issuance path
- Plane conditions removed: child_source/publication.rs (capture, verify_caller,
  verify_live, verify_task_scope), turn_decision/execution.rs (ordinary child
  publication always captured for delegate/sessions_spawn), turn_decision/issued.rs
  (delegation storage readback), outgoing_delegation, session_worker ordinary_source,
  sessions_spawn {spawn, batch, batch_journal, tool}, runner/run.rs,
  process_terminal/ordinary.rs, parent.rs restore_ordinary_process, delegate.rs.
- config/generation.rs: for_disabled_child -> for_child, assertion removed, encoding
  unchanged (S7 pinned by config::generation::tests::for_child_fingerprint_encoding_is_unchanged).
- Every child kind uses the ordinary/operator publication. from_worker_channel sites
  already pass MemoryFabric::workspace_id() (S9).
- Child money/scope: TurnRuntimeHolder ordinary_money/recursive_allowances; the
  incoming-grant arm of recursive_money/issue_recursive_allowance removed (S6 note in
  report: the explicit allow_delegation recursive opt-in required a decision-issued
  grant and is now refused on every surface, as it already was on ordinary surfaces).

P2-R16 delegated issuance deleted
- IssuedChildGrant, ChildGrantRuntime, ChildGrantData, ParentModelSelection,
  child_source/recursive.rs, ChildSegmentSequence, restore_worker_input,
  ChildModelSelection, ChildProviderBinding::{build, build_worker, source_proof,
  selection_for}, the outgoing-grant task local, ToolExecutionContext.child_grant,
  SpawnExecutionContext.incoming_grant, TurnRuntimeHolder.parent_grant,
  TerminalBinding.parent_grant, PublicationInputs.parent_grant,
  ChildDecisionPublication, OwnedTurnSourceProof::from_child_receipt,
  LogicalTaskTerminalRef::granted, DelegateChildAuthority::Granted (now a struct),
  task_mode incoming grant branch, ChildExecutionPolicy.
- turn_decision/delegation.rs deleted: issue_child_grant and
  validate_delegation_storage removed; the live ordinary helpers
  (configured_delegation_target, verified_delegation_scope, outgoing_delegation)
  moved to turn_decision/child_scope.rs (G5 rehomes turn_decision).
- Wire: WorkerManifest.{capability_manifest, child_source}, WorkerChildSource,
  WorkerResult.completion_evidence and WorkerCompletionEvidence removed;
  WorkerManifest is deny_unknown_fields; WorkerOrdinarySource.capability_manifest kept.
- authority_journal: authority/launch_parent/batch_parent tables and their code
  (ParentIssuerEvidence, LaunchParentRow, BatchParentRow, persist, persist_batch_parent,
  read_*_parent, verify) removed; decision_evidence/child_outcome kept for G5.
- Verifiers/fallbacks: verify_worker_parent_authority, delegated
  process_terminal/launch.rs (process_identity moved up), ProcessRecovery,
  VerifiedWorkerCompletion, delegated verify/recover_* paths, legacy_reader,
  repair_store, ParentSource::restore_process, batch_journal v1/3/4 arms and
  verify_store_parent (v2 only; ordinary_source required; seal bytes unchanged),
  operator segment decision_id branch (a decision record is refused).
- Delegated completion journal (session_worker::completion::persist) removed.
- Settled terminal facts keep parent_authority_id in the closed schema, always None.
- Dead after the cascade, deleted: legacy no-source worker provider path
  (validate_worker_route, worker_provider, build_worker_provider_selection,
  worker_lineage_payload, scoped_reliability, without_rotation_keys),
  RecursiveAllowance::{carve_child, ensure_available, claimed},
  ChildCapabilityManifest::{for_recursive_spawn, narrow_allowed},
  ParentAuthorityId::{as_uuid, decision_grant_id}, DecisionId::from_uuid,
  TurnDecisionService::settlement_ref, decision store ReplayChain /
  existing_shared_writer / existing_writer, SpawnDelegationStatus::from_grant,
  delegation::status/append_metadata, test route observer.

P2-R17 worker verification order
- Outer capability under T, then exactly one of ordinary_source/operator_source
  else typed WorkerManifestUnsupported (validate.rs and run.rs); the snapshot
  decoder maps undecodable (pre-strip) manifests to WorkerManifestUnsupported.

P2-R18 test written (not run): runtime::child_source::tests::every_child_kind_issues_without_decision_records.

Tests deleted (reason: exercise deleted delegated issuance):
- runtime::turn_decision::tests::child_process (module incl. g8_segment, route_binding,
  worker_probe) and recursive_budget (module)
- session_worker::runner::tests::authority (module), tests::probe (module; delegated
  parent-issued manifest), manifest_validation::worker_rejects_changed_config_generation_before_workspace_side_effects
- session_worker::runner::tests::credentials legacy no-source provider cells (13)
- tools::sessions_spawn::tests::child_authority (module: batch_v3_verifies_through_store_fallback,
  batch_v4_refuses_missing_parent_row, launch_v3_*, delegated child outcome/evidence cells)
- agent::turn_tests::engine_switch::{bound_child_context_keeps_scope_pin_and_grant} and the
  admit_bound_child_segment fixtures; turn_context::tests::child_turn_context_funds_recursive_money_only_through_the_parent_grant;
  admission_matrix::budget::segment_context_carries_budget; turn_decision::tests::execution::child_zero_budget_publishes_no_costed_direct_ref;
  admission_matrix::launcher_authority_cells
- tools::capability::child::recursive_tests recursive-spawn cells (3); providers credential
  rotation_keys_follow_the_root_connection; entrypoint worker cell
- decision store shared/store_preparation cells using removed lookups
- tests/session_worker_stdin_protocol.rs sealed_route_worker_sends_only_its_route_key_on_wire (delegated e2e)
Retargeted: shell_parity/tool_tiering assertions -> ordinary_child_publication;
recorded_chain/skill_receipts/turn_context -> terminal_settlement_ref; carve_child -> carve_ordinary_child.
Scanners: memory.rs/panic_policy.rs child_process entries, providers.rs worker factory site.

Acceptance A4: 188 lines / 50 files (1b87566) -> exit 1.
…nd terminal arms, scanners deleted

P2-R20: delete runtime/decision_journal/**, child_source/authority_journal.rs,
child_source/recovery.rs, the child outcome hook, runtime/causal_events* (sink,
repair, routes, worker), ingress_recovery/child_telemetry.rs, turn_decision
store_preparation, the turn_cleanup prepared-store drains and the service
store/durable_events/child_recovery fields. KeyDomain::{IngressRecovery,
ExplorationSeed} deleted (no user). load_key moves to turn_publication/builder.
Execution trace types move to tools/capability/execution_trace.rs.

P2-R21: IngressRecorder::prepare is repository-only; v1/v2 capsule arms,
legacy_key, LegacyEvidence, PreparedAdvisory, open_advisory_store,
recovery_only_legacy deleted; the SQLite scan selects only capsule version 3
in SQL. RecoveryScope::prepare: recordable = recording on && repository ready.
Test runtime::ingress_recovery::tests::legacy_capsules_are_ignored_without_key_reads.

P2-R22: DecisionSettlementRef, validate_durable_chain, PreparedReplay, the whole
agent/terminal/replay module (terminal_replay_v1), served_auxiliary.rs, the
recorded branches of finalize_turn_impl and recovery_commit::claim_semantic,
semantic_mac_v2/decision_id claim fields, OutcomeCorrelator/TerminalFacts
(causal_tree/outcome.rs), settlement_ref and the rejoin_terminal_inner legacy
branch deleted. Execution trace and close flag move onto TurnRuntimeContext.
Test agent::turn_tests::engine_switch::post_deletion_turn_commits_terminal_without_decision_rows
(cells a, b, b2, c, d).

Cascade (sole consumers deleted): causal_tree/{refusal, decision_select,
candidate_generator bridge}.rs, decision_engine/decision_score.rs, EngineDecision
reduced to decision_id/deadline/state/outgoing child, CandidateSet::NoLegalPath
payload, TurnDecisionContext hop/audit methods, DecisionId::parse and
CausalActionKind::ALL moved to cfg(test) test-support modules.

P2-R25: retire causal_domain, decision_seals, decision_terminals scanners and
their probes; AG-10 inverted (no production file opens a legacy decision
database); new decision_engine::no_legacy_decision_artifacts with planted
fixture; deleted paths dropped from memory, panic_policy, process,
retired_feature, NEUTRAL_HOMES.

P2-R26: plain_abstention_fixture and is_plain_answer_abstention already absent;
the delegate deadline test is already the no-decision variant.

G3 orphans deleted: security/approval_ledger/tests/historical_exact_call_approval/*.

Tests deleted (decision-only, D§2.6 or legacy store/replay): turn_decision tests
agent_decision, catalog_drift, enabled_service, execution, recorded_chain;
terminal_scope guard-link, disabled-store, fingerprint-reseal, recorded-rejoin
cells; ingress auth_02_2b_tests (plane-on store), auth_02_2a matrix/legacy
chain/outcome cells, authority_tests legacy MAC compatibility,
copied_workspace_terminals_verify_through_the_real_store,
ingress_recovery_unchanged_by_switch,
ingress_recovery_off_with_causal_tree_disabled_creates_no_key_or_store; chat
persist_restart legacy-seal, empty-store, missing-source, unauthenticated-marker
and queued-decision cells; causal_events tests; decision store timeout probes;
legacy generator approval-report assertions.

A5d: tests/architecture_boundaries/decision_engine.rs only. A5e: exit 1.
A5f: src/agent/turn_tests/engine_switch.rs only. audit_hops: exit 1.
A5a 1214/222 -> 752/174; A5b 708/153 -> 661/138; A5c 44 -> 20 files.
…xt; structural engine; delete causal_tree

P2-I2 check (G5a ruling): holds on 0d7c9b7. With a repository and
recording on (`MemoryFabric::records_recovery_terminal`), every terminal
commits through `recovery_commit::finalize_recovery_terminal`: E2 claim
MAC over the persisted semantic (OV-9), settled receipt facts (E5/E4) and
the cost outbox (E7) in one `commit_recovery_terminal`; restart rejoin
(`rejoin_terminal_inner` -> `load_settled_facts`) verifies the receipt,
the chain evidence, the semantic <-> payload comparison and E2/usage/outbox
(`restart_verify`). No restoration commit was needed.

G5B-R1/R2 (P2-R19): `TurnDecisionService` -> `turn_publication::service::
TurnRuntimeService`; `TurnDecisionContext` -> `turn_context::OpenedTurn`
(runtime, owner filesystem, actor, service id, main compaction, deadline).
Opening, admission (`admit_opened_context`), execution binding, surface
binding, child scope (+ `authority_within_policy`), call approval (gate:
`recordable`), owner filesystem move into `runtime/turn_context/`;
`verify_operator_spawn_source` into `child_source/publication/operator.rs`.
`has_decision_work` deleted (services always retained); `drain_advisory`.
`PublicationBuildError` -> `TurnRuntimeBuildError` (`ChildAuthority` ->
`WorkerSurface`). Holders: `channels/turn_source/{publication_slot,
admitted_turn}.rs`, `gateway/turn_source/publication_slot.rs`; field
renames `decisions`/`decision_slot` -> `publication_slot`,
`turn_decisions` -> `turn_service`, `publish_turn_runtimes`.
Fixtures deleted (evidence, selected-loader, ordinary-child); wrapper
calls unwrapped to `Box::pin(inner)` (keeps heap placement).

G5B-R3 (P2-R23): `src/causal_tree/` deleted. `admission_facts.rs` ->
`tools/surface_owner/facts.rs::PublicationFacts`. Engine is
structural-only: `admission_probe.rs` deleted, `GenerationError` moved to
`candidate_generator.rs`, `validate_structure`, no probe parameter,
approval-only/exact-call machinery and `approval_candidate`/
`collectable_approval_entry`/`names_entry` deleted; `CandidateSet::Legal`
keeps candidates + exact mentions. `CausalActionKind::RequestApproval`
deleted (its only constructor was `approval_candidate`).
`effective_approval_required` removed (state, canonical, projection).
`hash_candidate_set` keeps the empty approval-scope encoding.
Restart verifier: closed claim key set instead of naming legacy keys.

G5B-R6: AG-28 re-added as `turn_state.rs::trust_bearing_turn_state_is_
built_only_by_the_turn_opening` (+10 probes); loop-request scanner
renamed; compaction/tool_tables/process/providers/scanner_probes paths.

Tests moved: pinned_policy_version_is_order_independent_and_tracks_policy_
changes -> turn_publication::service::tests; discovery_surface,
skill_receipts -> turn_context::tests; terminal_scope ->
agent::terminal::tests. Restored (G5a had deleted them, G5b R1.2 keeps
them live): agent_budget_uses_pinned_route_and_read_only_spend_snapshot,
agent_hint_route_drift_is_refused_before_credential_resolution,
verified_native_tools_requires_matching_source_selection ->
turn_context::tests::publication_scope;
discovered_spec_with_changed_schema_still_fails_bind ->
turn_context::tests::execution_binding.
Added: candidate_generator::tests::{structural_set_rejects_duplicate_
candidate_ids, structural_set_requires_a_refusal_path,
structural_ordering_and_limits_are_preserved};
questions::tests::candidate_hash_separates_mentions_and_keeps_its_pre_
change_bytes (pinned literal).
Deleted (gate/approval annotation only): admission_probe::tests (all five
R14 tests), causal_tree::hard_gates_probe::legacy_generator_tests,
questions::tests::hard_gated_candidate_is_absent_from_compiled_request,
questions::tests::candidate_hash_separates_mentions_from_approval_scope
(replaced), surface_owner::tests::publication_rules_match_pre_strip_
direct_surface (HardGates parity). Rewritten: decision_advisory::tests::
engine_driver_analyses_the_structural_set (was ..._with_advisory_probe),
projection::tests::projection_renders_the_intrinsic_approval_requirement.
Renamed for A5a: four test names containing `turn_decision`; doctor
`ModelProbeOutcome` -> `ModelProbeVerdict` (B4 substring).
Local test struct engine_switch::OpenedTurn -> OpenedAgentTurn (B6).

Acceptance (before -> after, lines/files): A5a 752/174 -> 185/89, A5b
661/138 -> 652/132, A5c 20 -> 0 files, B1 228/79 -> 193/59, B2 349/90 ->
0, B4 209/50 -> 0, B6 = the three structs (enum comes with commit 2).
Every remaining A5a/A5b/B1 hit is the `[causal_tree]` config
(`config::schema::causal_tree`, `causal_tree.enabled/backend/scoring`),
i.e. P2-R24, which is blocked on a ruling (CausalPolicy destination).
…P2-R24)

Per the G5b/R24 ruling 2026-11-16:
- (1) `[decision_engine]` gains latency_budget_ms (u64, default 500, 50..=5000),
  token_ratio_limit (f32, default 0.35, finite > 0) and max_candidates
  (u8, default 12, 1..=32): today's causal_tree policy defaults and bounds.
  Readers moved: turn budget/deadline and token reservation
  (publication_scope), advisory and exact-mention caps (decision_advisory,
  child_scope), control ladder, lifecycle validation. The admin mutation
  allowlist (`engine_setting`) writes the three keys; SetPolicy/PolicyPatch/
  ResetPolicyToDefaults/SetEnabled deleted (no production constructor; the
  CLI enables through SetEngine(Enabled)). SetBackend raises a lower latency
  budget to the catalog recommendation and keeps a larger one.
- (2) No `[decision_engine.backend]` => the backend's serde default
  (static DEFAULT_BACKEND). migrate_backend, effective_backend,
  inherited_backend, empty_tables_only deleted.
- (3) durable_key deleted (service, plane status, control ladder). recordable =
  OV-13 (RecoveryScope.recordable: recording on AND activated repository) in
  build_publication and the operator process; factory keys are the persisted
  runtime key. Plane-gated refusals (EventStoreUnsupported,
  InboundEventsDisabled, operator worker recording, source "requires durable
  input" ensures, accounting-scope gate, auxiliary gate) removed; auxiliary
  receipt durability follows MemoryFabric::records_recovery_terminal.
  Console recovery/approvals/projection follow console_recordable (OV-13).
  with_process_recovery(true) (chat: controlled.is_none()).
- (4) decision_latency_budget_warning and extra_latency_budget_ms_explicit
  deleted with their tests; no replacement warning.

Deleted: config/schema/causal_tree.rs (+ migrate_legacy,
normalize_regeneration_document, CausalPolicy, Calibration/Rehearsal/
Scoring/Store/Evaluation configs), causal_tree_config_tests.rs, the init
causal regeneration guard, config/fixtures/agent-full-base-8a5d160d.toml,
deprecations::file_holding, the [causal_tree] init template block. Backend
types moved to config/schema/decision_engine/{backend,backend_kind}.rs
(CausalConfigError -> BackendConfigError, messages name decision_engine.*).
A [causal_tree] table falls under the loader's unknown-path refusal; no
warning, importer or alias. Risk classification, secret paths, config.patch
guard, provider uses and reference_use retarget to [decision_engine].
Control-ladder layer renamed decision_engine.

Tests deleted (legacy only): causal_tree_config_tests::*, causal_tree.rs inline
tests, decision_engine_config_tests legacy-copy/W1-W3 cells,
init::the_base_full_template_disable_reads_as_unset,
persistence::explicit_hosted_latency_ceiling_loads_without_an_upgrade_warning,
persistence::schema_one_hosted_backend_warns_on_load_..., paths::load_path_treats_
the_pre_catalog_serialized_latency_default_as_unset,
defaults_and_integrations::hosted_latency_warning_does_not_change_the_configured_ceiling,
mutation::set_backend_preserves_an_explicit_latency_budget_equal_to_the_default,
mutation::policy_patches_are_explicit_and_validated.
Tests added/rewritten: decision_engine_config_tests::{turn_limits_have_their_defaults_and_bounds,
a_causal_tree_table_is_an_unknown_path, absent_engine_backend_is_the_serde_default,
effective_backend_reads_the_engine_table}, mutation::{turn_limits_are_engine_settings_and_validated,
set_backend_raises_only_a_lower_latency_budget}, config.patch/POST /api/config/generation
round trips on [decision_engine]. Plane fixture params removed or mapped to
memory.events.record_user_messages where the cell is recorded vs unrecorded.
Scanners: causal_policy -> writers of config/schema/decision_engine*, chain
`.decision_engine`; decision_backend loopback path; probes retargeted.
tests/fixtures/config-schema.json updated by hand (causal_tree removed, three
keys added).

Acceptance (817ee4e -> this): A5a 186/90 -> 0 (exit 1); A5b 653/133 -> 0
(exit 1); B1 193/59 -> 0 (exit 1); A5c 0 files.
…rity; relaunch removed

G5B-R4 (recursion parity, production) and G5B-R5.3 (relaunch removal).
The R4.8 in-crate cells and the R5 real-parent worker harness follow in
the next commit.

R4:
- turn_context/child_scope.rs: the ordinary root's sealed parent set is
  every permitted catalog entry (operator-root rule). The
  allow_recursive_delegation capability_search capture and the
  delegator-only capture are deleted; depth, inherited branch and the
  allocation-free early return are unchanged.
- child_source/publication.rs: OrdinaryChildPublication = { inner, scope }.
  PartialEq compares both halves; new same_parent, with_recursive_delegation
  (ForeignParent, NoDelegator, DepthExhausted, Manifest, Caller) and
  verify_derived_from. verify_caller/verify_live/dispatch_pin/allows_tool/
  verify_task_scope/allocate_child_budget read the selected scope.
- tools/execution.rs: ChildCallContext::with_ordinary_publication re-scopes
  the caller only onto an object sharing its authenticated parent.
- sessions_spawn: refuse_recursive_request -> requested(args) (booleans
  only); spawn re-scopes the caller when allow_delegation is true and the
  caller carries an ordinary publication; operator sources are unchanged.
  Schema text rewritten; "recursive grants permit one child" deleted;
  goldens updated.
- SessionsSpawnConfig.{allow_recursive_delegation, max_recursive_depth} and
  config/schema/recursive_spawn.rs deleted (no deprecation or warning; the
  parser's unknown-key behaviour applies). Default moves to agent.rs.
  h2_entry "refuse recursive process delegation" check deleted.
  tests/fixtures/config-schema.json edited to drop both keys. Docs
  (configuration.md, tools.md) state the parity rule.

R5.3 (FU-PROCESS-RELAUNCH):
- ChildSessionRegistry::{relaunch, relaunch_record, relaunch_worker} and
  RelaunchIdentityError deleted. RelaunchRecipe stays as mailbox record
  data; ExecRef stays (mailbox producers).

Tests renamed:
- tools::sessions_spawn::delegation::tests::
  recursive_request_is_refused_without_a_decision_issued_grant ->
  recursive_request_parses_only_booleans (true is now accepted).
Tests deleted (reason: relaunch removed):
- chat::sessions::child::tests::{relaunch_keeps_session_addr_and_mints_new_run,
  concurrent_history_insert_and_relaunch_keep_both_updates}
- memory::sqlite::tests::mailbox::{send_before_and_after_task_reopen_uses_same_session_addr,
  run_pinned_message_nacks_after_relaunch}
- tests/session_worker_stdin_protocol.rs::{relaunch_real_worker_keeps_session_addr,
  relaunch_refuses_manifest_identity_mismatch_before_record_update,
  relaunch_scope_key_separates_session_and_agent_and_is_stable}
Tests trimmed (relaunch steps removed):
- child_registry_is_excluded_from_picker_resume_and_latest (newest activity
  is now an insert)
- relaunch_and_insert_preserve_complete_child_record ->
  insert_preserves_complete_child_record
- child_sessions_owner_and_agent_erase_remove_records_and_block_relaunch ->
  child_sessions_owner_and_agent_erase_remove_records
Tests deleted (reason: recursion-only vetoes and config keys deleted):
- config::schema::recursive_spawn::tests::{recursive_depth_two_is_refused_at_the_configuration_boundary,
  recursive_spawn_permission_is_default_off_and_requires_positive_depth}
- the allow_recursive_delegation cell of
  h2_entry::profile::tests::strict_profile_refuses_executable_and_remote_factories_before_writes

Acceptance (36f6ba1 -> this): B3 1 -> 0 files; B5 24/7 -> 0 (exit 1);
B6 3 -> 4 files; B7 35/11 -> 0 (exit 1). A5a/A5b/B1/B2/B4 stay exit 1,
A5c prints nothing.
…rsion cells

Parity default (controller ruling "G5b c2a ruling 2026-11-14"): an omitted
`allow_delegation` on sessions_spawn now means true. Every ordinary child
gets the derived recursive publication by default; only an explicit `false`
keeps the captured, non-delegating scope.
- delegation::requested: None | true -> true; false -> false; other values
  are still refused ("allow_delegation must be a boolean").
- OrdinaryChildPublication::select_for_spawn(caller, recursive): the derived
  scope, or the captured object itself for an opt-out. spawn.rs re-scopes the
  caller through it and ChildCallContext::with_ordinary_publication.
- Schema text "default true ... Set false for a child that cannot spawn",
  `default: true`; both sessions_spawn goldens updated; docs/configuration.md
  and docs/tools.md describe the default.

Tests (written, not run locally):
- runtime::child_source::tests::recursive_derivation_shares_the_authenticated_parent
  (a) derived keeps sessions_spawn at depth parent-1, seal verifies,
  same_parent && !=; (b) a second bound turn's publication -> ForeignParent and
  verify_derived_from refuses; (c) a depth-1 parent -> DepthExhausted;
  (d) select_for_spawn(false) is pointer-equal to the captured object.
- tools::sessions_spawn::tests::recursive::{task_child_runs_under_the_derived_publication,
  full_root_and_child_spawn_without_grant, supervised_child_follows_root_dispatch_approval
  (layer 1 only, see report), read_only_root_spawn_is_refused_as_without_recursion,
  root_without_delegate_derives_a_recursive_child,
  exhausted_allowance_spawns_a_zero_funded_child,
  omitted_flag_child_spawns_until_the_depth_bound}.
- recursive_request_parses_only_booleans: an omitted flag is now true.
- task_mode_no_tools_preserves_single_turn_trace_usage: its scripted spawn now
  passes allow_delegation=false (the cell asserts a tool-less child).
… strategy; omitted flag falls back at the bound

Ruling (1), Supervised child approval parity: a task child's turn adapter
dispatches its published execution under the run's production
AgentToolApprovalStrategy (run approval manager + resolver from the
SpawnApprovalResolverFactory), as a root turn does. Without a resolver it
refuses with the same literal as the root. ToolExecutionService::with_approval
(promoted from the cfg(test) with_test_approval) swaps only the strategy.
Layer 2 of supervised_child_follows_root_dispatch_approval added.

Ruling (2), omitted-flag fallback: DelegationRequest { Omitted, Explicit }.
An omitted allow_delegation that hits a bound (NoDelegator, DepthExhausted,
NotStrictSubset) issues a non-delegating child, as false does; integrity
refusals are never masked; explicit true keeps the typed refusal. Chain cell
and derivation cell (c) updated; schema text, goldens and docs updated.
…estored worker tests

R5.1: tests/ordinary_process_worker.rs with helpers parent.rs, provider.rs and
scan.rs. Real `prx chat --plain` parents (CARGO_BIN_EXE_prx) run with a cleared
environment, a process-local 0700 HOME and
OPENPRX_SESSION_WORKER_SECRET_PATH=<home>/session_worker.secret, shared by the
workers and a restarted parent (no SESSION_WORKER_SECRET, no production seam).
Named bounds: MAX_REQUEST_HEAD_BYTES 64 KiB, MAX_REQUEST_BODY_BYTES 1 MiB,
REQUEST_READ_TIMEOUT 3 s, MAX_CAPTURED_REQUESTS 256, MAX_PARENT_OUTPUT_BYTES
4 MiB per stream, PARENT_DEADLINE/WORKER_DEADLINE 180 s, HOLD_WAIT 30 s,
ACCEPT_POLL 5 ms, scan caps 64 MiB/4096 files. stdout/stderr drained on two
threads; Drop guards kill and reap the parent, release holds, stop and join
the server (bounded).

R5.2 (moved from the in-crate probe and P2-R18 cells, now in tests/):
session_worker_persists_no_secret_from_a_reply,
session_worker_persists_no_secret_from_a_provider_error (sentinel
AKIAIOSFODNN7EXAMPLE, pinned bodies), process_child_issues_without_decision_records,
batch_children_issue_without_decision_records,
restarted_parent_recovers_and_verifies_ordinary_launch.

R5.4: sealed_route_worker_sends_only_its_route_key_on_wire restored from
74e193f^ with P4 (opt-in succeeds), P5 (ordinary-source scope checks, pins
removed), P7 (fan-out with funding conservation) and the ordinary refusal
literals; forged capabilities are signed with T read from the secret file.
nextest: the new binary takes two slots and up to 180 s.
P2-R27: the decision inspector page is deleted; DecisionPlanePage becomes
IntentAnalysisPage (status, effective backend, last 20 analyses from
/api/decision-engine/*, write-only credential). api.js moves to
/api/decision-engine/* and drops the causal calls and the deleted approval
resume route; the approval panel keeps approve, reject and revoke. console/dist
is rebuilt.

P2-R28: docs/decision-plane.md is merged into docs/decision-engine.md with the
fresh-workspace and 410 notes; every backlink and remaining mention of
[causal_tree], the deleted CLI and routes, relaunch and the recursive keys is
retargeted to the end state, including the new [decision_engine] turn limits
in docs/configuration.md. CHANGELOG records the breaking removals and
default-on recursive delegation.

The shared worker harness no longer needs #[allow(dead_code)]: the restart
controls move to tests/ordinary_process_worker/restart.rs and harness faults
answer through Reply::Raw.

A6: 12/3 before -> only the 410 note in docs/decision-engine.md.
A7: 13/8 before -> exit 1.
New test files carry #![cfg(test)] so the scanners read them as test code;
the restart tests become the agent::terminal::tests::restart module instead
of an include!; the status DTO field is engine_status (serialized as
decision_engine) so the policy scanner sees no config write; the provider
label guard follows the claim redaction into recovery_commit.rs; the legacy
artifact scanner gets a planted probe; golden_vectors.rs and the restart
cells' brain.db edits are listed.
…ckend selection never changes latency_budget_ms

F1: engine_setting matches the exact P2-R8 allowlist first, so prompt_budget_tokens
and token_ratio_limit are accepted; unknown and credential-bearing keys are refused.
CLI (tests/cdp_cli.rs) and HTTP (config_apply_accepts_allowlisted_token_keys) success tests.
F6: a selection whose catalog latency recommendation exceeds the configured budget is
refused with CausalMutationError::LatencyBudgetBelowRecommendation naming both values,
engine on or off. set_backend_raises_only_a_lower_latency_budget replaced by
set_backend_never_changes_the_latency_budget.
…le row and stops with process shutdown

F2: the inline path re-arms whenever it leaves a pending/error row (Err or Error state);
a failed, cancelled or deadline-cut pass is never exhaustion and retries on the back-off;
only an exhausted scan with no retryable metered row idles. Re-arm uses an epoch plus
notify_waiters, so a re-arm during a pass is not lost.
F8: CostOutboxProjector::start takes the process shutdown token (child token) at the
gateway, channels and chat entries; the entry owns the handle and awaits stop(),
bounded by the 15 s pass deadline; passes stop at the next row on cancel.
F7: bounded ledger read and sync_channel(1) in cost_recovery tests.
Tests: background_scheduler_rearms_after_empty_pass_and_retries_transient_failures,
failed_or_incomplete_passes_are_retried_on_the_back_off,
projector_ends_with_the_process_shutdown_token.
…retained settlement evidence

F3: a failed, cancelled or refused turn records the cost of the metered usage retained
in RetainedProviderTrace, the evidence the terminal settles from. Real-turn test
ended_turns_record_the_settled_metered_cost (metered round, then failure / cancellation /
send refusal; 2 000 000 micro-USD each).
… for_child bytes against literals

F4: golden_constraints_and_effective_config_payloads compares the full production payload
with prefix literal + golden_effective_config.json (sorted compact JSON from an independent
encoder) + ']'; the seal is a pinned literal recomputed with ring over the literal.
for_child_fingerprint_encoding_is_unchanged pins the SHA-256 of the literal preimage.
… with WorkerManifestUnsupported

F9: parse_worker_manifest_line maps an inline decode failure to WorkerManifestUnsupported;
inline_pre_strip_manifest_is_worker_manifest_unsupported asserts the type through the reader.
F7: sync_channel(1) in transport_key tests.
…recommendation explicitly

F6 follow-on ruling: setup and onboarding (DecisionSetup::check, check_hosted, apply,
apply_hosted) lead the plan with latency_budget_ms = the catalog recommendation when the
written budget is lower (with_recommended_latency_budget), in the same transaction. The
plain mutation API keeps refusing. Test:
decision_engine_cli::tests::wizard_hosted_openrouter_writes_the_recommended_latency_budget.
…roduction skills_manage

F5 ruling: (b) wiring for publish, annotate, rollback and retire: production
SkillsManageTool execution on a temporary lifecycle repository; approved reaches the
lifecycle service past consume, verify and the exact grant; denied is never admitted;
a repeat is refused at consume. (a) end to end for annotate on the experiments fixture:
approve writes exactly one adoption record, deny none, repeat nothing more.
Test support: h2_entry::test_support::ReviewedGateway and profile::tests::{profile_over,
origin_of} (test-only). Added revoked_launch_cannot_approve_or_admit (exact digest only;
a revoked launch neither approves nor admits). No skills behaviour, action or test removed.
…ery backend selection entrypoint

The catalog latency_budget_ms recommendation is checked once on the resulting
backend selection in CausalConfigService::mutate, whenever a mutation selects
the backend (SetBackend, ResetBackendToAuto, the backend.kind/endpoint/model
keys of config set and the Gateway), with the engine on or off. Setup keeps
writing the budget explicitly. CLI and HTTP refusal tests cover SetEngine,
both enabled states, an unchanged stored config, and success after raising
the budget. cdp_cli reads config.toml under a named byte cap.
…s instead of sleeping

A cfg(test) PassObserver field reports each scheduler pass on a bounded
channel. The test waits (bounded) for the exhausted startup pass and a failed
scheduled projection, only then clears the ledger failure, and asserts the
acknowledging pass and exactly one ledger entry.
… TerminalNotReplayable

load_settled_facts normalizes every D§4.4 failure (receipt authentication,
facts parse, identity mismatch, evidence, a non-settled receipt, a repository
store without recovery authority) to TerminalNotReplayable, keeping the cause
in the chain. A missing receipt stays LegacyUnconfirmed. The negative restart
cells assert the type; the receipt-tampering cells lift and restore the
immutability trigger, and the prepared-parameters cell is non-vacuous.
… task ledger, console transcript, quarantine, approval gate

- sessions_spawn derives a recursive scope only when the child's own
  executor and surface can run sessions_spawn (else NoDelegator: an omitted
  flag falls back, an explicit true is refused) (G5b c2a/2b rulings)
- a child publishes manifest members directly unless capability_search is
  on its own surface (in-process delegate/task children, P2-R15)
- task-mode child executor keeps the parent's idempotency ledger
- recorded console turns project the committed assistant event into the
  transcript (the deleted replay projection did this)
- restart scan quarantines TerminalNotReplayable / LegacyUnconfirmed
  terminals instead of failing startup (D§4.4, P2-R22 cell d)
- approvals: recording off -> 409 disabled; no recovery repository -> 503
  (OV-13)
- worker manifest: structural checks before the P2-R17 source steps
- ordinary lineage refusal names the differing fields
…aviour

Rewritten (class a): decision-store reads -> no-store assertions (P2-R20,
P2-I1); enabled-plane receipt rule -> unrecorded proof carries no source
event (P2-R22); projector replaces scope-prepare projection (P2-R5);
repository-less backend gets no recorder (P2-R21); [causal_tree] fixtures
-> [decision_engine] (P2-R24); runtime key path (OV-2); analysis rows
precede the outcome (P2-R10); execution_trace null in terminals (D§4.2);
enabled-helper children use the production provider selection (P2-R15).
Deleted: doctor_reports_a_below_catalog_hosted_latency_budget (R24
ruling 4: the warning and its tests are deleted).
@g1e2x87
g1e2x87 merged commit 614280a into main Nov 14, 2026
32 checks passed
@g1e2x87
g1e2x87 deleted the feat/decision-engine-strip-p2 branch November 14, 2026 19:26
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant