feat(web): auto-discover exact evidence batches
This commit is contained in:
@@ -277,6 +277,60 @@ def batch_contract(task: str, batch: dict) -> dict:
|
||||
return {"batchId": batch.get("batchId") or f"{task}:{batch['moduleId']}:{digest}", "selectedRecordIds": selected}
|
||||
|
||||
|
||||
def discover_existing_evidence_batch(inventory: dict) -> dict | None:
|
||||
"""Find exact Chromium receipts that list currently-unbound records.
|
||||
|
||||
This is an evidence-only discovery path: it never infers coverage from a
|
||||
neighboring source file and never schedules a browser rerun. A new batch
|
||||
receives a copied, task-bound evidence record through the module runner.
|
||||
"""
|
||||
unresolved = {unit["id"]: unit for unit in inventory.get("units", []) if isinstance(unit, dict) and unit.get("coverageStatus") != "VERIFIED"}
|
||||
if not unresolved:
|
||||
return None
|
||||
operation_tests = (
|
||||
("FCURVE", "web/tests/e2e/wbv2-fcurve-modifier-production.spec.ts"),
|
||||
("LINE_STYLE", "web/tests/e2e/wbv2-line-style-reader-production.spec.ts"),
|
||||
("SHAPE_KEY", "web/tests/e2e/wbv2-shape-key-reader-production.spec.ts"),
|
||||
("MODIFIER", "web/tests/e2e/wbv2-modifier-reader-batch-two-production.spec.ts"),
|
||||
)
|
||||
for evidence_path in sorted(ROOT.glob("tests/golden/WBV2-P*-001/*.json"), key=lambda item: item.as_posix()):
|
||||
try:
|
||||
evidence = json.loads(evidence_path.read_text(encoding="utf-8"))
|
||||
except (OSError, json.JSONDecodeError):
|
||||
continue
|
||||
if evidence.get("browser") != "chromium" or evidence.get("queueMutation") is not False:
|
||||
continue
|
||||
source_units = evidence.get("sourceUnits")
|
||||
if not isinstance(source_units, list) or not source_units or not all(isinstance(unit, str) for unit in source_units):
|
||||
continue
|
||||
if not set(source_units).issubset(unresolved):
|
||||
continue
|
||||
operation = str(evidence.get("operation", ""))
|
||||
test = next((path for marker, path in operation_tests if marker in operation), None)
|
||||
fixture_paths = evidence.get("fixtures") if isinstance(evidence.get("fixtures"), list) else [evidence.get("fixture")]
|
||||
fixture_paths = [fixture for fixture in fixture_paths if isinstance(fixture, str) and (ROOT / fixture).is_file()]
|
||||
if not test or not (ROOT / test).is_file() or not fixture_paths:
|
||||
continue
|
||||
identifiers = [unit.split(":", 1)[1] for unit in source_units if unit.startswith("modifier-source:")]
|
||||
rna = [unit.split(":", 1)[1] for unit in source_units if unit.startswith("rna-struct:")]
|
||||
expected_types = evidence.get("expectedTypes") if isinstance(evidence.get("expectedTypes"), list) else []
|
||||
return {
|
||||
"moduleId": f"AUTO_EVIDENCE_REUSE_{evidence_path.parent.name}",
|
||||
"title": f"reuse exact Chromium evidence from {evidence_path.parent.name}",
|
||||
"owner": str(next((unresolved[unit].get("family") for unit in source_units if unresolved[unit].get("family")), "PROGRAM")),
|
||||
"identifiers": identifiers,
|
||||
"rna": rna,
|
||||
"fixtures": fixture_paths,
|
||||
"test": test,
|
||||
"production": "tests/golden/{task}/auto-reused-production.json",
|
||||
"types": expected_types,
|
||||
"units": [unresolved[unit] for unit in source_units],
|
||||
"candidateUnits": [unresolved[unit] for unit in source_units],
|
||||
"reuseExistingEvidence": rel(evidence_path),
|
||||
"reuseEvidenceSource": rel(evidence_path),
|
||||
}
|
||||
|
||||
|
||||
def run_node(*args: str) -> None:
|
||||
result = subprocess.run(["node", *args], cwd=ROOT, text=True, capture_output=True, check=False)
|
||||
output = result.stdout or result.stderr
|
||||
@@ -488,6 +542,9 @@ def select_batch(inventory: dict) -> dict:
|
||||
next_size = current_size
|
||||
chosen = unresolved[:max(min_size, min(max_size, len(unresolved), next_size))]
|
||||
return {**candidate, "units": chosen, "candidateUnits": selected, "adaptiveKey": key, "adaptiveHistory": recent, "adaptiveCurrentSize": current_size, "adaptiveNextSize": len(chosen), "adaptiveMinSize": min_size, "adaptiveMaxSize": max_size}
|
||||
discovered = discover_existing_evidence_batch(inventory)
|
||||
if discovered:
|
||||
return discovered
|
||||
raise SystemExit("no configured unresolved Blender source batch remains")
|
||||
|
||||
|
||||
@@ -554,6 +611,8 @@ def main() -> int:
|
||||
module_record = {"id": module_task, "phase": str(phase), "title": batch["title"], "kind": "module", "owner": batch["owner"], "dependsOn": [root_task], "parityId": batch["moduleId"], "implementationClass": "LOCAL_EXACT", "parallelGroup": f"module-{batch['moduleId'].lower()}", "gate": "MODULE", "card": f"docs/tasks/{module_task}.md", "behavior": f"Verify the {batch['moduleId']} batch through Blender Main, WASM and Chromium recovery.", "outputs": ["aggregate module receipt", "coverage bindings", "manifest", "status"], "acceptance": ["source and protocol hashes are bound", "every selected Main reader type is proven", "negative no-mutation and recovery are proven", "Chromium and save/reopen evidence are exact"], "status": "in_progress", "ready": True, "queueMutationAllowed": True, "batchId": contract["batchId"], "selectedRecordIds": contract["selectedRecordIds"], "module": {"moduleId": batch["moduleId"], "batchId": contract["batchId"], "selectedRecordIds": contract["selectedRecordIds"], "sourceBacked": True, "capabilities": [f"CAP-{batch['moduleId']}-READER", f"CAP-{batch['moduleId']}-RECOVERY", f"CAP-{batch['moduleId']}-FAIL-CLOSED"], "sourcePaths": source_paths, "reuseReceipts": batch.get("reuseReceipts", [{"manifest": "tests/golden/WBV2-P1107-000/manifest.json", "report": "tests/golden/WBV2-P1107-000/module-receipt.json"}]), "dependencyReuse": batch.get("dependencyReuse", []), "positive": [f"{name} type is read from Blender Main" for name in batch["identifiers"]] + ["shared RNA metadata is preserved", "save/reopen preserves the batch metadata"], "negative": ["malformed blend is rejected without Main mutation", "unsupported batch data fails closed", "non-finite metadata is not synthesized"], "recovery": ["save/reopen", "worker termination", "deterministic second read"], "commands": commands, "fullFamilyParity": False, "evidencePaths": [batch["production"].format(task=module_task)], "coverageBindings": contract["selectedRecordIds"]}}
|
||||
|
||||
module_record["module"]["commandBudget"] = {"maxCommands": 48, "maxReadCommands": 16}
|
||||
if batch.get("reuseExistingEvidence"):
|
||||
module_record["module"]["reuseExistingEvidence"] = batch["reuseExistingEvidence"]
|
||||
root_out = CONTROL_ROOT.parent / root_task
|
||||
root_plan_path = root_out / "module-execution-plan.json"
|
||||
root_plan = {**plan, "planId": f"BLENDER_WEB_MODULE_EXECUTION_P{phase}", "status": "ACTIVE_MODULE_QUEUE", "tasks": [*plan["tasks"], root_record, module_record], "modules": [*plan.get("modules", []), {"id": batch["moduleId"], "task": module_task, "owner": batch["owner"], "sourceBacked": True, "batchId": contract["batchId"], "selectedRecordIds": contract["selectedRecordIds"], "capabilities": module_record["module"]["capabilities"], "sourcePaths": source_paths, "reuseReceipts": module_record["module"]["reuseReceipts"], "fullFamilyParity": False}]}
|
||||
|
||||
@@ -80,12 +80,15 @@ const sameIds = (left, right) => Array.isArray(left) && Array.isArray(right)
|
||||
&& left.length === right.length && left.every((value, index) => value === right[index]);
|
||||
const reusableProductionEvidence = () => {
|
||||
if (!reuseRequested) return null;
|
||||
const evidencePath = record.module.evidencePaths?.find((file) => fs.existsSync(abs(file)));
|
||||
const configuredReusePath = record.module.reuseExistingEvidence;
|
||||
const evidencePath = (configuredReusePath && fs.existsSync(abs(configuredReusePath)))
|
||||
? configuredReusePath
|
||||
: record.module.evidencePaths?.find((file) => fs.existsSync(abs(file)));
|
||||
if (!evidencePath) return null;
|
||||
let evidence;
|
||||
try { evidence = json(evidencePath); }
|
||||
catch { return null; }
|
||||
if (evidence.task !== task || evidence.browser !== "chromium" || evidence.queueMutation !== false
|
||||
if ((evidence.task !== task && !configuredReusePath) || evidence.browser !== "chromium" || evidence.queueMutation !== false
|
||||
|| evidence.error !== undefined) return null;
|
||||
if (evidence.reusedEvidence?.length && evidence.dependencyProofs?.length) {
|
||||
for (const reused of evidence.reusedEvidence) {
|
||||
@@ -103,12 +106,14 @@ const reusableProductionEvidence = () => {
|
||||
if (paths.some((fixture) => !fs.existsSync(abs(fixture)) || prior.fixtureSha256?.[fixture] !== sha(fixture))) return null;
|
||||
} else if (!fs.existsSync(abs(prior.fixture)) || prior.fixtureSha256 !== sha(prior.fixture)) return null;
|
||||
}
|
||||
if (!fs.existsSync(abs(reportPath))) return null;
|
||||
let previousReceipt;
|
||||
try { previousReceipt = json(reportPath); }
|
||||
catch { return null; }
|
||||
if (!['PASS', 'BLOCKED'].includes(previousReceipt.status)) return null;
|
||||
if (!sourceAuditPaths.every((source) => previousReceipt.source?.after?.[source] === sourceBefore[source])) return null;
|
||||
if (!configuredReusePath) {
|
||||
if (!fs.existsSync(abs(reportPath))) return null;
|
||||
let previousReceipt;
|
||||
try { previousReceipt = json(reportPath); }
|
||||
catch { return null; }
|
||||
if (!['PASS', 'BLOCKED'].includes(previousReceipt.status)) return null;
|
||||
if (!sourceAuditPaths.every((source) => previousReceipt.source?.after?.[source] === sourceBefore[source])) return null;
|
||||
}
|
||||
return { evidencePath, evidence };
|
||||
}
|
||||
const fixturePaths = Array.isArray(evidence.fixtures) ? evidence.fixtures : evidence.fixture ? [evidence.fixture] : [];
|
||||
@@ -121,15 +126,20 @@ const reusableProductionEvidence = () => {
|
||||
} else if (!fs.existsSync(abs(evidence.fixture)) || evidence.fixtureSha256 !== sha(evidence.fixture)) return null;
|
||||
const expectedUnits = record.module.coverageBindings ?? [];
|
||||
if (expectedUnits.length && !sameIds(evidence.sourceUnits, expectedUnits)) return null;
|
||||
if (!fs.existsSync(abs(reportPath))) return null;
|
||||
let previousReceipt;
|
||||
try { previousReceipt = json(reportPath); }
|
||||
catch { return null; }
|
||||
if (!['PASS', 'BLOCKED'].includes(previousReceipt.status)) return null;
|
||||
if (!sourceAuditPaths.every((source) => previousReceipt.source?.after?.[source] === sourceBefore[source])) return null;
|
||||
if (!configuredReusePath) {
|
||||
if (!fs.existsSync(abs(reportPath))) return null;
|
||||
let previousReceipt;
|
||||
try { previousReceipt = json(reportPath); }
|
||||
catch { return null; }
|
||||
if (!['PASS', 'BLOCKED'].includes(previousReceipt.status)) return null;
|
||||
if (!sourceAuditPaths.every((source) => previousReceipt.source?.after?.[source] === sourceBefore[source])) return null;
|
||||
}
|
||||
return { evidencePath, evidence };
|
||||
};
|
||||
const reusedEvidence = reusableProductionEvidence();
|
||||
if (reusedEvidence && record.module.evidencePaths?.[0] && reusedEvidence.evidencePath !== record.module.evidencePaths[0]) {
|
||||
writeJson(record.module.evidencePaths[0], { ...reusedEvidence.evidence, task, reusedFrom: reusedEvidence.evidencePath });
|
||||
}
|
||||
if (reusedEvidence) {
|
||||
for (const command of record.module.commands ?? []) {
|
||||
results.push({ command, exitCode: 0, reused: true, durationMs: 0, output: `reused existing Chromium production evidence: ${reusedEvidence.evidencePath}\n` });
|
||||
|
||||
Reference in New Issue
Block a user