Skip to content

wiki-check: a superseded decision record can never return to fresh, so it is stale forever #1

Description

@emyann

What happens

scripts/wiki-check.mjs freshness-checks every page identically. It mentions superseded exactly once — at line 52-55, in a comment explaining that decisions/ legitimately use status: for their lifecycle:

// Legacy tooling-owned fields (decisions/ legitimately use `status:` for
// their lifecycle — proposed/accepted/superseded — so only flag the old
// computed freshness values).
if (/^status:\s*(fresh|stale|unmanaged)\s*$/m.test(parts.fm) || /^last_checked:/m.test(parts.fm)) legacyPages.push(rel);

The code never reads that value. So a record marked status: superseded is checked for drift like a living page — and it can never come back, because AGENTS.md forbids the only thing that would fix it:

Decision records are immutable-with-supersession: once status: accepted, never rewrite the decision […] When wiki-check.mjs flags an accepted decision stale (its covered files changed), that is a prompt to consider a superseding record, not to edit the old one.

For an accepted ADR that rule works: stale is an actionable prompt with an exit. For a superseded one there is no exit — the supersession already happened, and there is nothing left to do, ever. It contributes to the stale count and to the non-zero exit code permanently.

Why it matters

It is the erosion AGENTS.md warns about in the refresh-by-rote passage, arriving from the other side: a check that can never read clean stops being read.

This is not hypothetical at scale. In a repo that just deleted a predecessor system, five decision records went permanently stale at once — three superseded, two accepted whose covered files merely moved. The check will now never report all-green again, and the three genuinely-terminal ones are indistinguishable from the two that might still deserve attention.

Suggested shape (owner's call)

Split by lifecycle rather than muting decisions/ wholesale — the accepted-ADR signal is real and worth keeping:

  • status: superseded → its own reported category (ARCHIVED / historical), excluded from the stale count and from the exit code. Freshness is meaningless for a record whose whole job is to describe a past state.
  • status: accepted, covered file changed → stale, as today. Actionable: consider a superseding record.
  • status: accepted, covered file deleted → arguably deserves to be louder than a normal stale, not quieter. A live decision record covering code that no longer exists is the strongest possible "you owe a superseding record" signal, and today it is indistinguishable from a whitespace change.

A wiki-stamp guard that refuses to stamp a superseded record would close the same loop from the other end — stamping one asserts prose reflecting current code, which a superseded record by definition does not.

Version

Reproduced at cfe6ce4; also present in the 0.4.7 scripts vendored into a consuming repo.

Activity

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions