Select what to build in one domain and compose it into deliverable verticals — confirm or prune its capabilities against the firmed profile + KB, select which of the functionalities /understand created to build now, create the personas and user journeys, and bundle the functionalities into vertical slices, each behind a user-facing surface scaffolded slice by slice. The product-owner step of the ProductOS strategy pipeline, after /vision and /understand. Selects against the profile but never ...
Scanned 9/10/2026
Install to Claude Code
npx -y skills add kapilvirenahuja/garura --skill shape --agent claude-codeInstalls into .claude/skills of the current project.
Are you the author of Shape?
Add the live security badge to your README — it updates automatically with every re-scan.
[](https://www.skillsdirectory.com/skills/kapilvirenahuja-shape)More formats (shields.io, HTML) on the badges page.
---
name: shape
position: none
description: 'Select what to build in one domain and compose it into deliverable verticals — confirm or prune its capabilities against the firmed profile + KB, select which of the functionalities /understand created to build now, create the personas and user journeys, and bundle the functionalities into vertical slices, each behind a user-facing surface scaffolded slice by slice. The product-owner step of the ProductOS strategy pipeline, after /vision and /understand. Selects against the profile but never writes it; composes slices but never cuts epics or plans order. Opens no delivery issue.'
user-invocable: true
---
# shape
Take one domain that /understand has detailed — capabilities `detail: detailed` with
their functionalities created, profile `set` — and **select what to build and compose it
into deliverable verticals**. /shape is the **product owner**: it confirms the capabilities
that stay (prunes the rest), selects which existing functionalities to build now, creates
the personas served and the **user journeys** they travel, and bundles those functionalities
into the domain's **vertical slices**, each behind a user-facing surface. It reads the
profile to judge fit but **never writes it**, it never creates a functionality or authors
ICE (/understand did), and it never cuts epics (/grill) or plans order (/roadmap). One
domain per run.
**Always scaffold the UI.** Every slice names at least one surface a persona opens and
checks, and that surface is a thin scaffold — this slice's piece of screen, not the whole
UI. The product surface accretes slice by slice.
**Pipeline position: none.** /shape is a MIDDLE play of the strategy pipeline (vision → understand → shape → roadmap): it expects to run on the branch /vision already started, injects no `start-change` head and no close sequence, stops when its work is done, and leaves the branch as-is for the next play to pick up. The close belongs to /roadmap. It writes the persistent product model **directly, in place** on the already-started branch — there is no draft copy and no apply/promote step; review is the branch git diff and the pipeline's end PR. (#437, #500, ADR 026)
**Write discipline (ADR 026, `standards/rules/direct-model-write.md`).** The LLM authoring
skill writes ONLY the per-node record files — each slice, persona, journey, and decision
record, and the `_deferred` bucket — straight to the live model; the one shared-file mutation
(the spine `_spine.yaml` — the capability `status` flips, the persona/journey/decision refs,
and the `slices` index) is done by the deterministic keyed persist script, in place, keyed to
the domain so it cannot touch a capability outside it inside the spine. /shape never writes the
profile. The model tree is asserted clean at entry (F16) and the play commits its own model
delta at close (C15), so the working-tree diff vs HEAD is exactly this run's delta. Containment
is a post-write scoped guard (`scoped_write_guard.py`), not a draft.
## Compiled From
This play was compiled from the shape ICE (`reference/ice.md`) by play-editor
(#466 Batch C, Level 3 rollout per ADR 025; #467 Batch B — the checkpoint upgraded to a
conditional learned gate, see `standards/rules/gate-config.md`; #500 — migrated to
direct-model-write per ADR 026 and `standards/rules/direct-model-write.md`). Intent defines
constraints (C1–C15) and failure conditions (F1–F16); the expectation defines success
scenarios (S1–S8), a Done means (D1–D3, baked to `stop-condition.yaml`), and one
recovery entry per failure condition. To modify this play, update `reference/ice.md`
and recompile with play-editor.
Do NOT edit this file manually — it is a compiled artifact.
## Role
You are the orchestrator. You own the workflow and the step order. You delegate the
domain work — the grounded selection-and-composition bundle — to the `product-os-keeper`
agent via a JSON contract over files on disk, and you run the mechanical work (readiness
gate, bundle validation, the keyed in-place persist, and the post-write scoped guard)
through bundled scripts. You never write the model YAML yourself, you never create a
functionality or write the product profile, you never write the spine `_spine.yaml` (the keyed
persist owns it), and you never COMMIT the model delta before the single checkpoint (C12)
resolves — a typed approval, a recorded config skip, or a recorded policy auto-pass.
**Forbidden:** hand-writing slice/persona/journey/decision records or the spine; creating a
functionality or authoring ICE (that is /understand's); writing the product profile by any
route; reparenting, renaming, or deleting nodes; editing a capability beyond its `status`
flip; cutting epics or planning slice order; writing the spine by any route other than
`scripts/persist_shape.py`; committing the model delta before Step 5 resolves; running against
a dirty product-os tree (C15/F16); closing COMPLETED without the stop-condition verdict
reading held (C14/F14).
**Agent boundaries:**
| Agent | Domain | Skill it invokes | Phases |
|-------|--------|------------------|--------|
| `product-os-keeper` | Confirm/prune capabilities and select existing functionalities against the firmed profile + KB; create personas, user journeys, decisions; compose the domain's vertical slices behind scaffolded surfaces — writing each RECORD straight to the live model and emitting the spine-delta into the manifest (never the spine, never the profile) | `search-kb`, `author-shape-bundle` | Author |
`product-os-keeper` is the single **domain agent** this play uses (1 of the ≤5
budget). No utility agents are needed — git/issue machinery is absent (position none).
## Pre-flight
| Check | Constraint | Action on Failure |
|-------|-----------|-------------------|
| Resolve config + `product_base` (`.garura/core/config.yaml`) | — | Hard halt |
| Profile firmed (`set`) AND target capabilities `detail: detailed` with functionalities | C1 | Hard halt (REC1) |
| **Clean model tree** — `git status --porcelain -- <product_base>product-os` is empty | C15/F16 | Hard halt (REC16) |
Resolve config mechanically with the bundled resolver; /shape has no branch or issue
(position none):
```
python3 scripts/preflight.py --play shape --config .garura/core/config.yaml
python3 scripts/check_ready.py --spine <product_base>/product-os/_spine.yaml --domain <domain-id>
```
`preflight.py` returns config facts (`product_base`, `stm_base`, `evidence_record`).
`check_ready.py` is the readiness gate (C1): it halts unless the product profile is `set`
(firmed by /understand) and every target capability in the domain is `detail: detailed`
with at least one functionality. The **target domain** is the play argument (e.g.
`/shape token-dash`). If the profile is still `directional` or a capability is not yet
detailed, hard halt and route to /understand (REC1).
**Clean-tree assertion (C15/F16, ADR 026).** Before any work, assert the product-os tree
carries no uncommitted edits — `HEAD` is only a correct base for the scoped guard and the
change-shape if the tree is clean at entry:
```
test -z "$(git status --porcelain -- <product_base>product-os)" || { echo "HALT: dirty product-os tree (REC16)"; exit 1; }
```
If dirty, halt at pre-flight and ask for a clean model tree (commit or revert the pending
model edits, or run the prior pipeline play to its close) before /shape proceeds.
The run's working root (`<working>` below) is `{stm_base}_shaping/shape/<domain>/` — the
routing, the `shape-manifest.yaml`, the change-shape (`shape.json`), the persist record
(`persist-manifest.json`), and the captured scoped-guard report (`guard-report.json`) all
live under it. These are STM, non-model artifacts (ADR 008/017) — the model itself (the slice/
persona/journey/decision records and the spine) is written IN PLACE under
`<product_base>product-os/`, never into `<working>`. The stop condition evaluates against
`<working>`. Status markers live at `{stm_base}_shaping/shape/status/`.
Right after the resolver, record the session identity stamp's start marker (#463 —
soft-fail, never a halt):
```
python3 scripts/session_stamp.py --phase start \
--marker "{stm_base}_shaping/shape/status/session-stamp-shape.json" \
--cwd "$(pwd)" --branch "$(git branch --show-current)"
```
**Resume check:** if `{stm_base}_shaping/shape/status/<domain>.json` exists, resume —
skip completed steps, reset any in-progress step to pending, continue from the first
incomplete.
## Task DAG
Create ALL tasks immediately after resolving config — before any domain work.
The play owns this DAG; the agent must not edit its top-level tasks.
Write-then-review (ADR 026): the FULL model delta — the LLM records AND the keyed persist's
spine mutation — is written to the live model BEFORE the checkpoint, so the guard, the
change-shape, and the human all see the real delta. Nothing is COMMITTED before the gate
resolves; cancel reverts the uncommitted writes.
```
[T1] Author the bundle (records to live) blockedBy: []
[T2] Validate the live bundle blockedBy: [T1]
[T3] Persist (keyed, in place — the spine) blockedBy: [T2]
[T4] Guard the full delta + classify the shape blockedBy: [T3]
[T5] Checkpoint (approval over the full git diff) blockedBy: [T4]
[T6] Commit the model delta blockedBy: [T5]
[T7] Scenario Validation blockedBy: [T6]
[T8] Close blockedBy: [T7]
```
Mark each task in-progress before its step and completed right after its eval passes.
No runtime reordering. On resume, skip completed and reset in-progress to pending.
## Workflow
### Phase: Author (records to the live model)
**Step 1 — Author the bundle (records to live)** · Owner: `product-os-keeper` · Depends on: pre-flight
The persisted model does not record which KB shelf a domain came from, and the product
domain slug is not the KB shelf name. So the agent first **recovers the shelf** with
`search-kb`, then invokes `author-shape-bundle` to author the domain's selection-and-
composition bundle — confirm/prune per capability, select which existing functionalities to
build, the personas, the **user journeys through surfaces**, the decisions, AND the domain's
**vertical slices** (each naming at least one user-facing surface a named persona opens and
checks, bundling functionalities by spine id, plus the `_deferred` bucket), with stable ids.
Per ADR 026 the skill writes each RECORD **straight to the live model** in place and emits the
spine-delta as structured data in the manifest (it never writes `_spine.yaml` or the profile):
{
"task": "recover this domain's KB shelf via search-kb, then SELECT among the functionalities /understand already created against the firmed profile + that shelf, and COMPOSE the domain's vertical slices. Write each slice/persona/journey/decision RECORD straight to the live model in place; emit the spine-delta (capability status flips, the slices index, the refs) as structured data in the manifest — never write _spine.yaml or the profile. Never create a functionality or author ICE. Every slice MUST name >=1 user-facing surface (a thin UI scaffold a named persona opens and checks) — a backend-only/horizontal/whole-UI slice is invalid. Journeys are user journeys ON named surfaces. Name surfaces; never design them. Every selected functionality in a slice or _deferred; slices reference functionalities by spine id; no order/effort/depends_on",
"inputs": { "domain": "<id + slug + path under product-os>",
"product_base": "<product_base>",
"manifest_path": "<working>/shape-manifest.yaml" },
"outputs": { "routing": "<working>/routing.yaml",
"manifest": "<working>/shape-manifest.yaml" }
}
The agent passes the shelf `search-kb` resolved to `author-shape-bundle` as its `kb_shelf`.
It returns the contract with the output paths on disk — never inline content. The skill reads
the live spine **read-only**, writes the records IN PLACE under `<product_base>product-os/`,
and writes `shape-manifest.yaml` under `<working>` (STM) with the spine-delta as structured
data. It writes NO shared model file.
**SE-1 (F1/C1):** the readiness gate passed at pre-flight — the profile is `set` and the
target capabilities are `detail: detailed` with functionalities; otherwise the run halted
(REC1).
**SE-16 (F16/C15):** the product-os tree was clean at entry — the pre-flight assertion
(`git status --porcelain -- <product_base>product-os` empty) passed on a fresh start; a dirty
model tree halted at pre-flight (REC16), so the change-shape and the scoped guard reflect only
this run's delta.
### Phase: Validate
**Step 2 — Validate the live bundle** · Owner: play · Depends on: Step 1
Run the bundle validator over the LIVE records and the manifest before the persist. Under
direct-model-write the records are already written in place, and the spine has NOT yet been
mutated for this run (the keyed persist runs at Step 3). So the validator reads the live records
and resolves `functionality_ref`s against the untouched live spine:
```
python3 scripts/validate_shape.py --root <product_base>/product-os \
--manifest <working>/shape-manifest.yaml --spine <product_base>/product-os/_spine.yaml
```
**SE-2 (F2/C2):** the bundle selects and composes only — it created no functionality and
authored no ICE, and wrote no profile (the records hold only slices, personas, journeys, and
decisions; the spine-delta in the manifest is status flips + slice index + refs).
**SE-3 (F3/C3):** grounding holds — every kept capability and selected functionality in the
manifest carries a KB shelf or a recorded proposal; none is invented.
**SE-4 (F5/C5):** schema + integrity — personas, journeys, decisions, and slices carry
their required fields; every slice `functionality_ref` resolves to a real functionality in the
live spine; every journey persona and surface reference resolves.
**SE-5 (F6/C6):** placement holds — every selected functionality appears in a slice or the
`_deferred` bucket; `validate_shape.py` reports no unplaced functionality.
**SE-6 (F7/C7):** every slice is a user-facing scaffold — it names at least one surface
(name + persona + user_action), and is no backend-only, one-per-capability horizontal, or
whole-UI slice.
**SE-7 (F8/C8):** surfaces are named, not designed — no slice surface carries wireframe,
component, layout, or visual-design content.
**SE-8 (F9/C9):** journeys are user journeys through surfaces — each traverses at least one
named surface, none is a backend pipeline, and every surface a slice names is reached by a
journey.
**SE-9 (F10/C10):** slices reference functionalities by spine id (not copied) and carry no
`order`, `effort`, or resolved `depends_on`.
**SE-10 (F11/C11):** a decision is recorded for every prune and every material selection.
On any GAP, apply the matching recovery (REC2–REC11) and re-run before the persist.
### Phase: Persist (write the spine first, ADR 026 write-then-review)
**Step 3 — Persist (keyed, in place — the spine)** · Owner: play · Depends on: Step 2
Write-then-review (ADR 026): the FULL model delta is written to the live model BEFORE the
checkpoint, so the guard, the change-shape, and the human all see the real delta. The records
are already on the live model (Step 1). `persist_shape.py` now writes the SHARED file — the
spine `_spine.yaml` — in place, keyed to `--domain`: it reads the manifest's `spine_delta` and
flips the named capabilities' `status` field (active/deprecated — status field ONLY), attaches
the persona/journey/decision refs onto those capabilities (additive), and adds the new `slices`
index entries (skip-if-exists) — and it REFUSES to flip any capability the manifest does not
name or one outside the domain, and refuses any profile delta (this is the node-level
containment the file-level guard cannot provide). No draft, no record copy. Nothing is COMMITTED
yet — the commit (Step 6) happens only after the gate approves; on cancel the whole delta is
reverted (Step 5):
```
python3 scripts/persist_shape.py --manifest <working>/shape-manifest.yaml \
--product-base <product_base> --domain <domain-id> \
--out-manifest <working>/persist-manifest.json
```
**SE-12 (F4/C4):** each flipped capability changed in the `status` field only — a prune is
`deprecated`, never a delete; no reparent, rename, create/delete, or other out-of-scope
capability edit (the keyed persist writes only the status field and the ref lists).
### Phase: Guard + Classify (over the full delta)
**Step 4 — Guard the full delta + classify the shape** · Owner: play · Depends on: Step 3
**The run's write scope (the per-play guard policy, ADR 026).** The old
`apply_shape.py` encoded shape's write scope by construction; under direct-model-write that
same scope is the `scoped_write_guard.py` policy — resolve `<domain>` as the domain's folder
under product-os (e.g. `token-dash`):
--allow 'product-os/_spine.yaml' # the shared spine (persist writes: status flips + slice index + refs)
--add-only 'product-os/<domain>/slices/*' # slice records + the _deferred bucket (added)
--add-only 'product-os/<domain>/*/personas/*' # persona records (added)
--add-only 'product-os/<domain>/*/journeys/*' # journey records (added)
--add-only 'product-os/<domain>/*/decisions/*' # decision records (added)
**Guard ONCE over the full delta (C13).** After ALL writes (the LLM records from Step 1 and the
keyed persist's spine write from Step 3), run the scoped guard a single time over the whole
delta. Capture its report — its `ok` field is the stop condition's D3 input (this replaces the
old `check_shape.py` verify):
```
python3 scripts/scoped_write_guard.py --product-base <product_base> --base-ref HEAD \
--allow 'product-os/_spine.yaml' \
--add-only 'product-os/<domain>/slices/*' \
--add-only 'product-os/<domain>/*/personas/*' \
--add-only 'product-os/<domain>/*/journeys/*' \
--add-only 'product-os/<domain>/*/decisions/*' \
--out <working>/guard-report.json
```
If the guard exits non-zero, re-run with `--restore` to revert the offending paths, apply
REC13 (a functionality/domain/profile or an out-of-scope path changed), and re-persist before
the checkpoint.
**Classify the full working-tree delta (C12).** Classify the model tree's diff vs HEAD — now
the FULL delta (records + spine status flips + slice index + refs), per ADR 026 write-then-
review (no draft dir), so a high-impact run (many prunes and new slices) yields a materially
different shape key from a light one:
```
python3 scripts/classify_change.py --play shape \
--product-base <product_base> --base-ref HEAD --out <working>/shape.json
```
**SE-13 (F13/C13):** the scoped-write guard report reads `ok: true` — the model delta is
confined to /shape's scope (the spine plus the new slice/persona/journey/decision records); no
functionality, no domain, and no profile field changed, and no path outside the scope moved.
### Phase: Checkpoint (conditional gate, C12)
**Step 5 — Human review (class: standard, conditional)** · Owner: play · Depends on: Step 4
This checkpoint is a **conditional gate** (#467) per `standards/rules/gate-config.md` —
/shape is one of the eleven conditional document plays. Resolve it first match wins:
pinned (n/a here) → `gates.plays.shape` → the learned policy → `gates.classes.standard`
→ `gates.default` (absent ⇒ on). For the policy lookup, use the shape key classified in
Step 4.
Look the shape key up in the config-resolved policy (`gates.conditional.policy`):
**auto-pass** iff the shape is in the policy's `auto:` block AND not in `never_auto:`
AND Step 2 + Step 4 stand with no blocking finding (a `validate_shape.py` gap — /shape's
lint-equivalent guard — or a guard violation). On auto-pass, do NOT wait: record
`gate auto-passed by learned policy (shape: <shape-key>, policy v<version>)` as a
Checkpoint Decisions row, include the working-tree diff summary in the run record, append
the crossing's live-eval ledger line, and proceed to Step 6 (commit):
```
python3 scripts/gate_eval.py append --ledger <gates.conditional.ledger> --play shape \
--issue <strategy issue> --shape <shape-key> --predicted auto --human auto_pass \
--policy-version <policy version> --ts <run ts>
```
Anything else resolves the gate on (an explicit `gates.plays.shape: off` instead
records `gate skipped by config (<resolution path>)` as a Checkpoint Decisions row and
proceeds). When on, present the domain's selection bundle **inline over the real model git
diff** — the kept capabilities, the pruned capabilities (each with its reason), the selected
functionalities, the personas, the journeys, AND the vertical slices (each led by **the
user-facing surface it exposes**, with its outcome and the functionalities it bundles) plus
the `_deferred` bucket. Say plainly that the surface's design — and the slices' order, effort,
and dependencies — come later (/realize designs the surface; /roadmap plans the slices; /grill
cuts the epics), not here. Render the approval prompt
(`standards/templates/approval-prompt.md`) and wait for the typed response. Approve →
continue to Step 6 (commit). **Cancel → revert the working tree (ADR 026 step 6):** the full
delta is already on disk, so run the guard with `--restore` and an EMPTY allow set to
`git restore` the modified model paths and `git clean`/remove the new ones (byte-clean back
to HEAD), then halt — nothing was committed, and cancel means "revert what was written":
```
python3 scripts/scoped_write_guard.py --product-base <product_base> --base-ref HEAD \
--restore --out <working>/guard-report.json # empty --allow ⇒ every model path reverted
```
Then append the crossing's live-eval ledger line with the human's real action:
```
python3 scripts/gate_eval.py append --ledger <gates.conditional.ledger> --play shape \
--issue <strategy issue> --shape <shape-key> --predicted gate \
--human <approved_clean|approved_edited|rejected> --ts <run ts>
```
`<strategy issue>` is the strategy-pipeline issue the run's branch carries (opened by
/vision's start-change). `<gates.conditional.ledger>` / `<gates.conditional.policy>`
resolve from config `gates.conditional` (defaults `.garura/core/gate-evals.jsonl` /
`.garura/core/gate-policy.yaml`); `<policy version>` is the policy file's `version:`
field. `<run ts>` is the run's own UTC timestamp, derived the same way the close
derives `ts` (`date -u`), passed by the orchestrator.
**SE-11 (F12/C12):** the model delta is COMMITTED only after this gate resolves — a typed
approval, a recorded config skip, or a recorded policy auto-pass; Step 6 is the sole committer
and depends on this step; the full delta written in place (records + spine) shows as the branch
diff and is reverted byte-clean on cancel (ADR 026 step 6), so nothing becomes durable before
the gate resolves.
**SE-15 (F15):** every crossing of this gate appended exactly one live-eval ledger line
(shape, predicted `gate|auto`, the human's real action or `auto_pass`), and an
auto-pass fired only for a shape the policy lists in `auto:` (and not in `never_auto:`)
with no blocking finding standing.
### Phase: Commit (make the delta durable, ADR 026 step 7)
**Step 6 — Commit the model delta** · Owner: play · Depends on: Step 5
The gate approved (or auto-passed / was skipped by config). Commit the full model delta on
the branch (C15, ADR 026 step 7) — a lightweight persist step that makes the writes durable
and advances HEAD; it is NOT the pipeline start/end sequence (shape is a middle play). A
cancelled checkpoint never reaches this step — its tree was already restored in Step 5:
```
git add -- <product_base>product-os
git commit -m "feat(model): shape <domain> — slices, personas, journeys, capability status flips (#<issue>)"
```
**SE-14 (F14/C14):** the close is stop-condition gated — `check_stop_condition.py` over the
baked `stop-condition.yaml` (D1 the persist record `persist-manifest.json` exists; D2 it
stamps `applied: true`; D3 the captured `guard-report.json` reads `ok: true`) must read
**held** before any COMPLETED close, and the model delta is committed (C15); a run whose
persist or guard did not land closes HALTED, never COMPLETED (REC14).
### Phase: Scenario Validation
**Step 7 — Scenario evals** · Owner: play · Depends on: Step 6
- **SCE-1 (S1 — product owner, select + compose):** kept capabilities are `active`, the
selected (already-existing) functionalities are placed into slices, personas and journeys
are created and schema-valid, no functionality was created or changed, and the
stop-condition verdict reads held.
- **SCE-2 (S2 — architect, grounding):** every kept capability and selected functionality
traces to a KB shelf or a recorded proposal in the manifest.
- **SCE-3 (S3 — product owner, prune is soft):** a pruned capability is `deprecated` with a
recorded decision and still present — not deleted.
- **SCE-4 (S4 — reviewer, box and functionalities untouched):** the scoped-guard report reads
`ok`, so the profile is untouched and the model delta shows no change to any functionality or
domain.
- **SCE-5 (S5 — delivery lead, slices reference by id):** each slice has a name, outcome,
acceptance intent, and functionalities referenced by a resolvable spine id, with no copied
content and no `order`/`effort`/`depends_on`.
- **SCE-6 (S6 — planner, full coverage):** the union of all slices' `functionality_ref`s and
the `_deferred` bucket equals the selected functionalities — none unplaced.
- **SCE-7 (S7 — product owner, every slice is a user-facing scaffold):** every slice names at
least one surface (name + persona + user_action) with no design content; zero surface-less
slices, no one-per-capability horizontal slice; every named surface is reached by a journey.
- **SCE-8 (S8 — reviewer, the checkpoint + re-run):** the checkpoint showed kept/pruned
capabilities, functionalities, personas, journeys, and the slices + deferred bucket inline
over the full written delta before any COMMIT, and on cancel the working tree returns
byte-clean to HEAD — or, on the auto-pass path (a policy-listed shape), the gate resolved
with no wait and the recorded auto-pass, the appended ledger line, and the diff summary
stand in the approval's place; a re-run added no duplicate persona, journey, or slice.
### Phase: Evidence & Close
**Step 8 — Close** · Owner: play · Depends on: Step 7
Run the Standard Play Close. /shape is a **product-scoped** play (no issue) — use the
product-scoped evidence base and slug. Evidence recording is play-only and config-gated
per the D1 evidence rule (`standards/rules/evidence-recording.md`).
```bash
# --- Standard Play Close (canonical; see standards/rules/play-close.md) ---
# Path tokens resolved at pre-flight (resolve here if not already):
# ltm_project_target = yq '.ltm.project-target' .garura/core/config.yaml
# evidence_base, slug:
# project-scoped play : evidence_base="${stm_base}${issue}/evidence/shape/" ; slug="#${issue}"
# product-scoped play : evidence_base="${product_base}_evidence/shape/" ; slug="${product_slug}"
evidence_template=$(cat "${ltm_project_target}standards/templates/evidence-file.md")
delivery_template=$(cat "${ltm_project_target}standards/templates/delivery-report.md")
ts=$(date -u +%Y%m%d-%H%M%S)
evidence_dest="${evidence_base}${ts}.md"
mkdir -p "$(dirname "$evidence_dest")"
# Session identity stamp (#463) — close phase; start phase ran at pre-flight
session_stamp=$(python3 scripts/session_stamp.py --phase close \
--marker "${stm_base}_shaping/shape/status/session-stamp-shape.json")
# Stop-condition gate (#464) — Step C0: this play carries a baked manifest, so the
# gate is LIVE. Evaluate the Done means as the close's authoritative input.
python3 scripts/check_stop_condition.py \
--manifest "<play-dir>/stop-condition.yaml" \
--base "${stm_base}_shaping/shape/<domain>/" \
--out "${stm_base}_shaping/shape/status/stop-condition-shape.yaml"
sc_exit=$? # 0 held · 1 unmet · 2 error
# Conditional-gate policy refresh (#467) — soft: a distill failure never blocks the close
python3 scripts/distill_gate_policy.py --ledger "<gates.conditional.ledger>" --policy "<gates.conditional.policy>" --streak <gates.conditional.streak> --project "<project name from config>" || true
```
`/shape` is product-scoped: `evidence_base="${product_base}_evidence/shape/"` and
`slug="${product_slug}"` (the domain slug, e.g. `token-dash`).
**Step C0 — bind the verdict.** `sc_exit == 0` (held) permits `status: COMPLETED`.
Anything else closes `HALTED` with `exit_reason: stop_condition_unmet` and the evidence's
Stop Condition section names every unmet clause — fix the state per REC14 (re-run the keyed
persist, re-capture the scoped-guard report, or make the model-delta commit) and re-evaluate;
the close stays HALTED until the verdict reads held. An unevaluable verdict is never a pass.
**Step C1 — Write evidence file.** Gated by the resolved `evidence.record` flag (global
+ per-play `evidence.plays.shape`; first match wins, absent ⇒ record). When false, skip
the write and record `evidence skipped (record=false)` in the report's pointer line.
Otherwise fill the `evidence-file.md` slots (play `shape`, run_id `shape-${ts}`,
product_slug, started_at/completed_at, status per C0, exit_reason; artifacts produced: the
capability status flips, the selected functionalities, personas, journeys, decisions, the
vertical slices (each with its user-facing surface) + deferred bucket, the shape manifest, the
persist manifest (`persist-manifest.json`), the captured `guard-report.json`, the model-delta
commit sha, the stop-condition verdict; step and scenario eval results SE-1…SE-16 /
SCE-1…SCE-8; checkpoint decision from Step 5 including kept/pruned capabilities and the slices
(or the `gate skipped by config` / `gate auto-passed by learned policy` row when the gate
resolved without a wait) plus the gate ledger line(s) appended this run; the session identity
stamp fields from $session_stamp (#463): session_id, ledger_file, ledger_start_offset,
ledger_end_offset (null when unresolved — never blocks the close); and stop_condition per C0
with the Stop Condition section filled) and write to `$evidence_dest`. Do NOT hand-author the
body.
**Step C2 — Render delivery report.** Also render the **Next** line: resolve this play in `standards/rules/pipeline-next.md` and emit `**Next:** /<command> — <why>. Or run /next to see all recommended actions.` (only /next pointer, or omit, when the mapped command is null), per `play-close.md`. Fill the `delivery-report.md` slots and output the
report: `## shape Delivered — ${product_slug}`, the Run Summary table, the Pipeline
Steps table from the task DAG, the Artifacts Produced table (status flips, selected
functionalities, personas, journeys, decisions, the vertical slices + deferred bucket),
Next Steps (run /roadmap to plan the slices — order, dependencies, effort — across
domains; then /grill cuts a chosen slice into epics), and a pointer to `$evidence_dest`.
Always emitted; never gated.
```bash
# --- end Standard Play Close ---
```
## Scenario Validation
| Scenario | Persona | Eval |
|----------|---------|------|
| S1 — select + compose | product owner | SCE-1 |
| S2 — grounding | architect | SCE-2 |
| S3 — prune is soft | product owner | SCE-3 |
| S4 — box + functionalities untouched | reviewer | SCE-4 |
| S5 — slices reference by id | delivery lead | SCE-5 |
| S6 — full coverage | planner | SCE-6 |
| S7 — every slice is a user-facing scaffold | product owner | SCE-7 |
| S8 — the checkpoint + re-run | reviewer | SCE-8 |
## Recovery
| For | Trigger | Direction | Handoff |
|-----|---------|-----------|---------|
| F1 | the profile is `directional`, or a target capability is not detailed, at start | halt and route to /understand to firm the profile and detail the capabilities first | human |
| F2 | /shape created a functionality, authored ICE, or wrote the profile | strip the over-reach — remove the created functionality/ICE, revert the profile write; detailing and the box are /understand's | autonomous |
| F3 | a selected functionality or kept capability has no KB shelf match and no proposal | ground it against the KB, or record a propose-kb-node proposal; never keep an invented selection | autonomous |
| F4 | a capability edited beyond status, a reparent/rename, a create/delete, or a hard-deleted prune | revert the out-of-scope mutation; /shape flips capability status only and prunes soft (`deprecated`) | autonomous |
| F5 | a persona/journey/decision/slice fails its schema, or a slice/journey reference does not resolve | re-emit the failing record to conform, and fix the reference to a real spine functionality/persona/surface | autonomous |
| F6 | a selected functionality is in neither a slice nor the `_deferred` bucket | place it — add it to a slice, or record it in `_deferred` with a reason — before persisting | autonomous |
| F7 | a surface-less/horizontal slice, or a "whole UI at once" slice | re-cut the slice vertically toward one surface a persona opens and checks, as a thin scaffold; fold or defer functionalities that serve no surface | autonomous |
| F8 | /shape designed a surface (wireframe/component/layout/visual) instead of naming it | strip the design to the surface's name, persona, and user action; design is /realize's UX lens | autonomous |
| F9 | a journey is not a user journey on a surface, or a named surface no journey reaches | rewrite the journey as the persona's steps on a named surface; ensure every slice surface is reached by a journey | autonomous |
| F10 | a slice copied a functionality's content, or carries `order`/`effort`/`depends_on` | replace copied content with a spine `functionality_ref`, and strip the plan fields — /roadmap plans | autonomous |
| F11 | a prune or material selection with no decision record | write the decision (ADR) for each before persisting | autonomous |
| F12 | the model delta was committed with no checkpoint approval | revert the working tree (`scoped_write_guard.py --restore`, empty allow) and re-present the checkpoint; commit only after the human approves | human |
| F13 | a functionality/domain/profile changed, an out-of-scope path changed, or a re-run duplicated a record (scoped-guard violation) | the guard's `--restore` already reverted the offending paths; re-run writing only /shape's scope (spine status flips/slice index/refs, and the new slice/persona/journey/decision records), de-duplicating by stable id, after a human confirms the restore | human |
| F14 | the run is about to close COMPLETED with the Done means unmet | close HALTED (`stop_condition_unmet`) with the unmet clauses named; fix the state — re-run the keyed persist, re-capture the scoped-guard report, or make the model-delta commit — and re-evaluate; the close stays HALTED until the verdict reads held | autonomous |
| F15 | a conditional-gate crossing left no live-eval ledger line, or an auto-pass fired for a shape the policy does not list as auto (or that carried a blocking finding) | re-append the missing ledger line for the recorded crossing; when the auto-pass was unearned, re-run the gate as a live wait — render the approval prompt and wait for the typed response — before proceeding | autonomous |
| F16 | the product-os tree is dirty at entry (uncommitted model edits present) | halt at pre-flight and ask for a clean model tree — commit or revert the pending model edits (or run the prior pipeline play to its close) — before /shape proceeds | human |
## Pause and Resume
Steps run top to bottom. On entry, resolve config, resolve the target domain from the
play argument or the in-progress run, check the status marker, skip completed steps,
reset any in-progress step to pending, and continue. A fresh start with no marker runs
everything and creates the marker at Step 1. Resuming a run that already wrote model
records enters a dirty tree; the pre-flight clean-tree assertion (F16) is scoped to a FRESH
start — a resume continues its own in-progress delta.
## Compilation Metadata
| Field | Value |
|-------|-------|
| fingerprint | sha256:fc97705caf7b0b6d0895dc8f6e87c492fcb365a2e1d711f252734d37f4da0920 (of `reference/ice.md`) |
| compiled_by | play-editor (#500 direct-model-write, ADR 026); prior: play-editor (#467 Batch B, #466 Batch C) |
| pipeline_position | none |
| position_exception | middle of the strategy pipeline — runs on the branch /vision started; the close belongs to /roadmap (#437) |
| workflow_structure | A (single checkpoint — class: standard, conditional gate per gate-config.md #467; direct-model-write WRITE-THEN-REVIEW per ADR 026 — persist + guard + classify before the gate, commit after; stop-condition gated close) |
| stop_condition | stop-condition.yaml (D1–D3), gate live at Step C0 |
| domain_agents | 1 (product-os-keeper) |
| utility_agents | 0 |
| skills_used | search-kb, author-shape-bundle |
| scripts | 9 (preflight.py, check_ready.py, validate_shape.py, persist_shape.py, scoped_write_guard.py, classify_change.py, gate_eval.py, distill_gate_policy.py, check_stop_condition.py, session_stamp.py) |
| step_evals | 16 (SE-1…SE-16) |
| scenario_evals | 8 (SCE-1…SCE-8) |
| recovery_entries | 16 (one per failure condition; 12 autonomous / 4 human) |
**Recompiled note (#500, direct-model-write / ADR 026):** migrated from draft-then-apply to
direct-model-write. The old draft model tree and the apply/check promotion scripts
(`apply_shape.py`, `check_shape.py`) are removed; the authoring skill writes each per-node
record (slice/persona/journey/decision + the `_deferred` bucket) straight to the live model;
the new keyed `persist_shape.py` writes the shared spine (status flips, slice index, refs) in
place, keyed to the domain; containment is the post-write `scoped_write_guard.py` (its
`guard-report.json` is D3); `classify_change.py` reads the working-tree git diff
(`--product-base`/`--base-ref HEAD`); checkpoint cancel reverts the working tree via the guard
`--restore`; the play asserts a clean product-os tree at entry (F16) and commits its own
`feat(model)` delta after approval (C15). Order is **write-then-review** (ADR 026 "Order of
operations"): the full delta — LLM records AND the keyed persist's spine mutation — is written
to the live model FIRST (Steps 1+3), then guarded ONCE and classified over the full delta
(Step 4), then the gate resolves over the real git diff (Step 5), and only an approved gate
COMMITS (Step 6). Nothing becomes durable before approval; cancel reverts the uncommitted
writes. See `standards/rules/direct-model-write.md`.
**Recompiled note (#467 Batch B):** checkpoint upgraded to a conditional learned gate;
see `gate-config.md`.
**Direct-edit deviation note (#500) — INTENT CHANGE, HAND-COMPILED, CONVERGENCE UNVERIFIED:**
This SKILL was updated to the direct-model-write write-then-review shape (ADR 026) by a
**hand-compile from `reference/ice.md`**, NOT by a `/play-editor` run. This is an intent change
(it alters the write path, the containment guarantee, the checkpoint cancel semantics, and the
step order), so the sanctioned path is recompile-via-`/play-editor`; play-editor is
interactive-only (fully gated, human-checkpoint) and cannot run headless in this environment,
so the compiled output was produced by hand to match what play-editor would emit from the
current `reference/ice.md` (fingerprint above). **The `compiled_by` line names play-editor for
provenance intent, but no play-editor run actually occurred and convergence is UNVERIFIED.**
An interactive `/play-editor` convergence run against `reference/ice.md` is **REQUIRED** —
confirming the emitted SKILL matches this hand-compiled body and refreshing the fingerprint.
Note also: `reference/ice.md` C15 distinguishes the model-delta commit from the Standard Play
Close (evidence + delivery report) that /shape still runs; the close anchor block is retained
(required by `lint-components` structural.js and the play-creator G12 emit on every play), and
the `feat(model)` commit is the separate lightweight persist step, so both coexist as C15
describes.
Is this your skill, or is something wrong with this listing? Request removal or report an issue. Author removals are honored within 72 hours.
No comments yet. Be the first to comment!