Four patterns
Layer 2 carries structured behavior. The question is who produced each field, and whether automation may overwrite it.
AtomPRD’s answer: a per-field provenance map keyed by dotted field path.
{ "provenance": { "behavior.steps.0": { "pattern": "C", "source": "cri_x + fix_y", "confidence": 0.85 }, "behavior.steps.1.kind":{ "pattern": "D", "source": "llm:claude-sonnet-4.6", "confidence": 0.70 }, "behavior.steps.2": { "pattern": "A", "source": "manual", "lockedByUser": true } }}Every entry tags one field path with one of four patterns.
Pattern A — Hand authored 🟢
Section titled “Pattern A — Hand authored 🟢”You typed it. Highest confidence; nothing automatic touches it. (A entries are inherently “authored on purpose”; they ignore the lock flag because they’re already locked by definition.)
When to use. Domain logic that’s specific, custom action chains an LLM won’t get right, security-sensitive validations.
Behaviour. Survives every Pattern B/C/D re-run. Disappears only when you explicitly delete the field.
Pattern B — Convention default 🔵
Section titled “Pattern B — Convention default 🔵”The bridge codegen’s fallback when Layer 2 is absent. You don’t see B entries in the provenance map — they’re applied at codegen time, not stored.
When to use. Skeleton features you don’t want to author yet. The bridge emits TODO log stubs / list-view scaffolds / empty scenario files. Schema-valid; not executable until filled.
The Pattern B table for the SenLang bridge:
| Kind | Behavior absent → bridge emits |
|---|---|
feature | events[<slug>] = [{ $do: "log", level: "warn", msg: "TODO from @atomprd/codegen-senlang ..." }] |
ui_view (viewType=list + rendered_by) | Box + Heading + List + $Each over state.<entitySlot> + empty fallback Text |
ui_view (viewType=form + entity) | TextInput per field + Submit Button |
ui_view (viewType=detail + entity) | Heading + Text rows per field |
ui_view (viewType=dashboard|wizard|custom) | Heading + TODO Text stub |
rule | Preserved in rules[<slug>] but not bound |
criterion | Scenario emits when[] = [{ dispatch: "__todo_<id>" }], then{} = {} |
fixture | Legacy fixture.payload used as scenario.given.state directly |
Pattern C — Inferred from examples 🟡
Section titled “Pattern C — Inferred from examples 🟡”Deterministic. Same input → same output. Requires linked atoms with Layer 2 already filled in. Free, no LLM call.
When to use.
feature.behavior.stepsfromcriterion.behavior.before_after+fixture.behavior.api_calls.criterion.behavior.before_afterfrom a feature’s already-defined steps (reverse direction).ui_view.behavior.screensfromrendered_byentity + viewType template.rule.behavior.enforced_atfromuses_rulereverse edges to features.fixture.behavior.before_afterfromlinked_criteriaarray.
Lower confidence than A; higher than D (no creative guessing). Each inferred field carries confidence: 0.85 by default.
Pattern D — LLM suggested ⚪
Section titled “Pattern D — LLM suggested ⚪”Calls OpenRouter (Claude Sonnet 4.6 default) with a kind-specific system prompt + your Layer 1 prose + the atom’s current metadata. The model emits JSON matching the Layer 2 schema; the validator rejects shapes that don’t fit before they’re applied.
When to use.
- Brand-new feature with no linked criterion yet.
- Filling in details that are hard to derive deterministically (UI section layouts, action-chain branching).
- “Inspire me” mode when stuck.
Costs ~$0.001-0.01/call. Cached at sha256(prompt + system + model + version) by AiCache so repeated prompts on the same atom hit cache. Lower confidence than A/C; review the diff before applying.
The lock flag
Section titled “The lock flag”Every C/D suggestion goes through a diff modal before applying. Once you trust a field, click 🔒 in the Provenance tab to mark it lockedByUser: true. Subsequent re-runs of C / D will:
- Compute the suggested value.
- For each field path in current provenance with
lockedByUser: true, keep the current value. - Merge.
Lock things like:
- A custom action chain you wrote by hand (Pattern A — already locked, but explicit lock doesn’t hurt).
- A step that depended on a specific business invariant.
- A specific endpoint URL the LLM keeps mangling.
Workflow: author once, automate the rest
Section titled “Workflow: author once, automate the rest”graph TD
classDef a fill:#dcfce7,stroke:#16a34a,color:#0a0a0a;
classDef b fill:#dbeafe,stroke:#3b82f6,color:#0a0a0a;
classDef c fill:#fef3c7,stroke:#f59e0b,color:#0a0a0a;
classDef d fill:#f1f5f9,stroke:#64748b,color:#0a0a0a;
L1[Fill Layer 1 — userStory] --> Decide{Linked atoms<br/>have Layer 2?}
Decide -- yes --> InferC[Re-infer Pattern C]:::c
Decide -- no --> SuggestD[Suggest with AI Pattern D]:::d
InferC --> Diff[Diff modal: apply or cancel]
SuggestD --> Diff
Diff -- apply --> Lock[Lock fields you trust]
Lock --> Done[Layer 2 ready for codegen]:::a
Diff -- cancel --> L1
L1 -. skip Layer 2 .-> B[Codegen falls back to B at gen time]:::b
The lock + iterate dance is the whole point of the four-pattern split. You hand-author 20% of the structure (the parts that matter), automate 80%, and review the diff. Re-running C / D leaves your locked fields alone.
See also
Section titled “See also”- Two-layer model — what Layer 2 is and which kinds carry it.
- Authoring → Six-tab editor — Tab 5 is the per-field provenance + lock UI.
- MCP → Server —
get_behaviorreturns Layer 2 + provenance to the agent. - Codegen → Pattern C inference — the cloud endpoint table.