# Project artifacts

> PRODUCT.md, DESIGN.md, design.json sidecar, surface briefs, `.impeccable/` layout, schema stamps, deprecated sections, and context resolution order.

- Repository: pbakaus/impeccable
- GitHub: https://github.com/pbakaus/impeccable
- Human docs: https://grok-wiki.com/public/docs/pbakaus-impeccable-adadc04d8de4
- Complete Markdown: https://grok-wiki.com/public/docs/pbakaus-impeccable-adadc04d8de4/llms-full.txt

## Source Files

- `skill/scripts/context.mjs`
- `skill/scripts/lib/artifact-schema.mjs`
- `skill/scripts/lib/impeccable-paths.mjs`
- `skill/scripts/lib/surface-briefs.mjs`
- `skill/scripts/lib/staleness.mjs`
- `skill/reference/init.md`
- `skill/reference/document.md`

---

---
title: "Project artifacts"
description: "PRODUCT.md, DESIGN.md, design.json sidecar, surface briefs, `.impeccable/` layout, schema stamps, deprecated sections, and context resolution order."
---

Impeccable persists durable project context as markdown and JSON under the active project root and `.impeccable/`. Session boot loads that material through `skill/scripts/context.mjs` (`loadContext` / CLI), injects matching platform references when `## Platform` is native, resolves a surface brief when the target is unambiguous, and emits machine-readable directives (`NO_PRODUCT_MD`, `RESOLVED_CONTEXT`, `CONTEXT_STALE`, and related) that route Setup and design commands.

## Artifact map

| Artifact | Canonical path | Owner command | Role |
| --- | --- | --- | --- |
| Product record | `PRODUCT.md` (also `Product.md` / `product.md`) | `init` | Durable product truth: users, purpose, positioning, platform, constraints |
| Design system | `DESIGN.md` (also `Design.md` / `design.md`) | `document` (scan/seed); new-work finish path | Durable visual system; optional YAML token frontmatter + eight canonical sections |
| Design sidecar | `.impeccable/design.json` | `document` Step 4b | Extensions Stitch frontmatter cannot hold: ramps, shadows, motion, breakpoints, HTML/CSS snippets, narrative |
| Surface briefs | `.impeccable/surfaces/<slug>.md` | new-work / shape persistence via `surface-brief.mjs` | Per-surface strategy (mode, job, direction); not global product or tokens |
| Shared config | `.impeccable/config.json` | CLI / hook / detector writers | Detector ignores, hook limits, `projectRoots`, update/staleness toggles |
| Local config | `.impeccable/config.local.json` | Local overrides | Same key space as shared config; machine-local |
| Live state | `.impeccable/live/` | live setup + runtime | `config.json`, `server.json`, `sessions/`, `annotations/` |
| Critique store | `.impeccable/critique/` | critique flows | Critique artifacts (path helper in `impeccable-paths.mjs`) |

Authority split:

- **PRODUCT.md** — product facts only. No palettes, type systems, or surface mode.
- **DESIGN.md** — visual system only. No page strategy or visitor mode.
- **Surface brief** — one route/file/URL: mode, audience, job, chosen direction, open decisions.
- **design.json** — machine extensions of DESIGN.md, not a second product record.

```text
project root
├── PRODUCT.md
├── DESIGN.md
└── .impeccable/
    ├── config.json
    ├── config.local.json          # optional local overrides
    ├── design.json                # canonical sidecar (schemaVersion 2)
    ├── surfaces/
    │   └── <slug>.md              # frontmatter + brief body
    ├── critique/
    └── live/
        ├── config.json
        ├── server.json
        ├── sessions/
        └── annotations/
```

Legacy locations still resolved for read, migrated on write when severity is `auto`:

| Legacy path | Canonical path |
| --- | --- |
| `DESIGN.json` (project or context dir) | `.impeccable/design.json` |
| `.impeccable-live.json` | `.impeccable/live/server.json` |
| `.impeccable-live/sessions|annotations` | `.impeccable/live/sessions|annotations` |
| skill `scripts/config.json` (live) | `.impeccable/live/config.json` (unless `IMPECCABLE_LIVE_CONFIG` is set) |

## PRODUCT.md

Written by `init`. Never written by `document`. New files land at `PROJECT_ROOT/PRODUCT.md`; updates use the path `context.mjs` already resolved so monorepo children do not fork a second authority without confirmation.

### Schema stamp

```html
<!-- impeccable:product-schema 1 -->
```

| Constant / API | Value / behavior |
| --- | --- |
| `PRODUCT_SCHEMA_VERSION` | `1` |
| `productStampLine(version)` | Emits the HTML comment line |
| `readProductSchemaVersion(markdown)` | Integer or `null` if unstamped |
| `stampProductSchema(markdown, version)` | Idempotent add/replace; keeps stamp in place when present; otherwise inserts under the leading `#` heading |

Schema versions track **record shape**, not skill release. A PRODUCT.md from skill v4.0.0 is not stale under v4.0.1 unless the template changes.

Missing stamp means "written before stamping existed", not invalid. Boot treats unstamped files without any v4 section markers as legacy product records.

### Section template (v4)

Bare-value field: **`## Platform`** is a single line: `web` | `ios` | `android` | `adaptive`.

| Section | Required content |
| --- | --- |
| Platform | Bare platform value |
| Stack | Greenfield stack choice only; omit when the codebase already answers |
| Users | Primary users, situation, job |
| Product Purpose | What it does, why, success |
| Positioning | Mechanism a neighbor cannot copy |
| Operating Context | Workflows, environments, tools, materials |
| Capabilities and Constraints | Confirmed functionality, constraints, undecided facts |
| Brand Commitments | Binding name/voice/assets; omit when none |
| Evidence on Hand | Real assets/proof with paths; absences that must not be fabricated |
| Product Principles | 3–5 durable strategic principles (not visual recipes) |
| Accessibility & Inclusion | Product-specific needs; omit when none established |

`PRODUCT_V4_SECTIONS` used as the legacy fallback when no stamp is present: `Positioning`, `Operating Context`, `Evidence on Hand`, `Product Principles`.

Omit irrelevant sections rather than inventing prose. Record undecided facts as open; do not invent testimonials, pricing, or deployment claims.

### Platform extraction

`extractPlatform(product)` reads the first non-empty line under `## Platform`:

| Input | Result |
| --- | --- |
| `web` / `ios` / `android` / `adaptive` | That value |
| Line naming both `ios` and `android` (list separators, `and`, etc.) | `adaptive` |
| Empty / missing section | `null` → skill treats as **web** |
| Unrecognized value (toolchain names, typos) | `null` + boot `WARNING` that the project is treated as `web` |

Native values cause `context.mjs` to inline `reference/ios.md` and/or `reference/android.md` into boot output. `adaptive` loads both.

### Deprecated sections

| Heading | Status |
| --- | --- |
| `## Register` | Deprecated. v4 replaced brand/product register with per-surface visitor modes (Persuade, Operate, Read, Experience). Nothing reads Register. |

Boot finding id: `product-deprecated-register` (`severity: mention`). Treat the section as absent; offer deletion; do not let the value steer design.

## DESIGN.md

