Start with the shared pattern development guidance in:
docs/common/ai/pattern-development-guide.md
Read that guide first. It is the canonical reference.
For new or changed behavior, completion includes the whole source lifecycle:
write automated pattern tests, run every test entry with cf test, and attach
every entry with repeatable --test flags on piece new and every later
piece setsrc. Manual browser or CLI verification does not replace the
automated tests. Deployment packages and type-checks attached tests but does not
run them. A file the pattern ships with that is not code — a fixture, a lookup
table — is read with dataFile(path) from commonfabric and stored verbatim
rather than compiled. The path resolves against the module that reads it, as an
import specifier does, so ./data/cities.json is the file beside the pattern.
That call is the whole declaration: store the file where the call points, and
every command that builds the pattern attaches it, so a pattern that reads one
is checkable, testable, and deployable with no extra flag. --datafile remains
for a file the source cannot name, such as one read by a computed path.
Also read the foundational reactivity references before implementing or debugging pattern state:
docs/common/concepts/reactivity.mddocs/common/patterns/new-cells.md
These are required context for Common Fabric pattern work. TypeScript surface types can look like plain values even when runtime values are reactive cells.
Decide each state field's sharing boundary before building the UI:
PerSpace<T>: shared durable state for everyone in the space.PerUser<T>: state that follows one authenticated user across sessions.PerSession<T>: ephemeral state for one session, such as navigation, selected tab, selected room, selected item, local filter text, open modal, or focused item.
Default transient UI state to PerSession<> unless it intentionally needs to
persist for the user across sessions. A useful test: if the user opens the same
instance in a new tab, should this state carry over? If not, it is probably
PerSession<>. Multi-user patterns make this boundary especially important, but
the rule is about state lifetime, not only collaboration. Do not store user ids
or session ids in ordinary data to simulate isolation; use scope wrappers on the
relevant input and output types.
Two common scoped authoring styles are useful:
// Plain-input style: public API looks like ordinary data.
interface ChatInput {
conversation?: PerSpace<Conversation | Default<typeof DEFAULT_CONVERSATION>>;
name?: PerUser<string | Default<"">>;
selectedRoom?: PerSession<SelectedRoom | Default<EmptySelectedRoom>>;
}
// Writable-input style: handlers need stable writable cell handles.
type NameCell = Writable<string | Default<"">>;
type SelectedRoomCell = Writable<SelectedRoom | Default<EmptySelectedRoom>>;
interface ChatInputWithCells {
name?: PerUser<NameCell>;
selectedRoom?: PerSession<SelectedRoomCell>;
conversation?: PerSpace<Writable<Conversation>>;
}
Use the plain-input style when the pattern API should stay data-shaped. Use the
writable-input style when handlers need .key(...), .equals(...), or stable
cell bindings, especially in mutation-heavy multi-user UI.
For local cells that should not become public pattern inputs, use scoped cell constructors:
const sharedBoard = new Writable.perSpace(DEFAULT_BOARD);
const displayName = new Writable.perUser("");
const selectedItem = new Writable.perSession<string | null>(null);
Plain new Writable(...) inherits the containing pattern or factory scope. Use
new Writable.perUser(...) or new Writable.perSession(...) when the local
cell must have a specific sharing boundary independent of that context.
PerAny<T> is rare. Use it only when an inner value must override an outer
scope declaration and may validly come from any concrete scope:
type Selection = PerSession<{
item: PerUser<Item>;
attachment: PerAny<Attachment>;
}>;
Prefer PerSpace<>, PerUser<>, or PerSession<> whenever the inner value's
scope is known. Do not directly stack scope wrappers on the same value, such as
PerUser<PerSession<T>>; put the inner scoped declaration on the field or cell
that actually has that scope.
Identity (multi-user): Scope decides where state lives; identity decides
who it belongs to. Resolve the viewer via wish({ query: "#profile" }) (never
a typed-name field), store each participant's live #profile cell in the shared
PerSpace roster on join, render every participant with cf-profile-badge
bound to that cell (cf-avatar + snapshot only as an offline fallback), and
identify people by equals() on a cell reference, not display name. See
docs/common/patterns/multi-user-patterns.md#presenting-identity and
docs/common/components/COMPONENTS.md#identity-components.
When working in a Pattern Factory Build workspace, also read:
docs/common/ai/pattern-factory-build-guide.md
That guide defines the top-level build contract, verification posture, and documentation discipline for Pattern Factory runs.
When you're unsure whether a reactive expression site lowers the way you expect, inspect the emitted source directly with:
deno task cf check <pattern>.tsx --show-transformed
Also inspect the emitted source when a composed pattern result is assigned to a
PerUser<> or PerSession<> typed variable or output. Contextual scope types
should lower into the factory call; if they do not, the pattern may instantiate
state at the wrong sharing boundary.
Prefer direct authored expressions, including plain ternaries, first; if a
conditional site behaves unexpectedly or seems ambiguous, inspect the emitted
source with --show-transformed rather than guessing.
Pay special attention to the SES authoring section of
docs/common/ai/pattern-development-guide.md before adding module-scope setup,
timers, or reads of the clock or entropy. Authors call Date.now() (or
new Date()) and Math.random() directly — they are built-ins, nothing to
import. Inside the sandbox these are gated: allowed only in a handler (the clock
coarsened to one-second resolution), and throwing in a lift, computed, or the
pattern body. To read a live clock in a computed, use the reactive #now wish.
Also follow its binding guidance: when a control is already bound to a cell,
usually via $value or $checked, do not add a handler that simply writes the
same value back into that same cell.
Runtime notes:
- Use the
cfskill, or readskills/cf/SKILL.md, when you need CLI command details. - If your runtime supports delegation, pass file paths rather than pasted summaries.
Runtime-Specific Notes
Claude Code
Interactive labs sessions only. In a Pattern Factory workspace, the factory orchestrator owns delegation, critique, and finalization — do not Task these subagents, run a separate critic pass, or offer commits there.
- Use
EnterPlanModebefore building. - Scale the plan to the problem:
- simple pattern: 2-3 sentences
- medium pattern: short list
- complex pattern: structured plan with entities, relationships, and actions
- Delegate by role when that helps:
Task({
prompt: "Implement [feature]. Keep it simple, one file.",
subagent_type: "pattern-maker"
})
Task({
prompt: "Run every authored test, then deploy [pattern] with each test attached.",
subagent_type: "pattern-user"
})
Task({
prompt: "Review [file] for violations.",
subagent_type: "pattern-critic"
})
- Run a critic pass before first deploy unless the change is a tiny, low-risk fix.
- At useful milestones, offer a commit.
Other runtimes
- Preserve the same rhythm even when the invocation syntax differs:
- plan first
- keep the first slice runnable
- separate implementation, runtime testing, and critique when the task is large enough to justify it
Phase skills consult as needed:
- Pattern Factory Build:
docs/common/ai/pattern-factory-build-guide.md - Reactivity and local cells:
docs/common/concepts/reactivity.md,docs/common/patterns/new-cells.md - Types:
docs/common/concepts/types-and-schemas/ - Actions/handlers:
docs/common/concepts/action.md,docs/common/concepts/handler.md - Testing:
docs/common/workflows/pattern-testing.md - Existing patterns:
packages/patterns/index.md— check the "Status tiers" section before copying idioms from any pattern; onlyexemplarentries are style references. - Components:
packages/patterns/catalog/catalog.tsx— the authoritative, type-checked component catalog. Story files inpackages/patterns/catalog/stories/show live usage for each component. Also seedocs/common/components/COMPONENTS.mdfor narrative docs. - Debugging: start with
docs/development/debugging/README.md, then follow the linked gotcha or workflow doc for the exact failure.