Repository navigation
fix(service-settings)!: a host that binds its own engine and states no organization source reads and writes as the organization D9 derives - #25350
Conversation
…s as the organization D9 derives through the bound engine Claude-Session: https://claude.ai/code/session_014FFTcf6msoLbXiD6BR7wHW Co-authored-by: Claude <noreply@anthropic.com>
… organization facts; pin the derivation The suites that bind their own engine now say what the composition holds: a double states the lean source at the bind site, and a suite running the real ObjectQL forwards that engine's registry onto the adapter. Pins: one organization through a registry-less adapter reads and writes as it; no organization object is loud on the real engine and zero organizations on a double; a stated lean source and no engine keep their readings. Claude-Session: https://claude.ai/code/session_014FFTcf6msoLbXiD6BR7wHW Co-authored-by: Claude <noreply@anthropic.com>
…rived organization; name the omission's symptom Claude-Session: https://claude.ai/code/session_014FFTcf6msoLbXiD6BR7wHW Co-authored-by: Claude <noreply@anthropic.com>
📓 Docs Drift CheckThis PR changes 1 package(s): 3 hand-written doc(s) NAME something this change touched and may need an implementation-accuracy re-verification:
What this run could not see
Coarse fallback — 9 page(s) merely mention a changed package (the pre-#9192 predicate, kept for the deliberately-wide backstop): Which tree this was computed onThis run read A worktree cut from an older # while this PR is open — GitHub drops the merge commit once it closes
git fetch origin ae29634cd9e271ae0c70d93165155fa0c588855a && git checkout ae29634cd9e271ae0c70d93165155fa0c588855a
# afterwards, rebuild it from the two parents, which stay fetchable
git fetch origin 4d7e2b1f384a4cb7226c8f629abdf33fede45cf1 4b63a5dd714e75ce46791a0cbd256675081000da && git checkout -B drift-repro 4d7e2b1f384a4cb7226c8f629abdf33fede45cf1 && git merge --no-ff 4b63a5dd714e75ce46791a0cbd256675081000da
node scripts/docs-audit/affected-docs.mjs --json 4d7e2b1f384a4cb7226c8f629abdf33fede45cf1
|
…the engine's dispatch predicate Claude-Session: https://claude.ai/code/session_014FFTcf6msoLbXiD6BR7wHW Co-authored-by: Claude <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_014FFTcf6msoLbXiD6BR7wHW Co-authored-by: Claude <noreply@anthropic.com>
Contract reviewServed-tier: Inputs read: card #25249 body, its claim ① Derived judgments1. The route (ruling item 1). 2. The public surface (item 2). 3. The test migration (item 4). Seven files, two ways, matching the ruling's split:
4. The pins (item 5). All in
5. Docs (item 6). 6. Legitimate callers. Plugin path: 7. Premises P1-P4 (item 9), re-read on the head by this review: P1 8. Gate verdicts on the head (40 check-runs, read at 02:17Z): ② Semver levelChangeset
③ Boundary flags
Implemented-by: VERDICT: PASS Generated by Claude Code |
席位核对 · ACCEPT · 2026-10-23T03:25Z
Generated by Claude Code |
…-c4vx2u # Conflicts: # packages/services/service-settings/src/settings-service.ts
Contract reviewServed-tier: Inputs read: card #25249 body, claim ① Derived judgments1. The merge delta is mechanical and the route is ruling item 1, unchanged. The merge touched one file with conflicts, 2. 3. The D9 predicate 4. Docblocks: no contradiction survives. Grep of the head's 5. Nothing else in the branch's surface moved through the merge. 6. #25352's new pin is consistent with this route. 7. Pins, tests, docs: as the earlier record verified. Record 8. Gate verdicts on ② Semver levelThe changeset ③ Boundary flags
Implemented-by: VERDICT: PASS Generated by Claude Code |
席位核对 · ACCEPT(新 head)· 2026-10-23T04:49Z
Generated by Claude Code |
Requested by Sales · project thread
Fixes #25249
Clause-②: no (narrowing)
Metadata-protocol: none
Ruling-ref: 6355249103 (letter A). Why
Clause-②: no (narrowing): nothing is added to any accept set.SettingsServiceOptionsand thebindEngineextras are unchanged. What narrows is which organization a host that binds its own engine, and states no organization source, reads and writes as.What changed
SettingsService.organizationReachOf(packages/services/service-settings/src/settings-service.ts) now takes ADR-0131 D9's answer fromorganizationDecision():organizationsource, when the host bound one (the plugin always does);no-organization-object, as before;resolveGuestOrganization(this.engine, posture), the same call the plugin's source makes, through the engine the service holds.There is no second predicate, no registry-sniffing arm, and no
catcharound the probe. An engine that exposes noregistry(the plugin's ownwrapEngineAsSettingsEngine, any hand-writtenSettingsEngine) cannot say the organization object is absent, so D9 reads it as registered and askssys_organization. A lean self-binding host declares the composition at its bind site:bindEngine(engine, audit, { organization: async () => ({ kind: 'no-organization-object' }) }), or hands over an engine whoseregistry.getObjectanswers.The retired docblock sentence ("Unset … reads as a composition that registers no organization object") is rewritten on the source type, its field and the
bindEngineextra. Theindex.tshost paragraph gains the declaration line and names the symptom of omitting it. Nodocs/adredit.Also: a changeset (
@objectstack/service-settingspatch, BREAKING, FROM/TO, the one-line fix).Branch
The harness assigned the branch
claude/project-thread-c4vx2u, not the repo-conventionclaude/issue-25249-…. This note is for the Duplicate Fix Guard. The card'sClaim:(6354964172) names this branch.Tests
Premises P1–P4 of the ruling were re-read on the branch before the migration:
registryHoldsObjectanswerstruewith noregistry.getObject(coreresolve-authz-context.ts:1235);wrapEngineAsSettingsEnginereturnsfind/insert/updateonly and forwardsfindverbatim; the plugin's onlybindEnginecall states a source; the plugin is the only non-test constructor in the repo. All four hold.Migration (45 tests in 7 files, red on the route alone). Each suite now states its composition truthfully, through a shared testkit (
src/__tests__/settings-organization.testkit.ts):LEAN_ORGANIZATION_SOURCEat its bind:settings-getmany,settings-user-rung-owner.pin,settings-system-context.pin(its constructor-engine case moves tobindEnginewith the same crypto provider and secret store);ObjectQLbehind the production adapter forwards that engine'sregistry(withEngineRegistry), so D9 answersno-organization-objectitself:settings-secret-rotation,sys-secret-orphan-report,settings-global-rung,settings-user-reference.pin.No default in the service, no stub
getObject, no new input. The four construction sites outside the package pass{ env }and no engine; they are untouched.Pins (
settings-organization-reach.pin.test.ts, new section):sys_organizationrow, no source: the tenant and user rungs read and write as that organization, and the rows with no organization do not answer;AuthzStoreUnavailableErrornamingsys_organization(pinned as measured;isMissingTableErrornot widened); on a double whose probe answers nothing, no tenant rung and the tenant write refused (SETTINGS_VALIDATION);Ablation (
scripts/ablation-replace.mjson the committed route): the engine branch oforganizationDecisionreplaced by the old lean answer. (a) and both (b) pins went red; (c), (d) and the plugin pins (e) stayed green, the expected direction. Restored: blob equals HEAD,git diff HEADempty.Runs on
e12143e6(after mergingorigin/main):vitest runinpackages/services/service-settings: 58 files / 987 tests passed (982 before the new pins).pnpm --filter @objectstack/service-settings typecheck: exit 0.!title) and the contract-review-tier review: reported on the card in the os-dev-report and on this PR.Out of scope
Hosts outside this repo that bind
SettingsServicethemselves (cloud control-plane mocks, embedded runtimes) are downstream of the published package. The changeset's FROM / TO and its one-line fix are the hand-off; no cloud card is filed from here.Generated by Claude Code
🤖 Generated with Claude Code
https://claude.ai/code/session_014FFTcf6msoLbXiD6BR7wHW