archive-spec について
archive-specは、Roundfixの仕様・QAプロセスを使うプロジェクトで、完了またはキャンセルした仕様をアーカイブするエージェントスキルです。前提条件を検証し、参照を整理して、元のリビジョン情報を含む記録を残します。
主な機能
- 01完了した仕様のアーカイブ
- 02承認済みQA例外でのアーカイブ
- 03アーカイブ前のタスクとQA状態の検証
- 04参照の付け替えと関連ファイルの移動
- 05仕様のキャンセルと関連項目の復元
活用シーン
- 01QA通過後に実装済み仕様をアーカイブ
- 02承認済みのQA例外を記録してアーカイブ
- 03終了する仕様フォルダーから参照ファイルを移動
- 04仕様をキャンセルして項目をバックログへ戻す
インストール
コマンドをコピーしてターミナルで実行します。
npx skills add https:/ / github.com/ marcioaltoe/ skills --skill archive-specインストール後、Claude Code で/archive-specを入力して読み込まれたか確認します。
詳細
アーカイブ仕様
完了した仕様をアーカイブするには、解決済みアーカイブルートに <slug>.md を書き込み、<resolved-spec-root>/<slug>/ を削除します。このディレクトリは、記録の source_revision にある Git に残ります。ソースは設定された Spec Root です。宛先はその解決済みアーカイブルートで、組み込みの docs/specs ルートでは docs/history/specs/、外部またはデフォルト以外のルートでは <spec-root>/_archived/ となり、roundfix archive の宛先と一致します。通常のアーカイブは、_実装済み、検証済み、自己完結済み_であることを意味します。すべてのタスクが完了し、QA に合格し、インデックスに登録されたすべての参照を Spec が所有している状態です。QA Archive Override は、検証済みと主張することなく、明示的な例外を記録します。どちらの処置の後も、ls <spec-root>/ を一度実行すれば、進行中の作業と履歴を区別でき、アーカイブは何が、なぜ構築されたかの記録として grep できます。
トリガーは公開ではなく仕様の完了です。QA ゲートに合格したら implement-spec ループの最後に自動で実行するか、ユーザーから依頼されたときに実行します。マージとリリースは別途ユーザーが判断する手順です。アーカイブのコミットはブランチに含まれ、機能自身の PR に同梱されます。
アーカイブの参照先変更
アーカイブレコードを書き込む前に、アーカイブではアーカイブの参照先変更を行います。Live Document が所有元の Spec を参照している場合、参照されている非コアファイルをそれぞれ docs/references/<basename> に自動で昇格し、その後、各 Live Document の引用を昇格先のコピーに書き換えます。Spec フォルダーまたはコア成果物への引用は、アーカイブレコードを指します。書き換えたファイルがある場合、レコードの relinked にその一覧を記載します。ソースの所有者は 1 つのままなので、2 つ目の Spec にコピーは必要ありません(ADR-0260、ADR-0083)。
Spec フォルダーは削除してアーカイブします。コピーされた成果物と昇格された各コピーにおいて、削除されるフォルダー内を指す宛先は昇格されたコピーを指すか、Git ロケーター `<path>@<commit>` になります。アーカイブレコード自体を指すことはありません。そのコミットにはパスが保持されており、デフォルトブランチまたはタグから到達できます。そのようなコミットがない場合、ロケーターはパスとレコードのプルリクエストを指します。その他の宛先は、リポジトリ内の対象をそのまま保持します(ADR-0278)。
昇格先がすでに存在する場合、2 つの昇格先でベース名が重複する場合、または書き換え対象の Live Document に HEAD でコミットされていない変更がある場合、アーカイブは書き込みを拒否します。Live Document 以外のファイルにある引用や、書き換え後も残る引用は、ファイル、行、対象を示す警告になります。アーカイブはこれらのファイルを変更しません。
スカッシュマージ
スカッシュマージでは、source_revision はプルリクエストのヘッド参照にのみ残ります。レコードでは、そのプルリクエストを pull_request に記載します。
QA の完了処理
同じ結果によって、作成された qa Task が完了処理され、アーカイブに残る内容が決まります。
| 結果 | 完了処理 | アーカイブするもの |
|---|---|---|
pass | QA Task を completed として完了処理し、レポートに許可されないブロック行がなければ Spec をアーカイブ可能にします。 | QA Report と判定結果を記載した Archive Record。Spec、QA レポートおよび証拠は、レコードの source_revision において Git に残ります。 |
条件を満たす宣言済みの partial | いずれの行も失敗またはスキップされておらず、finding によるブロックもなく、宣言されたすべてのブロック行が対応する ## Unreachable Acceptance 宣言によってカバーされ、さらに環境によるブロック行が、事前 PR の Pull Request 行であり、プロベナンスに Pull Request 行が記載された blocked (environment: no open Pull Request) として記録されているか、Run サンドボックスが到達できなかった外部証拠の行であり、プロベナンスに外部証拠の行が記載された blocked (environment: network denied: <host>) として記録されている場合、QA Task を completed として完了処理します。どちらの行にも Unreachable Acceptance 宣言は必要なく、未達成の行がこれらの行だけである partial も条件を満たします。 | QA Report と判定結果を記載し、宣言の satisfied-by レコードを unproven として保持する Archive Record。Spec、QA レポートおよび証拠は、レコードの source_revision において Git に残ります。 |
environment-blocked | 行をブロック状態のままにします。同等の証拠が環境ポリシーを満たしていれば、レポートは pass として完了処理できます。 | これだけでは何もアーカイブしません。条件を満たすレポートがあれば Spec をアーカイブできます。 |
failed | QA Task を未解決のままにし、承認済みのオーバーライドが適用されない限り、アーカイブを拒否します。 | 何もアーカイブしません。 |
missing | QA Task を未解決のままにし、承認済みのオーバーライドが適用されない限り、アーカイブを拒否します。 | 何もアーカイブしません。 |
override | QA Task のステータスもレポートの判定結果も変更せず、QA が失敗または欠落していても、明示的に承認されたものとしてアーカイブを完了処理します。 | qa_override、qa_override_approval、qa_override_reason、QA Task が未完了の場合は qa_override_qa_outcome と qa_override_qa_task_status、および qa_override_revision を含む Archive Record。QA Task とレポートは変更されず、レコードの source_revision において Git に残ります。 |
Spec の承認によりオフラインに保たれる行
上記の条件を満たす宣言済みの partial は、Spec 自身の承認によって参照が禁止されているため、ゲートが参照しなかった外部証拠の行も受け入れます。この行は、プロベナンスに外部証拠の行を記載した blocked (environment: <subject> prohibited by Spec authorization) として記録されます。subject には、承認で禁止されている対象を承認の文言どおりに指定します。ネットワーク参照の場合は network、別のリポジトリの場合は another repository です。長さは 1~80 文字で、括弧は含められません。ネットワーク拒否の行と同様に、これが条件を満たす partial の判定を左右することはなく、Unreachable Acceptance 宣言も不要です。1 つの行が免除対象の種類を複数に数えられることはなく、完了処理、roundfix qa-report accept、roundfix archive はすべて同じポリシーに従ってこの行を扱います(ADR-0276、ADR-0335)。
Spec のキャンセル
納品しない Spec は roundfix cancel <slug> --reason <text> でキャンセルします。手動で削除してはいけません。このコマンドは disposition が cancelled の Archive Record を書き込み、採用された各 Backlog Entry を status: open として docs/backlog/ に戻し、採用された各 Finding を status: pending として docs/findings/ に戻した後、Spec フォルダーを削除します。バイト列は source_revision の Git に残ります。キャンセルされたレコードが前提条件を満たすことはなく、別の Spec に優先することもなく、Delivery Retry によって拒否されます。
無視されたファイルと削除の失敗
Git が Spec フォルダー内のファイルを無視している場合、Archive Command と Cancel Command は処理を拒否します。各コマンドは無視されたファイルをすべて一覧表示し、何も書き込みません。オペレーターは各ファイルを削除するか、git add -f でコミットしてから再試行する必要があります。
Spec フォルダーの最後の削除に失敗した場合、両コマンドはすべての書き込みを保持し、終了コード 1 で終了して、削除を完了するコマンドを表示します:
chmod -R u+w '<folder>' && rm -rf '<folder>'。
前提条件 — 信頼せず、検証する
変更を加える前に、最新のコマンド出力を使って以下の 3 項目すべてを確認してください:
-
必須タスクが完了していること。 通常のアーカイブでは、
_tasks.mdに記載された各task_NN.mdを読み、すべてのstatusがcompletedである必要があります。QA Archive Override では、QA Task 以外のすべてのタスクがcompletedである必要があります。QA Task はpendingまたはfailedのままでもかまいません。QA Task もcompletedの場合は、最新レポートが引き続き対象外でなければなりません。対象となる場合、オーバーライドは不要であり、拒否されます。コマンド:
grep -n '^status:' <each-task-file-listed-in-_tasks.md>を実行し、出力を保持してください。status: completed以外の値がある場合、通常のアーカイブはブロックされます。オーバーライド中は、別のステータスを持つ QA Task 以外のタスクのみがアーカイブをブロックします。そのタスクのファイル名を示してください。 -
QA に合格していること。
qa/内の最新レポートが、リポジトリの QA 検証ツールに合格する必要があります。構造化検証の代わりに行の grep を使ってはいけません。Roundfix では、Archive Command と同じinternal/spec.QAVerdictコントラクトを使用します。qa-report-YYYY-MM-DD[-NN].mdというファイル名の規則に基づいて最新レポートを選択し、その YAML フロントマターを解析して、サポート対象のverdictがあることを確認します。存在する場合、ブロックされた行数を示す両方のフィールドが非負の整数であることを必須とし、rows_blocked_findingがゼロ以外なのにverdict: passとなっているレポートは拒否します。証拠として、検証ツールのレポートパスと結果を保持してください。qa/ディレクトリがない場合、最新レポートが不正な場合、または合格でない verdict の場合、通常のアーカイブはブロックされます。roundfix archive <slug> --qa-override --approval <source> --reason <text>を通じて明示的な QA Archive Override を行う場合に限り、処理を続行してください。Archive Record を編集してオーバーライドを記録してはいけません。該当する最新レポートがあっても、失敗または保留中の QA Task が完了扱いになるわけではありません。レポートが条件を満たし、すべてのタスクが完了している場合に限り、オーバーライドは拒否されます。 -
Spec が自己完結していること。
docs/specs/<slug>/references/_index.mdが存在する場合、またはシンボリックリンクである場合は、この前提条件を適用してください。切れているものを含め、シンボリックリンクは無効なインデックス状態であり、レガシー Spec として扱ってはいけません。このパスが存在せず、シンボリックリンクでもない Spec のみがこの契約より前のものであり、過去の成果物を後から整備せずに合格となります。インデックス付き Spec では、インデックス内のすべてのpathが_index.mdからの相対パスとして存在し、更新されていないすべてのsourceパスが存在せず、Spec 内の Markdown リンクのリンク先がdocs/_inbox/またはdocs/findings/内を指していないことが必要です。コマンド: まず、このリンク先チェックを実行してください。構文は、どちらかのツリーの名前に触れているだけの文章ではなく、インライン形式または参照形式の Markdown リンクに意図的に一致するようになっています:
spec_dir=docs/specs/<slug> link_hits=$(grep --include='*.md' -RInE '(\]\([^)]*(docs/)?(_inbox|findings)/[^)]*\)|^[[:space:]]*\[[^]]+\]:[[:space:]]*<?[^[:space:]>]*(docs/)?(_inbox|findings)/)' "$spec_dir") link_status=$? if test "$link_status" -eq 0; then printf '%s\n' "$link_hits" echo "self-containment failed: rewrite each listed link at adoption step 8" exit 1 fi test "$link_status" -eq 1 || exit "$link_status"次に、すべてのデータ行を解析して検証してください。インデックスは現在の Spec に属します。
ownerは 4 桁のプレフィックスと一致し、typeはinbox、finding、またはbacklogのいずれかであり、各sourceとpathはそれぞれ一度だけ登場する必要があります。pathは_index.mdからの相対パスである単一のベース名でなければなりません。絶対パス、.、..、パス区切り文字、およびシンボリックリンクは拒否し、references/の外へのトラバーサルやリンクを許可しないでください。リポジトリのルートから以下を実行し、正規化された行と診断があればその内容を証拠として保持してください:slug=<slug> index="docs/specs/$slug/references/_index.md" expected_owner=${slug%%-*} parsed_index=$(mktemp) || exit 1 trap 'rm -f "$parsed_index"' EXIT HUP INT TERM if test -L "$(dirname "$index")"; then printf 'self-containment failed: references/ must not be a symbolic link\n' >&2 exit 1 fi if test -L "$index" || test ! -f "$index"; then printf 'self-containment failed: references/_index.md must be a regular, non-symbolic-link file\n' >&2 exit 1 fi awk -F '|' -v expected_owner="$expected_owner" ' function trim(value) { gsub(/^[[:space:]]+|[[:space:]]+$/, "", value) return value } function reject(message) { print "self-containment failed: " message > "/dev/stderr" invalid = 1 } function separator(value) { value = trim(value) return value ~ /^:?-{3,}:?$/ } /^[[:space:]]*$/ || /^[[:space:]]*#/ { next } !header { if (NF != 7 || trim($2) != "source" || trim($3) != "type" || trim($4) != "owner" || trim($5) != "adopted date" || trim($6) != "path") { reject("_index.md must use the fixed source | type | owner | adopted date | path header") } else { header = 1 } next } !divider { if (NF != 7 || !separator($2) || !separator($3) || !separator($4) || !separator($5) || !separator($6)) { reject("_index.md has an invalid table separator") } else { divider = 1 } next } { source = trim($2) type = trim($3) owner = trim($4) adopted = trim($5) path = trim($6) if (NF != 7 || source == "" || type == "" || owner == "" || adopted == "" || path == "") { reject("invalid index row at line " NR ": " $0) next } if (type != "inbox" && type != "finding" && type != "backlog") { reject("type must be `inbox`, `finding`, or `backlog` at line " NR ": " type) } if (owner != expected_owner) { reject("owner must be " expected_owner " at line " NR ": " owner) } if (seen_source[source]++) { reject("duplicate source at line " NR ": " source) } if (seen_path[path]++) { reject("duplicate path at line " NR ": " path) } if (adopted !~ /^[0-9][0-9][0-9][0-9]-[0-9][0-9]-[0-9][0-9]$/) { reject("adopted date must be YYYY-MM-DD at line " NR ": " adopted) } if ((type == "inbox" && source !~ /^docs\/_inbox\/[^\/]+\.md$/) || (type == "finding" && source !~ /^docs\/findings\/[^\/]+\.md$/) || (type == "backlog" && source !~ /^docs\/backlog\/[^\/]+\.md$/)) { reject("source does not match type at line " NR ": " source) } print source "|" type "|" owner "|" adopted "|" path } END { if (!header || !divider) { reject("_index.md is missing its fixed table header") } if (invalid) { exit 1 } } ' "$index" > "$parsed_index" || exit $? while IFS='|' read -r source type owner adopted path; do case "$path" in ""|.|..|/*|*/*|*\\*) printf 'self-containment failed: path must be one basename relative to `_index.md`: %s\n' "$path" >&2 exit 1 ;; esac source_basename=${source##*/} if test "$path" != "$source_basename"; then printf 'self-containment failed: path must equal source basename for %s: %s != %s\n' "$source" "$path" "$source_basename" >&2 exit 1 fi current="$(dirname "$index")/$path" if test -L "$current" || test ! -f "$current"; then printf 'self-containment failed: invalid or missing path %s; finish adoption step 7\n' "$path" >&2 exit 1 fi if test -e "$source" || test -L "$source"; then printf 'self-containment failed: source still exists at %s; finish adoption step 7\n' "$source" >&2 exit 1 fi done < "$parsed_index" cat "$parsed_index"
roundfix archive <slug> --qa-override --approval <source> --reason <text> でのみ実行できる QA アーカイブのオーバーライドは、未完了の QA タスク、適格でない最新レポート、QA 証跡の欠落または読み取り不能という、通常の QA 前提条件の未達をオーバーライドします。すべてのタスクが完了していて、最新レポートが条件を満たしている場合は、その Spec を通常どおりアーカイブできるため、このコマンドは拒否されます。自己完結性がオーバーライドされることはありません。検証はメンテナーがオーバーライドできますが、自己完結性は成果物の特性であり、採用を完了して修正する必要があります。
マージ済み PR やリリースタグは前提条件ではなく、アーカイブがそれらを待つこともありません。
いずれかのチェックに失敗した場合は処理を中止し、問題のあるタスク、レポート、ソース、またはリンクと、自己完結性の失敗を修正する採用手順を報告してください。Spec はアクティブなままです。
QA の失敗、欠落、その他の不適格な状態にかかわらずアーカイブするには、明示的な承認と理由をアーカイブコマンドで指定します。
roundfix archive <slug> --qa-override --approval <source> --reason <text>
オーバーライドには、QA 以外のすべてのタスクが completed であることが引き続き必要です。最新レポートの判定にかかわらず、失敗または保留中の QA タスクを受け入れます。すべてのタスクが完了していて、最新レポートが条件を満たしている場合は、同じ Spec を通常どおりアーカイブできるため、拒否されます。QA タスクやレポートは変更せずに、承認元、理由、確認された QA 結果、アーカイブ時のリビジョンを記録します。QA タスクが完了していない場合は、qa_override_qa_task_status も記録します。読み取り不能なレポートの記録には、マシン上の絶対パスではなく、Spec フォルダーを基準としたパスを使用します。
手順
QA アーカイブのオーバーライドでは、roundfix archive <slug> --qa-override --approval <source> --reason <text> を通じてのみアーカイブを実行してください。通常のアーカイブでは、roundfix archive <slug> を実行します。このコマンドは前提条件を検証し、Archive Record を書き込み、Spec フォルダーを削除します。Archive Record を手動で編集しないでください。拒否、出所情報、アーカイブのライフサイクルはこのコマンドが管理します。Delivery Queue はコミット件名として docs: archive <slug>(Conventional Commits)を使用します。これが 100 文字を超える場合は、docs: archive the <spec number> Spec(Spec 番号のない slug では docs: archive the Spec)を使用します。依頼されない限り、プッシュしないでください。
新しいパスと、引き継がれるものを報告してください(タスクの ## Result セクションにある未完了のフォローアップは、アーカイブではなく新しい Spec に記載します)。作業がまだマージされていない場合は、github-pr-workflow を使って PR を作成することを提案してください。PR を作成するかどうかはユーザーが決定します。
Archive Record
アーカイブすると、解決済みのアーカイブルートの下に <slug>.md が書き込まれます。このレコードには QA Report と判定、その処置、削除された Spec フォルダーを復元可能な状態で保持している Git リビジョンである source_revision が記録されます。アーカイブする前に roundfix archive <slug> --plan を実行して、アーカイブの内容と、候補ファイルに関する Archive Advice を確認してください。繰り返し利用できる知識は --promote <path> で昇格させます。昇格すると、そのファイルがアーカイブ変更で docs/references/ にコピーされます。
アーカイブ解除
まれに行う、明示的な操作であり、アーカイブの取り消しです。レコードの source_revision にある Git から Spec フォルダーを復元し、レコードを削除します。通常、再開は新しい作業を意味します。アーカイブ済みの Spec を参照する新しい Spec を作成することを優先してください。
アンチパターン
- QA の失敗や欠落を黙ってアーカイブすること — オーバーライドはユーザーの明示的な指示とし、記録に残す必要があります。
- マージ済み PR やリリースタグをアーカイブの条件にすること — ゲートとなるのは完了(タスクと QA)です。公開は別の、ユーザー主導の手順です。
- 完了した Spec を「PR がマージされるまで」アクティブなセットに残すこと — アクティブフォルダーは進行中の作業専用です。
- アーカイブ済みの Spec を編集すること — これは記録です。新しい要件は、以前の Spec にリンクする新しい Spec に記載してください。
- アーカイブせずに削除すること — 「これ、すでに試したのでは?」への答えは、墓場にあります。