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
- 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).
- Ensure graph preparation and arm clone share the same SanitizedGraphSnapshot instance and pass snapshot.sanitized_head through unchanged.
- Discard any cached graph snapshot when the task's sanitized history changes (do not reuse across re-runs with different sanitization inputs).
- 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
- Always pass snapshot.sanitized_head when materializing; never recompute it separately.
- Do not cache SanitizedGraphSnapshot across differently sanitized tasks.
- Treat identity drift as a harness bug, not a config error.
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
- target-controlled {name} must not be a directory
- target .gitnexus path must be a real directory before graph
- sanitized graph metadata is not bound to the parentless task
- sanitized graph metadata does not prove a --pdg build
- transcript artifact size does not match its results row: {pa
AI-assisted analysis of abhigyanpatwari/GitNexus@d540b00184 (2026-08-12).
Data as JSON: /api/errors/6cb49e122fc1ab36.
Report an issue: GitHub.