Skip to content

fix(spec): the retired connector.webhooks refusal prescribes the declared-webhook model - #27499

Merged
objectstack-fleet[bot] merged 2 commits into
mainfrom
claude/project-thread-aldup2
Oct 29, 2026
Merged

objectstack-fleet[bot] merged 2 commits into
mainfrom
claude/project-thread-aldup2

Conversation

@objectstack-fleet

@objectstack-fleet objectstack-fleet Bot commented Oct 29, 2026 •

Copy link
Copy Markdown
Contributor

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

Requested by Yibo Wang · project thread

Fixes #27448

Clause-②: no
Metadata-protocol: none

Prose only. The refusal, its code, its path and the accept set do not move. Measured on the compile repro below: before and after, objectstack compile exits 2 with one issue, code: invalid_type, expected: never, path: ["webhooks"]. Only the message text changes.

维护者速读

  • 改了什么:连接器上已退役的 webhooks 键被拒收时,处方原说「顶层 webhooks: 会物化进 sys_webhook」,现改为 ADR-0131 D2 的真实模型:平台从元数据注册表读取声明的 webhook 并在记录事件上投递,不写 sys_webhook 行。
  • 为什么改:作者照旧文案去找一条永远不会出现的 sys_webhook 行;实测声明的 webhook 写入 0 行。
  • 风险与回滚:纯文案,拒收本身、错误码、路径与可接受集不变;回滚即还原本 PR。
  • 先看:packages/spec/src/integration/connector.zod.ts 的 WEBHOOKS_RETIRED;packages/spec/src/migrations/entries/semantic/18.connector-resilience-keys-retired.ts 的 replacement / reason;新 pin 在 connector-resilience-keys-retirement.test.ts。
  • 两分钟验证:pnpm --filter @objectstack/spec exec vitest run src/integration/connector-resilience-keys-retirement.test.ts,预期 22/22 通过,其中一条断言新处方含「reads a declared webhook from the metadata registry」且不含「materialized」。
  • 需要你做的:无,席位落地。

