Community编程与开发github.com

marcioaltoe/archive-spec

归档一个已完成的规范——验证所有任务完成、QA通过且索引引用自包含,然后将 <slug>.md 写入解析后的归档根目录,并移除Spec文件夹,该文件夹会保留在Git中记录在source_revision下。

archive-spec 是什么?

该技能用于归档一个已完成的Spec(规范)。它会自动验证所有前置条件:每个任务都已完成、QA测试已通过(除非是QA覆盖覆盖),并且引用的Spec是自包含的。然后,它会生成一个归档记录(Archive Record),其中包含修订历史、QA结果和链接的引用,并将归档版本写入解析后的归档根目录(例如,对于内置的docs/specs根目录是docs/history/specs/,对于其他根目录是<spec-root>/_archived/)。同时,Spec文件夹会被移除,但其内容会保留在Git中,以便在记录source_revision处可追溯。该技能支持正常归档(所有任务完成且QA通过)、QA归档覆盖(明确例外)、取消(cancelled)和无需QA归档(当Task Graph声明qa: declined时)。它还具有引用重链接(Archive Relink)功能,可以将非核心文件自动提升到docs/references/目录,并相应地重写引用。该技能会检查架构前提条件,例如任务完成状态、结构化QA报告验证和引用自包含性,确保只有验证过的Spec才能归档。它支持Roundfix框架,通常会在QA通过后自动运行,也可以手动触发。

兼容平台~Claude Code~Codex CLI~Cursor
npx skills add https://github.com/marcioaltoe/roundfix/tree/HEAD/skills/archive-spec

Installed? Explore more 编程与开发 skills: steipete/bluebubbles, steipete/eightctl, steipete/blucli · View all 6 →

在你喜欢的 AI 中提问

打开一个已预加载此 Agent Skill 的新对话。

文档

归档 Spec

归档一个已完成的 Spec,写入 <slug>.md 到解析后的归档根目录,并移除 <resolved-spec-root>/<slug>/,该目录会保留在 Git 中记录在 source_revision 下。源路径是配置的 Spec Root;目标路径是解析后的归档根目录——对于内置的 docs/specs 根目录是 docs/history/specs/,对于外部或非默认根目录是 <spec-root>/_archived/,与 roundfix archive 的目标一致。正常归档意味着 已实现、已验证且自包含——所有任务完成,当 Task Graph 设有 QA 关卡时 QA 通过,并且每个索引引用都由 Spec 拥有。QA 归档覆盖记录明确的例外而不声称已验证。在任一处置后,ls <spec-root>/ 将正在进行的工作与归档分开,归档作为已构建内容及其原因的记录,保持可 grep 搜索。

触发条件是 Spec 完成,而非发布:在 Roundfix Spec Run 结束时,一旦 QA 关卡通过,自动运行此归档;或者当用户要求时运行。合并和发布是单独的、由用户驱动的步骤——归档提交只是随分支一起传播,并在该功能自己的 PR 中发布。

归档重链接

在写入归档记录之前,归档会执行归档重链接。它会自动将实时文档引用的每个非核心文件从所有者 Spec 提升到 docs/references/<basename>,然后将每个实时文档的引用重写为指向提升后的副本;对 Spec 文件夹或核心工件的引用将指向归档记录。该记录在 relinked 下列出已重写的文件(如果有)。源文件只有一个所有者,因此次要 Spec 不需要副本(ADR-0260,ADR-0083)。

Spec 文件夹通过删除而退役。在复制的输出和每个提升的副本中,移除文件夹内的目标指定一个提升的副本,或者成为一个 Git 定位器 `<path>@<commit>`;它永远不会指向记录本身。提交保存路径并可以从默认分支或标签到达。当不存在这样的提交时,定位器指定路径和记录的拉取请求。其他目标保留其仓库目标(ADR-0278)。

