@ghostlygawd/codeweb 0.9.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (81) hide show
  1. package/.claude-plugin/plugin.json +30 -0
  2. package/CHANGELOG.md +480 -0
  3. package/LICENSE +21 -0
  4. package/README.md +573 -0
  5. package/agents/codeweb-dissector.md +56 -0
  6. package/agents/codeweb-domain-mapper.md +63 -0
  7. package/commands/codeweb.md +57 -0
  8. package/hooks/hooks.json +47 -0
  9. package/hooks/post-edit-diff.mjs +83 -0
  10. package/hooks/pre-edit-impact.mjs +94 -0
  11. package/hooks/session-brief.mjs +53 -0
  12. package/package.json +64 -0
  13. package/scripts/annotate.mjs +46 -0
  14. package/scripts/bench-ts-engine.mjs +59 -0
  15. package/scripts/bench.mjs +133 -0
  16. package/scripts/break-cycles.mjs +0 -0
  17. package/scripts/brief.mjs +28 -0
  18. package/scripts/build-report.mjs +182 -0
  19. package/scripts/campaign.mjs +61 -0
  20. package/scripts/check-consistency.mjs +45 -0
  21. package/scripts/ci-gate.mjs +86 -0
  22. package/scripts/cluster3.mjs +112 -0
  23. package/scripts/codemod.mjs +130 -0
  24. package/scripts/context-pack.mjs +118 -0
  25. package/scripts/deadcode.mjs +96 -0
  26. package/scripts/diff.mjs +0 -0
  27. package/scripts/explain.mjs +87 -0
  28. package/scripts/extract-symbols.mjs +1307 -0
  29. package/scripts/find-similar.mjs +86 -0
  30. package/scripts/find.mjs +50 -0
  31. package/scripts/fitness.mjs +90 -0
  32. package/scripts/grammars/PROVENANCE.md +36 -0
  33. package/scripts/grammars/tree-sitter-c-sharp.wasm +0 -0
  34. package/scripts/grammars/tree-sitter-go.wasm +0 -0
  35. package/scripts/grammars/tree-sitter-java.wasm +0 -0
  36. package/scripts/grammars/tree-sitter-python.wasm +0 -0
  37. package/scripts/grammars/tree-sitter-rust.wasm +0 -0
  38. package/scripts/grammars/tree-sitter-typescript.wasm +0 -0
  39. package/scripts/hotspots.mjs +53 -0
  40. package/scripts/lib/annotations.mjs +51 -0
  41. package/scripts/lib/bench-core.mjs +178 -0
  42. package/scripts/lib/brief-core.mjs +92 -0
  43. package/scripts/lib/campaign.mjs +73 -0
  44. package/scripts/lib/claims-check.mjs +44 -0
  45. package/scripts/lib/cli.mjs +140 -0
  46. package/scripts/lib/complexity.mjs +68 -0
  47. package/scripts/lib/dup-check.mjs +51 -0
  48. package/scripts/lib/find-core.mjs +85 -0
  49. package/scripts/lib/gate-md.mjs +50 -0
  50. package/scripts/lib/graph-ops.mjs +319 -0
  51. package/scripts/lib/hotspots.mjs +34 -0
  52. package/scripts/lib/query-core.mjs +85 -0
  53. package/scripts/lib/reading-order.mjs +53 -0
  54. package/scripts/lib/reliance.mjs +143 -0
  55. package/scripts/lib/risk.mjs +18 -0
  56. package/scripts/lib/shingles.mjs +39 -0
  57. package/scripts/lib/skeleton.mjs +41 -0
  58. package/scripts/lib/stats.mjs +92 -0
  59. package/scripts/lib/ts-engine.mjs +605 -0
  60. package/scripts/mcp-server.mjs +370 -0
  61. package/scripts/optimize.mjs +152 -0
  62. package/scripts/overlap.mjs +380 -0
  63. package/scripts/placement.mjs +93 -0
  64. package/scripts/query.mjs +94 -0
  65. package/scripts/reading-order.mjs +36 -0
  66. package/scripts/refresh.mjs +73 -0
  67. package/scripts/release-utils.mjs +158 -0
  68. package/scripts/release.mjs +62 -0
  69. package/scripts/report-template.html +730 -0
  70. package/scripts/review.mjs +112 -0
  71. package/scripts/risk.mjs +77 -0
  72. package/scripts/run.mjs +96 -0
  73. package/scripts/screenshot.mjs +104 -0
  74. package/scripts/simulate-edit.mjs +70 -0
  75. package/scripts/stats.mjs +31 -0
  76. package/scripts/trend.mjs +147 -0
  77. package/scripts/version-sync.mjs +19 -0
  78. package/skills/codebase-anatomy/SKILL.md +174 -0
  79. package/skills/codebase-anatomy/references/engine-detection.md +49 -0
  80. package/skills/codebase-anatomy/references/graph-schema.md +120 -0
  81. package/skills/codebase-anatomy/references/overlap-heuristics.md +52 -0
