Astrolabe / ajxpublic /astrolabe
Repository

Verification (Checker & Review)

reference scope internal/verify Verification failed
  • Checker
  • Issue
  • PageResult
  • Report
  • Reviewer
  • ReviewVerdict
  • internal/verify/mermaid.go
Relevant source files · 5

Every page astrolabe generates is a set of factual claims about source code, backed by citations. The internal/verify package is the quality gate that makes those claims checkable: a deterministic Checker that statically validates each page's citations, links, and mermaid diagrams against the real repository, and an optional LLM-backed Reviewer that fact-checks the prose against the very excerpts it cites. The same machinery serves two consumers: the agent re-drafts failing pages during generation (Agent Core), and the astrolabe check CLI re-validates a published bundle in CI (CLI & Pipeline Orchestration).

How it works

The entry point is Checker, configured with the repository root, the full set of site pages (for link validation), and an optional symbol universe from the code graph. Checker::CheckAll walks every page and collects one PageResult per page into a Report (CheckAll).

Checker::checkPage runs the full battery of static checks (checkPage), in order:

  1. Structure — the page must normalize cleanly, otherwise an IssueStructure finding is recorded.
  2. Citations — every citation link is resolved on disk. A page with zero citations fails immediately.
  3. Link destinations — every link must be a citation (src/...), an intra-site wiki link (<slug>.md), or an http(s) URL; anything else is an IssueUnrecognizedLink. Wiki links are checked against the set of known slugs.
  4. Overview navigation — if the page is the overview, it must link to every reference page in the site, otherwise IssueOverviewNavigation.
  5. Mermaid diagrams — delegated to Checker::checkMermaid (below).

Checker::checkCitation is where hallucinated references are caught (checkCitation). It reads the cited file from RepoRoot, verifies the line range is in bounds and not inverted (IssueCitationRange), and then — when the label names a symbol — requires that the symbol actually appears verbatim in the cited lines, emitting IssueCitationSymbol otherwise. Whether containment is required is decided by Checker::containmentRequired (containmentRequired): when a symbol universe is available, only labels matching a known symbol (or its last segment) are held to the strict standard; narrative labels like see users.go only need a valid target. Without a universe, any single-token identifier-looking label is checked. The matching itself is done by the free function mentionsSymbol, which compares the final dot/colon-separated segment of the label against the snippet (mentionsSymbol) — so [Checker::checkCitation](...) must contain checkCitation in the cited lines.

Mermaid validation lives in its own file. Checker::checkMermaid extracts every fenced mermaid block and inspects each backtick-quoted token (checkMermaid): tokens that look like source files (by extension, per looksLikeFile) must exist on disk under the repo root; all other tokens must resolve in the symbol universe when one is loaded. Failures are reported as IssueMermaidReference. The extraction helpers ExtractMermaidBlocks and the extension table live in the same file (ExtractMermaidBlocks).

The Reviewer is the LLM-assisted second opinion. Its Client is an LLM Client and its ReadSource callback loads cited source text (in the agent, bound to the graph's source reader). Reviewer::Review sends a strict fact-checker system prompt together with the page and asks for a JSON verdict (Review). The prompt is built by Reviewer::buildPrompt, which embeds the full page markdown followed by every cited excerpt, labeled with its citation (buildPrompt). The reply is cleaned of markdown fences and stray prose by extractJSON before unmarshalling into a ReviewVerdict (extractJSON). Each unsupported claim becomes a harness Issue of kind unsupported-claim; a rejection with no listed claims is itself flagged.

Both static and LLM findings flow into the same issue vocabulary, so downstream consumers treat them uniformly.

Results and how they're used

A Report aggregates PageResults and offers two conveniences: Report::AllPassed for a simple gate, and Report::Summaries, which converts findings into per-page wiki.VerificationSummary values embedded in the published bundle by the Wiki Bundle & Pages writer (Summaries).

The agent's verify stage wires everything together: it constructs a Reviewer only when review is enabled, computes the symbol set from the graph, then loops — check, re-draft each failing page with its issues as feedback, and retry until clean or the retry budget is exhausted (verifyWithRetries). In CI, astrolabe check --repo ... --bundle ... loads the bundle's pages (mapping forge-mode citation URLs back to src/ citations), loads the symbol universe, runs Checker::CheckAll, and prints PASS/FAIL per page, exiting non-zero on any failure (cmdCheck).

Issue kinds

Kind Meaning
structure page failed normalization
no-citation page contains no citation links
citation-path cited file not found in the repository
citation-range cited lines out of bounds or inverted
citation-symbol cited lines do not contain the labeled symbol
wiki-link inter-page link targets a missing page
unrecognized-link link is neither a citation, page link, nor http(s)
overview-navigation overview does not link to a reference page
mermaid-reference mermaid token resolves to no known symbol or file
unsupported-claim reviewer flagged a claim the sources don't support

Relationships

flowchart LR
    run["cmd/astrolabe main.run"]
    run --> cmdGenerate["cmdGenerate"]
    run --> cmdCheck["cmdCheck"]
    cmdCheck --> Checker
    cmdCheck --> loadSymbolSet["loadSymbolSet"]
    Agent["internal/agent.Agent"] --> verifyWithRetries["Agent::verifyWithRetries"]
    verifyWithRetries --> Checker
    verifyWithRetries --> Reviewer
    Reviewer --> LLMClient["internal/llm.Client"]
    Checker --> checkPage["Checker::checkPage"]
    checkPage --> checkCitation["Checker::checkCitation"]
    checkPage --> checkMermaid["Checker::checkMermaid"]
    checkCitation --> mentionsSymbol["mentionsSymbol"]
    checkCitation --> containmentRequired["Checker::containmentRequired"]
    Checker --> Report
    Report --> PageResult
    PageResult --> Issue
    Report --> Summaries["Report::Summaries"]
    Summaries --> VerificationSummary["internal/wiki.VerificationSummary"]
    checkPage --> ExtractCitations["wiki.ExtractCitations"]
    Review --> buildPrompt["Reviewer::buildPrompt"]
    buildPrompt --> ExtractCitations
    Review --> extractJSON["extractJSON"]
    Review --> ReviewVerdict

Reference

Symbol Kind What it does Location
Checker struct Static validator: repo root, page set, symbol universe internal/verify/checker.go
Checker::CheckAll method Validates every page, returns the full report internal/verify/checker.go
Checker::checkPage method Runs all per-page checks and produces a PageResult internal/verify/checker.go
Checker::checkCitation method Resolves a citation on disk and checks range and symbol containment internal/verify/checker.go
Checker::containmentRequired method Decides whether a label must name a symbol in the cited lines internal/verify/checker.go
mentionsSymbol function Verbatim last-segment symbol match in a cited snippet internal/verify/checker.go
Issue struct One harness finding (kind + detail) internal/verify/checker.go
PageResult struct Per-page outcome: pass/fail, citation count, issues internal/verify/checker.go
Report struct Bundle-wide outcome with AllPassed and Summaries internal/verify/checker.go
Checker::checkMermaid method Validates mermaid block tokens against files and symbols internal/verify/mermaid.go
ExtractMermaidBlocks function Pulls fenced mermaid block bodies from a page internal/verify/mermaid.go
Reviewer struct LLM-assisted fact-checker with client and source reader internal/verify/review.go
Reviewer::Review method Reviews one page, returns verdict and issues internal/verify/review.go
Reviewer::buildPrompt method Builds the page-plus-cited-sources prompt internal/verify/review.go
extractJSON function Extracts the verdict JSON from a model reply internal/verify/review.go
ReviewVerdict struct Parsed review outcome: approved + unsupported claims internal/verify/review.go

Generated by astrolabe from commit ecc180b3 (tag 0.4.0).