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-mismatchDriftType 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>
CopilotAI review requested due to automatic review settings June 11, 2026 13:54

CopilotAI 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
FileDescription
README.mdUpdates CLI command description to mention dspack contract surface.
pnpm-lock.yamlLocks new watcher dependency (ajv).
packages/watcher/src/crossSurfaceDrift/normalize.tsWidens surface ID typing to include contract.
packages/watcher/src/crossSurfaceDrift/cliCrossSurfaceDrift.tsAdds --dspack flag/config loading, contract+code-only mode, and reporting.
packages/watcher/src/crossSurfaceDrift/analyze.tsAdds contract snapshot construction and contract-vs-live comparisons.
packages/watcher/src/crossSurfaceDrift/tests/contractDrift.test.tsUnit tests for contract drift semantics + no-contract regression guard.
packages/watcher/src/crossSurfaceDrift/tests/cliContractE2e.test.tsCLI e2e tests for --dspack behavior with live surfaces down.
packages/watcher/src/contractSurface/types.tsDefines minimal dspack document types used by the watcher.
packages/watcher/src/contractSurface/surface.tsMaps dspack component entries to drift SurfaceProp + derived variants.
packages/watcher/src/contractSurface/schema/README.mdDocuments schema provenance and pinning strategy.
packages/watcher/src/contractSurface/schema/dspack.v0.2.schema.jsonVendored dspack v0.2 JSON schema.
packages/watcher/src/contractSurface/schema/dspack.v0.1.schema.jsonVendored dspack v0.1 JSON schema.
packages/watcher/src/contractSurface/loadContract.tsImplements file loader + Ajv validation for dspack v0.1/v0.2.
packages/watcher/src/contractSurface/index.tsExposes contract surface public API (load + accessors).
packages/watcher/src/contractSurface/tests/surface.test.tsTests mapping/lookup rules for contract surface accessors.
packages/watcher/src/contractSurface/tests/loadContract.test.tsTests loader acceptance/rejection paths and error messages.
packages/watcher/src/fixtures/contract/shadcn-demo.dspack.jsonAdds a committed dspack fixture for self-contained tests.
packages/watcher/src/fixtures/contract/README.mdDocuments contract fixture files used by tests.
packages/watcher/src/fixtures/contract/invalid-version.dspack.jsonFixture for unsupported dspack version rejection.
packages/watcher/src/fixtures/contract/invalid-schema.dspack.jsonFixture for schema violation rejection.
packages/watcher/package.jsonAdds ajv dependency (watcher-only).
packages/shared/src/surfaceMetadata.tsExtends surface taxonomy with surfaceType: 'contract'.
packages/shared/src/crossSurfaceDrift.tsAdds contract surface identifiers, findings fields, and contract data types.
packages/shared/src/configLoader.tsAdds default + validation/merge support for contract.dspackPath.
packages/shared/src/config.tsAdds config schema for contract surface (contract.dspackPath).
packages/cli/src/commands/design.tsUpdates af design drift help text to mention contract + example flag.
docs/cli-reference.mdDocuments af design drift + --dspack behavior and semantics.
docs/adapter-model.mdDocuments contract surface and why it is not a DesignAdapter.
claude.mdUpdates repo guidance to mention contract surface constraints.
Files not reviewed (1)
  • pnpm-lock.yaml: Language not supported

Comment threadpackages/watcher/src/crossSurfaceDrift/analyze.ts
Comment threadpackages/watcher/src/crossSurfaceDrift/analyze.ts
Comment threadpackages/watcher/src/crossSurfaceDrift/analyze.ts
@ryandmonk
ryandmonk merged commit 273e12d into mainJun 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 freeto 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

@ryandmonk