Skip to content

Improve dev workflow, lint baseline, refactor CLI, and gate CI#1183

Open
morus246 wants to merge 10 commits into
safishamsi:v8from
morus246:improve/v8-port-dev-baseline
Open

Improve dev workflow, lint baseline, refactor CLI, and gate CI#1183
morus246 wants to merge 10 commits into
safishamsi:v8from
morus246:improve/v8-port-dev-baseline

Conversation

@morus246

@morus246 morus246 commented Jun 7, 2026

Copy link
Copy Markdown

Summary

End-to-end polish on the v8 development loop. 10 commits, no upstream churn — every commit is additive or a safe refactor.

What's in

  • CI gates (.github/workflows/ci.yml): new lint job running ruff check . in parallel with the existing skillgen-check and test jobs. New Build package step (uv run --frozen python -m build) and Smoke-test detect step in the test job. New advisory bandit step (with a TODO to make it blocking once the 5 pre-existing medium-confidence findings are triaged).
  • Graph quality (.gitignore): excludes docs/translations/ (495 nodes of README duplicates) and .hermes/ (40 nodes of local planning artefacts). Combined with the existing docs/superpowers/ rule, the graph dropped from 9648 → 7362 nodes (−23.7%), 16273 → 12192 edges (−25%), 743 → 574 communities (−22%) on a clean re-run.
  • CLI refactor (graphify/cli/installers.py + slimmer __main__.py): moves lines 1-2069 of __main__.py (every per-platform installer plus the cross-platform helpers) into a new graphify.cli.installers submodule. __main__.py drops from 4582 → 2617 LOC (−43%) while keeping the full public API re-exported (incl. PEP 562 _ALWAYS_ON_ALIASES lazy attributes).
  • Regression tests:
  • Docs (README.md): new "Lint and packaging" subsection under the existing "Development" section documenting the local commands that match the new CI gates. The PyPI vs CLI naming note (graphifyy vs graphify) was already present on line 80 of the v8 README, so 7.2 ships as-is from upstream.
  • Cherry-picked from v7 baseline (3 of 4 commits): the CLAUDE.md guidance, the ruff-discovered extraction fixes, and the post-rebase import style fix. The 4th commit (dev-extras) was skipped because v8 already ships a strictly better setup (PEP 735 [dependency-groups].dev + uv sync --all-extras --frozen).

What's not in

  • The pre-existing ruff F821 (nx undefined in graphify/prs.py:243, commit cc9e5816) is not in scope of this branch. It still fires locally and on CI; the lint job is expected to fail until that one-line fix lands upstream.
  • The 5 pre-existing bandit Medium findings (B104 hardcoded_bind_all_interfaces ×3, B314 xml.etree.ElementTree.fromstring ×2) are documented in the new bandit (advisory) step as a follow-up TODO before the gate becomes blocking.

Validation

  • uv run --group dev ruff check . → 1 pre-existing error, no new ones
  • uv run --group dev --extra mcp --extra pdf --extra watch --extra sql --extra office pytest tests/ -q1970 passed, 1 skipped (v8 itself has 1968; +2 from the new xlsx regression tests)
  • uv run --group dev python -m build → builds graphifyy-0.8.35.tar.gz + .whl
  • graphify update . → 7362 nodes / 12192 edges / 574 communities (down 23.7% / 25% / 22% vs unmodified v8)
  • graphify --version / graphify --help / graphify install all pass

