Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 6 additions & 0 deletions .console/log.md
Original file line number Diff line number Diff line change
Expand Up @@ -339,3 +339,9 @@ Created profile yamls for each with lazygit git pane and standard helpers.
## 2026-05-23 — Genericize fleet-repo ref + standardize hook

- Genericized SyncingSolution ref in .custodian/config.yaml comment (public repo; private fleet layer must not be named). Standardized .hooks/pre-push.

## 2026-05-24 — Platform tab anchors at PlatformManifest + export CL_ANCHOR

- launcher._multi_pane_block: cross-repo group tab cwd → PlatformManifest (was bare ~/Documents/GitHub). git-watcher still spans all group repos.
- bootstrap.get_claude_command: wrapper now exports CL_ANCHOR=<cwd> so OC-launched sessions satisfy the CL guard hooks (which now hard-require CL_ANCHOR, no CWD fallback). Single-repo tabs anchor at their repo; group tab at PlatformManifest.
- Added tests/test_anchor_launch.py (2 tests). NOTE: pre-existing test_watcher_pane.py failures are unrelated (confirmed on clean main).
5 changes: 5 additions & 0 deletions src/operator_console/bootstrap.py
Original file line number Diff line number Diff line change
Expand Up @@ -138,9 +138,14 @@ def get_claude_command(

sf = str(session_file).replace("'", "'\\''")
pd = str(project_dir).replace("'", "'\\''")
# Anchor the session at its cwd (a manifest for group tabs, the repo for
# single-repo tabs). ContextLifecycle's guard hooks hard-require CL_ANCHOR
# and refuse to fall back to CWD, so it must be exported before launch.
ca = str(cwd.resolve()).replace("'", "'\\''")

script = (
"#!/usr/bin/env bash\n"
f"export CL_ANCHOR='{ca}'\n"
f"SESSION_FILE='{sf}'\n"
f"PROJECT_DIR='{pd}'\n"
"_save_session() {\n"
Expand Down
13 changes: 9 additions & 4 deletions src/operator_console/launcher.py
Original file line number Diff line number Diff line change
Expand Up @@ -110,20 +110,25 @@ def _multi_pane_block(
indent: str = " ",
tab_name: str | None = None,
) -> str:
safe_cwd = str(_GITHUB_DIR).replace("'", "'\\''")
# Cross-repo group tabs anchor at PlatformManifest (the ecosystem manifest /
# cognition host) rather than the bare workspace root — so the session has a
# valid CL_ANCHOR (see bootstrap.get_claude_command) instead of an
# un-anchored ~/Documents/GitHub. The git-watcher still spans all group repos.
group_cwd = _GITHUB_DIR / "PlatformManifest"
safe_cwd = str(group_cwd).replace("'", "'\\''")
session_key = tab_name or _multi_tab_name(profiles)
i = indent

claude_cmd = get_claude_command(
profiles[0], Path(profiles[0]["repo_root"]),
console_dir=console_dir, session_key=session_key, claude_cwd=_GITHUB_DIR,
console_dir=console_dir, session_key=session_key, claude_cwd=group_cwd,
)
codex_cmd = get_codex_command(
profiles[0], _GITHUB_DIR,
profiles[0], group_cwd,
console_dir=console_dir, session_key=session_key,
)
aider_cmd = get_aider_command(
profiles[0], _GITHUB_DIR,
profiles[0], group_cwd,
console_dir=console_dir, session_key=session_key,
)

Expand Down
45 changes: 45 additions & 0 deletions tests/test_anchor_launch.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
# SPDX-License-Identifier: Proprietary
# Copyright (C) 2026 ProtocolWarden
"""Launch anchoring: OC-launched Claude sessions must export CL_ANCHOR, and the
cross-repo group tab must anchor at PlatformManifest (not the bare workspace root)."""

from __future__ import annotations

from pathlib import Path

from operator_console.bootstrap import get_claude_command
from operator_console.launcher import _multi_pane_block


def _console_dir(tmp_path: Path) -> Path:
cd = tmp_path / "console"
(cd / "config" / "profiles").mkdir(parents=True)
return cd


def test_claude_wrapper_exports_cl_anchor_equal_to_cwd(tmp_path):
console_dir = _console_dir(tmp_path)
anchor = tmp_path / "PlatformManifest"
cmd = get_claude_command(
{"name": "platform", "repo_root": str(tmp_path / "repo")},
tmp_path / "repo",
console_dir=console_dir,
session_key="platform",
claude_cwd=anchor,
)
# cmd == "bash '<script>'" — read the generated wrapper
script_path = cmd.split("'")[1]
script = Path(script_path).read_text(encoding="utf-8")
assert f"export CL_ANCHOR='{anchor.resolve()}'" in script


def test_group_tab_anchors_at_platform_manifest(tmp_path):
console_dir = _console_dir(tmp_path)
profiles = [
{"name": "a", "repo_root": str(tmp_path / "A")},
{"name": "b", "repo_root": str(tmp_path / "B")},
]
block = _multi_pane_block(profiles, console_dir=console_dir, tab_name="platform")
# Panes cd into PlatformManifest, not the bare ~/Documents/GitHub root.
assert "PlatformManifest" in block
assert "cd '" in block and "/PlatformManifest'" in block
Loading