Skip to content
Back to skills

Archive Prd

ASecurity

Archives completed stories from prd.json to reduce token usage.

  • 6 stars
  • 0 votes
  • 0 copies
  • 4 views
  • Added February 10, 2026
developmentshellbashnodetestinggitdocumentation

Security analysis

A100/100

Scanned September 20, 2026

npx -y skills add djnsty23/claude-auto-dev --skill archive-prd --agent claude-code

Installs into .claude/skills of the current project.

Are you the author of Archive Prd?

Add the live security badge to your README. It updates with every re-scan.

Security grade badge for Archive Prd
[![Security: A — Skills Directory](https://www.skillsdirectory.com/api/skills/djnsty23-archive-prd/badge)](https://www.skillsdirectory.com/skills/djnsty23-archive-prd)

More formats (shields.io, HTML) on the badges page. Keep it an A: scan every change in CI with Pro.

Download with Pro
SKILL.md
---
name: archive-prd
description: Archives completed stories from prd.json to reduce token usage.
when_to_use: "Invoked when the user says \"archive\", \"compact prd\", \"prd too large\"."
allowed-tools: Read, Write, Edit, Bash
model: haiku
user-invocable: true
argument-hint: "[status|S-ID|unarchive S-ID]"
---

# PRD Archival System

Archive eligible completed stories to keep prd.json small while retaining every
unresolved story and every prerequisite that retained work still needs.

## "archive" Command

```
1. Read prd.json
2. Separate stories with isArchivable() from scripts/prd-states.js:
   - ARCHIVE: isArchivable(story) === true, excluding QA and prerequisite
              records referenced by any story that will remain
   - KEEP:    everything else — null, false, "deferred", "needs-setup",
              a MISSING passes key, any unrecognised value, and type="qa"
              (even passed QA stories stay, for re-testing), and completed
              prerequisites still referenced by retained stories
3. PROVE THIS RUN'S SPLIT BEFORE WRITING: selected-active-to-archive count +
   retained-active count must equal the pre-run active count. The two selected
   id sets are disjoint and cover every pre-run active record. Preexisting
   archive records are a separate population, never part of that equation.
   If the invariant fails, STOP — write nothing, report the ids that fell
   through. This runs BEFORE step 4, not after: a loss detected after the
   write is a loss.
4. Create a unique tracked archive or merge an existing one without replacement
5. Update main prd.json with summary
6. Report: "Archived X stories, Y remain active" with both counts and the total
```

**Why the two-bucket version was destructive:** the old split (ACTIVE:
false|null|qa / COMPLETED: true) matched neither bucket for `"deferred"`,
`"needs-setup"`, and keyless stories — they were written to neither file and
silently deleted. `isArchivable()` exists because of exactly that incident (its
own comment block records it); route through it rather than re-deriving buckets.

## New prd.json Schema (After Archive)

```json
{
  "project": "Project Name",
  "version": "1.3.2",
  "lastUpdated": "2026-01-22",
  "roadmapPhase": "Current Phase",

  "archived": {
    "totalCompleted": 41,
    "lastArchived": "2026-01-22",
    "files": ["prd-archives/prd-archive-2026-01.json"],
    "summary": {
      "S01-S10": "Core foundation - registry, funnels, OAuth, caching",
      "S11-S20": "Navigation, QA, dashboard, exports, favorites",
      "S21-S30": "Time granularity, GA4 schema, accessibility, mobile",
      "S31-S41": "Token refresh, metrics, documentation, scope rules"
    }
  },

  "stories": {}
}
```

## Archive File Schema

```json
{
  "archivedAt": "2026-01-22T10:00:00Z",
  "project": "Project Name",
  "version": "1.3.2",
  "stories": {
    "S1-001": { "id": "S1-001", "title": "Completed outcome", "passes": true }
  }
}
```

## When to Archive

| Condition | Action |
|-----------|--------|
| 4+ total sprints | Auto-suggest archive |
| prd.json > 500 lines | Suggest archive |
| prd.json > 50KB | Archive eligible completed records; preserve all other work |
| User says "archive" | Manual archive |
| All stories complete | Archive and start fresh |

Sprint age selects history to inspect, not records to delete. Retain unresolved
stories from every sprint. Remove an old sprint container only after all of its
records were safely archived or explicitly carried forward.

## Archive Process

```
0. PROVE THE DESTINATION IS DURABLE — BEFORE ANY WRITE
   Resolve this loaded plugin's scripts/check-archive-path.js, then run it
   against the chosen archive path in the target project. Do not assume the
   plugin-root environment variable exists in the shell.
   Verify that the actual target is a git worktree and read the checker's
   verdict and stderr, not its exit alone: it returns 0 with NO-REPO for an
   unbacked local path. A missing/unreadable repo or unavailable check does not
   establish durability; preserve the PRD until a real tracked destination is
   verified. Exit 1 means git would ignore the path: stop and write nothing. Do not
   "fix" it with a .gitignore negation — a `!` rule cannot re-include a file
   whose parent directory is excluded, so it looks like an exception and grants
   nothing. Write somewhere outside the ignored tree instead.

1. BACKUP
   mkdir -p prd-archives
   Write a unique backup path; do not overwrite an earlier same-day snapshot
   Inventory all preexisting archive ids and record payloads separately, with
   their paths/hashes, before proposing a monthly merge or updating totals

2. EXTRACT ARCHIVABLE
   Filter with isArchivable() from scripts/prd-states.js (and keep type="qa"
   regardless). KEEP everything it rejects: null, false, "deferred",
   "needs-setup", a missing passes key, any unrecognised value. Keep completed
   prerequisites referenced by retained stories too; propagate that keep-set
   through their blockedBy links until it stops growing. Run workPlan before
   and after the proposed split: archiving must not make a ready story blocked
   by a newly missing dependency. Preserve id-keyed records and all own keys.

3. PROVE THE SPLIT — BEFORE ANY WRITE
   Let A be this run's selected active records and K the retained active records.
   A.size + K.size === preRunActive.size, with zero id overlap and unchanged
   payloads across their union. Existing archives are not included in A.
   On failure, write nothing and name the missing/conflicting records.

4. CREATE ARCHIVE
   Write to a fresh uniquely named file under prd-archives/ (step 0 proved git
   keeps it), or explicitly merge the existing monthly archive by story id.
   Never replace an existing archive with only this run's records. Every prior
   archive record must survive with the same payload. If an incoming id collides
   with a different archived payload, report the conflict and preserve both
   snapshots until its provenance is resolved; do not silently overwrite it.
   On re-archive, preserve files[] without duplicates and compute totalCompleted
   from distinct archived story ids, counting an already-present identical id
   once. Read back the archive before changing the PRD.

5. GENERATE SUMMARY
   Group stories by ID range (10 per group)
   Write 1-line summary per group

6. UPDATE MAIN PRD
   Remove archived stories
   Add/extend "archived" section with summary
   Keep all QA stories (even passed ones for re-testing)

7. VALIDATE
   Report remaining size; a size target never permits dropping retained work
   Re-assert the step-3 invariant using A and K against the written files.
   Confirm every A record is archived with its original payload and every K
   record remains active. Separately confirm every preexisting archive record
   remains unchanged. A monthly archive's full count includes older records
   and cannot substitute for A.size; equal totals alone cannot prove either
   preservation property.
   Re-run step 0 against the archive AS WRITTEN, and confirm `git status`
   actually shows it. Stage the archive and PRD together by explicit path and
   commit together under the existing mandate. Counting stories proves
   completeness, not durability.
```

Note the shape this must survive: real projects store `stories` as an OBJECT
keyed by id (never `.filter()` it — `Object.values()` first), and `archived`
is a top-level key the archive itself adds, so a re-archive must not treat an
already-archived file as un-archived.

## Accessing Archived Stories

If you need details on an archived story:

```
User: "What was S15 about?"
Claude:
1. Check archived.summary for S15 range
2. Read prd-archives/prd-archive-2026-01.json if needed
3. Report story details
```

## Quick Reference

| Say | Action |
|-----|--------|
| `archive` | Archive completed stories |
| `archive status` | Show archive stats |
| `archive S15` | Show archived story S15 |
| `unarchive S15` | Restore story to active |

---

## Token Optimization

Measure the actual before/after file size and story population. Archive eligibility
and dependency preservation determine the split; a token budget or sprint count
cannot override them. Report any required retained history even when the PRD
remains larger than the preferred target.

## Proving the run

Two properties, and they fail independently. Assert both.

**Observable 1 — COMPLETENESS: no story is lost.** This run's selected active
records A plus retained active records K exactly reconstruct the pre-run active
PRD by id and payload. Read back every A record from the archive and K from the
PRD. Separately preserve every preexisting archive record. Adding the entire
monthly archive count to the retained active count double-counts old history.

Read both written files and compare the actual id sets and record payloads to
the preserved pre-archive snapshot; counts alone can hide one lost/one duplicate
record. Verify the dependency-ready set stayed intact too.

Check the total before and after and state both numbers. A story dropped during
the move looks exactly like a story that was never there.

**Observable 2 — DURABILITY: the archive is a file git will keep.** Resolve
`archive_path` from the file actually written before running:

```bash
node "${CLAUDE_PLUGIN_ROOT}/scripts/check-archive-path.js" "$archive_path"
git status --short <the archive you wrote>     # it must appear
```

⚠️ **Counting is not durability, and this is how the archive was lost.**
`[measured 2026-08-29]` a project archived 159 completed stories, and its count
check passed *correctly* — both files existed at that moment. The archive had
been written to `.claude/archives/`, a gitignored path, so `git add -A` skipped
it in silence and the commit carried only the deletion from prd.json. The archive
and the backup taken beside it lived on one machine's disk and nowhere else.
The count check could never have caught it: it measures completeness while the
failure mode is durability. A check that reports green about a property it does
not examine is this project's signature failure — see `skills/rule-gate-integrity`.

**If an archive was already lost this way, it is probably recoverable.**
Archiving REMOVES stories from a tracked `prd.json`, so the commit *before* the
archive commit holds the complete pre-archive state — every story with its full
`verified` record:

```bash
git log --oneline -S'"archived"' -- prd.json    # find the archive commit
git show <archive-commit>^:prd.json             # the real backup
```

That recovered the full 164-story file in the incident above. Do **not** send
anyone to the archive path for recovery: under the conditions that lose the
archive, that file is exactly what does not exist.

Attribution

Is this your skill, or is something wrong with this listing? Request removal or report an issue. Author removals are honored within 72 hours.

Comments

Loading comments…