feat: deckle v1 — Hugo CMS harness (complete rebuild) #32
No reviewers
Labels
No milestone
No project
No assignees
1 participant
Notifications
Due date
No due date set.
Dependencies
No dependencies set.
Reference
Cloonar/deckle!32
Loading…
Add table
Add a link
Reference in a new issue
No description provided.
Delete branch "afk/31"
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?
Closes #31
Builds deckle v1 in full per the spec: a single Go binary that turns a plain Hugo site in a git repository into a safe CMS for a non-technical editor — SolidJS editing SPA, lossless canonical content engine, sidecar-YAML constraints, shipped element library + theme, git-backed save/publish, and one-command scaffolding.
Approach
The pre-reset tree (before PR #30) was restored as the baseline — it already covered the engine, Hugo pinning, supervisor, loopback server, sidecar loading, and tree browsing — then audited against this spec section by section (spec won every divergence: trailing-newline canonicalization, site-local
.harness/cache,harness.yamltitle/languages,boolfield vocabulary, typed placement, structured body policy) before the remaining milestones were built on top.Build-order note (recorded per spec): milestones were kept serial on shared surfaces; the only reordering was pulling forward work on disjoint, new file surfaces (theme/library, media core, git package) while unrelated surfaces were in flight. No shared file was ever edited concurrently — the failure mode that killed the retired plan.
What shipped (by milestone)
serve(loopback-only, supervisedhugo server, pinned Hugo downloaded/checksummed into gitignored.harness/, PATH fallback with warning),harness.yaml(title, single language, pin),/api/site.structure|content|inlinegroups,none|markdown|rtebody policy with validated allowlist — h1 never — andallowedInlinecross-checks, typedallowedIn/allowedAtplacement), page-type sidecars with_rootdeclaration, sidecar scaffolder CLI from Go template AST.ce/attrsmarkers only underhugo.IsServer(production hygiene test-pinned); origin-validated postMessage bridge; viewport toggle.renderer.unsafe), links via URL or site tree, inline chips as atomic pills with sidecar popover forms and lossless round-trip.media/library refs; single-file→bundle conversion on first upload; thumbnails outside the repo; delete warns with referencing pages, never blocks; alt required by default with sidecar opt-out.harness init— embedded-by-construction scaffold (theme, library, sidecars, starter page, both CI variants with commented deploy steps); atomic never-overwrite refusal;init && serveverified working end-to-end.Verification
go vet ./...clean;go test ./...green across all 16 packages (engine corpus/fuzz, placement matrices, page ops on temp site copies, git flows on real temp repos with a bare origin, forge fake servers, media sniffing/EXIF oracles, marker/bridge production hygiene).tsc --noEmitclean; production build clean.harness init→serve→ supervised Hugo running → preview HTML carryingdata-ce-*markers; API surface exercised end-to-end.Notes for review
markup.goldmark.renderer.unsafe = true(nested shortcode output in rte bodies); the server's body validation rejecting raw HTML everywhere is the compensating control, documented in the theme README and code.go.modpins Hugo's parser library at v0.161.1 while the binary pin is 0.163.3 — independent concerns (lexer API vs build tool), flagged here for awareness.🤖 Generated with Claude Code
https://claude.ai/code/session_013uRB6W7FXUjjVwtSSaLsD3
internal/content gains typed child-index-path mutations (NodeAt, InsertChild, RemoveChild, MoveChild): full validation before any tree change, adjacent-text merge on removal (Serialize refuses adjacent text), toIndex interpreted against the post-removal list, into-own-descendant rejection. New /api/element/{insert,delete,move} endpoints build inserted shortcodes from schema defaults (block form for body-capable elements, self-closing otherwise) and enforce all three placement axes server-side — allowedIn incl. the page token, allowedAt vs the page's effective type, and the page type's elements allowlist — with the file byte-untouched on every failure. Unknown elements stay movable and deletable but never insertable. /api/page responses now carry the page's effective type for client-side picker filtering. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_013uRB6W7FXUjjVwtSSaLsD3