AstroCraft — home
AstroCraft
Buy a licence
All documentation

Review and publish

The review queue spanning every collection and page, the build gate that runs before any push, and the live check that reads your site's commit stamp.

Last updated

/admin/review/ is workspace-wide, and deliberately so. Publish sends a branch, so every fact on this screen — the ahead count, the unpushed commits, the button’s own number — is repo-wide and always was.

The queue

A real diff against what the remote has, joined to your collections. Each card is a change, and a change is either an entry or a page — the queue has spanned both since pages became composable.

Three standings:

Standing Means
Uncommitted changed on disk, not committed
Draft committed, on the staging branch, not yet published
Live on the publish branch

An entry card with a draftField carries a visible/hidden switch. Flipping it is an ordinary content save — editor-allowed, conflict-checked against that change’s own digest — not a special verb.

A page’s props are diffed only when its section list has not moved. Otherwise a reorder would read as six separate edits.

The Approve and Request-changes buttons were deleted rather than wired. Push sends the whole branch, so per-entry approval had nothing to act on. Pretending otherwise would have been a control that did nothing.

The rail

Branch — where Publish pushes. Admins get a combo box; editors get a readout. Typing a name nothing lists yet is legal: the name is validated by git check-ref-format rather than by a regex, stored immediately, and created on the remote by your next publish. The list’s last row is a New branch action for exactly that.

Unpushed commits — read straight from git, because git keeps no push log of its own.

Preview your changes — hidden until you have set a staging preview URL in Settings, because a branch deploy’s URL is platform-specific and only you know it.

What Publish does

Admin-only, and it takes no input. In order:

  1. Reads the branch from the settings store — never from admin.config.ts, which is only the default.
  2. Pins a commit. The sha is read once and both gates examine that same sha. A save that lands mid-publish rides the next one.
  3. Validates every outgoing entry against your Zod schema, reading the bytes off HEAD. A malformed entry blocks the push and is named per field. An entry marked draft: true still blocks — it rides the branch even though nothing renders it.
  4. Checks the licence. See Licence and activation.
  5. Runs your build command, killed at your configured timeout. This is the gate: a failing build refuses the publish and hands back a bounded tail of its output. The build writes to a throwaway directory, so it never overwrites the dist/ the container is serving.
  6. Pushes <sha>:refs/heads/<branch>.
  7. Polls your production site until its <meta name="build-rev"> stamp is a descendant of what was pushed.

Nothing is ever pushed that did not build.

The three worded states

Never a spinner:

  • publishing…
  • Your changes are live ✓
  • this is taking longer than usual

The stamp is read server-side, because your site is usually another origin and CORS bars a browser from reading it. The comparison is ancestry rather than equality, so a commit landing on top of yours still reads as live.

Every refusal, and its next step

Refusal What to do
blocked A frontmatter value fails your schema. The field is named. Fix it and publish again — this one is never throttled, because it never reached a build.
build-failed Your build command exited non-zero. The tail is on screen.
rejected The remote has commits you do not. Press Update from the site’s repository — a fetch and fast-forward of your own branch. Only genuine divergence, where both sides hold commits the other lacks, still names a terminal.
auth The push credential is missing or wrong. See Environment variables.
no-remote This checkout has no remote by the configured name.
busy Another publish is in flight. It refuses rather than queueing.
cooling A 60-second per-account cooldown, armed around the build. It carries the seconds remaining.

The cooldown has one consequence worth knowing: publish → rejected → sync → publish again now waits. Clearing the cooldown on rejection would let a diverged checkout rebuild forever.

Auto-push

A landed commit pushes its own branch to its own name, debounced about three seconds. That is what makes the staging preview live: you save, the commit lands, the branch pushes, your staging deploy rebuilds with PREVIEW=1, and drafts render there and nowhere else.

Auto-push failure is a log line, never a dialog. The symptom you will actually notice is the site panel’s unpushed count not draining.

It defaults off in a development tree, so a save on your laptop cannot reach the real remote by surprise. The settings row says which way it landed.

Troubleshooting

Publish says “no identity”. The deployment’s GIT_COMMITTER_NAME / GIT_COMMITTER_EMAIL are unset. Commits are authored by the signed-in account and committed by that identity.

The publish button’s count looks wrong. It counts commits ahead of <remote>/<publish branch>, which is not the same as the upstream your auto-push drains. On a first publish to a branch the remote does not have yet, it counts every commit — which is the honest answer.

“Your changes are live” never arrives. Either your production build is slow, or the page answering / has no <meta name="build-rev">. Publishing still worked; only the confirmation depends on that line.

The staging deploy does not show drafts. PREVIEW=1 is missing from that deploy’s build environment. It is the one flag that lets drafts render.

Nothing appears in the queue although files changed. git diff cannot see an untracked file, so the queue unions the diff with status output. If something is genuinely missing, it is a bug worth reporting.

Publish refuses with a licence message. See Licence and activation — and note that an outage is not one of the refusals, because it rides a fourteen-day grace.