author-shape-bundle
Turns a firmed domain — capabilities detailed by /understand (each with its functionalities
already created), profile set — into a selection-and-composition bundle: which
capabilities stay, which are pruned, which existing functionalities to build now, the
personas served, the journeys travelled, the decisions, and the domain's vertical slices.
It reads the profile to judge fit and the KB shelves to ground the selection.
Altitude — the product owner
/vision was the CXO, /understand the product manager. /shape is the product owner: it
decides what gets built and in what shippable shape. It does not detail (that was
/understand) and does not plan order (that is /roadmap). Its output is deliverable
verticals — each a thin slice a user can open and check.
Write discipline — direct-model-write containment split (ADR 026)
Under direct-model-write (standards/rules/direct-model-write.md) there is no draft/ tree.
The split is mandatory:
- You write ONLY the per-node record files — each slice record, each persona record, each
journey record, each decision (ADR) record, and the slice
_deferred.yaml bucket —
straight to the live model under the domain's home at
{product_base}product-os/{domain}/…. Each is its own file with a stable id, so a re-run
overwrites its own record and never duplicates.
- You NEVER write any shared model file: not
_spine.yaml, not profile.yaml. The spine
mutation — the capability status flips, the persona/journey/decision refs attached onto
those capabilities, and the slices index entries — is the job of /shape's deterministic
keyed persist script (persist_shape.py), which merges it into the live spine in place,
keyed to the domain. Node-level containment inside the spine (only the named capabilities
flip, only their status field moves) is the persist's, not yours.
- The spine entries you used to write into a draft
_spine.yaml are now emitted as
structured data in shape-manifest.yaml (a non-model STM artifact) under a
spine_delta block. The keyed persist reads that block and applies it to the live spine.
What it does NOT do
It does not create functionalities and does not author ICE — /understand already
created and detailed every functionality (a spine functionalities entry + a
functionality.md). /shape SELECTS among those and composes them; it never re-makes them.
It also never writes the profile (the box is /understand's; /shape selects against it) and
never cuts epics (/grill) or plans order (/roadmap).
The always-scaffold-the-UI principle
A slice is a vertical only if a user can OPEN a surface and CHECK its outcome. So every
slice names at least one user-facing surface, and that surface is a thin scaffold — the
slice's own piece of the screen, not the whole dashboard. The UI is built up slice by
slice; the full product surface accretes as the slices land. Never cut a backend-only slice
(a pure ingestion or rollup layer with no surface), and never try to deliver "the whole
dashboard" in one slice.
Inputs
| Field | Required | Description |
|---|
domain | yes | The target domain: its id, slug, and the path to its folder under {product_base}product-os/. |
kb_shelf | yes | The KB shelf this domain maps to, resolved by the play via search-kb (the product domain name is NOT the shelf name). |
product_base | yes | From config. Read the live spine (capabilities + their functionalities, all detailed) and the profile; WRITE each record file (slices, personas, journeys, decisions, _deferred) in place under {product_base}product-os/{domain}/…. Never write _spine.yaml or the profile. |
manifest_path | yes | Output path under STM for shape-manifest.yaml — the provenance + the spine_delta the keyed persist consumes. |
stm_base | yes | From config. |
Procedure
Reasoning (keep/prune judgment, which functionalities to build now, naming personas,
sketching journeys, cutting slices toward surfaces) is yours. Schema conformance and stable
ids are non-negotiable.
-
Read the domain + the profile. From the live _spine.yaml, load the domain's
capabilities (all detail: detailed) and their functionalities, plus the profile box.
Read the KB shelf via the router (python3 $KB shelf <kb_shelf>) for selection grounding.
-
Confirm or prune each capability. Against profile fit and the capability's grounding,
decide active (keep) or deprecated (prune). Each prune becomes a decision. (Status
flip only — never edit the capability otherwise; the flip is applied by the keyed persist,
not by you.)
-
Select the functionalities to build now. From the functionalities /understand already
created under each kept capability, choose which to build in this round. Selecting is a
prioritization call, not creation — you never make a new functionality here. Every
selected functionality must land in a slice or the _deferred bucket (step 7).
-
Create personas + user journeys (records, in place). Write each persona record (who
the functionalities serve) and each journey record straight to the live model. A journey
is a user journey through a surface — the ordered steps a persona takes ON a named
surface to reach an outcome, never a backend pipeline. Each journey lists its
surface_refs (the slice surface ids it runs on) and its steps. Stable ids from name/slug.
-
Record decisions (records, in place). Write one decision (ADR) record per prune and per
material selection choice, at the right level (capability or functionality), straight to
the live model.
-
Compose vertical slices — each with a scaffolded surface (records, in place). Write
each slice record straight to the live model, bundling the selected functionalities into
the domain's slices — vertical increments cut TOWARD a user-facing surface, that may
cross capabilities of THIS domain. For each slice: a stable id, a name, an outcome (the
usable thing it delivers), the surface list (≥1 surface, each with a stable id, a
name, the persona_ref who opens it, and the user_action), an acceptance_intent, and
a functionalities list where each entry carries the functionality_ref (the spine
functionality id — the reference IS the id; the grounding is its functionality.md) and a
flag ( | ). NAME the surface, never DESIGN it (no
wireframes/components — that is /realize's UX lens). The surface is a thin scaffold (this
slice's piece of screen), not the whole product UI. Wire each surface to a journey. Do NOT
set , , or resolved (that is /roadmap). Every selected
functionality must land in at least one slice OR the bucket.
Output — records on the live model + a manifest
{product_base}product-os/
{domain}/{capability}/
personas/{persona-id}.yaml # persona record (in place on the live model)
journeys/{journey-id}.yaml # journey record (surface_refs + steps)
decisions/{decision-id}.yaml # ADR per prune + material selection
{domain}/slices/{slice-id}.yaml # slice record (references functionalities by spine id)
{domain}/slices/_deferred.yaml # selected-but-not-placed functionalities (if any)
{manifest_path} # shape-manifest.yaml (STM, non-model)
You do NOT write {product_base}product-os/_spine.yaml or the profile — the keyed persist
writes the spine from the manifest's spine_delta.
A slice record references functionalities by their spine id; the surface is named, not designed:
slice:
id: slice-trusted-coverage
domain_ref: dom-token-dash
name: "Trusted source coverage"
outcome: "A CTO opens the coverage view and sees every source's trust state from fixtures"
surface:
- id: surface-coverage-view
name: "Source coverage view"
persona_ref: persona-cto
user_action: "open the coverage view and read each source's resolved trust state"
functionalities:
- { functionality_ref: func-source-usage-ingest, delivery: full }
- { functionality_ref: func-privacy-trust-labeling, delivery: full }
- { functionality_ref: func-source-coverage-freshness, delivery: full }
- { functionality_ref: func-dashboard-presentation, delivery: partial }
acceptance_intent: "the CTO can open the coverage view and every source shows a resolved trust state"
status: proposed
The shape-manifest.yaml carries the provenance AND the structured spine_delta:
shape:
domain_ref: dom-token-dash
capabilities:
- { id: cap-coverage, decision: active, grounding: <kb shelf or proposal> }
- { id: cap-legacy, decision: deprecated, grounding: <kb shelf or proposal> }
decisions: [dec-prune-legacy, dec-select-coverage]
spine_delta:
capabilities:
- id: cap-coverage
status: active
personas: [persona-cto]
journeys: [journey-check-coverage]
decisions: [dec-select-coverage]
- { id: cap-legacy, status: deprecated, decisions: [dec-prune-legacy] }
[, ]
Return the enriched contract with the record paths on the live model and the
shape-manifest.yaml path — paths, never inline content.
Rules
- Select + compose, never create. Choose among the functionalities /understand created;
never make a functionality or author ICE here.
- Selector, never box-mover. Read the profile; never write it.
- Records to live, the spine to the manifest. Write the slice/persona/journey/decision
records straight to the live model; NEVER write
_spine.yaml or the profile — emit the spine
mutation as the manifest's spine_delta for the keyed persist.
- Grounded. Every kept capability and selected functionality traces to a KB shelf or a
proposal, recorded in the manifest.
- Stable ids. Personas, journeys, decisions, slices use ids from name/slug so re-runs
never duplicate.
- Soft prune. A pruned capability is
deprecated, never deleted; status flip only (applied
by the keyed persist), never edit a capability otherwise.
- Every slice is a user-facing vertical with a scaffolded surface. Each slice names ≥1
surface a persona opens and checks; a backend-only or one-per-capability horizontal slice
is invalid. NAME the surface, never DESIGN it; the surface is a thin scaffold, not the
whole UI.
- Journeys are user journeys. Each journey traverses a named surface via
surface_refs;
every surface a slice names is reached by at least one journey.
- Slices reference by spine id. A slice points at each functionality by its spine
functionality_ref id; the functionality's grounding stays the single source of truth.
- Cover every functionality. Every selected functionality lands in a slice or the
_deferred bucket — nothing unplaced. A functionality may appear in more than one slice.
- Compose, don't plan. Slices carry no
order, effort, or resolved depends_on —
that is /roadmap's. Free-text dependency_notes are fine.
- Schema-true. Slice records conform to the slice schema; the manifest
spine_delta
conforms to the spine schema; persona/journey/decision records conform to their schemas.