test(issue-5): phase 1 delegation characterization suite

The three lib.sh delegation helpers (delegate_submit_job,
delegate_publish_event, start_watchdog) had zero direct test coverage, so a
"full suite green" gate would have been vacuous for the Phase 1 extraction.
Add the regression oracle before the move:

- C-01..C-09 golden tests for the shell helpers: the non-fatal contract (empty
  job id, missing script, non-zero interpreter exit), exact argv forwarding,
  stdout-is-the-job-id-alone, and the daemon's PID/dedup/log-path behaviour.
  All are parametrized over MAM_DELEGATION_PY so the legacy and python bodies
  must agree.
- CLI contract snapshots taken from the argparse parsers rather than --help
  text, so wording stays free to change but option names, choices and defaults
  do not.
- Auto-completion invariant guards for the three surfaces that decide job
  status: EVENT_TO_STATUS, the wrapper's exit-code mapping, and reconcile.sh's
  terminal mutation literals.
- conftest: PV-2/PV-3 session guards asserting that every non-delegation
  lib_py.* module resolves to the real skills tree and that no temp-tree
  sys.path entry contributes a lib_py package. MAM_ENV_FILE is pointed at an
  absent file inside the sandbox so delegation code can never inject the real
  .mam.env into a test process.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01UrDNJkHmm18uu7n7JwV5Qd
This commit is contained in:
GodopuandClaude Opus 5 committed 2026-09-06 15:33:37 +09:00
1 parent fc0bd96d8a
commit 265a1f6c3d
7 files changed
+531

No files matched your search

+62
View File
@@ -42,6 +42,10 @@ def mam_sandbox(tmp_path, monkeypatch):
monkeypatch.setenv("LOCAL_BIN", str(tmp_path / ".local" / "bin"))
monkeypatch.setenv("AGENT_SESSIONS_YAML", str(yaml_path))
monkeypatch.setenv("WORKSPACE_ROOT", str(tmp_path))
# D6 (issue #5 phase 1): point the delegation env loader at a file that does not
# exist, so importing or running delegation code inside a sandbox never injects the
# real .mam.env into this process's os.environ.
monkeypatch.setenv("MAM_ENV_FILE", str(tmp_path / ".mam.env.absent"))
monkeypatch.delenv("HERDR_SESSION_NAME", raising=False)
monkeypatch.delenv("HERDR_SERVER_NAME", raising=False)
monkeypatch.delenv("HERDR_WORKSPACE_ID", raising=False)
@@ -1044,3 +1048,61 @@ print(str(uuid.uuid4()))
monkeypatch.setenv("PATH", f"{bin_dir}:{new_path}")
return bin_dir
# ==============================================================================
# Issue #5 Phase 1 — tree provenance guards (PV-2 / PV-3)
#
# The delegation shims bind the `lib_py.delegation` package to the tree next to
# themselves. Two invariants must hold for the whole session, and both are
# checked once at teardown so a violation anywhere in the suite is caught:
#
# PV-2 every imported `lib_py.*` module OTHER than the delegation package
# resolves to the real skills tree (a sandbox copy must never shadow
# lib_py.agents / layout / state / ...).
# PV-3 no sys.path entry under pytest's basetemp contributes a `lib_py`
# package directory. (Sandbox *scripts* dirs are fine: they contain no
# lib_py, which is why test_tier1_unit.py's inserts stay legal.)
# ==============================================================================
@pytest.fixture(scope="session", autouse=True)
def delegation_tree_provenance_guard(tmp_path_factory):
yield
real_skills = Path(SKILLS_DIR).resolve()
basetemp = Path(tmp_path_factory.getbasetemp()).resolve()
def _under(path, root):
try:
Path(path).resolve().relative_to(root)
return True
except (ValueError, OSError):
return False
# --- PV-2 -----------------------------------------------------------------
offenders = []
for name, mod in sorted(sys.modules.items()):
if name != "lib_py" and not name.startswith("lib_py."):
continue
if name == "lib_py.delegation" or name.startswith("lib_py.delegation."):
continue # delegation may legitimately bind to a sandbox copy (D2/PV-1)
origin = getattr(mod, "__file__", None)
if not origin:
continue
if not _under(origin, real_skills):
offenders.append("{} -> {}".format(name, origin))
assert not offenders, (
"PV-2: non-delegation lib_py modules were loaded from outside the real skills "
"tree ({}):\n {}".format(real_skills, "\n ".join(offenders))
)
# --- PV-3 -----------------------------------------------------------------
polluted = [
entry for entry in sys.path
if entry and _under(entry, basetemp) and (Path(entry) / "lib_py").is_dir()
]
assert not polluted, (
"PV-3: sys.path carries temp-tree entries that provide a lib_py package "
"(these would shadow the real tree for any later first-time import):\n "
+ "\n ".join(polluted)
)