# Atomic Card Baseline Schema

## Purpose

Define the **universal baseline** every backlog card MUST satisfy — for ANY prefix
(`FEAT`/`CHORE`/`BUG`/`DOC`/`PERF`/`UI`) and ANY origin (a `/prd` run, a follow-up, a
GitHub-issue triage, a manual edit). `/new` and `/new2` consume cards **type-blind**: they
scale per-card review depth on `review_profile` and run the same pipeline regardless of prefix.
A card that drifts off this baseline (e.g. a `CHORE` with no `review_profile` or no `scope`)
silently degrades that pipeline. This module is the **single source of truth** for *which
fields a card needs* and *in what state*; every writer and every consumer cites it.

## Scope

**In**: the per-profile field-state matrix, the profile-detection rule, and the
consumer contract (HALT / BACK-FILL / WARN) that `/new` ingestion enforces.
**Out**: the *value* decisions — `owner_agent` routing lives in `prd-card-writer.md § Rule A`
(enum SSOT: `.claude/agents/REGISTRY.md`); `review_profile` criteria live in
`prd-card-writer.md § Rule C`. This module says a card MUST carry those fields and in what
state; it **never copies** the Rule A enum or the Rule C decision table (doing so would
re-introduce the drift those SSOTs exist to prevent).

## The three card profiles

Baseline requirements are **profile-aware** — a flat "every field required and non-empty"
rule would wrongly reject valid cards (an epic legitimately has `owner_agent: ""` and
`files_likely_touched: []`; a standalone CHORE legitimately has no `scope_boundaries` or
`links.prd`). Detect the profile first, then apply its column:

- **EPIC** — filename matches `*-epic.yml`, OR `group.sequence == 0`, OR `review_profile: skip`
  with `owner_agent: ""`. A tracker, never implemented. `/new` excludes it from the per-card
  loop.
- **STANDALONE** — a single card with no PRD/epic lineage: no `group.parent` pointing at a
  `-00` epic AND no `links.prd`. Typical: a follow-up (`<id>-followup-<gate>.yml`), a
  graph-align CHORE, an ad-hoc BUG.
- **CHILD** — a card produced under a PRD epic (`group.parent` → a `-00` epic id). The default
  for `/prd`-generated work.

## Field-state matrix

Legend: **R** = required, present **and non-empty** · **E** = required key, value MAY be empty
(`[]` / `""`) · **C** = conditional, omittable per a documented carve-out (never a HALT) ·
**—** = not applicable for this profile.

| Field | EPIC | CHILD | STANDALONE | Notes |
|---|---|---|---|---|
| `id` | R | R | R | |
| `title` | R | R | R | |
| `status` | R | R | R | enum `TODO`/`IN_PROGRESS`/`DONE` |
| `priority` | R | R | R | enum `HIGH`/`MEDIUM`/`LOW` |
| `owner_agent` | E (`""`) | R | R | CHILD/STANDALONE ∈ Rule A enum; EPIC is `""` (tracker) |
| `execution_mode` | R | R | R | |
| `group` | R (`sequence: 0`) | R (`parent`+`sequence`) | C | STANDALONE may set `group.parent` to a tracking slug / originating id; `sequence` omittable |
| `git_strategy` | R | R | R | branch/base/target |
| `context` | R | R | R | |
| `scope` | R | R | R | **never omittable** — encodes intent |
| `scope_boundaries` | R | C | C | "Omit for standalone cards with no siblings" (`prd-card-writer.md`) |
| `requirements` | C | R | R | EPIC tracks via AC-EPIC, not `requirements` |
| `acceptance_criteria` | R (AC-EPIC) | R | R | |
| `definition_of_done` | R | R | R | |
| `depends_on` / `blocks` | E (`[]`) | E | E | |
| `estimated_complexity` | C | R | R | |
| `review_profile` | R (`skip`) | R | R | ∈ `{skip,light,balanced,deep}`; CHILD/STANDALONE via Rule C |
| `parallel_group` | R (`0`) | R | C | |
| `files_likely_touched` | E (`[]`, never code) | R | R | EPIC tracks docs only |
| `links.prd` | C | R | C | STANDALONE/non-PRD cards legitimately lack a PRD |
| `links.design` | C | C | C | required when the card has UI scope |
| `canonical_docs` | R | R | C (WARN) | "REQUIRED for all cards generated from a PRD"; a manual CHORE may lack it |
| `data_sources` | E (`[]`) | E (`[]` if no data) | E | key always present, value may be `[]` |
| `documentation_impact` | R | C | C | |
| `execution_strategy` | R | — | — | epic-only orchestration block |
| `data_fields` | — | C | C | only if it reads/writes persistence |
| `db_indexes` | — | C | C | only if it adds a compound query |
| `test_plan` · `integration_points` · `existing_patterns` · `anti_patterns` · `reuse_analysis` · `input_output_examples` · `error_handling` | C | C | C | populate when applicable (see `prd-card-writer.md` Required Fields) |
| `assumptions` · `unknowns` · `notes` | C | C | C | |

## Consumer contract — HALT / BACK-FILL / WARN

`/new` and `/new2` ingestion (pre-flight) enforce this matrix against the detected profile.
The three classes are deliberately distinct so a thin card is fixed where it can be and only
blocks where intent is genuinely missing:

- **HALT (non-derivable — ask the user):** a **R** field that cannot be safely synthesized.
  The canonical HALT set is `requirements`, `acceptance_criteria`, `files_likely_touched`,
  and `scope` (CHILD/STANDALONE). `scope_boundaries` is **NOT** in the HALT set — it is **C**.
- **BACK-FILL (deterministically computable — compute, persist, log):** `review_profile`
  (compute via `prd-card-writer.md § Rule C`) and `owner_agent` (default `coder` for a
  non-epic; **never** back-fill an epic's `""`). Write the value to the card on disk in the
  **main repo** with a `[BACKFILL] <id>: <field>=<value>` log. **Check-and-skip**: only write
  when the field is genuinely absent (idempotent across pre-flight re-entry).
- **WARN (expected but tolerable absent):** `canonical_docs` / `links.prd` on a STANDALONE /
  non-PRD card. Log, do not block.

A back-fill never rescues a card missing a HALT field: a card without `scope` blocks regardless
of how many derivable fields were filled.

## Writer contract

The canonical writer is the `prd-card-writer` agent. It emits the full baseline for the
relevant profile — including its **Standalone Single-Card Mode** for follow-up / CHORE / BUG /
issue cards (see `prd-card-writer.md`). **Every** card-creation path delegates to it rather
than hand-writing a partial stub; the only exception is a documented crisis fallback on total
agent outage, and even that stub is normalized by the consumer contract above on first
ingestion. New writers MUST cite this module, not re-list fields.

## Validator

`framework/scripts/validate-card-baseline.js` (and the CI sibling
`scripts/check-card-baseline.js`) parse this matrix, detect the card's profile, and assert
each field's state (R present+non-empty, E key present, C ignored, enums valid). They read the
field list from THIS file — never an embedded copy — so the schema and the validator cannot
drift.
