Skip to content

refactor(wedge): extract claude_sdk_executor wedge state into runtime_wedge module - #2154

Merged
HongmingWang-Rabbit merged 4 commits into
stagingfrom
refactor/extract-wedge-state-from-claude-sdk
Apr 27, 2026
Merged

refactor(wedge): extract claude_sdk_executor wedge state into runtime_wedge module#2154
HongmingWang-Rabbit merged 4 commits into
stagingfrom
refactor/extract-wedge-state-from-claude-sdk

Conversation

@HongmingWang-Rabbit

Copy link
Copy Markdown
Contributor

Summary

Prerequisite for the universal-runtime refactor (task #87) to move `claude_sdk_executor.py` out of molecule-runtime into the claude-code template repo. `heartbeat.py` had a hard import:

```python
from claude_sdk_executor import is_wedged, wedge_reason
```

which would break the moment the executor moves out of the runtime package — the heartbeat would lose access to the wedge state used to flip workspace status to degraded.

Changes

  • `workspace/runtime_wedge.py` (NEW) — single-flag state + `mark_wedged` / `clear_wedge` / `is_wedged` / `wedge_reason` / `reset_for_test`. Same semantics as the original (sticky first-write-wins, auto-clear on observed success). 100 LOC.
  • `workspace/claude_sdk_executor.py` — drops in-file definitions; re-exports the same names from `runtime_wedge` as a backwards-compat shim. Any third-party adapter that imported the old names keeps working for one release cycle.
  • `workspace/heartbeat.py` — `_runtime_state_payload()` now imports from `runtime_wedge`. Lazy-import pattern preserved.

Tests (10 new)

  • Default state, mark sets flag, first-write-wins, clear restores healthy, clear-when-not-wedged is no-op, re-marking after clear is allowed
  • Re-export shim: each old name in claude_sdk_executor IS the runtime_wedge function (identity check), state is shared (marking via shim is observable via runtime_wedge and vice versa)

Verification

  • 1251/1251 workspace pytest pass (was 1241; +10 = exact match for the new test_runtime_wedge.py cases)
  • All existing test_claude_sdk_executor.py cases (which call `_mark_sdk_wedged` via the shim) still pass

Sequencing

After this lands + claude-code template image rebuilds with local executor (template PR #13), the molecule-core deletion of `workspace/claude_sdk_executor.py` becomes safe. The shim deletion ships alongside the file deletion in that follow-up PR.

🤖 Generated with Claude Code

…_wedge module

Prerequisite for the universal-runtime refactor (task #87) to move
claude_sdk_executor.py out of molecule-runtime into the claude-code
template repo. heartbeat.py had a hard import:

    from claude_sdk_executor import is_wedged, wedge_reason

which would break the moment the executor moves out of the runtime
package — the heartbeat would lose access to the wedge state used to
flip workspace status to degraded.

Extract the wedge state to a runtime-side module that the heartbeat
can keep importing regardless of which adapter executor is wedged:

  - workspace/runtime_wedge.py — single-flag state + mark_wedged /
    clear_wedge / is_wedged / wedge_reason / reset_for_test. Same
    semantics as the original claude_sdk_executor implementation
    (sticky first-write-wins, auto-clear on observed success). 100
    LOC of pure stateless helpers; lock-free ok because there's one
    executor per workspace process today.

  - workspace/claude_sdk_executor.py — drops the in-file definitions;
    re-exports the same names from runtime_wedge as a backwards-compat
    shim. Any third-party adapter that imported is_wedged / wedge_reason
    / _mark_sdk_wedged from claude_sdk_executor keeps working for one
    release cycle while they migrate to runtime_wedge.

  - workspace/heartbeat.py — _runtime_state_payload() now imports
    from runtime_wedge instead of claude_sdk_executor. Lazy-import
    pattern preserved; the docstring updated to explain the new
    cross-cutting source-of-truth.

Tests (10 new in test_runtime_wedge.py):
  - Default state (unwedged), mark sets flag, first-write-wins,
    clear restores healthy, clear-when-not-wedged is no-op,
    re-marking after clear is allowed
  - Re-export shim: each old name in claude_sdk_executor IS the
    runtime_wedge function (identity check), state is shared
    (marking via the executor shim is observable via runtime_wedge
    and vice versa)

Verification:
  - 1251/1251 workspace pytest pass (was 1241 after orphan deletion;
    +10 = exactly the new test_runtime_wedge.py cases)
  - All existing test_claude_sdk_executor.py cases (which call
    _mark_sdk_wedged via the shim) still pass

After this lands + the claude-code template image rebuilds with the
local claude_sdk_executor.py copy (template PR #13), the molecule-
core deletion of workspace/claude_sdk_executor.py becomes safe (the
shim deletion comes alongside the file deletion, since runtime_wedge
is the new public API).

See project memory `project_runtime_native_pluggable.md`.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Comment thread workspace/claude_sdk_executor.py Fixed
Hongming Wang and others added 2 commits April 27, 2026 00:12
…raded

Doc-only follow-up to the wedge-state extraction. Adds proactive
guidance so the next adapter (hermes / codex / langgraph / a future
template) discovers the runtime_wedge primitive and integrates the
~6 LOC pattern uniformly instead of inventing its own wedge state.

Two additions:

  - workspace/runtime_wedge.py — new "How to use from a NEW adapter"
    section in the module docstring with the minimum viable
    integration recipe, what-you-get-for-free list, and explicit
    DON'TS (don't store local wedge state, don't mark for transient
    errors, don't write your own clear logic). Plus a "when wedge is
    the WRONG primitive" note to keep adopters from over-using it.

  - workspace/adapter_base.py — adds runtime_wedge to the
    "Cross-cutting capabilities your adapter can opt into" list in
    BaseAdapter's docstring (alongside capabilities() and
    idle_timeout_override()). Discoverability path: adapter author
    reads BaseAdapter docstring → sees runtime_wedge mention → reads
    runtime_wedge module docstring → has the recipe.

Also tightens the "to add a new agent infra" steps in BaseAdapter to
match the actual current model (standalone template repo + ADAPTER_MODULE
env var) rather than the obsolete workspace/adapters/<infra>/ layout
that hasn't been the path since the universal-runtime extraction
started.

Zero code change. Tests untouched (1251/1251 still pass).

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
…oc + dedupe shim rationale

Three changes from /code-review-and-quality on PR #2154:

1. Optional (architecture): wrap state in a private _WedgeState class
   instead of bare module-level globals. Public API (mark_wedged /
   clear_wedge / is_wedged / wedge_reason / reset_for_test) is
   unchanged — adapters never see the class. The class is forward-cover
   for any future per-scope variant (multiple executors per process, a
   keyed registry, etc.) without churning the call sites. Today there's
   exactly one instance (_DEFAULT) so behavior is identical.

2. Optional (readability): clarify the import path in the integration
   recipe — in a TEMPLATE repo it's `from molecule_runtime.runtime_wedge`
   (PyPI package); in molecule-core itself it's `from runtime_wedge`
   (top-level module). Removes the trap where a contributor reading the
   docstring while editing in-repo copies the template-style import and
   gets ImportError.

3. Nit (readability): dedupe the shim rationale. claude_sdk_executor's
   re-export comment now points to runtime_wedge's "Compatibility shim"
   section as the source of truth instead of restating the same content.
   Avoids docs-in-two-places drift risk.

Verification:
  - 1251/1251 workspace pytest pass (no behavior change — class wrap
    is pure plumbing; module-level helpers delegate to the singleton)
  - All shim re-export identity tests still pass (the shim's
    `is_wedged is runtime_wedge.is_wedged` assertion holds because we
    re-export the SAME function object that delegates to _DEFAULT)

No new tests needed — the existing test suite covers the public API
contract; the class is an implementation detail behind that contract.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Comment thread workspace/claude_sdk_executor.py Fixed
Addresses github-code-quality unused-import flag on the runtime_wedge
re-export shim.  Adds __all__ listing the names that exist purely for
backwards-compat (is_wedged / wedge_reason / _reset_sdk_wedge_for_test)
so static analysis recognizes the imports as deliberate exports.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
@HongmingWang-Rabbit
HongmingWang-Rabbit added this pull request to the merge queue Apr 27, 2026
Merged via the queue into staging with commit 66b9c04 Apr 27, 2026
14 checks passed
@molecule-ai
molecule-ai Bot deleted the refactor/extract-wedge-state-from-claude-sdk branch May 20, 2026 06:22
HongmingWang-Rabbit pushed a commit that referenced this pull request Jun 12, 2026
…n firing loop (#2149)' (#2154) from regression/2149-scheduler-real-pg into main
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