6.4 KiB
AgenticCode — What It Does
The Problem
Big Natural (and Java) codebases are hard to understand. A single feature may
spread across dozens of small programs linked by CALLNAT calls and shared data
areas. Figuring out what a feature does — or what it would take to rewrite it —
means reading many files by hand.
AI assistants (like Claude) cannot help much here today. They can read source files one by one, but they have no way to answer basic questions like:
- Who calls this program?
- Which database tables does this feature read or write?
- Where does the value in this field come from?
What AgenticCode Does
AgenticCode reads a Natural/Java codebase, builds a graph of everything it finds, and gives AI agents a simple API to query that graph.
Source files → Parse → Graph (Neo4j) → Enrich → Agent API
An agent calls the API like a tool — asking questions in plain HTTP — and gets structured answers back, without reading any source files itself.
What Gets Extracted
The parser reads each Natural program and pulls out:
- Subroutines — every
DEFINE SUBROUTINEblock with its line range - Calls — every
CALLNATandPERFORM, including dynamic ones (CALLNAT #VAR) - Database access — every
READ,FIND,STORE,UPDATE,DELETEwith the table name and full SQL text - Data declarations — every field in
DEFINE DATA, with type, length, and scope (PARAMETER/LOCAL/GLOBAL) - Includes — every
PARAMETER USINGandLOCAL USINGreference links the program to its LDA or PDA file;.ldaand.pdafiles are fully parsed (fields, levels, REDEFINE blocks,VIEW OFtable links) - Field references through includes —
MOVE STRUCT.FIELD TO ...or bareMOVE SORT-KEY TO ...are resolved to the actual field declared in the included data area - Variable reads and writes —
MOVE,COMPUTE,ASSIGN, arithmetic (ADD/SUBTRACT/etc.) - Control flow —
IF,FOR,REPEAT,DECIDE - Dispatch tables —
DECIDE ON VALUEblocks that map a field value to a called program
For Java it extracts classes, methods, fields, cross-class calls, and JPA entity mappings (@Entity, @Column,
inherited columns).
What an Agent Can Ask
Call graph
- Who calls this program? (
/modules/{name}/callers) — who depends on it - What does this program call? (
/modules/{name}/callees) — its direct dependencies, filtered by?scope=external(CALLNAT) or?scope=internal(PERFORM) - What is the full call tree? (
/modules/{name}/call-tree?depth=N) — everything reachable, up to N hops deep
Database access
- Which tables does this feature touch? (
/modules/{name}/db-accesses?depth=N) — including tables accessed by called subprograms - What is the exact SQL? (
/modules/{name}/sql-statements?depth=N) — full statement text, table, view, line range - What columns does this table have? (
/db-tables/{name}/columns) — from Natural data areas or Java@Entityannotations
Data areas and includes
- Which data areas does this program include? (
/modules/{name}/data-structures) — lists everyPARAMETER/LOCAL USINGarea and inline group, with type (PDA/LDA/GDA), field count, and file - What fields are in this data area? (
/data-structures/{name}/fields) — flattened list with Natural types (A8,I4), constant values, nesting level, and scope
Dataflow
- Where does this field's value go? (
/variables/{name}/flow-forward) — follows it forward acrossCALLNATargument lists - Where does this field's value come from? (
/variables/{name}/flow-backward) — traces it back to its origin - Which programs write and which programs read this shared PDA field? (
/variables/{name}/field-flow) — across the whole call graph
Search
- Find by name (
/search/identifier?name={n}) — any identifier across all programs - Find by value (
/search/value?value={v}) — find which program assigns a string like'WGEAGB0S'to a field
How Ingest Works
Ingest is split into two steps so large codebases stay fast:
- Fast pass (
/ingest-call-graph) — reads all files, builds the call graph. 6,309 Natural files in 67 seconds. Skips the expensive field-level resolution. - Deep pass (
/ingest/{name}) — fully resolves a single program and all programs it depends on. A 56-program tree in 5 seconds, even when the full call graph is already loaded.
Endpoints that need field-level data return a clear 409 error with a nextAction
hint if a program has only been fast-ingested — so an agent knows exactly what to
ask an operator to do next.
Numbers from a Real Codebase (upms)
| What | Result |
|---|---|
| Files ingested (fast pass) | 6,309 in 67 s |
| Deep ingest of a 56-program tree | 5.2 s |
Dynamic CALLNAT targets resolved for one dispatcher |
124 programs |
Dispatch table rows recovered for KDWWIFN0 |
~140, matching a hand-built CSV |
What This Enables
A question like "what would it take to rewrite WGEAGB0S in Spring Boot?" that
used to take hours of manual reading now takes one agent session:
- Overview: callers, callees, subroutines
- Full call tree — the scope of the feature
- All database tables touched, including behind
CALLNAThops - Exact SQL to port to Panache/JPA
- Column schema for the new
@Entityclass - Where the key field comes from (dataflow)
- Who else depends on this program (blast radius)
The agent can then generate a typed Spring Boot service — with the right entity mapping, business logic, and a complete picture of what else is affected.
Tech Stack
| Component | Technology |
|---|---|
| Server | Quarkus (Java 21, Virtual Threads) |
| Graph DB | Neo4j 5.x |
| Natural parser | Custom (no open-source parser exists) |
| Java parser | JavaParser library |
| API | JAX-RS / RESTEasy Reactive |
| Tests | JUnit 5 + RestAssured + Testcontainers |