Files
OpenFlare/.auto/check_gate_weaken.py
T
ryan 5971e2a9ed chore(autoresearch): re-baseline harness on pinned real-risk yardstick
The committed golangci gate now reports 0 issues, so the previous
lint_issues metric was saturated and could no longer measure progress.
Measure debt against an immutable .auto/lint.ref.yaml snapshot that adds
analyzers for genuine defects (panics, error unwrapping, dead stores,
missing enum cases, method ordering, suppression hygiene) while excluding
cosmetic churn (tagliatelle, wrapcheck). Guard enforces build, vet, tests,
the Cordis architecture gate, and anti-cheat floors: the yardstick cannot
be edited, the project gate may only be strengthened, nolint directives may
only shrink, and no test may disappear.
2026-08-29 07:52:03 +08:00

118 lines
4.1 KiB
Python

#!/usr/bin/env python3
"""Anti-cheat: prove .golangci.yml was only ever strengthened, never weakened.
Compares the live gate against the immutable snapshot taken at run start.
Exits non-zero with a reason if any hardening rule is violated.
"""
import os
import sys
import yaml
ROOT = os.path.dirname(os.path.dirname(os.path.abspath(__file__)))
BASELINE = os.path.join(ROOT, ".auto", "gate.baseline.yml")
LIVE = os.path.join(ROOT, ".golangci.yml")
# threshold-like knobs: (path, direction) where direction "max" means the value
# is an upper bound (smaller == stricter), "min" means a lower bound.
STRICTNESS = [
(("linters", "settings", "dupl", "threshold"), "max"),
(("linters", "settings", "cyclop", "max-complexity"), "max"),
(("linters", "settings", "cyclop", "package-average"), "max"),
(("linters", "settings", "nestif", "min-complexity"), "min"),
(("linters", "settings", "funlen", "lines"), "max"),
(("linters", "settings", "funlen", "statements"), "max"),
(("linters", "settings", "gocyclo", "min-complexity"), "min"),
(("linters", "settings", "lll", "line-length"), "max"),
]
def load(path):
with open(path, encoding="utf-8") as fh:
return yaml.safe_load(fh) or {}
def dig(doc, path):
node = doc
for key in path:
if not isinstance(node, dict) or key not in node:
return None
node = node[key]
return node
def enabled_linters(doc):
lint = doc.get("linters") or {}
if lint.get("enable-presets"):
return None # preset based; fall back to "any removal is suspicious"
return set(lint.get("enable") or [])
def main():
try:
base, live = load(BASELINE), load(LIVE)
except OSError as exc:
print(f"gate snapshot unreadable: {exc}")
return 1
except yaml.YAMLError as exc:
print(f".golangci.yml is not parseable: {exc}")
return 1
problems = []
base_lint, live_lint = base.get("linters") or {}, live.get("linters") or {}
if (base_lint.get("default") or "none") != (live_lint.get("default") or "none"):
problems.append("linters.default changed")
base_set, live_set = enabled_linters(base), enabled_linters(live)
if base_set is None or live_set is None:
if set((base.get("linters") or {}).get("enable-presets") or []) - set(
(live.get("linters") or {}).get("enable-presets") or []
):
problems.append("an enable-preset was removed")
elif dropped := base_set - live_set:
problems.append(f"linters disabled: {sorted(dropped)}")
for path, direction in STRICTNESS:
old, new = dig(base, path), dig(live, path)
if old is None or new is None:
continue
try:
old_f, new_f = float(old), float(new)
except (TypeError, ValueError):
continue
if direction == "max" and new_f > old_f:
problems.append(f"{'.'.join(path)} loosened {old} -> {new}")
if direction == "min" and new_f < old_f:
problems.append(f"{'.'.join(path)} loosened {old} -> {new}")
base_mnd = set(dig(base, ("linters", "settings", "mnd", "checks")) or [])
live_mnd = set(dig(live, ("linters", "settings", "mnd", "checks")) or [])
if base_mnd - live_mnd:
problems.append(f"mnd checks dropped: {sorted(base_mnd - live_mnd)}")
issues_live = live.get("issues") or {}
for key in ("exclude-rules", "exclude-patterns"):
if issues_live.get(key) and not (base.get("issues") or {}).get(key):
problems.append(f"issues.{key} added (suppresses reporting)")
for key in ("max-issues-per-linter", "max-same-issues"):
old = (base.get("issues") or {}).get(key)
new = issues_live.get(key)
if old == 0 and new != 0:
problems.append(f"issues.{key} no longer 0 — findings would be truncated")
# Exclusions expressed through the newer 'linters.exclusions' block.
if (live_lint.get("exclusions") or {}) and not (base_lint.get("exclusions") or {}):
problems.append("linters.exclusions added")
if problems:
print("\n".join(f" - {p}" for p in problems))
return 1
return 0
if __name__ == "__main__":
sys.exit(main())