Repository navigation
3.0.0 (2026-10-21)
Bug Fixes
- analytics: gate attribution capture on posthog config + stack-update ordering note (#4584) (9e61b85), closes #4024
- app: keep the requested page across sign-in (#4725) (713b464)
- auth: identify the signed-in user again on session restore (#4752) (#4755) (1447360)
- auth: re-fetch serverConfig after in-SPA signin/signup (#4539) (1cb1c98), closes Node#3566 #4261 #4538
- auth: signout resets serverConfig to null (#4541) (2c02fac), closes #4539 #4540 #4459
- auth: stop signin auth-watcher from racing navigation past validate() (#4534) (0addeb9), closes #4528 #4533 #4533
- auth: stop signup auth-watcher from racing past email verification (#4528) (cbf4947), closes #4437
- billing: #4654 follow-ups (packs empty-state, stale comments, migration note) (#4674) (62eaedc)
- billing: compute gauge and weekly meter go quota-only, matching quota-threshold alerts (#4550) (7d881f6), closes #4548
- billing: guest plan CTAs go to sign-up and return to pricing (#4678) (cf553ad)
- billing: move the zero-quota overage guard into BillingMeterProgressComponent (#4554) (7effea0), closes #4553
- billing: render pending cancellation on the subscriptions panel (#4551) (1318444)
- config: derive the apiBase() test expectation from the loaded config (#4690) (53e064f), closes #4689
- home: replace real third-party brand defaults with RFC-2606 placeholders (#4623) (b0de8a5), closes Vue#4340
- modules: warn on config.modules.* keys that match no registered module (#4529) (1ab89c4), closes #4480 #4480
Features
- analytics: consent_choice on decline via PostHog cookieless mode (#4588) (319a5fd), closes #4587
- analytics: first-touch attribution capture + consent_choice event (#4572) (c6feca7), closes #4520
- auth: optional sign.accentImage above the sign-in/sign-up title (#4744) (e7ed70f)
- auth: optional sign.upNotice line under the sign-up form (#4742) (1689f17), closes #4730
- billing: skip live plans fetch under SEO prerender + shared isPrerenderCrawl helper (#4543) (779eb10), closes #4542
- home: configurable image on the not-found page (#4743) (1fe5ed4)
- invitations: shareable referral link, capture ref and show personal link (#4754) (#4756) (c637db5)
- seo: serve a build-time docs API snapshot during prerender capture (#4531) (ea3e6b9), closes #4530 #4530 #4530 #4530
- skills: thin store actions and crud-only module scaffold (#4712) (f54158e), closes pierreb-devkit/Vue#4711 pierreb-devkit/Node#4173
- users: email preference switches on account page (#4709) (7f6adac), closes #4708 #4459
BREAKING CHANGES
- the top-level
plans/packsstatic-content collections and the
legacytabs: { plans, units }label object are removed, with no compatibility
bridge. A downstream project moves its plans and packs intotabs[]entries in
the same pass that absorbs this change, and setsannualToggle: trueon each
tab billed annually. See MIGRATIONS.md.
Claude-Session: https://claude.ai/code/session_01VAeEZbYQnihV4gcnGFnc6L
- fix(billing): warn when tabs config is provided but unusable
normalizeTabs() silently fell back to the devkit demo catalogue
whenever the resolved tabs value was not a non-empty array, with no
signal — unlike the neighbouring both-slots-filled case, which
already warns. A downstream consumer still on the pre-migration
tabs: { plans, units } object shape would render demo plans and
packs on a live pricing page with nothing telling them why.
Thread whether the tabs key was explicitly present in config (vs.
simply absent) into normalizeTabs so it can warn only on an
explicit-but-unusable value, keeping the default-install path silent.
Claude-Session: https://claude.ai/code/session_01VAeEZbYQnihV4gcnGFnc6L
- fix(billing): gate pricing and checkout on the active tab's annual option
Apply the pre-merge review findings on the pricing tabs change.
effectiveAnnualnow gates every annual-dependent decision: the displayed
amount, the /year period, the savings chip, and the price id that reaches
checkout. A tab declaringannualToggle: falsecould previously still send
an annual price id to checkout while rendering a disabled toggle.resolvePlanPricinglowercases both sides of the case-insensitive Stripe
name fallback, so a static id likePromatches a Stripe namePROinstead
of silently losing its price objects and leaving checkout unavailable.- JSDoc headers on the modified computeds.
Claude-Session: https://claude.ai/code/session_01VAeEZbYQnihV4gcnGFnc6L
- refactor(billing): name the Stripe plan match predicate
The match logic passed to candidates.find() moves into a named
matchesStaticPlan() helper carrying a full JSDoc header, leaving a
one-line callback at the call site.
Review feedback asked for a JSDoc header on the inline predicate. The
repository documents named functions rather than inline callbacks (7 of
459 array callbacks in src/ carry one), and toAmount() sits directly
above as that same pattern, so extracting satisfies the guideline in the
codebase's own idiom instead of introducing an outlier.
Pure refactor: no behaviour change, 2755/2755 unit tests unchanged.
Claude-Session: https://claude.ai/code/session_01VAeEZbYQnihV4gcnGFnc6L