/* ============================================================
   Lineage — mock dataset
   A unified, chronological history of a repo where AI agent
   runs and human git commits live on the same spine.
   ============================================================ */

window.LINEAGE_REPO = {
  org: "acme",
  name: "atlas",
  branch: "main",
  desc: "Ingestion + query platform · 312k LOC · TypeScript / Rust",
};

/* Org is the real scope. Agents are global — a session can commit
   across any of these repos in a single run. */
window.LINEAGE_ORG = "acme";
window.LINEAGE_REPOS = [
  { name: "atlas",   color: "#8b7cf6", desc: "Ingestion + query platform" },
  { name: "gateway", color: "#5aa9d6", desc: "Public API gateway" },
  { name: "web",     color: "#d8a05a", desc: "Dashboard web app" },
  { name: "proto",   color: "#5fb89a", desc: "Shared schemas · @acme/proto" },
];

// Headline: cost -> output, last 7 days
window.LINEAGE_METRICS = {
  period: "Last 7 days",
  spend: 1284.6,
  spendDelta: +0.18,
  agentRuns: 342,
  agentRunsDelta: +0.31,
  commits: 489,
  agentAuthored: 217,
  linesAdd: 48210,
  linesDel: 16940,
  mergedPRs: 58,
  costPerPR: 22.15,
  costPerPRDelta: -0.09,
  acceptRate: 0.86,
  // 7-day spend sparkline ($/day)
  spark: [121, 96, 152, 138, 205, 261, 311],
};

/* helper to keep diffs terse */
const H = (path, hunk, lines) => ({ path, hunk, lines });
const L = (t, s) => ({ t, s }); // t: '+', '-', ' '

