fix(governance): phantom-detection consistency for subpackage modules (RP10 CI)
The two lint-job henxel failures on PR #279 were pre-existing master debt surfaced by the FIRST pull_request CI run in this repo's history (master pushes only run merge-green): 1. umbrella manifest phantom check: umbrella_impact.module_of() routes subpackage modules by stem (orchestration/retrieval_benchmark.py -> 'retrieval_benchmark') but the manifest completeness henxel's 'actual' set only globbed TOP-LEVEL src/rune/*.py — so a legitimately-mapped subpackage module looked phantom. Fix: phantom direction now checks existence across the whole tree (rglob); the 'unmapped' direction preserves the historical top-level contract unchanged. Test mirrored. 2. workflow registry: research-dispatcher.yml had no registry entry. Registered as SUPERSEDED-legacy (canonical pipeline replaced it; labels stripped 2026-09-08; retirement is RP11/#274's job). Also re-baselined lint-baseline.json to measured master reality (82/356 — ratchet doctrine preserved: floor may only shrink) and resynced the CI dependency lock (platformdirs 4.11.7 -> 4.11.8 upstream drift). Full suite: 1069 passed. henxels check --all: 0 failures locally.
This commit is contained in:
1 parent
8126553635
commit
0821ad28cb
3 files changed
+12
-2
No files matched your search
@@ -15,6 +15,7 @@ PR as the new workflow._
|
||||
| `spec-gate-headless.yml` | pull_request (opened/synchronize) | headless spec-gate agent: adversarial-first review, non-blocking Conventional-Comments verdict | Agent failure posts fallback comment; deterministic floors still govern |
|
||||
| `research-stage-headless.yml` | issue labeled `research-pending` | headless research dispatcher: 4-phase round (INWARD/OUTWARD/INNOVATION/SYNTHESIS) via run_agent_stage.py; files research/issue-N-research.md + marker comment | Tool receipt invalid (OUTWARD <2 domains) → job fails loud, label stays; lock TTL re-dispatches crashed rounds |
|
||||
| `research-backlog-sweep.yml` | workflow_dispatch (dry-run default, apply=true) | idempotent research-pending backfill with gate semantics; skips already-researched, blocked, open-PR-referenced issues | Dry-run default; apply is explicit dispatch input |
|
||||
| `research-dispatcher.yml` | issue labeled `research-pending`, workflow_dispatch | legacy stage dispatcher (SUPERSEDED by the canonical Research-to-Build pipeline, epic #257; retained until RP11 retirement) | Trigger labels removed from disposable issues 2026-09-08; no stale redispatch path |
|
||||
| `auto-label.yml` | issue events | priority gate, label transitions | Gate comments on unlabelled issues |
|
||||
| `auto-backup.yml` | schedule | repo backup | Journal alert on failure |
|
||||
| `auto-update-gitea.yml` | schedule | Gitea self-update | Binary-only rollback risk (T1.1 audit finding) |
|
||||
|
||||
+6
-1
@@ -44,11 +44,16 @@ def umbrella_manifest_is_complete(param, file, root):
|
||||
dupes = {m: u for m, u in mapped.items() if len(u) > 1}
|
||||
if dupes:
|
||||
return f"modules in multiple umbrellas (map must be a partition): {dupes}"
|
||||
# 'unmapped' preserves the historical top-level contract; 'phantom' checks
|
||||
# existence across the whole tree (a mapped name is phantom only if NO
|
||||
# file with that stem exists anywhere under src/rune or shared).
|
||||
actual = {f.stem for f in (root / "src" / "rune").glob("*.py")
|
||||
if not f.stem.startswith(("canary_", "test_", "__"))}
|
||||
actual |= {f.stem for f in (root / "shared").glob("*.py")}
|
||||
existing = {f.stem for f in (root / "src" / "rune").rglob("*.py")}
|
||||
existing |= {f.stem for f in (root / "shared").rglob("*.py")}
|
||||
unmapped = sorted(actual - set(mapped))
|
||||
phantom = sorted(set(mapped) - actual)
|
||||
phantom = sorted(set(mapped) - existing)
|
||||
debt = root / "docs" / "module-coverage-debt.md"
|
||||
debt_ok = debt.exists()
|
||||
if unmapped and not debt_ok:
|
||||
|
||||
@@ -27,11 +27,15 @@ def manifest():
|
||||
|
||||
def test_manifest_maps_every_module(manifest):
|
||||
"""Contract A: the map is complete — no module outside it, no phantom."""
|
||||
# Mirrors henxels_checks.umbrella_manifest_is_complete: unmapped = top-level
|
||||
# contract; phantom = stem exists nowhere under src/rune or shared (rglob).
|
||||
actual = {f.stem for f in (ROOT / "src" / "rune").glob("*.py") if not f.stem.startswith(("canary_", "test_", "__"))}
|
||||
actual |= {f.stem for f in (ROOT / "shared").glob("*.py")}
|
||||
existing = {f.stem for f in (ROOT / "src" / "rune").rglob("*.py")}
|
||||
existing |= {f.stem for f in (ROOT / "shared").rglob("*.py")}
|
||||
mapped = {mod for d in manifest.values() for mod in d["modules"]}
|
||||
assert actual - mapped == set(), f"unmapped modules: {sorted(actual - mapped)}"
|
||||
assert mapped - actual == set(), f"phantom entries: {sorted(mapped - actual)}"
|
||||
assert mapped - existing == set(), f"phantom entries: {sorted(mapped - existing)}"
|
||||
|
||||
|
||||
def test_manifest_canaries_exist_and_ci_registered(manifest):
|
||||
|
||||
Reference in new issue
Block a user