// Article page — centered column, collapsible corner TOC, pinned lab callout
const Article = ({ onNav, theme }) => {
  const [tocOpen, setTocOpen] = React.useState(() => window.innerWidth > 1180);
  const toc = [
    ['s-intro', '§ 00 — the problem'],
    ['s-loop', '§ 01 — the loop, in four lines'],
    ['s-modes', '§ 02 — the three failure modes'],
    ['s-giveup', '§ 03 — knowing when to stop'],
    ['s-eval', '§ 04 — how i evaluate this'],
    ['s-end', '§ 05 — what i\'d change'],
  ];

  return (
    <PageShell topPath="~/writing/the-agentic-loop" topCurrent="./less" nav="writing" onNav={onNav}
      fullBleedChildren={
        <style>{`
          @media (max-width: 1180px) {
            .bwm-toc-rail { position: fixed !important; top: auto !important; bottom: 16px !important; right: 16px !important; width: auto !important; left: auto !important; transform: none !important; }
            .bwm-toc-rail .bwm-toc-panel, .bwm-toc-rail .bwm-lab-pin { display: ${tocOpen ? 'block' : 'none'}; }
            .bwm-toc-rail { width: ${tocOpen ? '260px' : 'auto'} !important; }
          }
        `}</style>
      }>
      <div style={{ position: 'relative' }}>
      {/* Floating TOC — pinned to the right side of the 1200px shell. At narrow widths collapses to a corner dock. */}
      <div className="bwm-toc-rail" style={{
        position: 'absolute', top: 48, right: 0, width: tocOpen ? 240 : 'auto',
        fontFamily: 'var(--mono)', fontSize: 11, zIndex: 5,
      }}>
        <button onClick={() => setTocOpen(o => !o)} style={{
          width: '100%', background: 'var(--ink-2)', border: '1px solid var(--rule)',
          color: 'var(--brass)', padding: '8px 12px', cursor: 'pointer',
          fontFamily: 'inherit', fontSize: 10, letterSpacing: '0.08em', textTransform: 'uppercase',
          display: 'flex', alignItems: 'center', gap: 8, textAlign: 'left',
        }}>
          <span>{tocOpen ? '▾' : '▸'}</span><span>{tocOpen ? 'table of contents' : 'toc'}</span>
        </button>
        {tocOpen && (
          <div className="bwm-toc-panel" style={{ background: 'var(--ink-2)', border: '1px solid var(--rule)', borderTop: 'none', padding: '10px 12px' }}>
            {toc.map((t, i) => (
              <a key={t[0]} href={`#${t[0]}`} style={{
                display: 'block', padding: '5px 0', color: i === 0 ? 'var(--brass)' : 'var(--fg-dim)',
                textDecoration: 'none', borderLeft: i === 0 ? '2px solid var(--brass)' : '2px solid transparent',
                paddingLeft: 10,
              }}>{t[1]}</a>
            ))}
          </div>
        )}

        {/* Pinned lab callout */}
        {tocOpen && (
          <div className="bwm-lab-pin" style={{
            marginTop: 20, background: 'var(--ink-2)', border: '1px solid var(--brass)',
            padding: 14, fontFamily: 'var(--mono)', fontSize: 11,
          }}>
            <div style={{ color: 'var(--brass)', fontSize: 10, textTransform: 'uppercase', letterSpacing: '0.1em', marginBottom: 8 }}>◉ companion lab</div>
            <div style={{ color: 'var(--fg)', fontSize: 12, marginBottom: 6, fontWeight: 600 }}>the-agentic-loop</div>
            <div style={{ color: 'var(--fg-dim)', lineHeight: 1.6, marginBottom: 10 }}>
              Runnable code for everything in this post. Tests, eval harness, CI.
            </div>
            <div style={{ color: 'var(--fg-dimmer)', fontSize: 10 }}>
              $ git clone<br/>
              $ uv sync && pytest
            </div>
            <div style={{ marginTop: 10, display: 'flex', justifyContent: 'space-between', fontSize: 10, color: 'var(--fg-dimmer)' }}>
              <span>284★</span><span>MIT</span><span style={{ color: 'var(--brass)' }}>→ clone</span>
            </div>
          </div>
        )}
      </div>

      {/* Article body */}
      <article style={{ maxWidth: 680, margin: '0 auto', padding: '48px 0 80px', fontFamily: 'var(--mono)' }}>
        <div style={{ fontSize: 11, color: 'var(--brass)', marginBottom: 16 }}>
          ~/writing/2026-04-18-agentic-loop.md · <span style={{ color: 'var(--fg-dimmer)' }}>git HEAD · 3 revisions</span>
        </div>

        <div style={{ display: 'flex', gap: 14, fontSize: 11, color: 'var(--fg-dimmer)', marginBottom: 14 }}>
          <span>2026-04-18 14:32</span>
          <span>·</span>
          <span>14 min read</span>
          <span>·</span>
          <Tag>#agents</Tag>
          <Tag>#patterns</Tag>
        </div>

        <h1 style={{ fontSize: 32, fontWeight: 700, letterSpacing: '-0.03em', lineHeight: 1.1, margin: '0 0 16px', color: 'var(--fg)' }}>
          The agentic loop, explained by its failure modes
        </h1>
        <div style={{ fontSize: 16, color: 'var(--fg-dim)', lineHeight: 1.55, fontStyle: 'italic', borderLeft: '2px solid var(--brass)', paddingLeft: 14, marginBottom: 36 }}>
          A loop isn't clever until it can admit defeat. A field guide to when
          agents give up, when they shouldn't, and the thin line between.
        </div>

        <section id="s-intro" style={{ marginBottom: 32 }}>
          <h2 style={{ fontSize: 18, color: 'var(--brass)', marginTop: 32, marginBottom: 14, letterSpacing: '-0.02em' }}>§ 00 — the problem</h2>
          <p style={{ fontSize: 14, color: 'var(--fg)', lineHeight: 1.75, margin: '0 0 14px' }}>
            Every agent framework in the last eighteen months is the same four-line loop in
            different coats. Plan. Call a tool. Observe. Decide whether to continue. The
            magic is supposed to live in the decision, but in my experience the magic lives in
            the <em style={{ color: 'var(--brass)', fontStyle: 'normal' }}>exit conditions</em> — the things that are supposed to stop the loop.
          </p>
          <p style={{ fontSize: 14, color: 'var(--fg)', lineHeight: 1.75, margin: '0 0 14px' }}>
            This post is a field guide to those exit conditions. I'll show you the failure
            modes I see most often, how I detect them, and the small changes that made my
            agents dramatically more reliable without making them any smarter.
          </p>
        </section>

        <section id="s-loop" style={{ marginBottom: 32 }}>
          <h2 style={{ fontSize: 18, color: 'var(--brass)', marginTop: 32, marginBottom: 14, letterSpacing: '-0.02em' }}>§ 01 — the loop, in four lines</h2>
          <p style={{ fontSize: 14, color: 'var(--fg)', lineHeight: 1.75, margin: '0 0 16px' }}>
            Here is the loop. Every production agent I've shipped is a variation of it.
          </p>
          <CodeBlock
            filename="agent.py · the loop"
            language="python"
            style={theme.codeStyle}
            code={`# the four-line loop every agent is hiding
while not goal.done():
    plan = model.plan(goal, history)
    result = plan.call_tool()
    history.append(result)
    # the interesting part is what we DON'T do here`}
          />
          <p style={{ fontSize: 14, color: 'var(--fg)', lineHeight: 1.75, margin: '0 0 14px' }}>
            Look at the comment. Everything interesting about an agent is the code that
            isn't in that loop — the budget checks, the sanity checks, the
            "am I going in circles" checks.
          </p>
        </section>

        <section id="s-modes" style={{ marginBottom: 32 }}>
          <h2 style={{ fontSize: 18, color: 'var(--brass)', marginTop: 32, marginBottom: 14, letterSpacing: '-0.02em' }}>§ 02 — the three failure modes</h2>
          <p style={{ fontSize: 14, color: 'var(--fg)', lineHeight: 1.75, margin: '0 0 14px' }}>
            In logs of thousands of agent runs, three failure modes come up again and again:
          </p>
          <ol style={{ fontSize: 14, color: 'var(--fg)', lineHeight: 1.8, paddingLeft: 20 }}>
            <li><span style={{ color: 'var(--brass)' }}>The spiral</span> — the agent tries the same tool with the same arguments, three, four, twelve times.</li>
            <li><span style={{ color: 'var(--brass)' }}>The drift</span> — the agent slowly forgets the goal and optimizes for a nearby proxy.</li>
            <li><span style={{ color: 'var(--brass)' }}>The capitulation</span> — the agent gives up too early because one tool returned an empty list.</li>
          </ol>
          <CodeBlock
            filename="guard.py · detecting the spiral"
            language="python"
            style={theme.codeStyle}
            code={`def detect_spiral(history, window=3):
    """Three identical tool calls in a row. Time to intervene."""
    recent = history[-window:]
    if len(recent) < window:
        return False
    sigs = {(h.tool, h.args_hash) for h in recent}
    return len(sigs) == 1`}
          />
        </section>

        <section id="s-giveup" style={{ marginBottom: 32 }}>
          <h2 style={{ fontSize: 18, color: 'var(--brass)', marginTop: 32, marginBottom: 14, letterSpacing: '-0.02em' }}>§ 03 — knowing when to stop</h2>
          <p style={{ fontSize: 14, color: 'var(--fg)', lineHeight: 1.75, margin: '0 0 14px' }}>
            The cheapest way I've found to make agents honest: every tool result gets a
            confidence score from the planner, and the planner must justify continuing
            when that score drops below a threshold.
          </p>
        </section>

        <div style={{ margin: '48px 0', padding: '20px', background: 'var(--ink-2)', border: '1px solid var(--rule)' }}>
          <div style={{ fontSize: 10, color: 'var(--brass)', textTransform: 'uppercase', letterSpacing: '0.1em', marginBottom: 8 }}>── marginal note ──</div>
          <div style={{ fontSize: 13, color: 'var(--fg-dim)', lineHeight: 1.7 }}>
            An agent that can say "I don't know" is more valuable than an agent that's right 95%
            of the time. The 5% is where the production outages live.
          </div>
        </div>

        <div style={{ marginTop: 60, padding: '20px 0', borderTop: '1px solid var(--rule)', borderBottom: '1px solid var(--rule)', display: 'flex', justifyContent: 'space-between', fontSize: 11, color: 'var(--fg-dimmer)' }}>
          <span>← prev: <span style={{ color: 'var(--brass)' }}>hybrid search in three lines</span></span>
          <span>next: <span style={{ color: 'var(--brass)' }}>tool calling is a state machine</span> →</span>
        </div>
      </article>
      </div>
    </PageShell>
  );
};

window.Article = Article;
