54def load_octrc(project_root: Path) -> tuple[dict, list[str]]:
55 """Load the project's OCT RC config (``.octrc.json`` / ``octrc.json``).
57 FS-539: prefers ``.option_c/octrc.json`` when present, falls back to
58 the legacy ``<project_root>/.octrc.json`` otherwise. The concrete
59 path is resolved via :func:`oct.core.option_c_dir.resolve_octrc`.
63 (config, errors) : tuple[dict, list[str]]
64 ``config`` is the parsed JSON object, or an empty dict if the file
65 is missing / unreadable / malformed / not a JSON object.
66 ``errors`` is a list of human-readable error strings; empty when
67 the file was absent or loaded successfully.
69 octrc_path = resolve_octrc(project_root)
70 label = octrc_path.name
71 if not octrc_path.is_file():
75 raw = octrc_path.read_text(encoding=
"utf-8")
76 except OSError
as exc:
77 return {}, [f
"{label} unreadable: {exc}"]
80 data = json.loads(raw)
81 except json.JSONDecodeError
as exc:
82 return {}, [f
"{label} has invalid JSON: {exc}"]
84 if not isinstance(data, dict):
86 f
"{label} must be a JSON object, got "
87 f
"{type(data).__name__}"