Skip to content

Commit b566f83

Browse files
huangyiireneclaude
andauthored
chore(spec): register the admin-platform-admin-standing dogfood proof tag, and red the drift that let it accumulate (#12549)
* chore(spec): register the dogfood proof tag `admin-platform-admin-standing` * feat(spec): fail check:liveness on an unregistered dogfood proof tag --------- Co-authored-by: Claude <noreply@anthropic.com>
1 parent 1b7e3d2 commit b566f83

2 files changed

Lines changed: 87 additions & 3 deletions

File tree

packages/spec/scripts/liveness/check-liveness.mts

Lines changed: 39 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -582,7 +582,17 @@ function classify(type: string, path: string, status: string, led: any, cat: any
582582

583583
// Reverse integrity: every `@proof:` tag declared under the dogfood proof tree
584584
// must be registered in proof-registry.mts. An orphan tag means a proof was
585-
// written but never wired into the high-risk-class list — flag it (warning).
585+
// written but never wired into the high-risk-class list.
586+
//
587+
// RED since 2026-08-26, having been a ⚠ before. The warning was calibrated for
588+
// a backlog era — it reported 13 orphan tags at once, then 11 — and a signal
589+
// that never fails the build cannot close the class it names: the 11 were
590+
// registered in one round, a 12th arrived with the next dogfood proof, and the
591+
// second card was written from the same ⚠ line as the first. The census that
592+
// licenses the flip is that round's own arithmetic: the list is at ZERO on the
593+
// commit this changes, so the gate starts green and only a NEW unregistered tag
594+
// can red it — the same "a check that starts at zero can be red" reasoning the
595+
// key-mention half was switched on under.
586596
function scanOrphanProofs() {
587597
const proofDir = join(repoRoot, 'packages/qa/dogfood/test');
588598
if (!existsSync(proofDir)) return; // spec may be consumed standalone (published)
@@ -820,6 +830,10 @@ const failed =
820830
// be red; that is the whole reason the census came first.
821831
report.keyMentionUnanchored.length > 0 ||
822832
report.keyMentionStale.length > 0 ||
833+
// A dogfood `@proof:` tag the registry never learned about. Red rather than ⚠
834+
// since 2026-08-26 — see scanOrphanProofs for why the warning could not close
835+
// the class, and for the zero-census that lets this start green.
836+
report.orphanProofs.length > 0 ||
823837
report.orphanEntries.length > 0 ||
824838
report.verification.errors.length > 0 ||
825839
report.producers.errors.length > 0 ||
@@ -937,8 +951,29 @@ if (asJson) {
937951
);
938952
}
939953
if (report.orphanProofs.length) {
940-
console.log(`\n ${report.orphanProofs.length} unregistered dogfood proof tag(s) — add to proof-registry.mts:`);
954+
console.log(`\n ${report.orphanProofs.length} unregistered dogfood proof tag(s) — add to proof-registry.mts:`);
941955
report.orphanProofs.forEach((s: string) => console.log(` ${s}`));
956+
console.log(
957+
'\n A `@proof:` tag on disk that the registry does not know is a proof no ledger entry\n' +
958+
' can cite: `BOUND_PROOF_PATHS` is built from HIGH_RISK_CLASSES, so an unregistered\n' +
959+
' proof cannot bind a `live` verdict however thoroughly it runs.\n\n' +
960+
' Add a HIGH_RISK_CLASSES entry in proof-registry.mts. Registering is mechanical;\n' +
961+
' the judgment half is `bound`. Read the proof file and ask: is there an AUTHORABLE\n' +
962+
' property whose `live` status it actually gates?\n' +
963+
' • yes → `bound: true` + the `ledgerBindings` entry, and put the same `proof` ref\n' +
964+
' on that ledger row (the wiring test in proof-registry.test.ts holds both ends);\n' +
965+
' • no → `bound: false` + a `blockedReason` naming what it guards INSTEAD (a\n' +
966+
' runtime/service invariant, a breadth sweep, or an entry already spoken for —\n' +
967+
' a ledger row carries one `proof` ref). Registered-and-honestly-unbound is a\n' +
968+
' first-class outcome; most of the registry is exactly that.\n' +
969+
' ⛔ Do not bind a proof to a property it does not AUTHOR just to reach `bound: true`\n' +
970+
' — a citation for a property the proof never exercises is the false comfort the\n' +
971+
' whole ledger exists to end.\n\n' +
972+
' This was a ⚠ until 2026-08-26. It reported 13 tags, then 11, and a warning nothing\n' +
973+
' fails on cannot close a class: the 11 were registered in one round and a 12th\n' +
974+
' arrived with the next dogfood proof, its card written off the same ⚠ line. The list\n' +
975+
' was at zero when this became red, so a hit here is a NEW tag, not a backlog.',
976+
);
942977
}
943978
if (report.proofMissing.length) {
944979
console.log(`\n✗ ${report.proofMissing.length} high-risk 'live' propert(ies) missing a runtime proof:`);
@@ -1168,7 +1203,8 @@ if (asJson) {
11681203
'every container inheritance is declared, every `live` entry\'s repo-local evidence path ' +
11691204
'resolves, every `path:NNN` citation names a line that file actually has and every cited ' +
11701205
'file names the property it is evidence for (or is a recorded exemption), all bound ' +
1171-
'high-risk proofs resolve, and the README state table carries a row ' +
1206+
'high-risk proofs resolve, every dogfood `@proof:` tag on disk is registered in ' +
1207+
'proof-registry.mts, and the README state table carries a row ' +
11721208
`for each of the ${report.readmeRowCount} governed type(s) it claims to index.`,
11731209
);
11741210
console.log(

packages/spec/scripts/liveness/proof-registry.mts

Lines changed: 48 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -791,6 +791,54 @@ export const HIGH_RISK_CLASSES: HighRiskClass[] = [
791791
// for a property it does not exercise.
792792
ledgerBindings: [{ type: 'permission', path: 'fields.readable' }],
793793
},
794+
795+
// ─────────────────────────────────────────────────────────────────────────
796+
// 2026-08-26 round — ONE tag, and the reason it is worth a paragraph is that
797+
// it is a RECURRENCE of the block above: the 2026-08-21 round registered the
798+
// eleven tags that existed then, and a twelfth arrived with the next dogfood
799+
// proof. Registering instances one round at a time never closes the class,
800+
// because the drift signal was a ⚠ that nothing failed on — so the gate is
801+
// switched to red in the same PR (check-liveness.mts), which is the half
802+
// that stops a thirteenth.
803+
//
804+
// Direction of drift, decided by reading both sides rather than assuming:
805+
// the REGISTRY lagged. `admin-platform-admin-standing` is not a misspelling
806+
// of an already-registered tag — the file names itself, its tag and its
807+
// sibling (`admin-route-nonadmin-refusal`, registered above) distinctly, and
808+
// no registered id is within a typo of it.
809+
// ─────────────────────────────────────────────────────────────────────────
810+
811+
{
812+
id: 'admin-platform-admin-standing',
813+
label: 'Platform-admin standing across the /admin/ route surface',
814+
summary:
815+
'the DUAL of `admin-route-nonadmin-refusal`, over the same derived `/admin/` route population: '
816+
+ 'no route refuses a genuine ADR-0068 platform admin unless the refusal is a RECORDED by-design '
817+
+ 'ruling (#9969\'s seven consumer-less vendor routes, #9968\'s `set-role`). better-auth\'s stock '
818+
+ 'admin plugin authorizes on the legacy `user.role === \'admin\'` scalar that ADR-0068 D2 stopped '
819+
+ 'synthesizing, so the vendor\'s own endpoints refuse a real platform admin — and this file is '
820+
+ 'what keeps that answer a ruled outcome instead of an unread gap. Two anti-vacuity pins carry '
821+
+ 'it: the subject\'s standing is asserted as a CONTROL before any route answer is read '
822+
+ '(`positions[]` contains `platform_admin`, `isPlatformAdmin` true, `sys_user.role` NOT '
823+
+ '`\'admin\'` — the fixture a real deployment has, not the `role = \'admin\'` scalar the unit '
824+
+ 'tests write), and every route is fired with a payload valid enough to REACH the gate, because '
825+
+ 'better-auth validates the body BEFORE the admin check and an empty-body sweep draws a '
826+
+ '`400 VALIDATION_ERROR` byte-identical for member and admin while looking exactly like a '
827+
+ 'passing security suite.',
828+
proofId: 'admin-platform-admin-standing',
829+
proofRef:
830+
'packages/qa/dogfood/test/admin-platform-admin-standing.dogfood.test.ts#admin-platform-admin-standing',
831+
bound: false,
832+
ledgerBindings: [],
833+
blockedReason:
834+
'platform-admin standing is the ADR-0068 D2 identity resolution (`positions[]` / '
835+
+ '`isPlatformAdmin`, consolidated since #11686 as `hasPlatformAdminStanding`) evaluated inside '
836+
+ 'the auth plugin\'s route gate — runtime principal resolution, not an authorable per-type '
837+
+ 'property, so there is no metadata key whose `live` status it gates. It is also a BREADTH guard '
838+
+ 'over a DERIVED route population, the `admin-route-nonadmin-refusal` / `permission-model-zoo` '
839+
+ 'shape: binding it to any single entry would misrepresent both what it covers and what that '
840+
+ 'entry is proven by. It runs unconditionally in the dogfood suite.',
841+
},
794842
];
795843

796844
/** Bound ledger paths → the class that binds them. Key: `<type>/<path>`. */

0 commit comments

Comments
 (0)