| name | spf-create-behavior |
| description | Create a new SPF behavior with conventions-aligned shape. Walks through purpose articulation (carries /refactor-behavior's purpose-first discipline), signal type choice, slot map design, composition placement, cleanup pattern selection, test placement, and engine wiring. Distinct from /refactor-behavior (which modifies an existing behavior preserving its purpose) and /spf-update-behavior (which modifies an existing behavior whose purpose is changing). Triggers: "create behavior", "new behavior", "create SPF behavior", "add behavior", "scaffold behavior", "new SPF behavior". |
Create an SPF Behavior
Scaffold a new SPF behavior in packages/spf/src/playback/behaviors/ (or
appropriate sub-path for variant-specific behaviors) with conventions-aligned
shape. The canonical failure mode without this discipline is jumping from
"we need a new behavior" to defineBehavior({...}) without articulating
purpose โ producing a behavior whose slot map drifts, whose composition
placement is unclear, or whose cleanup contract doesn't match the project
convention.
This skill is a stub scoped for use by /spf-implement-feature. Failure-
mode catalog grows from real use; the seeded entries below capture the
load-bearing patterns identified at skill-creation time.
Usage
/spf-create-behavior [<behavior-name-or-purpose>]
The skill is typically invoked from /spf-implement-feature's Step 6 when a
chunk requires a new behavior, but can be invoked directly when the user
knows the behavior shape upfront.
Reference docs
Required reading before drafting:
internal/design/spf/conventions/behaviors.md โ when to define a behavior,
one-vs-several decomposition, per-type specialization vs uniform-across-
tracks, file placement, source-reset handling, cleanup convention
(named-cleanup-collection + wrapper for multi-cleanup, not AbortController
for SPF behaviors).
internal/design/spf/conventions/signals.md โ when to use Signal<T> vs
ReadonlySignal<T>, multi-writer slot characterization, peek /
equalsById helpers, initialState / initialContext seeding.
internal/design/spf/conventions/reactors.md โ if the behavior is a
reactor (state-machine driven).
internal/design/spf/conventions/actors.md โ if the behavior creates or
consumes Actors.
internal/design/spf/conventions/config.md โ config surface conventions.
packages/spf/src/CLAUDE.md โ source layout + dependency rules.
- Existing similar behaviors as templates (e.g.,
switchVideoQuality as a
template for switchAudioQuality).
Failure-mode catalog (seeded; grows with use)
-
Purpose-articulation skipped. Same failure mode as /refactor-behavior:
jumping from "we need a behavior" to defineBehavior without naming what
the behavior does in business terms. Carries the purpose-first
discipline. The articulation should answer: what business rule does this
behavior implement? What signal does it react to or write? What lifecycle
does it own?
-
Slot map over-sized. Including more slots in stateKeys /
contextKeys than the behavior actually reads/writes locks the behavior
to a fixed composition. Audio-only / video-only / live variants can't
compose the behavior without wiring no-op slots. Per behaviors.md ยง
Inverse: behaviors that operate uniformly across tracks, compose against
the aggregating resource (e.g., mediaSource.sourceBuffers) when the
behavior's logic is uniform, not against per-type slot pairs.
-
Cleanup pattern mismatch. Per the project convention (recorded in
feedback_spf_cleanup_pattern memory and conventions/behaviors.md),
SPF behaviors use named-cleanup-collection + wrapper for multi-cleanup,
not AbortController (which the broader CLAUDE.md cleanup-pattern section
recommends for non-SPF code). New behaviors must match the SPF-specific
convention.
-
Composition-variant logic in always-on behavior. When a behavior is
variant-specific (lives only in a live variant, audio-only variant, etc.),
it must live as a separate behavior composed into the variant, not as a
runtime conditional inside an always-on behavior. Same failure mode as
the spf-document-feature catalog entry.
-
Tests written after the fact. TDD discipline: write the test in
tests/<behavior-name>.test.ts first, run it failing, then implement.
Implementation-first produces tests that pass by construction.
Steps (do these in order)
Step 1 โ Articulate purpose
Carry /refactor-behavior's purpose-first discipline forward to new
behaviors. Before any code:
- What business rule does this behavior implement? Name it in plain
language. "Audio quality switching responds to bandwidth state by writing
selectedAudioTrackId to the next viable quality."
- What signal does it react to or write? Inputs (read slots) and
outputs (write slots).
- What lifecycle does it own? When does it activate, when does it
cleanup, what triggers source-identity reset?
- What's the failure mode if the behavior weren't there? Helps clarify
what's load-bearing.
Stop and report to user with the purpose articulation. The user
confirms before proceeding to slot map design.
Step 2 โ Choose signal types + slot map
Per conventions/signals.md:
Signal<T> (writable) vs ReadonlySignal<T> โ express read/write
intent at the use site. If the behavior writes a slot, it needs Signal<T>.
If only reads, ReadonlySignal<T>.
- Multi-writer characterization โ if writing a slot another behavior
also writes, characterize coordination along the three axes from
conventions/signals.md: decision domain (config / DOM / intent /
derived), trigger (one-shot vs ongoing), cost (cheap vs side-effect-heavy).
stateKeys / contextKeys sizing โ include only what the behavior
actually reads/writes. Per the slot-map-over-sized failure mode, narrow
is better.
initialState / initialContext seeding โ if the behavior's first
read of a slot needs a meaningful default, seed via initialState rather
than relying on undefined-narrowing.
Step 3 โ Choose composition placement
- Always-on or variant-specific? Composed into
createSimpleHlsEngine
(always-on), or into a variant factory (createAudioOnlyHlsEngine,
etc.)?
- Position in the composition order? Per
packages/spf/docs/hls-engine.md,
the composition has a logical order (lead-in: presentation resolution;
middle: per-track-type setup, MSE, segment loading; tail-out: adapter
integration). Where does this behavior slot in?
- Per-type or uniform? Per
conventions/behaviors.md's per-type vs
uniform-across-tracks decision โ if the behavior's logic varies per
track-type, follow the per-type pattern (sibling behaviors + shared
helper); if uniform, compose against the aggregating resource.
Step 4 โ Implement (TDD)
- Write the test first.
packages/spf/src/playback/behaviors/tests/ <behavior-name>.test.ts (or sub-path per behavior placement).
- Run the test failing.
- Implement the behavior in
packages/spf/src/playback/behaviors/ <behavior-name>.ts. Apply conventions throughout.
- Run the test passing.
- Wire into the engine composition at the position chosen in Step 3.
- Run the composition tests (
engine.test.ts and related) to verify
no regression.
Step 5 โ Final-shape audit + commit
Per the parent skill (/spf-implement-feature), commits are typically
batched at the feature-implementation level, not per-behavior. If invoked
standalone, propose a per-behavior commit shape and ask the user to confirm.
Audit checklist:
- Conventions adherence โ behaviors.md, signals.md, cleanup pattern
- No scope creep โ does the implementation match the purpose
articulation from Step 1?
- Tests cover the purpose โ does the test assert the business rule, not
just incidental implementation details?
- Composition wiring complete โ engine composition tests pass
When this is the wrong skill
- Refactoring an existing behavior, purpose preserved โ
/refactor-behavior
- Updating an existing behavior, purpose changing โ
/spf-update-behavior
- Splitting / merging existing behaviors โ
/split-behavior /
/merge-behaviors (often routed through /refactor-behavior)
- Creating a media-layer or network-layer helper (not a behavior) โ
manual for now; future media-layer / network-layer skills will own this
How the failure-mode catalog grows
Same pattern as other SPF skills: when a new failure mode surfaces during
use, add an entry to the catalog above with a worked-example citation.
This skill is a stub; the catalog is expected to grow significantly as
real use surfaces patterns.