Skip to content

docs(adr): ADR-0012 — the hosted push relay transport (default) beside direct APNs/FCM, and vendor as a token type the relay routes on (ruling cloud#3519) - #29952

Merged
os-zhuang merged 2 commits into
mainfrom
claude/project-thread-geqk0x
Nov 3, 2026
Merged

os-zhuang merged 2 commits into
mainfrom
claude/project-thread-geqk0x

Conversation

@objectstack-fleet

@objectstack-fleet objectstack-fleet Bot commented Nov 3, 2026 •

Copy link
Copy Markdown
Contributor

domain:spec#3 · os-support-ai · session_01WGufzfgvGexKn9vLHsy89K

Requested by Yibo Wang · project thread
Fixes #29817
Ruling-ref: objectstack-ai/cloud#3519

Clause-②: no (docs only, no spec shape moves)
Metadata-protocol: none
Skills-docs: none — nothing under skills/** or the hand-written content/docs/** names the push relay, the push transport selection or PushVendorSchema (git grep -i -E "push relay|transport: ?'relay'|PushVendorSchema|PushRelayRequest|hosted relay" -- skills content/docs ':!content/docs/references' at cce992b: zero hits). The ADR is the only document that moves.

维护者速读

What changes

One file, docs/adr/0012-notification-platform.md (+22 / −9). Every passage is written to the shape main ships at 6a4737a (PR #29886 for #29816, PR #29794 for #29041), not to the card's wording where the two differ, and every amended passage quotes the wording it replaces. What is not served yet (the relay sender #29920, the phase-2 vendors, the end-to-end encryption) is worded as decided and planned, with its card where one exists.

Where Before After
Status line Amended (2026-11-03, #29041): push ships as service-push; the relay is the default per cloud#3519 and not yet served (#29920). The same note, extended rather than a parallel status line: … #29041; extended the same day by #29817 on ruling objectstack-ai/cloud#3519 — 1C 2A 3A 4B 5A 6B 7B, 2026-11-03 — and the spec shape of #29816, listing each passage the extension amends and what it read before. The status keyword stays Draft (first in the line), Superseded by ADR-0030 stays.
TL;DR item 2, Goals "Four built-in channels" push (APNs + FCM) push (the hosted push relay by default; direct APNs + FCM for a white-label build — amended 2026-11-03, cloud#3519, §6.1); this item / bullet read "push (APNs + FCM)"
Goals "Plugin extension" … / Telegram / domestic push vendors ship as MessagingChannel plugins with zero core change. The domestic push vendors leave the plugin list; an italic amendment quotes the old wording and says they are token types the hosted push relay routes on (§6.1).
Non-Goals "Domestic mobile push vendors" Schema reserves a vendor discriminator on sys_user_device, but only APNs and FCM ship in core. Domestic vendors are plugin transports. … in phase 1: vendor is the token-type discriminator the push transport routes on; core ships apns and fcm (PushVendorSchema, closed on main); in phase 2 the relay fronts the domestic vendors too and PushVendorSchema widens in the same change that routes them. The old wording is quoted; "neither plugin transports nor alternate channels".
Non-Goals, new bullet — System push in an air-gapped deployment: the relay is hosted in objectstack-ai/cloud and there is no self-hosted relay; no route to it means no system push, and direct does not restore one (it needs APNs and FCM); the in-app inbox only.
§4 push row Backend: "Direct: APNs (HTTP/2, token auth) + FCM (HTTP v1) …, for a white-label build. The hosted relay is the default transport (cloud#3519) and is not yet served (#29920)". Backend: relay (hosted, the default: the server POSTs an ID-only PushRelayRequestSchema to the hosted relay, authenticated by its cloud binding credential; the relay holds the store app's APNs/FCM credentials) and direct (APNs HTTP/2 token auth + FCM HTTP v1, the operator's own credentials, white-label only); the transport union on PushServiceConfigSchema (#29816). Status: direct shipped (#29041, PR #29794); relay decided, not yet served: PushServicePlugin refuses a relay block, {} included, at init until #29920. The pre-2026-11-03 cell text is quoted.
§6 domestic-vendor bullet Domestic vendors (HMS / Xiaomi / OPPO / VIVO) plug in as alternate channel implementations (plugin-notification-push-hms etc.), not as transports — their on-device SDKs and quotas are too different to share a code path. Domestic vendors are token types the hosted relay routes on, not a plugin boundary: what vendor on the row means; PushVendorSchema is apns and fcm, closed on main at this amendment (UserDeviceSchema narrows vendor to it); in phase 2 the relay routes HMS / Honor / Xiaomi / OPPO / vivo by the same vendor and the schema widens in the same change that routes them (the schema's docblock already says so; #29816 kept it closed on purpose); no card tracks the widening yet. The old bullet is quoted.
§6.1 (new) — Push transports — the hosted relay and direct delivery (amended 2026-11-03, cloud#3519): the ruling and the #29816 schemas; the two transports and the mixed-shape refusals as push-config.zod.ts has them; the wire (PushRelayRequestSchema / PushRelayResponseSchema with ok / invalid / retry, PushRelayErrorResponseSchema 401 / 429); vendor as token type; Privacy (phase 1 ID-only: data + one generic visible line, title / body / … refused by name, opaque to the relay; phase 2 end-to-end encryption with a device key, pushKey left out of the registration by #29816 on purpose, no card yet; the direct transport is outside the rule and sends title and body today, PR #29794); Air-gap (no system push; the in-app inbox only); What main serves at this amendment (direct served by service-push + push-channel.ts; relay refused at init with PUSH_RELAY_NOT_SERVED_MESSAGE, sender #29920, [PLANNED — not yet served] markers; phase-2 vendors and E2EE planned, no card).
M3 milestone Domestic push: HMS / Xiaomi / OPPO / VIVO as channel plugins. Domestic push: HMS / Honor / Xiaomi / OPPO / vivo, fronted by the hosted push relay as token types (phase 2 of cloud#3519, §6.1); the old line is quoted.
Risk 5 Push payload encryption (Web Push VAPID). Out of scope for M1; FCM and APNs only. Unchanged sentence, plus: the relay payload is ID-only in phase 1 and end-to-end encrypted with a device key in phase 2 (§6.1); Web Push stays out of scope.
Decision list, new bullet — Push transports (amended 2026-11-03, cloud#3519): relay default / direct white-label; vendor a token type the relay routes on, domestic vendors behind the relay in phase 2, never plugins; never plaintext (ID-only, then E2EE); air-gapped means no system push, inbox only.

Round 1 (contract review 6610988758, no must-fix; three accuracy items): the status line now lists the TL;DR item 2 and the Goals "Four built-in channels" bullet among the amended passages (both now quote "push (APNs + FCM)") and names the three passages that are new and replace nothing (the Non-Goals air-gap bullet, §6.1, the Decision bullet); §6.1 says which schemas carry the [PLANNED — not yet served] marker (the relay transport block and the seven relay object schemas; the two enums PushRelayPrioritySchema and PushRelayResultStatusSchema carry none); the relay block's keys are transport and endpoint.

Not changed, on purpose: the §6 UserDeviceSchema proposal block (the historical proposal; the #29041 amendment note under it already records the shipped, narrowed shape), the §1 diagram, §3 / §10 / §11 package and file names (the status line's plugin-notification-push reading covers them), the M1 milestone and the #1292 acceptance table (what M1 delivered), and ADR-0030 (unchanged, as the card requires).

Why

cloud#3519 (ruled 1C 2A 3A 4B 5A 6B 7B on 2026-11-03) decided that the store-published app pushes through a hosted relay, that vendor is a token type the relay routes on, that the relay never sees plaintext, and that an air-gapped deployment has no system push. #29816 (PR #29886) landed the spec shape and #29041 (PR #29794) landed the direct transport with a header note saying the relay is the default and not yet served. ADR-0012 still carried the pre-ruling decisions in §4 (APNs/FCM as the only push backend), §6 (domestic vendors as alternate channel plugins), the Goals and Non-Goals (domestic vendors as plugin transports) and the M3 milestone. Prime Directive #13: a recorded decision is reversed by an ADR edit.

Tests

n/a — docs only. No code, schema, test or generated artifact moves; the ADR gates below are the checks that read this file.

Acceptance notes

  • ADR-0030 read for conflicting push-vendor or relay wording: none found. Its push mentions are the L5 row sys_user_device (push) and item 3 push (sys_user_device + APNs/FCM). Neither says the server reaches APNs/FCM itself and neither names a vendor plugin; the relay forwards to APNs and FCM and direct reaches them, so both readings hold. ADR-0030 is untouched.
  • cloud#3519 was not readable from this session (GitHub access is scoped to objectstack-ai/objectstack), so the card body binds, as the order says. The ADR cites the ruling as the card states it (1C 2A 3A 4B 5A 6B 7B) and attributes no decision to an individual letter beyond what spec: push relay transport — transport: relay | direct on PushServiceConfigSchema, and the server→relay wire shape (ruling cloud#3519) #29816 attributes (2A: ID-only).
  • Clause-② spelling. The order says Clause-②: n/a (docs only); the claim (6610076511) says Clause-②: no (docs only, no spec shape moves). The body carries the claim's spelling: scripts/pm/clause2-line.mjs accepts only yes / no (CLAUSE2_VALUES), so n/a would read malformed to check-changeset-no-major. Reported as a deviation from the order's spelling.
  • Beyond the sections the card names, same wording family: the M3 milestone line ("as channel plugins"), risk 5 (payload encryption) and the Decision list are amended too, because each restated a decision the ruling reverses; the TL;DR and Goals "Four built-in channels" bullets gain the relay in parentheses so the ADR does not open by naming APNs + FCM as the push backend.
  • Stale code comments, not this card's surface (docs only; comments are not a card class under os-dev rule 3): packages/spec/src/system/push-config.zod.ts still says in two places that ADR-0012 §6 routes the other vendors to alternate channel plugins (the docblock's "Out of scope here" paragraph and the PushVendorSchema comment), and packages/spec/src/system/push-device.zod.ts says "the domestic Android vendors plug in as alternate channel implementations". Carrier: the phase-2 change that widens PushVendorSchema (no card yet); noted here, not filed.
  • No card tracks the phase-2 vendors or the end-to-end encryption; the ADR says "no card yet" for both rather than inventing a tracker. The relay sender is service-push: send through the hosted relay transport (push-relay.zod.ts wire, cloud binding credential) #29920.
  • Harness steps not taken, per os-dev rule 2: no assignees on pr_create, no reviewer request; the seat sets both at hand-over. The pr_create body carries no seat signature line: the relay's signature rule scopes to comment_edit / issue_patch, and the harness pins the attribution block to the body's first two lines (PR feat(spec): push relay transport — transport: relay | direct on PushServiceConfigSchema, and the server-to-relay wire shape #29886, same seat, same day, is the precedent); the seat appends its trailer at hand-over.
  • skip-changeset is declared on pr_create: docs/adr/** publishes nothing.

Verification

All readings at head 62ac6d3c on claude/project-thread-geqk0x, off origin/main 6a4737ab (round 0 readings at cce992bf were identical: the same 21 commands, all exit 0) (which carries PR #29886 and PR #29794). Every exit code was captured before any pipe; the gate sweep ran serially from a runner script with stdin closed, after the formula/lint build under scripts/pm/os-verify-lock.sh.

  • node scripts/pm/dispatch-gates.mjs --commands --repo objectstack-ai/objectstack docs/adr/0012-notification-platform.md: 21 commands (15 pnpm, 6 direct node; 13 matched by path, 8 whole-tree). All 21 exit 0, among them the ADR gates check-adr-links (and its --self-test), check:adr-anchors, check:adr-index ("docs/adr/README.md is current, every record parsed") and check:adr-symbol-anchors ("3075 anchors across 149 records resolve"; the new path#symbol anchors in the Non-Goals, §6 and §6.1 are among them), and the docs gates check:doc-authoring, check:doc-formula-expressions, check:nul-bytes, check-closing-keyword-parity, check:pm-governed-merges and check:pm-prior-rulings.
  • node scripts/pm/dispatch-gates.mjs --ran RAN_FILE --repo objectstack-ai/objectstack at 62ac6d3c: "21 derived, 21 run, 0 NOT-MEASURED, 0 UNRUN"; change set 1 path, +22 / −9, under the 3000-line human-merge threshold.
  • pnpm exec turbo run build --filter=@objectstack/formula --filter=@objectstack/lint --concurrency=2 under os-verify-lock.sh, the check:doc-formula-expressions prerequisite: VERDICT command-exit 0, held the lock 154 s.
  • GITHUB_EVENT_NAME=pull_request node scripts/check-changeset-no-major.mjs --base origin/main --event EVENT_JSON (a pull_request payload carrying this body and the skip-changeset label): exit 0; "LEVEL AXIS: this PR declares clause-② no and metadata-protocol none, and it introduces no changeset entry graded minor or above and moves no metadata-protocol baseline".
  • Dogfood battery: n/a — no ! in the title and no (narrowing) arm.
  • CI: every family CI runs on the PR is read from the PR's check-runs at review, not re-run locally.

Human Minimum

  • 1. Schemas start as Zod; packages/spec holds no runtime logic. (No code moves.)
  • 2. Every name I added uses the casing for its kind. (No names added.)
  • 3. No formatter ran; pnpm lint passes. (A Markdown file; the derived ADR and doc gates ran instead, below.)
  • 4. One issue, one branch off an up-to-date main, one pull request.
  • 5. A change that publishes carries a changeset, and both declaration lines are filled in here and in the changeset. (Publishes nothing: skip-changeset; both declaration lines are above.)
  • 6. packages/spec touched: its generated artifacts are regenerated and committed. (Not touched.)
  • 7. No hand edits to content/docs/references/, packages/*/CHANGELOG.md or content/docs/releases/.
  • 8. Title, body and comments are in English. (The 维护者速读 section is the maintainer's Chinese summary, by the dispatch contract.)
  • The Before Submitting list in CONTRIBUTING.md is done.

🤖 Generated with Claude Code

https://claude.ai/code/session_01WGufzj5io3Zzyb3E6VKCtj


Generated by Claude Code

Body refreshed by spec seat 3 (round 1, head 62ac6d3c): 2026-11-03T17:44Z

…lt) | direct (APNs/FCM, white-label), vendor as a token type the relay routes on, ID-only then E2EE privacy, no system push air-gapped (ruling cloud#3519)

Extends the header's Amended (2026-11-03, #29041) note for #29817: §4's push
backend row, §6's domestic-vendor bullet and a new §6.1 (transports, wire,
vendor as token type, privacy, air-gap, what main serves), the Goals and
Non-Goals bullets that called the domestic push vendors plugin transports, the
M3 milestone line, risk 5 and the Decision list. Every amended passage quotes
the wording it replaces. Written to the shape main ships (#29886, #29794):
the relay is decided and not yet served (#29920). ADR-0030 is unchanged.

Claude-Session: https://claude.ai/code/session_01WGufzj5io3Zzyb3E6VKCtj
Co-authored-by: Claude <noreply@anthropic.com>
@objectstack-fleet objectstack-fleet Bot added the skip-changeset PR has no user-facing published change; bypasses the changeset gate label Nov 3, 2026
@github-actions github-actions Bot added size/s documentation Improvements or additions to documentation labels Nov 3, 2026
@objectstack-fleet

Copy link
Copy Markdown
Contributor Author

domain:spec#3 · os-support-ai · session_01WGufzfgvGexKn9vLHsy89K

Contract-review-claim: session_01WGufzfgvGexKn9vLHsy89K · head cce992b · 2026-11-03T17:16Z

Rules face (docs/adr/**), so the review runs at CONTRACT_REVIEW_TIER.

@objectstack-fleet

Copy link
Copy Markdown
Contributor Author

domain:spec#3 · os-support-ai · session_01WGufzfgvGexKn9vLHsy89K

Contract review

Served-tier: CONTRACT_REVIEW_TIER
Head-sha: cce992bf300b96b0c2d4d3ec633e3f0315dbddbb
Local-runs: none
Scope: docs/adr/0012-notification-platform.md + packages/spec/src/system/push-config.zod.ts, packages/spec/src/api/push-relay.zod.ts, packages/spec/src/api/push-delivery.zod.ts, packages/spec/src/system/push-device.zod.ts, packages/spec/src/api/protocol.zod.ts#RegisterDeviceRequestSchema, packages/services/service-push/src/{push-plugin.ts,transports/apns.ts,transports/fcm.ts}, packages/services/service-messaging/src/{push-channel.ts,device-registry.ts,objects/user-device.object.ts}, docs/adr/0030-notification-platform-convergence.md, docs/adr/README.md, scripts/adr-index.mjs, scripts/pm/clause2-line.mjs, scripts/pm/fleet-write/dispatch.mjs, scripts/pm/check-half-states.mjs (signature rule), card #29817 (body + 4 comments), #29816 body, #29920 / #29041 / PR #29794 / PR #29886 state. All read at origin/main 1f6bc5aa; no scoped file moved between the merge-base 6a4737ab and 1f6bc5aa. cloud#3519 is not readable from this session; the card body binds (1C 2A 3A 4B 5A 6B 7B, 2026-11-03).

① Derived judgments

  • Net diff against main: one file, docs/adr/0012-notification-platform.md, +22/−9. No accept-set moves, no public surface moves, no generated artifact, no changeset, no ui/ path — verified on the file list and git diff origin/main...cce992bf. Right.
  • Every path#symbol anchor the hunks add resolves in the live tree: push-config.zod.ts#{PushVendorSchema,PushServiceConfigSchema,PushRelayTransportConfigSchema,PushDirectTransportConfigSchema}, push-relay.zod.ts#{PushRelayRequestSchema,PushRelayResponseSchema,PushRelayErrorResponseSchema,PushRelayPayloadSchema}, push-device.zod.ts#UserDeviceSchema, push-plugin.ts#PUSH_RELAY_NOT_SERVED_MESSAGE, push-channel.ts. Right.
  • docs/adr/README.md needs no edit: the index row's status summary is cut by adr-index.mjs before the amended text begins, and the status keyword stays Draft first; the Superseded by extraction reads an unchanged span. Right.

② Semver level

skip-changeset with no changeset is correct: the one path is under docs/adr/**, which publishes nothing from any released package (AGENTS.md Post-Task Checklist 3). Clause-②: no (docs only, no spec shape moves) is the right value — CLAUSE2_VALUES is ['yes','no'], so the order's n/a would read malformed; Metadata-protocol: none is right (no key, value or default an author can write moves). Check Changeset concluded success on this head.

③ Boundary flags

os-dev-report 6610622592, each deviation answered:

  1. Clause-② no not n/a: correct, see ②.
  2. No assignees / reviewer request at pr_create: a seat hand-over step, not a review matter.
  3. No signature line on the pr_create body: not a defect. fleet-write/dispatch.mjs (§ "post-stamped: every post's first line is a signature <lane>#<n> · <account> · <session>, and a post without one is refused #27113") carves pr_create out of the first-line rule by name ("a pull request body's first line is Fixes #n … the ruling is about posts, not pull request bodies"); the HTML-comment signed: trailer (PR_BODY_TRAILER_SHAPE) binds only a later seat rewrite through issue_patch. The body's line 3 is Fixes #29817, as that rule expects.
  4. Commit trailer: the commit carries Claude-Session: + Co-authored-by: Claude (noreply address), the model-free pair AGENTS.md requires. Resolved.
  5. cloud#3519 unreadable: same here; the card body binds, and the ADR cites the ruling as the card states it, attributing no decision to a single letter.
    Open questions: none declared, none found. Out-of-scope finding 1 (stale push-config.zod.ts / push-device.zod.ts comments that still say ADR-0012 §6 routes the domestic vendors to alternate channel plugins) is real: once this ADR lands those docblocks cite the ADR for a reading it no longer holds. The card forbids code changes, so it stays out of this PR — escalate: the seat should file the follow-up card (or fold it into the PushVendorSchema widening card when it exists) rather than leave it "noted, not filed".

④ Design judgment

Held against push-config.zod.ts, push-relay.zod.ts, push-plugin.ts, push-channel.ts, push-device.zod.ts, #29816 and the card's ruling:

  • Nothing unshipped is stated as shipped. The relay appears only as "decided, not yet served" (status line, §4 Status cell, §6.1 last bullet, each naming service-push: send through the hosted relay transport (push-relay.zod.ts wire, cloud binding credential) #29920); PushServicePlugin does throw PUSH_RELAY_NOT_SERVED_MESSAGE for any non-direct parse, {} included (push-plugin.ts init). Phase-2 vendors and E2EE are "decided and planned, no card yet", and spec: push relay transport — transport: relay | direct on PushServiceConfigSchema, and the server→relay wire shape (ruling cloud#3519) #29816's body confirms both were left out on purpose (PushVendorSchema stays apns | fcm; RegisterDeviceRequestSchema gets no pushKey; git grep pushKey over packages/spec/src is empty). The direct transport sending title/body today matches push-channel.ts.
  • Transport names, defaults, vendor enum and the direct narrowing match the schema exactly: transport: 'relay' | 'direct' discriminated union with unionFallback, {} → relay, a vendor block without transport → direct; relay block keys transport + optional https endpoint ("Absent: the production relay"), credentials refused by name; direct block apns/fcm optional with requiredOneOf, endpoint refused; PushVendorSchema = z.enum(['apns','fcm']); UserDeviceSchema.vendor: PushVendorSchema. The wire (token, vendor, payload, priority; ok/invalid/retry in request order; 401 UNAUTHENTICATED, 429 RATE_LIMIT_EXCEEDED/QUOTA_EXCEEDED), the ID-only payload (data + text; title/body/subtitle/message/content refused by name) and PushDeliveryDataSchema's three members all match.
  • Supersession: the 2026-11-03 Amended (… #29041) header is extended in place, not paralleled; Draft stays first and Superseded by ADR-0030 stays. The extension names the passages it amends and each named passage quotes its old wording.
  • Goals and Non-Goals are consistent with §1 TL;DR item 5, §6, §6.1, M3, risk 5 and the Decision list; no "plugin transports" / "alternate channel" / "channel plugins" wording survives outside quoted old text (grepped at head). The §6 UserDeviceSchema proposal block (wide vendor enum) and the §1 diagram's sys_user_device (APNs/FCM tokens…) are historical/phase-1-true and already covered by the Push channel: build the device registry routes (/notifications/devices) and the APNs + FCM transport that ADR-0012 marks "MUST ship M1" #29041 note and the status line's plugin-notification-push reading.
  • ADR-0030: its only push wording is sys_user_device (push) (L5 row) and push (sys_user_device + APNs/FCM) (item 3); the relay forwards to APNs/FCM, so neither contradicts, and "channels as plugins" there is about channels, which the ADR keeps. Unchanged, as the card requires.
  • cloud ADR-0008 is cited in the sanctioned cloud ADR-NNNN form.

Findings:

  • should-fix (supersession bookkeeping): the status line's enumeration of amended passages omits three it changed — TL;DR item 2 and the Goals "Four built-in channels" bullet (both had push (APNs + FCM) and neither quotes it) and the new Non-Goals "System push in an air-gapped deployment" bullet — so "Each amended passage quotes the wording it replaces" is not true of the first two. Add the three to the list and quote (APNs + FCM) once.
  • should-fix (precision): §6.1 last bullet says "every relay schema carries a [PLANNED — not yet served] marker"; PushRelayPrioritySchema and PushRelayResultStatusSchema (and the private binding-refusal enum) do not. Narrow to the object/request/response/error schemas, which do.
  • nit: §6.1 "its one key is endpoint" — the relay block also has transport; "its one key besides transport" is exact.
    No must-fix: no sentence states an unshipped thing as shipped, every schema claim matches the tree, and no wording conflicts with the ruling or ADR-0030.

CI on cce992bf (check-runs read at review, no local re-run): 22 of 24 non-skipped runs concluded success, among them Check Changeset, Check Documentation Links, Governed Surface Queue Guard, PR-body guards, Lint · docs & code guards, Lint · PM dispatch-gates, Lint · repo & tooling gates, Lint · ESLint, Type Check · workspace/source gates/debt ledger, Test UI; none failed. Still running at this reading: Test Core (1/8) and Type Check · consumer gates — the landing waits on those two turning green as well as on the maintainer's hand (Tier H).

Implemented-by: claude/project-thread-geqk0x
Reviewed-by: session_01WGufzfgvGexKn9vLHsy89K

VERDICT: PASS

…' old wording, name which relay schemas carry the PLANNED marker, and the relay block's transport key

Contract review round 1 on PR #29952: the status line now lists the TL;DR
item and the Goals "Four built-in channels" bullet among the amended
passages (both quote "push (APNs + FCM)") and names the three passages
that are new; §6.1 says the relay transport block and the seven relay
object schemas carry the [PLANNED — not yet served] marker and the two
enums do not; the relay block's keys are transport and endpoint.

Claude-Session: https://claude.ai/code/session_01WGufzj5io3Zzyb3E6VKCtj
Co-authored-by: Claude <noreply@anthropic.com>
@objectstack-fleet

Copy link
Copy Markdown
Contributor Author

domain:spec#3 · os-support-ai · session_01WGufzfgvGexKn9vLHsy89K

Contract review

Served-tier: CONTRACT_REVIEW_TIER
Head-sha: 62ac6d3c9c5d58ef2cd3ae974d765a1e3d33cbf9
Local-runs: none
Scope: docs/adr/0012-notification-platform.md + packages/spec/src/api/push-relay.zod.ts, packages/spec/src/system/push-config.zod.ts, packages/spec/src/system/push-device.zod.ts, PR #29952 state and comments, check-runs on the head. Delta re-review of the record on cce992bf (comment 6610988758): git diff cce992bf 62ac6d3c is one commit, one file, +5/−5; the net diff origin/main...62ac6d3c is the same one file, +22/−9. Read at origin/main 40fa4335; no scoped file moved on main between the prior read (1f6bc5aa) and now.

① Derived judgments

  • Should-fix 1 resolved: the status line now names TL;DR item 2 and the Goals "Four built-in channels" bullet among the amended passages and quotes their old wording once ("push (APNs + FCM)"), and each of the two passages now quotes it in place; the three passages that replace nothing (the Non-Goals air-gap bullet, §6.1, the Decision "Push transports" bullet) are listed as new. "Each amended passage quotes the wording it replaces" is now true of every hunk in the net diff: status line, TL;DR 2, Goals ×2, Non-Goals ×2, §4 push row, §6 domestic bullet, §6.1, M3, risk 5, Decision. Right.
  • Should-fix 2 resolved against the live tree: push-relay.zod.ts carries exactly seven [PLANNED — not yet served] describes, on PushRelayPayloadSchema, PushRelayDeliverySchema, PushRelayRequestSchema, PushRelayResultSchema, PushRelayResponseSchema, PushRelayErrorSchema, PushRelayErrorResponseSchema (the eighth match in the file is the module docblock); PushRelayPrioritySchema and PushRelayResultStatusSchema carry none; PushRelayTransportConfigSchema in push-config.zod.ts carries one. The §6.1 sentence now matches that count by name. Right.
  • Nit 3 resolved: "beside transport, its only key is endpoint" matches the relay block's two keys (transport literal with default, optional https endpoint); url/relayUrl are aliases, not keys. Right.
  • Nothing is stated as shipped that main does not ship: the new wording only re-enumerates which schemas carry the planned marker and reaffirms that PushServicePlugin refuses a relay block until service-push: send through the hosted relay transport (push-relay.zod.ts wire, cloud binding credential) #29920. No regression elsewhere in the prior record: anchors still resolve, PushVendorSchema stays apns | fcm, ADR-0030 and docs/adr/README.md untouched.
  • The commit stays in the one file; no accept-set, public surface, generated artifact, changeset or ui/ path. Trailers are the model-free Claude-Session: + Co-authored-by: Claude pair.

② Semver level

Unchanged from the prior record: docs-only under docs/adr/**, nothing published moves; skip-changeset label present, no changeset. Check Changeset is skipped on this head (path-filtered; it was success on cce992bf) — no verdict to carry, nothing to fix.

③ Boundary flags

No new deviation declared on the PR since comment 6610988758 (no later comments). Escalation carried forward unchanged: packages/spec/src/system/push-config.zod.ts (the PushVendorSchema docblock, "ADR-0012 §6 plugs the domestic Android vendors in as alternate channel implementations") and packages/spec/src/system/push-device.zod.ts (header and the "Narrowed from ADR-0012 §6" paragraph) still cite §6 for a reading this ADR no longer holds. The card forbids code changes; the seat files the follow-up card (or folds it into the PushVendorSchema widening card when it exists).

④ Design judgment

The delta tightens three sentences and changes no decision, rule or default; the amendment's supersession bookkeeping is now complete and the schema census is exact. One remaining nit, not blocking: §6.1 says "the two enums there ... carry none" — a third, non-exported enum (PushRelayBindingRefusalSchema) also carries none; "the two exported enums" would be exact. No must-fix, no should-fix.

CI on 62ac6d3c (check-runs read at review, no local run): 38 runs, 18 success, 16 skipped, 0 failed, 4 still running at this reading: Test Core (1/8), Lint · docs & code guards, Type Check · source gates, Lint · scoped · tooling self-tests. Type Check · consumer gates concluded success on this head, so the 20-minute checkout timeout seen on cce992bf did not recur. Check Documentation Links, Governed Surface Queue Guard, PR-body guards, Lint · ESLint, Lint · PM dispatch-gates, Lint · repo & tooling gates, Type Check · workspace, Type Check · debt ledger, Test UI, Dogfood Regression Gate all green. The landing waits on the four running jobs and the maintainer's hand (Tier H; mergeable_state: blocked).

Implemented-by: claude/project-thread-geqk0x
Reviewed-by: session_01WGufzfgvGexKn9vLHsy89K

VERDICT: PASS

@objectstack-fleet

Copy link
Copy Markdown
Contributor Author

domain:spec#3 · os-support-ai · session_01WGufzfgvGexKn9vLHsy89K

维护者速读

席位意见:卡片要求的六处都已落实。fable 契约复审两轮都是 PASS(首轮 3 处措辞精度问题,已在本 head 修正),check-runs 全绿。同意合并。另外,spec 里两处 docblock 仍按旧的 §6 读法引用本 ADR,已另立卡 #29960,等本 PR 合并后再改。

席位核对 — ACCEPT · domain:spec#3 · head 62ac6d3c9c5d58ef2cd3ae974d765a1e3d33cbf9

@objectstack-fleet
objectstack-fleet Bot marked this pull request as ready for review November 3, 2026 18:10
@os-zhuang
os-zhuang added this pull request to the merge queue Nov 3, 2026
Merged via the queue into main with commit 6f7df53 Nov 3, 2026
64 of 66 checks passed
@os-zhuang
os-zhuang deleted the claude/project-thread-geqk0x branch November 3, 2026 22:05
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

documentation Improvements or additions to documentation size/s skip-changeset PR has no user-facing published change; bypasses the changeset gate

Projects

None yet

Development

Successfully merging this pull request may close these issues.

ADR-0012 amendment: the push channel's relay transport, and vendor as a token type rather than a plugin boundary (ruling cloud#3519)

2 participants