git:20260529.5656e38 to git:20260529.0565655

66 added, 80 removed. Audit A to A.

---
name: staleness-discipline
- description: "Detect, classify, and prune stale plugins in Alex_ACT_Plugin_Mall -- define what stale means and how to remove gracefully"
- lastReviewed: 2026-05-05
+ description: "Detect, classify, and prune stale source stores in the Mall — define what stale means and how to remove gracefully without breaking downstream consumers."
+ lastReviewed: 2026-05-29
---
# Staleness Discipline
- Define what "stale" means in the Mall and how to remove entries without breaking heirs that depend on them.
+ Define what "stale" means for a source store in the Mall, and how to remove an entry from `sources/supported-stores.json` without breaking downstream consumers.
+ The Mall is read-mostly: downstream agents install from upstream at pinned refs. Pruning a source from the registry stops new catalog entries from that source but does not retroactively break existing installs (those still resolve to their pinned SHA). The discipline below keeps the catalog honest.
+
## When to Use
- - Monthly mall audit (fired by `/audit-mall`)
- - A heir reports a broken or outdated entry
- - A `/prune-store` invocation
- - Coherence-check finds an Edition reference to a Mall entry that should be pruned
+ - Weekly catalog-refresh PR shows a store with degraded signals (no recent commits, broken license signal, archived upstream)
+ - A reviewer flags a store as no longer fit during PR review
+ - `/prune-source` invocation
- ## What "Stale" Means
+ ## What "stale" means
An entry is stale if **any** of these apply:
- | Signal | Threshold |
- |---|---|
- | Link unreachable | HTTP 4xx/5xx for 2+ consecutive checks across 7 days |
- | Upstream archived | Repo settings show "Archived" or equivalent |
- | No upstream activity | No commits, releases, or issue activity in 18+ months |
- | License changed unfavorably | Switched to a more restrictive or unclear license |
- | Description drift | Mall description no longer matches upstream README's description |
- | Replaced by superior alternative | Another Mall entry covers the same ground better |
- | Heir reports persistent breakage | >= 2 heirs report the entry doesn't work as documented |
- | Missing plugin.json | Plugin folder lacks machine manifest |
- | Missing README.md | Plugin folder lacks human-readable description |
- | Shape mismatch | plugin.json shape doesn't match actual files in the folder |
- | Token cost stale | plugin.json token_cost differs from actual file sizes by >20% |
+ | Signal | Threshold | Where it shows up |
+ |---|---|---|
+ | Remote unreachable | `bootstrap-sources.cjs` clone fails in 2+ consecutive cron runs | Workflow log |
+ | Upstream archived | `fetch-github-stats.cjs` returns `archived: true` | `scoring/github-stats.json` |
+ | No upstream activity | `last_commit` older than 18 months | `scoring/github-stats.json` + maintenance signal = 0 |
+ | License changed unfavorably | SPDX id changed to NOASSERTION or to a more restrictive license | `scoring/github-stats.json` `license` field |
+ | Replaced by superior alternative | Another source covers the same domain better with higher trust scores | Per-store trust delta in `scoring/trust-audit.md` |
+ | Persistent scan failure | `scan-sources.cjs` produces 0 plugins for 2+ consecutive runs after previously producing >0 | `catalog/stores/<name>.json` `plugins` array shrinks to 0 |
+ | Schema or shape drift | `normalize-frontmatter.cjs` warns or errors on >50% of plugins from that source | Normalize log |
## Classification
| Stale signal | Action |
|---|---|
- | Link unreachable, but upstream still active | Refresh (update URL) — not prune |
- | Upstream archived but stable | Keep, mark `archived: stable` in entry |
- | No activity AND archived | Prune |
- | License change | Defer to ACT pass — license escalation may need user input |
- | Description drift only | Refresh |
- | Replaced by superior alternative | Prune the worse entry, update the better one's description |
- | Persistent heir breakage | Investigate before pruning — may be a Mall-side fix |
-
- ## Pruning Procedure
-
- Pruning is **always reversible by re-add**. Never delete history.
+ | Remote unreachable, but upstream still alive on GitHub | Refresh — fix `remote` URL, don't prune |
+ | Upstream archived but plugins still valuable | Keep (note `archived` in PR review); catalog continues to surface them with degraded maintenance score |
+ | Upstream archived + no activity for years | Prune |
+ | License changed to unclear or restrictive | Surface in PR review; user decides whether to keep with degraded license signal or prune |
+ | Replaced by superior alternative | Prune the worse entry; downstream consumers can opt into the better one via `--from-store` override |
+ | Persistent scan failure with no clear cause | Investigate `pluginDir` first — upstream may have renamed the folder. Only prune after fix attempts fail. |
- 1. Run [mall-curation](../mall-curation/SKILL.md)'s decision tree to confirm prune is correct
- 2. Check coherence: does any Edition skill or instruction reference the entry being pruned?
- - If yes, fix the Edition reference *first* (open Edition PR), then prune
- - If no, proceed
- 3. Comment out the entry in STORES.md and CATALOG.json (do not delete)
- 4. Add a `pruned: YYYY-MM-DD reason: <reason>` line above the commented entry
- 5. Update README counts
- 6. Open Mall PR titled `prune: <entry name> (<reason>)`
- 7. After merge, document in `docs/` if this prune sets a class precedent
- 8. Record in `docs/ledgers/curation-log.md` with `M` or `S` classification
+ ## Pruning procedure
- ## Pruning Anti-Patterns
+ Pruning is **always reversible by re-add**. The two-step removal (comment-out → observe → delete) is the safety net. Run via `/prune-source`.
- | Anti-pattern | Correction |
- |---|---|
- | Hard-deleting the entry | Comment out instead — preserves history and reversibility |
- | Skipping the coherence check | Pruning a referenced entry breaks the Edition for fleet heirs |
- | Pruning without updating README counts | The Mall claims N stores; the count must match reality |
- | Pruning multiple entries in one PR without separate rationales | Each prune is a decision; bundle wastes the audit trail |
+ 1. Confirm at least one staleness signal above fires
+ 2. Comment out the entry in `sources/supported-stores.json` and add `// pruned-pending: YYYY-MM-DD reason: <reason>` above it
+ 3. Commit `[behaviour] prune source (pending): <name> — <reason>`
+ 4. Wait one weekly cron cycle and verify the catalog-refresh PR shows:
+ - The store missing from `catalog/stores/`
+ - Plugin count drops by the right amount in `catalog/index.json`
+ - No other unintended diffs
+ 5. After a clean cycle, delete the commented-out entry and commit `[behaviour] prune source (delete): <name>`
+ 6. Record the decision in `docs/curation-log.md`
- ## Re-Add Procedure
+ ## Re-add procedure
If a pruned store revives (un-archived, link restored, license clarified):
1. Run full [store-evaluation](../store-evaluation/SKILL.md) — past acceptance does not transfer
- 2. If it passes, uncomment in STORES.md and CATALOG.json
- 3. Update the prune line to `re-added: YYYY-MM-DD`
- 4. Increment counts
-
- ## Output Template (prune verdict)
-
- ```markdown
- # Prune Verdict — <store name>
+ 2. If it passes, add a fresh entry via `/add-source`
+ 3. Record the re-add in `docs/curation-log.md`
- **Entry**: <STORES.md line or CATALOG.json ID>
- **Date**: YYYY-MM-DD
- **Reason**: <which staleness signal fired>
+ ## Pruning anti-patterns
- ## Coherence Check
+ | Anti-pattern | Correction |
+ |---|---|
+ | Hard-deleting the entry in one commit | Always use the two-step (comment-out → observe → delete) so the catalog refresh PR can verify the impact cleanly |
+ | Skipping the observation cycle | Without one clean cron run, you don't know whether the catalog-refresh PR will surface only the expected diffs |
+ | Pruning multiple stores in one commit | Each prune is a decision; bundle wastes the audit trail and makes one bad prune undoable without reverting the others |
+ | Pruning for "I don't like the maintainer" or subjective reasons | The staleness signals are the bar; the commit message must cite the firing signal |
+ | Pruning `plugin-mall` itself | Constitutional to this Mall — would break the catalog's ability to surface first-party plugins |
- - Edition references: <count>
- - References fixed first: yes/no
- - Files affected: <list>
+ ## Output template (PR comment for a prune proposal)
- ## Counts After Prune
+ ```markdown
+ **Prune candidate: `<store-name>`**
- - STORES.md: <before> → <after>
- - CATALOG.json: <before> → <after>
- - README badge: <before> → <after>
+ Staleness signals firing:
+ - [x] <signal 1>: <evidence from workflow log / github-stats / trust-audit>
+ - [ ] ...
- ## ADR?
+ Action: comment-out now, delete after one clean cron cycle.
- Yes | No — <if yes, link to docs/ADR-NNN>
+ Downstream impact: <known consumers, if any; otherwise "no known consumers; downstream agents that depend on this store can pin to a specific SHA before deletion">
+ ```
- ## ACT Pass Trail
+ ## Falsifiability
- <markers>
- ```
+ This skill is wrong if any of the following occur by **2026-08-29** (90 days):
- ## Would Revise If
+ - Pruned stores are re-added within 30 days ≥2 times in a quarter (staleness signals miscalibrated — pruning prematurely)
+ - A store flagged as stale by these signals is still being actively used by a known downstream consumer (signals threshold too aggressive)
+ - The two-step procedure causes a registry-vs-catalog drift that ships ≥1 time (the reversible pattern is leaking)
- Revise if pruned entries are re-added within 30 days under new evidence 2+ times in a quarter (signals over-pruning), or if entries flagged stale by the script remain useful to active heirs (signals threshold miscalibration).
+ Track outcomes in `docs/curation-log.md` tagged `[STALENESS]`.
## Related
- - [mall-curation](../mall-curation/SKILL.md)
- - [store-evaluation](../store-evaluation/SKILL.md)
- - [mall-maintenance-rules](../../instructions/mall-maintenance-rules.instructions.md)
- - `/prune-store` prompt
+ - [source-inventory](../source-inventory/SKILL.md) — adding and editing registry entries
+ - [store-evaluation](../store-evaluation/SKILL.md) — gate for re-add
+ - [mall-self-curation](../mall-self-curation/SKILL.md) — the pipeline this discipline keeps clean
+ - [mall-maintenance-rules](../../instructions/mall-maintenance-rules.instructions.md) — always-on routing
+ - `/prune-source` prompt