wg-publish-easy

wg publish: easy setup for rsync-deploying wg html output on a schedule

Metadata

Statusdone
Assignedagent-1304
Agent identityf51439356729d112a6c404803d88015d5b44832c6c584c62b96732b63c2b0c7e
Created2026-04-30T23:36:57.179784618+00:00
Started2026-04-30T23:38:52.970951212+00:00
Completed2026-05-01T00:36:06.979114885+00:00
Tagsfix,deploy,html,publishing, eval-scheduled
Eval score0.87
└ blocking impact0.85
└ completeness0.92
└ constraint fidelity0.85
└ coordination overhead0.82
└ correctness0.94
└ downstream usability0.80
└ efficiency0.90
└ intent fidelity0.87
└ style adherence0.93

Description

Description

Currently to publish wg html via rsync on a schedule, the user has to:

  1. Write the wg html ... command
  2. Write the rsync ... command with the right flags
  3. Schedule via system crontab OR wg add --cron
  4. 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

  1. Runs wg html with the deployment's flags (--since, --public-only, --out, etc.)
  2. Runs rsync -avz --delete <out-dir>/ <rsync-target> (sane defaults; let user override flags via deployment config if needed)
  3. Logs success/failure to ~/.wg/publish.log (or task log if scheduled via wg-cron)
  4. Returns appropriate exit code

Forks for the implementer (resolve in design step)

Fork A — cron mechanism

  • A1) System crontab (crontab -e line). 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 add time 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 in wg publish list
  • wg publish run my-blog executes 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-blog includes 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

Messages 2 messages (2 unread)

  1. #1user2026-05-01T00:07:44.971633737+00:00read
    USER REDIRECT 2026-04-30: STOP — do not rename wg publish <task-id>. The user wants the new feature scoped under `wg html publish` instead, since that's what it logically is — a sub-action of wg html.
    
    Revised command surface:
    
      wg html                                # render (existing, unchanged)
      wg html publish add <name> --rsync <target> [--schedule <cron>] [--since 7d] [--public-only]
      wg html publish list
      wg html publish show <name>
      wg html publish run <name>
      wg html publish remove <name>
      wg html publish edit <name>
    
    CRITICAL: `wg publish <task-id>` (the existing command for releasing paused tasks) MUST stay exactly as-is. Do NOT refactor to `wg publish task <task-id>`. Revert any work you've started on that namespace shift.
    
    State storage: `.workgraph/html-publish.toml` (or similar — under .workgraph/, project-local). All other forks (cron via wg-task A2, log-only D1, SSH config + --ssh-key) stay as you already resolved them.
    
    This avoids the namespace conflict entirely, organizes the feature under the parent it belongs to, and removes any back-compat concern about wg publish <task-id> callers (CLAUDE.md, agent-guide, COMMANDS.md, every batch chat agents file).
  2. #2wg-publish-easy2026-05-01T00:26:38.361560334+00:00read
    Acknowledged USER REDIRECT — pivoting now: revert wg publish refactor (restore Publish{id,only} as-is), nest new feature under wg html publish, store config at .workgraph/html-publish.toml. All other resolved forks (A2 cron, D1 log-only, --ssh-key, --ssh-config-host) stay.

Log