Follows the external [DESIGN.md format](https://raw.githubusercontent.com/google-labs-code/design.md/main/docs/spec.md). **No product-schema stamp** — by design, so Stitch lint stays clean. Staleness uses sidecar schema version, sidecar mtime vs DESIGN.md mtime, section coverage, and deeper doctor proxies.

### Frontmatter (normative tokens)

Allowed top-level token groups: `colors`, `typography`, `rounded`, `spacing`, `components` (plus `name` / `description`). Token refs use `{path.to.token}`. Component sub-tokens are limited to eight props: `backgroundColor`, `textColor`, `typography`, `rounded`, `padding`, `size`, `height`, `width`. Shadows, motion, focus rings, and backdrop filters do not belong in frontmatter.

### Body sections (fixed order)

1. Overview  
2. Colors  
3. Typography  
4. Layout  
5. Elevation & Depth  
6. Shapes  
7. Components  
8. Do's and Don'ts  

Omit unused sections; do not rename headings.

### Document modes

| Mode | When | Sidecar |
| --- | --- | --- |
| Scan (default) | Tokens/components/rendered output exist | Write/regenerate `.impeccable/design.json` |
| Seed | Pre-implementation scaffold after product truth exists | Skip sidecar; seed marker comment in DESIGN.md |

Do not silently overwrite an existing DESIGN.md; confirm refresh, overwrite, or merge.

## design.json sidecar

Canonical path: `.impeccable/design.json`. Candidates (first existing wins for reads):

1. `<projectRoot>/.impeccable/design.json`
2. `<projectRoot>/DESIGN.json`
3. `<contextDir>/DESIGN.json` when distinct

`DESIGN_SIDECAR_SCHEMA_VERSION` is **2**.

### schemaVersion 2 shape

```json
{
  "schemaVersion": 2,
  "generatedAt": "ISO-8601",
  "title": "Design System: …",
  "extensions": {
    "colorMeta": {},
    "typographyMeta": {},
    "shadows": [],
    "motion": [],
    "breakpoints": []
  },
  "components": [],
  "narrative": {
    "northStar": "",
    "overview": "",
    "keyCharacteristics": [],
    "rules": [],
    "dos": [],
    "donts": []
  }
}
```

| Field | Purpose |
| --- | --- |
| `extensions.colorMeta.<token>` | `role`, `displayName`, `canonical`, `tonalRamp` keyed by frontmatter color name |
| `extensions.typographyMeta.<token>` | Display names / purpose for type roles |
| `extensions.shadows` / `motion` / `breakpoints` | Tokens frontmatter cannot hold |
| `components[]` | Self-contained `html` + `css` for shadow-DOM panel render; `refersTo` maps to frontmatter component keys; classes prefixed `ds-` |
| `narrative` | North star, overview, rules, do/don'ts pulled from DESIGN.md prose |

**v1 → v2:** primitives moved to DESIGN.md frontmatter. Old sidecar `tokens.*` arrays are obsolete; outdated sidecars get finding `design-sidecar-schema-outdated` and should be regenerated with `document`.

## Surface briefs

Location: `.impeccable/surfaces/`. Version constant: `SURFACE_BRIEF_VERSION = 1`.

### Target normalization

| Input | Normalized form |
| --- | --- |
| Project-relative file | POSIX relative path from project root |
| Absolute path inside project | Relative path |
| `/route` (when not a real project file) | `route:/route` |
| `route:/…` | Normalized route (no `..`, strip query/hash) |
| `https://…` | Origin + path, no hash/search, no trailing slash |

Path for a target: `.impeccable/surfaces/<slug>.md` where slug is derived from the normalized target.

### Frontmatter written by `writeSurfaceBrief`

```yaml
---
version: 1
slug: "…"
primary_target: "src/pages/index.astro"
related_targets: []
---
```

Body stays small: scope and visitor mode; audience, job, action/task, proof/content, constraints; chosen direction and memorable moment; unresolved decisions. Do not copy PRODUCT.md facts or DESIGN.md tokens.

### CLI helper

```bash
node skill/scripts/surface-brief.mjs path <target>
node skill/scripts/surface-brief.mjs list
node skill/scripts/surface-brief.mjs read [target]
node skill/scripts/surface-brief.mjs write <primary-target> <body-file> [related-target ...]
```

### Resolution reasons (`resolveSurfaceBrief`)

| Reason | Meaning |
| --- | --- |
| `only-brief` | No target; exactly one brief exists → use it |
| `ambiguous` | No target; multiple briefs → none selected |
| `slug` | Exact path match for normalized target |
| `mapping` | Unique brief lists the target in primary/related |
| `ambiguous-target` | Multiple briefs claim the target |
| `not-found` / `invalid-target` / `none` | No usable brief |

Boot injects `# SURFACE BRIEF (path)` when one brief is selected. Otherwise, if candidates exist: `SURFACE_CONTEXT_AVAILABLE` with candidate JSON and instructions to run `surface-brief.mjs read`.

Orphaned file targets (primary path missing on disk) produce finding `surface-brief-orphaned` (`mention`). Route and URL primaries are not filesystem-checked.

## Context resolution order

Implemented in `resolveContext` / `loadContext` (`context.mjs`). First match wins per file class.

1. **Active project root** — directory selected by cwd, monorepo workspace child, or `--target` nearest context root. Looks for PRODUCT/DESIGN names in the root, then `.agents/context/`, then `docs/`.
2. **Repo-root inheritance** — when `projectRoot ≠ repoRoot` (monorepo or nested product), the same local search runs at the repo root as a **per-file fallback** (product and design can come from different levels).
3. **`IMPECCABLE_CONTEXT_DIR`** — absolute or cwd-relative escape hatch; only when neither product nor design was found above.
4. **Default** — if nothing found, `contextDir` is the active `projectRoot` and boot emits `NO_PRODUCT_MD` (and optionally visual-system directives).

Monorepo detection: workspace package-manager manifests, marker files (`pnpm-workspace.yaml`, `turbo.json`, `nx.json`, `lerna.json`), and/or `.impeccable/config.json` `projectRoots` globs.

### Boot output shape

When PRODUCT exists, stdout concatenates (separated by `---`):

1. `# PRODUCT.md` body  
2. `# DESIGN.md` body if present  
3. Surface brief block or `SURFACE_CONTEXT_AVAILABLE`  
4. `RESOLVED_CONTEXT` JSON (`projectRoot`, `repoRoot`, paths, platform, surface brief reason/candidates, `hasVisualImplementation`)  
5. Native platform reference blocks when platform is ios/android/adaptive  
6. Gap directives (`INCUMBENT_WORLD_UNDOCUMENTED`, `WORLD_DISCOVERY_REQUIRED`, …)  
7. Staleness / update directives when applicable  

When PRODUCT is missing, boot still prints DESIGN.md if present (so a later init in-session does not lose visual authority) plus `NO_PRODUCT_MD` / `PRODUCT_INIT_REQUIRED` or the visual-incumbent variants.

`loadContext()` returns the structured form used by live scripts without requiring stdout parsing.

## Schema stamps and staleness

Three drift kinds:

| Kind | Owner | Typical fix |
| --- | --- | --- |
| Tool version | `computeUpdateDirective` in `context.mjs` | `npx impeccable update` |
| Schema | Tier 1 `collectBootFindings` / doctor | `init`, `document`, auto migrate |
| Truth | Human + `document` / `init` | Rewrite from interview or code |

Finding shape: `{ id, artifact, path, severity, summary, fix }`.

| Severity | Behavior |
| --- | --- |
| `auto` | Fix on next write; never shown to the user at boot |
| `mention` | State once (throttled ~weekly per project) |
| `route` | Name the owning command (`init`, `document`, …) |

### Tier 1 boot findings (cheap)

| Id | Artifact | Severity |
| --- | --- | --- |
| `product-deprecated-*` | PRODUCT.md | mention |
| `product-schema-legacy` | PRODUCT.md | route → `init` |
| `product-schema-outdated` | PRODUCT.md | route → `init` |
| `platform-native-evidence` | PRODUCT.md | mention (web resolve vs native files/deps) |
| `design-sidecar-legacy-path` | design.json | auto |
| `design-sidecar-schema-outdated` | design.json | route → `document` |
| `design-sidecar-stale` | design.json | mention (DESIGN.md newer than sidecar) |
| `config-unknown-keys` / `config-unknown-detector-keys` | config | mention |
| `surface-brief-orphaned` | surface brief | mention |
| `config-project-roots-match-nothing` | config | mention |

Opt out: `"stalenessCheck": false` in config, or `IMPECCABLE_NO_STALENESS_CHECK=1`. Tier 2 (git, ignore-list validation, deeper truth proxies) is on-demand via `doctor`.

## Config keys that touch artifacts

Recognized top-level keys in `.impeccable/config.json` and `config.local.json`:

| Key | Role |
| --- | --- |
| `hook` | Hook enablement and limits |
| `detector` | `ignoreRules`, `ignoreFiles`, `ignoreValues`, `designSystem`, `extensions` |
| `updateCheck` | Skill update polling |
| `stalenessCheck` | Boot staleness gate |
| `projectRoots` | Monorepo workspace globs for active-project discovery |
| `$schema`, `version` | Metadata only |

Unknown top-level or detector keys are reported; they do not apply.

## Environment variables

| Variable | Effect on artifacts / context |
| --- | --- |
| `IMPECCABLE_CONTEXT_DIR` | Alternate PRODUCT/DESIGN directory when defaults are empty |
| `IMPECCABLE_LIVE_CONFIG` | Override live config path |
| `IMPECCABLE_NO_STALENESS_CHECK` | Disable boot Tier 1 staleness emission |
| `IMPECCABLE_UPDATE_HOST` / `IMPECCABLE_UPDATE_CACHE` | Skill version check (not project artifacts) |

## Troubleshooting

| Symptom | Check |
| --- | --- |
| `NO_PRODUCT_MD` every session | File name case (`PRODUCT.md` / `Product.md` / `product.md`), fallback dirs, monorepo `--target` / child cwd |
| Wrong app’s PRODUCT in monorepo | Rerun with active child cwd or `--target`; read `RESOLVED_CONTEXT.projectRoot` |
| Native refs never load | `## Platform` missing, typo, or toolchain name instead of `ios`/`android`/`adaptive` |
| Register still steers output | Delete deprecated `## Register`; modes live in surface briefs |
| Live panel shows generic components | Missing or v1 sidecar → run `document` scan mode |
| Sidecar contradicts DESIGN.md | DESIGN.md mtime newer → `document` refresh sidecar only |
| Surface brief not injected | Multiple briefs without unambiguous target → pass target or `surface-brief.mjs read` |
| `projectRoots` ignored | Patterns match nothing → boot finding; repo root becomes active project |

## Next

<CardGroup>
  <Card title="Initialize product context" href="/init-and-document">
    Interview and write PRODUCT.md, document DESIGN.md, optional live config.
  </Card>
  <Card title="Modes and platform" href="/modes-and-platform">
    Visitor modes and platform values that govern which references load.
  </Card>
  <Card title="Configuration reference" href="/configuration-reference">
    Full `.impeccable/config.json` and live config field list.
  </Card>
  <Card title="Doctor" href="/doctor">
    Tier 1/2 staleness findings, auto fixes, and opt-out.
  </Card>
  <Card title="Commands and routing" href="/commands-and-routing">
    How Setup consumes context boot output and routes sub-commands.
  </Card>
</CardGroup>
