Related docs: Architecture · Methodology · Toolchain
The origin design selected a property graph for relational codebase knowledge. The comparison below records that rationale; it is not a benchmark against every flat or relational implementation.
Flat files cannot answer cross-file dependency queries without a full scan. A question like "which programs call this function across all modules" requires reading every file. There is no index for call chains. There is no path traversal. There is no way to ask "what is the blast radius if this table changes?" without scanning everything.
A relational database requires an explicit schema and represents recursive graph traversal less directly. Recursive CTEs and multi-table joins can support these queries, but the origin team preferred the property-graph model for extensible relationship types and traversal-oriented access.
A property graph is the natural representation:
flowchart LR
subgraph WHY["Why graph wins"]
G1["Any call chain\nin one Cypher query"]
G2["New node/edge types\nadded on the fly"]
G3["Native cross-module\npath traversal"]
G4["20 relationship types\nno schema migration"]
end
subgraph NOTFLAT["Why not flat files"]
F1["No cross-file\ndependency index"]
F2["No path traversal"]
F3["Full scan for\nevery relationship query"]
end
subgraph NOTREL["Why not relational"]
R1["Schema must be known\nupfront"]
R2["JOIN explosion at\n5+ relationship types"]
R3["Recursive CTEs for\ncall chains"]
end
| Label | Represents | Key Properties |
|---|---|---|
SourceFile |
A source code file | name, qualified_name, module, file_type, path, line_count |
Table |
A database table | name, qualified_name, database, field_count, index_count |
Field |
A column in a table | name, data_type, format, label, mandatory, initial_value |
Index |
A database index | name, fields, unique, primary |
Sequence |
A database sequence | name; additional properties are implementation-specific in the public baseline |
Screen |
A UI screen | name, type (GUI/char-mode), module, scr_id |
Frame |
A frame within a screen | name, screen, widget_count |
IncludeFile |
A shared include / macro | name, module, parameter_count, usage_count |
Module |
A top-level module | name, program_count, file_types |
Database |
A database schema | name, table_count, field_count |
ValidationPackage |
A documented domain VP | domain, status, program_count |
E2EProcess |
An end-to-end process | name, confidence, layer_count, vp_coverage |
Open contract: the public documents name
E2EProcessnodes but do not specify their relationships to programs, screens, or Validation Packages. Do not invent those edges when implementing the baseline; recover them from PoC artifacts or introduce them as a separately versioned proposal.
Twenty relationship types cover five categories. Each edge carries provenance properties — see Provenance Model below.
flowchart LR
A["SourceFile A"] -->|"CALLS"| B["SourceFile B"]
A -->|"INCLUDES"| I["IncludeFile"]
A -->|"REFERENCES"| C["SourceFile C"]
| Relationship | From | To | Meaning |
|---|---|---|---|
CALLS |
SourceFile | SourceFile | A calls B at runtime |
INCLUDES |
SourceFile | IncludeFile | A statically includes I |
REFERENCES |
SourceFile | SourceFile | A references C without a direct call |
flowchart LR
P["SourceFile"] -->|"READS"| T["Table"]
P -->|"WRITES"| T
P -->|"CREATES"| T
P -->|"DELETES"| T
P -->|"REFERENCES_FIELD"| F["Field"]
| Relationship | From | To | Meaning |
|---|---|---|---|
READS |
SourceFile | Table | Program performs a SELECT / FOR EACH READ |
WRITES |
SourceFile | Table | Program performs an UPDATE / ASSIGN |
CREATES |
SourceFile | Table | Program performs a CREATE / INSERT |
DELETES |
SourceFile | Table | Program performs a DELETE |
REFERENCES_FIELD |
SourceFile | Field | Program references a specific field |
flowchart LR
DB["Database"] -->|"HAS_TABLE"| T["Table"]
T -->|"HAS_FIELD"| F["Field"]
T -->|"HAS_INDEX"| I["Index"]
DB -->|"HAS_SEQUENCE"| S["Sequence"]
| Relationship | From | To | Meaning |
|---|---|---|---|
HAS_TABLE |
Database | Table | Table belongs to database |
HAS_FIELD |
Table | Field | Field belongs to table |
HAS_INDEX |
Table | Index | Index belongs to table |
HAS_SEQUENCE |
Database | Sequence | Sequence belongs to database |
| Relationship | From | To | Meaning |
|---|---|---|---|
RENDERS_SCREEN |
SourceFile | Screen | Program renders or launches this screen |
HAS_FRAME |
Screen | Frame | Screen contains this frame |
ON_EVENT |
Frame | SourceFile | Frame event triggers this handler |
| Relationship | From | To | Meaning |
|---|---|---|---|
DOCUMENTS |
ValidationPackage | SourceFile | VP covers this program |
COVERS_TABLE |
ValidationPackage | Table | VP documents this table |
VP_REFERENCES |
ValidationPackage | SourceFile | VP references but doesn't primarily cover this program |
PRIMARY_TABLE |
Screen | Table | Screen's primary data entity |
EXPOSES |
SourceFile | Field | Program exposes this field in its UI |
Every relationship edge carries four provenance properties written when the edge is created. Re-ingestion may refresh an edge and its provenance; an audit therefore needs the graph-run identity as well as the edge. The public baseline does not yet specify immutable snapshots or citation re-anchoring across source revisions.
flowchart LR
EDGE["Graph Edge\n(any relationship type)"]
subgraph PROVENANCE["Provenance Properties on Every Edge"]
P1["source\n'xref' | 'regex' | 'schema' | 'derived'"]
P2["source_file\npath/to/evidence/file"]
P3["source_line\ninteger line number (0 if N/A)"]
P4["confidence\n'compiler' | 'regex' | 'schema' | 'derived'"]
end
EDGE --> P1
EDGE --> P2
EDGE --> P3
EDGE --> P4
source records the detection mechanism; confidence records the trust assigned to
the resulting relationship. They are related but not interchangeable. The origin
baseline has only xref, regex, schema, and derived, which cannot honestly
represent AST detection. The parser guide therefore shows ast as a
PROPOSED-EXPERIMENT; adding it to a normative interchange contract requires a
versioned, tested schema change.
| Detection mechanism | Condition | Default confidence |
|---|---|---|
xref |
Compiler-produced cross-reference with a resolved target | compiler |
ast (PROPOSED-EXPERIMENT) |
Target resolved against the analysed program/module set | compiler |
ast (PROPOSED-EXPERIMENT) |
Syntax observed but target unresolved or name-only | regex |
schema |
Parsed schema definition | schema |
regex |
Source-text pattern | regex |
derived |
Filesystem, configuration, or documentation-derived fact | derived |
| Tier | Source | Trust | Example |
|---|---|---|---|
compiler |
Compiler cross-reference output | Highest — compiler-verified | .xref cross-reference files, .class from Java, type-checked imports from TypeScript |
schema |
Schema definition files | High — schema-verified | .df, .sql, .prisma |
regex |
Pattern-matching on source code | Medium — inferred | FOR EACH table_name, CALL program_name |
derived |
Documentation, filesystem, XML | Lower — document-derived | VP references, folder structure, config files |
// Compiler-verified CALLS edge
MERGE (caller:SourceFile {qualified_name: "module:program_a.p"})
MERGE (callee:SourceFile {qualified_name: "module:program_b.p"})
MERGE (caller)-[:CALLS {
source: "xref",
source_file: "module/src/program_a.p.xref",
source_line: 42,
confidence: "compiler"
}]->(callee)
// Regex-fallback READS edge (no xref available)
MERGE (prog:SourceFile {qualified_name: "module:legacy_import.p"})
MERGE (tbl:Table {qualified_name: "erp:sales_order"})
MERGE (prog)-[:READS {
source: "regex",
source_file: "module/src/legacy_import.p",
source_line: 117,
confidence: "regex"
}]->(tbl)The Graph Uploader supports two upload modes, selectable via CLI flag:
Used for the initial upload or when the graph is known to be dirty:
- Wipe everything:
MATCH (n) DETACH DELETE n(in batches to avoid timeout) - Upload from generated Cypher: all MERGE statements with provenance
- Post-upload audit: verify only allowed labels and relationship types exist
Used for everyday updates:
- MERGE/upsert all generated Cypher (idempotent — creates if missing, updates if existing)
- Tag every node touched by this run:
_graph_run: "<timestamp>" - Detect stale nodes: nodes NOT tagged by this run
- Report stale nodes for human review (never auto-delete)
- Prune on confirmation: only delete stale nodes if
--pruneflag is passed
These are the only query examples published by the current repository. Reconstructed copies live in the seed template library; they are not the lost full PoC query library.
// Programs that write to one qualified table identity.
MATCH (s:SourceFile)-[:WRITES]->(t:Table {qualified_name: $table_qualified_name})
RETURN s.name, s.module, s.file_type
ORDER BY s.module, s.name
// Recorded call paths from a qualified root, limited to 5 edges.
// This does not establish that deeper or dynamically resolved calls are absent.
MATCH path = (root:SourceFile {qualified_name: $entry_point})-[:CALLS*1..5]->(callee)
RETURN [node IN nodes(path) | node.name] AS chain,
length(path) AS depth
ORDER BY depth
// Tables written in one module and read in another: candidate handoff evidence.
// Shared access alone does not establish temporal order or producer/consumer direction.
MATCH (writer:SourceFile)-[:WRITES]->(t:Table)<-[:READS]-(reader:SourceFile)
WHERE writer.module <> reader.module
RETURN DISTINCT t.name AS table,
writer.module AS producer_module,
reader.module AS consumer_module
ORDER BY t.name
// Programs writing tables that contain status-like fields.
// This query does not prove that each program writes the matched field.
MATCH (s:SourceFile)-[:WRITES]->(t:Table)-[:HAS_FIELD]->(f:Field)
WHERE f.name CONTAINS 'status' OR f.name CONTAINS '_stat'
RETURN t.name AS table,
f.name AS status_field,
collect(DISTINCT s.name) AS updating_programs
// Candidate screen entry points: screens with no recorded rendering program.
// An empty incoming-edge set is bounded by parser and graph coverage.
MATCH (scr:Screen)
WHERE NOT ()-[:RENDERS_SCREEN]->(scr)
RETURN scr.name, scr.module, scr.type
ORDER BY scr.module, scr.name
// Programs with recorded CRUD relationships to at least 5 tables.
MATCH (s:SourceFile)-[r]->(t:Table)
WHERE type(r) IN ['READS', 'WRITES', 'CREATES', 'DELETES']
WITH s, count(DISTINCT t) AS table_count
WHERE table_count >= 5
RETURN s.name, s.module, table_count
ORDER BY table_count DESCThe examples below start or configure Neo4j for a future tool implementation. They do not make the current specification-only repository executable, and the Docker command starts only the database service.
CodeGraph requires a running Neo4j instance. Three options:
Neo4j Aura (Cloud) — A possible team deployment. A future implementation can
configure NEO4J_URI=neo4j+s://your-instance.databases.neo4j.io; this repository
does not ship an environment loader or uploader.
Neo4j Desktop — A possible local deployment. Download from
neo4j.com/download and configure a future uploader
with NEO4J_URI=bolt://localhost:7687.
Docker — For CI/CD or server deployments:
docker run \
--name codegraph-neo4j \
-p 7474:7474 -p 7687:7687 \
-e NEO4J_AUTH=neo4j/your-password \
neo4j:5The connection variables shown above are interface examples; no runnable uploader or
.env contract is currently included.
Embedded graph option (roadmap): A Kuzu-based embedded graph backend that requires zero external services is planned for v2 — targeting single-developer local use cases.