From eed0ab7bae5fcc5d05a25cea5161dabc4f935878 Mon Sep 17 00:00:00 2001 From: JavaZero <71128095+JavaZeroo@users.noreply.github.com> Date: Tue, 27 Oct 2026 21:06:24 +0800 Subject: [PATCH 1/4] perf: run one owned hosted CPU-boundary diagnosis for #54 Fixed six-observation diagnostic, separate from the original v1 cost batches. Reviewed local head: 01ede8c6cbd68f55b95fefb9a03caadb09bf01e9. Publication tree matches the reviewed tree; default cost mode remains separate. --- .github/workflows/r54-cpu.yml | 53 +++++++++++ CMakeLists.txt | 2 +- experiments/measure/R54-CPU-HOSTED.md | 21 +++++ experiments/measure/r54_cpu_hosted.py | 58 ++++++++++++ experiments/measure/r54_hosted.py | 4 +- experiments/measure/r54_publish.py | 58 ++++++++---- experiments/measure/test_r54_cpu_hosted.py | 101 +++++++++++++++++++++ 7 files changed, 275 insertions(+), 22 deletions(-) create mode 100644 .github/workflows/r54-cpu.yml create mode 100644 experiments/measure/R54-CPU-HOSTED.md create mode 100644 experiments/measure/r54_cpu_hosted.py create mode 100644 experiments/measure/test_r54_cpu_hosted.py diff --git a/.github/workflows/r54-cpu.yml b/.github/workflows/r54-cpu.yml new file mode 100644 index 00000000..3409e31c --- /dev/null +++ b/.github/workflows/r54-cpu.yml @@ -0,0 +1,53 @@ +name: Bounded R54 hosted CPU diagnosis + +on: + push: + branches: ['perf/grapheme-cpu-hosted-r54'] + paths: + - '.github/workflows/r54-cpu.yml' + - 'experiments/measure/*.py' + +permissions: + contents: read + +concurrency: + group: r54-cpu-${{ github.ref }} + cancel-in-progress: false + +jobs: + cpu-boundary: + # Evidence or exporter fixes must not silently create replacement observations. + if: github.event.created == true + runs-on: windows-2025 + timeout-minutes: 30 + env: + PYTHONUTF8: '1' + CARGO_HOME: ${{ github.workspace }}/local/r54-cargo-home + steps: + - uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1 + with: + fetch-depth: 0 + persist-credentials: false + - uses: actions/setup-python@6d4fd22b20465ccc0f7354476588e8df7c9ca0e0 # v7.1.0 + with: + python-version: '3.12' + - name: Install fixed CPU observer + run: python -m pip install -r benchmarks/requirements.txt + - name: Select fixed compiler + run: rustup default 1.92.0 + - name: Verify build, accounting and publication boundaries + run: python -m unittest experiments.measure.test_r54_contract.CostPlanTests experiments.measure.test_r54_batch experiments.measure.test_r54_hosted experiments.measure.test_r54_cpu_diagnostic experiments.measure.test_r54_cpu_hosted -v + - name: Build audited pair and execute one six-observation diagnosis + id: diagnose + run: python -m experiments.measure.r54_cpu_hosted --out local/r54-cpu-raw + - name: Prepare the synthetic artifact whitelist + if: always() && steps.diagnose.outcome != 'skipped' + run: python -m experiments.measure.r54_publish --experiment cpu-boundary --raw local/r54-cpu-raw --out local/r54-cpu-public + - name: Preserve complete or failed synthetic evidence + if: always() && steps.diagnose.outcome != 'skipped' + uses: actions/upload-artifact@cf430e030ddbb5b0abf93d22962f4752f3646cd9 # v7.0.2 + with: + name: r54-cpu-${{ github.run_id }}-${{ github.run_attempt }} + retention-days: 30 + if-no-files-found: error + path: local/r54-cpu-public/ diff --git a/CMakeLists.txt b/CMakeLists.txt index 61d22796..b44cd3b6 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -292,7 +292,7 @@ if(BUILD_TESTING) endif() termforge_add_python_test(core -m unittest experiments.core.test_core experiments.core.test_unicode -v) termforge_add_python_test(recovery -m unittest experiments.core.test_recovery -v) - termforge_add_python_test(measure -m unittest experiments.measure.test_measure experiments.measure.test_r54_contract.CostPlanTests experiments.measure.test_r54_batch experiments.measure.test_r54_hosted experiments.measure.test_r54_cpu_diagnostic -v) + termforge_add_python_test(measure -m unittest experiments.measure.test_measure experiments.measure.test_r54_contract.CostPlanTests experiments.measure.test_r54_batch experiments.measure.test_r54_hosted experiments.measure.test_r54_cpu_diagnostic experiments.measure.test_r54_cpu_hosted -v) termforge_add_python_test(binary_paths -m unittest experiments.test_build_paths -v) termforge_add_python_test(conpty_margin_contract -m unittest experiments.conpty.test_margin_contract -v) termforge_add_python_test(benchmark_tools -m unittest discover -s benchmarks/tests -v) diff --git a/experiments/measure/R54-CPU-HOSTED.md b/experiments/measure/R54-CPU-HOSTED.md new file mode 100644 index 00000000..5ae187fd --- /dev/null +++ b/experiments/measure/R54-CPU-HOSTED.md @@ -0,0 +1,21 @@ +# R54:独立 hosted CPU 分量诊断 + +本次延续[六位置CPU边界合同](R54-CPU-DIAGNOSTIC.md),回答在一个新GitHub-hosted Windows VM中,自身区间外CPU、runner CPU与全机余额分别多大。共享工作站的[一次实际诊断](results/2026-10-27-r54-cpu-boundary/README.md)已显示这些分量可以分开记录,但其活动水平不支持成本验收;旧hosted第24位置的原因仍未知。 + +## 固定执行 + +以 PR #106 的 `beeb64496b2e2898e8619fa5e9c39f490a3891c4` 为基点。新入口为[工作流](../../.github/workflows/r54-cpu.yml),仅在分支`perf/grapheme-cpu-hosted-r54`首次创建push执行;拒绝attempt2及后续重跑。后续代码/导出器/证据修正不自动触发替代观察。原 `r54-cost.yml`、两批各112位置、原5%护栏及其失败记录不变。 + +一个新的`windows-2025` VM依次准备Python3.12、psutil7.2.2、Rust1.92.0,执行合同测试,然后[复建并审计](r54_build.py)原固定 `.3/.4` 及四次短正确性。仍是MSVC x64、Release、locked/offline、no-default-features、jobs1、独立target及两个共同harness覆盖;80/84来源文件、原锁文件、真实编译/链接日志和EXE哈希保持可追溯。它是新构建,不借用本地或上次hosted二进制身份。 + +构建/测试结束后只执行一次 `.3,.4,.4,.3,.3,.4`,全部为原interactive 22,000B、chunk1、observe0、内部CPU/墙钟至少2秒。使用未经修改的`r54_cpu_diagnostic`完整子进程/runner/全机计量,原完整输出oracle和来源前后审计继续执行。单子进程90秒、清理5秒;来源、输出、计数或退出不成立时立即停止,不补样。构建期限600秒、job30分钟;构建失败不进入诊断。 + +本次不跑空闲资格预检,不计算A/B成本比或吞吐回归,不接受或调整产品性能容差。`accounting_complete`只表示六份诊断记录完整。全机余额保留正负,可能包含其他guest活动、计数粒度和采样边界;不把它称为某个外部进程的精确CPU,也不能补造旧hosted缺失的全生命周期计数。两台宿主的绝对数值不能直接用于性能比较。 + +## 证据和公开范围 + +保留GitHub事件/run/attempt/commit、实际runner/映像、构建来源、host OS/CPU、原始计数及全部观察;分析时从提供者作业元数据独立核对身份。没有修改电源、优先级、亲和性或后台服务的步骤。物理宿主排他性与频率仍未知。 + +发布器的显式`cpu-boundary`模式只包含本次构建和六位置诊断的固定白名单,与原成本批次分开。它在任何staging之前核对本次raw schema/run/attempt/source,失败和不完整流也保留;测量、sanity及诊断入口identity的stdout/stderr字节不改;bundle内的构建identity日志保留完整内容并执行路径前缀替换。元数据中的实际长/短/转义路径改为别名,子PID及进程清单不公开;清单同时列原始/公开SHA和大小。绝不递归上传checkout、Cargo缓存或任意目录。新分支身份和schema不会被原默认发布模式接受。 + +只有下载并审核实际产物、源码/构建、全部原字节oracle和CPU等式之后,才将观察写入 #54。若此诊断支持改进后续环境计量,须另行冻结新协议与完整样本计划;不倒改原v1,也不以诊断完成代替受控A/B、allocator/projection及组件归因、预算提案和用户确认。 diff --git a/experiments/measure/r54_cpu_hosted.py b/experiments/measure/r54_cpu_hosted.py new file mode 100644 index 00000000..cda0cbac --- /dev/null +++ b/experiments/measure/r54_cpu_hosted.py @@ -0,0 +1,58 @@ +"""One fresh hosted build followed by the separately declared CPU-boundary diagnosis.""" +import argparse +import json +from pathlib import Path + +from experiments.measure.r54_batch import sha, utc, write_json +from experiments.measure.r54_build import prepare_builds +from experiments.measure.r54_contract import ROOT +from experiments.measure.r54_cpu_diagnostic import execute as diagnose +from experiments.measure.r54_hosted import hosted_identity +from experiments.measure.r54_inputs import git, require + +BRANCH = 'perf/grapheme-cpu-hosted-r54' + + +def execute(output): + output = Path(output).resolve() + require(output.is_relative_to(ROOT / 'local'), 'hosted artifacts must stay under the workspace local directory') + output.mkdir(parents=True, exist_ok=False) + manifest = dict(schema='termforge-r54-cpu-hosted-v1', status='preparing', started_utc=utc(), + source=git(ROOT, 'rev-parse', 'HEAD'), runner_sha256=sha(Path(__file__)), + workflow_sha256=sha(ROOT / '.github/workflows/r54-cpu.yml'), + performance_gate='not_evaluated', diagnostic=None) + try: + manifest['hosted_identity'] = hosted_identity(BRANCH) + write_json(output / 'hosted.json', manifest) + prepare_builds(output / 'bundle') + declaration = dict(host_kind='hosted_vm', no_known_concurrent_build_or_test=True, + known_concurrent_work=['This job completed setup, tests and both audited builds before diagnosis.', + 'GitHub runner service and operating-system background services remain.', + 'Physical host scheduling and neighboring virtual machines are unobserved.'], + purpose='One six-observation CPU-boundary diagnostic; no idle qualification or cost acceptance.') + write_json(output / 'environment-declaration.json', declaration) + manifest['status'] = 'diagnosing' + write_json(output / 'hosted.json', manifest) + result = diagnose(output / 'bundle/plan-v1.json', output / 'bundle', + output / 'environment-declaration.json', output / 'diagnostic') + manifest['diagnostic'] = result + require(result['status'] == 'accounting_complete', 'diagnosis stopped; no replacement observations') + manifest['status'] = 'accounting_complete' + except BaseException as error: + manifest.update(status='failed', error=f'{type(error).__name__}: {error}') + raise + finally: + manifest['finished_utc'] = utc() + write_json(output / 'hosted.json', manifest) + return manifest + + +def main(): + parser = argparse.ArgumentParser(description=__doc__) + parser.add_argument('--out', type=Path, required=True) + args = parser.parse_args() + print(json.dumps(execute(args.out))) + + +if __name__ == '__main__': + main() diff --git a/experiments/measure/r54_hosted.py b/experiments/measure/r54_hosted.py index a59cb725..426d2f81 100644 --- a/experiments/measure/r54_hosted.py +++ b/experiments/measure/r54_hosted.py @@ -10,7 +10,7 @@ from experiments.measure.r54_inputs import git, require -def hosted_identity(): +def hosted_identity(branch='perf/grapheme-hosted-r54'): names = ('GITHUB_ACTIONS', 'RUNNER_ENVIRONMENT', 'RUNNER_OS', 'GITHUB_REPOSITORY', 'GITHUB_SHA', 'GITHUB_REF', 'GITHUB_RUN_ID', 'GITHUB_RUN_NUMBER', 'GITHUB_RUN_ATTEMPT', 'GITHUB_EVENT_NAME', 'ImageOS', 'ImageVersion') @@ -19,7 +19,7 @@ def hosted_identity(): identity['RUNNER_OS'] == 'Windows' and os.name == 'nt', 'requires an actual GitHub-hosted Windows job') require(identity['GITHUB_REPOSITORY'] == 'JavaZeroo/termforge' and identity['GITHUB_EVENT_NAME'] == 'push', 'unexpected repository or event') - require(identity['GITHUB_REF'] == 'refs/heads/perf/grapheme-hosted-r54', 'unexpected experiment branch') + require(identity['GITHUB_REF'] == 'refs/heads/' + branch, 'unexpected experiment branch') require(identity['GITHUB_RUN_ATTEMPT'] == '1', 'a workflow rerun must not replace the first attempt') require(identity['GITHUB_SHA'] == git(ROOT, 'rev-parse', 'HEAD'), 'workflow/source identity mismatch') return identity diff --git a/experiments/measure/r54_publish.py b/experiments/measure/r54_publish.py index 5a9787a0..e3c2efd3 100644 --- a/experiments/measure/r54_publish.py +++ b/experiments/measure/r54_publish.py @@ -11,10 +11,17 @@ from experiments.measure.r54_hosted import hosted_identity from experiments.measure.r54_inputs import require +EXPERIMENTS = { + 'cost': dict(branch='perf/grapheme-hosted-r54', raw_schema='termforge-r54-hosted-v1', + publication_schema='termforge-r54-hosted-publication-v1'), + 'cpu-boundary': dict(branch='perf/grapheme-cpu-hosted-r54', raw_schema='termforge-r54-cpu-hosted-v1', + publication_schema='termforge-r54-cpu-hosted-publication-v1'), +} + def public_environment(value): """CPU counters remain; process names/PIDs and unrecognized fields do not.""" - fields = ('status', 'host', 'started_utc', 'finished_utc', 'intervals', 'declaration', 'policy', 'power_policy', 'error') + fields = ('status', 'host', 'started_utc', 'finished_utc', 'intervals', 'declaration', 'policy', 'power_policy', 'error', 'scope') result = {name: value[name] for name in fields if name in value} if 'host' in result: host = value['host'] @@ -22,9 +29,9 @@ def public_environment(value): return result -def validate_raw_identity(root, identity): +def validate_raw_identity(root, identity, schema='termforge-r54-hosted-v1'): value = json.loads((root / 'hosted.json').read_text(encoding='utf-8')) - require(value['schema'] == 'termforge-r54-hosted-v1' and value['hosted_identity'] == identity and + require(value['schema'] == schema and value['hosted_identity'] == identity and value['source'] == identity['GITHUB_SHA'], 'raw evidence belongs to another run, attempt or source') @@ -74,8 +81,9 @@ def publish_jsonl(original, replacements): return redact_paths(original, replacements), status -def selected_paths(root): +def selected_paths(root, experiment='cost'): """No recursive directory export: only named builder and runner artifacts.""" + require(experiment in EXPERIMENTS, 'unknown experiment') names = ['hosted.json', 'environment-declaration.json'] names += ['bundle/' + name for name in ('preparation.json', 'plan-v1.json', 'build-plan.json', 'build-audit.json')] for variant in ('baseline', 'candidate'): @@ -84,24 +92,35 @@ def selected_paths(root): names += [f'bundle/{variant}-{step}.{suffix}' for suffix in ('stdout', 'stderr', 'process.json')] for route in ('project', 'interactive'): names += [f'bundle/sanity-{variant}-{route}/{stream}.bin' for stream in ('stdout', 'stderr')] - for seed in SEEDS: - prefix = f'batch-{seed}/' - names += [prefix + name for name in ('manifest.json', 'runner.json', 'environment.json', 'environment-policy.json', - 'environment-declaration.json', 'samples/context.json', 'samples/manifest.json', 'samples/samples.jsonl', 'samples/summary.json')] - for directory in ('baseline-identity', 'candidate-identity', 'power-policy', 'power-policy-after'): + if experiment == 'cost': + for seed in SEEDS: + prefix = f'batch-{seed}/' + names += [prefix + name for name in ('manifest.json', 'runner.json', 'environment.json', 'environment-policy.json', + 'environment-declaration.json', 'samples/context.json', 'samples/manifest.json', 'samples/samples.jsonl', 'samples/summary.json')] + for directory in ('baseline-identity', 'candidate-identity', 'power-policy', 'power-policy-after'): + names += [prefix + directory + '/' + name for name in ('record.json', 'stdout.bin', 'stderr.bin')] + for sequence in range(112): + names += [prefix + f'samples/{sequence:03d}/' + name for name in ('record.json', 'stdout.bin', 'stderr.bin')] + else: + prefix = 'diagnostic/' + names += [prefix + name for name in ('manifest.json', 'runner.json', 'environment.json', 'binding.json', + 'plan-v1.json', 'observations/context.json', 'observations/protocol.json', 'observations/manifest.json')] + for directory in ('baseline-identity', 'candidate-identity'): names += [prefix + directory + '/' + name for name in ('record.json', 'stdout.bin', 'stderr.bin')] - for sequence in range(112): - names += [prefix + f'samples/{sequence:03d}/' + name for name in ('record.json', 'stdout.bin', 'stderr.bin')] + for sequence in range(6): + names += [prefix + f'observations/{sequence:03d}/' + name for name in ('record.json', 'stdout.bin', 'stderr.bin')] return [Path(name) for name in names if (root / name).is_file()] -def publish(root, output): - identity = hosted_identity() +def publish(root, output, experiment='cost'): + require(experiment in EXPERIMENTS, 'unknown experiment') + spec = EXPERIMENTS[experiment] + identity = hosted_identity(spec['branch']) root, output = Path(root).resolve(), Path(output).resolve() require(root.is_relative_to(ROOT / 'local') and output.is_relative_to(ROOT / 'local'), 'publication paths must stay in workspace local') require(root != output and not output.is_relative_to(root), 'public output must be separate from raw records') require(not output.exists(), 'public output already exists') - validate_raw_identity(root, identity) + validate_raw_identity(root, identity, spec['raw_schema']) staging = output.with_name(output.name + '.partial') require(staging.is_relative_to(ROOT / 'local'), 'staging path escaped workspace') staging.mkdir(parents=True, exist_ok=False) @@ -116,13 +135,13 @@ def text(value): def transform(value): if isinstance(value, dict): - return {key: transform(item) for key, item in value.items()} + return {key: transform(item) for key, item in value.items() if key != 'child_pid'} if isinstance(value, list): return [transform(item) for item in value] return text(value) if isinstance(value, str) else value inventory = [] - for relative in selected_paths(root): + for relative in selected_paths(root, experiment): source = root / relative require(source.resolve().is_relative_to(root), 'artifact path escaped raw directory') original = source.read_bytes() @@ -130,7 +149,7 @@ def transform(value): # The measurement EXE consumes only public, fixed synthetic workloads. # Preserve its raw streams even on failure. Other logs can contain host # path prefixes; redact those without discarding their full contents. - raw_sample = bool(re.fullmatch(r'(?:batch-\d+/samples/\d{3}|bundle/sanity-(?:baseline|candidate)-(?:project|interactive))/(?:stdout|stderr)\.bin', relative.as_posix())) + raw_sample = bool(re.fullmatch(r'(?:batch-\d+/samples/\d{3}|bundle/sanity-(?:baseline|candidate)-(?:project|interactive)|diagnostic/(?:observations/\d{3}|(?:baseline|candidate)-identity))/(?:stdout|stderr)\.bin', relative.as_posix())) if raw_sample: data, treatment = original, 'byte_exact_owned_synthetic_stream' elif relative.suffix == '.json': @@ -152,7 +171,7 @@ def transform(value): original_sha256=hashlib.sha256(original).hexdigest(), published_bytes=len(data), published_sha256=sha(target), treatment=treatment, jsonl_status=jsonl_status)) - write_json(staging / 'publication.json', dict(schema='termforge-r54-hosted-publication-v1', + write_json(staging / 'publication.json', dict(schema=spec['publication_schema'], hosted_identity=identity, artifacts=inventory, raw_process_identities_published=False, full_environment_variables_published=False, performance_gate='not_evaluated', limitation='guest CPU evidence cannot establish exclusive physical host or stable frequency')) @@ -164,8 +183,9 @@ def main(): parser = argparse.ArgumentParser(description=__doc__) parser.add_argument('--raw', type=Path, required=True) parser.add_argument('--out', type=Path, required=True) + parser.add_argument('--experiment', choices=EXPERIMENTS, default='cost') args = parser.parse_args() - print(json.dumps(publish(args.raw, args.out))) + print(json.dumps(publish(args.raw, args.out, args.experiment))) if __name__ == '__main__': diff --git a/experiments/measure/test_r54_cpu_hosted.py b/experiments/measure/test_r54_cpu_hosted.py new file mode 100644 index 00000000..e799167b --- /dev/null +++ b/experiments/measure/test_r54_cpu_hosted.py @@ -0,0 +1,101 @@ +"""Synthetic publication and hosted-identity boundaries, never actual hosted runs.""" +import json +import os +from pathlib import Path +import subprocess +import sys +import tempfile +import unittest +from unittest.mock import patch + + +class CpuHostedBoundaryTests(unittest.TestCase): + def test_wrong_experiment_or_stale_run_is_rejected_before_publication_staging(self): + from experiments.measure import r54_publish as publisher + identity = dict(GITHUB_RUN_ID='123', GITHUB_RUN_ATTEMPT='1', GITHUB_SHA='a' * 40) + with tempfile.TemporaryDirectory() as folder: + root = Path(folder) + raw = root / 'local/raw' + raw.mkdir(parents=True) + output = root / 'local/public' + for schema, recorded in (('termforge-r54-hosted-v1', identity), + ('termforge-r54-cpu-hosted-v1', dict(identity, GITHUB_RUN_ID='122'))): + (raw / 'hosted.json').write_text(json.dumps(dict(schema=schema, source='a' * 40, + hosted_identity=recorded)), encoding='utf-8') + with patch.object(publisher, 'ROOT', root), \ + patch.object(publisher, 'hosted_identity', return_value=identity), self.assertRaises(ValueError): + publisher.publish(raw, output, experiment='cpu-boundary') + self.assertFalse(output.exists()) + self.assertFalse(output.with_name('public.partial').exists()) + + def test_non_hosted_entry_preserves_failure_without_building(self): + root = Path(__file__).resolve().parents[2] + (root / 'local').mkdir(exist_ok=True) + with tempfile.TemporaryDirectory(dir=root / 'local') as folder: + output = Path(folder) / 'raw' + result = subprocess.run([sys.executable, '-m', 'experiments.measure.r54_cpu_hosted', '--out', str(output)], + cwd=root, env=dict(os.environ, GITHUB_ACTIONS='false'), + capture_output=True, timeout=15) + self.assertNotEqual(result.returncode, 0) + manifest = json.loads((output / 'hosted.json').read_text(encoding='utf-8')) + self.assertEqual(manifest['status'], 'failed') + self.assertIn('actual GitHub-hosted Windows job', manifest['error']) + self.assertFalse((output / 'bundle').exists()) + self.assertFalse((output / 'diagnostic').exists()) + + def test_branch_attempt_and_source_cannot_be_mixed(self): + from experiments.measure import r54_hosted as hosted + identity = dict(GITHUB_ACTIONS='true', RUNNER_ENVIRONMENT='github-hosted', RUNNER_OS='Windows', + GITHUB_REPOSITORY='JavaZeroo/termforge', GITHUB_EVENT_NAME='push', + GITHUB_REF='refs/heads/perf/grapheme-cpu-hosted-r54', GITHUB_RUN_ATTEMPT='1', + GITHUB_SHA='a' * 40) + with patch.dict(os.environ, identity, clear=True), patch.object(os, 'name', 'nt'), \ + patch.object(hosted, 'git', return_value='a' * 40): + self.assertEqual(hosted.hosted_identity('perf/grapheme-cpu-hosted-r54')['GITHUB_SHA'], 'a' * 40) + with self.assertRaises(ValueError): + hosted.hosted_identity() + for key, value in (('GITHUB_RUN_ATTEMPT', '2'), ('GITHUB_SHA', 'b' * 40)): + with patch.dict(os.environ, {key: value}), self.assertRaises(ValueError): + hosted.hosted_identity('perf/grapheme-cpu-hosted-r54') + + def test_publication_keeps_failed_streams_and_separates_the_six_observation_scope(self): + from experiments.measure import r54_publish as publisher + identity = dict(GITHUB_RUN_ID='123', GITHUB_RUN_ATTEMPT='1', GITHUB_SHA='a' * 40) + with tempfile.TemporaryDirectory() as folder: + root = Path(folder) + raw = root / 'local/raw' + raw.mkdir(parents=True) + (raw / 'hosted.json').write_text(json.dumps(dict(schema='termforge-r54-cpu-hosted-v1', + source='a' * 40, hosted_identity=identity, status='failed')), encoding='utf-8') + record = raw / 'diagnostic/observations/000/record.json' + record.parent.mkdir(parents=True) + record.write_text(json.dumps(dict(child_pid=12345, status='failed', + child_cpu=dict(total_seconds=2.25), command=[str(root / 'tool.exe')])), encoding='utf-8') + payload = b'partial\xff\x00\n' + (record.parent / 'stdout.bin').write_bytes(payload) + for extra in ('diagnostic/observations/006/stdout.bin', 'batch-20261026/samples/000/stdout.bin', + 'diagnostic/observations/000/private.json', 'credentials.json'): + path = raw / extra + path.parent.mkdir(parents=True, exist_ok=True) + path.write_bytes(b'do-not-publish') + output = root / 'local/public' + # Host and short-path queries are OS boundaries. No fixture claims + # a real hosted identity or CPU measurement. + with patch.object(publisher, 'ROOT', root), \ + patch.object(publisher, 'hosted_identity', return_value=identity), \ + patch.object(publisher, 'windows_short_path', side_effect=lambda path: path): + publisher.publish(raw, output, experiment='cpu-boundary') + publication = json.loads((output / 'publication.json').read_text(encoding='utf-8')) + self.assertEqual(publication['schema'], 'termforge-r54-cpu-hosted-publication-v1') + self.assertEqual({item['path'] for item in publication['artifacts']}, + {'hosted.json', 'diagnostic/observations/000/record.json', 'diagnostic/observations/000/stdout.bin'}) + published = json.loads((output / 'diagnostic/observations/000/record.json').read_text(encoding='utf-8')) + self.assertNotIn('child_pid', published) + self.assertEqual(published['child_cpu']['total_seconds'], 2.25) + self.assertEqual(published['status'], 'failed') + self.assertTrue(published['command'][0].startswith('${WORKSPACE}')) + self.assertEqual((output / 'diagnostic/observations/000/stdout.bin').read_bytes(), payload) + + +if __name__ == '__main__': + unittest.main() From c14adb9a7fa446476ad5c95c1f8c1b139ceb5ac4 Mon Sep 17 00:00:00 2001 From: JavaZero <71128095+JavaZeroo@users.noreply.github.com> Date: Tue, 27 Oct 2026 21:22:13 +0800 Subject: [PATCH 2/4] fix: canonicalize hosted fixtures and declare isolated setup-corrected diagnosis (#54) --- .github/workflows/r54-cpu.yml | 2 +- experiments/measure/R54-CPU-HOSTED.md | 6 +++++- experiments/measure/r54_cpu_hosted.py | 7 ++----- experiments/measure/r54_hosted.py | 5 ++++- experiments/measure/r54_publish.py | 4 ++-- experiments/measure/test_r54_cpu_hosted.py | 8 ++++++-- 6 files changed, 20 insertions(+), 12 deletions(-) diff --git a/.github/workflows/r54-cpu.yml b/.github/workflows/r54-cpu.yml index 3409e31c..e56eeb55 100644 --- a/.github/workflows/r54-cpu.yml +++ b/.github/workflows/r54-cpu.yml @@ -2,7 +2,7 @@ name: Bounded R54 hosted CPU diagnosis on: push: - branches: ['perf/grapheme-cpu-hosted-r54'] + branches: ['perf/grapheme-cpu-hosted-r54', 'perf/grapheme-cpu-hosted-r54-setup2'] paths: - '.github/workflows/r54-cpu.yml' - 'experiments/measure/*.py' diff --git a/experiments/measure/R54-CPU-HOSTED.md b/experiments/measure/R54-CPU-HOSTED.md index 5ae187fd..1d7f3443 100644 --- a/experiments/measure/R54-CPU-HOSTED.md +++ b/experiments/measure/R54-CPU-HOSTED.md @@ -4,7 +4,11 @@ ## 固定执行 -以 PR #106 的 `beeb64496b2e2898e8619fa5e9c39f490a3891c4` 为基点。新入口为[工作流](../../.github/workflows/r54-cpu.yml),仅在分支`perf/grapheme-cpu-hosted-r54`首次创建push执行;拒绝attempt2及后续重跑。后续代码/导出器/证据修正不自动触发替代观察。原 `r54-cost.yml`、两批各112位置、原5%护栏及其失败记录不变。 +以 PR #106 的 `beeb64496b2e2898e8619fa5e9c39f490a3891c4` 为基点。入口为[工作流](../../.github/workflows/r54-cpu.yml),只接受下述两个明确分支的创建push,拒绝attempt2;后续代码/导出器/证据修正不自动触发替代观察。原 `r54-cost.yml`、两批各112位置、原5%护栏及其失败记录不变。 + +首个`perf/grapheme-cpu-hosted-r54`创建作业 [40509157581](https://github.com/JavaZeroo/termforge/actions/runs/40509157581) 来自`eed0ab7bae5fcc5d05a25cea5161dabc4f935878`,attempt1,在31项准备测试中的发布夹具路径边界比较处失败。夹具根路径未规范化,被测发布器则将输入路径resolve后比较;旧job没有记录该临时目录的实际路径形式。构建、诊断和导出均未启动,实际观察为0,旧run不重跑。本地使用真实8.3别名复现了该缺陷:旧比较为false、规范化后为true;这不是旧hosted路径形式的直接证据。修正仅将夹具根目录规范化,不放宽发布器路径边界。 + +修正与审查后,另行预声明的`perf/grapheme-cpu-hosted-r54-setup2`执行分支在创建时完成一次相同构建/六位置诊断,使用新source/run身份。两分支按完整身份区分,第二入口不覆盖首次准备失败,不增加或替换任何已存在的测量样本。该入口仍拒绝attempt2,修订push不会自动重测。 一个新的`windows-2025` VM依次准备Python3.12、psutil7.2.2、Rust1.92.0,执行合同测试,然后[复建并审计](r54_build.py)原固定 `.3/.4` 及四次短正确性。仍是MSVC x64、Release、locked/offline、no-default-features、jobs1、独立target及两个共同harness覆盖;80/84来源文件、原锁文件、真实编译/链接日志和EXE哈希保持可追溯。它是新构建,不借用本地或上次hosted二进制身份。 diff --git a/experiments/measure/r54_cpu_hosted.py b/experiments/measure/r54_cpu_hosted.py index cda0cbac..f523fad5 100644 --- a/experiments/measure/r54_cpu_hosted.py +++ b/experiments/measure/r54_cpu_hosted.py @@ -7,12 +7,9 @@ from experiments.measure.r54_build import prepare_builds from experiments.measure.r54_contract import ROOT from experiments.measure.r54_cpu_diagnostic import execute as diagnose -from experiments.measure.r54_hosted import hosted_identity +from experiments.measure.r54_hosted import CPU_BOUNDARY_BRANCHES, hosted_identity from experiments.measure.r54_inputs import git, require -BRANCH = 'perf/grapheme-cpu-hosted-r54' - - def execute(output): output = Path(output).resolve() require(output.is_relative_to(ROOT / 'local'), 'hosted artifacts must stay under the workspace local directory') @@ -22,7 +19,7 @@ def execute(output): workflow_sha256=sha(ROOT / '.github/workflows/r54-cpu.yml'), performance_gate='not_evaluated', diagnostic=None) try: - manifest['hosted_identity'] = hosted_identity(BRANCH) + manifest['hosted_identity'] = hosted_identity(CPU_BOUNDARY_BRANCHES) write_json(output / 'hosted.json', manifest) prepare_builds(output / 'bundle') declaration = dict(host_kind='hosted_vm', no_known_concurrent_build_or_test=True, diff --git a/experiments/measure/r54_hosted.py b/experiments/measure/r54_hosted.py index 426d2f81..2a14f0d6 100644 --- a/experiments/measure/r54_hosted.py +++ b/experiments/measure/r54_hosted.py @@ -9,6 +9,8 @@ from experiments.measure.r54_contract import ROOT, SEEDS from experiments.measure.r54_inputs import git, require +CPU_BOUNDARY_BRANCHES = ('perf/grapheme-cpu-hosted-r54', 'perf/grapheme-cpu-hosted-r54-setup2') + def hosted_identity(branch='perf/grapheme-hosted-r54'): names = ('GITHUB_ACTIONS', 'RUNNER_ENVIRONMENT', 'RUNNER_OS', 'GITHUB_REPOSITORY', @@ -19,7 +21,8 @@ def hosted_identity(branch='perf/grapheme-hosted-r54'): identity['RUNNER_OS'] == 'Windows' and os.name == 'nt', 'requires an actual GitHub-hosted Windows job') require(identity['GITHUB_REPOSITORY'] == 'JavaZeroo/termforge' and identity['GITHUB_EVENT_NAME'] == 'push', 'unexpected repository or event') - require(identity['GITHUB_REF'] == 'refs/heads/' + branch, 'unexpected experiment branch') + branches = (branch,) if isinstance(branch, str) else branch + require(identity['GITHUB_REF'] in ('refs/heads/' + name for name in branches), 'unexpected experiment branch') require(identity['GITHUB_RUN_ATTEMPT'] == '1', 'a workflow rerun must not replace the first attempt') require(identity['GITHUB_SHA'] == git(ROOT, 'rev-parse', 'HEAD'), 'workflow/source identity mismatch') return identity diff --git a/experiments/measure/r54_publish.py b/experiments/measure/r54_publish.py index e3c2efd3..426fb9e0 100644 --- a/experiments/measure/r54_publish.py +++ b/experiments/measure/r54_publish.py @@ -8,13 +8,13 @@ from experiments.measure.r54_batch import sha, write_json from experiments.measure.r54_contract import ROOT, SEEDS -from experiments.measure.r54_hosted import hosted_identity +from experiments.measure.r54_hosted import CPU_BOUNDARY_BRANCHES, hosted_identity from experiments.measure.r54_inputs import require EXPERIMENTS = { 'cost': dict(branch='perf/grapheme-hosted-r54', raw_schema='termforge-r54-hosted-v1', publication_schema='termforge-r54-hosted-publication-v1'), - 'cpu-boundary': dict(branch='perf/grapheme-cpu-hosted-r54', raw_schema='termforge-r54-cpu-hosted-v1', + 'cpu-boundary': dict(branch=CPU_BOUNDARY_BRANCHES, raw_schema='termforge-r54-cpu-hosted-v1', publication_schema='termforge-r54-cpu-hosted-publication-v1'), } diff --git a/experiments/measure/test_r54_cpu_hosted.py b/experiments/measure/test_r54_cpu_hosted.py index e799167b..6b6e6e02 100644 --- a/experiments/measure/test_r54_cpu_hosted.py +++ b/experiments/measure/test_r54_cpu_hosted.py @@ -14,7 +14,7 @@ def test_wrong_experiment_or_stale_run_is_rejected_before_publication_staging(se from experiments.measure import r54_publish as publisher identity = dict(GITHUB_RUN_ID='123', GITHUB_RUN_ATTEMPT='1', GITHUB_SHA='a' * 40) with tempfile.TemporaryDirectory() as folder: - root = Path(folder) + root = Path(folder).resolve() raw = root / 'local/raw' raw.mkdir(parents=True) output = root / 'local/public' @@ -52,6 +52,10 @@ def test_branch_attempt_and_source_cannot_be_mixed(self): with patch.dict(os.environ, identity, clear=True), patch.object(os, 'name', 'nt'), \ patch.object(hosted, 'git', return_value='a' * 40): self.assertEqual(hosted.hosted_identity('perf/grapheme-cpu-hosted-r54')['GITHUB_SHA'], 'a' * 40) + with patch.dict(os.environ, GITHUB_REF='refs/heads/perf/grapheme-cpu-hosted-r54-setup2'): + self.assertEqual(hosted.hosted_identity(hosted.CPU_BOUNDARY_BRANCHES)['GITHUB_SHA'], 'a' * 40) + with self.assertRaises(ValueError): + hosted.hosted_identity() with self.assertRaises(ValueError): hosted.hosted_identity() for key, value in (('GITHUB_RUN_ATTEMPT', '2'), ('GITHUB_SHA', 'b' * 40)): @@ -62,7 +66,7 @@ def test_publication_keeps_failed_streams_and_separates_the_six_observation_scop from experiments.measure import r54_publish as publisher identity = dict(GITHUB_RUN_ID='123', GITHUB_RUN_ATTEMPT='1', GITHUB_SHA='a' * 40) with tempfile.TemporaryDirectory() as folder: - root = Path(folder) + root = Path(folder).resolve() raw = root / 'local/raw' raw.mkdir(parents=True) (raw / 'hosted.json').write_text(json.dumps(dict(schema='termforge-r54-cpu-hosted-v1', From 4772f4f937c4f0f220be1630592f84bed3a591c8 Mon Sep 17 00:00:00 2001 From: JavaZero <71128095+JavaZeroo@users.noreply.github.com> Date: Tue, 27 Oct 2026 21:28:13 +0800 Subject: [PATCH 3/4] fix: trigger the predeclared CPU diagnosis independently of push file diffs (#54) --- .github/workflows/r54-cpu.yml | 5 +---- experiments/measure/R54-CPU-HOSTED.md | 8 ++++++-- experiments/measure/r54_hosted.py | 3 ++- experiments/measure/test_r54_cpu_hosted.py | 9 +++++---- 4 files changed, 14 insertions(+), 11 deletions(-) diff --git a/.github/workflows/r54-cpu.yml b/.github/workflows/r54-cpu.yml index e56eeb55..1b398c4b 100644 --- a/.github/workflows/r54-cpu.yml +++ b/.github/workflows/r54-cpu.yml @@ -2,10 +2,7 @@ name: Bounded R54 hosted CPU diagnosis on: push: - branches: ['perf/grapheme-cpu-hosted-r54', 'perf/grapheme-cpu-hosted-r54-setup2'] - paths: - - '.github/workflows/r54-cpu.yml' - - 'experiments/measure/*.py' + branches: ['perf/grapheme-cpu-hosted-r54', 'perf/grapheme-cpu-hosted-r54-setup2', 'perf/grapheme-cpu-hosted-r54-entry3'] permissions: contents: read diff --git a/experiments/measure/R54-CPU-HOSTED.md b/experiments/measure/R54-CPU-HOSTED.md index 1d7f3443..ca98eeaa 100644 --- a/experiments/measure/R54-CPU-HOSTED.md +++ b/experiments/measure/R54-CPU-HOSTED.md @@ -4,11 +4,15 @@ ## 固定执行 -以 PR #106 的 `beeb64496b2e2898e8619fa5e9c39f490a3891c4` 为基点。入口为[工作流](../../.github/workflows/r54-cpu.yml),只接受下述两个明确分支的创建push,拒绝attempt2;后续代码/导出器/证据修正不自动触发替代观察。原 `r54-cost.yml`、两批各112位置、原5%护栏及其失败记录不变。 +以 PR #106 的 `beeb64496b2e2898e8619fa5e9c39f490a3891c4` 为基点。入口为[工作流](../../.github/workflows/r54-cpu.yml),只接受下述三个明确分支的创建push,拒绝attempt2;后续代码/导出器/证据修正不自动触发替代观察。原 `r54-cost.yml`、两批各112位置、原5%护栏及其失败记录不变。 首个`perf/grapheme-cpu-hosted-r54`创建作业 [40509157581](https://github.com/JavaZeroo/termforge/actions/runs/40509157581) 来自`eed0ab7bae5fcc5d05a25cea5161dabc4f935878`,attempt1,在31项准备测试中的发布夹具路径边界比较处失败。夹具根路径未规范化,被测发布器则将输入路径resolve后比较;旧job没有记录该临时目录的实际路径形式。构建、诊断和导出均未启动,实际观察为0,旧run不重跑。本地使用真实8.3别名复现了该缺陷:旧比较为false、规范化后为true;这不是旧hosted路径形式的直接证据。修正仅将夹具根目录规范化,不放宽发布器路径边界。 -修正与审查后,另行预声明的`perf/grapheme-cpu-hosted-r54-setup2`执行分支在创建时完成一次相同构建/六位置诊断,使用新source/run身份。两分支按完整身份区分,第二入口不覆盖首次准备失败,不增加或替换任何已存在的测量样本。该入口仍拒绝attempt2,修订push不会自动重测。 +修正与审查后,`perf/grapheme-cpu-hosted-r54-setup2`于2026-10-27 13:22:55 UTC在`c14adb9a7fa446476ad5c95c1f8c1b139ceb5ac4`创建。提供者列表只出现无路径过滤的[默认回归40511533857](https://github.com/JavaZeroo/termforge/actions/runs/40511533857),没有该分支的CPU或候选工作流作业,实际诊断观察仍为0;原实验分支同源码更新对应的CPU作业40511437233为skipped。不能将默认回归计作诊断执行。 + +GitHub的[路径过滤规则](https://docs.github.com/en/actions/reference/workflows-and-actions/workflow-syntax#git-diff-comparisons)依赖push文件差异,空差异不运行工作流。上述缺席与新分支在已发布commit上创建、两个工作流均有路径过滤相符,但事件实际文件列表未留存。为消除入口对此差异计算的依赖,本诊断移除paths过滤,仍保留明确分支白名单、创建push条件和attempt1检查。 + +下一入口预声明为`perf/grapheme-cpu-hosted-r54-entry3`的新创建push,使用修订后新source/run身份,一次完成原构建及六位置诊断。前三个入口的实际状态分别保留,不重建旧分支、不重跑旧run、不增加或替换任何已存在的测量样本;后续修订push仍不会执行诊断。该入口变化不改测量代码、oracle、停止条件或产品容差。 一个新的`windows-2025` VM依次准备Python3.12、psutil7.2.2、Rust1.92.0,执行合同测试,然后[复建并审计](r54_build.py)原固定 `.3/.4` 及四次短正确性。仍是MSVC x64、Release、locked/offline、no-default-features、jobs1、独立target及两个共同harness覆盖;80/84来源文件、原锁文件、真实编译/链接日志和EXE哈希保持可追溯。它是新构建,不借用本地或上次hosted二进制身份。 diff --git a/experiments/measure/r54_hosted.py b/experiments/measure/r54_hosted.py index 2a14f0d6..e30214bd 100644 --- a/experiments/measure/r54_hosted.py +++ b/experiments/measure/r54_hosted.py @@ -9,7 +9,8 @@ from experiments.measure.r54_contract import ROOT, SEEDS from experiments.measure.r54_inputs import git, require -CPU_BOUNDARY_BRANCHES = ('perf/grapheme-cpu-hosted-r54', 'perf/grapheme-cpu-hosted-r54-setup2') +CPU_BOUNDARY_BRANCHES = ('perf/grapheme-cpu-hosted-r54', 'perf/grapheme-cpu-hosted-r54-setup2', + 'perf/grapheme-cpu-hosted-r54-entry3') def hosted_identity(branch='perf/grapheme-hosted-r54'): diff --git a/experiments/measure/test_r54_cpu_hosted.py b/experiments/measure/test_r54_cpu_hosted.py index 6b6e6e02..62738e2b 100644 --- a/experiments/measure/test_r54_cpu_hosted.py +++ b/experiments/measure/test_r54_cpu_hosted.py @@ -52,10 +52,11 @@ def test_branch_attempt_and_source_cannot_be_mixed(self): with patch.dict(os.environ, identity, clear=True), patch.object(os, 'name', 'nt'), \ patch.object(hosted, 'git', return_value='a' * 40): self.assertEqual(hosted.hosted_identity('perf/grapheme-cpu-hosted-r54')['GITHUB_SHA'], 'a' * 40) - with patch.dict(os.environ, GITHUB_REF='refs/heads/perf/grapheme-cpu-hosted-r54-setup2'): - self.assertEqual(hosted.hosted_identity(hosted.CPU_BOUNDARY_BRANCHES)['GITHUB_SHA'], 'a' * 40) - with self.assertRaises(ValueError): - hosted.hosted_identity() + for branch in ('perf/grapheme-cpu-hosted-r54-setup2', 'perf/grapheme-cpu-hosted-r54-entry3'): + with patch.dict(os.environ, GITHUB_REF='refs/heads/' + branch): + self.assertEqual(hosted.hosted_identity(hosted.CPU_BOUNDARY_BRANCHES)['GITHUB_SHA'], 'a' * 40) + with self.assertRaises(ValueError): + hosted.hosted_identity() with self.assertRaises(ValueError): hosted.hosted_identity() for key, value in (('GITHUB_RUN_ATTEMPT', '2'), ('GITHUB_SHA', 'b' * 40)): From f871fd99ec5bc1027d1e7ccfcdd5860d57b0f12f Mon Sep 17 00:00:00 2001 From: JavaZero <71128095+JavaZeroo@users.noreply.github.com> Date: Tue, 27 Oct 2026 21:44:23 +0800 Subject: [PATCH 4/4] docs: retain audited hosted CPU observations and all entry outcomes (#54) --- docs/EVIDENCE.md | 1 + docs/FEATURES.md | 2 +- experiments/measure/R54-CPU-HOSTED.md | 2 +- .../2026-10-27-r54-cpu-hosted/README.md | 54 + .../2026-10-27-r54-cpu-hosted/evidence.zip | Bin 0 -> 123388 bytes .../2026-10-27-r54-cpu-hosted/validation.json | 2538 +++++++++++++++++ .../2026-10-27-r54-cpu-hosted/verify.py | 177 ++ 7 files changed, 2772 insertions(+), 2 deletions(-) create mode 100644 experiments/measure/results/2026-10-27-r54-cpu-hosted/README.md create mode 100644 experiments/measure/results/2026-10-27-r54-cpu-hosted/evidence.zip create mode 100644 experiments/measure/results/2026-10-27-r54-cpu-hosted/validation.json create mode 100644 experiments/measure/results/2026-10-27-r54-cpu-hosted/verify.py diff --git a/docs/EVIDENCE.md b/docs/EVIDENCE.md index 566598b7..4148376e 100644 --- a/docs/EVIDENCE.md +++ b/docs/EVIDENCE.md @@ -165,5 +165,6 @@ python -m unittest experiments.shell.test_workflow_sessions -v | 固定字素成本的批次协议与执行 | [R54 v1 合同和构建/环境记录](../experiments/measure/R54-COST.md)、[执行器](../experiments/measure/r54_batch.py)、[合成进程回归](../experiments/measure/test_r54_batch.py)、[实际预检及限定验证](../experiments/measure/results/2026-10-27-r54-runner/validation.json) | 固定两个种子、各 112 个位置,字节原样保留、首错停止、完整批次后逐组配对统计。共享工作站十区间活动预检未通过,未启动成本样本;测量相关 26 项测试通过,首次 Linux 候选 CI 的采样依赖导入错误及修正分别留证。5% 是环境诊断护栏,并非产品性能容差;实际批次、扰动归因及用户预算确认由 #54 接续。 | | 首次 hosted 字素成本批次 | [来源、停止位置与归档](../experiments/measure/results/2026-10-27-r54-hosted/README.md)、[机器可读记录](../experiments/measure/results/2026-10-27-r54-hosted/validation.json) | 两份固定构建与4次sanity通过,空闲预检通过;第24位置的未归属CPU活动估计5.410%触发原5%护栏,保留23个有效记录和1个失败记录,第二批未启动。全部24次输出正确,但批次不完整,不生成配对成本摘要;临时VM逐进程归因、完整受控批次和预算仍未完成。 | | CPU计量边界诊断(#54) | [固定六位置及归档](../experiments/measure/results/2026-10-27-r54-cpu-boundary/README.md)、[独立复核](../experiments/measure/results/2026-10-27-r54-cpu-boundary/verify.py) | 同一共享Windows宿主、原已审计构建的六次输出和来源核验通过;分别保留完整子进程、内层区间、runner和全机计数。区间外CPU为0.078125–0.109375s,仍有明显未归属活动,不算空闲成本批次,不反推旧hosted原因或修改原5%护栏。 | +| 新hosted VM的CPU分量(#54) | [预声明](../experiments/measure/R54-CPU-HOSTED.md)、[六位置/来源/完整归档与只读复核](../experiments/measure/results/2026-10-27-r54-cpu-hosted/README.md) | 新构建和六个输出均通过;完整扣除自身/runner后余额仍为3.333%–5.263%,不归因旧事件、不作成本验收。准备失败与未创建作业的入口分别保留,未重跑或补样。 | 目前没有足够证据判断 TermForge 距离 WindTerm 快或慢多少。正式结论必须同时提供负载/配置/环境身份、正确性、完成端点、观测开销、重复样本与限制。未知字段保持未知;历史校准未完成不转成要求用户反复手动运行的前置。执行规则见 [ADR-0002](adr/0002-automation-first-investigation.md),核心选型重审见 [ADR-0003](adr/0003-core-reference-and-reuse.md)。 diff --git a/docs/FEATURES.md b/docs/FEATURES.md index 9f3c415e..fae8b271 100644 --- a/docs/FEATURES.md +++ b/docs/FEATURES.md @@ -71,7 +71,7 @@ | 自动正确性与构建 | **限定验收**:Windows shell/ConPTY/呈现及 Windows/Linux 核心 CI、差分/恢复/队列/生命周期场景 | 产品打包、干净机器使用、依赖升级、长期回归和更广平台矩阵;#40 的 CI 纯夹具超时、窄窗口选择 #73、四 shell 原事件 #80、hosted 准备 #81 和[复制夹具 OpenClipboard 5 #85](https://github.com/JavaZeroo/termforge/issues/85) 等独立失败仍未闭合;#72 保存判据与 #80 同步条件的限定修正不代替其他配置或未知根因验收 | R2 / R4 | | Windows 产品交付 | **实现中,未验收**:首个 [v0.1.0-alpha.1 预发布](releases/v0.1.0-alpha.1.md)提供 Windows x64、静态 MSVC 运行库、附资源许可和构建身份的免安装 ZIP;未签名,已验环境限 Windows 11 本机与 Server 2025 CI,布局仍写入 LOCALAPPDATA | 这是 R1 体验包,尚非 R2 日常开发版;干净机器与更广平台矩阵、签名/安装、配置升级及持续运行仍待完成。Windows 10 未验收,Server 2022 已知资源门槛未过 | R2 / R3 | | Linux/macOS 与其他架构 | **未实现**客户端;Linux 核心 CI 已有。[官方平台范围台账](../research/windterm-2.7.0-platform-scope.md)已固定 2.7.0 的 Windows x86/x64、macOS x64、Linux x64 资产及声明下限;这不是 TermForge 支持或实测结论 | 最终需真实 Linux/macOS 客户端;平台调查可提前进行,整合及完整验收排在 Windows 11 x64 完整版本之后。其他系统版本、最低版本和架构由用户后续明确;旧系统及 ARM 等保持调查记录,不自动承诺或排除,也不能由核心 CI 推断产品支持 | 持续调查;R5 全平台出口 | -| 吞吐、响应、内存与稳定性 | **实现中,未验收**:已有机制实验、连续 CPU 区间校准与合成回归;候选字素改动的受限诊断中 project/interactive 吞吐中位数约下降 16%/9%,由 [#54](https://github.com/JavaZeroo/termforge/issues/54) 跟踪。[冻结批次执行器](../experiments/measure/R54-COST.md#冻结批次执行器windows)核对构建来源、原始输出、停止规则与逐轮配对统计;[独立 hosted 构建与实测](../experiments/measure/results/2026-10-27-r54-hosted/README.md)已验证来源和失败保留,但首批因环境护栏停止,尚无完整成本批次;[独立CPU边界诊断](../experiments/measure/results/2026-10-27-r54-cpu-boundary/README.md)另记完整子进程/执行器分量,不改原护栏或判成本通过 | 旧诊断的共享宿主、allocator 包装与缺少真实呈现/输入时延端点使其不构成正式预算;新的不完整批次也不作吞吐结论。仍需同负载/同正确性、无插桩对照、观测开销和长期/多会话公平性;可靠原版基线后提出逐项预算及允许波动范围,**由用户明确确认**。与 WindTerm 的实际差距**待取证** | 当前优先;贯穿 R1–R5 | +| 吞吐、响应、内存与稳定性 | **实现中,未验收**:已有机制实验、连续 CPU 区间校准与合成回归;候选字素改动的受限诊断中 project/interactive 吞吐中位数约下降 16%/9%,由 [#54](https://github.com/JavaZeroo/termforge/issues/54) 跟踪。[冻结批次执行器](../experiments/measure/R54-COST.md#冻结批次执行器windows)核对构建来源、原始输出、停止规则与逐轮配对统计;[独立 hosted 构建与实测](../experiments/measure/results/2026-10-27-r54-hosted/README.md)已验证来源和失败保留,但首批因环境护栏停止,尚无完整成本批次;[共享宿主CPU诊断](../experiments/measure/results/2026-10-27-r54-cpu-boundary/README.md)及[新hosted六位置](../experiments/measure/results/2026-10-27-r54-cpu-hosted/README.md)分别记录完整子进程/执行器分量;后者仍有3.333%–5.263%未归属余额,不改原护栏或判成本通过 | 旧诊断的共享宿主、allocator 包装与缺少真实呈现/输入时延端点使其不构成正式预算;新的不完整批次也不作吞吐结论。仍需同负载/同正确性、无插桩对照、观测开销和长期/多会话公平性;可靠原版基线后提出逐项预算及允许波动范围,**由用户明确确认**。与 WindTerm 的实际差距**待取证** | 当前优先;贯穿 R1–R5 | | 大历史与动态内存压缩 | **未实现**产品能力;WindTerm 压缩属于官方性能声明,内部机制尚未证实 | 1/10/100 万行等明确规模下的内存/滚动/搜索/淘汰实验;根据瓶颈决定独立存储/压缩实现 | R4 | | 完整目标验收 | **待取证**:已有固定版本和局部观察,没有完整行为/性能对照矩阵 | 先在 Windows 11 x64 完整验收功能/行为/UI/性能及独立构建,再完成 Linux/macOS 全平台出口;重要差异和性能容差须有用户确认,Windows 通过不代表全平台完成;不依赖 WindTerm 二进制或专有资源 | R5 分平台出口 | diff --git a/experiments/measure/R54-CPU-HOSTED.md b/experiments/measure/R54-CPU-HOSTED.md index ca98eeaa..4a63896b 100644 --- a/experiments/measure/R54-CPU-HOSTED.md +++ b/experiments/measure/R54-CPU-HOSTED.md @@ -12,7 +12,7 @@ GitHub的[路径过滤规则](https://docs.github.com/en/actions/reference/workflows-and-actions/workflow-syntax#git-diff-comparisons)依赖push文件差异,空差异不运行工作流。上述缺席与新分支在已发布commit上创建、两个工作流均有路径过滤相符,但事件实际文件列表未留存。为消除入口对此差异计算的依赖,本诊断移除paths过滤,仍保留明确分支白名单、创建push条件和attempt1检查。 -下一入口预声明为`perf/grapheme-cpu-hosted-r54-entry3`的新创建push,使用修订后新source/run身份,一次完成原构建及六位置诊断。前三个入口的实际状态分别保留,不重建旧分支、不重跑旧run、不增加或替换任何已存在的测量样本;后续修订push仍不会执行诊断。该入口变化不改测量代码、oracle、停止条件或产品容差。 +第三入口预声明为`perf/grapheme-cpu-hosted-r54-entry3`的新创建push,使用修订后新source/run身份,一次完成原构建及六位置诊断。前三个入口的实际状态分别保留,不重建旧分支、不重跑旧run、不增加或替换任何已存在的测量样本;后续修订push仍不会执行诊断。该入口变化不改测量代码、oracle、停止条件或产品容差。[实际run40512310499及归档复核](results/2026-10-27-r54-cpu-hosted/README.md)已完成:六个位置输出正确、计量完整,扣除自身完整工作后仍有3.333%–5.263%的余额;没有成本或性能验收结论。 一个新的`windows-2025` VM依次准备Python3.12、psutil7.2.2、Rust1.92.0,执行合同测试,然后[复建并审计](r54_build.py)原固定 `.3/.4` 及四次短正确性。仍是MSVC x64、Release、locked/offline、no-default-features、jobs1、独立target及两个共同harness覆盖;80/84来源文件、原锁文件、真实编译/链接日志和EXE哈希保持可追溯。它是新构建,不借用本地或上次hosted二进制身份。 diff --git a/experiments/measure/results/2026-10-27-r54-cpu-hosted/README.md b/experiments/measure/results/2026-10-27-r54-cpu-hosted/README.md new file mode 100644 index 00000000..10a4e13a --- /dev/null +++ b/experiments/measure/results/2026-10-27-r54-cpu-hosted/README.md @@ -0,0 +1,54 @@ +# R54:新 hosted VM 的 CPU 分量诊断 + +本次按[预声明合同](../../R54-CPU-HOSTED.md)完成一次六位置诊断,全部输出正确、退出0,完整子进程/runner/全机计量可复核。**它没有执行空闲资格筛选或正式成本批次,不构成性能验收。** + +## 来源与执行身份 + +- 源码 `4772f4f937c4f0f220be1630592f84bed3a591c8`,树 `86e2342d16acd101fe182793d20aef3f1f017633`;与审查树一致。 +- [run 40512310499](https://github.com/JavaZeroo/termforge/actions/runs/40512310499),job `122217071656`,attempt1,创建push分支 `perf/grapheme-cpu-hosted-r54-entry3`。提供者元数据、作业日志及归档身份相符。 +- 实际映像 `windows-2025-vs2026` / `20261021.278.1`;Windows Server 2025 build26100、UBR33650、24H2,AMD EPYC 7763,guest4逻辑/2物理CPU,Python3.12.10、psutil7.2.2。 +- Rust1.92.0、MSVC14.51.36231 x64;Release、locked/offline、no-default-features、jobs1,独立target。构建前31项准备测试通过(14.042s),两次构建和四次短正确性通过后才进入诊断。 + +| 核心 | 固定来源 | 本次新EXE SHA-256 | 字节数 | +|---|---|---|---:| +| `.3` | `65988f5f20f62294ff52fcac5b5b81e563f93f3b` | `5f31a54e5543eca286e98a10b31aae060b0a52f2ccbcfb4bfc1d7983d6428c03` | 523264 | +| `.4` | `3d6d44d3b3e9a9a3e41da98e8711e9f19a211fd8` | `45a15862e2bd8c8ce93073923b68f23bc6f3f848aa147ed1ce6340135e214207` | 580096 | + +80/84个跟踪来源文件、原锁文件与两处共同harness覆盖均核对。Git blob和实际Windows检出换行不同的文件,逐个用完整SHA验证准确的LF→CRLF转换;没有修改证据来匹配仓库字节。4份共同harness/校验入口及164份原文件共168次来源核对,其中115份对应CRLF检出、53份直接匹配blob。它是独立构建,不沿用此前EXE身份。 + +## 实际六位置 + +固定interactive 22,000B、chunk1、observe0,内部CPU/墙钟至少2秒,顺序 `.3,.4,.4,.3,.3,.4`;单进程90秒、清理5秒。未重跑或补样,原完整oracle不变。 + +| sequence | 核心 | 自身区间外CPU/s | runner CPU/s | 全机未归属余额/s | 余额占全机总CPU区间 | +|---:|---|---:|---:|---:|---:| +| 0 | `.3` | 0.093750 | 0 | 0.500000 | 5.263158% | +| 1 | `.4` | 0.062500 | 0.015625 | 0.281250 | 3.333333% | +| 2 | `.4` | 0.078125 | 0 | 0.328125 | 3.888889% | +| 3 | `.3` | 0.078125 | 0 | 0.328125 | 3.860294% | +| 4 | `.3` | 0.062500 | 0 | 0.296875 | 3.492647% | +| 5 | `.4` | 0.078125 | 0 | 0.312500 | 3.703704% | + +余额为全机busy减完整子进程CPU及runner同区间CPU,允许正负;可能包括其他guest活动、计数粒度和非原子端点。sequence0即使扣除自身完整工作,余额仍超过原5%环境护栏的数值;本诊断不应用该护栏判通过。结果仅支持“补齐自身计量不能消除这个新环境的全部余额”,不将余额归因某一进程,不解释旧hosted第24位置失败,也不证明物理宿主排他或频率稳定。 + +没有计算吞吐比、没有改原v1两批各112位置及5%护栏,也没有接受产品性能容差。`accounting_complete`仅表示诊断记录完整;`performance_gate=not_evaluated`,正式成本验收样本为0。 + +## 保留的入口缺口 + +首次source `eed0ab7` 的 [40509157581](https://github.com/JavaZeroo/termforge/actions/runs/40509157581)在31项准备测试中的路径边界比较失败,实际构建/测量0。夹具根路径未规范化;本地8.3别名复现该缺陷,但旧job实际临时路径形式未记录。只规范化测试ROOT,生产路径边界未放宽。首次四条普通CI也在Windows的同一测试失败,Linux成功;元数据与错误摘录留在 [validation.json](validation.json)。 + +修订 `c14adb9a` 的四条普通CI及setup2默认CI均通过;在已发布source创建setup2时,仅出现无路径过滤的默认回归40511533857,没有CPU或候选workflow run。CPU入口随后去掉paths过滤,并单独预声明entry3;创建事件与attempt1限制保持。没有删除重建旧分支、重跑旧run或用新观察覆盖失败。触发解释及其未知范围见合同。 + +## 归档和复核 + +审核后的[evidence.zip](evidence.zip)与提供者产物字节相同:123388B,SHA-256 `263a3bf8e229b89f1e443b12e522daf69d30de403affa0dc49fe674c0dd8f241`,artifact `12738430576`。75文件中74份有原始/公开哈希清单,24份sanity/诊断identity/观察流保持原字节;元数据移除子PID、长短路径改别名,不含checkout、缓存、进程清单或WindTerm私有产物。 + +完整构建日志、来源清单、原始计数与输出均在归档;[validation.json](validation.json)整理实际身份和结论。运行以下只读复核,不启动编译或测量,需包含固定来源提交的Git历史: + +```powershell +python experiments/measure/results/2026-10-27-r54-cpu-hosted/verify.py +``` + +复核包含所有公开文件哈希、24份原字节流、来源及构建绑定、四次sanity、六次完整输出oracle和全部CPU等式。本地measurement40/40(19.436s),入口增量4/4(0.297s);实际hosted测试与上述本地测试身份分别保留。 + +后续仍需受控完整A/B、allocator/projection扰动与组件归因、可靠WindTerm基线及用户确认的逐项预算。#54、#34和R2保持未完成,产品核心与后端继续作为候选。 diff --git a/experiments/measure/results/2026-10-27-r54-cpu-hosted/evidence.zip b/experiments/measure/results/2026-10-27-r54-cpu-hosted/evidence.zip new file mode 100644 index 0000000000000000000000000000000000000000..f1bdd091320fba887772775b84d33de5281cbcc0 GIT binary patch literal 123388 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 literal 0 HcmV?d00001 diff --git a/experiments/measure/results/2026-10-27-r54-cpu-hosted/validation.json b/experiments/measure/results/2026-10-27-r54-cpu-hosted/validation.json new file mode 100644 index 00000000..b3554a61 --- /dev/null +++ b/experiments/measure/results/2026-10-27-r54-cpu-hosted/validation.json @@ -0,0 +1,2538 @@ +{ + "schema": "termforge-r54-cpu-hosted-validation-v1", + "source": "4772f4f937c4f0f220be1630592f84bed3a591c8", + "hosted_identity": { + "GITHUB_ACTIONS": "true", + "RUNNER_ENVIRONMENT": "github-hosted", + "RUNNER_OS": "Windows", + "GITHUB_REPOSITORY": "JavaZeroo/termforge", + "GITHUB_SHA": "4772f4f937c4f0f220be1630592f84bed3a591c8", + "GITHUB_REF": "refs/heads/perf/grapheme-cpu-hosted-r54-entry3", + "GITHUB_RUN_ID": "40512310499", + "GITHUB_RUN_NUMBER": "3", + "GITHUB_RUN_ATTEMPT": "1", + "GITHUB_EVENT_NAME": "push", + "ImageOS": "win25-vs2026", + "ImageVersion": "20261021.278.1" + }, + "host": { + "host": { + "platform": "Windows-2025Server-10.0.26100-SP0", + "python": "3.12.10", + "psutil": "7.2.2", + "cpu_logical": 4, + "cpu_physical": 2, + "cpu": { + "ProcessorNameString": "AMD EPYC 7763 64-Core Processor ", + "Identifier": "AMD64 Family 25 Model 1 Stepping 1" + }, + "windows": { + "CurrentBuildNumber": "26100", + "UBR": 33650, + "DisplayVersion": "24H2" + } + }, + "declaration": { + "host_kind": "hosted_vm", + "no_known_concurrent_build_or_test": true, + "known_concurrent_work": [ + "This job completed setup, tests and both audited builds before diagnosis.", + "GitHub runner service and operating-system background services remain.", + "Physical host scheduling and neighboring virtual machines are unobserved." + ], + "purpose": "One six-observation CPU-boundary diagnostic; no idle qualification or cost acceptance." + }, + "scope": "accounting diagnosis on declared host; no idle qualification or cost acceptance" + }, + "builds": { + "baseline": { + "source": "65988f5f20f62294ff52fcac5b5b81e563f93f3b", + "version": "0.26.1-dev+termforge.3", + "candidate_tree": "38f901ce2407d73ab7d4ae70987c294849bafa92", + "tracked_candidate_files": 80, + "changed_paths": [ + "experiments/candidates/alacritty/Cargo.toml", + "experiments/candidates/alacritty/src/measure.rs" + ], + "binary": { + "bytes": 523264, + "sha256": "5f31a54e5543eca286e98a10b31aae060b0a52f2ccbcfb4bfc1d7983d6428c03" + }, + "identity": { + "allocator_wrapper_compiled": false, + "projection_selectable": true, + "schema": "termforge-core-cost-build-v1", + "timing_gates_compiled": true + }, + "linker": { + "path": "C:\\Program Files\\Microsoft Visual Studio\\18\\Enterprise\\VC\\Tools\\MSVC\\14.51.36231\\bin\\HostX64\\x64\\link.exe", + "sha256": "a8c42e25c91ca2453e1110540ac08f0fc12d7539454a11b9af8baeecb469837d" + }, + "receipt_sha256": "886d9a3f024d46a221dc4833a485e0c9c2c4b6e6193dd42190fe10fc6bc70a8a" + }, + "candidate": { + "source": "3d6d44d3b3e9a9a3e41da98e8711e9f19a211fd8", + "version": "0.26.1-dev+termforge.4", + "candidate_tree": "2cce55fcf2d379440110bbbf8612117cce3edf64", + "tracked_candidate_files": 84, + "changed_paths": [ + "experiments/candidates/alacritty/Cargo.toml", + "experiments/candidates/alacritty/src/measure.rs" + ], + "binary": { + "bytes": 580096, + "sha256": "45a15862e2bd8c8ce93073923b68f23bc6f3f848aa147ed1ce6340135e214207" + }, + "identity": { + "allocator_wrapper_compiled": false, + "projection_selectable": true, + "schema": "termforge-core-cost-build-v1", + "timing_gates_compiled": true + }, + "linker": { + "path": "C:\\Program Files\\Microsoft Visual Studio\\18\\Enterprise\\VC\\Tools\\MSVC\\14.51.36231\\bin\\HostX64\\x64\\link.exe", + "sha256": "a8c42e25c91ca2453e1110540ac08f0fc12d7539454a11b9af8baeecb469837d" + }, + "receipt_sha256": "5d8e407aa31a9d2fc3921a15fcc15e295e9fa8a748acf2f40e81744d1cdaa914" + } + }, + "observations": [ + { + "item": { + "sequence": 0, + "variant": "baseline", + "route": "interactive", + "chunk": 1 + }, + "inner_cpu_seconds": 2.234375, + "child_cpu": { + "creation_filetime": 134375815087328400, + "exit_filetime": 134375815111035208, + "kernel_seconds": 0.609375, + "user_seconds": 1.71875, + "total_seconds": 2.328125 + }, + "runner_cpu_seconds": 0.0, + "environment": { + "total_seconds": 9.5, + "busy_seconds": 2.828125, + "busy_percent": 29.769736842105264, + "before": { + "user": 224.96875, + "kernel_including_idle": 1054.8125, + "idle": 938.25, + "system": 116.5625, + "perf_counter_seconds": 320.6986669, + "utc": "2026-10-27T13:31:48.731677+00:00" + }, + "after": { + "user": 226.921875, + "kernel_including_idle": 1062.359375, + "idle": 944.921875, + "system": 117.4375, + "perf_counter_seconds": 323.0708238, + "utc": "2026-10-27T13:31:51.102693+00:00" + } + }, + "accounting": { + "inner_only_balance_seconds": 0.59375, + "inner_only_balance_percent": 6.25, + "child_outside_inner_seconds": 0.09375, + "runner_cpu_seconds": 0.0, + "unassigned_balance_seconds": 0.5, + "unassigned_balance_percent": 5.2631578947368425 + }, + "state_fnv1a64": "8035857386658397897" + }, + { + "item": { + "sequence": 1, + "variant": "candidate", + "route": "interactive", + "chunk": 1 + }, + "inner_cpu_seconds": 2.015625, + "child_cpu": { + "creation_filetime": 134375815111116270, + "exit_filetime": 134375815132177544, + "kernel_seconds": 0.578125, + "user_seconds": 1.5, + "total_seconds": 2.078125 + }, + "runner_cpu_seconds": 0.015625, + "environment": { + "total_seconds": 8.4375, + "busy_seconds": 2.375, + "busy_percent": 28.14814814814815, + "before": { + "user": 226.921875, + "kernel_including_idle": 1062.359375, + "idle": 944.921875, + "system": 117.4375, + "perf_counter_seconds": 323.0773715, + "utc": "2026-10-27T13:31:51.111019+00:00" + }, + "after": { + "user": 228.546875, + "kernel_including_idle": 1069.171875, + "idle": 950.984375, + "system": 118.1875, + "perf_counter_seconds": 325.1850088, + "utc": "2026-10-27T13:31:53.217016+00:00" + } + }, + "accounting": { + "inner_only_balance_seconds": 0.359375, + "inner_only_balance_percent": 4.2592592592592595, + "child_outside_inner_seconds": 0.0625, + "runner_cpu_seconds": 0.015625, + "unassigned_balance_seconds": 0.28125, + "unassigned_balance_percent": 3.3333333333333335 + }, + "state_fnv1a64": "6154007251599136798" + }, + { + "item": { + "sequence": 2, + "variant": "candidate", + "route": "interactive", + "chunk": 1 + }, + "inner_cpu_seconds": 2.03125, + "child_cpu": { + "creation_filetime": 134375815132252696, + "exit_filetime": 134375815153338773, + "kernel_seconds": 0.5625, + "user_seconds": 1.546875, + "total_seconds": 2.109375 + }, + "runner_cpu_seconds": 0.0, + "environment": { + "total_seconds": 8.4375, + "busy_seconds": 2.4375, + "busy_percent": 28.88888888888889, + "before": { + "user": 228.546875, + "kernel_including_idle": 1069.234375, + "idle": 951.046875, + "system": 118.1875, + "perf_counter_seconds": 325.1909982, + "utc": "2026-10-27T13:31:53.223062+00:00" + }, + "after": { + "user": 230.296875, + "kernel_including_idle": 1075.921875, + "idle": 957.046875, + "system": 118.875, + "perf_counter_seconds": 327.3011683, + "utc": "2026-10-27T13:31:55.334541+00:00" + } + }, + "accounting": { + "inner_only_balance_seconds": 0.40625, + "inner_only_balance_percent": 4.814814814814815, + "child_outside_inner_seconds": 0.078125, + "runner_cpu_seconds": 0.0, + "unassigned_balance_seconds": 0.328125, + "unassigned_balance_percent": 3.888888888888889 + }, + "state_fnv1a64": "6154007251599136798" + }, + { + "item": { + "sequence": 3, + "variant": "baseline", + "route": "interactive", + "chunk": 1 + }, + "inner_cpu_seconds": 2.03125, + "child_cpu": { + "creation_filetime": 134375815153414663, + "exit_filetime": 134375815174567926, + "kernel_seconds": 0.484375, + "user_seconds": 1.625, + "total_seconds": 2.109375 + }, + "runner_cpu_seconds": 0.0, + "environment": { + "total_seconds": 8.5, + "busy_seconds": 2.4375, + "busy_percent": 28.676470588235293, + "before": { + "user": 230.296875, + "kernel_including_idle": 1075.921875, + "idle": 957.046875, + "system": 118.875, + "perf_counter_seconds": 327.3071463, + "utc": "2026-10-27T13:31:55.340090+00:00" + }, + "after": { + "user": 232.109375, + "kernel_including_idle": 1082.609375, + "idle": 963.109375, + "system": 119.5, + "perf_counter_seconds": 329.4241257, + "utc": "2026-10-27T13:31:57.457105+00:00" + } + }, + "accounting": { + "inner_only_balance_seconds": 0.40625, + "inner_only_balance_percent": 4.779411764705882, + "child_outside_inner_seconds": 0.078125, + "runner_cpu_seconds": 0.0, + "unassigned_balance_seconds": 0.328125, + "unassigned_balance_percent": 3.860294117647059 + }, + "state_fnv1a64": "8035857386658397897" + }, + { + "item": { + "sequence": 4, + "variant": "baseline", + "route": "interactive", + "chunk": 1 + }, + "inner_cpu_seconds": 2.046875, + "child_cpu": { + "creation_filetime": 134375815174640115, + "exit_filetime": 134375815195919780, + "kernel_seconds": 0.5625, + "user_seconds": 1.546875, + "total_seconds": 2.109375 + }, + "runner_cpu_seconds": 0.0, + "environment": { + "total_seconds": 8.5, + "busy_seconds": 2.40625, + "busy_percent": 28.308823529411764, + "before": { + "user": 232.109375, + "kernel_including_idle": 1082.609375, + "idle": 963.109375, + "system": 119.5, + "perf_counter_seconds": 329.4296819, + "utc": "2026-10-27T13:31:57.463164+00:00" + }, + "after": { + "user": 233.84375, + "kernel_including_idle": 1089.375, + "idle": 969.203125, + "system": 120.171875, + "perf_counter_seconds": 331.5593194, + "utc": "2026-10-27T13:31:59.593006+00:00" + } + }, + "accounting": { + "inner_only_balance_seconds": 0.359375, + "inner_only_balance_percent": 4.227941176470588, + "child_outside_inner_seconds": 0.0625, + "runner_cpu_seconds": 0.0, + "unassigned_balance_seconds": 0.296875, + "unassigned_balance_percent": 3.4926470588235294 + }, + "state_fnv1a64": "8035857386658397897" + }, + { + "item": { + "sequence": 5, + "variant": "candidate", + "route": "interactive", + "chunk": 1 + }, + "inner_cpu_seconds": 2.03125, + "child_cpu": { + "creation_filetime": 134375815195995616, + "exit_filetime": 134375815217130487, + "kernel_seconds": 0.453125, + "user_seconds": 1.65625, + "total_seconds": 2.109375 + }, + "runner_cpu_seconds": 0.0, + "environment": { + "total_seconds": 8.4375, + "busy_seconds": 2.421875, + "busy_percent": 28.703703703703702, + "before": { + "user": 233.84375, + "kernel_including_idle": 1089.4375, + "idle": 969.25, + "system": 120.1875, + "perf_counter_seconds": 331.5651614, + "utc": "2026-10-27T13:31:59.597040+00:00" + }, + "after": { + "user": 235.671875, + "kernel_including_idle": 1096.046875, + "idle": 975.265625, + "system": 120.78125, + "perf_counter_seconds": 333.6804548, + "utc": "2026-10-27T13:32:01.713906+00:00" + } + }, + "accounting": { + "inner_only_balance_seconds": 0.390625, + "inner_only_balance_percent": 4.62962962962963, + "child_outside_inner_seconds": 0.078125, + "runner_cpu_seconds": 0.0, + "unassigned_balance_seconds": 0.3125, + "unassigned_balance_percent": 3.7037037037037037 + }, + "state_fnv1a64": "6154007251599136798" + } + ], + "archive": { + "bytes": 123388, + "sha256": "263a3bf8e229b89f1e443b12e522daf69d30de403affa0dc49fe674c0dd8f241", + "files": 75, + "inventory_files": 74, + "byte_exact_streams": 24 + }, + "source_checkout_forms": { + "experiments/candidates/alacritty/Cargo.toml": "git_blob_lf_to_crlf", + "experiments/candidates/alacritty/src/measure.rs": "git_blob_lf_to_crlf", + "experiments/measure/run.py": "git_blob_bytes", + "experiments/measure/r54_contract.py": "git_blob_bytes", + "baseline/experiments/candidates/alacritty/Cargo.lock": "git_blob_lf_to_crlf", + "baseline/experiments/candidates/alacritty/Cargo.toml": "git_blob_lf_to_crlf", + "baseline/experiments/candidates/alacritty/PROVENANCE.md": "git_blob_bytes", + "baseline/experiments/candidates/alacritty/README.md": "git_blob_bytes", + "baseline/experiments/candidates/alacritty/include/termforge_candidate.h": "git_blob_lf_to_crlf", + "baseline/experiments/candidates/alacritty/results/2026-10-24-windows/summary.json": "git_blob_bytes", + "baseline/experiments/candidates/alacritty/results/2026-10-24-windows/validation.json": "git_blob_bytes", + "baseline/experiments/candidates/alacritty/results/2026-10-25-termforge-1/alternate-query/candidate.jsonl": "git_blob_lf_to_crlf", + "baseline/experiments/candidates/alacritty/results/2026-10-25-termforge-1/alternate-query/candidate.stderr.txt": "git_blob_bytes", + "baseline/experiments/candidates/alacritty/results/2026-10-25-termforge-1/alternate-query/chunks.txt": "git_blob_lf_to_crlf", + "baseline/experiments/candidates/alacritty/results/2026-10-25-termforge-1/alternate-query/input.bin": "git_blob_lf_to_crlf", + "baseline/experiments/candidates/alacritty/results/2026-10-25-termforge-1/alternate-query/report.json": "git_blob_bytes", + "baseline/experiments/candidates/alacritty/results/2026-10-25-termforge-1/bounded-history/candidate.jsonl": "git_blob_lf_to_crlf", + "baseline/experiments/candidates/alacritty/results/2026-10-25-termforge-1/bounded-history/candidate.stderr.txt": "git_blob_bytes", + "baseline/experiments/candidates/alacritty/results/2026-10-25-termforge-1/bounded-history/chunks.txt": "git_blob_lf_to_crlf", + "baseline/experiments/candidates/alacritty/results/2026-10-25-termforge-1/bounded-history/input.bin": "git_blob_lf_to_crlf", + "baseline/experiments/candidates/alacritty/results/2026-10-25-termforge-1/bounded-history/report.json": "git_blob_bytes", + "baseline/experiments/candidates/alacritty/results/2026-10-25-termforge-1/fragmentation-byte/candidate.jsonl": "git_blob_lf_to_crlf", + "baseline/experiments/candidates/alacritty/results/2026-10-25-termforge-1/fragmentation-byte/candidate.stderr.txt": "git_blob_bytes", + "baseline/experiments/candidates/alacritty/results/2026-10-25-termforge-1/fragmentation-byte/chunks.txt": "git_blob_lf_to_crlf", + "baseline/experiments/candidates/alacritty/results/2026-10-25-termforge-1/fragmentation-byte/input.bin": "git_blob_lf_to_crlf", + "baseline/experiments/candidates/alacritty/results/2026-10-25-termforge-1/fragmentation-byte/report.json": "git_blob_bytes", + "baseline/experiments/candidates/alacritty/results/2026-10-25-termforge-1/fragmentation-whole/candidate.jsonl": "git_blob_lf_to_crlf", + "baseline/experiments/candidates/alacritty/results/2026-10-25-termforge-1/fragmentation-whole/candidate.stderr.txt": "git_blob_bytes", + "baseline/experiments/candidates/alacritty/results/2026-10-25-termforge-1/fragmentation-whole/chunks.txt": "git_blob_lf_to_crlf", + "baseline/experiments/candidates/alacritty/results/2026-10-25-termforge-1/fragmentation-whole/input.bin": "git_blob_lf_to_crlf", + "baseline/experiments/candidates/alacritty/results/2026-10-25-termforge-1/fragmentation-whole/report.json": "git_blob_bytes", + "baseline/experiments/candidates/alacritty/results/2026-10-25-termforge-1/summary.json": "git_blob_bytes", + "baseline/experiments/candidates/alacritty/results/2026-10-25-termforge-2/alternate-query/candidate.jsonl": "git_blob_lf_to_crlf", + "baseline/experiments/candidates/alacritty/results/2026-10-25-termforge-2/alternate-query/candidate.stderr.txt": "git_blob_bytes", + "baseline/experiments/candidates/alacritty/results/2026-10-25-termforge-2/alternate-query/chunks.txt": "git_blob_lf_to_crlf", + "baseline/experiments/candidates/alacritty/results/2026-10-25-termforge-2/alternate-query/input.bin": "git_blob_lf_to_crlf", + "baseline/experiments/candidates/alacritty/results/2026-10-25-termforge-2/alternate-query/report.json": "git_blob_bytes", + "baseline/experiments/candidates/alacritty/results/2026-10-25-termforge-2/bounded-history/candidate.jsonl": "git_blob_lf_to_crlf", + "baseline/experiments/candidates/alacritty/results/2026-10-25-termforge-2/bounded-history/candidate.stderr.txt": "git_blob_bytes", + "baseline/experiments/candidates/alacritty/results/2026-10-25-termforge-2/bounded-history/chunks.txt": "git_blob_lf_to_crlf", + "baseline/experiments/candidates/alacritty/results/2026-10-25-termforge-2/bounded-history/input.bin": "git_blob_lf_to_crlf", + "baseline/experiments/candidates/alacritty/results/2026-10-25-termforge-2/bounded-history/report.json": "git_blob_bytes", + "baseline/experiments/candidates/alacritty/results/2026-10-25-termforge-2/fragmentation-byte/candidate.jsonl": "git_blob_lf_to_crlf", + "baseline/experiments/candidates/alacritty/results/2026-10-25-termforge-2/fragmentation-byte/candidate.stderr.txt": "git_blob_bytes", + "baseline/experiments/candidates/alacritty/results/2026-10-25-termforge-2/fragmentation-byte/chunks.txt": "git_blob_lf_to_crlf", + "baseline/experiments/candidates/alacritty/results/2026-10-25-termforge-2/fragmentation-byte/input.bin": "git_blob_lf_to_crlf", + "baseline/experiments/candidates/alacritty/results/2026-10-25-termforge-2/fragmentation-byte/report.json": "git_blob_bytes", + "baseline/experiments/candidates/alacritty/results/2026-10-25-termforge-2/fragmentation-whole/candidate.jsonl": "git_blob_lf_to_crlf", + "baseline/experiments/candidates/alacritty/results/2026-10-25-termforge-2/fragmentation-whole/candidate.stderr.txt": "git_blob_bytes", + "baseline/experiments/candidates/alacritty/results/2026-10-25-termforge-2/fragmentation-whole/chunks.txt": "git_blob_lf_to_crlf", + "baseline/experiments/candidates/alacritty/results/2026-10-25-termforge-2/fragmentation-whole/input.bin": "git_blob_lf_to_crlf", + "baseline/experiments/candidates/alacritty/results/2026-10-25-termforge-2/fragmentation-whole/report.json": "git_blob_bytes", + "baseline/experiments/candidates/alacritty/results/2026-10-25-termforge-2/summary.json": "git_blob_bytes", + "baseline/experiments/candidates/alacritty/run.py": "git_blob_bytes", + "baseline/experiments/candidates/alacritty/src/ffi.rs": "git_blob_lf_to_crlf", + "baseline/experiments/candidates/alacritty/src/main.rs": "git_blob_lf_to_crlf", + "baseline/experiments/candidates/alacritty/src/measure.rs": "git_blob_lf_to_crlf", + "baseline/experiments/candidates/alacritty/test_candidate.py": "git_blob_bytes", + "baseline/experiments/candidates/alacritty/vendor/alacritty_terminal/CHANGELOG.md": "git_blob_bytes", + "baseline/experiments/candidates/alacritty/vendor/alacritty_terminal/Cargo.toml": "git_blob_lf_to_crlf", + "baseline/experiments/candidates/alacritty/vendor/alacritty_terminal/LICENSE-APACHE": "git_blob_lf_to_crlf", + "baseline/experiments/candidates/alacritty/vendor/alacritty_terminal/src/event.rs": "git_blob_lf_to_crlf", + "baseline/experiments/candidates/alacritty/vendor/alacritty_terminal/src/event_loop.rs": "git_blob_lf_to_crlf", + "baseline/experiments/candidates/alacritty/vendor/alacritty_terminal/src/grid/mod.rs": "git_blob_lf_to_crlf", + "baseline/experiments/candidates/alacritty/vendor/alacritty_terminal/src/grid/resize.rs": "git_blob_lf_to_crlf", + "baseline/experiments/candidates/alacritty/vendor/alacritty_terminal/src/grid/row.rs": "git_blob_lf_to_crlf", + "baseline/experiments/candidates/alacritty/vendor/alacritty_terminal/src/grid/storage.rs": "git_blob_lf_to_crlf", + "baseline/experiments/candidates/alacritty/vendor/alacritty_terminal/src/grid/tests.rs": "git_blob_lf_to_crlf", + "baseline/experiments/candidates/alacritty/vendor/alacritty_terminal/src/index.rs": "git_blob_lf_to_crlf", + "baseline/experiments/candidates/alacritty/vendor/alacritty_terminal/src/lib.rs": "git_blob_lf_to_crlf", + "baseline/experiments/candidates/alacritty/vendor/alacritty_terminal/src/selection.rs": "git_blob_lf_to_crlf", + "baseline/experiments/candidates/alacritty/vendor/alacritty_terminal/src/sync.rs": "git_blob_lf_to_crlf", + "baseline/experiments/candidates/alacritty/vendor/alacritty_terminal/src/term/cell.rs": "git_blob_lf_to_crlf", + "baseline/experiments/candidates/alacritty/vendor/alacritty_terminal/src/term/color.rs": "git_blob_lf_to_crlf", + "baseline/experiments/candidates/alacritty/vendor/alacritty_terminal/src/term/mod.rs": "git_blob_lf_to_crlf", + "baseline/experiments/candidates/alacritty/vendor/alacritty_terminal/src/term/search.rs": "git_blob_lf_to_crlf", + "baseline/experiments/candidates/alacritty/vendor/alacritty_terminal/src/thread.rs": "git_blob_lf_to_crlf", + "baseline/experiments/candidates/alacritty/vendor/alacritty_terminal/src/tty/mod.rs": "git_blob_lf_to_crlf", + "baseline/experiments/candidates/alacritty/vendor/alacritty_terminal/src/tty/unix.rs": "git_blob_lf_to_crlf", + "baseline/experiments/candidates/alacritty/vendor/alacritty_terminal/src/tty/windows/blocking.rs": "git_blob_lf_to_crlf", + "baseline/experiments/candidates/alacritty/vendor/alacritty_terminal/src/tty/windows/child.rs": "git_blob_lf_to_crlf", + "baseline/experiments/candidates/alacritty/vendor/alacritty_terminal/src/tty/windows/conpty.rs": "git_blob_lf_to_crlf", + "baseline/experiments/candidates/alacritty/vendor/alacritty_terminal/src/tty/windows/mod.rs": "git_blob_lf_to_crlf", + "baseline/experiments/candidates/alacritty/vendor/alacritty_terminal/src/vi_mode.rs": "git_blob_lf_to_crlf", + "candidate/experiments/candidates/alacritty/Cargo.lock": "git_blob_lf_to_crlf", + "candidate/experiments/candidates/alacritty/Cargo.toml": "git_blob_lf_to_crlf", + "candidate/experiments/candidates/alacritty/PROVENANCE.md": "git_blob_bytes", + "candidate/experiments/candidates/alacritty/README.md": "git_blob_bytes", + "candidate/experiments/candidates/alacritty/grapheme_checks.py": "git_blob_bytes", + "candidate/experiments/candidates/alacritty/include/termforge_candidate.h": "git_blob_lf_to_crlf", + "candidate/experiments/candidates/alacritty/results/2026-10-24-windows/summary.json": "git_blob_bytes", + "candidate/experiments/candidates/alacritty/results/2026-10-24-windows/validation.json": "git_blob_bytes", + "candidate/experiments/candidates/alacritty/results/2026-10-25-termforge-1/alternate-query/candidate.jsonl": "git_blob_lf_to_crlf", + "candidate/experiments/candidates/alacritty/results/2026-10-25-termforge-1/alternate-query/candidate.stderr.txt": "git_blob_bytes", + "candidate/experiments/candidates/alacritty/results/2026-10-25-termforge-1/alternate-query/chunks.txt": "git_blob_lf_to_crlf", + "candidate/experiments/candidates/alacritty/results/2026-10-25-termforge-1/alternate-query/input.bin": "git_blob_lf_to_crlf", + "candidate/experiments/candidates/alacritty/results/2026-10-25-termforge-1/alternate-query/report.json": "git_blob_bytes", + "candidate/experiments/candidates/alacritty/results/2026-10-25-termforge-1/bounded-history/candidate.jsonl": "git_blob_lf_to_crlf", + "candidate/experiments/candidates/alacritty/results/2026-10-25-termforge-1/bounded-history/candidate.stderr.txt": "git_blob_bytes", + "candidate/experiments/candidates/alacritty/results/2026-10-25-termforge-1/bounded-history/chunks.txt": "git_blob_lf_to_crlf", + "candidate/experiments/candidates/alacritty/results/2026-10-25-termforge-1/bounded-history/input.bin": "git_blob_lf_to_crlf", + "candidate/experiments/candidates/alacritty/results/2026-10-25-termforge-1/bounded-history/report.json": "git_blob_bytes", + "candidate/experiments/candidates/alacritty/results/2026-10-25-termforge-1/fragmentation-byte/candidate.jsonl": "git_blob_lf_to_crlf", + "candidate/experiments/candidates/alacritty/results/2026-10-25-termforge-1/fragmentation-byte/candidate.stderr.txt": "git_blob_bytes", + "candidate/experiments/candidates/alacritty/results/2026-10-25-termforge-1/fragmentation-byte/chunks.txt": "git_blob_lf_to_crlf", + "candidate/experiments/candidates/alacritty/results/2026-10-25-termforge-1/fragmentation-byte/input.bin": "git_blob_lf_to_crlf", + "candidate/experiments/candidates/alacritty/results/2026-10-25-termforge-1/fragmentation-byte/report.json": "git_blob_bytes", + "candidate/experiments/candidates/alacritty/results/2026-10-25-termforge-1/fragmentation-whole/candidate.jsonl": "git_blob_lf_to_crlf", + "candidate/experiments/candidates/alacritty/results/2026-10-25-termforge-1/fragmentation-whole/candidate.stderr.txt": "git_blob_bytes", + "candidate/experiments/candidates/alacritty/results/2026-10-25-termforge-1/fragmentation-whole/chunks.txt": "git_blob_lf_to_crlf", + "candidate/experiments/candidates/alacritty/results/2026-10-25-termforge-1/fragmentation-whole/input.bin": "git_blob_lf_to_crlf", + "candidate/experiments/candidates/alacritty/results/2026-10-25-termforge-1/fragmentation-whole/report.json": "git_blob_bytes", + "candidate/experiments/candidates/alacritty/results/2026-10-25-termforge-1/summary.json": "git_blob_bytes", + "candidate/experiments/candidates/alacritty/results/2026-10-25-termforge-2/alternate-query/candidate.jsonl": "git_blob_lf_to_crlf", + "candidate/experiments/candidates/alacritty/results/2026-10-25-termforge-2/alternate-query/candidate.stderr.txt": "git_blob_bytes", + "candidate/experiments/candidates/alacritty/results/2026-10-25-termforge-2/alternate-query/chunks.txt": "git_blob_lf_to_crlf", + "candidate/experiments/candidates/alacritty/results/2026-10-25-termforge-2/alternate-query/input.bin": "git_blob_lf_to_crlf", + "candidate/experiments/candidates/alacritty/results/2026-10-25-termforge-2/alternate-query/report.json": "git_blob_bytes", + "candidate/experiments/candidates/alacritty/results/2026-10-25-termforge-2/bounded-history/candidate.jsonl": "git_blob_lf_to_crlf", + "candidate/experiments/candidates/alacritty/results/2026-10-25-termforge-2/bounded-history/candidate.stderr.txt": "git_blob_bytes", + "candidate/experiments/candidates/alacritty/results/2026-10-25-termforge-2/bounded-history/chunks.txt": "git_blob_lf_to_crlf", + "candidate/experiments/candidates/alacritty/results/2026-10-25-termforge-2/bounded-history/input.bin": "git_blob_lf_to_crlf", + "candidate/experiments/candidates/alacritty/results/2026-10-25-termforge-2/bounded-history/report.json": "git_blob_bytes", + "candidate/experiments/candidates/alacritty/results/2026-10-25-termforge-2/fragmentation-byte/candidate.jsonl": "git_blob_lf_to_crlf", + "candidate/experiments/candidates/alacritty/results/2026-10-25-termforge-2/fragmentation-byte/candidate.stderr.txt": "git_blob_bytes", + "candidate/experiments/candidates/alacritty/results/2026-10-25-termforge-2/fragmentation-byte/chunks.txt": "git_blob_lf_to_crlf", + "candidate/experiments/candidates/alacritty/results/2026-10-25-termforge-2/fragmentation-byte/input.bin": "git_blob_lf_to_crlf", + "candidate/experiments/candidates/alacritty/results/2026-10-25-termforge-2/fragmentation-byte/report.json": "git_blob_bytes", + "candidate/experiments/candidates/alacritty/results/2026-10-25-termforge-2/fragmentation-whole/candidate.jsonl": "git_blob_lf_to_crlf", + "candidate/experiments/candidates/alacritty/results/2026-10-25-termforge-2/fragmentation-whole/candidate.stderr.txt": "git_blob_bytes", + "candidate/experiments/candidates/alacritty/results/2026-10-25-termforge-2/fragmentation-whole/chunks.txt": "git_blob_lf_to_crlf", + "candidate/experiments/candidates/alacritty/results/2026-10-25-termforge-2/fragmentation-whole/input.bin": "git_blob_lf_to_crlf", + "candidate/experiments/candidates/alacritty/results/2026-10-25-termforge-2/fragmentation-whole/report.json": "git_blob_bytes", + "candidate/experiments/candidates/alacritty/results/2026-10-25-termforge-2/summary.json": "git_blob_bytes", + "candidate/experiments/candidates/alacritty/run.py": "git_blob_bytes", + "candidate/experiments/candidates/alacritty/src/ffi.rs": "git_blob_lf_to_crlf", + "candidate/experiments/candidates/alacritty/src/main.rs": "git_blob_lf_to_crlf", + "candidate/experiments/candidates/alacritty/src/measure.rs": "git_blob_lf_to_crlf", + "candidate/experiments/candidates/alacritty/test_candidate.py": "git_blob_bytes", + "candidate/experiments/candidates/alacritty/vendor/alacritty_terminal/CHANGELOG.md": "git_blob_bytes", + "candidate/experiments/candidates/alacritty/vendor/alacritty_terminal/Cargo.toml": "git_blob_lf_to_crlf", + "candidate/experiments/candidates/alacritty/vendor/alacritty_terminal/LICENSE-APACHE": "git_blob_lf_to_crlf", + "candidate/experiments/candidates/alacritty/vendor/alacritty_terminal/src/event.rs": "git_blob_lf_to_crlf", + "candidate/experiments/candidates/alacritty/vendor/alacritty_terminal/src/event_loop.rs": "git_blob_lf_to_crlf", + "candidate/experiments/candidates/alacritty/vendor/alacritty_terminal/src/grid/mod.rs": "git_blob_lf_to_crlf", + "candidate/experiments/candidates/alacritty/vendor/alacritty_terminal/src/grid/resize.rs": "git_blob_lf_to_crlf", + "candidate/experiments/candidates/alacritty/vendor/alacritty_terminal/src/grid/row.rs": "git_blob_lf_to_crlf", + "candidate/experiments/candidates/alacritty/vendor/alacritty_terminal/src/grid/storage.rs": "git_blob_lf_to_crlf", + "candidate/experiments/candidates/alacritty/vendor/alacritty_terminal/src/grid/tests.rs": "git_blob_lf_to_crlf", + "candidate/experiments/candidates/alacritty/vendor/alacritty_terminal/src/index.rs": "git_blob_lf_to_crlf", + "candidate/experiments/candidates/alacritty/vendor/alacritty_terminal/src/lib.rs": "git_blob_lf_to_crlf", + "candidate/experiments/candidates/alacritty/vendor/alacritty_terminal/src/selection.rs": "git_blob_lf_to_crlf", + "candidate/experiments/candidates/alacritty/vendor/alacritty_terminal/src/sync.rs": "git_blob_lf_to_crlf", + "candidate/experiments/candidates/alacritty/vendor/alacritty_terminal/src/term/cell.rs": "git_blob_lf_to_crlf", + "candidate/experiments/candidates/alacritty/vendor/alacritty_terminal/src/term/color.rs": "git_blob_lf_to_crlf", + "candidate/experiments/candidates/alacritty/vendor/alacritty_terminal/src/term/grapheme.rs": "git_blob_lf_to_crlf", + "candidate/experiments/candidates/alacritty/vendor/alacritty_terminal/src/term/mod.rs": "git_blob_lf_to_crlf", + "candidate/experiments/candidates/alacritty/vendor/alacritty_terminal/src/term/search.rs": "git_blob_lf_to_crlf", + "candidate/experiments/candidates/alacritty/vendor/alacritty_terminal/src/thread.rs": "git_blob_lf_to_crlf", + "candidate/experiments/candidates/alacritty/vendor/alacritty_terminal/src/tty/mod.rs": "git_blob_lf_to_crlf", + "candidate/experiments/candidates/alacritty/vendor/alacritty_terminal/src/tty/unix.rs": "git_blob_lf_to_crlf", + "candidate/experiments/candidates/alacritty/vendor/alacritty_terminal/src/tty/windows/blocking.rs": "git_blob_lf_to_crlf", + "candidate/experiments/candidates/alacritty/vendor/alacritty_terminal/src/tty/windows/child.rs": "git_blob_lf_to_crlf", + "candidate/experiments/candidates/alacritty/vendor/alacritty_terminal/src/tty/windows/conpty.rs": "git_blob_lf_to_crlf", + "candidate/experiments/candidates/alacritty/vendor/alacritty_terminal/src/tty/windows/mod.rs": "git_blob_lf_to_crlf", + "candidate/experiments/candidates/alacritty/vendor/alacritty_terminal/src/vi_mode.rs": "git_blob_lf_to_crlf", + "candidate/experiments/candidates/alacritty/vendor/alacritty_terminal/tests/data/GraphemeBreakTest-17.0.0.txt": "git_blob_lf_to_crlf", + "candidate/experiments/candidates/alacritty/vendor/alacritty_terminal/tests/data/UNICODE-LICENSE.txt": "git_blob_lf_to_crlf" + }, + "sanity_oracles": 4, + "observation_oracles": 6, + "formal_cost_samples": 0, + "performance_gate": "not_evaluated", + "old_hosted_event_cause": "unknown", + "source_tree": "86e2342d16acd101fe182793d20aef3f1f017633", + "provider": { + "run": 40512310499, + "attempt": 1, + "job": 122217071656, + "event": "push", + "branch": "perf/grapheme-cpu-hosted-r54-entry3", + "source": "4772f4f937c4f0f220be1630592f84bed3a591c8", + "status": "completed", + "conclusion": "success", + "labels": [ + "windows-2025" + ], + "started_at": "2026-10-27T13:28:22Z", + "completed_at": "2026-10-27T13:32:07Z", + "steps": [ + { + "number": 1, + "name": "Set up job", + "status": "completed", + "conclusion": "success", + "started_at": "2026-10-27T13:28:23Z", + "completed_at": "2026-10-27T13:28:24Z" + }, + { + "number": 2, + "name": "Run actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1", + "status": "completed", + "conclusion": "success", + "started_at": "2026-10-27T13:28:24Z", + "completed_at": "2026-10-27T13:28:33Z" + }, + { + "number": 3, + "name": "Run actions/setup-python@6d4fd22b20465ccc0f7354476588e8df7c9ca0e0", + "status": "completed", + "conclusion": "success", + "started_at": "2026-10-27T13:28:33Z", + "completed_at": "2026-10-27T13:28:33Z" + }, + { + "number": 4, + "name": "Install fixed CPU observer", + "status": "completed", + "conclusion": "success", + "started_at": "2026-10-27T13:28:33Z", + "completed_at": "2026-10-27T13:28:40Z" + }, + { + "number": 5, + "name": "Select fixed compiler", + "status": "completed", + "conclusion": "success", + "started_at": "2026-10-27T13:28:40Z", + "completed_at": "2026-10-27T13:28:49Z" + }, + { + "number": 6, + "name": "Verify build, accounting and publication boundaries", + "status": "completed", + "conclusion": "success", + "started_at": "2026-10-27T13:28:49Z", + "completed_at": "2026-10-27T13:29:04Z" + }, + { + "number": 7, + "name": "Build audited pair and execute one six-observation diagnosis", + "status": "completed", + "conclusion": "success", + "started_at": "2026-10-27T13:29:04Z", + "completed_at": "2026-10-27T13:32:02Z" + }, + { + "number": 8, + "name": "Prepare the synthetic artifact whitelist", + "status": "completed", + "conclusion": "success", + "started_at": "2026-10-27T13:32:02Z", + "completed_at": "2026-10-27T13:32:03Z" + }, + { + "number": 9, + "name": "Preserve complete or failed synthetic evidence", + "status": "completed", + "conclusion": "success", + "started_at": "2026-10-27T13:32:03Z", + "completed_at": "2026-10-27T13:32:04Z" + }, + { + "number": 17, + "name": "Post Run actions/setup-python@6d4fd22b20465ccc0f7354476588e8df7c9ca0e0", + "status": "completed", + "conclusion": "success", + "started_at": "2026-10-27T13:32:04Z", + "completed_at": "2026-10-27T13:32:04Z" + }, + { + "number": 18, + "name": "Post Run actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1", + "status": "completed", + "conclusion": "success", + "started_at": "2026-10-27T13:32:04Z", + "completed_at": "2026-10-27T13:32:06Z" + }, + { + "number": 19, + "name": "Complete job", + "status": "completed", + "conclusion": "success", + "started_at": "2026-10-27T13:32:06Z", + "completed_at": "2026-10-27T13:32:06Z" + } + ], + "artifact_id": 12738430576, + "artifact_digest": "sha256:263a3bf8e229b89f1e443b12e522daf69d30de403affa0dc49fe674c0dd8f241", + "image": "windows-2025-vs2026", + "image_version": "20261021.278.1" + }, + "previous_entries": [ + { + "source": "eed0ab7bae5fcc5d05a25cea5161dabc4f935878", + "run": 40509157581, + "job": 122206160085, + "attempt": 1, + "result": "preparation_test_path_boundary_failure", + "preparation_tests": 31, + "errors": 1, + "builds": 0, + "observations": 0, + "actual_temp_path_form": "not_recorded" + }, + { + "source": "c14adb9a7fa446476ad5c95c1f8c1b139ceb5ac4", + "branch": "perf/grapheme-cpu-hosted-r54-setup2", + "default_run": 40511533857, + "result": "no_cpu_workflow_run_listed", + "observations": 0, + "actual_push_file_list": "not_recorded" + } + ], + "local_tests": { + "measurement": { + "tests": 40, + "seconds": 19.436 + }, + "entry3_boundary": { + "tests": 4, + "seconds": 0.297 + }, + "hosted_preparation": { + "tests": 31, + "seconds": 14.042 + } + }, + "initial_ci": { + "40509376603": { + "jobs": [ + { + "id": 122206943124, + "name": "alacritty-headless (windows-2025, experiments/candidates/alacritty/target/release/termforge-alacr...", + "conclusion": "failure", + "steps": [ + { + "name": "Set up job", + "status": "completed", + "conclusion": "success", + "number": 1, + "started_at": "2026-10-27T13:08:09Z", + "completed_at": "2026-10-27T13:08:10Z" + }, + { + "name": "Run actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1", + "status": "completed", + "conclusion": "success", + "number": 2, + "started_at": "2026-10-27T13:08:10Z", + "completed_at": "2026-10-27T13:08:16Z" + }, + { + "name": "Run actions/setup-python@6d4fd22b20465ccc0f7354476588e8df7c9ca0e0", + "status": "completed", + "conclusion": "success", + "number": 3, + "started_at": "2026-10-27T13:08:16Z", + "completed_at": "2026-10-27T13:08:16Z" + }, + { + "name": "Install fixed semantic references", + "status": "completed", + "conclusion": "success", + "number": 4, + "started_at": "2026-10-27T13:08:16Z", + "completed_at": "2026-10-27T13:08:22Z" + }, + { + "name": "Install fixed CPU observer", + "status": "completed", + "conclusion": "success", + "number": 5, + "started_at": "2026-10-27T13:08:22Z", + "completed_at": "2026-10-27T13:08:25Z" + }, + { + "name": "Select fixed Rust toolchain", + "status": "completed", + "conclusion": "success", + "number": 6, + "started_at": "2026-10-27T13:08:25Z", + "completed_at": "2026-10-27T13:08:34Z" + }, + { + "name": "Build locked candidate", + "status": "completed", + "conclusion": "success", + "number": 7, + "started_at": "2026-10-27T13:08:34Z", + "completed_at": "2026-10-27T13:09:17Z" + }, + { + "name": "Test Rust projection", + "status": "completed", + "conclusion": "success", + "number": 8, + "started_at": "2026-10-27T13:09:17Z", + "completed_at": "2026-10-27T13:09:33Z" + }, + { + "name": "Test fixed upstream and compatibility patches", + "status": "completed", + "conclusion": "success", + "number": 9, + "started_at": "2026-10-27T13:09:33Z", + "completed_at": "2026-10-27T13:09:50Z" + }, + { + "name": "Build candidate measurement", + "status": "completed", + "conclusion": "success", + "number": 10, + "started_at": "2026-10-27T13:09:50Z", + "completed_at": "2026-10-27T13:09:51Z" + }, + { + "name": "Build candidate measurement without allocator wrapper", + "status": "completed", + "conclusion": "success", + "number": 11, + "started_at": "2026-10-27T13:09:51Z", + "completed_at": "2026-10-27T13:10:22Z" + }, + { + "name": "Configure native trace on Windows", + "status": "completed", + "conclusion": "success", + "number": 12, + "started_at": "2026-10-27T13:10:22Z", + "completed_at": "2026-10-27T13:10:33Z" + }, + { + "name": "Configure native trace on Unix", + "status": "completed", + "conclusion": "skipped", + "number": 13, + "started_at": "2026-10-27T13:10:33Z", + "completed_at": "2026-10-27T13:10:33Z" + }, + { + "name": "Build native trace", + "status": "completed", + "conclusion": "success", + "number": 14, + "started_at": "2026-10-27T13:10:33Z", + "completed_at": "2026-10-27T13:10:37Z" + }, + { + "name": "Build native measurement", + "status": "completed", + "conclusion": "success", + "number": 15, + "started_at": "2026-10-27T13:10:37Z", + "completed_at": "2026-10-27T13:10:41Z" + }, + { + "name": "Build and test candidate C++ adapter", + "status": "completed", + "conclusion": "success", + "number": 16, + "started_at": "2026-10-27T13:10:41Z", + "completed_at": "2026-10-27T13:10:49Z" + }, + { + "name": "Build and test candidate shell on Windows", + "status": "completed", + "conclusion": "success", + "number": 17, + "started_at": "2026-10-27T13:10:49Z", + "completed_at": "2026-10-27T13:11:35Z" + }, + { + "name": "Verify empty-title GUI and UTF-8 conversion on Windows", + "status": "completed", + "conclusion": "success", + "number": 18, + "started_at": "2026-10-27T13:11:35Z", + "completed_at": "2026-10-27T13:12:03Z" + }, + { + "name": "Build real application capture on Windows", + "status": "completed", + "conclusion": "success", + "number": 19, + "started_at": "2026-10-27T13:12:03Z", + "completed_at": "2026-10-27T13:12:07Z" + }, + { + "name": "Run headless integration checks", + "status": "completed", + "conclusion": "success", + "number": 20, + "started_at": "2026-10-27T13:12:07Z", + "completed_at": "2026-10-27T13:12:08Z" + }, + { + "name": "Test runner and failure gates", + "status": "completed", + "conclusion": "success", + "number": 21, + "started_at": "2026-10-27T13:12:08Z", + "completed_at": "2026-10-27T13:12:08Z" + }, + { + "name": "Compare common native traces", + "status": "completed", + "conclusion": "success", + "number": 22, + "started_at": "2026-10-27T13:12:08Z", + "completed_at": "2026-10-27T13:12:11Z" + }, + { + "name": "Test common comparison and failure gates", + "status": "completed", + "conclusion": "success", + "number": 23, + "started_at": "2026-10-27T13:12:11Z", + "completed_at": "2026-10-27T13:12:14Z" + }, + { + "name": "Compare extended capability traces", + "status": "completed", + "conclusion": "success", + "number": 24, + "started_at": "2026-10-27T13:12:14Z", + "completed_at": "2026-10-27T13:12:15Z" + }, + { + "name": "Test extended capabilities and unsupported gates", + "status": "completed", + "conclusion": "success", + "number": 25, + "started_at": "2026-10-27T13:12:15Z", + "completed_at": "2026-10-27T13:12:16Z" + }, + { + "name": "Compare real application traces on Windows", + "status": "completed", + "conclusion": "success", + "number": 26, + "started_at": "2026-10-27T13:12:16Z", + "completed_at": "2026-10-27T13:12:17Z" + }, + { + "name": "Test real application traces on Windows", + "status": "completed", + "conclusion": "success", + "number": 27, + "started_at": "2026-10-27T13:12:17Z", + "completed_at": "2026-10-27T13:12:29Z" + }, + { + "name": "Run bounded recovery fuzz", + "status": "completed", + "conclusion": "success", + "number": 28, + "started_at": "2026-10-27T13:12:29Z", + "completed_at": "2026-10-27T13:12:29Z" + }, + { + "name": "Test fuzz minimization and fixed regressions", + "status": "completed", + "conclusion": "success", + "number": 29, + "started_at": "2026-10-27T13:12:29Z", + "completed_at": "2026-10-27T13:12:31Z" + }, + { + "name": "Compare interleaved project costs", + "status": "completed", + "conclusion": "success", + "number": 30, + "started_at": "2026-10-27T13:12:31Z", + "completed_at": "2026-10-27T13:13:02Z" + }, + { + "name": "Compare interleaved interactive costs", + "status": "completed", + "conclusion": "success", + "number": 31, + "started_at": "2026-10-27T13:13:02Z", + "completed_at": "2026-10-27T13:13:42Z" + }, + { + "name": "Test measurement contracts", + "status": "completed", + "conclusion": "failure", + "number": 32, + "started_at": "2026-10-27T13:13:42Z", + "completed_at": "2026-10-27T13:14:15Z" + }, + { + "name": "Preserve synthetic candidate evidence", + "status": "completed", + "conclusion": "success", + "number": 33, + "started_at": "2026-10-27T13:14:15Z", + "completed_at": "2026-10-27T13:14:19Z" + }, + { + "name": "Post Run actions/setup-python@6d4fd22b20465ccc0f7354476588e8df7c9ca0e0", + "status": "completed", + "conclusion": "skipped", + "number": 65, + "started_at": "2026-10-27T13:14:19Z", + "completed_at": "2026-10-27T13:14:19Z" + }, + { + "name": "Post Run actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1", + "status": "completed", + "conclusion": "success", + "number": 66, + "started_at": "2026-10-27T13:14:19Z", + "completed_at": "2026-10-27T13:14:22Z" + }, + { + "name": "Complete job", + "status": "completed", + "conclusion": "success", + "number": 67, + "started_at": "2026-10-27T13:14:22Z", + "completed_at": "2026-10-27T13:14:22Z" + } + ], + "log_evidence": [ + "2026-10-27T13:12:08.8322257Z Ran 6 tests in 0.149s", + "2026-10-27T13:12:14.1163493Z Ran 4 tests in 2.122s", + "2026-10-27T13:12:16.1916158Z Ran 4 tests in 0.522s", + "2026-10-27T13:12:28.9034935Z Ran 8 tests in 10.796s", + "2026-10-27T13:12:30.9126353Z Ran 4 tests in 0.716s", + "2026-10-27T13:14:15.3632402Z ERROR: test_publication_keeps_failed_streams_and_separates_the_six_observation_scope (test_r54_cpu_hosted.CpuHostedBoundaryTests.test_publication_keeps_failed_streams_and_separates_the_six_observation_scope)", + "2026-10-27T13:14:15.3646222Z ValueError: publication paths must stay in workspace local", + "2026-10-27T13:14:15.3646942Z Ran 49 tests in 31.869s", + "2026-10-27T13:14:15.3647151Z FAILED (errors=1)" + ] + }, + { + "id": 122206943421, + "name": "alacritty-headless (ubuntu-24.04, experiments/candidates/alacritty/target/release/termforge-alacr...", + "conclusion": "success", + "steps": [ + { + "name": "Set up job", + "status": "completed", + "conclusion": "success", + "number": 1, + "started_at": "2026-10-27T13:08:10Z", + "completed_at": "2026-10-27T13:08:11Z" + }, + { + "name": "Run actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1", + "status": "completed", + "conclusion": "success", + "number": 2, + "started_at": "2026-10-27T13:08:11Z", + "completed_at": "2026-10-27T13:08:13Z" + }, + { + "name": "Run actions/setup-python@6d4fd22b20465ccc0f7354476588e8df7c9ca0e0", + "status": "completed", + "conclusion": "success", + "number": 3, + "started_at": "2026-10-27T13:08:13Z", + "completed_at": "2026-10-27T13:08:13Z" + }, + { + "name": "Install fixed semantic references", + "status": "completed", + "conclusion": "success", + "number": 4, + "started_at": "2026-10-27T13:08:13Z", + "completed_at": "2026-10-27T13:08:14Z" + }, + { + "name": "Install fixed CPU observer", + "status": "completed", + "conclusion": "success", + "number": 5, + "started_at": "2026-10-27T13:08:14Z", + "completed_at": "2026-10-27T13:08:15Z" + }, + { + "name": "Select fixed Rust toolchain", + "status": "completed", + "conclusion": "success", + "number": 6, + "started_at": "2026-10-27T13:08:15Z", + "completed_at": "2026-10-27T13:08:22Z" + }, + { + "name": "Build locked candidate", + "status": "completed", + "conclusion": "success", + "number": 7, + "started_at": "2026-10-27T13:08:22Z", + "completed_at": "2026-10-27T13:08:43Z" + }, + { + "name": "Test Rust projection", + "status": "completed", + "conclusion": "success", + "number": 8, + "started_at": "2026-10-27T13:08:43Z", + "completed_at": "2026-10-27T13:08:52Z" + }, + { + "name": "Test fixed upstream and compatibility patches", + "status": "completed", + "conclusion": "success", + "number": 9, + "started_at": "2026-10-27T13:08:52Z", + "completed_at": "2026-10-27T13:09:03Z" + }, + { + "name": "Build candidate measurement", + "status": "completed", + "conclusion": "success", + "number": 10, + "started_at": "2026-10-27T13:09:03Z", + "completed_at": "2026-10-27T13:09:03Z" + }, + { + "name": "Build candidate measurement without allocator wrapper", + "status": "completed", + "conclusion": "success", + "number": 11, + "started_at": "2026-10-27T13:09:03Z", + "completed_at": "2026-10-27T13:09:21Z" + }, + { + "name": "Configure native trace on Windows", + "status": "completed", + "conclusion": "skipped", + "number": 12, + "started_at": "2026-10-27T13:09:21Z", + "completed_at": "2026-10-27T13:09:21Z" + }, + { + "name": "Configure native trace on Unix", + "status": "completed", + "conclusion": "success", + "number": 13, + "started_at": "2026-10-27T13:09:21Z", + "completed_at": "2026-10-27T13:09:23Z" + }, + { + "name": "Build native trace", + "status": "completed", + "conclusion": "success", + "number": 14, + "started_at": "2026-10-27T13:09:23Z", + "completed_at": "2026-10-27T13:09:24Z" + }, + { + "name": "Build native measurement", + "status": "completed", + "conclusion": "success", + "number": 15, + "started_at": "2026-10-27T13:09:24Z", + "completed_at": "2026-10-27T13:09:27Z" + }, + { + "name": "Build and test candidate C++ adapter", + "status": "completed", + "conclusion": "success", + "number": 16, + "started_at": "2026-10-27T13:09:27Z", + "completed_at": "2026-10-27T13:09:34Z" + }, + { + "name": "Build and test candidate shell on Windows", + "status": "completed", + "conclusion": "skipped", + "number": 17, + "started_at": "2026-10-27T13:09:34Z", + "completed_at": "2026-10-27T13:09:34Z" + }, + { + "name": "Verify empty-title GUI and UTF-8 conversion on Windows", + "status": "completed", + "conclusion": "skipped", + "number": 18, + "started_at": "2026-10-27T13:09:34Z", + "completed_at": "2026-10-27T13:09:34Z" + }, + { + "name": "Build real application capture on Windows", + "status": "completed", + "conclusion": "skipped", + "number": 19, + "started_at": "2026-10-27T13:09:34Z", + "completed_at": "2026-10-27T13:09:34Z" + }, + { + "name": "Run headless integration checks", + "status": "completed", + "conclusion": "success", + "number": 20, + "started_at": "2026-10-27T13:09:34Z", + "completed_at": "2026-10-27T13:09:34Z" + }, + { + "name": "Test runner and failure gates", + "status": "completed", + "conclusion": "success", + "number": 21, + "started_at": "2026-10-27T13:09:34Z", + "completed_at": "2026-10-27T13:09:34Z" + }, + { + "name": "Compare common native traces", + "status": "completed", + "conclusion": "success", + "number": 22, + "started_at": "2026-10-27T13:09:34Z", + "completed_at": "2026-10-27T13:09:35Z" + }, + { + "name": "Test common comparison and failure gates", + "status": "completed", + "conclusion": "success", + "number": 23, + "started_at": "2026-10-27T13:09:35Z", + "completed_at": "2026-10-27T13:09:36Z" + }, + { + "name": "Compare extended capability traces", + "status": "completed", + "conclusion": "success", + "number": 24, + "started_at": "2026-10-27T13:09:36Z", + "completed_at": "2026-10-27T13:09:36Z" + }, + { + "name": "Test extended capabilities and unsupported gates", + "status": "completed", + "conclusion": "success", + "number": 25, + "started_at": "2026-10-27T13:09:36Z", + "completed_at": "2026-10-27T13:09:37Z" + }, + { + "name": "Compare real application traces on Windows", + "status": "completed", + "conclusion": "skipped", + "number": 26, + "started_at": "2026-10-27T13:09:37Z", + "completed_at": "2026-10-27T13:09:37Z" + }, + { + "name": "Test real application traces on Windows", + "status": "completed", + "conclusion": "skipped", + "number": 27, + "started_at": "2026-10-27T13:09:37Z", + "completed_at": "2026-10-27T13:09:37Z" + }, + { + "name": "Run bounded recovery fuzz", + "status": "completed", + "conclusion": "success", + "number": 28, + "started_at": "2026-10-27T13:09:37Z", + "completed_at": "2026-10-27T13:09:37Z" + }, + { + "name": "Test fuzz minimization and fixed regressions", + "status": "completed", + "conclusion": "success", + "number": 29, + "started_at": "2026-10-27T13:09:37Z", + "completed_at": "2026-10-27T13:09:37Z" + }, + { + "name": "Compare interleaved project costs", + "status": "completed", + "conclusion": "success", + "number": 30, + "started_at": "2026-10-27T13:09:37Z", + "completed_at": "2026-10-27T13:10:06Z" + }, + { + "name": "Compare interleaved interactive costs", + "status": "completed", + "conclusion": "success", + "number": 31, + "started_at": "2026-10-27T13:10:06Z", + "completed_at": "2026-10-27T13:10:45Z" + }, + { + "name": "Test measurement contracts", + "status": "completed", + "conclusion": "success", + "number": 32, + "started_at": "2026-10-27T13:10:45Z", + "completed_at": "2026-10-27T13:11:11Z" + }, + { + "name": "Preserve synthetic candidate evidence", + "status": "completed", + "conclusion": "success", + "number": 33, + "started_at": "2026-10-27T13:11:11Z", + "completed_at": "2026-10-27T13:11:14Z" + }, + { + "name": "Post Run actions/setup-python@6d4fd22b20465ccc0f7354476588e8df7c9ca0e0", + "status": "completed", + "conclusion": "success", + "number": 65, + "started_at": "2026-10-27T13:11:14Z", + "completed_at": "2026-10-27T13:11:14Z" + }, + { + "name": "Post Run actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1", + "status": "completed", + "conclusion": "success", + "number": 66, + "started_at": "2026-10-27T13:11:14Z", + "completed_at": "2026-10-27T13:11:15Z" + }, + { + "name": "Complete job", + "status": "completed", + "conclusion": "success", + "number": 67, + "started_at": "2026-10-27T13:11:15Z", + "completed_at": "2026-10-27T13:11:15Z" + } + ], + "log_evidence": [] + } + ] + }, + "40509377108": { + "jobs": [ + { + "id": 122206924548, + "name": "regression (windows-2025, build/bin/Release)", + "conclusion": "failure", + "steps": [ + { + "name": "Set up job", + "status": "completed", + "conclusion": "success", + "number": 1, + "started_at": "2026-10-27T13:08:07Z", + "completed_at": "2026-10-27T13:08:08Z" + }, + { + "name": "Run actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1", + "status": "completed", + "conclusion": "success", + "number": 2, + "started_at": "2026-10-27T13:08:08Z", + "completed_at": "2026-10-27T13:08:13Z" + }, + { + "name": "Run actions/setup-python@6d4fd22b20465ccc0f7354476588e8df7c9ca0e0", + "status": "completed", + "conclusion": "success", + "number": 3, + "started_at": "2026-10-27T13:08:13Z", + "completed_at": "2026-10-27T13:08:13Z" + }, + { + "name": "Install test dependencies", + "status": "completed", + "conclusion": "success", + "number": 4, + "started_at": "2026-10-27T13:08:13Z", + "completed_at": "2026-10-27T13:08:20Z" + }, + { + "name": "Configure", + "status": "completed", + "conclusion": "success", + "number": 5, + "started_at": "2026-10-27T13:08:20Z", + "completed_at": "2026-10-27T13:08:35Z" + }, + { + "name": "Build", + "status": "completed", + "conclusion": "success", + "number": 6, + "started_at": "2026-10-27T13:08:35Z", + "completed_at": "2026-10-27T13:09:33Z" + }, + { + "name": "Verify shell preparation guards and oracles", + "status": "completed", + "conclusion": "success", + "number": 7, + "started_at": "2026-10-27T13:09:33Z", + "completed_at": "2026-10-27T13:09:34Z" + }, + { + "name": "Prepare hosted Windows shell prerequisites", + "status": "completed", + "conclusion": "success", + "number": 8, + "started_at": "2026-10-27T13:09:34Z", + "completed_at": "2026-10-27T13:10:14Z" + }, + { + "name": "Preserve sanitized shell preparation result", + "status": "completed", + "conclusion": "success", + "number": 9, + "started_at": "2026-10-27T13:10:14Z", + "completed_at": "2026-10-27T13:10:16Z" + }, + { + "name": "Test", + "status": "completed", + "conclusion": "failure", + "number": 10, + "started_at": "2026-10-27T13:10:16Z", + "completed_at": "2026-10-27T13:14:29Z" + }, + { + "name": "Preserve clipboard and window integration evidence", + "status": "completed", + "conclusion": "success", + "number": 11, + "started_at": "2026-10-27T13:14:29Z", + "completed_at": "2026-10-27T13:14:34Z" + }, + { + "name": "Full ASCII state replay", + "status": "completed", + "conclusion": "skipped", + "number": 12, + "started_at": "2026-10-27T13:14:34Z", + "completed_at": "2026-10-27T13:14:34Z" + }, + { + "name": "Full Unicode state replay", + "status": "completed", + "conclusion": "skipped", + "number": 13, + "started_at": "2026-10-27T13:14:34Z", + "completed_at": "2026-10-27T13:14:34Z" + }, + { + "name": "Preserve synthetic failure evidence", + "status": "completed", + "conclusion": "success", + "number": 14, + "started_at": "2026-10-27T13:14:34Z", + "completed_at": "2026-10-27T13:14:39Z" + }, + { + "name": "Post Run actions/setup-python@6d4fd22b20465ccc0f7354476588e8df7c9ca0e0", + "status": "completed", + "conclusion": "skipped", + "number": 27, + "started_at": "2026-10-27T13:14:39Z", + "completed_at": "2026-10-27T13:14:39Z" + }, + { + "name": "Post Run actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1", + "status": "completed", + "conclusion": "success", + "number": 28, + "started_at": "2026-10-27T13:14:39Z", + "completed_at": "2026-10-27T13:14:41Z" + }, + { + "name": "Complete job", + "status": "completed", + "conclusion": "success", + "number": 29, + "started_at": "2026-10-27T13:14:41Z", + "completed_at": "2026-10-27T13:14:41Z" + } + ], + "log_evidence": [ + "2026-10-27T13:09:34.6075054Z Ran 23 tests in 0.141s", + "2026-10-27T13:10:42.1081064Z ERROR: test_publication_keeps_failed_streams_and_separates_the_six_observation_scope (experiments.measure.test_r54_cpu_hosted.CpuHostedBoundaryTests.test_publication_keeps_failed_streams_and_separates_the_six_observation_scope)", + "2026-10-27T13:10:42.1091660Z ValueError: publication paths must stay in workspace local", + "2026-10-27T13:10:42.1092267Z Ran 40 tests in 14.741s", + "2026-10-27T13:10:42.1092447Z FAILED (errors=1)", + "2026-10-27T13:14:29.5651977Z 98% tests passed, 1 tests failed out of 46" + ] + }, + { + "id": 122206924940, + "name": "regression (ubuntu-24.04, build/bin)", + "conclusion": "success", + "steps": [ + { + "name": "Set up job", + "status": "completed", + "conclusion": "success", + "number": 1, + "started_at": "2026-10-27T13:08:07Z", + "completed_at": "2026-10-27T13:08:09Z" + }, + { + "name": "Run actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1", + "status": "completed", + "conclusion": "success", + "number": 2, + "started_at": "2026-10-27T13:08:09Z", + "completed_at": "2026-10-27T13:08:11Z" + }, + { + "name": "Run actions/setup-python@6d4fd22b20465ccc0f7354476588e8df7c9ca0e0", + "status": "completed", + "conclusion": "success", + "number": 3, + "started_at": "2026-10-27T13:08:11Z", + "completed_at": "2026-10-27T13:08:11Z" + }, + { + "name": "Install test dependencies", + "status": "completed", + "conclusion": "success", + "number": 4, + "started_at": "2026-10-27T13:08:11Z", + "completed_at": "2026-10-27T13:08:13Z" + }, + { + "name": "Configure", + "status": "completed", + "conclusion": "success", + "number": 5, + "started_at": "2026-10-27T13:08:13Z", + "completed_at": "2026-10-27T13:08:14Z" + }, + { + "name": "Build", + "status": "completed", + "conclusion": "success", + "number": 6, + "started_at": "2026-10-27T13:08:14Z", + "completed_at": "2026-10-27T13:08:27Z" + }, + { + "name": "Verify shell preparation guards and oracles", + "status": "completed", + "conclusion": "success", + "number": 7, + "started_at": "2026-10-27T13:08:27Z", + "completed_at": "2026-10-27T13:08:28Z" + }, + { + "name": "Prepare hosted Windows shell prerequisites", + "status": "completed", + "conclusion": "skipped", + "number": 8, + "started_at": "2026-10-27T13:08:28Z", + "completed_at": "2026-10-27T13:08:28Z" + }, + { + "name": "Preserve sanitized shell preparation result", + "status": "completed", + "conclusion": "skipped", + "number": 9, + "started_at": "2026-10-27T13:08:28Z", + "completed_at": "2026-10-27T13:08:28Z" + }, + { + "name": "Test", + "status": "completed", + "conclusion": "success", + "number": 10, + "started_at": "2026-10-27T13:08:28Z", + "completed_at": "2026-10-27T13:08:50Z" + }, + { + "name": "Preserve clipboard and window integration evidence", + "status": "completed", + "conclusion": "skipped", + "number": 11, + "started_at": "2026-10-27T13:08:50Z", + "completed_at": "2026-10-27T13:08:50Z" + }, + { + "name": "Full ASCII state replay", + "status": "completed", + "conclusion": "success", + "number": 12, + "started_at": "2026-10-27T13:08:50Z", + "completed_at": "2026-10-27T13:08:50Z" + }, + { + "name": "Full Unicode state replay", + "status": "completed", + "conclusion": "success", + "number": 13, + "started_at": "2026-10-27T13:08:50Z", + "completed_at": "2026-10-27T13:08:50Z" + }, + { + "name": "Preserve synthetic failure evidence", + "status": "completed", + "conclusion": "skipped", + "number": 14, + "started_at": "2026-10-27T13:08:50Z", + "completed_at": "2026-10-27T13:08:50Z" + }, + { + "name": "Post Run actions/setup-python@6d4fd22b20465ccc0f7354476588e8df7c9ca0e0", + "status": "completed", + "conclusion": "success", + "number": 27, + "started_at": "2026-10-27T13:08:50Z", + "completed_at": "2026-10-27T13:08:51Z" + }, + { + "name": "Post Run actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1", + "status": "completed", + "conclusion": "success", + "number": 28, + "started_at": "2026-10-27T13:08:51Z", + "completed_at": "2026-10-27T13:08:51Z" + }, + { + "name": "Complete job", + "status": "completed", + "conclusion": "success", + "number": 29, + "started_at": "2026-10-27T13:08:51Z", + "completed_at": "2026-10-27T13:08:51Z" + } + ], + "log_evidence": [] + } + ] + }, + "40509157544": { + "jobs": [ + { + "id": 122206159254, + "name": "alacritty-headless (windows-2025, experiments/candidates/alacritty/target/release/termforge-alacr...", + "conclusion": "failure", + "steps": [ + { + "name": "Set up job", + "status": "completed", + "conclusion": "success", + "number": 1, + "started_at": "2026-10-27T13:06:32Z", + "completed_at": "2026-10-27T13:06:33Z" + }, + { + "name": "Run actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1", + "status": "completed", + "conclusion": "success", + "number": 2, + "started_at": "2026-10-27T13:06:33Z", + "completed_at": "2026-10-27T13:06:39Z" + }, + { + "name": "Run actions/setup-python@6d4fd22b20465ccc0f7354476588e8df7c9ca0e0", + "status": "completed", + "conclusion": "success", + "number": 3, + "started_at": "2026-10-27T13:06:39Z", + "completed_at": "2026-10-27T13:06:39Z" + }, + { + "name": "Install fixed semantic references", + "status": "completed", + "conclusion": "success", + "number": 4, + "started_at": "2026-10-27T13:06:39Z", + "completed_at": "2026-10-27T13:06:45Z" + }, + { + "name": "Install fixed CPU observer", + "status": "completed", + "conclusion": "success", + "number": 5, + "started_at": "2026-10-27T13:06:45Z", + "completed_at": "2026-10-27T13:06:47Z" + }, + { + "name": "Select fixed Rust toolchain", + "status": "completed", + "conclusion": "success", + "number": 6, + "started_at": "2026-10-27T13:06:47Z", + "completed_at": "2026-10-27T13:06:57Z" + }, + { + "name": "Build locked candidate", + "status": "completed", + "conclusion": "success", + "number": 7, + "started_at": "2026-10-27T13:06:57Z", + "completed_at": "2026-10-27T13:07:28Z" + }, + { + "name": "Test Rust projection", + "status": "completed", + "conclusion": "success", + "number": 8, + "started_at": "2026-10-27T13:07:28Z", + "completed_at": "2026-10-27T13:07:41Z" + }, + { + "name": "Test fixed upstream and compatibility patches", + "status": "completed", + "conclusion": "success", + "number": 9, + "started_at": "2026-10-27T13:07:41Z", + "completed_at": "2026-10-27T13:07:53Z" + }, + { + "name": "Build candidate measurement", + "status": "completed", + "conclusion": "success", + "number": 10, + "started_at": "2026-10-27T13:07:53Z", + "completed_at": "2026-10-27T13:07:54Z" + }, + { + "name": "Build candidate measurement without allocator wrapper", + "status": "completed", + "conclusion": "success", + "number": 11, + "started_at": "2026-10-27T13:07:54Z", + "completed_at": "2026-10-27T13:08:15Z" + }, + { + "name": "Configure native trace on Windows", + "status": "completed", + "conclusion": "success", + "number": 12, + "started_at": "2026-10-27T13:08:15Z", + "completed_at": "2026-10-27T13:08:25Z" + }, + { + "name": "Configure native trace on Unix", + "status": "completed", + "conclusion": "skipped", + "number": 13, + "started_at": "2026-10-27T13:08:25Z", + "completed_at": "2026-10-27T13:08:25Z" + }, + { + "name": "Build native trace", + "status": "completed", + "conclusion": "success", + "number": 14, + "started_at": "2026-10-27T13:08:25Z", + "completed_at": "2026-10-27T13:08:29Z" + }, + { + "name": "Build native measurement", + "status": "completed", + "conclusion": "success", + "number": 15, + "started_at": "2026-10-27T13:08:29Z", + "completed_at": "2026-10-27T13:08:32Z" + }, + { + "name": "Build and test candidate C++ adapter", + "status": "completed", + "conclusion": "success", + "number": 16, + "started_at": "2026-10-27T13:08:32Z", + "completed_at": "2026-10-27T13:08:39Z" + }, + { + "name": "Build and test candidate shell on Windows", + "status": "completed", + "conclusion": "success", + "number": 17, + "started_at": "2026-10-27T13:08:39Z", + "completed_at": "2026-10-27T13:09:22Z" + }, + { + "name": "Verify empty-title GUI and UTF-8 conversion on Windows", + "status": "completed", + "conclusion": "success", + "number": 18, + "started_at": "2026-10-27T13:09:22Z", + "completed_at": "2026-10-27T13:09:43Z" + }, + { + "name": "Build real application capture on Windows", + "status": "completed", + "conclusion": "success", + "number": 19, + "started_at": "2026-10-27T13:09:43Z", + "completed_at": "2026-10-27T13:09:46Z" + }, + { + "name": "Run headless integration checks", + "status": "completed", + "conclusion": "success", + "number": 20, + "started_at": "2026-10-27T13:09:46Z", + "completed_at": "2026-10-27T13:09:46Z" + }, + { + "name": "Test runner and failure gates", + "status": "completed", + "conclusion": "success", + "number": 21, + "started_at": "2026-10-27T13:09:46Z", + "completed_at": "2026-10-27T13:09:47Z" + }, + { + "name": "Compare common native traces", + "status": "completed", + "conclusion": "success", + "number": 22, + "started_at": "2026-10-27T13:09:47Z", + "completed_at": "2026-10-27T13:09:49Z" + }, + { + "name": "Test common comparison and failure gates", + "status": "completed", + "conclusion": "success", + "number": 23, + "started_at": "2026-10-27T13:09:49Z", + "completed_at": "2026-10-27T13:09:51Z" + }, + { + "name": "Compare extended capability traces", + "status": "completed", + "conclusion": "success", + "number": 24, + "started_at": "2026-10-27T13:09:51Z", + "completed_at": "2026-10-27T13:09:52Z" + }, + { + "name": "Test extended capabilities and unsupported gates", + "status": "completed", + "conclusion": "success", + "number": 25, + "started_at": "2026-10-27T13:09:52Z", + "completed_at": "2026-10-27T13:09:52Z" + }, + { + "name": "Compare real application traces on Windows", + "status": "completed", + "conclusion": "success", + "number": 26, + "started_at": "2026-10-27T13:09:52Z", + "completed_at": "2026-10-27T13:09:53Z" + }, + { + "name": "Test real application traces on Windows", + "status": "completed", + "conclusion": "success", + "number": 27, + "started_at": "2026-10-27T13:09:53Z", + "completed_at": "2026-10-27T13:10:05Z" + }, + { + "name": "Run bounded recovery fuzz", + "status": "completed", + "conclusion": "success", + "number": 28, + "started_at": "2026-10-27T13:10:05Z", + "completed_at": "2026-10-27T13:10:05Z" + }, + { + "name": "Test fuzz minimization and fixed regressions", + "status": "completed", + "conclusion": "success", + "number": 29, + "started_at": "2026-10-27T13:10:05Z", + "completed_at": "2026-10-27T13:10:06Z" + }, + { + "name": "Compare interleaved project costs", + "status": "completed", + "conclusion": "success", + "number": 30, + "started_at": "2026-10-27T13:10:06Z", + "completed_at": "2026-10-27T13:10:36Z" + }, + { + "name": "Compare interleaved interactive costs", + "status": "completed", + "conclusion": "success", + "number": 31, + "started_at": "2026-10-27T13:10:36Z", + "completed_at": "2026-10-27T13:11:16Z" + }, + { + "name": "Test measurement contracts", + "status": "completed", + "conclusion": "failure", + "number": 32, + "started_at": "2026-10-27T13:11:16Z", + "completed_at": "2026-10-27T13:11:44Z" + }, + { + "name": "Preserve synthetic candidate evidence", + "status": "completed", + "conclusion": "success", + "number": 33, + "started_at": "2026-10-27T13:11:44Z", + "completed_at": "2026-10-27T13:11:49Z" + }, + { + "name": "Post Run actions/setup-python@6d4fd22b20465ccc0f7354476588e8df7c9ca0e0", + "status": "completed", + "conclusion": "skipped", + "number": 65, + "started_at": "2026-10-27T13:11:49Z", + "completed_at": "2026-10-27T13:11:49Z" + }, + { + "name": "Post Run actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1", + "status": "completed", + "conclusion": "success", + "number": 66, + "started_at": "2026-10-27T13:11:49Z", + "completed_at": "2026-10-27T13:11:50Z" + }, + { + "name": "Complete job", + "status": "completed", + "conclusion": "success", + "number": 67, + "started_at": "2026-10-27T13:11:50Z", + "completed_at": "2026-10-27T13:11:50Z" + } + ], + "log_evidence": [ + "2026-10-27T13:09:47.1510883Z Ran 6 tests in 0.112s", + "2026-10-27T13:09:51.1475615Z Ran 4 tests in 1.624s", + "2026-10-27T13:09:52.7038066Z Ran 4 tests in 0.401s", + "2026-10-27T13:10:04.9281496Z Ran 8 tests in 10.681s", + "2026-10-27T13:10:06.4638361Z Ran 4 tests in 0.571s", + "2026-10-27T13:11:44.7874775Z ERROR: test_publication_keeps_failed_streams_and_separates_the_six_observation_scope (test_r54_cpu_hosted.CpuHostedBoundaryTests.test_publication_keeps_failed_streams_and_separates_the_six_observation_scope)", + "2026-10-27T13:11:44.7884371Z ValueError: publication paths must stay in workspace local", + "2026-10-27T13:11:44.7884990Z Ran 49 tests in 28.223s", + "2026-10-27T13:11:44.7885202Z FAILED (errors=1)" + ] + }, + { + "id": 122206159638, + "name": "alacritty-headless (ubuntu-24.04, experiments/candidates/alacritty/target/release/termforge-alacr...", + "conclusion": "success", + "steps": [ + { + "name": "Set up job", + "status": "completed", + "conclusion": "success", + "number": 1, + "started_at": "2026-10-27T13:06:33Z", + "completed_at": "2026-10-27T13:06:34Z" + }, + { + "name": "Run actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1", + "status": "completed", + "conclusion": "success", + "number": 2, + "started_at": "2026-10-27T13:06:34Z", + "completed_at": "2026-10-27T13:06:36Z" + }, + { + "name": "Run actions/setup-python@6d4fd22b20465ccc0f7354476588e8df7c9ca0e0", + "status": "completed", + "conclusion": "success", + "number": 3, + "started_at": "2026-10-27T13:06:36Z", + "completed_at": "2026-10-27T13:06:36Z" + }, + { + "name": "Install fixed semantic references", + "status": "completed", + "conclusion": "success", + "number": 4, + "started_at": "2026-10-27T13:06:36Z", + "completed_at": "2026-10-27T13:06:37Z" + }, + { + "name": "Install fixed CPU observer", + "status": "completed", + "conclusion": "success", + "number": 5, + "started_at": "2026-10-27T13:06:37Z", + "completed_at": "2026-10-27T13:06:38Z" + }, + { + "name": "Select fixed Rust toolchain", + "status": "completed", + "conclusion": "success", + "number": 6, + "started_at": "2026-10-27T13:06:38Z", + "completed_at": "2026-10-27T13:06:46Z" + }, + { + "name": "Build locked candidate", + "status": "completed", + "conclusion": "success", + "number": 7, + "started_at": "2026-10-27T13:06:46Z", + "completed_at": "2026-10-27T13:07:08Z" + }, + { + "name": "Test Rust projection", + "status": "completed", + "conclusion": "success", + "number": 8, + "started_at": "2026-10-27T13:07:08Z", + "completed_at": "2026-10-27T13:07:18Z" + }, + { + "name": "Test fixed upstream and compatibility patches", + "status": "completed", + "conclusion": "success", + "number": 9, + "started_at": "2026-10-27T13:07:18Z", + "completed_at": "2026-10-27T13:07:30Z" + }, + { + "name": "Build candidate measurement", + "status": "completed", + "conclusion": "success", + "number": 10, + "started_at": "2026-10-27T13:07:30Z", + "completed_at": "2026-10-27T13:07:30Z" + }, + { + "name": "Build candidate measurement without allocator wrapper", + "status": "completed", + "conclusion": "success", + "number": 11, + "started_at": "2026-10-27T13:07:30Z", + "completed_at": "2026-10-27T13:07:48Z" + }, + { + "name": "Configure native trace on Windows", + "status": "completed", + "conclusion": "skipped", + "number": 12, + "started_at": "2026-10-27T13:07:48Z", + "completed_at": "2026-10-27T13:07:48Z" + }, + { + "name": "Configure native trace on Unix", + "status": "completed", + "conclusion": "success", + "number": 13, + "started_at": "2026-10-27T13:07:48Z", + "completed_at": "2026-10-27T13:07:51Z" + }, + { + "name": "Build native trace", + "status": "completed", + "conclusion": "success", + "number": 14, + "started_at": "2026-10-27T13:07:51Z", + "completed_at": "2026-10-27T13:07:54Z" + }, + { + "name": "Build native measurement", + "status": "completed", + "conclusion": "success", + "number": 15, + "started_at": "2026-10-27T13:07:54Z", + "completed_at": "2026-10-27T13:07:56Z" + }, + { + "name": "Build and test candidate C++ adapter", + "status": "completed", + "conclusion": "success", + "number": 16, + "started_at": "2026-10-27T13:07:56Z", + "completed_at": "2026-10-27T13:08:03Z" + }, + { + "name": "Build and test candidate shell on Windows", + "status": "completed", + "conclusion": "skipped", + "number": 17, + "started_at": "2026-10-27T13:08:03Z", + "completed_at": "2026-10-27T13:08:03Z" + }, + { + "name": "Verify empty-title GUI and UTF-8 conversion on Windows", + "status": "completed", + "conclusion": "skipped", + "number": 18, + "started_at": "2026-10-27T13:08:03Z", + "completed_at": "2026-10-27T13:08:03Z" + }, + { + "name": "Build real application capture on Windows", + "status": "completed", + "conclusion": "skipped", + "number": 19, + "started_at": "2026-10-27T13:08:03Z", + "completed_at": "2026-10-27T13:08:03Z" + }, + { + "name": "Run headless integration checks", + "status": "completed", + "conclusion": "success", + "number": 20, + "started_at": "2026-10-27T13:08:03Z", + "completed_at": "2026-10-27T13:08:03Z" + }, + { + "name": "Test runner and failure gates", + "status": "completed", + "conclusion": "success", + "number": 21, + "started_at": "2026-10-27T13:08:03Z", + "completed_at": "2026-10-27T13:08:04Z" + }, + { + "name": "Compare common native traces", + "status": "completed", + "conclusion": "success", + "number": 22, + "started_at": "2026-10-27T13:08:04Z", + "completed_at": "2026-10-27T13:08:04Z" + }, + { + "name": "Test common comparison and failure gates", + "status": "completed", + "conclusion": "success", + "number": 23, + "started_at": "2026-10-27T13:08:04Z", + "completed_at": "2026-10-27T13:08:05Z" + }, + { + "name": "Compare extended capability traces", + "status": "completed", + "conclusion": "success", + "number": 24, + "started_at": "2026-10-27T13:08:05Z", + "completed_at": "2026-10-27T13:08:06Z" + }, + { + "name": "Test extended capabilities and unsupported gates", + "status": "completed", + "conclusion": "success", + "number": 25, + "started_at": "2026-10-27T13:08:06Z", + "completed_at": "2026-10-27T13:08:06Z" + }, + { + "name": "Compare real application traces on Windows", + "status": "completed", + "conclusion": "skipped", + "number": 26, + "started_at": "2026-10-27T13:08:06Z", + "completed_at": "2026-10-27T13:08:06Z" + }, + { + "name": "Test real application traces on Windows", + "status": "completed", + "conclusion": "skipped", + "number": 27, + "started_at": "2026-10-27T13:08:06Z", + "completed_at": "2026-10-27T13:08:06Z" + }, + { + "name": "Run bounded recovery fuzz", + "status": "completed", + "conclusion": "success", + "number": 28, + "started_at": "2026-10-27T13:08:06Z", + "completed_at": "2026-10-27T13:08:06Z" + }, + { + "name": "Test fuzz minimization and fixed regressions", + "status": "completed", + "conclusion": "success", + "number": 29, + "started_at": "2026-10-27T13:08:06Z", + "completed_at": "2026-10-27T13:08:06Z" + }, + { + "name": "Compare interleaved project costs", + "status": "completed", + "conclusion": "success", + "number": 30, + "started_at": "2026-10-27T13:08:06Z", + "completed_at": "2026-10-27T13:08:36Z" + }, + { + "name": "Compare interleaved interactive costs", + "status": "completed", + "conclusion": "success", + "number": 31, + "started_at": "2026-10-27T13:08:36Z", + "completed_at": "2026-10-27T13:09:16Z" + }, + { + "name": "Test measurement contracts", + "status": "completed", + "conclusion": "success", + "number": 32, + "started_at": "2026-10-27T13:09:16Z", + "completed_at": "2026-10-27T13:09:42Z" + }, + { + "name": "Preserve synthetic candidate evidence", + "status": "completed", + "conclusion": "success", + "number": 33, + "started_at": "2026-10-27T13:09:42Z", + "completed_at": "2026-10-27T13:09:45Z" + }, + { + "name": "Post Run actions/setup-python@6d4fd22b20465ccc0f7354476588e8df7c9ca0e0", + "status": "completed", + "conclusion": "success", + "number": 65, + "started_at": "2026-10-27T13:09:45Z", + "completed_at": "2026-10-27T13:09:45Z" + }, + { + "name": "Post Run actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1", + "status": "completed", + "conclusion": "success", + "number": 66, + "started_at": "2026-10-27T13:09:45Z", + "completed_at": "2026-10-27T13:09:45Z" + }, + { + "name": "Complete job", + "status": "completed", + "conclusion": "success", + "number": 67, + "started_at": "2026-10-27T13:09:45Z", + "completed_at": "2026-10-27T13:09:45Z" + } + ], + "log_evidence": [] + } + ] + }, + "40509157365": { + "jobs": [ + { + "id": 122206159312, + "name": "regression (ubuntu-24.04, build/bin)", + "conclusion": "success", + "steps": [ + { + "name": "Set up job", + "status": "completed", + "conclusion": "success", + "number": 1, + "started_at": "2026-10-27T13:06:32Z", + "completed_at": "2026-10-27T13:06:33Z" + }, + { + "name": "Run actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1", + "status": "completed", + "conclusion": "success", + "number": 2, + "started_at": "2026-10-27T13:06:33Z", + "completed_at": "2026-10-27T13:06:34Z" + }, + { + "name": "Run actions/setup-python@6d4fd22b20465ccc0f7354476588e8df7c9ca0e0", + "status": "completed", + "conclusion": "success", + "number": 3, + "started_at": "2026-10-27T13:06:34Z", + "completed_at": "2026-10-27T13:06:35Z" + }, + { + "name": "Install test dependencies", + "status": "completed", + "conclusion": "success", + "number": 4, + "started_at": "2026-10-27T13:06:35Z", + "completed_at": "2026-10-27T13:06:37Z" + }, + { + "name": "Configure", + "status": "completed", + "conclusion": "success", + "number": 5, + "started_at": "2026-10-27T13:06:37Z", + "completed_at": "2026-10-27T13:06:44Z" + }, + { + "name": "Build", + "status": "completed", + "conclusion": "success", + "number": 6, + "started_at": "2026-10-27T13:06:44Z", + "completed_at": "2026-10-27T13:06:53Z" + }, + { + "name": "Verify shell preparation guards and oracles", + "status": "completed", + "conclusion": "success", + "number": 7, + "started_at": "2026-10-27T13:06:53Z", + "completed_at": "2026-10-27T13:06:54Z" + }, + { + "name": "Prepare hosted Windows shell prerequisites", + "status": "completed", + "conclusion": "skipped", + "number": 8, + "started_at": "2026-10-27T13:06:54Z", + "completed_at": "2026-10-27T13:06:54Z" + }, + { + "name": "Preserve sanitized shell preparation result", + "status": "completed", + "conclusion": "skipped", + "number": 9, + "started_at": "2026-10-27T13:06:54Z", + "completed_at": "2026-10-27T13:06:54Z" + }, + { + "name": "Test", + "status": "completed", + "conclusion": "success", + "number": 10, + "started_at": "2026-10-27T13:06:54Z", + "completed_at": "2026-10-27T13:07:10Z" + }, + { + "name": "Preserve clipboard and window integration evidence", + "status": "completed", + "conclusion": "skipped", + "number": 11, + "started_at": "2026-10-27T13:07:10Z", + "completed_at": "2026-10-27T13:07:10Z" + }, + { + "name": "Full ASCII state replay", + "status": "completed", + "conclusion": "success", + "number": 12, + "started_at": "2026-10-27T13:07:10Z", + "completed_at": "2026-10-27T13:07:10Z" + }, + { + "name": "Full Unicode state replay", + "status": "completed", + "conclusion": "success", + "number": 13, + "started_at": "2026-10-27T13:07:10Z", + "completed_at": "2026-10-27T13:07:10Z" + }, + { + "name": "Preserve synthetic failure evidence", + "status": "completed", + "conclusion": "skipped", + "number": 14, + "started_at": "2026-10-27T13:07:10Z", + "completed_at": "2026-10-27T13:07:10Z" + }, + { + "name": "Post Run actions/setup-python@6d4fd22b20465ccc0f7354476588e8df7c9ca0e0", + "status": "completed", + "conclusion": "success", + "number": 27, + "started_at": "2026-10-27T13:07:10Z", + "completed_at": "2026-10-27T13:07:11Z" + }, + { + "name": "Post Run actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1", + "status": "completed", + "conclusion": "success", + "number": 28, + "started_at": "2026-10-27T13:07:11Z", + "completed_at": "2026-10-27T13:07:11Z" + }, + { + "name": "Complete job", + "status": "completed", + "conclusion": "success", + "number": 29, + "started_at": "2026-10-27T13:07:11Z", + "completed_at": "2026-10-27T13:07:11Z" + } + ], + "log_evidence": [] + }, + { + "id": 122206159781, + "name": "regression (windows-2025, build/bin/Release)", + "conclusion": "failure", + "steps": [ + { + "name": "Set up job", + "status": "completed", + "conclusion": "success", + "number": 1, + "started_at": "2026-10-27T13:06:33Z", + "completed_at": "2026-10-27T13:06:35Z" + }, + { + "name": "Run actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1", + "status": "completed", + "conclusion": "success", + "number": 2, + "started_at": "2026-10-27T13:06:35Z", + "completed_at": "2026-10-27T13:06:42Z" + }, + { + "name": "Run actions/setup-python@6d4fd22b20465ccc0f7354476588e8df7c9ca0e0", + "status": "completed", + "conclusion": "success", + "number": 3, + "started_at": "2026-10-27T13:06:42Z", + "completed_at": "2026-10-27T13:06:42Z" + }, + { + "name": "Install test dependencies", + "status": "completed", + "conclusion": "success", + "number": 4, + "started_at": "2026-10-27T13:06:42Z", + "completed_at": "2026-10-27T13:06:53Z" + }, + { + "name": "Configure", + "status": "completed", + "conclusion": "success", + "number": 5, + "started_at": "2026-10-27T13:06:53Z", + "completed_at": "2026-10-27T13:07:10Z" + }, + { + "name": "Build", + "status": "completed", + "conclusion": "success", + "number": 6, + "started_at": "2026-10-27T13:07:10Z", + "completed_at": "2026-10-27T13:08:45Z" + }, + { + "name": "Verify shell preparation guards and oracles", + "status": "completed", + "conclusion": "success", + "number": 7, + "started_at": "2026-10-27T13:08:45Z", + "completed_at": "2026-10-27T13:08:47Z" + }, + { + "name": "Prepare hosted Windows shell prerequisites", + "status": "completed", + "conclusion": "success", + "number": 8, + "started_at": "2026-10-27T13:08:47Z", + "completed_at": "2026-10-27T13:09:33Z" + }, + { + "name": "Preserve sanitized shell preparation result", + "status": "completed", + "conclusion": "success", + "number": 9, + "started_at": "2026-10-27T13:09:33Z", + "completed_at": "2026-10-27T13:09:34Z" + }, + { + "name": "Test", + "status": "completed", + "conclusion": "failure", + "number": 10, + "started_at": "2026-10-27T13:09:34Z", + "completed_at": "2026-10-27T13:14:21Z" + }, + { + "name": "Preserve clipboard and window integration evidence", + "status": "completed", + "conclusion": "success", + "number": 11, + "started_at": "2026-10-27T13:14:21Z", + "completed_at": "2026-10-27T13:14:30Z" + }, + { + "name": "Full ASCII state replay", + "status": "completed", + "conclusion": "skipped", + "number": 12, + "started_at": "2026-10-27T13:14:30Z", + "completed_at": "2026-10-27T13:14:30Z" + }, + { + "name": "Full Unicode state replay", + "status": "completed", + "conclusion": "skipped", + "number": 13, + "started_at": "2026-10-27T13:14:30Z", + "completed_at": "2026-10-27T13:14:30Z" + }, + { + "name": "Preserve synthetic failure evidence", + "status": "completed", + "conclusion": "success", + "number": 14, + "started_at": "2026-10-27T13:14:30Z", + "completed_at": "2026-10-27T13:14:38Z" + }, + { + "name": "Post Run actions/setup-python@6d4fd22b20465ccc0f7354476588e8df7c9ca0e0", + "status": "completed", + "conclusion": "skipped", + "number": 27, + "started_at": "2026-10-27T13:14:38Z", + "completed_at": "2026-10-27T13:14:38Z" + }, + { + "name": "Post Run actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1", + "status": "completed", + "conclusion": "success", + "number": 28, + "started_at": "2026-10-27T13:14:38Z", + "completed_at": "2026-10-27T13:14:41Z" + }, + { + "name": "Complete job", + "status": "completed", + "conclusion": "success", + "number": 29, + "started_at": "2026-10-27T13:14:41Z", + "completed_at": "2026-10-27T13:14:41Z" + } + ], + "log_evidence": [ + "2026-10-27T13:08:46.9370598Z Ran 23 tests in 0.204s", + "2026-10-27T13:10:07.8942946Z ERROR: test_publication_keeps_failed_streams_and_separates_the_six_observation_scope (experiments.measure.test_r54_cpu_hosted.CpuHostedBoundaryTests.test_publication_keeps_failed_streams_and_separates_the_six_observation_scope)", + "2026-10-27T13:10:07.8959865Z ValueError: publication paths must stay in workspace local", + "2026-10-27T13:10:07.8961082Z Ran 40 tests in 16.284s", + "2026-10-27T13:10:07.8961443Z FAILED (errors=1)", + "2026-10-27T13:14:21.2079969Z 98% tests passed, 1 tests failed out of 46" + ] + } + ] + } + }, + "limits": [ + "No idle qualification, A/B cost ratio or performance acceptance.", + "Signed CPU residual includes possible guest activity, counter quantization and non-atomic endpoints; no process attribution.", + "Own outside-inner CPU does not explain all residual activity in this new run; old hosted cause remains unknown.", + "New binaries and host identity are independent of earlier local and hosted builds.", + "Full controlled batches, wrapper/projection/component attribution and user-confirmed budgets remain incomplete." + ] +} diff --git a/experiments/measure/results/2026-10-27-r54-cpu-hosted/verify.py b/experiments/measure/results/2026-10-27-r54-cpu-hosted/verify.py new file mode 100644 index 00000000..eff75a07 --- /dev/null +++ b/experiments/measure/results/2026-10-27-r54-cpu-hosted/verify.py @@ -0,0 +1,177 @@ +"""Read retained evidence only; never build or launch a measurement.""" +import hashlib +import json +from pathlib import Path +import re +import subprocess +import sys +import zipfile + +ROOT = Path(__file__).resolve().parents[4] +sys.path.insert(0, str(ROOT)) +from experiments.measure.r54_contract import SOURCES, canonical, expected_sample, protocol, validate_sample +from experiments.measure.r54_inputs import IDENTITY, OVERLAYS +from experiments.measure.run import validate_result + +SOURCE = '4772f4f937c4f0f220be1630592f84bed3a591c8' +RUN = '40512310499' +ORDER = ('baseline', 'candidate', 'candidate', 'baseline', 'baseline', 'candidate') + +def digest(data): + return hashlib.sha256(data).hexdigest() + +def source_file_matches(commit, name, sha): + data = subprocess.check_output(['git','show',commit+':'+name],cwd=ROOT) + if digest(data) == sha: + return 'git_blob_bytes' + # Windows checkout can convert text without explicit eol attributes to CRLF. + # Require the complete captured SHA to match that exact conversion. + assert b'\0' not in data and b'\r\n' not in data, name + assert digest(data.replace(b'\n', b'\r\n')) == sha, name + return 'git_blob_lf_to_crlf' + +def main(path): + assert path.stat().st_size == 123388 + assert digest(path.read_bytes()) == '263a3bf8e229b89f1e443b12e522daf69d30de403affa0dc49fe674c0dd8f241' + with zipfile.ZipFile(path) as archive: + read = lambda name: json.loads(archive.read(name)) + publication = read('publication.json') + assert publication['schema'] == 'termforge-r54-cpu-hosted-publication-v1' + inventory = {v['path']: v for v in publication['artifacts']} + assert len(inventory) == len(publication['artifacts']) == 74 + assert len(archive.namelist()) == 75 + assert set(archive.namelist()) == set(inventory) | {'publication.json'} + exact = [] + for name, row in inventory.items(): + assert not Path(name).is_absolute() and '..' not in Path(name).parts + data = archive.read(name) + assert len(data) == row['published_bytes'] and digest(data) == row['published_sha256'], name + assert not re.search(rb'[A-Za-z]:[\\/]+(?:Users|a)[\\/]', data, re.I), name + assert b'child_pid' not in data and b'process_inventory' not in data, name + if row['treatment'] == 'byte_exact_owned_synthetic_stream': + assert row['original_bytes'] == len(data) and row['original_sha256'] == digest(data), name + exact.append(name) + assert len(exact) == 24 + original = lambda name: inventory[name]['original_sha256'] + identity = publication['hosted_identity'] + assert identity['GITHUB_SHA'] == SOURCE and identity['GITHUB_RUN_ID'] == RUN + assert identity['GITHUB_RUN_ATTEMPT'] == '1' and identity['GITHUB_EVENT_NAME'] == 'push' + assert identity['GITHUB_REF'] == 'refs/heads/perf/grapheme-cpu-hosted-r54-entry3' + assert identity['GITHUB_REPOSITORY'] == 'JavaZeroo/termforge' + assert identity['RUNNER_ENVIRONMENT'] == 'github-hosted' and identity['RUNNER_OS'] == 'Windows' + hosted = read('hosted.json') + assert hosted['source'] == SOURCE and hosted['hosted_identity'] == identity + assert hosted['status'] == 'accounting_complete' and hosted['performance_gate'] == 'not_evaluated' + for key, name in (('runner_sha256','experiments/measure/r54_cpu_hosted.py'), + ('workflow_sha256','.github/workflows/r54-cpu.yml')): + assert digest(subprocess.check_output(['git','show',SOURCE+':'+name],cwd=ROOT)) == hosted[key] + runner = read('diagnostic/runner.json') + assert runner['commit'] == SOURCE and runner['worktree_status'] == '' + for name, sha in runner['sources'].items(): + assert digest(subprocess.check_output(['git','show',SOURCE+':'+name],cwd=ROOT)) == sha, name + preparation = read('bundle/preparation.json') + assert preparation['status'] == 'built_and_audited' and preparation['source'] == SOURCE + plan, builds = read('bundle/plan-v1.json'), read('bundle/build-plan.json') + assert plan['protocol'] == protocol() + assert plan['protocol_sha256'] == digest(canonical(protocol())) + assert builds['harness_source'] == SOURCE and builds['frozen_plan']['sha256'] == original('bundle/plan-v1.json') + assert builds['protocol_sha256'] == plan['protocol_sha256'] + checkout_forms = {name: source_file_matches(SOURCE, name, sha) for name, sha in plan['harness_sha256'].items()} + binding, audit = read('diagnostic/binding.json'), read('bundle/build-audit.json') + assert audit['passed'] is True and audit['formal_samples'] == 0 + assert binding['plan_sha256'] == original('bundle/plan-v1.json') + assert binding['harness_source'] == SOURCE and binding['protocol_sha256'] == plan['protocol_sha256'] + for name, sha in binding['evidence'].items(): + assert original('bundle/'+name) == sha, name + for variant, source in SOURCES.items(): + spec, observed = builds['builds'][variant], audit['builds'][variant] + receipt = read('bundle/'+variant+'-build.json') + assert spec['commit'] == observed['source'] == source['commit'] + assert spec['version'] == observed['version'] == source['version'] + assert len(spec['before_files']) == len(spec['after_files']) == (80 if variant == 'baseline' else 84) + assert set(spec['before_files']) == set(spec['after_files']) + assert set(spec['changed_paths']) == set(spec['overlay_files']) == set(OVERLAYS) + for name, before in spec['before_files'].items(): + checkout_forms[variant+'/'+name] = source_file_matches(source['commit'], name, before) + expected = plan['harness_sha256'][name] if name in OVERLAYS else before + assert spec['after_files'][name] == expected, name + assert spec['overlay_patch_sha256'] == original('bundle/'+variant+'-harness.patch') + assert receipt['status'] == 'built_identity_only' and receipt['exit_code'] == 0 and not receipt['timed_out'] + assert receipt['build_plan_sha256'] == original('bundle/build-plan.json') + assert observed['receipt_sha256'] == original('bundle/'+variant+'-build.json') + assert receipt['binary'] == observed['binary'] == binding['builds'][variant]['binary'] + assert receipt['identity'] == observed['identity'] == read('bundle/'+variant+'-identity.stdout') == IDENTITY + assert read('diagnostic/'+variant+'-identity/stdout.bin') == IDENTITY + assert read('diagnostic/'+variant+'-identity/record.json')['exit_code'] == 0 + assert receipt['command'] == spec['command'] + for flag in ('--release','--locked','--offline','--no-default-features','--jobs','1','x86_64-pc-windows-msvc'): + assert flag in spec['command'] + for name, log in receipt['logs'].items(): + assert log['sha256'] == original('bundle/'+name) + assert log['bytes'] == inventory['bundle/'+name]['original_bytes'] + assert audit['builds']['baseline']['linker'] == audit['builds']['candidate']['linker'] + assert len(audit['short_sanity_calls']) == 4 + for call in audit['short_sanity_calls']: + assert call['passed'] and call['exit_code'] == 0 and not call['timed_out'] + prefix = 'bundle/sanity-'+call['variant']+'-'+call['route']+'/' + for name, stream in call['streams'].items(): + data = archive.read(prefix+name) + assert digest(data) == stream['sha256'] and len(data) == stream['bytes'] + validate_result(read(prefix+'stdout.bin'), .1, expected_sample(dict(variant=call['variant'], route=call['route'], chunk=4096))) + manifest = read('diagnostic/manifest.json') + observations = read('diagnostic/observations/manifest.json') + assert hosted['diagnostic'] == manifest + assert manifest['status'] == observations['status'] == 'accounting_complete' + assert manifest['observations'] == observations + assert observations['attempted'] == observations['accounted_observations'] == 6 + assert observations['performance_gate'] == 'not_evaluated' + for key, name in (('context_sha256','context.json'), ('protocol_sha256','protocol.json')): + assert observations[key] == original('diagnostic/observations/'+name) + rows = [] + for index, variant in enumerate(ORDER): + prefix = f'diagnostic/observations/{index:03d}/' + row = read(prefix+'record.json') + assert row['item'] == dict(sequence=index, variant=variant, route='interactive', chunk=1) + assert row['status'] == 'accounted' and row['exit_code'] == 0 and not row['timed_out'] + for name, stream in row['streams'].items(): + data = archive.read(prefix+name) + assert digest(data) == stream['sha256'] and len(data) == stream['bytes'] + raw = read(prefix+'stdout.bin') + assert raw == row['result'] + validate_sample(raw,row['item']) + env, child, accounting = row['environment'], row['child_cpu'], row['accounting'] + before, after = env['before'], env['after'] + busy = after['user']+after['system']-before['user']-before['system'] + total = busy+after['idle']-before['idle'] + assert abs(busy-env['busy_seconds']) < 1e-8 and abs(total-env['total_seconds']) < 1e-8 + assert child['total_seconds'] == child['kernel_seconds']+child['user_seconds'] + assert child['exit_filetime'] > child['creation_filetime'] + inner, runner_cpu = raw['interval_cpu_seconds'], row['runner_cpu_after_seconds']-row['runner_cpu_before_seconds'] + assert runner_cpu == row['runner_cpu_seconds'] == accounting['runner_cpu_seconds'] + assert child['total_seconds'] >= inner + assert accounting['child_outside_inner_seconds'] == child['total_seconds']-inner + assert accounting['inner_only_balance_seconds'] == busy-inner + assert abs(accounting['inner_only_balance_percent']-(busy-inner)*100/total) < 1e-8 + assert accounting['unassigned_balance_seconds'] == busy-child['total_seconds']-runner_cpu + assert abs(accounting['unassigned_balance_percent']-accounting['unassigned_balance_seconds']*100/total) < 1e-8 + rows.append(dict(item=row['item'],inner_cpu_seconds=inner,child_cpu=child,runner_cpu_seconds=runner_cpu, + environment=env,accounting=accounting,state_fnv1a64=raw['state_fnv1a64'])) + result = dict(schema='termforge-r54-cpu-hosted-validation-v1',source=SOURCE,hosted_identity=identity, + host=read('diagnostic/environment.json'),builds=audit['builds'],observations=rows, + archive=dict(bytes=path.stat().st_size,sha256=digest(path.read_bytes()),files=75,inventory_files=74,byte_exact_streams=24), + source_checkout_forms=checkout_forms,sanity_oracles=4,observation_oracles=6,formal_cost_samples=0, + performance_gate='not_evaluated',old_hosted_event_cause='unknown') + retained = json.loads(path.with_name('validation.json').read_text(encoding='utf-8')) + for name, value in result.items(): + assert retained[name] == value, name + provider = retained['provider'] + assert provider['run'] == int(RUN) and provider['attempt'] == 1 and provider['source'] == SOURCE + assert provider['job'] == 122217071656 and provider['conclusion'] == 'success' + assert provider['artifact_digest'] == 'sha256:'+digest(path.read_bytes()) + assert sum(value == 'git_blob_lf_to_crlf' for value in checkout_forms.values()) == 115 + assert len(checkout_forms) == 168 + print(json.dumps(dict(files=75,hashed_evidence=74,byte_exact_streams=24,sanity_oracles=4,observation_oracles=6, + formal_cost_samples=0,performance_gate='not_evaluated'))) + +if __name__ == '__main__': + main(Path(__file__).with_name('evidence.zip'))