Class-wide additive sidecar overlays from feature bundles, with a theme hook registry #304

Open
opened 2026-10-15 12:27:55 +02:00 by dominik.polakovics · 0 comments

This was generated by AI during triage.

Summary

Let a feature bundle add fields to sidecars it does not own, by class ("every page type", "every block element") rather than by name, together with a collision-free way for the feature to ship the theme partial that consumes them. The motivating case is an SEO feature adding noindex, canonical and social-image fields to every page type and the head partial that renders them.

Context

From a design discussion on 2026-10-15 about TCA-style sidecar extension. Sibling issues: engine capabilities for Access-tab fields, and reusable palettes. Sidecar versioning (#302) is the groundwork.

Today a feature never touches an existing sidecar. The only cross-file edit is the children allowance (docs/features.md "Page-type allowances"), kept textual, soft and revertible. deckle-hosted's private bundles work around this with a hosted- prefix on every element they ship; the contact-form manifest spells out that vendoring an existing element name would collide. Site files override theme files, so two features cannot both ship partials/head.html either.

Design settled in the discussion

  • Additive only. An overlay adds params or fields; it never changes another sidecar's label, default, options or placement. A field with two owners is the drift CLAUDE.md's "Every rule has exactly one owner" forbids, and harness check could no longer name one file as wrong.
  • Conflict is a load error, never precedence. Two overlays, or an overlay and a sidecar, claiming one param name on one schema fail to load naming both files. With that rule there is no load order to define.
  • Targets are classes, never names. A feature cannot know a site's own elements. Items children and inline chips are excluded by the loader, where that rule already lives; the Access tab stays closed (see the capability issue).
  • Position. Overlay fields are appended after the sidecar's own fields of their tab, in feature install order. No anchor such as after:, because the anchored param differs per element.
  • Everything resolves at load into the same expanded schema. Forms, checks, translations, history and the client never learn the concept (ADR 0015 keeps holding).
  • Named by the contributor. The overlay file and the partial carry the feature's name, so the receipt removes them cleanly and nothing collides.
  • Theme hook registry. A config step appends the feature name to a list in hugo.toml; the theme ranges that list and calls partials/hooks/<slot>/<feature>.html when it exists. The list is install order and is the only ordering that exists; harness remove reverts the entry. Theme requirements become named capabilities a manifest can require, extending what the news bundle already states in prose ("needs a theme with the l10n partials").

Open for triage

  • Where the overlay file lives and its grammar: the dotted sidecar name is already the language-file grammar, so it needs its own directory or spelling.
  • The class selector vocabulary: by family, by group, by container-ness.
  • Translation of an overlay field in a connected translation: by its tab, as today.
  • What a save does with a stored param whose overlay was removed: today that is an unknown-parameter error.
  • Which hook slots the theme contract defines first (head, element frame attributes, page footer).
  • Docs: AGENTS.md (a new section beside "Feature bundles"), docs/features.md "The manifest" and a new config step, the theme README, docs/harness.md "The receipt"; skills create-feature, redo-theme, add-page-type, migrate-site.

Blocked by

> *This was generated by AI during triage.* ## Summary Let a feature bundle add fields to sidecars it does not own, by class ("every page type", "every block element") rather than by name, together with a collision-free way for the feature to ship the theme partial that consumes them. The motivating case is an SEO feature adding noindex, canonical and social-image fields to every page type and the head partial that renders them. ## Context From a design discussion on 2026-10-15 about TCA-style sidecar extension. Sibling issues: engine capabilities for Access-tab fields, and reusable palettes. Sidecar versioning (#302) is the groundwork. Today a feature never touches an existing sidecar. The only cross-file edit is the `children` allowance (docs/features.md "Page-type allowances"), kept textual, soft and revertible. deckle-hosted's private bundles work around this with a `hosted-` prefix on every element they ship; the contact-form manifest spells out that vendoring an existing element name would collide. Site files override theme files, so two features cannot both ship `partials/head.html` either. ## Design settled in the discussion - **Additive only.** An overlay adds params or fields; it never changes another sidecar's label, default, options or placement. A field with two owners is the drift CLAUDE.md's "Every rule has exactly one owner" forbids, and `harness check` could no longer name one file as wrong. - **Conflict is a load error, never precedence.** Two overlays, or an overlay and a sidecar, claiming one param name on one schema fail to load naming both files. With that rule there is no load order to define. - **Targets are classes, never names.** A feature cannot know a site's own elements. Items children and inline chips are excluded by the loader, where that rule already lives; the Access tab stays closed (see the capability issue). - **Position.** Overlay fields are appended after the sidecar's own fields of their tab, in feature install order. No anchor such as `after:`, because the anchored param differs per element. - **Everything resolves at load into the same expanded schema.** Forms, checks, translations, history and the client never learn the concept (ADR 0015 keeps holding). - **Named by the contributor.** The overlay file and the partial carry the feature's name, so the receipt removes them cleanly and nothing collides. - **Theme hook registry.** A config step appends the feature name to a list in `hugo.toml`; the theme ranges that list and calls `partials/hooks/<slot>/<feature>.html` when it exists. The list is install order and is the only ordering that exists; `harness remove` reverts the entry. Theme requirements become named capabilities a manifest can require, extending what the news bundle already states in prose ("needs a theme with the l10n partials"). ## Open for triage - Where the overlay file lives and its grammar: the dotted sidecar name is already the language-file grammar, so it needs its own directory or spelling. - The class selector vocabulary: by family, by `group`, by container-ness. - Translation of an overlay field in a connected translation: by its tab, as today. - What a save does with a stored param whose overlay was removed: today that is an unknown-parameter error. - Which hook slots the theme contract defines first (head, element frame attributes, page footer). - Docs: AGENTS.md (a new section beside "Feature bundles"), docs/features.md "The manifest" and a new config step, the theme README, docs/harness.md "The receipt"; skills `create-feature`, `redo-theme`, `add-page-type`, `migrate-site`. ## Blocked by - #302
Sign in to join this conversation.
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#304
No description provided.