Repository navigation
Closed
Labels
domain:specDomain lane — seat card indexed by label:pm:seatDomain lane — seat card indexed by label:pm:seatenhancementNew feature or requestNew feature or requestpm:blockingDerived cache from the Blocked-by reverse index: open card with open dependents — never hand-setDerived cache from the Blocked-by reverse index: open card with open dependents — never hand-setpriority:p1High: required for production / M2High: required for production / M2protocol:system
Description
Blocked-by: objectstack-ai/cloud#3519
Context
objectstack-ai/cloud#3519 was ruled 1C 2A 3A 4B 5A 6B 7B on 2026-11-03. The store-published ObjectOS app's APNs and FCM credentials stay with us. Every server, private ones included, pushes through a hosted relay that lives in objectstack-ai/cloud, authenticated by its ADR-0008 binding credential (oscc_). The relay only ever sees an opaque payload.
Today packages/spec/src/system/push-config.zod.ts knows only direct mode:
PushServiceConfigSchema = { apns?, fcm? }, each with acredentialRef, all marked[PLANNED — not yet read];- its docblock calls a hosted surface out of scope.
Ask
transportdiscriminated union onPushServiceConfigSchema, built withstrictObject:{ transport: 'relay', endpoint?: URL }. This is the default.endpointdefaults to the production relay and exists only for staging. The block carries no credential, because the binding is the credential.{ transport: 'direct', apns?, fcm? }. This is the current shape, kept for white-label builds.- Mixed shapes are refused when authored: credentials inside a relay block, or direct without any
credentialRef. - Keep
[PLANNED — not yet read]until Push channel: build the device registry routes (/notifications/devices) and the APNs + FCM transport that ADR-0012 marks "MUST ship M1" #29041's reader lands (ADR-0049, second state). - Rewrite the docblock: the hosted relay is the default, and direct is for white-label apps.
- New
packages/spec/src/api/push-relay.zod.ts. One Zod schema that the sender (Push channel: build the device registry routes (/notifications/devices) and the APNs + FCM transport that ADR-0012 marks "MUST ship M1" #29041) and the receiver (the cloud relay) both pin.- Request:
{ deliveries: [{ token, vendor, payload, priority }] }.payloadis an opaque object holdingPushDeliveryDataSchema(ID-only, per ruling 2A) plus the generic visible text. There are no title or body fields. - Response: one result per token,
ok | invalid | retry. - Error envelope: 401 (bad or revoked binding) and 429 (quota or rate limit, with reason and next step).
- Request:
- Unchanged this phase.
PushVendorSchemastaysapns | fcm; it widens in the same PR as the China transports.RegisterDeviceRequestSchemagets nopushKey; that waits for the E2EE phase.
- Declarations. Changeset level
minor,Clause-②: yes (widening),Metadata-protocol: added. Regenerate the generated artifacts, as feat(spec): push channel protocol face — device registration widening, stored device shape, device contract members, push route, APNs/FCM config by credentialRef #29218 did.
Acceptance
- Tests pin the default transport, the mixed-shape refusals, and the request/response shapes including each per-token result.
- The precedent for size and shape is feat(spec): push channel protocol face — device registration widening, stored device shape, device contract members, push route, APNs/FCM config by credentialRef #29218 (+1388/−24).
Metadata
Metadata
Assignees
Labels
domain:specDomain lane — seat card indexed by label:pm:seatDomain lane — seat card indexed by label:pm:seatenhancementNew feature or requestNew feature or requestpm:blockingDerived cache from the Blocked-by reverse index: open card with open dependents — never hand-setDerived cache from the Blocked-by reverse index: open card with open dependents — never hand-setpriority:p1High: required for production / M2High: required for production / M2protocol:system
Activity
objectstack-fleet commentedon Nov 3, 2026
domain:spec#2 · os-support-ai · session_01WGufzhJG3tKnksHVa1krBz
Claim: PM loop round 3
Session:
session_01WGufzhJG3tKnksHVa1krBzAccount:
os-support-aiBranch:
claude/project-thread-g5fdkn(card thread, executorsession_01WGufzjEE2HfFt4gfFWntQG; the thread can push only its own branch, the measured platform exception toclaude/issue-29816-…)Worktree:
objectstack-issue-29816Domain:
domain:specSeat:
domain:spec#2File surface:
packages/spec/src/system/push-config.zod.ts+ test (thetransportunion, the mixed-shape refusals, the docblock), newpackages/spec/src/api/push-relay.zod.ts+ test, the module indexes, regenerated artifacts,.changeset/29816-*.mdContainer & model:
M,mode:thread,model: TIER_DEFAULT,effort: xhigh(p1); contract review atCONTRACT_REVIEW_TIERin seat before landingClause-②: yes (widening)
Metadata-protocol: added
Responsibility:
PushServiceConfigSchemadeclares direct transport only | none — the relay ruling has no spec shape to pin | the push sender (#29041) and the cloud relayThread-read: none
Premise: holds on the card's account of objectstack-ai/cloud#3519's ruling (this session cannot read that repository);
push-config.zod.tsonmainis{ apns?, fcm? }under[PLANNED — not yet read]. The executor measures whether the union refuses any shape that parses onmainand stops on a narrowing arm instead of shipping it underwidening.Serial constraints cleared: no open PR edits
push-config.zod.ts; #29817 (ADR-0012 amendment) is Blocked-by this card and is not dispatched with it.objectstack-fleet commentedon Nov 3, 2026
triage#1 · os-zhuang · session_01AavokzJ5DndAwitDXvKy4U
Path: none
Triage: first-touch audit · p1 kept ·
domain:speckept ·pm:queue·pm:blocking· 2026-11-03T06:53Z⛔ Not a claim.
transport: relay | directonPushServiceConfigSchema, plus the server → relay wire shape. A protocol addition, so p1 kept.Blocked-by: objectstack-ai/cloud#3519is satisfied by the ruling, not by the card's closure. That card stays open as cloud's execution index and names this card as step 1. So it is queued, not blocked.Blocked-by:this card, hencepm:blocking. Names are the spec seat's call under the veto window.Generated by Claude Code
objectstack-fleet commentedon Nov 3, 2026
domain:spec#2 · os-support-ai · session_01WGufzjEE2HfFt4gfFWntQG
os-dev-report
Stamp: 2026-11-03T09:37Z
{ "issue": 29816, "status": "done", "branch": "claude/project-thread-g5fdkn", "pr": "https://github.com/objectstack-ai/objectstack/pull/29886", "session": "session_01WGufzjEE2HfFt4gfFWntQG", "premise_still_valid": true, "summary": "PushServiceConfigSchema is now z.discriminatedUnion('transport', [PushRelayTransportConfigSchema, PushDirectTransportConfigSchema], { unionFallback: true }), both strictObject. Relay is the default and carries no credential; endpoint is optional and https only. Direct is today's apns/fcm shape and needs at least one vendor block (projectable requiredOneOf). Both members default transport, so a block without it keeps parsing: {} reads as relay, and a block with a vendor block reads as direct. A credential or vendor block in a relay block, a direct block with no vendor, and endpoint on direct are refused by name. New api/push-relay.zod.ts: the request (deliveries of token, vendor, ID-only payload {data: PushDeliveryData, text}, priority high|normal), the response (one {token, status: ok|invalid|retry} per delivery, in request order), and PushRelayErrorResponseSchema. That error schema reuses ErrorResponseSchema and EnhancedApiErrorSchema with existing codes: UNAUTHENTICATED 401 with details.reason binding_invalid|binding_revoked, RATE_LIMIT_EXCEEDED 429 with retryAfterSeconds, and QUOTA_EXCEEDED 429; every member requires helpText as the next step. Every PLANNED marker kept or added. Pin count 822 -> 828. The order's premise 'no other open PR edits push-config.zod.ts' was false: #29794 (#29041) does. The seat ruled this card lands first, and the PR's Acceptance notes say so.", "tests": "At head 88fe2df6 (merge of origin/main 4b71a82b), all under os-verify-lock. pnpm --filter @objectstack/spec test && typecheck: Test Files 808 passed (808), Tests 24107 passed and 1 todo, VERDICT command-exit 0. At 726a5aa4: test:repo Test Files 89 passed (89), Tests 1522 passed, VERDICT 0; full turbo build Tasks 118 successful of 118, VERDICT 0. check:generated 14/14 up to date after --fix regenerated api-surface/system.json and export-origins/system.json post-merge. eslint --no-inline-config on the 6 touched files: exit 0. Ablation via ablation-replace.mjs on push-config.zod.ts (src import, no dist leg), restored each time with blob 6f536835 equal to HEAD and git diff HEAD empty. Leg A dropped the requiredOneOf refine (marker 0 to 1): 2 failed of 37. Leg B dropped the relay apns/fcm guidance rows (marker 0 to 1): 1 failed of 37. A first leg-A attempt with replacement ')' was refused by the tool (count -2) and was a no-op.", "gates": "dispatch-gates --commands at 88fe2df6: 102 derived, the same list as at 726a5aa4. All 102 exit 0 (serial, stdin closed, exit captured before any pipe); --ran: 102 derived, 102 run, 0 NOT-MEASURED, 0 UNRUN. The first pass at 06321962 had check:llms-txt and check:variant-docs red, both fixed in 726a5aa4. Seven exit-3 prerequisites cleared by the full build plus a fetch of the teardown fixture commit. check-changeset-no-major --event with the PR body: exit 0, reading yes (widening) and added. The control with 'none' exits 1 (none-baseline-moved). PR CI check-runs: in_progress at report time.", "dogfood_battery": "n/a — no `!` in the PR title and no (narrowing) arm", "mcp_calls": "5 — mcp__github__issue_read x2 (29816 get, get_comments), mcp__github__pull_request_read x3 (29218 get_files, 29794 get_files, 29794 get); no write tool", "api_writes": "2 — fleet-write relay pr_create (objectstack-ai/objectstack#29886, 16619 bytes sent and stored, identical, no assignees) and this os-dev-report comment via post-stamped.mjs; git push is not a REST write", "open_questions": [ { "question": "One JS-only value main accepts is now refused: vendor members present as undefined with no vendor block, e.g. { apns: undefined }. Every JSON document still parses. Keep Clause-②: yes (widening)?", "options": ["A keep widening and name it in Acceptance notes (done)", "B declare apns/fcm as undefined-only keys on the relay block to keep it parsing"], "recommendation": "A, because B would put phantom keys on the relay's authorable surface and published JSON Schema, for a value that delivers to nothing and that no reader parses." }, { "question": "{} changes declared meaning from 'no vendor delivers' to 'the relay' (the card's default). Is Metadata-protocol: added enough, or is it semantic?", "options": ["A added (declared; nothing reads the shape, and the gate reads added)", "B semantic"], "recommendation": "A, because no reader exists, so no deployment's behaviour moves." } ], "security_findings": "none", "out_of_scope_findings": [ "carrier: cloud · which credential a cloud-hosted server (OS_CLOUD_API_KEY, no oscc_ bearer) presents to the relay is the relay's to state; this PR says only that the binding credential is the bearer · noted, not filed", "carrier: #29041 · PR #29794 reads top-level apns/fcm; it owes the transport narrowing in service-push when it merges after this card (seat ruling: serial) · noted, not filed" ], "deviations": [ "The PR body's attribution thread link is this card thread's (harness session context), not the seat-2 thread link in the order file.", "The card's 'ADR-0008 binding credential' is cited as cloud ADR-0008: in this repository ADR-0008 is the metadata repository and change log.", "No PR assignee or reviewer and no PR activity subscription, per os-dev rule 2 and the order, over the harness's default." ] }objectstack-fleet commentedon Nov 3, 2026
domain:spec#2 · os-support-ai · session_01WGufzhJG3tKnksHVa1krBz
Landed. PR #29886 merged through the queue (merge commit
1bb9536e); this card closed as completed by itsFixesline.MERGEDat 2026-11-03T10:32Z, head88fe2df6, contract review PASS 6600358082, ACCEPT 6600717510closed(completed)State change:
pm:dispatchedremoved. What shipped:PushServiceConfigSchemais atransportunion with the hosted relay as the default, andapi/push-relay.zod.tscarries the relay request, the per-token response and the 401 / 429 envelopes. Left elsewhere: the ADR-0012 amendment #29817 (Blocked-by this card, now unblocked); the two relay-wire should-fixes ride with the first producer, #29041 (posted there); #29794 owes thetransport === 'direct'narrowing per the serial note 6598066351.Merge origin/main into the push channel branch (relay | direct union, #…