test_governance_drift.py
python
sha256:a78e7e5a8740e03315f325d19edeb3aa1b306b3337d04abbaa9a9e0f3bbeb7a1
docs: MuseHub-first before ISR #74 — staging solidify NEXT
Human
10 hours ago
| 1 | """Unit tests for governance drift detection (§8 unit tier).""" |
| 2 | |
| 3 | from __future__ import annotations |
| 4 | |
| 5 | from dataclasses import replace |
| 6 | |
| 7 | from tools.governance_hygiene.drift import detect_drift |
| 8 | from tools.governance_hygiene.parse import pr_matches_row |
| 9 | from tools.governance_hygiene.types import MergedPullRequest, QueueRow, VerifiedReads |
| 10 | |
| 11 | |
| 12 | def _reads(github_main: str = "cafebabe") -> VerifiedReads: |
| 13 | return VerifiedReads( |
| 14 | regime="git-only", |
| 15 | r1_github_main_sha=github_main, |
| 16 | r1_command="git rev-parse origin/main", |
| 17 | r2_anchor_sha=github_main, |
| 18 | r2_source="origin/main", |
| 19 | r3_canonical_main_sha=github_main, |
| 20 | r3_command="git rev-parse origin/main", |
| 21 | r4_merged_prs=(), |
| 22 | r5_branch="main", |
| 23 | r5_dirty=False, |
| 24 | r5_regime="git-only", |
| 25 | ) |
| 26 | |
| 27 | |
| 28 | def test_detect_drift_d1_drifted_d2_d3_aligned() -> None: |
| 29 | handover = "| **main HEAD** | `deadbeef` |" |
| 30 | roadmap = "| **9A-5 Governance Hygiene Agent** | Auto | **TODO** | x |" |
| 31 | drift = detect_drift(_reads("cafebabe"), handover, roadmap) |
| 32 | assert drift.d1_handover_vs_git == "drifted" |
| 33 | assert drift.d2_anchor_vs_canonical == "aligned" |
| 34 | assert drift.d3_queue_vs_merged == "aligned" |
| 35 | |
| 36 | |
| 37 | def test_detect_drift_fully_aligned() -> None: |
| 38 | handover = "| **main HEAD** | `cafebabe` |" |
| 39 | roadmap = "| **9A-5 Governance Hygiene Agent** | Auto | **TODO** | x |" |
| 40 | drift = detect_drift(_reads(), handover, roadmap) |
| 41 | assert drift.fully_aligned is True |
| 42 | |
| 43 | |
| 44 | def _row(label: str, status: str = "**NEXT**") -> QueueRow: |
| 45 | return QueueRow(phase_label=label, model="Thinking", status=status, deliverable="x", raw_line="|") |
| 46 | |
| 47 | |
| 48 | _GSW_LAND_B_TITLE = "Mirror: mirror: GSW land-b docs sync + muse code add staging fix" |
| 49 | _GSW_CLOSEOUT_TITLE = ( |
| 50 | "Mirror: mirror: GSW closeout — queue PLS post-land sync + pr_matches_row slice-token fix" |
| 51 | ) |
| 52 | |
| 53 | |
| 54 | def test_pr_match_rejects_generic_fragment_overlap() -> None: |
| 55 | """Live GSW land-b regression: a mirror PR title sharing only boilerplate |
| 56 | words (land / sync / main / post) must never stamp an unrelated open row.""" |
| 57 | assert pr_matches_row(_GSW_LAND_B_TITLE, _row("**PLS-a Post-land main sync freeze**")) is False |
| 58 | assert pr_matches_row(_GSW_LAND_B_TITLE, _row("**PLS-b Post-land main sync build**")) is False |
| 59 | |
| 60 | |
| 61 | def test_pr_match_rejects_bare_prefix_when_compound_id_exists() -> None: |
| 62 | """Live PR #55 closeout: title 'queue PLS …' must not stamp open PLS-a/PLS-b. |
| 63 | Compound slice IDs (PLS-a) are required; bare PLS is not enough.""" |
| 64 | assert pr_matches_row(_GSW_CLOSEOUT_TITLE, _row("**PLS-a Post-land main sync freeze**")) is False |
| 65 | assert pr_matches_row(_GSW_CLOSEOUT_TITLE, _row("**PLS-b Post-land main sync build**")) is False |
| 66 | assert pr_matches_row( |
| 67 | "feat(PLS-a): freeze post-land main sync", |
| 68 | _row("**PLS-a Post-land main sync freeze**"), |
| 69 | ) is True |
| 70 | |
| 71 | |
| 72 | def test_pr_match_accepts_compound_slice_id() -> None: |
| 73 | assert pr_matches_row( |
| 74 | "mirror: GSW-FIX governance-sync write-path", |
| 75 | _row("**GSW-FIX → main**"), |
| 76 | ) is True |
| 77 | # Bare GSW without the compound GSW-FIX is not enough when the label has one. |
| 78 | assert pr_matches_row(_GSW_LAND_B_TITLE, _row("**GSW-FIX → main**")) is False |
| 79 | |
| 80 | |
| 81 | def test_pr_match_accepts_non_compound_slice_token() -> None: |
| 82 | """Labels without hyphenated IDs still match on a distinctive token (PMHF).""" |
| 83 | assert pr_matches_row( |
| 84 | "Mirror: PMHF post-merge handover freshness land closeout", |
| 85 | _row("**PMHF → main**"), |
| 86 | ) is True |
| 87 | |
| 88 | |
| 89 | def test_pr_match_rejects_visibility_checklist_overlap() -> None: |
| 90 | """Live ONS land-b regression: Contributor PR #63 title sharing only |
| 91 | English product words (visibility / checklist) must never stamp the |
| 92 | open Tier-3 row ``Public repository visibility flip`` DONE.""" |
| 93 | contributor_title = ( |
| 94 | "Mirror: mirror: Contributor prep — CONTRIBUTING, laundry purge, " |
| 95 | "visibility checklist" |
| 96 | ) |
| 97 | assert ( |
| 98 | pr_matches_row( |
| 99 | contributor_title, |
| 100 | _row("**Public repository visibility flip**"), |
| 101 | ) |
| 102 | is False |
| 103 | ) |
| 104 | # Full-label substring still matches when the PR is truly about the flip. |
| 105 | assert ( |
| 106 | pr_matches_row( |
| 107 | "Mirror: Public repository visibility flip — Settings private→public", |
| 108 | _row("**Public repository visibility flip**"), |
| 109 | ) |
| 110 | is True |
| 111 | ) |
| 112 | |
| 113 | |
| 114 | def test_pr_match_requires_word_boundary() -> None: |
| 115 | """Slice IDs must match as whole words — `PLS` inside `pulse` is no match.""" |
| 116 | assert pr_matches_row("chore: pulse metrics cleanup", _row("**PLS-a Post-land main sync freeze**")) is False |
| 117 | |
| 118 | |
| 119 | def test_pr_match_full_label_substring_still_matches() -> None: |
| 120 | assert pr_matches_row( |
| 121 | "feat: K10 Honesty module ledger gates", |
| 122 | _row("**K10 Honesty module**"), |
| 123 | ) is True |
| 124 | |
| 125 | |
| 126 | def test_detect_drift_d3_ignores_mention_of_queued_slice() -> None: |
| 127 | """D3 stays aligned when a merged PR only *mentions* a still-open slice.""" |
| 128 | handover = "| **main HEAD** | `cafebabe` |" |
| 129 | roadmap = ( |
| 130 | "| **PLS-a Post-land main sync freeze** | Thinking | **NEXT** | x |\n" |
| 131 | "| **PLS-b Post-land main sync build** | Auto | **QUEUED** | x |" |
| 132 | ) |
| 133 | reads = replace( |
| 134 | _reads(), |
| 135 | r4_merged_prs=( |
| 136 | MergedPullRequest( |
| 137 | number=55, |
| 138 | title=_GSW_CLOSEOUT_TITLE, |
| 139 | merge_commit_sha="4650171" + "0" * 33, |
| 140 | merged_at="2026-07-31T16:45:00Z", |
| 141 | ), |
| 142 | ), |
| 143 | ) |
| 144 | drift = detect_drift(reads, handover, roadmap) |
| 145 | assert drift.d3_queue_vs_merged == "aligned" |
File History
2 commits
sha256:a78e7e5a8740e03315f325d19edeb3aa1b306b3337d04abbaa9a9e0f3bbeb7a1
docs: MuseHub-first before ISR #74 — staging solidify NEXT
Human
10 hours ago
sha256:4671b7f787ddbe63ced31c895b688c77ab495653b65a730b423329f26b3c1439
feat: K1-P1 complete — agent provenance, build-verification…
Sonnet 4.6
patch
52 days ago