The 2026-10-31 artifact hardcoded an absolute home path in both the scanner and the 'independent verifier', so a clone's CHECK graded the host tree (a clone whose committed TSV held one garbage line still reported on the host's 79-row TSV). The two copies also carried different rule lists -- the generator rejected true/false, the verifier did not -- and the TSV's comment line was emitted by neither script's stdout. - draft_compliance_rule.py: the bare-command rule, stated once, 11 cases - generator: paths from __file__, artifact excluded from its own population, deterministic output (comment + header + rows) - verifier: own re-derivation, both directions, grades the report's six counts, rc 0/1/2, --selftest one control + eight mutants + rule-agreement + no-host-path - CHECK: four legs (selftest, rederive, byte-identical regeneration, copy-parity) - report: measured numbers (79 drafts, 14/14/11, 68 failing, 3 non-bare)
71 lines
2.4 KiB
Python
71 lines
2.4 KiB
Python
#!/usr/bin/env python3
|
|
"""Generate the draft-compliance TSV for the .md drafts in this directory.
|
|
|
|
The lane directory is derived from THIS FILE's location, never from a host path,
|
|
so a clone grades its own tree. `--lane` exists for a fixture tree; `--out`
|
|
writes the TSV instead of stdout, and the output is deterministic: the same tree
|
|
produces the same bytes, comment line and header included.
|
|
|
|
The artifact itself (`draft-compliance-report-2026-10-31.md`) is NOT a row: the
|
|
report publishes the table, it is not a draft in it. That exclusion is what makes
|
|
the population 79 rather than 80 -- the 2026-10-31 report's prose said 79 while
|
|
its TSV carried 80 rows, and the missing row was the report counting itself.
|
|
|
|
Rule, columns and the artifact name live in `draft_compliance_rule.py`; the
|
|
verifier re-derives this table from the files with its own code.
|
|
"""
|
|
import argparse
|
|
import os
|
|
import sys
|
|
|
|
HERE = os.path.dirname(os.path.abspath(__file__))
|
|
sys.path.insert(0, HERE)
|
|
|
|
import draft_compliance_rule as rule # noqa: E402
|
|
|
|
|
|
def scan(lane):
|
|
rows = []
|
|
for name in sorted(os.listdir(lane)):
|
|
if not name.endswith(".md") or name == rule.ARTIFACT_MD:
|
|
continue
|
|
path = os.path.join(lane, name)
|
|
if not os.path.isfile(path):
|
|
continue
|
|
with open(path, "r", encoding="utf-8", errors="replace") as f:
|
|
text = f.read()
|
|
rows.append(rule.row_for(name, text))
|
|
return rows
|
|
|
|
|
|
def main(argv=None):
|
|
ap = argparse.ArgumentParser(
|
|
description="Generate the draft-compliance TSV for this lane's .md drafts.")
|
|
ap.add_argument("--lane", default=HERE,
|
|
help="directory of .md drafts (default: this file's dir)")
|
|
ap.add_argument("--out", default=None,
|
|
help="write the TSV here instead of stdout")
|
|
a = ap.parse_args(argv)
|
|
|
|
if not os.path.isdir(a.lane):
|
|
print("cannot read lane directory: %s" % a.lane, file=sys.stderr)
|
|
return 2
|
|
|
|
rows = scan(a.lane)
|
|
lines = ["# measured %d markdown files" % len(rows),
|
|
"\t".join(rule.COLUMNS)]
|
|
lines.extend("\t".join(r) for r in rows)
|
|
body = "\n".join(lines) + "\n"
|
|
|
|
if a.out:
|
|
with open(a.out, "w", encoding="utf-8", newline="\n") as f:
|
|
f.write(body)
|
|
print("wrote %s (%d rows)" % (a.out, len(rows)), file=sys.stderr)
|
|
else:
|
|
sys.stdout.write(body)
|
|
return 0
|
|
|
|
|
|
if __name__ == "__main__":
|
|
raise SystemExit(main())
|