Repository navigation
refactor(plugin-webhooks,platform-objects)!: 18.x step 7c: retire the step 1–3 dual reads (declared-webhook mirror skip, sys_setting_audit global scope) - #24836
Conversation
…r retired; webhook activation door Claude-Session: https://claude.ai/code/session_01PDSxBZE561TFe1N2iEjKXZ Co-authored-by: Claude <noreply@anthropic.com>
…he 18.0 dual read; door pins for webhook Claude-Session: https://claude.ai/code/session_01PDSxBZE561TFe1N2iEjKXZ Co-authored-by: Claude <noreply@anthropic.com>
…asserts no row and a dispatch Claude-Session: https://claude.ai/code/session_01PDSxBZE561TFe1N2iEjKXZ Co-authored-by: Claude <noreply@anthropic.com>
…the checklist, the webhooks guide and the showcase; changeset Claude-Session: https://claude.ai/code/session_01PDSxBZE561TFe1N2iEjKXZ Co-authored-by: Claude <noreply@anthropic.com>
…tcher's switch read Claude-Session: https://claude.ai/code/session_01PDSxBZE561TFe1N2iEjKXZ Co-authored-by: Claude <noreply@anthropic.com>
…posture builds no wall, as its 18.0 seeded copy stamped with the Default Organization was Claude-Session: https://claude.ai/code/session_01PDSxBZE561TFe1N2iEjKXZ Co-authored-by: Claude <noreply@anthropic.com>
…red writes; the declared-webhook double is drivable by the limit gate Claude-Session: https://claude.ai/code/session_01PDSxBZE561TFe1N2iEjKXZ Co-authored-by: Claude <noreply@anthropic.com>
…does not implement Claude-Session: https://claude.ai/code/session_01PDSxBZE561TFe1N2iEjKXZ Co-authored-by: Claude <noreply@anthropic.com>
…authors; checklist and admitsOrganization docblock say a declared webhook is deployment-wide under single Claude-Session: https://claude.ai/code/session_01PDSxBZE561TFe1N2iEjKXZ Co-authored-by: Claude <noreply@anthropic.com>
…0g-webhooks-registry
…the fixture's customized declared-webhook mirror Claude-Session: https://claude.ai/code/session_01PDSxBZE561TFe1N2iEjKXZ Co-authored-by: Claude <noreply@anthropic.com>
…n fail on a double delivery Claude-Session: https://claude.ai/code/session_01PDSxBZE561TFe1N2iEjKXZ Co-authored-by: Claude <noreply@anthropic.com>
…0g-webhooks-registry # Conflicts: # content/docs/permissions/tenant-audit-census.mdx # docs/audits/2026-08-tenant-audit-write-call-sites.counts.md
…0g-webhooks-registry
…0g-webhooks-registry # Conflicts: # content/docs/permissions/tenant-audit-census.mdx # docs/audits/2026-08-tenant-audit-write-call-sites.counts.md
…-registry' into claude/issue-24581-ceremony-open
…NOT NULL door end to end
…he ceremony, then boot; boot-refusal-order step 2 back on the binary
…he checklist; changeset
…ap, docs and strictness-ledger artifacts
…el — the cipher names no organization Claude-Session: https://claude.ai/code/session_014UATXWqAXqkaZkbG92ox6V Co-authored-by: Claude <noreply@anthropic.com>
…sys_flow_credential leave reach) Claude-Session: https://claude.ai/code/session_014UATXWqAXqkaZkbG92ox6V Co-authored-by: Claude <noreply@anthropic.com>
… retired old home — sys_setting_audit refuses a global-scope row Claude-Session: https://claude.ai/code/session_01KMpT5c9dcX9kj3rBBWaki8 Co-authored-by: Claude <noreply@anthropic.com>
…NG narrowing) Claude-Session: https://claude.ai/code/session_01KMpT5c9dcX9kj3rBBWaki8 Co-authored-by: Claude <noreply@anthropic.com>
…' into claude/issue-24583-7c-dual-reads
…-dual-reads # Conflicts: # packages/plugins/plugin-webhooks/src/declared-webhooks.ts
…ration.mdx, equal to main) Claude-Session: https://claude.ai/code/session_01KMpT5c9dcX9kj3rBBWaki8 Co-authored-by: Claude <noreply@anthropic.com>
📓 Docs Drift CheckThis PR changes 5 package(s): 5 hand-written doc(s) NAME something this change touched and may need an implementation-accuracy re-verification:
⛔ 2 release-owned page(s) also name something this change touched. These are read-only:
What this run could not see
Coarse fallback — 146 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 3c303f2f23b47d92a8fe7423343f7f7b8f18e5d0 && git checkout 3c303f2f23b47d92a8fe7423343f7f7b8f18e5d0
# afterwards, rebuild it from the two parents, which stay fetchable
git fetch origin 1366740c6c80be43b18b6b72a68736032245e3e9 cdd16215002a2e9450296f0342eba95261a463d5 && git checkout -B drift-repro 1366740c6c80be43b18b6b72a68736032245e3e9 && git merge --no-ff cdd16215002a2e9450296f0342eba95261a463d5
node scripts/docs-audit/affected-docs.mjs --json 1366740c6c80be43b18b6b72a68736032245e3e9
|
Contract reviewServed-tier: PR #24836 · card #24583, step 7 part 7c · merge-base ① Derived judgments1. Webhooks — the dual read is gone; what remains is regime C, and it is pinned.
2. Kept on measurement — both right, both complete-feature-safe.
3. Settings audit —
4. Identity mail — docblocks only. 5. The two object files in the diff. 6. Tests — rewritten to the new rule; nothing weakened. 7. Check-runs on ② Semver level
③ Boundary flags
Implemented-by: VERDICT: PASS |
…e, the webhook liveness evidence, 24529's pending note (deliberate correction), the launch-window citation Claude-Session: https://claude.ai/code/session_01KMpT5c9dcX9kj3rBBWaki8 Co-authored-by: Claude <noreply@anthropic.com>
|
Seat addendum · epic PM Head In-seat check: PASS.
Deliberate correction (#17712). Not governed; 556 changed lines. The seat marks it ready and arms auto-merge. |
Part of #24583 (step 7, part 7c: the step 1–3 dual reads). #24583 remains open: parts 7a and 7b land separately.
Clause-②: yes (narrowing)
Base. Branched from
origin/mainmerged withclaude/issue-24581-ceremony-open(#24681, steps 5+6), with merge commits only. #24681 has merged (12d14765c), andmainwas merged in again at78f99c34c. The one conflict was my edit ofdeclared-webhooks.tsagainst the squash of the same lines; plugin-webhooks is byte-identical between #24681's headc6f775cbdandmain. The deferredmigration.mdxregeneration is discharged at8f6bf26e6and equalsmain. The diff againstmainis 26 files, +252 / −291, with no governed path.What changes
plugin-webhooks: step 1's dual read retires.
classifyDeclaredNameHolderis deleted. So are the dispatcher's mirror skip (AutoEnqueuer'smirrorIds) and the redeliver guard's mirror branch.sys_webhookrow that holds a declared webhook's name now answers as itself, and the declaration is withheld, reported once per name (regime C). The dispatcher's holder read asks foridandnameonly.ref_idnames a row is judged by that row.declared-webhooks.ts,auto-enqueuer.ts,redeliver-guard.ts,sys-webhook.object.ts,webhook-outbox-plugin.tsandwebhook-provenance.ts.bindWebhookProvenanceStampis kept, not deleted (measured below:customizedstill has a reader).migrateLegacyWebhookSecrets: only its docblock narrows (measured below: no code narrowing is possible).Settings audit: step 2.
sys_setting_audit.scopeno longer declaresglobal, so the engine refuses a global-scope row there (VALIDATION_FAILEDonscope).platform-objectstranslation bundles and the es-ES source hashes are regenerated (node scripts/check-i18n-bundles.mjs --write, one leaf each).settings-service-plugin.tsandsys-platform-setting-audit.object.ts.Identity mail: step 3, docblocks only.
sys-identity-email.object.ts,sys-email.object.ts,email-log-objects.tsandemail-plugin.tsnow say the ceremony moved the rows. Nothing is removable:sys_emailstays, because the move selects rows by shape.sys_email, because a queued tenant mail row stays there.Pins rewritten to the new rule, none weakened:
declared-webhooks.test.ts§3 and its redelivery case.sys-setting.scope-options.test.ts: the audit table's options are the spec enum minusglobal, and the sibling's are['global'].settings-deployment-level-expand.test.ts: the dual-read case becomes a refusal pin plus a tenant-rung control.settings-audit-global-rung-sibling.dogfood.test.ts: part B now boots over the same file and pins the refusal, with its control.Regime C, recorded and pinned
Step 5a leaves a seeded row whose name no installed package declares, and reports it. If a package declares that name later, the row now withholds the declaration and answers as itself. Before, it was skipped as a pristine mirror. The remedy is to rename or delete the row. Pinned by
a seeded row the ceremony left, its name declared later, withholds the declaration(declared-webhooks.test.ts).Premise measurements ("after certification")
Certified database, from the 18.0→HEAD harness after the ceremony (green on 8f6bf26).
sys_webhookcopy under a declared name is deleted, and its deliveries are re-sourced todeclared:NAME.SELECT COUNT(*) FROM sys_setting_audit WHERE scope = 'global'returns 0.upgrade-18x-harness.dogfood.test.ts.Fresh birth-marked install. R1 below, after a global setting write through the settings door:
sys_setting_audit: 1 row, 0 global.sys_platform_setting_audit: 2 rows, 2 global.sys_webhook: 0 rows.settingAuditObjectFor).Walled boot. The same reading under
isolatedwith the real@objectstack/organizations: 1/0, 2/2, 0.customizedstill has a live reader: the ceremony.runDeclaredWebhooksinorganization-ownership-apply.tssettles a seeded row by it: a pristine copy is deleted, an edited one is kept as a clone. The ceremony re-runs from the start when a superseding reviewed plan replaces its checkpoint. A seeded row 5a kept can be edited and then declared later, so dropping the stamp would let that re-run delete an administrator's edit. "Once nothing readscustomized" does not hold, so the stamp stays and its docblock names the ceremony as its reader.migrateLegacyWebhookSecrets. What 5a leaves:definition_json;Any of these can still carry a cleartext blob, and the sweep is keyed on the data. So the code stays, and the docblock now names exactly these rows.
no-organization-object. The one shipped composition with no organization object isexamples/embed-objectql, a lean ObjectQL embedding. It uses neither webhooks nor settings, so none of 7c's readers is on its path. Everyobjectstack.config.tsapp composes plugin-auth, which registerssys_organization. Theos initoutput was not measured.Proof (head 8f6bf26 unless stated)
pnpm dev -- --fresh -p 38963: health 200, seeded-admin sign-in 200, 0 error lines in the boot log. Readings above.upgrade-18x-harness.dogfood.test.ts: 8/8.organization-ownership-boot-refusal-order.dogfood.test.tspasses.identity-mail-read-gate.dogfood.test.tsusesceremoniedFixtureand passes.settings-audit-global-rung-sibling,webhook-materializationandwall-declared-webhook-deployment-wide.OS_TENANCY_POSTURE=isolatedos dev --fresh --seed-admin, run from a scratch app that re-exports the showcase config and declares the real@objectstack/organizations: health 200, sign-in 200, 0 ERROR lines. The banner lists theOrganizationsplugin, and the walled membership-policy gate was satisfied withinvite-only.OS_TEST_TIERS=nightly,serve-organizations-host-resolution+serve-organizations-mount-failure): 7/7.typecheckexits 0 for all four.check-i18n-bundles: all bundles in sync.node scripts/pm/dispatch-gates.mjs --commandson 8f6bf26 derives 77 commands over the 26 paths. All 77 ran with exit codes captured before any pipe, and all 77 exit 0.--ranreports 77 run, 0 NOT-MEASURED, a derived zero.check-empty-changesetflagged only feat(cli,runtime)!: the upgrade ceremony opens — v17 and 18.0 databases take it, and a server serves only a database it created or one the ceremony certified (18.x steps 5 and 6) #24681's own changeset edits.check-platform-checklist --changed-from origin/main: exit 0, 0 anchors lost.check-governed-merges --branch claude/issue-24583-7c-dual-reads: 0 of 26 paths on the register, 543 changed lines, under the human-merge line.pnpm --filter @objectstack/spec check:generated: up to date after the merge'sgen:docsdischarge.scripts/ablation-replace.mjs, the mirror skip was put back intoAutoEnqueuer.resolveDeclared: a pristinepackagerow is not counted as a holder. The anchor went 1 → 0 and the blob changed.declared-webhooks.test.tswent red, 2 failed / 20 passed: the regime-C pin and the switched-off-row pin. After the restore,git diff HEADwas empty. The test imports the source by relative path, so nodistrebuild was involved.NOT MEASURED
os initoutput reachingno-organization-object: out of 7c's readers' reach, not booted.Acceptance notes
isolated, the showcase's seed prints[tenant-audit]warnings ("bulkCreate on tenant-scoped object ... without options.tenantId"). They exist before this change and are not touched here.Generated by Claude Code