test_gsb_reconcile_write.py
python
sha256:0e9549ec7b463911bc08b7d586dc320b1ac9b1f5c943ee7e3865dcc6cb0f6f83
chore(governance): sync handover+roadmap to 84db8c8 (drift:…
Human
9 hours ago
| 1 | """Integration: injected-runner ``--write`` through §GSB C0 reconcile (§GSB.8). |
| 2 | |
| 3 | Fixture 1 (per regime): existing ``feat/governance-sync-<today>`` behind |
| 4 | ``T_target`` → FF then successful ensure, plan/push under the original dated |
| 5 | name. Fixture 2: diverged tip → uniquified ``-2`` used for commit / push / |
| 6 | ``pr_url``. Fixture 3 (``muse+git-mirror``): Muse HEAD already stranded on |
| 7 | the stale dated branch while Git HEAD is on advanced main → C0 still |
| 8 | reconciles both histories so C1 does not exit ``2``. |
| 9 | """ |
| 10 | |
| 11 | from __future__ import annotations |
| 12 | |
| 13 | import io |
| 14 | from contextlib import redirect_stdout |
| 15 | from datetime import date |
| 16 | from pathlib import Path |
| 17 | |
| 18 | from cli.kit_root import kit_root |
| 19 | from tests.support import gsw_runner, run_cli, seed_gsw_repo |
| 20 | |
| 21 | |
| 22 | def _feature_branch() -> str: |
| 23 | return f"feat/governance-sync-{date.today().isoformat()}" |
| 24 | |
| 25 | |
| 26 | def test_git_only_existing_branch_behind_ff_then_ensure(tmp_path: Path) -> None: |
| 27 | branch = _feature_branch() |
| 28 | handover_path, _ = seed_gsw_repo(tmp_path, "git-only") |
| 29 | runner = gsw_runner( |
| 30 | tmp_path, |
| 31 | "git-only", |
| 32 | existing_git_branches={branch}, |
| 33 | git_tips={branch: "stale1"}, # main defaults to feedface |
| 34 | git_ancestors={"feedface": {"stale1"}}, |
| 35 | ) |
| 36 | code = run_cli(["governance-sync", "--write"], cwd=tmp_path, runner=runner, kit=kit_root()) |
| 37 | assert code == 0 |
| 38 | assert runner.git_branch == branch |
| 39 | # FF happened via an ancestor-validated tip move, then one sync commit. |
| 40 | assert any(c.startswith("git branch -f ") for c, _ in runner.calls) |
| 41 | assert runner.git_commit_count == 1 |
| 42 | tip = runner.git_tips[branch] |
| 43 | assert "feedface" in runner.git_ancestors[tip] |
| 44 | # Push targets the original dated name — no uniquify on the FF path. |
| 45 | push_calls = [c for c, _ in runner.calls if c.startswith("git push")] |
| 46 | assert push_calls and all(branch in c and f"{branch}-" not in c for c in push_calls) |
| 47 | assert not any(c.startswith("muse") for c, _ in runner.calls) |
| 48 | assert "cafebabe" in handover_path.read_text(encoding="utf-8") |
| 49 | |
| 50 | |
| 51 | def test_muse_only_existing_branch_behind_ff_then_ensure(tmp_path: Path) -> None: |
| 52 | branch = _feature_branch() |
| 53 | handover_path, _ = seed_gsw_repo(tmp_path, "muse-only") |
| 54 | original = handover_path.read_text(encoding="utf-8") |
| 55 | runner = gsw_runner( |
| 56 | tmp_path, |
| 57 | "muse-only", |
| 58 | existing_muse_branches={branch}, |
| 59 | muse_tips={branch: "sha256:stale"}, # main defaults to sha256:musetip |
| 60 | muse_ancestors={"sha256:musetip": {"sha256:stale"}}, |
| 61 | ) |
| 62 | code = run_cli(["governance-sync", "--write"], cwd=tmp_path, runner=runner, kit=kit_root()) |
| 63 | assert code == 0 |
| 64 | assert runner.muse_branch == branch |
| 65 | assert any( |
| 66 | c.startswith("muse") and f" update-ref {branch} sha256:musetip" in c |
| 67 | for c, _ in runner.calls |
| 68 | ) |
| 69 | assert runner.muse_commit_count == 1 |
| 70 | tip = runner.muse_tips[branch] |
| 71 | assert "sha256:musetip" in runner.muse_ancestors[tip] |
| 72 | assert handover_path.read_text(encoding="utf-8") != original |
| 73 | # §GSB.8 least privilege: muse-only never invokes git/gh. |
| 74 | assert not any(c.startswith(("git ", "gh ")) for c, _ in runner.calls) |
| 75 | |
| 76 | |
| 77 | def test_muse_git_mirror_existing_branch_behind_ff_both_histories(tmp_path: Path) -> None: |
| 78 | branch = _feature_branch() |
| 79 | seed_gsw_repo(tmp_path, "muse+git-mirror") |
| 80 | runner = gsw_runner( |
| 81 | tmp_path, |
| 82 | "muse+git-mirror", |
| 83 | existing_git_branches={branch}, |
| 84 | existing_muse_branches={branch}, |
| 85 | git_tips={branch: "gitstale"}, |
| 86 | muse_tips={branch: "sha256:stale"}, |
| 87 | git_ancestors={"feedface": {"gitstale"}}, |
| 88 | muse_ancestors={"sha256:musetip": {"sha256:stale"}}, |
| 89 | ) |
| 90 | code = run_cli(["governance-sync", "--write"], cwd=tmp_path, runner=runner, kit=kit_root()) |
| 91 | assert code == 0 |
| 92 | assert runner.git_branch == branch |
| 93 | assert runner.muse_branch == branch |
| 94 | assert any(c.startswith("git branch -f ") for c, _ in runner.calls) |
| 95 | assert any(c.startswith("muse") and " update-ref " in c for c, _ in runner.calls) |
| 96 | push_calls = [c for c, _ in runner.calls if c.startswith("git push")] |
| 97 | assert push_calls and all(branch in c for c in push_calls) |
| 98 | assert not any("--force" in c for c, _ in runner.calls) |
| 99 | |
| 100 | |
| 101 | def test_diverged_tip_uniquifies_commit_push_and_pr_url(tmp_path: Path) -> None: |
| 102 | """Fixture 2: a diverged same-day branch keeps its tip; commit/push/pr_url |
| 103 | all use the deterministic ``-2`` name (frozen-plan replace).""" |
| 104 | branch = _feature_branch() |
| 105 | seed_gsw_repo(tmp_path, "muse+git-mirror") |
| 106 | runner = gsw_runner( |
| 107 | tmp_path, |
| 108 | "muse+git-mirror", |
| 109 | existing_git_branches={branch}, |
| 110 | existing_muse_branches={branch}, |
| 111 | # Muse side diverged; git side behind — cross-history rule → uniquify. |
| 112 | git_tips={branch: "gitstale"}, |
| 113 | muse_tips={branch: "sha256:divergent"}, |
| 114 | git_ancestors={"feedface": {"gitstale"}}, |
| 115 | ) |
| 116 | buf = io.StringIO() |
| 117 | with redirect_stdout(buf): |
| 118 | code = run_cli( |
| 119 | ["governance-sync", "--write"], cwd=tmp_path, runner=runner, kit=kit_root() |
| 120 | ) |
| 121 | assert code == 0 |
| 122 | uniquified = f"{branch}-2" |
| 123 | assert runner.git_branch == uniquified |
| 124 | assert runner.muse_branch == uniquified |
| 125 | # Diverged original tips untouched (no partial FF, no rewind). |
| 126 | assert runner.git_tips[branch] == "gitstale" |
| 127 | assert runner.muse_tips[branch] == "sha256:divergent" |
| 128 | push_calls = [c for c, _ in runner.calls if c.startswith("git push")] |
| 129 | assert push_calls and all(uniquified in c for c in push_calls) |
| 130 | assert f"main...{uniquified}?expand=1" in buf.getvalue() |
| 131 | |
| 132 | |
| 133 | def test_mirror_muse_head_stranded_on_stale_branch_reconciles(tmp_path: Path) -> None: |
| 134 | """Fixture 3 (R1-M2): Muse HEAD already on the stale dated branch while |
| 135 | Git HEAD is on advanced main — T_target falls back to configured main on |
| 136 | the Muse side, C0 fast-forwards both histories, and C1 succeeds.""" |
| 137 | branch = _feature_branch() |
| 138 | seed_gsw_repo(tmp_path, "muse+git-mirror") |
| 139 | runner = gsw_runner( |
| 140 | tmp_path, |
| 141 | "muse+git-mirror", |
| 142 | muse_branch=branch, |
| 143 | # Session work in progress on the shared tree — also keeps the KH2 |
| 144 | # muse-sync gate (muse-dirty + git-clean → pending) out of the way so |
| 145 | # the reconcile path itself is what's under test. |
| 146 | git_dirty=True, |
| 147 | existing_git_branches={branch}, |
| 148 | existing_muse_branches={"main"}, |
| 149 | git_tips={branch: "gitstale"}, |
| 150 | muse_tips={branch: "sha256:stale", "main": "sha256:musetip"}, |
| 151 | git_ancestors={"feedface": {"gitstale"}}, |
| 152 | muse_ancestors={"sha256:musetip": {"sha256:stale"}}, |
| 153 | # Shared worktree holds the advanced-main bytes; the stale tips hold |
| 154 | # distinct day-1 content (the live PLS land-b shape). |
| 155 | content_map={ |
| 156 | "sha256:stale": "content:day1-muse", |
| 157 | "gitstale": "content:day1-git", |
| 158 | }, |
| 159 | ) |
| 160 | code = run_cli(["governance-sync", "--write"], cwd=tmp_path, runner=runner, kit=kit_root()) |
| 161 | assert code == 0 |
| 162 | assert runner.git_branch == branch |
| 163 | assert runner.muse_branch == branch |
| 164 | # Muse FF used update-ref against configured main — not HEAD-equal skip. |
| 165 | assert any( |
| 166 | c.startswith("muse") and f" update-ref {branch} sha256:musetip" in c |
| 167 | for c, _ in runner.calls |
| 168 | ) |
| 169 | assert any(c.startswith("git branch -f ") for c, _ in runner.calls) |
| 170 | # The Git checkout was never refused by leftover stale bytes. |
| 171 | assert not any("--force" in c for c, _ in runner.calls) |
| 172 | assert runner.muse_commit_count == 1 |
File History
1 commit
sha256:6abcf1fa82a7a621ccbc945f19acdba5bc0db54569599404a1452fb4a096a199
fix(ISR): default require_independent_second_reviewer to require
Human
minor
⚠
9 hours ago