realign.py python
119 lines 3.9 KB
Raw
sha256:a78e7e5a8740e03315f325d19edeb3aa1b306b3337d04abbaa9a9e0f3bbeb7a1 docs: MuseHub-first before ISR #74 — staging solidify NEXT Human 1 day ago
1 """Muse realignment guard (§5 / §D2F.5)."""
2
3 from __future__ import annotations
4
5 from adapters.base import VcsAdapter, read_bridge_git_sha
6 from adapters.config import OverseerConfig
7 from adapters.errors import ReadError
8 from adapters.runner import CommandRunner, quote_arg
9 from tools.governance_hygiene.types import DriftReport, VerifiedReads
10
11
12 def plan_realign(
13 config: OverseerConfig,
14 adapter: VcsAdapter,
15 reads: VerifiedReads,
16 drift: DriftReport,
17 ) -> tuple[bool, str]:
18 """Return whether realign would run and a human-readable reason."""
19 if drift.d2_anchor_vs_canonical != "drifted":
20 return False, "D2 aligned — skip realign"
21 if config.vcs.regime != "muse+git-mirror":
22 return False, f"{config.vcs.regime}: realign no-op"
23 if reads.r1_github_main_sha is None:
24 return False, "missing R1 github main sha"
25 # Ancestry uses Git ID space (git_sha), never muse_commit_id / R2 Muse tip (§D2F.5).
26 git_anchor = read_bridge_git_sha(adapter.repo_root, "last_export")
27 if not git_anchor:
28 git_anchor = read_bridge_git_sha(adapter.repo_root, "last_import")
29 if not git_anchor:
30 return False, "missing bridge git_sha for realign ancestry — operator required"
31 if not _github_superset_of_anchor(
32 adapter,
33 git_anchor,
34 reads.r1_github_main_sha,
35 ):
36 return False, "R1 is not a content superset of anchor — operator required"
37 return True, "D2 drift + superset precondition met"
38
39
40 def execute_realign_guard(
41 config: OverseerConfig,
42 adapter: VcsAdapter,
43 reads: VerifiedReads,
44 drift: DriftReport,
45 *,
46 dry_run: bool,
47 ) -> tuple[str | None, str | None]:
48 """
49 Run §5 guard sequence.
50
51 Returns ``(summary, error_command)`` where error_command is set on verification failure.
52 """
53 should_run, reason = plan_realign(config, adapter, reads, drift)
54 if not should_run:
55 return reason, None
56
57 max_commits = config.thresholds.realign_max_commits
58 preview = adapter.realign(dry_run=True, max_commits=max_commits)
59 if isinstance(preview, ReadError):
60 return None, preview.command
61
62 if preview.would_import > max_commits:
63 return (
64 f"realign withheld: would_import={preview.would_import} > max={max_commits}",
65 None,
66 )
67
68 if dry_run:
69 return (
70 f"realign planned: would_import={preview.would_import} "
71 f"from {preview.from_ref} to {preview.to_ref}",
72 None,
73 )
74
75 applied = adapter.realign(dry_run=False, max_commits=max_commits)
76 if isinstance(applied, ReadError):
77 return None, applied.command
78
79 if not applied.applied:
80 return f"realign not applied: {applied.reason}", None
81
82 anchor = adapter.read_canonical_anchor()
83 if isinstance(anchor, ReadError):
84 return None, anchor.command
85
86 muse_main = config.vcs.muse.main_branch
87 head = adapter.read_head(f"muse:{muse_main}")
88 if isinstance(head, ReadError):
89 return None, head.command
90
91 # Verify in Muse ID space (muse_commit_id vs muse tip) — never git_sha == tip (§D2F.5).
92 if anchor.anchor_sha.lower() != head.sha.lower():
93 return None, "realign verification failed: muse_commit_id != muse main"
94
95 return (
96 f"realign applied: imported {applied.would_import} "
97 f"from {applied.from_ref} to {applied.to_ref}",
98 None,
99 )
100
101
102 def _github_superset_of_anchor(
103 adapter: VcsAdapter,
104 anchor_sha: str,
105 main_sha: str,
106 ) -> bool:
107 """True when ``main_sha`` contains ``anchor_sha`` (recovery precondition)."""
108 if anchor_sha.lower() == main_sha.lower():
109 return True
110 runner = adapter.runner
111 repo_root = str(adapter.repo_root)
112 cmd = (
113 "git merge-base --is-ancestor "
114 + quote_arg(anchor_sha)
115 + " "
116 + quote_arg(main_sha)
117 )
118 result = runner.run(cmd, cwd=repo_root)
119 return result.ok
File History 2 commits
sha256:a78e7e5a8740e03315f325d19edeb3aa1b306b3337d04abbaa9a9e0f3bbeb7a1 docs: MuseHub-first before ISR #74 — staging solidify NEXT Human 1 day ago
sha256:4671b7f787ddbe63ced31c895b688c77ab495653b65a730b423329f26b3c1439 feat: K1-P1 complete — agent provenance, build-verification… Sonnet 4.6 patch 53 days ago