Skip to content

chore: bump astro to 7.2.9 - #1102

Merged
max-sixty merged 2 commits into
mainfrom
chore/bump-astro-7
Aug 30, 2026
Merged

max-sixty merged 2 commits into
mainfrom
chore/bump-astro-7

Conversation

@tend-agent

@tend-agent tend-agent commented Aug 30, 2026 •

Copy link
Copy Markdown
Collaborator

Moves the site from Astro 5 to Astro 7 (^5.14.0 → ^7.2.9), the last pin in the weekly sweep still behind. Two majors, so this is checked against the built output rather than just a green build: the rendered page is semantically unchanged, and the one behaviour change that does reach the output — Astro 7 makes compressHTML: 'jsx' the default, which strips whitespace between elements — lands only in places where flex or grid already ignores it.

publish-site.yaml builds on PRs precisely for this, so CI is the second check.

The one Astro 7 major that doesn't land on the built site is astro dev backgrounding itself under an agent environment, which would break the wt worktree dev-server tether. A second commit sets ASTRO_DEV_BACKGROUND on that post-start step to keep the foreground behaviour; details in the review thread.

How the output was compared

Both versions were built from the same source and the dist/ trees diffed.

Structure and text. With scope hashes and asset hashes normalised, the two index.html files are identical in tag sequence and attributes, and all 123 text nodes are byte-identical. The <pre> block carrying the install commands is byte-identical too, newlines included.

The whitespace change. Astro 7 removes inter-element whitespace at 239 of 286 tag gaps. Most sit between block elements, where it never rendered. Six kinds of gap have an inline-capable element on both sides, and each is inside a container that does not render whitespace between its children:

Gap Container Why it can't show
</svg> <span>tend</span> (wordmark) .site-header .wordmark display:inline-flex; gap:.55rem — flex ignores it, the gap supplies the space
</a> <a> ×2 (footer links) .site-footer nav display:flex; gap:1.4rem
</span> <span>, </span> <a>, </a> <time> ×36 (activity placeholder rows) .activity-rows / .activity-row display:grid with grid-template-columns:subgrid

Same-line whitespace is preserved as the changelog says: tended by hand · <a>MIT</a> still carries its space.

CSS. Identical selector set. Every declaration difference is a Lightning CSS normalisation with the same computed result — transparent → #0000 and background:0 0, animation:X 3s ease → animation:3s ease X, .05s → 50ms, grid-column:1 / -1 → 1/-1, column-gap:0;row-gap:.5rem → gap:.5rem 0, transition:opacity .25s ease → .25s (ease is the initial value).

JS. Bundles are re-minified by a different engine, so they don't diff usefully. Every string the runtime depends on is present in the same number of modules in both builds: api.tend-src.com, /activity, /currently-tending, recent-activity, currently-tending, stats, data-stat-strip, activity-row. Not verified: the bundles were not executed in a browser — there's no headless browser in this environment.

Astro 7 breaking changes, against what this site uses: @astrojs/db removal, astro:transitions deprecated-API removal, experimental.rustCompiler/queuedRendering/cache/routeRules flag removals, and the Sätteri Markdown processor all touch features the site doesn't use — it is one static .astro page with client scripts. Vite 8 and the Rust compiler are internal.

Node. Astro 7 requires node >=22.12.0. publish-site.yaml uses node-version: '22', which resolves to the newest 22.x (22.23.2 on this runner), so it is satisfied — worth knowing if that pin is ever narrowed.

Two majors. Rendered output compared build-to-build: identical structure and
all 123 text nodes. Astro 7's compressHTML:'jsx' default strips inter-element
whitespace, but every inline-adjacent case sits in a flex or grid container
that already ignored it.

@tend-agent tend-agent left a comment

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

One gap in the breaking-change audit — the Astro 7 major that lands on this repo's dev workflow rather than on the built site.

