Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
14 changes: 13 additions & 1 deletion .claude-plugin/marketplace.json
Original file line number Diff line number Diff line change
Expand Up @@ -65,6 +65,16 @@
"./skills/repo-sync"
]
},
{
"name": "doc-drift",
"description": "Detect documentation drift against a pinned git boundary and draft Keep a Changelog notes from merged PRs",
"source": "./",
"strict": false,
"skills": [
"./skills/doc-drift-scanner",
"./skills/doc-drift-fixer"
]
},
{
"name": "repoman",
"description": "The RepoMan entrypoint: first-time setup, enabling a program, enrolling repos or an org, or running a program across enrolled repos. A single intent-dispatching skill.",
Expand All @@ -79,7 +89,9 @@
"./skills/github-pr-review",
"./skills/repo-sync",
"./skills/automation-health-dashboard",
"./skills/github-weekly-report"
"./skills/github-weekly-report",
"./skills/doc-drift-scanner",
"./skills/doc-drift-fixer"
]
}
]
Expand Down
22 changes: 22 additions & 0 deletions .github/workflows/shellcheck.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
name: shellcheck

on:
pull_request:
push:
branches: [main]

permissions: {}

jobs:
shellcheck:
runs-on: ubuntu-latest
permissions:
contents: read
steps:
- uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
- name: Install shellcheck
run: sudo apt-get update && sudo apt-get install -y shellcheck
- name: shellcheck (vendor tooling)
# Explicit paths — never a glob. Do not lint skills/repoman/scripts/
# here; the engine is linted in rossoctl/automation.
run: shellcheck --severity=warning scripts/vendor-engine.sh tests/test-vendor-engine.sh
3 changes: 2 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
# RepoMan

RepoMan runs automated maintenance **programs** (PR review, link health, dependency
bumps, weekly reports, repo sync) across a set of GitHub repos you enroll once. It
bumps, weekly reports, repo sync, doc drift) across a set of GitHub repos you enroll once. It
handles the capability checks and output wiring for you, so you install once and
enable programs on the fly.

Expand Down Expand Up @@ -49,6 +49,7 @@ The `repoman` bundle ships these programs, each ready to enable:
| [repo-sync](skills/repo-sync/) | Keep local clones of enrolled repos current; read-only toward GitHub |
| [automation-health](skills/automation-health-dashboard/) | Generate an executive-facing dashboard combining all program metrics |
| [weekly-report](skills/github-weekly-report/) | Weekly org activity reports with merged PRs, CI health, and active-epic tracking |
| [doc-drift](skills/doc-drift-scanner/) | Scan repos for documentation drift and draft changelog notes from merged PRs ([fixer](skills/doc-drift-fixer/) re-verifies and opens mechanical doc fix PRs) |

Each program is also published as its own standalone plugin, for using one on its own
(for example a one-off PR review) without the full RepoMan setup. Run `/plugin` after
Expand Down
26 changes: 23 additions & 3 deletions scripts/vendor-engine.sh
Original file line number Diff line number Diff line change
Expand Up @@ -19,14 +19,18 @@ while [ $# -gt 0 ]; do
done
[ -n "$SRC" ] && [ -n "$DEST" ] || { echo "ERROR: --source and --dest are required" >&2; exit 2; }

# The 23 engine scripts. Flat set: nearly all source program-lib.sh,
# The 26 engine shell scripts. Flat set: nearly all source program-lib.sh,
# which sources core.sh/github-api.sh/fork.sh/org.sh/repoman-requirements.sh.
MANIFEST="automation-health-dashboard core dep-bump-fixer dep-bump-scanner \
doc-drift-fixer doc-drift-precheck doc-drift-scanner \
extract-broken-links fork github-api link-health-fixer link-health-scanner \
org parse-diff-map pr-review-fixer pr-review-impact pr-review-lib \
pr-review-scanner program-lib repo-sync repoman-check repoman-index \
repoman-requirements repoman-run repoman-setup weekly-report"

# Non-shell engine files copied alongside the manifest (explicit, no glob).
EXTRA_FILES="doc-drift-check.py"

# Pre-validate the WHOLE manifest before copying anything, so a source missing
# late in the list cannot leave a half-vendored dest with a stale _vendor.json.
# shellcheck disable=SC2086
Expand All @@ -35,6 +39,12 @@ for f in $MANIFEST; do
echo "ERROR: missing engine source: $SRC/scripts/$f.sh (nothing copied)" >&2; exit 1
fi
done
# shellcheck disable=SC2086
for f in $EXTRA_FILES; do
if [ ! -f "$SRC/scripts/$f" ]; then
echo "ERROR: missing engine source: $SRC/scripts/$f (nothing copied)" >&2; exit 1
fi
done

mkdir -p "$DEST"
# shellcheck disable=SC2086
Expand All @@ -43,6 +53,12 @@ for f in $MANIFEST; do
echo "ERROR: copy failed for $f.sh" >&2; exit 1
fi
done
# shellcheck disable=SC2086
for f in $EXTRA_FILES; do
if ! cp "$SRC/scripts/$f" "$DEST/$f"; then
echo "ERROR: copy failed for $f" >&2; exit 1
fi
done

# Provenance stamp: record the SOURCE repo's HEAD sha (not the dest repo's).
if ! SHA=$(git -C "$SRC" rev-parse HEAD 2>/dev/null); then
Expand All @@ -51,7 +67,10 @@ fi
NOW=$(date -u +%Y-%m-%dT%H:%M:%SZ)
# word-splitting $MANIFEST into one line per script is intentional
# shellcheck disable=SC2086
if ! FILES_JSON=$(printf '%s\n' $MANIFEST | sed 's/$/.sh/' | jq -R . | jq -s .); then
if ! FILES_JSON=$( {
printf '%s\n' $MANIFEST | sed 's/$/.sh/'
printf '%s\n' $EXTRA_FILES
} | jq -R . | jq -s .); then
echo "ERROR: could not build file list" >&2; exit 1
fi
if ! jq -n --arg repo "rossoctl/automation" --arg sha "$SHA" --arg at "$NOW" \
Expand All @@ -61,4 +80,5 @@ if ! jq -n --arg repo "rossoctl/automation" --arg sha "$SHA" --arg at "$NOW" \
echo "ERROR: provenance stamp write failed" >&2; exit 1
fi

echo "Vendored $(echo "$MANIFEST" | wc -w | tr -d ' ') engine scripts to $DEST (source $SHA)"
# shellcheck disable=SC2086
echo "Vendored $(echo $MANIFEST | wc -w | tr -d ' ') engine scripts (+ extras) to $DEST (source $SHA)"
63 changes: 63 additions & 0 deletions skills/doc-drift-fixer/SKILL.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,63 @@
---
name: doc-drift-fixer
description: >-
Re-verify doc-drift scanner findings and open mechanical documentation fix
PRs. Corrections get their own docs: fix PR; additions fold into one rolling
update/doc-drift PR per repo. Never edits code to match docs, never merges,
never publishes a GitHub release. Use after a doc-drift scan.
license: Complete terms in LICENSE.txt
---

# Doc Drift Fixer

Re-verify findings from the doc-drift scanner. Applies mechanical documentation
fixes on a fork and opens PRs. Non-mechanical findings stay on the rollup issue
checklist for a human.

**Run scripts with `--help` first.** Engine scripts live under the RepoMan
plugin scripts dir.

## Prerequisites

- `bash` 3.2+
- `python3` 3.8+
- `gh` (authenticated; needs fork create + PR create)
- `jq`
- A prior scanner run that wrote `$HOME/reports/doc-drift/latest.json`
- `fork_owner` set in `~/.repoman/config.json`

### Requirements (machine-readable)

- pat_scopes: [repo]
- labels_required: [doc-drift]
- labels_applied: []
- programs: [fixer]

## Running

Default is dry-run (preview only):

```bash
bash "${CLAUDE_PLUGIN_ROOT}/skills/repoman/scripts/doc-drift-fixer.sh" --dry-run --issue-limit 5
```

Live (opens PRs):

```bash
bash "${CLAUDE_PLUGIN_ROOT}/skills/repoman/scripts/doc-drift-fixer.sh" --live
```

## PR shapes

| Finding | Action |
|---------|--------|
| Mechanical correction (unique replacement) | Own PR: `docs: fix …` |
| Mechanical addition (enum gap, release draft) | Single rolling `update/doc-drift` PR per repo |
| Non-mechanical (absence-claim prose, retired name without replacement) | Rollup issue checklist only |
| Release date mismatch | Changelog heading PR only — does **not** edit the published GitHub release |
| Release draft | Rolling PR body includes paste-ready Keep a Changelog markdown |

## Related

- Scanner skill: `doc-drift-scanner`
- Friendly name: **Doc-Drift-Manager** (`doc-drift`)
99 changes: 99 additions & 0 deletions skills/doc-drift-scanner/SKILL.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,99 @@
---
name: doc-drift-scanner
description: >-
Scan enrolled repos for documentation drift against a pinned git boundary:
missing paths, flag/subcommand parity, configured enumerations, retired names,
version strings, absence claims, and changelog-vs-GitHub-release dates. Creates
one rollup issue per repo and writes structured JSON reports. Use when docs
may no longer match the code, or when drafting release notes from merged PRs.
license: Complete terms in LICENSE.txt
---

# Doc Drift Scanner

Scan enrolled repositories for documentation that disagrees with the code at a
pinned git ref. Creates or updates one rollup `[doc-drift]` issue per repo and
writes `latest.json` / `history.json`.

**Run scripts with `--help` first** to see full usage and options. The engine
lives in the RepoMan plugin scripts dir — do not copy it into this skill.

## Prerequisites

- `bash` 3.2+ (works with the macOS system default; no bash-4 features used)
- `python3` 3.8+ (stdlib only; no PyYAML)
- `gh` (GitHub CLI, authenticated with repo access)
- `jq` (JSON processor)
- RepoMan config at `~/.repoman/config.json` with enrolled clones under `repos_dir`

### Requirements (machine-readable)

- pat_scopes: [repo]
- labels_required: []
- labels_applied: [doc-drift]
- programs: [scanner]

## Before Running

### 1. Verify authentication

```bash
gh auth status
```

### 2. Ensure clones are current

Prefer `repoman run repo-sync` (or enable `repo-sync`) so scanners read fresh
trees. The run orchestrator refreshes clones for this program automatically.

### 3. Confirm report location

Reports default to `$HOME/reports/doc-drift`. Override with `REPORTS_DIR`.

## Running the Scanner

Always dry-run first:

```bash
bash "${CLAUDE_PLUGIN_ROOT}/skills/repoman/scripts/doc-drift-scanner.sh" --dry-run
```

Live run (creates/updates/closes rollup issues):

```bash
bash "${CLAUDE_PLUGIN_ROOT}/skills/repoman/scripts/doc-drift-scanner.sh" --issue-limit 5
```

Or via the orchestrator:

```bash
bash "${CLAUDE_PLUGIN_ROOT}/skills/repoman/scripts/repoman-run.sh" doc-drift --dry-run
```

## Optional per-repo config

Repos may commit `.repoman/doc-drift.yaml` at the boundary ref to opt into
enumeration parity, retired-name denylists, and a custom changelog path. Without
it, only generic checks run (paths, flags, versions, absence claims, release
date/draft when GitHub metadata is supplied).

## Precheck (DAM schedules)

`--worklist` is required. Pass a persistent path. The script's default path

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

nit: "required" is accurate as a rule for callers, but nothing enforces it — the flag is optional and silently falls back to $PROGRAM_TMPDIR/worklist.json, which the EXIT trap in setup_workspace deletes. Verified: a bare run prints worklist: /tmp/doc-drift-precheck-XXXXXX/worklist.json, and that file is gone once the script returns; with --worklist "$REPORTS_DIR/worklist.json" the file survives with {count, findings[]}.

So this doc is the right guardrail for now. The durable fix is upstream in doc-drift-precheck.sh: default the worklist to $REPORTS_DIR (or exit non-zero instead of printing a path it is about to delete), otherwise the next caller that omits the flag hits the same silent dead end.

sits in a temp directory that is removed on exit, so a bare run prints a
`worklist:` path that is already gone.

```bash
mkdir -p "${REPORTS_DIR:-$HOME/reports/doc-drift}"
bash "${CLAUDE_PLUGIN_ROOT}/skills/repoman/scripts/doc-drift-precheck.sh" \
--worklist "${REPORTS_DIR:-$HOME/reports/doc-drift}/worklist.json"
```

Exit `1` when the worklist is empty (skip the model turn). Exit `0` and print
`worklist: <path>` when there is work. Any other exit is fail-open. Read the
file at the `--worklist` path you passed; do not use the script's default.

## Related

- Fixer skill: `doc-drift-fixer`
- Friendly name: **Doc-Drift-Manager** (`doc-drift`)
4 changes: 3 additions & 1 deletion skills/repoman/SKILL.md
Original file line number Diff line number Diff line change
Expand Up @@ -76,6 +76,7 @@ on the left, skill dir on the right):
| `pr-review` | `skills/github-pr-review/` |
| `automation-health` | `skills/automation-health-dashboard/` |
| `repo-sync` | `skills/repo-sync/` |
| `doc-drift` | `skills/doc-drift-scanner/` |
Comment thread
jimcadden marked this conversation as resolved.
Resolved

