Git save and per-page publish: wip safety commits, publish per bundle, pre-flight, discard #10
Labels
No milestone
No project
No assignees
1 participant
Notifications
Due date
No due date set.
Dependencies
No dependencies set.
Reference
Cloonar/deckle#10
Loading…
Add table
Add a link
Reference in a new issue
No description provided.
Delete branch "%!s()"
Deleting a branch is permanent. Although the deleted branch may continue to exist for a short time before it actually gets removed, it CANNOT be undone in most cases. Continue?
What to build
The git backbone (docs/DECISIONS.md D2, D3, D14, D16 pre-flight half): Save writes the file and safety-commits the working tree to a
wipref (crash-safe, never pushed to main). Per-page Publish stages only that page's bundle, commits to main, and pushes; "Publish all" commits everything pending. Before any push,pull --rebase(tool wins insidecontent/, remote wins elsewhere) and a local pre-flighthugobuild of the to-be-published state — build failure refuses the publish and shows the error. Per-page Discard reverts a bundle to its published state. Draft state = uncommitted diff; the UI shows per-page Draft/Published badges.Acceptance criteria
wipref commit; killing and restarting the tool loses no saved workpull --rebaseruns before publish; a developer push to theme files merges cleanly under the path policyBlocked by
Agent Brief
Category: enhancement
Summary: Git backbone for the editing flow: every Save additionally safety-commits the working tree to a local
wipref (crash-safe, never pushed); per-page Publish stages only that page's bundle, commits to main, and pushes after apull --rebase(path policy: tool wins incontent/, remote wins elsewhere) and a local pre-flight Hugo build that blocks broken publishes; per-page Discard reverts a bundle to its published state; the UI shows per-page Draft/Published/Modified badges derived from uncommitted diff state.Dependency note: this issue is hard-blocked by #3 (edit one element end-to-end). It builds on #3's save path — the endpoint that writes a canonically serialized page file to disk — by wrapping it in git semantics; the scheduler handles ordering, so simply do not start until #3 is merged. The issue body's
docs/DECISIONS.mdreferences (D2, D3, D14, D16) point at a file that is not committed — this brief is self-contained and expands those decisions inline.Current behavior:
The harness supervises
hugo serverfor live preview and (after #3) can save an edited element: the save endpoint validates against the sidecar schema and writes the canonically serialized file straight to disk. That write is the only persistence: nothing is versioned by the tool, there is no publish or discard concept, and no notion of a page being "draft" versus "published". If an editor's machine dies mid-session, saved work exists only as loose working-tree files; if a save produces a broken site, nothing stops it from reaching the deployed branch by whatever manual process exists. The tool never invokes git; whether the site directory is a git repository is currently irrelevant to it.Desired behavior:
Model. The site directory is a git repository whose
mainbranch (with a push remote) is the published source of truth — a push to main is what triggers deployment. The editing model on top of git:wipsafety ref.index.md/_index.md), the whole directory including page resources; for a plain single-file page, that file.Save → wip safety commit. Every successful save, after writing the file, commits a snapshot of the entire working tree to a local
wipref. That ref exists purely for crash safety and is never pushed. The snapshot must not disturb the editor's real state: the working tree, the index, and the checked-out branch are exactly as before (use a mechanism that commits without touching the user's index/HEAD). Killing the tool at any point and restarting it loses no saved work — the files are on disk and their history is recoverable from thewipref. Save must not fail overall if the working tree is momentarily identical to the previous snapshot (an empty diff is not an error).Per-page Publish. Publishing page A stages exactly A's bundle paths, commits them to main with a meaningful message identifying the page, and pushes. Page B's pending edits stay uncommitted in the working tree throughout — they are neither committed, stashed away permanently, nor lost. "Publish all" commits every pending content change and pushes. A publish that would be empty (page already matches main) is reported as such, not committed.
Pre-flight build. Before anything is committed to main, the tool runs a local
hugobuild of the exact tree that would be published — main plus the bundle(s) being published, not the whole working tree (page B's half-finished draft must not be able to fail page A's publish, and must not sneak into it either). If the build fails, the publish is refused, nothing is committed or pushed, and the Hugo error output is shown to the editor. The pre-flight must not disrupt the running preview server (build into a throwaway destination).Pull --rebase and path policy. Before pushing, the tool runs
pull --rebaseagainst the remote. Conflicts resolve by path policy: insidecontent/the tool's local version wins; everywhere else (theme, layouts, config, assets) the remote version wins. Net effect: a developer pushing theme changes while an editor works never blocks a publish — the rebase merges cleanly under the policy and the publish proceeds. If the push is rejected anyway (e.g. a race), the error is surfaced to the editor and retrying the publish works without duplicating commits.Per-page Discard. Discarding page A reverts A's bundle to its last-published (main) state, after an explicit confirmation in the UI. Other pages' pending edits are untouched. The preview reflects the revert via LiveReload since the files change on disk. Discarding a page that was never published (no version on main) removes it, with the confirmation making that explicit.
Draft/Published/Modified badges. Page state is derived from the uncommitted diff against main: Published (bundle identical to main), Modified (exists on main, has uncommitted local changes), Draft (exists locally, never published to main). The tree and page views in the SPA show these badges, served by the API, and they update after save, publish, and discard.
Draft-link warning. Publishing a page whose content links to a page that is still in Draft state (would 404 on the live site) warns the editor before proceeding; the editor can confirm and publish anyway.
Degradation. If the site directory is not a git repository (or main/remote is missing), the tool does not crash: saving still writes files as in #3, and the git-dependent surface (publish, discard, badges) reports a clear, actionable error state instead of pretending to work.
Key interfaces:
gitCLI or uses a library is the implementer's choice, but the server should depend on its interface, not on git details.wipref commit.draft/published/modified) exposed wherever the SPA renders the page tree and the open page, so badges are data-driven.Acceptance criteria:
wipref snapshotting the working tree; the ref is never pushed, and the editor's index, HEAD, and branch are untouched by the snapshotpull --rebaseruns before every push; a concurrent remote push touching theme files merges cleanly under the path policy (tool wins incontent/, remote wins elsewhere) and the publish succeedsOut of scope:
git pushX-Remote-User, locks, and CSRF protection (#13) — a fixed configured/default author is fine herehugoon PATH as the preview already doesClosing as superseded: the maintainer has decided on a clean-slate reset of the project (issue #29, PR #30). The v1 implementation this issue builds on is being removed and the plan will be re-drawn from scratch; this spec is retired rather than actioned.