Skip to content

parity gate: one ledger, one test, two assertions that can fail today - #78

Merged
cfdude merged 7 commits into
mainfrom
dev
Aug 4, 2026
Merged

parity gate: one ledger, one test, two assertions that can fail today#78
cfdude merged 7 commits into
mainfrom
dev

Conversation

@cfdude

@cfdude cfdude commented Aug 4, 2026

Copy link
Copy Markdown
Owner

Implements docs/superpowers/specs/2026-07-31-platform-parity-mechanism-design.md (the platform-parity-mechanism epic), per docs/superpowers/plans/2026-08-03-platform-parity-mechanism.md.

What this is

Claude Code is the permanent base platform; every other platform is held to parity with it. This is the mechanical layer of parity enforcement — it catches "you added a capability and never considered porting it," the one failure mode that exists even with a single platform and the one EDD structurally cannot catch (a forgotten capability has no scenario, so a corpus run stays green while the gap widens).

  • docs/parity-ledger.json — 9 capabilities claiming all 22 shipped artifacts. A capability is the unit, not a file: platforms implement the same capability differently (Codex derives command names from prompt-file stems, Hermes registers commands in plugin code), so a file-for-file mapping would force a correspondence that cannot exist. Unported platforms are absent from platforms[], not present-with-nulls — no half-truth to maintain.
  • scripts/test/parity-helpers.mjs + scripts/test/parity.test.mjs — the gate. Every artifact under commands/, agents/, skills/, hooks/, .claude-plugin/ must be claimed by exactly one capability, and every claimed path must exist.

Both assertions can fail today

That was the bar the spec set, in reaction to four vacuous guard tests found while building the EDD harness. Verified by falsification, not by assertion:

Change Result
new commands/zzz-scratch.md fails — unclaimed
agents/reconciler.md claimed twice fails — double-claimed
agents/reconciler.md removed fails — missing
skills/conductor/references/foo.md fails — recursion is real, not asserted-only
symlinked artifact under a walked root fails — see below

The fixture tests and the CI gate call the same parityViolations(). A re-implementation seam would have proved nothing about what actually runs — the same trap as the hook-JSON regex that matched zero commands.

An earlier draft had a third assertion, "every exemption carries a reason." With zero exemptions it could never fail, so exemptions were dropped entirely and filed as parity-ledger-exemptions, to be designed against a real can't-support case with a test that can actually fail.

Two holes found by the final review, both fixed

  • Symlinks were silently skipped. readdir uses lstat semantics, so entry.isFile() is false for a symlink — a symlinked artifact shipped via git without ever needing to be claimed. The walk now recurses only on directories and treats everything else as a leaf.
  • Two assertions shared one test, unclaimed first, so a stray unclaimed file threw before doubleClaimed was ever evaluated. Split.

And one usability fix

The walk reads the filesystem, so it also picked up git-ignored cruft — a macOS .DS_Store under skills/ failed the gate locally with wrong advice, and since --no-verify is banned here, hard-blocked the commit. walkArtifacts now filters through one batched git check-ignore --stdin and fails open outside a git repo.

Deliberately not git ls-files: that means tracked, and a brand-new unstaged commands/foo.md is untracked-but-not-ignored — filtering on tracked-ness would reintroduce exactly the miss this gate exists to catch. There's an anti-trap test asserting that file is still walked, and an in-code warning for the next person tempted to simplify it.

No version bump — stated, not skipped

No CHANGELOG.md entry, no MIGRATIONS entry, no README/Mintlify change. Nothing user-facing ships: no subcommand, no flag, no state.json schema change. This is a repo-maintenance gate in the same class as the existing SKILL.md/README.md dispatch-drift tests, which are likewise unversioned. Version stays 0.25.0.

CLAUDE.md gains the obligation next to release discipline, so the next person adding a command knows the ledger exists.

Tests

301 passing, 0 failing (287 baseline + 14 parity tests).

Consequence

hermes-platform-support now has no remaining unarchived depends-on. Its port proceeds under the gate — claiming each capability as it goes, rather than reconstructing them afterward.

https://claude.ai/code/session_01BigZtNRPv6wBuZZ9ipUHW5

cfdude added 7 commits July 31, 2026 10:28
Scoped deliberately small per the user's steer: scaffolding, not a system. The
hard part of multi-platform support is the Hermes port, not this ledger, and a
rich gate built against zero second platforms would be inventing structure for a
problem not yet met.

Ships one JSON ledger plus one test with two assertions -- every artifact claimed
by exactly one capability, and every claimed path exists. Both can fail today,
with one platform. That is the bar.

A capability, not a file, is the unit: platforms implement the same capability
differently (Codex uses filename-derived prompt files, Hermes registers commands
in plugin code), so file-for-file would force a correspondence that cannot exist.
Unported platforms are ABSENT from platforms[] rather than present-with-nulls, so
there is no half-truth to maintain.

DROPPED an exemption assertion that an earlier draft had. With zero exemptions it
could never fail -- the vacuous-coverage pattern that appeared four separate times
building the EDD harness. Exemptions are filed instead, to be designed against a
real cannot-support case with a test that can actually fail.

Four follow-ups filed so the deferred work is durable rather than conversational,
including the user's specific concern that the harness EXISTING is not the same as
EDD being DONE (the corpus covers one scenario against 22 artifacts) and that
parity must be REASSESSED on every platform added, not once.

Claude-Session: https://claude.ai/code/session_01WBqyLnBbYEi5hp2noc3cMC
…tion

Symlinked artifacts under a walked root were silently skipped since
readdirSync's isFile() is false for symlinks (lstat semantics) -- a
symlinked file could ship via git without ever needing to be claimed
in the parity ledger. walkArtifacts now recurses only on isDirectory()
and treats every other entry (including symlinks) as a leaf.

Also split the "exactly one capability" gate test into two separate
tests (unclaimed, doubleClaimed) so a stray unclaimed file can no
longer mask a genuine double-claim by throwing first.
walkArtifacts() read the filesystem directly, so a macOS .DS_Store recreated
under skills/ (ignored by .gitignore) tripped the parity gate with "add them
to docs/parity-ledger.json" -- wrong advice for a file that never ships. Since
--no-verify is banned here, that hard-blocked every macOS developer while CI
on a clean checkout was unaffected.

Filter the walk through a single `git check-ignore --stdin` call, failing
open (returning everything) if git is missing, errors, or rootDir isn't a
git repository -- fixtureRepo()'s temp dirs stay ungitted and must keep
passing unchanged. Deliberately not `git ls-files`: that means "tracked",
and an untracked-but-not-ignored new artifact must still be walked or the
gate stops catching the exact miss it exists for.
@cfdude
cfdude merged commit 10bbef1 into main Aug 4, 2026
8 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant