Skip to content

feat(blueprint-plugin): deterministic tracker-integrity check + config surface for repo conventions #2128

Description

@laurigates

What

Add a deterministic tracker-integrity check to blueprint-plugin — a script that validates feature-tracker.json against itself and against doc frontmatter, and exits non-zero on divergence. Plus a manifest config surface so repo-specific conventions can extend it without forking.

This is the verification counterpart to #1354. That issue fixed sync writing wrong statuses; nothing detects the disagreement afterwards, so a tracker that drifts by any other route stays wrong indefinitely.

Why

Dogfooding blueprint on a C/SDL2 port (skullcaps-native, format 3.3 → 3.4) surfaced five tracker defects that had accumulated for ~3 months. All five are mechanical and none was caught by any existing check:

Defect Observed
statistics cache diverged from features[] total_features: 25 vs 27 actual; in_progress: 5 vs 8; completion_percentage: 68.0 vs recomputed 66.7
Status enum drift one feature "completed", seventeen "complete" — the complete count silently undercounted
Dead statistics buckets partial: 0, pending: 2 naming statuses no feature record uses
Two timestamps for one fact updated_at: 2026-05-20 vs last_updated: 2026-05-19, already disagreeing
FR cited by docs, absent from tracker FR-025 referenced by five documents — including inside another feature's own evidence string — but never minted, so invisible to every status query
Doc frontmatter vs tracker 13 PRDs/PRPs at status: Draft whose FRs were all complete

The statistics one is the sharpest: it's a cache of the features array, so every downstream "68% complete" figure quoted from that file was wrong, and nothing recomputed it.

These are schema invariants, not project opinions — which is what makes them plugin-ownable.

How

A scripts/blueprint-tracker-check.sh (or Python, matching tools/ conventions) emitting the existing === SECTION === / KEY=VALUE / STATUS= convention used by check-adr-numbers.sh and get-automation-config.sh, exit 1 on defects. Checks:

  1. Recompute statistics from features[]; report per-field divergence.
  2. Status enum: flag any value outside the canonical set.
  3. features[].status vs tasks.{pending,completed}[] membership.
  4. FR ids cited in docs/** but absent from features[].
  5. Doc frontmatter status: vs tracker, for docs whose FRs have all landed.
  6. Duplicate/dead statistics buckets and duplicate timestamp fields.

One implementation note worth encoding centrally. My first version walked the whole JSON tree for id-bearing objects and reported 7 false "duplicate records" — .tasks.pending[] / .tasks.completed[] legitimately repeat an FR id already in .features[], which is the documented drain design. Status must be read from .features[] only, with the task lists checked separately for agreement. Easy to get wrong once per repo; better encoded once.

Config surface

Checks 2 and 5 need repo-specific vocabulary — which statuses count as "unfinished", which as "done". Following the precedent #1043 set with structure.generated_rules_path, a manifest block with working defaults:

"validation": {
  "status_vocabulary": {
    "done": ["complete"],
    "unfinished": ["draft", "proposed", "ready", "in_progress"]
  },
  "exclude_basenames": ["README.md"],
  "doc_globs": ["docs/prds/*.md", "docs/prps/*.md", "docs/adrs/*.md"]
}

Absent block behaves as the defaults, so it's purely additive.

What should NOT be upstreamed

To draw the boundary explicitly — the same dogfooding produced checks that are genuinely project-specific and belong in the repo, not the plugin:

  • "any feature title containing 50 levels contradicts ADR-0024" — a project fact.
  • An exemption for SAVE_LEVEL_COUNT 50, a deliberate over-allocation that a naive sweep would have "fixed" into a bug.

Those want the config surface to point at a repo-local extension, or simply to stay as a local script. Happy to be argued out of the config block entirely if the preference is "plugin owns invariants, repos own conventions, no bridge."

Status

Working, tested implementation exists locally (~200 lines, ruff-clean, fault-injection verified: regressing one enum and one doc status turns it red, restoring returns clean). Wired to just, pre-commit, and a CI job. Happy to open a PR against blueprint-plugin/scripts/ with a companion tests/test-blueprint-tracker-check.sh matching the existing four — say the word on the config-surface shape first, since that's the part with a real design choice in it.

Metadata

Metadata

Assignees

No one assigned

    Labels

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions