Flow views: Backend Flow, Frontend Flow, Full Flow How the three graph views are built, end to end, and where to change things.
This document exists to make you self-sufficient in this part of the codebase. It follows one URL from a source file to a drawn circle, names every method it passes through with its input and its output, and ends with a decision table for adding a framework the pipeline has never seen.
Asking "the UI is an SPA, so what is a page?" — that, and how a component's API call is attributed to the screen that renders it, is answered in detail in frontend-mapping.md.
New here? end-to-end-walkthrough.md follows one project through the whole sequence in order; this document goes deeper on one part of it.
Read architecture.md first if you want the shape of the whole service. Ignore ingestion-pipeline.md for anything on this page — it documents the retired regex/LLM path (SourceIngestionService.ingest), which is commented out at ProjectSourceService.java:124. The live path is ingestWithTreeSitter, described in §3.
Conventions, same as the rest of docs/: every class, method and constant named here exists in the source and is greppable. Code blocks are copied from the file they cite. Numbers and node values are real output from project 270 unless a fragment is explicitly labelled illustrative.
The three views are three questions about one graph:
View Endpoint The question it answers Backend Flow GET /api/v1/projects/{id}/backend/flows What does the server do? One tree per controller, down to the table. Frontend Flow GET /api/v1/projects/{id}/navigation Where can a user go? Page-to-page navigation only. Full Flow GET /api/v1/projects/{id}/user-journeys What happens when a user clicks? A page followed all the way to a table, across repos. The analogy: the pipeline is a postal sorting office. Extraction reads every envelope and writes down the address (endpoints) or the destination someone wrote on it (api_calls). Linking is the sorter that matches the two piles. The views are three different ways of laying the sorted mail out on a table — by sender, by recipient, or as a complete delivery route.
The important consequence, and the reason the system is framework-agnostic: the sorter never reads the language on the envelope. By the time linking happens, a Spring @GetMapping, a FastAPI @router.delete, a Django path() and an Express app.get() have all become the same thing — a string in the endpoints slot. Every framework-specific decision was made earlier, in one config table.
The pipeline in one diagram
View — GET, on every page load
Link — POST /api/v1/ingest/project/
Ingest — POST /api/v1/project-source (once per repo)
S3, key source_nodes.yaml
Project.linkedYmlPath
ProjectSourceService.cloneRepository
temp-clones/
FileDiscoveryService.discover
extractorMap.get(language) → extractor.extract(ctx)
YamlGraphWriter.write
output/codekg/graph_
mergeAndWriteYaml all repos → source_nodes.yaml
structuralLinks resolveRouteRefs → linkBackendUi → linkPageNavigation → …
emitGraph knowledge_base.yaml → S3
BackendFlowService / NavigationService / UserJourneyService .build(nodes, depth)
FlowTree.jsx + layoutFlowTree
The two HTTP calls are separate, and this trips everyone up once. Ingesting a repo does not link it. IngestionController.java:48 is the only trigger for LinkageEngineService.initiateProjectIngestion, and every read API serves whatever that pass last produced. Ingest sets is_outdated_graph = true (ProjectSourceService.java:142) precisely to signal "linking is now stale".
3.1 The three steps that pick an extractor
folder ──► FileDiscoveryService.discover ──► LanguageDetector.detect ──► extractorMap.get(lang)
FileDiscoveryService.discover(String rootFolder, String languageFilter) → List
// FileDiscoveryService.java:56-67
try (Stream
LanguageDetector.detect(Path) → Language or null. Pure extension lookup:
// LanguageDetector.java:258-263 public Language detect(Path file) { String name = file.getFileName().toString(); int dot = name.lastIndexOf('.'); if (dot < 0) return null; return EXT_MAP.get(name.substring(dot).toLowerCase()); } extractorMap is built once at startup, from every Spring bean implementing the interface:
// SourceIngestionService.java:98-102 @PostConstruct void initExtractorMap() { extractorMap = languageExtractors.stream() .collect(Collectors.toMap(LanguageAstExtractor::supports, e -> e)); } Collectors.toMap has no merge function, so two beans claiming the same Language fail at startup. One extractor per language, by construction. Six extractors hard-code supports(); MarkupAstExtractor and DeclarativeAstExtractor return an instance field and are registered once per language in SharedExtractorConfig.java.
3.2 What fires, per framework This table is the genericity story in one place. Every row ends in the same two slots — endpoints for a server, api_calls + screen_route for a screen — which is what lets §4 be framework-blind.
Stack File Extractor Detection mechanism Slot produced Spring MVC OrderController.java JavaAstExtractor annotation table, RouteRegistry.DEFAULT_JAVA_ANNOTATIONS endpoints JAX-RS / Quarkus OrderResource.java JavaAstExtractor same table — Path=ANY + GET=GET combined endpoints Micronaut OrderController.java JavaAstExtractor same table — Controller=ANY + Get=GET endpoints Servlet ReportServlet.java JavaAstExtractor WebServlet=ANY, via typeEndpoints fallback endpoints Kotlin + Spring OrderController.kt DeclarativeAstExtractor Kotlin annotation reader → the Java table endpoints FastAPI router.py PythonAstExtractor decorator table + APIRouter(prefix=) endpoints Flask views.py PythonAstExtractor decorator table + methods=[…] + Blueprint(url_prefix=) endpoints Django urls.py PythonAstExtractor call-routes table (path, re_path) endpoints on a MODULE node Express / Fastify orders.routes.js JavaScriptAstExtractor call-routes + the two-argument guard endpoints on a MODULE node ASP.NET OrdersController.cs CSharpAstExtractor attribute table, [controller] substitution endpoints .NET minimal API Program.cs CSharpAstExtractor call-routes (MapGet…) endpoints on a file node Go / Ruby / PHP main.go, routes.rb, web.php DeclarativeAstExtractor per-language call-routes endpoints on a MODULE node Next.js app/…/page.tsx ReactAstExtractor screen-patterns file routing screen_route, api_calls React Router App.tsx ReactAstExtractor route_decls, resolved at link time screen_route (stamped) Vue / Svelte .vue, .svelte MarkupAstExtractor markup attributes +