← Strata / docs

Strata · Atlas — Product Requirements Document

The third Strata team after Excavation and the Classification Lab. Atlas extracts the why: the decisions a repository made and the reasoning behind them. Where the Excavator recovers the labor a finished thing hides and the Lab recovers the filing scheme it argued into shape, Atlas recovers the arguments — the choices, the rejected alternatives, the rationale — that the surface erases most completely of all.

Status: planned (the org surface lists it as such, with no count claimed). This document is the plan that earns it a real count.


1. Thesis

A repository records every change, but it does not record, in any structured way, why. The reasoning lives scattered and unindexed: a clause in a commit body, a sentence in a PR description, a Switch X to Y subject line whose because is implied and never written down. Code review happens, decisions are made, alternatives are rejected — and then the merge closes over all of it. Six months later "why is the schema like this?" has no answer but archaeology.

This is the same erasure the rest of Strata studies, one layer deeper. Bowker and Star: infrastructure becomes invisible when it works. A decision becomes invisible the moment it ships, because the shipped code shows only the option that won, never the deliberation that chose it. The losing alternative leaves no trace in the tree — only in the history, and only if someone wrote it down.

Atlas's contribution is not a new source of truth. It is an index over an existing, neglected one. The reasoning is already in the commit graph and the PR record; it is simply unstructured, unlinked, and unsearchable. Atlas mines it into a decision log, links each decision to the code it governs, and answers questions over it.

The method is the argument, as everywhere in Strata: every decision Atlas reports is grounded in a real commit or PR, quoted and attributed, or it is not reported at all.

2. The honest data problem (read this before designing anything)

The single most consequential fact about Atlas, stated up front the way METHODOLOGY.md states the residual-lexicon problem: the why is sparse, latent, and noisy in this repository, and Atlas must never manufacture rationale that the record does not contain.

What a survey of the actual history (1,348 commits, 31 May 2026) shows:

Source of "why"Availability hereQuality
Formal ADRs / decision records0 filesn/a — none exist
PR-reference commits (#NNN in subject)504the richest seam — squash-merge subjects + bodies
Commit bodies (lines beyond the subject)6,714 linesuneven — some real reasoning, much boilerplate
Explicit decision language (because, instead of, switch, replace, drop, avoid, prefer)~181 commitsthe high-signal core
Conventional-commit prefixes (feat/fix/refactor…)partial (~80 commits)a weak type signal, inconsistently applied

The implication for the design:

  • Atlas is an extractor, not an oracle of intent. It surfaces stated reasoning. Where reasoning was never written, Atlas says "decision detected, rationale not recorded" — it does not infer motive. This is the Atlas equivalent of essay No. 10's discipline: where the instrument is blind, it says so loudly.
  • Precision over recall. A decision log of 150 real, well-grounded decisions is worth more than 1,300 noisy ones. The detector should bias toward high-confidence extractions and report its own coverage (how much of the history it could and could not read).
  • The LLM is a structurer, not a source. Where Atlas uses a model (Section 5), the model only reformats and classifies text that is already in the record. It is never asked "why do you think they did this?" — only "extract the decision and its stated reason from this commit/PR, verbatim where possible, and return null if absent."

3. The three agents

Atlas is three agents over one shared artifact (the decision log), mirroring how the Classification Lab is four agents over one dataset.

3.1 Rationale Miner

Extracts decisions and their stated reasoning from commits and PR bodies.

Input: the Bedrock record (commits, subjects, bodies, PR associations). Output: a decisions[] array, one row per detected decision:

{
  id,                  // stable hash of the source commit + slug
  title,               // the decision, as a short imperative ("Replace admin-password auth")
  kind,                // see typology below
  rationale,           // the stated why, quoted/condensed from the record — or null
  rationale_source,    // "subject" | "body" | "pr_description" | "none"
  alternatives,        // rejected options named in the text, if any — usually []
  commit_sha,          // attribution
  pr_number,           // if the decision arrived via a PR
  date,
  confidence,          // 0..100 — how strongly the text reads as a real, reasoned decision
  files_touched_count  // bridge to the Linker (Section 3.2)
}

Decision typology (the Atlas analogue of the Lab's regrading-event typology — a small, defensible, declared set, not an open-ended LLM taxonomy):

  1. replacement — one approach swapped for another (Switch X to Y, Replace A with B). The highest-value kind: it names a loser, which is the trace a decision usually erases.
  2. removal — something deliberately dropped (drop jsdom, remove gamification).
  3. introduction — a new approach adopted where there was none (adopt …, Add … to replace …).
  4. reversal — an earlier decision undone (a later commit that contradicts an earlier one; detected as a pair, windowed, with its parameters declared — never claimed as a primitive).
  5. constraint — a rule asserted (always …, never …, must …), often in docs/config.

kind is assigned by the same most-specific-first priority discipline the Lab uses, and the raw matched phrase is stored regardless of the headline label so an analyst can re-aggregate. A decision with no detectable rationale is still recorded (kind + null rationale) — its absence of stated reasoning is itself data.

3.2 Linker

Connects each decision to the code it governs, so "why is the schema like this" resolves to commits.

The Miner gives the what and why; the Linker gives the where. For each decision it maps the commit's changed paths to the areas the Excavator already knows (the 71 feature areas), producing a bidirectional index:

  • decision → areas it touched (and the single area it most governs, by churn share);
  • area → the decisions that shaped it, newest first.

This is the join that makes Atlas useful rather than merely interesting: it turns a flat decision log into "here is the reasoning behind src/lib/auth," answerable because the Linker bound each auth decision to that path. It reuses the Excavator's area model and the Bedrock file-event record — no new excavation, just a join.

3.3 Oracle

Answers questions over the decision log.

The reader-facing surface. Given a natural-language question ("why did auth change?", "what did we stop using and why?"), the Oracle retrieves the relevant decisions from the log (by area, by kind, by keyword) and answers strictly from grounded rows, each answer citing its commit/PR. It is retrieval-over-a-curated-index, not free generation: if the log has no grounded decision for a question, the Oracle says so rather than inventing one.

The Oracle is the only agent that need not run at excavation time — it serves the committed atlas.json the way the Omnibar serves item search, and is the natural seed of a future /strata/atlas companion surface (Section 7).

4. Outputs & the data contract

Atlas writes one file, matching every other Strata agent: atlas.json in strata/output/, copied to strata/runs/atlas.json by the workflow and served at build time. Shape:

{
  "generated_at": "…",
  "repo_present": "YYYY-MM-DD",
  "coverage": {                      // the honesty block — reported, never hidden
    "commits_scanned": 1348,
    "decisions_detected": 0,         // real count fills on first run
    "with_rationale": 0,             // how many carried a stated why
    "rationale_rate": 0.0,           // with_rationale / decisions_detected
    "unreadable_note": "…"           // what the miner could not parse
  },
  "decisions": [ /* Rationale Miner rows, Linker-augmented with areas */ ],
  "by_area": { "src/lib/auth": ["decision-id", …], … }   // the Linker index
}

coverage.rationale_rate is to Atlas what residual_load is to the Lab and the survival-over-100% count is to essay No. 10: the headline self-measurement that keeps the team honest. A low rate is not a failure to hide — it is the finding that this team writes down its reasoning N% of the time, which is worth knowing.

Source-free by construction, like the digest: Atlas copies decision titles, quoted rationale, paths, SHAs, and counts — never file contents.

5. Method & limits (the METHODOLOGY-style section)

  • Determinism where possible. Detection (typology matching, PR association, path→area linking) is deterministic given the repo and the declared phrase lexicons, exactly like regrader.js. Re-running on the same range reproduces the log.
  • The LLM is bounded and optional. A model may be used only to (a) condense a verbose PR body into a one-line rationale and (b) classify ambiguous kind. It runs per-decision on text already in the record, returns null when no rationale is present, and every model- derived field is flagged (rationale_source distinguishes a quoted line from a model condensation). Atlas must produce a usable log with the LLM off (deterministic lexicon pass only); the model raises recall, never invents content. This mirrors the enrichment pipeline's posture and keeps Atlas runnable in CI without an API key.
  • Declared lexicons, versioned. The decision-language and residual-boilerplate phrase lists are explicit constants (like the residual lexicon), versioned with this document.
  • Known blind spots, stated:
    • Reasoning discussed only in review threads (not echoed into the merge commit or PR body) is invisible — Atlas reads the merged record, not live review conversation.
    • A decision made silently (no descriptive commit) cannot be detected; it surfaces, if at all, only as an unexplained change the Excavator counts but Atlas cannot name.
    • Squash-merge subjects compress multiple decisions into one line; Atlas attributes to the merge and may under-count decisions bundled in a single PR.
    • The model can mis-condense. Hence rationale_source flagging and the deterministic fallback: a reviewer can always read the quoted original.

6. Why it belongs in Strata (fit with the existing teams)

TeamRecoversAtlas's relation
Excavationthe hidden laborAtlas reuses its 71-area model as the Linker's target
Classification Labthe filing scheme over timeAtlas is the same move on decisions instead of categories
Gamesthe abandoned worka reversal (Atlas kind 4) often explains a graveyard death
Curatorthe finished worka constraint (kind 5) often explains why a quiet thing stayed quiet
Atlasthe reasoningthe why beneath all of the above

Atlas closes a real gap: every other team answers what happened; Atlas is the first to answer why. It also feeds the others — a Necromancer death with an Atlas reversal attached is a richer epitaph; the No. 10 essay's "what the graph never records" ledger gains a measured rationale-rate to cite.

7. Surfaces (the Oracle — SHIPPED in v2)

v1 shipped the agent and the data (atlas.json + coverage); v2 ships the Oracle surface at /strata/atlas (surfaces/atlas-oracle.html, served by src/app/strata/atlas/route.js through the shared buildSurface hydrator, the same path the Museum/City/Graveyard/Org routes use). It is a searchable, filterable gallery of the decision log: a free-text box over title + rationale + area, one-tap kind filters (replacement/removal/introduction/constraint, color-coded) and top-area filters, each card showing the decision, its rationale quoted verbatim (or a dashed "no reason recorded" when absent), any dropped alternative, the governing area (clickable to filter), and a link to the real commit. The coverage strip — decisions logged, % with a stated reason — sits at the top, so the honesty metric is the first thing a reader sees. It joins the hub as the fifth companion tile, beside the Organization.

This is retrieval-over-a-curated-index, not generation: the surface only ever shows grounded, committed rows, and an empty search says "the Atlas only knows what was written down" rather than inventing an answer — the Oracle's discipline from §3.3, enforced in the UI.

Still out of scope: a natural-language Q&A front-end (the box is keyword/substring search, not an LLM prompt) and any write-back to the other agents. Those remain future bets; the shipped Oracle is the grounded, deterministic reader the decision log earned.

8. Build plan (the INTEGRATION-style steps)

  1. strata/agents/atlas.js — Rationale Miner + Linker in one agent file, matching the museum.js / cartographer.js shape: reads REPO_PATH, deterministic lexicon pass, optional model pass behind an env flag, writes atlas.json. (The Oracle is a query function over the committed log, not part of the excavation run.)
  2. Orchestrator — add Atlas to run-strata.mjs after the Lab; print an ATLAS section (decisions detected, rationale rate).
  3. build-report.mjs — fold Atlas coverage into the digest's series block, and let buildOrg() read atlas.json so the Organization tile shows the live decision count.
  4. Workflow — extend the strata-run.yml commit loop to include atlas.json.
  5. (Fast follow, separate PR) the /strata/atlas route + Oracle surface.

Each step is independently shippable; v1 is steps 1–4.

9. Success criteria

  • Atlas runs deterministically in CI with the model off and produces a non-empty, fully-grounded decision log on this repository.
  • Every reported decision cites a real commit/PR; zero decisions carry rationale not present in the record (audited against rationale_source).
  • coverage.rationale_rate is reported and believable (spot-checked against the ~181 decision-language commits the survey found).
  • The Linker resolves at least the obvious cases (e.g. the auth-replacement decisions bind to src/lib/auth / src/middleware).
  • The Organization tile shows a real decisions_detected count, moving Atlas from "planned" to "built" honestly — no fabricated number at any point.

10. Decisions (resolved)

The four open questions are settled as follows, and v1 is built to them:

  1. Model off by default in the committed run. The committed atlas.json is produced by the deterministic lexicon pass only, so the public decision count is reproducible without an API key and Atlas runs in CI unchanged. A richer model pass is available behind ATLAS_MODEL=1 for local exploration and flagged per-field (rationale_source), but it never feeds the committed artifact. Matches the enrichment pipeline's gating posture.
  2. Atlas depends on Bedrock PR-enrichment and degrades gracefully. When GITHUB_REPO / GITHUB_TOKEN are present it reads PR descriptions (the richest seam — 504 #NNN commits); when absent it falls back to commit subjects + bodies only, and coverage records which mode produced the log. No hard dependency: a tokenless run still yields a real, smaller log.
  3. Public-log confidence threshold is ≥ 60. Decisions below the bar are detected but held out of the published decisions[]; coverage.excluded_low_confidence reports how many, so the threshold's effect is visible and tunable (the residual-lexicon sensitivity discipline, applied to confidence).
  4. The Oracle is a standalone /strata/atlas surface, not a concierge skill. Atlas keeps its own committed log and its own (deferred) route, consistent with the other Strata surfaces, rather than folding the decision log into the site concierge. This keeps Strata self-contained and the data provenance clean; a concierge bridge can come later over the same atlas.json if wanted. (Route + Oracle UI remain the deferred fast-follow per Section 7 — only the agent + data ship in v1.)