Skip to content

WriteObservabilityOptions.strictReadonlyWrites still documents "INSERT ignores it", but #5503 wired insert to refuse #7064

Description

@os-zhuang

Found while implementing #6437 (widening DroppedFieldsEvent.reason), in the TSDoc block that change edits for a different reason. Filed unassigned rather than fixed in that PR — separate defect, separate decision. Recording only, not claiming.

Fact

packages/spec/src/contracts/data-engine.ts, closing line of strictReadonlyWrites:

INSERT ignores it, for the same reason onFieldsDropped never fires there:
insert is exempt from both strips, so there is nothing to refuse.

packages/objectql/src/engine.ts disagrees, and has since #5503:

if (autonumberDropped.length > 0) {
  const drop: DroppedFieldsEvent = { object, fields: autonumberDropped, reason: 'readonly' };
  if (options?.strictReadonlyWrites === true) {
    throw new ReadonlyFieldRejectedError(object, autonumberDropped, [drop], 'insert');
  }
  
}

ReadonlyFieldRejectedError's own doc records the same thing from the other side — "Thrown by engine.update — and, since #5503, by engine.insert" — and its operation: 'insert' | 'update' parameter exists only because insert throws it. Both statements cannot be true; the contract's is the stale one.

Why it matters more than a typo

Both halves of the sentence are load-bearing and both are now false:

  1. "INSERT ignores it" — a caller reading the contract concludes it is safe to pass strictReadonlyWrites: true on a create. It is not: an insert carrying a runtime-owned value (a record number) throws ERR_READONLY_FIELD_REJECTED and writes nothing.
  2. "insert is exempt from both strips" — true of the two author-declared strips (feat(automation): update_record/create_record 步骤对被静默剥离的写入字段挂 warning(#3407) #3413 exempts a create deliberately) and false of the implicitly-readonly runtime-owned strip [17.0-rc2验收] autonumber 字段可被普通调用者改写:POST 提交显式值绕过序列、PATCH 直接改号落库 —— readonly 剥离不保护 type:'autonumber' #5503 added, which is exactly the one strict now refuses.

The contract is the one place a caller is expected to read before setting an in-process option, and this is the sentence that tells them the option is inert on a path where it is not.

Landing site

packages/spec/src/contracts/data-engine.ts — the final paragraph of the strictReadonlyWrites TSDoc. Prose only; the .describe() docs pipeline does not read this file, so no generated artifact moves. Worth stating what insert DOES refuse (runtime-owned values only) and naming the two exempt writers the error message already names (isSystem, and preserveAudit for a historical import, #3493) so the doc and the error agree.

Related: #5503, #5126, #3413, #3493, #6437.


Generated by Claude Code

Metadata

Metadata

Assignees

Type

No type

Projects

No projects

Milestone

No milestone

Relationships

None yet

Development

No branches or pull requests

Issue actions