G7 증명과 G8 clean-head 승격 준비
This commit is contained in:
parent
94c681d450
commit
5221f79e3f
52 changed files with 6876 additions and 506 deletions
|
|
@ -265,6 +265,153 @@ def topology() -> dict[str, object]:
|
|||
}
|
||||
|
||||
|
||||
def windows_topology() -> dict[str, object]:
|
||||
process_metrics = {
|
||||
"api": {
|
||||
"rss_bytes": 200_000,
|
||||
"peak_rss_bytes": 240_000,
|
||||
"cpu_time_seconds": 2.0,
|
||||
"cpu_percent": 3.0,
|
||||
"handles": 40,
|
||||
"threads": 8,
|
||||
},
|
||||
"cloudflared": {
|
||||
"rss_bytes": 100_000,
|
||||
"peak_rss_bytes": 120_000,
|
||||
"cpu_time_seconds": 1.0,
|
||||
"cpu_percent": 1.5,
|
||||
"handles": 20,
|
||||
"threads": 4,
|
||||
},
|
||||
}
|
||||
process_tcp = {
|
||||
"api": {"connections": 2, "established": 1, "listeners": 1},
|
||||
"cloudflared": {"connections": 4, "established": 4, "listeners": 0},
|
||||
}
|
||||
samples = [
|
||||
{
|
||||
"sequence": index + 1,
|
||||
"observed_at_utc": "2026-08-07T00:30:00Z",
|
||||
"processes": copy.deepcopy(process_metrics),
|
||||
"process_tcp": copy.deepcopy(process_tcp),
|
||||
"host_tcp": {"connections": 20},
|
||||
"api_listener": {
|
||||
"port": 8001,
|
||||
"owned_listener_count": 1,
|
||||
"conflicting_listener_count": 0,
|
||||
},
|
||||
}
|
||||
for index in range(31)
|
||||
]
|
||||
return {
|
||||
"schema_version": "vignette.g7-topology-evidence.v1",
|
||||
"topology_mode": "windows_host",
|
||||
"status": "passed",
|
||||
"started_at_utc": "2026-08-07T00:00:00Z",
|
||||
"ended_at_utc": "2026-08-07T01:00:00Z",
|
||||
"scope": {
|
||||
"metadata_only": True,
|
||||
"raw_command_output_retained": False,
|
||||
"socket_endpoints_retained": False,
|
||||
"request_payloads_retained": False,
|
||||
"audio_retained": False,
|
||||
"transcripts_retained": False,
|
||||
"topology_boundary": "windows_host_api_and_cloudflared_processes",
|
||||
"configured_listener_port_retained": True,
|
||||
"cloudflare_edge": {
|
||||
"internal_queue_measured": False,
|
||||
"evidence_boundary": "separate_external_artifact_required",
|
||||
},
|
||||
},
|
||||
"requested": {
|
||||
"public_host": "api.example.test",
|
||||
"repo_root": r"D:\workspace\vignette",
|
||||
"git_sha": "5" * 40,
|
||||
"source_pin": {
|
||||
"git_tree_sha": "8" * 40,
|
||||
"script_sha256": {
|
||||
"runner": "9" * 64,
|
||||
"collector": "a" * 64,
|
||||
"checker": "b" * 64,
|
||||
},
|
||||
"runtime_dependencies": {"psutil": "6.1.1"},
|
||||
},
|
||||
"samples": 31,
|
||||
"interval_seconds": 100.0,
|
||||
"api_listen_port": 8001,
|
||||
"roles": {
|
||||
"api": {
|
||||
"pid": 301,
|
||||
"expected_executable_name": "python.exe",
|
||||
"expected_executable_sha256": "3" * 64,
|
||||
"expected_cwd": r"D:\workspace\vignette\apps\api",
|
||||
},
|
||||
"cloudflared": {
|
||||
"pid": 302,
|
||||
"expected_executable_name": "cloudflared.exe",
|
||||
"expected_executable_sha256": "4" * 64,
|
||||
"expected_cwd": r"D:\workspace\vignette",
|
||||
},
|
||||
},
|
||||
},
|
||||
"source_provenance": {
|
||||
"detached_head": True,
|
||||
"tracked_clean": True,
|
||||
"git_sha": "5" * 40,
|
||||
"git_tree_sha": "8" * 40,
|
||||
"script_sha256": {
|
||||
"runner": "9" * 64,
|
||||
"collector": "a" * 64,
|
||||
"checker": "b" * 64,
|
||||
},
|
||||
"runtime_dependencies": {"psutil": "6.1.1"},
|
||||
},
|
||||
"samples_completed": 31,
|
||||
"targets": {
|
||||
"api": {
|
||||
"role": "api",
|
||||
"pid": 301,
|
||||
"started_at": "2026-08-06T00:00:00Z",
|
||||
"executable_name": "python.exe",
|
||||
"executable_sha256": "3" * 64,
|
||||
"command_line_sha256": "6" * 64,
|
||||
"git_sha": "5" * 40,
|
||||
"cwd": r"D:\workspace\vignette\apps\api",
|
||||
},
|
||||
"cloudflared": {
|
||||
"role": "cloudflared",
|
||||
"pid": 302,
|
||||
"started_at": "2026-08-06T00:00:00Z",
|
||||
"executable_name": "cloudflared.exe",
|
||||
"executable_sha256": "4" * 64,
|
||||
"command_line_sha256": "7" * 64,
|
||||
"git_sha": "5" * 40,
|
||||
"cwd": r"D:\workspace\vignette",
|
||||
},
|
||||
},
|
||||
"samples": samples,
|
||||
"summary": {
|
||||
"processes": {
|
||||
role: {
|
||||
**{f"{field}_max": value for field, value in metrics.items()},
|
||||
**{
|
||||
f"tcp_{field}_max": value
|
||||
for field, value in process_tcp[role].items()
|
||||
},
|
||||
}
|
||||
for role, metrics in process_metrics.items()
|
||||
},
|
||||
"api_listener": {
|
||||
"port": 8001,
|
||||
"owned_listener_count_min": 1,
|
||||
"conflicting_listener_count_max": 0,
|
||||
},
|
||||
"host_tcp": {"connections_max": 20},
|
||||
},
|
||||
"failure_type": None,
|
||||
}
|
||||
|
||||
|
||||
class G7ExternalProofTests(unittest.TestCase):
|
||||
@classmethod
|
||||
def setUpClass(cls) -> None:
|
||||
|
|
@ -286,6 +433,97 @@ class G7ExternalProofTests(unittest.TestCase):
|
|||
self.assertTrue(gain["passed"])
|
||||
self.assertGreaterEqual(gain["held_out_participants"], 30)
|
||||
|
||||
def test_windows_host_topology_passes_without_weakening_compose(self) -> None:
|
||||
errors: list[str] = []
|
||||
payload = windows_topology()
|
||||
compose_payload = topology()
|
||||
compose_payload["topology_mode"] = "linux_compose"
|
||||
|
||||
self.checker.validate_topology(payload, errors)
|
||||
self.checker.validate_topology(compose_payload, errors)
|
||||
self.checker.validate_binding(public_soak(), runtime(), payload, errors)
|
||||
|
||||
self.assertEqual([], errors)
|
||||
|
||||
def test_windows_host_identity_listener_and_summary_fail_closed(self) -> None:
|
||||
cases = (
|
||||
(
|
||||
lambda payload: payload["targets"]["api"].__setitem__("pid", 999),
|
||||
"topology:api_pid_pin",
|
||||
),
|
||||
(
|
||||
lambda payload: payload["samples"][0]["api_listener"].__setitem__(
|
||||
"owned_listener_count", 0
|
||||
),
|
||||
"topology:listener_owner_sample",
|
||||
),
|
||||
(
|
||||
lambda payload: payload["summary"]["processes"]["api"].__setitem__(
|
||||
"rss_bytes_max", 1
|
||||
),
|
||||
"topology:api_rss_bytes_max",
|
||||
),
|
||||
)
|
||||
for mutate, failure in cases:
|
||||
with self.subTest(failure=failure):
|
||||
payload = windows_topology()
|
||||
mutate(payload)
|
||||
errors: list[str] = []
|
||||
self.checker.validate_topology(payload, errors)
|
||||
self.assertIn(failure, errors)
|
||||
|
||||
def test_windows_source_and_toolchain_provenance_fail_closed(self) -> None:
|
||||
cases = (
|
||||
(
|
||||
lambda payload: payload["source_provenance"].__setitem__(
|
||||
"detached_head", False
|
||||
),
|
||||
"topology:windows_detached_head",
|
||||
),
|
||||
(
|
||||
lambda payload: payload["source_provenance"].__setitem__(
|
||||
"tracked_clean", False
|
||||
),
|
||||
"topology:windows_tracked_clean",
|
||||
),
|
||||
(
|
||||
lambda payload: payload["source_provenance"].__setitem__(
|
||||
"git_tree_sha", "c" * 40
|
||||
),
|
||||
"topology:windows_source_git_tree_sha",
|
||||
),
|
||||
(
|
||||
lambda payload: payload["source_provenance"]["script_sha256"].__setitem__(
|
||||
"runner", "c" * 64
|
||||
),
|
||||
"topology:windows_source_script_sha256",
|
||||
),
|
||||
(
|
||||
lambda payload: payload["source_provenance"][
|
||||
"runtime_dependencies"
|
||||
].__setitem__("psutil", "6.1.0"),
|
||||
"topology:windows_source_psutil_version",
|
||||
),
|
||||
)
|
||||
for mutate, failure in cases:
|
||||
with self.subTest(failure=failure):
|
||||
payload = windows_topology()
|
||||
mutate(payload)
|
||||
errors: list[str] = []
|
||||
self.checker.validate_topology(payload, errors)
|
||||
self.assertIn(failure, errors)
|
||||
|
||||
unpinned_version = windows_topology()
|
||||
unpinned_version["requested"]["source_pin"]["runtime_dependencies"][
|
||||
"psutil"
|
||||
] = "6.1.0"
|
||||
unpinned_version["source_provenance"]["runtime_dependencies"][
|
||||
"psutil"
|
||||
] = "6.1.0"
|
||||
errors = []
|
||||
self.checker.validate_topology(unpinned_version, errors)
|
||||
self.assertIn("topology:windows_psutil_version_pin", errors)
|
||||
|
||||
def test_decided_local_stack_is_accepted(self) -> None:
|
||||
"""2026-08-08 소유자 결정: 노트북 faster-whisper STT + MeloTTS TTS (둘 다 MIT)."""
|
||||
|
||||
|
|
@ -353,6 +591,44 @@ class G7ExternalProofTests(unittest.TestCase):
|
|||
self.assertIn("voice_soak:physical_microphone_used", errors)
|
||||
self.assertIn("binding:no_concurrent_overlap", errors)
|
||||
|
||||
def test_concurrent_window_requires_full_3000_second_intersection(self) -> None:
|
||||
exact_errors: list[str] = []
|
||||
exact_voice = public_soak()
|
||||
exact_voice["ended_at_utc"] = "2026-08-07T00:50:00Z"
|
||||
exact_runtime = runtime()
|
||||
exact_runtime["ended_at_utc"] = "2026-08-07T00:50:00Z"
|
||||
exact_topology = topology()
|
||||
exact_topology["ended_at_utc"] = "2026-08-07T00:50:00Z"
|
||||
|
||||
self.checker.validate_binding(
|
||||
exact_voice,
|
||||
exact_runtime,
|
||||
exact_topology,
|
||||
exact_errors,
|
||||
)
|
||||
|
||||
self.assertEqual([], exact_errors)
|
||||
|
||||
short_errors: list[str] = []
|
||||
short_voice = public_soak()
|
||||
short_voice["ended_at_utc"] = "2026-08-07T00:50:00Z"
|
||||
short_runtime = runtime()
|
||||
short_runtime["started_at_utc"] = "2026-08-07T00:00:01Z"
|
||||
short_runtime["ended_at_utc"] = "2026-08-07T00:50:00Z"
|
||||
short_topology = topology()
|
||||
short_topology["ended_at_utc"] = "2026-08-07T00:50:00Z"
|
||||
self.checker.validate_binding(
|
||||
short_voice,
|
||||
short_runtime,
|
||||
short_topology,
|
||||
short_errors,
|
||||
)
|
||||
|
||||
self.assertIn(
|
||||
"binding:concurrent_overlap_below_3000_seconds",
|
||||
short_errors,
|
||||
)
|
||||
|
||||
def test_runtime_fallback_and_short_topology_fail(self) -> None:
|
||||
errors: list[str] = []
|
||||
runtime_payload = runtime()
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue