--- name: monorepo-document description: Syncs unified documentation (`_docs/*.md` and equivalent) in a monorepo after one or more components changed. Reads `_docs/_repo-config.yaml` (produced by monorepo-discover) to know which doc files each component feeds into and which cross-cutting docs own which concerns. Touches ONLY documentation files — never CI, compose, env templates, or component directories. Use when a submodule/package added/changed an API, schema, permission, event, or dependency and the unified docs need to catch up. --- # Monorepo Document Propagates component changes into the unified documentation set. Strictly scoped to `*.md` files under `docs.root` (and `repo.root_readme` if referenced as cross-cutting). ## Scope — explicit | In scope | Out of scope | | -------- | ------------ | | `_docs/*.md` (primary and cross-cutting) | `.env.example`, `docker-compose.*.yml` → use `monorepo-cicd` | | Root `README.md` **only** if `_repo-config.yaml` lists it as a doc target (e.g., services table) | Install scripts (`ci-*.sh`) → use `monorepo-cicd` | | Docs index (`_docs/README.md` or similar) cross-reference tables | Component-internal docs (`/README.md`, `/docs/*`) | | Cross-cutting docs listed in `docs.cross_cutting` | `_docs/_repo-config.yaml` itself (only `monorepo-discover` and `monorepo-onboard` write it) | If a component change requires CI/env updates too, tell the user to also run `monorepo-cicd`. This skill does NOT cross domains. ## Preconditions (hard gates) 1. `_docs/_repo-config.yaml` exists. 2. Top-level `confirmed_by_user: true` in the config. 3. `docs.root` is set (non-null) in the config. 4. Components-in-scope have `confirmed: true` mappings, OR the user explicitly approves an inferred mapping for this run. If any gate fails: - Config missing → redirect: "Run `monorepo-discover` first." - `confirmed_by_user: false` → "Please review the config and set `confirmed_by_user: true`." - `docs.root: null` → "Config has no docs root. Run `monorepo-discover` to re-detect, or edit the config." - Component inferred but not confirmed → ask user: "Mapping `` → `` is inferred. Use it this run? (y/n/edit config first)" ## Mitigations (same M1–M7 spirit) - **M1** Separation: this skill only syncs docs; never touches CI or config. - **M3** Factual vs. interpretive: don't guess mappings. Use config. If config has an `unresolved:` entry for a component in scope, SKIP it (M5) and report. - **M4** Batch questions at checkpoints: end of scope determination, end of drift check. - **M5** Skip over guess: missing/ambiguous mapping → skip and report, never pick a default. - **M6** Assumptions footer every run; append to config's `assumptions_log:`. - **M7** Drift detection before action: re-scan `docs.root` to verify config-listed docs still exist; if not, stop and ask. ## Workflow ### Phase 1: Drift check (M7) Before editing anything: 1. For each component in scope, verify its `primary_doc` and each `secondary_docs` file exists on disk. 2. For each entry in `docs.cross_cutting`, verify the file exists. 3. If any expected file is missing → **stop**, ask user whether to: - Run `monorepo-discover` to refresh the config, OR - Skip the missing file for this run (recorded as skipped in report) Do NOT silently create missing docs. That's onboarding territory. ### Phase 2: Determine scope If the user hasn't specified which components changed, ask: > Which components changed? (a) list them, (b) auto-detect from recent commits, (c) skip to review changes you've already made. For **auto-detect**, for each component in config: ```bash git -C log --oneline -20 # submodule # or git log --oneline -20 -- # monorepo subfolder ``` Flag components whose recent commits touch doc-relevant concerns: - API/route files (controllers, handlers, OpenAPI specs, route definitions) - Schema/migration files - Auth/permission files (attributes, middleware, policies) - Streaming/SSE/websocket event definitions - Public exports (`index.ts`, `mod.rs`, `__init__.py`) - Component's own README if it documents API - Environment variable additions (only impact docs if a Configuration section exists) Present the flagged list; ask for confirmation before proceeding. ### Phase 3: Classify changes per component For each in-scope component, read recent diffs and classify changes: | Change type | Target doc concern | | ----------- | ------------------ | | New/changed REST endpoint | Primary doc API section; cross-cutting arch doc if pattern changes | | Schema/migration | Cross-cutting schema doc; primary doc if entity documented there | | New permission/role | Cross-cutting roles/permissions doc; index permission-matrix table | | New streaming/SSE event | Primary doc events section; cross-cutting arch doc | | New inter-component dependency | Cross-cutting arch doc; primary doc dependencies section | | New env variable (affects docs) | Primary doc Configuration section only — `.env.example` is out of scope | Match concerns to docs via `docs.cross_cutting[].owns`. If a concern has no owner, add to an in-memory unresolved list and skip it (M5) — tell the user at the end. ### Phase 4: Apply edits For each mapping (component change → target doc): 1. Read the target doc. 2. Locate the relevant section (heading match, anchor, or `primary_doc_section` from config). 3. Edit only that section. Preserve: - Heading structure and anchors (inbound links depend on them) - Table column widths / alignment style - ASCII diagrams (characters, indentation, widths) - Cross-reference wording style 4. Update cross-references when needed: if a renamed doc is linked elsewhere, fix links too. ### Phase 5: Skip-and-report (M5) Skip a component, don't guess, if: - No mapping in config (the component itself isn't listed) - Mapping tagged `confirmed: false` and user didn't approve it in Phase 2 - Component internally inconsistent (README asserts endpoints not in code) — surface contradiction Each skip gets a line in the report with the reason. ### Phase 6: Lint / format Run markdown linter or formatter if the project has one (check for `.markdownlintrc`, `prettier`, or similar at repo root). Skip if none. ### Phase 7: Report + assumptions footer (M6) Output: ``` monorepo-document run complete. Docs updated (N): - _docs/01_flights.md — added endpoint POST /flights/gps-denied-start - _docs/00_roles_permissions.md — added permission `FLIGHTS.GPS_DENIED.OPERATE` - _docs/README.md — permission-matrix row updated Skipped (K): - satellite-provider: no confirmed mapping (config has unresolved entry) - detections-semantic: internal README references endpoints not in code — needs reconciliation Assumptions used this run: - component `flights` → `_docs/02_flights.md` (user-confirmed in config) - roles doc = `_docs/00_roles_permissions.md` (user-confirmed cross-cutting) - target branch: `dev` (from conventions.work_branch) Next step: review the diff in your editor, then commit with ` Sync docs after ` (or your own message). ``` Append to `_docs/_repo-config.yaml` under `assumptions_log:`: ```yaml - date: 2026-04-17 skill: monorepo-document run_notes: "Synced " assumptions: - "" ``` ## What this skill will NEVER do - Modify files inside component directories - Edit CI files, compose files, install scripts, or env templates - Create new doc files (that's `monorepo-onboard`) - Change `confirmed_by_user` or any `confirmed: ` flag - Auto-commit or push - Guess a mapping not in the config ## Edge cases - **Component has no primary doc** (UI component that spans all feature docs): if config has `primary_doc: null` or similar marker, iterate through `docs.cross_cutting` where the component is referenced. Don't invent a doc. - **Multiple components touch the same cross-cutting doc in one run**: apply sequentially; after each edit re-read to get updated line numbers. - **Cosmetic-only changes** (whitespace renames, internal refactors without API changes): inform user, ask whether to sync or skip. - **Large gap** (doc untouched for months, component has dozens of commits): ask user which commits matter — don't reconstruct full history.