Accelint QRSPI Archive
SkillAI & modelsArchive an OpenSpec change end-to-end. This skill invokes openspec-archive-change or openspec-bulk-archive-change itself to perform the native merge, then immediately follows up with the cross-capability linking and running indices OpenSpec doesn't build on its own, linking every capability a change touched via a shared `related:` frontmatter list, keeping `openspec/specs/INDEX.md` current, and appending a row to `openspec/changes/archive/INDEX.md`. Use this skill whenever the user wants to archive a change, says "archive this change", "bulk archive these changes", "run openspec-archive-change", "run openspec-bulk-archive-change", "update the specs index", "cross-link the specs", or wants the archived-change changelog kept current. This skill is purely additive on the linking side, it never prunes a `related:` entry and never changes a change's `Status` column after the initial write; that pruning/synthesis work belongs to `accelint-archive-synthesis`.
Available today. Use it from your connected AI after setup.
No other account needed.
Add ahel to your AI once: Claude, ChatGPT, Cursor, Claude Code or Codex. Then ask it to use this.
Then ask your AI: use the Accelint QRSPI Archive skill
What this skill tells your AI
The instructions your AI receives, as published by gohypergiant/agent-skills in skills/accelint-qrspi-archive/SKILL.md and read by ahel’s review.
Archive an OpenSpec change and follow it with the cross-capability linking and index bookkeeping OpenSpec doesn't do on its own. This skill invokes openspec-archive-change or openspec-bulk-archive-change itself — it's the entry point for archiving a change, not a step that reacts after someone has already archived one — waits for the merge to fully resolve, and then links every pair of capabilities the change touched via a shared related: frontmatter list, keeps a single running index of all specs up to date, and appends to an append-only changelog of every archived change.
Cross-linking has to happen after the merge resolves, not before it, which is exactly why this skill runs the native command itself as its own first phase rather than treating "a merge happened" as some external event to watch for. A delta spec in openspec/changes/<slug>/specs/ is still provisional — openspec-bulk-archive-change may resolve conflicts across several changes in chronological order before a capability's spec reaches its final shape. Only the merged, archived spec is worth indexing; anything computed earlier would be linking against content that might still change underneath it.
What This Skill Does
Automates: the full archive operation for one or more OpenSpec changes in a single invocation — invoking openspec-archive-change or openspec-bulk-archive-change itself, then immediately following up with cross-capability linking and index maintenance.
Scope: everything from "archive this change" through updated indices. This skill calls the native command itself during archive and extraction; it does not wait for the merge to have happened some other way first.
Output: the change(s) archived via OpenSpec's own merge, plus updated related: frontmatter and a regenerated ## Related Specs section on every touched spec, an updated openspec/specs/INDEX.md (patched for the capabilities this batch touched, or built fresh project-wide the first time the file doesn't exist yet), and one appended row per archived change in openspec/changes/archive/INDEX.md.
Does NOT: implement the merge or conflict-resolution logic itself (that's OpenSpec's own, which this skill invokes via the native command rather than reimplementing), prune any related: entry, change a change's Status column after its initial write, reorder existing changelog rows, or shell out to the OpenSpec CLI to read local spec files (plain file reads are sufficient — see Explicitly Out of Scope).
Prerequisites
- OpenSpec CLI installed and initialized, with one or more changes ready to archive.
- Sub-agent support, for per-capability spec writes only — those always run as subagents, unconditionally, and this is a hard requirement for normal operation there, not an optional speedup for large batches (see Error Handling for the degraded fallback if unavailable). Archive and extraction never uses a subagent, regardless of whether sub-agent support exists — see the Archive and Extract section for why.
- Each change's
openspec/changes/<slug>/design.mdhas YAML frontmatter includingspecs_touched(a non-empty list of capability names) anddecisions(a list of{id, choice, rationale, alternatives}entries). - Every capability named in any
specs_touchedlist already hasopenspec/specs/<capability>/spec.mdwith a## Purposeor### Purposeheading in its body — this skill reads that heading rather than duplicating purpose text into frontmatter, and rewriting the correct behavior depends on that heading actually being there (verified in preflight checks, Task B).
If any of these are missing, report the gap and guide the user to resolve it before proceeding — do not silently substitute a guessed default for a missing field. This applies as-is to spec writing's sub-agent support and a touched spec's missing ## Purpose or ### Purpose heading. A change's missing specs_touched/decisions frontmatter is handled differently: preflight Task A derives a candidate from the change's own files and gets the author's explicit confirmation before writing it, rather than stopping outright — see Task A below for why a hard stop isn't actually necessary here, and why it still isn't a silent guess.
Workflow Overview
┌────────────────────────────────────────────────────────────────────────┐
│ Stage Action Output │
├────────────────────────────────────────────────────────────────────────┤
│ 0 Preflight Verify frontmatter + Purpose Go / no-go │
│ headings before touching anything │
│ 1 Archive+Extract Run openspec-archive-change or Change │
│ openspec-bulk-archive-change yourself, records │
│ in this context (never a subagent); │
│ stay with any internal sync branch │
│ until archive's own steps finish, │
│ then read back specs_touched + decisions │
│ 2 Validate Confirm archive records are Checked │
│ structurally complete records │
│ 3 Link Combine new co-touch pairs across New │
│ this batch's changes (no file I/O) partners │
│ 4 Write specs SUBAGENT (one per capability, Updated │
│ always, never inline): merges new specs │
│ partners with existing related:, │
│ sorts, writes frontmatter + body │
│ 5 Specs index Patch specs/INDEX.md for the INDEX.md │
│ capabilities this batch touched │
│ (full rebuild only if the index is │
│ missing) │
│ 6 Change log Append one row per archived change INDEX.md │
│ to changes/archive/INDEX.md (append-only) │
│ 7 Report Summarize what changed Summary │
└────────────────────────────────────────────────────────────────────────┘
Critical: for openspec-bulk-archive-change, validation through reporting run exactly ONCE, after every
merge in the batch has resolved — never once per intermediate merge. Running
early would compute pairs against a specs_touched set that hasn't finished
accumulating cross-change conflicts, and would patch INDEX.md against a
half-finished batch.
Spec writing always delegates to subagents, regardless of batch size — one
capability or forty. This isn't a parallelization optimization that only
kicks in for large batches; it's how this skill keeps raw spec.md contents
out of the parent's context on every run, the same pattern
accelint-qrspi-propose and accelint-qrspi-apply use.
Archive and extraction is the mirror image: it never delegates to a subagent, regardless of
batch size or whether sub-agent support is even available. openspec-archive-change and
openspec-bulk-archive-change are themselves agent-driven, multi-step skills — not a
single deterministic CLI call — and a subagent handed instructions to run openspec-archive-change
has no reliable way to resume that skill's own remaining steps once it
branches internally into something like a separate sync skill, and no way
to surface an interactive prompt back to the user if one comes up. Both of
those are failure modes this skill hit in practice, not hypothetical ones —
see the Archive and Extract section for the full account.
Implementation Steps
Execute these steps in order without stopping between them unless an error occurs:
Preflight Checks
Goal: confirm the archive operation's inputs are shaped correctly before touching any spec or index file. Task A is a narrow exception: once the author confirms a derived specs_touched/decisions candidate, it writes that back into design.md — that's filling in an input step 7 expects to already be there.
-
Determine scope: a single-change archive (openspec-archive-change with a change name) or a bulk archive (openspec-bulk-archive-change) spanning several pending changes.
-
Verification Task A — design.md frontmatter. For every change about to be archived, read
openspec/changes/<slug>/design.mdand confirm its frontmatter contains a non-emptyspecs_touchedlist and adecisionslist where each entry has at leastidandchoice:--- change: add-live-sync specs_touched: [sync/protocol, ui/status-indicator] decisions: - id: D1 choice: polling with 5s interval rationale: no infra budget for a message broker this quarter alternatives: [websocket push, long polling] ---If frontmatter is present and well-formed, proceed as-is — this is the expected case for any change that went through
accelint-qrspi-propose, which is wherespecs_touched/decisionsare supposed to get written at design time in the first place. If changes are consistently arriving here without this frontmatter, that's a signal to go fixaccelint-qrspi-propose(oraccelint-qrspi-apply) so it writes this block as part of its own normal workflow — that closes the gap at the source instead of leaning on the recovery path below run after run.If frontmatter is missing or malformed for a change, this skill still does not silently substitute a guessed value — the change's author has to make that call explicitly, not this skill. But a hard stop with no path forward isn't the only way to get that explicit confirmation, and most of the time the missing field is recoverable from material the change's own author already wrote:
- Derive a candidate. For
specs_touched, look at the change'sproposal.mdcapability declarations and the delta spec directories underopenspec/changes/<slug>/specs/. Fordecisions, look at any Decisions section inproposal.mdor decision prose already present indesign.md. - Present it for confirmation — don't write it yet. Show the derived candidate to the user and ask them to (a) confirm it as written, (b) edit it first, or (c) pause so they can fix
design.mdthemselves and re-invoke this skill later. Only once the user picks (a) or (b) does the candidate become the value this skill writes intodesign.md's frontmatter — at that point it's the same explicit author confirmation the well-formed case gets for free, just captured one step later than at propose time. - Stop outright, with no candidate offered, only when there's nothing in the change's own files to derive from — e.g. an empty delta specs directory and no capability declarations anywhere in
proposal.md. In that case, report exactly which change and which field is missing, the same as before.
This is evaluated per change in a bulk-archive batch — one change needing confirmation doesn't block preflight for the others.
- Derive a candidate. For
-
Verification Task B — Purpose heading convention. For every capability named across all
specs_touchedlists in this batch, check whetheropenspec/specs/<capability>/spec.mdcontains a heading that describes its purpose:Acceptable headings (check in this order, first match wins):
## Purposeor### Purpose## Overviewor### Overview
Treat Overview and Purpose as semantically equivalent — both describe what the capability does and why it exists, which is what index updates and spec writing need.
If none of these headings exist: Ask the user how to handle it:
- (a) Add a placeholder
## Purposeheading with text_Purpose not yet documented_for now - (b) Pause so they can add the heading themselves first
- (c) Read the spec content and generate a
## Purposeheading based on what the spec describes
Option (c) is usually best when the spec has meaningful content — the agent can synthesize a purpose statement from what's already documented. Option (b) is better for specs that are stubs or need domain expertise to describe accurately. Option (a) is a last resort when you need to unblock immediately but will need to come back and fix it later.
Note: This check applies only to capabilities that already have MAIN specs at
openspec/specs/<capability>/spec.md. Brand-new capabilities (per step 4) don't have MAIN specs yet, so skip this check for them — they'll get their Purpose heading when their spec is created during archive. -
For any capability in
specs_touchedthat has noopenspec/specs/<capability>/directory yet — a brand-new capability introduced by this change — note it separately. Step 19 will need to create itsspec.mdfrontmatter from scratch rather than editing an existing file, and step 20's Purpose column will need the user to supply a value manually since nothing exists yet to read. -
Report the preflight summary before proceeding: changes in scope, capabilities touched, and any Task A or Task B outcomes. If Task A ends in a stop for any change — the user chose to pause and fix
design.mdthemselves, or no candidate could be derived at all — do not proceed to step 6 for that change. A Task A candidate the user confirmed counts as passing, the same as frontmatter that was already well-formed. If Task B fails for some capability, that's fine to resolve later — steps 6-17 don't touch spec bodies, so only flag it as blocking once step 18 is about to reach that capability.
Archive and Extract (runs inline — never a subagent)
- Let OpenSpec do the actual merge, then read back the data steps 17 and 23 need — done directly in this context, not handed to a subagent.
This step never runs as a subagent, regardless of batch size and regardless of whether sub-agent support is available at all. That's a reversal of this skill's 1.0.0 behavior, made after running into two concrete failure modes in practice:
- openspec-archive-change and openspec-bulk-archive-change are agent-driven skills, not a single deterministic CLI call. They read project state, decide what needs syncing, and — when a sync is needed — hand off internally to a separate sync skill before returning to finish the rest of the archive workflow (merging delta specs, moving the change into
openspec/changes/archive/). A subagent given instructions to run openspec-archive-change has no reliable way to tell "I finished the sync skill this archive step referred me to" apart from "I finished the thing I was actually asked to do" — there's no caller to check back with mid-task. In practice this showed up exactly that way: the subagent ran the sync step, considered its job done, and returned control without ever reaching the merge. Running archive directly in this context means the same agent that issued the instruction is the one watching it branch into sync, so it can recognize the branch for what it is and carry on to archive's remaining steps once sync finishes — the same continuity a person would have running the command themselves. - A subagent can't surface an interactive prompt to the user. openspec-archive-change and openspec-bulk-archive-change may raise more than the routine sync y/n — openspec-bulk-archive-change in particular can prompt for confirmation before merging changes that touch overlapping specs (see step 2 below). A subagent that hits a prompt like that is stuck: it can't hand the question to the user and get a real answer, and guessing on the user's behalf is worse than not proceeding. Running archive inline means any such prompt lands in the same conversation the user is already in.
This does give something up: openspec-archive-change's own internal work — comparing delta specs against main specs, resolving bulk-archive's cross-change ordering — now happens directly in this context instead of being absorbed by an isolated subagent, so more of it enters context than the 1.0.0 design intended. That's an accepted cost of correctness over context economy, not an oversight; spec writing still isolates its own, typically larger, per-capability file content in a subagent exactly as before, so this cost is confined to the archive step's own scope. Do not work around this by shelling out to openspec-archive-change/openspec bulk-archive directly instead of the openspec-archive-change/openspec-bulk-archive-change skill — the skill is where OpenSpec's own delta-spec comparison and edge-case judgment actually live (bulk-archive's conflict resolution isn't reproducible with a bare CLI flag), and bypassing it back to a raw CLI call would throw away the same hybrid-agent judgment this skill exists to keep.
-
Determine scope: a single-change archive (openspec-archive-change with a change name) or a bulk archive (openspec-bulk-archive-change) spanning several pending changes.
-
Known interactive prompt — always sync. openspec-archive-change and openspec-bulk-archive-change will, more often than not, pause mid-run to ask whether to sync. Always answer yes, every time it comes up — this is a routine part of the archive operation completing, not a decision point that needs the user's input. This is the one interactive prompt you always answer yourself.
-
Run the appropriate skill invocation:
For single change:
Invoke the openspec-archive-change skill.
<change-name>
For bulk archive:
Invoke the openspec-bulk-archive-change skill.
-
If the run branches internally — most commonly by handing off to a separate sync skill partway through — that's normal, expected shape for this command, not a sign that anything has gone wrong or that the task is finished. Stay with it: once the branch completes, pick back up with archive's own remaining steps (merging delta specs into the main specs, moving the change into
openspec/changes/archive/) rather than treating the branch's completion as the end of this section. -
If any other interactive prompt comes up — most commonly
openspec-bulk-archive-changeasking for confirmation before merging changes that touch overlapping specs — that's a real question only the user can answer, unlike the routine sync prompt in step 8. Surface it to the user directly and wait for their answer before continuing. -
Wait until every merge in this operation has fully resolved. For a bulk archive, this means ALL changes in the batch, not just the first.
-
If ANY merge reports unresolved conflicts, STOP immediately. Do not attempt to resolve it, and do not proceed to step 14 for ANY change in this batch — a partial extraction is worse than none, since there's no way to tell a stalled batch from a clean one otherwise. Report the conflict verbatim to the user and stop. Do not proceed to validation — parsing anything out of a partially-merged batch would propagate garbage into every capability the batch touches.
-
For each change that archived successfully, read its
design.mdfrontmatter from its new archived path (e.g.openspec/changes/archive/2026-03-02-add-live-sync/design.md) and build one record:
{
change: "add-live-sync",
date: "2026-03-02", // from the archive folder's own
// YYYY-MM-DD prefix, NEVER from
// anything inside design.md
archivePath: "openspec/changes/archive/2026-03-02-add-live-sync/",
specsTouched: ["sync/protocol", "ui/status-indicator"],
decisions: [{ id: "D1", choice: "polling with 5s interval", ... }]
}
- Keep the list of records from step 14, grouped by change, for validation — plus an explicit note to yourself that no unresolved conflicts remain.
Output: one record per archived change (change, date, archivePath, specsTouched, decisions), held in this context and passed straight to the validation step.
Validate Extracted Records
Goal: confirm archive records are structurally sound before cross-link computation depends on them — a sanity check on what was extracted, not a re-verification of the source data (preflight Task A already confirmed the source design.md frontmatter was well-formed before archive ran).
-
Confirm every record has a non-empty
specsTouchedand at least onedecisionsentry withchoicepopulated. If a record is missing either, something went wrong building it during archive (not in the original data, which Task A already validated) — re-run archive for that change rather than proceeding with a partial record. -
Keep records grouped by change, exactly as returned. Cross-link computation computes pairs within a single change's own
specsTouched— two unrelated changes archived in the same bulk-archive batch don't imply their capabilities co-touch each other, even though they landed at the same moment.
Output: the same record set from archive, confirmed structurally complete and ready for cross-link computation and INDEX appending.
Compute Cross-Links (All-Pairs Union)
Goal: for each change, compute the symmetric co-touch pairs within its own specs_touched, and combine those pairs across every change in this archive operation into one set of newly-contributed partners per capability. This step touches zero files — it only combines the specsTouched lists already sitting in the validated records. Merging those new partners with whatever related: entries a spec already has (never dropping any of them) happens during spec writing, inside the subagent that's already opening that file — there's no reason for the parent to read spec frontmatter just to seed a union that spec writing can do in the same breath as its own file read.
-
For a change with
specs_touched: [A, B, C], the contributed pairs are all 2-combinations excluding self:(A,B),(A,C),(B,C). Co-touch has no direction — pairing(A,B)means A gains B as a related partner and B gains A in the same step. -
This is a pure computation with no dependency on file I/O, so it's worth writing as a small pure function rather than eyeballing it per capability:
type ChangeLink = {
readonly change: string;
readonly specsTouched: readonly string[];
};
// All 2-combinations within one change's specs_touched. Pure, no self-pairs,
// no directionality — a co-touch relationship reads the same both ways.
const pairsFromChange = (
link: ChangeLink,
): ReadonlyArray<readonly [string, string]> =>
link.specsTouched.flatMap((a, i) =>
link.specsTouched.slice(i + 1).map((b) => [a, b] as const),
);
// Fold every change's pairs into one partner-set-per-capability map. This
// starts from an empty map every time — it combines pairs ACROSS the
// changes in this batch, not against a spec's on-disk related: list. That
// merge is deliberately left to spec writing, which is the step that opens the file.
const accumulateNewPartners = (
pairsByChange: ReadonlyArray<ReadonlyArray<readonly [string, string]>>,
): ReadonlyMap<string, ReadonlySet<string>> => {
const next = new Map<string, Set<string>>();
for (const pairs of pairsByChange) {
for (const [a, b] of pairs) {
next.set(a, (next.get(a) ?? new Set()).add(b));
next.set(b, (next.get(b) ?? new Set()).add(a));
}
}
return next;
};
- Fold every change's pairs into the same accumulating map, starting from empty — a bulk-archive batch of three changes touching overlapping capabilities should have all three changes' pairs combined before spec writing ever touches a file, so each capability's subagent is invoked once with a complete new-partner list rather than three times with partial data.
Shortened here. Read the whole file on GitHub.
Signals
- GitHub stars
- 24
- Forks
- 5
- Last commit
- Sep 2026
Advanced
- Item type
- skill
- Key
accelint-qrspi-archive- Source
- github.com/gohypergiant/agent-skills