plan: what the event assertions found

Four defects and one deliberate non-fix, recorded where the milestone can see
them rather than only in five commit messages. The one worth carrying forward:
the runner dropped `collectionOptions`, so every "unacknowledged write" case in
the corpus had been running an acknowledged write and passing, because the two
produce results the expectation accepts either way. Only the wire distinguished
them and nothing read the wire.

Also states plainly that scorecards from before this are not comparable with
ones after, and why `bypassDocumentValidation` is left failing: both available
refusals are worse than four undeserved entries in the fail column, and one of
them is the shape that turns a scorecard into flattery.
This commit is contained in:
A.Shakhmatov
2026-08-09 13:28:39 +03:00
parent 6d5c860e11
commit e65da2740e

73
PLAN.md
View File

@@ -721,6 +721,70 @@ between `compact` and `checkpoint`; it is out of this scope because it wants
its own design pass, and because the free list must not add a second instance its own design pass, and because the free list must not add a second instance
of the same shape. of the same shape.
### The spec runner starts reading `expectEvents`
354 of the 487 cases declare `expectEvents` and the runner read none of them,
so a case could send the wrong command entirely and still be counted a pass as
long as the result came back right. The old `pass` column was an upper bound by
construction and said so; it is now an assertion that the engine answered
correctly **and** was asked the right question. **Scorecards recorded before
this are not comparable with ones recorded after.**
The totals moved 168/124/195 → 194/97/196 across the commits, but the path
matters more than the endpoints: turning the assertion on cost 34 passes, and
every one of them was a defect the result column could not see.
What it found, in the order it found them:
1. **The runner dropped `collectionOptions`.** Every collection entity was
built as `db.collection(name)`, so the 15 entities declaring
`writeConcern: {w: 0}` never got it — **every "unacknowledged write" case in
the corpus was running an acknowledged write.** They passed because the two
produce results a `$$unsetOrMatches` expectation accepts either way. Only
the command on the wire distinguished them, and nothing read the command.
2. **The wire version disagreed with the version string.** `buildInfo` said
4.4.0, the handshake said maxWireVersion 8, which is 4.2. A driver believes
the wire version: it refused *client-side* to send `hint` on an
unacknowledged delete or findAndModify, and withheld `comment` from
getMore, listCollections and listDatabases. 16 cases. The 8 was not
arbitrary — it was tied to keeping drivers off the streaming hello protocol
— but that turned out to rest entirely on omitting `topologyVersion`, which
is checked in the driver and is the whole mechanism. A test now asserts the
two numbers agree, since drifting apart silently was the actual defect.
3. **`$$unsetOrMatches` was changing root-ness.** The operator wraps a value,
it does not reposition it; the runner matched what stood behind it as a
nested document. 25 cases, all of them results the engine had right.
4. **An event's command is not the shape the driver sends.** A sort is held as
a JS `Map`, so `Object.keys` on it is empty and every expected key read as
missing. It hides well: EJSON prints a Map exactly like a document, so the
event dump reads as evidence the matcher is wrong about something else.
Two assertions are declined, both enumerated in the runner and in
`scorecard.txt`, and neither can hide anything the engine did:
- **`maxTimeMS`** — the harness's own doing. Every client carries CSOT
`timeoutMS`, which overwrites `maxTimeMS` with the remaining budget, so the
value on the wire is ours. Refused unconditionally rather than only when it
would fail, so it cannot become a pass by coincidence. One case, and dropping
`timeoutMS` instead would cost far more — it is what replaced the outer race
that once produced ~190 phantom timeout FAILs.
- **`cmap`/`sdam` event types, `ignoreExtraEvents`, `hasServiceId`,
`hasServerConnectionId`** — none occurs in this corpus; reported unsupported
where asserted rather than waived.
**Left failing on purpose: `bypassDocumentValidation: false`, 4 cases.**
mongodb@7.5.0 strips the field unless it is exactly `true` on the bulk and
findAndModify paths (`lib/bulk/common.js:292`,
`lib/operations/find_and_modify.js:19`) while sending it correctly for single
-document operations, so 4 sibling cases pass and 4 fail on a difference that
is entirely the driver's. The field is built client-side and never reaches the
engine. A refusal was written and thrown away: made unconditional it also
skipped the 4 that legitimately pass, and made conditional it would be a
skip-when-it-would-fail rule, which is the shape that turns a scorecard into
flattery. Four undeserved entries in the fail column is the cheaper error, and
this note is the correction. Revisit when the driver is bumped — which already
has to be its own commit with its own re-recorded scorecard.
--- ---
## 6. Deferred designs (grill each at its milestone) ## 6. Deferred designs (grill each at its milestone)
@@ -759,10 +823,11 @@ of the same shape.
the anchor rewritten — resuming at it returned updated documents twice, the anchor rewritten — resuming at it returned updated documents twice,
caught by draining a collection being updated underneath. caught by draining a collection being updated underneath.
Still open in M1: the doc-level free list, sessions plumbing (`lsid` Still open in M1: the doc-level free list and sessions plumbing (`lsid`
accepted), and command-monitoring (`expectEvents`) in the spec runner. The accepted). The eight reclamation bugs above were cleared first, as
eight reclamation bugs above were cleared first, as preconditions for the preconditions for the free list rather than as work of their own;
free list rather than as work of their own. command-monitoring (`expectEvents`) landed next, so that the free list and
sessions are measured by an instrument that is no longer known to overstate.
**A prerequisite the free list must honour**, recorded here while it is **A prerequisite the free list must honour**, recorded here while it is
still being designed: *an offset that was ever a record start must remain a still being designed: *an offset that was ever a record start must remain a
record start.* `doc_bytes` reads a `u32` length prefix in place, so an record start.* `doc_bytes` reads a `u32` length prefix in place, so an