diff --git a/.github/refresh-reviewers/tests/test_inputs_docs.py b/.github/refresh-reviewers/tests/test_inputs_docs.py index 1659c471..65e359f3 100644 --- a/.github/refresh-reviewers/tests/test_inputs_docs.py +++ b/.github/refresh-reviewers/tests/test_inputs_docs.py @@ -15,12 +15,13 @@ * `on.workflow_call.inputs` in `.github/workflows/refresh-reviewers.yml`, and * the "Inputs" table in `docs/callers/refresh-reviewers.md`. -This is deliberately a TWO-set check. `.github/refresh-reviewers/README.md`'s -"Knob defaults (and why)" table is intentionally partial (it omits -`reviewer_config_path`, `map_exclude`, `extra_exclude_paths` and `workflows_ref`) -and uses combined-cell rows (`| `top_k` / `floor` |`), so the bare-name table -regex neither captures nor should capture it — including it would make this test -permanently red or vacuous. +The guide's Inputs table is checked for full set-equality in BOTH directions. +`.github/refresh-reviewers/README.md`'s "Knob defaults (and why)" table is +intentionally partial (it omits `reviewer_config_path`, `map_exclude`, +`extra_exclude_paths` and `workflows_ref`), so it is checked ONE way only — no +phantom knob — but its combined-cell rows (`| `top_k` / `floor` |`) ARE parsed, +so every knob it names is still policed. Set-equality there would be permanently +red, since the table omits many inputs by design. Deliberately parsed WITHOUT PyYAML, like the cursor-review model (test_workflow_inputs_docs.py): this repo is stdlib-only and CI installs no @@ -43,6 +44,7 @@ SETUP_GUIDE = os.path.normpath( os.path.join(HERE, "..", "..", "..", "docs", "callers", "refresh-reviewers.md") ) +README = os.path.normpath(os.path.join(HERE, "..", "README.md")) # An input declaration: the key alone on its 6-space line, directly under # ` inputs:`. Sub-keys of an input (description/type/default) are 8-space, @@ -51,6 +53,15 @@ # A table row's first cell: `| `name` | ...`. TABLE_KEY = re.compile(r"^\|\s*`([A-Za-z0-9_-]+)`\s*\|") HEADING = re.compile(r"^#{2,}\s") +# An input's `default:` / `required:` sub-keys — 8-space, one level under the +# 6-space input key — used to pin the guide's Default column to the real value. +INPUT_DEFAULT = re.compile(r"^ default:\s*(.*?)\s*$") +INPUT_REQUIRED = re.compile(r"^ required:\s*(\S+)\s*$") +# A `with:` mapping in an example caller (at any indent), and a mapping key +# directly under it. A placeholder scalar VALUE (``, a login) never +# starts a line, so MAPPING_KEY never mistakes one for an input. +WITH_LINE = re.compile(r"^(\s*)with:\s*$") +MAPPING_KEY = re.compile(r"^\s*([A-Za-z0-9_-]+):") def read_lines(path): @@ -61,6 +72,32 @@ def read_lines(path): return [line.rstrip("\r") for line in f.read().split("\n")] +def section_lines(lines, heading): + """Lines strictly under `heading` (a `## …` line), up to the next heading at + the same or a higher level. Lets a scan be scoped to one section instead of + the whole file, so an unrelated block elsewhere can't stand in for it.""" + out, in_section = [], False + for line in lines: + if line.strip() == heading: + in_section = True + continue + if in_section and HEADING.match(line): + break + if in_section: + out.append(line) + return out + + +def split_cells(line): + """Split a markdown table row into trimmed cells, honoring GFM's escaped + `\\|` (a literal pipe inside a cell) so a Default value that contains a pipe + — plausible for a regex-valued input — doesn't shift every later column and + make the Default comparison run against the wrong cell.""" + body = line.strip().strip("|") + cells = re.split(r"(?