Astrolabe / ajxpublic /astrolabe
Repository

CLI & Pipeline Orchestration

reference scope cmd/astrolabe Verification failed
  • cmd/astrolabe/main.go
  • cmd/astrolabe/main_test.go
  • templates/wiki/template.yml
  • .github/workflows/astrolabe.yml
  • .gitlab-ci.yml
Relevant source files · 5

cmd/astrolabe is the single entry point of the project: a thin binary that wires every internal package into one pipeline and turns it into a command-line tool with exactly three verbs — generate, check and version. It deliberately contains no business logic of its own; its job is to parse flags and environment, pick a model provider, assemble the [Agent Core](Agent Core.md) with a [Graph Model & Providers](Graph Model & Providers.md), run it against a repository, and report the outcome in a form CI can gate on. Everything the binary does is exercised end-to-end by main_test.go, and the same two verbs are driven twice more from automation: a GitHub Actions workflow and a GitLab CI/CD component template. This page covers that orchestration layer; how the agent plans and drafts pages lives in [Agent Core](Agent Core.md), and how bundles become a site in [Astro Theme & Site Rendering](Astro Theme & Site Rendering.md).

How it works

The main function is a one-liner that turns process exit into a return value (os.Exit(run(os.Args[1:]))), because every command reports success or failure through an exit code rather than panicking. run dispatches on the first argument: generate and check are real subcommands, version prints the constant version = "0.4.0", and anything else prints usage and exits with status 2 (run). The usage text doubles as documentation — it enumerates every flag and its environment-variable fallback (ASTROLABE_BASE_URL, ASTROLABE_API_KEY, ASTROLABE_MODEL, …) so astrolabe help is enough to configure a CI job.

cmdGenerate is the meat of the file. It defines the full flag set — repository and output paths, provider credentials, page caps, retry counts, the review toggle, incremental-build controls (--full, --skip-unchanged), forge URL templates, and logging/search knobs — validating the parsed values before anything expensive happens (cmdGenerate). Then it sets up observability via telemetry.Setup: with no OTEL_* configuration this degrades to no-op providers and plain logfmt logging on stderr, and when configured it emits GenAI-convention traces, metrics and logs, with a deferred shutdown that flushes for up to ten seconds.

Version stamps are auto-detected next: when --commit, --tag or --branch are empty, the helper gitOutput shells out to git -C <repo> for rev-parse HEAD, describe --tags --exact-match and rev-parse --abbrev-ref HEAD, silently returning "" for detached checkouts or missing history. This matters because those stamps end up in page footers and citation URLs, pinning documentation to an immutable commit rather than a moving branch.

A cheap skip short-circuit comes before any model client exists: with --skip-unchanged (or ASTROLABE_SKIP_UNCHANGED=true) and no --full, the command loads <out>/state.json, compares its commit to the current HEAD, and if they match exits 0 having made zero model calls — only ensuring a search index exists if it's missing. This is what makes nightly "document the repo" pipelines free when nothing changed.

The provider selection is a three-way switch with a clear precedence, spelled out in the usage text as --dry-run > astrolabe.yaml acp.command > OpenAI-compatible:

  1. --dry-run substitutes agenttest.NewReferenceMock(), a built-in deterministic mock model, so demos and smoke tests never touch the network.
  2. If the repository (or a parent) contains an astrolabe.yaml with an acp.command, the CLI spawns that agent process via [ACP Agent Transport](ACP Agent Transport.md) and wraps it as an llm.Client, honoring its working directory, chat timeout and config options. Passing --acp without such a config is a usage error (exit 2).
  3. Otherwise an OpenAI-compatible client is constructed from --base-url/--api-key/--model; a missing model name is a usage error, and a missing API key is only a warning, since some self-hosted providers need none.

With the client chosen, the CLI assembles agent.Config — repo root, a graph.SQLiteProvider with Sync: true (so the codegraph index is refreshed as part of the run), the client, page/retry/round limits, the commit stamps and URL templates — and calls agent.New(...).Run(ctx, out). The actual page planning, drafting, review and verification are all the agent's job ([Agent Core](Agent Core.md)); the CLI only inspects the returned Result: it tallies res.Report.Pages into passed/failed, prints per-page FAIL lines with issue kinds, prints the summary bundle written to <dir> [<mode> build]: N pages (X passed, Y failed), and reports model token usage when the client exposes a Usage() method. Exit code 1 if anything failed — unless --allow-failed was given, in which case the bundle with failed-badge pages is still published.

cmdCheck is the verification-only half. It loads every pages/*.md from a bundle back into wiki.Page values via loadBundlePages — which, for forge-mode bundles, rewrites citation URLs from the forge template back into canonical src/... links so the checker can resolve them against the repository — loads the symbol universe from the codegraph index for mermaid validation (loadSymbolSet, warning and skipping validation when no index is available), and hands everything to the [Verification (Checker & Review)](Verification (Checker & Review).md) checker. It prints PASS <slug> (n citations) / FAIL <slug> lines and exits non-zero on any failure.

The tests

main_test.go treats the CLI as a black box: the cli helper builds the real binary into a temp dir (disabling search indexing so tests don't depend on pagefind), and each test shells out to it (main_test.go). TestGenerateCheckRoundTrip generates a bundle from the fixture repository with --dry-run, asserts the summary line and the presence of index.html, index.json, llms.txt, verification.json and a page, then runs check and expects PASS store (main_test.go). TestCheckDetectsTamperedBundle proves the checker isn't decorative: it rewrites one citation to a nonexistent file and asserts check fails with a citation-path issue. TestGenerateFullThenIncremental and TestGenerateSkipUnchanged exercise the caching contract with a real git history — full build, then commit a change and observe an [incremental build], then --skip-unchanged making no model calls until a new commit appears (main_test.go). Two subtler tests pin the incremental path's edge cases: a bundle written inside the repository must not be indexed as code (its src/ snapshot would duplicate every symbol), and forge-mode incremental builds re-pin reused pages' citations to the new commit so they keep verifying without re-drafting.

CI integration

Two shipping integrations show the intended production shape. The GitHub Actions workflow (.github/workflows/astrolabe.yml) runs on every push to main and on demand: it installs codegraph, builds the binary, generates the bundle — falling back to --dry-run when no API key secret is configured so the workflow stays green out of the box — re-verifies with check, builds the Astro theme site, and uploads bundle and site as artifacts, with commented deploy recipes for GitHub Pages, S3/CloudFront and Cloudflare Workers (astrolabe.yml workflow).

The GitLab side is a reusable CI/CD component, templates/wiki/template.yml, which is what powers astrolabe's own nightly self-documentation. It exposes inputs (image, model, reasoning effort, page cap, full vs. incremental, allow_failed, extra args, timeout, cache settings) and implements one job that maps them to the same CLI flags: it pins citations to the commit via the GitLab forge URL template, passes --skip-unchanged by default (the cached bundle plus codegraph index are cached, so unchanged commits make no model calls), runs check unless allow_failed is set, and finally produces a site/ artifact — either the raw bundle or the theme build (template.yml component, job script). The repo's own .gitlab-ci.yml composes this component with a container-image build and a Cloudflare Workers deploy job, scheduled nightly via ASTROLABE_DOCS=true (.gitlab-ci.yml).

Relationships

flowchart LR
    cli["astrolabe CLI"]
    wf["GitHub Actions workflow"]
    gl["GitLab CI component template"]
    agent["agent.New + Run"]
    checker["verify.Checker.CheckAll"]
    mock["agenttest.ReferenceMock"]
    acp["ACP agent process"]
    openai["OpenAI-compatible endpoint"]
    codegraph["codegraph index"]
    cli --> agent
    cli --> checker
    agent --> mock
    agent --> acp
    agent --> openai
    agent --> codegraph
    wf --> cli
    gl --> cli

The CLI is the only caller-side entry point; CI systems, tests and humans all reach the same two verbs.

Reference

Symbol Kind What it does Location
run function Dispatches generate / check / version / help, exits by status code cmd/astrolabe/main.go L44–L75
cmdGenerate function Full generation pipeline: flags, telemetry, provider selection, agent run, exit-code policy cmd/astrolabe/main.go L126–L300
cmdCheck function Re-verifies an existing bundle with the checker, prints PASS/FAIL per page cmd/astrolabe/main.go L303–L340
loadBundlePages function Parses pages/*.md back into wiki.Page values; maps forge citation URLs to src/ links cmd/astrolabe/main.go L345–L385
loadSymbolSet function Loads codegraph symbols for mermaid validation; warns and degrades when absent cmd/astrolabe/main.go L407–L425
gitOutput function Runs git -C <repo>, returns trimmed stdout or "" cmd/astrolabe/main.go L390–L397
TestGenerateCheckRoundTrip test generate → artifact presence → check round trip on the fixture repo cmd/astrolabe/main_test.go L29
TestGenerateFullThenIncremental test Full build, incremental after a commit, --full forces rebuild cmd/astrolabe/main_test.go L120–L325
TestGenerateSkipUnchanged test --skip-unchanged makes no model calls on an unchanged commit cmd/astrolabe/main_test.go L120–L325
wiki component CI template Reusable GitLab CI/CD component wrapping generate + check + site build templates/wiki/template.yml
astrolabe workflow CI workflow GitHub Actions: generate, re-verify, theme build, artifact upload .github/workflows/astrolabe.yml
pipeline CI config Self-hosting pipeline: image build, wiki component, Cloudflare deploy .gitlab-ci.yml

Generated by astrolabe from commit ecc180b3 (tag 0.4.0).