Skip to content

[pull] main from TryGhost:main - #1467

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

[pull] main from TryGhost:main#1467
pull[bot] merged 8 commits into
code:mainfrom
TryGhost:main

Conversation

@pull

@pull pull Bot commented Sep 3, 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 : )

peterzimon and others added 8 commits September 3, 2026 06:42
ref https://linear.app/ghost/issue/DES-1509

## Why

The display-oriented `dlig` and `cv05` Inter features introduced with
Admin 7 page chrome look noisy in compact labels and controls. They
should remain part of the editorial heading treatment without changing
the rest of the Milestone 1 typography rollout.

## What changed

- keep `zero` and `ss01` across eligible Admin 7 typography
- apply `dlig` and `cv05` only to semantic `h1`–`h6` headings
- preserve the existing Admin 7 flag and portal boundaries for Admin and
Settings
- add computed-style acceptance coverage for both Admin and Settings
ref https://linear.app/ghost/issue/HKG-1979

- The outbound webmention job was an inline closure on the legacy mentions JobManager - live objects that can never leave the process. Moving it behind the class-based interface as a serialisable message is the precondition for the durable backend, and retires that queue's last tenant ahead of the legacy job system removal.
- dispatches an event-time snapshot (source URL, html, previous html) instead of a closure, so the job survives serialisation and a future replay recomputes the same bounded target set
- registers on the routed webmentions lane at the legacy queue's concurrency, because publish-triggered external HTTP is slow and flood-prone - the isolation the old second job manager existed for must survive the migration
- makes the jobs service instance process-lifetime: services capture it behind their init guards, so per-boot instances stranded those references on dead queues under the test harness's in-process restarts, silently dropping dispatches - a latent problem for the inbound job too
- rewrites the outbound e2e waits to track job processing rather than queue state, so the tests assume nothing about the in-memory backend and keep working when a durable one arrives
- leaves production behavior unchanged beyond the platform deltas every migrated job shares: JobsService lifecycle logging, Sentry capture for send failures, and a bounded shutdown drain
ref https://linear.app/ghost/issue/BER-3794/members-manage-their-own-custom-fields-in-their-account

Several endpoints on the members API exist only to serve the member making the
request, and each works out who that is for itself. Nothing said what they should
answer when nobody is signed in, so each arrived at its own answer: reading the
member gives an empty response, while changing one or clearing a suppression give
a bad-request whose reason names an internal cookie. Recorded before any of it
moves, so that changing an answer is a decision this file states rather than
something noticed afterwards.
ref https://linear.app/ghost/issue/BER-3794/members-manage-their-own-custom-fields-in-their-account

Every endpoint that exists to serve the member making the request worked out who
that was for itself, so each also decided for itself what to do when nobody was
there, and they decided differently: reading a member answered with nothing, while
changing one answered that a cookie was missing, under a status saying the request
was malformed rather than that nobody was signed in. One endpoint forgot to decide
at all. A route now says which it is by the gate it sits behind, and that gate
establishes an identity rather than loading a member, because what to load is
something only the endpoint knows: most of them need to act on a member rather
than describe one, and a member read before a write is stale by the time it is
answered anyway. Reading and changing a member's own record are endpoints now, so
the projection of a member is reached the way every other one is, by the
endpoint's name, and errors are answered the way the rest of this API already
answers them. What a member may change about themselves moved out of the request
handler with them, because which fields are writable is a fact about members
rather than a question about HTTP. Nothing a member receives changes, and the
snapshots recording it are unchanged.
ref https://linear.app/ghost/issue/BER-3794/members-manage-their-own-custom-fields-in-their-account

Which of the extra fields a publisher defines about their members may be seen, and
by whom, is about to stop being the same answer everywhere: staff and the member
themselves will be told different things about the same field. Nothing in the
domain could express that, because nothing recorded which side of Ghost was
asking. The read carried a boolean instead, meaning only whether to bother
fetching at all, which is a question about cost rather than about access. This
replaces it with the audience making the request, so the decision has somewhere to
live before there is a rule to put there. Every answer it gives today is the one
Ghost already gives. The argument is required rather than defaulted, which is what
makes a new caller state which door it came through instead of inheriting whatever
the last author assumed, and adding it turned the one existing caller that had
never said so, the value collected at checkout, into a compile error until it did.
ref https://linear.app/ghost/issue/BER-3794/members-manage-their-own-custom-fields-in-their-account

The extra fields a publisher defines about their members could only be changed by
staff, so the person who knows the right answer was the one person who could not
give it, and the data went stale as people moved and changed jobs. A member's own
account now carries what they hold in those fields and accepts a change back,
alongside their name, because their account panel is one form with one Save and
splitting the write would leave a half-succeeded state to explain. What there is
to fill in is a separate request, because it describes the site rather than the
member and changes on a different schedule. Values are resolved before the member
is touched, so a field nobody has defined costs the whole request rather than
leaving a member renamed and their answers rejected; the write it sits beside
reconciles subscriptions with Stripe and sends events, and no rollback would undo
those. A site that has defined no fields is unaffected: the key is absent, and the
snapshots pinning what a member receives are unchanged.
ref
https://linear.app/ghost/issue/DES-1280/auto-focus-newly-added-filter-fields-in-shade-filter

> **Scope: Shade only.** This fixes the component. Admin needs a
separate change (DES-1496) before the behaviour is visible on Members or
Comments — see Testing.

### Why

Adding a filter you type into — Name, Email, a number — left the caret
behind, so you had to click into the empty box you'd just asked for.
Filters that open a picker never had this problem, which made it read as
an inconsistency rather than a missing feature. Reported by Peter.

### What it does

The value input takes focus when its filter was just added. One file
plus tests, no consumer changes.

Excluded on purpose: dates and ranges arrive already carrying a value
rather than waiting on input, so taking the caret would interrupt;
pickers already focus their own menu; booleans have nothing to type
into.

### Testing

**This won't visibly fix Members or Comments yet.** Those pages render
two `<Filters>` and swap between them when the first filter is added,
which destroys the component mid-action. Separate Admin bug — DES-1496.

To see it working: Storybook → `Patterns / Filters`, or add a *second*
filter in Admin, which already works.

### Tests

Three added — text and number take focus, date is left alone. The
existing 20 pass.

---

- [x] I've read and followed the [Contributor
Guide](https://github.com/TryGhost/Ghost/blob/main/.github/CONTRIBUTING.md)
- [x] I've explained my change
- [x] I've written an automated test to prove my change works
@pull pull Bot locked and limited conversation to collaborators Sep 3, 2026
@pull pull Bot added the ⤵️ pull label Sep 3, 2026
@pull
pull Bot merged commit 8477639 into code:main Sep 3, 2026
2 checks passed
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.

5 participants