diff --git a/components/mdx.tsx b/components/mdx.tsx
index a640575..8f3d8de 100644
--- a/components/mdx.tsx
+++ b/components/mdx.tsx
@@ -1,9 +1,17 @@
import defaultMdxComponents from 'fumadocs-ui/mdx';
+import { Step, Steps } from 'fumadocs-ui/components/steps';
+import { Tab, Tabs } from 'fumadocs-ui/components/tabs';
import type { MDXComponents } from 'mdx/types';
+import { StackLayers } from '@/components/stack-layers';
export function getMDXComponents(components?: MDXComponents) {
return {
...defaultMdxComponents,
+ Step,
+ Steps,
+ Tab,
+ Tabs,
+ StackLayers,
...components,
} satisfies MDXComponents;
}
diff --git a/components/stack-layers.tsx b/components/stack-layers.tsx
new file mode 100644
index 0000000..3a1ec7c
--- /dev/null
+++ b/components/stack-layers.tsx
@@ -0,0 +1,75 @@
+import Link from 'fumadocs-core/link';
+
+// The layers of the stack, drawn with the brand's colours so the diagram
+// follows the light and dark schemes. ASTRA is the foundation the others
+// read from and write to.
+const layers = [
+ {
+ name: 'Agent Skills',
+ role: 'Work with your agent',
+ text: 'Teach Claude Code or Codex to scope, build, run and report on the analysis with you.',
+ href: '/agent-skills',
+ color: 'var(--lc-color-slate-blue)',
+ },
+ {
+ name: 'Lightcone Lab',
+ role: 'Explore',
+ text: 'A JupyterLab workbench for the project: its inventory, pipeline, provenance and report.',
+ href: '/lightcone-lab',
+ color: 'var(--lc-color-vert-de-gris)',
+ },
+ {
+ name: 'MySTRA',
+ role: 'Communicate',
+ text: 'A report that references the analysis by path, so it stays in step with it.',
+ href: '/mystra',
+ color: 'var(--lc-color-wax-red)',
+ },
+ {
+ name: 'Lightcone CLI',
+ role: 'Execute',
+ text: 'Runs every recipe in a sandbox and records the provenance of every output.',
+ href: '/lightcone-cli',
+ color: 'var(--lc-color-blue-ink)',
+ },
+ {
+ name: 'ASTRA',
+ role: 'Describe',
+ text: 'The specification: the inputs, outputs, decisions and evidence of the analysis, in astra.yaml.',
+ href: '/astra',
+ color: 'var(--lc-color-antique-gold)',
+ base: true,
+ },
+];
+
+export function StackLayers() {
+ return (
+
+
+
+ Everything builds on ASTRA: the layers above read from and write to the analysis's
+ single source of truth.
+
+
+ );
+}
diff --git a/content/docs/(stack)/astra.mdx b/content/docs/(stack)/astra.mdx
new file mode 100644
index 0000000..c06366e
--- /dev/null
+++ b/content/docs/(stack)/astra.mdx
@@ -0,0 +1,123 @@
+---
+title: ASTRA
+description: The open specification every Lightcone project is written in.
+---
+
+ASTRA, the Agentic Schema for Transparent Research Analysis, is an open specification for describing
+a research analysis in a YAML file, `astra.yaml`. Code captures how an analysis runs, but not its
+structure: `astra.yaml` records its inputs, outputs, methodological choices and evidence, so that the
+work is easier to review, reproduce and extend.
+
+ASTRA doesn't depend on any tool: agents, workflow runners and people can all read the same file.
+It has its own documentation, schema and releases at [astra-spec.org](https://astra-spec.org/latest/);
+this page covers what you need to know to use it with the Lightcone Stack.
+
+## What an analysis records
+
+| Element | What you record |
+| -------------- | ------------------------------------------------------------------------ |
+| Inputs | The datasets, files and upstream analyses you use |
+| Outputs | The metrics, figures, tables and other artifacts you intend to produce |
+| Recipes | The command that produces each output from its declared inputs and choices |
+| Decisions | Methodological choices, their alternatives, and the reasons for them |
+| Universes | A selection of one option per decision, to run together |
+| Prior insights | Existing knowledge, with its evidence, that informs the approach |
+| Findings | Claims supported by the analysis's outputs |
+
+## Decisions and universes
+
+Results depend on choices: which data to include, how to treat outliers, which prior to assume. In
+ordinary research code those choices are scattered across scripts, notebooks and memory. ASTRA gives
+each one an explicit place: a **decision** names the options that were considered and why one might
+choose each.
+
+```yaml title="astra.yaml (excerpt)"
+outputs:
+ - id: fit_params
+ type: table
+ format: csv
+ description: Slope, intercept and scatter for the fitted relation.
+ inputs: [catalog_data]
+ decisions: [fit_method]
+ recipe:
+ command: >-
+ python src/fit_period_luminosity.py
+ --catalog {inputs.catalog_data}
+ --method {decisions.fit_method}
+ --out {output}
+
+decisions:
+ fit_method:
+ label: Fitting method
+ rationale: The fitting method determines how outliers influence the inferred relation.
+ default: ordinary_least_squares
+ options:
+ ordinary_least_squares:
+ label: Ordinary least squares
+ robust_linear:
+ label: Robust linear fit
+```
+
+Picking one option per decision gives a **universe**, a single runnable configuration. A universe is
+just a small YAML file:
+
+```yaml title="universes/baseline.yaml"
+id: baseline
+description: Default configuration for the period-luminosity fit.
+
+decisions:
+ fit_method: ordinary_least_squares
+```
+
+The set of all universes is the analysis's **multiverse**. Running several of them shows whether a
+result holds when you change a method, a model or a prior.
+
+## Evidence
+
+Knowledge comes into an analysis as **prior insights** and goes out as **findings**. Both carry
+their evidence: for a published result, the paper's identifier and the exact quote it rests on.
+ASTRA's tools fetch the paper and check that the quote is really there, so a citation can't be
+invented.
+
+## How the stack uses it
+
+- **[Agent Skills](/agent-skills)**: the `astra` skill teaches your agent the format, so it writes and
+ revises `astra.yaml` with you. A hook validates the file each time the agent saves it.
+- **[Lightcone CLI](/lightcone-cli)**: `lc` executes the recipes. Each output that `lc` runs needs a
+ `format`, a `recipe`, and its `inputs` and `decisions` declared; its file lands at
+ `results//