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
plugin-auth: one refused key in the auth settings pass drops every sibling setting, and says so only at warn #20412
Filing-gate: ① product defect with a named landing point, reach measured
Filed on the maintainer's instruction in session session_d8bf7d34-4f31-407d-ac85-6cb849eec4bf (2026-09-28), verbatim: 「直接开卡吧」. The finding was surfaced by the os-dev on #20389 (report comment 5865880545) and escalated by the contract review on PR #20406 (comment 5866574059).
Defect
packages/plugins/plugin-auth/src/auth-plugin.ts, applySettings (read at main7db1332f19): the main this.authManager.applyConfigPatch(patch) for the auth settings namespace runs inside one outer try. Its catch only logs ctx.logger.warn('Auth: failed to apply auth settings: ' + …).
⇒ When assertAudienceConfig (or any other entry validation) refuses ONE key in the pass, the whole patch is dropped. Password policy, MFA, rate limits, session expiry and social providers from that pass are not applied. The settings console still shows them as saved, and the only trace is a warn.
The audience block right beside it already does the opposite, on purpose. It has its own try, and on refusal logs at error: [auth] audience settings REFUSED — the standing posture (…) keeps ruling.
Reach (measured)
Measured by the dev on maindb74b169dc: stack config audience.posture: 'open', OS_AUTH_REQUIRE_EMAIL_VERIFICATION=false and session_expiry_days = 3. Result: the verification key is refused and session.expiresIn stays undefined, with only the warn above.
After #20406 merges, that particular trigger is gone. The path stays reachable through any other refused key: email_domain plus an env false, a console-stored false under open, or a plugins SCIM/admin coherence refusal.
Expected
A refused key does not take its siblings with it: either isolate the refused key and apply the rest, or refuse the pass loudly at error, naming the key, so the console and the log cannot disagree with what is applied. Which of the two is the landing seat's call; the silent warn is the defect.
Reader
The domain:services execution seat, from pm:queue.
Dedupe
Search (open + closed) on 2026-09-28: "failed to apply auth settings" → 2 hits, applySettings sibling auth settings dropped → 2, auth settings pass refused key dropped → 69. The only topical hits are #20389 / #20406 themselves, which name this finding as out of scope. No existing card.
Claim: PM loop round 1
Session: session_017B6YKCGu8CTY2KBWgwaHAs
Account: os-warren (the seat's linked user as GET /user answers it; always the card's assignee)
Branch: claude/issue-20412-auth-settings-sibling-isolation
Worktree: objectstack-issue-20412
Domain: domain:services
Seat: domain:services#1
File surface: packages/plugins/plugin-auth/src/auth-plugin.ts (the applySettings pass for the auth settings namespace and its outer catch), new tests under packages/plugins/plugin-auth/src/ beside the existing auth-plugin settings tests, .changeset/20412-*.md. If the refusal can only be attributed inside AuthManager.applyConfigPatch, auth-manager.ts joins the surface and the report says why. ⛔ No packages/spec, ⛔ no service-settings. (Stop on breach; explain in the report.)
Container & model: M, mode:subagent, model: opus (dispatch-gates --tier on 789b2ae5: no path-derived mandate, floor sonnet · default opus · ceiling fable)
Clause-②: no
Thread-read: none
Serial constraints cleared: read at 2026-09-28T09:29Z on origin/main789b2ae5. auth-plugin.ts last moved in 65352b7d (#20406, merged today, same-day churn). Zero of the 12 open PRs touch packages/plugins/plugin-auth/. Among the 26 open pm:dispatched cards, the only claim naming an auth or mail surface is #16506 (plugin-email queue-delivery test, the domain:engine seat's), which is disjoint. The sibling #20413 (this seat, same round) edits only service-settings text and docs/qa, so it is disjoint and pins nothing this card changes.
Clause-②: no: the settings pass stops dropping accepted sibling keys when one key is refused. No key's own validation changes, and no published contract surface or error code is touched. This is a durability/consistency degradation (the console reads a setting as saved that the runtime never applied), so AGENTS.md's degradation rule puts the refusal at error.
os-dev-report
{
"issue": 20412,
"status": "done",
"branch": "claude/issue-20412-auth-settings-sibling-isolation",
"pr": "#20429",
"session": "session_017B6YKCGu8CTY2KBWgwaHAs — subagent, the dispatching PM session's id (mode:subagent)",
"premise_still_valid": true,
"summary": "Implemented the seat ruling that a refused key does not take its accepted siblings with it. AuthPlugin's auth settings pass (auth-plugin.ts applySettings) is now applied in pieces split along applyConfigPatch's two entry validators. Each settings key that lands in emailAndPassword (assertAudienceConfig) or plugins (assertScimAdminCoherence) is applied alone. mfa_required=true stays one piece with the twoFactor plugin it turns on. Every other key goes out in one application the manager does not validate. Each piece has its own try; a refusal logs ONCE at error, naming the key, stating that the standing value keeps ruling and that the console shows the stored one, followed by the manager's remedy-bearing message, as the audience block beside it does. The outer catch now sees only a whole-pass failure (the namespace read) and moved from warn to error ('auth settings NOT APPLIED'), because that is the line the card names. The ruling's premise held: attribution needs no validation copied into the plugin, and per-key verdicts equal the whole-block verdicts on this tree. The landing point is therefore the plugin, with no auth-manager.ts change. PM mechanism assumptions held: applySettings starts at line 1452 on 40b315b with one patch under a warn catch, and the card's remaining triggers are live. Measured on unmodified main 40b315b, 5 new pins red and the control green: the console door (posture opened through the console, then a console-stored require_email_verification false), stack-config open plus a console false, email_domain plus an env false, and the SCIM/admin plugins refusal all left session.expiresIn undefined. Per AGENTS.md (a new durability seam joins DURABILITY_CRITICAL_CALLEES in the fixing PR), applyConfigPatch was added to the gate vocabulary. That pulled in the census's by-value copy and one engine-double ledger row for the new test; see deviations.",
"tests": "Final head 950c40c. (1) Measurement on unmodified main 40b315b: 'pnpm --filter @objectstack/plugin-auth exec vitest run --maxWorkers=2 src/auth-settings-refusal-isolation.test.ts' gave 'Tests 5 failed | 1 passed (6)', failing on 'expected undefined to be 259200' x3, 'expected undefined to be 604800' x1 and 'expected [] to have a length of 1' (read-failure level). (2) After the fix, the new file plus audience-posture-setting, auth-plugin, membership-policy-setting and auth-settings-ordering.pin: 'Test Files 5 passed (5) / Tests 129 passed (129)'. (3) Full package: 'pnpm --filter @objectstack/plugin-auth exec vitest run --maxWorkers=2' gave 'Test Files 115 passed (115) / Tests 2460 passed (2460)' at 846b285; later commits touch only scripts/ ledgers and the census copy. (4) 'pnpm --filter @objectstack/plugin-auth run typecheck' after building plugin-auth: exit 0, 'check:test-typecheck: OK'. The new test is in the tsconfig.test.json program (--listFiles: 1). The first typecheck attempt exited 2 on examples/basic-usage.ts TS2307 because plugin-auth's own dist was absent: a prerequisite, not a red gate; re-run after the build was green. (5) Ablation leg 1, the old whole-pass drop put back: auth-plugin.ts was restored from 40b315b (tree only). On-disk proof: blob dd862c8d equals the base blob, old-warn count 1, applyPiece count 0. Pins 'Tests 5 failed | 1 passed (6)'. check:durability-log-level red: '1 durability report(s) degrade quietly … auth-plugin.ts:1799'. Restore: git checkout HEAD, blob 45e547d5 equals the HEAD blob, git diff HEAD empty. (6) Ablation leg 2, isolation kept but the refusal logged at warn, via scripts/ablation-replace.mjs (anchor 1 to 0, blob 45e547d5 to 0a104f02): pins 'Tests 4 failed | 2 passed (6)', each at 'expected [] to have a length of 1' (2 for the plugins test). Gate red at auth-plugin.ts:1767. Restored: blob equals HEAD, git diff HEAD empty. Neither ablation needed a build: the test imports AuthPlugin by a relative source path. (7) eslint --no-inline-config --format json on the 4 lintable changed files: 4 files, 0 errors, 0 warnings. Population from eslint.config.mjs files glob '**/*.{ts,tsx,mts,cts,js,jsx,mjs,cjs}' minus NEVER_LINTED, with none of the 4 reported ignored. Invariance: no parserOptions.project or projectService anywhere, so type-aware linting is off. (8) Derived union at 950c40c: 86 commands, 84 exit 0, 2 exit 3 (NOT MEASURED, see gates). 'dispatch-gates --ran' prints '86 derived famil(ies) accounted for — 84 run, 2 NOT-MEASURED (2 DERIVED from a recorded exit 3)', 0 UNRUN. (9) Extra, because the diff edits their inputs: check:optional-error-sink, check:startup-registry-verdict, measure-return-propagating-durability-seams, measure-durability-swallow-family --self-test (all families), and metadata-protocol sys-metadata-repository.draft-drain.test.ts (reads the gate file; 11/11). All exit 0. check:durability-log-level: 38 seams (was 36), all loud.",
"mcp_calls": "0 — no MCP GitHub tool called",
"api_writes": "3 — each sent as one fleet-write relay repository_dispatch (POST /repos/objectstack-ai/objectstack/dispatches), executed as objectstack-fleet[bot]: (1) pr_create: POST /repos/objectstack-ai/objectstack/pulls (draft, #20429; relay run 36413021977 success); (2) label-write --assign os-warren: POST /repos//issues/20429/assignees (relay run 36413143343 success; zero label writes, read-back matches); (3) this os-dev-report comment: POST /repos//issues/20412/comments. git push: 5 pushes of the branch (not REST).",
"open_questions": [],
"out_of_scope_findings": [
"carrier: none (承接者:无) · noted, not filed · observation, pre-existing, order unchanged by this PR: the verification piece is judged against the standing posture before the same pass's audience declaration applies (the audience block's comment records this order as deliberate). A single save that closes open to invite_only AND stores require_email_verification false logs one REFUSED line for that key, although the effective requirement already matches (invite_only resolves an undeclared value to off) and the next pass accepts the stored false. Before this PR the same save dropped the whole pass at warn. In the PR's Acceptance notes."
],
"gates": {
"head": "950c40c5",
"derived": 86,
"ran_exit_0": 84,
"not_measured": [
"pnpm check:dual-build-cjs-loads — exit 3 PREREQUISITE NOT MET: reads every workspace package's dist, 39 absent in this worktree; targeted probe: plugin-auth dist/index.js (require) and dist/index.mjs (import) both load, AuthPlugin is a function",
"pnpm check:type-check-debt — exit 3 PREREQUISITE NOT MET: ledgered packages runtime, service-cluster, service-job have no built type entry; diff touches none of them; plugin-auth typecheck + check:test-typecheck green"
],
"unrun": 0,
"reconciliation": "dispatch-gates --ran: 86 derived famil(ies) accounted for — 84 run, 2 NOT-MEASURED (2 DERIVED from a recorded exit 3)",
"ci": "in_progress — not awaited"
},
"line_budget": {
"changed_lines": 514,
"additions": 477,
"deletions": 37,
"files": 6,
"human_merge_threshold": 5000,
"verdict": "under"
},
"deviations": [
"file surface widened beyond the claim (auth-plugin.ts + tests + changeset), all under scripts/: scripts/check-durability-degradation-log-level.mjs (applyConfigPatch added to DURABILITY_CRITICAL_CALLEES, per the AGENTS.md rule that a new seam joins the vocabulary in the PR that fixes it), scripts/measure-durability-swallow-family.mjs (the census's by-value gate-vocabulary copy; check:swallow-census-controls reds on the drift otherwise), scripts/engine-double-contract.pinned.json (one row via check-engine-double-contract --write for the new test's findOne double, as the gate itself prescribed). No packages/spec, no service-settings, no auth-manager.ts.",
"outer catch lifted from warn to error ('[auth] auth settings NOT APPLIED'): after the split it sees only a whole-pass failure (the namespace read). This is the line the card names as the defect, levelled by AGENTS.md's durability/consistency question.",
"attribution form conflict, noted: the harness reminder asked for a model-named Co-Authored-By trailer and a different PR footer. Commits carry AGENTS.md's model-free pair (Claude-Session + 'Co-authored-by: Claude'); the PR body ends with AGENTS.md's session-URL footer. The pre-push hook accepted all 5 pushes.",
"check:pm-dispatch-gates was run detached (its battery took 1026.8s on the shared box, beyond the foreground cap) and read from its log at 950c40c: exit 0, 'dispatch-gates self-test: 1976 cases pass'. The process finished before this report."
],
"files_changed": [
".changeset/20412-auth-settings-sibling-isolation.md",
"packages/plugins/plugin-auth/src/auth-plugin.ts",
"packages/plugins/plugin-auth/src/auth-settings-refusal-isolation.test.ts",
"scripts/check-durability-degradation-log-level.mjs",
"scripts/engine-double-contract.pinned.json",
"scripts/measure-durability-swallow-family.mjs"
]
}
domain:services seat (#6021) · session_017B6YKCGu8CTY2KBWgwaHAs · 2026-09-28T11:05Z. This reviews report 5868596370 against GitHub and origin/main.
Review items
PR shape. Draft, base main, head claude/issue-20412-auth-settings-sibling-isolation (the claim's branch), assignee os-warren. The first line is Fixes #20412, with no other closing keyword in the body. Clause-②: no is line-start in the body and in the changeset.
The ruling, executed. The seat's ruling (claim 5867200492, dispatch) was: a refused key must not take its accepted siblings with it, and each refusal is logged once at error, naming the key. The ruling's premise was attribution without copied validation, and the report measured it held. The auth pass is split along applyConfigPatch's two entry validators (assertAudienceConfig for emailAndPassword, assertScimAdminCoherence for plugins), with mfa_required kept together with its twoFactor plugin. The outer catch now sees only a whole-pass failure and moves to error. There is no auth-manager.ts change, so the plugin is the landing point.
Pins and their failure proofs.
On unmodified main the new file reads 5 failed | 1 passed (6), with session.expiresIn left undefined across the four live triggers.
Leg 1 (the whole-pass drop put back) turns the pins red 5 of 6 and check:durability-log-level red.
Leg 2 (isolation kept, level warn) turns them red 4 of 6.
Both restores are proven by blob equality with HEAD. The full plugin-auth suite is 2460/2460.
Surface beyond the claim, amended in this round. Three scripts/ files:
check-durability-degradation-log-level.mjs: applyConfigPatch joins DURABILITY_CRITICAL_CALLEES. AGENTS.md: "found a new one, add it to DURABILITY_CRITICAL_CALLEES in the same PR that fixes it".
measure-durability-swallow-family.mjs: the census's by-value copy of that vocabulary, which check:swallow-census-controls requires to stay in step.
engine-double-contract.pinned.json: one row for the new test's findOne double, written by the gate's own --write.
This strengthens an existing gate's vocabulary; it is not a new gate. Gate scripts anchor to domain:spec. This cross-lane touch is declared here and in the round report. At this reading, no other open PR touches any of the three files. Claim surface amended: these three files join auth-plugin.ts, the test and the changeset.
Changeset.@objectstack/plugin-authpatch, Clause-②: no. Right: a published package's runtime behaviour moves (accepted settings now apply), and no key, option or error code changes. A contract review is owed on the .changeset prose. It runs at CONTRACT_REVIEW_TIER before readiness.
Out-of-scope finding. A single save that closes open to invite_only and stores require_email_verification: false logs one REFUSED line for that key, because the verification piece is judged against the standing posture first; the next pass accepts it. This is pre-existing ordering that the audience block's own comment records as deliberate. Before this PR the same save dropped the whole pass. ⇒ Acceptance notes (carrier: none). No reach: to a wrong answer at a public door, so no card.
CI. At this reading on 950c40c5: 16 success, 3 skipped, 12 in progress, 0 red. Not ready until every check is green and the contract review passes.
Report hygiene.mcp_calls 0. api_writes 3, all through the relay. Two families are NOT MEASURED locally on declared prerequisites (check:dual-build-cjs-loads, check:type-check-debt), and CI answers both.
The round-1 contract review of PR #20421 (5868073710) independently named this card's shape on main as a live defect, a console-stored false under open dropping the pass's siblings at warn. This PR is its fix.
Delivered on origin/main:7d63088958 (fix(plugin-auth): a refused auth setting no longer drops the settings saved with it). Record: ACCEPT 5868627018, contract review PASS 5868779058 at CONTRACT_REVIEW_TIER on head 950c40c5, every check green.
What changed for a user: when the settings console stores one value that the auth manager refuses, the other auth settings saved in the same pass (password policy, MFA, rate limits, session lifetime, social providers) now apply. The refused key is logged once at error, by name. applyConfigPatch is now in the durability gate's vocabulary.
Board clean-up in this write: the closing keyword closed the card but left pm:dispatched and the assignee. Both are cleared here, per the release rule.
Activity
objectstack-fleet commentedon Sep 28, 2026
Claim: PM loop round 1
Session:
session_017B6YKCGu8CTY2KBWgwaHAsAccount:
os-warren(the seat's linked user asGET /useranswers it; always the card's assignee)Branch:
claude/issue-20412-auth-settings-sibling-isolationWorktree:
objectstack-issue-20412Domain:
domain:servicesSeat:
domain:services#1File surface:
packages/plugins/plugin-auth/src/auth-plugin.ts(theapplySettingspass for theauthsettings namespace and its outercatch), new tests underpackages/plugins/plugin-auth/src/beside the existing auth-plugin settings tests,.changeset/20412-*.md. If the refusal can only be attributed insideAuthManager.applyConfigPatch,auth-manager.tsjoins the surface and the report says why. ⛔ Nopackages/spec, ⛔ noservice-settings. (Stop on breach; explain in the report.)Container & model:
M,mode:subagent,model: opus(dispatch-gates --tieron789b2ae5: no path-derived mandate, floor sonnet · default opus · ceiling fable)Clause-②: no
Thread-read: none
Serial constraints cleared: read at 2026-09-28T09:29Z on
origin/main789b2ae5.auth-plugin.tslast moved in65352b7d(#20406, merged today, same-day churn). Zero of the 12 open PRs touchpackages/plugins/plugin-auth/. Among the 26 openpm:dispatchedcards, the only claim naming an auth or mail surface is #16506 (plugin-emailqueue-delivery test, thedomain:engineseat's), which is disjoint. The sibling #20413 (this seat, same round) edits onlyservice-settingstext anddocs/qa, so it is disjoint and pins nothing this card changes.Clause-②: no: the settings pass stops dropping accepted sibling keys when one key is refused. No key's own validation changes, and no published contract surface or error code is touched. This is a durability/consistency degradation (the console reads a setting as saved that the runtime never applied), so AGENTS.md's degradation rule puts the refusal aterror.Generated by Claude Code
objectstack-fleet commentedon Sep 28, 2026
os-dev-report
{
"issue": 20412,
"status": "done",
"branch": "claude/issue-20412-auth-settings-sibling-isolation",
"pr": "#20429",
"session": "session_017B6YKCGu8CTY2KBWgwaHAs — subagent, the dispatching PM session's id (mode:subagent)",
"premise_still_valid": true,
"summary": "Implemented the seat ruling that a refused key does not take its accepted siblings with it. AuthPlugin's auth settings pass (auth-plugin.ts applySettings) is now applied in pieces split along applyConfigPatch's two entry validators. Each settings key that lands in emailAndPassword (assertAudienceConfig) or plugins (assertScimAdminCoherence) is applied alone. mfa_required=true stays one piece with the twoFactor plugin it turns on. Every other key goes out in one application the manager does not validate. Each piece has its own try; a refusal logs ONCE at error, naming the key, stating that the standing value keeps ruling and that the console shows the stored one, followed by the manager's remedy-bearing message, as the audience block beside it does. The outer catch now sees only a whole-pass failure (the namespace read) and moved from warn to error ('auth settings NOT APPLIED'), because that is the line the card names. The ruling's premise held: attribution needs no validation copied into the plugin, and per-key verdicts equal the whole-block verdicts on this tree. The landing point is therefore the plugin, with no auth-manager.ts change. PM mechanism assumptions held: applySettings starts at line 1452 on 40b315b with one patch under a warn catch, and the card's remaining triggers are live. Measured on unmodified main 40b315b, 5 new pins red and the control green: the console door (posture opened through the console, then a console-stored require_email_verification false), stack-config open plus a console false, email_domain plus an env false, and the SCIM/admin plugins refusal all left session.expiresIn undefined. Per AGENTS.md (a new durability seam joins DURABILITY_CRITICAL_CALLEES in the fixing PR), applyConfigPatch was added to the gate vocabulary. That pulled in the census's by-value copy and one engine-double ledger row for the new test; see deviations.",
"tests": "Final head 950c40c. (1) Measurement on unmodified main 40b315b: 'pnpm --filter @objectstack/plugin-auth exec vitest run --maxWorkers=2 src/auth-settings-refusal-isolation.test.ts' gave 'Tests 5 failed | 1 passed (6)', failing on 'expected undefined to be 259200' x3, 'expected undefined to be 604800' x1 and 'expected [] to have a length of 1' (read-failure level). (2) After the fix, the new file plus audience-posture-setting, auth-plugin, membership-policy-setting and auth-settings-ordering.pin: 'Test Files 5 passed (5) / Tests 129 passed (129)'. (3) Full package: 'pnpm --filter @objectstack/plugin-auth exec vitest run --maxWorkers=2' gave 'Test Files 115 passed (115) / Tests 2460 passed (2460)' at 846b285; later commits touch only scripts/ ledgers and the census copy. (4) 'pnpm --filter @objectstack/plugin-auth run typecheck' after building plugin-auth: exit 0, 'check:test-typecheck: OK'. The new test is in the tsconfig.test.json program (--listFiles: 1). The first typecheck attempt exited 2 on examples/basic-usage.ts TS2307 because plugin-auth's own dist was absent: a prerequisite, not a red gate; re-run after the build was green. (5) Ablation leg 1, the old whole-pass drop put back: auth-plugin.ts was restored from 40b315b (tree only). On-disk proof: blob dd862c8d equals the base blob, old-warn count 1, applyPiece count 0. Pins 'Tests 5 failed | 1 passed (6)'. check:durability-log-level red: '1 durability report(s) degrade quietly … auth-plugin.ts:1799'. Restore: git checkout HEAD, blob 45e547d5 equals the HEAD blob, git diff HEAD empty. (6) Ablation leg 2, isolation kept but the refusal logged at warn, via scripts/ablation-replace.mjs (anchor 1 to 0, blob 45e547d5 to 0a104f02): pins 'Tests 4 failed | 2 passed (6)', each at 'expected [] to have a length of 1' (2 for the plugins test). Gate red at auth-plugin.ts:1767. Restored: blob equals HEAD, git diff HEAD empty. Neither ablation needed a build: the test imports AuthPlugin by a relative source path. (7) eslint --no-inline-config --format json on the 4 lintable changed files: 4 files, 0 errors, 0 warnings. Population from eslint.config.mjs files glob '**/*.{ts,tsx,mts,cts,js,jsx,mjs,cjs}' minus NEVER_LINTED, with none of the 4 reported ignored. Invariance: no parserOptions.project or projectService anywhere, so type-aware linting is off. (8) Derived union at 950c40c: 86 commands, 84 exit 0, 2 exit 3 (NOT MEASURED, see gates). 'dispatch-gates --ran' prints '86 derived famil(ies) accounted for — 84 run, 2 NOT-MEASURED (2 DERIVED from a recorded exit 3)', 0 UNRUN. (9) Extra, because the diff edits their inputs: check:optional-error-sink, check:startup-registry-verdict, measure-return-propagating-durability-seams, measure-durability-swallow-family --self-test (all families), and metadata-protocol sys-metadata-repository.draft-drain.test.ts (reads the gate file; 11/11). All exit 0. check:durability-log-level: 38 seams (was 36), all loud.",
"mcp_calls": "0 — no MCP GitHub tool called",
"api_writes": "3 — each sent as one fleet-write relay repository_dispatch (POST /repos/objectstack-ai/objectstack/dispatches), executed as objectstack-fleet[bot]: (1) pr_create: POST /repos/objectstack-ai/objectstack/pulls (draft, #20429; relay run 36413021977 success); (2) label-write --assign os-warren: POST /repos//issues/20429/assignees (relay run 36413143343 success; zero label writes, read-back matches); (3) this os-dev-report comment: POST /repos//issues/20412/comments. git push: 5 pushes of the branch (not REST).",
"open_questions": [],
"out_of_scope_findings": [
"carrier: none (承接者:无) · noted, not filed · observation, pre-existing, order unchanged by this PR: the verification piece is judged against the standing posture before the same pass's audience declaration applies (the audience block's comment records this order as deliberate). A single save that closes open to invite_only AND stores require_email_verification false logs one REFUSED line for that key, although the effective requirement already matches (invite_only resolves an undeclared value to off) and the next pass accepts the stored false. Before this PR the same save dropped the whole pass at warn. In the PR's Acceptance notes."
],
"gates": {
"head": "950c40c5",
"derived": 86,
"ran_exit_0": 84,
"not_measured": [
"pnpm check:dual-build-cjs-loads — exit 3 PREREQUISITE NOT MET: reads every workspace package's dist, 39 absent in this worktree; targeted probe: plugin-auth dist/index.js (require) and dist/index.mjs (import) both load, AuthPlugin is a function",
"pnpm check:type-check-debt — exit 3 PREREQUISITE NOT MET: ledgered packages runtime, service-cluster, service-job have no built type entry; diff touches none of them; plugin-auth typecheck + check:test-typecheck green"
],
"unrun": 0,
"reconciliation": "dispatch-gates --ran: 86 derived famil(ies) accounted for — 84 run, 2 NOT-MEASURED (2 DERIVED from a recorded exit 3)",
"ci": "in_progress — not awaited"
},
"line_budget": {
"changed_lines": 514,
"additions": 477,
"deletions": 37,
"files": 6,
"human_merge_threshold": 5000,
"verdict": "under"
},
"deviations": [
"file surface widened beyond the claim (auth-plugin.ts + tests + changeset), all under scripts/: scripts/check-durability-degradation-log-level.mjs (applyConfigPatch added to DURABILITY_CRITICAL_CALLEES, per the AGENTS.md rule that a new seam joins the vocabulary in the PR that fixes it), scripts/measure-durability-swallow-family.mjs (the census's by-value gate-vocabulary copy; check:swallow-census-controls reds on the drift otherwise), scripts/engine-double-contract.pinned.json (one row via check-engine-double-contract --write for the new test's findOne double, as the gate itself prescribed). No packages/spec, no service-settings, no auth-manager.ts.",
"outer catch lifted from warn to error ('[auth] auth settings NOT APPLIED'): after the split it sees only a whole-pass failure (the namespace read). This is the line the card names as the defect, levelled by AGENTS.md's durability/consistency question.",
"attribution form conflict, noted: the harness reminder asked for a model-named Co-Authored-By trailer and a different PR footer. Commits carry AGENTS.md's model-free pair (Claude-Session + 'Co-authored-by: Claude'); the PR body ends with AGENTS.md's session-URL footer. The pre-push hook accepted all 5 pushes.",
"check:pm-dispatch-gates was run detached (its battery took 1026.8s on the shared box, beyond the foreground cap) and read from its log at 950c40c: exit 0, 'dispatch-gates self-test: 1976 cases pass'. The process finished before this report."
],
"files_changed": [
".changeset/20412-auth-settings-sibling-isolation.md",
"packages/plugins/plugin-auth/src/auth-plugin.ts",
"packages/plugins/plugin-auth/src/auth-settings-refusal-isolation.test.ts",
"scripts/check-durability-degradation-log-level.mjs",
"scripts/engine-double-contract.pinned.json",
"scripts/measure-durability-swallow-family.mjs"
]
}
Generated by Claude Code
objectstack-fleet commentedon Sep 28, 2026
ACCEPT: PR #20429 at
950c40c5(Fixes #20412)domain:servicesseat (#6021) ·session_017B6YKCGu8CTY2KBWgwaHAs· 2026-09-28T11:05Z. This reviews report5868596370against GitHub andorigin/main.Review items
PR shape. Draft, base
main, headclaude/issue-20412-auth-settings-sibling-isolation(the claim's branch), assigneeos-warren. The first line isFixes #20412, with no other closing keyword in the body.Clause-②: nois line-start in the body and in the changeset.The ruling, executed. The seat's ruling (claim
5867200492, dispatch) was: a refused key must not take its accepted siblings with it, and each refusal is logged once aterror, naming the key. The ruling's premise was attribution without copied validation, and the report measured it held. Theauthpass is split alongapplyConfigPatch's two entry validators (assertAudienceConfigforemailAndPassword,assertScimAdminCoherenceforplugins), withmfa_requiredkept together with itstwoFactorplugin. The outer catch now sees only a whole-pass failure and moves toerror. There is noauth-manager.tschange, so the plugin is the landing point.Pins and their failure proofs.
mainthe new file reads5 failed | 1 passed (6), withsession.expiresInleftundefinedacross the four live triggers.check:durability-log-levelred.warn) turns them red 4 of 6.HEAD. The fullplugin-authsuite is 2460/2460.Surface beyond the claim, amended in this round. Three
scripts/files:check-durability-degradation-log-level.mjs:applyConfigPatchjoinsDURABILITY_CRITICAL_CALLEES. AGENTS.md: "found a new one, add it toDURABILITY_CRITICAL_CALLEESin the same PR that fixes it".measure-durability-swallow-family.mjs: the census's by-value copy of that vocabulary, whichcheck:swallow-census-controlsrequires to stay in step.engine-double-contract.pinned.json: one row for the new test'sfindOnedouble, written by the gate's own--write.This strengthens an existing gate's vocabulary; it is not a new gate. Gate scripts anchor to
domain:spec. This cross-lane touch is declared here and in the round report. At this reading, no other open PR touches any of the three files. Claim surface amended: these three files joinauth-plugin.ts, the test and the changeset.Changeset.
@objectstack/plugin-authpatch,Clause-②: no. Right: a published package's runtime behaviour moves (accepted settings now apply), and no key, option or error code changes. A contract review is owed on the.changesetprose. It runs atCONTRACT_REVIEW_TIERbefore readiness.Out-of-scope finding. A single save that closes
opentoinvite_onlyand storesrequire_email_verification: falselogs one REFUSED line for that key, because the verification piece is judged against the standing posture first; the next pass accepts it. This is pre-existing ordering that the audience block's own comment records as deliberate. Before this PR the same save dropped the whole pass. ⇒ Acceptance notes (carrier: none). Noreach:to a wrong answer at a public door, so no card.CI. At this reading on
950c40c5: 16 success, 3 skipped, 12 in progress, 0 red. Not ready until every check is green and the contract review passes.Report hygiene.
mcp_calls0.api_writes3, all through the relay. Two families are NOT MEASURED locally on declared prerequisites (check:dual-build-cjs-loads,check:type-check-debt), and CI answers both.The round-1 contract review of PR #20421 (
5868073710) independently named this card's shape onmainas a live defect, a console-storedfalseunderopendropping the pass's siblings atwarn. This PR is its fix.Generated by Claude Code
objectstack-fleet commentedon Sep 28, 2026
Landed: PR #20429 →
7d630889(merge queue). The card closedcompleteddomain:servicesseat (#6021) ·session_017B6YKCGu8CTY2KBWgwaHAs· 2026-09-28T12:07Z.origin/main:7d63088958(fix(plugin-auth): a refused auth setting no longer drops the settings saved with it). Record: ACCEPT5868627018, contract review PASS5868779058atCONTRACT_REVIEW_TIERon head950c40c5, every check green.error, by name.applyConfigPatchis now in the durability gate's vocabulary.pm:dispatchedand the assignee. Both are cleared here, per the release rule.Release: session
session_017B6YKCGu8CTY2KBWgwaHAs· 因: delivered (Fixes #20412, merged) · 去向: closedcompleted.Generated by Claude Code
fix(plugin-auth): a refused auth setting no longer drops the settings…
fix(plugin-auth): the no-sign-in-account boot report states the email…