config_gen.py python
200 lines 6.4 KB
Raw
sha256:4671b7f787ddbe63ced31c895b688c77ab495653b65a730b423329f26b3c1439 feat: K1-P1 complete — agent provenance, build-verification… Sonnet 4.6 patch 55 days ago
1 """Default config generation for ``overseer init``."""
2
3 from __future__ import annotations
4
5 from pathlib import Path
6
7 import yaml
8
9 from adapters.config import SUPPORTED_CONFIG_VERSION, OverseerConfig, load_config
10 from adapters.errors import ConfigError
11
12
13 def detect_regime(repo_root: Path) -> str | None:
14 """Advisory regime detection from repo markers."""
15 has_git = (repo_root / ".git").exists()
16 has_muse = (repo_root / ".muse").is_dir()
17 has_bridge = (repo_root / ".muse" / "git-bridge.toml").is_file()
18 if has_bridge:
19 return "muse+git-mirror"
20 if has_muse:
21 return "muse-only"
22 if has_git:
23 return "git-only"
24 return None
25
26
27 def default_config_dict(
28 *,
29 regime: str,
30 repo_name: str,
31 docs_dir: str,
32 ) -> dict:
33 """Build a default config mapping for the given regime."""
34 docs_dir = docs_dir.strip("/") or "docs"
35 base = {
36 "overseer_config_version": SUPPORTED_CONFIG_VERSION,
37 "repo": {
38 "name": repo_name,
39 "root_relative_docs": docs_dir,
40 },
41 "thresholds": {
42 "realign_max_commits": 50,
43 "drift_warn_only": True,
44 },
45 "freeze_contract": {
46 "enabled": True,
47 "reviewer": {
48 "mode": "agent",
49 "model": "thinking-high",
50 "provider": "local",
51 "fallback": "human",
52 },
53 "human_escalation": ["security"],
54 },
55 }
56
57 if regime == "git-only":
58 base["vcs"] = {
59 "regime": "git-only",
60 "canonical": "git",
61 "git": {
62 "remote": "origin",
63 "main_branch": "main",
64 "mirror_branch": None,
65 "feature_branch_pattern": "feat/{slug}",
66 },
67 "muse": {
68 "staging_remote": None,
69 "main_branch": None,
70 },
71 }
72 base["docs"] = {
73 "handover": "OVERSEER-HANDOVER.md",
74 "roadmap": "ROADMAP.md",
75 "coordination": None,
76 "standing_decisions": "ROADMAP.md",
77 }
78 return base
79
80 if regime == "muse-only":
81 base["vcs"] = {
82 "regime": "muse-only",
83 "canonical": "muse",
84 "git": {
85 "remote": "origin",
86 "main_branch": "main",
87 "mirror_branch": None,
88 "feature_branch_pattern": "feat/{slug}",
89 },
90 "muse": {
91 "staging_remote": None,
92 "main_branch": "main",
93 },
94 }
95 base["docs"] = {
96 "handover": "MUSEHUB-OVERSEER-HANDOVER.md",
97 "roadmap": "MUSEHUB-ROADMAP.md",
98 "coordination": None,
99 "standing_decisions": "MUSEHUB-ROADMAP.md",
100 }
101 return base
102
103 if regime == "muse+git-mirror":
104 base["vcs"] = {
105 "regime": "muse+git-mirror",
106 "canonical": "muse",
107 "git": {
108 "remote": "origin",
109 "main_branch": "main",
110 "mirror_branch": "muse-mirror",
111 "feature_branch_pattern": "feat/{slug}",
112 },
113 "muse": {
114 "staging_remote": "staging",
115 "main_branch": "main",
116 },
117 }
118 base["docs"] = {
119 "handover": "OVERSEER-HANDOVER.md",
120 "roadmap": "ROADMAP.md",
121 "coordination": "CROSS-REPO-COORDINATION.md",
122 "standing_decisions": "CROSS-REPO-COORDINATION.md",
123 }
124 return base
125
126 raise ConfigError(f"unsupported regime {regime!r}")
127
128
129 def config_dict_to_yaml(data: dict) -> str:
130 """Serialize a config mapping to YAML text."""
131 return yaml.safe_dump(data, sort_keys=False, allow_unicode=True, default_flow_style=False)
132
133
134 def load_config_from_dict(data: dict, path: str) -> OverseerConfig:
135 """Validate a config dict via a temporary path label."""
136 return load_config_from_text(config_dict_to_yaml(data), path)
137
138
139 def load_config_from_text(text: str, path: str) -> OverseerConfig:
140 """Parse config text by writing to a temp file for ``load_config`` validation."""
141 from tempfile import NamedTemporaryFile
142
143 with NamedTemporaryFile("w", suffix=".yaml", delete=False, encoding="utf-8") as handle:
144 handle.write(text)
145 temp_path = Path(handle.name)
146 try:
147 return load_config(temp_path)
148 finally:
149 temp_path.unlink(missing_ok=True)
150
151
152 def configs_equal(a: OverseerConfig, b: OverseerConfig) -> bool:
153 """Return True when two validated configs are equivalent."""
154 return config_dict_to_yaml(config_to_dict(a)) == config_dict_to_yaml(config_to_dict(b))
155
156
157 def config_to_dict(config: OverseerConfig) -> dict:
158 """Convert ``OverseerConfig`` back to a YAML-compatible mapping."""
159 return {
160 "overseer_config_version": config.overseer_config_version,
161 "repo": {
162 "name": config.repo.name,
163 "root_relative_docs": config.repo.root_relative_docs,
164 },
165 "vcs": {
166 "regime": config.vcs.regime,
167 "canonical": config.vcs.canonical,
168 "git": {
169 "remote": config.vcs.git.remote,
170 "main_branch": config.vcs.git.main_branch,
171 "mirror_branch": config.vcs.git.mirror_branch,
172 "feature_branch_pattern": config.vcs.git.feature_branch_pattern,
173 },
174 "muse": {
175 "staging_remote": config.vcs.muse.staging_remote,
176 "main_branch": config.vcs.muse.main_branch,
177 "working_dir": config.vcs.muse.working_dir,
178 },
179 },
180 "docs": {
181 "handover": config.docs.handover,
182 "roadmap": config.docs.roadmap,
183 "coordination": config.docs.coordination,
184 "standing_decisions": config.docs.standing_decisions,
185 },
186 "thresholds": {
187 "realign_max_commits": config.thresholds.realign_max_commits,
188 "drift_warn_only": config.thresholds.drift_warn_only,
189 },
190 "freeze_contract": {
191 "enabled": config.freeze_contract.enabled,
192 "reviewer": {
193 "mode": config.freeze_contract.reviewer.mode,
194 "model": config.freeze_contract.reviewer.model,
195 "provider": config.freeze_contract.reviewer.provider,
196 "fallback": config.freeze_contract.reviewer.fallback,
197 },
198 "human_escalation": list(config.freeze_contract.human_escalation),
199 },
200 }
File History 1 commit
sha256:4671b7f787ddbe63ced31c895b688c77ab495653b65a730b423329f26b3c1439 feat: K1-P1 complete — agent provenance, build-verification… Sonnet 4.6 patch 55 days ago