docs(engagement): Phase 11b's live walk — three decisions and six defects #197
Reference in New Issue
Block a user
No description provided.
Delete Branch "docs/engagement-phase-11b-walk"
Deleting a branch is permanent. Although the deleted branch may continue to exist for a short time before it actually gets removed, it CANNOT be undone in most cases. Continue?
The design of record for Phase 11b's acceptance walk, which closes the phase. Pairs with website#179, Module-uo#24 and servuo-plugins#19.
The walk was not a formality. It found six defects, four of them in code shipped by earlier phases, and none of the six was visible in a unit test — the reason being the same every time: each is a disagreement between two things that agree with each other in a fixture.
Three decisions, settled by the org lead before any code
uo.house.idoc_warningshipsdelay_seconds: 900and nothing could cancel ituo.house.refreshed— a 26th trigger, with a body and a seeded rule of its ownchanneljoins the cooldown key — a cooldown is per delivery, not per occasionuo.vendor.expiringcould not fire, because the market sweep does not diff feesBridgeMarket.Signature()withexempt+periodsRemainingDecision 11 closes a gap §4.2a already named: a delay without a cancellation is just a late mail. The rule's
cancel_onlisted the outcome where the warning is pointless (the collapse) and not the one where it is wrong (the repair) — and the wire had been carrying that transition all along. It ships with a body as well, which was the org lead's call rather than the minimum: an office that only ever writes when something is wrong teaches its reader to dread the seal.Decision 12 has the widest blast radius and had been latent since Phase 4a. The claim runs inside the engine's per-channel loop, so the first channel claimed the cooldown and every later one was reported as
cooled— andinappis ranked first deliberately, so a rule naming email and in-app delivered the inbox item and silently never the mail. Core's ownnews.postrule has that shape. Every in-universe body Phase 11b wrote was unreachable behind its own in-app twin. Claiming once per user before the loop was the alternative and was refused: an operator who says "one a day about this house" means one mail and one inbox item.Decision 13 is a Phase 10 omission.
feeswas added to the frame and never added to the sweep's change detector, so the notification whose whole subject is a vendor running out of gold could fire only when somebody happened to reprice an item on a shop that was already broke.Four repairs that needed no decision, because the answer was a fact
max_sends_per_hourvalidated and then dropped from the normalized rule, against aNOT NULLcolumn. The test asserted the rejection of a bad ceiling and never that a good one survives.owner-audienced trigger resolved to nobody —link.user_idwhere the model returnsuserId, and it looks exactly like the ordinary unlinked-account case, which the code treats as normal and does not log. The test fake returneduser_idand agreed with the bug, while the fake next door, for the same model, returneduserId.module.json'smounts, so every declaredexamplewas a 404; and nourlvariable was ever populated, so the buttons rendered with an empty href and vanished.What the walk proved, rung by rung
Against real ServUO + the release sidecar + a live SMTP catcher: a
Greatlytransition produced one notification to the linked owner on both channels and nothing to a second linked-and-subscribed player; the next transition inside the day was cooled; the owner logging back in cancelled both pending rows and sent the reassurance letter; a vendor drawn to its last period warned its owner once and was cooled on the second crossing; a failed game login over a real socket produced one notice and a successful one produced none; three genuine shutdown/boot cycles produced one downtime fan-out and two fully-cooled ones; a governor seated on a livecity.updateproduced both the town's bulletin and the crown's letter; a real staff action reached the admin and neither the editor nor the moderator, both subscribed; and the rule editor refusedstaff,authenticatedandeveryoneon anadmin-ceiling trigger while acceptingadmin.Files
website/ENGAGEMENT.md— the three decisions, the four repairs, the rung-by-rung result, and the ServUOAgeless-vs-LikeNewfact. The 11b bullet and §8.6's family table now read 26 triggers / 34 bodies.website/BACKEND_DESIGN.md—engagement_cooldownsgainschannelin its PRIMARY KEY, with the migration'sinformation_schemaguard and why MariaDB forces one.link/v5.md— the sweep has to diff the fees or the frame never comes, stated as the general rule for the next enrichment since it is emit cadence rather than frame shape.modules/uo/API.md— §5.7a the cancel-shaped trigger; §5.7b every link comes fromconfig/clientPaths.js, and the two mistakes that made every call-to-action dead.AI-assisted: written with Claude Code.
🤖 Generated with Claude Code
The walk is the last piece of 11b and it was not a formality. It found six defects, four of them in code shipped by earlier phases, and none of the six was visible in a unit test: each is a disagreement between two things that agree with each other in a fixture. Three of the fixes were decisions rather than repairs, all settled by the org lead before any code: 11 `uo.house.idoc_warning` ships delay_seconds: 900 and nothing could cancel it -> add `uo.house.refreshed`, a 26th trigger with a body and a rule 12 a rule with a cooldown delivered on exactly ONE of its channels -> `channel` joins the cooldown key; a cooldown is per DELIVERY 13 `uo.vendor.expiring` could not fire, because the market sweep does not diff fees -> widen BridgeMarket.Signature() with exempt + periodsRemaining Files: website/ENGAGEMENT.md the three decisions, the four repairs, and what the walk proved rung by rung; the 11b bullet and the §8.6 family table now read 26 triggers / 34 bodies website/BACKEND_DESIGN.md engagement_cooldowns gains `channel` in its PRIMARY KEY, with the migration's information_schema guard and why MariaDB forces one link/v5.md the sweep has to DIFF the fees or the frame never comes -- stated as the general rule for the next enrichment, since it is emit cadence and not shape modules/uo/API.md §5.7a the cancel-shaped trigger and the Ageless-vs- LikeNew ServUO fact; §5.7b every link comes from config/clientPaths.js, and the two mistakes that made every call-to-action a dead link Pairs with website#<core>, Module-uo#<uo> and servuo-plugins#<plugin>. Co-Authored-By: Claude <noreply@anthropic.com>