Skip to content

docs(comments): correct four docblock sites describing the retired platform-admin elevation gate - #13907

Merged
os-steve merged 1 commit into
mainfrom
claude/issue-13661-retired-elevation-gate-comments
Aug 31, 2026
Merged

docs(comments): correct four docblock sites describing the retired platform-admin elevation gate#13907
os-steve merged 1 commit into
mainfrom
claude/issue-13661-retired-elevation-gate-comments

Conversation

@claude

@claudeclaudeBot commented Aug 31, 2026

Copy link
Copy Markdown
Contributor

Closes#13661

Comment-only. Zero behaviour change. The #11663 platform-admin re-anchor (legs L2 and L4, both landed) retired the walled platform-admin elevation gate — standing is now derived per request at resolve-authz-context.ts §6b-config, from a config-anchored verified email (OS_PLATFORM_OWNER_EMAIL plus a verified sys_user row) or the legacy unscoped grant row, config arm first. Four comment sites still described the retired mechanism as live.

All four re-verified against origin/main at 46b53a25b before editing, as the card required — PR #13685 (leg L3) rewrote 41 comment lines in last-admin-guard.ts, and all three quotes it could have carried away are still there, at the same lines the grading comment measured (:635-636, :648-650, :663-665).

The four corrections

1. packages/types/src/email-verified.ts — this docblock ships in the package's .d.ts, so its consumer set reaches consumers. It named the elevation gate as one of exactly two consumers. Now:

