Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
34 changes: 34 additions & 0 deletions API.md
Original file line number Diff line number Diff line change
Expand Up @@ -410,6 +410,40 @@ No timestamps are included in the manifest so that bundles remain deterministic.

---

### `generate-contract` (Phase 0)

Extracts a deterministic contract artifact from a Next.js app by analyzing app code. **Extraction only** — no enforcement, no network calls. Output is schema-valid; extracted-only fields live under `x_extracted`.

**Synopsis:**
```bash
interfacectl generate-contract --app-root <path> --surface <id> [--out <path>] [--report-out <path>] [--schema <path>]
```

**Description:**
- Scans the app directory for routes (app router), layout shell (`app/layout.tsx` or `app/(shell)/layout.tsx`), `@surfaces/ui` component imports, and `/auth` routes.
- Writes a contract JSON (default `contracts/generated/<surfaceId>.contract.json`) and an extraction report (default `contracts/generated/<surfaceId>.extraction.json`).
- Validates the generated contract against the schema before writing. Running the command twice produces identical output (stable key order, no timestamps).

**Options:**

| Option | Description | Default |
|--------|-------------|---------|
| `--app-root <path>` | Path to the Next.js app root (directory containing `app/`) | (required) |
| `--surface <id>` | Surface identifier (e.g. `surfaces-web`) | (required) |
| `--out <path>` | Output path for contract JSON | `contracts/generated/<surfaceId>.contract.json` |
| `--report-out <path>` | Output path for extraction report | `contracts/generated/<surfaceId>.extraction.json` |
| `--schema <path>` | Optional path to the contract schema JSON file | Bundled schema |

**Exit Codes:**
- `0`: Contract and report written successfully
- `1`: Schema validation failed for generated contract

**Phase 0 scope:** Routes, hasShell, designSystemComponents (from `@surfaces/ui`), authAware. Other fields use placeholders or defaults; the report lists warnings for any omitted extraction.

**Phase 0 guardrails:** No Babel or heavy AST. Uses filesystem + regex for determinism, debuggability, and minimal deps. See [docs/plans/phase-0-extraction-guardrails.md](docs/plans/phase-0-extraction-guardrails.md) for extraction limits and when AST tooling may be added.

---

## Traceability fields (Phase 2)

Diff and enforce JSON output include optional traceability fields for correlation and debugging. These fields are additive and optional; existing consumers should keep working.
Expand Down
94 changes: 92 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -12,12 +12,14 @@ Before planning or implementing any feature, read "docs/strategy.md" and produce

See "docs/feature-plan.template.md".

This repository contains two packages:
This repository contains three packages:

- **`@surfaces/interfacectl-validator`** — Core validation library with TypeScript types, schema validation, and bundled contract schema definitions. Provides the foundation for contract validation.

- **`@surfaces/interfacectl-cli`** — Command-line interface that consumes the validator to run contract checks from any repository. Most users only need this package.

- **`@surfaces/interfacectl-extractor`** — Library that extracts a contract from a Next.js app (Phase 0). Used by the CLI `generate-contract` command. Exports `extractContractFromNextApp({ appRoot, surfaceId })`.

## Requirements

- **Node.js**: >=18.20.0 or >=20.10.0 (required for `with { type: "json" }` import syntax support)
Expand All @@ -43,7 +45,7 @@ For detailed command documentation, see [API.md](API.md).

## Commands Overview

The CLI provides three main commands:
The CLI provides four main commands:

### `validate`

Expand All @@ -69,10 +71,98 @@ Enforces policy on interface contracts using configurable enforcement modes: `fa
interfacectl enforce [options]
```

### `compile`

Compiles a validated interface contract into a deterministic, runtime-readable bundle. The bundle includes a manifest, a normalized contract, per-surface files, and per-constraint files for downstream tools to consume.

This command does **not** perform enforcement or runtime gating. It produces a stable artifact intended for inspection, tooling, or future runtime consumption.

```bash
interfacectl compile --contract <path> --out <dir>
```

### `generate-contract` (Phase 0)

