Skip to content

Releases: rhnfzl/human-html

v2.0.0

Choose a tag to compare

@github-actions github-actions released this 13 Aug 07:51
8aa124d

Breaking. data-audience is retired rather than aliased and now blocks, so a post-cutoff artifact still carrying the pre-rename summary marker fails until it is changed to data-summary="true". Artifacts dated before 2026-05-25 are grandfathered and unaffected. The summary itself does not change, only the attribute.

Ownership attaches to the claim rather than the artifact, every summary says what would change its own conclusion, and no surface an author reads or copies names a job title any more. Length gains the ceiling required-section never had, read-time is checked against the prose instead of trusted, and the review state has three names. Four rounds of independent review are folded in, each of which found real defects in the previous round's fixes.

  • House rule: the prompt lives in the provenance footer, never in a card at the top (references/patterns.md). A visible "the prompt that made this" block with a copy button looks like transparency and costs the most valuable space on the page on something the reader did not come for; they opened a plan to find out whether the plan is sound. promptHash in the footer already makes the artifact auditable. The rule is about placement, not concealment: put the full prompt in the footer whenever it is non-sensitive, which is more transparent than a card and costs the reader nothing.
  • Files read (references/patterns.md, optional, best in understanding / research / review). An artifact explaining code makes claims about a codebase that keeps moving, and six weeks later nobody can tell which claims still hold. A collapsed <details> block lists the files consulted and the revision they were read at, which converts the question into a command: git diff --stat <rev> -- <paths>, and empty output means nothing the artifact read has changed since, in history or in the reader's working copy. Deliberately not <rev>..HEAD, which compares two commits and so reports an uncommitted edit as clean; both forms were run against a planted uncommitted edit to confirm which one catches it. The revision is the whole pattern, so check warns when a sourceFiles block omits readAtRevision or ships an empty path list, and stays silent when there is no block at all. The pathspec may be wider than the list and must never be narrower, which is the one way this pattern fails while looking fine: a trimmed pathspec still prints nothing while a file a claim rests on has moved. Mirrored into the provenance JSON-LD as sourceFiles so it is machine-checkable, and demonstrated in two shipped examples: understanding-canonical.html (three files it traces a request through) and dynamic-port-correspondence.html (five files across a port, all read at one branch tip that carries both implementations, with a pathspec widened on purpose because a correspondence map is also wrong when a sixth file appears that nobody mapped, said in a clause so the mismatch does not read as a slip). Two tests run over every example carrying the block rather than one named file, so a new one is covered on arrival: the visible list, the <summary> count and the JSON-LD must agree, and the pathspec must contain every path listed. Deliberately NOT added to review, research, plan, status, incident or prototype: those examples cite no file paths at all, and the pattern's own rule is to list what was read rather than what exists. Needs no CSS; the scaffold already styles details.
  • The files-read guards could pass while the block was wrong, and the snippet people copy broke a rule printed beside it. Three findings from the Codex second eye, all reproduced before being accepted and all reproduced again as failures afterwards. First, the revision was matched anywhere in the block, so the <summary> could drift to a different revision while the command still carried the declared one; the summary is the revision the reader actually reads, so it and the command are now checked separately. Second, the path regex demanded exactly <li><code>path</code></li>, so a visible entry the JSON-LD never declared could hide behind an attribute; attributes are tolerated now, and a count assertion fails when any <li> is not read as a path, which also catches a path written without <code>. Both guards were sharing nothing, which is how they drifted, so they now share one parser. Third, the patterns.md snippet listed routes/*.ts and diffed the wider routes/, costing every copier a false alarm whenever an undeclared routes/README.md changed. The snippet is now held tighter than a shipped artifact, its pathspec having to equal its declared paths rather than merely contain them, because it is the copy source and a bare widening there teaches the exception as the default. Widening stays legal for an artifact, demonstrated with its explaining clause in dynamic-port-correspondence.html. A new test holds the snippet to this, since nothing checked it at all before.
  • A second reviewer pass found four more ways the guards were wrong, two of them in the fixes for the first pass. Each reproduced before being accepted. A revision was matched by substring, so a declared main was satisfied by a summary reading domain and by a command touching domain/file.ts; readAtRevision is any non-empty string and so may be a branch or tag rather than a hex SHA, which makes that collision reachable rather than theoretical. Both revisions are now compared as exact tokens. Only the first files-read block in a file was ever parsed, so a second would go unchecked, which the guards now assert against rather than quietly inspecting one block out of several. The rule holding the patterns.md snippet to its declared paths compared ordered lists, so the same paths in a different order failed while covering the identical scope; order is no longer checked, and a genuinely trimmed pathspec still fails. And the claim that empty output proves nothing changed in history was too strong in the other direction: git diff <rev> -- <paths> compares content at the two ends, so a file changed and later reverted also reads as clean. That is the right answer for staleness and the wrong claim about history, so both the pattern and the example now say "still matches what was read".
  • Same-data control block (references/patterns.md). A multi-option comparison has a failure mode that is invisible from outside: each option gets measured against whichever input flatters it, and the table at the end looks like a fair scoreboard while being four anecdotes in a grid. One block before the first option names the single input every option is judged against, says which way it is biased and confirms the bias falls evenly, states the properties that actually separate the designs rather than just the volume, and asserts that every number below comes from that input. dynamic-design-directions.html shipped as the worked example a release earlier; this documents it so it can be reached deliberately. The snippet carries the two CSS rules it needs, both drawn from scaffold variables, because .control and .fixed are the example's own and copying the markup alone would render unstyled.
  • Ownership attaches to the claim, not to the artifact (Rule 11, claim-owner, WARN). A recommendation nobody holds is the artifact arguing on its own behalf, and a reader has nothing to push back against. The rule is forced by the two kinds that carry both description and judgment: an architecture reports Before/After then recommends, a review reports Concerns then issues a Verdict, and both split down the same seam. Attaching ownership below the level where they split dissolves the problem rather than assigning it by fiat. data-owner on the section, plus the holder named in prose beside one sentence of their own doubt, because a name alone is bureaucracy and a name with a stated doubt tells a reviewer where to spend attention. No kind list: a heading regex selects the judgment section exactly as comparison-visual selects a comparison, so kind sensitivity emerges instead of being declared. research and understanding carry no such heading and never trip it; decision always does. In force in dynamic mode too, since who holds a call does not depend on which sections exist. status is the deliberate gap, its accountability being artifact-wide and already carried by the ribbon's Owner. The search window is the subtle part and is what the tests target: an owner sits on the section's opening tag, before the heading, so the window cannot start where comparison-visual's does, and it is floored at the previous heading so a neighbour's owner cannot satisfy an unowned section. A first cut of the marker regex accepted data-owner="", because \S matched the closing quote; the test written for that case is what caught it.
  • The reviewer field starts empty and says so. The scaffold shipped reviewer: "<human reviewer or role>" and rendered "reviewed by <reviewer>", which reads as a claim that review happened. It now ships "pending" and renders not yet reviewed by a human. Measured on a live lane of 197 artifacts before the change: 89 carried a human name, 51 carried no reviewer field at all, 29 carried an agent name, 18 carried some spelling of "pending" across nine distinct spellings, and 10 shipped the unfilled placeholder, which is an author hand-rolling a state the schema never offered. An agent name there is honest reporting rather than a mistake; the schema simply has no word for it yet. The shipped examples keep their reviewer names on purpose: they show the end of the lifecycle, the scaffold shows the start.
  • Every summary states what would change its own conclusion (Rule 1, fourth bullet, deliberately not validated). The first three bullets orient and none of them concludes, so a reader who cannot tell whether the opening is complete has one safe move, which i...
Read more

v1.3.0

Choose a tag to compare

@github-actions github-actions released this 26 Jul 13:31
dff262d

Artifacts gain a dynamic mode for the cases where a prescribed section skeleton makes the document worse, and a written spine covering what an artifact must never do whatever shape it takes. Rule 9 (no-JS robustness) becomes verifiable instead of assumed. Hook automation is one idempotent command. Three contract defects in the shipped examples are fixed, and the summary marker stops naming a job title.

Added

  • artifact-mode="dynamic". The nine kinds pre-decide a section skeleton before anyone knows what the artifact needs to say, which is the right trade most of the time and the wrong one when the shape is the argument (a findings taxonomy, a four-way exploration, a line-by-line port map). Adding <meta name="artifact-mode" content="dynamic"> beside the kind, or scaffolding with new <kind> "<title>" --mode dynamic, stands down exactly three rules and nothing else: required-section (off), read-map (off), and nav-anchors (BLOCK to WARN). Mode is a separate field from kind on purpose, so the kind still says what the document is for and drives the gallery and the filename, and a plan can go dynamic when the skeleton stops fitting. Every other rule is untouched, because the rule set was always two populations wearing one coat: required-section says a plan looks like this, viewport-meta says a human on a phone can read this, and only the first is about shape. The mode-selection rule is one question, does the reader benefit from this looking like the last one, with the kind as the tie-break, since a kind that fits slightly badly costs a reader less than an invented structure that fits nothing.
  • references/artifact-spine.md. The rules say what an artifact must contain; nothing said what it must never do, and a mode defined only by what it removes drifts straight to a model's defaults. Three layers: the mechanical floor (plus the requirements that cannot be linted, colour never carrying meaning alone, hover always paired with focus and touch, and a scroll container always reachable by keyboard), the spine an artifact never trades away in any mode (no coined framework names, never name or segment a reader, no engineered keystone, hedges stay, every claim earned, credibility from this artifact's own evidence, avoid the first person, never stage common knowledge as a reveal, inherit the tokens), and prose discipline (after every list one sentence on what it implies; name the concrete failure not the category; small precise numbers over adjectives; one canonical noun per thing; no participle analysis tails; vary the block rhythm). Dynamic mode is free in structure and locked in style. The floor table states what each check actually proves next to what it aims at, because every one of them is a marker check.
  • render --no-js. Rule 9 protects readers in JavaScript-disabled previews (iOS Quick Look, Android file previews, email) and was the one rule the harness could not check: js-content-fallback only greps for a <noscript>, so it cannot tell a control from content and cannot read whether the fallback matches what the script would build. render --no-js serves the page as a JS-disabled browser treats it and screenshots the result, so you can look. No Chrome flag does this, which is why it is a rewrite: --disable-javascript is accepted and silently ignored (its screenshot is byte-identical both to a run with JS on and to a run with a deliberately invented flag), and --blink-settings=scriptEnabled=false makes headless Chrome emit no output at all. simulate_no_js() reproduces the only two things disabling JS changes, and says so plainly rather than claiming to be the browser's own setting.
  • Three dynamic examples that share no structure on purpose (examples/dynamic-*.html), across three different kinds, so no single shape reads as the template a dynamic artifact should copy. A test fails if any two of them converge on a shared kind or heading set.
  • Global hook activation. python3 <skill-dir>/activate_hooks.py merges the advisory and autoindex hooks into Claude Code, Codex, Cursor, and Windsurf settings without replacing unrelated configuration.
  • Windsurf support. A thin adapter translates Cascade's write and command hook payloads into the existing agent-neutral hook input.
  • Discoverable wiring metadata. The shipped hooks include their event and matcher headers for downstream installers.
  • Implementation deviation ledger (references/workflow-integrations.md). The during-the-build log that had no home: plan is written before the work and review happens after it, so everything the code teaches you mid-build evaporates into agent scrollback and the next attempt rediscovers it. Four fixed fields per entry, what the plan said, what the code revealed, the conservative choice taken, what to revisit, closing with a mandatory fold-forward block that turns the surprises into three instructions for the next attempt. Uses the existing status kind; no new artifact kind and no validator rule.
  • Reader-response compiler (references/workflow-integrations.md). Most artifacts end with the reader having read; this one ends with them having replied, as a block they paste straight back. The no-JS shape is prescribed rather than suggested, because the obvious implementation renders as dead controls and an empty box in exactly the previews Rule 9 exists to protect.
  • Review order vs execution order (references/patterns.md). A plan sorted by likelihood-of-tweaking rather than build order, so every judgment call is up front and the mechanical work collapses into <details>. Includes flagging the weakest part of your own plan, and pre-writing the likeliest pushbacks so the reader can send one in a click.
  • Non-goals (references/patterns.md). "What we deliberately did not do", written as the omission plus its reason. Pre-empts the whole class of review comment that begins "why didn't you also...". Distinguished from Open questions: a non-goal is settled, an open question needs an owner and a date.
  • Linked source-to-target correspondence (references/patterns.md). For ports and migrations: corresponding regions in the source and target excerpts share a token and a number, with the trap in a numbered margin note, plus a preserved / deliberately changed / dropped ledger. The numbered correspondence must carry the meaning with no pointer and no JS.
  • Pre-merge self-check (references/patterns.md). A handful of questions attached to a review artifact, answerable only by someone who understood the change. Native <details>, so it works with JavaScript off. Explicitly not a gate and never scored: answering questions about a document is not evidence the change is safe, so the honest claim is the narrow one, that a reviewer who cannot answer these has not read what they are about to approve.

Changed

  • data-audience="pm" is now data-summary="true", and the pm-summary rule is summary-first. The contract bans labelling reading depth by job title, and the nine canonical read maps were relabelled for that reason. The summary marker was the same segmentation one layer down in the markup, and it was the one rule that blocked unless you wrote it. The pre-rename attribute is still accepted so already-shipped artifacts keep validating, and retired rule IDs still answer to suppression comments, so nothing breaks on upgrade. The .pm-summary class is now .lead-summary throughout, since leaving it behind means the next author copies the job title forward.

Fixed

  • js-content-fallback now detects DOM insertion, not one phrase. The rule matched only .innerHTML =, so content assembled with createElement + textContent + appendChild passed silently, including in this skill's own prototype-canonical.html, which shipped an empty <ul> filled by JS and no <noscript>, rendering blank in iOS Quick Look. Detection now covers innerHTML / outerHTML assignment, insertAdjacentHTML, replaceChildren, appendChild / append / prepend, insertBefore, and document.write, and deliberately not a bare createElement (a detached node puts nothing on the page), so the JS-injected copy-button pattern stays clean. Also fixes a pre-existing false positive: .innerHTML === (a read) matched as a write.
  • prototype-canonical.html never had a working static floor. It hid its inert slider and copy button through a <noscript> stylesheet rule, but those controls carry an inline style="display:flex", and an inline declaration beats a stylesheet rule. A reader with JavaScript off saw a live-looking slider driving nothing, and it passed js-content-fallback throughout because a <noscript> element was present. Fixed with display: none !important; the trap is documented in SKILL.md and the spine, because anyone hiding an inline-styled control will hit it. Found by render --no-js on its first real use.
  • Renaming a rule broke every suppression written against its old name. _add matches the emitted rule ID exactly, so an installed workspace carrying <!-- human-html-disable: pm-summary --> went from passing to a blocking summary-first error, with nothing in the message explaining why. Retired IDs now keep answering through _RULE_ID_ALIASES, guarded by a test that also asserts the alias does not silence the rule for artifacts that never suppressed it. Any future rename adds its alias there.
  • Reading guides in all nine canonical examples were role-based, which the contract forbids. SKILL.md requires depth-based guides and explicitly bans job-title labels, yet every example shipped Exec (2 min) / PM (5 min) / Engineer (15 min). Because SKILL.md tells agents to read the canonical example before writing an artifact of that kind, the example was quietly overriding the rule. Relabelled to Quick read / Standard / Full read, keeping the time budgets.
  • **Mermaid lab...
Read more

v1.2.4

Choose a tag to compare

@github-actions github-actions released this 12 Jul 07:19

Fix a broken remote install: npx skills add rhnfzl/human-html now ships the full skill, not just SKILL.md. No change to generated artifacts.

  • The skill moved into skills/human-html/. The skills installer copies a skill's full payload only when it lives in a subdirectory; a root-level remote skill shipped SKILL.md alone, dropping the engine, references, templates, examples, and hooks, so the installed skill told the agent to run a script and read files that were never installed. Relocating the skill into skills/human-html/ puts it on the subdirectory code path, so every support file lands. Mirrors the slide-sage and explore-unknowns layout.
  • Re-install note for existing users. If a previous install left the skill referencing a script or reference files that were not present, re-run npx skills add rhnfzl/human-html (or your installer's equivalent) to pull the complete payload.
  • Plugin channel unchanged. The Claude Code plugin still installs from the same marketplace (source: "./"); the slash command and hooks resolve the relocated engine via $CLAUDE_PLUGIN_ROOT/skills/human-html/. Manual "clone and symlink" now targets skills/human-html/, and the release version-guard reads the relocated SKILL.md.

v1.2.3

Choose a tag to compare

@github-actions github-actions released this 10 Jul 08:22

Security-posture housekeeping. No functional change to generated artifacts.

  • Add SECURITY.md. Documents the skill's two intentional external touch-points and why they are safe: the optional bring-your-own-bucket S3 publish helper (uploads to a bucket you own, then prints a link - never downloads or executes), and the optional client-side, version-pinned, self-gating Mermaid CDN import used only for live (non-shipped) diagrams. Gives human auditors and LLM-assisted skill scanners the rationale in-repo.
  • CI: bump actions/checkout@v4 to @v5 in the release workflow; the v4 pin runs on the deprecated Node 20 runner.

v1.2.2

Choose a tag to compare

@github-actions github-actions released this 10 Jul 08:13

Clear the Snyk E005 "suspicious download URL" audit finding on scripts/publish-s3.sh, and harden the returned link.

  • No hardcoded S3 endpoint in the shipped script. The two hand-built virtual-hosted S3 object URLs - which the skills.sh Snyk audit flagged as a generic "personal file hosting" download URL (rule E005) - are gone. The open URL is now derived from the AWS CLI's own presigner: the presigned URL is generated first and the clean object URL is that URL minus its query string. Behaviour is unchanged (public / static-website buckets still get the clean direct URL; private buckets still get the presigned one), but no literal S3 host string lives in the skill source for a scanner to match.
  • Two latent bugs fixed for free. The old no-HUMAN_HTML_S3_REGION branch built the legacy global endpoint (breaks on newer regions) and left ${key} un-encoded; the presigner-derived URL is region-correct and URL-encoded by the SDK.

v1.2.1

Choose a tag to compare

@github-actions github-actions released this 09 Jul 16:40

Fix a pre-existing horizontal overflow at phone width, and harden the generator against its cause.

  • Three canonical examples (architecture, review, understanding) no longer overflow at 390px. They predate the v1.1.4 overflow discipline: a wide <table> without the responsive reflow (architecture), .compare-before / .compare-after grid items without min-width:0 so a wide <pre> forced the track past the viewport (review), and a long unbroken path in inline <code> with no wrap (understanding). The overflow was present before the v1.2.0 dark-mode change, not introduced by it.
  • Generator hardened against the same class. Grid items (.grid-cols-2 / .grid-2 / .grid-3 children and the compare blocks) now carry min-width:0 so a wide child scrolls instead of pushing the page, and inline code gets overflow-wrap:anywhere so a long path or token breaks. Verified with torture content (over-long <pre> lines in a compare grid plus a 72-character nonbreaking token) at 390px in both themes: zero overflow, layout-audit silent.

v1.2.0

Choose a tag to compare

@github-actions github-actions released this 09 Jul 16:28

Dark mode: every artifact and the gallery index now ship a built-in light/dark toggle (fixed top-right, sun/moon).

  • Follows the reader's OS by default. First open with no saved choice matches prefers-color-scheme; the toggle overrides and persists per-site in localStorage. With JavaScript off the page still follows the OS via a CSS media query and the (non-functional) toggle button stays hidden, so nothing is a dead control.
  • Degrade-safe and print-safe. The base :root stays light and renders on any engine; the contrast-verified dark palette is emitted into two screen-scoped selectors (an explicit :root[data-theme="dark"] choice and the OS media query). Wrapping the dark tokens in @media screen keeps print-to-PDF light for clean handoff. A blocking head script applies a saved theme before first paint (no flash) and uses no .innerHTML, so the no-JS content contract is untouched.
  • Dark palette. A cool, navy-biased set mirroring the light tokens one-for-one: elevation inverts (raised surfaces get lighter), the six severity triples are re-tuned for a dark ground, the code block gains a border so it does not melt into the page, and shadows drop their tint. All text pairings clear WCAG AA.
  • Mermaid diagrams pick their palette at load from the resolved theme, so a diagram matches the page it opens in; a mid-session toggle re-themes diagrams on the next reload.
  • Branding dark. A plain :root brand override still styles light only; to brand dark, append a :root[data-theme="dark"] override. SKILL.md and references/patterns.md updated: the scaffold is no longer described as light-only.

v1.1.4

Choose a tag to compare

@rhnfzl rhnfzl released this 09 Jul 12:55

Scaffold robustness: fixes so a shipped artifact cannot overflow horizontally (which fired the runtime layout-audit banner and clipped content on a phone).

  • .meta-ribbon span was white-space:nowrap, so a single long value (e.g. a wordy Status) could not wrap and pushed past a 390px phone viewport. Now white-space:normal; overflow-wrap:anywhere; short label/value pairs still sit together, only an over-long value wraps internally.
  • .bars misused as a definition list no longer blows out the page. .bars dd now carries min-width:0; overflow-wrap:anywhere so a <dt>/<dd> prose list fed into the progress grid degrades instead of forcing a max-content column.
  • New .deflist component for the label + wrapping description pattern (risks, key terms, glossary rows, a chip plus a paragraph). references/diagram-types.md and references/patterns.md steer authors to it and warn against .bars for prose.
  • New figure.diagram / .diagram-scroll / .diagram-src scaffold styles for shipping a diagram as inline SVG (self-contained under Quick Look, email, offline) with the source in an adjacent <details>.
  • Scrub two &mdash; that rendered a visible em dash in emitted output (the gallery lede and the review scaffold body).

Verified with a real headless-Chrome measurement at 390px and 1280px: the fixed scaffold has zero true overflow sources; reverting the fixes reproduces the 1491px blowout.

v1.1.3

Choose a tag to compare

@rhnfzl rhnfzl released this 09 Jul 05:57

Review fixes.

  • publish-s3.sh: refuse to overwrite an existing object at the same key unless HUMAN_HTML_S3_OVERWRITE=1 is set; stop the auth-failure retry hint from double-applying HUMAN_HTML_S3_PREFIX.
  • references/patterns.md: fix heading hierarchy and correct an accessibility note about role="region" and table semantics.
  • references/workflow-integrations.md: add the required confidence field to the Decision schema and example, sync a JSON-LD question with its rendered text, fix a dangling cross-reference.
  • Scrub remaining em dashes from the reference docs.

v1.1.2

Choose a tag to compare

@rhnfzl rhnfzl released this 08 Jul 20:30

Consistency release.

  • All nine canonical examples now share one house design system, so the gallery reads as a single consistent set. Content, metadata, comparison visuals, and the prototype's interactive sandbox are preserved; only the presentation was re-based onto the current scaffold.
  • Reconciled a number inconsistency in the architecture example.