From 8b54dae750fa5b2e5ea698d7d64a8721cff9cb16 Mon Sep 17 00:00:00 2001 From: Utkarsh Sengar Date: Sat, 22 Aug 2026 12:22:39 -0700 Subject: [PATCH 1/2] patterns: vendor session-explainer for offline init MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Mirrors the new official pattern from the Worker repo so `htmlbin patterns init` installs it when the catalog is unreachable. src/patterns/bundled-data.ts is generated from patterns/*.md by scripts/build-bundled-patterns.mjs on prebuild/pretypecheck/pretest and is gitignored, so vendoring the markdown is the whole change. Also updates the htmlbin-publish skill, which names the bundled set explicitly and would otherwise go stale. Verified: file is byte-identical to the Worker repo's copy (SHA-256 ad4b0636…), and `patterns init` installs it correctly from both the live catalog and the offline bundled fallback, producing the same hash in all three places. Full suite green (162 passed, 6 skipped). Companion Worker PR: utsengar/htmlbin#41 Co-Authored-By: Claude Opus 5 (1M context) --- patterns/session-explainer.md | 76 +++++++++++++++++++++++++++++++++ skills/htmlbin-publish/SKILL.md | 2 +- 2 files changed, 77 insertions(+), 1 deletion(-) create mode 100644 patterns/session-explainer.md 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/skills/htmlbin-publish/SKILL.md b/skills/htmlbin-publish/SKILL.md index 8d64d99..2bad51f 100644 --- a/skills/htmlbin-publish/SKILL.md +++ b/skills/htmlbin-publish/SKILL.md @@ -39,7 +39,7 @@ If no patterns are listed: htmlbin patterns init This installs the official pattern catalog locally. The bundled set today is -`summary-roundup`, `pr-explainer`, `plan-spec-explainer`. +`summary-roundup`, `pr-explainer`, `plan-spec-explainer`, `session-explainer`. ### 3. Match the task to a pattern From 63cd40f4d06ed9f10a8a04a2294a6080a153c7f1 Mon Sep 17 00:00:00 2001 From: Utkarsh Sengar Date: Sat, 22 Aug 2026 20:33:06 -0700 Subject: [PATCH 2/2] patterns: vendor prescriptive skeletons so offline init installs them MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit session-explainer is prescriptive: it mandates one structure and ships a reference skeleton rather than describing a layout in prose. The bundle globbed patterns/*.md only, so an offline `patterns init` installed the pattern without the structure it points at. The skeleton now travels with the pattern on every path. - scripts/build-bundled-patterns.mjs picks up a .template.html sibling and emits it as `template: { filename, body }`. - init writes it from the bundle offline, and fetches it from the catalog online. The pattern's own front matter decides whether to look, so the three patterns without a skeleton cost no extra request. - `patterns add ` gets it too, but only for catalog sources — a file/gist/github source has nowhere well-defined to look, and guessing would mean a speculative fetch. - schema.ts learns two optional keys: `brand_scope` ("all" | "colors-only") and `template`. Two things worth calling out. The skeleton is nested on its pattern's InstallResult rather than pushed into `installed` as a sibling. First pass pushed it, which broke an existing assertion (`installed` had an entry whose name already carried an extension) and, worse, made installed.length stop meaning "patterns installed". Nesting keeps that invariant and matches what `add` returns. The destination filename is derived from the validated pattern name, never from the catalog. A catalog entry is remote input, and `template: ../../../etc/passwd` would otherwise be a write outside the patterns dir. schema.ts pins the value to `.template.html` and there are tests for the traversal cases. Verified: 171 tests pass (was 162). init installs the skeleton from both the live catalog and the bundled fallback, byte-identical to the worker's copy by SHA-256, idempotent on a second run, and `patterns list` still reports 4 patterns rather than counting the skeleton as a fifth. Companion worker PR: utsengar/htmlbin#41 Co-Authored-By: Claude Opus 5 (1M context) --- patterns/session-explainer.md | 108 ++++--- patterns/session-explainer.template.html | 349 +++++++++++++++++++++++ scripts/build-bundled-patterns.mjs | 47 ++- src/bin.ts | 15 +- src/patterns/catalog.ts | 2 + src/patterns/init.ts | 29 +- src/patterns/install.ts | 57 +++- src/patterns/paths.ts | 9 + src/patterns/schema.ts | 54 ++++ test/e2e/patterns.test.ts | 37 ++- test/patterns/schema.test.ts | 69 +++++ 11 files changed, 728 insertions(+), 48 deletions(-) create mode 100644 patterns/session-explainer.template.html diff --git a/patterns/session-explainer.md b/patterns/session-explainer.md index 6531b24..21df999 100644 --- a/patterns/session-explainer.md +++ b/patterns/session-explainer.md @@ -9,6 +9,8 @@ triggers: - publish my agent session - show my thinking on this brand_sensing: true +brand_scope: colors-only +template: session-explainer.template.html --- # Session explainer @@ -21,56 +23,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 | +| `