Skip to content

[pull] main from TryGhost:main - #1464

Merged
pull[bot] merged 15 commits into
code:mainfrom
TryGhost:main
Sep 2, 2026
Merged

[pull] main from TryGhost:main#1464
pull[bot] merged 15 commits into
code:mainfrom
TryGhost:main

Conversation

@pull

@pull pull Bot commented Sep 2, 2026

Copy link
Copy Markdown

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 : )

rob-ghost and others added 15 commits September 2, 2026 11:18
A member who is subscribed to a newsletter the publisher has since archived could
not save anything from their account panel. Ghost tried to preserve the archived
newsletter even when the request was not changing newsletters at all, and had
nothing to add it to, so the whole save failed and the member was shown an error
however they got there. Archived newsletters are now only carried over when the
request is actually setting newsletters, which is the only time there is anything
to carry them into.
closes https://linear.app/ghost/issue/BER-3902/

- removed the `giftSubCustomization` labs flag and made the released behavior unconditional
- promoted the released Portal gift pages to the canonical component paths and removed the legacy implementations
- made the Admin gift-promotion settings unconditional and removed obsolete rollout tests
ref https://linear.app/ghost/issue/BER-3864/differentiate-public-and-private-member-projections

Ghost assembles one member payload and several surfaces narrow it independently
on the way out, each with its own allowlist and no shared definition. Two of
those surfaces are fed by a single read: the members API response Portal
consumes, and the @member data a theme renders. That read is about to move
behind a member-account module with a recipe of its own, so what each surface
currently receives needs to be written down first, while it is still
demonstrably unchanged. Anything that differs afterwards is then a decision
someone made rather than drift nobody noticed.

The members API snapshots cover a free, a paid and a comped member, because
those three assemble differently: a comped member's subscription is synthesized
from their products rather than read from Stripe, and only a paid member
exercises the offer and next-payment steps. Whole bodies are snapshotted rather
than selected keys, since a key appearing or vanishing is the failure worth
catching and asserting named keys would miss it. The theme's shape is pinned by
having the test theme render the keys of @member rather than their values,
which keeps the assertion about the contract instead of the fixture data.

The comped member is built in the test: the comped@test.com fixture is
status paid despite its name, so it never reaches the branch it appears to
cover.
ref https://linear.app/ghost/issue/BER-3920/

- adds labs flag in preparation for gating UI changes during development
…ng (#30447)

no ref

The editor's data-loss-critical flows had no browser e2e coverage. These two specs pin the current behavior of the Ember editor so it cannot regress silently.
no ref

Adds email-preview and email-retry support to the `admin-x-framework` data layer so the React publish flow can render newsletter previews, send test emails, and retry failed sends.
…ework (#30446)

no ref

Added wiring for email recipient filters and the members count handling for the publish flows.
ref https://linear.app/ghost/issue/HKG-2001

Boot starts the jobs service before the web app is mounted and before any
recurring job is scheduled, so nothing can legitimately enqueue or schedule
before start() - the buffering path (a paused constructor queue resumed on
start) only existed defensively. A pre-start enqueue or recurring schedule
now throws so a boot-ordering regression surfaces immediately, while an
enqueue after shutdown() remains a silently dropped benign race. A recurring
tick that cannot enqueue is caught and logged rather than escaping the timer
callback as an uncaught exception. This also simplifies the ground for
routing job types onto separate queues.
ref https://linear.app/ghost/issue/HKG-2001

Job types with different execution characteristics - slow, untrusted,
flood-prone vs. fast, internal, scheduled - could not be isolated from each
other: every type shared the in-memory backend's single queue, so any
high-volume type could occupy all of the shared workers and starve the rest.
The old Bree-based system isolated such work by running an entire second job
manager; the class-based service had no equivalent.

Handlers can now declare a queue and concurrency together where they are
registered (jobsService.handle(Job, handler, {queue, concurrency})), keeping
execution policy next to the handler and out of both the job classes and the
dispatch sites; a handler that declares nothing runs on the shared default
lane, whose name is reserved. The queue is routing metadata resolved at
dispatch time - delivery always routes by job type, never by queue, so
envelopes stay valid across deploys that move a type between queues.
Declared queues are handed to the backend at start() as desired state: the
in-memory backend runs one fastq lane per queue and enforces concurrency per
process, and a declaration a backend cannot satisfy fails loudly at start()
rather than being silently dropped.
ref https://linear.app/ghost/issue/HKG-2001

Webmention processing fetches external pages and is triggered by
unauthenticated requests, so a flood of webmentions could occupy all of the
in-memory backend's shared workers and starve other background jobs.
Routing it onto its own webmentions queue restores the isolation the old
dedicated mentions job manager provided before the job moved to the
class-based service; the concurrency matches that legacy queue's inline
fastq concurrency of 3.
no ref
- fixes node 24 compatability issues in tryghost/request
no ref

This change should have no user impact. All I did was run this:

```sh
tb fmt ghost/core/core/server/data/tinybird/pipes/mv_hits.pipe
```

(I didn't choose to format other files because, annoyingly, `tb fmt`
removes comments, which I wasn't ready to do.)
… admin (#30462)

no ref

`@tryghost/koenig-lexical` ships two bundles. The UMD build (`koenig-lexical.umd.js`) inlines the editor stylesheet and appends it to `<head>` when it evaluates. The ESM build (`koenig-lexical.js`) extracts it to `dist/style.css` and leaves loading to the consumer, but the package `exports` map only exposed `"."`, so ESM consumers had no supported way to reach the stylesheet.

The React admin imports Koenig through the ESM entry.
no refs

## Context

Many TypeScript workspaces already expose a `test:types` target, but CI
currently selects only affected `test:unit` targets. That leaves
standalone typechecks unprotected and makes their dependency on built
workspace packages implicit. The [recent Admin-X typecheck regression
fixed in #30291](#30291)
demonstrated that these failures can land on `main` while unit-test CI
remains green.

## Summary

- Added a root `pnpm test:types` command and a cached Nx `test:types`
target that builds dependency projects first.
- Added an affected-project `Typecheck` job with its own required CI
signal and main-branch failure notification.
- Included typechecking in the public-app publishing prerequisites while
allowing the job to be skipped when no typed project is affected.
- Removed Shade typechecking from `test:unit` so unit tests and
typechecks are reported independently.
- Documented how to run repository-wide and project-specific typechecks.
- Separated `ghost/core`'s `lint:types` script into `test:types` (for
typechecks) and `lint:code` (for linting .ts files)
no ref

The `editorReact` Labs flag currently serves a placeholder on `/editor/*`. This PR turns it into a real post editor: it reads the post or page, renders it in Koenig with the full post `cardConfig`, and keeps edits in memory. **Nothing is saved yet** - the only write is the mobiledoc-to-lexical conversion a legacy post needs before it can open; saving lands separately with the save engine. With the flag off, Ember serves the editor exactly as before.
@pull pull Bot locked and limited conversation to collaborators Sep 2, 2026
@pull pull Bot added the ⤵️ pull label Sep 2, 2026
@pull
pull Bot merged commit ae04c61 into code:main Sep 2, 2026
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.

Projects

None yet

Development

Successfully merging this pull request may close these issues.

7 participants