1"""Workspace-anchored path resolution for every piece of connector state.
3CONNECT-02 defect 1 (proposal V1 §7). Before this module the state directory
4was a module-level ``Path(os.environ.get(..., ".option_c/orchestration/oc_connect"))``
5evaluated once at import time and interpreted against the *current working
6directory*. Two failures followed from the one expression: a send issued from
7any subdirectory looked for connector state in a directory that has none and
8died with ``Telegram destination 'operator' is not configured``, and the value
9could not be re-resolved after import even when the process changed directory.
11Resolution here is per call and anchored by marker walk, the way OCT's own
12project-root discovery works:
141. an absolute ``OC_CONNECT_STATE_DIR`` wins outright (an explicit operator
15 override needs no discovery);
162. otherwise walk upward from *start* for ``.option_c_workspace.json`` — the
17 workspace marker wins even when a nearer subproject carries its own
18 ``.option_c/``, because connector authority is workspace-wide, not
203. failing that, the nearest ancestor holding an ``.option_c/`` directory;
214. failing that, raise. Guessing a root would put operator grants somewhere the
22 operator cannot find them.
24A *relative* ``OC_CONNECT_STATE_DIR`` resolves against the workspace root, not
25the cwd — reinstating cwd-relative behaviour through the override would
26reintroduce exactly the defect this module removes.
30- oc_diagnostics (_dbg structured logger)
32from __future__
import annotations
35from pathlib
import Path
37from oc_diagnostics
import _dbg
39WORKSPACE_MARKER =
".option_c_workspace.json"
40PROJECT_MARKER =
".option_c"
41STATE_SUBPATH = (
".option_c",
"orchestration",
"oc_connect")
42CREDENTIAL_SUBPATH = (
".option_c",
"connector_secrets.json")
48COMMS_ANCHOR =
"oc_chat"
49COMMS_SUBPATH = (
".option_c",
"comms")
51STATE_DIR_ENV =
"OC_CONNECT_STATE_DIR"
52CREDENTIAL_ENV =
"OC_CONNECT_SECRETS"
56 """No workspace or Option C project marker was found above the start path."""
60 candidate = Path(path).expanduser()
62 return candidate.resolve()
64 return candidate.absolute()
68 """Return the workspace root above *start*, failing loudly when absent."""
69 func =
"find_workspace_root"
70 origin =
_resolved(start
if start
is not None else Path.cwd())
71 project_fallback: Path |
None =
None
72 for parent
in (origin, *origin.parents):
73 if (parent / WORKSPACE_MARKER).is_file():
75 "OC_CONNECT.WORKSPACE",
77 f
"anchored on workspace marker root={parent}",
81 if project_fallback
is None and (parent / PROJECT_MARKER).is_dir():
82 project_fallback = parent
83 if project_fallback
is not None:
85 "OC_CONNECT.WORKSPACE",
87 f
"anchored on project marker root={project_fallback}",
90 return project_fallback
92 f
"no Option C workspace marker ({WORKSPACE_MARKER}) or project marker "
93 f
"({PROJECT_MARKER}/) was found at or above {origin}"
98 """Return an explicit root when given, otherwise discover one."""
104def _from_env(name: str, root: str | Path |
None) -> Path |
None:
105 raw = os.environ.get(name)
106 if raw
is None or not raw.strip():
108 override = Path(raw.strip()).expanduser()
109 if override.is_absolute():
115 """Return the connector state directory for grants and destinations."""
116 override =
_from_env(STATE_DIR_ENV, root)
117 if override
is not None:
123 """Return the credential sidecar path, anchored the same way as state."""
124 override =
_from_env(CREDENTIAL_ENV, root)
125 if override
is not None:
131 """Return the comms bus root for this workspace."""
133 if (anchor / COMMS_ANCHOR).is_dir():
134 return anchor.joinpath(COMMS_ANCHOR, *COMMS_SUBPATH)
135 return anchor.joinpath(*COMMS_SUBPATH)
142 "CREDENTIAL_SUBPATH",
147 "WorkspaceNotFoundError",
150 "find_workspace_root",
Path comms_root(str|Path|None root=None)
Path|None _from_env(str name, str|Path|None root)
Path credential_path(str|Path|None root=None)
Path resolve_root(str|Path|None root=None)
Path state_dir(str|Path|None root=None)
Path find_workspace_root(str|Path|None start=None)
Path _resolved(str|Path path)