P24-02: 操作と通知の全受入

This commit is contained in:
Agent Control Runner
2026-10-17 14:07:06 +09:00
parent 9641ed52de
commit bd3a134051
28 changed files with 23898 additions and 187 deletions
+36 -36
View File
@@ -65,18 +65,18 @@ def compute_sha256(path: Path) -> str:
return hashlib.sha256(path.read_bytes()).hexdigest()
def get_actual_git_head() -> str:
env_head = os.environ.get("CANDIDATE_COMMIT", os.environ.get("TARGET_HEAD"))
if env_head:
cleaned = env_head.strip().lower()
if HEX_COMMIT_PATTERN.match(cleaned) and not (set(cleaned) <= {"0"} or set(cleaned) <= {"f"}):
return cleaned
def get_actual_git_head(root: Path = None) -> str:
root = root if root is not None else Path(__file__).resolve().parents[1]
try:
import subprocess
res = subprocess.run(["git", "rev-parse", "HEAD"], capture_output=True, text=True, timeout=5)
res = subprocess.run(["git", "rev-parse", "HEAD"], cwd=root, capture_output=True, text=True, timeout=5)
if res.returncode == 0:
h = res.stdout.strip().lower()
if HEX_COMMIT_PATTERN.match(h) and not (set(h) <= {"0"} or set(h) <= {"f"}):
for name in ("CANDIDATE_COMMIT", "TARGET_HEAD"):
if name in os.environ and os.environ[name].strip().lower() != h:
print(f"ERROR: {name} mismatch with real git HEAD {h}", file=sys.stderr)
return ""
return h
except Exception:
pass
@@ -84,15 +84,11 @@ def get_actual_git_head() -> str:
def get_required_suite_roster(root: Path) -> list[str]:
tests_dir = root / "tests"
if not tests_dir.is_dir():
return []
roster = ["tests/run_tests.gd"]
for p in sorted(tests_dir.rglob("test*.gd")):
rel = p.relative_to(root).as_posix()
if "export" not in p.relative_to(root).parts and rel != "tests/s0/test_kernel_and_save.gd":
roster.append(rel)
return sorted(list(set(roster)))
import importlib.util
spec = importlib.util.spec_from_file_location("p24_required_roster", root / "tools/run_all_tests.py")
module = importlib.util.module_from_spec(spec)
spec.loader.exec_module(module)
return module.roster(root)
def find_index_file(custom_path: str = None) -> Path:
@@ -376,53 +372,57 @@ def _validate_condition_artifact_content(
required_suites = get_required_suite_roster(repo_root)
total_required = len(required_suites)
reported_suites = full_roster.get("total_suites", full_roster.get("suites_count", full_roster.get("suites")))
if isinstance(reported_suites, int):
if reported_suites < total_required:
return False, f"Full roster reports only {reported_suites} suites, but repository requires at least {total_required} suites"
for field in ("total_suites", "suites_count"):
if field in full_roster and (type(full_roster[field]) is not int or full_roster[field] != total_required):
return False, f"Full roster {field} must equal {total_required}"
roster_failures = full_roster.get("failures")
if roster_failures is not None and (isinstance(roster_failures, bool) or not isinstance(roster_failures, int) or roster_failures != 0):
if "failures" in full_roster and (type(roster_failures) is not int or roster_failures != 0):
return False, f"Full roster regression reported failures: {roster_failures!r}"
# Check suite entries if provided in full_roster or referenced all-tests artifact
suite_entries = full_roster.get("results", full_roster.get("rows", full_roster.get("all_tests")))
if not suite_entries and "rows" in full_roster and full_roster["rows"] in validated_artifacts:
rows_art = validated_artifacts[full_roster["rows"]]
if isinstance(suite_entries, str) and suite_entries in validated_artifacts:
rows_art = validated_artifacts[suite_entries]
try:
suite_entries = json.loads(rows_art["file"].read_text(encoding="utf-8"))
except Exception:
suite_entries = None
if not isinstance(suite_entries, list) or not suite_entries:
return False, "Full roster requires a non-empty suite list"
if isinstance(suite_entries, list) and suite_entries:
seen_suites = set()
for entry in suite_entries:
if not isinstance(entry, dict):
continue
return False, "Full roster row must be a dict"
s_path = entry.get("test", entry.get("suite", entry.get("path")))
if not isinstance(s_path, str) or not s_path:
return False, "Full roster row missing suite path"
if s_path not in required_suites:
return False, f"Unknown full roster suite: {s_path}"
if s_path in seen_suites:
return False, f"Duplicate full roster suite: {s_path}"
if s_path:
seen_suites.add(s_path)
p = repo_root / s_path
if not p.is_file():
return False, f"Full roster suite does not exist on disk: {s_path}"
expected_sha = entry.get("source_sha256")
if expected_sha and HEX64_PATTERN.match(expected_sha):
if not isinstance(expected_sha, str) or not HEX64_PATTERN.fullmatch(expected_sha):
return False, f"Full roster missing or invalid source_sha256: {s_path}"
if expected_sha:
actual_sha = compute_sha256(p)
if actual_sha != expected_sha:
return False, f"Full roster source SHA256 mismatch for {s_path}: expected {expected_sha}, got {actual_sha}"
e_fails = entry.get("failures", entry.get("reported_failures", 0))
if isinstance(e_fails, int) and e_fails != 0:
e_fails = entry.get("failures")
if type(e_fails) is not int or e_fails != 0:
return False, f"Full roster suite {s_path} failed with {e_fails} failures"
e_passed = entry.get("passed", entry.get("completion", {}).get("passed") if isinstance(entry.get("completion"), dict) else None)
if e_passed is False:
return False, f"Full roster suite {s_path} marked passed: false"
if entry.get("passed") is not True:
return False, f"Full roster suite {s_path} passed must be True"
missing_from_roster = set(required_suites) - seen_suites
if missing_from_roster and len(seen_suites) >= total_required:
pass
elif missing_from_roster and len(suite_entries) >= total_required:
pass
elif missing_from_roster:
if seen_suites != set(required_suites):
return False, f"Full roster execution missing required suites: {sorted(list(missing_from_roster))[:5]}"
return True, ""
@@ -474,7 +474,7 @@ def verify_evidence(index_path: Path) -> bool:
actual_head = get_actual_git_head()
if not actual_head:
print("ERROR: Unable to resolve verified actual candidate git HEAD from environment (CANDIDATE_COMMIT/TARGET_HEAD) or git repository", file=sys.stderr)
print("ERROR: Unable to verify real repository git HEAD; git failed or CANDIDATE_COMMIT/TARGET_HEAD mismatched", file=sys.stderr)
return False
if candidate_commit_norm != actual_head:
+27 -4
View File
@@ -1,10 +1,17 @@
{
"schema_version": 1,
"suite_policies": {
"tests/ui/test_operations_notification.gd": {
"source_sha256": "9c26386c529b8c8135639e8d0f0ed390da7139d64ff1e268975b05948cab9147",
"ordinary_cleanup_prepared": true,
"ordinary_control_labels": ["single"],
"tests/s4/test_original_relationship_model.gd": {
"source_sha256": "c74b243dbbb54dba9bbc165e29508cca910f0beb63e32d25ebbb8ea606dccce9",
"ordinary_cleanup_prepared": false,
"ordinary_control_labels": [],
"old_bounded_teardown": false,
"new_audio_QA": false
},
"tests/s4/test_original_relationship_runtime.gd": {
"source_sha256": "c56bdb99dd43a4e7ceb3ca2861e8db39972c5c4e805126a77e2f7da32ca5bdc9",
"ordinary_cleanup_prepared": false,
"ordinary_control_labels": [],
"old_bounded_teardown": false,
"new_audio_QA": false
},
@@ -398,6 +405,22 @@
],
"old_bounded_teardown": false,
"new_audio_QA": false
},
"tests/ui/test_p08_normal_placement_ops.gd": {
"source_sha256": "d3c246d34f8fd66f6ed237e42a86df89ebaf481bc9dee143b4de8d654ce0eb2e",
"ordinary_cleanup_prepared": true,
"ordinary_control_labels": [
"single"
],
"old_bounded_teardown": false,
"new_audio_QA": false
},
"tests/ui/test_operations_notification.gd": {
"source_sha256": "9c26386c529b8c8135639e8d0f0ed390da7139d64ff1e268975b05948cab9147",
"ordinary_cleanup_prepared": true,
"ordinary_control_labels": ["single"],
"old_bounded_teardown": false,
"new_audio_QA": false
}
},
"outside_routes": {