From 641daa79c4d40f7eb6947941834ee985e9fe919c Mon Sep 17 00:00:00 2001 From: andy-zen-dev Date: Mon, 21 Sep 2026 02:52:25 +0300 Subject: [PATCH 01/11] SCRIP-CORPUS-045 add Darwin fail-closed render profile --- scriptorium/darwin_render_profile.py | 299 +++++++++++++++++++++++++++ 1 file changed, 299 insertions(+) create mode 100644 scriptorium/darwin_render_profile.py diff --git a/scriptorium/darwin_render_profile.py b/scriptorium/darwin_render_profile.py new file mode 100644 index 00000000..8771a75e --- /dev/null +++ b/scriptorium/darwin_render_profile.py @@ -0,0 +1,299 @@ +"""Freeze a fail-closed Page-rendering decision profile for Darwin/Rachinsky 1864. + +The profile consumes only the committed source-free exact-388 rendering-surface freeze. +It classifies every observed tag/template shape and deliberately refuses to invent +Wikisource template, reference or math expansion semantics. This is a profile boundary, +not a renderer: page prose, inter-page composition, body counts/digests and corpus/ +FantLab gates stay closed until unresolved semantic classes are independently frozen. +""" + +from __future__ import annotations + +import argparse +from hashlib import sha256 +import json +from pathlib import Path +from typing import Mapping, Sequence + +from .darwin_render_surface_freeze import validate_freeze_manifest + +PROFILE_VERSION = "scriptorium-darwin-page-render-profile-v1" +PROFILE_STATUS = "fail_closed_profile_frozen_template_reference_math_semantics_pending" +CANDIDATE_ID = "darwin-origin-species-rachinsky-1864-ru" +SURFACE_SCHEMA = "scriptorium-darwin-render-surface-freeze-v1" +SURFACE_STATUS = "surface_inventory_frozen_renderer_unfrozen" +EXPECTED_DEPENDENCY_COUNT = 388 +EXPECTED_INDEX_SHA256 = "a1b1011095dc84ab0a5cd3ef45b2a46ce99588a0ef9cf507c1f711c925ba86dd" +EXPECTED_SURFACE_FREEZE_SHA256 = "e5ae625fef6afb2e890de990e1ef3b9e108a409a2e0ea2136947c2c3e1cea44d" + +TAG_DECISIONS: dict[tuple[str, str], tuple[str, str]] = { + ("big", "close"): ("preserve_inner_text_container", "defined"), + ("big", "open"): ("preserve_inner_text_container", "defined"), + ("div", "close"): ("preserve_inner_text_container", "defined"), + ("div", "open"): ("preserve_inner_text_container", "defined"), + ("math", "close"): ("provider_math_semantics_required", "unresolved"), + ("math", "open"): ("provider_math_semantics_required", "unresolved"), + ("noinclude", "close"): ("strip_nontranscluded_region", "defined"), + ("noinclude", "open"): ("strip_nontranscluded_region", "defined"), + ("pagequality", "self_closing"): ("drop_proofread_metadata", "defined"), + ("ref", "close"): ("provider_reference_semantics_required", "unresolved"), + ("ref", "open"): ("provider_reference_semantics_required", "unresolved"), + ("references", "self_closing"): ("provider_reference_semantics_required", "unresolved"), + ("small", "close"): ("preserve_inner_text_container", "defined"), + ("small", "open"): ("preserve_inner_text_container", "defined"), + ("sub", "close"): ("preserve_inner_text_container", "defined"), + ("sub", "open"): ("preserve_inner_text_container", "defined"), + ("sup", "close"): ("preserve_inner_text_container", "defined"), + ("sup", "open"): ("preserve_inner_text_container", "defined"), +} + +_TEMPLATE_SHAPES = ( + ("=", 0, 0), + ("Lang", 2, 0), + ("^", 0, 0), + ("^", 1, 0), + ("dotted TOC", 3, 0), + ("fsp", 0, 0), + ("inline float", 4, 2), + ("lang", 2, 0), + ("lang-en", 1, 0), + ("lang-la", 1, 0), + ("nobr", 1, 0), + ("noindent", 0, 0), + ("noindent", 1, 0), + ("nop", 0, 0), + ("psp", 0, 0), + ("razr2", 1, 0), + ("right", 1, 0), + ("roman", 1, 0), + ("sc", 1, 0), + ("ВАР", 2, 0), + ("ВАР2", 2, 0), + ("Градус", 1, 0), + ("Перенос", 2, 0), + ("Перенос", 2, 1), + ("Перенос2", 2, 0), + ("ПримВТ", 0, 0), + ("Так в тексте", 1, 0), + ("акут", 0, 0), + ("во", 1, 1), + ("гравис", 0, 0), + ("дробь", 2, 0), + ("мби", 3, 0), + ("нп", 0, 0), + ("опечатка", 2, 0), + ("опечатка2", 2, 0), + ("разделитель", 1, 0), + ("ц", 1, 0), + ("ё", 0, 0), +) + +TEMPLATE_DECISIONS: dict[tuple[str, int, int], tuple[str, str]] = { + shape: ( + "inter_page_semantics_required" if shape[0] == "nop" else "provider_template_semantics_required", + "unresolved", + ) + for shape in _TEMPLATE_SHAPES +} + + +def _canonical_json(value: object) -> str: + return json.dumps(value, ensure_ascii=False, sort_keys=True, separators=(",", ":")) + + +def _sha256_canonical(value: object) -> str: + return sha256(_canonical_json(value).encode("utf-8")).hexdigest() + + +def _shape_key_tag(row: Mapping[str, object]) -> tuple[str, str]: + name = row.get("name") + kind = row.get("kind") + count = row.get("count") + if not isinstance(name, str) or not isinstance(kind, str) or not isinstance(count, int) or count < 1: + raise ValueError("invalid tag-shape row") + return name, kind + + +def _shape_key_template(row: Mapping[str, object]) -> tuple[str, int, int]: + name = row.get("name") + positional = row.get("positional") + named = row.get("named") + count = row.get("count") + if ( + not isinstance(name, str) + or not isinstance(positional, int) + or not isinstance(named, int) + or not isinstance(count, int) + or positional < 0 + or named < 0 + or count < 1 + ): + raise ValueError("invalid template-shape row") + return name, positional, named + + +def _validate_surface_identity(surface: Mapping[str, object]) -> None: + validate_freeze_manifest(surface) + expected = { + "schema_version": SURFACE_SCHEMA, + "status": SURFACE_STATUS, + "candidate_id": CANDIDATE_ID, + "literary_dependency_count": EXPECTED_DEPENDENCY_COUNT, + "source_revision_index_sha256": EXPECTED_INDEX_SHA256, + "freeze_manifest_sha256": EXPECTED_SURFACE_FREEZE_SHA256, + "identity_replay_match": True, + "source_text_included": False, + "rendering_profile_frozen": False, + "literary_body_count_and_digests_frozen": False, + "minimum_300k_proved": False, + "admitted_for_calibration": False, + "fantlab_source_edition_match": "unknown", + "diagnostic_ready": False, + "m2_parity_admissible": False, + } + for key, value in expected.items(): + if surface.get(key) != value: + raise ValueError(f"render-surface identity/gate drift: {key}") + + +def build_profile(surface: Mapping[str, object]) -> dict[str, object]: + """Build the deterministic source-free profile, failing closed on surface drift.""" + + _validate_surface_identity(surface) + tag_rows = surface.get("tag_shapes") + template_rows = surface.get("template_shapes") + if not isinstance(tag_rows, list) or not isinstance(template_rows, list): + raise ValueError("render surface is missing shape inventories") + + observed_tags = {_shape_key_tag(row) for row in tag_rows if isinstance(row, dict)} + observed_templates = {_shape_key_template(row) for row in template_rows if isinstance(row, dict)} + if len(observed_tags) != len(tag_rows) or len(observed_templates) != len(template_rows): + raise ValueError("duplicate or invalid render-surface shape rows") + if observed_tags != set(TAG_DECISIONS): + missing = sorted(observed_tags - set(TAG_DECISIONS)) + stale = sorted(set(TAG_DECISIONS) - observed_tags) + raise ValueError(f"tag decision coverage drift: unclassified={missing!r} stale={stale!r}") + if observed_templates != set(TEMPLATE_DECISIONS): + missing = sorted(observed_templates - set(TEMPLATE_DECISIONS)) + stale = sorted(set(TEMPLATE_DECISIONS) - observed_templates) + raise ValueError(f"template decision coverage drift: unclassified={missing!r} stale={stale!r}") + + tag_rules: list[dict[str, object]] = [] + for row in tag_rows: + assert isinstance(row, dict) + key = _shape_key_tag(row) + handling, status = TAG_DECISIONS[key] + tag_rules.append({ + "name": key[0], + "kind": key[1], + "count": int(row["count"]), + "handling": handling, + "semantic_status": status, + }) + + template_rules: list[dict[str, object]] = [] + for row in template_rows: + assert isinstance(row, dict) + key = _shape_key_template(row) + handling, status = TEMPLATE_DECISIONS[key] + template_rules.append({ + "name": key[0], + "positional": key[1], + "named": key[2], + "count": int(row["count"]), + "handling": handling, + "semantic_status": status, + }) + + tag_rules.sort(key=lambda row: (str(row["name"]), str(row["kind"]))) + template_rules.sort(key=lambda row: (str(row["name"]), int(row["positional"]), int(row["named"]))) + unresolved_tag_rules = [row for row in tag_rules if row["semantic_status"] == "unresolved"] + unresolved_template_rules = [row for row in template_rules if row["semantic_status"] == "unresolved"] + + profile: dict[str, object] = { + "profile_version": PROFILE_VERSION, + "status": PROFILE_STATUS, + "candidate_id": CANDIDATE_ID, + "source_text_included": False, + "source_surface": { + "schema_version": SURFACE_SCHEMA, + "literary_dependency_count": EXPECTED_DEPENDENCY_COUNT, + "source_revision_index_sha256": EXPECTED_INDEX_SHA256, + "freeze_manifest_sha256": EXPECTED_SURFACE_FREEZE_SHA256, + }, + "extraction_policy": { + "exact_revision_identity_required_before_content_fetch": True, + "comments": "drop", + "noinclude_regions": "drop", + "unknown_tag_shape": "fail_closed", + "unknown_template_shape": "fail_closed", + "template_expansion_policy": "do_not_guess; freeze provider/local semantics before literary-body rendering", + "math_policy": "do_not_guess; freeze provider math-to-text semantics before literary-body rendering", + "reference_policy": "do_not_guess; freeze provider reference inclusion/placement semantics before literary-body rendering", + "persist_source_wikitext": False, + "persist_rendered_prose": False, + }, + "tag_rules": tag_rules, + "template_rules": template_rules, + "coverage": { + "observed_tag_shape_count": len(tag_rules), + "observed_tag_token_count": sum(int(row["count"]) for row in tag_rules), + "observed_template_shape_count": len(template_rules), + "observed_template_invocation_count": sum(int(row["count"]) for row in template_rules), + "unresolved_tag_shape_count": len(unresolved_tag_rules), + "unresolved_tag_token_count": sum(int(row["count"]) for row in unresolved_tag_rules), + "unresolved_template_shape_count": len(unresolved_template_rules), + "unresolved_template_invocation_count": sum(int(row["count"]) for row in unresolved_template_rules), + "all_observed_shapes_classified": True, + }, + "rendering_profile_frozen": True, + "renderer_semantics_complete": False, + "renderer_implementation_ready": False, + "rendering_equivalence_claimed": False, + "inter_page_composition_frozen": False, + "literary_body_count_and_digests_frozen": False, + "minimum_300k_proved": False, + "admitted_for_calibration": False, + "fantlab_source_edition_match": "unknown", + "diagnostic_ready": False, + "m2_parity_admissible": False, + } + profile["profile_sha256"] = _sha256_canonical(profile) + return profile + + +def validate_profile(profile: Mapping[str, object], surface: Mapping[str, object]) -> None: + expected = build_profile(surface) + if dict(profile) != expected: + raise ValueError("Darwin/Rachinsky rendering profile drift") + + +def build_profile_from_path(surface_path: Path) -> dict[str, object]: + value = json.loads(surface_path.read_text(encoding="utf-8")) + if not isinstance(value, dict): + raise ValueError("render-surface JSON must be an object") + return build_profile(value) + + +def main(argv: Sequence[str] | None = None) -> int: + parser = argparse.ArgumentParser(description=__doc__) + parser.add_argument("--surface", type=Path, required=True) + parser.add_argument("--output", type=Path, required=True) + args = parser.parse_args(argv) + profile = build_profile_from_path(args.surface) + args.output.parent.mkdir(parents=True, exist_ok=True) + args.output.write_text( + json.dumps(profile, ensure_ascii=False, indent=2, sort_keys=True) + "\n", + encoding="utf-8", + ) + print(json.dumps({ + "profile_sha256": profile["profile_sha256"], + "renderer_semantics_complete": profile["renderer_semantics_complete"], + "unresolved_template_shape_count": profile["coverage"]["unresolved_template_shape_count"], + "unresolved_tag_shape_count": profile["coverage"]["unresolved_tag_shape_count"], + }, sort_keys=True)) + return 0 + + +if __name__ == "__main__": + raise SystemExit(main()) From 8a6498c381c5a222e26157789bce36c96ba9cc18 Mon Sep 17 00:00:00 2001 From: andy-zen-dev Date: Mon, 21 Sep 2026 02:52:59 +0300 Subject: [PATCH 02/11] SCRIP-CORPUS-045 freeze Darwin render profile artifact --- ...cies-rachinsky-1864-ru.render-profile.json | 107 ++++++++++++++++++ 1 file changed, 107 insertions(+) create mode 100644 corpus/candidates/source-edition-traces/darwin-origin-species-rachinsky-1864-ru.render-profile.json diff --git a/corpus/candidates/source-edition-traces/darwin-origin-species-rachinsky-1864-ru.render-profile.json b/corpus/candidates/source-edition-traces/darwin-origin-species-rachinsky-1864-ru.render-profile.json new file mode 100644 index 00000000..160d43b9 --- /dev/null +++ b/corpus/candidates/source-edition-traces/darwin-origin-species-rachinsky-1864-ru.render-profile.json @@ -0,0 +1,107 @@ +{ + "admitted_for_calibration": false, + "candidate_id": "darwin-origin-species-rachinsky-1864-ru", + "coverage": { + "all_observed_shapes_classified": true, + "observed_tag_shape_count": 18, + "observed_tag_token_count": 5164, + "observed_template_invocation_count": 4583, + "observed_template_shape_count": 38, + "unresolved_tag_shape_count": 5, + "unresolved_tag_token_count": 518, + "unresolved_template_invocation_count": 4583, + "unresolved_template_shape_count": 38 + }, + "diagnostic_ready": false, + "extraction_policy": { + "comments": "drop", + "exact_revision_identity_required_before_content_fetch": true, + "math_policy": "do_not_guess; freeze provider math-to-text semantics before literary-body rendering", + "noinclude_regions": "drop", + "persist_rendered_prose": false, + "persist_source_wikitext": false, + "reference_policy": "do_not_guess; freeze provider reference inclusion/placement semantics before literary-body rendering", + "template_expansion_policy": "do_not_guess; freeze provider/local semantics before literary-body rendering", + "unknown_tag_shape": "fail_closed", + "unknown_template_shape": "fail_closed" + }, + "fantlab_source_edition_match": "unknown", + "inter_page_composition_frozen": false, + "literary_body_count_and_digests_frozen": false, + "m2_parity_admissible": false, + "minimum_300k_proved": false, + "profile_sha256": "2ebcfea51282505c28824f54f6a1795bd7e87e2f958ac59f5c843e68c2066f3d", + "profile_version": "scriptorium-darwin-page-render-profile-v1", + "renderer_implementation_ready": false, + "renderer_semantics_complete": false, + "rendering_equivalence_claimed": false, + "rendering_profile_frozen": true, + "source_surface": { + "freeze_manifest_sha256": "e5ae625fef6afb2e890de990e1ef3b9e108a409a2e0ea2136947c2c3e1cea44d", + "literary_dependency_count": 388, + "schema_version": "scriptorium-darwin-render-surface-freeze-v1", + "source_revision_index_sha256": "a1b1011095dc84ab0a5cd3ef45b2a46ce99588a0ef9cf507c1f711c925ba86dd" + }, + "source_text_included": false, + "status": "fail_closed_profile_frozen_template_reference_math_semantics_pending", + "tag_rules": [ + {"count": 64, "handling": "preserve_inner_text_container", "kind": "close", "name": "big", "semantic_status": "defined"}, + {"count": 64, "handling": "preserve_inner_text_container", "kind": "open", "name": "big", "semantic_status": "defined"}, + {"count": 386, "handling": "preserve_inner_text_container", "kind": "close", "name": "div", "semantic_status": "defined"}, + {"count": 388, "handling": "preserve_inner_text_container", "kind": "open", "name": "div", "semantic_status": "defined"}, + {"count": 26, "handling": "provider_math_semantics_required", "kind": "close", "name": "math", "semantic_status": "unresolved"}, + {"count": 26, "handling": "provider_math_semantics_required", "kind": "open", "name": "math", "semantic_status": "unresolved"}, + {"count": 776, "handling": "strip_nontranscluded_region", "kind": "close", "name": "noinclude", "semantic_status": "defined"}, + {"count": 776, "handling": "strip_nontranscluded_region", "kind": "open", "name": "noinclude", "semantic_status": "defined"}, + {"count": 388, "handling": "drop_proofread_metadata", "kind": "self_closing", "name": "pagequality", "semantic_status": "defined"}, + {"count": 39, "handling": "provider_reference_semantics_required", "kind": "close", "name": "ref", "semantic_status": "unresolved"}, + {"count": 39, "handling": "provider_reference_semantics_required", "kind": "open", "name": "ref", "semantic_status": "unresolved"}, + {"count": 388, "handling": "provider_reference_semantics_required", "kind": "self_closing", "name": "references", "semantic_status": "unresolved"}, + {"count": 808, "handling": "preserve_inner_text_container", "kind": "close", "name": "small", "semantic_status": "defined"}, + {"count": 808, "handling": "preserve_inner_text_container", "kind": "open", "name": "small", "semantic_status": "defined"}, + {"count": 2, "handling": "preserve_inner_text_container", "kind": "close", "name": "sub", "semantic_status": "defined"}, + {"count": 2, "handling": "preserve_inner_text_container", "kind": "open", "name": "sub", "semantic_status": "defined"}, + {"count": 92, "handling": "preserve_inner_text_container", "kind": "close", "name": "sup", "semantic_status": "defined"}, + {"count": 92, "handling": "preserve_inner_text_container", "kind": "open", "name": "sup", "semantic_status": "defined"} + ], + "template_rules": [ + {"count": 2, "handling": "provider_template_semantics_required", "name": "=", "named": 0, "positional": 0, "semantic_status": "unresolved"}, + {"count": 255, "handling": "provider_template_semantics_required", "name": "Lang", "named": 0, "positional": 2, "semantic_status": "unresolved"}, + {"count": 122, "handling": "provider_template_semantics_required", "name": "^", "named": 0, "positional": 0, "semantic_status": "unresolved"}, + {"count": 90, "handling": "provider_template_semantics_required", "name": "^", "named": 0, "positional": 1, "semantic_status": "unresolved"}, + {"count": 6, "handling": "provider_template_semantics_required", "name": "dotted TOC", "named": 0, "positional": 3, "semantic_status": "unresolved"}, + {"count": 7, "handling": "provider_template_semantics_required", "name": "fsp", "named": 0, "positional": 0, "semantic_status": "unresolved"}, + {"count": 2, "handling": "provider_template_semantics_required", "name": "inline float", "named": 2, "positional": 4, "semantic_status": "unresolved"}, + {"count": 179, "handling": "provider_template_semantics_required", "name": "lang", "named": 0, "positional": 2, "semantic_status": "unresolved"}, + {"count": 2, "handling": "provider_template_semantics_required", "name": "lang-en", "named": 0, "positional": 1, "semantic_status": "unresolved"}, + {"count": 1, "handling": "provider_template_semantics_required", "name": "lang-la", "named": 0, "positional": 1, "semantic_status": "unresolved"}, + {"count": 22, "handling": "provider_template_semantics_required", "name": "nobr", "named": 0, "positional": 1, "semantic_status": "unresolved"}, + {"count": 14, "handling": "provider_template_semantics_required", "name": "noindent", "named": 0, "positional": 0, "semantic_status": "unresolved"}, + {"count": 14, "handling": "provider_template_semantics_required", "name": "noindent", "named": 0, "positional": 1, "semantic_status": "unresolved"}, + {"count": 4, "handling": "inter_page_semantics_required", "name": "nop", "named": 0, "positional": 0, "semantic_status": "unresolved"}, + {"count": 13, "handling": "provider_template_semantics_required", "name": "psp", "named": 0, "positional": 0, "semantic_status": "unresolved"}, + {"count": 8, "handling": "provider_template_semantics_required", "name": "razr2", "named": 0, "positional": 1, "semantic_status": "unresolved"}, + {"count": 2, "handling": "provider_template_semantics_required", "name": "right", "named": 0, "positional": 1, "semantic_status": "unresolved"}, + {"count": 30, "handling": "provider_template_semantics_required", "name": "roman", "named": 0, "positional": 1, "semantic_status": "unresolved"}, + {"count": 52, "handling": "provider_template_semantics_required", "name": "sc", "named": 0, "positional": 1, "semantic_status": "unresolved"}, + {"count": 388, "handling": "provider_template_semantics_required", "name": "ВАР", "named": 0, "positional": 2, "semantic_status": "unresolved"}, + {"count": 2, "handling": "provider_template_semantics_required", "name": "ВАР2", "named": 0, "positional": 2, "semantic_status": "unresolved"}, + {"count": 21, "handling": "provider_template_semantics_required", "name": "Градус", "named": 0, "positional": 1, "semantic_status": "unresolved"}, + {"count": 253, "handling": "provider_template_semantics_required", "name": "Перенос", "named": 0, "positional": 2, "semantic_status": "unresolved"}, + {"count": 2, "handling": "provider_template_semantics_required", "name": "Перенос", "named": 1, "positional": 2, "semantic_status": "unresolved"}, + {"count": 259, "handling": "provider_template_semantics_required", "name": "Перенос2", "named": 0, "positional": 2, "semantic_status": "unresolved"}, + {"count": 15, "handling": "provider_template_semantics_required", "name": "ПримВТ", "named": 0, "positional": 0, "semantic_status": "unresolved"}, + {"count": 1, "handling": "provider_template_semantics_required", "name": "Так в тексте", "named": 0, "positional": 1, "semantic_status": "unresolved"}, + {"count": 46, "handling": "provider_template_semantics_required", "name": "акут", "named": 0, "positional": 0, "semantic_status": "unresolved"}, + {"count": 28, "handling": "provider_template_semantics_required", "name": "во", "named": 1, "positional": 1, "semantic_status": "unresolved"}, + {"count": 108, "handling": "provider_template_semantics_required", "name": "гравис", "named": 0, "positional": 0, "semantic_status": "unresolved"}, + {"count": 16, "handling": "provider_template_semantics_required", "name": "дробь", "named": 0, "positional": 2, "semantic_status": "unresolved"}, + {"count": 76, "handling": "provider_template_semantics_required", "name": "мби", "named": 0, "positional": 3, "semantic_status": "unresolved"}, + {"count": 99, "handling": "provider_template_semantics_required", "name": "нп", "named": 0, "positional": 0, "semantic_status": "unresolved"}, + {"count": 3, "handling": "provider_template_semantics_required", "name": "опечатка", "named": 0, "positional": 2, "semantic_status": "unresolved"}, + {"count": 135, "handling": "provider_template_semantics_required", "name": "опечатка2", "named": 0, "positional": 2, "semantic_status": "unresolved"}, + {"count": 17, "handling": "provider_template_semantics_required", "name": "разделитель", "named": 0, "positional": 1, "semantic_status": "unresolved"}, + {"count": 62, "handling": "provider_template_semantics_required", "name": "ц", "named": 0, "positional": 1, "semantic_status": "unresolved"}, + {"count": 2227, "handling": "provider_template_semantics_required", "name": "ё", "named": 0, "positional": 0, "semantic_status": "unresolved"} + ] +} From 577cba32696f5661dbf8f4d0fe54f5a9f266d3d4 Mon Sep 17 00:00:00 2001 From: andy-zen-dev Date: Mon, 21 Sep 2026 02:53:12 +0300 Subject: [PATCH 03/11] SCRIP-CORPUS-045 test Darwin fail-closed render profile --- tests/test_darwin_render_profile.py | 109 ++++++++++++++++++++++++++++ 1 file changed, 109 insertions(+) create mode 100644 tests/test_darwin_render_profile.py diff --git a/tests/test_darwin_render_profile.py b/tests/test_darwin_render_profile.py new file mode 100644 index 00000000..5b4f40fd --- /dev/null +++ b/tests/test_darwin_render_profile.py @@ -0,0 +1,109 @@ +from __future__ import annotations + +from copy import deepcopy +import json +from pathlib import Path +import unittest + +from scriptorium.darwin_render_profile import ( + build_profile, + build_profile_from_path, + validate_profile, +) + + +ROOT = Path(__file__).resolve().parents[1] +SURFACE = ROOT / "corpus/candidates/source-edition-traces/darwin-origin-species-rachinsky-1864-ru.render-surface.json" +PROFILE = ROOT / "corpus/candidates/source-edition-traces/darwin-origin-species-rachinsky-1864-ru.render-profile.json" + + +def load_surface() -> dict[str, object]: + value = json.loads(SURFACE.read_text(encoding="utf-8")) + assert isinstance(value, dict) + return value + + +class DarwinRenderProfileTests(unittest.TestCase): + def test_committed_profile_rebuilds_exactly_from_reviewed_surface(self) -> None: + expected = build_profile_from_path(SURFACE) + committed = json.loads(PROFILE.read_text(encoding="utf-8")) + self.assertEqual(committed, expected) + validate_profile(committed, load_surface()) + + def test_profile_classifies_all_shapes_but_keeps_semantics_incomplete(self) -> None: + profile = build_profile(load_surface()) + coverage = profile["coverage"] + self.assertEqual(coverage["observed_tag_shape_count"], 18) + self.assertEqual(coverage["observed_tag_token_count"], 5164) + self.assertEqual(coverage["observed_template_shape_count"], 38) + self.assertEqual(coverage["observed_template_invocation_count"], 4583) + self.assertEqual(coverage["unresolved_tag_shape_count"], 5) + self.assertEqual(coverage["unresolved_tag_token_count"], 518) + self.assertEqual(coverage["unresolved_template_shape_count"], 38) + self.assertEqual(coverage["unresolved_template_invocation_count"], 4583) + self.assertTrue(coverage["all_observed_shapes_classified"]) + self.assertTrue(profile["rendering_profile_frozen"]) + self.assertFalse(profile["renderer_semantics_complete"]) + self.assertFalse(profile["renderer_implementation_ready"]) + self.assertFalse(profile["rendering_equivalence_claimed"]) + + def test_profile_fails_closed_on_new_template_shape(self) -> None: + surface = deepcopy(load_surface()) + surface["template_shapes"] = list(surface["template_shapes"]) + surface["template_shapes"].append({"name": "unexpected", "positional": 0, "named": 0, "count": 1}) + with self.assertRaisesRegex(ValueError, "Darwin rendering-surface freeze digest drift"): + build_profile(surface) + + def test_profile_fails_closed_on_missing_tag_shape(self) -> None: + surface = deepcopy(load_surface()) + surface["tag_shapes"] = list(surface["tag_shapes"])[1:] + with self.assertRaisesRegex(ValueError, "Darwin rendering-surface freeze digest drift"): + build_profile(surface) + + def test_profile_fails_closed_when_existing_frozen_count_changes_without_digest_update(self) -> None: + surface = deepcopy(load_surface()) + surface["tag_shapes"] = [dict(row) for row in surface["tag_shapes"]] + surface["tag_shapes"][0]["count"] += 1 + with self.assertRaisesRegex(ValueError, "Darwin rendering-surface freeze digest drift"): + build_profile(surface) + + def test_math_reference_and_template_semantics_remain_explicitly_unresolved(self) -> None: + profile = build_profile(load_surface()) + unresolved_tags = { + (row["name"], row["kind"]) + for row in profile["tag_rules"] + if row["semantic_status"] == "unresolved" + } + self.assertEqual( + unresolved_tags, + { + ("math", "open"), + ("math", "close"), + ("ref", "open"), + ("ref", "close"), + ("references", "self_closing"), + }, + ) + self.assertTrue(all(row["semantic_status"] == "unresolved" for row in profile["template_rules"])) + nop = [row for row in profile["template_rules"] if row["name"] == "nop"] + self.assertEqual(nop[0]["handling"], "inter_page_semantics_required") + + def test_profile_keeps_composition_body_and_parity_gates_closed(self) -> None: + profile = build_profile(load_surface()) + for key in ( + "inter_page_composition_frozen", + "literary_body_count_and_digests_frozen", + "minimum_300k_proved", + "admitted_for_calibration", + "diagnostic_ready", + "m2_parity_admissible", + ): + self.assertFalse(profile[key], key) + self.assertEqual(profile["fantlab_source_edition_match"], "unknown") + self.assertFalse(profile["source_text_included"]) + self.assertFalse(profile["extraction_policy"]["persist_source_wikitext"]) + self.assertFalse(profile["extraction_policy"]["persist_rendered_prose"]) + + +if __name__ == "__main__": + unittest.main() From a2c847160b6996c25addc79771a3c3682a5d9ac6 Mon Sep 17 00:00:00 2001 From: andy-zen-dev Date: Mon, 21 Sep 2026 02:53:22 +0300 Subject: [PATCH 04/11] SCRIP-CORPUS-045 verify Darwin render profile --- .github/workflows/darwin-render-profile.yml | 79 +++++++++++++++++++++ 1 file changed, 79 insertions(+) create mode 100644 .github/workflows/darwin-render-profile.yml diff --git a/.github/workflows/darwin-render-profile.yml b/.github/workflows/darwin-render-profile.yml new file mode 100644 index 00000000..cdefd627 --- /dev/null +++ b/.github/workflows/darwin-render-profile.yml @@ -0,0 +1,79 @@ +name: Darwin/Rachinsky 1864 render profile + +on: + pull_request: + paths: + - ".github/workflows/darwin-render-profile.yml" + - "scriptorium/darwin_render_profile.py" + - "tests/test_darwin_render_profile.py" + - "tests/test_darwin_provenance_trace.py" + - "corpus/candidates/darwin-origin-species-rachinsky-1864-ru.md" + - "corpus/candidates/source-edition-traces/darwin-origin-species-rachinsky-1864-ru.json" + - "corpus/candidates/source-edition-traces/darwin-origin-species-rachinsky-1864-ru.render-surface.source-graph.json" + - "corpus/candidates/source-edition-traces/darwin-origin-species-rachinsky-1864-ru.render-surface.json" + - "corpus/candidates/source-edition-traces/darwin-origin-species-rachinsky-1864-ru.render-profile.json" + workflow_dispatch: + +permissions: + contents: read + +jobs: + verify: + name: Verify source-free fail-closed profile + runs-on: ubuntu-latest + timeout-minutes: 10 + steps: + - name: Checkout pull-request head + uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1 + with: + ref: ${{ github.event.pull_request.head.sha || github.sha }} + persist-credentials: false + + - name: Set up Python 3.13 + uses: actions/setup-python@5fda3b95a4ea91299a34e894583c3862153e4b97 # v7.0.0 + with: + python-version: "3.13" + + - name: Run profile and provenance tests + run: | + python -m unittest tests.test_darwin_render_profile -v + python -m unittest tests.test_darwin_provenance_trace -v + + - name: Deterministically rebuild and compare profile + run: | + set -euo pipefail + mkdir -p build/repro + python -m scriptorium.darwin_render_profile \ + --surface corpus/candidates/source-edition-traces/darwin-origin-species-rachinsky-1864-ru.render-surface.json \ + --output build/repro/darwin-origin-species-rachinsky-1864-ru.render-profile.json + python - <<'PY' + import json + from pathlib import Path + expected = json.loads(Path("corpus/candidates/source-edition-traces/darwin-origin-species-rachinsky-1864-ru.render-profile.json").read_text(encoding="utf-8")) + rebuilt = json.loads(Path("build/repro/darwin-origin-species-rachinsky-1864-ru.render-profile.json").read_text(encoding="utf-8")) + assert rebuilt == expected + assert rebuilt["profile_sha256"] == "2ebcfea51282505c28824f54f6a1795bd7e87e2f958ac59f5c843e68c2066f3d" + assert rebuilt["rendering_profile_frozen"] is True + assert rebuilt["renderer_semantics_complete"] is False + assert rebuilt["renderer_implementation_ready"] is False + assert rebuilt["coverage"]["unresolved_template_shape_count"] == 38 + assert rebuilt["coverage"]["unresolved_template_invocation_count"] == 4583 + assert rebuilt["coverage"]["unresolved_tag_shape_count"] == 5 + assert rebuilt["coverage"]["unresolved_tag_token_count"] == 518 + assert rebuilt["inter_page_composition_frozen"] is False + assert rebuilt["literary_body_count_and_digests_frozen"] is False + assert rebuilt["minimum_300k_proved"] is False + assert rebuilt["admitted_for_calibration"] is False + assert rebuilt["fantlab_source_edition_match"] == "unknown" + assert rebuilt["diagnostic_ready"] is False + assert rebuilt["m2_parity_admissible"] is False + print("SCRIPTORIUM_DARWIN_RENDER_PROFILE_SHA256=" + rebuilt["profile_sha256"]) + PY + + - name: Upload source-free profile evidence + uses: actions/upload-artifact@ea165f8d65b6e75b540449e92b4886f43607fa02 # v4.6.2 + with: + name: darwin-rachinsky-1864-render-profile + path: build/repro/darwin-origin-species-rachinsky-1864-ru.render-profile.json + if-no-files-found: error + retention-days: 7 From 1aea88541330eea4c8e5106a5ed376632e68db80 Mon Sep 17 00:00:00 2001 From: andy-zen-dev Date: Mon, 21 Sep 2026 02:53:36 +0300 Subject: [PATCH 05/11] SCRIP-CORPUS-045 link Darwin render profile provenance --- ...y-1864-ru.render-surface.source-graph.json | 26 ++++++++++++++----- 1 file changed, 19 insertions(+), 7 deletions(-) diff --git a/corpus/candidates/source-edition-traces/darwin-origin-species-rachinsky-1864-ru.render-surface.source-graph.json b/corpus/candidates/source-edition-traces/darwin-origin-species-rachinsky-1864-ru.render-surface.source-graph.json index 5ac39b84..d71ef011 100644 --- a/corpus/candidates/source-edition-traces/darwin-origin-species-rachinsky-1864-ru.render-surface.source-graph.json +++ b/corpus/candidates/source-edition-traces/darwin-origin-species-rachinsky-1864-ru.render-surface.source-graph.json @@ -43,10 +43,18 @@ "source_text_included": false }, { - "id": "future-renderer", - "kind": "unfrozen_page_wikitext_to_prose_profile", - "status": "unfrozen", - "must_cover_exactly_frozen_surface": true + "id": "durable-render-profile", + "kind": "frozen_source_free_fail_closed_render_decision_profile", + "path": "darwin-origin-species-rachinsky-1864-ru.render-profile.json", + "profile_version": "scriptorium-darwin-page-render-profile-v1", + "profile_sha256": "2ebcfea51282505c28824f54f6a1795bd7e87e2f958ac59f5c843e68c2066f3d", + "rendering_profile_frozen": true, + "renderer_semantics_complete": false, + "renderer_implementation_ready": false, + "rendering_equivalence_claimed": false, + "unresolved_tag_shape_count": 5, + "unresolved_template_shape_count": 38, + "source_text_included": false } ], "edges": [ @@ -67,13 +75,17 @@ }, { "from": "durable-render-surface-freeze", - "to": "future-renderer", - "relation": "constrains_supported_construct_surface" + "to": "durable-render-profile", + "relation": "classified_fail_closed_without_guessing_unresolved_provider_semantics" } ], "gate_boundary": { "surface_inventory_frozen": true, - "rendering_profile_frozen": false, + "rendering_profile_frozen": true, + "renderer_semantics_complete": false, + "renderer_implementation_ready": false, + "rendering_equivalence_claimed": false, + "inter_page_composition_frozen": false, "literary_body_count_and_digests_frozen": false, "minimum_300k_proved": false, "admitted_for_calibration": false, From d8adbaf3943b387cecc1181de4ba6d08825d92ae Mon Sep 17 00:00:00 2001 From: andy-zen-dev Date: Mon, 21 Sep 2026 02:54:32 +0300 Subject: [PATCH 06/11] SCRIP-CORPUS-045 reconcile Darwin structured provenance --- ...arwin-origin-species-rachinsky-1864-ru.json | 18 ++++++++++++------ 1 file changed, 12 insertions(+), 6 deletions(-) diff --git a/corpus/candidates/source-edition-traces/darwin-origin-species-rachinsky-1864-ru.json b/corpus/candidates/source-edition-traces/darwin-origin-species-rachinsky-1864-ru.json index 7752a8ae..be3889ad 100644 --- a/corpus/candidates/source-edition-traces/darwin-origin-species-rachinsky-1864-ru.json +++ b/corpus/candidates/source-edition-traces/darwin-origin-species-rachinsky-1864-ru.json @@ -17,7 +17,7 @@ "rendered orthography transformations unless a deterministic transform/identity contract is established" ] }, - "source_identity_status": "rendered_route_graph_418_page_revision_identities_388_30_composition_markup_surface_and_independent_scan_binary_identity_frozen_rendered_body_unfrozen", + "source_identity_status": "rendered_route_graph_418_page_revision_identities_388_30_composition_markup_surface_render_profile_and_independent_scan_binary_identity_frozen_rendered_body_unfrozen", "source": { "provider": "Russian Wikisource", "observed_at": "2026-09-19", @@ -77,7 +77,7 @@ ], "topology_frozen": true, "complete_page_revision_set_frozen": true, - "identity_scope": "all 418 included Page-sequence dependencies have exact captured revision ID, revision timestamp and MediaWiki SHA-1 in a source-free digest-pinned sharded manifest; the 388 literary versus 30 apparatus composition decision is frozen by scriptorium-darwin-literary-body-contract-v1, while Page-wikitext-to-prose rendering and final literary-body identity remain unfrozen" + "identity_scope": "all 418 included Page-sequence dependencies have exact captured revision ID, revision timestamp and MediaWiki SHA-1 in a source-free digest-pinned sharded manifest; the 388 literary versus 30 apparatus composition decision and fail-closed rendering decision profile are frozen, while unresolved provider/template/reference/math/inter-page semantics and final literary-body identity remain unfrozen" }, "page_revision_identity_manifest": { "index": "darwin-origin-species-rachinsky-1864-ru.page-revisions.index.json", @@ -136,7 +136,12 @@ 411 ], "selection_and_partition_frozen": true, - "page_wikitext_to_prose_rendering_profile_frozen": false, + "page_wikitext_to_prose_rendering_profile_frozen": true, + "render_profile_version": "scriptorium-darwin-page-render-profile-v1", + "render_profile_sha256": "2ebcfea51282505c28824f54f6a1795bd7e87e2f958ac59f5c843e68c2066f3d", + "renderer_semantics_complete": false, + "renderer_implementation_ready": false, + "rendering_equivalence_claimed": false, "composite_separator_semantics_frozen": false, "literary_body_count_and_digests_frozen": false, "source_text_committed": false @@ -207,6 +212,7 @@ "source-free topology counts: 399 rendered-route Page dependencies and 418 parent+route dependencies", "exact revision ID, revision timestamp and MediaWiki SHA-1 for all 418 included Page dependencies, committed source-free in four SHA-256-pinned shards", "scriptorium-darwin-literary-body-contract-v1 selection/partition: 388 exact Page dependencies from numbered routes /1 through /14 are literary inputs and 30 frozen dependencies are apparatus", + "scriptorium-darwin-page-render-profile-v1 fail-closed decision profile over all 18 tag shapes / 5,164 tag tokens and 38 template shapes / 4,583 invocations; profile SHA-256 2ebcfea51282505c28824f54f6a1795bd7e87e2f958ac59f5c843e68c2066f3d; unresolved semantics are not promoted", "Page-sequence 114 oldid=3364724 source-declared no-text and explicitly excluded from route /4", "Page-sequence 411 oldid=3364733 source-declared no-text and uniquely aligned with displayed print page 388", "displayed bibliographic print-page spans 1-387 and 389-399 attached to the rendered routes", @@ -218,7 +224,7 @@ ], "unfrozen": [ "full mapping from displayed print page numbers to ProofreadPage sequence positions outside the uniquely resolved page-388 gap", - "candidate-specific Page-wikitext-to-prose rendering behavior over the 388 frozen literary dependencies", + "provider/template/reference/math semantics explicitly marked unresolved by the frozen fail-closed profile", "composite separator/body-rendering semantics needed to materialize one literary body", "literary-body character count and raw/normalized digests", "FantLab analyzer-input bytes/source edition" @@ -235,7 +241,7 @@ "page_revision_identity_freeze_is_not_threshold_proof": true, "composition_contract_is_not_threshold_proof": true, "admitted_for_calibration": false, - "reason": "The 418 exact Page identities and the 388-literary/30-apparatus composition partition are frozen source-free, and the exact backing DjVu now has an independent byte identity; Page-wikitext rendering, final body composition semantics, literary-body count and digests remain unfrozen, so scan identity does not prove the >=300,000-character rule." + "reason": "The 418 exact Page identities, 388-literary/30-apparatus partition, exact backing DjVu identity and fail-closed render decision profile are frozen source-free; unresolved provider/template/reference/math/inter-page semantics, literary-body count and digests remain unfrozen, so these prerequisites do not prove the >=300,000-character rule." }, "fantlab": { "work_id": 969964, @@ -254,7 +260,7 @@ "m2_parity_admissible": false }, "next_evidence": [ - "Define and verify a candidate-specific fail-closed Page-wikitext-to-prose rendering profile over the 388 exact literary dependencies selected by scriptorium-darwin-literary-body-contract-v1, without persisting source prose.", + "Resolve and independently freeze the provider/template/reference/math semantics left explicit by scriptorium-darwin-page-render-profile-v1, then implement deterministic exact-revision rendering without persisting source prose; inter-page separator/composition semantics remain a separate decision.", "Freeze literary-body character count plus raw and scriptorium-text-v1 normalized digests; admit to general calibration/profile corpus only if the rendered body independently clears 300,000 characters including spaces.", "Keep FantLab matching separate: locate a linguistic result and analyzer-input/source-edition evidence attributable specifically to the Rachinsky translation before assigning diagnostic or M2 weight." ] From 9e85b1a2a8127a6497789ba94bd9017848a3b273 Mon Sep 17 00:00:00 2001 From: andy-zen-dev Date: Mon, 21 Sep 2026 02:54:50 +0300 Subject: [PATCH 07/11] SCRIP-CORPUS-045 assert Darwin render-profile provenance --- tests/test_darwin_provenance_trace.py | 41 +++++++++++++++++++++++---- 1 file changed, 35 insertions(+), 6 deletions(-) diff --git a/tests/test_darwin_provenance_trace.py b/tests/test_darwin_provenance_trace.py index 5f61a372..7690e0ea 100644 --- a/tests/test_darwin_provenance_trace.py +++ b/tests/test_darwin_provenance_trace.py @@ -14,6 +14,12 @@ SCAN_RECEIPT_PATH = TRACE_PATH.with_name( "darwin-origin-species-rachinsky-1864-ru.scan-identity.json" ) +RENDER_PROFILE_PATH = TRACE_PATH.with_name( + "darwin-origin-species-rachinsky-1864-ru.render-profile.json" +) +RENDER_SOURCE_GRAPH_PATH = TRACE_PATH.with_name( + "darwin-origin-species-rachinsky-1864-ru.render-surface.source-graph.json" +) class DarwinProvenanceTraceTests(unittest.TestCase): @@ -21,8 +27,10 @@ class DarwinProvenanceTraceTests(unittest.TestCase): def setUpClass(cls) -> None: cls.trace = json.loads(TRACE_PATH.read_text(encoding="utf-8")) cls.scan_receipt = json.loads(SCAN_RECEIPT_PATH.read_text(encoding="utf-8")) + cls.render_profile = json.loads(RENDER_PROFILE_PATH.read_text(encoding="utf-8")) + cls.render_source_graph = json.loads(RENDER_SOURCE_GRAPH_PATH.read_text(encoding="utf-8")) - def test_composition_contract_is_frozen_without_promoting_rendered_body(self) -> None: + def test_composition_contract_is_frozen_with_profile_but_without_promoting_rendered_body(self) -> None: contract = self.trace["source"]["literary_body_composition_contract"] self.assertEqual( contract["contract_version"], @@ -37,11 +45,32 @@ def test_composition_contract_is_frozen_without_promoting_rendered_body(self) -> self.assertEqual(contract["apparatus_dependency_count"], 30) self.assertEqual(contract["explicit_no_text_non_dependencies"], [114, 411]) self.assertTrue(contract["selection_and_partition_frozen"]) - self.assertFalse(contract["page_wikitext_to_prose_rendering_profile_frozen"]) + self.assertTrue(contract["page_wikitext_to_prose_rendering_profile_frozen"]) + self.assertEqual(contract["render_profile_version"], "scriptorium-darwin-page-render-profile-v1") + self.assertEqual(contract["render_profile_sha256"], self.render_profile["profile_sha256"]) + self.assertFalse(contract["renderer_semantics_complete"]) + self.assertFalse(contract["renderer_implementation_ready"]) + self.assertFalse(contract["rendering_equivalence_claimed"]) self.assertFalse(contract["composite_separator_semantics_frozen"]) self.assertFalse(contract["literary_body_count_and_digests_frozen"]) self.assertFalse(contract["source_text_committed"]) + def test_render_profile_source_graph_is_synchronized_without_promoting_gates(self) -> None: + boundary = self.render_source_graph["gate_boundary"] + self.assertTrue(boundary["surface_inventory_frozen"]) + self.assertTrue(boundary["rendering_profile_frozen"]) + self.assertFalse(boundary["renderer_semantics_complete"]) + self.assertFalse(boundary["renderer_implementation_ready"]) + self.assertFalse(boundary["rendering_equivalence_claimed"]) + self.assertFalse(boundary["inter_page_composition_frozen"]) + self.assertFalse(boundary["literary_body_count_and_digests_frozen"]) + self.assertFalse(boundary["minimum_300k_proved"]) + self.assertFalse(boundary["admitted_for_calibration"]) + self.assertEqual(boundary["fantlab_source_edition_match"], "unknown") + nodes = {node["id"]: node for node in self.render_source_graph["nodes"]} + self.assertEqual(nodes["durable-render-profile"]["profile_sha256"], self.render_profile["profile_sha256"]) + self.assertFalse(nodes["durable-render-profile"]["source_text_included"]) + def test_independent_scan_identity_is_reconciled_source_free(self) -> None: metadata = self.trace["source"]["scan_file_metadata"] identity = metadata["independent_binary_identity"] @@ -75,11 +104,11 @@ def test_threshold_and_fantlab_gates_remain_closed(self) -> None: self.assertFalse(admissibility["gate_ready"]) self.assertFalse(admissibility["m2_parity_admissible"]) - def test_next_evidence_no_longer_redefines_the_frozen_partition_or_scan_identity(self) -> None: + def test_next_evidence_starts_after_the_frozen_profile_boundary(self) -> None: next_evidence = self.trace["next_evidence"] - self.assertIn("388 exact literary dependencies", next_evidence[0]) - self.assertIn("Page-wikitext-to-prose rendering profile", next_evidence[0]) - self.assertNotIn("Define a candidate-specific deterministic fail-closed literary-body extraction/composition contract", next_evidence[0]) + self.assertIn("provider/template/reference/math semantics", next_evidence[0]) + self.assertIn("inter-page separator/composition semantics", next_evidence[0]) + self.assertFalse(any("Define and verify a candidate-specific fail-closed Page-wikitext-to-prose rendering profile" in item for item in next_evidence)) self.assertFalse(any("retrieve one exact DjVu byte stream" in item for item in next_evidence)) From 9827a820fcfe829f1c2640e9a49a2ef059988208 Mon Sep 17 00:00:00 2001 From: andy-zen-dev Date: Mon, 21 Sep 2026 02:55:43 +0300 Subject: [PATCH 08/11] SCRIP-CORPUS-045 expose Darwin render-profile boundary --- ...darwin-origin-species-rachinsky-1864-ru.md | 21 +++++++++++++------ 1 file changed, 15 insertions(+), 6 deletions(-) diff --git a/corpus/candidates/darwin-origin-species-rachinsky-1864-ru.md b/corpus/candidates/darwin-origin-species-rachinsky-1864-ru.md index 5b3e0b54..42ef51be 100644 --- a/corpus/candidates/darwin-origin-species-rachinsky-1864-ru.md +++ b/corpus/candidates/darwin-origin-species-rachinsky-1864-ru.md @@ -67,7 +67,7 @@ The boundary is evidence-based rather than inferred from nominal page count. The The implementation fails closed if the 418-Page identity inventory, identity-row shape, topology partition or generated contract drifts. It may later permit **transient exact-revision MediaWiki main-slot fetches only after identity verification**, but it does not serialize Page wikitext or rendered prose. -This is deliberately a composition/input freeze, **not yet a literary-body identity freeze**. The Page-wikitext-to-prose rendering profile and composite separator semantics remain unfrozen, so no body count or body digest is claimed by this unit. +This is deliberately a composition/input freeze, **not yet a literary-body identity freeze**. The candidate-specific rendering decision profile is now frozen separately below, but unresolved provider/template/reference/math semantics and composite separator semantics still prevent a rendered literary-body identity. ## Source-free rendering-surface inventory @@ -79,7 +79,13 @@ The full hosted audit covered all **388/388** literary dependencies with `identi Independent exact-head review re-read all settled CI on `ee97e29af7225131002b6e0922c731536d903d13`, independently downloaded the exact-head artifact, recomputed its audit/receipt/freeze digests, and found no blocker before squash merge `1d9a7795724a68747ea1bb6e1fe1e7ea8b590519`. -This **does not freeze the renderer**. It freezes only the set of markup constructs the future candidate-specific renderer must handle or explicitly reject. `rendering_profile_frozen=false` remains mandatory until a separate implementation proves deterministic semantics for this exact inventory. +## Fail-closed rendering decision profile + +**SCRIP-CORPUS-045 / Draft PR #178** adds `scriptorium-darwin-page-render-profile-v1`, bound cryptographically to the reviewed exact-388 source-free surface freeze before any shape classification is accepted. The profile covers all **18 tag shapes / 5,164 tag tokens** and all **38 template shapes / 4,583 template invocations**. Its canonical profile SHA-256 is **`2ebcfea51282505c28824f54f6a1795bd7e87e2f958ac59f5c843e68c2066f3d`**. + +The decision table defines only conservative local markup handling: ordinary `big`, `div`, `small`, `sub` and `sup` wrappers preserve inner text; `` is excluded from transcluded content; ProofreadPage `pagequality` metadata is dropped. It intentionally leaves **5 tag shapes / 518 tokens** unresolved for provider semantics: `` open/close, `` open/close and self-closing ``. All **38 template shapes / 4,583 invocations** remain unresolved rather than inferring expansion from names; `nop` is additionally marked as inter-page-sensitive. + +This is a **frozen decision boundary, not a renderer**. `rendering_profile_frozen=true` coexists deliberately with `renderer_semantics_complete=false`, `renderer_implementation_ready=false`, `rendering_equivalence_claimed=false`, and `inter_page_composition_frozen=false`. Any changed count, new/missing shape, stale surface digest or wrong candidate identity fails closed. No Page wikitext, template argument values, rendered prose, OCR, DjVu/PDF bytes or literary text are committed. ## Independent scan binary identity @@ -99,11 +105,12 @@ The retained Wikisource work surface explicitly states that the work is in the p ## Corpus boundary -This candidate is still **not admitted** to the >=300,000-character calibration/profile corpus. The source family, route graph, 418-dependency topology, exact 418 Page revision identities, the 388/30 literary-versus-apparatus composition boundary, the exact 388-Page source-free markup-surface inventory, and the backing DjVu byte identity are now frozen, but the actual literary prose has not yet been deterministically rendered or counted. +This candidate is still **not admitted** to the >=300,000-character calibration/profile corpus. The source family, route graph, 418-dependency topology, exact 418 Page revision identities, the 388/30 literary-versus-apparatus composition boundary, the exact 388-Page source-free markup-surface inventory, fail-closed render decision profile, and backing DjVu byte identity are now frozen, but the actual literary prose has not yet been deterministically rendered or counted. Still required before corpus admission: -- define and verify a candidate-specific fail-closed Page-wikitext-to-prose rendering profile that covers the frozen construct inventory; +- independently resolve/freeze the provider/template/reference/math semantics explicitly left unresolved by the render profile and separately freeze inter-page composition/separator semantics; +- implement deterministic exact-revision rendering under those frozen semantics without persisting source prose; - freeze the rendered literary-body character count including spaces plus raw/normalized digests; - verify that the frozen body itself, not nominal page count, topology, construct counts or scan byte size, clears **300,000 characters**. @@ -119,13 +126,15 @@ FantLab work **`work969964`** identifies Darwin's 1859 English monograph. The cu - [`source-edition-traces/darwin-origin-species-rachinsky-1864-ru.source-graph.json`](source-edition-traces/darwin-origin-species-rachinsky-1864-ru.source-graph.json) — exact route/topology evidence and apparatus roles. - [`source-edition-traces/darwin-origin-species-rachinsky-1864-ru.page-revisions.index.json`](source-edition-traces/darwin-origin-species-rachinsky-1864-ru.page-revisions.index.json) — authoritative source-free index for all 418 exact Page revision identities and four committed shards. - [`source-edition-traces/darwin-origin-species-rachinsky-1864-ru.render-surface.json`](source-edition-traces/darwin-origin-species-rachinsky-1864-ru.render-surface.json) — deterministic source-free 388-Page construct inventory with no source payload. -- [`source-edition-traces/darwin-origin-species-rachinsky-1864-ru.render-surface.source-graph.json`](source-edition-traces/darwin-origin-species-rachinsky-1864-ru.render-surface.source-graph.json) — provenance edges from frozen Page identities through literary composition and hosted markup audit to the durable inventory. +- [`source-edition-traces/darwin-origin-species-rachinsky-1864-ru.render-surface.source-graph.json`](source-edition-traces/darwin-origin-species-rachinsky-1864-ru.render-surface.source-graph.json) — provenance edges from frozen Page identities through literary composition and hosted markup audit to the durable inventory and frozen decision profile. +- [`source-edition-traces/darwin-origin-species-rachinsky-1864-ru.render-profile.json`](source-edition-traces/darwin-origin-species-rachinsky-1864-ru.render-profile.json) — deterministic fail-closed source-free rendering decision profile with explicit unresolved semantics and closed downstream gates. - [`source-edition-traces/darwin-origin-species-rachinsky-1864-ru.scan-identity.json`](source-edition-traces/darwin-origin-species-rachinsky-1864-ru.scan-identity.json) — independently verified source-free DjVu byte count, SHA-1 cross-check and SHA-256; scan bytes are not stored. - [`../../scriptorium/darwin_body_contract.py`](../../scriptorium/darwin_body_contract.py) — deterministic source-free 388/30 composition contract generator and fail-closed validator. - [`../../scriptorium/darwin_render_surface.py`](../../scriptorium/darwin_render_surface.py) — exact-revision identity-verifying hosted construct audit. - [`../../scriptorium/darwin_render_surface_freeze.py`](../../scriptorium/darwin_render_surface_freeze.py) — deterministic compact freeze reducer/validator. +- [`../../scriptorium/darwin_render_profile.py`](../../scriptorium/darwin_render_profile.py) — deterministic fail-closed source-free decision-profile builder/validator. - [`../../scriptorium/darwin_scan_identity.py`](../../scriptorium/darwin_scan_identity.py) — streaming scan-identity capture/replay with provider cross-checks and no binary persistence. ## Next evidence -A later bounded unit should use the now-frozen template/tag surface to define and verify a candidate-specific fail-closed rendering profile for the 388 literary dependencies without persisting source prose. Only then should a separate identity/admission unit compute source-free counts/digests and independently prove the >=300,000-character threshold. FantLab parity remains a separate, stricter source-matching problem. +A later bounded unit should resolve and independently freeze the provider/template/reference/math semantics explicitly left unresolved by `scriptorium-darwin-page-render-profile-v1`; inter-page separator/composition semantics should remain a separately reviewable decision. Only after deterministic rendering exists should a separate identity/admission unit compute source-free counts/digests and independently prove the >=300,000-character threshold. FantLab parity remains a separate, stricter source-matching problem. From 87fd2e92bc8137eba948c1decf305f2de196bfbd Mon Sep 17 00:00:00 2001 From: andy-zen-dev Date: Mon, 21 Sep 2026 02:56:10 +0300 Subject: [PATCH 09/11] SCRIP-CORPUS-045 record Darwin render-profile change --- project/CHANGELOG.d/SCRIP-CORPUS-045.md | 22 ++++++++++++++++++++++ 1 file changed, 22 insertions(+) create mode 100644 project/CHANGELOG.d/SCRIP-CORPUS-045.md diff --git a/project/CHANGELOG.d/SCRIP-CORPUS-045.md b/project/CHANGELOG.d/SCRIP-CORPUS-045.md new file mode 100644 index 00000000..a57066dd --- /dev/null +++ b/project/CHANGELOG.d/SCRIP-CORPUS-045.md @@ -0,0 +1,22 @@ +# SCRIP-CORPUS-045 — Darwin/Rachinsky fail-closed render profile + +- Issue: #177 +- Draft PR: #178 +- Stream: corpus / provenance; renderer prerequisite for a translated nonfiction candidate +- Base: `master@87929007415ede118cc5700a1e34f2cb3cfc576f` + +## Change + +Freeze `scriptorium-darwin-page-render-profile-v1` over the already reviewed exact-388 source-free markup-surface inventory for the 1864 Rachinsky translation of Darwin's *On the Origin of Species*. + +The profile validates the frozen surface's canonical self-digest before classification, covers all 18 tag shapes / 5,164 tag tokens and all 38 template shapes / 4,583 invocations, and fails closed on changed counts or new/missing shapes. Conservative local wrapper/noinclude/ProofreadPage metadata handling is defined; ``, ``, ``, every template shape, and `nop` inter-page behavior remain explicitly unresolved rather than inferred. + +Canonical profile SHA-256: `2ebcfea51282505c28824f54f6a1795bd7e87e2f958ac59f5c843e68c2066f3d`. + +Structured provenance and the public candidate page are synchronized with `rendering_profile_frozen=true` while `renderer_semantics_complete=false`, `renderer_implementation_ready=false`, `rendering_equivalence_claimed=false`, `inter_page_composition_frozen=false`, body/admission gates false, FantLab source match unknown, diagnostics false and M2 weight zero. + +No Page wikitext, template argument values, OCR, rendered prose, DjVu/PDF bytes or literary text are retained. + +## Handoff + +This authored unit intentionally leaves PR #178 Draft. A later run must independently review the exact final head and settled checks before Ready/merge. Provider/template/reference/math semantics and inter-page composition remain subsequent evidence units; this change does not implement a renderer or prove >=300k/FantLab parity. From 80fa06bf5eda50f890536c2d337291f413da6a5d Mon Sep 17 00:00:00 2001 From: andy-zen-dev Date: Mon, 21 Sep 2026 02:56:27 +0300 Subject: [PATCH 10/11] SCRIP-CORPUS-045 add authored run receipt --- project/run-receipts/SCRIP-CORPUS-045.md | 31 ++++++++++++++++++++++++ 1 file changed, 31 insertions(+) create mode 100644 project/run-receipts/SCRIP-CORPUS-045.md diff --git a/project/run-receipts/SCRIP-CORPUS-045.md b/project/run-receipts/SCRIP-CORPUS-045.md new file mode 100644 index 00000000..84346ddb --- /dev/null +++ b/project/run-receipts/SCRIP-CORPUS-045.md @@ -0,0 +1,31 @@ +# Run receipt — SCRIP-CORPUS-045 + +- Issue: #177 +- Draft PR: #178 +- Base: `master@87929007415ede118cc5700a1e34f2cb3cfc576f` +- Authored status: `REVIEW_PENDING` +- Unit type: corpus / provenance — fail-closed renderer prerequisite + +## Produced + +- `scriptorium/darwin_render_profile.py`: deterministic profile builder/validator bound to the reviewed Darwin exact-388 render-surface freeze. +- `darwin-origin-species-rachinsky-1864-ru.render-profile.json`: source-free canonical decision profile, SHA-256 `2ebcfea51282505c28824f54f6a1795bd7e87e2f958ac59f5c843e68c2066f3d`. +- `tests/test_darwin_render_profile.py`: exact rebuild, shape coverage, stale-digest mutation, unresolved-semantics and closed-gate regressions. +- `.github/workflows/darwin-render-profile.yml`: exact-head Python 3.13 verification, deterministic rebuild and source-free artifact upload. +- synchronized public candidate page, main source-edition trace and render-surface source graph. + +## Frozen boundary + +- 18/18 observed tag shapes classified; 5,164 observed tag tokens. +- 38/38 observed template shapes classified; 4,583 observed template invocations. +- unresolved: 5 tag shapes / 518 tokens (`math`, `ref`, `references`) and all 38 template shapes / 4,583 invocations; `nop` is explicitly inter-page-sensitive. +- render profile frozen, but renderer semantics/implementation/equivalence and inter-page composition are not frozen. +- literary-body count/digests, >=300k admission, FantLab source match, diagnostic readiness and M2 parity remain closed. + +## Safety / source boundary + +No Page wikitext, template argument values, OCR, rendered prose, DjVu/PDF bytes or literary text were committed. The profile consumes only the committed source-free freeze and validates its cryptographic self-digest before classification. + +## Verification handoff + +PR #178 is intentionally Draft. Record final exact-head CI/artifact evidence in the PR conversation after the final authored commit settles; a later independent wake must judge that exact head before Ready/merge. From b8238144caeb23256eae6a61a49e47b07c687f29 Mon Sep 17 00:00:00 2001 From: andy-zen-dev Date: Mon, 21 Sep 2026 02:57:06 +0300 Subject: [PATCH 11/11] SCRIP-CORPUS-045 advance durable state to review pending --- project/STATE_AND_QUEUE.md | 31 ++++++++++++++++--------------- 1 file changed, 16 insertions(+), 15 deletions(-) diff --git a/project/STATE_AND_QUEUE.md b/project/STATE_AND_QUEUE.md index 26029ca0..710a6645 100644 --- a/project/STATE_AND_QUEUE.md +++ b/project/STATE_AND_QUEUE.md @@ -1,22 +1,23 @@ # STATE_AND_QUEUE — Scriptorium -STATE_REVISION: 198 +STATE_REVISION: 199 PHASE: M1 — Deterministic FantLab surface -LAST_COMMITTED_RUN_AT: 2026-09-20T22:53:00Z -LAST_RESULT: SCRIP-CORPUS-044 / Issue #175 completed. Independent exact-head re-review read repaired PR #176 at `0983a7411e3c01c548b0594386cb3e1e53a73419` against unchanged base `af2f2e8c857ebb024ccac591ad50e61f0c60f3d6`, confirmed 17 commits ahead / 0 behind, 11 changed files, no inline review threads, closure of both prior blockers from review `5261712805`, and all 18 PR-triggered workflows settled `success`. Dedicated render-profile run `35540098091` / job `106156079398` checked out the exact reviewed SHA and passed profile/provenance tests plus deterministic rebuild; Pages run `35540098061` / job `106156079491` checked out the same SHA and passed the full standard-library suite, canonical site build and deterministic rebuild. Artifact `10614018525` was independently downloaded and verified as a 1,734-byte source-free ZIP / SHA-256 `60c714b6e0abeace4daf65864443d85b623907d577b019e6a669e7b4e905a1d3`, containing only the 9,854-byte profile JSON whose canonical profile SHA-256 recomputes to `88e410311435ab6bcfbd70ab0a2cea73ffe304e3ce2876d241d25ea38cc5d5cd`. Re-review was recorded as COMMENT `5262003280`, not self-approval; PR #176 was marked Ready and squash-merged as `7146f0e29e5567c4368bc34d4f18bc3f71cb3ff4`, closing Issue #175 completed. -LAST_VERIFIED_PROGRESS: Master now retains an independently reviewed source-free fail-closed rendering decision profile for the 1928 *Twelve Chairs* candidate, bound to the reviewed exact-410 markup-surface freeze SHA-256 `8a4f04238c68c40e59464e76a7911f0bee9488e09daab9f8772b1af970768d23` and Page-index SHA-256 `c1e3280984e640b8fe739a752ff49a3327633f49b52a635bd2d2d1e6a09f538f`. The profile classifies all 12 observed tag shapes / 3,488 tokens and all 30 template shapes / 602 invocations, validates the frozen surface cryptographically before consumption, and keeps all 30 template shapes plus 410 `` tokens semantically unresolved. Canonical structured provenance now agrees with `rendering_profile_frozen=true` while `renderer_semantics_complete=false`, `renderer_implementation_ready=false` and `rendering_equivalence_claimed=false`. No Page wikitext, template argument values, OCR, rendered prose, scan bytes or literary text are retained. Inter-page composition/separators, literary-body count/digests, Scriptorium-proven >=300k admission and FantLab analyzer-input/source-edition identity remain unfrozen/unknown; diagnostic readiness is false and M2 remains 0/5. +LAST_COMMITTED_RUN_AT: 2026-09-20T23:58:00Z +LAST_RESULT: SCRIP-CORPUS-045 / Issue #177 authored as Draft PR #178 from `master@87929007415ede118cc5700a1e34f2cb3cfc576f`. The unit freezes a source-free fail-closed rendering decision profile for the already reviewed exact-388 Darwin/Rachinsky 1864 literary Page surface. The profile cryptographically validates the frozen render-surface self-digest before classification, covers all 18 tag shapes / 5,164 tag tokens and all 38 template shapes / 4,583 invocations, and keeps 5 reference/math tag shapes / 518 tokens plus every template shape explicitly unresolved. Canonical profile SHA-256 is `2ebcfea51282505c28824f54f6a1795bd7e87e2f958ac59f5c843e68c2066f3d`. Structured provenance and the public candidate page are synchronized without promoting renderer/body/admission/FantLab/M2 gates. The PR remains Draft for an independent exact-head review after final checks settle. +LAST_VERIFIED_PROGRESS: Master still retains independently reviewed SCRIP-CORPUS-044 for the 1928 *Twelve Chairs*. Branch `scrip-corpus-045-darwin-render-profile` now adds the authored Darwin/Rachinsky render-profile candidate over the reviewed 388-literary/30-apparatus composition and exact-388 source-free markup inventory. The authored profile is a decision boundary, not a renderer: `rendering_profile_frozen=true` while `renderer_semantics_complete=false`, `renderer_implementation_ready=false`, `rendering_equivalence_claimed=false`, `inter_page_composition_frozen=false`, literary-body counts/digests and >=300k admission remain false, FantLab source match remains unknown, diagnostics remain false and M2 remains 0/5. No Page wikitext, template argument values, OCR, rendered prose, scan bytes or literary text are retained. ## Current unit ```text -UNIT_ID: SCRIP-CORPUS-044 -ISSUE: #175 (closed completed) -STATUS: COMPLETE -PR: #176 (squash-merged as 7146f0e29e5567c4368bc34d4f18bc3f71cb3ff4) -NEXT_ACTION: Resume normal-flow selection from the queue. A later bounded unit may resolve and independently freeze the - template/reference semantics explicitly left unresolved by the reviewed fail-closed profile; inter-page - composition/separators remain a separate subsequent decision. Keep literary-body, >=300k admission, - FantLab source-match, diagnostic and M2 gates closed until their evidence is independently verified. +UNIT_ID: SCRIP-CORPUS-045 +ISSUE: #177 (open) +STATUS: REVIEW_PENDING +PR: #178 (Draft) +NEXT_ACTION: Independently review the exact final PR #178 head after all PR-triggered workflows settle. Verify the + cryptographic surface validation, exact 18-tag/38-template coverage, explicit unresolved + math/reference/template/inter-page classes, structured-provenance synchronization and source-free + artifact. Do not self-approve the authored change. If clean, mark Ready and merge in that later run; + otherwise record blockers. Keep body, >=300k admission, FantLab source-match, diagnostic and M2 gates closed. ``` ## Current milestone gate @@ -50,7 +51,7 @@ Detailed evidence lives in `corpus/candidates/`, `benchmarks/`, `project/run-rec - **The Twelve Chairs** — SCRIP-CORPUS-044 is complete. Master retains the independently reviewed parent route graph, all 410 exact Page dependency identities, exact source-free backing Commons PDF identity (77,978,350 bytes; SHA-1 `4ab6aa42c3517169e99c1177b6fb6412cfe9187d`; SHA-256 `5a82f8101f9c17dfafcf8b45dc9ed5a7cdfa12a3e88d18bd0f0987e4fcd51eb4`), reviewed source-free four-gap audit, reviewed gap composition-membership decision, independently reviewed exact-410 source-free markup-surface inventory, and independently reviewed fail-closed rendering decision profile. The profile is cryptographically bound to the reviewed surface and classifies all 12 tag shapes and 30 template shapes, but every template shape and `` remain semantically unresolved, so renderer implementation/readiness/equivalence remain false. The four audited gaps stay excluded; 150/314 remain `nonempty_body_unclassified`, 151/315 retain `no_transcluded_body`. Inter-page composition/separators, literary-body counts/digests and Scriptorium-proven >=300k admission remain unfrozen. The 40-chapter later 1938/1961 editorial family stays distinct; coauthorship prevents individual-author attribution without an explicit VOICE model. - **The Life of Klim Samgin** — SCRIP-CORPUS-020 is complete. Four exact parent revisions and the Part 2 dependency are replay-frozen; merged PRs #111–#124 freeze the target-only `#lst`, inferred `poemx1`/Poem boundaries, structural surface and all thirteen plain literary values; #126 merged the candidate-specific fail-closed extractor; #127 merged the canonical source-free per-part/composite literary-body identities; #128 reconciled the structured provenance trace. Exact historical MediaWiki/Poem deployment and FantLab analyzer-input/source-edition identity remain unresolved. - **Children of Captain Grant — Beketova translation** — SCRIP-CORPUS-026 is complete. Merged PR #140 keeps exact Russian Wikisource revision `oldid=5304880` replay-frozen and adds candidate-specific `scriptorium-beketova-captain-grant-wikisource-body-v1`, freezing a 1,095,467-character / 2,040,240-byte literary body with raw and normalized SHA-256 `4931eba535a4ef3989ac905d895258f00db57029e488d98511db50a7ce4d1161`. Beketova / Detgiz 1955 / `az.lib.ru` bibliography and explicit source PD evidence covering the translation remain retained. The frozen body clears the >=300k rule for general calibration/profile use. No FantLab linguistic result or analyzer-input identity is established for this translation; M2 weight remains zero. -- **On the Origin of Species — Rachinsky translation** — SCRIP-CORPUS-037 is complete. Master retains the 418 exact Page revision identities, reviewed 388-literary/30-apparatus composition contract, merged source-free exact-388 markup-surface inventory, and independently reviewed exact backing Commons DjVu identity: 27,368,263 bytes, SHA-1 `75ef508588194ae74874272ce290f3ec1043ea9b`, SHA-256 `7f3ae1aadd4a844b193783a0c350e23815a76e7bb14a92bae7fa1c077354a2c8`; no scan bytes are committed. Rendering semantics, literary-body count/digests and >=300k admission remain unfrozen; `admitted_for_calibration=false`. FantLab work 969964 currently exposes Timiryazev rather than Rachinsky; source match/M2 remain unknown/zero. +- **On the Origin of Species — Rachinsky translation** — SCRIP-CORPUS-045 is authored in Draft PR #178. Master already retains the 418 exact Page revision identities, reviewed 388-literary/30-apparatus composition contract, merged source-free exact-388 markup-surface inventory, and independently reviewed exact backing Commons DjVu identity: 27,368,263 bytes, SHA-1 `75ef508588194ae74874272ce290f3ec1043ea9b`, SHA-256 `7f3ae1aadd4a844b193783a0c350e23815a76e7bb14a92bae7fa1c077354a2c8`. PR #178 adds a fail-closed decision profile bound to that reviewed surface: all 18 tag / 38 template shapes classified, while math/reference semantics plus all template semantics remain explicitly unresolved. Renderer implementation/equivalence, body count/digests and >=300k admission remain unfrozen; `admitted_for_calibration=false`. FantLab work 969964 currently exposes Timiryazev rather than Rachinsky; source match/M2 remain unknown/zero. ## Deterministic / morphology findings @@ -71,7 +72,7 @@ Detailed evidence lives in `corpus/candidates/`, `benchmarks/`, `project/run-rec - Shining World exposes a merged inspectable source-free inventory artifact and corrected public/structured provenance that distinguish the 34 links advertised by the reviewed index from the 19 pages that actually exist; missing targets are visible blockers, not silently filled text. - Running on Waves now exposes in master a source-free frozen literary-body identity and >=300k general-corpus admission while preserving the separate 1980/az.lib.ru route and keeping FantLab source match/M2 closed. - Merged SCRIP-CORPUS-044 extends the dedicated 1928 *Twelve Chairs* candidate page with the independently reviewed fail-closed exact-surface render decision profile and synchronized structured provenance, including explicit unresolved template/reference counts, while preserving the distinction between a frozen profile boundary and renderer/body equivalence. -- Merged SCRIP-CORPUS-037 makes Darwin/Rachinsky's exact backing DjVu byte identity inspectable as source-free provenance alongside the already merged 418 Page identities, 388/30 literary/apparatus contract and exact-388 markup-surface inventory, while explicitly keeping renderer, body identity/admission and FantLab gates open. +- Draft PR #178 extends the Darwin/Rachinsky public candidate page and structured provenance with a source-free exact-388 fail-closed render decision profile while explicitly exposing the unresolved 38 template shapes / 4,583 invocations and 5 math/reference tag shapes / 518 tokens; it does not claim renderer/body equivalence. - Klim Samgin exposes the source graph, fail-closed extractor, canonical source-free per-part/composite literary-body identities, public candidate page, and a structured provenance trace consistent with those identities. Evidence remains explicitly diagnostic-only and source-match unknown. - Live Pages deployment remains disabled behind `SCRIPTORIUM_PAGES_DEPLOY_ENABLED=true` plus repository Pages administration. @@ -90,5 +91,5 @@ Detailed evidence lives in `corpus/candidates/`, `benchmarks/`, `project/run-rec 11. Klim Samgin is fully frozen at the candidate-specific literary-body/provenance level, but exact historical Russian Wikisource MediaWiki-core/Poem deployment equivalence and FantLab analyzer-input bytes/edition remain unproven; these are evidence boundaries, not open acceptance criteria for closed Issue #109. 12. The Beketova *Children of Captain Grant* translation now has a frozen literary body large enough for general corpus admission, but no FantLab linguistic result/analyzer-input identity has been established for this translation; it cannot advance M2 until that evidence exists. 13. The 1928 *Twelve Chairs* route now has independently reviewed exact source-free identities for all 410 referenced Page dependencies, exact backing Commons PDF identity, merged four-gap evidence, reviewed gap composition membership, independently reviewed exact-410 source-free markup-surface inventory and an independently reviewed fail-closed decision profile. All 30 observed template shapes / 602 invocations and all 410 `` tokens still require evidenced semantics; inter-page composition, body counts/digests and FantLab source identity remain unresolved. -14. Darwin/Rachinsky 1864 has a reviewed source-free 388/30 composition contract, exact-388 markup inventory and independently frozen backing scan byte identity. Renderer semantics, body count/digests and >=300k admission remain unresolved, and FantLab's current Russian translation surface is a different translation. +14. Darwin/Rachinsky 1864 has a reviewed source-free 388/30 composition contract, exact-388 markup inventory and independently frozen backing scan byte identity. Draft PR #178 now supplies the fail-closed decision profile, but 38 template shapes / 4,583 invocations plus math/reference semantics and inter-page composition still require evidence before deterministic body rendering; body count/digests and >=300k admission remain unresolved, and FantLab's current Russian translation surface is a different translation. 15. Pages live activation remains a repository-admin effect and is off.