adapter.py
python
sha256:a78e7e5a8740e03315f325d19edeb3aa1b306b3337d04abbaa9a9e0f3bbeb7a1
docs: MuseHub-first before ISR #74 — staging solidify NEXT
Human
1 day ago
| 1 | """Muse-only VCS adapter backend (§4).""" |
| 2 | |
| 3 | from __future__ import annotations |
| 4 | |
| 5 | from adapters.base import BaseAdapter |
| 6 | from adapters.errors import ReadError, WriteError |
| 7 | from adapters.runner import CommandResult |
| 8 | from adapters.types import ( |
| 9 | AnchorResult, |
| 10 | CommitResult, |
| 11 | HeadResult, |
| 12 | MirrorResult, |
| 13 | RealignResult, |
| 14 | StatusResult, |
| 15 | ) |
| 16 | |
| 17 | GIT_FORBIDDEN = "git forbidden in this regime" |
| 18 | |
| 19 | |
| 20 | class MuseOnlyAdapter(BaseAdapter): |
| 21 | """MuseHub backend — git and mirror are hard no-ops.""" |
| 22 | |
| 23 | def status(self) -> StatusResult | ReadError: |
| 24 | branch_result = self._muse("branch", "--show-current") |
| 25 | if isinstance(branch_result, ReadError): |
| 26 | return branch_result |
| 27 | dirty_result = self._muse_dirty() |
| 28 | if isinstance(dirty_result, ReadError): |
| 29 | return dirty_result |
| 30 | return StatusResult( |
| 31 | regime=self.regime, |
| 32 | dirty=dirty_result, |
| 33 | branch=branch_result.stdout, |
| 34 | notes=["canonical=muse", "git-forbidden"], |
| 35 | muse_dirty=dirty_result, |
| 36 | git_dirty=None, |
| 37 | ) |
| 38 | |
| 39 | def read_head(self, ref: str) -> HeadResult | ReadError: |
| 40 | if ref.startswith("origin/") or ref.startswith(self.config.vcs.git.remote + "/"): |
| 41 | return ReadError("read_head", GIT_FORBIDDEN) |
| 42 | muse_ref = ref.removeprefix("muse:") |
| 43 | return self._muse_rev_parse_sha(muse_ref) |
| 44 | |
| 45 | def read_canonical_anchor(self) -> AnchorResult | ReadError: |
| 46 | main = self.config.vcs.muse.main_branch |
| 47 | if not main: |
| 48 | return ReadError("read_canonical_anchor", "muse.main_branch not configured") |
| 49 | head = self.read_head(f"muse:{main}") |
| 50 | if isinstance(head, ReadError): |
| 51 | return head |
| 52 | return AnchorResult(anchor_sha=head.sha, source=f"muse:{main}") |
| 53 | |
| 54 | def realign(self, *, dry_run: bool, max_commits: int) -> RealignResult: |
| 55 | return RealignResult( |
| 56 | would_import=0, |
| 57 | applied=False, |
| 58 | from_ref=None, |
| 59 | to_ref=None, |
| 60 | reason="single-history", |
| 61 | ) |
| 62 | |
| 63 | def commit_feature( |
| 64 | self, |
| 65 | *, |
| 66 | branch: str, |
| 67 | message: str, |
| 68 | paths: list[str], |
| 69 | ) -> CommitResult | ReadError | WriteError: |
| 70 | unsafe = self._validate_paths(paths) |
| 71 | if unsafe: |
| 72 | return unsafe |
| 73 | if self._is_protected_branch(branch): |
| 74 | return WriteError( |
| 75 | "commit_feature", |
| 76 | f"refused protected branch {branch!r}", |
| 77 | ) |
| 78 | |
| 79 | # §GSW.6.1: skip checkout when Muse HEAD is already on the branch — |
| 80 | # dirty handover/roadmap paths must never fail the commit here. |
| 81 | current = self._muse("rev-parse", "--abbrev-ref", "HEAD") |
| 82 | if isinstance(current, ReadError): |
| 83 | return current |
| 84 | if current.stdout.strip() != branch: |
| 85 | # §GSW.6.2: bare checkout refuses dirty tracked files (Muse 0.2.x); |
| 86 | # retry with --autoshelf to carry them. --force is forbidden. |
| 87 | checkout = self._muse("checkout", branch) |
| 88 | if isinstance(checkout, ReadError): |
| 89 | checkout = self._muse("checkout", "--autoshelf", branch) |
| 90 | if isinstance(checkout, ReadError): |
| 91 | return checkout |
| 92 | |
| 93 | if paths: |
| 94 | for path in paths: |
| 95 | # Muse 0.2.x has no top-level `add`; staging is `muse code add` |
| 96 | # (matches the KH2 remediation string). Found live in GSW land-b. |
| 97 | add = self._muse("code", "add", path) |
| 98 | if isinstance(add, ReadError): |
| 99 | return add |
| 100 | |
| 101 | commit = self._muse("commit", "-m", message) |
| 102 | if isinstance(commit, ReadError): |
| 103 | return commit |
| 104 | |
| 105 | head = self._muse_rev_parse_sha("HEAD") |
| 106 | if isinstance(head, ReadError): |
| 107 | return head |
| 108 | return CommitResult(committed=True, sha=head.sha) |
| 109 | |
| 110 | def mirror(self, *, dry_run: bool) -> MirrorResult: |
| 111 | return MirrorResult( |
| 112 | diff_summary="", |
| 113 | pushed=False, |
| 114 | reason=GIT_FORBIDDEN, |
| 115 | ) |
| 116 | |
| 117 | def _git(self, *args: str) -> CommandResult | ReadError: |
| 118 | return ReadError("git", GIT_FORBIDDEN) |
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
52 days ago