Skip to content

docs(activation): capture the denied-permission dead-end learning (#5609)#5624

Merged
koala73 merged 2 commits into
mainfrom
docs/5609-activation-blocked-state-learning
Jul 25, 2026
Merged

docs(activation): capture the denied-permission dead-end learning (#5609)#5624
koala73 merged 2 commits into
mainfrom
docs/5609-activation-blocked-state-learning

Conversation

@koala73

@koala73 koala73 commented Jul 25, 2026

Copy link
Copy Markdown
Owner

Follow-up to #5609 / PR #5615 (merged). Docs only — no code changes.

Why

This was the fourth follow-on fix to the same activation wizard — #5534 (the origin feature) → #5600#5607#5609 — with no learning captured for the area. The reusable parts were being rederived each time.

What's here

docs/solutions/logic-errors/denied-push-permission-rendered-as-retryable-failure.md records the bug class behind #5609: a terminal platform refusal modelled as a transient failure. The confirm handler's 'verified' | 'failed' union had no way to say "do not retry", and failed is what puts a "Try again" button on screen — so a denied notification permission, which no retry can ever clear, got one.

Four lessons the fix's review surfaced, each with the concrete failure mode:

  • Classify from live platform state, not the thrown error. Matching the denial error string was the obvious route and is wrong: strings get reworded, localized, or wrapped, and the classification degrades silently with no test failing. getPushPermission() is authoritative and already exposed.
  • When a state becomes reachable at a new time, funnel every read through one accessor. The blocked state was previously mount-only, so direct step.state reads were safe; making it reachable mid-flow turned each one into a latent bug. Two reviewers independently found two un-migrated reads — inert then, silent failures later.
  • When a fix reclassifies an outcome into an existing bucket, check what signal that erases. Moving denials from failed to skipped was right for the user and destroyed the ability to size the denial cohort. Restoring it cost ~6 lines; the durable half is Activation outcomes: a browser-blocked step is indistinguishable from a voluntary skip in the durable record #5617.
  • A test DOM whose innerHTML getter replays the assigned string will go vacuous. It is sound only while nothing mutates the subtree post-render — and this component already does that elsewhere (the brief step's preview swap, verified at ProActivationInterstitial.ts:1055-1056 on main). Once coverage reaches there, doesNotMatch starts passing because nothing was parsed rather than because the thing is absent: green while red.

Also adds an Activation Step State entry to CONCEPTS.md. The existing Activation Interstitial entry describes the flow but not the per-step state vocabulary, and specifically not the terminal-vs-retryable distinction that this bug turned on.

Grounding

Every file:line citation was verified against merged main (8d225505f), not the local checkout — one had drifted and was corrected. Both bundled validators pass (frontmatter parser-safety; mechanical path/SHA/link claims), as do biome and markdownlint. Cross-checked specifically:

An overlap check across docs/solutions/ scored Low — the closest neighbour is billing-state-cancelled-but-paid-through-misclassified-as-lapsed.md, which is the same root-cause class running in the opposite direction (a state that merely sounded terminal treated as terminal). The two are cross-linked rather than merged.

Post-Deploy Monitoring & Validation

No additional operational monitoring required — documentation only, no runtime surface.

)

The fourth follow-on fix to the activation wizard (#5534 -> #5600 -> #5607
-> #5609) with nothing captured, so the reusable parts were being rederived
each time.

Records the bug class behind #5609 — a terminal platform refusal modelled as
a transient failure, which is what put a retry button on a state no retry can
clear — plus four things the fix's review surfaced: classify from live
platform state rather than a thrown error string; funnel every read through
one accessor when a state becomes reachable at a new time; check what signal
is erased when a fix reclassifies an outcome into an existing bucket; and a
test DOM whose innerHTML replays the assigned string goes vacuous rather than
failing.

Adds an Activation Step State entry to CONCEPTS.md for the terminal-versus-
retryable distinction the existing Activation Interstitial entry did not
cover.

Claude-Session: https://claude.ai/code/session_01SirbngHaZJc6i9jMJ5mxhF
@vercel

vercel Bot commented Jul 25, 2026

Copy link
Copy Markdown

The latest updates on your projects. Learn more about Vercel for GitHub.

Project Deployment Actions Updated (UTC)
worldmonitor Ready Ready Preview, Comment Jul 25, 2026 10:08pm

Request Review

@mintlify

mintlify Bot commented Jul 25, 2026

Copy link
Copy Markdown
Contributor

Preview deployment for your docs. Learn more about Mintlify Previews.

Project Status Preview Updated (UTC)
WorldMonitor 🟢 Ready View Preview Jul 25, 2026, 10:02 PM

💡 Tip: Enable Workflows to automatically generate PRs for you.

@mintlify

mintlify Bot commented Jul 25, 2026

Copy link
Copy Markdown
Contributor

Preview deployment for your docs. Learn more about Mintlify Previews.

Project Status Preview Updated (UTC)
WorldMonitor 🟡 Building Jul 25, 2026, 10:01 PM

💡 Tip: Enable Workflows to automatically generate PRs for you.

@koala73
koala73 merged commit f73de5b into main Jul 25, 2026
27 checks passed
@koala73
koala73 deleted the docs/5609-activation-blocked-state-learning branch July 25, 2026 22:29
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant