Repository navigation
Commit bd6dab0
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)
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/main` merged with
`claude/issue-24581-ceremony-open` (#24681, steps 5+6), with merge
commits only. #24681 has merged (`12d14765c`), and `main` was merged in
again at `78f99c34c`. The one conflict was my edit of
`declared-webhooks.ts` against the squash of the same lines;
plugin-webhooks is byte-identical between #24681's head `c6f775cbd` and
`main`. The deferred `migration.mdx` regeneration is discharged at
`8f6bf26e6` and equals `main`. The diff against `main` is 26 files, +252
/ −291, with no governed path.
## What changes
**plugin-webhooks: step 1's dual read retires.**
- `classifyDeclaredNameHolder` is deleted. So are the dispatcher's
mirror skip (`AutoEnqueuer`'s `mirrorIds`) and the redeliver guard's
mirror branch.
- Any `sys_webhook` row 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 for `id` and `name` only.
- A delivery whose `ref_id` names a row is judged by that row.
- Docblocks are rewritten in `declared-webhooks.ts`, `auto-enqueuer.ts`,
`redeliver-guard.ts`, `sys-webhook.object.ts`,
`webhook-outbox-plugin.ts` and `webhook-provenance.ts`.
- `bindWebhookProvenanceStamp` is **kept**, not deleted (measured below:
`customized` still has a reader).
- `migrateLegacyWebhookSecrets`: only its docblock narrows (measured
below: no code narrowing is possible).
**Settings audit: step 2.**
- `sys_setting_audit.scope` no longer declares `global`, so the engine
refuses a global-scope row there (`VALIDATION_FAILED` on `scope`).
- The four `platform-objects` translation bundles and the es-ES source
hashes are regenerated (`node scripts/check-i18n-bundles.mjs --write`,
one leaf each).
- The "history reads both tables" docblocks are fixed in
`settings-service-plugin.ts` and `sys-platform-setting-audit.object.ts`.
**Identity mail: step 3, docblocks only.**
`sys-identity-email.object.ts`, `sys-email.object.ts`,
`email-log-objects.ts` and `email-plugin.ts` now say the ceremony moved
the rows. Nothing is removable:
- The body read gate on `sys_email` stays, because the move selects rows
by shape.
- A mail job with no object still reads `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 minus `global`, 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.
- The es-ES echo ledger: 45 → 44 echoes.
- `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).
- Every `sys_webhook` copy under a declared name is deleted, and its
deliveries are re-sourced to `declared:NAME`.
- `SELECT COUNT(*) FROM sys_setting_audit WHERE scope = 'global'`
returns 0.
- Both assertions sit in `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.
- The settings writer sends a global-scope entry to the sibling
(`settingAuditObjectFor`).
- **Walled boot.** The same reading under `isolated` with the real
`@objectstack/organizations`: 1/0, 2/2, 0.
- **`customized` still has a live reader: the ceremony.**
`runDeclaredWebhooks` in `organization-ownership-apply.ts` settles 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 reads `customized`" does not
hold, so the stamp stays and its docblock names the ceremony as its
reader.
- **`migrateLegacyWebhookSecrets`.** What 5a leaves:
- authored rows;
- the ceremony's clones, which keep their `definition_json`;
- seeded rows no package declares.
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 is `examples/embed-objectql`, a lean ObjectQL
embedding. It uses neither webhooks nor settings, so none of 7c's
readers is on its path. Every `objectstack.config.ts` app composes
plugin-auth, which registers `sys_organization`. The `os init` output
was not measured.
## Proof (head 8f6bf26 unless stated)
- **R1.** `pnpm dev -- --fresh -p 38963`: health 200, seeded-admin
sign-in 200, 0 error lines in the boot log. Readings above.
- **R2 + R3.**
- `upgrade-18x-harness.dogfood.test.ts`: 8/8.
- `organization-ownership-boot-refusal-order.dogfood.test.ts` passes.
- `identity-mail-read-gate.dogfood.test.ts` uses `ceremoniedFixture` and
passes.
- The six-file dogfood run totals 50/50, together with
`settings-audit-global-rung-sibling`, `webhook-materialization` and
`wall-declared-webhook-deployment-wide`.
- **R4.**
- `OS_TENANCY_POSTURE=isolated` `os 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 the `Organizations` plugin, and the walled
membership-policy gate was satisfied with `invite-only`.
- Step 4's 7 walled e2e cases (`OS_TEST_TIERS=nightly`,
`serve-organizations-host-resolution` +
`serve-organizations-mount-failure`): 7/7.
- **Suites.**
- plugin-webhooks 222/222 (21 files).
- service-settings 895/895.
- platform-objects 1199/1199.
- plugin-email 571/571.
- `typecheck` exits 0 for all four. `check-i18n-bundles`: all bundles in
sync.
- **Gates.**
- `node scripts/pm/dispatch-gates.mjs --commands` on 8f6bf26 derives
77 commands over the 26 paths. All 77 ran with exit codes captured
before any pipe, and all 77 exit 0. `--ran` reports 77 run, 0
NOT-MEASURED, a derived zero.
- Earlier, before #24681 landed, the derivation over the union diff gave
185 commands. 184 exited 0. `check-empty-changeset` flagged only
#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's `gen:docs` discharge.
- **Ablation of the main pin.** Through `scripts/ablation-replace.mjs`,
the mirror skip was put back into `AutoEnqueuer.resolveDeclared`: a
pristine `package` row is not counted as a holder. The anchor went 1 → 0
and the blob changed. `declared-webhooks.test.ts` went red, 2 failed /
20 passed: the regime-C pin and the switched-off-row pin. After the
restore, `git diff HEAD` was empty. The test imports the source by
relative path, so no `dist` rebuild was involved.
## NOT MEASURED
- `os init` output reaching `no-organization-object`: out of 7c's
readers' reach, not booted.
## Acceptance notes
- Under `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](https://claude.ai/code/session_01KMpT5c9dcX9kj3rBBWaki8)_
---------
Co-authored-by: Claude <noreply@anthropic.com>1 parent 4be7281 commit bd6dab0
29 files changed
Lines changed: 259 additions & 297 deletions
File tree
- .changeset
- packages
- platform-objects/src
- apps/translations
- audit
- system
- plugins
- plugin-email/src
- plugin-webhooks/src
- qa/dogfood/test
- services/service-settings/src
- spec/liveness
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
22 | 22 | | |
23 | 23 | | |
24 | 24 | | |
25 | | - | |
| 25 | + | |
26 | 26 | | |
27 | 27 | | |
28 | | - | |
| 28 | + | |
29 | 29 | | |
30 | 30 | | |
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
| 1 | + | |
| 2 | + | |
| 3 | + | |
| 4 | + | |
| 5 | + | |
| 6 | + | |
| 7 | + | |
| 8 | + | |
| 9 | + | |
| 10 | + | |
| 11 | + | |
| 12 | + | |
| 13 | + | |
| 14 | + | |
| 15 | + | |
| 16 | + | |
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
3411 | 3411 | | |
3412 | 3412 | | |
3413 | 3413 | | |
3414 | | - | |
3415 | 3414 | | |
3416 | 3415 | | |
3417 | 3416 | | |
| |||
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
3411 | 3411 | | |
3412 | 3412 | | |
3413 | 3413 | | |
3414 | | - | |
3415 | 3414 | | |
3416 | 3415 | | |
3417 | 3416 | | |
| |||
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
57 | 57 | | |
58 | 58 | | |
59 | 59 | | |
60 | | - | |
61 | 60 | | |
62 | 61 | | |
63 | 62 | | |
| |||
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
3411 | 3411 | | |
3412 | 3412 | | |
3413 | 3413 | | |
3414 | | - | |
3415 | 3414 | | |
3416 | 3415 | | |
3417 | 3416 | | |
| |||
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
116 | 116 | | |
117 | 117 | | |
118 | 118 | | |
119 | | - | |
120 | | - | |
121 | 119 | | |
122 | 120 | | |
123 | 121 | | |
| |||
180 | 178 | | |
181 | 179 | | |
182 | 180 | | |
183 | | - | |
184 | 181 | | |
185 | 182 | | |
186 | 183 | | |
| |||
239 | 236 | | |
240 | 237 | | |
241 | 238 | | |
242 | | - | |
| 239 | + | |
243 | 240 | | |
244 | 241 | | |
245 | | - | |
246 | | - | |
| 242 | + | |
| 243 | + | |
| 244 | + | |
| 245 | + | |
| 246 | + | |
247 | 247 | | |
248 | | - | |
| 248 | + | |
249 | 249 | | |
250 | 250 | | |
251 | 251 | | |
| |||
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
3411 | 3411 | | |
3412 | 3412 | | |
3413 | 3413 | | |
3414 | | - | |
3415 | 3414 | | |
3416 | 3415 | | |
3417 | 3416 | | |
| |||
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
100 | 100 | | |
101 | 101 | | |
102 | 102 | | |
103 | | - | |
104 | | - | |
105 | | - | |
106 | | - | |
| 103 | + | |
| 104 | + | |
| 105 | + | |
| 106 | + | |
| 107 | + | |
| 108 | + | |
107 | 109 | | |
108 | 110 | | |
109 | 111 | | |
| |||
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
19 | 19 | | |
20 | 20 | | |
21 | 21 | | |
22 | | - | |
23 | | - | |
24 | | - | |
| 22 | + | |
| 23 | + | |
| 24 | + | |
| 25 | + | |
25 | 26 | | |
26 | 27 | | |
27 | 28 | | |
| |||
0 commit comments