P04-01: 同時発生の更新規約
This commit is contained in:
@@ -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.
|
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.
|
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.
|
||||||
|
|||||||
@@ -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.
|
||||||
+9
-2
@@ -30,8 +30,15 @@ def rejects(call):
|
|||||||
|
|
||||||
|
|
||||||
def tree(root):
|
def tree(root):
|
||||||
return {p.relative_to(root).as_posix(): hashlib.sha256(p.read_bytes()).hexdigest()
|
rows = {}
|
||||||
for p in root.rglob('*') if p.is_file()}
|
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.
|
# Explicit audited package-local closure; see REPAIR49_STATUS.md.
|
||||||
|
|||||||
+44
@@ -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()
|
||||||
@@ -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
|
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 /
|
destruction/midnight/exit drivers, S1–S4/collector fresh Q, derived 184 suites /
|
||||||
329 parse, truthful index and Root exact-candidate qualification remain pending.
|
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.
|
||||||
|
|||||||
+176
-15
@@ -1,10 +1,13 @@
|
|||||||
#!/usr/bin/env python3
|
#!/usr/bin/env python3
|
||||||
"""Root-only typed mutations against fresh native S5 raw; never synthetic positives."""
|
"""Root-only typed mutations against fresh native S5 raw; never synthetic positives."""
|
||||||
import argparse
|
import argparse
|
||||||
|
from contextlib import contextmanager
|
||||||
|
import hashlib
|
||||||
import json
|
import json
|
||||||
from pathlib import Path
|
from pathlib import Path
|
||||||
import struct
|
import struct
|
||||||
import sys
|
import sys
|
||||||
|
import time
|
||||||
|
|
||||||
sys.dont_write_bytecode = True
|
sys.dont_write_bytecode = True
|
||||||
import s5_order_adapter as a
|
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')
|
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):
|
def guard_mutations(record):
|
||||||
"""All typed AFTER nodes are tested, including every changed journal field.
|
"""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.
|
must still reject it. No native evidence is produced by these controls.
|
||||||
"""
|
"""
|
||||||
oracle = a.guard_oracle
|
oracle = a.guard_oracle
|
||||||
|
estimate = static_estimate(record)
|
||||||
a.require(oracle.verify(record, a.series.variant, a.require), 'guard control prerequisite')
|
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')
|
after = record.at('after')
|
||||||
|
|
||||||
def visit(node, path):
|
def visit(node, path):
|
||||||
|
nonlocal nodes, candidates
|
||||||
|
nodes += 1
|
||||||
|
kinds[str(node.kind)] = kinds.get(str(node.kind), 0) + 1
|
||||||
replacements = []
|
replacements = []
|
||||||
if node.kind in (4, 21):
|
if node.kind in (4, 21):
|
||||||
replacements = [('value', oracle.atom(node.kind, node.value + '.mutated')),
|
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('<I', len(node.value)+1)
|
replacements.append(('extra-row', node.raw[:start-4] + struct.pack('<I', len(node.value)+1)
|
||||||
+ node.raw[start:] + node.value[-1].raw))
|
+ node.raw[start:] + node.value[-1].raw))
|
||||||
if len(node.value) > 1:
|
if len(node.value) > 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):
|
for i, child in enumerate(node.value):
|
||||||
visit(child, path + '/' + str(i))
|
visit(child, path + '/' + str(i))
|
||||||
elif node.kind == 27:
|
elif node.kind == 27:
|
||||||
if len(node.value) > 1:
|
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:
|
for key, child in node.value:
|
||||||
visit(key, path + '/key:' + str(key.value))
|
visit(key, path + '/key:' + str(key.value))
|
||||||
visit(child, path + '/' + str(key.value))
|
visit(child, path + '/' + str(key.value))
|
||||||
for label, encoded in replacements:
|
for label, encoded in replacements:
|
||||||
|
candidates += 1
|
||||||
|
identity = encoded == node.raw
|
||||||
changed_after = mutation.replace(after, node, encoded)
|
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):
|
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:
|
try:
|
||||||
changed = a.series.variant(changed_record)
|
tested = a.series.variant(copied_bytes) if copied else changed
|
||||||
if copied:
|
|
||||||
changed_record = mutation.replace(changed, changed.at('expected'), changed_after)
|
|
||||||
oracle.verify(a.series.variant(changed_record), a.series.variant, a.require)
|
|
||||||
except ValueError:
|
except ValueError:
|
||||||
checks.append(path + ':' + label + (':AFTER-copy' if copied else ''))
|
if identity:
|
||||||
continue
|
raise
|
||||||
raise ValueError('guard mutation accepted: ' + path + ':' + label)
|
tested = None
|
||||||
visit(after, 'after')
|
if tested is None:
|
||||||
return checks
|
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():
|
def pure_controls():
|
||||||
@@ -82,7 +229,9 @@ def pure_controls():
|
|||||||
('id_generator', d([('version', atom(2, 1)), ('counters', d([('domain_event', atom(2, 2))]))])),
|
('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))])),
|
('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))])),
|
('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)]))]))
|
before = a.series.variant(d([('systems', d([('runtime', runtime)]))]))
|
||||||
results = {}
|
results = {}
|
||||||
for guard in ('G1', 'G2'):
|
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)),
|
record = a.series.variant(d([('before', before.raw), ('command', command.raw), ('guard', atom(4, guard)),
|
||||||
('expected', expected.raw), ('after', expected.raw), ('result', result.raw)]))
|
('expected', expected.raw), ('after', expected.raw), ('result', result.raw)]))
|
||||||
results[guard] = guard_mutations(record)
|
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}
|
return {'synthetic_serializer_controls_only': True, 'negative_controls': results, 'formal_acceptance': False}
|
||||||
|
|
||||||
|
|
||||||
def main():
|
def main():
|
||||||
parser = argparse.ArgumentParser(description=__doc__)
|
parser = argparse.ArgumentParser(description=__doc__)
|
||||||
parser.add_argument('--pure-only', action='store_true')
|
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('--raw-root', type=Path)
|
||||||
parser.add_argument('--writer-sha256')
|
parser.add_argument('--writer-sha256')
|
||||||
parser.add_argument('--reader-sha256')
|
parser.add_argument('--reader-sha256')
|
||||||
parser.add_argument('--scratch', type=Path)
|
parser.add_argument('--scratch', type=Path)
|
||||||
args = parser.parse_args()
|
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:
|
if args.pure_only:
|
||||||
print(json.dumps(pure_controls(), sort_keys=True))
|
print(json.dumps(pure_controls(), sort_keys=True))
|
||||||
return
|
return
|
||||||
@@ -133,6 +290,7 @@ def main():
|
|||||||
cases[case] = ({k: raw.typed(v) for k, v in branch['points'].items()},
|
cases[case] = ({k: raw.typed(v) for k, v in branch['points'].items()},
|
||||||
[raw.typed(r) for r in branch['phases']])
|
[raw.typed(r) for r in branch['phases']])
|
||||||
checks = []
|
checks = []
|
||||||
|
guard_reports = {}
|
||||||
|
|
||||||
def negative(label, case, owner, target, replacement):
|
def negative(label, case, owner, target, replacement):
|
||||||
points, phases = cases[case]
|
points, phases = cases[case]
|
||||||
@@ -202,7 +360,9 @@ def main():
|
|||||||
for case in ('S5D_DESTRUCTION_RAID', 'S5D_IN_FLIGHT'):
|
for case in ('S5D_DESTRUCTION_RAID', 'S5D_IN_FLIGHT'):
|
||||||
branch = raw.reference(writer['branches'][case])
|
branch = raw.reference(writer['branches'][case])
|
||||||
for label, reference in branch['guards'].items():
|
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'
|
command = 'S5C_DESTRUCTION_COMMAND'
|
||||||
points, phases = cases[command]
|
points, phases = cases[command]
|
||||||
end = points['held']
|
end = points['held']
|
||||||
@@ -251,6 +411,7 @@ def main():
|
|||||||
start = 12 if ledger.raw[:4] == struct.pack('<I', 65564) else 8
|
start = 12 if ledger.raw[:4] == struct.pack('<I', 65564) else 8
|
||||||
negative('replay-extra-row', midnight, replay, ledger, ledger.raw[:start-4] + struct.pack('<I', len(ledger.value)+1) + ledger.raw[start:] + ledger.value[-1].raw)
|
negative('replay-extra-row', midnight, replay, ledger, ledger.raw[:start-4] + struct.pack('<I', len(ledger.value)+1) + ledger.raw[start:] + ledger.value[-1].raw)
|
||||||
print(json.dumps({'positive_cases': list(positive['cases']), 'negative_controls': checks,
|
print(json.dumps({'positive_cases': list(positive['cases']), 'negative_controls': checks,
|
||||||
|
'negative_count': len(checks), 'guard_reports': guard_reports,
|
||||||
'formal_acceptance': False}, sort_keys=True))
|
'formal_acceptance': False}, sort_keys=True))
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user