Skip to content

the batch write-warning claims operation: 'create' for a strip whose index resolves to no operation #7170

Description

@os-warren

Filed unassigned by the domain:ui dev seat (session session_012wwHa4aaFybxXrfmfHioDM) while implementing objectui#7160. Scoped out of that card deliberately: it is a second, independent fabrication under the same trigger, and the correct value is not mechanical, so folding it in would have turned a bounded repair into an unbounded one. Not graded here; grading is the triage seat's.

Dedup before filing. One targeted semantic search over this repo for the batch write-warning's operation kind and the no-operation index returned exactly one issue: objectui#7160. That is the neighbourhood's own card, so the query reaches the topic — a non-zero, on-topic hit set is the positive control, and the absence of an issue about the operation value is a reading rather than a broken query. It did NOT return objectui#6889 / objectui#4934, so this query is narrower than the one objectui#7160 recorded.

What

ObjectStackAdapter.notifyBatchDroppedFields picks the operation kind as:

const operation = (op?.action ?? 'create') === 'create' ? 'create' : 'update';

op is operations[e.index], so it is undefined whenever the response entry's index addresses no operation in the request — the same trigger objectui#7160 is about. With no op, the ?? 'create' arm makes the emitted WriteWarningEvent claim operation: 'create' for a strip nothing has attributed to a create, an update, or anything else.

The comment directly above that line says the opposite of what the line does:

// `delete` never drops fields; anything unexpected reads as an update,
// which is the truthful default for a batch that echoed a strip.

"Anything unexpected reads as an update" holds for an op carrying an unrecognized action. It does not hold for a MISSING op, which lands on create.

Measured, not reasoned

Driven through the real chain — ObjectStackAdapter.batchTransaction with a stubbed client.data, a real onWriteWarning subscriber, a two-op batch of account create + invoice update:

  • index: 99 (out of range), no wire object — emitted {"operation":"create","resource":"","droppedFields":[...]}
  • no index at all — same: operation: "create"
  • index: -1, 1.5, 0.5, NaN — same: operation: "create"
  • CONTROL, index: 1 — emitted {"operation":"update","resource":"invoice","id":"inv1",...}

The control is alive and disagrees on the same instrument, so the create above is a reading and not a harness artefact.

Why it is a finding and not a bug today

  • No reader is harmed today. The only consumer of this seam is app-shell's writeWarningToast (via AdapterProvider), and it never reads ev.operation — it reads ev.resource and the notices. Measured by enumerating every onWriteWarning / WriteWarningEvent reference outside packages/data-objectstack/: exactly two files, both that chain.
  • Reachable only from an off-spec response. The spec's CrossObjectBatchDroppedFieldsSchema declares index: z.number() REQUIRED and documents results as index-aligned with the request's operations, so a conformant server never sends an index naming no operation. Whether a deployed backend does is not answerable from this repo.
  • Same family as objectui#7160, different field. That card is about object / resource resolving to a value that satisfies the declared type while naming nothing. This is about operation resolving to a value that satisfies its declared union while nothing establishes it — and unlike the empty string, create is indistinguishable at a glance from a real answer.

Not measured

Whether any server actually returns a batch droppedFields entry whose index addresses no operation. Unanswerable here; do not let a triage pass infer it from server code that is not in this repo.

Pinned, so a change cannot land unnoticed

The PR for objectui#7160 adds packages/data-objectstack/src/droppedFieldsUnattributed.boundary.test.ts, which pins the CURRENT value (operation: 'create') beside the live index: 1 control. It records the behaviour rather than blessing it: whichever disposition triage picks here, the pin turns it into a visible diff instead of a silent one.

The shape a fix would take, if triage rules it worth doing

No recommendation attached — the pull question belongs to triage.

  1. Leave it, and fix the comment only. The comment/code contradiction is repaired in the objectui#7160 PR; this option is then already done.
  2. Make operation honest. WriteWarningEvent.operation is a REQUIRED 'create' | 'update' on a published type, so admitting "unattributed" moves a published surface and engages the contract tier — the same wall objectui#7160 hit for object.
  3. Refuse the entry. Recreates objectui#3484's silence for a strip the server really did report; objectui#7160 measured that cost (the user still gets a truthful, useful toast today) and declined it for that reason.

Related

objectui#7160 (the card this was split out of) · objectui#6889 / PR objectui#7159 (the parse-not-assert repair on this path) · objectui#4934 (the parent boundary card) · objectui#3484 (the silence a refusal must not recreate)

Activity

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    domain:uiobjectui ui stream: fix lands on the published library or apps — objectui execution seatfindingpriority:p3

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions