Event feed conformance: three corrections to what tier 2 claims (3/3)
Third of the split. Stacked on #705, which is stacked on #777 — review after
those. Three corrections to the tier-2 conformance family, all of them about the
suite claiming more than it proves.
1. Row 15's kill claim is partial, and the sentinel obligation is withdrawn
The mutation table said fixture 30 kills follow-cross-origin-redirect partly
through "the harness obligation that the fixture's foreign origin is bound to a
sentinel listener whose any-request fails the scenario". Fixture 30's own
description said the same.
No implementation meets that obligation, and meeting it would prove nothing.
At tier 2 the poll lane is a seam: the driver receives the fixture's scripted
302 and hands the connector an already-formed redirect-refused verdict. The Go
driver says so itself — "the driver is the seam, so the foreign origin is
unreachable by construction". The connector never sees a Location header and
never decides whether to follow one, so a silent sentinel is a statement about
the driver, not the connector.
That makes the mutation unreachable at this tier — it lives below the seam.
What fixture 30 does kill is the half above it: mishandling the verdict by
retrying it, misclassifying it, or echoing more of the Location than its
origin. Real zero-egress proof is the Layer-1 adapter's own 302 test, tracked
for G1b.
The fixture's description is amended too. Fixtures are merged contract and are
not edited lightly, but leaving the identical false claim in the file the other
five SDKs read would make this a half-correction. Prose only — no assertion
changed, both pin probes still verify.
This also corrects #705's own kill-matrix, which inherited the claim.
2. An advance whose window would fire any timer is rejected, in every driver
The driver ran advance through the plain clock Advance, with a comment
noting that a fixture wanting a chained firing "would pass AdvanceSettling the
rendezvous". That is an opt-in, and the next fixture author is exactly who would
not take it.
Stress-testing the settle killed it, for reasons that are fundamental rather
than effort:
- Waiting for a firing to be consumed deadlocks. The connector deliberately
does not consume a staleness firing promptly — a window closing during a
delivery is latched and observed later, which is what §23 requires. A clock
that waited would hang against the behavior the spec mandates. - Waiting for the follow-on arming requires knowing one is coming. Nothing
distinguishes "has not armed yet" from "will not arm".
So the reentrant clause is not settleable where the connector runs
concurrently, and the divergence is made unscriptable instead — by keying
the rule on firing, not arming. Arming happens on the connector's schedule,
so a driver could only detect it by waiting and then assuming nothing more was
coming — a heuristic wearing a MUST. What an advance would fire is decided from
the clock's own state before time moves: an advance whose window holds any
initially due timer now fails, naming fireTimer as the deterministic
alternative. This is stricter than an arming rule on purpose — a firing that
arms nothing is rejected too; a script that wants that firing writes
fireTimer and names the timer. No firing in the window means nothing can be
armed in response, so an accepted advance is provably inert. Unconditional — a
schema field would let an author take the divergence rather than avoid it. Go
implements the decision as a single critical section (AdvanceIfQuiet):
due-set read and time movement under one hold of the clock's locks.
The family README carries the constraint, because it binds all six SDKs and they
are split across single-threaded and concurrent test clocks.
Self-tested both ways: the reject case (an advance across the handshake
deadline, which is due inside the window) fails, and the control (a quiet
window) still passes — without which the guard would reject every advance,
including fixture 05's.
3. Fixture 31 — a straggler below the entry page's served id
Closes the hole where a dedupe ordering live ids against the highest poll-served
id passes the whole suite.
Fixture 20 already proves a post-snapshot straggler is delivered, but its entry
page serves no events, so a highest-served-id implementation passes it too:
with nothing served the mark is unset and every id clears it. Every other
straggler in the suite is buffered pre-cut.
31 serves id 99 on the entry page and pushes 41 afterwards, in the streaming
lane. 41 must be delivered — dedupe tracks actually-delivered ids and 41 was
never served by poll — while a re-push of 99 is still suppressed, which keeps
this a dedupe fixture rather than a no-dedupe one.
The kill was verified, and the first two attempts were wrong in instructive
ways. A mutant in admitLive survived — that is the buffer-admission path, and
a streaming straggler never goes through it. A mutant in the shared deliver()
gate failed six fixtures, so it was broader than the hole and would not have
justified a new fixture. The mutant that matches the claim — the ordering applied
only to the streaming lane — passes all of 01–30 and is killed by 31 alone.
Verification
go build / go vet / go test -race -count=1 pass; 23/23 fixtures green;
5 driver self-test groups pass; make event-feed-fixtures-check clean with both
pin probes verified; make go-lint 0 issues.
Summary by cubic
Tightens tier-2 event-feed conformance so the shared contract claims only deterministic behavior the suite can prove. Drivers now reject scheduling-dependent fixture scripts, while schema-valid numeric fixtures use portable value and range rules.
Conformance
advancemust be first or immediately followexpectStateplus non-emptyexpectTimers; windows with initially due timers are rejected, withfireTimeras the deterministic alternative.- The loader validates every numeric literal once by exact mathematical value before typed decoding, while preserving opaque event details and status-only response bodies.
- Duration and
assertDelayMsdecoding preserves absent, null, and value states and rejects invalid bounds or unknown members. - All integer fields have schema-declared limits, including million-item capacity ceilings, the IANA sendable close-code range with 1012–1014 included, and fixed 64-bit ping epochs.
- Fixture 31 proves that a live event below the highest poll-served ID is delivered while a duplicate served ID is suppressed.
- Fixture 30 now proves terminal redirect refusal with no retry or further poll; the adapter-level zero-egress proof remains tracked in #819.
- The Go reference documents the cable lane’s retained-payload ceiling and transient decode allocation without claiming a poll-page size bound.
Checks
- The tracking gate fails closed on missing or malformed YAML, duplicate keys, multiple documents, unregistered mentions, unsweepable files, and mention-count drift.
- Fixture, schema, pin-probe, Go, lint, and tracking self-tests pass.
Reconciled with the shipped feed contract (5bfcd836)
Rebased onto the reconciled #705. Fixture 31's rows take the shipped nine-key poll row and eleven-key push payload, and every integer field in the schema now carries an explicit, portable ceiling (int64 for ids and epoch positions, int32 for counts/capacities/seconds, 65535 for a close code) so a schema-valid fixture is never one a driver must refuse at its own integer width — the open review thread on schema.json.