Skip to content

service-push: send through the hosted relay transport (push-relay.zod.ts wire, cloud binding credential) #29920

Closed

Description

@objectstack-fleet

domain:server#4 · os-warren · session_01JQwxbacSXJQqAztgt4UMrS

Blocked-by: #29794
Related: #29816 (spec, landed), #29817 (ADR-0012 amendment), objectstack-ai/cloud#3519 (ruling)

Context

#29816 made transport: relay | direct the shape of PushServiceConfigSchema, with relay as the default ({} reads as relay), and added the server→relay wire in packages/spec/src/api/push-relay.zod.ts. PR #29794 (round 3) ships service-push reading only the direct arm. relay, including {}, is refused loudly at PushServicePlugin.init and names this card. The relay block keeps its refused-not-served marker until this card lands.

Ask

Implement the relay transport in service-push:

  • Send. POST a PushRelayRequestSchema body (one delivery per device token: token, vendor, ID-only payload, priority) to the relay endpoint, or the default endpoint when none is set.
  • Auth. The bearer is the server's cloud binding credential (cloud ADR-0008, the oscc_… binding). Resolve it through the binding, never inline config, and never log it. No binding means a loud init refusal that names the missing binding.
  • Answers. Map PushRelayResponseSchema per token: invalid retires the device row the same way the direct transports do; retry reuses the existing retry path. 401 is a loud, non-retryable failure that names the binding.
  • Privacy. The payload stays ID-only (phase 1). No title or body leaves the server.
  • Flip the refusal. Remove the init refusal, and turn the relay block and wire markers from not-served to served in the same PR, then regenerate.

Acceptance

  • Tests pin each arm: {}, an explicit relay endpoint, no binding, 401, and an invalid and a retry result.
  • Direct-transport behaviour is unchanged.
  • The changeset names the newly served relay transport.

Filed by the seat as the follow-up that PR #29794's relay refusal names.

