feat(news): enable categories taxonomy and term pages in the news bundle #132

Merged
dominik.polakovics merged 1 commit from afk/129 into main 2026-07-19 18:55:01 +02:00

harness add news now configures the site for a single categories taxonomy and the news bundle renders working term pages. Closes #129.

What changed

Config step — a new ensureCategoriesTaxonomy step runs after ensureBuildFuture and edits hugo.toml with the same line-based, surgical approach:

  • removes taxonomy/term from disableKinds; when the array empties, the entry and its attached # No taxonomies in the starter site… comment are deleted whole (the comment is only true while the kinds are disabled);
  • declares [taxonomies] category = 'categories' — presence wins: an existing [taxonomies] table is never merged into, mirroring buildFuture's idempotence style;
  • adds taxonomy = ['html'] / term = ['html'] to [outputs], extending the site's existing "restrict output to HTML" policy — without this Hugo would emit its embedded RSS feed for /categories/ and every term, and term RSS is explicitly out of scope.

Re-running the step on its own output is a byte-identical no-op, and it refuses (errors) on any config the scanner cannot confidently model. A site that never adds the news feature keeps taxonomy kinds disabled — scaffold/site/hugo.toml is untouched.

Templates — the bundle vendors term.html (a category's news items, non-draft, date-descending, paginated exactly like the section list via the shared pager partial) and taxonomy.html (the /categories/ overview: terms in use with non-draft item counts, draft-only terms skipped). They sit at the layouts root, not _default/: since Hugo 0.146 a _default/taxonomy.html is a lookup candidate for both the taxonomy and term kinds and shadows _default/term.html (verified against the real binary). The item-card markup is extracted into a shared partials/news-teasers.html used by both the section list and term pages so the two can never drift.

Docs/UX — post-add next steps now explain the front-matter categories opt-in (the editor field ships in a separate stacked issue), and scaffold/site/AGENTS.md documents the bundle's extended config edits per the CLAUDE.md drift rule.

Verification

  • go test ./..., go vet ./..., go build ./..., gofmt — all clean (real-Hugo integration tests included).
  • addcmd: decode-based post-add config assertions, fixed-point idempotence, seeded variants (existing [taxonomies] untouched, extra disableKinds values kept with their comment, pre-restricted outputs untouched), table-driven transform unit tests (every output re-parsed with Hugo's TOML decoder), and an end-to-end init→add→hugo build asserting /categories/ + term page render with no index.xml under them.
  • theme: the fixture site now files eleven items under product and three under company, plus one draft item carrying product and a draft-only secret term. Subtests pin overview hrefs/titles/counts in .ByTitle order, term-page pagination (10 newest + pager + page/2 overflow), single-page term without pager, and the one-feed sweep. A second --buildDrafts build proves the templates' explicit draft filters: term lists and counts unchanged, the generated /categories/secret/ page lists nothing, and the overview omits it — a plain build cannot exercise those filters because Hugo drops drafts on its own.

🤖 Generated with Claude Code

`harness add news` now configures the site for a single `categories` taxonomy and the news bundle renders working term pages. Closes #129. ## What changed **Config step** — a new `ensureCategoriesTaxonomy` step runs after `ensureBuildFuture` and edits `hugo.toml` with the same line-based, surgical approach: - removes `taxonomy`/`term` from `disableKinds`; when the array empties, the entry and its attached `# No taxonomies in the starter site…` comment are deleted whole (the comment is only true while the kinds are disabled); - declares `[taxonomies] category = 'categories'` — presence wins: an existing `[taxonomies]` table is never merged into, mirroring buildFuture's idempotence style; - adds `taxonomy = ['html']` / `term = ['html']` to `[outputs]`, extending the site's existing "restrict output to HTML" policy — without this Hugo would emit its embedded RSS feed for `/categories/` and every term, and term RSS is explicitly out of scope. Re-running the step on its own output is a byte-identical no-op, and it refuses (errors) on any config the scanner cannot confidently model. A site that never adds the news feature keeps taxonomy kinds disabled — `scaffold/site/hugo.toml` is untouched. **Templates** — the bundle vendors `term.html` (a category's news items, non-draft, date-descending, paginated exactly like the section list via the shared pager partial) and `taxonomy.html` (the `/categories/` overview: terms in use with non-draft item counts, draft-only terms skipped). They sit at the layouts **root**, not `_default/`: since Hugo 0.146 a `_default/taxonomy.html` is a lookup candidate for both the taxonomy and term kinds and shadows `_default/term.html` (verified against the real binary). The item-card markup is extracted into a shared `partials/news-teasers.html` used by both the section list and term pages so the two can never drift. **Docs/UX** — post-add next steps now explain the front-matter `categories` opt-in (the editor field ships in a separate stacked issue), and `scaffold/site/AGENTS.md` documents the bundle's extended config edits per the CLAUDE.md drift rule. ## Verification - `go test ./...`, `go vet ./...`, `go build ./...`, `gofmt` — all clean (real-Hugo integration tests included). - addcmd: decode-based post-add config assertions, fixed-point idempotence, seeded variants (existing `[taxonomies]` untouched, extra `disableKinds` values kept with their comment, pre-restricted outputs untouched), table-driven transform unit tests (every output re-parsed with Hugo's TOML decoder), and an end-to-end init→add→hugo build asserting `/categories/` + term page render with no `index.xml` under them. - theme: the fixture site now files eleven items under `product` and three under `company`, plus one draft item carrying `product` and a draft-only `secret` term. Subtests pin overview hrefs/titles/counts in `.ByTitle` order, term-page pagination (10 newest + pager + page/2 overflow), single-page term without pager, and the one-feed sweep. A second `--buildDrafts` build proves the templates' explicit draft filters: term lists and counts unchanged, the generated `/categories/secret/` page lists nothing, and the overview omits it — a plain build cannot exercise those filters because Hugo drops drafts on its own. 🤖 Generated with [Claude Code](https://claude.com/claude-code)
feat(news): enable categories taxonomy and term pages in the news bundle
All checks were successful
ci / build-and-test (pull_request) Successful in 6m8s
d4186a793e
`harness add news` now configures the site for a single `categories`
taxonomy and the bundle renders working term pages:

- New config step ensureCategoriesTaxonomy (after ensureBuildFuture):
  removes taxonomy/term from disableKinds (deleting the entry and its
  attached comment when the array empties), declares
  `[taxonomies] category = 'categories'` (presence wins, never merged),
  and restricts taxonomy/term output to HTML so no term feeds are
  emitted. Byte-identical fixed point on re-run, refuses input the
  line-based scanner cannot model.
- New bundle templates at the layouts root (on Hugo >= 0.146 a
  _default/taxonomy.html would shadow _default/term.html): term.html
  lists a category's non-draft news items newest-first, paginated like
  the section list via the shared pager partial; taxonomy.html lists the
  terms in use with non-draft item counts, skipping draft-only terms.
  The item-card markup is extracted into partials/news-teasers.html,
  shared by the section list and term pages so they cannot drift.
- Post-add next steps mention categories and the front-matter opt-in;
  scaffold/site/AGENTS.md documents the extended config edits.
- Tests: addcmd unit/idempotence/seeded-variant coverage for the new
  transforms plus an end-to-end add-and-build assertion of the rendered
  taxonomy pages; theme integration tests build the fixture site twice
  (plain and --buildDrafts) pinning overview terms/counts/order, term
  pagination, and that the templates' explicit draft filters hold when
  Hugo includes drafts.

Closes #129

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Author
Owner

This was generated by AI while landing a PR.

Landing validation — PASS

Verification signal relied on: Forgejo Actions CI, ci / build-and-test (pull_request)success (6m8s, run 121). Not re-run. This repo's CI is unconditional and installs the pinned Hugo 0.163.3 before go vet ./... && go test ./... && go build, so the real-Hugo theme integration tests covering this PR's acceptance criteria genuinely executed. No conditional gate skipped the path this diff touches.

Checks performed

  • Mergeable: clean merge against main, no conflicts.
  • Conventions: title feat(news): … is Conventional Commits. Body carries a working Closes #129, so the merge will auto-close the issue and release the afk/129 claim.
  • AGENTS.md drift rule (CLAUDE.md): satisfied — scaffold/site/AGENTS.md documents the news bundle's extended hugo.toml edits (taxonomy declaration, re-enabled kinds, HTML-restricted outputs).
  • Diff review vs. issue intent: all five acceptance criteria are met and directly tested. scaffold/site/hugo.toml is untouched, so a site that never adds news keeps disableKinds = ['taxonomy', 'term']. The root-level taxonomy.html / term.html placement is correct and justified in-comment — since Hugo 0.146 a _default/taxonomy.html shadows _default/term.html. Idempotence holds by inspection: on a second run all three transforms hit their unchanged paths and the file is byte-identical with no write.
  • Test coverage: table-driven unit tests per transform (including refusal paths), decode-based post-add assertions, fixed-point idempotence, seeded config variants, and an end-to-end init→add→hugo build asserting /categories/ and term pages render with no index.xml. A second --buildDrafts build proves the templates' explicit draft filters, which a plain build cannot exercise.

Non-blocking observations

  • ensureTaxonomiesTable matches a taxonomies table without checking isArray, so a [[taxonomies]] array-of-tables would also count as "already configured" — whereas ensureTaxonomyHTMLOutputs does check !isArray for [outputs]. The asymmetry is harmless: it fails toward leaving the config untouched, which is the safe direction for a presence-wins step.
  • The vendored taxonomy.html / term.html are generic root templates and would render any future taxonomy, not just categories. Intentional and documented in the template comments; worth revisiting only if a second taxonomy is ever added.

Verdict: PASS — no blockers. Awaiting the maintainer's merge confirmation.

> *This was generated by AI while landing a PR.* ## Landing validation — PASS **Verification signal relied on:** Forgejo Actions CI, `ci / build-and-test (pull_request)` — **success** (6m8s, run 121). Not re-run. This repo's CI is unconditional and installs the pinned Hugo 0.163.3 before `go vet ./... && go test ./... && go build`, so the real-Hugo theme integration tests covering this PR's acceptance criteria genuinely executed. No conditional gate skipped the path this diff touches. **Checks performed** - **Mergeable:** clean merge against `main`, no conflicts. - **Conventions:** title `feat(news): …` is Conventional Commits. Body carries a working `Closes #129`, so the merge will auto-close the issue and release the `afk/129` claim. - **AGENTS.md drift rule (CLAUDE.md):** satisfied — `scaffold/site/AGENTS.md` documents the news bundle's extended `hugo.toml` edits (taxonomy declaration, re-enabled kinds, HTML-restricted outputs). - **Diff review vs. issue intent:** all five acceptance criteria are met and directly tested. `scaffold/site/hugo.toml` is untouched, so a site that never adds news keeps `disableKinds = ['taxonomy', 'term']`. The root-level `taxonomy.html` / `term.html` placement is correct and justified in-comment — since Hugo 0.146 a `_default/taxonomy.html` shadows `_default/term.html`. Idempotence holds by inspection: on a second run all three transforms hit their unchanged paths and the file is byte-identical with no write. - **Test coverage:** table-driven unit tests per transform (including refusal paths), decode-based post-add assertions, fixed-point idempotence, seeded config variants, and an end-to-end init→add→`hugo build` asserting `/categories/` and term pages render with no `index.xml`. A second `--buildDrafts` build proves the templates' explicit draft filters, which a plain build cannot exercise. **Non-blocking observations** - `ensureTaxonomiesTable` matches a `taxonomies` table without checking `isArray`, so a `[[taxonomies]]` array-of-tables would also count as "already configured" — whereas `ensureTaxonomyHTMLOutputs` does check `!isArray` for `[outputs]`. The asymmetry is harmless: it fails toward leaving the config untouched, which is the safe direction for a presence-wins step. - The vendored `taxonomy.html` / `term.html` are generic root templates and would render any future taxonomy, not just `categories`. Intentional and documented in the template comments; worth revisiting only if a second taxonomy is ever added. **Verdict: PASS** — no blockers. Awaiting the maintainer's merge confirmation.
Sign in to join this conversation.
No reviewers
No milestone
No project
No assignees
1 participant
Notifications
Due date
The due date is invalid or out of range. Please use the format "yyyy-mm-dd".

No due date set.

Dependencies

No dependencies set.

Reference
Cloonar/deckle!132
No description provided.