当提升目标已存在、两个提升共享一个基本名称、或者要重写的实时文档在 HEAD 处有未提交的更改时,归档拒绝写入。不是实时文档的文件中的引用,或重写后剩余的引用,会发出警告,指明文件、行和目标;归档保持这些文件不变。

Squash 合并

Squash 合并仅在拉取请求的 head 引用中保留 source_revision;记录在 pull_request 中指定该拉取请求。

声明的 QA 拒绝

声明 qa: declined 且带有非空 qa_reason 的 Task Graph,在所有任务完成后即归档,无需 QA 报告且无需覆盖,其归档记录的处置为 no-qa。对于这样的 Spec,拒绝使用 --qa-override。所有其他归档前置条件仍然适用。

QA 结算

相同的结果结算了已创建的 qa Task,并确定归档留下什么:

结果结算归档
pass将 QA Task 结算为 completed,并且在报告没有不允许的阻塞行时使 Spec 符合归档条件。归档记录,指定 QA 报告和裁决;Spec、其 QA 报告和证据保留在 Git 中记录在 source_revision 下。
符合条件的声明 partial当没有行失败、被跳过或因发现受阻,每个声明阻塞的行都被匹配的 ## Unreachable Acceptance 声明覆盖,并且每个环境阻塞的行是 PR 前的 Pull Request 行,记录为 blocked (environment: no open Pull Request),并在其来源中指定 Pull Request 行,或者是 Run 沙箱无法访问的外部证据行,记录为 blocked (environment: network denied: <host>),并在其来源中指定外部证据行时,将 QA Task 结算为 completed。这两行都不需要 Unreachable Acceptance 声明,并且仅由这些行不满足的 partial 符合条件。归档记录,指定 QA 报告和裁决,并将声明的 satisfied-by 记录作为 unproven 携带;Spec、其 QA 报告和证据保留在 Git 中记录在 source_revision 下。
environment-blocked使该行保持阻塞;当等效证据满足环境策略时,报告仍可以结算为 pass。本身不归档任何内容;符合条件的报告可以归档 Spec。
failed使 QA Task 保持未解决,并且除非有授权的覆盖,否则拒绝归档。不归档任何内容。
missing使 QA Task 保持未解决,并且除非有授权的覆盖,否则拒绝归档。不归档任何内容。
override不更改 QA Task 状态或报告裁决;在 QA 失败或缺失的情况下,将归档结算为明确授权。归档记录包含 qa_override、qa_override_approval、qa_override_reason、qa_override_qa_outcome、当 QA Task 未完成时的 qa_override_qa_task_status、以及 qa_override_revision;QA Task 和报告在 Git 中记录在 source_revision 下保持不变。

Spec 授权保持离线的行

上述符合条件的声明 partial 也接受外部证据行,由于 Spec 自身的授权禁止查找,该行未被查找,记录为 blocked (environment: <subject> prohibited by Spec authorization),并在其来源中指定外部证据行。subject 命名授权禁止的内容,使用其措辞:network 用于网络查找,或 another repository;长度为 1 到 80 个字符,不包含括号。与网络拒绝行一样,它永远不会决定符合条件的 partial,也不需要 Unreachable Acceptance 声明,一行最多计入一种豁免类型,并且结算、roundfix qa-report accept 和 roundfix archive 通过相同的策略读取它(ADR-0276,ADR-0335)。

取消 Spec

不会交付的 Spec 使用 roundfix cancel <slug> --reason <text> 取消,切勿手动删除。该命令写入一个处置为 cancelled 的归档记录,将每个采用的 Backlog Entry 返回到 docs/backlog/ 并设置 status: open,将每个采用的 Finding 返回到 docs/findings/ 并设置 status: pending,然后移除 Spec 文件夹,同时其字节保留在 Git 中的 source_revision 下。已取消的记录永远不会满足前提条件,永远不会取代另一个 Spec,并且 Delivery Retry 会拒绝它。

