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
Original file line number Diff line number Diff line change
Expand Up @@ -59,7 +59,7 @@ echo "Starting login process..."
# Check if Docker credentials exist
if [[ -n "$DSTACK_DOCKER_USERNAME" && -n "$DSTACK_DOCKER_PASSWORD" ]]; then
echo "Docker credentials found"
DOCKER_REGISTRY_TARGET="${DSTACK_DOCKER_REGISTRY:-docker.io}"
DOCKER_REGISTRY_TARGET="${DSTACK_DOCKER_REGISTRY:-ghcr.io}"
echo "Target Docker registry: $DOCKER_REGISTRY_TARGET"

# Check if already logged in
Expand Down
23 changes: 13 additions & 10 deletions packages/challenges/agent-challenge/golden/live-registry-refs.json
Original file line number Diff line number Diff line change
@@ -1,25 +1,28 @@
{
"schema": "harbor-independence/live-registry-refs@1",
"note": "SIDE manifest (NOT the frozen golden dataset digest). Maps a small deterministic subset of Terminal-Bench 2.1 task_ids to PULLABLE, digest-pinned registry refs published to the miner's public Docker Hub namespace so an in-CVM DooD orchestrator can `docker pull` them for a live smoke E2E. This file does NOT affect golden/dataset-digest.json, its per-task content_digest_sha256, the canonical_content_digest_sha256, or the canonical compose/measurement. Resolution is opt-in and fail-closed (see agent_challenge.canonical.live_registry).",
"note": "SIDE manifest (NOT the frozen golden dataset digest). Maps a small deterministic subset of Terminal-Bench 2.1 task_ids to PULLABLE, digest-pinned GHCR refs under ghcr.io/baseintelligence so an in-CVM DooD orchestrator can pull them for a live smoke E2E. D6: only GHCR shipping refs are accepted (see agent_challenge.canonical.live_registry). This file does NOT affect golden/dataset-digest.json, its per-task content_digest_sha256, the canonical_content_digest_sha256, or the canonical compose/measurement. Resolution is opt-in and fail-closed. OPS_REQUIRED: orchestrator_image digest is the T1 local buildx manifest (not yet pushed to GHCR); task image digests retain prior content hashes under the new GHCR path pattern and must be republished to GHCR before live pull succeeds.",
"dataset": "terminal-bench/terminal-bench-2-1",
"namespace": "docker.io/mathiiss",
"orchestrator_image": "docker.io/mathiiss/agent-challenge-canonical@sha256:02331f0909f617e333f113be376d353770a673669946bcddaac3c53cbde7c9d8",
"orchestrator_image_source": "services.yaml build-canonical (`uv run python -m agent_challenge.canonical.build --build`) built reproducibly (BuildKit SOURCE_DATE_EPOCH + rewrite-timestamp, provenance/sbom off) and pushed to the miner Docker Hub namespace",
"namespace": "ghcr.io/baseintelligence",
"orchestrator_image": "ghcr.io/baseintelligence/agent-challenge-canonical@sha256:ea399cee1b3c9015024918a6070901e6b7b4bee432c8afe1e7dd40a95547e0bc",
"orchestrator_image_source": "T1 local agent-recipe buildx (SOURCE_DATE_EPOCH + rewrite-timestamp, provenance/sbom off) → manifest sha256:ea399cee1b3c9015024918a6070901e6b7b4bee432c8afe1e7dd40a95547e0bc; tag was ghcr.io/baseintelligence/agent-challenge-canonical:t1-local. OPS_REQUIRED: replace with the first published GHCR digest from agent-recipe publish-eval-image.yml before T3/prod pin.",
"tasks": {
"adaptive-rejection-sampler": {
"registry_ref": "docker.io/mathiiss/agent-challenge-tb21-adaptive-rejection-sampler@sha256:7c8bd5835f19506222805de68d65f83d6cca5b502f7d71dc5e2d9d4dd447c0c8",
"registry_ref": "ghcr.io/baseintelligence/agent-challenge-tb21-adaptive-rejection-sampler@sha256:7c8bd5835f19506222805de68d65f83d6cca5b502f7d71dc5e2d9d4dd447c0c8",
"source_ref": "alexgshaw/adaptive-rejection-sampler:20251031",
"content_digest_sha256": "bcaa2399985cd57666018025846289ab25e193ae0dd8fb7f0ffab2410c24d4de"
"content_digest_sha256": "bcaa2399985cd57666018025846289ab25e193ae0dd8fb7f0ffab2410c24d4de",
"ops_status": "BLOCKED_REPUBLISH — path retargeted to GHCR; digest is historical content pin until image is pushed under ghcr.io/baseintelligence/"
},
"bn-fit-modify": {
"registry_ref": "docker.io/mathiiss/agent-challenge-tb21-bn-fit-modify@sha256:c0371862a0861f282eb206471452ea9aa8d494e3687bdc8167c96ab39d73db50",
"registry_ref": "ghcr.io/baseintelligence/agent-challenge-tb21-bn-fit-modify@sha256:c0371862a0861f282eb206471452ea9aa8d494e3687bdc8167c96ab39d73db50",
"source_ref": "alexgshaw/bn-fit-modify:20251031",
"content_digest_sha256": "b5f9644970c17ad9ddb46b7266f7bcd87c761d77d7e6f55d7cfe7284d5ff66e9"
"content_digest_sha256": "b5f9644970c17ad9ddb46b7266f7bcd87c761d77d7e6f55d7cfe7284d5ff66e9",
"ops_status": "BLOCKED_REPUBLISH — path retargeted to GHCR; digest is historical content pin until image is pushed under ghcr.io/baseintelligence/"
},
"break-filter-js-from-html": {
"registry_ref": "docker.io/mathiiss/agent-challenge-tb21-break-filter-js-from-html@sha256:2a5bd51bab582993befc1a24252c03af673cb1db7cf7d0b2195d66a42a3872a7",
"registry_ref": "ghcr.io/baseintelligence/agent-challenge-tb21-break-filter-js-from-html@sha256:2a5bd51bab582993befc1a24252c03af673cb1db7cf7d0b2195d66a42a3872a7",
"source_ref": "alexgshaw/break-filter-js-from-html:20251031",
"content_digest_sha256": "678008d1a4fd1e6e1b9b3cc9a327219fe4b410a31eafc52e9099bbf947eea600"
"content_digest_sha256": "678008d1a4fd1e6e1b9b3cc9a327219fe4b410a31eafc52e9099bbf947eea600",
"ops_status": "BLOCKED_REPUBLISH — path retargeted to GHCR; digest is historical content pin until image is pushed under ghcr.io/baseintelligence/"
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -4,12 +4,15 @@
Terminal-Bench task by a *bare content digest* (``harbor_registry_ref =
"sha256:<64hex>"``) with NO repository, so an in-CVM DooD orchestrator cannot
``docker pull`` it. For a live smoke E2E a small deterministic subset of task
images is published to the miner's public Docker Hub namespace as pullable,
digest-pinned refs and recorded in a SEPARATE side manifest
images is published to the org GHCR namespace (``ghcr.io/baseintelligence/…``)
as pullable, digest-pinned refs and recorded in a SEPARATE side manifest
(``golden/live-registry-refs.json``). That side manifest never touches
``dataset-digest.json`` -- the frozen content digests and the canonical
measurement stay byte-identical.

D6: shipping live refs MUST be GHCR. ``docker.io`` and the retired
``mathiiss`` namespace are rejected fail-closed at parse time.

Resolution is **opt-in and fail-closed**: with no live manifest configured
(no explicit path, no env var) callers get NO live refs and fall back to the
existing per-task behavior, so flag-off / offline runs are byte-identical. When a
Expand Down Expand Up @@ -50,6 +53,9 @@
# and an un-namespaced ``name@sha256`` so only a real registry ref is accepted.
_PULLABLE_REF_RE = re.compile(r"^(?=[^@]*/)[A-Za-z0-9][\w.\-/:]*@sha256:[0-9a-f]{64}$")

#: Shipping live-registry refs must live under this registry host prefix (D6).
LIVE_REGISTRY_HOST_PREFIX = "ghcr.io/"


class LiveRegistryError(ValueError):
"""The live-registry side manifest is missing, malformed, or not pullable."""
Expand Down Expand Up @@ -83,6 +89,32 @@ def assert_pullable_ref(ref: Any, *, what: str = "registry ref") -> str:
return ref # type: ignore[return-value]


def assert_live_registry_ref(ref: Any, *, what: str = "registry ref") -> str:
"""Return ``ref`` if it is a GHCR digest-pinned shipping ref (D6), else raise.

Builds on :func:`assert_pullable_ref`, then rejects ``docker.io``, the
retired ``mathiiss`` namespace, and any non-GHCR host so live smoke never
pins a Hub image again.
"""

pinned = assert_pullable_ref(ref, what=what)
lowered = pinned.lower()
if "mathiiss" in lowered:
raise LiveRegistryError(
f"{what} must not use the retired mathiiss namespace (D6), got {pinned!r}"
)
if lowered.startswith("docker.io/") or lowered.startswith("index.docker.io/"):
raise LiveRegistryError(
f"{what} must not use docker.io (D6 — GHCR only), got {pinned!r}"
)
if not lowered.startswith(LIVE_REGISTRY_HOST_PREFIX):
raise LiveRegistryError(
f"{what} must be a GHCR digest-pinned ref "
f"({LIVE_REGISTRY_HOST_PREFIX}…@sha256:<64hex>), got {pinned!r}"
)
return pinned


@dataclass(frozen=True)
class LiveRegistry:
"""Parsed live-registry side manifest.
Expand Down Expand Up @@ -111,23 +143,24 @@ def __bool__(self) -> bool:


def _ref_from_entry(task_id: str, entry: Any) -> str:
"""Extract + validate the pullable ref from a manifest task entry."""
"""Extract + validate the pullable GHCR ref from a manifest task entry."""

if isinstance(entry, str):
ref = entry
elif isinstance(entry, Mapping):
ref = entry.get("registry_ref")
else:
raise LiveRegistryError(f"live-registry task {task_id!r} is not a string or mapping")
return assert_pullable_ref(ref, what=f"live-registry ref for task {task_id!r}")
return assert_live_registry_ref(ref, what=f"live-registry ref for task {task_id!r}")


def parse_live_registry(data: Mapping[str, Any]) -> LiveRegistry:
"""Parse + validate a live-registry side-manifest document.

Every task ref must be a pullable ``repo@sha256`` ref (a bare content digest
or floating tag is rejected). ``orchestrator_image``, when present, must be
pullable too. Task keys are normalized to their bare name.
Every task ref must be a GHCR pullable ``repo@sha256`` ref (a bare content
digest, floating tag, ``docker.io``, or retired ``mathiiss`` namespace is
rejected). ``orchestrator_image``, when present, must be GHCR-pullable too.
Task keys are normalized to their bare name.
"""

if not isinstance(data, Mapping):
Expand All @@ -143,7 +176,7 @@ def parse_live_registry(data: Mapping[str, Any]) -> LiveRegistry:

orchestrator = data.get("orchestrator_image")
if orchestrator is not None:
orchestrator = assert_pullable_ref(orchestrator, what="orchestrator_image")
orchestrator = assert_live_registry_ref(orchestrator, what="orchestrator_image")

return LiveRegistry(orchestrator_image=orchestrator, task_refs=task_refs, raw=dict(data))

Expand Down Expand Up @@ -203,9 +236,11 @@ def resolve_live_registry_refs(
"DEFAULT_LIVE_REGISTRY_PATH",
"LIVE_REGISTRY_ENV",
"LIVE_REGISTRY_FILENAME",
"LIVE_REGISTRY_HOST_PREFIX",
"LIVE_REGISTRY_SCHEMA",
"LiveRegistry",
"LiveRegistryError",
"assert_live_registry_ref",
"assert_pullable_ref",
"is_pullable_ref",
"load_live_registry",
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -686,6 +686,18 @@ async def _load_review_envelope_for_run(
) -> Mapping[str, Any] | str | None:
"""Load receipted review-domain envelope for score-chain re-verify."""

materials = await _load_review_materials_for_run(session, run)
if materials is None:
return None
return materials.get("envelope")


async def _load_review_materials_for_run(
session: AsyncSession,
run: EvalRun,
) -> dict[str, Any] | None:
"""Load envelope + verification outcome (package residual) for score chain."""

submission = await session.scalar(
select(AgentSubmission).where(AgentSubmission.id == run.submission_id)
)
Expand All @@ -703,9 +715,32 @@ async def _load_review_envelope_for_run(
):
return None
envelope = assignment.review_report_envelope_json
outcome_raw = assignment.review_verification_outcome_json
outcome: Mapping[str, Any] | None = None
if isinstance(outcome_raw, str) and outcome_raw.strip():
try:
import json as _json

parsed = _json.loads(outcome_raw)
if isinstance(parsed, dict):
outcome = parsed
except (TypeError, ValueError):
outcome = None
elif isinstance(outcome_raw, dict):
outcome = outcome_raw
residual = None
if isinstance(outcome, Mapping):
pr = outcome.get("package_residual")
if isinstance(pr, dict):
residual = pr
env_out: Mapping[str, Any] | str | None = None
if isinstance(envelope, str) and envelope:
return envelope
return None
env_out = envelope
elif isinstance(envelope, dict):
env_out = envelope
if env_out is None and residual is None and outcome is None:
return None
return {"envelope": env_out, "outcome": outcome, "package_residual": residual}


async def _run_gate_with_deadline(
Expand All @@ -719,6 +754,8 @@ async def _run_gate_with_deadline(
deadline_seconds: float,
dual_flags_on: bool = False,
review_envelope: Mapping[str, Any] | str | bytes | None = None,
review_outcome: Mapping[str, Any] | None = None,
package_residual: Mapping[str, Any] | None = None,
key_release_grant: Mapping[str, Any] | None = None,
agent_llm_kwargs: Mapping[str, Any] | None = None,
settings: ChallengeSettings | None = None,
Expand Down Expand Up @@ -756,6 +793,8 @@ def _decide() -> AttestationDecision:
settings_dual_flags_on=True,
eval_plan=plan,
review_envelope=review_envelope,
review_outcome=review_outcome,
package_residual=package_residual,
key_release_grant=key_release_grant,
key_granted_flag=key_granted,
score_binding=binding,
Expand Down Expand Up @@ -892,8 +931,14 @@ async def process_direct_eval_result(
review_envelope: Mapping[str, Any] | str | None = None
key_release_grant: Mapping[str, Any] | None = None
agent_llm_kwargs: dict[str, Any] | None = None
review_outcome: Mapping[str, Any] | None = None
package_residual: Mapping[str, Any] | None = None
if dual_flags_on:
review_envelope = await _load_review_envelope_for_run(session, current)
materials = await _load_review_materials_for_run(session, current)
if materials is not None:
review_envelope = materials.get("envelope") # type: ignore[assignment]
review_outcome = materials.get("outcome") # type: ignore[assignment]
package_residual = materials.get("package_residual") # type: ignore[assignment]
key_release_grant = _key_release_grant_from_result(
plan=plan,
validated=validated,
Expand Down Expand Up @@ -931,6 +976,8 @@ async def process_direct_eval_result(
deadline_seconds=settings.eval_result_verifier_deadline_seconds,
dual_flags_on=dual_flags_on,
review_envelope=review_envelope,
review_outcome=review_outcome,
package_residual=package_residual,
key_release_grant=key_release_grant,
agent_llm_kwargs=agent_llm_kwargs,
settings=settings,
Expand Down
Loading
Loading