The atom model
A traditional PRD is a chapter-shaped artifact: tens of pages, headings, paragraphs, screenshots. It’s optimised for a human reading top-to-bottom in one sitting.
That shape breaks once an LLM is in the loop:
- A 40-page PRD does not fit in any current model’s context window without aggressive truncation.
- A diff at the chapter level masks which feature actually changed.
- Cross-references (
see Section 4.2) are unverifiable — nothing prevents the section from being renamed or deleted. - An agent given the whole document has to find the relevant slice every prompt; tokens are wasted re-reading.
AtomPRD attacks the shape problem directly. A PRD is a flat list of atoms + a list of typed edges between them.
Atom properties
Section titled “Atom properties”Every atom has:
| Property | Meaning |
|---|---|
| Stable ID | feat_create_habit_h14a. Format: <prefix>_<slug>_<hash4>. Never changes across edits. |
| Kind | One of 26 fixed kinds. Closed vocabulary. |
| Self-contained | An LLM can produce code for it given only its content + atoms it explicitly references. |
| Context-window-fit | 500–2000 tokens, never more than ~4K. Larger ideas split into multiple atoms. |
| Typed relationships | 20 relation types. Schema-checked. |
The “ID never changes” rule is load-bearing. It’s the foreign key for relations, provenance, source-map sidecars, and audit logs. If you rename a feature, the name changes; the ID does not. Tools can detect rename versus delete because the ID is stable.
Atoms vs blocks vs the chapter shape
Section titled “Atoms vs blocks vs the chapter shape”graph LR classDef atom fill:#eef0ff,stroke:#4f46e5,color:#0a0a0a; classDef rel fill:#fafafa,stroke:#737373,color:#525252; vis[vision]:::atom per[persona]:::atom mod[module]:::atom feat[feature]:::atom ent[entity]:::atom fld[field]:::atom cri[criterion]:::atom fix[fixture]:::atom ui[ui_view]:::atom mod -- parent_of --> feat feat -- uses_entity --> ent ent -- parent_of --> fld cri -- verifies --> feat fix -- linked_criteria --> cri ent -- rendered_by --> ui
The hierarchy Project → Module → Feature → Block is expressed via parent_of edges, not nested fields. Each block atom is its own row in storage. That gives you:
- Independent diffs. A change to one criterion doesn’t touch its sibling features.
- Independent versioning. Deleting one fixture leaves linked criteria intact (the dangling edge surfaces in validation).
- Independent context windows. An agent grabs
feat_create_habit + cri_first_load + fix_empty_state— three atoms, ~1500 tokens — instead of an entire feature subtree.
Why 26 kinds and not “anything goes”?
Section titled “Why 26 kinds and not “anything goes”?”A closed vocabulary is the second load-bearing decision. It buys:
- LLMs gen reliably. GPT-4o / Claude can be told “emit one of these 26 kinds” and stay in-vocabulary. With an open kind set the model invents kinds and tools downstream can’t dispatch on them.
- Schema-validated semantics. Each kind has its own Zod schema.
criterionrequires EARS structure;servicerequires atype;env_varrequiressecret: boolean. The validator catches the wrong kind being used as a workaround. - Cross-tool interoperability. Two tools agreeing on the kind set can exchange manifests without a translation layer.
The cost: occasionally you want an atom that doesn’t fit. Pick the closest kind and use note or description for the residual prose. v0.4+ may add new kinds; existing ones stay frozen.
See also
Section titled “See also”- The 26 atom kinds — the full reference table.
- Two-layer model — why 5 of the 26 carry structured Behavior.
- Spec → IDs — exact regex, hash collision rules.
- RFC-001 — Relation kind enforcement — how relation types are constrained per atom kind pair.