window.LINEAGE_EVENTS = [
  /* ---------- TODAY ---------- */
  {
    id: "ev-220",
    type: "agent",
    ts: "2026-06-06T09:41:00",
    day: "Today",
    branch: "main",
    agent: { name: "atlas-maint", model: "claude-sonnet-4.5" },
    trigger: "Scheduled · nightly debt sweep",
    task: "Find and remove dead exports across the query package, then make sure the build and tests still pass.",
    summary: "Pruned 23 unused exports in packages/query, tightened 4 barrel files.",
    reasoning: [
      "Built an export reachability graph from the package entrypoints.",
      "Flagged 23 symbols with zero external references; cross-checked against dynamic-import sites.",
      "Removed them in 7 files and collapsed two re-export barrels that became empty.",
      "Ran typecheck + unit suite to confirm nothing downstream broke.",
    ],
    tools: [
      { kind: "search", label: "grep", detail: "export (const|function|class) across 412 files", status: "ok" },
      { kind: "read", label: "read", detail: "packages/query/src/**  · 38 files", status: "ok" },
      { kind: "edit", label: "edit", detail: "7 files · −214 lines", status: "ok" },
      { kind: "run", label: "pnpm typecheck", detail: "0 errors", status: "ok" },
      { kind: "run", label: "pnpm test query", detail: "418 passed", status: "ok" },
    ],
    files: [
      { path: "packages/query/src/index.ts", add: 0, del: 41 },
      { path: "packages/query/src/planner/barrel.ts", add: 2, del: 88 },
      { path: "packages/query/src/exec/index.ts", add: 1, del: 53 },
      { path: "packages/query/src/util/cast.ts", add: 0, del: 32 },
    ],
    diff: [
      H("packages/query/src/planner/barrel.ts", "@@ -1,9 +1,2 @@", [
        L("-", "export * from './hashJoin';"),
        L("-", "export * from './mergeJoin';"),
        L("-", "export * from './nestedLoop'; // unused since v3"),
        L(" ", "export * from './scan';"),
        L("+", "export { planScan } from './scan';"),
      ]),
    ],
    tokens: { in: 184200, out: 12400, cache: 1120000 },
    cost: 3.18,
    duration: 214,
    result: { kind: "commit", hash: "8c1f0ad", status: "merged", pr: null },
  },
  {
    id: "ev-219",
    type: "commit",
    ts: "2026-06-06T09:20:00",
    day: "Today",
    branch: "main",
    author: { name: "Dana Okafor", handle: "dokafor", color: "#6aa6ff" },
    message: "infra: bump ingest worker memory to 2Gi",
    hash: "a3f9c21",
    add: 4, del: 4,
  },
  {
    id: "ev-218",
    type: "agent",
    ts: "2026-06-06T08:52:00",
    day: "Today",
    branch: "fix/ingest-backpressure",
    agent: { name: "claude-code", model: "claude-opus-4.1" },
    trigger: "Dana Okafor · @lineage fix the backpressure stall",
    task: "The /v1/ingest endpoint stalls under burst load and never recovers. Diagnose the backpressure handling and fix it. Add a regression test.",
    summary: "Found unbounded queue growth; added a bounded channel with shed-on-full + a load test.",
    reasoning: [
      "Reproduced the stall with a 50k-event burst against a local worker.",
      "Traced it to an unbounded mpsc channel between the HTTP handler and the writer — memory climbed until the GC thrashed.",
      "Replaced it with a bounded channel (cap 8192) that sheds with 429 + Retry-After when full.",
      "Added a load-test that asserts steady-state throughput recovers within 2s after a burst.",
      "Verified p99 latency dropped from 4.1s to 180ms under the same burst.",
    ],
    tools: [
      { kind: "run", label: "cargo run --bin loadgen", detail: "burst 50k · reproduced stall", status: "ok" },
      { kind: "read", label: "read", detail: "crates/ingest/src/pipeline.rs", status: "ok" },
      { kind: "edit", label: "edit", detail: "pipeline.rs · bounded channel", status: "ok" },
      { kind: "run", label: "cargo test", detail: "first run · 1 failed", status: "warn" },
      { kind: "edit", label: "edit", detail: "fix Retry-After header units", status: "ok" },
      { kind: "run", label: "cargo test", detail: "62 passed", status: "ok" },
    ],
    files: [
      { path: "crates/ingest/src/pipeline.rs", add: 74, del: 21 },
      { path: "crates/ingest/src/handler.rs", add: 18, del: 6 },
      { path: "crates/ingest/tests/backpressure.rs", add: 96, del: 0 },
    ],
    diff: [
      H("crates/ingest/src/pipeline.rs", "@@ -42,9 +42,14 @@ impl Pipeline {", [
        L("-", "let (tx, rx) = mpsc::unbounded_channel();"),
        L("+", "// Bounded: shed load instead of buffering to OOM."),
        L("+", "let (tx, rx) = mpsc::channel(8192);"),
        L(" ", ""),
        L(" ", "tokio::spawn(write_loop(rx, store.clone()));"),
        L("+", "self.tx = tx;"),
      ]),
      H("crates/ingest/src/handler.rs", "@@ -18,3 +18,9 @@ async fn ingest(", [
        L(" ", "match pipe.try_send(batch) {"),
        L("+", "    Err(TrySendError::Full(_)) => return shed(retry_after_ms(80)),"),
        L(" ", "    Err(e) => return Err(e.into()),"),
        L(" ", "    Ok(_) => {}"),
      ]),
    ],
    tokens: { in: 412800, out: 41200, cache: 2840000 },
    cost: 11.74,
    duration: 612,
    result: { kind: "pr", hash: "f20b7e4", status: "open", pr: 1482, reviewers: ["dokafor", "rmehta"] },
  },

  /* ---------- YESTERDAY ---------- */
  {
    id: "ev-214",
    type: "agent",
    ts: "2026-06-05T17:10:00",
    day: "Yesterday",
    branch: "main",
    agent: { name: "atlas-maint", model: "claude-sonnet-4.5" },
    trigger: "Scheduled · dependency audit",
    task: "Apply safe semver-compatible dependency updates and regenerate the lockfile. Skip anything with a breaking changelog.",
    summary: "Updated 11 deps, held back 2 majors flagged as breaking.",
    reasoning: [
      "Listed outdated deps and read each changelog between current and latest compatible.",
      "Applied 11 patch/minor bumps; held axios@2 and vite@6 (breaking notes).",
      "Regenerated lockfile and ran the full suite.",
    ],
    tools: [
      { kind: "run", label: "pnpm outdated", detail: "13 candidates", status: "ok" },
      { kind: "read", label: "read", detail: "13 changelogs", status: "ok" },
      { kind: "edit", label: "edit", detail: "package.json · 11 bumps", status: "ok" },
      { kind: "run", label: "pnpm test", detail: "1,204 passed", status: "ok" },
    ],
    files: [
      { path: "package.json", add: 11, del: 11 },
      { path: "pnpm-lock.yaml", add: 320, del: 286 },
    ],
    diff: [
      H("package.json", "@@ -22,7 +22,7 @@", [
        L("-", '"fastify": "4.26.1",'),
        L("+", '"fastify": "4.28.0",'),
        L("-", '"zod": "3.22.4",'),
        L("+", '"zod": "3.23.8",'),
      ]),
    ],
    tokens: { in: 96400, out: 8200, cache: 540000 },
    cost: 1.92,
    duration: 168,
    result: { kind: "commit", hash: "5db4490", status: "merged", pr: null },
  },
  {
    id: "ev-213",
    type: "commit",
    ts: "2026-06-05T15:48:00",
    day: "Yesterday",
    branch: "main",
    author: { name: "Ravi Mehta", handle: "rmehta", color: "#f0883e" },
    message: "docs: clarify retention window in ingest README",
    hash: "c70aa18",
    add: 22, del: 7,
  },
  {
    id: "ev-212",
    type: "agent",
    ts: "2026-06-05T14:30:00",
    day: "Yesterday",
    branch: "feat/query-cache",
    agent: { name: "claude-code", model: "claude-opus-4.1" },
    trigger: "Ravi Mehta · @lineage add a query result cache",
    task: "Add an LRU result cache to the query executor keyed by normalized query + tenant. Invalidate on writes to touched tables. Target >40% hit rate on the replayed prod workload.",
    summary: "Added a tenant-scoped LRU + table-tag invalidation. Replay shows 52% hit rate.",
    reasoning: [
      "Designed a cache key = sha256(normalizedAST + tenantId) so semantically equal queries collapse.",
      "Tagged each entry with the set of tables it read; writes publish table tags that evict matching entries.",
      "Sized the LRU at 256MB/tenant with a global ceiling; added metrics for hit/miss/evict.",
      "Replayed 1.2M prod queries: 52% hit rate, p50 query time −63%.",
      "Considered a TTL but invalidation-on-write made it unnecessary and safer for correctness.",
    ],
    tools: [
      { kind: "read", label: "read", detail: "packages/query/src/exec/*", status: "ok" },
      { kind: "edit", label: "edit", detail: "new cache.ts · 210 lines", status: "ok" },
      { kind: "edit", label: "edit", detail: "wire invalidation in writer", status: "ok" },
      { kind: "run", label: "pnpm bench replay", detail: "hit 52% · p50 −63%", status: "ok" },
      { kind: "run", label: "pnpm test query", detail: "431 passed", status: "ok" },
    ],
    files: [
      { path: "packages/query/src/exec/cache.ts", add: 210, del: 0 },
      { path: "packages/query/src/exec/executor.ts", add: 34, del: 8 },
      { path: "packages/query/src/write/commit.ts", add: 16, del: 2 },
    ],
    diff: [
      H("packages/query/src/exec/cache.ts", "@@ +1,12 @@ new file", [
        L("+", "export class ResultCache {"),
        L("+", "  private lru = new Lru<string, Rows>({ maxBytes: 256 << 20 });"),
        L("+", "  key(ast: AST, tenant: string) {"),
        L("+", "    return sha256(normalize(ast) + '|' + tenant);"),
        L("+", "  }"),
        L("+", "}"),
      ]),
    ],
    tokens: { in: 528600, out: 58400, cache: 3120000 },
    cost: 16.4,
    duration: 884,
    result: { kind: "pr", hash: "b91c3de", status: "merged", pr: 1471, reviewers: ["rmehta"] },
  },
  {
    id: "ev-210",
    type: "agent",
    ts: "2026-06-05T11:02:00",
    day: "Yesterday",
    branch: "fix/flaky-e2e",
    agent: { name: "flake-hunter", model: "claude-haiku-4.5" },
    trigger: "CI · 3 consecutive flaky runs on e2e/auth",
    task: "The e2e/auth suite fails intermittently in CI. Identify the flake and stabilize it without disabling the test.",
    summary: "Race on token clock; replaced wall-clock sleep with a deterministic fake timer.",
    reasoning: [
      "Pulled the last 30 CI runs; failure correlated with a 50ms expiry sleep.",
      "Root cause: test asserted expiry using real time, flaky on loaded runners.",
      "Swapped to the suite's fake-timer and advanced it explicitly.",
    ],
    tools: [
      { kind: "read", label: "read", detail: "ci logs · 30 runs", status: "ok" },
      { kind: "edit", label: "edit", detail: "e2e/auth/expiry.spec.ts", status: "ok" },
      { kind: "run", label: "test --repeat 200", detail: "200/200 passed", status: "ok" },
    ],
    files: [
      { path: "e2e/auth/expiry.spec.ts", add: 9, del: 6 },
    ],
    diff: [
      H("e2e/auth/expiry.spec.ts", "@@ -30,6 +30,6 @@", [
        L("-", "await sleep(50); // wait for token to expire"),
        L("+", "clock.tick(TOKEN_TTL_MS + 1);"),
      ]),
    ],
    tokens: { in: 42800, out: 3100, cache: 180000 },
    cost: 0.38,
    duration: 96,
    result: { kind: "commit", hash: "2e9f7b0", status: "merged", pr: null },
  },

  /* ---------- JUN 4 ---------- */
  {
    id: "ev-205",
    type: "agent",
    ts: "2026-06-04T16:20:00",
    day: "Jun 4",
    branch: "feat/sso-okta",
    agent: { name: "claude-code", model: "claude-opus-4.1" },
    trigger: "Dana Okafor · @lineage wire up Okta SSO",
    task: "Implement SAML SSO via Okta for the dashboard. Support JIT provisioning and group→role mapping. Document the admin setup.",
    summary: "Added SAML handler, JIT user creation, group mapping, and an admin guide. Flagged a secret-rotation TODO for a human.",
    reasoning: [
      "Implemented the SAML assertion consumer with signature validation against Okta's metadata.",
      "On first login, JIT-provision the user and map Okta groups to Atlas roles via a config table.",
      "Stored the IdP cert in the secrets manager — but rotation policy needs a human decision, left a TODO + opened a checklist.",
      "Wrote an admin runbook with screenshots placeholders for the Okta side.",
    ],
    tools: [
      { kind: "read", label: "read", detail: "auth module · 14 files", status: "ok" },
      { kind: "edit", label: "edit", detail: "saml.ts + provisioning.ts", status: "ok" },
      { kind: "run", label: "pnpm test auth", detail: "88 passed", status: "ok" },
      { kind: "edit", label: "edit", detail: "docs/sso-okta.md", status: "ok" },
      { kind: "flag", label: "human review", detail: "secret rotation policy", status: "warn" },
    ],
    files: [
      { path: "packages/auth/src/saml.ts", add: 188, del: 0 },
      { path: "packages/auth/src/provisioning.ts", add: 74, del: 0 },
      { path: "docs/sso-okta.md", add: 140, del: 0 },
    ],
    diff: [
      H("packages/auth/src/saml.ts", "@@ +1,8 @@ new file", [
        L("+", "export async function consumeAssertion(req: Req) {"),
        L("+", "  const a = await verifySignature(req.body, idpCert());"),
        L("+", "  const user = await jitProvision(a.nameId, a.groups);"),
        L("+", "  return issueSession(user);"),
        L("+", "}"),
      ]),
    ],
    tokens: { in: 690200, out: 72100, cache: 4010000 },
    cost: 21.8,
    duration: 1320,
    result: { kind: "pr", hash: "d4e8a90", status: "open", pr: 1465, reviewers: ["dokafor", "rmehta"] },
  },
  {
    id: "ev-204",
    type: "commit",
    ts: "2026-06-04T13:15:00",
    day: "Jun 4",
    branch: "main",
    author: { name: "Sora Lindqvist", handle: "soral", color: "#a371f7" },
    message: "chore: enable strict null checks in query pkg",
    hash: "9920fce",
    add: 61, del: 48,
  },
  {
    id: "ev-202",
    type: "agent",
    ts: "2026-06-04T10:40:00",
    day: "Jun 4",
    branch: "fix/null-checks",
    agent: { name: "atlas-maint", model: "claude-sonnet-4.5" },
    trigger: "Sora Lindqvist · @lineage fix the strictNullChecks fallout",
    task: "Turning on strictNullChecks surfaced 140 errors in packages/query. Fix them correctly — no non-null assertions unless provably safe.",
    summary: "Resolved 140 null errors with real guards; only 3 provably-safe non-null assertions.",
    reasoning: [
      "Grouped the 140 errors into 6 patterns (optional map gets, unchecked array .find, etc.).",
      "Fixed each with explicit guards or default values rather than blanket `!`.",
      "Used `!` only where an invariant guarantees presence, with a comment each time.",
    ],
    tools: [
      { kind: "run", label: "tsc --noEmit", detail: "140 errors", status: "warn" },
      { kind: "edit", label: "edit", detail: "31 files", status: "ok" },
      { kind: "run", label: "tsc --noEmit", detail: "0 errors", status: "ok" },
      { kind: "run", label: "pnpm test query", detail: "431 passed", status: "ok" },
    ],
    files: [
      { path: "packages/query/src/planner/cost.ts", add: 28, del: 19 },
      { path: "packages/query/src/exec/rows.ts", add: 41, del: 30 },
    ],
    diff: [
      H("packages/query/src/exec/rows.ts", "@@ -88,3 +88,6 @@", [
        L("-", "const col = schema.columns.find(c => c.name === name);"),
        L("-", "return col.type;"),
        L("+", "const col = schema.columns.find(c => c.name === name);"),
        L("+", "if (!col) throw new UnknownColumn(name);"),
        L("+", "return col.type;"),
      ]),
    ],
    tokens: { in: 318400, out: 36900, cache: 1980000 },
    cost: 9.06,
    duration: 540,
    result: { kind: "commit", hash: "1a7c0d5", status: "merged", pr: null },
  },
  {
    id: "ev-200",
    type: "commit",
    ts: "2026-06-04T09:05:00",
    day: "Jun 4",
    branch: "main",
    author: { name: "Dana Okafor", handle: "dokafor", color: "#6aa6ff" },
    message: "Merge pull request #1458 from feat/metrics-v2",
    hash: "77b1e2a",
    add: 0, del: 0,
    merge: true,
  },
];