@@ -0,0 +1,19 @@
1
+ #!/usr/bin/env node
2
+ /**
3
+ * Propagate the canonical version (package.json) and the canonical MCP tool count
4
+ * (scripts/mcp-server.mjs) out to every place they would otherwise drift:
5
+ * the plugin manifest and the skill frontmatter. Run after bumping package.json.
6
+ *
7
+ * node scripts/version-sync.mjs (or: npm run version-sync)
8
+ */
9
+ import { dirname, join } from 'node:path';
10
+ import { fileURLToPath } from 'node:url';
11
+ import { getVersion, mcpToolCount, applySync } from './release-utils.mjs';
12
+
13
+ const ROOT = join(dirname(fileURLToPath(import.meta.url)), '..');
14
+ const version = getVersion(ROOT);
15
+ const count = mcpToolCount(ROOT);
16
+ const changed = applySync(ROOT, version, count);
17
+
18
+ process.stdout.write(`version-sync: v${version}, ${count} MCP tools.\n`);
19
+ process.stdout.write(changed.length ? ` updated: ${changed.join(', ')}\n` : ' all targets already in sync.\n');
@@ -0,0 +1,174 @@
1
+ ---
2
+ name: codebase-anatomy
3
+ description: Dissect a codebase to atomic nodes (functions, classes, symbols), wire the call/import web, tag each node's domain, and build a cross-domain overlap graph that ranks consolidation/de-duplication opportunities, then render an interactive HTML map. Use to restructure your own codebase into well-defined non-duplicative systems, OR to fully map and review an external repo (git URL / owner-repo) before adopting it. Triggers include "map this codebase", "dependency/relationship graph", "find duplication/overlap", "atomic dissection", "review this repo before I use it", and the /codeweb command.
4
+ version: 0.9.0
5
+ metadata:
6
+ origin: community
7
+ ---
8
+
9
+ # Codebase Anatomy (codeweb)
10
+
11
+ Build a "biological web" of a system: every atomic part (function, class, method, exported
12
+ symbol) is a node; calls/imports/inheritance are the edges; each node belongs to a domain; and
13
+ an **overlap graph** shows where separate parts do the same work — the simplification targets.
14
+
15
+ The goal is not a pretty picture. It is an **evidence-backed restructure plan**: which
16
+ duplicated logic should collapse into one well-defined system, and who should depend on it.
17
+
18
+ ## When to Use
19
+
20
+ - Restructuring your own codebase and you need to *see* the real dependency web and where it's
21
+ duplicative before moving code.
22
+ - Onboarding to a large/legacy system at symbol resolution, not just folder-level.
23
+ - Reviewing an **external** repo (a plugin, library, or template you found on GitHub) end-to-end
24
+ before adopting it — what it does, how it's wired, and whether it's worth committing to.
25
+ - Hunting cross-cutting duplication that file/module tools miss (the same check coded N times in
26
+ N domains).
27
+
28
+ ## Two Modes
29
+
30
+ - **internal** — analyze the current project; produce a domain map + overlap graph + restructure
31
+ recommendations.
32
+ - **external** — `target` is a git URL or `owner/repo`. Clone it **read-only** to a temp dir,
33
+ map it the same way, and add an **adoption review** (risk, dependencies, architecture verdict,
34
+ "should you adopt this?").
35
+
36
+ Auto-detect: a URL or `owner/repo` ⇒ external; a path or `.` ⇒ internal. `--mode` overrides.
37
+
38
+ ## Non-Negotiable Rules
39
+
40
+ - **Never execute the target.** No build, run, test, install, or entrypoint — for internal or
41
+ external code. Only read files and invoke read-only static-analysis tools that inspect files
42
+ already on disk. This is absolute for external repos (their toolchain can run their code).
43
+ - **Evidence over guesswork.** Every node, edge, domain, and overlap must trace to code you read
44
+ or a tool emitted. When unsure of an edge or an overlap, omit it or mark it `low` — never
45
+ inflate. A report that cries wolf is worse than no report.
46
+ - **No silent truncation.** If you cap depth, sample, or skip files, say so in the report and in
47
+ `meta`. Coverage gaps must be visible.
48
+ - **One graph, one schema.** Everything conforms to `references/graph-schema.md`. The HTML
49
+ renderer and all agents depend on it.
50
+ - **Reuse, don't reinvent.** Hand off to `repo-scan` (file/library classification),
51
+ `codebase-onboarding` (guides), `refactor-cleaner` (acting on the list) rather than redoing
52
+ their jobs.
53
+
54
+ ## Outputs (under `<target>/.codeweb/`)
55
+
56
+ 1. `graph.json` — the web: `nodes`, `edges`, `domains`, `overlaps`, plus `meta` (roles,
57
+ staleness stamps, stats).
58
+ 2. `report.html` — self-contained interactive map (force graph, domain tree, node details,
59
+ ranked overlap tab; product-only filter by default). No network/CDN.
60
+ 3. `report.md` — the same map as plain markdown (mermaid domain graph + overlaps).
61
+ 4. `overlap.md` — the ranked consolidation opportunities in plain markdown.
62
+ 5. `optimize.md` — the consolidation advisory (ready / blocked / review tiers).
63
+ 6. `fragment.json` — the raw extractor output (pipeline stage 1).
64
+ 7. (external mode) an adoption review section appended to `overlap.md` and your final summary.
65
+
66
+ ## References
67
+
68
+ - `references/graph-schema.md` — the exact JSON shape and merge rules. **Read before dissecting.**
69
+ - `references/engine-detection.md` — the hybrid engine: which analysis tool per language, how to
70
+ probe for it, and depth/scaling rules.
71
+ - `references/overlap-heuristics.md` — the four overlap kinds and the severity rubric.
72
+
73
+ ## Core Workflow
74
+
75
+ ### 0 — Scope & acquire
76
+
77
+ - Parse `target`, `--depth`, `--engine`, `--focus`, `--mode`, `--open`.
78
+ - internal: resolve the path (default `.`). external: `git clone --depth 1 <url>` into a temp
79
+ dir (e.g. `${TMPDIR}/codeweb-<repo>`); **do not** run anything inside it.
80
+ - Detect languages, package managers, and repo size (`rg --files | wc -l`). Choose effective
81
+ depth: `auto` ⇒ module-level overview, then symbol-level on the densest/most-overlapping
82
+ subsystems. Probe for analysis tools per `engine-detection.md`; record the engine.
83
+
84
+ ### Fast path (default) — one-command deterministic engine
85
+
86
+ For languages the bundled extractor handles (**JavaScript, TypeScript, Python, Rust, Go, Java, C#, Ruby, PHP, Kotlin, Swift**), run the whole
87
+ pipeline in a single command instead of dissecting by hand. It is faster, cheaper, and
88
+ reproducible, and emits the same `graph.json` schema:
89
+
90
+ ```bash
91
+ node "${CLAUDE_PLUGIN_ROOT}/scripts/run.mjs" "<target>" --target "<label>" --out-dir "<target>/.codeweb"
92
+ ```
93
+
94
+ It chains extract → cluster (directory-anchored domains) → overlap (body-confirmed duplication)
95
+ → optimize (consolidation advisory) → render, writing `fragment.json`, `graph.json`, `overlap.md`, `optimize.md`, `report.html`, `report.md` into the
96
+ workspace (defaults to `<plugin>/.codeweb/runs/<slug>/` when `--out-dir` is omitted). The script is
97
+ read-only over the target and resolves its own paths, so it works from any cwd. **When it
98
+ succeeds, skip steps 1–5 and go to step 6.** Use the agent-based passes below only as a
99
+ **fallback** — for languages the extractor can't parse, an explicit `--engine read`, or to enrich
100
+ findings the scripts left `low`-confidence.
101
+
102
+ ### 1 — Survey & partition (agent-based fallback; skip if the fast path ran)
103
+
104
+ - Build the subsystem map: top-level source dirs / packages, excluding vendored and build dirs
105
+ (lean on `repo-scan`'s classification idea — project vs third-party vs artifact).
106
+ - Partition the project code into ~4–12 scopes of comparable size. These are the parallel units.
107
+
108
+ ### 2 — Dissect (parallel `codeweb-dissector`)
109
+
110
+ - Spawn one `codeweb-dissector` per scope **in parallel** (single message, multiple agents).
111
+ Pass each: its scope, the engine mode, the detected tools, and the node/edge schema.
112
+ - Each returns `{nodes, edges}` for its slice. They may reference ids outside their scope.
113
+ - If subagents aren't available, run the passes sequentially with the same instructions.
114
+
115
+ ### 3 — Merge
116
+
117
+ - Union all nodes (dedupe by `id`) and edges (unique by `from,to,kind`; sum `weight`).
118
+ - Drop edges whose endpoints aren't in the node set (dangling refs to skipped code).
119
+
120
+ ### 4 — Domain-map & overlap (`codeweb-domain-mapper`)
121
+
122
+ - Pass the merged `{nodes, edges}` to one `codeweb-domain-mapper` with `overlap-heuristics.md`.
123
+ - It returns per-node `domain`, a `domains[]` summary, and ranked `overlaps[]`. Merge `domain`
124
+ back onto each node by `id`; attach `domains` and `overlaps` to the graph.
125
+
126
+ ### 5 — Stamp & render
127
+
128
+ - Write `.codeweb/graph.json`.
129
+ - Run the renderer:
130
+ ```bash
131
+ node "${CLAUDE_PLUGIN_ROOT}/scripts/build-report.mjs" "<target>/.codeweb/graph.json"
132
+ ```
133
+ (add `--open` to launch it). It computes `meta.stats`, stamps `generatedAt`, drops any
134
+ remaining dangling edges, writes the normalized graph back to `graph.json`, and writes
135
+ `report.html` + `report.md` beside it.
136
+
137
+ ### 6 — Overlap.md + summary
138
+
139
+ - Write `overlap.md`: the ranked overlaps as a checklist — title, kind, severity, the symbols
140
+ involved, evidence, and the named consolidation target. This is the restructure plan.
141
+ - Report to the user: artifact paths, the domain count, and the **top 3–5 consolidation
142
+ opportunities** with their recommendations.
143
+
144
+ ### 7 — External adoption review (external mode only)
145
+
146
+ Append an adoption verdict: what the repo is and does (from the domain map), notable
147
+ dependencies and risk surface (lean on `security-review` / `repo-scan` signals), architecture
148
+ quality (cohesion vs the overlap graph), and a clear **adopt / adapt / avoid** recommendation
149
+ with reasons. Then clean up the temp clone.
150
+
151
+ ## Scaling
152
+
153
+ Follow `engine-detection.md`: module depth for first pass and huge repos; expand to symbol depth
154
+ on the top subsystems by size and cross-domain edge density. If total nodes would exceed ~2000,
155
+ cap symbol expansion to the focus area and state it. The HTML renderer auto-starts in
156
+ domain-aggregated view above ~600 nodes and lets the user expand.
157
+
158
+ ## Handoffs
159
+
160
+ - `refactor-cleaner` — execute the consolidation list.
161
+ - `codebase-onboarding` — turn the domain map into an onboarding guide.
162
+ - `code-tour` — anchor a guided tour to the symbol index.
163
+ - `repo-scan` — deeper file-level / third-party classification.
164
+
165
+ ## Output Format
166
+
167
+ ```text
168
+ TARGET — path/url · mode · languages · engine · depth
169
+ WEB — nodes / edges / domains (+ coverage caveats)
170
+ DOMAINS — each domain: name · node count · one-line role
171
+ OVERLAPS — ranked: severity · kind · title · → consolidation target
172
+ ARTIFACTS— .codeweb/graph.json · report.html · overlap.md
173
+ VERDICT — (external only) adopt / adapt / avoid, with reasons
174
+ ```
@@ -0,0 +1,49 @@
1
+ # Hybrid engine — tool detection & fallback
2
+
3
+ codeweb prefers **precise edges from static-analysis tools** when they are installed, and falls
4
+ back to **agent reading** otherwise. Per subsystem, detect what's available, use it, and record
5
+ the chosen engine in `meta.engine`.
6
+
7
+ > **Native fast path (no tools required):** JavaScript, TypeScript, Python, Rust, and Go are parsed
8
+ > directly by the bundled regex extractor (`scripts/extract-symbols.mjs`). The table below is the
9
+ > *optional* sharpening / agent-fallback path — for these languages it only refines edges, and for
10
+ > the others it is the primary route.
11
+
12
+ ## Detection order (per language)
13
+
14
+ | Language | Preferred tool(s) | Detect with | Emits |
15
+ |---|---|---|---|
16
+ | JS/TS | `madge` (imports), `ts-morph`/`tsc --listFiles` | `npx --no-install madge -V`; `tsconfig.json` present | import graph, modules |
17
+ | Python | `pydeps`, `pyan3`, stdlib `ast` via a one-off script | `python -c "import ast"`; `pip show pydeps` | import + call graph |
18
+ | Go | `go list -deps`, `callgraph` (golang.org/x/tools) | `go version` | package + call graph |
19
+ | Rust | `cargo modules`, `cargo-call-stack` | `cargo --version` | module tree, calls |
20
+ | Java/Kotlin | `jdeps` (JDK), tree-sitter | `jdeps -version` | class/package deps |
21
+ | C/C++ | `clangd`/`clang -emit-ast`, `cscope`, `ctags` | `clang --version`; `ctags --version` | symbols, includes |
22
+ | Any | **universal-ctags** (symbols), **tree-sitter** (AST), `ripgrep` (call sites) | `ctags --version`; `tree-sitter --version` | symbol index |
23
+
24
+ `universal-ctags` + `ripgrep` is the broad fallback spine: ctags gives the symbol list with
25
+ locations; ripgrep finds call sites to draw edges. Both are read-only and cross-language.
26
+
27
+ ## Rules
28
+
29
+ - **Probe, don't install.** Check for a tool with a version/`--help` call. If it's missing,
30
+ do **not** `npm install` / `pip install` / `cargo install` it — drop to reading. (Especially
31
+ for external repos: installing their toolchain can execute their code.)
32
+ - **Tool output is the spine, reading fills the flesh.** Even with a tool, read representative
33
+ files to populate `summary`, `kind`, `loc`, and to confirm call edges the tool can't resolve
34
+ (dynamic dispatch, reflection, DI).
35
+ - **Record per-subsystem engine.** A repo can be `tools` for its Go service and `read` for its
36
+ shell scripts. Set `meta.engine` to the dominant mode, and note mixed coverage in the report.
37
+ - **Never execute the target.** No build, no run, no test, no entrypoint. Only invoke analysis
38
+ tools that statically inspect files already on disk.
39
+
40
+ ## Scaling / depth
41
+
42
+ - `module` depth: stop at file/module nodes and import edges — fast, good for first pass and
43
+ huge repos. Default top-level view.
44
+ - `symbol` depth: expand to function/class/method nodes and call edges — run it on the densest
45
+ or most-overlapping subsystems rather than the whole repo when node counts get large.
46
+ - `auto`: module-level everywhere, then symbol-level on the top subsystems by size and by
47
+ cross-domain edge density (where overlap is most likely).
48
+ - If total nodes would exceed ~2000, cap symbol expansion to the focus area and say so in the
49
+ report — never silently truncate.
@@ -0,0 +1,120 @@
1
+ # codeweb graph schema (`graph.json`)
2
+
3
+ The single source of truth that flows between dissectors, the domain-mapper, and the HTML
4
+ renderer. All examples below use synthetic values.
5
+
6
+ ```json
7
+ {
8
+ "meta": {
9
+ "target": "src/ or https://github.com/owner/repo",
10
+ "mode": "internal | external",
11
+ "engine": "hybrid | tools | read",
12
+ "complexityEngine": "tree-sitter(...)", // present ONLY under --engine tree-sitter; signals exact
13
+ // complexity + class-qualified method ids + dispatch edges
14
+ "depth": "module | symbol | auto",
15
+ "languages": ["typescript", "python"],
16
+ "generatedAt": "ISO-8601 string, stamped by the renderer (not by agents)",
17
+ "stats": { "files": 0, "nodes": 0, "edges": 0, "domains": 0, "overlaps": 0 }
18
+ },
19
+
20
+ "nodes": [
21
+ {
22
+ "id": "src/auth/login.ts:loginUser", // <repo-relative-path>:<symbol> (path alone for file/module nodes).
23
+ // Under --engine tree-sitter a METHOD's <symbol> is class-qualified
24
+ // (`Class.method`) so same-named methods don't collide; bare otherwise.
25
+ "label": "loginUser", // display name — a method label stays BARE (`method`, never `Class.method`)
26
+ "kind": "function", // function | class | method | module | file
27
+ "file": "src/auth/login.ts",
28
+ "line": 42,
29
+ "loc": 120, // size of the symbol body, for node radius
30
+ "exports": true,
31
+ "domain": "auth", // assigned by domain-mapper (empty from dissectors)
32
+ "summary": "Authenticates a user and issues a session token.",
33
+ "complexity": 7, // F4: approximate cyclomatic complexity (function|method only; absent on class/module)
34
+ "maxDepth": 3 // F4: max control-flow nesting depth (function|method only)
35
+ }
36
+ ],
37
+
38
+ "edges": [
39
+ {
40
+ "from": "src/auth/login.ts:loginUser",
41
+ "to": "src/db/query.ts:runQuery",
42
+ "kind": "call", // call | import | inherit | ref | test (emitted) · dataflow (reserved)
43
+ "weight": 1 // number of occurrences; optional, default 1
44
+ }
45
+ ],
46
+
47
+ "domains": [
48
+ {
49
+ "name": "auth",
50
+ "nodes": 12,
51
+ "summary": "Authentication, session issuance, and authorization checks.",
52
+ "files": ["src/auth/"] // optional, representative paths
53
+ }
54
+ ],
55
+
56
+ "overlaps": [
57
+ {
58
+ "id": "ov1",
59
+ "title": "User validation duplicated across auth, billing, and api",
60
+ "kind": "duplicate-logic", // duplicate-logic | parallel-impl | shared-responsibility | tangled-domain
61
+ "severity": "high", // high | medium | low
62
+ "domains": ["auth", "billing", "api"],
63
+ "nodes": [
64
+ "src/auth/login.ts:validateUser",
65
+ "src/billing/charge.ts:checkUser",
66
+ "src/api/guard.ts:assertUser"
67
+ ],
68
+ "evidence": "All three re-implement the same email + password + active-role check.",
69
+ "recommendation": "Extract a single auth.validateUser; have billing and api depend on it."
70
+ }
71
+ ]
72
+ }
73
+ ```
74
+
75
+ ## Edge kinds
76
+
77
+ - **`call`** — a function/method invokes another, OR passes it by name as a higher-order argument
78
+ (`arr.map(fn)`, `rl.on('x', fn)`). The deterministic extractor resolves the target by import alias,
79
+ same-file definition, or a unique global definition, and DROPS ambiguous multi-definition names
80
+ rather than guess (precision over recall). A method call `obj.fn()` is NOT wired to a top-level `fn`
81
+ by the regex engine. Under `--engine tree-sitter`, dynamic-dispatch calls DO resolve: `this.m()`
82
+ (within a class) and typed-receiver `x.m()` (where `x: T` is a known class) wire to the
83
+ class-qualified method id (`<path>:T.m`); untyped/array/generic receivers are still dropped.
84
+ - **`import`** — a module imports a symbol from another module.
85
+ - **`inherit`** — a class extends/subclasses another (`class X extends Y`, `class X(Y):`), resolved
86
+ with the same precision gate as calls. Counts toward reachability: an extended base is not a
87
+ dead-code orphan, and `--impact` of a base includes its subclasses.
88
+ - **`ref`** — a symbol references a CLASS by identity without invoking it directly: `obj instanceof X`
89
+ or a static-method call `X.from(...)` (where `X` is an imported class or a same-file class). The
90
+ `.from()` site ALSO emits a `call` edge to the static method; the `ref` edge records the dependency on
91
+ the class itself so `--dependents <class>` surfaces every user (an `instanceof`/static-factory user is
92
+ not a `call`-edge caller). Precision-safe: an object-default alias (`import utils from './utils'`)
93
+ emits no `ref` — `utils` is not a class. Counts toward `--dependents` and reachability (not an orphan).
94
+ - **`test`** — a `call`/`ref` originating in a test file (`*.test.*`, `tests/` …) to a production
95
+ symbol, reclassified so production caller/orphan queries can exclude test-only usage while
96
+ `--dependents`/`--tests` still surface it.
97
+ - **`dataflow`** — RESERVED, not emitted by any stage today. Precise value/taint tracking
98
+ (source→sink) needs type/dispatch resolution and alias awareness the deterministic regex extractor
99
+ does not have; a noisy approximation would undermine the precision the other edge kinds guarantee
100
+ (codeweb's whole contract is "don't guess"). Reserved for a future optional type-resolution tier —
101
+ the schema lists it so consumers can forward-handle it, but no code produces it. Use an external
102
+ analyzer (Semgrep/CodeQL) for taint until then.
103
+
104
+ ## Merge rules (orchestrator)
105
+
106
+ - Dissectors emit `nodes` + `edges` per scope. Deduplicate by node `id`; an edge is unique by
107
+ `(from, to, kind)` — sum `weight` on collision.
108
+ - Edges may reference node ids owned by another scope. After merging all dissectors, drop any
109
+ edge whose `from` or `to` id does not exist as a node (dangling reference to skipped code).
110
+ - The domain-mapper returns `{nodes:[{id,domain}], domains, overlaps}`. Merge each `domain`
111
+ back onto the matching full node by `id`.
112
+ - The renderer (`build-report.mjs`) computes `meta.stats`, stamps `meta.generatedAt`, and persists
113
+ both back into `graph.json` (along with the dangling-edge drop), so the on-disk graph matches the
114
+ rendered report.
115
+
116
+ ## Minimum viable graph
117
+
118
+ A graph is renderable with just `nodes` and `edges`. `domains` and `overlaps` enrich the
119
+ report; if absent, the renderer treats every node as domain `"unassigned"` and shows an empty
120
+ overlap tab.
@@ -0,0 +1,52 @@
1
+ # Overlap detection heuristics
2
+
3
+ The overlap graph is the payoff of codeweb: it shows where the system does the **same work in
4
+ more than one place**, so it can be restructured into well-defined, non-duplicative systems.
5
+
6
+ ## The four overlap kinds
7
+
8
+ | Kind | Signal | Example |
9
+ |---|---|---|
10
+ | `duplicate-logic` | The same algorithm/validation/transform appears in 2+ symbols with near-identical bodies or behaviour. | Three modules each parse the same date format by hand. |
11
+ | `parallel-impl` | Two or more competing implementations of one capability, often with diverging behaviour. | A `fetch` wrapper and an `axios` wrapper both used for API calls. |
12
+ | `shared-responsibility` | One concern is smeared across many domains with no single owner. | Retry/backoff logic copy-pasted into every network call site. |
13
+ | `tangled-domain` | A single symbol mixes responsibilities from multiple domains and should be split. | `saveOrder()` that also sends email, writes audit logs, and charges a card. |
14
+
15
+ ## How to find them
16
+
17
+ 1. **Name + signature clustering.** Group symbols whose names/params suggest the same job
18
+ (`validateUser`, `checkUser`, `assertUser`). Read the bodies to confirm they overlap.
19
+ 2. **Edge-pattern clustering.** Symbols in different domains that call the same downstream set,
20
+ or that are called from the same set of callers, are candidates for consolidation.
21
+ 3. **Structural similarity.** Similar control flow / similar literal sets (regexes, status
22
+ codes, field names) across symbols signals copy-paste drift.
23
+ 4. **Cross-domain fan-in.** A utility imported by many domains may be fine (good reuse) — but a
24
+ *behaviour* re-implemented in many domains is overlap. Distinguish "one shared function used
25
+ widely" (healthy) from "the same idea coded N times" (overlap).
26
+
27
+ ## Severity rubric
28
+
29
+ Score each overlap on three axes, then take the max-weighted band:
30
+
31
+ - **Duplication count** — 2 places = low, 3–4 = medium, 5+ = high.
32
+ - **Blast radius** — how many domains / how much traffic flows through the duplicated path.
33
+ Touches 1 domain = low, 2 = medium, 3+ or a core path = high.
34
+ - **Divergence risk** — have the copies already drifted (different edge cases, different bug
35
+ fixes)? Drift present = bump one band; security/correctness-sensitive logic = high.
36
+
37
+ `high` = block-worthy simplification target. `medium` = worth a refactor ticket. `low` = note.
38
+
39
+ ## Writing the recommendation
40
+
41
+ Always name the **single well-defined system** the pieces should collapse into, and who should
42
+ depend on it. Good: *"Extract `auth.validateUser` as the one validator; billing and api import
43
+ it; delete the local copies."* Bad: *"Reduce duplication."* The recommendation is the bridge
44
+ from diagnosis to a concrete restructure.
45
+
46
+ ## What is NOT overlap
47
+
48
+ - Genuinely independent logic that merely looks similar (different domains, different invariants).
49
+ - Intentional layering (an interface + one implementation).
50
+ - Test doubles / fixtures mirroring production shapes.
51
+ Flagging these erodes trust in the report — when unsure, mark `low` with explicit evidence
52
+ rather than inflating severity.