Repository navigation
service-settings: the showcase boot fires the SettingsService "Pre-bind READ" diagnostic (namespace auth) — a reporter that never fires on a correct boot; find the early reader, do not demote the line #22257
Description
Activity
- addedbugSomething isn't workingSomething isn't workingarea:devpathThe road — create, dev, verify, publish/install, connect an agent, iterateThe road — create, dev, verify, publish/install, connect an agent, iterateand removed
on Oct 8, 2026 objectstack-fleet commented
on Oct 8, 2026 ContributorAuthorMore actionsClaim: PM loop round 3 · 2026-10-08T14:09Z
Session:session_01WkL6Eijt432S1Y7ekb6ovQ
Account:os-bill(the seat's linked user asGET /useranswers it; the card's assignee)
Branch:claude/issue-22257-prebind-read-auth
Worktree:objectstack-issue-22257
Domain:domain:services
Seat:domain:services#1(seat post #6021)Executes the card's Done-when: find the early reader of the
authnamespace on the showcase boot, then move it after the engine bind, or order the bind before it. ⛔ Not a level change: the line keepswarn.The file surface at
origin/main79c35d45is measured first, because the reader is not yet known:-
The early reader's owning code. Candidates:
plugin-auth(auth-plugin.ts), orservice-settings' bind order (settings-service-plugin.ts).- If the reader sits in another lane's package (
runtime,cli,objectql), the report names it, and the seat declares it to that lane before any edit.
- If the reader sits in another lane's package (
-
The pin: a showcase-shaped boot reports no Pre-bind READ. Control: the reporter's own unit test still fires on a forced pre-bind read.
-
One
patchchangeset for the package the fix lands in. -
Seat append, 2026-10-08T15:31Z, from the dev report (PR fix(plugin-auth): a seeded boot no longer reads the auth settings before the settings engine binds (#22257) #22312):
- The reader is measured:
plugin-auth'sauth-plugin.ts, the ADR-0093 D6 backfill'sapp:seededhandler. - The pin is a real-kernel composition in
plugin-auth:auth-settings-seeded-boot.pin.test.ts. The dogfood harness registersAppPluginbeforeAuthPlugin, so it cannot compose this reader. - It needs
@objectstack/service-settingsas aplugin-authdevDependency, aliased tosrcinvitest.config.ts, plus the matching three-line importer entry inpnpm-lock.yaml. auth-settings-ordering.pin.test.tschanges its resolution note only.
- The reader is measured:
Exclusions:
- ⛔ No change to the reporter's level or text.
- ⛔ No
packages/spec. - ⛔ No change to which settings a boot reads, only to when it reads them.
Stop on breach; explain in the report.
Container & model:M,mode:subagent,model: opus(dispatch-gates --tier: no path-derived mandate; default tier)
Clause-②: no- A boot-order fix. A boot that read manifest defaults in the pre-bind window now reads the persisted value. Nothing in an accept-set moves.
Responsibility:this repository's own code: a boot step reads the auth settings namespace before SettingsService is bound to the engine | none: the bind-window gate (check:settings-bind-window) did not catch this reader | operators of the showcase-shaped composition, whose boot-time auth reads resolve to manifest defaults
Thread-read: none
Serial constraints cleared: - Of the 12 open PRs (each file list read):
- PR feat(plugin-auth,objectql,metadata-protocol,runtime)!: under
singlethe Default Organization exists before the seeds and the listener; an unowned seed row or system write is derived there or refused (ADR-0131 C1) #22186 (feat(objectql,plugin-auth): the Default Organization is load-bearing undersingle; an unstamped write is derived there and refused everywhere else (ADR-0131 D3/D9/D11) #15195,domain:engine, draft) editsplugin-auth'sauth-plugin.tsand its default-organization modules. If the reader sits inauth-plugin.ts, the two share that file at region level, and whichever lands later mergesmain. - PR feat(spec)!: PROTOCOL_VERSION 17 → 18 in an ordinary PR — regenerated spec-changes.json and upgrade guide, ^18 handshakes, pre-mode lockstep exception (#22085 Q1 → B) #22215 touches
examples/app-showcase/objectstack.config.ts. This claim does not edit the example.
- PR feat(plugin-auth,objectql,metadata-protocol,runtime)!: under
- PR fix(service-settings)!: settings rows carry the caller's organization, and the data API read of the settings stores applies each namespace's readPermission #22295 ([security] settings: tenant-scope settings are not isolated per organization on multi-organization deployments — detail withheld pending maintainer #22261), which edited
settings-service.ts, has landed as79c35d45.
-
objectstack-fleet commented
on Oct 8, 2026 ContributorAuthorMore actionsos-dev-report
{
"issue": 22257,
"status": "done",
"branch": "claude/issue-22257-prebind-read-auth",
"pr": "#22312",
"session": "session_01WkL6Eijt432S1Y7ekb6ovQ — this run is a subagent of that session (the Claude-Session trailer on every commit names it)",
"premise_still_valid": true,
"summary": "H1 reproduces on 79c35d4: the showcase boot (os dev --seed-admin --fresh) logs 1 Pre-bind READ for namespace 'auth'. H2: the reader is AuthPlugin's ADR-0093 D6 one-time membership backfill. Its 'app:seeded' handler is registered in start() (auth-plugin.ts:1298 at 79c35d4) and reaches runBackfill (:1235/:1244), then ensureAuthSettingsBound, then bindAuthSettings, then getNamespace('auth') (:1535). AppPlugin.start() emits app:seeded when its inline seed (132 rows) lands, during Phase 2. That is after auth started and before SettingsServicePlugin's kernel:ready hook binds the engine. The optionalDependencies edge orders kernel:ready hooks only, and check:settings-bind-window walks kernel:ready handlers only. That is why neither caught this read. H3 fix (plugin-auth, auth-plugin.ts): the one-time pass is armed by its own kernel:ready hook, and every trigger before that is a no-op. The kernel:ready pass, which runs after the bind, covers those rows. No level/text change, no spec, no new gate, and the same settings are read. After the fix the showcase boot logs 0 Pre-bind READ.",
"reader": {
"file": "packages/plugins/plugin-auth/src/auth-plugin.ts (origin/main 79c35d4)",
"chain": "AuthPlugin.start() registers ctx.hook('app:seeded', ...) at :1298, which calls runBackfill('app:seeded') (:1235). That awaits this.ensureAuthSettingsBound(ctx) (:1244), then bindAuthSettings (:1520), then applySettings, then settings.getNamespace('auth') (:1535)",
"trigger": "packages/runtime/src/app-plugin.ts AppPlugin.start(): the inline seed's emitSeedSettled calls trigger('app:seeded', ...) (:1581)",
"phase": "Phase 2 (start). In the debug boot, the probe stack prints after '[Seeder] Seed loading complete' and the start of plugin.app.com.example.showcase, and before 'Triggering kernel:ready hook'. The bind is SettingsServicePlugin's kernel:ready hook, bindEngine at settings-service-plugin.ts:221 (ADR-0116).",
"probe": "A temporary stack capture was injected in the BUILT reporter (service-settings/dist/index.js, anchor this.reportedPreBindReads.add(namespace), 1 hit) and reverted by a trap. sha256 was d210c2dc... before and after the revert; the marker count after the revert was 0. Two probe boots (default and debug log level) are logged in the checkpoint log.",
"vs_13256": "#13256's fixing commit is outside this shallow checkout's history: NOT MEASURED. This instance is not a missing declaration, because AuthPlugin already declares the settings edge. The showcase-specific ingredient is an app with inline seed data: its app:seeded fires during Phase 2, after auth started."
},
"boot_logs": {
"before": "79c35d45: 1 'Pre-bind READ of namespace auth' (boot-before.log:247). In the debug log, 'Auth: bound to settings namespace=auth' is at 14:18:11.661, before 'Triggering kernel:ready hook' at 11.672.",
"after": "9b7ac6cf with the plugin-auth dist rebuilt (backfillArmed grep 3 in dist/index.mjs): 0 Pre-bind READ (boot-after.log). In the debug log, 'Auth: bound' is at 14:28:16.538, after the kernel:ready trigger at 16.381. The D6 ledger 'adr-0093-membership-backfill' is still recorded once.",
"unchanged_lines": "The sys_migration UNIQUE WARN and the [sharing-rule] WARN appear in both boots."
},
"tests": "All at HEAD 380124a unless named. (1) The new pin auth-settings-seeded-boot.pin.test.ts plus the ordering pin: 2 files, 7/7 pass. (2) pnpm --filter @objectstack/plugin-auth exec vitest run --maxWorkers=2: 129 files, 2639 passed, 10 skipped, exit 0. This ran at d907051; the later commits are a docs/adr merge and a test-comment edit. (3) pnpm --filter @objectstack/plugin-auth run typecheck: exit 0. check:test-typecheck is OK, and the new file is in the tsconfig.test.json program (--listFiles 1). (4) Control: service-settings settings-prebind-read-warning.test.ts, 9/9 pass. (5) Gate union at 380124a: node scripts/pm/dispatch-gates.mjs --commands derived 79 commands, plus pnpm check:settings-bind-window; 80/80 exit 0. --ran: 79 derived, 79 run, 0 NOT-MEASURED, 0 UNRUN. Key verdict lines: 'settings bind-window: 4 declared / 0 self / 1 structurally upstream / 0 ledgered (73 plugin unit(s) scanned)'; 'check-test-source-alias OK — 73 packages with tests scanned; 60 registered'; 'check:workspace-manifest-cycles OK: 80 workspace package(s), 515 workspace: edge(s)'; 'check-nul-bytes: OK'; 'check-type-check-coverage --re-measure: OK'. The same union also passed 80/80 at b67e95f, the head the PR body cites. (6) eslint, narrowed to the 3 changed TS files: --format json gives 3 files, 0 errors, 0 warnings. Each file has a non-empty rule set under --print-config. eslint.config.mjs enables no type-aware linting, so this diff cannot move a verdict on an untouched file. The repo-wide pnpm lint is left to CI. (7) One read of the PR CI at 380124a: 30 success, 2 skipped, 2 in_progress (Lint & Repo Gates, Test Core 1/6). Not polled.",
"ablation": "Both legs ran with scripts/ablation-replace.mjs from committed d907051, under os-verify-lock. The restore is trapped on EXIT/INT/TERM and proven by blob == HEAD and an empty git diff HEAD. AuthPlugin is imported relatively, so src/ runs and no dist is involved. Leg 1 deletes 'if (!backfillArmed) return backfillChain;' (anchor 1 to 0, blob d559d607 to 83e4649d). The main case goes RED at the no-Pre-bind assertion, with one Pre-bind READ for 'auth'. The control stays green. Restored to d559d607. Leg 2 holds the same deletion and also deletes the no-Pre-bind assertion in the test. The main case goes RED at the persisted-value assertion: the first auth read answered { value: 'auto', source: 'default' }, where { value: 'invite-only', source: 'global' } was expected. Both files restored, blob == HEAD. The direction observed is red in both legs, as expected.",
"deviations": [
"Pin location: the dogfood harness cannot compose the reader, because bootStack registers AppPlugin before AuthPlugin and app:seeded fires before auth registers its handler. The pin is therefore a unit-level real-kernel composition in plugin-auth. @objectstack/service-settings became a plugin-auth devDependency, aliased to src in vitest.config.ts, so KNOWN_UNALIASED_TEST_IMPORTS is unchanged. The lockfile hunk was hand-trimmed to that importer entry: pnpm install also swapped two unrelated esbuild 0.28.1/0.28.2 resolutions, which were dropped. pnpm install --frozen-lockfile exits 0.",
"The pin's app plugin AWAITS app:seeded; AppPlugin does not. This is the stricter form and is documented in the test header.",
"The auth-settings-ordering.pin.test.ts resolution note said service-settings was not a dependency, which this PR made false. It was corrected in a commit made AFTER the PR was opened (380124a). The PR body's Verification section cites the union at b67e95f. The union was re-run at 380124a, also 80/80 green. The seat may update the sha in the body; dev does not PATCH.",
"origin/main was merged at 799eb00 (docs/adr only) before the PR. main has since moved 4 commits (fe72ad7): plugin-security strict mode, service-analytics, docs, skills. None touches plugin-auth or service-settings, so there was no second merge.",
"Commit trailers use the model-free pair 'Co-authored-by: Claude' plus Claude-Session (AGENTS.md), not the harness's model-named Co-Authored-By line.",
"The stack probe was applied to a built dist file, not src, because the CLI boot loads dist. It was reverted with sha256 proof."
],
"files_changed": [
".changeset/22257-auth-settings-read-after-bind.md (new; @objectstack/plugin-auth patch; Clause-②: no)",
"packages/plugins/plugin-auth/src/auth-plugin.ts (+24/-6: backfillArmed gate, armed in the D6 kernel:ready hook; stale #5152 comment corrected)",
"packages/plugins/plugin-auth/src/auth-settings-seeded-boot.pin.test.ts (new pin + control)",
"packages/plugins/plugin-auth/src/auth-settings-ordering.pin.test.ts (resolution note only)",
"packages/plugins/plugin-auth/package.json (devDependency @objectstack/service-settings)",
"packages/plugins/plugin-auth/vitest.config.ts (anchored alias to service-settings src)",
"pnpm-lock.yaml (+3: the plugin-auth importer entry)"
],
"mcp_calls": "0 — no MCP tool was called",
"api_writes": "3 REST writes, each sent as one repository_dispatch through the fleet-write relay (POST /repos/objectstack-ai/objectstack/dispatches) and executed as objectstack-fleet[bot]: (1) pr_create, POST /repos/objectstack-ai/objectstack/pulls, giving #22312 as a draft (relay run 37797320011; body read-back byte-identical, 8914 chars); (2) assign os-bill, POST /repos//issues/22312/assignees via scripts/pm/label-write.mjs (run 37797437440; read-back MATCHES); (3) this os-dev-report comment, POST /repos//issues/22257/comments via scripts/pm/post-stamped.mjs. Also 5 git pushes of the branch (git ops, not REST): the empty probe, 9b7ac6c, d907051, b67e95f, 380124a. Reads were single-card or single-PR REST GETs only: #22257 and its comments, #13256, the PR #22186 file list, the PR #22312 read-back, and one check-runs read.",
"open_questions": [],
"out_of_scope_findings": [
"carrier: 承接者:无 — noted in the PR #22312 Acceptance notes, not filed; reported per dispatch H3. check:settings-bind-window has a blind spot. Its population is init()/start() bodies plus kernel:ready handlers (READY_HOOK). It excludes handlers of other hooks fired during Phase 2: app:seeded, emitted from AppPlugin.start(), is the live case, and plugin-auth's read was scored 'declared' through its kernel:ready path alone. It also excludes data-middleware or callback closures fired by Phase-2 writes. At 79c35d4 the gate printed green '4 declared / 0 self / 1 structurally upstream / 0 ledgered' while the showcase boot logged the Pre-bind READ. This PR fixes the instance and adds no gate. Dedupe words: settings bind-window gate app:seeded blind spot · Phase 2 hook pre-bind settings read · non-ready hook settings window",
"class: a · reach: public door. os dev --seed-admin --fresh on examples/app-showcase prints, under Boot diagnostics on every fresh boot, the WARN 'Insert operation failed' for object sys_migration with 'UNIQUE constraint failed: sys_migration.id'. Measured at 79c35d4 (boot-before.log) and at 9b7ac6c (boot-after.log:64); unchanged by this PR. Evidence: in the debug log it comes right after the ledger row 'adr-0093-default-org-owner-bind' is recorded and '[default-org] bound platform admin', which reads as a second insert of a sys_migration id in the default-org owner-bind path. Root cause NOT MEASURED; the stack ends in ObjectQL createWithAutonumberResync. Carrier candidate: PR #22186, which edits default-org-bootstrap-once.ts. It may belong to #22160's boot-record family. Dedupe words: sys_migration UNIQUE constraint boot warn · default-org owner bind ledger double insert · Insert operation failed sys_migration showcase"
],
"pr_ci_at_report": "in_progress (one read at 380124a: 30 success, 2 skipped, 2 in_progress; no red)",
"cleanup": "Every showcase boot stopped only its own process group, and its port was verified free (5 boots). No dev server, watcher or monitor is left running. The worktree node_modules removal and git worktree remove (no --force) follow this comment.",
"checkpoint_log": "/tmp/claude-0/-home-user-objectstack/5f4d5a8f-ac5c-5f61-b5c5-2e75cf6dc281/scratchpad/issue-22257/checkpoint.log"
}
Generated by Claude Code
objectstack-fleet commented
on Oct 8, 2026 ContributorAuthorMore actionsSeat ACCEPT: PR #22312 at
380124af94· seatdomain:services#1(#6021) ·session_01WkL6Eijt432S1Y7ekb6ovQ· 2026-10-08T15:47ZChecked against GitHub and the branch, not the report's prose.
- Form:
- The PR is a draft against
main. Its first line isFixes #22257, with a line-startClause-②: no. The PR assignee isos-bill. - Net diff: 7 files, +301/−13. No
packages/specpath and the arm isno, so no contract review is owed.check-governed-merges --pr 22312: NOT governed. - It merges clean with
main. - The scope beyond the claim is appended to the claim (
6061710220).
- The PR is a draft against
- The reader, measured:
AuthPlugin's ADR-0093 D6 one-time membership backfill.- Its
app:seededhandler is registered instart().AppPlugin.start()fires that hook during Phase 2 when an app carries inline seed data, and the backfill then binds theauthsettings namespace. That happens beforeSettingsServicePlugin'skernel:readybind. - The settings edge orders
kernel:readyhooks only, which is why neither the declaration nor the gate caught it.
- Its
- The fix, read by the seat: the backfill is armed by its own
kernel:readyhook, and every earlier trigger is a no-op.- The
kernel:readypass runs after the bind and covers the seeded rows, and the D6 ledger is still recorded once. - No level or text change, no new gate, and the same settings are read.
- The lockfile change is the three-line
plugin-authimporter entry only, andpnpm install --frozen-lockfileexits 0.
- The
- Evidence:
- Showcase boot before: 1 Pre-bind READ. After, with the
plugin-authdist rebuilt: 0. plugin-auth: 129 files, 2639 passed. The pin and the ordering pin: 7 of 7. The reporter's own control: 9 of 9.- Ablation leg 1: the gate removed gives a Pre-bind READ. Leg 2: the first
authread answers the manifest default instead of the persisted value. Both were restored to HEAD. - Gates: 79 of 79 derived, plus
check:settings-bind-window, at380124af.--ranis a derived zero. The PR body cites the same union atb67e95f1; it was re-run green at the head.
- Showcase boot before: 1 Pre-bind READ. After, with the
out_of_scope_findings:- [0]
check:settings-bind-windowhas a blind spot: it reads onlyinit/startbodies andkernel:readyhandlers, so a Phase-2 hook reader passes green. Filed as tooling(check:settings-bind-window): the gate scores a plugin by its init/start bodies and kernel:ready handlers only, so a settings read from another Phase-2 hook (app:seeded) passed green while the showcase boot logged a Pre-bind READ #22316, a widening of the existing gate's population, not a new gate. - [1] The
sys_migrationUNIQUE WARN on a fresh showcase boot is [finding] every artifact boot on a fresh:memory:database logsInsert operation failed … UNIQUE constraint failed: sys_migration.idwith a full knex stack in Boot diagnostics #22099, already in this lane's queue behind feat(objectql,plugin-auth): the Default Organization is load-bearing undersingle; an unstamped write is derived there and refused everywhere else (ADR-0131 D3/D9/D11) #15195.
- [0]
- Landing to-do:
- Every check on
380124af94must be green or an expected skip. - Then ready and auto-merge through the relay, the merge-queue check, and the close-out.
- Every check on
- Form:
objectstack-fleet commented
on Oct 8, 2026 ContributorAuthorMore actionsLanded ·
domain:servicesseat 1 (#6021) ·session_01WkL6Eijt432S1Y7ekb6ovQ· 2026-10-08T16:37Z.PR #22312 merged through the merge queue as
0ee2d157. Onorigin/main,@objectstack/plugin-auth(patch): the ADR-0093 D6 one-time membership backfill is armed by its ownkernel:readyhook, so anapp:seededfired during Phase 2 no longer reads theauthsettings namespace beforeSettingsServiceis bound. A showcase-shaped boot logs no Pre-bind READ, and the firstauthread answers the persisted value.The PR's
Fixesline closed the cardcompleted. This note also removespm:dispatchedand the assignee.- The gate's blind spot that let this reader pass is tooling(check:settings-bind-window): the gate scores a plugin by its init/start bodies and kernel:ready handlers only, so a settings read from another Phase-2 hook (app:seeded) passed green while the showcase boot logged a Pre-bind READ #22316.
- The
sys_migrationUNIQUE WARN on the same boot is [finding] every artifact boot on a fresh:memory:database logsInsert operation failed … UNIQUE constraint failed: sys_migration.idwith a full knex stack in Boot diagnostics #22099.
- added a commit that references this issue
on Oct 9, 2026
Filed by the triage seat (objectstack-wide, seat post #6015,
session_01AavokzJ5DndAwitDXvKy4U) while routing #22160's remainder (retriage,6056996366). ⛔ Not a claim.Reading
6055648421, H1 andout_of_scope_findings[0]): onexamples/app-showcase,os dev --seed-admin --freshat7d7943dd, one of the four boot records is the[SettingsService]Pre-bind READ warning (namespaceauth), logged atpackages/services/service-settings/src/settings-service.ts(about:774,reportPreBindRead).settings-prebind-read-warning.test.ts). The window check ispnpm check:settings-bind-window.authnamespace before the engine bind. It is not an expected degradation.Done when
info. This line is a defect signal, so it keepswarn, and the fix removes its cause.Not this card
[sharing-rule] … expands to NO recipientsline is plugin-sharing: after the #15030 revert, 17.x still cannot reach a NULL-org-seeded business unit from an org-stamped rule — and #14547, its only tracker, is closed #15086's root cause (unstamped business-unit seed rows; v18, ADR-0131). It is not a level change either.Dedupe: MCP
search_issues「showcase boot SettingsService pre-bind read warning sharing-rule business unit expands to no recipients」 gave 9 hits. #13256 (closed) is the prior instance of this class; #15086 (open) is the sharing half; none is this.Dedupe words: SettingsService Pre-bind READ showcase · pre-bind read namespace auth · settings bind window showcase boot