Recut the visual design — braided agent strands, refined palette
Some checks failed
Deploy website to GitHub Pages / deploy (push) Has been cancelled

This pass replaces the v2 visual language with a more handcrafted,
signature-feeling system. Structure and copy are preserved; only the
visual + interaction layer is reworked.

Visual metaphor
---------------
The dominant motif is a braid of two agent strands (claude-code in
ember, codex in indigo) weaving over and under each other through
hexagonal "checkpoint stones." The stones carry short hashes; the
rightmost is "wet" — currently being written — and gets a soft ember
pulse. Braiding has cultural weight (DNA, rope, river confluence) and
reads as "structured intertwining," which is exactly what shared
reasoning state is. The same motif scales down to a taut, stretched
braid in the drift section.

Palette
-------
Refined to feel like a signature, not a template:
- Deeper cool-black `#070710` background (warm accents pop harder
  against blue-black than against grey-black).
- Ember `#ffb663` — warmer and more saturated than v2's `#f5c773`;
  campfire ember, not corporate gold. This is the Smriti color.
- Indigo `#7c8cff` — saturated, fresh; not v2's washed `#8ba0c7`.
- Aurora `#7be3b0` — third pole used sparingly for matches / done
  states, so the page isn't just ember + indigo.
- Warm off-white paper `#e9e7e2` for body text.
- Subtle SVG-noise grain overlay (5% opacity, overlay blend) gives
  the dark surface tooth.
- Three hand-placed ember-glow anchor regions (hero, centerpiece,
  CTA) signal hierarchy — not decorative gradient blobs.

Section redesigns
-----------------
- Hero — braid + 5 hexagonal stones replacing v2's circle-and-line
  timeline. Subtle 14s sway on the strands. The rightmost (wet)
  stone glows and pulses.
- Missing primitive — replaced the rigid 3-column-grid + connector
  gutter with a typographic glossary. Mapped rows get an ember dot;
  unmapped rows get an indigo dot and the "no equivalent" label.
  Reads like a hand-typeset comparison sheet, not a spreadsheet.
- HANDOFF.md vs structured — both panels now have a lined-paper
  background. The HANDOFF panel has a `COLLISION` (red) badge; the
  checkpoint panel has a `RESOLVED` (aurora) badge. The conflict
  line types in, gets struck through, and the resolution types in
  underneath on scroll-in.
- What Smriti is — checkpoint render becomes a parsed structured
  tree with labeled field/value pairs and intent/agent tag pills.
- Multi-agent coordination (centerpiece) — completely rebuilt.
  Three columns: claude-code panel (left) | task pool (center) |
  codex panel (right). Tasks are "packets" that fly from the
  central pool into the side panels via JS-animated transforms,
  with springy easing. Each agent has a working stack that
  populates as packets land. Plays once on scroll-in, replayable.
  Bonus: clicking any unclaimed packet fires it manually to
  whichever agent has fewer claims.
- Drift — repo timeline replaced with a tensile braid stretched
  between the checkpoint stone and the HEAD stone. On scroll-in,
  the HEAD stone nudges +14px right, gets the warning color, and
  the "+2 commits ahead" badge fades in.
- Built with Smriti — terminal panel now has traffic-light dots, a
  blinking caret, and ember underline animation on
  "67 cross-agent continuations" on scroll-in. Numbers are
  semantically colored (ember = main, indigo = codex, aurora =
  success rates).
- Try it — two terminals now sit on a very subtle skew axis (like
  an open laptop spread); hover snaps them straight.
- Brand mark — replaced the dot-grid with a hexagonal stone and two
  woven strand-threads. Soft ember pulse every ~6s.

Motion
------
Tasteful, per-section motion treatments rather than uniform fade-up.
Slow braid sway on hero. Live-typing markdown collision. Springy
packet flight. Drift stone nudge. CLI caret blink. Brand mark pulse.
SVG path draw-in. Honors `prefers-reduced-motion: reduce` — all
animations skip, every element lands in its terminal state, carets
go static.

Implementation
--------------
Still pure HTML + CSS + ~330 lines of vanilla JS. No framework, no
build step, no webfont, no third-party requests. IntersectionObserver
thresholds tuned to 0.18 so animations fire reliably on mobile (tall
stacked sections were below the previous 0.35–0.45 thresholds).
Existing GitHub Pages workflow uploads ./website unchanged.

Mobile responsive at 980 / 720 / 420 breakpoints. Theatre stacks
vertically on mobile, packets still fly to the correct positions
via computed bounding rects. Nav collapses to the GitHub CTA on
small screens.
This commit is contained in:
Himanshu Dongre 2026-05-21 13:06:38 +05:30
parent b0bc283441
commit 15e099b67e
3 changed files with 2077 additions and 1377 deletions

View file

