Skip to content

Agent Workflows6 min read

Spec Sync

Keep documentation accurate as the code evolves

Spec Sync (🔄 in the mode dropdown) keeps an existing wiki honest as the code evolves. It reads the git history since the last sync, classifies every commit as doc-relevant or doc-neutral, maps the relevant ones to specific wiki pages, and presents a structured plan you approve before a single character changes. Edits are surgical — never rewrites, never paraphrases, never a renamed anchor — because a wiki updated by paraphrase drifts, and drift that looks authoritative is worse than no docs at all.

Spec Sync pairs with Spec Bootstrap: bootstrap once, sync forever. It has the same narrow edit access (docs/, sources/, spec/, scripts/ plus .nexgilerules*), and in practice it goes further — sync edits stay inside docs/; touching anything else is a scope violation by its own rules.

Every sync starts from a checkpoint. During pre-flight, the agent reads the tail of docs/log.md and extracts the most recent line of the form:

Last-synced commit: a1b2c3d4…

That SHA — written by the original [bootstrap] entry or by the previous [sync] entry — is the baseline. If no such line exists (or docs/log.md is missing), it falls back to the first commit that touched docs/wiki/ and notes that in the plan. Pre-flight also samples pages to confirm the wiki follows the expected anatomy, runs the audit battery on the current wiki so pre-existing failures are reported (but not blamed on this sync), and detects manual wiki edits made since the baseline so they are surfaced rather than overwritten.

The agent enumerates git log --no-merges <baseline>..HEAD (you can narrow the scope: an explicit range, --paths, --since, or a single commit) and records, per commit, the author, date, subject, and every file added, modified, deleted, or renamed — renames get special care via git log --follow, because a renamed file silently breaks every path:line reference pointing at it. Related commits are clustered: a ten-commit feature branch is one logical change, presented as one net diff, with every SHA listed for traceability. Reverts are matched to the commits they undo, so a synced change that was later reverted gets its wiki edit reversed too.

Rules apply in order; first match wins.

Doc-relevant (examples):

Change Wiki layer affected
migrations/**, schema or ORM model files data — table pages
**/routes/**, **/handlers/**, **/controllers/** api — endpoint pages
Auth middleware, security policies auth
Frontend pages/views ui — route pages
Dependency added or removed in package.json etc. ops + a new ADR
.env* or env-loading code ops/env-vars
Dockerfiles, CI workflows, deploy config ops

Doc-neutral: pure renames of internal helpers, formatter passes, comment-only edits, test additions for already-documented behavior, root README.md edits, .gitignore and editor configs.

Ambiguous → doc-relevant. When unsure, the agent surfaces it in the plan rather than silently skipping; you can mark it skip.

Nothing is edited before you approve the plan. It lays out, per cluster: the commits and files involved, the exact wiki edits (which page, which section anchor, what changes), the projected page-size impact against the 300-line cap, and the deeplinks affected. It also lists new pages to create, pages to archive (with every inbound link found), ADRs to add, pre-existing wiki issues that are not this sync’s job, manual edits detected, and open questions — for example, a commit whose message says “no-op refactor” while the diff shows a behavior change.

### Cluster 1 — "last-login tracking"
**Commits:** a1b2c3d, e4f5g6h by Priya on 2026-07-28
**Wiki impact:**
1. EDIT wiki/data/users.md §columns — add row: last_login | TIMESTAMPTZ | yes
Status: [spec] → [code] · Sources: append commit a1b2c3d
2. EDIT wiki/api/auth/login.md §side-effects — "updates users.last_login to NOW()"
**Page-size impact:** users.md 142 → 156 ✓ within 300

You respond with one of: apply all · apply 1, 3 (or apply cluster 1 only) · skip 2 · defer X (kept for the next sync) · replan with <correction>. The agent never assumes apply all from silence.

For each approved cluster — applied in dependency order (data → auth → events → api → services → ui → integrations → ops) — the agent follows a strict protocol:

  • Read the full target page first, locate the exact lines using the page’s anchors, and edit only those lines. Everything not in the diff is preserved verbatim.
  • Anchors are immutable. Every <a id="..."> slug is an external API — other pages link to it. New content gets new anchors; renaming an existing one is grounds to stop and ask.
  • Provenance is appended, never replaced: the commit’s short SHA is added to the page’s Sources line, and the Status flag transitions per a fixed table — [spec][code] when the first implementing commit lands, [code][drift] when code contradicts the wiki without a same-change doc update (resolving drift requires an ADR), removed features go to [archived] with the page moved to sources/archive/.
  • [planned] tags are dropped once the file exists (verified with test -f), shifted path:line references are re-read from the file at HEAD — never guessed — and the page-size budget line is recomputed.
  • Split before overflow: if an edit would push a page past 300 lines, the page is split first, inbound references updated, and only then edited.
  • Never touched: files outside docs/, existing ADRs (superseded by new ones instead), existing log.md lines, and any edit you didn’t approve.

Replacing a schema table row with “see code for details”, or summarizing a column table into prose, is classified as spec degradation and rejected outright.

After applying edits, the sync must pass all six audits with empty failure output before it may declare done (via node scripts/spec-audit.mjs when present, or the manual equivalents):

  1. Deeplink — every page/anchor link resolves.
  2. Page-size — no page over its cap.
  3. Orphan — every page is linked from somewhere.
  4. Forbidden-pattern — no vague verbs or untagged TODOs in the files this sync touched.
  5. Provenance — touched pages still carry their Sources header.
  6. Code-reference line-range — every path:line-line reference points at an existing file, and the range fits inside it.

A failing audit is fixed and the full set re-run — the agent is explicitly forbidden from editing the checks to make them pass.

The final act is appending one block to docs/log.md (never editing prior lines):

2026-07-30 [sync] Synced 7 commits / 2 clusters from a1b2c3d..9e8f7a6.
Authors: Priya, Marco.
Pages updated: 3 — data/users.md, api/auth/login.md, ops/env-vars.md
Pages added: 1 — api/billing/list-invoices.md
Status transitions: 2 spec→code
Audits: 6/6 passed.
Last-synced commit: 9e8f7a6…

That closing Last-synced commit: line is what the next sync reads as its baseline — the chain of checkpoints is the whole mechanism.

Run Spec Sync after code lands: at each merged feature branch, or on a weekly rhythm for busy repos. Small windows keep plans short and reviewable; a 50-commit backlog produces a plan you’ll skim instead of read (the workflow refuses oversized cluster batches for exactly this reason). Two cheap cases are handled cleanly: if every commit since baseline is doc-neutral, the plan says “no doc-relevant changes” and still logs the new HEAD so the next sync starts fresh; if nothing was committed at all, the sync exits with “no changes”.

Bootstrap creates the contract; Sync keeps it true. Together they give you documentation that survives refactors: pages whose Status flags tell you what is spec, what is implemented, and what has drifted — commit by commit, with an audit trail in docs/log.md. If docs/wiki/ doesn’t exist yet, Spec Sync will tell you to switch to 📚 Spec Bootstrap and stop.