Skip to content

Commit 6038ff6

Browse files
committed
Merge origin/main into claude/issue-24341-identity-default-row-scope
2 parents 0367cb1 + 5a871e4 commit 6038ff6

41 files changed

Lines changed: 4710 additions & 279 deletions

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.
Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
---
2+
'@objectstack/core': patch
3+
---
4+
5+
perf(core): an authenticated request resolves its grants in one round fewer, and a write to an unrelated object no longer retires the localization cache
6+
7+
Clause-②: no (no accept set, served body or authz answer moves: fewer queries and rounds for the same answer; owes the seat's review of record before the queue, at `CONTRACT_REVIEW_TIER` by the seat's choice, since it changes when authz and localization caches are trusted)
8+
9+
- **The Default Organization read and the activation-ledger read are one wave.** `resolveUserAuthzGrants` read `sys_organization` (by slug) and then `sys_metadata_activation`, each awaited after the other. Both depend only on the first wave of reads, so they now run together. While the organization read is still open, the ledger is asked about every permission-set name that may apply. In one case that adds the name `admin_full_access` to the ledger's `name IN` list: an organization-owned `admin_full_access` grant that turns out not to anchor platform standing. The envelope is unchanged in every case: positions, permissions, `org_user_ids`, `accessible_org_ids` and posture. The organization read's filter is unchanged, and so are the organization-less grant readings.
10+
- **The localization success cache is retired only by a write that can change its answer.** That is a write to `sys_setting` or `sys_platform_setting`, observed after it settles by an engine middleware. A non-`write` epoch reason (a peer node's `remote` hint, `metadata`, `manual`), a `localization` settings change or `OS_LOCALIZATION_CACHE_TTL_MS` also retires it. Before, any engine write to any object retired it, so on an idle boot the outbox pollers' claim updates made warm requests read the settings again. A success is cached only on an engine that has both the write epoch and `registerMiddleware`. With `OS_LOCALIZATION_CACHE_TTL_MS=0`, nothing is registered on the engine. The failure memo for a missing `sys_setting` table is unchanged: it is retired only by its TTL.
Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
---
2+
'@objectstack/metadata-protocol': patch
3+
---
4+
5+
perf(metadata-protocol): the served by-name read takes its stored rows from the overlay cache, and only a `sys_metadata` write retires that cache
6+
7+
Clause-②: no (no accept set, served body or authz answer moves: fewer queries and rounds for the same answer; owes the seat's review of record before the queue, at `CONTRACT_REVIEW_TIER` by the seat's choice, since it changes when authz and localization caches are trusted)
8+
9+
- **`getMetaItem` reads the item's active `sys_metadata` rows through the overlay cache.** The list read already used that cache. The REST API-exposure gate reads the object by name on every `/data` request, and that read was its one uncached `sys_metadata` round trip. The cached value is the raw row set. Parsing, merging and registry hydration still run on every call, hit or miss. Only the served face reads through the cache. The protocol's own authoring checks, such as the destructive-change check before an object save, still read the store on every call.
10+
- **Only a write to `sys_metadata` retires the cache.** The write is observed by an engine middleware after it settles. Every non-`write` epoch reason also retires it, including the metadata cluster bridge's `remote` bump, and `OS_METADATA_OVERLAY_CACHE_TTL_MS` still bounds it. Before, any engine write to any object retired it. A row set is cached only on an engine that has both the write epoch and `registerMiddleware`. With the TTL set to `0`, nothing is registered on the engine.
Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
---
2+
'@objectstack/spec': patch
3+
---
4+
5+
`record:related_list`'s `parentDataSource` is described as rendered
6+
7+
Clause-②: no (describe and TSDoc text only: `RecordRelatedListProps` accepts exactly what it accepted before, and every refusal keeps its issue `code` and `path`)
8+
9+
- The `parentDataSource` describe no longer opens with `[PLANNED — not yet rendered]`. The console renders the key: a related list reads its parent record from the binding, the first row by its `sort`, and draws a notice in its place when no row answers, never an empty list. Without the key, the record page's record is the parent, as before. The rest of the describe is unchanged.
10+
- The key's TSDoc names the console's read points in place of the planned note, and no longer calls the `api` data source unrendered.
Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
---
2+
'@objectstack/objectql': patch
3+
---
4+
5+
fix(objectql): a bound action named like an action its target object already lists, with a different declaration, is reported at boot naming both packages; `registerAction` reports replacing another owner's handler
6+
7+
Clause-②: no (a warning only: no accept set narrows, no door answer moves, and no published surface gains a key; owes the seat's review of record before the queue)
8+
9+
- **The listing.** A top-level action bound by `objectName` to an object that already lists an action of the same name is still not attached: the object keeps serving its own declaration, as the action router does. When the two declarations differ, the boot now warns once per pair, at `kernel:ready`, naming the object, the action, the package whose declaration is served and the package whose declaration is not listed. Two packages binding different declarations of one name to a third package's object are reported the same way (the first one attached is served).
10+
- **Not a collision.** One declaration carried twice is compared without its provenance stamps (`_packageId`, `_provenance`, the `_lock` envelope, `protection`) and without `objectName`, and is not reported: a same-stack bound action merged onto its object, or the same declaration shipped by two packages.
11+
- **Later artifacts.** An artifact registered after the boot (a marketplace install, a ledger rehydrate) is judged once it has registered, for the pairs naming one of its packages; a pair the boot already reported is not reported again.
12+
- **The handler.** `ObjectQL.registerAction` keys handlers by `OBJECT:ACTION`. A registration under a key another owner holds still replaces it (the last registration runs), and now warns, naming both owners: the action door runs the new owner's handler whichever declaration the object lists, and withdrawing that owner leaves the action with no handler. The same owner re-registering is silent, and so is the authored-action resync, which tears its set down first. A registration with no package is its own owner.
13+
- **Unchanged.** Nothing is refused and no boot fails on it; the served `actions`, the router's precedence and which handler runs are as before. Measured on stock CRM, showcase and todo boots: no new warning.
14+
- **Fix.** Rename one of the two actions, or drop the duplicate declaration.

‎.changeset/pre.json‎

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
11
{
2-
"mode": "pre",
2+
"mode": "exit",
33
"tag": "next"
44
}

‎.claude/skills/pm-dispatch/references/decision-analysis.md‎

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@
66

77
- 每批恰好 5 张,末批不足照呈;呈完即停等回批,⛔ 永不一条消息投多批。
88
- 批序:在飞被阻塞 > 运营阻塞 > 用户可见 > 结构性;卡面分析只一层 = 立卡席六项 + 四棱。
9-
- 总监批前卡评论是增量:命令复核前提,逐棱同异,推荐/回退/口径;不同意字母才展开四维。
9+
- 总监批前卡评论是增量:命令复核前提,逐棱同异,推荐/口径;不同意字母才展开四维。
1010
- 回批一行式逐卡对应字母选项;带的附加条件随执行落地,⛔ 不为它另起请示轮。
1111
- 呈递详解 = 逐卡散文按正文六项;⛔ 表格行、判词、栅格不构成详解,压缩批次是违规。
1212
- 受管草稿批例外:一 PR 一行(改了什么/为什么/席位意见/一个动作),⛔ 不套六项;速读住 PR。
@@ -23,7 +23,7 @@
2323
- 业务含义直译:每选项配一句业务上等于什么,允许行业类比。
2424
- 四轴从业务立场论证(含四棱行):论据是客户、产品或事故后果,机制名词只作括号补充。
2525
- 防 AI 犯错轴写清出错时谁看到什么:响亮拒绝,还是静默泄露。
26-
- 推荐 + 回退 + 置信缺口:荐一项、给回退项、明说看不见什么;⛔ 不沿用他席推荐。
26+
- 推荐 + 置信缺口:只荐一,余各一句不取理由;盲区明说,⛔ 不因此另荐;⛔ 不沿用他席推荐。
2727
- 推荐先只按①得出:终态句写两年后该能力的平台样子与主流平台建模,点名参照。
2828
- ②③④ 与工作量只调时序分期 ⛔ 不翻字母;契约迁就实现、分期半量 ⛔ 不作推荐。
2929
- ①读数相近而工作量差 ≥3× 推荐须点名;翻字母只凭维护者卡上另裁,唯④新增门禁例外。

‎.claude/skills/pm-dispatch/references/lanes/hotcrm.md‎

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@
55
## 形态
66

77
- 本席为姊妹仓执行座位:在 hotcrm 仓认领、派发、复核、落地。
8-
- ⛔ 不产 `domain:*`、type 与定级,纪律全文在 SKILL.md 多仓协调规则 4。
8+
- ⛔ 不产 `domain:*`;机械三务(含首触定级)按 cross-repo-coordination.md 单车道仓条自理。
99

1010
## 范围
1111

@@ -30,7 +30,7 @@
3030
- 发现 declared 不等于 enforced 先问生产者在哪;生产者在平台 ⇒ 上游卡。
3131
- 不扩散需求:创业阶段聚焦原则全额适用,四维框架的不扩散那一轴是本车道首要过滤器。
3232
- 框架单源在 SKILL.md 〈升级与决策〉,⛔ 不另抄。
33-
- 投机功能形状不入队:走决策箱或关 not planned;定级归分诊或维护者,本席只供证据。
33+
- 投机功能形状不入队:走决策箱或关 not planned;定级按 filing-gate.md,理由落卡,维护者可否决。
3434
- 双版本兼容:hotcrm 必须在 objectstack 社区版可运行,部署到 objectos 企业版时获得企业能力。
3535
- 操作判据:hotcrm 元数据只可依赖社区能力面,企业特性恒为增强,⛔ 永不硬依赖。
3636
- 会 break 社区版运行的卡定义上是错范围,回分诊。

‎content/docs/concepts/metadata-lifecycle.mdx‎

Lines changed: 12 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -206,13 +206,18 @@ The hash is `sha256:` + 64-hex of a canonical JSON serialization of the body, wi
206206
> writes.
207207
>
208208
> ⚠️ **"Peers re-read from the shared database" has one bound worth naming.** The
209-
> `sys_metadata` overlay read inside `protocol.ts:getMetaItems` is cached across
210-
> requests, keyed on the engine write epoch. A **local** write advances that epoch
211-
> before its own middleware chain runs, so read-your-writes is exact on the node
212-
> that made the write. A **peer's** write does not: `metadata.changed` invalidates
213-
> the MetadataManager caches this note is about, but it does not retire the
214-
> overlay-read cache. What retires that on a peer is either of two receipt
215-
> paths that bump the same local write epoch: the `metadata.mutated`
209+
> `sys_metadata` overlay rows `protocol.ts` reads — the list read inside
210+
> `getMetaItems`, and the served by-name read inside `getMetaItem` — are cached
211+
> across requests. A **local** `sys_metadata` write retires them when its
212+
> statement completes (an engine middleware sees it), so read-your-writes is
213+
> exact on the node that made the write; a write to any other object does not
214+
> retire them. Inside an engine transaction the statement completes before the
215+
> commit, so a read landing between the two can keep the pre-commit rows until
216+
> the next `sys_metadata` write or the TTL. A **peer's**
217+
> write does not either: `metadata.changed` invalidates the MetadataManager
218+
> caches this note is about, but it does not retire the overlay-read cache. What
219+
> retires that on a peer is either of two receipt paths that bump the local
220+
> write epoch for a non-`write` reason, which retires the cache wholesale: the `metadata.mutated`
216221
> channel's own — `protocol.ts`'s `applyRemoteMetadataMutation` calls
217222
> `bumpWriteEpoch(engine, 'remote')` right after its registry converges and
218223
> before local listeners run (#13609, 2026-09-03) — or the `authz.invalidated`

‎content/docs/deployment/environment-variables.mdx‎

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -375,8 +375,8 @@ the hosted ObjectOS Cloud control plane.
375375
| `OS_SANDBOX_HOOK_TIMEOUT_MS` | number | `250` | Default **CPU-time** budget for a sandboxed **hook** body (QuickJS, ADR-0102): how much *VM-active* time a body may burn — idle host-await time and a nested hook's own run are NOT charged. A loaded/slow host rarely needs to raise this now (it is not wall-clock), but the knob remains. Only a positive integer is honored; unset / non-numeric / non-positive keeps the 250ms default. A hook body's own declared `timeoutMs` still wins over this. |
376376
| `OS_SANDBOX_ACTION_TIMEOUT_MS` | number | `5000` | Default **CPU-time** budget for a sandboxed **action** body (QuickJS). Same resolution rules as the hook variant above (positive integer only; an action body's own `timeoutMs` still wins). |
377377
| `OS_SANDBOX_WALL_CEILING_MS` | number | `30000` | Wall-clock ceiling (ADR-0102) — the backstop that cuts a hook/action body stuck on a host call that never settles (which burns no CPU, so the CPU budget alone would never fire). The effective ceiling is `max(this, cpuBudget)`, so it can never cut a body still inside its CPU budget. Positive integer only; unset keeps 30s. |
378-
| `OS_LOCALIZATION_CACHE_TTL_MS` | number | `30000` | Staleness bound, in milliseconds, for the cross-request cache of a workspace's reference localization (`timezone` / `locale` / `currency`, read from `sys_setting`) — leg C of #11633. `0` means **off**, a real path that restores the uncached query pattern exactly. Unlike `OS_AUTHZ_GRANTS_CACHE_TTL_MS` (which is off by default) this one ships **on**, because its invalidation is synchronous and in-process rather than TTL-bound: a `localization` settings change and any engine write both retire a cached answer immediately, so the TTL only bounds what neither seam can see — a write made on another replica with no cluster bridge attached at all: neither the `metadata.mutated` channel's receipt path (`applyRemoteMetadataMutation` calls `bumpWriteEpoch`, #13609) nor the `authz.invalidated` bridge, both of which bump this same epoch when attached. ⚠️ A malformed value reads as `0` (off), the opposite arm from the grants variable and deliberately so: there `0` is also the default, whereas here folding `3OOO` (letter O) into the default would hand you a **longer** window than the one you were setting. Deployment config only — never a settings row, because `sys_setting` is the table this cache caches. |
379-
| `OS_METADATA_OVERLAY_CACHE_TTL_MS` | number | `30000` | Staleness bound, in milliseconds, for the cross-request cache of the `sys_metadata` overlay read inside `getMetaItems` — leg D of #11633. `0` means **off**, a real path that restores the uncached query pattern exactly. Ships **on**, for the same reason as `OS_LOCALIZATION_CACHE_TTL_MS`: invalidation is synchronous and in-process, because every `sys_metadata` write goes through the engine and so advances the write epoch that retires the entry before the next read. What is cached is the overlay ROW SET only — never the merged answer — so the SchemaRegistry, the MetadataService and the artifact table are re-consulted on every call, cached or not, and the read-side registry hydration keeps running on a cache hit. The TTL therefore bounds one thing: a write made on **another replica** with no cluster bridge attached at all — neither the `metadata.mutated` channel's own receipt path (`applyRemoteMetadataMutation` calls `bumpWriteEpoch` right after registry convergence, #13609) nor the `authz.invalidated` bridge, either of which retires this entry the moment it is attached and fires. ⚠️ A malformed value reads as `0` (off) — same arm and same reason as `OS_LOCALIZATION_CACHE_TTL_MS`. Deployment config only, never a settings row. |
378+
| `OS_LOCALIZATION_CACHE_TTL_MS` | number | `30000` | Staleness bound, in milliseconds, for the cross-request cache of a workspace's reference localization (`timezone` / `locale` / `currency`, read from `sys_setting`) — leg C of #11633. `0` means **off**, a real path that restores the uncached query pattern exactly. Unlike `OS_AUTHZ_GRANTS_CACHE_TTL_MS` (which is off by default) this one ships **on**, because its invalidation is synchronous and in-process rather than TTL-bound: a `localization` settings change, a write to `sys_setting` or `sys_platform_setting` (the tables the answer is read from), and a non-`write` bump of the engine write epoch each retire a cached answer immediately. A write to any other object does not. So the TTL only bounds what none of these seams can see — a write made on another replica with no cluster bridge attached at all: neither the `metadata.mutated` channel's receipt path (`applyRemoteMetadataMutation` calls `bumpWriteEpoch`, #13609) nor the `authz.invalidated` bridge, both of which bump this same epoch when attached. ⚠️ A malformed value reads as `0` (off), the opposite arm from the grants variable and deliberately so: there `0` is also the default, whereas here folding `3OOO` (letter O) into the default would hand you a **longer** window than the one you were setting. Deployment config only — never a settings row, because `sys_setting` is the table this cache caches. |
379+
| `OS_METADATA_OVERLAY_CACHE_TTL_MS` | number | `30000` | Staleness bound, in milliseconds, for the cross-request cache of the `sys_metadata` overlay rows read inside `getMetaItems` (the list read) and `getMetaItem` (the served by-name read the REST API-exposure gate makes on every `/data` request) — leg D of #11633. `0` means **off**, a real path that restores the uncached query pattern exactly. Ships **on**, for the same reason as `OS_LOCALIZATION_CACHE_TTL_MS`: invalidation is synchronous and in-process, because every `sys_metadata` write goes through the engine, whose middleware retires the entry when the write's statement completes, before the next read. Outside a transaction that is the commit. Inside an engine transaction it is earlier than the commit: a read that lands between the statement and the commit can keep the pre-commit rows until the next `sys_metadata` write or the TTL. A write to any other object does not retire it; a non-`write` bump of the engine write epoch retires it wholesale. What is cached is the overlay ROW SET only — never the merged answer — so the SchemaRegistry, the MetadataService and the artifact table are re-consulted on every call, cached or not, and the read-side registry hydration keeps running on a cache hit. The TTL therefore bounds one thing: a write made on **another replica** with no cluster bridge attached at all — neither the `metadata.mutated` channel's own receipt path (`applyRemoteMetadataMutation` calls `bumpWriteEpoch` right after registry convergence, #13609) nor the `authz.invalidated` bridge, either of which retires this entry the moment it is attached and fires. ⚠️ A malformed value reads as `0` (off) — same arm and same reason as `OS_LOCALIZATION_CACHE_TTL_MS`. Deployment config only, never a settings row. |
380380
| `OS_INLINE_SEED_BUDGET_MS` | number | `8000` | Time budget for synchronous seed execution at boot before deferring to a worker. |
381381
| `OS_TENANT_AUDIT` | flag | `1` | Set to `0` to silence the tenant-isolation audit warnings emitted by the SQL driver. |
382382

0 commit comments

Comments
 (0)