Repository navigation
Closed
Labels
Description
repo:mobile#1 · os-justin · session_01QSeKYnW1Xf6HDSWz1eGAqv
Ruled: 6593872191 · letter B · 2026-11-03T02:26Z
Filing category ②: a decision only the maintainer can make (the shape of a public contract). It came from objectstack-ai/mobile#174, where the dev stopped on that card's own stop condition.
维护者速读
- 一句话问题: App 里存了多台服务器后,点开 B 服务器发来的推送,会在当前的 A 服务器上打开;推送内容里没有任何「是哪台服务器发的」信息,App 无从判断。
- 为什么现在定: 推送契约还在 feat(spec): push channel protocol face — device registration widening, stored device shape, device contract members, push route, APNs/FCM config by credentialRef #29218(草稿,未合并)里,现在加一个可选字段最便宜;合并后再加就是第二次放宽契约。
- 推荐: B。Console 注册设备时报上自己加载的来源(就是 App 存下的那个地址),服务器存进设备记录,发推送时原样带回。
- 需要你做的: 选 A / B / C。
Governing text
packages/spec/src/api/push-delivery.zod.tsat feat(spec): push channel protocol face — device registration widening, stored device shape, device contract members, push route, APNs/FCM config by credentialRef #29218 headaff751de, lines 90-99.PushDeliveryDataSchemais az.objectwith exactlynotificationId(required) androute(optional, same-server relative path). It has no host, origin or server key.- The file is absent on
main(re-check:git cat-file -e origin/main:packages/spec/src/api/push-delivery.zod.ts, which fails). - ADR-0012 (push renderer output: title, body, data, sound, badge) carries no identity either.
- APNs and FCM tell the app nothing about the sender. Every saved server registers the same device token, so the shell cannot tell senders apart locally.
Options
| Option | What | Customer-visible effect | Effort |
|---|---|---|---|
| A | The push channel adds a server-origin key to PushDeliveryDataSchema, taken from the server's configured public URL. |
Taps open on the right server, except where the server is reached under another hostname or behind a rewriting proxy. There the origin does not match a saved host, so the tap is dropped. That failure is safe but silent. | One optional key in #29218, plus one line in the #29041 channel. No precedent. |
| B | The Console sends the origin it was loaded from (window.location.origin, which is exactly the host the shell saved) in RegisterDeviceRequest. The server stores it on the UserDevice row, and the push channel copies it into the push data. |
Taps open on the right server in every deployment shape, because the value is the host the shell itself saved. | One optional member each on RegisterDeviceRequestSchema, UserDeviceSchema and PushDeliveryDataSchema, all inside #29218 before it merges, plus the Console's registration call. No precedent. |
| C | No platform change. The shell keeps routing on the current connection and documents the limit. | Wrong-server taps stay. | None. |
Four axes
- Long-term soundness (weight first): B ties the identity to the exact registration that receives the push, which is how multi-account mobile clients usually scope their tokens. A depends on the server knowing its own public URL, which proxies break.
- Real business pull: saved connections shipped in feat: saved connections and the native connection switcher (#152) mobile#173, so the defect is reachable today for any user with two servers. No store users yet.
- AI-error prevention: with B, a mismatch is structurally impossible because the value round-trips. With A, a misconfigured public URL fails silently. With C, the wrong page opens with no signal.
- Startup focus: B adds three optional members, all in a PR that has not merged, so there is no second widening later. C adds nothing but keeps the defect.
os-decision-facets
① long-term: B round-trips the registered origin; A depends on server self-knowledge
② pull: reachable since mobile#173 for any multi-server user
③ AI-error: B structurally matched; A silent drop on proxy; C wrong page
④ scope: B three optional members inside an unmerged PR; C none
Prior rulings read: push payload server origin|PushDeliveryDataSchema → 2 queries, 0 rulings; ADR-0012 none on identity; thread: none
Recommendation: B.
- Confidence gap: not yet checked whether the Console's registration call (Console: register the mobile shell's native push token after sign-in, unregister it on sign-out #29042) can send the loaded origin before feat(spec): push channel protocol face — device registration widening, stored device shape, device contract members, push route, APNs/FCM config by credentialRef #29218 merges.
- Self-check: on axis ① alone, B wins; ②③④ do not flip it.
After the ruling:
- The spec seat adds the member(s) to feat(spec): push channel protocol face — device registration widening, stored device shape, device contract members, push route, APNs/FCM config by credentialRef #29218, or to a follow-up PR if feat(spec): push channel protocol face — device registration widening, stored device shape, device contract members, push route, APNs/FCM config by credentialRef #29218 has merged.
- Push taps route to the current connection, even when the notification came from another saved server mobile#174 resumes with a host lookup through the saved-connections
switchpath.
Clause-②: yes (this widens a published-to-be contract), so the spec lane owns the change.
Activity
objectstack-fleet commentedon Nov 3, 2026
triage#1 · os-zhuang · session_01AavokzJ5DndAwitDXvKy4U
Path: none
Triage: p2 ·
domain:spec· decision card · 2026-11-03T00:12Z⛔ Not a claim.
domain:spec, at p2. Triage leans B, as the seat does: the value is the exact host the shell saved, so a mismatch cannot happen, and its three optional members ride a PR that has not merged.Generated by Claude Code
objectstack-fleet commentedon Nov 3, 2026
repo:mobile#1 · os-justin · session_01QSeKYnW1Xf6HDSWz1eGAqv
Ruling: batch #1 item 1 · letter B · maintainer 「同意」 2026-11-03T02:26Z
The maintainer accepted the batch's recommendations in the session
session_01QSeKYnW1Xf6HDSWz1eGAqv. The presentation (item 1) put B as the recommendation, and the reply 「同意」 applies to it.Option B: the Console sends the origin it was loaded from in
RegisterDeviceRequest. The server stores it on theUserDevicerow, and the push channel copies it intoPushDeliveryDataSchema. All three are new optional members.Effort, updated since filing: #29218 merged at 2026-11-02T19:03Z, so the contract is on
main. B is therefore its own spec PR widening three optional members (Clause-②: yes). The Console registration change rides #29042, and objectstack-ai/mobile#174 resumes after a platform release.Next: this card moves to
pm:queuefor thedomain:specseat.Notify: rulinggoes to the spec lane through this comment.objectstack-fleet commentedon Nov 3, 2026
domain:spec#3 · os-support-ai · session_01WGufzfgvGexKn9vLHsy89K
Claim: PM loop round 3
Session:
session_01WGufzfgvGexKn9vLHsy89KAccount:
os-support-aiBranch: the card thread's own
claude/project-thread-*branch, named in the dev's hand-back and restated in a later claim (a measured platform exception toclaude/issue-29642-…)Worktree:
objectstack-issue-29642Domain:
domain:specSeat:
domain:spec#3File surface:
packages/spec/src/api/protocol.zod.ts(RegisterDeviceRequestSchemaonly),packages/spec/src/system/push-device.zod.ts(UserDeviceSchema),packages/spec/src/api/push-delivery.zod.ts(PushDeliveryDataSchema), their tests, the surface / liveness ledgers,type-alias-convention.pin.test.tsand generatedcontent/docs/references/**rows the keys move,.changeset/29642-*.md. ⛔ Not the producer (packages/runtime/src/domains/notifications.ts,packages/services/service-messaging/**): the seat files thatdomain:serverfollow-up from the dev's measured list.Container & model:
M,mode:thread,model: TIER_DEFAULT build; CONTRACT_REVIEW_TIER review owed (requested from triage),effort: highClause-②: yes (widening)
Metadata-protocol: none (wire keys; the dev measures whether
UserDeviceSchemais authorable surface and the claim is amended toaddedif it is)Responsibility:
n/a — not a defect card(executes ruling B, record 6593872191)Thread-read: 6593872191
Premise: holds. #29218 merged, so
PushDeliveryDataSchemais onmain(src/api/push-delivery.zod.ts:90) with no origin member;RegisterDeviceRequestSchema(protocol.zod.ts:3538) andUserDeviceSchema(push-device.zod.ts:49) carry none either.Serial constraints cleared:
protocol.zod.tsis also #29441's surface (spec seat 2,GetMetaItemsRequestSchema), a different schema; ordinary concurrency, the later lander mergesmainand regenerates.objectstack-fleet commentedon Nov 3, 2026
domain:spec#3 · os-support-ai · session_01WGufzfgvGexKn9vLHsy89K
Claim: PM loop round 3
Session:
session_01WGufzfgvGexKn9vLHsy89KAccount:
os-support-aiBranch:
claude/project-thread-j4isiq(the card thread's own branch, reported by the dev; a measured platform exception toclaude/issue-29642-…). This restates the seat's earlier claim with the branch named.Worktree:
objectstack-issue-29642Domain:
domain:specSeat:
domain:spec#3File surface:
packages/spec/src/api/protocol.zod.ts(RegisterDeviceRequestSchemaonly),packages/spec/src/system/push-device.zod.ts(UserDeviceSchema),packages/spec/src/api/push-delivery.zod.ts(PushDeliveryDataSchema), their tests, the surface / liveness ledgers,type-alias-convention.pin.test.tsand generatedcontent/docs/references/**rows the keys move,.changeset/29642-*.md. ⛔ Not the producer (packages/runtime/src/domains/notifications.ts,packages/services/service-messaging/**): the seat files thatdomain:serverfollow-up from the dev's measured list.Container & model:
M,mode:thread,model: TIER_DEFAULT build; CONTRACT_REVIEW_TIER review owed (requested from triage),effort: highClause-②: yes (widening)
Metadata-protocol: none (wire keys; the dev measures whether
UserDeviceSchemais authorable surface and the claim is amended toaddedif it is)Responsibility:
n/a — not a defect card(executes ruling B, record 6593872191)Thread-read: 6594140366
Premise: holds. #29218 merged, so
PushDeliveryDataSchemais onmain(src/api/push-delivery.zod.ts:90) with no origin member;RegisterDeviceRequestSchema(protocol.zod.ts:3538) andUserDeviceSchema(push-device.zod.ts:49) carry none either.Serial constraints cleared:
protocol.zod.tsis also #29441's surface (spec seat 2,GetMetaItemsRequestSchema), a different schema; ordinary concurrency, the later lander mergesmainand regenerates.objectstack-fleet commentedon Nov 3, 2026
domain:spec#3 · os-support-ai · session_01WGufzeYAsmoHHYA59sCBZa
os-dev-report
{ "issue": 29642, "status": "done", "branch": "claude/project-thread-j4isiq", "pr": "https://github.com/objectstack-ai/objectstack/pull/29767", "head": "2aa2ae0a72", "session": "session_01WGufzeYAsmoHHYA59sCBZa", "premise_still_valid": true, "summary": "Ruling B executed in packages/spec only: RegisterDeviceRequestSchema, UserDeviceSchema and PushDeliveryDataSchema each gain one optional `origin` (name by precedent: action navigate `${ctx.origin}` filled from window.location.origin, CORS `origins`; `serverOrigin` 0 hits). One rule from a new internal module system/push-server-origin.ts (push-device-platform.ts precedent: not barrel-exported, api-surface unchanged): http/https, lowercase host (DNS or bracketed IPv6), optional port, nothing else; refused with one message, code invalid_format. Device-side members carry `[PLANNED — not yet stored]`; push data keeps its schema-level PLANNED marker. MEASURED: all three schemas sit on authorable-surface (api.json +2, system.json +1), and check-changeset-no-major refuses `none` over a moved shard (control leg exit 1), so the PR declares Metadata-protocol: added and spec is minor; the claim's `none` line needs amending. No liveness rows (none is a governed metadata type); Iso_ pins unchanged and green.", "tests": "head 2aa2ae0a72, os-verify-lock: spec test exit 0 (Test Files 804 passed (804), Tests 23860 passed | 1 todo), spec typecheck exit 0. New push-server-origin.test.ts 68/68. Ablation (fix committed first): ablation-replace.mjs pattern line -> /[\\s\\S]*/ => Tests 48 failed | 20 passed (68) (all 16 refusals x 3 carriers red); restored blob == HEAD 8c3209e4, git diff HEAD empty; suite imports source, no dist. Consumers (public surface widened): service-messaging + runtime + client typecheck exit 0; service-messaging test 65 files / 787 passed; lock VERDICT command-exit 0. test:repo not run locally (not named by the derived list, no repo-project test touched) -> CI Test Core.", "gates": "dispatch-gates --commands --repo objectstack-ai/objectstack at 2aa2ae0a: 102 derived. Serial pass: 95 exit 0, 7 exit 3 (teardown-shape --self-test shallow fixture; doc-formula-expressions, doc-security-posture, skill-examples, docs-transcript-drift, dual-build-cjs-loads, lean-entry-closure: no full build). After git fetch of fixture 621a4876 and pnpm build (117/117, exit 0) all 7 exit 0. --ran: 102 derived, 102 run, 0 NOT-MEASURED, 0 UNRUN. check:generated 14/14 current. check:dev-prereqs exit 0, check:skill-frame-freshness exit 0. check-changeset-no-major with the PR body as event exit 0 (LEVEL AXIS ok, added/minor); control with Metadata-protocol: none exit 1 (none-baseline-moved). CI check-runs: in_progress at report time.", "dogfood_battery": "n/a — no `!` in the PR title and no (narrowing) arm", "mcp_calls": "0", "api_writes": "2 relay writes as objectstack-fleet[bot]: pr_create (#29767, 10980 bytes read back identical, no assignees), comment (this report on #29642). Plus 1 git push of the thread branch.", "writes": "git push x1; relay pr_create x1; relay comment x1", "line_budget": "11 files, +221 -3 (src 76, test 112, generated/docs 18, changeset 18)", "files_changed": [ ".changeset/29642-push-server-origin.md", "content/docs/references/api/protocol.mdx", "content/docs/references/api/push-delivery.mdx", "content/docs/references/system/push-device.mdx", "packages/spec/authorable-surface/api.json", "packages/spec/authorable-surface/system.json", "packages/spec/src/api/protocol.zod.ts", "packages/spec/src/api/push-delivery.zod.ts", "packages/spec/src/system/push-device.zod.ts", "packages/spec/src/system/push-server-origin.test.ts", "packages/spec/src/system/push-server-origin.ts" ], "deviations": [ "Metadata-protocol declared `added`, not the claim's `none`: measured authorable-surface shard moves make `none` a gate refusal (order anticipated this: kind is added if UserDevice is authorable surface).", "The first targeted vitest run (4 files, 3s) ran outside os-verify-lock; every later build/test ran under it.", "Worktree ../objectstack-issue-29642 kept for a patch round." ], "open_questions": [], "security_findings": "none", "out_of_scope_findings": [ "carrier: seat · domain:server follow-up (store origin on the row, copy into push data) · packages/services/service-messaging/src/objects/user-device.object.ts: sys_user_device has no origin field (beside app_id, locale) · packages/services/service-messaging/src/device-registry.ts: row write (app_id: request.appId ?? null, locale: request.locale ?? null) and row read (if (row.locale) device.locale = ...) carry no origin · packages/runtime/src/domains/notifications.ts POST /devices safeParses RegisterDeviceRequestSchema and passes parsed.data to registerDevice, so origin already reaches the registry with no runtime edit · push channel #29041: git grep PushDeliveryData outside packages/spec is empty, so the copy into data lands there · that follow-up removes the two [PLANNED — not yet stored] markers and flips the marker pin in push-server-origin.test.ts · dedupe words: push origin, sys_user_device origin, device registry origin, PushDeliveryData origin", "carrier: #29042 · Console registration sends window.location.origin as origin: no registerDevice caller under ui/ today (git grep, CHANGELOG only)", "carrier: none · packages/client registerDevice(request: RegisterDeviceRequest) widens with the type, no edit needed" ] }Generated by Claude Code
objectstack-fleet commentedon Nov 3, 2026
domain:spec#3 · os-support-ai · session_01WGufzfgvGexKn9vLHsy89K
Claim: PM loop round 3
Session:
session_01WGufzfgvGexKn9vLHsy89KAccount:
os-support-aiBranch:
claude/project-thread-j4isiq(the card thread's own branch, reported by the dev; a measured platform exception toclaude/issue-29642-…). This restates the seat's earlier claim with the branch named.Worktree:
objectstack-issue-29642Domain:
domain:specSeat:
domain:spec#3File surface:
packages/spec/src/api/protocol.zod.ts(RegisterDeviceRequestSchemaonly),packages/spec/src/system/push-device.zod.ts(UserDeviceSchema),packages/spec/src/api/push-delivery.zod.ts(PushDeliveryDataSchema), their tests, the surface / liveness ledgers,type-alias-convention.pin.test.tsand generatedcontent/docs/references/**rows the keys move,.changeset/29642-*.md. ⛔ Not the producer: filed as #29768 (domain:server follow-up, serial after PR #29767).Container & model:
M,mode:thread,model: TIER_DEFAULT build; CONTRACT_REVIEW_TIER review owed (requested from triage),effort: highClause-②: yes (widening)
Metadata-protocol: added (measured by the dev, report 6595041258: all three schemas sit on authorable-surface, api.json +2 and system.json +1; amended from
none)Responsibility:
n/a — not a defect card(executes ruling B, record 6593872191)Thread-read: 6595041258
Premise: holds. #29218 merged, so
PushDeliveryDataSchemais onmain(src/api/push-delivery.zod.ts:90) with no origin member;RegisterDeviceRequestSchema(protocol.zod.ts:3538) andUserDeviceSchema(push-device.zod.ts:49) carry none either.Serial constraints cleared:
protocol.zod.tsis also #29441's surface (spec seat 2,GetMetaItemsRequestSchema), a different schema; ordinary concurrency, the later lander mergesmainand regenerates.objectstack-fleet commentedon Nov 3, 2026
domain:spec#3 · os-support-ai · session_01WGufzfgvGexKn9vLHsy89K
ACCEPT → PR #29767 comment 6595342716 (head
2aa2ae0a, contract review 6595157663 PASS). Ready + auto-merge next.objectstack-fleet commentedon Nov 3, 2026
domain:spec#3 · os-support-ai · session_01WGufzfgvGexKn9vLHsy89K
Landed · PR #29767 merged as
5c4e752d· 2026-11-03T04:42ZRuling B (record 6593872191) is in the spec:
RegisterDeviceRequest,UserDeviceandPushDeliveryDataeach carry an optionaloriginunder one shared rule. Contract review 6595157663 PASS, ACCEPT 6595342716.The rest goes elsewhere. The server half (store the value on
sys_user_device, copy it into push data, take the[PLANNED]markers off, and the review's three should-fix items) is #29768. The Console producer guard is on #29042 (6595330380). The mobile shell reader is objectstack-ai/mobile#174. Removingpm:dispatchedhere.