@ -4,10 +4,10 @@
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Smriti — Version control for project reasoning state</title>
<meta name="description" content="Code has Git. Multi-agent reasoning does not. Smriti is version control for project reasoning state — versioned, structured, branchable snapshots so multiple coding agents can coordinate on the same codebase without overwriting each other's thinking.">
<meta name="theme-color" content="#0a0a0e">
<meta name="description" content="Code has Git. Multi-agent reasoning does not. Smriti is version control for project reasoning state — versioned, structured, branchable snapshots so multiple coding agents coordinate on the same codebase without overwriting each other's thinking.">
<meta name="theme-color" content="#070710">
<meta property="og:title" content="Smriti — Version control for project reasoning state">
<meta property="og:description" content="Code has Git. Multi-agent reasoning does not. Smriti is version control for project reasoning state — so multiple coding agents can coordinate on the same codebase without overwriting each other's thinking.">
<meta property="og:description" content="Code has Git. Multi-agent reasoning does not. Smriti is version control for project reasoning state — so multiple coding agents coordinate on the same codebase without overwriting each other's thinking.">
<meta property="og:type" content="website">
<meta name="twitter:card" content="summary_large_image">
<link rel="stylesheet" href="styles.css">
@ -18,20 +18,22 @@
<header class="site-header">
<div class="container header-row">
<a class="brand" href="/" aria-label="Smriti">
<svg class="brand-mark" viewBox="0 0 22 22" fill="none" aria-hidden="true">
<circle cx="6" cy="6" r="2.5" stroke="currentColor" stroke-width="1.5"/>
<circle cx="16" cy="6" r="2.5" stroke="currentColor" stroke-width="1.5" fill="currentColor"/>
<circle cx="6" cy="16" r="2.5" stroke="currentColor" stroke-width="1.5" fill="currentColor"/>
<circle cx="16" cy="16" r="2.5" stroke="currentColor" stroke-width="1.5"/>
<line x1="6" y1="6" x2="16" y2="6" stroke="currentColor" stroke-width="1" stroke-dasharray="1.5 1.5"/>
<line x1="6" y1="16" x2="16" y2="16" stroke="currentColor" stroke-width="1" stroke-dasharray="1.5 1.5"/>
<line x1="6" y1="6" x2="6" y2="16" stroke="currentColor" stroke-width="1" stroke-dasharray="1.5 1.5"/>
<line x1="16" y1="6" x2="16" y2="16" stroke="currentColor" stroke-width="1" stroke-dasharray="1.5 1.5"/>
<svg class="brand-mark" viewBox="0 0 22 22" aria-hidden="true">
<!-- Hexagonal stone with two strand-threads weaving through -->
<polygon class="stone" points="6,3.5 14,3.5 18,11 14,18.5 6,18.5 2,11"
fill="#070710" stroke="#ffb663" stroke-width="1.4"/>
<path d="M 3 6 C 7 6, 7 16, 11 11 C 15 6, 15 16, 19 16"
fill="none" stroke="#ffb663" stroke-width="1.2" stroke-linecap="round"/>
<path d="M 3 16 C 7 16, 7 6, 11 11 C 15 16, 15 6, 19 6"
fill="none" stroke="#7c8cff" stroke-width="1.2" stroke-linecap="round" opacity="0.85"/>
<!-- Micro pulse ring -->
<circle class="pulse" cx="11" cy="11" r="2.5"
fill="#ffb663" opacity="0"/>
</svg>
<span>Smriti</span>
</a>
<nav>
<a href="#how">How it works</a>
<a href="#primitive">Primitives</a>
<a href="#coordination">Coordination</a>
<a href="#try">Try it</a>
<a class="nav-cta" href="https://github.com/himanshudongre/smriti">GitHub</a>
@ -44,11 +46,14 @@
<!-- ═══════════════════════════ Hero ═══════════════════════════ -->
<section class="hero">
<div class="container hero-grid">
<div>
<h1>Code has Git.<span class="hero-em">Multi-agent reasoning does not.</span></h1>
<div class="hero-copy">
<h1>
<span class="l1">Code has Git.</span>
<span class="l2">Multi-agent reasoning<br>does not.</span>
</h1>
<p class="hero-sub">Smriti is version control for project reasoning state. Versioned, structured, branchable snapshots of <strong>what was decided</strong>, <strong>what's still open</strong>, and <strong>what each agent is doing right now</strong> — so multiple coding agents coordinate on the same codebase without overwriting each other's thinking.</p>
<div class="hero-cta">
<a class="btn btn-primary" href="#coordination">See how it works</a>
<a class="btn btn-primary" href="#coordination">See it coordinate</a>
<a class="btn btn-secondary" href="https://github.com/himanshudongre/smriti">View on GitHub →</a>
</div>
<div class="hero-meta">
@ -57,128 +62,174 @@
</div>
</div>
<!-- Ambient hero diagram: two agent timelines crossing through shared checkpoints -->
<div class="hero-diagram" aria-hidden="true">
<svg viewBox="0 0 880 180" preserveAspectRatio="xMidYMid meet" role="img" aria-label="Two agent timelines threaded through shared checkpoints">
<!-- Lane labels -->
<text class="lane-label" x="0" y="59">claude-code</text>
<text class="lane-label" x="0" y="129">codex</text>
<!-- Hero braid — two agent strands weaving through hexagonal checkpoint stones -->
<div class="hero-braid" aria-hidden="true">
<svg viewBox="0 0 600 240" class="braid" preserveAspectRatio="xMidYMid meet" role="img" aria-label="Two agent strands weaving through hexagonal checkpoint stones">
<!-- Dashed lane lines -->
<line class="lane-line" x1="120" y1="55" x2="860" y2="55"/>
<line class="lane-line" x1="120" y1="125" x2="860" y2="125"/>
<!-- Faint lane echoes (very subtle) -->
<g opacity="0.18">
<line x1="20" y1="80" x2="580" y2="80" stroke="#36343d" stroke-width="0.7" stroke-dasharray="2 8"/>
<line x1="20" y1="160" x2="580" y2="160" stroke="#36343d" stroke-width="0.7" stroke-dasharray="2 8"/>
</g>
<!-- Vertical threads on shared checkpoints -->
<line class="lane-line" x1="340" y1="55" x2="340" y2="125" stroke-dasharray="2 3"/>
<line class="lane-line" x1="620" y1="55" x2="620" y2="125" stroke-dasharray="2 3"/>
<g class="sway">
<!-- Agent paths through nodes -->
<path class="agent-line claude" d="M 200 55 L 340 55 L 620 55 L 760 55"/>
<path class="agent-line codex" d="M 340 125 L 480 125 L 620 125"/>
<!-- Lane labels -->
<text class="label claude" x="0" y="86">claude-code</text>
<text class="label codex" x="0" y="174">codex</text>
<!-- ── Strand A (claude, ember) — under -->
<path class="strand claude" d="
M 20 80
C 80 80, 80 160, 140 160
C 200 160, 200 80, 260 80
C 320 80, 320 160, 380 160
C 440 160, 440 80, 500 80
C 540 80, 540 120, 580 120"/>
<!-- ── Strand B (codex, indigo) — drawn second so it appears over A at crossings -->
<path class="strand codex" d="
M 20 160
C 80 160, 80 80, 140 80
C 200 80, 200 160, 260 160
C 320 160, 320 80, 380 80
C 440 80, 440 160, 500 160
C 540 160, 540 120, 580 120"/>
<!-- ── Over patches — re-draw claude's strand at alternating crossings,
so the braid alternates which strand is on top. -->
<!-- Crossings at x=80, 200, 320, 440. claude over at 80, 320 (alternating). -->
<path class="patch claude" d="M 65 105 C 78 110, 82 130, 95 135" stroke="#ffb663"/>
<path class="patch claude" d="M 305 105 C 318 110, 322 130, 335 135" stroke="#ffb663"/>
<!-- codex over at 200, 440 -->
<path class="patch codex" d="M 185 135 C 198 130, 202 110, 215 105" stroke="#7c8cff"/>
<path class="patch codex" d="M 425 135 C 438 130, 442 110, 455 105" stroke="#7c8cff"/>
<!-- ── Sealed checkpoint stones at crossings -->
<g class="stone-group">
<polygon class="stone sealed" points="65,107 95,107 105,120 95,133 65,133 55,120"/>
<text class="stone-hash" x="80" y="124" text-anchor="middle">47d5bb0</text>
</g>
<g class="stone-group">
<polygon class="stone sealed" points="185,107 215,107 225,120 215,133 185,133 175,120"/>
<text class="stone-hash" x="200" y="124" text-anchor="middle">6ddfc2e</text>
</g>
<g class="stone-group">
<polygon class="stone sealed" points="305,107 335,107 345,120 335,133 305,133 295,120"/>
<text class="stone-hash" x="320" y="124" text-anchor="middle">73914a9</text>
</g>
<g class="stone-group">
<polygon class="stone sealed" points="425,107 455,107 465,120 455,133 425,133 415,120"/>
<text class="stone-hash" x="440" y="124" text-anchor="middle">5d00ba9</text>
</g>
<!-- ── Wet stone at convergence — where the strands meet for the next checkpoint -->
<g class="stone-group is-wet">
<circle class="stone-pulse" cx="580" cy="120" r="13"/>
<polygon class="stone wet" points="565,107 595,107 605,120 595,133 565,133 555,120"/>
<text class="stone-hash" x="580" y="124" text-anchor="middle">b0bc283</text>
</g>
</g>
<!-- Static caption below -->
<text x="300" y="222" text-anchor="middle"
font-family="ui-monospace,monospace" font-size="10.5"
fill="#5a5764" letter-spacing="0.06em">
← past checkpoints current ·
</text>
<!-- Nodes -->
<!-- n1 claude only -->
<circle class="checkpoint" cx="200" cy="55" r="6"/>
<!-- n2 shared -->
<circle class="checkpoint shared" cx="340" cy="55" r="6"/>
<circle class="checkpoint shared" cx="340" cy="125" r="6"/>
<!-- n3 codex only -->
<circle class="checkpoint" cx="480" cy="125" r="6" stroke="#8ba0c7"/>
<!-- n4 shared (pulses) -->
<circle class="pulse" cx="620" cy="55" r="6"/>
<circle class="checkpoint shared" cx="620" cy="55" r="6"/>
<circle class="checkpoint shared" cx="620" cy="125" r="6"/>
<!-- n5 claude only -->
<circle class="checkpoint" cx="760" cy="55" r="6"/>
</svg>
</div>
</div>
</section>
<!-- ═══════════════ The missing primitive (Git ↔ Smriti) ═══════════════ -->
<section class="section reveal">
<div class="container">
<!-- ═══════════════ The missing primitive (typographic glossary) ═══════════════ -->
<section class="section reveal" id="primitive">
<div class="container container-narrow">
<p class="section-eyebrow">The missing primitive</p>
<h2>Git tracks code history.<br>Nothing tracks reasoning history.</h2>
<h2>Git tracks <span class="accent">code</span> history.<br>Nothing tracks <span class="alt">reasoning</span> history.</h2>
<p class="lede">A single human committing serial code has Git. Multiple agents writing reasoning state in parallel have nothing. Smriti closes the gap with the same primitives, plus three Git was never built to have.</p>
<div class="primitive-map" aria-label="Comparison of Git primitives and Smriti primitives">
<div class="glossary">
<!-- Header row -->
<div class="pm-header pm-header-git">Code · Git</div>
<div class="pm-header pm-header-gutter"></div>
<div class="pm-header pm-header-smriti">Reasoning · Smriti</div>
<!-- Row 1: commit ↔ checkpoint -->
<div class="pm-cell git">commit</div>
<div class="pm-cell gutter"><span class="pm-connector"></span></div>
<div class="pm-cell smriti">
<span class="term">checkpoint</span>
<span class="desc">Structured snapshot of reasoning state at an inflection point.</span>
<div class="gloss-header">
<div>Code · Git</div>
<div>Reasoning · Smriti</div>
</div>
<!-- Row 2: branch ↔ fork -->
<div class="pm-cell git">branch</div>
<div class="pm-cell gutter"><span class="pm-connector"></span></div>
<div class="pm-cell smriti">
<span class="term">fork</span>
<span class="desc">Branch from any checkpoint to explore an alternative without losing the trunk.</span>
<div class="gloss-row">
<div class="git-term">commit</div>
<div class="smriti-block">
<span class="smriti-term">checkpoint</span>
<span class="smriti-desc">Structured snapshot of reasoning state at an inflection point.</span>
</div>
</div>
<!-- Row 3: diff ↔ compare -->
<div class="pm-cell git">diff</div>
<div class="pm-cell gutter"><span class="pm-connector"></span></div>
<div class="pm-cell smriti">
<span class="term">compare</span>
<span class="desc">Structured diff of two checkpoints — decisions, tasks, claims.</span>
<div class="gloss-row">
<div class="git-term">branch</div>
<div class="smriti-block">
<span class="smriti-term">fork</span>
<span class="smriti-desc">Branch from any checkpoint to explore an alternative without losing the trunk.</span>
</div>
</div>
<!-- Row 4: revert ↔ restore -->
<div class="pm-cell git">revert</div>
<div class="pm-cell gutter"><span class="pm-connector"></span></div>
<div class="pm-cell smriti">
<span class="term">restore</span>
<span class="desc">Return to a clean checkpoint when an experiment goes sideways.</span>
<div class="gloss-row">
<div class="git-term">diff</div>
<div class="smriti-block">
<span class="smriti-term">compare</span>
<span class="smriti-desc">Structured diff of two checkpoints — decisions, tasks, claims.</span>
</div>
</div>
<!-- Row 5: working-tree drift ↔ repo-state drift -->
<div class="pm-cell git">working-tree drift</div>
<div class="pm-cell gutter"><span class="pm-connector"></span></div>
<div class="pm-cell smriti">
<span class="term">repo-state drift</span>
<span class="desc">Flag when the live repo has moved past the checkpoint base.</span>
<div class="gloss-row">
<div class="git-term">revert</div>
<div class="smriti-block">
<span class="smriti-term">restore</span>
<span class="smriti-desc">Return to a clean checkpoint when an experiment goes sideways.</span>
</div>
</div>
<!-- Row 6: — ↔ active claims (unmapped) -->
<div class="pm-cell git empty"></div>
<div class="pm-cell gutter"></div>
<div class="pm-cell smriti unmapped">
<span class="term">active claims</span>
<span class="desc">Advisory, time-bounded — agents see each other coming.</span>
<div class="gloss-row">
<div class="git-term">working-tree drift</div>
<div class="smriti-block">
<span class="smriti-term">repo-state drift</span>
<span class="smriti-desc">Flag when the live repo has moved past the checkpoint base.</span>
</div>
</div>
<!-- Row 7: — ↔ freshness checks (unmapped) -->
<div class="pm-cell git empty"></div>
<div class="pm-cell gutter"></div>
<div class="pm-cell smriti unmapped">
<span class="term">freshness checks</span>
<span class="desc">"Has shared state moved since my base?" before checkpointing.</span>
<div class="gloss-row unmapped">
<div class="git-term">no equivalent</div>
<div class="smriti-block">
<span class="smriti-term">active claims</span>
<span class="smriti-desc">Advisory, time-bounded — agents see each other coming.</span>
</div>
</div>
<!-- Row 8: — ↔ structured tasks + IDs (unmapped) -->
<div class="pm-cell git empty"></div>
<div class="pm-cell gutter"></div>
<div class="pm-cell smriti unmapped">
<span class="term">structured tasks + IDs</span>
<span class="desc">Stable identifiers so two agents can talk about the same work.</span>
<div class="gloss-row unmapped">
<div class="git-term">no equivalent</div>
<div class="smriti-block">
<span class="smriti-term">freshness checks</span>
<span class="smriti-desc">"Has shared state moved since my base?" before checkpointing.</span>
</div>
</div>
<div class="pm-footer">
<span class="swatch"></span>
The last three have no Git equivalent. Git is built for one human committing serial code. Smriti is built for multiple agents writing reasoning state in parallel.
<div class="gloss-row unmapped">
<div class="git-term">no equivalent</div>
<div class="smriti-block">
<span class="smriti-term">structured tasks &amp; IDs</span>
<span class="smriti-desc">Stable identifiers so two agents can talk about the same work.</span>
</div>
</div>
</div>
<p class="glossary-footnote">
The last three<span class="dot"></span>have no Git equivalent. Git is built for one human committing serial code. Smriti is built for multiple agents writing reasoning state in parallel.
</p>
</div>
</section>
@ -186,62 +237,54 @@
<section class="section reveal">
<div class="container">
<p class="section-eyebrow">Why <code>HANDOFF.md</code> isn't enough</p>
<h2>Markdown is prose.<br>Coordination is structure.</h2>
<h2>Markdown is prose.<br>Coordination is <span class="accent">structure</span>.</h2>
<p class="lede">A shared markdown file works until two agents need to edit it at once, ask "is my base still current?", or compare two takes on the same decision. The moment coordination needs to be queried, branched, or validated against the repo, prose stops working.</p>
<div class="collapse-grid">
<div class="collapse-pair">
<div class="collapse-card">
<div class="panel-header">
<span class="filename">
<span class="dots"><span></span><span></span><span></span></span>
HANDOFF.md
</span>
<span>prose</span>
<div class="md-surface" data-md-surface>
<div class="surface-bar">
<span class="filename">HANDOFF.md</span>
<span class="badge">collision</span>
</div>
<div class="panel-body">
<span class="line heading"># Current work</span>
<div class="surface-body">
<span class="line h"># Current work</span>
<span class="line">- Status: refactoring rate-limit middleware</span>
<span class="line">- Owner: ?</span>
<span class="line">- Base: main, but unsure if still current</span>
<span class="line">&nbsp;</span>
<span class="line heading">## In progress</span>
<span class="line from-claude">- [claude-code] writing the guard logic</span>
<span class="line from-codex struck">- [codex] writing the guard logic too</span>
<span class="line from-codex">- [codex] (no idea claude was on this)</span>
<span class="line h">## In progress</span>
<span class="line claude">- [claude-code] writing the guard logic</span>
<span class="line codex struck" data-md-conflict>- [codex] writing the guard logic too</span>
<span class="line codex" data-md-resolution>- [codex] (no idea claude was on this)<span class="typing-caret"></span></span>
<span class="line">&nbsp;</span>
<span class="line heading">## Open</span>
<span class="line h">## Open</span>
<span class="line">- need tests</span>
<span class="line">- need someone to update the README</span>
</div>
<div class="panel-footer bad">
<div class="surface-foot">
<strong>What broke:</strong> two agents picked the same task. No claim, no freshness check, no shared task ID. Last write wins.
</div>
</div>
<div class="collapse-card">
<div class="panel-header">
<span class="filename">
<span class="dots"><span></span><span></span><span></span></span>
checkpoint
</span>
<span>structured</span>
<div class="cp-surface">
<div class="cp-bar">
<span class="filename">checkpoint · b0bc283</span>
<span class="badge">resolved</span>
</div>
<div class="panel-body">
<span class="line field"><span class="key">objective:</span> close deletion-safety thread</span>
<span class="line field"><span class="key">decisions:</span></span>
<span class="line field indent-1">- require <span class="val">force=true</span> for non-empty deletes</span>
<span class="line field indent-1">- 409 if claim is unresolved</span>
<span class="line field"><span class="key">in_progress:</span></span>
<span class="line field indent-1">- <span class="val">[implement]</span> deletion-safety-guard <span class="id">id=t-2</span></span>
<span class="line field indent-1">- <span class="val">[test]</span> deletion-safety-tests <span class="id">id=t-1</span></span>
<span class="line field"><span class="key">active_claims:</span></span>
<span class="line field indent-1">- claude-code → t-2, ttl 4h</span>
<span class="line field indent-1">- codex → t-1, ttl 4h</span>
<span class="line field"><span class="key">repo_state:</span></span>
<span class="line field indent-1">head=<span class="id">5d00ba9</span> branch=main ahead=0</span>
</div>
<div class="panel-footer good">
<span class="ln"><span class="k">objective:</span> close deletion-safety thread</span>
<span class="ln"><span class="k">decisions:</span></span>
<span class="ln in-1">- require <span class="v">force=true</span> for non-empty deletes</span>
<span class="ln in-1">- <span class="v">409</span> if claim is unresolved</span>
<span class="ln"><span class="k">in_progress:</span></span>
<span class="ln in-1">- <span class="v">[implement]</span> deletion-safety-guard <span class="id">id=t-2</span></span>
<span class="ln in-1">- <span class="vi">[test]</span> deletion-safety-tests <span class="id">id=t-1</span></span>
<span class="ln"><span class="k">active_claims:</span></span>
<span class="ln in-1">- claude-code → t-2, ttl 4h</span>
<span class="ln in-1">- codex → t-1, ttl 4h</span>
<span class="ln"><span class="k">repo_state:</span></span>
<span class="ln in-1">head=<span class="id">b0bc283</span> branch=main ahead=0</span>
<div class="cp-foot">
<strong>What's preserved:</strong> queryable fields, stable IDs, active claims, repo state. Two agents see each other.
</div>
</div>
@ -254,61 +297,69 @@
<section class="section reveal" id="how">
<div class="container">
<p class="section-eyebrow">What Smriti is</p>
<h2>A coordination substrate, not a notebook.</h2>
<h2>A coordination substrate,<br>not a notebook.</h2>
<div class="is-grid">
<div>
<p>Smriti is a small backend, a CLI, an MCP server, and skill packs for Claude Code and Codex. It records <strong>structured snapshots</strong> at inflection points — not a running log of everything an agent saw — and exposes them as a queryable state brief every agent reads at session start.</p>
<p>One Space per project. Per-project attachment via <code>.smriti.json</code>. Local SQLite by default; Postgres for shared/team workflows. No orchestrator. No router. No assignment logic. Agents read the same structured state and decide for themselves.</p>
<p>One Space per project. Per-project attachment via <code>.smriti.json</code>. Local SQLite by default; Postgres for shared workflows. No orchestrator. No router. No assignment logic. Agents read the same structured state and decide for themselves.</p>
<p>The hard part isn't storing reasoning — it's making it <em>self-validating against the live repo</em> and <em>safe for parallel writers</em>. That's the work.</p>
</div>
<div class="checkpoint-card" aria-label="Example structured checkpoint">
<div class="cp-header">
<span class="cp-id">checkpoint · 5d00ba9</span>
<span class="cp-meta">
<div class="struct-tree">
<div class="tree-bar">
<span class="hash">checkpoint · b0bc283</span>
<span class="meta">
<span>claude-code</span>
<span>30m ago</span>
</span>
</div>
<div class="cp-body">
<div class="tree-body">
<div class="cp-field">
<span class="label">objective</span>
<span class="value">Ship Pages deploy for the static landing page.</span>
<div class="tline">
<span class="field">objective</span>
<span class="val">Ship the v2 website redesign.</span>
</div>
<div class="cp-field">
<span class="label">decisions</span>
<ul class="cp-list" style="display:grid;gap:0.3rem;">
<li><span class="value">Use GitHub Actions source, not legacy branch-folder.</span></li>
<li><span class="value">Keep <code>.nojekyll</code> as a forward-compatible marker.</span></li>
</ul>
<div class="tline">
<span class="field">decisions</span>
<span class="val">
<ul>
<li>Stay zero-build (no framework, no node_modules).</li>
<li>Use the braided-strand motif as the visual language.</li>
</ul>
</span>
</div>
<div class="cp-field">
<span class="label">in_progress</span>
<ul class="cp-list">
<li>
<span class="badge implement">implement</span>
<span>publish via Pages</span>
<span class="id">id=website-deploy-target</span>
</li>
</ul>
<div class="tline">
<span class="field">in_progress</span>
<span class="val">
<ul>
<li>
<span class="tag implement">implement</span>
<span>landing page redesign</span>
<span class="id-pill">id=website-v2</span>
</li>
</ul>
</span>
</div>
<div class="cp-field">
<span class="label">active_claims</span>
<div class="cp-claim">
<span class="agent claude">claude-code</span>
<span class="value muted">website-deploy-target · ttl 4h</span>
</div>
<div class="tline">
<span class="field">active_claims</span>
<span class="val">
<ul>
<li>
<span class="tag claude">claude-code</span>
<span class="muted">website-v2 · ttl 4h</span>
</li>
</ul>
</span>
</div>
<div class="cp-field">
<span class="label">repo_state</span>
<span class="cp-repo">
head=<span class="ok">5d00ba9</span> · branch=<span class="ok">main</span> · ahead=<span class="ok">0</span>
<div class="tline">
<span class="field">repo_state</span>
<span class="val">
<span class="repo-line">head=<span class="ok">b0bc283</span> · branch=<span class="ok">main</span> · ahead=<span class="ok">0</span></span>
</span>
</div>
@ -320,82 +371,81 @@
<!-- ═══════════════ Centerpiece: Multi-agent coordination ═══════════════ -->
<section class="section reveal" id="coordination">
<div class="container">
<p class="section-eyebrow">Multi-agent coordination protocol, not orchestration</p>
<h2>Two agents. Same task pool.<br>Complementary work — no orchestrator.</h2>
<div class="container container-wide">
<p class="section-eyebrow">Multi-agent coordination · protocol, not orchestration</p>
<h2>Two agents. Same task pool.<br><span class="accent">Complementary work</span> — no orchestrator.</h2>
<p class="lede">Each agent reads the shared state, sees the others' active claims, and picks something complementary. The "routing logic" is structured metadata, not a queue manager.</p>
<div class="theatre" data-theatre>
<div class="theatre-frame">
<div class="theatre-pool">
<span class="pool-label">task pool</span>
<div class="theatre-tasks">
<div class="task-chip" data-task="t-1">
<span class="chip-intent test">test</span>
<span>deletion-safety-tests</span>
<span class="chip-id">t-1</span>
</div>
<div class="task-chip" data-task="t-2">
<span class="chip-intent implement">implement</span>
<span>deletion-safety-guard</span>
<span class="chip-id">t-2</span>
</div>
<div class="task-chip" data-task="t-3">
<span class="chip-intent doc">doc</span>
<span>update-quickstart</span>
<span class="chip-id">t-3</span>
</div>
<div class="task-chip" data-task="t-4">
<span class="chip-intent investigate">investigate</span>
<span>failing-build</span>
<span class="chip-id">t-4</span>
</div>
<!-- Claude panel (left) -->
<div class="agent-panel claude" data-agent="claude">
<div class="panel-head">
<span class="dot"></span>
<span>claude-code</span>
<span class="status" data-agent-status>idle</span>
</div>
<div class="work-label">working on</div>
<ul class="work-stack" data-work-stack="claude"></ul>
</div>
<div class="theatre-lanes">
<div class="lane claude" data-lane="claude">
<div class="lane-id">
<span class="agent-dot"></span>
<span>claude-code</span>
</div>
<div class="lane-track">
<div class="lane-claim">
<span class="chip-intent implement">implement</span>
<span class="scope">deletion-safety-guard</span>
<span class="ttl">ttl 4h</span>
</div>
</div>
</div>
<!-- Pool (center) -->
<div class="pool">
<div class="pool-label">task pool · shared</div>
<ul class="pool-list" data-pool>
<li class="packet" data-task="t-1" data-intent="test">
<span class="intent test">test</span>
<span class="name">deletion-safety-tests</span>
<span class="pid">t-1</span>
</li>
<li class="packet" data-task="t-2" data-intent="implement">
<span class="intent implement">implement</span>
<span class="name">deletion-safety-guard</span>
<span class="pid">t-2</span>
</li>
<li class="packet" data-task="t-3" data-intent="doc">
<span class="intent doc">doc</span>
<span class="name">update-quickstart</span>
<span class="pid">t-3</span>
</li>
<li class="packet" data-task="t-4" data-intent="investigate">
<span class="intent investigate">investigate</span>
<span class="name">failing-build</span>
<span class="pid">t-4</span>
</li>
</ul>
</div>
<div class="lane codex" data-lane="codex">
<div class="lane-id">
<span class="agent-dot"></span>
<span>codex</span>
</div>
<div class="lane-track">
<div class="lane-claim">
<span class="chip-intent test">test</span>
<span class="scope">deletion-safety-tests</span>
<span class="ttl">ttl 4h</span>
</div>
</div>
<!-- Codex panel (right) -->
<div class="agent-panel codex" data-agent="codex">
<div class="panel-head">
<span class="dot"></span>
<span>codex</span>
<span class="status" data-agent-status>idle</span>
</div>
<div class="work-label">working on</div>
<ul class="work-stack" data-work-stack="codex"></ul>
</div>
</div>
<div class="theatre-caption">
<span class="step-text" data-step-text>Same task pool. No orchestrator. <span class="muted">Watch each agent read the pool, claim something, and route around the other's claim.</span></span>
<div class="controls">
<div class="theatre-controls">
<span class="step-text" data-step-text>Same task pool. <span class="muted">No orchestrator. Watch each agent read the pool, claim something, and route around the other's claim.</span></span>
<div class="controls-right">
<span class="step-counter" data-step-counter>0 / 5</span>
<button class="btn btn-ghost" type="button" data-theatre-replay aria-label="Replay coordination demo">↻ Replay</button>
</div>
</div>
<p class="theatre-hint">
Click any unclaimed packet to fire it manually <span class="key">click</span>
</p>
</div>
<p class="caption">This is the actual pattern that built this site. Same task pool, two agents, complementary picks, no human routing. <code>smriti metrics smriti-dev</code> below shows the cumulative coordination cost — 87 claims, 97% completion, 67 cross-agent continuations.</p>
<p class="caption">This is the pattern that built this site. Same task pool, two agents, complementary picks, no human routing. <code>smriti metrics smriti-dev</code> below shows the cumulative coordination cost.</p>
</div>
</section>
@ -403,52 +453,83 @@
<section class="section reveal">
<div class="container">
<p class="section-eyebrow">Drift &amp; trust</p>
<h2>Reasoning state stays honest about the repo.</h2>
<h2>Reasoning state stays <span class="accent">honest</span> about the repo.</h2>
<p class="lede">A reasoning snapshot is only useful if it tracks the code it's reasoning about. Every checkpoint records the repo HEAD and branch. Every state brief compares it back to the live repo — and surfaces the drift before another agent acts on stale ground.</p>
<div class="drift-grid">
<div class="drift-diagram">
<div class="label">repo timeline</div>
<svg viewBox="0 0 460 130" preserveAspectRatio="xMidYMid meet" aria-label="Repo timeline showing checkpoint base, two commits, and current HEAD">
<line x1="40" y1="65" x2="420" y2="65" stroke="#3a3a45" stroke-width="1" stroke-dasharray="2 3"/>
<div class="drift-canvas" data-drift>
<span class="canvas-label">repo timeline</span>
<svg viewBox="0 0 460 200" preserveAspectRatio="xMidYMid meet"
aria-label="Repo timeline showing checkpoint base, two commits, and current HEAD">
<circle cx="60" cy="65" r="9" fill="#0a0a0e" stroke="#f5c773" stroke-width="2"/>
<circle cx="60" cy="65" r="3" fill="#f5c773"/>
<!-- Timeline -->
<line class="timeline" x1="40" y1="100" x2="420" y2="100"/>
<circle cx="200" cy="65" r="5" fill="#3a3a45"/>
<circle cx="290" cy="65" r="5" fill="#3a3a45"/>
<!-- Tensile braid between base stone and HEAD -->
<path class="braid-taut a" d="M 70 100 C 130 92, 200 108, 260 92 C 320 76, 360 108, 370 100"/>
<path class="braid-taut b" d="M 70 100 C 130 108, 200 92, 260 108 C 320 124, 360 92, 370 100"/>
<circle cx="380" cy="65" r="9" fill="#0a0a0e" stroke="#d8a35a" stroke-width="2"/>
<circle cx="380" cy="65" r="3" fill="#d8a35a"/>
<!-- Intermediate commit dots -->
<circle class="commit-dot" cx="180" cy="100" r="4"/>
<circle class="commit-dot" cx="280" cy="100" r="4"/>
<path d="M 75 50 Q 220 30, 365 50" fill="none" stroke="#d8a35a" stroke-width="1" stroke-dasharray="3 3" opacity="0.6"/>
<!-- Base stone (checkpoint, ember) -->
<g>
<polygon class="stone-base" points="55,87 85,87 95,100 85,113 55,113 45,100"/>
<text x="70" y="104" text-anchor="middle"
font-family="ui-monospace,monospace" font-size="9.5"
fill="#ffb663" letter-spacing="0.04em">abc1234</text>
<text x="70" y="138" text-anchor="middle"
font-family="ui-monospace,monospace" font-size="9"
fill="#5a5764" letter-spacing="0.06em">CHECKPOINT</text>
</g>
<text x="60" y="100" text-anchor="middle" font-family="ui-monospace,monospace" font-size="11" fill="#9090a0">abc1234</text>
<text x="60" y="116" text-anchor="middle" font-family="ui-monospace,monospace" font-size="10" fill="#60606a">checkpoint</text>
<!-- HEAD stone (drifted) -->
<g class="stone-head-group">
<polygon class="stone-head" points="355,87 385,87 395,100 385,113 355,113 345,100"/>
<text x="370" y="104" text-anchor="middle"
font-family="ui-monospace,monospace" font-size="9.5"
fill="#9090a0" letter-spacing="0.04em">f4e5d6c</text>
<text x="370" y="138" text-anchor="middle"
font-family="ui-monospace,monospace" font-size="9"
fill="#5a5764" letter-spacing="0.06em">MAIN HEAD</text>
</g>
<text x="200" y="100" text-anchor="middle" font-family="ui-monospace,monospace" font-size="11" fill="#60606a">def5678</text>
<text x="290" y="100" text-anchor="middle" font-family="ui-monospace,monospace" font-size="11" fill="#60606a">9a1b2c3</text>
<!-- Commit hashes under intermediate dots -->
<text x="180" y="124" text-anchor="middle"
font-family="ui-monospace,monospace" font-size="9"
fill="#5a5764">def5678</text>
<text x="280" y="124" text-anchor="middle"
font-family="ui-monospace,monospace" font-size="9"
fill="#5a5764">9a1b2c3</text>
<text x="380" y="100" text-anchor="middle" font-family="ui-monospace,monospace" font-size="11" fill="#9090a0">f4e5d6c</text>
<text x="380" y="116" text-anchor="middle" font-family="ui-monospace,monospace" font-size="10" fill="#60606a">main HEAD</text>
<!-- Drift badge -->
<g class="drift-badge">
<rect x="170" y="55" width="118" height="22" rx="11"
fill="rgba(216,163,90,0.14)" stroke="#d8a35a" stroke-width="1"/>
<text x="229" y="70" text-anchor="middle"
font-family="ui-monospace,monospace" font-size="11"
fill="#d8a35a" font-weight="600" letter-spacing="0.04em">
+2 commits ahead
</text>
</g>
<text x="225" y="22" text-anchor="middle" font-family="ui-monospace,monospace" font-size="11" fill="#d8a35a" font-weight="600">+2 commits ahead</text>
</svg>
</div>
<div class="drift-state">
<div class="panel-header">smriti state my-project (excerpt)</div>
<div class="panel-body">
<div class="heading">## Repo state</div>
<div class="field">branch <span class="key">main</span> · HEAD <span class="key">f4e5d6c</span> · upstream <span class="ahead">+2/-0</span></div>
<div class="field">- recorded checkpoint: <span class="key">abc1234</span></div>
<div class="field">- repo is <span class="ahead">2 commits ahead</span> of checkpoint base</div>
<div class="field">- branch: <span class="ok">matches</span></div>
<div class="field">- working tree: <span class="ok">clean</span></div>
<div class="field">&nbsp;</div>
<div class="field"><span class="warn">→ reconcile before checkpointing</span></div>
<div class="ds-bar">
<span class="filename">smriti state my-project (excerpt)</span>
<span></span>
</div>
<span class="h">## Repo state</span>
<span class="ln">branch <span class="k">main</span> · HEAD <span class="k">f4e5d6c</span> · upstream <span class="ahead">+2/-0</span></span>
<span class="ln">- recorded checkpoint: <span class="k">abc1234</span></span>
<span class="ln">- repo is <span class="ahead">2 commits ahead</span> of checkpoint base</span>
<span class="ln">- branch: <span class="ok">matches</span></span>
<span class="ln">- working tree: <span class="ok">clean</span></span>
<span class="ln action">→ reconcile before checkpointing</span>
</div>
</div>
@ -458,37 +539,38 @@
</section>
<!-- ═══════════════ Built with Smriti ═══════════════ -->
<section class="section reveal">
<section class="section reveal" id="built">
<div class="container">
<p class="section-eyebrow">Built with Smriti</p>
<h2>The substrate was built on itself.</h2>
<h2>The substrate was built <span class="accent">on itself</span>.</h2>
<p class="lede">Claude Code and Codex co-built this project end-to-end through Smriti's own state. Real numbers from the project Space, as of today:</p>
<div class="built-cli" aria-label="smriti metrics output">
<div class="panel-header">
<span>terminal</span>
<span>smriti-dev</span>
<div class="terminal" data-terminal>
<div class="term-bar">
<span class="dots"><span></span><span></span><span></span></span>
<span class="title">~/Documents/GitHub/smriti</span>
<span class="space-tag">smriti-dev</span>
</div>
<div class="panel-body">
<div class="cmd"><span class="prompt">$</span><span class="accent">smriti metrics</span> smriti-dev</div>
<div class="out-header"># smriti-dev — project metrics</div>
<div class="term-body">
<div class="cmd"><span class="pr">$</span><span class="verb">smriti metrics</span> <span class="arg">smriti-dev</span><span class="caret"></span></div>
<div class="out-h"># smriti-dev — project metrics</div>
<div class="out-section">## Coordination</div>
<div class="out-row"><span class="num">129</span> checkpoints · <span class="num">2</span> agents (claude-code: <span class="num">79</span>, codex-local: <span class="num-alt">50</span>)</div>
<div class="out-row"><span class="num">67</span> cross-agent continuations</div>
<div class="out-row"><span class="num">87</span> claims · <span class="num">97%</span> completion · <span class="num">48</span> with task IDs</div>
<div class="out-sec">## Coordination</div>
<div class="out"><span class="n">129</span> checkpoints · <span class="n">2</span> agents (<span class="agent-c">claude-code</span>: <span class="n">79</span>, <span class="agent-x">codex-local</span>: <span class="ni">50</span>)</div>
<div class="out"><span class="highlight"><span class="n">67</span> cross-agent continuations</span></div>
<div class="out"><span class="n">87</span> claims · <span class="na">97%</span> completion · <span class="n">48</span> with task IDs</div>
<div class="out-section">## State quality</div>
<div class="out-row"><span class="num">3.2</span> decisions/checkpoint · <span class="num">1.8</span> tasks/checkpoint</div>
<div class="out-row"><span class="num">66</span> with structured tasks · <span class="num">64</span> with task IDs</div>
<div class="out-row"><span class="num">7</span> milestones · 1 noise label</div>
<div class="out-sec">## State quality</div>
<div class="out"><span class="n">3.2</span> decisions/checkpoint · <span class="n">1.8</span> tasks/checkpoint</div>
<div class="out"><span class="n">66</span> with structured tasks · <span class="n">64</span> with task IDs</div>
<div class="out"><span class="n">7</span> milestones · 1 noise label</div>
<div class="out-section">## Branches</div>
<div class="out-row"><span class="num">0</span> active · <span class="num">0</span> integrated · <span class="num">0</span> abandoned</div>
<div class="out-sec">## Branches</div>
<div class="out"><span class="n">0</span> active · <span class="n">0</span> integrated · <span class="n">0</span> abandoned</div>
</div>
</div>
<p class="built-note">The 67 cross-agent continuations are the headline. Each is a moment where one agent ended a session, the other started, read the shared state, and continued the same thread <em>as if it had been there for the work</em>.</p>
<p class="built-note">The <em>67 cross-agent continuations</em> are the headline. Each is a moment where one agent ended a session, the other started, read the shared state, and continued the same thread as if it had been there for the work.</p>
</div>
</section>
@ -496,42 +578,46 @@
<section class="section reveal" id="try">
<div class="container">
<p class="section-eyebrow">Try it</p>
<h2>Up and running in minutes.</h2>
<h2>Up and running in <span class="accent">minutes</span>.</h2>
<p class="lede"><strong>No Docker, no API keys, no cloud required.</strong> The core coordination loop runs on a local SQLite file. API keys come in only for optional LLM-assisted features.</p>
<div class="try-grid">
<div class="try-stack">
<div class="try-block">
<div class="block-header">
<div class="try-term">
<div class="tt-bar">
<span class="ix">01</span>
<h3>Install</h3>
<span class="marker">terminal 1</span>
</div>
<div class="block-body">
<pre><code>git clone https://github.com/himanshudongre/smriti
cd smriti
make setup-local
make dev-local # backend, leave running</code></pre>
<div class="tt-body">
<pre><span class="verb">git clone</span> <span class="arg">https://github.com/himanshudongre/smriti</span>
<span class="verb">cd</span> <span class="arg">smriti</span>
<span class="verb">make</span> <span class="arg">setup-local</span>
<span class="verb">make</span> <span class="arg">dev-local</span> <span class="note"># backend, leave running</span></pre>
</div>
</div>
<div class="try-block">
<div class="block-header">
<div class="try-term">
<div class="tt-bar">
<span class="ix">02</span>
<h3>Run</h3>
<span class="marker">terminal 2</span>
</div>
<div class="block-body">
<pre><code>cd smriti
source backend/.venv/bin/activate
<div class="tt-body">
<pre><span class="verb">cd</span> <span class="arg">smriti</span>
<span class="verb">source</span> <span class="arg">backend/.venv/bin/activate</span>
smriti doctor # verify install
smriti quickstart # seeded demo Space + 3-min walkthrough</code></pre>
<p class="note">After the walkthrough, <code>cd</code> into any of your own projects and run <code>smriti init my-project</code> to attach it. One Space per project.</p>
<span class="verb">smriti</span> <span class="arg">doctor</span> <span class="note"># verify install</span>
<span class="verb">smriti</span> <span class="arg">quickstart</span> <span class="note"># seeded demo + 3-min walkthrough</span></pre>
<div class="note-block">
After the walkthrough, <code>cd</code> into any of your own projects and run <code>smriti init my-project</code> to attach it. One Space per project.
</div>
</div>
</div>
</div>
<div class="try-footer">
<div class="try-foot">
<span class="reqs">requires Python 3.11+ &nbsp;·&nbsp; Node 20.19+ / 22.12+</span>
<a href="https://github.com/himanshudongre/smriti#getting-started" class="btn btn-ghost">Full setup guide →</a>
</div>
@ -541,7 +627,7 @@ smriti quickstart # seeded demo Space + 3-min walkthrough</code></pre>
<!-- ═══════════════ CTA ═══════════════ -->
<section class="cta">
<div class="container container-narrow">
<h2>Build with the missing primitive.</h2>
<h2>Build with the <span class="accent">missing primitive</span>.</h2>
<p>Smriti is open source. It works today for solo builders running multi-agent workflows.</p>
<div class="cta-buttons">
<a class="btn btn-primary" href="https://github.com/himanshudongre/smriti">Star on GitHub</a>

