Skip to content

docs(spec): push vendor docblocks read ADR-0012 §6.1, not alternate channel plugins - #30631

Merged
objectstack-fleet[bot] merged 7 commits into
mainfrom
claude/issue-29960-push-vendor-docblocks
Nov 5, 2026
Merged

objectstack-fleet[bot] merged 7 commits into
mainfrom
claude/issue-29960-push-vendor-docblocks

Conversation

@objectstack-fleet

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

Copy link
Copy Markdown
Contributor

domain:spec#3 · os-sales · session_01L6bhgTqqeHSwZYJmApbDCa

Requested by Sales · project thread

Fixes #29960

Clause-②: no
Metadata-protocol: none

维护者速读

  • 改了什么:推送配置与推送设备的说明注释(含生成的参考文档页)以及推送服务文档页的一句话,改为按 ADR-0012 §6.1 的新口径描述国产安卓厂商;只改注释,不动任何 schema、导出或默认值。
  • 为什么改:ADR 修订后,厂商只是发送方按其路由的令牌类型(今天是直连通道,托管中继上线后由中继路由),二期由中继接入 HMS / 荣耀 / 小米 / OPPO / vivo;旧注释仍说它们是“另一套通道插件”,会把后续扩展厂商的人引向错误架构。
  • 风险与回滚:极低;纯注释与文档文字,PushVendorSchema 仍为 apns | fcm,回滚即还原本 PR 的提交。
  • 先看:packages/spec/src/system/push-config.zod.ts(PushVendorSchema 注释那段)、content/docs/kernel/runtime-services/push-service.mdx(第 86-91 行)、packages/spec/src/system/push-device.zod.ts。
  • 两分钟验证:git grep -nE "alternate channel|separate channel" -- packages/spec/src content/docs/references content/docs/kernel 预期无输出。
  • 需要你做的:无,席位落地。

What changes

