From de026ec0d760057aafefab464bf7d0ca303522b3 Mon Sep 17 00:00:00 2001 From: Utkarsh Sengar Date: Sat, 22 Aug 2026 12:20:11 -0700 Subject: [PATCH 1/4] =?UTF-8?q?patterns:=20add=20session-explainer=20?= =?UTF-8?q?=E2=80=94=20publish=20an=20agent=20session=20as=20a=20narrative?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Engineers have started sharing agent session traces to show how they thought about a problem, not just what shipped. YC's Spring 2026 application asks for one; interviews are following. "Sharing a session" turns out to be three products with contradictory requirements, split on whether curation helps or hurts: - Evidence ("did you really think this?") wants raw and unedited. - Narrative ("how should I think about this?") wants curated. - Craft transfer ("how do good people use agents?") wants an index. This pattern serves only the middle one, and the other two are explicit non-goals. Evidence needs 10MB+ and tamper-evidence; measured across 1,293 real transcripts locally, the distribution is bimodal — a cluster at ~150KB and a tail from 6MB to 21MB, with the shareable long sessions landing 3-10x over MAX_HTML_BYTES. Curation is arithmetic here, not taste, which is why this is a pattern and not a platform change. Craft transfer needs a browse UI, which hard rule #4 forbids. Every existing tool in this space (Lore, simonw/claude-code-transcripts, Mantra, and assorted local viewers) is a mechanical transcoder: JSONL in, faithful HTML out. None authors a narrative, and none redacts. That gap is the one thing an agent that just did the work is uniquely placed to fill, so the pattern carries a redaction floor — secrets, home-dir paths, unreviewed excerpts, passcode-by-default for internal, rotate anything the session touched. Trace-UX conventions borrowed from Braintrust/Langfuse/LangSmith where they survive a static self-contained page (two altitudes, anchored steps, one scrollable Ctrl-F-able body) and rejected where they don't (latency waterfalls, token/cost telemetry, span trees — debugging affordances for the wrong audience). Content only. No schema change, no new endpoint, no route work; the existing /.well-known/patterns/:filename handler picks it up. - patterns/session-explainer.md — source of truth - src/patterns.ts — byte-for-byte mirror + PatternMeta entry (verified by SHA-256 against the served body) - scripts/agent-e2e.sh — count 3 -> 4, add the name to the assertion loop - skill + CLAUDE.md references Co-Authored-By: Claude Opus 5 (1M context) --- CLAUDE.md | 1 + patterns/session-explainer.md | 76 ++++++++++++++++++++++++++++ scripts/agent-e2e.sh | 4 +- skills/htmlbin/SKILL.md | 2 +- src/patterns.ts | 94 +++++++++++++++++++++++++++++++++++ src/skill.ts | 2 +- 6 files changed, 175 insertions(+), 4 deletions(-) create mode 100644 patterns/session-explainer.md diff --git a/CLAUDE.md b/CLAUDE.md index 74f31a7..d99719a 100644 --- a/CLAUDE.md +++ b/CLAUDE.md @@ -822,6 +822,7 @@ patterns/ ─ human-browsable canonical pattern markdown — source of pr-explainer.md ─ "explain this PR / summarize this diff" summary-roundup.md ─ discussion summaries, weekly status, incident timelines plan-spec-explainer.md─ plan.md / spec.md publishing + session-explainer.md ─ agent session writeups — problem, approach, dead ends # Mirrored byte-for-byte into src/patterns.ts (wrangler .md-import gotcha). .github/workflows/ diff --git a/patterns/session-explainer.md b/patterns/session-explainer.md new file mode 100644 index 0000000..6531b24 --- /dev/null +++ b/patterns/session-explainer.md @@ -0,0 +1,76 @@ +--- +name: session-explainer +description: Explain an agent session — the problem, the approach, the dead ends, and what the thinking actually was. +triggers: + - publish this session + - share this trace + - share my claude code session + - make a page from this transcript + - publish my agent session + - show my thinking on this +brand_sensing: true +--- + +# Session explainer + +## When to use + +When the source is an agent session — a transcript of work with a coding agent. The drop's job is to explain **how the author thought about a problem**, not to replay the conversation. The reader wants the reasoning; the transcript is raw material, not the deliverable. + +**When not to use.** If the audience needs unedited proof that the work happened — a grant or accelerator application, an interview artifact, an audit — this pattern is the wrong tool and so is a drop. Those readers want the original file precisely because nobody curated it, and curation is this pattern's whole point. Say so and hand them the raw transcript instead. + +Sessions worth publishing also tend to be the long ones, and a long transcript does not fit in a drop. Distilling isn't a stylistic preference here; it's the only thing that fits. + +## Content checklist + +- Title + a one-line statement of the problem +- Meta line: agent/tool, date, rough session length +- **The problem** — what was actually being solved, in the author's framing, before any solution +- **The approach** — and why that one, not the obvious alternative +- **Dead ends** — what was tried and abandoned, and what killed it. This is the highest-signal part of any session and the first thing a mechanical transcript renderer throws away. Don't skip it because it looks like failure; it's the reason the page is worth reading. +- Turning points, each with a short excerpt and a stable `id` anchor so a reader can link to one +- What shipped, with a link +- What the author would do differently + +## Prose floor + +Every drop is read by a human. Write like one wrote it: + +- Name the actor — "the team decided", not "a decision was made" +- Cut throat-clearing openers ("Here's the thing:", "Let me be clear") — start with the point +- Delete emphasis crutches: "Full stop.", "Let that sink in.", adverbs like "really" / "literally" +- Skip binary-contrast drama ("Not because X. Because Y.") — just say Y +- Vary sentence length — three short staccato fragments in a row reads as manufactured urgency + +## Redaction floor + +Session transcripts are the single most credential-dense artifact on a developer's machine. Live API keys have been found in published session logs more than once. Every excerpt you include has to clear this list: + +- **Scan for secrets.** API keys, bearer tokens, `.env` contents, database URLs, connection strings, signed URLs, private hostnames. High-entropy strings are guilty until proven innocent. +- **Strip absolute home paths.** `/Users//…` and `/home//…` leak identity and local layout. Rewrite as a repo-relative path. +- **Never publish an excerpt the human hasn't seen.** Sessions routinely contain customer names, unshipped work, internal URLs, and third-party code. Show them what you're including before it goes out. +- **Default to a passcode for anything team-internal** (`POST /api/drops/:slug/passcode`). Be honest with the human about what it is: a share gate, not encryption. The HTML is stored unencrypted. +- **If the session touched credentials at all, say so** and tell the human to rotate them — whether or not the value made it into the drop. The agent read them; that's enough. + +## Layout directions + +1. **Annotated walkthrough** — the default. Narrative prose carrying the story, with short collapsed `
` excerpts anchored at each turning point. Reads top to bottom. +2. **Decision log** — sessions whose value is a sequence of choices. One block per decision: what was considered, what was picked, why. Skimmable by decision, not by time. +3. **Problem → dead ends → resolution** — debugging sessions. Front-load the symptom, walk the failed hypotheses in order with what disproved each, land on the root cause. + +## How to pick + +Shape of the session, not its length: + +- Exploration or a build with a clear arc → **annotated walkthrough** +- Several independent judgment calls → **decision log** +- One symptom chased to a root cause → **problem → dead ends → resolution** + +## Don't + +- Dump the raw transcript. It won't fit, and a wall of turns isn't a thing anyone reads. +- Include latency, token counts, or cost per step. Those are pipeline-debugging numbers borrowed from observability tools; this reader is here for the thinking. Shipping them reads as a pasted dashboard. +- Render a span tree or a nested waterfall. A coding session is essentially linear — a tree adds chrome and no meaning. +- Claim the page is complete, unedited, or tamper-evident. It's a curated account. If that distinction matters to the reader, point them at the source file. +- Sanitize the author's voice, including the parts where they were wrong. The wrong turns are the content. +- Quote a teammate, a customer, or a third party from the transcript without the human's explicit OK. diff --git a/scripts/agent-e2e.sh b/scripts/agent-e2e.sh index e0bb275..50b6a42 100755 --- a/scripts/agent-e2e.sh +++ b/scripts/agent-e2e.sh @@ -79,12 +79,12 @@ curl -s "$BASE/.well-known/patterns/index.json" -o "$TMP/patterns.json" jq -e . < "$TMP/patterns.json" > /dev/null \ && ok "patterns/index.json is valid JSON" \ || fail "patterns/index.json valid" "parse error" -assert_json "$TMP/patterns.json" '.patterns | length' '3' "manifest lists three starter patterns" +assert_json "$TMP/patterns.json" '.patterns | length' '4' "manifest lists four starter patterns" CT_PJ=$(curl -s -o /dev/null -w "%{content_type}" "$BASE/.well-known/patterns/index.json") assert_contains "$CT_PJ" "application/json" "patterns/index.json served as application/json" -for name in pr-explainer summary-roundup plan-spec-explainer; do +for name in pr-explainer summary-roundup plan-spec-explainer session-explainer; do assert_json "$TMP/patterns.json" \ "[.patterns[] | select(.name==\"$name\")] | length" '1' \ "manifest includes $name" diff --git a/skills/htmlbin/SKILL.md b/skills/htmlbin/SKILL.md index 5d00e99..db68a3a 100644 --- a/skills/htmlbin/SKILL.md +++ b/skills/htmlbin/SKILL.md @@ -525,7 +525,7 @@ Drops should look like they belong to the human publishing them, not like a gene ## Patterns — local first, official as fallback -Common drop kinds (PR explainers, summary roundups, plan/spec writeups, …) ship as small markdown files anyone can author. Each pattern names triggers, a content checklist, layout directions, and a "don't" list. Read patterns to decide *structure*; use brand sensing (above) to decide *look*. +Common drop kinds (PR explainers, summary roundups, plan/spec writeups, session explainers, …) ship as small markdown files anyone can author. Each pattern names triggers, a content checklist, layout directions, and a "don't" list. Read patterns to decide *structure*; use brand sensing (above) to decide *look*. **Where patterns live.** Resolve in this order — first match wins per pattern name: diff --git a/src/patterns.ts b/src/patterns.ts index a78916c..b77beab 100644 --- a/src/patterns.ts +++ b/src/patterns.ts @@ -205,6 +205,84 @@ Length + structural shape of the source: - Include rationale that references internal incidents, customers, or people without the human's explicit OK. Plans often have sensitive context — ask before publishing it. `; +const SESSION_EXPLAINER_MD = `--- +name: session-explainer +description: Explain an agent session — the problem, the approach, the dead ends, and what the thinking actually was. +triggers: + - publish this session + - share this trace + - share my claude code session + - make a page from this transcript + - publish my agent session + - show my thinking on this +brand_sensing: true +--- + +# Session explainer + +## When to use + +When the source is an agent session — a transcript of work with a coding agent. The drop's job is to explain **how the author thought about a problem**, not to replay the conversation. The reader wants the reasoning; the transcript is raw material, not the deliverable. + +**When not to use.** If the audience needs unedited proof that the work happened — a grant or accelerator application, an interview artifact, an audit — this pattern is the wrong tool and so is a drop. Those readers want the original file precisely because nobody curated it, and curation is this pattern's whole point. Say so and hand them the raw transcript instead. + +Sessions worth publishing also tend to be the long ones, and a long transcript does not fit in a drop. Distilling isn't a stylistic preference here; it's the only thing that fits. + +## Content checklist + +- Title + a one-line statement of the problem +- Meta line: agent/tool, date, rough session length +- **The problem** — what was actually being solved, in the author's framing, before any solution +- **The approach** — and why that one, not the obvious alternative +- **Dead ends** — what was tried and abandoned, and what killed it. This is the highest-signal part of any session and the first thing a mechanical transcript renderer throws away. Don't skip it because it looks like failure; it's the reason the page is worth reading. +- Turning points, each with a short excerpt and a stable \`id\` anchor so a reader can link to one +- What shipped, with a link +- What the author would do differently + +## Prose floor + +Every drop is read by a human. Write like one wrote it: + +- Name the actor — "the team decided", not "a decision was made" +- Cut throat-clearing openers ("Here's the thing:", "Let me be clear") — start with the point +- Delete emphasis crutches: "Full stop.", "Let that sink in.", adverbs like "really" / "literally" +- Skip binary-contrast drama ("Not because X. Because Y.") — just say Y +- Vary sentence length — three short staccato fragments in a row reads as manufactured urgency + +## Redaction floor + +Session transcripts are the single most credential-dense artifact on a developer's machine. Live API keys have been found in published session logs more than once. Every excerpt you include has to clear this list: + +- **Scan for secrets.** API keys, bearer tokens, \`.env\` contents, database URLs, connection strings, signed URLs, private hostnames. High-entropy strings are guilty until proven innocent. +- **Strip absolute home paths.** \`/Users//…\` and \`/home//…\` leak identity and local layout. Rewrite as a repo-relative path. +- **Never publish an excerpt the human hasn't seen.** Sessions routinely contain customer names, unshipped work, internal URLs, and third-party code. Show them what you're including before it goes out. +- **Default to a passcode for anything team-internal** (\`POST /api/drops/:slug/passcode\`). Be honest with the human about what it is: a share gate, not encryption. The HTML is stored unencrypted. +- **If the session touched credentials at all, say so** and tell the human to rotate them — whether or not the value made it into the drop. The agent read them; that's enough. + +## Layout directions + +1. **Annotated walkthrough** — the default. Narrative prose carrying the story, with short collapsed \`
\` excerpts anchored at each turning point. Reads top to bottom. +2. **Decision log** — sessions whose value is a sequence of choices. One block per decision: what was considered, what was picked, why. Skimmable by decision, not by time. +3. **Problem → dead ends → resolution** — debugging sessions. Front-load the symptom, walk the failed hypotheses in order with what disproved each, land on the root cause. + +## How to pick + +Shape of the session, not its length: + +- Exploration or a build with a clear arc → **annotated walkthrough** +- Several independent judgment calls → **decision log** +- One symptom chased to a root cause → **problem → dead ends → resolution** + +## Don't + +- Dump the raw transcript. It won't fit, and a wall of turns isn't a thing anyone reads. +- Include latency, token counts, or cost per step. Those are pipeline-debugging numbers borrowed from observability tools; this reader is here for the thinking. Shipping them reads as a pasted dashboard. +- Render a span tree or a nested waterfall. A coding session is essentially linear — a tree adds chrome and no meaning. +- Claim the page is complete, unedited, or tamper-evident. It's a curated account. If that distinction matters to the reader, point them at the source file. +- Sanitize the author's voice, including the parts where they were wrong. The wrong turns are the content. +- Quote a teammate, a customer, or a third party from the transcript without the human's explicit OK. +`; + type PatternMeta = { name: string; description: string; @@ -259,6 +337,22 @@ const PATTERNS: ReadonlyArray<{ meta: PatternMeta; md: string }> = [ }, md: PLAN_SPEC_EXPLAINER_MD, }, + { + meta: { + name: "session-explainer", + description: + "Explain an agent session — the problem, the approach, the dead ends, and what the thinking actually was.", + triggers: [ + "publish this session", + "share this trace", + "share my claude code session", + "make a page from this transcript", + "publish my agent session", + "show my thinking on this", + ], + }, + md: SESSION_EXPLAINER_MD, + }, ]; export type PatternIndex = { diff --git a/src/skill.ts b/src/skill.ts index e201355..5eec4a4 100644 --- a/src/skill.ts +++ b/src/skill.ts @@ -524,7 +524,7 @@ Drops should look like they belong to the human publishing them, not like a gene ## Patterns — local first, official as fallback -Common drop kinds (PR explainers, summary roundups, plan/spec writeups, …) ship as small markdown files anyone can author. Each pattern names triggers, a content checklist, layout directions, and a "don't" list. Read patterns to decide *structure*; use brand sensing (above) to decide *look*. +Common drop kinds (PR explainers, summary roundups, plan/spec writeups, session explainers, …) ship as small markdown files anyone can author. Each pattern names triggers, a content checklist, layout directions, and a "don't" list. Read patterns to decide *structure*; use brand sensing (above) to decide *look*. **Where patterns live.** Resolve in this order — first match wins per pattern name: From bbeffc92ccc634b8e1a1fc9d23123bae2d897731 Mon Sep 17 00:00:00 2001 From: Utkarsh Sengar Date: Sat, 22 Aug 2026 12:42:37 -0700 Subject: [PATCH 2/4] skill: name the official catalog, and assert it in e2e MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The previous commit added "session explainers" to a parenthetical in the Patterns section, which was effectively invisible — the comprehensive skill never enumerated the catalog at all, so no pattern was named as an actual entry. An agent that reads SKILL.md without fetching index.json had no way to learn session-explainer exists. Two inconsistencies made this worth fixing rather than defending as by-design: the CLI's htmlbin-publish skill already enumerates its bundled set, and CLAUDE.md describes this skill as the comprehensive reference. Adds a one-paragraph catalog listing after the resolution order, naming all four patterns with their one-line job, and points at index.json as authoritative so the list can't rot into a lie as the catalog grows. Also adds an e2e guard: every name in patterns/index.json must appear in SKILL.md. Driven off the catalog rather than hardcoded, so a future pattern that lands in the catalog and never reaches the skill fails the suite instead of shipping invisible. Both src/skill.ts and skills/htmlbin/SKILL.md updated; the new block is byte-identical in each (verified against the served body). Verified: tsc clean, all 136 e2e checks pass (was 132). Co-Authored-By: Claude Opus 5 (1M context) --- scripts/agent-e2e.sh | 10 ++++++++++ skills/htmlbin/SKILL.md | 2 ++ src/skill.ts | 2 ++ 3 files changed, 14 insertions(+) diff --git a/scripts/agent-e2e.sh b/scripts/agent-e2e.sh index 50b6a42..107dd8f 100755 --- a/scripts/agent-e2e.sh +++ b/scripts/agent-e2e.sh @@ -123,6 +123,16 @@ grep -q "\\./\\.htmlbin/patterns/" "$TMP/skill.md" \ grep -q "~/.config/htmlbin/patterns/" "$TMP/skill.md" \ && ok "SKILL.md documents the machine-global patterns path" \ || fail "skill global path" "missing" + +# Every catalog pattern must be named in SKILL.md. The skill teaches the +# convention, but an agent that skims it without fetching index.json still +# needs to know what's on offer — a new pattern that lands in the catalog +# and never reaches the skill is invisible to those agents. +for name in $(jq -r '.patterns[].name' < "$TMP/patterns.json"); do + grep -q "\`$name\`" "$TMP/skill.md" \ + && ok "SKILL.md names $name" \ + || fail "skill names $name" "not in SKILL.md" +done grep -q "^## Quality floor" "$TMP/skill.md" \ && ok "SKILL.md documents the quality floor" \ || fail "skill quality floor" "missing" diff --git a/skills/htmlbin/SKILL.md b/skills/htmlbin/SKILL.md index db68a3a..ca9c5cc 100644 --- a/skills/htmlbin/SKILL.md +++ b/skills/htmlbin/SKILL.md @@ -534,6 +534,8 @@ Common drop kinds (PR explainers, summary roundups, plan/spec writeups, session 3. **Official catalog** — fetch from `https://htmlbin.dev/.well-known/patterns/index.json` for the list, or `https://htmlbin.dev/.well-known/patterns/.md` for a specific one. Cache once per session. 4. **No pattern at all** — freestyle within the quality floor. Always valid; patterns are starting floors, not requirements. +**The official catalog today** — `pr-explainer` (a pull request, merge, or diff), `summary-roundup` (discussion threads, weekly status, incident timelines), `plan-spec-explainer` (a plan, spec, or design document), `session-explainer` (an agent session — the problem, the approach, the dead ends). Treat `index.json` as authoritative rather than this list: the catalog can grow between skill revisions. + **Pattern file schema.** YAML front matter + markdown body. Authors write these in any text editor; no tooling required. ```markdown diff --git a/src/skill.ts b/src/skill.ts index 5eec4a4..4e11da2 100644 --- a/src/skill.ts +++ b/src/skill.ts @@ -533,6 +533,8 @@ Common drop kinds (PR explainers, summary roundups, plan/spec writeups, session 3. **Official catalog** — fetch from \`https://htmlbin.dev/.well-known/patterns/index.json\` for the list, or \`https://htmlbin.dev/.well-known/patterns/.md\` for a specific one. Cache once per session. 4. **No pattern at all** — freestyle within the quality floor. Always valid; patterns are starting floors, not requirements. +**The official catalog today** — \`pr-explainer\` (a pull request, merge, or diff), \`summary-roundup\` (discussion threads, weekly status, incident timelines), \`plan-spec-explainer\` (a plan, spec, or design document), \`session-explainer\` (an agent session — the problem, the approach, the dead ends). Treat \`index.json\` as authoritative rather than this list: the catalog can grow between skill revisions. + **Pattern file schema.** YAML front matter + markdown body. Authors write these in any text editor; no tooling required. \`\`\`markdown From 35da5f9e72fdf7fef21442754c0397e8ca9c3342 Mon Sep 17 00:00:00 2001 From: Utkarsh Sengar Date: Sat, 22 Aug 2026 15:36:57 -0700 Subject: [PATCH 3/4] patterns: make session-explainer prescriptive, with a required skeleton MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The pattern described a layout in prose and offered three options, which is how the other patterns work. That is too loose for this one: these pages are only worth comparing to each other if they share a shape, and prose guidance produces a different page every time. Structure is now fixed; palette and type still adapt. Three mechanics carry it: - patterns/session-explainer.template.html — a working skeleton with SLOT_* placeholders. Its CSS is split by a marked comment into a BRAND TOKENS block the agent adapts and a STRUCTURE block it must not touch. An agent filling a skeleton drifts far less than one authoring from a description. - brand_sensing: colors-only, replacing true. A prescriptive layout and full brand sensing contradict each other, so the pattern says which half wins: adapt the palette, leave the structure. - A conformance checklist of assertions the agent can actually run (rail present, both panels exist, zero script tags, every deep link resolves, no overflow at 360px, no absolute home paths). Serving: getPatternAsset() replaces getPatternMd() at the route and returns body + content-type, so .md goes out as text/markdown and .template.html as text/html. buildPatternIndex() adds template_url for any pattern declaring one, so agents find it from the index rather than guessing the URL. PatternMeta gains an optional template field. Also rewrote the pattern body around what a reader learns. The insights block now holds transferable lessons with a 150-word cap, and says explicitly that a fact about the artifact (file size, PR count) is not a lesson. Hard numeric limits replace the soft "content checklist". Enforcement stops here deliberately. The server does not validate published HTML — that would break "HTML uploads exactly as posted" — and ./.htmlbin/patterns/ still overrides the catalog. Template plus a self-checkable floor is the ceiling, and the pattern says so. One thing testing the skeleton against its own checklist surfaced: a hidden tab panel reports zero-size elements, so overflow and tap-target checks silently pass for whichever tab is inactive. The checklist now tells the agent to run the layout checks once per tab. Same failure mode as verifying with a string count. Verified: tsc clean, all 146 e2e checks pass (was 136, +10 for the template), served .md and .template.html both byte-identical to patterns/ by SHA-256. The CLI's offline bundle globs *.md only, so the template is not vendored; an offline init falls back to the pattern's prose. Noted in CLAUDE.md as a follow-up rather than fixed here. Co-Authored-By: Claude Opus 5 (1M context) --- CLAUDE.md | 30 ++ patterns/session-explainer.md | 109 +++-- patterns/session-explainer.template.html | 349 ++++++++++++++++ scripts/agent-e2e.sh | 27 ++ skills/htmlbin/SKILL.md | 2 + src/index.ts | 12 +- src/patterns.ts | 489 +++++++++++++++++++++-- src/skill.ts | 2 + 8 files changed, 946 insertions(+), 74 deletions(-) create mode 100644 patterns/session-explainer.template.html diff --git a/CLAUDE.md b/CLAUDE.md index d99719a..61554f2 100644 --- a/CLAUDE.md +++ b/CLAUDE.md @@ -514,6 +514,35 @@ mirror its content into `src/patterns.ts`'s `PATTERNS` array (with a No DB migration, no schema change, no new endpoint — the existing `/.well-known/patterns/:filename` route picks it up automatically. +**Prescriptive patterns (structure fixed, not a menu).** Most patterns +offer 3–5 layout directions and let the agent pick. `session-explainer` +does not: it mandates one structure, because these pages are only worth +comparing to each other if they share a shape. Two mechanics support that: + +- **A reference skeleton** at `patterns/.template.html`, mirrored + into `src/patterns.ts` as a second string constant and referenced from + the pattern's front matter (`template: .template.html`). Served + by the same route via `getPatternAsset()`, which returns body + + content-type so `.md` goes out as `text/markdown` and `.template.html` + as `text/html`. `buildPatternIndex()` adds `template_url` for any + pattern that declares one, so agents discover it from the index. + The template's CSS is split by a marked comment: a **BRAND TOKENS** + block the agent adapts, and a **STRUCTURE** block it must not touch. +- **`brand_sensing: colors-only`** instead of `true` — adapt palette and + type, leave the structure alone. A prescriptive pattern and full brand + sensing contradict each other; pick one per pattern. + +Enforcement tops out there on purpose. The server does **not** validate +published HTML (that would break "HTML uploads exactly as posted"), and +`./.htmlbin/patterns/` still overrides the official catalog. The pattern +carries a conformance checklist for the agent to self-check instead. + +The CLI's offline bundle globs `patterns/*.md` only, so templates are +**not** vendored — an offline `patterns init` gets the pattern's prose +fallback ("build the same structure from the requirements below"). If +that becomes a real gap, teaching `scripts/build-bundled-patterns.mjs` +and the CLI's install path about a second file per pattern is the fix. + **The CLI's `patterns` subcommand** ([utsengar/htmlbin-cli](https://github.com/utsengar/htmlbin-cli)) offers `list / init / add` for managing local installs. The skill's @@ -823,6 +852,7 @@ patterns/ ─ human-browsable canonical pattern markdown — source of summary-roundup.md ─ discussion summaries, weekly status, incident timelines plan-spec-explainer.md─ plan.md / spec.md publishing session-explainer.md ─ agent session writeups — problem, approach, dead ends + session-explainer.template.html ─ required skeleton for the above (prescriptive) # Mirrored byte-for-byte into src/patterns.ts (wrangler .md-import gotcha). .github/workflows/ diff --git a/patterns/session-explainer.md b/patterns/session-explainer.md index 6531b24..fc16b2f 100644 --- a/patterns/session-explainer.md +++ b/patterns/session-explainer.md @@ -8,7 +8,8 @@ triggers: - make a page from this transcript - publish my agent session - show my thinking on this -brand_sensing: true +brand_sensing: colors-only +template: session-explainer.template.html --- # Session explainer @@ -21,56 +22,96 @@ When the source is an agent session — a transcript of work with a coding agent Sessions worth publishing also tend to be the long ones, and a long transcript does not fit in a drop. Distilling isn't a stylistic preference here; it's the only thing that fits. -## Content checklist +## Start from the template, don't invent a layout -- Title + a one-line statement of the problem -- Meta line: agent/tool, date, rough session length -- **The problem** — what was actually being solved, in the author's framing, before any solution -- **The approach** — and why that one, not the obvious alternative -- **Dead ends** — what was tried and abandoned, and what killed it. This is the highest-signal part of any session and the first thing a mechanical transcript renderer throws away. Don't skip it because it looks like failure; it's the reason the page is worth reading. -- Turning points, each with a short excerpt and a stable `id` anchor so a reader can link to one -- What shipped, with a link -- What the author would do differently +This pattern is **prescriptive**. Unlike the other official patterns it does not offer layout choices, because the structure is what makes these pages comparable to each other. -## Prose floor + curl -s https://htmlbin.dev/.well-known/patterns/session-explainer.template.html -Every drop is read by a human. Write like one wrote it: +Fill every `SLOT_*` placeholder and each `` region. The template's CSS is split by a marked line: adapt the **BRAND TOKENS** block, treat everything under **STRUCTURE** as fixed. -- Name the actor — "the team decided", not "a decision was made" -- Cut throat-clearing openers ("Here's the thing:", "Let me be clear") — start with the point -- Delete emphasis crutches: "Full stop.", "Let that sink in.", adverbs like "really" / "literally" -- Skip binary-contrast drama ("Not because X. Because Y.") — just say Y -- Vary sentence length — three short staccato fragments in a row reads as manufactured urgency +If you cannot fetch the template, build the same structure from the requirements below. Do not substitute a different one. + +## Required structure + +All five are mandatory. A page missing any of them is not this pattern. + +1. **A sticky left rail** (`.rail`) holding the session's numbers and context. It stays on screen while the content column scrolls, so the reader keeps the session's identity while reading any one part. +2. **Two tabs** — `Highlights` (`#p1`, default) and `Dead ends` (`#p2`). Driven by `:target` and `:has()`, never JavaScript. +3. **A compressed timeline** in Highlights. One line per step. This is the index to the whole session. +4. **One card per dead end** in the second tab, `id="dead-N"`. +5. **Deep links from timeline to cards.** A timeline row that has a card is an `` whose gutter reads `dead-N →`. Clicking it switches tab, scrolls to the card, and rings it. Rows without a card stay a `
` and get a plain gutter label. + +## Numbers must be measured + +Every value in the rail comes from the transcript. Count it; never estimate, never round for effect. **Omit a tile rather than guess** — a wrong number is worse than a missing one. Mark at most one tile `.hot`, and only when it carries the page's central constraint. + +Required in the context block: `agent`, `model`. Add `window`, `repo`, `outcome` when known. + +## Hard limits + +| Thing | Limit | +|---|---| +| Insights | 2–4 items, **150 words total** | +| Timeline rows | 8–16, one line each | +| Dead-end cards | 2–6 | +| Lede | 2 sentences | +| `