mirror of
https://github.com/ruvnet/RuView.git
synced 2026-10-22 06:32:35 -05:00
fix(training): bind compact parent lineage to validated bytes
This commit is contained in:
1 parent
8282a6b21e
commit
56b0085b0b
4 files changed
+109
-9
No files matched your search
@@ -0,0 +1,52 @@
|
||||
{
|
||||
"decision": "INCONCLUSIVE",
|
||||
"date": "2026-10-13",
|
||||
"hypothesis": "Compact export lineage and retention metadata must remain bound to the exact baseline receipt and model bytes admitted by MotionBundle, even if the source paths change after validation.",
|
||||
"baseline": {
|
||||
"commit": "8282a6b21e83756f636d377c4fe5be4a1e489792",
|
||||
"tree": "6a4e65ec1dc9d22e946c6a39978ee6e291543201",
|
||||
"source_sha256": {
|
||||
"scripts/csi_training/espfi_motion.py": "c6a8529ccf9d9f834383a2bdf5b14626764ec7ca2ade7ad5d4c1c14797d76054",
|
||||
"scripts/csi_training/espfi_motion_compact.py": "8da946e1c575ae20b96dc3fdb229bb85cdc5054f6c2efb774aca506773770c2e",
|
||||
"scripts/tests/test_espfi_motion_compact.py": "109031ec230bb0aeadd26acabe2f5c119d714b4c71f9fc28351cf801176c65c5"
|
||||
},
|
||||
"adversarial_result": "FAIL: a forged second receipt read changed emitted parent lineage"
|
||||
},
|
||||
"candidate_source_sha256": {
|
||||
"scripts/csi_training/espfi_motion.py": "25d4f84e16c0b699654caef5c19614ef1d23c7ef0cc1e224e931cc5b95195b63",
|
||||
"scripts/csi_training/espfi_motion_compact.py": "33886d88705410646787a73b05474e36e50be0d4a26535147a1a3bfaddd4b037",
|
||||
"scripts/tests/test_espfi_motion_compact.py": "68b562b1e31ef21ce1db10457f0965d5a882e853e3d8aa40240e82fc47a798e3"
|
||||
},
|
||||
"data_and_split_policy": "No dataset, split, preprocessing, model weights, thresholds, predictions, or final-test payloads were read or changed.",
|
||||
"acceptance": {
|
||||
"adversarial_lineage_and_size_test": "PASS",
|
||||
"focused_compact_tests": "4/4 PASS",
|
||||
"motion_regression_tests": "40/40 PASS",
|
||||
"broader_csi_tests": "72 PASS; collection stopped on missing local optional h5py dependency",
|
||||
"diff_check": "PASS",
|
||||
"independent_security_review": "NO BLOCK",
|
||||
"hosted_ci": "PENDING"
|
||||
},
|
||||
"load_benchmark": {
|
||||
"workload": "500 alternating loads after 20 warmups; 100 traced loads per arm",
|
||||
"baseline": {
|
||||
"p50_ms": 0.974565,
|
||||
"p95_ms": 1.322553,
|
||||
"peak_bytes": 16793369
|
||||
},
|
||||
"candidate": {
|
||||
"p50_ms": 0.98026,
|
||||
"p95_ms": 1.285652,
|
||||
"peak_bytes": 16793409
|
||||
},
|
||||
"frozen_gate": "candidate p95 <= max(1.2 * baseline p95, baseline p95 + 0.1 ms) and peak <= baseline + 4096 bytes",
|
||||
"result": "PASS"
|
||||
},
|
||||
"security_scope": {
|
||||
"fixed": "Parent receipt hash, model hash, model byte count, and retention gate use the exact buffers validated by MotionBundle.",
|
||||
"residual": "Initial multi-path intake is not an authenticated atomic directory snapshot; coherent pre-validation substitution remains outside this patch."
|
||||
},
|
||||
"cost": "Existing authorized ruOS CPU only; monetary cost unknown.",
|
||||
"rollback": "Revert the containing commit.",
|
||||
"next_action": "Require terminal hosted CI at the exact published head before accepting the candidate."
|
||||
}
|
||||
@@ -4,6 +4,7 @@ Independent feature experiment, not a reproduction of an external model.
|
||||
Only grouped training folds select a candidate; final test arrays are absent.
|
||||
"""
|
||||
import argparse
|
||||
import hashlib
|
||||
import json
|
||||
from pathlib import Path
|
||||
import time
|
||||
@@ -88,7 +89,8 @@ class MotionBundle:
|
||||
def __init__(self, directory):
|
||||
root = Path(directory)
|
||||
status = json.loads(_read(root/'status.json', 65536))
|
||||
report = json.loads(_read(root/'metrics.json', 16*1024**2))
|
||||
report_bytes = _read(root/'metrics.json', 16*1024**2)
|
||||
report = json.loads(report_bytes)
|
||||
if (not isinstance(status, dict) or not isinstance(report, dict)
|
||||
or status.get('status') != 'complete' or status.get('promotionAuthorized') is not False
|
||||
or report.get('status') != 'complete' or report.get('promotionAuthorized') is not False
|
||||
@@ -99,8 +101,8 @@ class MotionBundle:
|
||||
if self.candidate not in GRID:
|
||||
raise ValueError('Unknown candidate')
|
||||
data = _read(root/'model.safetensors', 1024**2)
|
||||
import hashlib
|
||||
if hashlib.sha256(data).hexdigest() != report.get('model_sha256'):
|
||||
model_sha256 = hashlib.sha256(data).hexdigest()
|
||||
if model_sha256 != report.get('model_sha256'):
|
||||
raise ValueError('Model hash mismatch')
|
||||
self.state = load(data)
|
||||
width = WIDTHS[self.candidate['family']]
|
||||
@@ -112,6 +114,13 @@ class MotionBundle:
|
||||
for k, shape in expected.items()) or not (self.state['feature_scale'] > 0).all()):
|
||||
raise ValueError('Invalid motion model state')
|
||||
|
||||
provenance = report.get('provenance', {})
|
||||
self._verified_manifest_sha256 = (provenance.get('manifest_sha256')
|
||||
if isinstance(provenance, dict) else None)
|
||||
self._verified_model_sha256 = model_sha256
|
||||
self._verified_model_bytes = len(data)
|
||||
self._verified_receipt_sha256 = hashlib.sha256(report_bytes).hexdigest()
|
||||
|
||||
def predict(self, values):
|
||||
return predict_features(self.state, motion_features(values, self.candidate['family']))
|
||||
|
||||
|
||||
@@ -89,10 +89,8 @@ def export(parts, provenance, baseline_directory, output, max_seconds=600):
|
||||
raise ValueError('Invalid time budget')
|
||||
_validate(parts)
|
||||
baseline = MotionBundle(baseline_directory)
|
||||
prior_bytes = _read(Path(baseline_directory)/'metrics.json',16*1024**2)
|
||||
prior = json.loads(prior_bytes)
|
||||
if (baseline.candidate != BASELINE or not provenance.get('manifest_sha256')
|
||||
or prior.get('provenance',{}).get('manifest_sha256') != provenance['manifest_sha256']):
|
||||
or baseline._verified_manifest_sha256 != provenance['manifest_sha256']):
|
||||
raise ValueError('Baseline candidate or provenance mismatch')
|
||||
root = Path(output)
|
||||
root.mkdir(parents=True, exist_ok=False)
|
||||
@@ -148,15 +146,15 @@ def export(parts, provenance, baseline_directory, output, max_seconds=600):
|
||||
timings[name].append((time.perf_counter_ns()-before)/1e6)
|
||||
latency = {k:{'p50_ms':float(np.percentile(v,50)),'p95_ms':float(np.percentile(v,95))} for k,v in timings.items()}
|
||||
size = (root/'model.safetensors').stat().st_size
|
||||
old_size = (Path(baseline_directory)/'model.safetensors').stat().st_size
|
||||
old_size = baseline._verified_model_bytes
|
||||
checks = {'model_below_10percent':size<.1*old_size,
|
||||
'parity_all_admitted':True,
|
||||
'p95_within_5percent':latency['candidate']['p95_ms']<=1.05*latency['incumbent']['p95_ms']}
|
||||
metadata = {'status':'complete','promotionAuthorized':False,'spec':SPEC,
|
||||
'model_sha256':digest(root/'model.safetensors'),
|
||||
'random_map_sha256':{k:tensor_hash(v) for k,v in generated.items()},
|
||||
'baseline_model_sha256':prior['model_sha256'],
|
||||
'baseline_receipt_sha256':hashlib.sha256(prior_bytes).hexdigest(),
|
||||
'baseline_model_sha256':baseline._verified_model_sha256,
|
||||
'baseline_receipt_sha256':baseline._verified_receipt_sha256,
|
||||
'provenance':{'manifest_sha256':provenance['manifest_sha256']}}
|
||||
report = dict(metadata, provenance=provenance, evidence='SYNTHETIC' if provenance.get('evidence')=='SYNTHETIC' else 'MEASURED',
|
||||
parity=parity,latency=latency,model_bytes=size,baseline_model_bytes=old_size,
|
||||
|
||||
@@ -1,9 +1,11 @@
|
||||
"""Synthetic storage and numerical-parity contracts, not sensing evidence."""
|
||||
import hashlib
|
||||
import json
|
||||
from pathlib import Path
|
||||
import sys
|
||||
import tempfile
|
||||
import unittest
|
||||
from unittest.mock import patch
|
||||
import numpy as np
|
||||
from safetensors.numpy import load, save_file
|
||||
sys.path.insert(0,str(Path(__file__).resolve().parents[1]))
|
||||
@@ -12,6 +14,7 @@ from csi_training.espfi_motion import MotionBundle
|
||||
from csi_training.espfi_train import digest, write_json
|
||||
from test_espfi_context import make_baseline
|
||||
from test_espfi_generalize import fixture
|
||||
import csi_training.espfi_motion_compact as compact_module
|
||||
|
||||
|
||||
class CompactMotionTests(unittest.TestCase):
|
||||
@@ -104,5 +107,43 @@ class CompactMotionTests(unittest.TestCase):
|
||||
with self.assertRaisesRegex(ValueError,'Invalid compact'):
|
||||
CompactMotionBundle(root/'compact')
|
||||
|
||||
def test_export_parent_lineage_uses_verified_baseline_generation(self):
|
||||
parts,provenance=fixture()
|
||||
with tempfile.TemporaryDirectory() as tmp:
|
||||
root=Path(tmp)
|
||||
baseline=root/'baseline'
|
||||
make_baseline(parts,provenance,baseline)
|
||||
expected_model=digest(baseline/'model.safetensors')
|
||||
expected_receipt=digest(baseline/'metrics.json')
|
||||
expected_model_bytes=(baseline/'model.safetensors').stat().st_size
|
||||
forged=json.loads((baseline/'metrics.json').read_text())
|
||||
forged['model_sha256']='f'*64
|
||||
forged_bytes=json.dumps(forged,separators=(',',':'),sort_keys=True).encode()
|
||||
forged_receipt=hashlib.sha256(forged_bytes).hexdigest()
|
||||
real_read=compact_module._read
|
||||
injected=[]
|
||||
swapped=[]
|
||||
|
||||
def replace_second_receipt(path,limit):
|
||||
if Path(path)==baseline/'metrics.json':
|
||||
injected.append(Path(path))
|
||||
return forged_bytes
|
||||
result=real_read(path,limit)
|
||||
if Path(path)==root/'compact'/'model.safetensors' and not swapped:
|
||||
(baseline/'model.safetensors').write_bytes(b'x')
|
||||
swapped.append(Path(path))
|
||||
return result
|
||||
|
||||
with patch.object(compact_module,'_read',side_effect=replace_second_receipt):
|
||||
report=export(parts,provenance,baseline,root/'compact')
|
||||
metadata=json.loads((root/'compact'/'metadata.json').read_text())
|
||||
self.assertEqual(injected,[])
|
||||
self.assertEqual(len(swapped),1)
|
||||
self.assertEqual(metadata['baseline_model_sha256'],expected_model)
|
||||
self.assertEqual(metadata['baseline_receipt_sha256'],expected_receipt)
|
||||
self.assertNotEqual(metadata['baseline_receipt_sha256'],forged_receipt)
|
||||
self.assertEqual(report['baseline_model_bytes'],expected_model_bytes)
|
||||
self.assertTrue(report['retention_checks']['model_below_10percent'])
|
||||
|
||||
if __name__=='__main__':
|
||||
unittest.main()
|
||||
Reference in new issue
Block a user