Skip to content

Architecture Map, Flow | Graph codebase view, and separate test graph - #19

Merged
askuma merged 8 commits into
mainfrom
feat/flow-view-and-test-graph
Oct 23, 2026
Merged

askuma merged 8 commits into
mainfrom
feat/flow-view-and-test-graph

Conversation

@askuma

@askuma askuma commented Oct 23, 2026

Copy link
Copy Markdown
Owner

Summary

Adds the Architecture Map and reworks the codebase graph section of the generated WORKFLOW.html so it's easier to read.

Architecture Map (components)

  • Semantic graph builder with new detectors (crons, tools, agents, composition root), rendered as a layered left-to-right map with grouped components.
  • "Show import details" jumps from a component to the exact files behind it.
  • Stores are connected to the services that use them, including through a generic ORM session.
  • Opt-in LLM label enrichment through a local CLI; it does nothing when the CLI isn't installed.
  • The Integrations strip and legend now sit above and below the map instead of on top of nodes.

Codebase (files): Flow | Graph toggle

  • One section with two views of the same nodes and edges:
    • Flow: a layered left-to-right diagram. Columns are dependency depth, every detected service (each LLM, store, queue, integration) is its own node in the last column, and import cycles are dashed. Crossings are reduced with barycenter sweeps.
    • Graph: the existing force-directed view.
  • Search, "hide isolated", reset and the selected node are shared between the views. The explanatory text and legend switch with the active tab.
  • Data movement is animated in both directions: a filled dot for the call/request, a hollow dot for the return/response. Graphs with more than 60 edges only animate the hovered or clicked node's paths. Animation respects prefers-reduced-motion and pauses off-screen.
  • Flow is the default up to 80 nodes; larger or directory-aggregated graphs open on Graph.

Tests (files)

  • Test files (tests/, test_*.py, conftest.py, *.spec.ts, *_test.go, ...) are split into their own section with the same Flow | Graph toggle, so fixtures no longer flood the main graph with service edges. On this repo the main graph drops from 123 to 35 edges.

Page polish

  • Section titles are 16px (from 13px) and explanatory text 13px (from 11px).
  • Backgrounds are plain (no dot grids); in light mode the page tone is set apart from the white panels.
  • The older Full System Architecture diagram is collapsed by default, since the Architecture Map covers it.

Testing

  • python3 -m pytest: 122 passed, including new tests for the test/source split and the merged section markup.
  • Checked in headless Chrome: no JS errors, node/edge counts match between views, tabs and drill-down work, and the Flow dots render.

askuma added 8 commits August 26, 2026 19:27
Implement core semantic graph generation following foglamp.dev/scan schema:
- New detectors: detect_crons(), detect_tools(), detect_agents(), detect_composition_root()
- build_semantic_graph() generates 14-node curated architecture map from detected components
- Creates entry (routes), service, store, external, cron, agent, model, tool nodes
- Generates edges with kinds: triggers, calls, reads, writes
- Tracks component membership for drill-down filtering
- Returns foglamp-compatible schema (project, stats, topModels/Tools/Integrations, graph)

Verified on fastapi/full-stack-fastapi-template: 12 nodes, 21 edges, 0 agents/models/tools
Next: render semantic graph in HTML with drill-down UI and import details toggle
Add SEMANTIC_GRAPH_JS renderer: deterministic column-by-kind SVG layout
(cron/entry -> agent/service -> model/tool -> store/external), grouped
node clustering with accent colors, click-to-detail panel, and a "Show
import details" button that isolates the corresponding files in the
existing Codebase Graph section below (via a new isolate-nodes hook added
to GRAPH_JS) instead of duplicating a second graph engine.

Fix bugs found via headless-Chrome visual + interaction testing (CDP):
- group tags were appended to the wrong SVG parent, rendering off-canvas
- entry/service linking was N x M fan-out instead of real import-based
  correlation (checked per-file text reference to the service module)
- entry-node grouping used a URL-path-prefix heuristic that breaks when
  routers don't repeat their mount prefix in the route decorator string;
  replaced with file-based grouping (router filename -> feature name)
- store/external branding used a nonexistent domain field from the
  detectors; added a static brand-domain lookup instead
- external nodes included detect_external_sources()'s synthetic "Users /
  API Clients" and "Cron / Scheduler" entries, which aren't real
  third-party integrations
- cron nodes fanned out to every service in the repo with no real
  evidence connecting them; now only link to the composition root

Add 27 tests covering all 4 new detectors and build_semantic_graph(),
including regressions for each bug above. Full suite: 107 passed.

Verified end-to-end against fastapi/full-stack-fastapi-template via
headless Chrome and the DevTools protocol: click a semantic node, panel
opens, click "Show import details", and the exact files are isolated and
highlighted in the Codebase Graph's existing search/isolate mechanism.
…derer

