Repository navigation
feat(spec): push channel protocol face — device registration widening, device contract members, push route, APNs/FCM config by reference - #29101
Conversation
…rt config cases (red) Claude-Session: https://claude.ai/code/session_01FEkQ5oc56xChD6Wrc4cGSt Co-Authored-By: Claude <noreply@anthropic.com>
…, device contract members, push delivery data, APNs/FCM transport config by reference Claude-Session: https://claude.ai/code/session_01FEkQ5oc56xChD6Wrc4cGSt Co-Authored-By: Claude <noreply@anthropic.com>
…ush channel protocol face Claude-Session: https://claude.ai/code/session_01FEkQ5oc56xChD6Wrc4cGSt Co-Authored-By: Claude <noreply@anthropic.com>
📓 Docs Drift CheckThis PR changes 1 package(s): 3 hand-written doc(s) NAME something this change touched and may need an implementation-accuracy re-verification:
⛔ 1 release-owned page(s) also name something this change touched. These are read-only:
What this run could not see
Coarse fallback — 168 page(s) merely mention a changed package (the pre-#9192 predicate, kept for the deliberately-wide backstop): Which tree this was computed onThis run read A worktree cut from an older # while this PR is open — GitHub drops the merge commit once it closes
git fetch origin 94bd0910b5a88e4e2a623cc700f347d0121feada && git checkout 94bd0910b5a88e4e2a623cc700f347d0121feada
# afterwards, rebuild it from the two parents, which stay fetchable
git fetch origin 12aad143f717e140cc1ec7227281009ef37bb192 088613bebab0047841f35dda237422bbe53942bf && git checkout -B drift-repro 12aad143f717e140cc1ec7227281009ef37bb192 && git merge --no-ff 088613bebab0047841f35dda237422bbe53942bf
node scripts/docs-audit/affected-docs.mjs --json 12aad143f717e140cc1ec7227281009ef37bb192
|
|
triage#1 · os-zhuang · session_01AavokzJ5DndAwitDXvKy4U Contract-review-claim: Taken under Generated by Claude Code |
|
triage#1 · os-zhuang · session_01AavokzJ5DndAwitDXvKy4U Contract reviewServed-tier: Inputs read: card #29067 body and all five comments (claim, os-dev-report, parked note, release, the round-2 ① Derived judgmentsGate verdicts on the head (the check-runs, not re-run): 60 success or skipped; two red lanes, both this diff's doing:
Accept-set and public-surface changes the diff implies, each judged:
② Semver level
③ Boundary flagsDev flags and
New public names — each needs the maintainer's approval before landing (escalated, not FAIL reasons):
④ Design judgment
M5 (must-fix, the missing file): card Wanted 3 — a Must-fix: M1 (declaration and level), M2 ( Implemented-by: VERDICT: FAIL Generated by Claude Code |
|
triage#1 · os-zhuang · session_01AavokzJ5DndAwitDXvKy4U Maintainer ruling: the new public names on this PR are approved · 2026-11-02T09:36ZThe maintainer answered triage's escalation of the new public names (contract review on head Approved, as listed by the review:
Two notes on what the approval does not settle:
The FAIL record's must-fixes still stand. This ruling approves names only. Generated by Claude Code |
|
domain:spec#1 · os-steve · session_01ADHkmPi25XXKx92Xmg4viS Superseded by PR #29218 · 2026-11-02T09:44Z The spec seat took #29067 over from server seat 5. Its card thread resumed this PR's three commits (to |
Requested by Leehom · project thread
Fixes #29067
Clause-②: yes (widening)
Metadata-protocol: none
维护者速读
INotificationService加三个可选设备方法,新增推送数据契约(只允许同站相对路径的 route),新增 APNs / FCM 传输配置(密钥只能按引用写)。/notifications/devices) and the APNs + FCM transport that ADR-0012 marks "MUST ship M1" #29041 推送通道和 notifications: build the device registry (sys_user_device, /notifications/devices routes, client methods) on #29067's contract (server slice 1 of #29041) #29068 设备注册路由都卡在这张 spec 卡上;手机壳(mobile#144)和 Console(Console: register the mobile shell's native push token after sign-in, unregister it on sign-out #29042)要靠它注册设备、点推送打开页面。DEFAULT_NOTIFICATION_ROUTES字节不变。回滚即 revert 本 PR。packages/spec/src/api/push-delivery.zod.ts(route 的拒收正则)、packages/spec/src/system/push-config.zod.ts(内联密钥两道拒收)、packages/spec/src/contracts/notification-service.ts(三个设备方法的语义)。pnpm --filter @objectstack/spec exec vitest run src/api/push-delivery.test.ts src/system/push-config.test.ts src/api/register-device-request.test.ts,预期 3 个文件全绿。What changes
RegisterDeviceRequestSchema(api/protocol.zod.ts): adds optionalvendor(PushVendorSchema:apnsorfcm; absent, the server derives it fromplatform),appIdandlocale.deviceIdstays optional. Its describe now says that re-registering adeviceIdrefreshes the device, and that without one the server mints an id and answers it, and the client keeps it. No.default()was added, so the bare alias stays isomorphic. The response andUnregisterDevice*are unchanged.INotificationService: adds optionalregisterDevice(userId, request),unregisterDevice(userId, deviceId)andinvalidateDeviceToken(token). Their JSDoc states one live owner per token, not found (NOT_FOUND) for another user's device, and that an invalidated row is kept, disabled, with its invalidation time. Absence means the capability is unavailable.PushDeliveryDataSchema/PushRouteSchema(newapi/push-delivery.zod.ts):notificationIdand an optionalroute. The route must start with exactly one slash, and it may not contain a backslash, whitespace or a control character (a URL parser turns a backslash, or a stripped tab or newline, into a protocol-relative reference). The JSDoc states the derivation: the route is the notification's existingactionUrl(url/actionUrlin the payload, else/OBJECT/ID) when it passes the schema, and absent otherwise. Nonotifykey is added, and the removed namePushNotificationSchemais not reused.PushServiceConfigSchema(newsystem/push-config.zod.ts, besideemail-config.zod.ts, which declares the email transport configuration): a strictapnsblock (teamId,keyId,topic,environment: sandbox | production,keyRef) and a strictfcmblock (projectId,serviceAccountRef). An inline secret is refused twice. Under a key such askey,privateKey,p8,serviceAccountorserviceAccountJsonit is refused by name, with the ref key as the prescription. Inside a ref, any value containing whitespace or braces (a PEM block, a JSON document) is refused. The block is not wired into the stack definition: its one reader is the push plugin in Push channel: build the device registry routes (/notifications/devices) and the APNs + FCM transport that ADR-0012 marks "MUST ship M1" #29041.api-surface/,export-origins/,declaration-map/,authorable-surface/,json-schema.manifest/, the strictness-ledger counts andcontent/docs/references/**, all written bypnpm --filter @objectstack/spec check:generated --fixplusgen:schema. The type-alias pin file is hand-maintained: 811 → 817 isomorphic pins, for the six new aliases.Tests
6b3ba82c, commit8dcce8b7):register-device-request.test.tsfailed 6 of its cases (the widened round-trip and the five vendor refusals), andpush-delivery.test.ts/push-config.test.tsfailed at import.088613be: the touched suites (register-device-request,push-delivery,push-config,notification-service,protocol,plugin-rest-api,type-alias-convention.pin) passed, Test Files 7/7 and Tests 327/327.check-spec-parsed-alias.mjspassed (817 pinned, OK).check:generated --fixexited 0 andcheck:docsexited 0.plugin-rest-api.test.ts, 3 endpoints) is unchanged and green.Acceptance notes
patch.UserDeviceSchema(ruling 3). No published wire shape needs the stored row: the request and response cover the routes.DEFAULT_NOTIFICATION_ROUTES' JSDoc ("device registration, preferences, listing") was already false onmainbefore this PR. This diff does not make it false, so it is left for notifications: build the device registry (sys_user_device, /notifications/devices routes, client methods) on #29067's contract (server slice 1 of #29041) #29068, which adds the device rows.Verification
Stopped early at the maintainer's wrap-up word. Not measured locally: the
dispatch-gates --commandsbattery, the full@objectstack/specpnpm test/pnpm typecheck,pnpm linton spec, and the finalorigin/mainmerge. Reason: wrap-up. CI runs the full set, and the seat reads its check-runs.Generated by Claude Code