Skip to content

feat(vm-core): restore move-only Continuation — eliminate Arc/share_handle (K1, SPEC-VM-021)#475

Merged
proboscis merged 3 commits into
mainfrom
issue/vm-k1-restore-move-only-continuation/run-20260612-014853
Jun 12, 2026
Merged

feat(vm-core): restore move-only Continuation — eliminate Arc/share_handle (K1, SPEC-VM-021)#475
proboscis merged 3 commits into
mainfrom
issue/vm-k1-restore-move-only-continuation/run-20260612-014853

Conversation

@proboscis

Copy link
Copy Markdown
Owner

Summary

Restores the move-only Continuation law (SPEC-VM-021) that was eroded by PR #404's Arc<Mutex<Option<>>> + share_handle() mechanism. Preserves #404's exception-propagation semantics via a lighter-weight Py<PyK> handle approach.

Core changes (K1 coupling core — B2 ⇔ B3 ⇔ B6 ⇔ B10 ⇔ B12):

  • continuation.rs: Continuation.chain is now plain Option<DetachedFiberChain> — no Arc, no Mutex. share_handle() removed entirely. take() is self.chain.take() (Option::take, not lock+take).
  • value.rs: Added is_generator_handler() trait method to Callable — differentiates Python generator handlers (need PyK backup for exception recovery across yields) from synchronous Rust handlers (consume continuation directly via Value::Continuation).
  • step.rs: eval_perform / eval_perform_with_k branch on is_generator_handler():
    • Generator path (Python @do): wraps k in PyK, stashes Py<PyK> handle for exception recovery, passes Value::Opaque(PyShared(PyK)) to call_handler
    • Synchronous path (Rust): passes Value::Continuation(k) directly, no backup needed
  • vm.rs: pending_handler_chain_backup: Option<Continuation>pending_handler_k_handle: Option<Py<PyK>>
  • frame.rs: Frame::Program.chain_backuphandler_k_handle: Option<Py<PyK>>
  • python_generator_stream.rs: PythonCallable::is_generator_handler() returns true
  • invariants.rs: Rewritten to scan Py<PyK> handles instead of Arc cells

Bug fix: Stale-backup-leak — when a generator handler returns non-Expand DoCtrl, the backup handle now drops instead of persisting to corrupt the next Program frame.

Documentation: Decision record D19, constraint graph B3 row updated.

Acceptance Criteria Evidence

1. Move-only Continuation restored

  • continuation.rs: chain: Option<DetachedFiberChain> (line ~252), no Arc/Mutex anywhere
  • share_handle() removed — grep confirms zero occurrences
  • take() uses self.chain.take() (line ~277)

2. PR #404 exception-propagation preserved

All 6 regression tests pass:

test_handler_bind_exception_propagation.py (6 tests) — PASSED

3. Guard layer tests resurrected (6 tests)

test_move_semantics_architecture.py:
  test_chain_field_is_plain_option — PASSED
  test_no_shared_handle_api — PASSED
  test_one_shot_via_option_take — PASSED
  test_vm_does_not_store_continuation — PASSED
  test_frame_does_not_store_continuation — PASSED
  test_value_continuation_panics_on_clone — PASSED

4. Stale-backup-leak regression test

vm_tests.rs: test_stale_backup_does_not_leak_for_non_expand_generator_handler — PASSED

5. Full test suites

  • Rust (default features): 28 tests passed
  • Rust (invariant-checks): 36 tests passed (8 invariant tests)
  • Python integration: 541 passed, 1 pre-existing failure (unrelated doc test on main)
  • Python guard layer: 6 passed

6. Decision record & constraint graph

  • D19 added to docs/crystallization/decision-records.md
  • B3 row updated in docs/crystallization/constraint-graph.md

Files Changed (11 files, +549 −335)

File Change
continuation.rs Remove Arc/Mutex/share_handle, plain Option chain
value.rs Add is_generator_handler() trait method
step.rs Branch eval_perform on handler type; fix stale-backup leak
vm.rs pending_handler_k_handle: Option<Py<PyK>>
frame.rs handler_k_handle: Option<Py<PyK>>
python_generator_stream.rs Override is_generator_handler() → true
invariants.rs Rewrite for Py handle scanning
vm_tests.rs Add stale-backup-leak regression test
test_move_semantics_architecture.py Rewrite 6 guard layer tests
decision-records.md D19: K1 restoration
constraint-graph.md B3 row evidence update

Reference

Issue: vm-k1-restore-move-only-continuation

