Repository navigation
Commit e1fede6
docs(adr-0120): record the caller-supplied id collision on the create doors as an accepted residual, document it, pin it (#28694)
domain:devx#1 · sunhaolin · session_017xRBcVy4VXmkQjn2MGeaWk
## 维护者速读
- 改了什么:ADR-0120 加一条带日期的修订注记(S10 之下),记录「记录 `id` 是全部署唯一键,新建撞上已有 `id`
一律同一种拒绝」这一已接受的残余;HTTP 协议文档 Create Record 一节补上调用方自带 `id` 与撞键的回答;新增两个
dogfood 用例钉住今天的行为。零产品代码改动。
- 为什么改:QA 第三轮发现的安全加固(细节按披露裁决不公开)
- 风险与回滚:只动文档、ADR 与测试,不改运行时;回滚即 revert 本
PR。风险在于今后有人改动新建门的拒绝形状时,新用例会转红,这是有意的。
-
先看:`docs/adr/0120-unique-scope-vocabulary-and-null-safe-tenant-uniqueness.md`(修订注记全文,对照你
2026-11-01 的「同意」)→
`content/docs/protocol/kernel/http-protocol.mdx`(Create Record 新增段)→
`packages/qa/dogfood/test/create-caller-id-collision-walled.dogfood.test.ts`(跨组织一侧的断言)。
- 两分钟验证:`pnpm --filter @objectstack/dogfood exec vitest run
test/create-caller-id-collision.dogfood.test.ts
test/create-caller-id-collision-walled.dogfood.test.ts`,预期 2 个文件、30
个用例全绿。
- 需要你做的:本 PR 触及 `docs/adr/**`(Tier H),请审阅修订注记措辞后亲手合并或入队。
## What changes
A docs + pin PR that records a maintainer ruling. **No product code
changes.**
- **ADR-0120** gains a dated amendment note under its
business-requirement matrix (and a pointer on the status line and in the
S10 row), in the record's own amendment form. It states that S10's
no-oracle promise covers `'organization'` uniqueness only; that the
record `id` is the one deployment-global key; and that a create
colliding on it answers one refusal, identical for every visibility of
the row that holds the id, naming no organization or owner. It records
the ruling chain (ruling record `5934879010` rule 2, extended by the
maintainer on 2026-11-01 from upsert to every create door and to the
same-organization half) and the options considered and rejected.
- **`content/docs/protocol/kernel/http-protocol.mdx`**, Create Record: a
caller may supply `id`; the duplicate-`id` answer (`409
UNIQUE_VIOLATION`, nothing written, no owner or organization detail, the
same for every visibility, on every create door).
- **Two dogfood pins** at the HTTP door, on `POST /data/:object`,
per-object batch create and the cross-object `POST /batch`:
- `create-caller-id-collision.dogfood.test.ts` — same organization,
stock showcase (added to the `shared-showcase` project: no boot options,
no metadata or organization writes, assertions scoped to its own rows).
- `create-caller-id-collision-walled.dogfood.test.ts` — another
organization, walled two-organization boot under `isolated` and `group`;
the wall itself is measured in a precondition case rather than assumed.
- Each door: a fresh caller-supplied `id` lands as given (control); the
hidden-row and visible-row collisions return the same status and the
same body, whole; both rows are byte-identical after; the body names no
organization, owner, or user.
The pins confirm today's behaviour matches the ruling: no answer
differed by visibility and none named an organization, so there was
nothing to stop and report.
## Tests
- `pnpm --filter @objectstack/dogfood exec vitest run --maxWorkers=1` on
the two new files, under the verify lock: `Test Files 2 passed (2)`,
`Tests 30 passed (30)`, lock `VERDICT command-exit 0`.
- NOT MEASURED locally: the whole `shared-showcase` project, which the
same-organization file joins. Reason: five verify-lock attempts answered
`99` (queue-timeout) over about 45 minutes behind a sibling dispatch's
full-suite hold; CI's dogfood gate runs the project. The new file itself
ran inside that project (on its own boot of the shared stack) and passed
in the run above.
- Type check, narrowed and declared: `tsc -p` over a temporary config
that extends the package's `tsconfig.json` with `files` set to the two
new tests (deleted afterwards, tree clean): exit 0, both files in the
program (`--listFiles`, 1113 files), zero errors. The package-wide `pnpm
--filter @objectstack/dogfood typecheck` is NOT MEASURED locally, for
the same lock-queue reason; CI's `TypeScript Type Check` runs it.
- No ablation: the diff changes no tested behaviour; it pins today's.
The precondition cases are what keep the identity assertions non-vacuous
(the hidden row is measured hidden; the fresh-id control is measured
honoured).
## Acceptance notes
- The walled half uses `bootStack(..., { multiTenant: 'posture-only'
})`. That option's docblock in `packages/verify/src/harness.ts` says the
stand-in "stamps no `organization_id` and scopes no query"; on this boot
the row is stamped with the creating organization and another
organization's member reads `404` for it, both asserted in the
precondition case. Noted, not filed: a docblock reading, outside this
PR's scope.
- Doors outside the three pinned here (`createMany`, import insert and
upsert, clone `overrides.id`) are named in the ADR note and the doc;
they are not pinned by this PR.
- No changeset: the diff touches `docs/adr/**`, `content/docs/**` and
the private `@objectstack/dogfood` package, none of which ships in any
published package's `files[]`. Declared with `skip-changeset` at
creation.
## Verification
Head `393827105`. It adds two commits on top of `c2c7d9183`, both from
the contract-tier review, both prose-only in the same two files:
- `41e8ea516`: the ADR-0120 note's second fact and the Create Record
paragraph in `http-protocol.mdx` now name the store precondition. The
409 holds on a store that enforces the primary key (the SQL-family
drivers); `driver-memory` declares none.
- `393827105`: the same two hunks call `driver-memory` the development
and test driver, not a production store. This replaces a descriptor that
its README does not support.
Re-run at `393827105`, exit codes captured before any pipe:
- `node scripts/check-adr-links.mjs` :: exit 0 (965 relative link
destinations resolve)
- `pnpm check:adr-index` :: exit 0 (`docs/adr/README.md is current`)
- `pnpm check:doc-anchors` :: exit 0 (855 internal fragment links
resolve)
- `pnpm check:doc-authoring` :: exit 0
The dogfood run, the narrowed type check and the derived gate families
below were taken at `c2c7d9183`. The two commits after it touch neither
test file and no gate-read path outside those two prose files. Not run
locally by design: `test:repo` (this diff edits none of its tests, and
the derivation names none of it) and the repo-wide scans CI owns.
<details>
<summary>Derived gate families (dispatch-gates --commands, reconciled
with --ran)</summary>
Each line is `command :: exit code`, captured before any pipe.
- `node scripts/check-adr-links.mjs` :: exit 0
- `node scripts/check-adr-links.mjs --self-test` :: exit 0
- `node scripts/check-closing-keyword-parity.mjs` :: exit 0
- `node scripts/check-closing-keyword-parity.mjs --self-test` :: exit 0
- `node scripts/check-comment-mask-adoption.mjs` :: exit 0
- `node scripts/check-comment-mask-adoption.mjs --self-test` :: exit 0
- `node scripts/check-comment-mask-corpus.mjs` :: exit 0
- `node scripts/check-doc-route-spelling.mjs --advisory` :: exit 0
- `node scripts/check-doc-route-spelling.mjs --self-test` :: exit 0
- `node scripts/check-docs-section-name.mjs` :: exit 0
- `node scripts/check-docs-section-name.mjs --self-test` :: exit 0
- `node scripts/check-dts-emitted.mjs --self-test` :: exit 0
- `node scripts/check-issue-citations.mjs` :: exit 0
- `node scripts/check-plugin-teardown-shape.mjs` :: exit 0
- `node scripts/check-plugin-teardown-shape.mjs --self-test` :: exit 0
- `node scripts/check-section-landing-index.mjs` :: exit 0
- `node scripts/check-section-landing-index.mjs --self-test` :: exit 0
- `node scripts/ci/assert-turbo-remote-cache.mjs --self-test` :: exit 0
- `node scripts/ci/plan-test-core-shards.mjs --self-test` :: exit 0
- `node scripts/ci/prune-turbo-cache.mjs --self-test` :: exit 0
- `node scripts/docs-audit/check-affected-docs.mjs` :: exit 0
- `node scripts/docs-audit/check-drift-comment.mjs` :: exit 0
- `pnpm --filter @objectstack/lint run check:doc-formula-expressions` ::
exit 0
- `pnpm --filter @objectstack/lint run check:doc-security-posture` ::
exit 0
- `pnpm --filter @objectstack/spec run check:docs` :: exit 0
- `pnpm --filter @objectstack/spec run check:empty-state` :: exit 0
- `pnpm --filter @objectstack/spec run check:liveness` :: exit 0
- `pnpm --filter @objectstack/spec run check:skill-examples` :: exit 3 —
NOT MEASURED, PREREQUISITE NOT MET: reads `packages/client-react/dist`
declarations, outside the dogfood build closure built here; CI runs it
in Lint & Repo Gates / TypeScript Type Check
- `pnpm --filter @objectstack/spec run check:strictness-ledger` :: exit
0
- `pnpm --filter @objectstack/spec run check:variant-docs` :: exit 0
- `pnpm --filter @objectstack/spec run check:yaml-examples` :: exit 0
- `pnpm check:adr-anchors` :: exit 0
- `pnpm check:adr-index` :: exit 0
- `pnpm check:adr-symbol-anchors` :: exit 0
- `pnpm check:ci-filter-parity` :: exit 0
- `pnpm check:closed-ceremony-text` :: exit 0
- `pnpm check:corpus-claim-drift` :: exit 0
- `pnpm check:cross-package-test-inputs` :: exit 0
- `pnpm check:doc-anchors` :: exit 0
- `pnpm check:doc-authoring` :: exit 0
- `pnpm check:doc-frontmatter` :: exit 0
- `pnpm check:docs-audit-scope` :: exit 0
- `pnpm check:docs-redirects` :: exit 0
- `pnpm check:docs-single-h1` :: exit 0
- `pnpm check:docs-spec-enumerations` :: exit 0
- `pnpm check:docs-transcript-drift` :: exit 0
- `pnpm check:driver-memory-census` :: exit 0
- `pnpm check:dts-closure` :: exit 0
- `pnpm check:dual-build-cjs-loads` :: exit 3 — NOT MEASURED,
PREREQUISITE NOT MET: reads the dist of seven packages outside the
dogfood build closure built here; CI runs it in Lint & Repo Gates /
TypeScript Type Check
- `pnpm check:engine-double-contract` :: exit 0
- `pnpm check:gitlink-declared` :: exit 0
- `pnpm check:issue-citations` :: exit 0
- `pnpm check:keyed-text-bounds` :: exit 0
- `pnpm check:lean-entry-closure` :: exit 0
- `pnpm check:logger-receiver-detach` :: exit 0
- `pnpm check:nul-bytes` :: exit 0
- `pnpm check:objectql-double-limit` :: exit 0
- `pnpm check:org-identifier` :: exit 0
- `pnpm check:page-declaration-shape` :: exit 0
- `pnpm check:platform-object-tenancy-census` :: exit 0
- `pnpm check:pm-governed-merges` :: exit 0
- `pnpm check:pm-prior-rulings` :: exit 0
- `pnpm check:published-files` :: exit 0
- `pnpm check:published-readme-links` :: exit 0
- `pnpm check:query-options-erasure` :: exit 0
- `pnpm check:react-page-adapter-contract` :: exit 0
- `pnpm check:refd-timer-probe` :: exit 0
- `pnpm check:registry-log-declared` :: exit 0
- `pnpm check:rest-log-spy-declared` :: exit 0
- `pnpm check:role-word` :: exit 0
- `pnpm check:skill-identifier-liveness` :: exit 0
- `pnpm check:slot-lookup` :: exit 0
- `pnpm check:sourcemap-no-sources-content` :: exit 0
- `pnpm check:system-context-census` :: exit 0
- `pnpm check:test-source-alias` :: exit 0
- `pnpm check:tier-file-adoption` :: exit 0
- `pnpm check:type-check-coverage` :: exit 0
- `pnpm check:type-check-debt` :: exit 3 — NOT MEASURED, PREREQUISITE
NOT MET: its own closure build never got a turn on the shared verify
lock; CI runs it in Lint & Repo Gates / TypeScript Type Check
- `pnpm check:undeclared-dep-imports` :: exit 0
- `pnpm check:vendor-version-stamps` :: exit 0
- `pnpm check:watch-hint-literal` :: exit 0
- `pnpm check:where-matcher` :: exit 0
`node scripts/pm/dispatch-gates.mjs --repo objectstack-ai/objectstack
--ran FILE` (FILE = the lines above):
```text
✓ dispatch-gates --ran: 82 derived famil(ies) accounted for — 79 run, 3 NOT-MEASURED (3 DERIVED from a recorded exit 3).
```
</details>
---
_Generated by [Claude
Code](https://claude.ai/code/session_017xRBcVy4VXmkQjn2MGeaWk)_
---------
Co-authored-by: Claude <noreply@anthropic.com>1 parent 4bf48a8 commit e1fede6
5 files changed
Lines changed: 494 additions & 2 deletions
File tree
- content/docs/protocol/kernel
- docs/adr
- packages/qa/dogfood
- test
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
658 | 658 | | |
659 | 659 | | |
660 | 660 | | |
| 661 | + | |
| 662 | + | |
| 663 | + | |
| 664 | + | |
| 665 | + | |
| 666 | + | |
| 667 | + | |
| 668 | + | |
| 669 | + | |
| 670 | + | |
| 671 | + | |
| 672 | + | |
| 673 | + | |
| 674 | + | |
| 675 | + | |
| 676 | + | |
| 677 | + | |
| 678 | + | |
| 679 | + | |
| 680 | + | |
| 681 | + | |
| 682 | + | |
| 683 | + | |
| 684 | + | |
| 685 | + | |
| 686 | + | |
| 687 | + | |
| 688 | + | |
| 689 | + | |
| 690 | + | |
| 691 | + | |
| 692 | + | |
661 | 693 | | |
662 | 694 | | |
663 | 695 | | |
| |||
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
1 | 1 | | |
2 | 2 | | |
3 | | - | |
| 3 | + | |
4 | 4 | | |
5 | 5 | | |
6 | 6 | | |
| |||
156 | 156 | | |
157 | 157 | | |
158 | 158 | | |
159 | | - | |
| 159 | + | |
160 | 160 | | |
161 | 161 | | |
162 | 162 | | |
| |||
170 | 170 | | |
171 | 171 | | |
172 | 172 | | |
| 173 | + | |
| 174 | + | |
| 175 | + | |
| 176 | + | |
| 177 | + | |
| 178 | + | |
| 179 | + | |
| 180 | + | |
| 181 | + | |
| 182 | + | |
| 183 | + | |
| 184 | + | |
| 185 | + | |
| 186 | + | |
| 187 | + | |
| 188 | + | |
| 189 | + | |
| 190 | + | |
| 191 | + | |
| 192 | + | |
| 193 | + | |
| 194 | + | |
| 195 | + | |
| 196 | + | |
| 197 | + | |
| 198 | + | |
| 199 | + | |
| 200 | + | |
| 201 | + | |
| 202 | + | |
| 203 | + | |
| 204 | + | |
| 205 | + | |
| 206 | + | |
| 207 | + | |
| 208 | + | |
| 209 | + | |
| 210 | + | |
| 211 | + | |
| 212 | + | |
| 213 | + | |
| 214 | + | |
| 215 | + | |
| 216 | + | |
| 217 | + | |
| 218 | + | |
| 219 | + | |
| 220 | + | |
| 221 | + | |
| 222 | + | |
| 223 | + | |
| 224 | + | |
| 225 | + | |
| 226 | + | |
| 227 | + | |
| 228 | + | |
| 229 | + | |
| 230 | + | |
| 231 | + | |
| 232 | + | |
| 233 | + | |
| 234 | + | |
| 235 | + | |
| 236 | + | |
| 237 | + | |
| 238 | + | |
| 239 | + | |
| 240 | + | |
| 241 | + | |
| 242 | + | |
| 243 | + | |
| 244 | + | |
| 245 | + | |
| 246 | + | |
| 247 | + | |
173 | 248 | | |
174 | 249 | | |
175 | 250 | | |
| |||
0 commit comments