Repository navigation
Description
domain:spec#3 · os-support-ai · session_01WGufzfgvGexKn9vLHsy89K
Filing gate: ② a maintainer ruling's server half. Ruling B on #29642 (record 6593872191, maintainer 「同意」 2026-11-03T02:26Z): "the Console sends the origin it was loaded from in RegisterDeviceRequest. The server stores it on the UserDevice row, and the push channel copies it into PushDeliveryDataSchema." The spec half is PR #29767 (#29642), which declares origin on all three schemas with the device-side members marked [PLANNED — not yet stored]. ⛔ Not a claim. Suggested-grade: p2 (inherits #29642; without it the declared origin is never stored or sent, so the mobile shell still opens a tap on the wrong server).
Business reason. A user with two saved servers in the mobile shell (objectstack-ai/mobile#173) taps a push from server B and lands on server A. The fix only works once the server stores the origin at registration and sends it back on every push. Reader: the domain:server seat, after PR #29767 merges.
What to build (measured by the #29642 dev, os-dev-report 6595041258)
packages/services/service-messaging/src/objects/user-device.object.ts:sys_user_devicegains anoriginfield besideapp_id/locale.packages/services/service-messaging/src/device-registry.ts: the row write storesrequest.origin ?? null(besideapp_id: request.appId ?? null,locale: request.locale ?? null), and the row read setsdevice.originwhen present (besideif (row.locale) device.locale = …).packages/runtime/src/domains/notifications.ts: no edit needed;POST /devicesalready passesparsed.datatoregisterDevice, sooriginreaches the registry once the spec merges.- The push channel (Push channel: build the device registry routes (
/notifications/devices) and the APNs + FCM transport that ADR-0012 marks "MUST ship M1" #29041): copy the device row'sorigininto the pushdata(PushDeliveryDataSchema.origin).git grep PushDeliveryDataoutsidepackages/specis empty today, so the copy lands there. - Remove the two
[PLANNED — not yet stored]markers inpackages/specand flip the marker pin inpush-server-origin.test.tsin the same PR (a spec-text edit inside the server PR, the declaration's own retirement).
Out of scope
- The Console sending
window.location.originat registration rides Console: register the mobile shell's native push token after sign-in, unregister it on sign-out #29042.
Serial: after PR #29767.
Dedupe: searched "sys_user_device" origin (3 hits: #29642, #29068 closed, #29067 closed) and push origin device registry (47 hits; nearest #29042 Console registration, #29067/#29068 the closed registry build). New.
Links back: #29642, PR #29767, #29042, #29041, objectstack-ai/mobile#174.
Activity
objectstack-fleet commentedon Nov 3, 2026
domain:spec#3 · os-support-ai · session_01WGufzfgvGexKn9vLHsy89K
Carried here from the contract review of PR #29767 (record 6595157663, PASS, three should-fix items on
packages/spec/src/system/push-server-origin.ts), since this card edits that module when the[PLANNED …]markers come off:http://my_svc:3000and a trailing-dot FQDN are refused althoughwindow.location.origincan answer them. Allow_in a label, or name the limit in the docblock.:443on https,:80on http, leading zeros, out of range) and the IPv6 arm accepts[:::]; tighten, or note that the Console is the one producer.Also for the Console producer (#29042): send
originonly whenwindow.location.protocolishttp:orhttps:; an opaque"null"origin is refused and fails the whole registration with a 400.objectstack-fleet commentedon Nov 3, 2026
triage#1 · os-zhuang · session_01AavokzJ5DndAwitDXvKy4U
Path: none
Triage: first-touch audit · p2 ·
domain:server·pm:blocked· 2026-11-03T04:52Z⛔ Not a claim.
Blocked-by: #29767
sys_user_devicestores the device'sorigin, and the push channel copies it into the push data. Without it the declaredoriginis never stored, and a two-server shell still opens a tap on the wrong server. p2, inheriting [Decision] Push delivery data: name the server that sent a push, so a mobile shell with several saved servers opens a tap on the right one #29642.Generated by Claude Code
objectstack-fleet commentedon Nov 5, 2026
domain:server#3 · os-elon-musk · session_016WV4cytQjG9X1CrCgytUM5
Claim: PM loop round 1 · 2026-11-05T21:51Z
Session:
session_016WV4cytQjG9X1CrCgytUM5Account:
os-elon-musk(the seat's linked user asGET /useranswers it; the card's assignee)Branch:
claude/issue-29768-push-device-originWorktree:
objectstack-issue-29768Domain:
domain:serverSeat:
domain:server#3File surface:
packages/services/service-messaging/src/objects/user-device.object.ts(sys_user_device.originbesideapp_id/locale),packages/services/service-messaging/src/device-registry.ts(row write and read),packages/services/service-messaging/src/push-channel.ts(copy the row'sorigininto the pushdata), tests beside each and anyservice-messagingpin/catalog the new field moves (regenerated with repo tooling); the declaration retirement inpackages/spec: the two[PLANNED — not yet stored]prefixes (packages/spec/src/api/protocol.zod.tsRegisterDeviceRequestSchema.origin,packages/spec/src/system/push-device.zod.tsUserDeviceSchema.origin) and the marker pin inpackages/spec/src/system/push-server-origin.test.ts, plus the docblock-only should-fix lines of 6595162668 onpush-server-origin.ts(no validator change); root.changeset/files (stop on breach; explain in the report)Container & model:
M,mode:thread,model: default(dispatch-gates --tier: no path-derived mandate; floor sonnet · default opus · ceiling fable),effort: highClause-②: no
Metadata-protocol: none
Responsibility: n/a — not a defect card
Thread-read: 6595775612
Premise: holds — unblocked: the wait was on #29767, closed 2026-11-03T04:42Z (PR merged); re-derived on
origin/main:sys_user_devicehas nooriginfield, both[PLANNED — not yet stored]markers and their pin are still present, and the push channel now lives inservice-messaging/src/push-channel.ts(it readsPushDeliveryDataSchema), so the copy lands thereSerial constraints cleared: none: no open PR touches
packages/services/service-messaging/**orpackages/spec/src/system/push-*(19 open non-mailbox PRs read at claim time)objectstack-fleet commentedon Nov 5, 2026
domain:server#3 · os-elon-musk · session_016WV4cytQjG9X1CrCgytUM5
Claim amendment to 6670490478 · 2026-11-05T21:54Z
Branch:
claude/project-thread-9eb7x4(the card thread's designated push branch, the recorded platform exception toclaude/issue-<n>-<slug>)Session:
session_016WV4cytQjG9X1CrCgytUM5(seat) · dev card threadsession_016WV4czbHcjvoBYWDw2gRAqEvery other line of 6670490478 stands.
objectstack-fleet commentedon Nov 5, 2026
domain:server#3 · os-elon-musk · session_016WV4cytQjG9X1CrCgytUM5
Claim: PM loop round 1 · 2026-11-05T22:47Z
Session:
session_016WV4cytQjG9X1CrCgytUM5(seat) · dev card threadsession_016WV4czbHcjvoBYWDw2gRAqAccount:
os-elon-musk(the seat's linked user asGET /useranswers it; the card's assignee)Branch:
claude/project-thread-9eb7x4(the card thread's designated push branch, the recorded platform exception toclaude/issue-<n>-<slug>)Worktree:
objectstack-issue-29768Supersedes: claim 6670490478 and its amendments 6670564350 (re-posted whole so the closing-target guard reads the live branch)
Domain:
domain:serverSeat:
domain:server#3File surface:
packages/services/service-messaging/src/objects/user-device.object.ts(sys_user_device.originbesideapp_id/locale),packages/services/service-messaging/src/device-registry.ts(row write and read),packages/services/service-messaging/src/push-channel.ts(copy the row'sorigininto the pushdata), tests beside each and anyservice-messagingpin/catalog the new field moves (regenerated with repo tooling); the declaration retirement inpackages/spec: the two[PLANNED — not yet stored]prefixes (packages/spec/src/api/protocol.zod.tsRegisterDeviceRequestSchema.origin,packages/spec/src/system/push-device.zod.tsUserDeviceSchema.origin) and the marker pin inpackages/spec/src/system/push-server-origin.test.ts, plus the docblock-only should-fix lines of 6595162668 onpush-server-origin.ts(no validator change); root.changeset/files (stop on breach; explain in the report)Container & model:
M,mode:thread,model: default(dispatch-gates --tier: no path-derived mandate; floor sonnet · default opus · ceiling fable),effort: highClause-②: no
Metadata-protocol: none
Responsibility: n/a — not a defect card
Thread-read: 6670564350
Premise: holds — unblocked: the wait was on #29767, closed 2026-11-03T04:42Z (PR merged); re-derived on
origin/main:sys_user_devicehas nooriginfield, both[PLANNED — not yet stored]markers and their pin are still present, and the push channel now lives inservice-messaging/src/push-channel.ts(it readsPushDeliveryDataSchema), so the copy lands thereSerial constraints cleared: none: no open PR touches
packages/services/service-messaging/**orpackages/spec/src/system/push-*(19 open non-mailbox PRs read at claim time)objectstack-fleet commentedon Nov 5, 2026
domain:server#3 · os-elon-musk · session_016WV4czbHcjvoBYWDw2gRAq
os-dev-report
{ "issue": 29768, "status": "done", "branch": "claude/project-thread-9eb7x4", "pr": "https://github.com/objectstack-ai/objectstack/pull/30705", "session": "session_016WV4czbHcjvoBYWDw2gRAq", "premise_still_valid": true, "summary": "sys_user_device gains an optional, unbounded origin column; DeviceRegistry writes origin: request.origin ?? null beside app_id/locale on insert and refresh, and toUserDevice answers it, so listDevices returns it and the push channel's existing withOrigin copy finally has a value. Spec: the two [PLANNED — not yet stored] prefixes are removed, the marker pin flipped, and push-server-origin.ts takes the three 6595162668 should-fix items by the docblock route only (validator unchanged). The new label/help is hand-translated in all seven translated locales.", "tests": "Measured on origin/main b77c160a (registry+object reverted, pins kept): row.origin undefined, listDevices no origin, push data {notificationId, route} only. New pins: device-registry.integration.test.ts top-level describe (4: stored+listed; null when absent and no member; refresh replaces/clears; push data through createPushChannel over the real MessagingService carries it), user-device.object.test.ts +1 (optional text, no maxLength), push-server-origin.test.ts marker pin flipped. Ablations: registry+object at main => 4 red (null case is the negative control, stays green); read-back line removed => 2 red; PLANNED prefix restored on UserDeviceSchema.origin => flipped pin red. Head: service-messaging 66 files / 850 tests pass, typecheck exit 0; spec push-server-origin.test.ts 68 pass; spec check:test-typecheck OK with an 8 GB heap (OOM at default heap, environmental).", "gates": "node scripts/pm/dispatch-gates.mjs --commands at 48036dd1: 108 families, all exit 0 (sweep split by a container restart after 52, resumed at 53 on the same head and tree). --ran reconcile: 108 derived, 108 run, 0 NOT-MEASURED, 0 UNRUN. pnpm lint exit 0. check-i18n-bundles.mjs exit 0 (in sync). CI check-runs on the PR: pending at report time.", "line_budget": "+168 / -17 over 19 files (well under 3000)", "files_changed": [ ".changeset/29768-push-device-origin-stored.md", "content/docs/references/api/protocol.mdx (generated)", "content/docs/references/system/push-device.mdx (generated)", "packages/services/service-messaging/src/device-registry.ts", "packages/services/service-messaging/src/device-registry.integration.test.ts", "packages/services/service-messaging/src/objects/user-device.object.ts", "packages/services/service-messaging/src/objects/user-device.object.test.ts", "packages/services/service-messaging/src/translations/{en,ar,es-ES,fr,ja-JP,pt-BR,zh-CN,zh-Hant}.objects.generated.ts (8)", "packages/spec/src/api/protocol.zod.ts (describe + docblock)", "packages/spec/src/system/push-device.zod.ts (describe)", "packages/spec/src/system/push-server-origin.ts (docblock)", "packages/spec/src/system/push-server-origin.test.ts" ], "deviations": [ "Do-step 3 needed no edit: push-channel.ts on origin/main already copies device.origin into each device's push data (withOrigin, landed with the channel) and push-channel.test.ts already pins it over a registry double; the gap was only that the registry never stored or answered origin. The end-to-end pin (real registry -> real channel) is added in device-registry.integration.test.ts instead.", "Translation bundles (8 generated files under service-messaging/src/translations) are outside the order's literal list; they are the service-messaging catalog the new field moves, regenerated with check-i18n-bundles.mjs --write, translated-locale values hand-written.", "The column carries no maxLength on purpose: spec's PUSH_SERVER_ORIGIN_PATTERN bounds shape, not length, so a bounded column could refuse a value the registration accepts.", "A refresh without origin clears the stored one (null), the same overwrite semantics app_id and locale already have.", "Branch is the harness-designated claude/project-thread-9eb7x4 (claim amended by the seat, 6670564350)." ], "clause_2": "no — no new published export, error code or answer member; the response shape already declared origin. Metadata-protocol: none (system object, engine-owned, not author-writable).", "dogfood_battery": "n/a — no `!` in the PR title and no (narrowing) arm", "mcp_calls": "0 — no MCP GitHub calls (reads went through gh api GET; subscribe_pr_activity on PR #30705 is a session subscription, not a GitHub write)", "api_writes": "3 — git push x2 to claude/project-thread-9eb7x4; relay pr_create (fw-20261105T224704Z-2c730a, PR #30705, 7194 bytes identical); plus this report comment via post-stamped", "open_questions": [], "security_findings": "none", "out_of_scope_findings": [ "carrier: PR body · noted, not filed — packages/services/service-push/src/push-service.ts PushMessage docblock lists data as notificationId and route only, not origin; not wrong about what it lists, transports forward data whole." ] }