🤖 Generated with Claude Code

proboscis and others added 3 commits June 12, 2026 22:53
…andle (K1, SPEC-VM-021)

Restore the constructive move-only law for Continuation (SPEC-VM-021
invariants 1-4) that was eroded by PR #404's Arc<Mutex<Option<>>> backup
mechanism. The #404 exception-propagation semantics are preserved using a
Py<PyK> handle (a Python reference to the K object, not a continuation
copy).

Changes:
- continuation.rs: Remove Arc<Mutex<Option<DetachedFiberChain>>> →
  plain Option<DetachedFiberChain>. Remove share_handle().
- vm.rs: Replace pending_handler_chain_backup: Option<Continuation> →
  pending_handler_k_handle: Option<Py<PyK>>.
- frame.rs: Replace chain_backup → handler_k_handle: Option<Py<PyK>>.
- step.rs: Add is_generator_handler() branching in eval_perform and
  eval_perform_with_k. Generator handlers (Python @Do) use PyK wrapping
  + backup handle for exception recovery. Synchronous handlers (Rust
  CallableRef) receive Value::Continuation directly with no backup.
  Fix stale-backup-leak: only restore handle for DoCtrl::Expand results.
- value.rs: Add Callable::is_generator_handler() trait method.
- python_generator_stream.rs: Override is_generator_handler()=true for
  PythonCallable.
- invariants.rs: Rewrite to scan Py<PyK> handles instead of Arc cells.
- vm_tests.rs: Add stale-backup-leak regression test.
- test_move_semantics_architecture.py: Rewrite guard layer to assert
  SPEC-VM-021 invariants (6 tests, all passing).
- decision-records.md: D19 documenting the erosion and restoration.
- constraint-graph.md: Update B3 row evidence with new line numbers.

Fixes: vm-k1-restore-move-only-continuation

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
…est green

The guard layer in packages/doeff-vm-core/tests/ was CI-invisible:
not in pytest testpaths.  This is the dead-layer pattern that buried
the K1 erosion for 6 weeks.

Changes:
  pyproject.toml — add packages/doeff-vm-core/tests to testpaths.

Deleted files (guarding removed architecture — vacuously true):
  test_exception_enrichment_architecture.py
    Both tests reference vm_trace.rs which no longer exists.
  test_interceptor_chain_architecture.py
    Sole test references fn current_interceptor_chain which was removed.

Deleted tests within surviving files:
  test_dispatch_id_elimination_architecture.py
    - test_trace_state_runtime_has_no_preserved_dispatch_side_buffers
      trace_state.rs was removed in prior refactors.
  test_dispatch_origin_architecture.py (5 tests removed):
    - test_vm_runtime_has_no_dispatch_side_table_left
      References dispatch.rs and dispatch_state.rs (both removed).
    - test_current_interceptor_chain_hot_path_skips_dispatch_origin_view_materialization
      fn current_interceptor_chain no longer exists.
    - test_current_handler_identity_hot_path_skips_full_handler_chain_materialization
      References vm_trace.rs (removed).
    - test_start_dispatch_hot_path_skips_full_handler_chain_materialization
      fn start_dispatch no longer exists.
    - test_dispatch_resume_does_not_mutate_current_handler_caller_chain
      fn handle_dispatch_resume no longer exists.
  test_frame_based_traceback_architecture.py (3 tests removed):
    - test_trace_state_has_no_active_chain_assembly_state_wrapper
      trace_state.rs removed.
    - test_dispatch_display_lives_on_frame_snapshots_not_frame_dispatch_side_map
      trace_state.rs removed.
    - test_capture_module_has_no_capture_event_enum
      capture.rs removed.
  test_lexical_scope_architecture.py (1 test removed):
    - test_scheduler_spawn_path_no_longer_requests_get_handlers
      Rust scheduler module removed; scheduler is Python now.

Fixed tests:
  test_lexical_scope_architecture.py
    - test_handler_lookup_walks_parent_chain: pattern updated from
      "let next = seg.parent;" to "cursor = seg.parent;" matching
      actual dispatch.rs code.
    - test_spawn_reuses_live_fiber_chain_without_scope_cloning: removed
      stale ReturnToContinuation assertion (dispatch.rs no longer uses
      this variant for spawn).
  test_vm_module_split.py
    - Updated module list: removed vm_trace.rs, added handler.rs.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
@proboscis proboscis merged commit d97a6d7 into main Jun 12, 2026
1 check passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant