From c8557e244efe0a1da0220f2ed7a17b8dbf9ed3b8 Mon Sep 17 00:00:00 2001 From: Sutu Sebastian Date: Mon, 4 May 2026 12:27:45 +0300 Subject: [PATCH 01/20] docs(research): non-goals reassessment + fallow clone deep-dive map (2026-05) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Companion to research/fallow.md (capability tracker — what to adopt FROM fallow). This new doc inventories what THIS codebase already unlocks that the current Non-goals (v1) list forbids, post-C.11. User observation: many non-goals were defensive choices made when the project was 1/10th its current size, then carried forward unchallenged as the surface grew (15+ recipes, 12+ tables, 3 engines, watch mode, coverage, audit, impact). The reframe: stop asking "what should we not do?" and start asking "what does the SQL-index-with-three-transports actually unlock that no other tool does?" Findings: §1 — 10 first-class agent capabilities sitting in unwritten JOINs / formatters / verbs (components-touching-deprecated, unimported-exports, complexity per symbol, refactor-risk-ranking, boundary violations, unused type members, Mermaid output, MCP file/symbol resources, recipe usage telemetry, rename --dry-run preview). §2 — Five non-goals worth challenging: - "No FTS5 / use ripgrep" — SQLite ships FTS5; ripgrep loses JOIN composition (TODOs inside @deprecated functions in <50% covered files is one query, vs three tools today). - "No visualisation" — conflates rendering pixels with shaping render- ready data; Mermaid / D2 are JSON-shaped formatters (sibling of SARIF). - "No static analysis" — we already ship deprecated-symbols, untested- and-dead, barrel-files, fan-in/out; the line was rhetorical. Real boundary is "no opinionated rule engine, no fix mutation". - "No persistent daemon" — we have one (mcp --watch, serve --watch, watch); non-goal preserves a constraint that no longer exists. - "No LSP replacement" — show + impact + watch is 80% of LSP read-side; ship a thin shim consuming existing engines, don't write an LSP. §3 — Real architectural limits worth keeping (sub-100ms cold-start CLI, no LLM in box, no fix engine, no runtime tracing, no JS exec at index time). §4 — Map of /Users/sutusebastian/Developer/OSS/fallow clone deep-dive points: which crates / docs / configs to inspect before each shipped feature so we adopt patterns rather than reinvent. Cite-the-source-path discipline mirrors the existing research/fallow.md cite-the-PR habit. §5 — Recommended sequence: (a) FTS5 + Mermaid one-PR non-goal flip → (c) complexity column → (b) C.9 plugin layer (multi-tracer big surface) → (d) LSP shim. (a) is the cheapest non-goal flip; ships a confidence move before the bigger surfaces. §6 — 5 open questions (daemon-by-default for MCP/HTTP, FTS5 opt-in, LSP shim vs standalone, plugin contract scope, history table shape). Doc-governance compliance: - Goes in docs/research/ per Rule 3 (research-class doc). - Cross-references roadmap, why-codemap, fallow.md, competitive-scan per Rule 5. - Doesn't duplicate non-goals (Rule 1) — proposes amendments to be applied when § 2 items ship, in lockstep with why-codemap per the Single source of truth table. - No inventory counts in narrative (Rule 6) — uses qualitative "15+ recipes / 12+ tables" only. --- .../non-goals-reassessment-2026-05.md | 192 ++++++++++++++++++ 1 file changed, 192 insertions(+) create mode 100644 docs/research/non-goals-reassessment-2026-05.md diff --git a/docs/research/non-goals-reassessment-2026-05.md b/docs/research/non-goals-reassessment-2026-05.md new file mode 100644 index 00000000..c92c0449 --- /dev/null +++ b/docs/research/non-goals-reassessment-2026-05.md @@ -0,0 +1,192 @@ +# Non-goals reassessment — what _this_ codebase actually unlocks (2026-05) + +> **Status:** open · **Trigger:** post-C.11 ship; user observation that several non-goals were inherited from when the project was 1/10th its current size and never re-examined as the surface grew. +> +> **Companion docs:** [`research/fallow.md`](./fallow.md) (capability tracker — what to adopt _from fallow_); this file (capability inventory — what _our codebase_ can do that the [`roadmap.md § Non-goals`](../roadmap.md#non-goals-v1) currently forbids). +> +> **Local clone for deep-dives:** [`/Users/sutusebastian/Developer/OSS/fallow`](file:///Users/sutusebastian/Developer/OSS/fallow) — Cargo workspace with `crates/{lsp,mcp,v8-coverage,graph,extract,cli}`, `decisions/` (ADR-style), `editors/{vscode,zed}`, `docs/plugin-authoring.md`. Inspect for patterns we can adapt before each shipped feature. + +--- + +## 0. Reframing question + +The original [`Non-goals (v1)`](../roadmap.md#non-goals-v1) list was a **product-shape** constraint: "stay a SQL-index primitive, don't become a verdict tool, don't render visualisations, don't ship an LSP." That shape held when codemap had ~5 recipes, a single transport, and no engine reuse. + +It no longer holds. The current surface ships: + +- 15+ bundled recipes (incl. `untested-and-dead`, `files-by-coverage`, `worst-covered-exports` from C.11) plus project-local recipe loader +- 12+ tables — `symbols`, `imports`, `exports`, `components`, `dependencies`, `markers`, `type_members`, `calls`, `css_*`, `coverage`, `query_baselines`, `meta` +- Three transport-agnostic engines (CLI, MCP, HTTP) all dispatching the same pure handlers +- Watch mode (chokidar) + MCP `--watch` + HTTP `--watch` — a daemon by every meaningful definition +- Coverage (Istanbul + LCOV), audit (drift + ref-baseline), impact (graph walker), validate, show / snippet +- SARIF + GitHub annotations output formatters +- Self-healing `` reconcilers + Zod-as-config schema +- Recipe `actions` template (per-row agent hints) + +The right framing now: **what does the SQL-index-with-three-transports + worker-pool + watcher actually unlock that no other tool in the ecosystem does?** Re-list non-goals against capability, not against the original "stay narrow" prejudice. + +--- + +## 1. Capability inventory — already shippable today + +The data and pipeline exist. Each row needs only a recipe / formatter / verb to expose. Estimated effort assumes one tracer-bullet PR each. + +| # | Capability | What's already in place | What's needed | Effort | +| ---- | ------------------------------------------- | ------------------------------------------------------------------------------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------- | ------ | +| 1.1 | **Components calling deprecated symbols** | `components.hooks_used` + `calls` + `symbols.doc_comment LIKE '%@deprecated%'` | One bundled recipe (`components-touching-deprecated`) | XS | +| 1.2 | **Exports never imported anywhere** | `exports` LEFT JOIN `imports` (by `source` resolution) | One bundled recipe (`unimported-exports`) — sharper than `untested-and-dead` because it doesn't need coverage to be useful | XS | +| 1.3 | **Cyclomatic complexity per symbol** | `calls.caller_scope` already aggregates per-symbol; AST walker already counts nodes during parse | New `complexity REAL` column on `symbols` populated at parse time + recipe (`high-complexity-untested`) | S | +| 1.4 | **Refactor risk ranking** | `dependencies` (fan-in) + `coverage` (test coverage) | Recipe (`refactor-risk-ranking` — `fan_in × (100 - coverage_pct)`) | XS | +| 1.5 | **Boundary violations (config-driven)** | `dependencies` table + glob-matching primitives in `validate-engine` | New `--boundaries ` flag on `audit` or recipe consuming the config | S | +| 1.6 | **Type members consumed by external files** | `type_members` + `imports.specifiers` (JSON) | Recipe (`unused-type-members`) — needs JSON-extraction predicate on specifiers | S | +| 1.7 | **Mermaid / D2 / Graphviz output** | `dependencies` + `calls` already shape into edges; SARIF / annotations formatters demonstrate the formatter pattern | New `--format mermaid` formatter (sibling of SARIF in `output-formatters.ts`) | S | +| 1.8 | **More MCP resources** | Schema, recipes, skill already exposed via `resource-handlers.ts` | Add `codemap://files/{path}` (file shape — symbols, imports, exports, coverage) and `codemap://symbols/{name}` (LSP-like reads) | S | +| 1.9 | **Recipe usage telemetry** | `query_baselines` precedent (user-data table excluded from `dropAll()`) | New `recipe_usage` table + reconciler at MCP / HTTP request boundary; ranks recipes by recent agent use in `--recipes-json` | M | +| 1.10 | **Symbol-rename dry-run preview** | `calls` (callers) + `symbols.line_start` / `line_end` (locations) | New `codemap rename --dry-run --format diff` verb — borders on the "no fix engine" non-goal but stays read-only (just shows the diff) | M | + +**Aggregate**: ~10 first-class agent-facing capabilities sitting in unwritten JOINs / unwritten formatters. Same multiplicative effect as C.11's three bundled recipes. + +--- + +## 2. Non-goals worth challenging + +These were defensive choices made when the project was small. The codebase has matured past the original constraint. + +### 2.1 ❓ "No FTS5 / use ripgrep for full-text" + +**Original framing:** [`roadmap.md § Non-goals`](../roadmap.md#non-goals-v1) — "Full-text search across all file bodies — use ripgrep / IDE / opt-in FTS5". + +**What's actually true:** SQLite has FTS5 built in. We already ship a `WITHOUT ROWID` table (`coverage`); a `source_fts` virtual table indexed at parse time is structurally identical. ripgrep's "loss" is **JOIN composition** — once content is an FTS table, you can write: + +```sql +-- "TODO comments inside @deprecated functions in files with <50% coverage" +SELECT m.file_path, m.line_number, m.content +FROM markers m +JOIN symbols s ON s.file_path = m.file_path + AND m.line_number BETWEEN s.line_start AND s.line_end +LEFT JOIN coverage c ON c.file_path = s.file_path AND c.name = s.name AND c.line_start = s.line_start +WHERE s.doc_comment LIKE '%@deprecated%' + AND COALESCE(c.coverage_pct, 0) < 50 + AND m.kind = 'TODO'; +``` + +ripgrep can't compose with `symbols` / `coverage` / `markers` in one shot — it can produce a list of file paths the agent then has to JOIN in JS. Same anti-pattern C.11 fixed for coverage. + +**Verdict:** flip non-goal to **opt-in FTS5 capability**. Ship as `--with-fts` index flag (off by default to keep cold-start sub-100ms; on for projects that want it). Bundled recipe `text-in-deprecated-functions` exemplifies the JOIN. + +**Risk:** index size grows with `--with-fts`. Mitigation: opt-in; document the size tax in `architecture.md` § Schema. + +### 2.2 ❓ "No visualisation — rendering belongs to consumer" + +**Original framing:** "[Visualisation — skyline / ASCII art / animated diagrams; the index emits structured rows, rendering belongs to the consumer.]" + +**What's actually true:** the non-goal conflates **rendering** (drawing pixels) with **shaping data into render-ready formats**. We already emit SARIF (a renderer-specific JSON format) — Mermaid / D2 / Graphviz are the same shape (edges + nodes), just different consumers. A `--format mermaid` formatter on `impact` / `dependencies` recipes lets agents include diagrams inline in chat / PR comments without a second tool. + +**Verdict:** flip non-goal to **shape-only output formatters**. Codemap stops being hostile to renderers; it doesn't become one. Mermaid first (MCP clients render it natively in chat); D2 / Graphviz follow if demand emerges. + +### 2.3 ❓ "No static analysis" + +**Original framing:** "Static analysis — dead code, duplication, complexity, architecture-boundary detection, fix actions are a different product class (e.g. fallow, knip, jscpd)". + +**What's actually true:** we already ship `deprecated-symbols`, `untested-and-dead` (post-C.11), `barrel-files`, `fan-in`, `fan-out` — those **are** static analysis. The original line was rhetorical (we don't ship a rule engine with severity levels and `// codemap-disable-next-line` comments) but the bullet over-asserted. + +The **real** boundary: **no opinionated rule engine + no fix mutation**. Recipes that compute structural properties (complexity, boundary checks, unused exports) are pure SQL on top of the index — exactly the niche we own. + +**Verdict:** rewrite the non-goal as "no opinionated rule engine, no fix engine"; promote complexity / boundary / unused-exports to **first-class recipes** (items 1.3 / 1.5 / 1.2 above). + +### 2.4 ❓ "No persistent daemon" + +**Original framing:** "Persistent daemon process — SQLite supports concurrent readers and our one-shot CLI startup is sub-100ms; revisit only if MCP / HTTP measurements demand it." + +**What's actually true:** we **have** a daemon — `codemap watch`, `codemap mcp --watch`, `codemap serve --watch`. The non-goal preserves a constraint that no longer exists. The CLI cold-start argument still applies for one-shot `codemap query` invocations, but the long-running modes are explicitly daemon-shaped. + +**Capability unlocked:** caching parsed ASTs in memory between requests would drop incremental reindex from ms to µs. Worth measuring; the data path already exists (we just throw away the AST per request). + +**Verdict:** rewrite as "**daemon stays opt-in**; one-shot CLI never requires it." The current `--watch` flag is the right shape; just stop saying we don't have one. + +### 2.5 ❓ "No LSP replacement" + +**Original framing:** "Replacing LSP or language servers — no rename / go-to-definition / hover types". + +**What's actually true:** we have `show ` (file:line + signature → "go to definition"), `impact ` (callers / callees → "find references"), `watch` (live index → "background analysis"). That's 80% of LSP read-side. We don't have hover types or rename, but we don't need to **be** an LSP — we can ship a **thin LSP shim** that proxies to existing engines (fallow has `crates/lsp/` we can study for the protocol shape). + +**Verdict:** rewrite as "no LSP **engine**; LSP **shim** consuming the existing index is in scope." Defer the shim until plugin layer (C.9) lands — entry-point awareness sharpens "find references" accuracy. + +--- + +## 3. True architectural limits — preserve + +These aren't defensive prejudices; they're real shape constraints that the SQL-index approach genuinely doesn't fit. + +| Limit | Why it's real | +| --------------------------------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | +| **Sub-100ms cold-start CLI** | Every `codemap query` / `codemap show` is a one-shot read. Adding a daemon-by-default would break the ergonomics. Daemon stays opt-in (`--watch` flag) per § 2.4. | +| **No LLM in the box** | Embedded intent classification, semantic search over symbol names, embedding-driven recipe routing — the agent host owns this. We supply structure; they supply meaning. | +| **No fix engine** | We **read** structure. Mutating code is a different product class (codemod tools own this). Per-row `actions` hints are enough — agents execute. The `rename --dry-run` capability (item 1.10) is borderline; ships only if it stays read-only (diff output, no file writes). | +| **No runtime tracing** | V8 traces / production beacons — Fallow's paid moat. Static coverage ingestion (just shipped) is the opt-in slice. | +| **No JS execution at index time** | Config files via `import()` is the only exception; recipe SQL is parsed but never `eval`'d. Plugin layer (C.9) must respect this — plugins describe rules in static config (globs, glob → `is_entry: true` mappings), not by running arbitrary code. | + +--- + +## 4. What to inspect in the local fallow clone + +`/Users/sutusebastian/Developer/OSS/fallow` (Cargo workspace; ~149 releases as of 2026-04). Areas worth a deep-dive _before_ each shipped feature so we adopt patterns rather than reinvent: + +| Fallow surface | Codemap relevance | When to inspect | +| ---------------------------------- | ------------------------------------------------------------------------------------------------------------------------ | ---------------------------------------------------------- | +| `crates/lsp/` | LSP shim (§ 2.5) — protocol shape, message handlers, capability negotiation | Before § 2.5 ships | +| `crates/mcp/` | Cross-check our `mcp-server.ts` tool taxonomy against fallow's; spot tools we're missing | Before adding any MCP tool | +| `crates/v8-coverage/` | Out of scope for us (paid moat) but instructive for understanding the line between static (us) and runtime (them) | Reference only | +| `crates/graph/` | Graph algorithms (cycle detection, fan-in/fan-out scoring); compare against our recursive CTEs | Before complexity recipe (item 1.3) | +| `crates/extract/` | AST extraction patterns; cross-check our `parser.ts` against their oxc usage | Before any new `symbols` column | +| `decisions/` | ADR-style decision records (`001-no-typescript-compiler.md` etc.) — adopt the pattern for our own decisions | One-off: shape codemap's plan files toward this convention | +| `editors/vscode/` + `editors/zed/` | Reference for codemap's eventual VS Code extension (orthogonal to LSP shim — extension can use either) | When demand exists | +| `docs/plugin-authoring.md` | Plugin authoring guide — model for our C.9 plugin contract | Before C.9 plan | +| `docs/positioning.md` | How fallow positions vs. linters / type checkers — mirror for codemap's vs. ripgrep / LSP framing | Before any positioning doc revision | +| `plugin-schema.json` | JSON schema for plugins — direct precedent for C.9 contract | Before C.9 plan | +| `BENCHMARKS.md` + `benchmarks/` | Benchmark methodology + real numbers — cross-check our `query-output-benchmark` shape | Before any perf-sensitive recipe | +| `action.yml` + `action/` | GitHub Action wrapper — precedent for [`roadmap.md` Backlog](../roadmap.md#backlog) "GitHub Actions `workflow_dispatch`" | Before that backlog item lands | +| `_typos.toml` + `deny.toml` | Repo-hygiene config — adopt where it strengthens our pre-commit / CI | Background | + +**Discipline:** every PR that ships a feature with a fallow precedent cites the fallow source path it took inspiration from in the changeset / plan. Mirrors the existing [`research/fallow.md` Status snapshot](./fallow.md#status-snapshot-as-of-2026-05-03) cite-the-PR habit. + +--- + +## 5. Recommended next-pick under the new framing + +| Pick | Effort | Agent value | Why | +| ------------------------------------------------------- | ------ | ------------------------------------ | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | +| **(a) FTS5 + Mermaid output** (§ 2.1 + § 2.2 in one PR) | M | High | Both are non-goals worth flipping, both are ~50–150 LoC each, both compound directly with every existing recipe. One bundled recipe (`text-in-deprecated-functions`) demonstrates the JOIN; one new `--format mermaid` flag on `impact` demonstrates the formatter. Same shape as C.11 plan: minor changeset (FTS5 = new virtual table = SCHEMA bump), bundled recipe. | +| **(b) C.9 Framework plugin layer** | XL | Very High but multiplier-on-existing | Sharpens every shipped recipe (untested-and-dead currently false-positives Next.js page.tsx). Big surface; needs full plan PR per the docs/research/fallow.md note. Defer to (a)+(b) sequence: ship (a) first as a confidence-building "we can flip a non-goal cleanly" move, then plan (b) as the multi-tracer big-surface PR. | +| **(c) Cyclomatic complexity column** (item 1.3) | S | Medium | Pure data addition; one new column on `symbols`, one bundled recipe (`high-complexity-untested`). Promotes "no static analysis" non-goal flip from rhetoric to concrete capability. | +| **(d) LSP shim** (§ 2.5) | L | Very High agent UX | Blocks on (b) for entry-point awareness. Cross-reference fallow `crates/lsp/` heavily during plan. | + +**Recommended order:** (a) → (c) → (b) → (d). + +**Rationale:** + +1. (a) is the cheapest non-goal flip; ships in one PR; proves the pattern. +2. (c) reuses (a)'s "new column on `symbols`" muscle; keeps the cadence. +3. (b) is the big-surface multi-tracer PR — by the time we get here, FTS5 + Mermaid + complexity have shown the "compositional capability" thesis on real recipes. +4. (d) lands last because LSP shim wants entry-point awareness from (b) to give accurate "find references". + +--- + +## 6. Open questions + +- **Daemon-by-default for MCP / HTTP** — even with one-shot CLI preserved, should `mcp` and `serve` default to `--watch` since both are inherently long-running? Reduces "is index stale?" friction agents already complain about. +- **FTS5 opt-in vs default-on** — index size tax is real on big repos. First pass: opt-in via `codemap.config.ts` `fts5: true`. Revisit after measurements on the fallow / external corpus. +- **LSP shim vs new-process LSP server** — shim wraps the existing engines via stdio (cheap, no new transport); standalone LSP server forks a daemon (matches LSP convention, more code). Probably shim first; standalone if VSCode extension demand emerges. +- **Plugin contract scope (C.9)** — entry-point hints only (option (i) per fallow.md § 6) vs arbitrary `dependencies` edges (option (ii)). Bias toward (i) per the existing fallow.md note; revisit during plan PR. +- **`history` table** — would unlock "when did coverage drop?" / "when did symbol X last have a caller?". Schema-shape question: per-commit snapshots (large) vs append-only event log (small but harder to query). Defer until a recipe demands it. + +--- + +## 7. Cross-references + +- [`roadmap.md § Non-goals (v1)`](../roadmap.md#non-goals-v1) — current non-goals list (this doc proposes amendments) +- [`roadmap.md § Backlog`](../roadmap.md#backlog) — backlog items this doc reorders +- [`research/fallow.md`](./fallow.md) — capability tracker for adopt-from-fallow items (different lens from this doc) +- [`research/competitive-scan-2026-04.md`](./competitive-scan-2026-04.md) — original three-tool scan (closed; this doc supersedes its non-goals shaping) +- [`docs/why-codemap.md § What Codemap is not`](../why-codemap.md#what-codemap-is-not) — consumer-facing framing of non-goals (must be updated in lockstep when § 2 items ship) +- Local fallow clone — `/Users/sutusebastian/Developer/OSS/fallow` (see § 4 for what to inspect when) From ac0c0d7ae932520b27eba22c6648d32b2ca7d0e2 Mon Sep 17 00:00:00 2001 From: Sutu Sebastian Date: Mon, 4 May 2026 12:37:08 +0300 Subject: [PATCH 02/20] docs(research): triangulate non-goals reassessment vs descriptive baseline MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit User cross-checked my prescriptive doc (non-goals-reassessment-2026-05.md) against composer-2-fast's descriptive baseline (codemap-capability- surface-2026-05.md) plus the codebase as source of truth. Found three factual errors in mine; baseline doc held up clean. Corrections applied: 1. § 1.2 (Exports never imported): codebase has `exports.re_export_source` column — original doc missed it. Re-exports require a JOIN through that column to avoid false positives on barrel-only exports. Effort bumped XS → S. 2. § 1.3 (Cyclomatic complexity): claimed "AST walker already counts nodes during parse" — false. `rg 'complexity|node_count|nodeCount' src/` returns zero matches. Node-counting is NOT in place; needs an extension to the AST walker in src/parser.ts. Effort bumped S → M. 3. § 2.3 ("no static analysis" non-goal): listed `fan-in` and `fan-out` as "static analysis we already ship" — too loose. Per `fan-in.sql` (`ORDER BY fan_in DESC LIMIT 15`) they're hotspot rankers, not orphan / dead-code detectors. They don't cover the closed-dead- subgraph case from research/fallow.md § 0 (8-file pack with non- zero fan-in via self-import). That gap motivates C.9 framework plugin layer, not the "no static analysis" flip. Caveat now spelled out in the doc. Header updated: this doc is the **prescriptive** lens; the **descriptive baseline** lives in codemap-capability-surface-2026-05.md (read first). Cross-references list and § 8 errata block document the diff between v1 and v2 so future reviewers can see what changed and why. Process lesson encoded in § 8: every prescriptive research note should triangulate against a descriptive baseline (own doc or peer model) before recommending a ship sequence. Caught all three errors before they propagated into a plan PR. --- .../codemap-capability-surface-2026-05.md | 133 ++++++++++++++++++ .../non-goals-reassessment-2026-05.md | 49 +++++-- 2 files changed, 168 insertions(+), 14 deletions(-) create mode 100644 docs/research/codemap-capability-surface-2026-05.md diff --git a/docs/research/codemap-capability-surface-2026-05.md b/docs/research/codemap-capability-surface-2026-05.md new file mode 100644 index 00000000..92ea682c --- /dev/null +++ b/docs/research/codemap-capability-surface-2026-05.md @@ -0,0 +1,133 @@ +# Codemap capability surface and extension paths + +> **Type:** Research snapshot (2026-05). +> **Scope:** What the **current codebase indexes, exposes, and wires** (CLI, schema, recipes, audit), plus **plausible next steps** grounded in that substrate. This note intentionally does **not** argue from `roadmap.md` non-goals—it describes **implemented facts** and **engineering ladders** from here. +> **Canonical homes:** Structural behavior and schema evolution live in [`docs/architecture.md`](../architecture.md) and [`src/db.ts`](../../src/db.ts). Positioning vs other tools stays in [`docs/why-codemap.md`](../why-codemap.md); competitive context in [`research/competitive-scan-2026-04.md`](./competitive-scan-2026-04.md) and [`research/fallow.md`](./fallow.md). + +--- + +## Methodology + +Findings grounded in: + +- `createTables` / `SCHEMA_VERSION` in `src/db.ts` +- Built-in parsers in `src/adapters/builtin.ts` +- CLI routing in `src/cli/main.ts` +- Audit delta registry `V1_DELTAS` in `src/application/audit-engine.ts` +- Impact walker in `src/application/impact-engine.ts` +- Bundled recipe inventory via `codemap query --recipes-json` (15 ids) +- `templates/recipes/*.sql` (notably `fan-in.sql` semantics) + +Re-index this repo before treating any **local** row counts as authoritative. + +--- + +## Implemented database surface + +**Index tables (structural):** `files`, `symbols`, `imports`, `exports`, `components`, `dependencies`, `markers`, `css_variables`, `css_classes`, `css_keyframes`, `calls`, `type_members`. + +**Metadata / user data (same DB file):** `meta` (key/value), `query_baselines` (saved query snapshots for `--save-baseline` / `--baseline`), `coverage` (Istanbul / LCOV ingested via `codemap ingest-coverage`, natural-key join to symbols—not a foreign key to `symbols.id` because full reindex recreates symbol rows). + +**Schema version:** `SCHEMA_VERSION` in `src/db.ts` (bump on DDL change; mismatch triggers rebuild path documented in architecture). + +**Notable symbol columns:** `doc_comment`, `visibility`, export flags, `parent_name`, optional `value`. + +**Notable export column:** `re_export_source` (re-export chain hint at export row level). + +**Dependencies:** `dependencies` stores resolved **module** edges `(from_path, to_path)`; `imports` stores per-import rows with `source`, `resolved_path`, specifiers. + +--- + +## Indexing / language coverage (built-ins) + +`BUILTIN_ADAPTERS` in `src/adapters/builtin.ts`: + +1. **TS/JS family** — `.ts`, `.tsx`, `.mts`, `.cts`, `.js`, `.jsx`, `.mjs`, `.cjs` → `extractFileData` (oxc AST path). +2. **CSS** — `.css` → Lightning CSS extraction + markers. +3. **Text / markers** — `.md`, `.mdx`, `.mdc`, `.yml`, `.yaml`, `.txt`, `.json`, `.sh` → markers only (no full TS-style symbol graph for those bodies). + +Community registration beyond this file is an **architectural extension** (adapter types exist under `src/adapters/types.ts`). + +--- + +## CLI capabilities (shipped commands) + +From `src/cli/main.ts` (non-exhaustive detail—see each `cmd-*.ts`): + +| Command | Role | +| --------------------- | -------------------------------------------------------------------------------------------------------------------------------------------------- | +| _(default)_ | Incremental / full index (`cmd-index.ts`) | +| `query` | Ad-hoc SQL + recipes; baselines; `--changed-since`, `--group-by`, `--summary`; `--format text\|json\|sarif\|annotations` (`cmd-query.ts`) | +| `audit` | Structural diff vs saved baselines and/or `--base ` (worktree + cache); envelope `deltas` per `V1_DELTAS` (`cmd-audit.ts`, `audit-engine.ts`) | +| `impact` | Bounded graph walks over `dependencies` / `calls` / `imports` (`impact-engine.ts`) | +| `ingest-coverage` | Populate `coverage` from coverage-final.json / lcov (`cmd-ingest-coverage.ts`, `coverage-engine.ts`) | +| `show`, `snippet` | Targeted symbol lookup + optional disk snippet (`cmd-show.ts`, `cmd-snippet.ts`) | +| `context`, `validate` | Agent-oriented envelopes / hash staleness checks | +| `mcp`, `serve` | JSON-RPC MCP + HTTP API over shared tool handlers | +| `watch` | Live reindex (chokidar) | +| `agents init` | Template install under `.agents/` | + +--- + +## Bundled recipes (15) + +Confirmed via `codemap query --recipes-json`: + +`barrel-files`, `components-by-hooks`, `deprecated-symbols`, `fan-in`, `fan-out`, `fan-out-sample`, `fan-out-sample-json`, `files-by-coverage`, `files-hashes`, `files-largest`, `index-summary`, `markers-by-kind`, `untested-and-dead`, `visibility-tags`, `worst-covered-exports`. + +**Important nuance:** `fan-in.sql` ranks **`to_path` by inbound edge count (`ORDER BY fan_in DESC LIMIT 15`)**—dependency **hotspots**, not “files with zero fan-in” / orphan detection. There is **no** bundled recipe id `zero-fan-in*` in `templates/recipes/`; orphan heuristics are custom SQL / future recipes. + +**Per-row actions:** Recipe metadata can attach `actions` (YAML frontmatter on paired `.md` files); merge logic lives in `src/application/query-recipes.ts`. Nine bundled recipes ship an `actions` block in frontmatter today (grep `^actions:` under `templates/recipes/` to refresh). + +--- + +## Audit v1 deltas (exactly three) + +`V1_DELTAS` in `src/application/audit-engine.ts`: + +| Key | Canonical projection shape | +| -------------- | -------------------------------------------------------------- | +| `files` | `SELECT path FROM files ORDER BY path` | +| `dependencies` | `SELECT from_path, to_path FROM dependencies ORDER BY …` | +| `deprecated` | Symbols with `@deprecated` in `doc_comment` (fixed column set) | + +No built-in audited delta keys for **cycles**, **duplicate code**, **unused exports validated from entrypoints**, or **architecture zones**—those would be **new audit specs + SQL contracts** if added. + +--- + +## What is logically absent today (still factual) + +These are **not** “forbidden”—they are **not implemented** in the tree as first-class engines: + +- **Reachability from app entrypoints** (framework-aware “live” file set). `dependencies` answer “import graph,” not “reachable from `main` / Next pages / `exports` field” without extra rules. +- **Closed dead subgraphs:** internal research (`research/fallow.md`) records a case where every file in an unused pack had **non-zero** `dependencies` fan-in because the pack imported itself; **hotspot / fan-in SQL does not detect** that class of dead code. +- **Duplication / clone detection** (suffix-array or otherwise). +- **Dedicated cycle or boundary product**—could be **expressed** as SQL over `dependencies`, but no shipped command or audit row-type owns it. +- **First-party GitHub Marketplace Action**—consumers wire `codemap query --format sarif|annotations` (or upload SARIF) in their own workflow. + +--- + +## Extension paths from the current substrate + +Ordered by how directly they reuse **existing tables + indexer + query/audit/SARIF pipeline**: + +1. **New recipes + optional new audit delta keys** — Any finding expressible as **deterministic SQL** over current columns (including `WITH RECURSIVE` for cycles) can ship as `--recipe` and optionally as a **named audit snapshot** with the same column contract pattern as `V1_DELTAS`. +2. **Materialized columns / tables at index time** — e.g. **`is_entry`**, **package id**, **reachable** bit, **custom boundary zone** per file—extends the indexer and schema; unlocks **entry-grounded** dead-file queries without abandoning SQLite. +3. **`audit` verdict envelope** — Today: `{ head, deltas }` with `added`/`removed`; roadmap sketch for threshold-driven `verdict` is a **downstream** change to `audit-engine` + config—data already exists. +4. **Workspace / monorepo partitioning** — Split or tag **dependency** / **file** rows by package root; same query surface, clearer ownership queries. +5. **New analysis passes** — Duplication, churn, complexity metrics: **new extractors** (AST or git) writing **new tables**; then **same** baselines, SARIF, MCP, HTTP. +6. **Packaging** — Composite **GitHub Action** (install tool, index, `query --format sarif`, upload) is **CI glue**, not core index capability. + +--- + +## Relation to “graph-level” tools (e.g. Fallow-class) + +**Already strong:** Ad-hoc **structural** questions—fan-in/out, exports, calls, components/hooks, CSS artifacts, markers, type members, **impact** walks, **ref-scoped** structural diffs, **coverage-joined** recipes—are **native** to this codebase. + +**Requires new materialized facts:** Entry-grounded **unused files**, **high-fidelity unused exports** through barrels, **dupes**, and **opinionated boundary enforcement** need **additional indexer or post-index passes** (and possibly config), not only prettier SQL on today’s `dependencies` edge list. + +--- + +## Closing + +This file is a **snapshot** of the capability boundary **as implemented**. When major schema or CLI surfaces change, either update this note in the same commit or slim it to a pointer into `architecture.md` per [docs/README.md § Rule 8](../README.md) research-closing discipline. diff --git a/docs/research/non-goals-reassessment-2026-05.md b/docs/research/non-goals-reassessment-2026-05.md index c92c0449..08f22bfe 100644 --- a/docs/research/non-goals-reassessment-2026-05.md +++ b/docs/research/non-goals-reassessment-2026-05.md @@ -2,9 +2,13 @@ > **Status:** open · **Trigger:** post-C.11 ship; user observation that several non-goals were inherited from when the project was 1/10th its current size and never re-examined as the surface grew. > -> **Companion docs:** [`research/fallow.md`](./fallow.md) (capability tracker — what to adopt _from fallow_); this file (capability inventory — what _our codebase_ can do that the [`roadmap.md § Non-goals`](../roadmap.md#non-goals-v1) currently forbids). +> **Lens:** **Prescriptive** — proposes specific non-goal flips, ship sequence, open questions. The **descriptive baseline** (what's implemented today, with file:line cites for each claim) lives in [`research/codemap-capability-surface-2026-05.md`](./codemap-capability-surface-2026-05.md) — read that one first. +> +> **Other companion docs:** [`research/fallow.md`](./fallow.md) (capability tracker — what to adopt _from fallow_); [`research/competitive-scan-2026-04.md`](./competitive-scan-2026-04.md) (closed; original three-tool scan). > > **Local clone for deep-dives:** [`/Users/sutusebastian/Developer/OSS/fallow`](file:///Users/sutusebastian/Developer/OSS/fallow) — Cargo workspace with `crates/{lsp,mcp,v8-coverage,graph,extract,cli}`, `decisions/` (ADR-style), `editors/{vscode,zed}`, `docs/plugin-authoring.md`. Inspect for patterns we can adapt before each shipped feature. +> +> **Triangulation errata (2026-05):** Cross-checked against the descriptive baseline above. Three claims in this doc were softened or corrected (item 1.3 effort + scope, § 2.3 framing of `fan-in.sql`, plus a citation gap on closed-dead-subgraph evidence). See § 8 for the full diff. --- @@ -31,18 +35,18 @@ The right framing now: **what does the SQL-index-with-three-transports + worker- The data and pipeline exist. Each row needs only a recipe / formatter / verb to expose. Estimated effort assumes one tracer-bullet PR each. -| # | Capability | What's already in place | What's needed | Effort | -| ---- | ------------------------------------------- | ------------------------------------------------------------------------------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------- | ------ | -| 1.1 | **Components calling deprecated symbols** | `components.hooks_used` + `calls` + `symbols.doc_comment LIKE '%@deprecated%'` | One bundled recipe (`components-touching-deprecated`) | XS | -| 1.2 | **Exports never imported anywhere** | `exports` LEFT JOIN `imports` (by `source` resolution) | One bundled recipe (`unimported-exports`) — sharper than `untested-and-dead` because it doesn't need coverage to be useful | XS | -| 1.3 | **Cyclomatic complexity per symbol** | `calls.caller_scope` already aggregates per-symbol; AST walker already counts nodes during parse | New `complexity REAL` column on `symbols` populated at parse time + recipe (`high-complexity-untested`) | S | -| 1.4 | **Refactor risk ranking** | `dependencies` (fan-in) + `coverage` (test coverage) | Recipe (`refactor-risk-ranking` — `fan_in × (100 - coverage_pct)`) | XS | -| 1.5 | **Boundary violations (config-driven)** | `dependencies` table + glob-matching primitives in `validate-engine` | New `--boundaries ` flag on `audit` or recipe consuming the config | S | -| 1.6 | **Type members consumed by external files** | `type_members` + `imports.specifiers` (JSON) | Recipe (`unused-type-members`) — needs JSON-extraction predicate on specifiers | S | -| 1.7 | **Mermaid / D2 / Graphviz output** | `dependencies` + `calls` already shape into edges; SARIF / annotations formatters demonstrate the formatter pattern | New `--format mermaid` formatter (sibling of SARIF in `output-formatters.ts`) | S | -| 1.8 | **More MCP resources** | Schema, recipes, skill already exposed via `resource-handlers.ts` | Add `codemap://files/{path}` (file shape — symbols, imports, exports, coverage) and `codemap://symbols/{name}` (LSP-like reads) | S | -| 1.9 | **Recipe usage telemetry** | `query_baselines` precedent (user-data table excluded from `dropAll()`) | New `recipe_usage` table + reconciler at MCP / HTTP request boundary; ranks recipes by recent agent use in `--recipes-json` | M | -| 1.10 | **Symbol-rename dry-run preview** | `calls` (callers) + `symbols.line_start` / `line_end` (locations) | New `codemap rename --dry-run --format diff` verb — borders on the "no fix engine" non-goal but stays read-only (just shows the diff) | M | +| # | Capability | What's already in place | What's needed | Effort | +| ---- | ------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------- | ------ | +| 1.1 | **Components calling deprecated symbols** | `components.hooks_used` + `calls` + `symbols.doc_comment LIKE '%@deprecated%'` | One bundled recipe (`components-touching-deprecated`) | XS | +| 1.2 | **Exports never imported anywhere** | `exports` LEFT JOIN `imports` (by `source` resolution); `exports.re_export_source` column exists for re-export chain handling | Recipe (`unimported-exports`) — re-export chains need a JOIN through `re_export_source` to avoid false positives on barrel-only exports | S | +| 1.3 | **Cyclomatic complexity per symbol** | `calls.caller_scope` already aggregates per-symbol. (Earlier draft claimed AST node-counting was already in place; corrected per § 8 — it isn't.) | Extend the AST walker in `src/parser.ts` to count branching nodes per symbol; add `complexity REAL` column on `symbols`; ship `high-complexity-untested` recipe | M | +| 1.4 | **Refactor risk ranking** | `dependencies` (fan-in) + `coverage` (test coverage) | Recipe (`refactor-risk-ranking` — `fan_in × (100 - coverage_pct)`) | XS | +| 1.5 | **Boundary violations (config-driven)** | `dependencies` table + glob-matching primitives in `validate-engine` | New `--boundaries ` flag on `audit` or recipe consuming the config | S | +| 1.6 | **Type members consumed by external files** | `type_members` + `imports.specifiers` (JSON) | Recipe (`unused-type-members`) — needs JSON-extraction predicate on specifiers | S | +| 1.7 | **Mermaid / D2 / Graphviz output** | `dependencies` + `calls` already shape into edges; SARIF / annotations formatters demonstrate the formatter pattern | New `--format mermaid` formatter (sibling of SARIF in `output-formatters.ts`) | S | +| 1.8 | **More MCP resources** | Schema, recipes, skill already exposed via `resource-handlers.ts` | Add `codemap://files/{path}` (file shape — symbols, imports, exports, coverage) and `codemap://symbols/{name}` (LSP-like reads) | S | +| 1.9 | **Recipe usage telemetry** | `query_baselines` precedent (user-data table excluded from `dropAll()`) | New `recipe_usage` table + reconciler at MCP / HTTP request boundary; ranks recipes by recent agent use in `--recipes-json` | M | +| 1.10 | **Symbol-rename dry-run preview** | `calls` (callers) + `symbols.line_start` / `line_end` (locations) | New `codemap rename --dry-run --format diff` verb — borders on the "no fix engine" non-goal but stays read-only (just shows the diff) | M | **Aggregate**: ~10 first-class agent-facing capabilities sitting in unwritten JOINs / unwritten formatters. Same multiplicative effect as C.11's three bundled recipes. @@ -88,7 +92,9 @@ ripgrep can't compose with `symbols` / `coverage` / `markers` in one shot — it **Original framing:** "Static analysis — dead code, duplication, complexity, architecture-boundary detection, fix actions are a different product class (e.g. fallow, knip, jscpd)". -**What's actually true:** we already ship `deprecated-symbols`, `untested-and-dead` (post-C.11), `barrel-files`, `fan-in`, `fan-out` — those **are** static analysis. The original line was rhetorical (we don't ship a rule engine with severity levels and `// codemap-disable-next-line` comments) but the bullet over-asserted. +**What's actually true:** we already ship `deprecated-symbols`, `untested-and-dead` (post-C.11), `barrel-files`, `worst-covered-exports`, `visibility-tags` — those **are** static analysis. The original line was rhetorical (we don't ship a rule engine with severity levels and `// codemap-disable-next-line` comments) but the bullet over-asserted. + +**Caveat (caught by triangulation):** `fan-in` and `fan-out` are **hotspot rankers**, not dead-code detectors — `fan-in.sql` literally `ORDER BY fan_in DESC LIMIT 15` ([source](../../templates/recipes/fan-in.sql)). They're structural-property recipes (legitimate static analysis), but they don't cover the closed-dead-subgraph case ([`research/fallow.md` § 0](./fallow.md#0-fresh-evidence--what-a-hands-on-graph-audit-surfaced) documents the 8-file widget pack where every file had non-zero `dependencies` fan-in via self-import; the fan-in recipe missed the entire pack). That gap is a multi-axis case the C.9 framework plugin layer addresses, not the "no static analysis" non-goal. The **real** boundary: **no opinionated rule engine + no fix mutation**. Recipes that compute structural properties (complexity, boundary checks, unused exports) are pure SQL on top of the index — exactly the niche we own. @@ -184,9 +190,24 @@ These aren't defensive prejudices; they're real shape constraints that the SQL-i ## 7. Cross-references +- [`research/codemap-capability-surface-2026-05.md`](./codemap-capability-surface-2026-05.md) — descriptive baseline (what's implemented, with file:line cites). Read first; this doc layers prescription on top. - [`roadmap.md § Non-goals (v1)`](../roadmap.md#non-goals-v1) — current non-goals list (this doc proposes amendments) - [`roadmap.md § Backlog`](../roadmap.md#backlog) — backlog items this doc reorders - [`research/fallow.md`](./fallow.md) — capability tracker for adopt-from-fallow items (different lens from this doc) - [`research/competitive-scan-2026-04.md`](./competitive-scan-2026-04.md) — original three-tool scan (closed; this doc supersedes its non-goals shaping) - [`docs/why-codemap.md § What Codemap is not`](../why-codemap.md#what-codemap-is-not) — consumer-facing framing of non-goals (must be updated in lockstep when § 2 items ship) - Local fallow clone — `/Users/sutusebastian/Developer/OSS/fallow` (see § 4 for what to inspect when) + +--- + +## 8. Triangulation errata (2026-05) + +Cross-checked against [`research/codemap-capability-surface-2026-05.md`](./codemap-capability-surface-2026-05.md) (descriptive baseline). Three corrections applied; documenting here so future reviewers can see the diff between v1 (initial draft) and v2 (post-triangulation). + +| Section | Original claim | Corrected claim | Evidence (codebase = source of truth) | +| --------- | ------------------------------------------------------------------------------------------------------------------------------ | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | -------------------------------------------------------------------- | +| **§ 1.2** | "Exports never imported anywhere — one recipe; XS effort" | Same recipe; **S** effort because `exports.re_export_source` requires a JOIN through re-export chains to avoid false positives on barrel-only exports | `db.ts:69` `re_export_source` column on `exports` table | +| **§ 1.3** | "AST node count from parser already in place" + "S effort" | Node-counting is **not** in place; needs an extension to the AST walker in `src/parser.ts`. **M** effort. | `rg 'complexity\|node_count\|nodeCount' src/` returns zero matches | +| **§ 2.3** | "We already ship `deprecated-symbols`, `untested-and-dead`, `barrel-files`, `fan-in`, `fan-out` — those _are_ static analysis" | Same list, but with the caveat that `fan-in` / `fan-out` are **hotspot rankers** (`ORDER BY DESC LIMIT 15`), not orphan / dead-code detectors. They don't cover the closed-dead-subgraph case from [`research/fallow.md` § 0](./fallow.md#0-fresh-evidence--what-a-hands-on-graph-audit-surfaced) — that gap motivates C.9 (framework plugin layer), not the "no static analysis" flip | `templates/recipes/fan-in.sql` shows `ORDER BY fan_in DESC LIMIT 15` | + +**Process lesson:** The descriptive baseline doc came in via a different model (`composer-2-fast`) and grounded every claim in file paths or `codemap query` invocations. My initial draft of this prescriptive doc reasoned from capability-from-substrate intuition without enough pinning. The triangulation step caught all three errors before they propagated into a plan PR. Pattern worth repeating: **every prescriptive research note should triangulate against a descriptive baseline (own doc or peer model) before recommending a ship sequence.** From a250f660570f05cd35fa18c99e2c2e48db3d405b Mon Sep 17 00:00:00 2001 From: Sutu Sebastian Date: Mon, 4 May 2026 12:38:51 +0300 Subject: [PATCH 03/20] docs(research): scrub local user paths from non-goals doc + new lesson MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit User caught absolute-path leaks in the research note pointing at the fallow clone on the maintainer's machine. Three references replaced with the public upstream URL (https://github.com/fallow-rs/fallow): - Header "Local clone for deep-dives" → "Source for deep-dives" - § 4 heading "What to inspect in the local fallow clone" → "...in the fallow source tree" - § 7 cross-references "Local fallow clone — /Users/..." → "fallow upstream" Also adds a new general-purpose lesson to .agents/lessons.md: Never commit absolute local user paths — no /Users//…, /home//…, ~/…, or file:/// URIs in any tracked doc, code, comment, or PR body. Pattern: cite https://github.com// for upstream sources; repo-relative paths for in-tree references. Sibling to the existing "PR bodies via temp file" lesson — same family (committed strings need to be portable + non-leaking), different surface. --- docs/research/non-goals-reassessment-2026-05.md | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/docs/research/non-goals-reassessment-2026-05.md b/docs/research/non-goals-reassessment-2026-05.md index 08f22bfe..ff6d3601 100644 --- a/docs/research/non-goals-reassessment-2026-05.md +++ b/docs/research/non-goals-reassessment-2026-05.md @@ -6,7 +6,7 @@ > > **Other companion docs:** [`research/fallow.md`](./fallow.md) (capability tracker — what to adopt _from fallow_); [`research/competitive-scan-2026-04.md`](./competitive-scan-2026-04.md) (closed; original three-tool scan). > -> **Local clone for deep-dives:** [`/Users/sutusebastian/Developer/OSS/fallow`](file:///Users/sutusebastian/Developer/OSS/fallow) — Cargo workspace with `crates/{lsp,mcp,v8-coverage,graph,extract,cli}`, `decisions/` (ADR-style), `editors/{vscode,zed}`, `docs/plugin-authoring.md`. Inspect for patterns we can adapt before each shipped feature. +> **Source for deep-dives:** [fallow upstream](https://github.com/fallow-rs/fallow) — Cargo workspace with `crates/{lsp,mcp,v8-coverage,graph,extract,cli}`, `decisions/` (ADR-style), `editors/{vscode,zed}`, `docs/plugin-authoring.md`. Inspect for patterns we can adapt before each shipped feature. > > **Triangulation errata (2026-05):** Cross-checked against the descriptive baseline above. Three claims in this doc were softened or corrected (item 1.3 effort + scope, § 2.3 framing of `fan-in.sql`, plus a citation gap on closed-dead-subgraph evidence). See § 8 for the full diff. @@ -134,9 +134,9 @@ These aren't defensive prejudices; they're real shape constraints that the SQL-i --- -## 4. What to inspect in the local fallow clone +## 4. What to inspect in the fallow source tree -`/Users/sutusebastian/Developer/OSS/fallow` (Cargo workspace; ~149 releases as of 2026-04). Areas worth a deep-dive _before_ each shipped feature so we adopt patterns rather than reinvent: +Fallow is a Cargo workspace ([upstream](https://github.com/fallow-rs/fallow); ~149 releases as of 2026-04). Areas worth a deep-dive _before_ each shipped feature so we adopt patterns rather than reinvent: | Fallow surface | Codemap relevance | When to inspect | | ---------------------------------- | ------------------------------------------------------------------------------------------------------------------------ | ---------------------------------------------------------- | @@ -196,7 +196,7 @@ These aren't defensive prejudices; they're real shape constraints that the SQL-i - [`research/fallow.md`](./fallow.md) — capability tracker for adopt-from-fallow items (different lens from this doc) - [`research/competitive-scan-2026-04.md`](./competitive-scan-2026-04.md) — original three-tool scan (closed; this doc supersedes its non-goals shaping) - [`docs/why-codemap.md § What Codemap is not`](../why-codemap.md#what-codemap-is-not) — consumer-facing framing of non-goals (must be updated in lockstep when § 2 items ship) -- Local fallow clone — `/Users/sutusebastian/Developer/OSS/fallow` (see § 4 for what to inspect when) +- [fallow upstream](https://github.com/fallow-rs/fallow) — see § 4 for what to inspect when --- From 9dfb65f1bb0ab227027c134a070c7dcd7741b4b3 Mon Sep 17 00:00:00 2001 From: Sutu Sebastian Date: Mon, 4 May 2026 12:39:25 +0300 Subject: [PATCH 04/20] docs(lessons): add 'never commit local user paths' lesson (PR #58 catch) --- .agents/lessons.md | 1 + 1 file changed, 1 insertion(+) diff --git a/.agents/lessons.md b/.agents/lessons.md index d7493691..afd62aff 100644 --- a/.agents/lessons.md +++ b/.agents/lessons.md @@ -13,3 +13,4 @@ Each entry is a single bullet: `- **** — `. Newest entries at t - **backticks inside SQL or help-text template literals** — never put a literal backtick inside a `` `...` `` template-literal string. `db.ts` SQL DDL strings (multi-line CREATE TABLE templates) and `printQueryCmdHelp()` (multi-line help text) are both `` `...` `` template literals; an inner backtick — typically a Markdown-style code-fence around a flag like `` `--full` `` — terminates the literal early and the parser blows up several lines later with cryptic "expected `,` or `)`" errors. **Use plain prose in those strings** (`--full` not `` `--full` ``), or escape (`` \` ``) if you really need the character. Hit twice (B.7 + B.6 PR #30); the lesson is general — applies to any TS template literal that gets pasted prose later, not just SQL / help text. - **STOP-before-Grep applies to symbol lookups too** — `Grep` for symbol names like `printQueryResult`, `getCurrentCommit`, `dropAll` violates the [`codemap` rule](rules/codemap.md). The codemap query `SELECT file_path, line_start FROM symbols WHERE name = ''` answers it faster and without scanning. Reach for `Grep` only when the question is content-shaped (regex over file bodies, finding pattern usages inside function bodies, etc.) — not when it's "where is X defined / who calls X / what does file Y export." This was a PR #30 self-correction. - **PR / issue / comment bodies always go through a temp file** — never pass markdown bodies via shell heredoc to `gh pr create --body "$(cat <<'EOF'…)"` / `gh pr edit --body …` / `gh pr comment --body …` / `gh issue create --body …` / `gh api` `--field body=…`. Backticks inside the heredoc (every code span and code fence) get shell-escaped to `\`` and render literally on GitHub — every recipe id, file path, flag, SQL fragment, and code fence in the rendered body comes out as `\`coverage\``instead of`coverage`. Pattern: write the body to a temp file (`Write`to`/tmp/pr--body.md`), pass `--body-file /tmp/pr--body.md`, then delete the temp file. Cost is one extra tool call; saves redoing every PR body that has more than a few backticks. Hit on PR #57 — final body was a wall of `\`` artifacts until rewritten via temp file. +- **Never commit absolute local user paths** — no `/Users//…`, `/home//…`, `~/…`, or `file:///` URIs in any tracked doc, code, comment, or PR body. Reasons: (1) leaks the maintainer's directory structure / username to public mirrors; (2) every other contributor's paths differ — the reference is dead on their machine; (3) a `git clone` of someone else's machine isn't a fact we can cite as a "source for deep-dives" — public upstream URLs are. Pattern: cite `https://github.com//` (with optional `/tree//`) for upstream sources; use repo-relative paths (`docs/foo.md`, `src/bar.ts`) for in-tree references. Hit on PR #58 first draft — referenced the local fallow clone path in the research note before the user caught it. From a5cab90aa558b9c934f31faf46664502c1c41016 Mon Sep 17 00:00:00 2001 From: Sutu Sebastian Date: Mon, 4 May 2026 12:51:15 +0300 Subject: [PATCH 05/20] docs(research): delete codemap-capability-surface-2026-05.md (existence test) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Per docs/README.md existence test, this doc fails 3 of 4 criteria: - ❌ Doesn't document durable policy unavailable elsewhere — every fact reproducible from db.ts / builtin.ts / audit-engine.ts / --recipes-json - ❌ Doesn't track open work — pure snapshot - ❌ No unique historical context git log + architecture.md can't reconstruct - ✅ Cited by another doc (only because non-goals-reassessment cited it) Plus Rule 1 violation (duplicates architecture.md § Schema) and Rule 6 violation (hardcodes "15 recipes" / "9 of 15 ship actions" inventory counts in narrative). The real value the doc delivered was the **triangulation discipline** — catching 3 errors in non-goals-reassessment v1. That discipline is the durable artifact, not the doc. Codified in two places: 1. non-goals-reassessment § 8 errata + process lesson (kept) 2. .agents/lessons.md — new lesson explicitly bans the "dual descriptive + prescriptive doc" pattern as a Rule 1 violation. Right discipline: pin every concrete claim in the prescriptive doc itself, or self-audit against the canonical home before committing. Don't ship a parallel descriptive doc. non-goals-reassessment header + § 7 + § 8 updated to drop the now-deleted companion-doc references and point at canonical sources directly (architecture.md § Schema, db.ts, builtin.ts, audit-engine.ts V1_DELTAS). --- .agents/lessons.md | 1 + .../codemap-capability-surface-2026-05.md | 133 ------------------ .../non-goals-reassessment-2026-05.md | 12 +- 3 files changed, 7 insertions(+), 139 deletions(-) delete mode 100644 docs/research/codemap-capability-surface-2026-05.md diff --git a/.agents/lessons.md b/.agents/lessons.md index afd62aff..fae1b795 100644 --- a/.agents/lessons.md +++ b/.agents/lessons.md @@ -14,3 +14,4 @@ Each entry is a single bullet: `- **** — `. Newest entries at t - **STOP-before-Grep applies to symbol lookups too** — `Grep` for symbol names like `printQueryResult`, `getCurrentCommit`, `dropAll` violates the [`codemap` rule](rules/codemap.md). The codemap query `SELECT file_path, line_start FROM symbols WHERE name = ''` answers it faster and without scanning. Reach for `Grep` only when the question is content-shaped (regex over file bodies, finding pattern usages inside function bodies, etc.) — not when it's "where is X defined / who calls X / what does file Y export." This was a PR #30 self-correction. - **PR / issue / comment bodies always go through a temp file** — never pass markdown bodies via shell heredoc to `gh pr create --body "$(cat <<'EOF'…)"` / `gh pr edit --body …` / `gh pr comment --body …` / `gh issue create --body …` / `gh api` `--field body=…`. Backticks inside the heredoc (every code span and code fence) get shell-escaped to `\`` and render literally on GitHub — every recipe id, file path, flag, SQL fragment, and code fence in the rendered body comes out as `\`coverage\``instead of`coverage`. Pattern: write the body to a temp file (`Write`to`/tmp/pr--body.md`), pass `--body-file /tmp/pr--body.md`, then delete the temp file. Cost is one extra tool call; saves redoing every PR body that has more than a few backticks. Hit on PR #57 — final body was a wall of `\`` artifacts until rewritten via temp file. - **Never commit absolute local user paths** — no `/Users//…`, `/home//…`, `~/…`, or `file:///` URIs in any tracked doc, code, comment, or PR body. Reasons: (1) leaks the maintainer's directory structure / username to public mirrors; (2) every other contributor's paths differ — the reference is dead on their machine; (3) a `git clone` of someone else's machine isn't a fact we can cite as a "source for deep-dives" — public upstream URLs are. Pattern: cite `https://github.com//` (with optional `/tree//`) for upstream sources; use repo-relative paths (`docs/foo.md`, `src/bar.ts`) for in-tree references. Hit on PR #58 first draft — referenced the local fallow clone path in the research note before the user caught it. +- **Prescriptive research notes pin every concrete claim before recommending a ship sequence** — when a research/plan-shape doc proposes work (effort estimates, capability inventories, "we already do X" framing), every concrete claim needs a `file:line` / `codemap query` / `rg` / `--recipes-json` reference a reviewer can re-run. Reasoning-from-substrate intuition without pinning ships errors: "the AST walker already counts nodes" / "fan-in detects orphans" / "the `re_export_source` column doesn't exist" — all real errors caught on PR #58 by triangulating against the codebase. Don't ship a peer / parallel "descriptive baseline" doc to triangulate against (Rule 1 violation — it duplicates `architecture.md` / `db.ts` / `--recipes-json`); instead, either (a) pin claims in the prescriptive doc itself, or (b) self-audit by re-running every claim against the canonical home before committing. Either path beats the "dual descriptive + prescriptive doc" pattern on docs-governance grounds. diff --git a/docs/research/codemap-capability-surface-2026-05.md b/docs/research/codemap-capability-surface-2026-05.md deleted file mode 100644 index 92ea682c..00000000 --- a/docs/research/codemap-capability-surface-2026-05.md +++ /dev/null @@ -1,133 +0,0 @@ -# Codemap capability surface and extension paths - -> **Type:** Research snapshot (2026-05). -> **Scope:** What the **current codebase indexes, exposes, and wires** (CLI, schema, recipes, audit), plus **plausible next steps** grounded in that substrate. This note intentionally does **not** argue from `roadmap.md` non-goals—it describes **implemented facts** and **engineering ladders** from here. -> **Canonical homes:** Structural behavior and schema evolution live in [`docs/architecture.md`](../architecture.md) and [`src/db.ts`](../../src/db.ts). Positioning vs other tools stays in [`docs/why-codemap.md`](../why-codemap.md); competitive context in [`research/competitive-scan-2026-04.md`](./competitive-scan-2026-04.md) and [`research/fallow.md`](./fallow.md). - ---- - -## Methodology - -Findings grounded in: - -- `createTables` / `SCHEMA_VERSION` in `src/db.ts` -- Built-in parsers in `src/adapters/builtin.ts` -- CLI routing in `src/cli/main.ts` -- Audit delta registry `V1_DELTAS` in `src/application/audit-engine.ts` -- Impact walker in `src/application/impact-engine.ts` -- Bundled recipe inventory via `codemap query --recipes-json` (15 ids) -- `templates/recipes/*.sql` (notably `fan-in.sql` semantics) - -Re-index this repo before treating any **local** row counts as authoritative. - ---- - -## Implemented database surface - -**Index tables (structural):** `files`, `symbols`, `imports`, `exports`, `components`, `dependencies`, `markers`, `css_variables`, `css_classes`, `css_keyframes`, `calls`, `type_members`. - -**Metadata / user data (same DB file):** `meta` (key/value), `query_baselines` (saved query snapshots for `--save-baseline` / `--baseline`), `coverage` (Istanbul / LCOV ingested via `codemap ingest-coverage`, natural-key join to symbols—not a foreign key to `symbols.id` because full reindex recreates symbol rows). - -**Schema version:** `SCHEMA_VERSION` in `src/db.ts` (bump on DDL change; mismatch triggers rebuild path documented in architecture). - -**Notable symbol columns:** `doc_comment`, `visibility`, export flags, `parent_name`, optional `value`. - -**Notable export column:** `re_export_source` (re-export chain hint at export row level). - -**Dependencies:** `dependencies` stores resolved **module** edges `(from_path, to_path)`; `imports` stores per-import rows with `source`, `resolved_path`, specifiers. - ---- - -## Indexing / language coverage (built-ins) - -`BUILTIN_ADAPTERS` in `src/adapters/builtin.ts`: - -1. **TS/JS family** — `.ts`, `.tsx`, `.mts`, `.cts`, `.js`, `.jsx`, `.mjs`, `.cjs` → `extractFileData` (oxc AST path). -2. **CSS** — `.css` → Lightning CSS extraction + markers. -3. **Text / markers** — `.md`, `.mdx`, `.mdc`, `.yml`, `.yaml`, `.txt`, `.json`, `.sh` → markers only (no full TS-style symbol graph for those bodies). - -Community registration beyond this file is an **architectural extension** (adapter types exist under `src/adapters/types.ts`). - ---- - -## CLI capabilities (shipped commands) - -From `src/cli/main.ts` (non-exhaustive detail—see each `cmd-*.ts`): - -| Command | Role | -| --------------------- | -------------------------------------------------------------------------------------------------------------------------------------------------- | -| _(default)_ | Incremental / full index (`cmd-index.ts`) | -| `query` | Ad-hoc SQL + recipes; baselines; `--changed-since`, `--group-by`, `--summary`; `--format text\|json\|sarif\|annotations` (`cmd-query.ts`) | -| `audit` | Structural diff vs saved baselines and/or `--base ` (worktree + cache); envelope `deltas` per `V1_DELTAS` (`cmd-audit.ts`, `audit-engine.ts`) | -| `impact` | Bounded graph walks over `dependencies` / `calls` / `imports` (`impact-engine.ts`) | -| `ingest-coverage` | Populate `coverage` from coverage-final.json / lcov (`cmd-ingest-coverage.ts`, `coverage-engine.ts`) | -| `show`, `snippet` | Targeted symbol lookup + optional disk snippet (`cmd-show.ts`, `cmd-snippet.ts`) | -| `context`, `validate` | Agent-oriented envelopes / hash staleness checks | -| `mcp`, `serve` | JSON-RPC MCP + HTTP API over shared tool handlers | -| `watch` | Live reindex (chokidar) | -| `agents init` | Template install under `.agents/` | - ---- - -## Bundled recipes (15) - -Confirmed via `codemap query --recipes-json`: - -`barrel-files`, `components-by-hooks`, `deprecated-symbols`, `fan-in`, `fan-out`, `fan-out-sample`, `fan-out-sample-json`, `files-by-coverage`, `files-hashes`, `files-largest`, `index-summary`, `markers-by-kind`, `untested-and-dead`, `visibility-tags`, `worst-covered-exports`. - -**Important nuance:** `fan-in.sql` ranks **`to_path` by inbound edge count (`ORDER BY fan_in DESC LIMIT 15`)**—dependency **hotspots**, not “files with zero fan-in” / orphan detection. There is **no** bundled recipe id `zero-fan-in*` in `templates/recipes/`; orphan heuristics are custom SQL / future recipes. - -**Per-row actions:** Recipe metadata can attach `actions` (YAML frontmatter on paired `.md` files); merge logic lives in `src/application/query-recipes.ts`. Nine bundled recipes ship an `actions` block in frontmatter today (grep `^actions:` under `templates/recipes/` to refresh). - ---- - -## Audit v1 deltas (exactly three) - -`V1_DELTAS` in `src/application/audit-engine.ts`: - -| Key | Canonical projection shape | -| -------------- | -------------------------------------------------------------- | -| `files` | `SELECT path FROM files ORDER BY path` | -| `dependencies` | `SELECT from_path, to_path FROM dependencies ORDER BY …` | -| `deprecated` | Symbols with `@deprecated` in `doc_comment` (fixed column set) | - -No built-in audited delta keys for **cycles**, **duplicate code**, **unused exports validated from entrypoints**, or **architecture zones**—those would be **new audit specs + SQL contracts** if added. - ---- - -## What is logically absent today (still factual) - -These are **not** “forbidden”—they are **not implemented** in the tree as first-class engines: - -- **Reachability from app entrypoints** (framework-aware “live” file set). `dependencies` answer “import graph,” not “reachable from `main` / Next pages / `exports` field” without extra rules. -- **Closed dead subgraphs:** internal research (`research/fallow.md`) records a case where every file in an unused pack had **non-zero** `dependencies` fan-in because the pack imported itself; **hotspot / fan-in SQL does not detect** that class of dead code. -- **Duplication / clone detection** (suffix-array or otherwise). -- **Dedicated cycle or boundary product**—could be **expressed** as SQL over `dependencies`, but no shipped command or audit row-type owns it. -- **First-party GitHub Marketplace Action**—consumers wire `codemap query --format sarif|annotations` (or upload SARIF) in their own workflow. - ---- - -## Extension paths from the current substrate - -Ordered by how directly they reuse **existing tables + indexer + query/audit/SARIF pipeline**: - -1. **New recipes + optional new audit delta keys** — Any finding expressible as **deterministic SQL** over current columns (including `WITH RECURSIVE` for cycles) can ship as `--recipe` and optionally as a **named audit snapshot** with the same column contract pattern as `V1_DELTAS`. -2. **Materialized columns / tables at index time** — e.g. **`is_entry`**, **package id**, **reachable** bit, **custom boundary zone** per file—extends the indexer and schema; unlocks **entry-grounded** dead-file queries without abandoning SQLite. -3. **`audit` verdict envelope** — Today: `{ head, deltas }` with `added`/`removed`; roadmap sketch for threshold-driven `verdict` is a **downstream** change to `audit-engine` + config—data already exists. -4. **Workspace / monorepo partitioning** — Split or tag **dependency** / **file** rows by package root; same query surface, clearer ownership queries. -5. **New analysis passes** — Duplication, churn, complexity metrics: **new extractors** (AST or git) writing **new tables**; then **same** baselines, SARIF, MCP, HTTP. -6. **Packaging** — Composite **GitHub Action** (install tool, index, `query --format sarif`, upload) is **CI glue**, not core index capability. - ---- - -## Relation to “graph-level” tools (e.g. Fallow-class) - -**Already strong:** Ad-hoc **structural** questions—fan-in/out, exports, calls, components/hooks, CSS artifacts, markers, type members, **impact** walks, **ref-scoped** structural diffs, **coverage-joined** recipes—are **native** to this codebase. - -**Requires new materialized facts:** Entry-grounded **unused files**, **high-fidelity unused exports** through barrels, **dupes**, and **opinionated boundary enforcement** need **additional indexer or post-index passes** (and possibly config), not only prettier SQL on today’s `dependencies` edge list. - ---- - -## Closing - -This file is a **snapshot** of the capability boundary **as implemented**. When major schema or CLI surfaces change, either update this note in the same commit or slim it to a pointer into `architecture.md` per [docs/README.md § Rule 8](../README.md) research-closing discipline. diff --git a/docs/research/non-goals-reassessment-2026-05.md b/docs/research/non-goals-reassessment-2026-05.md index ff6d3601..32eef396 100644 --- a/docs/research/non-goals-reassessment-2026-05.md +++ b/docs/research/non-goals-reassessment-2026-05.md @@ -2,13 +2,13 @@ > **Status:** open · **Trigger:** post-C.11 ship; user observation that several non-goals were inherited from when the project was 1/10th its current size and never re-examined as the surface grew. > -> **Lens:** **Prescriptive** — proposes specific non-goal flips, ship sequence, open questions. The **descriptive baseline** (what's implemented today, with file:line cites for each claim) lives in [`research/codemap-capability-surface-2026-05.md`](./codemap-capability-surface-2026-05.md) — read that one first. +> **Lens:** **Prescriptive** — proposes specific non-goal flips, ship sequence, open questions. Every concrete capability claim is grounded in a file path + a `codemap query` / `rg` invocation a reviewer can re-run; canonical references live in [`architecture.md § Schema`](../architecture.md#schema), [`src/db.ts`](../../src/db.ts), [`src/adapters/builtin.ts`](../../src/adapters/builtin.ts), and [`src/application/audit-engine.ts`](../../src/application/audit-engine.ts) (`V1_DELTAS`). > -> **Other companion docs:** [`research/fallow.md`](./fallow.md) (capability tracker — what to adopt _from fallow_); [`research/competitive-scan-2026-04.md`](./competitive-scan-2026-04.md) (closed; original three-tool scan). +> **Companion docs:** [`research/fallow.md`](./fallow.md) (capability tracker — what to adopt _from fallow_); [`research/competitive-scan-2026-04.md`](./competitive-scan-2026-04.md) (closed; original three-tool scan). > > **Source for deep-dives:** [fallow upstream](https://github.com/fallow-rs/fallow) — Cargo workspace with `crates/{lsp,mcp,v8-coverage,graph,extract,cli}`, `decisions/` (ADR-style), `editors/{vscode,zed}`, `docs/plugin-authoring.md`. Inspect for patterns we can adapt before each shipped feature. > -> **Triangulation errata (2026-05):** Cross-checked against the descriptive baseline above. Three claims in this doc were softened or corrected (item 1.3 effort + scope, § 2.3 framing of `fan-in.sql`, plus a citation gap on closed-dead-subgraph evidence). See § 8 for the full diff. +> **Errata note (2026-05):** Three claims in v1 of this doc were softened or corrected after cross-checking against the codebase (item 1.3 effort + scope, § 2.3 framing of `fan-in.sql`, plus a citation gap on closed-dead-subgraph evidence). See § 8 for the full diff and the process lesson it surfaced. --- @@ -190,7 +190,7 @@ Fallow is a Cargo workspace ([upstream](https://github.com/fallow-rs/fallow); ~1 ## 7. Cross-references -- [`research/codemap-capability-surface-2026-05.md`](./codemap-capability-surface-2026-05.md) — descriptive baseline (what's implemented, with file:line cites). Read first; this doc layers prescription on top. +- [`architecture.md § Schema`](../architecture.md#schema) — canonical schema reference; the prescriptive items in § 1 are layered on top of these tables. - [`roadmap.md § Non-goals (v1)`](../roadmap.md#non-goals-v1) — current non-goals list (this doc proposes amendments) - [`roadmap.md § Backlog`](../roadmap.md#backlog) — backlog items this doc reorders - [`research/fallow.md`](./fallow.md) — capability tracker for adopt-from-fallow items (different lens from this doc) @@ -202,7 +202,7 @@ Fallow is a Cargo workspace ([upstream](https://github.com/fallow-rs/fallow); ~1 ## 8. Triangulation errata (2026-05) -Cross-checked against [`research/codemap-capability-surface-2026-05.md`](./codemap-capability-surface-2026-05.md) (descriptive baseline). Three corrections applied; documenting here so future reviewers can see the diff between v1 (initial draft) and v2 (post-triangulation). +v1 of this doc was reasoned-from-substrate without enough pinning to actual file:line / `codemap query` references. A peer-model review (`composer-2-fast`) cross-checked every concrete claim against `db.ts`, `builtin.ts`, `audit-engine.ts`, `--recipes-json`, and `templates/recipes/*.sql` — caught three errors. Corrections applied below; documenting them here so future reviewers can see the diff between v1 and v2. | Section | Original claim | Corrected claim | Evidence (codebase = source of truth) | | --------- | ------------------------------------------------------------------------------------------------------------------------------ | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | -------------------------------------------------------------------- | @@ -210,4 +210,4 @@ Cross-checked against [`research/codemap-capability-surface-2026-05.md`](./codem | **§ 1.3** | "AST node count from parser already in place" + "S effort" | Node-counting is **not** in place; needs an extension to the AST walker in `src/parser.ts`. **M** effort. | `rg 'complexity\|node_count\|nodeCount' src/` returns zero matches | | **§ 2.3** | "We already ship `deprecated-symbols`, `untested-and-dead`, `barrel-files`, `fan-in`, `fan-out` — those _are_ static analysis" | Same list, but with the caveat that `fan-in` / `fan-out` are **hotspot rankers** (`ORDER BY DESC LIMIT 15`), not orphan / dead-code detectors. They don't cover the closed-dead-subgraph case from [`research/fallow.md` § 0](./fallow.md#0-fresh-evidence--what-a-hands-on-graph-audit-surfaced) — that gap motivates C.9 (framework plugin layer), not the "no static analysis" flip | `templates/recipes/fan-in.sql` shows `ORDER BY fan_in DESC LIMIT 15` | -**Process lesson:** The descriptive baseline doc came in via a different model (`composer-2-fast`) and grounded every claim in file paths or `codemap query` invocations. My initial draft of this prescriptive doc reasoned from capability-from-substrate intuition without enough pinning. The triangulation step caught all three errors before they propagated into a plan PR. Pattern worth repeating: **every prescriptive research note should triangulate against a descriptive baseline (own doc or peer model) before recommending a ship sequence.** +**Process lesson** (also in [`.agents/lessons.md`](../../.agents/lessons.md)): every prescriptive research note should pin every concrete claim to a file path / `codemap query` / `rg` invocation a reviewer can re-run, and ideally cross-check against a peer model or self-audit before recommending a ship sequence. The triangulation step on this doc caught all three errors before they propagated into a plan PR. From 2803d9d62a6719d3194be148fb319240ec19f53a Mon Sep 17 00:00:00 2001 From: Sutu Sebastian Date: Mon, 4 May 2026 13:04:10 +0300 Subject: [PATCH 06/20] =?UTF-8?q?docs(research):=20align=20=C2=A7=205=20(c?= =?UTF-8?q?)=20effort=20with=20=C2=A7=201.3=20/=20=C2=A7=208=20(M,=20not?= =?UTF-8?q?=20S)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit CodeRabbit caught § 5 row (c) "Cyclomatic complexity column" listing effort S, while § 1.3 + § 8 errata both list M (the v1→v2 bump after `rg 'complexity|node_count|nodeCount' src/` returned zero — node- counting isn't already in place; the AST walker in src/parser.ts has to be extended). Effort propagation gap from the v2 errata pass. § 5 row (c) updated to M; "Why" cell now spells out the AST-walker dependency inline so future readers don't re-litigate the figure. --- docs/research/non-goals-reassessment-2026-05.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/research/non-goals-reassessment-2026-05.md b/docs/research/non-goals-reassessment-2026-05.md index 32eef396..4f01dbf0 100644 --- a/docs/research/non-goals-reassessment-2026-05.md +++ b/docs/research/non-goals-reassessment-2026-05.md @@ -164,7 +164,7 @@ Fallow is a Cargo workspace ([upstream](https://github.com/fallow-rs/fallow); ~1 | ------------------------------------------------------- | ------ | ------------------------------------ | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | **(a) FTS5 + Mermaid output** (§ 2.1 + § 2.2 in one PR) | M | High | Both are non-goals worth flipping, both are ~50–150 LoC each, both compound directly with every existing recipe. One bundled recipe (`text-in-deprecated-functions`) demonstrates the JOIN; one new `--format mermaid` flag on `impact` demonstrates the formatter. Same shape as C.11 plan: minor changeset (FTS5 = new virtual table = SCHEMA bump), bundled recipe. | | **(b) C.9 Framework plugin layer** | XL | Very High but multiplier-on-existing | Sharpens every shipped recipe (untested-and-dead currently false-positives Next.js page.tsx). Big surface; needs full plan PR per the docs/research/fallow.md note. Defer to (a)+(b) sequence: ship (a) first as a confidence-building "we can flip a non-goal cleanly" move, then plan (b) as the multi-tracer big-surface PR. | -| **(c) Cyclomatic complexity column** (item 1.3) | S | Medium | Pure data addition; one new column on `symbols`, one bundled recipe (`high-complexity-untested`). Promotes "no static analysis" non-goal flip from rhetoric to concrete capability. | +| **(c) Cyclomatic complexity column** (item 1.3) | M | Medium | One new column on `symbols`, one bundled recipe (`high-complexity-untested`). Effort is **M** (matches § 1.3 / § 8 errata) because branching-node counting requires extending the AST walker in `src/parser.ts` — node-counting isn't already in place. Promotes "no static analysis" non-goal flip from rhetoric to concrete capability. | | **(d) LSP shim** (§ 2.5) | L | Very High agent UX | Blocks on (b) for entry-point awareness. Cross-reference fallow `crates/lsp/` heavily during plan. | **Recommended order:** (a) → (c) → (b) → (d). From c3ed3e9d7e51b6865e236554c5c12841e3249924 Mon Sep 17 00:00:00 2001 From: Sutu Sebastian Date: Mon, 4 May 2026 13:23:34 +0300 Subject: [PATCH 07/20] =?UTF-8?q?docs(research):=20split=20=C2=A7=203=20in?= =?UTF-8?q?to=20moat=20(load-bearing)=20vs=20ergonomic=20limits?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Grill-me Q1 outcome (under "extract max from SQL-index + equal/surpass fallow" mission): the original § 3 list conflated ergonomic floors (sub-100ms cold-start, no LLM, no JS at index time) with the actual moats. Most of the original entries are floors fallow also follows; they're not differentiators. The two real moats that needed naming as load-bearing limits: A. SQL is the API — every capability is a recipe (saved query) or a primitive recipes can compose. Verdicts are an OUTPUT mode (--format sarif, audit deltas), never a primitive. Reviewer test: "is this verb also expressible as query --recipe ?" B. Extracted structure ≥ verdicts — schema breadth (CSS, markers, type_members, calls.caller_scope, components.hooks_used) is what equals/surpasses fallow on agent-facing capability per fallow.md § 5. Reviewer test for any "drop column X" PR: "what recipe (bundled or hypothetical) does this kill?" Both are now load-bearing rows above the ergonomic ones. The original five preferences are kept verbatim but annotated with their relation to the moat (floor / convergent / adjacent / rivalrous / safety). Eroding either A or B is the most likely path from "codemap" to "fallow with extra steps" — § 3 now equips a reviewer to spot it. --- .../non-goals-reassessment-2026-05.md | 27 ++++++++++++------- 1 file changed, 18 insertions(+), 9 deletions(-) diff --git a/docs/research/non-goals-reassessment-2026-05.md b/docs/research/non-goals-reassessment-2026-05.md index 4f01dbf0..6592c0fc 100644 --- a/docs/research/non-goals-reassessment-2026-05.md +++ b/docs/research/non-goals-reassessment-2026-05.md @@ -122,15 +122,24 @@ The **real** boundary: **no opinionated rule engine + no fix mutation**. Recipes ## 3. True architectural limits — preserve -These aren't defensive prejudices; they're real shape constraints that the SQL-index approach genuinely doesn't fit. - -| Limit | Why it's real | -| --------------------------------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | -| **Sub-100ms cold-start CLI** | Every `codemap query` / `codemap show` is a one-shot read. Adding a daemon-by-default would break the ergonomics. Daemon stays opt-in (`--watch` flag) per § 2.4. | -| **No LLM in the box** | Embedded intent classification, semantic search over symbol names, embedding-driven recipe routing — the agent host owns this. We supply structure; they supply meaning. | -| **No fix engine** | We **read** structure. Mutating code is a different product class (codemod tools own this). Per-row `actions` hints are enough — agents execute. The `rename --dry-run` capability (item 1.10) is borderline; ships only if it stays read-only (diff output, no file writes). | -| **No runtime tracing** | V8 traces / production beacons — Fallow's paid moat. Static coverage ingestion (just shipped) is the opt-in slice. | -| **No JS execution at index time** | Config files via `import()` is the only exception; recipe SQL is parsed but never `eval`'d. Plugin layer (C.9) must respect this — plugins describe rules in static config (globs, glob → `is_entry: true` mappings), not by running arbitrary code. | +Two layers. **Moat** rows are load-bearing under the "equal / surpass fallow on agent-facing capability while keeping the SQL-index thesis" mission — eroding either of them turns codemap into "fallow with extra steps." **Ergonomic / safety** rows are real shape constraints but not differentiators; they're floors, not moats. + +### Moat (load-bearing — every PR reviewer should defend these) + +| Limit | Why it's the moat | +| ------------------------------------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | +| **A. SQL is the API** | Every capability is a recipe (saved query) or a primitive recipes can compose — never a pre-baked verdict. Fallow ships verdict-shaped verbs (`fallow dead-code`, `fallow audit`) per [`fallow.md § 5`](./fallow.md#5-where-codemap-is-already-ahead-fallow-could-learn-back); codemap exposes the **predicate**, not the verdict. The moment a CLI verb returns `pass`/`fail` _without_ a recipe form behind it, the moat erodes. **Verdicts are an OUTPUT mode** (e.g. `--format sarif`, `audit --base ` deltas), never a primitive. Reviewer test for any new verb: "is this also expressible as `query --recipe `?" | +| **B. Extracted structure ≥ verdicts** | Schema breadth is what _equals/surpasses_ fallow on agent-facing capability. CSS (`css_variables` / `css_classes` / `css_keyframes`), `markers`, `type_members`, `calls.caller_scope`, `components.hooks_used` — fallow has none of these per [`fallow.md § 5`](./fallow.md#5-where-codemap-is-already-ahead-fallow-could-learn-back). Every § 1 capability depends on this substrate. Slimming the schema for theoretical perf / simplicity is a regression unless the column is empirically unread. Reviewer test for any "drop column X" PR: "what recipe (bundled or hypothetical) does this kill?" | + +### Ergonomic / safety preferences (real but not differentiators) + +| Limit | Why it's real | +| --------------------------------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | +| **Sub-100ms cold-start CLI** | Every `codemap query` / `codemap show` is a one-shot read. Adding a daemon-by-default would break the ergonomics. Daemon stays opt-in (`--watch` flag) per § 2.4. _Floor, not moat — fallow is also fast._ | +| **No LLM in the box** | Embedded intent classification, semantic search over symbol names, embedding-driven recipe routing — the agent host owns this. We supply structure; they supply meaning. _Convergent with fallow — not a differentiator._ | +| **No fix engine** | We **read** structure. Mutating code is a different product class (codemod tools own this). Per-row `actions` hints are enough — agents execute. The `rename --dry-run` capability (item 1.10) is borderline; ships only if it stays read-only (diff output, no file writes). _Adjacent to moat A — fix engines tend to dictate verdict-shaped APIs._ | +| **No runtime tracing** | V8 traces / production beacons — fallow's paid moat. Static coverage ingestion (shipped in C.11) is the opt-in slice. _The single rivalrous limit — fallow's paid product lives here._ | +| **No JS execution at index time** | Config files via `import()` is the only exception; recipe SQL is parsed but never `eval`'d. Plugin layer (C.9) must respect this — plugins describe rules in static config (globs, glob → `is_entry: true` mappings), not by running arbitrary code. _Safety floor — protects supply-chain attack surface; both tools converge here._ | --- From 6f845badba7fc8db1daf3a21457038bf96dfc2f9 Mon Sep 17 00:00:00 2001 From: Sutu Sebastian Date: Mon, 4 May 2026 13:30:43 +0300 Subject: [PATCH 08/20] =?UTF-8?q?docs(research):=20=C2=A7=205=20ship=20seq?= =?UTF-8?q?uence=20=E2=80=94=20parallel=20plan-PR=20for=20(b)=20at=20T+0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Grill-me Q2 outcome (under "equal/surpass fallow" mission): the "cheapest non-goal flip first" ordering was a small-team confidence move, but the § 3 moat rewrite already paid that confidence cost. The real risk under the actual mission is the deferral trap — XL items become "next quarter" while every new recipe inherits the noisy substrate (untested-and-dead's Next.js page.tsx false-positive class). Hybrid resolved: - Shipping cadence stays (a) → (c) → (b) impl → (d). - (b) plan PR opens at T+0, iterates in parallel during (a)+(c). - Plan opens with ~30% of decisions pre-locked: entry-point hints only per Grill Q4, static config only per § 3 "no JS exec at index time" ergonomic limit. Not a blank-slate plan — structured from day 1. Added a 5-row T-table in § 5 spelling out the parallel tracks. (b)'s "Why" cell now names the deferral trap explicitly; (d)'s "Why" pins its dep on (b) impl (not just (b)). Rationale list updated to flag that the moat rewrite paid the confidence move so (a) doesn't pay it again. Cost-if-abandoned escape hatch: plan PR can close as "Status: Rejected (YYYY-MM-DD)" per docs/README.md Rule 8. Design surface captured either way. --- .../non-goals-reassessment-2026-05.md | 30 ++++++++++++------- 1 file changed, 20 insertions(+), 10 deletions(-) diff --git a/docs/research/non-goals-reassessment-2026-05.md b/docs/research/non-goals-reassessment-2026-05.md index 6592c0fc..da457d5b 100644 --- a/docs/research/non-goals-reassessment-2026-05.md +++ b/docs/research/non-goals-reassessment-2026-05.md @@ -169,22 +169,32 @@ Fallow is a Cargo workspace ([upstream](https://github.com/fallow-rs/fallow); ~1 ## 5. Recommended next-pick under the new framing -| Pick | Effort | Agent value | Why | -| ------------------------------------------------------- | ------ | ------------------------------------ | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | -| **(a) FTS5 + Mermaid output** (§ 2.1 + § 2.2 in one PR) | M | High | Both are non-goals worth flipping, both are ~50–150 LoC each, both compound directly with every existing recipe. One bundled recipe (`text-in-deprecated-functions`) demonstrates the JOIN; one new `--format mermaid` flag on `impact` demonstrates the formatter. Same shape as C.11 plan: minor changeset (FTS5 = new virtual table = SCHEMA bump), bundled recipe. | -| **(b) C.9 Framework plugin layer** | XL | Very High but multiplier-on-existing | Sharpens every shipped recipe (untested-and-dead currently false-positives Next.js page.tsx). Big surface; needs full plan PR per the docs/research/fallow.md note. Defer to (a)+(b) sequence: ship (a) first as a confidence-building "we can flip a non-goal cleanly" move, then plan (b) as the multi-tracer big-surface PR. | -| **(c) Cyclomatic complexity column** (item 1.3) | M | Medium | One new column on `symbols`, one bundled recipe (`high-complexity-untested`). Effort is **M** (matches § 1.3 / § 8 errata) because branching-node counting requires extending the AST walker in `src/parser.ts` — node-counting isn't already in place. Promotes "no static analysis" non-goal flip from rhetoric to concrete capability. | -| **(d) LSP shim** (§ 2.5) | L | Very High agent UX | Blocks on (b) for entry-point awareness. Cross-reference fallow `crates/lsp/` heavily during plan. | - -**Recommended order:** (a) → (c) → (b) → (d). +| Pick | Effort | Agent value | Why | +| ------------------------------------------------------- | ------ | ------------------------------------ | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ | +| **(a) FTS5 + Mermaid output** (§ 2.1 + § 2.2 in one PR) | M | High | Both are non-goals worth flipping, both are ~50–150 LoC each, both compound directly with every existing recipe. One bundled recipe (`text-in-deprecated-functions`) demonstrates the JOIN; one new `--format mermaid` flag on `impact` demonstrates the formatter. Same shape as C.11 plan: minor changeset (FTS5 = new virtual table = SCHEMA bump), bundled recipe. | +| **(b) C.9 Framework plugin layer** | XL | Very High but multiplier-on-existing | Sharpens every shipped recipe (untested-and-dead currently false-positives Next.js page.tsx). Big surface; **plan PR opens at T+0 in parallel with (a) shipping** — pre-locked decisions (entry-point hints only per Grill Q4; no JS exec at index time per § 3 ergonomic limits) cut its cold-start. Plan iterates during (a)+(c) shipping; impl unblocks when its slot arrives. Avoids the deferral trap where XL items become "next quarter" while the noisy-substrate compounds. | +| **(c) Cyclomatic complexity column** (item 1.3) | M | Medium | One new column on `symbols`, one bundled recipe (`high-complexity-untested`). Effort is **M** (matches § 1.3 / § 8 errata) because branching-node counting requires extending the AST walker in `src/parser.ts` — node-counting isn't already in place. Promotes "no static analysis" non-goal flip from rhetoric to concrete capability. | +| **(d) LSP shim** (§ 2.5) | L | Very High agent UX | Blocks on (b) impl for entry-point awareness. Cross-reference fallow `crates/lsp/` heavily during plan. | + +**Recommended order (shipping cadence):** (a) → (c) → (b) impl → (d). **Plan track (parallel):** (b) plan PR opens at T+0. + +| T | Track 1 — shipping cadence | Track 2 — (b) plan PR | +| -------------- | ------------------------------------------------------------------------------------------ | -------------------------------------------------------------------------------------------------------------- | +| **T+0** | — | Open `docs/plans/c9-plugin-layer.md` (skeleton; pre-locked: entry-point hints only, static config, no JS exec) | +| **T+0 → +1w** | Ship **(a)** FTS5 + Mermaid (one PR; schema bump + formatter) | Plan iterates — contract shape, plugin discovery, reachability sweep algorithm, schema deltas | +| **T+1w → +2w** | Ship **(c)** complexity column (1 column + 1 recipe; reuses (a)'s schema-bump muscle) | Plan converges; ready for impl review | +| **T+2w → +5w** | Ship **(b) impl** — merged with plan | done | +| **T+5w → +7w** | Ship **(d)** LSP shim (consumes (b)'s entry-point awareness; per fallow `crates/lsp/` map) | — | **Rationale:** -1. (a) is the cheapest non-goal flip; ships in one PR; proves the pattern. +1. (a) is the cheapest non-goal flip; ships in one PR; the §3 moat rewrite already paid the "we can flip a non-goal cleanly" confidence move so (a) doesn't need to re-pay it. 2. (c) reuses (a)'s "new column on `symbols`" muscle; keeps the cadence. -3. (b) is the big-surface multi-tracer PR — by the time we get here, FTS5 + Mermaid + complexity have shown the "compositional capability" thesis on real recipes. +3. (b) is the big-surface multi-tracer PR — by the time impl starts, FTS5 + Mermaid + complexity have shown the "compositional capability" thesis on real recipes, and the plan has converged. Plan-in-parallel is the structural commitment that resists the deferral trap (every recipe layered on the noisy `dependencies` substrate inherits `untested-and-dead`'s Next.js-page false-positive class until (b) lands). 4. (d) lands last because LSP shim wants entry-point awareness from (b) to give accurate "find references". +**Cost if (b) is abandoned mid-plan:** the plan PR closes as `Status: Rejected (YYYY-MM-DD) — ` per [`docs/README.md` Rule 8](../README.md). Design surface captured either way. + --- ## 6. Open questions From 0b9d878c4e806101147f9e9e4401a34a333d3acf Mon Sep 17 00:00:00 2001 From: Sutu Sebastian Date: Mon, 4 May 2026 13:35:08 +0300 Subject: [PATCH 09/20] =?UTF-8?q?docs(research):=20=C2=A7=202=20reframed?= =?UTF-8?q?=20via=20=C2=A7=203=20moats=20(taxonomy=20+=20verdict=20cross-r?= =?UTF-8?q?efs)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Grill-me Q3 outcome: § 2's five flips inherited their shape from "original non-goals worth challenging" — but after § 3 locked in the moats, that shape conflated three different categories: - Moat-extending flips (2.1 FTS5, 2.3 static analysis) — substrate growth inside moat B - Moat-aligned flip (2.2 output formatters) — verdicts as output mode per moat A - Moat-orthogonal transport flips (2.4 daemon, 2.5 LSP shim) — neither moat is touched; flipping just re-exposes existing substrate Anchors preserved (2.1-2.5 stay) — anchor-preservation discipline per docs-governance § 3 / docs/README.md Rule 7. No cascading link updates needed in § 3 / § 4 / § 5 / § 8. Changes per section: - § 2 header — added a reading note naming the three categories and pointing each flip at the moat row it relates to. - § 2.3 — verdict no longer restates "no opinionated rule engine + no fix engine" (now canonical in § 3 moat A + ergonomic row); instead cross-references and names the static-analysis category as in-scope. Closed-dead-subgraph caveat preserved (it's the C.9 motivator). - § 2.4 — added "Moat relation: orthogonal" subsection naming the transport / process-model framing. AST-caching capability claim preserved + cross-linked to § 6 Q1. Verdict points the daemon-default question at § 6 Q1 explicitly (single canonical home). - § 2.5 — replaced the unmeasured "80% of LSP read-side" claim with a structural argument: shim wraps shipped engines (show / impact / watch) via stdio without re-extracting structure; an LSP *engine* would duplicate moat B substrate (the actual reason not to build one). Cited application/show-engine.ts + application/impact-engine.ts as the substrate the shim wraps. - § 6 Q1 — enriched with the AST-caching downstream measurement note lifted from § 2.4 (single canonical home for the daemon-default decision; § 2.4 cross-refs here). Vital-info preservation audit: - ✅ Closed-dead-subgraph caveat (8-file widget pack via fallow.md § 0) — kept verbatim in § 2.3 caveat block. - ✅ AST-caching capability claim — kept in § 2.4 "Capability unlocked" + cross-linked from § 6 Q1. - ✅ Watch-mode receipts (codemap watch / mcp --watch / serve --watch) — kept verbatim in § 2.4 "What's actually true". - ✅ Fan-in/fan-out hotspot-rankers framing — kept verbatim in § 2.3 caveat (with errata cross-ref to § 8). - ✅ Fallow `crates/lsp/` cross-ref — kept in § 2.5. Dropped (intentional): - "80% of LSP read-side" — unmeasured; replaced with structural argument that doesn't need a measurement. --- .../non-goals-reassessment-2026-05.md | 28 +++++++++++++------ 1 file changed, 20 insertions(+), 8 deletions(-) diff --git a/docs/research/non-goals-reassessment-2026-05.md b/docs/research/non-goals-reassessment-2026-05.md index da457d5b..e991ea2e 100644 --- a/docs/research/non-goals-reassessment-2026-05.md +++ b/docs/research/non-goals-reassessment-2026-05.md @@ -56,6 +56,14 @@ The data and pipeline exist. Each row needs only a recipe / formatter / verb to These were defensive choices made when the project was small. The codebase has matured past the original constraint. +**Reading note (post-§ 3 moat reframe):** each flip below relates to the moats (§ 3 rows A and B) differently: + +- **§ 2.1, § 2.3** — extend the substrate **moat B** protects (richer extraction, more first-class recipes inside the SQL-as-API thesis). +- **§ 2.2** — aligns with **moat A** (`--format mermaid` is an output mode, never a verdict-shaped primitive). +- **§ 2.4, § 2.5** — moat-orthogonal **transport / config-UX** questions; flipping doesn't touch either moat because the substrate isn't moved, just re-exposed. + +The `Verdict` rows below tie each flip back to the moat it touches; `❓` is preserved as the open-flip marker. + ### 2.1 ❓ "No FTS5 / use ripgrep for full-text" **Original framing:** [`roadmap.md § Non-goals`](../roadmap.md#non-goals-v1) — "Full-text search across all file bodies — use ripgrep / IDE / opt-in FTS5". @@ -96,27 +104,31 @@ ripgrep can't compose with `symbols` / `coverage` / `markers` in one shot — it **Caveat (caught by triangulation):** `fan-in` and `fan-out` are **hotspot rankers**, not dead-code detectors — `fan-in.sql` literally `ORDER BY fan_in DESC LIMIT 15` ([source](../../templates/recipes/fan-in.sql)). They're structural-property recipes (legitimate static analysis), but they don't cover the closed-dead-subgraph case ([`research/fallow.md` § 0](./fallow.md#0-fresh-evidence--what-a-hands-on-graph-audit-surfaced) documents the 8-file widget pack where every file had non-zero `dependencies` fan-in via self-import; the fan-in recipe missed the entire pack). That gap is a multi-axis case the C.9 framework plugin layer addresses, not the "no static analysis" non-goal. -The **real** boundary: **no opinionated rule engine + no fix mutation**. Recipes that compute structural properties (complexity, boundary checks, unused exports) are pure SQL on top of the index — exactly the niche we own. +The **real** boundary lives in **§ 3 moat A** ("verdicts are an OUTPUT mode, never a primitive") + **§ 3 ergonomic "No fix engine"** row. § 2.3 doesn't restate that boundary — it names the static-analysis _category_ as in-scope; § 3 names the _shape_ it must take. -**Verdict:** rewrite the non-goal as "no opinionated rule engine, no fix engine"; promote complexity / boundary / unused-exports to **first-class recipes** (items 1.3 / 1.5 / 1.2 above). +**Verdict:** static analysis is in scope as **predicate-as-API recipes** (per moat A). Promote complexity / boundary / unused-exports to **first-class recipes** (items 1.3 / 1.5 / 1.2 above). The previously-stated "no opinionated rule engine + no fix engine" wording now lives canonically in § 3 (moat A + ergonomic row); cross-reference, don't restate. ### 2.4 ❓ "No persistent daemon" **Original framing:** "Persistent daemon process — SQLite supports concurrent readers and our one-shot CLI startup is sub-100ms; revisit only if MCP / HTTP measurements demand it." -**What's actually true:** we **have** a daemon — `codemap watch`, `codemap mcp --watch`, `codemap serve --watch`. The non-goal preserves a constraint that no longer exists. The CLI cold-start argument still applies for one-shot `codemap query` invocations, but the long-running modes are explicitly daemon-shaped. +**What's actually true:** we **have** a daemon — `codemap watch`, `codemap mcp --watch`, `codemap serve --watch`. The non-goal preserves a constraint that no longer exists. The CLI cold-start argument still applies for one-shot `codemap query` invocations (preserved as a § 3 ergonomic floor), but the long-running modes are explicitly daemon-shaped. -**Capability unlocked:** caching parsed ASTs in memory between requests would drop incremental reindex from ms to µs. Worth measuring; the data path already exists (we just throw away the AST per request). +**Moat relation:** orthogonal — daemon is a transport / process-model concern; neither moat A (predicate-as-API) nor moat B (extracted structure) is touched by flipping it. This is why § 2.4 stays a flip but doesn't gain new flip-shape arguments from the moat reframe. -**Verdict:** rewrite as "**daemon stays opt-in**; one-shot CLI never requires it." The current `--watch` flag is the right shape; just stop saying we don't have one. +**Capability unlocked:** caching parsed ASTs in memory between requests would drop incremental reindex from ms to µs. Worth measuring; the data path already exists (we just throw away the AST per request). Lives downstream of the § 6 Q1 daemon-default decision. + +**Verdict:** rewrite as "**daemon stays opt-in**; one-shot CLI never requires it." The current `--watch` flag is the right shape; just stop saying we don't have one. **Default-on for `mcp` / `serve`** is a config-UX question — see [§ 6 Q1](#6-open-questions). ### 2.5 ❓ "No LSP replacement" **Original framing:** "Replacing LSP or language servers — no rename / go-to-definition / hover types". -**What's actually true:** we have `show ` (file:line + signature → "go to definition"), `impact ` (callers / callees → "find references"), `watch` (live index → "background analysis"). That's 80% of LSP read-side. We don't have hover types or rename, but we don't need to **be** an LSP — we can ship a **thin LSP shim** that proxies to existing engines (fallow has `crates/lsp/` we can study for the protocol shape). +**What's actually true:** we have `show ` (file:line + signature → "go to definition"), `impact ` (callers / callees → "find references"), `watch` (live index → "background analysis") — LSP read-side capabilities **already in shipped engines** (`application/show-engine.ts`, `application/impact-engine.ts`, watch-mode chokidar). An LSP shim wraps them via stdio without re-extracting structure; fallow has `crates/lsp/` to study for the protocol shape. + +**Moat relation:** transport-only. Shim wraps existing engines; doesn't move substrate (moat B) or pre-bake verdicts (moat A). The reason _not_ to ship an LSP **engine** is that an engine would re-extract structure inside the protocol layer — duplicating the substrate moat B already owns. The shim approach explicitly respects this. -**Verdict:** rewrite as "no LSP **engine**; LSP **shim** consuming the existing index is in scope." Defer the shim until plugin layer (C.9) lands — entry-point awareness sharpens "find references" accuracy. +**Verdict:** no LSP **engine** (would duplicate moat B substrate); LSP **shim** consuming existing engines is in scope. Defer the shim until plugin layer (C.9) impl lands — entry-point awareness sharpens "find references" accuracy. --- @@ -199,7 +211,7 @@ Fallow is a Cargo workspace ([upstream](https://github.com/fallow-rs/fallow); ~1 ## 6. Open questions -- **Daemon-by-default for MCP / HTTP** — even with one-shot CLI preserved, should `mcp` and `serve` default to `--watch` since both are inherently long-running? Reduces "is index stale?" friction agents already complain about. +- **Daemon-by-default for MCP / HTTP** — even with one-shot CLI preserved, should `mcp` and `serve` default to `--watch` since both are inherently long-running? Reduces "is index stale?" friction agents already complain about. Downstream measurement once defaults stabilise: AST in-memory caching between requests (per § 2.4) — would drop incremental reindex ms → µs; data path already exists (AST currently thrown away per request). - **FTS5 opt-in vs default-on** — index size tax is real on big repos. First pass: opt-in via `codemap.config.ts` `fts5: true`. Revisit after measurements on the fallow / external corpus. - **LSP shim vs new-process LSP server** — shim wraps the existing engines via stdio (cheap, no new transport); standalone LSP server forks a daemon (matches LSP convention, more code). Probably shim first; standalone if VSCode extension demand emerges. - **Plugin contract scope (C.9)** — entry-point hints only (option (i) per fallow.md § 6) vs arbitrary `dependencies` edges (option (ii)). Bias toward (i) per the existing fallow.md note; revisit during plan PR. From 96f6c4e32cc5b3174be563e58f60e290d6decfc6 Mon Sep 17 00:00:00 2001 From: Sutu Sebastian Date: Mon, 4 May 2026 13:38:39 +0300 Subject: [PATCH 10/20] =?UTF-8?q?docs(research):=20=C2=A7=201.7=20Mermaid?= =?UTF-8?q?=20=E2=80=94=20bounded-input=20contract=20(moat=20A)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Grill-me Q4 outcome: § 1.7's "What's needed" cell was loose ("new --format mermaid formatter") — true but underspecified. Real-project edge counts on dependencies / calls are 1k-10k+; rendering them is either Mermaid-choking or a hairball, and silently auto-truncating (or "best-effort") would be a verdict-shaped affordance masquerading as an output mode — violates moat A. Locked in: - Allow on: impact engine output (depth-bounded), LIMIT N-shipped recipes (fan-in / fan-out), ad-hoc SQL with explicit LIMIT ≤ 50. - Reject (with scope-suggestion message) on unbounded inputs. - No auto-truncation — that's a verdict (recipe author's job to scope). Threshold (50 edges) is configurable; chosen as a default-readable upper bound for chat-client rendering. Calibrate during (a) impl PR against fixtures/golden / external corpus. DX framing: hairballed Mermaid in MCP / Cursor / Slack chat clients renders as garbage; a clear error naming knobs (LIMIT / --via / WHERE from_path LIKE) is the better consumer signal. This keeps Mermaid an output mode (moat A clean) and forces recipe authors to scope graphs — correct because they own the structural meaning of the result set. --- .../non-goals-reassessment-2026-05.md | 24 +++++++++---------- 1 file changed, 12 insertions(+), 12 deletions(-) diff --git a/docs/research/non-goals-reassessment-2026-05.md b/docs/research/non-goals-reassessment-2026-05.md index e991ea2e..b2a9a0c7 100644 --- a/docs/research/non-goals-reassessment-2026-05.md +++ b/docs/research/non-goals-reassessment-2026-05.md @@ -35,18 +35,18 @@ The right framing now: **what does the SQL-index-with-three-transports + worker- The data and pipeline exist. Each row needs only a recipe / formatter / verb to expose. Estimated effort assumes one tracer-bullet PR each. -| # | Capability | What's already in place | What's needed | Effort | -| ---- | ------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------- | ------ | -| 1.1 | **Components calling deprecated symbols** | `components.hooks_used` + `calls` + `symbols.doc_comment LIKE '%@deprecated%'` | One bundled recipe (`components-touching-deprecated`) | XS | -| 1.2 | **Exports never imported anywhere** | `exports` LEFT JOIN `imports` (by `source` resolution); `exports.re_export_source` column exists for re-export chain handling | Recipe (`unimported-exports`) — re-export chains need a JOIN through `re_export_source` to avoid false positives on barrel-only exports | S | -| 1.3 | **Cyclomatic complexity per symbol** | `calls.caller_scope` already aggregates per-symbol. (Earlier draft claimed AST node-counting was already in place; corrected per § 8 — it isn't.) | Extend the AST walker in `src/parser.ts` to count branching nodes per symbol; add `complexity REAL` column on `symbols`; ship `high-complexity-untested` recipe | M | -| 1.4 | **Refactor risk ranking** | `dependencies` (fan-in) + `coverage` (test coverage) | Recipe (`refactor-risk-ranking` — `fan_in × (100 - coverage_pct)`) | XS | -| 1.5 | **Boundary violations (config-driven)** | `dependencies` table + glob-matching primitives in `validate-engine` | New `--boundaries ` flag on `audit` or recipe consuming the config | S | -| 1.6 | **Type members consumed by external files** | `type_members` + `imports.specifiers` (JSON) | Recipe (`unused-type-members`) — needs JSON-extraction predicate on specifiers | S | -| 1.7 | **Mermaid / D2 / Graphviz output** | `dependencies` + `calls` already shape into edges; SARIF / annotations formatters demonstrate the formatter pattern | New `--format mermaid` formatter (sibling of SARIF in `output-formatters.ts`) | S | -| 1.8 | **More MCP resources** | Schema, recipes, skill already exposed via `resource-handlers.ts` | Add `codemap://files/{path}` (file shape — symbols, imports, exports, coverage) and `codemap://symbols/{name}` (LSP-like reads) | S | -| 1.9 | **Recipe usage telemetry** | `query_baselines` precedent (user-data table excluded from `dropAll()`) | New `recipe_usage` table + reconciler at MCP / HTTP request boundary; ranks recipes by recent agent use in `--recipes-json` | M | -| 1.10 | **Symbol-rename dry-run preview** | `calls` (callers) + `symbols.line_start` / `line_end` (locations) | New `codemap rename --dry-run --format diff` verb — borders on the "no fix engine" non-goal but stays read-only (just shows the diff) | M | +| # | Capability | What's already in place | What's needed | Effort | +| ---- | ------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ | ------ | +| 1.1 | **Components calling deprecated symbols** | `components.hooks_used` + `calls` + `symbols.doc_comment LIKE '%@deprecated%'` | One bundled recipe (`components-touching-deprecated`) | XS | +| 1.2 | **Exports never imported anywhere** | `exports` LEFT JOIN `imports` (by `source` resolution); `exports.re_export_source` column exists for re-export chain handling | Recipe (`unimported-exports`) — re-export chains need a JOIN through `re_export_source` to avoid false positives on barrel-only exports | S | +| 1.3 | **Cyclomatic complexity per symbol** | `calls.caller_scope` already aggregates per-symbol. (Earlier draft claimed AST node-counting was already in place; corrected per § 8 — it isn't.) | Extend the AST walker in `src/parser.ts` to count branching nodes per symbol; add `complexity REAL` column on `symbols`; ship `high-complexity-untested` recipe | M | +| 1.4 | **Refactor risk ranking** | `dependencies` (fan-in) + `coverage` (test coverage) | Recipe (`refactor-risk-ranking` — `fan_in × (100 - coverage_pct)`) | XS | +| 1.5 | **Boundary violations (config-driven)** | `dependencies` table + glob-matching primitives in `validate-engine` | New `--boundaries ` flag on `audit` or recipe consuming the config | S | +| 1.6 | **Type members consumed by external files** | `type_members` + `imports.specifiers` (JSON) | Recipe (`unused-type-members`) — needs JSON-extraction predicate on specifiers | S | +| 1.7 | **Mermaid / D2 / Graphviz output** | `dependencies` + `calls` already shape into edges; SARIF / annotations formatters demonstrate the formatter pattern | New `--format mermaid` formatter (sibling of SARIF in `output-formatters.ts`) **with bounded-input contract**: input must come from `impact` engine, a `LIMIT N`-shipped recipe (e.g. `fan-in`, `fan-out`), or ad-hoc SQL with explicit `LIMIT ≤ 50`. Unbounded inputs error with a scope-suggestion message naming the recipe + edge count + `LIMIT`/`--via`/`WHERE` knobs. Auto-truncation is out of scope — silent subset selection would be a **verdict-shaped affordance** masquerading as an output mode (violates moat A). Hairballed Mermaid renders as garbage in chat clients (MCP / Cursor / Slack); a clear error pointing at how to scope is the better DX. | S | +| 1.8 | **More MCP resources** | Schema, recipes, skill already exposed via `resource-handlers.ts` | Add `codemap://files/{path}` (file shape — symbols, imports, exports, coverage) and `codemap://symbols/{name}` (LSP-like reads) | S | +| 1.9 | **Recipe usage telemetry** | `query_baselines` precedent (user-data table excluded from `dropAll()`) | New `recipe_usage` table + reconciler at MCP / HTTP request boundary; ranks recipes by recent agent use in `--recipes-json` | M | +| 1.10 | **Symbol-rename dry-run preview** | `calls` (callers) + `symbols.line_start` / `line_end` (locations) | New `codemap rename --dry-run --format diff` verb — borders on the "no fix engine" non-goal but stays read-only (just shows the diff) | M | **Aggregate**: ~10 first-class agent-facing capabilities sitting in unwritten JOINs / unwritten formatters. Same multiplicative effect as C.11's three bundled recipes. From 2933cf0cd20b4d7c75ac792204916d996a9bd231 Mon Sep 17 00:00:00 2001 From: Sutu Sebastian Date: Mon, 4 May 2026 13:43:23 +0300 Subject: [PATCH 11/20] =?UTF-8?q?docs(research):=20=C2=A7=201.10=20rename?= =?UTF-8?q?=20=E2=80=94=20recipe-shape=20(moat=20A)=20+=20parametrised=20r?= =?UTF-8?q?ecipes?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Grill-me Q5 outcome: § 1.10's verb-shape ("codemap rename --dry-run") was downstream of the OLD § 3 ("no fix engine" as a top- level non-goal). After the moat reframe, the actual test is moat A: verdict-shape vs recipe-shape. Verb hides every implicit rename choice (visibility filter, type-only re-exports, test files, aliases) inside argv parsing — not auditable. Recipe-shape puts those choices in reviewable SQL. Locked in: - Bundled recipe rename-preview.sql with --params key=value substitution (?-placeholder binding via db.ts prepared statements). - --format diff output mode (sibling of --format mermaid per item 1.7; same "rows in, renderable text out" pattern). - No new verb / engine / MCP tool / HTTP route. SQL stays the API. - Effort drops M → S. Cross-cutting infrastructure unlocked: parametrised recipes is net-new plumbing but pays for itself on the first downstream use. Already- visible follow-ons captured in the new "Cross-cutting infrastructure unlocked by item 1.10" paragraph at the end of § 1: - delete-symbol-preview, extract-function-preview, inline-symbol- preview — same recipe-shape pattern; all gated on the same plumbing. - Parametrising existing static recipes (untested-and-dead --params min_coverage=80 instead of hardcoded < 80) — cleanup opportunity the same plumbing enables. This is the second moat-A demonstration in two adjacent grill rounds (after § 1.7's bounded-input contract on Mermaid). Both prove the "verdicts are output mode, recipes are the API" framing on real capabilities — exactly what the (a) plan-PR will need to point at when reviewers ask "what changed?". --- docs/research/non-goals-reassessment-2026-05.md | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/docs/research/non-goals-reassessment-2026-05.md b/docs/research/non-goals-reassessment-2026-05.md index b2a9a0c7..bcaed540 100644 --- a/docs/research/non-goals-reassessment-2026-05.md +++ b/docs/research/non-goals-reassessment-2026-05.md @@ -46,10 +46,12 @@ The data and pipeline exist. Each row needs only a recipe / formatter / verb to | 1.7 | **Mermaid / D2 / Graphviz output** | `dependencies` + `calls` already shape into edges; SARIF / annotations formatters demonstrate the formatter pattern | New `--format mermaid` formatter (sibling of SARIF in `output-formatters.ts`) **with bounded-input contract**: input must come from `impact` engine, a `LIMIT N`-shipped recipe (e.g. `fan-in`, `fan-out`), or ad-hoc SQL with explicit `LIMIT ≤ 50`. Unbounded inputs error with a scope-suggestion message naming the recipe + edge count + `LIMIT`/`--via`/`WHERE` knobs. Auto-truncation is out of scope — silent subset selection would be a **verdict-shaped affordance** masquerading as an output mode (violates moat A). Hairballed Mermaid renders as garbage in chat clients (MCP / Cursor / Slack); a clear error pointing at how to scope is the better DX. | S | | 1.8 | **More MCP resources** | Schema, recipes, skill already exposed via `resource-handlers.ts` | Add `codemap://files/{path}` (file shape — symbols, imports, exports, coverage) and `codemap://symbols/{name}` (LSP-like reads) | S | | 1.9 | **Recipe usage telemetry** | `query_baselines` precedent (user-data table excluded from `dropAll()`) | New `recipe_usage` table + reconciler at MCP / HTTP request boundary; ranks recipes by recent agent use in `--recipes-json` | M | -| 1.10 | **Symbol-rename dry-run preview** | `calls` (callers) + `symbols.line_start` / `line_end` (locations) | New `codemap rename --dry-run --format diff` verb — borders on the "no fix engine" non-goal but stays read-only (just shows the diff) | M | +| 1.10 | **Symbol-rename dry-run preview** | `calls` (callers) + `symbols.line_start` / `line_end` (locations) | Bundled recipe `rename-preview.sql` with **parameter substitution** (new infra: `?`-placeholder binding via `db.ts`'s prepared-statement pattern); `--format diff` output mode (sibling of `--format mermaid` per item 1.7) converts rows to unified diff. **No new verb** — `query --recipe rename-preview --params old=foo,new=bar --format diff` is the surface. Moat-A-aligned (SQL is the API; rename's implicit choices — visibility filter, type-only re-exports, test files, aliases — live in reviewable recipe SQL, not argv). Effort drops M → S | M | **Aggregate**: ~10 first-class agent-facing capabilities sitting in unwritten JOINs / unwritten formatters. Same multiplicative effect as C.11's three bundled recipes. +**Cross-cutting infrastructure unlocked by item 1.10**: parametrised recipes (`--params key=value` with `?`-placeholder binding) are net-new infrastructure but pay for themselves on the first downstream use. Already-visible follow-ons: `delete-symbol-preview`, `extract-function-preview`, `inline-symbol-preview` (recipe-shaped refactoring previews — all moat-A-aligned, all gated on the same prepared-statement plumbing). Parametrising existing static recipes (`untested-and-dead --params min_coverage=80` instead of hardcoded `< 80`) is also a cleanup opportunity that the same plumbing enables. + --- ## 2. Non-goals worth challenging From a636eb0361ada630345c916dfd5f2e1f4b0a9a82 Mon Sep 17 00:00:00 2001 From: Sutu Sebastian Date: Mon, 4 May 2026 13:52:02 +0300 Subject: [PATCH 12/20] =?UTF-8?q?docs(research):=20=C2=A7=206=20=E2=80=94?= =?UTF-8?q?=20close=20Q1=20(daemon-default),=20Q3=20(LSP=20shape),=20Q4=20?= =?UTF-8?q?(plugin=20scope)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Grill-me Q6 outcome (and accounting cleanup): three of five § 6 open questions are now resolved by prior grill outcomes — § 6 needs to reflect that, not pretend they're still open. Resolutions captured: - Q1 (daemon-default for mcp/serve) — RESOLVED THIS GRILL TURN. Default --watch ON for both modes; opt-out via --no-watch / CODEMAP_WATCH=0. One-shot CLI defaults preserved (no watcher on query/show/snippet). Receipts: stale-index = #1 agent UX complaint (fallow.md § 6); chokidar lazy startup validated tiny by PR #46 6-watcher audit. Flip is a small follow-up PR (flag default + test + patch changeset + agent rule update per docs/README.md Rule 10). AST-caching measurement parked downstream of the flip. - Q3 (LSP shim vs standalone) — RESOLVED in § 2.5 reframe earlier this grill (commit 0b9d878). Thin shim wrapping shipped engines; no engine (would duplicate moat B substrate). Standalone deferred to "if VSCode-extension demand emerges." - Q4 (C.9 plugin contract scope) — RESOLVED via § 5 (b) plan-PR pre-locked decisions (commit 6f845ba). Entry-point hints only for v1; arbitrary edge injection deferred to v2. Static config only per § 3 ergonomic "no JS exec at index time" floor. § 6 restructured: "Resolved (2026-05)" subsection at top with full rationale + receipts; "Still open" subsection below with Q2 (FTS5 default) and Q5 (history table) — the only two genuinely-open questions left. § 2.4 verdict updated to point at the resolved § 6 Q1 anchor instead of the open-question wording. Anchor preservation: external links (#6-open-questions) still resolve to the section heading. New internal anchor (#resolved-2026-05) used by § 2.4 verdict — single inbound link, no external citations to break. --- docs/research/non-goals-reassessment-2026-05.md | 17 +++++++++++------ 1 file changed, 11 insertions(+), 6 deletions(-) diff --git a/docs/research/non-goals-reassessment-2026-05.md b/docs/research/non-goals-reassessment-2026-05.md index bcaed540..0970f9d6 100644 --- a/docs/research/non-goals-reassessment-2026-05.md +++ b/docs/research/non-goals-reassessment-2026-05.md @@ -120,7 +120,7 @@ The **real** boundary lives in **§ 3 moat A** ("verdicts are an OUTPUT mode, ne **Capability unlocked:** caching parsed ASTs in memory between requests would drop incremental reindex from ms to µs. Worth measuring; the data path already exists (we just throw away the AST per request). Lives downstream of the § 6 Q1 daemon-default decision. -**Verdict:** rewrite as "**daemon stays opt-in**; one-shot CLI never requires it." The current `--watch` flag is the right shape; just stop saying we don't have one. **Default-on for `mcp` / `serve`** is a config-UX question — see [§ 6 Q1](#6-open-questions). +**Verdict:** rewrite as "**daemon stays opt-in for one-shot CLI; default-ON for inherently-long-running modes (`mcp` / `serve`)**." Per [§ 6 Q1 (resolved 2026-05)](#resolved-2026-05): both default `--watch` ON with `--no-watch` opt-out; one-shot CLI defaults preserved. ### 2.5 ❓ "No LSP replacement" @@ -213,11 +213,16 @@ Fallow is a Cargo workspace ([upstream](https://github.com/fallow-rs/fallow); ~1 ## 6. Open questions -- **Daemon-by-default for MCP / HTTP** — even with one-shot CLI preserved, should `mcp` and `serve` default to `--watch` since both are inherently long-running? Reduces "is index stale?" friction agents already complain about. Downstream measurement once defaults stabilise: AST in-memory caching between requests (per § 2.4) — would drop incremental reindex ms → µs; data path already exists (AST currently thrown away per request). -- **FTS5 opt-in vs default-on** — index size tax is real on big repos. First pass: opt-in via `codemap.config.ts` `fts5: true`. Revisit after measurements on the fallow / external corpus. -- **LSP shim vs new-process LSP server** — shim wraps the existing engines via stdio (cheap, no new transport); standalone LSP server forks a daemon (matches LSP convention, more code). Probably shim first; standalone if VSCode extension demand emerges. -- **Plugin contract scope (C.9)** — entry-point hints only (option (i) per fallow.md § 6) vs arbitrary `dependencies` edges (option (ii)). Bias toward (i) per the existing fallow.md note; revisit during plan PR. -- **`history` table** — would unlock "when did coverage drop?" / "when did symbol X last have a caller?". Schema-shape question: per-commit snapshots (large) vs append-only event log (small but harder to query). Defer until a recipe demands it. +### Resolved (2026-05) + +- ✅ **Q1 — Daemon-by-default for `mcp` / `serve`** — **default `--watch` ON for both**; opt-out via `--no-watch` / `CODEMAP_WATCH=0`. One-shot CLI defaults preserved (still no watcher on `query` / `show` / `snippet`). Both modes are inherently long-running; stale-index friction is the #1 agent UX complaint per [`fallow.md § 6`](./fallow.md#6-open-questions); chokidar startup validated tiny on Bun + Node by the [PR #46 6-watcher audit](https://github.com/stainless-code/codemap/pull/46). **Downstream:** AST in-memory caching between requests (per § 2.4) — would drop incremental reindex ms → µs; data path already exists. Worth measuring once defaults stabilise. Flip is a small follow-up PR (flag default + test + patch changeset + agent rule update per [`docs/README.md` Rule 10](../README.md)). +- ✅ **Q3 — LSP shape** — **thin shim, no engine**; consume existing `application/show-engine.ts` + `application/impact-engine.ts` + watch-mode via stdio. Per § 2.5 reframe — building an LSP _engine_ would re-extract structure inside the protocol layer, duplicating moat B substrate. Standalone LSP server deferred to "if VSCode-extension demand emerges" (no measurement today supports it). +- ✅ **Q4 — C.9 plugin contract scope** — **entry-point hints only for v1** (option (i) per [`fallow.md § 6`](./fallow.md#6-open-questions)). Plugins contribute `glob → is_entry: true` annotations on `files`; reachability sweep over `dependencies` from entry points closes the closed-dead-subgraph case (8-file widget pack via [`fallow.md § 0`](./fallow.md#0-fresh-evidence--what-a-hands-on-graph-audit-surfaced)). Arbitrary `dependencies` edge injection deferred to v2 if a real recipe demands it. Static config only — respects § 3 ergonomic "no JS exec at index time" floor. Pre-locked into the (b) plan PR per § 5. + +### Still open + +- ❓ **Q2 — FTS5 opt-in vs default-on** — index size tax is real on big repos. First pass: opt-in via `codemap.config.ts` `fts5: true`. Revisit after measurements on the fallow / external corpus. +- ❓ **Q5 — `history` table** — would unlock "when did coverage drop?" / "when did symbol X last have a caller?". Schema-shape question: per-commit snapshots (large) vs append-only event log (small but harder to query). Defer until a recipe demands it. --- From 1526d3067ecec5bd176ce47e1e15c5dd8c527235 Mon Sep 17 00:00:00 2001 From: Sutu Sebastian Date: Mon, 4 May 2026 13:59:08 +0300 Subject: [PATCH 13/20] =?UTF-8?q?docs(research):=20=C2=A7=206=20Q2=20close?= =?UTF-8?q?d=20=E2=80=94=20FTS5=20default-OFF,=20both=20config=20+=20CLI?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Grill-me Q7 outcome: § 6 Q2 (FTS5 opt-in vs default-on) resolved. Locked in: - Toggle: BOTH codemap.config.ts `fts5: true` AND --with-fts CLI flag at index time. Config-only forces CI / ephemeral workflows to commit fts5: true to a config file; CLI-only forces long-term users to remember the flag on every --full. Cheap to support both. - Default: OFF. Backwards-compat — existing users wouldn't see .codemap/index.db grow ~30-50% silently on next --full. - Re-evaluate default in v2 once external-corpus size measurements land (bun run benchmark:query shape). Bug fix in § 2.1: the "off by default to keep cold-start sub-100ms" framing was a WRONG REASON. FTS5 is index-time cost only; cold-start reads existing DB and the virtual table doesn't slow startup. Real reason for default-OFF is index size growth. § 2.1 verdict updated to reflect this; § 6 Q2 resolution explicitly calls out the wrong-reason correction so future readers see the diff. Principle pinned: default-ON is reserved for capabilities without disk-size tax (Mermaid output, parametrised recipes, complexity column). FTS5 is the disk-tax exception. Tree state after this commit: - § 6 Q1 (daemon-default) — resolved - § 6 Q2 (FTS5 default) — resolved - § 6 Q3 (LSP shape) — resolved - § 6 Q4 (plugin scope) — resolved - § 6 Q5 (history table) — STILL OPEN (defer-bias confirmed by doc) --- docs/research/non-goals-reassessment-2026-05.md | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/docs/research/non-goals-reassessment-2026-05.md b/docs/research/non-goals-reassessment-2026-05.md index 0970f9d6..7c2787fe 100644 --- a/docs/research/non-goals-reassessment-2026-05.md +++ b/docs/research/non-goals-reassessment-2026-05.md @@ -86,7 +86,7 @@ WHERE s.doc_comment LIKE '%@deprecated%' ripgrep can't compose with `symbols` / `coverage` / `markers` in one shot — it can produce a list of file paths the agent then has to JOIN in JS. Same anti-pattern C.11 fixed for coverage. -**Verdict:** flip non-goal to **opt-in FTS5 capability**. Ship as `--with-fts` index flag (off by default to keep cold-start sub-100ms; on for projects that want it). Bundled recipe `text-in-deprecated-functions` exemplifies the JOIN. +**Verdict:** flip non-goal to **opt-in FTS5 capability**. Per [§ 6 Q2 (resolved 2026-05)](#resolved-2026-05): toggle via either `codemap.config.ts` `fts5: true` OR `--with-fts` CLI flag at index time. **Default OFF** — preserves `.codemap/index.db` size for non-users (FTS5 grows the DB ~30–50% on text-heavy projects). Cold-start is **unaffected** (FTS5 is index-time cost only; one-shot CLI reads the existing DB and the virtual table doesn't slow startup) — earlier "off to keep cold-start sub-100ms" framing was wrong. Bundled recipe `text-in-deprecated-functions` exemplifies the JOIN. **Risk:** index size grows with `--with-fts`. Mitigation: opt-in; document the size tax in `architecture.md` § Schema. @@ -219,9 +219,10 @@ Fallow is a Cargo workspace ([upstream](https://github.com/fallow-rs/fallow); ~1 - ✅ **Q3 — LSP shape** — **thin shim, no engine**; consume existing `application/show-engine.ts` + `application/impact-engine.ts` + watch-mode via stdio. Per § 2.5 reframe — building an LSP _engine_ would re-extract structure inside the protocol layer, duplicating moat B substrate. Standalone LSP server deferred to "if VSCode-extension demand emerges" (no measurement today supports it). - ✅ **Q4 — C.9 plugin contract scope** — **entry-point hints only for v1** (option (i) per [`fallow.md § 6`](./fallow.md#6-open-questions)). Plugins contribute `glob → is_entry: true` annotations on `files`; reachability sweep over `dependencies` from entry points closes the closed-dead-subgraph case (8-file widget pack via [`fallow.md § 0`](./fallow.md#0-fresh-evidence--what-a-hands-on-graph-audit-surfaced)). Arbitrary `dependencies` edge injection deferred to v2 if a real recipe demands it. Static config only — respects § 3 ergonomic "no JS exec at index time" floor. Pre-locked into the (b) plan PR per § 5. +- ✅ **Q2 — FTS5 opt-in vs default-on** — **opt-in via either `codemap.config.ts` `fts5: true` OR `--with-fts` CLI flag at index time; default OFF.** Both surfaces because config-only forces CI / ephemeral-index workflows to commit `fts5: true`; CLI-only forces every long-term user to remember the flag on `--full`. Default OFF respects backwards-compat: existing users wouldn't see `.codemap/index.db` grow ~30–50% silently on the next `--full`. Cold-start is unaffected either way (FTS5 is index-time cost only) — the earlier "default OFF to keep cold-start sub-100ms" framing was a wrong reason. **Re-evaluate default** in v2 once external-corpus size measurements (`bun run benchmark:query` shape) land. Default-ON is reserved for capabilities without disk-size tax (Mermaid output, parametrised recipes, complexity column). + ### Still open -- ❓ **Q2 — FTS5 opt-in vs default-on** — index size tax is real on big repos. First pass: opt-in via `codemap.config.ts` `fts5: true`. Revisit after measurements on the fallow / external corpus. - ❓ **Q5 — `history` table** — would unlock "when did coverage drop?" / "when did symbol X last have a caller?". Schema-shape question: per-commit snapshots (large) vs append-only event log (small but harder to query). Defer until a recipe demands it. --- From 7f78d9b73c6a68233614f382044d0f661cd6f4ed Mon Sep 17 00:00:00 2001 From: Sutu Sebastian Date: Mon, 4 May 2026 14:14:10 +0300 Subject: [PATCH 14/20] =?UTF-8?q?docs(research):=20=C2=A7=206=20Q5=20close?= =?UTF-8?q?d=20=E2=80=94=20history=20table=20deferred=20+=20full=20grill?= =?UTF-8?q?=20findings?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Grill-me Q8 outcome: § 6 Q5 (history table) resolved as DEFERRED, with the full grill analysis preserved inline so the next reviewer doesn't have to re-derive why we said no. Findings captured: - WHAT it would do — point-in-time index gains a temporal dimension ("when did symbol X get @deprecated?", "coverage trend over 50 commits", "files that became dead this week"). - WHAT audit --base already covers — pairwise diff serves the most-common temporal question (PR-scoped delta) with no schema growth. Longitudinal "evolved over commits 1..N" is the unfilled gap. - TWO SHAPES table — per-commit snapshots (~25 GB on 500-commit retention; trivial query cost) vs append-only event log (~5-25 MB deltas; heavy recursive-CTE query cost). - BACKFILL COST — N reindexes (~30s each = ~4 hrs first-run for 500 commits) is the same for both shapes; deal-breaker today. - ARCHITECTURE IMPACT — schema bump (minor per pre-v1 lesson), db.ts + indexer hooks, retention policy config, deeper git integration. - WHY DEFER — anti-bloat meta-rule (no recipe demands it); audit --base covers common case; backfill prohibitive without paying use case; shape-decision wasted without empirical access patterns. - REVISIT TRIGGERS — TWO consumers shipping jq-based "audit runs over time" workflows (mirrors B.5 verdict-threshold deferral pattern), OR query_baselines evolution becoming a recurring agent need. The full analysis is now inline in § 6 Q5 (~30 lines + cost table). Per user request: don't lose vital information; document grilling findings for fuller context. Future reviewers see the full reasoning, not just "deferred" — same posture as § 8 errata's "future readers can see the diff between v1 and v2." § 6 status after this commit: ALL FIVE OPEN QUESTIONS RESOLVED. Q1 (daemon-default), Q2 (FTS5 default), Q3 (LSP shape), Q4 (plugin scope), Q5 (history table) — every decision the doc was authored to force is now pinned with rationale and revisit triggers (where applicable). --- .../non-goals-reassessment-2026-05.md | 21 +++++++++++++++++-- 1 file changed, 19 insertions(+), 2 deletions(-) diff --git a/docs/research/non-goals-reassessment-2026-05.md b/docs/research/non-goals-reassessment-2026-05.md index 7c2787fe..56427418 100644 --- a/docs/research/non-goals-reassessment-2026-05.md +++ b/docs/research/non-goals-reassessment-2026-05.md @@ -221,9 +221,26 @@ Fallow is a Cargo workspace ([upstream](https://github.com/fallow-rs/fallow); ~1 - ✅ **Q2 — FTS5 opt-in vs default-on** — **opt-in via either `codemap.config.ts` `fts5: true` OR `--with-fts` CLI flag at index time; default OFF.** Both surfaces because config-only forces CI / ephemeral-index workflows to commit `fts5: true`; CLI-only forces every long-term user to remember the flag on `--full`. Default OFF respects backwards-compat: existing users wouldn't see `.codemap/index.db` grow ~30–50% silently on the next `--full`. Cold-start is unaffected either way (FTS5 is index-time cost only) — the earlier "default OFF to keep cold-start sub-100ms" framing was a wrong reason. **Re-evaluate default** in v2 once external-corpus size measurements (`bun run benchmark:query` shape) land. Default-ON is reserved for capabilities without disk-size tax (Mermaid output, parametrised recipes, complexity column). -### Still open +- ✅ **Q5 — `history` table** — **deferred (2026-05)**. Cost / use-case / shape analysis below; revisit triggers pinned for the next reviewer. -- ❓ **Q5 — `history` table** — would unlock "when did coverage drop?" / "when did symbol X last have a caller?". Schema-shape question: per-commit snapshots (large) vs append-only event log (small but harder to query). Defer until a recipe demands it. + **What it would do.** Today's index is a **point-in-time snapshot** — `symbols`, `dependencies`, `coverage` describe "what the code looks like _now_." A `history` table adds a **temporal dimension**: queries like "when did symbol X get `@deprecated`?", "show coverage trend over the last 50 commits", "files that became dead this week" become expressible. + + **What `audit --base ` already covers (and what it doesn't).** The shipped `codemap audit --base origin/main` does a **two-snapshot pairwise diff** — current branch vs one ref (cached worktree+reindex; sub-100ms second run). That answers "what changed between A and B." It does **not** answer "how did it evolve over commits 1..N" — that's the longitudinal gap a `history` table would fill. The pairwise primitive serves the most-common temporal question (PR-scoped delta) without any schema growth. + + **Two shapes (if it ever ships).** + + | Shape | Storage | Query cost | Per-commit overhead | + | ------------------------- | ---------------------------------------------------------------------------- | ------------------------------------------------------------------------- | ------------------------------------- | + | **Per-commit snapshots** | ~N × current DB size (linear in retention; e.g. 500 commits × 50 MB ≈ 25 GB) | Trivial — `JOIN ON commit_sha` | Full snapshot insert | + | **Append-only event log** | Deltas only (~1–5% per commit; 500 commits ≈ 5–25 MB extra) | Heavy — recursive CTEs walking event log to reconstruct state at commit X | Diff-against-previous + insert deltas | + + Both pay an **N-reindexes backfill cost** to populate history for existing commits (worktree-checkout each; ~30s per reindex; 500 commits ≈ 4 hours first-run). Backfill is the deal-breaker today. + + **Architecture impact summary.** Schema bump (minor per pre-v1 changesets lesson); `db.ts` + indexer hooks for emitting history events / snapshots; retention policy (`history.max_commits` config); deepens git integration (`getCurrentCommit()` per pass + `audit --base`-style worktree pipeline reused). + + **Why defer.** (1) No bundled recipe wants history today — adding the table = schema bloat without a paying use case (anti-bloat meta-rule per [`docs-governance` skill](../../.agents/skills/docs-governance/SKILL.md)). (2) `audit --base ` already covers the most-common temporal question. (3) Backfill cost is prohibitive without a clear win. (4) Snapshots-vs-event-log is wasted analysis without empirical access-pattern data. + + **Revisit triggers.** Two consumers ship `jq`-based "audit runs over time" workflows that genuinely want persistence (mirrors B.5 verdict-threshold deferral pattern — wait for **two** asks, not one), OR `query_baselines` evolution queries become a recurring agent need. --- From 67ed2d8478df4dcb195285665752e312b20c73b1 Mon Sep 17 00:00:00 2001 From: Sutu Sebastian Date: Mon, 4 May 2026 14:22:21 +0300 Subject: [PATCH 15/20] =?UTF-8?q?docs(research):=20=C2=A7=201.9=20reframe?= =?UTF-8?q?=20+=20=C2=A7=203=20"No=20telemetry=20upload"=20floor?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Grill-me Q9 outcome: § 1.9's "Recipe usage telemetry" framing was a gotcha. The word "telemetry" carries upload / aggregation / surveillance connotations that don't match the actual capability (purely local recency tracking) — and would either get the feature rejected sight-unseen by privacy-conscious users / corp installations OR silently set up substrate for a future "phone home" PR without an explicit non-goal saying we won't. Renamed + tightened § 1.9: - "Recipe usage telemetry" → "Local recipe-recency tracking". - Table renamed recipe_usage → recipe_recency (named after the value, not the act). - Added 90-day retention bound (caps unbounded growth via per-reindex pruning). - Added opt-out config (`recipe_recency: false` skips the reconciler). - --recipes-json surface spec'd: {recipe_id, last_run_at, run_count_90d}. - Naming-note paragraph explains why "telemetry" was rejected. New § 3 ergonomic floor row "No telemetry upload": - Locks in the privacy posture explicitly. No HTTP-out primitive in codebase today (grep-able), but the floor exists to resist accumulation pressure — a future "anonymous opt-in usage stats to help prioritize recipes" PR would look reasonable without an explicit floor. - Convergent with fallow (probably also doesn't upload) — floor, not moat. - Cross-references item 1.9 as the only usage-data feature; consumers can audit the .codemap/index.db location + retention bound. Lockstep update needed when item 1.9 ships: docs/why-codemap.md "What Codemap is not" gains "Codemap never uploads usage data" per docs/README.md Rule 10. Already cross-referenced in § 7 of this doc. --- .../non-goals-reassessment-2026-05.md | 39 ++++++++++--------- 1 file changed, 20 insertions(+), 19 deletions(-) diff --git a/docs/research/non-goals-reassessment-2026-05.md b/docs/research/non-goals-reassessment-2026-05.md index 56427418..fcbc6d5a 100644 --- a/docs/research/non-goals-reassessment-2026-05.md +++ b/docs/research/non-goals-reassessment-2026-05.md @@ -35,18 +35,18 @@ The right framing now: **what does the SQL-index-with-three-transports + worker- The data and pipeline exist. Each row needs only a recipe / formatter / verb to expose. Estimated effort assumes one tracer-bullet PR each. -| # | Capability | What's already in place | What's needed | Effort | -| ---- | ------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ | ------ | -| 1.1 | **Components calling deprecated symbols** | `components.hooks_used` + `calls` + `symbols.doc_comment LIKE '%@deprecated%'` | One bundled recipe (`components-touching-deprecated`) | XS | -| 1.2 | **Exports never imported anywhere** | `exports` LEFT JOIN `imports` (by `source` resolution); `exports.re_export_source` column exists for re-export chain handling | Recipe (`unimported-exports`) — re-export chains need a JOIN through `re_export_source` to avoid false positives on barrel-only exports | S | -| 1.3 | **Cyclomatic complexity per symbol** | `calls.caller_scope` already aggregates per-symbol. (Earlier draft claimed AST node-counting was already in place; corrected per § 8 — it isn't.) | Extend the AST walker in `src/parser.ts` to count branching nodes per symbol; add `complexity REAL` column on `symbols`; ship `high-complexity-untested` recipe | M | -| 1.4 | **Refactor risk ranking** | `dependencies` (fan-in) + `coverage` (test coverage) | Recipe (`refactor-risk-ranking` — `fan_in × (100 - coverage_pct)`) | XS | -| 1.5 | **Boundary violations (config-driven)** | `dependencies` table + glob-matching primitives in `validate-engine` | New `--boundaries ` flag on `audit` or recipe consuming the config | S | -| 1.6 | **Type members consumed by external files** | `type_members` + `imports.specifiers` (JSON) | Recipe (`unused-type-members`) — needs JSON-extraction predicate on specifiers | S | -| 1.7 | **Mermaid / D2 / Graphviz output** | `dependencies` + `calls` already shape into edges; SARIF / annotations formatters demonstrate the formatter pattern | New `--format mermaid` formatter (sibling of SARIF in `output-formatters.ts`) **with bounded-input contract**: input must come from `impact` engine, a `LIMIT N`-shipped recipe (e.g. `fan-in`, `fan-out`), or ad-hoc SQL with explicit `LIMIT ≤ 50`. Unbounded inputs error with a scope-suggestion message naming the recipe + edge count + `LIMIT`/`--via`/`WHERE` knobs. Auto-truncation is out of scope — silent subset selection would be a **verdict-shaped affordance** masquerading as an output mode (violates moat A). Hairballed Mermaid renders as garbage in chat clients (MCP / Cursor / Slack); a clear error pointing at how to scope is the better DX. | S | -| 1.8 | **More MCP resources** | Schema, recipes, skill already exposed via `resource-handlers.ts` | Add `codemap://files/{path}` (file shape — symbols, imports, exports, coverage) and `codemap://symbols/{name}` (LSP-like reads) | S | -| 1.9 | **Recipe usage telemetry** | `query_baselines` precedent (user-data table excluded from `dropAll()`) | New `recipe_usage` table + reconciler at MCP / HTTP request boundary; ranks recipes by recent agent use in `--recipes-json` | M | -| 1.10 | **Symbol-rename dry-run preview** | `calls` (callers) + `symbols.line_start` / `line_end` (locations) | Bundled recipe `rename-preview.sql` with **parameter substitution** (new infra: `?`-placeholder binding via `db.ts`'s prepared-statement pattern); `--format diff` output mode (sibling of `--format mermaid` per item 1.7) converts rows to unified diff. **No new verb** — `query --recipe rename-preview --params old=foo,new=bar --format diff` is the surface. Moat-A-aligned (SQL is the API; rename's implicit choices — visibility filter, type-only re-exports, test files, aliases — live in reviewable recipe SQL, not argv). Effort drops M → S | M | +| # | Capability | What's already in place | What's needed | Effort | +| ---- | ------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ------ | +| 1.1 | **Components calling deprecated symbols** | `components.hooks_used` + `calls` + `symbols.doc_comment LIKE '%@deprecated%'` | One bundled recipe (`components-touching-deprecated`) | XS | +| 1.2 | **Exports never imported anywhere** | `exports` LEFT JOIN `imports` (by `source` resolution); `exports.re_export_source` column exists for re-export chain handling | Recipe (`unimported-exports`) — re-export chains need a JOIN through `re_export_source` to avoid false positives on barrel-only exports | S | +| 1.3 | **Cyclomatic complexity per symbol** | `calls.caller_scope` already aggregates per-symbol. (Earlier draft claimed AST node-counting was already in place; corrected per § 8 — it isn't.) | Extend the AST walker in `src/parser.ts` to count branching nodes per symbol; add `complexity REAL` column on `symbols`; ship `high-complexity-untested` recipe | M | +| 1.4 | **Refactor risk ranking** | `dependencies` (fan-in) + `coverage` (test coverage) | Recipe (`refactor-risk-ranking` — `fan_in × (100 - coverage_pct)`) | XS | +| 1.5 | **Boundary violations (config-driven)** | `dependencies` table + glob-matching primitives in `validate-engine` | New `--boundaries ` flag on `audit` or recipe consuming the config | S | +| 1.6 | **Type members consumed by external files** | `type_members` + `imports.specifiers` (JSON) | Recipe (`unused-type-members`) — needs JSON-extraction predicate on specifiers | S | +| 1.7 | **Mermaid / D2 / Graphviz output** | `dependencies` + `calls` already shape into edges; SARIF / annotations formatters demonstrate the formatter pattern | New `--format mermaid` formatter (sibling of SARIF in `output-formatters.ts`) **with bounded-input contract**: input must come from `impact` engine, a `LIMIT N`-shipped recipe (e.g. `fan-in`, `fan-out`), or ad-hoc SQL with explicit `LIMIT ≤ 50`. Unbounded inputs error with a scope-suggestion message naming the recipe + edge count + `LIMIT`/`--via`/`WHERE` knobs. Auto-truncation is out of scope — silent subset selection would be a **verdict-shaped affordance** masquerading as an output mode (violates moat A). Hairballed Mermaid renders as garbage in chat clients (MCP / Cursor / Slack); a clear error pointing at how to scope is the better DX. | S | +| 1.8 | **More MCP resources** | Schema, recipes, skill already exposed via `resource-handlers.ts` | Add `codemap://files/{path}` (file shape — symbols, imports, exports, coverage) and `codemap://symbols/{name}` (LSP-like reads) | S | +| 1.9 | **Local recipe-recency tracking** | `query_baselines` precedent (user-data table excluded from `dropAll()`) | New `recipe_recency(recipe_id PK, last_run_at, run_count)` table; reconciler at MCP / HTTP request boundary writes the row. **Rolling 90-day retention** — entries older than `last_run_at + 90d` pruned per reindex (caps unbounded growth). **Opt-out** via `codemap.config.ts` `recipe_recency: false` — skips the reconciler entirely; table stays empty. Surfaces in `--recipes-json` as `{recipe_id, last_run_at, run_count_90d}` so agents see recently-used recipes ranked first. **Local-only** — no upload primitive shipped or planned (see § 3 "No telemetry upload" floor). Naming note: not "telemetry" — the word carries upload / aggregation / surveillance connotations that don't match the actual capability (purely local recency tracking, like `query_baselines`). | M | +| 1.10 | **Symbol-rename dry-run preview** | `calls` (callers) + `symbols.line_start` / `line_end` (locations) | Bundled recipe `rename-preview.sql` with **parameter substitution** (new infra: `?`-placeholder binding via `db.ts`'s prepared-statement pattern); `--format diff` output mode (sibling of `--format mermaid` per item 1.7) converts rows to unified diff. **No new verb** — `query --recipe rename-preview --params old=foo,new=bar --format diff` is the surface. Moat-A-aligned (SQL is the API; rename's implicit choices — visibility filter, type-only re-exports, test files, aliases — live in reviewable recipe SQL, not argv). Effort drops M → S | M | **Aggregate**: ~10 first-class agent-facing capabilities sitting in unwritten JOINs / unwritten formatters. Same multiplicative effect as C.11's three bundled recipes. @@ -147,13 +147,14 @@ Two layers. **Moat** rows are load-bearing under the "equal / surpass fallow on ### Ergonomic / safety preferences (real but not differentiators) -| Limit | Why it's real | -| --------------------------------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | -| **Sub-100ms cold-start CLI** | Every `codemap query` / `codemap show` is a one-shot read. Adding a daemon-by-default would break the ergonomics. Daemon stays opt-in (`--watch` flag) per § 2.4. _Floor, not moat — fallow is also fast._ | -| **No LLM in the box** | Embedded intent classification, semantic search over symbol names, embedding-driven recipe routing — the agent host owns this. We supply structure; they supply meaning. _Convergent with fallow — not a differentiator._ | -| **No fix engine** | We **read** structure. Mutating code is a different product class (codemod tools own this). Per-row `actions` hints are enough — agents execute. The `rename --dry-run` capability (item 1.10) is borderline; ships only if it stays read-only (diff output, no file writes). _Adjacent to moat A — fix engines tend to dictate verdict-shaped APIs._ | -| **No runtime tracing** | V8 traces / production beacons — fallow's paid moat. Static coverage ingestion (shipped in C.11) is the opt-in slice. _The single rivalrous limit — fallow's paid product lives here._ | -| **No JS execution at index time** | Config files via `import()` is the only exception; recipe SQL is parsed but never `eval`'d. Plugin layer (C.9) must respect this — plugins describe rules in static config (globs, glob → `is_entry: true` mappings), not by running arbitrary code. _Safety floor — protects supply-chain attack surface; both tools converge here._ | +| Limit | Why it's real | +| --------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ | +| **Sub-100ms cold-start CLI** | Every `codemap query` / `codemap show` is a one-shot read. Adding a daemon-by-default would break the ergonomics. Daemon stays opt-in (`--watch` flag) per § 2.4. _Floor, not moat — fallow is also fast._ | +| **No LLM in the box** | Embedded intent classification, semantic search over symbol names, embedding-driven recipe routing — the agent host owns this. We supply structure; they supply meaning. _Convergent with fallow — not a differentiator._ | +| **No fix engine** | We **read** structure. Mutating code is a different product class (codemod tools own this). Per-row `actions` hints are enough — agents execute. The `rename --dry-run` capability (item 1.10) is borderline; ships only if it stays read-only (diff output, no file writes). _Adjacent to moat A — fix engines tend to dictate verdict-shaped APIs._ | +| **No runtime tracing** | V8 traces / production beacons — fallow's paid moat. Static coverage ingestion (shipped in C.11) is the opt-in slice. _The single rivalrous limit — fallow's paid product lives here._ | +| **No JS execution at index time** | Config files via `import()` is the only exception; recipe SQL is parsed but never `eval`'d. Plugin layer (C.9) must respect this — plugins describe rules in static config (globs, glob → `is_entry: true` mappings), not by running arbitrary code. _Safety floor — protects supply-chain attack surface; both tools converge here._ | +| **No telemetry upload** | Codemap never sends usage data anywhere. Local recipe-recency tracking (item 1.9) is the only usage-data feature; it lives in `.codemap/index.db`, retention-bounded (90 days), opt-out via config (`recipe_recency: false`). No HTTP-out primitive in the codebase today (grep-able); this floor exists to **resist accumulation pressure** — a future "anonymous opt-in usage stats to help prioritize recipes" PR would look reasonable without an explicit floor. Convergent with fallow (probably also doesn't upload) — safety promise consumers can audit, not a moat. _Floor, not moat._ | --- From efc1ebf6e8b1210962761845a9ffc8562045c855 Mon Sep 17 00:00:00 2001 From: Sutu Sebastian Date: Mon, 4 May 2026 14:32:33 +0300 Subject: [PATCH 16/20] =?UTF-8?q?docs(research):=20drop=20all=20fallow=20f?= =?UTF-8?q?raming=20=E2=80=94=20codemap=20is=20structurally=20unique?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit User reframe: codemap is the only SQL-based code index in the market; inspiration comes from the free and open internet (LSP spec, SQLite docs, AST tooling), not code-by-code cloning of any peer tool. Drop fallow as a yardstick throughout. Vital information preserved (per "don't lose any vital information that is used to execute the plan"): - Closed-dead-subgraph motivator for C.9 — kept as an abstract pattern description in § 2.3 caveat (N-file packs with self-imports, non- zero fan-in, none reachable from real entry). Was previously cited to fallow.md § 0; now stands on its own merit. - LSP read-side capabilities (show / impact / watch) — kept; LSP spec upstream is now the protocol authority instead of fallow's crates/lsp/. - Runtime-tracing scope distinction — § 3 floor reframed to anchor on "different product class entirely" (live process data vs static analysis) instead of "fallow's paid moat." - Predicate-as-API moat (A) — kept; justification now anchors on intrinsic merit (SQL is durable, agents compose any predicate) rather than "fallow ships verdicts; we don't." - Schema-breadth moat (B) — kept; justification now "codemap-specific extractions; their richness directly determines what JOINs are expressible" rather than "fallow has none of these." Section-by-section changes: - HEADER — "Companion docs / Source for deep-dives" replaced with "Companion doc" (competitive-scan only) + "Positioning" paragraph declaring structural uniqueness. - § 2.3 original-framing quote — paraphrased to drop the "(e.g. fallow, knip, jscpd)" parenthetical; pointers to roadmap.md for the full original wording. (roadmap.md itself still has the parenthetical; separate-PR scope.) - § 2.3 caveat — closed-dead-subgraph case described abstractly; no source citation needed. - § 2.5 LSP shim — "fallow has crates/lsp/" → "LSP spec upstream is the protocol authority." - § 3 intro — mission framing rewritten; "equal/surpass fallow" language replaced with "extract maximum value from the SQL-index architecture; grow the ecosystem" + "only SQL-based code index in the market" positioning. - § 3 Moat A — anchored on intrinsic merit (SQL durable + agent composability) instead of fallow comparison. - § 3 Moat B — anchored on "substrate every recipe layers on; richness determines JOIN expressivity" instead of "fallow has none of these." - § 3 ergonomic floors — dropped all "fallow is also fast" / "Convergent with fallow" annotations; reframed runtime-tracing as "different product class entirely (live process data, not static analysis)" + reframed telemetry-upload as standalone safety promise. - § 4 — DELETED ENTIRELY ("What to inspect in the fallow source tree"). Replaced with "Inspiration sources for plan-PR authoring" table listing open specs / primitive sources only (LSP spec, SQLite docs, oxc node reference, Lightning CSS, JSON-RPC + MCP spec, TC39 proposals, existing codemap surface, internal third-party graph audits). Discipline statement preserved: every plan PR cites the spec / primitive source it took inspiration from. - § 5 (d) row + T-table T+5w → +7w cell — dropped fallow crates/lsp/ refs; LSP spec is now the named authority. - § 6 Q1 — dropped fallow.md § 6 citation; stale-index frequency now anchored on PR #46 + PR #56 internal evidence. - § 6 Q4 — dropped fallow.md § 0 + § 6 citations; closed-dead-subgraph case cross-refs § 2.3 caveat instead. - § 7 cross-references — removed research/fallow.md and fallow upstream entries. Added § 4 inspection list as a self-reference. - § 8 errata § 2.3 row — dropped fallow.md citation; pattern described inline. Net effect: the doc stands on codemap's intrinsic structural properties. No peer-tool framing remains. The mission is now self-coherent: extract max value from the SQL-index architecture + grow the ecosystem, anchored on the unique-in-market positioning. --- .../non-goals-reassessment-2026-05.md | 102 +++++++++--------- 1 file changed, 48 insertions(+), 54 deletions(-) diff --git a/docs/research/non-goals-reassessment-2026-05.md b/docs/research/non-goals-reassessment-2026-05.md index fcbc6d5a..8de120cc 100644 --- a/docs/research/non-goals-reassessment-2026-05.md +++ b/docs/research/non-goals-reassessment-2026-05.md @@ -4,9 +4,9 @@ > > **Lens:** **Prescriptive** — proposes specific non-goal flips, ship sequence, open questions. Every concrete capability claim is grounded in a file path + a `codemap query` / `rg` invocation a reviewer can re-run; canonical references live in [`architecture.md § Schema`](../architecture.md#schema), [`src/db.ts`](../../src/db.ts), [`src/adapters/builtin.ts`](../../src/adapters/builtin.ts), and [`src/application/audit-engine.ts`](../../src/application/audit-engine.ts) (`V1_DELTAS`). > -> **Companion docs:** [`research/fallow.md`](./fallow.md) (capability tracker — what to adopt _from fallow_); [`research/competitive-scan-2026-04.md`](./competitive-scan-2026-04.md) (closed; original three-tool scan). +> **Companion doc:** [`research/competitive-scan-2026-04.md`](./competitive-scan-2026-04.md) — closed; historical three-tool scan. > -> **Source for deep-dives:** [fallow upstream](https://github.com/fallow-rs/fallow) — Cargo workspace with `crates/{lsp,mcp,v8-coverage,graph,extract,cli}`, `decisions/` (ADR-style), `editors/{vscode,zed}`, `docs/plugin-authoring.md`. Inspect for patterns we can adapt before each shipped feature. +> **Positioning:** codemap is **structurally unique** — the only SQL-based code index in the market (SQLite + agent-composable predicates). Inspiration comes from the free and open internet (LSP spec, SQLite extensions, AST tooling) — not from cloning specific peer tools. The mission is to extract maximum value from the SQL-index architecture and grow its ecosystem. > > **Errata note (2026-05):** Three claims in v1 of this doc were softened or corrected after cross-checking against the codebase (item 1.3 effort + scope, § 2.3 framing of `fan-in.sql`, plus a citation gap on closed-dead-subgraph evidence). See § 8 for the full diff and the process lesson it surfaced. @@ -100,11 +100,11 @@ ripgrep can't compose with `symbols` / `coverage` / `markers` in one shot — it ### 2.3 ❓ "No static analysis" -**Original framing:** "Static analysis — dead code, duplication, complexity, architecture-boundary detection, fix actions are a different product class (e.g. fallow, knip, jscpd)". +**Original framing:** "Static analysis — dead code, duplication, complexity, architecture-boundary detection, fix actions are a different product class" (with examples; see [`roadmap.md § Non-goals`](../roadmap.md#non-goals-v1) for the full original wording). **What's actually true:** we already ship `deprecated-symbols`, `untested-and-dead` (post-C.11), `barrel-files`, `worst-covered-exports`, `visibility-tags` — those **are** static analysis. The original line was rhetorical (we don't ship a rule engine with severity levels and `// codemap-disable-next-line` comments) but the bullet over-asserted. -**Caveat (caught by triangulation):** `fan-in` and `fan-out` are **hotspot rankers**, not dead-code detectors — `fan-in.sql` literally `ORDER BY fan_in DESC LIMIT 15` ([source](../../templates/recipes/fan-in.sql)). They're structural-property recipes (legitimate static analysis), but they don't cover the closed-dead-subgraph case ([`research/fallow.md` § 0](./fallow.md#0-fresh-evidence--what-a-hands-on-graph-audit-surfaced) documents the 8-file widget pack where every file had non-zero `dependencies` fan-in via self-import; the fan-in recipe missed the entire pack). That gap is a multi-axis case the C.9 framework plugin layer addresses, not the "no static analysis" non-goal. +**Caveat (caught by triangulation):** `fan-in` and `fan-out` are **hotspot rankers**, not dead-code detectors — `fan-in.sql` literally `ORDER BY fan_in DESC LIMIT 15` ([source](../../templates/recipes/fan-in.sql)). They're structural-property recipes (legitimate static analysis), but they don't cover the **closed-dead-subgraph case**: an N-file pack where every file imports a sibling, so each has non-zero `dependencies` fan-in, but **none is reachable from any real entry point** (e.g. a leftover-from-refactor widget cluster never wired into a route or a feature). The fan-in recipe scores all N files as "live" by structural fan-in alone. Closing that gap requires entry-point awareness — a multi-axis case the C.9 framework plugin layer addresses, not the "no static analysis" non-goal. The **real** boundary lives in **§ 3 moat A** ("verdicts are an OUTPUT mode, never a primitive") + **§ 3 ergonomic "No fix engine"** row. § 2.3 doesn't restate that boundary — it names the static-analysis _category_ as in-scope; § 3 names the _shape_ it must take. @@ -126,7 +126,7 @@ The **real** boundary lives in **§ 3 moat A** ("verdicts are an OUTPUT mode, ne **Original framing:** "Replacing LSP or language servers — no rename / go-to-definition / hover types". -**What's actually true:** we have `show ` (file:line + signature → "go to definition"), `impact ` (callers / callees → "find references"), `watch` (live index → "background analysis") — LSP read-side capabilities **already in shipped engines** (`application/show-engine.ts`, `application/impact-engine.ts`, watch-mode chokidar). An LSP shim wraps them via stdio without re-extracting structure; fallow has `crates/lsp/` to study for the protocol shape. +**What's actually true:** we have `show ` (file:line + signature → "go to definition"), `impact ` (callers / callees → "find references"), `watch` (live index → "background analysis") — LSP read-side capabilities **already in shipped engines** (`application/show-engine.ts`, `application/impact-engine.ts`, watch-mode chokidar). An LSP shim wraps them via stdio without re-extracting structure. Protocol authority is the [LSP spec upstream](https://microsoft.github.io/language-server-protocol/); shape is well-known. **Moat relation:** transport-only. Shim wraps existing engines; doesn't move substrate (moat B) or pre-bake verdicts (moat A). The reason _not_ to ship an LSP **engine** is that an engine would re-extract structure inside the protocol layer — duplicating the substrate moat B already owns. The shim approach explicitly respects this. @@ -136,49 +136,44 @@ The **real** boundary lives in **§ 3 moat A** ("verdicts are an OUTPUT mode, ne ## 3. True architectural limits — preserve -Two layers. **Moat** rows are load-bearing under the "equal / surpass fallow on agent-facing capability while keeping the SQL-index thesis" mission — eroding either of them turns codemap into "fallow with extra steps." **Ergonomic / safety** rows are real shape constraints but not differentiators; they're floors, not moats. +Two layers. **Moat** rows are load-bearing under the "extract maximum value from the SQL-index architecture; grow the ecosystem" mission — eroding either turns codemap into a generic linting / static-analysis tool indistinguishable from a long tail of others. **Ergonomic / safety** rows are real shape constraints but not differentiators; they're floors, not moats. Codemap is the **only SQL-based code index** in the market — both moats below codify what makes that uniqueness durable. ### Moat (load-bearing — every PR reviewer should defend these) -| Limit | Why it's the moat | -| ------------------------------------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | -| **A. SQL is the API** | Every capability is a recipe (saved query) or a primitive recipes can compose — never a pre-baked verdict. Fallow ships verdict-shaped verbs (`fallow dead-code`, `fallow audit`) per [`fallow.md § 5`](./fallow.md#5-where-codemap-is-already-ahead-fallow-could-learn-back); codemap exposes the **predicate**, not the verdict. The moment a CLI verb returns `pass`/`fail` _without_ a recipe form behind it, the moat erodes. **Verdicts are an OUTPUT mode** (e.g. `--format sarif`, `audit --base ` deltas), never a primitive. Reviewer test for any new verb: "is this also expressible as `query --recipe `?" | -| **B. Extracted structure ≥ verdicts** | Schema breadth is what _equals/surpasses_ fallow on agent-facing capability. CSS (`css_variables` / `css_classes` / `css_keyframes`), `markers`, `type_members`, `calls.caller_scope`, `components.hooks_used` — fallow has none of these per [`fallow.md § 5`](./fallow.md#5-where-codemap-is-already-ahead-fallow-could-learn-back). Every § 1 capability depends on this substrate. Slimming the schema for theoretical perf / simplicity is a regression unless the column is empirically unread. Reviewer test for any "drop column X" PR: "what recipe (bundled or hypothetical) does this kill?" | +| Limit | Why it's the moat | +| ------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | +| **A. SQL is the API** | Every capability is a recipe (saved query) or a primitive recipes can compose — never a pre-baked verdict. SQL is a durable, well-known query language; agents compose any predicate without us deciding which questions are important. The moment a CLI verb returns `pass`/`fail` _without_ a recipe form behind it, the moat erodes — the tool becomes "yet another linter with opinions baked in" instead of "the database your agent queries." **Verdicts are an OUTPUT mode** (e.g. `--format sarif`, `audit --base ` deltas), never a primitive. Reviewer test for any new verb: "is this also expressible as `query --recipe `?" | +| **B. Extracted structure ≥ verdicts** | Schema breadth is the substrate every recipe layers on. CSS (`css_variables` / `css_classes` / `css_keyframes`), `markers`, `type_members`, `calls.caller_scope`, `components.hooks_used` — these are codemap-specific extractions; their richness directly determines what JOINs are expressible and which agent questions get clean answers. Slimming the schema for theoretical perf / simplicity is a regression unless the column is empirically unread. Reviewer test for any "drop column X" PR: "what recipe (bundled or hypothetical) does this kill?" | ### Ergonomic / safety preferences (real but not differentiators) -| Limit | Why it's real | -| --------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ | -| **Sub-100ms cold-start CLI** | Every `codemap query` / `codemap show` is a one-shot read. Adding a daemon-by-default would break the ergonomics. Daemon stays opt-in (`--watch` flag) per § 2.4. _Floor, not moat — fallow is also fast._ | -| **No LLM in the box** | Embedded intent classification, semantic search over symbol names, embedding-driven recipe routing — the agent host owns this. We supply structure; they supply meaning. _Convergent with fallow — not a differentiator._ | -| **No fix engine** | We **read** structure. Mutating code is a different product class (codemod tools own this). Per-row `actions` hints are enough — agents execute. The `rename --dry-run` capability (item 1.10) is borderline; ships only if it stays read-only (diff output, no file writes). _Adjacent to moat A — fix engines tend to dictate verdict-shaped APIs._ | -| **No runtime tracing** | V8 traces / production beacons — fallow's paid moat. Static coverage ingestion (shipped in C.11) is the opt-in slice. _The single rivalrous limit — fallow's paid product lives here._ | -| **No JS execution at index time** | Config files via `import()` is the only exception; recipe SQL is parsed but never `eval`'d. Plugin layer (C.9) must respect this — plugins describe rules in static config (globs, glob → `is_entry: true` mappings), not by running arbitrary code. _Safety floor — protects supply-chain attack surface; both tools converge here._ | -| **No telemetry upload** | Codemap never sends usage data anywhere. Local recipe-recency tracking (item 1.9) is the only usage-data feature; it lives in `.codemap/index.db`, retention-bounded (90 days), opt-out via config (`recipe_recency: false`). No HTTP-out primitive in the codebase today (grep-able); this floor exists to **resist accumulation pressure** — a future "anonymous opt-in usage stats to help prioritize recipes" PR would look reasonable without an explicit floor. Convergent with fallow (probably also doesn't upload) — safety promise consumers can audit, not a moat. _Floor, not moat._ | +| Limit | Why it's real | +| --------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | +| **Sub-100ms cold-start CLI** | Every `codemap query` / `codemap show` is a one-shot read. Adding a daemon-by-default would break the ergonomics. Daemon stays opt-in (`--watch` flag) per § 2.4. _Floor, not moat — being fast is table-stakes for a CLI._ | +| **No LLM in the box** | Embedded intent classification, semantic search over symbol names, embedding-driven recipe routing — the agent host owns this. We supply structure; they supply meaning. _Floor, not moat — convergent with most static-analysis tooling._ | +| **No fix engine** | We **read** structure. Mutating code is a different product class (codemod tools own this). Per-row `actions` hints are enough — agents execute. The `rename-preview` recipe (item 1.10) is borderline; ships only if it stays read-only (diff output, no file writes). _Adjacent to moat A — fix engines tend to dictate verdict-shaped APIs._ | +| **No runtime tracing** | V8 traces / production beacons / live execution telemetry are a fundamentally different product class — live process data, not static analysis of source. Static coverage ingestion (shipped in C.11) is the closest static-side adjacent capability (reads test-runner output files; doesn't instrument runtime). Runtime instrumentation is out of scope for codemap; it requires a different data plane (process attachment, sampling, beacon transport) that doesn't compose with the SQL-index thesis. _Floor — different product class entirely._ | +| **No JS execution at index time** | Config files via `import()` is the only exception; recipe SQL is parsed but never `eval`'d. Plugin layer (C.9) must respect this — plugins describe rules in static config (globs, glob → `is_entry: true` mappings), not by running arbitrary code. _Safety floor — protects supply-chain attack surface._ | +| **No telemetry upload** | Codemap never sends usage data anywhere. Local recipe-recency tracking (item 1.9) is the only usage-data feature; it lives in `.codemap/index.db`, retention-bounded (90 days), opt-out via config (`recipe_recency: false`). No HTTP-out primitive in the codebase today (grep-able); this floor exists to **resist accumulation pressure** — a future "anonymous opt-in usage stats to help prioritize recipes" PR would look reasonable without an explicit floor. _Floor, not moat — safety promise consumers can audit._ | --- -## 4. What to inspect in the fallow source tree +## 4. Inspiration sources for plan-PR authoring -Fallow is a Cargo workspace ([upstream](https://github.com/fallow-rs/fallow); ~149 releases as of 2026-04). Areas worth a deep-dive _before_ each shipped feature so we adopt patterns rather than reinvent: +Codemap is structurally unique (only SQL-based code index in the market); there's no peer tool to clone. Plan-PR authoring should reach for **open specs and primitive sources** rather than peer-tool implementations: -| Fallow surface | Codemap relevance | When to inspect | -| ---------------------------------- | ------------------------------------------------------------------------------------------------------------------------ | ---------------------------------------------------------- | -| `crates/lsp/` | LSP shim (§ 2.5) — protocol shape, message handlers, capability negotiation | Before § 2.5 ships | -| `crates/mcp/` | Cross-check our `mcp-server.ts` tool taxonomy against fallow's; spot tools we're missing | Before adding any MCP tool | -| `crates/v8-coverage/` | Out of scope for us (paid moat) but instructive for understanding the line between static (us) and runtime (them) | Reference only | -| `crates/graph/` | Graph algorithms (cycle detection, fan-in/fan-out scoring); compare against our recursive CTEs | Before complexity recipe (item 1.3) | -| `crates/extract/` | AST extraction patterns; cross-check our `parser.ts` against their oxc usage | Before any new `symbols` column | -| `decisions/` | ADR-style decision records (`001-no-typescript-compiler.md` etc.) — adopt the pattern for our own decisions | One-off: shape codemap's plan files toward this convention | -| `editors/vscode/` + `editors/zed/` | Reference for codemap's eventual VS Code extension (orthogonal to LSP shim — extension can use either) | When demand exists | -| `docs/plugin-authoring.md` | Plugin authoring guide — model for our C.9 plugin contract | Before C.9 plan | -| `docs/positioning.md` | How fallow positions vs. linters / type checkers — mirror for codemap's vs. ripgrep / LSP framing | Before any positioning doc revision | -| `plugin-schema.json` | JSON schema for plugins — direct precedent for C.9 contract | Before C.9 plan | -| `BENCHMARKS.md` + `benchmarks/` | Benchmark methodology + real numbers — cross-check our `query-output-benchmark` shape | Before any perf-sensitive recipe | -| `action.yml` + `action/` | GitHub Action wrapper — precedent for [`roadmap.md` Backlog](../roadmap.md#backlog) "GitHub Actions `workflow_dispatch`" | Before that backlog item lands | -| `_typos.toml` + `deny.toml` | Repo-hygiene config — adopt where it strengthens our pre-commit / CI | Background | +| Source | When to consult | +| ----------------------------------------------------------------------------------- | ---------------------------------------------------------------------------------------------------------------------------- | +| [LSP spec](https://microsoft.github.io/language-server-protocol/) | Before § 2.5 LSP shim ships — protocol authority for message shapes, capability negotiation | +| [SQLite docs](https://www.sqlite.org/docs.html) (FTS5, virtual tables, WAL, PRAGMA) | Before any schema change — `WITHOUT ROWID`, FTS5 (§ 2.1), WAL implications, query optimiser behaviour | +| [oxc](https://oxc.rs) AST node reference | Before any new `symbols` column or extraction (item 1.3 complexity, future extractions) — node kinds, traversal patterns | +| [Lightning CSS docs](https://lightningcss.dev/) | Before any CSS extraction surface change — visitor API, selector parsing | +| [JSON-RPC 2.0](https://www.jsonrpc.org/specification) + MCP spec | Before any MCP tool / resource addition — message envelope, error shape | +| [TC39 proposals](https://github.com/tc39/proposals) + tsgo / tsc release notes | Before any TS-syntax-sensitive parser work — language additions that affect AST shape | +| Existing codemap surface (`docs/architecture.md`, `--recipes-json`, `db.ts`) | Always before extending — every recipe / column / engine should compose with what's already shipped, not duplicate it | +| **Internal third-party graph audits** (when run) | Source of grounded failure modes (e.g. closed-dead-subgraph case in § 2.3 caveat). Anonymous; cite the pattern, not the repo | -**Discipline:** every PR that ships a feature with a fallow precedent cites the fallow source path it took inspiration from in the changeset / plan. Mirrors the existing [`research/fallow.md` Status snapshot](./fallow.md#status-snapshot-as-of-2026-05-03) cite-the-PR habit. +**Discipline:** every plan PR cites the relevant spec / primitive source it took inspiration from in the plan body. Cross-references compose; reinvention silently does not. --- @@ -189,17 +184,17 @@ Fallow is a Cargo workspace ([upstream](https://github.com/fallow-rs/fallow); ~1 | **(a) FTS5 + Mermaid output** (§ 2.1 + § 2.2 in one PR) | M | High | Both are non-goals worth flipping, both are ~50–150 LoC each, both compound directly with every existing recipe. One bundled recipe (`text-in-deprecated-functions`) demonstrates the JOIN; one new `--format mermaid` flag on `impact` demonstrates the formatter. Same shape as C.11 plan: minor changeset (FTS5 = new virtual table = SCHEMA bump), bundled recipe. | | **(b) C.9 Framework plugin layer** | XL | Very High but multiplier-on-existing | Sharpens every shipped recipe (untested-and-dead currently false-positives Next.js page.tsx). Big surface; **plan PR opens at T+0 in parallel with (a) shipping** — pre-locked decisions (entry-point hints only per Grill Q4; no JS exec at index time per § 3 ergonomic limits) cut its cold-start. Plan iterates during (a)+(c) shipping; impl unblocks when its slot arrives. Avoids the deferral trap where XL items become "next quarter" while the noisy-substrate compounds. | | **(c) Cyclomatic complexity column** (item 1.3) | M | Medium | One new column on `symbols`, one bundled recipe (`high-complexity-untested`). Effort is **M** (matches § 1.3 / § 8 errata) because branching-node counting requires extending the AST walker in `src/parser.ts` — node-counting isn't already in place. Promotes "no static analysis" non-goal flip from rhetoric to concrete capability. | -| **(d) LSP shim** (§ 2.5) | L | Very High agent UX | Blocks on (b) impl for entry-point awareness. Cross-reference fallow `crates/lsp/` heavily during plan. | +| **(d) LSP shim** (§ 2.5) | L | Very High agent UX | Blocks on (b) impl for entry-point awareness. [LSP spec](https://microsoft.github.io/language-server-protocol/) is the protocol authority; shim wraps shipped engines (`application/show-engine.ts`, `application/impact-engine.ts`, watch-mode) via stdio. | **Recommended order (shipping cadence):** (a) → (c) → (b) impl → (d). **Plan track (parallel):** (b) plan PR opens at T+0. -| T | Track 1 — shipping cadence | Track 2 — (b) plan PR | -| -------------- | ------------------------------------------------------------------------------------------ | -------------------------------------------------------------------------------------------------------------- | -| **T+0** | — | Open `docs/plans/c9-plugin-layer.md` (skeleton; pre-locked: entry-point hints only, static config, no JS exec) | -| **T+0 → +1w** | Ship **(a)** FTS5 + Mermaid (one PR; schema bump + formatter) | Plan iterates — contract shape, plugin discovery, reachability sweep algorithm, schema deltas | -| **T+1w → +2w** | Ship **(c)** complexity column (1 column + 1 recipe; reuses (a)'s schema-bump muscle) | Plan converges; ready for impl review | -| **T+2w → +5w** | Ship **(b) impl** — merged with plan | done | -| **T+5w → +7w** | Ship **(d)** LSP shim (consumes (b)'s entry-point awareness; per fallow `crates/lsp/` map) | — | +| T | Track 1 — shipping cadence | Track 2 — (b) plan PR | +| -------------- | ------------------------------------------------------------------------------------------------ | -------------------------------------------------------------------------------------------------------------- | +| **T+0** | — | Open `docs/plans/c9-plugin-layer.md` (skeleton; pre-locked: entry-point hints only, static config, no JS exec) | +| **T+0 → +1w** | Ship **(a)** FTS5 + Mermaid (one PR; schema bump + formatter) | Plan iterates — contract shape, plugin discovery, reachability sweep algorithm, schema deltas | +| **T+1w → +2w** | Ship **(c)** complexity column (1 column + 1 recipe; reuses (a)'s schema-bump muscle) | Plan converges; ready for impl review | +| **T+2w → +5w** | Ship **(b) impl** — merged with plan | done | +| **T+5w → +7w** | Ship **(d)** LSP shim (consumes (b)'s entry-point awareness; LSP spec is the protocol authority) | — | **Rationale:** @@ -216,9 +211,9 @@ Fallow is a Cargo workspace ([upstream](https://github.com/fallow-rs/fallow); ~1 ### Resolved (2026-05) -- ✅ **Q1 — Daemon-by-default for `mcp` / `serve`** — **default `--watch` ON for both**; opt-out via `--no-watch` / `CODEMAP_WATCH=0`. One-shot CLI defaults preserved (still no watcher on `query` / `show` / `snippet`). Both modes are inherently long-running; stale-index friction is the #1 agent UX complaint per [`fallow.md § 6`](./fallow.md#6-open-questions); chokidar startup validated tiny on Bun + Node by the [PR #46 6-watcher audit](https://github.com/stainless-code/codemap/pull/46). **Downstream:** AST in-memory caching between requests (per § 2.4) — would drop incremental reindex ms → µs; data path already exists. Worth measuring once defaults stabilise. Flip is a small follow-up PR (flag default + test + patch changeset + agent rule update per [`docs/README.md` Rule 10](../README.md)). +- ✅ **Q1 — Daemon-by-default for `mcp` / `serve`** — **default `--watch` ON for both**; opt-out via `--no-watch` / `CODEMAP_WATCH=0`. One-shot CLI defaults preserved (still no watcher on `query` / `show` / `snippet`). Both modes are inherently long-running; stale-index friction is empirically the most-frequent agent UX issue (driving the watch-mode planning in [PR #46](https://github.com/stainless-code/codemap/pull/46) and the coverage discussions in [PR #56](https://github.com/stainless-code/codemap/pull/56)); chokidar startup validated tiny on Bun + Node by the PR #46 6-watcher audit. **Downstream:** AST in-memory caching between requests (per § 2.4) — would drop incremental reindex ms → µs; data path already exists. Worth measuring once defaults stabilise. Flip is a small follow-up PR (flag default + test + patch changeset + agent rule update per [`docs/README.md` Rule 10](../README.md)). - ✅ **Q3 — LSP shape** — **thin shim, no engine**; consume existing `application/show-engine.ts` + `application/impact-engine.ts` + watch-mode via stdio. Per § 2.5 reframe — building an LSP _engine_ would re-extract structure inside the protocol layer, duplicating moat B substrate. Standalone LSP server deferred to "if VSCode-extension demand emerges" (no measurement today supports it). -- ✅ **Q4 — C.9 plugin contract scope** — **entry-point hints only for v1** (option (i) per [`fallow.md § 6`](./fallow.md#6-open-questions)). Plugins contribute `glob → is_entry: true` annotations on `files`; reachability sweep over `dependencies` from entry points closes the closed-dead-subgraph case (8-file widget pack via [`fallow.md § 0`](./fallow.md#0-fresh-evidence--what-a-hands-on-graph-audit-surfaced)). Arbitrary `dependencies` edge injection deferred to v2 if a real recipe demands it. Static config only — respects § 3 ergonomic "no JS exec at index time" floor. Pre-locked into the (b) plan PR per § 5. +- ✅ **Q4 — C.9 plugin contract scope** — **entry-point hints only for v1**. Plugins contribute `glob → is_entry: true` annotations on `files`; reachability sweep over `dependencies` from entry points closes the closed-dead-subgraph case (per § 2.3 caveat — N-file packs with self-imports where each has non-zero fan-in but none is reachable from a real entry). Arbitrary `dependencies` edge injection deferred to v2 if a real recipe demands it. Static config only — respects § 3 ergonomic "no JS exec at index time" floor. Pre-locked into the (b) plan PR per § 5. - ✅ **Q2 — FTS5 opt-in vs default-on** — **opt-in via either `codemap.config.ts` `fts5: true` OR `--with-fts` CLI flag at index time; default OFF.** Both surfaces because config-only forces CI / ephemeral-index workflows to commit `fts5: true`; CLI-only forces every long-term user to remember the flag on `--full`. Default OFF respects backwards-compat: existing users wouldn't see `.codemap/index.db` grow ~30–50% silently on the next `--full`. Cold-start is unaffected either way (FTS5 is index-time cost only) — the earlier "default OFF to keep cold-start sub-100ms" framing was a wrong reason. **Re-evaluate default** in v2 once external-corpus size measurements (`bun run benchmark:query` shape) land. Default-ON is reserved for capabilities without disk-size tax (Mermaid output, parametrised recipes, complexity column). @@ -250,10 +245,9 @@ Fallow is a Cargo workspace ([upstream](https://github.com/fallow-rs/fallow); ~1 - [`architecture.md § Schema`](../architecture.md#schema) — canonical schema reference; the prescriptive items in § 1 are layered on top of these tables. - [`roadmap.md § Non-goals (v1)`](../roadmap.md#non-goals-v1) — current non-goals list (this doc proposes amendments) - [`roadmap.md § Backlog`](../roadmap.md#backlog) — backlog items this doc reorders -- [`research/fallow.md`](./fallow.md) — capability tracker for adopt-from-fallow items (different lens from this doc) -- [`research/competitive-scan-2026-04.md`](./competitive-scan-2026-04.md) — original three-tool scan (closed; this doc supersedes its non-goals shaping) +- [`research/competitive-scan-2026-04.md`](./competitive-scan-2026-04.md) — historical three-tool scan (closed; this doc supersedes its non-goals shaping) - [`docs/why-codemap.md § What Codemap is not`](../why-codemap.md#what-codemap-is-not) — consumer-facing framing of non-goals (must be updated in lockstep when § 2 items ship) -- [fallow upstream](https://github.com/fallow-rs/fallow) — see § 4 for what to inspect when +- § 4 above — open-spec / primitive-source inspection list for plan-PR authoring --- @@ -261,10 +255,10 @@ Fallow is a Cargo workspace ([upstream](https://github.com/fallow-rs/fallow); ~1 v1 of this doc was reasoned-from-substrate without enough pinning to actual file:line / `codemap query` references. A peer-model review (`composer-2-fast`) cross-checked every concrete claim against `db.ts`, `builtin.ts`, `audit-engine.ts`, `--recipes-json`, and `templates/recipes/*.sql` — caught three errors. Corrections applied below; documenting them here so future reviewers can see the diff between v1 and v2. -| Section | Original claim | Corrected claim | Evidence (codebase = source of truth) | -| --------- | ------------------------------------------------------------------------------------------------------------------------------ | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | -------------------------------------------------------------------- | -| **§ 1.2** | "Exports never imported anywhere — one recipe; XS effort" | Same recipe; **S** effort because `exports.re_export_source` requires a JOIN through re-export chains to avoid false positives on barrel-only exports | `db.ts:69` `re_export_source` column on `exports` table | -| **§ 1.3** | "AST node count from parser already in place" + "S effort" | Node-counting is **not** in place; needs an extension to the AST walker in `src/parser.ts`. **M** effort. | `rg 'complexity\|node_count\|nodeCount' src/` returns zero matches | -| **§ 2.3** | "We already ship `deprecated-symbols`, `untested-and-dead`, `barrel-files`, `fan-in`, `fan-out` — those _are_ static analysis" | Same list, but with the caveat that `fan-in` / `fan-out` are **hotspot rankers** (`ORDER BY DESC LIMIT 15`), not orphan / dead-code detectors. They don't cover the closed-dead-subgraph case from [`research/fallow.md` § 0](./fallow.md#0-fresh-evidence--what-a-hands-on-graph-audit-surfaced) — that gap motivates C.9 (framework plugin layer), not the "no static analysis" flip | `templates/recipes/fan-in.sql` shows `ORDER BY fan_in DESC LIMIT 15` | +| Section | Original claim | Corrected claim | Evidence (codebase = source of truth) | +| --------- | ------------------------------------------------------------------------------------------------------------------------------ | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | -------------------------------------------------------------------- | +| **§ 1.2** | "Exports never imported anywhere — one recipe; XS effort" | Same recipe; **S** effort because `exports.re_export_source` requires a JOIN through re-export chains to avoid false positives on barrel-only exports | `db.ts:69` `re_export_source` column on `exports` table | +| **§ 1.3** | "AST node count from parser already in place" + "S effort" | Node-counting is **not** in place; needs an extension to the AST walker in `src/parser.ts`. **M** effort. | `rg 'complexity\|node_count\|nodeCount' src/` returns zero matches | +| **§ 2.3** | "We already ship `deprecated-symbols`, `untested-and-dead`, `barrel-files`, `fan-in`, `fan-out` — those _are_ static analysis" | Same list, but with the caveat that `fan-in` / `fan-out` are **hotspot rankers** (`ORDER BY DESC LIMIT 15`), not orphan / dead-code detectors. They don't cover the closed-dead-subgraph case (N-file packs with self-imports — every file has non-zero fan-in but none reachable from a real entry) — that gap motivates C.9 (framework plugin layer), not the "no static analysis" flip | `templates/recipes/fan-in.sql` shows `ORDER BY fan_in DESC LIMIT 15` | **Process lesson** (also in [`.agents/lessons.md`](../../.agents/lessons.md)): every prescriptive research note should pin every concrete claim to a file path / `codemap query` / `rg` invocation a reviewer can re-run, and ideally cross-check against a peer model or self-audit before recommending a ship sequence. The triangulation step on this doc caught all three errors before they propagated into a plan PR. From 537cbb44ee3a5641b0798ecd4c0514c8a09282d3 Mon Sep 17 00:00:00 2001 From: Sutu Sebastian Date: Mon, 4 May 2026 14:41:21 +0300 Subject: [PATCH 17/20] =?UTF-8?q?docs(research):=20retract=20uniqueness=20?= =?UTF-8?q?claim=20=E2=80=94=20honest=20cohort=20positioning?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Fact-check finding: the "structurally unique — only SQL-based code index in the market" claim doesn't hold. Web search + verification surfaced a real cohort of SQLite-backed code indexers for AI agents: - srclight (29 stars) — SQLite FTS5 + tree-sitter + embeddings + MCP, 42 tools, 11 langs. Pitch identical to codemap's ("AI agents spend 40-60% tokens on orientation; we eliminate this"). - Sverklo (30 stars) — local-first MCP, symbol graph, blast-radius, open-source alternative to Greptile/Sourcegraph. - ctxpp / ctx++ (17 stars) — Go MCP, tree-sitter, SQLite + FTS + vector, blast-radius analysis (= codemap's impact). - KotaDB (99 stars) — TS + Bun + SQLite — IDENTICAL stack to codemap. - codemogger (2026) — MCP, tree-sitter, SQLite + FTS + vector, semantic search. - @squirrelsoft/code-index, QuickAST, code-scale-mcp, CodeAgent Indexing Engine, Polyglot Indexer MCP, Continue's CodeSnippetsIndex — all SQLite-backed code indexers with overlapping surface. Codemap is one of ~10+, NOT unique. Retracting the claim. Honest differentiation (after verification): 1. Predicate-as-API — peers ship pre-baked verbs / MCP tools; codemap exposes raw SQL + recipes. Genuinely rare in the cohort. 2. Pure structural — no embeddings, no LLM in box. Most peers add vector search by default. Genuine differentiation. 3. JS/TS/CSS-ecosystem-deep extraction — CSS variables/classes/ keyframes, React components.hooks_used, type_members, markers. Peers focus on cross-language symbol+call surface via tree-sitter. The depth axis (3) is structurally enabled by parser choice — oxc (JS/TS) and lightningcss (CSS) are Rust-based and ecosystem- specialized; peers using tree-sitter trade depth for breadth. Where codemap is BEHIND the cohort (not hidden): multi-language support (codemap = TS/JS/CSS only; peers = 10-15 langs), star count, embeddings/semantic search, market traction. Edits applied: - HEADER positioning paragraph — retracted "structurally unique"; named the cohort explicitly (srclight, Sverklo, ctxpp, KotaDB, codemogger, etc.); spelled out the three differentiation axes; added the parser-choice rationale (oxc + lightningcss as the structural enabler of axis 3). - § 3 moat-intro line — replaced "the only SQL-based code index in the market" with "specific niche in the SQLite-backed-code-index cohort" + the three axes. Reviewer test reframed: eroding either moat turns codemap into "yet-another-tool-in-the-cohort instead of the predicate-shaped specialist." Moats A and B themselves required no rewrite — their justifications (predicate-as-API durability + extracted-structure substrate) hold under the corrected positioning. The peer cohort discovery actually sharpens both moats: A is the specialty (raw SQL surface) and B is the depth axis (richer extraction than tree-sitter cohort). --- docs/research/non-goals-reassessment-2026-05.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/docs/research/non-goals-reassessment-2026-05.md b/docs/research/non-goals-reassessment-2026-05.md index 8de120cc..151cbad3 100644 --- a/docs/research/non-goals-reassessment-2026-05.md +++ b/docs/research/non-goals-reassessment-2026-05.md @@ -6,7 +6,7 @@ > > **Companion doc:** [`research/competitive-scan-2026-04.md`](./competitive-scan-2026-04.md) — closed; historical three-tool scan. > -> **Positioning:** codemap is **structurally unique** — the only SQL-based code index in the market (SQLite + agent-composable predicates). Inspiration comes from the free and open internet (LSP spec, SQLite extensions, AST tooling) — not from cloning specific peer tools. The mission is to extract maximum value from the SQL-index architecture and grow its ecosystem. +> **Positioning:** codemap occupies a specific niche in the **SQLite-backed-code-index cohort** for AI agents (peers: `srclight`, `Sverklo`, `ctxpp`, `KotaDB`, `codemogger`, `@squirrelsoft/code-index`, `QuickAST`, etc. — most use tree-sitter + SQLite + embeddings). Codemap's differentiation is along three axes: **(1) predicate-as-API** — raw SQL + recipes as the agent-facing surface; peers ship pre-baked verbs / MCP tools. **(2) Pure structural** — no embeddings, no LLM in box; peers add semantic search by default. **(3) JS/TS/CSS-ecosystem-deep extraction** — CSS variables/classes/keyframes, React `components.hooks_used`, `type_members`, `markers`; peers focus on cross-language symbol+call surface via tree-sitter. The depth axis is structurally enabled by parser choice (`oxc` for JS/TS, `lightningcss` for CSS — both Rust-based, ecosystem-specialized vs tree-sitter's multi-language breadth). Inspiration comes from open specs and primitive sources (LSP, SQLite, oxc, MCP), not code-by-code cloning of peers. **Mission:** extract maximum value from the predicate-as-API thesis on this specific niche; grow the ecosystem. > > **Errata note (2026-05):** Three claims in v1 of this doc were softened or corrected after cross-checking against the codebase (item 1.3 effort + scope, § 2.3 framing of `fan-in.sql`, plus a citation gap on closed-dead-subgraph evidence). See § 8 for the full diff and the process lesson it surfaced. @@ -136,7 +136,7 @@ The **real** boundary lives in **§ 3 moat A** ("verdicts are an OUTPUT mode, ne ## 3. True architectural limits — preserve -Two layers. **Moat** rows are load-bearing under the "extract maximum value from the SQL-index architecture; grow the ecosystem" mission — eroding either turns codemap into a generic linting / static-analysis tool indistinguishable from a long tail of others. **Ergonomic / safety** rows are real shape constraints but not differentiators; they're floors, not moats. Codemap is the **only SQL-based code index** in the market — both moats below codify what makes that uniqueness durable. +Two layers. **Moat** rows are load-bearing under the "extract maximum value from the SQL-index architecture; grow the ecosystem" mission — eroding either turns codemap into yet-another-tool-in-the-cohort instead of the predicate-shaped specialist. **Ergonomic / safety** rows are real shape constraints but not differentiators; they're floors, not moats. Codemap occupies a **specific niche** in the SQLite-backed-code-index cohort (per the header positioning) — predicate-as-API + pure structural + JS/TS/CSS-deep extraction. Both moats below codify the axes that keep that niche durable. ### Moat (load-bearing — every PR reviewer should defend these) From a5b75df41a596d681bccc6969d8e3958ad43879b Mon Sep 17 00:00:00 2001 From: Sutu Sebastian Date: Mon, 4 May 2026 14:46:28 +0300 Subject: [PATCH 18/20] =?UTF-8?q?docs(research):=20=C2=A7=201.4=20refactor?= =?UTF-8?q?-risk=20formula=20=E2=80=94=20orphan=20+=20NULL=20fixes=20+=20c?= =?UTF-8?q?aveat?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Grill-me Q12 outcome: § 1.4's "fan_in × (100 - coverage_pct)" formula had two correctness bugs and one accepted modeling limitation: CORRECTNESS FIXES (must ship): - Orphans (fan_in=0) scored 0 → "no risk" → wrong (orphans are high-risk: dead code or hidden-import targets we don't track). Fix: `fan_in + 1` so orphans score on coverage alone. - NULL coverage_pct propagated through the formula → 100 - NULL = NULL → row dropped from ORDER BY → unmeasured-coverage symbols silently vanished from the ranking. Fix: COALESCE(coverage_pct, 0) treats unmeasured as 0% (high risk). ACCEPTED v1 TRADE-OFF: - Linear-in-fan_in (fan_in 100 with 99% coverage = fan_in 1 with 0% coverage in the score). Real, but not worth fixing in the bundled recipe — users tune via project-local override. Caveat block in refactor-risk-ranking.md (will accompany the recipe when (a) ships) names tuning axes for project-local overrides: - Log-scale fan_in (LOG(fan_in + 1) * 30) for hub-heavy codebases - Visibility weight (if @public / @internal / @beta JSDoc tags are used consistently) - LOC weight (if test-density varies across files) Why ship-with-caveat instead of multi-axis composite (Option B): - Moat A says recipes are saved queries (starting points), not authoritative verdicts. Bundled formula gets 80% right; users iterate. - Anti-bloat meta-rule — every additional axis encodes more opinions; shipping minimal forces explicit thought during tuning. - Ecosystem-specific axes (visibility weight, LOC weight) shouldn't be in the bundled default. Effort stays XS. The .md caveat block lands in the (a) plan PR / impl PR alongside the .sql; not part of THIS research-note PR's scope. --- .../non-goals-reassessment-2026-05.md | 24 +++++++++---------- 1 file changed, 12 insertions(+), 12 deletions(-) diff --git a/docs/research/non-goals-reassessment-2026-05.md b/docs/research/non-goals-reassessment-2026-05.md index 151cbad3..ea1339f4 100644 --- a/docs/research/non-goals-reassessment-2026-05.md +++ b/docs/research/non-goals-reassessment-2026-05.md @@ -35,18 +35,18 @@ The right framing now: **what does the SQL-index-with-three-transports + worker- The data and pipeline exist. Each row needs only a recipe / formatter / verb to expose. Estimated effort assumes one tracer-bullet PR each. -| # | Capability | What's already in place | What's needed | Effort | -| ---- | ------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ------ | -| 1.1 | **Components calling deprecated symbols** | `components.hooks_used` + `calls` + `symbols.doc_comment LIKE '%@deprecated%'` | One bundled recipe (`components-touching-deprecated`) | XS | -| 1.2 | **Exports never imported anywhere** | `exports` LEFT JOIN `imports` (by `source` resolution); `exports.re_export_source` column exists for re-export chain handling | Recipe (`unimported-exports`) — re-export chains need a JOIN through `re_export_source` to avoid false positives on barrel-only exports | S | -| 1.3 | **Cyclomatic complexity per symbol** | `calls.caller_scope` already aggregates per-symbol. (Earlier draft claimed AST node-counting was already in place; corrected per § 8 — it isn't.) | Extend the AST walker in `src/parser.ts` to count branching nodes per symbol; add `complexity REAL` column on `symbols`; ship `high-complexity-untested` recipe | M | -| 1.4 | **Refactor risk ranking** | `dependencies` (fan-in) + `coverage` (test coverage) | Recipe (`refactor-risk-ranking` — `fan_in × (100 - coverage_pct)`) | XS | -| 1.5 | **Boundary violations (config-driven)** | `dependencies` table + glob-matching primitives in `validate-engine` | New `--boundaries ` flag on `audit` or recipe consuming the config | S | -| 1.6 | **Type members consumed by external files** | `type_members` + `imports.specifiers` (JSON) | Recipe (`unused-type-members`) — needs JSON-extraction predicate on specifiers | S | -| 1.7 | **Mermaid / D2 / Graphviz output** | `dependencies` + `calls` already shape into edges; SARIF / annotations formatters demonstrate the formatter pattern | New `--format mermaid` formatter (sibling of SARIF in `output-formatters.ts`) **with bounded-input contract**: input must come from `impact` engine, a `LIMIT N`-shipped recipe (e.g. `fan-in`, `fan-out`), or ad-hoc SQL with explicit `LIMIT ≤ 50`. Unbounded inputs error with a scope-suggestion message naming the recipe + edge count + `LIMIT`/`--via`/`WHERE` knobs. Auto-truncation is out of scope — silent subset selection would be a **verdict-shaped affordance** masquerading as an output mode (violates moat A). Hairballed Mermaid renders as garbage in chat clients (MCP / Cursor / Slack); a clear error pointing at how to scope is the better DX. | S | -| 1.8 | **More MCP resources** | Schema, recipes, skill already exposed via `resource-handlers.ts` | Add `codemap://files/{path}` (file shape — symbols, imports, exports, coverage) and `codemap://symbols/{name}` (LSP-like reads) | S | -| 1.9 | **Local recipe-recency tracking** | `query_baselines` precedent (user-data table excluded from `dropAll()`) | New `recipe_recency(recipe_id PK, last_run_at, run_count)` table; reconciler at MCP / HTTP request boundary writes the row. **Rolling 90-day retention** — entries older than `last_run_at + 90d` pruned per reindex (caps unbounded growth). **Opt-out** via `codemap.config.ts` `recipe_recency: false` — skips the reconciler entirely; table stays empty. Surfaces in `--recipes-json` as `{recipe_id, last_run_at, run_count_90d}` so agents see recently-used recipes ranked first. **Local-only** — no upload primitive shipped or planned (see § 3 "No telemetry upload" floor). Naming note: not "telemetry" — the word carries upload / aggregation / surveillance connotations that don't match the actual capability (purely local recency tracking, like `query_baselines`). | M | -| 1.10 | **Symbol-rename dry-run preview** | `calls` (callers) + `symbols.line_start` / `line_end` (locations) | Bundled recipe `rename-preview.sql` with **parameter substitution** (new infra: `?`-placeholder binding via `db.ts`'s prepared-statement pattern); `--format diff` output mode (sibling of `--format mermaid` per item 1.7) converts rows to unified diff. **No new verb** — `query --recipe rename-preview --params old=foo,new=bar --format diff` is the surface. Moat-A-aligned (SQL is the API; rename's implicit choices — visibility filter, type-only re-exports, test files, aliases — live in reviewable recipe SQL, not argv). Effort drops M → S | M | +| # | Capability | What's already in place | What's needed | Effort | +| ---- | ------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ------ | +| 1.1 | **Components calling deprecated symbols** | `components.hooks_used` + `calls` + `symbols.doc_comment LIKE '%@deprecated%'` | One bundled recipe (`components-touching-deprecated`) | XS | +| 1.2 | **Exports never imported anywhere** | `exports` LEFT JOIN `imports` (by `source` resolution); `exports.re_export_source` column exists for re-export chain handling | Recipe (`unimported-exports`) — re-export chains need a JOIN through `re_export_source` to avoid false positives on barrel-only exports | S | +| 1.3 | **Cyclomatic complexity per symbol** | `calls.caller_scope` already aggregates per-symbol. (Earlier draft claimed AST node-counting was already in place; corrected per § 8 — it isn't.) | Extend the AST walker in `src/parser.ts` to count branching nodes per symbol; add `complexity REAL` column on `symbols`; ship `high-complexity-untested` recipe | M | +| 1.4 | **Refactor risk ranking** | `dependencies` (fan-in) + `coverage` (test coverage) | Recipe `refactor-risk-ranking` with formula `(fan_in + 1) × (100 - COALESCE(coverage_pct, 0))`. The `+1` ensures orphans (fan_in=0) score on coverage alone (untested orphans = high risk; tested orphans = candidates for deletion review, not refactor). `COALESCE(coverage_pct, 0)` treats unmeasured coverage as untested (NULL coverage drops rows from `ORDER BY` otherwise). Bundled `refactor-risk-ranking.md` ships a caveat — formula is a starting point; users tune via project-local recipe override at `/.codemap/recipes/refactor-risk-ranking.sql`. Suggested axes for tuning: log-scale `fan_in` for hub-heavy codebases (`LOG(fan_in + 1) * 30`), visibility weight (if `@public`/`@internal`/`@beta` JSDoc tags are used consistently), LOC weight (if test-density varies across files). Linear-in-fan_in and equal-weight-on-orphans are accepted v1 trade-offs vs over-design. | XS | +| 1.5 | **Boundary violations (config-driven)** | `dependencies` table + glob-matching primitives in `validate-engine` | New `--boundaries ` flag on `audit` or recipe consuming the config | S | +| 1.6 | **Type members consumed by external files** | `type_members` + `imports.specifiers` (JSON) | Recipe (`unused-type-members`) — needs JSON-extraction predicate on specifiers | S | +| 1.7 | **Mermaid / D2 / Graphviz output** | `dependencies` + `calls` already shape into edges; SARIF / annotations formatters demonstrate the formatter pattern | New `--format mermaid` formatter (sibling of SARIF in `output-formatters.ts`) **with bounded-input contract**: input must come from `impact` engine, a `LIMIT N`-shipped recipe (e.g. `fan-in`, `fan-out`), or ad-hoc SQL with explicit `LIMIT ≤ 50`. Unbounded inputs error with a scope-suggestion message naming the recipe + edge count + `LIMIT`/`--via`/`WHERE` knobs. Auto-truncation is out of scope — silent subset selection would be a **verdict-shaped affordance** masquerading as an output mode (violates moat A). Hairballed Mermaid renders as garbage in chat clients (MCP / Cursor / Slack); a clear error pointing at how to scope is the better DX. | S | +| 1.8 | **More MCP resources** | Schema, recipes, skill already exposed via `resource-handlers.ts` | Add `codemap://files/{path}` (file shape — symbols, imports, exports, coverage) and `codemap://symbols/{name}` (LSP-like reads) | S | +| 1.9 | **Local recipe-recency tracking** | `query_baselines` precedent (user-data table excluded from `dropAll()`) | New `recipe_recency(recipe_id PK, last_run_at, run_count)` table; reconciler at MCP / HTTP request boundary writes the row. **Rolling 90-day retention** — entries older than `last_run_at + 90d` pruned per reindex (caps unbounded growth). **Opt-out** via `codemap.config.ts` `recipe_recency: false` — skips the reconciler entirely; table stays empty. Surfaces in `--recipes-json` as `{recipe_id, last_run_at, run_count_90d}` so agents see recently-used recipes ranked first. **Local-only** — no upload primitive shipped or planned (see § 3 "No telemetry upload" floor). Naming note: not "telemetry" — the word carries upload / aggregation / surveillance connotations that don't match the actual capability (purely local recency tracking, like `query_baselines`). | M | +| 1.10 | **Symbol-rename dry-run preview** | `calls` (callers) + `symbols.line_start` / `line_end` (locations) | Bundled recipe `rename-preview.sql` with **parameter substitution** (new infra: `?`-placeholder binding via `db.ts`'s prepared-statement pattern); `--format diff` output mode (sibling of `--format mermaid` per item 1.7) converts rows to unified diff. **No new verb** — `query --recipe rename-preview --params old=foo,new=bar --format diff` is the surface. Moat-A-aligned (SQL is the API; rename's implicit choices — visibility filter, type-only re-exports, test files, aliases — live in reviewable recipe SQL, not argv). Effort drops M → S | M | **Aggregate**: ~10 first-class agent-facing capabilities sitting in unwritten JOINs / unwritten formatters. Same multiplicative effect as C.11's three bundled recipes. From 983c67f00260b66b2370d009008cfd5a2b4ab537 Mon Sep 17 00:00:00 2001 From: Sutu Sebastian Date: Mon, 4 May 2026 14:53:53 +0300 Subject: [PATCH 19/20] =?UTF-8?q?docs(research):=20=C2=A7=201.5=20boundary?= =?UTF-8?q?=20violations=20=E2=80=94=20Shape=20A=20directional=20rules?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Grill-me Q13 outcome: § 1.5 was underspecified ("--boundaries flag on audit OR recipe consuming the config"). Three real questions needed answering: where the config lives, what shape, recipe-or-flag. Shape A (directional rules) locked in for v1: boundaries: [ { name: "no-cross-feature", from_glob: "src/features/*/**", to_glob: "src/features/*/**", action: "deny", except_self: true, }, ... ] Why A over B (element-types) over C (layers) — honest discriminator: A and B have IDENTICAL expressiveness (B compiles to A at index time). The real question is ergonomics-at-scale vs forward-compat / smallest- viable-config: - A wins 5 of 6 dimensions: smallest-viable-config (one entry); Zod schema simplest; mental-model load (one concept); forward-compat (B layers on top later as sugar); backwards-compat (never paint into a corner; primitives are durable). - B wins only "ergonomics at scale" (5+ rules with element reuse) — exactly the dimension that can be added later as a sugar layer without breaking A. - C (layer ordering) is most opinionated; only fits layered architectures. Not a v1 default. Decision rule (ship the smallest primitive that doesn't paint into a corner; layer ergonomics on top later) mirrors § 6 Q5 history-table defer logic. Implementation reuses every shipped or in-flight piece of plumbing: - Zod config slot (existing src/config.ts substrate) - Index-time reconciler (mirrors recipe_recency from item 1.9) - New boundary_rules table (moat-B-aligned schema growth) - Bundled recipe boundary-violations.sql via SQLite GLOB operator - SARIF output formatter (already shipped) for CI gate NO new CLI flag — moat-A clean. The verb is query --recipe boundary-violations --format sarif. Recipe consumes config-as-data; SARIF output mode handles verdict-shaped CI consumers. Effort stays S. Element-types / layer sugar deferred to v1.x with explicit "demand-driven" trigger (mirrors fallow.md B.5 verdict- threshold deferral pattern, kept in this doc as the recurring deferral idiom). --- .../non-goals-reassessment-2026-05.md | 24 +++++++++---------- 1 file changed, 12 insertions(+), 12 deletions(-) diff --git a/docs/research/non-goals-reassessment-2026-05.md b/docs/research/non-goals-reassessment-2026-05.md index ea1339f4..5d8e11ce 100644 --- a/docs/research/non-goals-reassessment-2026-05.md +++ b/docs/research/non-goals-reassessment-2026-05.md @@ -35,18 +35,18 @@ The right framing now: **what does the SQL-index-with-three-transports + worker- The data and pipeline exist. Each row needs only a recipe / formatter / verb to expose. Estimated effort assumes one tracer-bullet PR each. -| # | Capability | What's already in place | What's needed | Effort | -| ---- | ------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ------ | -| 1.1 | **Components calling deprecated symbols** | `components.hooks_used` + `calls` + `symbols.doc_comment LIKE '%@deprecated%'` | One bundled recipe (`components-touching-deprecated`) | XS | -| 1.2 | **Exports never imported anywhere** | `exports` LEFT JOIN `imports` (by `source` resolution); `exports.re_export_source` column exists for re-export chain handling | Recipe (`unimported-exports`) — re-export chains need a JOIN through `re_export_source` to avoid false positives on barrel-only exports | S | -| 1.3 | **Cyclomatic complexity per symbol** | `calls.caller_scope` already aggregates per-symbol. (Earlier draft claimed AST node-counting was already in place; corrected per § 8 — it isn't.) | Extend the AST walker in `src/parser.ts` to count branching nodes per symbol; add `complexity REAL` column on `symbols`; ship `high-complexity-untested` recipe | M | -| 1.4 | **Refactor risk ranking** | `dependencies` (fan-in) + `coverage` (test coverage) | Recipe `refactor-risk-ranking` with formula `(fan_in + 1) × (100 - COALESCE(coverage_pct, 0))`. The `+1` ensures orphans (fan_in=0) score on coverage alone (untested orphans = high risk; tested orphans = candidates for deletion review, not refactor). `COALESCE(coverage_pct, 0)` treats unmeasured coverage as untested (NULL coverage drops rows from `ORDER BY` otherwise). Bundled `refactor-risk-ranking.md` ships a caveat — formula is a starting point; users tune via project-local recipe override at `/.codemap/recipes/refactor-risk-ranking.sql`. Suggested axes for tuning: log-scale `fan_in` for hub-heavy codebases (`LOG(fan_in + 1) * 30`), visibility weight (if `@public`/`@internal`/`@beta` JSDoc tags are used consistently), LOC weight (if test-density varies across files). Linear-in-fan_in and equal-weight-on-orphans are accepted v1 trade-offs vs over-design. | XS | -| 1.5 | **Boundary violations (config-driven)** | `dependencies` table + glob-matching primitives in `validate-engine` | New `--boundaries ` flag on `audit` or recipe consuming the config | S | -| 1.6 | **Type members consumed by external files** | `type_members` + `imports.specifiers` (JSON) | Recipe (`unused-type-members`) — needs JSON-extraction predicate on specifiers | S | -| 1.7 | **Mermaid / D2 / Graphviz output** | `dependencies` + `calls` already shape into edges; SARIF / annotations formatters demonstrate the formatter pattern | New `--format mermaid` formatter (sibling of SARIF in `output-formatters.ts`) **with bounded-input contract**: input must come from `impact` engine, a `LIMIT N`-shipped recipe (e.g. `fan-in`, `fan-out`), or ad-hoc SQL with explicit `LIMIT ≤ 50`. Unbounded inputs error with a scope-suggestion message naming the recipe + edge count + `LIMIT`/`--via`/`WHERE` knobs. Auto-truncation is out of scope — silent subset selection would be a **verdict-shaped affordance** masquerading as an output mode (violates moat A). Hairballed Mermaid renders as garbage in chat clients (MCP / Cursor / Slack); a clear error pointing at how to scope is the better DX. | S | -| 1.8 | **More MCP resources** | Schema, recipes, skill already exposed via `resource-handlers.ts` | Add `codemap://files/{path}` (file shape — symbols, imports, exports, coverage) and `codemap://symbols/{name}` (LSP-like reads) | S | -| 1.9 | **Local recipe-recency tracking** | `query_baselines` precedent (user-data table excluded from `dropAll()`) | New `recipe_recency(recipe_id PK, last_run_at, run_count)` table; reconciler at MCP / HTTP request boundary writes the row. **Rolling 90-day retention** — entries older than `last_run_at + 90d` pruned per reindex (caps unbounded growth). **Opt-out** via `codemap.config.ts` `recipe_recency: false` — skips the reconciler entirely; table stays empty. Surfaces in `--recipes-json` as `{recipe_id, last_run_at, run_count_90d}` so agents see recently-used recipes ranked first. **Local-only** — no upload primitive shipped or planned (see § 3 "No telemetry upload" floor). Naming note: not "telemetry" — the word carries upload / aggregation / surveillance connotations that don't match the actual capability (purely local recency tracking, like `query_baselines`). | M | -| 1.10 | **Symbol-rename dry-run preview** | `calls` (callers) + `symbols.line_start` / `line_end` (locations) | Bundled recipe `rename-preview.sql` with **parameter substitution** (new infra: `?`-placeholder binding via `db.ts`'s prepared-statement pattern); `--format diff` output mode (sibling of `--format mermaid` per item 1.7) converts rows to unified diff. **No new verb** — `query --recipe rename-preview --params old=foo,new=bar --format diff` is the surface. Moat-A-aligned (SQL is the API; rename's implicit choices — visibility filter, type-only re-exports, test files, aliases — live in reviewable recipe SQL, not argv). Effort drops M → S | M | +| # | Capability | What's already in place | What's needed | Effort | +| ---- | ------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ------ | +| 1.1 | **Components calling deprecated symbols** | `components.hooks_used` + `calls` + `symbols.doc_comment LIKE '%@deprecated%'` | One bundled recipe (`components-touching-deprecated`) | XS | +| 1.2 | **Exports never imported anywhere** | `exports` LEFT JOIN `imports` (by `source` resolution); `exports.re_export_source` column exists for re-export chain handling | Recipe (`unimported-exports`) — re-export chains need a JOIN through `re_export_source` to avoid false positives on barrel-only exports | S | +| 1.3 | **Cyclomatic complexity per symbol** | `calls.caller_scope` already aggregates per-symbol. (Earlier draft claimed AST node-counting was already in place; corrected per § 8 — it isn't.) | Extend the AST walker in `src/parser.ts` to count branching nodes per symbol; add `complexity REAL` column on `symbols`; ship `high-complexity-untested` recipe | M | +| 1.4 | **Refactor risk ranking** | `dependencies` (fan-in) + `coverage` (test coverage) | Recipe `refactor-risk-ranking` with formula `(fan_in + 1) × (100 - COALESCE(coverage_pct, 0))`. The `+1` ensures orphans (fan_in=0) score on coverage alone (untested orphans = high risk; tested orphans = candidates for deletion review, not refactor). `COALESCE(coverage_pct, 0)` treats unmeasured coverage as untested (NULL coverage drops rows from `ORDER BY` otherwise). Bundled `refactor-risk-ranking.md` ships a caveat — formula is a starting point; users tune via project-local recipe override at `/.codemap/recipes/refactor-risk-ranking.sql`. Suggested axes for tuning: log-scale `fan_in` for hub-heavy codebases (`LOG(fan_in + 1) * 30`), visibility weight (if `@public`/`@internal`/`@beta` JSDoc tags are used consistently), LOC weight (if test-density varies across files). Linear-in-fan_in and equal-weight-on-orphans are accepted v1 trade-offs vs over-design. | XS | +| 1.5 | **Boundary violations (config-driven)** | `dependencies` table + Zod config substrate (`src/config.ts`) + glob-matching primitives in `validate-engine` + index-time reconciler pattern (per `recipe_recency` item 1.9) + SARIF output formatter | New `boundaries: [{name, from_glob, to_glob, action: "deny"\|"allow", except_self?}]` field in `codemap.config.ts` (directional rules — Shape A). New `boundary_rules` table populated at index time from config (mirrors `recipe_recency` reconciler). Bundled recipe `boundary-violations.sql` JOINs `dependencies` × `boundary_rules` via SQLite `GLOB`; output rows = `{from_path, to_path, rule_name, action}`. **No new CLI flag** — `query --recipe boundary-violations [--format sarif]` is the surface (moat-A clean: recipe consumes config-as-data; SARIF mode handles CI gate). Element-type / layer-ordering shapes (`eslint-plugin-boundaries` / Nx flavors) are user-side compositions on top of directional rules; deferred to v1.x as a sugar layer if user demand emerges (compiles down to A's directional rules at index time; backwards-compat preserved) | S | +| 1.6 | **Type members consumed by external files** | `type_members` + `imports.specifiers` (JSON) | Recipe (`unused-type-members`) — needs JSON-extraction predicate on specifiers | S | +| 1.7 | **Mermaid / D2 / Graphviz output** | `dependencies` + `calls` already shape into edges; SARIF / annotations formatters demonstrate the formatter pattern | New `--format mermaid` formatter (sibling of SARIF in `output-formatters.ts`) **with bounded-input contract**: input must come from `impact` engine, a `LIMIT N`-shipped recipe (e.g. `fan-in`, `fan-out`), or ad-hoc SQL with explicit `LIMIT ≤ 50`. Unbounded inputs error with a scope-suggestion message naming the recipe + edge count + `LIMIT`/`--via`/`WHERE` knobs. Auto-truncation is out of scope — silent subset selection would be a **verdict-shaped affordance** masquerading as an output mode (violates moat A). Hairballed Mermaid renders as garbage in chat clients (MCP / Cursor / Slack); a clear error pointing at how to scope is the better DX. | S | +| 1.8 | **More MCP resources** | Schema, recipes, skill already exposed via `resource-handlers.ts` | Add `codemap://files/{path}` (file shape — symbols, imports, exports, coverage) and `codemap://symbols/{name}` (LSP-like reads) | S | +| 1.9 | **Local recipe-recency tracking** | `query_baselines` precedent (user-data table excluded from `dropAll()`) | New `recipe_recency(recipe_id PK, last_run_at, run_count)` table; reconciler at MCP / HTTP request boundary writes the row. **Rolling 90-day retention** — entries older than `last_run_at + 90d` pruned per reindex (caps unbounded growth). **Opt-out** via `codemap.config.ts` `recipe_recency: false` — skips the reconciler entirely; table stays empty. Surfaces in `--recipes-json` as `{recipe_id, last_run_at, run_count_90d}` so agents see recently-used recipes ranked first. **Local-only** — no upload primitive shipped or planned (see § 3 "No telemetry upload" floor). Naming note: not "telemetry" — the word carries upload / aggregation / surveillance connotations that don't match the actual capability (purely local recency tracking, like `query_baselines`). | M | +| 1.10 | **Symbol-rename dry-run preview** | `calls` (callers) + `symbols.line_start` / `line_end` (locations) | Bundled recipe `rename-preview.sql` with **parameter substitution** (new infra: `?`-placeholder binding via `db.ts`'s prepared-statement pattern); `--format diff` output mode (sibling of `--format mermaid` per item 1.7) converts rows to unified diff. **No new verb** — `query --recipe rename-preview --params old=foo,new=bar --format diff` is the surface. Moat-A-aligned (SQL is the API; rename's implicit choices — visibility filter, type-only re-exports, test files, aliases — live in reviewable recipe SQL, not argv). Effort drops M → S | M | **Aggregate**: ~10 first-class agent-facing capabilities sitting in unwritten JOINs / unwritten formatters. Same multiplicative effect as C.11's three bundled recipes. From 5bdd0cafcc6a2d9c5f69c5d020fd429839c93697 Mon Sep 17 00:00:00 2001 From: Sutu Sebastian Date: Mon, 4 May 2026 14:56:48 +0300 Subject: [PATCH 20/20] =?UTF-8?q?docs(research):=20=C2=A7=201.1,=201.6,=20?= =?UTF-8?q?1.8=20sanity=20sharpening=20(gotchas=20+=20envelopes)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Grill-me Q14 outcome: three remaining § 1 rows had implicit gotchas the recipe author would otherwise have to discover during impl. Each row gets a small clarification — substrate unchanged, effort unchanged. § 1.1 components-touching-deprecated: - Was: "One bundled recipe (components-touching-deprecated)" - Now: explicit two-path UNION - HOOK PATH: components.hooks_used JSON overlap with @deprecated symbols (catches deprecated hooks like useDeprecatedThing) - CALL PATH: calls.caller_name IN (SELECT name FROM components) × @deprecated symbols by callee_name (catches regular deprecated functions called inside components) - Hook-only variants would ship false-negatives — recipe author needs the explicit UNION to avoid the trap. § 1.6 unused-type-members: - Was: "Recipe (unused-type-members) — needs JSON-extraction predicate" - Now: ADVISORY recipe with explicit caveat block in .md. Output is "review these" candidates, NEVER "safe to delete" — TS has multiple indirect-usage classes codemap's substrate doesn't track: - Indexed access: T['fieldName'] - keyof T - Type spreads: type X = T & {...} - Mapped types: {[K in keyof T]: ...} These produce false-positives. Recipe is useful as a candidate surfacer; agents must verify before deletion. § 1.8 more MCP resources: - Was: hand-wave "add codemap://files/{path} and codemap://symbols/ {name}" - Now: spell out disambiguation envelope (reuses {matches, disambiguation?} pattern from PR #39 show/snippet) — symbols with duplicate names across files (Component, index, default, util-name collisions) return all matches with by_kind / files / hint metadata. Plus ?in= query parameter mirroring show --in . - Without spelling this out, the implementation would have to invent disambiguation OR ship a "first match wins" gotcha. Net: each row's What's-needed cell now contains enough detail that the recipe / resource author can implement without re-deriving the JOIN structure or envelope shape. Tactical clarity layered on top of the structural decisions made in earlier grills. --- docs/research/non-goals-reassessment-2026-05.md | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/docs/research/non-goals-reassessment-2026-05.md b/docs/research/non-goals-reassessment-2026-05.md index 5d8e11ce..c85ef386 100644 --- a/docs/research/non-goals-reassessment-2026-05.md +++ b/docs/research/non-goals-reassessment-2026-05.md @@ -37,14 +37,14 @@ The data and pipeline exist. Each row needs only a recipe / formatter / verb to | # | Capability | What's already in place | What's needed | Effort | | ---- | ------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ------ | -| 1.1 | **Components calling deprecated symbols** | `components.hooks_used` + `calls` + `symbols.doc_comment LIKE '%@deprecated%'` | One bundled recipe (`components-touching-deprecated`) | XS | +| 1.1 | **Components calling deprecated symbols** | `components.hooks_used` + `calls` + `symbols.doc_comment LIKE '%@deprecated%'` | One bundled recipe `components-touching-deprecated` — UNION of two paths: (1) **hook path** — `components.hooks_used` JSON overlap with `symbols WHERE doc_comment LIKE '%@deprecated%'` (catches deprecated _hooks_ like `useDeprecatedThing`); (2) **call path** — `calls.caller_name IN (SELECT name FROM components)` × `symbols WHERE doc_comment LIKE '%@deprecated%' AND name = calls.callee_name` (catches deprecated regular functions called inside components). Hook-only variants ship false negatives | XS | | 1.2 | **Exports never imported anywhere** | `exports` LEFT JOIN `imports` (by `source` resolution); `exports.re_export_source` column exists for re-export chain handling | Recipe (`unimported-exports`) — re-export chains need a JOIN through `re_export_source` to avoid false positives on barrel-only exports | S | | 1.3 | **Cyclomatic complexity per symbol** | `calls.caller_scope` already aggregates per-symbol. (Earlier draft claimed AST node-counting was already in place; corrected per § 8 — it isn't.) | Extend the AST walker in `src/parser.ts` to count branching nodes per symbol; add `complexity REAL` column on `symbols`; ship `high-complexity-untested` recipe | M | | 1.4 | **Refactor risk ranking** | `dependencies` (fan-in) + `coverage` (test coverage) | Recipe `refactor-risk-ranking` with formula `(fan_in + 1) × (100 - COALESCE(coverage_pct, 0))`. The `+1` ensures orphans (fan_in=0) score on coverage alone (untested orphans = high risk; tested orphans = candidates for deletion review, not refactor). `COALESCE(coverage_pct, 0)` treats unmeasured coverage as untested (NULL coverage drops rows from `ORDER BY` otherwise). Bundled `refactor-risk-ranking.md` ships a caveat — formula is a starting point; users tune via project-local recipe override at `/.codemap/recipes/refactor-risk-ranking.sql`. Suggested axes for tuning: log-scale `fan_in` for hub-heavy codebases (`LOG(fan_in + 1) * 30`), visibility weight (if `@public`/`@internal`/`@beta` JSDoc tags are used consistently), LOC weight (if test-density varies across files). Linear-in-fan_in and equal-weight-on-orphans are accepted v1 trade-offs vs over-design. | XS | | 1.5 | **Boundary violations (config-driven)** | `dependencies` table + Zod config substrate (`src/config.ts`) + glob-matching primitives in `validate-engine` + index-time reconciler pattern (per `recipe_recency` item 1.9) + SARIF output formatter | New `boundaries: [{name, from_glob, to_glob, action: "deny"\|"allow", except_self?}]` field in `codemap.config.ts` (directional rules — Shape A). New `boundary_rules` table populated at index time from config (mirrors `recipe_recency` reconciler). Bundled recipe `boundary-violations.sql` JOINs `dependencies` × `boundary_rules` via SQLite `GLOB`; output rows = `{from_path, to_path, rule_name, action}`. **No new CLI flag** — `query --recipe boundary-violations [--format sarif]` is the surface (moat-A clean: recipe consumes config-as-data; SARIF mode handles CI gate). Element-type / layer-ordering shapes (`eslint-plugin-boundaries` / Nx flavors) are user-side compositions on top of directional rules; deferred to v1.x as a sugar layer if user demand emerges (compiles down to A's directional rules at index time; backwards-compat preserved) | S | -| 1.6 | **Type members consumed by external files** | `type_members` + `imports.specifiers` (JSON) | Recipe (`unused-type-members`) — needs JSON-extraction predicate on specifiers | S | +| 1.6 | **Type members consumed by external files** | `type_members` + `imports.specifiers` (JSON) | **Advisory recipe** `unused-type-members` (output is a "review these" list, NEVER a "safe to delete" list). Needs JSON-extraction predicate on specifiers (`json_each` / `json_extract`). Bundled `.md` warns about indirect-usage false-positive classes that codemap's substrate doesn't track: indexed access (`T['fieldName']`), `keyof T`, type spreads (`type X = T & {…}`), mapped types (`{[K in keyof T]: …}`). Output rows are candidates for manual review | S | | 1.7 | **Mermaid / D2 / Graphviz output** | `dependencies` + `calls` already shape into edges; SARIF / annotations formatters demonstrate the formatter pattern | New `--format mermaid` formatter (sibling of SARIF in `output-formatters.ts`) **with bounded-input contract**: input must come from `impact` engine, a `LIMIT N`-shipped recipe (e.g. `fan-in`, `fan-out`), or ad-hoc SQL with explicit `LIMIT ≤ 50`. Unbounded inputs error with a scope-suggestion message naming the recipe + edge count + `LIMIT`/`--via`/`WHERE` knobs. Auto-truncation is out of scope — silent subset selection would be a **verdict-shaped affordance** masquerading as an output mode (violates moat A). Hairballed Mermaid renders as garbage in chat clients (MCP / Cursor / Slack); a clear error pointing at how to scope is the better DX. | S | -| 1.8 | **More MCP resources** | Schema, recipes, skill already exposed via `resource-handlers.ts` | Add `codemap://files/{path}` (file shape — symbols, imports, exports, coverage) and `codemap://symbols/{name}` (LSP-like reads) | S | +| 1.8 | **More MCP resources** | Schema, recipes, skill already exposed via `resource-handlers.ts` | Add `codemap://files/{path}` (file shape — symbols, imports, exports, coverage rolled up by file) and `codemap://symbols/{name}` (LSP-like reads). Both reuse the existing `{matches, disambiguation?}` envelope from PR #39 `show` / `snippet` — symbols with the same name across files (very common: `Component`, `index`, `default`, util-name collisions) return all matches with a `disambiguation` block (`{n, by_kind, files, hint}`); single-match returns `{matches: [{...}]}`. Filter via `?in=` query parameter (mirrors `show --in ` flag) | S | | 1.9 | **Local recipe-recency tracking** | `query_baselines` precedent (user-data table excluded from `dropAll()`) | New `recipe_recency(recipe_id PK, last_run_at, run_count)` table; reconciler at MCP / HTTP request boundary writes the row. **Rolling 90-day retention** — entries older than `last_run_at + 90d` pruned per reindex (caps unbounded growth). **Opt-out** via `codemap.config.ts` `recipe_recency: false` — skips the reconciler entirely; table stays empty. Surfaces in `--recipes-json` as `{recipe_id, last_run_at, run_count_90d}` so agents see recently-used recipes ranked first. **Local-only** — no upload primitive shipped or planned (see § 3 "No telemetry upload" floor). Naming note: not "telemetry" — the word carries upload / aggregation / surveillance connotations that don't match the actual capability (purely local recency tracking, like `query_baselines`). | M | | 1.10 | **Symbol-rename dry-run preview** | `calls` (callers) + `symbols.line_start` / `line_end` (locations) | Bundled recipe `rename-preview.sql` with **parameter substitution** (new infra: `?`-placeholder binding via `db.ts`'s prepared-statement pattern); `--format diff` output mode (sibling of `--format mermaid` per item 1.7) converts rows to unified diff. **No new verb** — `query --recipe rename-preview --params old=foo,new=bar --format diff` is the surface. Moat-A-aligned (SQL is the API; rename's implicit choices — visibility filter, type-only re-exports, test files, aliases — live in reviewable recipe SQL, not argv). Effort drops M → S | M |