Repository navigation
Commit a2e94c2
Fixes #22678
Clause-②: no
**Tier H, draft only.** `docs/adr/**` is a governed surface (Prime
Directive #14): this PR stays a draft and lands only on the maintainer's
APPROVED review. ⛔ No seat readies, queues or arms auto-merge on it.
#22590 remains open (it is `Blocked-by:` this card and objectui#12081).
## What this PR carries
One new record, **ADR-0139**
(`docs/adr/0139-engine-internal-semi-join-leaf-readable-parent-ids.md`,
Proposed), transcribing ruling `6097072172` on #22590 (batch #312 item
2, maintainer 「同意」, letter A as the end state with the ADR as its first
deliverable), plus one status-line pointer each on ADR-0055 and
ADR-0056. It declares and implements nothing: no code, no spec, no
driver file. The execution cards are listed in the record and ⛔ not
filed.
What the record decides, in the ruling's own scope and nothing wider:
- **D1** — one engine-internal filter leaf, `ReadableIds` ("the ids of
object X this caller may read"), a branded comparand in the position a
list of ids holds today (`$in` on the pointer field). No request can
spell it: a JSON `where` cannot carry the brand, and the unbranded shape
is already refused `INVALID_FILTER` / 400 by the comparand door. Losing
the brand fails closed, never silently widens.
- **D2** — ObjectQL composes it per request from `canReadObject` +
`getReadFilter` (both halves, fail closed, deny sentinel ⇒ `$in: []`),
lowers the scope the way the parent's own read is lowered, marks it
`policy`, and leaves the tenant wall to the driver.
- **D3** — `driver-sql` compiles it as a subquery inside
`applyTenantScope`; sqlite-wasm and local Turso inherit; the Turso
remote transport and `driver-memory` implement it; `driver-mongodb`
refuses loudly until a card measures a pipeline form. The refusal is one
ADR-0112 envelope (500 `INTERNAL_ERROR`, not an author error). ⛔ No
`DriverCapabilities` bit returns.
- **D4** — the six consumers (activity stream, compliance ledger,
withheld-update rule, comment threads, reactions, attachments) emit one
leaf per registered parent object and no pre-scan; the withheld-update
rule moves to a writer-stamped `changed_fields` column with an
idempotent backfill through the migration door.
- **D5** — three classes stay on today's bounded fail-closed probe,
recognised by the engine refusing to compose a leaf for them: a
delegated (on-behalf-of) caller; a parent narrowed by an object-scoped
read middleware; a federated parent — which includes every parent of
`sys_activity` on a deployment that registers the ADR-0057 `telemetry`
datasource (recorded as a limit).
- **D6** — ADR-0055 row (a) and ADR-0056's non-goal are reversed for
this one leaf only; the RLS compiler gains no subquery form and ADR-0055
§2's mechanism (b) stands.
## Verification
Premise checked on `origin/main` `d8830c28` by symbol, not by the card's
line numbers: `querySubqueries` and `joins` are retired keys in
`packages/spec/src/data/driver.zod.ts`; `PARENT_GATE_SCAN_LIMIT = 2_000`
in `parent-record-read-gate.ts`; `RELATION_FILTER_ID_CAP = 1000` in
`packages/objectql/src/relation-filter-lowering.ts` (the card's
`engine.ts` attribution for the cap's definition is stale — the engine
imports it); ADR-0055's row (a) and ADR-0056's non-goal read as the
ruling quotes them. The six consumers were located at function level;
only the activity stream was measured (report `6095496633`), the other
five are source readings and the record says so per row.
Gates at head `f87ef2345a` (after two wording rounds answering contract
review 6100682640), derived by `node scripts/pm/dispatch-gates.mjs
--commands --repo objectstack-ai/objectstack` from the branch's change
set (19 commands, identical to the dispatch's list), each exit captured
before any pipe, reconciled with `--ran`: **19 derived, 19 run, 0
NOT-MEASURED, 0 UNRUN**. `check:doc-formula-expressions` first exited 3
(PREREQUISITE NOT MET: no `@objectstack/formula` / `@objectstack/lint`
dist) and was re-run after `turbo run build
--filter=@objectstack/formula --filter=@objectstack/lint` under the
verify lock (VERDICT command-exit 0); the re-run exited 0.
`check-adr-symbol-anchors`: 2306 anchors across 142 records resolve, 0
line anchors (every reference this record makes is now a full
`path#symbol` anchor the gate judges). `check-adr-links`: 725
destinations resolve. No package test or typecheck is owed (no package
touched); no ablation (nothing built). Control-byte self-scan of the
three files: none.
## Acceptance notes
- **ADR number 0139, not 0136.** 0136 is a gap on `main` (no record, no
branch, no history names it); the record takes max+1 so the registry
stays monotone in time and cannot collide with whatever reserved the
gap. The dispatch said "the next free number"; this is the deviation,
declared.
- **285 lines against a suggested 250.** The six-consumer table, the
seven-row execution plan the ruling requires, and the three fail-closed
classes with their recognition rule are the overage; the References
section was folded into the header and the Alternatives and Consequences
compressed once already.
- Two back-pointers landed as one line each: ADR-0055's status line (row
(a) revisited for one leaf; §2 stands) and ADR-0056's non-goal bullet
(the compiler gains no form).
- Findings noted, not filed (⛔ no card from this seat):
`.claude/skills/pm-dispatch/references/compile-surfaces.md` cites
`driver-memory/src/memory-matcher.ts` (`checkCondition`), a file that no
longer exists on `main` (the evaluator is mingo in `memory-driver.ts`) —
a stale reference in an agent-read file, carrier: the next edit of that
table. `plugin-sharing`'s `sharing-service.ts` reads record-share grants
at `limit: 5000` with no warning (source reading, from report
`6095496633`; the record lists it as open item O4).
## 维护者速读(草稿)
**改了什么:** 新增一份 ADR(0139,状态 Proposed),把你 10 月 10 日「同意」的 A
方案写成正式决策:引擎内部加**一个**不可编写、请求里永不接受的过滤叶子「这个调用者能读的 X 对象 id」,由 ObjectQL
组合权限范围后下推成对父表的子查询;driver-sql
实现,其余驱动实现或响亮拒绝;六个树内消费者(动态、审计台账、撤回更新规则、评论、表情、附件)改用它,撤回更新规则改为写入时盖一列
`changed_fields` 并回填;委托上下文、自带插件读闸的父对象、跨数据源父对象三类保留今天的有界 fail-closed
探针。另外在 ADR-0055 与 ADR-0056 各加一行指针。不改任何代码、spec 或驱动;执行卡只列在 ADR 里,不立。
**为什么改:** 实测一位普通成员应看到 1,808 条动态,系统只给 1,105 条且「总数」也显示 1,105,管理员恰好截在
2,000;原因是六处读闸都靠一个 2,000 行的预扫描窗口,多组织部署下所有组织的行挤同一个窗口。ADR-0055
当年明确拒绝过子查询,按第 13 条基本指令,反转一个已记录的决定本身需要一份新 ADR,所以代码之前先交这份文档。
**风险与代价(含回滚):** 这是一个真实的能力扩张,收窄为一个引擎内部叶子;工程量按裁决估计约 3–5k 行、6–10 个
PR、横跨三条车道。已点名的限制:driver-mongodb 在量过之前对这六类读会响亮报错(500)而不是悄悄少给;注册了
`telemetry` 独立数据源的部署,动态表与父表不在同一驱动,这个叶子帮不上,仍靠过渡方案 C 的响亮
400;方言绑定参数上限只取了文档值,未实测。回滚:ADR 处于
Proposed,未批准即不生效;批准后任何一张执行卡都可单独不落地,文档改动本身可整体 revert。
**席位意见:**(留空)
**你要做的:** 在本 PR 上给出 APPROVED review(或在评论里指出要改的决策点),批准后由 `domain:engine`
席位按 ADR 列出的顺序立执行卡。
---
_Generated by [Claude
Code](https://claude.ai/code/session_01JfJfBUC3cQ6hhgm9MQK76T)_
---------
Co-authored-by: objectstack-fleet[bot] <objectstack-fleet[bot]@users.noreply.github.com>
Co-authored-by: Claude <noreply@anthropic.com>
1 parent 165587e commit a2e94c2
3 files changed
Lines changed: 287 additions & 1 deletion
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
1 | 1 | | |
2 | 2 | | |
3 | 3 | | |
| 4 | + | |
4 | 5 | | |
5 | 6 | | |
6 | 7 | | |
| |||
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
166 | 166 | | |
167 | 167 | | |
168 | 168 | | |
169 | | - | |
| 169 | + | |
170 | 170 | | |
171 | 171 | | |
172 | 172 | | |
| |||
0 commit comments