Extracts a **deterministic contract artifact** from a Next.js app by analyzing app code and config. This is **contract extraction only** — no enforcement, no network calls.

**Phase 0 scope:** Routes (app router), layout shell presence (`app/layout.tsx` or `app/(shell)/layout.tsx`), design system usage (`@surfaces/ui` component imports), and auth posture (`/auth` routes). Values that cannot be extracted safely are omitted and reported as warnings in the extraction report.

**Phase 0 guardrails:** No Babel or heavy AST frameworks. Extraction uses filesystem walks and regex for determinism, debuggability, and minimal dependency surface. See [docs/plans/phase-0-extraction-guardrails.md](docs/plans/phase-0-extraction-guardrails.md) for what we extract, what we omit, and when AST tooling may be added in a later phase.

Outputs:

- **Contract:** `contracts/generated/<surfaceId>.contract.json` — schema-valid contract with extracted data under `x_extracted`.
- **Report:** `contracts/generated/<surfaceId>.extraction.json` — machine-readable extraction summary and warnings for debugging.

```bash
interfacectl generate-contract --app-root <path> --surface <surfaceId> [--out <path>] [--report-out <path>]
```

Running the command twice produces identical contract and report (stable key order, no timestamps). See [API.md](API.md) for full options.

For complete command documentation with all options, exit codes, and output formats, see [API.md](API.md).

**Generation-time gating:** `interfacectl validate` is the canonical command for contract compliance. Use it to gate changes before merge or deployment. For local use and CI, run validate with your contract path and, for deterministic exit codes, use `--exit-codes v2`. The command `enforce --mode fail` is optional; it runs a structural diff then applies a policy threshold and is useful when you want to block on diff severity separately from compliance.

## Lifecycle overview

interfacectl separates interface governance into clear phases:

- **Validate**
Enforces contract compliance at generation time.

- **Diff / Enforce**
Detects and classifies drift with traceability (stableId, contractRef, ruleRef).

- **Compile**
Produces a deterministic, runtime-readable bundle (manifest, normalized contract, surfaces, constraints) that serves as the handoff point to other tools.

- **Runtime consumption (framing only)**
Consumption semantics are documented in Phase 4. No runtime enforcement or loaders exist in this repo.

See:
- `docs/plans/phase-3-compile-directory-bundle.md`
- `docs/plans/phase-4-runtime-consumption-framing.md`

### Lifecycle diagram

```
Design / Generation time
────────────────────────

Contract
│
▼
validate
│
▼
diff / enforce
│
▼
compile
│
▼
┌─────────────────────────────┐
│ Deterministic bundle │
│ - manifest.json │
│ - contract.normalized.json │
│ - surfaces/*.json │
│ - constraints/*.json │
└─────────────────────────────┘

Runtime (not implemented here)
──────────────────────────────

bundle
│
▼
[consumer]
│
├─ read / reference
├─ observe / compare
└─ explain (future)
```

## Usage Examples

### Validation
Expand Down
24 changes: 12 additions & 12 deletions docs/contract-baseline.md
Original file line number Diff line number Diff line change
@@ -1,16 +1,16 @@
# Contract baseline

This document describes the minimal authoritative interface contract used by interfacectl. It lets you understand what the contract requires without reading the JSON schema file alone. For the exact schema, see the schema location below.
This document defines the minimal authoritative interface contract enforced by interfacectl. It exists so builders can understand what a valid contract must declare without reading the JSON schema file directly. This document describes intent and requirements. The schema remains the source of truth.

## Required top-level fields

Every contract must include:

- **contractId** (string, non-empty): Identifier for the contract. Used for traceability.
- **version** (string): Semantic version. Must match the pattern `X.Y.Z` (e.g. `1.0.0`).
- **version** (string): Semantic version of the contract. Must match the pattern `X.Y.Z` (for example, `1.0.0`). Used to track compatibility over time.
- **surfaces** (array, at least one item): List of surfaces (apps or UIs) that the contract governs. Each surface has its own required and allowed values.
- **sections** (array, at least one item): List of sections that surfaces can implement. Each section has an id, intent, and description.
- **constraints** (object): Global constraints. Today the only required part is **motion**: an object with **allowedDurationsMs** (array of integers, milliseconds) and **allowedTimingFunctions** (array of strings).
- **constraints** (object): Global constraints. At present, the only required constraint is **motion**, defined as an object with **allowedDurationsMs** (array of integers, milliseconds) and **allowedTimingFunctions** (array of strings). Additional constraint categories may be added in future schema versions.

## Per-surface required fields

Expand All @@ -20,33 +20,33 @@ Each entry in **surfaces** must have:
- **displayName** (string, non-empty): Human-readable name.
- **type** (string): Either `"web"` or `"cli"`.
- **requiredSections** (array): Section ids that this surface must implement. Section id pattern: `a-z0-9` and dots (e.g. `main.hero`).
- **allowedFonts** (array, at least one string): Font family values that the surface may use. Implementations are checked against this list.
- **layout** (object): Must include **maxContentWidth** (number, minimum 1). Optionally **requiredContainers** (array of strings) and **pageFrame** (containerSelector, containerMaxWidthPx, paddingXpx, and optional alignment and enforcement).
- **allowedFonts** (array, at least one string): Font family values that the surface may use. Implementations are checked against this list. This list is enforced during compliance evaluation.
- **layout** (object): Must include **maxContentWidth** (number, minimum 1). Optional fields include **requiredContainers** (array of strings) and **pageFrame**. When present, **pageFrame** is enforced according to the contract definition.

## Per-section required fields

Each entry in **sections** must have:

- **id** (string): Matches the pattern used in surface requiredSections.
- **intent** (string, non-empty): Purpose of the section. Not yet used as an enforcement hook; documented for clarity.
- **intent** (string, non-empty): Purpose of the section. Documented for clarity. Not currently used as an enforcement input.
- **description** (string, non-empty): Human-readable description.

## Optional: color policy

The top-level **color** object is optional. When present it can include:

- **sourceOfTruth**: If `type` is `"tokens"`, you must provide **tokenNamespaces** (array of strings). Used to allow or disallow CSS variable namespaces for colors.
- **rawValues**: **policy** is required (`"off"`, `"warn"`, or `"strict"`). Optional **allowlist** and **denylist** arrays of color values. Controls whether raw color literals (hex, rgb, hsl) are allowed.
- **rawValues**: **policy** is required (`"off"`, `"warn"`, or `"strict"`). Optional **allowlist** and **denylist** arrays of color values. Controls whether raw color literals (hex, rgb, hsl) are permitted.
- **semantics**: Optional **roles** (accent, text, background, border) each with **enforcement** (`"off"`, `"warn"`, `"strict"`).
- **consistency**: Optional **acrossSurfaces** with **enforcement** and **signals**.
- **consistency**: Optional **acrossSurfaces** object with **enforcement** level and **signals** used for reporting.

## Deprecated fields

- **allowedColors** (per-surface): Deprecated. The schema still accepts it for compatibility. Prefer the top-level **color** policy with **sourceOfTruth** and **rawValues**. Migration: move per-surface color allowlists into **color.sourceOfTruth** (e.g. token namespaces) or **color.rawValues** (allowlist/denylist). The CLI emits a deprecation warning when allowedColors is present.
- **allowedColors** (per-surface): Deprecated. The schema still accepts it for compatibility. Prefer the top-level **color** policy with **sourceOfTruth** and **rawValues**. Migration: move per-surface color allowlists into **color.sourceOfTruth** (e.g. token namespaces) or **color.rawValues** (allowlist/denylist). The CLI emits a deprecation warning when allowedColors is present. Deprecated fields are accepted for compatibility but may be removed in a future major version.

## Where contract semantics live in the repo

- **Schema file:** `packages/interfacectl-validator/src/schema/surfaces.web.contract.schema.json`. This is the authoritative JSON Schema. The CLI uses a bundled copy; you can override with `--schema <path>`.
- **Schema file:** `packages/interfacectl-validator/src/schema/surfaces.web.contract.schema.json`. This is the authoritative JSON Schema. The CLI uses a bundled copy of this schema by default. You may override it with `--schema <path>` if needed.
- **Structure validation:** The function **validateContractStructure** in `packages/interfacectl-validator/src/index.ts` loads the schema and validates contract JSON (via AJV). It returns errors or the parsed contract.
- **Compliance evaluation:** The function **evaluateContractCompliance** in the same file takes a validated contract and a list of surface descriptors. It returns a validation summary with per-surface reports and violations. Compliance rules (fonts, colors, layout, motion, sections, pageFrame) are implemented in **evaluateSurfaceCompliance** in that file.
- **CLI entry point:** The validate command is implemented in `packages/interfacectl-cli/src/commands/validate.ts`. It calls **collectSurfaceDescriptors** (in `packages/interfacectl-cli/src/descriptors/static-analysis.ts`) to gather descriptors from the codebase, then passes the contract and descriptors to **evaluateContractCompliance**. The CLI maps violation types to stable codes (e.g. `font.disallowed`) in that command file.
- **Compliance evaluation:** The function **evaluateContractCompliance** in the same file takes a validated contract and a list of surface descriptors. It returns a validation summary with per-surface reports and violations. Compliance rules for fonts, colors, layout, motion, sections, and pageFrame are implemented in **evaluateSurfaceCompliance** in the same file.
- **CLI entry point:** The validate command is implemented in `packages/interfacectl-cli/src/commands/validate.ts`. It calls **collectSurfaceDescriptors** (in `packages/interfacectl-cli/src/descriptors/static-analysis.ts`) to gather descriptors from the codebase, then passes the contract and descriptors to **evaluateContractCompliance**. The CLI maps violation types to stable codes in this command implementation.
47 changes: 47 additions & 0 deletions docs/plans/phase-0-extraction-guardrails.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
# Phase 0 Extraction Guardrails

**Scope:** Contract extraction from Next.js app code (`interfacectl generate-contract`). Phase-scoped and revisitable.

---

## No heavy AST in Phase 0

We avoid Babel, TypeScript compiler API, or other full AST frameworks in Phase 0 for:

- **Determinism** — Filesystem walks and regex are predictable; AST tooling can vary by version and config.
- **Debuggability** — Extraction logic is easy to trace; failures map directly to file paths and patterns.
- **Minimal dependency surface** — No parser or transpiler deps; faster installs and fewer version skew risks.

---

## What we extract (Phase 0)

| Signal | Method |
|-------|--------|
| Routes | `readdir` over `app/`; page.tsx, route.ts patterns |
| Layout shell | `access()` for `app/layout.tsx`, `app/(shell)/layout.tsx` |
| Design system usage | Regex scan for `@surfaces/ui` imports in .tsx/.ts/.jsx/.js |
| Auth posture | `access()` for `app/auth/` directory |

---

## What we do not extract (Phase 0)

- JSX structure, component trees, or props
- Section IDs from `data-contract-section` or similar
- Fonts, colors, or layout values from CSS/JS
- Dynamic route params or middleware

These require full parsing. Omit and emit a warning in the extraction report.

---

## When to add AST tooling

Add Babel, ts-morph, or similar **only when**:

1. A concrete extraction requirement is approved (e.g. "extract section IDs from data attributes").
2. Regex or filesystem-based extraction demonstrably fails for that requirement.
3. A follow-up plan documents the new dependency and determinism guarantees.

This is a default, not a block. Later phases may introduce AST-based extraction under a separate plan.
1 change: 1 addition & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@
},
"pnpm": {
"overrides": {
"@surfaces/interfacectl-extractor": "link:packages/interfacectl-extractor",
"@surfaces/interfacectl-validator": "link:packages/interfacectl-validator"
}
},
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
export interface GenerateContractOptions {
appRoot: string;
surfaceId: string;
outPath?: string;
reportOutPath?: string;
schemaPath?: string;
}
export declare function runGenerateContractCommand(options: GenerateContractOptions): Promise<number>;
//# sourceMappingURL=generate-contract.d.ts.map

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

Loading
Loading