The docblocks that read ADR-0012 §6 as "the domestic Android vendors plug in as alternate channel plugins / implementations" now carry the reading ADR-0012 §6 and §6.1 (as amended by PR #29952) decide: vendor is the token type the sender routes on (the direct transport today, where PushService.send picks transports[message.vendor]; the hosted relay, the default, once it is served), core ships apns | fcm, and phase 2 adds HMS / Honor / Xiaomi / OPPO / vivo behind the relay, with no per-vendor plugin and no per-vendor channel.

  • packages/spec/src/system/push-config.zod.ts: the module docblock's "Out of scope here" paragraph, and the PushVendorSchema docblock (still "closed on purpose"; a new value is still a spec change made together with its transport, now named as the relay's routing for that vendor).
  • packages/spec/src/system/push-device.zod.ts: the UserDeviceSchema docblock's "Narrowed from" paragraph.
  • packages/spec/src/api/register-device-request.test.ts: the file-header comment.
  • content/docs/references/system/push-config.mdx: regenerated by check:generated --fix (the only stale artifact).
  • content/docs/kernel/runtime-services/push-service.mdx: the hand-written push service page said the other Android vendors "plug in as separate channel implementations". It now says only apns and fcm ship today, and the other Android vendors (HMS / Honor / Xiaomi / OPPO / vivo) are planned for a later phase behind the hosted relay, as further values of vendor, the token type the sender routes on (this service's transports today, the relay once it is served), not as plugins or channels of their own. The page's own "The hosted relay: not served yet" section, which the sentence points to, is unchanged.
  • .changeset/29960-push-vendor-docblocks.md: @objectstack/spec patch, because the TSDoc of a src/**/*.zod.ts file ships in the published declarations.

Comment text only: no schema, .describe(), export, default or test assertion moves. Citations of §6 for the device registry and its lifecycle stay as they are (push-device.zod.ts line 12, push-device.test.ts line 3, protocol.zod.ts, notification-service.ts, platform-capabilities.ts); only the alternate-channel reading moves, and where it moves it cites §6.1.

Tests

  • pnpm --filter @objectstack/spec exec vitest run --maxWorkers=2 src/system/push-config.test.ts src/system/push-device.test.ts src/api/register-device-request.test.ts src/api/push-relay.test.ts: Test Files 4 passed (4), Tests 117 passed (117).
  • pnpm --filter @objectstack/spec typecheck: exit 0.
  • pnpm --filter @objectstack/spec check:generated: all 14 generated artifacts up to date (after --fix regenerated check:docs).
  • eslint --no-inline-config on the three changed .ts files: exit 0.

Acceptance notes

  • The push service page (content/docs/kernel/runtime-services/push-service.mdx) carried the same retired reading. The seat widened the claim to cover it (claim amendment on spec(system): push-config / push-device docblocks still cite ADR-0012 §6 for "domestic vendors are alternate channel plugins", the reading #29817 retires #29960), and it is fixed in this PR. A re-grep of content/docs, packages/spec/src, packages/services/service-push, packages/services/service-messaging/src, skills and docs for "separate channel" / "alternate channel" / "plug in as" finds no other ADR-0012 §6 vendor reading outside the ADR's own quoted amendment text.
  • push-device.zod.ts line 12, "(ADR-0012 §6, narrowed to what core ships)", cites §6 for the stored device shape (the registry), which §6 still is, so it is unchanged.
  • Contract review (PASS, no must-fix) raised two should-fixes, both landed here in 86e42da79. S1: the docblocks named only the relay as what routes on vendor; on main the relay is not served and the direct transport routes on it today (packages/services/service-push/src/push-service.ts line 92, transports[message.vendor]; packages/services/service-messaging/src/push-channel.ts line 147 filters devices by the configured vendors), so both docblocks, the docs page and the changeset now say "the token type the sender routes on", naming the direct transport today and the hosted relay (the default) once served. S2: the vendor list uses the ADR's five-way separators, HMS / Honor / Xiaomi / OPPO / vivo, everywhere it is written.
  • No ablation: no tested behaviour changed.

Verification

All readings on head 52082426b (objectstack-ai/objectstack; origin/main merged in at 88640a098).

Derived gate families: 103 derived, 103 run, 0 NOT-MEASURED, 0 UNRUN
  • node scripts/pm/dispatch-gates.mjs --commands --repo objectstack-ai/objectstack: 103 commands, every one run after pnpm build under the verify lock (VERDICT command-exit 0), with its exit code captured before any pipe; all 103 exit 0 on one pass.
  • --ran reconciliation: "103 derived, 103 run, 0 NOT-MEASURED (a DERIVED zero — all 103 recorded an exit code and none of them is 3)".
  • Docs gates for the page: check:doc-authoring, check:doc-anchors, check:docs-redirects, check:runtime-services-index: exit 0. check:type-check-debt: exit 0, "0 raw tsc error(s) total, none above its recorded number". check-changeset-no-major --base origin/main: exit 0. check:nul-bytes: exit 0.
Package gates
  • pnpm --filter @objectstack/spec build, targeted vitest and pnpm --filter @objectstack/spec typecheck (verify lock): Test Files 4 passed (4), Tests 117 passed (117); VERDICT command-exit 0.
  • pnpm --filter @objectstack/spec check:generated: "All 14 generated artifacts are up to date" (--fix regenerated push-config.mdx in 52082426b).
  • eslint --no-inline-config on the three changed .ts files: exit 0.

CI-carried families (the required contexts) are read from this PR's check-runs.


Generated by Claude Code

🤖 Generated with Claude Code

https://claude.ai/code/session_01L6bhgTqqeHSwZYJmApbDCa

…hannel plugins

The push-config and push-device docblocks and the register-device-request
test header still cited ADR-0012 §6 for "domestic vendors are alternate
channel plugins". The amended ADR (§6.1) makes vendor a token type the
hosted relay routes on, with the domestic vendors arriving behind the relay
in phase 2. Comment text only.

Claude-Session: https://claude.ai/code/session_01L6bhgTqqeHSwZYJmApbDCa
Co-authored-by: Claude <noreply@anthropic.com>
@github-actions github-actions Bot added size/s documentation Improvements or additions to documentation protocol:system tests tooling Fix lands in tooling, not a product package — enters pm:queue only with Unblocks: or a named surface labels Nov 5, 2026
@github-actions

github-actions Bot commented Nov 5, 2026 •

Copy link
Copy Markdown
Contributor

📓 Docs Drift Check

⚠️ 2 changed file(s) yielded no anchor (packages/spec/src/system/push-config.zod.ts, packages/spec/src/system/push-device.zod.ts), so the pages documenting them are NOT COVERED by this run — this is not a clean bill of health for those files. Nothing else in this diff resolved to a documentable surface (no symbol, route or SDK anchor derived from 1 changed package(s)).

What this run could not see
  • 2 changed file(s) yielded no anchor (packages/spec/src/system/push-config.zod.ts, packages/spec/src/system/push-device.zod.ts) — pages documenting those are invisible to this run
  • a page that states a rule by its inputs shares no identifier with the emitter that implements the rule, so an emitter-only diff cannot list it — not on this run and not on any run. Measured on fix(driver-sql): emit varchar(maxLength) for a text field a declared index keys on #11430: content/docs/protocol/objectql/types.mdx documents the text-family column mapping by the ObjectQL type names it maps FROM (text / textarea / html) while the diff changed createColumn; it went unlisted, and it was the page that diff falsified, in four places. No shared token exists to detect this on, so a rule your change carries has to be re-read by hand in the pages that restate it.
  • a key NAME is not a key, so the hand re-read the line above prescribes can land on the wrong schema. The same spelling is authorable on one governed type and a [REMOVED] tombstone on another for each of active, aria, joins, objects, template, tools and version (censused on [finding] tools is a key on BOTH AgentSchema (tombstoned, dead) and SkillSchema (live, cloud-attested), so a name-based search attributes skill examples to the agent key — it produced a false stop-the-line alarm on PR #19059 #19093 over the liveness ledger's governed types, top-level keys); nothing in a search result distinguishes the two, so a grep hit on a LIVE example reads as evidence about the DEAD key. Measured on fix(spec): the agent.tools liveness row says dead — it claimed live on a key the schema tombstoned #19059: content/docs/ai/agents.mdx was reported as contradicting the agent.tools tombstone over its tools: example at :161, which is inside the defineSkill({ block opened at :155 — the page was already correct. Settle ownership by PARSING the value against both schemas, never by the name: that literal PASSES SkillSchema, and as an AgentSchema it FAILS at tools with the tombstone prescription. ⛔ These names are not the whole class — a key retired through a .strict() guidance map leaves no tombstone in the walked shape and none of them here (tool.category, live as AIToolDefinition.category).

Coarse fallback — 170 page(s) merely mention a changed package (the pre-#9192 predicate, kept for the deliberately-wide backstop): node scripts/docs-audit/affected-docs.mjs --json 14c67d446e577926460f3ed893e44cc32cbecb12 → packageMentionDocs.

…sh-vendor-docblocks

Claude-Session: https://claude.ai/code/session_01L6bhgTqqeHSwZYJmApbDCa
Co-authored-by: Claude <noreply@anthropic.com>
… token types

The page still said the other Android vendors plug in as separate channel
implementations, the ADR-0012 §6 reading the amendment retired. Core ships
apns and fcm; the domestic vendors are planned behind the hosted relay as
further vendor values, not as plugins or channels.

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

Copy link
Copy Markdown
Contributor Author

domain:spec#3 · os-sales · session_01L6bhgTqqeHSwZYJmApbDCa

Contract-review-claim: session_01L6bhgTqqeHSwZYJmApbDCa · head 28f2cc1 · 2026-11-05T19:59Z

Full review at CONTRACT_REVIEW_TIER (published schema face: system/push-config.zod.ts, system/push-device.zod.ts; comment text only), run by an isolated at-tier subagent of this seat.


Generated by Claude Code

@objectstack-fleet

Copy link
Copy Markdown
Contributor Author

domain:spec#3 · os-sales · session_01L6bhgTqqeHSwZYJmApbDCa

Contract review

Served-tier: CONTRACT_REVIEW_TIER
Head-sha: 28f2cc1f737a2f1900429c761135023f4045fdba
Local-runs: none
Scope: packages/spec/src/system/push-config.zod.ts, packages/spec/src/system/push-device.zod.ts + docs/adr/0012-notification-platform.md (§6 amended domestic-vendor bullet, §6.1), packages/spec/src/api/push-relay.zod.ts (PushRelayDeliverySchema.vendor), packages/spec/src/api/protocol.zod.ts (RegisterDeviceRequestSchema.vendor), packages/services/service-push/src/push-plugin.ts, packages/services/service-push/src/push-service.ts, packages/services/service-messaging/src/push-channel.ts, packages/services/service-messaging/src/objects/user-device.object.ts (sys_user_device)

Inputs: card #29960 (body; comments 6611521127, 6665182964, 6666461990, 6666548945, 6667839156), PR #30631 (body, file list, net diff against merge-base dc5181124), and the head's check-runs. The PR body and the dev reports were read as claims; every judgment below is against the tree. origin/main has moved 12 commits past the merge-base; git diff dc5181124 origin/main on the six PR paths, ADR-0012, push-relay.zod.ts, protocol.zod.ts, service-push and the messaging push files is empty, so the head's reading is main's reading.

Gate verdicts, read at 2026-11-05T20:09Z after every run had completed: 69 check-runs on the head, 59 success, 10 skipped, 0 failure, 0 in progress. The ten required contexts: Lint & Repo Gates, TypeScript Type Check (carries check:docs, check:api-surface, check:authorable-surface), Test Core, Build Core, Governed Surface Queue Guard, Type Check UI, Test UI, Build & E2E UI, Live E2E UI all success; Lint UI skipped (no ui/ path in the diff). The skipped ten are path-filtered or opt-in jobs (Auto Label, Check PR Size, Temporal Conformance, UI Published Artifacts, Dogfood Regression Gate, Dogfood Verify CLI, Schedule gate, Packed-tarball smoke, 18.x upgrade harness, Lint UI).

① Derived judgments

  • No shape, .describe(), default or export moves — right. Every +/- line in the two hits hunks is a * docblock line. At the head, PushVendorSchema is still z.enum(['apns', 'fcm']) with its .describe() byte-identical to main, UserDeviceSchema.vendor is still PushVendorSchema.describe('Push vendor the token belongs to: …'), and no export, strictObject guidance map, alias or default in either file moved. The accept set of PushServiceConfigSchema, PushDirectTransportConfigSchema, PushRelayTransportConfigSchema and UserDeviceSchema is unchanged; check:api-surface and check:authorable-surface are green on the head inside TypeScript Type Check.
  • The published surface does move, in text only — right, and the diff carries its one derived artifact. The module docblock of push-config.zod.ts is the source of content/docs/references/system/push-config.mdx (build-docs.ts), and the regenerated page's hunk is the "Out of scope here" paragraph and nothing else; check:docs is green. The two docblocks also ship in dist/**/*.d.ts (packages/spec/package.json types entries; the tsup DTS pass), so the published declarations' documentation text changes — the reason ② is a patch, not "no changeset".
  • Consumers of PushVendorSchema are untouched and need no change — right. Its consumers on main are UserDeviceSchema.vendor (push-device.zod.ts:58), RegisterDeviceRequestSchema.vendor (protocol.zod.ts:3711, "apns or fcm; absent, derived from platform"), PushRelayDeliverySchema.vendor (push-relay.zod.ts:93, "apns or fcm"), the PushVendor type in service-push (the transports record keyed by vendor in push-plugin.ts and push-service.ts), and sys_user_device.vendor (Field.select of apns / fcm, user-device.object.ts:77). None carries the retired "alternate channel" reading, so none is owed an edit; the test-file header is the only other carrier and it moves in the same PR (lane seat's accept).
  • Nothing is left behind. A grep of the head tree for alternate channel, separate channel, plug in as, channel plugin and plugin-notification-push across packages/spec/src, content/docs, packages/services, skills and docs (ADR-0012 excluded) finds only unrelated hits: ADR-0013 / ADR-0022 (messaging channel plugins, a different subject), email-service.ts / sms-service.ts (transports that plug in, a different subject), a design/ note that predates the amendment, and the new test header, which carries the amended reading.
  • The Docs Drift Check comment (6666462165) reported the two .zod.ts files as yielding no anchor. That is the advisory backstop saying the hand re-read is owed; this record is that re-read (the push-config.mdx reference page is in the diff; push-service.mdx was re-read, see ④).

② Semver level

@objectstack/spec patch — right. The diff publishes documentation text in the declarations and the generated reference page; no runtime, no schema shape, no accept-set, no export and no default moves, so nothing above patch is owed and nothing below it would carry the TSDoc change into CHANGELOG.md. Clause-②: no — right: no narrowing, no capability taken from a consumer, no refusal made real. Metadata-protocol: none — right: no authorable key, guidance map or alias on PushServiceConfigSchema or its blocks moves (check:authorable-surface green). The changeset body states exactly what the diff does ("Comment text only … PushVendorSchema stays apns | fcm"), cites the ADR by section and carries no tracker number or model identifier. check-changeset-no-major and the changeset gates are inside the green Lint & Repo Gates.

③ Boundary flags

  • push-device.zod.ts :12 left unchanged ("the stored device shape of the push device registry (ADR-0012 §6, narrowed to what core ships)") — answered: right to leave. §6 is still "Push channel — device lifecycle", the device registry and its invalidation loop; its amended text names UserDeviceSchema as "the shipped device shape … narrowed to apns | fcm and ios | android | web". The citation is about the registry shape, not the retired plugin reading. The same holds for the other §6 citations the PR leaves in place and I read: push-device.test.ts :3, protocol.zod.ts (the registration header), push-plugin.ts ("ADR-0012 §4, §6"), user-device.object.ts ("ADR-0012 §6, §11"). The dispatch's assumption that :12 carried the plugin reading was wrong; the dev's deviation is the correct call.
  • The merge of main (af8fca1) — answered: accepted. It is a merge, not a rebase, of a branch the dev alone pushed; the net diff against merge-base dc5181124 is the six files and nothing from main; no path in scope moved on main since. The head's check-runs are on the merged tree, and the queue leg re-runs the required set against main at landing.
  • The widened surface (push-service.mdx) — answered: in order. The claim amendment 6666548945 records the widening on the card before the round-1 push, it is the same defect family on a hand-written page the docblocks do not feed, the path is not governed, and the sentence lands in this PR; it is the lane seat's accept at TIER_DEFAULT and is not reviewed here. Read as an observation in ④.
  • open_questions in both dev reports: [] — nothing to answer or escalate.
  • The round-0 deviation "did not merge origin/main before opening the PR" is superseded by the round-1 merge; the round-1 deviation "the docs sentence cites no ADR" is a page-voice choice on a file outside the contract face — noted, not a defect.
  • Lane seat's remainder (test-file header, regenerated reference page, push-service.mdx, changeset): ACCEPT at TIER_DEFAULT as a standalone PR of those files. The test header moves no assertion (comment lines only); the reference page is what build-docs.ts emits (check:docs green); the changeset is judged in ②.

④ Design judgment

Every new sentence in the two hits files was tried against ADR-0012 §6 (the amended domestic-vendor bullet) and §6.1 on main, and against the code on main that routes on vendor.

push-config.zod.ts, module docblock, "Out of scope here" paragraph. "Out of scope here: the domestic Android vendors (HMS / Honor, Xiaomi, OPPO, vivo). ADR-0012 §6.1 has the hosted relay front them in phase 2, as further values of vendor, the token type the relay routes on; there is no per-vendor plugin and no per-vendor channel." — TRUE. §6.1: "phase 2 adds the domestic vendors (HMS / Honor / Xiaomi / OPPO / vivo) behind the same relay and widens PushVendorSchema with them. There is no per-vendor plugin and no per-vendor channel." §6: "In phase 2 the relay fronts HMS / Honor / Xiaomi / OPPO / vivo as well". It describes phase 2, not a served relay; the same docblock's "What is served today" paragraph and the relay block's [PLANNED — not yet served] .describe() still say the relay is not served (held: PushServicePlugin.init throws PUSH_RELAY_NOT_SERVED_MESSAGE on any non-direct transport). Complete for its purpose, which is to say what this file does not configure.

push-config.zod.ts, PushVendorSchema docblock. "Push vendors core delivers through: apns (Apple) and fcm (Firebase)." (unchanged) — TRUE: service-push builds ApnsPushTransport and FcmPushTransport from the direct block and nothing else. "Closed on purpose. vendor is the token type the hosted relay routes on (ADR-0012 §6.1): phase 1 routes these two, and phase 2 adds the domestic Android vendors (HMS / Honor, Xiaomi, OPPO, vivo) behind the same relay, with no per-vendor plugin and no per-vendor channel." — TRUE against §6.1's third bullet, which it restates in the ADR's own tense ("Phase 1 routes apns and fcm; phase 2 adds …"); "phase 1 routes" and "phase 2 adds" are parallel plan statements, and no sentence says the relay delivers today. "So a new value here is a spec change made together with its transport: the relay's routing for that vendor." — TRUE and load-bearing: §6 cites this docblock for exactly that phrase ("its docblock says a new value is a spec change made together with its transport"), and the PR keeps it verbatim and names the transport as the relay's routing, matching "PushVendorSchema widens to those values in the same change that routes them". The .describe() is unchanged and still true.

push-device.zod.ts, UserDeviceSchema docblock, "Narrowed from" paragraph. "Narrowed from the ADR-0012 §6 sketch: vendor is the core pair (apns | fcm; it is the token type the hosted relay routes on, and the domestic Android vendors join it in phase 2 behind the same relay, ADR-0012 §6.1) and platform is the registration's own three (ios | android | web)." — TRUE. The §6 sketch enumerates seven vendors and five platforms; the shipped shape narrows to the pair and the three (PUSH_DEVICE_PLATFORMS, shared with RegisterDeviceRequestSchema), as §6's amendment records. "join it in phase 2 behind the same relay" is §6.1. Nothing in the paragraph says the relay is served; :12 above it still cites §6 for the registry, correctly (③).

Sentences the PR leaves in place, re-read for truth now: the module docblock's "it refuses the relay transport, not yet served" (held by push-plugin.ts), "What is served today" (held), the direct-transport paragraph citing §4 (held), and push-device.zod.ts's "@objectstack/service-push holds only the vendor transports and never touches the table" (held by user-device.object.ts's header and push-plugin.ts). None is now false.

Observation on the lane seat's push-service.mdx sentence (not a driver of the verdict): "Only apns and fcm ship today. The other Android vendors (HMS / Honor, Xiaomi, OPPO, vivo) are planned for a later phase behind the hosted relay (below), as further values of vendor, the token type the relay routes on, and not as plugins or channels of their own." — true against §6/§6.1, and "(below)" lands on the page's own "The hosted relay: not served yet" section, so the page does not describe the relay as served.

Findings. No must-fix. Two should-fix, neither a falsehood:

  • S1 (should-fix, both hits files): "the token type the hosted relay routes on" names only the relay. On main the relay is not served and the direct transport is what routes on vendor today — PushService.send picks transports[message.vendor] (push-service.ts :92) and the push channel filters the recipient's devices by the vendors the service has transports for (push-channel.ts :147). ADR §6 puts it as "so the sender knows where the delivery goes". The standalone PushVendorSchema TSDoc (what an IDE hover shows, with no module docblock beside it) therefore gives the relay-only reading. Proposed: "the token type the sender routes on — the direct transport today, the hosted relay (the default) once served (ADR-0012 §6.1)". Carrier: this PR, one clause in each file; the regenerated page follows.
  • S2 (should-fix, both hits files, minor): the vendor list's separators. "HMS / Honor, Xiaomi, OPPO, vivo" reads as four vendors with HMS and Honor as one; the ADR's amended lists write "HMS / Honor / Xiaomi / OPPO / vivo" five ways, and its §6 sketch enum has hms and no honor, so whether Honor is its own token type is left to the phase-2 widening. All five names are present, so nothing is false. Proposed: the ADR's five-way list. Carrier: this PR (punctuation only), together with S1.

Implemented-by: claude/issue-29960-push-vendor-docblocks
Reviewed-by: session_01L6bhgTqqeHSwZYJmApbDCa

VERDICT: PASS


Generated by Claude Code

…sh-vendor-docblocks

Claude-Session: https://claude.ai/code/session_01L6bhgTqqeHSwZYJmApbDCa
Co-authored-by: Claude <noreply@anthropic.com>
…endor list

Contract-review should-fixes: the direct transport routes on vendor today
(PushService.send picks transports[message.vendor]); the hosted relay, the
default, does once served. The domestic vendor list uses the ADR's
separators, HMS / Honor / Xiaomi / OPPO / vivo.

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

Copy link
Copy Markdown
Contributor Author

domain:spec#3 · os-sales · session_01L6bhgTqqeHSwZYJmApbDCa

Contract review — delta

Served-tier: CONTRACT_REVIEW_TIER
Head-sha: 52082426b060c2f3420a3a01fb69baa60080b842
Local-runs: none
Scope: packages/spec/src/system/push-config.zod.ts, packages/spec/src/system/push-device.zod.ts + docs/adr/0012-notification-platform.md (§6 amended bullet, §6.1), packages/services/service-push/src/push-service.ts, packages/services/service-messaging/src/push-channel.ts, packages/spec/src/system/push-config.zod.ts (PushServiceConfigSchema default), packages/services/service-push/src/push-plugin.ts

Delta since the PASS record 6668108542 on 28f2cc1f7: three commits on the branch, read as the dev's round-2 report 6669123669 claims them and judged on the tree.

  • 88640a098, merge of origin/main (14c67d446) — clean, no hand-resolved hunk. git diff-tree --cc on the merge lists no path, so no file differs from both parents; the merge against its branch parent touches none of the six PR files; the merge against its main parent is exactly the six-file branch delta (+34 / -13). Nothing from main entered a PR file by hand.
  • 86e42da79, S1 + S2 — the only content commit; judged in ④ below.
  • 52082426b, regenerated push-config.mdx — regeneration only. One file, +4 / -4, the "Out of scope here" paragraph; its text is byte-equal to the module docblock paragraph at the head (lines 85-88), which is what build-docs.ts emits from it. check:docs is green on the head inside TypeScript Type Check.

Net diff against the new merge-base 14c67d446: the same six files (+38 / -13). Every +/- line in the two hits files is still a * docblock line; PushVendorSchema is still z.enum(['apns', 'fcm']) with its .describe() byte-identical to main; no export, default, alias or guidance map moved. origin/main has moved past the merge-base (b8929f486 at this read) on packages/spec/src/system/ files the PR does not touch (email-template.zod.ts, i18n-resolver.ts, platform-locales.ts, validation-message.ts) and not on ADR-0012, push-relay.zod.ts, protocol.zod.ts, service-push or the messaging push files, so the readings below are main's.

Gate verdicts, read at 2026-11-05T20:58Z after every run had completed (the last, Check Changeset, was in progress at 20:54Z and complete at 20:58Z): 69 check-runs on the head, 59 success, 10 skipped, 0 failure, 0 in progress. The ten required contexts: Lint & Repo Gates, TypeScript Type Check, Test Core, Build Core, Governed Surface Queue Guard, Type Check UI, Test UI, Build & E2E UI, Live E2E UI all success; Lint UI skipped (no ui/ path in the diff). The other skips are the same path-filtered or opt-in jobs as on the PASS head.

① ② ③ — unchanged

①, ② and ③ of record 6668108542 stand: comment text only, @objectstack/spec patch (TSDoc ships in the declarations; the changeset's wording change is text, not level), Clause-②: no, Metadata-protocol: none. No new dev flag and open_questions: []. The dev's deviation "S1 applied beyond the two docblocks" (the module paragraph, push-service.mdx, the changeset) is in order: the same clause in the same family, each in a file already in the PR; the module paragraph is in a hits file and is judged below, the other two are the lane seat's ACCEPT at TIER_DEFAULT, noted as observations.

④ Design judgment — the new sentences

push-config.zod.ts, PushVendorSchema docblock. "vendor is the token type the sender routes on: the direct transport today, the hosted relay (the default) once served (ADR-0012 §6.1)." — TRUE and now complete. "The sender routes on" is §6's own phrasing ("so the sender knows where the delivery goes") and §6.1's ("tells the sender, and through it the relay"). "The direct transport today" holds against the code on main: under transport: 'direct' the push service holds one transport per configured vendor and PushService.send picks this.opts.transports[message.vendor] (push-service.ts :92), answering vendor_not_configured for a vendor with no block; upstream, the push channel addresses only the recipient's devices whose vendor the service lists (push-channel.ts :147, devices.filter((d) => vendors.includes(d.vendor))). So on main the routing on vendor is the push service's choice among the direct transport's vendor blocks, which is what "the direct transport" names in this schema's own vocabulary (PushDirectTransportConfigSchema: "one block per vendor"). "The hosted relay (the default) once served" holds: PushServiceConfigSchema reads {} as the relay, and PushServicePlugin.init throws PUSH_RELAY_NOT_SERVED_MESSAGE for any non-direct transport until the sender ships. "Phase 1 routes these two, and phase 2 adds the domestic Android vendors (HMS / Honor / Xiaomi / OPPO / vivo) behind the relay, with no per-vendor plugin and no per-vendor channel." — TRUE: §6.1's third bullet in its own tense and its own five-way list (S2). "So a new value here is a spec change made together with its transport: the relay's routing for that vendor." — unchanged from the PASS head, still the clause §6 cites. The .describe() is unchanged.

push-config.zod.ts, module docblock, "Out of scope here". "(HMS / Honor / Xiaomi / OPPO / vivo) … as further values of vendor, the token type the sender routes on; there is no per-vendor plugin and no per-vendor channel." — TRUE: the ADR's list and the ADR's subject; consistent with the PushVendorSchema docblock below it and the "What is served today" paragraph above it.

push-device.zod.ts, UserDeviceSchema docblock, "Narrowed from". "vendor is the core pair (apns | fcm; it is the token type the sender routes on: the direct transport today, the hosted relay (the default) once served, and the domestic Android vendors (HMS / Honor / Xiaomi / OPPO / vivo) join it in phase 2 behind the relay, ADR-0012 §6.1) and platform is the registration's own three (ios | android | web)." — TRUE on every clause, by the same readings; the §6 sketch's seven vendors and five platforms are still narrowed to the pair and the three. Nothing says the relay is served; :12 still cites §6 for the registry, correctly.

Sentences left in place were re-read; none is now false.

Observations (lane seat's ACCEPT, not drivers of the verdict). push-service.mdx :88-92: "the token type the sender routes on (this service's transports today, the relay once it is served)" — true, and "this service's transports" is the exact mechanism (PushService.send). The changeset body: "the token type the sender routes on (the direct transport today, the hosted relay, the default, once served) … phase 2 adds HMS / Honor / Xiaomi / OPPO / vivo behind the relay" — true and matches the docblocks it describes; level unchanged.

Findings. None. S1 and S2 of record 6668108542 are landed as proposed; no must-fix, no should-fix.

Implemented-by: claude/issue-29960-push-vendor-docblocks
Reviewed-by: session_01L6bhgTqqeHSwZYJmApbDCa

VERDICT: PASS


Generated by Claude Code

@objectstack-fleet

Copy link
Copy Markdown
Contributor Author

domain:spec#3 · os-sales · session_01L6bhgTqqeHSwZYJmApbDCa

维护者速读

  • 改了什么:推送配置与推送设备的说明注释(含生成的参考文档页)以及推送服务文档页的一句话,改为按 ADR-0012 §6.1 的新口径描述国产安卓厂商;只改注释,不动任何 schema、导出或默认值。
  • 为什么改:ADR 修订后,厂商只是发送方按其路由的令牌类型(今天是直连通道,托管中继上线后由中继路由),二期由中继接入 HMS / 荣耀 / 小米 / OPPO / vivo;旧注释仍说它们是“另一套通道插件”,会把后续扩展厂商的人引向错误架构。
  • 风险与回滚:极低;纯注释与文档文字,PushVendorSchema 仍为 apns | fcm,回滚即还原本 PR 的提交。
  • 先看:packages/spec/src/system/push-config.zod.ts(PushVendorSchema 注释那段)、content/docs/kernel/runtime-services/push-service.mdx(第 86-91 行)、packages/spec/src/system/push-device.zod.ts。
  • 两分钟验证:git grep -nE "alternate channel|separate channel" -- packages/spec/src content/docs/references content/docs/kernel 预期无输出。
  • 需要你做的:无,席位落地。

席位意见:契约复审两轮都 PASS。第一轮提了两处措辞,第二轮修补都改了:一是 vendor 是「发送方按它路由」的令牌类型,今天走直连通道,中继上线后走中继,不只是中继;二是五家厂商按 ADR 的写法分隔。增量复审无遗留。推送服务文档页的同一句旧说法,席位已把认领范围扩进本 PR 一起修了。

席位核对 — ACCEPT · domain:spec#3 · head 52082426b060c2f3420a3a01fb69baa60080b842

  • Shape: 6 files (+38 −13), draft, base main, first card reference Fixes #29960.

    • Hand-written changes:
      • docblock text in packages/spec/src/system/push-config.zod.ts (the module's "Out of scope here" paragraph and PushVendorSchema) and in packages/spec/src/system/push-device.zod.ts (UserDeviceSchema);
      • the file-header comment of packages/spec/src/api/register-device-request.test.ts;
      • the sentence at content/docs/kernel/runtime-services/push-service.mdx :88-92;
      • the changeset.
    • Regenerated: content/docs/references/system/push-config.mdx.
    • No schema, .describe(), export or default change. No content/docs/releases/ edit.
  • Premise measured: on origin/main b8929f48, the retired ADR-0012 §6 reading still stands in four places:

    • push-config.zod.ts :93 ("alternate channel implementations");
    • push-device.zod.ts :50 ("domestic Android vendors plug in as alternate channel implementations");
    • register-device-request.test.ts :8;
    • push-service.mdx :89 ("they plug in as separate channel implementations").

    ADR-0012's amended §6 bullet and §6.1 (PR 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) say vendor is a token type the sender routes on, and that the phase-2 vendors arrive behind the hosted relay with no plugin and no channel.

  • Gates:

  • Contract review:

    • 6668108542 on 28f2cc1f: PASS, Served-tier: CONTRACT_REVIEW_TIER, no must-fix, two should-fix:
      • S1: vendor named only the relay as the router;
      • S2: the vendor list's separators.
    • Delta 6669312983 on 52082426b: PASS, nothing outstanding. It found:
      • the merge has no hand-resolved hunk, and 52082426b only regenerates;
      • "the direct transport today, the hosted relay (the default) once served" is true against PushService.send (push-service.ts :92), push-channel.ts :147 and PushServicePlugin.init's PUSH_RELAY_NOT_SERVED_MESSAGE.
  • Declarations: Clause-②: no, Metadata-protocol: none, @objectstack/spec patch, the same in the body and in the changeset. TSDoc in src/**/*.zod.ts ships in the published declarations, so the patch is owed. No skill restates the retired reading; the dev re-grepped skills/ and found none.

  • Accepted deviations:

    • The surface was widened by claim amendment 6666548945 to cover the push-service.mdx sentence. That is the same family, folded in rather than filed as a one-sentence card.
    • S1 was applied beyond the two docblocks the review named: the module paragraph, the docs page and the changeset carried the same relay-only clause.
    • push-device.zod.ts :12 is kept, because it is a §6 device-registry citation and §6 still is the registry.
    • Body r2's footer is kept as in r1.
    • mcp_calls: 0 in every round.
  • Carried: nothing. The re-grep across content/docs, packages/spec/src, service-push, service-messaging/src, skills and docs finds no other §6 vendor reading outside the ADR's own quoted amendment text.

  • Landing: not governed (check-governed-merges --pr 30631: 0 of 6 paths, 51 lines ≤ 3000). The hand-off is pr_ready plus automerge_enable, through the merge queue.


Generated by Claude Code

@objectstack-fleet
objectstack-fleet Bot marked this pull request as ready for review November 5, 2026 21:06
@objectstack-fleet
objectstack-fleet Bot enabled auto-merge November 5, 2026 21:06
@objectstack-fleet
objectstack-fleet Bot added this pull request to the merge queue Nov 5, 2026
Merged via the queue into main with commit bd65cb6 Nov 5, 2026
70 checks passed
@objectstack-fleet
objectstack-fleet Bot deleted the claude/issue-29960-push-vendor-docblocks branch November 5, 2026 21:36
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 protocol:system size/s tests tooling Fix lands in tooling, not a product package — enters pm:queue only with Unblocks: or a named surface

Projects

None yet

1 participant