| name | generate-user-docs |
| description | Synchronizes user-facing documentation with the current knowledge base through validate -> stale gate -> scan -> approval -> process orchestration. |
| allowed-tools | Bash(echo *), Bash(rp1 *), Bash(git *), Bash(mkdir *), Read, Write, Glob |
| metadata | {"category":"documentation","is_workflow":true,"workflow":{"run_policy":"fresh","identity_args":[]},"version":"2.1.0","tags":["documentation","sync","workflow","parallel"],"created":"2025-12-28T00:00:00.000Z","updated":"2026-04-04T00:00:00.000Z","author":"cloud-on-prem/rp1","sub_agents":["rp1-base:scribe"]} |
Generate User Docs
§ROLE: User-doc sync orchestrator. Reconcile user-facing docs against {kbRoot}/ in one scan pass and one process pass.
§OBJ
- Sync docs to current KB facts
- Keep orchestration state under
{workRoot}/
- Delegate file-level scan/process only
- Ask approval exactly once after scan
- If the KB is stale but structurally valid, require one explicit pre-scan decision gate
§DO
- Execute discovery + scan immediately
- Use
rp1-base:scribe only for file batches
- Spawn scan/process batches in parallel with background dispatch
- When the KB is stale, warn with a three-way gate: continue, rebuild first, or cancel
- Keep user-visible output terse:
- KB validation status
- Scan summary + approval gate
- Final report
- Preserve
scan_results.json; do NOT delete it automatically
§DONT
- Do NOT ask approval before scan
- Do NOT edit docs directly in the parent
- Do NOT iterate on scan or process more than once
- Do NOT silently continue with a stale KB
- Do NOT treat diffs inside the docs being reconciled as KB-staleness signals for this run
§PATHS
- Use the
RUN_ID from the generated Workflow Bootstrap section
- Derive
RUN_NAME as "Docs: sync user docs" (max 60 chars)
- Set
DATESTAMP to the current date in YYYY-MM-DD
- Set
SESSION_DIR=docs-sync/{DATESTAMP}-{RUN_ID}
- Set
SCAN_RESULTS_REL={SESSION_DIR}/scan_results.json
- Set
SCAN_RESULTS_PATH={workRoot}/{SCAN_RESULTS_REL}
STATE-MACHINE
stateDiagram-v2
[*] --> discover
discover --> validate_kb : docs_ready
discover --> failed : no_docs
validate_kb --> scan : kb_current
validate_kb --> stale_kb_gate : kb_stale
validate_kb --> failed : kb_invalid
stale_kb_gate --> scan : continue_with_stale
stale_kb_gate --> cancelled : rebuild_requested
stale_kb_gate --> cancelled : rejected
scan --> approve : results_written
scan --> failed : scan_failed
approve --> process : approved
approve --> cancelled : rejected
process --> finalize : processing_done
process --> failed : process_failed
finalize --> [*] : done
failed --> [*] : stopped
cancelled --> [*] : stopped
First emit:
rp1 agent-tools emit \
--workflow generate-user-docs \
--type status_change \
--run-id {RUN_ID} \
--name "{RUN_NAME}" \
--step discover \
--data '{"status": "running"}'
State progression:
- Enter
discover, validate_kb, stale_kb_gate, scan, approve, process, and finalize with {"status": "running"}
- Enter
failed with {"status": "failed"}
- Use
emit end-run --run-id {RUN_ID} --outcome cancelled --reason "..." for intentional stops from stale_kb_gate or approve. Do not use skipped as the run-level cancellation status.
- Finish
finalize with {"status": "completed"}
{% plan_tool "Track workflow: discover, validate_kb, stale_kb_gate, scan, approve, process, finalize. Keep exactly one step in progress." %}
§PROC
1. Discover Docs + Style
Discover candidate user docs via Glob:
README.md
docs/**/*.md
docs/**/*.mdx
**/*.md
**/*.mdx
Dedupe by path. Keep project-relative paths only.
Exclude common generated, dependency, build, and internal metadata paths:
.rp1/**, node_modules/**, .git/**, vendor/**, .venv/**, venv/**
dist/**, **/dist/**, build/**, out/**, coverage/**, **/target/**, **/__pycache__/**
.cache/**, .next/**, .nuxt/**, .svelte-kit/**, .idea/**, .vscode/**, .github/**
Exclude non-user-doc basenames:
AGENTS.md
DEVELOPMENT.md
CHANGELOG.md
LICENSE
LICENSE.md
CODE_OF_CONDUCT.md
SECURITY.md
CONTRIBUTING.md:
- Exclude only if it is a tiny stock template or placeholder
- Keep it if it is clearly project-specific user documentation
Set DOC_FILES to the remaining paths.
If DOC_FILES is empty:
Log:
Discovered {DOC_FILES.length} documentation files
Pick up to 3 sample files for style inference:
- Prefer
README.md if present
- Prefer one
docs/ file
- Prefer one other remaining file
Read each sample and infer:
heading_style: atx | setext
list_marker: dash | asterisk | plus | numbered
code_fence: backtick | tilde | indent
link_style: inline | reference
max_line_length: p90 line length rounded to nearest 10 and clamped to 80..120
Build STYLE_CONFIG:
{
"heading_style": "atx",
"list_marker": "dash",
"list_style": "dash",
"code_fence": "backtick",
"link_style": "inline",
"max_line_length": 100
}
Rules:
list_marker is canonical
list_style is a compatibility alias; keep it equal to list_marker
- If style inference is mixed or weak, choose the dominant style or the default shown above
Log the inferred style in one short block.
2. Validate KB
Use Glob to confirm {kbRoot}/state.json exists.
If missing:
Read {kbRoot}/state.json.
Extract:
If git_commit missing or empty:
Verify the commit exists:
git cat-file -e {KB_GIT_COMMIT}^{commit} 2>/dev/null || echo "INVALID"
If output is INVALID:
Compute:
git rev-parse HEAD
git rev-list --count {KB_GIT_COMMIT}..HEAD
Store:
HEAD_COMMIT
COMMITS_SINCE_KB
Compute repo changes since KB build:
git diff --name-only {KB_GIT_COMMIT} HEAD
Derive STALE_CHANGES:
- Start with the diff list above
- Drop generated or ignored paths from §1
- Drop
.rp1/work/**
- Drop any path present in
DOC_FILES
- Treat all remaining paths as KB-affecting changes for this workflow
Build:
{
"state": "current",
"generated_at": "ISO-8601 timestamp",
"git_commit": "commit sha",
"head_commit": "commit sha",
"commits_behind": 0,
"stale_paths": []
}
Rules:
state = "current" when STALE_CHANGES is empty
state = "stale" when STALE_CHANGES is non-empty
commits_behind = COMMITS_SINCE_KB
stale_paths = STALE_CHANGES
This filter is intentionally strict about non-doc-output changes and intentionally permissive about changes inside DOC_FILES; those docs are the workflow outputs being reconciled.
If STALE_CHANGES is empty:
If STALE_CHANGES is non-empty:
- Output:
WARNING: Knowledge base is out of sync for this workflow.
KB Generated: {generated_at}
KB Commit: {KB_GIT_COMMIT}
Current HEAD: {HEAD_COMMIT}
Behind by: {COMMITS_SINCE_KB} commits
KB-affecting changes since KB build (showing up to 30):
{first 30 stale paths}
- Emit the stale-KB gate:
rp1 agent-tools emit \
--workflow generate-user-docs \
--type waiting_for_user \
--run-id {RUN_ID} \
--step stale_kb_gate \
--data '{"prompt": "Knowledge base is stale. Continue with the stale KB, rebuild the KB first, or cancel?", "context": "KB is {COMMITS_SINCE_KB} commits behind HEAD with {STALE_CHANGES.length} KB-affecting changed paths"}'
- Present the gate using the standard Liquid prompt syntax:
KB_DECISION = {% ask_user "Knowledge base is stale. Continue with the stale KB, rebuild the KB first, or cancel?", options: "Continue with stale KB", "Rebuild KB first", "Cancel" %}
- If
KB_DECISION == "Continue with stale KB":
- If
KB_DECISION == "Rebuild KB first":
- Output:
Documentation update stopped before scan.
Rebuild the KB with /rp1-base:knowledge-build, then re-run this command.
- End the run explicitly:
rp1 agent-tools emit end-run \
--run-id {RUN_ID} \
--outcome cancelled \
--reason "User chose to rebuild the KB before scanning docs"
- STOP
- If
KB_DECISION == "Cancel":
- Output:
Documentation update cancelled before scan.
KB is stale by {COMMITS_SINCE_KB} commits.
- End the run explicitly:
rp1 agent-tools emit end-run \
--run-id {RUN_ID} \
--outcome cancelled \
--reason "User cancelled docs sync at the stale KB gate"
- STOP
3. Scan
Create the session directory:
mkdir -p {workRoot}/{SESSION_DIR}
Batch DOC_FILES into groups of 5.
Spawn one background rp1-base:scribe per batch:
{% dispatch_agent "rp1-base:scribe", background %}
MODE: scan
FILES: {actual JSON array of project-relative paths for this batch}
KB_ROOT: {kbRoot}
KB_INDEX_PATH: {kbRoot}/index.md
Task: return JSON only with:
mode
classifications
summary
- optional
errors
{% enddispatch_agent %}
Wait for all scan agents to finish.
For each response:
- Parse JSON
- Valid only if:
mode == "scan"
classifications is an array
summary is present
- Capture top-level
errors when present
- Append per-file scan errors from successful responses into
AGGREGATED.errors
- Track
TOTAL_BATCHES and FAILED_BATCHES
Failure policy:
- If no scan batch succeeded: transition to
failed
- If
failed_batches / total_batches >= 0.5: transition to failed
- Otherwise continue with successful batches only
Carry KB_STATUS into AGGREGATED.kb unchanged.
Aggregate successful results into AGGREGATED:
{
"generated_at": "ISO-8601 timestamp",
"kb": {
"state": "current",
"generated_at": "ISO-8601 timestamp",
"git_commit": "commit sha",
"head_commit": "commit sha",
"commits_behind": 0,
"stale_paths": []
},
"style": {
"heading_style": "atx",
"list_marker": "dash",
"list_style": "dash",
"code_fence": "backtick",
"link_style": "inline",
"max_line_length": 100
},
"files": {
"README.md": {
"sections": [
{
"heading": "Quick Start",
"line": 10,
"level": 2,
"scenario": "fix",
"kb_match": "modules.md:20"
}
]
}
},
"summary": {
"total_files": 1,
"verify": 0,
"add": 0,
"fix": 1
},
"errors": [
{
"file": "README.md",
"error": "Could not read one heading body cleanly"
}
],
"scan_failures": [
{
"batch": 2,
"error": "Invalid JSON"
}
]
}
Write SCAN_RESULTS_PATH.
Compute:
TOTAL_SECTIONS = AGGREGATED.summary.verify + AGGREGATED.summary.add + AGGREGATED.summary.fix
Show the scan summary:
Documentation Scan Complete
{AGGREGATED.summary.total_files} files, {TOTAL_SECTIONS} sections: {AGGREGATED.summary.verify} verify, {AGGREGATED.summary.add} add, {AGGREGATED.summary.fix} fix
If AGGREGATED.kb.state == "stale":
- Add one short warning line:
Using stale KB by user choice: {AGGREGATED.kb.commits_behind} commits behind
If scan_failures is non-empty:
- Add one short warning line with the failure count
- Do NOT dump raw agent output unless the scan is already failing
If errors is non-empty:
- Add one short warning line with the per-file error count
- Preserve the errors in
scan_results.json for process/reporting
4. Approval
Emit the approval gate:
rp1 agent-tools emit \
--workflow generate-user-docs \
--type waiting_for_user \
--run-id {RUN_ID} \
--step approve \
--data '{"prompt": "Proceed with documentation updates?", "context": "Docs sync approval after scan phase"}'
APPROVAL = {% ask_user "Proceed with documentation updates?", options: "Yes", "No" %}
If APPROVAL == "No":
- Output:
Documentation update cancelled.
Scan results preserved at: {workRoot}/{SCAN_RESULTS_REL}
- End the run explicitly:
rp1 agent-tools emit end-run \
--run-id {RUN_ID} \
--outcome cancelled \
--reason "User declined to apply documentation updates after the scan"
- STOP
If APPROVAL == "Yes", continue.
5. Process
Set PROCESS_FILES = Object.keys(AGGREGATED.files).
Batch into groups of 5.
Spawn one background rp1-base:scribe per batch:
{% dispatch_agent "rp1-base:scribe", background %}
MODE: process
FILES: {actual JSON array of project-relative paths for this batch}
KB_ROOT: {kbRoot}
SCAN_RESULTS_PATH: {workRoot}/{SCAN_RESULTS_REL}
STYLE: {actual JSON.stringify(STYLE_CONFIG)}
Task: return JSON only with:
mode
results
summary
- optional
errors
{% enddispatch_agent %}
Wait for all process agents to finish.
For each response:
- Parse JSON
- Valid only if:
mode == "process"
results is an array
If no process batch response is valid:
- Output
ERROR: Process phase failed before any valid result was returned.
- Transition to
failed
- STOP
Aggregate file outcomes:
SUCCESSFUL_FILES: status == "success"
PARTIAL_FILES: status == "partial"
FAILED_FILES: status == "failed"
Rules:
success and partial both count as processed
partial means useful edits landed, but some edits failed or review markers were inserted
failed means the file was not usefully updated
Build:
{
"files_processed": 0,
"files_succeeded": 0,
"files_partial": 0,
"files_failed": 0,
"total_sections_verified": 0,
"total_sections_added": 0,
"total_sections_fixed": 0,
"total_edits_applied": 0,
"failed_files": []
}
Count section and edit totals from both SUCCESSFUL_FILES and PARTIAL_FILES.
Final report:
Documentation Sync Complete
KB used: {AGGREGATED.kb.state}
- Commits behind at scan start: {AGGREGATED.kb.commits_behind}
Files processed: {files_processed}
- Succeeded: {files_succeeded}
- Partial: {files_partial}
- Failed: {files_failed}
Changes applied:
- Sections verified: {total_sections_verified}
- Sections added: {total_sections_added}
- Sections fixed: {total_sections_fixed}
- Total edits: {total_edits_applied}
Scan results: {workRoot}/{SCAN_RESULTS_REL}
Git-ready: docs: sync {files_succeeded + files_partial} files with KB ({total_edits_applied} edits)
If failed_files is non-empty:
- List up to 10 lines as
{path}: {first error or fallback message}
If files_succeeded + files_partial == 0:
- Transition to
failed
- STOP after the report
Do NOT delete SCAN_RESULTS_PATH.
Transition to finalize.