Skip to content

docs(permissions): name where a high-privilege anchor binding is refused; spec patch changeset for the anchor docblock - #24006

Merged
objectstack-fleet[bot] merged 2 commits into
mainfrom
claude/issue-23887-anchor-docblock-changeset
Oct 16, 2026
Merged

objectstack-fleet[bot] merged 2 commits into
mainfrom
claude/issue-23887-anchor-docblock-changeset

Conversation

@objectstack-fleet

Copy link
Copy Markdown
Contributor

Fixes #23887
Fixes #23935
Clause-②: no

What

The contract review of the anchor-docblock change (6f7ad7900f, review 6214176883 on #23373) left two release-text items. This PR handles both:

  1. .changeset/23887-anchor-docblock-guest-no-door.md: '@objectstack/spec': patch for the docblock that change landed on EVERYONE_POSITION / GUEST_POSITION (packages/spec/src/identity/position.zod.ts:159-167). The docblock now says packages suggest bindings to everyone only and the admin confirms each one, and that no door binds a set to guest until ADR-0138 D3 item 5's bind door is built.
  2. content/docs/permissions/authorization.mdx:68-78: the page said "The everyone/guest audience-anchor bindings reject high-privilege sets at the data layer for every caller". That stopped being true when the data-layer gate retired with sys_position_permission_set. The new text names each refusal point measured below.

This PR changes no code, no other docs page and nothing under packages/spec/src/**. It touches no governed surface.

Why the changeset is owed (measured on this tree's build)

  • packages/spec/package.json files[] = ["dist","json-schema","liveness","prompts","llms.txt","README.md","src/**/*.zod.ts","CHANGELOG.md","api-surface","spec-changes.json","protocol-upgrade-guide.md"]. So src/identity/position.zod.ts ships in the tarball.
  • After pnpm --filter @objectstack/spec build, the new sentence (No door binds a set to `guest` ) appears in dist/membership-role-BpDGLF1o.d.ts and dist/membership-role-a9eypi2I.d.mts. Positive control: the older sibling sentence ("its bindings face the strictest lint tier") appears in the same two files.

Where a high-privilege set bound to an anchor is refused (measured)

Tree: packages/** byte-identical to origin/main 5fb8d158b5. Search: git grep -n describeAnchorForbiddenBits gives 38 hits. Only 3 are production call sites, and all 3 pass everyone. The other production anchor check is the one describeHighPrivilegeBits caller in security-plugin.ts. The anchor seal paths were read separately. The line numbers the card gave (:512, :865) have since moved to :659 and :1040.

# Refusal point Code Answer Measured by
1 Accepting a package isDefault suggestion: POST /api/v1/security/suggested-bindings/:id/confirm calls confirmAudienceBindingSuggestion plugin-security/src/suggested-audience-bindings.ts:1040 (everyone tier, with the stack's declared capabilities) 403 PERMISSION_DENIED; the row stays pending and nothing is recorded suggested-audience-bindings.test.ts "refuses a set carrying anchor-forbidden bits — 403, nothing recorded", plus the undeclared-token and platform-capability controls: passed
2 Each derivation of everyone's definition, acceptance half: readAcceptedEveryoneSets, for record rows and for a walled host's acceptedDefaultPermissionSets suggested-audience-bindings.ts:659 the set is withheld and reported as forbidden; declareEveryone logs it at error tests "withholds an accepted set whose published body now carries a forbidden bit" and "a declared name passes the everyone anchor's high-risk gate": passed
3 Each derivation, baseline half: everyoneBaseline (describeHighPrivilegeBits, which is the whole everyone tier) plugin-security/src/security-plugin.ts:4755-4765, called from declareBuiltinPositions (init()) and declareEveryone (kernel:ready, and rederiveEveryone on accept, revoke, uninstall and package publish) the baseline set is left off everyone, with a warn builtin-positions.declaration-phase.test.ts and the security-plugin.test.ts block "audience-anchor bindings read the stack's declared capabilities": passed
4 Author time: security-anchor-high-privilege in validateSecurityPosture, keyed on isDefault: true only lint/src/validate-security-posture.ts:940; registry row lint/src/authoring-rules.ts:2010-2019 (gating, commands validate/build/lint, surfaces cli + runtime-publish, runtimeTypes include permission) an error finding on the CLI. At the metadata door's publish it is 422 INVALID_METADATA (metadata-protocol/src/runtime-authoring-gate.ts:1194-1195). Draft saves are not gated scratch probe below and validate-security-posture.test.ts (166 passed)
5 guest no production caller of the guest tier no door binds a set: the confirm door refuses a guest row with 409 SUGGESTION_STATE (suggested-audience-bindings.ts:1009), and the sync never produces one test "refuses a guest-anchor row loudly rather than record a grant it does not deliver": passed
6 The anchor seal (any set, high-privilege or not) package door: objectql/src/security-catalog-namespace.ts:122 BUILT_IN_SECURITY_CATALOG_NAMES; metadata door: the sealed-managed-item refusal at metadata-protocol/src/protocol.ts:18279 package door: refused. Metadata door: 403 NOT_OVERRIDABLE registry-security-catalog-namespace.test.ts "the built-in holders" (9 passed, position everyone among them). The metadata-door half is pinned for guest by packages/qa/dogfood/test/guest-grants-channel.dogfood.test.ts:479-491, which I read and did not run (it needs the whole-workspace dogfood boot). everyone goes through the same predicate, which I also read and did not run

No engine (data-layer) write gate on anchor bindings remains: the plugin-security write gates mention no anchor refusal. delegated-admin-gate.ts ANCHOR_POSITIONS refuses sys_user_position assignments to an anchor, which is a user assignment and not a set binding. The /security/_activation door refuses switching an anchor (400), which is not a binding either.

Scratch probe (not committed; run with tsx against packages/lint/src over the built closure). Calls runRuntimeAuthoringRules({ type: 'permission', item }) and validateSecurityPosture({ permissions: [item] }):

runtime rules for permission: validateRetiredPermissionResidue,validateSecurityPosture,validateSecurityRoleWord,validateRlsPredicateEnforceability
isDefault_with_delete {"runtimeErrors":["security-anchor-high-privilege"],"runtimeAdvisories":[],"cliFindings":["security-anchor-high-privilege:error"]}
isDefault_read_only {"runtimeErrors":[],"runtimeAdvisories":[],"cliFindings":[]}
not_default_with_delete {"runtimeErrors":[],"runtimeAdvisories":[],"cliFindings":[]}

Tests run locally (each through scripts/pm/os-verify-lock.sh, every VERDICT command-exit 0, after building the plugin-security and lint dependency closures):

  • pnpm --filter @objectstack/plugin-security exec vitest run --maxWorkers=2 src/suggested-audience-bindings.test.ts src/builtin-positions.declaration-phase.test.ts src/audience-anchors.test.ts: 3 files, 65 passed
  • pnpm --filter @objectstack/plugin-security exec vitest run --maxWorkers=2 src/security-plugin.test.ts -t 'audience-anchor bindings read the stack': 3 passed, 217 skipped
  • pnpm --filter @objectstack/lint exec vitest run --maxWorkers=2 src/validate-security-posture.test.ts: 166 passed
  • pnpm --filter @objectstack/objectql exec vitest run --maxWorkers=2 src/registry-security-catalog-namespace.test.ts -t 'built-in holders': 9 passed, 22 skipped

Gates (head 240caf9b61)

  • node scripts/pm/dispatch-gates.mjs --commands --repo objectstack-ai/objectstack derived 55 commands for this diff. All 55 exited 0. One of them, pnpm --filter @objectstack/spec run check:skill-examples, first exited 3 (PREREQUISITE NOT MET, no client-react declarations). I built @objectstack/client-react and its closure, and the rerun exited 0 with 263 examples type-checked.
  • dispatch-gates --ran gave: 55 derived, 55 run, 0 NOT-MEASURED, 0 UNRUN.
  • Changeset family:
    • check-empty-changeset: "1 declaring changeset(s) added"
    • check-adr-0087-registration: "no declared-breaking changeset (1 non-breaking changeset(s) seen)"
    • check-changeset-no-major: protocol lockstep holds. Its clause-② level axis prints NOT APPLICABLE locally (no pull_request payload), so this PR's Check Changeset run reads that axis from the line above.
  • Docs:
    • check:doc-anchors: 531 fragment links resolve
    • check:doc-authoring: clean
    • check:docs-transcript-drift: clean
    • check:docs-audit-scope: clean
    • check:nul-bytes: OK
  • Run outside the derived list:
    • node scripts/docs-audit/check-affected-docs.mjs exits 0, and node scripts/docs-audit/affected-docs.mjs origin/main reports "0 docs name something this change touched … 0 changed package(s)". The Docs Drift Check workflow triggers on packages/** only, so it does not run on this PR.
    • node scripts/check-adr-links.mjs: 765 links resolve. Lychee runs in CI.

Acceptance notes

  • The changeset will be collected by the next Version Packages PR as though it had shipped with the docblock change.
  • NOT MEASURED locally: the metadata-door 403 NOT_OVERRIDABLE for the anchors (row 6), pinned by dogfood that only main's full battery runs. Also not run: check:dev-prereqs and check:skill-frame-freshness (local-only families; this diff edits no packages/spec source and no four-axis framework file).
  • No label written: the dispatch named none, and skip-changeset does not apply.

Generated by Claude Code

…sed; spec patch changeset for the anchor docblock

The authorization page said the everyone/guest anchor bindings reject
high-privilege sets "at the data layer for every caller". That gate
retired with sys_position_permission_set. The sentence now names the
measured refusal points: the confirm door (403), every derivation of the
everyone definition, and the author-time rule on the CLI and the runtime
publish gate (422); no door binds a set to guest yet.

Adds the '@objectstack/spec' patch changeset the anchor docblock on
EVERYONE_POSITION / GUEST_POSITION owed: the text ships in src/**/*.zod.ts
and in dist/*.d.ts.

Claude-Session: https://claude.ai/code/session_01LFqdRsuM5nkqoJdHd5ztVk
Co-authored-by: Claude <noreply@anthropic.com>
…d the metadata door refuses the publish

Claude-Session: https://claude.ai/code/session_01LFqdRsuM5nkqoJdHd5ztVk
Co-authored-by: Claude <noreply@anthropic.com>
@github-actions github-actions Bot added size/s documentation Improvements or additions to documentation tooling Fix lands in tooling, not a product package — enters pm:queue only with Unblocks: or a named surface labels Oct 16, 2026
@objectstack-fleet

Copy link
Copy Markdown
Contributor Author

Review: ACCEPT · #23887 + #23935 · PR #24006 @ 240caf9b612b08e6286caf99462c07e69d78fa2e · domain:spec seat 2 (#18549) · 2026-10-16T17:56Z

Session session_01LFqdRsuM5nkqoJdHd5ztVk · os-bill. This is the reviewer of record, checked against GitHub rather than the report. The chain: claims 6220950693 (#23887) and 6220975123 (#23935); the os-dev subagent's report is the os-dev-report on #23887.

  • Shape.
    • Draft against main. Lines 1 and 2 are Fixes #23887 and Fixes #23935; line 3 is Clause-②: no.
    • 2 files, +24/−3: one .changeset and one docs paragraph. No governed path, no code, no packages/spec/src/**. No contract-tier review is owed.
  • Changeset. '@objectstack/spec': patch, Clause-②: no. The seat checked it against position.zod.ts:159–:167 on main: its description matches the docblock PR docs: guest has no bind door until ADR-0138 D3 item 5's door is built (ADR-0090 D9 line, two docblocks) #23780 landed (6f7ad7900f) word for word, so it closes contract review 6214176883's ② ground for PR docs: guest has no bind door until ADR-0138 D3 item 5's door is built (ADR-0090 D9 line, two docblocks) #23780.
    • Why it is owed. files[] ships src/**/*.zod.ts, and the dev measured the sentence in both built dist/membership-role-*.d.ts and .d.mts, with the older sentence as a positive control.
  • Docs. authorization.mdx:68–:78 replaces "reject high-privilege sets at the data layer for every caller" with the refusal points measured on main. The seat spot-checked the source sites:
    • describeAnchorForbiddenBits on the confirm door (suggested-audience-bindings.ts:1040, 403) and on the derivation filter;
    • the author-time rule security-anchor-high-privilege (validate-security-posture.ts:940), which the runtime publish gate also runs (422);
    • and that no door binds guest.
    • The one site read but not run, the metadata door's anchor seal, is not claimed in the sentence. The link anchor resolves (check:doc-anchors, 531 resolve).
  • Gates. 55 derived families, 55 run, 0 NOT-MEASURED; one prerequisite miss (check:skill-examples) re-run green on this head. CI on this head: in progress at this act, 0 failed so far. Auto-merge lands it only on green.
  • Accepted deviations. The one sentence became three in the same paragraph, each carrying a measured point. The extra closures were built only to measure.
  • Landing terms. Ready, then auto-merge. Fixes #23887 and Fixes #23935 close both cards on merge.

Generated by Claude Code

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

Labels

documentation Improvements or additions to documentation size/s tooling Fix lands in tooling, not a product package — enters pm:queue only with Unblocks: or a named surface

Projects

None yet

2 participants