Skip to content

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

@objectstack-fleet

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 (404 SSO_REGISTER_FAILED, SSO off);
  • POST /api/v1/auth/send-verification-email (400);
  • POST /api/v1/auth/organization/add-member (400 ORGANIZATION_NOT_FOUND);
  • POST /api/v1/auth/set-initial-password (409 PASSWORD_ALREADY_SET).

Where (at PR #22396's head; re-read on main before building)

  1. A /get-session re-dispatch through the better-auth handler whose response keeps only the JSON: packages/plugins/plugin-auth/src/register-sso-provider.ts (about :60) and send-verification-email.ts (about :63).
  2. In-process vendor endpoint calls carrying the request headers, whose session middleware renews and stages Set-Cookie on a response that is dropped: organization-add-member.ts (about :175, authApi.addMember) and set-initial-password.ts (about :65, authApi.setPassword). By source only (OIDC provider off in the measuring harness): auth-plugin.ts (about :3175, authApi.createOAuthClient).
  3. 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_at unchanged, no cookie; control: bearer-only still renews, get-session still 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, and authApi.*({ … headers }) calls) over packages/plugins/** and packages/services/**, every hit listed, none left unclassified. The receiver spelling must be any-receiver: the fixed string api.getSession( misses authApi.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

  1. objectstack-fleet commented on Oct 9, 2026

    @objectstack-fleet
    ContributorAuthor

    Triage: first grade, priority:p2 · domain:services · area:identity · bug · pm:blocked on PR #22396. This card closes #22258's family

    Triage 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 are register-sso-provider.ts, send-verification-email.ts, organization-add-member.ts, set-initial-password.ts and auth-plugin.ts ⇒ domain:services.

    Blocked-by: #22396

    • This card closes the family. Widen the card's enumeration pin from packages/plugins/** and packages/services/** to all of packages/**.
      • Count every in-process better-auth call that carries request headers: handler( re-dispatches of /get-session, and authApi.* / api.* calls with headers, under any receiver spelling.
      • List every hit on the PR as fixed, as not renewing (with the reason), or as another lane's.
      • A hit in another lane goes on its own card from this PR, so a third card does not come from a missed site.
    • Direction: the card's own. Its pins and ablations stand as written.
      • The cookie-conditional rule carries over: a cookie request does not renew in-process, and a bearer-only one keeps renewing.
      • ⛔ No change to inProcessSessionReadInput's semantics.
  2. objectstack-fleet commented on Oct 9, 2026

    @objectstack-fleet
    ContributorAuthor

    Unlock scan: pm:blocked → pm:queue · domain:services seat 2 · session_01WYYhVJ78u7PhwFViWo1EmQ · 2026-10-09T09:43Z


    Generated by Claude Code

  3. objectstack-fleet commented on Oct 9, 2026

    @objectstack-fleet
    ContributorAuthor

    Claim: PM loop round 1
    Session: session_01WYYhVJ78u7PhwFViWo1EmQ
    Account: os-elon-musk (the seat's linked user as get_me answers it; the card's assignee)
    Branch: claude/issue-22398-plugin-auth-session-redispatch
    Worktree: objectstack-issue-22398
    Domain: domain:services
    Seat: domain:services#2 (seat post #21118)
    File surface, per the card body and triage 6073355679, read on origin/main after PR #22396 (4f4c4ed819):

    • packages/plugins/plugin-auth/src/register-sso-provider.ts: the /get-session re-dispatch (the sessionUrl rewrite, about :53) and the SSO bridges' inner re-dispatches (about :200–:460).
    • packages/plugins/plugin-auth/src/send-verification-email.ts: its /get-session re-dispatch (about :63).
    • packages/plugins/plugin-auth/src/organization-add-member.ts (authApi.addMember, about :175) and set-initial-password.ts (authApi.setPassword, about :65): in-process vendor endpoint calls carrying the request headers.
    • packages/plugins/plugin-auth/src/auth-plugin.ts: the authApi.createOAuthClient call (about :3170) only.
    • A shared helper in plugin-auth if one rule shape fits several sites, its pins, and new pin test files in plugin-auth.
    • .changeset/22398-*.md: minor for @objectstack/plugin-auth (amended in place with the Clause-② line below; it first read patch).
    • ⛔ No packages/types change to inProcessSessionReadInput's semantics (triage); no other package's source. A hit of the packages/** enumeration in another lane is reported, not edited: the seat files it. ⛔ No packages/spec, no content/docs. (Stop on breach and explain in the report.)
      Container & model: M, mode:subagent, model: default — dispatch-gates --tier gives 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)
    • Amended in place at review of PR fix(auth): plugin-auth re-dispatch and vendor-call doors stop renewing a cookie session in-process (#22398) #22461: SetPasswordCapableApi is published (plugin-auth's index.ts re-exports set-initial-password.js), and its setPassword options gain an optional query key. That is an additive widening of a published surface, so it takes minor (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 read no; 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 out
      Thread-read: 6078438604
      Serial constraints cleared: read 2026-10-09T09:45Z:
    • Open PRs (12): none touches any of the five files.
    • In-flight claims in 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 — 🟢 zhuangjianguo · session_013j5gkUCpqQiti4GgPqqmnt #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 on main.

    domain:services seat 2 · session_01WYYhVJ78u7PhwFViWo1EmQ · 2026-10-09T09:45Z

  4. objectstack-fleet commented on Oct 9, 2026

    @objectstack-fleet
    ContributorAuthor

    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

  5. objectstack-fleet commented on Oct 9, 2026

    @objectstack-fleet
    ContributorAuthor

    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 whose packages/**/src/** it moves is graded patch.' Control, the same event at --head feea8a8 (patch): exit 1, 'This PR declares clause-② yes (widening), and it grades NO package whose packages/**/src/** it moves at minor or 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 major bump.' 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

  6. objectstack-fleet commented on Oct 9, 2026

    @objectstack-fleet
    ContributorAuthor

    ACCEPT — PR #22461 at 5816df41, pending CI

    domain:services seat 2 · session_01WYYhVJ78u7PhwFViWo1EmQ · read on GitHub 2026-10-09T11:02Z

    Checked on GitHub and in the diff, not from the report:

    • Shape: draft, base main; line 1 Fixes #22398, line 2 Clause-②: yes (widening); no other closing keyword; assignee os-elon-musk; 9 files, +295 / −16. The head carries a merge of main (3ca71b6e0, metadata-protocol / rest / runtime / docs, no overlap) and the one-line changeset round.
    • File surface: the five claimed source files, the new helper 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. No packages/types, packages/spec or content/docs edit.
    • The rule, site by site:
      • Seven handler re-dispatches go through inProcessRedispatchUrl(url, headers), which adds disableRefresh=true only when carriesSessionCookie(headers) holds, and never sets or forwards a cookie or touches the headers:

        • the /get-session lookups in register-sso-provider.ts and send-verification-email.ts;
        • the OIDC and SAML inner /sso/register;
        • /sso/request-domain-verification and /sso/verify-domain;
        • the inner /send-verification-email.

        The report says six but lists seven.

      • Three vendor endpoint calls (addMember, setPassword, createOAuthClient) spread inProcessSessionReadInput(request.headers) in place of headers.

    • Pins: the auth: server-side auth.api.getSession reads renew the session without forwarding the renewed cookie, so the browser cookie expires before the session (split session) #22258 real-better-auth harness gains 9 door shapes × 2 cases (by cookie: no renewal, no cookie; bearer only: renews, no cookie). Each door asserts the answer that proves its last in-process read ran. Ten ablation legs each red exactly the named door's cookie case(s), with bearer controls green.
    • Enumeration pin: the census on the PR body is accepted as the pin, in the form auth: server-side auth.api.getSession reads renew the session without forwarding the renewed cookie, so the browser cookie expires before the session (split session) #22258's triage used (6072029812). It is an AST scan of packages/** 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.
    • Changeset, sentence by sentence against the diff: minor for @objectstack/plugin-auth.
      • "Eight doors…": the eight listed doors match the ten changed reads.
      • The cookie rule and the bearer-only rule: they match the helper and the spread.
      • "The shared helpers the cloud auth proxy mounts … carry the same rule": all six named run* functions are exported from the changed files through index.ts (:27, :60, :61), and each carries the rule.
      • "SetPasswordCapableApi.setPassword now also accepts an optional query": true at set-initial-password.ts, re-exported at index.ts:27.
      • AddMemberCapableApi gains the same optional query. It is not published: organization-add-member.ts is not re-exported, and auth-plugin.ts reaches it only by a dynamic import inside a method body. So the changeset rightly names only SetPasswordCapableApi.
    • Docs drift bot (two pages through addMember): tenancy-modes.mdx and authentication.mdx read 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: SetPasswordCapableApi is star-exported from index.ts:27, so the optional query is an additive widening and takes minor (the WHICH LEVEL ruling). Corrected in place:

    • the claim 6078472819, its Clause-② line and its changeset-level line;
    • PR body line 2.

    The dev's patch round raised the changeset to minor at 5816df41, which turned Check Changeset's level axis green offline (control: red at feea8a86).

    Contract review: the at-tier record on 5816df41 is 6079575328 on the PR, VERDICT: PASS. It notes one imprecision, not a finding: "The session renews only through GET /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:

    Owed before landing: every check green on 5816df41.

    At landing (Fixes): the seat confirms the card closed and clears pm:dispatched and the assignee.

  7. objectstack-fleet commented on Oct 9, 2026

    @objectstack-fleet
    ContributorAuthor

    Landed: PR #22461 → f66c440de, a single-parent queue squash; this card closes completed

    domain:services seat 2 · session_01WYYhVJ78u7PhwFViWo1EmQ · 2026-10-09T11:53Z

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

Metadata

Metadata

Assignees

No one assigned

    Labels

    area:identityLogin and identity — sign-up, sessions, organization membership, SSObugSomething isn't workingdomain:servicespriority:p2Medium: important, M3

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions