test_init_migrate.py python
395 lines 14.0 KB
Raw
sha256:a78e7e5a8740e03315f325d19edeb3aa1b306b3337d04abbaa9a9e0f3bbeb7a1 docs: MuseHub-first before ISR #74 — staging solidify NEXT Human 1 day ago
1 """Unit tests for ``overseer init --migrate`` (§K6.4 / §K6.10)."""
2
3 from __future__ import annotations
4
5 from pathlib import Path
6
7 from cli.version_lock import ORIGIN_KIT, ORIGIN_PRESERVED, read_version_lock
8 from tests.support import PILOT, git_status_runner, lock_origins, muse_mirror_status_runner, run_cli, seed_pilot_tree
9
10
11 def _migrate(tmp_path: Path, config_name: str, *extra: str) -> int:
12 runner = (
13 git_status_runner()
14 if "videofactory" in config_name
15 else muse_mirror_status_runner(tmp_path)
16 )
17 return run_cli(
18 [
19 "init",
20 "--migrate",
21 "--from-config",
22 str(PILOT / config_name),
23 "--non-interactive",
24 *extra,
25 ],
26 cwd=tmp_path,
27 runner=runner,
28 )
29
30
31 def _runner_for(tmp_path: Path, config_name: str = "config-scooling.yaml"):
32 if "videofactory" in config_name:
33 return git_status_runner()
34 return muse_mirror_status_runner(tmp_path)
35
36
37 def test_migrate_preserves_differing_living_docs(tmp_path: Path) -> None:
38 seed_pilot_tree(
39 tmp_path,
40 handover_rel="docs/OVERSEER-HANDOVER.md",
41 handover_text="# HAND SCOOLING\n",
42 roadmap_rel="docs/ROADMAP.md",
43 roadmap_text="# HAND ROADMAP\n",
44 )
45 code = _migrate(tmp_path, "config-scooling.yaml")
46 assert code == 0
47 assert (tmp_path / "docs/OVERSEER-HANDOVER.md").read_text(encoding="utf-8") == "# HAND SCOOLING\n"
48 origins = lock_origins(tmp_path)
49 assert origins["docs/OVERSEER-HANDOVER.md"] == ORIGIN_PRESERVED
50 assert origins["docs/ROADMAP.md"] == ORIGIN_PRESERVED
51 assert origins[".overseer/policy/tiers.yaml"] == ORIGIN_KIT
52
53
54 def test_migrate_force_still_preserves_living_docs(tmp_path: Path) -> None:
55 seed_pilot_tree(
56 tmp_path,
57 handover_rel="docs/OVERSEER-HANDOVER.md",
58 handover_text="# KEEP\n",
59 roadmap_rel="docs/ROADMAP.md",
60 roadmap_text="# KEEP ROAD\n",
61 )
62 code = _migrate(tmp_path, "config-scooling.yaml", "--force")
63 assert code == 0
64 assert (tmp_path / "docs/OVERSEER-HANDOVER.md").read_text(encoding="utf-8") == "# KEEP\n"
65 assert lock_origins(tmp_path)["docs/OVERSEER-HANDOVER.md"] == ORIGIN_PRESERVED
66
67
68 def test_migrate_seeds_absent_living_doc_as_preserved(tmp_path: Path) -> None:
69 seed_pilot_tree(
70 tmp_path,
71 handover_rel="docs/OVERSEER-HANDOVER.md",
72 handover_text="# KN HAND\n",
73 roadmap_rel=None,
74 )
75 code = _migrate(tmp_path, "config-knowtation.yaml")
76 assert code == 0
77 assert (tmp_path / "docs/ROADMAP.md").is_file()
78 assert lock_origins(tmp_path)["docs/ROADMAP.md"] == ORIGIN_PRESERVED
79 assert lock_origins(tmp_path)["docs/OVERSEER-HANDOVER.md"] == ORIGIN_PRESERVED
80
81
82 def test_migrate_promote_differing_living_doc(tmp_path: Path) -> None:
83 seed_pilot_tree(
84 tmp_path,
85 handover_rel="docs/OVERSEER-HANDOVER.md",
86 handover_text="# OLD\n",
87 roadmap_rel="docs/ROADMAP.md",
88 roadmap_text="# OLD R\n",
89 )
90 code = _migrate(
91 tmp_path,
92 "config-scooling.yaml",
93 "--force",
94 "--include-preserved",
95 )
96 assert code == 0
97 text = (tmp_path / "docs/OVERSEER-HANDOVER.md").read_text(encoding="utf-8")
98 assert text != "# OLD\n"
99 assert lock_origins(tmp_path)["docs/OVERSEER-HANDOVER.md"] == ORIGIN_KIT
100
101
102 def test_migrate_promote_identical_living_doc_ownership_only(tmp_path: Path) -> None:
103 run_cli(
104 [
105 "init",
106 "--from-config",
107 str(PILOT / "config-scooling.yaml"),
108 "--non-interactive",
109 ],
110 cwd=tmp_path,
111 runner=muse_mirror_status_runner(tmp_path),
112 )
113 before = (tmp_path / "docs/OVERSEER-HANDOVER.md").read_bytes()
114 code = _migrate(
115 tmp_path,
116 "config-scooling.yaml",
117 "--force",
118 "--include-preserved",
119 )
120 assert code == 0
121 assert (tmp_path / "docs/OVERSEER-HANDOVER.md").read_bytes() == before
122 assert lock_origins(tmp_path)["docs/OVERSEER-HANDOVER.md"] == ORIGIN_KIT
123
124
125 def test_migrate_include_preserved_without_force_is_noop(tmp_path: Path) -> None:
126 seed_pilot_tree(
127 tmp_path,
128 handover_rel="docs/OVERSEER-HANDOVER.md",
129 handover_text="# KEEP\n",
130 roadmap_rel="docs/ROADMAP.md",
131 roadmap_text="# KEEP R\n",
132 )
133 code = _migrate(tmp_path, "config-scooling.yaml", "--include-preserved")
134 assert code == 0
135 assert (tmp_path / "docs/OVERSEER-HANDOVER.md").read_text(encoding="utf-8") == "# KEEP\n"
136 assert lock_origins(tmp_path)["docs/OVERSEER-HANDOVER.md"] == ORIGIN_PRESERVED
137
138
139 def test_migrate_refuses_shared_asset_conflict(tmp_path: Path) -> None:
140 seed_pilot_tree(
141 tmp_path,
142 handover_rel="docs/OVERSEER-HANDOVER.md",
143 handover_text="# H\n",
144 roadmap_rel="docs/ROADMAP.md",
145 roadmap_text="# R\n",
146 )
147 policy = tmp_path / ".overseer" / "policy"
148 policy.mkdir(parents=True, exist_ok=True)
149 (policy / "tiers.yaml").write_text("altered: true\n", encoding="utf-8")
150 code = _migrate(tmp_path, "config-scooling.yaml")
151 assert code == 4
152
153
154 def test_kn_r2_pass_updates_without_force(tmp_path: Path) -> None:
155 consumer_rule = (PILOT / "knowtation-no-docs-only-pr.mdc").read_text(encoding="utf-8")
156 seed_pilot_tree(
157 tmp_path,
158 handover_rel="docs/OVERSEER-HANDOVER.md",
159 handover_text="# KN\n",
160 roadmap_rel=None,
161 extra_cursor_rules={"no-docs-only-pr-to-main.mdc": consumer_rule},
162 )
163 code = _migrate(tmp_path, "config-knowtation.yaml")
164 assert code == 0
165 lock = read_version_lock(tmp_path / ".overseer" / "version.lock")
166 entry = next(e for e in lock.footprint if e.path == ".cursor/rules/no-docs-only-pr-to-main.mdc")
167 assert entry.origin == ORIGIN_KIT
168 # Written content should be rendered kit (tokens substituted), not consumer bytes
169 written = (tmp_path / ".cursor/rules/no-docs-only-pr-to-main.mdc").read_text(encoding="utf-8")
170 assert "{{vcs.git.main_branch}}" not in written
171 assert "main" in written
172
173
174 def test_kit_only_digest_excludes_preserved(tmp_path: Path) -> None:
175 seed_pilot_tree(
176 tmp_path,
177 handover_rel="docs/OVERSEER-HANDOVER.md",
178 handover_text="# H\n",
179 roadmap_rel="docs/ROADMAP.md",
180 roadmap_text="# R\n",
181 )
182 assert _migrate(tmp_path, "config-scooling.yaml") == 0
183 lock = read_version_lock(tmp_path / ".overseer" / "version.lock")
184 from cli.version_lock import compute_lock_digest
185
186 assert lock.footprint_digest == compute_lock_digest(list(lock.footprint))
187 assert any(e.origin == ORIGIN_PRESERVED for e in lock.footprint)
188
189
190 def test_hand_edit_preserved_check_footprint_ok(tmp_path: Path) -> None:
191 seed_pilot_tree(
192 tmp_path,
193 handover_rel="docs/OVERSEER-HANDOVER.md",
194 handover_text="# H\n",
195 roadmap_rel="docs/ROADMAP.md",
196 roadmap_text="# R\n",
197 )
198 assert _migrate(tmp_path, "config-scooling.yaml") == 0
199 (tmp_path / "docs/OVERSEER-HANDOVER.md").write_text("# H edited\n", encoding="utf-8")
200 code = run_cli(
201 ["status", "--check-footprint", "--json"],
202 cwd=tmp_path,
203 runner=muse_mirror_status_runner(tmp_path),
204 json_mode=True,
205 )
206 assert code == 0
207
208
209 def test_hand_edit_preserved_default_sync_exit_zero(tmp_path: Path) -> None:
210 seed_pilot_tree(
211 tmp_path,
212 handover_rel="docs/OVERSEER-HANDOVER.md",
213 handover_text="# H\n",
214 roadmap_rel="docs/ROADMAP.md",
215 roadmap_text="# R\n",
216 )
217 assert _migrate(tmp_path, "config-scooling.yaml") == 0
218 (tmp_path / "docs/OVERSEER-HANDOVER.md").write_text("# H edited\n", encoding="utf-8")
219 code = run_cli(["sync", "-y"], cwd=tmp_path, runner=muse_mirror_status_runner(tmp_path))
220 assert code == 0
221 assert (tmp_path / "docs/OVERSEER-HANDOVER.md").read_text(encoding="utf-8") == "# H edited\n"
222
223
224 def test_sync_force_alone_does_not_overwrite_preserved(tmp_path: Path) -> None:
225 seed_pilot_tree(
226 tmp_path,
227 handover_rel="docs/OVERSEER-HANDOVER.md",
228 handover_text="# H\n",
229 roadmap_rel="docs/ROADMAP.md",
230 roadmap_text="# R\n",
231 )
232 assert _migrate(tmp_path, "config-scooling.yaml") == 0
233 code = run_cli(["sync", "--force", "-y"], cwd=tmp_path, runner=muse_mirror_status_runner(tmp_path))
234 assert code == 0
235 assert (tmp_path / "docs/OVERSEER-HANDOVER.md").read_text(encoding="utf-8") == "# H\n"
236 assert lock_origins(tmp_path)["docs/OVERSEER-HANDOVER.md"] == ORIGIN_PRESERVED
237
238
239 def test_sync_force_include_preserved_promotes(tmp_path: Path) -> None:
240 seed_pilot_tree(
241 tmp_path,
242 handover_rel="docs/OVERSEER-HANDOVER.md",
243 handover_text="# H\n",
244 roadmap_rel="docs/ROADMAP.md",
245 roadmap_text="# R\n",
246 )
247 assert _migrate(tmp_path, "config-scooling.yaml") == 0
248 code = run_cli(
249 ["sync", "--force", "--include-preserved", "-y"],
250 cwd=tmp_path,
251 runner=muse_mirror_status_runner(tmp_path),
252 )
253 assert code == 0
254 assert lock_origins(tmp_path)["docs/OVERSEER-HANDOVER.md"] == ORIGIN_KIT
255 assert "# H\n" not in (tmp_path / "docs/OVERSEER-HANDOVER.md").read_text(encoding="utf-8")
256
257
258 def test_hand_edit_seeded_roadmap_check_and_sync(tmp_path: Path) -> None:
259 seed_pilot_tree(
260 tmp_path,
261 handover_rel="docs/OVERSEER-HANDOVER.md",
262 handover_text="# KN\n",
263 roadmap_rel=None,
264 )
265 assert _migrate(tmp_path, "config-knowtation.yaml") == 0
266 (tmp_path / "docs/ROADMAP.md").write_text("# seeded then edited\n", encoding="utf-8")
267 assert run_cli(
268 ["status", "--check-footprint"],
269 cwd=tmp_path,
270 runner=muse_mirror_status_runner(tmp_path),
271 ) == 0
272 assert run_cli(["sync", "-y"], cwd=tmp_path, runner=muse_mirror_status_runner(tmp_path)) == 0
273 assert (tmp_path / "docs/ROADMAP.md").read_text(encoding="utf-8") == "# seeded then edited\n"
274
275
276 def test_migrate_force_overwrites_shared_asset_without_preserve_flag(tmp_path: Path) -> None:
277 seed_pilot_tree(
278 tmp_path,
279 handover_rel="docs/OVERSEER-HANDOVER.md",
280 handover_text="# H\n",
281 roadmap_rel="docs/ROADMAP.md",
282 roadmap_text="# R\n",
283 )
284 policy = tmp_path / ".overseer" / "policy"
285 policy.mkdir(parents=True, exist_ok=True)
286 (policy / "tiers.yaml").write_text("altered: true\n", encoding="utf-8")
287 code = _migrate(tmp_path, "config-scooling.yaml", "--force")
288 assert code == 0
289 assert (policy / "tiers.yaml").read_text(encoding="utf-8") != "altered: true\n"
290 assert lock_origins(tmp_path)[".overseer/policy/tiers.yaml"] == ORIGIN_KIT
291
292
293 def test_migrate_preserve_shared_assets_keeps_differing_shared(tmp_path: Path) -> None:
294 seed_pilot_tree(
295 tmp_path,
296 handover_rel="docs/OVERSEER-HANDOVER.md",
297 handover_text="# H\n",
298 roadmap_rel="docs/ROADMAP.md",
299 roadmap_text="# R\n",
300 )
301 policy = tmp_path / ".overseer" / "policy"
302 policy.mkdir(parents=True, exist_ok=True)
303 hand = "altered: true\n# consumer-owned\n"
304 (policy / "tiers.yaml").write_text(hand, encoding="utf-8")
305 code = _migrate(tmp_path, "config-scooling.yaml", "--preserve-shared-assets")
306 assert code == 0
307 assert (policy / "tiers.yaml").read_text(encoding="utf-8") == hand
308 assert lock_origins(tmp_path)[".overseer/policy/tiers.yaml"] == ORIGIN_PRESERVED
309
310
311 def test_migrate_force_preserve_shared_assets_still_keeps_shared(tmp_path: Path) -> None:
312 seed_pilot_tree(
313 tmp_path,
314 handover_rel="docs/OVERSEER-HANDOVER.md",
315 handover_text="# KEEP\n",
316 roadmap_rel="docs/ROADMAP.md",
317 roadmap_text="# KEEP R\n",
318 )
319 policy = tmp_path / ".overseer" / "policy"
320 policy.mkdir(parents=True, exist_ok=True)
321 hand = "consumer-owned-policy: true\n"
322 (policy / "tiers.yaml").write_text(hand, encoding="utf-8")
323 code = _migrate(tmp_path, "config-scooling.yaml", "--force", "--preserve-shared-assets")
324 assert code == 0
325 assert (policy / "tiers.yaml").read_text(encoding="utf-8") == hand
326 assert (tmp_path / "docs/OVERSEER-HANDOVER.md").read_text(encoding="utf-8") == "# KEEP\n"
327 assert lock_origins(tmp_path)[".overseer/policy/tiers.yaml"] == ORIGIN_PRESERVED
328 assert lock_origins(tmp_path)["docs/OVERSEER-HANDOVER.md"] == ORIGIN_PRESERVED
329
330
331 def test_migrate_promote_overwrites_preserved_shared(tmp_path: Path) -> None:
332 seed_pilot_tree(
333 tmp_path,
334 handover_rel="docs/OVERSEER-HANDOVER.md",
335 handover_text="# H\n",
336 roadmap_rel="docs/ROADMAP.md",
337 roadmap_text="# R\n",
338 )
339 policy = tmp_path / ".overseer" / "policy"
340 policy.mkdir(parents=True, exist_ok=True)
341 (policy / "tiers.yaml").write_text("altered: true\n", encoding="utf-8")
342 code = _migrate(
343 tmp_path,
344 "config-scooling.yaml",
345 "--force",
346 "--include-preserved",
347 "--preserve-shared-assets",
348 )
349 assert code == 0
350 assert (policy / "tiers.yaml").read_text(encoding="utf-8") != "altered: true\n"
351 assert lock_origins(tmp_path)[".overseer/policy/tiers.yaml"] == ORIGIN_KIT
352
353
354 def test_classify_preserve_shared_unit() -> None:
355 from cli.commands.init import MigrateClass, _classify_migrate
356 from cli.footprint import FootprintFile
357
358 item = FootprintFile(destination=".overseer/policy/tiers.yaml", source="x", content=b"kit\n")
359 assert (
360 _classify_migrate(
361 item=item,
362 existing=b"hand\n",
363 is_living=False,
364 promote=False,
365 kn_r2_pass=False,
366 force=True,
367 preserve_shared=True,
368 )
369 == MigrateClass.PRESERVED
370 )
371 assert (
372 _classify_migrate(
373 item=item,
374 existing=b"hand\n",
375 is_living=False,
376 promote=True,
377 kn_r2_pass=False,
378 force=True,
379 preserve_shared=True,
380 )
381 == MigrateClass.UPDATED
382 )
383
384
385 def test_preserve_shared_assets_argparse_accepted() -> None:
386 from cli.main import build_parser
387
388 ns = build_parser().parse_args(["init", "--preserve-shared-assets", "--non-interactive"])
389 assert ns.preserve_shared_assets is True
390
391
392 def test_migrate_argparse_unknown_flag() -> None:
393 from cli.main import main
394
395 assert main(["init", "--migrate", "--not-a-real-flag"]) == 1
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