From a9ef681fb8f3356507d3d6e58ae30a339deb8a6c Mon Sep 17 00:00:00 2001 From: Agent Control Runner Date: Sun, 18 Oct 2026 21:01:07 +0900 Subject: [PATCH] =?UTF-8?q?P04-01:=20=E5=90=8C=E6=99=82=E7=99=BA=E7=94=9F?= =?UTF-8?q?=E3=81=AE=E6=9B=B4=E6=96=B0=E8=A6=8F=E7=B4=84?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../execution/p04-01/ORDER_AUDIT.md | 33 +++ .../execution/p04-01/REPAIR50_STATUS.md | 68 +++++++ .../p04-01/repair48_identity_controls.py | 11 +- .../p04-01/repair50_stream_controls.py | 44 ++++ .../execution/p04-01/s5/S5_ROOT_RUN.md | 22 ++ .../p04-01/s5/s5_order_adapter_controls.py | 191 ++++++++++++++++-- 6 files changed, 352 insertions(+), 17 deletions(-) create mode 100644 docs/plans/original-completion-2026-10-02/execution/p04-01/REPAIR50_STATUS.md create mode 100644 docs/plans/original-completion-2026-10-02/execution/p04-01/repair50_stream_controls.py diff --git a/docs/plans/original-completion-2026-10-02/execution/p04-01/ORDER_AUDIT.md b/docs/plans/original-completion-2026-10-02/execution/p04-01/ORDER_AUDIT.md index 23e199d..a219c40 100644 --- a/docs/plans/original-completion-2026-10-02/execution/p04-01/ORDER_AUDIT.md +++ b/docs/plans/original-completion-2026-10-02/execution/p04-01/ORDER_AUDIT.md @@ -108,3 +108,36 @@ The preserved S1–S4, r22 index, old registered validation and f5 full178 run r The eleven phases remain APPLY_COMMANDS, TIME_BOUNDARIES, MANA, CONSTRUCTION, LOGISTICS, FACILITY_COMPLETION, VITALS_AND_ENVIRONMENT, INCIDENTS_AND_EVENTS, FACILITY_QUEUES, AI_DECISIONS, PUBLISH_SNAPSHOT. See the structured audit for preserved phase analysis and the new source manifest for exact M references. See [CURRENT_STATUS.md](CURRENT_STATUS.md) and [REPAIR31_STATUS.md](REPAIR31_STATUS.md) before any Root execution. + +## REPAIR50 source preparation (2026-10-18; no acceptance claim) + +The commander handoff reports an identity order mutation on an all-equal array +(`recent_events/17/payload/current_cell = [0,0,0]`), raw SHA prefix +`bb0ccb1d…`, and 761.085 seconds elapsed. These are supplied observations, +not measurements repeated by this worker; the full raw SHA/log remains Root +material. The old control reversed every multi-element array and classified +the resulting identical bytes as a negative. The oracle correctly accepted +that identity. Existing failure records remain unchanged. + +REPAIR50 separates byte-identical identity positives from negatives, swaps the +first unequal typed byte pair, and lists collections with no genuine order +variant. All AFTER keys/values, journal/body fields and both copied modes +remain traversed. Type-invalid mutations still require ValueError rejection +and are also listed as parser rejections; copied records contain the changed +bytes in both AFTER and EXPECTED even when an intermediate parse is invalid. +The oracle, adapters, public checker, native-series implementation and R49 +closure roster are unchanged. No Node patch optimization is used. + +The temporary expected memo keys full BEFORE/command/guard bytes by SHA256 +and length plus oracle/parser source SHA256. Its cache contains only genuine +derivation output bytes. It reconstructs fresh Nodes on each return, restores +the wrapper with finally, and retains verify for each parseable mutation. +Self-controls compare hit/nonmemo bytes for first/last and each mutated kind, +require a one-byte BEFORE miss and reject, and check normal/exception cleanup. +The SHA fixture now streams 1 MiB implementation chunks without changing +file selection, traversal, relative keys or digest semantics. + +Worker validation: AST parsing only; pure, stream, native and formal checks +are unexecuted. See [REPAIR50_STATUS.md](REPAIR50_STATUS.md) for Root commands, +static estimation and reuse prerequisites. Original 30/88, formal 16 and P04 +credit 0 remain unchanged; the whole unit is incomplete. diff --git a/docs/plans/original-completion-2026-10-02/execution/p04-01/REPAIR50_STATUS.md b/docs/plans/original-completion-2026-10-02/execution/p04-01/REPAIR50_STATUS.md new file mode 100644 index 0000000..952a912 --- /dev/null +++ b/docs/plans/original-completion-2026-10-02/execution/p04-01/REPAIR50_STATUS.md @@ -0,0 +1,68 @@ +# REPAIR50 source-ready handoff + +Scope: control classification/memo, tree() read method only, stream equivalence +control and documentation. No Git write, engine, Runner, integration or formal +test was performed. Three changed/new Python sources passed AST parsing. + +## Root-only execution and estimation + +From this package directory, run the following pure controls once and retain +their separate JSON output and external elapsed/RSS measurements: + +``` +python3 s5/s5_order_adapter_controls.py --pure-only +python3 repair50_stream_controls.py +``` + +The first emits separate negative counts/lists, identity positive counts/lists, +no-genuine-order lists, memo hit/miss, parser-rejection list and elapsed per +guard. The second compares the complete whole-read and streamed SHA maps and +key insertion order for empty, Unicode name/content, binary, sparse, chunk +boundary -1/exact/+1, multiple chunks and nested files. It uses temporary +synthetic files only and creates no native acceptance evidence. + +Before heavy typed controls, parse each actual retained guard record with: + +``` +python3 s5/s5_order_adapter_controls.py --estimate-record /absolute/path/to/guard.var +``` + +This performs no derivation or verify: output includes record byte size, +AFTER node count, kind histogram, replacement count and replacement count x2 +(including identities, never reported as negatives). Guard mutation disk +estimate is zero; stream control logical fixture size is about 10 MiB, with +the sparse part filesystem-dependent. Memory scales with retained parsed +representatives (at most one per mutated kind, plus first/last), record bytes, +parser overhead and output labels. Memo holds one pair of expected/result raw +bytes for unchanged inputs; it never caches AFTER or recorded EXPECTED. +Time scales with mutation count x record bytes: parse/verify still examine +whole records. Use the synthetic run's external peak RSS and elapsed, together +with its emitted counts/bytes, as an initial calibration; real record shape +and source-derivation costs make this an estimate, not an ETA or resource cap. +Actual guard bytes/counts and calibrated measurements are Root-pending. + +## complete_B scope reuse remains conditional + +The supplied commander handoff reports R49 session56645 exit 0, 49-file map, +S5 facts/payload equality, no B changes, log SHA prefix bdef6715 and closure +tree prefix b54a35ed. This worker has not independently verified that run. +Root must mechanically compare its saved full raw map with the new tree and +record only the authorized control/tree-read/new-control/document changes. +Inspect import/exec and input closure for builder, collector, native-series, +reader, oracle, wrapper, probe and helper; preserve S47 raw, 21pin, legacy5, +full184 originals, oldQ source/engine SHA and public run_all_tests.py SHA. + +Important: repair48_identity_controls.CLOSURE explicitly lists the changed +order control, identity control and S5_ROOT_RUN.md as snapshot material. +The roster is unchanged. Direct searches in builder/native-series/order +adapter did not reference these names, but this is not a complete graph proof +and does not establish reuse. Root must distinguish the actual two-directory +analysis/payload execution/input closure from this snapshot roster and apply +all commander conditions. If any condition fails, rerun complete_B once. +No R49 closure bytes or raw evidence were rewritten to obtain reuse. + +Then preserve the recorded Root order: pure controls → clean exact-Q check-only +→ capacity/PID confirmation → fresh native6 → reader/collector → all eight +full exact-source reuse conditions (failure means one full run) → index → one +registered p04-acceptance validation → independent whole-unit review → normal +gates/RootDB join/ledger/main push. Only whole-unit acceptance awards credit. diff --git a/docs/plans/original-completion-2026-10-02/execution/p04-01/repair48_identity_controls.py b/docs/plans/original-completion-2026-10-02/execution/p04-01/repair48_identity_controls.py index 2fed753..2b2bc29 100644 --- a/docs/plans/original-completion-2026-10-02/execution/p04-01/repair48_identity_controls.py +++ b/docs/plans/original-completion-2026-10-02/execution/p04-01/repair48_identity_controls.py @@ -30,8 +30,15 @@ def rejects(call): def tree(root): - return {p.relative_to(root).as_posix(): hashlib.sha256(p.read_bytes()).hexdigest() - for p in root.rglob('*') if p.is_file()} + rows = {} + for p in root.rglob('*'): + if p.is_file(): + digest = hashlib.sha256() + with p.open('rb') as source: + for chunk in iter(lambda: source.read(1024 * 1024), b''): + digest.update(chunk) + rows[p.relative_to(root).as_posix()] = digest.hexdigest() + return rows # Explicit audited package-local closure; see REPAIR49_STATUS.md. diff --git a/docs/plans/original-completion-2026-10-02/execution/p04-01/repair50_stream_controls.py b/docs/plans/original-completion-2026-10-02/execution/p04-01/repair50_stream_controls.py new file mode 100644 index 0000000..22722eb --- /dev/null +++ b/docs/plans/original-completion-2026-10-02/execution/p04-01/repair50_stream_controls.py @@ -0,0 +1,44 @@ +#!/usr/bin/env python3 +"""Root-only engine-free whole-read/stream SHA equivalence controls.""" +import hashlib +import importlib.util +import json +from pathlib import Path +import sys +import tempfile +import time + +sys.dont_write_bytecode = True + + +def main(): + started = time.monotonic() + path = Path(__file__).with_name('repair48_identity_controls.py') + spec = importlib.util.spec_from_file_location('r50_identity', path) + module = importlib.util.module_from_spec(spec) + exec(compile(path.read_bytes(), str(path), 'exec'), module.__dict__) + chunk = 1024 * 1024 # Implementation boundary, never an acceptance limit. + with tempfile.TemporaryDirectory(prefix='p04-r50-stream-') as directory: + root = Path(directory) + (root / 'empty').write_bytes(b'') + (root / '日本語🌳').write_bytes('木と水\n🌳'.encode()) + (root / 'binary').write_bytes(bytes(range(256)) * 5) + for size in (chunk - 1, chunk, chunk + 1, chunk * 3 + 17): + (root / str(size)).write_bytes((bytes(range(256)) * (size // 256 + 1))[:size]) + with (root / 'sparse').open('wb') as stream: + stream.seek(chunk * 4 + 3) + stream.write(b'\xff') + nested = root / 'nested' + nested.mkdir() + (nested / '全bytes').write_bytes(b'\x00\xff\x80\n') + whole = {p.relative_to(root).as_posix(): hashlib.sha256(p.read_bytes()).hexdigest() + for p in root.rglob('*') if p.is_file()} + streamed = module.tree(root) + assert list(streamed) == list(whole), 'same traversal/key insertion order' + assert streamed == whole, 'complete SHA map equality' + print(json.dumps({'stream_equivalence': 'pass', 'files': whole, + 'elapsed': time.monotonic() - started, 'formal_acceptance': False}, sort_keys=True)) + + +if __name__ == '__main__': + main() diff --git a/docs/plans/original-completion-2026-10-02/execution/p04-01/s5/S5_ROOT_RUN.md b/docs/plans/original-completion-2026-10-02/execution/p04-01/s5/S5_ROOT_RUN.md index 69a9154..f4bcf3f 100644 --- a/docs/plans/original-completion-2026-10-02/execution/p04-01/s5/S5_ROOT_RUN.md +++ b/docs/plans/original-completion-2026-10-02/execution/p04-01/s5/S5_ROOT_RUN.md @@ -359,3 +359,25 @@ Runtime helper fingerprints and source/provenance guards bind the actual Q. No full regression, 15+EV batch or original credit is claimed. The S5 destruction/midnight/exit drivers, S1–S4/collector fresh Q, derived 184 suites / 329 parse, truthful index and Root exact-candidate qualification remain pending. + +## REPAIR50 preparation (2026-10-18) + +Commander-reported prior observation: all-equal array reversal produced +identical bytes (raw SHA prefix `bb0ccb1d…`), correctly accepted by the oracle; +the old control incorrectly required reject. Reported elapsed: 761.085 seconds. +This worker has not rerun that observation and does not supply a full SHA +from the abbreviated handoff. Earlier failure records are preserved. + +The order control now records identity accept separately, swaps unequal typed +bytes, lists no-genuine-order cases, avoids duplicate parsing of the uncopied +record and memoizes only genuine BEFORE/command/guard derivations under strict +source-bound keys with finally restoration. Both copied modes and all +AFTER/journal/body traversal remain. Negative rejection is still ValueError; +parser rejection is separately visible. Synthetic equal-array, swap, type, +value, AFTER-copy and memo controls are prepared in --pure-only. + +repair48_identity_controls.tree now hashes fixed-size chunks; its roster and +all other fixture logic remain unchanged. Root must run the new +repair50_stream_controls.py equivalence control. Only AST parse was performed +by this worker. No pure/native/formal pass, reuse approval or credit is claimed. +See ../REPAIR50_STATUS.md before running the existing Root sequence. diff --git a/docs/plans/original-completion-2026-10-02/execution/p04-01/s5/s5_order_adapter_controls.py b/docs/plans/original-completion-2026-10-02/execution/p04-01/s5/s5_order_adapter_controls.py index de94ac4..90a9ba5 100644 --- a/docs/plans/original-completion-2026-10-02/execution/p04-01/s5/s5_order_adapter_controls.py +++ b/docs/plans/original-completion-2026-10-02/execution/p04-01/s5/s5_order_adapter_controls.py @@ -1,10 +1,13 @@ #!/usr/bin/env python3 """Root-only typed mutations against fresh native S5 raw; never synthetic positives.""" import argparse +from contextlib import contextmanager +import hashlib import json from pathlib import Path import struct import sys +import time sys.dont_write_bytecode = True import s5_order_adapter as a @@ -13,6 +16,70 @@ mutation = a.module('order_typed_mutation', Path(__file__).with_name('s5_power_a BUILD = Path('/Users/korizatou114/Projects/TheTree-managed-worktrees/p04-01/.runtime/P04-01/build') +def order_variant(node): + """Swap the first unequal typed byte pair, preserving the header.""" + rows = [r.raw if node.kind == 28 else r[0].raw + r[1].raw for r in node.value] + for i in range(len(rows)): + for j in range(i + 1, len(rows)): + if rows[i] != rows[j]: + header = len(node.raw) - sum(map(len, rows)) + rows[i], rows[j] = rows[j], rows[i] + return node.raw[:header] + b''.join(rows) + return None + + +def static_estimate(record): + kinds, nodes, candidates = {}, 0, 0 + + def visit(node): + nonlocal nodes, candidates + nodes += 1 + kinds[str(node.kind)] = kinds.get(str(node.kind), 0) + 1 + candidates += (2 if node.kind in (4, 21, 2) else 1 if node.kind == 1 else + int(bool(node.value)) + int(len(node.value) > 1) if node.kind == 28 else + int(len(node.value) > 1) if node.kind == 27 else 0) + if node.kind == 28: + for child in node.value: + visit(child) + elif node.kind == 27: + for key, child in node.value: + visit(key) + visit(child) + visit(record.at('after')) + return {'record_bytes': len(record.raw), 'after_nodes': nodes, 'kind_counts': kinds, + 'candidate_count': candidates, 'verify_mutations': candidates * 2, + 'disk_mutation_bytes': 0, + 'memory_model': 'O(record_bytes * (mutated_kinds + 2) + after_nodes + output_labels); parser overhead unmeasured', + 'time_model': 'O(verify_mutations * record_bytes); calibrate with --pure-only; not a measured ETA'} + + +@contextmanager +def expected_memo(stats, guard_raw): + oracle = a.guard_oracle + original = oracle.expected + sources = tuple(hashlib.sha256(Path(m.__file__).read_bytes()).digest() + for m in (oracle, a.series)) + cache = {} + + def wrapped(before, command, guard, parse, require): + require(parse(guard_raw).value == guard, 'memo exact guard binding') + blobs = (before.raw, command.raw, guard_raw) + key = (tuple((len(b), hashlib.sha256(b).digest()) for b in blobs), sources) + if key not in cache: + stats['miss'] += 1 + tree, result = original(before, command, guard, parse, require) + cache[key] = (tree.raw, result.raw) + else: + stats['hit'] += 1 + return tuple(parse(b) for b in cache[key]) + + oracle.expected = wrapped + try: + yield original + finally: + oracle.expected = original + + def guard_mutations(record): """All typed AFTER nodes are tested, including every changed journal field. @@ -20,11 +87,20 @@ def guard_mutations(record): must still reject it. No native evidence is produced by these controls. """ oracle = a.guard_oracle + estimate = static_estimate(record) a.require(oracle.verify(record, a.series.variant, a.require), 'guard control prerequisite') - checks = [] + started = time.monotonic() + checks, identities, no_order = [], [], [] + stats = {'hit': 0, 'miss': 0} + kinds, nodes, candidates = {}, 0, 0 + representatives, first_last = {}, [] + parser_rejections = [] after = record.at('after') def visit(node, path): + nonlocal nodes, candidates + nodes += 1 + kinds[str(node.kind)] = kinds.get(str(node.kind), 0) + 1 replacements = [] if node.kind in (4, 21): replacements = [('value', oracle.atom(node.kind, node.value + '.mutated')), @@ -40,30 +116,101 @@ def guard_mutations(record): replacements.append(('extra-row', node.raw[:start-4] + struct.pack(' 1: - replacements.append(('unsorted', node.raw[:start] + b''.join(r.raw for r in reversed(node.value)))) + encoded = order_variant(node) + if encoded is None: + no_order.append(path + ':order-no-genuine-variant') + replacements.append(('identity-order' if encoded is None else 'unsorted', + node.raw if encoded is None else encoded)) for i, child in enumerate(node.value): visit(child, path + '/' + str(i)) elif node.kind == 27: if len(node.value) > 1: - replacements.append(('unsorted', oracle.dictionary([(k.raw, v.raw) for k, v in reversed(node.value)]))) + encoded = order_variant(node) + if encoded is None: + no_order.append(path + ':order-no-genuine-variant') + replacements.append(('identity-order' if encoded is None else 'unsorted', + node.raw if encoded is None else encoded)) for key, child in node.value: visit(key, path + '/key:' + str(key.value)) visit(child, path + '/' + str(key.value)) for label, encoded in replacements: + candidates += 1 + identity = encoded == node.raw changed_after = mutation.replace(after, node, encoded) + changed_bytes = mutation.replace(record, after, changed_after) + try: + changed = a.series.variant(changed_bytes) + except ValueError: + if identity: + raise + changed = None for copied in (False, True): - changed_record = mutation.replace(record, after, changed_after) + name = path + ':' + label + (':AFTER-copy' if copied else '') + # Copy even malformed typed AFTER bytes into EXPECTED, without + # parsing the invalid intermediate record a second time. + copied_bytes = (record.raw[:8] + b''.join( + k.raw + (changed_after if v is after or v is record.at('expected') else v.raw) + for k, v in record.value)) if copied else None try: - changed = a.series.variant(changed_record) - if copied: - changed_record = mutation.replace(changed, changed.at('expected'), changed_after) - oracle.verify(a.series.variant(changed_record), a.series.variant, a.require) + tested = a.series.variant(copied_bytes) if copied else changed except ValueError: - checks.append(path + ':' + label + (':AFTER-copy' if copied else '')) - continue - raise ValueError('guard mutation accepted: ' + path + ':' + label) - visit(after, 'after') - return checks + if identity: + raise + tested = None + if tested is None: + checks.append(name) + parser_rejections.append(name) + # Derivation inputs remain the original record's inputs. + tested = record + elif identity: + a.require(oracle.verify(tested, a.series.variant, a.require), 'identity accepted') + identities.append(name) + else: + try: + oracle.verify(tested, a.series.variant, a.require) + except ValueError: + checks.append(name) + else: + raise ValueError('guard mutation accepted: ' + name) + representatives.setdefault(node.kind, tested) + first_last[:] = [first_last[0] if first_last else tested, tested] + original = oracle.expected + with expected_memo(stats, record.at('guard').raw) as derive: + visit(after, 'after') + for tested in first_last + list(representatives.values()): + args = (tested.at('before'), tested.at('command'), tested.at('guard').value, + a.series.variant, a.require) + hit, plain = oracle.expected(*args), derive(*args) + a.require(tuple(n.raw for n in hit) == tuple(n.raw for n in plain), 'memo bytes') + before = record.at('before') + target = before.at('systems').at('runtime').at('next_command_id') + bad = bytearray(target.raw) + bad[4] ^= 1 + changed_before = a.series.variant(mutation.replace(before, target, bytes(bad))) + changed_record = a.series.variant(mutation.replace(record, before, changed_before.raw)) + misses = stats['miss'] + try: + oracle.verify(changed_record, a.series.variant, a.require) + except ValueError: + pass + else: + raise ValueError('one-byte BEFORE mutation accepted') + a.require(stats['miss'] == misses + 1, 'one-byte BEFORE cache miss') + a.require(oracle.expected is original, 'memo wrapper restored') + try: + with expected_memo({'hit': 0, 'miss': 0}, record.at('guard').raw): + raise ValueError('cleanup sentinel') + except ValueError: + pass + a.require(oracle.expected is original, 'exceptional memo wrapper restored') + a.require(estimate['verify_mutations'] == candidates * 2 and estimate['after_nodes'] == nodes + and estimate['kind_counts'] == kinds, 'static enumeration agrees') + return {'negative_controls': checks, 'negative_count': len(checks), + 'identity_positive_count': len(identities), 'identity_positive_list': identities, + 'no_genuine_order_variant': no_order, 'memo': stats, + 'parser_rejections': parser_rejections, + 'elapsed': time.monotonic() - started, + 'static_estimate': estimate} def pure_controls(): @@ -82,7 +229,9 @@ def pure_controls(): ('id_generator', d([('version', atom(2, 1)), ('counters', d([('domain_event', atom(2, 2))]))])), ('domain_events', d([('version', atom(2, 1)), ('published_ids', arr([atom(4, 'domain_event.000000000001'), atom(4, 'domain_event.000000000002')], 4)), ('recent_events', arr([], 27))])), ('operations', d([('enabled', atom(1, 0))])), - ('business', d([('wallet', atom(2, 100)), ('reservations', arr([], 27))]))]) + ('business', d([('wallet', atom(2, 100)), ('reservations', arr([], 27)), + ('equal_array', arr([atom(2, 0)] * 3, 2)), + ('swap_array', arr([atom(2, 1), atom(2, 2)], 2))]))]) before = a.series.variant(d([('systems', d([('runtime', runtime)]))])) results = {} for guard in ('G1', 'G2'): @@ -102,17 +251,25 @@ def pure_controls(): record = a.series.variant(d([('before', before.raw), ('command', command.raw), ('guard', atom(4, guard)), ('expected', expected.raw), ('after', expected.raw), ('result', result.raw)])) results[guard] = guard_mutations(record) + report = results[guard] + a.require(any('equal_array:identity-order' in x for x in report['identity_positive_list']), 'equal array positive') + for label in ('swap_array:unsorted', 'wallet:value', 'wallet:int-float', 'wallet:value:AFTER-copy'): + a.require(any(label in x for x in report['negative_controls']), 'synthetic coverage: ' + label) return {'synthetic_serializer_controls_only': True, 'negative_controls': results, 'formal_acceptance': False} def main(): parser = argparse.ArgumentParser(description=__doc__) parser.add_argument('--pure-only', action='store_true') + parser.add_argument('--estimate-record', type=Path, help='parse one guard record and print static estimate only') parser.add_argument('--raw-root', type=Path) parser.add_argument('--writer-sha256') parser.add_argument('--reader-sha256') parser.add_argument('--scratch', type=Path) args = parser.parse_args() + if args.estimate_record: + print(json.dumps(static_estimate(a.series.variant(args.estimate_record.read_bytes())), sort_keys=True)) + return if args.pure_only: print(json.dumps(pure_controls(), sort_keys=True)) return @@ -133,6 +290,7 @@ def main(): cases[case] = ({k: raw.typed(v) for k, v in branch['points'].items()}, [raw.typed(r) for r in branch['phases']]) checks = [] + guard_reports = {} def negative(label, case, owner, target, replacement): points, phases = cases[case] @@ -202,7 +360,9 @@ def main(): for case in ('S5D_DESTRUCTION_RAID', 'S5D_IN_FLIGHT'): branch = raw.reference(writer['branches'][case]) for label, reference in branch['guards'].items(): - checks.extend(label + ':' + name for name in guard_mutations(raw.typed(reference))) + report = guard_mutations(raw.typed(reference)) + guard_reports[case + ':' + label] = report + checks.extend(label + ':' + name for name in report['negative_controls']) command = 'S5C_DESTRUCTION_COMMAND' points, phases = cases[command] end = points['held'] @@ -251,6 +411,7 @@ def main(): start = 12 if ledger.raw[:4] == struct.pack('