plan/results: record the two post-gate CRUD corrections

The gate results file said the replacement-style-update gap was found and not
fixed, and PLAN said it was left alone. Both were true when written and are not
now, so a reader would take the M0 scorecard for the current one. The M0 figures
stay as measured -- they are the gate result -- with a pointer to
tests/spec/scorecard.txt, which always holds the current number.
This commit is contained in:
2026-08-04 00:03:02 +03:00
parent 21489723a9
commit d597597a4c
2 changed files with 39 additions and 11 deletions

35
PLAN.md
View File

@@ -600,11 +600,36 @@ The lesson worth carrying into M1 is the shape of 1-4: every one is a
reach a steady state. The unit suite proved each mechanism works once. Only
the churn gate showed that none of them worked twice.
A compatibility gap also surfaced, out of M0's scope and left alone:
`update.apply` (`src/update.zig:18`) rejects any update document whose first
key is not `$`, so `replaceOne`, `findOneAndReplace` and `bulkWrite`'s
`replaceOne` all fail with "bad update". It is a CRUD feature rather than
storage, and it is already inside what the 161 spec failures cover.
A compatibility gap also surfaced, out of M0's scope: `update.apply` rejected
any update document whose first key was not `$`, so `replaceOne`,
`findOneAndReplace` and `bulkWrite`'s `replaceOne` all failed with "bad
update". Fixed straight after the gate rather than deferred to a milestone,
since it was small and the scorecard put a number on it (below).
### After the gate, before M1: two CRUD corrections
Not milestone work — both came out of the gate run and were cheap enough that
deferring them would have cost more in explanation than in code.
**Replacement-style writes.** MongoDB decides operator-vs-replacement on the
update document's first field; a replacement replaces every field except `_id`,
which is immutable. Also refuses two options rather than ignoring them: `multi`
with a replacement, and `sort` on an update spec (a MongoDB 8.0 addition —
ignoring it would silently write a *different* document than the client asked
for). Took the scorecard from 131 pass / 161 fail to 161 / 131, sixteen files
improved, none regressed.
**`nModified` counted every write.** MongoDB counts a document as modified only
if the update altered it, and writes nothing when it did not. Decided in the
engine, where the document is already serialized and the check lands before the
log append, so a no-op now costs no log record, no fsync and no garbage. That in
turn exposed `_id` not being stored first: MongoDB moves it to the front
whatever order it arrives in, and the Node driver appends a generated `_id`, so
replacing a document with itself was a byte-level change. 163 pass / 129 fail.
The pattern worth noting for M1: both were found by *running* the suites, not by
reading them, and the second was only visible because the first stopped masking
it.
---

View File

@@ -148,9 +148,12 @@
# argument for keeping both the churn gate and the concurrent benchmark in the
# gate list rather than treating them as optional.
#
# ONE COMPATIBILITY GAP FOUND, NOT FIXED (out of M0's scope, storage-only)
# `update.apply` (src/update.zig:18) rejects any update document whose first
# key is not `$`, so replacement-style writes -- replaceOne, findOneAndReplace,
# bulkWrite's replaceOne -- fail with "bad update". Found while building the
# churn workload. It is a CRUD feature, not storage, and it is already part of
# what the 161 spec failures cover.
# ONE COMPATIBILITY GAP FOUND HERE, FIXED AFTER THE GATE
# `update.apply` rejected any update document whose first key was not `$`, so
# replacement-style writes -- replaceOne, findOneAndReplace, bulkWrite's
# replaceOne -- failed with "bad update". Found while building the churn
# workload, out of scope for a storage milestone, and fixed immediately after in
# `update: replacement-style writes` and `db: a write that changes nothing is
# not a write`. The scorecard above is the M0 figure and is left as measured;
# those two commits took it to 163 pass / 129 fail, and `tests/spec/scorecard.txt`
# always holds the current one.