Skip to content

docs(adr): anchor the default-active-org session hook to ADR-0093 D9 (#8474) - #8529

Merged
os-zhuang merged 1 commit into
mainfrom
claude/issue-8474-defaultactiveorg-anchor
Aug 14, 2026
Merged

docs(adr): anchor the default-active-org session hook to ADR-0093 D9 (#8474)#8529
os-zhuang merged 1 commit into
mainfrom
claude/issue-8474-defaultactiveorg-anchor

Conversation

@qq9340100

Copy link
Copy Markdown
Collaborator

Part of #8474

What was wrong

session.create.beforedefaultActiveOrg (the hook that stamps a session's
activeOrganizationId from the caller's sys_member row) was cited across
plugin-auth, plugin-sharing and the dogfood suite as "ADR-0081 D1".

Two independent facts make that citation unusable:

  • docs/adr/0081-trusted-react-page-tier.md is the trusted kind:'react' page
    tier. It says nothing about organizations.
  • It has no D-numbered decisions at all — its Decision section is numbered
    1–4. So "D1" names nothing even within the record.

pnpm check:adr-anchors cannot catch this: it verifies a cited ADR exists,
and 0081 does. A reader following the citation lands on a real, accepted,
plausible-looking record with no signal they are in the wrong document.

Where the decision actually belongs

The card offered two options; the answer turned out to be a refinement of the
second — the decision was never recorded in this repo's ADR series, and
ADR-0093 already knew why. Its Relates to line says the label is inherited
from a decision record that "predates this repo's ADR series". That pre-repo
number now collides with this repo's ADR-0081.

Three independent signals put the record at ADR-0093:

  1. scripts/adr-anchors/packages__plugins__plugin-auth__src__auth-manager.ts.json
    already lists ADR-0093 (not 0081) as that file's governing record, and its
    invariant text already describes the session.create.before seam.
  2. ADR-0123 (accepted today, out of [Decision] What is an authenticated session with NO active organization? Three subsystems answered it three contradictory ways in one shift (fail-open / fail-closed / permanently-invisible ledger rows) #8247) already anchors the hook to ADR-0093
    twice — including the explicit composeDatabaseHooksdefaultActiveOrg
    pointer.
  3. The stamp is a read of the invariant ADR-0093 D1 states and D2 owns: it can
    only resolve an organization when a sys_member row exists, and which row
    exists is decided entirely by D1's policy and D2's reconciler.

But ADR-0093's existing D1/D2 do not decide the stamp — D1 is the membership
invariant, D2 is the user.create.after reconciler; the hook appears only as
background in Context. Re-pointing the sites at "ADR-0093 D2" would have swapped
one wrong-but-existing anchor for another — the same defect class as #8386.

So per triage's ruling, this records the shipped behaviour as ADR-0093 D9
and points every site there.

Changes

Behaviour is unchanged. The TypeScript diff is provably comment-only — zero
non-comment lines changed.

Why one PR and not the two-PR split

The split shape (#8290/#8291, #8384/#8385, #8437/#8440) needs the two halves to
be independent. Here they are not: the code half's new citations point at
ADR-0093 D9, which only exists in the ADR half. Landing the code half first
would re-point six sites at a decision number that does not exist — reproducing
exactly the defect this card exists to close, with a different number. So this is
a single maintainer-merged draft.

Deliberately NOT in this PR

Re-deriving the census found 34ADR-0081 D1 sites carrying four distinct
claims
. Only claim A — the active-org stamp, this card's subject — is fixed
here. The other three claims (the ensureDefaultOrganization bootstrap, the org
capability gate, the posture/entitlement wall), plus the separate ADR-0081 D2
family, are a different set of decisions sharing the same inherited label. They
are filed separately and are not addressed here; #8474 covers claim A only.

Historical records were left untouched per the "keep history as history" rule
(#8291 / #8385 / #8440): two .changeset/*.md files and four published
CHANGELOG.md files carry the old label and are not rewritten.

Verification

  • pnpm check:adr-anchors — OK. 121 decision numbers, 23726 citations across
    4016 files resolve; D9 accepted as a decision number.
  • node scripts/check-adr-links.mjs — OK, 540 link destinations resolve.
  • pnpm check:nul-bytes · check:docs-audit-scope · check:test-source-alias ·
    check:type-source-resolution · check:i18n — all OK.
  • node scripts/check-adr-merge-approval.mjs — HTTP 401 locally (no token);
    CI judges it.
  • vitest run src/auth-manager.test.ts -t "active-org" — 9 passed.

⚠️ Maintainer-merged: docs/adr/** is in the diff (Prime Directive #14). No
auto-merge, no enqueue.


Generated by Claude Code

…8474)
`session.create.before` -> `defaultActiveOrg` was cited across plugin-auth,
plugin-sharing and the dogfood suite as "ADR-0081 D1". ADR-0081 in this repo
is the trusted `kind:'react'` page tier and has no D-numbered decisions at
all, so the citation resolved to a real, accepted, plausible-looking record
that says nothing about active organizations.
Record the shipped behaviour as ADR-0093 D9 (seam, selection, only-when-absent,
best-effort, opt-out, and the #8245/#8247 ordering) and re-point every
active-org-stamp citation at it. ADR-0093 already owns `sys_member` and the
membership lifecycle the stamp reads, ADR-0123 already anchors the hook there,
and `scripts/adr-anchors/...auth-manager.ts.json` already lists ADR-0093 as
that file's governing record.
Behaviour is unchanged — comments and prose only.
Part of #8474
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Jqe56GnYFddggeAyfkZFVz
@vercel

vercelBot commented Aug 13, 2026

Copy link
Copy Markdown

The latest updates on your projects. Learn more about Vercel for GitHub.

1 Skipped Deployment
ProjectDeploymentActionsUpdated (UTC)
objectstackIgnoredIgnoredAug 13, 2026 5:30pm

Request Review

@qq9340100qq9340100 added skip-changeset PR has no user-facing published change; bypasses the changeset gate and removed size/s labels Aug 13, 2026 — with Claude
@github-actions

Copy link
Copy Markdown
Contributor

📓 Docs Drift Check

This PR changes 2 package(s): @objectstack/plugin-auth, @objectstack/plugin-sharing.

13 hand-written doc(s) reference the affected code and may need an implementation-accuracy re-verification:

  • content/docs/deployment/cli.mdx(via @objectstack/plugin-auth)
  • content/docs/deployment/production-readiness.mdx(via @objectstack/plugin-auth)
  • content/docs/kernel/contracts/cache-service.mdx(via @objectstack/plugin-auth)
  • content/docs/kernel/runtime-services/examples.mdx(via @objectstack/plugin-sharing)
  • content/docs/kernel/runtime-services/sharing-service.mdx(via @objectstack/plugin-sharing)
  • content/docs/kernel/services-checklist.mdx(via @objectstack/plugin-auth, @objectstack/plugin-sharing)
  • content/docs/permissions/authentication.mdx(via @objectstack/plugin-auth)
  • content/docs/permissions/authorization.mdx(via packages/plugins/plugin-sharing)
  • content/docs/permissions/permissions-matrix.mdx(via packages/plugins/plugin-sharing)
  • content/docs/permissions/sso.mdx(via @objectstack/plugin-auth)
  • content/docs/plugins/index.mdx(via @objectstack/plugin-auth)
  • content/docs/plugins/packages.mdx(via @objectstack/plugin-auth, @objectstack/plugin-sharing)
  • content/docs/protocol/objectql/security.mdx(via packages/plugins/plugin-sharing)

2 release-owned page(s) also reference the affected code. These are read-only:

  • content/docs/releases/implementation-status.mdx(via @objectstack/plugin-auth, @objectstack/plugin-sharing)
  • content/docs/releases/v9.mdx(via @objectstack/plugin-auth)

content/docs/releases/ is RELEASE-OWNED (AGENTS.md "Documentation Guardrails"): release
notes are written centrally at release time, and a code PR that edits them is the exact PR
that guardrail exists to stop. They are still audited — read-only. If one of them is actually
wrong, file an issue or open a dedicated docs-only PR; do not edit it here.

Advisory only. To re-verify, run the docs-accuracy-audit workflow scoped to these files:
node scripts/docs-audit/affected-docs.mjs origin/main → pass the list as args.docs.

@qq9340100Claude

Copy link
Copy Markdown
CollaboratorAuthor

The red ADR maintainer approval is by design — no fix is coming, and none is needed

Job log read rather than inferred (check-adr-merge-approval, run 31726034790):

❌ This change touches docs/adr/ and this PR is not in a mergeable state under the ADR rules.
• docs/adr/0093-tenancy-mode-and-membership-lifecycle.md
No state-setting review (APPROVED / CHANGES_REQUESTED / DISMISSED) has been submitted at all.
Fix: anyone with review rights on this repo approves the PR …

That is the gate working exactly as intended: the PR touches docs/adr/**, so it is unmergeable until a human approves. Nothing about the diff is wrong, and pushing another commit would not change it. This check stays red until the maintainer acts, and that is the desired state.

⚠️Recording something the log makes explicit, because it is the hazard #8012 documented. The gate now says, in its own output, "This gate does NOT check who approved" — the #8161 relaxation (maintainer, 2026-08-12: 「门禁改成只要求「APPROVED review 存在」」/「不要指定具体的人」). So this seat could satisfy this gate right now by submitting an approving review, and is deliberately not doing so. The standing ruling is 「adr 只能由维护者自己确认,人工合并」 — confirming is precisely what an approving review is, and that half is no longer enforced mechanically. It is enforced by the seat declining to use access it holds, which is the condition #8012 was filed about. Stated out loud rather than left as a silent non-action.

⛔ Nothing is armed on this PR: no auto-merge, not enqueued. (Per PR #8302, landed today, arming auto-merge here would itself turn this gate red regardless of approval.)

Docs Drift Check — advisory, and answered

The bot flags 13 hand-written docs via @objectstack/plugin-auth / @objectstack/plugin-sharing. No re-verification is needed: the TypeScript diff is provably comment-only — zero non-comment lines changed — so no described behaviour moved. The drift check fires on package-level change detection, which cannot see that distinction. The two release-owned pages it lists are read-only and were not touched.


Generated by Claude Code

@qq9340100Claude

Copy link
Copy Markdown
CollaboratorAuthor

Ready for the maintainer to merge. Flipped out of draft by the domain:devx PM seat (#6023) at ~18:0xZ — nothing else changed, head SHA is unchanged at 8b7b5e7.

Why the flip, stated explicitly so it is not mistaken for a step toward AI-merging this:

This PR was parked as a draft "awaiting a human merge". That was a mistake on this seat's part — GitHub will not merge a draft PR for anyone, the maintainer included. Parking an ADR PR as draft does not wait for a human, it blocks the human. Flipping to ready-for-review is not merging, not enqueueing, and not arming auto-merge; it is the minimum required for the maintainer to be able to act.

What this seat has deliberately not done, and will not do:

On merge, #8474 needs closing by hand — this PR says Part of, so nothing closes automatically. Per the 17:5xZ ruling: claim A (the active-org stamp) is the whole of #8474; the remaining three claims and the ADR-0081 D2 family are residue and belong to #8531.


Generated by Claude Code

@qq9340100Claude

Copy link
Copy Markdown
CollaboratorAuthor

State record from the domain:devx PM seat (#6023) — reporting only, taking no action.

At 2026-08-14T00:27:47Z this PR was added to the merge queue by os-zhuang. Measured state at that moment:

  • ADR maintainer approvalfailure (job 94534521031, completed 2026-08-13T17:30:39Z, head 8b7b5e7 unchanged since)
  • reviews on this PR — zero. Not "no approving review": no review of any kind exists.
  • every other check — success or skipped

This seat did not enqueue it, and will not dequeue it. Both halves are deliberate:

  • I did not enqueue it. The standing commitment is that docs/adr/** PRs are never AI-merged, enqueued, or armed with auto-merge (⛔ Discipline: ADRs are confirmed and merged by the maintainer only — no AI seat may merge, queue, or auto-merge a docs/adr/** PR #6741), and I have said twice on this thread that I would not turn that gate green with a review I am mechanically able to cast — the ruling 「adr 只能由维护者自己确认,人工合并」 makes the approving act the maintainer's, so casting it myself would be routing around the control rather than satisfying it.
  • I will not dequeue it either. os-zhuang is a shared identity that this seat cannot distinguish between the maintainer and another AI seat. The standing rule for that ambiguity is: default to treating the actor as the maintainer, do not revert, report. Reverting a maintainer's deliberate action on a guess would be worse than the risk of leaving it.

⚠️What is worth a human's attention, stated plainly: if this merges from the queue while ADR maintainer approval is red and no review exists, then that gate is not actually blocking merges via the queue path — and the control it implements would have been bypassed without anyone deciding to bypass it. That is a fact about the gate wiring, not about whoever queued this. Two outcomes are possible and both are informative:

  • the queue rejects it on the red required check → the control works, and this was a no-op;
  • it merges → the control has a queue-shaped hole, and that is worth a card against the gate rather than against this PR.

I am watching for either. On merge, #8474 still needs closing by hand — this PR says Part of, so nothing closes automatically — with the residue going to #8531 per the 17:5xZ ruling.


Generated by Claude Code

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

Labels

documentationImprovements or additions to documentationskip-changesetPR has no user-facing published change; bypasses the changeset gatetests

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants

@qq9340100@os-zhuang