check_workflow
The workflow validator. It is the oracle for the planning surfaces: the roadmap grammar, the
changelog order, the local links, the llms.txt index, and the text-hygiene checks. Run it
with python scripts/check_workflow.py.
Check a repository against the UBC-FRESH dev workflow surfaces.
This module is the oracle for the planning surfaces. It parses ROADMAP.md and
CHANGE_LOG.md, resolves local Markdown links and the docs/llms.txt index, and
scans the text that a commit would include for defects that must not reach a public
repository: control characters, byte-order marks, personal paths, and secret-shaped
strings. Outside a git repository, the scan walks the tree and honors the root
.gitignore. It uses only the standard library, so it runs in a fresh clone before
anything is installed.
Command-line usage:
python scripts/check_workflow.py [ROOT]
python scripts/check_workflow.py --files PATH [PATH ...]
The exit status is 0 when no errors are found. Warnings are printed, but they do not
fail the run. Finding codes are stable; see docs/agents/oracles-and-evidence.rst.
- class check_workflow.Diagnostic(severity: str, code: str, path: str, line: int, message: str)[source]
Bases:
objectOne finding: its severity, its stable code, its location, and its message.
- severity
Either
"error"or"warning".- Type:
str
- code
Stable finding code, such as
ROADMAP-STATUS.- Type:
str
- path
Repository-relative path of the file that the finding concerns.
- Type:
str
- line
One-based line number, or
0for a whole-file finding.- Type:
int
- message
Explanation of what to change.
- Type:
str
- class check_workflow.Task(phase: int, number: int, checked: bool, line: int, issue: int | None, subtasks: list[tuple[int, bool]]=<factory>)[source]
Bases:
objectA task line in a phase section, together with its subtask lines.
- phase
The phase number that the task ID claims.
- Type:
int
- number
The task number within its phase.
- Type:
int
- checked
Whether the task checkbox is ticked.
- Type:
bool
- line
One-based line number of the task line.
- Type:
int
- issue
The child issue number, when the task line names one.
- Type:
int or None
- subtasks
One
(line number, checked)pair for each subtask line under the task.- Type:
list of tuple of (int, bool)
- class check_workflow.Phase(number: int, line: int, parent: str | None = None, branch: str | None = None, status: str | None = None, tasks: list[Task] = <factory>)[source]
Bases:
objectA
## Phase <n>:section, its header lines, and its tasks.- number
The phase number from the heading.
- Type:
int
- line
One-based line number of the heading.
- Type:
int
- parent
The
Parent issue:value, such as#1orTBD.- Type:
str or None
- branch
The
Branch:value, without backticks.- Type:
str or None
- status
The
Status:value.- Type:
str or None
- class check_workflow.IgnoreRule(pattern: Pattern[str], anchored: bool, directory_only: bool, negated: bool)[source]
Bases:
objectOne rule from the root
.gitignore, compiled for the file walk.- pattern
The compiled pattern that the candidate must match in full.
- Type:
re.Pattern[str]
- anchored
Truewhen the rule contains a slash, so it matches the path from the root.Falsewhen it matches the name of any path component.- Type:
bool
- directory_only
Truewhen the rule ends in a slash, so it applies to directories only.- Type:
bool
- negated
Truewhen the rule starts with!, which re-includes a path that an earlier rule ignores.- Type:
bool
- check_workflow.error(code: str, path: str, line: int, message: str) Diagnostic[source]
Build an error-severity finding.
- Parameters:
code (str) – Stable finding code, such as
LINK-BROKEN.path (str) – Repository-relative path that the finding concerns.
line (int) – One-based line number, or
0for a whole-file finding.message (str) – Explanation of what to change.
- Returns:
Diagnostic – A finding with severity
"error".
- check_workflow.warning(code: str, path: str, line: int, message: str) Diagnostic[source]
Build a warning-severity finding.
- Parameters:
code (str) – Stable finding code, such as
FILE-RECOMMENDED.path (str) – Repository-relative path that the finding concerns.
line (int) – One-based line number, or
0for a whole-file finding.message (str) – Explanation of what to change.
- Returns:
Diagnostic – A finding with severity
"warning".
- check_workflow.is_ignored(rel: str, *, is_dir: bool, rules: Sequence[IgnoreRule]) bool[source]
Report whether a path is ignored, the way git reports it.
A path under an ignored directory is ignored, even when a later rule re-includes it, because git does not re-include a file whose directory is excluded.
- Parameters:
rel (str) – The path, relative to the root, with forward slashes.
is_dir (bool) – Whether the path is a directory.
rules (sequence of IgnoreRule) – The rules from
load_ignore_rules(), in file order.
- Returns:
bool –
Truewhen the path, or one of the directories that contain it, is ignored.
- check_workflow.load_ignore_rules(root: Path) list[IgnoreRule][source]
Read the root
.gitignorerules, with a conservative rule for each unsupported negation.Only the root file is read. A
.gitignorein a subdirectory is not read, and neither is.git/info/exclude, because the walk has no index to consult.- Parameters:
root (Path) – Repository root, or the directory that stands in for one.
- Returns:
list of IgnoreRule – The rules in file order. The list is empty when the root has no
.gitignore.
- check_workflow.iter_text_files(root: Path) Iterator[Path][source]
Yield the UTF-8 text files that the scans should read.
The candidates are the files that git would commit, when the root is a repository top level. Otherwise they are the files under the root that its
.gitignoredoes not ignore. Theteststree, the validator itself, symbolic links, oversized files, and files that are not valid UTF-8 are skipped in both cases.- Parameters:
root (Path) – Repository root or directory to scan.
- Yields:
Path – Absolute path of each file that passes the filters, in sorted order.
- check_workflow.parse_roadmap(lines: list[str]) list[Phase][source]
Parse the phase sections of a roadmap into structured records.
Phase headers, status lines, tasks, and subtasks are recognized by the grammar in
docs/reference/workflow-model.rst. Lines outside a phase section are ignored.- Parameters:
lines (list of str) – The roadmap, one entry per line, without line endings.
- Returns:
list of Phase – One record for each
## Phase <n>:heading, in file order.
- check_workflow.parse_tracker(lines: list[str]) list[tuple[int, list[str], int]][source]
Find the rows of the Issue Tracker Map table.
- Parameters:
lines (list of str) – The roadmap, one entry per line.
- Returns:
list of tuple of (int, list of str, int) – For each row that starts with a phase ID: the phase number, the stripped table cells, and the one-based line number.
- check_workflow.check_roadmap(path: str, text: str) list[Diagnostic][source]
Check a roadmap’s phase grammar, phase statuses, and tracker map.
- Parameters:
path (str) – Name of the roadmap file, used in the findings.
text (str) – The full roadmap text.
- Returns:
list of Diagnostic – Every finding for the roadmap. The list is empty when the roadmap is consistent.
- check_workflow.check_changelog(path: str, text: str) list[Diagnostic][source]
Check that changelog entries are ISO dates in non-decreasing order.
- Parameters:
path (str) – Name of the changelog file, used in the findings.
text (str) – The full changelog text.
- Returns:
list of Diagnostic – Findings for malformed headings, out-of-order entries, and an empty changelog.
- check_workflow.check_required_files(root: Path) list[Diagnostic][source]
Check that the required files exist, and warn about recommended ones.
- Parameters:
root (Path) – Repository root to check.
- Returns:
list of Diagnostic –
FILE-MISSINGerrors for absent required files, andFILE-RECOMMENDEDwarnings for absent recommended files.
- check_workflow.check_markdown_links(root: Path) list[Diagnostic][source]
Check that every local link in a Markdown file points at an existing path.
- Parameters:
root (Path) – Repository root to check.
- Returns:
list of Diagnostic –
LINK-BROKENerrors for each local link whose target does not exist.
- check_workflow.check_llms(root: Path) list[Diagnostic][source]
Check that every link in
docs/llms.txtnames an existing docs page.A link that ends in
.htmlmaps to the.rstsource with the same stem. Any other link is resolved relative todocs/.- Parameters:
root (Path) – Repository root to check.
- Returns:
list of Diagnostic –
LLMS-BROKENerrors for each link without a matching page. The list is empty when the index is absent, becauseFILE-MISSINGreports that case.
- check_workflow.scan_text(rel: str, data: bytes) list[Diagnostic][source]
Scan one file for control characters, byte-order marks, and unsafe strings.
- Parameters:
rel (str) – Repository-relative path, used in the findings.
data (bytes) – The raw file contents, which must be valid UTF-8.
- Returns:
list of Diagnostic – One finding for each defect found. A line can produce more than one finding.
- check_workflow.scan_repository(root: Path) list[Diagnostic][source]
Run the text scans over every file that
iter_text_files()yields.- Parameters:
root (Path) – Repository root to scan.
- Returns:
list of Diagnostic – All text-hygiene findings for the repository.
- check_workflow.check_files(paths: list[Path]) list[Diagnostic][source]
Run only the text scans on the given files, such as issue body files.
- Parameters:
paths (list of Path) – Files to scan.
- Returns:
list of Diagnostic –
FILE-MISSINGfor absent files,ENCODINGfor files that are not valid UTF-8, and the text-hygiene findings for every other file.
- check_workflow.run(root: Path) list[Diagnostic][source]
Run every repository check against a root and return all findings.
- Parameters:
root (Path) – Repository root to check.
- Returns:
list of Diagnostic – The findings from every check, in a stable order.
- check_workflow.main(argv: list[str] | None = None) int[source]
Run the validator from the command line.
- Parameters:
argv (list of str or None, optional) – Arguments to parse.
Nonereadssys.argv.- Returns:
int –
0when no errors were found, and1otherwise.- Raises:
SystemExit – When the argument parser rejects the arguments, or the root is not a directory.