---
name: pstack-doctor
preamble-tier: 2
version: 0.1.0
description: |
  Programmatic alignment audit for pstack-managed repos. Greps the
  repo for known-stale vocabulary, dropped invocations (gstack, codex),
  and posture references that drifted from the canonical conventions.
  Reports findings with file:line evidence; `--fix` applies safe
  auto-substitutions. Run on demand, or wire into CI via
  `.github/workflows/pstack-doctor.yml`.
  Use when asked to "audit pstack alignment", "/pstack-doctor",
  "check for drift", or "run the doctor".
triggers:
  - /pstack-doctor
  - audit pstack
  - check for drift
  - run the doctor
allowed-tools:
  - Bash
  - Read
  - Write
  - Edit
---

# pstack-doctor


## Preamble — pstack upgrade check

Before doing the skill's actual work, run:

```bash
"${CLAUDE_SKILL_DIR}/../../scripts/pstack-update-check.sh" 2>/dev/null || true
```

Silent unless a pstack upgrade is available. When it fires it prints one
line ("ℹ pstack vX is available...") that the user can act on at their
convenience — it does not block the rest of this skill.

Drift detection for the layered conventions pstack maintains across docs,
skill bodies, templates, and runtime config. Catches the class of bug
where a vocabulary change lands in one place but not the others — the
exact failure mode that produced the v0.3 → v1.0 alignment incidents
documented in `docs/anti-patterns.md`.

## Args

```
/pstack-doctor                # run audit, human-readable output
/pstack-doctor --fix          # apply replacements where a fix is defined
/pstack-doctor --ci           # non-zero exit on error-level findings
/pstack-doctor --json         # machine-readable output
/pstack-doctor --verbose      # include info-level findings
```

Combinations: `--fix --ci`, `--json --verbose`, etc.

## What it checks

### Category A: Forbidden phrases (implemented)

Greps scoped files for known-stale patterns listed in
`forbidden-phrases.yaml`. Each entry has:

- `pattern` — substring to search for
- `reason` — why this is stale (which decision deprecated it)
- `added` — when the pattern was added to the list
- `replacement` (optional) — enables `--fix` auto-substitution
- `severity` (optional, default `error`) — `error` blocks CI; `warning` informs

Adding a new pattern is just editing the YAML — no skill body change.
Every drift incident should produce a new entry so the same drift can't
return silently.

### Category B: vocabulary consistency (implemented in Task #63)

Diffs the **structure-block switch coverage** between the two canonical
sources of pstack vocabulary:

- `plugins/pstack/templates/CLAUDE.md` (mentions switches inline: e.g.
  `` `use_milestones` `` / `` `structure.review_depth` ``)
- `docs/architecture/project-registry.md` (documents switches in the
  schema table: `` `structure.use_*` ``, `` `structure.review_depth` ``)

When a switch is mentioned in one source but not the other → drift.
Flagged as `warning` severity (informs but doesn't block CI).

**Scope reframe note.** The original Task #63 sketch called for parsing
a hypothetical Issue-hierarchy table from `project-registry.md`. That
file actually has no hierarchy table — only the schema + Structural
posture description. The implementation diffs the *structural-posture
switch set* instead, which is the vocabulary that actually drifts
between the two files.

No YAML to edit — the check is hard-coded in
`pstack-doctor-audit.py::check_b_vocabulary_consistency`. Adding a new
switch is automatic: as long as it's mentioned in BOTH files
(backtick-quoted), the check stays green.

### Categories C–E (deferred to follow-up Tasks under #38)

The skill scaffolding supports five categories total:

- **C. Skill type-filing audit** — verify each issue-filing skill
  (`pstack-issue`, `pstack-plan`, `pstack-add-project`) explicitly sets
  the correct `type:` for each shape. Parses SKILL.md bodies.
- **D. Schema consistency** — check `~/.pstack/config.json` against the
  schema documented in `project-registry.md`. Surface present-but-undocumented
  fields and documented-but-missing fields.
- **E. Plan-doc status** — for each `docs/plans/*.md`, parse the Status
  line and cross-check against git tag reality (shipped releases should
  match tags; "in progress" should not predate the latest tag).

These are listed in the skill body for visibility but **not yet
implemented**. Follow-up Tasks under #38 will deliver them one at a time
as the value justifies the build.

## How it works

The skill delegates to `plugins/pstack/scripts/pstack-doctor-audit.py`,
which:

1. Loads `forbidden-phrases.yaml` (alongside this SKILL.md by default).
2. Walks the scopes section's `include` globs minus `exclude` globs to
   build the file list.
3. Reads each file line-by-line, checking each pattern against each line.
4. Emits findings with file:line:line-text + the pattern's reason +
   suggested fix.
5. With `--fix`, applies replacements in-place for fixable findings.
6. With `--ci`, exits non-zero on any error-level finding (for GHA gating).

The script is plain Python 3, no external dependencies. The YAML parser
is hand-written for the constrained schema we use — keeps the install
footprint minimal.

## Steps

### 1. Run the audit

```bash
SCRIPT="${CLAUDE_SKILL_DIR}/../../scripts/pstack-doctor-audit.py"
python3 "$SCRIPT"
```

The script defaults to scanning the current working directory's repo.
Specify `--repo-root <path>` to scan a different repo.

### 2. Triage findings

For each finding, decide:

- **Apply the fix** (if `replacement` is defined): re-run with `--fix`.
  Safe auto-substitution; replaces every occurrence with the documented
  replacement.
- **Manual edit** (if no `replacement`, or the context needs nuance):
  open the file at the reported line and rewrite per the reason field.
- **Add an exception** (if the finding is a legitimate reference, e.g.,
  documenting the old phrase in `anti-patterns.md`): add the file to
  the `exclude` list in `forbidden-phrases.yaml`'s `scopes` section.

### 3. Re-run to confirm

```bash
python3 "$SCRIPT"
```

Should report `✓ no findings`. Commit the fixes alongside any related
work.

### 4. (Optional) Add a new pattern

When you catch a new drift item that doesn't yet have an entry:

1. Open `plugins/pstack/skills/pstack-doctor/forbidden-phrases.yaml`.
2. Add a new entry under `phrases:` with `pattern`, `reason`, `added`
   date, and `replacement` if a safe auto-fix exists.
3. Run `/pstack-doctor` to verify the new pattern catches the drift.
4. Commit the YAML change alongside the drift fix.

## CI integration

Pstack ships `.github/workflows/pstack-doctor.yml` that runs the audit
on every PR. The workflow:

1. Checks out the PR branch.
2. Runs `python3 plugins/pstack/scripts/pstack-doctor-audit.py --ci`.
3. Fails the PR if any error-level findings are reported.

To skip the workflow for a project that doesn't want CI gating, delete
or rename the workflow file in that project's `.github/workflows/`.

## Output examples

**Clean run:**

```
pstack-doctor: ✓ no findings (51 files scanned, 18 patterns)
```

**Findings present:**

```
pstack-doctor: 2 finding(s) across 2 file(s)

  [error] README.md:42
    pattern: 'Parent Epic: #'
    reason:  v1.0.0 vocabulary correction — parent can be Epic OR Feature; use generic 'Parent: #N'
    line:    Format: `Parent Epic: #N` — pstack-execute uses this...
    fix:     replace with 'Parent: #'  (run --fix)

  [error] docs/architecture/some-old.md:7
    pattern: 'gstack /codex'
    reason:  v1.0.0 — codex calls replaced by Haiku-via-Task + optional Gemini CLI
    line:    Calls gstack /codex review for cross-model coverage.
    fix:     replace with (no replacement defined; edit manually)
```

## Do not

- Do not edit `pstack-doctor-audit.py` to special-case patterns. New
  drift goes in `forbidden-phrases.yaml`, not the script.
- Do not run `--fix` blindly when one of the findings is in a file
  you've been actively editing — review the diff before committing.
- Do not add patterns to the YAML for one-off mistakes that won't
  recur. The list is for *systemic* drift caught by alignment failures,
  not typo policing.

## Reference

- Story: [#38](https://github.com/periant-llc/pstack/issues/38)
- Anti-pattern documenting the drift incidents that motivated this skill:
  `docs/anti-patterns.md` — "Bypassing /pstack-pr for tactical fixes"
  and "Trusting release-please's manifest as the source-of-truth"
- Companion enforcement: `/pstack-pr` issue-link gate (#45)
- Spec frozen at: `docs/specs/pstack-doctor.md`