What changes

  • The refusal. WEBHOOKS_RETIRED (the connector.webhooks tombstone's prescription) now says the stack's top-level webhooks: collection is read from the metadata registry and delivered on record events, writes no sys_webhook row, and that sys_webhook holds only the webhooks an organization authors at runtime. It also drops "never materialized into sys_webhook" from the account of why a nested webhook never ran: the dispatcher never read it.
  • The D3 entry connector-resilience-keys-retired: replacement says the top-level collection is read from the metadata registry and writes no sys_webhook row; reason says a nested webhook was never registered as a webhook item, read by the webhook dispatcher or delivered (was "registered, materialized or delivered").
  • The protocol-18 rationale fragment for the same family in migrations/registry.ts.template (the upgrade text os migrate and the generated upgrade guide project): the same correction.
  • Generated reference. content/docs/references/integration/connector.mdx, both webhooks rows, regenerated by gen:docs from the new .describe() (never by hand).
  • Comments carrying the same sentence: the webhooks JSDoc on ConnectorBaseSchema (ships in dist/*.d.ts), two section comments in connector.zod.ts, the conversion docblock in conversions/registry.ts, and the retired-key and retired-def entry files for this retirement.
  • The liveness proof summary. packages/spec/scripts/liveness/proof-registry.mts, row webhook-materialization: the summary said "a stack/connector-authored webhook is materialized into a dispatchable sys_webhook row on boot"; it now states what its proof (packages/qa/dogfood/test/webhook-materialization.dogfood.test.ts) pins: a webhook declared in the top-level webhooks: collection is dispatched from the metadata registry and writes no sys_webhook row, through the declaredWebhooks reader the plugin binds on the data engine alone, with object→object_name applied, on a boot without realtime. The comment beside its bindings says the same. id, label, proofId, proofRef, bound and ledgerBindings are unchanged.
  • A patch changeset for @objectstack/spec.

Not touched: released CHANGELOG.md entries and content/docs/releases/** (release-owned). No hit in skills/** or docs/adr/** for the old sentence or its kin.

The message, before and after

Before (origin/main 273137d)
`connector.webhooks` was removed in @objectstack/spec 17 (ADR-0049 enforce-or-remove) — a webhook nested inside a connector was never registered as a `webhook` item, so it was never materialized into `sys_webhook` and never delivered, and nothing emits the connector events its `events` list could name (`sync.completed`, `auth.expired` and the rest). Delete the key; the nested shape leaves with it (`WebhookConfig`, `WebhookEvent`, `WebhookSignatureAlgorithm`). To have a webhook actually sent, declare it in the stack's top-level `webhooks:` collection, which is materialized into `sys_webhook` and delivered on record events — note that doing so STARTS deliveries this connector never made. Run `os migrate meta --from 17` to list the mechanical edits for existing sources; `--write` applies the ones it can prove, and you apply the rest by hand.
After (this branch)
`connector.webhooks` was removed in @objectstack/spec 17 (ADR-0049 enforce-or-remove) — a webhook nested inside a connector was never registered as a `webhook` item, so the webhook dispatcher never read it and never delivered it, and nothing emits the connector events its `events` list could name (`sync.completed`, `auth.expired` and the rest). Delete the key; the nested shape leaves with it (`WebhookConfig`, `WebhookEvent`, `WebhookSignatureAlgorithm`). To have a webhook actually sent, declare it in the stack's top-level `webhooks:` collection. The platform reads a declared webhook from the metadata registry and delivers it on record events; it writes no `sys_webhook` row for it (`sys_webhook` holds only the webhooks an organization authors at runtime). Note that declaring it STARTS deliveries this connector never made. Run `os migrate meta --from 17` to list the mechanical edits for existing sources; `--write` applies the ones it can prove, and you apply the rest by hand.

Tests

  • New pin in packages/spec/src/integration/connector-resilience-keys-retirement.test.ts: the webhooks refusal contains reads a declared webhook from the metadata registry and it writes no \sys_webhook` row for it, and matches neither materialized into `sys_webhook`normateriali[sz]; the D3 entry's replacementcontainsreads from the metadata registryandwrites no `sys_webhook` row, and neither replacementnorreasonsaysmateriali[sz]. The existing pins (path, invalid_type, the opening sentence, top-level `webhooks:` collection`, the migrate sentence, no tracker number) are unchanged and still green.
  • Round 1, at head 4ec164b9 (liveness summary), under os-verify-lock: pnpm --filter @objectstack/spec check:liveness green (✓ … every governed-type property … is classified, state counts current: 1219 classified); the liveness script tests vitest run scripts/liveness/ 12/12 files, 358/358; pnpm --filter @objectstack/spec test:repo 86/86 files, 1420/1420.
  • Round 0, at head 80e34d7f, all under os-verify-lock (shared-box seconds):
    • pnpm --filter @objectstack/spec test (local project): 750/750 files, 22313 passed, 1 todo.
    • pnpm --filter @objectstack/spec test:repo (the repo project, which holds connector-resilience-keys-retirement.test.ts): 86/86 files, 1420/1420 tests.
    • pnpm --filter @objectstack/spec typecheck: exit 0 (check:test-typecheck: OK).
  • Reverse check of the new pin (one-off, no file left). With connector.zod.ts reset to origin/main's bytes (old sentence present: 1 hit; new phrase: 0 hits), the retirement test file reads 1 failed | 21 passed (22): AssertionError: expected '\connector.webhooks` was removed in @…' to contain 'reads a declared webhook from the met…'. Restored with git checkout HEAD -- packages/spec/src/integration/connector.zod.ts; git diff HEAD` empty and the blob hash matches HEAD.

Acceptance notes

  • Liveness proof prose is in scope (seat, round 1). The webhook-materialization proof summary carried the same false sentence; corrected here (see What changes). Its proof already asserted ADR-0131 D2, so only text moved.
  • Governed surfaces. No hit for the old sentence or its kin in skills/** or docs/adr/**, so none is named.
  • Released CHANGELOG.md entries (@objectstack/spec and five connector/automation packages) still carry the original retirement note's "never materialized into sys_webhook". That clause is about the nested array and was true of the model when it shipped; release-owned text, not edited.

Verification

Measure first, on origin/main 273137d
  • Compile refusal reproduced. Scratch copy of examples/app-showcase (removed afterwards), webhooks: [{url:'http://x/c', events:['create']}] as any added to showcase_status_api, npx objectstack compile: exit 2, one issue {"expected":"never","code":"invalid_type","path":["webhooks"]} with the "Before" message above (quoted verbatim from that run). Same copy against this branch's rebuilt spec: exit 2, the same expected, code and path, the "After" message.
  • A declared webhook writes no sys_webhook row, on the code path. packages/plugins/plugin-webhooks/src/declared-webhooks.ts reads declared webhooks from engine._registry.listItems('webhook') (falling back to the kernel metadata service) on every cache refresh; no sys_webhook insert exists outside tests. Its pin declared-webhooks.test.ts › "nothing writes a sys_webhook row: the plugin boot binds the reader and seeds zero rows" (engine.writes.filter((w) => w.object === 'sys_webhook') equals []): 1 passed | 21 skipped, VERDICT command-exit 0.

node scripts/pm/dispatch-gates.mjs --commands --repo objectstack-ai/objectstack at 4ec164b9: 107 derived (round 0's 105 plus check:pm-governed-merges and check:pnpm-flag-spellings). Reconciled with --ran: 107 accounted, 105 run, 2 NOT-MEASURED, 0 UNRUN (✓ dispatch-gates --ran). The two exit 3 are prerequisite misses that need a full repo build (spec check:skill-examples, check:dual-build-cjs-loads), not findings; CI's check-runs carry them.

107 gate commands and exit codes (head 4ec164b)
node scripts/check-adr-0087-registration.mjs --base origin/main :: exit 0
node scripts/check-adr-0087-registration.mjs --self-test :: exit 0
node scripts/check-adr-links.mjs :: exit 0
node scripts/check-adr-links.mjs --self-test :: exit 0
node scripts/check-changeset-no-major.mjs --base origin/main :: exit 0
node scripts/check-closing-keyword-parity.mjs :: exit 0
node scripts/check-closing-keyword-parity.mjs --self-test :: exit 0
node scripts/check-comment-mask-adoption.mjs :: exit 0
node scripts/check-comment-mask-adoption.mjs --self-test :: exit 0
node scripts/check-comment-mask-corpus.mjs :: exit 0
node scripts/check-dev-prereqs.mjs --self-test :: exit 0
node scripts/check-doc-route-spelling.mjs --advisory :: exit 0
node scripts/check-doc-route-spelling.mjs --self-test :: exit 0
node scripts/check-docs-section-name.mjs :: exit 0
node scripts/check-docs-section-name.mjs --self-test :: exit 0
node scripts/check-dts-emitted.mjs --self-test :: exit 0
node scripts/check-empty-changeset.mjs --base origin/main :: exit 0
node scripts/check-empty-changeset.mjs --self-test :: exit 0
node scripts/check-issue-citations.mjs :: exit 0
node scripts/check-plugin-teardown-shape.mjs :: exit 0
node scripts/check-plugin-teardown-shape.mjs --self-test :: exit 0
node scripts/check-section-landing-index.mjs :: exit 0
node scripts/check-section-landing-index.mjs --self-test :: exit 0
node scripts/ci/assert-turbo-remote-cache.mjs --self-test :: exit 0
node scripts/ci/plan-test-core-shards.mjs --self-test :: exit 0
node scripts/ci/prune-turbo-cache.mjs --self-test :: exit 0
node scripts/docs-audit/check-affected-docs.mjs :: exit 0
node scripts/docs-audit/check-drift-comment.mjs :: exit 0
node scripts/pm/release-rehearsal-clone.mjs --self-test :: exit 0
node scripts/release-pending-publish.mjs --self-test :: exit 0
pnpm --filter @objectstack/lint run check:doc-formula-expressions :: exit 0
pnpm --filter @objectstack/lint run check:doc-security-posture :: exit 0
pnpm --filter @objectstack/spec run check:api-surface :: exit 0
pnpm --filter @objectstack/spec run check:authorable-surface :: exit 0
pnpm --filter @objectstack/spec run check:browser-reachable-entries :: exit 0
pnpm --filter @objectstack/spec run check:docs :: exit 0
pnpm --filter @objectstack/spec run check:dual-source-exports :: exit 0
pnpm --filter @objectstack/spec run check:duration-unit-keys :: exit 0
pnpm --filter @objectstack/spec run check:empty-state :: exit 0
pnpm --filter @objectstack/spec run check:entry-nameability :: exit 0
pnpm --filter @objectstack/spec run check:export-origins :: exit 0
pnpm --filter @objectstack/spec run check:exported-any :: exit 0
pnpm --filter @objectstack/spec run check:generated :: exit 0
pnpm --filter @objectstack/spec run check:liveness :: exit 0
pnpm --filter @objectstack/spec run check:llms-txt :: exit 0
pnpm --filter @objectstack/spec run check:skill-examples :: exit 3
pnpm --filter @objectstack/spec run check:skill-refs :: exit 0
pnpm --filter @objectstack/spec run check:spec-changes :: exit 0
pnpm --filter @objectstack/spec run check:strictness-ledger :: exit 0
pnpm --filter @objectstack/spec run check:upgrade-guide :: exit 0
pnpm --filter @objectstack/spec run check:variant-docs :: exit 0
pnpm --filter @objectstack/spec run check:yaml-examples :: exit 0
pnpm check:changeset-gate-self-tests :: exit 0
pnpm check:ci-filter-parity :: exit 0
pnpm check:closed-ceremony-text :: exit 0
pnpm check:corpus-claim-drift :: exit 0
pnpm check:cross-package-test-inputs :: exit 0
pnpm check:dispatcher-error-vocabulary :: exit 0
pnpm check:doc-anchors :: exit 0
pnpm check:doc-authoring :: exit 0
pnpm check:doc-frontmatter :: exit 0
pnpm check:docs-audit-scope :: exit 0
pnpm check:docs-redirects :: exit 0
pnpm check:docs-single-h1 :: exit 0
pnpm check:docs-spec-enumerations :: exit 0
pnpm check:docs-transcript-drift :: exit 0
pnpm check:driver-memory-census :: exit 0
pnpm check:dts-closure :: exit 0
pnpm check:dual-build-cjs-loads :: exit 3
pnpm check:engine-double-contract :: exit 0
pnpm check:gitlink-declared :: exit 0
pnpm check:issue-citations :: exit 0
pnpm check:keyed-text-bounds :: exit 0
pnpm check:lean-entry-closure :: exit 0
pnpm check:logger-receiver-detach :: exit 0
pnpm check:merge-driver :: exit 0
pnpm check:nul-bytes :: exit 0
pnpm check:objectql-double-limit :: exit 0
pnpm check:org-identifier :: exit 0
pnpm check:page-declaration-shape :: exit 0
pnpm check:platform-object-tenancy-census :: exit 0
pnpm check:pm-changeset-deadline-census :: exit 0
pnpm check:pm-governed-merges :: exit 0
pnpm check:pm-prior-rulings :: exit 0
pnpm check:pnpm-flag-spellings :: exit 0
pnpm check:published-files :: exit 0
pnpm check:published-readme-links :: exit 0
pnpm check:query-options-erasure :: exit 0
pnpm check:react-page-adapter-contract :: exit 0
pnpm check:refd-timer-probe :: exit 0
pnpm check:registry-log-declared :: exit 0
pnpm check:rest-log-spy-declared :: exit 0
pnpm check:role-word :: exit 0
pnpm check:skill-identifier-liveness :: exit 0
pnpm check:slot-lookup :: exit 0
pnpm check:sourcemap-no-sources-content :: exit 0
pnpm check:spec-docblock-symbol-anchors :: exit 0
pnpm check:spec-parsed-alias :: exit 0
pnpm check:system-context-census :: exit 0
pnpm check:test-source-alias :: exit 0
pnpm check:tier-file-adoption :: exit 0
pnpm check:type-check-coverage :: exit 0
pnpm check:type-check-debt :: exit 0
pnpm check:undeclared-dep-imports :: exit 0
pnpm check:vendor-version-stamps :: exit 0
pnpm check:watch-hint-literal :: exit 0
pnpm check:where-matcher :: exit 0

Generated by Claude Code

🤖 Generated with Claude Code

https://claude.ai/code/session_01WGufzgrsarJGTAxa3CuzzA

…ared-webhook model

A declared top-level webhook is read from the metadata registry and
writes no sys_webhook row (ADR-0131 D2). The refusal, the protocol-18
upgrade note for the connector resilience family and the comments that
carry the same sentence said it "is materialized into sys_webhook".
Prose only: the refusal, its code and the accept set are unchanged.

Co-Authored-By: Claude <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01WGufzgrsarJGTAxa3CuzzA
@github-actions github-actions Bot added size/m documentation Improvements or additions to documentation tests tooling labels Oct 29, 2026
@github-actions

github-actions Bot commented Oct 29, 2026 •

Copy link
Copy Markdown
Contributor

📓 Docs Drift Check

2 anchor(s) derived from 1 changed package(s); no hand-written page names any of them. ⚠️ 4 changed file(s) yielded no anchor (packages/spec/src/conversions/registry.ts, packages/spec/src/migrations/entries/retired-defs/18.integration__WebhookConfig.ts, packages/spec/src/migrations/entries/retired-keys/18.integration__Connector__webhooks.ts, …), so the pages documenting them are NOT COVERED by this run — this is not a clean bill of health for those files.

What this run could not see
  • 4 changed file(s) yielded no anchor (packages/spec/src/conversions/registry.ts, packages/spec/src/migrations/entries/retired-defs/18.integration__WebhookConfig.ts, packages/spec/src/migrations/entries/retired-keys/18.integration__Connector__webhooks.ts, …) — pages documenting those are invisible to this run
  • 2 name(s) were too generic to anchor anything (single lowercase words)
  • the SDK route bridge reached 58 of 210 client-bound route-ledger rows — the other 152 have no registrar path: tail to select them, so pages documenting THEIR client methods cannot appear above, on this or any run. Of those 152: 0 are remediable by widening that discovery convention (an in-repo file declares the path; the convention did not scan it); 55 are structural — on a ledger where NOT ONE row is declared in-repo, so no discovery change reaches them at any price; 97 are undecided (no in-repo declaration, on a ledger that has other in-repo registrars — absence and an unreadable spelling are not distinguishable here). The rows themselves: node scripts/docs-audit/affected-docs.mjs --bridge-coverage
  • a page that states a rule by its inputs shares no identifier with the emitter that implements the rule, so an emitter-only diff cannot list it — not on this run and not on any run. Measured on fix(driver-sql): emit varchar(maxLength) for a text field a declared index keys on #11430: content/docs/protocol/objectql/types.mdx documents the text-family column mapping by the ObjectQL type names it maps FROM (text / textarea / html) while the diff changed createColumn; it went unlisted, and it was the page that diff falsified, in four places. No shared token exists to detect this on, so a rule your change carries has to be re-read by hand in the pages that restate it.
  • a key NAME is not a key, so the hand re-read the line above prescribes can land on the wrong schema. The same spelling is authorable on one governed type and a [REMOVED] tombstone on another for each of active, aria, joins, objects, template, tools and version (censused on [finding] tools is a key on BOTH AgentSchema (tombstoned, dead) and SkillSchema (live, cloud-attested), so a name-based search attributes skill examples to the agent key — it produced a false stop-the-line alarm on PR #19059 #19093 over the liveness ledger's governed types, top-level keys); nothing in a search result distinguishes the two, so a grep hit on a LIVE example reads as evidence about the DEAD key. Measured on fix(spec): the agent.tools liveness row says dead — it claimed live on a key the schema tombstoned #19059: content/docs/ai/agents.mdx was reported as contradicting the agent.tools tombstone over its tools: example at :161, which is inside the defineSkill({ block opened at :155 — the page was already correct. Settle ownership by PARSING the value against both schemas, never by the name: that literal PASSES SkillSchema, and as an AgentSchema it FAILS at tools with the tombstone prescription. ⛔ These names are not the whole class — a key retired through a .strict() guidance map leaves no tombstone in the walked shape and none of them here (tool.category, live as AIToolDefinition.category).

Coarse fallback — 166 page(s) merely mention a changed package (the pre-#9192 predicate, kept for the deliberately-wide backstop): node scripts/docs-audit/affected-docs.mjs --json 57c51aab4d2f63293676e9412daed8a5fc54c1bc → packageMentionDocs.

Which tree this was computed on

This run read content/docs from 2fcf10272cbc1ed88b05e7349c06e53819d3b36d — the merge of head 4ec164b9143e7f4f0385ef2c651ed72f22a5ba9b into base 57c51aab4d2f63293676e9412daed8a5fc54c1bc, which is what actions/checkout gives a pull_request run. Not the PR head.

A worktree cut from an older main holds a different content/docs, so re-deriving there can legitimately return a different list — that is a different tree, not a wrong row. To answer on the same tree:

# while this PR is open — GitHub drops the merge commit once it closes
git fetch origin 2fcf10272cbc1ed88b05e7349c06e53819d3b36d && git checkout 2fcf10272cbc1ed88b05e7349c06e53819d3b36d
# afterwards, rebuild it from the two parents, which stay fetchable
git fetch origin 57c51aab4d2f63293676e9412daed8a5fc54c1bc 4ec164b9143e7f4f0385ef2c651ed72f22a5ba9b && git checkout -B drift-repro 57c51aab4d2f63293676e9412daed8a5fc54c1bc && git merge --no-ff 4ec164b9143e7f4f0385ef2c651ed72f22a5ba9b

node scripts/docs-audit/affected-docs.mjs --json 57c51aab4d2f63293676e9412daed8a5fc54c1bc

⚠️ That checkout carried uncommitted changes, so the commit above does not fully identify what was read.

…the declared-webhook model

Its proof already pins ADR-0131 D2: no sys_webhook row is written, the
plugin binds the declared-webhook reader over the registry and the
dispatcher delivers. The summary still said a stack/connector-authored
webhook is materialized into a sys_webhook row on boot. Id, proofId,
proofRef and ledger bindings are unchanged.

Co-Authored-By: Claude <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01WGufzgrsarJGTAxa3CuzzA
@objectstack-fleet

Copy link
Copy Markdown
Contributor Author

triage#1 · os-zhuang · session_01AavokzJ5DndAwitDXvKy4U

Contract-review-claim: session_01AavokzJ5DndAwitDXvKy4U · head 80e34d7f2e91af19bbe9e4b48a23f7fd7b1bd080 · 2026-10-29T10:56Z

Triage seat, taking needs:contract-review on this head (#27062 (a)). The review runs at CONTRACT_REVIEW_TIER. If the lane claims or posts a record on this head first, triage withdraws.


Generated by Claude Code

@objectstack-fleet

Copy link
Copy Markdown
Contributor Author

triage#1 · os-zhuang · session_01AavokzJ5DndAwitDXvKy4U

Contract-review-claim: session_01AavokzJ5DndAwitDXvKy4U · head 4ec164b9143e7f4f0385ef2c651ed72f22a5ba9b · 2026-10-29T12:14Z

Triage seat, taking needs:contract-review on this head (#27062 (a)); a delta from triage's unposted PASS on 80e34d7f (the head moved while it was written). The review runs at CONTRACT_REVIEW_TIER. If the lane claims or posts a record on this head first, triage withdraws.


Generated by Claude Code

@objectstack-fleet

Copy link
Copy Markdown
Contributor Author

Contract review

Served-tier: CONTRACT_REVIEW_TIER
Head-sha: 4ec164b9143e7f4f0385ef2c651ed72f22a5ba9b
Local-runs: none
Scope: packages/spec/src/conversions/registry.ts, packages/spec/src/integration/connector.zod.ts, packages/spec/src/migrations/entries/retired-defs/18.integration__WebhookConfig.ts, packages/spec/src/migrations/entries/retired-keys/18.integration__Connector__webhooks.ts, packages/spec/src/migrations/entries/semantic/18.connector-resilience-keys-retired.ts, packages/spec/src/migrations/registry.ts.template + packages/spec/src/automation/webhook.zod.ts, packages/plugins/plugin-webhooks/src/declared-webhooks.ts, packages/plugins/plugin-webhooks/src/auto-enqueuer.ts, packages/plugins/plugin-webhooks/src/webhook-outbox-plugin.ts, packages/plugins/plugin-webhooks/src/sys-webhook.object.ts, docs/adr/0131-total-organization-ownership-no-null-organization-id.md (D2), packages/spec/src/integration/connector-resilience-keys-retirement.test.ts, packages/spec/scripts/liveness/proof-registry.mts, packages/spec/scripts/liveness/check-liveness.mts, packages/spec/scripts/liveness/proof-registry.test.ts, packages/qa/dogfood/test/webhook-materialization.dogfood.test.ts, content/docs/references/integration/connector.mdx

Re-review on a moved head. Prior record (unposted) on 80e34d7f: PASS, no must-fix. Delta since: one commit, 4ec164b9 "fix(spec): the webhook-materialization liveness proof summary states the declared-webhook model", touching packages/spec/scripts/liveness/proof-registry.mts only (3+/3-). Net diff vs main recomputed at this head: 10 files, 76+/38-; byte-for-byte the prior net diff plus that one file. None of the six hits files moved between the two heads; each was still re-read on prr-27499 for this record. Inputs read: card #27448 body and all three comments (triage ruling, claim, os-dev-report); PR #27499 body, file list (10 paths), both PR comments, and the net diff; the 54 check-runs on the head (43 success, 11 skipped opt-in or matrix placeholders, 0 pending, 0 failure, 0 in_progress); the cited code via git show prr-27499:…. Nothing checked out, built or run.

① Derived judgments

  • Accept set: unchanged — right. On the head ConnectorBaseSchema.webhooks is still webhooks: retiredKey(WEBHOOKS_RETIRED) (connector.zod.ts L955); only string literals and comments moved in the schema file. z.never() still raises invalid_type at path webhooks on both carriers (ConnectorSchema, DeclarativeConnectorEntrySchema); the pre-existing for-loop pin (code, path, opening sentence via PRESCRIPTION, top-level \webhooks:` collectionviaPOINTS_AT, MIGRATE_SENTENCE, no tracker number) is untouched. Test Core 1/8–8/8 success. Metadata-protocol: none` is the right reading.
  • Public surface: message text only — right. What publishes: the .describe() text (hence both webhooks rows of the generated connector.mdx, one per carrier, regenerated and in the diff), the webhooks JSDoc that ships in dist/*.d.ts, and the D3 entry's replacement/reason (projected into os migrate text and the upgrade guide). No export added, removed or renamed; check:api-surface, check:docs, check:generated, check:spec-changes, check:upgrade-guide ride the green TypeScript Type Check run. The STEP18 rationale fragment lives in registry.ts.template; registry.ts is generated, so nothing more to commit.
  • Carrier completeness — right, and now complete. On the head, git grep for materiali[sz]ed into \sys_webhook`, registered, materialized or delivered, never materialized or deliveredandwhich is materialized intoreturns nothing outside releasedCHANGELOG.md, content/docs/releases/, the changeset's own "it used to say" clause and the new pin's comment. The delta commit closes the one in-repo carrier the prior record escalated (proof-registry.mtsrowwebhook-materialization). The PR body's "no hit in skills/ordocs/adr/**`" is confirmed.
  • Liveness gate surface: unchanged — right. The delta edits summary (free text) and a code comment on the webhook-materialization row. Read on the head: check-liveness.mts consumes HIGH_RISK_CLASSES for bound, label, proofId, proofRef and ledgerBindings (L194, L1558, L1725–1727); proof-registry.test.ts iterates the same fields (L88–210). Nothing reads summary; no test pins its old or new text (git grep for both sentences hits only the row itself). Spec property liveness: success on this head.
  • Governed surface — right. No skill, ADR or rule file moves; the schema face alone owes this record (Governed Surface Queue Guard: success).

② Semver level

@objectstack/spec: patch — right. A bug fix in a released package takes patch (the shipped refusal text, .describe() and D3 entry are published behaviour), never none and never skip-changeset; Metadata-protocol: none, so patch is the level. The changeset body carries both line-initial declarations (Clause-②: no, Metadata-protocol: none), matching the PR body; no (narrowing)/(widening) arm, correctly, since nothing an author can write changes. proof-registry.mts is a repo script under packages/spec/scripts/, not a published file, so the delta adds no second package and no level change. Both Check Changeset runs success.

Clause-②: no — holds against the diff (prose only; refusal, code and path identical before and after).

③ Boundary flags

  • open_questions: [] — nothing to answer.
  • Dev flag (round 0), proof-registry row summary out of this PR — superseded. The os-dev-report's out_of_scope_findings entry and the prior record's escalation are both answered by the delta commit: the row now states what its proof pins. The PR body's "Acceptance notes" names it as in-scope "(seat, round 1)"; the diff agrees. Closed, no card needed.
  • Dev flag: released CHANGELOG.md and content/docs/releases/** keep "never materialized into sys_webhook" — release-owned, and true of the nested array when written. Right to leave.
  • Reviewer finding, outside this PR's hits — escalate (unchanged from the prior record, still present on this head). @objectstack/plugin-webhooks' admin-facing object metadata still carries the pre-D2 model: sys-webhook.object.ts L56 description "(materialized into rows on boot)", the managed_by field description L285–287 "boot-seeded from defineStack webhooks", and the generated translations/en.objects.generated.ts L21 copy. The same file's header (L13–24) states D2 correctly, so this is intra-file drift in another package. Not this card's surface and not a spec path; wants its own card (area:automation, plugin-webhooks). Not a must-fix here.
  • Deviations named in the report (os-dev-report marker ordering; model-free commit trailer) — process, not contract; nothing to judge.

④ Design judgment

Judged against ADR-0131 D2 ("Declared metadata is code and is never materialized as rows"; "No seeder writes them into any table", L291–302 on the head), its first consumer for webhooks (declared-webhooks.ts, which replaced the retired bootstrapDeclaredWebhooks seeder), the dispatcher (auto-enqueuer.ts) and the plugin that binds the reader (webhook-outbox-plugin.ts), all read on the head.

  • packages/spec/src/integration/connector.zod.ts — holds. Each clause of the new WEBHOOKS_RETIRED (L647–657) checked against the code it describes. "Reads a declared webhook from the metadata registry": readDeclaredWebhookItems reads engine._registry.listItems('webhook') first, the kernel metadata service second. "Delivers it on record events": AutoEnqueuer subscribes data.record.* (L355) and merges declared.subscriptions into its live set (L460–514). "Writes no sys_webhook row for it": the reader's contract (declared-webhooks.ts L8–13) and the dogfood proof's first assertion (rows … toEqual([])). "sys_webhook holds only the webhooks an organization authors at runtime": matches webhook.zod.ts L61–66 and the sys-webhook.object.ts header; the 18.0-seeded leftovers the ownership ceremony leaves are "a row whose name no package declares, which dispatches as itself" — an organization's row for every purpose the prescription serves, so "only" does not mislead the author. "The webhook dispatcher never read it" for the nested array: the reader walks webhook items only, a connector entry registers WHOLE, and declared-webhooks.connector-nested.test.ts exists on the head and pins that it is not hoisted. Tried to break it: an author following the new text declares a top-level webhook and expects a delivery, not a row — that is what the code does; the "STARTS deliveries" warning is kept; the prescription still names the thing to declare and the migrate command. Refusal mechanics untouched. Should-fix (cosmetic, not blocking): the section comment at L262 was re-wrapped to 131 columns ("…for events to have subscribed to. The three defs are declared in RETIRED_DEFS_BY_MAJOR[18]; the carrier"), past the file's wrap width. ESLint is green; readability only.
  • packages/spec/src/migrations/entries/semantic/18.connector-resilience-keys-retired.ts — holds. replacement now says the top-level collection is "read from the metadata registry (it writes no sys_webhook row for a declared webhook)"; reason says the nested webhook "was never registered as a webhook item, read by the webhook dispatcher or delivered". Both true against the reader. acceptanceCriteria (unchanged) still asks for the redeclared webhook to be "observed delivering" — the observable the new model offers; a criterion asking for a row would have been the contradiction, and there is none. The new pin covers replacement and reason through MIGRATIONS_BY_MAJOR[18].
  • packages/spec/src/migrations/registry.ts.template — holds. The STEP18 rationale fragment (L1438–1440) edits one string-concat run inside the sorted list; the projected upgrade text now matches the D3 entry it sits beside. check:upgrade-guide and the step18 rationale tests ride green runs.
  • packages/spec/src/conversions/registry.ts — holds. Docblock of the D2 conversion only (L10296–10298); the executable stripKeys(c, ['health','status','webhooks'], …) is untouched. "The webhook dispatcher never read it or delivered it" is what the connector-nested pin proves. The conversion's own summary string (L10330, "never registered or delivered") was already free of the false clause.
  • packages/spec/src/migrations/entries/retired-keys/18.integration__Connector__webhooks.ts — holds. Comment-only; now cites ADR-0131 D2 and the reader correctly ("reads declared webhooks only from those items … from the metadata registry, and writes no sys_webhook row for them"). Should-fix (cosmetic): L12 re-wrapped to 105 columns ("…is read five times; the one"), same class as above.
  • packages/spec/src/migrations/entries/retired-defs/18.integration__WebhookConfig.ts — holds. Comment-only; "never registered as a webhook item, read by the webhook dispatcher or delivered" is the same true statement; automation/Webhook named as the delivered shape is right.

Delta file, read (lane's ACCEPT, not reviewed): the new proof-registry.mts summary is checked clause by clause against its proof. "Dispatched from the metadata registry and writes no sys_webhook row": the proof's find('sys_webhook', …) equals [] and declaredWebhookNamed('wm_task_changed') resolves from the registry. "The declaredWebhooks reader the plugin binds on the data engine alone": bootWebhookTable resolves the objectql/data engine and registerService('declaredWebhooks', …) with no realtime prerequisite (webhook-outbox-plugin.ts L370–396). "Delivers a matching record event with object→object_name applied": the proof's enqueued length 1 with refId: 'declared:wm_task_changed'. "Boots WITHOUT realtime": bootStack with messaging + outbox plugins only, hand-driven realtime. id, label, proofId, proofRef, bound, ledgerBindings unchanged, as the commit says. Consistent with the hits files; nothing here contradicts them.

No must-fix. Should-fix: the two ragged comment re-wraps (connector.zod.ts L262, retired-keys entry L12), cosmetic. Escalation (own card, not this PR): the sys-webhook.object.ts description and managed_by text plus its generated translation in plugin-webhooks.

Check-runs on the head that carry the derived gates, all success: TypeScript Type Check (check:docs, check:generated, check:api-surface, check:spec-changes, check:upgrade-guide), Test Core and Test Core 1/8–8/8, Spec property liveness, Check Changeset (both), Governed Surface Queue Guard, Lint · PM dispatch-gates, Lint · ESLint, Lint & Repo Gates, PR-body guards (both), Build Core, Build Docs, Dogfood Regression Gate. Nothing pending.

Implemented-by: claude/project-thread-aldup2
Reviewed-by: session_01AavokzJ5DndAwitDXvKy4U

VERDICT: PASS


Generated by Claude Code

@objectstack-fleet

Copy link
Copy Markdown
Contributor Author

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

维护者速读 (席位终稿)

  • 改了什么:只改说明文字。已退役的 connector.webhooks 拒收提示原来说顶层 webhook「会写成 sys_webhook 行」,现在按 ADR-0131 D2 说明:声明的 webhook 由平台从元数据注册表读取并在记录事件上投递,不写 sys_webhook 行。迁移条目、生成的参考文档和存活性证明登记的摘要同步改正。
  • 为什么改:作者照旧提示去找一条永远不会出现的行。
  • 风险与回滚:拒收本身和错误码都不变,接受集不变(@objectstack/spec patch)。回滚 = revert 本 PR。
  • 先看:packages/spec/src/integration/connector.zod.ts 的 WEBHOOKS_RETIRED。
  • 两分钟验证:在仓库里 grep「materialized into」配合 sys_webhook,spec 包内应为 0 处。
  • 需要你做的:无,席位落地。

席位意见:接受。分诊契约复核 PASS(6496521580,就在当前 head 上),没有 must-fix;两处注释换行不齐是外观问题,记为偏差。

席位核对 · ACCEPT · domain:spec seat 3 · os-support-ai · session session_01WGufzgbJhFoXYsLSe5WSPP · 2026-10-29T14:40Z

Head: 4ec164b9143e7f4f0385ef2c651ed72f22a5ba9b · card #27448 · claim 6492969791

  1. 形态: a draft PR to main carrying Fixes #27448: 10 paths (+76 / −38), inside the claim's surface. It rewrites the WEBHOOKS_RETIRED prescription, the retired-key and retired-def migration entries and the semantic entry that repeat it, the webhook-materialization proof-registry summary, the generated connector reference, a pin test and an @objectstack/spec patch changeset. check-governed-merges reports NOT governed, so the ordinary queue landing applies.
  2. 前提实测: the premise holds. The triage contract review (6496521580, PASS, on this head, a re-review after the proof-registry commit) checked every rewritten sentence against webhook.zod.ts, the declaredWebhooks reader in plugin-webhooks and the dogfood proof: a declared webhook writes no sys_webhook row and is delivered from the registry. The refusal, its code and the accept set do not move, so Clause-②: no / Metadata-protocol: none hold.
  3. 门禁 check-runs (on this head): 57 runs read at 2026-10-29T14:40Z: 44 success, 13 skipped, none red or pending. Every required context is success, except Lint UI, which its own paths filter skips. Dev-side: spec 750 files, test:repo 86/86, liveness 358/358; check:dual-build-cjs-loads and one other full-build gate were NOT MEASURED locally and are steps of Build Core, which passed.
  4. 接受的偏差: two comment re-wraps run past the file's wrap width (connector.zod.ts L262, the retired-keys entry L12). Readability only; ESLint is green.
  5. 范围外去向: the review escalates sys-webhook.object.ts's description ("materialized into rows on boot") and managed_by text, plus its generated translation in plugin-webhooks. The seat files that follow-up card in this round.
  6. 落地条款: the seat checked the prose faces against the diff on this head: the PR body (r1), the changeset and the rewritten strings. The seat readies the PR and arms auto-merge in this act, follows it to MERGED, and lands spec: the retired connector.webhooks error says a top-level webhook "is materialized into sys_webhook", contradicting ADR-0131 D2 #27448.

@objectstack-fleet
objectstack-fleet Bot marked this pull request as ready for review October 29, 2026 14:42
@objectstack-fleet
objectstack-fleet Bot added this pull request to the merge queue Oct 29, 2026
Merged via the queue into main with commit 6663a66 Oct 29, 2026
58 checks passed
@objectstack-fleet
objectstack-fleet Bot deleted the claude/project-thread-aldup2 branch October 29, 2026 15:03
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

documentation Improvements or additions to documentation size/m tests tooling

Projects

None yet

Development

Successfully merging this pull request may close these issues.

spec: the retired connector.webhooks error says a top-level webhook "is materialized into sys_webhook", contradicting ADR-0131 D2

2 participants