Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
191 commits
Select commit Hold shift + click to select a range
a0f9059
Bumped version to 6.57.2-rc.0
github-actions[bot] Aug 10, 2026
c8be291
Fixed docker build to upload core image as artifact in forks (#29873)
acburdine Aug 10, 2026
bd61548
Changed settings confirmations to a context provider (#29818)
9larsons Aug 10, 2026
9a2e092
🐛 Fixed missing email notification when a member import fails
rob-ghost Aug 10, 2026
8d02f63
Added a scoped Shade code editor for tag details (#29804)
9larsons Aug 10, 2026
ae6ed4b
Pinned that the members API neither reads nor writes custom fields
rob-ghost Aug 10, 2026
da98f00
Changed custom field keys to be minted with underscores
rob-ghost Aug 10, 2026
a21010a
Fixed flaky tag code injection acceptance test (#29875)
9larsons Aug 11, 2026
de65389
Update dependency @tanstack/react-query to v5.101.4 (#29587)
tryghost-renovate[bot] Aug 11, 2026
ae3a024
Keyed custom field values by field key instead of field id
rob-ghost Aug 6, 2026
95cff57
Kept the custom_field_id-reading migrations idempotent across the re-key
rob-ghost Aug 6, 2026
7d16a7f
Cleaned up stale routing comments, dead code and two domain-model typ…
vershwal Aug 11, 2026
579fc56
Improved internal package migration safeguards (#29842)
ErisDS Aug 11, 2026
9ceb0fb
Removed the llms.txt labs flag (#29878)
ErisDS Aug 11, 2026
3cf2c24
Removed the Explore labs flag (#29879)
ErisDS Aug 11, 2026
f905464
Updated settings API snapshot (#29883)
ErisDS Aug 11, 2026
2d98407
Changed API snapshots to stop encoding the exact feature flag list (#…
kevinansfield Aug 11, 2026
07d4593
Removed the Featurebase Feedback labs flag (#29882)
kevinansfield Aug 11, 2026
95b89f6
Removed the dangerZoneResetAuth labs flag
rob-ghost Aug 11, 2026
eed181f
Added a publisher-stated order to custom field definitions
rob-ghost Aug 11, 2026
c3721ff
Removed the per-field sort from the member custom field values read
rob-ghost Aug 11, 2026
d53aceb
Added drag-to-reorder to the custom fields settings list
rob-ghost Aug 11, 2026
181cbde
Refactored email analytics Mailgun utility to be a function (#29845)
cmraible Aug 11, 2026
b5293ba
Removed commentsThreads & commentsPinning labs flags (#29885)
kevinansfield Aug 11, 2026
58f014d
TypeScriptified "fetch email analytics Mailgun events" utility (#29846)
EvanHahn Aug 11, 2026
4dc2d62
TypeScriptify email analytics service (#29858)
EvanHahn Aug 11, 2026
08e35e1
Improved types for email analytics job data (#29872)
EvanHahn Aug 11, 2026
2ffde82
Derived the custom-field key cap from the schema in values-service
rob-ghost Aug 11, 2026
560730c
Pinned the custom-fields schema type and status lists to their source
rob-ghost Aug 11, 2026
0e39908
Compile-forced the custom-field presentation parts against the value …
rob-ghost Aug 11, 2026
94c80d0
Removed unused parameter for Mailgun client init (#29889)
EvanHahn Aug 11, 2026
ee005e8
Removed unused lexicalIndicators labs flag
kevinansfield Aug 12, 2026
6e17cad
Removed unused emailCustomization labs flag
kevinansfield Aug 12, 2026
09a0dd7
Improved error reporting when routes.yaml or redirects fail to upload…
vershwal Aug 12, 2026
b16b165
Updated scheduled release time to Tuesday 3pm UTC (#29874)
acburdine Aug 12, 2026
021a6b0
TypeScriptified test assertion helpers (#29911)
EvanHahn Aug 12, 2026
d563c7a
TypeScriptified several frontend helper tests (#29903)
EvanHahn Aug 12, 2026
0d89154
Bumped bookshelf-plugins and removed smarterCounts labs flag (#29910)
kevinansfield Aug 12, 2026
05990c5
Moved custom field type presentation into shared
renatoworks Aug 6, 2026
4f724b5
Tied a custom field type's labels and values to its value schema
renatoworks Aug 6, 2026
399acf3
Read the address editor's parts from the shared catalog
rob-ghost Aug 12, 2026
dd087ab
Tightened what the custom field catalogs claim about themselves
rob-ghost Aug 12, 2026
6cebc38
Stopped a custom field type from the future taking the surface down
rob-ghost Aug 12, 2026
4ca74b0
Excluded GitHub docs from Core CI tests (#29914)
ErisDS Aug 12, 2026
bfb342e
Added foundational codebase documentation (#29913)
ErisDS Aug 12, 2026
3783a38
Replaced nrwl/nx-set-shas action with in-repo script (#29918)
acburdine Aug 12, 2026
ada2639
Removed the customFonts labs flag (#29886)
ErisDS Aug 12, 2026
ecf87f8
Removed unused `totalEvents` value from `EventProcessingResult` (#29926)
EvanHahn Aug 12, 2026
5076979
TypeScriptified email analytics processing result tests (#29927)
EvanHahn Aug 12, 2026
47d8b0e
Disabled compile caching by default (#29928)
acburdine Aug 12, 2026
81a2215
Improved form and container border contrast (#29929)
peterzimon Aug 13, 2026
b91694f
Update AGENTS.md (#29930)
louisghost Aug 13, 2026
41def3e
Added internal package golden path checks (#29877)
ErisDS Aug 13, 2026
f146bfa
Added translation and internationalization guides (#29931)
ErisDS Aug 13, 2026
002deb8
✨ Added support for Docker secrets to config loading (#29924)
acburdine Aug 13, 2026
7481efc
Updated public app development commands (#29932)
ErisDS Aug 13, 2026
a4e84d3
🐛 Fixed the members import mapping step on short screens
renatoworks Aug 5, 2026
0a4c422
🐛 Fixed the label dropdown running off the bottom of the screen
renatoworks Aug 11, 2026
cd1110c
Changed an empty members import mapping target to leave the column out
renatoworks Aug 11, 2026
5c7e28b
🐛 Fixed a members import silently dropping columns a short first row …
rob-ghost Aug 12, 2026
79ab775
🎨 Improved Admin error messages to say what actually went wrong
rob-ghost Aug 13, 2026
b40c2db
Added creating a custom field while mapping a members import
renatoworks Aug 11, 2026
e0521f9
Removed the legacy Explore integration (#29880)
ErisDS Aug 13, 2026
75fc5fc
Added 1-click export mockup behind a labs flag (#29915)
sagzy Aug 13, 2026
484acd1
Added configuration guide (#29937)
ErisDS Aug 13, 2026
c3b5e9b
Fixed pack.mjs script not including license file (#29939)
acburdine Aug 13, 2026
d7a31b2
Updated Translating Ghost documentation links (#29942)
ErisDS Aug 13, 2026
884f4a8
Added error handling guide (#29940)
ErisDS Aug 13, 2026
00464ec
Added agent payments for premium markdown URLs (#29840)
louisghost Aug 13, 2026
cd17f77
Added Ghost development setup for Amp orbs (#29941)
louisghost Aug 13, 2026
f4d4043
Removed duplicate package license files (#29936)
ErisDS Aug 13, 2026
0de24e9
Added authentication guide (#29943)
ErisDS Aug 13, 2026
3d87059
Changed gifts service entry point to TypeScript (#29935)
kevinansfield Aug 13, 2026
ec53d73
✨ Released the React member details screen
rob-ghost Aug 4, 2026
cac9f33
Cleaned up the memberDetailsReact flag and deleted the Ember member s…
rob-ghost Aug 4, 2026
c0b55b1
Fixed flaky integration modal acceptance test (#29946)
acburdine Aug 13, 2026
1a7fc83
Fixed incorrect unit in targetDeliveryWindow JSDoc (#29901)
muratcorlu Aug 13, 2026
9dda616
Updated migration checklist: you don't need to test in SQLite (#29948)
EvanHahn Aug 13, 2026
74eeda5
Tested email analytics database queries (#29890)
EvanHahn Aug 13, 2026
84acdc3
Updated actions/docker digests to latest (#29949)
acburdine Aug 13, 2026
aac8c2a
Added types for database connection object (#29891)
EvanHahn Aug 13, 2026
b23de25
Tested email analytics initializer (#29892)
EvanHahn Aug 13, 2026
c19b9a2
Update CSS preprocessors (#29478)
tryghost-renovate[bot] Aug 13, 2026
47018a3
Fixed renovate config issues (#29947)
acburdine Aug 13, 2026
375981f
Updated nx + other root dev dependencies (#29954)
acburdine Aug 13, 2026
ca8e43e
Update Docker image digests (#29590)
tryghost-renovate[bot] Aug 13, 2026
51aa95d
Updated vitest/types/scripts dependencies (#29956)
acburdine Aug 13, 2026
4f6a758
Removed jest dep from apps/activitypub (#29957)
acburdine Aug 13, 2026
011dd67
Updated more dev dependencies (#29960)
acburdine Aug 13, 2026
05447b2
Cleaned up remaining node-fetch/cross-fetch references (#29961)
acburdine Aug 13, 2026
120bbd1
Initialize email analytics Prometheus metrics in proper spot (#29893)
EvanHahn Aug 13, 2026
58af71d
🐛 Improved reliability of email analytics events (#29895)
EvanHahn Aug 13, 2026
1ad33ce
TypeScriptify email analytics service tests (#29923)
EvanHahn Aug 13, 2026
a415856
Updated email analytics service wrapper to always define sub-service …
EvanHahn Aug 13, 2026
f6d69ce
Migrated Codex documentation into the repo (#29958)
ErisDS Aug 13, 2026
089f7e8
TypeScriptify email analytics service wrapper (#29964)
EvanHahn Aug 13, 2026
3982329
Update dependency @xyflow/react to v12.11.2 (#29591)
tryghost-renovate[bot] Aug 13, 2026
87558c7
Update tryghost/actions digest to 12da067 (#29972)
tryghost-renovate[bot] Aug 14, 2026
d656bf3
Removed unnecessary intl dep
acburdine Aug 13, 2026
65c3621
Added override for consolidate template dependency
acburdine Aug 13, 2026
9851cc4
Added pnpmfile override for consolidate peer deps
acburdine Aug 13, 2026
22bd9ed
Updated docker/pack scripts to prune source ts files
acburdine Aug 13, 2026
d242891
Removed more unnecessary prod dependencies
acburdine Aug 13, 2026
6e146ea
Update dependency @tanstack/react-virtual to v3.14.9 (#29588)
tryghost-renovate[bot] Aug 14, 2026
a74cb09
Update dependency @testing-library/user-event to v14.6.3 (#29974)
tryghost-renovate[bot] Aug 14, 2026
158426e
Update dependency concurrently to v10.0.4 (#29975)
tryghost-renovate[bot] Aug 14, 2026
8c296b5
Update dependency dompurify to v3.4.13 (#29976)
tryghost-renovate[bot] Aug 14, 2026
8a0e088
Update dependency eslint-plugin-react-refresh to v0.5.4 (#29977)
tryghost-renovate[bot] Aug 14, 2026
407e032
Update Types packages (#29589)
tryghost-renovate[bot] Aug 14, 2026
665bda0
Update dependency body-parser to v1.20.6 (#29592)
tryghost-renovate[bot] Aug 14, 2026
c92ae41
Updated acceptance and E2E testing documentation (#29933)
ErisDS Aug 14, 2026
cdee01e
Stopped documentation-only changes from running code tests (#29985)
ErisDS Aug 14, 2026
4fd9412
Documented the koenig package APIs (#29984)
ErisDS Aug 14, 2026
5f649f3
Update dependency preact to v10.29.8 (#29980)
tryghost-renovate[bot] Aug 15, 2026
3a2fae1
Updated public app release documentation (#29986)
ErisDS Aug 15, 2026
9f07bd8
Updated Shade and i18n setup documentation (#29993)
ErisDS Aug 16, 2026
a175b0a
Changed CodeRabbit to prioritise important feedback (#29994)
ErisDS Aug 16, 2026
ead7044
Improved internal package migration safeguards (#29944)
ErisDS Aug 16, 2026
4a1e047
Moved E2E guidance into codebase docs (#29997)
ErisDS Aug 16, 2026
95117e1
Updated commit message guidance (#29995)
ErisDS Aug 16, 2026
04f2328
Added Node.js compatibility reference (#30000)
ErisDS Aug 16, 2026
b45ecaa
Added codebase documentation guidance (#29998)
ErisDS Aug 16, 2026
cbe73b1
🐛 Fixed card assets busting caches on every Ghost restart (#29659)
jloh Aug 17, 2026
db060f2
Added CodeRabbit review rules for TypeScript + Zod type safety
allouis Jul 27, 2026
be3a152
Fixed the boot benchmark failing on a missing card asset manifest (#3…
vershwal Aug 17, 2026
d4611c6
Improved CodeRabbit's Ghost review context (#30003)
ErisDS Aug 17, 2026
c5f2fd9
Added documentation link checks (#29999)
ErisDS Aug 17, 2026
10371a9
Updated Koenig Lexical agent guidance (#30005)
ErisDS Aug 17, 2026
89bde20
Expanded database codebase documentation (#30006)
ErisDS Aug 17, 2026
27e3bd3
Added theme compatibility documentation (#30009)
ErisDS Aug 17, 2026
2168378
Moved root agent guidance into codebase docs (#30007)
ErisDS Aug 17, 2026
5142a2e
Fixed documentation changes running core tests (#30008)
ErisDS Aug 17, 2026
81cb22e
🐛 Stopped automation runs when member unsubscribes (#29959)
EvanHahn Aug 17, 2026
5d50c27
Removed stale ignoreDeps from Renovate config (#30013)
9larsons Aug 17, 2026
aa25788
Renamed Mailgun provider ID database column (#30025)
EvanHahn Aug 17, 2026
66c0bf7
Updated metascraper + jsdom dependencies to latest (#30026)
acburdine Aug 17, 2026
696657a
TypeScriptified data generator "luck" utility (#30028)
EvanHahn Aug 17, 2026
45118e9
🐛 Fixed redirecting outbound requests crashing Ghost (#30027)
9larsons Aug 17, 2026
1637e97
🐛 Fixed periodic host limits in Admin (#29837)
betschki Aug 17, 2026
c0254ce
🎨 Changed the upgrade destination on host limits to be configurable (…
betschki Aug 17, 2026
a422393
TypeScriptify data generator table importer base class (#30029)
EvanHahn Aug 17, 2026
155709f
Updated nql/nql-lang deps to latest (#30032)
acburdine Aug 17, 2026
ac87f8e
Fixed stack overflow error in email batches data seeder (#30022)
EvanHahn Aug 17, 2026
3db4c02
Updated @tryghost/metrics to latest, dedupe framework versions (#30031)
acburdine Aug 17, 2026
dc96d09
Fixed stack overflow error in table importer (#30021)
EvanHahn Aug 17, 2026
fbd2b66
Tightened object lookup in data generator sorter (#30034)
EvanHahn Aug 17, 2026
b658b76
Update dependency parse-email-address to v0.0.4 (#30036)
EvanHahn Aug 17, 2026
4f028d8
Added index to prep for automation run analytics query (#29950)
EvanHahn Aug 17, 2026
accdee7
Update Docker image digests (#29990)
tryghost-renovate[bot] Aug 17, 2026
022293f
Added automations records to data generator (#30033)
EvanHahn Aug 17, 2026
ce08414
Added "last run created at" stat to automation browse endpoint (#29951)
EvanHahn Aug 17, 2026
cd998be
TypeScriptified email service events (#30038)
EvanHahn Aug 17, 2026
a6dd93e
Updated signing keys to use 2048-bit instead of 1024 (#28) (#30043)
acburdine Aug 18, 2026
778060c
Fixed api key permission checks (#30044)
acburdine Aug 18, 2026
5ef16ab
Fixed filter fields for bulk posts/pages requests (#30046)
acburdine Aug 18, 2026
2adf170
Fixed password resets to run in a transaction (#30047)
acburdine Aug 18, 2026
6fda019
Fixed private site comment read middleware (#30045)
acburdine Aug 18, 2026
abed8d0
Moved shared Shade guidance into contributor docs (#30010)
ErisDS Aug 18, 2026
9b1b61d
🎨 Refined the React tag details screen (#29934)
peterzimon Aug 18, 2026
b6ca85f
🐛 Fixed text filters losing values ending in a dollar sign
rob-ghost Aug 17, 2026
4021006
Refined tag detail field alignment (#30049)
peterzimon Aug 18, 2026
4576ebe
Added limits for agent guidance (#30011)
ErisDS Aug 18, 2026
8fb840d
Bumped @tryghost/nql and mongo-knex to the latest patch
rob-ghost Aug 18, 2026
8f81bcc
Added filter segment primitives and a renderer-owned operator to Shade
rob-ghost Aug 11, 2026
5a76b67
Added filtering members by their custom field values
rob-ghost Aug 11, 2026
1245944
Added a custom field filter to the members list
rob-ghost Aug 11, 2026
2d49231
Capped the custom fields filter group behind a Show more
rob-ghost Aug 11, 2026
e4c3c0c
Rejected unsupported clauses in a custom field filter compound
rob-ghost Aug 12, 2026
f3b5062
Named the field in custom field filter accessibility labels
rob-ghost Aug 12, 2026
589d180
Showed archived custom field filters as read-only pills
rob-ghost Aug 12, 2026
d27a852
Added development URL testing guide (#30056)
ErisDS Aug 18, 2026
43c3275
Upload content CSVs to new endpoint from universal importer modal (#3…
PaulAdamDavis Aug 18, 2026
fe7771d
Added API-level tests pinning every member filter operator
rob-ghost Aug 18, 2026
22f52dd
🎨 Improved automation canvas panning so the flow can't be lost off sc…
zach1618 Aug 18, 2026
0c58752
🐛 Fixed .rpm and .deb files being rejected by the file upload card (#…
9larsons Aug 18, 2026
1eaff16
TypeScriptified data generator date utils (#30039)
EvanHahn Aug 18, 2026
7e9d564
🌐 Fixed grammar in Swiss German (de-CH) email translations (#29733)
tbraendle Aug 18, 2026
3879bed
Changed advanced settings dialogs to controlled components (#30014)
9larsons Aug 18, 2026
d35e519
Changed theme settings dialogs to controlled components (#30015)
9larsons Aug 18, 2026
9e65e0b
Added @tryghost/adapter-base-jobs contract package
allouis Aug 17, 2026
7d4c891
Added the in-memory jobs service and backend
allouis Aug 17, 2026
21ee978
Used `const` for variables that are never reassigned (#30066)
EvanHahn Aug 18, 2026
612f102
Removed useless assertions from storage utils tests (#30068)
EvanHahn Aug 18, 2026
1773748
Added "total run count" stat to automation browse endpoint (#30037)
EvanHahn Aug 18, 2026
8299808
🌐 Updated Serbian translations (#29768)
esterkane Aug 18, 2026
ad4d6d8
Removed all `@ts-ignore` usages (#30063)
EvanHahn Aug 18, 2026
291bddb
Forbade `@ts-ignore` (#30064)
EvanHahn Aug 18, 2026
50bc753
Installed dormant Oxfmt (#29955)
EvanHahn Aug 18, 2026
a2c4557
Improved the field picker in the members import mapping (#30062)
renatoworks Aug 18, 2026
6a53bd3
Changed membership settings dialogs to controlled components (#30016)
9larsons Aug 18, 2026
e35ef3e
v6.58.0
github-actions[bot] Aug 18, 2026
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
The table of contents is too big for display.
Diff view
Diff view
  •  
  •  
  •  
2 changes: 2 additions & 0 deletions .agents/bin/docker
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
#!/bin/sh
exec sudo --preserve-env=PUBLIC_URL /usr/bin/docker "$@"
6 changes: 6 additions & 0 deletions .agents/resume
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
#!/usr/bin/env bash
set -euo pipefail

if ! systemctl is-active --quiet amp-svc-docker.service || ! systemctl is-active --quiet amp-svc-ghost.service; then
amp orb services ensure
fi
40 changes: 40 additions & 0 deletions .agents/setup
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
#!/usr/bin/env bash
set -euo pipefail

node_version="$(cat .node-version)"
node_dir="$HOME/.local/node-v${node_version}-linux-x64"

if [[ ! -x "$node_dir/bin/node" ]]; then
archive="/tmp/node-v${node_version}-linux-x64.tar.xz"
curl -fsSLo "$archive" "https://nodejs.org/dist/v${node_version}/node-v${node_version}-linux-x64.tar.xz"
mkdir -p "$HOME/.local"
tar -xJf "$archive" -C "$HOME/.local"
rm -f "$archive"
fi

export PATH="$node_dir/bin:$PATH"
corepack enable --install-directory "$node_dir/bin"
pnpm setup

profile_marker="# Ghost orb Node.js"
profile_export="export PATH=\"$node_dir/bin:\$PATH\""
if grep -Fqx "$profile_marker" "$HOME/.bash_profile" 2>/dev/null; then
sed -i "/^${profile_marker}$/ { n; c\\
$profile_export
}" "$HOME/.bash_profile"
else
cat >> "$HOME/.bash_profile" <<EOF

$profile_marker
$profile_export
EOF
fi

if ! /usr/bin/docker compose version >/dev/null 2>&1; then
sudo install -m 0755 -d /etc/apt/keyrings
sudo curl -fsSL https://download.docker.com/linux/debian/gpg -o /etc/apt/keyrings/docker.asc
sudo chmod a+r /etc/apt/keyrings/docker.asc
echo "deb [arch=$(dpkg --print-architecture) signed-by=/etc/apt/keyrings/docker.asc] https://download.docker.com/linux/debian $(. /etc/os-release && echo "$VERSION_CODENAME") stable" | sudo tee /etc/apt/sources.list.d/docker.list >/dev/null
sudo apt-get update
sudo apt-get install -y docker-ce docker-ce-cli containerd.io docker-buildx-plugin docker-compose-plugin
fi
46 changes: 7 additions & 39 deletions .agents/skills/commit/SKILL.md
Original file line number Diff line number Diff line change
Expand Up @@ -14,48 +14,16 @@ Use this skill whenever the user asks you to create a git commit for the current
- `git diff`
- `git log -5 --oneline`
2. Only stage files relevant to the requested change. Do not include unrelated untracked files, generated files, or likely-local artifacts.
3. Always follow Ghost's commit conventions (see below) for commit messages
4. Run `git status --short` after committing and confirm the result.
3. Read and follow `.github/CONTRIBUTING.md#commit-messages`. It is the
source of truth for Ghost's commit conventions.
4. For publishable packages, check whether a release intent is required. A
package `README.md` is published and requires a release; repository-only
Markdown such as `AGENTS.md`, `CLAUDE.md`, changelogs, and package-local
`docs/` does not.
5. Run `git status --short` after committing and confirm the result.

## Important
- Do not push to remote unless the user explicitly asks
- Keep commits focused and avoid bundling unrelated changes
- If there are no relevant changes, do not create an empty commit
- If hooks fail, fix the issue and create a new commit. Never bypass hooks.

## Commit message format

We have a handful of simple standards for commit messages which help us to generate readable changelogs. Please follow this wherever possible and mention the associated issue number.

- **1st line:** Max 80 character summary
- Written in past tense e.g. “Fixed the thing” not “Fixes the thing”
- Start with one of: Fixed, Changed, Updated, Improved, Added, Removed, Reverted, Moved, Released, Bumped, Cleaned
- **2nd line:** [Always blank]
- **3rd line:** `ref <issue link>`, `fixes <issue link>`, `closes <issue link>` or blank
- **4th line:** Why this change was made - the code includes the what, the commit message should describe the context of why - why this, why now, why not something else?

If your change is **user-facing** please prepend the first line of your commit with **an emoji**.

Because emoji commits are the release notes, it's important that anything that gets an emoji is a user-facing change that's significant and relevant for end-users to see.

The first line of an emoji commit message should be from the perspective of the user. For example, 🐛 Fixed a race condition in the members service is technical and tells the user nothing, but 🐛 Fixed a bug causing active members to lose access to paid content tells the user reading the release notes “oh yeah, they fixed that bug I kept hitting.”

### Main emojis we are using:

- ✨ Feature
- 🎨 Improvement / change
- 🐛 Bug Fix
- 🌐 i18n (translation) submissions
- 💡 Anything else flagged to users or whoever is writing release notes

### Example

```
✨ Added config flag for disabling page analytics

ref https://linear.app/tryghost/issue/ENG-1234/

- analytics are brand new under development, therefore they need to be behind a flag
- not using the developerExperiments flag as that is already in wide use and we aren't ready to deploy this anywhere yet
- using the term `pageAnalytics` as this was discussed as best reflecting what this does
```
Original file line number Diff line number Diff line change
Expand Up @@ -62,6 +62,9 @@ Use the subject `Changed <package> file extensions to TypeScript`.
Apply the package contract from `packages/README.md`: shared config packages,
minimal package-local config, ESM metadata and exports, standard scripts, and a
single compiled output unless a verified consumer requires an exception.
Replace the package's `migration` status with
`ghostPackage.goldenPath: compliant` only after every mechanical golden-path
check passes.

Use the subject `Converted <package> to TypeScript`.

Expand Down
17 changes: 12 additions & 5 deletions .agents/skills/create-database-migration/SKILL.md
Original file line number Diff line number Diff line change
Expand Up @@ -5,20 +5,27 @@ description: Create a database migration to add a table, add columns to an exist

# Create Database Migration

Read the canonical human guidance in
[`docs/practices/database-migrations.md`](../../../docs/practices/database-migrations.md)
before making a migration. This skill is the executable checklist that
accompanies it.

## Instructions

1. Create a new, empty migration file: `cd ghost/core && pnpm migrate:create <kebab-case-slug>`. IMPORTANT: do not create the migration file manually; always use this script to create the initial empty migration file. The slug must be kebab-case (e.g. `add-column-to-posts`).
2. The above command will create a new directory in `ghost/core/core/server/data/migrations/versions` if needed, create the empty migration file with the appropriate name, and bump the core and admin package versions to RC if this is the first migration after a release.
3. Update the migration file with the changes you want to make in the database, following the existing patterns in the codebase. Where appropriate, prefer to use the utility functions in `ghost/core/core/server/data/migrations/utils/*`.
4. Update the schema definition file in `ghost/core/core/server/data/schema/schema.js`, and make sure it aligns with the latest changes from the migration.
5. Test the migration manually: `cd ghost/core && pnpm knex-migrator migrate --v {version directory} --force`
6. If adding or dropping a table, update `ghost/core/core/server/data/exporter/table-lists.js` as appropriate.
7. If adding or dropping a table, also add or remove the table name from the expected tables list in `ghost/core/test/integration/exporter/exporter.test.js`. This test has a hardcoded alphabetically-sorted array of all database tables — it runs in CI integration tests (not unit tests) and will fail if the new table is missing.
8. Run the schema integrity test, and update the hash: `cd ghost/core && pnpm test:single test/unit/server/data/schema/integrity.test.js`
9. Run unit tests in Ghost core, and iterate until they pass: `cd ghost/core && pnpm test:unit`
6. Roll the migration back to test `down()`: `cd ghost/core && pnpm knex-migrator rollback --v {previous version} --force`, then migrate forward again.
7. Run the migration integration test, which covers initialization, rollback, forward migration, and idempotency: `cd ghost/core && pnpm test:single test/integration/migrations/migration.test.js`. Migrations must pass the database-backed suites against both MySQL and SQLite.
8. If adding or dropping a table, update `ghost/core/core/server/data/exporter/table-lists.js` as appropriate. The consistency assertion in `ghost/core/test/unit/server/data/exporter/index.test.js` checks that every schema table is classified in the exporter lists.
9. Run the focused exporter unit test when the table lists change: `cd ghost/core && pnpm test:single test/unit/server/data/exporter/index.test.js`.
10. Run the schema integrity test, and update the hash: `cd ghost/core && pnpm test:single test/unit/server/data/schema/integrity.test.js`
11. Run unit tests in Ghost core, and iterate until they pass: `cd ghost/core && pnpm test:unit`

## Examples
See [examples.md](examples.md) for example migrations.

## Rules
See [rules.md](rules.md) for rules that should always be followed when creating database migrations.
See [rules.md](rules.md) for rules that should always be followed when creating database migrations.
72 changes: 72 additions & 0 deletions .agents/skills/migrate-internal-package/SKILL.md
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,28 @@ Explain every cross-repository or administrative action before it happens.
PR, deprecating npm versions, or force-pushing.
- Never ask for, display, or store credentials or OTPs.

## Work in isolated checkouts

Never manipulate history in a checkout containing unrelated work. Fetch both
repositories, confirm the source checkout is clean, and create a dedicated
Ghost worktree from the freshly fetched `origin/main`.

Run history-changing commands individually or in a fail-fast shell. A failed
`git worktree add` must not be followed by `git subtree add` in whichever
checkout happens to be current. Before choosing a branch or path, list existing
worktrees and matching local and remote branches. Use a migration-specific slug,
for example `codex/import-<package>-from-<source>`, rather than a generic name.

If the destination branch or path already exists, stop and inspect its
cleanliness, base, divergence, source split and attached worktree. Do not mutate,
delete or silently reuse it. Present the evidence and ask the user whether to
resume, preserve and supersede, or remove it when more than one choice is
reasonable. A previous attempt can contain valid unmerged history even when its
remote branch is gone.

Before importing, record and compare the destination `HEAD` and `origin/main`;
they must match. Recheck the first parent immediately after the subtree commit.

## Confirm this workflow applies

Before changing either repository, record the source repository, its default
Expand All @@ -36,6 +58,22 @@ Publication or download counts alone do not prove that a package must remain a
supported public API. If supported external consumers still need new releases,
stop: this internal-only workflow is the wrong publishing model.

For the public-consumer audit, check npm metadata, first-party repositories and
documentation, plus GitHub code search for package dependencies and runtime
imports. Classify results as first-party consumers, independent integrations,
Ghost forks, or deployed Ghost installation snapshots. Forks and installation
snapshots show historical presence only. Require a recent update, current
deployment, active dependency or another freshness signal before treating them
as evidence of continued installation needs; even then, they do not establish
an independently supported API. Record the evidence and confidence behind the
ownership decision; stop and ask if current support expectations remain
unclear.

Run registry-only `npm view` commands from a neutral temporary directory. A
repository's `devEngines` policy can reject the host Node version before npm
contacts the registry, which is unrelated to the package metadata audit. Record
that failure separately if using a neutral directory does not resolve it.

## Produce these work products in order

1. A green Ghost import PR with reachable source history.
Expand Down Expand Up @@ -63,14 +101,48 @@ commits manually.
After the subtree commit, add focused integration commits that:

- make the package private with an internal placeholder version;
- set `ghostPackage.goldenPath` to `migration` and `ghostPackage.reason` to a
concise explanation of the remaining modernization work;
- switch Ghost consumers to `workspace:*`;
- update the lockfile with `pnpm`;
- minimally adapt configuration and tests to work in Ghost;
- retain runtime behavior for the later modernization PR.

Before editing package metadata, map each source `workspace:*` dependency to
its destination state:

- use `workspace:*` when the dependency already exists in Ghost;
- use `catalog:` when Ghost's catalog version satisfies the imported package;
- add a named migration catalog with the exact published version when changing
the shared catalog would broaden the migration or alter runtime behavior;
- stop if a required dependency is unpublished or exists only in the source
workspace.

Document temporary named-catalog entries for the modernization follow-up. Never
inline dependency versions; Ghost's strict catalog policy still applies. Do not
import additional packages implicitly.

If the package is legacy JavaScript or CommonJS, read
[`references/legacy-integration.md`](references/legacy-integration.md)
completely before creating integration commits.

Verify that the subtree commit has two parents and that representative file
history crosses into the source repository before opening the PR.

Before opening the PR, also verify the source split is reachable from the branch
tip, the consumer resolves the workspace package through its production import
path, package lint and tests pass through Nx, relevant consumer tests pass, the
full build passes, and the Ghost archive contains the internal package. Record
the exact commit IDs and commands in the handoff.

For a pilot or first use, include a structured gap report in the handoff:

- `Observed`: the exact failure or ambiguity and the command/state that exposed it;
- `Worked around`: the safe action taken, without hiding the original gap;
- `Skill change`: the concrete instruction, preflight or script improvement;
- `Tooling change`: anything that cannot be solved within this repository;
- `Confidence`: high, medium or low, with unresolved evidence called out.

## 2. Merge the import without rewriting history

This is the exceptional PR. It must use GitHub's **Create a merge commit**
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,32 +4,83 @@ Read this reference before creating the Ghost import PR.

## Create package-only source history

Work from an up-to-date clone of the source repository. Use its actual default
Work from an up-to-date, clean, full clone of the source repository. Shallow and
partial clones can complete `git subtree split` while still failing later when
Ghost fetches the split, because the local source cannot serve promised objects.
Reject them before splitting:

```bash
set -euo pipefail

test "$(git rev-parse --is-shallow-repository)" = "false"
test -z "$(git config --local --get extensions.partialClone || true)"
```

If either check fails, create a fresh full clone without `--depth`,
`--filter` or sparse/partial clone options. Use the source's actual default
branch and a temporary branch name that cannot be confused with a product
branch. For example, when the default branch is `main`:
branch.

`git subtree split` may inspect thousands of commits and run for several
minutes. Use `--quiet` in agent or CI-style runners so its progress stream does
not flood or interrupt the runner. Run it on its own, keep the process alive
until it returns an exit status, and do not mistake an output timeout for
completion. For example, when the default branch is `main`:

```bash
set -euo pipefail

git fetch origin
git switch --detach origin/main
test -z "$(git status --porcelain)"
git subtree split \
--quiet \
--prefix=<source-package-path> \
-b migrate-<package>-history
-b migrate-<package>-history \
origin/main
```

Record the split tip and inspect the resulting package-only history:
Confirm the branch was actually created, then record the split tip and inspect
the resulting package-only history:

```bash
git rev-parse migrate-<package>-history
git log --oneline --reverse migrate-<package>-history
git show-ref --verify refs/heads/migrate-<package>-history
source_split_tip=$(git rev-parse migrate-<package>-history)
git log --oneline --reverse "$source_split_tip"
```

`git subtree split` retains the relevant commit authorship and chronology while
excluding unrelated source-repository paths.

## Attach the history to Ghost

From a branch based on freshly fetched Ghost `origin/main`, fetch the local
source split and add it without `--squash`:
Before creating the worktree, inspect collisions rather than discovering them
halfway through the import:

```bash
git fetch --prune origin
git worktree list --porcelain
git branch --list 'codex/import-<package>*'
git branch --remotes --list 'origin/codex/import-<package>*'
```

If a match exists, record its worktree, cleanliness, base/divergence, imported
split and remote state. Do not delete or overwrite it without an explicit user
decision. Otherwise create or enter a dedicated Ghost worktree, then verify its
branch, cleanliness, base and empty destination before attaching history:

```bash
set -euo pipefail

test "$(git rev-parse --git-dir)" != "$(git rev-parse --git-common-dir)"
test "$(git branch --show-current)" = "codex/import-<package>-from-<source>"
test -z "$(git status --porcelain)"
test "$(git rev-parse HEAD)" = "$(git rev-parse origin/main)"
test ! -e "packages/<ghost-directory>"
```

Fetch the local source split and add it without `--squash`. Run these as
separate checked operations and stop immediately if either fails:

```bash
git fetch /absolute/path/to/source-repository migrate-<package>-history
Expand All @@ -49,13 +100,40 @@ git-subtree-split: <source-split-tip>
Verify the topology before adding integration commits:

```bash
git show --no-patch --format='%H%nparents: %P%n%B' HEAD
set -euo pipefail

source_split_tip="<recorded-source-split-tip>"
subtree_commit=$(git rev-parse HEAD)
ghost_parent=$(git rev-parse HEAD^1)
imported_parent=$(git rev-parse HEAD^2)
source_path="path/to/representative-file"
destination_path="packages/<ghost-directory>/$source_path"

test "$ghost_parent" = "$(git rev-parse origin/main)"
test "$imported_parent" = "$source_split_tip"
git merge-base --is-ancestor "$source_split_tip" HEAD

source_history=$(git log --full-history --format=%H "$source_split_tip" -- "$source_path")
destination_history=$(git log --full-history --format=%H -- "$destination_path")
test -n "$source_history"
test -n "$destination_history"
test "$(git rev-parse "$source_split_tip:$source_path")" = \
"$(git rev-parse "$subtree_commit:$destination_path")"

git show --no-patch --format='%H%nparents: %P%n%B' "$subtree_commit"
git log --graph --oneline --decorate --all --max-count=40
git log --follow -- packages/<ghost-directory>/path/to/representative-file
git log --full-history --oneline -- "$destination_path"
git log --full-history --oneline "$source_split_tip" -- "$source_path"
```

The subtree commit must have two parents, and its second parent must equal the
recorded split tip.
The subtree commit must have two parents: its first parent must equal the
recorded Ghost base and its second parent must equal the recorded split tip.
Checking the prefixed destination path with `--full-history` and the unprefixed
split path separately is more reliable around merge boundaries than relying on
ordinary path history or `--follow`, either of which may show only the subtree
merge. Requiring non-empty histories and equal blob IDs makes a missing or
mismatched representative file stop the import before integration edits obscure
the source state.

The Admin API schema migration from TryGhost/SDK is a known-good example:

Expand Down
Loading