GEML Illustrated · profile · geml-codemap/v1 (final, 2026-07-03) · 中文

GEML Illustrated · geml-codemap

A codemap writes a codebase's call graph as a set of GEML documents: one per container, methods as empty-bodied code blocks, edges as CSV tables, entry points in the meta. It is an application-layer profile: the core specification is untouched, and declaring profile = "geml-codemap/v1" admits three attribute keys on code. This page uses the generated documents in playground/codemap/ to line up the directory layout, the meta keys, the method block, the edge tables, the split of validation duties and the agent's consumption path against the profile document.

Board

13 rules, each with its source and status

Settled fixed by the profile document. Observed not prescribed, this is what the tool does today. No implementation gap found on this page.

Settled 12Observed 1
RuleSourceStatus
1An application-layer profile, not part of the standard. The declaration admits three keys on code: anchor, name, entry-via (profiles.ts). Required: an old map without it gets one warning per code block; a rebuild fixes it.§2 · §8.6Settled
2Layout: under .geml-code-graph/, index.geml (repo metadata + module summary table), one <container>.geml per container, _index/name-lookup.json, _index/cross-stack.json, _build/ (intermediates; agents never read it). foldings.geml tunes build-time naming, style.geml tunes display: seeded on first build, never overwritten.§1Settled
3Container document name = display path sanitized (/→--, anything outside [A-Za-z0-9_.-]→-), -2 on collision. Build skips .gitignored files by default and --exclude drops more; edges to excluded symbols vanish with them.§1Settled
4Exactly one meta per document: profile (all), module (containers: display path, build root and common prefix stripped), src (real relative path), entry (methods called from outside the container, space-separated, checked by verify), resolution-default cpg / heuristic, repo commit container on the index, optional graph-depth.§2Settled
5Method block: code {#id src=path#Lstart-end anchor="…"}, empty body. src= is an ordinary attribute the agent follows to open the source; anchor= is the engine-level stable identity; name= is written only when sanitizing changed the id.§3Settled
6id = the method's short name sanitized; on a same-document collision every member gets -<first 6 of sha256(anchor)>, widening to 8, 10… if still colliding. A rename = a new id = dangling references = verify error, which is a feature.§3Settled
7Symbol-level classes: .leaf (zero outgoing edges including unresolved, and called), .accessor (bean-style get/set/is leaf, hidden by renderers by default), .test, .flow-entry. entry is never on a block, only in the meta.§3Settled
8Edge tables (empty ones are not emitted): #calls(from,to,kind,confidence), #called-by(from,to,kind,site), #unresolved(from,to; hidden), #api-calls / #api-served-by (cross-stack http edges), #ref-by reserved. References are #id or doc.geml#id; plain-text cells carry no commas or newlines and swap brackets for parentheses, because cells parse as inline content.§4Settled
9Cross-stack links: frontend and backend are matched by METHOD + normalized path into http edges; heuristic, so they live in their own tables and never mix into the verified #calls; the endpoint is an edge label, not a node; detectors are pluggable per language; _index/cross-stack.json records the audit.§4.1Settled
10Division of labour: geml check handles structure, id uniqueness, native references; CSV cells and meta values are opaque to the standard, on purpose. codemap verify resolves every from/to in #calls / #called-by and the meta entry; a dangling one = exit 1. Cross-stack tables are lenient: the #id end must resolve, the file:line end may lie outside the map.§5Settled
11Rendering: the generated documents are pure data and contain no diagram block; a renderer that recognizes a codemap document gives a layered method-flow view (roots = meta entry, depth = graph-depth); to embed the graph elsewhere write diagram {format=geml-code-graph src=…}, with src as its only attribute.§6 · GEP-0003Settled
12Versioning and trust: build --history commits each changed document into its own .gemlhistory; revert doc '#method' --rev -1 rolls back one method. resolution-default says where the edges came from; the confidence column and candidate rows are where the resolver refused to guess for you; #unresolved is a blind spot, not "no calls".§7 · §8Settled
13CLI: geml codemap build | verify | render | serve | refresh | find, dir defaults to ./.geml-code-graph, codegraph / code-graph are aliases. All 35 playground documents pass verify; find needs _index/name-lookup.json, which the playground does not ship.geml codemap --helpObserved
Layout

What a codemap directory holds

Left, the layout from profile §1; right, the real head of the playground's index.geml.

Directory and entry document Settled
.geml-code-graph/
index.geml entry: repo metadata + module summary table <container>.geml one per container (module | dir | file granularity) _index/ name-lookup.json name → {anchor, doc, id}: the agent's first hop cross-stack.json cross-stack audit: endpoints, verb mismatches, unreached routes foldings.geml tunes build-time module naming — you edit it, build never overwrites style.geml tunes display (a geml-style/v1 sheet) — same rule _build/ raw indexer output, symbols/edges.jsonl; regenerable, gitignore-able
geml codemap
$ geml codemap verify playground/codemap
verify: 35/35 documents pass geml check; profile references: all resolve
$ geml codemap find playground/codemap renderChart
no name-lookup at …\_index\name-lookup.json — build the codemap first
playground/codemap/index.geml · real file, head
=== meta
profile = "geml-codemap/v1"
repo = geml
commit = fa40d89
container = file
entry = geml-viewer--content.js.geml#main
resolution-default = cpg
===

# Code map — geml

=== table {#modules format=csv}
module,                        doc,                                 methods, entries, tests
geml-parser/geml.ts,           geml-parser--geml.ts.geml,           72,      23,      0
geml-parser/cli.ts,            geml-parser--cli.ts.geml,            63,      0,       0
geml-parser/render.ts,         geml-parser--render.ts.geml,         50,      6,       0
…                              %% 27 rows, one container document each
===
Naming
§1: a container document's name is the sanitized display path, geml-parser/chart.ts → geml-parser--chart.ts.geml. The display path algorithm is §2 module: strip the build's source root (src/main/java, a bare src), then the longest common segment prefix within the module; test code folds into a top-level test/.
Two tuning surfaces
foldings.geml and style.geml are files you edit, not build products: seeded on the first build, never rewritten after. style.geml is a geml-style/v1 sheet; missing or unreadable, the renderer falls back to its built-in defaults, exactly the behaviour before the sheet existed.
Document

One container document: meta, method blocks, edge tables

geml-parser--chart.ts.geml, generated by the builder and not edited. Two methods, one outgoing table, one incoming table.

Methods are empty code blocks, edges are CSV tables Settled
GEML · real file
=== meta
profile = "geml-codemap/v1"
module = geml-parser/chart.ts             %% display path
src = geml-parser/src/chart.ts            %% real path, used to locate the source
entry = #buildChart                        %% called from outside the container; verify checks it
resolution-default = cpg
===

# geml-parser/chart.ts

=== code {#str .leaf src=geml-parser/src/chart.ts#L51-53
          anchor="scip-typescript npm @geml/geml 1.9.1 src/`chart.ts`/str()."}
===                                        %% empty body: the source is one hop away via src=

=== code {#buildChart src=geml-parser/src/chart.ts#L55-140
          anchor="scip-typescript npm @geml/geml 1.9.1 src/`chart.ts`/buildChart()."}
===

=== table {#calls format=csv}
from,        to,   kind, confidence
#buildChart, #str, call,                  %% empty confidence = high
…
===

=== table {#called-by format=csv}
from,                                    to,          kind, site
#buildChart,                             #str,        call, geml-parser/src/chart.ts:61
geml-parser--geml.ts.geml#resolveCharts, #buildChart, call, geml-parser/src/geml.ts:1797
===                                        %% cross-document reference: a method in another container document

=== table {#unresolved format=csv hidden}   %% the blind-spot table, hidden
What each part is
PartRuleSource
metaexactly one; entry is the only fact written in the meta rather than on a block§2 · §3
code {#id …}empty body; id is the sanitized short name; src= is an ordinary routing attribute; anchor= is the engine identity, an admitted key§3
.leafzero outgoing edges and called; renderers dim it. .accessor hidden by default, .test filterable§3
#callsoutgoing edges; kind is call or candidate (a virtual-dispatch candidate, right after its primary call row); empty confidence = high§4
#called-byincoming edges, aggregated by the generator across the whole graph; site is plain-text file:line§4
#unresolvedblind spots, hidden; to is plain text, verbatim, unchecked§4
doc.geml#idcross-document reference as in §5.2; verify resolves it cell by cell§4 · §5
Division of labour
§5: geml check looks only at structure, ids and native references; #buildChart inside a CSV cell is text to the standard, and the standard grows no codemap-shaped hole. codemap verify is what resolves every from/to and the meta entry, exit 1 on a dangling one. Red means the map is stale or partially updated: rebuild before trusting navigation.
Plain-text cells
§4: site and the unresolved to carry no commas or newlines (the generator substitutes spaces) and swap brackets for parentheses, because table cells parse as inline content and f[i](&x) would read as a link.
anchor
Here the anchor is a scip symbol string (backticks, dots); a double-quoted attribute value holds it as is, since attribute values are not parsed as inline content.
Consuming

How an agent uses it, and how much to trust

The §8 cheat-sheet, plus cross-stack links and the trust semantics.

Four commands for one navigation Settled
shell · §8
$ node -e "console.log(JSON.stringify(require('./.geml-code-graph/_index/name-lookup.json')['buildChart']))"
{"anchor":"…","doc":"geml-parser--chart.ts.geml","id":"buildChart"}   name → document + id
$ geml get .geml-code-graph/geml-parser--chart.ts.geml '#buildChart'   the method block; src= is one hop to source
$ geml get .geml-code-graph/geml-parser--chart.ts.geml '#calls'        outgoing; follow doc.geml#id downwards
$ geml get .geml-code-graph/geml-parser--chart.ts.geml '#called-by'    who calls me, with site
$ head -8 .geml-code-graph/geml-parser--chart.ts.geml                 meta: the entry surface at a glance
Trust semantics · §8
You seeIt means
resolution-default = cpgedges come from compiler-grade precise resolution; heuristic is syntax-level
confidence emptyhigh. A value marks where the resolver would not guess for you
kind = candidatevirtual dispatch / multiple implementations, right after the primary call row, inheriting its confidence
rows in #unresolveda blind spot, not "no calls"
empty #called-by under heuristicdoes not mean nobody calls it
#api-calls · #api-served-bycross-stack http edges, matched, never mixed into #calls; method-mismatch flags verb disagreement
Cross-stack
§4.1: frontend and backend are two disjoint call trees that "call" each other through HTTP strings across the network; the profile joins them into http edges by METHOD + normalized path. Heuristic, so separate tables with a match confidence per row; the endpoint is a label on the edge rather than a node; framework knowledge lives only in per-language detectors. _index/cross-stack.json lists endpoints, verb mismatches (a contract-drift signal), routes nobody calls, and frontend calls that match no route.
Versioning
§7: build --history puts each changed document into its own .gemlhistory, geml history get shows how the graph evolved, geml revert doc '#method' --rev -1 rolls back one method. See page 8.
Rendering
§6: a codemap document carries no diagram block itself, it is pure data; to embed the graph in any document write diagram {format=geml-code-graph src=index.geml}, src only. See page 4.
Evidence

Probes

Run against the current in-repo build, 1.9.2. Example files come from playground/codemap/, generated by geml codemap build.

Command / fileResultMaps to
geml codemap verify playground/codemap35/35 pass profile references: all resolveboard 10, 13
geml codemap find playground/codemap renderChartno name-lookup: the playground does not ship _index/name-lookup.jsonboard 13
index.geml · geml-parser--chart.ts.gemlmeta keys, empty-bodied code, #calls / #called-by / #unresolved match the profilelayout, document
profiles.ts registrationgeml-codemap/v1 admits anchor / name / entry-via on codeboard 1