Conversation
no issue ## Summary - Removed the Linear issue triage Agentic Workflow source markdown - Removed the generated GitHub Actions lock workflow for the Linear triage agent ## Why The Linear triage workflow has been failing, and this ends the initial automation experiment instead of continuing scheduled runs.
Closes https://linear.app/ghost/issue/DES-1166/support-escalation-re-email-needs-verification Previously we would imply that newsletter sending was still possible when access was set to Nobody. Access = Nobody disables newsletter sending. The messaging has been updated to reflect that. The disabled state has also been improved. | Before | After | |--------|--------| | <img width="714" height="353" alt="Screenshot 2026-05-01 at 12 53 36" src="https://github.com/user-attachments/assets/8e5cb169-53d1-4f29-a9b2-76573678273a" /> | <img width="707" height="375" alt="Screenshot 2026-05-01 at 12 53 09" src="https://github.com/user-attachments/assets/77bd1013-fe0b-44f2-8baa-92da92c94a07" /> |
…7668) ref https://linear.app/ghost/issue/BER-3600/design-iteration Refreshed every screen in the gift subscription flow — selection, confirmation, redemption, and the magic link state when redeeming — with a single shared 50/50 split layout: action on the left, gift card preview on the right. ### What's new - **Unified gift card preview** across all four screens — same site icon + duration + tier, brand-tinted gradient, cross ribbons, wrapped-bow SVG, subtle cursor-follow tilt. - **Selection screen**: title + duration toggle + tier radio list + pill CTA on the left; benefits below the card on the right with smooth height animation when switching tiers. - **Confirmation screen**: shareable link pill with copy button on the left; reused gift card on the right with collapsible "Gift details" toggle. - **Redemption screen**: title "A gift, just for you" + name/email form + redeem button; same toggle reveals tier benefits with card tilt that makes them look like they're sliding out from behind the card. - **Magic link in gift mode**: when reached via gift redemption, swaps the centered modal for the same 50/50 layout so the gift card stays put across the form → "Now check your email!" transition.
ref https://linear.app/ghost/issue/BER-3565 ref https://linear.app/ghost/issue/BER-3542 ref #27615 ref https://ghost.slack.com/archives/CJBJ3MZFD/p1777972226248739 ## What changed The migration `2026-04-29-12-13-44-add-batch-id-to-members-status-events.js` now passes `{algorithm: 'auto'}` to `createAddColumnMigration`, so the emitted DDL is `ALTER TABLE members_status_events ADD COLUMN batch_id...` omits the default `ALGORITHM=COPY`. ## Why `createAddColumnMigration` delegates to `commands.addColumn`, which on MySQL appends `ALGORITHM=COPY` by default unless `{algorithm: 'auto'}` is explicitly passed. `ALGORITHM=COPY` rebuilds the entire table: it creates a new copy with the new column, copies every row across, then swaps it in. The cost is O(rows), and on `members_status_events` — which gets one row per member status change — this scales with the size of the member base. We hit this on a staging site with ~6M `members_status_events` rows on boot: ``` [MIGRATE] Migration error: alter table `members_status_events` add `batch_id` varchar(24) null, algorithm=copy - Connection lost: The server closed the connection. ``` `ALGORITHM=AUTO` lets MySQL pick the best strategy. For a nullable trailing-column add on MySQL 8.0+ it picks `INSTANT`, which is a metadata-only change and effectively constant-time regardless of table size. This isn't a new conclusion — [#25018 (`Added utm_ columns to events tables`)](2ea046c) introduced the `algorithm` option for exactly this reason after observing 60–250s migrations on a 2M-row `members_created_events` table vs. 2–3s when MySQL was allowed to choose. All the column adds in that PR (and the canonical `add-utm-fields.js` migration) use `{algorithm: 'auto'}`. ## Why we're modifying an existing migration We don't normally do this — `.agents/skills/create-database-migration/rules.md` states migrations are immutable once on `main`. We're making an exception here because: 1. **A new migration cannot fix the original.** The bad `ALTER TABLE` runs first when production upgrades through `6.36/`. Adding a fix in a later folder doesn't change that. 2. **The schema is unchanged.** Only the MySQL execution hint differs. `schema.js` is untouched, so the integrity hash isn't bumped, and the end-state column definition is identical. 3. **The migration helper is idempotent.** `createColumnMigration` checks `hasColumn` and skips if the column already exists — so sites that already ran the original version on `6.36.0` simply no-op when redeployed against this commit. They don't see the migration twice and there's no schema drift between sites that ran the old version and sites that will run the new one. In short: existing sites that already migrated are unaffected; sites that haven't yet (including production) get the fast path.
## Summary This PR consolidates core components in Shade. It restructures three existing components into three primitives that later on will compose into Shade components to eliminate duplication. **No existing apps (Stats, Posts etc.) change in this PR**, so risk is contained to the new files. - **`TrendBadge`** — value + direction (up / down / same) with optional tooltip. Replaces the duplicated value+arrow markup currently inlined in `card.tsx` (`KpiCardHeaderValue`) and `tabs.tsx` (`KpiTabValue`). - **`MetricValue`** — generic label + large value + "after" slot, sized `md` / `lg`. The two KPI wrappers above will compose this around `TrendBadge` in Phase 3. - **`surfaceField(mode)`** — shared border / bg / radius / focus-ring / invalid style recipe which is currently copy-pasted across `input.tsx`, `textarea.tsx` and `input-group.tsx`. Exposed as a recipe helper rather than a wrapper component since every consumer already has its own root element. `'self'` mode applies directly to a focusable element; `'within'` mode applies to a wrapper that contains a focusable child. ref https://linear.app/tryghost/issue/DES-1343 ## Why now Shade already has solid tokens and primitives. The next weak spot is the component layer itself. `apps/shade/src/components/ui/` has duplicated visual recipes and KPI markup repeated across files. This milestone tightens that. Phase 1 ships only additive primitives so each subsequent phase can refactor one consumer at a time behind a green smoke test.
## Summary This PR is part of consolidating core components in Shade. **Stacked on #27565.** It finishes what the first PR set up: the new primitives (`TrendBadge`, `MetricValue`, `inputSurface`) are in place; this one cleans up where things live. - KPI wrappers moved out of `ui/card.tsx` and `ui/tabs.tsx` into `features/kpi/`. New `KpiTabs` / `KpiTabsList` / `KpiTabsContent` wrappers added so future code can stop writing `<Tabs variant='kpis'>` directly. - `gh-chart.tsx` (Ghost-flavoured analytics chart) moved out of `ui/` into `features/charts/`. - `filters.tsx` (full filter-builder feature) moved out of `ui/` into `features/filters/`. - `Foundations` pinned at the top of the Storybook sidebar order. Grep guardrail run: `ui/` is clean of product-domain words in production code. ref https://linear.app/tryghost/issue/DES-1343 ## Why now This is the consumer-affecting half of Shade core component work: they **don't** affect any consumer files. All consumers import via the `@tryghost/shade/components` and `@tryghost/shade/patterns` entrypoints, and those were updated to point at the new internal paths. The `kpis` cva variant remains in `ui/tabs.tsx` as an internal implementation detail used only by `features/kpi/kpi-tabs.tsx`. Removing it cleanly would have required baking a long override class string into the wrapper and fighting tailwind-merge to neutralize segmented defaults — not worth the regression risk. ## Trade-offs - `ui/multi-select-combobox.tsx` imports types from `features/filters/`. That's a layering inversion (`ui/` depending on `features/`) — pre-existing, out of scope to fix here. - TypeScript path aliases like `@/components/...` survive into emitted `.d.ts` files and break consumers (their tsconfig doesn't share shade's path mapping). All `features/*` files in this PR use **relative imports** for cross-shade-file type references for this reason.
…tion (#27669) ## Summary Rewrites the Shade Storybook documentation pages so they read like prose written by a human, not bullet points generated by a machine. Adds a proper **Terms** section to the Architecture page that defines the words we throw around (tokens, primitives, components, recipes, layouts, features, app). ref https://linear.app/tryghost/issue/DES-1343 ## What changed Six docs rewritten: - **introduction.mdx** — Welcome page. What Shade is, who it's for, getting set up. - **architecture.mdx** — How the layered system works. **New Terms section** defining each layer with examples. Includes the file-system-vs-entrypoint mismatch (`features/` on disk vs `@tryghost/shade/patterns`) that's been a quiet source of confusion. - **tokens.mdx** — What tokens are, how they're defined, how to consume them. - **primitives-guide.mdx** — Why primitives exist, when to reach for each, prop reference, migration examples. - **component-contracts.mdx** — What you can rely on from a Shade component (rules and guarantees). - **contributing.mdx** — Practical guide for adding to or changing Shade. One doc left alone: - **migration-root-imports.mdx** — Already a tight reference table; that's the right format for it.
no ref - `pnpm audit` reported 15 axios advisories — 5 high, 9 moderate, 1 low — all in versions `<1.15.2`. They're the prototype-pollution / SSRF / header-injection cluster published in the past two weeks. - Two resolved versions of axios in the tree: `1.13.6` (via `nx`, root devDep) and `1.15.0` (via `@slack/webhook`, ghost/core). Both are vulnerable. The override `axios@<1.15.2: ^1.15.2` pins both up to a patched version within the same major. - Same-major bump; no caller API changes. Pure lockfile/override change — no source files touched.
no ref Three vite advisories landed against apps/admin's pinned `7.1.12` — two high-severity (dev-server WebSocket file-read, `server.fs.deny` query-string bypass) and one moderate (path traversal in optimized-deps `.map` handling). All three are patched in `7.3.2`, which is a same-major patch bump within v7.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to subscribe to this conversation on GitHub.
Already have an account?
Sign in.
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
See Commits and Changes for more details.
Created by
pull[bot] (v2.0.0-alpha.4)
Can you help keep this open source service alive? 💖 Please sponsor : )