Screenshotted the actual foglamp.dev/scan output (headless Chrome +
software WebGL) since it hadn't been visually inspected before, and it
looked meaningfully different from the first implementation:

- Real layout reads left to right by data flow (source -> logic -> sink),
  not by a fixed kind-based column grid. Replaced the column assignment
  with a proper Sugiyama-style topological layering: each boxed group
  collapses to one meta-node, Kahn's algorithm assigns a longest-path
  layer, then real nodes inherit their meta-node's layer and expand back
  out. Fully isolated nodes (no in or out edges, e.g. a store nothing was
  confidently linked to) are pushed to the last layer instead of
  defaulting to layer 0, where they misleadingly read as a source.
- Groups render as an actual bounding box around their stacked members
  (only when a group has >=2 real members - a "group" of one is just a
  plain node, not a box), with a short internal arrow between two members
  when a real edge connects them - not just a shared accent color.
- Added small inline-SVG icon glyphs per kind (lightning bolt, filled dot,
  db-cylinder, globe) in a colored chip, replacing the old kind-badge text
  in the corner - no external icon font or network fetch.
- Edges are visible by default (not dimmed until hover) with an
  always-on-brand color for labeled edges, plus a genuine per-edge
  animated flow dot via native SVG <animateMotion> along an <mpath> - the
  arrow animation from the reference site, with no extra JS animation
  loop needed.
- Legend restyled as pill chips (Triggers / Services / Stores / External)
  matching the reference's fixed bottom bar language, deduplicated so
  entry+cron don't produce two "Trigger" pills.

Verified via a synthetic 2-node-per-group fixture (the real fastapi
template's deterministic detector doesn't happen to produce a >=1-member
group large enough to exercise box rendering) - screenshots at each step
confirm boxes, icons, and labeled edges all render as intended. Full
suite still 107/107; click-to-detail-panel and the import-details
drill-down re-verified working after the rewrite.
… output

Compared against an actual foglamp.dev/scan screenshot and closed the
remaining gaps: gateway and frontend nodes, synthesized Sentry and SMTP
sink nodes from the composition root, a working CSS dash-flow animation
on edges (the JS scaffolding from the prior session had no matching
keyframes), fan-out-aware edge anchoring, a dotted-grid canvas
background, a bottom-pinned pill legend, a floating Integrations card,
and muted-gray edge labels instead of pink.

Also fixes an UnboundLocalError in topIntegrations construction that
had never been exercised end-to-end before, with a regression test.
The service->store edge only fired when the service file spelled out
the store's name literally (e.g. "postgres" in crud.py). That misses
the common case of a layered app where the connection lives in a
separate db/core module and the service only ever sees a Session/db
object (SQLModel, SQLAlchemy, Django ORM, Mongoose, etc.) - Postgres
was showing up completely disconnected in the FastAPI template demo
because of this.

Add a fallback: when a service shows generic session.add()/.exec()/
.objects.create()-style usage and there's exactly one non-cache store
detected repo-wide, link them. With more than one candidate store the
link stays unmade rather than guessing which one is meant.
Architecture Map service-node labels were purely mechanical: a fixed
stem dictionary (crud/services/service/repository/dao) or nothing.
Generically-named business-logic files got no label improvement at all.

Add enrich_service_labels(): opt-in, shells out to the user's local
`claude` CLI in headless mode (-p --output-format json) to relabel
existing service nodes with content-derived labels instead of the
stem dictionary. Never invents new nodes/edges. No-op whenever `claude`
isn't on PATH, the call times out, or the response is malformed --
falls back to the deterministic labels, never raises.

Wired through all four distribution surfaces identically:
- CLI: --enrich-labels flag
- MCP: enrich_labels parameter on generate_workflow
- Copilot extension: enrichLabels request field
- Skill: always passes --enrich-labels (already an authenticated
  claude session, so the cost is already accepted)

Verified against a fresh full-stack-fastapi-template clone: labels
went from "CRUD service" / "routing + middleware" to "User/Item data
access" / "CORS middleware + Sentry + routing", within the existing
label/sub/detail caps. A run without the flag reproduces the prior
deterministic output exactly.

9 new tests (all mock the subprocess boundary -- no real claude
process in CI): claude-not-on-PATH no-op, enrich_labels=False never
shells out, valid relabel applied, oversized fields truncated to caps,
unknown ids ignored, malformed JSON / non-zero exit / timeout all fall
back safely, no-op when there are no service nodes. Full suite: 110 -> 119.
@askuma
askuma merged commit 612f148 into main Oct 23, 2026
3 checks passed
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