| name | launch |
| position | end |
| description | Land one VALIDATED epic on a human's evidenced acceptance — the HITL gate closing the execute pipeline (implement → validate → launch). Brings the increment up live on the run lens's local environment (cloud is /deploy's), builds HITL testing scenarios from the epic's user_check + acceptance — each telling the human what to RUN and what to TEST — walks them one at a time expecting a typed answer, and only a complete accepted sign-off releases the close chain that merges the epic (then stamped delivered + kept as the as-delivered record, never deleted; prod follows from main via CD). A rejected scenario becomes a defect report on the epic's tracked issue and stamps the epic fix_required — /implement's lightweight fix loop, the same seam /validate uses. An agent never signs for the human (#436). The /launch command in the ProductOS command model. Use when a validated epic is ready for human acceptance. |
| user-invocable | true |
launch
Take one epic /validate stamped validated and land it on a human's evidenced
acceptance — the HITL gate, and it is critical: agents built it, agents verified it
hard, now the HUMAN accepts the running product, and only then does it land. The
increment comes up live on the slice's local environment — the one the run lens's
run.yaml declares for /launch, never a cloud environment — and the play builds the
HITL testing scenarios from the epic's
user_check and acceptance criteria: each tells the human what to run (concrete steps on
the deployed environment) and what to test (what they should see), then expects an answer
before moving on.
The sign-off is the human's, evidenced (#436): every scenario's result is the typed
response recorded verbatim beside what was shown. An agent never answers for the human,
paraphrases hesitation into a pass, or skips an unanswered scenario. A complete accepted
record releases the close chain; a rejection becomes a defect report on the epic's
tracked issue (the project-tracking connection every epic defect honors) and stamps the
epic fix_required — /implement re-enters lightweight with the report as its exact work
list, the same seam /validate uses.
Pipeline position: end. /launch closes the execute pipeline: it runs on the epic
branch the pipeline carries, and after the evidenced sign-off the D2 end sequence —
commit-change → propose-change → review-change → merge-change — lands the work.
After the merge, the epic schema's /merge fill executes: status → delivered, and the
epic record is KEPT in place as the as-delivered record — never deleted. Production
rollout is never this play's act — it follows from main via the run lens's CD. (#434,
decisions 20 + 24; ADR 019, #439)
Compiled From
This play was compiled from the launch ICE (reference/ice.md) by play-editor.
Intent defines constraints (C1–C12) and failure conditions (F1–F11); the expectation
defines success scenarios (S1–S5), 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, the step order, and the HITL walk
itself — presenting one scenario at a time and recording the human's typed answers is
YOUR work, never an agent's. You delegate the two operational pieces — standing the
environment up and authoring the scenarios — via JSON contracts over files on disk, and
you run every mechanical part (eligibility, coverage, the sign-off validation, the close
gate, the defect report, the stamps, the delivered-stamp fill, the final self-check)
through bundled scripts.
Forbidden: answering, paraphrasing, or auto-accepting a scenario for the human
(forged evidence, #436); running the close chain — or any part of it — before the gate
script says release; standing up or probing any cloud environment (dev/qa/stage/prod — /deploy's and CD's); touching production;
deleting the epic at all, or stamping it delivered before the merge member lands; dropping
a rejection instead of reporting it on the tracked issue; hand-rolling commit/PR/merge
steps (they come only via
the injected end members); closing COMPLETED without the stop condition held — the
sign-off gate resolved either way (C12/F11); capping the HITL walk — a human's scenarios
are never capped, the cap concept does not apply to a HITL walk (C12).
Agent boundaries:
| Agent | Type | Domain | Skills it invokes | Phases |
|---|
env-operator | domain | Live environments: stand the increment up on the declared local environment, prove it reachable, tear it down after the walk | stand-up-launch-env | Deploy; Teardown |
product-os-keeper | domain | Author the HITL scenarios from the epic's box | author-hitl-scenarios | Scenarios |
project-orchestrator | utility | Post the defect report to the epic's tracked issue (the epic-defect connection) | manage-issue | Defect path |
repo-orchestrator | utility | Evidence self-commit per the close standard | — | Evidence & Close |
Domain agents: 2 (env-operator, product-os-keeper) — within the ≤5 budget. The injected
end members carry their own agents; not counted here.
Arguments
/launch [--epic <[domain/][slice-id/]epic-id>]
With no argument, resolve the single validated epic carrying this branch's issue_ref.
Pre-flight
The play captures the two live reads once (git branch --show-current;
git status --porcelain to a file), then calls the resolvers — facts from scripts,
policy here:
python3 scripts/preflight.py --play launch --config .garura/core/config.yaml \
--branch "<current branch>" --porcelain-file <captured>
python3 scripts/check_ready_launch.py --product-base <product_base> --epic <epic_id> \
--run-lens <lens_dir>/run.yaml --issue <issue from preflight> \
> {working}/ready-facts.json
| Check | Constraint | Action on Failure |
|---|
| Config + tokens resolved; on the epic's issue branch | — | Hard halt |
Epic stamped validated, issue_ref matches the branch issue, user_check + acceptance present | C1 | Hard halt (REC1) |
Run lens run.yaml declares a local environment (environment resolved in the facts) | C2 | Hard halt — the lens must declare the local environment first |
The epic file resolves from the play argument or the branch issue, exactly as /validate
resolves it. ready-facts.json carries environment (the resolved local environment —
every later environment assertion compares against it), user_check, and acceptance.
{working} resolves to {stm_base}{issue}/launch/ — the stop-condition manifest's
relative paths resolve against the STM root {stm_base}{issue}/.
Right after the resolvers, record the session identity stamp's start marker (#463 —
soft-fail, never a halt):
python3 scripts/session_stamp.py --phase start \
--marker "{stm_base}{issue}/status/session-stamp-launch.json" \
--cwd "$(pwd)" --branch "$(git branch --show-current)"
Resume check: if {stm_base}{issue}/launch/status/progress.json exists, resume —
skip completed steps, reset any in-progress step to pending; an already-answered
scenario is never re-asked (the record stands), an unanswered one is re-presented.
Task DAG
Create ALL tasks immediately after pre-flight — before any domain work. The play owns
this DAG; agents must not edit its top-level tasks. T8–T12 run only on the release
decision; T13–T14 only on fix_required — the gate (T7) decides, never the model.
[T1] Deploy to local env (env-operator) blockedBy: []
[T2] Author HITL scenarios (product-os-keeper) blockedBy: [T1]
[T3] Coverage gate (script) blockedBy: [T2]
[T3b] Surface-contract gate (script) blockedBy: [T3]
[T4] HITL walk (natural loop — one at a time, typed answers; uncapped, exit = complete sign-off or a rejection recorded) blockedBy: [T3b]
[T5] Validate the sign-off record (script) blockedBy: [T4]
[T6] Teardown env (env-operator) blockedBy: [T5]
[T7] Close gate (script decides) blockedBy: [T5]
[T8] commit-change (injected — end, 1st) blockedBy: [T7] — release only
[T9] propose-change (injected — end, 2nd) blockedBy: [T8] — release only
[T10] review-change (injected — end, 3rd) blockedBy: [T9] — release only
[T11] merge-change (injected — end, 4th) blockedBy: [T10] — release only
[T12] Deliver epic (stamp delivered, kept, script) blockedBy: [T11] — release only
[T13] Defect report + post to issue blockedBy: [T7] — fix_required only
[T14] Stamp fix_required (script) blockedBy: [T13] — fix_required only
[T15] Verify the run (script) blockedBy: [T12 or T14, T6]
[T16] Scenario Validation blockedBy: [T15]
[T17] Evidence & Close blockedBy: [T16]
Workflow
Phase: Deploy
Step 1 — Deploy to local env · Owner: env-operator · Depends on: pre-flight
{
"task": "stand up the launch environment on the declared local environment and prove it reachable",
"inputs": { "run_lens": "<lens_dir>/run.yaml",
"environment": "<environment from ready-facts.json>",
"repo_root": ".", "mode": "up" },
"outputs": { "deploy_record": "{working}/deploy.json" },
"task_id": "env-operate"
}
SE-2 (F5/C2): deploy.json exists with reachable: true and environment equal to
the gate-resolved local environment; no cloud environment appears in it (re-asserted
mechanically at Step 11). A wrong environment is torn down and redeployed on the declared
local environment (REC5).
Phase: Scenarios
Step 2 — Author HITL scenarios · Owner: product-os-keeper · Depends on: Step 1
{
"task": "build the HITL testing scenarios from the epic's user_check + acceptance: per scenario, what to RUN (concrete steps from the deploy record's address) and what to TEST (what the human should see); every criterion covered, every scenario tracing to the box",
"inputs": { "epic_file": "<epic_file>",
"deploy_record": "{working}/deploy.json" },
"outputs": { "out_scenarios": "{working}/scenarios.yaml" }
}
Step 3 — Coverage gate · Owner: play · Depends on: Step 2
python3 scripts/check_scenario_coverage.py --scenarios {working}/scenarios.yaml \
--product-base <product_base> --epic <epic_id>
SE-3 (F4/C3): exits 0 — every acceptance criterion and the user_check have a
scenario; every scenario's covers resolves into the epic; every scenario carries run
steps and an observable check. On a gap, rebuild the set (REC4) and re-run.
Step 3b — Surface-contract gate · Owner: play · Depends on: Step 3
The deploy target and the scenarios must match the epic's DECLARED surface
(surface_type on the spine entry, Must open in the epic.md Surface section) per
surface-contract.md — checked here, BEFORE the
human is ever asked to accept anything:
python3 scripts/check_surface_contract.py --product-base <product_base> --epic <epic_id> \
--deploy-record {working}/deploy.json --scenarios {working}/scenarios.yaml
SE-9 (F9/C10): exits 0 — the deploy record's reachable target satisfies the surface:
a web_dashboard/server_api epic has a real openable URL or declared endpoint, never a
local command (python3 ...) or a JSON-artifact path; a cli epic names a command; a
non-user-facing surface claims no URL. A shape that can't satisfy the declared surface is
REJECTED here — redeploy a real reachable target per the run lens and re-check before any
scenario is shown (REC9). SE-10 (F10/C11): exits 0 — every scenario covering a
must_open artifact preserves its user verb; a unit-test/command run standing in for an
"open" check is a verb mismatch, rejected before the walk — the scenario set is rebuilt to
preserve the surface's verbs and the gate re-run (REC10). The gate runs before Step 4, so
neither a mismatched target nor a verb-broken scenario ever reaches the human.
Phase: The HITL walk (the human accepts the running product)
Step 4 — Walk the scenarios (class: one-way-door, pinned) · Owner: play · Depends on: Step 3
Pinned per gate-config.md — #436: an agent never signs for the human; no config value can
disable it.
The walk is a NATURAL loop (C12): one scenario per round, until every scenario carries
the human's typed answer. Its exit is the complete sign-off (or a rejection recorded) —
NEVER an iteration cap: a human's scenarios are never capped; the cap concept does not
apply to a HITL walk. Present ONE scenario at a time, plainly: the title, the run steps
(from the deployed address), and what to check. Then WAIT for the typed response — the
play never proceeds past an unanswered scenario, never suggests the answer, never
converts silence or hesitation into a pass. Record each entry verbatim into the sign-off
record as it happens:
signoff:
scenarios:
- id: hitl-1
shown_to_human: "<the run steps + check exactly as presented>"
human_response: "<the human's typed words, verbatim>"
result: accepted | rejected
A response that does not clearly accept or reject is followed up — plainly, one
question — until the human's own words decide it.
Step 5 — Validate the sign-off record · Owner: play · Depends on: Step 4
python3 scripts/validate_signoff.py --signoff {working}/signoff.yaml \
--scenarios {working}/scenarios.yaml
SE-4 (F3/C4, C9): exits 0 — every scenario has its entry; every entry carries
shown_to_human and a non-empty verbatim human_response; no agent-authored tell; no
unanswered scenario. The record is ON DISK at this point — the durable evidence of the
walk (C9); the close step carries it into the evidence file. A forged or empty entry is
stripped and the scenario re-presented to the human (REC3).
Step 6 — Teardown · Owner: env-operator · Depends on: Step 5
Same contract as Step 1 with "mode": "down" and the existing record. The walk is
done; the environment is not needed for the close. (On any abort, teardown still runs.)
Phase: The gate
Step 7 — Close gate · Owner: play (script) · Depends on: Step 5
python3 scripts/check_close_gate.py --signoff {working}/signoff.yaml \
--scenarios {working}/scenarios.yaml --out {working}/gate.json
SE-5 (F2/C5): the decision is computed mechanically — release only when the
validated record holds zero rejections and zero gaps; the close chain's first member
dispatches only after gate.json reads release (re-proven at Step 11: end evidence
exists ⟺ release). blocked → fix the record per its errors (REC2), never proceed.
SE-11 (F11/C12): gate.json always carries the final resolution fields on BOTH real
outcomes — {resolved: true, outcome: release|fix_required}; a blocked record stays
resolved: false and the walk stays open. The close's stop-condition bind step gates on
it: COMPLETED only with the stop-condition verdict held — an unresolved walk closes
HALTED, never COMPLETED (REC11). The walk itself is never capped — its only exit is the
resolved gate.
Phase: Close chain (injected — D2 position: end) — release only
Step 8 — commit-change · Owner: commit-change (sub-play) · Depends on: Step 7 = release
Step 9 — propose-change · Owner: propose-change (sub-play) · Depends on: Step 8
Step 10 — review-change · Owner: review-change (sub-play) · Depends on: Step 9
Step 11a — merge-change · Owner: merge-change (sub-play) · Depends on: Step 10
Each dispatched with parent_run_id as an explicit member step (JSON contract over
files; evidence under {stm_base}{issue}/evidence/launch/end/). The members own their
own evals. merge-change's record is captured to
{stm_base}{issue}/evidence/launch/end/merge.json — the delivered fill's precondition.
Step 11b — Deliver the epic · Owner: play (script) · Depends on: Step 11a
The epic schema's /merge fill, executed by /launch right after the merge member (the
/implement-anchors-the-/start-fill pattern — merge-change is generic and knows nothing
of epics):
python3 scripts/deliver_epic.py --product-base <product_base> --epic <epic_id> \
--merge-evidence {stm_base}{issue}/evidence/launch/end/merge.json \
--delivery-record {working}/delivery.json
SE-7 (F7/C7): exits 0 — it refuses without merged evidence (the delivered stamp never
precedes merge), records delivered and writes that status back into the kept epic record
(never deletes it); on the defect path this step never runs and the epic stays as-is.
Idempotent on resume.
Phase: Defect path — fix_required only
Step 12 — Render + post the defect report · Owner: play (script), then
project-orchestrator · Depends on: Step 7 = fix_required
python3 scripts/render_defect_report.py --signoff {working}/signoff.yaml \
--scenarios {working}/scenarios.yaml --epic-id <epic_id> --round <n> \
--out-yaml {working}/report.yaml --out-md {working}/report.md
Then dispatch project-orchestrator to post report.md as a comment on the epic's
tracked issue via manage-issue — every epic defect lives on the issue; the loop must
survive a lost session.
python3 scripts/stamp_fix_required.py --product-base <product_base> --epic <epic_id> \
--gate-file {working}/gate.json
SE-6 (F6/C6): on any rejection: report.yaml exists with one finding per rejected
scenario (each citing the human's own words + what was shown — the renderer refuses an
empty-response rejection), the issue carries the posted comment ref, and the epic file
reads fix_required — /implement's fix mode picks the report up as its exact work
list. The play then halts unclosed: no end member dispatches.
Phase: Verify
Step 13 — Verify the run · Owner: play · Depends on: Step 11b or Step 12 (+ Step 6)
python3 scripts/check_launch.py --gate {working}/gate.json \
--facts {working}/ready-facts.json --deploy-record {working}/deploy.json \
--product-base <product_base> --epic <epic_id> --delivery-record {working}/delivery.json \
--report-yaml {working}/report.yaml \
--end-evidence-dir {stm_base}{issue}/evidence/launch/end
SE-1 (F1/C1): holistic re-assert — the run only got here through the eligibility
gate (exit 0 recorded in the progress marker). SE-8 (F8/C8): no production rollout
happened: the play has no prod step by shape, and check_launch.py proves no
cloud-environment target appears in the deploy record — prod (and every cloud tier) is
recorded as following from main via CD, nothing more. check_launch.py exits 0: end evidence exists iff the gate
said release (F2); the deploy stayed on the declared local environment (F5); the epic's end-state
matches the path taken — delivered+kept on release, present+fix_required on defect
(F7/F6).
Phase: Scenario Validation
Step 14 — Scenario evals · Owner: play · Depends on: Step 13
- SCE-1 (S1 — product owner, happy launch): the sign-off record holds every
scenario with a verbatim typed response, all accepted; end evidence exists for all
four members;
deploy.json cites the lens tier; the epic record is present reading
delivered and delivery.json reads delivered.
- SCE-2 (S2 — product owner, rejection): the issue carries the defect-report
comment; the epic reads
fix_required; zero end-sequence evidence exists.
- SCE-3 (S3 — auditor, no forged evidence):
validate_signoff.py exits 0; any
entry missing the human's own text fails it (fixture-proven).
- SCE-4 (S4 — delivery lead, coverage):
check_scenario_coverage.py exits 0 —
zero unmapped in both directions.
- SCE-5 (S5 — operator, safe environment): the deploy record's
environment equals
the facts' resolved local environment and check_launch.py finds no cloud-tier target
anywhere in it.
Phase: Evidence & Close
Step 15 — Close · Owner: play · Depends on: Step 14
Run the Standard Play Close. /launch is project-scoped (the epic's issue).
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 "${stm_base}${issue}/status/session-stamp-launch.json")
python3 scripts/check_stop_condition.py \
--manifest "<play-dir>/stop-condition.yaml" \
--base "${stm_base}${issue}/" \
--out "${stm_base}${issue}/status/stop-condition-launch.yaml"
sc_exit=$?
Step C0 — bind the verdict. sc_exit == 0 (held) permits status: COMPLETED — the
sign-off gate resolved either way counts as done: release with zero rejections, or
fix_required with the defect recorded (C12). 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. There is no loop_cap_exhausted
here — the HITL walk is a human's walk and is never capped; an unresolved walk simply
holds the run open (HALTED at close, resumed at the unanswered scenario).
Step C1 — Write evidence file. Gated by the resolved evidence.record flag (global
- per-play
evidence.plays.launch; 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 launch, run_id launch-${ts}, the
issue, started_at/completed_at, status per C0, exit_reason; artifacts: deploy.json,
scenarios.yaml, the sign-off record, gate.json (with its final resolved/outcome
fields), delivery.json or report.yaml/.md + the stamp, the end-member evidence, the
stop-condition verdict; step + scenario eval results SE-1…SE-11 / SCE-1…SCE-5; the HITL
walk record — C9; 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. Then dispatch
repo-orchestrator for the evidence self-commit when evidence was recorded.
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 it:
## launch — #${issue} (the outcome plainly: delivered → "the epic landed on your
sign-off; production follows from main"; fix_required → "you rejected N scenarios — the
defect report is on the issue; run /implement to fix exactly what it names"), the Run
Summary table, the Pipeline Steps table from the task DAG (including the injected end
members), the Artifacts Produced table, Next Steps, and a pointer to $evidence_dest.
Always emitted; never gated.
Scenario Validation
| Scenario | Persona | Eval |
|---|
| S1 — happy launch | product owner | SCE-1 |
| S2 — rejection | product owner | SCE-2 |
| S3 — no forged evidence | auditor | SCE-3 |
| S4 — coverage | delivery lead | SCE-4 |
| S5 — safe environment | operator | SCE-5 |
Recovery
| For | Trigger | Direction | Handoff |
|---|
| F1 | the epic is not stamped validated | halt; /validate must accept first | human |
| F2 | the close chain attempted with unanswered or rejected scenarios | block the chain; return to the open scenarios | autonomous |
| F3 | a result not in the human's own typed words | strip it, re-present the scenario, record only the typed answer | human |
| F4 | a scenario maps to nothing, or a criterion has no scenario | rebuild the scenario set from the epic's fields | autonomous |
| F5 | an environment other than the declared local one was brought up (a cloud environment) | tear down; bring up only the declared local environment | autonomous |
| F6 | a rejection smoothed over or dropped | build the defect report, post it through the project-tracking role, stamp fix_required | autonomous |
| F7 | the epic deleted at all, stamped delivered before merge, or not stamped delivered after | restore the epic from git if deleted; execute the delivered stamp — merge always first | autonomous |
| F8 | a production rollout attempted | stop it; record the handoff — prod belongs to CD from main | autonomous |
| F9 | the deploy record's reachable target shape doesn't satisfy the epic's surface.type — a local command or JSON-artifact path standing in for a web_dashboard/server_api surface | reject before HITL; require a real reachable target (a local/preview URL or the declared endpoint), redeploy per the run lens, and re-check the shape before any scenario is shown | human |
| F10 | a HITL scenario's verb doesn't preserve the must_open artifact's user verb — a unit-test run covering an "open" check | reject before the walk; rebuild the scenario set so each scenario preserves the surface's verb for the artifact it covers, then re-run the coverage and verb gate | autonomous |
| F11 | the run is about to close COMPLETED with the gate unresolved — gate.json missing or resolved not true | return to the open scenarios (or fix the blocked record per its errors), re-run the close gate so gate.json carries its final resolution, then re-evaluate the stop condition; the close stays HALTED until the verdict reads held — the walk is never capped, its only exit is the resolved gate | autonomous |
Pause and Resume
Steps run top to bottom. On entry, resolve config + the epic, check the progress marker
at {stm_base}{issue}/launch/status/progress.json, skip completed steps, reset any
in-progress step to pending, and continue. The sign-off record is append-only across
resumes: an answered scenario is never re-asked, an unanswered one is re-presented. The
injected end members are themselves resumable. deliver_epic.py is idempotent (an epic
already stamped delivered with its delivery record present is a clean resume, never an
error).
Compilation Metadata
| Field | Value |
|---|
| fingerprint | sha256:68f0632c5699a21df5b322985bb260b066121658700d45e385de394034564e3b (of reference/ice.md) |
| compiled_by | play-editor (#466 Batch B); prior: play-creator; play-editor #439 (ADR 019 — epic kept on delivery), #442 (ADR 022 — surface contract, C10/C11/F9/F10), #434 (per-environment model — LOCAL environment, C2/F5/S1/S5/REC5) |
| pipeline_position | end (the execute closer — commit-change → propose-change → review-change → merge-change injected after the sign-off gate; release path only) |
| workflow_structure | A (the HITL walk is the mandatory human phase — a natural loop, uncapped, exit = the resolved sign-off gate; never skippable; gate pinned per gate-config.md #436) |
| stop_condition | stop-condition.yaml (D1–D3), gate live at Step C0; no iteration cap — a human's scenarios are never capped |
| domain_agents | 2 (env-operator — NEW, product-os-keeper) |
| utility_agents | 2 (project-orchestrator, repo-orchestrator) |
| member_subplays | commit-change, propose-change, review-change, merge-change |
| skills_used | stand-up-launch-env (new), author-hitl-scenarios (new), manage-issue |
| scripts | 12 (preflight.py, check_ready_launch.py, check_scenario_coverage.py, check_surface_contract.py, validate_signoff.py, check_close_gate.py — writes the final resolved/outcome fields on both outcomes, render_defect_report.py, stamp_fix_required.py, deliver_epic.py, check_launch.py, check_stop_condition.py — Done-means gate, session_stamp.py — #463 identity stamp) |
| step_evals | 11 (SE-1…SE-11; one per failure condition; SE-9/SE-10 cover the surface contract C10/C11; SE-11 the gated close C12) |
| scenario_evals | 5 (SCE-1…SCE-5) |
| recovery_entries | 11 (one per failure condition; 8 autonomous / 3 human) |
| structural_constraints | C8-part (no production step exists in the play's shape; the beyond-tier scan re-proves it), C9 (evidence via the Standard Play Close) |
Direct-edit deviation note (#434, spine + grounding model)
Non-intent edit: the epic moved from a per-epic epic.yaml file to an entry in the spine
epics index (product-os/_spine.yaml) plus an epic.md grounding doc, and the run lens
moved from run.yaml to run.md. The mechanism scripts were retargeted:
check_ready_launch.py reads the epic entry by id from the spine, takes user_check +
acceptance from epic.md, and reads the dev/QA tier from the run.md ## Environments section;
check_scenario_coverage.py and check_surface_contract.py read acceptance / surface_type
Must open from the spine entry + epic.md; deliver_epic.py and stamp_fix_required.py
make the validated → delivered / validated → fix_required flips as surgical writes to the
spine epics entry (the entry + epic.md are the kept as-delivered record, never deleted);
check_launch.py proves the end-state on the kept spine entry. Prose invocations updated to
match. No constraint/failure/scenario/eval text changed — the sign-off walk, the surface
contract, the kept-epic guarantee, and the dev/QA-only rule are identical; only the storage
moved. reference/ice.md and the fingerprint are unchanged.
Per-environment migration note (#434) — supersedes the tier parts above
Intent edit (via play-editor, recompiled): the run lens moved to the structured
run.yaml per-environment model, and /launch now brings the increment up on the slice's
LOCAL environment (type local, tier 0) rather than an abstract "dev/QA tier" — cloud
environments (dev/qa/stage/prod) belong to /deploy and CD. C2, F5, S1, S5, and REC5 were
restated from "dev/QA tier" to "the local environment"; reference/ice.md and the
fingerprint changed accordingly. The mechanism was retargeted: check_ready_launch.py
reads the local environment from run.yaml environments[] and emits environment (not
tier) in the facts; the Deploy dispatch passes environment + run.yaml to env-operator
(task_id env-operate, matching the updated agent contract); stand-up-launch-env reads the
local environment's local.run/seed and writes environment in the deploy record;
check_launch.py compares the record's environment to the resolved local one and rejects
any cloud tier. The sign-off walk, surface contract, kept-epic guarantee, and prod-is-CD's
rule are unchanged.