patterns: add session-explainer — publish an agent session as a narrative - #41
Merged
Conversation
This was referenced Aug 22, 2026
…tive
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) <noreply@anthropic.com>
utsengar
force-pushed
the
session-explainer-pattern
branch
from
August 22, 2026 19:32
8d45474 to
de026ec
Compare
🚀 Cloudflare previewURL: https://72f75b95-htmlbin.utkarsh2012.workers.dev Built from commit
|
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) <noreply@anthropic.com>
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) <noreply@anthropic.com>
`brand_sensing: colors-only` broke the CLI. Its validator requires that field to be a boolean and throws on anything else, and because `patterns init` loops every catalog entry inside one try/catch, the throw dropped the whole run into the bundled fallback. Every published CLI would have quietly installed 3 patterns and reported offline: true against a catalog that was serving 4. Reproduced it end to end before fixing: init against a local worker returned offline: true with session-explainer missing. Unknown front-matter keys are ignored by the existing validator, so the scope moves to an additive `brand_scope: colors-only` alongside `brand_sensing: true`. Old CLIs skip it, new ones read it, and the catalog stays installable either way. Same reason `template` is a new key rather than an extension of an existing one. CLAUDE.md now records the constraint, since it applies to any future pattern metadata: keep it additive, and never change the type of a field an older validator already checks. Verified: tsc clean, 146 e2e checks pass, and init against the local worker returns offline: false with all 4 patterns. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
utsengar
added a commit
to utsengar/htmlbin-cli
that referenced
this pull request
Aug 23, 2026
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 <name>.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 <name>` 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 `<name>.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) <noreply@anthropic.com>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
What
Adds a fourth official pattern,
session-explainer: publishing an agent session as a narrative — the problem, the approach, the dead ends, what shipped.Content only. No schema change, no new endpoint, no route work — the existing
/.well-known/patterns/:filenamehandler picks it up.Why
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 ("attach a coding agent session you're particularly proud of", 10 MB cap); interviews are following.
"Sharing a session" turns out to be three products with contradictory requirements, split on whether curation helps or hurts:
This pattern serves only the middle one. The other two are explicit non-goals:
MAX_HTML_BYTES, before HTML inflation. Curation is arithmetic here, not taste — which is exactly why this is a pattern and not a platform change. YC also accepts 10 MB direct uploads, so the need is already met.Positioning vs prior art
Rendering a Claude Code session is commoditized — Lore,
simonw/claude-code-transcripts, Mantra,claude-devtools, and assorted local viewers. We are deliberately not building a trace viewer (hard rule #1 applies directly to Lore).But every one of those 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 scan, home-dir path stripping, no unreviewed excerpts, passcode-by-default for internal, and rotate anything the session touched.
Trace UX
Borrowed from Braintrust / Langfuse / LangSmith where the convention survives a static self-contained page — two altitudes (narrative + collapsed
<details>), anchored linkable steps, one scrollable Ctrl-F-able body. Rejected where it doesn't: latency waterfalls, token/cost telemetry, span trees. Those are pipeline-debugging affordances for the wrong audience.Files
patterns/session-explainer.md— source of truthsrc/patterns.ts— byte-for-byte mirror +PatternMetaentryscripts/agent-e2e.sh— count 3 → 4, added the name to the assertion loopCLAUDE.mdreferencesCompanion CLI PR (vendored pattern +
htmlbin-publishskill list): utsengar/htmlbin-cli#13Verification
npx tsc --noEmit— cleannpm run test:e2e— all 132 checks pass (was 126; +6 new pattern assertions)patterns/session-explainer.mdby SHA-256 (ad4b0636…) — the mirror is hand-maintained, so this was checked rather than assumedNotes for the reviewer (pre-existing, not from this PR)
npm run devis broken onmain— barewranglerhits the.binshim__dirnamebug CLAUDE.md predicted ("if they ever break the same way, copy the direct-invoke pattern"). Worked around locally withnode node_modules/wrangler/bin/wrangler.js dev. Worth a follow-up applying the direct-invoke fix todev/deploy/tailinpackage.json.src/skill.tsandskills/htmlbin/SKILL.mdhave ~52 lines of pre-existing drift. My one-line edit went into both, but the mirrors are otherwise out of sync and nothing enforces it. Worth its own PR.migrations/0001-github-oauth.sqlfails against a local DB created fromschema.sql(duplicategithub_user_id), which leaves0002-drop-metadata.sqlunapplied and silently breaks 36 e2e checks locally. Not a production issue, but it makes a fresh local setup look broken.🤖 Generated with Claude Code