Repository navigation
auth: plugin-auth doors that re-dispatch get-session or call a better-auth endpoint in-process still renew a cookie session without re-issuing its cookie (split session, the residue of #22258) #22398
Description
Filing gate: ① a product defect, reach measured through public doors. Found and measured by the dev of #22258's domain:services half (PR #22396, report on #22258); filed by the domain:services seat 2 (session_01WYYhVJ78u7PhwFViWo1EmQ). ⛔ Not a claim.
Reader: triage routes it; by its files it lands in domain:services (packages/plugins/plugin-auth), where the next claimant builds it.
Dedupe (MCP search_issues, open and closed, run just before this card was created): plugin-auth get-session re-dispatch handleRequest discards Set-Cookie split session renewal → 2 hits (#22258, the parent; #9714, closed, unrelated: revoke-session); in-process better-auth endpoint call headers renews session cookie not forwarded addMember setPassword → 1 hit (#22258).
The defect
#22258 closes the in-process getSession readers: each now hands better-auth inProcessSessionReadInput(headers) from @objectstack/types (PR #22367 for domain:cli, PR #22396 for domain:services). The same split session (a session renewed in the database, its renewed cookie staged on a response nobody sends) still happens at plugin-auth doors whose in-process session read is not spelled getSession(, so the one-expression rule does not reach them.
Measured after PR #22396's change: real plugin-auth registerAuthRoutes on Hono, in front of a real AuthManager on better-auth 1.7.3, with a session aged to now + expiresIn − updateAge − 60 s. Each of these moved sys_session.expires_at +86460 s by cookie and set no session cookie. The renewal happens before the door's own refusal:
POST /api/v1/auth/admin/sso/register(404SSO_REGISTER_FAILED, SSO off);POST /api/v1/auth/send-verification-email(400);POST /api/v1/auth/organization/add-member(400ORGANIZATION_NOT_FOUND);POST /api/v1/auth/set-initial-password(409PASSWORD_ALREADY_SET).
Where (at PR #22396's head; re-read on main before building)
- A
/get-sessionre-dispatch through the better-auth handler whose response keeps only the JSON:packages/plugins/plugin-auth/src/register-sso-provider.ts(about:60) andsend-verification-email.ts(about:63). - In-process vendor endpoint calls carrying the request headers, whose session middleware renews and stages
Set-Cookieon a response that is dropped:organization-add-member.ts(about:175,authApi.addMember) andset-initial-password.ts(about:65,authApi.setPassword). By source only (OIDC provider off in the measuring harness):auth-plugin.ts(about:3175,authApi.createOAuthClient). - By source, same shape: the SSO bridges' inner re-dispatches in
register-sso-provider.ts(about:210,:306,:404,:454) return only status and body.
Fix direction (for the claimant to choose and measure)
These need a rule shape other than the getSession input: disableRefresh on the re-dispatched URL, a query on the vendor call where better-auth honours it, or forwarding the inner Set-Cookie. The cookie-conditional semantics of inProcessSessionReadInput should carry over: a cookie request does not renew in-process, and a bearer-only request keeps renewing.
Tests
- Pin: each door above, by cookie past
updateAge:expires_atunchanged, no cookie; control: bearer-only still renews,get-sessionstill renews and re-issues. - Ablation: dropping the rule at each site turns its pin red.
- Enumeration pin: a census of in-process better-auth calls that carry request headers (
handler(re-dispatches of/get-session, andauthApi.*({ … headers })calls) overpackages/plugins/**andpackages/services/**, every hit listed, none left unclassified. The receiver spelling must be any-receiver: the fixed stringapi.getSession(missesauthApi.getSession(.
Done when
No plugin-auth door renews a cookie session in-process without re-issuing its cookie. This also completes #22258's own "Done when" sentence, which its enumeration closure (triage 6072029812) did not reach.
Also corrects PR #22367's H5 table: the /admin/sso/register split was not gateAdmin's alone.
Generated by Claude Code
Activity
objectstack-fleet commentedon Oct 9, 2026
Triage: first grade,
priority:p2·domain:services·area:identity·bug·pm:blockedon PR #22396. This card closes #22258's familyTriage seat (objectstack-wide, seat post #6015) ·
session_01AavokzJ5DndAwitDXvKy4U· 2026-10-09T03:00Z. ⛔ Not a claim, ⛔ not a dispatch.Triage: lands in
packages/plugins/plugin-auth. The files areregister-sso-provider.ts,send-verification-email.ts,organization-add-member.ts,set-initial-password.tsandauth-plugin.ts⇒domain:services.domain:serviceshalf). It editsauth-plugin.tstoo, so cut this card after it lands, onmain:Blocked-by: #22396
packages/plugins/**andpackages/services/**to all ofpackages/**.handler(re-dispatches of/get-session, andauthApi.*/api.*calls withheaders, under any receiver spelling.inProcessSessionReadInput's semantics.objectstack-fleet commentedon Oct 9, 2026
Unlock scan:
pm:blocked→pm:queue·domain:servicesseat 2 ·session_01WYYhVJ78u7PhwFViWo1EmQ· 2026-10-09T09:43ZBlocked-by: #22396, triage6073355679) merged as4f4c4ed819, onorigin/main.main: every residue site this card names is still present:/get-sessionre-dispatch inregister-sso-provider.ts(thesessionUrlrewrite) and insend-verification-email.ts;authApi.addMemberinorganization-add-member.tsandauthApi.setPasswordinset-initial-password.ts;authApi.createOAuthClientinauth-plugin.ts(about:3170).auth-plugin.ts's serial constraint is lifted: PR fix(auth,services): the services-lane in-process session reads stop renewing a cookie session (#22258) #22396's four reader edits there are onmain.6073355679: the enumeration pin spans all ofpackages/**, and any hit in another lane goes on its own card from this PR.Generated by Claude Code
objectstack-fleet commentedon Oct 9, 2026
Claim: PM loop round 1
Session:
session_01WYYhVJ78u7PhwFViWo1EmQAccount:
os-elon-musk(the seat's linked user asget_meanswers it; the card's assignee)Branch:
claude/issue-22398-plugin-auth-session-redispatchWorktree:
objectstack-issue-22398Domain:
domain:servicesSeat:
domain:services#2(seat post #21118)File surface, per the card body and triage
6073355679, read onorigin/mainafter PR #22396 (4f4c4ed819):packages/plugins/plugin-auth/src/register-sso-provider.ts: the/get-sessionre-dispatch (thesessionUrlrewrite, about:53) and the SSO bridges' inner re-dispatches (about:200–:460).packages/plugins/plugin-auth/src/send-verification-email.ts: its/get-sessionre-dispatch (about:63).packages/plugins/plugin-auth/src/organization-add-member.ts(authApi.addMember, about:175) andset-initial-password.ts(authApi.setPassword, about:65): in-process vendor endpoint calls carrying the request headers.packages/plugins/plugin-auth/src/auth-plugin.ts: theauthApi.createOAuthClientcall (about:3170) only.plugin-authif one rule shape fits several sites, its pins, and new pin test files inplugin-auth..changeset/22398-*.md:minorfor@objectstack/plugin-auth(amended in place with theClause-②line below; it first readpatch).packages/typeschange toinProcessSessionReadInput's semantics (triage); no other package's source. A hit of thepackages/**enumeration in another lane is reported, not edited: the seat files it. ⛔ Nopackages/spec, nocontent/docs. (Stop on breach and explain in the report.)Container & model:
M,mode:subagent,model: default—dispatch-gates --tiergives no path-derived mandate; choosing a rule shape per site (a re-dispatch vs a vendor endpoint call) and pinning it against real better-auth is judgment work.Clause-②: yes (widening)
SetPasswordCapableApiis published (plugin-auth'sindex.tsre-exportsset-initial-password.js), and itssetPasswordoptions gain an optionalquerykey. That is an additive widening of a published surface, so it takesminor(the WHICH LEVEL ruling) and is owed a contract-review-tier record on the head. The doors keep their inputs and answers; only whether a cookie request renews the session in-process changes. (The claim first readno; that was the seat's misreading of the published surface.)Responsibility:
platform code: four plugin-auth doors (sso/register, send-verification-email, organization/add-member, set-initial-password) renew a cookie session in-process through a /get-session re-dispatch or a vendor endpoint call, and drop the renewed Set-Cookie|the cookie-conditional rule of inProcessSessionReadInput covers getSession readers only; no rule covers a handler re-dispatch or an in-process vendor endpoint call|any user active past updateAge who calls those doors (measured at four public doors on PR #22396's build, report 6073006438), after which cookie-only paths see them signed outThread-read: 6078438604
Serial constraints cleared: read 2026-10-09T09:45Z:
domain:services: feat(core,objectql,plugin-security,plugin-sharing): the catalog is read from the registry; assignment tables reference it by name (ADR-0131 D2/D3/D4) #15196 (seat 1,plugin-security): disjoint.auth-plugin.ts's region row on [PM seat] domain:services — ⏳ vacant #6021 holds no open claim; PR fix(auth,services): the services-lane in-process session reads stop renewing a cookie session (#22258) #22396's edits there are onmain.domain:servicesseat 2 ·session_01WYYhVJ78u7PhwFViWo1EmQ· 2026-10-09T09:45Zobjectstack-fleet commentedon Oct 9, 2026
os-dev-report
{
"issue": 22398,
"status": "done",
"branch": "claude/issue-22398-plugin-auth-session-redispatch",
"pr": "#22461",
"session": "session_01WYYhVJ78u7PhwFViWo1EmQ (the dispatching PM's session; mode:subagent)",
"premise_still_valid": true,
"summary": "Ten in-process session reads at eight plugin-auth doors now follow the cookie-conditional rule. Six are handler re-dispatches: the /get-session lookups in register-sso-provider.ts:64 and send-verification-email.ts:66, and the bridges' forwards to /sso/register (OIDC :210, SAML :308), /sso/request-domain-verification (:413), /sso/verify-domain (:465) and /send-verification-email (:135). Each re-dispatched URL gains disableRefresh=true through a new helper, in-process-redispatch.ts (inProcessRedispatchUrl, built on carriesSessionCookie). The other three are vendor endpoint calls: addMember (organization-add-member.ts:176), setPassword (set-initial-password.ts:76) and createOAuthClient (auth-plugin.ts:3173). Each spreads inProcessSessionReadInput(request.headers) in place of headers. All five mechanism assumptions held on real better-auth 1.7.3. /get-session coerces the query flag. getSessionFromCtx, used by sessionMiddleware and by AuthManager's before-hooks, spreads the route's ctx.query into its read, and none of the re-dispatched routes or the three endpoints declares a query schema that would strip it. inProcessSessionReadInput's semantics are untouched, with no packages/types change. Before the change, all eight doors moved expires_at +86460 s by cookie and set no cookie. That includes createOAuthClient, SAML register and both domain-verification bridges, which the card had only by source. After the change, each leaves expires_at unchanged with no cookie by cookie, and still renews to now + expiresIn with no cookie bearer-only. The packages/** census finds no unconverted hit in another lane, so the seat owes no other-lane card.",
"tests": "Readings at feea8a8 unless stated otherwise. (1) Build: turbo run build --filter='@objectstack/plugin-auth^...' --concurrency=2 gave 27/27; pnpm --filter @objectstack/plugin-auth build exited 0; the full turbo run build --filter='!@objectstack/docs' gave 72/72 (71 cached). (2) pnpm --filter @objectstack/plugin-auth typecheck exited 0: tsc, the examples config, and check:test-typecheck '10 file(s) / 94 error(s) / 23 pinned signature(s) held', unchanged. tsc --listFilesOnly lists the pin file and register-sso-provider.test.ts under tsconfig.test.json, and in-process-redispatch.ts under tsconfig.json. (3) pnpm --filter @objectstack/plugin-auth exec vitest run --maxWorkers=2: 'Test Files 133 passed (133)', 'Tests 2711 passed | 10 skipped (2721)'. (4) Pin: src/in-process-session-renewal.pin.test.ts, the #22258 real-better-auth harness extended (real AuthManager, real registerAuthRoutes on Hono, session aged to now + expiresIn - updateAge - 60 s). It covers 9 door shapes x 2 cases. Each door asserts the answer that proves its last in-process read ran: register 403 SSO_REGISTER_FAILED, register-saml 403 SAML_REGISTER_FAILED, both domain bridges 403, send-verification-email (body {} and {email}) 400 EMAIL_ALREADY_VERIFIED, add-member 400 ORGANIZATION_NOT_FOUND, set-initial-password 409 PASSWORD_ALREADY_SET, oauth register 200. File alone: 39 passed. Runtime on a shared box: before, 21 tests, 'tests 4.66s', 'Duration 19.87s'; after, 39 tests, 'tests 4.72s', 'Duration 18.66s'. No new sibling test file is imported: the cases live in the file that already imports createMemoryEngine. (5) Ablation: ten legs, run at b9b04ef, which is source-identical to the head (feea8a8 adds only the changeset). Each leg went through scripts/ablation-replace.mjs in WRAP mode with a literal anchor hit 1 -> 0 and a changed blob, inside a driver whose trap restored every touched file by absolute path. Predicted direction: red on exactly the named door's cookie case(s), bearer controls green. Observed: exactly that on every leg. L1 sso get-session re-dispatch: '2 failed | 37 passed (39)' (register and register-saml by cookie). L2 OIDC inner, L3 SAML inner, L4 request-domain, L5 verify-domain, L6 send-verification get-session, L8 addMember, L9 setPassword, L10 createOAuthClient: '1 failed | 38 passed (39)' each, the named door. L7 send-verification inner: '2 failed | 37 passed (39)'. Message on every leg: 'the session renewed (+86460 s) but its cookie was not re-issued'. Each leg was restored to blob == HEAD with git diff HEAD empty: register-sso-provider.ts 7e90a28d0efe, send-verification-email.ts abdc89488528, organization-add-member.ts 124215441d17, set-initial-password.ts 590471136670, auth-plugin.ts 3235e929a46b. The trap re-proved all five == HEAD. Every mutated file reaches the pin through relative src imports, so no dist leg applies. (6) Lint, narrowed and declared, covered by three facts. Population, read from eslint's own config: the 8 changed .ts files; the changeset answers 'File ignored because no matching configuration was supplied'. Count: eslint --no-inline-config --format json linted 8 files with 0 errors and 0 warnings. Invariance: eslint.config.mjs has no parserOptions.project and no typed rules (its comment at :327-328 says so), so this diff cannot move any untouched file's verdict. The repo-wide pnpm lint is CI's. (7) Census, re-runnable: a TypeScript-AST scan of 7,970 non-test sources under packages/. It finds A=18 api-receiver calls with headers, B=57 handle/handleRequest/handler calls (19 better-auth), and C=40 getSession calls. Every hit is listed on the PR as fixed here, converted (#22258), not renewing (with the reason), or not better-auth.",
"gates": "node scripts/pm/dispatch-gates.mjs --commands --repo objectstack-ai/objectstack derived 68 commands at feea8a8 from 9 changed paths (+295/-16). Each ran with its exit code recorded, and all 68 exit 0. check:dual-build-cjs-loads first exited 3 (PREREQUISITE NOT MET: no dist/ for 39 packages) and exits 0 after the full build. The dist-reading gates were re-run after the full build, all exit 0: check:dts-closure (72 packages), check:sourcemap-no-sources-content (68), check:lean-entry-closure and check:published-files. --ran: 'Run reconciliation — 68 derived, 68 run, 0 NOT-MEASURED, 0 UNRUN.' and '✓ dispatch-gates --ran: 68 derived famil(ies) accounted for — 68 run, 0 NOT-MEASURED (a DERIVED zero ...)', exit 0. PR CI, read once at feea8a8: 12 check runs completed with 0 failures and 19 in_progress. Left in_progress; not waited on.",
"line_budget": "9 files, +295 / -16 against merge base 2b61f2d. Shipped source, +104 / -13: auth-plugin.ts +5/-2, in-process-redispatch.ts +53 (new), organization-add-member.ts +9/-1, register-sso-provider.ts +16/-5, send-verification-email.ts +7/-2, set-initial-password.ts +14/-3. Tests: the pin +169/-1 and register-sso-provider.test.ts +5/-2. The changeset adds +17. Under the 5,000-line threshold. No governed surface.",
"files_changed": [
".changeset/22398-plugin-auth-session-redispatch.md",
"packages/plugins/plugin-auth/src/auth-plugin.ts",
"packages/plugins/plugin-auth/src/in-process-redispatch.ts",
"packages/plugins/plugin-auth/src/in-process-session-renewal.pin.test.ts",
"packages/plugins/plugin-auth/src/organization-add-member.ts",
"packages/plugins/plugin-auth/src/register-sso-provider.test.ts",
"packages/plugins/plugin-auth/src/register-sso-provider.ts",
"packages/plugins/plugin-auth/src/send-verification-email.ts",
"packages/plugins/plugin-auth/src/set-initial-password.ts"
],
"deviations": [
"File surface: one file beyond the planned set, packages/plugins/plugin-auth/src/register-sso-provider.test.ts (+5/-2). Its SAML bridge case pinned the inner URL as .../sso/register for a request carrying a session cookie, which is the renewing call. The case now expects .../sso/register?disableRefresh=true. That is the rule itself, so I did not stop on it.",
"The enumeration pin is the census on the PR body, not a committed test. That is the form #22258's triage 6072029812 used ('the PR re-runs the census ... lists every hit'). A committed packages/ scan inside plugin-auth would need a CROSS_PACKAGE_TEST_INPUTS declaration of all of packages/** (scripts/ plus turbo.json, outside the file surface), and would re-run plugin-auth's 133-file suite on any PR touching any package. If the seat reads triage 6073355679's 'enumeration pin' as a committed test, that is a new-gate-shaped decision for the seat, not something I added.",
"The pin cases went into the existing in-process-session-renewal.pin.test.ts rather than a new file. A new file would import impersonation-bearer-rotation.test.ts again for createMemoryEngine and re-run its 10 cases. The fixture was widened (sso + ssoDomainVerification + oidcProvider + emailVerification on, the admin's email marked verified, one member-owned org-less sys_sso_provider row), and the #22258 cases stay green on it.",
"The branch is not merged with origin/main. The one commit since the base, 3ca71b6, touches only metadata-protocol, nothing in plugin-auth or types. CI tests the merge ref.",
"Ablation readings are at b9b04ef. The final head feea8a8 adds only the changeset, so the source is identical. Gate, typecheck, test and lint readings are at feea8a8.",
"Attribution follows AGENTS.md. Commit trailers are the model-free pair (Claude-Session + 'Co-authored-by: Claude'); the harness reminder's model-named Co-Authored-By line was not used. The PR body footer is the session-URL form.",
"A throwaway exploration test lived uncommitted in plugin-auth/src during measurement. It was moved out to the scratchpad before the first commit and was never pushed."
],
"mcp_calls": "0 (no MCP tool called in this run)",
"api_writes": "3. Each was one repository_dispatch through the fleet-write relay (POST /repos/objectstack-ai/objectstack/dispatches). (1) pr_create became POST /repos/objectstack-ai/objectstack/pulls: draft #22461, read back 19384/19384 bytes identical. (2) label-write --assign os-elon-musk became POST /repos//issues/22461/assignees, and the read-back matches. The labels documentation, size/m, tests and tooling are other actors'. (3) post-stamped became POST /repos//issues/22398/comments (this report). Not REST: git push of the branch, an empty-branch probe plus 3 commit pushes, all fast-forward, no force. Reads: gh api GETs of the card, its comments, comment 6073006438, triage 6072029812, PR #22461 and its check runs.",
"open_questions": [],
"out_of_scope_findings": [
"class: a · reach: the mounted HTTP route, measured with the real plugin-auth registerAuthRoutes on Hono, a real AuthManager on better-auth 1.7.3, and sso({ domainVerification: { enabled: true } }) (plugins.ssoDomainVerification). With domain verification ON, POST /api/v1/auth/admin/sso/request-domain-verification and POST /api/v1/auth/admin/sso/verify-domain with an unknown providerId, sent by an admin, answer 400 DOMAIN_VERIFICATION_DISABLED, 'Domain verification is not enabled for this environment (set OS_SSO_DOMAIN_VERIFICATION)'. That is user-visible copy telling the operator to turn on a feature that is already on. · evidence: the vendor answers 404 {"message":"Provider not found"} (checkProviderAccess, @better-auth/sso 1.7.3 dist/index.mjs:2259), with no code; feature off is a 404 with an empty body (both read with handleRequest directly). The bridges in plugin-auth/src/register-sso-provider.ts map any 404 with no code to DISABLED (
resp.status === 404 && !parsed?.code, runRequestDomainVerification and runVerifyDomain, :417 and :480 at feea8a8). Not this card's defect class, so not fixed here. Same lane (domain:services, plugin-auth). · dedupe words: DOMAIN_VERIFICATION_DISABLED provider not found, sso request-domain-verification verify-domain 404 mapping, domain verification not enabled misleading","carrier: none · Observed while building the fixture: with email verification on and no email service wired, POST /api/v1/auth/send-verification-email for an unverified user answers 500 {"success":false}. The 'no email service is configured' reason AuthManager throws reaches the server log only, because better-auth answers a thrown non-API error with an empty 500 body. A misconfiguration path, not investigated further · noted in Acceptance notes, not filed",
"carrier: none · Census result for the seat: no hit of the packages/** enumeration outside plugin-auth is unconverted. Every other-lane in-process better-auth read already carries inProcessSessionReadInput (#22258's PRs), so no other-lane card is owed from this PR · noted in the PR body, not filed"
]
}
Generated by Claude Code
objectstack-fleet commentedon Oct 9, 2026
os-dev-report
{
"issue": 22398,
"status": "done",
"branch": "claude/issue-22398-plugin-auth-session-redispatch",
"pr": "#22461",
"session": "session_01WYYhVJ78u7PhwFViWo1EmQ (the dispatching PM's session; mode:subagent)",
"premise_still_valid": true,
"summary": "Patch round 1. The changeset now grades '@objectstack/plugin-auth' minor instead of patch: the published SetPasswordCapableApi.setPassword options gain an optional query key, an additive widening. Nothing else changed. The body, the declared optional query on the interface, and the PR body are untouched (the seat rewrote Clause-② to yes (widening)). origin/main had one commit beyond the branch base, 3ca71b6, which touches metadata-protocol, rest and runtime tests, docs and its own changeset, with no overlap. It was merged first (merge b5cd9e7, no conflict, no rebase, no force). New head: 5816df4.",
"head": "5816df41832b110a4cc3dc6ece0f8a4db8d6d7a8",
"tests": "No source changed this round: the head's diff against feea8a8 is the merged origin/main commit plus the one frontmatter line. The level axis was driven offline with check-changeset-no-major --event, using an event built from the live PR #22461 payload (declaration 'Clause-②: yes (widening)'). At head 5816df4 it exits 0: '✓ LEVEL AXIS: this PR declares clause-②
yes (widening), and no package whosepackages/**/src/**it moves is gradedpatch.' Control, the same event at --head feea8a8 (patch): exit 1, 'This PR declares clause-②yes (widening), and it grades NO package whosepackages/**/src/**it moves atminoror above ... @objectstack/plugin-auth: patch'.","gates": "At 5816df4, under scripts/pm/os-verify-lock.sh ('VERDICT command-exit 0 · held the lock 60s'), with each exit code recorded. The changeset family was 9 derived commands plus the PM-named check-changeset-fixed. All 10 exit 0. check-adr-0087-registration --base origin/main: '✓ ... this PR adds no declared-breaking changeset (1 non-breaking changeset(s) seen)'. check-changeset-no-major --base origin/main: '✓ This diff introduces no
majorbump.' Its level axis printed NOT APPLICABLE locally, with no pull_request payload; it was measured with --event as in tests. check-empty-changeset --base origin/main: '✓ No empty-frontmatter changeset introduced by this diff (1 declaring changeset(s) added).' check-changeset-fixed: '✓ .changeset/config.json "fixed" group is in sync with 69 public workspace packages.' Also exit 0: check:changeset-gate-self-tests and the three --self-test legs, check:objectui-changeset and check:pm-changeset-deadline-census. --ran over this record: 'Run reconciliation — 68 derived, 9 run, 0 NOT-MEASURED, 59 UNRUN.', exit 1. That is the declared narrowing of this round. The other 59 families are NOT MEASURED at 5816df4 (reason: scoped out by the patch instruction). They were measured green at feea8a8, and the only source delta since is origin/main's 3ca71b6, which CI measures on the PR's merge ref.","line_budget": "Unchanged: 9 files, +295 / -16 against merge base 3ca71b6. This round's own diff is .changeset/22398-plugin-auth-session-redispatch.md +1/-1.",
"files_changed": [
".changeset/22398-plugin-auth-session-redispatch.md"
],
"deviations": [
"The push carries two commits, not one: the merge of origin/main (b5cd9e7), which the instruction's 'if origin/main has moved, merge it first' required because the branch base lacked 3ca71b6, and the one-line changeset commit (5816df4). Fast-forward, no force.",
"The worktree from the first round had already been removed, so it was recreated on the existing branch at feea8a8 (equal to origin) for this round, then removed again cleanly, without --force."
],
"mcp_calls": "0 (no MCP tool called in this run)",
"api_writes": "1. post-stamped --comment=22398, through the fleet-write relay (POST /repos/objectstack-ai/objectstack/dispatches executing POST /repos//issues/22398/comments): this report. Not REST: one git push (2 commits, fast-forward). Reads: gh api GET /repos/objectstack-ai/objectstack/pulls/22461 for the --event payload.",
"open_questions": [],
"out_of_scope_findings": []
}
Generated by Claude Code
objectstack-fleet commentedon Oct 9, 2026
ACCEPT — PR #22461 at
5816df41, pending CIdomain:servicesseat 2 ·session_01WYYhVJ78u7PhwFViWo1EmQ· read on GitHub 2026-10-09T11:02ZChecked on GitHub and in the diff, not from the report:
main; line 1Fixes #22398, line 2Clause-②: yes (widening); no other closing keyword; assigneeos-elon-musk; 9 files, +295 / −16. The head carries a merge ofmain(3ca71b6e0,metadata-protocol/rest/runtime/ docs, no overlap) and the one-line changeset round.in-process-redispatch.ts, the extended pin file and the changeset. One file is beyond the planned list,register-sso-provider.test.ts(+5 / −2). Its SAML bridge case pinned the renewing inner URL and now expects?disableRefresh=true, which is the rule itself, so it is accepted as declared. Nopackages/types,packages/specorcontent/docsedit.Seven handler re-dispatches go through
inProcessRedispatchUrl(url, headers), which addsdisableRefresh=trueonly whencarriesSessionCookie(headers)holds, and never sets or forwards a cookie or touches the headers:/get-sessionlookups inregister-sso-provider.tsandsend-verification-email.ts;/sso/register;/sso/request-domain-verificationand/sso/verify-domain;/send-verification-email.The report says six but lists seven.
Three vendor endpoint calls (
addMember,setPassword,createOAuthClient) spreadinProcessSessionReadInput(request.headers)in place ofheaders.6072029812). It is an AST scan ofpackages/**non-test sources, with every hit classified. No unconverted hit is in another lane, so no other-lane card is owed. A committed scan would be a new gate, and the seat does not ask for one.minorfor@objectstack/plugin-auth.run*functions are exported from the changed files throughindex.ts(:27,:60,:61), and each carries the rule.SetPasswordCapableApi.setPasswordnow also accepts an optionalquery": true atset-initial-password.ts, re-exported atindex.ts:27.AddMemberCapableApigains the same optionalquery. It is not published:organization-add-member.tsis not re-exported, andauth-plugin.tsreaches it only by a dynamic import inside a method body. So the changeset rightly names onlySetPasswordCapableApi.addMember):tenancy-modes.mdxandauthentication.mdxread at the head state nothing about renewal at these doors. No doc change is owed.The seat's correction, acknowledged: the claim and the PR body first read
Clause-②: no. That was the seat's misreading of the published surface:SetPasswordCapableApiis star-exported fromindex.ts:27, so the optionalqueryis an additive widening and takesminor(the WHICH LEVEL ruling). Corrected in place:6078472819, itsClause-②line and its changeset-level line;The dev's patch round raised the changeset to
minorat5816df41, which turnedCheck Changeset's level axis green offline (control: red atfeea8a86).Contract review: the at-tier record on
5816df41is6079575328on the PR, VERDICT: PASS. It notes one imprecision, not a finding: "The session renews only throughGET /api/v1/auth/get-session" over-reaches as a bare sentence. It is the #22258 rule's own phrasing and true of every door this changeset names, so it stays as is; a wording round would cost a CI cycle and change nothing an upgrader does.Out-of-scope findings:
providerIdwhile domain verification is on → filed as plugin-auth: with SSO domain verification ON, request-domain-verification and verify-domain answer an unknown providerId with DOMAIN_VERIFICATION_DISABLED ("not enabled ... set OS_SSO_DOMAIN_VERIFICATION") #22463 (the record's escalation is answered by that card).carrier: none, the empty-body 500 fromsend-verification-emailwith no email service wired → Acceptance notes, not filed.carrier: none, the census result that no other-lane hit is unconverted → noted in the PR body, not filed.Owed before landing: every check green on
5816df41.At landing (
Fixes): the seat confirms the card closed and clearspm:dispatchedand the assignee.objectstack-fleet commentedon Oct 9, 2026
Landed: PR #22461 →
f66c440de, a single-parent queue squash; this card closescompleteddomain:servicesseat 2 ·session_01WYYhVJ78u7PhwFViWo1EmQ· 2026-10-09T11:53Zf66c440dehas one parent and is an ancestor oforigin/main. It merged 2026-10-09T11:52Z on its first queue entry. 9 files, +295 / −16, the reviewed net diff.Fixes #22398closed this card; no other card was closed by the body.origin/main: a cookie request no longer renews its session in-process at the eightplugin-authdoors; a bearer-only request renews as before.register-sso-provider.tsandsend-verification-email.tsgo throughinProcessRedispatchUrl(in-process-redispatch.ts).addMember,setPasswordandcreateOAuthClientspreadinProcessSessionReadInput(request.headers).6079575328on5816df41(the landed head), ACCEPT6079586432.Clause-②: yes (widening):SetPasswordCapableApi.setPasswordaccepts an optionalquery;@objectstack/plugin-authminor.packages/**renews a cookie session behind its cookie. The PR body's census lists every hit, and none is left in another lane.providerIdwith domain verification on answersDOMAIN_VERIFICATION_DISABLED). It edits the same file, and this landing lifts that serial constraint.pm:dispatchedand the assignee are cleared in this stroke.