Skip to content

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.

Every atom has:

PropertyMeaning
Stable IDfeat_create_habit_h14a. Format: <prefix>_<slug>_<hash4>. Never changes across edits.
KindOne of 26 fixed kinds. Closed vocabulary.
Self-containedAn LLM can produce code for it given only its content + atoms it explicitly references.
Context-window-fit500–2000 tokens, never more than ~4K. Larger ideas split into multiple atoms.
Typed relationships20 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.

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.

A closed vocabulary is the second load-bearing decision. It buys:

  1. 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.
  2. Schema-validated semantics. Each kind has its own Zod schema. criterion requires EARS structure; service requires a type; env_var requires secret: boolean. The validator catches the wrong kind being used as a workaround.
  3. 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.