test_gsw_write_order.py python
125 lines 4.9 KB
Raw
sha256:0e9549ec7b463911bc08b7d586dc320b1ac9b1f5c943ee7e3865dcc6cb0f6f83 chore(governance): sync handover+roadmap to 84db8c8 (drift:… Human 2 days ago
1 """Integration tests for §GSW write order on all three regime fixtures.
2
3 Injected-runner ``--write`` per regime: the call/event log must show
4 realign (or skip) → branch ensure → doc writes → commit (§GSW.10
5 integration tier). Docs become dirty only after branch ensure.
6 """
7
8 from __future__ import annotations
9
10 from datetime import date
11 from pathlib import Path
12
13 from cli.kit_root import kit_root
14 from tests.support import gsw_runner, run_cli, seed_gsw_repo
15
16
17 def _feature_branch() -> str:
18 return f"feat/governance-sync-{date.today().isoformat()}"
19
20
21 def _events_for_write(tmp_path: Path, regime: str, monkeypatch, **runner_kwargs):
22 """Run ``governance-sync --write`` recording commands + doc writes in order."""
23 handover_path, roadmap_path = seed_gsw_repo(tmp_path, regime)
24 events: list = []
25
26 from tools.governance_hygiene import engine as engine_mod
27
28 real_write = engine_mod.atomic_write_text
29
30 def recording_write(path: Path, text: str) -> None:
31 events.append(("write", path.name))
32 real_write(path, text)
33
34 monkeypatch.setattr(engine_mod, "atomic_write_text", recording_write)
35
36 inner = gsw_runner(tmp_path, regime, **runner_kwargs)
37
38 class Wrapper:
39 def run(self, command: str, *, cwd: str | None = None):
40 events.append(("cmd", command))
41 return inner.run(command, cwd=cwd)
42
43 code = run_cli(
44 ["governance-sync", "--write"], cwd=tmp_path, runner=Wrapper(), kit=kit_root()
45 )
46 return code, events, inner, (handover_path, roadmap_path)
47
48
49 def _first_index(events, predicate) -> int:
50 return next(index for index, event in enumerate(events) if predicate(event))
51
52
53 def _doc_write_indexes(events, names) -> list[int]:
54 return [
55 index
56 for index, event in enumerate(events)
57 if event[0] == "write" and event[1] in names
58 ]
59
60
61 def test_git_only_write_order(tmp_path: Path, monkeypatch) -> None:
62 code, events, runner, _ = _events_for_write(tmp_path, "git-only", monkeypatch)
63 assert code == 0
64 ensure = _first_index(events, lambda e: e[0] == "cmd" and "git checkout -b" in e[1])
65 writes = _doc_write_indexes(events, {"OVERSEER-HANDOVER.md", "ROADMAP.md"})
66 commit = _first_index(events, lambda e: e[0] == "cmd" and e[1].startswith("git commit"))
67 assert ensure < min(writes) < max(writes) < commit
68 assert runner.git_branch == _feature_branch()
69 assert not any(e[0] == "cmd" and e[1].startswith("muse") for e in events)
70
71
72 def test_muse_only_write_order(tmp_path: Path, monkeypatch) -> None:
73 code, events, runner, docs = _events_for_write(tmp_path, "muse-only", monkeypatch)
74 assert code == 0
75 ensure = _first_index(
76 events, lambda e: e[0] == "cmd" and "checkout -b" in e[1] and e[1].startswith("muse")
77 )
78 writes = _doc_write_indexes(
79 events, {"MUSEHUB-OVERSEER-HANDOVER.md", "MUSEHUB-ROADMAP.md"}
80 )
81 commit = _first_index(
82 events, lambda e: e[0] == "cmd" and e[1].startswith("muse") and " commit " in e[1]
83 )
84 assert ensure < min(writes) < max(writes) < commit
85 assert runner.muse_branch == _feature_branch()
86 assert not any(e[0] == "cmd" and e[1].startswith(("git ", "gh ")) for e in events)
87
88
89 def test_muse_git_mirror_dual_head_before_writes(tmp_path: Path, monkeypatch) -> None:
90 """§GSW.5.1: both Muse HEAD and Git HEAD are on the feature branch before doc writes."""
91 code, events, runner, _ = _events_for_write(tmp_path, "muse+git-mirror", monkeypatch)
92 assert code == 0
93 muse_ensure = _first_index(
94 events, lambda e: e[0] == "cmd" and e[1].startswith("muse") and "checkout -b" in e[1]
95 )
96 git_ensure = _first_index(
97 events, lambda e: e[0] == "cmd" and "git checkout -b" in e[1]
98 )
99 writes = _doc_write_indexes(events, {"OVERSEER-HANDOVER.md", "ROADMAP.md"})
100 commit = _first_index(
101 events, lambda e: e[0] == "cmd" and e[1].startswith("muse") and " commit " in e[1]
102 )
103 assert muse_ensure < min(writes)
104 assert git_ensure < min(writes)
105 assert max(writes) < commit
106 assert runner.git_branch == _feature_branch()
107 assert runner.muse_branch == _feature_branch()
108
109
110 def test_muse_git_mirror_realign_runs_before_branch_ensure(tmp_path: Path, monkeypatch) -> None:
111 """§GSW.3.1 step B: realign apply (git-import) stays on the original branch —
112 strictly before the feature-branch ensure and all doc writes."""
113 # D2 drift: R3 reads a moved muse tip; post-apply verification re-reads the
114 # bridge-matching tip (sequenced rev-parse values).
115 code, events, runner, _ = _events_for_write(
116 tmp_path,
117 "muse+git-mirror",
118 monkeypatch,
119 muse_rev_parse_main_values=["sha256:moved", "sha256:musetip"],
120 )
121 assert code == 0
122 realign = _first_index(events, lambda e: e[0] == "cmd" and "git-import" in e[1])
123 ensure = _first_index(events, lambda e: e[0] == "cmd" and "checkout -b" in e[1])
124 writes = _doc_write_indexes(events, {"OVERSEER-HANDOVER.md", "ROADMAP.md"})
125 assert realign < ensure < min(writes)
File History 1 commit
sha256:6abcf1fa82a7a621ccbc945f19acdba5bc0db54569599404a1452fb4a096a199 fix(ISR): default require_independent_second_reviewer to require Human minor 2 days ago