fix: warn before missing CC2 source failure
This commit is contained in:
parent
4a4d4061f3
commit
99cc652fe1
|
|
@ -12,7 +12,7 @@ import sys
|
||||||
from dataclasses import dataclass
|
from dataclasses import dataclass
|
||||||
from datetime import datetime, timezone
|
from datetime import datetime, timezone
|
||||||
from pathlib import Path
|
from pathlib import Path
|
||||||
from typing import Any
|
from typing import Any, Callable
|
||||||
|
|
||||||
REQUIRED_ITEM_FIELDS = [
|
REQUIRED_ITEM_FIELDS = [
|
||||||
"id",
|
"id",
|
||||||
|
|
@ -94,26 +94,42 @@ def slugify(text: str, limit: int = 54) -> str:
|
||||||
return slug[:limit].strip("-") or "item"
|
return slug[:limit].strip("-") or "item"
|
||||||
|
|
||||||
|
|
||||||
def find_source_omx(repo_root: Path, env_value: str | None = None) -> Path:
|
def source_omx_candidates(repo_root: Path, env_value: str | None = None) -> list[Path]:
|
||||||
candidates: list[Path] = []
|
candidates: list[Path] = []
|
||||||
env = env_value if env_value is not None else os.environ.get("CC2_SOURCE_OMX")
|
env = env_value if env_value is not None else os.environ.get("CC2_SOURCE_OMX")
|
||||||
if env:
|
if env:
|
||||||
candidates.append(Path(env).expanduser())
|
candidates.append(Path(env).expanduser())
|
||||||
candidates.append(repo_root / ".omx")
|
candidates.append(repo_root / ".omx")
|
||||||
candidates.extend(parent / ".omx" for parent in repo_root.parents)
|
candidates.extend(parent / ".omx" for parent in repo_root.parents)
|
||||||
|
return list(dict.fromkeys(candidate.resolve() for candidate in candidates))
|
||||||
|
|
||||||
|
|
||||||
|
def format_source_omx_search(paths: list[Path]) -> str:
|
||||||
|
searched = "\n".join(f" - {path}" for path in paths)
|
||||||
|
return (
|
||||||
|
f"searched:\n{searched}\n"
|
||||||
|
"Recovery: restore the frozen CC2 source bundle or Set CC2_SOURCE_OMX=/path/to/.omx. "
|
||||||
|
"The source root must contain plans/claw-code-2-0-adaptive-plan.md and research/."
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
def find_source_omx(
|
||||||
|
repo_root: Path,
|
||||||
|
env_value: str | None = None,
|
||||||
|
warn: Callable[[str], None] | None = None,
|
||||||
|
) -> Path:
|
||||||
checked: list[Path] = []
|
checked: list[Path] = []
|
||||||
for candidate in dict.fromkeys(candidate.resolve() for candidate in candidates):
|
for candidate in source_omx_candidates(repo_root, env_value):
|
||||||
checked.append(candidate)
|
checked.append(candidate)
|
||||||
if (candidate / "plans" / "claw-code-2-0-adaptive-plan.md").exists() and (candidate / "research").exists():
|
if (candidate / "plans" / "claw-code-2-0-adaptive-plan.md").exists() and (candidate / "research").exists():
|
||||||
return candidate
|
return candidate
|
||||||
|
|
||||||
searched = "\n".join(f" - {path}" for path in checked)
|
search_message = format_source_omx_search(checked)
|
||||||
|
if warn is not None:
|
||||||
|
warn(f"warning: searching for CC2 source .omx\n{search_message}")
|
||||||
raise FileNotFoundError(
|
raise FileNotFoundError(
|
||||||
"could not locate source .omx with plans/claw-code-2-0-adaptive-plan.md and research/\n"
|
"could not locate source .omx with plans/claw-code-2-0-adaptive-plan.md and research/\n"
|
||||||
f"searched:\n{searched}\n"
|
f"{search_message}"
|
||||||
"Recovery: restore the frozen CC2 source bundle or Set CC2_SOURCE_OMX=/path/to/.omx. "
|
|
||||||
"The source root must contain plans/claw-code-2-0-adaptive-plan.md and research/."
|
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
|
|
@ -425,9 +441,9 @@ def validate_board(board: dict[str, Any]) -> list[str]:
|
||||||
return errors
|
return errors
|
||||||
|
|
||||||
|
|
||||||
def build_board(repo_root: Path) -> dict[str, Any]:
|
def build_board(repo_root: Path, warn: Callable[[str], None] | None = None) -> dict[str, Any]:
|
||||||
roadmap_path = repo_root / "ROADMAP.md"
|
roadmap_path = repo_root / "ROADMAP.md"
|
||||||
source_omx = find_source_omx(repo_root)
|
source_omx = find_source_omx(repo_root, warn=warn)
|
||||||
research = source_omx / "research"
|
research = source_omx / "research"
|
||||||
plan_path = source_omx / "plans" / "claw-code-2-0-adaptive-plan.md"
|
plan_path = source_omx / "plans" / "claw-code-2-0-adaptive-plan.md"
|
||||||
headings, actions = parse_roadmap(roadmap_path)
|
headings, actions = parse_roadmap(roadmap_path)
|
||||||
|
|
@ -510,7 +526,7 @@ def main() -> int:
|
||||||
repo_root = args.repo_root.resolve()
|
repo_root = args.repo_root.resolve()
|
||||||
out_dir = args.out_dir or (repo_root / ".omx" / "cc2")
|
out_dir = args.out_dir or (repo_root / ".omx" / "cc2")
|
||||||
try:
|
try:
|
||||||
board = build_board(repo_root)
|
board = build_board(repo_root, warn=lambda message: print(message, file=sys.stderr))
|
||||||
except FileNotFoundError as exc:
|
except FileNotFoundError as exc:
|
||||||
print(f"error: {exc}", file=sys.stderr)
|
print(f"error: {exc}", file=sys.stderr)
|
||||||
return 1
|
return 1
|
||||||
|
|
|
||||||
|
|
@ -39,6 +39,23 @@ class CC2BoardGeneratorTests(unittest.TestCase):
|
||||||
self.assertIn("plans/claw-code-2-0-adaptive-plan.md", message)
|
self.assertIn("plans/claw-code-2-0-adaptive-plan.md", message)
|
||||||
self.assertIn("research/", message)
|
self.assertIn("research/", message)
|
||||||
|
|
||||||
|
def test_missing_source_omx_emits_warning_before_failure(self) -> None:
|
||||||
|
generator = load_generator()
|
||||||
|
warnings: list[str] = []
|
||||||
|
with tempfile.TemporaryDirectory() as temp_dir:
|
||||||
|
repo_root = Path(temp_dir) / "repo"
|
||||||
|
repo_root.mkdir()
|
||||||
|
|
||||||
|
with self.assertRaises(FileNotFoundError):
|
||||||
|
generator.find_source_omx(repo_root, warn=warnings.append)
|
||||||
|
|
||||||
|
self.assertEqual(1, len(warnings))
|
||||||
|
warning = warnings[0]
|
||||||
|
self.assertIn("warning: searching for CC2 source .omx", warning)
|
||||||
|
self.assertIn("searched:", warning)
|
||||||
|
self.assertIn(str(repo_root / ".omx"), warning)
|
||||||
|
self.assertIn("Set CC2_SOURCE_OMX", warning)
|
||||||
|
|
||||||
def test_source_omx_can_be_supplied_by_env(self) -> None:
|
def test_source_omx_can_be_supplied_by_env(self) -> None:
|
||||||
generator = load_generator()
|
generator = load_generator()
|
||||||
with tempfile.TemporaryDirectory() as temp_dir:
|
with tempfile.TemporaryDirectory() as temp_dir:
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue