feat(harness): feature lifecycle — features listing, receipts, self-allowing add, remove with purge #146
No reviewers
Labels
No labels
bug
enhancement
needs-triage
ready-for-agent
ready-for-human
No milestone
No project
No assignees
1 participant
Notifications
Due date
No due date set.
Dependencies
No dependencies set.
Reference
Cloonar/deckle!146
Loading…
Add table
Add a link
Reference in a new issue
No description provided.
Delete branch "afk/144"
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 #144
What
Feature bundles get a full, machine-readable lifecycle so deckle-hosted can drive them and render its Features UI from the listing.
scaffold/features/<name>/feature.yaml:label,description, integer bundleversion,allow: [{pageType, childOf}]. Validated by a test for every embedded bundle; never vendored into a site.harness features [--site <dir>] [--json]: the catalogue; with--site, installed state and version per feature, read from the receipt.harness add <feature> [--site] [--json]now alsochildrenlist (home.yaml→children: [page, section, news]), comment-preserving, idempotent, soft on a missing/unparsable/unmodellable sidecar (reported as skipped, manual step printed);editor/features/<name>.yaml: every vendored path with its SHA-256, thehugo.tomledits that actually changed something (verbatim lines, so they can be undone exactly), and the applied allowances;already-installed, exit 0, nothing changes); refuses collisions and an unmodellablehugo.tomlbefore writing a byte.harness remove <feature> [--site] [--purge] [--json], driven by the receipt:--purge→blocked, exit 1, not one byte changes; the preview lists feature pages (effective type, with their subtrees), feature elements on other pages at any depth, customised vendored files, pages that mention the feature but cannot be parsed, and pages linking to purged pages;hugo.tomledit only if still exactly as add left it (else kept and reported — an owner-addedtagstaxonomy keeps the kinds enabled), removes the types from every page-type sidecar'schildren, deletes the receipt;assets/media/; identical on clean, dirty and non-git trees.init → add news → remove newsrestores the tree byte for byte, directories included.--jsonemits exactly one object withformatVersion: 1on stdout; exit codes keep the 0/1/2 convention. Statuses: addadded | already-installed | conflict | error; removeremoved | not-installed | blocked | error.hugo.tomlconfig steps are now apply/revert pairs of pure text functions; CRLF files and Hugo's case-insensitive keys are handled; inline/dottedoutputsis refused rather than producing invalid TOML.scaffold/site/AGENTS.mddescribe the three commands, the receipt, the allowance and the purge semantics (drift test passes).Layout
internal/addcmd: manifest/catalogue, receipt, reversiblehugo.tomledits (ApplyConfig/RevertConfig), comment-preservingchildrenedits, the add flow.internal/removecmd: content usage scan and purge (Scan/Purge) and the remove orchestration (Run).cmd/harness/features.go: the three commands, text and JSON rendering.Verification
go vet ./...,go test ./...(26 packages, with Hugo 0.163.3 on PATH so the Hugo-build tests run),web: npm test(1556 tests) — all green.init,features --json,add news --json,features --site --json, news section + item +news-liston home,remove news --json(blocked, tree hash unchanged),remove news --purge --json, Hugo build of the result — OK. Round trip (init → add → remove) diffed against the post-init snapshot: identical.index.mdpurge, CRLFhugo.toml, inline/dotted tables, symlinked ancestors, unknown receipt edit IDs); all are fixed and covered by tests.Not in scope (per the issue)
Bundles outside the binary, updating an installed feature, adopting pre-receipt installs, git operations, restoring purged content, the hosted side.
🤖 Generated with Claude Code
[autoland] verdict: pass
Verdict: PASS (with non-blocking CONCERNS)
Signal relied on: Forgejo Actions
ci / build-and-testrun 132 is green (web build +npm test,go vet,go test ./...with Hugo 0.163.3,go build). Not re-run. The branch sits directly on currentmain(no conflict).Checked: Conventional Commits title; body carries
Closes #144; diff scope matches issue #144 (manifest,harness features, receipt-writingaddwith allowance, receipt-drivenremove --purge, JSON contract);scaffold/site/AGENTS.mdand README updated per CLAUDE.md; no drive-by changes incmd/harness/main.go. Two independent review passes overinternal/addcmd,internal/removecmdandcmd/harnessconfirmed: the blocked/preview path is read-only (tree-hash tests), path-traversal andassets/media/.gitguards are present and tested, element stripping works at arbitrary depth, round trip init→add→remove is byte-exact at both package and CLI level.CONCERNS (edge cases, worth a follow-up issue):
internal/addcmd/children.gosweepChildTypes: checks whether each sidecar file is a symlink but not whethereditor/pagetypesitself is a symlinked directory, soremove --purgewrites the children edit through the link (outside the site) while the same run refuses to delete vendored files behind it. Only reachable if the owner symlinks the directory after the add.internal/addcmd/receipt.go/config.go: a recordedhugo.tomlowner line containing a TAB (e.g.disableKinds\t= [...]) is emitted by the YAML encoder as a plain scalar and loses the tab on read-back, so the "verbatim" revert re-inserts a slightly altered line while reportingreverted. Still valid TOML; fix is to quote recorded lines or assert Marshal→Unmarshal fidelity inWriteReceipt.removecmd.Run's mid-write failure path (partial-report JSON withstatus: errorand the "receipt still in place; re-run" hint).