Add --output flag to graphify export html; dated report/HTML names in the Claude skill - #3428
Add --output flag to graphify export html; dated report/HTML names in the Claude skill#3428staypo wants to merge 1 commit into
Conversation
…mes in the Claude skill
The main build path (skill.md Steps 4-6) always wrote graphify-out/GRAPH_REPORT.md
and graph.html - generic names with no indication of what corpus they cover or
when they were generated, which gets confusing once a few reports pile up in the
same graphify-out/ directory.
- skill.md: new Step 2.6 computes a `{corpus-slug}-{YYYY-MM-DD}` basename once per
run (slug from the processed folder's name). Steps 4/5 write the markdown report
to `{base}-report.md` instead of GRAPH_REPORT.md; Step 6 passes the new --output
flag to `graphify export html` so the HTML lands at `{base}.html`.
- cli.py: `graphify export html` gains an --output PATH flag (reusing the shared
--output flag already parsed for callflow-html), letting callers choose the
target path instead of always writing graph.html. Purely additive - omit the
flag and behavior is unchanged.
- references/update.md: --update recomputes the basename (same slug, fresh date)
before reusing Steps 4-8.
- graph.json intentionally keeps its fixed name - it's read back in by --update,
--cluster-only, query, path, explain, --neo4j-push, --mcp, and the benchmark
step across sessions, so renaming it would break "ask questions weeks later."
Scoped out of this change (documented as a follow-up, not silently left broken):
`graphify cluster-only`/`label` and `--watch`/the git commit hook write
GRAPH_REPORT.md/graph.html directly from cli.py with their own stale-marker and
shrink-guard state keyed to those literal filenames. Renaming those safely is a
separate, larger change than this one.
Added two tests for the new flag (custom --output path, and --no-viz + --output
removing the right file). Full export/cli test suite passes (505 passed, 1
pre-existing unrelated failure from a missing optional `openai` dep in my test
venv).
There was a problem hiding this comment.
Graphify reviewed this change.
Looks safe to merge — no coupling regressions and no blocking issues, checked against the code graph (not a self-assessment).
Formal verification. No changes could be formally verified in this run.
Graphify review — findings
No blocking issues surfaced.
Analysis details — impact, health, verification
Impact & health
Graphify review
Impact — 381 functions depend on the 172 functions this change touches.
Health — this change adds coupling hotspots:
- new:
dispatch_command()— 2 callers, 124 callees - new:
_make_graph()— 35 callers, 6 callees - new:
_stale_graph_sources()— 7 callers, 6 callees - new:
_run_hook_guard()— 4 callers, 8 callees - new:
test_poisoned_manifest_is_healed()— 0 callers, 6 callees
Verification — 381 functions in the blast radius were not formally verified this run (proofs are advisory here).
Gate & verification
graphify gate
PASS — objectively clean (no health regressions, tests not run — proofs not run this pass (advisory)). Grounded, not self-assessed.
Advisory (not blocking):
- verification_scope: 327 function(s) in the blast radius were not formally verified this run
Test selection
Test selection
259 of 259 test file(s) selected (100%) via static blast radius.
Escalated to a full run for safety — the selection is not trustworthy on its own (see below). CI should run the whole suite.
tests/test_affected_cli.py— impact, full-run-safetytests/test_affected_member_seed.py— full-run-safetytests/test_agents_platform.py— impact, full-run-safetytests/test_analyze.py— full-run-safetytests/test_anthropic_custom_endpoint.py— full-run-safetytests/test_antigravity_install.py— full-run-safetytests/test_apm_fallback_version.py— full-run-safetytests/test_architecture_doc.py— full-run-safetytests/test_astro_extraction.py— full-run-safetytests/test_astro_import_ids.py— full-run-safetytests/test_atomic_canvas_export.py— full-run-safetytests/test_atomic_version_stamp.py— full-run-safetytests/test_atomic_writes.py— full-run-safetytests/test_backend_extras.py— full-run-safetytests/test_benchmark.py— full-run-safetytests/test_benchmark_raw_graph.py— full-run-safetytests/test_build.py— full-run-safetytests/test_build_merge_hyperedges_and_prune.py— full-run-safetytests/test_build_merge_shrink_guard.py— full-run-safetytests/test_builtin_global_type_refs.py— full-run-safetytests/test_cache.py— full-run-safetytests/test_callflow_html.py— full-run-safetytests/test_cargo_introspect.py— full-run-safetytests/test_carried_hyperedge_remap.py— full-run-safetytests/test_case_sensitive_resolution.py— full-run-safetytests/test_charmap_encoding.py— full-run-safetytests/test_chunking.py— full-run-safetytests/test_cjs_module_extension.py— full-run-safetytests/test_claude_cli_backend.py— full-run-safetytests/test_claude_md.py— full-run-safetytests/test_cli_broken_pipe.py— full-run-safetytests/test_cli_export.py— impact, changed-test, full-run-safetytests/test_cli_help.py— full-run-safetytests/test_cluster.py— full-run-safetytests/test_codebuddy.py— impact, full-run-safetytests/test_community_hub_labels.py— full-run-safetytests/test_community_labels_skill.py— full-run-safetytests/test_confidence.py— full-run-safetytests/test_corrupt_graph_json.py— full-run-safetytests/test_cpp_nested_and_cli.py— full-run-safetytests/test_cpp_objc_cross_file_calls.py— full-run-safetytests/test_cpp_preprocess.py— full-run-safetytests/test_cross_extension_reexport_self_cycle.py— full-run-safetytests/test_cross_language_call_resolution.py— full-run-safetytests/test_cross_repo_member_calls.py— full-run-safetytests/test_cross_repo_shared_types.py— full-run-safetytests/test_csharp_call_site_generic_args.py— full-run-safetytests/test_csharp_enum_members.py— full-run-safetytests/test_csharp_field_generic_args.py— full-run-safetytests/test_csharp_interface_dispatch.py— full-run-safety- … and 209 more
non-code file(s) changed (
graphify/skill.md,graphify/skills/claude/references/add-watch.md,graphify/skills/claude/references/hooks.md,graphify/skills/claude/references/update.md) → running the full suite for safety (a code graph can't see config/fixture/data deps)
changed code file(s) with no mapped test (
graphify/skill.md,graphify/skills/claude/references/add-watch.md,graphify/skills/claude/references/hooks.md,graphify/skills/claude/references/update.md) — a coverage gap or a missing link — running the full suite rather than only the selected tests
Selection is safe under the controlled-regression assumption; always-run tests + a periodic full run are the backstops. Advisory — it never changes the check verdict.
Formal verification
Could not verify: Could not verify dispatch\_command.
The verifier did not have enough to check dispatch\_command, so it is saying so rather than guessing. No false assurance is the whole point.
Guarantee: No guarantee either way, this is an honest abstention, not a pass.
Note: Reason: not verifiable: all 23 sampled inputs raised on both versions — the function never executed, so 'no divergence' would be vacuous (mostly SystemExit — names the real obstacle, not a sampling gap)
· 5 more finding(s) on lines outside this diff (see the check run).
Summary
The main build path (Claude
skill.mdSteps 4-6) always writesgraphify-out/GRAPH_REPORT.mdandgraph.html- generic names that say nothing about which corpus they cover or when they were generated. That gets confusing once a few runs pile up in the samegraphify-out/directory (which report is which?).This PR:
--output PATHflag tographify export html(reusing the shared--outputflag already parsed forcallflow-html). Omit it and behavior is byte-for-byte unchanged.skill.mdmain build path (new Step 2.6) to compute a{corpus-slug}-{YYYY-MM-DD}basename once per run, and use it for the markdown report ({base}-report.mdinstead ofGRAPH_REPORT.md) and the HTML export ({base}.htmlvia the new--outputflag).references/update.mdso--updaterecomputes the basename before reusing Steps 4-8.What's intentionally left unchanged
graph.jsonkeeps its fixed name. It's read back in by--update,--cluster-only,query,path,explain,--neo4j-push,--mcp, and the benchmark step across sessions - renaming it per run would break "ask questions weeks later."graphify cluster-only/labeland--watch/the git commit hook still write the fixedGRAPH_REPORT.md/graph.htmlnames. Both are implemented directly incli.pywith their own stale-marker and shrink-guard state keyed to those literal filenames (e.g. the "existing graph.html left unchanged" skip logic,#1392-adjacent guards). Renaming those safely is a bigger, separate change than this one - documented as a known scope gap inskill.mdand the relevant reference docs rather than silently left inconsistent.--svg/--graphml/--neo4j/--falkordbexports are untouched for the same reason (scope).Testing
Added two tests to
tests/test_cli_export.py:test_export_html_output_flag_writes_to_custom_path---outputwrites to the custom path and does not also creategraph.html.test_export_html_no_viz_removes_file_at_output_path---no-viz --output PATHremoves the file at the custom path.Ran the full export/cli suite against an editable install of this branch:
505 passed, 23 skipped(1 unrelated failure intest_ollama_retry_cap.pyfrom a missing optionalopenaidependency in my test venv, not from this change).Context
I'm a graphify user - I ran into the generic-filename issue while using the Claude skill to build several knowledge-graph reports back to back in the same directory and couldn't tell them apart. Happy to adjust scope/naming convention if you'd prefer a different approach (e.g. a dedicated flag name instead of reusing
--output, or a different slug/date format).