Experiment formal simulator exposure registration red plan 01 - kmosoti/FabricO11y GitHub Wiki
Separate simulator witness: source registration red pre-flight 01
Prospective NOT RUN. Clean trust-only dc4ceaea632a53d1c43f22bc438e1c2ff7747fb7 on new milestone/simulator-exposure-witness is based on exact frozen candidate 8fb6f413179b094288316e25845c137440ba23fa. Only two added files (243-line controls and 188-line prospective protocol); production zero. All Rust/Cargo/config/vendor compilation inputs and canonical three-helper/source authority remain unchanged. C-081 admits a separate helper source/witness design, not Cargo, native, smoke or FULL. Frozen A/B checkouts are untouched.
One serial source-only default-capped cell with fresh full namespace DATA census, full 1 GiB reserve, admission including reserve below 95,000,000,000 bytes / 100GB ceiling; 16GiB high /20GiB max/no swap/1800 seconds. Pure hand-written receipt/two-second/two-identity controls; no simulator process, file publication, timer, source helper execution or compiler.
Expected16methods: eight literal/static positives, exactly the named SimulatorMechanismAvailability.test_separate_simulator_admission_mechanism_is_available failure because simulator_exposure.py is absent, and seven explicit dependent mechanism skips. This is missing admission mechanism, not native Rust timing failure. Any import/setup failure or unexpected positive failure stops correction and is reported. Preserve actual receipt/log/failure evidence before any separately committed production checker.
The original source/rate/delivery/query oracles remain independent and unchanged. Literal publication flags and hand-written compilation maps are prospective integration obligations, not actual native/file-system or complete source-census authority. The future witness's 1ms bracket remains unmeasured; future implementation, complete compilation-input census, cold-build reserve, native control commands and FULL require new exact pre-flights. No old 4GiB build estimate, native/helper substitution or canonical manifest relaxation is admitted.
Exact command
python3 -B /run/media/kmosoti/data/FabricO11y/results/candidate-build-fresh-8fb6f413179b-01/run-step-simulator-registration-red-01.py simulator-exposure-registration-red-01 1073741824 env PYTHONPATH=tools/qualification:tools python3 -B -m unittest -v test_simulator_exposure
Pins
/run/media/kmosoti/data/FabricO11y/results/candidate-build-fresh-8fb6f413179b-01/run-step-simulator-registration-red-01.pySHA-2563851aa358dd5d60bf58b9fe6d692baae9b8516c3b4b4a4bf62b8d8ec4136600b, 7041 bytes./run/media/kmosoti/data/FabricO11y/scratch/simulator-exposure-witness/tools/qualification/test_simulator_exposure.pySHA-2561bb53d2bb730c5cb70c456d4c76bdcb95498d8ce1a86075d5b3516916cff1a87, 14295 bytes./run/media/kmosoti/data/FabricO11y/scratch/simulator-exposure-witness/docs/experiments/benchmarks/simulator-exposure-witness-protocol.mdSHA-256394a42b632c1eab5dd04611fcc71d054c154f7828b7efede5865464461a99e0c, 11626 bytes./run/media/kmosoti/data/FabricO11y/scratch/simulator-exposure-witness/tools/resource_group.pySHA-256454acd27fdd5a87a35c697d6a89d2fc807c5c2757a98676dba2745efece7667e, 10723 bytes./run/media/kmosoti/data/FabricO11y/scratch/simulator-exposure-witness/examples/spindle_sim.rsSHA-2562b2828fed73cfe654fadc6739e31c51b9004a42a5174f51be2cb08b161385264, 22837 bytes./run/media/kmosoti/data/FabricO11y/scratch/simulator-exposure-witness/tools/qualification/cross_family/inputs.pySHA-2563dfd336bd132fa7d904aea783c81db94f6832bea304474d86250cb2329dbec44, 3568 bytes.
Complete trust-only delta
commit dc4ceaea632a53d1c43f22bc438e1c2ff7747fb7
Author: Kennedy Mosoti <[email protected]>
AuthorDate: Tue Oct 13 00:14:55 2026 -0500
Commit: Kennedy Mosoti <[email protected]>
CommitDate: Tue Oct 13 00:14:55 2026 -0500
Register separate simulator begin and lifetime controls
Trust-boundary update: C-081 admits a separately frozen R3 qualification simulator because the unchanged example has no live begin witness and can finish after its final due offer, before the duration boundary. Register independent literal receipt, conservative integer timing, exclusive publication, loaded-process, worker coverage and complete compilation-input controls before checker or native-source changes.
Keep the canonical package/helper authority and all Rust compilation inputs unchanged in this commit. The optional native witness design preserves existing callers and requires separate native clock, atomic publication and loaded identity evidence. Initial mechanism absence is not a Rust runtime defect; no compile, native trial or full soak is claimed or admitted.
diff --git a/docs/experiments/benchmarks/simulator-exposure-witness-protocol.md b/docs/experiments/benchmarks/simulator-exposure-witness-protocol.md
new file mode 100644
index 0000000..5b4d8c2
--- /dev/null
+++ b/docs/experiments/benchmarks/simulator-exposure-witness-protocol.md
@@ -0,0 +1,188 @@
+# Registered separate simulator exposure witness
+
+Prospective registration, 2026-10-13. Coordination
+[C-081](https://github.com/kmosoti/FabricO11y/issues/59#issuecomment-6138310799)
+admits the separately frozen qualification helper and its controls, **not a
+build, native trial, smoke or full soak**. The source/transition proposal is
+[wiki revision 875f7f2](https://github.com/kmosoti/FabricO11y/wiki/Experiment-formal-soak-simulator-source-transition-amendment-proposal-01),
+SHA-256 `c36e7068c2d46663d755598d2222b09b8e8068a865d1387f403b8e5843b6bfc4`.
+This file registers helper identity and lifetime only;
+the separately registered R3 phase amendment belongs to the soak harness.
+No result is recorded here.
+
+## Source question and fixed scope
+
+Frozen candidate `8fb6f413179b094288316e25845c137440ba23fa` parses no live begin
+or hold-through-boundary option in
+[`examples/spindle_sim.rs`](../../../examples/spindle_sim.rs). Its worker exits
+once the final due offer has been acknowledged; its begin timestamp appears in
+the summary only after joins. These are source observations, not measurements
+of a two-hour run. Keeping external probes alive after simulator exit does not
+satisfy native lifetime or supply a native pre-offer witness.
+The unchanged Rust source SHA-256 at registration is
+`2b2828fed73cfe654fadc6739e31c51b9004a42a5174f51be2cb08b161385264`.
+
+Only `examples/spindle_sim.rs` may change **Rust compilation inputs**, from the
+exact candidate base. Preserve workers, identities, seeds, due-offer scheduling,
+rates, bodies, burst behavior, payload/wire bytes, retries, ACK interpretation
+and existing drain/failure semantics. No dependency, core or product change is
+admitted. The optional `--begin-witness PATH` mode preserves old callers when
+absent; it must not silently turn on for another workload. Full R3 uses 7,200
+seconds, 100 identities/workers and `0xA11FA001`; short native controls need a
+separately frozen command and do not establish full exposure.
+
+## Two independent source authorities
+
+Canonical packages, package-source tuple, three-helper manifest and helper
+bytes remain unchanged. The original
+[`cross_family/inputs.py`](../../../tools/qualification/cross_family/inputs.py)
+must still require that exact package source and all three original helper
+hashes. `server_dump` and `spool_dump` remain the graders; a new simulator must
+not replace canonical `spindle_sim` or rewrite its manifest.
+
+The additional owner-admitted helper record binds candidate base, clean helper
+commit/tree, complete source manifest/bundle, changed helper-source hash,
+binary hash and actual build receipt hash. Compare the **complete compilation
+input path sets and bytes** against canonical source: only the helper path
+may differ, and it must actually differ. Cargo manifests/lock/config, all other
+Rust/protobuf/build inputs and complete vendored/transitive sources remain
+equal. Added/deleted inputs, symlinks, uncensused generated input or an altered
+build configuration block admission. Documentation and prospective Python
+controls do not excuse a Rust compilation difference. `prepare_soak.source_paths`
+is a source-census precedent, not proof that an incomplete hand-written list
+is complete. Freeze dependency/toolchain/configuration and relevant environment
+inputs too; refuse a dirty or unbound build source.
+
+The build record binds actual command, Rust/toolchain/target/profile/features/
+flags, source tuple, output hash, exits, cgroup/resource observations and
+confirmed descendant stop. Loaded helper identity must agree with that record
+and the independently admitted expected tuple. Hashes in an untrusted witness
+or mutually agreeing receipts are not independent source authority. Canonical
+admission runs first; the opt-in helper admission is additional, never a default
+exception to it.
+
+## Exclusive native begin and conservative lifetime
+
+The proposed native path opens/hashes the kernel's loaded `/proc/self/exe`,
+records held-descriptor device/inode plus PID, process start ticks and boot ID,
+and checks identity while reading. Independent controller observations bind
+the actual executable and original process identity, not just its pathname.
+Linux documents the executable reference in
+[`proc_pid_exe(5)`](https://man7.org/linux/man-pages/man5/proc_pid_exe.5.html).
+Expected binary/source/build hashes come from the separate admitted record;
+the native program does not invent its source commit from an argument.
+
+Bracket the existing `Instant`/Unix begin with actual `CLOCK_MONOTONIC` reads
+from the same process. Preserve the original `began_unix_ns` used by events and
+summary. Store integer `mono_before_ns`, `mono_after_ns` and the Unix anchor;
+refuse reversed/negative brackets or width over **1,000,000 ns**. Use the upper
+monotonic bracket endpoint for a conservative completion boundary. The bound
+is prospective; no host observation yet establishes that it is attainable.
+No wall-clock offset or host clock change is allowed. The existing pinned libc
+dependency is available; no new dependency is needed. Linux's
+[`clock_gettime(3)`](https://man7.org/linux/man-pages/man3/clock_gettime.3.html)
+defines the distinct realtime/monotonic domains. A numeric timestamp alone
+does not establish that the proper clock was called.
+
+Write a complete private record to an exclusively created owned staging file,
+sync it, then publish to the fresh final witness path without overwrite. A
+same-filesystem hard-link publication is the proposed no-replace operation;
+[`link(2)`](https://man7.org/linux/man-pages/man2/link.2.html) refuses an existing
+destination. Confirm canonical owned parent paths, no links/mounts, regular
+single-link final file, mode 0400 and unchanged held-descriptor bytes; remove
+only the staging name this invocation actually created after publication.
+Retain partial/uncertain evidence. Publication must complete before worker
+offers; failure, an existing destination or a changed witness must produce no
+successful trial. Native/file-operation controls must establish exclusivity,
+atomic full-record visibility and pre-offer ordering; self-asserted booleans in
+the witness cannot establish them.
+
+In witness mode, successful completion must wait until both the original
+`Instant` elapsed interval reaches the requested duration and a native
+monotonic end read reaches `mono_after_ns + duration_ns`. This conservatively
+covers anchor uncertainty; it neither shortens exposure nor invents an exact
+end. Record original elapsed nanoseconds, actual monotonic end, begin hash,
+parameters and process identity in the end summary. Full-drain failures and
+remaining undelivered work still refuse success. Native wait/transition time
+remains inside the separately bounded exposure transition allowance; no phase
+or outer deadline grows. Old callers keep their original behavior.
+
+## Independent literal controls and admission mechanism
+
+[`test_simulator_exposure.py`](../../../tools/qualification/test_simulator_exposure.py)
+contains hand-written two-second/two-identity receipt records and a deliberately
+small compilation map. Those records are not real receipts or a complete
+native-source census. They exercise two prospective pure Python interfaces:
+`validate_source_build(source, build, canonical, expected)` and
+`validate_native(begin, summary, offers, loaded, expected)`. These read no clock
+and authenticate no owner. The test-only reference encodes the registration;
+it is not a fallback implementation or an oracle derived from Rust.
+
+`source/build/canonical` consume caller-read manifests and actual build file
+hashes; `expected` is separately pinned. `begin` wraps caller-read witness bytes
+as `{'sha256': actual_hash, 'value': parsed}`. `summary` is the original parsed
+native summary, and `offers` includes original created-event evidence. `loaded`
+joins independent process/binary/source/build observations and stop/exit plus
+publication-control observations. Its flags are an **integration obligation**,
+not evidence when fabricated by the helper. Parameter fields and identity names
+are fixed by the literal records. Before grading, independently bind every raw
+file hash and the exact observer/unit/cgroup/command/stop receipt.
+
+The literal controls require:
+
+- exact conservative boundary success and refusal one nanosecond early in
+ original elapsed time or the monotonic end;
+- absent, changed, nonexclusive, non-atomic, late or nonregular witness refusal;
+- reversed, oversized, negative or incorrectly typed clock brackets;
+- summary/witness begin/parameter/process disagreement, changed loaded
+ executable device/inode/hash, process start/boot, helper source or build hash;
+- every identity's original created sequence for every scheduled second,
+ without a missing worker/identity, duplicate, extra sequence or early stamp;
+- wrong source/bundle/build/toolchain/stop, any canonical helper substitution
+ and every added/deleted/changed non-helper compilation input.
+
+Created-event coverage supplements the unchanged source/rate/delivery oracles;
+it does not establish body correctness or actual thread count on its own. The
+canonical-source control pins the original guard, which must remain unchanged
+after native implementation too. The changed helper is admitted by a separate
+source record while its original bytes remain pinned as the canonical input.
+
+Initial source execution is expected to report one named missing
+`simulator_exposure.py` availability failure, eight literal/static positives, and seven explicit
+mechanism-dependent skips. That red is **mechanism absence**, not a demonstrated
+Rust timing or product defect. After root records it, a separate production
+correction may supply the bounded checker; unchanged controls must then run.
+No literal record, source grep, shortened timer or Python check proves that the
+compiled simulator held its native lifetime.
+
+## Native prerequisites and execution limits
+
+Before any compile, freeze a cold-build command and complete reservation from
+actual cold-build footprint data, including tools/copies/output/margin. The
+obsolete 4 GiB estimate is forbidden. Use the default enforced resource
+launcher and fresh namespace census below 95 GB/100 GB; build once unless a
+real mismatch requires another construction. No compile is admitted here.
+
+After a separate pre-flight, required evidence includes the named example
+tests, fast/extended checks of the exact helper compilation inputs, real short
+native begin/lifetime/existing-destination/changed-witness controls, loaded
+executable identity and host monotonic/realtime observation controls. Exercise
+unchanged canonical helper refusal and additional admission with real frozen
+receipt files. Bind every command, raw bytes, failure, resource/stop/cleanup and
+observer hash. Preserve original failures. Source controls and new helper CI
+cannot be reused as shipped-product migration evidence.
+
+Root alone may run the source-only registration cell after exact pre-flight,
+default 1800 seconds and full 1 GiB reservation; **not run at registration**:
+
+```sh
+python3 -B tools/resource_group.py -- env PYTHONPATH=tools/qualification:tools \
+ python3 -B -m unittest -v test_simulator_exposure
+```
+
+No native process or Cargo command is part of this cell. Full R3 still needs
+fresh #41/#42/#45/#43/#46, complete runtime integration, admitted full-rate smoke,
+real S1 setup/transition and other phase maxima with 25% margin, stopped
+immutable checkpoint and separate grading. If measured values do not fit the
+owner's 7,500-second deadline, request a concrete larger deadline with the
+numbers; never shave the 7,200-second offered interval.
diff --git a/tools/qualification/test_simulator_exposure.py b/tools/qualification/test_simulator_exposure.py
new file mode 100644
index 0000000..3ef937b
--- /dev/null
+++ b/tools/qualification/test_simulator_exposure.py
@@ -0,0 +1,243 @@
+"""C-081 prospective literals: receipt semantics, not native timing evidence.
+
+The small hand-written compilation map and two-second/two-identity records
+are synthetic controls. No Rust code, timer, subprocess or native helper runs.
+"""
+import copy
+import hashlib
+import importlib.util
+from pathlib import Path
+import types
+import unittest
+
+ROOT = Path(__file__).resolve().parents[2]
+MECHANISM = Path(__file__).with_name('simulator_exposure.py')
+BASE = '8fb6f413179b094288316e25845c137440ba23fa'
+HELPER = 'examples/spindle_sim.rs'
+NS = 1_000_000_000
+
+
+def source_records():
+ original = {HELPER: '2b2828fed73cfe654fadc6739e31c51b9004a42a5174f51be2cb08b161385264',
+ 'Cargo.toml': '2' * 64, 'Cargo.lock': '3' * 64,
+ '.cargo/config.toml': '4' * 64, 'src/spindle/runtime.rs': '5' * 64,
+ 'vendor/pinned/src/lib.rs': '6' * 64}
+ canonical = {'candidate': BASE, 'source_commit': '1' * 40,
+ 'source_manifest_sha256': '7' * 64, 'source_bundle_sha256': '8' * 64,
+ 'helper_manifest_sha256': '9' * 64,
+ 'helpers': {'spool_dump': 'a' * 64, 'server_dump': 'b' * 64, 'spindle_sim': 'c' * 64}}
+ source = {'base_commit': BASE, 'commit': '2' * 40, 'tree': '3' * 40,
+ 'manifest_sha256': 'd' * 64, 'bundle_sha256': 'e' * 64,
+ 'compilation_inputs': original | {HELPER: 'f' * 64}}
+ build = {'sha256': 'a' * 64, 'value': {'state': 'built', 'exit': 0,
+ 'stop_confirmed': True, 'source': copy.deepcopy(source), 'binary_sha256': 'b' * 64,
+ 'command': ['cargo', 'build', '--offline', '--locked', '--release', '--example', 'spindle_sim'],
+ 'configuration': {'rustc': '1.99.0', 'target': 'x86_64-unknown-linux-gnu',
+ 'profile': 'release', 'features': [], 'rustflags': []}}}
+ expected = {'canonical': copy.deepcopy(canonical), 'canonical_inputs': original,
+ 'source': copy.deepcopy(source), 'build': copy.deepcopy(build)}
+ return source, build, canonical, expected
+
+
+def native_records():
+ process = {'pid': 55, 'start_ticks': 123, 'boot_id': 'literal-boot',
+ 'exe_device': 7, 'exe_inode': 9}
+ parameters = {'seconds': 2, 'identities': 2, 'workers': 2, 'seed': 0xA11FA001}
+ identity = {'process': process, 'binary_sha256': 'b' * 64,
+ 'source_commit': '2' * 40, 'build_receipt_sha256': 'a' * 64}
+ expected = {'identity': copy.deepcopy(identity), 'parameters': parameters,
+ 'begin_sha256': 'c' * 64, 'max_clock_bracket_ns': 1_000_000}
+ begin = {'sha256': 'c' * 64, 'value': {'process': copy.deepcopy(process),
+ 'binary_sha256': 'b' * 64, 'parameters': copy.deepcopy(parameters),
+ 'mono_before_ns': 10 * NS, 'mono_after_ns': 10 * NS + 100,
+ 'began_unix_ns': 1_791_840_000_000_000_000}}
+ summary = {'seconds': 2, 'identities': 2, 'workers': 2, 'seed': 0xA11FA001,
+ 'began_unix_ns': begin['value']['began_unix_ns'], 'begin_witness_sha256': 'c' * 64,
+ 'process': copy.deepcopy(process), 'elapsed_ns': 2 * NS,
+ 'ended_monotonic_ns': 12 * NS + 100, 'undelivered': 0}
+ loaded = identity | {'exit': 0, 'stop_confirmed': True,
+ 'publication': {'atomic': True, 'exclusive': True, 'before_first_offer': True,
+ 'regular': True, 'nlink': 1, 'mode': 0o400, 'unchanged_sha256': 'c' * 64}}
+ offers = [{'e': 'created', 'id': identity, 'seq': second + 1,
+ 't': begin['value']['began_unix_ns'] + second * NS,
+ 'generated_ns': begin['value']['began_unix_ns'] + second * NS}
+ for identity in (0, 1) for second in (0, 1)]
+ return begin, summary, offers, loaded, expected
+
+
+def reference_source_build(source, build, canonical, expected):
+ if canonical != expected['canonical']:
+ raise ValueError('canonical helper authority')
+ actual, original = source['compilation_inputs'], expected['canonical_inputs']
+ if (set(actual) != set(original) or actual[HELPER] == original[HELPER]
+ or any(actual[name] != original[name] for name in original if name != HELPER)):
+ raise ValueError('compilation inputs')
+ if (source != expected['source'] or source['base_commit'] != BASE
+ or build != expected['build'] or build['value']['source'] != source
+ or build['value']['state'] != 'built' or type(build['value']['exit']) is not int
+ or build['value']['exit'] != 0 or build['value']['stop_confirmed'] is not True):
+ raise ValueError('separate simulator source/build')
+ return True
+
+
+def reference_native(begin, summary, offers, loaded, expected):
+ publication = loaded['publication']
+ if (not isinstance(begin, dict) or begin.get('sha256') != expected['begin_sha256']
+ or publication.get('unchanged_sha256') != expected['begin_sha256']
+ or any(publication.get(key) is not True for key in
+ ('atomic', 'exclusive', 'before_first_offer', 'regular'))
+ or publication.get('nlink') != 1 or publication.get('mode') != 0o400):
+ raise ValueError('begin witness')
+ b, identity, parameters = begin['value'], expected['identity'], expected['parameters']
+ lo, hi = b['mono_before_ns'], b['mono_after_ns']
+ if (type(lo) is not int or type(hi) is not int
+ or not 0 <= lo <= hi <= lo + expected['max_clock_bracket_ns']
+ or type(b['began_unix_ns']) is not int or b['began_unix_ns'] < 0):
+ raise ValueError('clock brackets')
+ if (any(loaded.get(key) != value for key, value in identity.items())
+ or b['process'] != identity['process'] or b['binary_sha256'] != identity['binary_sha256']
+ or b['parameters'] != parameters or summary['process'] != identity['process']
+ or any(summary.get(key) != value for key, value in parameters.items())
+ or summary['began_unix_ns'] != b['began_unix_ns']
+ or summary['begin_witness_sha256'] != begin['sha256']):
+ raise ValueError('summary/identity binding')
+ duration = parameters['seconds'] * NS
+ if (type(summary['elapsed_ns']) is not int or summary['elapsed_ns'] < duration
+ or type(summary['ended_monotonic_ns']) is not int
+ or summary['ended_monotonic_ns'] < hi + duration
+ or type(loaded['exit']) is not int or loaded['exit'] != 0
+ or loaded['stop_confirmed'] is not True or summary['undelivered'] != 0):
+ raise ValueError('native lifetime')
+ created = [item for item in offers if item.get('e') == 'created']
+ wanted = {(identity, second + 1) for identity in range(parameters['identities'])
+ for second in range(parameters['seconds'])}
+ if (len(created) != len(wanted) or {(item['id'], item['seq']) for item in created} != wanted
+ or any(type(item[key]) is not int for item in created for key in ('id', 'seq', 't', 'generated_ns'))
+ or any(not b['began_unix_ns'] <= item['t'] <= item['generated_ns'] for item in created)):
+ raise ValueError('worker offers')
+ return True
+
+
+REFERENCE = types.SimpleNamespace(validate_source_build=reference_source_build,
+ validate_native=reference_native)
+
+
+class LiteralSimulatorControls(unittest.TestCase):
+ subject = REFERENCE
+
+ def test_exact_native_boundary_and_one_ns_early_refusal(self):
+ records = native_records()
+ self.assertIs(self.subject.validate_native(*records), True)
+ for key in ('elapsed_ns', 'ended_monotonic_ns'):
+ bad = copy.deepcopy(records); bad[1][key] -= 1
+ with self.subTest(boundary=key), self.assertRaisesRegex(ValueError, 'native lifetime'):
+ self.subject.validate_native(*bad)
+
+ def test_missing_changed_and_nonexclusive_begin_refusals(self):
+ records = native_records()
+ variants = [(None, records[3]), (records[0] | {'sha256': 'd' * 64}, records[3])]
+ for key, value in (('exclusive', False), ('atomic', False), ('before_first_offer', False),
+ ('regular', False), ('nlink', 2), ('mode', 0o600), ('unchanged_sha256', 'd' * 64)):
+ loaded = copy.deepcopy(records[3]); loaded['publication'][key] = value
+ variants.append((records[0], loaded))
+ for index, (begin, loaded) in enumerate(variants):
+ with self.subTest(witness=index), self.assertRaisesRegex(ValueError, 'begin witness'):
+ self.subject.validate_native(begin, records[1], records[2], loaded, records[4])
+
+ def test_reversed_uncertain_and_invalid_clock_refusals(self):
+ records = native_records(); b = records[0]['value']
+ for change in ({'mono_after_ns': b['mono_before_ns'] - 1},
+ {'mono_after_ns': b['mono_before_ns'] + 1_000_001},
+ {'mono_before_ns': -1}, {'mono_after_ns': True}, {'began_unix_ns': -1}):
+ bad = copy.deepcopy(records); bad[0]['value'].update(change)
+ with self.subTest(clock=change), self.assertRaisesRegex(ValueError, 'clock brackets'):
+ self.subject.validate_native(*bad)
+
+ def test_summary_witness_loaded_binary_source_and_build_refusals(self):
+ records = native_records()
+ changes = ((0, ('value', 'process', 'pid'), 56),
+ (0, ('value', 'process', 'exe_inode'), 10), (0, ('value', 'binary_sha256'), 'd' * 64),
+ (1, ('seconds',), 1), (1, ('workers',), 1), (1, ('seed',), 17),
+ (1, ('began_unix_ns',), records[1]['began_unix_ns'] + 1),
+ (1, ('begin_witness_sha256',), 'd' * 64), (1, ('process', 'start_ticks'), 124),
+ (3, ('process', 'boot_id'), 'other-boot'), (3, ('process', 'exe_device'), 8),
+ (3, ('binary_sha256',), 'd' * 64), (3, ('source_commit',), '4' * 40),
+ (3, ('build_receipt_sha256',), 'd' * 64))
+ for index, path, value in changes:
+ bad = copy.deepcopy(records); target = bad[index]
+ for key in path[:-1]: target = target[key]
+ target[path[-1]] = value
+ with self.subTest(identity=path), self.assertRaisesRegex(ValueError, 'summary/identity binding'):
+ self.subject.validate_native(*bad)
+
+ def test_missing_worker_duplicate_and_final_second_offer_refusals(self):
+ records = native_records(); offers = records[2]
+ variants = (offers[:-1], offers[:2], offers + [offers[0]],
+ [item | {'seq': 3} if index == 3 else item for index, item in enumerate(offers)],
+ [offers[0] | {'t': records[1]['began_unix_ns'] - 1}] + offers[1:])
+ for index, observed in enumerate(variants):
+ with self.subTest(offers=index), self.assertRaisesRegex(ValueError, 'worker offers'):
+ self.subject.validate_native(records[0], records[1], observed, records[3], records[4])
+ for change in ({'exit': 1}, {'stop_confirmed': False}):
+ with self.assertRaisesRegex(ValueError, 'native lifetime'):
+ self.subject.validate_native(*records[:3], records[3] | change, records[4])
+
+ def test_separate_source_build_and_unchanged_canonical_authority(self):
+ records = source_records()
+ self.assertIs(self.subject.validate_source_build(*records), True)
+ for name in ('spool_dump', 'server_dump', 'spindle_sim'):
+ bad = copy.deepcopy(records); bad[2]['helpers'][name] = 'f' * 64
+ with self.subTest(canonical=name), self.assertRaisesRegex(ValueError, 'canonical helper authority'):
+ self.subject.validate_source_build(*bad)
+ for index, path, value in ((0, ('commit',), '4' * 40), (0, ('base_commit',), '5' * 40),
+ (0, ('manifest_sha256',), 'f' * 64), (0, ('bundle_sha256',), 'f' * 64),
+ (1, ('sha256',), 'f' * 64), (1, ('value', 'binary_sha256'), 'f' * 64),
+ (1, ('value', 'exit'), 1), (1, ('value', 'stop_confirmed'), False),
+ (1, ('value', 'configuration', 'rustc'), 'other-toolchain')):
+ bad = copy.deepcopy(records); target = bad[index]
+ for key in path[:-1]: target = target[key]
+ target[path[-1]] = value
+ with self.subTest(source=path), self.assertRaisesRegex(ValueError, 'separate simulator source/build'):
+ self.subject.validate_source_build(*bad)
+
+ def test_only_one_changed_rust_compilation_input_is_admitted(self):
+ records = source_records()
+ for name in records[3]['canonical_inputs']:
+ bad = copy.deepcopy(records)
+ bad[0]['compilation_inputs'][name] = ('0' * 64 if name != HELPER
+ else records[3]['canonical_inputs'][HELPER])
+ with self.subTest(input=name), self.assertRaisesRegex(ValueError, 'compilation inputs'):
+ self.subject.validate_source_build(*bad)
+ for added in (True, False):
+ bad = copy.deepcopy(records)
+ if added: bad[0]['compilation_inputs']['vendor/extra.rs'] = '0' * 64
+ else: del bad[0]['compilation_inputs']['Cargo.lock']
+ with self.subTest(added=added), self.assertRaisesRegex(ValueError, 'compilation inputs'):
+ self.subject.validate_source_build(*bad)
+
+
+class SimulatorMechanismAvailability(unittest.TestCase):
+ def test_separate_simulator_admission_mechanism_is_available(self):
+ self.assertTrue(MECHANISM.is_file(), 'separate simulator_exposure.py unavailable; no native defect claimed')
+
+
[email protected](MECHANISM.is_file(), 'admission mechanism absent; native runtime remains unrun')
+class SimulatorMechanismControls(LiteralSimulatorControls):
+ @classmethod
+ def setUpClass(cls):
+ spec = importlib.util.spec_from_file_location('simulator_exposure', MECHANISM)
+ cls.subject = importlib.util.module_from_spec(spec)
+ spec.loader.exec_module(cls.subject)
+
+
+class CanonicalSourceControls(unittest.TestCase):
+ def test_canonical_input_guard_remains_original(self):
+ expected = {'tools/qualification/cross_family/inputs.py':
+ '3dfd336bd132fa7d904aea783c81db94f6832bea304474d86250cb2329dbec44'}
+ for path, digest in expected.items():
+ with self.subTest(source=path):
+ self.assertEqual(hashlib.sha256((ROOT / path).read_bytes()).hexdigest(), digest)
+
+
+if __name__ == '__main__':
+ unittest.main()