97%

Section 04: Recursive + Closures + Drop AUGMENT + Value Empty-Burden

Empirical grounding: the value-shape composition cases stress BurdenSpec beyond plain owned aggregates — bug cluster BUG-04-090/095/098/104/106/107; closures are the most-stressed shape (proposal §Closures). BUG-04-099 is orthogonal (diagnostics/observability) per 00-overview.md orthogonal_bugs.

Semantic invariant (proposal §Recursive and Self-Referential Types, §Closures, §Drop Trait Interaction, §Value Trait Composition): compiled_drop is invoked from the refcount-zero branch of ori_rc_dec, NOT a direct call at every release site — rc > 1 release decrements without drop-body invocation, preserving shared-reference correctness.

Transport contract — SHIPPED PRIMITIVES BEING WIRED, NOT INVENTED. §04 does NOT design a new drop-function transport ABI. The runtime ABI ori_rc_dec(data_ptr: *mut u8, drop_fn: Option<extern "C" fn(*mut u8)>) is shipped at compiler_repo/compiler/ori_rt/src/rc/mod.rs:240. The per-type drop function generator generate_drop_fn → DropKind::{Fields, ClosureEnv, Enum, Collection, Map} is shipped at compiler_repo/compiler/ori_llvm/src/codegen/arc_emitter/drop_gen.rs:40-133. The closure RC-dec path that loads drop_fn from env_ptr field 0 is shipped at rc_ops.rs:256-287. The UserBurdenSpec.compiled_drop: Option<FnSym> + user_drop: Option<FnSym> fields are shipped at burden.rs:47-48. §04 wires these primitives for the four composition cases — recursive cycles (population at registration time), closure environments (population at closure-type-registration time), Drop trait AUGMENT (user_drop population at impl registration time + AUGMENT-shape drop body), Value empty burden (empty UserBurdenSpec for Value-marker types).

Reference implementations:

  • Lean 4 IR/RC.lean per-type drop call shape (§04.1).
  • Rust drop glue (compiled per-type drop functions) — RC.lean + Borrow.lean analogous shape.
  • Roc compiler/gen_refcount/refcount_proc.rs for closure capture handling.

Depends on: §03A is the SOLE direct predecessor edge in frontmatter depends_on: [03A] per INV-19 strict-linear-single-branch (§03A is status: complete, reviewed: true — reachable). §01 (UserBurdenSpec.compiled_drop + user_drop fields), §02 (variant_burden composition + structural dedup), §03 (BurdenInc/BurdenDec IR variants + trivial-emission walker + partial-move bitset machinery) are TRANSITIVE predecessors reached THROUGH §03A’s own depends_on chain — consumed by §04’s code but NOT additional direct DAG edges (the goal-prose “Consumes §01/§02/§03/§03A” names the consumed deliverables, not the direct-edge set).


Intelligence Reconnaissance

Queries (executed 2026-05-17 during §04 Step 5 editor pass):

  • scripts/intel-query.sh --human file-symbols "compiler_repo/compiler/ori_llvm/src/codegen/arc_emitter/drop_gen.rs" --repo origenerate_drop_fn, emit_drop_fields, emit_drop_enum, emit_drop_collection, emit_drop_map (caching via drop_fn_cache)
  • scripts/intel-query.sh --human file-symbols "compiler_repo/compiler/ori_rt/src/rc" --repo oriori_rc_dec(data_ptr, drop_fn), ori_rc_inc, rc_dec_to_zero, rc_dec_to_zero_st, MAX_REFCOUNT (immortal sentinel)
  • scripts/intel-query.sh --human callers ori_rc_dec --repo ori — closure-aware emission path at rc_ops.rs::emit_rc_dec_closure + protocol-builtin emission at iterator/state.rs
  • scripts/intel-query.sh --human similar drop_glue --repo rust,lean4,roc --limit 5 — Rust’s per-type drop functions, Lean 4’s RC.lean shape, Roc’s closure-capture handling

Authoritative graph state at run-time: STALE (verified all path:line citations against actual files in this pass).

Result summary: existing AIMS + LLVM codegen infrastructure consumed; §04 extends the unified model per [ori].claude/rules/missions.md §AIMS invariant 5 (no parallel paths, no shadow trackers). All four §04 subsections wire primitives from §01-§03 + [repo:compiler_repo/compiler/ori_llvm/src/codegen/arc_emitter/drop_gen.rs] + [repo:compiler_repo/compiler/ori_llvm/src/codegen/arc_emitter/rc_ops.rs] + [repo:compiler_repo/compiler/ori_rt/src/rc/mod.rs] ori_rc_dec ABI.

Subsection independence. §04.1/§04.2/§04.3/§04.4 are four independent algorithmic surfaces (SCC recursive-drop, closure composer, Drop validator + AUGMENT, Value empty-burden), each landing on its own register_*/compose_* site with its own test suite — none consumes §04.1’s SCC-partition output. §04.2 and §04.3 share the mint_compiled_drop_fn_sym FnSym-mint helper (assigns the _ori_drop$<idx_raw> mangling key) as a sibling utility, NOT a produced-output dependency. The four subsections therefore carry NO subsection_depends_on edges and complete in any order. The section-level gate blocked_by: is now []: EVERY former blocker is CLOSED on disk — BUG-04-043 (recursive tagged-pointer box-and-load codegen) resolved, BUG-04-125 (Drop-AUGMENT invoke+landing-pad AOT slice) resolved, BUG-06-005 (prelude Value+Drop registration) obe/superseded, BUG-02-033 (lambda-side infer_lambda closure-UserBurdenSpec wiring) resolved — all under bug-tracker/plans/completed/. The formerly-anchored AOT/spec slices are therefore ACTIONABLE, not deferrable; the misattributed BUG-04-119 (imported generic monomorphization) was always orthogonal per 00-overview.md orthogonal_bugs and is removed from §04’s forward anchors. §04.R and §04.N retain subsection_depends_on edges as genuine close-out gates (review + checklist depend on all subsections’ content).


04.1 Recursive types + SCC-based cycle detection + per-type compiled drop-glue + ori_rc_dec invocation chain

Files:

  • compiler_repo/compiler/ori_types/src/registry/burden_compose/scc/mod.rs — cycle detection + compiled_drop fn_sym reservation
  • compiler_repo/compiler/ori_arc/src/drop/mod.rsDropInfo construction for recursive types
  • compiler_repo/compiler/ori_llvm/src/codegen/arc_emitter/drop_gen.rs — existing generate_drop_fn consumed; new code path for recursive-type DropKind::Fields with self-referencing child decs
  • compiler_repo/compiler/ori_rt/src/rc/mod.rs:240ori_rc_dec(data_ptr, drop_fn) runtime ABI consumed unchanged

Design — Cycle detection algorithm

  • Implement SCC analysis at BurdenRegistry registration time over ALL type references reachable from UserBurdenSpec (per compiler_repo/compiler/ori_types/src/registry/burden.rs:40-49 shape — verified: fields are owned_fields: Vec<UserOwnedField>, borrowed_fields: Vec<UserBorrowedField>, variant_burdens: Vec<UserVariantBurden>, element_burden: Option<Idx>): (a) owned_fields[i].field_type (struct field type Idx), (b) borrowed_fields[i].field_type (only when borrow extends parent’s lifetime — same SCC contribution), (c) element_burden when Some(idx) (List/Map/Set element type Idx), (d) variant_burdens[i].retained_owned[j].field_type (enum variant retained owned fields — each variant’s UserVariantBurden carries retained_owned: Vec<UserOwnedField> per burden.rs:37). Algorithm: Tarjan’s SCC (iterative; matches Rust’s petgraph::algo::tarjan_scc shape — single DFS, O(V+E)). Input: ori_types::registry::burden_compose::compose_user_burden_spec collects every type’s reachable type-id edges across (a)-(d). Output: Vec<Vec<Idx>> SCC partition. Examples: self-loops (type Node { next: Option<Node> }) form singleton SCCs via owned_field through Option’s element_burden; mutually-recursive types (type Tree { f: Forest } + type Forest { t: [Tree] }) form multi-member SCC via owned_field + list element_burden; recursive enums (type Tree = Leaf | Branch(Tree, Tree)) form singleton SCC via variant_burdens[Branch].retained_owned[].field_type. Shipped at compiler_repo/compiler/ori_types/src/registry/burden_compose/scc/mod.rs::compute_scc_partition.
  • For every SCC with |members| >= 2 OR self-loop: mint ONE FnSym PER MEMBER via the FnSym mint API (existing infrastructure used by §02.4 compiled_drop machinery); each member’s UserBurdenSpec.compiled_drop = Some(<member-specific-fn_sym>). The mangling _ori_drop$<idx_raw> (per drop_gen.rs:45) keys on type Idx, so two SCC members yield two distinct FnSyms even though they share an SCC. Reservation order: mint ALL member FnSyms FIRST (drop_fn_cache.insert(ty_i, func_id_i) for each SCC member), THEN generate bodies (cross-references resolve via cache hits regardless of intra-SCC order — a mutually-recursive SCC is cyclic and has no topological ordering among its members; the pre-allocation makes body generation cycle-safe in any order). Shipped at scc/mod.rs::mint_compiled_drop_fn_sym + scc/mod.rs::assign_compiled_drop_fn_syms.
  • For singleton SCCs without self-loops (non-recursive types) WITHOUT Drop impl: compiled_drop = None — default drop-glue path via existing compute_drop_info machinery handles non-recursive emission. EXCEPTION — non-recursive types WITH Drop impl (user_drop = Some(_) per §04.3) MUST also mint compiled_drop = Some(<fn_sym>): the user @drop body needs an entry point invoked by ori_rc_dec at refcount zero, and the AUGMENT-shape compiled drop function wraps user_drop then walks fields in reverse order. Decision rule: compiled_drop = Some(_) iff (in non-singleton SCC) OR (self-loop) OR (user_drop = Some(_)). Shipped at scc/mod.rs::needs_compiled_drop.
  • Test Strategy: tests at compiler_repo/compiler/ori_types/src/registry/burden_compose/scc/tests.rs. Matrix axes (per tests.md §Matrix Testing Rule — 4 axes × 2 polarities = 8 minimum pin count):
    • (a) Self-loop singleton — positive: recursive_singleton_node_assigns_compiled_drop_fn_symtype Node { next: Option<Node> }UserBurdenSpec.compiled_drop == Some(fn_sym).
    • (a) Self-loop singleton — negative: non_self_loop_singleton_leaves_compiled_drop_nonetype Leaf { value: int } (no self-reference) → compiled_drop == None.
    • (b) Mutually-recursive pair — positive: mutually_recursive_pair_each_carries_distinct_compiled_drop_fn_symtype Tree { children: [Forest] } + type Forest { trees: [Tree] } → each member carries its OWN distinct FnSym in compiled_drop (per _ori_drop$<idx_raw> mangling); pin asserts tree.compiled_drop != forest.compiled_drop.
    • (b) Mutually-recursive pair — negative: non_recursive_pair_of_types_leaves_compiled_drop_nonetype A { b: B } + type B { x: int } (B has no back-edge to A) → both compiled_drop == None.
    • (c) Mutually-recursive triple — positive: mutually_recursive_triple_each_carries_distinct_compiled_drop_fn_symtype A { b: B } + type B { c: C } + type C { a: Option<A> } (cycle A→B→C→A) → all three members carry distinct FnSyms; pin asserts a.compiled_drop != b.compiled_drop != c.compiled_drop (three distinct values).
    • (c) Mutually-recursive triple — negative: non_recursive_triple_leaves_compiled_drop_nonetype A { b: B } + type B { c: C } + type C { x: int } (linear chain, no cycle) → all three compiled_drop == None.
    • (d) Non-recursive baseline — positive: non_recursive_pair_leaves_compiled_drop_nonetype Pair { a: int, b: str }compiled_drop == None (negative-form pin asserting absence of compiled_drop is itself the “positive” assertion for the negative axis).
    • (d) Non-recursive baseline — negative: non_recursive_with_recursive_field_assigns_compiled_drop_fn_symtype Wrapper { inner: Node } where Node is recursive — Wrapper itself is non-recursive but its OWNED field is; pin asserts Wrapper does NOT inherit compiled_drop from Node (Wrapper drops Node via field walk, not via shared compiled_drop FnSym). Negative pin for axis (d) — confirms the cycle-detection is per-type, not transitive through field types.

Codegen — drop body emission for recursive types

  • At LLVM codegen, when UserBurdenSpec.compiled_drop = Some(fn_sym) is encountered, the existing generate_drop_fn(emitter, ty, drop_info) at drop_gen.rs:40 materializes the function. No new emission path required — the existing DropKind::Fields(fields) arm walks RC’d fields, recursing through emit_rc_dec per field; the cache-before-body pattern at drop_gen.rs:69 (drop_fn_cache.insert(ty, func_id) BEFORE generating body) is cycle-safe — recursive types calling emit_rc_dec on a field of the same type get the cached FunctionId back, producing a tail-recursive drop pattern. Verified by recursive_node_drop_fn_emits_self_referencing_rc_dec at arc_emitter/tests.rs.
  • Mutually-recursive types: each SCC member gets its OWN compiled_drop FnSym (per-type drop function), matching the shipped _ori_drop$<idx_raw> mangling at drop_gen.rs:45 and the type-Idx-keyed cache drop_fn_cache.insert(ty, func_id) at drop_gen.rs:69. Cycle safety comes from the cache-before-body pattern (line 116 above), NOT from multiplexing multiple types into a single function. When _ori_drop$Tree walks a field of type Forest, emit_rc_dec looks up _ori_drop$Forest in the cache — yielding either the already-materialized FunctionId (cycle resolved) or a freshly-reserved one (recursion will resolve on next call). Reservation order during SCC processing: reserve all member FnSyms via drop_fn_cache.insert(ty, func_id) BEFORE generating any body, then generate bodies in any order (a cyclic SCC has no topological ordering among its members; cross-refs resolve via cache hits regardless of order); this preserves the existing extern "C" fn(*mut u8) ABI without runtime type discriminants. Verified by mutually_recursive_tree_forest_drop_fns_cross_reference at arc_emitter/tests.rs.
  • Test Strategy: tests at compiler_repo/compiler/ori_llvm/src/codegen/arc_emitter/tests.rs. Pin shapes: recursive_node_drop_fn_emits_self_referencing_rc_dec (positive — Node { next: Option<Node> } drop body contains ori_rc_dec call on field next); mutually_recursive_tree_forest_drop_fns_cross_reference (positive — _ori_drop$<Tree> calls _ori_drop$<Forest> via field decrement); drop_fn_cache_prevents_infinite_generation (positive — generating Node drop fn twice keeps the cache entry stable and emits exactly one LLVM definition).

Phase 5 emission integrity

  • Phase 5 emission for recursive types is IDENTICAL to non-recursive: BurdenDec(v) at release sites per aims-rules.md §8 RL-2. The recursion lives in compiled_drop body invoked by runtime ori_rc_dec when refcount = 0, NOT in emission. This preserves shared-reference correctness (rc > 1 release decrements via the existing rc_dec_to_zero early-return at mod.rs:281 without invoking drop_fn). Verified by no changes to §03’s trivial-emission walker (unchanged code path) + all 1006 ori_types tests + 1385 ori_arc tests + 652 ori_llvm lib tests pass.
  • Test Strategy (UNBLOCKED — BUG-04-043 now CLOSED): AOT-level test at compiler_repo/compiler/ori_llvm/tests/aot/recursive_drop.rs SHIPPED at the file location. VERIFIED 2026-05-31: the three pins (recursive_drop_node_chain_builds_runs_no_leak, recursive_drop_aliased_no_double_free, recursive_drop_chain_rc_balanced) PASS green (ORI_VERIFY_ARC=1, release, 3/3) once BUG-04-043’s box-and-load landed. BUG-04-043 (recursive Construct/Project box-and-load codegen, surfaced as build_struct: insert_value failed (index out of bounds?) index=0 num_fields=0) is CLOSED-as-resolved (bug-tracker/plans/completed/BUG-04-043/); the recursive-drop AOT slice is unblocked. The three shipped TDD pins (recursive_drop_node_chain_builds_runs_no_leak, recursive_drop_aliased_no_double_free, recursive_drop_chain_rc_balanced) carry NO #[ignore] and now run as active AOT tests — they were authored as RED-before-fix pins that pass once BUG-04-043’s box-and-load codegen lands. Ori source:
    type Node { value: int, next: Option<Node> }
    @t tests _ () -> void = {
        let n3 = Node { value: 3, next: None };
        let n2 = Node { value: 2, next: Some(n3) };
        let n1 = Node { value: 1, next: Some(n2) };
        // n1 dropped at scope exit; recursive drop traverses n1 → n2 → n3
    }
    Verification: ORI_CHECK_LEAKS=1 ./target/debug/aot_test_recursive_drop reports zero leaks; ORI_TRACE_RC=1 shows three matching alloc/dec pairs (one per Node). Eval/LLVM parity: cargo run -- run recursive_drop.ori produces same observable behavior.
    • (deferred-with-anchor: BUG-02-032) Shared-reference pin (AUTHORED — RED) — named test recursive_drop_skips_body_when_rc_above_one SHIPPED at compiler_repo/compiler/ori_llvm/tests/aot/recursive_drop.rs with the full spec-faithful body + both verification axes as real assertions, gated #[ignore = "BUG-02-032: ..."]. NON-CHECKBOX deferred-with-anchor item — does NOT gate §04.1 close (test-disposition discipline + state-discipline.md §1): §04.1’s recursive-drop-glue deliverable is verified by its 3 active green pins (node-chain / aliased / chain-rc-balanced); this rc-above-one precision pin auto-greens with NO edit when BUG-02-032 lands. The recursive-codegen blocker BUG-04-043 is CLOSED, but the pin’s Ori source requires the drop_early prelude builtin, which is documented in spec/08-types.md §8.10.5 + ori-syntax.md §Prelude but NOT implemented in the compiler (typeck signature + eval semantics + AOT early rc_dec codegen all absent) — tracked as BUG-02-032 (error[E2003]: unknown identifier drop_early, confirmed RED 2026-06-02). The test is authored RED-before-fix and goes green with NO edit once BUG-02-032 lands; the new reusable compile_and_run_with_env helper (tests/aot/util/compile.rs) supports the ORI_TRACE_RC=1 axes. Checkbox stays unchecked until BUG-02-032 closes and the pin runs green. Ori source:
      use std.testing { assert_eq };
      type Node { value: int, next: Option<Node> }
      @t tests _ () -> void = {
          let n3 = Node { value: 3, next: None };
          let n2 = Node { value: 2, next: Some(n3) };
          let n1 = Node { value: 1, next: Some(n2) };
          let n1_alias = n1;          // RC bump: n1's heap node now has rc = 2
          drop_early(value: n1);       // explicit lifetime shortening per spec/08-types.md:1145-1148
          // POST-drop_early state: refcount-zero branch of ori_rc_dec MUST NOT fire on n1's heap node
          // because n1_alias still holds rc = 1; recursive compiled drop body is NOT invoked.
          // n1_alias keeps the chain alive; scope-exit drop of n1_alias reaches rc = 0 and the
          // recursive compiled drop body THEN traverses n1_alias -> n2 -> n3.
          assert_eq(actual: n1_alias.value, expected: 1);
      }
      Verification axes (both pins required):
      1. Positive — recursive body invocation at final rc = 0: ORI_TRACE_RC=1 shows n1_alias scope-exit ori_rc_dec hitting rc-zero branch and emitting three child decs (n1 → n2 → n3); ORI_CHECK_LEAKS=1 reports zero leaks.
      2. Negative — recursive body skipped at rc > 1: ORI_TRACE_RC=1 shows the drop_early(value: n1) call decrementing the heap node’s rc from 2 → 1 WITHOUT entering the _ori_drop$Node body (count of _ori_drop$Node invocations BEFORE n1_alias scope exit = 0). The negative pin asserts the shared-reference correctness invariant: rc > 1 release decrements WITHOUT compiled drop body invocation per success_criterion 2.
    • Spec citation: explicit-drop surface is drop_early(value: T) -> void per compiler_repo/docs/ori_lang/v2026/spec/08-types.md:1145-1148; drop_explicit does NOT exist in the spec. Cited in recursive_drop.rs test source verbatim.

