abhigyanpatwari/GitNexus · critical · SandboxError

sanitized task identity drifted between graph preparation an

Error message

sanitized task identity drifted between graph preparation and arm clone ({self.sanitized_head} != {sanitized_head})

What it means

Anti-replay guard in SanitizedGraphSnapshot.materialize. A sanitized graph is built from exactly one parentless commit identified by sanitized_head; when an arm clone later materializes the graph, it must pass the same sanitized_head. A mismatch means the clone is being asked to consume a graph built from a different sanitized history, which would invalidate the benchmark's isolation invariant.

Source

Thrown at eval/workflow_bench/sanitized_graph.py:68

@dataclass(frozen=True)
class SanitizedGraphSnapshot:
    """A graph whose only source was one deterministic parentless commit."""

    assets: TaskAssetSnapshot
    sanitized_head: str

    @property
    def digest(self) -> str:
        return self.assets.digest

    @property
    def manifest_digest(self) -> str:
        return self.assets.manifest_digest

    def materialize(self, clone: Path, *, sanitized_head: str) -> None:
        if sanitized_head != self.sanitized_head:
            raise SandboxError(
                "sanitized task identity drifted between graph preparation and arm clone "
                f"({self.sanitized_head} != {sanitized_head})"
            )
        self.assets.materialize(clone)


def _is_restricted_path(value: str) -> bool:
    relative = PurePosixPath(value)
    if relative.is_absolute() or not relative.parts or ".." in relative.parts:
        return False
    return (
        relative.parts[0] == ".gitnexus" or relative == HIDDEN_HARNESS_PATH or HIDDEN_HARNESS_PATH in relative.parents
    )


def validate_no_prebuilt_graph_assets(task: Mapping[str, Any]) -> None:
    """Reject declarations that could reintroduce an unsanitized graph/oracle."""

View on GitHub (pinned to d540b00184)

Solutions

  1. Treat this as a harness bug: capture both sanitized_head values in the error and trace where each was computed (graph preparation vs arm clone setup).
  2. Ensure graph preparation and arm clone share the same SanitizedGraphSnapshot instance and pass snapshot.sanitized_head through unchanged.
  3. Discard any cached graph snapshot when the task's sanitized history changes (do not reuse across re-runs with different sanitization inputs).
  4. Add an assertion at the call site that builds the snapshot so the drift is caught at the earliest possible point.

Example fix

# before: graph built once, reused across two sanitized heads
graph = prepare_sanitized_graph(snapshot_a)
clone_b.materialize(graph, sanitized_head=head_b)  # drift
# after: materialize with the snapshot's own head
clone.materialize(graph, sanitized_head=graph.sanitized_head)
Defensive patterns

Strategy: validation

Validate before calling

# This is a harness-internal invariant; validate at the call site.
from eval.workflow_bench.sanitized_graph import SanitizedGraphSnapshot

def materialize_safe(snapshot: SanitizedGraphSnapshot, clone, sanitized_head: str) -> None:
    if sanitized_head != snapshot.sanitized_head:
        raise RuntimeError(
            f"identity drift: snapshot={snapshot.sanitized_head} call={sanitized_head}; "
            "rebuild the graph for this clone"
        )
    snapshot.materialize(clone, sanitized_head=sanitized_head)

Type guard

def head_matches(snapshot, sanitized_head: str) -> bool:
    return sanitized_head == snapshot.sanitized_head

Try / catch

try:
    snapshot.materialize(clone, sanitized_head=head)
except SandboxError as exc:
    if "identity drifted" in str(exc):
        log.error("graph/clone head mismatch; rebuild the sanitized graph for this clone")
    raise

Prevention

When it happens

Trigger: materialize() is called with a sanitized_head string that differs from the snapshot's frozen sanitized_head. This is a harness-internal wiring error (graph built from commit A, arm run parameterized with commit B), not a user typo, since both values are computed by the harness.

Common situations: Two task snapshots with different sanitized heads sharing one graph object by mistake; a partial re-run that reuses a cached SanitizedGraphSnapshot against a freshly sanitized task; a refactor that decoupled graph-build from arm-clone parameterization; concurrent runs mixing up state.

Related errors


AI-assisted analysis of abhigyanpatwari/GitNexus@d540b00184 (2026-08-12). Data as JSON: /api/errors/6cb49e122fc1ab36. Report an issue: GitHub.