chore(release): 0.4.0#50
Merged
Merged
Conversation
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
…nvelopes Step 0.1 (TRACKER). Addresses C1/D2 schema halves, A5, B5 foundations: - InstanceNode: one per JSX call site (id instance:<file>:<line>#<Name>), wired owner --renders--> instance --instance-of--> definition; staticProps captured. parentInstanceId lands with the Phase 2.1 instance tree. - Evidence + Confidence types; QueryResult<T> envelope with ok/ambiguous/ declined — no query API returns a bare value. matchComponentsByText returns ambiguous + disambiguation question on ties, declined(no-signal) on misses; traceLineage accepts definition or instance ids and walks through instances. - EdgeCondition (flag/role/branch/response) + via on edges; GraphMeta stub; graph version bumped to 2. - vitest wired into core + parser-react: 18 tests (envelope invariants, tie/ decline behavior, instance emission on the demo app, transitive trace). - CLI prints confidence, evidence, instance call sites. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
…hema-v2 feat(core): schema v2 — instance nodes, evidence, confidence, query envelopes
Step 0.2 (TRACKER). The measurement loop every later phase is gated by: - @coderadar/eval: fixture discovery, golden.json diffing, scorecard.json, history.jsonl (--record), thresholds gate with non-zero exit. - Per-check expectedFail (xfail semantics): failing marked checks report xfail and don't gate; passing marked checks report unexpected-pass and DO gate, so capability arrival is an explicit reviewed event. Forbidden (poison) checks never xfail. - Fixtures: c1-shared-datatable (headline case — per-instance attributions xfail'd until step 2.2, poison assertions active now), a4-generic-text (ambiguity honesty on 'Save' collisions), demo-app (baseline, app shared with examples/). - Current scorecard: 25 pass / 0 fail / 2 xfail / 0 unexpected-pass; lineage precision 1.000, recall 0.714 (xfail gap), match accuracy 1.000. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
…al-harness feat(eval): eval harness + first failure-mode fixtures
Step 0.3 (TRACKER). G2/G3/D1 foundations: - saveGraph/loadGraph: version-checked persistence — refuses graphs newer than the library (an old reader must never misinterpret a new graph). - collectGraphMeta: commit SHA + dirty flag recorded at scan time; coderadar scan prints provenance. - schemas/lineage-graph.schema.json generated from the TS types (ts-json-schema-generator), committed, drift-gated by a test that regenerates and diffs. Located at repo root since dist/ is gitignored (TRACKER updated to match). - CLI query commands load through the validating loader: a v99 graph now fails loudly with an upgrade message instead of being silently misread. 24 unit tests green; eval gate OK (25 pass / 0 fail / 2 xfail). Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
…aph-storage feat(core): graph storage, schema versioning, scan provenance
…rtifact Step 0.4 (TRACKER). ubuntu-latest, pnpm-cached, 15-min timeout, concurrency-cancelled. Scorecard uploaded on every run (pass or fail). Threshold ratchet rule documented in the workflow header: raising eval/thresholds.json is free, lowering requires PR-body justification. Full step sequence verified locally (frozen install, build, typecheck, 24 tests, eval gate OK). Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
ci: Gate 0 pipeline — build, typecheck, tests, eval gate, scorecard artifact
…terns, base-URL stripping
Step 1.1 (TRACKER). Failure modes C2 (constants half), C3:
- resolveEndpoint/resolveStringValue: cross-file constant folding via
go-to-definition (named constants, ENDPOINTS.X object members, + concat,
as/satisfies unwrap, depth-capped), template literals with unknown parts
normalized to :param placeholders (/api/users/${user.id} → /api/users/:id),
configured baseUrls stripped from resolved endpoints.
- DataSourceNode gains raw (source text) + resolved (full|partial|none);
endpoint is now the canonical pattern — the cross-graph join key.
- Fixtures c2-endpoint-constants (object member / named const / concat) and
c3-dynamic-endpoints (dynamic id + dynamic resource segment), both with
<dynamic>-collapse forbidden assertions.
- Thresholds ratcheted: minLineagePrecision 0.9, minLineageRecall 0.8.
Scorecard: 38 pass / 0 fail / 2 xfail; precision 1.000, recall 0.833
(was 0.714), match accuracy 1.000. 33 unit tests.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
…dpoint-resolution feat(parser-react): endpoint resolution — constant folding, route patterns, base-URL stripping
…+ config, chains to depth 3
Step 1.2 (TRACKER). Failure mode C2 (wrapper half):
- detectWrappers: any named callable (function, const fn, object-literal
method) whose body reaches fetch/axios/another wrapper with one of its own
params in the URL is classified as a wrapper. Templates compose through
chains (useApi → apiClient.get → request → fetch), so a call site's
argument substitutes all the way down with constants folded:
useApi('/projects') → /api/projects.
- Method inference: name suffix (apiClient.post → POST) beats inner method.
- ScanOptions.apiWrappers declares wrappers the heuristic can't see.
- Wrapper bodies are plumbing: their own internal fetch emits no data source,
so ':path' placeholders never leak into consumer attribution (fixture has
forbidden assertions for exactly that poison).
- Thresholds ratcheted: minLineageRecall 0.85. Scorecard: 47 pass / 0 fail /
2 xfail; recall 0.857. 39 unit tests (6 new wrapper tests).
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
…i-wrapper feat(parser-react): API-client wrapper adapter — heuristic detection + config, chains to depth 3
…r endpoints
Step 1.3 (TRACKER). Failure mode C5:
- endpointFromFunction: queryFn/mutationFn/fetcher expressions resolve to a
function body — inline arrows directly, references via go-to-definition
(cross-file) — and the first data source inside is extracted with the full
1.1/1.2 machinery (constant folding + wrapper substitution work inside
queryFns for free).
- Covers v4/v5 object form ({queryKey, queryFn} / {mutationFn}), v3
positional forms (useQuery(key, fn) / useMutation(fn)), and SWR key-or-
fetcher fallback. Mutations pick up the inner call's method (POST).
- DataSourceNode gains queryKey — the cache identity recorded alongside the
endpoint, groundwork for cache-sharing analysis (C7).
- Fixture c5-queryfn-indirection with the poison assertion that a query KEY
must never be reported as an endpoint.
- Thresholds ratcheted: minLineageRecall 0.88. Scorecard: 60 pass / 0 fail /
2 xfail; recall 0.889. 44 unit tests.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
…eryfn-following feat(parser-react): follow react-query/SWR queryFn references to their endpoints
…ns keys
Step 1.4 (TRACKER). Failure mode A2:
- renderedText is now structured: { text, source: jsx|attribute|i18n, key?,
locale?, branch? } — provenance travels into match evidence ('matched
"Membres de l'équipe" (i18n key team.title, locale fr)').
- loadLocaleTable: JSON/YAML locale files via ScanOptions.i18n
{ localeGlobs, defaultLocale }; nested keys flattened; locale inferred
from filename or parent dir; dependency-free mini-glob.
- t('key') / anything.t('key') / <Trans i18nKey> expand to one entry per
locale, so a screenshot in ANY language matches the same component;
i18next namespace: prefixes fall back to the bare key.
- Eval runner: golden.json gains a scan field (per-fixture scan options).
- Fixture a2-i18n-keys (en + fr, t() and Trans): all 5 locale queries green.
- Scorecard: 67 pass / 0 fail / 2 xfail. 49 unit tests.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
…18n-adapter feat(parser-react): i18n adapter — locale-file resolution for t()/Trans keys
…hared normalization
Step 1.5 (TRACKER). Failure modes A7 (extraction half), A8:
- core/text.ts: normalizeText (lowercase, unicode-safe punctuation strip,
whitespace collapse, naive plural folding) + textMatches (bidirectional
containment with * wildcards) — ONE normalization used by extraction and
matching, and later by the Phase 4 matcher.
- Template JSX children extract as wildcard entries: {`${count} items in
cart`} → '* items in cart' (template: true), so the screenshot text
'3 items in cart' matches. Resolvable template parts fold via 1.1.
- Branch tagging: text guarded by ternaries, && gates, or early-return ifs
carries branch: <condition>; ternary else-branches negate. Evidence
surfaces it: 'renders only when isAdmin'. Ternary string branches and
{"literal"} JSX expressions are now extracted at all (found by the new
a8 test failing).
- Fixtures a7-transformed-text (CSS uppercase / template / plural queries)
and a8-conditional-text (error/empty/admin states).
- Scorecard: 77 pass / 0 fail / 2 xfail. 60 unit tests.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
…ext-hardening feat: rendered-text hardening — template wildcards, branch tagging, shared normalization
…ation — Gate 1 complete
Step 1.6 (TRACKER). Failure mode D4; closes Phase 1:
- Class components: React.Component/PureComponent/bare Component subclasses
scanned — render() is the text/instance/event body, the whole class body
yields lifecycle fetches (componentDidMount), this.state keys become
class-state nodes, this.props.X accesses become props, and method-
reference handlers (onClick={this.refresh}) resolve.
- HOC unwrapping: const Panel = connect(map)(PanelInner) records an alias;
<Panel/> call sites materialize as instances OF PanelInner (alias chain
bounded at 3). Eval components check now counts instances by definitionId,
not tag name — the semantically correct reading the HOC case exposed.
- Graceful degradation: a class with no render() emits an incomplete-flagged
node instead of vanishing; coderadar scan prints the incomplete count.
- Body-walking helpers genericized from FunctionLike to Node so classes and
functions share extraction (extractRenderedText/BodyFacts/InstanceSites).
- Scorecard: 91 pass / 0 fail / 2 xfail; precision 1.000, recall 0.895.
67 unit tests. GATE 1 PASSES.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
…egacy-patterns feat(parser-react): class components, HOC unwrapping, graceful degradation — Gate 1 complete
…ing, design-system instances
Step 2.1 (TRACKER). Failure modes C1 (graph half), A5:
- JSX tags resolve through the file's imports via getExportedDeclarations:
barrel files, renames (export { ProfileCardInner as ProfileCard }), and
default-export aliases (export { default as Avatar }) all land on the
original definition's node id. Same-file and unique-name fallbacks retained;
HOC aliases compose with import resolution.
- Design-system instances (A5): tags imported from configured
designSystemPackages (or resolving into node_modules) materialize as
instances flagged external-definition with definitionId external:<pkg>#<Name>
— the usage site is ours even when the definition isn't. Unconfigured
unresolvable imports (react-i18next Trans etc.) stay excluded.
- Same-body nesting: <Card><Button/></Card> sets Button.parentInstanceId to
the Card call site — first bricks of the render tree.
- Scorecard: 99 pass / 0 fail / 2 xfail. 72 unit tests.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
…nstance-tree feat(parser-react): instance tree — import-resolved definitions, nesting, design-system instances
Step 2.2 (TRACKER). THE step this project exists for: - resolvePropFlow: every instance's expression props trace back to the data populating them in the parent scope, emitting data-source --provides-data--> instance edges (via: propName). Origins: useState pairs (setter call sites → data-source in the same statement), direct results (useQuery/useApi, incl. await/as-cast wrapping), project-hook results (hook's fetches-from edges), and simple derivations (data ?? [], raw.items) — depth-capped. - traceLineage: instance traces include incoming provides-data sources; definition traces return a perInstance breakdown, NEVER merged — merging is exactly the C1 poison. - c1-shared-datatable expectedFail markers removed (the xfail→green ritual): DataTable@UsersPage → /api/users only; @InvoicesPage → /api/invoices only. - New fixture c1-prop-variants (react-query result prop through ?? [], custom-hook result prop) with cross-page poison assertions. - Scorecard: 108 pass / 0 fail / 0 xfail; precision 1.000, recall 1.000 (was 0.895), match accuracy 1.000. Thresholds ratcheted to 0.95/0.95. 77 unit tests. Milestone M2 achieved. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
…rop-flow feat: prop-flow — per-instance data attribution (C1, the headline case)
… (B1)
Step 2.3 (TRACKER). Failure mode B1, the core static-analysis investment:
- resolveHandlerChains: events whose handler is a prop of their own
component follow the prop through instance call sites up to 4 levels —
renamed hops (onSave={onSaveDraft}), inline-arrow wraps
(onSaveDraft={() => onPersist()}), props.X access, destructure defaults —
until a real function body grounds the chain; bodies are mined for data
sources and wired event --triggers--> data-source.
- Chains that never ground flag the event unresolved-prop-handler and emit
NOTHING (the orphan fixture forbids cross-chain endpoint bleed).
- Bug found by the fixture: expression-bodied arrows ARE the call node —
getDescendantsOfKind excludes self; both body scans now include it.
- Fixture b1-prop-drilled-handler: SaveButton.onClick grounds in
POST /api/drafts through Toolbar → EditorPanel → DraftEditor.
- Scorecard: 119 pass / 0 fail; precision/recall 1.000. 80 unit tests.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
…andler-resolution feat(parser-react): handler resolution through props — 4-level chains (B1)
Step 2.4 (TRACKER). Failure mode C6, temporal decoupling: - detectStores: createSlice → one global StateNode per slice (stateKind redux); createAsyncThunk payload creators mined for data sources; extraReducers addCase(thunk.fulfilled/rejected/pending) associates thunk sources to their slice via data-source --writes-state--> slice edges. Zustand: create() stores named use* become StateNodes with their internal fetches wired as writers. - Readers: useSelector((s) => s.users.list) parses the slice path and links reads-state to the GLOBAL slice node (falling back to the old per-component node when no slice matches); zustand hook calls link to their store node; dispatch(fetchUsers()) gives the dispatching component a fetches-from edge. - traceLineage: visiting a StateNode now follows incoming writes-state edges back to the populating data sources — reader → slice → login-time API. - Fixture c6-store-decoupled: UserDirectory (NO fetch of its own) → /api/users; CartWidget → /api/cart; cross-store poison assertions. - Fixed a declaration-order bug (detectStores called before addEdge existed). - Scorecard: 129 pass / 0 fail; precision/recall 1.000. 85 unit tests. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
…tore-adapter feat: store adapter — redux/zustand writers ↔ readers (C6)
…done Step 2.5 (TRACKER). Failure mode A9; closes Phase 2: - createPortal components flagged 'portal': the screenshot's DOM position won't match the render tree, but matching already surfaces both the modal (its text) and the trigger site (its instance list). - Toast adapters (react-hot-toast toast/toast.*, notistack enqueueSnackbar, antd message.*): string arguments become renderedText entries with source 'portal' on the CALLING component — 'Order deleted' exists only as a call argument, and now matches its trigger. - Prop-flow correction the fixture forced: function-valued props and on[A-Z] attributes are callbacks, not data — the fetch inside onConfirm no longer emits provides-data. The DELETE still reaches the dialog's lineage the RIGHT way: through the 2.3 handler chain (onClick:onConfirm --triggers--> DELETE /api/orders/selected). - Scorecard: 137 pass / 0 fail; precision/recall 1.000. 88 unit tests. GATE 2 PASSES — Phase 2 (instance graph & cross-file data flow) complete. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
blastRadius(graph, target) in core: a dependency-direction-aware reverse BFS. dependencyOf() maps each edge kind to resource↔dependent so a change propagates the correct way regardless of edge direction (fetches-from and provides-data both reach a data source's consumers); journey edges don't propagate impact. Resolves a target by node id, component name, endpoint, state name, or route path; returns ImpactNode[] (node, relation, distance) nearest-first, always high-confidence. Wired into the context bundle's blastRadius section and a new CLI `impact <node>` command (--depth cap). c1 golden gains a `blast` block: both DataTable instances (d1) + both pages (d2) for the shared definition, and every /api/users consumer with an over-reach guard forbidding the invoices side. New GoldenBlast type + `blast` check kind in the harness. 5 core unit tests; eval 258/0/0, gate OK. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
…last-radius feat(impact): blast radius — reverse-dependency traversal (5.3)
New `TestNode` (kind `test`, framework vitest/jest/unknown) and `covered-by` edge in core; lineage-graph schema regenerated, drift gate green. A `detectTests` parser pass excludes test files from the component/instance scan (isTestFile) so they never emit spurious nodes, then sweeps them: every component a test renders (JSX tag) or imports becomes a covered-by edge. Imports are resolved to their source file for precise attribution, with a name fallback. The context bundle populates its `tests` section from the matched component's render subtree (componentSubtree) and pushes an `untested` warning when the matched component has no coverage; blastRadius counts a test as a dependent of the component it covers. New f3-test-coverage fixture + GoldenCoverage / `coverage` check kind (UserList covered, Sidebar untested). 6 parser unit tests incl. two bundle-level. eval 262/0/0, gate OK. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
…est-coverage feat(coverage): test-coverage mapping — TestNode + covered-by (5.4)
…(5.5)
DataSourceNode gains an optional ResponseType { name, fields, source }
(one level of fields); lineage-graph and context-bundle schemas
regenerated, drift gates green.
New response.ts parser module:
- responseFromCall: recovers the type from a call's generic argument
(axios.get<User[]>, useQuery<T>) or, failing that, the annotation on
the nearest enclosing typed variable whose initializer holds the call
(const data: Invoice[] = await fetch(...).then(r => r.json())). Stops at
function boundaries; reads property signatures only (methods skipped).
- loadOpenApi / linkOpenApiResponses: post-pass filling untyped sources
from an OpenAPI 3 JSON spec, matching `${METHOD} ${endpoint}` with
{id}->:id normalization and $ref resolution.
Exposed via the `openapi` scan option and CLI `scan --openapi`. Bundle
lineage dataSources carry responseType; `trace` prints it.
New f4-typed-responses fixture + GoldenResponse / `responses` check kind
covering all three sources. 5 parser unit tests incl. bundle-level.
eval 265/0/0, gate OK.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
…esponse-schema feat(response): response-schema linking — generic/annotation/OpenAPI (5.5)
gitHistory(root, files, limit) in agent-sdk: per-file `git log --follow` (execFileSync, no network), commits deduped across files and sorted newest-first by committer time. parsePrNumber pulls `#NN` from merge/ squash subjects. Any failure — not a repo, git missing, untracked path — yields [] so the bundle degrades gracefully. The bundle `history` section (default 5 commits) is populated from the matched component's files plus its render subtree; BundleCommit gains an optional `pr` (context-bundle schema regenerated, drift gate green). Integration tests run against this repo's own history; PR parsing tested deterministically. 5 agent-sdk unit tests + a bundle-wiring test. eval 265/0/0, gate OK. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
…it-history feat(history): git-history context in the bundle (5.6)
New @coderadar/mcp package. createServer(graph) registers five tools over @modelcontextprotocol/sdk's McpServer with zod input schemas and agent-facing descriptions: - resolve_context(ticket) → the full budgeted ContextBundle (buildBundle) - find_component(terms), trace_lineage(id), journeys(start, depth?), blast_radius(node, depth?) → core query envelopes Each tool returns a QueryResult envelope as JSON text; descriptions tell the agent that ambiguous/disambiguation is for a human. No LLM in the node. Bin `coderadar-mcp` loads a graph from CODERADAR_GRAPH (or argv) and serves stdio. Also shipped in the published ui-lineage bundle as the `ui-lineage-mcp` bin (MCP SDK + zod kept external, installed as runtime deps). Integration test drives a real stdio Client: lists all five tools and round-trips ok/ambiguous/declined over a4-generic-text. Gate 5 passed. M5 reached — ticket in → budgeted context bundle out, over MCP. Full suite: 193 unit tests, eval 265/0/0. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
…cp-server feat(mcp): MCP server — Gate 5, M5 reached (5.7)
Add .codesandbox/tasks.json so importing the repo as a CodeSandbox devbox auto-runs `pnpm install` + `pnpm -r build`, and exposes one-click tasks (test, eval, typecheck, scan-demo). Pin Node 22 via .nvmrc to match CI. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
chore: CodeSandbox devbox config + .nvmrc
Bump all packages to 0.3.0 and the generator / CLI / MCP version strings. Ships the agent interface: context bundles (resolve/bundle), blast radius (impact), test-coverage mapping, response-schema linking (incl. scan --openapi), and the ui-lineage-mcp MCP server. README changelog updated. Verified: 7 packages build, 193 unit tests, eval 265/0/0, and `npm pack` yields ui-lineage-0.3.0.tgz with both bins and no runtime @coderadar/* leakage. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
chore(release): 0.3.0
…al wildcard (6F.1, A14)
Field validation of ui-lineage@0.3.0 found that rendered text normalizing to
empty ("|", "/", "-") matched every query — needle.includes("") is always
true — so find_component returned the same punctuation components for any
input, including gibberish, and resolve_context inherited false "high
confidence" matches. Pure-"*" templates collapsed to a match-everything regex
the same way.
New hasMatchSignal guard in textMatches: a target must carry at least 2
alphanumeric characters (wildcards/spaces excluded) to match at all. With no
surviving targets, gibberish falls through to the existing
declined("no-signal") path.
Also introduces Phase 6F (field hardening, 8 steps from the 2026-07-15
validation feedback) in TRACKER.md and failure mode A14 in the catalog.
New fixture a14-punctuation-wildcard + 8 core unit tests. Eval 271/0/0,
gate OK; all package tests and typecheck green.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
…-punctuation-matcher fix(core): punctuation-only rendered text no longer acts as a universal wildcard (6F.1, A14)
…age gaps (6F.2) New fixture eval/fixtures/field-patterns mirrors the shapes the 2026-07-15 ih-frontend validation used where every gate scored 1.000 but the field run failed: tsconfig-path-alias barrel imports, Loadable(lazy(() => import())) page elements, route arrays composed in a separate file and passed to createBrowserRouter as an identifier, an RTK Query store, and tests rendering through a custom providers wrapper. Golden asserts target behavior; 9 checks the scanner cannot satisfy yet are marked expectedFail naming the enabling step (6F.3 aliases/instances, 6F.4 RTK Query, 6F.5 object-config routes). Unexpected-pass gating removes stale marks the moment a capability lands. Scanning the fixture confirmed the field diagnosis: the @ui alias import yields no instance node at all for UsersPage's <Grid/>, and 0 route/data-source/hook nodes exist. Relative two-hop barrel rename and covered-by through the custom wrapper already work and stay as passing checks. checks.ts: xfail-marked attributions no longer feed the lineage precision/recall tallies while marked, so known-missing capabilities don't depress global metrics. Eval: 280 pass / 0 fail / 9 xfail / 0 unexpected-pass, gate OK, metrics 1.000. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
…-field-patterns test(eval): field-pattern regression fixture with xfail-tracked coverage gaps (6F.2)
…s to definitions (6F.3) The field run linked only 28% of JSX instances to definitions. Two scanner changes close the diagnosed causes: 1. scanReact honors the scanned app's own tsconfig.json (tsConfigFilePath; file discovery unchanged), so baseUrl/paths make alias imports like "@ui" resolvable through multi-hop rename barrels. Before, such usages produced no instance node at all. 2. New lazyDefinition resolution: a JSX tag naming a module-scope const X = Loadable(lazy(() => import("./Page"))) (or bare React.lazy) unwraps to the dynamic import and resolves the target module's default export. Exercised by a new PreviewPane fixture component whose variable name differs from the definition name, so only the unwrap can link it. Fixture golden: both 6F.3 expectedFail marks removed — DataGrid instances = 2 and blast radius DataGrid → {UsersPage, InvoicesPage} now pass unmarked; 100% of the fixture's component usages resolve. 2 new parser unit tests (121 total). Eval 283 pass / 0 fail / 7 xfail (all owned by 6F.4–6F.5) / 0 unexpected-pass, gate OK, metrics 1.000. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
…-instance-resolution feat(parser): resolve tsconfig-path aliases and Loadable/lazy wrappers (6F.3)
…ts as data sources (6F.4)
The field run had ~40 injectEndpoints files under src/store/api/** and 0
data-source nodes: the entire server-cache dimension was invisible.
- New "rtk-query" DataSourceKind in core (schema regenerated).
- detectStores recognizes createApi and api.injectEndpoints declarations:
rtkBaseCall resolves the (possibly imported) receiver back to its createApi
through chained injectEndpoints hops; rtkBaseUrl reads the fetchBaseQuery
baseUrl; every builder.query|mutation becomes a DataSourceNode — string-form
and object-form ({ url, method }) query configs, template params normalized
to :param form, baseUrl joined.
- Generated hooks (useXQuery, useXMutation, useLazyXQuery) register in a new
rtkHooks map; the component body walk wires fetches-from edges at call
sites, so traceLineage from a consuming component reaches its endpoint.
Fixture: 3 data sources (GET /api/users, GET /api/invoices,
POST /api/invoices/:id/pay); both 6F.4 attribution xfails removed and passing.
4 new parser tests (205 total across packages). Eval 285 pass / 0 fail /
5 xfail (all owned by 6F.5) / 0 unexpected-pass, gate OK, metrics 1.000.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
…-rtk-query feat(parser): RTK Query extractor (6F.4)
…preads, Loadable wrappers (6F.5) The field app produced 0 route nodes: its createBrowserRouter config was an imported identifier composed by spreading separately-declared route arrays, with every page element wrapped in Loadable(lazy(() => import())). - New routeArrayElements in routes.ts: a router config resolves whether it's an array literal, an imported identifier (go-to-definition), or spread-composed from other arrays; applied to the createBrowserRouter argument and every children property; as/satisfies unwrapped; hop-bounded. - resolveLazyVariable unwraps wrapper helpers around the lazy call, so routes-to lands on the imported page's default export through Loadable(lazy(() => import())). - navigates-to effects and journeys lit up as soon as route nodes existed. Field-patterns golden: all 5 remaining xfails removed (3 routes, the onClick→/users effect, the "/"→"/users" terminal journey). The fixture is now fully green with zero marks — Gate 6F's extractor criteria are met. 4 new parser tests (209 total across packages). Eval 290 pass / 0 fail / 0 xfail / 0 unexpected-pass, gate OK, metrics 1.000. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
…-object-routes feat(parser): object-config React Router recognition (6F.5)
…F.7) Field feedback: matches were "ranked only by the query's own character rarity", and find_component's real score was nested where agents misread it. - matchComponents applies IDENTIFIER_AFFINITY (x1.5) to a matched term that also names the candidate component — its name, props, or file basename, camelCase-split, tokenized, fuzzy-tolerant. "calendar" matching CalendarPanel now outranks the same text rendered incidentally elsewhere; genuinely-tied generic terms stay tied (ambiguity honesty unchanged at 1.000). Evidence lines note "(also names the component)". - Candidate gains optional top-level `score` (raw ranking score; confidence stays the calibrated signal), set by matchComponents, printed by the CLI (score=… confidence=…), inherited by MCP through the envelope JSON. 2 new core tests (211 total). Eval 290 pass / 0 fail / 0 xfail, gate OK, metrics 1.000. Candidate isn't in the generated schemas (drift gate green). Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
…-scoring-polish feat(core): identifier-affinity ranking + top-line candidate score (6F.7)
…explorer (6F.8) New `ui-lineage visualize -g <graph> -o <html>` renders the whole lineage graph as a single self-contained HTML file (graph JSON + all CSS/JS inlined, zero network requests) that opens in any browser with no server. - visualize.ts: pure renderVisualization(graph) → HTML string (trivially testable) + toViewModel trimming nodes and dropping dangling edges. The embedded JSON escapes "<" so rendered text containing "</script>" can't break out of the data script. - Client: canvas force-directed "galaxy" with grid-approximated repulsion (neighbor-cell only) that scales to thousands of nodes; node radius by degree; 9 node kinds color-coded with a legend. - Interactions: per-kind node + edge filter toggles, search-and-fly, click → detail panel (name, kind, file:line, connection count, flags) with neighbor highlight + dim-others (visual blast radius), drag-to-pin, scroll-zoom, physics pause, always-labels. Verified live in-browser on the demo-app graph: renders, filters, physics toggle, and search→select all work with zero console errors. CLI package gains a test script + vitest; 5 new tests (embed round-trip, dangling-edge drop, </script> breakout guard, self-contained output). README documents the command. Eval unaffected (290/0/0/0); typecheck clean. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
…-visualizer feat(cli): galaxy visualizer — self-contained interactive HTML graph explorer (6F.8)
Bump all packages to 0.4.0 and the generator / CLI / MCP version strings. Ships Phase 6F field-hardening from the 2026-07-15 validation feedback: punctuation-wildcard matcher fix + no-signal decline (A14), tsconfig-path alias / multi-hop barrel / Loadable(lazy) instance resolution, the RTK Query extractor, object-config React Router recognition, identifier-affinity scoring with a top-line candidate score, and the new `visualize` command (self-contained interactive HTML galaxy). README changelog updated. Verified: 7 packages build, 216 unit tests, eval 290/0/0, typecheck clean, and `npm pack` yields ui-lineage-0.4.0.tgz (both bins, visualize wired, no runtime @coderadar/* leakage). Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
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.
Release 0.4.0
Bumps all packages to 0.4.0 and the generator / CLI / MCP version strings. Ships the Phase 6F field-hardening work from the 2026-07-15 validation feedback so the tester can re-run against a real Redux/RTK-Query/React-Router codebase.
What's in it (vs 0.3.0)
|,/,-) no longer acts as a universal wildcard; gibberish declinesno-signalinstead of false high-confidence. (fix(core): punctuation-only rendered text no longer acts as a universal wildcard (6F.1, A14) #43)Loadable(lazy(() => import()))wrappers resolve to definitions; fixesblast_radiusreturning 0 and the sparse render graph. (feat(parser): resolve tsconfig-path aliases and Loadable/lazy wrappers (6F.3) #45)createApi/injectEndpoints/builder.query|mutation→ data sources; generated hooks wire per-componentfetches-from. (feat(parser): RTK Query extractor (6F.4) #46)journeys("/path")works. (feat(parser): object-config React Router recognition (6F.5) #47)score. (feat(core): identifier-affinity ranking + top-line candidate score (6F.7) #48)visualize— new command: self-contained interactive HTML galaxy. (feat(cli): galaxy visualizer — self-contained interactive HTML graph explorer (6F.8) #49)Plus the
field-patternsregression fixture (#44) that locks all of the above against recurrence.Verification
npm pack→ui-lineage-0.4.0.tgz(171 KB, 21 files), bothui-lineageandui-lineage-mcpbins,visualizewired,--versionreports 0.4.0, no runtime@coderadar/*leakageAfter merge
Publish to npm from a clean checkout of
main:Then the tester points their MCP client at
ui-lineage@0.4.0(ui-lineage-mcp) and re-runs the validation.🤖 Generated with Claude Code