portable/compound-engineering/skills/html-artifact-composer/SKILL.md
Composes a self-contained, interactive HTML artifact (island-first, single file, token-layer themed, injection-safe) from a workflow command's structured plan payload. Invoked by a workflow command at its artifact-write step -- currently `/workflows:plan` -- never by the user directly.
npx skillsauth add the-rabak/compound-engineering-plugin html-artifact-composerInstall this skill globally with one command. Works with Claude Code, Cursor, and Windsurf.
3 of 9 scanners reported clean
Some scanners were skipped, did not run, or reported a non-clean status. Review each row below.
The create capability of the html-artifacts subsystem (CONTEXT.md → Composer skill). A workflow command invokes this skill at its artifact-write step with a structured payload; this skill turns that payload into one self-contained .html file and returns its path. It never talks to the user directly and never asks for design input.
Any command that reaches its artifact-write step (/workflows:plan today; brainstorm/architecture and other emitters as they migrate) delegates composition to a fresh subagent — it does not run the composer inline in its own context. By the write step the command has accumulated a large context (brainstorm input, gathering-helper reports, the planning/authoring dialogue) that the projection does not need; running a single-file HTML generation on top of it bloats the orchestrator, degrades the generation, and drags that transcript into every later step. The payload is already the clean handoff, so the split is natural:
payload (see Input contract) from its own steps. The payload alone must carry every fact the artifact will show — the one hard rule means nothing else could leak in anyway.SKILL.md (point it at the file — do not paste the skill body into the prompt; the skill is its instruction set), the target_path, the payload, and the design-DNA refs this skill loads. The subagent does the whole classify → project cycle.Orchestrator owns facts (the payload); the composition subagent owns projection (the HTML) — the same delegation /workflows:work uses for heavy units. Every future command that gains a composer write-step invokes it this way, by reference to this section, so the pattern stays single-sourced instead of drifting per command.
Before composing anything, load:
commands/workflows/references/html-artifacts/island-contract.md — the fixed-core schema (Tier 1 envelope + Tier 2 plan contract core), Tier 3/4, the serialization primitive, and the field-coverage map. The island this skill writes MUST conform to it exactly.commands/workflows/references/html-artifacts/primitives-catalog.md — the invariant design DNA (token layer, TOC, tabs, accordion, exporters, injection-safe rendering, etc.) every artifact is built from.commands/workflows/references/html-artifacts/gallery-manifest.md — the one-line-per-entry archetype/recipe index. This is the only gallery file read on every generation.If any of these three cannot be loaded, stop and report the contract as unavailable instead of inventing a schema or a design.
The caller (a workflow command) supplies:
target_path — where to write the artifact, e.g. docs/plans/2026-07-09-feat-csv-export-plan.html.payload — an object covering every Tier 1 envelope field and every Tier 2 plan contract-core field required by island-contract.md's REQUIRED_FIXED_CORE_KEYS, plus the Tier 3 prose fields and any Tier 4 ext{} content the command has already gathered. The caller is responsible for gathering this content (WHY anchor, execution slices, TDD/evidence contract, etc.); this skill is responsible only for turning it into a conformant island and a faithful HTML projection — it does not invent missing facts.If the payload is missing a required fixed-core field, stop and report exactly which field is missing rather than fabricating a placeholder value.
Assign the artifact a primary type (already present on the payload, e.g. plan) and a small set of tags describing what it actually contains: does it have execution slices (vertical-slices tag), a pipeline/architecture diagram worth drawing, exception/waiver arrays worth a risk table, a milestone sequence worth a timeline? Classification is grounded in what the payload actually carries — never tag for an archetype the payload has no data to back.
Read gallery-manifest.md (and only that file) and match rows by type/tags[]. A plan payload with execution slices, at least one TDD exception or constitution waiver, and more than one slice typically matches:
implementation-plan (always, for a plan-kind payload — it is the anchor archetype for this document family)milestone-grid when slices[] has more than one entry with meaningful depends_on chainsrisk-table when tdd.exceptions[] or constitution.waivers is non-emptyflowchart only when the payload's ext{} content includes an actual pipeline/architecture graph to drawLoad only the matched exemplar/recipe file(s) from archetypes//recipes/ — never the whole gallery. Compose their primitives into one document; do not literally copy an exemplar's sample content, only its structural pattern. A matched archetype is a floor, not a ceiling: you are expected to recompose, reorder, augment, and add novel island-backed sections beyond it, and to vary that composition between artifacts — see "Design for engagement" below. Matching tells you what the content is, not what the finished piece must look like.
If no manifest row's type/tags[] combination fits the payload, draft a throwaway template directly from primitives-catalog.md's primitives, following the nearest recipe sheet's "Bespoke-drafting note" as a blueprint for structure. A bespoke template still obeys every invariant below — bespoke means "no pre-built archetype," not "invariants optional."
Generation order is fixed, in this order, every time:
island-contract.md exactly: every Tier 1 + Tier 2 required field from the payload, the Tier 3 prose fields verbatim, any Tier 4 ext{} content the bespoke/bespoke-bridging sections need, and render_meta (see below). Do this before writing a single line of visible HTML.JSON.stringify(island), then replace < → \u003C, > → \u003E, / → \u002F, U+2028 → \u2028, U+2029 → \u2029. Do not use HTML-entity escaping (<) for the island — it is not reversible inside a <script type="application/json"> element (see island-contract.md's "Serialization primitive" section for why). Embed the result verbatim in <script type="application/json" id="artifact-data">…</script>.&, <, >) at render time — this is what keeps a hostile payload (</script>, <img onerror=...>) inert in the visible view even though it is preserved verbatim (and safely) inside the island. This escape is for text-node content only: island prose must never be interpolated into a quoted HTML attribute value (title="…", aria-label="…", data-*="…", alt="…") — the 3-entity set does not cover "/', so a payload like x" onmouseover="alert(1) would survive verbatim there as attribute-injection XSS. Render prose only as element text content; if a fact needs to label an element, put it in the element's text, never its attribute.ext{}, never as HTML-only prose. If the composed layout wants to show something no Tier 1/2/3 field carries, add it to the island's ext{} region first, then render it from there. An HTML section with no corresponding island field anywhere (fixed-core or ext) is a contract violation, not creative freedom.primitives-catalog.md → "The three exporters"). Copy-as-JSON must read the island script's raw textContent and copy it verbatim — never re-JSON.stringify the parsed object, which would not guarantee byte-identity with what was written.render_meta with every archetype id actually composed (in the order composed) and a design_seed string that is distinct per artifact but stable within one (e.g. <type>-<name>-<date>-<a-short-distinct-suffix>) — so re-projection reproduces this design while different artifacts get different visual identities rather than one shared house stamp. The composer writes this field but never reads it back; the html-artifact-mutator skill (v2) is the reader — a content-class mutation's re-projection reuses this exact recorded { archetypes, design_seed } to hold the design stable across edits instead of re-classifying from scratch.These artifacts exist for one reason: to make a human want to open a dense document and actually read it. A projection that is perfectly faithful but visually flat has already failed that job, even when every fact is present. Here, presentation is not decoration — it is the point. Approach each artifact as a piece of design work, not a template to fill.
design_seed stand for a genuine per-artifact aesthetic commitment, not a constant. This is the closest thing to "turning up the temperature" that a projection skill has: deliberate variation, chosen fresh each time.<p> blocks, and use whitespace, sidebars, and visual hierarchy so the eye always has somewhere to land. A dense ext{} block is a candidate for a diagram or a staged reveal, not a 400-word slab.type/status/date — the one narrow carve-out from "Design for engagement" above. These three scalar fields MUST render as a <span class="badge">{value}</span> element — exactly one per field, holding the HTML-entity-escaped value as its text content. Every other layout, wording, and visual choice around them stays free; only this specific markup is fixed, so the html-artifact-mutator skill's scalar fast-path can find exactly one element to patch in place instead of falling back to an appended "Related Artifacts" section.http(s) src/href/@import/url(); no <link> tag; one <style> block; the JSON island plus one component/exporter <script> block.:root custom-property block with a light/dark override — never a hardcoded design choice. This is what makes the output consistent and effortless for the user (SC5): the composer never asks the user for a design decision.@media breakpoint; navigation collapses gracefully on narrow viewports.ext{} backing if no fixed-core field fits) or remove it from the HTML.textContent.render_meta.archetypes lists exactly the archetypes actually composed, and render_meta.design_seed is set.target_path and return that path to the caller. Do not print the full HTML into the conversation — the caller only needs the path.gallery-manifest.md first, then load only the matched files.serialize/extract module. The escaping primitive is authored directly into the artifact's inline <script> each time (per primitives-catalog.md); the only place a reusable JS implementation of it is allowed to live is tests/support/island-spec.ts, which this skill never imports.html-artifact-mutator skill's job, not this skill's.development
Mutates a single field or content region of an existing self-contained HTML artifact in place (island-first, no-added-facts, injection-safe) without regenerating the document from scratch. Invoked by a workflow command whenever it needs to back-write into an artifact it already composed -- e.g. a ref/status update, or a content rewrite that must re-project the affected view -- never by the user directly.
development
Mutates a single field or content region of an existing self-contained HTML artifact in place (island-first, no-added-facts, injection-safe) without regenerating the document from scratch. Invoked by a workflow command whenever it needs to back-write into an artifact it already composed -- e.g. a ref/status update, or a content rewrite that must re-project the affected view -- never by the user directly.
development
Composes a self-contained, interactive HTML artifact (island-first, single file, token-layer themed, injection-safe) from a workflow command's structured plan payload. Invoked by a workflow command at its artifact-write step -- currently `/workflows:plan` -- never by the user directly.
testing
This skill should be used at the end of core compound-engineering workflows to inspect completed artifacts, show the workflow progress checklist, and give the exact next-session command with inputs.