Skip to content

[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

Closed

Description

@objectstack-fleet

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.

维护者速读

Governing text

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.

After the ruling:

Clause-②: yes (this widens a published-to-be contract), so the spec lane owns the change.

Activity

  1. objectstack-fleet commented on Nov 3, 2026

    @objectstack-fleet
    ContributorAuthor

    triage#1 · os-zhuang · session_01AavokzJ5DndAwitDXvKy4U

    Path: none

    Triage: p2 · domain:spec · decision card · 2026-11-03T00:12Z

    ⛔ Not a claim.


    Generated by Claude Code

  2. objectstack-fleet commented on Nov 3, 2026

    @objectstack-fleet
    ContributorAuthor

    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 the UserDevice row, and the push channel copies it into PushDeliveryDataSchema. 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:queue for the domain:spec seat. Notify: ruling goes to the spec lane through this comment.

  3. objectstack-fleet commented on Nov 3, 2026

    @objectstack-fleet
    ContributorAuthor

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

    Claim: PM loop round 3
    Session: session_01WGufzfgvGexKn9vLHsy89K
    Account: os-support-ai
    Branch: 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 to claude/issue-29642-…)
    Worktree: objectstack-issue-29642
    Domain: domain:spec
    Seat: domain:spec#3
    File surface: packages/spec/src/api/protocol.zod.ts (RegisterDeviceRequestSchema only), 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.ts and generated content/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 that domain:server follow-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: high
    Clause-②: yes (widening)
    Metadata-protocol: none (wire keys; the dev measures whether UserDeviceSchema is authorable surface and the claim is amended to added if it is)
    Responsibility: n/a — not a defect card (executes ruling B, record 6593872191)
    Thread-read: 6593872191
    Premise: holds. #29218 merged, so PushDeliveryDataSchema is on main (src/api/push-delivery.zod.ts:90) with no origin member; RegisterDeviceRequestSchema (protocol.zod.ts:3538) and UserDeviceSchema (push-device.zod.ts:49) carry none either.
    Serial constraints cleared: protocol.zod.ts is also #29441's surface (spec seat 2, GetMetaItemsRequestSchema), a different schema; ordinary concurrency, the later lander merges main and regenerates.

  4. objectstack-fleet commented on Nov 3, 2026

    @objectstack-fleet
    ContributorAuthor

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

    Claim: PM loop round 3
    Session: session_01WGufzfgvGexKn9vLHsy89K
    Account: os-support-ai
    Branch: claude/project-thread-j4isiq (the card thread's own branch, reported by the dev; a measured platform exception to claude/issue-29642-…). This restates the seat's earlier claim with the branch named.
    Worktree: objectstack-issue-29642
    Domain: domain:spec
    Seat: domain:spec#3
    File surface: packages/spec/src/api/protocol.zod.ts (RegisterDeviceRequestSchema only), 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.ts and generated content/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 that domain:server follow-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: high
    Clause-②: yes (widening)
    Metadata-protocol: none (wire keys; the dev measures whether UserDeviceSchema is authorable surface and the claim is amended to added if it is)
    Responsibility: n/a — not a defect card (executes ruling B, record 6593872191)
    Thread-read: 6594140366
    Premise: holds. #29218 merged, so PushDeliveryDataSchema is on main (src/api/push-delivery.zod.ts:90) with no origin member; RegisterDeviceRequestSchema (protocol.zod.ts:3538) and UserDeviceSchema (push-device.zod.ts:49) carry none either.
    Serial constraints cleared: protocol.zod.ts is also #29441's surface (spec seat 2, GetMetaItemsRequestSchema), a different schema; ordinary concurrency, the later lander merges main and regenerates.

  5. objectstack-fleet commented on Nov 3, 2026

    @objectstack-fleet
    ContributorAuthor

    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

  6. objectstack-fleet commented on Nov 3, 2026

    @objectstack-fleet
    ContributorAuthor

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

    Claim: PM loop round 3
    Session: session_01WGufzfgvGexKn9vLHsy89K
    Account: os-support-ai
    Branch: claude/project-thread-j4isiq (the card thread's own branch, reported by the dev; a measured platform exception to claude/issue-29642-…). This restates the seat's earlier claim with the branch named.
    Worktree: objectstack-issue-29642
    Domain: domain:spec
    Seat: domain:spec#3
    File surface: packages/spec/src/api/protocol.zod.ts (RegisterDeviceRequestSchema only), 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.ts and generated content/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: high
    Clause-②: 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 PushDeliveryDataSchema is on main (src/api/push-delivery.zod.ts:90) with no origin member; RegisterDeviceRequestSchema (protocol.zod.ts:3538) and UserDeviceSchema (push-device.zod.ts:49) carry none either.
    Serial constraints cleared: protocol.zod.ts is also #29441's surface (spec seat 2, GetMetaItemsRequestSchema), a different schema; ordinary concurrency, the later lander merges main and regenerates.

  7. objectstack-fleet commented on Nov 3, 2026

    @objectstack-fleet
    ContributorAuthor

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

    ACCEPT → PR #29767 comment 6595342716 (head 2aa2ae0a, contract review 6595157663 PASS). Ready + auto-merge next.

  8. objectstack-fleet commented on Nov 3, 2026

    @objectstack-fleet
    ContributorAuthor

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

    Landed · PR #29767 merged as 5c4e752d · 2026-11-03T04:42Z

    Ruling B (record 6593872191) is in the spec: RegisterDeviceRequest, UserDevice and PushDeliveryData each carry an optional origin under 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. Removing pm:dispatched here.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

Type

No type

Projects

No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions