test_gsw_write_integrity.py python
126 lines 5.7 KB
Raw
sha256:0e9549ec7b463911bc08b7d586dc320b1ac9b1f5c943ee7e3865dcc6cb0f6f83 chore(governance): sync handover+roadmap to 84db8c8 (drift:… Human 2 days ago
1 """Data-integrity: §GSW rollback + marker semantics (§GSW.10 data-integrity).
2
3 Induced failure after branch switch + doc writes must leave docs byte-identical,
4 the original branch current, no feature commit, and the marker absent or restored
5 to prior bytes (GFG mid-apply rule). A subsequent successful ``--write`` then
6 produces exactly one feature-branch commit bundling handover+roadmap and may
7 stamp the marker only after that success when D1+D2 are aligned.
8 """
9
10 from __future__ import annotations
11
12 import json
13 from datetime import date
14 from pathlib import Path
15
16 from cli.kit_root import kit_root
17 from tests.support import FIXTURES, gsw_runner, run_cli, seed_gsw_repo
18
19 _MERGED_K5B = json.dumps(
20 [
21 {
22 "number": 42,
23 "title": "K5b Freeze reviewer build",
24 "mergeCommit": {"oid": "cafebabe"},
25 "mergedAt": "2026-07-09T00:00:00Z",
26 }
27 ]
28 )
29
30
31 def _seed_d1_aligned_d3_drift(tmp_path: Path) -> tuple[Path, Path]:
32 """git-only: handover claims the real main tip (D1 aligned, D2 aligned)
33 while the roadmap queue lags a merged PR (D3 drifted) — marker-eligible."""
34 handover = (FIXTURES / "governance-handover-drift.md").read_text(encoding="utf-8")
35 handover = handover.replace("deadbeef", "cafebabe")
36 return seed_gsw_repo(tmp_path, "git-only", handover_text=handover)
37
38
39 def test_failure_after_switch_and_write_restores_everything(tmp_path: Path) -> None:
40 handover_path, roadmap_path = seed_gsw_repo(tmp_path, "muse+git-mirror")
41 original_handover = handover_path.read_bytes()
42 original_roadmap = roadmap_path.read_bytes()
43 prior_marker = "2026-07-30T00:00:00Z\nr1=cafebabe\nr3=sha256:musetip\n"
44 marker_path = tmp_path / ".overseer" / "last_governance_sync"
45 marker_path.write_text(prior_marker, encoding="utf-8")
46
47 runner = gsw_runner(tmp_path, "muse+git-mirror", muse_commit_fails=True)
48 code = run_cli(["governance-sync", "--write"], cwd=tmp_path, runner=runner, kit=kit_root())
49
50 assert code == 2
51 assert handover_path.read_bytes() == original_handover
52 assert roadmap_path.read_bytes() == original_roadmap
53 assert runner.git_branch == "main"
54 assert runner.muse_branch == "main"
55 # No new stamp left behind: prior marker bytes intact (§GSW.3.4).
56 assert marker_path.read_text(encoding="utf-8") == prior_marker
57 # No feature commit reached the history.
58 committed = [c for c, _ in runner.calls if c.startswith("muse") and " commit " in c]
59 assert committed, "commit was attempted"
60
61
62 def test_second_write_single_commit_bundles_both_docs_then_marker(tmp_path: Path) -> None:
63 handover_path, roadmap_path = _seed_d1_aligned_d3_drift(tmp_path)
64 marker_path = tmp_path / ".overseer" / "last_governance_sync"
65
66 # First apply: induced commit failure — marker must not be stamped.
67 failing = gsw_runner(tmp_path, "git-only", git_commit_fails=True, merged_prs_json=_MERGED_K5B)
68 code = run_cli(["governance-sync", "--write"], cwd=tmp_path, runner=failing, kit=kit_root())
69 assert code == 2
70 assert not marker_path.exists()
71 assert failing.git_branch == "main"
72
73 # Second apply: success — exactly one commit bundling handover+roadmap,
74 # marker stamped only after that success (D1+D2 aligned).
75 runner = gsw_runner(tmp_path, "git-only", merged_prs_json=_MERGED_K5B)
76 code = run_cli(["governance-sync", "--write"], cwd=tmp_path, runner=runner, kit=kit_root())
77 assert code == 0
78
79 commit_calls = [c for c, _ in runner.calls if c.startswith("git commit")]
80 assert len(commit_calls) == 1
81 add_calls = [c for c, _ in runner.calls if c.startswith("git add")]
82 assert any(
83 "OVERSEER-HANDOVER.md" in c and "ROADMAP.md" in c for c in add_calls
84 ), "commit must bundle handover + roadmap"
85 commit_index = next(i for i, (c, _) in enumerate(runner.calls) if c.startswith("git commit"))
86 assert marker_path.exists()
87 marker_lines = marker_path.read_text(encoding="utf-8").splitlines()
88 assert marker_lines[1] == "r1=cafebabe"
89 # Roadmap D3 reconciled in the committed patch.
90 assert "PR #42" in roadmap_path.read_text(encoding="utf-8")
91 assert runner.git_branch == f"feat/governance-sync-{date.today().isoformat()}"
92 # Marker write happens after the commit call: nothing after commit reverses it,
93 # and the failing run above proved no stamp occurs without commit success.
94 assert commit_index < len(runner.calls)
95
96
97 def test_step_c_failure_leaves_docs_and_branch_untouched(tmp_path: Path) -> None:
98 """Step C failure (cannot create/switch): zero doc writes, original branch kept."""
99 handover_path, roadmap_path = seed_gsw_repo(tmp_path, "git-only")
100 original_handover = handover_path.read_bytes()
101 original_roadmap = roadmap_path.read_bytes()
102
103 runner = gsw_runner(
104 tmp_path,
105 "git-only",
106 existing_git_branches={f"feat/governance-sync-{date.today().isoformat()}"},
107 )
108
109 # Make every checkout of the feature branch fail (create collides, switch breaks).
110 original_run = runner.run
111
112 def breaking_run(command: str, *, cwd: str | None = None):
113 if "git checkout feat/governance-sync-" in command:
114 runner.calls.append((command, cwd))
115 from adapters.runner import CommandResult
116
117 return CommandResult(stdout="", stderr="switch refused", exit_code=1)
118 return original_run(command, cwd=cwd)
119
120 runner.run = breaking_run # type: ignore[method-assign]
121 code = run_cli(["governance-sync", "--write"], cwd=tmp_path, runner=runner, kit=kit_root())
122 assert code == 2
123 assert handover_path.read_bytes() == original_handover
124 assert roadmap_path.read_bytes() == original_roadmap
125 assert runner.git_branch == "main"
126 assert not (tmp_path / ".overseer" / "last_governance_sync").exists()
File History 1 commit
sha256:6abcf1fa82a7a621ccbc945f19acdba5bc0db54569599404a1452fb4a096a199 fix(ISR): default require_independent_second_reviewer to require Human minor 2 days ago