Everything in this module so far has been per-agent mechanics. This topic assembles it into one workflow you can actually run against a real project: a bidirectional sync loop where Confluence specs drive initial code scaffolding, code changes drive documentation updates, and a scheduled automation keeps drift from accumulating silently — which it otherwise always does, on every team, without exception.
Workflow Overview: Keeping Code and Confluence Documentation in Sync
The core problem this workflow solves: specs and code diverge the moment either one changes and nobody remembers to update the other. Confluence MCP doesn't eliminate that discipline gap, but it removes the excuse — updating a Confluence page from a code diff is now a five-minute prompt instead of a context-switch into a browser tab nobody wants to open.
The workflow has three stages, and they map to a genuine engineering lifecycle, not a hypothetical one:
- Spec to scaffold — a Confluence page (written by a human, product spec or technical design) becomes initial code structure, before a single line is hand-written.
- Code to docs — as the implementation evolves past the initial spec (and it always does), the agent regenerates or patches the Confluence page to reflect what was actually built.
- Ongoing sync automation — a scheduled or CI-triggered job flags drift between code and docs on a cadence, rather than relying on someone remembering to run the prompt manually.
This is deliberately agent-agnostic — the workflow works identically with Claude Code, OpenCode, Gemini CLI, or Cursor, since it's built entirely on the mcp-atlassian tool set covered in prior topics. The example below uses Claude Code CLI for the interactive stages (its planning reliability matters most for the multi-step stages) and a scripted claude -p invocation for the automation stage.
Tips
- Treat this as a loop, not a one-time project — the value compounds only if steps 2 and 3 actually run on a recurring basis, not just once after the initial spec-to-scaffold pass.
- Pick one agent as your "documentation agent of record" for a given service, even if your team uses multiple agents day to day — consistent tool-calling behavior matters more than which agent you pick.
- Start this workflow on a single, well-scoped service rather than rolling it out org-wide — the review overhead in the first few runs is real, and you want to tune prompts before scaling up.
Step 1: Reading a Confluence Spec Page and Scaffolding Code Structure
Assume a product spec already exists on Confluence: "Webhook Delivery Service — Technical Spec," in the ENG space, covering retry semantics, payload signing, and delivery status tracking. The goal is to turn that into a working project skeleton.
Read the Confluence page "Webhook Delivery Service — Technical Spec" in
ENG. Based on it, scaffold a new service at `services/webhook-delivery/`:
- `main.py` — FastAPI app with routes for webhook registration and
delivery status lookup, matching the endpoints table in the spec
- `models.py` — Pydantic models for WebhookSubscription and
DeliveryAttempt, matching the data model section
- `retry.py` — stub function `calculate_backoff(attempt: int) -> float`
implementing the exponential backoff formula described in the
"Retry Semantics" section, with a docstring citing the formula
- `signing.py` — stub function `sign_payload(payload: bytes, secret: str) -> str`
per the "Payload Signing" section (HMAC-SHA256, per the spec)
Leave business logic as `# TODO: implement per spec` where the spec is
ambiguous about the exact behavior, rather than guessing.
That last instruction matters more than it looks. Specs are rarely fully unambiguous — this one probably says something like "retry with exponential backoff up to 5 attempts" without nailing down the exact base delay or jitter strategy. An agent under instruction to "just implement it" will pick reasonable defaults and not tell you it guessed; an agent instructed to flag ambiguity with an explicit TODO gives you a reviewable list of decisions still needed, which is far more useful at this stage than confidently-wrong code.
After scaffolding, a quick verification prompt closes the loop before you start hand-writing logic:
Compare the scaffolded code against the spec page one more time.
List anything in the spec you weren't able to represent in code
(ambiguous requirements, sections you skipped, assumptions you made).
This second pass reliably surfaces things like "the spec mentions a dead-letter queue for failed deliveries after max retries, but doesn't specify the storage backend — I didn't scaffold this" — exactly the kind of gap you want surfaced explicitly rather than silently dropped.
Tips
- Explicitly instruct the agent to flag spec ambiguity with TODOs rather than silently picking a default — the difference between a reviewable gap list and hidden guesses is the whole value of this step.
- Run a dedicated "what didn't you represent" follow-up after scaffolding — it catches omissions the initial generation pass doesn't surface on its own.
- Keep the scaffold intentionally thin (stubs and structure, not full business logic) — the goal here is a correct skeleton to build on, not a finished implementation generated from a possibly-incomplete spec.
Step 2: Generating a Confluence Page from Source Code Annotations
Once the webhook service is actually implemented — real retry logic, real signing, tests passing — the documentation needs to catch up to what got built, which by now has diverged from the original spec in the small ways implementation always diverges from design (an added max_delay_seconds cap the spec never mentioned, a WEBHOOK_SECRET_ROTATION_DAYS config value that emerged from a security review mid-build).
Read the current implementation in `services/webhook-delivery/`
(main.py, models.py, retry.py, signing.py) and the existing test suite
in `tests/test_webhook_delivery.py`. Update the Confluence page
"Webhook Delivery Service — Technical Spec" in ENG to reflect the
actual implementation:
- Update the "Retry Semantics" section with the real backoff formula
and the max_delay_seconds cap (not in the original spec)
- Add a new "Configuration" section documenting WEBHOOK_SECRET_ROTATION_DAYS
and any other env vars read in main.py
- Keep the original "Overview" and "Data Model" sections unless the
actual models.py has diverged from them — check field-by-field
- Add a footer note: "Last synced with implementation: [today's date],
commit [current git SHA]"
That footer convention — a synced-as-of marker with a commit SHA — is worth adopting as a standing pattern. It turns "is this doc still accurate?" from a judgment call into a fact you can check: compare the SHA in the footer against the current HEAD, and if they've diverged by more than a few weeks or a few dozen commits, that's your signal to re-run the sync rather than trusting a page that might be stale.
Footer example the agent should generate:
---
_Last synced with implementation: 2026-08-21, commit a3f9c21_
The field-by-field data model check matters because Pydantic models drift from their original spec constantly — a field gets renamed during a refactor, a validation constraint gets tightened, an optional field becomes required. Asking the agent to check "field-by-field" rather than just "update if needed" produces a materially more thorough diff in practice; the vaguer instruction tends to get a surface-level pass that misses subtle type or constraint changes.
Tips
- Adopt a "last synced, commit SHA" footer convention on any Confluence page kept in sync with code — it converts staleness from a guess into a checkable fact.
- Ask for field-by-field comparison explicitly on data model sections — vague "update if needed" instructions produce shallower diffs than a directive to check every field.
- Preserve sections that haven't actually changed rather than regenerating the whole page from scratch each sync — regenerating everything discards any manual edits or comments a human added to unrelated sections.
Step 3: Setting Up MCP Automation to Keep Docs and Code in Sync
Manual sync prompts work, but they only run when someone remembers to run them. The last piece is a scheduled job that flags — not necessarily auto-fixes — drift between code and docs, so staleness gets caught within days instead of discovered six months later when someone hits an inaccurate runbook during an incident.
A cron-triggered script using Claude Code's non-interactive mode (claude -p) is the simplest version of this that a small team can actually maintain:
#!/usr/bin/env bash
set -euo pipefail
source .env # loads CONFLUENCE_URL, CONFLUENCE_USERNAME, CONFLUENCE_API_TOKEN
claude -p "Read the Confluence page 'Webhook Delivery Service — Technical
Spec' in ENG, note its 'Last synced with implementation' footer commit
SHA. Compare against the current git log for
services/webhook-delivery/ since that SHA. If there are more than 5
commits since the last sync, or any commit touches main.py, models.py,
retry.py, or signing.py, output a summary of what changed and flag
this as NEEDS_SYNC. Otherwise output IN_SYNC. Do not modify the
Confluence page." \
--output-format json > /tmp/docs-drift-check.json
STATUS=$(jq -r '.result' /tmp/docs-drift-check.json | grep -o 'NEEDS_SYNC\|IN_SYNC' | head -1)
if [ "$STATUS" = "NEEDS_SYNC" ]; then
echo "Docs drift detected for webhook-delivery — posting Slack alert"
# curl to your Slack webhook here, or open a tracking ticket
fi
Deliberately read-only: the automation flags drift and hands a human the decision to run the actual sync prompt from Step 2, rather than letting an unattended job rewrite documentation without review. That's a conscious trade-off — full auto-sync is technically possible (just chain the Step 2 prompt after a NEEDS_SYNC result), but for anything beyond a low-stakes internal wiki, an unattended write to shared documentation is the kind of thing that erodes trust in the whole system the first time it gets something wrong in a way nobody catches for a week.
Wire it into a CI schedule (GitHub Actions example) rather than a bare cron job if your team is already living in CI:
name: Confluence Docs Drift Check
on:
schedule:
- cron: "0 9 * * 1" # every Monday, 9am UTC
workflow_dispatch: {}
jobs:
check-drift:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- name: Run drift check
env:
CONFLUENCE_URL: ${{ secrets.CONFLUENCE_URL }}
CONFLUENCE_USERNAME: ${{ secrets.CONFLUENCE_USERNAME }}
CONFLUENCE_API_TOKEN: ${{ secrets.CONFLUENCE_API_TOKEN }}
ANTHROPIC_API_KEY: ${{ secrets.ANTHROPIC_API_KEY }}
run: bash scripts/check-docs-drift.sh
Use a dedicated Confluence service account (scoped via CONFLUENCE_SPACES_FILTER to just the spaces this automation covers) for the CI credentials — not a personal API token — for the same audit-trail and blast-radius reasons covered back in the first topic of this module. And keep the weekly cadence honest with the team: a drift-check that nobody acts on for months is worse than no automation at all, because it creates a false sense that documentation is being kept current when it's actually just being flagged and ignored.
Tips
- Keep the automated check strictly read-only (flag drift, don't auto-write) — reserve the actual sync-and-rewrite step for a human-triggered run, at least until you've built real trust in the prompt's output quality over several manual cycles.
- Use a dedicated, narrowly-scoped service account for any scheduled or CI-triggered Confluence automation, never a personal token.
- Set a real cadence (weekly is a reasonable default for an actively-developed service) and actually route the NEEDS_SYNC signal somewhere a human will see it — Slack, a tracking ticket, a dashboard — or the automation becomes silent and useless within a month.
Tips
Tips
- The spec-to-scaffold and code-to-docs directions are both genuinely reliable across the agents covered in this module; the automation layer is the part most teams under-invest in, and it's the part that actually prevents drift from recurring.
- Start this whole workflow on one service, tune the prompts against your team's actual spec-writing and docstring conventions, then template it out — a one-size-fits-all prompt rarely survives contact with a second team's different documentation habits.
- This closes the module: Confluence MCP's real value isn't any single generated page — it's collapsing the cost of keeping specs and implementation honest with each other from "a task nobody has time for" to "a five-minute prompt run on a schedule."