fix: bring workflow_graph_ast_* infrastructure under the 40-line/method cap - #279
Merged
Merged
Conversation
…od cap Issue #275 (the 300-line/file cap on workflow_graph_source_ast.py) was already resolved on main by PR #278's follow-up commit (0702b77) — an independent split into ap_sync_loop.py / workflow_graph_ast_symbols.py / workflow_graph_ast_edges.py / workflow_graph_ast_response.py landed and closed #275 before this branch was rebased. Recreating this PR's own (differently-named) split would just duplicate already-merged work and conflict with it, so this commit keeps main's file layout and instead fixes what #278's split left over-cap at the METHOD level (CLAUDE.md's 40-line/method cap, docstrings included) — the defect PR #279 was refused for: a false ledger row claiming "§4.2 pass" while AST measurement found 4 functions over cap (up to 53 lines), and, once this branch's own rebase touched these files, 3 more pre-existing violations in already-merged code (up to 193 lines) surfaced as boy-scout material in the same touched files (coding-standards.md §14). Pure extract-method, behavior-preserving: - ap_sync_loop.py: _drain_pending_tasks (90) -> guard (_loop_is_drainable) + scheduling (_run_task_drain/_cancel_and_await_pending). - workflow_graph_ast_response.py: as_list (46) -> _zip_columns_rows + _from_legacy_shape; added build_path_tails (was duplicated inline in both symbols.py and edges.py — extracted once, DRY). - workflow_graph_ast_symbols.py: _symbol_type_from_label (41) -> a dispatch dict (_LABEL_TO_SYMBOL_TYPE, OCP-friendly over an if-chain); symbol_batches_async (127) -> _where_for_tails/_build_symbol_query/ _parse_symbol_batch; verify_symbols_async (44) -> _collect_all_symbol_names/_qualname_matches. - workflow_graph_ast_edges.py: edge_batches_async (193) -> rel-table enumeration split by family (_calls_and_member_rel_tables/ _import_rel_tables/_uses_rel_tables) + _run_edge; _run_edge (94) -> _build_edge_query/_edge_row_to_dict; _edge_row_to_dict (46) -> _split_edge_endpoints/_parse_provenance. Verification: - AST measurement: every function in all 5 files is <=40 lines, every file <=300 lines (ledger claim now TRUE by measurement, not asserted). - ruff check + ruff format --check: clean on all touched files. - pyright: 0 diagnostics on all touched files + the two consuming handlers (unified_search.py, wiki_verify.py). - Full suite: 6751 passed, 5 skipped, 0 modified/added (pure refactor). - Paired mutation-testing comparison (mutmut, same 3 test files) proves no regression: main's pre-refactor originals scored 244 killed / 460 survived / 876 total; this refactor scores 299 killed / 272 survived / 776 total — strictly more kills, strictly fewer survivors (dedup via build_path_tails + the dispatch-dict reduces mutable branch surface). The remaining ~272 survivors are a pre-existing, unchanged-in-kind gap (this suite targets the streaming/bounded-wait contract, not Cypher query-string construction or row-parsing detail — same class of gap the original PR's own before/after comparison already reported); closing it is a dedicated test-hardening effort, out of scope for a behavior-preserving cap-compliance fix (coding-standards.md §15.1). Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_012Yu6EnWspTfqHoGkExyS6u
cdeust
force-pushed
the
fix-split-workflow-graph-source-ast-275
branch
from
July 30, 2026 13:28
a2f7522 to
1d0f4a7
Compare
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
Issue #275 (the 300-line/file cap on
workflow_graph_source_ast.py) wasalready resolved on
mainby PR #278's follow-up commit (0702b77) — anindependent split into
ap_sync_loop.py/workflow_graph_ast_symbols.py/
workflow_graph_ast_edges.py/workflow_graph_ast_response.pylandedand closed #275 while this branch was still based on the pre-#278 tree.
Rebasing this branch's own (differently-named) split onto that state would
just duplicate already-merged work and conflict with it —
docs/module- inventory.mdalready lists all four modules — so this PR now keepsmain's file layout and instead fixes what was actually wrong: a false
ledger row in this PR's prior revision claimed "§4.2 pass" while AST
measurement found 4 functions over the 40-line/method cap (up to 53
lines), and once the rebase touched these files, AST measurement found 3
more pre-existing violations in the already-merged #278 split (up to 193
lines) — boy-scout material in the same touched files (coding-standards.md
§14).
Pure extract-method, behavior-preserving, no file renames/moves:
ap_sync_loop.py_drain_pending_tasks(90) →_loop_is_drainable(guard) +_run_task_drain/_cancel_and_await_pending(schedule/await)workflow_graph_ast_response.pyas_list(46) →_zip_columns_rows+_from_legacy_shape; addedbuild_path_tails(was duplicated inline in bothsymbols.pyandedges.py— extracted once)workflow_graph_ast_symbols.py_symbol_type_from_label(41) → a dispatch dict (_LABEL_TO_SYMBOL_TYPE, OCP-friendly over an if-chain);symbol_batches_async(127) →_where_for_tails/_build_symbol_query/_parse_symbol_batch;verify_symbols_async(44) →_collect_all_symbol_names/_qualname_matchesworkflow_graph_ast_edges.pyedge_batches_async(193) → rel-table enumeration split by family (_calls_and_member_rel_tables/_import_rel_tables/_uses_rel_tables) +_run_edge;_run_edge(94) →_build_edge_query/_edge_row_to_dict;_edge_row_to_dict(46) →_split_edge_endpoints/_parse_provenancePublic import paths are unchanged (
WorkflowGraphASTSource,_SyncLoop,_SYMBOL_LABELSall still resolve fromworkflow_graph_source_ast, whichthis PR does not touch — it had no over-cap functions).
Completion Ledger (coding-standards.md §13.2)
_drain_pending_tasksbehavior unchangedtests_py/infrastructure/test_workflow_graph_source_ast.py::TestDrainPendingTasks(5 tests) — all pass unmodified_SyncLoopconstruction/close/wedge-timeout contract unchangedTestSyncLoopConstruction,TestSyncLoopCloseRealLoop,TestBoundedWaitTimeout,TestSingleReaderOwnership— all pass unmodifiedas_list/build_path_tailsnormalization unchangedTestIncrementalYield,TestNoFullMaterialization(exerciseas_listindirectly viaiter_symbols/iter_ast_edges) — pass unmodified_symbol_type_from_labeldispatch-dict equivalencefunction→function,struct→class,Import→import) — same 3 branches as the original if-chain, now table-drivensymbol_batches_async/verify_symbols_asyncper-graph skip-on-error unchangedtests_py/infrastructure/test_ble001_sweep_infrastructure.py::TestWorkflowAstGraphSkips— pass unmodifiededge_batches_async/_run_edgeunchangedTestIncrementalYield::test_edges_yield_incrementally— pass unmodifiedVerification
ruff check+ruff format --check: clean on all touched files.pyright: 0 errors/warnings on all 5 infra files + the two consuminghandlers (
unified_search.py,wiki_verify.py).pytestfull suite: 6751 passed, 5 skipped, 0 modified/added (purerefactor — same behavior, same tests).
scripts/check_doc_claims.py/scripts/generate_repo_badges.py --check: both green, untouched (issue fix: eliminate the six-file test-count conflict class with a monotone floor (issue #293) #294's monotone floor isauthoritative on
main; this PR does not edit any doc/badge file).Mutation testing (§12) — paired before/after comparison
Scoped mutmut run (the 5 infra files vs.
test_workflow_graph_source_ast.pytest_ble001_sweep_infrastructure.py+test_s110_sweep_infrastructure.py),compared against an identical scoped run against
main's current (alreadyfix: drain pending tasks before stopping the AP sync loop (issue #258) #278-split, pre-this-PR) files, same test files:
main(pre-refactor)The refactor reduces survivors (460→272) and increases kills
(244→299) — consistent with deduplicating the path-tails logic
(
build_path_tails, previously copy-pasted in bothsymbols.pyandedges.py) and replacing_symbol_type_from_label's if-chain with adispatch dict (fewer independently-mutable branches). No regression.
The remaining ~272 survivors are a pre-existing, unchanged-in-kind
gap: this suite (by its own docstring) targets the streaming/ordering/
bounded-wait contract, not Cypher query-string construction or
edge/symbol row-parsing detail — identically true before and after this
PR (main's pre-refactor score already showed the same 460-survivor
shape). Closing it is a dedicated test-hardening effort (~300 new
assertions across query construction and row parsing), out of scope for
a behavior-preserving cap-compliance fix (coding-standards.md §15.1: a
large prerequisite is sequenced as its own PR, not mixed into this one).
Test plan
pytest tests_py/infrastructure/test_workflow_graph_source_ast.py tests_py/infrastructure/test_ble001_sweep_infrastructure.py tests_py/infrastructure/test_s110_sweep_infrastructure.py— 64 passed, 0 modified, 0 addedruff check+ruff format --checkcleanpyright0 diagnosticsWorkflowGraphASTSource,_SyncLoop,_SYMBOL_LABELS,symbol_batches_async,verify_symbols_async,edge_batches_async,as_list,build_path_tailsall resolveCo-Authored-By: Claude noreply@anthropic.com