314 lines
14 KiB
Python
314 lines
14 KiB
Python
"Compare exported source-only documentation with repository bytes, without services."
|
|
|
|
from __future__ import annotations
|
|
|
|
import argparse
|
|
import copy
|
|
import hashlib
|
|
import json
|
|
from collections import Counter
|
|
from pathlib import Path
|
|
|
|
|
|
def audit(repo: Path, directory: Path) -> dict:
|
|
manifest = json.loads((directory / "manifest.json").read_text(encoding="utf-8"))
|
|
sources = {
|
|
p.relative_to(repo).as_posix()
|
|
for p in (repo / "example_ComfyUI_workflows").rglob("*")
|
|
if p.is_file()
|
|
}
|
|
assert {e["path"] for e in manifest} == sources
|
|
assert len(manifest) == len(sources)
|
|
files = []
|
|
for entry in manifest:
|
|
raw = (repo / entry["path"]).read_bytes()
|
|
source = json.loads(raw.decode("utf-8-sig"))
|
|
doc = json.loads((directory / entry["document"]).read_text(encoding="utf-8"))
|
|
assert hashlib.sha1(raw).hexdigest() == entry["source_sha"]
|
|
assert doc["source_review"]["source_sha"] == entry["source_sha"]
|
|
nodes = {}
|
|
source_links = []
|
|
|
|
def visit(graph, scope=""):
|
|
ui = "nodes" in graph
|
|
rows = graph["nodes"] if ui else [dict(n, id=k) for k, n in graph.items()]
|
|
for node in rows:
|
|
nid = scope + str(node["id"])
|
|
nodes[nid] = (
|
|
node["widgets_values"]
|
|
if ui and "widgets_values" in node
|
|
else ([] if ui else node["inputs"])
|
|
)
|
|
if not ui:
|
|
source_links.extend(
|
|
{
|
|
"scope": scope,
|
|
"node_id": nid,
|
|
"field": key,
|
|
"upstream_node_id": str(value[0]),
|
|
"output_slot": value[1],
|
|
}
|
|
for key, value in node["inputs"].items()
|
|
if isinstance(value, list) and len(value) == 2
|
|
)
|
|
source_links.extend({"scope": scope, "link": link} for link in graph.get("links", []))
|
|
for sub in graph.get("definitions", {}).get("subgraphs", []):
|
|
visit(sub, scope + sub["id"] + "/")
|
|
|
|
visit(source)
|
|
assert doc["source_links"] == source_links
|
|
assert {n["node_id"]: n["source_values"] for n in doc["source_nodes"]} == nodes
|
|
for param in doc["params"]:
|
|
assert param["default"] == nodes[param["node_id"]][param["field"]]
|
|
assert param["unit"] is None
|
|
assert param["min"] is None and param["max"] is None
|
|
for note in doc["source_notes"]:
|
|
assert note["content"] == nodes[note["node_id"]]
|
|
checked_fields = doc["source_review"].get("reviewed_named_fields", [])
|
|
assert len({(f["node_id"], f["field"]) for f in checked_fields}) == len(checked_fields)
|
|
for field in checked_fields:
|
|
assert field["source_value"] == nodes[field["node_id"]][field["field"]]
|
|
fourth = doc["source_review"].get("fourth_review")
|
|
if fourth:
|
|
assert fourth["operation_check"] in doc["steps"]
|
|
assert {(f["node_id"], f["field"]) for f in checked_fields} == {
|
|
(p["node_id"], p["field"]) for p in doc["params"]
|
|
}
|
|
semantic = doc["source_review"].get("semantic_review")
|
|
if semantic:
|
|
assert semantic["batch"] == "HEL-892-third"
|
|
assert semantic["operation_check"] in doc["steps"]
|
|
assert semantic["conclusions"] and semantic["unknowns"]
|
|
assert {(f["node_id"], f["field"]) for f in checked_fields} == {
|
|
(p["node_id"], p["field"]) for p in doc["params"]
|
|
}
|
|
for kind in ("nodes", "notes"):
|
|
for ref in semantic["evidence_refs"][kind]:
|
|
key, index = ref["pointer"].strip("/").split("/")
|
|
assert doc[key][int(index)]["node_id"] == ref["node_id"]
|
|
assert semantic["mechanical_inventory"] == {
|
|
"named_value_count": len(doc["params"]),
|
|
"note_count": len(doc["source_notes"]),
|
|
"link_count": len(doc["source_links"]),
|
|
"position_widget_node_count": len(
|
|
doc["source_review"]["evidence"]["unnamed_widget_node_ids"]
|
|
),
|
|
}
|
|
if not checked_fields:
|
|
assert semantic["named_parameter_finding"] == ("未发现可由源文件确认的具名参数")
|
|
assert doc["status"] == {"imported": True, "doc_done": False, "exec_verified": False}
|
|
assert doc["source_review"]["complete"] is False
|
|
files.append(
|
|
{
|
|
"path": entry["path"],
|
|
"source_sha": entry["source_sha"],
|
|
"document": entry["document"],
|
|
"state": entry["state"],
|
|
"manual_scope": doc["source_review"]["scope"],
|
|
"purpose": doc["purpose"],
|
|
"source_specific_instruction": doc["steps"][0],
|
|
"source_evidence": doc["source_review"]["evidence"],
|
|
"unconfirmed": doc["source_review"]["unconfirmed"],
|
|
"reviewed_named_fields": checked_fields,
|
|
"semantic_review": semantic,
|
|
}
|
|
)
|
|
return {
|
|
"files": files,
|
|
"total": len(files),
|
|
"source_structure_purpose_partially_reviewed": len(files),
|
|
"full_documents_completed": 0,
|
|
"files_with_selected_named_fields_reviewed": sum(
|
|
bool(f["reviewed_named_fields"]) for f in files
|
|
),
|
|
"selected_named_fields_reviewed": sum(len(f["reviewed_named_fields"]) for f in files),
|
|
"real_executions": 0,
|
|
"states": dict(Counter(f["state"] for f in files)),
|
|
"named_values_preserved": sum(f["source_evidence"]["named_value_count"] for f in files),
|
|
"unnamed_widget_nodes": sum(
|
|
len(f["source_evidence"]["unnamed_widget_node_ids"]) for f in files
|
|
),
|
|
"semantic_batch_files": sum(bool(f["semantic_review"]) for f in files),
|
|
"semantic_batch_without_named_fields": sum(
|
|
bool(f["semantic_review"]) and not f["reviewed_named_fields"] for f in files
|
|
),
|
|
"semantic_batch_named_fields": sum(
|
|
len(f["reviewed_named_fields"]) for f in files if f["semantic_review"]
|
|
),
|
|
"named_values_without_manual_field_review": sum(
|
|
f["source_evidence"]["named_value_count"] - len(f["reviewed_named_fields"])
|
|
for f in files
|
|
),
|
|
"named_values_without_verified_units_ranges_effects": sum(
|
|
f["source_evidence"]["named_value_count"] for f in files
|
|
),
|
|
"scope_limit": "人工结论与机械原值清单分开;源语义局部审校非完整说明定稿",
|
|
}
|
|
|
|
|
|
def audit_partition(repo: Path, report: dict, partition_path: Path) -> dict:
|
|
partition = json.loads(partition_path.read_text(encoding="utf-8"))
|
|
prior, added = partition["prior_16"], partition["new_140"]
|
|
prior_paths = {e["path"] for e in prior}
|
|
added_paths = {e["path"] for e in added}
|
|
assert len(prior) == len(prior_paths) == 16
|
|
assert len(added) == len(added_paths) == 140
|
|
assert prior_paths.isdisjoint(added_paths)
|
|
assert prior_paths | added_paths == {f["path"] for f in report["files"]}
|
|
reviews = json.loads(
|
|
(repo / ("calliope-backend/src/calliope/comfyui/source_reviews.json")).read_text(
|
|
encoding="utf-8"
|
|
)
|
|
)
|
|
for entry in prior:
|
|
# The third-batch snapshot remains immutable historical evidence.
|
|
# Reconstruct its exact payload, excluding only the explicit batch-four delta.
|
|
review = copy.deepcopy(reviews[entry["path"]])
|
|
fourth = review.pop("fourth_review", None)
|
|
if fourth:
|
|
fourth_fields = {tuple(k) for k in fourth["fields"]}
|
|
review["reviewed_named_fields"] = [
|
|
f
|
|
for f in review["reviewed_named_fields"]
|
|
if (f["node_id"], f["field"]) not in fourth_fields
|
|
]
|
|
assert review["reviewed_named_fields"] == entry["reviewed_named_fields"]
|
|
assert (
|
|
hashlib.sha256(
|
|
json.dumps(review, ensure_ascii=False, sort_keys=True).encode()
|
|
).hexdigest()
|
|
== entry["review_sha256"]
|
|
)
|
|
by_path = {f["path"]: f for f in report["files"]}
|
|
for entry in prior + added:
|
|
assert entry["source_sha"] == by_path[entry["path"]]["source_sha"]
|
|
assert all(by_path[p]["semantic_review"] for p in added_paths)
|
|
assert all(not by_path[p]["semantic_review"] for p in prior_paths)
|
|
return {
|
|
"prior_files": 16,
|
|
"prior_named_fields": 42,
|
|
"new_files": 140,
|
|
"new_named_fields": report["semantic_batch_named_fields"],
|
|
"new_without_named_fields": report["semantic_batch_without_named_fields"],
|
|
"disjoint_complete_sha_verified": True,
|
|
"prior_review_payloads_unchanged": True,
|
|
}
|
|
|
|
|
|
def audit_fourth(repo: Path, report: dict, partition_path: Path) -> dict:
|
|
partition = json.loads(partition_path.read_text(encoding="utf-8"))
|
|
mechanical = {tuple(k) for k in partition["mechanical_1227"]}
|
|
prior = {tuple(k) for k in partition["prior_542"]}
|
|
added = {tuple(k) for k in partition["added_685"]}
|
|
assert len(mechanical) == len(partition["mechanical_1227"]) == 1227
|
|
assert len(prior) == len(partition["prior_542"]) == 542
|
|
assert len(added) == len(partition["added_685"]) == 685
|
|
assert prior.isdisjoint(added) and prior | added == mechanical
|
|
assert len({k[0] for k in added}) == 16
|
|
reviews = json.loads(
|
|
(repo / ("calliope-backend/src/calliope/comfyui/source_reviews.json")).read_text(
|
|
encoding="utf-8"
|
|
)
|
|
)
|
|
current_ids, checked_added, categories = set(), set(), Counter()
|
|
for path, original in reviews.items():
|
|
review = copy.deepcopy(original)
|
|
fourth = review.pop("fourth_review", None)
|
|
fields = review["reviewed_named_fields"]
|
|
current_ids.update((path, f["node_id"], f["field"]) for f in fields)
|
|
if fourth:
|
|
raw = (repo / path).read_bytes()
|
|
source = json.loads(raw.decode("utf-8-sig"))
|
|
assert hashlib.sha1(raw).hexdigest() == review["source_sha"]
|
|
new_fields = [f for f in fields if (path, f["node_id"], f["field"]) in added]
|
|
assert {(f["node_id"], f["field"]) for f in new_fields} == {
|
|
tuple(k) for k in fourth["fields"]
|
|
}
|
|
for f in new_fields:
|
|
assert f["source_path"] == path and f["source_sha"] == review["source_sha"]
|
|
value = source
|
|
for part in f["source_pointer"].strip("/").split("/"):
|
|
part = part.replace("~1", "/").replace("~0", "~")
|
|
value = value[int(part)] if isinstance(value, list) else value[part]
|
|
assert json.dumps(value, sort_keys=True) == json.dumps(
|
|
f["source_value"], sort_keys=True
|
|
)
|
|
nid = f["node_id"]
|
|
if "nodes" in source:
|
|
node = next(n for n in source["nodes"] if str(n["id"]) == nid)
|
|
assert f["class_type"] == node["type"]
|
|
for direction, endpoint in (("incoming", 3), ("outgoing", 1)):
|
|
actual = [
|
|
{"pointer": f"/links/{i}", "link": link}
|
|
for i, link in enumerate(source.get("links", []))
|
|
if str(link[endpoint]) == nid
|
|
]
|
|
assert f["connections"][direction] == actual
|
|
else:
|
|
assert f["class_type"] == source[nid]["class_type"]
|
|
assert f["connections"]["incoming"] == [
|
|
{"field": k, "link": v} for k, v in source[nid]["inputs"].items()
|
|
if isinstance(v, list)
|
|
]
|
|
assert f["connections"]["outgoing"] == [
|
|
{"node_id": target, "field": k, "link": v}
|
|
for target, n in source.items() for k, v in n["inputs"].items()
|
|
if isinstance(v, list) and len(v) == 2 and v[0] == nid
|
|
]
|
|
assert f["category"] in {
|
|
"runtime_parameter",
|
|
"frontend_state",
|
|
"current_value_only",
|
|
"semantic_unknown",
|
|
}
|
|
assert f["label"] and f["operation_check"] and f["connections"]
|
|
categories[f["category"]] += 1
|
|
checked_added.add((path, f["node_id"], f["field"]))
|
|
assert fourth["classification_counts"] == dict(
|
|
Counter(f["category"] for f in new_fields)
|
|
)
|
|
review["reviewed_named_fields"] = [
|
|
f for f in fields if (path, f["node_id"], f["field"]) not in added
|
|
]
|
|
assert (
|
|
hashlib.sha256(
|
|
json.dumps(review, ensure_ascii=False, sort_keys=True).encode()
|
|
).hexdigest()
|
|
== partition["prior_review_sha256"][path]
|
|
)
|
|
assert current_ids == mechanical and checked_added == added
|
|
assert report["selected_named_fields_reviewed"] == 1227
|
|
return {
|
|
"mechanical_values_checked": 1227,
|
|
"prior_reviewed": 542,
|
|
"added_reviewed": 685,
|
|
"new_files": 16,
|
|
"classification_counts_new": dict(categories),
|
|
"duplicates": 0,
|
|
"prior_156_payloads_preserved": True,
|
|
"added_fields_with_runtime_source_use": categories["runtime_parameter"],
|
|
"added_fields_without_confirmed_runtime_meaning": 685 - categories["runtime_parameter"],
|
|
"verified_units_ranges_effects": 0,
|
|
"full_documents_completed": 0,
|
|
"real_executions": 0,
|
|
}
|
|
|
|
|
|
if __name__ == "__main__":
|
|
parser = argparse.ArgumentParser(description=__doc__)
|
|
parser.add_argument("--repo", type=Path, required=True)
|
|
parser.add_argument("--documents", type=Path, required=True)
|
|
parser.add_argument("--output", type=Path, required=True)
|
|
parser.add_argument("--partition", type=Path)
|
|
parser.add_argument("--fourth-partition", type=Path)
|
|
args = parser.parse_args()
|
|
report = audit(args.repo, args.documents)
|
|
if args.partition:
|
|
report["partition"] = audit_partition(args.repo, report, args.partition)
|
|
if args.fourth_partition:
|
|
report["fourth_partition"] = audit_fourth(args.repo, report, args.fourth_partition)
|
|
args.output.write_text(
|
|
json.dumps(report, ensure_ascii=False, indent=2) + "\n", encoding="utf-8"
|
|
)
|
|
print(json.dumps({k: v for k, v in report.items() if k != "files"}, ensure_ascii=False))
|