Skip to content

v1.3.0

Choose a tag to compare

@github-actions github-actions released this 26 Jul 13:31
· 22 commits to main since this release
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 label clipping was fixed in the scaffold but never backfilled to the examples. All nine canonical examples carried live mermaid with neither half of the fix, so every shipped diagram clipped its node labels at the right edge. Both halves are now present in all nine.
  • A scroll container with no tab stop cannot be scrolled by keyboard. Two dynamic examples wrapped a wide table in overflow-x: auto with no role, no label, and no tabindex, so a keyboard reader could not reach the clipped columns at all. Fixed in both, and added to the spine as a floor requirement, since the standard wide-table treatment produces this trap by default.
  • Sixteen tab stops that did nothing. dynamic-port-correspondence.html put tabindex="0" on every <mark> region and on the numbered link inside it, doubling the tab order to 32 stops for 8 pairs with every second stop inert on activation. The highlight listens with focusin, which bubbles, so focus on the inner link already reaches the region's listener; verified in a browser that all 16 regions still light both halves with the attribute gone. The spine states the rule now, because "make it focusable" reads as "add tabindex" and that is wrong whenever the region already contains a link, a <summary>, or a control.
  • artifact-mode failed silently in two ways. An unrecognised value fell through to the full rule set with no signal, so dyanmic was indistinguishable from a validator that ignored the field; and the same field written as an element attribute rather than a meta tag was ignored entirely. Both now WARN under an artifact-mode rule, naming the value or the element, and stay held to the full rule set.
  • The spine overstated what check establishes. Its table read as though the checks proved the properties in the left column: summary-first finds the summary section anywhere in the page, so one buried above the footer passes; js-content-fallback is satisfied by any <noscript>, including an empty one about something else; table-responsive cannot tell whether a scroll container is keyboard reachable. The table now carries a third column for what each check actually proves, and the layer is described as marked rather than validated.
  • The spine cited an unearned population statistic, in the file whose own rule bans them. Replaced with the two mechanisms that actually defeat hue alone plus the WCAG clause. Also narrowed "never address a reader" to "never segment a reader": the ban is on sorting readers into groups, never on the second person.
  • Documentation that contradicted the code. SKILL.md claimed four rules always block after nav-anchors became mode-dependent; the post-init blurb promised a per-kind section skeleton for every new, which dynamic mode does not produce; Rule 1 read as though the three-bullet block were the only compliant opener while patterns.md documents BLUF as an alternative (now named as equally compliant in both); and the reading-guide snippet linked to #lead-summary, an id nothing emitted, so the scaffold carries it now.
  • Three factual defects in the dynamic examples. dynamic-port-correspondence.html claimed the TypeScript port "clamps downward" an over-capacity bucket; it clamps the headroom, so a downgrade from 600 to 100 with 600 tokens held leaves 600 in place and the tier keeps serving the old allowance until spent. dynamic-design-directions.html recorded a 502 window ending 09:18:00 and then evidenced nine 502 responses after it, and its lead implied direction 3 had the lowest outage cost while its own table gave direction 4 fewer losses. dynamic-blindspot-pass.html gave a rebuild duration as both "40 minutes" and "5h40m", and miscounted an archive-to-flip interval by a day.
  • A <noscript> mentioned inside an HTML comment corrupted the --no-js rewrite. The unwrap regex treated the mention as a real opening tag, so the non-greedy match ran to the real closing tag and deleted the page between them. Comments are stripped first, guarded by a test that fails against the pre-fix transform.
  • tests/ grew from nothing to 32 tests, covering the DOM-insertion regex, the mode branch in both directions, the suppression alias, the no-JS rewrite, and contract properties across all twelve shipped examples. Each new test fails against the tree it was written for.