Repository navigation
fix(plugin-webhooks, cli): a departed package's activation row switches no remaining webhook holder, and the ownership ceremony never picks a holder for a two-holder copy (ADR-0150 D8) - #33253
Conversation
…es no remaining webhook holder; a two-holder copy's switch names no package Claude-Session: https://claude.ai/code/session_016WV4cyrFFk86Jqf7Mp5Rzw Co-authored-by: Claude <noreply@anthropic.com>
📓 Docs Drift CheckThis PR changes 2 package(s): 1 hand-written doc(s) NAME something this change touched and may need an implementation-accuracy re-verification:
What this run could not see
Coarse fallback — 39 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 e2c976000d4c5184968fc6b189a965b59f21911e && git checkout e2c976000d4c5184968fc6b189a965b59f21911e
# afterwards, rebuild it from the two parents, which stay fetchable
git fetch origin 199e3624426faa50f413e18657abc44ea20ff3cb 9eefd98b899ad3563fe5a10c31063df1cb3ebba4 && git checkout -B drift-repro 199e3624426faa50f413e18657abc44ea20ff3cb && git merge --no-ff 9eefd98b899ad3563fe5a10c31063df1cb3ebba4
node scripts/docs-audit/affected-docs.mjs --json 199e3624426faa50f413e18657abc44ea20ff3cb
|
…neration-1 test Claude-Session: https://claude.ai/code/session_016WV4cyrFFk86Jqf7Mp5Rzw Co-authored-by: Claude <noreply@anthropic.com>
Contract reviewServed-tier: ① Derived judgments
② Semver level
③ Boundary flags
Implemented-by: VERDICT: FAIL S1 (code + pin) and S2 (declaration) → delta review by review#3 on the new head. N1 optional. |
…when the environment is a holder; tell both unowned-row kinds Review round 1 on the departed-row fix. In the 18.x ceremony's declared-webhooks step, a customized copy naming no package under holders that include the environment wrote a package-less row, which the switch reads as the environment slot's own: it picked the environment. It now writes no switch row and reports the copy's switch naming every holder. reportUnownedRows tells a name both a departed row and a package-less row under two holders. The changeset re-declares Clause-② (mixed) and carries the operator line for a webhook switched off through a departed row. Claude-Session: https://claude.ai/code/session_016WV4cyrFFk86Jqf7Mp5Rzw Co-authored-by: Claude <noreply@anthropic.com>
|
Contract-review-claim: session_016WV4d1PGptBW3QRQNmy5e6 · head 9eefd98 · 2026-11-14T15:29:41Z review#3 delta. |
Contract reviewServed-tier: Delta review of ① Derived judgments
② Semver level
③ Boundary flags
④ Design judgmentADR-0150 D8 makes an activation row its holder's switch, never another's; reading a departed package's row as nobody's switch is the direct application, and the ceremony now refuses to pick a holder in the one composition where a package-less row would silently mean the environment. The outbound-delivery consequence is real but is the ADR's intended answer, and the release note plus warn give the operator the one action needed. Holds against the cited code and the card's scenario. Implemented-by: VERDICT: PASS |
|
domain:server#5 · os-elon-musk · session_016WV4cyrFFk86Jqf7Mp5Rzw |
|
domain:server#5 · os-elon-musk · session_016WV4cyrFFk86Jqf7Mp5Rzw
|
Requested by Elon · project thread
Fixes #32695
Clause-②: yes (widening; narrowing on a departed row switched on) — widening: a departed package's row switched off no longer keeps a remaining holder off, so its outbound deliveries start on upgrade. Narrowing: a departed package's row switched on no longer turns on a holder shipped inactive.
Metadata-protocol: none
Area-lease pilot #33059 · area E2 · seat
domain:server#5· os-elon-musk · session_016WV4cyrFFk86Jqf7Mp5Rzw维护者速读
packages/plugins/plugin-webhooks/src/declared-webhooks.ts的webhookSlotSwitch与reportUnownedRows;packages/cli/src/utils/organization-ownership-apply.ts的 declared-webhooks 步骤。pnpm --filter @objectstack/plugin-webhooks exec vitest run src/declared-webhooks.test.ts,预期 59 个全过(含[#32695]四条)。What changes
S1 — a departed package's activation row (
plugin-webhooks).webhookSlotSwitchread, for a slot with no row of its own, every row that is no slot's own: rows naming no package AND rows naming a package that no longer declares the name. Under one holder those were its switch (if (slots.length <= 1) return unowned.every(Boolean)), so a departed package's row switched the remaining holder in both directions; under two, an off row kept both off. Now only a row naming no package (while the environment declares nothing of the name) is read that way — the D8 row a name-keyed reader wrote before. A departed package's row switches no slot, at any holder count, andDeclaredWebhookSourcesays so once per name atwarn(new exportdepartedWebhookRows), naming the departed package, the holders and the per-holder address?package=PACKAGE(PR #33204).S4 — the 18.x ownership ceremony (
cli). The declared-webhooks step builtnew Map(list.map(d => [d.name, d.packageId])), so under two holders of a name the holder listed LAST was the fallback for a customized copy whose definition names no package. Now the step keeps every holder per name; such a copy's activation row names the name's one holder, or — under two or more — no package (the name-keyed switch it was: off keeps each holder with no row of its own off, on turns none on), and the step's notes name every holder and the per-holder address. The card offered "refuse or report"; reporting keeps the run from blocking on a copy whose switch has a non-guessing spelling, and nothing is picked.FROM → TO:
On
Clause-②: re-declaredyes (widening; narrowing on a departed row switched on)per review S2, the spelling review#3 accepted on #33252: the departed-off rows start outbound deliveries on upgrade with no operator action (widening), the departed-on row stops one (narrowing). The changeset carries the operator line: to keep such a webhook off, switch the holder off atPOST /api/v1/security/_activation/webhook/NAME?package=PACKAGE; thewarnnames that address. No export in the published index moves (departedWebhookRowsis module-internal).Review round 1 (head 399e550, review#3 FAIL) → 9eefd98: S1: when the environment is one of a name's holders, the ceremony no longer writes a package-less row for a copy naming no package (that row is the environment slot's own switch, so it picked the environment); it writes none and reports the copy's switch naming every holder. A clone of such a copy is not switched either and says so. S2: Clause-② re-declared, operator line added. N1:
reportUnownedRowsno longer returns after the departed-row warning, so a name with both a departed row and a package-less row under two holders is told both, each once.Tests
plugin-webhooks/src/declared-webhooks.test.ts: the[D8] one holderpin no longer pins the departed row as the switch (that is the defect); new describe[#32695] a departed package's row switches no remaining holder: one holder both directions; two holders in both registration orders; said once naming departed package, holders and the address; CONTROL package-less row and own row; two holders with a departed row and a package-less row — each warning said once (N1).cli/src/utils/organization-ownership-apply.generation-1.test.ts(integration project): two holders with the second listed last and first — the row names no package, one note naming both; the environment and a package, the environment listed last and first — no switch row, one note naming both, and not the name-keyed note; CONTROL a copy naming its package beside a second holder listed first; CONTROL one holder, copy naming no package.Acceptance notes
setActivation(cli) reads and updates an existing activation row by(metadata_type, name)only, whatever itspackage_id. Pre-existing and unchanged here; on an 18.x database the ceremony runs before any per-package row exists. Noted, not filed (no repro on a real upgrade path).declared:NAME(no package) for deliveries; a two-holder redelivery of such a row is refused naming both ([Q4]pin, unchanged).Verification
Ablation (both source files reverted to origin/main, tests kept)
plugin-webhooks
declared-webhooks.test.ts:Tests 3 failed | 56 passed (59)— one-holder both directions, two holders both orders, said-once red; CONTROL green. cli generation-1:Tests 2 failed | 19 passed (21)— both two-holder orders red; CONTROLs green. Restored withgit checkout HEAD -- …,git diff HEADempty.Ablation, review round 1 (the fix reverted to 399e550, new pins kept)
cli generation-1:
Tests 2 failed | 21 passed (23)— both environment-and-package orders red. plugin-webhooks:Tests 1 failed | 59 passed (60)— the N1 pin red. Restored from the saved fixed copies, diff re-checked.Local runs at fab0533
The repo gate farm is left to CI on this PR.
Generated by Claude Code
🤖 Generated with Claude Code
https://claude.ai/code/session_016WV4cyrFFk86Jqf7Mp5Rzw