Repository navigation
Conversation
Scanner/fixer/precheck plus offline checker covering path, flag, enum, retired-name, version, absence, and changelog-vs-release-date checks. Signed-off-by: Jim Cadden <jcadden@ibm.com>
clawgenti
left a comment
There was a problem hiding this comment.
Solid, well-structured addition — CI green, DCO signed, the fail-open/fail-closed contracts are consistent, and the offline checker avoids injection-prone subprocess patterns. Non-blocking findings:
- scripts/automation-health-dashboard.sh:409 — trend Delta column will always render
0(see inline). - scripts/doc-drift-check.py:241 — mini YAML parser hardcodes which keys are lists.
- scripts/doc-drift-check.py:39 —
SUBCMD_DOC_REonly matches same-line subcommand lists. - tests/test-doc-drift-precheck.sh:41 — exit-code/stdout contracts are simulated rather than exercised against the real script.
Reviewed by clawgenti using the github-pr-review skill
- Dashboard Delta uses (.new//0)/(.fixed//0) for null-safe trend rows - YAML parser detects list-vs-map from the next indented "- " line - Subcommands: also collects following bullet items (same-column OK) - Precheck test exercises the real script with gh stubbed Signed-off-by: Jim Cadden <jcadden@ibm.com>
- Dashboard Delta uses (.new//0)/(.fixed//0) for null-safe trend rows - YAML parser detects list-vs-map from the next indented "- " line - Subcommands: also collects following bullet items (same-column OK) - Precheck test exercises the real script with gh stubbed Assisted-By: Claude (Anthropic AI) <noreply@anthropic.com> Signed-off-by: Jim Cadden <jcadden@ibm.com>
|
Addressed the four review suggestions in fbf8f7b:
Assisted-By: Claude Code |
clawgenti
left a comment
There was a problem hiding this comment.
All four previous-round findings are addressed — the scanner now emits new/fixed deltas for the dashboard trend, the YAML subset parser detects lists structurally, subcommand lists accept multi-line bullets, and the precheck test exercises the real script. Remaining findings are minor dead-code cleanups; nothing blocking.
- scripts/doc-drift-check.py:234 — unreachable branch in
parse_simple_yaml(nit) - scripts/doc-drift-check.py:402 — redundant
existsre-check incheck_paths(nit) - scripts/doc-drift-check.py:809 — dead
passblock incheck_versions(nit) - scripts/doc-drift-fixer.sh:240 —
enumerations|pathscase never stages an edit (suggestion)
Reviewed by clawgenti using the github-pr-review skill
Drop the unreachable YAML flow-map branch and the redundant path existence re-check. Prefer a stable tag over marketplace version when the boundary is that tag. Leave enum and path corrections on the rollup issue until a mechanical edit exists. Signed-off-by: Jim Cadden <jcadden@ibm.com>
Drop the unreachable YAML flow-map branch and the redundant path existence re-check. Prefer a stable tag over marketplace version when the boundary is that tag. Leave enum and path corrections on the rollup issue until a mechanical edit exists. Assisted-By: Claude (Anthropic AI) <noreply@anthropic.com> Signed-off-by: Jim Cadden <jcadden@ibm.com>
|
Addressed the second-round nits in c4b55a6:
Assisted-By: Claude Code |
clawgenti
left a comment
There was a problem hiding this comment.
Well-structured addition — the pinned-ref design, offline checker, and fixture coverage are solid, and the scanner/fixer wiring matches the existing link-health/dep-bump patterns. One real bug in the versions check slipped past the fixtures (the CHANGELOG.md exclusion never matches), plus two small dead-code leftovers.
- must-fix
scripts/doc-drift-check.py:809— the CHANGELOG.md skip incheck_versionsnever fires, so historical## [x.y.z]headings become false-positive version-drift findings (verified on a fixture), and the fixer would then rewrite those headings repo-wide. - suggestion
scripts/doc-drift-check.py:998—newest_blob_citedis computed but never used. - nit
scripts/doc-drift-check.py:431— both branches of this conditional are identical.
Reviewed by clawgenti using the github-pr-review skill
The CHANGELOG skip compared an uppercased basename to a mixed-case literal, so historical version headings became mechanical corrections and the fixer replaced that token across the repo. Edits now touch only the flagged line, a failed scan keeps the previous findings, and compact YAML lists parse. Signed-off-by: Jim Cadden <jcadden@ibm.com>
|
Addressed the open findings in b95f446.
Assisted-By: Claude Code |
b95f446 to
f668ded
Compare
clawgenti
left a comment
There was a problem hiding this comment.
Follow-up on c4b55a6: the changelog-history false positive is fixed (basename-based CHANGELOG skip), the dead paths flagged last round are gone, and the dashboard trend reads the new new/fixed deltas — but the ubuntu CI leg is now red: the fixture loader crashes on Python 3.14.
Findings:
- must-fix
tests/test-doc-drift-check.sh:388— ubuntu-latest (Python 3.14) fails inRun test suite: the heredoc loads doc-drift-check.py viaexec_modulewithout registering it insys.modules, and@dataclassthen raisesAttributeError(details inline; one-line fix).
Reviewed by clawgenti using the github-pr-review skill
Keep the doc-drift shellcheck step and tests alongside the attribution-consumer checks that landed on main. Signed-off-by: Jim Cadden <jcadden@ibm.com> Assisted-by: Cursor <cursoragent@cursor.com>
Python 3.14 looks up the class module in sys.modules while processing @DataClass, so the fixture loader must register the module before exec_module. Signed-off-by: Jim Cadden <jcadden@ibm.com> Assisted-by: Cursor <cursoragent@cursor.com>
|
Addressed the Python 3.14 fixture crash in 7f836fd and merged upstream/main in 139cec5.
Assisted-By: Cursor |
clawgenti
left a comment
There was a problem hiding this comment.
Solid addition of the deterministic doc-drift program — the checker/scanner/fixer/precheck are well-factored and fail-safe throughout. Verified locally: full fixture test suite passes, shellcheck is clean on all new scripts, the create_fork_pr/diff_against_previous contracts match program-lib on main, and the registry/dashboard wiring is consistent.
All checks pass. Ready for human review.
Reviewed by clawgenti using the github-pr-review skill
jimcadden
left a comment
There was a problem hiding this comment.
Reviewed at 7f836fd (10 files, +2976/−11). Verdict: not ready to merge — one systemic precision problem; everything else is polish.
What passes
tests/test-doc-drift-check.sh → ALL PASS, tests/test-doc-drift-precheck.sh → ALL PASS, shellcheck --severity=warning on the three scripts → clean, and the neighbouring suites (-run, -setup, -check, -requirements, -index, automation-health-dashboard, lib-inventory, lib-modules) still pass.
The release logic (mergedAt vs publishedAt, draft vs published, tag ≠ declared version), per-line dedupe keys, compact-YAML parsing and the line-scoped fixer replace are correct and genuinely well tested.
P1 — the checker is ~all false positives on real input
I ran it against rossoctl/automation itself at main (197af28, no .repoman/doc-drift.yaml): 969 findings, 964 corrections, and I could not find a single true one.
- 670
flags— two causes:- (a) for a doc that names more than one script, every flag in the whole doc is checked against each named script's parser, so README's
--profile/--org/--fork-owner/--flagbecome "corrections" againstgithub-api.sh,dep-bump-scanner.sh,pr-review-fixer.sh, … — and those flags are consumed byload_org_profile()inprogram-lib.sh, not by the individual parsers; - (b) the intra-script check treats prose words on a
# Subcommands:line as subcommands (Each,writes) and any innercasearm as one (central,same,end).
- (a) for a doc that names more than one script, every flag in the whole doc is checked against each named script's parser, so README's
- 299
paths— no basename/relative resolution: 89 of them name a file that does exist in the tree (link-health-fixer.sh→scripts/link-health-fixer.sh). It also captures non-paths:rossoctl/agent-skills,gemini-2.5-flash,discord.accounts,.,../README.md.
Consequence is operational, not cosmetic: the rollup issue is 900 bullets nobody can action, doc-drift-precheck.sh exits 0 on every repo forever (so every scheduled run burns a model turn), and the dashboard's correction count is pure noise.
Repro:
python3 scripts/doc-drift-check.py --clone <main checkout> \
--repo rossoctl/automation --releases '[]' --prs '[]' | jq .counts
P1 — ships no documentation
This repo's convention per program is a README Programs row + <program> in the repoman-run.sh --help list at README:53 + standing-orders/<program>.md (docs/program-template.md:405) + a dated design spec in docs/specs/.
The PR adds 2.4k lines and zero .md. The .repoman/doc-drift.yaml schema is documented nowhere, and with the file absent the defaults silently disable sources_of_truth / retired_names, so per-repo behaviour quietly differs.
P2
- The scanner is untested — CI only does
bash -non it. link-health and dep-bump each have index-wiring, subset-carryforward and dry-run-baseline tests; the carry-forward-on-failed-repo and issue open/close/ledger paths are the risky bits and have no coverage. - The release/PR fetch block is copy-pasted between scanner and precheck and costs 21
ghcalls per repo (1 + onegh release viewper tag) on a path that runs before every model turn.gh api repos/{o}/{r}/releasesreturns bodies in one call; the shared helper belongs inprogram-lib.sh. enumerationscorrections andpathscorrections aremechanical=truewithreplace_with: "", but the fixer's*)branch prints "no mechanical edit path" — that breaks the meaning themech_corrpartition relies on.boundary: mainresolvesHEAD, notorigin/main; the fixer checks branches out in the shared clone, so an interrupted run makes "pinned ref" non-deterministic.scripts = {basename: path}silently drops duplicates — repoman has bothscripts/x.shandskills/repoman/scripts/x.sh, so one is checked arbitrarily.
Nits
.gitignorehas no__pycache__/: the test imports the checker via importlib and a local run createsscripts/__pycache__/doc-drift-check.cpython-312.pyc(reproduced) → untracked junk on the nextgit add -A.tag.lstrip("v")/tag.lstrip()— useremoveprefix/.strip().- Fixer replacing a retired name with
""leaves double spaces. test-automation-health-dashboard.shwasn't extended even though the dashboard grew a whole doc-drift section.
A whole-document flag scan and an unscoped path match turned a run on this repo into hundreds of false corrections. Findings now follow the line that names a script, and only in-repo paths count. Signed-off-by: Jim Cadden <jcadden@ibm.com> Assisted-by: Cursor <cursoragent@cursor.com>
jimcadden
left a comment
There was a problem hiding this comment.
Re-review at f1b4e06 (7f836fd..f1b4e06, +672/−130). The P1 precision blocker is cleared — this is now a useful tool rather than a noise generator.
Precision, measured on real input
rossoctl/automation@ main197af28: 969 findings → 10 (9 corrections, 1 addition). I checked all ten by hand; 9 hold up.- True ones:
scripts/lib/and fourtests/*.shthat no longer exist;set-requirementspresent inrepoman-setup.sh's dispatcher but missing from itsSubcommands:header;--kagenti-dirclaimed onautomation-health-dashboard.sh(noKAGENTIanywhere inscripts/); and the best one —docs/running-without-openclaw.md:187tells people to runlink-health-scanner.sh --profile myorg, which actually exits 1 withUnknown option: --profile. That's a real doc bug, pre-existing, that nothing else in the pipeline would have found. - The one false positive:
dep-bump-scanner.sh accepts --programatdocs/specs/2026-09-30-repoman-phase5-index-dashboard-design.md:227. That--programbelongs torepoman-index.sh, named on the previous line of the same wrapped bullet. jimcadden/repoman@b3f128c: 3 findings — the sameset-requirementsgap in the vendored copy (true), plus 2 bundled-SKILL path hits (see item 4).- Whole-repo scans take 4.3s, so per-line findings are cheap.
Repro:
python3 scripts/doc-drift-check.py --clone <main checkout> \
--repo rossoctl/automation --releases '[]' --prs '[]' | jq .counts
Docs P1: done properly
README Programs row, the repoman-run.sh --help entry, standing-orders/doc-drift.md, and a dated spec carrying the yaml schema and the precision rules — and the rules it states are actually what the code does (I checked each one against the fixture). Line-scoped and mechanical: false semantics are documented too. .gitignore has __pycache__/ and *.pyc; paths / enumerations corrections are non-mechanical as agreed.
Tests
+120 lines hitting exactly the FP classes from the first review — span binding, prose Subcommands: lines, inner case arms, basename resolution, non-paths. All 35 suites in the CI list pass locally; shellcheck --severity=warning is clean on all three groups (lib modules, repoman tests, doc-drift scripts).
New, small
- P3 — a flag binds to every mention before it on the line, not the nearest:
`scripts/alpha.sh` and `scripts/beta.sh --frobnicate`reports both alpha and beta accepting--frobnicate(reproduced; only the second is a claim). Fix is one line: bind each flag to the greatest mention-end ≤ flag start. - P3 —
_line_in_spandoestext.find(span), so a doc with two identical lines yields one finding at the first line and silently drops the second (reproduced). Pair each span with its offset instead. - P2 —
ignore_pathsis only read bycheck_retired_namesandcheck_versions(check.py:1021,:1169).paths/flagsfindings cannot be suppressed at all, and this repo'sdocs/specs/**are historical — the--kagenti-dirfinding is un-silenceable without a code change. Honorignore_pathsin those checks too (or addignore_checks). - P3 — path resolution is repo-relative only, so a bundled skill doc misreports its own bundle:
skills/repoman/SKILL.md:399namesscripts/org.sh, which exists atskills/repoman/scripts/org.sh. Before giving up on a slash path, trydirname(doc)/raw. - Nit —
PATH_EXTSsilently ignores.csv/.ini/.cfg/.conf/.js/.ts/.jsonl, so those claims are never checked. Defensible, but say so in the spec.
Still open from the first review
The scanner still has no behavioural test — CI runs bash -n on it and nothing else, and carry-forward-on-failure plus the open/close ledger are the risky paths. The release/PR fetch is still copy-pasted between scanner and precheck and still costs 21 gh calls per repo (1 + one gh release view per tag) on the path that runs before every model turn. boundary: main still resolves HEAD (check.py:214) — the standing order now documents that honestly, but the fixer checks branches out in the shared clone, so a "pinned" ref still isn't deterministic.
Verdict
Precision blocker gone, docs and tests in good shape — I'd merge this and take item 3 plus the scanner test as follow-ups rather than hold it.
clawgenti
left a comment
There was a problem hiding this comment.
This follow-up binds doc claims to single real files: flags attach only to scripts named earlier on the same line (inheriting accepted flags through program-lib.sh and its modules), path claims require a repo-file extension plus a first directory present in the tree, and subcommand extraction is limited to dispatcher case arms — with new fixture coverage for each rule and the design doc and standing orders recording the semantics. CI is green on all four checks and the checker at head parses cleanly.
All checks pass. Ready for human review.
Reviewed by clawgenti using the github-pr-review skill
A flag after two script names was claimed for every script before it, and two identical lines collapsed to one finding. ignore_paths now covers path and flag checks, and a slash path next to the document counts as present. Assisted-by: Cursor <cursoragent@cursor.com> Signed-off-by: Jim Cadden <jcadden@ibm.com>
|
Addressed the five items from the re-review in 97254a5.
The wrapped-bullet Still open, as follow-ups: scanner behavioral tests (carry-forward and the issue ledger), the duplicated release/PR fetch, and Assisted-By: Cursor |
jimcadden
left a comment
There was a problem hiding this comment.
Re-review at 97254a5 (f1b4e06..97254a5, 4 files, +112/−59). All four items from the last review are fixed and verified end-to-end, not just by unit test.
Verified
- Nearest script before the flag.
`scripts/alpha.sh` and `scripts/beta.sh --frobnicate`now reports beta only; alpha is clean. The new fixture asserts both halves of that. - Duplicate lines.
_doc_spansreturns(offset, line)and_line_atcounts from the offset, so two identical lines are two findings with their own line numbers — the test now asserts exactly 2 distinctdoc_linevalues for the repeatedbeta.sh --dry-runline. ignore_pathsreachespathsandflags. Verified against a real repo, not a fixture: committed.repoman/doc-drift.yamlwithignore_paths: ["skills/**"]tojimcadden/repomantakes that scan from 2 findings to 0.- Bundle-relative path fallback.
jimcadden/repomanscan drops 3 → 2;skills/repoman/SKILL.md:399citingscripts/org.shfor the copy atskills/repoman/scripts/org.shis correctly suppressed, and a genuine miss in the same doc is still reported. PATH_EXTSblind spots and theignore_pathsscope are now stated in the spec, which matches the code.
Precision, current
| Repo @ ref | Findings | True |
|---|---|---|
rossoctl/automation @ 197af28 |
10 | 9 |
jimcadden/repoman @ b3f128c |
2 | 1 |
All 35 suites in the CI list pass locally; shellcheck --severity=warning is clean on the doc-drift scripts and their tests.
The one remaining false positive
dep-bump-scanner.sh accepts --program at docs/specs/2026-09-30-repoman-phase5-index-dashboard-design.md:227 is unchanged, and the nearest-mention rule cannot catch it by construction: that bullet names only dep-bump-scanner.sh and elides the real command with "same shape with". Two ways out, both cheap — name repoman-index.sh on that line (the claim becomes checkable, which is the point of the check), or add docs/specs/** to ignore_paths, which now covers flag findings.
For the repoman hit, skills/repoman/SKILL.md:405 cites tests/test-repoman-match-name.sh, which lives in this repo and the doc says so explicitly — that is what ignore_paths is for.
Still open from the first review
The scanner has no behavioural test (bash -n only in CI); carry-forward-on-failure and the issue ledger are the risky paths. The release/PR fetch is still duplicated between scanner and precheck and still costs 21 gh calls per repo (1 + one gh release view per tag) on the path that runs before every model turn. boundary: main still resolves HEAD (check.py:214).
Verdict
Precision is where it needs to be, the rules are documented as implemented, and every rule has a test. I'd merge this and take the scanner test plus the fetch de-duplication as separate PRs.
rubambiza
left a comment
There was a problem hiding this comment.
Independent review at 97254a5. I re-verified the key claims from a clean checkout rather than relying on the prior review loop:
- Both suites pass clean (
test-doc-drift-check.sh26/26,test-doc-drift-precheck.shall pass),shellcheck --severity=warningclean on all three scripts. - Reproduced the checker against this repo at
main→ 10 findings, 9 true (the one FP is--programatdocs/specs/2026-09-30-...-design.md:227, where the nearest-mention rule cannot catch it by construction). ignore_pathsnow reachescheck_pathsandcheck_flags— confirmed.- Security surface is read-only: all
subprocessuse is list-formgit, noshell=True/eval/os.system/network in the Python, output only to stdout; the fixer stays on fork branches with--force-with-leaseand never pushes the default branch.
The test (macos-latest) red is a runner-capacity infra failure ("the job was not acquired by Runner of type hosted"), not a test failure — ubuntu, pr-title, and DCO all pass.
Author: jimcadden (MEMBER — maintainer)
Areas reviewed: Python, Shell, CI, Docs
Agent/IDE config (.claude/.vscode): none
Commits: 8, all signed-off: yes
CI status: ubuntu / pr-title / DCO passing; macOS failed on runner capacity (infra, not code)
Two non-blocking notes inline: the checker is a strong candidate for the same module split program-lib.sh got (follow-up), and one test fixture is fragile under tag.gpgsign. Approving.
| @@ -0,0 +1,1636 @@ | |||
| #!/usr/bin/env python3 | |||
There was a problem hiding this comment.
suggestion: At 1636 LOC this exceeds the per-module bar the program-lib.sh decomposition set for this repo (~185-278 LOC, single-purpose, self-contained). The file already has clean cohesion seams — git helpers, the YAML-subset config reader, the Finding model, code-claim checks (paths/flags), metadata checks (enum/retired/versions/absence), release/changelog, and orchestration — that would split into importable units of roughly 300 LOC or less (the paths+flags cluster would itself split to get there).
One real constraint the shell refactor did not face: the tests load this file via importlib.util.spec_from_file_location and scripts/ is not a Python package, so sibling imports would need a package layout or sys.path handling, and agent-skills vendors scripts as a flat per-skill directory. That makes the split a deliberate refactor with tradeoffs rather than a mechanical move. Given that and the long review loop already behind this PR, I would take it as a dedicated follow-up rather than hold the merge. Not blocking.
| git -C "$VER" init -q | ||
| git -C "$VER" -c user.email=t@t -c user.name=t add -A | ||
| git -C "$VER" -c user.email=t@t -c user.name=t commit -qm "ver" | ||
| git -C "$VER" tag v1.0.0 |
There was a problem hiding this comment.
nit: git tag v1.0.0 is a lightweight tag with no -m. For a contributor whose global config sets tag.gpgsign=true (common), this fixture dies with fatal: no tag message? and aborts the whole suite locally — even though CI is green, because CI has no such config. The fixtures already isolate identity with -c user.email/-c user.name; adding -c tag.gpgsign=false to the tag call(s) closes the same gap. Non-blocking.
Summary
doc-drift-check.py(pinned-ref checker), scanner, fixer, and precheck.publishedAtplus Unreleased drafts from merged PRs.doc-driftintoKNOWN_PROGRAMS,repoman-run.sh, and the automation-health dashboard; shellcheck + fixture tests in CI.Engine source of truth for rossoctl/repoman#8. Companion vendor PR will follow in
rossoctl/repomanafter this merges.Follow-ups
Out of scope here, tracked separately (both from the review at #pullrequestreview-5742023444; neither blocks this PR):
doc-drift-scanner.shhas no behavioural test: carry-forward, rollup open/close and ledger are untestedghcalls per repoTest plan
bash tests/test-doc-drift-check.shbash tests/test-doc-drift-precheck.shshellcheck --severity=warning scripts/doc-drift-scanner.sh scripts/doc-drift-fixer.sh scripts/doc-drift-precheck.shshellcheck (doc-drift)+ suite entries for the new testsAssisted-By: Claude Code