>- Sweep all active OpenSpec proposals for staleness, conflicts, and obsolescence against the current codebase and archived changes. Use when proposals may be outdated, when checking cross-proposal conflicts, or before starting a batch of implementations. Produces a gap-analysis report, updates a priority queue file, and can auto-fix trivial issues or guide conversations for complex ones.
npx skills add https://github.com/BlackBeltTechnology/pi-agent-dashboard --skill spec-coherence-check
Analyze active OpenSpec proposals against the current codebase state,
detect staleness / conflicts / obsolescence, and orchestrate updates.
Input: Optional --proposal <name> for single-proposal mode.
No arguments = full sweep of all active proposals.
a) Get all active proposals:
openspec list --json
If --proposal <name> was provided, filter to just that proposal.
b) List all archived changes:
ls openspec/changes/archive/
Parse archive directory names to extract dates. Format is YYYY-MM-DD-<name>.
Extract the first 10 characters as the date string.
c) Date each active proposal using this fallback chain:
git log --follow --diff-filter=A --format='%ai' -- "openspec/changes/<name>/proposal.md" | tail -1
Parse the date portion (first 10 chars: YYYY-MM-DD).
stat -f "%SB" -t "%Y-%m-%d" "openspec/changes/<name>/proposal.md"stat -c "%W" "openspec/changes/<name>/proposal.md" (convert epoch to date)d) Read artifacts for each active proposal.
Read only the files that exist — not all proposals have all artifacts:
openspec/changes/<name>/proposal.md (always exists)openspec/changes/<name>/design.md (if exists)openspec/changes/<name>/tasks.md (if exists)openspec/changes/<name>/specs/ directory (if exists)From each proposal, extract and note:
src/... in Impact and body text### Modified Capabilities and ### New Capabilities## Impact section specificallye) Read relevant archived changes.
For each active proposal, identify archives dated after its creation date.
For each such archive, read its proposal.md and extract:
## What Changes — summary of modifications## Capabilities — look for BREAKING markers, Modified, Removed entries## Impact — files and components touchedKeep only archives whose Impact or Capabilities overlap with the proposal
being analyzed (same files or same capabilities).
For each active proposal, extract all file paths from:
## Impact section (look for src/... patterns and filenames like FooBar.tsx)For each extracted path:
find src/ -path "*<filename>" -o -name "<filename>" 2>/dev/null
If a referenced file does not exist anywhere in the codebase:
stale, autoFixable true<path> no longer exists" rg -l "<key-term-from-filename>" src/ --type ts
If a likely replacement is found, note it in the issue for auto-fix.
For each archived change that is dated after the proposal's creation:
Compare the archive's data against the proposal:
Impact files? If yes, the proposal may reference outdated file state.
proposal lists under Modified Capabilities? If yes, the proposal's
assumptions about those capabilities may be invalid.
BREAKING in its Capabilitiessection for capabilities the proposal touches?
For each overlap found:
broken, autoFixable false<archive-name> has BREAKING changes to<capability> which this proposal modifies. Specifically: <detail>"
stale, autoFixable true<file> was modified by <archive-name> after thisproposal was created. Impact section may be outdated."
For each proposal that has Context, Non-Goals, or Design assumptions sections,
extract key statements and verify them against the current codebase.
What to check:
X still does Y.
rg "<key pattern>" src/ --type ts -l
Then read the file to verify the claim.
rg "<Z-related-pattern>" src/ --type ts -l
If Z now exists, the Non-Goal is invalidated.
rg "<message_type>" <protocol-source-files>
find <components-dir>/ -name "<ComponentName>*"
rg "<W-pattern>" <source-dir>/ --type ts -l
For each invalidated statement:
assumption wrong): severity broken, autoFixable false
<evidence>"severity stale, autoFixable true
For each proposal, check whether the feature it introduces already exists:
ls openspec/specs/ | grep "<capability-keyword>"
rg -l "<feature-keyword>" src/ --type ts
ls -la <planned-new-file-path> 2>/dev/null
If strong evidence the feature already exists:
obsolete<file> exists / spec <name> already covers this capability"
Skip this step if running in single-proposal mode (--proposal <name>).
Build a file-touch matrix. For each proposal, extract the files from its
Impact section. Then identify all files/capabilities touched by 2+ proposals.
For each overlap:
lowmediumhighRecord each conflict with:
For each proposal, calculate a priority score (lower = implement first):
Base = 50
SUBTRACT:
-20 status is "ok" (no issues, ready to implement)
-15 complexity is "trivial" (1-2 files, isolated)
-10 no cross-proposal conflicts
-10 no dependencies on other proposals
- 5 touches fewer than 5 files
ADD:
+20 status is "broken" (needs rework before implementable)
+15 other proposals depend on this one (infrastructure change)
+10 complexity is "fundamental" (architecture-level)
+ 5 has cross-proposal conflicts
Classify complexity:
trivial: 1-2 files, isolated fix, no protocol/architecture changesminor: small feature, well-scoped, < 5 filesmajor: cross-cutting, multiple components, protocol changesfundamental: architecture-level, breaking changesDependency override: If proposal A should be done after proposal B
(because B establishes patterns/infrastructure A needs), then
A.priority MUST be higher (worse) than B.priority regardless of raw scores.
Obsolete override: If status is "obsolete", set priority = 999.
Empty override: If a change directory has no proposal.md or only an
empty directory, set status = "empty" and priority = 999.
Sort proposals by priority (ascending). This is the suggested
implementation order.
Display the report to the user in this format:
## Coherence Sweep Report — <YYYY-MM-DD>
### Summary
| Proposal | Status | Issues | Complexity | Priority | Created |
|----------|--------|--------|------------|----------|---------|
| name | ✅/⚠️/🔴/💀/📭 | N | trivial/minor/major/fundamental | N | YYYY-MM-DD |
Status legend: ✅ OK ⚠️ STALE 🔴 BROKEN 💀 OBSOLETE 📭 EMPTY
### Cross-Proposal Conflicts
| File/Area | Proposals | Severity | Suggested Resolution |
|-----------|-----------|----------|---------------------|
### Suggested Implementation Order
1. **name** (priority N) — reason
2. **name** (priority N) — reason
...
### Detailed Issues
(Show only for proposals with issues — skip ✅ OK proposals)
#### <proposal-name> (<status emoji>)
1. **[STALE]** Description
- Caused by: <archive-name or codebase change>
- Auto-fixable: yes
- Fix: update `<old>` → `<new>` in `<artifact>`
2. **[BROKEN]** Description
- Caused by: <archive-name>
- Auto-fixable: no
- Recommendation: <specific action>
For large sweeps (10+ proposals), show the summary table first, then
detailed issues only for flagged proposals. Do not expand ✅ OK proposals.
Write the analysis results to .pi/proposal-queue.json.
If the file already exists, read it first:
cat .pi/proposal-queue.json
Extract any notes fields from existing proposal entries. These are
user-added annotations that MUST be preserved in the updated file.
Write the JSON file following the schema in
references/proposal-queue-schema.md.
Include:
lastChecked: current ISO-8601 timestamplastSweepSummary: e.g., "3 broken, 2 stale, 9 ok, 0 obsolete"proposals: array with full analysis per proposalconflicts: array of cross-proposal conflictsAfter writing, announce:
> "Wrote .pi/proposal-queue.json with N proposals, M conflicts."
After displaying the sweep report, use the AskUserQuestion tool to ask:
> "Which proposals do you want to address? Pick from the flagged ones
> (e.g., 'terminal-emulator, session-tree-navigation'), say 'all' to
> process all flagged proposals in priority order, or 'none' to stop here."
If the user says "none", stop. The sweep report and JSON file are the output.
For each selected proposal, proceed based on its status:
For each issue with autoFixable: true:
In `<artifact>`:
- Old: `<old text>`
+ New: `<new text>`
openspec validate <name>
status to "ok" in .pi/proposal-queue.json.
For each issue with autoFixable: false:
## Issue: <short title>
**In your proposal:** "<quote from the proposal artifact>"
**In reality:** "<what actually exists or changed in the codebase>"
**Caused by:** <archived change name or codebase evolution>
### Options
A) <option that simplifies the proposal to match current reality>
B) <option that preserves the original intent with adjustments>
C) Defer — needs deeper investigation before deciding
D) Mark as obsolete — this aspect is no longer needed
custom response).
specs/) to reflect the decision. Show the changes before applying.
notes field in the JSON:"Deferred: <issue description> — needs investigation"
the proposal are marked D, suggest archiving the whole proposal.
> "This changes the proposal fundamentally. Want me to regenerate
> design.md and tasks.md? (This would use openspec-ff-change to
> recreate downstream artifacts.)"
openspec validate <name> --strict
.pi/proposal-queue.json with resolved issues and new status. ## Proposal `<name>` appears obsolete
**Evidence:** <why it's obsolete — feature exists at `<file>`,
capability `<name>` already covers this, etc.>
Archive this proposal?
openspec archive <name> --skip-specs --yes
Remove the entry from .pi/proposal-queue.json.
appropriate, add a note explaining why it's still relevant.
When processing a proposal that has entries in the conflicts array:
## Conflict: <file/area>
**Proposal A (`<name>`):** <what it plans to do>
**Proposal B (`<name>`):** <what it plans to do>
**Suggested resolution:** <from the conflicts array>
.pi/proposal-queue.json:dependsOn if an ordering was agreedThe dating fallback chain handles this — filesystem birthtime is the
second option. If stat also fails, use the oldest archive date.
electron-embedding) may have anempty directory or only a directory with no proposal.md. Mark these as
status "empty" with priority 999 and skip all detection steps.
proposal.md withoutdesign.md or tasks.md. Run detection only against the artifacts that
exist. Don't flag missing optional artifacts as issues.
YYYY-MM-DD-<name>format. Always parse the first 10 characters as the date. Some names
may contain extra hyphens — only the first 10 chars matter.
the lower severity: STALE over BROKEN, BROKEN over OBSOLETE. Every
issue MUST cite specific evidence (file path, archive name, code
snippet). Never flag something without evidence.
proposals sequentially — gather context for one, analyze it, move to
the next. Don't try to hold all proposals in memory at once. The
summary table and JSON file accumulate results incrementally.
stat -f "%SB", Linux usesstat -c "%W". Try macOS first, fall back to Linux syntax.
auto-fixes MUST be displayed before applying.
via guided conversation. Only STALE issues with autoFixable: true
can be auto-fixed.
proposals marked obsolete.
openspec validate after any artifact modification..pi/proposal-queue.json,read the existing file first and carry over any notes fields.
specific file, archived change, or code snippet. Never speculate
about what might be wrong. If you can't find evidence, don't flag it.
--proposal <name> was given,do not analyze other proposals or run cross-proposal conflict detection.
Take blackbelttechnology/spec-coherence-check from the repository into ~/.claude/skills for personal
use, or into .claude/skills inside a project.
The agent identifies a skill by the name field in its header. Two skills with the
same name cannot sit side by side — one of them will be ignored.