/* ============================================================
   Repository tree — provenance is who/what last touched it.
   No cost noise here; this is the engineer's code browser.
   by: "agent" | "human"
   ============================================================ */
window.LINEAGE_TREE = [
  { name: ".github", type: "dir", by: "human", who: "Dana Okafor", color: "#6aa6ff", age: "6d", msg: "ci: cache cargo + pnpm stores" },
  { name: "crates", type: "dir", by: "agent", who: "claude-code", age: "3h", msg: "fix backpressure stall on /v1/ingest",
    children: [
      { name: "ingest", type: "dir", by: "agent", who: "claude-code", age: "3h", msg: "bounded ingest channel",
        children: [
          { name: "src", type: "dir", by: "agent", who: "claude-code", age: "3h", msg: "shed-on-full",
            children: [
              { name: "pipeline.rs", type: "file", lang: "rust", by: "agent", who: "claude-code", age: "3h", msg: "bounded channel + shed", file: "crates/ingest/src/pipeline.rs" },
              { name: "handler.rs", type: "file", lang: "rust", by: "agent", who: "claude-code", age: "3h", msg: "return 429 on shed", file: "crates/ingest/src/handler.rs" },
              { name: "lib.rs", type: "file", lang: "rust", by: "human", who: "Ravi Mehta", color: "#f0883e", age: "12d", msg: "scaffold ingest crate" },
            ] },
          { name: "tests", type: "dir", by: "agent", who: "claude-code", age: "3h", msg: "regression test",
            children: [
              { name: "backpressure.rs", type: "file", lang: "rust", by: "agent", who: "claude-code", age: "3h", msg: "burst recovery test", file: "crates/ingest/tests/backpressure.rs" },
            ] },
          { name: "Cargo.toml", type: "file", lang: "toml", by: "human", who: "Ravi Mehta", color: "#f0883e", age: "5d", msg: "bump tokio to 1.38" },
        ] },
    ] },
  { name: "packages", type: "dir", by: "agent", who: "claude-code", age: "1d", msg: "add tenant-scoped query cache",
    children: [
      { name: "query", type: "dir", by: "agent", who: "claude-code", age: "1d", msg: "result cache",
        children: [
          { name: "src", type: "dir", by: "agent", who: "claude-code", age: "1d", msg: "exec/cache",
            children: [
              { name: "exec", type: "dir", by: "agent", who: "claude-code", age: "1d", msg: "LRU cache",
                children: [
                  { name: "cache.ts", type: "file", lang: "ts", by: "agent", who: "claude-code", age: "1d", msg: "tenant-scoped LRU + tag invalidation", file: "packages/query/src/exec/cache.ts" },
                  { name: "executor.ts", type: "file", lang: "ts", by: "agent", who: "claude-code", age: "1d", msg: "wire cache lookup", file: "packages/query/src/exec/executor.ts" },
                ] },
              { name: "planner", type: "dir", by: "human", who: "Sora Lindqvist", color: "#a371f7", age: "2d", msg: "strict null checks" },
              { name: "index.ts", type: "file", lang: "ts", by: "agent", who: "atlas-maint", age: "3h", msg: "prune dead exports", file: "packages/query/src/index.ts" },
            ] },
        ] },
      { name: "auth", type: "dir", by: "agent", who: "claude-code", age: "2d", msg: "Okta SAML SSO",
        children: [
          { name: "src", type: "dir", by: "agent", who: "claude-code", age: "2d", msg: "saml",
            children: [
              { name: "saml.ts", type: "file", lang: "ts", by: "agent", who: "claude-code", age: "2d", msg: "assertion consumer", file: "packages/auth/src/saml.ts" },
            ] },
        ] },
    ] },
  { name: "docs", type: "dir", by: "agent", who: "claude-code", age: "2d", msg: "SSO admin runbook",
    children: [
      { name: "sso-okta.md", type: "file", lang: "md", by: "agent", who: "claude-code", age: "2d", msg: "Okta setup guide", file: "docs/sso-okta.md" },
    ] },
  { name: "Cargo.toml", type: "file", lang: "toml", by: "human", who: "Ravi Mehta", color: "#f0883e", age: "5d", msg: "workspace members" },
  { name: "package.json", type: "file", lang: "json", by: "agent", who: "atlas-maint", age: "1d", msg: "update 11 deps, hold 2 majors", file: "package.json" },
  { name: "README.md", type: "file", lang: "md", by: "human", who: "Sora Lindqvist", color: "#a371f7", age: "1d", msg: "clarify retention window", file: "README.md" },
];

/* repo authorship split — the one signal worth surfacing on the home view */
window.LINEAGE_AUTHORSHIP = {
  agentPct: 61,
  langs: [
    { name: "Rust", pct: 44, color: "#dea584" },
    { name: "TypeScript", pct: 49, color: "#6aa6ff" },
    { name: "Other", pct: 7, color: "#8b8f9a" },
  ],
};

/* ============================================================
   File blame — contiguous blocks, each attributed to the run
   (agent) or commit (human) that authored it. evId links to the
   run so an engineer can read the *intent*, not just the diff.
   ============================================================ */
const CODE = (s) => s.replace(/^\n/, "").replace(/\n$/, "").split("\n");

