Skip to content

spec(notification): the push device registry's protocol face and the push payload route (spec slice of #29041) #29067

Closed

Description

@objectstack-fleet

domain:server#5 · os-project-manager · session_01FEkQ5oc56xChD6Wrc4cGSt

Filing gate: the packages/spec/** slice of #29041 (p1, triage 2026-11-02 「协议新增定 p1」), split off by domain:server seat 5 so that no server dev order carries a spec hunk. The server runtime slices are blocked on this card. Graded p1, the same as its parent (a protocol addition: new served routes, a new system object, a new channel's payload contract). Graded under 「定级你可以自己决定」 (Leehom, 2026-11-01). Contract review at CONTRACT_REVIEW_TIER is owed (declaration face).

Reach: the mobile shell (objectstack-ai/mobile#144) and the Console (#29042), which register a native APNs/FCM token under the signed-in session; authors of notify flow nodes whose deliveries go to phones.
Reader who acts: the domain:spec seat.

What is there on main

Wanted (the protocol face only, ⛔ no runtime)

  1. Device registration contract. Revise or replace RegisterDeviceRequestSchema / UnregisterDevice* so that one request registers or refreshes a device for the current user. It carries vendor (apns | fcm in core; ADR-0012 routes HMS, Xiaomi, OPPO and vivo to plugins), token, a client-generated deviceId that is stable per app install, platform, an optional appId and locale. The response names the device. The removed fields' disposition goes under ADR-0087 if any field an author can write changes.
  2. Route rows. Restore the device routes in DEFAULT_NOTIFICATION_ROUTES (register/refresh and unregister, under /api/v1/notifications/devices), with request and response schemas. Preferences stay out (not asked for).
  3. The stored device shape. A UserDeviceSchema per ADR-0012 §6, narrowed to what core ships. State:
    • (a) whether a device row belongs to a user or to a user and an organization;
    • (b) that a token registered by a second user moves to that user (one live owner per token);
    • (c) the invalidation fields (enabled, invalidated_at).
  4. INotificationService device members (register, unregister, list for a user, invalidate by token), as optional members so that existing implementations stay valid.
  5. Push payload contract. A push delivery carries a Console route (for example /apps/crm/opportunity/123): a relative path only, and an absolute or off-server URL is refused or dropped at the contract. State where it comes from: an explicit notify key, or derived from the node's existing sourceObject / sourceId. Prefer derivation if it needs no new authorable key.
  6. Push transport configuration. An APNs (token auth: team id, key id, topic/bundle id, sandbox or production, the .p8 key by credentialRef) and FCM HTTP v1 (project id, service-account JSON by credentialRef) config shape. Secrets go only by reference (ADR-0143 D3), never inline. Place it where per-environment email transport selection lives, or say why not. If hosted environments need a cloud-side surface, that half goes to domain:cloud (triage note on Push channel: build the device registry routes (/notifications/devices) and the APNs + FCM transport that ADR-0012 marks "MUST ship M1" #29041).
  7. Update NotificationChannelSchema's JSDoc once a server slice registers push (that can ride the server slice; name the hand-off).

Tests: schema round-trip and refusal cases (an off-server route, an unknown vendor, an inline secret), and the route catalog test that pins DEFAULT_NOTIFICATION_ROUTES.

Declarations: a widening (Clause-②: yes). Metadata-protocol per what changes; ADR-0087 disposition if any published field is renamed or removed.

Blocked-by: none.
Blocks: the server device-registry slice (filed beside this card) and #29041 (push channel).

Dedupe: searched device registry spec push token route, RegisterDeviceRequestSchema; only #29041 and #29042. New.

Links back: #29041, #29042, ADR-0012 §6 / §10 / §11, ADR-0143 D3.

Activity

  1. objectstack-fleet commented on Nov 2, 2026

    @objectstack-fleet
    ContributorAuthor

    domain:server#5 · os-project-manager · session_01FEkQ5oc56xChD6Wrc4cGSt

    Claim: PM loop round 1 · card thread · 2026-11-02T06:58Z
    Session: session_01FEkQ5oc56xChD6Wrc4cGSt (the seat); the card thread is card thread cmsg_01TenzsRopZj4jMbvjX8uz1X73gbsd3Etq79gjkhHnJ4d7
    Account: os-project-manager (the seat's linked user; the card's assignee)
    Branch: claude/project-thread-d3h66h (the card thread's harness-pinned branch; it names no issue, so this claim is the identity record)
    Worktree: the card thread's own container checkout
    Domain: domain:server
    Seat: domain:server#5
    Ruling-ref: 「定级你可以自己决定」 (Leehom, 2026-11-01T12:11Z) with the card's grade and this seat's rulings in the order; seat 5 refill agreed with seats 1–3 (2026-11-02T06:48Z); this domain:spec slice of #29041 is ordered by a server seat because no spec-lane seat runs in this project (coordinator, the #28508 precedent); contract review owed at the declaration face.}).}
    File surface: packages/spec/src/api/protocol.zod.ts, packages/spec/src/contracts/notification-service.ts, the push payload and transport schemas' home in packages/spec/src/**, generated pins, tests, changeset; ⛔ no route rows (they ride #29068), ⛔ no packages/runtime/** / packages/services/** / packages/client/**
    Container & model: S, mode:thread, model and effort as the thread was opened
    Clause-②: yes (widening only)
    Metadata-protocol: none
    Responsibility: the push device registry's wire and contract face: widened RegisterDevice*, optional INotificationService device members, push payload route, APNs/FCM transport config by reference
    Thread-read: none
    Premise: holds (read at origin/main d8c611d9: no device route, no device contract member, no push payload or transport shape exists in spec)
    Serial constraints cleared: none; #29068 and #29041 wait on this card
    Resume-from: none — fresh from origin/main

    State: pm:queue → pm:dispatched, assigned to os-project-manager, recorded by this claim. Order: pm-orders/server/order-29067.md in the project.

  2. objectstack-fleet commented on Nov 2, 2026

    @objectstack-fleet
    ContributorAuthor

    domain:server#5 · os-project-manager · session_01FEkQ5oc56xChD6Wrc4cGSt

    os-dev-report
    {
    "issue": 29067,
    "status": "rework",
    "branch": "claude/project-thread-d3h66h",
    "pr": "#29101",
    "session": "session_01FEkQ5oc56xChD6Wrc4cGSt",
    "premise_still_valid": true,
    "summary": "All seven rulings implemented at head 088613b: RegisterDeviceRequestSchema widened with optional vendor (apns|fcm), appId, locale (deviceId refresh / server-minted id stated in describes); INotificationService gains optional registerDevice / unregisterDevice / invalidateDeviceToken with the three stated semantics; new api/push-delivery.zod.ts (PushDeliveryDataSchema, PushRouteSchema: one leading slash, no backslash, whitespace or control character; route derived from actionUrl, no notify key); new system/push-config.zod.ts beside email-config.zod.ts (strict APNs token-auth and FCM HTTP v1 blocks, secrets by keyRef / serviceAccountRef, inline secret refused by name and as reference content). No route rows, no UserDeviceSchema, NotificationChannelSchema JSDoc unchanged. Status rework only because the maintainer called wrap-up before the local gate battery and the final origin/main merge ran.",
    "tests": "Red first at 8dcce8b (base 6b3ba82): register-device-request 6 failed (widened round-trip + 5 vendor refusals), push-delivery and push-config failed at import. Green at 088613b under os-verify-lock: VERDICT command-exit 0, Test Files 7 passed (7), Tests 327 passed (327) over register-device-request, push-delivery, push-config, notification-service, protocol, plugin-rest-api, type-alias-convention.pin. No ablation (new schemas, red-first covers them).",
    "gates": "Local: pnpm --filter @objectstack/spec build exit 0; check:generated --fix exit 0 after regenerating api-surface, export-origins, declaration-map, docs, strictness-ledger (plus gen:schema for authorable-surface and json-schema.manifest); check:docs exit 0; node scripts/check-spec-parsed-alias.mjs exit 0 (817 pinned). NOT MEASURED: dispatch-gates --commands battery and --ran reconciliation, full spec pnpm test / typecheck, scoped pnpm lint, final origin/main merge; reason: maintainer wrap-up. CI check-runs on PR 29101: in_progress at report time.",
    "dogfood_battery": "n/a — no ! in the PR title and no (narrowing) arm",
    "mcp_calls": "0",
    "api_writes": "2 — fleet-write pr_create (PR 29101); this os-dev-report comment via post-stamped",
    "open_questions": [],
    "out_of_scope_findings": [
    "carrier: #29068 · DEFAULT_NOTIFICATION_ROUTES JSDoc says (device registration, preferences, listing) while the table lists only listing; false on main before this PR, left byte-identical per ruling 2, noted in PR acceptance notes"
    ]
    }


    Generated by Claude Code

  3. objectstack-fleet commented on Nov 2, 2026

    @objectstack-fleet
    ContributorAuthor

    domain:server#5 · os-project-manager · session_01FEkQ5oc56xChD6Wrc4cGSt

    Parked at the maintainer's wrap-up · 2026-11-02T07:21Z

    Draft PR #29101 (head 088613be) is handed back with status rework (os-dev-report 6571436526). All seven rulings are implemented and the touched suites are green, but the dispatch-gates --commands battery, the full spec test and typecheck, the scoped lint and the final origin/main merge are NOT MEASURED. The declaration-face contract review is still owed. The PR stays a draft and is not landed in this round. The next seat session resumes from this head: finish the unmeasured gates, then the contract review, then ACCEPT. #29068 and #29041 stay blocked.

  4. objectstack-fleet commented on Nov 2, 2026

    @objectstack-fleet
    ContributorAuthor

    domain:server#5 · os-project-manager · session_01FEkQ5oc56xChD6Wrc4cGSt

    Release: session_01FEkQ5oc56xChD6Wrc4cGSt · seat 5 signs off at the maintainer's wrap-up · back to pm:queue for the next domain:server seat
    Resume-from: claude/project-thread-d3h66h@088613be

    The maintainer's words, verbatim: 「尽快提交代码,然后收工」 (2026-11-02T07:10Z) and 「自动合并中的也交给下一任跟进」 (2026-11-02T07:23Z). This is the default hand-back; seat 5 keeps no stay-behind.

    Next: draft PR #29101 carries all seven rulings of pm-orders/server/order-29067.md (os-dev-report 6571436526, status rework). Still owed: the dispatch-gates --commands battery, the full spec test and typecheck, the scoped lint and the final origin/main merge, then the declaration-face contract review before ACCEPT. No spec-lane seat runs in this project, so a server seat orders it (coordinator's #28508 precedent). #29068 and #29041 stay blocked on this card.

  5. objectstack-fleet commented on Nov 2, 2026

    @objectstack-fleet
    ContributorAuthor

    domain:spec#1 · os-steve · session_01ADHkmPi25XXKx92Xmg4viS

    Claim: PM loop round 2 (taken over from domain:server#5, release 6571715272) · 2026-11-02T08:13Z
    Session: session_01ADHkmPi25XXKx92Xmg4viS (seat); dev = card thread session_01ADHkmT6K7dHh79Lej3sWr5
    Account: os-steve
    Branch: claude/project-thread-cjde2j (reset to 088613be, carrying the previous dev's three commits unchanged; a superseding draft PR replaces #29101, which the seat closes as superseded)
    Worktree: the card thread's own checkout of objectstack-ai/objectstack
    Domain: domain:spec
    Seat: domain:spec#1
    File surface: the files PR #29101 already carries (packages/spec/src/api/protocol.zod.ts device request/response schemas, packages/spec/src/api/push-delivery.zod.ts, packages/spec/src/system/push-config*, packages/spec/src/contracts/notification-service.ts, the api/system index exports and their tests), DEFAULT_NOTIFICATION_ROUTES in packages/spec/src/api/plugin-rest-api.zod.ts and its catalog test, any migration entry ADR-0087 owes, the ledger rows and generated references the spec gates name, root .changeset/; no runtime, no cloud-side surface (stop on breach; explain in the report)
    Container & model: L, mode:thread, model: default — the tier is the PM's per-card call (floor sonnet · default opus · ceiling fable); one CONTRACT_REVIEW_TIER review (declaration face) before landing, effort: xhigh
    Clause-②: yes (widening)
    Metadata-protocol: added
    Responsibility: n/a — not a defect card
    Thread-read: 6571715272
    Premise: holds (on origin/main RegisterDeviceRequestSchema and its siblings are route-less protocol-method contracts, DEFAULT_NOTIFICATION_ROUTES lists list/read/read-all only, and INotificationService has no device member; PR #29101 at 088613be carries the seven rulings of report 6571436526, status rework)
    Serial constraints cleared: none (the seat's other in-flight cards #28914, #28912, #28896, #28993 and #29023 hold disjoint files)

    Ruling-ref: the card (Wanted 1–7) and the rulings carried in os-dev-report 6571436526. Every new public name waits for the maintainer's approval before landing. Declared-not-served routes and schemas carry the house planned channel (ADR-0049) until #29068 serves them.

    Amended: the file surface adds packages/spec/src/system/push-device.zod.ts and its test (Wanted 3, the stored UserDeviceSchema). The device route rows in DEFAULT_NOTIFICATION_ROUTES are NOT added here. The house has no planned channel for a route row (handlerStatus: 'planned' was retired under ADR-0049: "an endpoint that has no handler yet is simply not registered"), and the runtime request-schema conformance test reds a catalog POST row with no served case. The rows land with their handlers in the server slice #29068, which flips the catalog pin. The schemas carry [PLANNED — not enforced]-family describe markers until then (the dev's reading in the seat thread).

  6. objectstack-fleet commented on Nov 2, 2026

    @objectstack-fleet
    ContributorAuthor

    domain:spec#1 · os-steve · session_01ADHkmT6K7dHh79Lej3sWr5
    os-dev-report

    {
      "issue": 29067,
      "status": "done",
      "branch": "claude/project-thread-cjde2j",
      "pr": "https://github.com/objectstack-ai/objectstack/pull/29218",
      "session": "session_01ADHkmT6K7dHh79Lej3sWr5",
      "premise_still_valid": true,
      "summary": "Resumed the previous dev's three commits (088613be, PR #29101) on this thread's own branch, merged origin/main through os-regen-merge.sh, and opened the superseding draft PR #29218 (head 73fabdf2). Added on top, card over report: UserDeviceSchema (system/push-device.zod.ts) with rules (a) user-owned, (b) one live owner per token, (c) dead token keeps its row disabled; INotificationService.listDevices; APNs/FCM secrets keyed credentialRef (ADR-0143 D3) instead of keyRef/serviceAccountRef; [PLANNED — not yet served|read] describe markers on every device, delivery, stored-device and transport schema, tracker ids only in // comments; changeset minor + Metadata-protocol added; llms.txt counts. No route row (Wanted 2): measured, no house planned channel for route rows exists, so they ride #29068 with their handlers (seat agreed, claim amended).",
      "tests": "Source at ed89754e (final head 73fabdf2 adds only the llms.txt count fix). Under os-verify-lock: spec build + check:generated --fix VERDICT 0, check:generated re-run all 14 up to date VERDICT 0; pnpm --filter @objectstack/spec test VERDICT 0, Test Files 794 passed (794), Tests 23423 passed 1 todo; spec typecheck VERDICT 0 (incl. check:test-typecheck); closure build VERDICT 0; service-messaging test 62/62 files, 759 tests, typecheck 0; runtime notification-schema-conformance + request-schema-gate.conformance 2/2 files, 25 tests, typecheck 0; client typecheck 0. Scoped eslint --no-inline-config over 13 changed .ts files exit 0. Ablation: push-config.zod.ts replaced by its 088613be bytes (on-disk counts: keyRef 1, PLANNED 0) → push-config.test.ts exit 1, 19 of 21 failed; restored via git checkout HEAD, blob hash = HEAD, git diff HEAD empty → exit 0, 21 passed; trap-guarded. Spec tests import src, so no rebuild leg was needed. Dist proof: packages/spec/dist/contracts/index.d.ts carries listDevices, dist/system/index.d.ts exports UserDeviceSchema.",
      "gates": "Local, final head 73fabdf2: node scripts/pm/dispatch-gates.mjs --commands --repo objectstack-ai/objectstack derived 103; all 103 exit 0 (stdin closed, exit captured before any pipe); --ran: 103 derived, 103 run, 0 NOT-MEASURED (derived zero), 0 UNRUN. First round at ed89754e: 102/103 exit 0, check:llms-txt exit 1 (system 37 vs 39, api 33 vs 34, heading 213 vs 216), fixed in 73fabdf2. Prerequisite: turbo build packages/** VERDICT 0. check-changeset-no-major --base origin/main --event (PR body): exit 0, level axis yes (widening) + added. CI check-runs on PR 29218: not started at report time.",
      "dogfood_battery": "n/a — no ! in the PR title and no (narrowing) arm",
      "mcp_calls": "4 — issue_read get x1, issue_read get_comments x2, pull_request_read get x1 (read-back of PR 29218); no write tool",
      "api_writes": "2 — fleet-write relay pr_create (PR 29218, read-back 11062/11062 bytes identical, assignees none); this os-dev-report comment via post-stamped",
      "files_changed": "36 vs merge base 2af0ae2a: 13 hand-written under packages/spec/src (protocol.zod.ts, push-delivery.zod.ts, push-config.zod.ts, push-device.zod.ts new, notification-service.ts, api/index.ts, system/index.ts, 5 test files of which push-device.test.ts new, type-alias-convention.pin.test.ts), .changeset/29067-push-device-registry-spec.md, packages/spec/llms.txt, and 21 generated (content/docs/references x10, api-surface x2, authorable-surface x2, declaration-map x2, export-origins x2, json-schema.manifest x2, strictness-ledger counts x2)",
      "line_budget": "+1222 / -22 total; hand-written (src + changeset + llms.txt) +827 / -11, of which tests +408 / -3; the rest is regenerated",
      "deviations": [
        "Branch: this thread's own harness branch claude/project-thread-cjde2j, reset to 088613be, with a superseding PR #29218 instead of pushing to claude/project-thread-d3h66h / PR #29101 (this session may push only its own branch). Seat agreed; the seat retires #29101.",
        "File surface: added packages/spec/src/system/push-device.zod.ts + test (seat amended the claim) and packages/spec/llms.txt (hand-kept file check:llms-txt names).",
        "Card Wanted 2 not built: no route rows in DEFAULT_NOTIFICATION_ROUTES (see open_questions 1); the card wins over the report elsewhere (UserDeviceSchema, listDevices, credentialRef).",
        "authorable-surface/system.json: build-schemas refused the two branch-only keys keyRef/serviceAccountRef as vanished; took main's shard (git restore --source=origin/main) and regenerated with gen:schema. Neither key was ever on main or released.",
        "Tests cited at ed89754e; 73fabdf2 changes only llms.txt, so the suites were not re-run; the full gate battery was re-run at 73fabdf2.",
        "test:repo (vitest repo project) not run locally: the diff touches none of its tests and the derivation does not name it; CI Test Core runs it.",
        "check-platform-checklist not run: no exported symbol renamed or deleted.",
        "Not subscribed to PR activity and no MCP assignee/reviewer writes on the PR: the standard order and os-dev forbid both (dispatch contract over the platform posture)."
      ],
      "handoff_slices": [
        "domain:server #29068: mount POST /api/v1/notifications/devices (register/refresh) and the unregister route; add both rows to DEFAULT_NOTIFICATION_ROUTES with RegisterDeviceRequestSchema / RegisterDeviceResponseSchema / UnregisterDevice* ; flip packages/spec/src/api/plugin-rest-api.test.ts (toHaveLength(3), not.toContain('/devices')); add the violating-body case to packages/runtime/src/request-schema-gate.conformance.test.ts; implement registerDevice / unregisterDevice / listDevices / invalidateDeviceToken and sys_user_device per UserDeviceSchema rules (a)(b)(c); remove the [PLANNED — not yet served] markers on the four device schemas and UserDeviceSchema; correct the DEFAULT_NOTIFICATION_ROUTES JSDoc.",
        "domain:server #29041: the push channel produces PushDeliveryData (route from actionUrl via PushRouteSchema, dropped when it fails), reads PushServiceConfig through a configuration door it adds with its reader, calls invalidateDeviceToken on APNs BadDeviceToken/Unregistered and FCM UNREGISTERED; removes the [PLANNED] markers on PushDeliveryDataSchema and the transport schemas; updates NotificationChannelSchema JSDoc (Wanted 7).",
        "domain:cloud (only if hosted environments need it): a per-environment surface that resolves the APNs .p8 and FCM service-account credentialRef in the hosted secrets layer. Not built here."
      ],
      "open_questions": [
        {
          "question": "Card Wanted 2 asks for the device route rows in DEFAULT_NOTIFICATION_ROUTES now. Measured: no house planned channel exists for a route row (handlerStatus 'planned' retired under ADR-0049 with 'an endpoint that has no handler yet is simply not registered', HANDLER_STATUS_RETIRED in plugin-rest-api.zod.ts; the table's own comment on the device rows' earlier removal; packages/runtime/src/request-schema-gate.conformance.test.ts reds any dispatcher-catalog body route with a requestSchema and no served case; the liveness ledger governs metadata types only). Land the rows here, or with their handlers?",
          "options": ["A: rows land with their handlers in #29068 (built: no rows here)", "B: add the rows here, which needs a new planned channel for route rows and a red runtime gate until #29068 lands"],
          "recommendation": "A, because the house cure for declared-not-served routes is not to register them, and B reintroduces the exact defect the table's comment records; seat agreed, claim amended."
        },
        {
          "question": "New public names awaiting the maintainer's approval before landing: schemas UserDeviceSchema/UserDevice, PushDeliveryDataSchema/PushDeliveryData, PushRouteSchema/PushRoute, PushServiceConfigSchema/PushServiceConfig, ApnsTransportConfigSchema/ApnsTransportConfig, FcmTransportConfigSchema/FcmTransportConfig, PushVendorSchema/PushVendor (values apns, fcm); RegisterDeviceRequest keys vendor, appId, locale; UserDevice keys deviceId, userId, vendor, token, platform, appId, locale, name, enabled, invalidatedAt, lastSeenAt; PushDeliveryData keys notificationId, route; transport keys apns, fcm, teamId, keyId, topic, environment (sandbox|production), projectId, credentialRef; INotificationService members registerDevice, unregisterDevice, listDevices, invalidateDeviceToken. No route path is added here (#29068 proposes POST /api/v1/notifications/devices and its unregister path).",
          "options": ["A: approve as listed", "B: rename named items before landing"],
          "recommendation": "A, because every key follows an existing house spelling (credentialRef from ADR-0143 D3, deviceId/token/platform/name from the published RegisterDeviceRequest, ADR-0012 section 6 for the device fields in camelCase)."
        },
        {
          "question": "The card reads as if vendor and a client-generated deviceId are always carried. Both stay optional (vendor derived from platform; absent deviceId minted by the server), as the previous dev ruled. Keep them optional?",
          "options": ["A: keep optional (built), a pure widening", "B: require vendor and deviceId, a narrowing of the published request that needs Clause-② (narrowing), a major changeset and an ADR-0087 disposition"],
          "recommendation": "A, because the card declares a widening and requiring either refuses bodies that parse today."
        },
        {
          "question": "Implementation choices stated in the contract, for review: (a) a device belongs to its user only, no organization member; (b) a token taken over by a second user removes the first user's row for it; a web registration without vendor is refused (nothing to derive); listDevices answers disabled devices too, newest lastSeenAt first.",
          "options": ["A: keep as stated", "B: scope devices to user + organization, or keep the displaced row disabled instead of removing it"],
          "recommendation": "A, because a phone receives its signed-in person's notifications from any organization, and a token row cannot exist without its token."
        }
      ],
      "out_of_scope_findings": [
        "carrier: #29068 · DEFAULT_NOTIFICATION_ROUTES JSDoc names device registration and preferences while the table lists only the inbox routes; false on main before this PR; noted in the PR acceptance notes"
      ]
    }

    Stamped 2026-11-02T09:43Z.


    Generated by Claude Code

  7. objectstack-fleet commented on Nov 2, 2026

    @objectstack-fleet
    ContributorAuthor

    domain:spec#1 · os-steve · session_01ADHkmPi25XXKx92Xmg4viS

    Decision needed · new public names · 2026-11-02T14:42Z

    维护者速读

    Seat recommendation: A. The seat lands PR #29218 once the ruling is on this card.

  8. objectstack-fleet commented on Nov 2, 2026

    @objectstack-fleet
    ContributorAuthor

    domain:spec#1 · os-steve · session_01ADHkmPi25XXKx92Xmg4viS

    Decision withdrawn · 2026-11-02T17:08Z

    The seat withdraws its "Decision needed" comment 6579984959. That question was only about names (UserDeviceSchema, the push schemas and their keys). pm-dispatch has no maintainer gate for choosing a public name: the seat picks it, and only a real semantic or contract split goes to the maintainer. No such split is open here. The seat keeps the recommended names and lands PR #29218 on its CONTRACT_REVIEW_TIER PASS. needs-user-decision is removed.

  9. objectstack-fleet commented on Nov 2, 2026

    @objectstack-fleet
    ContributorAuthor

    domain:spec#1 · os-steve · session_01ADHkmPi25XXKx92Xmg4viS

    Landed · 2026-11-02T19:19Z

    Probe Reading
    PR #29218 MERGED 2026-11-02T19:03Z, merge commit e9fb529317, through the merge queue
    #29067 closed completed by Fixes #29067

    pm:dispatched is removed. The seat ACCEPTed, and the CONTRACT_REVIEW_TIER review passed. The seat carried that PASS to the merge-only head 91892164, which was a strictness-ledger regen.

    What changed: the push channel's protocol face (ADR-0012 §6), the packages/spec slice of #29041. It covers the device registration widening, the stored device shape, the device contract members, the push route, and APNs/FCM config by credentialRef. All of it is declared ahead of its producer, so every new or touched schema carries a [PLANNED — not yet served] / [PLANNED — not yet read] marker.

    Unblocked: #29068 (server device registry) owns the device route rows and their handlers, the web-without-vendor refusal, the request-schema-gate violating-body case, and the catalog pin flip in plugin-rest-api.test.ts. #29041 (push channel) can build on this face.

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