diff --git a/.github/ISSUE_TEMPLATE/feature.yml b/.github/ISSUE_TEMPLATE/feature.yml new file mode 100644 index 0000000..0e7a5c5 --- /dev/null +++ b/.github/ISSUE_TEMPLATE/feature.yml @@ -0,0 +1,51 @@ +name: Feature +description: Propose a feature aligned to Surfaces strategy +title: "[Feature] " +labels: ["feature"] +body: + - type: markdown + attributes: + value: | + Read docs/strategy.md before filing. + - type: textarea + id: summary + attributes: + label: Summary + description: Describe the feature in 2 to 4 sentences. + validations: + required: true + - type: textarea + id: alignment + attributes: + label: Strategy alignment + description: Explain how this strengthens the decision filter sentence in docs/strategy.md. + validations: + required: true + - type: dropdown + id: enforcement_timing + attributes: + label: Enforcement timing + options: + - Generation time + - Runtime + - Both + validations: + required: true + - type: dropdown + id: violation_handling + attributes: + label: Violation handling + options: + - Block + - Correct + - Constrain + - Signal only + validations: + required: true + - type: textarea + id: acceptance + attributes: + label: Acceptance criteria + description: Provide 5 to 10 testable criteria. + validations: + required: true \ No newline at end of file diff --git a/.github/PULL_REQUEST_HYGIENE.md b/.github/PULL_REQUEST_HYGIENE.md new file mode 100644 index 0000000..b1b8e3c --- /dev/null +++ b/.github/PULL_REQUEST_HYGIENE.md @@ -0,0 +1,40 @@ +# Repo hygiene: scratch area and planning discipline + +## Goal + +Eliminate untracked noise and make it hard for planning drafts or local artifacts to leak into feature PRs. + +## Scope (strict) + +This PR is about repository hygiene only. No behavior, strategy, or feature changes. + +## What changed + +- **scratch/** + Added a tracked directory with contents gitignored. Use it for WIP planning and local artifacts so they don’t show up as untracked in normal work. `.gitkeep` keeps the directory in the repo; everything else in `scratch/` is ignored. + +- **.gitignore** + Updated to ignore `scratch/*` while keeping `scratch/.gitkeep`, with a short comment. + +## What to keep vs not commit (canonical) + +**Keep and commit (already in repo or in this PR):** +- `.github/pull_request_template.md` – enforces discipline. +- `docs/agent-planning-prompt.md` – durable instruction source for coding agents (if you use it that way). +- `docs/plans/phase-3-post-merge-checklist.md` – intentional. + +**Do not commit yet:** +- AGENTS.md if it is exploratory or incomplete. +- Phase drafts that are not approved (e.g. future phases). +- Cursor-local planning artifacts. + +Put WIP and unapproved drafts in `scratch/`; they will stay local. + +## Acceptance criteria + +- [ ] `git status` is clean after normal work (no stray planning files). +- [ ] No planning drafts show up as untracked when working on feature PRs. +- [ ] PR template is enforced by default. +- [ ] No functional code changes. + +This PR should be tiny and boring. That’s a feature. diff --git a/.github/pull_request_template.md b/.github/pull_request_template.md new file mode 100644 index 0000000..253296a --- /dev/null +++ b/.github/pull_request_template.md @@ -0,0 +1,105 @@ +## Strategy check + +- [ ] I read "docs/strategy.md" +- [ ] This PR strengthens the decision filter sentence +- [ ] Enforcement timing is explicit: generation time, runtime, or both +- [ ] Violation handling is defined: blocked, corrected, constrained, or signaled +- [ ] CLI behavior is tied to contract semantics, not free-form heuristics + +## What changed +Describe the change. + +## Why it matters +Explain the user-facing and system-facing impact. + +## Contract and enforcement notes +1. Contract fields added or changed +2. Enforcement point impacted +3. Expected behavior on violation + +## Tests +List tests added or updated. +## Phase 0 and Phase 1 PR Review Checklist + +This PR claims to implement work from: +- docs/plans/contract-first-enforcement-alignment.md +- docs/plans/phase-0-1-execution-plan.md + +Complete this checklist before requesting review. + +--- + +## 1. Scope gate + +- [ ] Changes are limited to Phase 0 or Phase 1 scope. +- [ ] No implementation of `compile`, `explain`, new validation rules, or schema changes. +- [ ] No CI job was added to enforce interfacectl unless explicitly approved. + +If any box is unchecked, this PR is out of scope and should not merge. + +--- + +## 2. Strategy and plan alignment + +- [ ] I read docs/strategy.md. +- [ ] I read docs/plans/contract-first-enforcement-alignment.md. +- [ ] This PR preserves the decision that `validate` is the canonical generation-time gate. +- [ ] The README planning constraint remains intact. + +--- + +## 3. Gating clarity + +- [ ] Documentation clearly states that `interfacectl validate` is the canonical contract compliance gate. +- [ ] Documentation does not imply that `enforce --mode fail` is the canonical compliance gate. +- [ ] If `enforce --mode fail` is referenced, it is positioned as optional policy-based enforcement on structural diff. + +--- + +## 4. Exit code determinism + +- [ ] All new or updated CI examples explicitly use `--exit-codes v2`. +- [ ] Environment variable names referenced in docs are confirmed in code. +- [ ] Docs do not mix v1 and v2 exit code examples. + +--- + +## 5. Contract documentation quality + +- [ ] Docs describe the minimal authoritative contract and required fields. +- [ ] Deprecated contract elements are called out explicitly. +- [ ] Docs point to real schema and validation entry points. + +--- + +## 6. Gate alias checks (only if applicable) + +Apply this section only if this PR adds or modifies a `gate` command. + +- [ ] `gate` delegates to `validate` only. +- [ ] `gate` introduces no new behavior or implicit workflows. +- [ ] `gate` does not invoke diff or policy logic implicitly. +- [ ] `gate` is registered in the CLI command registration entry point. +- [ ] Help text and docs state clearly that `gate` is an alias for `validate`. + +--- + +## 7. Verification + +- [ ] Documented commands run successfully against existing fixtures. +- [ ] All referenced file paths exist in the repo. +- [ ] Any referenced environment variables can be verified quickly in code. + +--- + +## What changed + +Describe the change. + +## Why it matters + +Explain the user-facing and system-facing impact. + +## Tests + +List tests added or updated. \ No newline at end of file diff --git a/.gitignore b/.gitignore index 66f7bd4..72ec7f2 100644 --- a/.gitignore +++ b/.gitignore @@ -2,4 +2,7 @@ node_modules .pnpm-store .DS_Store .changeset/*-changes.md -scratch/ +# WIP planning and local artifacts; directory exists, contents ignored +scratch/* +!scratch/.gitkeep +.cursor/ diff --git a/docs/agent-planning-prompt.md b/docs/agent-planning-prompt.md new file mode 100644 index 0000000..c676d5f --- /dev/null +++ b/docs/agent-planning-prompt.md @@ -0,0 +1,16 @@ +You are a coding agent working in this repo. + +Required reading: +- docs/strategy.md +- docs/feature-plan.template.md + +Task: +1. Read docs/strategy.md. +2. Produce a Feature Plan using docs/feature-plan.template.md. +3. Only after the plan is complete, propose code changes. + +Hard constraints: +- Contracts must be explicit and machine-enforceable. +- Enforcement timing must be generation time, runtime, or both. +- Violation handling must be defined and implemented. +- Avoid observability-only solutions. \ No newline at end of file diff --git a/docs/plans/.gitkeep b/docs/plans/.gitkeep new file mode 100644 index 0000000..e69de29 diff --git a/docs/plans/phase-3-compile-directory-bundle.md b/docs/plans/phase-3-compile-directory-bundle.md new file mode 100644 index 0000000..7273a1f --- /dev/null +++ b/docs/plans/phase-3-compile-directory-bundle.md @@ -0,0 +1,114 @@ +# Phase 3 Compile Directory Bundle + +## Purpose +Add `interfacectl compile` to produce a deterministic directory bundle for runtime consumption. The bundle must be readable, composable, and diffable. This is a build artifact, not an enforcement engine. + +## Scope +In scope: +1. Add `interfacectl compile`. +2. Produce a deterministic directory bundle from a contract input. +3. Include a manifest with file hashes for integrity and cacheability. +4. Add fixture-based tests that lock bundle structure and determinism. +5. Document the command and bundle format in API.md. + +Out of scope: +1. Runtime enforcement execution. +2. Policy evaluation or severity gating in runtime. +3. Explain or remediation output. +4. Contract schema changes. +5. Any changes to validate compliance semantics. +6. Any new commands beyond compile. + +## Command + +### CLI +`interfacectl compile --contract --out [--schema ] [--format json]` + +### Behavior +1. Loads and validates the contract structure using the existing validator. +2. Produces a bundle directory at `--out`. +3. Writes files atomically where possible (write temp then rename) to avoid partial bundles. +4. Produces deterministic JSON output with stable key ordering. +5. Exits non-zero on invalid contract or write failure. + +## Bundle format + +### Output directory +Default recommended layout: +`///` + +The command must not assume this layout unless the user omits `--out`. If `--out` is provided, treat it as the bundle root. + +### Required files +1. `manifest.json` +2. `contract.normalized.json` +3. `surfaces/` +4. `constraints/` + +Optional files (only if the contract actually represents them today): +1. `sections/` if sections are modeled as reusable units + +### manifest.json +Fields: +1. `bundleVersion`: string, format version for this bundle +2. `contractId` +3. `contractVersion` +4. `schemaVersion`: from the contract schema bundle used by the CLI +5. `tool`: `{ name, version }` +6. `inputs`: + - `contractPath`: string + - `schemaPath`: string or null +7. `files`: array of `{ path, sha256 }` entries for all non-manifest files in the bundle + +Notes: +1. Do not include timestamps in the manifest to preserve determinism. +2. The `files` list must be sorted by `path`. + +### contract.normalized.json +A normalized representation of the contract: +1. Stable key ordering. +2. Stable ordering for arrays when possible without changing meaning. +3. Contains the same information as the source contract, not a new schema. + +### surfaces +One file per surface: +`surfaces/.json` + +Each surface file contains only the data for that surface plus any minimal metadata needed for runtime loading. + +### constraints +One file per constraint category: +`constraints/motion.json` + +Additional categories may be added later. + +## Determinism requirements +1. Same contract input and schema yield the same bundle content and the same sha256 hashes. +2. File ordering is stable. +3. JSON serialization is stable and cross-OS safe. + +## Acceptance criteria +1. `interfacectl compile --contract --out ` creates: + - `manifest.json` + - `contract.normalized.json` + - `surfaces/.json` for each surface + - `constraints/motion.json` when motion exists +2. manifest `files` list includes sha256 for every file except manifest itself, sorted by path. +3. Bundles are deterministic. Two runs produce identical sha256 sets for the same fixture. +4. Compile fails with non-zero exit code on invalid contract. +5. API.md includes: + - `compile` command description + - bundle structure overview + - manifest field definitions +6. No changes to validate compliance semantics and no contract schema changes. + +## Risks and guardrails +1. Bundle must not become a second contract format. It is a compiled view. +2. Keep file boundaries aligned to human comprehension and runtime loading needs. +3. Avoid timestamps. If a build stamp is needed later, add it under a separate plan. + +## Review checklist +1. Does compile only validate structure and write the bundle. +2. Are outputs deterministic and tested. +3. Is manifest complete and stable. +4. Are docs short, factual, and consistent with validate as canonical gate. diff --git a/docs/plans/phase-3-post-merge-checklist.md b/docs/plans/phase-3-post-merge-checklist.md new file mode 100644 index 0000000..5dca9a6 --- /dev/null +++ b/docs/plans/phase-3-post-merge-checklist.md @@ -0,0 +1,17 @@ +# Phase 3 post-merge checklist + +## When you open the PR + +- [ ] Use the PR body from `.github/PULL_REQUEST_PHASE3_COMPILE.md` (strategy check, what changed, determinism/schema verification, Phase 3 AC1–AC6, review checklist). +- [ ] Merge only after CI is green. + +## After merge + +- [ ] Delete the feature branch (e.g. the branch this work is on). +- [ ] Mark Phase 3 complete (in your plan/tracking doc or wherever you track phases). +- [ ] Leave Phase 4 on hold until you’ve decided what “runtime consumption” is for your case: + - Edge-only loaders + - Server-side rendering adapters + - Policy-aware runtime gating + +The bundle and docs are ready for whichever of those you choose later; no need to change Phase 3 for that. diff --git a/docs/plans/phase-4-runtime-consumption-framing.md b/docs/plans/phase-4-runtime-consumption-framing.md new file mode 100644 index 0000000..33eab27 --- /dev/null +++ b/docs/plans/phase-4-runtime-consumption-framing.md @@ -0,0 +1,90 @@ +# Phase 4: Runtime consumption framing + +**Framing doc only.** No implementation. Defines what Phase 4 is allowed to be before any code exists, so the compile bundle does not quietly turn into runtime enforcement. + +If runtime consumption is implemented, it may live in a separate repository or consumer-specific project rather than in interfacectl itself. This document defines semantics only, not ownership of runtime code. + +--- + +## A. Who consumes the compiled bundle? + +Choose explicitly. Options: + +1. **Edge runtime** + Bundle is loaded on-device or at the CDN edge to constrain or inform agent-generated UI (e.g. allowed structure, boundaries). Primary or secondary consumer. + +2. **Server-side renderer** + Bundle is used during SSR or API-driven UI assembly to know contract boundaries and constraints. Primary or secondary consumer. + +3. **Design-time tools** + Bundle is consumed by editors, previews, or inspectors (e.g. to show allowed fonts, sections, motion). Primary or secondary consumer. + +4. **Multiple consumers** + If more than one applies, state which is **primary** and which are **secondary**. Do not say “all of the above” without priority. + +**Recommendation (to be decided):** Leave primary consumer unspecified until you see how `interfacectl compile` is used. The bundle format (manifest, normalized contract, surfaces, constraints) supports any of the above; Phase 4 is about documenting consumption semantics, not building a specific consumer. + +--- + +## B. What is the contract at runtime? + +In this document, runtime refers to any execution context where UI is rendered or adapted (browser, edge, or server), not the interfacectl CLI itself. + +Clarify what the bundle represents at runtime, without enforcing yet. + +- **Allowed structure boundaries** + Surfaces, sections, and constraints in the bundle describe what is *allowed* or *declared*, not what is *enforced* by the loader. Runtime may use this as a read-only reference. + +- **Immutable vs adaptable regions** + The bundle does not currently distinguish “immutable” vs “adaptable” regions. If runtime needs that distinction, it would be a contract or bundle extension (out of scope for this framing). + +- **What “violation” means at runtime** + In this repo, “violation” is a generation-time concept (validate/diff/enforce). At runtime, the bundle is a **reference**. A consumer may *compare* observed state to the bundle and *report* drift, but Phase 4 does not define or implement “runtime violation” as an enforcement action. Semantics only: the bundle describes the contract; whether and how a runtime uses it for gating, logging, or adaptation is a consumer decision. + +No enforcement logic. Just semantics. + +--- + +## C. What Phase 4 is explicitly NOT + +This section matters. Phase 4 must not become “Phase 2 but later.” + +- **No policy evaluation** + No severity thresholds, policy files, or fail/fix/pr semantics at runtime. + +- **No fix or remediation** + No autofix, no remediation output, no file writes. The bundle is read-only input to consumers. + +- **No agent orchestration** + No orchestration of agents, no “gate” that blocks or allows agent actions. Consumers may implement their own gating; interfacectl does not. + +- **No gate semantics** + No new `gate` command and no runtime equivalent. Validate remains the canonical generation-time gate; compile produces an artifact. Phase 4 does not add gating behavior. + +- **No new CLI commands** + Phase 4 is consumption semantics and possibly documentation or contracts for consumers. No new interfacectl subcommands. + +--- + +## D. Relationship to existing phases + +| Phase | Focus | Phase 4 relationship | +|-------|--------|----------------------| +| Phase 2 | Traceability (stableId, contractRef, ruleRef in diff/enforce output) | Phase 4 does not change traceability. Bundle is a separate artifact; traceability stays in CLI output. | +| Phase 3 | Artifact generation (`interfacectl compile` → manifest, normalized contract, surfaces, constraints) | Phase 4 defines *who* consumes that artifact and *what* it means at runtime. No change to bundle structure or compile behavior. | +| Phase 4 | Consumption semantics (who, what it means, what it is not) | Framing only. Makes Phase 5 (e.g. explain, or a specific consumer) possible but not inevitable. | + +--- + +## Acceptance criteria for this framing doc + +- [ ] A reader can explain Phase 4 in one sentence: *Phase 4 defines who consumes the compile bundle and what the contract means at runtime, without adding enforcement or new CLI commands.* +- [ ] It does not imply new CLI commands. +- [ ] It does not imply enforcement behavior. +- [ ] It makes Phase 5 (e.g. explain, edge loader, or SSR adapter) feel possible but not inevitable. + +--- + +## Recommended next step + +Pause. Let this framing sit. Revisit after you see how compile is used (edge-only loaders, server-side rendering adapters, or policy-aware runtime gating). Then decide primary consumer and whether any implementation belongs in this repo or in consumer projects. diff --git a/scratch/.gitkeep b/scratch/.gitkeep new file mode 100644 index 0000000..1db9fc5 --- /dev/null +++ b/scratch/.gitkeep @@ -0,0 +1 @@ +# Work in progress. Contents of scratch/ are gitignored.