# `modules.json` — the state / source of truth One file per project. The HTML and MD are pure projections of it (regenerated by `render.py`); never hand-edit the outputs. Edit `modules.json` (or let the scripts and subagents edit it) and re-render. ```jsonc { "meta": { "project": "My App", // shown in the title (module NAMES are never translated) "lang": "en", // UI display language: "en"|"zh" (default "en"). // localizes the chrome + report; module `desc` // should be authored in this language. ids/labels stay. "subtitle": "short tagline", // optional header sub-line (write in meta.lang) "generatedAt": "2026-01-01", "rev": "abc1234", // git HEAD at the last codemap run (the baseline // `update`/`check` diff from; set by scan.py --stamp-rev) "htmlPath": ".codemap/codemap.html", // output location (for reciprocal links) "mdPath": ".codemap/codemap.md", "spineDesc": "A user edits … → … → persistence.", // shown on the spine view "tracked_loc": 184900, // filled by scan.py --write (union, de-duped) "tracked_files": 980, "locLine": "≈185k first-party LoC …" // optional override of the report line }, "excludes": ["__pycache__","/node_modules/", "..."], // optional; sensible defaults if absent "bands": [ // visual layers, top → bottom (data-flow order) {"id":"festore","tier":"fe","t":"Frontend · Stores","d":"one store per domain"}, {"id":"wire1","wire":true,"t":"◀ WebSocket · HTTP ▶"}, // a divider, not a band {"id":"becore","tier":"be","t":"Backend · Core","d":"scene model, protocol"} ], "spine": ["p_viewport","editorStore","ws_svc","be_main","core_scene","persistence"], "reportThemes": [ ["Dual-format is the most-repeated violation", "snake||camel recurs in N handlers …"], ["Duplication is the dominant theme", "X and Y reimplement …"] ], "modules": [ { "id": "core_scene", // stable unique id (used in deps + edges) "label": "Scene", // shown on the card "band": "becore", // which band it sits in "path": "core/scene.py", // human-readable location (shown in detail) "paths": ["src/core/scene.py"], // globs scan.py counts (root-relative; may use ** wildcards) "exclude": [], // optional extra excludes for this module "coupling": "core", // low | med | high | core (manual / structural) "deps": ["core_object","core_components"], // downstream: what this calls "desc": "Central data structure …", // 1-line "what it does", shown on click; // author in meta.lang (zh for this project) "tests": ["server/.../tests/test_scene.py"], // globs for this module's tests (the // regression net); NOT counted in the // module's own audit scope / loc "lastFix": { // optional: outcome of the most recent `fix` "at":"2026-06-11","accepted":true, "ran":"pytest tests/test_scene.py", "scoreBefore":68,"scoreAfter":84 }, // ---- filled by scan.py --write ---- "loc": 1234, "contentHash": "ab12…", // hash of current code // ---- filled by apply_audit.py (one subagent's result) ---- "score": 88, "grade": "B", "tags": ["legacy"], "findings": [{"sev":"LOW","loc":"core/scene.py:72","text":"… evidence …"}], "auditedHash": "ab12…", // contentHash at audit time → staleness check "auditedAt": "2026-06-10", "auditedRev": "3837998" } ] } ``` ## Field ownership | Field | Written by | When | |---|---|---| | `meta.*`, `bands`, `spine`, `coupling`, `deps`, `desc`, `paths`, `label`, `id`, `band` | **model** (decomposition) | `init`, and when structure changes | | `loc`, `contentHash`, `meta.tracked_*` | `scan.py --write` | every scan | | `score`, `grade`, `tags`, `findings`, `auditedHash/At/Rev` | `apply_audit.py` (from an **auditor subagent**) | each (re)audit | | `tests` | **test-author subagent** | `test`, and the baseline step of `fix` | | `lastFix` | **model** (after the acceptance gate) | each `fix` | | `reportThemes` | **model** (synthesis) | after a full/partial audit | Test files are **excluded from a module's audit scope** by default (so editing a test does not mark the module stale and test LoC does not inflate the production-code score). The `tests` field records them separately as the regression net. ## Staleness A module is **stale** when `contentHash != auditedHash` (its code changed since last audit), **unaudited** when it has no `score`/`auditedHash`, **empty** when its `paths` match no files (likely deleted → drop it or fix the globs). `scan.py` reports these; only stale + unaudited modules need a fresh subagent. ## Coupling vs score `coupling` (low/med/high/core) is a **structural** property — how central the module is (degree + role). `score` is a **quality** property — how clean the code is. They are independent: a `core` module can be clean (A) and a `low` leaf can be broken (F). ## Custom audit standard (`standard.json`) The scoring standard is data, not code: `reference/standard.json` holds the rubric, severities, coupling levels, and the issue `tags` (each `{id,label,labelZh,bad,en,zh}`). A project overrides it at `/.codemap/modules.json`'s sibling `standard.json`. The map renders + lets you edit it on the **Standard** page (Export to save). Audits must use the project standard's tags when present — including custom ones.