Brand outputs as codemap.html/md, add MIT LICENSE, promotional README + sample-project example screenshots

This commit is contained in:
Xingyu Chen
2026-06-10 18:49:39 -07:00
parent f7c1431c84
commit 4171596825
12 changed files with 2056 additions and 144 deletions
+21
View File
@@ -0,0 +1,21 @@
MIT License
Copyright (c) 2026 Xingyu Chen
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
+127 -131
View File
@@ -1,192 +1,188 @@
# codemap # 🧹 codemap
A [Claude Code](https://claude.com/claude-code) **Agent Skill** that builds and **A code janitor for AI coding agents.** Point it at any repo and it draws an
incrementally maintains an **interactive architecture map + per-module code-quality **interactive architecture map**, scores **every module 0100** for technical debt, and
audit** for any codebase. helps you **clean up the spaghetti** — incrementally, one commit at a time.
It decomposes a project into *functional* modules (not files), draws their dependency ![Claude Code skill](https://img.shields.io/badge/Claude%20Code-skill-f59e0b)
graph as a layered, clickable HTML page, and scores each module 0100 for code health — ![works with Codex](https://img.shields.io/badge/works%20with-Codex%20%2F%20any%20agent-7c8794)
hunting for monkeypatching, fallbacks, legacy/dead code, stubs, dual-format handling, ![Python 3 · stdlib only](https://img.shields.io/badge/python-3%20·%20stdlib%20only-3776ab)
bloat, duplication, and glue. Every module's score comes from an **independent ![language agnostic](https://img.shields.io/badge/langs-Py%20·%20TS%20·%20Rust%20·%20C%23%20·%20C%2B%2B-555)
subagent** against a fixed rubric. It's **incremental**: a per-module content hash means ![license MIT](https://img.shields.io/badge/license-MIT-blue)
re-runs only re-audit what changed.
## What you get > Every codebase rots into a 屎山 (spaghetti / big ball of mud) eventually — monkeypatches,
> silent fallbacks, dead "legacy" paths, half-finished stubs, copy-pasted duplication,
> god-files, and valueless glue. **codemap finds that rot, ranks it, and hands an AI agent
> a clean punch-list to fix it** — with a regression-gated fix loop so cleanup never breaks
> your build.
Three coupled artifacts, kept in sync: ![architecture map](examples/01-map.png)
| File | What | Where (default) | ---
## Why codemap
Most "architecture diagram" tools draw *files and imports*. codemap is different:
- **Functional modules, not files.** It groups code into the capabilities that actually
matter (a store, a handler group, a feature, a plugin) and lays them out along the
real data-flow.
- **It grades the rot.** Every module gets a health **score (0100) and grade (AF)** plus
concrete `file:line` findings, hunting specifically for the smells that make code
unmaintainable: `monkeypatch`, `fallback`, `silent-except`, `legacy`/dead code, `stub`,
`fake-output`, `dual-format`, `bloat`, `duplication`, `glue`, `god-component`, …
- **Independent, honest scoring.** Each module is audited by a **separate AI subagent**
against a fixed rubric — no single pass rubber-stamping the whole repo.
- **Incremental + git-aware.** A per-module content hash + the last-run commit mean re-runs
only re-audit what changed, and `update` shows you the **commits since last time** and
which modules they touched.
- **Cleanup that can't regress.** `fix` runs a four-role loop — lock a test baseline →
fix → an **independent acceptance check** proves the pre-fix tests still pass → re-score.
It's the maintenance pass you never have time to do, turned into something an agent can
run on a schedule.
## Screenshots
| Select a module — dependencies + audit | Audit report | Editable standard |
|---|---|---| |---|---|---|
| `config.json` | your saved **preferences** (UI language, output location, title) | `<project>/.codemap/` | | ![module](examples/02-module.png) | ![report](examples/03-report.png) | ![standard](examples/04-standard.png) |
| `modules.json` | the **source of truth** (modules, deps, coupling, LoC, hash, score, findings) | `<project>/.codemap/` |
| `architecture-map.html` | self-contained **interactive map** (health coloring, filters, dependency highlighting, audit report) | `<project>/.codemap/` |
| `architecture-audit.md` | the written **report** (per-layer scores, LoC table, worst offenders, themes) | `<project>/.codemap/` |
Everything lives in **`<project>/.codemap/`** by default. On first run (`generate`) the - **Click any module** to highlight what it calls (downstream) and what depends on it
tool asks your preferences — UI language, output location (point it at `docs/` if you (upstream), with its score, smell tags, and `file:line` findings.
want the HTML/MD committed/visible), project title — and saves them to - **Health vs coupling** color modes — problems pop amber/red, healthy modules recede to a
`.codemap/config.json`. The HTML and MD are **generated** from `modules.json` and must muted green (colorblind-friendly; the cue is saturation, not just hue).
never be hand-edited. - **Filter** by grade (≤ B/C/D/F) or by issue tag; jump straight to the worst offenders.
- **Editable Standard page** — change descriptions, **add your own issue tags** to capture
### Interactive map features *your* definition of a problem, and Export to `standard.json`; future audits use it.
- Layered bands top→bottom along the data-flow; click a module to highlight what it - **i18n** — English or Chinese UI (`meta.lang`); module names are never translated.
**calls** (downstream) and what **depends on it** (upstream). - **Copy-fix button** on each module — copies `/codemap fix <module>` to paste into your agent.
- Per-module **health score + grade (AF)**, smell tags, and concrete `file:line` findings.
- Color modes: **coupling** or **health** (problems pop amber/red, healthy modules
recede to a muted green — colorblind-friendly, the cue is saturation not just hue).
- **Filters**: by grade level (≤ B/C/D/F) and by issue tag; live match count.
- **Audit report** view: averages, grade spread, worst offenders, cross-cutting themes.
- **Standard page**: a built-in "Standard" view explaining the score→grade rubric, the
finding severities, and every smell tag — so the scores are self-documenting.
- **i18n**: set `meta.lang` to `"en"` or `"zh"` (module names are never translated).
## Languages ## Languages
Language-agnostic. The scripts count LoC and hash bytes for **any** text source, and Language-agnostic. LoC and hashing work on **any** text source and `paths` are plain globs,
`paths` are plain globs, so it works for Python, **TypeScript/JS, Rust, C#/.NET, C/C++**, so it covers **Python, TypeScript/JS, Rust, C#/.NET, C/C++, Go, Java, Swift**, and more.
Go, Java, Swift, and more. Build/test/generated trees are excluded out of the box Build/test/generated trees are excluded out of the box (`target/`, `bin/`, `obj/`,
(`target/`, `bin/`, `obj/`, `node_modules/`, `cmake-build*`, `__pycache__/`, `dist/`, `node_modules/`, `cmake-build*`, `__pycache__/`, `dist/`, `*.d.ts`, `*.Designer.cs`, …).
`*.d.ts`, `*.Designer.cs`, …). The audit rubric names *behaviors*, not syntax — The rubric names *behaviors*, not syntax — `reference/STANDARDS.md` maps each smell to its
`reference/STANDARDS.md` maps each smell to its per-language form (e.g. `any-escape` = per-language form (e.g. `any-escape` = `as any` / `dynamic` / `void*` / `reinterpret_cast`
`as any` / `dynamic` / `void*` / `reinterpret_cast` / `unsafe`). / `unsafe`).
## Requirements ## Requirements
- **Python 3** (standard library only — no `pip install`, no external packages). - **Python 3** standard library only. No `pip install`, no external packages.
- **An AI coding agent** to drive the audit/fix/test steps **Claude Code** (native - **An AI coding agent** to drive the audit/fix/test steps: **Claude Code** (native skill)
skill) or **any other agent that can read instructions and spawn sub-tasks**, e.g. or **any agent that reads instructions and spawns sub-tasks**, e.g. OpenAI **Codex**
OpenAI **Codex** (see [Using with Codex / other agents](#using-with-codex--other-agents)). (see [Using with Codex](#using-with-codex--other-agents)).
- A browser to open the generated HTML. That's it. - A browser to open the generated HTML. That's it.
## Install ## Install
A skill is just a folder under `~/.claude/skills/`. Clone this repo into it: A Claude Code skill is just a folder under `~/.claude/skills/`:
```bash ```bash
git clone <this-repo-url> ~/.claude/skills/codemap git clone https://github.com/Asixa/codemap-skill ~/.claude/skills/codemap
``` ```
(Windows PowerShell: `git clone <url> $env:USERPROFILE\.claude\skills\codemap`.) (Windows PowerShell: `git clone https://github.com/Asixa/codemap-skill $env:USERPROFILE\.claude\skills\codemap`.)
Restart Claude Code (or start a new session). The skill appears as `/codemap`. Restart Claude Code (or start a new session). The skill appears as **`/codemap`**.
## Usage ## Usage
Talk to Claude in natural language, or use the subcommands. Claude reads `SKILL.md` Talk to Claude in plain language, or use the subcommands. On the first run, codemap asks
and runs the scripts; the **audit / fix / test** steps spawn independent subagents. your preferences (UI language, output location, project title) and saves them to
`<project>/.codemap/config.json`. Everything it produces lives in `<project>/.codemap/`.
| Command | Does | | Command | Does |
|---|---| |---|---|
| `/codemap generate` | first build: decompose → scan → audit every module → render | | `/codemap generate` | first build: ask prefs → decompose into modules → scan → audit every module → render |
| `/codemap check` | read-only: is the map stale? lists drifted / new / deleted modules | | `/codemap check` | read-only: is the map stale? shows commits since last run + drifted / new / deleted modules |
| `/codemap update` | incremental: re-audit only changed modules, re-render | | `/codemap update` | incremental + git-aware: re-audit only changed modules, re-render |
| `/codemap test <module>` | generate tests (regression net) for a module | | `/codemap test <module>` | generate a regression-net of tests for a module |
| `/codemap fix <module>` | regression-gated fix: lock baseline → fix → independent acceptance → re-score | | `/codemap fix <module>` | regression-gated cleanup: lock baseline → fix → independent acceptance → re-score |
You can also run the deterministic scripts directly (no AI needed for these): The deterministic scripts (no AI needed) can also be run by hand:
```bash ```bash
S=~/.claude/skills/codemap S=~/.claude/skills/codemap
# what changed since last audit — incl. a `git` block listing the commits since the # what changed since last run — a `git` block lists commits + affected modules
# last codemap run (meta.rev) and which modules they touched python3 $S/scripts/scan.py --root . --state .codemap/modules.json
python3 $S/scripts/scan.py --root . --state .codemap/modules.json # cache the current HEAD as the new baseline (end of an update)
# after an update, cache the current HEAD as the new baseline for next time python3 $S/scripts/scan.py --root . --state .codemap/modules.json --stamp-rev
python3 $S/scripts/scan.py --root . --state .codemap/modules.json --stamp-rev # pick targets cheaply, without reading the whole state (for agents)
# find modules to act on without reading the whole state (token-cheap, for agents)
python3 $S/scripts/query.py --state .codemap/modules.json --max-grade C --format ids python3 $S/scripts/query.py --state .codemap/modules.json --max-grade C --format ids
python3 $S/scripts/query.py --state .codemap/modules.json --tag dual-format python3 $S/scripts/query.py --state .codemap/modules.json --tag dual-format
# regenerate the HTML + MD from the state # regenerate the HTML + report from the state
python3 $S/scripts/render.py --state .codemap/modules.json \ python3 $S/scripts/render.py --state .codemap/modules.json --template $S/assets/template.html \
--template $S/assets/template.html \ --out-html .codemap/codemap.html --out-md .codemap/codemap.md
--out-html .codemap/architecture-map.html --out-md .codemap/architecture-audit.md
``` ```
> On Windows use `python` instead of `python3`. > On Windows use `python` instead of `python3`.
## Using with Codex / other agents ## Using with Codex / other agents
The skill mechanism is Claude-specific, but the **engine is tool-agnostic**: the four The skill mechanism is Claude-specific, but the **engine is tool-agnostic** four
scripts are deterministic stdlib Python, and the workflow + rubric are plain Markdown deterministic stdlib-Python scripts plus a Markdown workflow and rubric. **OpenAI Codex**
(`SKILL.md`, `reference/STANDARDS.md`). Any capable agent can drive it. auto-reads the shipped **`AGENTS.md`**. To use codemap from Codex (or Cursor, Aider, …):
**OpenAI Codex** auto-reads an `AGENTS.md` in the working directory — this repo ships one 1. Clone this repo somewhere the agent can read, e.g. `git clone <url> ~/.codemap`.
that points Codex at the workflow and rubric. To use codemap from Codex (or Cursor, 2. Tell the agent: *"Use the codemap tool at `<path>` to map/audit this project — follow
Aider, etc.): its `SKILL.md`; score each module with a separate sub-task per `reference/STANDARDS.md`."*
3. It runs the same `scan → audit → apply_audit → render` loop, using `query.py` to target.
1. Make the tool available — clone this repo somewhere the agent can read it, e.g.
`git clone <url> ~/.codemap` (or vendor it into your project).
2. Tell the agent: *"Use the codemap tool at `<path>` to build/update the architecture
map for this project. Follow its `SKILL.md`; score each module with a separate
sub-task using `reference/STANDARDS.md`."*
3. The agent runs the same commands shown above (`scan.py` → per-module audit →
`apply_audit.py``render.py`), using `query.py` to pick targets cheaply.
The deterministic parts (scan / query / render / apply_audit) you can also run **by
hand** with no agent at all — only the *scoring*, *fixing*, and *test-writing* need a
model, and those just follow `reference/STANDARDS.md`.
## How it works ## How it works
``` ```
modules.json ──scan.py──▶ + LoC & content hash per module (stale = hash != auditedHash) modules.json ──scan.py──▶ + LoC, content hash & git diff (stale = hash != auditedHash)
│ (decomposition + descriptions are authored by the model) │ (decomposition + module descriptions: authored by the agent)
│◀─apply_audit.py── one INDEPENDENT subagent's score per module (fixed rubric) │◀─apply_audit.py── one INDEPENDENT subagent's score per module (fixed rubric)
│◀─query.py────────── token-cheap targeting (by grade / tag / severity / staleness) │◀─query.py────────── token-cheap targeting (by grade / tag / severity / staleness)
└──render.py────────▶ architecture-map.html + architecture-audit.md └──render.py────────▶ codemap.html + codemap.md
``` ```
- **Four separate subagent roles, never merged**: *auditor* (scores), *test-author* `modules.json` is the source of truth (commit it for an audit history); the HTML/MD are
(writes tests), *fixer* (changes code), *acceptance/verifier* (proves no regression). pure projections, regenerated by `render.py`. **Four separate subagent roles, never
A `fix` is accepted only when an independent acceptance subagent shows the pre-fix merged:** *auditor* (scores), *test-author* (writes tests), *fixer* (changes code),
green tests are still green and the build is clean. *acceptance/verifier* (proves no regression). Tests are the regression net and are kept
- Tests are excluded from a module's audit scope (they're the regression net, tracked out of a module's own audit scope.
separately in the module's `tests` field).
## Customizing the standard (capture *your* "屎山")
The scoring standard is **data, not code** (`reference/standard.json`: rubric, severities,
coupling, and issue tags with descriptions). Open the **Standard** page in the map → **Edit**
→ tweak descriptions, **add your own tags**, then **Export** to
`<project>/.codemap/standard.json`. Custom tags flow through the whole map and are used by
future audits. The prose version + the exact subagent prompt live in `reference/STANDARDS.md`.
## Repository layout ## Repository layout
``` ```
codemap/ codemap/
SKILL.md # the orchestration instructions Claude reads SKILL.md # the orchestration the agent reads
README.md # this file AGENTS.md # entry point for Codex / other agents
README.md
LICENSE # MIT
reference/ reference/
STANDARDS.md # the scoring rubric, smell taxonomy, severity, subagent prompts STANDARDS.md # scoring rubric, smell taxonomy, severities, subagent prompts
DATA_MODEL.md # the modules.json schema DATA_MODEL.md # modules.json schema
scripts/ # deterministic, stdlib-only Python standard.json # the machine-readable default standard (overridable per project)
scan.py # LoC + content hash + staleness report scripts/ # deterministic, stdlib-only Python
query.py # filter modules (grade/tag/severity/...) → ids/paths/findings scan.py # LoC + content hash + git diff + staleness
apply_audit.py # merge one subagent's audit result into the state query.py # filter modules (grade/tag/severity/…) → ids/paths/findings
render.py # modules.json → HTML + MD apply_audit.py # merge one subagent's audit into the state
render.py # modules.json → HTML + report
assets/ assets/
template.html # the interactive map shell (data injected at render time) template.html # the interactive map shell (data injected at render time)
examples/ # the screenshots above
``` ```
## Customizing the standard (capture your own "what's a problem") ## License
The standard is **data, not code** — it lives in `reference/standard.json` (rubric, [MIT](LICENSE) © 2026 Xingyu Chen.
severities, coupling, and the issue tags with descriptions). Two ways to customize:
- **In the map**: open the **Standard** page (header button), click **Edit**, change any ---
description, add your own tags (`+ Tag`), then **Export** → save the downloaded
`standard.json` to `<project>/.codemap/standard.json`. Edits are kept in the
browser until you export. Custom tags flow through the whole map (cards, filters,
report) and are used by future audits.
- **By file**: copy `reference/standard.json` to `<project>/.codemap/standard.json`
and edit it. `render.py` prefers the project file over the skill default.
The prose version + the exact subagent audit prompt live in `reference/STANDARDS.md` <sub>Keywords: code quality · technical debt · refactoring · code janitor · legacy code
(keep the two in sync when changing the shipped defaults). cleanup · architecture visualization · dependency graph · static analysis · code audit ·
Claude Code skill · Codex · AI agents · spaghetti code · 屎山.</sub>
## "Fix this" buttons & automation
Each module's detail panel has a **Copy fix prompt** button — it copies `/codemap fix
<module>` to the clipboard for you to paste into Claude Code (or adapt for Codex). The
page intentionally does **not** auto-launch an agent: a generated static HTML has no
privileged channel to the Claude CLI/agent, and auto-launching the CLI is fragile and
increasingly restricted. Copy-to-clipboard is the robust, future-proof bridge.
## Notes
- `modules.json` is meant to be **committed** with your project — it's the audit history
and what makes diffs/incrementality reviewable.
- The engine is **language-agnostic**: `paths` globs and LoC counting work for any stack;
the audit subagent reads whatever code the globs point at.
+5 -5
View File
@@ -12,15 +12,15 @@ description: >-
"is the arch map current", "update the architecture diagram", "fix module X". "is the arch map current", "update the architecture diagram", "fix module X".
--- ---
# Architecture Audit & Map # codemap — Architecture Map & Code-Quality Audit
Builds and maintains three coupled artifacts for a project: Builds and maintains three coupled artifacts for a project:
1. **`modules.json`** — the source of truth: every *functional* module (not file) with 1. **`modules.json`** — the source of truth: every *functional* module (not file) with
its paths, dependencies, coupling, LoC, content hash, score, grade, tags, findings. its paths, dependencies, coupling, LoC, content hash, score, grade, tags, findings.
2. **`architecture-map.html`** — a self-contained interactive map (layered modules, 2. **`codemap.html`** — a self-contained interactive map (layered modules,
dependency highlighting, health coloring, audit-report view). dependency highlighting, health coloring, audit-report view).
3. **`architecture-audit.md`** — the written report (per-layer scores, per-module LoC 3. **`codemap.md`** — the written report (per-layer scores, per-module LoC
table, worst offenders, cross-cutting themes). table, worst offenders, cross-cutting themes).
The HTML and MD are **always regenerated** from `modules.json` by `render.py`. Never The HTML and MD are **always regenerated** from `modules.json` by `render.py`. Never
@@ -51,7 +51,7 @@ prompt) and `standard.json` (the machine copy) in sync if you change the default
- `config.json` — the user's saved preferences (UI language, output location, title…). - `config.json` — the user's saved preferences (UI language, output location, title…).
- `modules.json` — the state (source of truth). - `modules.json` — the state (source of truth).
- `standard.json` — optional per-project custom audit standard. - `standard.json` — optional per-project custom audit standard.
- `architecture-map.html` + `architecture-audit.md` — the generated outputs (default). - `codemap.html` + `codemap.md` — the generated outputs (default).
The output location is a user preference: if they want the HTML/MD committed/visible, The output location is a user preference: if they want the HTML/MD committed/visible,
let them point it at `docs/` instead (ask — see `generate` step 0). Set let them point it at `docs/` instead (ask — see `generate` step 0). Set
@@ -129,7 +129,7 @@ Use when no `modules.json` exists yet (this is also "init").
Write `config.json` like: Write `config.json` like:
```json ```json
{"lang":"zh","project":"My App","subtitle":"…","outputDir":".codemap", {"lang":"zh","project":"My App","subtitle":"…","outputDir":".codemap",
"htmlFile":"architecture-map.html","mdFile":"architecture-audit.md"} "htmlFile":"codemap.html","mdFile":"codemap.md"}
``` ```
and apply it to `meta` when you build `modules.json`. Re-read `config.json` on later and apply it to `meta` when you build `modules.json`. Re-read `config.json` on later
runs so preferences persist. runs so preferences persist.
Binary file not shown.

After

Width:  |  Height:  |  Size: 445 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 453 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 487 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 554 KiB

File diff suppressed because one or more lines are too long
+186
View File
@@ -0,0 +1,186 @@
<!--
This file: .codemap/codemap.md (written report)
Interactive map: .codemap/codemap.html
-->
# Acme Storefront — Functional Module Quality Audit
> **Interactive view:** [`.codemap/codemap.html`](codemap.html) — per-module scores, findings, LoC, and the dependency graph. This file is the written report.
**Generated:** 2026-01-01 · **Modules:** 49 · **Size:** ≈ 28,600 LoC · 214 files (sample)
## Health by layer
| Layer | Modules | Avg score |
|---|--:|--:|
| Frontend · Shell & Routing | 3 | 87 |
| Frontend · Pages | 7 | 73 |
| Frontend · State Stores | 6 | 79 |
| Frontend · Transport | 5 | 80 |
| Backend · API Routes | 6 | 77 |
| Backend · Services | 8 | 73 |
| Backend · Domain Core | 5 | 89 |
| Backend · Persistence | 3 | 82 |
| Backend · Workers & Jobs | 3 | 77 |
| Integrations | 3 | 74 |
## Per-module lines of code & score
_LoC is the representative file/folder per module; folder-level modules overlap and are not additive._
### Frontend · Shell & Routing
| Module | LoC | Score | Tags |
|---|--:|:--|:--|
| App Shell | 420 | 84 B | — |
| Navigation | 240 | 88 B | — |
| Router | 180 | 90 A | — |
### Frontend · Pages
| Module | LoC | Score | Tags |
|---|--:|:--|:--|
| Admin | 2,100 | 66 C | bloat, any-escape |
| Checkout | 1,860 | 58 D | god-component, dual-format, fallback |
| Cart | 1,420 | 70 C | god-component |
| Product | 1,240 | 72 C | bloat |
| Catalog | 980 | 78 B | — |
| Account | 760 | 82 B | — |
| Search | 540 | 84 B | — |
### Frontend · State Stores
| Module | LoC | Score | Tags |
|---|--:|:--|:--|
| checkoutStore | 480 | 62 C | dual-format, legacy |
| catalogStore | 410 | 86 B | — |
| cartStore | 360 | 74 C | duplication |
| authStore | 290 | 80 B | — |
| searchStore | 220 | 82 B | — |
| uiStore | 150 | 88 B | — |
### Frontend · Transport
| Module | LoC | Score | Tags |
|---|--:|:--|:--|
| apiClient | 690 | 68 C | glue, bloat |
| wsClient | 230 | 84 B | — |
| adminClient | 180 | 80 B | — |
| paymentsClient | 140 | 86 B | — |
| pricingClient | 110 | 84 B | — |
### Backend · API Routes
| Module | LoC | Score | Tags |
|---|--:|:--|:--|
| orders routes | 610 | 74 C | dual-format |
| API Gateway | 540 | 88 B | — |
| payments routes | 480 | 55 D | stub, fallback |
| auth routes | 420 | 78 B | silent-except |
| products routes | 360 | 84 B | — |
| search routes | 240 | 82 B | — |
### Backend · Services
| Module | LoC | Score | Tags |
|---|--:|:--|:--|
| OrderService | 1,480 | 64 C | duplication, bloat |
| PricingEngine | 880 | 70 C | over-fit |
| InventoryService | 540 | 78 B | — |
| AuthService | 520 | 80 B | — |
| SearchService | 470 | 76 B | fallback |
| CatalogService | 430 | 86 B | — |
| NotificationService | 300 | 84 B | — |
| PaymentService | 260 | 48 D | stub, fake-output |
### Backend · Domain Core
| Module | LoC | Score | Tags |
|---|--:|:--|:--|
| Order | 380 | 88 B | — |
| Product | 260 | 90 A | — |
| User | 210 | 88 B | — |
| Money | 120 | 92 A | — |
| TokenUtil | 90 | 86 B | — |
### Backend · Persistence
| Module | LoC | Score | Tags |
|---|--:|:--|:--|
| Repository | 640 | 72 C | duplication |
| Migrations | 220 | 85 B | — |
| DB Pool | 180 | 90 A | — |
### Backend · Workers & Jobs
| Module | LoC | Score | Tags |
|---|--:|:--|:--|
| WebhookDispatcher | 340 | 68 C | silent-except, legacy |
| EmailWorker | 260 | 80 B | — |
| Templates | 150 | 84 B | — |
### Integrations
| Module | LoC | Score | Tags |
|---|--:|:--|:--|
| Stripe Gateway | 280 | 78 B | — |
| Shipping Provider | 230 | 74 C | glue |
| Analytics | 190 | 70 C | silent-except |
## Worst offenders
- **PaymentService (48/D)** — services/payment.py:31: charge()/refund() return a canned `{status:'succeeded'}` — sandbox stub, no real gateway call.
- **payments routes (55/D)** — api/payments.py:44: webhook handler always returns 200 without verifying the signature (stub).
- **Checkout (58/D)** — src/pages/Checkout.tsx: 1860-line god-component mixing the address/shipping/payment steps, validation and direct API calls.
- **checkoutStore (62/C)** — src/stores/checkout.ts:40: reads both snake_case and camelCase address fields (dual-format).
- **OrderService (64/C)** — services/order.py: 1480-line service; the order state machine is duplicated between place() and fulfill().
- **Admin (66/C)** — src/pages/Admin.tsx: 2100-line page: reports, tables and editors all in one file.
- **apiClient (68/C)** — src/transport/apiClient.ts: ~50 one-line get/post wrappers that only forward args (glue) — generate or collapse to a typed client.
- **WebhookDispatcher (68/C)** — workers/webhooks.py:55: `except: pass` swallows delivery errors — failed webhooks vanish.
- **Cart (70/C)**
- **PricingEngine (70/C)** — services/pricing.py:120: discount rules hardcoded to the current promo set (over-fit).
## All findings
### HIGH (4)
- **Checkout** · `src/pages/Checkout.tsx` — 1860-line god-component mixing the address/shipping/payment steps, validation and direct API calls.
- **payments routes** · `api/payments.py:44` — webhook handler always returns 200 without verifying the signature (stub).
- **OrderService** · `services/order.py` — 1480-line service; the order state machine is duplicated between place() and fulfill().
- **PaymentService** · `services/payment.py:31` — charge()/refund() return a canned `{status:'succeeded'}` — sandbox stub, no real gateway call.
### MED (13)
- **Product** · `src/pages/Product.tsx` — 1240-line component: gallery, variant picker and reviews in one file.
- **Checkout** · `src/pages/Checkout.tsx:412` — reads both `postal_code` and `postalCode` from the address form (dual-format).
- **Admin** · `src/pages/Admin.tsx` — 2100-line page: reports, tables and editors all in one file.
- **cartStore** · `src/stores/cart.ts:90` — cart totals re-implemented here and in PricingEngine (duplication).
- **checkoutStore** · `src/stores/checkout.ts:40` — reads both snake_case and camelCase address fields (dual-format).
- **apiClient** · `src/transport/apiClient.ts` — ~50 one-line get/post wrappers that only forward args (glue) — generate or collapse to a typed client.
- **orders routes** · `api/orders.py:88` — accepts both the legacy and v2 cart payload shapes (dual-format).
- **payments routes** · `api/payments.py:70` — falls back to marking the order paid when the provider call times out.
- **OrderService** · `services/order.py:620` — inventory reservation logic copy-pasted from InventoryService.
- **PricingEngine** · `services/pricing.py:120` — discount rules hardcoded to the current promo set (over-fit).
- **PaymentService** · `services/payment.py:88` — 'TODO: wire the real provider before launch.'
- **Repository** · `data/repo.py` — per-entity CRUD copy-pasted across 9 repositories — extract a base.
- **WebhookDispatcher** · `workers/webhooks.py:55``except: pass` swallows delivery errors — failed webhooks vanish.
### LOW (9)
- **Checkout** · `src/pages/Checkout.tsx:980` — silent catch around the shipping-rate fetch falls back to a flat rate.
- **Admin** · `src/pages/Admin.tsx:300` — several `as any` casts around the chart library.
- **checkoutStore** · `src/stores/checkout.ts:8` — legacy single-step draft kept for old links.
- **apiClient** · `src/transport/apiClient.ts:1` — one 690-line file mixing transport with the whole endpoint surface.
- **auth routes** · `api/auth.py:140` — broad except around the OAuth token exchange logs but swallows the cause.
- **SearchService** · `services/search.py:80` — documented fallback to SQL LIKE when Elasticsearch is unreachable.
- **WebhookDispatcher** · `workers/webhooks.py:12` — legacy v1 payload path kept alongside v2.
- **Shipping Provider** · `integrations/shipping.py` — adapter forwards every field unchanged (glue).
- **Analytics** · `integrations/analytics.py:22` — fire-and-forget send swallows failures silently.
## Cross-cutting themes
- **Payments is the weakest area.** PaymentService and the payments routes are still sandbox stubs (fake-output / stub) — real provider integration is unfinished, yet it is already wired into checkout.
- **Checkout and Order carry the most debt.** checkout_page, cartStore and OrderService are god-components with duplicated state-machine logic; the multi-step checkout mixes UI, validation and API calls in one file.
- **Dual-format is creeping in at the order boundary.** orders routes, checkoutStore and the checkout page accept both legacy and v2 payload shapes — normalize once at the transport layer instead.
- **apiClient is mostly glue.** ~50 near-identical endpoint wrappers add no value; generate them or collapse to a single typed client.
File diff suppressed because it is too large Load Diff
+2 -2
View File
@@ -15,8 +15,8 @@ subagents edit it) and re-render.
"generatedAt": "2026-01-01", "generatedAt": "2026-01-01",
"rev": "abc1234", // git HEAD at the last codemap run (the baseline "rev": "abc1234", // git HEAD at the last codemap run (the baseline
// `update`/`check` diff from; set by scan.py --stamp-rev) // `update`/`check` diff from; set by scan.py --stamp-rev)
"htmlPath": ".codemap/architecture-map.html", // output location (for reciprocal links) "htmlPath": ".codemap/codemap.html", // output location (for reciprocal links)
"mdPath": ".codemap/architecture-audit.md", "mdPath": ".codemap/codemap.md",
"spineDesc": "A user edits … → … → persistence.", // shown on the spine view "spineDesc": "A user edits … → … → persistence.", // shown on the spine view
"tracked_loc": 184900, // filled by scan.py --write (union, de-duped) "tracked_loc": 184900, // filled by scan.py --write (union, de-duped)
"tracked_files": 980, "tracked_files": 980,
+6 -6
View File
@@ -1,5 +1,5 @@
#!/usr/bin/env python3 #!/usr/bin/env python3
"""render.py — regenerate architecture-map.html + architecture-audit.md from modules.json. """render.py — regenerate codemap.html + codemap.md from modules.json.
modules.json is the single source of truth. The HTML and MD are pure projections modules.json is the single source of truth. The HTML and MD are pure projections
of it and must never be hand-edited. Run scan.py --write before rendering so LoC / of it and must never be hand-edited. Run scan.py --write before rendering so LoC /
@@ -8,7 +8,7 @@ content hashes are current.
Usage: Usage:
python render.py --state modules.json \ python render.py --state modules.json \
--template assets/template.html \ --template assets/template.html \
--out-html architecture-map.html --out-md architecture-audit.md --out-html codemap.html --out-md codemap.md
Stdlib only. Stdlib only.
""" """
@@ -70,12 +70,12 @@ def render_md(state):
out = [] out = []
proj = meta.get("project", "Project") proj = meta.get("project", "Project")
out.append("<!--") out.append("<!--")
out.append(f" This file: {meta.get('mdPath', 'architecture-audit.md')} (written report)") out.append(f" This file: {meta.get('mdPath', 'codemap.md')} (written report)")
out.append(f" Interactive map: {meta.get('htmlPath', 'architecture-map.html')}") out.append(f" Interactive map: {meta.get('htmlPath', 'codemap.html')}")
out.append("-->\n") out.append("-->\n")
out.append(f"# {proj} — Functional Module Quality Audit\n") out.append(f"# {proj} — Functional Module Quality Audit\n")
out.append(f"> **Interactive view:** [`{meta.get('htmlPath','architecture-map.html')}`]" out.append(f"> **Interactive view:** [`{meta.get('htmlPath','codemap.html')}`]"
f"({os.path.basename(meta.get('htmlPath','architecture-map.html'))}) — " f"({os.path.basename(meta.get('htmlPath','codemap.html'))}) — "
"per-module scores, findings, LoC, and the dependency graph. This file is the written report.\n") "per-module scores, findings, LoC, and the dependency graph. This file is the written report.\n")
gen = meta.get("generatedAt", "") gen = meta.get("generatedAt", "")
loc_line = meta.get("locLine") or ( loc_line = meta.get("locLine") or (