Docs Sync Quick Reference
The cheat sheet version of this page: commands, paths, and review steps at a glance.
Open the cheat sheet →How documentation syncs from the Nexus code repository into review merge requests on this docs site
This documentation site receives automated sync merge requests from the internal Nexus code repository whenever documentation changes reach its canonical main branch. External contributions become eligible only after the team reviews and merges them into the internal repository. Nothing is published automatically: every sync arrives as a merge request requiring sign-off from both the Nexus author who made the change and a docs maintainer.
Goal: Keep the docs site in sync with documentation in the Nexus codebase, with human approval on every change and without ever disturbing the curated sidebar order.
How it works:
nexus mainsync/* branchmain or push a protected v* tagThe Nexus pipeline must pass before its dispatch job runs.main.main.versions/manifest.json.main runs validation, build, and deploymentThe sync jobs never publish directly.The receiver applies these controls before publication:
| Control | Enforcement point | Result when the check fails |
|---|---|---|
| Canonical sender | Nexus docs-sync-dispatch rules |
Only the canonical internal Nexus project can dispatch; the public mirror is skipped |
| Pipeline source | .gitlab-ci.yml workflow and job rules |
Sync jobs run only for downstream pipelines created with GitLab’s native trigger: keyword |
| Trigger routing | .gitlab-ci.yml job rules |
Only merge and release values select a sync job |
| Fixed repository | clone_nexus() |
Trigger variables cannot change the internal repository, host, or default branch |
| Clone scope | clone_nexus() |
Clones only main (--single-branch) with full history, not other branches — full history is required so ancestry checks and catch-up diffs can walk arbitrarily far back |
| Commit verification | clone_nexus() |
The supplied full commit SHA must exist and belong to canonical Nexus main |
| Catch-up state | state.json and merge.sh |
Each docs-bearing sync starts at the last reviewed source commit; rewritten history fails closed |
| Documentation scope | is_docs_path() |
Code-only merges exit successfully without creating a branch or merge request |
| Release checks | release.sh |
The semantic-version tag must resolve to the verified commit; broken relative links or assets fail snapshot creation |
| Human review | GitLab merge request protections | Sync automation cannot publish directly; publication starts only after the review merge request reaches main |
When a developer merges to Nexus main:
docs-sync-dispatch triggers this repodocs-sync-merge only (build/deploy are skipped in triggered pipelines). The job pushes (or refreshes) the sync/nexus-main branch and opens a review MRmain, which runs the normal build → deploy pipeline and publishes the siteThe heart of the sync is scripts/docs-sync/path-map.tsv, a tab-separated mapping of Nexus repo paths to curated site pages:
docs/architecture/data-model.md concepts/data-models.mddocs/runbooks/audit-out-of-band.md security/audit-archive-export.mddocs/dev/ci.md contributing/ci-cd-pipeline.mdCHANGELOG.md reference/changelog.mdThis map determines ownership:
| File type | Owner | Sync behavior |
|---|---|---|
Mapped pages (in path-map.tsv) |
Nexus | Body replaced on every Nexus edit; curated frontmatter (title/description) preserved; no sidebar change |
| Docs-side pages (not in the map, e.g. this page) | This repo | Never touched by the sync — no Nexus change can modify or delete them |
Holding-area pages (src/content/docs/nexus/) |
Nexus | Parked copies awaiting a placement decision; deleted if deleted in Nexus |
The sidebar is manually curated in astro.config.mjs. The sync never reorders existing entries:
When docs/architecture/data-model.md changes in Nexus:
concepts/data-models.md# Heading is stripped)When a brand-new doc appears in Nexus, the sync applies a placement suggestion directly in the MR diff:
Section is inferred from the Nexus path:
| Nexus path | Suggested destination | Sidebar group |
|---|---|---|
docs/decisions/* |
concepts/ |
Architecture Decisions (ADRs) |
docs/architecture/* |
concepts/ |
Key Concepts |
docs/reference/* |
reference/ |
Reference |
docs/security/* |
security/ |
Security |
docs/runbooks/*, docs/ops/* |
operations/ |
Operations |
docs/dev/* |
contributing/ |
Contributing |
docs/perf/* |
contributing/ |
Testing & Performance Baselines |
docs/process/*, docs/community/*, docs/legal/* |
contributing/ |
Governance & Process |
docs/guides/*, docs/getting-started/* |
getting-started/ |
Getting Started |
The page is created there with proper frontmatter (title from the first heading; ADR numeric prefixes like 0022- are dropped from the filename to match site conventions)
A sidebar entry is appended to the end of that section/group in astro.config.mjs
The new mapping is recorded in path-map.tsv so future edits update the page in place
All of this is in the MR diff. As a reviewer you can:
path-map.tsv line to match; future syncs follow your decisionpath-map.tsv lineFiles the sync can’t place confidently (e.g. services/gateway/README.md, or a filename that would collide with an existing page) are parked under src/content/docs/nexus/<original path> with no sidebar entry. The MR flags them under “New pages needing a placement decision” — reachable by direct URL but invisible in navigation until you place them.
When a mapped file is deleted in Nexus, the sync deletes the site page and removes its sidebar entry. Deletions of unmapped Nexus files with no site counterpart are skipped with a log line. Docs-side pages can never be deleted by the sync.
Git rename detection turns a rename into delete-old + add-new. The old page and sidebar entry are removed; the new path goes through new-file placement. No automatic redirect is created — add one manually if the old URL matters.
When a protected v* tag is pushed to Nexus:
versions/<tag>/versions/manifest.json is updatedrelease/docs-<tag>Latest/current docs are unaffected; they’re owned by the merge path.
All documentation pages include metadata in their frontmatter to track ownership and review status:
---title: Page Titledescription: Page descriptionowner: ProductlastReviewed: 2026-08-05---| Field | Purpose | Values |
|---|---|---|
owner |
Team responsible for maintaining this documentation | Product, Engineering, Design |
lastReviewed |
Date this page was last reviewed for accuracy | ISO date format (YYYY-MM-DD) |
When syncing from Nexus:
owner: Engineering and lastReviewed set to today’s date (UTC)lastReviewed date automatically updated to today’s date (UTC) when content changesowner field, it’s automatically set to EngineeringFor docs-side pages (not in path-map.tsv):
Product (or whatever the page specifies)lastReviewed manually whenever you make substantial content changesDisplay:
Credentials and review policy live in this repository. The Nexus repository requires one code change (below) so its docs-sync-dispatch job targets this project directly; nothing else needs to change in Nexus project settings.
Nexus’s .gitlab-ci.yml docs-sync-dispatch job must:
trigger: project: vortexaq/vortexai/nexus-docs (not read a DOCS_SYNC_PROJECT_PATH variable — this project no longer accepts a caller-supplied path anyway)if: $CI_SERVER_HOST != "lab.10xnatsec.com" || $CI_PROJECT_PATH != "vortexaq/vortexai/nexus" → when: never, so the public mirror can never dispatch a syncNEXUS_COMMIT_SHA, RELEASE_TAG, and NEXUS_ACTOR ($GITLAB_USER_LOGIN) — nothing else is read by this receiverThis change already ships on the Nexus side as of its ci(docs-sync): target nexus-docs directly and ci(docs-sync): restore NEXUS_ACTOR for reviewer assignment commits.
Set in Settings → CI/CD → Variables (masked; protect if your sync branches are protected):
| Variable | Scope needed | Description |
|---|---|---|
NEXUS_GIT_HOST |
n/a | Git host of the canonical Nexus project. Kept out of tracked source and out of MR/doc output since this repository is public; set once, mark Protected, and restrict “Minimum role to use pipeline variables” to Maintainer+ so a manually-triggered pipeline can’t override it |
DOCS_SYNC_BOT_TOKEN |
api |
Project access token from this project (Settings → Access tokens, role Developer+). Pushes sync branches, opens MRs, resolves reviewer usernames |
NEXUS_REPO_READ_TOKEN |
read_repository |
Token that can clone the internal Nexus repo (vortexaq/vortexai/nexus). A personal access token from any account with read access to Nexus works — no Nexus project settings required |
| Variable | Purpose |
|---|---|
DOCS_MAINTAINER |
Comma-separated GitLab usernames always requested as MR reviewers, alongside the Nexus author who made the change; enforce the two-approval requirement itself in project settings |
trigger: keyword runs with the triggering user’s permissions. Manage this in this repo’s Settings → Members.DOCS_SYNC_BOT_TOKEN bot user only needs to create branches and MRs (no push-to-main rights required — everything goes through MRs)main, disallow direct bot pushes, and require a successful pipeline before merge. Configure the approval rule to require sign-off from both the Nexus author (NEXUS_ACTOR, requested automatically) and a docs maintainer — requesting reviewers alone does not enforce that; the project’s approval rule does.docs-sync-merge job log:
docs-sync trigger received: TRIGGER_SOURCE=merge NEXUS_COMMIT_SHA=abc1234...cloning canonical vortexaq/vortexai/nexus (main only) at abc1234diffing verified range def5678..abc1234documentation changes:M docs/decisions/backlog.mdA docs/decisions/0022-registry-state-from-signed-artifacts.mdpushed sync/nexus-maindocs sync merge request opened: <url>sync/nexus-main merge request; nothing publishes until it’s mergedCI/CD → Run pipeline → branch main → add variables:
TRIGGER_SOURCE = mergeNEXUS_COMMIT_SHA = <full SHA from internal Nexus main>(Manually run pipelines have $CI_PIPELINE_SOURCE == "web", so also temporarily widen the job rule to allow it.)
| Symptom | Check |
|---|---|
| Trigger never arrives | Does the Nexus merger have Developer+ in this project? Is the hardened Nexus docs-sync-dispatch job merged and green? |
| Clone of Nexus fails | Is NEXUS_REPO_READ_TOKEN set and unexpired? Does its owner still have Nexus read access? |
| Commit verification fails | Confirm the full SHA exists on the internal Nexus main; public-mirror and feature-branch commits are intentionally rejected |
| State ancestry fails | Internal Nexus history was rewritten or state.json was edited incorrectly; reconcile manually rather than bypassing the check |
| MR creation fails (401/403) | Does DOCS_SYNC_BOT_TOKEN have api scope? Is it expired? |
| Reviewer not assigned | Username must exist and be a member here; fallback actor strings like Jane Doe <jane@…> are skipped by design (check job log) |
| Sync MR has odd placement | Adjust in the MR: move file + sidebar entry + fix the path-map.tsv line |
| Same MR keeps updating | Expected: retried triggers force-push the same sync/nexus-main branch instead of duplicating MRs |
What counts as documentation lives in scripts/docs-sync/lib.sh:
DOCS_INCLUDE_PATTERNS='docs/**.md'
DOCS_EXCLUDE_PATTERNS='__snapshots__/**/__snapshots__/**.test.*node_modules/**/node_modules/*.gitlab/*'Section inference lives in suggest_section() and sidebar_group_for_nexus_path() in scripts/docs-sync/lib.sh. Edit these as the Nexus docs layout or the site’s sidebar structure evolves.
Delete its line from scripts/docs-sync/path-map.tsv. Future Nexus edits to that source file will be treated as a new file (and likely park in the holding area) instead of overwriting your page.
scripts/docs-sync/state.json records the latest internal Nexus commit represented by a merged sync MR. A code-only Nexus merge creates no MR; the next documentation-bearing merge catches up from the recorded commit. The first run without state performs a full reconciliation.
Comment out the docs-sync-merge / docs-sync-release rules in .gitlab-ci.yml.
main. Trigger variables cannot redirect the read token to another repository.main.main without approval from both the Nexus author who made the change (NEXUS_ACTOR, forwarded by the sender and requested as a reviewer) and a docs maintainer (DOCS_MAINTAINER). This is enforced by the nexus-docs approval and protected-branch rules configured by a maintainer — requesting reviewers is routing, not enforcement.mainDocs Sync Quick Reference
The cheat sheet version of this page: commands, paths, and review steps at a glance.
Open the cheat sheet →CI/CD Pipeline
The general CI/CD overview for this repo: stages, gates, and release flow.
See the pipeline →