astro dev daemonizes itself when it detects an agent. Astro 7 added background dev-server management (withastro/astro#16610): when am-i-vibing reports an agent environment, astro dev spawns a detached child and returns. am-i-vibing is now a direct astro dependency, and it types a bare CLAUDECODE env var as claude-code / agent — so any worktree whose post-start hook runs from inside a Claude Code session takes that path.

That collides with the server step in .config/wt.toml — wt step tether -C site -- npm run dev -- --port {{ branch | hash_port }} — whose comment says "Tether owns the whole Astro process group until the worktree is removed". In astro@7.2.9 the spawn is detached: true plus child.unref() (dist/cli/server.js, in background()), so the server gets its own process group and the tethered npm run dev exits as soon as it has forked. Teardown on worktree removal no longer reaches the real server, which keeps holding the branch's hashed port; a later worktree on that branch then binds the next free port while wt list's URL column still points at the orphan serving the old tree. Restarts are at least benign — background() sees the existing .astro/dev.json lock and returns 0 rather than erroring.

Two doc claims go stale with it: the child redirects stdout/stderr to .astro/dev.log, so wt config state logs (site/README.md) and "logs land in .git/wt/logs/" (root CLAUDE.md) stop describing where dev-server output goes.

The opt-out is one env var on that step — the gate is !process.env.ASTRO_DEV_BACKGROUND && isRunByAgent(), so ASTRO_DEV_BACKGROUND=0 (or any non-empty value) restores the foreground behaviour the tether expects. npm run preview has the identical shape under ASTRO_PREVIEW_BACKGROUND, and site/README.md documents that script too.

I read this off the shipped astro@7.2.9 and am-i-vibing@0.4.0 rather than running it in a worktree, so the trigger condition — whether CLAUDECODE actually reaches the hook in practice — is the part worth confirming. Happy to push the wt.toml line here if you want it folded into this PR.

What else I checked, all clean
  • Lockfile portability. All nine @astrojs/compiler-binding-* platform packages are in the lockfile as optional deps, so npm ci resolves on darwin/linux/win and both libcs — not just the platform that regenerated it. No orphaned astro-5-era entries, single node_modules/astro, root dependencies in sync with package.json.
  • astro/tsconfigs/strict still ships in 7.2.9 (fetched it). Worth checking explicitly because nothing in CI typechecks the site — astro build doesn't run astro check — so a removed preset would have failed silently in editors only.
  • Config keys. site, base, server.host all still validate; the green build-site job on this PR is the evidence, since Astro rejects unknown config.
  • Remaining 7.0.0 majors. @astrojs/db removal, astro:transitions deprecated-API removal, the experimental.rustCompiler/queuedRendering/cache/routeRules flag removals, Sätteri as the Markdown processor, and advanced routing's src/fetch.ts entrypoint all miss a single static page with no .md content and no on-demand rendering. The Rust compiler's stricter syntax handling is covered by the same green build.
  • Version. 7.2.9 is npm's current latest; a caret range on a major bump matches how the other pins in the weekly sweep are written.

Astro 7 re-spawns `astro dev` detached when it detects an agent environment
(a bare `CLAUDECODE` satisfies `am-i-vibing`), which takes the real server
out of the tether's process group and redirects its output to
`site/.astro/dev.log`. Set `ASTRO_DEV_BACKGROUND` on the post-start step to
keep the pre-7 foreground behaviour the tether and the log docs assume.
@tend-agent

Copy link
Copy Markdown
Collaborator Author

Confirmed against the shipped packages and folded the fix in as 4d28a6a — the tether step now runs env ASTRO_DEV_BACKGROUND=0 npm run dev, with the reasoning in a comment above it. Checks are green on that head. Both doc claims stay accurate as a result: foreground is restored, so wt config state logs and .git/wt/logs/ still describe where dev-server output goes, and neither site/README.md nor CLAUDE.md needs an edit.

Still unconfirmed, as you flagged: I read this off the packages rather than running a worktree, so whether CLAUDECODE actually reaches the post-start hook in practice is the part to check locally. The fix is inert if it doesn't.

One thing the audit didn't reach — Astro 7 also adds a dev-server lockfile at site/.astro/dev.json, and that file is per-process state that wt step copy-ignored will carry between worktrees. .astro/ is gitignored (site/.gitignore) and the exclude list holds only .venv/, so a new worktree inherits the primary's lockfile. If the primary has a live dev server, checkExistingServer finds that pid alive with an astro command line and the foreground path throws Another astro dev server is already running — the new worktree gets no server at all. Stale and recycled pids are handled (isLockFileProcessAlive checks the command line too), so this only bites while the primary's server is up.

I didn't push an exclude for it: wt isn't installed on the runner, so I can't check whether [step.copy-ignored].exclude matches nested paths by gitignore semantics (.astro/) or by prefix from the repo root (site/.astro/) — and a pattern that silently matches nothing is worse than no line. If the primary worktree does run a server, one entry beside .venv/ is the fix.

Where this is in the code

Read from npm pack astro@7.2.9 and am-i-vibing@0.4.0:

  • dist/cli/dev/index.js — const agentDetected = !process.env.ASTRO_DEV_BACKGROUND && isRunByAgent(), then wantsBackground = !!flags.background || agentDetected. Presence-tested, so any non-empty value disables it; 0 reads as "background off" at the call site.
  • dist/cli/agent.js — isRunByAgent() is detectAgenticEnvironment().type === "agent", and am-i-vibing's dist/detector-*.mjs lists { id: "claude-code", type: "agent", envVars: ["CLAUDECODE"] } — a bare presence check, no value match.
  • dist/cli/server.js, background() — spawn(process.execPath, [astroBin, ...args], { detached: true, stdio: ["ignore", logFd, logFd] }) then child.unref(), with logFd on .astro/dev.log. That's both halves of the problem: own process group, and output away from the tether.
  • dist/core/dev/lockfile.js — getLockFileURL is .astro/dev.json relative to the astro root, i.e. site/; checkExistingServer removes it only when isLockFileProcessAlive says no.

Residual cosmetic effect of the fix: the foreground server writes background: !!process.env.ASTRO_DEV_BACKGROUND into its own lockfile, so astro dev status will label a tethered server "background". Nothing reads that field but the status string.

npm run preview has the identical shape under ASTRO_PREVIEW_BACKGROUND, but nothing automated runs it — left alone.

@max-sixty
max-sixty merged commit a6ca0ce into main Aug 30, 2026
9 checks passed
@max-sixty
max-sixty deleted the chore/bump-astro-7 branch August 30, 2026 18:29
max-sixty pushed a commit that referenced this pull request Aug 30, 2026
Moves the site from Astro 5 to Astro 7 (`^5.14.0` → `^7.2.9`), the last
pin in the weekly sweep still behind. Two majors, so this is checked
against the built output rather than just a green build: the rendered
page is semantically unchanged, and the one behaviour change that does
reach the output — Astro 7 makes `compressHTML: 'jsx'` the default,
which strips whitespace between elements — lands only in places where
flex or grid already ignores it.

`publish-site.yaml` builds on PRs precisely for this, so CI is the
second check.

The one Astro 7 major that doesn't land on the built site is `astro dev`
backgrounding itself under an agent environment, which would break the
`wt` worktree dev-server tether. A second commit sets
`ASTRO_DEV_BACKGROUND` on that post-start step to keep the foreground
behaviour; details in the review thread.

<details><summary>How the output was compared</summary>

Both versions were built from the same source and the `dist/` trees
diffed.

**Structure and text.** With scope hashes and asset hashes normalised,
the two `index.html` files are identical in tag sequence and attributes,
and all **123 text nodes are byte-identical**. The `<pre>` block
carrying the install commands is byte-identical too, newlines included.

**The whitespace change.** Astro 7 removes inter-element whitespace at
239 of 286 tag gaps. Most sit between block elements, where it never
rendered. Six kinds of gap have an inline-capable element on both sides,
and each is inside a container that does not render whitespace between
its children:

| Gap | Container | Why it can't show |
|---|---|---|
| `</svg> <span>tend</span>` (wordmark) | `.site-header .wordmark` |
`display:inline-flex; gap:.55rem` — flex ignores it, the gap supplies
the space |
| `</a> <a>` ×2 (footer links) | `.site-footer nav` | `display:flex;
gap:1.4rem` |
| `</span> <span>`, `</span> <a>`, `</a> <time>` ×36 (activity
placeholder rows) | `.activity-rows` / `.activity-row` | `display:grid`
with `grid-template-columns:subgrid` |

Same-line whitespace is preserved as the changelog says: `tended by hand
· <a>MIT</a>` still carries its space.

**CSS.** Identical selector set. Every declaration difference is a
Lightning CSS normalisation with the same computed result —
`transparent` → `#0000` and `background:0 0`, `animation:X 3s ease` →
`animation:3s ease X`, `.05s` → `50ms`, `grid-column:1 / -1` → `1/-1`,
`column-gap:0;row-gap:.5rem` → `gap:.5rem 0`, `transition:opacity .25s
ease` → `.25s` (`ease` is the initial value).

**JS.** Bundles are re-minified by a different engine, so they don't
diff usefully. Every string the runtime depends on is present in the
same number of modules in both builds: `api.tend-src.com`, `/activity`,
`/currently-tending`, `recent-activity`, `currently-tending`, `stats`,
`data-stat-strip`, `activity-row`. Not verified: the bundles were not
executed in a browser — there's no headless browser in this environment.

**Astro 7 breaking changes, against what this site uses:** `@astrojs/db`
removal, `astro:transitions` deprecated-API removal,
`experimental.rustCompiler`/`queuedRendering`/`cache`/`routeRules` flag
removals, and the Sätteri Markdown processor all touch features the site
doesn't use — it is one static `.astro` page with client scripts. Vite 8
and the Rust compiler are internal.

**Node.** Astro 7 requires `node >=22.12.0`. `publish-site.yaml` uses
`node-version: '22'`, which resolves to the newest 22.x (22.23.2 on this
runner), so it is satisfied — worth knowing if that pin is ever
narrowed.

</details>

---------

Co-authored-by: tend-agent <270458913+tend-agent@users.noreply.github.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants