Skip to content

Commit 8b27638

Browse files
test(dogfood): pin five records-forms checklist items at the REST door (slice 1) (#26788)
<!-- claude-projects-attribution: {"github_login":"os-warren"} --> _Requested by **Warren** · [project thread](https://claude.ai/code/project/chan_01U5RdnAAmkSQmZbqh6FtqZ8?thread=cmsg_01U5RdnAAmkSQmZbqh6FtqZ8EZuqRp2sWT5mEbwB3fUdmh)_ Refs #26573 (slice 1 of about 5 items: `predicate-relationship-traversal`, `write-answer-advisories-rollback-and-tree-delete`, `comment-reactions-and-edit-stamps`, `encrypted-field-behavior`, `write-time-option-and-element-shape-judgement`) Clause-②: no Metadata-protocol: none ## 维护者速读 - 改了什么:records-forms 区 5 个清单项的 api 条款原先只有引擎层/进程内的钉测,现在新增 5 个 dogfood 测试文件,在真实启动的栈上经 REST 门断言这些条款,并把 5 项的 `automated.ref` 改为指向新钉测(revision 各加 1,history 引本卡)。 - 为什么改:18.2.0 Tier 1 扫描把这些条款判为 PARTIAL(钉测低于条款 oracle 的层);按维护者「同意」的分卡,每次发版都要补跑活腿,钉在 oracle 层之后就是常驻可复跑的证据。 - 风险与回滚:只加测试和清单文字,不动产品代码、不加 CI 车道;新文件跑在现有 dogfood 腿里(isolated 项目)。回滚即 revert 本 PR。 - 先看:`packages/qa/dogfood/test/encrypted-field-behavior.dogfood.test.ts`(无加密提供者的 fail-closed 腿是启动后把引擎的 provider 置空,看这个建模是否可接受);`packages/qa/dogfood/test/comment-reactions-and-edit-stamps.dogfood.test.ts`(实测码 405 / 403);`docs/qa/platform-checklist/areas/records-forms.json` 的 5 处 `automated` 与 history。 - 两分钟验证:`pnpm --filter @objectstack/dogfood exec vitest run --maxWorkers=2 test/predicate-relationship-traversal.dogfood.test.ts test/write-answer-warnings-rest.dogfood.test.ts test/comment-reactions-and-edit-stamps.dogfood.test.ts test/encrypted-field-behavior.dogfood.test.ts test/write-time-option-and-element-shape.dogfood.test.ts`(先建依赖闭包),预期 5 个文件 22 个用例全绿;`node scripts/check-platform-checklist.mjs` 预期 OK。 - 需要你做的:无,席位落地。 ## What changes Five new dogfood files, each booting a real stack through `@objectstack/verify` and driving the REST doors (`/data/:object`, `/data/:object/query`, `/data/:object/import`), so these api clauses are pinned at the layer their oracle names: | Item (rev → new) | Clauses now pinned over REST | Pin | |---|---|---| | `predicate-relationship-traversal` (4 → 5) | acceptance[0]–[3], negative[0]–[2] | `predicate-relationship-traversal.dogfood.test.ts` — the showcase invoice rule as shipped, plus the scratch pair `fixtures.knownGaps` describes for a member with no parent grant | | `write-answer-advisories-rollback-and-tree-delete` (2 → 3) | acceptance[1]; acceptance[2] and negative[0] | `write-answer-warnings-rest.dogfood.test.ts` (new) reads the PATCH body's `warnings[]`; the existing `hook-after-abort-rollback.dogfood.test.ts` already refused the aborting create over REST and read the store back, and is now named | | `comment-reactions-and-edit-stamps` (3 → 4) | acceptance[1]–[3] | `comment-reactions-and-edit-stamps.dogfood.test.ts` — org-bound comments fixture + plugin-audit, the member armed (`assertArmed`) as `org_member` | | `encrypted-field-behavior` (5 → 6) | acceptance[2]–[4], negative[1]–[3] | `encrypted-field-behavior.dogfood.test.ts` — showcase field zoo for the echoed mask and the aggregate refusal; a scratch object on an engine left with no crypto provider for fail-closed | | `write-time-option-and-element-shape-judgement` (1 → 2) | acceptance[1]–[4] | `write-time-option-and-element-shape.dogfood.test.ts` — scratch app: faulting option gate, kept-option import, matched-row dry run, multi-lookup element shape | Each item's `automated.ref` names the new pin and what it scores; the existing engine/unit pins stay named as acceptance[0]'s pins and as evidence of their own layer (RUNNER rule 6). One history row per item, citing #26573. No clause text, fixture, step or `knownGaps` line was edited (the claim's file surface). Left out of this slice: `record-discussion-mentions` a9 (needs the #26625 walled two-organization recipe) and the browser/dom-oracle items, which need Playwright or live-boot specs. ## Why The 18.2.0 release-candidate Tier 1 run scored these clauses PARTIAL: every pin was green, but each asserted the clause below its oracle's layer (in-process protocol calls, ObjectQL directly, or an ObjectKernel with no HTTP adapter). A pin at the oracle's layer turns a release-sweep live leg into a standing, re-runnable proof. The triage ruling allows ~5-item slices and no new CI lane; the new files fall under the dogfood package's existing `isolated` vitest project and run where the dogfood legs run today. ## Tests Measured answers the pins now hold (all first read live, then pinned exactly): - reaction PATCH → `405 OBJECT_API_METHOD_NOT_ALLOWED` (the object's `apiMethods` offers no update); foreign reaction DELETE → `403 PERMISSION_DENIED` with the member holding the object delete bit, so the refusal is the own-record floor; own DELETE is the 2xx control. - the warning-rule PATCH → `200` with `warnings: [{ rule: project_health_progression, severity: warning, field: health, code: invalid_transition, message }]`; one-step change and the create answer carry no `warnings` key. - faulting option → `400 VALIDATION_FAILED`, `tier` / `rule_violation`, `constraint.rule: visibleWhen`, `constraint.reason: unevaluable`. - multi-lookup elements `{id}`, `false`, `42` → `400` `refs` / `invalid_type`, `constraint.detail` starting `at index 1:`. - credential group-by / MIN / MAX over `POST /data/showcase_field_zoo/query` → `400 INVALID_FIELD`; a group-by on `name` is the `200` control. - fail-closed secret POST → `500 INTERNAL_ERROR` and no row stored; a password write on the same engine → `201`, masked on read. <details><summary>Runs</summary> - Dependency closure: `pnpm exec turbo run build --filter='@objectstack/dogfood^...' --concurrency=2` under `os-verify-lock.sh` → `Tasks: 67 successful, 67 total`, VERDICT command-exit 0. - First run (pre-merge head): `pnpm --filter @objectstack/dogfood exec vitest run --maxWorkers=2` over the five files → `Test Files 5 passed (5)`, `Tests 22 passed (22)`. - After merging `origin/main` (head `8919aecc`, which brought a driver-sql refactor): `pnpm --filter @objectstack/driver-sql build`, then the five files plus `hook-after-abort-rollback.dogfood.test.ts` → `Test Files 6 passed (6)`, `Tests 28 passed (28)`, VERDICT command-exit 0. - `pnpm --filter @objectstack/dogfood typecheck` (`tsc --noEmit`) → VERDICT command-exit 0. - Final head `5db5a474` (after merging `origin/main` with #26337): the dependency closure rebuilt (`Tasks: 67 successful, 67 total`), then the five files plus `hook-after-abort-rollback.dogfood.test.ts` → `Test Files 6 passed (6)`, `Tests 28 passed (28)`, then `pnpm --filter @objectstack/dogfood typecheck`, one locked command, VERDICT command-exit 0. </details> No ablation was run: this diff adds tests and changes no behaviour they pin (os-dev: ablation only where the diff changes a pinned behaviour). Each refusal assertion has a same-door admitted control beside it, so none passes on a door that refuses everything. ## Acceptance notes - `encrypted-field-behavior` acceptance[3]: `bootStack` always wires a crypto provider, so the fail-closed leg unsets it once after bootstrap (`engine.setCryptoProvider(undefined)`), before any request. That is the engine state of a boot that never wired one; no stock composition boots that way, so on a live stock boot the clause stays blocked(fixture). `fixtures.knownGaps[1]` still says the path has "NO live leg"; it is still true of a live boot, and its rewording is left to an item-text pass since this slice's surface is `automated.ref` / `revision` / `history` only. The refusal arrives as the sanitised `500 INTERNAL_ERROR`, because the engine throws a plain `Error`: noted, not filed, since an unwired provider is a host composition fault, not an author error. - `predicate-relationship-traversal` acceptance[3]: now scored live on the scratch substitute the item's `fixtures.knownGaps` already prescribes, rather than only from the engine pin. - `write-answer-advisories…` acceptance[2] / negative[0] were already pinned over REST by `hook-after-abort-rollback.dogfood.test.ts` (it reads the store back through the engine rather than a list count); revision 2 did not name it. ## Verification - `node scripts/check-platform-checklist.mjs` → exit 0, `OK — 16 areas, 329 items (324 active, 1 planned)`. - `node scripts/pm/dispatch-gates.mjs --commands --repo objectstack-ai/objectstack` → 55 commands on head `8919aecc`, each run and reconciled with `--ran`: `55 derived famil(ies) accounted for — 54 run, 1 NOT-MEASURED`. The NOT-MEASURED one is `pnpm check:dual-build-cjs-loads` (exit 3, PREREQUISITE NOT MET: it reads every package's `dist/` and this worktree built only the dogfood closure); CI runs it. `check-plugin-teardown-shape --self-test` first exited 3 on the shallow clone and is exit 0 after `git fetch --unshallow`. - After merging `origin/main` again (it brought #26337, which touches `coverage.json` and `packages/qa/dogfood/test`): `check-platform-checklist` exit 0 on the new head; the five files are re-run there (see Runs). - CI families are read from this PR's check-runs, not re-run locally. ## Human Minimum - [x] 1. Schemas start as Zod; `packages/spec` holds no runtime logic. (not touched) - [x] 2. Every name I added uses the casing for its kind. - [x] 3. No formatter ran; `pnpm lint` passes. (CI-owned run) - [x] 4. One issue, one branch off an up-to-date `main`, one pull request. - [x] 5. A change that publishes carries a changeset — nothing publishes here (`@objectstack/dogfood` is private; `docs/qa/**` ships in no package), so `skip-changeset`; both declaration lines are filled in above. - [x] 6. `packages/spec` not touched. - [x] 7. No hand edits to `content/docs/references/`, `packages/*/CHANGELOG.md` or `content/docs/releases/`. - [x] 8. Title, body and comments are in English (the maintainer brief above is Chinese by the repo's communication rule). --- _Generated by [Claude Code](https://claude.ai/code/session_01JQwxbZ3oTCkvGGTMNd5XYU)_ --------- Co-authored-by: Claude <noreply@anthropic.com>
1 parent f7c95cd commit 8b27638

6 files changed

Lines changed: 880 additions & 11 deletions

‎docs/qa/platform-checklist/areas/records-forms.json‎

Lines changed: 41 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -4182,7 +4182,7 @@
41824182
"title": "Secret/encrypted field: ciphertext at rest, masked on every read, no-op mask re-submit, and fail-CLOSED without an ICryptoProvider (ADR-0100)",
41834183
"since": "v16",
41844184
"status": "active",
4185-
"revision": 5,
4185+
"revision": 6,
41864186
"priority": "P1",
41874187
"surface": "mixed",
41884188
"personas": [
@@ -4254,7 +4254,7 @@
42544254
],
42554255
"automated": {
42564256
"kind": "api",
4257-
"ref": "packages/qa/dogfood/test/field-zoo-roundtrip.dogfood.test.ts (+ field-zoo.matrix.ts masked vectors for f_secret & f_password) — acceptance[0]'s masked-GET half ONLY (its API oracle; knownGaps[2] makes the raw-column read the DB-inspectable extra, and that leg, the 'secret:' ref column and the sys_secret ciphertext, is asserted only by the engine-layer secret-fields.test.ts below), acceptance[1] and negative[0], at the HTTP door: the showcase is booted in-process, POST /data/showcase_field_zoo writes both fields, and GET /data/showcase_field_zoo/ID reads them back as SECRET_MASK, never the written value. packages/objectql/src/secret-fields.test.ts drives a real ObjectQL engine over a stub driver, with no HTTP, so it scores NO api clause of this item: it is engine-layer evidence beside acceptance[0]'s DB-inspectable leg ('encrypts on write: row stores a ref, sys_secret holds ciphertext, no cleartext on the row'), and for acceptance[2] ('update: echoing the read mask back does NOT overwrite the stored secret'), acceptance[3] ('fail-closed: writing a secret field with no CryptoProvider throws'), acceptance[4] (the 'objectql aggregate() rejects credential fields (ADR-0100 / #3171)' describe) and negative[1]–[3]. acceptance[2] and [4] are driven over the API; acceptance[3] is blocked(fixture) on every stock composition (knownGaps[1])"
4257+
"ref": "packages/qa/dogfood/test/field-zoo-roundtrip.dogfood.test.ts (+ field-zoo.matrix.ts masked vectors for f_secret & f_password) — acceptance[0]'s masked-GET half ONLY, acceptance[1] and negative[0], at the HTTP door (POST then GET /data/showcase_field_zoo/ID reads both fields back as SECRET_MASK). packages/qa/dogfood/test/encrypted-field-behavior.dogfood.test.ts, at the REST data door: on the showcase, acceptance[2] and negative[2] (a PATCH echoing SECRET_MASK leaves the driver-held secret: ref byte-identical and resolveSecretField still answers the original) and acceptance[4] and negative[3] (POST /data/showcase_field_zoo/query grouping by, or taking MIN/MAX of, f_secret or f_password is 400 INVALID_FIELD; a group-by naming no credential column is the 200 control); on a scratch object whose engine is left with no crypto provider (unset once after bootstrap, before any request — the engine state of a boot that never wired one, which no stock composition is, fixtures.knownGaps[1]), acceptance[3] and negative[1] (a non-empty secret POST is refused, 500 INTERNAL_ERROR, the sanitised branch of the engine's plain fail-closed Error, and no row is stored; a password write is 201 and masked on read). packages/objectql/src/secret-fields.test.ts drives a real engine over a stub driver with no HTTP: engine-layer evidence beside acceptance[0]'s DB-inspectable leg, never an api clause's pass"
42584258
},
42594259
"source": [
42604260
"packages/objectql/src/secret-fields.ts#SECRET_REF_PREFIX (SECRET_MASK, SECRET_REF_PREFIX, collectSecretFields/collectMaskedReadFields/collectCredentialFields)",
@@ -4294,6 +4294,12 @@
42944294
"date": "2026-10-26",
42954295
"change": "automated.ref says the dogfood pin scores acceptance[0]'s masked-GET half only; the 'secret:' ref column and sys_secret ciphertext leg is asserted only in packages/objectql/src/secret-fields.test.ts, at the engine layer (Tier 1 run #25815 (6679644a, pin b41ed7f68956)). No clause changed",
42964296
"ref": "#25846"
4297+
},
4298+
{
4299+
"revision": 6,
4300+
"date": "2026-10-27",
4301+
"change": "automated.ref names the dogfood pin for acceptance[2]–[4] and negative[1]–[3] at the REST data door (#26573 slice 1). acceptance[3] is now pinned on an engine left unwired in-process; on a stock live boot it stays blocked(fixture) as fixtures.knownGaps[1] says, and that note's 'NO live leg' now reads only for a live boot. No clause changed",
4302+
"ref": "#26573"
42974303
}
42984304
]
42994305
},
@@ -5029,7 +5035,7 @@
50295035
"title": "A validation rule reads a parent field one hop through a lookup",
50305036
"since": "v17",
50315037
"status": "active",
5032-
"revision": 4,
5038+
"revision": 5,
50335039
"priority": "P1",
50345040
"surface": "api",
50355041
"personas": [
@@ -5088,8 +5094,8 @@
50885094
"seed-data-thin"
50895095
],
50905096
"automated": {
5091-
"kind": "test",
5092-
"ref": "acceptance[3] ONLY. packages/objectql/src/engine-predicate-relationship.test.ts, on a real engine: 'reads the related row under SYSTEM authority' (the related read runs with isSystem true and the acting userId carried), 'ACCEPTS and REFUSES on the real write path, per the parent field', and 'resolves the FK off the PRIOR row when the patch does not carry it'. The other clauses still need the run"
5097+
"kind": "dogfood",
5098+
"ref": "packages/qa/dogfood/test/predicate-relationship-traversal.dogfood.test.ts, at the REST data door of a booted stack: on the showcase as shipped, acceptance[0] (an invoice on an active account is 2xx and reads back, no unevaluable fault), acceptance[1] and negative[0] (an invoice on a churned account is 400 VALIDATION_FAILED with one _record rule_violation entry carrying the authored message, and nothing is stored), acceptance[2] (a PATCH carrying only the repointed account is refused and the stored account is unchanged) and negative[1] (no readable-parent case answers the unevaluable fault); on a scratch pair, acceptance[3] and negative[2] for the persona no stock showcase persona is (fixtures.knownGaps): a member with create/edit on the child and no grant on the parent, whose parent read is the control (403 PERMISSION_DENIED), writes 2xx against a non-tripping parent and 400 with the authored message against a tripping one. packages/objectql/src/engine-predicate-relationship.test.ts stays engine-layer evidence of the mechanism ('reads the related row under SYSTEM authority' and its two siblings)"
50935099
},
50945100
"source": [
50955101
"examples/app-showcase/src/data/objects/invoice.object.ts (no_invoice_for_churned_account)",
@@ -5120,6 +5126,12 @@
51205126
"date": "2026-10-26",
51215127
"change": "acceptance[3].verify says its engine-pin route is a declared layer exception, not a rule-6 pass of an HTTP oracle, and the run record names the layer (Tier 1 run #25815 (6679644a, pin b41ed7f68956)). No clause changed",
51225128
"ref": "#25846"
5129+
},
5130+
{
5131+
"revision": 5,
5132+
"date": "2026-10-27",
5133+
"change": "automated.ref names the dogfood pin that scores every clause at the api layer (#26573 slice 1): the showcase invoice rule over REST for acceptance[0]–[2] and negative[0]–[1], and the scratch substitute fixtures.knownGaps describes for acceptance[3] and negative[2], so acceptance[3] no longer rests on the engine layer alone. kind test → dogfood. No clause changed",
5134+
"ref": "#26573"
51235135
}
51245136
]
51255137
},
@@ -5355,7 +5367,7 @@
53555367
"title": "Write-time value judgement on v18: picking an option whose stored visibleWhen cannot be evaluated is refused (reason unevaluable), an import with createMissingOptions keeps an unmatched select value while a later PATCH sending it back is refused, an import dry run admits exactly what the write admits, and a malformed element in a multi-valued lookup is refused with its index",
53565368
"since": "v18",
53575369
"status": "active",
5358-
"revision": 1,
5370+
"revision": 2,
53595371
"priority": "P2",
53605372
"surface": "api",
53615373
"personas": [
@@ -5412,7 +5424,7 @@
54125424
],
54135425
"automated": {
54145426
"kind": "unit",
5415-
"ref": "packages/objectql/src/validation/rule-validator.option-visibility.test.ts + packages/objectql/src/engine-field-predicate-fault.test.ts + packages/objectql/src/import-kept-option-values.test.ts + packages/objectql/src/validation/record-validator.kept-option-values.test.ts + packages/objectql/src/validate-update-stored-row.test.ts + packages/objectql/src/validate-preview-parent.test.ts + packages/objectql/src/engine-multi-reference-element-shape.test.ts"
5427+
"ref": "packages/objectql/src/validation/rule-validator.option-visibility.test.ts + packages/objectql/src/engine-field-predicate-fault.test.ts + packages/objectql/src/import-kept-option-values.test.ts + packages/objectql/src/validation/record-validator.kept-option-values.test.ts + packages/objectql/src/validate-update-stored-row.test.ts + packages/objectql/src/validate-preview-parent.test.ts + packages/objectql/src/engine-multi-reference-element-shape.test.ts — acceptance[0]'s pins, each evidence of its own layer (RUNNER rule 6). The api clauses' pin: packages/qa/dogfood/test/write-time-option-and-element-shape.dogfood.test.ts, at the REST doors of a booted scratch stack: acceptance[1] (picking an option whose visibleWhen reads a misspelt key is 400 VALIDATION_FAILED, rule_violation, constraint rule visibleWhen reason unevaluable, nothing stored), acceptance[2] (POST /data/OBJECT/import with createMissingOptions stores the unmatched select value, its dry run reports the same verdict, the option off refuses it invalid_option, and a later PATCH sending it back is 400 invalid_option), acceptance[3] (an update-mode import matched by name: the dry run and the real import agree, admitting a note whose requiredWhen reads the omitted, stored country, and both refusing note required where the merge violates the rule) and acceptance[4] (an object, false or a number in a multiple lookup is 400 invalid_type with constraint.detail 'at index 1: …', nothing stored)"
54165428
},
54175429
"source": [
54185430
"packages/objectql/src/validation/rule-validator.ts",
@@ -5426,6 +5438,12 @@
54265438
"date": "2026-10-16",
54275439
"change": "initial — authored from the v18 line (18.0.0-next.*) delta: the capability landed on the v18 pre-release line and the ledger carried no `since: \"v18\"` item for it",
54285440
"ref": "#23879"
5441+
},
5442+
{
5443+
"revision": 2,
5444+
"date": "2026-10-27",
5445+
"change": "automated.ref names the dogfood pin that scores acceptance[1]–[4] at the REST data and import doors (#26573 slice 1); the objectql pins stay acceptance[0]'s. No clause changed",
5446+
"ref": "#26573"
54295447
}
54305448
]
54315449
},
@@ -5434,7 +5452,7 @@
54345452
"title": "Write semantics changed on v18: a warning-severity validation hit comes back in the 2xx write answer as warnings[], a blocking after* hook abort rolls the whole write back, a `tree` field (with or without an explicit reference) honours its deleteBehavior, and a cascade delete is distinguishable to the child hook",
54355453
"since": "v18",
54365454
"status": "active",
5437-
"revision": 2,
5455+
"revision": 3,
54385456
"priority": "P1",
54395457
"surface": "api",
54405458
"personas": [
@@ -5485,7 +5503,7 @@
54855503
],
54865504
"automated": {
54875505
"kind": "unit",
5488-
"ref": "packages/objectql/src/validation/advisory-write-answer.test.ts + packages/objectql/src/engine-after-hook-abort-unit.test.ts + packages/objectql/src/engine-cascade-delete.test.ts + packages/objectql/src/engine-cascade-delete-tree.test.ts + packages/verify/src/delete-behavior-referenceless-tree.test.ts — acceptance[0]'s pins. For the api clauses they are evidence of their own layers (RUNNER rule 6): advisory-write-answer.test.ts calls ObjectStackProtocolImplementation createData / updateData in-process, so the REST PATCH body acceptance[1] reads is not observed; engine-after-hook-abort-unit.test.ts drives ObjectQL directly, so no HTTP pin covers the aborting afterInsert (acceptance[2], negative[0])"
5506+
"ref": "packages/objectql/src/validation/advisory-write-answer.test.ts + packages/objectql/src/engine-after-hook-abort-unit.test.ts + packages/objectql/src/engine-cascade-delete.test.ts + packages/objectql/src/engine-cascade-delete-tree.test.ts + packages/verify/src/delete-behavior-referenceless-tree.test.ts — acceptance[0]'s pins, each evidence of its own layer for the api clauses (RUNNER rule 6). The api clauses' pins, at the REST door of a booted stack: packages/qa/dogfood/test/write-answer-warnings-rest.dogfood.test.ts for acceptance[1] (on the showcase as shipped, a health jump on showcase_project answers 200 with warnings [{rule: project_health_progression, severity: warning, field: health, code: invalid_transition, message}] and the write is stored; a one-step change and the create answer carry no warnings key); packages/qa/dogfood/test/hook-after-abort-rollback.dogfood.test.ts for acceptance[2] and negative[0] (an aborting afterInsert refuses POST /data/OBJECT, and neither the row nor the hook chain's ctx.api write is stored)"
54895507
},
54905508
"source": [
54915509
"packages/objectql/src/engine.ts",
@@ -5504,6 +5522,12 @@
55045522
"date": "2026-10-26",
55055523
"change": "automated.ref says what its pins reach at the api clauses' layer (Tier 1 run #25815 (6679644a, pin b41ed7f68956); re-read on main cc378e93): advisory-write-answer.test.ts calls the protocol in-process, so acceptance[1]'s REST PATCH body is unobserved; engine-after-hook-abort-unit.test.ts drives ObjectQL directly, so acceptance[2] and negative[0] have no HTTP pin. The run's third note, acceptance[3] scored on the referenceless rtl_* tree fixture, needs no edit: the clause names no carrier. No clause changed",
55065524
"ref": "#25846"
5525+
},
5526+
{
5527+
"revision": 3,
5528+
"date": "2026-10-27",
5529+
"change": "automated.ref names the HTTP pins for acceptance[1], acceptance[2] and negative[0] (#26573 slice 1): the new write-answer-warnings-rest dogfood file reads the REST PATCH body, and the existing hook-after-abort-rollback dogfood file already refused the aborting create over REST and read the store back, which revision 2 had not named. acceptance[3] is unchanged. No clause changed",
5530+
"ref": "#26573"
55075531
}
55085532
]
55095533
},
@@ -5512,7 +5536,7 @@
55125536
"title": "Discussion on v18: reactions are their own records (sys_comment_reaction — 201 with user_id forced, duplicate 409, unreadable comment 403, no update, no deleting another member's), the retired sys_comment.reactions column is refused, a comment edit is platform-stamped, and milestone activity is withheld whole from a reader missing a watched field",
55135537
"since": "v18",
55145538
"status": "active",
5515-
"revision": 3,
5539+
"revision": 4,
55165540
"priority": "P2",
55175541
"surface": "api",
55185542
"personas": [
@@ -5565,7 +5589,7 @@
55655589
],
55665590
"automated": {
55675591
"kind": "unit",
5568-
"ref": "packages/plugins/plugin-audit/src/objects/sys-comment-reaction.object.test.ts + packages/plugins/plugin-security/src/comment-reaction-own-record-floor.test.ts + packages/plugins/plugin-audit/src/comment-edit-stamp.integration.test.ts + packages/plugins/plugin-audit/src/sys-comment-reactions-door.integration.test.ts (a 'door' test by name only: it boots ObjectKernel + ObjectQLPlugin over sqlite-wasm and maps the thrown error with mapDataError, with no HTTP adapter, so it is engine-layer evidence beside acceptance[1] and [2] and never their pass, RUNNER rule 6) + packages/plugins/plugin-audit/src/activity-field-redaction.test.ts"
5592+
"ref": "packages/plugins/plugin-audit/src/objects/sys-comment-reaction.object.test.ts + packages/plugins/plugin-security/src/comment-reaction-own-record-floor.test.ts + packages/plugins/plugin-audit/src/comment-edit-stamp.integration.test.ts + packages/plugins/plugin-audit/src/sys-comment-reactions-door.integration.test.ts + packages/plugins/plugin-audit/src/activity-field-redaction.test.ts — acceptance[0]'s pins, each engine-layer evidence (the 'door' test boots no HTTP adapter, RUNNER rule 6). The api clauses' pin: packages/qa/dogfood/test/comment-reactions-and-edit-stamps.dogfood.test.ts, at the REST data door of an org-bound stack (the comments fixture + plugin-audit, the member armed as org_member and holding a delete-capable set on sys_comment_reaction): acceptance[1] (201 with user_id forced to the caller; duplicate 409 UNIQUE_VIOLATION; a comment the member cannot read 403 RECORD_NOT_ACCESSIBLE; PATCH 405 OBJECT_API_METHOD_NOT_ALLOWED; another member's reaction 403 PERMISSION_DENIED and the row survives, the own delete the control), acceptance[2] (reactions on PATCH and in a $filter 400 INVALID_FIELD, absent from the read) and acceptance[3] (a body edit stores is_edited true and a server edited_at; the client-sent false and 2000-01-01 are dropped)"
55695593
},
55705594
"source": [
55715595
"packages/plugins/plugin-audit/src/objects/sys-comment-reaction.object.ts",
@@ -5590,6 +5614,12 @@
55905614
"date": "2026-10-26",
55915615
"change": "automated.ref says sys-comment-reactions-door.integration.test.ts drives no HTTP door (Tier 1 run #25815 (6679644a, pin b41ed7f68956); re-read on main cc378e93): it boots ObjectKernel + ObjectQLPlugin over sqlite-wasm and maps errors with mapDataError, so it cannot score the api clauses acceptance[1] and [2]. No clause changed",
55925616
"ref": "#25846"
5617+
},
5618+
{
5619+
"revision": 4,
5620+
"date": "2026-10-27",
5621+
"change": "automated.ref names the dogfood pin that scores acceptance[1]–[3] at the REST data door (#26573 slice 1), with the measured refusals: an update is refused 405 OBJECT_API_METHOD_NOT_ALLOWED (the object offers no update) and a foreign delete 403 PERMISSION_DENIED (the own-record floor). No clause changed",
5622+
"ref": "#26573"
55935623
}
55945624
]
55955625
},

0 commit comments

Comments
 (0)