주기 회기 검증 격리 보강

This commit is contained in:
Yun Chan 2026-08-10 01:25:45 +09:00
parent 9a7dd98cb6
commit 4cd354881f
10 changed files with 181 additions and 69 deletions

View file

@ -906,6 +906,65 @@ class PeriodicRunner:
values["E2E_PERIODIC_LEARNER_RESULT"] = str(result)
return _clean_child_env(values)
def _prepare_returned_practice_fixture(
self,
*,
stage: str,
base_url: str,
database_url: str,
database_admin_url: str,
stack_env: Mapping[str, str],
fixture_path: Path,
) -> dict[str, Any]:
harness_env = _clean_child_env(
{
"PYTHONPATH": str(REPO_ROOT / "apps" / "api"),
"PYTHONUTF8": "1",
}
)
self._run(
stage,
[
self.config.python_exe,
"-X",
"utf8",
str(FIXTURE_SCRIPT),
"--api-base-url",
f"{base_url}/api",
"--database-url",
database_url,
"--database-admin-url",
database_admin_url,
"--practice-internal-token",
stack_env["VIGNETTE_PRACTICE_INTERNAL_TOKEN"],
"--transfer-internal-token",
stack_env["VIGNETTE_CALIBRATION_TRANSFER_INTERNAL_TOKEN"],
"--out",
str(fixture_path),
"--request-timeout",
str(self.config.request_timeout),
"--review-poll-timeout",
str(self.config.readiness_timeout),
],
env=harness_env,
timeout=20 * 60,
)
fixture = json.loads(fixture_path.read_text(encoding="utf-8"))
setup = fixture.get("setup_proof") or {}
expected_setup = {
"source_review_ready": True,
"follow_up_review_ready": True,
"distinct_persona": True,
"initial_runtime_observation_count": 0,
"initial_actual_transfer_execution_count": 0,
}
if any(setup.get(key) != value for key, value in expected_setup.items()):
raise GateError("returned-practice fixture setup proof is incomplete")
return {
**expected_setup,
"fixture_sha256": _sha256_path(fixture_path),
}
def _validate_periodic_result(self, result_path: Path) -> dict[str, Any]:
try:
payload = json.loads(result_path.read_text(encoding="utf-8"))
@ -1070,7 +1129,8 @@ class PeriodicRunner:
self.state.temp_dir = temp_dir
self.state.env_file = temp_dir / "stack.env"
self.state.override_file = temp_dir / "compose.periodic.yml"
fixture_path = temp_dir / "returned-practice-fixture.json"
periodic_fixture_path = temp_dir / "periodic-learner-fixture.json"
returned_fixture_path = temp_dir / "returned-regression-fixture.json"
periodic_result = temp_dir / "periodic-result.json"
stack_env = build_stack_environment(runtime)
write_env_file(self.state.env_file, stack_env)
@ -1184,53 +1244,15 @@ class PeriodicRunner:
"database_sentinel_sha256": sentinel_sha,
}
harness_env = _clean_child_env(
{
"PYTHONPATH": str(REPO_ROOT / "apps" / "api"),
"PYTHONUTF8": "1",
}
)
self._run(
"prepare_returned_practice_fixture",
[
self.config.python_exe,
"-X",
"utf8",
str(FIXTURE_SCRIPT),
"--api-base-url",
f"{base_url}/api",
"--database-url",
database_url,
"--database-admin-url",
database_admin_url,
"--practice-internal-token",
stack_env["VIGNETTE_PRACTICE_INTERNAL_TOKEN"],
"--transfer-internal-token",
stack_env["VIGNETTE_CALIBRATION_TRANSFER_INTERNAL_TOKEN"],
"--out",
str(fixture_path),
"--request-timeout",
str(self.config.request_timeout),
"--review-poll-timeout",
str(self.config.readiness_timeout),
],
env=harness_env,
timeout=20 * 60,
)
fixture = json.loads(fixture_path.read_text(encoding="utf-8"))
setup = fixture.get("setup_proof") or {}
expected_setup = {
"source_review_ready": True,
"follow_up_review_ready": True,
"distinct_persona": True,
"initial_runtime_observation_count": 0,
"initial_actual_transfer_execution_count": 0,
}
if any(setup.get(key) != value for key, value in expected_setup.items()):
raise GateError("returned-practice fixture setup proof is incomplete")
self.state.proof["fixture"] = {
**expected_setup,
"fixture_sha256": _sha256_path(fixture_path),
self.state.proof["fixtures"] = {
"periodic": self._prepare_returned_practice_fixture(
stage="prepare_periodic_learner_fixture",
base_url=base_url,
database_url=database_url,
database_admin_url=database_admin_url,
stack_env=stack_env,
fixture_path=periodic_fixture_path,
)
}
web_dir = REPO_ROOT / "apps" / "web"
@ -1248,7 +1270,11 @@ class PeriodicRunner:
"--reporter=line",
],
cwd=web_dir,
env=self._browser_env(runtime, fixture_path, periodic_result),
env=self._browser_env(
runtime,
periodic_fixture_path,
periodic_result,
),
timeout=25 * 60,
)
if not re.search(r"\b1 passed\b", periodic_run.stdout + periodic_run.stderr):
@ -1257,6 +1283,17 @@ class PeriodicRunner:
periodic_result
)
self.state.proof["fixtures"]["returned_regression"] = (
self._prepare_returned_practice_fixture(
stage="prepare_returned_regression_fixture",
base_url=base_url,
database_url=database_url,
database_admin_url=database_admin_url,
stack_env=stack_env,
fixture_path=returned_fixture_path,
)
)
returned_run = self._run(
"returned_practice_desktop_mobile",
[
@ -1271,7 +1308,7 @@ class PeriodicRunner:
"--reporter=line",
],
cwd=web_dir,
env=self._browser_env(runtime, fixture_path),
env=self._browser_env(runtime, returned_fixture_path),
timeout=25 * 60,
)
if not re.search(r"\b4 passed\b", returned_run.stdout + returned_run.stderr):