Automated Documentation Maintenance System
Date: 2026-03-13 Status: Design approved, pending implementation
Problem
When code changes land in source repos (atomiq-sdk, chain libs), the hand-written developer documentation in atomiq-docs can become stale. Currently the only process is "remember to update docs," which doesn't scale.
Solution
A GitHub Actions-based system that automatically detects code changes in source repos and creates draft PRs on atomiq-docs with proposed documentation updates, using the Claude API for diff analysis and doc rewriting.
Architecture
Source Repo (e.g. atomiq-sdk) atomiq-docs repo
┌──────────────────────────┐ ┌──────────────────┐
│ push to main (src/) │ │ │
│ │ │ │ │
│ ┌──────▼──────────┐ │ clone │ │
│ │ caller workflow │ │ │ │
│ │ (~10 lines) │─────┼───────────►│ reads docs/ │
│ │ │ │ │ │
│ │triggers reusable│ │ Claude │ │
│ │ workflow in │ │ API │ │
│ │ atomiq-docs │─────┼──────────► │ │
│ │ │ │ │ │
│ │ ────┼─────┼──────────► │ PR with changes │
│ └─────────────────┘ │ │ │
└──────────────────────────┘ └──────────────────┘
Components
- Reusable workflow (
atomiq-docs/.github/workflows/docs-check-reusable.yml) — contains all logic: diff collection, Claude API call, PR creation - Caller workflow — lightweight ~10 line workflow in each source repo that triggers the reusable workflow
Secrets Required (GitHub org-level)
ANTHROPIC_API_KEY— for Claude API callsDOCS_REPO_PAT— GitHub PAT with write access to atomiq-docs (branches + PRs)
Repository Visibility Requirement
The reusable workflow pattern (uses: atomiqlabs/atomiq-docs/...@main) requires that atomiq-docs is either a public repo or that cross-repo workflow access is enabled within the GitHub org. If atomiq-docs is private, use the repository_dispatch pattern as a fallback (source repo sends an event, atomiq-docs listens for it).
Trigger Conditions
- Event: Push to
mainbranch - Path filter:
src/**by default (ignores test-only, config-only changes). Caller workflows can broaden per-repo (e.g.,['src/**', 'index.ts', 'types/**']) if public API surface exists outsidesrc/. - Skip mechanism: Commit message contains
[no-docs] - Concurrency: One run at a time per source repo, using
concurrency: docs-update-${{ inputs.repo-name }}
Reusable Workflow Steps
Step 1: Collect the diff
Get the code diff for the push event:
- For single commits:
git diff HEAD~1..HEAD -- src/ - For multi-commit pushes: diff against the
beforeSHA from the push event - Include commit messages for intent/context
Step 2: Clone atomiq-docs
Shallow clone of atomiq-docs repo. Read doc files from the subdirectory specified by the doc-paths input (e.g., docs/developers/ for SDK changes). The sdk-reference/ directory is always ignored since it auto-regenerates from TypeDoc. Internal files (docs/superpowers/) are also excluded.
Step 3: Call Claude API
Send a structured prompt containing:
- The git diff
- Commit messages
- Doc files from the relevant subdirectory as tagged blocks (filtered by
doc-pathsinput) - The source repo name for context
Only the doc subtree relevant to the source repo is included. This keeps the payload focused and within reasonable token limits.
Error handling: Retry up to 2 times with exponential backoff on transient failures (429, 500). Validate the response is valid JSON with the expected schema before proceeding. On persistent failure, create a GitHub issue on atomiq-docs noting the failed check rather than silently failing.
Step 4: Parse response
Claude returns structured JSON. If needs_update is false, exit cleanly (no PR, no noise).
Step 5: Create PR on atomiq-docs
- Check for existing open PR from this repo:
gh pr list --head docs-update/<repo-name> --state open - If found, force-push updated changes to the existing branch
- If not, create new branch:
docs-update/<source-repo>-<short-sha> - Apply file replacements from Claude's response
- Push and create PR via GitHub CLI
- PR body includes Claude's explanation of what changed and why
Prompt Design
System Prompt
You are a documentation maintenance assistant for Atomiq, a trustless
cross-chain DEX. You review code changes and update user-facing
documentation to keep it accurate.
Rules:
- Only modify docs that are actually affected by the code changes
- Preserve the existing writing style and markdown formatting
- Preserve all import statements, JSX components, and MDX-specific
syntax exactly as-is. Only modify prose text and code examples.
- Focus on accuracy: method names, parameter types, usage patterns,
code examples
- Do NOT rewrite docs for style — only fix what the code change broke
- If a code change is purely internal (no public API impact), say
"no docs update needed"
- Atomiq prioritizes backwards compatibility — if something is
deprecated but still works, note the deprecation rather than
removing the old usage
Response Format
{
"needs_update": true,
"summary": "Brief description of what changed and why docs need updating",
"changes": [
{
"file": "docs/developers/quick-start-browser.md",
"reason": "createSwap() now takes an options object instead of positional args",
"replacements": [
{
"old": "const swap = await sdk.createSwap(amount, address)",
"new": "const swap = await sdk.createSwap({ amount, address })"
}
]
}
]
}
The replacement-based format (rather than full file content) reduces output tokens, minimizes risk of corrupting MDX/JSX syntax, and produces cleaner PR diffs.
Caller Workflow Template
This goes in each source repo at .github/workflows/docs-check.yml:
name: Check docs
on:
push:
branches: [main]
paths: [src/**]
jobs:
docs-check:
if: "!contains(github.event.head_commit.message, '[no-docs]')"
uses: atomiqlabs/atomiq-docs/.github/workflows/docs-check-reusable.yml@main
secrets:
ANTHROPIC_API_KEY: ${{ secrets.ANTHROPIC_API_KEY }}
DOCS_REPO_PAT: ${{ secrets.DOCS_REPO_PAT }}
with:
repo-name: ${{ github.event.repository.name }}
doc-paths: docs/developers/
The doc-paths input controls which doc subtree is sent to Claude, keeping the payload focused.
Edge Cases
Multiple rapid pushes
Uses concurrency: docs-update-${{ inputs.repo-name }} to serialize runs per source repo. If an open PR already exists from the same source repo, the action updates it (force-push to existing branch) instead of creating a new one. Detection via gh pr list --head docs-update/<repo-name> --state open.
Claude says no update needed
Action exits cleanly with success status. No PR created, no noise.
Large diffs
If the diff exceeds ~50KB, fall back to:
- Commit messages
- List of changed files
- Public API signature changes (exported function/class/type signatures) Rather than raw line-by-line diff.
PR merge conflicts
If two source repos update the same doc file concurrently, the second PR will have merge conflicts. This is expected and resolved manually during review, same as any normal PR.
API failures
Transient Claude API failures are retried (2 retries, exponential backoff). Persistent failures create a GitHub issue on atomiq-docs with the error details so the check isn't silently lost.
Rollout Plan
- Phase 1: atomiq-sdk (highest doc surface area, most impactful)
- Phase 2: atomiq-chain-starknet, atomiq-chain-solana, atomiq-chain-evm, atomiq-base
- Phase 3: Other repos as needed
Scope Boundaries
In scope
- Hand-written docs in
docs/directory (MD and MDX files) - Developer guides, quick starts, swap type docs, advanced usage
Out of scope
- SDK reference (
sdk-reference/) — auto-generated by TypeDoc, stays in sync automatically - Protocol-level docs (Bitcoin light client, submarine swaps) — these change rarely and are driven by protocol design, not code
- LP setup guides — operational docs, not API-driven
- Internal spec files (
docs/superpowers/)
Cost Estimate
- Claude API call per push to main with src/ changes
- Typical payload: ~30-80KB of filtered docs + ~10KB diff, ~5KB output (replacement format)
- At Sonnet pricing: roughly $0.10-0.50 per run
- Expected frequency: a few times per week across all repos
- Monthly cost: ~$5-20