## State-inference decision tree

Expand Down Expand Up @@ -165,7 +166,7 @@ state so it never lies about what is installed:
one-line description read from the program's `SKILL.md` (the same source the
gate uses; use the program→skill-dir map above). The authoritative backend
names remain the `KNOWN_PROGRAMS` allowlist in `repoman-setup.sh`
(`link-health`, `dep-bump`, `pr-review`, `automation-health`, `repo-sync`) —
(`link-health`, `dep-bump`, `pr-review`, `automation-health`, `repo-sync`, `weekly-report`, `doc-drift`) —
show the friendly names to the user, resolve back to the canonical token
before any writer call. The description in each `SKILL.md` frontmatter is
usually a YAML **folded block scalar** (`description: >-` with the text on the
Expand Down Expand Up @@ -211,6 +212,7 @@ does not resolve must never reach them.
| PR-Review-Manager | `pr-review` |
| Automation-Health-Manager | `automation-health` |
| Repo-Sync-Manager | `repo-sync` |
| Doc-Drift-Manager | `doc-drift` |

Match user input leniently against this list: "pr review", "pr-review-manager",
and "prmanager" all resolve to `pr-review`; "dep bump" → `dep-bump`. **Always**
Expand Down
10 changes: 7 additions & 3 deletions skills/repoman/scripts/_vendor.json
Original file line number Diff line number Diff line change
@@ -1,12 +1,15 @@
{
"source_repo": "rossoctl/automation",
"source_sha": "0311752eba5350c74b8c9650137d3e6da65bd547",
"vendored_at": "2026-10-23T03:46:40Z",
"source_sha": "fa61909029207b08d17eff5625bd0aa4772c90b2",
"vendored_at": "2026-11-03T22:56:54Z",
"files": [
"automation-health-dashboard.sh",
"core.sh",
"dep-bump-fixer.sh",
"dep-bump-scanner.sh",
"doc-drift-fixer.sh",
"doc-drift-precheck.sh",
"doc-drift-scanner.sh",
"extract-broken-links.sh",
"fork.sh",
"github-api.sh",
Expand All @@ -25,6 +28,7 @@
"repoman-requirements.sh",
"repoman-run.sh",
"repoman-setup.sh",
"weekly-report.sh"
"weekly-report.sh",
"doc-drift-check.py"
]
}
Loading
Loading