feat: preview-site skill; serve prints the preview URL #247

Merged
dominik.polakovics merged 3 commits from afk/245 into main 2026-10-13 01:07:16 +02:00

Closes #245

Engine

  • internal/supervisor: adds Supervisor.Ready(), a channel closed exactly once on the first transition to running. The supervisor owns the state and serve only waits on the channel.
  • harness serve now prints a second stdout line, site preview ready at <hugo.previewUrl>, after the listening line. It prints once per process: not again after a crash and restart, and never when Hugo fails before it first runs. The tests are cmd/harness/serve_test.go, including a real supervisor that serves, crashes and comes back, and new Ready tests in internal/supervisor.
  • README: "Quick start" shows both lines and the serve bullet describes them. The "Scaffolding" skill list now names previewing.

Scaffold

  • New skill preview-site with the six steps from the issue. It uses POSIX sh blocks: a detached nohup start with the PID and log in the git-ignored .harness/, reuse of this site's recorded run, a fallback to --addr 127.0.0.1:0 when 8090 is foreign, a bounded wait on /api/status, and on failure the error with no link. It also covers handing over the links, restart and stop, and the Publish caveat.
  • AGENTS.md:
    • The intro names preview-site.
    • A new "Previewing locally" section owns the facts: addresses and loopback only, startup order (the pinned Hugo download happens before the listening line), the two stdout lines, the /api/status fields, the single list of changes that need a restart, and what Save and Publish do locally.
    • The "Languages" note, add-backend-layout and setup-site now point at that list.
  • verify-site, redo-theme (plus references/showcase-page.md), setup-site (its report ends with both links), edit-content and migrate-site link to preview-site instead of starting serve inline. verify-site's /api/page curl uses the reported port.
  • scaffold/skills_test.go:
    • expectedSkills includes preview-site.
    • TestAgentsDocNamesSkills checks for it.
    • New TestOnlyPreviewSiteStartsServe fails if any other skill contains serve --site.
  • CLAUDE.md's list of skill tasks names previewing.

Decisions on the issue's assumptions

  • Telling sites apart: /api/site carries no site directory, and title plus languages can collide. The skill reuses only a run it recorded for this site: the PID is alive and the logged address answers /api/site, with a title sanity check. Any other listener on 8090 is treated as foreign, and the skill moves to a free port and says so. No server change.
  • Readiness: a channel on the supervisor, so serve has no polling loop.
  • Note: the status JSON key is hugo.previewUrl, not previewURL as the issue spells it.

Verification

  • go vet ./... and go test ./... are green.
  • The skill's own sh blocks were run as written, from a shell with no harness on PATH and the binary at ~/.deckle/harness/harness:
    • Happy path: READY with both links. The process was still alive in a later command, detached with PPID 1. A second run gives REUSE. Stop leaves no Hugo behind.
    • Foreign listener on 8090: fallback to a free port, and both links work.
    • Undownloadable Hugo pin: FAILED with the hugobin error and no link.
    • Broken theme template: FAILED with lastBuildError from the status payload and no link.
    • Port race at listen: the retry with ADDR=127.0.0.1:0 works.

Left alone (out of scope)

  • The README --preview-base-url bullet still mentions a postMessage origin, which may be stale after #242.
  • The starter page scaffold/site/content/_index.md says "Run harness serve".

🤖 Generated with Claude Code

Closes #245 ## Engine - `internal/supervisor`: adds `Supervisor.Ready()`, a channel closed exactly once on the first transition to `running`. The supervisor owns the state and serve only waits on the channel. - `harness serve` now prints a second stdout line, `site preview ready at <hugo.previewUrl>`, after the listening line. It prints once per process: not again after a crash and restart, and never when Hugo fails before it first runs. The tests are `cmd/harness/serve_test.go`, including a real supervisor that serves, crashes and comes back, and new `Ready` tests in `internal/supervisor`. - README: "Quick start" shows both lines and the `serve` bullet describes them. The "Scaffolding" skill list now names previewing. ## Scaffold - New skill `preview-site` with the six steps from the issue. It uses POSIX `sh` blocks: a detached `nohup` start with the PID and log in the git-ignored `.harness/`, reuse of this site's recorded run, a fallback to `--addr 127.0.0.1:0` when 8090 is foreign, a bounded wait on `/api/status`, and on failure the error with no link. It also covers handing over the links, restart and stop, and the Publish caveat. - `AGENTS.md`: - The intro names `preview-site`. - A new "Previewing locally" section owns the facts: addresses and loopback only, startup order (the pinned Hugo download happens **before** the listening line), the two stdout lines, the `/api/status` fields, **the single list of changes that need a restart**, and what Save and Publish do locally. - The "Languages" note, `add-backend-layout` and `setup-site` now point at that list. - `verify-site`, `redo-theme` (plus `references/showcase-page.md`), `setup-site` (its report ends with both links), `edit-content` and `migrate-site` link to `preview-site` instead of starting serve inline. `verify-site`'s `/api/page` curl uses the reported port. - `scaffold/skills_test.go`: - `expectedSkills` includes `preview-site`. - `TestAgentsDocNamesSkills` checks for it. - New `TestOnlyPreviewSiteStartsServe` fails if any other skill contains `serve --site`. - `CLAUDE.md`'s list of skill tasks names previewing. ## Decisions on the issue's assumptions - **Telling sites apart:** `/api/site` carries no site directory, and title plus languages can collide. The skill reuses only a run it recorded for this site: the PID is alive and the logged address answers `/api/site`, with a title sanity check. Any other listener on 8090 is treated as foreign, and the skill moves to a free port and says so. No server change. - **Readiness:** a channel on the supervisor, so serve has no polling loop. - **Note:** the status JSON key is `hugo.previewUrl`, not `previewURL` as the issue spells it. ## Verification - `go vet ./...` and `go test ./...` are green. - The skill's own `sh` blocks were run as written, from a shell with no `harness` on PATH and the binary at `~/.deckle/harness/harness`: - **Happy path:** READY with both links. The process was still alive in a later command, detached with PPID 1. A second run gives REUSE. Stop leaves no Hugo behind. - **Foreign listener on 8090:** fallback to a free port, and both links work. - **Undownloadable Hugo pin:** FAILED with the `hugobin` error and no link. - **Broken theme template:** FAILED with `lastBuildError` from the status payload and no link. - **Port race at listen:** the retry with `ADDR=127.0.0.1:0` works. ## Left alone (out of scope) - The README `--preview-base-url` bullet still mentions a postMessage origin, which may be stale after #242. - The starter page `scaffold/site/content/_index.md` says "Run `harness serve`". 🤖 Generated with [Claude Code](https://claude.com/claude-code)
The supervisor now owns a Ready() channel, closed exactly once on the
first transition to running. serve arms announcePreview after the
listening line, so stdout reads:

  deckle harness listening on http://127.0.0.1:8090/
  site preview ready at <hugo.previewUrl>

The second line is printed once per process. It is not printed again after a
crash and restart, and never when Hugo fails before it first runs.

Refs #245

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
feat(scaffold): preview-site skill and AGENTS.md "Previewing locally"
All checks were successful
ci / build-and-test (pull_request) Successful in 20m34s
ci / release (pull_request) Has been skipped
c76f6c55bc
New scaffolded skill preview-site. It reuses this site's own recorded run or
starts the harness detached (nohup, PID and log in .harness/), falls back to
a free port when 8090 is foreign, waits on /api/status until Hugo is
running, hands over the editor and preview links (or the failure and no
link), and covers restart, stop and the Publish caveat.

AGENTS.md gains "Previewing locally", which owns the facts the skill needs:
addresses and the loopback-only rule, startup order, the two stdout lines,
the status fields, the single list of changes that need a restart, and what
Save and Publish do locally. The "Languages" restart note and the
add-backend-layout and setup-site restart notes now point there.

verify-site, redo-theme (plus the showcase reference), setup-site,
edit-content and migrate-site link to preview-site instead of starting
serve inline. A new drift test, TestOnlyPreviewSiteStartsServe, keeps it
that way.

Closes #245

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Author
Owner

[autoland] verdict: pass

Verdict: CONCERNS (non-blocking). Approve only, not merged.

Signal: CI never started (build-and-test stayed "Waiting to run", so no runner picked it up). Instead I ran the CI job's own steps locally on c76f6c5: web npm ci && npm run build && npm test (115 files, 2828 tests passed), go vet ./..., and go test ./.... All of them passed.

Conventions: the Conventional Commits title is fine and Closes #245 is present. The diff matches the issue. AGENTS.md, the affected skills, CLAUDE.md and the README all move together. Readiness has one owner: Supervisor.setState closes Ready(), and serve only waits on it.

Concern:

  • scaffold/site/AGENTS.md:183-191 ("Saving and Publish"), relayed by preview-site/SKILL.md:173: this says saving in a local editor "commits to no branch and pushes nothing", with no condition attached. That only holds in WIP mode, when HEAD is on main or detached. When the checkout is on any other branch, the harness runs in branch mode (README "Branch mode"). Every save then commits on that branch and pushes it. So a developer or agent previewing from a feature branch, or from the editor branch that setup-site says never to push, gets their saves committed and pushed while the skill tells them to "save freely". Fix: state the condition, or name branch mode, in the AGENTS.md fact. The skill then inherits the fix through its pointer.

Nits (not blocking):

  • scaffold/site/AGENTS.md:273 says "Previewing locally" below, but the section is above it, at line 137.
  • scaffold/site/.gitignore:1 still describes .harness/ as only the thumbnail cache. It now also holds preview.pid and preview.log.
  • README.md:2081 was not re-wrapped.
[autoland] verdict: pass Verdict: CONCERNS (non-blocking). Approve only, not merged. Signal: CI never started (build-and-test stayed "Waiting to run", so no runner picked it up). Instead I ran the CI job's own steps locally on c76f6c5: web `npm ci && npm run build && npm test` (115 files, 2828 tests passed), `go vet ./...`, and `go test ./...`. All of them passed. Conventions: the Conventional Commits title is fine and `Closes #245` is present. The diff matches the issue. AGENTS.md, the affected skills, CLAUDE.md and the README all move together. Readiness has one owner: `Supervisor.setState` closes `Ready()`, and serve only waits on it. Concern: - scaffold/site/AGENTS.md:183-191 ("Saving and Publish"), relayed by preview-site/SKILL.md:173: this says saving in a local editor "commits to no branch and pushes nothing", with no condition attached. That only holds in WIP mode, when HEAD is on `main` or detached. When the checkout is on any other branch, the harness runs in branch mode (README "Branch mode"). Every save then commits on that branch and pushes it. So a developer or agent previewing from a feature branch, or from the `editor` branch that setup-site says never to push, gets their saves committed and pushed while the skill tells them to "save freely". Fix: state the condition, or name branch mode, in the AGENTS.md fact. The skill then inherits the fix through its pointer. Nits (not blocking): - scaffold/site/AGENTS.md:273 says `"Previewing locally" below`, but the section is above it, at line 137. - scaffold/site/.gitignore:1 still describes `.harness/` as only the thumbnail cache. It now also holds preview.pid and preview.log. - README.md:2081 was not re-wrapped.
docs(scaffold): "Saving and Publish" states the WIP/branch-mode condition
All checks were successful
ci / build-and-test (pull_request) Successful in 20m20s
ci / release (pull_request) Has been skipped
8e8d81a4e9
The AGENTS.md fact said saving in a local editor commits to no branch and
pushes nothing, unconditionally. That holds only in WIP mode (HEAD on
`main`, detached, or no repository). On any other branch the harness runs
in branch mode and every save is committed on that branch and pushed, so
an agent previewing from a feature branch — or from the `editor` branch
setup-site says never to push — would have been told to "save freely"
while its saves went to the remote.

The fact now names both modes and the one switch between them (the
checked-out branch), points at `GET /api/git/state` for the running mode,
and limits "save freely" to WIP mode. The preview-site skill's step 6
inherits it: check `git branch --show-current` before starting and say in
the hand-over when saves will be committed and pushed.

Review nits from #247: "Previewing locally" is above the element
procedure, not below; the scaffold .gitignore comment names the preview
run (PID and log) that `.harness/` now holds; the README skill-list
paragraph is re-wrapped.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Author
Owner

Addressed the review concern and the three nits in 8e8d81a.

Concern (AGENTS.md "Saving and Publish"): the fact now states the condition. With HEAD on main, detached, or in no repository the harness is in WIP mode and a save writes files only, commits to no branch and pushes nothing. With HEAD on any other branch it is in branch mode: that branch is the draft branch, and every save is committed on it and pushed at once. It tells the reader to check git branch --show-current before starting a local editor, warns about feature branches and the deckle-hosted editor branch (pointing at setup-site), names GET /api/git/state as the way to read the running mode, and limits "save freely" to WIP mode. Publish is described as pushing main in both modes.

preview-site step 6 inherits the fix through its pointer: it now says to run git branch --show-current before step 2 and to say in the hand-over when saves will be committed and pushed.

Nits: "Previewing locally" is now referenced as above the element procedure; the scaffold .gitignore comment names the preview run (PID and log) in .harness/; the README skill-list paragraph is re-wrapped.

go vet ./... and go test ./... are green (web built first for the embed).

Addressed the review concern and the three nits in 8e8d81a. **Concern (AGENTS.md "Saving and Publish"):** the fact now states the condition. With HEAD on `main`, detached, or in no repository the harness is in WIP mode and a save writes files only, commits to no branch and pushes nothing. With HEAD on any other branch it is in branch mode: that branch is the draft branch, and every save is committed on it and pushed at once. It tells the reader to check `git branch --show-current` before starting a local editor, warns about feature branches and the deckle-hosted `editor` branch (pointing at `setup-site`), names `GET /api/git/state` as the way to read the running mode, and limits "save freely" to WIP mode. Publish is described as pushing `main` in both modes. `preview-site` step 6 inherits the fix through its pointer: it now says to run `git branch --show-current` before step 2 and to say in the hand-over when saves will be committed and pushed. **Nits:** "Previewing locally" is now referenced as *above* the element procedure; the scaffold `.gitignore` comment names the preview run (PID and log) in `.harness/`; the README skill-list paragraph is re-wrapped. `go vet ./...` and `go test ./...` are green (web built first for the embed).
Sign in to join this conversation.
No reviewers
No milestone
No project
No assignees
1 participant
Notifications
Due date
The due date is invalid or out of range. Please use the format "yyyy-mm-dd".

No due date set.

Dependencies

No dependencies set.

Reference
Cloonar/deckle!247
No description provided.