window.LINEAGE_FILES = {
  "crates/ingest/src/pipeline.rs": {
    lang: "rust",
    blame: [
      { kind: "human", who: "Ravi Mehta", color: "#f0883e", age: "12d", hash: "e1c0a4d", msg: "scaffold ingest crate", code: CODE(`
use tokio::sync::mpsc;
use crate::store::Store;
use crate::error::{Error, Result};

pub struct Pipeline {
    tx: mpsc::Sender<Batch>,
    store: Store,
}
`) },
      { kind: "agent", who: "claude-code", model: "claude-opus-4.1", age: "3h", evId: "ses-backpressure", msg: "bounded channel + shed-on-full", code: CODE(`
impl Pipeline {
    pub fn new(store: Store) -> Self {
        // Bounded: shed load instead of buffering to OOM.
        let (tx, rx) = mpsc::channel(8192);
        tokio::spawn(write_loop(rx, store.clone()));
        Self { tx, store }
    }
`) },
      { kind: "human", who: "Ravi Mehta", color: "#f0883e", age: "9d", hash: "b7740aa", msg: "store accessor", code: CODE(`
    pub fn store(&self) -> &Store {
        &self.store
    }
`) },
      { kind: "agent", who: "claude-code", model: "claude-opus-4.1", age: "3h", evId: "ses-backpressure", msg: "shed on full instead of awaiting", code: CODE(`
    pub async fn ingest(&self, batch: Batch) -> Result<()> {
        match self.tx.try_send(batch) {
            Err(TrySendError::Full(_)) => Err(Error::ShedLoad),
            Err(e) => Err(e.into()),
            Ok(()) => Ok(()),
        }
    }
}
`) },
      { kind: "agent", who: "claude-code", model: "claude-opus-4.1", age: "3h", evId: "ses-backpressure", msg: "drain loop", code: CODE(`
async fn write_loop(mut rx: mpsc::Receiver<Batch>, store: Store) {
    while let Some(batch) = rx.recv().await {
        store.put(batch).await;
    }
}
`) },
    ],
  },
  "packages/query/src/exec/cache.ts": {
    lang: "ts",
    blame: [
      { kind: "agent", who: "claude-code", model: "claude-opus-4.1", age: "1d", evId: "ses-cache", msg: "tenant-scoped LRU result cache", code: CODE(`
import { Lru } from "../util/lru";
import { sha256 } from "../util/hash";
import type { AST, Rows } from "../types";

// Keyed by normalized query + tenant so semantically equal
// queries collapse onto one cache entry.
export class ResultCache {
  private lru = new Lru<string, Rows>({ maxBytes: 256 << 20 });

  key(ast: AST, tenant: string): string {
    return sha256(normalize(ast) + "|" + tenant);
  }

  get(ast: AST, tenant: string): Rows | undefined {
    return this.lru.get(this.key(ast, tenant));
  }
`) },
      { kind: "human", who: "Sora Lindqvist", color: "#a371f7", age: "1d", hash: "9920fce", msg: "tighten types", code: CODE(`
  set(ast: AST, tenant: string, rows: Rows, tables: string[]): void {
    const k = this.key(ast, tenant);
    this.lru.set(k, rows);
    for (const t of tables) this.tagIndex.add(t, k);
  }
`) },
      { kind: "agent", who: "claude-code", model: "claude-opus-4.1", age: "1d", evId: "ses-cache", msg: "evict by table tag on write", code: CODE(`
  // Writes publish the tables they touched; we evict every
  // cached entry that read from any of them.
  invalidate(tables: string[]): void {
    for (const t of tables) {
      for (const k of this.tagIndex.take(t)) this.lru.delete(k);
    }
  }
}
`) },
    ],
  },
};

/* ============================================================
   SESSIONS — an agent session is stored ONCE here. A session may
   produce 0..N commits; commits reference it by sessionId. Orphan
   sessions produced no commit (explored / abandoned / superseded)
   but still cost money. outcome (orphans): explored | abandoned | superseded
   step.t: prompt | think | tool | test | commit
   ============================================================ */
