Skip to content

chore(release): 0.4.0#50

Merged
officialCodeWork merged 97 commits into
mainfrom
chore/release-0.4.0
Jul 15, 2026
Merged

chore(release): 0.4.0#50
officialCodeWork merged 97 commits into
mainfrom
chore/release-0.4.0

Conversation

@officialCodeWork

Copy link
Copy Markdown
Owner

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)

Plus the field-patterns regression fixture (#44) that locks all of the above against recurrence.

Verification

  • 7 packages build · 216 unit tests green · eval 290 pass / 0 fail, all honesty metrics 1.000 · typecheck clean
  • npm packui-lineage-0.4.0.tgz (171 KB, 21 files), both ui-lineage and ui-lineage-mcp bins, visualize wired, --version reports 0.4.0, no runtime @coderadar/* leakage

After merge

Publish to npm from a clean checkout of main:

pnpm install && pnpm build && cd packages/cli && npm publish

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

DeepCodeWork and others added 30 commits July 13, 2026 02:05
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>
DeepCodeWork and others added 29 commits July 15, 2026 11:58
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>
…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>
@officialCodeWork
officialCodeWork merged commit e9da138 into main Jul 15, 2026
1 check passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants