Skip to content

feat: daily docs-sync bot keeping kilo-docs in sync with merged PRs - #12512

Merged
iscekic merged 7 commits into
mainfrom
feat/docs-sync-bot
Jul 24, 2026
Merged

feat: daily docs-sync bot keeping kilo-docs in sync with merged PRs#12512
iscekic merged 7 commits into
mainfrom
feat/docs-sync-bot

Conversation

@iscekic

@iscekic iscekic commented Jul 24, 2026

Copy link
Copy Markdown
Collaborator

What

Adds .github/workflows/docs-sync.yml: a daily (07:00 UTC) + manually dispatchable workflow that keeps packages/kilo-docs in sync with features merged to Kilo-Org/cloud and this repo, by running Kilo CLI headless and maintaining one rolling PR for human review.

How it works

  1. Watermark — derived from the bot's own PR body marker (<!-- docs-sync: processed-through ... -->): latest open auto-docs PR, else last merged one, else 72h fallback, capped at 14 days. No external state; missed or failed runs self-heal on the next run. Manual replays via the since dispatch input.
  2. Collect — GitHub search API for PRs merged since the watermark in both repos; deterministic pre-filter drops auto-docs-labeled PRs (loop guard), chore|test|ci|build|docs|style|refactor|revert titles, and docs-only-path PRs. Bot-authored PRs are kept (release/dependency bots ship user-facing changes). Emits a slim digest (for triage) and a full digest with patch excerpts (for the edit pass).
  3. Triage (Kilo CLI, chunks of 25) — JSON classification: docs-worthy? which docs sections? priority? A chunk that fails twice degrades to "unclassified" entries instead of failing the run; every digest PR is guaranteed a triage entry so the PR body's skipped table is complete.
  4. Edit (Kilo CLI, batches of 5) — the agent edits packages/kilo-docs following its own AGENTS.md + STYLE_GUIDE.md (Markdoc tags, nav updates, redirect rules), with bounded context per batch. Batch failures surface as skipped entries in the PR body; nothing fails silently.
  5. Verifykilo-docs build + test suite (content-integrity, headings, sitemap, redirects). One LLM fix pass on failure; still red → the PR opens as a draft.
  6. Rolling PR — branch docs/auto-sync; while a PR is open the next run appends to it (changes/skipped tables merge via marker sections, humans' commits on the branch are preserved); after a merge a fresh branch starts. Draft when more than 15 files change or verification fails. Every change row links the merged PR it documents.

Files

  • .github/workflows/docs-sync.yml — the pipeline (also added to the script/check-workflows.ts allowlist)
  • .github/docs-sync/*.mjs — dependency-free Node helpers: watermark, collect, triage, filter-worthy, edit, prepare-branch, upsert-pr, extract-json, lib
  • .github/docs-sync/triage-prompt.md, edit-prompt.md — the agent prompts; tune these to adjust quality

Manual steps (after merge)

  1. First validation run — Actions → docs-sync → Run workflow with dry_run=true and since set to ~7 days back. Download the docs-sync-out artifact, read triage.json, and tune .github/docs-sync/triage-prompt.md if the verdicts look wrong. No branches or PRs are created in dry-run mode.
  2. First real run — dispatch again with dry_run=false and review the rolling PR it opens. The 07:00 UTC cron activates automatically once this lands on the default branch.
  3. Later, when satisfied — remove auto-docs.yml and the DOC_WEBHOOK_* secrets from both repos (left in place intentionally for now; @iscekic will do this manually).
  4. Optional tuning — set repo variables DOCS_SYNC_TRIAGE_MODEL and/or DOCS_SYNC_EDIT_MODEL to change the default (kilo/moonshotai/kimi-k3; the edit pass runs at --variant high). A cheaper model for triage is the obvious first knob.

No other setup needed, already verified in this repo:

  • ✅ Auth: the workflow reuses the existing KILO_API_KEY secret, which the CLI's kilo provider reads natively (no config file or extra secret required).
  • ✅ "Allow GitHub Actions to create and approve pull requests" is already enabled (can_approve_pull_request_reviews: true).
  • POSTHOG_API_KEY exists (verify step uses it for the docs build, same as docs-build.yml).

Testing

  • collect.mjs run live against both repos: ~130 merged PRs in a 3.5-day window → 116 kept after the deterministic pre-filter.
  • Real end-to-end triage with the production prompt + kilo/moonshotai/kimi-k3: 116 PRs in 5 chunks → 116 entries, 25 docs-worthy, 0 failed chunks. Verdict quality hand-checked (accurate mobile/JetBrains/CLI feature classifications with sound reasons).
  • Kilobot reviewed (2 rounds, 14 threads): round 1 — 10 findings fixed in 4c68610 (HTML-comment sanitization, non-content draft gate, conflict-preserving branch handoff, API retry coverage, per-PR isolation, watermark trust + future clamp, chunk subset validation, files_total accuracy, edit stderr piping, comment wording), 1 already resolved earlier; round 2 — 3 findings fixed in 8b7da4d (pipe escaping in changeRow, sanitizing agent-chosen file paths into draftReasons, logging expected fetch misses). Every thread replied to and resolved.
  • triage.mjs and edit.mjs failure paths validated with a shimmed kilo binary: retries, degrade-on-failure, coverage-fill, and dedupe all behave (permanently failing chunks/batches become visible "skipped" rows, never silent).
  • upsert-pr.mjs body rendering round-trip tested (marker sections merge without duplicates, watermark marker updates, draft reasons render).
  • Repo checks pass locally: check-workflows.ts, check-opencode-annotations.ts, check-forbidden-strings.ts; the prompt files contain no GFM tables (table-padding check not triggered).
  • kilo run verified working on Linux (fresh install in a node:24 container) and macOS.

CLI invocation notes (baked into the code)

  • Message positional must come before flags: --file is multi-value and consumes a trailing message as a file path (File not found). All call sites use kilo run "<msg>" ... -f file ordering.
  • kilo run prints the assistant message twice (streaming render + final summary), so raw stdout can contain the same JSON array back-to-back — extract-json.mjs parses the largest valid trailing array.
  • Invoking kilo run from inside another running Kilo harness inherits KILO_RUN_ID/KILO_SERVER_* env and fails with Session not found — scrub the env or use a clean shell locally; CI runners are unaffected.

Notes

  • Rolling PRs use the auto-docs label (auto-created on first run) and the docs/ branch prefix per kilo-docs conventions.
  • The workflow never checks out or executes code from PR branches (schedule/dispatch only, minimal permissions).
  • A timed-out or failed run creates no PR and advances no watermark, so the next run simply redoes the window — safe to re-dispatch at any time.

Adds a scheduled workflow that keeps packages/kilo-docs in sync with PRs
merged to Kilo-Org/cloud and Kilo-Org/kilocode:

- watermark.mjs derives the processing window from the bot's own PR body
  marker (self-healing, no external state; 72h fallback, 14d cap)
- collect.mjs queries merged PRs via the GitHub API and applies a
  deterministic pre-filter (bots, chores, docs-only PRs)
- triage.mjs classifies PRs in chunks of 25 with kilo run; failed chunks
  degrade to unclassified instead of failing the run
- edit.mjs updates docs in batches of 5 PRs with kilo run, bounded per
  batch; failures surface as skipped entries in the PR body
- verify runs the kilo-docs build + test suite; one LLM fix pass on
  failure; still-red becomes a draft PR
- upsert-pr.mjs maintains one rolling auto-docs PR (appends while open,
  fresh branch after merge), with a 15-file draft cap and a
  machine-readable processed-through watermark

Also adds docs-sync.yml to the workflow allowlist in
script/check-workflows.ts.
@iscekic iscekic self-assigned this Jul 24, 2026
Comment thread .github/docs-sync/edit.mjs Outdated
Comment thread .github/docs-sync/upsert-pr.mjs Outdated
Comment thread .github/docs-sync/prepare-branch.mjs Outdated
Comment thread .github/docs-sync/collect.mjs Outdated
Comment thread .github/docs-sync/watermark.mjs
Comment thread .github/docs-sync/triage.mjs
Comment thread .github/docs-sync/lib.mjs
Comment thread .github/docs-sync/extract-json.mjs Outdated
Comment thread .github/docs-sync/collect.mjs Outdated
@kilo-code-bot

kilo-code-bot Bot commented Jul 24, 2026

Copy link
Copy Markdown
Contributor

Code Review Summary

Status: No Issues Found | Recommendation: Merge

Incremental review of commit 99b04972d0 vs previously-reviewed 8b7da4d06b. One file changed: collect.mjs. The change removes the author.endsWith("[bot]") pre-filter drop (and the associated dropped.bot counter/summary line) so bot-authored PRs (e.g. release/dependency bots) are no longer excluded from the digest. The existing auto-docs label check still guards against the bot's own rolling PR causing a self-referential loop, and the docs-only path filter still applies, so this is a scoped, intentional behavior change matching the PR description rather than a regression. No bugs, dangling references to the removed dropped.bot field, or new issues were introduced.

All previously reported findings in this PR (unscoped agent write access, watermark trust, cross-chunk triage contamination, retry coverage, PR-body interpolation, file-list classification, JSON array extraction, etc.) are in unchanged files/lines for this commit and remain tracked on their existing resolved threads — not repeated here.

Files Reviewed (1 file changed in this commit)
  • .github/docs-sync/collect.mjs - no new issues
Previous Review Summaries (5 snapshots, latest commit 8b7da4d)

Current summary above is authoritative. Previous snapshots are kept for context only.

Previous review (commit 8b7da4d)

Status: No Issues Found | Recommendation: Merge

Incremental review of commit 8b7da4d06b (fix: address second Kilobot review round) vs previously-reviewed 4c68610a5. Two files changed: prepare-branch.mjs and upsert-pr.mjs. This commit resolves the three previously-tracked findings on those files:

  • upsert-pr.mjs:36 (SUGGESTION) — changeRow now escapes | in e.action, matching skippedRow.
  • upsert-pr.mjs non-content file list (WARNING) — agent-chosen file paths are now run through clean() and pipe-escaped before joining into the PR body.
  • prepare-branch.mjs:41 (SUGGESTION) — the two previously-empty catch blocks around the dated/stale branch fetch now log an informative message instead of silently swallowing the error.

No new issues were introduced by this commit. All other previously reported findings in this PR (unscoped agent write access, unauthenticated watermark trust, cross-chunk triage contamination, limited retry coverage, missing per-PR error isolation, force-reset branch handling, unescaped PR-body interpolation, truncated file-list classification, JSON array extraction) are unchanged by this commit, in unchanged files, and remain tracked on their existing threads — not repeated here.

Files Reviewed (2 files changed in this commit)
  • .github/docs-sync/prepare-branch.mjs - previous SUGGESTION resolved
  • .github/docs-sync/upsert-pr.mjs - previous SUGGESTION + WARNING resolved

Previous review (commit 4c68610)

Status: 1 Issue Found | Recommendation: Address before merge

Incremental review of commit e37cabb2d (fix: handle kilo run double-printed assistant output) vs previously-reviewed d1f66fe. Only .github/docs-sync/extract-json.mjs changed. This commit resolves the previously-tracked WARNING there ("JSON array extraction is not bracket-depth aware") by rewriting parseTriageEntries to brute-force try each [ from the end against the final ] and validate the parsed result, correctly handling kilo run's double-printed stdout. The nine other previously reported findings (unscoped agent write access + inherited secret, unescaped PR-body interpolation, force-reset branch, missing per-PR error isolation, unauthenticated watermark trust, cross-chunk triage contamination, limited retry coverage, truncated file-list classification, dead stderr-tail logging in edit.mjs) are unchanged by this commit and remain tracked as active inline comments — not repeated here.

Overview

Severity Count
CRITICAL 0
WARNING 0
SUGGESTION 1
Issue Details (click to expand)

SUGGESTION

File Line Issue
.github/docs-sync/extract-json.mjs 15 Comment says the loop finds the "largest valid trailing array", but it actually returns the narrowest (most recent) match first and only widens on failure
Files Reviewed (1 file changed in this commit)
  • .github/docs-sync/extract-json.mjs - 1 issue (previous WARNING resolved)

Fix these issues in Kilo Cloud

Previous review (commit e37cabb)

Status: 1 Issue Found | Recommendation: Address before merge

Incremental review of commit e37cabb2d (fix: handle kilo run double-printed assistant output) vs previously-reviewed d1f66fe. Only .github/docs-sync/extract-json.mjs changed. This commit resolves the previously-tracked WARNING there ("JSON array extraction is not bracket-depth aware") by rewriting parseTriageEntries to brute-force try each [ from the end against the final ] and validate the parsed result, correctly handling kilo run's double-printed stdout. The nine other previously reported findings (unscoped agent write access + inherited secret, unescaped PR-body interpolation, force-reset branch, missing per-PR error isolation, unauthenticated watermark trust, cross-chunk triage contamination, limited retry coverage, truncated file-list classification, dead stderr-tail logging in edit.mjs) are unchanged by this commit and remain tracked as active inline comments — not repeated here.

Overview

Severity Count
CRITICAL 0
WARNING 0
SUGGESTION 1
Issue Details (click to expand)

SUGGESTION

File Line Issue
.github/docs-sync/extract-json.mjs 15 Comment says the loop finds the "largest valid trailing array", but it actually returns the narrowest (most recent) match first and only widens on failure
Files Reviewed (1 file changed in this commit)
  • .github/docs-sync/extract-json.mjs - 1 issue (previous WARNING resolved)

Fix these issues in Kilo Cloud

Previous review (commit d1f66fe)

Status: 1 Issue Found | Recommendation: Address before merge

Incremental review of commit d1f66fe (fix: correct kilo run invocation and auth) vs previously-reviewed 405ed74. The nine previously reported findings (unscoped agent write access + inherited secret, unescaped PR-body interpolation, force-reset branch, missing per-PR error isolation, unauthenticated watermark trust, cross-chunk triage contamination, limited retry coverage, non-bracket-aware JSON extraction, truncated file-list classification) are unchanged by this commit and remain tracked as active inline comments — not repeated here.

Overview

Severity Count
CRITICAL 0
WARNING 1
SUGGESTION 0
Issue Details (click to expand)

WARNING

File Line Issue
.github/docs-sync/edit.mjs 69 New stderr-tail logging is dead code: execFileSync still uses stdio: ["ignore", "inherit", "inherit"], so err.stderr is always empty and the log always falls back to err.message, unlike the equivalent fix in triage.mjs (which uses pipe)
Files Reviewed (3 files changed in this commit)
  • .github/docs-sync/edit.mjs - 1 issue
  • .github/docs-sync/triage.mjs
  • .github/workflows/docs-sync.yml

Fix these issues in Kilo Cloud

Previous review (commit 405ed74)

Status: 9 Issues Found | Recommendation: Address before merge

Overview

Severity Count
CRITICAL 3
WARNING 5
SUGGESTION 1
Issue Details (click to expand)

CRITICAL

File Line Issue
.github/docs-sync/edit.mjs 61 Agent write access unscoped (--dir process.cwd()) plus inherited KILO_CONFIG_CONTENT secret, combined with untrusted PR content in context
.github/docs-sync/upsert-pr.mjs 30 action/reason interpolated into PR body unescaped for HTML-comment sequences — watermark/marker spoofing risk
.github/docs-sync/prepare-branch.mjs 36 Merge-conflict path force-resets the branch, discarding human commits despite the stated preservation guarantee

WARNING

File Line Issue
.github/docs-sync/collect.mjs 67 No error isolation around per-PR API calls; one flaky PR aborts the whole run
.github/docs-sync/watermark.mjs 32 Watermark marker trusted from any auto-docs-labeled PR with no author check, and no upper-bound clamp
.github/docs-sync/triage.mjs 77 Shared seen Set across chunks allows cross-chunk contamination of triage results
.github/docs-sync/lib.mjs 52 Retry logic only covers 403 rate limits, not other transient/5xx failures
.github/docs-sync/extract-json.mjs 15 JSON extraction via indexOf/lastIndexOf is not bracket-depth aware

SUGGESTION

File Line Issue
.github/docs-sync/collect.mjs 69 Docs-only classification and files_total based on pagination-truncated file list (300-file cap)
Files Reviewed (13 files)
  • .github/docs-sync/collect.mjs - 2 issues
  • .github/docs-sync/edit-prompt.md
  • .github/docs-sync/edit.mjs - 1 issue
  • .github/docs-sync/extract-json.mjs - 1 issue
  • .github/docs-sync/filter-worthy.mjs
  • .github/docs-sync/lib.mjs - 1 issue
  • .github/docs-sync/prepare-branch.mjs - 1 issue
  • .github/docs-sync/triage-prompt.md
  • .github/docs-sync/triage.mjs - 1 issue
  • .github/docs-sync/upsert-pr.mjs - 1 issue
  • .github/docs-sync/watermark.mjs - 1 issue
  • .github/workflows/docs-sync.yml
  • script/check-workflows.ts

Fix these issues in Kilo Cloud


Reviewed by claude-sonnet-5 · Input: 22 · Output: 3.7K · Cached: 456.9K

Review guidance: REVIEW.md from base branch main

- message positional must come before flags: --file is multi-value and
  consumes a trailing message as a file path (File not found)
- authenticate via the existing KILO_API_KEY repo secret (the kilo
  provider reads it natively); drop the DOCS_SYNC_KILO_CONFIG config
  secret requirement
- fix default model IDs: gateway provider id is kilo/, not kilocode/
- include stderr tail in triage/edit failure logs
Comment thread .github/docs-sync/edit.mjs
kilo run prints the assistant message twice (streaming render + final
summary), so stdout can contain the same JSON array back-to-back. Parse
the largest valid trailing array instead of slicing first-to-last
bracket. Verified against real chunked triage output.
Comment thread .github/docs-sync/extract-json.mjs
iscekic added 2 commits July 24, 2026 15:31
- edit.mjs: unambiguous summary file path in the batch prompt and a
  fallback read when the agent drops the docs-sync-out/ prefix, so real
  edits never report as skipped
- prepare-branch.mjs: use the open auto-docs PR's actual head.ref
  instead of assuming docs/auto-sync
- upsert-pr.mjs: compute the 15-file draft cap on the cumulative PR
  diff (origin/main...HEAD), not just the latest commit
Security:
- sanitize HTML-comment sequences out of agent-generated PR body values
  so a crafted value cannot forge section markers or the watermark
- draft any PR whose diff touches non-content files in packages/kilo-docs
  (outside pages/ and lib/nav/) — build-executable changes force human
  review before merge
- on merge conflict, keep the conflicted rolling branch untouched
  (preserving human commits) and continue on a fresh dated branch that
  links the old PR

Resilience:
- retry GitHub API calls on network errors and 5xx, not just 403
  rate limits
- isolate per-PR collect failures instead of aborting the run
- trust watermark markers only on bot-authored PRs and clamp future
  dates loudly
- validate chunk triage entries belong to their chunk before the shared
  dedupe
- use changed_files for files_total and skip docs-only classification
  on truncated (300+) file lists
- pipe stderr in the edit pass so failure warnings carry the real CLI
  error
Comment thread .github/docs-sync/upsert-pr.mjs
Comment thread .github/docs-sync/upsert-pr.mjs Outdated
Comment thread .github/docs-sync/prepare-branch.mjs
- escape pipe characters in changeRow actions (same as skippedRow)
- sanitize agent-chosen file paths before they land in draftReasons
  and the PR body (residual marker-forgery path via filenames)
- log expected fetch misses in prepare-branch instead of silent catches
@marius-kilocode

Copy link
Copy Markdown
Collaborator

@iscekic Documentation is often not properly reviewed (especially if this will run after a large merge). If somebody would manage to poison the docs and get through the review, what credentials will be exposed to a potential injection attack?

@iscekic

iscekic commented Jul 24, 2026

Copy link
Copy Markdown
Collaborator Author

@iscekic Documentation is often not properly reviewed (especially if this will run after a large merge). If somebody would manage to poison the docs and get through the review, what credentials will be exposed to a potential injection attack?

@marius-kilocode You mean a prompt attack on the agent making the doc update? I'm guessing any secret we have available to CI.

Release and dependency bots ship user-facing changes (e.g. JetBrains
release PRs from kilo-maintainer[bot]). The auto-docs label check and
docs-only path filter remain as the loop guards.
@iscekic
iscekic enabled auto-merge (squash) July 24, 2026 15:34
@iscekic
iscekic merged commit 1346963 into main Jul 24, 2026
29 checks passed
@iscekic
iscekic deleted the feat/docs-sync-bot branch July 24, 2026 15:38
t7tran pushed a commit to t7tran/kilocode that referenced this pull request Aug 14, 2026
…ilo-Org#12512)

* feat: daily docs-sync bot workflow (Kilo CLI)

Adds a scheduled workflow that keeps packages/kilo-docs in sync with PRs
merged to Kilo-Org/cloud and Kilo-Org/kilocode:

- watermark.mjs derives the processing window from the bot's own PR body
  marker (self-healing, no external state; 72h fallback, 14d cap)
- collect.mjs queries merged PRs via the GitHub API and applies a
  deterministic pre-filter (bots, chores, docs-only PRs)
- triage.mjs classifies PRs in chunks of 25 with kilo run; failed chunks
  degrade to unclassified instead of failing the run
- edit.mjs updates docs in batches of 5 PRs with kilo run, bounded per
  batch; failures surface as skipped entries in the PR body
- verify runs the kilo-docs build + test suite; one LLM fix pass on
  failure; still-red becomes a draft PR
- upsert-pr.mjs maintains one rolling auto-docs PR (appends while open,
  fresh branch after merge), with a 15-file draft cap and a
  machine-readable processed-through watermark

Also adds docs-sync.yml to the workflow allowlist in
script/check-workflows.ts.

* fix: correct kilo run invocation and auth

- message positional must come before flags: --file is multi-value and
  consumes a trailing message as a file path (File not found)
- authenticate via the existing KILO_API_KEY repo secret (the kilo
  provider reads it natively); drop the DOCS_SYNC_KILO_CONFIG config
  secret requirement
- fix default model IDs: gateway provider id is kilo/, not kilocode/
- include stderr tail in triage/edit failure logs

* fix: handle kilo run double-printed assistant output

kilo run prints the assistant message twice (streaming render + final
summary), so stdout can contain the same JSON array back-to-back. Parse
the largest valid trailing array instead of slicing first-to-last
bracket. Verified against real chunked triage output.

* fix: reviewer-pass robustness fixes

- edit.mjs: unambiguous summary file path in the batch prompt and a
  fallback read when the agent drops the docs-sync-out/ prefix, so real
  edits never report as skipped
- prepare-branch.mjs: use the open auto-docs PR's actual head.ref
  instead of assuming docs/auto-sync
- upsert-pr.mjs: compute the 15-file draft cap on the cumulative PR
  diff (origin/main...HEAD), not just the latest commit

* fix: address Kilobot review findings

Security:
- sanitize HTML-comment sequences out of agent-generated PR body values
  so a crafted value cannot forge section markers or the watermark
- draft any PR whose diff touches non-content files in packages/kilo-docs
  (outside pages/ and lib/nav/) — build-executable changes force human
  review before merge
- on merge conflict, keep the conflicted rolling branch untouched
  (preserving human commits) and continue on a fresh dated branch that
  links the old PR

Resilience:
- retry GitHub API calls on network errors and 5xx, not just 403
  rate limits
- isolate per-PR collect failures instead of aborting the run
- trust watermark markers only on bot-authored PRs and clamp future
  dates loudly
- validate chunk triage entries belong to their chunk before the shared
  dedupe
- use changed_files for files_total and skip docs-only classification
  on truncated (300+) file lists
- pipe stderr in the edit pass so failure warnings carry the real CLI
  error

* fix: address second Kilobot review round

- escape pipe characters in changeRow actions (same as skippedRow)
- sanitize agent-chosen file paths before they land in draftReasons
  and the PR body (residual marker-forgery path via filenames)
- log expected fetch misses in prepare-branch instead of silent catches

* feat: keep bot-authored PRs in the docs-sync digest

Release and dependency bots ship user-facing changes (e.g. JetBrains
release PRs from kilo-maintainer[bot]). The auto-docs label check and
docs-only path filter remain as the loop guards.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants