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
5 changes: 3 additions & 2 deletions loopx/control_plane/turn_driver/operation_requests.py
Original file line number Diff line number Diff line change
Expand Up @@ -12,9 +12,10 @@
import json
import math
from pathlib import Path
import re
from typing import Any, Mapping

from loopx.control_plane.content_digest import BARE_SHA256_PATTERN


def request_reference_instruction(configured: bool) -> str:
if not configured:
Expand Down Expand Up @@ -101,7 +102,7 @@ def operation_request_source(
if path is None and sha256 is None:
return None
if (path is None or not path.is_absolute() or not isinstance(sha256, str)
or re.fullmatch(r"[0-9a-f]{64}", sha256) is None):
or BARE_SHA256_PATTERN.fullmatch(sha256) is None):
raise ValueError("frozen operation request requires an absolute file and SHA-256")
source = OperationRequestSource(path, sha256)
source.read() # Fail before SDK startup; no file path/content enters errors.
Expand Down
1 change: 1 addition & 0 deletions tests/architecture/test_content_digest_single_owner.py
Original file line number Diff line number Diff line change
Expand Up @@ -200,6 +200,7 @@
"loopx.control_plane.todos.completion_validation_store",
"loopx.control_plane.todos.machine_section_projection",
"loopx.control_plane.turn_driver.codex_sessions",
"loopx.control_plane.turn_driver.operation_requests",
"loopx.control_plane.work_items.governed_transition_proposal",
"loopx.control_plane.work_items.progress_review_policy",
"loopx.domain_packs.issue_fix",
Expand Down
16 changes: 15 additions & 1 deletion tests/control_plane/test_operation_requests.py
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,9 @@
from examples import operation_action_fixtures as fixtures
from loopx.control_plane.turn_driver.codex_operation_host import operation_tool_handler
from loopx.control_plane.turn_driver.codex_sessions import _store_codex_cli_session
from loopx.control_plane.turn_driver.operation_requests import operation_request_source
from loopx.control_plane.turn_driver.operation_requests import (
OperationRequestSource, operation_request_source,
)
from tests.control_plane.test_dsh_operation_connection import (
DshOperationConnection, PROFILE, call, envelope, event, exchange, opened,
)
Expand Down Expand Up @@ -174,6 +176,18 @@ def test_operator_file_requires_strict_bounded_json(tmp_path, raw):
operation_request_source(path, hashlib.sha256(raw).hexdigest())


@pytest.mark.parametrize("digest", [
"", "a" * 63, "a" * 65, "A" * 64, "g" * 64,
"sha256:" + "a" * 64, "a" * 64 + "\n", "\n" + "a" * 64,
"a" * 64 + " ", " " + "a" * 64, b"a" * 64, 64,
])
def test_invalid_digest_shape_is_refused_before_frozen_file_io(tmp_path, monkeypatch, digest):
monkeypatch.setattr(OperationRequestSource, "read",
lambda *_: pytest.fail("invalid digest must not read the frozen file"))
with pytest.raises(ValueError, match="requires an absolute file and SHA-256"):
operation_request_source(tmp_path / "request.json", digest)


@pytest.mark.parametrize("path,digest", [(None, "a" * 64), ("relative.json", "a" * 64),
("/absent.json", None), ("/absent.json", "bad")])
def test_source_configuration_cannot_be_partial_or_model_selected(path, digest):
Expand Down
Loading