← the Atlas

The kolu Atlas

Reference·budding·implemented·

kolu's in-repo second brain — what it is, how it works, how it compares to Claude Code Artifacts, and the plan to scale it.

This is the Atlas’s note about itself — authored as MDX, living in the Atlas it describes (docs/atlas/), rendered to a self-contained docs/atlas/dist/meta.html you read in the Code tab. No dev server. It covers the whole thing in one place: what the Atlas is and the rule for what goes in it, how it’s built, how it compares to Artifacts in Claude Code (cloud-hosted, governed pages generated from a session — not a substitute for this), and the plan to scale it without losing what makes it ours.

today proposed Author — docs/atlas/src/content/atlas/*.mdx markdown / MDX + typed component kit frontmatter: kind · maturity · status · parents proposed: + tags · supersedes (typed, auto-inverse relations) just atlas::build Build — Astro (self-contained project) proposed: atlasGraph — byTag · edges · backlinks fail-fast on any dead internal link render → committed dist/<slug>.html (inlined, relative links) proposed: + Pagefind static search index ci::atlas-sync — rebuild must match committed bytes two surfaces · one build Kolu Code tab local · offline · no server kolu.dev/atlas/ public mirror + public:true gate (proposed) later — only when a 2nd repo exists Later — scale to many projects Option A: + project field + slug prefix — one repo, no new machinery Option C: aggregator folds per-repo dist (Astro Content Layer) → kolu.dev/atlas/<project>/<slug> · Pagefind catalog · Git-perm ACL Claude Artifacts cloud single page (claude.ai) governed · org-auth · versioned capture-of-work — not a corpus ≠ Atlas — different job, no shared pipeline
The Atlas is a build pipeline (blue = today, green = proposed): MDX → Astro → committed self-contained HTML, rendered in both Kolu's Code tab and the public mirror. Artifacts (right, detached) shares none of it — a different job by construction.

What the Atlas is

Premise: you already have a second brain — assign roles, don’t build a new system. kolu’s existing stores:

The one routing rule decides where a thing goes:

Substantial, structured artifact — or lightweight, transient node?

Substantial → an Atlas note Lightweight → a GitHub Issue
Proposals, designs, features, analyses, bug investigations, history Quick bug tickets, tasks, roadmap items, questions

The surfaces it sits between:

Surface Where Role
Public the blog (kolu.dev) + per-release changelog outward-facing; one post per release
Atlas docs/atlas/docs/atlas/dist/ the working brain; markdown/MDX notes, internal-first

History isn’t a third place — it’s the Atlas over time: a settled note is just evergreen, git is the history, the changelog is the release artifact.

How it works

docs/atlas/ is its own little Astro project — decoupled from the public website/ (different audience + cadence), with its committed dist/ folded into kolu.dev/atlas/ at the website’s build.

Format — markdown prose + MDX components

Why markdown for prose Evidence
Far fewer tokens — paid on every agent read Cloudflare: ~80% fewer tokens md vs html
The CLIs kolu runs prefer it Claude Code & OpenCode send Accept: text/markdown
Renders where it matters github.com renders .md; the Code tab renders it (#1093)

Proposals — the contributor intake lane

The component kit

Because a note can be .mdx, every component below is a live import from docs/atlas/src/components/ — rendered here, not screenshotted. Props are typechecked at build.

Inline chips

Small typed references for the prose:

Component Live Usage
<PrLink> — GitHub PR, repo baked in #1095 <PrLink pr={1095} />
<Issue> — GitHub issue #951 <Issue n={951} />
<Commit> — short sha → commit 7ec2566 <Commit sha="7ec2566a" />
<Cite> — a linked file:line docs/atlas/astro.config.mjs:14-19 <Cite file="…" lines="14-19" />
<Kbd> — a keyboard chord Ctrl +B <Kbd keys="Ctrl+B" />

Block components

<Callout> — a typed box with markdown inside. kind: note · accent · good · warn · danger. Usage: <Callout kind="warn" title="…">body</Callout>.

<Terminal> — a faux transcript. Usage: <Terminal title="…" lines={["$ cmd", "output"]} />; in lines, $ is a prompt + command, # a comment, anything else is output.

kolu — just atlas::build
$ just atlas::build
✓ Completed in 1.59s.
# self-contained dist/ previews in the Code tab

<Svg> + <D2> — an architecture diagram, inlined as self-contained SVG: hand-author the SVG (<Svg svg={…} />, the pipeline above) for full visual control, or let <D2> lay one out from a graph DSL. <AtlasMockup> — a one-off, self-contained HTML + inline-SVG prototype, for when a note needs something markdown can’t draw:

atlas / meta.html
A note grows up
seedlingbuddingevergreen

<Toc> — a nested, auto-inserted table of contents (the Contents box at the top), built from the note’s headings; notes never import it. <Roadmap> + <Milestone> — a status-marked roadmap (done ✓ · now ▸ · next ○); see The roadmap below for the live one.

Bring your own component

A note isn’t limited to the shared kit — but a note-local component lives in the .mdx itself, never as a separate file (it keeps the note self-contained; src/components/ is reserved for components reused across notes). Two ways:

  1. Inline export const at the top of the .mdx. Defined here as Spark, used live: inline.
  2. Raw inline markup — for a true one-off, drop JSX inline with no named component: raw inline JSX.

Promotion path: inline → shared kit, once a component proves reused (and earns scoped styles + typed props as a real .astro).

Two jobs, not substitutes

With Artifacts in Claude Code shipped, the obvious question is whether it replaces the Atlas. It doesn’t — the clean tell is the unit each optimizes. The Atlas optimizes the corpus — a navigable, ever-living tree of interlinked notes an agent treats as durable memory, reviewed through the same PR/diff machinery as code. Artifacts optimize the single page — one session’s investigation handed as a governed, link-shareable snapshot to a teammate (often a non-engineer), deliberately not a searchable library.

Axis kolu Atlas Claude Code Artifacts
Locality Source and built dist/<slug>.html live in Git. No server, no account, no vendor to read a note. Cloud-only on Anthropic infra at a private claude.ai/code/artifact/<uuid> URL. No self-host (cannot be forked).
Format / agent cost Authored as markdown/MDX; structure from a typed kit. The source is what agents re-read — markdown token rates, the format CLIs prefer. Generated styled HTML; Anthropic’s own docs note it’s more token-intensive to re-ingest at scale.
Render surface Two from one build: Kolu’s Code tab (local, offline) and kolu.dev/atlas/. One: the claude.ai viewer behind org auth. No in-IDE embed, no public URL.
Organization A graph over the notes + their parents; categories are moc notes, backlinks derived. No full-text search yet. Flat single-author gallery, opaque UUID keys; “a capture of work, not an application.”
Versioning Git is the history — full blame/diff/PR review. First-class in-product versions at one URL + restore; but no diff/blame, and a stray session silently forks.
Access control None at note level — internal repo + a public mirror. Compliance-grade: private-by-default, org-auth only, never public; audit + retention + Compliance API.
Discovery at scale Deterministic tree at ~50 notes; no full-text search UI. Weak by design — flat galleries, no search/folders.
Audience Internal engineering + the agents themselves. Cross-role org — explicitly legal, security, SRE, management.
Cost / gating Free, ungated, vendor-independent. Beta, Team/Enterprise only; interactive claude.ai login; extra tokens per generate.
Longevity Durable, portable, offline, CI-checked. You own it forever. Vendor-bound; a retention policy auto-deletes; export the HTML to survive.

Where the Atlas genuinely wins: it renders locally in Kolu with zero network; markdown/MDX is the cheapest thing an agent can re-read (~80% fewer tokens than HTML — Cloudflare’s figure, see Format above; it’s the format CLIs request); Git is the history; and it’s graph-able by construction, because note-to-note edges already exist as plain ./slug.html links. Where Artifacts win, decisively: enterprise governance, cross-role reach to people who will never open the repo, zero-build cloud sharing, and in-product version restore for non-git users.

Scaling the Atlas

The worry is that the Atlas won’t survive going from one repo / ~50 notes to many projects. It’s real, but mostly contingent — and the cheap within-repo wins come first.

What breaks at multi-project scale

Ranked by real bite. The first cluster is critical only under a multi-team / separate-repo / confidential-project future the repo hasn’t actually adopted; the second bites even a single growing repo.

Contingent on going multi-repo / multi-team:

  1. Flat slug = filename = URL. Two projects’ release-workflow.mdx collide. Within one repo it’s a prefix convention, not a wall.
  2. No project axis (docs/atlas/src/content.config.ts) — project A’s and B’s bugs file under the same bug index and interleave.
  3. Relative cross-links are load-bearing for both render targets (the Code-tab iframe and the cp -r dist in website/default.nix). A note in another repo isn’t a sibling file, so ./other.html 404s — the sharpest real constraint on any federation, and the reason not to build it speculatively.
  4. No access control + unconditional public mirror. website/default.nix folds all of dist/ into /atlas/ on merge; draft:true only hides from the index (it still builds and publishes). Public-by-merge is the intended contract today — and a breach the moment a confidential project lands.

Bites even a single growing repo:

  1. Committed dist/ churn — ~71 KB of minified HTML per note; every edit re-emits the whole file, and ci::atlas-sync does a full build + a scrambled-TZ idempotency build, so CI cost scales with note count. This argues against inlining a heavy graph into every page.
  2. No full-text search UI — the graph + hub cards are the browse at ~50 notes; at a few hundred there’s no escape hatch.
  3. One generated entry page, no pagination — the index cards (every note listed) become an unscrollable wall at 500+.
  4. The force graph turns into a hairball past a few hundred nodes (docs/atlas/src/lib/graphView.ts) — it stops being legible long before it stops building; it needs filtering / focus-by-hub / an orphan view (the graph’s own roadmap).

Fixing the tree/graph

The index notes are a lifecycle/type axis, not a topic — non-monotonic (a feature becomes reference), so a note’s index parent changes on a lifecycle flip while the stable thing (topic) isn’t an axis at all. Right-sized redesign, by leverage-per-line:

  1. Derive backlinks from the existing link mechanism — shipped first (#1426). Edges already exist as ./slug.html links; a build pass inverts them, no new wikilink syntax, and a link to a non-existent slug is a fail-fast build error. The note→note graph is sparse on its own (most edges cluster on a few hubs like electricity and odu) — so the graph view promotes the four categories to real moc notes that every note files under via parents, which both densifies the graph and removes every orphan.
  2. Add optional tags, not a required area. A required topic axis forces a backward-incompatible migration of every note and imposes single-membership where the need is many-to-many. One optional field keeps “nothing unfiled” intact and adds the topic lens for free:
    // src/content.config.ts — add ONE optional field; no migration, no index inversion
    tags: z.array(z.string()).default([]),   // many-to-many facets; derive a tag → notes map
  3. Generalize typed relations with auto-inverses. parentshas-children, supersedessuperseded-by, any link → referenced-by. Keep the vocabulary tiny. status: superseded is a dangling flag today; supersedes: <slug> makes the lineage a real bidirectional edge.
  4. Graph view + Maps of Content — now shipped (#1434). The trap was inlining a JS graph + graph.json into every committed page (multiplying the dist/ churn) and trusting the Code tab to run it. The resolution: compute the layout at build time with d3-force and bake it to one self-contained SVG on the index page only, with a single is:inline script for interaction (no bundle). The decomplected data model came with it — a category is no longer a hardcoded kind enum but a real note marked moc: true, and every note files under one through parents, the single edge mechanism.

Scaling to many projects

Chosen path: Option A now, Option C later — not the aggregator up front. There is no second repo, no confidential project, no cross-role audience today. Standing up an aggregator + search-catalog + resolver now is textbook Backstage-cargo-culting for a small team — a direct violation of the fail-fast / no-premature-abstraction philosophy.

Is federated aggregation a @kolu/* package? No. It fails all three electricity tests: it isn’t domain-agnostic (it aggregates the Atlas domain itself), it hides no hard volatility (git clone + getCollection() + pagefind is a bounded build-time pipeline; Astro’s Content Layer is already the receptacle), and no foreign app graduates onto it. It’s a leaf-tier Astro extension — and, by the same logic, so is the component kit: prefer vendoring it per-repo over extracting @kolu/atlas-kit.

The roadmap

Where it’s been and where it’s going — smallest-valuable-first; each forward step ships independently and keeps the Atlas working at every stage.

Define success up front so “won’t scale” becomes answerable: time-to-find a note, % reachable in ≤2 hops, agent token cost per corpus re-read (the headline advantage, quantified), CI time per PR as notes grow, and zero confidential leaks.


History: a 2026-06-02 adversarial research pass flipped HTML-all-the-way → markdown-first; Astro was then chosen for rendering, the Atlas extracted into a self-contained docs/atlas/ project, and notes moved to MDX with a typed component kit. This note is the merge of the original second-brain design rationale into the meta comparison + roadmap, so the Atlas’s note about itself lives in one place.