Conversation
VSCode-side follow-up to the Rust commits 791022b (RAG removal) and ad22458 (Sessions removal). MCP shape no longer carries `rag` / `chunk_refs` / `fetch_chunks` / session_* / usage_stats — ext must stop wiring them. Removed ------- - `src/daemon/rag-flags.ts` (RagSettings + ragSpawnFlags) - `src/daemon/rag-settings.ts` (readRagSettings/writeRagEnabled/etc.) - `tests/rag-flags.test.ts` - RAG fields/methods on McpClient (`ragSettings`, `ragDownloading`, `setRagSettings`, `ragSettingsSnapshot`, `scanRagMarkers`, `fetchChunks`, `usageStats`, model-download endpoint wait) - RAG fields on LspClient + ragSpawnFlags splat in spawn args - RAG settings/commands in extension.ts (`readRagSettings`, `watchRagSettings`, the daemon auto-restart-on-rag-change wiring) - Command palette entries: `Standardoc.rag.toggle`, `.rag.switchEmbedder`, `.rag.rebuild`, `.showTokenSavings`, `.resetTokenSavings` - StatusBar `RagSettings?` parameter (the `· RAG (embedder)` suffix) - commands-render `formatUsageStats` + `UsageStatsJson` - skill-template sections for `fetch_chunks`, `usage_stats`, `session_save` / `session_list` / `session_get` / `session_sync_in` / `session_sync_out` + the prose-retrieval workflow recipe + the session-handoff workflow recipe - mcp/types `CurrentRevisionJson.rag` field - README sections for RAG settings + RAG commands + token-savings Preserved --------- - `enrichment_description` on `SymbolContextJson` (the enrichments table is kept Rust-side as scaffolding for the future AI layer; ext just renders the field when it's `null` today) Settings -------- - `standardoc.ragEnabled`, `standardoc.ragEmbedder` removed from `package.json` (users' workspace settings with these keys will be silently ignored — VSCode warns about unknown contributed settings but doesn't error). Tests ----- - typecheck clean - 180/180 bun tests pass - `bun run build` bundles cleanly (1.39 MB, 379 modules) Cleanup motivation: the Rust commits made the MCP server stop serving `rag` / `fetch_chunks` / sessions / usage_stats. The extension would otherwise spawn `--rag` flags the daemon rejects and call tools that no longer exist.
Two tiny followups from the A1/A3 cleanup audit. ir/signature.rs tests --------------------- The compact_rust_tokens tests embedded the deleted RAG types as literal strings to exercise tokenization. Inoffensive at compile time but the names linger as dead concepts in greps: - `standardoc_core::RagWatcherHandle` → `standardoc_core::WatcherHandle` - `Arc<dyn Embedder>` → `Arc<dyn Logger>` No semantic change — the tokenizer doesn't care which identifier it sees, the test verifies whitespace-stripping shape only. cli/main.rs ----------- `confirm_destructive` was only ever called from `cmd_reset_usage` (deleted with A1). Drop the dead helper. cargo test --workspace green (714 + 110 + 95 + 13 + …).
Refonte du playground en visualiseur de graphe sémantique style Blueprint
Unreal Engine.
- payload: fetch_graph gagne projects[], language + project_id sur les
symboles ; barre accent langage par chip (palette GitHub linguist)
- frames projet colorées par kind, imbriquées par rel_path
- semantic zoom : LodTier {Project, Module, Chip}, seuils 0.16 / 0.46
- layout en couches de dépendance (Sugiyama) à tous les tiers, câbles
de dépendance Bézier persistants aux tiers Project/Module
- navigation : double-clic zoom-to-fit, minimap, breadcrumb live
- légende overlay repliable, profiler HUD déplacé bottom-left
Both variants were defined in the EdgeKind enum + persisted via SQL CHECK constraint + parsed in MCP handler + colored in viz palette, but no extractor ever emitted them in prod. `query::dependents` match treated them as inbound deps but the branch was always cold. R-Sem-1 of the IR refactor — opens the path for R-Sem-2 (promote LocalDeclKind to RawSymbol.decl_kind). Note: residual cleanup in viz palette/payload/playground will land with the V1->V5 viz commit since those files carry uncommitted V1->V5 work.
…ary, find_symbol_fqdns, list_symbol_fqdns
Four additive MCP tools tailored for agent navigation patterns:
- `get_code(fqdn)`: agent variant of `get_body` with strip_attrs=true and
strip_inline_comments=true by default. Returns pure code, no doc /
attribute / inline-comment dilution. The leading description is already
surfaced by `get_context.context.document_description` - duplicating it
in the body is wasted context.
- `get_context_summary(fqdn)`: cheap mapping probe. Returns the symbol
header plus per-direction neighbor counts (callers/callees/imports/
imported_by/dependents/tests) instead of the full neighbor lists.
Designed as a first-pass before deciding whether `get_context` is worth
the round-trip.
- `find_symbol_fqdns(query, ...)`: FTS5 search returning `[{fqdn, kind}]`
per match instead of the full RawSymbol. Same filters and did_you_mean
envelope as `find_symbol`. Default discovery probe.
- `list_symbol_fqdns(...)`: filter-only listing returning `{items:
[{fqdn, kind}], next_cursor}`. Same pagination as `list_symbols`.
R-A1 / R-A4 / R-A5 of the consumer-aware refactor (Vague A in the audit
plan). Zero breaking - existing tools unchanged.
…add relative_to projection on *_fqdns
Phase 1 of the FQDN audit (R-FQ-1 + R-FQ-4).
R-FQ-1 (centralize path-conventions):
Move per-language compute_module_path bodies into a single helper
walk_core::compute_module_path(conventions, package_relative) driven
by a LanguagePathConventions struct (extensions / root_aliases /
strip_src_prefix). Each lang-provider now exposes its conventions
as a `const` and delegates its compute_module_path to the helper.
Side-effect cleanup: Lua's compute_module_path no longer returns a
dot-separated intermediate form that the caller had to remap to
`::`; it returns `::`-joined directly. The legacy
`.replace('.', "::")` at the FQDN composition site is now a no-op
and was dropped. Same simplification for TS where the helper used
to return `/`-separated and the caller did `.replace('/', "::")`.
No FQDN output change at the symbol level (all four languages
already emitted `crate_or_pkg::module::name` shapes); pure DRY.
R-FQ-4 (relative_to projection):
Add an optional `relative_to: Option<String>` field to
FindSymbolFqdnsParams + ListSymbolFqdnsParams. When set, matching
FQDNs are returned in relative form (leading `::` marker, prefix
stripped). FQDNs that don't share the anchor pass through verbatim.
Boundary-aware: `foo::bar` does NOT shorten `foo::barista::x`.
Helper `relative_fqdn(fqdn, anchor) -> String` carries five unit
tests covering prefix/self-match/no-match/empty-anchor/boundary
edge cases.
The two MCP tool descriptions advertise the new param so a forgetful
agent re-discovers it.
Phase 2 K (Kind hierarchy refonte) foundation. Adds the refined
declaration-kind axis alongside the coarse `Kind` bucket, with storage
round-trip and schema bump. All lang-providers leave `decl_kind: None`
at this step — population per language lands in K-Step-B.
- standardoc-ir::kinds::DeclKind: 22 built-in variants covering
Module/Namespace/Crate, Struct/Enum/Union/Class/Interface/TypeAlias,
Function/Method/Constructor/Getter/Setter, Const/Static/Var/Field/
EnumVariant, DeclarativeMacro/ProcMacro/Decorator — plus
`Custom { lang, tag }` escape hatch. `Impl` intentionally absent
(Rust impl blocks are wrappers, not symbols); `Trait` collapses
into `Interface`.
- RawSymbol gains `decl_kind: Option<DeclKind>` with
`skip_serializing_if = "Option::is_none"` — legacy rows deserialize
to `None`.
- storage: `symbols.decl_kind TEXT` column (nullable, no CHECK —
values validated Rust-side via `decl_kind_to_sql_text` /
`decl_kind_from_sql_text`). Flat encoding: built-ins as snake_case
(`"method"`, `"enum_variant"`, …), Custom as
`"custom:<lang>:<tag>"`. Wired into `insert_symbol` UPSERT and
every SELECT path (`SYMBOL_COLUMNS` + 4 explicit selects).
- SUPPORTED_SCHEMA_VERSION bumped 0 → 1; cold-start triggers
destructive rebuild on stale DBs (the index is a derived cache).
1630 tests passing, 0 failed.
…ctor
Phase 2 K-Step-B (1/4) — first language to gain DeclKind coverage.
All Rust-emitted symbols now carry a `Some(DeclKind::…)` matching
the syn item that produced them:
- `extract.rs` file-module symbol → `Module`
- `extract_fn` (top-level fn) → `Function`
- `extract_struct` → `Struct` (fields → `Field`)
- `extract_enum` → `Enum` (variants → `EnumVariant`)
- `extract_union` → `Union`
- `extract_type_alias` → `TypeAlias`
- `extract_trait` → `Interface` (trait collapses
per Phase 2 §4.2)
- trait-method items → `Method`
- impl-method items → `Method`
- `extract_const` / `extract_static` → `Const` / `Static`
- `extract_macro_def` → `DeclarativeMacro`
- `extract_use.rs` re-export → stays `None` (phantom symbol,
target kind unknown without
cross-file resolution)
`type_def_symbol` / `value_def_symbol` helpers gain a typed
`decl_kind: DeclKind` parameter, threaded through 6 call sites.
8 new unit tests in `rust::walk::tests` verify the mapping, including
the "impl blocks emit methods, not impl-symbols" invariant first
documented in K-Step-A's MCP investigation.
1638 tests passing, 0 failed.
Phase 2 K-Step-B (2/4) — TypeScript joins Rust in DeclKind coverage.
17 emission sites in ts/walk.rs + the file-module symbol in
ts/extract.rs now carry a `Some(DeclKind::…)`:
- file-module symbol → Module
- extract_fn_decl → Function
- extract_class_inner → Class
- extract_constructor → Constructor
- extract_class_prop /
extract_private_prop → Field
- extract_method /
extract_private_method → Method / Getter / Setter
(dispatched on swc MethodKind)
- extract_interface_decl → Interface
interface property → Field
interface method → Method
interface getter / setter → Getter / Setter
- extract_enum_decl → Enum
enum_member → EnumVariant
- extract_type_alias_decl → TypeAlias
- extract_var_decl → Function if arrow/fn expr, else
Const / Var per VarDeclKind
- process_export_default_decl → Function or Interface (variant-aware)
`ts/ffi_tagger.rs` synthetic FFI imports stay `None` for the same
reason Rust `re_export` does — these are binding-discovery records,
not source-level declarations; the target's decl_kind is unknown
without cross-file resolution.
8 new unit tests in `ts::walk::tests` cover all variants including
the const-arrow-function-becomes-Function carve-out.
1646 tests passing, 0 failed.
Phase 2 K-Step-B (3/4) — C joins Rust and TS. 7 emission sites in
c/walk.rs + file-module symbol in c/extract.rs now carry a
`Some(DeclKind::…)`:
- file-module symbol → Module
- fn definition (`fn`) → Function
- fn prototype (`fn_decl`) → Function
- global var (`global`) → Static (C globals have static
storage duration regardless of
the `static` keyword — that only
toggles linkage)
- struct → Struct
- union → Union
- typedef → TypeAlias
- enum + enumerators → Enum + EnumVariant (both inline
`enum { … }` and `typedef enum
{ … } Alias` paths)
- #define object-like macro → DeclarativeMacro
- #define function-like macro → DeclarativeMacro
(C preprocessor maps to the same
DeclKind as Rust `macro_rules!` —
both are textual substitution;
`language_kind` preserves the
object-vs-fn distinction)
`push_symbol` + `emit_struct_like` helpers gain a typed
`decl_kind: DeclKind` parameter, threaded through every caller.
5 new unit tests in `c::tests` cover fn def + prototype, struct,
union, enum + variants, typedef, both macro flavors. Global-var
emission is not currently exercised by the C extractor (out of
scope, pre-existing limitation) — the wiring is in place for the
day it lands.
1651 tests passing, 0 failed.
Phase 2 K-Step-B (4/4) — Lua closes the foundation. All 5 Lua
extractor emission sites now carry a `Some(DeclKind::…)`:
- file-module symbol → Module
- `local function foo()` → Function
- `function foo()` / `function
M.foo()` → Function
- `function M:bar()` → Method (Lua's `:` colon-call form
desugars to `self`-first arg —
consistent with Rust impl methods)
- `local x = …` → Var (matches the LocalDeclKind::Let
collapse policy in §4.5; mutability
flag deferred to Phase 2 polish)
- `M.foo = function() end` → Function (extract_assignment emits
as Kind::Function when RHS is a
function literal, so DeclKind
matches the coarse axis)
`lua/ffi_tagger.rs` synthetic FFI imports stay `None` for the same
reason Rust re_export and TS FFI do — these are binding-discovery
records, not source-level declarations.
7 new unit tests in `lua::extract::tests` cover all 6 emission
shapes (file-module, local fn, global fn, dotted fn, colon-method,
local var, table-assigned fn).
1658 tests passing, 0 failed.
This closes Phase 2 K-Step-B for all four extractor languages
(Rust + TypeScript + C + Lua). DeclKind is now populated at the
source for every symbol that survives extraction.
Phase 2 K-Step-C — finalize the impl-block refonte. R-K-6 (drop
impl-block symbol emission) was already in place since pre-Phase-2;
this step lands R-K-4 (`implements_trait`) and R-K-5 (`receiver_type`)
on `RawSymbol`, persists them through storage, and populates them
for the Rust extractor.
IR (`standardoc-ir::symbol`):
- `pub implements_trait: Option<String>` — when a `Method` lives
inside `impl Trait for Type { ... }`, carries the trait FQDN.
Inherent impl methods and free functions stay `None`. Per-method
mirror of the `EdgeKind::Implements` edge from Type → Trait.
- `pub receiver_type: Option<TypeRef>` — for `DeclKind::Method`
symbols, the type the method dispatches on. Rust impl method
→ impl-er type; trait method definition → the trait itself
(Self : Trait). Free functions stay `None`.
Both fields default to `None` and are `skip_serializing_if` —
legacy rows round-trip cleanly.
Storage:
- `symbols.implements_trait TEXT` and `symbols.receiver_type TEXT`
columns (both nullable, no CHECK — values are unconstrained FQDN
strings).
- `SUPPORTED_SCHEMA_VERSION` bumped 1 → 2; cold-start reset on
mismatch.
- `insert_symbol` UPSERT writes both columns; `SYMBOL_COLUMNS` +
`SymbolRowRaw` + `read_symbol_row` + `build_symbol` + the 4
explicit SELECTs all updated; `context_for_symbol` row offsets
shifted (15→17, 16→18).
Rust extractor (`extract_impl` + `extract_trait`):
- `extract_impl` impl_fn emission: `receiver_type = target_fqdn`,
`implements_trait = trait_path string` (Some for `impl Trait
for Type`, None for `impl Type`). The raw trait path is captured
once and reused — same source the existing `Implements` edge
consumes.
- `extract_trait` trait_fn emission: `receiver_type = trait_fqdn`
(Self : Trait), `implements_trait = None`.
4 new tests in `rust::walk::tests` cover all 4 shapes:
inherent impl method, trait-impl method, trait method def,
free function. The existing `decl_kind_round_trip_method` test
in `standardoc-ir` now exercises the new fields end-to-end via
serde.
TS and Lua extractors carry the schema additions but stay at
`None` for both fields — populating receiver_type for TS class
methods and Lua colon-methods is a low-cost follow-up (K polish),
not blocking K-Step-D.
1662 tests passing, 0 failed.
Phase 2 K-Step-D — coarse-kind axis clarification. The `Kind::Function`
variant served as an umbrella for free functions, methods, constructors,
getters and setters; the new `Callable` name reflects that umbrella role
and stops conflating with `DeclKind::Function` (which now stays the
fine-grained "free function" refinement).
BREAKING (wire + storage):
- IR `Kind::Function` → `Kind::Callable` (serde renames `"function"` →
`"callable"`).
- SQL `symbols.kind` CHECK constraint accepts `'callable'` instead of
`'function'`. `SUPPORTED_SCHEMA_VERSION` bumped 2 → 3 — cold-start
triggers destructive rebuild on the first boot after this lands.
- MCP `parse_kind("function")` now rejects; accepts `"callable"`.
- ext/vscode `SymbolKindJson` union updated; `formatSymbolHeader`
test asserts `"callable"`.
No legacy alias retained — Phase 2 already requires re-extraction
(schema bump) so a clean break costs nothing extra. Consumers wanting
both names can adapt at the wire boundary if needed.
Mechanical scope:
- 219 `Kind::Function` callsites renamed across 37 Rust files
- `kind_to_sql_text` / `kind_from_sql_text` updated
- 5 SQL test fixtures with literal `'function'` updated to `'callable'`
- 1 graph-viz `Kind` enum (local to that crate) renamed
- Tests asserting JSON `"kind":"function"` updated to `"callable"`
DeclKind::Function STAYS unchanged — it's the refined "free function"
variant under Kind::Callable, distinct from Method/Constructor/Getter/
Setter siblings.
1662 tests passing, 0 failed. WASM build clean. ext/vscode bun tests
all green (180/180).
…ver_type across wire layers
Surface the K-Step-A/C nullable refinement fields on every consumer of the
symbol wire: the daemon's `GraphSymbol`, the viz crate's `SymbolEntry`,
the MCP `get_context_summary` payload, and the ext/vscode `RawSymbolJson`
type. Previously they were only reachable via `get_context` (which returns
the full `RawSymbol`); `fetch_graph` and the agent-tuned summary tools
silently dropped them.
- standardoc-core: `GraphSymbol` gains `decl_kind: Option<DeclKind>`,
`implements_trait: Option<String>`, `receiver_type: Option<String>`
(the `TypeRef.display` projection — wire economy, the TypeRef has
nothing else today). `load_graph_symbols` SELECT now reads the three
nullable columns; `GraphSymbolRowRaw` and `read_graph_symbol` extend
in lockstep. New test asserts the JSON shape both when fields are
NULL (skipped via `skip_serializing_if`) and when set.
- standardoc-graph-viz: `SymbolEntry` mirrors the same three fields as
`Option<String>` (consistent with how `visibility` / `language_kind`
are already stored as raw strings — K-Step-F will match on the
snake_case values without pulling a local DeclKind enum).
- standardoc-server: `get_context_summary` JSON `symbol` block carries
the three fields. `receiver_type` is projected to `t.display` for
consistency with the `GraphSymbol` wire shape.
- ext/vscode: `RawSymbolJson` gains three optional fields; new typed
union `DeclKindJson` covers the 22 built-in `snake_case` variants
plus the `{ custom: { lang, tag } }` escape hatch (with paired
`LanguageJson` union). `formatSymbolHeader` now appends the
`decl_kind` to the coarse `kind` when present (e.g.
`kind: callable (method)`); `formatDeclKind` helper renders the
Custom variant as `custom:<lang>:<tag>`.
`find_symbol_fqdns` and `list_symbol_fqdns` are intentionally left as
`{fqdn, kind}` projections — the whole point of the `*_fqdns` variants
is a tight payload.
cargo workspace check ✓, wasm32 check ✓, tsc ✓, 1663 tests passed
(+1 new wire shape test), 180 ext/vscode bun tests passed.
…n + Rust/C first-pass classification (schema v4)
The viz pivot to "where does flow start, what does it touch?" needs the
daemon to classify each symbol as an entry-point or an internal node;
without a structured signal the renderer would have to re-derive
heuristics from name/visibility, violating the dumb-renderer contract.
This commit is the foundation layer: add the classification field,
persist it, and populate the unambiguous Rust + C cases.
- standardoc-ir: new `EntryPointKind` enum (snake_case serde) with three
variants — `BinaryMain` (`fn main` at crate root / `int main`),
`PublicApi` (deferred to a follow-up), `FfiExport` (`#[no_mangle]` in
Rust, `luaopen_*` in C). `RawSymbol.entry_point: Option<EntryPointKind>`
with `skip_serializing_if = "Option::is_none"` so legacy / internal
rows stay JSON-cheap.
- standardoc-core: schema bumps v3 → v4 (`init_v0.sql` adds nullable
`entry_point` column + SQL CHECK, `schema_meta` seed updated).
`SYMBOL_COLUMNS` const + four hand-rolled `SELECT s.…` queries gain
the column. `SymbolRowRaw` / `read_symbol_row` / `build_symbol` plumb
through. `insert_symbol` upsert handles the new column on conflict.
`context_for_symbol`'s indexed `row.get(N)` shifted to compensate
for the wider `SYMBOL_COLUMNS`. New `entry_point_to_sql_text` /
`entry_point_from_sql_text` helpers in `storage::conv`.
- lang-provider Rust: `extract_fn` now calls `classify_fn_entry_point`
— name == "main" AND parent_fqdn has no `::` → `BinaryMain` (covers
bin crates and `src/bin/*.rs` without needing to know the crate
target); `#[no_mangle]` attribute → `FfiExport`. `PublicApi`
classification needs the resolver's transitive `pub mod` chain and
is deferred.
- lang-provider C: `emit_function_definition` calls
`classify_c_fn_entry_point` — `main` → `BinaryMain`, `luaopen_*`
prefix → `FfiExport`. `PublicApi` for plain non-`static` C functions
is deferred (almost every non-static C fn is also cross-TU-callable,
so blanket tagging would drown the signal). New `push_symbol_with_entry`
wraps the existing `push_symbol` helper. Prototypes (`.h`
declarations) intentionally NOT tagged — only definitions are the
real flow root.
- Tests: round-trip on `EntryPointKind` serde shape, three Rust
extractor tests (main detection, no_mangle detection, false-positive
guard on nested `mod inner { fn main }`), two C extractor tests
(main detection, luaopen_* detection). Existing tests updated for
schema v4. 1670 tests passed / 0 failed (was 1662).
TS / Lua extractors and `PublicApi` follow in a later step (each
language needs its own signal extraction logic — TS export resolution
goes through `module_lookup`, Lua needs return-table walking). The
field is wire-side `None` for symbols extracted by those providers
until they catch up — non-breaking.
…yers K-Step-E pattern, applied to the Phase 3 (Flow) entry-point field shipped in 3.1. The viz / agent / ext layer now sees which symbols are flow roots without a second round-trip through `get_context`. - standardoc-core: `GraphSymbol` gains `entry_point: Option<EntryPointKind>` (typed enum — matches the `decl_kind` precedent). `load_graph_symbols` SELECT reads `s.entry_point`, `GraphSymbolRowRaw` + `read_graph_symbol` extended, mapping uses `entry_point_from_sql_text`. New test `wire_shape_carries_entry_point_when_set` asserts the snake_case wire shape; the existing flat-shape test now also asserts `entry_point` is skipped when NULL. - standardoc-graph-viz: `SymbolEntry` mirrors the field as `Option<String>` — consistent with `decl_kind` / `visibility` / `language_kind`, the viz crate stays free of `standardoc_ir` dependency for K-Step-F flow shaping. - standardoc-server: `get_context_summary` JSON `symbol` block carries `entry_point` so MCP consumers see the flow-root signal in the lightweight summary, not just in the full `get_context` payload. - ext/vscode: new `EntryPointKindJson` union type (`binary_main | public_api | ffi_export`), `RawSymbolJson` gains the optional field. Mirrors `DeclKindJson` shape and gives TS autocomplete for any consumer that branches on entry-point kind. cargo workspace check ✓, wasm32 check ✓, tsc ✓, 1671 tests passed (+1 new wire shape test = 1670 from 3.1 → 1671 here), 180 ext/vscode bun tests passed.
…viz refresh Sub-step 3.3 ships the visual highlight for entry-points so the viz answers "where does it start?" at first glance. A coloured glow halo (cornflower blue for binary_main, amber for public_api, orange for ffi_export) is painted behind each card in 2D and around each impostor in 3D, with the silhouette tracking the shape (circle/hex) rather than always being a circle. Data flow plumbed end-to-end: RawSymbol.entry_point (3.1) → wire GraphSymbol (3.2) → SymbolEntry → TreeNode → Card → LevelNode → NodeInstance.halo → chip.wgsl New palette helper entry_point_halo_color gates on Option so an unknown wire tag simply paints no halo until the renderer learns about it. 2D: render::draw_cards paints two concentric rounded fills behind the body (12px / 6px pad, alpha 0.18 / 0.35) mimicking the 3D shader's soft falloff. 3D: NodeInstance gains halo: [f32; 4]. The vertex stage inflates the billboard quad by 1.35× when halo.a > 0 so the impostor keeps its full apparent size and the aura lives in the new margin. The fragment stage splits each pixel by metric < shape_extent (impostor re-scaled into its own [-1, 1] frame, delegated to existing shade_sphere / shade_cube) vs metric ∈ [shape_extent, 1.0] (halo ring with smoothstep falloff). This commit also bundles the V1→V5 viz refresh that had been sitting uncommitted on dev across prior sessions (per this session's decision, shipping as one commit rather than splitting the entangled tree): cards-only refactor (hierarchy.rs deleted, scene + layout + render rewritten level-only), hex isometric cube impostor, ghost ring with cross-edges, label LOD threshold, gradient edges, 3D picking + hover, test-symbol filter regex, virtual module nodes (ensure_virtual_path), new force3d + camera modules, separate edge.wgsl shader. Tests: 1671 passed / 0 failed (no regression vs Phase 3.2 baseline). cargo check --workspace and --target wasm32-unknown-unknown both clean.
…y-point satellites
Sub-step 3.4 transforms the workspace-overview view from a scatter
plot of project cubes into an actual graph that answers two questions
the IR already knew the answer to but the renderer wasn't surfacing:
1. "Who depends on whom?" — inter-project edges aggregated from the
symbol-level cross-link set, with weight = count of underlying
links and direction preserved (so std-cli→std-ir and std-ir→
std-cli are separate edges when both exist).
2. "What can I run?" — each project cube now orbits a small ring of
entry-point satellite spheres (binary_main / public_api /
ffi_export), halo-coloured the same as 3.3 cards. Cap at 5 per
project; surplus collapses into a single "+N" overflow badge.
Pipeline changes:
- `DrillTree.entry_points_by_project: HashMap<u32, Vec<u32>>` built
once at `build()` from a local `project_of[]` parent walk. Sorted
by fqdn so the satellite ring is stable rebuild-to-rebuild.
- `DrillTree::level_edges()` returns `Vec<(u32, u32, u32)>` now —
directed pairs with weight, no more `if a < b { swap }` dedup
that flattened direction. Also exposes `is_root_level()` so
workspace-only enrichments can gate.
- `scene::Edge` gains `weight: u32`. The 2D `render::draw_edges`
multiplies `set_line_width` by `(1 + 0.25·(weight-1))` capped at
3×; the 3D edge.wgsl can't vary line topology width per segment,
so weight modulates per-vertex `color.a` instead — single-link
edges read at 0.4 alpha, saturating to 1.0 around weight 5.
- `GraphEngine` gains `satellites: Vec<SatelliteSpec>` parallel to
the post-ghost section of `build_level_nodes()` output. Each spec
carries a parent primary index + orbit angle + EP tree_idx (or
`u32::MAX` sentinel for overflow badges). Positions recompute
each frame from the parent's current force-settled center so the
ring tracks layout settling.
- `pick()` extends to a third zone (satellites after primaries and
ghosts); `drill_pick()` fires `focus_to(ep_fqdn)` on satellite
click so the user lands inside the program at its natural starting
point. Overflow badges are explicit no-ops on click.
- `label_layout()` emits EP satellite names on hover (gated like
primaries) and always shows the "+N" overflow text — there's at
most one badge per cube and the count IS its whole point.
`LevelNode` gains `#[derive(Clone)]` so satellite generation can read
the parent slice and push new entries without borrow-checker gymnastics.
Deferred: 2D satellites. The current 2D grid layout has no natural
space for satellite chips without significant rework; the workspace
overview pain was sharpest in 3D (the screenshot the user pointed at)
and the edge-aggregation fix lands for both. 2D-satellite follow-up
documented in handoff #109.
Tests: 1671 passed / 0 failed (no regression vs Phase 3.3 baseline).
cargo check --workspace and --target wasm32-unknown-unknown both clean.
Picks up TS where Phase 3.1 left it (Rust + C first-pass landed in 38e7946, TS/Lua deferred). Adds a conservative `BinaryMain` heuristic for TS function declarations so the playground's `main.ts::main` finally gets a halo in the 3D viz instead of being indistinguishable from any helper function. Heuristic: a function named `main` whose `parent_fqdn` has at most one `::` segment — i.e. it sits at the project root (`<project>::main`) OR at the root of a top-level file (`<project>::<file>::main`). Deeper paths (`<project>::<dir>::<file>::main`) are NOT tagged — a `main` buried inside a subfolder almost always means "the function in this module that does the main thing", not the runtime entry-point. Applied at both `extract_fn_decl` (regular `function main() {}`) and `process_export_default_decl` (`export default function main() {}`). Class methods named `main` are unaffected because they route through `extract_method`, which still emits `entry_point: None` — guarded by a regression test so a future drift doesn't accidentally start tagging `class Runner { main() {} }`. `PublicApi` is still deferred for TS — it would need export-graph walking across the project (resolving the package barrel) which is substantial scope. `FfiExport` has no clean TS equivalent (the wasm-bindgen Rust glue is already covered by the Rust pass). Lua skipped entirely for this pass — workspace audit found zero \`main()\` conventions in pure Lua code (the only entry-point signal is `luaopen_*` which is C-side and already covered). Re-evaluate when a Lua project with an actual `main` convention shows up. Tests: 1675 passed / 0 failed (+4 new in ts::walk::tests: entry_point_main_at_module_root_tagged_binary_main, entry_point_default_export_main_tagged_binary_main, entry_point_non_main_function_is_none, entry_point_main_helper_inside_class_not_tagged). cargo check --workspace and --target wasm32-unknown-unknown both clean.
Mirror palette.rs kind + entry-point colours into CSS custom properties so canvas-rendered and DOM-rendered surfaces share one visual identity. Add spacing/typography scales, shell grid template variables, and panel chrome tokens — fondations for the 4-panel layout (Explorer + Overview + Focus Graph + Symbol Details). No existing tokens removed or renamed; consumers fall back through var() so toolbar/hud/graph remain unaffected.
…viz/mcp-client
Move the MCP client class + wire types out of the playground bootstrap
into a dedicated lib module so future shell consumers (Symbol Details
panel, Explorer file tree, Callers Graph popup, eventually the VSCode
webview) share one transport-agnostic surface.
Two static constructors: McpBrowse.connect(transport, info?) accepts any
SDK Transport so a future VSCode webview can pass a postMessage bridge;
McpBrowse.connectHttp(url, info?) keeps the playground's HTTP-streaming
case as a one-liner.
Adds @modelcontextprotocol/sdk as a peerDependency mirroring the
classigo/matchigo pattern, and installs it in lib/ so subpath imports
('/client', '/client/streamableHttp.js', '/shared/transport.js')
resolve under moduleResolution: bundler. Playground main.ts now imports
McpBrowse + the Browse* types from the lib instead of defining them
locally.
Minimal observable holding the shell's current focused FQDN plus a capped MRU list. Every panel of the multi-panel shell (Symbol Details, Focus Graph, Source View, Field Details, Callers Graph) is a lens on this single value, so the store is intentionally trivial — one current, one recents array, one subscriber set. Persists snapshots to localStorage under 'standardoc:focus-state' so the shell remembers the last focus across reloads, with a try/catch fallback for restricted contexts (Safari private mode, sandboxed iframes). Mirrors every mutation as a 'sd-focus-change' CustomEvent on document for legacy components that still drive off DOM events. Singleton 'focusStore' for the conventional shell case; the FocusStore class is also exported so tests and embedded scenarios can isolate.
CSS-grid shell hosting five named slots (toolbar / explorer / overview / focus / details) per the multi-panel mockup. Children are placed by data-slot attribute so the element itself stays logic-free — pure styling host wrapped as a Web Component for tag-map typing uniformity. Token-driven grid template lets the playground or VSCode webview override --sd-shell-grid-cols / --sd-shell-grid-rows without forking the SCSS. Ships a generic .panel chrome helper (header + body) for pure-DOM panels to reuse. Spawnable popups (Field Details, Callers Graph, Source View) are out of scope here — Phase 4.
Five stacked sections: search input, tree, entry points, recently viewed, legend. Data injected via property setters (tree, entryPoints) — element does not call MCP itself, host orchestrates the fetch and hands shaped data in. Recents subscribe directly to the focusStore singleton so the host doesn't need to plumb that update separately. Click semantics: every clickable row (tree leaf with FQDN, entry point, recent) calls focusStore.setFocus(fqdn) AND fires sd-explorer-select so hosts can react beyond the focus change (telemetry, route sync, etc). Tree click also toggles expand/collapse for nodes with children. Search input is wired with a 150ms debounce emitting sd-explorer-search — filter logic itself lives in the upcoming search component, not inline here. Entry-point badges colour-mirror palette.rs entry-point hue (binary_main / public_api / ffi_export). Legend is hardcoded to the kind palette tokens for visual continuity with the canvas.
… panel Header (name + tags + file:line + icon buttons) → tabs (Overview / Fields / Methods / Source) → scrolling body → action footer. Data injected via the 'symbol' property setter — host calls something like 'el.symbol = await mcp.getContext(fqdn)'-derived SymbolDetail. Overview tab is fully wired: documentation with show-more truncation, relations breakdown bucketed by edge kind (Used by / Uses types / Calls / Imported by / Tested by / Implements / Extends), top-5 per bucket with 'See all >' surfacing when total > 5, entry-point kind badge mirroring the Explorer palette. Fields / Methods / Source tabs render placeholders pointing to Phase 4 (Field Details + Source View panels with Shiki/Monaco integration). Relation-item click also calls focusStore.setFocus(target) so clicking a 'Used by' entry shifts global focus — the upstream Explorer / Focus Graph / future Source View all re-render off the same store. Element also subscribes to focusStore directly so it can show the focused FQDN in an empty-state header during the host's fetch window, instead of going blank between focus change and symbol property arrival.
Standalone autocomplete input dropping anywhere in the host shell (top toolbar, Explorer header, command-palette overlay). Wire contract: the element emits sd-search-query (150ms debounced) as the user types and expects the host to set 'results' via property setter — keeps the element transport-agnostic so VSCode webview can route MCP through a postMessage bridge later instead of HTTP. Keyboard: optional Cmd/Ctrl+K global shortcut behind global-shortcut attribute (no global side-effect when omitted), Esc clears + closes, ArrowUp/Down navigates active item, Enter selects. Result click uses mousedown so it fires before the input's blur-close. Selection calls focusStore.setFocus AND emits sd-search-select so a host wrapping the component in a command palette can react beyond the focus change. 'loading' property surfaces a search-in-flight status row so the dropdown doesn't flicker between two empty renders during the host's MCP fetch window.
…ojects / listSymbols / findSymbolsByPattern Adds the four MCP endpoints the shell panels need beyond the fetch_graph + current_revision baseline. Wire types stay one-to-one with what the daemon emits — the playground host (and future VSCode webview) is responsible for shaping these into UI-shaped data (the Explorer's ExplorerTreeNode, the Symbol Details' SymbolDetail) so the lib stays MCP-shape agnostic past this module. getContext surfaces CALLS + IMPORTS edges by design (mirror of the daemon-side get_context contract); the full edge breakdown needed by the Symbol Details mockup (USES_TYPE / TESTS / IMPLEMENTS / EXTENDS / REFERENCES) is reached by pairing with fetchNeighborhood(fqdn, ...). That dual-fetch pattern is intentional — get_context returns documentation and resolved-symbol enrichment that fetch_graph does not, while fetch_graph carries every edge kind that get_context omits.
…round Adds a second entry point on the playground dev server so the new shell can be visually validated without breaking the legacy single- canvas playground: GET / → legacy index.html → main.js (main.ts bundle) GET /shell.html → new shell.html → shell.js (shell.ts bundle) shell.ts wires the full multi-panel mockup against the daemon: - panel-layout root with the 4 main slots + toolbar - explorer: top-level project list from list_projects, entry points filtered client-side from list_symbols (no server-side filter yet) - search: debounced find_symbols_by_pattern, ⌘K shortcut active - overview: existing <standardoc-graph> driven by fetch_graph, click on a node feeds focusStore.setFocus - focus slot: placeholder (Phase 3 owns the specialised FocusGraphCanvas) - details: subscribes to focusStore — get_context + fetchNeighborhood pair to populate every relation bucket (CALLS + IMPORTS from get_context, USES_TYPE / TESTS / IMPLEMENTS / EXTENDS / REFERENCES from the focal neighborhood). Stale-fetch token guards focus shifts. server.ts: bundleMain renamed bundleEntry(entry); /shell.js wired through the same Bun.build pipeline with the scss-modules plugin so the shell components' .module.scss imports hash + inject correctly.
Bun's resolver follows tsconfig 'paths' first and can't pivot from the
.ts-implying glob mapping ('@standarx/standardoc-viz/*' -> '../lib/src/*')
to a .scss target when the bare specifier carries no extension. So
'@standarx/standardoc-viz/styles/tokens' was unresolvable at bundle
time even though lib/package.json exports it correctly.
Drop the import and inline the :root token block in shell.html — it's
pure CSS (no SCSS features in this section) so the duplication is
mechanical. The SCSS source stays canonical for components that @use
it through relative paths; this inline copy mirrors it for the shell
chrome. Components themselves still work via their var() fallbacks
when the inline tokens are absent, but with them present the entry-
point badges and kind palette match palette.rs exactly.
Explorer gains 'expandable' + 'loading' flags on ExplorerTreeNode plus a new 'sd-explorer-expand' event for the host to lazily populate children on first expansion. Click on the twisty of an expandable node with no children attached → event fires → host fetches → host mutates the tree and re-sets the property, the panel re-renders. shell.ts wires this for the project drill: list_symbols(module=label) returns a project's root-level items (re-exports, top-level fns, crate-root structs) which become leaf children with their FQDN preserved so a click cascades to focusStore.setFocus and Symbol Details. Deeper module navigation (intermediate module nodes) needs a different IR query (no 'list children modules' on the daemon today) and is left for Phase 3 alongside the Overview canvas. For now a project + one level of root symbols is enough to validate the wiring + UX.
- edge palette: the cross-edge kind colours/labels lived in two TS copies (overview.cross-edges CROSS_EDGE_KINDS + legend EDGE_ENTRIES) that had already drifted -- the legend omitted EXTENDS while the overview kept it. Hoist the canonical list into a leaf `src/edge-kinds.ts`; overview re-exports it, the legend derives its swatches from it (now shows every real edge kind, EXTENDS included). Still mirrors Rust cross_edge_style. - kind family: search's bucketKind and symbol-details' kindFamilyTagClass each carried their own kind->family Set/switch with divergent vocabularies -- symbol-details was missing class_method / struct_field / class_property, so some TS members rendered untinted. Replace both with one leaf `src/kind-family.ts` (union vocabulary); bucketKind becomes kindFamily, kindFamilyTagClass maps the family to its CSS class. No behaviour change beyond the legend now listing EXTENDS and the kind-family fix tinting the previously-missed member kinds. tsc --noEmit clean; playwright shell suite 3/3 logic tests green.
…reads them Cross-layer dumb-renderer pass (lot 4). The graph shell re-derived three things the daemon already knows: - project_id: list_symbols now carries it via a dedicated ListSymbol projection (files JOIN), mirroring GraphSymbol on fetch_graph. Drops the shell's path-prefix inferProjectId (a copy of reconcile_files_project_id). - is_test: list_symbols + fetch_graph stamp the symbol_looks_like_test verdict. Removes shell/symbols.ts::looksLikeTest and the symbol-details fields/methods filter; the get_context-fed relations filter keeps a slimmed fqdn-only looksLikeTest (follow-up lot). - language: pruned from the fetch_graph wire (GraphSymbol) + BrowseSymbol (0 readers). list_symbols exclude_tests reuses the projected is_test. Tests: new core list_symbols_projected_* + suites green; tsc + playwright (3 logical) green.
…crash Audit ext/vscode (daemon/supervisor + webview/MCP relay clusters), 3 findings: - EXT-MCPCLIENT-DEAD-METHODS: drop McpClient.listSymbols/checkStale/ currentRevision (never called - only getContext/findSymbol are wired via commands.ts) + their dead *Json types (ListSymbolsPageJson, CheckStaleJson, CheckStaleEntryJson, CurrentRevisionJson). - EXT-RELAY-START-NORETRY: WebviewMcpRelay.ensureStarted memoised a rejected start() forever; reset this.started=null on failure so a later frame opens a fresh transport instead of wedging the relay. - EXT-SUPERVISOR-MCP-CRASH-UNDETECTED: McpClient now fires onExit on an unexpected child death (stop() nulls this.child first to tell teardown apart); the supervisor restarts on it. A dead MCP daemon paired with a live LSP previously stayed stuck at `ready`. typecheck clean; bun test 195 pass.
…runner Lot 2 of the ext/vscode audit (quality only, no behaviour change): - EXT-SYMBOLKIND-DEAD: remove symbolKind.ts + symbol-kind-map.ts (+ test). iconForSymbolKind was never called - a SymbolKind->themeIcon map kept alive only by its own test. - EXT-HOVER-RUNPREVIEW-DUP: extract the shared spawn/timeout/cancel/parse plumbing into preview-runner.ts::runPreviewSubprocess; the stdignore + sxd hovers now call it (they already shared hover-render/PatternPreview). - EXT-HOVER-DEBOUNCE-DEAD: drop HOVER_DEBOUNCE_MS + the unused __test_internals export (never imported, never applied). - EXT-CMD-STRIPNEWLINE-MISNOMER: rename stripTrailingNewline -> ensureTrailingNewline (it normalises to one trailing newline, never strips). typecheck clean; bun test 166 pass.
…SION-DEFAULT)
The daemon (DEFAULT_MCP_HTTP_PORT) and the proxy (DEFAULT_PROXY_PORT) both
defaulted to 7700; the daemon binds first, so the proxy hit EADDRINUSE and
crashed silently (best-effort) - it never ran. And .mcp.json pointed at the
daemon, bypassing the proxy entirely.
serve_mcp_http is already designed for `--http 0` ("typically the VSCode
supervisor passes 127.0.0.1:0"): ephemeral bind + writes the resolved URL to
mcp.endpoint + reuses the previous port for reconnect-safety. The extension
just wasn't wiring it. Restore the intended architecture:
- daemon: DEFAULT_MCP_HTTP_PORT 7700 -> 0 (ephemeral). Frees 7700.
- proxy: keeps 7700, now binds, watches mcp.endpoint, forwards.
- .mcp.json: points at the proxy route http://<bind>:<port>/ws/<id>/mcp
(stable across daemon restarts + sibling windows). <id> via shelling out to
`standardoc workspace-id` so it stays bit-identical to the proxy's hash.
- in-window consumers (webview, chat provider) still use the daemon directly
via mcp.url().
- package.json: mcpHttpPort default 0 (ephemeral) + both settings clarified.
No Rust change (serve_mcp_http already handles port 0). typecheck clean;
bun test 166 pass.
…TS-GLOBALS) There is no JsProvider — every .js/.jsx/.ts/.tsx/.vue/.svelte file flows through TsProvider, which resolves builtins under Language::TypeScript (build_ts_lookup hardcodes it). The TS registry omitted the ambient runtime globals (console, window, parseInt, Proxy, Reflect, undefined, isNaN, …) that only the JS registry carried, so every bare reference to them resolved Unresolved — pure graph noise. The JS registry was itself write-only dead data: no lookup ever passes Language::JavaScript. Extract the ambient globals into a shared register_ambient_globals(reg, lang) seeded by both js::register_all (JavaScript) and ts::register_all (TypeScript). TS is now a strict superset of JS with zero duplication; the JS registry stays honest for a future JsProvider.
…LTIN-C-DUP) The C header block registered time/assert/errno/signal as Kind::Module, then the same bare names as fn/macro/value. Both tiers are Edge, so both reach the cold-start seeder, where insert_symbol is UPSERT-by-fqdn — the two <builtin>::c::time entries collapsed to one row (last writer wins), silently dropping the header's Module semantics. C never calls BuiltinRegistry::lookup, so this was a seeding collision, not a lookup one. Keep the .h in every header's builtin fqdn so headers form a namespace distinct from same-named identifiers: #include <time.h> resolves to <builtin>::c::time.h (Module), separate from time() at <builtin>::c::time (Callable). The include emitter stops stripping .h; import-record local_name still trims it so it stays the bare stem.
…BUILTIN-JS-ERR/KIND/INT) - JS-ERR: seed the 4 ECMAScript error globals (SyntaxError, ReferenceError, EvalError, URIError) under JavaScript too, matching the TS registry. - KIND: Math/JSON are namespace objects (member access, never constructed), so Kind::Module in TS — consistent with console and the JS registry instead of the prior JS-Module / TS-Type split. - INT: split the signed-only abs and unsigned-only is_power_of_two / next_power_of_two out of the shared int-method sweep so we stop seeding phantom u8::abs / i8::is_power_of_two rows.
…(RUST-DEADCODE/TYPENAME-DOC)
- collect_globals: remove the `.or_else(|| { let _ = ty.span(); None })`
no-op tail and the `use syn::spanned::Spanned;` it was the only consumer of.
- lookup: remove the `const _: Option<&ImplItem> = None;` keep-alive scaffold
(and its `#[allow(dead_code)]`) plus the now-unused `ImplItem` import.
- type_name::substitute_template: the doc listed a closed container set for
`T = args[0]`, but the `_` arm applies it to any single-type-param nominal
— describe the catch-all honestly.
…UP-NESTED) class / interface / enum / type-alias declarations nested in a function or block body were never bound in the AOT ModuleLookup — only module-level hoisting bound their names, so scope-local references to a nested type resolved Unresolved. visit_fn_decl / visit_var_decl already mirror this by binding when current_scope != ROOT; the four type-decl handlers now do the same, binding the name at the enclosing scope before opening their own type-container scope.
…-NSEXPORT) The walker already emits the namespace re-export edge, but record_export_named only handled ExportSpecifier::Named — `export * as ns from "mod"` left `ns` unbound in the ModuleLookup (no binding, no ImportRecord), so refs to `ns` resolved Unresolved and the cross-workspace flatten missed it. Mirror the import-namespace shape: bind the alias to the whole source module with no origin symbol.
…S-LOOKUP-SCOPELINE/NPM-1) - ts::lookup::push_scope: document that ScopeRange.start_line/end_line carry swc byte offsets (not lines) here — nothing reads those fields, scope containment runs off the span_to_scope byte map. The rust builder fills real lines. - externals::npm: add mts/cts to NPM_EXTENSIONS (the TS pair of mjs/cjs); TsProvider already maps them to TypeScript via language_for.
…t versions The chained `assert!(reg.lookup_method(...).is_some())` form wraps differently between rustfmt versions (chain_width heuristic). Bind each lookup to a local first so the asserts stay single-line and format identically everywhere.
query.rs / graph.rs each had one line that didn't match `cargo fmt --all` (method-chain and binary-op wrapping) — pre-existing on dev, not from this branch's work. Format them so `cargo fmt --all -- --check` passes.
…EXTS/WARN-ISTERMINAL) - targets_non_source_file: add vue/svelte to SOURCE_EXTS — the SFC extractor indexes them, so a Read should hit the MCP-first gate like any .ts/.rs file instead of being waved through as non-source. - emit_warn: drop the dead `let _ = stderr.is_terminal();` (and the now-unused IsTerminal import) and correct the doc — lines are always emitted, never no-op'd on TTY/pipe state.
…ts (GV-FOCUS-INDIRECT-STALEDOC) Three doc sites (module header, Bucket enum, layout()) claimed depth-2+ neighbours with no direct edge are "dropped/skipped entirely" — V0 behaviour. bucket_for_neighbor actually falls them through to Bucket::Indirect (due south) so multi-hop views surface them. Align the docs with the code.
…-LSP-SELECTOR/MCPCLIENT-NO-ISERROR) - lsp/client documentSelector: add `lua` and `c` so editor LSP features (hover/diagnostics) activate on .lua/.c/.h files the daemon already indexes. - mcp/client.callTool: check result.isError and throw instead of returning the daemon's error payload as if it were a successful text result.
…EXT-INIT-JSONC) Claude Code tolerates comments and trailing commas in .claude/settings.json, but init did a strict JSON.parse → such a file fell to `invalid` and init declined to auto-merge. Add a string-aware stripJsonc (comments + trailing commas, string-literal safe) and retry through it on parse failure, leaving the plain-JSON path untouched.
…ALK-RENDER-DUP) render_expr_name was functionally identical to render_member_expr_name and render_ts_entity_name a pure pass-through. Drop both and point the call sites at render_member_expr_name. No behaviour change (938 tests unchanged).
…(TS-EXTRACT-DUPS/PROPNAME-DUP) prop_name_static (ffi_tagger) and method_name_string (extract_items) were identical PropName->Option<String> extractors; ts_enum_member_id_name was duplicated across extract_items and lookup. Consolidate into ts::helpers as prop_name_static + ts_enum_member_id_name; drop the three copies. No behaviour change (938 tests unchanged).
…e generic (TS-VISIT-FORUSE-REP)
The six near-identical visit_{type_ann,type_params,ts_type,pat,ts_fn_param,
ts_type_param_decl}_for_uses wrappers differed only in node type. Replace them
with a single visit_for_uses<N: for<'a,'b> VisitWith<CallVisitor<'a,'b>>> and
redirect the 13 call sites. CallVisitor becomes pub(crate) so it can appear in
the bound. No behaviour change (938 tests unchanged).
…ORD) Extractors stamped columns in mixed units: TS used swc col_display (tab-expanded), C used tree-sitter byte offsets, Lua used full_moon's 1-based char count, and the Module symbol used byte length. The LSP / VSCode consumers forward the column straight into Position.character, which is UTF-16 code units, so navigation drifted on tabs and on any multibyte character before a symbol. Normalize every extractor on UTF-16; consumers stay unchanged: - utils::location: utf16_len / utf16_col / line_and_utf16_col helpers; content_extent now measures the last line in UTF-16. - TS: read swc Loc.col, which is already UTF-16 (its multibyte adjustment maps a 4-byte char to two units), instead of the tab-expanded col_display; drop the now-dead clamp_col. - C: convert tree-sitter byte columns against the source line. - Lua: derive line + UTF-16 column from full_moon's absolute byte offset, which also corrects the previous 1-based off-by-one column. - Rust: proc-macro2 exposes no source text here, so its char columns stay (equal to UTF-16 except for astral chars); documented on span_to_location. Adds decisive non-ASCII column tests per provider.
…sx (TS-DTS-MINOR) syntax_for only set dts:true for `.d.ts`, so the ambient-declaration parsing leniency never reached the `.d.mts` / `.d.cts` / `.d.tsx` variants already listed in TS_CONVENTIONS.
…UTF8) The non-identifier branch copied a raw byte via `ch as char`, which splits a multi-byte character into separate Latin-1 chars. Copy the whole UTF-8 char instead and advance by its byte length. Latent today (the templates are ASCII), hardened with a multibyte round-trip test.
…-COORD) extract_sfc emitted each Vue/Svelte template-ref REFERENCES edge with a Site column computed by byte_offset_to_line_col (byte columns) — the last column producer still off the UTF-16 convention, so a multibyte char before an interpolation drifted the column. Point it at line_and_utf16_col (line + UTF-16 column from an absolute byte offset) and remove the now-unused byte-column helper and its tests.
…ime (RUST-CRATENAME-CACHE-STALE) The per-Cargo.toml crate-name cache never invalidated, so renaming [package].name left stale FQDNs until a daemon restart. Key the cached value on the Cargo.toml mtime and only count a hit when it still matches: unchanged manifests keep the single-read fast path, a rename is picked up on the next extraction. Fully local to resolve_crate_info — no cross-crate invalidation plumbing.
…er_type
A `.method()` call on Arc/Rc/Box captured receiver_type as the pointer
("Arc") instead of the pointee, so e.g. `Arc<RwLock<T>>::read()` recorded
"Arc" and never resolved against RwLock. Peel the pure Deref wrappers
(Arc/Rc/Box) before taking the nominal, with a denylist for the pointers'
own inherent methods (clone/strong_count/downgrade/...) where the pointer
itself IS the receiver. Conservative: Mutex/RwLock/RefCell keep their
nominal because lock/read/borrow genuinely are their methods.
Direct helper tests + behavioral tests. The aggregate resolution delta is
not measured here (would need a live rescan); the full suite shows no
behavioral regression.
…fn + unused enum) `TsProvider::extract_external_dts` was a public method whose body was just `todo!()`, and the `TsExtractMode` enum carried `#[allow(dead_code)]` and was never threaded through the visit layer: the external-package `.d.ts` indexing feature was scaffolded but never wired (0 callers anywhere). A panicking public method has no place in a release base — remove the dead scaffold. The feature itself is tracked for a later release.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
No description provided.