You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
{{ message }}
Repository navigation
fix(plugin-auth): a failed read in the last-administrator guard's default-slug capture rule answers 403 PERMISSION_DENIED, while auth-manager's sibling slug guard answers 503 for the same fault — give the read fault one code #25364
Filing gate ③: a finding of the contract review of PR #25286 (#25271; record 6352123147, ① 4), carried on the #15212 ledger (6352526831). It is outside that PR's surface.
The sibling slug guard in auth-manager.ts answers the same kind of fault with 503 SERVICE_UNAVAILABLE (slugGuardReadFaultApiError).
So a caller cannot tell "you may not" from "try again" by code on this door.
Wanted
One code for a read fault across both guards. Most likely 503 SERVICE_UNAVAILABLE, or a dedicated code with its own row in packages/spec's error-code ledger (ADR-0112).
The refusal on the merits stays 403 PERMISSION_DENIED.
Measure first: every half of the last-administrator guard uses failClosed. Say whether to change one half or the shared shape, because changing one half alone splits the guard's vocabulary.
Acceptance
A read fault answers the same code from both guards, pinned at the HTTP door (organization/update) and the engine door.
Claim: PM loop round 1
Session: session_01CuJfNjvts78GKesupEdgbz (a card thread scheduled by the project's coordinator, which follows .claude/agents/os-dev.md as its role file; the seat session is session_014Hse9cj2i4o5UAVrd21NKr)
Account: os-tesla
Branch: claude/project-thread-ef9bkc (the thread's designated branch: a card thread can push only that branch, a measured platform exception to claude/issue-<n>-<slug>)
Worktree: objectstack-issue-25364
Domain: domain:engine
Seat: domain:engine#1 (seat post #6367)
File surface: packages/plugins/plugin-auth/src/last-admin-guard.ts (failClosed and its read-fault refusal) + its tests, plus any repo-wide pin on the old read-fault code (a domain:services package the seat declares) (stop on breach; explain in the report)
Container & model: S (non-mechanical, so M treatment), mode:thread, model: TIER_DEFAULT (dispatch-gates --tier: no path-derived mandate), effort: high
Clause-②: no
Metadata-protocol: none
Responsibility: plugin-auth's last-administrator guard produces it | the sibling slugGuardReadFaultApiError already answers 503 | any caller of organization/update during a read fault, rare and fail-closed either way
Thread-read: none
Serial constraints cleared: #25338 (this seat, in flight) edits plugin-auth's create-user route, a different file. No other in-flight card of either engine seat edits last-admin-guard.ts; epic #15212 introduced the rule in #25286 (merged).
Clause-② reading: no, an existing code is reused and a fail-closed refusal keeps refusing; a needed new code stops the dev. Dispatch order: /mnt/project-files/engine-seat1/dispatch-25364.md (+ common.md). Ruling carried: none (governing text ADR-0112). Claimed 2026-10-23T04:51Z.
{
"issue": 25364,
"status": "done",
"branch": "claude/project-thread-ef9bkc",
"pr": "https://github.com/objectstack-ai/objectstack/pull/25388",
"head": "3aee19812a51779419547eff2374614e19c0828f",
"session": "session_01CuJfNjvts78GKesupEdgbz",
"premise_still_valid": true,
"summary": "Measured: every judging half of the last-administrator guard runs in failClosed, which answered a read fault 403 PERMISSION_DENIED; the Default Organization slug-capture rule has its own catch with the same 403. Changed the shared shape: a module-private readFault() (code SERVICE_UNAVAILABLE, status 503, object, cause) thrown by failClosed and the slug rule, both passing an inner read fault through so nested envelopes do not double-wrap. Two doors the refusal crosses had to follow in the same package: objectql-adapter.ts gains a 503 arm (without it organization/update answered 500 with no code, measured by reverting only that file), and auth-manager-actor-guards.ts maps the standing judge's read fault on organization/delete to ORGANIZATION_DELETE_READ_FAULT_CODE. Refusals on the merits, including the maxScan overflow, stay 403 PERMISSION_DENIED; no refusal became an allow.",
"tests": "plugin-auth full suite at 3aee1981 under os-verify-lock: VERDICT command-exit 0, Test Files 187 passed (187), Tests 3528 passed | 10 skipped. Pins: organization/update HTTP door {status:503, code:SERVICE_UNAVAILABLE} with driver text withheld and slug unchanged; engine door {code:SERVICE_UNAVAILABLE, status:503}; 3 flipped fail-closed pins in last-admin-guard.test.ts; new: maxScan overflow stays {PERMISSION_DENIED,403}, judgeOrganizationDelete inner read fault answered once, organization/delete via the real guard 503 with nothing attempted and CONTROL 403 PERMISSION_DENIED. Reverse check (src only, tests import src, so no dist leg): the three source files restored to d12e650 gave Tests 7 failed | 137 passed over the four touched test files, every new/flipped pin red; restored from HEAD, git diff HEAD empty, porcelain clean. Adapter-only revert: HTTP pin reads expected {status:500, code:null}.",
"gates": "pnpm --filter @objectstack/plugin-auth typecheck (tsc src, tsconfig.examples, check:test-typecheck): VERDICT command-exit 0. dispatch-gates --commands at 3aee1981: 69 derived; 67 run exit 0; --ran verdict: 69 accounted, 67 run, 2 NOT-MEASURED, 0 UNRUN. NOT MEASURED (exit 3 prerequisite): node scripts/check-plugin-teardown-shape.mjs --self-test (pinned fixture commit unreachable in this shallow clone), pnpm check:dual-build-cjs-loads (needs a full monorepo build). PR check-runs: in_progress at report time.",
"dogfood_battery": "n/a — no ! in the PR title and no (narrowing) arm",
"line_budget": "8 files, +209 / -24 (source +111/-15, tests +82/-9, changeset +16)",
"files_changed": [
".changeset/25364-last-admin-guard-read-fault-code.md",
"packages/plugins/plugin-auth/src/last-admin-guard.ts",
"packages/plugins/plugin-auth/src/objectql-adapter.ts",
"packages/plugins/plugin-auth/src/auth-manager-actor-guards.ts",
"packages/plugins/plugin-auth/src/last-admin-guard.test.ts",
"packages/plugins/plugin-auth/src/last-admin-guard.default-organization.test.ts",
"packages/plugins/plugin-auth/src/single-reslug-default-anchor.test.ts",
"packages/plugins/plugin-auth/src/organization-delete-read-fault.test.ts"
],
"deviations": [
"Landing widened inside the same package beyond last-admin-guard.ts: objectql-adapter.ts (the better-auth door; without its arm the read fault answers 500) and auth-manager-actor-guards.ts (the organization/delete standing judge mapped only PERMISSION_DENIED). Both are plugin-auth, domain:services; named in the PR body.",
"The adapter arm keys on the declared pair (code SERVICE_UNAVAILABLE and status 503), not on the guard, so any engine error declaring that pair now answers 503 at the better-auth door instead of an empty 500; recorded under Acceptance notes.",
"Two derived gate families NOT MEASURED locally (exit 3 prerequisite), left to CI."
],
"mcp_calls": "3 — mcp__github__issue_read x3 (get, get_comments x2); no write tool",
"api_writes": "2 relay writes — pr_create (POST /repos/objectstack-ai/objectstack/pulls draft + POST /issues/25388/assignees) and this os-dev-report comment (POST /repos/objectstack-ai/objectstack/issues/25364/comments); plus 3 git pushes to claude/project-thread-ef9bkc",
"open_questions": [],
"out_of_scope_findings": []
}
Activity
objectstack-fleet commentedon Oct 23, 2026
Claim: PM loop round 1
Session:
session_01CuJfNjvts78GKesupEdgbz(a card thread scheduled by the project's coordinator, which follows.claude/agents/os-dev.mdas its role file; the seat session issession_014Hse9cj2i4o5UAVrd21NKr)Account:
os-teslaBranch:
claude/project-thread-ef9bkc(the thread's designated branch: a card thread can push only that branch, a measured platform exception toclaude/issue-<n>-<slug>)Worktree:
objectstack-issue-25364Domain:
domain:engineSeat:
domain:engine#1(seat post #6367)File surface:
packages/plugins/plugin-auth/src/last-admin-guard.ts(failClosedand its read-fault refusal) + its tests, plus any repo-wide pin on the old read-fault code (adomain:servicespackage the seat declares) (stop on breach; explain in the report)Container & model:
S (non-mechanical, so M treatment),mode:thread,model: TIER_DEFAULT (dispatch-gates --tier: no path-derived mandate),effort: highClause-②: no
Metadata-protocol: none
Responsibility: plugin-auth's last-administrator guard produces it | the sibling
slugGuardReadFaultApiErroralready answers 503 | any caller oforganization/updateduring a read fault, rare and fail-closed either wayThread-read: none
Serial constraints cleared: #25338 (this seat, in flight) edits
plugin-auth's create-user route, a different file. No other in-flight card of either engine seat editslast-admin-guard.ts; epic #15212 introduced the rule in #25286 (merged).Clause-② reading: no, an existing code is reused and a fail-closed refusal keeps refusing; a needed new code stops the dev. Dispatch order:
/mnt/project-files/engine-seat1/dispatch-25364.md(+common.md). Ruling carried: none (governing text ADR-0112). Claimed 2026-10-23T04:51Z.Generated by Claude Code
objectstack-fleet commentedon Oct 23, 2026
os-dev-report
{ "issue": 25364, "status": "done", "branch": "claude/project-thread-ef9bkc", "pr": "https://github.com/objectstack-ai/objectstack/pull/25388", "head": "3aee19812a51779419547eff2374614e19c0828f", "session": "session_01CuJfNjvts78GKesupEdgbz", "premise_still_valid": true, "summary": "Measured: every judging half of the last-administrator guard runs in failClosed, which answered a read fault 403 PERMISSION_DENIED; the Default Organization slug-capture rule has its own catch with the same 403. Changed the shared shape: a module-private readFault() (code SERVICE_UNAVAILABLE, status 503, object, cause) thrown by failClosed and the slug rule, both passing an inner read fault through so nested envelopes do not double-wrap. Two doors the refusal crosses had to follow in the same package: objectql-adapter.ts gains a 503 arm (without it organization/update answered 500 with no code, measured by reverting only that file), and auth-manager-actor-guards.ts maps the standing judge's read fault on organization/delete to ORGANIZATION_DELETE_READ_FAULT_CODE. Refusals on the merits, including the maxScan overflow, stay 403 PERMISSION_DENIED; no refusal became an allow.", "tests": "plugin-auth full suite at 3aee1981 under os-verify-lock: VERDICT command-exit 0, Test Files 187 passed (187), Tests 3528 passed | 10 skipped. Pins: organization/update HTTP door {status:503, code:SERVICE_UNAVAILABLE} with driver text withheld and slug unchanged; engine door {code:SERVICE_UNAVAILABLE, status:503}; 3 flipped fail-closed pins in last-admin-guard.test.ts; new: maxScan overflow stays {PERMISSION_DENIED,403}, judgeOrganizationDelete inner read fault answered once, organization/delete via the real guard 503 with nothing attempted and CONTROL 403 PERMISSION_DENIED. Reverse check (src only, tests import src, so no dist leg): the three source files restored to d12e650 gave Tests 7 failed | 137 passed over the four touched test files, every new/flipped pin red; restored from HEAD, git diff HEAD empty, porcelain clean. Adapter-only revert: HTTP pin reads expected {status:500, code:null}.", "gates": "pnpm --filter @objectstack/plugin-auth typecheck (tsc src, tsconfig.examples, check:test-typecheck): VERDICT command-exit 0. dispatch-gates --commands at 3aee1981: 69 derived; 67 run exit 0; --ran verdict: 69 accounted, 67 run, 2 NOT-MEASURED, 0 UNRUN. NOT MEASURED (exit 3 prerequisite): node scripts/check-plugin-teardown-shape.mjs --self-test (pinned fixture commit unreachable in this shallow clone), pnpm check:dual-build-cjs-loads (needs a full monorepo build). PR check-runs: in_progress at report time.", "dogfood_battery": "n/a — no ! in the PR title and no (narrowing) arm", "line_budget": "8 files, +209 / -24 (source +111/-15, tests +82/-9, changeset +16)", "files_changed": [ ".changeset/25364-last-admin-guard-read-fault-code.md", "packages/plugins/plugin-auth/src/last-admin-guard.ts", "packages/plugins/plugin-auth/src/objectql-adapter.ts", "packages/plugins/plugin-auth/src/auth-manager-actor-guards.ts", "packages/plugins/plugin-auth/src/last-admin-guard.test.ts", "packages/plugins/plugin-auth/src/last-admin-guard.default-organization.test.ts", "packages/plugins/plugin-auth/src/single-reslug-default-anchor.test.ts", "packages/plugins/plugin-auth/src/organization-delete-read-fault.test.ts" ], "deviations": [ "Landing widened inside the same package beyond last-admin-guard.ts: objectql-adapter.ts (the better-auth door; without its arm the read fault answers 500) and auth-manager-actor-guards.ts (the organization/delete standing judge mapped only PERMISSION_DENIED). Both are plugin-auth, domain:services; named in the PR body.", "The adapter arm keys on the declared pair (code SERVICE_UNAVAILABLE and status 503), not on the guard, so any engine error declaring that pair now answers 503 at the better-auth door instead of an empty 500; recorded under Acceptance notes.", "Two derived gate families NOT MEASURED locally (exit 3 prerequisite), left to CI." ], "mcp_calls": "3 — mcp__github__issue_read x3 (get, get_comments x2); no write tool", "api_writes": "2 relay writes — pr_create (POST /repos/objectstack-ai/objectstack/pulls draft + POST /issues/25388/assignees) and this os-dev-report comment (POST /repos/objectstack-ai/objectstack/issues/25364/comments); plus 3 git pushes to claude/project-thread-ef9bkc", "open_questions": [], "out_of_scope_findings": [] }Generated by Claude Code
objectstack-fleet commentedon Oct 23, 2026
ACCEPT → PR #25388 comment 6358536499 (ready + auto-merge set 2026-10-23T06:01Z).
Generated by Claude Code