docs(spec): name the shape the transports carry in the websocket retirement note - #16978
Merged
zhuangjianguo merged 2 commits intoSep 8, 2026
Conversation
…rement note The retirement note for the deleted `FilterOperator` / `EventFilterCondition` / `EventFilterSchema` vocabulary said the subscription shape the transports actually carry is the `filters: z.unknown()` on `SubscriptionEventSchema` (`api/realtime.zod.ts`). Measured, no transport parses that schema at all: the shipped subscription path carries a plain TS interface one directory over, `contracts/realtime-service.ts#RealtimeSubscriptionOptions`, whose `object` and `eventTypes` are the only two fields `matchesSubscription` reads. Only the sentence's second half moves. Its first half — `matchesSubscription` matches on object name and event type only — was already exact. `SubscriptionEventSchema.filters` keeps its place in the note as the sibling declaration of the same unenforced kind, which is what it is; it is simply no longer described as a shape anything transports. The correction uses a symbol anchor rather than a bare name, because the whole point of the sentence is which of four same-ish spellings is meant, and rather than a line number, because prose citations by line rot. No key, type, export or tombstone is touched, and no behaviour changes. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_016N6xmWt5hYm94ffVEwGH8x
…t-note correction Both halves of the publish test say yes, so this is not a `skip-changeset`: the changed path matches `src/**/*.zod.ts` in the package's `files[]`, and the changed text is byte-present in the packed tarball. Measured on the real `npm pack` output with controls — a `.describe()` from the same file reaches `src` + `dist` + `json-schema` (7 files), a string unique to a sibling `*.test.ts` reaches none, and this `//` comment reaches exactly the shipped source file. `src/**/*.zod.ts` is the tree that decides it. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_016N6xmWt5hYm94ffVEwGH8x
Contributor
📓 Docs Drift Check
What this run could not see
Coarse fallback — 131 page(s) merely mention a changed package (the pre-#9192 predicate, kept for the deliberately-wide backstop): |
zhuangjianguo
marked this pull request as ready for review
September 8, 2026 22:16
zhuangjianguo
enabled auto-merge
September 8, 2026 22:16
zhuangjianguo
deleted the
claude/issue-15243-websocket-retirement-note-names-carried-shape
branch
September 8, 2026 22:43
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Fixes #15243
Clause-②: no
This corrects one clause of a retirement comment so that it names the shape the transports
actually carry. No accept set moves, no export is added or removed, no key is retired or revived,
and no schema arm, bound or behaviour changes.
SubscriptionEventSchema.filtersandEventSubscriptionSchema.filtersboth stay exactly as they are, and the ADR-0104 tombstoneparagraph is untouched. The dispatch's fence — stop and report if correcting the sentence appears
to require touching a key, a type, an export or a tombstone — was never reached: the diff is six
comment lines plus a changeset. 拉回已声明契约 ⇒ 常规档.
What was wrong
packages/spec/src/api/websocket.zod.tscarries the retirement note for the deletedFilterOperator/EventFilterCondition/EventFilterSchemavocabulary. Two sentences in, itstated, as a positive fact:
The first half is exact. The second half named the wrong shape — and since it was the only
prose in the repo connecting the two, it read as evidence that
SubscriptionEventSchemahas aconsumer. The liveness ledger seeded in PR #15242 grades every property of that schema
deadonthe opposite measurement, so the tree carried a ledger row and a source comment that disagreed.
The correction
Only the sentence's second half moved:
Three things this does deliberately, each one flagged by triage as likely to be simplified away:
matchesSubscriptionmatches on object name and event typeonly — already exact, and left alone byte for byte.
SubscriptionEventSchema.filterskeeps its place in the note, as the sibling declaration ofthe same unenforced kind. It is simply no longer described as a shape anything transports.
contracts/realtime-service.ts#RealtimeSubscriptionOptions— not abare name and not a line number. The whole point of the sentence is which of four same-ish
spellings is meant, so a bare name would re-plant the very ambiguity it exists to remove; and a
path:NNNcitation is what PR docs(spec,rest): anchor record-validator citations by symbol, not line #16956 spent seven prose citations repointing away from.Quoted verbatim from triage, per the repo's rule that a Chinese ruling is not translated:
Why the named symbol is the right one
Four same-ish spellings exist; exactly one is executed.
SubscriptionSchema/SubscriptionEventSchemaapi/realtime.zod.tsEventSubscriptionSchemaapi/websocket.zod.tsRealtimeSubscriptionOptionscontracts/realtime-service.tsSubscription(file-local interface)service-realtime/src/in-memory-realtime-adapter.tsRe-measured on this branch's own base
ee4a59b6c, not inherited from the card's5f5511f0:RealtimeSubscriptionOptionsis declared atpackages/spec/src/contracts/realtime-service.ts:38and consumed at
:109. It is the only declaration site of that symbol in the repo — one hit —so the new anchor resolves unambiguously.
matchesSubscriptionreadssub.options(typedRealtimeSubscriptionOptions) and comparesopts.objectandopts.eventTypes, and nothing else. Hence "the only two fields that predicatereads" — deliberately not "the only two fields on the interface", since the interface also
declares a
filter?key that the adapter never evaluates.The zero, with a firing control. A bare zero is not a reading, so both halves were re-run:
SubscriptionEventSchemaRealtimeSubscriptionOptions(control)packages/minuspackages/spec, excluding CHANGELOGspackages/spec, excluding CHANGELOGsThe control fires in both rows, so the zero is a measured absence rather than instrument silence.
The three extra hits in the wider row are
content/docs/references/api/realtime.mdx(generatedreference),
skills/objectstack-api/references/_index.md(published prose) anddocs/qa/platform-checklist/coverage.json(a waiver string) — no transport among them. The singleimportstatement namingSubscriptionEventSchemaanywhere outsidepackages/speclives in thatgenerated
.mdx. The card's claim stands: no transport parses aSubscriptionEvent.Changeset — both halves measured, not assumed
.changeset/websocket-retirement-note-carried-shape.md,@objectstack/spec: patch.files[]? Yes —websocket.zod.tsmatches thesrc/**/*.zod.tsentry.npm packtarball (2073 files) with controls that actually discriminate:
srcdistjson-schema//comment (subject).describe()from the same file (positive control)src/api/websocket.test.ts(negative control)The positive control lands in
distandjson-schema, so those zeros for the subject are ameasured absence, not a silent instrument; the negative control confirms the tarball genuinely
excludes what
files[]excludes. The tree that decides it issrc/**/*.zod.ts— the sourcefile ships to npm verbatim, comments included. So something published does move, and
skip-changesetwould have been wrong.Checks
Derived from the tree rather than recalled, via
node scripts/pm/dispatch-gates.mjs --repo objectstack-ai/objectstack, then reconciled with--ran: 68 of 69 derived gate families run and green.pnpm --filter @objectstack/spec buildcommand-exit 0)pnpm --filter @objectstack/spec testpnpm --filter @objectstack/spec typecheckcheck:scripts-typecheck,check:test-typecheck)pnpm lint(full repo,eslint . --no-inline-config)fdb725a6bpnpm --filter @objectstack/spec check:generatedpnpm check:nul-bytes+ a manual control-byte scan of both filespnpm check:dual-build-cjs-loadsDoes this comment reach a generated page? No.
check:generatedreports all fifteen artifactscurrent,
check:docs(content/docs/references/**) among them, so no docs build or regenerationis owed. That is consistent with the tarball reading: a
//line comment is not a.describe()string and does not travel to the reference tree.
The one gate not measured.
pnpm check:dual-build-cjs-loadsexited 3, its ownPREREQUISITE NOT METcode, printing "⛔ This is NOT a pass: nothing was measured" and listing 87packages with no
dist/. It wants a fullpnpm buildof the farm, which is CI's run, not acomment-only PR's. It is recorded as NOT MEASURED — not as a red, and not inside the green count.
A second gate,
check:doc-formula-expressions, exited 3 the same way; that one needed only@objectstack/formulaand@objectstack/lintbuilt, so it was cleared and re-run green.No ablation or reverse-verification run is reported, because there is none to run honestly: this
change adds no guard and alters no executed path, so there is no assertion whose failure could be
demonstrated. Report of absence is not being offered as success anywhere above.
验收备注
Observed while measuring, out of this card's declared file surface, not filed and not fixed —
for the PM to route:
packages/spec/liveness/realtime_subscription.json(_note) quotes the very clause this PRrewrites — "the prose in websocket.zod.ts:62-63 saying …" — as the warning that patches over
the source comment. After this PR that quoted prose no longer exists in the tree, so the ledger
note becomes a correction of something already corrected. Two things worth stating precisely:
its
path:NNNanchor already pointed at the sentence's first half rather than the secondhalf it quotes, measured at
ee4a59b6cbefore this change; and this PR does not move lines62-63 at all, since the edit begins at line 64. So the drift is pre-existing, not introduced
here.
check:livenessis green either way — the gate does not read the quotation.RealtimeEventTypespellsrecord.*while the runtime emitsdata.record.*, andSubscriptionSchema.filtersisz.unknown()nothing reads #14446's artifact, andediting it would pull
check:livenessin as a verification surface this card's file fence doesnot cover. The card, its triage and the claim all name
websocket.zod.tsas the only fileinvolved.
Generated by Claude Code