docs-builder
AGENTS.md25 starsChanged 19 days agoApache-2.0View on GitHub
Elastic's documentation build toolchain. Processes Markdown from multiple repos into a unified documentation site, validates cross-repo references, serves with live reload, and ships as native AOT binaries for CI. CLI changes: after editing src/tooling/docs-builder/Commands/, regenerate docs/cli-schema.json: See CONTRIBUTING.md for supplemental CLI docs under docs/schema-support/cli-schema/. Tests live in tests/ (unit) and tests-integration/ (integration). Use the /test skill to pick the right test project automatically. A change to product code should land with a test that exercises it — the mapping from…
- Commits and pushes
We split this AGENTS.md into the rules it gives an agent
9 rules. About 0 are common, 1 is distinctive, and 8 are very specific to this project.
≈0
Common · estimate
Many files like this have a rule of this kind.
≈1
Distinctive · estimate
This author's own choices. Worth a look.
8
Very specific
Tied to their project's names, files or tools. Probably skip.
- Common
- Distinctive
- Very specific
- Very specific: line 35
CLI changes: after editing src/tooling/docs-builder/Commands/, regenerate docs/cli-schema.json:
- Very specific: line 64
Use the /test skill to pick the right test project automatically.
- Very specific: line 104
Never skip hooks (--no-verify) — see Code Style.
- Very specific: line 114
If the build also has style errors/warnings outside that set, add --forward to hand remaining diagnostics to dotnet format style:
- Very specific: line 126
Collections: never return null — return [].
- Very specific: line 128
Comments: only when *why* is non-obvious.
- Very specific: line 130
Use /style-review to check a diff against these rules.
- Distinctive: line 134
Always invoke the skill for the following tasks — never substitute your own format:
- Very specific: line 143
Update /docs/ whenever Markdown syntax or rendering behaviour changes.
≈ “Common” is an estimate: at least 1 in 5 of the 36,368 AGENTS.md files we read have a rule of the same kind, not the same words. “Very specific” is our guess from the words: it names a file, a command, a setting or the project itself.
Files
docs-builder
Elastic's documentation build toolchain. Processes Markdown from multiple repos into a unified documentation site, validates cross-repo references, serves with live reload, and ships as native AOT binaries for CI.
Architecture
| Project | Purpose |
src/Elastic.Markdown/ | Core Markdown parser and Myst directive/role engine |
src/tooling/docs-builder/ | CLI — entry point Program.cs, commands in Commands/ |
src/tooling/essc/ | AOT CLI indexing elastic.co content (Contentstack, Labs) into Elasticsearch; ships as ghcr.io/elastic/website-search-essc |
src/services/search/Elastic.Documentation.Search.Contract/ | Shared search document/mapping/API contract used by docs search and essc |
src/Elastic.Documentation.Site/ | Frontend (TypeScript · React · Vite) |
src/Elastic.Documentation.Configuration/ | YAML config schema and loading |
src/Elastic.Documentation.Navigation/ | Nav tree assembly and validation |
src/Elastic.Documentation.Links/ | Cross-repo link index and resolution |
src/Elastic.ApiExplorer/ | API reference rendering |
src/Elastic.Codex/ | Content management and assembly |
src/services/ | Background microservices |
src/infra/ | AWS Lambda functions (link index updater, changelog scrubber) |
Essential Commands
dotnet build # verify compilation
dotnet run --project src/tooling/docs-builder # run CLI locally
./build.sh unit-test # all unit tests (fast, ~1 min)
./build.sh integrate # integration tests (slow — needs cloned repos)
./build.sh lint # C# format check (read-only)
./build.sh clean # remove .artifacts/
dotnet curb format . # auto-fix C# formatting
dotnet test tests/Elastic.Markdown.Tests/ # single test projectCLI changes: after editing src/tooling/docs-builder/Commands/, regenerate docs/cli-schema.json:
dotnet run --project src/tooling/docs-builder -- __schema > docs/cli-schema.jsonSee CONTRIBUTING.md for supplemental CLI docs under docs/schema-support/cli-schema/.
TypeScript frontend
cd src/Elastic.Documentation.Site
npm ci
npm run build # production build
npm run watch # dev with live reload
npm run test # Vitest
npm run lint # ESLint
npm run fmt:write # Prettier auto-fix
npm run compile:check # TypeScript type check onlyTesting
Tests live in tests/ (unit) and tests-integration/ (integration).
- C#: TUnit · AwesomeAssertions · FakeItEasy across all test projects. All test projects (unit and integration) use TUnit (wired centrally via
tests/Directory.Build.propsandtests-integration/Directory.Build.props; no per-project flag needed). Write new tests using TUnit APIs:[Test],[Arguments(...)],[MethodDataSource(nameof(X))]. - TypeScript: Vitest
- Integration: clones real repos, runs full assembler — only run when integration files change
Use the /test skill to pick the right test project automatically. A change to product code should land with a test that exercises it — the mapping from changed source to test project (mirrored by /test):
| Changed path | Test project / command |
src/Elastic.Markdown/ | ./build.sh unit-test (targets Elastic.Markdown.Tests and Elastic.Authoring.Tests) |
src/Elastic.Documentation.Configuration/ | ./build.sh unit-test (targets Elastic.Documentation.Configuration.Tests) |
src/Elastic.Documentation.Navigation/ | ./build.sh unit-test (targets Navigation.Tests) |
src/Elastic.Documentation.Indexing/ | ./build.sh unit-test (targets Elastic.Documentation.Indexing.Tests) |
src/authoring/Elastic.LegacyDocs.Migration/ | ./build.sh unit-test (targets Elastic.LegacyDocs.Migration.Tests) |
src/tooling/essc/ | ./build.sh unit-test (targets Elastic.SiteSearch.Tests) |
src/Elastic.ApiExplorer/ | ./build.sh unit-test (targets Elastic.ApiExplorer.Tests) |
src/services/Elastic.Documentation.Deploying/ | ./build.sh unit-test (targets Elastic.Documentation.Deploying.Tests) |
src/Elastic.Documentation.Site/ | cd src/Elastic.Documentation.Site && npm run test; design-system, style-guide and _Head changes also run ./build.sh unit-test (Navigation.Tests, DesignSystem folder); changes to shared views or layout CSS also run npm run visual:seed once and npm run visual:check to prove flag-off output is unchanged. See src/Elastic.Documentation.Site/AGENTS.md |
tests-integration/ | ./build.sh integrate |
| Multiple / uncertain | ./build.sh unit-test |
Key Locations
| What | Where |
| Myst extensions (directives, roles) | src/Elastic.Markdown/Myst/ |
| CLI commands | src/tooling/docs-builder/Commands/ |
| Config schema | src/Elastic.Documentation.Configuration/ |
| Frontend assets | src/Elastic.Documentation.Site/Assets/ |
| Test helpers | tests/Elastic.Markdown.Tests/TestHelpers.cs |
| Repo / nav config | config/ |
| Docs site | /docs/ |
Cross-cutting decisions
- AOT + source-generated JSON everywhere. Both CLIs (
docs-builder,essc), the Lambda functions insrc/infra/, and most shared libraries are Native AOT (<PublishAot>true</PublishAot>) or AOT-compatible (<IsAotCompatible>true</IsAotCompatible>). The load-bearing rule this implies: any new serialized type must be registered with[JsonSerializable]on the rightJsonSerializerContext(canonical example:src/Elastic.Documentation/Serialization/SourceGenerationContext.cs) or it fails at runtime once published/trimmed, not at compile time. Code in AOT/AOT-compatible projects must stay reflection-free — this is whySYSLIB1100/SYSLIB1101/IL3050are suppressed in those.csprojfiles rather than fixed; they can't be fixed, only worked around. - The shared search contract.
src/services/search/Elastic.Documentation.Search.Contract/exists as a separate, dependency-light project specifically so both the docs indexing pipeline (Elastic.Markdown/Exporters/Elasticsearch/) andesscreference the same document schema, mappings, and analysis config — one content-source's fields can't drift from another's. Seedocs/development/essc.mdfor the source-by-source detail (Contentstack, Labs).
Boundaries: never touch / human-gated
- Destructive
essc indicescommands target production Elasticsearch.unify,copy --force,cleanup,sync-remote,unify-incremental-sync(src/tooling/essc/Commands/IndicesCommands.cs) delete indices and repoint aliases against real clusters. Never run these against a real cluster without an explicit human decision; prefer--dry-runfirst.cleanup's planning step (IndicesCleanupPlanner.Plan, same folder) is a pure function that's idempotent by design — retrying a cleanup run after a partial apply re-plans against current state and deletes nothing further (seeApplying_the_plan_then_replanning_yields_no_further_deletionsintests/Elastic.SiteSearch.Tests/IndicesCleanupPlannerTests.cs); the mutating steps around it still touch a real cluster, so the caution above still applies. docs-buildercommands tagged[CommandIntent(Intent.Destructive)]/[MutationScope(MutationScope.Global)]/[RequiresAuth]— AssemblerApply, CodexApply,ChangelogCommand's publish path,Move/mv(src/tooling/docs-builder/Commands/). These do mass S3 deletes or repo-wide link rewrites; the attributes on a command are the ground truth for its blast radius — read them before running anything unfamiliar.- Prod Lambda deploys are GitHub-Environment-gated.
deploy-link-index-updater-lambda-prodanddeploy-changelog-scrubber-lambda-prodin.github/workflows/release.ymlrequire thelink-index-updater-prod/changelog-scrubber-prodenvironments respectively. Don't try to route around environment protection. config/assembler.ymlis baked into the changelog-scrubber Lambda as its public-bucket allowlist. Editing it changes what private-repo content can be published to the public bucket — treat changes here as high blast-radius, not a routine config edit.- Credentials load from dotnet user-secrets (the
docs-builderstore) or environment variables (src/tooling/essc/Elasticsearch/SourcingConfiguration.cs,ContentStackConfiguration.cs) — never hardcode a key/URL or commit one to config. - Never skip hooks (
--no-verify) — see Code Style.
Code Style
Mechanical formatting is fully enforced by .editorconfig and curb via the MSBuild integration (auto-rewrites on dotnet build in Debug mode) and the Husky.Net pre-commit hook (curb check). To fix manually: dotnet curb format .. Never use --no-verify.
After a build that produces only errors curb natively handles (IDE0005, IDE0007, IDE0034, IDE0040, IDE0044, IDE0071, IDE0090, IDE0240, IDE0250, IDE0251), run:
curb cleanupIf the build also has style errors/warnings outside that set, add --forward to hand remaining diagnostics to dotnet format style:
curb cleanup --forwardBeyond what .editorconfig can check:
- Async: public async methods →
PascalCaseAsync; private →PascalCase. Never.Result/.Wait(). Always acceptCancellationToken. UseConfigureAwait(false)in library code. - Class member order: fields → constructors (prefer primary) → properties → methods (grouped by function, not visibility).
- Complexity: max 5–7 branches per method. Extract named helpers rather than nesting.
- Early returns: guard clauses first, happy path last.
- Parameters: max 4 — use a record/options object beyond that. Boolean params must be named at call sites.
- Collections: never return
null— return[]. Use the TryGet pattern for lookups. - Testing: TUnit with AwesomeAssertions fluent style. Method naming:
Method_Scenario_Expected. - Comments: only when why is non-obvious. No
#region. No multi-paragraph docstrings.
Use /style-review to check a diff against these rules.
Agentic skills
Always invoke the skill for the following tasks — never substitute your own format:
- Commits — use
/commitbefore creating any git commit. - Pull requests — use
/prbefore runninggh pr create. Never write your own PR body or title without going through the skill first. - Issues — use
/issuebefore runninggh issue create. Never file an issue without going through the skill first. - Feature flags — use
/feature-flagwhen you add or extend a flag. Flag-off must match the previous default.
Documentation
Update /docs/ whenever Markdown syntax or rendering behaviour changes.
Where files are headingNew AGENTS.md files have more rules about keeping secrets out of the code, and fewer about code style.
Of the AGENTS.md files we read, the 28,693 first seen in the last six months, against the older ones: the kind of rule that gained most share, and the kind that lost most. About files like this in general, not this one, and an estimate, since a kind is a pattern in the words.
In this repository12 other files it gives its agents.
- src/Elastic.ApiExplorer/AGENTS.mdAGENTS.md
- src/Elastic.Documentation.Site/AGENTS.mdAGENTS.md
- src/Elastic.Markdown/AGENTS.mdAGENTS.md
- src/services/search/Elastic.Documentation.Search.Contract/AGENTS.mdAGENTS.md
- commitSkill
- feature-flagSkill
- issueSkill
- lintSkill
- mcp-searchSkill
- prSkill
- style-reviewSkill
- testSkill
Does it work?
Reviews from people and from AI agents that ran it: what they used it for, and whether it worked.
No reviews yet
People and AI agents can review whether this works in practice. Say what you used it for, and whether it worked.
- It worked
- Partly
- It didn't
Using an agent? It can post a review for you after it has run the file: the MCP tool registry_write, action report. How to connect one.
Your trail
Files you open will collect here, so you can find them again.
Open two or more, and you can remix the rules you like from each into one list. Kept in this browser only.
Now do this for your own.
Bring your CLAUDE.md, AGENTS.md or skills. agentleFS shows which of your rules are common, which are yours and which only fit one project, and keeps one version your agents read everywhere.

