codemap 把一个代码库的调用图写成一组 GEML 文档:每个容器一份,方法是空体 code 块,边是 CSV 表,入口写在 meta 里。它是应用层 profile:核心规范一字未动,只靠声明 profile = "geml-codemap/v1" 放行 code 上的三个属性键。这一页用 playground/codemap/ 里真实生成的文档做例子,把文件布局、meta 键、方法块、边表、校验分工和 agent 的消费方式逐条对到 profile 文档上。
规范已定 profile 文档写死的。实测 文档不规定、工具今天这样做。实现偏差本页未发现。
| 规则 | 出处 | 状态 | |
|---|---|---|---|
| 1 | 应用层 profile,不是标准的一部分。声明放行 code 块上的 anchor、name、entry-via 三个键(profiles.ts)。必需:没有声明的老图每个 code 块都 warning,重建即修。 | §2 · §8.6 | 规范已定 |
| 2 | 布局:.geml-code-graph/ 下 index.geml(仓库元数据 + 模块汇总表)、每容器一份 <container>.geml、_index/name-lookup.json、_index/cross-stack.json、_build/(中间产物,agent 不读)。foldings.geml 调构建期命名,style.geml 调显示:首次构建播种,之后不覆盖。 | §1 | 规范已定 |
| 3 | 容器文档名 = 显示路径 sanitize(/→--,其它非 [A-Za-z0-9_.-]→-),冲突加 -2。build 默认跳过 .gitignore 的文件,--exclude 再排除,被排除符号的边一起消失。 | §1 | 规范已定 |
| 4 | 每文档恰一个 meta:profile(全部)、module(容器:显示路径,剥掉构建根和公共前缀)、src(真实相对路径)、entry(被容器外调用的方法,空格分隔,verify 检查)、resolution-default cpg / heuristic、index 上 repo commit container、可选 graph-depth。 | §2 | 规范已定 |
| 5 | 方法块:code {#id src=path#Lstart-end anchor="…"},空体。src= 是普通属性,agent 顺着它打开源码;anchor= 是引擎级稳定身份;name= 只在 id 被 sanitize 改动时写。 | §3 | 规范已定 |
| 6 | id = 方法短名 sanitize;同文档同名冲突时每个成员追加 -<sha256(anchor) 前 6 位>,仍冲突再升到 8、10…。改名 = 新 id = 引用悬空 = verify error,这是特性不是缺陷。 | §3 | 规范已定 |
| 7 | 符号级类:.leaf(零出边含未解析、且被调用)、.accessor(bean 式 get/set/is 叶子,渲染器默认隐藏)、.test、.flow-entry。entry 从不在块上,只在 meta。 | §3 | 规范已定 |
| 8 | 边表(空表不发出):#calls(from,to,kind,confidence)、#called-by(from,to,kind,site)、#unresolved(from,to,hidden)、#api-calls / #api-served-by(跨栈 http 边)、#ref-by 保留。引用写法 #id 或 doc.geml#id;纯文本格不含逗号和换行,方括号换成圆括号,因为格按行内解析。 | §4 | 规范已定 |
| 9 | 跨栈链接:前后端按 METHOD + 归一化路径匹配成 http 边;启发式,所以放独立表、永不混进已验证的 #calls;endpoint 是边标签不是节点;探测器按语言可插拔;_index/cross-stack.json 记审计。 | §4.1 | 规范已定 |
| 10 | 分工:geml check 管结构、id 唯一、原生引用;CSV 格和 meta 值对标准不透明,有意如此。codemap verify 逐格解析 #calls / #called-by 的 from/to 和 meta entry,悬空 = exit 1;跨栈表宽松:#id 必须解析,file:line 端允许在图外。 | §5 | 规范已定 |
| 11 | 渲染:生成的文档是纯数据,不含 diagram 块;认出 codemap 文档的渲染器给分层方法流视图(根 = meta entry,深度 = graph-depth);嵌进别的文档用 diagram {format=geml-code-graph src=…},只有 src 一个属性。 | §6 · GEP-0003 | 规范已定 |
| 12 | 版本与信任:build --history 把变更文档提交进各自 .gemlhistory;revert doc '#method' --rev -1 回滚单个方法。resolution-default 说边从哪来,confidence 列和 candidate 行是解析器拒绝替你猜的地方,#unresolved 是盲区不是「没有」。 | §7 · §8 | 规范已定 |
| 13 | CLI:geml codemap build | verify | render | serve | refresh | find,dir 缺省 ./.geml-code-graph,codegraph / code-graph 是别名。playground 里 35 份文档 verify 全过;find 需要 _index/name-lookup.json,playground 没带。 | geml codemap --help | 实测 |
左边是 profile §1 的布局,右边是 playground 里真实 index.geml 的开头。
$ 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
=== 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 行,每行一个容器文档 ===
geml-parser/chart.ts → geml-parser--chart.ts.geml。显示路径的算法在 §2 module:剥掉构建源码根(src/main/java、裸 src),再剥模块内最长公共段前缀;测试代码折进顶层 test/。foldings.geml 和 style.geml 是你编辑的文件,不是构建产物:首次构建播种,之后永不重写。style.geml 是 geml-style/v1 样式表,缺了或读不了就回到渲染器内置默认,和有它之前的行为一样。geml-parser--chart.ts.geml,构建器生成、未改一字。两个方法,一张出边表,一张入边表。
=== meta profile = "geml-codemap/v1" module = geml-parser/chart.ts %% 显示路径 src = geml-parser/src/chart.ts %% 真实路径,用来定位源码 entry = #buildChart %% 被容器外调用的方法;verify 检查 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()."} === %% 空体:源码在 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, %% 空 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 === %% 跨文档引用:另一份容器文档里的方法 === table {#unresolved format=csv hidden} %% 盲区表,hidden
| 部件 | 规则 | 出处 |
|---|---|---|
| meta | 恰一个;entry 是唯一写在 meta 而不在块上的事实 | §2 · §3 |
| code {#id …} | 空体;id 是短名 sanitize;src= 普通路由属性;anchor= 引擎身份,被放行的键 | §3 |
| .leaf | 零出边且被调用;渲染器调暗。.accessor 默认隐藏,.test 可过滤 | §3 |
| #calls | 出边;kind 为 call 或 candidate(虚分派候选,紧跟主 call 行);空 confidence = high | §4 |
| #called-by | 入边,由生成器跨全图汇总;site 是 file:line 纯文本 | §4 |
| #unresolved | 盲区,hidden;to 原样纯文本、不检查 | §4 |
| doc.geml#id | 跨文档引用同 §5.2;verify 逐格解析 | §4 · §5 |
geml check 只看结构、id、原生引用;CSV 格里的 #buildChart 对标准来说是文本,标准不长 codemap 形状的洞。codemap verify 才逐格解析 from/to 和 meta entry,悬空就 exit 1,红了说明图陈旧或部分更新,先重建再信导航。f[i](&x) 会被读成链接。§8 的 cheat-sheet,加上跨栈链接和信任语义。
$ node -e "console.log(JSON.stringify(require('./.geml-code-graph/_index/name-lookup.json')['buildChart']))" {"anchor":"…","doc":"geml-parser--chart.ts.geml","id":"buildChart"} 名字 → 文档 + id $ geml get .geml-code-graph/geml-parser--chart.ts.geml '#buildChart' 方法块;src= 一跳到源码 $ geml get .geml-code-graph/geml-parser--chart.ts.geml '#calls' 出边;顺着 doc.geml#id 往下走 $ geml get .geml-code-graph/geml-parser--chart.ts.geml '#called-by' 谁调我,带 site $ head -8 .geml-code-graph/geml-parser--chart.ts.geml meta:入口面一眼看完
| 看到 | 意思 |
|---|---|
| resolution-default = cpg | 边来自编译器级精确解析;heuristic 是语法级 |
| confidence 空 | high。有值时是解析器不替你猜的地方 |
| kind = candidate | 虚分派 / 多实现候选,紧跟主 call 行,继承其 confidence |
| #unresolved 有行 | 盲区,不是「没有调用」 |
| heuristic 下 #called-by 为空 | 不等于没人调 |
| #api-calls · #api-served-by | 跨栈 http 边,匹配出来的,不与 #calls 混;method-mismatch 标记动词分歧 |
METHOD + 归一化路径把它们接成 http 边。启发式所以单独成表,每行带匹配置信度;endpoint 是边上的标签而不是节点;框架知识只在按语言的探测器里。_index/cross-stack.json 列 endpoint、动词分歧(合同漂移信号)、无人调用的路由、匹配不到路由的前端调用。build --history 让每份变更文档进自己的 .gemlhistory,geml history get 看图怎么演化,geml revert doc '#method' --rev -1 只回滚一个方法。见第 8 页。diagram {format=geml-code-graph src=index.geml},只有 src。见第 4 页。用仓库内当前构建,1.9.2。示例文件来自 playground/codemap/,由 geml codemap build 生成。
| 命令 / 文件 | 结果 | 对应 |
|---|---|---|
| geml codemap verify playground/codemap | 35/35 pass profile references: all resolve | 看板 10、13 |
| geml codemap find playground/codemap renderChart | no name-lookup:playground 未带 _index/name-lookup.json | 看板 13 |
| index.geml · geml-parser--chart.ts.geml | meta 键、空体 code、#calls / #called-by / #unresolved 与 profile 一致 | 布局、文档 |
| geml check profiles.ts 注册 | geml-codemap/v1 放行 code 上 anchor / name / entry-via | 看板 1 |