원클릭으로
openup-start-iteration
Begin a new OpenUP iteration with proper phase context and task selection
Codex 또는 Claude로 설치 이 Prompt를 복사해 Codex, Claude 또는 다른 어시스턴트에 붙여 넣으면 Skill 페이지를 검토하고 설치를 진행할 수 있습니다.
메뉴
Begin a new OpenUP iteration with proper phase context and task selection
Codex 또는 Claude로 설치 이 Prompt를 복사해 Codex, Claude 또는 다른 어시스턴트에 붙여 넣으면 Skill 페이지를 검토하고 설치를 진행할 수 있습니다.
SOC 직업 분류 기준
One-command project setup for OpenUP - interactive initialization wizard
Mark a task as complete, update roadmap, commit changes, and prepare traceability logs
Execute an already-claimed lane's Operations boxes with script/judgment classification — script steps run directly with zero self-brief, judgment steps self-brief and execute. Handles only pick/resume; every other resolve path routes to /openup-next.
Run OpenUP Assess Results at iteration end — check evaluation criteria, demo only completed acceptance-tested work, feed discovered work back, and trigger the milestone review at a phase boundary
Run the phase milestone go/no-go — prepare derived evidence, pause for the human decision via an input-request, and record the milestone (never advance the phase itself)
Run ONE OpenUP delivery cycle — resume the active iteration if one stopped mid-work, else claim the top READY lane, else promote the next pending roadmap task and start it. Always advances; only no-ops when nothing is left to do. The sequential continue-loop.
| name | openup-start-iteration |
| description | Begin a new OpenUP iteration with proper phase context and task selection |
| model | haiku |
| fit | {"great":["feature work","multi-step tasks","anything needing a deployed team"],"ok":["single-component changes that benefit from explicit iteration framing"],"poor":["typo fixes","hotfixes","exploratory spikes","throwaway scripts"]} |
| arguments | [{"name":"iteration_number","description":"The iteration number (optional, auto-increments if not provided)","required":false},{"name":"goal","description":"The iteration goal (optional, reads from project-status if not provided)","required":false},{"name":"task_id","description":"The task ID from roadmap to work on (required for task-based branching)","required":true},{"name":"track","description":"Ceremony track (quick|standard|full). Optional — auto-selected from scope when omitted. quick = docs/config/typo/≤~50 LOC single file (state + auto-log only, no plan gate, no team, no readiness); standard = single-feature (plan gate + scribe + /openup-readiness, solo by default — team opt-in); full = multi-role/architectural (standard + team default-on, opt-out + rubric at complete-task). See tracks.md.","required":false},{"name":"team","description":"Team type to automatically deploy after initialization (feature, investigation, construction, elaboration, inception, transition, planning, full, or none)","required":false},{"name":"deploy_team","description":"Whether to deploy a team after iteration initialization (true/false/auto, default: auto — deploy only on the full track; skip on quick/standard. Pass 'true' to force a team, 'false' to force solo)","required":false},{"name":"worktree","description":"Whether to create an isolated git worktree for this task (true/false, default: true — pass 'false' for a legacy in-place checkout). See T-009 / parallel-work.md.","required":false}] |
Initialize a new OpenUP iteration: read project state, create a task branch, and begin work.
After this skill completes, ALL of these must be true:
full track (or an explicit team: / deploy_team: true) calls for a team, it is deployed and coordinating before implementation begins. quick and standard default to solo sequential work — no team required.git rev-parse --abbrev-ref HEAD returns a non-trunk branch name. If it returns trunk, stop and report — do not proceed..openup/state.json createdThis skill has two entry modes, chosen by whether a concrete task_id was
handed in:
task_id is given — a human names a task, or
/openup-next resolved one lane). This is the classic flow: steps 0–9 below
create a branch/worktree for that one task and begin it. Unchanged.task_id — the board's plan-iteration decision, or a
human running /openup-start-iteration with no argument). Instead of starting
one hand-named task, plan a phase-appropriate iteration: derive the phase,
mint an iteration id, choose 1–5 objectives, and generate the iteration's
work-item lanes from the process map — then start its first lane through the
single-lane flow. Run §0b, then rejoin at step 3.The guard is exactly step 0's guard (was I handed a task_id?), extended: no
task_id and no pre-resolved lane ⇒ Plan Iteration (§0b).
Real Plan Iteration (KB §3): commit a set of work items to a named iteration instead of promoting one roadmap row. Everything deterministic is a script call; the only generative step is choosing objectives (the PM/analyst judgment the KB assigns to a human role).
Derive the phase (never guess it):
PHASE=$(python3 scripts/openup-lifecycle.py status --json | python3 -c 'import sys,json;print(json.load(sys.stdin)["phase"])')
Read the Development Case (T-076 tailoring) from docs/project-config.yaml
process: — the archetype sets iteration budget + ceremony. If the archetype
resolves to quick, or this phase is skipped/single-iteration: degenerate to
one work item — take the board's lane.task (the plan-iteration decision's
single next roadmap task) and go straight to the single-lane flow (step 3).
This is the efficiency guardrail: quick costs exactly today's promote path.
Mint the iteration id and record it:
ITER=$(python3 scripts/openup-process-map.py mint-iteration-id "$PHASE") # e.g. C3
Choose 1–5 objectives (the generative step) from, in order: the risk list (highest-exposure items first), the PM value order (roadmap pending order — consume as given, never re-rank), and the phase's objectives. Keep it to what one iteration can deliver.
Read the phase's activity composition from the process map:
python3 scripts/openup-process-map.py activities-for "$PHASE" --json
Each entry is {name, role, skills} — the ordered activities OpenUP runs in
this phase (Inception → vision/use-case/risk with analyst; Elaboration →
architecture/increment/test; Construction → dev/test; per KB §4).
Sketch the candidate work items — one per activity needed to meet the
objectives — deciding each one's planned touches and depends-on (you must
know these to author its spec anyway). Give each a neutral placeholder id for
now (wi-1, wi-2, …); the real cluster-prefixed id is assigned in step 6b
once the partition is known.
6a. Partition the candidates into non-colliding clusters (T-079). Feed the
sketched items to the partitioner — it returns the connected components of
the touches-overlap ∪ depends-on graph, so distinct clusters are disjoint
in touches and dependency-free across clusters, hence safe to run as
concurrent iterations:
echo '[{"id":"wi-1","touches":["scripts/x.py"],"depends-on":[]}, …]' \
| python3 scripts/openup-board.py partition --stdin
# → [["wi-1","wi-2"],["wi-3"]] (one inner list per cluster)
A single returned cluster is the common case (the quick archetype's
single work item is always one cluster) — it degenerates to exactly T-077's
one-iteration behavior. Parallelism appears only when the work is genuinely
disjoint; it is discovered from the structure of the work, never forced.
6b. Mint one named iteration per cluster and author its lanes. The base
mint-iteration-id result ($ITER, e.g. C3) is the first cluster's id;
derive each subsequent cluster's id by offsetting the ordinal by the cluster
index (C3, C4, C5, …) — a deterministic rule that needs no lane
persisted between mints. For each cluster, in the base checkout (so the board
sees the lanes, like the promote path):
python3 scripts/openup-claims.py reserve-id --prefix "${CLUSTER_ITER}-" --pad 3 --session-id "$CLUSTER_ITER" → C3-001…, C4-001…/openup-create-task-spec for change tasks; the activity skills: — e.g.
/openup-create-use-case, /openup-create-architecture-notebook — for
requirements/architecture work), with the activity role recorded as the
lane's hat in ## Operations.Author one iteration-plan instance per cluster —
/openup-create-iteration-plan — each recording its minted iteration id,
the objectives it serves, its committed work-item ids (C3-001… / C4-001…),
and its evaluation criteria. Each instance is the loop contract (§3.5) for
its iteration; the iteration id lives here (state carries only the active
lane's iteration_id, schema 2 — T-078).
Start the first lane of the first cluster through the single-lane flow: set
task_id to that cluster's first committed work item and continue at step 3
(track select) → §5 (branch/worktree) → §6 (begin). Every other lane — in this
cluster and the others — is a READY change folder the board picks next.
pick works
only that iteration's lanes._active_iteration_prefix → None) and picks
the top collision-free lane across both — so disjoint clusters run
concurrently, each lane in its own worktree (branch-per-lane =
worktree-per-lane isolation, live-run F5; see
parallel-lanes.md). An outer /loop or a second
agent begins the other cluster's first lane to actually parallelize.Concurrency is opt-in-by-structure. With one cluster this is exactly T-077 (one iteration, one prefix, sequential). N iterations appear only when the committed work items partition into ≥2 non-colliding clusters — no human hand-wires the parallelism, and cross-iteration task ids never collide because each carries its own iteration prefix.
When /openup-next calls this skill it has already resolved the lane in one
openup-board.py resolve call — the task_id and track you were handed are
the decision. In that case skip steps 1–2's re-read: do not re-open
docs/project-status.md to find the phase/iteration or docs/roadmap.md to
"find the task" — you already have both. Read the phase + iteration number once,
cheaply, from state (python3 scripts/openup-state.py get phase is unavailable
pre-init, so take them from the caller / the task's docs/changes/<id>/plan.md),
and go straight to step 3 (track is given) → step 5 (branch/worktree).
Only when invoked standalone (a human runs /openup-start-iteration with no
pre-resolved lane, or task_id is absent) do steps 1–2 below self-brief by
reading status + roadmap. The guard is simply: was I handed a task_id? If
yes, trust it; if no, read to discover it.
Read docs/project-status.md to establish context:
Read docs/roadmap.md to:
$ARGUMENTS[task_id]Value rationale (if present)The roadmap's pending order is product-manager input — consume it as given.
If the requested task sits below other READY pending entries, that is fine (the
human chose it), but do not yourself substitute a "more valuable" task or
re-order entries here: an item may be passed over only for mechanical reasons
(unmet dependency, collision, lease, missing spec). If the ordering looks wrong,
surface that to the product-manager role (.claude/teammates/product-manager.md)
— never re-prioritize from inside an execution skill.
Choose the ceremony track that matches the task's scope. Use $ARGUMENTS[track] if
provided; otherwise auto-select from the task description / docs/changes/{task_id}/plan.md
scope using this decision table:
| Track | When | Ceremony applied |
|---|---|---|
quick | docs / config / typo / comment / ≤ ~50 LOC, single file | state file + auto-log only — no plan gate, no team, no readiness |
standard | single-feature work | plan gate + scribe logging + /openup-readiness check; team optional |
full | multi-role / architectural / cross-cutting | standard + team deployment + rubric assessment at complete-task |
The intake hook (on-task-request.py) prints a Suggested track: … line — treat it as the
default unless the actual scope differs. The selected track drives two things below: the
Deploy-Team step (step 4) and the --track flag passed to openup-state.py init
(step 6). See tracks.md for the full wiring.
The retrospective cadence is enforced by a durable counter, not a convention. Read it:
python3 scripts/openup-state.py retro get # N = completed tasks since the last retrospective
If N >= 5 AND the selected track is full: REFUSE to start. Do not create a branch
or deploy a team. Print the redirect and stop:
⛔ Retrospective overdue — N iterations since the last one. Run
/openup-retrospectivebefore startingfull-track work (it resets the cadence). Or, if this work is genuinely lighter, re-run withtrack: standard.
If N >= 5 and the track is quick/standard: proceed, but surface a non-blocking
reminder in your summary: "Heads-up: retrospective overdue (N since last) — consider running
/openup-retrospective soon." The hard gate intentionally targets only the heavy track
(WS6b); see design.md DD2.
(gates.retro_due is set to N >= 5 by retro check in step 6 below — this step is the
human-facing decision; step 6 records the gate.)
full only)The default is solo, sequential work — one agent assumes roles as needed (analyst → developer → tester) and persists progress to the repo between steps. A team is deployed only when the track or an explicit argument calls for it; you do not need a team to start.
Track determines the team default:
quick ⇒ no team (proceed straight to step 5).standard ⇒ no team by default — work solo and sequentially. Deploy a team only
if $ARGUMENTS[team] is set or $ARGUMENTS[deploy_team] is "true" (e.g. you want
parallel specialists or independent review).full ⇒ team default-on — deploy the phase default unless $ARGUMENTS[deploy_team]
is "false" or $ARGUMENTS[team] is none.The explicit $ARGUMENTS[deploy_team] / $ARGUMENTS[team] args override the track default.
When a team IS deployed:
Auto-select team type if $ARGUMENTS[team] is not specified:
openup-investigation-teamopenup-planning-teaminception → openup-inception-team (analyst + project-manager)elaboration → openup-elaboration-team (architect + developer)construction → openup-construction-team (developer + tester) ← most commontransition → openup-transition-team (developer + tester + project-manager)Team type override: if $ARGUMENTS[team] is provided, use it directly:
deploy_team: false)Deploy the team using the Agent tool — spawn each role with:
PM takes the lead: after spawning, the project-manager agent coordinates all subsequent work for this iteration. Specialists (developer, architect, tester, analyst) receive focused subtasks from the PM.
Record the team gate — once the team is up, flip the gate in iteration state (created in step 6 below; if the branch/state does not exist yet, do this right after step 6):
python3 scripts/openup-state.py set-gate team_deployed true
Worktree-per-task is the default (T-009 design D6): each iteration gets an isolated
sibling worktree so parallel sessions never share a working tree. Pass worktree: false
to use a legacy in-place checkout (e.g. when a sibling path is impractical, or for a task
— like T-009 itself — that bootstraps the worktree feature).
# 1. Detect trunk
TRUNK=$(git symbolic-ref refs/remotes/origin/HEAD 2>/dev/null | sed 's@^refs/remotes/origin/@@')
[ -z "$TRUNK" ] && TRUNK="main"
git rev-parse --verify "$TRUNK" 2>/dev/null || TRUNK="master"
REPO=$(basename "$(git rev-parse --show-toplevel)")
BRANCH={type}/{task_id}-{short-description}
# 2. Refresh trunk
git checkout "$TRUNK" && git pull origin "$TRUNK" 2>/dev/null || true
# 3a. DEFAULT (worktree-on): isolated worktree on a new branch (worktree FIRST — D6)
git worktree add "../${REPO}-{task_id}" -b "$BRANCH" "$TRUNK"
# → continue all subsequent work inside ../${REPO}-{task_id}
# 3b. OPT-OUT (worktree: false): in-place branch
git checkout -b "$BRANCH"
# 4. VERIFY — this must NOT return trunk
git rev-parse --abbrev-ref HEAD
If the branch already exists, check its status:
begin (T-063)openup-session.py begin folds the entire acquire chain into one call:
stale-lease reap-warn → remote-check (T-044) → pre-flight + claim → heartbeat →
iteration-state init → session_begin log. If any step after the claim fails it
rolls the claim back (release), so a partial begin never strands a half-acquired lane
(Requirement 2). This replaces the former six-call §6/§6b sequence — the individual
openup-claims.py/openup-state.py verbs still exist for direct use, but the skill no
longer sequences them by hand.
The git worktree was already created in step 5 (worktree-first, claim-second) and
worktree creation stays there — begin owns only claim + state + log. The claim's
collision surface (touches) and depends-on are read from
docs/changes/{task_id}/plan.md frontmatter, so persist the plan before begin. Pass
the track selected in step 3 to --track. See
parallel-work.md.
BRANCH="$(git rev-parse --abbrev-ref HEAD)"
python3 scripts/openup-session.py begin \
--task-id "{task_id}" \
--iteration {iteration_number} \
--phase {inception|elaboration|construction|transition} \
--track {selected track: quick|standard|full} \
--branch "$BRANCH" \
--worktree "$(git rev-parse --show-toplevel)" \
--session-id "$BRANCH" \
--iterations-since-retro "$(python3 scripts/openup-state.py retro get)" \
--goal "{goal}" \
--run-id "{run_id}" \
[--plan docs/plans/{plan}.md] # standard/full only; the quick track has no plan gate
rc=$?
if [ "$rc" -eq 9 ]; then
# Remote duplicate (T-044): another clone already owns this task on origin. Record the
# counter event (clock-stamped — decides whether the heavier atomic ref-lock is ever
# justified), then roll the local lane back. Do NOT proceed.
python3 scripts/openup-state.py log-event --event duplicate_start_blocked \
--task-id {task_id} --branch "$BRANCH"
echo "Remote duplicate — another clone owns {task_id}. Do NOT proceed."
echo "Roll back this lane: git checkout \"$TRUNK\"; git branch -D \"$BRANCH\";"
echo " git worktree remove \"../\${REPO}-{task_id}\" 2>/dev/null || true"
exit 1
elif [ "$rc" -ne 0 ]; then
# Pre-flight refused (exit 3 unmet dep / 4 collision) or a post-claim step failed
# (begin already released the claim). Resolve the cause above, then roll back the
# worktree: git worktree remove "../${REPO}-{task_id}"
echo "begin refused (code $rc) — do NOT proceed. Resolve the dependency/collision above."
exit 1
fi
# begin seeded the retro INPUT (--iterations-since-retro); stamp the gate itself:
python3 scripts/openup-state.py retro check # sets gates.retro_due = (count >= 5)
The --iterations-since-retro flag seeds the new state's mirror from the durable
counter (.openup/retro.json), carrying the cadence forward across the iteration that
/openup-complete-task archived. If the team was already deployed in step 4, also run
python3 scripts/openup-state.py set-gate team_deployed true now.
Claims never expire on their own, but a heartbeat-stale claim now self-heals: it is
reaped by openup-board.py refresh (and warned about by the next begin). An abandoned
claim otherwise blocks its surface until a human removes
<git-common-dir>/openup/claims/{task_id}.json. /openup-complete-task's end releases
the claim and removes the worktree.
When this start promoted a task into a new lane (the spec docs/changes/{task_id}/
was just authored — the /openup-next promote path, or any first-time start), commit it
now. openup-board.py reads the working tree, so a spec left only as uncommitted
worktree files is invisible from trunk and from every other worktree/machine — a
"botched promote" that strands the lane and costs a recovery session. Committing here is
what makes the lane survive in git.
# Stage ONLY the spec folder — never docs/roadmap.md or docs/project-status.md.
# Those are derived shared views regenerated by sync-status.py; committing them
# here (on main, before the worktree branch exists) would push a roadmap row
# directly to trunk and cause a merge conflict when the task branch PR lands.
# The roadmap row for this task is added on the task branch (in the worktree),
# not on main — it reaches trunk through the normal PR merge.
# (.openup/ is gitignored Ring-3 state — NOT committed.)
git add "docs/changes/{task_id}/" 2>/dev/null || true
if ! git diff --cached --quiet; then
git commit -m "docs({task_id}): promote lane — author spec, board-visible [{task_id}]"
fi
# Verify: the spec is committed (not dangling) and the board sees the lane.
git status --porcelain "docs/changes/{task_id}/" # expect empty for the spec
If the spec was already committed before this start (e.g. registered in a prior
session / PR), this step is a no-op — git diff --cached --quiet short-circuits.
Check docs/input-requests/ for files with status: answered. Process any answered requests before continuing.
docs/project-status.mdis a derived shared view — do NOT scribe-edit it here.sync-status.pyis the sole writer; any edit made directly on main (or before the worktree branch exists) diverges from the PR copy and causes a merge conflict at push. (Root cause fixed in the local-main conflict after T-056 — see iteration learnings.)The iteration goal and task ID are already captured in
.openup/state.json(step 6) and in the run log (step 9). No further write is needed at start time; the status header is regenerated bysync-status.pyat/openup-complete-task.
Already done by begin (step 6). The session_begin record — carrying the
--goal, --run-id, --branch, and --phase passed to begin — is appended by the
deterministic logger inside openup-session.py begin, which stamps ts from the
system clock (the model never authors a timestamp). No separate log call is needed here;
this step is retained only as the map from the former standalone iteration_start log to
the folded session_begin event.
Returns a summary of: