Skip to content
Merged
Show file tree
Hide file tree
Changes from 16 commits
Commits
Show all changes
23 commits
Select commit Hold shift + click to select a range
61c7748
docs: add substrate tiers 1–6 rollout plan
SutuSebastian May 19, 2026
79c69fc
feat: add call-shape metadata to calls table (schema 28)
SutuSebastian May 19, 2026
8fb98b2
feat: add async/return-type columns on symbols (schema 29)
SutuSebastian May 19, 2026
5be2260
feat: add dynamic_imports table and extractor (schema 30)
SutuSebastian May 19, 2026
026889b
feat: add files.is_barrel and has_side_effects flags (schema 31)
SutuSebastian May 19, 2026
c086bdb
feat: add re-exported resolution_kind to bindings (schema 32)
SutuSebastian May 20, 2026
3b3349c
feat: side-effect import_specifiers rows + import_id FK (schema 33)
SutuSebastian May 20, 2026
52fc02f
feat: JSX + behavioral substrate tables (schema 34)
SutuSebastian May 20, 2026
0a69232
docs: mark substrate tiers 1–6 rollout complete
SutuSebastian May 20, 2026
f9377b7
chore: add changeset for substrate tiers 1–6 (minor)
SutuSebastian May 20, 2026
d65d269
docs: retire substrate tiers 1–6 rollout plan
SutuSebastian May 20, 2026
1b5c1dc
docs: align glossary and ship status with substrate tiers 1–6
SutuSebastian May 20, 2026
7852ba2
perf(insert): batch async_calls and try_catch inserts
SutuSebastian May 20, 2026
b9ebdb8
perf(insert): batch imports in insertImportsWithSpecifiers
SutuSebastian May 20, 2026
12b40c0
perf(insert): batch jsx_attributes inserts
SutuSebastian May 20, 2026
93b2659
test(fixtures): enrich minimal corpus for full substrate coverage
SutuSebastian May 20, 2026
268de25
chore(deps): bump oxc, zod, oxlint, and dev toolchain
SutuSebastian May 20, 2026
9fe9c11
fix(extractors): address CodeRabbit fact-checked review items
SutuSebastian May 20, 2026
75a1a65
docs: sync architecture schema version and recipe frontmatter
SutuSebastian May 20, 2026
377de85
fix(ci): scope unit tests to src and rebaseline perf for schema 34
SutuSebastian May 20, 2026
4473ca4
fix(ci): scope tests to ./src not **/src/**
SutuSebastian May 20, 2026
694eb1f
fix(qa): close read-only query gap and add regression coverage
SutuSebastian May 20, 2026
3fd7c93
test(index-engine): restore console spies in finally blocks
SutuSebastian May 20, 2026
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
34 changes: 34 additions & 0 deletions .changeset/substrate-tiers-1-6.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
---
"@stainless-code/codemap": minor
---

Substrate tiers 1–6 remainder (excludes C.9 / `files.is_entry`). **Schema bump** `SCHEMA_VERSION` 27 → **34** — first run after upgrade auto-rebuilds `.codemap/index.db` via the existing version-mismatch path.

**Tier 1 — call + import precision**

- `calls.{args_count,is_method_call,is_constructor_call,is_optional_chain}`; constructor vs call dedup key fix
- `symbols.{return_type,is_async,is_generator}`
- Side-effect `import_specifiers` rows (`kind='side-effect'`) + `import_id` FK to `imports`

**Tier 2 — bindings**

- `bindings.resolution_kind='re-exported'` when resolution walks a re-export chain

**Tier 3 — JSX**

- New tables `jsx_elements` / `jsx_attributes`; extractor with per-file parent linking post-pass

**Tier 5 — behavioral**

- New tables `async_calls`, `try_catch`, `decorators`, `jsdoc_tags`; context stack for `in_loop` / `in_try`

**Tier 6 — module graph (no entry points)**

- `dynamic_imports` table + extractor
- Post-pass `files.is_barrel` and parse-time `files.has_side_effects`

**Recipes + goldens:** `find-call-sites` (extended), `find-async-functions`, `find-dynamic-imports`, `find-barrel-files`, `find-side-effect-files`, `find-re-exported-bindings`, `find-side-effect-imports`, `find-jsx-usages`, `find-await-in-loop`, `find-swallowed-errors`, `find-decorator-usage`, `find-throws-jsdoc`.

**Out of scope:** C.9 plugin layer (`files.is_entry`, reachability-from-entry); tiers 7–13.

**Migration:** No in-place DDL — rebuild on schema mismatch preserves user-data tables (`coverage`, `query_baselines`, `recipe_recency`). Re-run `codemap --full` (or any index) after upgrade.
97 changes: 74 additions & 23 deletions docs/architecture.md
Original file line number Diff line number Diff line change
Expand Up @@ -193,15 +193,17 @@ All tables use `STRICT` mode. Tables marked with `WITHOUT ROWID` store data dire

### `files` — Every indexed file (`STRICT`)

| Column | Type | Description |
| ------------- | ------- | ---------------------------------------------- |
| path | TEXT PK | Relative path from project root |
| content_hash | TEXT | SHA-256 hex — see **Fingerprints** at § Schema |
| size | INTEGER | File size in bytes |
| line_count | INTEGER | Total lines |
| language | TEXT | `ts`, `tsx`, `css`, `md`, etc. |
| last_modified | INTEGER | File mtime (epoch ms) |
| indexed_at | INTEGER | When this row was written |
| Column | Type | Description |
| ---------------- | ------- | -------------------------------------------------------------------- |
| path | TEXT PK | Relative path from project root |
| content_hash | TEXT | SHA-256 hex — see **Fingerprints** at § Schema |
| size | INTEGER | File size in bytes |
| line_count | INTEGER | Total lines |
| language | TEXT | `ts`, `tsx`, `css`, `md`, etc. |
| last_modified | INTEGER | File mtime (epoch ms) |
| indexed_at | INTEGER | When this row was written |
| is_barrel | INTEGER | 1 when every export is a re-export and no local value symbols exist |
| has_side_effects | INTEGER | 1 when module-level calls or assignments were detected at parse time |

### `symbols` — Functions, constants, classes, interfaces, types, enums (`STRICT`)

Expand All @@ -228,21 +230,28 @@ All tables use `STRICT` mode. Tables marked with `WITHOUT ROWID` store data dire
| body_line_count | INTEGER | `line_end - line_start + 1` for function-shaped symbols; NULL for non-functions |
| param_count | INTEGER | Parameter count for function-shaped symbols; NULL otherwise |
| nesting_depth | INTEGER | Max conditional/loop/ternary nesting inside the body; NULL for non-functions |
| return_type | TEXT | Stringified return type for function-shaped symbols; NULL when unannotated or N/A |
| is_async | INTEGER | 1 for async function-shaped symbols (`function`, `method`, arrow-assigned `function` kind) |
| is_generator | INTEGER | 1 for generator function-shaped symbols |

### `calls` — Function-scoped call edges, deduped per file (`STRICT`)

| Column | Type | Description |
| ------------ | ---------- | --------------------------------------------------------------------------------------------------------------------------------- |
| id | INTEGER PK | Auto-increment row id |
| file_path | TEXT FK | References `files(path)` ON DELETE CASCADE |
| caller_name | TEXT | Name of the calling function/method |
| caller_scope | TEXT | Dot-joined scope path (e.g. `UserService.run`). Anonymous scopes encode as `$anon_<localId>` to avoid sibling-callback collisions |
| callee_name | TEXT | Name of the called function, `obj.method` / `obj.foo.bar` for member chains (recursive flatten), `this.method` for self |
| line_start | INTEGER | 1-based line of the callee identifier token (per [R.6]) |
| column_start | INTEGER | 0-based byte column of the callee token |
| column_end | INTEGER | One-past-last column |

Edges are deduped per (caller_scope, callee) per file: if `foo` calls `bar` three times in the same file, only one row is stored. Same-named methods in different classes get distinct `caller_scope` values. Module-level calls (outside any function) are excluded — only function-scoped calls are tracked.
| Column | Type | Description |
| ------------------- | ---------- | --------------------------------------------------------------------------------------------------------------------------------- |
| id | INTEGER PK | Auto-increment row id |
| file_path | TEXT FK | References `files(path)` ON DELETE CASCADE |
| caller_name | TEXT | Name of the calling function/method |
| caller_scope | TEXT | Dot-joined scope path (e.g. `UserService.run`). Anonymous scopes encode as `$anon_<localId>` to avoid sibling-callback collisions |
| callee_name | TEXT | Name of the called function, `obj.method` / `obj.foo.bar` for member chains (recursive flatten), `this.method` for self |
| line_start | INTEGER | 1-based line of the callee identifier token (per [R.6]) |
| column_start | INTEGER | 0-based byte column of the callee token |
| column_end | INTEGER | One-past-last column |
| args_count | INTEGER | Argument count; NULL when a spread argument is present |
| is_method_call | INTEGER | 1 when callee is a member expression (`obj.method()`) |
| is_constructor_call | INTEGER | 1 for `new Foo()` (`NewExpression`) |
| is_optional_chain | INTEGER | 1 when the call uses optional chaining (`?.`) |

Edges are deduped per (caller_scope, callee, call vs constructor) per file: if `foo` calls `bar` three times in the same file, only one row is stored. `foo()` and `new Foo()` with the same callee name remain distinct rows. Same-named methods in different classes get distinct `caller_scope` values. Module-level calls (outside any function) are excluded — only function-scoped calls are tracked.

### `type_members` — Properties and methods of interfaces and object-literal types (`STRICT`)

Expand Down Expand Up @@ -314,8 +323,9 @@ Edges are deduped per (caller_scope, callee) per file: if `foo` calls `bar` thre
| column_end | INTEGER | One-past-last column |
| imported_name | TEXT | Original exported name (or `default` / `*`) |
| local_name | TEXT | Local binding name (different from `imported_name` for `import { foo as bar }`) |
| kind | TEXT | `named` / `default` / `namespace` |
| kind | TEXT | `named` / `default` / `namespace` / `side-effect` |
| is_type_only | INTEGER | 1 if this specifier is `type`-only |
| import_id | INTEGER FK | Parent `imports.id`; populated for all specifier rows including side-effect |

### `scopes` — Lexical scope graph (`STRICT, WITHOUT ROWID`)

Expand Down Expand Up @@ -355,7 +365,7 @@ Per [R.12]. One row per non-`member`-kind `references` row. Resolved in a single
| ------------------ | ------- | ------------------------------------------------------------------------------- |
| reference_id | INTEGER | PK + FK → `references(id)` CASCADE |
| resolved_symbol_id | INTEGER | FK → `symbols(id)` SET NULL. NULL for `is_external=1` / `global` / `unresolved` |
| resolution_kind | TEXT | `same-file` / `imported` / `global` / `unresolved` |
| resolution_kind | TEXT | `same-file` / `imported` / `re-exported` / `global` / `unresolved` |
| is_external | INTEGER | 1 when the import target isn't in the indexed set (e.g. `react`, `lodash`) |

### `function_params` — Typed parameters per function/method (`STRICT`)
Expand Down Expand Up @@ -421,6 +431,47 @@ SCCs of size ≥ 2 from `dependencies`, plus size-1 SCCs with a self-edge. Compu
| cycle_id | INTEGER | Per-PR auto-numbered cycle id (shared across cycle members) |
| cycle_size | INTEGER | Number of files in the cycle |

### `dynamic_imports` — Dynamic `import()` sites (`STRICT`)

| Column | Type | Description |
| -------------- | ---------- | --------------------------------------------------------------------- |
| id | INTEGER PK | Auto-increment row id |
| file_path | TEXT FK | Containing file |
| line_start | INTEGER | 1-based line of the module specifier token |
| column_start | INTEGER | 0-based column of the specifier start |
| source_kind | TEXT | `literal` / `template` / `expression` |
| source_text | TEXT | Specifier text (literal value, template source, or expression source) |
| resolved_path | TEXT | Project-relative path when `source_kind = 'literal'` and resolvable |
| in_async_fn | INTEGER | 1 when the import sits inside an async function body |
| scope_local_id | INTEGER | Enclosing scope (joins `scopes.local_id`; `0` = module) |

### `jsx_elements` / `jsx_attributes` — JSX substrate (`STRICT`)

Every JSX element and attribute in `.tsx`/`.jsx` files. `parent_element_id` is filled in a post-insert pass within the file. Fragments use `is_fragment = 1` and empty `component_name`.

| Column (elements) | Type | Description |
| ----------------- | ---------- | -------------------------------------- |
| component_name | TEXT | Tag name (`ProductCard`, `article`, …) |
| is_self_closing | INTEGER | 1 for `<Foo />` |
| is_fragment | INTEGER | 1 for `<>…</>` |
| is_lowercase | INTEGER | 1 for native HTML tags |
| parent_element_id | INTEGER FK | Parent element row |
| children_count | INTEGER | Direct JSX child element count |

| Column (attributes) | Type | Description |
| ------------------- | ---- | ---------------------------------------------------------- |
| element_id | FK | Owning `jsx_elements.id` |
| value_kind | TEXT | `string` / `expression` / `boolean` / `spread` / `element` |

### `async_calls` / `try_catch` / `decorators` / `jsdoc_tags` — Behavioral substrate (`STRICT`)

| Table | Flagship signal |
| ------------- | ---------------------------------------------------------------------- |
| `async_calls` | `AwaitExpression` sites with `in_loop` / `in_try` context stack |
| `try_catch` | `TryStatement` shape + `catch_logs_only` / `catch_rethrows` heuristics |
| `decorators` | Decorator name + `target_kind`; `target_symbol_id` linked post-insert |
| `jsdoc_tags` | Structured tags (`@param`, `@throws`, …) per symbol from `doc_comment` |

### `runtime_markers` — Operational signals (`STRICT`)

Every `console.*` call, `debugger` statement, `throw` statement, and `process.env.X` access. Powers `find-leftover-console` + `env-var-audit`.
Expand Down
Loading
Loading