ワンクリックで
openup-complete-task
Mark a task as complete, update roadmap, commit changes, and prepare traceability logs
Codex または Claude でインストール この Prompt をコピーして Codex、Claude、または他のアシスタントに貼り付けると、Skill ページを確認してインストールできます。
メニュー
Mark a task as complete, update roadmap, commit changes, and prepare traceability logs
Codex または Claude でインストール この Prompt をコピーして Codex、Claude、または他のアシスタントに貼り付けると、Skill ページを確認してインストールできます。
SOC 職業分類に基づく
One-command project setup for OpenUP - interactive initialization wizard
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)
Begin a new OpenUP iteration with proper phase context and task selection
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-complete-task |
| description | Mark a task as complete, update roadmap, commit changes, and prepare traceability logs |
| model | inherit |
| fit | {"great":["finishing a roadmap-tracked task","ending an iteration cleanly"],"ok":["closing out ad-hoc work that needs commit + roadmap update"],"poor":["mid-task checkpoints","abandoning work-in-progress"]} |
| arguments | [{"name":"task_id","description":"The task ID to mark as complete (e.g., T-001)","required":true},{"name":"commit_message","description":"Custom commit message (optional, auto-generates if not provided)","required":false},{"name":"create_pr","description":"Create a pull request after completing the task (default: true — set to 'false' to skip)","required":false}] |
Finalize a completed task: commit remaining changes, update docs, create traceability logs, and create a PR.
IMPORTANT: This is the only way to close out a task. Never offer "just commit" or any other process-bypassing alternative. If the work is too small for this skill, use
/openup-quick-task— that is the lightweight-but-compliant path, not raw commits.
After this skill completes, ALL of these must be true:
n/atype(scope): description [T-XXX]openup-fence.py check exit 0) — no out-of-lane files, no stale viewspython3 scripts/check-docs.py exits 0 — every
authored work-product instance has valid frontmatter, resolvable trace
refs, and resolvable relative linksdocs/status-notes/scripts/sync-status.py (never hand-edited).claude/memory/iteration-learnings.mdopenup-state.py check-gates) and state is archivedcreate_pr was explicitly "false")Before marking a task as complete, verify:
The completion-time counterpart to the spec's ## Verification section: prove the
diff actually satisfies the spec, requirement by requirement, before anything is
committed. This is OpenUP's equivalent of OpenSpec /opsx:verify — same
per-criterion ✅/❌ idiom the rubrics use, applied to the requirements rather than
the spec's own quality.
docs/changes/{task_id}/plan.md ## Requirements.git diff <trunk>...HEAD) and the working tree — not against intentions:
✅ [requirement n] — <where in the diff it is satisfied>❌ [requirement n] — <what the diff is missing>Given / When / Then scenarios (T-020), grade each
scenario's Then as the observable check — run it where it is mechanically
checkable (a command, a file existence test, a passing unit test), read the
diff where it is not./openup-create-task-spec
re-run; per fix-spec-first) so the spec and the diff agree, then re-grade.docs/changes/{task_id}/design.md (it is part of the
traceability trail, not conversation-only state).A requirement that reads ✅ only because "that was the plan" is not verified. Point at the line of the diff (or the green test) that makes it true.
The spec's ## Success Measures section (rubric criterion 12) names the
instrumentation — an event, metric, or query — behind its falsifiable
expectation. Completing the feature without that instrumentation ships a
promise nobody can check.
## Success Measures from docs/changes/{task_id}/plan.md.n/a — <reason> (quick track or argued-unmeasurable): record
n/a in design.md and move on.✅ instrumentation — <where it exists>❌ instrumentation — <what is missing>/openup-create-task-spec) if the measure itself was wrong.docs/changes/{task_id}/design.md
so the retrospective can find the expectation when the read-back date passes.Most changes should already be committed as atomic commits during implementation (see commit-procedure.md). This step handles any leftover uncommitted work — including any docs/agent-logs/ delta auto-log-commit.py appended after an earlier commit in this lane (it can only fire post-commit; see conventions.md's Pre-Commit Housekeeping) and left dirty ever since.
git status --porcelain to check for uncommitted changesgit add <files> — always include docs/agent-logs/ if it shows a delta, folding any hook-appended shard into this commit rather than leaving it for yet another onetype(scope): description [$ARGUMENTS[task_id]]$ARGUMENTS[commit_message] if providedgit status --porcelain returns emptyThe shared views (docs/roadmap.md, docs/project-status.md) may only be
regenerated against the current trunk — regenerating them on a stale base
is what produces parallel-PR conflicts. And the lane's diff must stay inside
its claimed surface. Both are mechanical checks; run them before any view is
touched:
git fetch origin main
git rebase origin/main
# Fence: every changed file vs the trunk must be inside the lane —
# the task's `touches`, its change folder, or a lane-owned audit surface.
python3 scripts/openup-fence.py check
If the fence exits 8, completion is blocked:
OUT OF LANE: <file> — the diff escaped the lane. Either add the path to
the task's frontmatter touches and re-claim (openup-claims.py), or move
the edit to the task that owns that surface.STALE VIEW: <file> — should not occur right after the rebase above; if it
does, the rebase did not complete. Resolve it and re-run.(The same check runs for humans via the .githooks/pre-push hook — see
parallel-lanes.md.)
After the rebase, run the work-product validator. Any authored OpenUP
work-product instance under docs/ (a file whose frontmatter type:
is in the v1 spine: vision · requirement · work-item · iteration-plan ·
use-case · test-case · decision) must validate against the schema, resolve
its trace refs, and resolve its relative .md links:
# Schema + ref/link validation — hard failures block completion.
# Defaults to ./docs; pass --docs <path> to override.
python3 scripts/check-docs.py
# Required-coverage gaps (e.g. an approved requirement with no verified-by
# test). Required-severity gaps fail; advisory gaps are reported only.
python3 scripts/check-docs.py --coverage
If either exits non-zero, completion is blocked. The fix is the fix-spec-first rule:
schema / bad-ref-type / dangling-ref — re-run the originating
/openup-create-<type> skill (so its rubric is re-applied) to author the
missing field, not a hand-edit of the instance frontmatter.broken-link — the relative link target is wrong; either move/rename the
target or fix the link.coverage-gap (required severity) — author the missing test-case
(/openup-create-test-plan) or the missing upstream requirement
(/openup-create-task-spec for the requirement work-product); do not
silence the gap by downgrading the rule to advisory mid-task.Projects with the project-side hook installed (T-039) hit the same check at
git commit; this completion gate is the in-skill equivalent.
The two shared views are derived; this step writes the lane-owned inputs and lets the harness regenerate the views ("if a step is deterministic, the harness does it"). Do NOT hand-edit (or scribe-edit) the Status cells or the project-status header on a task branch.
Write the iteration note as a sharded, lane-owned file (this replaces
the old "prepend to project-status ## Notes" — the shared insertion
point was a guaranteed parallel-PR conflict). Compose the one-bullet
iteration summary (the content needs judgment), then let the scribe script
own the path, dated filename, and collision suffix:
python3 scripts/openup-scribe.py status-note --task-id {task_id} \
--body "- **Iteration N** (YYYY-MM-DD): <one-line summary>"
It writes docs/status-notes/YYYY-MM-DD-{task_id}.md — adding an -HHMM
suffix before .md if that name already exists — and prints the path. You
decide the content; the script owns the mechanics, so the layout never
drifts run to run.
Regenerate both views and record the gate in one deterministic step:
python3 scripts/sync-status.py
This flips the task's roadmap Status cell (stamped completed (YYYY-MM-DD)),
regenerates the project-status header, assembles ## Notes newest-first
from docs/status-notes/, and sets gates.roadmap_synced itself.
Roadmap prose (a program section's "Next step" narrative, Context paragraphs) is not derived. Update it only if it belongs to this task's scope, and only after the step-3 rebase. A scribe may write it.
If the PR later reports conflicts in
docs/roadmap.mdordocs/project-status.md: rebase onto the trunk and re-runpython3 scripts/sync-status.py— never hand-merge these files. Every field they carry is derived, so the re-run resolves the conflict deterministically.
If the spec's ## Rollout section says the change ships behind a feature flag
(rubric criterion 13), the flag's removal debt is enqueued now, in the same
completion — a flag whose removal exists only as an intention outlives every
intention.
Read ## Rollout from docs/changes/{task_id}/plan.md. Not flagged or
n/a → skip this step.
Flagged → add a row to the roadmap's Maintenance table. This is roadmap
content (step 4 point 3 class), not a derived Status cell — author it only
after the step-3 rebase, so T-{next free ID} is read against the
current trunk (allocating IDs on a stale base is how parallel lanes collide
on task IDs). A scribe may write it:
| T-{next free ID} | Remove feature flag `{flag_name}` ({task_id} fully rolled out) | pending | medium | {task_id} |
Completion is blocked until the removal row exists. Record the new task
ID in docs/changes/{task_id}/design.md next to the rollout notes.
Scribe step — collect commit SHAs and metadata yourself (they require git commands). Timestamps come from the clock, never from the model: capture
START=$(python3 scripts/openup-state.py get started_at)(script-stamped at iteration start) andEND=$(date -u +%Y-%m-%dT%H:%M:%SZ), and pass those concrete values — not[ts]placeholders — into the scribe brief for the human-readable.md:Agent(subagent_type="openup-scribe", description="Write agent run log", prompt="Write a traceability log entry. Branch: [branch]. Task: [task_id]. Commits: [sha list]. Phase: [phase]. Start: $START. End: $END. Files changed: [list]. Decisions: [list]. Create docs/agent-logs/YYYY/MM/DD/<timestamp>-agent-<branch>.md with the run metadata above. Report: file path created.")Then append the machine-readable
iteration_completerecord with the deterministic logger — it stampstsitself, keeping the JSONL trail honest (this replaces the old "scribe appends a JSONL record with[ts]" step that produced fabricated times):python3 scripts/openup-state.py log-event \ --event iteration_complete --task-id "[task_id]" \ --branch "[branch]" --phase "[phase]"
Synthesize the learnings from the session yourself — this needs judgment — then let the scribe script append them in the canonical block format (no agent round-trip; the format can't drift):
First, synthesize from your own work:
Then write the entry deterministically:
python3 scripts/openup-scribe.py learnings --task-id {task_id} \
--title "<task title>" \
--what-worked "<text>" \
--decisions "<text>" \
--gotchas "<text>" \
--conventions "<text>"
It appends to .claude/memory/iteration-learnings.md (creating it with a single
# Iteration Learnings header if absent). The synthesis is yours; the write is
mechanical.
Verify all required gates pass before finalizing. If this exits nonzero, completion is blocked — surface the unmet gates (printed one per line on stderr) and resolve each before continuing.
The gate set depends on the track — only full requires a team (teams are opt-in; quick and standard work solo by default, so they do not gate on team_deployed):
# full track (a team was deployed): require the full set
python3 scripts/openup-state.py check-gates --require team_deployed,log_written,roadmap_synced
# quick / standard track (solo, no team): omit the team gate
python3 scripts/openup-state.py check-gates --require log_written,roadmap_synced
(If standard work did explicitly deploy a team, use the full invocation to gate on it.)
Once gates pass, archive the iteration state and the change folder (Ring 2 → docs/changes/archive/).
If the task has a change folder (docs/changes/{task_id}/ exists — the standard three-ring case):
# 1. Flip the spec's frontmatter status to a SATISFIED value — dependency
# resolution (openup-claims.py preflight) reads this frontmatter, NOT the
# derived roadmap, so a dependent task is blocked unless the dep is marked
# satisfied here. Use `verified` for a rubric-graded `full`-track completion
# (the rubric ran), else `done`. An archived plan must never keep a
# non-satisfied status or it false-blocks downstream deps (T-048).
python3 - "$PWD/docs/changes/{task_id}/plan.md" "$PWD/.openup/state.json" <<'PY'
import json, re, sys, pathlib
plan, state = sys.argv[1], pathlib.Path(sys.argv[2])
track = (json.loads(state.read_text()).get("track") or "").lower() if state.exists() else ""
status = "verified" if track == "full" else "done"
t = open(plan).read()
t = re.sub(r'(?m)^status:\s*.*$', f'status: {status}', t, count=1)
open(plan, 'w').write(t)
print(f"plan status -> {status} (track={track or 'n/a'})")
PY
# 2. TEAR DOWN THE SESSION (T-063) — one atomic `end`: archive .openup/state.json INTO
# the change folder as state.json (validate, copy, remove live file), log `session_end`,
# AND release the live claim. Replaces the former separate `openup-state.py archive`
# (here) + `openup-claims.py release` (§7b). Worktree removal stays in §7b.
python3 scripts/openup-session.py end \
--task-id {task_id} --archive-to "docs/changes/{task_id}/state.json"
# 3. Move the whole change folder into the archive ring (preserves history)
git mv "docs/changes/{task_id}" "docs/changes/archive/{task_id}"
Otherwise (legacy / quick-track task with no change folder), end archives the state
into the dated agent-logs tree (release is idempotent — a no-op when the task ran in-place
with no claim):
python3 scripts/openup-session.py end \
--task-id {task_id} \
--archive-to "docs/agent-logs/$(date -u +%Y)/$(date -u +%m)/$(date -u +%d)/state-{task_id}.json"
Either way the live .openup/state.json is removed by end (via the archive step) and
the claim is released. Commit the archive move with the task's other completion commits.
Every completion advances the retrospective cadence. The counter is durable
(.openup/retro.json) and survives the archive above, so this is independent of state
removal — run it once per completed task:
python3 scripts/openup-state.py retro increment # prints the new count
When the count reaches the threshold (5), the next /openup-start-iteration will set
gates.retro_due and refuse a full-track start until /openup-retrospective runs
(which resets the counter). See state-file.md.
The live lease was already released by end in step 7 (release + archive + log are one
atomic teardown, T-063). All that remains is to remove the isolated worktree (T-009).
# Remove the worktree IF one was created (worktree-default-on tasks). Run this from a
# DIFFERENT worktree/the main checkout — git refuses to remove the worktree you are in.
# Add --force only if the tree is known-clean (all work committed/pushed).
REPO=$(basename "$(git rev-parse --show-toplevel)")
git worktree remove "../${REPO}-{task_id}" 2>/dev/null || \
echo "No worktree to remove (in-place task), or run from the main checkout."
If the task ran in-place (worktree: false), there is nothing to remove — end already
released the (possibly absent) claim.
PR is created by default. Skip ONLY if $ARGUMENTS[create_pr] is explicitly "false".
Push the branch:
git push -u origin $(git rev-parse --abbrev-ref HEAD)
Verify unmerged commits exist:
git log <trunk>..HEAD --oneline
Invoke /openup-create-pr skill with task_id: $ARGUMENTS[task_id]
Report result to user:
Auto-merge runs by default. Skip ONLY if $ARGUMENTS[auto_merge] is explicitly "false",
or if step 8 produced no PR (no unmerged commits).
This step runs after the PR is created (step 8). By this point the worktree has been removed
(step 7b), so the executor is in the main repo context on main. Run:
# Merge the PR — merge commit (not squash) preserves atomic commits on trunk
gh pr merge --merge --delete-branch
# Pull the merge commit to local main
git pull origin main
On success: local main now reflects the merged task commits; no stranded branch remains.
On failure (e.g. branch protection requires a review):
<PR URL>, then run git pull origin main."Returns a summary of: