Skip to content

plugin-auth: a session minted by mintPrincipalSession never boots the console — the console loops on boot, so #22269 cannot render under the requester #23813

Closed

Description

@objectstack-fleet

Sub-issue of #22269 (in scope: without it #22269's acceptance fails), cut by the claiming seat domain:services seat 2 (#21118) · session_018VqJPGA1NDNSCZsnecsChP. Domain and priority are the parent's (execution-duties.md, in-flight derivation). ⛔ Not a claim.

What was measured

From #22269's dev report 6208035645 (open_questions[0]), with #23772's mint (PR #23786, 9cb9d95574) merged into the dev's local tree:

  • A session minted by mintPrincipalSession(context, …) keeps the real console (.objectui-sha f96f771140, built by scripts/build-console.sh) on "Opening your console..." for a member and an administrator alike. The console loops get-session, organization/get-full-organization and organization/list.
  • A normal sign-in session for the same user renders the same print page on both driver placements.
  • On each get-session read the minted session's updatedAt advances (at 2026-10-16T02:44: 41.671 → 42.080 → 42.117), and expiresAt is clamped back by holdPrincipalSessionExpiry. So the TTL holds, as auth(spec,plugin-auth): a short-lived principal-session mint, an optional IAuthService member with its AuthManager implementation, the seam #22269's print service consumes (S2 of #22269) #23772's pins assert, but a renewal write still happens on every read.
  • Cause: indicated, not proven. The dev's experiment aborting the renewal write was declined by its session's permission classifier. A second hypothesis to rule out: the minted session's active organization (the loop includes both organization reads).

What to build

  1. Measure the cause first, on a real better-auth plus a booted console, both hypotheses, with the readings in the report.
  2. Fix it in packages/plugins/plugin-auth (src/principal-session-mint.ts, the AuthManager mint region), so a minted cookie boots the console. ⛔ No change to the IAuthService member's contract in packages/spec; if the fix needs one, stop and report it.
  3. Pin it: a minted session boots the console (its get-session answers without a renewal write, and the boot sequence completes), alongside auth(spec,plugin-auth): a short-lived principal-session mint, an optional IAuthService member with its AuthManager implementation, the seam #22269's print service consumes (S2 of #22269) #23772's existing pins (the TTL, no extension, revoke).

Governing text

#8346 ruling B′ 6051470224 (M2: "under the requesting user's principal (a short-lived credential; no system principal)"); #22269 seat rulings 6059363716 (OQ1 A); placement ruling 6204149204 (the pins apply to both placements); #23772 (the seam, closed completed).

Dedupe

MCP search_issues on this repo, closed included, at filing: "minted principal session renews on get-session console never boots mintPrincipalSession" → 0 hits. Positive control "short-lived principal session mint IAuthService member AuthManager" → #23772 and #22398 (the cookie-session renewal residue of #22258, closed), so the instrument reads this corpus.


Generated by Claude Code

Activity

  1. objectstack-fleet commented on Oct 16, 2026

    @objectstack-fleet
    ContributorAuthor

    Claim: PM loop round 4 · 2026-10-16T04:32Z
    Session: session_018VqJPGA1NDNSCZsnecsChP (the dispatching seat; the dev runs as a cloud session that seat post #21118 names)
    Account: os-elon-musk (the seat's linked user as GET /user answers it; the card's assignee from this act)
    Branch: claude/issue-23813-minted-session-console-boot
    Worktree: objectstack-issue-23813 (in the cloud session's container)
    Domain: domain:services
    Seat: domain:services#2 (seat post #21118)
    File surface (at origin/main 74b436263a or later; stop on a breach and explain it in the report): packages/plugins/plugin-auth/src/principal-session-mint.ts, the mint and session-hook regions of packages/plugins/plugin-auth/src/auth-manager.ts (mintPrincipalSession near :8341, the holdPrincipalSessionExpiry hook near :7859), their tests, one end-to-end pin that boots the console with a minted cookie (under packages/qa/dogfood/test/ or beside the mint's real-better-auth tests), and .changeset/23813-*.md. ⛔ Not packages/spec/** (the IAuthService member's contract stays). ⛔ Not packages/services/service-print/** (#22269's, in review as PR #23811).
    Container & model: M, mode:cloud (measurement on a real better-auth and a booted console with Chromium), model: opus (TIER_DEFAULT; dispatch-gates --tier --repo objectstack-ai/objectstack: "no path-derived mandate")
    Clause-②: no — the fix changes how a minted session behaves on read, inside the existing member's contract; no export, type or accept set moves. If the dev measures otherwise on the built declaration closure, it reports it.
    Responsibility: plugin-auth (this lane): a session minted by mintPrincipalSession is renewed on every get-session read and the console never leaves boot | none: no other path mints a session for the requesting user (#23772's measured fact) | every render of #22269's print service, so the archived PDF of #8346 M2 (target:v18, PDF for 18.0 GA)
    Thread-read: none
    Ruling-ref: #8346 B′ 6051470224 (M2); #22269 seat rulings 6059363716 (OQ1 A); placement ruling 6204149204.
    Serial constraints cleared: no open PR touches packages/plugins/plugin-auth/** (all open PRs' file lists read at this write). #23772 (9cb9d95574) landed the mint this card fixes. #22269's PR #23811 consumes it and binds after this lands.


    Generated by Claude Code

  2. objectstack-fleet commented on Oct 16, 2026

    @objectstack-fleet
    ContributorAuthor

    Dev handoff under the same claim (6208138185) · domain:services seat 2 · session_018VqJPGA1NDNSCZsnecsChP · 2026-10-16T05:18Z. ⛔ Not a new claim.

    • The first dev session session_019ni7tfMFU9R24GCL96m41d stopped at 2026-10-16T04:35Z. Its permission classifier blocked three actions in a row while it tried to delete the harness's pre-created local branch to recreate it in a worktree ("Git Destructive"), and the session then waited on a human review prompt. A PM interrupt and resume message did not clear it. It produced no commit, and claude/issue-23813-minted-session-console-boot was never pushed (git ls-remote read empty). It is archived.
    • The dev now is session_0138Lk1GC4xpkss8esKzWVFA, on the same branch and order, with a non-destructive worktree setup (detach the primary checkout; git worktree add on the existing branch).

    Generated by Claude Code

  3. objectstack-fleet commented on Oct 16, 2026

    @objectstack-fleet
    ContributorAuthor

    os-dev-report

    {
     "issue": 23813,
     "status": "done",
     "branch": "claude/issue-23813-minted-session-console-boot",
     "pr": "https://github.com/objectstack-ai/objectstack/pull/23828",
     "session": "session_0138Lk1GC4xpkss8esKzWVFA (cloud dev dispatched by domain:services seat 2, session_018VqJPGA1NDNSCZsnecsChP; takeover of archived session_019ni7tfMFU9R24GCL96m41d, which pushed nothing)",
     "premise_still_valid": true,
     "summary": "Cause proven. better-auth refreshed a minted session on every read: #23772's hold kept expiresAt, but the refresh still rewrote updatedAt, re-set the session cookie (7-day Max-Age) and made bearer() answer set-auth-token. The console's TokenStorage.subscribeRotation reads that header as a rotation and re-runs loadSession, and the new user re-runs refreshOrganizations, whose session reads refresh again, so the console loops. Hypothesis 2 (active organization) is ruled out: the same activeOrganizationId boots once the refresh is gone. Fix in packages/plugins/plugin-auth only: carriesPrincipalSession(ctx) in principal-session-mint.ts, and the global hooks.before (first hook on every endpoint, ahead of bearer()) calls better-auth's own request-scoped setShouldSkipSessionRefresh(true) for a request carrying a minted session, so no endpoint renews it on the cookie lane, the bearer lane or in-process. holdPrincipalSessionExpiry stays as the backstop. No IAuthService or packages/spec change; the built .d.ts closure is byte-identical.",
     "tests": "Head d4839df3ad. Reproduction (scratch probe, real better-auth via AuthManager.handleRequest, pre-fix): every minted get-session on the cookie and bearer lanes answered set-auth-token=true plus a better-auth.session_token Set-Cookie, updated_at advanced on each read (05:25:36.005 -> .011 -> .017 -> .021Z) while expires_at held. Post-fix: set-auth-token=false, no Set-Cookie, updated_at unchanged. New pin plugin-auth/src/principal-session-mint.no-refresh.pin.test.ts: cookie|bearer x /get-session|/list-sessions, 3 reads each with no set-auth-token, no session Set-Cookie and updated_at/expires_at unchanged, plus a control that the same read refreshes an unbranded short session, plus carriesPrincipalSession unit cases. New e2e qa/dogfood/test/principal-session-console-boot.dogfood.test.ts: showcase booted in-process with the built console (.objectui-sha daeb77fc84, scripts/build-console.sh) composed into its plugins via the CLI's createConsoleStaticPlugin, real Chromium (/opt/pw-browsers/chromium), every request answered by the real Hono app, minted cookie only. Results 3/3: control (normal sign-in admin), minted admin and minted member each boot with 1 get-session, 0 after the 3 s settle window, 0 set-auth-token. ABLATION (scripts/ablation-replace.mjs on the auth-manager.ts switch line, gated on a never-set env var; injected 1 / removed 0; ablation-dist-preflight marker present in 2 dist files, exit 0; plugin-auth rebuilt). No-refresh pin: 4 failed, 43 passed, 'read 1 reissued the minted session'; #23772's cookie- and bearer-lane pins stayed green (the hold holds). E2E: minted admin red {booted:false, getSessionDuringBoot:553, getSessionAfterSettle:65, setAuthTokenResponses:1238}, minted member red {651, 64, 1433}, control green. Restore: git checkout HEAD --, HEAD-blob hash matched, git diff HEAD 0 lines, rebuild, marker absent from all 14 dist files (dist reading exit 0; the tree leg exited 3 only because 2 of my own untracked files, the changeset and a debug copy, differed from HEAD; the debug copy was then deleted). Full suites: pnpm --filter @objectstack/plugin-auth test 150 files, 2942 passed, 10 skipped, exit 0; plugin-auth typecheck exit 0 (its tsconfig excludes **/*.test.ts, so the new unit pin is transpiled, not type-checked); dogfood e2e 3/3 and dogfood typecheck exit 0 on the final head (cli/console aliased to source).",
     "gates": "node scripts/pm/dispatch-gates.mjs --commands --repo objectstack-ai/objectstack (no paths) gave 82 commands at head d4839df3ad. Each run with stdin /dev/null and the exit code recorded before any pipe: 82 x exit 0. --ran: '82 derived famil(ies) accounted for — 82 run, 0 NOT-MEASURED (a DERIVED zero ...)', exit 0. First pass at f4a1051e: check:test-source-alias exit 1 (a real red: dogfood must alias @objectstack/cli/console to source), fixed in d4839df3ad. check-plugin-teardown-shape --self-test exit 3 (shallow clone), fixed by git fetch --unshallow origin main. check:dual-build-cjs-loads exit 3 (8 packages without dist), fixed by building them. All three green on the final pass. PR check-runs: not read; CI convergence is the PM's per contract.",
     "mcp_calls": "0",
     "api_writes": "2 — POST /repos/objectstack-ai/objectstack/dispatches (fleet-write relay pr_create, run 38862810611 success; its assignee leg POST /repos/objectstack-ai/objectstack/issues/23828/assignees in the same stroke; read-back: 8138 bytes sent, 8138 stored, identical, assignees os-elon-musk) and this os-dev-report comment POST /repos/objectstack-ai/objectstack/issues/23813/comments via scripts/pm/post-stamped.mjs. Plus git push (not REST) of the branch. Reads used gh api (REST GET) for the issue, its comments and comment 6208035645. No label writes (the budget names none).",
     "line_budget": "509 additions + 2 deletions vs merge-base d7957199 (8 files; pnpm-lock.yaml +59/-1 included). Non-test source: principal-session-mint.ts +48, auth-manager.ts +13/-1.",
     "files_changed": ".changeset/23813-minted-session-no-refresh.md; packages/plugins/plugin-auth/src/{auth-manager.ts, principal-session-mint.ts, principal-session-mint.no-refresh.pin.test.ts}; packages/qa/dogfood/{package.json, vitest.config.ts, test/principal-session-console-boot.dogfood.test.ts}; pnpm-lock.yaml",
     "deviations": [
      "File surface: packages/qa/dogfood/package.json (devDependency playwright-core 1.63.0, already in the lockfile via the showcase's @playwright/test), pnpm-lock.yaml, and packages/qa/dogfood/vitest.config.ts (the source alias check:test-source-alias demands) are outside the claim's named list. All three are forced by the one end-to-end pin the claim allows under packages/qa/dogfood/test/. pnpm install also re-resolved packages/apps/account's vitest peer to esbuild 0.28.1; this is tool output, not hand-edited, and is the same churn #22269's dev recorded.",
      "The fix lands in the global hooks.before of AuthManager.createAuthInstance (auth-manager.ts near :2066), not in the mint region (:8341) or the session.update.before hook (:7859). The update hook cannot stop a renewal without breaking get-session: a false abort makes updateSession answer null, and get-session then 401s and deletes the cookie. The before-hook is the one seam every endpoint passes first, ahead of bearer().",
      "My first console build ran concurrently with my own closure build in the same worktree and failed on packages/spec (a self-inflicted race). It was re-run under os-verify-lock and succeeded.",
      "First commit was refused by the pre-push check:commit-card-trailers hook (model name in the co-author trailer, injected by the harness attribution). It was amended before publishing; all pushed commits carry the model-free pair.",
      "Did not subscribe to PR activity: the dispatch contract overrides the platform's auto-watch posture.",
      "The ablation's restore leg used git checkout HEAD -- after ablation-replace --restore, and was proven by HEAD-blob hash plus an empty git diff HEAD."
     ],
     "open_questions": [
      {
       "question": "Where to stop the renewal (decided, recorded). A: better-auth's request-scoped setShouldSkipSessionRefresh from the global before-hook (taken). B: a signed dont-remember cookie (needs a second cookie in PrincipalSession, a packages/spec change, and holds on the cookie lane only). C: abort in session.update.before (get-session then 401s). D: change the console's rotation handling (a consumer-side workaround).",
       "options": [
        "A (taken)",
        "B",
        "C",
        "D"
       ],
       "recommendation": "A. Business need: measured, the print service's real render (#22269) needs it. Long-term: the fix sits at the producer, on all lanes, through better-auth's own switch, with no contract change. AI error-proofing: the credential's no-renewal promise is now enforced server-side for every endpoint, not just the expiry column. Startup focus: no new surface, 48 + 13 source lines."
      },
      {
       "question": "The e2e pin needs a built console and a Chromium. CI's Dogfood Regression Gate likely has neither, so it will print SKIPPED there. Should CI provision them for this file?",
       "options": [
        "A: no new CI step; the wire pin (no-refresh.pin, runs everywhere) carries CI, and the e2e runs in agent containers and wherever the console is built",
        "B: a governed .github/workflows step that builds the console and provisions Chromium for the dogfood gate"
       ],
       "recommendation": "A. Per the four axes, a new gate defaults to no unless the maintainer names it. The ablation showed the wire pin goes red on the same defect that loops the console."
      }
     ],
     "out_of_scope_findings": [
      "carrier: none · noted, not filed (observation): plugin-auth's tsconfig excludes **/*.test.ts, so its typecheck covers no test file; noted in the PR's Acceptance notes."
     ]
    }

    Generated by Claude Code

  4. objectstack-fleet commented on Oct 16, 2026

    @objectstack-fleet
    ContributorAuthor

    ACCEPT → PR #23828 comment 6210281350 · domain:services seat 2 · session_018VqJPGA1NDNSCZsnecsChP · 2026-10-16T07:20Z. Landing through the merge queue.


    Generated by Claude Code

  5. objectstack-fleet commented on Oct 16, 2026

    @objectstack-fleet
    ContributorAuthor

    Landed · domain:services seat 2 · session_018VqJPGA1NDNSCZsnecsChP · 2026-10-16T07:43Z.

    probe reading
    PR #23828 timeline added_to_merge_queue 2026-10-16T07:22Z → merged 2026-10-16T07:42Z through the merge queue
    merge commit on origin/main 0cfd0c49bd (git merge-base --is-ancestor exit 0)
    the fix on origin/main export function carriesPrincipalSession in principal-session-mint.ts → 1; setShouldSkipSessionRefresh(true) in auth-manager.ts → 1; control: #23772's holdPrincipalSessionExpiry in auth-manager.ts → 3 (the backstop stays)

    Generated by Claude Code

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

Metadata

Metadata

Assignees

No one assigned

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions