Repository navigation
Commit a839020
docs(spec): Seed.locale says which loads filter on it, and the ownership-queue contract says the registered service honours onFailedRead refuse (#30604)
domain:spec#3 · os-sales · session_01L6bhgTqqeHSwZYJmApbDCa
<!-- claude-projects-attribution: {"github_login":"os-sales"} -->
_Requested by **Sales** · [project
thread](https://claude.ai/code/project/chan_01D5bWFY3rFzgJcytD5gD4Uu?thread=cmsg_01D5bWFY3rFzgJcytD5gD4UuKB8Sg6c6sVpL96ruVvdxaf)_
Fixes #30347
Fixes #30389
Clause-②: no
Metadata-protocol: none
## 维护者速读
- **改了什么:** 只改 `@objectstack/spec` 的两处说明文字,不改任何代码或 schema 形状。①
`Seed.locale` 的说明不再说"安装不按语种挑":现在写明启动种子按应用的
`i18n.defaultLocale`;市场安装、重灌示例数据、重启自愈、按组织回放按"组织设置 → 安装/创建人语言 →
应用默认"挑;包应用和草稿发布仍不挑。② 队列服务契约不再说"注册的服务还不支持
`'refuse'`",写明激活台账读失败仍按"开启"是已定的决定,并写明 `isMember` 在 `'refuse'` 下何时答
`true`、何时拒绝。
- **为什么改:** PR #30346 和 PR #30386 合并后,这几句已发布的说明与运行时行为不符,作者照着读会得出错误结论。
- **风险与回滚:** 只动 TSDoc、`.describe()` 文案和由它生成的四个参考页,运行时零变化。回滚即撤 PR。
- **先看:** `packages/spec/src/data/seed.zod.ts`(`locale`
的说明,三级取值那一段);`packages/spec/src/contracts/ownership-queue-service.ts`(`isMember`
文档新增的四句)。
- **两分钟验证:** `git grep -n "do not filter by locale"
content/docs/references packages/spec/src`,预期每处命中都是"package apply and
draft publish do not filter by locale",不再有"The publish and install
paths"。
- **需要你做的:** 无,席位落地。
## What changes
Wording only, in one package (`@objectstack/spec`, `patch`). Each new
sentence below names the line on `origin/main` (`ffeda137b`, merged into
this branch in round 2) it rests on.
### #30347: `SeedSchema.locale` (`packages/spec/src/data/seed.zod.ts`)
**Before (docblock):** "Publishing and installing are locale-neutral
acts — this axis is scoped to the boot path, by decision. …
`SeedLoaderConfig.locale`, which only the boot path supplies … Package
apply, draft publish and marketplace install each build their own load
request and pass no locale …"
**Before (`.describe()`):** "The publish and install paths do not filter
by locale — they load every dataset and warn"
**After:** the docblock paragraph becomes three bullets, and the
`.describe()` says the same in one sentence. Round 1 (contract review
should-fix 1 and 2) names the reseeding user in the `.describe()` and
says what rung 1 reads for a load that names no organization.
| New sentence | Rests on |
|---|---|
| A request with no locale loads a `locale`-scoped dataset for every
locale, and the loader warns |
`packages/runtime/src/seed-locale.ts:30-32` (no rung ⇒ `undefined`,
`warnOnUnresolvedLocaleScope`);
`marketplace-install-local-plugin.ts:5305-5307` (spread, never `locale:
undefined`) |
| The boot seed and the dev hot-reload seeder filter on the app's
`i18n.defaultLocale` | `packages/runtime/src/app-plugin.ts:1937` (boot)
and `:2367` (hot reload), both `this.resolveSeedLocale()`, which reads
`i18n.defaultLocale` at `:2299-2303` |
| Install, reseed, heal and per-organization replay filter on what
`resolveSeedLocaleForLoad` resolves | its four callers:
`packages/cloud-connection/src/marketplace-install-local-plugin.ts:5239-5242`
(`applySideEffects`, reached by install `:2537` and reseed `:4376`),
`:1465` (restart heal), `:1728` (that plugin's own replayer);
`packages/runtime/src/app-plugin.ts:1825-1830` (the AppPlugin
per-organization replayer). All four go through `runInlineSeed`
(`:5287-5292`) or call the producer directly |
| Rung 1: the `localization.locale` setting for the organization, only
when `source` is not `'default'` | `seed-locale.ts:135-140` |
| Rung 1, a load that names no organization (a single-tenant install or
reseed, the restart heal) still reads the setting: the environment
override answers first, then the `global` setting, then, where no wall
stands and the deployment holds exactly one organization, that
organization's own setting | `seed-locale.ts:137`
(`settings.get('localization', 'locale', {})` when no organization is
named);
`packages/services/service-settings/src/settings-service.ts:1951-1966`
(the env override answers before any row is read), `:2119` and `:2175`
(the cascade order global, tenant, default, first non-null wins),
`:2137-2149` (the tenant rung, read for a tenant-scoped key:
`localization` is `scope: 'tenant'`,
`manifests/localization.manifest.ts:39`);
`settings-service.ts:2221-2227` (`organizationReachOf` hands an
organization-less caller to `resolveOrganizationReach`),
`packages/core/src/security/organization-reach.ts:87-95` (no wall ⇒ the
derived organization becomes the reach),
`packages/core/src/security/resolve-authz-context.ts:1676-1696`
(`resolveGuestOrganization`) and
`packages/core/src/security/system-write-organization.ts:115-124`
(`single` posture and exactly one `sys_organization` ⇒ `derived`); the
heal passes no organization (`marketplace-install-local-plugin.ts:1465`)
|
| Rung 2: `sys_user.locale` of the user who installs, reseeds or founds,
when one started the load (the `.describe()` now says "the installing,
reseeding or founding user") | `seed-locale.ts:142-153`; install passes
`admission.userId` (`marketplace-install-local-plugin.ts:1744` to
`:2537` to `:5239-5241`), reseed passes the reseeding user's
`admission.userId` (`:4376` to `:5239-5241`), the heal passes no user
(`:1462-1465`) |
| Rung 3: the app's `i18n.defaultLocale`, when the caller names one |
`seed-locale.ts:128-129, 155`; the marketplace plugin's own replayer
passes none (`marketplace-install-local-plugin.ts:1723-1728`) |
| A setting or user read that fails seeds nothing rather than another
language | `seed-locale.ts:34-39` (the throw reaches the caller, which
writes nothing) |
| Package apply and draft publish pass no locale |
`packages/runtime/src/domains/packages.ts:4000-4007` and
`packages/metadata-protocol/src/protocol-publish-batch.ts:115-125`:
neither request carries `locale` |
| The #17011 bound is narrowed to those two paths by the maintainer's
ruling (2026-11-05) | `seed-locale.ts:7-9` ("maintainer ruling A,
2026-11-05 … reversing the install-path bound recorded on
`SeedSchema.locale`") |
The `.describe()` change regenerates four references pages, all
committed: `content/docs/references/data/seed.mdx`,
`data/seed-loader.mdx`, `api/package-api.mdx`, `kernel/manifest.mdx`.
### #30389: `packages/spec/src/contracts/ownership-queue-service.ts`
1. **Before:** "the registered one, `@objectstack/plugin-sharing`'s,
honours `'refuse'` from #29604's code half on, so until then a caller
passing `'refuse'` still gets `'nobody'`." **After:** "the registered
one, `@objectstack/plugin-sharing`'s, honours `'refuse'`." Rests on
`packages/plugins/plugin-sharing/src/ownership-queue-service.ts:270-279`
(`expandMembers`) and `:290-297` (`isMember`), which take `onFailedRead`
and wrap a failure in `queueReadRefusal` (`:168`).
2. **Before:** "whether that posture moves is #29604's code half to
decide." **After:** "That posture stays by decision: it is the one the
sharing-rule service takes for the same ledger on its own `position`
recipients, so a queue's position and a rule's position cannot disagree
during an outage." Rests on `ownership-queue-service.ts:670-688`
(`switchedOff` catches the ledger read and returns nothing switched off,
under either answer) and
`packages/plugins/plugin-sharing/src/sharing-rule-service.ts`,
`readPositionActive` (a failed ledger read answers active).
3. **Added to `isMember`'s doc:** "Under `'refuse'` the registered
service reads the queue's own rows (`sys_queue`, `sys_queue_member`)
first, and a failure there rejects at once, since without them the
queue's member set would be incomplete (a declared queue's own
specifications come from the registry, but its authored additions come
from `sys_queue_member`). Past them it answers `true` as soon as a read
that succeeded reaches `userId`, even beside a failed read, and rejects
only when no successful read reaches `userId` and one failed." Rests on
`ownership-queue-service.ts:232-261` (`resolve` reads `authoredQueueRow`
and `authoredMemberSpecs` with `refuse` before any specification; a
declared queue's specifications come from `declaredMemberSpecs` at
`:243`, its authored additions from `authoredMemberSpecs` at `:237`) and
`:476-496` (`specsReachUser`: returns `true` on the first reaching spec,
keeps the first failure, throws it only after the walk). Round 1
(contract review should-fix 3) replaced the earlier reason, "since
without them the call does not know what to ask", which overstated it
for a declared queue.
Left as it was, because it is true: the `'nobody'` bullet still says the
admission consumers (D3, D4, D5) take `'nobody'`. Measured:
`sharing-service.ts:952`, `security-plugin-authz-middleware.ts:1610` and
`objectql/src/assignment-rules.ts:293` call without the option; only D6
(`approval-service.ts:3541`) and D7 (`sharing-rule-service.ts:1752`,
`:2198`) pass `'refuse'`.
## Tests
No behaviour changed, so no new test. Round 2 readings were taken on
head `d0863ab93` (after merging `origin/main` `ffeda137b`).
- `pnpm --filter @objectstack/spec exec vitest run --maxWorkers=2
src/data/seed.test.ts src/data/seed-loader.test.ts
src/data/seed-file.zod.test.ts
src/data/define-seed-record-keys.test.ts`: 4 files, 111 tests passed
(the spec tests that touch `seed.zod.ts`; round 2 changes no other
file's source).
- `pnpm --filter @objectstack/spec build`, then `gen:docs`, then
`check:generated`: "All 14 generated artifacts are up to date".
- `pnpm --filter @objectstack/spec typecheck`: exit 0, 0 errors.
- eslint on `seed.zod.ts`: exit 0.
- `node scripts/check-changeset-no-major.mjs --base origin/main --event`
(this body as a `pull_request` event): exit 0, "a non-protocol change at
`patch`".
- `node scripts/pm/dispatch-gates.mjs --commands --repo
objectstack-ai/objectstack`: 96 commands, run after a full `turbo run
build` (119/119), all exit 0, exit codes recorded before any pipe.
`--ran`: "96 derived, 96 run, 0 NOT-MEASURED, 0 UNRUN".
## Acceptance notes
- **The dispatch's reading of #30389 sentence 2 was wider than the
code.** "Whether that posture moves" names the activation-ledger read
posture, not the admission consumers' answer. PR #30386 kept the ledger
posture ("switched on" on a failed read). The new sentence says that.
The admission consumers' `'nobody'` was already stated and still holds
(see above).
- **`isMember` under `'refuse'` does not answer `true` beside every
failed read.** A failed `sys_queue` or `sys_queue_member` read rejects
before any specification is read, even for a declared queue whose
positions might reach the user (PR #30386's acceptance notes say the
same). The doc says so, so that "rejects only when no successful read
reaches the user" is not read as covering the queue's own rows.
- **One more published sentence turned false by PR #30346, outside this
PR's file surface.** The `locale` row note in
`packages/spec/liveness/seed.json` still says the marketplace install
passes no locale and calls publish and install locale-neutral. Same
defect class; not touched here because the claim covers only
`seed.zod.ts`, `ownership-queue-service.ts`, the regenerated pages and
one changeset, and the note is a long ruling record, not a mechanical
edit. Reported to the seat.
- **Card #30347 has two commits, not one.** The regenerated references
pages and the changeset went in a second commit (`9ba263492`) after the
source commit (`3bbc3eb9b`), because a force-push to fold them in was
refused in this session. #30389 is the third commit (`9d61fa4aa`).
- **Round 1.** The contract review (PASS, no must-fix) listed three
should-fix wording items; all three are in this PR: `d56c29454` (#30347,
rung 1 and the reseeding user), `d0db2c6a9` (#30389, the `isMember`
reason), `8629666d9` (the four regenerated references pages). The
liveness-note finding is filed separately as #30607. `origin/main` was
merged in first (`871582d63`); the moved citations (`packages.ts`,
`approval-service.ts`) are updated above.
- **Round 2.** The delta review at `8629666d9` (PASS, one should-fix)
found rung 1's organization-less parenthetical read as exhaustive while
missing a third source, the deployment's one organization's own row.
`d0863ab93` adds it to the docblock; the `.describe()` and the changeset
are unchanged (their wording stays true). `origin/main` `ffeda137b` was
merged first (`e2f2e2175`), and `15456e098` regenerates
`package-api.mdx` after that merge (its only difference from
`origin/main` is the two `locale` rows). The marketplace plugin moved in
that merge, so its line citations above are re-measured.
- `packages/spec/CHANGELOG.md` still carries the released "publish and
install paths do not filter by locale" entry. It is release-owned and
correct for the release that shipped it; not edited.
---
_Generated by [Claude
Code](https://claude.ai/code/session_01L6bhgTqqeHSwZYJmApbDCa)_
🤖 Generated with [Claude Code](https://claude.com/claude-code)
https://claude.ai/code/session_01L6bhgTqqeHSwZYJmApbDCa
---------
Co-authored-by: Claude <noreply@anthropic.com>1 parent ca37605 commit a839020
7 files changed
Lines changed: 61 additions & 21 deletions
File tree
- .changeset
- content/docs/references
- api
- data
- kernel
- packages/spec/src
- contracts
- data
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
| 1 | + | |
| 2 | + | |
| 3 | + | |
| 4 | + | |
| 5 | + | |
| 6 | + | |
| 7 | + | |
| 8 | + | |
| 9 | + | |
| 10 | + | |
| 11 | + | |
| 12 | + | |
| 13 | + | |
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
246 | 246 | | |
247 | 247 | | |
248 | 248 | | |
249 | | - | |
| 249 | + | |
250 | 250 | | |
251 | 251 | | |
252 | 252 | | |
| |||
354 | 354 | | |
355 | 355 | | |
356 | 356 | | |
357 | | - | |
| 357 | + | |
358 | 358 | | |
359 | 359 | | |
360 | 360 | | |
| |||
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
233 | 233 | | |
234 | 234 | | |
235 | 235 | | |
236 | | - | |
| 236 | + | |
237 | 237 | | |
238 | 238 | | |
239 | 239 | | |
| |||
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
33 | 33 | | |
34 | 34 | | |
35 | 35 | | |
36 | | - | |
| 36 | + | |
37 | 37 | | |
38 | 38 | | |
39 | 39 | | |
| |||
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
100 | 100 | | |
101 | 101 | | |
102 | 102 | | |
103 | | - | |
| 103 | + | |
104 | 104 | | |
105 | 105 | | |
106 | 106 | | |
| |||
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
62 | 62 | | |
63 | 63 | | |
64 | 64 | | |
65 | | - | |
66 | | - | |
| 65 | + | |
| 66 | + | |
| 67 | + | |
| 68 | + | |
67 | 69 | | |
68 | 70 | | |
69 | 71 | | |
| |||
76 | 78 | | |
77 | 79 | | |
78 | 80 | | |
79 | | - | |
80 | | - | |
| 81 | + | |
81 | 82 | | |
82 | 83 | | |
83 | 84 | | |
| |||
157 | 158 | | |
158 | 159 | | |
159 | 160 | | |
| 161 | + | |
| 162 | + | |
| 163 | + | |
| 164 | + | |
| 165 | + | |
| 166 | + | |
| 167 | + | |
| 168 | + | |
160 | 169 | | |
161 | 170 | | |
162 | 171 | | |
| |||
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
261 | 261 | | |
262 | 262 | | |
263 | 263 | | |
264 | | - | |
265 | | - | |
266 | | - | |
267 | | - | |
268 | | - | |
269 | | - | |
270 | | - | |
271 | | - | |
272 | | - | |
273 | | - | |
274 | | - | |
| 264 | + | |
| 265 | + | |
| 266 | + | |
| 267 | + | |
| 268 | + | |
| 269 | + | |
| 270 | + | |
| 271 | + | |
| 272 | + | |
| 273 | + | |
| 274 | + | |
| 275 | + | |
| 276 | + | |
| 277 | + | |
| 278 | + | |
| 279 | + | |
| 280 | + | |
| 281 | + | |
| 282 | + | |
| 283 | + | |
| 284 | + | |
| 285 | + | |
| 286 | + | |
| 287 | + | |
| 288 | + | |
| 289 | + | |
| 290 | + | |
| 291 | + | |
| 292 | + | |
275 | 293 | | |
276 | 294 | | |
277 | | - | |
| 295 | + | |
278 | 296 | | |
279 | 297 | | |
280 | 298 | | |
| |||
0 commit comments