zuvo:release-docs
Sync documentation with a release. Only updates docs whose source files actually changed.
Mandatory File Loading
Read these files before proceeding:
CORE FILES LOADED:
1. ../../shared/includes/env-compat.md — READ
2. ../../shared/includes/run-logger.md — READ
3. ../../shared/includes/retrospective.md — READ
Argument Parsing
| Input | Effect |
|---|---|
| (no flags) | Auto-detect range from memory/last-ship.json or git tags |
<range> |
Explicit git range (e.g., v1.1.0..v1.2.0) |
--dry-run |
Show proposed changes without writing |
Dispatch follows ../../shared/includes/execution-policy.md through env-compat. Reuse existing
authorization within that policy; session restrictions take precedence. Run each required gate
and report its actual independence or an unmet requirement.
Phase 0: Determine Range and Suffix
If an explicit
<range>argument was provided: use it. Skip remaining steps in this phase.Else if
memory/last-ship.jsonexists: read therangefield (SHA-based, e.g.,"abc1234..def5678") and use it directly forgit diff. Also readpreviousTagandnewTagfor display in the output block. If the artifact uses a legacy version-based range (e.g.,"v1.1.0..v1.2.0"), fall back to it but log: "Warning: legacy version-based range — consider re-running zuvo:ship for SHA-based artifact."Else: derive from git tags.
- Run
git describe --tags --abbrev=0to get the latest tag. - Run
git describe --tags --abbrev=0 <latest-tag>^to get the previous tag. - Construct range as
<previous-tag>..<latest-tag>.
- Run
If no range can be derived after the above steps:
- Interactive environments: ask the user to provide a range explicitly.
- Non-interactive environments (Codex App, Cursor): print
[AUTO-DECISION]: no range derivable. Skipping documentation sync.and exit with PASS verdict.
Compute
RANGE_SUFFIXfor use in evidence and output paths:- If
previousTagandnewTagare available:RANGE_SUFFIX = "<previousTag>_<newTag>"(e.g.,v1.1.0_v1.2.0) - Else if range contains tags: extract them (e.g.,
v1.1.0..v1.2.0→v1.1.0_v1.2.0) - Else: use short SHAs from the range (e.g.,
abc1234_def5678)
All downstream references to
<range-suffix>use this computed value.- If
Phase 1: Diff Analysis
- Run
git diff --name-only <range>to get all files changed in the range. - Classify each changed file:
- Source files:
.ts,.tsx,.js,.jsx,.py,.php,.go,.rs,.java,.rb,.swift,.kt - Doc files:
.md,.mdx,.rst,.txt - Config files:
.json,.yaml,.toml,.yml - Other: images, binaries, lock files, etc.
- Source files:
- Determine "docs-adjacent" source files using this priority order:
- Priority 1: Explicit mapping. If
docs/docs-map.yamlexists, use it:
Source files matching a key are docs-adjacent to the mapped doc files.# docs-map.yaml — maps source paths to documentation files src/auth/: docs/authentication.md src/orders/: [docs/orders.md, docs/api/orders-api.md] - Priority 2: Frontmatter. If doc files have
sources:in their YAML frontmatter (e.g.,sources: [src/auth/*]), use those globs to match changed source files. - Priority 3: Name heuristic (fallback).
src/auth/is docs-adjacent ifdocs/auth.mdor any*auth*file indocs/exists, or if any.mdfile mentions the module name. - If in doubt, include the file — false positives cause minor extra work; false negatives miss documentation updates.
- Priority 1: Explicit mapping. If
- Build the
DOCS_SKIPPEDlist: For all doc files known through Priority 1 or Priority 2 mappings whose corresponding source files did NOT change in this range, add them toDOCS_SKIPPED. This list is used in the Phase 5 output. If no docs-map or frontmatter mappings exist, setDOCS_SKIPPEDto"—". - If no docs-adjacent source files changed: print "No documentation updates required for this release." Proceed to Phase 2 (changelog verification) and then to Phase 5 output with PASS verdict.
- If
--dry-runis set: print the list of source files that would trigger doc updates and the doc files that would be updated, then exit without writing anything.
Phase 2: Verify Changelog
zuvo:ship is the sole owner of CHANGELOG.md. This skill does NOT generate or modify the changelog — ship already did that during the release commit.
- Verify that
CHANGELOG.mdcontains an entry for the current release version. If not, log a warning: "Changelog entry missing for this release — waszuvo:shiprun with--no-bump?" - Record the changelog state (present/missing, entry count by type if present) for the output block.
Phase 3: Doc Updates
For each documentation file whose corresponding source files changed:
Invoke:
Skill(skill="zuvo:docs", args="update <doc-file>")zuvo:docs updatehandles staleness detection and targeted section updates.Iron rule: Every documentation claim added or modified must reference a source file (file path, function name, or line reference). If
zuvo:docsproduces a claim without traceable evidence, flag it and request a correction before accepting the update.Write the evidence trail to:
zuvo/reports/release-docs-sources-<range-suffix>.mdUse one line per claim in this format:
<doc-file>→<claim summary>→<source-file:line>or<source-file:function>
If multiple doc files need updating, invoke zuvo:docs update for each in sequence.
Phase 4: Debt Detection
- Reuse the same mapping priority order from Phase 1:
docs/docs-map.yamlsources:YAML frontmatter- name heuristic (fallback only)
- A changed source file is documented if it resolves to at least one documentation file through Priority 1 or Priority 2.
- If only the fallback heuristic matches, mark the result as low-confidence and report it separately.
- A source file is undocumented only when no explicit mapping or frontmatter source rule matches it.
- Documentation debt is informational unless explicitly flagged by the user.
Phase 5: Output
Print the RELEASE-DOCS COMPLETE block:
RELEASE-DOCS COMPLETE
Range: <range>
Changelog: present (Added: N, Fixed: N, Changed: N) / missing — run zuvo:ship
Docs updated: <list of doc files updated, or "none">
Docs skipped: <list of known doc files (from docs-map/frontmatter) with no source changes, or "—" if no mapping exists>
Debt found: N file(s) (<list of undocumented files>) / none
Evidence: zuvo/reports/release-docs-sources-<range-suffix>.md
Verdict: PASS
If --dry-run was set and execution reached this phase (only on early exits), annotate the block with [DRY RUN — no files written].
After the output block, print and append the run log line:
Run: <ISO-8601-Z>\trelease-docs\t<project>\t-\t-\t<VERDICT>\t-\t5-phase\t<NOTES>\t<BRANCH>\t<SHA7>\t<INCLUDES>\t<TIER>
Retrospective (REQUIRED)
Follow the retrospective protocol from retrospective.md.
Gate check → structured questions → TSV emit → markdown append.
If gate check skips: print "RETRO: skipped (trivial session)" and proceed.
Append via wrapper (REQUIRED). Never >> directly to ~/.zuvo/runs.log — the wrapper is the gate that verifies a retro entry exists for this run. Order: retro bash executed → wrapper invoked → completion claimed.
printf '%b\n' "$RUN_LINE" | ~/.zuvo/append-runlog
Expected stdout: OK: appended to runs.log (retro verified for <skill> on <project>). If exit 2 with RETRO_REQUIRED — go execute the retro bash from retrospective.md first; never bypass with ZUVO_SKIP_RETRO_GATE=1. After the wrapper succeeds, print a Logs: evidence line (tail -1 ~/.zuvo/retros.log, grep -c "^<!-- RETRO -->" ~/.zuvo/retros.md, tail -1 ~/.zuvo/runs.log) before claiming completion. Printing the markdown retro section without executing the bash leaves all three log files empty.