~ruther/agent-environments

ab69ea113061c57676a1241ee39e332246b4078f — František Boháček a month ago 33260b4
Check shared dirs: link, target, manifest, and removal on undeclare
1 files changed, 59 insertions(+), 0 deletions(-)

M checks/default.nix
M checks/default.nix => checks/default.nix +59 -0
@@ 265,6 265,49 @@ in
    [ "$(cat $s/CLAUDE.md)" = edited ] || fail "user file modified"
  '';

  # A shared directory is a managed link to the other launcher's copy. The copy
  # is created on demand, so the link works whichever launcher starts first, and
  # undeclaring the share removes the link but never the data behind it.
  shared-dirs =
    let
      withShare = share: {
        claude.enable = true;
        launchers.claude-work.agent = "claude";
        launchers.claude-pers = {
          agent = "claude";
          sharedDirs = share;
        };
      };
      sharing = mkEnvironment {
        inherit pkgs;
        modules = [
          stubs
          (withShare { projects = "claude-work"; })
        ];
      };
      alone = mkEnvironment {
        inherit pkgs;
        modules = [
          stubs
          (withShare { })
        ];
      };
      work = "$HOME/.local/agent-environments/claude-work";
      pers = "$HOME/.local/agent-environments/claude-pers";
    in
    runTest "shared-dirs" ''
      ${sharing.launchers.claude-pers}/bin/claude-pers
      [ -d ${work}/projects ] || fail "shared target not created"
      [ "$(readlink ${pers}/projects)" = ${work}/projects ] || fail "link: $(readlink ${pers}/projects)"
      grep -qx projects ${pers}/.agent-environments-managed || fail "link not in manifest"
      echo data >${pers}/projects/session.jsonl
      [ "$(cat ${work}/projects/session.jsonl)" = data ] || fail "write did not land in the shared dir"

      ${alone.launchers.claude-pers}/bin/claude-pers
      [ ! -L ${pers}/projects ] || fail "undeclared share still linked"
      [ "$(cat ${work}/projects/session.jsonl)" = data ] || fail "shared data removed with the link"
    '';

  # The launcher's own bookkeeping paths must not write through symlinks into
  # files or directories outside the state dir.
  no-writes-through-symlinks = runTest "no-writes-through-symlinks" ''


@@ 439,5 482,21 @@ in
          src = skillSrc "x";
        }).drvPath
      ).success;
    # A shared dir must name another, existing launcher and a safe path component.
    assert lib.all (share: fails [
      stubs
      {
        claude.enable = true;
        launchers.x.agent = "claude";
        launchers.y = {
          agent = "claude";
          sharedDirs = share;
        };
      }
    ]) [
      { projects = "nope"; }
      { projects = "y"; }
      { "../escape" = "x"; }
    ];
    pkgs.runCommand "agent-environments-eval-errors" { } "touch $out";
}