Repository navigation
feat(push): requires push and the stack push key mount PushServicePlugin under os serve - #29968
Conversation
…gin under os serve The push capability token (spec, open edition, @objectstack/service-push), the top-level push stack key (PushServiceConfigSchema, single on compose), the core provider row and its argument through resolvePushCapabilityArg, which refuses the not-yet-served relay (an absent key included) and an inline secret before the kernel boots. cli and verify depend on service-push. Adds the services.push runtime-services page. Claude-Session: https://claude.ai/code/session_01WGufzgjFwEiezTWdb7bk9i Co-authored-by: Claude <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01WGufzgjFwEiezTWdb7bk9i Co-authored-by: Claude <noreply@anthropic.com>
…n the push composition in bootStack The envelope-key lists in spec (assembled body, unmanaged install) and the cli option-B pin classify the new top-level push key with email and sms; the verify relay pin asserts the reader refusal, before the kernel starts. Claude-Session: https://claude.ai/code/session_01WGufzgjFwEiezTWdb7bk9i Co-authored-by: Claude <noreply@anthropic.com>
…cli source aliases skills/objectstack-platform names the new top-level push key and the push token (check:skill-top-level-keys), paid for by dropping its stale registry path and count; the hand-written docs list gains the services.push page; the cli vitest config resolves service-push to src. Claude-Session: https://claude.ai/code/session_01WGufzgjFwEiezTWdb7bk9i Co-authored-by: Claude <noreply@anthropic.com>
Co-Authored-By: Claude <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01WGufzgjFwEiezTWdb7bk9i
📓 Docs Drift CheckThis PR changes 5 package(s): 3 hand-written doc(s) NAME something this change touched and may need an implementation-accuracy re-verification:
⛔ 3 release-owned page(s) also name something this change touched. These are read-only:
What this run could not see
Coarse fallback — 186 page(s) merely mention a changed package (the pre-#9192 predicate, kept for the deliberately-wide backstop): Which tree this was computed onThis run read A worktree cut from an older # while this PR is open — GitHub drops the merge commit once it closes
git fetch origin 630e31daad5fb8bddb17e5a12a70b74011c766f2 && git checkout 630e31daad5fb8bddb17e5a12a70b74011c766f2
# afterwards, rebuild it from the two parents, which stay fetchable
git fetch origin 3b8c724cf21fe2b39b2c635c6ef2a2448fa952fb 3612ed4d30422bb73fdbf5e858a1e42e566b06e6 && git checkout -B drift-repro 3b8c724cf21fe2b39b2c635c6ef2a2448fa952fb && git merge --no-ff 3612ed4d30422bb73fdbf5e858a1e42e566b06e6
node scripts/docs-audit/affected-docs.mjs --json 3b8c724cf21fe2b39b2c635c6ef2a2448fa952fb
|
|
domain:spec#2 · os-support-ai · session_01WGufzhJG3tKnksHVa1krBz Contract reviewServed-tier: ① Derived judgmentsJudged against seat ruling C (comment 6601454188): the token, the top-level
② Semver level
③ Boundary flags
④ Design judgment
Cited docs, outside
Minor, not graded: serve.ts's comment at 5144-5153 still says " No must-fix found. Ruling C's every clause is met on the tree: token, key and reader in one PR; a true Implemented-by: VERDICT: PASS |
…t push in the quick-start A push key on a stack whose requires does not name push is read by nothing; objectstack serve now logs one boot warning naming the key and the fix. The quick-start lists push beside email and sms among the stack configuration keys, and the changeset points its Skills-docs follow-up at #29969. Co-Authored-By: Claude <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01WGufzgjFwEiezTWdb7bk9i
|
domain:spec#2 · os-support-ai · session_01WGufzhJG3tKnksHVa1krBz domain:spec#2 · os-support-ai · session_01WGufzhJG3tKnksHVa1krBz · delta review on top of record 6612508737 Contract reviewServed-tier: ① Derived judgmentsThis is a delta review: one commit (3612ed4, "fix(serve): warn when a stack sets push without requiring it, and list push in the quick-start") on top of the head record 6612508737 passed. The delta touches five files, 80 insertions and 5 deletions, none of them a
② Semver levelUnchanged from the full record: the changeset still grades ③ Boundary flags
④ Design judgmentThe five
Minor, not graded: the comment at serve.ts 5144-5153 (email and sms readers) still omits Docs and changeset — every added sentence was checked against ① 1 through ① 4 and is true; no sentence claims more than serve does. No must-fix found. The three should-fixes are each done as asked; the warning is correctly placed, correctly scoped, echoes nothing, and nothing in the delta reopens a judgment of record 6612508737. Implemented-by: VERDICT: PASS |
|
domain:spec#2 · os-support-ai · session_01WGufzhJG3tKnksHVa1krBz 席位核对 — ACCEPT · domain:spec#2 · head
|
Requested by Yibo Wang · project thread
Fixes #29820
Clause-②: yes (widening)
Metadata-protocol: added
Skills-docs: follow-up owed — #29969 (skills/objectstack-platform/evals/config-plugins-ops.json: no eval asks for
requires: ['push']or thepushkey); this diff teaches both inskills/objectstack-platform/SKILL.md(the top-level key enumeration and therequirestable) and in the newcontent/docs/kernel/runtime-services/push-service.mdx维护者速读
requires: ['push']加上栈定义里新的顶层push键,objectstack serve(和@objectstack/verify的bootStack)就会挂上PushServicePlugin,不再需要宿主代码;托管 relay(含不写push键、push: {})与内联密钥在启动时被拒,报错写明原因与改法。notify选push渠道无处投递;relay 发送端尚未上线,若照常启动会挂上推送渠道、把每条推送都答成已发送而实际什么都没发出。requires: ['push']的栈会走新路径,未声明的启动不变;回滚即回退本 PR。唯一新增的启动失败是"声明了 push 却只配了 relay(或没配)",这是本卡要求的拒绝。packages/services/service-push/src/capability-arg.ts(relay 与 schema 拒绝及其报错文字)②packages/core/src/capability-composition.ts的push分支(serve 与 verify 共用的一条规则)③skills/objectstack-platform/SKILL.md的两处改动(Tier H,受管面)。pnpm --filter @objectstack/cli exec vitest run --project unit src/commands/serve-stack-email-sms-door.contract.test.ts,预期 push 段全部通过:direct 配置注册出 vendors 为['apns']的推送服务,无键 /{}/ relay 三种都以 "its sender has not shipped … transport: 'direct'" 被拒。skills/**(Tier H),需要一位 GOVERNED_APPROVERS 维护者批准后才能合并。What changes
@objectstack/specpushjoinsPLATFORM_CAPABILITY_TOKENS, provided by@objectstack/service-push, editionopen. It is requested, never always-on, because its schema default (the relay) is refused at boot.pushkey (PushServiceConfigSchema, secrets bycredentialRefonly) with aCOMPOSE_KEY_DISPOSITIONSrowsingle, classifieddeploymentin the unmanaged-install table and listed among the artifact-envelope keys.push-config.zod.ts's docblock now names the stack key andobjectstack serveas its reader. OnlyPushRelayTransportConfigSchemakeeps[PLANNED — not yet served].@objectstack/core:CAPABILITY_PROVIDERS.pushnamesPushServicePlugin, andresolveCapabilityArgument('push', …)readsresolvePushCapabilityArgoff the provider module, as theemailandsmsreaders are read. One rule serves bothobjectstack serveandbootStack.@objectstack/service-push:resolvePushCapabilityArg(stack.push)parses the key and throws, before the kernel boots:null,{},transport: 'relay'):PUSH_RELAY_NOT_SERVED_MESSAGE(names the missing relay sender, prescribestransport: 'direct'with anapnsand/orfcmblock), plus where the key is written on this boot path;transporthas itsinvalid_unionopened to the member the schema's own rule picks, so the guidance is not lost to "Invalid input".push, either refusal fails the boot asCapability "push" (@objectstack/service-push) failed to start: ….@objectstack/cli,@objectstack/verify: depend on@objectstack/service-push, so the provider the open-edition token names resolves out of the box. The cli's vitest config gains the anchored source alias.services.pushpage (content/docs/kernel/runtime-services/push-service.mdx): the two vendors, credential references, the relay's not-served status and its boot message, the device routes, dead-token retirement and the grading table; index rows in the runtime-services index, the kernel index and the versioning matrix. The generatedpush-configreference is regenerated.skills/objectstack-platform/SKILL.md, Tier H):pushis added to the top-level key enumeration (the required gatecheck:skill-top-level-keysrefuses the diff without it) and to therequirestable. The new tokens are paid for in the same file by deleting a stale path and count ("registry inpackages/cli/src/commands/serve.ts— its 19 opt-in entries" becomes "registry in@objectstack/core"; the registry moved to core and holds more than 19 entries).SKILL.md): 4539 → 4539 lines; whole shipped tree 224969 tokens after, 7 fewer than before (this is the onlyskills/**file touched).Why
Ruling C on #29820: the token, the key and the
os servereader land together, so no released version has apushtoken that boots to nothing or a key nothing reads. The relay is refused rather than served because a served relay would mount the messagingpushchannel and answer every pushsentwhile nothing left the server. A token with noCAPABILITY_PROVIDERSrow is not dropped silently: a new reverse pin asserts every open-edition token has a serve mount.Tests
packages/spec/src/kernel/platform-capabilities.test.ts: the token, its package and edition, not always-on,classifyinstallable.packages/spec/src/stack-email-sms.test.ts: the key: a direct block is carried through; a vendor block withouttransportreads as direct; relay,{}and absence are accepted at authoring; verdict parity withPushServiceConfigSchema; dispositionsingle; an inlinekeyrefused at['push','apns']withSTACK_SCHEMA_INVALID/422, value not echoed; a secret pasted intocredentialRefrefused; compose namespush.packages/services/service-push/src/capability-arg.test.ts(new): the direct argument constructs a plugin that registerspush; absent /null/{}/ relay / staging relay are refused with the relay message; inline APNs key, PEM incredentialRef, a relay credential and a vendor-less direct block are refused with their guidance and no echoed value.packages/core/src/capability-composition.test.ts: the reader is called withstack.pushonly ({}when absent); its refusal propagates; a provider module without the export is refused by name.packages/cli/src/commands/serve-stack-email-sms-door.contract.test.ts: serve composition: the table row;resolveServedCapabilities(['push'])contains push and([])does not; a direct block reaches the plugin and init registers vendors['apns']; no key /{}/ relay refused with the relay message; a raw-config inlineserviceAccountrefused, value not echoed.packages/cli/test/serve-capability-vocabulary.test.ts: new reverse pin, every open-edition token has a serve mount (a provider entry or a tier);serve-capability-identity.test.tsgains the push plugin name;option-b-reader-acceptance.pin.test.tslistspushamong envelope keys.packages/verify/src/harness.served-composition.test.ts:bootStackmounts the push plugin from a directfcmblock; a plain boot has none;requires: ['push']with no key rejects with the relay refusal.Acceptance notes
requirestable already omittedmailboxandpackage-registrybefore this change; noted for the skills lane, not fixed here.Verification
Local test runs (all under scripts/pm/os-verify-lock.sh)
pnpm exec turbo run build --concurrency=2: 120/120 tasks, VERDICT command-exit 0;@objectstack/specrebuilt again after the last spec src change, exit 0.pnpm --filter @objectstack/spec test: 808/808 files, 24288 passed, 1 todo.pnpm --filter @objectstack/spec typecheck: exit 0.pnpm --filter @objectstack/spec test:repo: 89/89 files, 1524/1524 tests.pnpm --filter @objectstack/cli exec vitest run --project unit --maxWorkers=2: 377/377 files, 5316/5316 tests. cli typecheck exit 0. The integration tier was not run locally: the diff touches no spawn entry, so CI runs it.pnpm --filter @objectstack/core testand typecheck: exit 0.pnpm --filter @objectstack/service-push testand typecheck: exit 0.pnpm --filter @objectstack/verify test: 48/48 files, 408/408 tests; typecheck exit 0.pnpm --filter @objectstack/metadata-protocol test: 311 passed, 4 skipped files; 22320 passed, 26 skipped tests.--no-inline-config) over the 19 changed.ts/.mjsfiles: exit 0. CI runs the fullpnpm lint.Ablation: the relay boot refusal
node scripts/ablation-replace.mjsturnedif (config.transport !== 'direct') {inpackages/services/service-push/src/capability-arg.tsintoif (false /* ablated-29820 */) {(anchor count 1 → 0, blob 8638e2857a5c → 2c1a8ad32f78).src/capability-arg.test.ts5 failed / 7 passed (all five relay rows: absent,null,{},transport: 'relay', staging endpoint); cliserve-stack-email-sms-door.contract.test.ts3 failed / 7 passed (no key,{}, relay).git diff HEADempty; the same two files 12/12 and 10/10.Gates: dispatch-gates
node scripts/pm/dispatch-gates.mjs --commands: 142 commands on the final tree, each run and its exit code recorded; all 142 exit 0.--ran: "142 derived famil(ies) accounted for — 142 run, 0 NOT-MEASURED".check:skill-top-level-keys(push missing from the skill's key list),check:test-source-alias(cli vitest alias),check:docs-audit-scope(the new page in the audit scope), andcheck-plugin-teardown-shape --self-test(exit 3 from a shallow clone; green after fetching its base commit).pnpm --filter @objectstack/spec run check:generated: exit 0.check-changeset-no-majorwith this body: exit 0 ("nomajorbump"; LEVEL AXIS ✓ foryes (widening)andadded; no moved package gradedpatch). Control: the same diff declaredMetadata-protocol: noneexits 1, refused only because the entries are gradedminor.Human Minimum
packages/specholds no runtime logic.pnpm lintpasses. (No formatter ran. ESLint over the 19 changed files passes; the fullpnpm lintis left to CI.)main, one pull request.packages/spectouched: its generated artifacts are regenerated and committed.content/docs/references/,packages/*/CHANGELOG.mdorcontent/docs/releases/.🤖 Generated with Claude Code
https://claude.ai/code/session_01WGufzgjFwEiezTWdb7bk9i