View file

@ -1,12 +1,16 @@
/* =============================================================
Smriti landing page interactions (v2)
No dependencies. Two responsibilities:
Smriti landing page interactions (v2.1)
No dependencies. Five responsibilities:
1. Reveal-on-scroll for .reveal sections.
2. The multi-agent coordination "protocol theatre" a 5-step
scripted animation that runs once on scroll-into-view and
can be replayed.
Honors prefers-reduced-motion: skips animation and leaves
everything in its terminal state.
2. Drift canvas toggles .is-drifted on scroll-in (CSS handles the rest).
3. Terminal panel toggles .is-revealed for the highlight underline.
4. Markdown surface types the conflict line, strikes it through,
types the resolution. On scroll-in. Replayable.
5. Protocol theatre 3-column live coordination demo. Tasks fly from
the central pool into the side agent panels as packets, settle into
work-stacks. Replay button. Click-to-fire bonus interaction.
Honors prefers-reduced-motion: skips animations and lands every element
in its terminal state.
============================================================= */
(function () {
@ -28,137 +32,382 @@
}
});
},
{ threshold: 0.12, rootMargin: "0px 0px -8% 0px" }
{ threshold: 0.1, rootMargin: "0px 0px -8% 0px" }
);
revealEls.forEach((el) => revealObs.observe(el));
}
// ── 2. Protocol theatre ────────────────────────────────────────
// ── 2. Drift canvas toggle ────────────────────────────────────
const drift = document.querySelector("[data-drift]");
if (drift) {
if (reduced || !("IntersectionObserver" in window)) {
drift.classList.add("is-drifted");
} else {
const driftObs = new IntersectionObserver(
(entries) => {
entries.forEach((entry) => {
if (entry.isIntersecting) {
setTimeout(() => drift.classList.add("is-drifted"), 400);
driftObs.unobserve(entry.target);
}
});
},
{ threshold: 0.18 }
);
driftObs.observe(drift);
}
}
// ── 3. Terminal reveal toggle ──────────────────────────────────
const terminal = document.querySelector("[data-terminal]");
if (terminal) {
if (reduced || !("IntersectionObserver" in window)) {
terminal.classList.add("is-revealed");
} else {
const termObs = new IntersectionObserver(
(entries) => {
entries.forEach((entry) => {
if (entry.isIntersecting) {
terminal.classList.add("is-revealed");
termObs.unobserve(entry.target);
}
});
},
{ threshold: 0.18 }
);
termObs.observe(terminal);
}
}
// ── 4. Markdown surface — type the collision, strike, resolve ───
const mdSurface = document.querySelector("[data-md-surface]");
if (mdSurface) {
const conflict = mdSurface.querySelector("[data-md-conflict]");
const resolution = mdSurface.querySelector("[data-md-resolution]");
const conflictText = conflict ? conflict.textContent : "";
const resolutionText = resolution
? resolution.textContent.replace(/\s*$/, "")
: "";
function resetMd() {
if (conflict) {
conflict.textContent = conflictText;
conflict.classList.remove("struck");
conflict.style.opacity = "0";
}
if (resolution) {
resolution.innerHTML = '<span class="typing-caret"></span>';
resolution.style.opacity = "1";
}
}
function playMd() {
if (reduced) {
// Land in resolved state directly.
if (conflict) {
conflict.textContent = conflictText;
conflict.classList.add("struck");
conflict.style.opacity = "0.5";
}
if (resolution) {
resolution.innerHTML =
resolutionText + '<span class="typing-caret"></span>';
}
return;
}
resetMd();
// Phase 1: fade conflict in.
setTimeout(() => {
if (conflict) {
conflict.style.transition = "opacity 350ms";
conflict.style.opacity = "1";
}
}, 250);
// Phase 2: strike it.
setTimeout(() => {
if (conflict) conflict.classList.add("struck");
}, 1400);
// Phase 3: type the resolution.
setTimeout(() => {
if (!resolution) return;
let i = 0;
const caretHtml = '<span class="typing-caret"></span>';
const tick = () => {
if (i <= resolutionText.length) {
resolution.innerHTML = resolutionText.slice(0, i) + caretHtml;
i++;
setTimeout(tick, 28);
}
};
tick();
}, 2050);
}
if (reduced || !("IntersectionObserver" in window)) {
playMd();
} else {
const mdObs = new IntersectionObserver(
(entries) => {
entries.forEach((entry) => {
if (entry.isIntersecting) {
playMd();
mdObs.unobserve(entry.target);
}
});
},
{ threshold: 0.18 }
);
mdObs.observe(mdSurface);
}
}
// ── 5. Protocol theatre ────────────────────────────────────────
const theatre = document.querySelector("[data-theatre]");
if (!theatre) return;
const claudeLane = theatre.querySelector('[data-lane="claude"]');
const codexLane = theatre.querySelector('[data-lane="codex"]');
const chipT1 = theatre.querySelector('[data-task="t-1"]');
const chipT2 = theatre.querySelector('[data-task="t-2"]');
const chipT3 = theatre.querySelector('[data-task="t-3"]');
const chipT4 = theatre.querySelector('[data-task="t-4"]');
const stepText = theatre.querySelector("[data-step-text]");
const stepCounter = theatre.querySelector("[data-step-counter]");
const replayBtn = theatre.querySelector("[data-theatre-replay]");
const pool = theatre.querySelector("[data-pool]");
const claudePanel = theatre.querySelector('[data-agent="claude"]');
const codexPanel = theatre.querySelector('[data-agent="codex"]');
const claudeStack = theatre.querySelector('[data-work-stack="claude"]');
const codexStack = theatre.querySelector('[data-work-stack="codex"]');
const claudeStatus = claudePanel.querySelector("[data-agent-status]");
const codexStatus = codexPanel.querySelector("[data-agent-status]");
const stepText = theatre.querySelector("[data-step-text]");
const stepCounter = theatre.querySelector("[data-step-counter]");
const replayBtn = theatre.querySelector("[data-theatre-replay]");
const STEPS = [
{
caption:
'Same task pool. No orchestrator. <span class="muted">Watch each agent read the pool, claim something, and route around the other\'s claim.</span>',
apply() {
[claudeLane, codexLane].forEach((l) => l.classList.remove("active", "has-claim"));
[chipT1, chipT2, chipT3, chipT4].forEach((c) =>
c.classList.remove("claimed-claude", "claimed-codex", "dimmed")
);
},
},
{
caption: '<span class="muted">claude-code reads the pool.</span>',
apply() {
claudeLane.classList.add("active");
codexLane.classList.remove("active", "has-claim");
},
},
{
caption:
'claude-code claims <strong>[implement] deletion-safety-guard</strong>.',
apply() {
claudeLane.classList.add("active", "has-claim");
chipT2.classList.add("claimed-claude");
},
},
{
caption:
'<span class="muted">codex reads the pool — sees claude-code\'s claim.</span>',
apply() {
claudeLane.classList.remove("active");
claudeLane.classList.add("has-claim");
codexLane.classList.add("active");
},
},
{
caption:
'codex picks complementary work — <strong>[test] deletion-safety-tests</strong>.',
apply() {
codexLane.classList.add("active", "has-claim");
chipT1.classList.add("claimed-codex");
},
},
{
caption:
'Different agents. Different work. <span class="muted">Coordination through shared state.</span>',
apply() {
[claudeLane, codexLane].forEach((l) => l.classList.remove("active"));
[claudeLane, codexLane].forEach((l) => l.classList.add("has-claim"));
chipT3.classList.add("dimmed");
chipT4.classList.add("dimmed");
},
},
];
const TOTAL = STEPS.length - 1; // 0..5, display "n / 5"
let stepTimer = null;
let currentStep = 0;
let hasPlayed = false;
function renderStep(i) {
currentStep = i;
STEPS[i].apply();
if (stepText) stepText.innerHTML = STEPS[i].caption;
if (stepCounter) stepCounter.textContent = i + " / " + TOTAL;
function getPackets() {
return Array.from(pool.querySelectorAll(".packet"));
}
function clearTimer() {
if (stepTimer) {
clearTimeout(stepTimer);
stepTimer = null;
}
function setStatus(agent, text) {
const el = agent === "claude" ? claudeStatus : codexStatus;
if (el) el.textContent = text;
}
function playFrom(i) {
clearTimer();
renderStep(i);
if (i >= TOTAL) return;
stepTimer = setTimeout(() => playFrom(i + 1), 1700);
function clearStacks() {
claudeStack.innerHTML = "";
codexStack.innerHTML = "";
}
function play() {
if (reduced) {
// Land on the final frame; skip the animation.
renderStep(TOTAL);
function resetTheatre() {
[claudePanel, codexPanel].forEach((p) => p.classList.remove("is-active"));
setStatus("claude", "idle");
setStatus("codex", "idle");
clearStacks();
getPackets().forEach((p) =>
p.classList.remove("claimed-claude", "claimed-codex", "dimmed")
);
if (stepCounter) stepCounter.textContent = "0 / 5";
}
function makeWorkItem(intent, name, pid, agent) {
const li = document.createElement("li");
li.className = "packet claimed-" + agent;
li.innerHTML =
'<span class="intent ' + intent + '">' + intent + "</span>" +
'<span class="name">' + name + "</span>" +
'<span class="pid">' + pid + "</span>";
li.style.opacity = "0";
li.style.transform = "translateY(6px)";
li.style.transition = "opacity 300ms, transform 300ms";
return li;
}
// Animate a packet flying from the pool into an agent's work-stack.
// Returns a Promise that resolves when the packet has settled.
function flyPacket(packet, agent) {
return new Promise((resolve) => {
const targetStack = agent === "claude" ? claudeStack : codexStack;
const intent = packet.dataset.intent;
const name = packet.querySelector(".name").textContent;
const pid = packet.querySelector(".pid").textContent;
// Mark the pool packet as claimed (color tint + still in pool).
const claimClass = "claimed-" + agent;
packet.classList.add(claimClass);
if (reduced) {
const li = makeWorkItem(intent, name, pid, agent);
targetStack.appendChild(li);
requestAnimationFrame(() => {
li.style.opacity = "1";
li.style.transform = "translateY(0)";
});
resolve();
return;
}
const theatreRect = theatre.getBoundingClientRect();
const srcRect = packet.getBoundingClientRect();
// Compute destination: top of the target stack, offset by current
// child count to stack vertically.
const stackRect = targetStack.getBoundingClientRect();
const dstX = stackRect.left - theatreRect.left;
const dstY = stackRect.top - theatreRect.top
+ targetStack.childElementCount * 40;
// Build the flying packet — a clone of the original styled identically.
const fly = document.createElement("div");
fly.className = "packet-flying packet " + claimClass;
fly.style.left = (srcRect.left - theatreRect.left) + "px";
fly.style.top = (srcRect.top - theatreRect.top) + "px";
fly.style.width = srcRect.width + "px";
fly.innerHTML =
'<span class="intent ' + intent + '">' + intent + "</span>" +
'<span class="name">' + name + "</span>" +
'<span class="pid">' + pid + "</span>";
theatre.appendChild(fly);
// Force layout flush, then transition to destination.
// eslint-disable-next-line no-unused-expressions
fly.offsetWidth;
const deltaX = dstX - (srcRect.left - theatreRect.left);
const deltaY = dstY - (srcRect.top - theatreRect.top);
requestAnimationFrame(() => {
fly.style.transform =
"translate(" + deltaX + "px, " + deltaY + "px) scale(0.98)";
});
let resolved = false;
const finalize = () => {
if (resolved) return;
resolved = true;
const li = makeWorkItem(intent, name, pid, agent);
targetStack.appendChild(li);
requestAnimationFrame(() => {
li.style.opacity = "1";
li.style.transform = "translateY(0)";
});
fly.remove();
resolve();
};
fly.addEventListener("transitionend", finalize, { once: true });
// Fallback in case transitionend doesn't fire (e.g. tab hidden).
setTimeout(finalize, 1700);
});
}
function setCaption(html, n) {
if (stepText) stepText.innerHTML = html;
if (stepCounter) stepCounter.textContent = n + " / 5";
}
// ── Scripted 5-step play ───────────────────────────────────────
let playing = false;
let playToken = 0;
function wait(ms) {
return new Promise((r) => setTimeout(r, ms));
}
async function play() {
if (playing) return;
playing = true;
const myToken = ++playToken;
resetTheatre();
const stepDelay = reduced ? 0 : 1400;
const aborted = () => myToken !== playToken;
// Step 1
setCaption('<span class="muted">claude-code reads the pool.</span>', 1);
claudePanel.classList.add("is-active");
setStatus("claude", "reading");
await wait(stepDelay);
if (aborted()) { playing = false; return; }
// Step 2 — fly t-2 to claude
const t2 = pool.querySelector('[data-task="t-2"]');
setCaption(
'claude-code claims <strong>[implement] deletion-safety-guard</strong>.',
2
);
if (t2) await flyPacket(t2, "claude");
setStatus("claude", "working");
await wait(reduced ? 0 : 600);
if (aborted()) { playing = false; return; }
// Step 3
setCaption(
'<span class="muted">codex reads the pool — sees claude-code\'s claim.</span>',
3
);
claudePanel.classList.remove("is-active");
codexPanel.classList.add("is-active");
setStatus("codex", "reading");
await wait(stepDelay);
if (aborted()) { playing = false; return; }
// Step 4 — fly t-1 to codex
const t1 = pool.querySelector('[data-task="t-1"]');
setCaption(
'codex picks complementary work — <strong>[test] deletion-safety-tests</strong>.',
4
);
if (t1) await flyPacket(t1, "codex");
setStatus("codex", "working");
await wait(reduced ? 0 : 600);
if (aborted()) { playing = false; return; }
// Step 5 — settle, dim remaining
setCaption(
'Different agents. Different work. <span class="muted">Coordination through shared state.</span>',
5
);
codexPanel.classList.remove("is-active");
const t3 = pool.querySelector('[data-task="t-3"]');
const t4 = pool.querySelector('[data-task="t-4"]');
if (t3) t3.classList.add("dimmed");
if (t4) t4.classList.add("dimmed");
playing = false;
}
// ── Click-to-fire bonus interaction ────────────────────────────
// Dimmed (passed-over) packets stay interactive — clicking re-activates
// them. Already-claimed packets are inert. Mid-play clicks are ignored.
pool.addEventListener("click", (e) => {
const packet = e.target.closest(".packet");
if (!packet) return;
if (
packet.classList.contains("claimed-claude") ||
packet.classList.contains("claimed-codex")
) {
return;
}
playFrom(0);
}
if (playing) return;
function reset() {
clearTimer();
renderStep(0);
}
// Re-activate if dimmed.
packet.classList.remove("dimmed");
// Initial render (step 0 — pristine state).
renderStep(0);
const claudeCount = claudeStack.childElementCount;
const codexCount = codexStack.childElementCount;
const agent = codexCount < claudeCount ? "codex" : "claude";
flyPacket(packet, agent);
});
// Replay button.
// ── Replay button ──────────────────────────────────────────────
if (replayBtn) {
replayBtn.addEventListener("click", () => {
reset();
// Give the DOM one frame to settle before re-playing.
playing = false;
playToken++;
requestAnimationFrame(() => play());
});
}
// Auto-play once on scroll-into-view (or immediately if reduced motion).
// ── Auto-play once on scroll-into-view ─────────────────────────
if (reduced || !("IntersectionObserver" in window)) {
play();
hasPlayed = true;
} else {
let hasPlayed = false;
const playObs = new IntersectionObserver(
(entries) => {
entries.forEach((entry) => {
@ -169,7 +418,7 @@
}
});
},
{ threshold: 0.4 }
{ threshold: 0.18 }
);
playObs.observe(theatre);
}

File diff suppressed because it is too large Load diff