Qt-removal R7.2: relocate 33 Qt-free survivor modules out of graphlink_app/#140
Merged
Merged
Conversation
…hlink_app/ Moves everything backend/ has a live runtime dependency on - 17 flat modules (api_provider, graphlink_task_config, graphlink_licensing, graphlink_chart_agent, graphlink_chart_data, graphlink_chart_rendering, graphlink_chat_agent, graphlink_artifact_agent, graphlink_grid_view_settings, graphlink_navigation_pins, graphlink_prompts, graphlink_version, graphlink_audio, graphlink_model_catalog, graphlink_secrets, graphlink_token_estimator, graphlink_memory) plus the graphlink_plugins/ package - to sit as ordinary siblings of backend/ at the repo root, preserving their existing bare-name cross-imports unchanged. backend/__init__.py's sys.path.insert hack (which reached into graphlink_app/ for exactly this reason) is removed entirely - repo root is already on sys.path whenever backend/ itself is importable, so no path manipulation is needed for its new siblings either. content_codec.py stays exactly where it is (graphlink_app/graphlink_session/), NOT moved - graphlink_session/deserializers.py and serializers.py (Qt-tainted, still live) import it via a normal package-relative import that a physical move would break. Its logic is ported into backend/canvas.py instead (the same "reimplement, don't import" precedent backend/chat_library.py and backend/crash_recovery.py already follow), removing the raw importlib.util.spec_from_file_location workaround this replaces. graphlink_plugins/ moved WHOLESALE - all 19 files, including 3 still-Qt-tainted ones (graphlink_plugin_context_menu.py, graphlink_plugin_portal.py, web_research/worker.py). A design pass had proposed leaving those 3 behind to keep the new location Qt-pure; that broke immediately at collection time (ModuleNotFoundError) because Python resolves a dotted import like graphlink_plugins.graphlink_plugin_portal against ONE specific package - whichever directory of that name comes first on sys.path - and two same-named packages can never coexist. graphlink_plugins/ is therefore NOT 100% Qt-free at its new home; that closes at the R7.6 cutover. Two required fixes an earlier design/verification pass had missed entirely (it only checked backend/'s own consumption of these files, not graphlink_app/'s own internal consumption of the files being pulled out from under it): - graphlink_app/tests/conftest.py's sys.path.insert only reached graphlink_app/ itself; now inserts repo root too, or ~30 test files under graphlink_app/tests/ fail collection. - graphlink_app/graphlink_app.py (the legacy entry point) needed its own sys.path insert added BEFORE its first graphlink_* import (line 5, `from graphlink_window import ChatWindow` - not line 7 as first proposed; verified by actually importing the module and watching it fail at the wrong place), since graphlink_window.py itself transitively needs api_provider/graphlink_audio/graphlink_prompts/graphlink_navigation_pins/ graphlink_token_estimator. pyproject.toml: the 17 relocated modules are dropped from py-modules (setuptools' build_py only ever consults package_dir[""] for flat modules, and per-module package_dir overrides are silently ignored for modules - confirmed empirically - so there's no way to keep listing them while they live outside graphlink_app/; a real wheel/sdist build won't bundle them, a known, accepted, currently-inert gap since nothing in this project's pipeline runs `pip install -e .` today). packages.find now searches both graphlink_app/ and repo root, with a package-dir override for graphlink_plugins (package-dir[""] applies to every discovered package regardless of which `where` root found it, so without this a real build fails outright rather than silently mis-packaging - verified via a real `python -m build --wheel`, which now succeeds and includes all 19 graphlink_plugins/ files at their new path). The dynamic version resolution (`attr = "graphlink_version.APP_VERSION"`) does hard-fail a real build now - verified empirically, not guessed - since setuptools' attr: resolution for a bare module name only ever consults package_dir[""], which must stay "graphlink_app" for the ~89 modules still there; documented plainly as a known gap of the same tier, closing at R7.6. Also fixed: 5 backend/tests/*.py files had comments describing the now-removed sys.path mechanism (code was already correct, only the explanation was stale); graphlink_app/tests/test_ui_token_acceptance.py's hardcoded-hex/font allowlist had two entries for files that no longer live in the scanned tree (graphlink_grid_view_settings.py, graphlink_chart_rendering.py) - removed rather than left to fail the "still exists" assertion; CONTRIBUTING.md and the PR template's compileall/pytest instructions and graphlink_plugins/ path reference updated to match. Verification: 2375 pytest (identical count to before - no test lost or duplicated), compileall clean from repo root, qt_burndown.json's pin re-verified unchanged at 152/84/68 (relocating already-Qt-free files changes nothing the gate counts), 1053 vitest/tsc/eslint/build unaffected (no frontend touched). Both real entry points smoke-tested directly: importing graphlink_app/graphlink_app.py as a module (up to but not including main()) resolves every graphlink_* import cleanly, and backend.app.create_app() resolves api_provider/graphlink_task_config/graphlink_plugins.*/etc from their new repo-root location with zero sys.path setup. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
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.
Relocates everything
backend/has a live runtime dependency on — 17 flat modules (api_provider,graphlink_task_config,graphlink_licensing, the chart/chat/artifact agents,graphlink_grid_view_settings,graphlink_navigation_pins,graphlink_prompts,graphlink_version,graphlink_audio,graphlink_model_catalog,graphlink_secrets,graphlink_token_estimator,graphlink_memory) plus thegraphlink_plugins/package — to sit as ordinary siblings ofbackend/at the repo root. Their existing bare-name cross-imports (import api_provider, etc.) are unchanged;backend/__init__.py'ssys.path.inserthack that used to reach intographlink_app/for this is removed entirely.content_codec.py stays put, gets ported instead
graphlink_session/deserializers.py/serializers.py(Qt-tainted, still live) import it via a normal package-relative import that a physical move would break. Its 6 functions are ported intobackend/canvas.pyinstead — same "reimplement, don't import" precedentchat_library.py/crash_recovery.pyalready follow — removing the rawimportlib.util.spec_from_file_locationworkaround this replaces.graphlink_plugins/ moved wholesale, not selectively
An earlier plan tried to leave 3 still-Qt-tainted files behind (
graphlink_plugin_context_menu.py,graphlink_plugin_portal.py,web_research/worker.py) to keep the new location Qt-pure. That broke immediately —ModuleNotFoundErroron collection — because Python resolves a dotted import likegraphlink_plugins.graphlink_plugin_portalagainst one specific package, whichever directory of that name comes first onsys.path. Two same-named packages can't coexist. Sographlink_plugins/moved wholesale (all 19 files) and is not 100% Qt-free at its new home — that closes at the R7.6 cutover.Two required fixes a design/verification pass missed entirely
It only checked
backend/'s own consumption of these modules, notgraphlink_app/'s own internal consumption of the files being pulled out from under it:graphlink_app/tests/conftest.py'ssys.path.insertonly reachedgraphlink_app/itself — now inserts repo root too, or ~30 test files fail collection.graphlink_app/graphlink_app.py(the legacy entry point) needed its ownsys.pathinsert before its firstgraphlink_*import — verified by actually importing the module and watching it fail at the wrong line when placed one line too late.pyproject.toml
The 17 relocated modules are dropped from
py-modules(setuptools'build_pyonly ever consultspackage_dir[""]for flat modules — confirmed empirically that per-module overrides are silently ignored — so a real wheel build won't bundle them; a known, accepted, currently-inert gap since nothing in this project's pipeline runspip install -e .today).packages.findnow searches both roots with apackage-diroverride forgraphlink_plugins— verified via a realpython -m build --wheel, which now succeeds. The dynamic version resolution does hard-fail a real build now (verified, not guessed) since it can only ever resolve againstpackage_dir[""], which must stay pointed atgraphlink_app/for the ~89 modules still there — documented plainly as a known gap of the same tier.Verification
2375 pytest — identical count to before (no test lost or duplicated),
compileallclean from repo root,qt_burndown.json's pin re-verified unchanged at 152/84/68, 1053 vitest/tsc/eslint/build unaffected (no frontend touched). Both real entry points smoke-tested directly: importinggraphlink_app.pyas a module resolves everygraphlink_*import cleanly, andbackend.app.create_app()resolves everything from its new repo-root location with zerosys.pathsetup.R7.3 (port the 3 confirmed test-coverage gaps for now-relocated survivor logic into
backend/tests/) is next.