Skip to content

declared ≠ enforced (ADR-0049): RealtimeEventType spells record.* while the runtime emits data.record.*, and SubscriptionSchema.filters is z.unknown() nothing reads #14446

Description

@os-litant

Filed out of scope from the skills/objectstack-api optimization flight (#14304, finding API-F-06). The skill previously carried two prose notes telling authors these keys are declared but not enforced; those notes are deleted there, because an author cannot act on them. The upstream question is a triage decision, not a doc edit — hence this card.

Two declared-but-unenforced surfaces

1. RealtimeEventType names events the runtime never emits.

packages/spec/src/api/realtime.zod.ts:33 declares the enum, and :47 makes it the type of SubscriptionSchema.type:

  • the enum's members are record.created / record.updated / record.deleted (realtime.zod.ts:33-40);
  • the event names the platform actually emits are data.record.created / data.record.updated / data.record.deleted (packages/spec/src/api/events.zod.ts:139-141; a live emitter at packages/services/service-knowledge/src/knowledge-service-plugin.ts:168);
  • the two vocabularies are disjoint — every enum member is a prefix-short spelling of a name nothing publishes.

Measured consumers of RealtimeEventType outside its own declaring file: packages/spec/src/api/realtime.test.ts (its own unit test) and packages/spec/src/type-alias-convention.pin.test.ts:545 (the alias pin). No runtime reader.

2. SubscriptionSchema.filters is z.unknown().optional().

packages/spec/src/api/realtime.zod.ts:49 — filters: z.unknown().optional().describe('Filter conditions'). An authorable key with no shape and no enforcement: whatever an author writes parses, and nothing consumes it.

Related and already closed: #4673 (DataEventType's data.field.changed has no producer) is the same ADR-0049 fourth state in the sibling enum, so this pair is not an isolated case. Realtime subscription authoring has 0 usages across examples/** and packages/apps/**.

The decision this card asks for

Under ADR-0049 enforce-or-remove, each of the two is one of:

  • enforce — repoint RealtimeEventType at the emitted data.record.* vocabulary (or make the runtime emit the declared one, whichever is the intended contract), and give filters a real schema the subscription path applies;
  • remove — retire both under the ADR-0087 registries, per the spec-property-retirement playbook;
  • keep and ledger — record why the gap is deliberate, so the next audit does not re-file it.

Both are authorable today, so this is a spec-side change, not a docs change. No PR is attached; it needs triage before anyone writes code.

Refs: #14304 · #14292 · ADR-0049 · ADR-0087

Activity

  1. added
    findingRecorded observation — held, not dispatchable until the findings triage round grades it
    on Sep 2, 2026
  2. added
    pm:queueReady for the PM dispatch loop
    domain:specDomain lane — seat card indexed by label:pm:seat
    and removed
    findingRecorded observation — held, not dispatchable until the findings triage round grades it
    on Sep 2, 2026
  3. added theissue type on Sep 2, 2026
  4. huangyiirene commented on Sep 2, 2026

    @huangyiirene
    Collaborator

    Triage: priority:p2 · pm:queue · domain:spec · type Task. First grading, finding comes off.

    Same class as #14369 (graded this shift), and the same answer on the unit of work: ⛔ this card is not "decide enforce-or-remove for two keys". ADR-0049's route runs through the liveness ledger, and the spec-property-retirement playbook's entry condition is a ledger verdict. ⇒ The deliverable is both keys landed in packages/spec/liveness/api.json with the evidence this card already measured — and nothing else in that PR: no key enforced, removed, re-described or repointed.

    ⚠️ Collision worth knowing before you start: #14369 is queued to write ten other keys into that same ledger file. ⛔ Not a Blocked-by: — different keys, no ordering dependency — but the two PRs will conflict on api.json if they run concurrently. Whoever starts second rebases; whoever starts first says so on the other card.

    One narrowing the ledger entry should carry, because it is measurable now

    The card asks "repoint RealtimeEventType at the emitted vocabulary, or make the runtime emit the declared one, whichever is the intended contract". Those are not symmetric:

    • data.record.created / .updated / .deleted is declared in packages/spec/src/api/events.zod.ts:139-141 and has a live emitter (knowledge-service-plugin.ts:168);
    • record.* has no runtime reader at all — the card's own measurement finds only the enum's unit test and the alias pin.

    ⇒ The emitted vocabulary is the established one. If the verdict is enforce, it means repointing the enum; ⛔ it does not mean changing what the runtime publishes, which would break a live event contract to satisfy a member nothing has ever used. Write that into the ledger entry so the next reader does not re-open a settled direction.

    p2, with the zero-usage reading stated rather than ignored. Realtime subscription authoring has 0 usages across examples/** and packages/apps/**, and I am not inflating that. It sits at p2 for the same reason #14369 does: this is a published authoring surface where every member is wrong, not merely unread. An author who writes the enum's own record.created gets a subscription that silently never fires — and the enum is what the API reference shows them. Latent, but customer-facing when it fires.

    filters: z.unknown().optional() is the easier half — an authorable key with no shape and no consumer, which is the textbook ADR-0049 fourth state. It should reach the ledger with the same evidence and, if the verdict is remove, go through ADR-0087 like any other retirement.

    #4673 is the useful precedent and the card is right to name it: DataEventType's data.field.changed with no producer, same fourth state in the sibling enum, already closed. ⇒ Check how that one was disposed of before proposing a verdict here; a second answer to the same question in the same file family would be its own defect.

    Size/model suggestion: S, sonnet — the measurement is done; the work is transcribing it into the ledger format and re-running it at a pinned ref.


    Generated by Claude Code

  5. added
    pm:dispatchedDispatched to a dev agent by /pm-dispatch
    and removed
    pm:queueReady for the PM dispatch loop
    on Sep 4, 2026
  6. self-assigned this
    on Sep 4, 2026
  7. os-justin commented on Sep 4, 2026

    @os-justin
    Collaborator

    Claim: PM loop round R1 (slot freed by #14412's delivery) — domain:spec seat (seat post #6017). Executing the triage grading (comment 5506262367, 2026-09-02): the deliverable is both surfaces landed in the liveness ledger with the evidence this card measured — ⛔ no key enforced, removed, re-described or repointed in this PR; the ledger entry records the direction narrowing the triage stated (the emitted data.record.* vocabulary is the established one; "enforce" would mean repointing the enum, never changing what the runtime publishes).
    Session: session_01H2oQebDDxYKfWZusyd8GXk (GitHub os-justin)
    Branch: claude/issue-14446-realtime-liveness-ledger
    Worktree: objectstack-issue-14446
    Domain: domain:spec
    File surface: packages/spec/liveness/** — the ledger rows for SubscriptionSchema.type (whose accept set is RealtimeEventType) and SubscriptionSchema.filters, in whichever ledger file the liveness gate's enrolment rule assigns (api.json if SubscriptionSchema is governed there; otherwise a new ledger file enrolled the way the four RestServerConfig files were in PR #14638 — the gate's script header under packages/spec/scripts/liveness/ is the authority), the generated liveness/state-counts.md, the gate's enrolment list if a new file needs it, one @objectstack/spec changeset (liveness/** ships in the tarball — the PR #14638 precedent). ⛔ Not packages/spec/src/api/realtime.zod.ts, not events.zod.ts (#14971 in flight there), not any runtime package. Stop on breach; explain in the report.
    Container & model: S, mode:subagent, model: opus (judgment: which ledger file, the verdict wording, the precedent read of the data.field.changed retirement PR #4685) — --tier packages/spec/liveness/api.json at 50d6c924 (06:19Z): no path-derived mandate; content: ledger data, no accept/reject behaviour moves. Triage's sonnet suggestion not taken: the enrolment mechanics are a judgment, not a transcription.
    Clause-②: no
    Serial constraints cleared: packages/spec/liveness/api.json last touched 8cb96ec4 (#13039, re-anchoring batch); realtime.zod.ts not written by this card; #14369 (the collision the triage warned about) CLOSED via PR #14638 (merged) — the collision is spent; open branch claude/issue-14442-package-record-serializable exists at 06:19Z (its card is not this lane's; the dev checks its file list at worktree time before touching liveness/**). In flight in this lane: #14971 (api/events.zod.ts), #14414 (automation/execution.zod.ts) — disjoint; #14971's dev reads liveness/api.json read-only. H17 index: no hit. Readings on origin/main 50d6c924 (06:19Z): RealtimeEventType members record.created/updated/deleted (realtime.zod.ts:34-36), filters: z.unknown().optional() (:49), emitted names data.record.* (events.zod.ts:139, emitter knowledge-service-plugin.ts:168); liveness/api.json has 0 hits for RealtimeEventType / SubscriptionSchema / filters; SubscriptionSchema has zero readers outside packages/spec (changelogs only); service-realtime/src reads no .filters.


    Generated by Claude Code

  8. os-justin commented on Sep 4, 2026

    @os-justin
    Collaborator

    Dispatch (R1 slot refill, 2026-09-04T06:21Z) — domain:spec seat, session_01H2oQebDDxYKfWZusyd8GXk (os-justin), seat post #6017. mode:subagent, model: opus, size S, Clause ② no. The dev leaves its own Claim: comment below before its first edit.

    Rulings (not re-decidable). Triage 5506262367 (2026-09-02), verbatim where operative: "The deliverable is both keys landed in packages/spec/liveness/api.json with the evidence this card already measured — and nothing else in that PR: no key enforced, removed, re-described or repointed." "If the verdict is enforce, it means repointing the enum; ⛔ it does not mean changing what the runtime publishes … Write that into the ledger entry so the next reader does not re-open a settled direction." "#4673 is the useful precedent … Check how that one was disposed of before proposing a verdict here." (It was disposed of by removal, PR #4685 — route 3 of the retirement playbook.) ⛔ This PR writes ledger rows and their evidence only; the enforce-or-remove verdict itself is a ledger status with evidence, not an edit to realtime.zod.ts.

    PM mechanism assumptions (verify; falsify freely). (a) The triage's "api.json" names the ledger by family, not by measurement: on origin/main 50d6c924 liveness/api.json is a per-type ledger (type / _note / props) with zero rows for SubscriptionSchema; the liveness gate under packages/spec/scripts/liveness/ decides which schemas each ledger file governs and how a schema no registry holds is enrolled (the four RestServerConfig files in PR #14638 went through the gate's SPEC_ONLY_SCHEMAS override). Read the gate's header first; if SubscriptionSchema is not governed by api.json, a new ledger file enrolled the same way is the correct landing and the triage's file name is a drift, not a ruling — say so in the report. (b) The two rows are SubscriptionSchema.type (accept set RealtimeEventType — the declared spelling record.* vs the emitted data.record.*; note the direction narrowing verbatim from the triage) and SubscriptionSchema.filters (z.unknown().optional(), no shape, no reader). Both read as the ledger's dead/unenforced state on the evidence measured; re-take every reading at your worktree's base and quote it with the ref (symbol-anchored evidence, the ledger's own row format — read two existing rows and liveness/README.md before writing). If the ledger's schema also wants the other SubscriptionSchema keys classified when a schema is first enrolled (the RestServerConfig precedent classified every authorable property of the enrolled sub-objects), do that — it is the enrolment's own rule, not scope creep; say which keys and why. (c) check:liveness regenerates liveness/state-counts.md; run it and commit the follower. (d) Changeset: liveness/** is in the tarball's files, so a patch changeset for @objectstack/spec (PR #14638's precedent); no BREAKING banner, no ADR-0087 marker.

    PM suggested route (optional). Follow PR #14638's shape end to end (changeset text included); the SubscriptionSchema.type row's note carries the settled direction; the filters row's note names the textbook fourth state and the removal route as the likely verdict for the follow-up card — ⛔ no removal here.

    Surface, gates, report. File surface = the claim comment 5536555785 (stop on breach). Before touching liveness/**, read the file list of the open branch claude/issue-14442-package-record-serializable and of the in-flight lane PRs (#14971 → api/events.zod.ts, #14414 → automation/execution.zod.ts) — report any overlap. Derive gates from your actual paths after the last commit (node scripts/pm/dispatch-gates.mjs --repo objectstack-ai/objectstack --commands, no path args — it reads the change set itself); at minimum pnpm --filter @objectstack/spec check:liveness, check:generated, check:published-files, check:doc-authoring, check:nul-bytes, check-system-context-census, the changeset gate self-tests. Report: os-dev-report first as a comment here, then as the return; machine-checkable fields only (status, pr, premise_still_valid, files_changed, gates pinned to the head sha, deviations, open_questions, out_of_scope_findings filed as cards after a dedup search).


    Generated by Claude Code

  9. os-justin commented on Sep 4, 2026

    @os-justin
    Collaborator

    Claim: dev subagent under the domain:spec PM seat (seat post #6017), executing dispatch 5536560085 and the PM claim 5536555785. Assignee untouched — it is the PM's.
    Session: session_01H2oQebDDxYKfWZusyd8GXk
    Branch: claude/issue-14446-realtime-liveness-ledger (pushed empty at claim time as the write-route probe; worktree base 5f5511f0, not the dispatch's 50d6c924 — origin/main had moved on by the time the worktree was cut, and the worktree is cut from origin/main per the standing rule)
    Worktree: objectstack-issue-14446

    File surface (stop on breach):

    • packages/spec/liveness/ — the new ledger file for the enrolled schema, plus its row in README.md's "Current state" table and the heading count that table checks;
    • packages/spec/liveness/state-counts.md — generated follower, regenerated by the gate;
    • packages/spec/scripts/liveness/check-liveness.mts — the enrolment only (GOVERNED, the SPEC_ONLY_SCHEMAS override and its rationale comment), the shape PR chore(spec): govern the four RestServerConfig sub-objects in the liveness ledger #14638 used for the four RestServerConfig sub-objects;
    • one @objectstack/spec patch changeset.

    Not touched: packages/spec/src/api/realtime.zod.ts, packages/spec/src/api/events.zod.ts (#14971 is in flight there), any runtime package, content/docs/releases/. No key enforced, removed, re-described or repointed.

    One premise correction found at the base ref, reported here because it changes which rows get written, not whether they do. Both keys the card measures are real, at exactly the lines the card cites, with exactly the shapes it quotes — but they are declared on SubscriptionEventSchema (realtime.zod.ts:46-50), not on SubscriptionSchema (:57-62, whose own keys are id / events / transport / channel). The platform's own authorable-surface census already spells them apart: packages/spec/authorable-surface/api.json lists api/SubscriptionEvent:type / :filters beside api/Subscription:id / :events / :transport / :channel. The rows land under the schema that actually declares them; the PR body carries the evidence with refs.

    Overlap check before touching liveness/: claude/issue-14442-package-record-serializable touches packages/runtime/** and one changeset only — no overlap. #14971 (api/events.zod.ts) and #14414 (automation/execution.zod.ts) are disjoint from liveness/. #14369, the collision the triage warned about, landed as PR #14638 and is spent.

    Clause-②: no


    Generated by Claude Code

  10. os-justin commented on Sep 4, 2026

    @os-justin
    Collaborator

    os-dev-report
    {
    "issue": 14446,
    "status": "done",
    "branch": "claude/issue-14446-realtime-liveness-ledger",
    "pr": "#15242",
    "head_sha": "779b0736",
    "base": "5f5511f0 (worktree cut from origin/main at claim time; the dispatch's 50d6c92 had already moved). Merged origin/main 9c1bcda into the branch before the gate derivation so dispatch-gates stopped reporting STALE TREE; those 5 files are main's, and git diff --name-only origin/main...HEAD is still exactly the 5 paths below.",
    "premise_still_valid": true,
    "premise_evidence": {
    "RealtimeEventType spells record.": "HOLDS - realtime.zod.ts:33-38 at 5f5511f: record.created / record.updated / record.deleted / field.changed. The card's enumeration omits the fourth member; field.changed is the same spelling DataEventType removed in 17.0.0 (#4673, PR #4685).",
    "runtime emits data.record.
    ": "HOLDS - events.zod.ts#DataEventType = data.record.created/updated/deleted; emitter branches on those strings in packages/services/service-knowledge/src/knowledge-service-plugin.ts.",
    "filters is z.unknown().optional()": "HOLDS - realtime.zod.ts:49, filters: z.unknown().optional().describe('Filter conditions').",
    "no ledger row exists yet": "HOLDS - zero rows for Subscription/Realtime across packages/spec/liveness/*.json (only webhook.json prose mentions the word).",
    "the two keys are on SubscriptionSchema": "CORRECTED - they are on SubscriptionEventSchema (realtime.zod.ts:46-50). SubscriptionSchema (:57-62) declares id / events / transport / channel. The cited lines :47 and :49 and the quoted shapes are exact; only the owning symbol was misattributed, in both the card body and the triage. packages/spec/authorable-surface/api.json already spells them apart: api/SubscriptionEvent:type and :filters beside api/Subscription:id/:events/:transport/:channel. Rows land as events[].type and events[].filters under the enrolled root. Substance of the premise unaffected, so premise_still_valid stays true."
    },
    "files_changed": [
    ".changeset/liveness-realtime-subscription-surface.md (new, patch @objectstack/spec)",
    "packages/spec/liveness/realtime_subscription.json (new, 6 rows, all dead)",
    "packages/spec/liveness/README.md (+1 table row, heading count 35 -> 36)",
    "packages/spec/liveness/state-counts.md (generated by gen:liveness-counts)",
    "packages/spec/scripts/liveness/check-liveness.mts (enrolment only: import, GOVERNED entry, SPEC_ONLY_SCHEMAS entry + rationale comment)"
    ],
    "enrolment_decision": "PM mechanism assumption (a) CONFIRMED: liveness/api.json governs ApiEndpointSchema (metadata type api, 13 endpoint props) and has no relation to SubscriptionSchema - subscription rows written there would report ORPHAN. The triage's file name is a drift, not a ruling. Authority = the gate's own header in packages/spec/scripts/liveness/check-liveness.mts (registry-rooted walk; a schema no registry holds is enrolled via SPEC_ONLY_SCHEMAS), the route webhook/query/validation/qa/manifest and the four RestServerConfig sub-objects (PR #14638) take. New ledger file realtime_subscription.json, rooted on SubscriptionSchema not RealtimeConfigSchema because the walk drills exactly one level (--dump confirms events auto-expands to type/object/filters); rooting on the config would leave both measured keys inheriting a container verdict. Drilling events adds no new undrilled-containers baseline entry. All six authorable properties classified per the enrolment's own rule (the RestServerConfig precedent): id, events[].type, events[].object, events[].filters, transport, channel - all dead, because nothing outside packages/spec imports SubscriptionSchema / SubscriptionEventSchema / RealtimeConfigSchema, so no key beneath them can be read.",
    "gates": {
    "derivation": "node scripts/pm/dispatch-gates.mjs --repo objectstack-ai/objectstack --commands, run after the last commit with no path args, on a tree at origin/main - exit 0, no STALE TREE warning, change set 5 paths vs merge base 9c1bcda, 43 runnable commands. All results below pinned to head 779b073; exits captured before any pipe.",
    "derived_families_43": "42 exit 0; 1 exit 3 (dual-build-cjs-loads, below).",
    "pnpm --filter @objectstack/spec check:liveness": "exit 0 - realtime_subscription 6 classified (dead 6); verdict lines: the README state table carries a row for each of the 36 governed type(s) it claims to index and packages/spec/liveness/state-counts.md is current - the same 36 row(s), no count column left in the README.",
    "pnpm --filter @objectstack/spec check:generated": "exit 0 - All 15 generated artifacts are up to date. FIRST run exited 1 claiming api-surface/ stale; that was a phantom of an unbuilt dist (the gate reads built output and says so). After pnpm --filter @objectstack/spec build (via scripts/pm/os-verify-lock.sh, VERDICT command-exit 0, held 133s) it is green with nothing regenerated. Second clean run is the one reported.",
    "pnpm check:published-files": "exit 0",
    "pnpm check:doc-authoring": "exit 0",
    "pnpm check:nul-bytes": "exit 0",
    "node scripts/check-system-context-census.mjs (+ --self-test)": "exit 0 / exit 0",
    "pnpm check:changeset-gate-self-tests": "exit 0",
    "node scripts/check-empty-changeset.mjs --self-test": "exit 0",
    "node scripts/check-adr-0087-registration.mjs --self-test": "exit 0",
    "pnpm check:pm-dispatch-gates / pm-governed-merges / pm-half-states": "exit 0 / 0 / 0",
    "pnpm check:dual-build-cjs-loads": "exit 3 - NOT MEASURED, not red: the gate prints PREREQUISITE NOT MET - this gate reads built output, and some package has no dist/ and its own This is NOT a pass: nothing was measured. It needs a whole-monorepo pnpm build; left to CI.",
    "gate-script own suites (owed because this PR edits packages/spec/scripts/liveness/check-liveness.mts)": "pnpm --filter @objectstack/spec exec vitest run --maxWorkers=2 scripts/liveness/check-liveness.test.ts scripts/liveness/elsewhere.test.ts scripts/liveness/evidence.test.ts scripts/liveness/readme-table.test.ts scripts/zod-graph.test.ts src/system/metadata-form-zod-reconciliation.test.ts - exit 0, Test Files 6 passed (6), Tests 210 passed (210) (via os-verify-lock.sh, VERDICT command-exit 0, held 87s). Plus pnpm --filter @objectstack/spec check:scripts-typecheck exit 0.",
    "artifact-roster families the derivation flags as non-evidence": "run rather than assumed - node scripts/check-changeset-fixed.mjs, check:spec-changes, check:authz-resolver, check:error-code-casing, check:filter-alias-parity, check:swallow-census-controls: all exit 0.",
    "pnpm lint (repo-wide eslint . --no-inline-config)": "exit 0 in 76s. Full scan run, no narrowing claimed."
    },
    "deviations_from_claim_surface": [
    "NONE on files: the delivered surface is exactly the claim's (liveness ledger + README + state-counts + the gate's enrolment list + one @objectstack/spec changeset). realtime.zod.ts, events.zod.ts and every runtime package untouched.",
    "Base ref is 5f5511f, not the dispatch's 50d6c92 - origin/main had moved before the worktree was cut; declared in the claim comment.",
    "One merge commit of origin/main 9c1bcda into the branch, taken so dispatch-gates would derive from a non-stale tree (its warning named 5 workflow/script files it derives from as changed). Disjoint from these paths."
    ],
    "overlap_check": "claude/issue-14442-package-record-serializable touches packages/runtime/src/app-plugin.ts, one test and one changeset - no overlap with liveness/. #14971 (api/events.zod.ts) and #14414 (automation/execution.zod.ts) are disjoint. #14369, the collision the triage warned about, landed as PR #14638 and is spent - liveness/api.json is untouched by this PR either way.",
    "summary": "Enrolled SubscriptionSchema in the spec liveness ledger as a new governed spec-only type (realtime_subscription), classifying all six authorable properties dead with symbol-anchored evidence, and quoting the triage's settled direction verbatim in the events[].type row. Both keys the card measured are ledgered; the card's and triage's attribution of them to SubscriptionSchema is corrected to SubscriptionEventSchema, reached as events[].type / events[].filters. No key enforced, removed, re-described or repointed.",
    "tests": "See gates. No ablation was run: this PR adds ledger data and a gate enrolment, so the falsifiable claim is the gate's own - it was measured in both directions instead. RED before the work: check:liveness exit 1 with realtime_subscription - counted in state-counts.md, no row in the README table and the stale state-counts diff, i.e. the gate does notice this type. GREEN after: exit 0 with the two verdict lines quoted above. The one phantom encountered (check:generated calling api-surface/ stale) was resolved by building spec first, not by regenerating - reported rather than silently fixed.",
    "mcp_calls": "7 - issue_read get_comments x2 (triage/claim/dispatch read, then claim readback), add_issue_comment x2 (claim, this report), create_pull_request x1, search_issues x1 (dedup, with #14446 returning as the positive control), issue_write x1 (the finding). Card body and the triage comment were read through the zero-quota public-repo payload channel; the container's REST channel is 403 for this seat (GitHub access is not enabled for this session), probed before use.",
    "open_questions": [],
    "out_of_scope_findings": [
    "filed as #15243 (finding, unassigned): websocket.zod.ts:61-63's retirement note claims the transports carry SubscriptionEventSchema's shape; measured, they carry contracts/realtime-service.ts#RealtimeSubscriptionOptions, which is what in-memory-realtime-adapter.ts#matchesSubscription reads. Comment-only, no behaviour. Dedup search returned 3 results, none a duplicate, with #14446 itself as the positive control that the search was live this session.",
    "not filed, recorded here: the card body enumerates RealtimeEventType as three members and omits field.changed, the fourth - the same spelling DataEventType removed under ADR-0049 in #4673 / PR #4685. Captured in the ledger row rather than as a separate card, since it is a detail of the card this PR closes.",
    "platform reading, not a repo defect: GitHub's issue-body sanitizer STRIPS the trailing --- + _Generated by [Claude Code](...)_ footer block on issue CREATE (measured 2026-09-04 on #15243: absent from both the stored body and the rendered page), while the same block survives verbatim on comments (measured on the claim comment 5536600582 the same hour). Not re-posted, per the no-footer-reposting rule; attribution for #15243 is carried in its prose instead."
    ]
    }


    Generated by Claude Code

  11. os-justin commented on Sep 4, 2026

    @os-justin
    Collaborator

    ACCEPT — PR #15242 (head 779b0736), reviewed 2026-09-04T07:02Z by the domain:spec seat, session_01H2oQebDDxYKfWZusyd8GXk (os-justin), seat post #6017. Clause ② no (ledger data + gate enrolment; no accept/reject behaviour moves, realtime.zod.ts untouched) — no contract review owed.

    Checklist readings, taken on GitHub: draft ✓ · base main (1bc3c092) ✓ · get_files = exactly the claimed surface (liveness/realtime_subscription.json new, liveness/README.md +1 row and the checked heading 35 → 36, liveness/state-counts.md regenerated, scripts/liveness/check-liveness.mts enrolment only — import, GOVERNED entry, SPEC_ONLY_SCHEMAS entry with rationale — and one patch changeset for @objectstack/spec, the PR #14638 precedent since liveness/** ships in the tarball) · no .zod.ts edited, no runtime package touched · governed-surface predicate 0 of 5 · no content/docs/releases/**. Substance: the triage's api.json name was a drift (that ledger governs ApiEndpointSchema) — the dev's enrolment of a new realtime_subscription root through the override is the mechanism the gate's own header prescribes and the RestServerConfig precedent took; rooting on SubscriptionSchema rather than the config is the one-level-drill argument, verified. The attribution correction is right: on origin/main type and filters are declared on SubscriptionEventSchema (realtime.zod.ts), SubscriptionSchema carries id / events / transport / channel — this seat's own claim repeated the card's misattribution. The events[].type row quotes the 2026-09-02 direction verbatim and records the fourth member field.changed (the card listed three) with its DataEventType precedent; all six rows dead with symbol-anchored evidence and the three same-name-different-shape traps named. Test evidence: check:liveness measured RED before (missing README row + stale counts) and GREEN after with the verdict lines quoted; the gate's own suites (6 files, 210 tests) and pnpm lint (whole) green at the head; check:generated green after a spec build (the first-run stale reading was an unbuilt-dist phantom, reported not hidden). Deviations: none on files; base 5f5511f0 + one merge commit of 9c1bcda3 (no rebase) — accepted. Out-of-scope: finding #15243 filed by the dev (the websocket.zod.ts retirement-note prose names the wrong carrier).

    Landing: CI on 779b0736 converging; on all-green: ready → squash auto-merge → queue; on MERGED pm:dispatched strips, landing note here with the ledger probe on origin/main. Follow-up for triage, not a dispatch: the whole SubscriptionSchema surface is now ledgered dead — the enforce-or-remove call per key is the human floor's, as the rows say.


    Generated by Claude Code

  12. os-justin commented on Sep 4, 2026

    @os-justin
    Collaborator

    Landing provenance (2026-09-04T07:28Z, domain:spec seat, session_01H2oQebDDxYKfWZusyd8GXk): PR #15242 head 779b0736 — all 34 check runs success or skipped (Test Core rollup 07:21:02Z, Lint & Repo Gates 07:14:03Z, Type Check rollup 07:10:09Z, Spec property liveness green), mergeable_state: clean, first body line Fixes #14446, governed-surface predicate 0 of 5. ACCEPT 5536953734 (Clause ② n/a) is the review of record. Flipped ready → squash auto-merge → merge queue; on MERGED: pm:dispatched stripped, landing note here with the ledger probe on origin/main.


    Generated by Claude Code

  13. os-justin commented on Sep 4, 2026

    @os-justin
    Collaborator

    Landed — domain:spec seat, session_01H2oQebDDxYKfWZusyd8GXk, 2026-09-04T08:34Z. PR #15242 MERGED 08:14:18Z through the merge queue as 368a82e6 (squash); Fixes closed this card at 08:14:19Z. Probed on origin/main 4dd5041b (08:32Z): packages/spec/liveness/realtime_subscription.json is present (six rows, all dead, the settled direction quoted in the events[].type note), packages/spec/scripts/liveness/check-liveness.mts:410 enrols realtime_subscription: SubscriptionSchema, and packages/spec/liveness/README.md:900 carries the row. pm:dispatched stripped (read-modify-write: priority:p2 · domain:spec kept). The card's one attribution correction (the two keys live on SubscriptionEventSchema, not SubscriptionSchema) is recorded in the PR body and the ledger note; the enforce-or-remove verdict on events[].type stays settled as "repoint the enum, never change what the runtime publishes" — a future card, not this one.


    Generated by Claude Code

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

Metadata

Metadata

Assignees

Labels

domain:specDomain lane — seat card indexed by label:pm:seatpriority:p2Medium: important, M3

Type

Projects

No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions