Skip to content

Dependency-graph selective remap (opt-in, conservative, multi-framework — React first) #75

Description

@BenSheridanEdwards

Part of #57. Revisits the rejected core of proposals #2/#3 (see spike #59) — but as a deliberate, opt-in, multi-framework capability, not a default that weakens the gate.

Goal

Speed up the pre-push capture by re-capturing only the surfaces a change can affect, and reusing the committed base maps for the rest — without ever silently missing a regression. Build a dependency graph that, given the changed files in a commit, returns the set of surfaces whose rendered output could have changed (or all). Support the major frameworks; start with React (the hardest).

The hard constraint (why this is subtle, not a quick win)

In the committed-map model, a wrong answer is silent and fatal: if the graph says a surface is unaffected and it actually changed, its stale committed map matches the base, the diff is empty, and the regression ships green. So the graph must over-approximate — when in doubt, re-capture. It is only sound if:

  • Any non-page-local style change forces all — a CSS reset, a :root/theme token, a global stylesheet, a CSS-in-JS global, a Tailwind/theme config change, a design-system primitive. These cascade everywhere.
  • The component→surface graph is complete for anything it claims is unaffected — including dynamic import(), React.lazy, context/providers, render props, children, and component registries/CMS-driven composition. A missed edge = a missed regression.
  • Runtime theming / data-driven styling that isn't visible in the static graph is treated conservatively.

For most PRs (which touch shared CSS/tokens) the sound answer is often all, so the realistic win is on changes provably scoped to a single surface's own component + styles.

Proposed shape

  • Opt-in, default off. The full-coverage gate stays the default and the source of truth.
  • Pluggable framework adapters. A small interface affectedSurfaces(changedFiles, surfaces) → Set<key> | 'all'; ship a React/TS adapter first (lean on an existing module-graph tool — dependency-cruiser/madge — rather than reinventing it), then others.
  • Conservative core, framework-agnostic. The "global change → all" classifier and the committed-map plumbing (capture subset, reuse committed base for the rest) live in core; only the import-graph resolution is per-framework.
  • Trust-but-verify safety net. main (or a scheduled run) always captures all surfaces, so any graph hole is caught at the latest on merge, not hidden forever. Document that a PR-time miss can still slip to merge — the residual risk the adopter accepts.
  • Loud about what it skipped. Log every surface it chose not to re-capture and why, so a reviewer can sanity-check.

Open questions to resolve in the spike

  1. Can "is this style change global?" be classified soundly across CSS Modules / styled-components / Tailwind / vanilla / CSS-in-JS? If not reliably, the conservative default is all on any CSS/config change — does that leave enough win?
  2. How complete can a static React graph be against dynamic composition, and how do we fail closed (→ all) when resolution is uncertain?
  3. Does the trust-but-verify-on-main safety net make PR-time selection an acceptable risk, or is consumer-declared mapping (the maintainer asserts completeness) the only honest opt-in?
  4. Per-framework adapter API + which existing graph tool to build on.

Acceptance

A design (and a React/TS proof-of-concept) that is opt-in, conservative (fails to all), framework-pluggable, and loud about skips, with the trust-but-verify net — or a documented decision that selective remap stays consumer-declared. The default gate must remain 100%-sound regardless.

Context: as of 3.0.0 the gate ships full coverage — every surface captured, changes determined by measuring the map (the map is the oracle), not by guessing. This issue is the path to making it faster on large apps without giving that up.

Metadata

Metadata

Assignees

No one assigned

    Labels

    enhancementNew feature or requestquestionFurther information is requested

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions