Webhook icon
If icon
Exit icon
Request icon
Log icon
Download icon
WorkingDirectory icon
Clone icon
Script icon
Loop icon
ChatCompletion icon
Anthropic icon
SlackIncomingWebhook icon
Pause icon
PushExecutionFiles icon
Create icon
Comment icon
Set icon

Docs Drift Guard with AI-Proposed Fixes and Approval for Every Merged Pull Request

When a pull request is merged, find docs that still mention what it renamed or removed, let AI propose fixes, and open a docs PR after approval.

Categories
AIInfrastructure

Code changes are checked by compilers, tests and CI. Documentation is not. When a pull request renames a CLI flag, an environment variable or a function, the README and the docs keep describing the old name until a user copies a command that no longer works. This blueprint catches that drift at merge time. It's for maintainers, platform teams and any team that keeps its docs in the same repository as its code.

How it works

  1. on_pull_request (io.kestra.plugin.core.trigger.Webhook) receives GitHub pull request events. Payloads without a pull request, such as GitHub's ping, stop at check_event.
  2. pull_request (io.kestra.plugin.core.http.Request) reads the pull request. guard (io.kestra.plugin.core.flow.If) stops when it isn't merged, or when the KV store shows it was already handled, so re-delivered webhooks never open a second docs PR.
  3. pr_diff (io.kestra.plugin.core.http.Download) saves the diff. find_affected_docs (io.kestra.plugin.core.flow.WorkingDirectory) clones the merge commit (io.kestra.plugin.git.Clone), and match (io.kestra.plugin.scripts.python.Script) lists the CLI flags, environment variables, functions, config keys and API paths that disappeared from the code. It then finds the docs pages that still mention them. This is plain text matching, so a pull request that touches no documented name costs nothing.
  4. check_pages (io.kestra.plugin.core.flow.Loop, three pages at a time) asks judge_page (io.kestra.plugin.ai.completion.ChatCompletion with Anthropic) whether each page is now wrong. The model answers in JSON with exact find-and-replace edits. A page whose answer can't be parsed is reported as not checked.
  5. build_patch applies only edits whose text appears exactly once on the page, so an invented or ambiguous edit can't corrupt a page. It writes docs.diff and a summary table.
  6. ask_reviewer (io.kestra.plugin.slack.notifications.SlackIncomingWebhook) posts a link, and approval (io.kestra.plugin.core.flow.Pause) waits for a decision: open the docs PR or discard. Without a decision within three days, the execution is cancelled.
  7. On approval, rebase_edits re-applies the edits on the latest base branch, so later docs changes are kept. push_branch (io.kestra.plugin.git.PushExecutionFiles) commits only the edited pages to docs-drift/pr-<number>, with delete: false. open_docs_pr (io.kestra.plugin.github.pulls.Create) opens the PR, and comment_on_source_pr (io.kestra.plugin.github.issues.Comment) links it from the original PR.
  8. save_outcome (io.kestra.plugin.core.kv.Set) records the result, including "no change needed" and "discarded". alert_failure posts any failure to Slack.

Untrusted text (the diff, the pages and the model's edits) travels as files or base64, so text such as {{ ... }} in your docs is never evaluated as a Kestra expression.

Prerequisites

  • A GitHub repository with its docs in Markdown, and a repository webhook for Pull requests events with content type application/json, pointing at https://<your-kestra>/api/v1/main/executions/webhook/company.team/docs-drift-guard/<DOCS_DRIFT_WEBHOOK_KEY>.
  • Docker on the worker for the Python script tasks.
  • Secrets:
    • GITHUB_TOKEN: a token for the repository. It reads the pull request, clones the repository, pushes the docs branch, opens the PR and comments. A fine-grained token needs Contents: read and write and Pull requests: read and write. It's also used as the HTTPS username, which GitHub accepts.
    • ANTHROPIC_API_KEY: Anthropic API key for judge_page.
    • SLACK_WEBHOOK_URL: Slack incoming webhook for the review request and failure alerts.
    • DOCS_DRIFT_WEBHOOK_KEY: a long random string that forms the secret part of the webhook URL.

Inputs

  • repository (STRING, default your-org/your-repo): owner/name. The webhook fills it in from the event.
  • pr_number (INT, default 1): the merged pull request. The webhook fills it in from the event.
  • docs_paths (STRING, default README.md,docs/**/*.md): comma-separated glob patterns for the documentation.
  • max_pages (INT, default 8): the most pages sent to the model per pull request, which caps the cost.
  • dry_run (BOOL, default false): propose fixes and stop. No review request, branch, PR or saved state.

Quick start

  1. Add the four secrets and save the flow.
  2. Pick a merged pull request that renamed a flag, variable or function, and run the flow manually with repository, pr_number and dry_run set to true.
  3. Read docs.diff and the summary in the patch outputs.
  4. Run it again with dry_run set to false, approve in the paused execution, and check the docs PR.
  5. Add the repository webhook so every merge is checked automatically.

Outputs

  • {{ outputs.pages_matched }} / {{ outputs.match.vars.candidate_count }}: pages that still mention a removed or renamed identifier. {{ outputs.match.vars.removed_identifiers }} lists the identifiers.
  • {{ outputs.docs_patch }} / {{ outputs.patch.outputFiles['docs.diff'] }}: the unified diff of the validated edits.
  • {{ outputs.patch.vars.summary }}, edits_accepted and edits_rejected: the per-page result and reason.
  • {{ outputs.docs_pr_url }} / {{ outputs.open_docs_pr.pullRequestUrl }}: the docs pull request.
  • KV key docs-drift-<owner>-<repo>-<number>: pages matched, files changed, the decision, the docs PR link and the execution ID.

Common pitfalls

  • Detection looks for identifiers that left the code: CLI flags, UPPER_CASE variables, function and class names, config keys and /api or /v1 paths. A behaviour change that keeps every name, such as a new default value, is not detected.
  • The model sees each matched page once. Pages longer than 40,000 characters are cut, and only the first max_pages matches are reviewed.
  • Delete the KV key to check a pull request again. If a docs-drift/pr-<number> branch already exists, delete it first.
  • The webhook URL is public. Keep DOCS_DRIFT_WEBHOOK_KEY long and random. The flow also confirms with the GitHub API that the pull request really was merged before doing anything.
  • Set the claude_model variable to change the model.

How to extend

  • Point docs_paths at another docs folder, or at .rst or .mdx files.
  • Send the review request to the pull request author by adding a GitHub mention to the Slack message, or request reviewers with the reviewers property of open_docs_pr.
  • Run it from a Schedule over the day's merged pull requests instead of a webhook.

Links

See How

New to Kestra?

Use blueprints to kickstart your first workflows.