en un clic
spectra-archive
Archive a completed change
Installer avec Codex ou Claude Copiez ce prompt, collez-le dans Codex, Claude ou un autre assistant, puis laissez-le vérifier la page du skill et l'installer pour vous.
Menu
Archive a completed change
Installer avec Codex ou Claude Copiez ce prompt, collez-le dans Codex, Claude ou un autre assistant, puis laissez-le vérifier la page du skill et l'installer pour vous.
Basé sur la classification professionnelle SOC
Implement or resume tasks from a Spectra change
Have a focused discussion about a topic and reach a conclusion
Implement or resume tasks from a Spectra change
Archive a completed change
Have a focused discussion about a topic and reach a conclusion
Orchestrate a task inside a fresh git worktree end-to-end. /wt TASK builds a worktree, auto-routes the executor (Claude subagent for UI work, Codex for non-UI coding/analysis/debug), and reports back. Override with --claude or --codex. Supports parallel multi-task via /wt A: ... B: .... Use when 使用者說「worktree」「開 wt」「並行做」,或需要在隔離環境跑 task 不影響主 working tree。
| name | spectra-archive |
| description | Archive a completed change |
| effort | low |
| license | MIT |
| compatibility | Requires spectra CLI. |
| metadata | {"author":"spectra","version":"1.0","generatedBy":"Spectra"} |
Archive a completed change.
Ownership(clade fork;cross-phase matrix in
rules/core/spectra-workflow.md):archive gate-check 負責 archive 前再跑 Layer C data-sanity +archive-gate.shCheck 1–7(journey / schema-types / exhaustiveness / manual-review kind / screenshot quality / stale verified-ui / pre-handoff-verdict)。不負責上游 phase 已 own 的 checkpoint(propose Layer A、apply Step 6c/Layer B、pre-handoff Step 8a.6/Layer E 各在自己 phase 抓);Check 7 只是機械驗證 Step 8a.6 的 E.1 verdict record 已存在(缺則擋 archive),不重跑 self-analysis 本身。
Input: Optionally specify a change name after /spectra-archive (e.g., /spectra-archive add-auth). If omitted, check if it can be inferred from conversation context. If vague or ambiguous you MUST prompt for available changes.
Prerequisites: This skill requires the spectra CLI. If any spectra command fails with "command not found" or similar, report the error and STOP.
Before running archive, classify the change into one of four dispositions. Pick the wrong path and you either (a) burn user time on a review that doesn't apply, (b) write spec for a feature that won't ship, or (c) abandon work that's actually still pending.
| Disposition | Trigger | spec sync | manual review | CLI |
|---|---|---|---|---|
| Park | Change is postponed but still planned. Premise still valid. | — | — | spectra park <name> (NOT this skill) |
| Standard archive | Implementation [x] complete + ## 人工檢查 [x] complete | applied | already done | spectra archive <name> (default flow) |
| Skip-archive (A) | Won't be implemented. Premise dissolved (decision changed / concept removed / change became redundant). | skipped | skipped | spectra archive <name> --mark-tasks-complete --skip-specs --no-validate -y |
| In-main-done archive (B) | Implementation [x] complete (built directly on main, no session worktree). Only ## 人工檢查 items remain unchecked. | applied | skipped | spectra archive <name> --mark-tasks-complete --no-validate -y |
| Apply-required (block) | Non-## 人工檢查 items still [ ] + change is still wanted. | — | — | STOP — run /spectra-apply <name> first, then re-classify |
## 人工檢查 task sections are fully [x]. Use the inspection check below.archive: <name>; skip — <reason premise dissolved>archive: <name>; in-main-done — <reason no worktree, e.g. built directly on main>To distinguish In-main-done archive (B) from Apply-required, parse openspec/changes/<name>/tasks.md:
awk '/^## 人工檢查/{mr=1; next} /^## /{mr=0} !mr && /^- \[ \]/{print NR": "$0}' \
openspec/changes/<name>/tasks.md
[x] → eligible for In-main-done archive (B)/spectra-apply <name> to finish them, then re-classify--mark-tasks-complete non-manual-review tasks without classifying — that hides incomplete implementation behind a green archive.node scripts/wt-helper.mjs cleanup <name> --force --force-discard-unland to drop branch + worktree; do NOT run merge-back (nothing meaningful to merge).wt-helper merge-back ... --noop-if-missing becomes silent no-op — proceed."Skip 人工檢查 + archive" is ambiguous between A and B. MUST disambiguate with the user before running:
Worktree exemption (clade fork addition): This skill is exempt from the [[worktree-default]] §1 worktree requirement. Archive is main-bound — every output (delta sync into openspec/specs/<capability>/spec.md, move into openspec/changes/archive/, screenshot sweep) targets main, so running inside a worktree adds a mandatory merge-back with no isolation benefit. The skill SHALL proceed regardless of whether cwd is on the main worktree or inside a session worktree; the orchestrator (e.g., /handoff Mode B Step 2B.5) SHALL dispatch this skill directly without routing through /wt. Do NOT add prose instructing the user to "open a worktree first" — that contradicts the §1 exemption.
Atomic merge-back contract (clade fork addition): Per [[worktree-default]] §5.5, worktree branches do NOT squash back to main at /wt return time — they wait until archive. Step 0 below absorbs any slug-matching session worktree into main BEFORE the archive gates run, so gates inspect the post-squash state and so main never carries half-done work between sessions.
Steps
Atomic merge-back from active worktree (clade fork addition)
Per [[worktree-default]] §5.5, any session worktree whose slug matches this change-name MUST be absorbed into main before archive gates run. If gates run on un-absorbed main, they would see a false-clean diff (worktree changes never landed) and produce a misleading archive.
Sidecar init (TD-155) — before running merge-back, create the in-flight sidecar so that subsequent steps record progress and an interrupted run leaves a detectable orphan:
node scripts/spectra-archive-sidecar.mjs init <change-name>
.spectra/in-flight-archive/<change-name>.json (helper resolves via git rev-parse --git-common-dir), so a linked worktree's archive is visible from main on the next session start (cross-session detection per plugins/hub-core/hooks/session-start-spectra-resume-check.sh).scripts/spectra-archive-sidecar.mjs does not exist (consumer pre-propagation), skip silently with a one-line note: Sidecar: skipped — helper not available (consumer pre-propagate).phase=merge-back), do NOT re-init — proceed directly to the merge-back command below.node scripts/wt-helper.mjs merge-back <change-name> --auto-stash --noop-if-missing
--noop-if-missing makes this a silent no-op when no matching worktree exists (solo archive path — change implemented directly on main).--auto-stash stashes any main-worktree blockers as wt-merge-block/<change-name>/<ISO> for later reconciliation via node scripts/stash-reconcile.mjs.Skip condition: if scripts/wt-helper.mjs does not exist (consumer hasn't propagated the merge-back subcommand yet), skip this step silently with a one-line note: Step 0: skipped — wt-helper merge-back not available (consumer pre-propagate).
After merge-back, sweep sibling worktrees that carry fork-time stale copies of the change being archived (prevents spectra CLI cross-worktree existence check from refusing):
node scripts/wt-helper.mjs sweep-siblings <change-name>
Sidecar advance (TD-155) — after merge-back returns (whether absorbed, no-op, or skipped), advance phase:
node scripts/spectra-archive-sidecar.mjs update <change-name> --phase gate-check --last-completed merge-back
(silent fail-safe: if sidecar helper or sidecar file is missing, ignore — sidecar lifecycle is best-effort visibility, not a hard dependency of archive correctness.)
Output (when worktree absorbed):
merge-back: <change-name> absorbed into main — proceed to Step 1merge-back: <change-name> absorbed into main (blockers stashed as wt-merge-block/<name>/<ISO>) + worktree cleaned — proceed; remind user in Step 8 summary that stash entry needs reconciliation0.5. Resume / mid-flight detection (clade fork addition — fires only when explicit change-name given)
Two independent resume paths share this step:
Before anything else, check for an in-flight sidecar:
node scripts/spectra-archive-sidecar.mjs read <change-name> 2>/dev/null
Branch:
/spectra-archive <X> --resume → enter Mid-flight resume mode. Parse sidecar's phase field and jump per Resume Dispatch Table below. Do NOT re-init the sidecar; do NOT re-run completed phases.--resume → STOP. Prompt the user:
Previous /spectra-archive for
<X>interrupted at phase<phase>(sidecar started<ISO>). Options: a) Resume — re-run as/spectra-archive <X> --resumeb) Discard previous run and start fresh —node scripts/spectra-archive-sidecar.mjs delete <X>then re-invoke without--resumeChoose a / b. Standard archive cannot proceed while a sidecar exists.
Read sidecar via node scripts/spectra-archive-sidecar.mjs read <change-name> (parse JSON .phase):
phase value | Action on --resume |
|---|---|
merge-back | re-run Step 0 from the top. wt-helper merge-back --noop-if-missing is idempotent — if the worktree was already absorbed in the prior run, it silently no-ops. |
gate-check | jump to Step 2 and re-run gates (2 / 3 / 3.3 / 3.5 / 5.5). All gates are idempotent: status / task / pattern checks are read-only; the [discuss] walkthrough in Step 3.5 only re-prompts items still unchecked. |
spec-sync | jump to Step 4 and re-run delta spec assessment. Comparison is idempotent. |
folder-mv | STOP — manual fixup required. Reason: Step 6 invokes spectra archive CLI which is a black box from clade's POV; mid-flight interrupt may leave openspec/changes/<X>/ partially renamed and openspec/specs/<cap>/spec.md deltas partially applied. Show the user: "phase=folder-mv means spectra archive CLI was mid-flight when interrupted. Cannot safely retry — reality is unknown. Manual fixup: (a) inspect openspec/changes/<X>/ and openspec/changes/archive/YYYY-MM-DD-<X>/ directory states; (b) inspect git status for partial spec delta writes; (c) reconcile by hand (either complete the move or roll back), then node scripts/spectra-archive-sidecar.mjs delete <X> and re-invoke from a clean state." |
screenshot-sweep | jump to Step 7 and re-run screenshot sweep. screenshots-archive Mode B is idempotent on re-copy (existing destination files are silently kept). |
cleanup | jump to Step 7.5 and re-run stash reconcile + Step 8 summary. Both are near no-ops on re-run. |
When no sidecar exists, also check the legacy discuss-deferred path:
if [ ! -d "openspec/changes/<change-name>" ] && [ -d "openspec/changes/archive/<change-name>" ]; then
if grep -q '(deferred-to-handoff:' "openspec/changes/archive/<change-name>/tasks.md" 2>/dev/null; then
# Discuss-deferred resume candidate
...
fi
fi
<name> not found in active or archived"(deferred-to-handoff: annotation → STOP with note: "change <name> already fully archived; nothing to resume"(deferred-to-handoff: annotations → enter Discuss-deferred resume mode: skip Step 0 (merge-back), skip Step 1 (selection prompt), skip Steps 2 / 3 / 3.3 / 3.5 / 4 / 5 / 6 / 7 / 8 entirely. Jump to Step 3.5b — Resume walkthrough below; that step is the only work performed in this resume path.Standard archive runs (active change directory exists + no sidecar) MUST NOT trigger discuss-deferred resume even if a homonymous archived change has deferred items.
Skip Step 0.5 entirely when no change-name was provided (Step 1 still handles selection from active changes).
If no change name provided, prompt for selection
Run spectra list --json to get available changes. Use the request_user_input 工具 to let the user select.
Show only active changes (not already archived). Include the schema used for each change if available.
IMPORTANT: Do NOT guess or auto-select a change. Always let the user choose.
Check artifact completion status
Run spectra status --change "<name>" --json to check artifact completion.
Parse the JSON to understand:
schemaName: The workflow being usedartifacts: List of artifacts with their status (done or other)If any artifacts are not done:
Check task completion status
Read the tasks file (typically tasks.md) to check for incomplete tasks.
Count leaf tasks marked with - [ ] (incomplete) vs - [x] (complete). For ## 人工檢查 items, parent #N lines that own scoped #N.M children have state derived from children (see rules/core/manual-review.md「Parent State Derivation」) — never count those parent lines directly.
If incomplete tasks found:
If no tasks file exists: Proceed without task-related warning.
3.3. Manual-Review Pattern Re-check (clade fork addition — archive gate for ## 人工檢查 hygiene)
## 人工檢查 items can drift between propose / ingest and archive (apply phase edits, screenshot annotations, fix-up rewrites). Re-run the same enforcement hook that /spectra-propose uses, so jargon leakage / abstract reference / missing URL etc. doesn't slip into archive:
bash scripts/spectra-advanced/post-propose-manual-review-check.sh <change-name>
Exit 2 = pattern findings (any of ABSTRACT_REFERENCE / CARD_WITHOUT_UID / UI_ITEM_NO_URL / MULTI_STEP_NOT_SCOPED / REVIEW_UI_BACKEND_ROUNDTRIP / INTERNAL_JARGON_LEAKAGE).
If findings exist:
tasks.md per hook remediation guidance; re-run hook; loop until clean@followup[TD-NNN] to each hit item + opens consumer-side TD; main thread re-runs hook to confirm bypass markers recognized; proceedIf hook exits 0: Silent skip — proceed to Step 3.5.
Reference: vendor/snippets/manual-review-enforcement/patterns.json + rules/core/manual-review.data-readiness.md.
3.5. Discuss Items Walkthrough (Step 2.5 in the spec — runs after artifact + task completion checks, before delta sync preview)
Spec authority: openspec/specs/manual-review-item-kind/spec.md "Spectra-Archive Discuss Walkthrough"
Read openspec/changes/<change-name>/tasks.md ## 人工檢查 section. Identify every unchecked item where kind = "discuss" (either explicit [discuss] marker, OR derived via Default Kind Derivation Rule when proposal.md contains **No user-facing journey (backend-only)**).
For each unchecked [discuss] item, the main thread Claude SHALL (proactively, without prompting):
Read the item description and surrounding context (proposal.md User Journeys, related task results, recent diff).
Classify the item's trigger condition (key for next step):
<title>, prod evlog row, prod migration \d output) and collect evidence.Prepare evidence relevant to the item — pick whichever combination is most informative:
grep / rg results showing the relevant code paths or migrations touchedgit diff excerpts (focused on the area the item references)For "External signal pending" items: skip evidence collection (there's nothing to collect yet). Move directly to step 4 with the trigger condition stated explicitly.
Present to the user, in this format:
### Discuss item #<id> [discuss] <description>
**Trigger condition:** <internal evidence | external signal already occurred | external signal pending — describe>
**Evidence:** (omit this section if trigger is "external signal pending")
<grep / diff / command output / summary>
**My read:** <one or two sentences explaining what the evidence implies, OR "waiting on <signal>; no evidence available yet — recommend Defer to HANDOFF so archive can proceed">
請確認:OK / Issue / Skip[ / Defer]
The Defer option is shown only when trigger is "external signal pending". For the other two trigger classes, only OK / Issue / Skip are valid — Claude has evidence available and there is nothing legitimate to wait on.
Wait for the user's response. Branch on the answer:
tasks.md for this line:
[x](claude-discussed: <ISO-8601-timestamp>) annotation between description and any trailing markers (@followup[TD-NNN] / @no-screenshot), preserving canonical ordering. Use the current ISO-8601 UTC timestamp (new Date().toISOString()).- [ ] #2 [discuss] Confirm rollout @no-screenshot- [x] #2 [discuss] Confirm rollout (claude-discussed: 2026-05-10T14:23:00Z) @no-screenshottasks.md:
[ ](issue: <user note>) annotation between description and trailing markerstasks.md:
[x](skip) annotation (or (skip: <reason>) if the user gave a reason)tasks.md:
[x](deferred-to-handoff: <ISO-8601-timestamp>) (awaiting-signal: <signal-desc>) between description and trailing markers (canonical ordering: kind marker → annotations → @followup / @no-screenshot)- [x] #2 [discuss] Confirm rollout (deferred-to-handoff: 2026-05-22T03:14:00Z) (awaiting-signal: prod deploy) @no-screenshotMove to the next unchecked [discuss] item until all are processed.
HANDOFF write (only fires when at least one item took the Defer path in this archive run):
Resolve target path: $MAIN_WT_PATH/HANDOFF.md (use git rev-parse --path-format=absolute --git-common-dir to find the main worktree even from a linked worktree; same idiom as handoff skill Step 1.5).
Locate ## Deferred discuss items heading. If missing, append the heading + an empty body at the end of HANDOFF.md.
For each deferred item, append an entry block (preserving any pre-existing entries in deferred-at ascending order):
<!-- deferred-begin:<change-name>:<item-id> -->
- **<change-name>** #<item-id> — <one-line description copied from tasks.md, stripped of kind marker and annotations>
- Awaiting signal: <signal-desc same as awaiting-signal annotation>
- Resume: `/spectra-archive <change-name>`
- Deferred at: <ISO-8601-timestamp same as deferred-to-handoff annotation>
<!-- deferred-end:<change-name>:<item-id> -->
The HTML markers are load-bearing — Resume mode (Step 1 path resolution) uses them for sed-based entry removal. Do NOT drop or rename them.
Skip-condition: if ## 人工檢查 has no unchecked [discuss] items, skip this step silently.
Out of scope here: unchecked [review:ui] items are NOT processed in this step — they are routed to /review-screenshot by the orchestrator's Archive Flow (spectra/SKILL.md Step 1). If [review:ui] items remain unchecked at this point, the orchestrator should already have prompted the user; if they reach Step 6 unchecked, archive-gate.sh Check 4 will block.
Restrictions (hard rules from manual-review.md):
[discuss] item [x] without showing the user evidence first AND receiving an explicit OK / Skip / Defer(claude-discussed: <ISO>) annotation without an actual discussion taking place(deferred-to-handoff: <ISO>) annotation without also writing the matching HANDOFF entry in the same archive run[discuss] items in one user prompt — present them one at a time so the user can give a focused answer per item[review:ui] items during this step3.5b. Resume walkthrough (clade fork addition — only runs when Step 0.5 detected Resume mode; skip otherwise)
For each line in openspec/changes/archive/<change-name>/tasks.md containing (deferred-to-handoff::
Read item description + extract the awaiting-signal: annotation text.
Re-classify trigger condition. The originally-pending signal typically has occurred by now; collect post-signal evidence (prod URL <title>, prod evlog row, prod migration \d output, etc.). If the signal still has not occurred, that's a legitimate "still pending" outcome.
Present to user identical to Step 3.5 walkthrough format, but with header:
### Resume discuss item #<id> [discuss] <description>
**Originally deferred at:** <ISO from deferred-to-handoff annotation>
**Awaiting signal:** <signal-desc from awaiting-signal annotation>
**Trigger condition now:** <internal evidence | external signal already occurred | external signal still pending>
**Evidence:** (omit if signal still pending)
<grep / diff / command output / summary>
**My read:** <one or two sentences>
請確認:OK / Issue / Skip / Still pending
"Defer" is NOT offered in Resume mode — that would re-defer the same item indefinitely. The user picks a terminal outcome (OK / Issue / Skip) or signals the item still needs more time (Still pending).
Branch on user response, editing openspec/changes/archive/<change-name>/tasks.md:
(deferred-to-handoff: ...) and (awaiting-signal: ...) annotations from the line. Insert (claude-discussed: <new-ISO>) between description and trailing markers. Keep checkbox [x].(deferred-to-handoff: ...) and (awaiting-signal: ...). Change checkbox [x] → [ ]. Append (issue: <user note>) between description and trailing markers.(deferred-to-handoff: ...) and (awaiting-signal: ...). Append (skip[: reason]). Keep checkbox [x].For each item resolved (OK / Issue / Skip) — i.e. NOT "Still pending" — remove the corresponding HANDOFF entry. Resolve $MAIN_WT_PATH first (same idiom as handoff skill Step 1.5):
GIT_COMMON_DIR="$(git rev-parse --path-format=absolute --git-common-dir 2>/dev/null)"
MAIN_WT_PATH="$(dirname "$GIT_COMMON_DIR")"
HANDOFF_FILE="$MAIN_WT_PATH/HANDOFF.md"
# Delete the block between deferred-begin / deferred-end markers for this change/item.
# Use sed; if markers not found (user manually edited), leave HANDOFF alone and report.
if grep -q "<!-- deferred-begin:<change-name>:<item-id> -->" "$HANDOFF_FILE"; then
sed -i.bak "/<!-- deferred-begin:<change-name>:<item-id> -->/,/<!-- deferred-end:<change-name>:<item-id> -->/d" "$HANDOFF_FILE"
rm "$HANDOFF_FILE.bak"
else
echo "warn: HANDOFF entry for <change-name>:<item-id> not found (markers missing); user should clean manually"
fi
After all deferred items processed:
## Deferred discuss items section body is now empty (no <!-- deferred-begin: markers remain anywhere under that heading), best-effort remove the heading too. If removal would risk corrupting surrounding markdown (heading is wedged between other sections), leave heading + empty body and tell user to clean manually.Resume walkthrough complete: X resolved (Y OK / Z Issue / W Skip) / V still pendingResume mode does NOT run any spectra CLI command. The archived change directory stays in place; only tasks.md (and HANDOFF.md entries) get edited. User stages + commits the resulting diff manually with a message like archive: <change-name>; resume — deferred items: X resolved, Y still pending.
Restrictions (Resume mode):
spectra archive CLI in Resume mode (change is already archived — archive flow is a no-op)(deferred-to-handoff: ...) annotations in Resume mode — Defer is forbidden here (would re-defer indefinitely)(deferred-to-handoff:) annotation, even in the same ## 人工檢查 section — Resume mode is scoped to deferred items onlyAssess delta spec sync state
Check for delta specs at openspec/changes/<name>/specs/. If none exist, proceed without sync prompt.
If delta specs exist:
openspec/specs/<capability>/spec.mdPrompt options:
If user chooses sync, use Task tool (agent_type: "general-purpose", prompt: "Use Skill tool to invoke spectra-sync-specs for change ''. Delta spec analysis: "). Proceed to archive regardless of choice.
Clean up tracking file
Delete .spectra/touched/<change-name>.json if it exists. This file contains implementation tracking data that is not needed after archiving.
rm -f .spectra/touched/<change-name>.json
If the file does not exist, silently continue.
5.5. Post-walkthrough gate re-check (clade fork addition — paired with --pre-skill PreToolUse hook)
Journey URL Touch (Check 1) bypass marker — if Check 1 reports Journey URL Touch 未通過 for a URL whose .vue was committed in main before this worktree was forked (typical: earlier propose iteration impl'd the UI, current worktree only carries verify evidence / tasks.md annotations / screenshot sweep), this is the expected atomic-landing edge case. Drop a bypass marker into the relevant tasks.md block:
<!-- journey-touch: intentional, reason: UI already committed in <commit-sha> (earlier propose iteration); this worktree only carries verify evidence -->
The gate honours the marker per-URL. Use only when commit history actually contains the touched UI (verify via git log -- <ui-path>); never use to silence a missing implementation.
The PreToolUse hook pre-archive-ux-gate.sh runs archive-gate.sh --pre-skill <name> before this skill starts, which skips Check 4 (Manual Review Kind Validation) because annotations are populated by Step 3.5 above. Now that Step 3.5 has run, validate the post-walkthrough state by re-running the gate without --pre-skill:
bash scripts/spectra-advanced/archive-gate.sh "<change-name>"
Branch on exit code:
pre-handoff-ledger.mjs record --layer E.1 ... 後重試,或對非適用 change 加 <!-- pre-handoff-verdict: intentional, reason: ... --> 到 tasks.md)。
request_user_input:
Skip condition: if the gate script does not exist (consumer pre-propagation state), warn and proceed (fail-open, matches existing Check 5 fail-open behavior).
Layer C — data-sanity audit(clade fork addition; pre-handoff quality gates; not in upstream spectra):archive-gate 過後、spec-sync 前,對本 change 跑 static data-shape audit,擋住「client query param literal 違反 server zod bound → silent 4xx → lookup map empty → admin list column 整列 fallback」這類 typecheck/lint/視覺都抓不到的資料形狀問題(perno app-status-badge-extraction root cause):
node <clade-vendor>/scripts/audit-data-sanity.mjs --consumer-path . --json
status: "pass" → 通過(advisory lookupRisks 印 stderr 供參考,不 block);進 sidecar advance。status: "fail"(PARAM_BOUNDARY,Critical)→ MUST block archive:顯示 violations 給 user,root-cause 修 client literal 到 bound 內(典型 perPage: 200→100)或調 server schema 後 re-run。NEVER silently bypass、NEVER 標 archive done 留給 user manual review 抓。audit-data-sanity.mjs 不存在(consumer pre-propagation)→ warn + 跳過(fail-open,與 Check 5 一致)。/data-sanity skill。Sidecar advance (TD-155) — once gates pass (exit 0 or user explicitly bypassed), advance phase before entering spec-sync / archive CLI:
node scripts/spectra-archive-sidecar.mjs update <change-name> --phase spec-sync --last-completed gate-check
(silent fail-safe if helper / sidecar missing.)
Perform the archive
Use the spectra archive CLI command which handles the full archive workflow
(spec snapshot, delta application, @trace injection, identity recording, vector indexing):
spectra archive <name>
Optional flags:
--skip-specs — skip delta spec application (for tooling/doc-only changes)--mark-tasks-complete — mark all incomplete tasks as complete before archiving--no-validate — skip delta spec validationIf archive fails with "already exists" error, suggest renaming existing archive.
Sidecar advance (TD-155) — only after spectra archive exits 0, advance phase:
node scripts/spectra-archive-sidecar.mjs update <change-name> --phase folder-mv --last-completed spec-sync
NEVER advance the sidecar to folder-mv if the CLI failed or was interrupted — leaving phase=spec-sync is the trigger that lets Step 0.5 detect the dangerous mid-CLI state and force manual fixup on next --resume. (silent fail-safe if helper / sidecar missing.)
Sweep screenshots (auto)
After successful archive, automatically invoke the screenshots-archive skill (via Skill tool) with change <change-name> to sweep the corresponding screenshot folders into screenshots/<env>/_archive/YYYY-MM/.
_archive/ (no extra confirmation here; screenshots-archive Mode B handles topic-name mismatch internally).--no-sweep (or said "不要 sweep 截圖") when invoking spectra-archive, skip this step and note in Step 8 summary: Screenshots: sweep skipped (user --no-sweep).screenshots-archive errors (e.g., disk write failure), do NOT fail the overall archive — log the error and note in Step 8 summary: Screenshots: sweep failed — see error above. The change is already archived; sweep is best-effort cleanup.Note (TD-160 preserve): when Step 0 ran wt-helper merge-back, gitignored worktree screenshots at screenshots/<env>/<topic>/ are automatically copied to main before cleanup destroys the worktree (see preserveWorktreeScreenshots in vendor/scripts/wt-helper.mjs). Step 7 sweep therefore finds the files in main as expected. Without this preserve, cleanup would delete them and sweep would be a no-op.
Sidecar advance (TD-155) — after sweep completes (success, skipped, or failed — all three count as "Step 7 phase reached"):
node scripts/spectra-archive-sidecar.mjs update <change-name> --phase screenshot-sweep --last-completed folder-mv
(silent fail-safe if helper / sidecar missing.)
7.5. Auto-reconcile merge-back stash (clade fork; auto-善後 — no tail)
merge-back (--auto-stash, Step 0) sets aside main's pre-merge dirty as wt-merge-block/<change>/... so the squash can land cleanly. This is main's OWN work, NOT stale change residue — typically HANDOFF entries from parallel sessions, ROADMAP/spec regen, telemetry appends. It MUST be restored to main's working tree (so it lands with the user's eventual /commit); the archive auto-善後 it here and NEVER leaves a stash tail for the user (the only exception is a genuine per-file divergence, which is surfaced, not buried).
NEVER blanket-drop a
wt-merge-blockstash. Proven incident (2026-06-11): awt-merge-blockstash held a parallel session'sreceiving-material-mapHANDOFF entry (blocked-status handoff for another in-flight change); the old "drop archived-slug" guidance would have destroyed it.wt-merge-block= main's blockers, not change residue. (stash-reconcile.mjsitself already recommendsapplyfor clean merge-block entries — the old request_user_input+keep default was the bug, not the script.)
node scripts/stash-reconcile.mjs --slug "<change-name>" --json
Parse JSON entries. If empty → Reconcile: 0 entries, proceed. For each entry, branch on namespace.kind (main thread runs the git ops directly — no request_user_input in the common path):
wt-merge-block → auto-restore per-file:
REF="<entry.ref e.g. stash@{0}>"
# archive already produced the authoritative version of these → keep current, do NOT restore the stale stash copy
REGEN_RE='^(openspec/ROADMAP\.md|openspec/specs/)'
surfaced=()
for f in $(git stash show --include-untracked --name-only "$REF"); do
echo "$f" | grep -qE "$REGEN_RE" && continue # keep current post-archive version
# restore only when main has NOT independently changed f since the stash
# (current working file === stash base → no divergence → safe to restore stash content)
if diff -q <(git show "$REF^1:$f" 2>/dev/null) "$f" >/dev/null 2>&1; then
git checkout "$REF" -- "$f" # restore main's set-aside work
else
surfaced+=("$f") # diverged since stash — do NOT auto-overwrite
fi
done
if [ ${#surfaced[@]} -eq 0 ]; then
git stash drop "$REF" # fully reconciled — no tail
fi # else: keep stash, report surfaced[] for manual handling (rare)
Report Reconcile: restored N file(s) from merge-block, dropped stash (or, if surfaced) Reconcile: M file(s) diverged — stash kept, see <list>.
wt-baseline / wt-final-baseline → pre-fork pin, stale post-archive (content pinned as refs/wt-baseline/...): git stash drop <entry.ref>. Report Reconcile: dropped baseline pin.
other / unknown kind → do NOT auto-touch; print ref + file list, note Reconcile: 1 unknown-shape entry kept (manual).
Restrictions:
wt-merge-block stash without first restoring its non-regenerated files — that loses main's real work (e.g. parallel-session HANDOFF entries).surfaced[]) — surface it instead; this is the only path that leaves a tail, and it is warranted (a genuine conflict needs human eyes).openspec/ROADMAP.md, openspec/specs/**) from the stash — the archive's freshly-applied version is authoritative; the stash holds the pre-archive snapshot, which would revert the archive.wt-merge-block entry — that is the old tail-leaving behavior this step replaces.--no-reconcile → note Reconcile: skipped (user --no-reconcile).Reconcile: failed — see error above + leave the stash intact for the user.Reconcile: restored N / dropped / Reconcile: M diverged — stash kept / Reconcile: dropped baseline pin / Reconcile: 0 entries / Reconcile: skipped / Reconcile: failed.Sidecar advance (TD-155) — after stash reconcile completes (any outcome), advance to final phase:
node scripts/spectra-archive-sidecar.mjs update <change-name> --phase cleanup --last-completed screenshot-sweep
(silent fail-safe if helper / sidecar missing.)
7.7. Notion ticket status — ensure 進行中 + surface pending 驗收中(clade fork addition — per [[spectra-notion-coupling]])
Skip-condition(任一成立即 silent skip):consumer-meta notion.ticketWorkflow !== true;或本 change 的 proposal.md(已搬到 openspec/changes/archive/<date>-<name>/proposal.md)頂部無 > **Notion ticket**: 連結。不適用時不要硬湊 ticket。
兩條件都成立時,MUST:
page_id(proposal.md 頂部)+ consumer-meta notion.dataSourceId。notion-fetch collection://<dataSourceId> 重撈 schema 校對 property key(中文 + 全形空格 + >=,憑記憶必錯)。未開始 / 需確認 → 補轉 → 進行中(per ~/.agents/skills/_notion-tdms-board/REFERENCE.md §3)。/commit」,tag 在 /commit Step 5 才生)→ NEVER 在此推 驗收中;改在 Step 8 summary 明文列 pending 動作(見下),由緊接的 /commit 發版後同一主線完成 進行中 → 驗收中 + 填 修復版本 >= <tag>。NEVER 在 archive 當下標 驗收中(無 tag)或填 修復版本 >=。NEVER 碰客戶側轉移(驗收中→完成 等)或 發布日期 / 驗收日期 / 名稱 / 驗收完成 欄位。
Display summary
Show archive completion summary including:
notion.ticketWorkflow + change 有連結):一行明文 📌 Notion ticket <page_id>:已 ensure 進行中;待 /commit 發版產 git tag 後 → 轉 驗收中 + 填 修復版本 >= <tag>(同一主線跑完 /commit 立即執行,NEVER 留到下次想起來)(per [[spectra-notion-coupling]])Sidecar cleanup (TD-155) — after the summary is displayed (archive considered complete from the user's perspective), delete the sidecar:
node scripts/spectra-archive-sidecar.mjs delete <change-name>
(silent fail-safe: if sidecar is already missing — e.g., consumer pre-propagate — the helper is a no-op.)
Output On Success
## Archive Complete
**Change:** <change-name>
**Schema:** <schema-name>
**Worktree:** ✓ Absorbed into main (or: no worktree — solo path)
**Archived to:** openspec/changes/archive/YYYY-MM-DD-<name>/
**Specs:** ✓ Synced to main specs
**Screenshots:** ✓ Swept to _archive/YYYY-MM/ (or: no screenshots / skipped (user --no-sweep) / sweep failed)
All artifacts complete. All tasks complete.
If Step 0 stashed blockers, append a line under Worktree:
**Stash to reconcile:** wt-merge-block/<change-name>/<ISO> (run `node scripts/stash-reconcile.mjs` to plan)
Output On Success (No Delta Specs)
## Archive Complete
**Change:** <change-name>
**Schema:** <schema-name>
**Archived to:** openspec/changes/archive/YYYY-MM-DD-<name>/
**Specs:** No delta specs
**Screenshots:** ✓ Swept to _archive/YYYY-MM/ (or: no screenshots / skipped / failed)
All artifacts complete. All tasks complete.
Output On Success With Warnings
## Archive Complete (with warnings)
**Change:** <change-name>
**Schema:** <schema-name>
**Archived to:** openspec/changes/archive/YYYY-MM-DD-<name>/
**Specs:** Sync skipped (user chose to skip)
**Screenshots:** Sweep failed — see error above
**Warnings:**
- Archived with 2 incomplete artifacts
- Archived with 3 incomplete tasks
- Delta spec sync was skipped (user chose to skip)
- Screenshot sweep failed (archive itself succeeded)
Review the archive if this was not intentional.
Output On Error (Archive Exists)
## Archive Failed
**Change:** <change-name>
**Target:** openspec/changes/archive/YYYY-MM-DD-<name>/
Target archive directory already exists.
**Options:**
1. Rename the existing archive
2. Delete the existing archive if it's a duplicate
3. Wait until a different date to archive
Guardrails
spectra-sync-specs (agent-driven)--no-sweep); sweep failure must surface in summary, but NEVER roll back the successful archive on sweep failurescreenshots-archive via Skill tool with explicit change <change-name> argument so Mode B logic (caller-trusted, internal topic-mismatch prompt) kicks in