Skip to content

readonlyStripWarning promises a commit that strict mode refuses, and omits the preserveAudit remedy its own strip implements #8214

Description

@os-zhuang

Found while implementing #8141 (the addressing-id half of this message's falsity) and deliberately left out of it: #8141's acceptance bar pins the line as unchanged in wording for the cases that still print, so both defects below are separate decisions about the same string and would collide with that pin. Filed here so the wording is decided once, on evidence, rather than drifting.

readonlyStripWarningpackages/objectql/src/validation/rule-validator.ts.

1. "COMMITTED WITHOUT IT" is false under strictReadonlyWrites

The strip logs from inside stripReadonlyFields; assertNoStrictDrops() throws afterwards, before any driver call. So a strict caller is told in prose that the update was committed without the field, while the update was refused outright and nothing was written.

Measured on a real ObjectQL + a recording driver, by-id update of an object with a readonlylocked_note, { strictReadonlyWrites: true }:

refusedCode : ERR_READONLY_FIELD_REJECTED
driverWrites : 0
warnLines : 1
claimsCommitted: true ← the line says "COMMITTED WITHOUT IT"

This is the #4632 shape inverted: the log promises a silent partial success where the engine actually delivered a loud refusal. A reader debugging from the log alone looks for a row that was never touched.

Same class on the insert side, unverified here: runtimeOwnedStripWarning says "the write is being COMMITTED WITHOUT IT" and insert discharges strictReadonlyWrites too (#5126 / #5503).

2. The message names isSystem as the remedy but never preserveAudit

stripReadonlyFields honours context.preserveAudit (#3493) — a whitelist, narrower than isSystem by construction, and the documented route for a historical import reinstating the original timeline. The message names only { context: { isSystem: true } }, the blanket exemption from the whole strip.

Its own runtime-owned twin names both, and there is a test pinning that it does (rule-validator.test.ts, "logs the runtime-owned message…": asserts the line contains isSystemandpreserveAudit). So the two sibling messages disagree about the remedy set for the same pair of exemptions.

Consequence: an import that forgot the flag reads this line and is steered to the strictly-worse posture — exactly the failure #8141 fixed for the addressing id, one remedy over. Whoever fixes it should decide whether the audit-family case deserves a targeted remedy sentence rather than a blanket one.

Scope note

Both are one edit to one string plus its pin tests, which is why this is one card and not two. ⛔ It must not silently re-open #8141: the address key no longer reaches this message at all, and the case that still prints must keep naming the field, the consequence and a remedy that is true for it — the line stays at warn so real forgery attempts stay visible (its docblock argues the level).

Filed unassigned by the #8141 dev lane (seat #6019, domain:engine-core).

Metadata

Metadata

Assignees

Type

Projects

No projects

Milestone

No milestone

Relationships

None yet

Development

No branches or pull requests

Issue actions