ONE resolution, several consumers, by design (#12751) — and since the #11663 platform-admin re-anchor (leg L4) the walled platform-admin ELEVATION GATE this paragraph used to name first is RETIRED: under a walled posture bootstrapPlatformAdmin writes no grant row and elevates nobody, it reports. Standing is derived PER REQUEST instead — from a config-anchored verified email, or the legacy unscoped grant row — so the consumer set now includes the authorization derivation itself

The list that follows names the live readers, verified by git grep isEmailVerifiedUserRow: matchesConfiguredPlatformAdmin (the derivation site, and through it plugin-auth's last-admin guard), resolvePlatformAdminStanding and isVerifiedPlatformOwnerRow in plugin-security, and the walled owner-verification boot diagnostic in plugin-auth. The drift argument is restated for that set:

They must all answer "is this row verified?" identically — a drift is no longer just a boot warning forecasting a refusal that will not be made, it is a diagnostic, an audit surface or a guard disagreeing with who actually resolves PLATFORM_ADMIN on the next request.

2. last-admin-guard.ts:635-636 — "un-makes every platform admin at once" is true for the grant anchor only:

derivation, so active: false on admin_full_access un-makes every GRANT-anchored platform admin at once — the same end state as renaming or deleting the row, reached by a payload that touches neither. ⚠️ Not "every platform admin": since the #11663 re-anchor (L2) standing has a SECOND anchor this write cannot reach — a config-anchored administrator (a declared OS_PLATFORM_OWNER_EMAIL address on a VERIFIED sys_user row) is derived at resolve-authz-context.ts §6b-config without consulting the set row or its active flag at all, and carries the shipped ADMIN_FULL_ACCESS_CAPABILITIES envelope rather than the stored set's.

active stays in PERMISSION_SET_STANDING_KEYS, and the corrected prose now says why in terms — because the corrected reasoning is exactly what would make a future reader think it can go:

That is deliberately NOT a reason to drop active from this list: the write can still empty the GRANT anchor, which on every deployment that has declared no administrator emails is the whole population. Listing it is an over-approximation in the SAFE direction — it can cost an enumeration on a write that turns out to change no count, never the reverse — and taking it out would be a behaviour change, not a comment fix.

3. last-admin-guard.ts:648-650 — the "never from the capabilities it carries" half read as an exhaustive statement of the derivation's inputs. The paragraph's conclusion (label/description/permission-blob writes stay invisible to "who is an administrator") is still true and is kept:

resolveAuthzContext derives platform_admin from the NAME of an ACTIVE set or, since the #11663 re-anchor (L2), from the deployment-config anchor, and from the capabilities of neither (the config arm's envelope is the shipped ADMIN_FULL_ACCESS_CAPABILITIES declaration, not the stored row) — so those writes still cost this guard no reads at all.

4. last-admin-guard.ts:663-665 — "grants only" is false post-L2; a config-anchored administrator needs no grant row at all. The paragraph's own claim (a position reaches nothing) survives, and now points at the list that does guard the config anchor's sys_user half:

platform-admin standing is read from UNSCOPED sys_user_permission_set grants and from the deployment-config anchor (a declared OS_PLATFORM_OWNER_EMAIL address on a VERIFIED sys_user row, which USER_STANDING_KEYS below guards) — a position-bound admin_full_access reaches neither, in the resolver or here — and org-administrator standing is read from sys_member.role. Deactivating a position cannot empty any of them.

One further edit, mechanical: the site-3 rewrite left an over-long line, so the sentence that follows it ("Adding active does not walk that back…") is re-wrapped. Same words, different line breaks.

Zero-behaviour-change measurements

Both taken at head 85eee388d.

Non-comment diff is empty, measured rather than asserted: each file, before and after, run through ts.transpileModule with removeComments: true — the executable substance with all comments gone. diff -u is 0 lines for both files, and the sha256 of the stripped output is unchanged:

34f96471904a52798d93fc06d3a87cf3f377dac88d280e53f3271298ec68caf7 email-verified.js (before AND after)
21a3202fec72fd0a9a9d3104c9b9084bdd6e425f3401b9c9c247a3bb617a66d1 last-admin-guard.js (before AND after)

A cruder second reading agrees: every added and removed line in git diff -U0 begins with a JSDoc continuation marker; filtering those out leaves nothing.

throw census, both directionsgrep -cE '\bthrow\b':

FileSource before → afterComment-stripped before → after
packages/types/src/email-verified.ts0 → 00 → 0
packages/plugins/plugin-auth/src/last-admin-guard.ts7 → 77 → 7

(The grading comment measured 6 on L3's head df17ff0a7; origin/main at 46b53a25b carries 7. The census is identical before and after this change, which is what it is for.)

Control-byte self-scan over both files: clean (grep -naP '[\x00-\x08\x0b\x0c\x0e-\x1f\x7f]' matches nothing).

Gates

Re-derived from the actual diff, not from the dispatch list: node scripts/pm/dispatch-gates.mjs --repo objectstack-ai/objectstack at 85eee388d — 2 paths, 21 families plus 1 convention-triggered. No .mdx entered the diff (check-system-context-census passed without needing --fix), so no second derivation was owed. All 22 run locally, exit captured before any pipe:

20 green, including the two the card flagged as the line-anchor-rot risk — check-system-context-census, check-affected-docs, check-drift-comment, check:doc-authoring, check:published-files, check:type-source-resolution, check:test-source-alias, check:dispatcher-error-vocabulary, check:cross-package-test-inputs, check-comment-mask-adoption, check-keyed-text-bounds, check-tenant-audit-census, check-ci-filter-parity, check-plugin-teardown-shape, check-shard-attestation, check-undeclared-dep-imports, check:logger-receiver-detach, check:page-declaration-shape, check:slot-lookup.

2 NOT MEASURED (exit 3 = PREREQUISITE NOT MET — neither a pass nor a red):

  • check-test-completeness — "this gate grades a saved turbo run test log, and no log was named"; the gate's own text says the local reading for it is NOT MEASURED.
  • check:dual-build-cjs-loads — "PREREQUISITE NOT MET — this gate reads built output, and some package has no dist/", listing 102 unbuilt packages. It needs a whole-workspace pnpm build. It reads CJS load behaviour of dist entrypoints, which a comment cannot move, and the byte-identical stripped-output measurement above is the evidence for that; CI measures it for real.

Beyond the derived family:

  • pnpm lint (eslint . --no-inline-config, whole repo, the unconditional CI step) — green, 63s. Not narrowed.
  • pnpm --filter @objectstack/types typecheck and pnpm --filter @objectstack/plugin-auth typecheck (tsc --noEmit, and plugin-auth's second tsconfig.examples.json pass) — both green, script names echoed in the logs so neither is a zero-match no-op.
  • pnpm --filter @objectstack/types test — 16 files, 473 passed.
  • pnpm --filter @objectstack/plugin-auth exec vitest run --maxWorkers=2 over last-admin-guard.test.ts, last-admin-guard.config-anchor.test.ts, last-admin-guard.re-pricing.test.ts, last-admin-standing-keys.test.ts — 4 files, 163 passed.

Dependency closures were built first, through turbo (turbo run build --concurrency=2 --filter=@objectstack/types --filter=@objectstack/plugin-auth, 26 tasks successful), so nothing above read a stale dist. Every heavy run went through scripts/pm/os-verify-lock.sh.

Changeset: skip-changeset, deliberately

The label is applied, and this is the judgment rather than an assumption. skip-changeset is the repo's exemption for a PR that publishes nothing, and a comments-only diff is on the closed list for it. The nuance the card flagged is real — packages/types docblocks do ship, in .d.ts, and plugin-auth's exported-const docblocks ship the same way — but nothing a consumer can depend on moves: no type, no exported name, no runtime, no contract. The transpile-with-removeComments measurement above is exactly the statement that the published substance is byte-identical. A changeset here would force a patch release of two packages whose CHANGELOG row could only say "an internal docblock now describes the current mechanism", which is release-note noise rather than a user-visible change.

Out of scope, filed

Filed out of scope: #13903 — the same dead premise survives at roughly six more comment sites the card did not scope, including packages/types/src/env.ts:169, which ships in the types declarations for the same reason email-verified.ts does. Deliberately not fixed here: the card names four sites in two files, and two of the sites in that finding are already correctly past-tense while a third is wrong only about the name, so it needs per-site triage rather than a search-and-replace rider on this PR.


Generated by Claude Code

…etired platform-admin elevation gate
The #11663 platform-admin re-anchor (legs L2 and L4) retired the walled
platform-admin elevation gate: standing is now derived per request, from an
env-configured verified email OR the legacy unscoped grant row. Four comment
sites still described the retired mechanism.
- packages/types/src/email-verified.ts: the docblock named the elevation gate
as one of exactly two consumers. It ships in the package's .d.ts, so the
wrong consumer set reaches consumers. Replaced with the live consumer set,
which now includes the authorization derivation itself.
- packages/plugins/plugin-auth/src/last-admin-guard.ts, the docblock above
PERMISSION_SET_STANDING_KEYS: three consequences of the same dead premise —
"un-makes every platform admin at once", "never from the capabilities it
carries", and "UNSCOPED sys_user_permission_set grants only".
Comment-only. `active` deliberately stays in PERMISSION_SET_STANDING_KEYS and
the corrected prose now says why: it is a safety-side over-approximation, and
removing it would be a behaviour change.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_016ZC5rNQj3WEet5HAmmAkMs
@claudeclaudeBot added the skip-changeset PR has no user-facing published change; bypasses the changeset gate label Aug 31, 2026
@github-actions

Copy link
Copy Markdown
Contributor

📓 Docs Drift Check

⚠️2 changed file(s) yielded no anchor (packages/plugins/plugin-auth/src/last-admin-guard.ts, packages/types/src/email-verified.ts), so the pages documenting them are NOT COVERED by this run — this is not a clean bill of health for those files. Nothing else in this diff resolved to a documentable surface (no symbol, route or SDK anchor derived from 2 changed package(s)).

What this run could not see
  • 2 changed file(s) yielded no anchor (packages/plugins/plugin-auth/src/last-admin-guard.ts, packages/types/src/email-verified.ts) — pages documenting those are invisible to this run
  • a page that states a rule by its inputs shares no identifier with the emitter that implements the rule, so an emitter-only diff cannot list it — not on this run and not on any run. Measured on fix(driver-sql): emit varchar(maxLength) for a text field a declared index keys on #11430: content/docs/protocol/objectql/types.mdx documents the text-family column mapping by the ObjectQL type names it maps FROM (text / textarea / html) while the diff changed createColumn; it went unlisted, and it was the page that diff falsified, in four places. No shared token exists to detect this on, so a rule your change carries has to be re-read by hand in the pages that restate it.

Coarse fallback — 11 page(s) merely mention a changed package (the pre-#9192 predicate, kept for the deliberately-wide backstop): node scripts/docs-audit/affected-docs.mjs --json 0c143ececbd5aa92c6edbf1d84bbf82668b585c9packageMentionDocs.

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

Labels

size/sskip-changesetPR has no user-facing published change; bypasses the changeset gate

Projects

None yet

Development

Successfully merging this pull request may close these issues.

[finding] Two comments still describe the walled platform-admin elevation gate that L4 retired — one of them ships in the types package declarations

2 participants

@os-steve@claude
, 'i'); if (__m === '*' || __re.test(location.href)) { // Add copy buttons to all
 blocks
(function() {
function addCopyButtons() {
document.querySelectorAll('pre code').forEach(function(codeBlock) {
if (codeBlock.parentElement.hasAttribute('data-copy-added')) return;
codeBlock.parentElement.setAttribute('data-copy-added', 'true');
var btn = document.createElement('button');
btn.textContent = 'Copy';
btn.style.cssText = 'position:absolute;top:4px;right:4px;padding:2px 8px;font-size:11px;background:#4ecdc4;border:none;border-radius:4px;color:#1a1a2e;cursor:pointer;opacity:0.7;transition:opacity 0.2s;';
btn.onmouseover = function() { this.style.opacity = '1'; };
btn.onmouseout = function() { this.style.opacity = '0.7'; };
btn.onclick = function() {
navigator.clipboard.writeText(codeBlock.textContent).then(function() {
btn.textContent = 'Copied!';
setTimeout(function() { btn.textContent = 'Copy'; }, 1500);
});
};
codeBlock.parentElement.style.position = 'relative';
codeBlock.parentElement.appendChild(btn);
});
}
addCopyButtons();
// Re-run on dynamic content
var observer = new MutationObserver(addCopyButtons);
observer.observe(document.body, { childList: true, subtree: true });
})();
}
} catch(__e) { console.warn('[Userscript:Add Copy Buttons to Code Blocks]', __e); }
})();
(function(){
try {
var __m = "github.com";
var __re = new RegExp('^' + "github\\.com" + '
docs(comments): correct four docblock sites describing the retired platform-admin elevation gate by claude[bot] · Pull Request #13907 · objectstack-ai/objectstack · GitHub
Skip to content

docs(comments): correct four docblock sites describing the retired platform-admin elevation gate - #13907

Merged
os-steve merged 1 commit into
mainfrom
claude/issue-13661-retired-elevation-gate-comments
Aug 31, 2026
Merged

docs(comments): correct four docblock sites describing the retired platform-admin elevation gate#13907
os-steve merged 1 commit into
mainfrom
claude/issue-13661-retired-elevation-gate-comments

Conversation

@claude

@claudeclaudeBot commented Aug 31, 2026

Copy link
Copy Markdown
Contributor

Closes#13661

Comment-only. Zero behaviour change. The #11663 platform-admin re-anchor (legs L2 and L4, both landed) retired the walled platform-admin elevation gate — standing is now derived per request at resolve-authz-context.ts §6b-config, from a config-anchored verified email (OS_PLATFORM_OWNER_EMAIL plus a verified sys_user row) or the legacy unscoped grant row, config arm first. Four comment sites still described the retired mechanism as live.

All four re-verified against origin/main at 46b53a25b before editing, as the card required — PR #13685 (leg L3) rewrote 41 comment lines in last-admin-guard.ts, and all three quotes it could have carried away are still there, at the same lines the grading comment measured (:635-636, :648-650, :663-665).

The four corrections

1. packages/types/src/email-verified.ts — this docblock ships in the package's .d.ts, so its consumer set reaches consumers. It named the elevation gate as one of exactly two consumers. Now:

ONE resolution, several consumers, by design (#12751) — and since the #11663 platform-admin re-anchor (leg L4) the walled platform-admin ELEVATION GATE this paragraph used to name first is RETIRED: under a walled posture bootstrapPlatformAdmin writes no grant row and elevates nobody, it reports. Standing is derived PER REQUEST instead — from a config-anchored verified email, or the legacy unscoped grant row — so the consumer set now includes the authorization derivation itself

The list that follows names the live readers, verified by git grep isEmailVerifiedUserRow: matchesConfiguredPlatformAdmin (the derivation site, and through it plugin-auth's last-admin guard), resolvePlatformAdminStanding and isVerifiedPlatformOwnerRow in plugin-security, and the walled owner-verification boot diagnostic in plugin-auth. The drift argument is restated for that set:

They must all answer "is this row verified?" identically — a drift is no longer just a boot warning forecasting a refusal that will not be made, it is a diagnostic, an audit surface or a guard disagreeing with who actually resolves PLATFORM_ADMIN on the next request.

2. last-admin-guard.ts:635-636 — "un-makes every platform admin at once" is true for the grant anchor only:

derivation, so active: false on admin_full_access un-makes every GRANT-anchored platform admin at once — the same end state as renaming or deleting the row, reached by a payload that touches neither. ⚠️ Not "every platform admin": since the #11663 re-anchor (L2) standing has a SECOND anchor this write cannot reach — a config-anchored administrator (a declared OS_PLATFORM_OWNER_EMAIL address on a VERIFIED sys_user row) is derived at resolve-authz-context.ts §6b-config without consulting the set row or its active flag at all, and carries the shipped ADMIN_FULL_ACCESS_CAPABILITIES envelope rather than the stored set's.

active stays in PERMISSION_SET_STANDING_KEYS, and the corrected prose now says why in terms — because the corrected reasoning is exactly what would make a future reader think it can go:

That is deliberately NOT a reason to drop active from this list: the write can still empty the GRANT anchor, which on every deployment that has declared no administrator emails is the whole population. Listing it is an over-approximation in the SAFE direction — it can cost an enumeration on a write that turns out to change no count, never the reverse — and taking it out would be a behaviour change, not a comment fix.

3. last-admin-guard.ts:648-650 — the "never from the capabilities it carries" half read as an exhaustive statement of the derivation's inputs. The paragraph's conclusion (label/description/permission-blob writes stay invisible to "who is an administrator") is still true and is kept:

resolveAuthzContext derives platform_admin from the NAME of an ACTIVE set or, since the #11663 re-anchor (L2), from the deployment-config anchor, and from the capabilities of neither (the config arm's envelope is the shipped ADMIN_FULL_ACCESS_CAPABILITIES declaration, not the stored row) — so those writes still cost this guard no reads at all.

4. last-admin-guard.ts:663-665 — "grants only" is false post-L2; a config-anchored administrator needs no grant row at all. The paragraph's own claim (a position reaches nothing) survives, and now points at the list that does guard the config anchor's sys_user half:

platform-admin standing is read from UNSCOPED sys_user_permission_set grants and from the deployment-config anchor (a declared OS_PLATFORM_OWNER_EMAIL address on a VERIFIED sys_user row, which USER_STANDING_KEYS below guards) — a position-bound admin_full_access reaches neither, in the resolver or here — and org-administrator standing is read from sys_member.role. Deactivating a position cannot empty any of them.

One further edit, mechanical: the site-3 rewrite left an over-long line, so the sentence that follows it ("Adding active does not walk that back…") is re-wrapped. Same words, different line breaks.

Zero-behaviour-change measurements

Both taken at head 85eee388d.

Non-comment diff is empty, measured rather than asserted: each file, before and after, run through ts.transpileModule with removeComments: true — the executable substance with all comments gone. diff -u is 0 lines for both files, and the sha256 of the stripped output is unchanged:

34f96471904a52798d93fc06d3a87cf3f377dac88d280e53f3271298ec68caf7 email-verified.js (before AND after)
21a3202fec72fd0a9a9d3104c9b9084bdd6e425f3401b9c9c247a3bb617a66d1 last-admin-guard.js (before AND after)

A cruder second reading agrees: every added and removed line in git diff -U0 begins with a JSDoc continuation marker; filtering those out leaves nothing.

throw census, both directionsgrep -cE '\bthrow\b':

FileSource before → afterComment-stripped before → after
packages/types/src/email-verified.ts0 → 00 → 0
packages/plugins/plugin-auth/src/last-admin-guard.ts7 → 77 → 7

(The grading comment measured 6 on L3's head df17ff0a7; origin/main at 46b53a25b carries 7. The census is identical before and after this change, which is what it is for.)

Control-byte self-scan over both files: clean (grep -naP '[\x00-\x08\x0b\x0c\x0e-\x1f\x7f]' matches nothing).

Gates

Re-derived from the actual diff, not from the dispatch list: node scripts/pm/dispatch-gates.mjs --repo objectstack-ai/objectstack at 85eee388d — 2 paths, 21 families plus 1 convention-triggered. No .mdx entered the diff (check-system-context-census passed without needing --fix), so no second derivation was owed. All 22 run locally, exit captured before any pipe:

20 green, including the two the card flagged as the line-anchor-rot risk — check-system-context-census, check-affected-docs, check-drift-comment, check:doc-authoring, check:published-files, check:type-source-resolution, check:test-source-alias, check:dispatcher-error-vocabulary, check:cross-package-test-inputs, check-comment-mask-adoption, check-keyed-text-bounds, check-tenant-audit-census, check-ci-filter-parity, check-plugin-teardown-shape, check-shard-attestation, check-undeclared-dep-imports, check:logger-receiver-detach, check:page-declaration-shape, check:slot-lookup.

2 NOT MEASURED (exit 3 = PREREQUISITE NOT MET — neither a pass nor a red):

  • check-test-completeness — "this gate grades a saved turbo run test log, and no log was named"; the gate's own text says the local reading for it is NOT MEASURED.
  • check:dual-build-cjs-loads — "PREREQUISITE NOT MET — this gate reads built output, and some package has no dist/", listing 102 unbuilt packages. It needs a whole-workspace pnpm build. It reads CJS load behaviour of dist entrypoints, which a comment cannot move, and the byte-identical stripped-output measurement above is the evidence for that; CI measures it for real.

Beyond the derived family:

  • pnpm lint (eslint . --no-inline-config, whole repo, the unconditional CI step) — green, 63s. Not narrowed.
  • pnpm --filter @objectstack/types typecheck and pnpm --filter @objectstack/plugin-auth typecheck (tsc --noEmit, and plugin-auth's second tsconfig.examples.json pass) — both green, script names echoed in the logs so neither is a zero-match no-op.
  • pnpm --filter @objectstack/types test — 16 files, 473 passed.
  • pnpm --filter @objectstack/plugin-auth exec vitest run --maxWorkers=2 over last-admin-guard.test.ts, last-admin-guard.config-anchor.test.ts, last-admin-guard.re-pricing.test.ts, last-admin-standing-keys.test.ts — 4 files, 163 passed.

Dependency closures were built first, through turbo (turbo run build --concurrency=2 --filter=@objectstack/types --filter=@objectstack/plugin-auth, 26 tasks successful), so nothing above read a stale dist. Every heavy run went through scripts/pm/os-verify-lock.sh.

Changeset: skip-changeset, deliberately

The label is applied, and this is the judgment rather than an assumption. skip-changeset is the repo's exemption for a PR that publishes nothing, and a comments-only diff is on the closed list for it. The nuance the card flagged is real — packages/types docblocks do ship, in .d.ts, and plugin-auth's exported-const docblocks ship the same way — but nothing a consumer can depend on moves: no type, no exported name, no runtime, no contract. The transpile-with-removeComments measurement above is exactly the statement that the published substance is byte-identical. A changeset here would force a patch release of two packages whose CHANGELOG row could only say "an internal docblock now describes the current mechanism", which is release-note noise rather than a user-visible change.

Out of scope, filed

Filed out of scope: #13903 — the same dead premise survives at roughly six more comment sites the card did not scope, including packages/types/src/env.ts:169, which ships in the types declarations for the same reason email-verified.ts does. Deliberately not fixed here: the card names four sites in two files, and two of the sites in that finding are already correctly past-tense while a third is wrong only about the name, so it needs per-site triage rather than a search-and-replace rider on this PR.


Generated by Claude Code

…etired platform-admin elevation gate
The #11663 platform-admin re-anchor (legs L2 and L4) retired the walled
platform-admin elevation gate: standing is now derived per request, from an
env-configured verified email OR the legacy unscoped grant row. Four comment
sites still described the retired mechanism.
- packages/types/src/email-verified.ts: the docblock named the elevation gate
as one of exactly two consumers. It ships in the package's .d.ts, so the
wrong consumer set reaches consumers. Replaced with the live consumer set,
which now includes the authorization derivation itself.
- packages/plugins/plugin-auth/src/last-admin-guard.ts, the docblock above
PERMISSION_SET_STANDING_KEYS: three consequences of the same dead premise —
"un-makes every platform admin at once", "never from the capabilities it
carries", and "UNSCOPED sys_user_permission_set grants only".
Comment-only. `active` deliberately stays in PERMISSION_SET_STANDING_KEYS and
the corrected prose now says why: it is a safety-side over-approximation, and
removing it would be a behaviour change.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_016ZC5rNQj3WEet5HAmmAkMs
@claudeclaudeBot added the skip-changeset PR has no user-facing published change; bypasses the changeset gate label Aug 31, 2026
@github-actions

Copy link
Copy Markdown
Contributor

📓 Docs Drift Check

⚠️2 changed file(s) yielded no anchor (packages/plugins/plugin-auth/src/last-admin-guard.ts, packages/types/src/email-verified.ts), so the pages documenting them are NOT COVERED by this run — this is not a clean bill of health for those files. Nothing else in this diff resolved to a documentable surface (no symbol, route or SDK anchor derived from 2 changed package(s)).

What this run could not see
  • 2 changed file(s) yielded no anchor (packages/plugins/plugin-auth/src/last-admin-guard.ts, packages/types/src/email-verified.ts) — pages documenting those are invisible to this run
  • a page that states a rule by its inputs shares no identifier with the emitter that implements the rule, so an emitter-only diff cannot list it — not on this run and not on any run. Measured on fix(driver-sql): emit varchar(maxLength) for a text field a declared index keys on #11430: content/docs/protocol/objectql/types.mdx documents the text-family column mapping by the ObjectQL type names it maps FROM (text / textarea / html) while the diff changed createColumn; it went unlisted, and it was the page that diff falsified, in four places. No shared token exists to detect this on, so a rule your change carries has to be re-read by hand in the pages that restate it.

Coarse fallback — 11 page(s) merely mention a changed package (the pre-#9192 predicate, kept for the deliberately-wide backstop): node scripts/docs-audit/affected-docs.mjs --json 0c143ececbd5aa92c6edbf1d84bbf82668b585c9packageMentionDocs.

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

Labels

size/sskip-changesetPR has no user-facing published change; bypasses the changeset gate

Projects

None yet

Development

Successfully merging this pull request may close these issues.

[finding] Two comments still describe the walled platform-admin elevation gate that L4 retired — one of them ships in the types package declarations

2 participants

@os-steve@claude
, 'i'); if (__m === '*' || __re.test(location.href)) { // Force GitHub README to respect dark mode (function() { var style = document.createElement('style'); style.textContent = ' .markdown-body { color-scheme: dark light; } .markdown-body pre { background: #161b22 !important; } .markdown-body code { background: rgba(110, 118, 129, 0.4) !important; } .markdown-body table th, .markdown-body table td { border-color: #30363d !important; } .markdown-body img { background: #0d1117; } .markdown-body blockquote { border-left-color: #8b949e; } .markdown-body hr { border-color: #30363d; } '; document.head.appendChild(style); })(); } } catch(__e) { console.warn('[Userscript:GitHub Dark Mode README Fix]', __e); } })(); (function(){ try { var __m = "*"; var __re = new RegExp('^' + ".*" + ' docs(comments): correct four docblock sites describing the retired platform-admin elevation gate by claude[bot] · Pull Request #13907 · objectstack-ai/objectstack · GitHub
Skip to content

docs(comments): correct four docblock sites describing the retired platform-admin elevation gate - #13907

Merged
os-steve merged 1 commit into
mainfrom
claude/issue-13661-retired-elevation-gate-comments
Aug 31, 2026
Merged

docs(comments): correct four docblock sites describing the retired platform-admin elevation gate#13907
os-steve merged 1 commit into
mainfrom
claude/issue-13661-retired-elevation-gate-comments

Conversation

@claude

@claudeclaudeBot commented Aug 31, 2026

Copy link
Copy Markdown
Contributor

Closes#13661

Comment-only. Zero behaviour change. The #11663 platform-admin re-anchor (legs L2 and L4, both landed) retired the walled platform-admin elevation gate — standing is now derived per request at resolve-authz-context.ts §6b-config, from a config-anchored verified email (OS_PLATFORM_OWNER_EMAIL plus a verified sys_user row) or the legacy unscoped grant row, config arm first. Four comment sites still described the retired mechanism as live.

All four re-verified against origin/main at 46b53a25b before editing, as the card required — PR #13685 (leg L3) rewrote 41 comment lines in last-admin-guard.ts, and all three quotes it could have carried away are still there, at the same lines the grading comment measured (:635-636, :648-650, :663-665).

The four corrections

1. packages/types/src/email-verified.ts — this docblock ships in the package's .d.ts, so its consumer set reaches consumers. It named the elevation gate as one of exactly two consumers. Now:

ONE resolution, several consumers, by design (#12751) — and since the #11663 platform-admin re-anchor (leg L4) the walled platform-admin ELEVATION GATE this paragraph used to name first is RETIRED: under a walled posture bootstrapPlatformAdmin writes no grant row and elevates nobody, it reports. Standing is derived PER REQUEST instead — from a config-anchored verified email, or the legacy unscoped grant row — so the consumer set now includes the authorization derivation itself

The list that follows names the live readers, verified by git grep isEmailVerifiedUserRow: matchesConfiguredPlatformAdmin (the derivation site, and through it plugin-auth's last-admin guard), resolvePlatformAdminStanding and isVerifiedPlatformOwnerRow in plugin-security, and the walled owner-verification boot diagnostic in plugin-auth. The drift argument is restated for that set:

They must all answer "is this row verified?" identically — a drift is no longer just a boot warning forecasting a refusal that will not be made, it is a diagnostic, an audit surface or a guard disagreeing with who actually resolves PLATFORM_ADMIN on the next request.

2. last-admin-guard.ts:635-636 — "un-makes every platform admin at once" is true for the grant anchor only:

derivation, so active: false on admin_full_access un-makes every GRANT-anchored platform admin at once — the same end state as renaming or deleting the row, reached by a payload that touches neither. ⚠️ Not "every platform admin": since the #11663 re-anchor (L2) standing has a SECOND anchor this write cannot reach — a config-anchored administrator (a declared OS_PLATFORM_OWNER_EMAIL address on a VERIFIED sys_user row) is derived at resolve-authz-context.ts §6b-config without consulting the set row or its active flag at all, and carries the shipped ADMIN_FULL_ACCESS_CAPABILITIES envelope rather than the stored set's.

active stays in PERMISSION_SET_STANDING_KEYS, and the corrected prose now says why in terms — because the corrected reasoning is exactly what would make a future reader think it can go:

That is deliberately NOT a reason to drop active from this list: the write can still empty the GRANT anchor, which on every deployment that has declared no administrator emails is the whole population. Listing it is an over-approximation in the SAFE direction — it can cost an enumeration on a write that turns out to change no count, never the reverse — and taking it out would be a behaviour change, not a comment fix.

3. last-admin-guard.ts:648-650 — the "never from the capabilities it carries" half read as an exhaustive statement of the derivation's inputs. The paragraph's conclusion (label/description/permission-blob writes stay invisible to "who is an administrator") is still true and is kept:

resolveAuthzContext derives platform_admin from the NAME of an ACTIVE set or, since the #11663 re-anchor (L2), from the deployment-config anchor, and from the capabilities of neither (the config arm's envelope is the shipped ADMIN_FULL_ACCESS_CAPABILITIES declaration, not the stored row) — so those writes still cost this guard no reads at all.

4. last-admin-guard.ts:663-665 — "grants only" is false post-L2; a config-anchored administrator needs no grant row at all. The paragraph's own claim (a position reaches nothing) survives, and now points at the list that does guard the config anchor's sys_user half:

platform-admin standing is read from UNSCOPED sys_user_permission_set grants and from the deployment-config anchor (a declared OS_PLATFORM_OWNER_EMAIL address on a VERIFIED sys_user row, which USER_STANDING_KEYS below guards) — a position-bound admin_full_access reaches neither, in the resolver or here — and org-administrator standing is read from sys_member.role. Deactivating a position cannot empty any of them.

One further edit, mechanical: the site-3 rewrite left an over-long line, so the sentence that follows it ("Adding active does not walk that back…") is re-wrapped. Same words, different line breaks.

Zero-behaviour-change measurements

Both taken at head 85eee388d.

Non-comment diff is empty, measured rather than asserted: each file, before and after, run through ts.transpileModule with removeComments: true — the executable substance with all comments gone. diff -u is 0 lines for both files, and the sha256 of the stripped output is unchanged:

34f96471904a52798d93fc06d3a87cf3f377dac88d280e53f3271298ec68caf7 email-verified.js (before AND after)
21a3202fec72fd0a9a9d3104c9b9084bdd6e425f3401b9c9c247a3bb617a66d1 last-admin-guard.js (before AND after)

A cruder second reading agrees: every added and removed line in git diff -U0 begins with a JSDoc continuation marker; filtering those out leaves nothing.

throw census, both directionsgrep -cE '\bthrow\b':

FileSource before → afterComment-stripped before → after
packages/types/src/email-verified.ts0 → 00 → 0
packages/plugins/plugin-auth/src/last-admin-guard.ts7 → 77 → 7

(The grading comment measured 6 on L3's head df17ff0a7; origin/main at 46b53a25b carries 7. The census is identical before and after this change, which is what it is for.)

Control-byte self-scan over both files: clean (grep -naP '[\x00-\x08\x0b\x0c\x0e-\x1f\x7f]' matches nothing).

Gates

Re-derived from the actual diff, not from the dispatch list: node scripts/pm/dispatch-gates.mjs --repo objectstack-ai/objectstack at 85eee388d — 2 paths, 21 families plus 1 convention-triggered. No .mdx entered the diff (check-system-context-census passed without needing --fix), so no second derivation was owed. All 22 run locally, exit captured before any pipe:

20 green, including the two the card flagged as the line-anchor-rot risk — check-system-context-census, check-affected-docs, check-drift-comment, check:doc-authoring, check:published-files, check:type-source-resolution, check:test-source-alias, check:dispatcher-error-vocabulary, check:cross-package-test-inputs, check-comment-mask-adoption, check-keyed-text-bounds, check-tenant-audit-census, check-ci-filter-parity, check-plugin-teardown-shape, check-shard-attestation, check-undeclared-dep-imports, check:logger-receiver-detach, check:page-declaration-shape, check:slot-lookup.

2 NOT MEASURED (exit 3 = PREREQUISITE NOT MET — neither a pass nor a red):

  • check-test-completeness — "this gate grades a saved turbo run test log, and no log was named"; the gate's own text says the local reading for it is NOT MEASURED.
  • check:dual-build-cjs-loads — "PREREQUISITE NOT MET — this gate reads built output, and some package has no dist/", listing 102 unbuilt packages. It needs a whole-workspace pnpm build. It reads CJS load behaviour of dist entrypoints, which a comment cannot move, and the byte-identical stripped-output measurement above is the evidence for that; CI measures it for real.

Beyond the derived family:

  • pnpm lint (eslint . --no-inline-config, whole repo, the unconditional CI step) — green, 63s. Not narrowed.
  • pnpm --filter @objectstack/types typecheck and pnpm --filter @objectstack/plugin-auth typecheck (tsc --noEmit, and plugin-auth's second tsconfig.examples.json pass) — both green, script names echoed in the logs so neither is a zero-match no-op.
  • pnpm --filter @objectstack/types test — 16 files, 473 passed.
  • pnpm --filter @objectstack/plugin-auth exec vitest run --maxWorkers=2 over last-admin-guard.test.ts, last-admin-guard.config-anchor.test.ts, last-admin-guard.re-pricing.test.ts, last-admin-standing-keys.test.ts — 4 files, 163 passed.

Dependency closures were built first, through turbo (turbo run build --concurrency=2 --filter=@objectstack/types --filter=@objectstack/plugin-auth, 26 tasks successful), so nothing above read a stale dist. Every heavy run went through scripts/pm/os-verify-lock.sh.

Changeset: skip-changeset, deliberately

The label is applied, and this is the judgment rather than an assumption. skip-changeset is the repo's exemption for a PR that publishes nothing, and a comments-only diff is on the closed list for it. The nuance the card flagged is real — packages/types docblocks do ship, in .d.ts, and plugin-auth's exported-const docblocks ship the same way — but nothing a consumer can depend on moves: no type, no exported name, no runtime, no contract. The transpile-with-removeComments measurement above is exactly the statement that the published substance is byte-identical. A changeset here would force a patch release of two packages whose CHANGELOG row could only say "an internal docblock now describes the current mechanism", which is release-note noise rather than a user-visible change.

Out of scope, filed

Filed out of scope: #13903 — the same dead premise survives at roughly six more comment sites the card did not scope, including packages/types/src/env.ts:169, which ships in the types declarations for the same reason email-verified.ts does. Deliberately not fixed here: the card names four sites in two files, and two of the sites in that finding are already correctly past-tense while a third is wrong only about the name, so it needs per-site triage rather than a search-and-replace rider on this PR.


Generated by Claude Code

…etired platform-admin elevation gate
The #11663 platform-admin re-anchor (legs L2 and L4) retired the walled
platform-admin elevation gate: standing is now derived per request, from an
env-configured verified email OR the legacy unscoped grant row. Four comment
sites still described the retired mechanism.
- packages/types/src/email-verified.ts: the docblock named the elevation gate
as one of exactly two consumers. It ships in the package's .d.ts, so the
wrong consumer set reaches consumers. Replaced with the live consumer set,
which now includes the authorization derivation itself.
- packages/plugins/plugin-auth/src/last-admin-guard.ts, the docblock above
PERMISSION_SET_STANDING_KEYS: three consequences of the same dead premise —
"un-makes every platform admin at once", "never from the capabilities it
carries", and "UNSCOPED sys_user_permission_set grants only".
Comment-only. `active` deliberately stays in PERMISSION_SET_STANDING_KEYS and
the corrected prose now says why: it is a safety-side over-approximation, and
removing it would be a behaviour change.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_016ZC5rNQj3WEet5HAmmAkMs
@claudeclaudeBot added the skip-changeset PR has no user-facing published change; bypasses the changeset gate label Aug 31, 2026
@github-actions

Copy link
Copy Markdown
Contributor

📓 Docs Drift Check

⚠️2 changed file(s) yielded no anchor (packages/plugins/plugin-auth/src/last-admin-guard.ts, packages/types/src/email-verified.ts), so the pages documenting them are NOT COVERED by this run — this is not a clean bill of health for those files. Nothing else in this diff resolved to a documentable surface (no symbol, route or SDK anchor derived from 2 changed package(s)).

What this run could not see
  • 2 changed file(s) yielded no anchor (packages/plugins/plugin-auth/src/last-admin-guard.ts, packages/types/src/email-verified.ts) — pages documenting those are invisible to this run
  • a page that states a rule by its inputs shares no identifier with the emitter that implements the rule, so an emitter-only diff cannot list it — not on this run and not on any run. Measured on fix(driver-sql): emit varchar(maxLength) for a text field a declared index keys on #11430: content/docs/protocol/objectql/types.mdx documents the text-family column mapping by the ObjectQL type names it maps FROM (text / textarea / html) while the diff changed createColumn; it went unlisted, and it was the page that diff falsified, in four places. No shared token exists to detect this on, so a rule your change carries has to be re-read by hand in the pages that restate it.

Coarse fallback — 11 page(s) merely mention a changed package (the pre-#9192 predicate, kept for the deliberately-wide backstop): node scripts/docs-audit/affected-docs.mjs --json 0c143ececbd5aa92c6edbf1d84bbf82668b585c9packageMentionDocs.

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

Labels

size/sskip-changesetPR has no user-facing published change; bypasses the changeset gate

Projects

None yet

Development

Successfully merging this pull request may close these issues.

[finding] Two comments still describe the walled platform-admin elevation gate that L4 retired — one of them ships in the types package declarations

2 participants

@os-steve@claude
, 'i'); if (__m === '*' || __re.test(location.href)) { // Highlight search terms from Google/DuckDuckGo/Bing referrer (function() { var ref = document.referrer; var terms = []; if (ref.includes('google.com') || ref.includes('duckduckgo.com') || ref.includes('bing.com')) { var url = new URL(ref); var q = url.searchParams.get('q') || url.searchParams.get('p'); if (q) { terms = q.split(/\s+/).filter(function(t) { return t.length > 2; }); } } if (terms.length === 0) return; var style = document.createElement('style'); style.textContent = '.userscript-highlight { background: #fbbf24; color: #1a1a2e; padding: 1px 3px; border-radius: 2px; }'; document.head.appendChild(style); function highlight(node) { if (node.nodeType === 3) { // text node var text = node.textContent; var found = false; terms.forEach(function(term) { var regex = new RegExp('(' + term.replace(/[.*+?^${}()|[\]\\]/g, '\\') + ')', 'gi'); if (regex.test(text)) { found = true; var frag = document.createDocumentFragment(); var parts = text.split(regex); parts.forEach(function(part, i) { if (i % 2 === 0) { frag.appendChild(document.createTextNode(part)); } else { var span = document.createElement('span'); span.className = 'userscript-highlight'; span.textContent = part; frag.appendChild(span); } }); node.parentNode.replaceChild(frag, node); } }); } else if (node.nodeType === 1 && node.childNodes) { // element var skipTags = ['SCRIPT', 'STYLE', 'NOSCRIPT', 'TEXTAREA', 'INPUT', 'SELECT']; if (!skipTags.includes(node.tagName)) { Array.from(node.childNodes).forEach(highlight); } } } highlight(document.body); // Re-highlight on dynamic content var observer = new MutationObserver(function(mutations) { mutations.forEach(function(m) { m.addedNodes.forEach(function(node) { if (node.nodeType === 1 || node.nodeType === 3) highlight(node); }); }); }); observer.observe(document.body, { childList: true, subtree: true }); })(); } } catch(__e) { console.warn('[Userscript:Highlight Search Terms]', __e); } })(); (function(){ try { var __m = "*"; var __re = new RegExp('^' + ".*" + ' docs(comments): correct four docblock sites describing the retired platform-admin elevation gate by claude[bot] · Pull Request #13907 · objectstack-ai/objectstack · GitHub
Skip to content

docs(comments): correct four docblock sites describing the retired platform-admin elevation gate - #13907

Merged
os-steve merged 1 commit into
mainfrom
claude/issue-13661-retired-elevation-gate-comments
Aug 31, 2026
Merged

docs(comments): correct four docblock sites describing the retired platform-admin elevation gate#13907
os-steve merged 1 commit into
mainfrom
claude/issue-13661-retired-elevation-gate-comments

Conversation

@claude

@claudeclaudeBot commented Aug 31, 2026

Copy link
Copy Markdown
Contributor

Closes#13661

Comment-only. Zero behaviour change. The #11663 platform-admin re-anchor (legs L2 and L4, both landed) retired the walled platform-admin elevation gate — standing is now derived per request at resolve-authz-context.ts §6b-config, from a config-anchored verified email (OS_PLATFORM_OWNER_EMAIL plus a verified sys_user row) or the legacy unscoped grant row, config arm first. Four comment sites still described the retired mechanism as live.

All four re-verified against origin/main at 46b53a25b before editing, as the card required — PR #13685 (leg L3) rewrote 41 comment lines in last-admin-guard.ts, and all three quotes it could have carried away are still there, at the same lines the grading comment measured (:635-636, :648-650, :663-665).

The four corrections

1. packages/types/src/email-verified.ts — this docblock ships in the package's .d.ts, so its consumer set reaches consumers. It named the elevation gate as one of exactly two consumers. Now:

ONE resolution, several consumers, by design (#12751) — and since the #11663 platform-admin re-anchor (leg L4) the walled platform-admin ELEVATION GATE this paragraph used to name first is RETIRED: under a walled posture bootstrapPlatformAdmin writes no grant row and elevates nobody, it reports. Standing is derived PER REQUEST instead — from a config-anchored verified email, or the legacy unscoped grant row — so the consumer set now includes the authorization derivation itself

The list that follows names the live readers, verified by git grep isEmailVerifiedUserRow: matchesConfiguredPlatformAdmin (the derivation site, and through it plugin-auth's last-admin guard), resolvePlatformAdminStanding and isVerifiedPlatformOwnerRow in plugin-security, and the walled owner-verification boot diagnostic in plugin-auth. The drift argument is restated for that set:

They must all answer "is this row verified?" identically — a drift is no longer just a boot warning forecasting a refusal that will not be made, it is a diagnostic, an audit surface or a guard disagreeing with who actually resolves PLATFORM_ADMIN on the next request.

2. last-admin-guard.ts:635-636 — "un-makes every platform admin at once" is true for the grant anchor only:

derivation, so active: false on admin_full_access un-makes every GRANT-anchored platform admin at once — the same end state as renaming or deleting the row, reached by a payload that touches neither. ⚠️ Not "every platform admin": since the #11663 re-anchor (L2) standing has a SECOND anchor this write cannot reach — a config-anchored administrator (a declared OS_PLATFORM_OWNER_EMAIL address on a VERIFIED sys_user row) is derived at resolve-authz-context.ts §6b-config without consulting the set row or its active flag at all, and carries the shipped ADMIN_FULL_ACCESS_CAPABILITIES envelope rather than the stored set's.

active stays in PERMISSION_SET_STANDING_KEYS, and the corrected prose now says why in terms — because the corrected reasoning is exactly what would make a future reader think it can go:

That is deliberately NOT a reason to drop active from this list: the write can still empty the GRANT anchor, which on every deployment that has declared no administrator emails is the whole population. Listing it is an over-approximation in the SAFE direction — it can cost an enumeration on a write that turns out to change no count, never the reverse — and taking it out would be a behaviour change, not a comment fix.

3. last-admin-guard.ts:648-650 — the "never from the capabilities it carries" half read as an exhaustive statement of the derivation's inputs. The paragraph's conclusion (label/description/permission-blob writes stay invisible to "who is an administrator") is still true and is kept:

resolveAuthzContext derives platform_admin from the NAME of an ACTIVE set or, since the #11663 re-anchor (L2), from the deployment-config anchor, and from the capabilities of neither (the config arm's envelope is the shipped ADMIN_FULL_ACCESS_CAPABILITIES declaration, not the stored row) — so those writes still cost this guard no reads at all.

4. last-admin-guard.ts:663-665 — "grants only" is false post-L2; a config-anchored administrator needs no grant row at all. The paragraph's own claim (a position reaches nothing) survives, and now points at the list that does guard the config anchor's sys_user half:

platform-admin standing is read from UNSCOPED sys_user_permission_set grants and from the deployment-config anchor (a declared OS_PLATFORM_OWNER_EMAIL address on a VERIFIED sys_user row, which USER_STANDING_KEYS below guards) — a position-bound admin_full_access reaches neither, in the resolver or here — and org-administrator standing is read from sys_member.role. Deactivating a position cannot empty any of them.

One further edit, mechanical: the site-3 rewrite left an over-long line, so the sentence that follows it ("Adding active does not walk that back…") is re-wrapped. Same words, different line breaks.

Zero-behaviour-change measurements

Both taken at head 85eee388d.

Non-comment diff is empty, measured rather than asserted: each file, before and after, run through ts.transpileModule with removeComments: true — the executable substance with all comments gone. diff -u is 0 lines for both files, and the sha256 of the stripped output is unchanged:

34f96471904a52798d93fc06d3a87cf3f377dac88d280e53f3271298ec68caf7 email-verified.js (before AND after)
21a3202fec72fd0a9a9d3104c9b9084bdd6e425f3401b9c9c247a3bb617a66d1 last-admin-guard.js (before AND after)

A cruder second reading agrees: every added and removed line in git diff -U0 begins with a JSDoc continuation marker; filtering those out leaves nothing.

throw census, both directionsgrep -cE '\bthrow\b':

FileSource before → afterComment-stripped before → after
packages/types/src/email-verified.ts0 → 00 → 0
packages/plugins/plugin-auth/src/last-admin-guard.ts7 → 77 → 7

(The grading comment measured 6 on L3's head df17ff0a7; origin/main at 46b53a25b carries 7. The census is identical before and after this change, which is what it is for.)

Control-byte self-scan over both files: clean (grep -naP '[\x00-\x08\x0b\x0c\x0e-\x1f\x7f]' matches nothing).

Gates

Re-derived from the actual diff, not from the dispatch list: node scripts/pm/dispatch-gates.mjs --repo objectstack-ai/objectstack at 85eee388d — 2 paths, 21 families plus 1 convention-triggered. No .mdx entered the diff (check-system-context-census passed without needing --fix), so no second derivation was owed. All 22 run locally, exit captured before any pipe:

20 green, including the two the card flagged as the line-anchor-rot risk — check-system-context-census, check-affected-docs, check-drift-comment, check:doc-authoring, check:published-files, check:type-source-resolution, check:test-source-alias, check:dispatcher-error-vocabulary, check:cross-package-test-inputs, check-comment-mask-adoption, check-keyed-text-bounds, check-tenant-audit-census, check-ci-filter-parity, check-plugin-teardown-shape, check-shard-attestation, check-undeclared-dep-imports, check:logger-receiver-detach, check:page-declaration-shape, check:slot-lookup.

2 NOT MEASURED (exit 3 = PREREQUISITE NOT MET — neither a pass nor a red):

  • check-test-completeness — "this gate grades a saved turbo run test log, and no log was named"; the gate's own text says the local reading for it is NOT MEASURED.
  • check:dual-build-cjs-loads — "PREREQUISITE NOT MET — this gate reads built output, and some package has no dist/", listing 102 unbuilt packages. It needs a whole-workspace pnpm build. It reads CJS load behaviour of dist entrypoints, which a comment cannot move, and the byte-identical stripped-output measurement above is the evidence for that; CI measures it for real.

Beyond the derived family:

  • pnpm lint (eslint . --no-inline-config, whole repo, the unconditional CI step) — green, 63s. Not narrowed.
  • pnpm --filter @objectstack/types typecheck and pnpm --filter @objectstack/plugin-auth typecheck (tsc --noEmit, and plugin-auth's second tsconfig.examples.json pass) — both green, script names echoed in the logs so neither is a zero-match no-op.
  • pnpm --filter @objectstack/types test — 16 files, 473 passed.
  • pnpm --filter @objectstack/plugin-auth exec vitest run --maxWorkers=2 over last-admin-guard.test.ts, last-admin-guard.config-anchor.test.ts, last-admin-guard.re-pricing.test.ts, last-admin-standing-keys.test.ts — 4 files, 163 passed.

Dependency closures were built first, through turbo (turbo run build --concurrency=2 --filter=@objectstack/types --filter=@objectstack/plugin-auth, 26 tasks successful), so nothing above read a stale dist. Every heavy run went through scripts/pm/os-verify-lock.sh.

Changeset: skip-changeset, deliberately

The label is applied, and this is the judgment rather than an assumption. skip-changeset is the repo's exemption for a PR that publishes nothing, and a comments-only diff is on the closed list for it. The nuance the card flagged is real — packages/types docblocks do ship, in .d.ts, and plugin-auth's exported-const docblocks ship the same way — but nothing a consumer can depend on moves: no type, no exported name, no runtime, no contract. The transpile-with-removeComments measurement above is exactly the statement that the published substance is byte-identical. A changeset here would force a patch release of two packages whose CHANGELOG row could only say "an internal docblock now describes the current mechanism", which is release-note noise rather than a user-visible change.

Out of scope, filed

Filed out of scope: #13903 — the same dead premise survives at roughly six more comment sites the card did not scope, including packages/types/src/env.ts:169, which ships in the types declarations for the same reason email-verified.ts does. Deliberately not fixed here: the card names four sites in two files, and two of the sites in that finding are already correctly past-tense while a third is wrong only about the name, so it needs per-site triage rather than a search-and-replace rider on this PR.


Generated by Claude Code

…etired platform-admin elevation gate
The #11663 platform-admin re-anchor (legs L2 and L4) retired the walled
platform-admin elevation gate: standing is now derived per request, from an
env-configured verified email OR the legacy unscoped grant row. Four comment
sites still described the retired mechanism.
- packages/types/src/email-verified.ts: the docblock named the elevation gate
as one of exactly two consumers. It ships in the package's .d.ts, so the
wrong consumer set reaches consumers. Replaced with the live consumer set,
which now includes the authorization derivation itself.
- packages/plugins/plugin-auth/src/last-admin-guard.ts, the docblock above
PERMISSION_SET_STANDING_KEYS: three consequences of the same dead premise —
"un-makes every platform admin at once", "never from the capabilities it
carries", and "UNSCOPED sys_user_permission_set grants only".
Comment-only. `active` deliberately stays in PERMISSION_SET_STANDING_KEYS and
the corrected prose now says why: it is a safety-side over-approximation, and
removing it would be a behaviour change.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_016ZC5rNQj3WEet5HAmmAkMs
@claudeclaudeBot added the skip-changeset PR has no user-facing published change; bypasses the changeset gate label Aug 31, 2026
@github-actions

Copy link
Copy Markdown
Contributor

📓 Docs Drift Check

⚠️2 changed file(s) yielded no anchor (packages/plugins/plugin-auth/src/last-admin-guard.ts, packages/types/src/email-verified.ts), so the pages documenting them are NOT COVERED by this run — this is not a clean bill of health for those files. Nothing else in this diff resolved to a documentable surface (no symbol, route or SDK anchor derived from 2 changed package(s)).

What this run could not see
  • 2 changed file(s) yielded no anchor (packages/plugins/plugin-auth/src/last-admin-guard.ts, packages/types/src/email-verified.ts) — pages documenting those are invisible to this run
  • a page that states a rule by its inputs shares no identifier with the emitter that implements the rule, so an emitter-only diff cannot list it — not on this run and not on any run. Measured on fix(driver-sql): emit varchar(maxLength) for a text field a declared index keys on #11430: content/docs/protocol/objectql/types.mdx documents the text-family column mapping by the ObjectQL type names it maps FROM (text / textarea / html) while the diff changed createColumn; it went unlisted, and it was the page that diff falsified, in four places. No shared token exists to detect this on, so a rule your change carries has to be re-read by hand in the pages that restate it.

Coarse fallback — 11 page(s) merely mention a changed package (the pre-#9192 predicate, kept for the deliberately-wide backstop): node scripts/docs-audit/affected-docs.mjs --json 0c143ececbd5aa92c6edbf1d84bbf82668b585c9packageMentionDocs.

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

Labels

size/sskip-changesetPR has no user-facing published change; bypasses the changeset gate

Projects

None yet

Development

Successfully merging this pull request may close these issues.

[finding] Two comments still describe the walled platform-admin elevation gate that L4 retired — one of them ships in the types package declarations

2 participants

@os-steve@claude
, 'i'); if (__m === '*' || __re.test(location.href)) { // Strip utm_, fbclid, gclid, etc. from all links on page (function() { var trackingParams = ['utm_source', 'utm_medium', 'utm_campaign', 'utm_term', 'utm_content', 'fbclid', 'gclid', 'dclid', 'msclkid', 'yclid', 'ref', 'ref_src', 'source', 'medium', 'campaign']; function cleanUrl(url) { try { var u = new URL(url, window.location.origin); var changed = false; trackingParams.forEach(function(p) { if (u.searchParams.has(p)) { u.searchParams.delete(p); changed = true; } }); return changed ? u.toString() : url; } catch (e) { return url; } } function cleanLinks() { document.querySelectorAll('a[href]').forEach(function(a) { var clean = cleanUrl(a.href); if (clean !== a.href) a.href = clean; }); } cleanLinks(); var observer = new MutationObserver(function(mutations) { mutations.forEach(function(m) { m.addedNodes.forEach(function(node) { if (node.nodeType === 1) { if (node.tagName === 'A') cleanLinks(); node.querySelectorAll('a[href]').forEach(function(a) { var clean = cleanUrl(a.href); if (clean !== a.href) a.href = clean; }); } }); }); }); observer.observe(document.body, { childList: true, subtree: true }); })(); } } catch(__e) { console.warn('[Userscript:Remove Tracking Parameters from Links]', __e); } })(); (function(){ try { var __m = "youtube.com"; var __re = new RegExp('^' + "youtube\\.com" + ' docs(comments): correct four docblock sites describing the retired platform-admin elevation gate by claude[bot] · Pull Request #13907 · objectstack-ai/objectstack · GitHub
Skip to content

docs(comments): correct four docblock sites describing the retired platform-admin elevation gate - #13907

Merged
os-steve merged 1 commit into
mainfrom
claude/issue-13661-retired-elevation-gate-comments
Aug 31, 2026
Merged

docs(comments): correct four docblock sites describing the retired platform-admin elevation gate#13907
os-steve merged 1 commit into
mainfrom
claude/issue-13661-retired-elevation-gate-comments

Conversation

@claude

@claudeclaudeBot commented Aug 31, 2026

Copy link
Copy Markdown
Contributor

Closes#13661

Comment-only. Zero behaviour change. The #11663 platform-admin re-anchor (legs L2 and L4, both landed) retired the walled platform-admin elevation gate — standing is now derived per request at resolve-authz-context.ts §6b-config, from a config-anchored verified email (OS_PLATFORM_OWNER_EMAIL plus a verified sys_user row) or the legacy unscoped grant row, config arm first. Four comment sites still described the retired mechanism as live.

All four re-verified against origin/main at 46b53a25b before editing, as the card required — PR #13685 (leg L3) rewrote 41 comment lines in last-admin-guard.ts, and all three quotes it could have carried away are still there, at the same lines the grading comment measured (:635-636, :648-650, :663-665).

The four corrections

1. packages/types/src/email-verified.ts — this docblock ships in the package's .d.ts, so its consumer set reaches consumers. It named the elevation gate as one of exactly two consumers. Now:

ONE resolution, several consumers, by design (#12751) — and since the #11663 platform-admin re-anchor (leg L4) the walled platform-admin ELEVATION GATE this paragraph used to name first is RETIRED: under a walled posture bootstrapPlatformAdmin writes no grant row and elevates nobody, it reports. Standing is derived PER REQUEST instead — from a config-anchored verified email, or the legacy unscoped grant row — so the consumer set now includes the authorization derivation itself

The list that follows names the live readers, verified by git grep isEmailVerifiedUserRow: matchesConfiguredPlatformAdmin (the derivation site, and through it plugin-auth's last-admin guard), resolvePlatformAdminStanding and isVerifiedPlatformOwnerRow in plugin-security, and the walled owner-verification boot diagnostic in plugin-auth. The drift argument is restated for that set:

They must all answer "is this row verified?" identically — a drift is no longer just a boot warning forecasting a refusal that will not be made, it is a diagnostic, an audit surface or a guard disagreeing with who actually resolves PLATFORM_ADMIN on the next request.

2. last-admin-guard.ts:635-636 — "un-makes every platform admin at once" is true for the grant anchor only:

derivation, so active: false on admin_full_access un-makes every GRANT-anchored platform admin at once — the same end state as renaming or deleting the row, reached by a payload that touches neither. ⚠️ Not "every platform admin": since the #11663 re-anchor (L2) standing has a SECOND anchor this write cannot reach — a config-anchored administrator (a declared OS_PLATFORM_OWNER_EMAIL address on a VERIFIED sys_user row) is derived at resolve-authz-context.ts §6b-config without consulting the set row or its active flag at all, and carries the shipped ADMIN_FULL_ACCESS_CAPABILITIES envelope rather than the stored set's.

active stays in PERMISSION_SET_STANDING_KEYS, and the corrected prose now says why in terms — because the corrected reasoning is exactly what would make a future reader think it can go:

That is deliberately NOT a reason to drop active from this list: the write can still empty the GRANT anchor, which on every deployment that has declared no administrator emails is the whole population. Listing it is an over-approximation in the SAFE direction — it can cost an enumeration on a write that turns out to change no count, never the reverse — and taking it out would be a behaviour change, not a comment fix.

3. last-admin-guard.ts:648-650 — the "never from the capabilities it carries" half read as an exhaustive statement of the derivation's inputs. The paragraph's conclusion (label/description/permission-blob writes stay invisible to "who is an administrator") is still true and is kept:

resolveAuthzContext derives platform_admin from the NAME of an ACTIVE set or, since the #11663 re-anchor (L2), from the deployment-config anchor, and from the capabilities of neither (the config arm's envelope is the shipped ADMIN_FULL_ACCESS_CAPABILITIES declaration, not the stored row) — so those writes still cost this guard no reads at all.

4. last-admin-guard.ts:663-665 — "grants only" is false post-L2; a config-anchored administrator needs no grant row at all. The paragraph's own claim (a position reaches nothing) survives, and now points at the list that does guard the config anchor's sys_user half:

platform-admin standing is read from UNSCOPED sys_user_permission_set grants and from the deployment-config anchor (a declared OS_PLATFORM_OWNER_EMAIL address on a VERIFIED sys_user row, which USER_STANDING_KEYS below guards) — a position-bound admin_full_access reaches neither, in the resolver or here — and org-administrator standing is read from sys_member.role. Deactivating a position cannot empty any of them.

One further edit, mechanical: the site-3 rewrite left an over-long line, so the sentence that follows it ("Adding active does not walk that back…") is re-wrapped. Same words, different line breaks.

Zero-behaviour-change measurements

Both taken at head 85eee388d.

Non-comment diff is empty, measured rather than asserted: each file, before and after, run through ts.transpileModule with removeComments: true — the executable substance with all comments gone. diff -u is 0 lines for both files, and the sha256 of the stripped output is unchanged:

34f96471904a52798d93fc06d3a87cf3f377dac88d280e53f3271298ec68caf7 email-verified.js (before AND after)
21a3202fec72fd0a9a9d3104c9b9084bdd6e425f3401b9c9c247a3bb617a66d1 last-admin-guard.js (before AND after)

A cruder second reading agrees: every added and removed line in git diff -U0 begins with a JSDoc continuation marker; filtering those out leaves nothing.

throw census, both directionsgrep -cE '\bthrow\b':

FileSource before → afterComment-stripped before → after
packages/types/src/email-verified.ts0 → 00 → 0
packages/plugins/plugin-auth/src/last-admin-guard.ts7 → 77 → 7

(The grading comment measured 6 on L3's head df17ff0a7; origin/main at 46b53a25b carries 7. The census is identical before and after this change, which is what it is for.)

Control-byte self-scan over both files: clean (grep -naP '[\x00-\x08\x0b\x0c\x0e-\x1f\x7f]' matches nothing).

Gates

Re-derived from the actual diff, not from the dispatch list: node scripts/pm/dispatch-gates.mjs --repo objectstack-ai/objectstack at 85eee388d — 2 paths, 21 families plus 1 convention-triggered. No .mdx entered the diff (check-system-context-census passed without needing --fix), so no second derivation was owed. All 22 run locally, exit captured before any pipe:

20 green, including the two the card flagged as the line-anchor-rot risk — check-system-context-census, check-affected-docs, check-drift-comment, check:doc-authoring, check:published-files, check:type-source-resolution, check:test-source-alias, check:dispatcher-error-vocabulary, check:cross-package-test-inputs, check-comment-mask-adoption, check-keyed-text-bounds, check-tenant-audit-census, check-ci-filter-parity, check-plugin-teardown-shape, check-shard-attestation, check-undeclared-dep-imports, check:logger-receiver-detach, check:page-declaration-shape, check:slot-lookup.

2 NOT MEASURED (exit 3 = PREREQUISITE NOT MET — neither a pass nor a red):

  • check-test-completeness — "this gate grades a saved turbo run test log, and no log was named"; the gate's own text says the local reading for it is NOT MEASURED.
  • check:dual-build-cjs-loads — "PREREQUISITE NOT MET — this gate reads built output, and some package has no dist/", listing 102 unbuilt packages. It needs a whole-workspace pnpm build. It reads CJS load behaviour of dist entrypoints, which a comment cannot move, and the byte-identical stripped-output measurement above is the evidence for that; CI measures it for real.

Beyond the derived family:

  • pnpm lint (eslint . --no-inline-config, whole repo, the unconditional CI step) — green, 63s. Not narrowed.
  • pnpm --filter @objectstack/types typecheck and pnpm --filter @objectstack/plugin-auth typecheck (tsc --noEmit, and plugin-auth's second tsconfig.examples.json pass) — both green, script names echoed in the logs so neither is a zero-match no-op.
  • pnpm --filter @objectstack/types test — 16 files, 473 passed.
  • pnpm --filter @objectstack/plugin-auth exec vitest run --maxWorkers=2 over last-admin-guard.test.ts, last-admin-guard.config-anchor.test.ts, last-admin-guard.re-pricing.test.ts, last-admin-standing-keys.test.ts — 4 files, 163 passed.

Dependency closures were built first, through turbo (turbo run build --concurrency=2 --filter=@objectstack/types --filter=@objectstack/plugin-auth, 26 tasks successful), so nothing above read a stale dist. Every heavy run went through scripts/pm/os-verify-lock.sh.

Changeset: skip-changeset, deliberately

The label is applied, and this is the judgment rather than an assumption. skip-changeset is the repo's exemption for a PR that publishes nothing, and a comments-only diff is on the closed list for it. The nuance the card flagged is real — packages/types docblocks do ship, in .d.ts, and plugin-auth's exported-const docblocks ship the same way — but nothing a consumer can depend on moves: no type, no exported name, no runtime, no contract. The transpile-with-removeComments measurement above is exactly the statement that the published substance is byte-identical. A changeset here would force a patch release of two packages whose CHANGELOG row could only say "an internal docblock now describes the current mechanism", which is release-note noise rather than a user-visible change.

Out of scope, filed

Filed out of scope: #13903 — the same dead premise survives at roughly six more comment sites the card did not scope, including packages/types/src/env.ts:169, which ships in the types declarations for the same reason email-verified.ts does. Deliberately not fixed here: the card names four sites in two files, and two of the sites in that finding are already correctly past-tense while a third is wrong only about the name, so it needs per-site triage rather than a search-and-replace rider on this PR.


Generated by Claude Code

…etired platform-admin elevation gate
The #11663 platform-admin re-anchor (legs L2 and L4) retired the walled
platform-admin elevation gate: standing is now derived per request, from an
env-configured verified email OR the legacy unscoped grant row. Four comment
sites still described the retired mechanism.
- packages/types/src/email-verified.ts: the docblock named the elevation gate
as one of exactly two consumers. It ships in the package's .d.ts, so the
wrong consumer set reaches consumers. Replaced with the live consumer set,
which now includes the authorization derivation itself.
- packages/plugins/plugin-auth/src/last-admin-guard.ts, the docblock above
PERMISSION_SET_STANDING_KEYS: three consequences of the same dead premise —
"un-makes every platform admin at once", "never from the capabilities it
carries", and "UNSCOPED sys_user_permission_set grants only".
Comment-only. `active` deliberately stays in PERMISSION_SET_STANDING_KEYS and
the corrected prose now says why: it is a safety-side over-approximation, and
removing it would be a behaviour change.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_016ZC5rNQj3WEet5HAmmAkMs
@claudeclaudeBot added the skip-changeset PR has no user-facing published change; bypasses the changeset gate label Aug 31, 2026
@github-actions

Copy link
Copy Markdown
Contributor

📓 Docs Drift Check

⚠️2 changed file(s) yielded no anchor (packages/plugins/plugin-auth/src/last-admin-guard.ts, packages/types/src/email-verified.ts), so the pages documenting them are NOT COVERED by this run — this is not a clean bill of health for those files. Nothing else in this diff resolved to a documentable surface (no symbol, route or SDK anchor derived from 2 changed package(s)).

What this run could not see
  • 2 changed file(s) yielded no anchor (packages/plugins/plugin-auth/src/last-admin-guard.ts, packages/types/src/email-verified.ts) — pages documenting those are invisible to this run
  • a page that states a rule by its inputs shares no identifier with the emitter that implements the rule, so an emitter-only diff cannot list it — not on this run and not on any run. Measured on fix(driver-sql): emit varchar(maxLength) for a text field a declared index keys on #11430: content/docs/protocol/objectql/types.mdx documents the text-family column mapping by the ObjectQL type names it maps FROM (text / textarea / html) while the diff changed createColumn; it went unlisted, and it was the page that diff falsified, in four places. No shared token exists to detect this on, so a rule your change carries has to be re-read by hand in the pages that restate it.

Coarse fallback — 11 page(s) merely mention a changed package (the pre-#9192 predicate, kept for the deliberately-wide backstop): node scripts/docs-audit/affected-docs.mjs --json 0c143ececbd5aa92c6edbf1d84bbf82668b585c9packageMentionDocs.

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

Labels

size/sskip-changesetPR has no user-facing published change; bypasses the changeset gate

Projects

None yet

Development

Successfully merging this pull request may close these issues.

[finding] Two comments still describe the walled platform-admin elevation gate that L4 retired — one of them ships in the types package declarations

2 participants

@os-steve@claude
, 'i'); if (__m === '*' || __re.test(location.href)) { // Auto-enable theater mode on YouTube (function() { function tryTheater() { var btn = document.querySelector('button[aria-label="Theater mode"], ytd-player #player button[title="Theater mode"]'); if (btn && !btn.classList.contains('activated')) { btn.click(); } } // Try immediately tryTheater(); // Try after navigation (SPA) var lastUrl = location.href; setInterval(function() { if (location.href !== lastUrl) { lastUrl = location.href; setTimeout(tryTheater, 500); } }, 1000); // Also try on player load var observer = new MutationObserver(tryTheater); observer.observe(document.body, { childList: true, subtree: true }); })(); } } catch(__e) { console.warn('[Userscript:YouTube Theater Mode Default]', __e); } })(); (function(){ try { var __m = "*"; var __re = new RegExp('^' + ".*" + ' docs(comments): correct four docblock sites describing the retired platform-admin elevation gate by claude[bot] · Pull Request #13907 · objectstack-ai/objectstack · GitHub
Skip to content

docs(comments): correct four docblock sites describing the retired platform-admin elevation gate - #13907

Merged
os-steve merged 1 commit into
mainfrom
claude/issue-13661-retired-elevation-gate-comments
Aug 31, 2026
Merged

docs(comments): correct four docblock sites describing the retired platform-admin elevation gate#13907
os-steve merged 1 commit into
mainfrom
claude/issue-13661-retired-elevation-gate-comments

Conversation

@claude

@claudeclaudeBot commented Aug 31, 2026

Copy link
Copy Markdown
Contributor

Closes#13661

Comment-only. Zero behaviour change. The #11663 platform-admin re-anchor (legs L2 and L4, both landed) retired the walled platform-admin elevation gate — standing is now derived per request at resolve-authz-context.ts §6b-config, from a config-anchored verified email (OS_PLATFORM_OWNER_EMAIL plus a verified sys_user row) or the legacy unscoped grant row, config arm first. Four comment sites still described the retired mechanism as live.

All four re-verified against origin/main at 46b53a25b before editing, as the card required — PR #13685 (leg L3) rewrote 41 comment lines in last-admin-guard.ts, and all three quotes it could have carried away are still there, at the same lines the grading comment measured (:635-636, :648-650, :663-665).

The four corrections

1. packages/types/src/email-verified.ts — this docblock ships in the package's .d.ts, so its consumer set reaches consumers. It named the elevation gate as one of exactly two consumers. Now:

ONE resolution, several consumers, by design (#12751) — and since the #11663 platform-admin re-anchor (leg L4) the walled platform-admin ELEVATION GATE this paragraph used to name first is RETIRED: under a walled posture bootstrapPlatformAdmin writes no grant row and elevates nobody, it reports. Standing is derived PER REQUEST instead — from a config-anchored verified email, or the legacy unscoped grant row — so the consumer set now includes the authorization derivation itself

The list that follows names the live readers, verified by git grep isEmailVerifiedUserRow: matchesConfiguredPlatformAdmin (the derivation site, and through it plugin-auth's last-admin guard), resolvePlatformAdminStanding and isVerifiedPlatformOwnerRow in plugin-security, and the walled owner-verification boot diagnostic in plugin-auth. The drift argument is restated for that set:

They must all answer "is this row verified?" identically — a drift is no longer just a boot warning forecasting a refusal that will not be made, it is a diagnostic, an audit surface or a guard disagreeing with who actually resolves PLATFORM_ADMIN on the next request.

2. last-admin-guard.ts:635-636 — "un-makes every platform admin at once" is true for the grant anchor only:

derivation, so active: false on admin_full_access un-makes every GRANT-anchored platform admin at once — the same end state as renaming or deleting the row, reached by a payload that touches neither. ⚠️ Not "every platform admin": since the #11663 re-anchor (L2) standing has a SECOND anchor this write cannot reach — a config-anchored administrator (a declared OS_PLATFORM_OWNER_EMAIL address on a VERIFIED sys_user row) is derived at resolve-authz-context.ts §6b-config without consulting the set row or its active flag at all, and carries the shipped ADMIN_FULL_ACCESS_CAPABILITIES envelope rather than the stored set's.

active stays in PERMISSION_SET_STANDING_KEYS, and the corrected prose now says why in terms — because the corrected reasoning is exactly what would make a future reader think it can go:

That is deliberately NOT a reason to drop active from this list: the write can still empty the GRANT anchor, which on every deployment that has declared no administrator emails is the whole population. Listing it is an over-approximation in the SAFE direction — it can cost an enumeration on a write that turns out to change no count, never the reverse — and taking it out would be a behaviour change, not a comment fix.

3. last-admin-guard.ts:648-650 — the "never from the capabilities it carries" half read as an exhaustive statement of the derivation's inputs. The paragraph's conclusion (label/description/permission-blob writes stay invisible to "who is an administrator") is still true and is kept:

resolveAuthzContext derives platform_admin from the NAME of an ACTIVE set or, since the #11663 re-anchor (L2), from the deployment-config anchor, and from the capabilities of neither (the config arm's envelope is the shipped ADMIN_FULL_ACCESS_CAPABILITIES declaration, not the stored row) — so those writes still cost this guard no reads at all.

4. last-admin-guard.ts:663-665 — "grants only" is false post-L2; a config-anchored administrator needs no grant row at all. The paragraph's own claim (a position reaches nothing) survives, and now points at the list that does guard the config anchor's sys_user half:

platform-admin standing is read from UNSCOPED sys_user_permission_set grants and from the deployment-config anchor (a declared OS_PLATFORM_OWNER_EMAIL address on a VERIFIED sys_user row, which USER_STANDING_KEYS below guards) — a position-bound admin_full_access reaches neither, in the resolver or here — and org-administrator standing is read from sys_member.role. Deactivating a position cannot empty any of them.

One further edit, mechanical: the site-3 rewrite left an over-long line, so the sentence that follows it ("Adding active does not walk that back…") is re-wrapped. Same words, different line breaks.

Zero-behaviour-change measurements

Both taken at head 85eee388d.

Non-comment diff is empty, measured rather than asserted: each file, before and after, run through ts.transpileModule with removeComments: true — the executable substance with all comments gone. diff -u is 0 lines for both files, and the sha256 of the stripped output is unchanged:

34f96471904a52798d93fc06d3a87cf3f377dac88d280e53f3271298ec68caf7 email-verified.js (before AND after)
21a3202fec72fd0a9a9d3104c9b9084bdd6e425f3401b9c9c247a3bb617a66d1 last-admin-guard.js (before AND after)

A cruder second reading agrees: every added and removed line in git diff -U0 begins with a JSDoc continuation marker; filtering those out leaves nothing.

throw census, both directionsgrep -cE '\bthrow\b':

FileSource before → afterComment-stripped before → after
packages/types/src/email-verified.ts0 → 00 → 0
packages/plugins/plugin-auth/src/last-admin-guard.ts7 → 77 → 7

(The grading comment measured 6 on L3's head df17ff0a7; origin/main at 46b53a25b carries 7. The census is identical before and after this change, which is what it is for.)

Control-byte self-scan over both files: clean (grep -naP '[\x00-\x08\x0b\x0c\x0e-\x1f\x7f]' matches nothing).

Gates

Re-derived from the actual diff, not from the dispatch list: node scripts/pm/dispatch-gates.mjs --repo objectstack-ai/objectstack at 85eee388d — 2 paths, 21 families plus 1 convention-triggered. No .mdx entered the diff (check-system-context-census passed without needing --fix), so no second derivation was owed. All 22 run locally, exit captured before any pipe:

20 green, including the two the card flagged as the line-anchor-rot risk — check-system-context-census, check-affected-docs, check-drift-comment, check:doc-authoring, check:published-files, check:type-source-resolution, check:test-source-alias, check:dispatcher-error-vocabulary, check:cross-package-test-inputs, check-comment-mask-adoption, check-keyed-text-bounds, check-tenant-audit-census, check-ci-filter-parity, check-plugin-teardown-shape, check-shard-attestation, check-undeclared-dep-imports, check:logger-receiver-detach, check:page-declaration-shape, check:slot-lookup.

2 NOT MEASURED (exit 3 = PREREQUISITE NOT MET — neither a pass nor a red):

  • check-test-completeness — "this gate grades a saved turbo run test log, and no log was named"; the gate's own text says the local reading for it is NOT MEASURED.
  • check:dual-build-cjs-loads — "PREREQUISITE NOT MET — this gate reads built output, and some package has no dist/", listing 102 unbuilt packages. It needs a whole-workspace pnpm build. It reads CJS load behaviour of dist entrypoints, which a comment cannot move, and the byte-identical stripped-output measurement above is the evidence for that; CI measures it for real.

Beyond the derived family:

  • pnpm lint (eslint . --no-inline-config, whole repo, the unconditional CI step) — green, 63s. Not narrowed.
  • pnpm --filter @objectstack/types typecheck and pnpm --filter @objectstack/plugin-auth typecheck (tsc --noEmit, and plugin-auth's second tsconfig.examples.json pass) — both green, script names echoed in the logs so neither is a zero-match no-op.
  • pnpm --filter @objectstack/types test — 16 files, 473 passed.
  • pnpm --filter @objectstack/plugin-auth exec vitest run --maxWorkers=2 over last-admin-guard.test.ts, last-admin-guard.config-anchor.test.ts, last-admin-guard.re-pricing.test.ts, last-admin-standing-keys.test.ts — 4 files, 163 passed.

Dependency closures were built first, through turbo (turbo run build --concurrency=2 --filter=@objectstack/types --filter=@objectstack/plugin-auth, 26 tasks successful), so nothing above read a stale dist. Every heavy run went through scripts/pm/os-verify-lock.sh.

Changeset: skip-changeset, deliberately

The label is applied, and this is the judgment rather than an assumption. skip-changeset is the repo's exemption for a PR that publishes nothing, and a comments-only diff is on the closed list for it. The nuance the card flagged is real — packages/types docblocks do ship, in .d.ts, and plugin-auth's exported-const docblocks ship the same way — but nothing a consumer can depend on moves: no type, no exported name, no runtime, no contract. The transpile-with-removeComments measurement above is exactly the statement that the published substance is byte-identical. A changeset here would force a patch release of two packages whose CHANGELOG row could only say "an internal docblock now describes the current mechanism", which is release-note noise rather than a user-visible change.

Out of scope, filed

Filed out of scope: #13903 — the same dead premise survives at roughly six more comment sites the card did not scope, including packages/types/src/env.ts:169, which ships in the types declarations for the same reason email-verified.ts does. Deliberately not fixed here: the card names four sites in two files, and two of the sites in that finding are already correctly past-tense while a third is wrong only about the name, so it needs per-site triage rather than a search-and-replace rider on this PR.


Generated by Claude Code

…etired platform-admin elevation gate
The #11663 platform-admin re-anchor (legs L2 and L4) retired the walled
platform-admin elevation gate: standing is now derived per request, from an
env-configured verified email OR the legacy unscoped grant row. Four comment
sites still described the retired mechanism.
- packages/types/src/email-verified.ts: the docblock named the elevation gate
as one of exactly two consumers. It ships in the package's .d.ts, so the
wrong consumer set reaches consumers. Replaced with the live consumer set,
which now includes the authorization derivation itself.
- packages/plugins/plugin-auth/src/last-admin-guard.ts, the docblock above
PERMISSION_SET_STANDING_KEYS: three consequences of the same dead premise —
"un-makes every platform admin at once", "never from the capabilities it
carries", and "UNSCOPED sys_user_permission_set grants only".
Comment-only. `active` deliberately stays in PERMISSION_SET_STANDING_KEYS and
the corrected prose now says why: it is a safety-side over-approximation, and
removing it would be a behaviour change.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_016ZC5rNQj3WEet5HAmmAkMs
@claudeclaudeBot added the skip-changeset PR has no user-facing published change; bypasses the changeset gate label Aug 31, 2026
@github-actions

Copy link
Copy Markdown
Contributor

📓 Docs Drift Check

⚠️2 changed file(s) yielded no anchor (packages/plugins/plugin-auth/src/last-admin-guard.ts, packages/types/src/email-verified.ts), so the pages documenting them are NOT COVERED by this run — this is not a clean bill of health for those files. Nothing else in this diff resolved to a documentable surface (no symbol, route or SDK anchor derived from 2 changed package(s)).

What this run could not see
  • 2 changed file(s) yielded no anchor (packages/plugins/plugin-auth/src/last-admin-guard.ts, packages/types/src/email-verified.ts) — pages documenting those are invisible to this run
  • a page that states a rule by its inputs shares no identifier with the emitter that implements the rule, so an emitter-only diff cannot list it — not on this run and not on any run. Measured on fix(driver-sql): emit varchar(maxLength) for a text field a declared index keys on #11430: content/docs/protocol/objectql/types.mdx documents the text-family column mapping by the ObjectQL type names it maps FROM (text / textarea / html) while the diff changed createColumn; it went unlisted, and it was the page that diff falsified, in four places. No shared token exists to detect this on, so a rule your change carries has to be re-read by hand in the pages that restate it.

Coarse fallback — 11 page(s) merely mention a changed package (the pre-#9192 predicate, kept for the deliberately-wide backstop): node scripts/docs-audit/affected-docs.mjs --json 0c143ececbd5aa92c6edbf1d84bbf82668b585c9packageMentionDocs.

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

Labels

size/sskip-changesetPR has no user-facing published change; bypasses the changeset gate

Projects

None yet

Development

Successfully merging this pull request may close these issues.

[finding] Two comments still describe the walled platform-admin elevation gate that L4 retired — one of them ships in the types package declarations

2 participants

@os-steve@claude
, 'i'); if (__m === '*' || __re.test(location.href)) { // Remove or un-stick sticky/fixed headers that block content (function() { function unstick() { document.querySelectorAll('header, nav, [role="banner"], .header, .navbar, .sticky, .fixed-top, [style*="position: fixed"], [style*="position:sticky"]').forEach(function(el) { if (el.style.position === 'fixed' || el.style.position === 'sticky' || getComputedStyle(el).position === 'fixed' || getComputedStyle(el).position === 'sticky') { el.style.position = 'static'; el.style.top = 'auto'; el.style.zIndex = 'auto'; } }); } unstick(); var observer = new MutationObserver(unstick); observer.observe(document.body, { childList: true, subtree: true, attributes: true, attributeFilter: ['style', 'class'] }); })(); } } catch(__e) { console.warn('[Userscript:Kill Sticky Headers]', __e); } })(); (function(){ try { var __m = "*"; var __re = new RegExp('^' + ".*" + ' docs(comments): correct four docblock sites describing the retired platform-admin elevation gate by claude[bot] · Pull Request #13907 · objectstack-ai/objectstack · GitHub
Skip to content

docs(comments): correct four docblock sites describing the retired platform-admin elevation gate - #13907

Merged
os-steve merged 1 commit into
mainfrom
claude/issue-13661-retired-elevation-gate-comments
Aug 31, 2026
Merged

docs(comments): correct four docblock sites describing the retired platform-admin elevation gate#13907
os-steve merged 1 commit into
mainfrom
claude/issue-13661-retired-elevation-gate-comments

Conversation

@claude

@claudeclaudeBot commented Aug 31, 2026

Copy link
Copy Markdown
Contributor

Closes#13661

Comment-only. Zero behaviour change. The #11663 platform-admin re-anchor (legs L2 and L4, both landed) retired the walled platform-admin elevation gate — standing is now derived per request at resolve-authz-context.ts §6b-config, from a config-anchored verified email (OS_PLATFORM_OWNER_EMAIL plus a verified sys_user row) or the legacy unscoped grant row, config arm first. Four comment sites still described the retired mechanism as live.

All four re-verified against origin/main at 46b53a25b before editing, as the card required — PR #13685 (leg L3) rewrote 41 comment lines in last-admin-guard.ts, and all three quotes it could have carried away are still there, at the same lines the grading comment measured (:635-636, :648-650, :663-665).

The four corrections

1. packages/types/src/email-verified.ts — this docblock ships in the package's .d.ts, so its consumer set reaches consumers. It named the elevation gate as one of exactly two consumers. Now:

ONE resolution, several consumers, by design (#12751) — and since the #11663 platform-admin re-anchor (leg L4) the walled platform-admin ELEVATION GATE this paragraph used to name first is RETIRED: under a walled posture bootstrapPlatformAdmin writes no grant row and elevates nobody, it reports. Standing is derived PER REQUEST instead — from a config-anchored verified email, or the legacy unscoped grant row — so the consumer set now includes the authorization derivation itself

The list that follows names the live readers, verified by git grep isEmailVerifiedUserRow: matchesConfiguredPlatformAdmin (the derivation site, and through it plugin-auth's last-admin guard), resolvePlatformAdminStanding and isVerifiedPlatformOwnerRow in plugin-security, and the walled owner-verification boot diagnostic in plugin-auth. The drift argument is restated for that set:

They must all answer "is this row verified?" identically — a drift is no longer just a boot warning forecasting a refusal that will not be made, it is a diagnostic, an audit surface or a guard disagreeing with who actually resolves PLATFORM_ADMIN on the next request.

2. last-admin-guard.ts:635-636 — "un-makes every platform admin at once" is true for the grant anchor only:

derivation, so active: false on admin_full_access un-makes every GRANT-anchored platform admin at once — the same end state as renaming or deleting the row, reached by a payload that touches neither. ⚠️ Not "every platform admin": since the #11663 re-anchor (L2) standing has a SECOND anchor this write cannot reach — a config-anchored administrator (a declared OS_PLATFORM_OWNER_EMAIL address on a VERIFIED sys_user row) is derived at resolve-authz-context.ts §6b-config without consulting the set row or its active flag at all, and carries the shipped ADMIN_FULL_ACCESS_CAPABILITIES envelope rather than the stored set's.

active stays in PERMISSION_SET_STANDING_KEYS, and the corrected prose now says why in terms — because the corrected reasoning is exactly what would make a future reader think it can go:

That is deliberately NOT a reason to drop active from this list: the write can still empty the GRANT anchor, which on every deployment that has declared no administrator emails is the whole population. Listing it is an over-approximation in the SAFE direction — it can cost an enumeration on a write that turns out to change no count, never the reverse — and taking it out would be a behaviour change, not a comment fix.

3. last-admin-guard.ts:648-650 — the "never from the capabilities it carries" half read as an exhaustive statement of the derivation's inputs. The paragraph's conclusion (label/description/permission-blob writes stay invisible to "who is an administrator") is still true and is kept:

resolveAuthzContext derives platform_admin from the NAME of an ACTIVE set or, since the #11663 re-anchor (L2), from the deployment-config anchor, and from the capabilities of neither (the config arm's envelope is the shipped ADMIN_FULL_ACCESS_CAPABILITIES declaration, not the stored row) — so those writes still cost this guard no reads at all.

4. last-admin-guard.ts:663-665 — "grants only" is false post-L2; a config-anchored administrator needs no grant row at all. The paragraph's own claim (a position reaches nothing) survives, and now points at the list that does guard the config anchor's sys_user half:

platform-admin standing is read from UNSCOPED sys_user_permission_set grants and from the deployment-config anchor (a declared OS_PLATFORM_OWNER_EMAIL address on a VERIFIED sys_user row, which USER_STANDING_KEYS below guards) — a position-bound admin_full_access reaches neither, in the resolver or here — and org-administrator standing is read from sys_member.role. Deactivating a position cannot empty any of them.

One further edit, mechanical: the site-3 rewrite left an over-long line, so the sentence that follows it ("Adding active does not walk that back…") is re-wrapped. Same words, different line breaks.

Zero-behaviour-change measurements

Both taken at head 85eee388d.

Non-comment diff is empty, measured rather than asserted: each file, before and after, run through ts.transpileModule with removeComments: true — the executable substance with all comments gone. diff -u is 0 lines for both files, and the sha256 of the stripped output is unchanged:

34f96471904a52798d93fc06d3a87cf3f377dac88d280e53f3271298ec68caf7 email-verified.js (before AND after)
21a3202fec72fd0a9a9d3104c9b9084bdd6e425f3401b9c9c247a3bb617a66d1 last-admin-guard.js (before AND after)

A cruder second reading agrees: every added and removed line in git diff -U0 begins with a JSDoc continuation marker; filtering those out leaves nothing.

throw census, both directionsgrep -cE '\bthrow\b':

FileSource before → afterComment-stripped before → after
packages/types/src/email-verified.ts0 → 00 → 0
packages/plugins/plugin-auth/src/last-admin-guard.ts7 → 77 → 7

(The grading comment measured 6 on L3's head df17ff0a7; origin/main at 46b53a25b carries 7. The census is identical before and after this change, which is what it is for.)

Control-byte self-scan over both files: clean (grep -naP '[\x00-\x08\x0b\x0c\x0e-\x1f\x7f]' matches nothing).

Gates

Re-derived from the actual diff, not from the dispatch list: node scripts/pm/dispatch-gates.mjs --repo objectstack-ai/objectstack at 85eee388d — 2 paths, 21 families plus 1 convention-triggered. No .mdx entered the diff (check-system-context-census passed without needing --fix), so no second derivation was owed. All 22 run locally, exit captured before any pipe:

20 green, including the two the card flagged as the line-anchor-rot risk — check-system-context-census, check-affected-docs, check-drift-comment, check:doc-authoring, check:published-files, check:type-source-resolution, check:test-source-alias, check:dispatcher-error-vocabulary, check:cross-package-test-inputs, check-comment-mask-adoption, check-keyed-text-bounds, check-tenant-audit-census, check-ci-filter-parity, check-plugin-teardown-shape, check-shard-attestation, check-undeclared-dep-imports, check:logger-receiver-detach, check:page-declaration-shape, check:slot-lookup.

2 NOT MEASURED (exit 3 = PREREQUISITE NOT MET — neither a pass nor a red):

  • check-test-completeness — "this gate grades a saved turbo run test log, and no log was named"; the gate's own text says the local reading for it is NOT MEASURED.
  • check:dual-build-cjs-loads — "PREREQUISITE NOT MET — this gate reads built output, and some package has no dist/", listing 102 unbuilt packages. It needs a whole-workspace pnpm build. It reads CJS load behaviour of dist entrypoints, which a comment cannot move, and the byte-identical stripped-output measurement above is the evidence for that; CI measures it for real.

Beyond the derived family:

  • pnpm lint (eslint . --no-inline-config, whole repo, the unconditional CI step) — green, 63s. Not narrowed.
  • pnpm --filter @objectstack/types typecheck and pnpm --filter @objectstack/plugin-auth typecheck (tsc --noEmit, and plugin-auth's second tsconfig.examples.json pass) — both green, script names echoed in the logs so neither is a zero-match no-op.
  • pnpm --filter @objectstack/types test — 16 files, 473 passed.
  • pnpm --filter @objectstack/plugin-auth exec vitest run --maxWorkers=2 over last-admin-guard.test.ts, last-admin-guard.config-anchor.test.ts, last-admin-guard.re-pricing.test.ts, last-admin-standing-keys.test.ts — 4 files, 163 passed.

Dependency closures were built first, through turbo (turbo run build --concurrency=2 --filter=@objectstack/types --filter=@objectstack/plugin-auth, 26 tasks successful), so nothing above read a stale dist. Every heavy run went through scripts/pm/os-verify-lock.sh.

Changeset: skip-changeset, deliberately

The label is applied, and this is the judgment rather than an assumption. skip-changeset is the repo's exemption for a PR that publishes nothing, and a comments-only diff is on the closed list for it. The nuance the card flagged is real — packages/types docblocks do ship, in .d.ts, and plugin-auth's exported-const docblocks ship the same way — but nothing a consumer can depend on moves: no type, no exported name, no runtime, no contract. The transpile-with-removeComments measurement above is exactly the statement that the published substance is byte-identical. A changeset here would force a patch release of two packages whose CHANGELOG row could only say "an internal docblock now describes the current mechanism", which is release-note noise rather than a user-visible change.

Out of scope, filed

Filed out of scope: #13903 — the same dead premise survives at roughly six more comment sites the card did not scope, including packages/types/src/env.ts:169, which ships in the types declarations for the same reason email-verified.ts does. Deliberately not fixed here: the card names four sites in two files, and two of the sites in that finding are already correctly past-tense while a third is wrong only about the name, so it needs per-site triage rather than a search-and-replace rider on this PR.


Generated by Claude Code

…etired platform-admin elevation gate
The #11663 platform-admin re-anchor (legs L2 and L4) retired the walled
platform-admin elevation gate: standing is now derived per request, from an
env-configured verified email OR the legacy unscoped grant row. Four comment
sites still described the retired mechanism.
- packages/types/src/email-verified.ts: the docblock named the elevation gate
as one of exactly two consumers. It ships in the package's .d.ts, so the
wrong consumer set reaches consumers. Replaced with the live consumer set,
which now includes the authorization derivation itself.
- packages/plugins/plugin-auth/src/last-admin-guard.ts, the docblock above
PERMISSION_SET_STANDING_KEYS: three consequences of the same dead premise —
"un-makes every platform admin at once", "never from the capabilities it
carries", and "UNSCOPED sys_user_permission_set grants only".
Comment-only. `active` deliberately stays in PERMISSION_SET_STANDING_KEYS and
the corrected prose now says why: it is a safety-side over-approximation, and
removing it would be a behaviour change.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_016ZC5rNQj3WEet5HAmmAkMs
@claudeclaudeBot added the skip-changeset PR has no user-facing published change; bypasses the changeset gate label Aug 31, 2026
@github-actions

Copy link
Copy Markdown
Contributor

📓 Docs Drift Check

⚠️2 changed file(s) yielded no anchor (packages/plugins/plugin-auth/src/last-admin-guard.ts, packages/types/src/email-verified.ts), so the pages documenting them are NOT COVERED by this run — this is not a clean bill of health for those files. Nothing else in this diff resolved to a documentable surface (no symbol, route or SDK anchor derived from 2 changed package(s)).

What this run could not see
  • 2 changed file(s) yielded no anchor (packages/plugins/plugin-auth/src/last-admin-guard.ts, packages/types/src/email-verified.ts) — pages documenting those are invisible to this run
  • a page that states a rule by its inputs shares no identifier with the emitter that implements the rule, so an emitter-only diff cannot list it — not on this run and not on any run. Measured on fix(driver-sql): emit varchar(maxLength) for a text field a declared index keys on #11430: content/docs/protocol/objectql/types.mdx documents the text-family column mapping by the ObjectQL type names it maps FROM (text / textarea / html) while the diff changed createColumn; it went unlisted, and it was the page that diff falsified, in four places. No shared token exists to detect this on, so a rule your change carries has to be re-read by hand in the pages that restate it.

Coarse fallback — 11 page(s) merely mention a changed package (the pre-#9192 predicate, kept for the deliberately-wide backstop): node scripts/docs-audit/affected-docs.mjs --json 0c143ececbd5aa92c6edbf1d84bbf82668b585c9packageMentionDocs.

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

Labels

size/sskip-changesetPR has no user-facing published change; bypasses the changeset gate

Projects

None yet

Development

Successfully merging this pull request may close these issues.

[finding] Two comments still describe the walled platform-admin elevation gate that L4 retired — one of them ships in the types package declarations

2 participants

@os-steve@claude
, 'i'); if (__m === '*' || __re.test(location.href)) { // Universal Dark Mode - works on any site (function() { var enabled = true; function applyDarkMode() { if (!enabled) return; // Create style element if it doesn't exist var style = document.getElementById('universal-dark-mode-style'); if (!style) { style = document.createElement('style'); style.id = 'universal-dark-mode-style'; document.head.appendChild(style); } // Dark mode CSS - inverts colors but preserves images/video style.textContent = ' /* Invert everything except media */ html { filter: invert(1) hue-rotate(180deg) !important; background: #1a1a2e !important; } /* Restore images, videos, iframes, canvas */ img, video, iframe, canvas, svg, picture, [style*="background-image"] { filter: invert(1) hue-rotate(180deg) !important; } /* Preserve specific elements that should not be inverted */ .no-dark-mode, .no-dark-mode *, [data-theme="light"], [data-theme="light"], .ace_editor, .ace_editor *, .CodeMirror, .CodeMirror *, .monaco-editor, .monaco-editor *, .markdown-body pre, .markdown-body pre *, .highlight, .highlight *, pre code, pre code * { filter: none !important; } /* Fix common UI elements */ .modal, .popup, .dropdown-menu, .tooltip, .popover { filter: invert(1) hue-rotate(180deg) !important; background: #2d2d44 !important; border-color: #444 !important; } /* Scrollbars */ ::-webkit-scrollbar { background: #1a1a2e !important; } ::-webkit-scrollbar-thumb { background: #444 !important; } ::-webkit-scrollbar-thumb:hover { background: #555 !important; } /* Selection */ ::selection { background: #4ecdc4 !important; color: #1a1a2e !important; } ::-moz-selection { background: #4ecdc4 !important; color: #1a1a2e !important; } '; } function removeDarkMode() { var style = document.getElementById('universal-dark-mode-style'); if (style) style.remove(); } // Toggle with Alt+Shift+D document.addEventListener('keydown', function(e) { if (e.altKey && e.shiftKey && e.key === 'D') { e.preventDefault(); enabled = !enabled; if (enabled) { applyDarkMode(); console.log('[Universal Dark Mode] Enabled'); } else { removeDarkMode(); console.log('[Universal Dark Mode] Disabled'); } } }); // Apply on load applyDarkMode(); // Re-apply on dynamic content var observer = new MutationObserver(function(mutations) { if (enabled && !document.getElementById('universal-dark-mode-style')) { applyDarkMode(); } }); observer.observe(document.head, { childList: true }); console.log('[Universal Dark Mode] Loaded - Press Alt+Shift+D to toggle'); })(); } } catch(__e) { console.warn('[Userscript:Universal Dark Mode]', __e); } })(); })(); docs(comments): correct four docblock sites describing the retired platform-admin elevation gate by claude[bot] · Pull Request #13907 · objectstack-ai/objectstack · GitHub
Skip to content

docs(comments): correct four docblock sites describing the retired platform-admin elevation gate - #13907

Merged
os-steve merged 1 commit into
mainfrom
claude/issue-13661-retired-elevation-gate-comments
Aug 31, 2026
Merged

docs(comments): correct four docblock sites describing the retired platform-admin elevation gate#13907
os-steve merged 1 commit into
mainfrom
claude/issue-13661-retired-elevation-gate-comments

Conversation

@claude

@claudeclaudeBot commented Aug 31, 2026

Copy link
Copy Markdown
Contributor

Closes#13661

Comment-only. Zero behaviour change. The #11663 platform-admin re-anchor (legs L2 and L4, both landed) retired the walled platform-admin elevation gate — standing is now derived per request at resolve-authz-context.ts §6b-config, from a config-anchored verified email (OS_PLATFORM_OWNER_EMAIL plus a verified sys_user row) or the legacy unscoped grant row, config arm first. Four comment sites still described the retired mechanism as live.

All four re-verified against origin/main at 46b53a25b before editing, as the card required — PR #13685 (leg L3) rewrote 41 comment lines in last-admin-guard.ts, and all three quotes it could have carried away are still there, at the same lines the grading comment measured (:635-636, :648-650, :663-665).

The four corrections

1. packages/types/src/email-verified.ts — this docblock ships in the package's .d.ts, so its consumer set reaches consumers. It named the elevation gate as one of exactly two consumers. Now:

ONE resolution, several consumers, by design (#12751) — and since the #11663 platform-admin re-anchor (leg L4) the walled platform-admin ELEVATION GATE this paragraph used to name first is RETIRED: under a walled posture bootstrapPlatformAdmin writes no grant row and elevates nobody, it reports. Standing is derived PER REQUEST instead — from a config-anchored verified email, or the legacy unscoped grant row — so the consumer set now includes the authorization derivation itself

The list that follows names the live readers, verified by git grep isEmailVerifiedUserRow: matchesConfiguredPlatformAdmin (the derivation site, and through it plugin-auth's last-admin guard), resolvePlatformAdminStanding and isVerifiedPlatformOwnerRow in plugin-security, and the walled owner-verification boot diagnostic in plugin-auth. The drift argument is restated for that set:

They must all answer "is this row verified?" identically — a drift is no longer just a boot warning forecasting a refusal that will not be made, it is a diagnostic, an audit surface or a guard disagreeing with who actually resolves PLATFORM_ADMIN on the next request.

2. last-admin-guard.ts:635-636 — "un-makes every platform admin at once" is true for the grant anchor only:

derivation, so active: false on admin_full_access un-makes every GRANT-anchored platform admin at once — the same end state as renaming or deleting the row, reached by a payload that touches neither. ⚠️ Not "every platform admin": since the #11663 re-anchor (L2) standing has a SECOND anchor this write cannot reach — a config-anchored administrator (a declared OS_PLATFORM_OWNER_EMAIL address on a VERIFIED sys_user row) is derived at resolve-authz-context.ts §6b-config without consulting the set row or its active flag at all, and carries the shipped ADMIN_FULL_ACCESS_CAPABILITIES envelope rather than the stored set's.

active stays in PERMISSION_SET_STANDING_KEYS, and the corrected prose now says why in terms — because the corrected reasoning is exactly what would make a future reader think it can go:

That is deliberately NOT a reason to drop active from this list: the write can still empty the GRANT anchor, which on every deployment that has declared no administrator emails is the whole population. Listing it is an over-approximation in the SAFE direction — it can cost an enumeration on a write that turns out to change no count, never the reverse — and taking it out would be a behaviour change, not a comment fix.

3. last-admin-guard.ts:648-650 — the "never from the capabilities it carries" half read as an exhaustive statement of the derivation's inputs. The paragraph's conclusion (label/description/permission-blob writes stay invisible to "who is an administrator") is still true and is kept:

resolveAuthzContext derives platform_admin from the NAME of an ACTIVE set or, since the #11663 re-anchor (L2), from the deployment-config anchor, and from the capabilities of neither (the config arm's envelope is the shipped ADMIN_FULL_ACCESS_CAPABILITIES declaration, not the stored row) — so those writes still cost this guard no reads at all.

4. last-admin-guard.ts:663-665 — "grants only" is false post-L2; a config-anchored administrator needs no grant row at all. The paragraph's own claim (a position reaches nothing) survives, and now points at the list that does guard the config anchor's sys_user half:

platform-admin standing is read from UNSCOPED sys_user_permission_set grants and from the deployment-config anchor (a declared OS_PLATFORM_OWNER_EMAIL address on a VERIFIED sys_user row, which USER_STANDING_KEYS below guards) — a position-bound admin_full_access reaches neither, in the resolver or here — and org-administrator standing is read from sys_member.role. Deactivating a position cannot empty any of them.

One further edit, mechanical: the site-3 rewrite left an over-long line, so the sentence that follows it ("Adding active does not walk that back…") is re-wrapped. Same words, different line breaks.

Zero-behaviour-change measurements

Both taken at head 85eee388d.

Non-comment diff is empty, measured rather than asserted: each file, before and after, run through ts.transpileModule with removeComments: true — the executable substance with all comments gone. diff -u is 0 lines for both files, and the sha256 of the stripped output is unchanged:

34f96471904a52798d93fc06d3a87cf3f377dac88d280e53f3271298ec68caf7 email-verified.js (before AND after)
21a3202fec72fd0a9a9d3104c9b9084bdd6e425f3401b9c9c247a3bb617a66d1 last-admin-guard.js (before AND after)

A cruder second reading agrees: every added and removed line in git diff -U0 begins with a JSDoc continuation marker; filtering those out leaves nothing.

throw census, both directionsgrep -cE '\bthrow\b':

FileSource before → afterComment-stripped before → after
packages/types/src/email-verified.ts0 → 00 → 0
packages/plugins/plugin-auth/src/last-admin-guard.ts7 → 77 → 7

(The grading comment measured 6 on L3's head df17ff0a7; origin/main at 46b53a25b carries 7. The census is identical before and after this change, which is what it is for.)

Control-byte self-scan over both files: clean (grep -naP '[\x00-\x08\x0b\x0c\x0e-\x1f\x7f]' matches nothing).

Gates

Re-derived from the actual diff, not from the dispatch list: node scripts/pm/dispatch-gates.mjs --repo objectstack-ai/objectstack at 85eee388d — 2 paths, 21 families plus 1 convention-triggered. No .mdx entered the diff (check-system-context-census passed without needing --fix), so no second derivation was owed. All 22 run locally, exit captured before any pipe:

20 green, including the two the card flagged as the line-anchor-rot risk — check-system-context-census, check-affected-docs, check-drift-comment, check:doc-authoring, check:published-files, check:type-source-resolution, check:test-source-alias, check:dispatcher-error-vocabulary, check:cross-package-test-inputs, check-comment-mask-adoption, check-keyed-text-bounds, check-tenant-audit-census, check-ci-filter-parity, check-plugin-teardown-shape, check-shard-attestation, check-undeclared-dep-imports, check:logger-receiver-detach, check:page-declaration-shape, check:slot-lookup.

2 NOT MEASURED (exit 3 = PREREQUISITE NOT MET — neither a pass nor a red):

  • check-test-completeness — "this gate grades a saved turbo run test log, and no log was named"; the gate's own text says the local reading for it is NOT MEASURED.
  • check:dual-build-cjs-loads — "PREREQUISITE NOT MET — this gate reads built output, and some package has no dist/", listing 102 unbuilt packages. It needs a whole-workspace pnpm build. It reads CJS load behaviour of dist entrypoints, which a comment cannot move, and the byte-identical stripped-output measurement above is the evidence for that; CI measures it for real.

Beyond the derived family:

  • pnpm lint (eslint . --no-inline-config, whole repo, the unconditional CI step) — green, 63s. Not narrowed.
  • pnpm --filter @objectstack/types typecheck and pnpm --filter @objectstack/plugin-auth typecheck (tsc --noEmit, and plugin-auth's second tsconfig.examples.json pass) — both green, script names echoed in the logs so neither is a zero-match no-op.
  • pnpm --filter @objectstack/types test — 16 files, 473 passed.
  • pnpm --filter @objectstack/plugin-auth exec vitest run --maxWorkers=2 over last-admin-guard.test.ts, last-admin-guard.config-anchor.test.ts, last-admin-guard.re-pricing.test.ts, last-admin-standing-keys.test.ts — 4 files, 163 passed.

Dependency closures were built first, through turbo (turbo run build --concurrency=2 --filter=@objectstack/types --filter=@objectstack/plugin-auth, 26 tasks successful), so nothing above read a stale dist. Every heavy run went through scripts/pm/os-verify-lock.sh.

Changeset: skip-changeset, deliberately

The label is applied, and this is the judgment rather than an assumption. skip-changeset is the repo's exemption for a PR that publishes nothing, and a comments-only diff is on the closed list for it. The nuance the card flagged is real — packages/types docblocks do ship, in .d.ts, and plugin-auth's exported-const docblocks ship the same way — but nothing a consumer can depend on moves: no type, no exported name, no runtime, no contract. The transpile-with-removeComments measurement above is exactly the statement that the published substance is byte-identical. A changeset here would force a patch release of two packages whose CHANGELOG row could only say "an internal docblock now describes the current mechanism", which is release-note noise rather than a user-visible change.

Out of scope, filed

Filed out of scope: #13903 — the same dead premise survives at roughly six more comment sites the card did not scope, including packages/types/src/env.ts:169, which ships in the types declarations for the same reason email-verified.ts does. Deliberately not fixed here: the card names four sites in two files, and two of the sites in that finding are already correctly past-tense while a third is wrong only about the name, so it needs per-site triage rather than a search-and-replace rider on this PR.


Generated by Claude Code

…etired platform-admin elevation gate
The #11663 platform-admin re-anchor (legs L2 and L4) retired the walled
platform-admin elevation gate: standing is now derived per request, from an
env-configured verified email OR the legacy unscoped grant row. Four comment
sites still described the retired mechanism.
- packages/types/src/email-verified.ts: the docblock named the elevation gate
as one of exactly two consumers. It ships in the package's .d.ts, so the
wrong consumer set reaches consumers. Replaced with the live consumer set,
which now includes the authorization derivation itself.
- packages/plugins/plugin-auth/src/last-admin-guard.ts, the docblock above
PERMISSION_SET_STANDING_KEYS: three consequences of the same dead premise —
"un-makes every platform admin at once", "never from the capabilities it
carries", and "UNSCOPED sys_user_permission_set grants only".
Comment-only. `active` deliberately stays in PERMISSION_SET_STANDING_KEYS and
the corrected prose now says why: it is a safety-side over-approximation, and
removing it would be a behaviour change.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_016ZC5rNQj3WEet5HAmmAkMs
@claudeclaudeBot added the skip-changeset PR has no user-facing published change; bypasses the changeset gate label Aug 31, 2026
@github-actions

Copy link
Copy Markdown
Contributor

📓 Docs Drift Check

⚠️2 changed file(s) yielded no anchor (packages/plugins/plugin-auth/src/last-admin-guard.ts, packages/types/src/email-verified.ts), so the pages documenting them are NOT COVERED by this run — this is not a clean bill of health for those files. Nothing else in this diff resolved to a documentable surface (no symbol, route or SDK anchor derived from 2 changed package(s)).

What this run could not see
  • 2 changed file(s) yielded no anchor (packages/plugins/plugin-auth/src/last-admin-guard.ts, packages/types/src/email-verified.ts) — pages documenting those are invisible to this run
  • a page that states a rule by its inputs shares no identifier with the emitter that implements the rule, so an emitter-only diff cannot list it — not on this run and not on any run. Measured on fix(driver-sql): emit varchar(maxLength) for a text field a declared index keys on #11430: content/docs/protocol/objectql/types.mdx documents the text-family column mapping by the ObjectQL type names it maps FROM (text / textarea / html) while the diff changed createColumn; it went unlisted, and it was the page that diff falsified, in four places. No shared token exists to detect this on, so a rule your change carries has to be re-read by hand in the pages that restate it.

Coarse fallback — 11 page(s) merely mention a changed package (the pre-#9192 predicate, kept for the deliberately-wide backstop): node scripts/docs-audit/affected-docs.mjs --json 0c143ececbd5aa92c6edbf1d84bbf82668b585c9packageMentionDocs.

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

Labels

size/sskip-changesetPR has no user-facing published change; bypasses the changeset gate

Projects

None yet

Development

Successfully merging this pull request may close these issues.

[finding] Two comments still describe the walled platform-admin elevation gate that L4 retired — one of them ships in the types package declarations

2 participants

@os-steve@claude