Skip to content

Add dspack contract surface to cross-surface drift analysis#3

Merged
ryandmonk merged 1 commit into
mainfrom
feat/dspack-contract-surface
Jun 11, 2026
Merged

Add dspack contract surface to cross-surface drift analysis#3
ryandmonk merged 1 commit into
mainfrom
feat/dspack-contract-surface

Conversation

@ryandmonk

Copy link
Copy Markdown
Collaborator

What

Adds a read-only dspack contract surface to the cross-surface drift engine. af design drift can now compare component metadata across Figma, Storybook, and code against a declared design-system contract — a dspack v0.1/v0.2 file committed to source control:

af design drift Button --dspack ./my-system.dspack.json
# or set contract.dspackPath in af.config.json

This is the first code-level integration between AF and the dspack ecosystem, completing the loop: dspack-export generate → commit the snapshot → serve it to agents via ds-mcp → AF polices it over time.

How it works

  • packages/watcher/src/contractSurface/ (new): Ajv loader that validates against vendored v0.1/v0.2 schemas (pinned to dspack commit 7008c3e, provenance in schema/README.md), mirroring ds-mcp's reference loader semantics — a file that loads in ds-mcp loads here. Read-only accessors map dspack component entries into the drift engine's prop/variant vocabulary (enum values → high-confidence constrained comparisons).
  • Drift engine: a new compareContractSurface() covers component presence, prop inventory vs. code, and enum-variant coverage vs. code and Figma. The three pre-existing comparison functions are untouched.
  • Direction semantics: the contract declaring something a live surface lacks is genuine drift (warn); code having something the contract lacks is a staleness signal (contract-staleness:* findings, info) with a one-line remediation hint pointing at dspack-export regeneration.
  • CLI: the contract counts toward the 2-surface availability gate (contract + code analysis works with Figma and Storybook down), serves as the component inventory when Storybook is unavailable, and renders a Contract ✓/✗ status segment.

Boundaries preserved

  • The contract surface is deliberately not a DesignAdapter and is never registered in the adapter registry (the drift CLI treats the registry's first available adapter as the Figma surface). Rationale documented in docs/adapter-model.md.
  • Surface taxonomy: classified as surfaceType: 'contract', read-only, external-non-authoritative — the descriptor layer carries no authority into reconciliation.
  • Out of scope by design: token drift, CI gating/thresholds, reconciliation changes (Phase 14F precedence untouched), write paths, dspack generation, ds-mcp involvement.

Backward compatibility

Dormant by default: with no --dspack flag and no contract config key, no new code path executes. All type widenings are additive, and a regression-guard test asserts no-contract reports contain no contract keys, no contract findings, and only the three legacy surface keys.

Testing

  • Full watcher suite: 1986/1986 (was 1953 before; +33 new tests, zero regressions; suite re-run after the type widening alone to prove it non-breaking in isolation)
  • New: 6 loader tests, 10 mapping tests, 12 analyzer contract tests, 5 CLI e2e tests driving main() against the committed shadcn-demo fixture (copied from dspack-export's golden fixture, self-contained — no cross-repo reference at test time) and the real demos/react-demo-app code surface
  • CLI package: 50/50; monorepo-wide pnpm typecheck clean
  • New dependency: ajv@^8.17.0 (watcher only)

Reviewer notes

  • contract-mismatch DriftType is defined but unused in this slice — reserved for value-level comparisons (e.g., default-value mismatch) in a follow-up.
  • Against AF's own demo app the sample output is warn-heavy because the demo Button intentionally differs from the shadcn fixture; a demo-matched contract fixture for a "No drift detected" happy path is a candidate follow-up before announcing.

🤖 Generated with Claude Code

Add a read-only dspack contract surface so `af design drift` can compare
component metadata across Figma, Storybook, and code against a declared
design-system contract (a dspack v0.1/v0.2 file committed to source control).
This is the first code-level integration between AF and the dspack ecosystem
(dspack / dspack-export / ds-mcp).

- shared: widen drift types with a DriftSurfaceId union ('figma' |
  'storybook' | 'code' | 'contract'), optional contract surface/finding
  members, ContractComponentData, and a 'contract' SurfaceType (descriptor
  only). All additive — no-contract reports are shape-identical to before,
  regression-guarded by test.
- shared: new contract.dspackPath config section (af.config.json).
- watcher: new contractSurface/ module — Ajv loader mirroring ds-mcp's
  reference semantics with vendored v0.1/v0.2 schemas (pinned to dspack
  commit 7008c3e), and read-only component/prop/enum-variant accessors.
  Deliberately NOT a DesignAdapter and never registered in the adapter
  registry.
- watcher: compareContractSurface() in the drift engine — presence, prop
  inventory vs code, and enum-variant coverage vs code and Figma. Contract
  declaring something a surface lacks is drift (warn); code having something
  the contract lacks is a staleness signal (contract-staleness:* findings,
  info) pointing at dspack-export regeneration. Existing comparison
  functions untouched.
- CLI: `af design drift --dspack <file>`, contract counts toward the
  2-surface availability gate, contract-as-inventory fallback when
  Storybook is down, Contract status segment, one staleness remediation
  hint per report.
- tests: 33 new (loader, mapping, analyzer, CLI e2e against the committed
  shadcn-demo fixture); full watcher suite 1986/1986.

Out of scope by design: token drift, CI gating, reconciliation changes
(Phase 14F semantics untouched), write paths, dspack generation, ds-mcp
involvement.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Copilot AI review requested due to automatic review settings June 11, 2026 13:54

Copilot AI left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Adds a new read-only dspack contract surface to AF’s cross-surface drift analysis so af design drift can compare component inventories/props/enum variants across Figma, Storybook, code (AST), and an optional dspack JSON contract (v0.1/v0.2), with config + CLI flag support and comprehensive watcher tests.

Changes:

  • Introduces packages/watcher/src/contractSurface/ with Ajv-based loader + vendored dspack schemas and mapping helpers to the drift engine vocabulary.
  • Extends the drift engine/types to include a contract surface (DriftSurfaceId) and adds contract-aware comparisons + CLI output/inventory behavior when Storybook/Figma are unavailable.
  • Adds config/docs/tests and watcher dependency updates to support the new surface end-to-end.

Reviewed changes

Copilot reviewed 28 out of 29 changed files in this pull request and generated 4 comments.

Show a summary per file
File Description
README.md Updates CLI command description to mention dspack contract surface.
pnpm-lock.yaml Locks new watcher dependency (ajv).
packages/watcher/src/crossSurfaceDrift/normalize.ts Widens surface ID typing to include contract.
packages/watcher/src/crossSurfaceDrift/cliCrossSurfaceDrift.ts Adds --dspack flag/config loading, contract+code-only mode, and reporting.
packages/watcher/src/crossSurfaceDrift/analyze.ts Adds contract snapshot construction and contract-vs-live comparisons.
packages/watcher/src/crossSurfaceDrift/tests/contractDrift.test.ts Unit tests for contract drift semantics + no-contract regression guard.
packages/watcher/src/crossSurfaceDrift/tests/cliContractE2e.test.ts CLI e2e tests for --dspack behavior with live surfaces down.
packages/watcher/src/contractSurface/types.ts Defines minimal dspack document types used by the watcher.
packages/watcher/src/contractSurface/surface.ts Maps dspack component entries to drift SurfaceProp + derived variants.
packages/watcher/src/contractSurface/schema/README.md Documents schema provenance and pinning strategy.
packages/watcher/src/contractSurface/schema/dspack.v0.2.schema.json Vendored dspack v0.2 JSON schema.
packages/watcher/src/contractSurface/schema/dspack.v0.1.schema.json Vendored dspack v0.1 JSON schema.
packages/watcher/src/contractSurface/loadContract.ts Implements file loader + Ajv validation for dspack v0.1/v0.2.
packages/watcher/src/contractSurface/index.ts Exposes contract surface public API (load + accessors).
packages/watcher/src/contractSurface/tests/surface.test.ts Tests mapping/lookup rules for contract surface accessors.
packages/watcher/src/contractSurface/tests/loadContract.test.ts Tests loader acceptance/rejection paths and error messages.
packages/watcher/src/fixtures/contract/shadcn-demo.dspack.json Adds a committed dspack fixture for self-contained tests.
packages/watcher/src/fixtures/contract/README.md Documents contract fixture files used by tests.
packages/watcher/src/fixtures/contract/invalid-version.dspack.json Fixture for unsupported dspack version rejection.
packages/watcher/src/fixtures/contract/invalid-schema.dspack.json Fixture for schema violation rejection.
packages/watcher/package.json Adds ajv dependency (watcher-only).
packages/shared/src/surfaceMetadata.ts Extends surface taxonomy with surfaceType: 'contract'.
packages/shared/src/crossSurfaceDrift.ts Adds contract surface identifiers, findings fields, and contract data types.
packages/shared/src/configLoader.ts Adds default + validation/merge support for contract.dspackPath.
packages/shared/src/config.ts Adds config schema for contract surface (contract.dspackPath).
packages/cli/src/commands/design.ts Updates af design drift help text to mention contract + example flag.
docs/cli-reference.md Documents af design drift + --dspack behavior and semantics.
docs/adapter-model.md Documents contract surface and why it is not a DesignAdapter.
claude.md Updates repo guidance to mention contract surface constraints.
Files not reviewed (1)
  • pnpm-lock.yaml: Language not supported

Comment thread packages/watcher/src/crossSurfaceDrift/analyze.ts
Comment thread packages/watcher/src/crossSurfaceDrift/analyze.ts
Comment thread packages/watcher/src/crossSurfaceDrift/analyze.ts
Comment thread packages/watcher/src/crossSurfaceDrift/cliCrossSurfaceDrift.ts
@ryandmonk ryandmonk merged commit 273e12d into main Jun 11, 2026
2 checks passed
ryandmonk added a commit that referenced this pull request Jun 12, 2026
…ew-followup

fix: land dspack contract-surface review fixes that missed PR #3
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