04.1 HISTORY

  • 2026-05-17 — §04.1 algorithmic landing + AOT slice blocker discovery. SCC-based cycle detection (Tarjan iterative) shipped at compiler_repo/compiler/ori_types/src/registry/burden_compose/scc/mod.rs with 15-cell matrix at scc/tests.rs covering self-loop singletons, mutually-recursive pairs/triples, non-recursive baselines, and decision-rule clauses. Codegen-IR-level cycle-safety pins shipped at compiler_repo/compiler/ori_llvm/src/codegen/arc_emitter/tests.rs (recursive_node_drop_fn_emits_self_referencing_rc_dec, mutually_recursive_tree_forest_drop_fns_cross_reference, drop_fn_cache_prevents_infinite_generation) consuming the existing _ori_drop$<idx_raw> mangling at drop_gen.rs:45 + cache-before-body pattern at drop_gen.rs:69 — no new emission path introduced. AOT-level tests (compiler_repo/compiler/ori_llvm/tests/aot/recursive_drop.rs) authored with #[ignore = "BUG-04-043: recursive struct types not yet supported by LLVM codegen"] per .claude/rules/test-disposition.md — BUG-04-043 is the load-bearing pre-existing blocker for the AOT slice (recursive type Node = { value: int, next: Option<Node> } triggers build_struct: insert_value failed (index out of bounds?) index=0 num_fields=0 at LLVM codegen). The §04.1 algorithmic deliverables stand on the unit + codegen-IR-level coverage; the AOT slice re-enables in the same commit that closes BUG-04-043.

04.2 Closure capture composition + compiled_drop population at closure-type-registration + env-header drop_fn transport

Files:

  • compiler_repo/compiler/ori_types/src/registry/burden_compose/closure/mod.rs — closure type detection + compiled_drop population
  • compiler_repo/compiler/ori_arc/src/lower/burden_lower/ — PartialApply transfer-point emission (already shipped in §03.3); is_owned_position SSOT now lives in ir/instr.rs+terminator.rs, consumed by burden_lower/
  • compiler_repo/compiler/ori_llvm/src/codegen/arc_emitter/rc_ops.rs:256-287 — existing emit_rc_dec_closure consumed unchanged
  • compiler_repo/compiler/ori_llvm/src/codegen/arc_emitter/drop_gen.rs:91 — existing DropKind::ClosureEnv(fields) arm consumed unchanged

Design — UserBurdenSpec for Closure populated at closure-type-registration time

  • At closure-type-registration time, populate UserBurdenSpec per the spec below. Phase ordering (per canon.md §1 pipeline): closure types are first encountered during TYPE CHECKING (phase 3, ori_types) when the type checker walks lambda/closure expressions in the AST and synthesizes the resulting Closure<R, captures> type; type-checking RUNS BEFORE ARC lowering (phase 5, ori_arc), so ori_types::registry::burden_compose::compose_user_burden_spec populates the closure’s BurdenSpec at the lambda-expression type-check site, NOT when ori_arc/src/lower/calls/mod.rs later lowers the lambda into a PartialApply IR node. Specifically: when ori_types::infer::expr::infer_lambda (or equivalent — verify symbol name at implementation time via scripts/intel-query.sh file-symbols compiler_repo/compiler/ori_types/src/infer --repo ori) constructs the closure type with its captured-binding signature, the same site registers the closure’s UserBurdenSpec via compose_user_burden_spec with these fields: self_heap_alloc: true (closure environment is heap-allocated); owned_fields: [<one OwnedField per captured-by-value capture>] — field index = capture position, field_type = captured binding’s resolved Idx; borrowed_fields: [<one BorrowedField per captured-by-reference capture>]; element_burden: None; variant_burdens: []; compiled_drop: Some(env_drop_fn_sym) — minted at registration time, materialized later by codegen via existing DropKind::ClosureEnv arm; user_drop: None (closures cannot have user @drop — only types do).
  • Capture-by-value of Owned binding: capture site IS the transfer point per §03.3 Rule 5 (already shipped). BurdenInc on env field at PartialApply; closure last-use emits BurdenDec(closure_var) which routes through existing emit_rc_dec_closure at rc_ops.rs:256, loading drop_fn from env_ptr field 0 and calling ori_rc_dec(env_ptr, drop_fn).
  • Capture-by-reference: borrow stored in borrowed_fields[i]; no drop on env field (borrows do not own); lifetime tied via existing borrow-inference machinery at ori_arc/src/borrow/mod.rs — borrow’s lifetime cannot outlive closure’s lifetime.
  • Captures-of-captures (nested closures): outer env field IS Closure<...> with its OWN UserBurdenSpec.compiled_drop. Recursion handled identically to recursive types per §04.1 — outer closure’s drop body recursively invokes inner closure’s compiled_drop via the inner field’s UserBurdenSpec lookup at codegen.
  • Capture of projection: treated as borrowed_fields entry with parent variable’s lifetime tied — the projection itself does not own; parent owns.
  • PartialApply transfer-point invariant: per §03.3 Rule 5 (already shipped), one transfer point per captured arg; binding consumed by PartialApply AND Owned callee in same expr → transfer-count 2 → one BurdenInc emitted (zero-net). Verified via existing test partial_apply_owned_capture_emits_single_burden_inc_per_03_3_rule_5_zero_net at burden_lower/tests.rs.
  • Borrow-check-refinement sync — owned_fields vs borrowed_fields MUST match the post-borrow-check capture classification. The closure UserBurdenSpec is composed at type-check time (phase 3), but borrow inference (ori_arc/src/borrow/mod.rs, phase 5) may refine a by-value capture into a by-reference borrow, drifting the composed owned_fields/borrowed_fields partition from the final physical env layout. The composer MUST either (a) run/re-run after borrow-check refinement settles the capture-by-value-vs-by-reference classification, OR (b) carry a re-evaluation step that re-partitions owned_fields/borrowed_fields against the post-borrow-check classification before codegen consumes the spec. A by-value→by-reference refinement that leaves the field in owned_fields would emit a spurious BurdenDec on a borrow at env drop. VERIFIED NO-DRIFT 2026-05-31 (commit a516d99cc): investigation established the live drop path admits NO drift, on two independent grounds: (1) compose_closure_burden_spec is a PURE mapping of its owned_captures/borrowed_captures inputs — it does NOT classify; the partition is exactly the caller-supplied classification, so feeding it the post-borrow-check classification yields a post-borrow-check-correct partition with zero residue in the opposite set (pinned by closure_owned_borrowed_partition_is_a_pure_function_of_classification_input in closure/tests.rs). (2) The shipped closure env-drop does NOT consume the owned/borrowed partition for its dec decision: the env physically OWNS a copy of every capture (stored by build_closure_env), so compute_closure_env_drop decs every classifier.needs_rc() capture over the post-borrow-check ARC-IR var types regardless of by-value/by-reference body classification. The owned/borrowed distinction governs only the BODY’s borrow treatment (wrapper RcInc-skip via capture_ownership, which is already post-borrow-check). Pinned behaviorally by test_closure_borrowed_capture_env_owns_copy_no_drift (a borrowed heap-str capture is leak-clean: env owns the copy, decs once, wrapper does not inc) + existing test_nested_closure_borrowed_str_param/_list_param. The composer’s classification INPUTS arrive post-borrow-check by construction whenever infer_lambda wires it — that wiring is the remaining lambda-side work below. (no fix fabricated for a non-gap per task directive; the lambda-side infer_lambda wiring at compiler_repo/compiler/ori_types/src/infer/expr/blocks.rs:223 is the remaining §04.2 work — formerly BUG-02-033, now CLOSED-as-resolved per bug-tracker/plans/completed/BUG-02-033/; verify-green per §04.N, do NOT treat as an open blocker.)

Env-header layout — drop_fn at offset 0

  • Lay-out invariant for closure environments (resolved from rc_ops.rs:281 evidence — let drop_fn = self.builder.load(ptr_ty, env_ptr, "rc_dec.drop_fn"); reads *env_ptr directly, so drop_fn lives AT env_ptr, NOT at a negative offset):
    [env_ptr - 8] : refcount (i64, RT-2 header convention — sits BEFORE data_ptr per arc.md §RT-2)
    [env_ptr + 0] : drop_fn (ptr, 8 bytes — the slot loaded by emit_rc_dec_closure)
    [env_ptr + 8] : captured_field_0
    [env_ptr + 16]: captured_field_1
    ...
    The env_ptr exposed to Ori code points AT the drop_fn slot (offset 0 of the data payload). The RC header sits at env_ptr - 8 per the standard arc.md §RT-2 convention. Captured fields start at env_ptr + 8 and extend forward.
  • No new ABI required: this layout is what emit_rc_dec_closure already implements at rc_ops.rs:256-287 — the load(ptr_ty, env_ptr) at line 281 reads drop_fn directly from the data pointer; the surrounding ori_rc_dec(env_ptr, drop_fn) call passes both the same env_ptr (for refcount decrement at env_ptr - 8) and the loaded drop_fn (for invocation when refcount reaches zero).
  • Capture-field access: closure body accesses captured field i via gep env_ptr, 8 + i*sizeof(field_i) — standard struct-field access pattern offset by the 8-byte drop_fn header slot. Field-i indices in UserBurdenSpec.owned_fields[i] MUST map to physical offsets accounting for the drop_fn slot.

Codegen — compiled_drop materialization via DropKind::ClosureEnv

  • At codegen, when UserBurdenSpec.compiled_drop = Some(env_drop_fn_sym) is encountered for a closure type, generate_drop_fn at drop_gen.rs:40 is called with DropKind::ClosureEnv(fields) where fields is derived from the closure’s owned_fields. The existing DropKind::ClosureEnv(fields) | DropKind::Fields(fields) shared arm at drop_gen.rs:91 emits the body — emit_drop_fields(data_ptr, ty, fields) walks each owned field, calling emit_rc_dec per field, then emit_drop_rc_free(data_ptr, ty) to release the env allocation.
  • No new codegen path. The DropKind::ClosureEnv variant is shipped; §04.2’s job is to populate UserBurdenSpec.compiled_drop for closure types so the existing path activates.
  • Single-SSOT closure-env drop dispatch — retire generate_env_drop_fn. Closure env-drop generation currently has TWO paths: the custom generate_env_drop_fn helper at compiler_repo/compiler/ori_llvm/src/codegen/arc_emitter/closures.rs:189 AND the shared DropKind::ClosureEnv arm at compiler_repo/compiler/ori_llvm/src/codegen/arc_emitter/drop_gen.rs:94. Duplicate dispatch risks divergent buffer offsets for collection-typed captures. Per AIMS Invariant 5 (no parallel emission paths per arc.md §Non-Negotiable Invariants), retire generate_env_drop_fn and route ALL closure-env drop generation through DropKind::ClosureEnv (drop_gen.rs:94) as the single SSOT dispatch. Re-point the closures.rs:189 call site at the shared arm; delete the divergent helper. DONE 2026-05-31 (commit a516d99cc): generate_env_drop_fn now derives DropKind::ClosureEnv(fields) via ori_arc::compute_closure_env_drop (the SSOT for which captures need RC + their logical indices) and decrements every field through the shared dec_value_rc tag-aware SSOT (single inline-value RC-dec dispatch: buffer-dec for List/Set/Map, inline-enum-dec for Option/Result/Enum, dynamic env-header-dec for closure captures) — the divergent per-tag dispatch + its parallel emit_buffer_rc_dec_* collection path is deleted. Closure-env-local concerns stay local (per-instance env struct type — closure envs have no pool Idx, so generate_drop_fn(ty: Idx) cannot key them; +1 field offset for the drop_fn header slot; ori_rc_free payload size). New pins test_closure_capture_map_drops_via_single_ssot + test_closure_capture_mixed_collections_drop_via_single_ssot (fail-on-revert verified — removing the dec leaks); 20 existing closure-drop AOT tests + 12-cell composer matrix green; --all-targets -D warnings clean; ori_llvm AOT failure set byte-identical pre/post (zero regressions).

Test Strategy

  • Tests at compiler_repo/compiler/ori_arc/src/lower/burden_lower/tests.rs (existing pattern from §03.3): pin shapes covering each capture variant matrix axis:
    • closure_capture_by_value_of_owned_str_emits_burden_inc_at_partial_apply (positive — let s = "hello"; let c = (() -> s.length()) — capture site = PartialApply = transfer point, BurdenInc on env field)
    • closure_capture_by_reference_emits_no_burden_inc (negative — capture-by-reference is borrow, no transfer, no BurdenInc)
    • nested_closure_emits_recursive_burden_inc_through_outer_env (positive — outer closure captures inner closure; outer drop walks inner drop transitively)
    • closure_capture_of_projection_emits_borrowed_field_with_parent_lifetime (positive — let p = Pair { a: 1, b: 2 }; let c = (() -> p.a) captures projection)
  • Specific PartialApply matrix pin: partial_apply_owned_capture_passed_to_owned_callee_emits_zero_net_burden_per_03_3_rule_5 (positive — the §03.3 Rule 5 invariant under §04 closure UserBurdenSpec population).
  • AOT-level test at compiler_repo/compiler/ori_llvm/tests/aot/closure_drop.rs: the file EXISTS on disk, registered as pub mod closure_drop; in compiler_repo/compiler/ori_llvm/tests/aot/main.rs, carrying TWO ACTIVE #[test]s — test_closure_capture_by_value_str_drops_at_scope_exit (closure_drop.rs:63) + test_closure_env_header_drop_fn_invokes_at_refcount_zero (closure_drop.rs:83) — with ZERO #[ignore] (verified on disk this pass). There is NO BUG-04-043 #[ignore] reason to remove: the scaffolds are already active. Remaining work is verifying the two active tests PASS green (ORI_CHECK_LEAKS=1 zero leaks + ORI_TRACE_RC=1 balanced env alloc/dec pairs + eval/LLVM parity) against the landed infer_lambda closure-UserBurdenSpec auto-registration wiring (compose_closure_burden_spec + register_user_burden at compiler_repo/compiler/ori_types/src/infer/expr/blocks.rs:223); the recursive-codegen blocker BUG-04-043 + lambda-side wiring blocker BUG-02-033 are both CLOSED-as-resolved (bug-tracker/plans/completed/). If either active test FAILS at AOT execution, that is a fresh AOT defect — /add-bug subsystem codegen for the failure mode, then fix to green; do NOT re-#[ignore] it. Implementer note: the closure_drop.rs:30 module docstring still narrates BUG-04-118 + BUG-04-043 as gating “scaffold re-enable” blockers (both CLOSED — BUG-04-118 obe per 00-overview.md absorbed_bugs[], BUG-04-043 resolved); strip that stale-blocker framing from the docstring when authoring the verification pass (it is a source-comment fix, not plan content). BUG-04-118, the prior empirical motivation, is CLOSED-as-obe per 00-overview.md absorbed_bugs[].

Subsection close-out (04.2) per protocol.

04.2 HISTORY

  • 2026-05-17 — §04.2 closure-capture-composition landing. Closure burden-spec composer shipped at compiler_repo/compiler/ori_types/src/registry/burden_compose/closure/mod.rs (compose_closure_burden_spec(closure_idx, owned_captures, borrowed_captures) -> UserBurdenSpec + mint_closure_drop_fn_sym(closure_idx) delegating to scc::mint_compiled_drop_fn_sym so closure drop functions share the _ori_drop$<idx_raw> mangling key with §04.1’s recursive-type drop functions per drop_gen.rs:45). Composer populates self_heap_alloc: true, owned_fields (capture-by-value), borrowed_fields (capture-by-reference), compiled_drop: Some(<minted FnSym>), user_drop: None. 11-cell matrix at closure/tests.rs covers capture-by-value singleton + multi-arg, capture-by-reference, mixed owned/borrowed, captures-of-captures (nested closures), capture-of-projection, per-Idx FnSym distinctness, zero-captures default-shape, and default-invariant clamps per tests.md §Matrix Clamping. 5 burden_lower walker pins shipped at compiler_repo/compiler/ori_arc/src/lower/burden_lower/tests.rs (closure_capture_by_value_of_owned_str_emits_burden_inc_at_partial_apply, closure_capture_by_reference_emits_no_burden_inc, nested_closure_emits_recursive_burden_inc_through_outer_env, closure_capture_of_projection_emits_borrowed_field_with_parent_lifetime, partial_apply_owned_capture_passed_to_owned_callee_emits_zero_net_burden_per_03_3_rule_5) verify the existing trivial-emission walker correctly consumes the composed-and-registered closure burden — exercised against Idx::STR and closure_idx types registered via crate::lower::test_utils::registered_struct_with_burden. AOT scaffolds at compiler_repo/compiler/ori_llvm/tests/aot/closure_drop.rs carry #[ignore = "BUG-04-118: §04.2 lambda-side wiring follow-up — auto-register closure UserBurdenSpec at infer_lambda site"] per .claude/rules/test-disposition.md; the lambda-side wiring at compiler_repo/compiler/ori_types/src/infer/expr/blocks.rs:223 (infer_lambda) is plan-tracked follow-up (calls compose_closure_burden_spec + register_user_burden at lambda-type-check time). Env-header layout ([env_ptr - 8] refcount, [env_ptr + 0] drop_fn, [env_ptr + 8..] captured-fields) is shipped at rc_ops.rs:256-287 (emit_rc_dec_closure loads drop_fn from *env_ptr); §04.2 does NOT introduce new ABI. Codegen consumption path is the existing DropKind::ClosureEnv(fields) | DropKind::Fields(fields) shared arm at compiler_repo/compiler/ori_llvm/src/codegen/arc_emitter/drop_gen.rs:91; §04.2 populates the closure-side burden so the existing path activates. All 1017 ori_types lib tests + 1390 ori_arc lib tests + 652 ori_llvm lib tests + cargo clippy -p ori_types -p ori_arc -p ori_llvm --all-targets -- -D warnings clean.

04.3 Drop AUGMENT body shape (DropKind::Fields + DropKind::Enum) + abort-on-drop-panic + EDROP_PARTIAL_MOVE (E2048) typeck rejection

Files:

  • compiler_repo/compiler/ori_types/src/check/registration/impls.rs — Drop impl detection + user_drop FnSym population at the explicit impl T: Drop registration site (NOT register_derived_impl — Drop is explicit-impl-only per drop-trait-proposal.md §Auto-derive)
  • compiler_repo/compiler/ori_types/src/check/validators/partial_move.rs — NEW validate_drop_partial_move validator (sibling to existing validate_partial_move per E2043)
  • compiler_repo/compiler/ori_diagnostic/src/error_code/mod.rs:159 — E2048 = “EDROP_PARTIAL_MOVE: partial move on type implementing Drop” (verified on disk: E2047 at line 158; E2048 at line 159; subsequent E2049 at line 160)
  • compiler_repo/compiler/ori_llvm/src/codegen/arc_emitter/drop_gen.rs:91 + :131 — BOTH DropKind::Fields(fields) AND DropKind::Enum(...) arms extended for user_drop FIRST + reverse-order field/variant walk; enum-shaped Drop types route through emit_drop_enum and MUST receive the same AUGMENT wiring (per gemini blind-spot: limiting AUGMENT to DropKind::Fields silently bypasses enum @drop bodies during refcount-zero drop)
  • compiler_repo/compiler/ori_rt/src/rc/mod.rs — abort-on-drop-panic runtime entry point invoked by codegen-emitted landing pad surrounding Apply(user_drop) (no unwind through drop bodies — preserves field-walk completion-or-abort semantics)

Design — Drop trait signature + AUGMENT semantics

Per drop-trait-proposal.md §Definition (approved 2026-01-30):

trait Drop {
    @drop (self) -> void
}
  • self is by-value per Ori’s method convention (ori-syntax.md §Declarations: “self (mutable in methods — mutations propagate to caller)”). The proposal explicitly bans @drop (self) -> bool (line 150 of drop-trait-proposal.md) — return type MUST be void. self cannot move out of fields (per drop-trait-proposal.md §Execution Timing).
  • Per drop-trait-proposal.md §85: “Fields are dropped in reverse declaration order, then the struct” — establishes the LIFO field-drop convention.

Drop AUGMENT body shape (struct + enum + panic-safety)

  • At codegen, when UserBurdenSpec.user_drop = Some(method_fn_sym) AND compiled_drop = Some(_) (every Drop type has compiled_drop because drop method gets a body): the generated drop function body shape is:
    1. Apply(user_drop, [data_ptr]) FIRST — calls user @drop (self) -> void; user sees fields VALID (compiler has not yet walked them).
    2. THEN walk owned_fields in REVERSE declaration order — for each field i (descending), emit_rc_dec(field_i). For enum types: walk the discriminant-selected variant’s owned fields in REVERSE declaration order via the existing emit_drop_enum switch-on-tag pattern.
    3. IF self_heap_alloc, emit_drop_rc_free(data_ptr, ty) — release the struct/enum allocation.
  • Struct extension (DropKind::Fields): Extension to existing emit_drop_fields at drop_gen.rs:140-184: add user_drop: Option<FnSym> parameter; if Some, emit Apply(user_drop, [data_ptr]) BEFORE the field-walk loop. Existing callers (DropKind::ClosureEnv, recursive-types DropKind::Fields without user_drop) pass None.
  • Enum extension (DropKind::Enum) — REQUIRED to avoid the Drop-on-enum silent bypass: extend DropKind::Enum { variants, user_drop } to carry an Option<FnSym> for the enum-level @drop body; extend emit_drop_enum at drop_gen.rs to emit Apply(user_drop, [data_ptr]) BEFORE the switch-on-tag-and-walk-variant-fields lowering. Without this, types declared as type T = A(int) | B(str) with impl T: Drop would silently drop fields without invoking the user @drop body — Drop trait silently bypassed at runtime. §04.3 wires AUGMENT into BOTH DropKind::Fields and DropKind::Enum; the compiled_drop minting at registration time (§04.3 Drop impl registration) keys on Type identity, not on field-vs-variant shape, so a single user_drop: Some(_) value flows through to whichever DropKind variant the emission path selects.
  • Self-recursion inhibition — no default ARC inc/dec on the by-value self param inside @drop bodies (self-drop infinite-recursion guard). The Apply(user_drop, [data_ptr]) glue passes the value by-value as self per the trait signature @drop (self) -> void. The compiled drop function MUST NOT emit a default scope-exit BurdenDec/RcDec on that self binding inside the @drop body — the drop glue is ALREADY the refcount-zero cleanup path for that allocation; a default dec on self would re-enter ori_rc_dec(data_ptr, drop_fn) and recurse the cleanup infinitely (and double-free). The AUGMENT glue suppresses default ARC inc/dec on the self param: the @drop body treats self as a borrowed view for the duration of the user body (fields valid, no ownership-transfer dec on self itself), with the compiler-side field-walk (step 2) + emit_drop_rc_free (step 3) owning the actual release. The self-recursion-inhibition guard splits into two independently-shippable verification surfaces:
    • ARC-IR-level absence assertion (SHIPPABLE NOW — NOT AOT-blocked). A codegen-IR-level test asserting the emitted AUGMENT drop body for a Drop type contains ZERO BurdenDec/RcDec on the self ArcVarId (the by-value self binding fed to Apply(user_drop, [data_ptr])). This inspects the emitted ARC IR directly (same harness shape as the §04.1 recursive_node_drop_fn_emits_self_referencing_rc_dec / §04.2 closure_capture_* codegen-IR pins) and does NOT require AOT execution, the prelude Drop wiring, or the BUG-04-125 invoke+landing-pad slice — it is a fail-on-revert structural pin that lands at compiler_repo/compiler/ori_llvm/src/codegen/arc_emitter/tests.rs as drop_augment_self_param_emits_no_default_dec_in_arc_ir. Shipped: the test asserts the generated _ori_drop$<T> for a no-RC-field Drop-shaped struct releases data_ptr ONLY via call void @ori_rc_free and emits NO call void @ori_rc_dec (a self dec on data_ptr would re-enter the drop fn); the paired recursive_node_drop_fn_emits_self_referencing_rc_dec makes the negative assertion non-vacuous (it DOES emit ori_rc_dec on a self-typed FIELD).
    • observable AOT positive pin (NOW ACTIONABLE — BUG-04-125 CLOSED): a Drop type whose @drop body merely reads a field drops exactly once (no infinite recursion, single emit_drop_rc_free), verified by AOT execution with ORI_CHECK_LEAKS=1 + eval/LLVM parity. The invoke+landing-pad blocker BUG-04-125 is CLOSED-as-resolved (bug-tracker/plans/completed/BUG-04-125/); author this pin at compiler_repo/compiler/ori_llvm/tests/aot/drop_augment.rs as an active #[test] alongside the invoke+landing-pad slice (success_criterion 6). Shipped + green: drop_struct_runs_user_method_first_then_fields_in_reverse_decl_order — the Logged::@drop body reads self.tag and the value drops exactly once (clean exit 0 under the harness’s ORI_CHECK_LEAKS=1, no infinite recursion); the LIFO field-order sentinel sequence ([drop-Resource-user, drop-Logged-b, drop-Logged-a]) doubly pins single-drop. Passes debug AND release (FastISel parity).

Drop panic-safety — abort-on-any-drop-panic

Per drop-trait-proposal.md §Drop and panic, a single panic during @drop is permitted (recoverable by the unwinder); a NESTED panic during cleanup aborts. The codegen path MUST preserve the invariant that fields ARE walked even if user_drop panics — otherwise a panicking @drop leaks the struct’s owned fields permanently.

  • Codegen invariant — landing-pad around Apply(user_drop) (NOW ACTIONABLE — BUG-04-125 CLOSED): when emitting an AUGMENT drop body, the Apply(user_drop, [data_ptr]) call MUST be emitted as an invoke (LLVM unwind-capable call) wrapped in a landing pad that — on unwind — STILL runs the field-walk + emit_drop_rc_free cleanup sequence BEFORE resuming the unwind. After the cleanup sequence on the unwind path, the landing pad re-emits Resume to propagate the original exception to the caller. Normal-return path executes the field-walk + free directly. Both paths converge on the same cleanup body via a shared LLVM basic block. Shipped: emit_drop_fields at drop_gen.rs emits the own_drop_unwinds path — invoke_user_drop_via_pointer (→ udrop.cont normal / udrop.cleanup unwind), the cleanup pad runs landingpad + ori_drop_cleanup_enter + the field-walk + emit_drop_rc_free + ori_drop_cleanup_exit + resume, so first-panic-recovery semantics hold. Verified green debug + release: drop_struct_user_panic_still_runs_field_walk_then_resumes_unwind (exit 1, both fields freed), drop_single_panic_does_not_abort_before_cleanup, drop_nested_panic_during_cleanup_aborts_process (exit 134), all under the harness’s ORI_CHECK_LEAKS=1.
  • Runtime invariant — nested-panic abort: ori_drop_double_panic_abort runtime symbol shipped at compiler_repo/compiler/ori_rt/src/rc/mod.rs — codegen-emitted cleanup landing pad will invoke this when wired with the landing-pad shape (Drop-AUGMENT invoke+landing-pad AOT slice — BUG-04-125 follow-up). Symbol matches Rust’s nested-panic-during-drop abort semantics and is available for the codegen-side landing-pad wiring.
  • Spec wiring: drop-trait-proposal.md §Drop and panic already names the abort-on-nested-panic invariant; runtime side shipped this slice. Spec compliance test deferred to the Drop-AUGMENT invoke+landing-pad AOT slice (BUG-04-125).
  • Alternative considered (spec-enforce abort-on-any-drop-panic) — REJECTED: gemini blind-spot proposed making ANY panic in @drop abort. Per drop-trait-proposal.md §Drop and panic first-panic recovery is intentional (lets users implement crash-resilient drop bodies that report error context via exit hooks). Choosing landing-pad approach preserves the proposal’s stated semantics without weakening them. Current slice uses call_drop_fn’s abort-on-any-panic guard (strict mode); landing-pad wiring restores first-panic-recovery semantics per the Drop-AUGMENT invoke+landing-pad AOT slice (BUG-04-125).

Drop impl registration — user_drop FnSym population

  • Registration site — explicit impl path, NOT derived-impl path: Drop is implemented explicitly by user code (impl Type: Drop { @drop (self) -> void = body }), NOT auto-derived via #derive(Drop). The user_drop FnSym population therefore lives at the explicit impl-registration site compiler_repo/compiler/ori_types/src/check/registration/impls.rs::register_impl (the function processing explicit impl T: Trait declarations), NOT derived.rs::register_derived_impl (which handles #derive(...) markers like E2029 Hashable-requires-Eq enforcement).
  • When register_impl encounters an impl Type: Drop { @drop (self) -> void = body }: the method’s compiled fn_sym is assigned to UserBurdenSpec.user_drop = Some(method_fn_sym) for the implementing Type’s BurdenSpec entry in TypeRegistry::burden.
  • derived.rs::register_derived_impl is NOT touched for Drop or for E2049 — it retains its existing responsibility for derived-trait validation (e.g., E2029 Hashable-requires-Eq). E2049 enforcement lives on the TWO non-derived surfaces (register_user_types for type-decl-side, register_impl for explicit-impl-side) per §04.4. Rationale: Drop is explicit-impl-only (NOT in the auto-derive set per drop-trait-proposal.md §Auto-derive), and Value is a type-declaration-only marker (per ori-syntax.md §Prelude, Value cannot be user-implemented via impl T: Value); neither marker passes through register_derived_impl. User_drop population = “Type has explicit Drop impl with @drop body” at impls.rs; E2049 = “type carries both Value AND Drop in its trait set” detected at whichever registration lands SECOND (register_user_types if Value declaration follows, register_impl if Drop impl follows).
  • DerivedTrait sync points (per ir.md §DerivedTrait) — Drop is NOT in the auto-derive set per drop-trait-proposal.md §Auto-derive (Drop is explicit-impl-only; user must write the @drop body), so NO new variant is added to the DerivedTrait enum at ori_ir/derives/mod.rs. The four sync points (ori_ir, ori_types, ori_eval, library/std/prelude.ori) remain unchanged for Drop — they only carry derived traits (Eq, Hashable, Clone, Debug, Comparable, Default, Printable, Serialize).

EDROP_PARTIAL_MOVE (E2048) typeck rejection

  • Error code allocation: E2048, "EDROP_PARTIAL_MOVE: partial move on type implementing Drop" at ori_diagnostic/src/error_code/mod.rs after E2047. Diagnostic message ships at ori_types/src/type_error/check_error/{message,format,kind,mod}.rs + the rendering arm at reporting/mod.rs.
  • Validator location: NEW compiler_repo/compiler/ori_types/src/check/validators/partial_move.rs::validate_drop_partial_move — sibling to the existing validate_partial_move (which emits E2043 for conditional partial moves on non-Drop types). Detection algorithm: AST walk over StmtKind::Let { init: Field { receiver: Ident(v), field } } patterns + ExprKind::Let { init: Field { ... } } (recursing through every reachable expression via shared collect_child_ids helper). Drop-trait detection: TraitRegistry::find_impl(drop_trait_idx, receiver_type); type-display-name resolution per Tag (Struct → pool.struct_name, Enum → pool.enum_name, Named/Applied → pool.named_name/pool.applied_name).
  • Allowed cases for Drop types: full moves (entire value consumed at once), references/borrows (don’t drop), match-destructuring (binding all fields simultaneously consumes whole value with all fields bound). These mirror the drop-trait-proposal.md allowed-uses per §Execution Timing.
  • Closure capture of partially-moved field of Drop type: same rejection (E2048). The validator runs over closure capture sites identically to other Let bindings — capture site is a binding-style consumption.
  • Wiring: validate_drop_partial_move invoked from finalize_body_and_export at ori_types/src/check/bodies/mod.rs immediately after the existing validate_partial_move call site (so E2043 and E2048 fire in disjoint axes).
  • Whole-value-consumption invariant on match-destructure of Drop types — forbid PARTIAL by-value destructure (double-free guard). The match-destructure allowance above is sound ONLY when the destructure binds the WHOLE value (every field bound by-value simultaneously), so the value is consumed exactly once and its @drop runs exactly once. A PARTIAL by-value destructure of a Drop type (binding a strict subset of fields by-value, leaving the residual value live) is a double-free / leak hazard: the bound fields move out (their drops now owned by the new bindings) while the residual still carries the type’s @drop glue, which would re-drop the moved-out fields. validate_drop_partial_move MUST reject a match-arm pattern that by-value-binds a PROPER subset of a Drop type’s fields (E2048), distinct from the whole-value bind-all-fields case which is allowed. Detection extends the AST walk to match-arm destructure patterns over Drop-typed scrutinees: a struct/variant pattern that binds fewer than all owned fields by-value (and does not bind the residual via a rest-binding that re-takes ownership of the whole) fires E2048. Positive pin: match d { D { a, b } -> ... } binding BOTH fields of a 2-field Drop type → accepted (whole-value consumption). Negative pin: match d { D { a, .. } -> ... } binding only a by-value of a Drop type → E2048 rejected. Shipped at validate_drop_partial_move (check_drop_match_destructure_at + partial_subset_field + variant_payload_count): the surface match form lowers to ExprKind::FunctionSeq(FunctionSeq::Match), so the walk now resolves scrutinee + arms from the FunctionSeq (struct {a, ..} rest-pattern OR fewer bound fields than the type/variant declares → E2048; bind-all → accepted) AND recurses into FunctionSeq children (match arm bodies, try-block statements) via walk_drop_function_seq_children so nested let $f = v.field projections inside arms are reached. Also repaired the shipped let-projection path: drop_aggregate_type_name now tries find_impl against BOTH the resolved (Struct/Enum) and unresolved (Named) receiver Idx — the impl is keyed under the Named form while the receiver resolves to Struct, so the prior single-key lookup silently missed and E2048 never fired (mirrors the codegen-side element_fn_gen.rs::drop_type_name resolve-state robustness). Verified by the 7-cell matrix at compiler_repo/compiler/ori_types/src/check/integration_tests.rs (drop_match_partial_struct_destructure_rejected_e2048, drop_match_whole_value_struct_destructure_accepted, drop_match_partial_enum_variant_destructure_rejected_e2048, drop_match_whole_payload_enum_variant_destructure_accepted, drop_match_partial_destructure_on_non_drop_type_accepted, drop_let_projection_rejected_e2048, drop_match_nested_let_projection_in_arm_rejected_e2048) — positive + negative + negative-space clamping, declaring a local trait Drop to register it (file-level #compile_fail spec discovery is gapped — BUG-07-183).

Test Strategy

  • Spec tests at compiler_repo/tests/spec/aims/drop_augment/ — NOW ACTIONABLE: the full Drop-trait surface requires the prelude’s Drop trait registered in library/std/prelude.ori + the library/std-side trait wiring (formerly BUG-06-005, CLOSED-as-obe at bug-tracker/plans/completed/BUG-06-005/); current slice ships the typeck E2048 validator that activates whenever Drop IS registered in the trait registry. Shipped: tests/spec/aims/drop_augment/ holds whole_value_destructure_allowed.ori (positive — runs + passes via cargo st, the tests-clause function test_whole_value_destructure_accepted compiles a Pair { a, b } whole-value match-destructure clean) plus let_projection_rejected.ori + partial_match_destructure_rejected.ori (negative #compile_fail(code: "E2048") files — both produce E2048 when ori check-ed directly, confirming the directive expectation is correct). On-disk disposition (verified): drop_augment.rs carries TWO #[ignore] slots citing OPEN blockers — BUG-05-006 (AOT Set element teardown segfault) + BUG-04-122. The on-disk drop_augment.rs:656 #[ignore] reason for BUG-04-122 reads “niche-encoded codegen gated off (NICHE_CODEGEN_READY=false)”; the single-variant-tagless / niche-encoded heap-payload codegen surface is the same BUG-04-122 cell (the IR-level niche dec SSOT routing is verified, the behavioral cell activates when niche codegen ships). Both are legitimate open-blocker dispositions per .claude/rules/test-disposition.md; those two cells STAY ignored until those bugs close (do NOT remove them, do NOT re-point them). There is NO BUG-04-119 #[ignore] in drop_augment.rs — that anchor (imported generic monomorphization) is unrelated and was never the disposition.
    • (deferred-with-anchor: BUG-07-183) The two negative #compile_fail spec files are NOT executed by the ori test runner — file-level #compile_fail on a plain @fn / type decl has its expected_errors dropped at parse time (only TestDef carries them), so the runner reports “NO TESTS FOUND” and verifies nothing (the long-standing E2043 conditional_partial_move_rejected.ori is identically un-run). This is a runner gap orthogonal to §04.3, tracked at BUG-07-183 (tooling/oric test runner). The E2048 deliverable’s negative-pin verification is fully covered by the run-able ori_types integration matrix (item §04.3 match-destructure above); the spec files activate once BUG-07-183 lands file-level #compile_fail discovery without modification.
  • Codegen tests scaffold at compiler_repo/compiler/ori_llvm/tests/aot/drop_augment.rs — NOW ACTIONABLE: AUGMENT-body codegen verification re-enables (invoke+landing-pad blocker BUG-04-125 CLOSED-as-resolved at bug-tracker/plans/completed/BUG-04-125/); author the four non-ignored pins below as active #[test]s (the two BUG-05-006 / BUG-04-122 #[ignore] cells remain ignored until those open bugs close). Shipped + green (debug AND release, harness ORI_CHECK_LEAKS=1): the four pins land as drop_struct_runs_user_method_first_then_fields_in_reverse_decl_order (struct LIFO [drop-Resource-user, drop-Logged-b, drop-Logged-a]), drop_enum_runs_user_method_first_then_variant_fields_in_reverse_order (enum LIFO), drop_struct_user_panic_still_runs_field_walk_then_resumes_unwind (landing-pad: exit 1, both fields freed), drop_nested_panic_during_cleanup_aborts_process (nested-panic abort, exit 134). The two BUG-05-006 / BUG-04-122 #[ignore] cells remain ignored (untouched, legitimate open-blocker dispositions; the BUG-04-122 cell’s on-disk reason at drop_augment.rs:656 is the “niche-encoded codegen gated off (NICHE_CODEGEN_READY=false)” disposition — same BUG-04-122 surface). 20/22 active pins pass; 2 ignored.
    • drop_augment_user_method_first_then_compiler_field_walk_reverse_order (struct AUGMENT, DropKind::Fields). Observable LIFO semantic pin per drop-trait-proposal.md §85: type Outer = { first: Logged, second: Logged, third: Logged } where each Logged’s @drop appends its tag to a shared log; positive assertion = observed drop sequence equals [third, second, first] (reverse decl order, after the Outer @drop body runs first); paired negative assertion = sequence does NOT equal forward order [first, second, third].
    • drop_augment_enum_user_method_first_then_variant_field_walk_reverse_order (enum AUGMENT, DropKind::Enum). Observable LIFO semantic pin: an enum variant carrying multiple Logged owned fields drops them in reverse decl order after the enum-level @drop body runs; positive = reverse-order log sequence, negative = NOT forward-order.
    • drop_augment_landing_pad_runs_field_walk_on_user_drop_panic_path (panic-safety, landing pad).
    • drop_augment_nested_panic_invokes_ori_drop_double_panic_abort (panic-safety, nested panic).
  • Rust unit tests scaffold at compiler_repo/compiler/ori_types/src/check/validators/partial_move/tests.rs:
    • drop_partial_move_validator_handles_invalid_body_root_without_panicking — smoke pin verifying ExprId::INVALID short-circuit.
    • drop_partial_move_validator_silent_when_drop_trait_unregistered — pre-deployment-shape pin verifying the validator silently no-ops when Drop is not yet wired into the trait registry. The full 4-axis matrix (Drop × {conditional, unconditional} × {E2048, E2043, both, none}) requires the library/std Drop trait + prelude wiring (formerly BUG-06-005, now CLOSED-as-obe at bug-tracker/plans/completed/BUG-06-005/) — NOW ACTIONABLE: author the end-to-end matrix at compiler_repo/compiler/ori_types/src/check/validators/partial_move/tests.rs once the prelude Drop trait is wired.
  • Verification gate: cargo test -p ori_types --lib check::validators::partial_move:: returns 3/3 passed; cargo test -p ori_llvm --lib codegen::arc_emitter::tests:: returns 36/36 passed (all migrated to new DropKind::{Fields,Enum} struct shape); cargo c -p ori_types -p ori_arc -p ori_llvm -p ori_rt -p ori_diagnostic --all-targets clean; cargo clippy -p ori_types -p ori_arc -p ori_llvm -p ori_rt -p ori_diagnostic --all-targets -- -D warnings clean. AOT slice gates NOW ACTIONABLE (the Drop-AUGMENT invoke+landing-pad blocker BUG-04-125 is CLOSED-as-resolved at bug-tracker/plans/completed/BUG-04-125/): run ORI_CHECK_LEAKS=1 + eval/LLVM parity on the positive pins at compiler_repo/compiler/ori_llvm/tests/aot/drop_augment.rs.

Subsection close-out (04.3) per protocol.

04.3 HISTORY

  • 2026-05-17 — §04.3 Drop AUGMENT typeck + DropKind API + runtime symbol landing. Five typeck-side + codegen-API + runtime deliverables shipped; AUGMENT body-shape + landing-pad AOT slice deferred to BUG-04-119 per scope discipline.
    • E2048 allocation (EDROP_PARTIAL_MOVE) shipped at compiler_repo/compiler/ori_diagnostic/src/error_code/mod.rs (next slot after E2047) + compiler_repo/compiler/ori_diagnostic/src/errors/E2048.md markdown with cure cases (full move / borrow / match destructure / drop_early); diagnostic kind TypeErrorKind::DropPartialMove at compiler_repo/compiler/ori_types/src/type_error/check_error/{kind,mod,message,format}.rs + reporting/mod.rs rendering arm. Mapping E2048 ↔ kind shipped at message.rs::code() arm.
    • validate_drop_partial_move validator shipped at compiler_repo/compiler/ori_types/src/check/validators/partial_move.rs (sibling to validate_partial_move for E2043). AST walk over Let { init: Field { ... } } patterns; Drop-impl detection via TraitRegistry::find_impl(drop_trait_idx, receiver_type). Disjoint-axis from E2043: every Drop partial move is forbidden regardless of CFG path; non-Drop conditional partial moves continue to fire E2043 via the existing validator. Wired from bodies/mod.rs::finalize_body_and_export via run_drop_partial_move_validator runner — invoked immediately after the existing run_partial_move_validator call site so E2043 + E2048 fire in disjoint axes on the same body walk.
    • Drop impl detection + user_drop + compiled_drop population at register_impl shipped at compiler_repo/compiler/ori_types/src/check/registration/impls.rs::populate_drop_burden_if_applicable. Resolves Drop’s trait Idx via the interner; when the impl-def’s trait_idx matches, mints user_drop = Some(FnSym) via mint_compiled_drop_fn_sym(self_type) AND mints compiled_drop = Some(FnSym) per the §04.1 decision rule (compiled_drop = Some(_) iff (non-singleton SCC) OR (self-loop) OR (user_drop = Some(_))). Calls TypeRegistry::register_user_burden to merge the user_drop / compiled_drop fields over the existing burden_compute output; preserves all field-walk machinery shipped in §04.1.
    • DropKind API change shipped at compiler_repo/compiler/ori_arc/src/drop/mod.rs: DropKind::Fields(Vec) → DropKind::Fields { fields, user_drop: Option<FnSym> }; DropKind::Enum(Vec<Vec>) → DropKind::Enum { variants, user_drop: Option<FnSym> }. FnSym carries #[cfg_attr(feature = "cache", serde(skip))] — cache hits re-derive user_drop from current TypeRegistry per AIMS Invariant 3 (no stale summaries). burden_bridge.rs::burden_to_drop_kind updated to emit struct shape; the “fields.is_empty() + user_drop.is_none()” check ensures Drop types with all-scalar fields STILL emit a DropKind so the AUGMENT body fires. Consumers updated: drop_gen.rs::generate_drop_fn dispatch arms; instr_dispatch.rs::BurdenDecPartial + BurdenDecVariant arms (user_drop intentionally NOT invoked at SetField/SetTag mid-mutation; only refcount-zero path).
    • emit_drop_fields + emit_drop_enum AUGMENT body shape shipped at compiler_repo/compiler/ori_llvm/src/codegen/arc_emitter/drop_gen.rs + drop_enum.rs. Both functions accept user_drop: Option<FnSym> parameter; when Some, invoke _ori_user_drop$<idx> BEFORE the field walk / discriminant switch. Field walk reverses declaration order per drop-trait-proposal.md §85. Current slice ships plain call to user @drop; the plan-mandated invoke + landing-pad lowering for first-panic recovery (per drop-trait-proposal.md §Drop and panic) is deferred to BUG-04-119; panic-safety is preserved by existing ori_rt::rc::call_drop_fn runtime abort-on-any-panic guard (strict mode — first-panic recovery semantics restored when landing-pad wiring lands).
    • ori_drop_double_panic_abort runtime symbol shipped at compiler_repo/compiler/ori_rt/src/rc/mod.rs. extern "C" nounwind-by-construction abort entry; matches Rust’s nested-panic-during-drop semantics. Symbol is available for the codegen-side landing-pad wiring (BUG-04-119); used as the “second panic during cleanup” abort target.
    • Tests: 3/3 validator smoke tests at partial_move/tests.rs (drop_partial_move_validator_handles_invalid_body_root_without_panicking, drop_partial_move_validator_silent_when_drop_trait_unregistered, existing partial_move_validator_handles_invalid_body_root_without_panicking). 36/36 existing arc_emitter drop tests at arc_emitter/tests.rs migrated to new DropKind::{Fields,Enum} struct shape. AOT scaffolds at compiler_repo/compiler/ori_llvm/tests/aot/drop_augment.rs carry 4 #[ignore = "BUG-04-119: ..."] tests pinning the full AUGMENT-body + landing-pad + nested-panic-abort surface.
    • Gates: cargo c -p ori_types -p ori_arc -p ori_llvm -p ori_rt -p ori_diagnostic --all-targets clean; cargo test -p ori_types --lib check::validators::partial_move:: 3/3 pass; cargo test -p ori_llvm --lib codegen::arc_emitter::tests:: 36/36 pass; cargo clippy -p ori_types -p ori_arc -p ori_llvm -p ori_rt -p ori_diagnostic --all-targets -- -D warnings clean. Full lib-test suite: 1019 ori_types + 1390 ori_arc + 652 ori_llvm + 367 ori_rt = 3428 tests, 0 failures.
    • Deferrals with concrete anchors per CLAUDE.md §“ALL Deferrals MUST Have Implementation Anchors”:
      • BUG-04-118 (closure UserBurdenSpec lambda-side wiring): closure-and-Drop interactions roundtrip through the same path once infer_lambda calls compose_closure_burden_spec + register_user_burden at lambda-typecheck time.
      • BUG-04-119 (AUGMENT body shape AOT slice): invoke + landing-pad + __gxx_personality_v0 wiring around Apply(user_drop); restores first-panic-recovery semantics per drop-trait-proposal.md §Drop and panic; closes the four AOT scaffolds at drop_augment.rs.

04.4 Value trait empty-burden + EVALUE_DROP_CONFLICT (E2049) at type-declaration + impl registration + mixed-Value/Heap composition

Files:

  • compiler_repo/compiler/ori_types/src/check/registration/user_types.rs::register_user_types — Surface 1 E2049 detection at type-decl registration (queries TraitRegistry for existing Drop impl when Value marker appears on declaration)
  • compiler_repo/compiler/ori_types/src/check/registration/impls.rs::register_impl — Surface 2 E2049 detection at Drop impl registration (queries TraitRegistry for Value marker on receiver type)
  • compiler_repo/compiler/ori_registry/src/burden/table.rs — Value primitives entries
  • compiler_repo/compiler/ori_diagnostic/src/error_code/mod.rs — E2049 = “EVALUE_DROP_CONFLICT: type carries both Value and Drop”
  • compiler_repo/compiler/ori_types/src/registry/burden_compose/mod.rs — variant_burden composition for mixed-Value/Heap (existing §02 machinery consumed)

Design — Empty UserBurdenSpec for Value types

Per ori-syntax.md §Prelude (Value trait section):

trait Value: Clone, Eq — marker trait; inline storage, bitwise copy, no ARC, no Drop; all fields must be Value; cannot be implemented manually; auto-satisfies Clone + Sendable.

  • At BurdenRegistry population time, when a type carries Value marker (either primitive — int/float/bool/char/byte/void/Duration/Size/Ordering — or user-defined type T: Value = { ... }), populate UserBurdenSpec:
    UserBurdenSpec {
        self_heap_alloc: false,    // Value types are inline-stored
        owned_fields: vec![],       // Value composition: all fields are Value, no heap ownership
        borrowed_fields: vec![],
        variant_burdens: vec![],
        element_burden: None,
        compiled_drop: None,        // No heap → no compiled_drop
        user_drop: None,            // Value cannot impl Drop (mutually exclusive)
    }
  • For primitives: emit entries into ori_registry::burden::table::BURDEN_TABLE (pure-const builtin table). Each primitive’s BuiltinBurdenSpec template has self_heap_alloc: false, owned_fields: &[], variant_burdens: &[], compiled_drop: None, user_drop: None. Existing BUILTIN_BURDEN_TABLE entries already satisfy this shape — verified: int/float/bool/char/byte/void/Duration/Size/Ordering carry BuiltinBurdenSpec::EMPTY per ori_registry::burden::table.
  • For user-defined type T: Value = { ... }: populated at register_user_types site via populate_value_burden_if_applicable at compiler_repo/compiler/ori_types/src/check/registration/user_types.rs. When decl.derives contains Value, calls register_user_burden(idx, UserBurdenSpec::default()) (empty spec) AND record_value_marker(idx) so Surface 2 can detect late-arriving Drop impls.
  • Value field-constraint enforcement (transitively-Value fields) — HARD §04.4 close-out gate (blocks_section_close); success_criterion 11. SHIPPED 2026-06-02 at populate_value_burden_if_applicable (enforce_value_field_constraint + cycle-aware field_idx_is_value); all four pins (value_type_all_value_fields_registers_empty_burden, value_type_with_str_field_rejected, value_type_with_nested_value_struct_field_accepts, value_type_with_recursive_value_field_terminates) pass in check/registration/tests.rs. The visited-set memo (seeded with the declaring type Idx) is the single cycle guard; a non-Value field emits E2032 (FieldMissingTraitInDerive) naming the offending field + the Value trait. Per ori-syntax.md §Prelude (Value requires all fields must be Value): at populate_value_burden_if_applicable, BEFORE registering the empty UserBurdenSpec, walk every declared field’s resolved Idx and assert each satisfies Value (primitive Value set OR a user type that itself carries the Value marker — recursive via the already-registered record_value_marker set + ori_registry::burden Value membership). A non-Value field (str/[T]/{K:V}/Set<T> or any heap-bearing user type) is a compile error naming the offending field. The empty UserBurdenSpec at populate_value_burden_if_applicable is sound ONLY when this transitive-Value field-walk guard holds — registering an empty burden for a type with a heap-bearing field would silently drop the field’s RC obligation (the latent silent-RC-drop). Validator anchored at the same Surface 1 site compiler_repo/compiler/ori_types/src/check/registration/user_types.rs::populate_value_burden_if_applicable. Cycle-aware traversal — MANDATORY. The transitive field-walk MUST carry a visited-set/memo keyed on type Idx so a recursive Value-marked type (e.g. a type whose field set transitively references itself through a Value wrapper) terminates instead of recursing infinitely; a field Idx already in the visited set is treated as resolved (its membership verdict is already being computed or has been computed) and does NOT re-descend. The validator records each type’s Value-membership verdict once per traversal and short-circuits on a revisit. Pins (positive + negative + transitive + cycle per tests.md §Matrix Testing Rule): value_type_all_value_fields_registers_empty_burden (positive — type Pt: Value = { x: float, y: float } → empty UserBurdenSpec, no diagnostic); value_type_with_str_field_rejected (negative — type Bad: Value = { name: str } → field-constraint diagnostic naming name: str); value_type_with_nested_value_struct_field_accepts (transitive — type Outer: Value = { inner: Pt } where Pt: Value → accepted via transitive Value-membership lookup); value_type_with_recursive_value_field_terminates (cycle — a Value-marked type whose field walk transitively revisits the same type Idx resolves via the visited-set memo without infinite recursion or stack overflow). Appended to the check/registration/tests.rs matrix alongside the E2049 cells. Implementer note (NOT part of this criterion — out-of-scope for the §04.4 field-walk deliverable): this validator walks declared FIELD Idx and does NOT enforce that a Value-marked GENERIC type PARAMETER is itself Value-bounded (e.g. type Box<T>: Value = { inner: T } with unbounded T would permit unsound monomorphization Box<HeapType> carrying an empty UserBurdenSpec). If that generic-parameter T: Value bound check is confirmed a real validator gap at §04.4 landing, the implementer files /add-bug subsystem typeck (registration-time Value-generic-bound check) per the §04.R agy-F3 re-review variant candidate item — do NOT widen this criterion to specify it.

EVALUE_DROP_CONFLICT (E2049) enforcement — type-declaration registration + Drop impl registration

  • Error code allocation: E2049, "EVALUE_DROP_CONFLICT: type marked Value cannot implement Drop" shipped at compiler_repo/compiler/ori_diagnostic/src/error_code/mod.rs (line 160 on disk, immediately after E2048 at line 159). Diagnostic message + suggestions wired through ValueDropConflict { type_name } variant on TypeErrorKind (kind.rs + constructors in mod.rs + format.rs + message.rs + reporting/mod.rs label arm). Imperative-fix errors/E2049.md ships matching the E2042/E2048 pattern.
  • Ori surface asymmetry: per ori-syntax.md §Declarations + §Prelude, Value is a compiler-marker trait set on the TYPE DECLARATION (type T: Value = { ... }), NEVER user-implemented via impl T: Value (Value’s invariants are statically enforced by the compiler). Drop, by contrast, is user-implemented exclusively via explicit impl T: Drop { @drop (self) -> void = body } (per drop-trait-proposal.md §Auto-derive, Drop is NOT auto-derivable). Two distinct registration surfaces detect the conflict:
    • Surface 1 — Type declaration: compiler_repo/compiler/ori_types/src/check/registration/user_types.rs::populate_value_burden_if_applicable. When decl.derives contains Value: query TraitRegistry::has_impl(drop_idx, idx). If present: emit E2049 with decl.span (type-decl is the second registration).
    • Surface 2 — Drop impl registration: compiler_repo/compiler/ori_types/src/check/registration/impls.rs::populate_drop_burden_if_applicable. When impl T: Drop is registered: query TypeRegistry::carries_value_marker(self_type) (populated by Surface 1 via record_value_marker). If true: emit E2049 with impl_def.span (Drop impl is the second registration).
  • NO impl T: Value site: per ori-syntax.md, impl T: Value is not valid Ori syntax — Value is type-declaration-only. The test matrix covers ONLY the two valid orderings: (Value-on-type-decl FIRST, Drop-impl SECOND) and (Drop-impl FIRST, Value-on-type-decl SECOND).
  • Order independence: whichever registration lands SECOND emits E2049. Verified by two paired tests: drop_impl_for_value_type_emits_e2049_at_register_impl_surface (type-decl first, impl second → Surface 2 fires) and value_type_decl_with_drop_impl_emits_e2049_at_user_types_surface (impl first, type-decl second → Surface 1 fires).

Mixed-Value/Heap composition — Result<ValueType, HeapType>

  • Generic composition: Result<int, str> (Value Ok + Heap Err) → UserBurdenSpec for Result<int, str> has:
    UserBurdenSpec {
        self_heap_alloc: false,   // Result inline-stored when payload allows (per repr.md §7 NI-4 niche encoding)
        owned_fields: vec![],     // No top-level owned fields
        variant_burdens: vec![
            VariantBurden { variant: Ok, burden: <empty UserBurdenSpec for int> },
            VariantBurden { variant: Err, burden: <full UserBurdenSpec for str> },
        ],
        ...
    }
  • Phase 5 emission for Result<int, str>: BurdenDec at last-use of the Result branches per variant_burden:
    • Ok arm: empty burden → no BurdenDec emitted (no heap to release).
    • Err arm: full str burden → BurdenDec emitted (releases the str’s RC).
  • Existing variant_burdens composition machinery handles this — composition is shipped per compose_user_burden at burden_compose/mod.rs. The empty-vs-full asymmetry is verified by result_value_heap_composition_inherits_distinct_variant_burdens at burden_compose/tests.rs: Ok arm’s field_type is Idx::INT (Value-role, empty burden via per-Idx lookup); Err arm’s field_type is Idx::STR (Heap-role, full burden via per-Idx lookup).
  • Niche encoding interaction: per repr.md §7 NI-3/NI-4, Option<T> and Result<T, E> may be niche-encoded (eliding the tag field). Burden composition operates on LOGICAL variants (Ok/Err, Some/None) — physical layout via niche or tagged encoding is orthogonal. Verified by option_value_type_niche_encoded_inherits_empty_burden at burden_compose/tests.rs: Option<int> produces None + Some logical arms regardless of how niche encoding lowers the physical layout.

Test Strategy

Spec tests at compiler_repo/tests/spec/aims/value_drop_conflict/. The #compile_fail("E2049") typeck-rejection tests below exercise compile-time E2049 enforcement (shipped at both registration surfaces per the [x] items above) and do NOT depend on AOT codegen — they are gated ONLY by the prelude Value + Drop trait registration in library/std/prelude.ori (formerly BUG-06-005, now CLOSED-as-obe). The prelude-wiring, lambda-side (infer_lambda), and Drop-AUGMENT-AOT blockers are ALL now closed (BUG-06-005 obe, BUG-02-033 resolved, BUG-04-125 resolved, BUG-04-043 resolved); the ORI_DUMP_AFTER_ARC-verified positive pins are NOW ACTIONABLE.

  • (deferred-with-anchor: BUG-01-009) value_drop_conflict_rejected.ori#compile_fail("E2049") negative pin: declares type T: Value, ... = { ... } THEN impl T: Drop. BLOCKED: the type T: Value = {...} type-decl trait-bound surface does NOT parse (E1001 — grammar.ebnf type_def lacks the [":" bounds] clause the spec mandates; tracked BUG-01-009), AND file-level #compile_fail is dropped by the ori test runner (BUG-07-183). E2049 negative enforcement is instead pinned RUNNABLY at compiler_repo/compiler/ori_types/src/check/integration_tests.rs::value_marker_with_drop_impl_fires_e2049_value_first (via #derive(Value) + impl Drop, the only parseable Value surface today). Re-author this .ori #compile_fail file when BOTH BUG-01-009 (surface) AND BUG-07-183 (runner) close.

  • (deferred-with-anchor: BUG-01-009) value_drop_conflict_order_independent.ori — both surface-ordering variants BLOCKED on the same BUG-01-009 + BUG-07-183 pair. Order-independence IS pinned runnably at the Rust unit level (value_type_decl_with_drop_impl_emits_e2049_at_user_types_surface Surface 1 + drop_impl_for_value_type_emits_e2049_at_register_impl_surface Surface 2 in check/registration/tests.rs); the .ori #compile_fail files re-author when the surface + runner blockers close.

  • value_primitives_have_empty_burden.ori — positive pin SHIPPED 2026-06-02 at compiler_repo/tests/spec/aims/value_drop_conflict/value_primitives_have_empty_burden.ori (runnable, dual-backend green). Zero-RC-op property pinned at the IR level by the AOT test value_empty_burden.rs::test_value_only_program_emits_zero_rc_ops (ORI_DUMP_AFTER_LLVM shows zero ori_rc_inc/ori_rc_dec); the .ori test pins observable behavior through interpreter + LLVM.

  • result_value_heap_emits_asymmetric_drop.ori — positive pin SHIPPED 2026-06-02 at compiler_repo/tests/spec/aims/value_drop_conflict/result_value_heap_emits_asymmetric_drop.ori (runnable, dual-backend green). Asymmetric drop pinned at the IR level by value_empty_burden.rs::test_result_value_heap_emits_asymmetric_drop (ori_str_rc_dec on Err path only, Ok-int path no drop); the .ori test pins observable behavior + zero-leak through interpreter + LLVM.

  • Rust unit tests appended to compiler_repo/compiler/ori_types/src/check/registration/tests.rs (existing sibling file matching the §04.3 pattern; a separate value_drop_conflict_tests.rs would duplicate the make_param + module-test scaffolding already in tests.rs). 4-axis matrix exercising both surfaces:

    • value_type_decl_with_drop_impl_emits_e2049_at_user_types_surfaceDrop-then-Value ordering: register Drop impl FIRST, then declare type Point: Value, ... = { ... }. Pin asserts exactly one E2049 fires at Surface 1 with span at the type declaration.
    • drop_impl_for_value_type_emits_e2049_at_register_impl_surfaceValue-then-Drop ordering: declare type Point: Value, ... = { ... } FIRST, then register impl Point: Drop. Pin asserts exactly one E2049 fires at Surface 2 with span at the impl declaration.
    • value_type_without_drop_impl_registers_empty_user_burden_specValue-only type: no E2049, empty UserBurdenSpec registered (self_heap_alloc=false, owned_fields=[], variant_burdens=[], compiled_drop=None, user_drop=None), carries_value_marker(idx) returns true.
    • drop_impl_for_non_value_type_registers_user_drop_no_e2049Drop-only type: no E2049, user_drop=Some(_) populated by populate_drop_burden_if_applicable, carries_value_marker(idx) returns false.
    • Field-walk pins (success_criterion 11) appended 2026-06-02: value_type_all_value_fields_registers_empty_burden (positive), value_type_with_str_field_rejected (negative — E2032 names the str field), value_type_with_nested_value_struct_field_accepts (transitive Value-membership), value_type_with_recursive_value_field_terminates (cycle — visited-set memo terminates). Plus runnable source-level E2049 negatives at check/integration_tests.rs (value_marker_with_drop_impl_fires_e2049_value_first + the two no-E2049 negative-space pins) — the BUG-07-183 runner-gap workaround for the file-level #compile_fail("E2049") spec files.
  • Burden composition tests at compiler_repo/compiler/ori_types/src/registry/burden_compose/tests.rs — two new cells appended:

    • result_value_heap_composition_inherits_distinct_variant_burdens — positive pin: Result<int, str> Ok arm’s field_type is Idx::INT (Value-role); Err arm’s field_type is Idx::STR (Heap-role). Per-variant transfer_kind is Move in both arms (kind erasure across the Value/Heap boundary at the composition level; role-asymmetric burden emission lives downstream via per-Idx TypeRegistry::burden(idx) lookup at codegen).
    • option_value_type_niche_encoded_inherits_empty_burden — positive pin: Option<int> produces two logical variant_burdens (None + Some) with Some carrying Idx::INT. Niche encoding operates on physical layout; burden composition operates on logical variants — the two are orthogonal.
  • AOT codegen test — SHIPPED 2026-06-02 at compiler_repo/compiler/ori_llvm/tests/aot/value_empty_burden.rs (registered pub mod value_empty_burden; in tests/aot/main.rs); 3 active #[test]s pass debug AND release:

    • test_value_only_program_emits_zero_rc_ops — Value-only program’s LLVM IR contains zero ori_rc_inc/ori_rc_dec calls (IR inspection via compile_and_capture_ir); zero-leak AOT run (ORI_CHECK_LEAKS=1); eval/LLVM exit-code parity.
    • test_heap_program_emits_rc_ops — negative-space clamp: a heap-bearing (str) program emits ≥1 RC op (proves the zero-RC result is the Value path, not a never-emits codegen).
    • test_result_value_heap_emits_asymmetric_dropResult<int, str> Err arm emits ori_str_rc_dec; both arms run zero-leak; eval/LLVM parity on both arms.
  • TPR checkpoint — the 2026-05-17 editor-pass round-loop short-circuit (third_party_review.status: clean, actionable_count=0) was superseded by subsequent content-drift auto-reversals. Current frontmatter SSOT is third_party_review.status: none, reviewed: false, status: in-review, blocked_by: [] (every prior blocker — BUG-04-043, BUG-04-125, BUG-06-005, BUG-02-033 — is now CLOSED on disk under bug-tracker/plans/completed/). Section-close /tpr-review re-runs at §04 close-out once the now-actionable item-level slices (Drop-AUGMENT invoke+landing-pad AOT, prelude Value+Drop wiring + the value_drop_conflict spec tests, lambda-side infer_lambda closure-UserBurdenSpec wiring, the transitively-Value field-constraint validator + its four pins) land; the prior “frontmatter-clean” claim no longer holds.

Subsection close-out (04.4) per protocol.


04.R — Third Party Review Findings

  • RESOLVED 2026-05-17 [TPR-04.R-001-opencode][Critical] STRUCTURE:work-order-violation — §01 now carries reviewed: true (verified: grep -E '^reviewed:' plans/aims-burden-tracking/section-01-burden-registry.md returns reviewed: true). The work-order predecessor invariant satisfied. Original finding text: Section-04 was advanced to status: in-review while predecessor section-01 (section-01-burden-registry.md) carries reviewed: false. The newly-expanded depends_on: ["01", "02", "03"] (per §04 frontmatter, expanded during Step 5 editor from prior depends_on: ["03"] per opencode blind-spots feedback) requires every predecessor to carry reviewed: true before a consumer section may transition to in-progress/in-review. Cure (caller-level decision — out of scope for §04 content rewrite): EITHER (a) run /review-plan plans/aims-burden-tracking/section-01-burden-registry.md --autopilot to flip section-01 from reviewed: falsereviewed: true per state-discipline.md §4 atomic-flip discipline (preferred when section-01 is genuinely review-ready), OR (b) revert section-04’s status: in-reviewnot-started via scripts/review_plan_runtime/status_flip.py::record_review_reversal until predecessor §01 satisfied. Recorded here for the parent /review-plan Step 7+8 verify gate to surface to the caller (/continue-roadmap autopilot or user). NEVER reviewed: true flip on §04 close-out while this item remains unchecked. Re-affirmed by the 2026-05-17 Step 5 editor pass: audit Critical finding TPR-04.R-001 unchanged — directive holds; the editor did NOT flip reviewed: on §04 nor on §01.

  • RESOLVED 2026-05-31 [TPR-04.R-006-agy+codex+opencode][Major] GROUNDEDNESS:misattributed-bug-anchor — anchors repointed to BUG-04-125 + closure-wiring candidate-bug note (see §04.R Candidate /add-bug items). SUPERSEDED-IN-PART (2026-05-31 editor pass): the “BUG-04-043 confirmed OPEN, blocked_by KEPT” portion of this finding is STALE — BUG-04-043 is now CLOSED-as-resolved (bug-tracker/plans/completed/BUG-04-043/); blocked_by is cleared to [] (opencode-F1), the recursive-drop AOT slice is unblocked, and the Drop-AUGMENT placeholder is resolved to BUG-04-125 (opencode-F3). Original 2026-05-31 finding text below retained for traceability only. The misattributed BUG-04-119 forward-deferral anchors (success_criteria 6+8, §04.3 panic-safety body + Test Strategy, §04.4 Test Strategy, §04.N) are now re-pointed to BUG-04-125 (the Drop-AUGMENT invoke+landing-pad AOT slice) per the 2026-05-31 editor-pass HISTORY entry (BUG-04-119 verified OPEN but tracks imported generic monomorphization, plan dir absent on disk — wholly unrelated to the Drop-AUGMENT codegen surface). blocked_by: ["BUG-04-043"] confirmed CORRECT (BUG-04-043 open, genuinely gates §04.1’s recursive-drop AOT slice). The parent /review-plan caller files the properly-scoped Drop-AUGMENT AOT bug and re-points the #[ignore] reasons in drop_augment.rs from BUG-04-119 to it in the commit closing it. Original finding text: §04 AOT-deferral anchors cite bug IDs whose actual tracker scope is unrelated: BUG-04-043 = “§07.3.A recursive tagged-pointer enum box-and-load codegen”, BUG-04-119 = “AOT imported-generic monomorphization”, BUG-04-118 (closed-obe) = “AIMS PIN-4 match_alias double-free” — none track §04.1/§04.2/§04.3 recursive/closure/AUGMENT AOT slices. blocked_by: [BUG-04-043] + the closure_drop.rs:63/:84 + drop_augment.rs:57 #[ignore] reasons reference wrong/stale bugs. Cure: determine §04’s true AOT-slice blockers (file proper bugs or correct the citations); re-point or un-ignore the AOT tests. Filed at §04-review cap-exit (cap_reached_with_substantive, 3 rounds, 2026-05-28); logged bug-tracker/diagnostic-questions.md 2026-05-28.

  • RESOLVED 2026-05-31 [TPR-04.R-005-opencode][Major] STRUCTURE:budget-overrun — budget:{body_lines:520} added 2026-05-31 editor pass (frontmatter lines 73-75, with rationale: the four BurdenSpec composition cases are one cohesive non-promotable scope per the §04 Subsection-independence analysis). The budget field now exists; the budget-vs-split decision resolved in favor of the budget per routing.md §4 (split would fragment one drop-glue story across four boundary-less siblings). Original finding text: §04 body is 458 lines vs routing.md §5 plan-section default 300; no budget: frontmatter field. Cure (deliberate split-vs-budget decision per routing.md §4): either split into letter-suffix siblings (≥3 work subsections) OR add budget: {body_lines: N, rationale: "..."} if the four BurdenSpec composition cases are genuinely one cohesive scope. Filed at §04-review cap-exit (cap_reached_with_substantive, 3 rounds, 2026-05-28).

  • REFUTED 2026-05-17 — stale finding [TPR-04.R-002-codex][Critical] STRUCTURE:tpr-exit-reason-drift — Verified: cap_reached_with_substantive IS in the /tpr-review canonical exit_reason enum defined at .claude/skills/tpr-review/references/exit-reason-enum.md (semantically-narrower sibling of cap_reached_max_rounds; routes identically to verify-done). Finding was authored before the enum extension landed. No tooling cure required; the drift was already resolved upstream. Verbatim refutation evidence: grep -l 'cap_reached_with_substantive' .claude/skills/tpr-review/references/exit-reason-enum.md matches (the token is absent from SKILL.md — it lives in the references/exit-reason-enum.md enum SSOT). Original finding text: /tpr-review round-loop emitted cap_reached_with_substantive at round 5/5; /review-plan orchestrator _dispatch_tpr_exit_reason() rejected with halt_reason: tpr_exit_reason_drift because that value is NOT in /tpr-review SKILL §1.7 canonical exit_reason enum (cap_reached_max_rounds + cap_reached_meta_only). Drift between scripts/tpr_review_runtime/round_loop.py::should_exit() emission and /review-plan’s mapping table BLOCKS §04 close-out at Step 7+8 verify regardless of TPR substantive convergence. Promoted from HISTORY into §04.R as a BLOCKING item per tooling-first hierarchy and per tooling-first.md §1.1 meta-tool boundary. Cure (caller-level — /improve-tooling SSOT): EITHER (a) align scripts/tpr_review_runtime/round_loop.py::should_exit() emission with /tpr-review SKILL §1.7 canonical enum (add cap_reached_with_substantive to the enum + the _dispatch_tpr_exit_reason() mapping table per decisions/31 Option C parity surface), OR (b) collapse the runtime’s emitted value to the existing cap_reached_max_rounds token (drops “substantive” qualifier; loses signal that /review-plan orchestrator uses to distinguish substantive cap-exit from meta-only cap-exit). Recommended option (a): preserve the substantive-vs-meta distinction by extending the canonical enum, since /review-plan downstream routing already distinguishes the two on _dispatch_tpr_exit_reason(). Banned cures: (1) Claude-side enum substitution in section bodies to fabricate a canonical value (banned by the orchestrator’s halt payload per tooling-first.md §1.1); (2) bypass-gate accepting tpr_exit_reason_drift without resolving (preserves drift permanently). This item GATES §04.N close-out — until the drift is resolved, /review-plan Step 7+8 §00.3 close-out cannot flip §04 reviewed: false → true regardless of /tpr-review substantive outcome, because the orchestrator halt PRECEDES the close-out gate. Caller (/continue-roadmap autopilot or user) MUST invoke /improve-tooling --gap tpr_exit_reason_drift before §04 close-out is attempted again.

  • RESOLVED 2026-05-29 [TPR-04.R-003-codex][Major] §04.N index.md section status checklist line reconciled with the status: in-review SSOT per state-discipline.md §1 (un-checked + reworded; no longer asserts derivation from status: complete). Original finding text: the §04.N checklist line asserted derivation from §04 frontmatter status: complete but the frontmatter is status: in-review (§04.1 in-progress). Filed at §04-review cap-exit (cap_reached_with_substantive, 3 rounds, 2026-05-28; survivor 2-of-3, claude-ds wrapper failed all rounds).

  • RESOLVED 2026-05-29 [TPR-04.R-004-codex+agy][Minor] The §04.3 self-by-value clause’s causal rationale tail (— because … but ARC ownership returns to the compiler-walk) replaced with a citation to drop-trait-proposal.md §Execution Timing per skill-vocabulary.md §3 rationale-tail. 2-reviewer agreement (codex + agy).

Candidate /add-bug items (editor cannot file bugs — parent /review-plan caller files via /add-bug)

The 2026-05-31 editor pass surfaced four observations that are IMPLEMENTATION-completeness concerns, not §04 plan-shape defects. Three (agy-F1/F2/F3) are ALREADY covered by existing §04 success criteria / checkbox items (no scope-widening needed — recorded here only as cross-references for the implementer); one (closure-wiring untracked-bug) needs a tracked bug ID the editor cannot mint:

  • closure-wiring follow-up (TRACKED — BUG-02-033, both blockers CLOSED): the closure_drop.rs:63/:83 AOT pins are ACTIVE #[test]s with ZERO #[ignore] on disk (verified this pass) — there is NO stale #[ignore] reason to re-point (the prior cross-reference asserting a BUG-04-043 #[ignore] was itself stale). Their formerly-residual blocker — the §04.2 lambda-side infer_lambda closure-UserBurdenSpec auto-registration wiring (compose_closure_burden_spec + register_user_burden at compiler_repo/compiler/ori_types/src/infer/expr/blocks.rs:223), tracked at BUG-02-033 — is CLOSED-as-resolved (bug-tracker/plans/completed/BUG-02-033/); BUG-04-043 + BUG-04-118 are likewise CLOSED. Remaining work is verifying the two active pins pass green against the landed wiring; the closure_drop.rs:30 module docstring’s stale BUG-04-118/BUG-04-043 “scaffold re-enable” narration is an implementer-time source-comment strip (do NOT edit the .rs from this plan-review pass). If an active pin FAILS at AOT, /add-bug subsystem codegen for the failure mode rather than re-#[ignore].
  • agy-F1 (match-pattern E2048 — ALREADY COVERED): validate_drop_partial_move not traversing ExprKind::Match patterns on Drop scrutinees is covered by the §04.3 whole-value-consumption invariant on match-destructure of Drop types (the - [ ] item with positive pin match d { D { a, b } -> ... } accepted / negative pin match d { D { a, .. } -> ... } E2048-rejected). If the validator gap is real at implementation time, file /add-bug for the match-path E2048 traversal; do NOT widen §04.
  • agy-F2 (transitive Value-membership — ALREADY COVERED): populate_value_burden_if_applicable lacking transitive cycle-aware Value field-constraint validation is covered by the §04.4 HARD close-out gate (success_criterion 11) + the cycle-aware-traversal MANDATORY requirement + four pins (value_type_all_value_fields_registers_empty_burden, value_type_with_str_field_rejected, value_type_with_nested_value_struct_field_accepts, value_type_with_recursive_value_field_terminates). No widening needed; the deliverable is specified.
  • agy-F3 (duplicate closure drop-glue dispatch — ALREADY COVERED): the closures.rs:189 generate_env_drop_fn vs drop_gen.rs DropKind::ClosureEnv dual dispatch (AIMS Invariant 5 risk) is covered by §04.2 L372 (retire generate_env_drop_fn, single-SSOT dispatch). If the duplicate dispatcher is confirmed at implementation time as an active divergence, file /add-bug for SSOT consolidation; do NOT widen §04.
  • agy-F3 re-review variant (Value-generic-parameter bound — CANDIDATE /add-bug, NOT YET COVERED): the re-review blind-spot agy-F3 (Value-marked generic type parameters may not be bounded by Value) targets a NARROWER surface than the §04.4 transitive-Value FIELD validator (success_criterion 11 / L510 walks declared field Idx, not generic parameter bounds). A declaration of the shape type Box<T>: Value = { inner: T } with T unbounded would permit an unsound monomorphization Box<HeapType> carrying an empty UserBurdenSpec — the field-walk validator inspects inner: T’s parameter Idx, not the constraint that EVERY instantiation of T must itself be Value. This generic-parameter T: Value bound enforcement at Value-marked-generic-type registration is NOT specified by any current §04.4 success criterion or checkbox. FILED 2026-06-02 as BUG-02-036 (subsystem typeck, severity medium, blocked_by: [BUG-01-009] — the registration-time Value-generic-bound check has no reachable input until the type T: Value = surface parses). The editor does NOT widen §04.4 success_criterion 11 to specify it (it is implementation-completeness scope, distinct from the §04.4 field-walk deliverable). Tracked at §04.R item TPR-04.R-007 below.

04.R.1 §04 close-out review findings (2026-06-02 /review-plan Step 5 editor pass)

  • FILED 2026-06-02 [TPR-04.R-007-codex+agy][Major] GROUNDEDNESS:coverage-gap — Value-generic-parameter-bound soundness gap. The §04.4 transitive-Value FIELD-constraint validator (enforce_value_field_constraint, user_types.rs::populate_value_burden_if_applicable) walks declared field Idx but does NOT enforce that a Value-marked GENERIC type PARAMETER is itself Value-bounded; type Box<T>: Value = { inner: T } with unbounded T permits unsound monomorphization Box<HeapType> carrying an empty UserBurdenSpec. 2-reviewer agreement (codex + agy). FILED as BUG-02-036 (subsystem typeck, severity medium, blocked_by: [BUG-01-009] — DOWNSTREAM of the parser gap blocking type T: Value = declarations; no reachable input until BUG-01-009 lands). Cross-ref: the §04.R agy-F3 re-review variant candidate item above. §04.4 success_criterion 11 (the field-walk deliverable) is verified and NOT widened — this is a distinct, narrower generic-parameter-bound surface.
  • ROUTING NOTE 2026-06-02 [TPR-04.R-010-opencode][Major] COHESION:cross-section-work-order — §04A/§04B are status: complete with depends_on: ['04'] while §04 is in-progress (complete successor, incomplete predecessor). opencode re-escalated re-opening §04A+§04B via record_review_reversal. Editor disposition: do NOT re-open §04A/§04B from §04’s review — that is cross-section lifecycle overreach. Each of §04A/§04B has its own /review-plan close-out that owns its status-vs-depends_on coherence; both are complete + reviewed: false on disk, so the roadmap picker dispatches them to /review-plan where the §04A↔§04 / §04B↔§04 frontmatter coherence is resolved. The prior round-2 adjudicator already downgraded the analogous finding to Major under sibling-independence. Editor records this routing note; does NOT flip §04A/§04B frontmatter (reviewed: / status: / depends_on: unchanged on both siblings).
  • DELIVERED 2026-06-02 [TPR-04.R-008-agy][Minor] GROUNDEDNESS:coverage-gap — nested-destructure E2048 recursion gap (genuine shipped gap, VERIFIED on disk, NOW FIXED). Pre-fix, validate_drop_partial_move rejected a PARTIAL by-value destructure only at the TOP level (E2048) via check_drop_match_destructure_atpartial_subset_field, which inspected ONLY the top-level arm pattern’s directly-bound fields and did NOT recurse into a sub-pattern bound to a Drop-typed field (match d { D { inner: Inner { x, .. } } } over a Drop Inner escaped). Fix (delivered): extracted the Idx-based Drop gate drop_type_name_for_idx (shared by the receiver-expr path + recursion per HYG §Algorithmic DRY); added recursive nested_drop_partial_move + nested_field_hit in compiler_repo/compiler/ori_types/src/check/validators/partial_move.rs walking nested struct/variant sub-patterns over Drop-typed fields (gates each level on the NESTED field’s own Drop status; fires independent of the outer scrutinee’s Drop status — a Drop field nested in a non-Drop outer is also caught); restructured check_drop_match_destructure_at (top-level check stays Drop-scrutinee-gated, nested check runs regardless). Matrix (TDD red→green): drop_match_nested_partial_struct_destructure_rejected_e2048 + drop_match_nested_partial_in_enum_payload_rejected_e2048 (negative pins, fired kinds: [] pre-fix), drop_match_nested_whole_value_struct_destructure_accepted + drop_match_nested_partial_on_non_drop_inner_accepted (clamps) in check/integration_tests.rs; spec .ori pins tests/spec/aims/drop_augment/nested_partial_match_destructure_{rejected,accepted}.ori (negative is BUG-07-183-runner-gapped, pinned via the integration tests; positive passes interpreter + LLVM). Full ori_types lib suite 1044 pass / 0 fail, clippy clean, dual-exec parity verified on the positive companion. partial_subset_field anchor preserved.
  • DELIVERED 2026-06-02 [TPR-04.R-009-opencode][Minor] GROUNDEDNESS:coverage-gap — cross-case interaction pin: recursive type WITH a user @drop (SCC compiled_drop + AUGMENT user_drop composed on the SAME type). §04.1 (recursive SCC drop-glue) and §04.3 (Drop AUGMENT body shape) each verify in isolation; their composition is now pinned. Delivered: recursive_drop_with_user_drop_composes_no_double_free in compiler_repo/compiler/ori_llvm/tests/aot/recursive_drop.rs — a recursive Node carrying impl Node: Drop { @drop (self) -> void = (); }, built into a 3-node chain, dropped at scope exit. The ORI_CHECK_LEAKS=1 oracle (via assert_aot_success) is the composition check: clean exit proves the AUGMENT user_drop + SCC compiled_drop compose (drop runs once per node, children traversed once, no leak, no double-free); a composition defect would surface as exit-2 leak (child-walk skipped) or double-free abort (children traversed twice). PASSES. Full recursive_drop suite 5 pass / 1 ignored (BUG-02-032). No composition defect surfaced — no /add-bug needed.

04.N Completion Checklist

  • E2048 EDROP_PARTIAL_MOVE + E2049 EVALUE_DROP_CONFLICT shipped with positive + negative pins per tests.md §Matrix Testing Rule. E2049 enforcement verified at BOTH non-derived surfaces (register_user_types Surface 1, register_impl Surface 2); register_derived_impl NOT touched.
  • cargo t green: 1025 ori_types + 1390 ori_arc + 652 ori_llvm + 367 ori_rt + 120 ori_diagnostic lib tests pass across this session’s deliverables (validators::partial_move, check::registration::{user_types, impls}, registry::burden_compose::{scc, closure}, lower::burden_lower, codegen::arc_emitter::tests).
  • Value field-constraint validator (success_criterion 11) ships at populate_value_burden_if_applicable — SHIPPED 2026-06-02. The transitive-Value field-walk guard (enforce_value_field_constraint + cycle-aware field_idx_is_value, visited-set memo) makes the empty UserBurdenSpec sound. Positive pin value_type_all_value_fields_registers_empty_burden + negative pin value_type_with_str_field_rejected (E2032 names the str field) + transitive pin value_type_with_nested_value_struct_field_accepts + cycle pin value_type_with_recursive_value_field_terminates all pass in check/registration/tests.rs. The HARD §04.4 close-out gate (success_criterion 11) is MET.
  • TPR-04.R-001 resolved 2026-05-17: §01 carries reviewed: true (verified). Cleared per §04.R item above.
  • TPR-04.R-002 refuted 2026-05-17 as stale finding — cap_reached_with_substantive already in the canonical enum at .claude/skills/tpr-review/references/exit-reason-enum.md. No /improve-tooling cure required; the drift was resolved upstream before the finding was authored. Cleared per §04.R item above.
  • 00-overview.md Mission Success Criteria checkbox flip — 00-overview.md cross-pointer update deferred to next /sync-claude pass; both §04.R blockers now resolved/refuted per the items above.
  • All 04.X subsections complete; UserBurdenSpec composition handles recursive (compiled_drop population via SCC analysis) + closure (env-header drop_fn transport via existing emit_rc_dec_closure) + Drop AUGMENT struct path (DropKind::Fields + user_drop) + Drop AUGMENT enum path (DropKind::Enum + user_drop) + Value (empty UserBurdenSpec for Value-marker types). Drop panic-safety landing pad NOW ACTIONABLE — author the invoke+landing-pad slice at compiler_repo/compiler/ori_llvm/src/codegen/arc_emitter/drop_gen.rs (the blocker BUG-04-125 is CLOSED-as-resolved at bug-tracker/plans/completed/BUG-04-125/). NOT complete: §04.1/§04.2/§04.3/§04.4 all in-progress, §04.R not-started, §04.N in-progress per frontmatter sections: SSOT (the four work subsections shipped their algorithmic deliverables 2026-05-17 but remain in-progress pending the now-unblocked AOT/spec slices). EVERY former cross-subsection blocker is CLOSED on disk: recursive-drop AOT BUG-04-043 (resolved), Drop-AUGMENT invoke+landing-pad BUG-04-125 (resolved), prelude Value+Drop BUG-06-005 (obe), lambda-side infer_lambda BUG-02-033 (resolved) — all under bug-tracker/plans/completed/. Two NARROW drop_augment cells remain gated by still-OPEN blockers (BUG-05-006 Set element teardown segfault + BUG-04-122, whose on-disk drop_augment.rs:656 #[ignore] reason is “niche-encoded codegen gated off (NICHE_CODEGEN_READY=false)”) — legitimate #[ignore] dispositions, not removable. BUG-04-119 (imported generic monomorphization) is unrelated and is NOT a disposition anchor in any drop_augment scaffold.
  • cargo st spec tests at tests/spec/aims/drop_augment/ + tests/spec/aims/value_drop_conflict/ — §04.4 PORTION DONE 2026-06-02: tests/spec/aims/value_drop_conflict/ ships two runnable positive-pin .ori tests (value_primitives_have_empty_burden.ori + result_value_heap_emits_asymmetric_drop.ori), both green on interpreter AND --backend=llvm; the two #compile_fail("E2049") .ori negatives are (deferred-with-anchor: BUG-01-009) (Value-marker surface does not parse) + BUG-07-183 (runner gap), with runnable E2049 negatives substituted in check/integration_tests.rs. value_empty_burden.rs AOT authored + registered (3 active #[test]s, no #[ignore]). REMAINING (other subsections, out of §04.4 scope): the drop_augment/ spec slice — drop_augment.rs carries TWO #[ignore] slots citing still-OPEN BUG-05-006 + BUG-04-122 (legitimate dispositions, STAY); recursive_drop.rs (zero #[ignore], three active) + closure_drop.rs (two active, zero #[ignore]) verify-green belongs to §04.1/§04.2.
  • ORI_CHECK_LEAKS=1 zero-leak verification — §04.4 PORTION DONE 2026-06-02: value_empty_burden.rs runs all 3 pins under ORI_CHECK_LEAKS=1 (via assert_aot_success) with zero leaks, debug AND release. REMAINING: leak verification for the §04.1/§04.2/§04.3 positive pins in recursive_drop.rs/closure_drop.rs/drop_augment.rs.
  • Eval/LLVM parity across positive pins — §04.4 PORTION DONE 2026-06-02: value_empty_burden.rs asserts interpreter-vs-AOT exit-code parity on all Value/Result pins; the two value_drop_conflict/*.ori spec tests pass on both backends via cargo st. REMAINING: parity for the §04.1/§04.2/§04.3 positive pins.
  • index.md section status — auto-derived from §04 frontmatter status: by scripts/plan_corpus/render.py index regeneration; pre-commit render-index hook re-syncs on next commit. Current SSOT is status: in-review (§04.1/§04.2/§04.3/§04.4 all in-progress per sections:); the index reflects in-review, NOT complete, until §04 close-out flips it.
  • /tpr-review passed (final) — section-close TPR not final-passed while §04 is open (§04.1-§04.4 in-progress; the AOT/spec slices are now ACTIONABLE — every former blocker BUG-04-043/BUG-04-125/BUG-06-005/BUG-02-033 is CLOSED on disk under bug-tracker/plans/completed/; BUG-04-118 is closed-obe and removed from live anchors). The 2026-05-17 §04 editor-pass round-loop (exit_reason: cap_reached_meta_only, third_party_review.status: clean) recorded a mid-pipeline TPR outcome, not the section-close final pass; TPR-04.R-002 stale-finding refuted — cap_reached_with_substantive IS in the canonical enum at .claude/skills/tpr-review/references/exit-reason-enum.md. section-close /tpr-review round 1 CLEAN: 3-of-3 reviewers, 0 actionable, 1 agreement cluster; codex-F1 pseudo-tested-method adjudicated false-positive (no-op @drop is intentional Ori test input; AUGMENT-compose verified by ORI_CHECK_LEAKS oracle). verdict: adjudicator-verdict-round-1.json
  • (deferred-with-anchor: section-close /impl-hygiene-review dispatch at next /continue-roadmap re-entry; in-line code authoring landed without new violations per cargo clippy -D warnings clean across all 5 crates this session) /impl-hygiene-review passed.

HISTORY

  • 2026-06-02 — /review-plan Step 5 editor pass (Opus authority, round 2): closure_drop.rs authoring-state contradiction resolved against disk. Three-reviewer-agreement Major (BS1) + audit re-flag: §04.2 L378 directed “un-ignore the closure_drop.rs:63/:84 scaffolds / remove the BUG-04-043 #[ignore]” and §04.N L596 said “closure_drop.rs not yet authored”, BOTH contradicting disk. Verified this pass: compiler_repo/compiler/ori_llvm/tests/aot/closure_drop.rs EXISTS, registered as pub mod closure_drop; in tests/aot/main.rs:12, with TWO ACTIVE #[test]s (test_closure_capture_by_value_str_drops_at_scope_exit :63 + test_closure_env_header_drop_fn_invokes_at_refcount_zero :83) and ZERO #[ignore]. Cures: (1) §04.2 L378 rewritten — dropped the no-such-#[ignore]-to-remove directive; states the two tests are already active; remaining work = verify-green against landed infer_lambda wiring (blocks.rs:223), else /add-bug subsystem codegen for the AOT failure (never re-#[ignore]). (2) §04.N L596 splitclosure_drop.rs correctly stated as EXISTING (verify-green, do NOT re-author); ONLY value_empty_burden.rs genuinely absent. (3) §04.R closure-wiring candidate item reconciled — the prior cross-reference asserting a stale BUG-04-043 #[ignore] on the pins was itself stale; the pins are active, BUG-02-033/BUG-04-043/BUG-04-118 all CLOSED. (4) BS2 implementer note recorded — the closure_drop.rs:30 module docstring still narrates BUG-04-118/BUG-04-043 as gating “scaffold re-enable” blockers (both closed); flagged as an implementer-time source-comment strip in §04.2 L378 + the §04.R item (the editor does NOT edit the .rs from a plan-review pass). (5) AR1 implementer note recorded — the §04.4 transitive-Value FIELD-walk validator (success_criterion 11) does NOT enforce Value-marked GENERIC-PARAMETER bounds (type Box<T>: Value = { inner: T } unsoundness); recorded as an out-of-scope /add-bug subsystem typeck implementer note at §04.4 + cross-referenced to the existing §04.R agy-F3 re-review variant candidate — NOT widened into the criterion per codex’s own disposition. BS3 (§04.3 self-drop recursion-guard checkbox ~L418) left untouched — already exists, no widening. Round-1 cures preserved: drop_augment.rs #[ignore] dispositions citing OPEN BUG-05-006 + BUG-04-122 unchanged; §04.1/§04.2 in-progress status flips unchanged; no fake-checkbox flip. reviewed:/status:/third_party_review: UNCHANGED — review state NOT reversed; close-out flip remains Step 7+8’s job.
  • 2026-06-02 — /review-plan Step 5 editor pass (Opus authority): all three item-level blockers CLOSED — deferred items converted to actionable. Verified against the bug-tracker (BUG-04-125 status=resolved, BUG-06-005 status=obe/superseded, BUG-02-033 status=resolved — all three plan dirs under bug-tracker/plans/completed/; recursive-drop AOT BUG-04-043 already resolved). Cures: (1) frontmatter blocked_by: [BUG-04-125, BUG-06-005, BUG-02-033] → [] — every section-level blocker is now closed on disk; §04 is no longer blocked and its unchecked items are actionable, not deferrable (gap-1/BS1). (2) 16 (deferred-with-anchor: ...) items converted to actionable - [ ] across §04 success_criteria 6+7, §04.2 closure_drop AOT, §04.3 self-recursion observable pin + panic-safety landing-pad invariant + Test Strategy + verification gate, §04.4 value_drop_conflict/value_primitives/result-asymmetric spec tests + value_empty_burden AOT, §04.N spec-test/leak/parity close-out items (gap-2/BS1/BS2). Per decisions/LEDGER.md §B.3 class split: ALL 16 are class (a) — §04’s OWN typeck/codegen deliverables that the now-closed bugs were gating (Drop-AUGMENT invoke+landing-pad AOT slice at drop_gen.rs, prelude Value+Drop wiring + E2049 spec tests, infer_lambda closure-UserBurdenSpec wiring at blocks.rs:223, the transitively-Value field-constraint validator + 4 pins). NONE is class (b): the §06/§09-coupled predicate-stack RC-imbalance residuals (the AOT 19-failure cohort = 17 BUG-04-123 + 2 BUG-04-121 per LEDGER §B.3; BUG-04-132 filed 2026-06-02 for the let-bound-aliased-value predicate-stack over-dec, defer_separate, §09 owns the cure) live in §06/§07/§09, NOT §04 — none was mis-labelled landable here. (3) depends_on body-vs-frontmatter reconcile — §04 body now clarifies §01/§02/§03 are TRANSITIVE predecessors reached through §03A’s chain, not additional direct DAG edges (frontmatter depends_on: [03A] per INV-19 strict-linear-single-branch; gap-5). (4) §04.1 body preamble (BS4) — the §04.2/§04.1 prose referencing the cleared section-level blocked_by: [] is now consistent with the cleared frontmatter (no stale [BUG-04-125, BUG-06-005, BUG-02-033] residue). The misattributed BUG-04-119 anchor (imported generic monomorphization, unrelated) is removed from forward deferral text; dated prior HISTORY entries referencing it stay as the historical record per state-discipline.md §7. reviewed:/status:/third_party_review: UNCHANGED — review state NOT reversed; close-out flip remains Step 7+8’s job. No work checkbox fake-completed.
  • 2026-05-29 — Linear-execution structural cure (2nd pass): §04.1 status-only flip to complete — its sole remaining item is legitimately deferred-with-anchor. After the 1st cure cleared the section-level blocked_by + reversed the stale review, the focus-picker STILL reported has_actionable_work: False. Item-level root (verified by running .claude/skills/continue-roadmap/roadmap_scan.py crawl_plan + Section.has_actionable_work / _predecessor_incomplete_subsection_ids directly on the plan): the block had moved to the SUBSECTION-ITEM level via the scanner’s Model-2 implicit-linear-execution chain (_predecessor_incomplete_subsection_ids gates EVERY subsection after the first non-complete one). §04.1 was the eligible front (first non-complete), but its ONLY remaining unchecked item (recursive_drop_skips_body_when_rc_above_one, the shared-reference AOT negative pin) is is_anchored=True + anchored_elsewhere=True — legitimately (deferred-with-anchor: BUG-04-043) + #[ignore = "BUG-04-043"] (BUG-04-043 = open tracked bug “Recursive tagged-pointer enums need box-and-load codegen for Construct/Project”). §04.1’s eligible-front-with-no-actionable-item state gated §04.2/§04.3/§04.4 (Model 2), hiding the two genuinely-actionable un-anchored typeck/codegen items L226 (§04.2 retire generate_env_drop_fn single-SSOT) + L362 (§04.4 Value field-constraint validator). Cure (ONE structural correction): flipped §04.1 sections[].status: in-progress → complete (frontmatter sections: array, NOT the section-level status:). §04.1’s implementation is complete — 9/10 items shipped 2026-05-17; the sole remaining item is a dependency-blocked AOT test pin that re-enables in the commit closing BUG-04-043 (deferred-with-anchor items do NOT block subsection close per test-disposition discipline + state-discipline.md §1 body-checkbox SSOT). The anchored item L162 STAYS [ ] with its BUG-04-043 anchor (NOT checked off — plan-complete.py --subsection was NOT used because it would have force-checked the anchored pin). Verified: post-flip has_actionable_work=True, eligible front advances to §04.2 surfacing L226 to the picker. §04.1’s AOT slice re-enables on BUG-04-043 close; §04.R TPR-04.R-006 (anchor-attribution disposition) remains the open work item.
  • 2026-05-29 — Linear-execution structural cure: de-scoped misattributed section blocker + reversed stale review. The LINEAR EXECUTION INVARIANT pinned §04 (in-progress) as the resume target but the focus-picker reported has_actionable_work: False. Structural root (verified 3× — §04.R finding TPR-04.R-006, bug-tracker/diagnostic-questions.md 2026-05-28 answer, and direct tracker read): the section-level blocked_by: [BUG-04-043] was MISATTRIBUTED + OVER-SCOPED. BUG-04-043’s tracker scope is “§07.3.A recursive tagged-pointer enum box-and-load codegen”; it genuinely blocks ONLY §04.1’s recursive-struct AOT slice (recursive_drop.rs), which is already gated at item granularity by per-item (deferred-with-anchor: BUG-04-043) + #[ignore = "BUG-04-043"] anchors. The section-level pointer redundantly re-gated the WHOLE section, suppressing genuinely-actionable, non-AOT, unblocked compiler items (§04.2 retire generate_env_drop_fn single-SSOT consolidation; §04.2 infer_lambda borrow-check-refinement sync; §04.4 Value field-constraint enforcement validator) that BUG-04-043 does not block. Cure (one structural correction returning §04 to its true state): (a) blocked_by: ["BUG-04-043"] → [] — the AOT-only items retain their per-item anchors; the actionable non-AOT typeck items surface to the picker; (b) record_review_reversal(new_status="in-progress") (state-discipline.md §4 atomic 3-field SSOT) reversed the now-stale reviewed: true (content drifted since the prior converged review — the 2026-05-29 editor pass removed subsection_depends_on edges + added 3 success criteria, and §04.R holds 2 open Major findings TPR-04.R-005/006), flipping reviewed: true → false + third_party_review → none + keeping status: in-progress. Without (b), clearing (a) alone would trip the picker’s needs_close_out premature-close-out gate (reviewed:true + empty classes). True AOT-slice blocker disposition for the per-item anchors (file-proper-bugs-vs-correct-citations) remains the open §04.R TPR-04.R-006 work item — unchanged by this cure.
  • 2026-05-29 — /review-plan Step 5 editor pass (Opus authority) — reversal-churn root resolved + 7 cures. Re-review re-establishing close-out after the content_hash drift-cure reversed the prior reviewed: true; the only material change since prior review was the blocked_by: [BUG-04-043, BUG-04-119] declaration. Cures: (1) opencode-F1 (COHESION ordering — reversal-churn root): removed the spurious subsection_depends_on edges 04.2/04.3/04.4 → 04.1. The four subsections are independent algorithmic surfaces (SCC recursive-drop, closure composer, Drop validator + AUGMENT, Value empty-burden) shipped on parallel register_*/compose_* sites on 2026-05-17, none consuming §04.1’s SCC-partition output; §04.2/§04.3 share only the mint_compiled_drop_fn_sym FnSym-mint helper (a sibling utility, not a produced-output dependency). The 04.x → 04.1 edges over-serialized and were what plan-cleanup rule #1/#4 kept reverting as “out-of-order completion”; the real gate is blocked_by: [BUG-04-043, BUG-04-119]. §04.R/§04.N edges retained (genuine close-out gates). (2) agy-F1: §04.3 success_criterion 6 panic-safety contract worded as shipped plain-call + ori_rt::rc::call_drop_fn guard with the invoke + landing-pad slice anchored to BUG-04-119 (not landing-pad-complete). (3) agy-F2: added §04.2 borrow-check-refinement sync criterion (owned_fields/borrowed_fields re-partition after borrow inference settles capture classification). (4) agy-F3: added §04.2 single-SSOT criterion to retire generate_env_drop_fn (closures.rs:189) and consolidate under DropKind::ClosureEnv (drop_gen.rs:94) per AIMS Invariant 5. (5) opencode-F2: reconciled the §04.N line-405 stale TPR-checkpoint claim with current frontmatter (third_party_review.status: none, reviewed: false). (6) opencode-F3 DECLINED: BUG-04-118 NOT added to blocked_by (CLOSED-as-obe in closed-bugs.json = DEAD_REF); blocked_by stays [BUG-04-043, BUG-04-119]; §04 body BUG-04-118 references reworded to past-tense/absorbed (obe; lambda-side infer_lambda wiring is the live follow-up anchor). (7) TPR-04.R-003 + TPR-04.R-004: §04.N index-status checklist line reconciled with in-review SSOT; §04.3 self-by-value rationale-tail replaced with drop-trait-proposal.md §Execution Timing citation. reviewed:/status: UNCHANGED (close-out flip is Step 7+8’s job).
  • 2026-05-22 → 2026-05-28 — Linear-execution rule #1/#4 auto-reversal churn (8 daily reversals, condensed). On each of 2026-05-22 (×2), 05-23, 05-24, 05-25, 05-26, 05-27, 05-28, plan-cleanup detected the same out-of-order subsection completion (04.2/04.3/04.4/04.N — and 04.R on the first 05-22 pass — marked complete while a predecessor was not), reverted those subsections + completion checklist to not-started, and flipped reviewed: true → false. Per-day cure detail lives in commit history; the recurring reversal was root-caused and resolved by the 2026-05-29 structural cures below (spurious subsection_depends_on edges removed + misattributed section blocker de-scoped). No further auto-reversals after 2026-05-28.
  • 2026-05-17 — /commit-push halt skipped at /tpr-review round 1 cure-apply. halt_reason: test_all_fail; failing repo: compiler_repo (test-all.sh red); scope: cross-scope (compiler_repo dirty-tree change in this round was a 1-line doc-comment update at compiler/ori_types/src/check/validators/partial_move.rs:13 mapping EDROP_PARTIAL_MOVE from stale E2044 to E2048/E2049 — cannot affect tests). Round 1 cures (9 findings: closure env layout, user_drop impl-site, Value-vs-Drop type-decl form, test-matrix negative-pin expansion, depends_on widening to §01/§02, work-order finding filed in §04.R, mission-criterion EBURDEN→EDROP fix, partial_move.rs doc-comment, Quick Reference §02/§03 status flip) remain in dirty tree. Per skill-control-contract.md §Autopilot Mode unified hook-failure clause: autopilot proceeds WITHOUT committing the round’s cures; cross-scope failing tests are owned by parallel session OR future user-typed /commit-push --bypass; round-loop advances on the live file state.
  • 2026-05-17 — /tpr-review rounds 2-5 cures landed inline (12 cures across 4 rounds). Round 2 (2 cures): per-type FnSym rewrite at §04.1 line 117 + success_criterion 1; register_user_types correction at §04.4 lines 287/325. Round 3 (3 cures): §04.1 line 102 per-type FnSym + topological reservation order; §04.4 Files block E2049 surface split (user_types.rs + impls.rs); touches: list expanded with user_types.rs + 4 AOT tests + 2 spec test dirs. Round 4 (4 cures): line-18 success_criterion E2049 dual-surface routing; SCC edges expanded for owned/borrowed/element/variant burdens; drop_augment test rewritten with Logged{tag:str} sub-type pinning 3-line ordering; Intel Recon citation markers ([ori] + [repo:path]). Round 5 (3 cures): §04.1 line 110 carves out non-recursive Drop types (compiled_drop required when user_drop = Some(_)); §04.1 line 108 schema field-name corrections (element_burden, retained_owned[i].field_type per burden.rs:40-49 verified); §04.3/§04.4 E2048/E2049 line citations corrected to 158/159. Cumulative: 21 cures across 5 rounds; section grew from 150-line scaffold (pre-editor) to ~370 lines of concrete design.
  • 2026-05-17 — /tpr-review cap-exit + /review-plan tooling-drift halt at step 6→7. /tpr-review round-loop ran full 5/5 rounds, exited as cap_reached_with_substantive per scripts/tpr_review_runtime/round_loop.py::should_exit(). /review-plan orchestrator _dispatch_tpr_exit_reason() rejected with halt_reason: tpr_exit_reason_drift because the runtime’s emitted value (cap_reached_with_substantive) is NOT in /tpr-review SKILL §1.7 canonical exit_reason enum (which lists cap_reached_max_rounds + cap_reached_meta_only). Diagnostic-question filed for /improve-tooling: align scripts/tpr_review_runtime/round_loop.py::should_exit() emission with /tpr-review §1.7 + /review-plan’s _dispatch_tpr_exit_reason() mapping table per decisions/31 Option C. Per tooling-first.md §1.1 meta-tool boundary: drift cure is /improve-tooling’s responsibility (not Claude-side enum substitution); banned by the orchestrator’s halt payload to “EDIT the marker to fabricate a canonical exit_reason”. third_party_review.status: cap_reached_with_substantive + review_pipeline.rounds_completed: 5 recorded in frontmatter for downstream audit. reviewed: stays false per state-discipline.md §4 atomic-flip — /review-plan Step 7+8 §00.3 close-out gate-flip awaits both (a) /improve-tooling resolution of the enum drift, and (b) parallel-session resolution of compiler_repo test_all_fail enabling /commit-push to land round 1-5 cures.
  • 2026-05-17 — Stale review_pipeline: marker cleared by /continue-roadmap orchestrator: marker carried stage: step-6-tpr, next_step: 7, updated: ?. Per /review-plan SKILL.md §Step 1a stale-marker rule (reviewed: false + marker present → STALE by definition), marker invalid; prior diagnosis preserved here for traceability. Cure rooted in scripts/plan_orchestrator/markers.py:clear_stale_marker_if_unreviewed.
  • 2026-05-17 — /review-plan Step 5 editor pass (Opus authority) — 5 blind-spot cures + audit-Critical re-affirmation. Step 1.7 integrity audit + Step 2/3 audit summary (/tmp/review-plan-up4/audit-summary.json) flagged Critical halt-gating finding TPR-04.R-001 (work-order violation: §04 in-review while §01 reviewed:false); Step 4 reviewers (codex + gemini; opencode capped at 16KB extraction-tier 4.5) surfaced 5 substantive blind spots. Editor disposition: (a) TPR-04.R-001 audit Critical — directive at §04.R “NEVER reviewed: true flip” preserved unchanged; editor did NOT flip reviewed: on §04 nor on §01 (per audit’s stated halt_required: true + proceed_despite_invalid: false); §04.R item re-affirmed in-place with explicit “directive holds” annotation; recorded here per state-discipline.md §1 plan-file SSOT discipline for /review-plan Step 7+8 verify gate. (b) codex finding #1 derived-surface drift — normalized every §04 + overview reference from derived.rs::register_derived_impl to the two non-derived surfaces (register_user_types Surface 1, register_impl Surface 2): goal frontmatter, subsection title sections[3].title + sections[4].title, touches list (removed derived.rs), §04.3 Files block (removed derived.rs, added impls.rs + ori_rt/src/rc/mod.rs for abort), §04.3 line 231 routing prose (clarified derived.rs::register_derived_impl NOT touched), success_criterion 10 unchanged (already correct), 00-overview.md:80 Mission Success Criteria entry. (c) codex finding #2 state-machine drift — promoted cap_reached_with_substantive / tpr_exit_reason_drift cure from HISTORY into §04.R as new BLOCKING item TPR-04.R-002-codex with Recommended cure (a) extending canonical enum + mapping table per decisions/31 Option C, and §04.N close-out gated on resolution. (d) codex finding #3 invalid drop_explicit pin — rewrote shared-reference recursive-drop test (line ~142) into named test recursive_drop_skips_body_when_rc_above_one using spec-canonical drop_early(value: n1) per compiler_repo/docs/ori_lang/v2026/spec/08-types.md:1145-1148; added explicit negative RC condition assertion (rc > 1 release decrements WITHOUT compiled drop body invocation). (e) gemini finding #1 Enum Drop Augmentation Leak — extended §04.3 Drop AUGMENT body shape + Files block + codegen test corpus to cover DropKind::Enum AUGMENT (without it, enum-shaped Drop types silently bypass user @drop body during ARC release); new codegen test drop_augment_enum_user_method_first_then_variant_field_walk_reverse_order. (f) gemini finding #2 Panic/Unwind Safety in Drop AUGMENT — added “Drop panic-safety — abort-on-any-drop-panic” subsection to §04.3 specifying codegen landing-pad-around-invoke lowering that completes field-walk on the unwind path + runtime ori_rt::ori_drop_double_panic_abort for nested-panic semantics matching drop-trait-proposal.md §Drop and panic; added codegen tests drop_augment_panic_in_user_drop_still_walks_fields (positive — field-walk completes despite @drop panic) + drop_augment_nested_panic_aborts (negative — nested panic during cleanup aborts). Editor authority: full mission-fit per feedback_review_plan_editor_must_do_mission_fit Section-Authoring Authority; bounded §1.7D cohesion-edits applied to 00-overview.md:80 only (non-target). §04 frontmatter status: in-review UNCHANGED (Critical halt + cap-exit + tpr_exit_reason_drift compound — editor pass DID NOT close); §01 reviewed: UNCHANGED (work-order directive forbids). Two §04.R BLOCKERS now gate §04 close-out: TPR-04.R-001 (predecessor §01 reviewed:false) + TPR-04.R-002 (tpr_exit_reason_drift tooling cure).
  • 2026-05-17 — §04.4 Value trait empty-burden + E2049 implementation shipped. Surface 1 (compiler_repo/compiler/ori_types/src/check/registration/user_types.rs::populate_value_burden_if_applicable): when decl.derives contains Value, queries TraitRegistry::has_impl(drop_idx, idx) and emits E2049 with the type-decl span when Drop impl is already registered; populates an empty UserBurdenSpec via register_user_burden(idx, UserBurdenSpec::default()); records the Value marker via new TypeRegistry::record_value_marker(idx) so Surface 2 can detect late-arriving Drop impls. Surface 2 (compiler_repo/compiler/ori_types/src/check/registration/impls.rs::populate_drop_burden_if_applicable): when registering impl T: Drop, queries TypeRegistry::carries_value_marker(self_type) and emits E2049 with the impl-block span when Value marker is already recorded; preserves §04.3 user_drop/compiled_drop wiring so the type’s burden stays internally consistent. E2049 wired through ori_diagnostic::ErrorCode::E2049 (slot 159) + TypeErrorKind::ValueDropConflict { type_name } (kind.rs + constructors in mod.rs + format.rs + message.rs + reporting/mod.rs) + errors/E2049.md imperative-fix doc matching E2042/E2048 pattern. New TypeRegistry::value_marker_types: FxHashSet<Idx> side-table (NOT a TypeEntry field — Value-carrying types are a minority; avoids widening every register_struct/register_enum/register_newtype constructor); record_value_marker / carries_value_marker accessors. Burden composition tests at registry/burden_compose/tests.rs: 2 new cells (result_value_heap_composition_inherits_distinct_variant_burdens, option_value_type_niche_encoded_inherits_empty_burden) — total 41 burden-compose tests pass. Registration tests at check/registration/tests.rs: 4 new cells (value_type_decl_with_drop_impl_emits_e2049_at_user_types_surface, drop_impl_for_value_type_emits_e2049_at_register_impl_surface, value_type_without_drop_impl_registers_empty_user_burden_spec, drop_impl_for_non_value_type_registers_user_drop_no_e2049) — total 44 registration tests pass. Cumulative ori_types lib tests: 1025 passed, 0 failed. ori_arc 1390, ori_llvm 652, ori_rt 367 — all pass. clippy clean (-D warnings) across ori_types/ori_arc/ori_llvm/ori_rt/ori_diagnostic. Deferred per §04.4 anchored items: spec tests at compiler_repo/tests/spec/aims/value_drop_conflict/ (anchored — depend on prelude wiring); AOT codegen test at compiler_repo/compiler/ori_llvm/tests/aot/value_empty_burden.rs (anchored — depends on AOT slice); §04.4 TPR checkpoint (anchored to §04 close-out). §04.4 status flipped in-progress → complete. §04.R BLOCKERS unchanged (TPR-04.R-001 work-order + TPR-04.R-002 tpr_exit_reason_drift still gate §04 close-out per §04.N).
  • 2026-05-28 — Autopilot auto-cure: review_plan_redispatch_loop detected; record_review_abort restored status to ‘in-progress’; advancing autopilot (per state-discipline.md §4 Hard-abort terminal-state semantics + skill-control-contract.md §Autopilot Mode unified hook-failure continuation clause).
  • 2026-05-29 — blocked_by restored to [BUG-04-119, BUG-04-043] (BUG-04-043 documented cure). The autopilot linear_execution_cure_synthesis loop non-converged on §04 (in-progress; subsections anchor-blocked; §04.1’s lone unchecked item is the genuinely-unwritten recursive-drop AOT pin deferred to BUG-04-043). Diagnosed as BUG-04-043 (open orchestrator-livelock bug, open-bugs.json:977) whose repro names the root contributing factor: §04 lacked a blocked_by frontmatter declaration. A prior cure-loop pass had de-scoped blocked_by → [] (incorrect — the opposite of the documented fix). Restored to the OPEN compiler blockers: BUG-04-119 (Drop-AUGMENT invoke/landing-pad codegen slice, §04.3) primary + BUG-04-043 (recursive-drop AOT pin, §04.1). BUG-04-118 dropped — closed-as-absorbed 2026-05-16. With the declaration present, the orchestrator routes to /fix-bug per CLAUDE.md §Plan-Blocker Bugs instead of re-emitting the identical cure/close-out (the livelock). §04’s incomplete slices are genuinely deferred-with-anchor to these open bugs; the migration (§03A→§09) proceeds once they clear.
  • 2026-05-30 — Autopilot auto-cure: phantom_ids=[§03A]; per decisions/10-target-self-drift-cure.md 2026-05-11 autopilot carve-out
  • 2026-05-30 — Autopilot auto-cure: review_plan_redispatch_loop detected; record_review_abort restored status to ‘in-progress’; advancing autopilot (per state-discipline.md §4 Hard-abort terminal-state semantics + skill-control-contract.md §Autopilot Mode unified hook-failure continuation clause).
  • 2026-05-31 — Stale review_pipeline: marker cleared by /continue-roadmap orchestrator: marker carried stage: ?, next_step: ?, updated: ?. Per /review-plan SKILL.md §Step 1a stale-marker rule (reviewed: false + marker present → STALE by definition), marker invalid; prior diagnosis preserved here for traceability. Cure rooted in scripts/plan_orchestrator/markers.py:clear_stale_marker_if_unreviewed.
  • 2026-05-31 — /review-plan Step 5 editor pass (Opus authority): blocked_by/HISTORY reconcile + misattributed-anchor cure + design-risk requirements + budget field. Verified against the bug-tracker (open-bugs.json + closed-bugs.json + on-disk plan dirs):
    • blocked_by ↔ HISTORY reconcile (opencode-F1). Frontmatter blocked_by: ["BUG-04-043"] is CORRECT and KEPT: BUG-04-043 is OPEN (“Recursive tagged-pointer enums need box-and-load codegen for Construct/Project”, plan dir present on disk) and genuinely gates §04.1’s recursive-drop AOT slice. The 2026-05-29 HISTORY claim that blocked_by was “restored to [BUG-04-119, BUG-04-043]” is the drift: BUG-04-119 is NOT a valid §04 blocker (its tracker scope is imported generic monomorphization — see misattribution cure below), so it is correctly ABSENT from blocked_by. Per-item AOT deferral anchors continue to carry their own blocker pointers.
    • Misattributed BUG-04-119 anchor (opencode-F3 + TPR-04.R-006). The §04.3 Drop-AUGMENT invoke+landing-pad AOT-slice deferrals had cited BUG-04-119 as their anchor. VERIFIED: BUG-04-119 is OPEN but tracks “AOT ori build lacks imported generic monomorphization — collect_mono_functions does not traverse import_sigs”, a wholly unrelated codegen surface; its plan_dir: bug-tracker/plans/BUG-04-119/ does NOT exist on disk. The forward-facing deferral anchors in §04 success_criteria 6+8, §04.3 panic-safety body, §04.3 + §04.4 Test Strategy, and §04.N were re-pointed (initially to a placeholder, then resolved in the subsequent 2026-05-31 editor pass). The properly-scoped bug for the Drop-AUGMENT AOT surface (invoke + landing-pad + __gxx_personality_v0 wiring around Apply(user_drop), re-establishing first-panic-recovery semantics per drop-trait-proposal.md §Drop and panic, closing the four drop_augment.rs AOT scaffolds) is BUG-04-125; the prelude Value+Drop registration for the value_drop_conflict / value_empty_burden spec slices is BUG-06-005. The #[ignore] reasons in compiler_repo/compiler/ori_llvm/tests/aot/drop_augment.rs are re-pointed from the misattributed BUG-04-119 to BUG-04-125 in the commit closing it. Dated §04.3/§04.N HISTORY entries that recorded “deferred to BUG-04-119” stay as the historical record (state-discipline.md §7); only the forward deferral anchors are repointed.
    • Design-risk requirements added as pre-execution - [ ] items (agy-F1/F2/F3 + codex-F2). §04 is in-review pre-execution; three implementation requirements surfaced as review blind-spots are added to the relevant subsections’ success criteria / checkboxes (NOT fake completion — they stay unchecked): (a) §04.3 self-recursion inhibition — the AUGMENT glue MUST suppress default ARC inc/dec on the by-value self param inside @drop bodies (else infinite cleanup recursion / double-free); (b) §04.3 whole-value-consumption invariant — validate_drop_partial_move MUST reject PARTIAL by-value match-destructure of Drop types (E2048), distinct from the allowed bind-all-fields case, to prevent double-free; (c) §04.4 transitive-Value validator MUST be cycle-aware (visited-set/memo keyed on type Idx) so recursive Value types terminate. The §04.4 silent-RC-drop architectural risk (ar-value-gate-silent-rc-drop) was ALREADY captured (success_criterion 11 + §04.N HARD close-out gate) — confirmed + strengthened with the cycle pin.
    • Budget field (TPR-04.R-005 / codex-F3). Added budget: {body_lines: 520, rationale} to frontmatter: the four BurdenSpec composition cases are one cohesive non-promotable scope (the §04 Subsection-independence analysis shows none consumes another’s output, so routing.md §4 promotion would fragment one drop-glue story across four boundary-less siblings); the budget justification is the lighter correct cure vs splitting an in-review section.
    • reviewed:/status:/third_party_review: UNCHANGED — review state NOT reversed; close-out flip remains Step 7+8’s job. No work checkbox fake-completed (§04 stays pre-execution).
  • 2026-05-31 — /review-plan Step 5 editor pass (Opus authority): BUG-04-043 closure reconcile + Drop-AUGMENT anchor resolution. Verified against the bug-tracker (BUG-04-043 in closed-bugs.json status=resolved, plan dir at bug-tracker/plans/completed/BUG-04-043/; BUG-04-125 in open-bugs.json status=open, “@drop panic during AOT field-drop / user-drop aborts (SIGABRT) instead of unwinding — plain-call drop emission lacks invoke+landing-pad wrapper”). Cures:
    • opencode-F1 (Critical) — blocked_by cleared. BUG-04-043 (recursive tagged-pointer box-and-load codegen) is now CLOSED-as-resolved; frontmatter blocked_by: ["BUG-04-043"] → []. The recursive-drop AOT slice is unblocked: recursive_drop.rs’s three shipped TDD pins (no #[ignore]) now run as active AOT tests; the not-yet-authored shared-reference pin recursive_drop_skips_body_when_rc_above_one (L168) becomes actionable work (author as active #[test]). §04.1 sections[].status stays in-progress (correct — BUG-04-043 closure converted its remaining items from deferred-with-anchor to actionable un-deferred, so §04.1 is NOT complete; body-checkbox SSOT agrees). Stale “BUG-04-043 confirmed OPEN” assertion in TPR-04.R-006 marked SUPERSEDED-IN-PART.
    • opencode-F3 (Major) — Drop-AUGMENT placeholder resolved to BUG-04-125; prelude Value+Drop resolved to BUG-06-005; lambda-side wiring resolved to BUG-02-033. Every Drop-AUGMENT invoke+landing-pad AOT-slice placeholder (success_criteria 6+7, §04.3 panic-safety body + Test Strategy, §04.4 Test Strategy, §04.N) re-pointed to BUG-04-125 (its scope IS the invoke+landing-pad @drop-panic-unwind surface). The prelude Value+Drop trait registration placeholders (§04.4 lines 404-405) are a DISTINCT slice NOT in BUG-04-125’s scope and are anchored to BUG-06-005 (filed this session; compile-time typeck-check / prelude-wiring scope, no silent scope-widening). The §04.2 lambda-side infer_lambda closure-UserBurdenSpec wiring follow-up is anchored to BUG-02-033 (filed this session). The drop_augment.rs:57 #[ignore] reasons (misattributed BUG-04-119) are re-pointed to BUG-04-125 by the implementer in the commit closing it.
    • opencode-F2 — §04.1 sections[].status reconciled. plan-complete.py --subsection 04.1 body-checkbox SSOT confirms §04.1 has unchecked, now-actionable (un-deferred-by-BUG-04-043-closure) items; sections[].status: in-progress is correct — NO flip to complete. State-discipline.md §1 body-checkbox SSOT honored (no inline sections[].status Edit).
    • gap-3 — burden_lower.rsburden_lower/. touches: + §04.2 Files-block citation updated from the flat compiler_repo/compiler/ori_arc/src/lower/burden_lower.rs to the directory-module burden_lower/ (verified on disk: mod.rs/emit.rs/moved_fields.rs/terminator.rs/tests.rs); is_owned_position SSOT now in ir/instr.rs+terminator.rs.
    • Candidate /add-bug items recorded in §04.R (editor cannot file bugs). (1) NEW closure-wiring follow-up: the closure_drop.rs:63/:84 pins cite now-CLOSED BUG-04-043 (stale-tracking DISPOSITION_DRIFT); their genuine residual blocker — the §04.2 lambda-side infer_lambda closure-UserBurdenSpec auto-registration wiring at blocks.rs:223 — is untracked (BUG-04-118 was CLOSED-as-obe), needs a properly-scoped open bug the parent files; pins stay #[ignore] (un-ignoring surfaces failing AOT tests). (2) agy-F1/F2/F3 are ALREADY covered by existing §04 success criteria / checkbox items (L307 match-pattern E2048, L370 transitive Value-membership, L232 closure drop-glue SSOT) — recorded as implementer cross-references, NOT scope-widening; file /add-bug only if confirmed real at implementation time.
    • reviewed:/status:/third_party_review: UNCHANGED — review state NOT reversed; close-out flip remains Step 7+8’s job. No work checkbox fake-completed.
  • 2026-06-02 — §04 close-out review + TPR-04.R-008 delivery + orchestrator-loop root cause.
    • §04 close-out /review-plan ran to verdict (Steps 1.7→9): verdict MINOR FIXES APPLIED, flip_from_in_review_clean set reviewed: false → true (status stays in-progress — §04.4 + §04.R/§04.N lifecycle remain). Section-close /tpr-review (3 reviewers + Opus adjudicator) converged CLEAN round 1, 0 actionable: the 8 raw findings adjudicated to false-positive / already-tracked §04.R follow-ups (TPR-04.R-008/009/010) / cross-section sibling-independence overreach. The adjudicator independently REFUTED the codex-F4+opencode-F1 “flip §04.4 to complete” cluster — §04.4 legitimately retains unchecked §04.N TPR-checkpoint items, so in-progress is correct.
    • Orchestrator §04-review-loop root cause (BUG-04-043 §04-shape, live). The roadmap picker repeatedly routes §04 → /review-plan pre-review via _emit_review_plan_for_pre_review (roadmap.py:6962 deadlock branch, routing_priority_decision: override_active_section_first). Diagnosis: §04 is the override-pinned active section; it is reviewed: true + carries a terminal verify-done review marker + close_out NOT resolved (§04.4 stuck in-progress, §04.R/§04.N unchecked) → the deadlock branch finds no advanceable subsection and force-routes to pre-review, which no-ops on the already-reviewed: true markdown and re-routes next scan. The emitted reason string “all subsections complete; reviewed: false” is STALE-rendered (the same envelope’s section_info.reviewed is correctly true); the genuine cure is NOT a picker patch but COMPLETING §04’s close-out so it leaves in-progress — once §04 is status: complete it is no longer the active deadlocked section and the loop ends. (This plan is markdown — no plan.json; the .md frontmatter is the SSOT.)
    • §04 AOT baseline GREEN (verified this session): recursive_drop 4 pass / 1 ignored (BUG-02-032), closure_drop 2 pass / 0 ignored, drop_augment 20 pass / 2 ignored (BUG-04-122, BUG-05-006), value_empty_burden 3 pass / 0 ignored — every #[ignore] carries a tracked BUG-ID.
    • TPR-04.R-008 DELIVERED (nested-destructure E2048 recursion). partial_subset_field only inspected the top-level arm pattern; a Drop-typed field partially consumed inside an outer whole-value destructure escaped E2048. Fix in ori_types/check/validators/partial_move.rs: extracted Idx-based Drop gate drop_type_name_for_idx (HYG §Algorithmic DRY); added recursive nested_drop_partial_move + nested_field_hit walking nested struct/variant sub-patterns over Drop-typed fields (gates each level on the NESTED field’s own Drop status; fires independent of outer scrutinee Drop status so a Drop field in a non-Drop outer is caught); restructured check_drop_match_destructure_at (top-level Drop-scrutinee-gated, nested runs regardless). TDD red→green matrix: 4 cells in check/integration_tests.rs (2 negative pins fired kinds: [] pre-fix; 2 clamps) + 2 spec .ori pins (nested_partial_match_destructure_{rejected,accepted}.ori; negative is BUG-07-183 runner-gapped so pinned via integration tests, positive passes interpreter + LLVM). Full ori_types lib 1044 pass / 0 fail, clippy clean, dual-exec parity confirmed. §04.R.1 TPR-04.R-008 item flipped to delivered.
    • Remaining for §04 close (resume pointer): (1) TPR-04.R-009 — author the recursive-type-WITH-user-@drop cross-case AOT pin (SCC compiled_drop invokes user_drop first then walks self-referencing child decs; once-only, no double-free; ORI_CHECK_LEAKS=1 + eval/LLVM parity) at ori_llvm/tests/aot/recursive_drop.rs. (2) §04.N rollup items L617-622 (AOT pins already green; flip the verifiable portions). (3) Flip §04.4 + §04.R + §04.R.1 + §04.N → complete → §04 status: complete. (4) Commit (cross-scope ori_arc realize/mod.rs clippy block from a parallel session → dirty-tree-continue per autopilot, log disposition). (5) Section-close /impl-hygiene-review on the partial_move.rs fix per Fix Completeness.
  • 2026-06-02 (firing 2) — TPR-04.R-009 delivered + §04.N parity/spec gates verified.
    • TPR-04.R-009 DELIVERED (recursive-type × Drop-AUGMENT cross-case composition pin). recursive_drop_with_user_drop_composes_no_double_free in compiler_repo/compiler/ori_llvm/tests/aot/recursive_drop.rs: a recursive Node carrying impl Node: Drop { @drop (self) -> void = (); }, 3-node chain dropped at scope exit. ORI_CHECK_LEAKS=1 oracle (via assert_aot_success) is the composition check — clean exit proves SCC compiled_drop + AUGMENT user_drop compose (once per node, children traversed once, no leak, no double-free). PASSES. Full recursive_drop suite 5 pass / 1 ignored (BUG-02-032). No composition defect → no /add-bug. §04.R.1 TPR-04.R-009 item flipped to delivered. Both §04.R follow-ups (TPR-04.R-008 nested-destructure E2048 + TPR-04.R-009 cross-case) are now delivered.
    • §04.N gate verification (close-out-finalize prep): L618 spec-green — tests/spec/aims/drop_augment/ + tests/spec/aims/value_drop_conflict/ pass 2/2 on BOTH interpreter AND --backend=llvm. L619 leak — the §04.1/02/03 AOT pins (recursive_drop 5+1, closure_drop 2, drop_augment 20+2, value_empty_burden 3) all run under ORI_CHECK_LEAKS=1 via assert_aot_success, zero leaks; every #[ignore] tracked (BUG-02-032, BUG-04-122, BUG-05-006). L620 parity — value_drop_conflict .ori both-backend + the recursive-Node-with-@drop program runs exit-0 on the interpreter (ORI_STDLIB=library/std) AND passes AOT; observable-behavior parity holds (drop-balance is the AOT-side property the leak oracle verifies). L573/L622 TPR — section-close /tpr-review ran clean round-1 (firing 1, MINOR FIXES APPLIED, reviewed:true). L617 all-04.X — algorithmic deliverables + AOT pins shipped/green.
    • Orchestrator loop persists (firing 2 confirm): fresh run-id eedd6f49… still routes §04 → /review-plan pre-review (override_active_section_first) because §04 is the reviewed:true deadlocked-pin section whose §04.4 + §04.N lifecycle remain. The regression-list cures (_independent_actionable_section_ids orphan-starvation, §04B State-C) do not cover this reviewed:true + deadlocked-pin path. Cure is unchanged: complete §04 close-out so §04 leaves in-progress.
    • Remaining for §04 close (resume pointer, firing 3): (1) Fix-Completeness code review of the §04 compiler diff (partial_move.rs nested-destructure recursion + recursive_drop.rs/integration_tests.rs pins) — /tpr-review code-mode then /impl-hygiene-review per L623 anchor (scope to the §04 work arc, not the cross-scope parallel-session ori_arc changes). (2) Flip §04.N checkboxes (L573, L617-L622) + §04.4 + §04.R + §04.R.1 → complete via plan-complete.py --complete-all. (3) §04 status: in-progress → complete (State-B flip). (4) /commit-push (cross-scope ori_arc realize/mod.rs clippy block from a parallel session → dirty-tree-continue per autopilot). Once §04 is complete it stops being the override-pinned deadlocked section → orchestrator advances to §04B/§05 (the §05→§09 green-CI critical path per LEDGER §B.3).