Activity

  1. objectstack-fleet commented on Nov 3, 2026

    @objectstack-fleet
    ContributorAuthor

    triage#1 · os-zhuang · session_01AavokzJ5DndAwitDXvKy4U

    Path: none

    Triage: first-touch audit · p2 · domain:server kept · pm:triage → pm:blocked · 2026-11-03T12:55Z

    ⛔ Not a claim.


    Generated by Claude Code

  2. added
    enhancementNew feature or request
    pm:blockedBlocked by another issue/PR — body carries Blocked-by: #N
    and removed on Nov 3, 2026
  3. objectstack-fleet commented on Nov 3, 2026

    @objectstack-fleet
    ContributorAuthor

    domain:server#4 · os-warren · session_01JQwxbacSXJQqAztgt4UMrS

    Carried into this card's scope (2026-11-03T14:31Z), from the PR #29794 round-3 contract review (6606491253 ③ and ④):

    1. Strict data on PushRelayPayloadSchema (spec seat 2's should-fix, Push channel: build the device registry routes (/notifications/devices) and the APNs + FCM transport that ADR-0012 marks "MUST ship M1" #29041 6601310426, addressed to "the first producer of the relay wire", which is this card).
    2. retryable: true on RATE_LIMIT_EXCEEDED in the relay's error answer (same source).
    3. The push-relay.zod.ts docblock still says "No push channel delivers yet … until the push channel ships". After feat(notifications): the push channel — service-push (APNs + FCM) and its channel shell in service-messaging #29794 the channel ships; only the relay sender does not. Fix that sentence in the same PR that flips the markers to served.

    All three land with the relay sender, not before.

  4. added
    pm:queueReady for the PM dispatch loop
    pm:dispatchedDispatched to a dev agent by /pm-dispatch
    and removed
    pm:blockedBlocked by another issue/PR — body carries Blocked-by: #N
    pm:queueReady for the PM dispatch loop
    on Nov 3, 2026
  5. 15 remaining items

  6. objectstack-fleet commented on Nov 5, 2026

    @objectstack-fleet
    ContributorAuthor

    domain:server#2 · os-bill · session_01Utp6Cbvcmv8YWFhFJwzjKL

    Release: session_01Utp6Cbvcmv8YWFhFJwzjKL (domain:server#2) · reason: seat sign-off · to: pm:queue for the next server seat.
    Maintainer: 「开发中的尽快收工」, project chat, 2026-11-05T22:01Z.
    Resume-from: claude/project-thread-tk5wos@634f4145cf0ecc7cd2e894e24604eded9910d19f

    State at release (WIP, no PR). The ruling as recorded on this card (Q1 A, Q2 A, Q3 A with path /api/v1/cloud/push/send, Q4 C) and the surface additions the correction comment approved are all coded. isControlPlaneDeclined moved into @objectstack/core to avoid a dependency cycle. The changeset is feat! with Clause-②: yes (narrowing) and its adr-0087 marker.

    Passed on that head: service-push 74, service-messaging 849, cloud-connection 896 plus tsc, core, spec push-relay / push-config 78, check:generated, test-typecheck.

    Owed, in order:

    1. Re-run the cli door contract test, the verify harness test and the full spec suite.
    2. The six ablations.
    3. Merge origin/main once, then check:generated.
    4. dispatch-gates, pnpm lint, the platform-checklist and check-governed-merges.
    5. Full dogfood (BREAKING).
    6. Open the draft PR and post the os-dev-report.

    For the seat: the contract review runs on the schema face. It covers packages/spec/src/contracts/cloud-binding-service.ts, packages/spec/src/api/push-relay.zod.ts, packages/spec/src/system/push-config.zod.ts, packages/spec/src/stack.zod.ts, packages/core/src/stack-auth.ts, the service-push index exports, packages/cloud-connection/src/cloud-binding.ts and the changeset. It is BREAKING, so queueing needs the maintainer's per-PR word.

  7. added
    pm:queueReady for the PM dispatch loop
    and removed
    pm:dispatchedDispatched to a dev agent by /pm-dispatch
    on Nov 5, 2026
  8. added
    pm:dispatchedDispatched to a dev agent by /pm-dispatch
    and removed
    pm:queueReady for the PM dispatch loop
    on Nov 5, 2026
  9. objectstack-fleet commented on Nov 5, 2026

    @objectstack-fleet
    ContributorAuthor

    domain:server#5 · os-elon-musk · session_016WV4cyvF7SP65frVFAYLv7

    Claim: PM loop round 2 · 2026-11-05T22:45Z — resumes os-bill's released work (Release 6670797151)
    Session: session_016WV4cyvF7SP65frVFAYLv7
    Account: os-elon-musk (the seat's linked user as GET /user answers it; the card's assignee)
    Branch: the card thread's designated claude/project-thread-* branch, named in a claim amendment once the thread reports it; it starts from the Resume-from sha below
    Worktree: objectstack-issue-29920
    Domain: domain:server (cross-domain half carried from the previous claims 6670277222 and correction 6670407970: one additive interface in packages/spec/src/contracts/cloud-binding-service.ts, packages/spec/src/api/push-relay.zod.ts, packages/spec/src/system/push-config.zod.ts within the card's spec exception)
    Seat: domain:server#5
    File surface: exactly the released branch's surface: packages/services/service-push/**, packages/cloud-connection/** (the cloud-binding service), packages/core/src/stack-auth.ts (isControlPlaneDeclined moved there), packages/spec/src/contracts/cloud-binding-service.ts, packages/spec/src/api/push-relay.zod.ts, packages/spec/src/system/push-config.zod.ts, packages/spec/src/stack.zod.ts and their regenerated outputs, packages/services/service-messaging/src/push-channel.ts and one line of messaging-service-plugin.ts, tests, one changeset. ⛔ docs/adr/** (ADR-0012 §6.1 is #30670), ui/**, apps/**, skills/**, .claude/** (stop on breach; explain in the report)
    Container & model: M, mode:thread, model: default (contract-review-tier review owed on the schema face, run by the seat before ACCEPT), effort: high
    Clause-②: yes (narrowing) — strict data on the relay wire, BREAKING, carried from the card and the released changeset
    Metadata-protocol: none
    Responsibility: service-push cannot send through the hosted relay with the cloud binding credential | cloud-connection binding + service-push relay transport | servers bound to cloud that send push
    Thread-read: 6670797151
    Premise: holds per the release state (all ruled items coded on the branch); the dev re-runs the owed list on origin/main after one merge
    Serial constraints cleared: no open PR touches service-push, cloud-connection or the three spec files (open PR file lists read at claim time); #30324 (this seat, in flight) touches cloud-connection install seeding only, a disjoint file set; #30670 (ADR-0012 §6.1 docs) is a separate card
    Resume-from: claude/project-thread-tk5wos@634f4145cf0ecc7cd2e894e24604eded9910d19f

    Rulings carried, as recorded on this card: Q1 A, Q2 A, Q3 A (default relay path resolveCloudUrl() + /api/v1/cloud/push/send, comment 6670221824), Q4 C; summary in 6670237128 and correction 6670407970. Owed, in order (from the release): cli door contract test, verify harness test, full spec suite; the six ablations; one origin/main merge then check:generated; dispatch-gates, pnpm lint, platform checklist, check-governed-merges; full dogfood (BREAKING); draft PR + os-dev-report. Queueing needs the maintainer's per-PR word (BREAKING).

  10. objectstack-fleet commented on Nov 5, 2026

    @objectstack-fleet
    ContributorAuthor

    domain:server#5 · os-elon-musk · session_016WV4cyvF7SP65frVFAYLv7

    Claim amendment to 6671501964 · 2026-11-05T22:49Z
    Branch: claude/project-thread-yvu7tv
    Dev session: session_016WV4cznEfnAJ8CyWBdiL2G (the card thread, mode:thread), dispatched by this seat; the claim's other lines stand.

  11. objectstack-fleet commented on Nov 6, 2026

    @objectstack-fleet
    ContributorAuthor

    domain:server#5 · os-elon-musk · session_016WV4cyvF7SP65frVFAYLv7
    os-dev-report

    Dev card thread session_016WV4cznEfnAJ8CyWBdiL2G, branch claude/project-thread-yvu7tv (claim amendment 6671581752). Status: done. Draft PR #30749, head 1b82707d. Resumed from claude/project-thread-tk5wos@634f4145 (Release 6670797151); the branch was read against the rulings (Q1 A, Q2 A, Q3 A, Q4 C) and nothing contradicts them.

    • Two gate reds fixed on the branch: the changeset ADR-0087 marker (predicate 3 of runtime-export-retired; the wire narrowings are now prose, see the open question) and check:test-source-alias (two anchored service-push aliases).
    • Owed list: cli door contract test, verify harness test and the full spec suite green; seven ablations red and restored; one origin/main merge, check:generated up to date; 129/129 derived gates green and reconciled; pnpm lint and the platform checklist green; check-governed-merges --pr 30749: not governed; full dogfood green.
    • For the seat: the contract-tier review on the schema face, and the maintainer's per-PR word before queueing (BREAKING).
    {
      "issue": 29920,
      "status": "done",
      "branch": "claude/project-thread-yvu7tv",
      "pr": "https://github.com/objectstack-ai/objectstack/pull/30749",
      "session": "session_016WV4cznEfnAJ8CyWBdiL2G",
      "premise_still_valid": true,
      "summary": "Resumed os-bill's released WIP (tk5wos@634f4145, rulings Q1 A, Q2 A, Q3 A, Q4 C already coded; read against the rulings, nothing contradicts them), merged origin/main once (4bf9a2d7, clean), ran the owed list and opened draft PR #30749. The gate sweep found two real reds, both fixed on the branch: the changeset's ADR-0087 runtime-export-retired marker failed predicate 3 (FROM lines named the package and the two wire narrowings as operands; now FROM/TO names only PUSH_RELAY_NOT_SERVED_MESSAGE and the wire narrowings are prose, commit 2e35f600), and check:test-source-alias (service-push tests resolved @objectstack/spec/api and @objectstack/core through dist; two anchored aliases, commit 1b82707d). Final head 1b82707d.",
      "tests": "At 2d054bbe after the closure build, under os-verify-lock (VERDICT command-exit 0): cli serve-stack-email-sms-door.contract.test 10/10; verify harness.served-composition.test 10/10; spec full suite 817 files, 25796 passed, 1 todo; service-push 74/74 (re-run at 1b82707d: 74/74); service-messaging 849/849; cloud-connection 896/896; core 3087/3087; typecheck exit 0 for spec, core, cloud-connection, service-push, service-messaging, cli, verify. check:generated: 14/14 up to date. Seven ablations via scripts/ablation-replace.mjs, each landed (anchor 1->0, blob changed), went red and restored to blob == HEAD: 401 retryable (1 red), invalid mapping (1), unbound guard (2), init air-gap refusal (4), vendors() availability (6), strict relay data in spec (1), notice locale (2). No rebuild needed: the service-push and messaging tests import the mutated source relatively, the spec test reads spec source. Dispatch named 'six ablations' without listing them; these seven are the ones chosen here (Q4 C added).",
      "gates": "dispatch-gates --commands: 129 families (same list at 2d054bbe and 1b82707d). Pass 1 at 2d054bbe: check-adr-0087-registration exit 1 and check:test-source-alias exit 1 (fixed, above); check-plugin-teardown-shape --self-test, check:skill-examples, check:dual-build-cjs-loads exit 3 (prerequisites: fixture commit fetched, whole-tree pnpm build 118/118). Pass 2 at 1b82707d: 129/129 exit 0; --ran reconciliation: 129 derived, 129 run, 0 NOT-MEASURED, 0 UNRUN (exit 0). pnpm lint exit 0 at 2e35f600, vitest.config.ts re-linted at 1b82707d exit 0. check-platform-checklist --changed-from origin/main: OK, 11 anchors, 0 lost. check-governed-merges --pr 30749: NOT governed, 1624 changed lines (under 3000). CI check-runs on #30749: in_progress at report time.",
      "dogfood_battery": "2d054bbe · os-verify-lock VERDICT command-exit 0 · Test Files 421 passed, 3 skipped (424) · Tests 3513 passed, 36 skipped (3549) · no failures. The two later commits touch only the changeset text and a service-push vitest alias.",
      "mcp_calls": "3 — mcp__github__issue_read (get, get_comments) and its tool-schema load; no write tool",
      "api_writes": "2 — pr_create (#30749) and this os-dev-report comment, both through the fleet-write relay; plus 3 git pushes",
      "open_questions": [
        { "question": "Does the reviewer accept the changeset's runtime-export-retired disposition with the two wire narrowings (strict PushRelayPayloadSchema.data, RATE_LIMIT_EXCEEDED retryable: true) stated as prose rather than FROM/TO lines?", "options": ["A: accept; the marker's reason covers why the ledger has nothing to rewrite", "B: register an ADR-0087 entry for the wire narrowing (packages/spec/src/migrations, outside this card's file surface; needs a surface amendment)"], "recommendation": "A, because no metadata author writes the relay wire and nothing stores it, and every category but registered refuses a *.zod.ts narrowing" }
      ],
      "security_findings": "none",
      "out_of_scope_findings": [
        "carrier: none · noted, not filed: CLOUD_BINDING_SERVICE spelled in both cloud-connection and service-push (no dependency between them)",
        "carrier: none · noted, not filed: a 200 answer with no result for the token falls back to results[0] (one delivery per request, so they coincide)",
        "carrier: none · noted, not filed: OS_CLOUD_URL unset but the cloud-connection plugin's controlPlaneUrl option 'off' boots the relay and every send fails relay_address_not_published",
        "carrier: seat · content/docs/kernel/runtime-services/push-service.mdx (hand-written operator page) is edited on the released branch, outside the claim's listed file surface"
      ]
    }

    Report taken 2026-11-06T00:31Z.


    Generated by Claude Code

  12. objectstack-fleet commented on Nov 6, 2026

    @objectstack-fleet
    ContributorAuthor

    domain:server#5 · os-elon-musk · session_016WV4cyvF7SP65frVFAYLv7

    Claim amendment to 6671501964 · 2026-11-06T00:43Z · PR #30749
    File surface widened (seat, gate-forced): docs/adr/0012-notification-platform.md, the readings this PR makes false only: the Status line's amendment parenthesis ("the hosted relay … is not yet served, #29920"), the §4 push row's last cell, the §6 dead-token amendment note (L379), and §6.1's "What main serves" bullet, which anchors packages/services/service-push/src/push-plugin.ts#PUSH_RELAY_NOT_SERVED_MESSAGE, the export this PR retires (check:adr-symbol-anchors red on 1b82707d; an anchor-exempt marker is maintainer-only). Each amended passage quotes what it replaces, per the ADR's own convention. ⛔ The "in the device locale" half-sentence in §6.1's privacy bullet stays for #30670, as ruled.
    Also: content/docs/kernel/runtime-services/push-service.mdx (already edited; the page documented the refusal this PR removes) and the stale heading comment in packages/spec/src/system/push-config.test.ts (contract review 6673473934, should-fix 3).
    Consequence: docs/adr/** is Tier H, so the PR is governed: approval by a GOVERNED_APPROVER, plus the maintainer's per-PR word for BREAKING. The claim's other lines stand.

  13. objectstack-fleet commented on Nov 6, 2026

    @objectstack-fleet
    ContributorAuthor

    domain:server#5 · os-elon-musk · session_016WV4cyvF7SP65frVFAYLv7

    Landed · 2026-11-06T05:39Z · PR #30749

    Probe Reading
    PR #30749 merged, merge commit 97e42498, through the merge queue, enqueued and merged by os-zhuang (a GOVERNED_APPROVERS account; the human merge is the governed approval record). Tier H (the ADR-0012 hunks). The maintainer's per-PR BREAKING word is recorded on the PR (6676435219). Seat ACCEPT 6674089209 and contract review delta PASS 6674074177 are both on head d14d749e
    #29920 closed by Fixes #29920

    Shipped: service-push sends through the hosted relay transport, per rulings Q1 A, Q2 A, Q3 A and Q4 C. ADR-0012's four "relay not served" readings are amended in the same PR. Follow-ups still open: #30762 (the review's should-fix 1 and 2: the cloud-binding service name in its spec contract, and the controlPlaneUrl empty-string reading), and #30670 (ADR-0012 §6.1 "device locale" wording plus the tree pin on "What main serves"; claimed by domain:server#5 and now unblocked).

    State change: pm:dispatched removed, assignee cleared.

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

Metadata

Metadata

Assignees

No one assigned

    Labels

    domain:serverDomain lane — seat card indexed by label:pm:seatenhancementNew feature or requestpriority:p2Medium: important, M3

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions