Metadata
| Status | done |
|---|---|
| Assigned | agent-1304 |
| Agent identity | f51439356729d112a6c404803d88015d5b44832c6c584c62b96732b63c2b0c7e |
| Created | 2026-04-30T23:36:57.179784618+00:00 |
| Started | 2026-04-30T23:38:52.970951212+00:00 |
| Completed | 2026-05-01T00:36:06.979114885+00:00 |
| Tags | fix,deploy,html,publishing, eval-scheduled |
| Eval score | 0.87 |
| └ blocking impact | 0.85 |
| └ completeness | 0.92 |
| └ constraint fidelity | 0.85 |
| └ coordination overhead | 0.82 |
| └ correctness | 0.94 |
| └ downstream usability | 0.80 |
| └ efficiency | 0.90 |
| └ intent fidelity | 0.87 |
| └ style adherence | 0.93 |
Description
Description
Currently to publish wg html via rsync on a schedule, the user has to:
- Write the
wg html ...command - Write the
rsync ...command with the right flags - Schedule via system crontab OR
wg add --cron - Manage credentials (SSH key, rsync user) themselves
User wants a higher-level command that abstracts this:
User quote 2026-04-30: 'how do i wg html and rsync in cron? could we set something up to make this easy to set up. rsync endpoints. user level cron? just for watching repos.'
Goal
A single subcommand to register a rsync deployment + schedule for wg html output. Like wg endpoint add but for rsync destinations.
Spec
Command surface
# Register a deployment
wg publish add <name> \
--rsync <ssh-target> # e.g. user@host:/var/www/wg/
[--schedule '*/15 * * * *'] # optional cron expression; if omitted, manual-only
[--since 7d] # optional --since flag passed to wg html
[--public-only] # optional flag passed to wg html
[--out /tmp/wg-publish-<name>] # optional staging dir; defaults to a tmpdir
# List registered deployments
wg publish list
# Run once now
wg publish run <name>
# Show details (rsync target, schedule, last run, last success/failure)
wg publish show <name>
# Remove a deployment
wg publish remove <name>
# Edit (open in $EDITOR)
wg publish edit <name>
What wg publish run <name> actually does
- Runs
wg htmlwith the deployment's flags (--since, --public-only, --out, etc.) - Runs
rsync -avz --delete <out-dir>/ <rsync-target>(sane defaults; let user override flags via deployment config if needed) - Logs success/failure to
~/.wg/publish.log(or task log if scheduled via wg-cron) - Returns appropriate exit code
Forks for the implementer (resolve in design step)
Fork A — cron mechanism
- A1) System crontab (
crontab -eline). Pros: standard. Cons: requires editing user crontab; not git-tracked. - A2) wg's own cron task (
wg add --cron <expr> --exec 'wg publish run <name>'). Pros: graph-tracked, visible in TUI. Cons: requires wg service running. - A3) Both — let the user pick at
wg publish addtime via--cron-via system|wg. Default to A2 since wg service likely already running for active projects.
Fork B — state storage
- B1)
~/.wg/publish.toml(global) — one file lists all deployments, scoped per-machine - B2) Per-project
.wg/publish.toml— deployments are project-scoped, git-tracked alongside the project - B3) Both with cascade — project-local wins; global as fallback for cross-project deployments
Recommend B2 by default (deployments are usually project-specific) with B3 as the cascade so users can have shared global ones.
Fork C — credential handling
- The rsync target uses SSH; the credential is the user's SSH key (typically
~/.ssh/id_*). No new secret-storage needed. - BUT: if the user wants to use a different SSH key or an rsync-over-https variant, expose
--ssh-key <path>or--ssh-config-host <name>so they can. - Out of scope for v1: API-key-based publish destinations (S3, R2, Netlify). Add later if needed.
Fork D — failure notifications
- D1) Log only (default). User checks
wg publish show <name>. - D2) Use existing notification backends (Matrix, Telegram, email, webhook — per CLAUDE.md) on failure. Configurable.
Recommend D1 default + D2 opt-in (--notify-on-failure).
Validation
- Failing tests written first
-
wg publish add my-blog --rsync user@host:/path/ --schedule '*/15 * * * *'registers; appears inwg publish list -
wg publish run my-blogexecutes the html build + rsync; succeeds against a local fixture (e.g. rsync to a tmpdir simulates the real path) -
Schedule actually fires when set up (verify by setting
*/1 * * * *, waiting a minute, checking the log) -
wg publish show my-blogincludes last-run timestamp + success/failure - Live smoke against a real rsync target (use a localhost SSH for the test, OR document it as deferred manual-test if no test fixture exists)
- Existing wg html / wg endpoint / wg cron functionality unchanged
- cargo build + cargo test pass
- Permanent smoke scenario added (with dummy rsync target)
- cargo install --path . was run before claiming done
Process note
The implementer should resolve forks A-D up front (post a short design note in the task log) and then implement against the chosen path. Don't ship a half-implementation that supports both A1 and A2 if A2 is the recommended default — pick one for v1 unless implementing both is genuinely cheap.
Depends on
Required by
Log
- 2026-04-30T23:36:57.162689827+00:00 Task paused
- 2026-04-30T23:38:31.434809744+00:00 Task published
- 2026-04-30T23:38:50.784084535+00:00 Lightweight assignment: agent=Careful Programmer (f5143935), exec_mode=full, context_scope=task, reason=Careful Programmer is the only available programmer agent; this substantial feature needs thorough implementation, design fork resolution, and rigorous validation per CLAUDE.md feedback patterns.
- 2026-04-30T23:38:52.970954298+00:00 Spawned by coordinator --executor claude --model opus
- 2026-04-30T23:39:45.768125097+00:00 Starting work — exploring codebase structure for similar commands (endpoint, html, cron)
- 2026-04-30T23:55:18.253561461+00:00 DESIGN NOTE — forks resolved: Fork A (cron): A2 — wg's own cron task. `--schedule` creates a wg task with `--cron` that calls `wg publish run <name>`. Graph-tracked, visible in TUI. v1 only — skip A1 (system crontab) and A3 (both). Fork B (state): B2 — project-local `.workgraph/publish.toml`. Deployments bound to project. Skip cascade for v1; can add B3 later. Fork C (credentials): SSH key via system SSH config. Optional `--ssh-key <path>` flag for v1. Skip API-key destinations. Fork D (notifications): D1 — log-only. Skip notification backends for v1. NAME CONFLICT: existing `wg publish <task-id>` (commands::resume::publish) needs to coexist. Will refactor to subcommand: `wg publish task <task-id>` for legacy + new subcommands `add/list/show/run/remove/edit`. Update 4 caller sites in add.rs/quickstart.rs. Implementation plan: 1. Add publish.rs config module (load/save .workgraph/publish.toml) 2. Add publish.rs commands module (add/list/show/run/remove/edit + run logic) 3. Refactor cli.rs Publish to subcommand form, keep `task` subcommand for legacy 4. Wire up cron via creating internal wg task on `add --schedule` 5. Tests + smoke scenario
- 2026-05-01T00:26:26.484282490+00:00 Implementation complete: - src/commands/publish.rs (new module — config + add/list/show/run/remove/edit) - src/cli.rs (Publish refactored to subcommand form: add/list/show/run/remove/edit + legacy 'task') - src/main.rs (dispatch new PublishCommands variants) - src/commands/mod.rs (register publish) - src/commands/add.rs, src/commands/quickstart.rs (legacy 'wg publish <id>' → 'wg publish task <id>') - tests/smoke/manifest.toml + scenarios/publish_run_against_local_rsync_target.sh - docs/COMMANDS.md (updated wg publish section) Tests: 10/10 publish unit tests pass; full cargo test --bin wg green (3294 passed, 0 fail). 3 pre-existing integration_e2e_smoke failures unrelated (route-required init regression on main). Live smoke verified against local rsync target — happy path + scheduled deployment lifecycle + cron task creation/abandonment + dry-run + bad-cron rejection + duplicate rejection + ssh-key flag.
- 2026-05-01T00:35:55.878736384+00:00 Committed: 231ded303 — pushed to remote (wg/agent-1304/wg-publish-easy)
- 2026-05-01T00:36:06.979122399+00:00 Task pending eval (agent reported done; awaiting `.evaluate-*` to score)
- 2026-05-01T00:38:46.183110904+00:00 PendingEval → Done (evaluator passed; downstream unblocks)