Skip to content

fix(workflow): invalidate subgraph cache on YAML file edit (#622)#635

Draft
eldar702 wants to merge 1 commit into
OpenBMB:mainfrom
eldar702:fix/622-subgraph-cache-mtime
Draft

fix(workflow): invalidate subgraph cache on YAML file edit (#622)#635
eldar702 wants to merge 1 commit into
OpenBMB:mainfrom
eldar702:fix/622-subgraph-cache-mtime

Conversation

@eldar702

@eldar702 eldar702 commented Jun 8, 2026

Copy link
Copy Markdown

Problem

Fixes #622. Editing a workflow YAML used by a subgraph node and re-running the subgraph executes the old version; only restarting ChatDev picks up the change.

workflow/subgraph_loader.py caches parsed subgraphs in a module-level dict _SUBGRAPH_CACHE keyed only on the resolved file path, with no staleness check:

if resolved_path not in _SUBGRAPH_CACHE:
    _SUBGRAPH_CACHE[resolved_path] = _load_graph_dict(resolved_path)
payload = _SUBGRAPH_CACHE[resolved_path]

Once a subgraph is loaded, the on-disk YAML is never re-read for the process lifetime — exactly the reporter's "stale until restart" symptom.

Fix

workflow/subgraph_loader.py — store the file's st_mtime alongside the cached payload and reload when the on-disk mtime changes:

mtime = resolved_path.stat().st_mtime
cached = _SUBGRAPH_CACHE.get(resolved_path)
if cached is None or cached[0] != mtime:
    _SUBGRAPH_CACHE[resolved_path] = (mtime, _load_graph_dict(resolved_path))
payload = _SUBGRAPH_CACHE[resolved_path][1]

The cache is preserved for unedited files (no extra parse on the common path) — the only new behavior is invalidation when the file actually changes. No new dependencies. 12-line change in 1 source file.

Test (RED-first)

Adds tests/test_subgraph_loader_cache.py:

  1. test_reload_after_edit_returns_new_content — write a subgraph YAML, load it (asserts v1), edit the same file (bumping mtime via os.utime to defeat coarse-clock filesystems), load again, assert it now reflects v2. This test is RED on main (returns the cached v1) and GREEN with this fix.
  2. test_unchanged_file_is_served_from_cache — proves an unedited file is not re-parsed, so the perf-oriented cache is retained.

Verified RED→GREEN locally:

# before the fix
tests/test_subgraph_loader_cache.py::test_reload_after_edit_returns_new_content FAILED
  AssertionError: subgraph_loader served a stale cached copy after the file was edited
  assert 'v1' == 'v2'

# after the fix
tests/test_subgraph_loader_cache.py::test_reload_after_edit_returns_new_content PASSED
tests/test_subgraph_loader_cache.py::test_unchanged_file_is_served_from_cache PASSED

Verification

  • uv run pytest tests/test_subgraph_loader_cache.py2 passed.
  • uv run pytest (excluding the pre-existing-on-main hanging tests/test_websocket_send_message_sync.py) → 56 passed. That websocket test hangs identically on a pristine main checkout and is untouched by this PR.

🤖 AI-assistance: Claude (Opus 4.8), reviewed

…BMB#622)

The module-level _SUBGRAPH_CACHE in workflow/subgraph_loader.py was keyed
solely on the resolved file path with no staleness check, so once a
subgraph YAML was parsed it was never re-read for the lifetime of the
process. Editing a workflow used by a subgraph node and re-running it
executed the stale version until ChatDev was restarted.

Store the file's mtime alongside the cached payload and reload when the
on-disk mtime changes. Unedited files are still served from cache, so
there is no behavior change for the common path.

Adds tests/test_subgraph_loader_cache.py with a RED-first regression test
(edit-then-reload returns the new content) plus a cache-preservation test
proving unchanged files are not re-parsed.
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.

Caching Issue with subgraphs

1 participant