忽略的文件和失败的删除

当 Git 忽略 Spec 文件夹下的任何文件时,Archive 命令和 Cancel 命令拒绝执行。每个命令列出每个被忽略的文件并且不写入任何内容。操作员必须删除每个文件或使用 git add -f 提交它,然后重试。

如果 Spec 文件夹的最终删除失败,两个命令都保留每次写入,退出 1,并打印完成删除的命令: chmod -R u+w '<folder>' && rm -rf '<folder>'。

前置条件——验证,不要轻信

在接触任何内容之前,使用新鲜的命令证据检查所有三个条件:

  1. 所需任务完成。 对于正常归档,读取 _tasks.md 中列出的每个 task_NN.md;每个 status 必须为 completed。对于 QA 归档覆盖,每个非 QA 任务必须为 completed;QA 任务可以保持 pending 或 failed。如果 QA 任务也是 completed,则最新的报告必须仍然不符合条件,否则覆盖是不必要的且会被拒绝。

    命令: 运行 grep -n '^status:' <each-task-file-listed-in-_tasks.md> 并保留其输出。任何 status: completed 以外的值都会阻止正常归档。在覆盖期间,只有具有其他状态的非 QA 任务才会阻止归档;指定该任务文件。

  2. QA 通过。 qa/ 中最新的报告必须通过仓库的 QA 验证器。不要用行 grep 替代结构化验证。在 Roundfix 中,使用与 Archive 命令相同的 internal/spec.QAVerdict 契约:选择符合 qa-report-YYYY-MM-DD[-NN].md 文件名契约的最新报告,解析其 YAML 前置元数据,要求支持 verdict,要求两个阻塞行字段在存在时是非负整数,并在 rows_blocked_finding 非零时拒绝 verdict: pass。保留验证器的报告路径和结果作为证据。缺失的 qa/ 目录、格式错误的最新报告或非通过的裁决会阻止正常归档。仅当通过 roundfix archive <slug> --qa-override --approval <source> --reason <text> 执行明确的 QA 归档覆盖时,才继续进行。切勿通过编辑归档记录来记录覆盖。符合条件的最新报告不会使失败或待处理的 QA 任务完成;仅当报告符合条件且所有任务都完成时,才会拒绝覆盖。

  3. 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 必须等于其四位数字前缀,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"
      # ... [为简洁而截断]
    

Individual skills in this repo

This repo contains 4 individual skills — each has its own dedicated page.

marcioaltoe/roundfix

Use Roundfix to plan releases with the read-only Release Plan Command, clean CodeRabbit pull request feedback, diagnose runtime readiness with the Doctor Command, execute a Spec's Task Graph with the Implement Command, monitor Runs through the Supervisor Run Event Stream, reclaim Run storage with the GC Command, archive completed Specs, run its `--follow-up`, `--status` and `--unattended` Skill Modes, and, inside daemon-assigned Batch runs, follow the bounded Review Issue or Task resolution contract.

marcioaltoe/write-prd

Write a PRD as a durable local artifact at docs/specs/<slug>/_prd.md — research first, clarify with one multiple-choice question at a time, record decisions as ADRs, then write directly without draft-approval rounds.

marcioaltoe/write-tasks

Decompose a spec's PRD/TechSpec into a dependency-ordered task graph — vertical-slice task files plus a machine-parseable _tasks.md DAG manifest under docs/specs/<slug>/, including the authored QA gate decision, decided autonomously from the Spec and escalated only when authority, architecture, or blast radius is at stake.

marcioaltoe/write-techspec

Write the technical spec at docs/specs/<slug>/_techspec.md — explore the architecture, settle technical decisions one question at a time, record them as ADRs, and produce a build order that write-tasks can decompose. Entry point for feature work with an approved PRD, and also for refactors and bug fixes, where it mints the spec folder with a minimal _prd.md instead of requiring a product interview.

相关技能