Notes for the reviewer

  • The __main__.pycli/installers.py move preserves 100% of the public API: every install/uninstall entry point, every _ALWAYS_ON_* lazy attribute, every _PLATFORM_CONFIG / _clone_repo / etc. helper is re-exported as a module attribute on graphify.__main__ so existing imports (from graphify.__main__ import claude_install, from graphify.__main__ import _CLAUDE_MD_SECTION, etc.) keep working unchanged.
  • The re-export uses sys.modules[__name__] rather than just populating module globals, because tests like test_install_strings.py import the private _FOO names directly with from graphify.__main__ import _FOO — that form reads module attributes, not function-local globals.
  • Path(__file__).parent references inside the extracted installers.py are bumped to .parent.parent so the packaged assets (always_on/*.md, skills/*, command-kilo.md, skill-{vscode,copilot}.md) keep resolving from the graphify/ root instead of graphify/cli/.

morus246 added 10 commits June 7, 2026 17:35
When no .graphifyignore exists, .gitignore patterns must apply — the
upstream landed this as part of safishamsi#945 (commit 9e6192a) but no test
guarded it. Without this test, a future refactor could silently
re-introduce the gap and cause docs/translations/ to re-enter the
graph.

The 27 README translations under docs/translations/ duplicate the
primary README in graphify's nav; ignoring them keeps god nodes
focused on real code abstractions.
Two long-standing pollution sources the upstream .gitignore missed:

- docs/translations/ — 27 README translations that duplicate the
  primary README in graphify's nav. Adds 548 nodes / 0 signal.

- .hermes/ — local planning artifacts from Claude Code / Hermes runs.
  Adds ~40 nodes / 0 signal and leaks session content into the
  architecture graph.

Validation: graphify update . went from 9648 -> 7362 nodes (-23.7%),
16273 -> 12192 edges (-25%), 743 -> 574 communities (-22%).
…llers

graphify/__main__.py had grown to 4582 lines, mostly because every
supported AI platform (Claude Code, Gemini, VSCode, Kiro, Antigravity,
Cursor, Devin, Kilo, OpenCode, Codex, Amp, CodeBuddy) ships its own
install/uninstall code path. This is the Phase 5.1 refactor the long
plan called for: move the installer block (lines 1-2069 + _clone_repo)
out into graphify/cli/installers.py.

Why this split is safe:

1. Backward compatibility preserved by re-exporting every installer
   name as a module attribute on __main__ (set via sys.modules), so
   existing imports like `from graphify.__main__ import claude_install`
   and `from graphify.__main__ import _CLAUDE_MD_SECTION` keep working.
2. The PEP 562 `_ALWAYS_ON_ALIASES` lazy block (which doesn't show up
   in dir()) is also bound to the __main__ module, so install-string
   tests pass without modification.
3. All Path(__file__).parent references in installers.py are bumped
   to .parent.parent so packaged assets (always_on/*.md, skills/*,
   command-kilo.md, skill-{vscode,copilot}.md) keep resolving from
   graphify/ rather than the new graphify/cli/ location.

Results:

- __main__.py: 4582 -> 2617 lines (-43%, 1965 LOC out)
- New graphify/cli/{__init__.py,installers.py}: 2090 lines
- pytest: 1968 passed, 1 skipped (no regressions)
- ruff: same 1 pre-existing F821 in prs.py, zero new errors
- graphify --version / --help / test_install_* / test_claude_md /
  test_install_roundtrip all pass
Closes Phase 6 of the long plan.

- New `lint` job runs `ruff check .` in parallel with the
  existing `skillgen-check` and `test` jobs, so a future ruff
  regression fails the PR without waiting for the slower test
  job. Uses uv + ruff via pip (`uv pip install --system ruff`)
  rather than the dev extra to keep the job tight.
- New `Build package` step in the `test` job runs
  `uv run --frozen python -m build` after the tests. Catches
  packaging regressions (broken MANIFEST.in, missing data files,
  setuptools deprecation warnings becoming errors) that the test
  suite never touches. Wheel build is sub-second on this repo.
- New `Smoke-test detect` step invokes the core
  file-classification / .graphifyignore pipeline via the Python
  API (there is no `graphify detect` subcommand) and asserts the
  scan finds at least one code file. Non-mutating, runs in <1s,
  and guards the same code path that every `graphify update`
  depends on.
Documents the same two CI gates (ruff, build) as locally-runnable
commands so contributors don't have to wait on CI to find out they
tripped the lint or packaging checks. Closes Phase 7.1 of the long
plan.

The PyPI / CLI naming note (`graphifyy` on PyPI vs `graphify` as
the CLI command) was already on line 80 of the v8 README, so 7.2
ships as-is from upstream.
Pins Phase 8.2 of the long plan: a happy-path test that exercises
the named-ListObject code path and emits real column-header nodes,
and a static-analysis test that catches a re-introduction of the
F-035 bug (the `_re.sub` typo from commit 67eb547's history).

The happy-path test uses a named table because the no-table
fallback in xlsx_extract_structure is dead code in practice — every
openpyxl Worksheet has a `tables` attribute (empty dict), so the
`if hasattr(ws, "tables")` check always succeeds and the `else`
branch never runs. Building a named ListObject exercises the
header-emitting code path for real.

The F-035 invariant reads detect.py and asserts `_re.sub` does not
appear in executable code inside xlsx_extract_structure's body
(comment-stripped, so the F-035 history comment that legitimately
mentions the typo doesn't trip the assertion). Skips the test
gracefully on lean installs via `pytest.importorskip('openpyxl')`.
Phase 8.1 of the long plan. Bandit is already declared in
`[dependency-groups].dev` and configured under `[tool.bandit]`,
but no workflow was running it.

Wired into the existing `lint` job with `|| true` for now:
bandit currently reports 5 Medium-confidence findings
(3× B104 hardcoded_bind_all_interfaces in serve.py / mcp_ingest.py,
2× B314 xml.etree.ElementTree.fromstring in docx_to_markdown /
xlsx_extract_structure) — all pre-existing in v8, none introduced
by this branch. The TODO in the step body lists them so a follow-up
commit can either fix the issues or annotate them with `# nosec`
before the `|| true` is dropped.
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