Imported from open-lakehouse/docs-factory (
AGENTS.md). Install upstream withnpx skills add open-lakehouse/docs-factory. Copyright stays with the author.
Agent & contributor notes — docs-factory
Orientation for anyone (human or agent) working in this repo. This file is a repo convention; it is not published to any docs site.
What this repo is
The authoritative content source for the restructured delta.io and
unitycatalog.io documentation, plus narrative blog drafts for the open-lakehouse
estate. We author engine-neutral explanations and multi-engine, copy/paste-runnable,
CI-tested examples here, then migrate docs into the sites later. Content is
builder-agnostic Markdown (.md): richness comes from remark plugins in the
local preview harness, not from JSX or site-specific syntax in the source files.
Narrative (blogs) stays separate from architectural fact (architecture/) and from
reference docs (content/). A fourth scope is kept apart too: decisions and design
about the factory itself (the site, emitters, review server, authoring pipeline)
live in docs/, not in architecture/ — the latter is only fact about the
lakehouse we document. The dividing line is architecture/adr/ADR-0002.
Layout
content/ Diátaxis-organized Markdown + colocated, tested snippets/ per page (tutorials / how-to / reference / explanation)
blogs/ narrative blog drafts (index.md + assets/ + snippets/ per post) + STORYLINE
emit/ deterministic blog draft → downstream target render (unitycatalog.io / delta.io MDX)
seed/ docs-factory-seed: deterministic Delta-table seeder (Python + Rust)
tools/docsnip/ content validation tooling (frontmatter validate, snippet check)
site/ throwaway Vite + React + MDX preview (docs + blogs); site/src/content-core is the shared parsing authority; build emits per-project llms.txt into site/public/
architecture/ LAKEHOUSE FACT: LikeC4 model + design docs + ADRs + estate facts (estate.yml, glossary)
docs/ FACTORY META: design + decisions about the factory itself (site/emit/server/authoring)
server/ review/release backend (Connect RPC + Postgres) for the in-app review layer
proto/ review/release service proto (docs_factory/review/v1)
research/ existing research reports (leave alone)
Load-bearing conventions
-
Colocated snippets are the source of truth for docs code. Docs never inline code; they reference a page's colocated
snippets/*.pyviaremark-code-snippetsfences (file=./snippets/... start=... end=...). The preview resolves them live; nothing is copied into the page.docsnip snippetcheckenforces that every fence resolves. A page with runnable snippets is folder-mode (<slug>/index.mdbesidesnippets/); give each script a PEP 723 block socontent/conftest.pyruns it as a test. The shared resolver + parsing contract lives insite/src/content-core/(seedocs/design/build-pipeline.md). -
Blog snippets live in
blogs/<slug>/snippets/. Samefile=/start=/end=fence contract; whole-file inlining (file=only) is also supported. Blog frontmatter is validated separately (seeblogs/CONVENTIONS.md). -
Region markers wrap only what the reader should see. Put seeding,
mainwrappers, and asserts outside thedocs-...-start/docs-...-endmarkers — except theseed_dataset(...)line for examples that read pre-existing data. -
Per-project
llms.txtis generated by the site build, not committed. The site prebuild (site/scripts/build-llmstxt.mjs) emitssite/public/*.llms.txtfromcontent/frontmatter, and Vite copies it intodist/. Regenerate locally withjust llmstxt; there is nothing to commit. -
Every content page carries frontmatter. Required:
title,diataxis,project. Blog drafts requiretitle,slug,status,tags,author,target(tags must exist inblogs/tags.yml).Status is two orthogonal axes — don't conflate them. A content page's git
statusis authoring intent only:draft(still being written) orready(the author asserts it's publishable). The review/release lifecycle is DB-canonical (review_state: none → in-review → changes-requested → approved → released), owned by the review server, never written back to git. A page is shown to anonymous site visitors only when it isreadyAND its DBreview_stateisreleased— publication is the intersection of author intent (git) and review outcome (DB); neither alone exposes content. Allowlisted reviewers see everything, so review can start while a page is stilldraft.llms.txtkeys on gitreadyalone (build-time, no DB), so areadypage can enter the agent index slightly before it's publicly viewable — an accepted skew that keeps authoring decoupled from the deploy DB. An explanation page also declaresexplains: <c4-element-id>(its canonical model concept); seecontent/README.md. -
Richness is a property of the renderer. Blog constructs (
:::tip,::::journey, LikeC4 diagrams) degrade to plain Markdown on GitHub; the preview upgrades them. Seeblogs/CONVENTIONS.md§5 andsite/README.md. -
A comment earns its place by stating the non-obvious. Write the why — an invariant, a subtle ordering/async/security constraint, a "why this and not the obvious alternative", or a format hint (
// uuid). Do not restate what the code says, echo the function/component name in a docstring, or narrate what the code doesn't do unless the not-doing is genuinely surprising. Use-case and motivation prose belongs in the PR description, not inline. Example — after always-rendering a copy button on collapsed code blocks, keep only the code-relevant fact:// ✗ a collapsed block often holds a file the reader is meant to copy into // their own project, so hiding the button behind expand-first is friction… // ✓ `code` holds the full contents regardless of expand state. <CodeCopyButton code={code} />
On each import, reflect on the conventions
When new ideas, drafts, or source material land here, reflect on whether the
experience surfaced a gap in the relevant conventions doc — and propose a concrete
update rather than silently working around it. When adding a blog tag, prefer an
existing entry from blogs/tags.yml; only add a new tag in the same change.
Common commands
uv sync --all-packages # install every workspace package
just preview # Vite preview at :4321 (docs + blogs)
just emit <slug> <target> # emit a blog draft (target: unitycatalog | delta)
uv run pytest # docsnip tests + colocated tutorial scripts
cd site && bun test src/content-core # content-core parsing-contract drift tests
uv run docsnip check # frontmatter + snippets
just llmstxt # regenerate site/public/*.llms.txt (also at site prebuild)
uv run ruff check . && uv run ty check # lint + types
cargo build # compile the Rust seed helper
just arch-dev # LikeC4 architecture model at :5173
Adding a tested snippet to a page
- Make the page folder-mode:
content/<project>/<bucket>/<slug>/index.mdbeside asnippets/dir. - Put the runnable code in
snippets/<name>.pywith a PEP 723# /// scriptblock (its deps, and[tool.uv.sources]for the localdocs-factory-seed), region markers wrapping only what the reader should see, and inlineasserts in__main__so running it to completion is its test. - Reference it from
index.mdwith a fence (file=./snippets/<name>.py start=... end=...), thenuv run docsnip check.content/conftest.pyruns the script in the default test lane.
Blog workflow
Read blogs/CONVENTIONS.md before working on posts.
Skills in .claude/skills/{blog-post,blog-review,blog-emit} automate the lifecycle.
Estate facts for cross-repo posts live in architecture/estate.yml;
narrative framing in blogs/STORYLINE.md.