| name | next |
| position | none |
| description | Recommend what to do next on the product — read the product model's current state (slices, lenses, epics, profile, roadmap order + dependencies), derive every runnable or blocked action through a deterministic decision tree, and present ONE next-best-action plus a ranked list (≤11 entries total), fitted to the person running. The model is the single source: no backlog, sprint plan, or work queue exists or is consulted. Operator fit is computed lexically from the person's git identity + commit history against the KB's work-intelligence shelf (glob path match + BM25 — never inference), and skipped with a notice when history is thin. A model inconsistency that blocks downstream work (e.g. a slice stamped realized with a lens missing) is a repair action and takes the next-best-action slot. Recommend ONLY: the play never modifies the model, never creates work items, never launches a play — multiple people and parallel agents can each pick a different lane from its output. Read-only and position none: runnable anytime, on any branch, even mid-pipeline. Use when the user asks "what next", "what should I work on", "where are we", or wants the backlog-of-the-model ranked. |
| user-invokable | true |
next
Read the product model, work out everything that could happen next — strategy
(vision, understand, shape, roadmap), realization (the six lenses), grilling,
execution (implement, validate, launch), learning (learn), and strategy refresh
once everything is delivered — and recommend: one next-best-action plus a
ranked list of alternatives (parallel lanes included), every entry carrying the
exact command and a plain-language explanation of why and what it unblocks.
The recommendation is advice, never action. The play writes nothing to the
model, opens nothing, launches nothing. Its only durable products are the
recommendation it presents to the user and — when evidence recording is on — the
evidence record of how it was derived; every transient working file it writes is
deleted once the recommendation has been verified and presented (C10).
Compiled From
This play was compiled from the next ICE (reference/ice.md) by play-creator and
recompiled by play-editor (#466 Batch D, Level 3 rollout per ADR 025).
Intent defines constraints (C1–C12) and failure conditions (F1–F9); the
expectation defines success scenarios (S1–S6), 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 run every
mechanical part through bundled scripts — the model scan, the decision tree, the
work classification, and the output enforcement are deterministic code, never
re-reasoned prose (C8). You delegate the one piece of judgment — ranking the
candidate set and writing the explanations — to the product-os-keeper agent,
which invokes the rank-recommendations skill via a JSON contract over files on
disk. You capture the two live git reads yourself at pre-flight (identity and
history); scripts never shell out to git. At close, after the output is verified
and presented, you delete the working folder yourself — the play leaves no scratch
behind (C10); the evidence record is never deleted.
Forbidden: writing anything under the product model; creating issues,
branches, or work items; launching another play from this one; deriving
candidates by reasoning instead of scripts/derive_candidates.py; classifying
the operator by inference instead of scripts/classify_work.py; consulting any
backlog or project store (none exists — the model is the only source, C1);
asserting operator fit when history is thin (C7).
Agent boundaries:
| Agent | Domain | Skills it invokes | Phases |
|---|
product-os-keeper | Rank the derived candidate set into NBA + ranked list with plain-language explanations, applying the work-intelligence shelf's fit rules (or skipping fit when history is thin) | rank-recommendations | Preparation |
product-os-keeper is the single domain agent this play uses (1 of the ≤5
budget). No utility agents — there is no git/issue machinery (read-only,
position none).
Pre-flight
| Check | Constraint | Action on Failure |
|---|
Resolve config (.garura/core/config.yaml) via scripts/preflight.py | — | Hard halt |
Work-intelligence map resolves (<knowledge_dir>/work-intelligence/work-map.yaml) | C7 | Hard halt |
Operator identity captured (git config user.name non-empty) | C7 | Degrade — treat as thin history (no fit), continue |
Product model present under <product_base>/product-os/ | — | Not a failure — feeds the tree's cold-start branch (S4) |
python3 scripts/preflight.py --play next --config .garura/core/config.yaml
preflight.py returns the config facts (product_base, stm_base,
evidence_record). Resolve <knowledge_dir> as the knowledge/ dir under the
memory path from config's ltm section, and the working dir as
<working> = ${product_base}_status/next/ (a play-lifecycle folder; the model
tree itself is never written). /next has no branch or issue resolution
(position none): it runs on whatever branch it finds, including main, and
leaves it untouched.
Capture the two live git reads now — the orchestrator runs them and writes the
files; every script only reads what was captured (layer rule):
git config user.name > <working>/operator.txt
git log --author="$(cat <working>/operator.txt)" \
--name-only --no-merges -n 200 > <working>/git-history.log
Right after the capture, record the session identity stamp's start marker (#463 —
soft-fail, never a halt). /next is read-only and issue-less, so the marker lives in
the play's own working folder — it travels with the run and is removed by Step 9's
self-clean; the stamp's durable copy is the evidence record:
python3 scripts/session_stamp.py --phase start \
--marker "<working>/session-stamp-next.json" \
--cwd "$(pwd)" --branch "$(git branch --show-current)"
Resume check: /next is cheap and advisory — a re-run is always a fresh run.
If <working>/ holds artifacts from a prior run, overwrite them; never resume
a stale recommendation against a model that may have moved.
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.
[T1] Capture & scan the model blockedBy: []
[T2] Derive candidates (the tree) blockedBy: [T1]
[T3] Classify the operator blockedBy: [T1]
[T4] Rank & explain blockedBy: [T2, T3]
[T5] Verify the output blockedBy: [T4]
[T6] Present recommendations blockedBy: [T5]
[T7] Scenario Validation blockedBy: [T6]
[T8] Close blockedBy: [T7]
[T9] Self-clean (delete working dir) blockedBy: [T8]
Mark each task in-progress before its step and completed right after its eval
passes. No runtime reordering. T2 and T3 are independent and may run in either
order once T1 completes.
Workflow
Phase: Preparation
Step 1 — Capture & scan the model · Owner: play · Depends on: pre-flight
Snapshot the model's state — read-only, with a content hash recorded so Step 5
can prove nothing changed:
python3 scripts/scan_model.py --product-base <product_base> \
--out <working>/model-state.json
A missing model is a valid snapshot (model_exists: false — the /vision
branch), never an error.
SE-1 (C1/C2): model-state.json exists and was read from <product_base>
only; when the model exists, model_hash is recorded; the play has written
nothing outside <working>.
Step 2 — Derive candidates (the tree) · Owner: play · Depends on: Step 1
Run the decision tree — strategy → lenses → repair → grill → execute →
learning → refresh — as a pure function of the snapshot:
python3 scripts/derive_candidates.py --state <working>/model-state.json \
--out <working>/candidates.json
SE-2 (F2, F6/C6, C9): candidates.json exists carrying BOTH the candidate
set and the inconsistency report (the report is present even when empty — F2's
detection always runs); every candidate cites its gates, and cross-slice
parallel lanes cite the roadmap order and depends_on that permit them (C6);
the tree's branches span the full loop (C9) — the file records a
derivation_hash over the result for the determinism re-check (C8).
SE-7 (F8/C11): checkable from candidates.json alone — for every slice with a
delivered epic that declares a user-facing surface (web_dashboard/server_api/
cli) and is not surface_verified (its surface-parity check never passed —
a downgrade or a legacy epic), the inconsistency report carries a surface-debt
entry for that slice, a repair/{slice}/surface candidate is present in the
repair lane, and EVERY live execute epic of that slice is blocked naming the
surface debt — no runnable execute candidate survives for a slice in surface debt
(the next epic is withheld). A delivered user-facing epic that is surface_verified
shipped its surface and raises no debt — the slice's execute work proceeds normally.
On a GAP, apply REC8 and re-run derivation.
Step 3 — Classify the operator · Owner: play · Depends on: Step 1
Lexical work-profile from the captured history — glob path matching + BM25
subject scoring against the work-intelligence map; no inference anywhere:
python3 scripts/classify_work.py --git-log <working>/git-history.log \
--map <knowledge_dir>/work-intelligence/work-map.yaml \
--author "$(cat <working>/operator.txt)" \
--out <working>/operator-profile.json
SE-3 (F4/C7): operator-profile.json exists; thin_history is computed
against the map's thresholds; when thin, dominant_work_types is empty — the
profile asserts nothing it cannot back.
Step 4 — Rank & explain · Owner: product-os-keeper · Depends on: Steps 2, 3
The play dispatches a JSON contract; the agent reads the intent pointer, gathers
the fit rules from the work-intelligence shelf, invokes rank-recommendations,
and verifies the artifact shape before returning — paths only, never inline
content:
{
"task": "rank /next's candidate set into one next-best-action plus a ranked list (≤11 total), each entry with the exact command and a plain-language why + what it unblocks; repair takes the NBA slot when inconsistencies exist; apply the shelf's fit rules only when thin_history is false, recording fit_reason on boosted entries; when thin, skip fit entirely and write the fit_notice; never add, drop, or re-derive a candidate — candidates.json is authoritative",
"intent": "core/components/plays/next/reference/ice.md",
"inputs": { "candidates": "<working>/candidates.json",
"operator_profile": "<working>/operator-profile.json",
"fit_rules": "<knowledge_dir>/work-intelligence/operator-fit.md",
"model_state": "<working>/model-state.json" },
"outputs": { "out_yaml": "<working>/recommendations.yaml",
"out_md": "<working>/recommendations.md" }
}
SE-4 (F5/C3/C4): the contract returns with both artifacts on disk;
recommendations.yaml carries an nba, ≤10 further entries, a basis block
pinning model_hash + derivation_hash, and cut_for_cap for every runnable
candidate that didn't make the cap.
Step 5 — Verify the output · Owner: play · Depends on: Step 4
Re-scan, re-derive, and run the enforcer — this is where the read-only proof,
the determinism proof, and every output rule are checked mechanically:
python3 scripts/scan_model.py --product-base <product_base> \
--out <working>/model-state-after.json
python3 scripts/derive_candidates.py --state <working>/model-state-after.json \
--out <working>/candidates-after.json
python3 scripts/check_output.py \
--recommendations <working>/recommendations.yaml \
--candidates <working>/candidates.json \
--candidates-after <working>/candidates-after.json \
--state-before <working>/model-state.json \
--state-after <working>/model-state-after.json \
--profile <working>/operator-profile.json
SE-5 (F1, F3, F5, F6 / C2, C4, C5, C7, C8): check_output.py exits 0 —
the model hash is unchanged (F3/C2: the play wrote nothing); the after-state
re-derivation matches the consumed derivation hash and every recommended entry
maps to a still-runnable (or named-blocker) candidate (F1/C8); the cap and
explanations hold (F5/C3/C4); repair holds the NBA when inconsistencies exist
(C5); no fit language under thin history and the notice is present (F4/C7);
every runnable candidate is listed or declared cut (F6).
On any GAP, apply the matching recovery (REC1–REC6) and re-run this step.
Step 6 — Present recommendations · Owner: play · Depends on: Step 5
Output <working>/recommendations.md to the user inline — the NBA as the
lead, the ranked list with one plain-language line each, the repair section when
the model needs fixing, and the fit summary or skip notice at the end. The
operator (or each parallel agent) picks an entry and runs its command
themselves — this play launches nothing (C2).
Phase: Scenario Validation
Step 7 — Scenario evals · Owner: play · Depends on: Step 6
- SCE-1 (S1 — builder, product mid-delivery): with a realized-but-lens-missing
slice and ready epics behind it, the NBA is the repair (the missing lens play)
and every blocked epic entry names the missing lens as its blocker — checkable
from
recommendations.yaml + candidates.json alone.
- SCE-2 (S2 — implementer, rich history): with two runnable epics and a
non-thin profile, the work-type/stack-matching epic ranks above the
non-matching one and carries a
fit_reason stating why.
- SCE-3 (S3 — new contributor, thin history): with history below the map's
thresholds,
fit_notice is present, no entry carries a fit_reason, and two
runs over the same model state produce identical entries (the derivation and
ranking basis hashes match).
- SCE-4 (S4 — founder, fresh start): with no product model,
the NBA's command is
/vision and its why explains the cold start.
- SCE-5 (S5 — product owner, everything delivered): with all slices realized
and every epic delivered, the list contains a learning (
/learn) or
strategy-refresh (/shape from deferred, /roadmap) entry with a
plain-language explanation — never "nothing to do".
- SCE-6 (S6 — team running parallel agents): with slice 1 mid-execution and
slice 2 planned with satisfied dependencies, a slice-2 lens entry appears
flagged as a parallel lane, its gates citing the roadmap order that permits it.
Phase: Evidence & Close
Step 8 — Close · Owner: play · Depends on: Step 7
Run the Standard Play Close. /next 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).
This phase ends with Step 9 — Self-clean (the bash block after the close): once
the evidence record and delivery report are written, the working folder is deleted
as the terminal action, so the run leaves no scratch behind (C10). Only the working
folder is removed — never the evidence folder, which is the durable record.
SE-6 (C10/F7): after Step 9, ${working} no longer exists on disk while the
evidence folder (when evidence recording is on) is intact — the play's only
surviving products are the presented recommendation and the evidence record. On a
GAP (working files remain), apply REC7 and re-run Step 9.
SE-8 (F9/C12): the close is stop-condition gated — check_stop_condition.py
over the baked stop-condition.yaml (D1 the derived candidate set record exists;
D2 the ranked recommendations yaml exists; D3 the human recommendations report
exists) reads held before the run closes COMPLETED; anything else closes
HALTED with the unmet clauses named. The gate runs at Step 8, BEFORE Step 9's
self-clean removes the working folder. This play has no checkpoint, so there are
no gates to class under gate-config.md — the stop-condition gate and the
evidence flag are the close's only switches.
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_stamp=$(python3 scripts/session_stamp.py --phase close \
--marker "<working>/session-stamp-next.json")
python3 scripts/check_stop_condition.py \
--manifest "<play-dir>/stop-condition.yaml" \
--base "<working>/" \
--out "<working>/stop-condition-next.yaml"
sc_exit=$?
/next is product-scoped: evidence_base="${product_base}_evidence/next/" and
slug="${product_slug}" (the product, e.g. token-burn-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. An unevaluable verdict
is never a pass.
Step C1 — Write evidence file. Gated by the resolved evidence.record flag
(global + per-play evidence.plays.next; 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 next,
run_id next-${ts}, product_slug, started_at/completed_at, status per C0,
exit_reason; artifacts produced: the two recommendation artifacts, the model-state
snapshots, the candidate sets, the operator profile, the stop-condition verdict;
step and scenario eval results SE-1…SE-8 / SCE-1…SCE-6; checkpoint decisions:
none — this play has no gates; 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: ## next Delivered — ${product_slug}, the Run Summary table,
the Pipeline Steps table from the task DAG, the Artifacts Produced table (the
NBA, the ranked entries, the inconsistency count, the fit basis), Next Steps
(the NBA's command — the whole point), and a pointer to $evidence_dest.
Always emitted; never gated.
Step 9 — Self-clean · Owner: play · Depends on: Step 8 (declared above; SE-6)
The terminal action. The recommendation has been verified (Step 5), presented
(Step 6), scenario-checked (Step 7), and the evidence record + delivery report
written (Step 8) — the working folder now has no further reader. Delete ONLY the
working folder so the run leaves no scratch behind (C10); never touch the evidence
folder (${product_base}_evidence/next/), the durable record. The stop-condition
verdict and the session-stamp marker go with the working folder — their durable
copies live in the evidence record written at Step 8.
rm -rf "${working}"
Scenario Validation
| Scenario | Persona | Eval |
|---|
| S1 — product mid-delivery, broken gate | builder | SCE-1 |
| S2 — rich work history | implementer | SCE-2 |
| S3 — thin history | new contributor | SCE-3 |
| S4 — fresh start | founder | SCE-4 |
| S5 — everything delivered | product owner | SCE-5 |
| S6 — parallel lanes | team running parallel agents | SCE-6 |
Recovery
| For | Trigger | Direction | Handoff |
|---|
| F1 | a recommended command would halt at its own gate when re-checked | re-derive candidates from current model state and regenerate the output; if the play's rules disagree with the target play's gate, surface the divergent rule | autonomous |
| F2 | downstream work halts on an inconsistency the run reported nothing about | record the missed inconsistency class, extend the consistency scan, re-issue recommendations | human |
| F3 | anything in the model changed, or a play was launched, during the run | halt immediately and surface exactly what changed — nothing is auto-reverted | human |
| F4 | fit reasoning appears in the output while history is below the trust threshold | strip fit weighting, regenerate ranking on model state alone, add the skip notice | autonomous |
| F5 | more than 11 entries, or an entry with no explanation | re-rank, cut to the cap, write the missing explanations | autonomous |
| F6 | the readiness gates admit an action the candidate set lacks | re-run derivation; if the action is still missing, the derivation rules are incomplete — record the gap | autonomous |
| F7 | working files remain after the run completes | delete the working folder; the already-presented recommendation is the only product | autonomous |
| F8 | a slice has surface debt (a delivered epic with unmet required user-facing surface) and /next would recommend its next execute epic | surface the debt as a blocking inconsistency, recommend the surface-repair action for that slice in the next-best-action slot, and withhold every further execute epic of that slice until the debt clears | autonomous |
| F9 | the close would report COMPLETED without the Done means held | evaluate the stop condition and surface the unmet clauses; re-run the producing step (derivation or ranking) to restore the missing artifact, or close HALTED with the verdict recorded | autonomous |
Pause and Resume
Steps run top to bottom. /next does not resume — a recommendation is only as
good as the model state it was derived from, so any interruption means a fresh
run: re-capture, re-scan, re-derive. Prior artifacts in <working>/ are
overwritten. There is no status marker.
Compilation Metadata
| Field | Value |
|---|
| fingerprint | sha256:8688af627c2acc30043b31a183fda12d142c1ebb4749e640114f6d476157f807 (of reference/ice.md) |
| compiled_by | play-editor (#466 Batch D, Level 3 rollout per ADR 025); prior: play-creator |
| pipeline_position | none |
| structural_constraints | C1 (scan + derivation read only the model tree — by construction of scan_model.py/derive_candidates.py); C9 (the decision tree's branches span strategy → lenses → repair → grill → execute → learning → refresh — derive_candidates.py); C11 (surface debt is detected mechanically — scan_model.py reads each epic's surface.type + surface_verified, derive_candidates.py flags a delivered user-facing epic that is not surface_verified, emits the surface-debt inconsistency + repair lane, and withholds further execute epics) |
| workflow_structure | readiness-only (analysis, no generation; no checkpoint — the output is advice; nothing to class under gate-config.md) |
| stop_condition | stop-condition.yaml (D1–D3), gate live at Step C0 — evaluated BEFORE the Step 9 self-clean |
| domain_agents | 1 (product-os-keeper) |
| utility_agents | 0 |
| skills_used | rank-recommendations |
| scripts | 7 (preflight.py, scan_model.py, derive_candidates.py, classify_work.py, check_output.py, check_stop_condition.py, session_stamp.py) |
| step_evals | 8 (SE-1…SE-8; every failure condition covered: F1→SE-5, F2→SE-2, F3→SE-5, F4→SE-3+SE-5, F5→SE-4+SE-5, F6→SE-2+SE-5, F7→SE-6, F8→SE-7, F9→SE-8) |
| scenario_evals | 6 (SCE-1…SCE-6) |
| recovery_entries | 9 (one per failure condition; 7 autonomous / 2 human) |