Docs that keep up.
Somewhere in your product, right now, there is a feature that behaves differently from how your docs say it behaves.
Nobody was careless. The engineer shipped it and moved on, with no reason to open the docs. The writer had no way of knowing anything changed. The gap between the two is real, it is growing, and it lives entirely inside people's heads.
That is the actual problem. Not the writing. The knowing.
You cannot grep a feeling. You cannot put "the docs are probably a bit wrong somewhere" on a board and expect anyone to pick it up. So it sits there, quietly widening, until a customer finds it for you and mentions it in a tone you do not enjoy.
The fix turned out to be almost annoyingly simple.
Our product is a git repository. Our docs are a git repository. So the gap between them is not a feeling at all. It is a range of commits. And a range of commits is something you can hand to an agent and say: close this.
That is the whole idea. Here is how it actually runs, and how to build the same thing for your docs by Friday.
1. First, give the agent files to work with
An agent needs files. Things it can diff, grep, branch, and be reverted from when it inevitably gets something wrong.
The good news is that you do not have to give anything up to get them. GitHub Sync runs in both directions, which is the whole reason we built it that way rather than as an export button with good marketing. Your pages live in a repository as plain Markdoc files and they are still the same pages your team edits visually. Push to the branch and it publishes. Edit in the editor and the change lands in the repository as a commit.
That second direction is the one people undersell. One-way export gives you a backup. Two-way sync means the repository is not a copy of your docs, it is your docs, so the writers who would rather never see a terminal and the agent that lives in one are working on exactly the same pages. Nobody has to pick a side, which is the only reason this survived contact with our own team.
And you do not have to take the agent's word for any of it, because the review happens exactly where your engineers already review things. That is how we run our own: the agent never commits straight to the published branch. It works on a branch and opens a pull request, and DeveloperHub runs a check on every pull request whose base is your synced branch. The check is a read-only dry run of the sync, so it reports what would happen before a single byte is written. Broken relative links or images, and invalid settings, navigation, reference specs or changelog frontmatter, fail it. Everything else comes back as a warning. Mark it required in your branch protection rules and nothing reaches your published docs without going green first.
Which is the good bit, really: the safety net is one your team already knows how to read. No new dashboard, no bespoke approval flow, just a red or green check on a pull request, reviewed by whoever would have reviewed it anyway.
2. Teach the agent your dialect before anything else
This is where we fell over first, and it is worth thirty seconds of your attention because it is the difference between "this works" and "this quietly ate a paragraph".
A good coding agent writes lovely Markdown. Your docs are almost certainly not Markdown. They are Markdown plus components: callouts, tabs, code groups, cards, accordions, API blocks. Every platform has its own dialect and its own opinions about what survives a round trip.
An agent that gets your dialect nearly right produces a diff that looks perfectly fine, then churns on the first save or silently drops a block. You will not catch it in review, because you will be reading the prose, not auditing whether an attribute name is canonical.
So write the dialect down for the agent. Ours is open source and lives at dh-skills:
/plugin marketplace add developerhub-io/dh-skills
/plugin install dh-skills@developerhub
Any other agent that reads the portable skills format:
npx skills add developerhub-io/dh-skills
On a different platform? Same lesson, different package: find or write the equivalent before you automate anything. An agent that is confidently wrong about your syntax is genuinely worse than no agent at all. Setup details in Writing Markdoc with AI Agents.
3. Write the procedure down once, and never explain it again
You can get a decent one-off out of "hey, look at the last month of commits and update the docs". You will get a different decent one-off next week, because you will phrase it differently, and you will have forgotten the caveat you added last time after it got something wrong.
So the workflow itself is a file. A skill: markdown with frontmatter, versioned in the docs repository, spelling out the procedure step by step. Not the goal. The steps.
The magic is not that the agent could not have worked it out. It is that every correction you make becomes permanent. Most of the rules in ours started life as something we did not want to see in a draft. Writing each one down meant never having to say it twice, which is more than can be said for most of my feedback to humans.
The run itself goes: read the checkpoint, take the commit range, throw out everything nobody would notice, verify what is left against the actual source, check what the docs already say, draft the pages, then the changelog, and stop for a human.
That fourth step is the one that earns its keep. Commit subjects are lossy and frequently lie about scope. So nothing gets written from a subject line: the agent opens the diffs, reads the changed files, and quotes real button labels and field names rather than paraphrasing something plausible.
The triage step is where most of the volume disappears, and the split matters more than it looks:
4. One small file turns this from a stunt into a loop
Everything so far describes a good one-off catch-up. Exactly one thing turns it into a loop, and it is a tiny JSON file:
{
"product_repo": "<path or remote>",
"branch": "main",
"checkpoint": { "sha": "...", "date": "...", "subject": "..." },
"synced_on": "2026-08-08"
}
That is it. The last product commit already reflected in the docs. Recorded, never inferred.
Inferring it is the tempting shortcut and it rots. "Commits since the docs were last touched" sounds equivalent and absolutely is not: docs get edited for typos, someone fixes a link, a range legitimately produces no changes at all. Every one of those silently drags an inferred watermark past work nobody ever documented. Now your loop has a hole in it and nothing will ever tell you.
Two rules keep the file honest, and they are the two we get asked about most.
A run that finds nothing still moves the checkpoint. "Nothing here was worth documenting" is a real result. Recording it is the entire point.
A rejected run does not move it. Bad drafts, another pass, an abandoned run: the checkpoint stays exactly where it was and the next run re-reads the same commits. Re-reading is cheap. Skipping is invisible. Always fail in the direction of doing the work twice.
Keep the file out of the sync, incidentally. Ours is excluded from the docs repository's git entirely, because your workflow's bookkeeping is not documentation and should not publish as a page.
5. Three rules worth stealing
Verification depth is not documentation depth. The verification step comes back with far more than belongs on a page, and the instinct is to use all of it because it was expensive to gather. Don't. That detail exists so what you write is correct, not so that all of it gets published.
Write for the reader, not for the commit. A change being user-facing is not the same as it being worth a sentence. Document what someone has to do, choose, or understand to avoid a nasty surprise. Leave out the things that simply work. Nobody has ever opened your docs to learn that a pane now scrolls correctly. Our test: if a reader would never have noticed the change unless it regressed, it does not go on the page.
Nothing publishes unreviewed. Every run ends with a summary: the branch and range scanned, what changed and why, which commits each change covers, and anything it could not verify. That last item is the one that buys the most trust. An agent that volunteers what it was unsure about is one you can leave alone with your docs.
6. Build it yourself
Six steps. The first two are the ones people skip, and then wonder why the output is inconsistent.
1. Get your docs into a repository. In DeveloperHub: Project Settings, Integrations, connect GitHub, pick a repository and branch, choose whether the first sync exports your pages or imports the repository's files. Two-way from then on. Full walkthrough in GitHub Sync.
2. Teach the agent your format. Install dh-skills as above. This is the step that decides whether the output is safe to publish.
3. Write the style contract. A CLAUDE.md or AGENTS.md at the root of the docs repo, holding what a style guide holds and an agent will otherwise get wrong on instinct: spelling, punctuation you never use, how internal links are written, product terminology (we say "user menu", never "avatar"), and what the docs never mention at all, like pricing. Keep it short and make every rule name a specific habit and forbid it. Ours is about a page.
4. Write the sync skill. One markdown file. Yours will differ in the details, but this is the shape:
---
name: sync-docs-from-product
description: Find product changes shipped since the docs were last updated,
decide which are user-facing, and update the docs and changelog to cover them.
---
# Sync docs from product commits
Docs repo: <path>. Product repo: <path>. Every push publishes.
## 1. Read the checkpoint
Read `<skill-dir>/last-sync.json`. Confirm the SHA still exists (it may have
been rebased away). If not, fall back to the docs repo's last commit date and
say that you did.
## 2. Take the range
Report which branch the product repo is on. Capture HEAD now and carry it
through the run. List `<checkpoint>..HEAD`. Use the commit range, not a date
filter: a date filter runs on committer date and silently skips a commit
authored before the watermark but merged after it.
## 3. Triage
Keep: new features, changed behaviour, new endpoints or commands, new UI.
Drop: refactors, dependency bumps, CI and build, lint, tests, internal tooling.
Keep a `fix:` only if it changes behaviour someone relies on.
## 4. Verify against the source
Do not trust commit subjects. Read the diffs and the changed files. Quote real
labels, flags and defaults. Separate what you confirmed from what you inferred.
## 5. Cross-check, then draft
Search the docs before writing; prefer updating a page over creating one.
Write the pages first, then the changelog, so its entries can link to them.
Resolve every link against the repo. Never invent a slug.
## 6. Present, then publish
Summarise the branch and range scanned, what changed and why, and anything you
could not verify. Do not commit until asked.
## 7. Move the checkpoint
Rewrite the state file with the head from step 2. Move it when the work is
accepted, or when the range legitimately produced nothing. Do not move it if
the drafts were rejected or the run was abandoned.
5. Add the checkpoint file, and keep it out of the sync. .gitignore, or .git/info/exclude if you would rather the ignore rule itself did not sync as content. Seed it by hand with the SHA your docs are genuinely current as of, and be honest about that first value, because everything downstream inherits it.
6. Pick a cadence. We run it when we ship. You could do weekly or daily.
7. Optional, and better than it sounds: give it eyes. Hand your agent a browser tool (Chrome MCP, Playwright, whatever it speaks) and it stops having to describe the new setting. It can open your app, click through to the thing that changed, and capture the screenshot the page needs. Put the house rules for a clean shot in the style contract, the viewport, which account to sign in with, which dev-only overlays to strip out of the DOM first, and the screenshots come out consistent without anyone opening a cropping tool. This is the step that surprised us most: a screenshot going stale is the most common way a page starts lying, and it is also the bit nobody ever volunteers to redo.
7. The best part: let your readers tell you what to write next
Everything above closes the gap between the product and the docs. There is a second gap, and for a while it was the honest limitation of this whole setup: the distance between what your docs say and what people actually turn up wanting to know.
Except that gap is not a mystery either. Your readers have been filing reports the whole time.
Every search that returns nothing is somebody telling you, in their own words, what they expected to find and what they called it. Every thumbs-down with a comment attached is a page failing in public. All of it has been sitting in your analytics the whole time. The only thing missing was a way to put it in front of the agent doing the writing.
So we handed it to the agent too. Our editor MCP server now exposes three more tools: get_search_analytics, which defaults to the searches that returned nothing at all, plus get_project_feedback and get_page_feedback for reader ratings and the comments they left. Ask which pages readers rate worst, and you get an answer. Ask what people searched for and did not find, and you get a ranked list of the docs you have not written.
Two inputs, one docs repository. The commits tell you what shipped. The readers tell you what you never wrote in the first place.
8. What it still will not do
It will not write your conceptual documentation. Getting-started guides, the architecture explainer, the page that says why a feature exists at all: none of that is derivable from a diff or a search log. This keeps your reference material honest so you have time for the writing that actually needs a human.
And it does not remove the reviewer. It removes the part of the job that was never really writing: working out what changed, digging through code to check what it truly does, and remembering which page mentions it. What is left is judgement, which is the part worth a person's afternoon.
The result
Our docs and our changelog now track the product on a cadence rather than on good intentions. Something ships, there is a run. The run finds nothing, it says so and moves the watermark. It finds something, a human reads the draft and presses publish.
The gap did not close because anyone tried harder. It closed because it stopped being a feeling and became a diff.
If your docs already live in a repository, the setup above is an afternoon. If they do not, GitHub Sync is where to start, and the skills are on GitHub for anyone to use or fork or improve.