window.LINEAGE_SESSIONS = {
  "ses-prune": {
    id: "ses-prune", agent: { name: "atlas-maint", model: "claude-sonnet-4.5" },
    trigger: "Scheduled · nightly debt sweep", ts: "2026-06-06T09:30:00", day: "Today",
    branch: "main", duration: 214, cost: 3.18, tokens: { in: 184200, out: 12400, cache: 1120000 },
    steps: [
      { t: "prompt", by: { name: "Scheduled", system: true }, text: "Find and remove dead exports across the query package, then make sure the build and tests still pass." },
      { t: "think", text: "I’ll build an export-reachability graph from the package entrypoints, then flag symbols with zero external references." },
      { t: "tool", kind: "search", label: "grep", detail: "export (const|function|class) · 412 files", status: "ok" },
      { t: "tool", kind: "read", label: "read", detail: "packages/query/src/** · 38 files", status: "ok" },
      { t: "think", text: "23 symbols are unreachable. Cross-checking against dynamic-import sites before deleting — none are referenced lazily." },
      { t: "tool", kind: "edit", label: "edit", detail: "7 files · −214 lines", status: "ok" },
      { t: "think", text: "Two re-export barrels became empty; collapsing them." },
      { t: "test", label: "pnpm typecheck", detail: "0 errors", status: "ok" },
      { t: "test", label: "pnpm test query", detail: "418 passed", status: "ok" },
      { t: "commit", hash: "8c1f0ad", text: "refactor(query): prune 23 dead exports" },
    ],
    files: [
      { path: "packages/query/src/index.ts", add: 0, del: 41 },
      { path: "packages/query/src/planner/barrel.ts", add: 2, del: 88 },
      { path: "packages/query/src/exec/index.ts", add: 1, del: 53 },
    ],
    diff: [
      H("packages/query/src/planner/barrel.ts", "@@ -1,9 +1,2 @@", [
        L("-", "export * from './hashJoin';"),
        L("-", "export * from './nestedLoop'; // unused since v3"),
        L(" ", "export * from './scan';"),
        L("+", "export { planScan } from './scan';"),
      ]),
    ],
  },

  "ses-backpressure": {
    id: "ses-backpressure", agent: { name: "claude-code", model: "claude-opus-4.1" },
    trigger: "Dana Okafor · @lineage fix the backpressure stall", ts: "2026-06-06T08:42:00", day: "Today",
    branch: "fix/ingest-backpressure", duration: 612, cost: 11.74, tokens: { in: 412800, out: 41200, cache: 2840000 },
    steps: [
      { t: "prompt", by: { name: "Dana Okafor", color: "#6aa6ff" }, text: "The /v1/ingest endpoint stalls under burst load and never recovers. Diagnose the backpressure handling and fix it. Add a regression test." },
      { t: "think", text: "Let me reproduce first — a 50k-event burst against a local worker should surface it." },
      { t: "tool", kind: "run", label: "cargo run --bin loadgen", detail: "burst 50k · reproduced stall", status: "ok" },
      { t: "think", text: "Memory climbs until the GC thrashes. The mpsc channel between the HTTP handler and writer is unbounded — that’s the leak." },
      { t: "tool", kind: "read", label: "read", detail: "crates/ingest/src/pipeline.rs", status: "ok" },
      { t: "tool", kind: "edit", label: "edit", detail: "bounded channel (cap 8192) + shed", status: "ok" },
      { t: "test", label: "cargo test", detail: "1 failed — Retry-After units", status: "warn" },
      { t: "think", text: "The Retry-After header is in seconds, I emitted ms. Fixing the unit." },
      { t: "tool", kind: "edit", label: "edit", detail: "handler.rs · fix Retry-After", status: "ok" },
      { t: "test", label: "cargo test", detail: "62 passed", status: "ok" },
      { t: "think", text: "p99 under the same burst dropped from 4.1s to 180ms. Recovery is steady within 2s." },
      { t: "commit", hash: "f20b7e4", text: "fix(ingest): bound channel + shed on full" },
    ],
    files: [
      { path: "crates/ingest/src/pipeline.rs", add: 74, del: 21 },
      { path: "crates/ingest/src/handler.rs", add: 18, del: 6 },
      { path: "crates/ingest/tests/backpressure.rs", add: 96, del: 0 },
    ],
    diff: [
      H("crates/ingest/src/pipeline.rs", "@@ -42,9 +42,14 @@ impl Pipeline {", [
        L("-", "let (tx, rx) = mpsc::unbounded_channel();"),
        L("+", "// Bounded: shed load instead of buffering to OOM."),
        L("+", "let (tx, rx) = mpsc::channel(8192);"),
        L(" ", "tokio::spawn(write_loop(rx, store.clone()));"),
      ]),
      H("crates/ingest/src/handler.rs", "@@ -18,3 +18,9 @@ async fn ingest(", [
        L(" ", "match pipe.try_send(batch) {"),
        L("+", "    Err(TrySendError::Full(_)) => return shed(retry_after(1)),"),
        L(" ", "    Ok(_) => {}"),
      ]),
    ],
  },

  "ses-deps": {
    id: "ses-deps", agent: { name: "atlas-maint", model: "claude-sonnet-4.5" },
    trigger: "Scheduled · dependency audit", ts: "2026-06-05T17:04:00", day: "Yesterday",
    branch: "main", duration: 168, cost: 1.92, tokens: { in: 96400, out: 8200, cache: 540000 },
    steps: [
      { t: "prompt", by: { name: "Scheduled", system: true }, text: "Apply safe semver-compatible dependency updates and regenerate the lockfile. Skip anything with a breaking changelog." },
      { t: "tool", kind: "run", label: "pnpm outdated", detail: "13 candidates", status: "ok" },
      { t: "tool", kind: "read", label: "read", detail: "13 changelogs", status: "ok" },
      { t: "think", text: "axios@2 and vite@6 both ship breaking changes — holding those back for a human. The other 11 are patch/minor." },
      { t: "tool", kind: "edit", label: "edit", detail: "package.json · 11 bumps", status: "ok" },
      { t: "test", label: "pnpm test", detail: "1,204 passed", status: "ok" },
      { t: "commit", hash: "5db4490", text: "chore(deps): update 11 deps, hold 2 majors" },
    ],
    files: [
      { path: "package.json", add: 11, del: 11 },
      { path: "pnpm-lock.yaml", add: 320, del: 286 },
    ],
    diff: [
      H("package.json", "@@ -22,7 +22,7 @@", [
        L("-", '"fastify": "4.26.1",'),
        L("+", '"fastify": "4.28.0",'),
        L("-", '"zod": "3.22.4",'),
        L("+", '"zod": "3.23.8",'),
      ]),
    ],
  },

  "ses-cache": {
    id: "ses-cache", agent: { name: "claude-code", model: "claude-opus-4.1" },
    trigger: "Ravi Mehta · @lineage add a query result cache", ts: "2026-06-05T14:15:00", day: "Yesterday",
    branch: "feat/query-cache", duration: 884, cost: 16.4, tokens: { in: 528600, out: 58400, cache: 3120000 },
    steps: [
      { t: "prompt", by: { name: "Ravi Mehta", color: "#f0883e" }, text: "Add an LRU result cache to the query executor keyed by normalized query + tenant. Invalidate on writes to touched tables. Target >40% hit rate on the replayed prod workload." },
      { t: "think", text: "Cache key = sha256(normalizedAST + tenantId) so semantically-equal queries collapse onto one entry." },
      { t: "tool", kind: "read", label: "read", detail: "packages/query/src/exec/*", status: "ok" },
      { t: "tool", kind: "edit", label: "edit", detail: "new cache.ts · 210 lines", status: "ok" },
      { t: "think", text: "Tagging each entry with the tables it read; writes publish table tags that evict matching entries. Safer than a TTL for correctness." },
      { t: "tool", kind: "edit", label: "edit", detail: "wire invalidation into writer", status: "ok" },
      { t: "test", label: "pnpm bench replay", detail: "1.2M queries · hit 52% · p50 −63%", status: "ok" },
      { t: "test", label: "pnpm test query", detail: "431 passed", status: "ok" },
      { t: "commit", hash: "b91c3de", text: "feat(query): tenant-scoped result cache" },
    ],
    files: [
      { path: "packages/query/src/exec/cache.ts", add: 210, del: 0 },
      { path: "packages/query/src/exec/executor.ts", add: 34, del: 8 },
      { path: "packages/query/src/write/commit.ts", add: 16, del: 2 },
    ],
    diff: [
      H("packages/query/src/exec/cache.ts", "@@ +1,6 @@ new file", [
        L("+", "export class ResultCache {"),
        L("+", "  private lru = new Lru<string, Rows>({ maxBytes: 256 << 20 });"),
        L("+", "  key(ast: AST, tenant: string) {"),
        L("+", "    return sha256(normalize(ast) + '|' + tenant);"),
        L("+", "  }"),
      ]),
    ],
  },

  "ses-flake": {
    id: "ses-flake", agent: { name: "flake-hunter", model: "claude-haiku-4.5" },
    trigger: "CI · 3 consecutive flaky runs on e2e/auth", ts: "2026-06-05T10:58:00", day: "Yesterday",
    branch: "fix/flaky-e2e", duration: 96, cost: 0.38, tokens: { in: 42800, out: 3100, cache: 180000 },
    steps: [
      { t: "prompt", by: { name: "CI", system: true }, text: "The e2e/auth suite fails intermittently in CI. Identify the flake and stabilize it without disabling the test." },
      { t: "tool", kind: "read", label: "read", detail: "last 30 CI runs", status: "ok" },
      { t: "think", text: "Failures correlate with a 50ms expiry sleep — the test asserts expiry against real wall-clock, flaky on loaded runners." },
      { t: "tool", kind: "edit", label: "edit", detail: "swap sleep → deterministic fake timer", status: "ok" },
      { t: "test", label: "test --repeat 200", detail: "200/200 passed", status: "ok" },
      { t: "commit", hash: "2e9f7b0", text: "test(auth): de-flake token expiry spec" },
    ],
    files: [{ path: "e2e/auth/expiry.spec.ts", add: 9, del: 6 }],
    diff: [
      H("e2e/auth/expiry.spec.ts", "@@ -30,6 +30,6 @@", [
        L("-", "await sleep(50); // wait for token to expire"),
        L("+", "clock.tick(TOKEN_TTL_MS + 1);"),
      ]),
    ],
  },

  /* one session → TWO commits (code + docs), stored once */
  "ses-sso": {
    id: "ses-sso", agent: { name: "claude-code", model: "claude-opus-4.1" },
    trigger: "Dana Okafor · @lineage wire up Okta SSO", ts: "2026-06-04T16:00:00", day: "Jun 4",
    branch: "feat/sso-okta", duration: 1320, cost: 21.8, tokens: { in: 690200, out: 72100, cache: 4010000 },
    steps: [
      { t: "prompt", by: { name: "Dana Okafor", color: "#6aa6ff" }, text: "Implement SAML SSO via Okta for the dashboard. Support JIT provisioning and group→role mapping. Document the admin setup." },
      { t: "tool", kind: "read", label: "read", detail: "auth module · 14 files", status: "ok" },
      { t: "think", text: "Implementing the assertion consumer with signature validation against Okta’s metadata; on first login I’ll JIT-provision and map groups to roles." },
      { t: "tool", kind: "edit", label: "edit", detail: "saml.ts + provisioning.ts", status: "ok" },
      { t: "test", label: "pnpm test auth", detail: "88 passed", status: "ok" },
      { t: "think", text: "The IdP cert rotation policy is a human decision — stored the cert in the secrets manager and left a TODO + checklist rather than guess.", flag: true },
      { t: "commit", hash: "d4e8a90", text: "feat(auth): Okta SAML SSO with JIT provisioning" },
      { t: "prompt", by: { name: "Ravi Mehta", color: "#f0883e" }, resume: true, from: { name: "Dana Okafor", color: "#6aa6ff" }, text: "Picking this up from Dana — I reviewed the PR. Split the admin runbook into its own commit, and expand the group→role mapping section with our real Okta group names." },
      { t: "think", text: "Resuming the same session with full context. Moving the runbook into its own commit so docs review is independent, and filling in the actual group names from our config." },
      { t: "tool", kind: "edit", label: "edit", detail: "docs/sso-okta.md · 140 lines", status: "ok" },
      { t: "commit", hash: "a91f2c0", text: "docs(auth): Okta admin runbook" },
    ],
    files: [
      { path: "packages/auth/src/saml.ts", add: 188, del: 0 },
      { path: "packages/auth/src/provisioning.ts", add: 74, del: 0 },
      { path: "docs/sso-okta.md", add: 140, del: 0 },
    ],
    diff: [
      H("packages/auth/src/saml.ts", "@@ +1,5 @@ new file", [
        L("+", "export async function consumeAssertion(req: Req) {"),
        L("+", "  const a = await verifySignature(req.body, idpCert());"),
        L("+", "  const user = await jitProvision(a.nameId, a.groups);"),
        L("+", "  return issueSession(user);"),
        L("+", "}"),
      ]),
    ],
  },

  "ses-nullchecks": {
    id: "ses-nullchecks", agent: { name: "atlas-maint", model: "claude-sonnet-4.5" },
    trigger: "Sora Lindqvist · @lineage fix the strictNullChecks fallout", ts: "2026-06-04T10:30:00", day: "Jun 4",
    branch: "fix/null-checks", duration: 540, cost: 9.06, tokens: { in: 318400, out: 36900, cache: 1980000 },
    steps: [
      { t: "prompt", by: { name: "Sora Lindqvist", color: "#a371f7" }, text: "Turning on strictNullChecks surfaced 140 errors in packages/query. Fix them correctly — no non-null assertions unless provably safe." },
      { t: "test", label: "tsc --noEmit", detail: "140 errors", status: "warn" },
      { t: "think", text: "Grouping the 140 into 6 patterns (optional map gets, unchecked array .find, …). I’ll fix each with real guards, not blanket `!`." },
      { t: "tool", kind: "edit", label: "edit", detail: "31 files", status: "ok" },
      { t: "think", text: "Only 3 sites use `!`, each where an invariant guarantees presence — commented inline." },
      { t: "test", label: "tsc --noEmit", detail: "0 errors", status: "ok" },
      { t: "test", label: "pnpm test query", detail: "431 passed", status: "ok" },
      { t: "commit", hash: "1a7c0d5", text: "fix(query): resolve 140 strictNullCheck errors" },
    ],
    files: [
      { path: "packages/query/src/planner/cost.ts", add: 28, del: 19 },
      { path: "packages/query/src/exec/rows.ts", add: 41, del: 30 },
    ],
    diff: [
      H("packages/query/src/exec/rows.ts", "@@ -88,3 +88,5 @@", [
        L("-", "const col = schema.columns.find(c => c.name === name);"),
        L("-", "return col.type;"),
        L("+", "const col = schema.columns.find(c => c.name === name);"),
        L("+", "if (!col) throw new UnknownColumn(name);"),
        L("+", "return col.type;"),
      ]),
    ],
  },

  /* multi-turn, resumed by a different person — Sora starts, Dana takes over */
  "ses-search": {
    id: "ses-search", agent: { name: "claude-code", model: "claude-opus-4.1" },
    trigger: "Sora Lindqvist · @lineage add full-text search over events", ts: "2026-06-03T13:20:00", day: "Jun 3",
    branch: "feat/event-search", duration: 1980, cost: 28.6, tokens: { in: 920000, out: 96000, cache: 5200000 },
    steps: [
      { t: "prompt", by: { name: "Sora Lindqvist", color: "#a371f7" }, text: "Add full-text search over ingested events. Users should be able to query by free text and filter by tenant + time range." },
      { t: "think", text: "Weighing Postgres tsvector vs a dedicated index. At ~2B events tsvector won’t hold; leaning toward an inverted index in the store we already run." },
      { t: "tool", kind: "read", label: "read", detail: "crates/store/src/*", status: "ok" },
      { t: "prompt", by: { name: "Sora Lindqvist", color: "#a371f7" }, text: "We don’t want a new service to operate. Can it live inside the store we already run?" },
      { t: "think", text: "Yes — I can build the inverted index as a column family in the same store, indexed on the write path. No new infra." },
      { t: "tool", kind: "edit", label: "edit", detail: "new search/index.rs · 280 lines", status: "ok" },
      { t: "test", label: "cargo bench index", detail: "1.1M docs/s ingest", status: "ok" },
      { t: "prompt", by: { name: "Sora Lindqvist", color: "#a371f7" }, text: "Nice. What’s query latency at p99 on a prod-sized set?" },
      { t: "test", label: "cargo bench query", detail: "p99 38ms over 2B docs", status: "ok" },
      { t: "think", text: "38ms p99 is well within budget. Wiring the /v1/search endpoint with tenant + time-range filters." },
      { t: "tool", kind: "edit", label: "edit", detail: "api: /v1/search endpoint", status: "ok" },
      { t: "test", label: "cargo test search", detail: "74 passed", status: "ok" },
      { t: "prompt", by: { name: "Dana Okafor", color: "#6aa6ff" }, resume: true, from: { name: "Sora Lindqvist", color: "#a371f7" }, text: "Taking this over while Sora’s out. Before we ship: cap results at 1k per page, and make absolutely sure queries are tenant-scoped — no cross-tenant leakage." },
      { t: "think", text: "Resuming with the full thread. Adding a pagination cap and making the tenant filter mandatory at the query boundary, with a test that proves cross-tenant isolation." },
      { t: "tool", kind: "edit", label: "edit", detail: "paginate + enforce tenant scope", status: "ok" },
      { t: "test", label: "cargo test search::isolation", detail: "12 passed", status: "ok" },
      { t: "prompt", by: { name: "Dana Okafor", color: "#6aa6ff" }, text: "Good. Ship it." },
      { t: "commit", hash: "6d2a9b1", text: "feat(search): inverted-index event search" },
    ],
    files: [
      { path: "crates/store/src/search/index.rs", add: 280, del: 0 },
      { path: "crates/api/src/routes/search.rs", add: 96, del: 0 },
    ],
    diff: [
      H("crates/store/src/search/index.rs", "@@ +1,6 @@ new file", [
        L("+", "pub struct InvertedIndex { cf: ColumnFamily }"),
        L("+", "impl InvertedIndex {"),
        L("+", "  pub fn insert(&self, doc: DocId, terms: &[Term]) {"),
        L("+", "    for t in terms { self.cf.append(t, doc); }"),
        L("+", "  }"),
        L("+", "}"),
      ]),
    ],
  },

  /* GLOBAL agent — one command, commits across THREE repos */
  "ses-protobump": {
    id: "ses-protobump", agent: { name: "atlas-maint", model: "claude-sonnet-4.5" },
    trigger: "Dana Okafor · lineage run --all \"bump @acme/proto to v3 and regenerate\"", ts: "2026-06-06T10:24:00", day: "Today",
    branch: "chore/proto-v3", duration: 942, cost: 14.2, tokens: { in: 612000, out: 58000, cache: 3380000 },
    global: true,
    steps: [
      { t: "prompt", by: { name: "Dana Okafor", color: "#6aa6ff" }, text: "Bump @acme/proto to v3 across every service that depends on it and regenerate the generated code. Open a PR per repo." },
      { t: "think", text: "@acme/proto v3 adds trace_id to the Event message. Three repos depend on it: atlas, gateway, web. I’ll update each, regenerate, and run each repo’s build independently." },
      { t: "tool", kind: "search", label: "scan org", detail: "@acme/proto dependents · 3 repos", status: "ok" },
      { t: "tool", kind: "edit", label: "atlas · edit", detail: "bump dep + buf generate", status: "ok" },
      { t: "test", label: "atlas · cargo build", detail: "ok", status: "ok" },
      { t: "commit", hash: "1f4a2c0", text: "chore(proto): regenerate for @acme/proto v3", repo: "atlas" },
      { t: "tool", kind: "edit", label: "gateway · edit", detail: "bump dep + buf generate", status: "ok" },
      { t: "test", label: "gateway · pnpm build", detail: "ok", status: "ok" },
      { t: "commit", hash: "9e7b3d1", text: "chore(proto): regenerate for @acme/proto v3", repo: "gateway" },
      { t: "tool", kind: "edit", label: "web · edit", detail: "bump dep + buf generate", status: "ok" },
      { t: "test", label: "web · pnpm build", detail: "ok", status: "ok" },
      { t: "commit", hash: "c2a8f50", text: "chore(proto): regenerate for @acme/proto v3", repo: "web" },
      { t: "think", text: "All three green. Opened a PR in each repo — they should land together to avoid a wire-format skew between services." },
    ],
    files: [
      { path: "atlas · crates/proto/mod.rs", add: 90, del: 60 },
      { path: "gateway · src/proto/index.ts", add: 48, del: 35 },
      { path: "web · src/proto/index.ts", add: 42, del: 32 },
    ],
    diff: [
      H("atlas · Cargo.toml", "@@ -8,1 +8,1 @@", [
        L("-", 'acme-proto = "2.4"'),
        L("+", 'acme-proto = "3.0"'),
      ]),
    ],
  },

  /* ───────── ORPHAN SESSIONS — spend, no commit ───────── */
  "ses-memleak": {
    id: "ses-memleak", agent: { name: "claude-code", model: "claude-opus-4.1" },
    trigger: "Ravi Mehta · @lineage the dashboard leaks memory in prod", ts: "2026-06-06T07:10:00", day: "Today",
    branch: "investigate/dash-memory", duration: 720, cost: 6.42, tokens: { in: 388000, out: 22600, cache: 2010000 },
    repo: "web", outcome: "explored",
    note: "Root cause is upstream in a third-party charting library, not our code. No change made — findings filed as an issue.",
    steps: [
      { t: "prompt", by: { name: "Ravi Mehta", color: "#f0883e" }, text: "Dashboard memory climbs ~4% per hour in prod and never plateaus. Find the leak." },
      { t: "think", text: "I’ll reproduce with a 6-hour soak locally and diff heap snapshots to find the retained set." },
      { t: "tool", kind: "run", label: "node --inspect soak.js", detail: "heap +220MB over 6h", status: "ok" },
      { t: "tool", kind: "read", label: "heap snapshots", detail: "3 snapshots · retained-size diff", status: "ok" },
      { t: "think", text: "The retained path traces into recharts’ tooltip cache — detached DOM nodes held by the library, not by our components." },
      { t: "tool", kind: "search", label: "grep", detail: "recharts usage · 18 files", status: "ok" },
      { t: "think", text: "This is an upstream bug. Patching their internals would be fragile; the right move is to file upstream and leave our code untouched until there's a fix or a clean workaround.", flag: true },
      { t: "tool", kind: "flag", label: "open issue", detail: "recharts#4821 · detached nodes in tooltip cache", status: "ok" },
    ],
    files: [],
    diff: [],
  },

  "ses-coldstart": {
    id: "ses-coldstart", agent: { name: "atlas-maint", model: "claude-sonnet-4.5" },
    trigger: "Scheduled · perf budget watch", ts: "2026-06-05T20:40:00", day: "Yesterday",
    branch: "perf/coldstart", duration: 360, cost: 3.10, tokens: { in: 162000, out: 14800, cache: 880000 },
    repo: "atlas", outcome: "abandoned",
    note: "The approach regressed the benchmark and was reverted. No commit — left notes on the ticket for a human.",
    steps: [
      { t: "prompt", by: { name: "Scheduled", system: true }, text: "Cold start on the query worker is 1.8s. Try to get it under 1s." },
      { t: "think", text: "Hypothesis: lazily load the planner’s rule tables instead of initializing them eagerly at boot." },
      { t: "tool", kind: "edit", label: "edit", detail: "lazy-init planner rules", status: "ok" },
      { t: "test", label: "pnpm bench coldstart", detail: "1.9s (+0.1s) — regressed", status: "warn" },
      { t: "think", text: "Lazy init just moved the cost to the first query and tripped a thundering-herd on warmup. Net worse. Reverting." },
      { t: "tool", kind: "edit", label: "revert", detail: "restore eager init", status: "ok" },
      { t: "think", text: "No safe win here without a larger redesign of the rule loader. Abandoning and documenting the dead end on the ticket." },
    ],
    files: [],
    diff: [],
  },

  "ses-ratelimit": {
    id: "ses-ratelimit", agent: { name: "claude-code", model: "claude-opus-4.1" },
    trigger: "Ravi Mehta · @lineage draft a public-API rate limiter", ts: "2026-06-06T06:20:00", day: "Today",
    branch: "draft/rate-limit", duration: 540, cost: 8.90, tokens: { in: 296000, out: 33400, cache: 1640000 },
    repo: "gateway", outcome: "superseded",
    note: "A working draft was handed to a human, who committed the shipped version manually. If that commit came from this work, link attribution to record it.",
    steps: [
      { t: "prompt", by: { name: "Ravi Mehta", color: "#f0883e" }, text: "Draft a per-tenant token-bucket rate limiter for the public API. 1000 req/min default, burst 50." },
      { t: "think", text: "Token bucket in a shared Redis hash keyed by tenant, refilled lazily on read so there’s no background timer." },
      { t: "tool", kind: "read", label: "read", detail: "packages/api/src/middleware/*", status: "ok" },
      { t: "tool", kind: "edit", label: "edit", detail: "new ratelimit.ts · 130 lines", status: "ok" },
      { t: "test", label: "pnpm test api", detail: "54 passed", status: "ok" },
      { t: "think", text: "Working draft on a branch. The header format and 429 copy are product decisions, so I’m handing this to Ravi rather than shipping it myself." },
    ],
    files: [{ path: "packages/api/src/middleware/ratelimit.ts", add: 130, del: 0 }],
    diff: [
      H("packages/api/src/middleware/ratelimit.ts", "@@ +1,6 @@ new file", [
        L("+", "export async function rateLimit(tenant: string) {"),
        L("+", "  const b = await bucket(tenant);     // refill lazily"),
        L("+", "  if (b.tokens < 1) return tooMany(b.resetMs);"),
        L("+", "  await b.take(1);"),
        L("+", "}"),
      ]),
    ],
  },
};

/* ============================================================
   COMMITS — a normal git history. Each commit references the
   session that produced it by sessionId (or null for human work).
   ============================================================ */
window.LINEAGE_COMMITS = [
  /* ── today ── */
  /* cross-repo: ONE session (ses-protobump) produced these three */
  { hash: "1f4a2c0", repo: "atlas", day: "Today", ts: "2026-06-06T10:34:00", branch: "chore/proto-v3",
    message: "chore(proto): regenerate for @acme/proto v3", parent: "8c1f0ad",
    author: { kind: "agent", name: "atlas-maint", model: "claude-sonnet-4.5" },
    add: 90, del: 60, ci: "passed", pr: 1488, sessionId: "ses-protobump" },
  { hash: "9e7b3d1", repo: "gateway", day: "Today", ts: "2026-06-06T10:33:00", branch: "chore/proto-v3",
    message: "chore(proto): regenerate for @acme/proto v3", parent: "77c01aa",
    author: { kind: "agent", name: "atlas-maint", model: "claude-sonnet-4.5" },
    add: 48, del: 35, ci: "passed", pr: 1489, sessionId: "ses-protobump" },
  { hash: "c2a8f50", repo: "web", day: "Today", ts: "2026-06-06T10:32:00", branch: "chore/proto-v3",
    message: "chore(proto): regenerate for @acme/proto v3", parent: "b0d41e9",
    author: { kind: "agent", name: "atlas-maint", model: "claude-sonnet-4.5" },
    add: 42, del: 32, ci: "passed", pr: 1490, sessionId: "ses-protobump" },

  { hash: "3b8e1f2", repo: "gateway", day: "Today", ts: "2026-06-06T10:05:00", branch: "main",
    message: "feat(api): token-bucket rate limiter", parent: "9e7b3d1",
    author: { kind: "human", name: "Ravi Mehta", color: "#f0883e" },
    add: 142, del: 4, ci: "passed", pr: null, sessionId: null },

  { hash: "8c1f0ad", repo: "atlas", day: "Today", ts: "2026-06-06T09:41:00", branch: "main",
    message: "refactor(query): prune 23 dead exports", parent: "a3f9c21",
    author: { kind: "agent", name: "atlas-maint", model: "claude-sonnet-4.5" },
    add: 6, del: 214, ci: "passed", pr: null, sessionId: "ses-prune" },

  { hash: "a3f9c21", repo: "atlas", day: "Today", ts: "2026-06-06T09:20:00", branch: "main",
    message: "infra: bump ingest worker memory to 2Gi", parent: "f20b7e4",
    author: { kind: "human", name: "Dana Okafor", color: "#6aa6ff" },
    add: 4, del: 4, ci: "passed", pr: null, sessionId: null },

  { hash: "f20b7e4", repo: "atlas", day: "Today", ts: "2026-06-06T08:52:00", branch: "fix/ingest-backpressure",
    message: "fix(ingest): bound channel + shed on full", parent: "5db4490",
    author: { kind: "agent", name: "claude-code", model: "claude-opus-4.1" },
    add: 188, del: 27, ci: "passed", pr: 1482, sessionId: "ses-backpressure" },

  /* ── yesterday ── */
  { hash: "5db4490", repo: "atlas", day: "Yesterday", ts: "2026-06-05T17:10:00", branch: "main",
    message: "chore(deps): update 11 deps, hold 2 majors", parent: "c70aa18",
    author: { kind: "agent", name: "atlas-maint", model: "claude-sonnet-4.5" },
    add: 331, del: 297, ci: "passed", pr: null, sessionId: "ses-deps" },

  { hash: "c70aa18", repo: "atlas", day: "Yesterday", ts: "2026-06-05T15:48:00", branch: "main",
    message: "docs: clarify retention window in ingest README", parent: "b91c3de",
    author: { kind: "human", name: "Ravi Mehta", color: "#f0883e" },
    add: 22, del: 7, ci: null, pr: null, sessionId: null },

  { hash: "b91c3de", repo: "atlas", day: "Yesterday", ts: "2026-06-05T14:30:00", branch: "feat/query-cache",
    message: "feat(query): tenant-scoped result cache", parent: "2e9f7b0",
    author: { kind: "agent", name: "claude-code", model: "claude-opus-4.1" },
    add: 260, del: 10, ci: "passed", pr: 1471, sessionId: "ses-cache" },

  { hash: "2e9f7b0", repo: "atlas", day: "Yesterday", ts: "2026-06-05T11:02:00", branch: "fix/flaky-e2e",
    message: "test(auth): de-flake token expiry spec", parent: "a91f2c0",
    author: { kind: "agent", name: "flake-hunter", model: "claude-haiku-4.5" },
    add: 9, del: 6, ci: "passed", pr: null, sessionId: "ses-flake" },

  /* ── jun 4 — two commits from ONE session (ses-sso) ── */
  { hash: "a91f2c0", repo: "atlas", day: "Jun 4", ts: "2026-06-04T16:24:00", branch: "feat/sso-okta",
    message: "docs(auth): Okta admin runbook", parent: "d4e8a90",
    author: { kind: "agent", name: "claude-code", model: "claude-opus-4.1" },
    add: 140, del: 0, ci: "passed", pr: 1465, sessionId: "ses-sso" },

  { hash: "d4e8a90", repo: "atlas", day: "Jun 4", ts: "2026-06-04T16:20:00", branch: "feat/sso-okta",
    message: "feat(auth): Okta SAML SSO with JIT provisioning", parent: "9920fce",
    author: { kind: "agent", name: "claude-code", model: "claude-opus-4.1" },
    add: 262, del: 0, ci: "passed", pr: 1465, sessionId: "ses-sso" },

  { hash: "9920fce", repo: "atlas", day: "Jun 4", ts: "2026-06-04T13:15:00", branch: "main",
    message: "chore(query): enable strict null checks", parent: "1a7c0d5",
    author: { kind: "human", name: "Sora Lindqvist", color: "#a371f7" },
    add: 61, del: 48, ci: "passed", pr: null, sessionId: null },

  { hash: "1a7c0d5", repo: "atlas", day: "Jun 4", ts: "2026-06-04T10:40:00", branch: "fix/null-checks",
    message: "fix(query): resolve 140 strictNullCheck errors", parent: "6d2a9b1",
    author: { kind: "agent", name: "atlas-maint", model: "claude-sonnet-4.5" },
    add: 69, del: 49, ci: "passed", pr: null, sessionId: "ses-nullchecks" },

  /* ── jun 3 ── */
  { hash: "6d2a9b1", repo: "atlas", day: "Jun 3", ts: "2026-06-03T16:40:00", branch: "feat/event-search",
    message: "feat(search): inverted-index event search", parent: "77b1e2a",
    author: { kind: "agent", name: "claude-code", model: "claude-opus-4.1" },
    add: 376, del: 0, ci: "passed", pr: 1452, sessionId: "ses-search" },

  { hash: "77b1e2a", repo: "atlas", day: "Jun 3", ts: "2026-06-03T09:05:00", branch: "main",
    message: "Merge pull request #1448 from feat/metrics-v2", parent: "e1c0a4d",
    author: { kind: "human", name: "Dana Okafor", color: "#6aa6ff" },
    add: 0, del: 0, ci: "passed", pr: 1448, merge: true, sessionId: null },
];

/* ============================================================
   Resolvers — links is a { [hash]: sessionId } map of MANUAL
   attributions layered on top of the intrinsic sessionId.
   ============================================================ */
window.sessionForCommit = function (commit, links) {
  const sid = commit.sessionId || (links && links[commit.hash]);
  return sid ? window.LINEAGE_SESSIONS[sid] : null;
};
window.attributionOf = function (commit, links) {
  if (commit.sessionId) return "auto";
  if (links && links[commit.hash]) return "manual";
  return null;
};
window.commitsForSession = function (sid, links) {
  const auto = window.LINEAGE_COMMITS.filter(c => c.sessionId === sid);
  const manual = window.LINEAGE_COMMITS.filter(c => !c.sessionId && links && links[c.hash] === sid);
  return { auto, manual, all: auto.concat(manual) };
};
window.sessionTask = function (s) {
  const p = s.steps.find(st => st.t === "prompt");
  return p ? p.text : "";
};
window.sessionList = function () {
  return Object.keys(window.LINEAGE_SESSIONS).map(k => window.LINEAGE_SESSIONS[k]);
};
window.isOrphan = function (sid, links) {
  return window.commitsForSession(sid, links).all.length === 0;
};

/* repos a session touched (from its commits, or its declared repo if orphan) */
window.repoMeta = function (name) {
  return window.LINEAGE_REPOS.find(r => r.name === name) || { name: name, color: "var(--tx-3)" };
};
window.sessionRepos = function (sid, links) {
  const out = [], seen = {};
  window.commitsForSession(sid, links).all.forEach(c => {
    if (c.repo && !seen[c.repo]) { seen[c.repo] = 1; out.push(c.repo); }
  });
  if (out.length === 0) {
    const s = window.LINEAGE_SESSIONS[sid];
    if (s && s.repo) out.push(s.repo);
  }
  return out;
};
window.sessionTurns = function (s) {
  return s.steps.filter(st => st.t === "prompt").length;
};
window.sessionParticipants = function (s) {
  const out = [], seen = {};
  s.steps.forEach(st => {
    if (st.t === "prompt" && st.by && !st.by.system && !seen[st.by.name]) {
      seen[st.by.name] = 1; out.push(st.by);
    }
  });
  return out;
};
window.sessionResumed = function (s) {
  return window.sessionParticipants(s).length > 1 || s.steps.some(st => st.resume);
};
