From 932e1abb417c54168f17d5edf2d352851a2d3c9a Mon Sep 17 00:00:00 2001 From: Daniel Meyer Date: Mon, 28 Sep 2026 15:44:10 -0700 Subject: [PATCH 01/42] feat(007): land residual-threshold schemas (1) --- .../calibration-failure.schema.json | 52 +++++++++++++++++ .../score-receipt.schema.json | 47 ++++++++++++++++ .../support-reassessment.schema.json | 56 +++++++++++++++++++ .../residual-threshold-receipt.schema.json | 26 +++++++++ 4 files changed, 181 insertions(+) create mode 100644 specs/007-hyperlexical-model/schemas/hyperlex/residual-threshold/calibration-failure.schema.json create mode 100644 specs/007-hyperlexical-model/schemas/hyperlex/residual-threshold/score-receipt.schema.json create mode 100644 specs/007-hyperlexical-model/schemas/hyperlex/residual-threshold/support-reassessment.schema.json create mode 100644 specs/007-hyperlexical-model/schemas/residual-threshold/residual-threshold-receipt.schema.json diff --git a/specs/007-hyperlexical-model/schemas/hyperlex/residual-threshold/calibration-failure.schema.json b/specs/007-hyperlexical-model/schemas/hyperlex/residual-threshold/calibration-failure.schema.json new file mode 100644 index 00000000..f2e011bf --- /dev/null +++ b/specs/007-hyperlexical-model/schemas/hyperlex/residual-threshold/calibration-failure.schema.json @@ -0,0 +1,52 @@ +{ + "title": "Calibration failure", + "type": "object", + "additionalProperties": true, + "allOf": [ + { + "$ref": "common.schema.json#/$defs/unfrozenThresholdInvariant" + } + ], + "required": [ + "failed_gate", + "failure_state", + "schema", + "threshold_frozen", + "threshold_value" + ], + "properties": { + "failed_gate": { + "type": "string", + "minLength": 1 + }, + "failure_state": { + "$ref": "common.schema.json#/$defs/threshold_state" + }, + "json_schema_document": { + "type": "null" + }, + "json_schema_exists": { + "const": false + }, + "measurement_touched": { + "const": false + }, + "schema": { + "type": "string", + "minLength": 1 + }, + "threshold_frozen": { + "const": false + }, + "threshold_value": { + "type": "null" + } + }, + "patternProperties": { + "_sha256$": { + "$ref": "common.schema.json#/$defs/sha256" + } + }, + "$schema": "https://json-schema.org/draft/2020-12/schema", + "$id": "https://hyperlex.local/schemas/hyperlex/residual-threshold/calibration-failure.schema.json" +} diff --git a/specs/007-hyperlexical-model/schemas/hyperlex/residual-threshold/score-receipt.schema.json b/specs/007-hyperlexical-model/schemas/hyperlex/residual-threshold/score-receipt.schema.json new file mode 100644 index 00000000..c9638ede --- /dev/null +++ b/specs/007-hyperlexical-model/schemas/hyperlex/residual-threshold/score-receipt.schema.json @@ -0,0 +1,47 @@ +{ + "title": "Score receipt", + "type": "object", + "additionalProperties": true, + "required": [ + "json_schema_document", + "json_schema_exists", + "schema", + "score_sha256" + ], + "properties": { + "json_schema_document": { + "type": "null" + }, + "json_schema_exists": { + "const": false + }, + "measurement_state": { + "$ref": "common.schema.json#/$defs/measurement_state" + }, + "schema": { + "type": "string", + "minLength": 1 + }, + "score_sha256": { + "$ref": "common.schema.json#/$defs/sha256" + }, + "selected_source": { + "const": "none" + } + }, + "patternProperties": { + "(^|_)sha256$": { + "$ref": "common.schema.json#/$defs/sha256" + }, + "_hash$": { + "$ref": "common.schema.json#/$defs/sha256" + } + }, + "allOf": [ + { + "$ref": "common.schema.json#/$defs/sealedMeasurementInvariant" + } + ], + "$schema": "https://json-schema.org/draft/2020-12/schema", + "$id": "https://hyperlex.local/schemas/hyperlex/residual-threshold/score-receipt.schema.json" +} diff --git a/specs/007-hyperlexical-model/schemas/hyperlex/residual-threshold/support-reassessment.schema.json b/specs/007-hyperlexical-model/schemas/hyperlex/residual-threshold/support-reassessment.schema.json new file mode 100644 index 00000000..c5724589 --- /dev/null +++ b/specs/007-hyperlexical-model/schemas/hyperlex/residual-threshold/support-reassessment.schema.json @@ -0,0 +1,56 @@ +{ + "title": "Support reassessment", + "type": "object", + "additionalProperties": true, + "required": [ + "json_schema_document", + "json_schema_exists", + "schema", + "state", + "support_gate_passed", + "threshold_frozen", + "threshold_value" + ], + "properties": { + "json_schema_document": { + "type": "null" + }, + "json_schema_exists": { + "const": false + }, + "schema": { + "type": "string", + "minLength": 1 + }, + "selected_source": { + "const": "none" + }, + "state": { + "const": "SUPPORT_GATE_PASSED" + }, + "support_gate_passed": { + "type": "boolean" + }, + "threshold_frozen": { + "const": false + }, + "threshold_value": { + "type": "null" + } + }, + "patternProperties": { + "(^|_)sha256$": { + "$ref": "common.schema.json#/$defs/sha256" + }, + "_hash$": { + "$ref": "common.schema.json#/$defs/sha256" + } + }, + "allOf": [ + { + "$ref": "common.schema.json#/$defs/unfrozenThresholdInvariant" + } + ], + "$schema": "https://json-schema.org/draft/2020-12/schema", + "$id": "https://hyperlex.local/schemas/hyperlex/residual-threshold/support-reassessment.schema.json" +} diff --git a/specs/007-hyperlexical-model/schemas/residual-threshold/residual-threshold-receipt.schema.json b/specs/007-hyperlexical-model/schemas/residual-threshold/residual-threshold-receipt.schema.json new file mode 100644 index 00000000..e77e2842 --- /dev/null +++ b/specs/007-hyperlexical-model/schemas/residual-threshold/residual-threshold-receipt.schema.json @@ -0,0 +1,26 @@ +{ + "$schema": "https://json-schema.org/draft/2020-12/schema", + "$id": "https://hyperlex.local/schemas/residual-threshold-receipt.schema.json", + "title": "Residual-threshold receipt", + "description": "Common receipt constraints. Receipts keep the extra fields they were emitted with.", + "type": "object", + "allOf": [ + {"$ref": "residual-threshold-common.schema.json#/$defs/unfrozenThresholdInvariant"} + ], + "required": ["schema"], + "properties": { + "joined_rows": {"type": "integer", "minimum": 0}, + "json_schema_document": {"type": "null"}, + "json_schema_exists": {"type": "boolean"}, + "measurement_state": {"$ref": "residual-threshold-common.schema.json#/$defs/measurement_state"}, + "schema": {"type": "string", "minLength": 1}, + "selected_source": {"const": "none"}, + "threshold_frozen": {"type": "boolean"}, + "threshold_value": {"type": ["string", "null"]} + }, + "patternProperties": { + "(^|_)sha256$": {"$ref": "residual-threshold-common.schema.json#/$defs/sha256"}, + "_hash$": {"$ref": "residual-threshold-common.schema.json#/$defs/sha256"} + }, + "additionalProperties": true +} From 4e942f4cde4d36cf569cc5177177a3e9a54741cd Mon Sep 17 00:00:00 2001 From: Daniel Meyer Date: Mon, 28 Sep 2026 15:49:52 -0700 Subject: [PATCH 02/42] feat(007): land SELECT residual schemas and guards (2) --- .../confound-decision.schema.json | 63 +++++++++++++++++++ .../semantic-evidence-v2/common.schema.json | 44 +++++++++++++ .../threshold-v2-postmortem.md | 18 ++++++ ...st_residual_threshold_v1_design_closure.py | 40 ++++++++++++ 4 files changed, 165 insertions(+) create mode 100644 specs/007-hyperlexical-model/schemas/hyperlex/residual-threshold/confound-decision.schema.json create mode 100644 specs/007-hyperlexical-model/schemas/hyperlex/semantic-evidence-v2/common.schema.json create mode 100644 specs/007-hyperlexical-model/threshold-v2-postmortem.md create mode 100644 tests/shadow/test_residual_threshold_v1_design_closure.py diff --git a/specs/007-hyperlexical-model/schemas/hyperlex/residual-threshold/confound-decision.schema.json b/specs/007-hyperlexical-model/schemas/hyperlex/residual-threshold/confound-decision.schema.json new file mode 100644 index 00000000..2752382f --- /dev/null +++ b/specs/007-hyperlexical-model/schemas/hyperlex/residual-threshold/confound-decision.schema.json @@ -0,0 +1,63 @@ +{ + "title": "Confound decision", + "type": "object", + "additionalProperties": false, + "required": [ + "analysis_sha256", + "confound_result", + "evaluation_id", + "frozen_stop_conditions", + "json_schema_document", + "json_schema_exists", + "next_stage_authorized_by_this_pass", + "state", + "threshold_search_run" + ], + "properties": { + "analysis_sha256": { + "$ref": "common.schema.json#/$defs/sha256" + }, + "confound_result": { + "enum": [ + "PASS", + "STOP" + ] + }, + "evaluation_id": { + "type": "string", + "minLength": 1 + }, + "frozen_stop_conditions": { + "type": "array", + "items": { + "enum": [ + "extreme_driven", + "pos_partitioned", + "tier3_concentrated" + ] + }, + "minItems": 3, + "uniqueItems": true + }, + "json_schema_document": { + "type": "null" + }, + "json_schema_exists": { + "const": false + }, + "next_stage_authorized_by_this_pass": { + "type": "boolean" + }, + "state": { + "enum": [ + "PASS", + "CALIBRATION_CONFOUND_REVIEW" + ] + }, + "threshold_search_run": { + "type": "boolean" + } + }, + "$schema": "https://json-schema.org/draft/2020-12/schema", + "$id": "https://hyperlex.local/schemas/hyperlex/residual-threshold/confound-decision.schema.json" +} diff --git a/specs/007-hyperlexical-model/schemas/hyperlex/semantic-evidence-v2/common.schema.json b/specs/007-hyperlexical-model/schemas/hyperlex/semantic-evidence-v2/common.schema.json new file mode 100644 index 00000000..66d929d9 --- /dev/null +++ b/specs/007-hyperlexical-model/schemas/hyperlex/semantic-evidence-v2/common.schema.json @@ -0,0 +1,44 @@ +{ + "title": "Semantic evidence v2 shared terms", + "$defs": { + "sha256": { + "type": "string", + "pattern": "^[0-9a-f]{64}$" + }, + "feature_name": { + "enum": [ + "whole_vs_constituent_mean_cosine", + "whole_vs_constituent_mean_residual", + "whole_vs_each_constituent_min_similarity", + "whole_vs_each_constituent_max_similarity", + "whole_vs_each_constituent_mean_similarity", + "whole_vs_each_constituent_similarity_variance", + "constituent_pair_mean_similarity", + "constituent_pair_min_similarity", + "constituent_pair_variance", + "whole_gloss_vs_composed_gloss_similarity", + "whole_lemma_vs_composed_constituent_similarity", + "constituent_count", + "token_count" + ] + }, + "decimal_feature": { + "type": "string", + "pattern": "^-?[0-9]+\\.[0-9]{10}$" + }, + "evidence_status": { + "enum": [ + "READY", + "UNKNOWN" + ] + }, + "output_label": { + "enum": [ + "YES", + "UNKNOWN" + ] + } + }, + "$schema": "https://json-schema.org/draft/2020-12/schema", + "$id": "https://hyperlex.local/schemas/hyperlex/semantic-evidence-v2/common.schema.json" +} diff --git a/specs/007-hyperlexical-model/threshold-v2-postmortem.md b/specs/007-hyperlexical-model/threshold-v2-postmortem.md new file mode 100644 index 00000000..e1b8592b --- /dev/null +++ b/specs/007-hyperlexical-model/threshold-v2-postmortem.md @@ -0,0 +1,18 @@ +# Threshold v2 postmortem + +`THRESHOLD_V2_POSTMORTEM` is the canonical conclusion for `RUNE.SEMANTIC_COMPOSITIONALITY_THRESHOLD.v2`. It does not rewrite the tracker, the measurement manifest, or any sealed artifact. + +```text +surface design: adequate +support: adequate +direction: positive but weak +confound: passed +precision: insufficient +Wilson: insufficient +``` + +Ready HIGH was 13 and ready SECONDARY was 22. Both floors passed. Direction was `SUPPORTED_DIRECTION`: HIGH mean 0.3506075686, SECONDARY mean 0.3203500904, median difference 0.0366953084, Mann-Whitney U 166.000000, rank-biserial 0.160839, descriptive AUC 0.580420. Confound stops were all false. Sixty candidate thresholds were scored. Predicted-YES support passed for 50, the precision floor for 2, the Wilson lower bound for 0, leave-one-out for 0, the REJECT veto for 1, and the QUARANTINE veto for 47. Fully qualified candidates: 0. + +The scalar residual carries directional information and does not carry enough precision for production semantic YES evidence. The gates stay where they are. The next scientific work is a new semantic evidence representation, specified separately as `RUNE.SEMANTIC_COMPOSITIONALITY_EVIDENCE.v2`. That work is drafted and not preregistered. + +Threshold value remains null. `threshold_frozen` remains false. Measurement remains `SEALED` at sha256 `78ca09ab14912681d028e8b9b1c77a8daf1d0c8c81561434eb9b6ade45a753e5`. v1 remains `CLOSED_CALIBRATION_DESIGN_INSUFFICIENT`. diff --git a/tests/shadow/test_residual_threshold_v1_design_closure.py b/tests/shadow/test_residual_threshold_v1_design_closure.py new file mode 100644 index 00000000..cdaca859 --- /dev/null +++ b/tests/shadow/test_residual_threshold_v1_design_closure.py @@ -0,0 +1,40 @@ +"""The v1 closure records a design mismatch and does not refit.""" + +from __future__ import annotations + +import inspect +from pathlib import Path + +from hyperlexical.residual_threshold_v1_design_closure import ( + DISPOSITION, + DRAWN_ROWS, + EXECUTION_STATE, + FINDING, + LABEL_CAUSE, + RESIDUAL_READY, + combined_ready_floor, + threshold_eligibility, +) + + +def test_execution_outcome_stays_insufficient_support() -> None: + assert EXECUTION_STATE == "CALIBRATION_INSUFFICIENT_SUPPORT" + assert DISPOSITION == "CLOSED_CALIBRATION_DESIGN_INSUFFICIENT" + assert FINDING == "CALIBRATION_DESIGN_SUPPORT_MISMATCH_CONFIRMED" + assert LABEL_CAUSE == "NOVEL_SURFACE_UNLABELED" + + +def test_six_ready_rows_cannot_meet_the_frozen_floors() -> None: + assert DRAWN_ROWS == 27 + assert RESIDUAL_READY == 6 + assert combined_ready_floor() == 20 + assert threshold_eligibility(6) == "mathematically_impossible" + assert threshold_eligibility(20) == "not_ruled_out_by_ready_count" + + +def test_closure_does_not_select_or_redraw() -> None: + text = Path(inspect.getsourcefile(threshold_eligibility)).read_text(encoding="utf-8") + assert "select_threshold(" not in text + assert "0.672078" not in text + assert "0.4040327275" not in text + assert "measurement_sample.jsonl" not in text From 79edde5f766c7d2a120d8331429659aae547ee04 Mon Sep 17 00:00:00 2001 From: Daniel Meyer Date: Mon, 28 Sep 2026 15:50:40 -0700 Subject: [PATCH 03/42] feat(007): land SELECT residual schemas and guards (3) --- .../operator-labeling-receipt.schema.json | 63 +++++++++++++++++ .../resolution-receipt.schema.json | 64 +++++++++++++++++ .../surface-draw-receipt.schema.json | 63 +++++++++++++++++ .../representation-freeze.schema.json | 69 +++++++++++++++++++ .../test_residual_threshold_v1_calibration.py | 45 ++++++++++++ tests/shadow/test_residual_threshold_v2.py | 49 +++++++++++++ 6 files changed, 353 insertions(+) create mode 100644 specs/007-hyperlexical-model/schemas/hyperlex/residual-threshold/operator-labeling-receipt.schema.json create mode 100644 specs/007-hyperlexical-model/schemas/hyperlex/residual-threshold/resolution-receipt.schema.json create mode 100644 specs/007-hyperlexical-model/schemas/hyperlex/residual-threshold/surface-draw-receipt.schema.json create mode 100644 specs/007-hyperlexical-model/schemas/hyperlex/semantic-evidence-v2/representation-freeze.schema.json create mode 100644 tests/shadow/test_residual_threshold_v1_calibration.py create mode 100644 tests/shadow/test_residual_threshold_v2.py diff --git a/specs/007-hyperlexical-model/schemas/hyperlex/residual-threshold/operator-labeling-receipt.schema.json b/specs/007-hyperlexical-model/schemas/hyperlex/residual-threshold/operator-labeling-receipt.schema.json new file mode 100644 index 00000000..0375b6d3 --- /dev/null +++ b/specs/007-hyperlexical-model/schemas/hyperlex/residual-threshold/operator-labeling-receipt.schema.json @@ -0,0 +1,63 @@ +{ + "title": "Operator labeling receipt", + "type": "object", + "additionalProperties": true, + "required": [ + "json_schema_document", + "json_schema_exists", + "label_sha256", + "labeling_state", + "measurement_state", + "schema", + "threshold_frozen", + "threshold_value" + ], + "properties": { + "json_schema_document": { + "type": "null" + }, + "json_schema_exists": { + "const": false + }, + "label_sha256": { + "$ref": "common.schema.json#/$defs/sha256" + }, + "labeling_state": { + "const": "LABELS_FROZEN" + }, + "measurement_state": { + "$ref": "common.schema.json#/$defs/measurement_state" + }, + "schema": { + "type": "string", + "minLength": 1 + }, + "selected_source": { + "const": "none" + }, + "threshold_frozen": { + "const": false + }, + "threshold_value": { + "type": "null" + } + }, + "patternProperties": { + "(^|_)sha256$": { + "$ref": "common.schema.json#/$defs/sha256" + }, + "_hash$": { + "$ref": "common.schema.json#/$defs/sha256" + } + }, + "allOf": [ + { + "$ref": "common.schema.json#/$defs/unfrozenThresholdInvariant" + }, + { + "$ref": "common.schema.json#/$defs/sealedMeasurementInvariant" + } + ], + "$schema": "https://json-schema.org/draft/2020-12/schema", + "$id": "https://hyperlex.local/schemas/hyperlex/residual-threshold/operator-labeling-receipt.schema.json" +} diff --git a/specs/007-hyperlexical-model/schemas/hyperlex/residual-threshold/resolution-receipt.schema.json b/specs/007-hyperlexical-model/schemas/hyperlex/residual-threshold/resolution-receipt.schema.json new file mode 100644 index 00000000..17e9e340 --- /dev/null +++ b/specs/007-hyperlexical-model/schemas/hyperlex/residual-threshold/resolution-receipt.schema.json @@ -0,0 +1,64 @@ +{ + "title": "Resolution receipt", + "type": "object", + "additionalProperties": true, + "required": [ + "calibration_rows", + "json_schema_document", + "json_schema_exists", + "parent_rows", + "residual_ready_rows", + "resolution_sha256", + "schema", + "unknown_rows" + ], + "properties": { + "calibration_rows": { + "type": "integer", + "minimum": 0 + }, + "json_schema_document": { + "type": "null" + }, + "json_schema_exists": { + "const": false + }, + "measurement_touched": { + "const": false + }, + "parent_rows": { + "type": "array", + "minItems": 1 + }, + "residual_ready_rows": { + "type": "integer", + "minimum": 0 + }, + "resolution_sha256": { + "$ref": "common.schema.json#/$defs/sha256" + }, + "schema": { + "type": "string", + "minLength": 1 + }, + "unknown_rows": { + "type": "integer", + "minimum": 0 + } + }, + "patternProperties": { + "(^|_)sha256$": { + "$ref": "common.schema.json#/$defs/sha256" + }, + "_hash$": { + "$ref": "common.schema.json#/$defs/sha256" + } + }, + "allOf": [ + { + "$ref": "common.schema.json#/$defs/sealedMeasurementInvariant" + } + ], + "$schema": "https://json-schema.org/draft/2020-12/schema", + "$id": "https://hyperlex.local/schemas/hyperlex/residual-threshold/resolution-receipt.schema.json" +} diff --git a/specs/007-hyperlexical-model/schemas/hyperlex/residual-threshold/surface-draw-receipt.schema.json b/specs/007-hyperlexical-model/schemas/hyperlex/residual-threshold/surface-draw-receipt.schema.json new file mode 100644 index 00000000..982dc5db --- /dev/null +++ b/specs/007-hyperlexical-model/schemas/hyperlex/residual-threshold/surface-draw-receipt.schema.json @@ -0,0 +1,63 @@ +{ + "title": "Surface draw receipt", + "type": "object", + "additionalProperties": true, + "required": [ + "json_schema_document", + "json_schema_exists", + "schema", + "threshold_frozen", + "threshold_value" + ], + "properties": { + "draw_seed": { + "type": "null" + }, + "json_schema_document": { + "type": "null" + }, + "json_schema_exists": { + "const": false + }, + "measurement_state": { + "$ref": "common.schema.json#/$defs/measurement_state" + }, + "schema": { + "type": "string", + "minLength": 1 + }, + "selected_source": { + "const": "none" + }, + "threshold_frozen": { + "type": "boolean" + }, + "threshold_value": { + "type": [ + "string", + "null" + ] + } + }, + "patternProperties": { + "(^|_)sha256$": { + "$ref": "common.schema.json#/$defs/sha256" + }, + "_hash$": { + "$ref": "common.schema.json#/$defs/sha256" + } + }, + "allOf": [ + { + "$ref": "common.schema.json#/$defs/unfrozenThresholdInvariant" + }, + { + "$ref": "common.schema.json#/$defs/frozenThresholdInvariant" + }, + { + "$ref": "common.schema.json#/$defs/sealedMeasurementInvariant" + } + ], + "$schema": "https://json-schema.org/draft/2020-12/schema", + "$id": "https://hyperlex.local/schemas/hyperlex/residual-threshold/surface-draw-receipt.schema.json" +} diff --git a/specs/007-hyperlexical-model/schemas/hyperlex/semantic-evidence-v2/representation-freeze.schema.json b/specs/007-hyperlexical-model/schemas/hyperlex/semantic-evidence-v2/representation-freeze.schema.json new file mode 100644 index 00000000..75f5f2c7 --- /dev/null +++ b/specs/007-hyperlexical-model/schemas/hyperlex/semantic-evidence-v2/representation-freeze.schema.json @@ -0,0 +1,69 @@ +{ + "title": "Representation family freeze", + "type": "object", + "additionalProperties": false, + "required": [ + "batch_size", + "composition_function", + "device_class", + "dtype", + "feature_definition_sha256", + "model_artifact_sha256", + "model_name", + "model_revision", + "random_seed", + "template_sha256", + "threading", + "tokenizer_sha256" + ], + "properties": { + "batch_size": { + "type": "integer", + "minimum": 1 + }, + "composition_function": { + "enum": [ + "normalized_mean_v1", + "weighted_mean_by_constituent_token_span", + "pairwise_relation_summary" + ] + }, + "device_class": { + "type": "string", + "minLength": 1 + }, + "dtype": { + "type": "string", + "minLength": 1 + }, + "feature_definition_sha256": { + "$ref": "common.schema.json#/$defs/sha256" + }, + "model_artifact_sha256": { + "$ref": "common.schema.json#/$defs/sha256" + }, + "model_name": { + "type": "string", + "minLength": 1 + }, + "model_revision": { + "type": "string", + "minLength": 1 + }, + "random_seed": { + "type": "integer" + }, + "template_sha256": { + "$ref": "common.schema.json#/$defs/sha256" + }, + "threading": { + "type": "string", + "minLength": 1 + }, + "tokenizer_sha256": { + "$ref": "common.schema.json#/$defs/sha256" + } + }, + "$schema": "https://json-schema.org/draft/2020-12/schema", + "$id": "https://hyperlex.local/schemas/hyperlex/semantic-evidence-v2/representation-freeze.schema.json" +} diff --git a/tests/shadow/test_residual_threshold_v1_calibration.py b/tests/shadow/test_residual_threshold_v1_calibration.py new file mode 100644 index 00000000..fd16d650 --- /dev/null +++ b/tests/shadow/test_residual_threshold_v1_calibration.py @@ -0,0 +1,45 @@ +"""Guards for the one-draw calibration runner.""" + +from __future__ import annotations + +import inspect +from pathlib import Path + +from hyperlexical.residual_threshold_v1 import MIN_CALIBRATION_HIGH, MIN_CALIBRATION_SECONDARY +from hyperlexical.residual_threshold_v1_calibration import ( + DRAW_SEED, + JSON_SCHEMA_DOCUMENT, + MANIFEST_FIELDS, + PER_CELL, + SAMPLING_RULE_ID, + draw_calibration, +) + + +def test_draw_rule_is_the_historical_prefix() -> None: + assert SAMPLING_RULE_ID == "positional_stratified_hash_prefix_v1" + assert PER_CELL == 2 + assert DRAW_SEED is None + assert JSON_SCHEMA_DOCUMENT is None + + +def test_draw_does_not_read_operator_buckets() -> None: + source = inspect.getsource(draw_calibration) + assert "operator_bucket" not in source + assert "select_threshold" not in source + + +def test_floors_come_from_the_frozen_procedure() -> None: + assert MIN_CALIBRATION_HIGH == 12 + assert MIN_CALIBRATION_SECONDARY == 8 + text = Path(inspect.getsourcefile(draw_calibration)).read_text(encoding="utf-8") + assert "0.672078" not in text + assert "0.4040327275" not in text + assert "measurement_sample.jsonl" not in text + assert "def measurement_acceptance" not in text + + +def test_manifest_contract_has_no_label_field() -> None: + assert "operator_bucket" not in MANIFEST_FIELDS + assert "calibration_row_id" in MANIFEST_FIELDS + assert "pwn30_synset" in MANIFEST_FIELDS diff --git a/tests/shadow/test_residual_threshold_v2.py b/tests/shadow/test_residual_threshold_v2.py new file mode 100644 index 00000000..0bdc07c4 --- /dev/null +++ b/tests/shadow/test_residual_threshold_v2.py @@ -0,0 +1,49 @@ +"""v2 preregisters a larger blind draw and does not relax v1.""" + +from __future__ import annotations + +import inspect +from decimal import Decimal +from pathlib import Path + +from hyperlexical.residual_threshold_v1 import ( + MIN_CALIBRATION_HIGH, + MIN_CALIBRATION_SECONDARY, + PRECISION_FLOOR, +) +from hyperlexical.residual_threshold_v2 import ( + CONSERVATIVE_READINESS, + REQUIRED_TARGET_READY, + V1_EXECUTION_STATE, + calibration_draw_size, + measurement_draw_size, + quota_from_readiness, +) + + +def test_quota_is_the_readiness_formula() -> None: + assert MIN_CALIBRATION_HIGH == 12 + assert MIN_CALIBRATION_SECONDARY == 8 + assert REQUIRED_TARGET_READY == 20 + assert CONSERVATIVE_READINESS == Decimal("0.20") + assert CONSERVATIVE_READINESS < (Decimal(6) / Decimal(27)) + assert quota_from_readiness() == 200 + assert calibration_draw_size() == 200 + assert measurement_draw_size() == 200 + assert PRECISION_FLOOR == Decimal("0.80") + + +def test_historical_rates_do_not_enter_the_formula() -> None: + source = inspect.getsource(quota_from_readiness) + assert "73" not in source + assert "225" not in source + assert "6" not in source + + +def test_preregistration_does_not_draw_or_refit() -> None: + text = Path(inspect.getsourcefile(quota_from_readiness)).read_text(encoding="utf-8") + assert "select_threshold(" not in text + assert "0.672078" not in text + assert V1_EXECUTION_STATE == "CALIBRATION_INSUFFICIENT_SUPPORT" + freeze = Path(inspect.getsourcefile(quota_from_readiness)).with_name("residual_threshold_v2_freeze.py").read_text(encoding="utf-8") + assert "select_threshold(" not in freeze From ddb302f224175511b6e8d2c4ee9b470a3c0ca58a Mon Sep 17 00:00:00 2001 From: Daniel Meyer Date: Mon, 28 Sep 2026 15:51:20 -0700 Subject: [PATCH 04/42] feat(007): land SELECT residual schemas and guards (4) --- .../schemas/hyperlex/select/CONFORMANCE.json | 42 +++++++++++ ...esidual-threshold-support-join.schema.json | 52 ++++++++++++++ ...ual-threshold-threshold-search.schema.json | 70 +++++++++++++++++++ .../semantic-evidence-v2-state-machine.md | 36 ++++++++++ 4 files changed, 200 insertions(+) create mode 100644 specs/007-hyperlexical-model/schemas/hyperlex/select/CONFORMANCE.json create mode 100644 specs/007-hyperlexical-model/schemas/residual-threshold/residual-threshold-support-join.schema.json create mode 100644 specs/007-hyperlexical-model/schemas/residual-threshold/residual-threshold-threshold-search.schema.json create mode 100644 specs/007-hyperlexical-model/semantic-evidence-v2-state-machine.md diff --git a/specs/007-hyperlexical-model/schemas/hyperlex/select/CONFORMANCE.json b/specs/007-hyperlexical-model/schemas/hyperlex/select/CONFORMANCE.json new file mode 100644 index 00000000..b21c2a48 --- /dev/null +++ b/specs/007-hyperlexical-model/schemas/hyperlex/select/CONFORMANCE.json @@ -0,0 +1,42 @@ +{ + "admission_schema_path": "specs/007-hyperlexical-model/schemas/hyperlex/select/admission.schema.json", + "admission_schema_sha256": "6f20efe73b74207243474724261c985d4e6dde888df497a1ef723dda02a42077", + "checked_artifacts": [ + "exp-20260926-select-004/admission-only-authorized.json", + "exp-20260926-select-004/admission-only-receipt.json", + "exp-20260926-select-004/preflight-authorized.json", + "exp-20260926-select-004/preflight.json", + "exp-20260926-select-003/threshold-authorization.json", + "exp-20260926-select-004/threshold-authorization.json", + "exp-20260927-select-005/source-001/THRESHOLD_AUTHORIZATION_PROPOSAL.json", + "exp-20260927-select-005/supply-001/THRESHOLD_AUTHORIZATION_PROPOSAL.json" + ], + "gaps": [ + { + "artifact": "exp-20260926-select-003/threshold-authorization.json", + "message": "'decision_thresholds' is a required property", + "schema": "hyperlex.threshold_authorization.v1" + }, + { + "artifact": "exp-20260926-select-003/threshold-authorization.json", + "message": "'sealed' is a required property", + "schema": "hyperlex.threshold_authorization.v1" + } + ], + "mode": "VALIDATE_ONLY", + "mutated": false, + "note": "Historical artifacts were read and not rewritten. The only gap is SELECT-003, which lacks sealed and decision_thresholds.", + "result": "SCHEMA_CONFORMANCE_GAP", + "schema": "hyperlex.schema_conformance_report.v1", + "threshold_authorization_schema_path": "specs/007-hyperlexical-model/schemas/hyperlex/select/threshold-authorization.schema.json", + "threshold_authorization_schema_sha256": "35a1b70cc805cf4271831a2414fd9ac85dcc15e5d23ec951d2d37665e89109a1", + "validated_artifacts": [ + "exp-20260926-select-004/admission-only-authorized.json", + "exp-20260926-select-004/admission-only-receipt.json", + "exp-20260926-select-004/preflight-authorized.json", + "exp-20260926-select-004/preflight.json", + "exp-20260926-select-004/threshold-authorization.json", + "exp-20260927-select-005/source-001/THRESHOLD_AUTHORIZATION_PROPOSAL.json", + "exp-20260927-select-005/supply-001/THRESHOLD_AUTHORIZATION_PROPOSAL.json" + ] +} diff --git a/specs/007-hyperlexical-model/schemas/residual-threshold/residual-threshold-support-join.schema.json b/specs/007-hyperlexical-model/schemas/residual-threshold/residual-threshold-support-join.schema.json new file mode 100644 index 00000000..27960085 --- /dev/null +++ b/specs/007-hyperlexical-model/schemas/residual-threshold/residual-threshold-support-join.schema.json @@ -0,0 +1,52 @@ +{ + "$schema": "https://json-schema.org/draft/2020-12/schema", + "$id": "https://hyperlex.local/schemas/residual-threshold-support-join.schema.json", + "title": "Residual-threshold support join row", + "type": "object", + "additionalProperties": false, + "required": [ + "calibration_row_id", + "operator_label", + "pos", + "pwn30_synset", + "residual_score", + "row_id", + "row_resolution_status", + "surface" + ], + "properties": { + "calibration_row_id": {"type": "integer", "minimum": 1}, + "operator_label": {"$ref": "residual-threshold-common.schema.json#/$defs/operator_label"}, + "pos": {"$ref": "residual-threshold-common.schema.json#/$defs/pos"}, + "pwn30_synset": {"$ref": "residual-threshold-common.schema.json#/$defs/pwn30_synset"}, + "residual_score": { + "anyOf": [ + {"type": "null"}, + {"$ref": "residual-threshold-common.schema.json#/$defs/residual_decimal"} + ] + }, + "row_id": {"$ref": "residual-threshold-common.schema.json#/$defs/row_id"}, + "row_resolution_status": {"$ref": "residual-threshold-common.schema.json#/$defs/row_resolution_status"}, + "surface": {"type": "string", "minLength": 1} + }, + "allOf": [ + { + "if": { + "properties": {"row_resolution_status": {"const": "UNKNOWN"}}, + "required": ["row_resolution_status"] + }, + "then": {"properties": {"residual_score": {"type": "null"}}} + }, + { + "if": { + "properties": {"row_resolution_status": {"const": "RESIDUAL_READY"}}, + "required": ["row_resolution_status"] + }, + "then": { + "properties": { + "residual_score": {"$ref": "residual-threshold-common.schema.json#/$defs/residual_decimal"} + } + } + } + ] +} diff --git a/specs/007-hyperlexical-model/schemas/residual-threshold/residual-threshold-threshold-search.schema.json b/specs/007-hyperlexical-model/schemas/residual-threshold/residual-threshold-threshold-search.schema.json new file mode 100644 index 00000000..0e22e88f --- /dev/null +++ b/specs/007-hyperlexical-model/schemas/residual-threshold/residual-threshold-threshold-search.schema.json @@ -0,0 +1,70 @@ +{ + "$schema": "https://json-schema.org/draft/2020-12/schema", + "$id": "https://hyperlex.local/schemas/residual-threshold-threshold-search.schema.json", + "title": "Residual-threshold search record", + "type": "object", + "required": [ + "candidate_count", + "candidates_evaluated", + "schema", + "search_executed", + "selected_threshold" + ], + "properties": { + "algorithm_id": {"type": "string"}, + "candidate_count": {"type": ["integer", "null"], "minimum": 0}, + "candidates_evaluated": {"type": "boolean"}, + "execution_001_search_sha256": {"$ref": "residual-threshold-common.schema.json#/$defs/sha256"}, + "execution_001_search_unchanged": {"type": "boolean"}, + "gate_pass_counts": { + "anyOf": [ + {"type": "null"}, + { + "type": "object", + "additionalProperties": {"type": "integer", "minimum": 0} + } + ] + }, + "json_schema_document": {"type": "null"}, + "json_schema_exists": {"type": "boolean"}, + "precision_denominator": {"type": "string"}, + "reason": {"type": "string"}, + "schema": {"type": "string", "minLength": 1}, + "search_executed": {"type": "boolean"}, + "selected_threshold": { + "anyOf": [ + {"type": "null"}, + {"$ref": "residual-threshold-common.schema.json#/$defs/residual_decimal"} + ] + }, + "selection_algorithm_id": {"type": "string"} + }, + "allOf": [ + { + "if": { + "properties": {"search_executed": {"const": false}}, + "required": ["search_executed"] + }, + "then": { + "properties": { + "candidate_count": {"type": "null"}, + "candidates_evaluated": {"const": false}, + "selected_threshold": {"type": "null"} + } + } + }, + { + "if": { + "properties": {"search_executed": {"const": true}}, + "required": ["search_executed"] + }, + "then": { + "properties": { + "candidate_count": {"type": "integer", "minimum": 0}, + "candidates_evaluated": {"const": true} + }, + "required": ["candidate_count", "gate_pass_counts"] + } + } + ] +} diff --git a/specs/007-hyperlexical-model/semantic-evidence-v2-state-machine.md b/specs/007-hyperlexical-model/semantic-evidence-v2-state-machine.md new file mode 100644 index 00000000..62ad5bad --- /dev/null +++ b/specs/007-hyperlexical-model/semantic-evidence-v2-state-machine.md @@ -0,0 +1,36 @@ +# Semantic evidence v2 state machine + +This machine belongs only to `RUNE.SEMANTIC_COMPOSITIONALITY_EVIDENCE.v2`. + +```text +SEMANTIC_EVIDENCE_V2_SPEC_DRAFTED + -> SEMANTIC_EVIDENCE_V2_PREREGISTERED + -> DEVELOPMENT_SURFACE_FROZEN + -> REPRESENTATION_EVALUATION_COMPLETE + -> REPRESENTATION_SELECTED + -> CALIBRATION_SURFACE_FROZEN + -> CALIBRATION_COMPLETE + -> THRESHOLD_FROZEN + -> MEASUREMENT_EXECUTED +``` + +Failure states: + +```text +INSUFFICIENT_RESOLUTION_COVERAGE +NO_REPRESENTATION_IMPROVEMENT +NO_DIRECTIONAL_SIGNAL +CALIBRATION_CONFOUND_REVIEW +NO_THRESHOLD_PASSES_PRECISION_GATE +MEASUREMENT_FAILURE +``` + +Current state: `SEMANTIC_EVIDENCE_V2_SPEC_DRAFTED`. + +`SEMANTIC_EVIDENCE_V2_PREREGISTERED` is not authorized. Preregistration has to freeze model name, revision, artifact hashes, tokenizer hash, template hash, dtype, device class, batch size, seed, threading, feature definitions, and composition-function version before any evaluation. Those revisions are not frozen in this draft. The maximum comparison is three representation families: MiniLM baseline, ModernBERT sentence or gloss pooling, and one domain-neutral encoder with strong STS behavior. No open-ended model search. + +`THRESHOLD_V2_CALIBRATION` may be read for postmortem and for feature hypothesis generation. If those rows are used to choose the successor architecture, they are `EVAL_SPENT` for any later threshold selection. They are not a clean calibration surface. + +Development, calibration, and measurement identities and synsets for the successor must be disjoint from each other and must not automatically include the sealed v2 measurement surface. Its disposition stays `RETAIN_FOR_V2_ONLY` until a separate governance decision says otherwise. + +No trainable composition layer and no model fine-tune belong in the first evidence evaluation. The goal of that evaluation is representational sufficiency before a predictor is trained. From 653c87dd47be95cbd8b2e90dcea3d8e1e5dc7479 Mon Sep 17 00:00:00 2001 From: Daniel Meyer Date: Mon, 28 Sep 2026 15:51:38 -0700 Subject: [PATCH 05/42] feat(007): land SELECT residual schemas and guards (5) --- .../test_residual_threshold_v2_calibration.py | 53 +++++++++++++++++++ 1 file changed, 53 insertions(+) create mode 100644 tests/shadow/test_residual_threshold_v2_calibration.py diff --git a/tests/shadow/test_residual_threshold_v2_calibration.py b/tests/shadow/test_residual_threshold_v2_calibration.py new file mode 100644 index 00000000..60b0dd83 --- /dev/null +++ b/tests/shadow/test_residual_threshold_v2_calibration.py @@ -0,0 +1,53 @@ +"""v2 calibration execution keeps the frozen gates and the sealed measurement surface.""" + +from __future__ import annotations + +import inspect +from pathlib import Path + +from hyperlexical.residual_threshold_v1 import MIN_CALIBRATION_HIGH, MIN_CALIBRATION_SECONDARY +import pytest + +from hyperlexical.residual_threshold_v2_calibration import ( + EXPECTED_MEASUREMENT, + _resolve, + _score, + lesk_tie_rows, + support_passed, +) + + +def test_structural_ambiguity_does_not_enter_glossbert() -> None: + rows = [ + {"constituent_index": 0, "resolution_method": "STRUCTURAL_EXACT", "resolution_status": "AMBIGUOUS"}, + {"constituent_index": 1, "resolution_method": "EXTENDED_LESK_V1", "resolution_status": "AMBIGUOUS"}, + {"constituent_index": 2, "resolution_method": "EXTENDED_LESK_V1", "resolution_status": "RESOLVED"}, + {"constituent_index": None, "resolution_method": "NONE", "resolution_status": None}, + ] + kept = lesk_tie_rows(rows) + assert [row["constituent_index"] for row in kept] == [1] + with pytest.raises(SystemExit): + lesk_tie_rows([{"constituent_index": 0, "resolution_method": "UNIQUE_LEMMA", "resolution_status": "AMBIGUOUS"}]) + + +def test_support_floors_are_unchanged() -> None: + assert MIN_CALIBRATION_HIGH == 12 + assert MIN_CALIBRATION_SECONDARY == 8 + assert support_passed(12, 8) is True + assert support_passed(11, 8) is False + assert support_passed(12, 7) is False + + +def test_execution_module_does_not_retune_or_open_measurement() -> None: + path = Path(inspect.getsourcefile(support_passed)) + text = path.read_text(encoding="utf-8") + assert EXPECTED_MEASUREMENT in text + assert "select_threshold(" in text + assert "0.672078" not in text + assert "0.4040327275" not in text + assert "semantic_noncompositional = YES" not in text + assert "semantic_noncompositional = NO" not in text + assert "_label_index" not in inspect.getsource(_resolve) + assert "_label_index" not in inspect.getsource(_score) + assert "MEASUREMENT_MANIFEST" not in inspect.getsource(_resolve) + assert "MEASUREMENT_MANIFEST" not in inspect.getsource(_score) From 6ed6f880fa4bb26cea320567bf27db5e3e9723ff Mon Sep 17 00:00:00 2001 From: Daniel Meyer Date: Mon, 28 Sep 2026 15:54:14 -0700 Subject: [PATCH 06/42] feat(007): land SELECT residual and schedule work (6) --- .../direction-decision.schema.json | 105 ++++++++++++++ .../support-join-row.schema.json | 92 ++++++++++++ .../threshold-decision.schema.json | 109 ++++++++++++++ .../threshold-search.schema.json | 134 ++++++++++++++++++ .../threshold-authorization.schema.json | 76 ++++++++++ .../test_residual_threshold_v2_labeling.py | 80 +++++++++++ 6 files changed, 596 insertions(+) create mode 100644 specs/007-hyperlexical-model/schemas/hyperlex/residual-threshold/direction-decision.schema.json create mode 100644 specs/007-hyperlexical-model/schemas/hyperlex/residual-threshold/support-join-row.schema.json create mode 100644 specs/007-hyperlexical-model/schemas/hyperlex/residual-threshold/threshold-decision.schema.json create mode 100644 specs/007-hyperlexical-model/schemas/hyperlex/residual-threshold/threshold-search.schema.json create mode 100644 specs/007-hyperlexical-model/schemas/hyperlex/select/threshold-authorization.schema.json create mode 100644 tests/shadow/test_residual_threshold_v2_labeling.py diff --git a/specs/007-hyperlexical-model/schemas/hyperlex/residual-threshold/direction-decision.schema.json b/specs/007-hyperlexical-model/schemas/hyperlex/residual-threshold/direction-decision.schema.json new file mode 100644 index 00000000..050f365e --- /dev/null +++ b/specs/007-hyperlexical-model/schemas/hyperlex/residual-threshold/direction-decision.schema.json @@ -0,0 +1,105 @@ +{ + "title": "Direction decision", + "type": "object", + "additionalProperties": false, + "allOf": [ + { + "$ref": "common.schema.json#/$defs/unfrozenThresholdInvariant" + } + ], + "required": [ + "admitted", + "analysis_scope", + "analysis_sha256", + "confound_analysis_run", + "direction_result", + "evaluation_id", + "gold", + "json_schema_document", + "json_schema_exists", + "measurement_state", + "next_legal_transition", + "next_transition_authorized", + "redraw_authorized", + "runtime_integration", + "select_005_authorized", + "selected_source", + "settled", + "state", + "threshold_frozen", + "threshold_search_run", + "threshold_value", + "uncertainty" + ], + "properties": { + "admitted": { + "const": 0 + }, + "analysis_scope": { + "const": "DIRECTION_ONLY" + }, + "analysis_sha256": { + "$ref": "common.schema.json#/$defs/sha256" + }, + "confound_analysis_run": { + "const": false + }, + "direction_result": { + "$ref": "common.schema.json#/$defs/direction_result" + }, + "evaluation_id": { + "type": "string", + "minLength": 1 + }, + "gold": { + "const": 0 + }, + "json_schema_document": { + "type": "null" + }, + "json_schema_exists": { + "const": false + }, + "measurement_state": { + "$ref": "common.schema.json#/$defs/measurement_state" + }, + "next_legal_transition": { + "type": "string" + }, + "next_transition_authorized": { + "const": false + }, + "redraw_authorized": { + "const": false + }, + "runtime_integration": { + "const": false + }, + "select_005_authorized": { + "const": false + }, + "selected_source": { + "const": "none" + }, + "settled": { + "const": 0 + }, + "state": { + "type": "string" + }, + "threshold_frozen": { + "const": false + }, + "threshold_search_run": { + "const": false + }, + "threshold_value": { + "type": "null" + }, + "uncertainty": { + "const": "UNCERTAINTY_INTERVAL_NOT_COMPUTED" + } + }, + "$schema": "https://json-schema.org/draft/2020-12/schema", + "$id": "https://hyperlex.local/schemas/hyperlex/residual-threshold/direction-decision.schema.json" +} diff --git a/specs/007-hyperlexical-model/schemas/hyperlex/residual-threshold/support-join-row.schema.json b/specs/007-hyperlexical-model/schemas/hyperlex/residual-threshold/support-join-row.schema.json new file mode 100644 index 00000000..2443a8c5 --- /dev/null +++ b/specs/007-hyperlexical-model/schemas/hyperlex/residual-threshold/support-join-row.schema.json @@ -0,0 +1,92 @@ +{ + "title": "Support join row", + "type": "object", + "additionalProperties": false, + "required": [ + "calibration_row_id", + "operator_label", + "pos", + "pwn30_synset", + "residual_score", + "row_id", + "row_resolution_status", + "surface" + ], + "properties": { + "calibration_row_id": { + "type": "integer", + "minimum": 1 + }, + "operator_label": { + "$ref": "common.schema.json#/$defs/operator_label" + }, + "pos": { + "$ref": "common.schema.json#/$defs/pos" + }, + "pwn30_synset": { + "$ref": "common.schema.json#/$defs/pwn30_synset" + }, + "residual_score": { + "anyOf": [ + { + "type": "null" + }, + { + "$ref": "common.schema.json#/$defs/residual_decimal" + } + ] + }, + "row_id": { + "$ref": "common.schema.json#/$defs/row_id" + }, + "row_resolution_status": { + "$ref": "common.schema.json#/$defs/row_resolution_status" + }, + "surface": { + "type": "string", + "minLength": 1 + } + }, + "allOf": [ + { + "if": { + "properties": { + "row_resolution_status": { + "const": "UNKNOWN" + } + }, + "required": [ + "row_resolution_status" + ] + }, + "then": { + "properties": { + "residual_score": { + "type": "null" + } + } + } + }, + { + "if": { + "properties": { + "row_resolution_status": { + "const": "RESIDUAL_READY" + } + }, + "required": [ + "row_resolution_status" + ] + }, + "then": { + "properties": { + "residual_score": { + "$ref": "common.schema.json#/$defs/residual_decimal" + } + } + } + } + ], + "$schema": "https://json-schema.org/draft/2020-12/schema", + "$id": "https://hyperlex.local/schemas/hyperlex/residual-threshold/support-join-row.schema.json" +} diff --git a/specs/007-hyperlexical-model/schemas/hyperlex/residual-threshold/threshold-decision.schema.json b/specs/007-hyperlexical-model/schemas/hyperlex/residual-threshold/threshold-decision.schema.json new file mode 100644 index 00000000..40b4b64d --- /dev/null +++ b/specs/007-hyperlexical-model/schemas/hyperlex/residual-threshold/threshold-decision.schema.json @@ -0,0 +1,109 @@ +{ + "title": "Threshold decision", + "description": "Unfrozen decisions require a null threshold. A frozen decision requires the numeric residual string and its frozen metrics. No frozen success record was emitted.", + "allOf": [ + { + "$ref": "common.schema.json#/$defs/unfrozenThresholdInvariant" + }, + { + "$ref": "common.schema.json#/$defs/frozenThresholdInvariant" + } + ], + "oneOf": [ + { + "$ref": "#/$defs/unfrozen" + }, + { + "$ref": "#/$defs/frozen" + } + ], + "$defs": { + "unfrozen": { + "type": "object", + "additionalProperties": true, + "required": [ + "schema", + "threshold_frozen", + "threshold_value" + ], + "properties": { + "schema": { + "type": "string", + "minLength": 1 + }, + "threshold_frozen": { + "const": false + }, + "threshold_value": { + "type": "null" + } + } + }, + "frozen": { + "type": "object", + "additionalProperties": true, + "required": [ + "schema", + "threshold_frozen", + "threshold_value", + "false_high", + "high_recall", + "leave_one_out_result", + "precision", + "predicted_yes_support", + "quarantine_predicted_yes", + "reject_predicted_yes", + "true_high", + "wilson_lower", + "wilson_upper" + ], + "properties": { + "schema": { + "type": "string", + "minLength": 1 + }, + "threshold_frozen": { + "const": true + }, + "threshold_value": { + "$ref": "common.schema.json#/$defs/residual_decimal" + }, + "false_high": { + "type": "integer", + "minimum": 0 + }, + "high_recall": { + "$ref": "common.schema.json#/$defs/effect_decimal" + }, + "leave_one_out_result": { + "const": "PASS" + }, + "precision": { + "$ref": "common.schema.json#/$defs/effect_decimal" + }, + "predicted_yes_support": { + "type": "integer", + "minimum": 8 + }, + "quarantine_predicted_yes": { + "const": 0 + }, + "reject_predicted_yes": { + "const": 0 + }, + "true_high": { + "type": "integer", + "minimum": 0 + }, + "wilson_lower": { + "$ref": "common.schema.json#/$defs/effect_decimal" + }, + "wilson_upper": { + "$ref": "common.schema.json#/$defs/effect_decimal" + } + } + } + }, + "$schema": "https://json-schema.org/draft/2020-12/schema", + "$id": "https://hyperlex.local/schemas/hyperlex/residual-threshold/threshold-decision.schema.json" +} diff --git a/specs/007-hyperlexical-model/schemas/hyperlex/residual-threshold/threshold-search.schema.json b/specs/007-hyperlexical-model/schemas/hyperlex/residual-threshold/threshold-search.schema.json new file mode 100644 index 00000000..41e7b6d7 --- /dev/null +++ b/specs/007-hyperlexical-model/schemas/hyperlex/residual-threshold/threshold-search.schema.json @@ -0,0 +1,134 @@ +{ + "$schema": "https://json-schema.org/draft/2020-12/schema", + "$id": "https://hyperlex.local/schemas/hyperlex/residual-threshold/threshold-search.schema.json", + "title": "Residual-threshold search record", + "type": "object", + "required": [ + "candidate_count", + "candidates_evaluated", + "schema", + "search_executed", + "selected_threshold" + ], + "properties": { + "algorithm_id": { + "type": "string" + }, + "candidate_count": { + "type": [ + "integer", + "null" + ], + "minimum": 0 + }, + "candidates_evaluated": { + "type": "boolean" + }, + "execution_001_search_sha256": { + "$ref": "common.schema.json#/$defs/sha256" + }, + "execution_001_search_unchanged": { + "type": "boolean" + }, + "gate_pass_counts": { + "anyOf": [ + { + "type": "null" + }, + { + "type": "object", + "additionalProperties": { + "type": "integer", + "minimum": 0 + } + } + ] + }, + "json_schema_document": { + "type": "null" + }, + "json_schema_exists": { + "type": "boolean" + }, + "precision_denominator": { + "type": "string" + }, + "reason": { + "type": "string" + }, + "schema": { + "type": "string", + "minLength": 1 + }, + "search_executed": { + "type": "boolean" + }, + "selected_threshold": { + "anyOf": [ + { + "type": "null" + }, + { + "$ref": "common.schema.json#/$defs/residual_decimal" + } + ] + }, + "selection_algorithm_id": { + "type": "string" + } + }, + "allOf": [ + { + "if": { + "properties": { + "search_executed": { + "const": false + } + }, + "required": [ + "search_executed" + ] + }, + "then": { + "properties": { + "candidate_count": { + "type": "null" + }, + "candidates_evaluated": { + "const": false + }, + "selected_threshold": { + "type": "null" + } + } + } + }, + { + "if": { + "properties": { + "search_executed": { + "const": true + } + }, + "required": [ + "search_executed" + ] + }, + "then": { + "properties": { + "candidate_count": { + "type": "integer", + "minimum": 0 + }, + "candidates_evaluated": { + "const": true + } + }, + "required": [ + "candidate_count", + "gate_pass_counts" + ] + } + } + ] +} diff --git a/specs/007-hyperlexical-model/schemas/hyperlex/select/threshold-authorization.schema.json b/specs/007-hyperlexical-model/schemas/hyperlex/select/threshold-authorization.schema.json new file mode 100644 index 00000000..ab6dfe21 --- /dev/null +++ b/specs/007-hyperlexical-model/schemas/hyperlex/select/threshold-authorization.schema.json @@ -0,0 +1,76 @@ +{ + "$schema": "https://json-schema.org/draft/2020-12/schema", + "$id": "https://hyperlex.local/schemas/hyperlex.threshold_authorization.v1", + "title": "SELECT metric threshold authorization", + "description": "Contract shape already read by admission. This schema does not assign SELECT-005 numbers.", + "type": "object", + "additionalProperties": false, + "required": [ + "decision_thresholds", + "experiment_id", + "schema", + "sealed" + ], + "properties": { + "schema": {"const": "hyperlex.threshold_authorization.v1"}, + "experiment_id": {"type": "string", "minLength": 1}, + "sealed": {"type": "boolean"}, + "artifact_sha256": {"$ref": "#/$defs/sha256"}, + "authorization_state": { + "enum": ["BLOCKED_PENDING_OPERATOR_AUTHORIZATION", "SEALED"] + }, + "comparison_baseline": {"type": "string"}, + "evaluation_quality": {"type": "string"}, + "decision_thresholds": { + "type": "object", + "additionalProperties": {"type": "number"}, + "properties": { + "classification_accuracy": {"type": "number"}, + "classify_macro_f1_nonnone": {"type": "number"}, + "observed_label_accuracy": {"type": "number"}, + "unbind_clean_exact": {"type": "number"} + } + }, + "baseline": {"type": "object"}, + "decision_rule": {"type": "object"}, + "derivation": {"type": "string"}, + "direction": {"type": "string"}, + "inherited_from_select_001": {"type": "boolean"}, + "inherited_from_select_002": {"type": "boolean"}, + "inherited_from_select_003": {"type": "boolean"}, + "inherited_from_select_004": {"type": "boolean"}, + "metrics": {"type": "array"}, + "not_seed_morph78": {"type": "boolean"}, + "operator": {"type": "object"}, + "preregistration_edited": {"type": "boolean"}, + "preregistration_sha256": {"$ref": "#/$defs/sha256"}, + "primary_comparison_supported_by_schedule_design": {"type": "string"}, + "primary_metric": {"type": "object"}, + "provenance": {"type": "string"}, + "reason_unsealed": {"type": "string"}, + "quality_limit": {"type": "object"}, + "repository_commit_at_authorization": {"type": "string"}, + "repository_dirty_state": {"type": "string"}, + "sealed_preregistration_commit": {"type": "string"}, + "state": {"type": "string"}, + "threshold_authorization_json_schema_exists": {"type": "boolean"}, + "timestamp": {"type": "string"}, + "training_launch_authorized": {"type": "boolean"} + }, + "$defs": { + "sha256": {"type": "string", "pattern": "^[0-9a-f]{64}$"} + }, + "allOf": [ + { + "if": { + "properties": {"sealed": {"const": true}}, + "required": ["sealed"] + }, + "then": { + "properties": { + "decision_thresholds": {"minProperties": 1} + } + } + } + ] +} diff --git a/tests/shadow/test_residual_threshold_v2_labeling.py b/tests/shadow/test_residual_threshold_v2_labeling.py new file mode 100644 index 00000000..bd6862a8 --- /dev/null +++ b/tests/shadow/test_residual_threshold_v2_labeling.py @@ -0,0 +1,80 @@ +"""Blind v2 operator labeling stays off the frozen scores and the measurement surface.""" + +from __future__ import annotations + +import inspect +from pathlib import Path + +import pytest + +from hyperlexical.residual_threshold_v2_labeling import ( + EXPECTED_MEASUREMENT, + LABEL_KEYS, + PACKET_KEYS, + assert_packet_blind, + forbidden_key, + label_rows, + packet_rows, +) +from hyperlexical.screen_eval import OPERATOR_BUCKETS, REASONS + + +def _manifest_row(index: int) -> dict: + return { + "frozen_gloss": f"gloss {index}", + "global_draw_order": index, + "pos": "noun", + "pwn30_synset": f"noun:{index:08d}", + "row_id": f"row-{index}", + "surface": f"surface {index}", + "token_count": 2, + } + + +def test_packet_rows_expose_only_canonical_review_fields() -> None: + rows = packet_rows([_manifest_row(1), _manifest_row(2)]) + assert [row["calibration_row_id"] for row in rows] == [1, 2] + assert set(rows[0]) == PACKET_KEYS + for key in rows[0]: + assert forbidden_key(key) is None + + +def test_packet_blindness_refuses_a_readiness_field() -> None: + row = packet_rows([_manifest_row(1)])[0] + row["residual_score"] = 0.1 + with pytest.raises(SystemExit): + assert_packet_blind([row]) + + +def test_labels_use_the_canonical_reason_ontology() -> None: + packet = packet_rows([_manifest_row(1)]) + decision = { + "calibration_row_id": 1, + "operator_evidence": "STRONG_IDIOM", + "operator_label": "HIGH", + "operator_note": "fixed nonliteral idiom", + "row_id": "row-1", + } + labels = label_rows(packet, [decision]) + assert set(labels[0]) == LABEL_KEYS + assert labels[0]["operator_label"] == "HIGH" + assert labels[0]["review_status"] == "REVIEWED" + assert "UNRESOLVED" in OPERATOR_BUCKETS + assert "INSUFFICIENT_SIGNAL" in REASONS["UNRESOLVED"] + with pytest.raises(SystemExit): + label_rows(packet, [{**decision, "operator_evidence": "STRONG_IDIOM", "operator_label": "REJECT"}]) + + +def test_labeling_module_does_not_score_or_search() -> None: + path = Path(inspect.getsourcefile(packet_rows)) + text = path.read_text(encoding="utf-8") + assert EXPECTED_MEASUREMENT in text + assert "select_threshold(" not in text + assert "support_passed(" not in text + assert "sentence-transformers" not in text + assert "all-MiniLM" not in text + assert "load_wordnet(" not in text + assert "glossbert_scores_exposed" in text + assert "joined_to_readiness" in text + assert "LABELS_FROZEN" in text + assert "CALIBRATION_INSUFFICIENT_SUPPORT" in text From 604b9a9ecf3038919a34ac37636789d97c38fc68 Mon Sep 17 00:00:00 2001 From: Daniel Meyer Date: Mon, 28 Sep 2026 15:57:04 -0700 Subject: [PATCH 07/42] feat(007): land SELECT residual and schedule work (7) --- .../shadow/hyperlexical/select_005_harvest.py | 75 +++++++++ .../hyperlexical/select_contract_schema.py | 58 +++++++ .../completion-receipt.schema.json | 150 +++++++++++++++++ .../direction-analysis.schema.json | 155 ++++++++++++++++++ .../operator-label-row.schema.json | 126 ++++++++++++++ .../resolution-row.schema.json | 135 +++++++++++++++ .../residual-threshold/score-row.schema.json | 129 +++++++++++++++ .../surface-manifest-row.schema.json | 135 +++++++++++++++ .../select/ROUTING_CONTRACT_RECEIPT.json | 59 +++++++ .../hyperlex/select/admission.schema.json | 102 ++++++++++++ .../residual-threshold-common.schema.json | 115 +++++++++++++ ...l-threshold-completion-receipt.schema.json | 77 +++++++++ ...al-threshold-confound-analysis.schema.json | 103 ++++++++++++ ...l-threshold-direction-analysis.schema.json | 79 +++++++++ ...ual-threshold-surface-manifest.schema.json | 73 +++++++++ .../semantic-evidence-v2.md | 37 +++++ .../training-dependency-review.md | 23 +++ .../test_residual_threshold_v2_completion.py | 91 ++++++++++ 18 files changed, 1722 insertions(+) create mode 100644 scripts/shadow/hyperlexical/select_005_harvest.py create mode 100644 scripts/shadow/hyperlexical/select_contract_schema.py create mode 100644 specs/007-hyperlexical-model/schemas/hyperlex/residual-threshold/completion-receipt.schema.json create mode 100644 specs/007-hyperlexical-model/schemas/hyperlex/residual-threshold/direction-analysis.schema.json create mode 100644 specs/007-hyperlexical-model/schemas/hyperlex/residual-threshold/operator-label-row.schema.json create mode 100644 specs/007-hyperlexical-model/schemas/hyperlex/residual-threshold/resolution-row.schema.json create mode 100644 specs/007-hyperlexical-model/schemas/hyperlex/residual-threshold/score-row.schema.json create mode 100644 specs/007-hyperlexical-model/schemas/hyperlex/residual-threshold/surface-manifest-row.schema.json create mode 100644 specs/007-hyperlexical-model/schemas/hyperlex/select/ROUTING_CONTRACT_RECEIPT.json create mode 100644 specs/007-hyperlexical-model/schemas/hyperlex/select/admission.schema.json create mode 100644 specs/007-hyperlexical-model/schemas/residual-threshold/residual-threshold-common.schema.json create mode 100644 specs/007-hyperlexical-model/schemas/residual-threshold/residual-threshold-completion-receipt.schema.json create mode 100644 specs/007-hyperlexical-model/schemas/residual-threshold/residual-threshold-confound-analysis.schema.json create mode 100644 specs/007-hyperlexical-model/schemas/residual-threshold/residual-threshold-direction-analysis.schema.json create mode 100644 specs/007-hyperlexical-model/schemas/residual-threshold/residual-threshold-surface-manifest.schema.json create mode 100644 specs/007-hyperlexical-model/semantic-evidence-v2.md create mode 100644 specs/007-hyperlexical-model/training-dependency-review.md create mode 100644 tests/shadow/test_residual_threshold_v2_completion.py diff --git a/scripts/shadow/hyperlexical/select_005_harvest.py b/scripts/shadow/hyperlexical/select_005_harvest.py new file mode 100644 index 00000000..85a051e6 --- /dev/null +++ b/scripts/shadow/hyperlexical/select_005_harvest.py @@ -0,0 +1,75 @@ +"""SELECT-005 harvest authorization stops when the exposure snapshot is stale. + +This module does not fetch, harvest, settle, recensus, or train. +It does not modify the routing contract. A fresh fence is permission to +start a later harvest. It is not a harvest. +""" + +from __future__ import annotations + +from typing import Any, Mapping, Sequence + +from .select_005_completion import BOX_SNAPSHOT_LIMIT_H +from .select_005_reserve import EXPERIMENT_ID, canonical_json, sha256_text + +TRANSITION = "SELECT_005_FRESH_SOURCE_HARVEST_AND_ROUTING_AUTHORIZATION" +STALE = "EXPOSURE_SNAPSHOT_STALE" +FRESH = "EXPOSURE_SNAPSHOT_FRESH" +REQUIRED_KINDS = ("vern", "boxhash") + + +def exposure_fence( + snapshots: Sequence[Mapping[str, Any]], + *, + compared_at_unix: str, + limit_h: float = BOX_SNAPSHOT_LIMIT_H, +) -> dict[str, Any]: + """Return the exposure decision for one frozen snapshot pair. + + Ages are ``compared_at_unix`` minus each ``mtime_unix``. Both values are + decimal strings, so the record does not read a clock and does not fetch. + """ + if float(limit_h) > BOX_SNAPSHOT_LIMIT_H: + raise SystemExit("REFUSE: exposure fence cannot be widened") + rows = [] + for item in snapshots: + mtime = str(item.get("mtime_unix") or "") + age_h = (float(compared_at_unix) - float(mtime)) / 3600.0 + rows.append( + { + "age_hours": f"{age_h:.6f}", + "kind": str(item.get("kind") or ""), + "mtime_unix": mtime, + "path": str(item.get("path") or ""), + "sha256": str(item.get("sha256") or ""), + "snapshot_id": str(item.get("snapshot_id") or ""), + } + ) + rows.sort(key=lambda row: (row["kind"], row["snapshot_id"], row["path"])) + present = {row["kind"] for row in rows} + missing = [kind for kind in REQUIRED_KINDS if kind not in present] + worst = max((row["age_hours"] for row in rows), default=None) + stale = bool(missing) or worst is None or float(worst) > float(limit_h) + binding = None + if rows: + binding = max(rows, key=lambda row: (float(row["age_hours"]), row["kind"])) + record = { + "binding_snapshot": binding, + "compared_at_unix": str(compared_at_unix), + "experiment_id": EXPERIMENT_ID, + "exposure_fence_bypassed": False, + "fetch_authorized": not stale, + "harvest_executed": False, + "limit_hours": f"{float(limit_h):.1f}", + "missing_kinds": missing, + "network_requests": 0, + "rows_acquired": 0, + "snapshots": rows, + "state": STALE if stale else FRESH, + "stop_before": "source_fetch" if stale else None, + "transition": TRANSITION, + "worst_age_hours": worst, + } + body = {key: value for key, value in record.items() if key != "record_sha256"} + record["record_sha256"] = sha256_text(canonical_json(body)) + return record diff --git a/scripts/shadow/hyperlexical/select_contract_schema.py b/scripts/shadow/hyperlexical/select_contract_schema.py new file mode 100644 index 00000000..bf83e3f0 --- /dev/null +++ b/scripts/shadow/hyperlexical/select_contract_schema.py @@ -0,0 +1,58 @@ +"""JSON Schema checks for routing, admission, and threshold authorization. + +Historical artifacts are validated and not rewritten. +""" + +from __future__ import annotations + +import json +from pathlib import Path +from typing import Any, Mapping + +from jsonschema import Draft202012Validator + +REPO_ROOT = Path(__file__).resolve().parents[3] +SCHEMA_DIR = REPO_ROOT / "specs/007-hyperlexical-model/schemas/hyperlex/select" +ROUTING_SCHEMA_PATH = SCHEMA_DIR / "eval-routing.schema.json" +THRESHOLD_SCHEMA_PATH = SCHEMA_DIR / "threshold-authorization.schema.json" +ADMISSION_SCHEMA_PATH = SCHEMA_DIR / "admission.schema.json" + + +def _validator(path: Path) -> Draft202012Validator: + schema = json.loads(path.read_text(encoding="utf-8")) + Draft202012Validator.check_schema(schema) + return Draft202012Validator(schema) + + +def routing_schema_errors(instance: Mapping[str, Any]) -> list[str]: + return sorted(error.message for error in _validator(ROUTING_SCHEMA_PATH).iter_errors(instance)) + + +def threshold_schema_errors(instance: Mapping[str, Any]) -> list[str]: + return sorted(error.message for error in _validator(THRESHOLD_SCHEMA_PATH).iter_errors(instance)) + + +def admission_schema_errors(instance: Mapping[str, Any]) -> list[str]: + return sorted(error.message for error in _validator(ADMISSION_SCHEMA_PATH).iter_errors(instance)) + + +def historical_schema_report( + admission_artifacts: Mapping[str, Mapping[str, Any]], + threshold_artifacts: Mapping[str, Mapping[str, Any]], +) -> dict[str, Any]: + """VALIDATE_ONLY. Names and messages, no artifact rewrite.""" + gaps = [] + for name, payload in sorted(admission_artifacts.items()): + for message in admission_schema_errors(payload): + gaps.append({"artifact": name, "schema": "hyperlex.admission.v1", "message": message}) + for name, payload in sorted(threshold_artifacts.items()): + for message in threshold_schema_errors(payload): + gaps.append( + {"artifact": name, "schema": "hyperlex.threshold_authorization.v1", "message": message} + ) + return { + "gaps": gaps, + "mode": "VALIDATE_ONLY", + "mutated": False, + "result": "SCHEMA_CONFORMANCE_GAP" if gaps else "HISTORICAL_VALIDATION_COMPLETE", + } diff --git a/specs/007-hyperlexical-model/schemas/hyperlex/residual-threshold/completion-receipt.schema.json b/specs/007-hyperlexical-model/schemas/hyperlex/residual-threshold/completion-receipt.schema.json new file mode 100644 index 00000000..5a95acd8 --- /dev/null +++ b/specs/007-hyperlexical-model/schemas/hyperlex/residual-threshold/completion-receipt.schema.json @@ -0,0 +1,150 @@ +{ + "$schema": "https://json-schema.org/draft/2020-12/schema", + "$id": "https://hyperlex.local/schemas/hyperlex/residual-threshold/completion-receipt.schema.json", + "title": "Residual-threshold calibration completion receipt", + "type": "object", + "additionalProperties": false, + "allOf": [ + { + "$ref": "common.schema.json#/$defs/unfrozenThresholdInvariant" + }, + { + "$ref": "common.schema.json#/$defs/frozenThresholdInvariant" + }, + { + "$ref": "common.schema.json#/$defs/sealedMeasurementInvariant" + } + ], + "required": [ + "calibration_redraw", + "confound_gate", + "direction_gate", + "json_schema_document", + "json_schema_exists", + "measurement_eligible", + "measurement_state", + "operator_labeling", + "runtime_integration", + "select_005_authorized", + "selected_source", + "support_gate", + "surface_draw", + "threshold_frozen", + "threshold_search", + "threshold_value", + "training_authorized", + "training_readiness" + ], + "properties": { + "calibration_redraw": { + "const": false + }, + "confound_gate": { + "enum": [ + "PASS", + "STOP", + "NOT_RUN" + ] + }, + "direction_gate": { + "enum": [ + "SUPPORTED_DIRECTION", + "INVERTED_DIRECTION", + "NO_DIRECTIONAL_SEPARATION", + "NOT_RUN" + ] + }, + "false_high": { + "type": "integer", + "minimum": 0 + }, + "high_recall": { + "$ref": "common.schema.json#/$defs/effect_decimal" + }, + "json_schema_document": { + "type": "null" + }, + "json_schema_exists": { + "const": false + }, + "leave_one_out_result": { + "const": "PASS" + }, + "measurement_eligible": { + "type": "boolean" + }, + "measurement_state": { + "$ref": "common.schema.json#/$defs/measurement_state" + }, + "operator_labeling": { + "const": "LABELS_FROZEN" + }, + "precision": { + "$ref": "common.schema.json#/$defs/effect_decimal" + }, + "predicted_yes_support": { + "type": "integer", + "minimum": 0 + }, + "quarantine_predicted_yes": { + "type": "integer", + "minimum": 0 + }, + "reject_predicted_yes": { + "type": "integer", + "minimum": 0 + }, + "runtime_integration": { + "const": false + }, + "select_005_authorized": { + "const": false + }, + "selected_source": { + "const": "none" + }, + "support_gate": { + "const": "SUPPORT_GATE_PASSED" + }, + "surface_draw": { + "const": "SURFACES_FROZEN" + }, + "threshold_frozen": { + "type": "boolean" + }, + "threshold_search": { + "anyOf": [ + { + "$ref": "common.schema.json#/$defs/threshold_state" + }, + { + "const": "NOT_RUN" + } + ] + }, + "threshold_value": { + "anyOf": [ + { + "type": "null" + }, + { + "$ref": "common.schema.json#/$defs/residual_decimal" + } + ] + }, + "training_authorized": { + "const": false + }, + "training_readiness": { + "enum": [ + "BLOCKED_BY_THRESHOLD_V2_CALIBRATION", + "CALIBRATION_COMPLETE_MEASUREMENT_PENDING", + "THRESHOLD_EVIDENCE_COMPLETE" + ] + }, + "true_high": { + "type": "integer", + "minimum": 0 + } + } +} diff --git a/specs/007-hyperlexical-model/schemas/hyperlex/residual-threshold/direction-analysis.schema.json b/specs/007-hyperlexical-model/schemas/hyperlex/residual-threshold/direction-analysis.schema.json new file mode 100644 index 00000000..47f85a4e --- /dev/null +++ b/specs/007-hyperlexical-model/schemas/hyperlex/residual-threshold/direction-analysis.schema.json @@ -0,0 +1,155 @@ +{ + "$schema": "https://json-schema.org/draft/2020-12/schema", + "$id": "https://hyperlex.local/schemas/hyperlex/residual-threshold/direction-analysis.schema.json", + "title": "Residual-threshold direction analysis", + "type": "object", + "additionalProperties": false, + "required": [ + "analysis_scope", + "confound_analysis_run", + "direction_result", + "direction_rule", + "high_distribution", + "high_larger_pairs", + "json_schema_document", + "json_schema_exists", + "labels_changed", + "mann_whitney_u", + "mean_difference", + "median_difference", + "precision_wilson_run", + "rank_biserial", + "readiness_changed", + "ready_high_count", + "ready_quarantine_count", + "ready_reject_count", + "ready_secondary_count", + "reject_quarantine_comparison_run", + "residual_recomputed", + "roc_auc", + "rows_excluded", + "secondary_distribution", + "secondary_larger_pairs", + "source_label_hash", + "source_score_hash", + "source_support_join_hash", + "source_support_join_receipt_hash", + "source_support_reassessment_hash", + "threshold_search_run", + "ties", + "uncertainty" + ], + "properties": { + "analysis_scope": { + "const": "DIRECTION_ONLY" + }, + "confound_analysis_run": { + "const": false + }, + "direction_result": { + "enum": [ + "SUPPORTED_DIRECTION", + "INVERTED_DIRECTION", + "NO_DIRECTIONAL_SEPARATION" + ] + }, + "direction_rule": { + "type": "string", + "minLength": 1 + }, + "high_distribution": { + "$ref": "common.schema.json#/$defs/distribution" + }, + "high_larger_pairs": { + "type": "integer", + "minimum": 0 + }, + "json_schema_document": { + "type": "null" + }, + "json_schema_exists": { + "const": false + }, + "labels_changed": { + "const": false + }, + "mann_whitney_u": { + "$ref": "common.schema.json#/$defs/effect_decimal" + }, + "mean_difference": { + "$ref": "common.schema.json#/$defs/residual_decimal" + }, + "median_difference": { + "$ref": "common.schema.json#/$defs/residual_decimal" + }, + "precision_wilson_run": { + "const": false + }, + "rank_biserial": { + "$ref": "common.schema.json#/$defs/effect_decimal" + }, + "readiness_changed": { + "const": false + }, + "ready_high_count": { + "type": "integer", + "minimum": 0 + }, + "ready_quarantine_count": { + "type": "integer", + "minimum": 0 + }, + "ready_reject_count": { + "type": "integer", + "minimum": 0 + }, + "ready_secondary_count": { + "type": "integer", + "minimum": 0 + }, + "reject_quarantine_comparison_run": { + "const": false + }, + "residual_recomputed": { + "const": false + }, + "roc_auc": { + "$ref": "common.schema.json#/$defs/effect_decimal" + }, + "rows_excluded": { + "const": 0 + }, + "secondary_distribution": { + "$ref": "common.schema.json#/$defs/distribution" + }, + "secondary_larger_pairs": { + "type": "integer", + "minimum": 0 + }, + "source_label_hash": { + "$ref": "common.schema.json#/$defs/sha256" + }, + "source_score_hash": { + "$ref": "common.schema.json#/$defs/sha256" + }, + "source_support_join_hash": { + "$ref": "common.schema.json#/$defs/sha256" + }, + "source_support_join_receipt_hash": { + "$ref": "common.schema.json#/$defs/sha256" + }, + "source_support_reassessment_hash": { + "$ref": "common.schema.json#/$defs/sha256" + }, + "threshold_search_run": { + "const": false + }, + "ties": { + "type": "integer", + "minimum": 0 + }, + "uncertainty": { + "const": "UNCERTAINTY_INTERVAL_NOT_COMPUTED" + } + } +} diff --git a/specs/007-hyperlexical-model/schemas/hyperlex/residual-threshold/operator-label-row.schema.json b/specs/007-hyperlexical-model/schemas/hyperlex/residual-threshold/operator-label-row.schema.json new file mode 100644 index 00000000..09eae022 --- /dev/null +++ b/specs/007-hyperlexical-model/schemas/hyperlex/residual-threshold/operator-label-row.schema.json @@ -0,0 +1,126 @@ +{ + "title": "Operator label row", + "oneOf": [ + { + "type": "object", + "additionalProperties": false, + "required": [ + "calibration_row_id", + "operator_bucket", + "operator_label_present", + "score_status" + ], + "properties": { + "calibration_row_id": { + "$ref": "common.schema.json#/$defs/row_id" + }, + "operator_bucket": { + "type": "null" + }, + "operator_label_present": { + "const": false + }, + "score_status": { + "$ref": "common.schema.json#/$defs/score_status" + } + } + }, + { + "type": "object", + "additionalProperties": false, + "required": [ + "operator_bucket", + "operator_label_present", + "row_id", + "score_status" + ], + "properties": { + "operator_bucket": { + "type": "null" + }, + "operator_label_present": { + "const": false + }, + "row_id": { + "$ref": "common.schema.json#/$defs/row_id" + }, + "score_status": { + "$ref": "common.schema.json#/$defs/score_status" + } + } + }, + { + "type": "object", + "additionalProperties": false, + "required": [ + "calibration_row_id", + "operator_evidence", + "operator_label", + "operator_note", + "pos", + "pwn30_synset", + "review_status", + "row_id", + "schema", + "surface" + ], + "properties": { + "calibration_row_id": { + "type": "integer", + "minimum": 1 + }, + "operator_evidence": { + "enum": [ + "AMBIGUOUS_SENSE", + "CONVENTIONALIZED_SHIFT", + "DOMAIN_LEXICALIZED", + "FIXED_COMPOSITIONAL", + "FIXED_NONLITERAL", + "LEXICALIZED_TRANSPARENT", + "ORGANIZATION", + "PERSON_NAME", + "PHRASAL_BINDING", + "PRODUCTIVE_NUMBER", + "PROVENANCE_UNCLEAR", + "REFERENTIAL_DOMINANCE", + "SPECIES_COMMON_NAME", + "STRONG_IDIOM", + "TAXONOMY", + "TECHNICAL_MEASUREMENT", + "TECHNICAL_PROCEDURE", + "TITLE_OR_DESIGNATION", + "VARIABLE_SLOT" + ] + }, + "operator_label": { + "$ref": "common.schema.json#/$defs/operator_label" + }, + "operator_note": { + "type": "string", + "minLength": 1 + }, + "pos": { + "$ref": "common.schema.json#/$defs/pos" + }, + "pwn30_synset": { + "$ref": "common.schema.json#/$defs/pwn30_synset" + }, + "review_status": { + "const": "REVIEWED" + }, + "row_id": { + "$ref": "common.schema.json#/$defs/row_id" + }, + "schema": { + "const": "hyperlex.residual_threshold_v2_operator_label.v1" + }, + "surface": { + "type": "string", + "minLength": 1 + } + } + } + ], + "$schema": "https://json-schema.org/draft/2020-12/schema", + "$id": "https://hyperlex.local/schemas/hyperlex/residual-threshold/operator-label-row.schema.json" +} diff --git a/specs/007-hyperlexical-model/schemas/hyperlex/residual-threshold/resolution-row.schema.json b/specs/007-hyperlexical-model/schemas/hyperlex/residual-threshold/resolution-row.schema.json new file mode 100644 index 00000000..65bcbe1c --- /dev/null +++ b/specs/007-hyperlexical-model/schemas/hyperlex/residual-threshold/resolution-row.schema.json @@ -0,0 +1,135 @@ +{ + "title": "Constituent resolution row", + "type": "object", + "additionalProperties": false, + "required": [ + "constituent_index", + "constituent_pos", + "constituent_surface", + "parent_row_id", + "parent_surface", + "parent_synset", + "resolution_provenance", + "resolution_status", + "resolution_tier", + "selected_pwn30_synset", + "selected_sense_key_if_available" + ], + "properties": { + "constituent_index": { + "type": "integer", + "minimum": 0 + }, + "constituent_pos": { + "anyOf": [ + { + "type": "null" + }, + { + "$ref": "common.schema.json#/$defs/pos" + } + ] + }, + "constituent_surface": { + "type": "string", + "minLength": 1 + }, + "parent_row_id": { + "$ref": "common.schema.json#/$defs/row_id" + }, + "parent_surface": { + "type": "string", + "minLength": 1 + }, + "parent_synset": { + "$ref": "common.schema.json#/$defs/pwn30_synset" + }, + "resolution_provenance": { + "enum": [ + "extended_lesk_margin", + "model_abstention", + "model_margin", + "no_candidate_synset", + "structural_exact_pointer", + "structural_pointer_conflict", + "unique_lemma_synset" + ] + }, + "resolution_status": { + "$ref": "common.schema.json#/$defs/constituent_resolution_status" + }, + "resolution_tier": { + "enum": [ + "TIER1_UNIQUE_LEMMA", + "TIER1_STRUCTURAL", + "TIER2_EXTENDED_LESK", + "TIER3_GLOSSBERT", + "UNRESOLVED" + ] + }, + "selected_pwn30_synset": { + "anyOf": [ + { + "type": "null" + }, + { + "$ref": "common.schema.json#/$defs/pwn30_synset" + } + ] + }, + "selected_sense_key_if_available": { + "type": [ + "string", + "null" + ] + } + }, + "allOf": [ + { + "if": { + "properties": { + "resolution_status": { + "enum": [ + "AMBIGUOUS", + "UNRESOLVED" + ] + } + }, + "required": [ + "resolution_status" + ] + }, + "then": { + "properties": { + "selected_pwn30_synset": { + "type": "null" + } + } + } + }, + { + "if": { + "properties": { + "resolution_status": { + "enum": [ + "EXACT", + "RESOLVED" + ] + } + }, + "required": [ + "resolution_status" + ] + }, + "then": { + "properties": { + "selected_pwn30_synset": { + "$ref": "common.schema.json#/$defs/pwn30_synset" + } + } + } + } + ], + "$schema": "https://json-schema.org/draft/2020-12/schema", + "$id": "https://hyperlex.local/schemas/hyperlex/residual-threshold/resolution-row.schema.json" +} diff --git a/specs/007-hyperlexical-model/schemas/hyperlex/residual-threshold/score-row.schema.json b/specs/007-hyperlexical-model/schemas/hyperlex/residual-threshold/score-row.schema.json new file mode 100644 index 00000000..c2e51e9a --- /dev/null +++ b/specs/007-hyperlexical-model/schemas/hyperlex/residual-threshold/score-row.schema.json @@ -0,0 +1,129 @@ +{ + "title": "Residual score row", + "oneOf": [ + { + "type": "object", + "additionalProperties": false, + "required": [ + "abstention_reason", + "row_id", + "score_status" + ], + "properties": { + "abstention_reason": { + "enum": [ + "ambiguous_content_constituent", + "fewer_than_two_content_constituents", + "unresolved_content_constituent" + ] + }, + "row_id": { + "$ref": "common.schema.json#/$defs/row_id" + }, + "score_status": { + "const": "UNKNOWN" + } + } + }, + { + "type": "object", + "additionalProperties": false, + "required": [ + "composed_vector_hash", + "constituent_representation_texts", + "constituent_resolution_tiers", + "constituent_vector_hashes", + "content_constituents", + "integrated_resolution_sha256", + "pos", + "residual_candidate_spec_sha256", + "residual_score", + "resolved_constituent_synsets", + "row_id", + "score_status", + "surface", + "synset", + "uses_tier3", + "whole_representation_text", + "whole_vector_hash" + ], + "properties": { + "composed_vector_hash": { + "$ref": "common.schema.json#/$defs/sha256" + }, + "constituent_representation_texts": { + "type": "array", + "items": { + "type": "string" + }, + "minItems": 2 + }, + "constituent_resolution_tiers": { + "type": "array", + "items": { + "type": "string" + }, + "minItems": 2 + }, + "constituent_vector_hashes": { + "type": "array", + "items": { + "$ref": "common.schema.json#/$defs/sha256" + }, + "minItems": 2 + }, + "content_constituents": { + "type": "array", + "items": { + "type": "string" + }, + "minItems": 2 + }, + "integrated_resolution_sha256": { + "$ref": "common.schema.json#/$defs/sha256" + }, + "pos": { + "$ref": "common.schema.json#/$defs/pos" + }, + "residual_candidate_spec_sha256": { + "$ref": "common.schema.json#/$defs/sha256" + }, + "residual_score": { + "$ref": "common.schema.json#/$defs/residual_decimal" + }, + "resolved_constituent_synsets": { + "type": "array", + "items": { + "$ref": "common.schema.json#/$defs/pwn30_synset" + }, + "minItems": 2 + }, + "row_id": { + "$ref": "common.schema.json#/$defs/row_id" + }, + "score_status": { + "const": "SCORED" + }, + "surface": { + "type": "string", + "minLength": 1 + }, + "synset": { + "$ref": "common.schema.json#/$defs/pwn30_synset" + }, + "uses_tier3": { + "type": "boolean" + }, + "whole_representation_text": { + "type": "string", + "minLength": 1 + }, + "whole_vector_hash": { + "$ref": "common.schema.json#/$defs/sha256" + } + } + } + ], + "$schema": "https://json-schema.org/draft/2020-12/schema", + "$id": "https://hyperlex.local/schemas/hyperlex/residual-threshold/score-row.schema.json" +} diff --git a/specs/007-hyperlexical-model/schemas/hyperlex/residual-threshold/surface-manifest-row.schema.json b/specs/007-hyperlexical-model/schemas/hyperlex/residual-threshold/surface-manifest-row.schema.json new file mode 100644 index 00000000..c5228453 --- /dev/null +++ b/specs/007-hyperlexical-model/schemas/hyperlex/residual-threshold/surface-manifest-row.schema.json @@ -0,0 +1,135 @@ +{ + "title": "Residual-threshold surface manifest row", + "oneOf": [ + { + "$ref": "#/$defs/v1Row" + }, + { + "$ref": "#/$defs/v2Row" + } + ], + "$defs": { + "v1Row": { + "type": "object", + "additionalProperties": false, + "required": [ + "calibration_row_id", + "draw_order", + "draw_seed", + "frozen_gloss", + "normalized_text_sha256", + "pos", + "pwn30_synset", + "sampling_rule_id", + "source_identity", + "surface" + ], + "properties": { + "calibration_row_id": { + "$ref": "common.schema.json#/$defs/row_id" + }, + "draw_order": { + "type": "integer", + "minimum": 1 + }, + "draw_seed": { + "type": "null" + }, + "frozen_gloss": { + "type": "string", + "minLength": 1 + }, + "normalized_text_sha256": { + "$ref": "common.schema.json#/$defs/sha256" + }, + "pos": { + "$ref": "common.schema.json#/$defs/pos" + }, + "pwn30_synset": { + "$ref": "common.schema.json#/$defs/pwn30_synset" + }, + "sampling_rule_id": { + "const": "positional_stratified_hash_prefix_v1" + }, + "source_identity": { + "const": "wordnet-3.0" + }, + "surface": { + "type": "string", + "minLength": 1 + } + } + }, + "v2Row": { + "type": "object", + "additionalProperties": false, + "required": [ + "allocation_rule_id", + "frozen_gloss", + "global_draw_order", + "normalized_text_sha256", + "pos", + "pwn30_synset", + "row_id", + "source_identity", + "stratum_id", + "surface", + "surface_role", + "token_count", + "within_stratum_order" + ], + "properties": { + "allocation_rule_id": { + "const": "positional_stratified_hash_round_robin_quota_v2" + }, + "frozen_gloss": { + "type": "string", + "minLength": 1 + }, + "global_draw_order": { + "type": "integer", + "minimum": 1 + }, + "normalized_text_sha256": { + "$ref": "common.schema.json#/$defs/sha256" + }, + "pos": { + "$ref": "common.schema.json#/$defs/pos" + }, + "pwn30_synset": { + "$ref": "common.schema.json#/$defs/pwn30_synset" + }, + "row_id": { + "$ref": "common.schema.json#/$defs/row_id" + }, + "source_identity": { + "const": "wordnet-3.0" + }, + "stratum_id": { + "type": "string", + "pattern": "^(noun|verb|adj|adv):[0-9]+$" + }, + "surface": { + "type": "string", + "minLength": 1 + }, + "surface_role": { + "enum": [ + "CALIBRATION_V2", + "MEASUREMENT_V2" + ] + }, + "token_count": { + "type": "integer", + "minimum": 1 + }, + "within_stratum_order": { + "type": "integer", + "minimum": 1 + } + } + } + }, + "$schema": "https://json-schema.org/draft/2020-12/schema", + "$id": "https://hyperlex.local/schemas/hyperlex/residual-threshold/surface-manifest-row.schema.json" +} diff --git a/specs/007-hyperlexical-model/schemas/hyperlex/select/ROUTING_CONTRACT_RECEIPT.json b/specs/007-hyperlexical-model/schemas/hyperlex/select/ROUTING_CONTRACT_RECEIPT.json new file mode 100644 index 00000000..dedb101c --- /dev/null +++ b/specs/007-hyperlexical-model/schemas/hyperlex/select/ROUTING_CONTRACT_RECEIPT.json @@ -0,0 +1,59 @@ +{ + "admission_receipt_created": false, + "admission_schema_path": "specs/007-hyperlexical-model/schemas/hyperlex/select/admission.schema.json", + "admission_schema_sha256": "6f20efe73b74207243474724261c985d4e6dde888df497a1ef723dda02a42077", + "best_changed": false, + "conformance_report_path": "specs/007-hyperlexical-model/schemas/hyperlex/select/CONFORMANCE.json", + "conformance_report_sha256": "fac5dacf06f1257137bfd72b91c728191840ce291d2c8e953292f4313dddcd2b", + "contract_id": "hyperlex.eval_routing.v1", + "contract_spec_path": "specs/007-hyperlexical-model/select-eval-routing-contract.md", + "contract_spec_sha256": "002e2ac67e5ee00c27ecedd4566b9e7d900852c51e3ac57aac957ac095899d96", + "determinism": "IDENTICAL", + "epochs": 0, + "experiment_id": "HLX-EXP-2026-09-27-SELECT-005", + "exposure_fence_bypassed": false, + "gradient_steps": 0, + "historical_validation": "SCHEMA_CONFORMANCE_GAP", + "identity_ledger_modified": false, + "optimizer_constructed": false, + "procedure_id": "select_eval_routing_v1", + "procedure_sha256": "ad187d262eba280dd4b88d2204e03d4dfb255f9b0fb443bd250a0e441a7ea6db", + "reserve_recensus": false, + "routing_schema_path": "specs/007-hyperlexical-model/schemas/hyperlex/select/eval-routing.schema.json", + "routing_schema_sha256": "147e2787e6e66ba47acd185fa6602a10bd5d57c15ea446f574b2e750277d31a0", + "routing_schema_validation": "PASS", + "routing_semantic_validation": "PASS", + "routing_store_written": false, + "routing_version": "v1", + "schema": "hyperlex.eval_routing_contract_receipt.v1", + "schema_conformance_gaps": [ + { + "artifact": "exp-20260926-select-003/threshold-authorization.json", + "message": "'decision_thresholds' is a required property", + "schema": "hyperlex.threshold_authorization.v1" + }, + { + "artifact": "exp-20260926-select-003/threshold-authorization.json", + "message": "'sealed' is a required property", + "schema": "hyperlex.threshold_authorization.v1" + } + ], + "select_005_thresholds_sealed": false, + "settlement_events_modified": false, + "source_harvest": false, + "state": "ROUTING_VALIDATION_COMPLETE", + "states_reached": [ + "ROUTING_CONTRACT_DRAFTED", + "ROUTING_CONTRACT_FROZEN", + "ROUTING_VALIDATION_COMPLETE" + ], + "threshold_authorization_schema_path": "specs/007-hyperlexical-model/schemas/hyperlex/select/threshold-authorization.schema.json", + "threshold_authorization_schema_sha256": "35a1b70cc805cf4271831a2414fd9ac85dcc15e5d23ec951d2d37665e89109a1", + "threshold_authorization_state": "BLOCKED_PENDING_OPERATOR_AUTHORIZATION", + "threshold_v2_measurement_sha256": "78ca09ab14912681d028e8b9b1c77a8daf1d0c8c81561434eb9b6ade45a753e5", + "threshold_v2_measurement_state": "SEALED", + "training_authorized": false, + "training_launch_authorized": false, + "transition": "SELECT_005_SETTLEMENT_ROUTING_CONTRACT_EXTENSION_AUTHORIZATION", + "weights_mutated": false +} diff --git a/specs/007-hyperlexical-model/schemas/hyperlex/select/admission.schema.json b/specs/007-hyperlexical-model/schemas/hyperlex/select/admission.schema.json new file mode 100644 index 00000000..f309686c --- /dev/null +++ b/specs/007-hyperlexical-model/schemas/hyperlex/select/admission.schema.json @@ -0,0 +1,102 @@ +{ + "$schema": "https://json-schema.org/draft/2020-12/schema", + "$id": "https://hyperlex.local/schemas/hyperlex.admission.v1", + "title": "Training admission receipt", + "description": "Fields emitted by admit_training_run. Schema creation does not admit SELECT-005.", + "type": "object", + "additionalProperties": false, + "required": [ + "admission_gate_sequence", + "best_moved", + "controlled_holdout_contract", + "epochs", + "gradient_steps", + "hlx_allow_no_holdout", + "holdout_admitted", + "launch_armed", + "optimizer_loaded", + "schema", + "training_started" + ], + "properties": { + "schema": {"const": "hyperlex.admission.v1"}, + "controlled_holdout_contract": { + "enum": ["CONTROLLED_RESERVE", "LEGACY_MANIFEST", "UNCONTROLLED"] + }, + "admission_gate_sequence": { + "type": "array", + "items": {"type": "string"}, + "minItems": 9 + }, + "failed_gate": {"type": ["string", "null"]}, + "environment_hash": {"$ref": "#/$defs/sha256"}, + "experiment_id": {"type": ["string", "null"]}, + "launch_armed": {"type": "boolean"}, + "training_started": {"const": false}, + "epochs": {"const": 0}, + "gradient_steps": {"const": 0}, + "optimizer_loaded": {"type": "boolean"}, + "best_moved": {"type": "boolean"}, + "hlx_allow_no_holdout": {"type": "boolean"}, + "holdout_admitted": {"type": "boolean"}, + "admission_result": { + "enum": ["ADMISSION_PASS", "NOT_ARMED", "NOT_READY"] + }, + "status": { + "anyOf": [ + {"type": "null"}, + {"enum": ["NOT_READY", "PREREGISTERED", "TRAINING_READY"]} + ] + }, + "ready_to_train": {"type": "boolean"}, + "scientific_contract_sealed": {"type": "boolean"}, + "decision_rule_sealed": {"type": "boolean"}, + "decision_threshold_state": {"type": "string"}, + "training_launch_authorized": {"type": "boolean"}, + "holdout_state": {"type": ["string", "null"]}, + "holdout_experiment_id": {"type": ["string", "null"]}, + "holdout_manifest_sha256": { + "anyOf": [{"type": "null"}, {"$ref": "#/$defs/sha256"}] + }, + "reserve_lifecycle": {"type": "string"}, + "reserve_events_sha256": {"$ref": "#/$defs/sha256"}, + "reserve_counts": { + "type": "object", + "additionalProperties": false, + "required": ["classify", "classify_non_none", "classify_observed", "unbind_clean"], + "properties": { + "classify": {"type": "integer", "minimum": 0}, + "classify_non_none": {"type": "integer", "minimum": 0}, + "classify_observed": {"type": "integer", "minimum": 0}, + "unbind_clean": {"type": "integer", "minimum": 0} + } + }, + "data_sha256": {"$ref": "#/$defs/sha256"}, + "holdout_filter_training_rows_removed": {"type": "integer", "minimum": 0}, + "holdout_train_row_id_overlap": {"type": "integer", "minimum": 0}, + "holdout_train_text_hash_overlap": {"type": "integer", "minimum": 0}, + "holdout_training_disjoint": {"type": "boolean"}, + "live_export_generation_enabled": {"type": "boolean"}, + "reserve_train_row_id_overlap": {"type": "integer", "minimum": 0}, + "reserve_train_text_hash_overlap": {"type": "integer", "minimum": 0}, + "training_export_path": {"type": "string"}, + "training_export_rows": {"type": "integer", "minimum": 0}, + "training_export_sha256_actual": {"$ref": "#/$defs/sha256"}, + "training_export_sha256_expected": {"$ref": "#/$defs/sha256"}, + "training_input_mode": {"type": "string"}, + "training_rows_removed": {"type": "integer", "minimum": 0}, + "allow_train": {"type": "boolean"}, + "brier": {"type": ["number", "null"]}, + "machine": {"type": "string"}, + "name_gate": {"type": "boolean"}, + "note": {"type": "string"}, + "python": {"type": "string"}, + "trunk_config": {"type": "boolean"}, + "trunk_dir": {"type": "string"}, + "trunk_dir_exists": {"type": "boolean"}, + "trunk_id": {"type": "string"} + }, + "$defs": { + "sha256": {"type": "string", "pattern": "^[0-9a-f]{64}$"} + } +} diff --git a/specs/007-hyperlexical-model/schemas/residual-threshold/residual-threshold-common.schema.json b/specs/007-hyperlexical-model/schemas/residual-threshold/residual-threshold-common.schema.json new file mode 100644 index 00000000..f3ed1f55 --- /dev/null +++ b/specs/007-hyperlexical-model/schemas/residual-threshold/residual-threshold-common.schema.json @@ -0,0 +1,115 @@ +{ + "$schema": "https://json-schema.org/draft/2020-12/schema", + "$id": "https://hyperlex.local/schemas/residual-threshold-common.schema.json", + "title": "Shared residual-threshold contract terms", + "description": "Retrospective definitions for artifacts already emitted. This file does not change those artifacts.", + "$defs": { + "sha256": { + "type": "string", + "pattern": "^[0-9a-f]{64}$" + }, + "row_id": { + "$ref": "#/$defs/sha256" + }, + "pwn30_synset": { + "type": "string", + "pattern": "^(noun|verb|adj|adv):[0-9]{8}$" + }, + "pos": { + "enum": ["noun", "verb", "adj", "adv"] + }, + "operator_label": { + "enum": ["HIGH", "SECONDARY", "REJECT", "QUARANTINE", "UNRESOLVED"] + }, + "row_resolution_status": { + "enum": ["RESIDUAL_READY", "UNKNOWN"] + }, + "measurement_state": { + "enum": ["SEALED"] + }, + "threshold_state": { + "enum": [ + "CALIBRATION_INSUFFICIENT_SUPPORT", + "NO_DIRECTIONAL_SIGNAL", + "CALIBRATION_CONFOUND_REVIEW", + "NO_THRESHOLD_PASSES_PRECISION_GATE", + "THRESHOLD_FROZEN" + ] + }, + "residual_decimal": { + "type": "string", + "pattern": "^-?[0-9]+\\.[0-9]{10}$" + }, + "effect_decimal": { + "type": "string", + "pattern": "^-?[0-9]+\\.[0-9]{6}$" + }, + "artifact_reference": { + "type": "object", + "additionalProperties": false, + "required": ["name", "sha256"], + "properties": { + "name": {"type": "string", "minLength": 1}, + "sha256": {"$ref": "#/$defs/sha256"} + } + }, + "distribution": { + "type": "object", + "additionalProperties": false, + "required": ["count", "max", "mean", "median", "min", "p10", "p25", "p75", "p90", "status", "std"], + "properties": { + "count": {"type": "integer", "minimum": 0}, + "max": {"$ref": "#/$defs/residual_decimal"}, + "mean": {"$ref": "#/$defs/residual_decimal"}, + "median": {"$ref": "#/$defs/residual_decimal"}, + "min": {"$ref": "#/$defs/residual_decimal"}, + "p10": {"$ref": "#/$defs/residual_decimal"}, + "p25": {"$ref": "#/$defs/residual_decimal"}, + "p75": {"$ref": "#/$defs/residual_decimal"}, + "p90": {"$ref": "#/$defs/residual_decimal"}, + "status": {"enum": ["DESCRIPTIVE", "NOT_COMPUTABLE"]}, + "std": {"$ref": "#/$defs/residual_decimal"} + } + }, + "unfrozenThresholdInvariant": { + "if": { + "properties": {"threshold_frozen": {"const": false}}, + "required": ["threshold_frozen"] + }, + "then": { + "properties": {"threshold_value": {"type": "null"}}, + "required": ["threshold_value"] + } + }, + "frozenThresholdInvariant": { + "if": { + "properties": {"threshold_frozen": {"const": true}}, + "required": ["threshold_frozen"] + }, + "then": { + "required": [ + "false_high", + "high_recall", + "leave_one_out_result", + "precision", + "predicted_yes_support", + "quarantine_predicted_yes", + "reject_predicted_yes", + "threshold_value", + "true_high" + ], + "properties": { + "false_high": {"type": "integer", "minimum": 0}, + "high_recall": {"$ref": "#/$defs/effect_decimal"}, + "leave_one_out_result": {"const": "PASS"}, + "precision": {"$ref": "#/$defs/effect_decimal"}, + "predicted_yes_support": {"type": "integer", "minimum": 1}, + "quarantine_predicted_yes": {"const": 0}, + "reject_predicted_yes": {"const": 0}, + "threshold_value": {"$ref": "#/$defs/residual_decimal"}, + "true_high": {"type": "integer", "minimum": 0} + } + } + } + } +} diff --git a/specs/007-hyperlexical-model/schemas/residual-threshold/residual-threshold-completion-receipt.schema.json b/specs/007-hyperlexical-model/schemas/residual-threshold/residual-threshold-completion-receipt.schema.json new file mode 100644 index 00000000..d205e8ed --- /dev/null +++ b/specs/007-hyperlexical-model/schemas/residual-threshold/residual-threshold-completion-receipt.schema.json @@ -0,0 +1,77 @@ +{ + "$schema": "https://json-schema.org/draft/2020-12/schema", + "$id": "https://hyperlex.local/schemas/residual-threshold-completion-receipt.schema.json", + "title": "Residual-threshold calibration completion receipt", + "type": "object", + "additionalProperties": false, + "allOf": [ + {"$ref": "residual-threshold-common.schema.json#/$defs/unfrozenThresholdInvariant"}, + {"$ref": "residual-threshold-common.schema.json#/$defs/frozenThresholdInvariant"} + ], + "required": [ + "calibration_redraw", + "confound_gate", + "direction_gate", + "json_schema_document", + "json_schema_exists", + "measurement_eligible", + "measurement_state", + "operator_labeling", + "runtime_integration", + "select_005_authorized", + "selected_source", + "support_gate", + "surface_draw", + "threshold_frozen", + "threshold_search", + "threshold_value", + "training_authorized", + "training_readiness" + ], + "properties": { + "calibration_redraw": {"const": false}, + "confound_gate": {"enum": ["PASS", "STOP", "NOT_RUN"]}, + "direction_gate": { + "enum": ["SUPPORTED_DIRECTION", "INVERTED_DIRECTION", "NO_DIRECTIONAL_SEPARATION", "NOT_RUN"] + }, + "false_high": {"type": "integer", "minimum": 0}, + "high_recall": {"$ref": "residual-threshold-common.schema.json#/$defs/effect_decimal"}, + "json_schema_document": {"type": "null"}, + "json_schema_exists": {"const": false}, + "leave_one_out_result": {"const": "PASS"}, + "measurement_eligible": {"type": "boolean"}, + "measurement_state": {"$ref": "residual-threshold-common.schema.json#/$defs/measurement_state"}, + "operator_labeling": {"const": "LABELS_FROZEN"}, + "precision": {"$ref": "residual-threshold-common.schema.json#/$defs/effect_decimal"}, + "predicted_yes_support": {"type": "integer", "minimum": 0}, + "quarantine_predicted_yes": {"type": "integer", "minimum": 0}, + "reject_predicted_yes": {"type": "integer", "minimum": 0}, + "runtime_integration": {"const": false}, + "select_005_authorized": {"const": false}, + "selected_source": {"const": "none"}, + "support_gate": {"const": "SUPPORT_GATE_PASSED"}, + "surface_draw": {"const": "SURFACES_FROZEN"}, + "threshold_frozen": {"type": "boolean"}, + "threshold_search": { + "anyOf": [ + {"$ref": "residual-threshold-common.schema.json#/$defs/threshold_state"}, + {"const": "NOT_RUN"} + ] + }, + "threshold_value": { + "anyOf": [ + {"type": "null"}, + {"$ref": "residual-threshold-common.schema.json#/$defs/residual_decimal"} + ] + }, + "training_authorized": {"const": false}, + "training_readiness": { + "enum": [ + "BLOCKED_BY_THRESHOLD_V2_CALIBRATION", + "CALIBRATION_COMPLETE_MEASUREMENT_PENDING", + "THRESHOLD_EVIDENCE_COMPLETE" + ] + }, + "true_high": {"type": "integer", "minimum": 0} + } +} diff --git a/specs/007-hyperlexical-model/schemas/residual-threshold/residual-threshold-confound-analysis.schema.json b/specs/007-hyperlexical-model/schemas/residual-threshold/residual-threshold-confound-analysis.schema.json new file mode 100644 index 00000000..b4c81ffc --- /dev/null +++ b/specs/007-hyperlexical-model/schemas/residual-threshold/residual-threshold-confound-analysis.schema.json @@ -0,0 +1,103 @@ +{ + "$schema": "https://json-schema.org/draft/2020-12/schema", + "$id": "https://hyperlex.local/schemas/residual-threshold-confound-analysis.schema.json", + "title": "Residual-threshold confound analysis", + "oneOf": [ + {"$ref": "#/$defs/notRun"}, + {"$ref": "#/$defs/executed"} + ], + "$defs": { + "stopFlags": { + "type": "object", + "additionalProperties": false, + "required": ["extreme_driven", "pos_partitioned", "tier3_concentrated"], + "properties": { + "extreme_driven": {"type": "boolean"}, + "pos_partitioned": {"type": "boolean"}, + "tier3_concentrated": {"type": "boolean"} + } + }, + "notRun": { + "type": "object", + "additionalProperties": false, + "required": [ + "confound_gate_executed", + "confound_result", + "frozen_stop_conditions", + "json_schema_document", + "json_schema_exists", + "reason", + "schema" + ], + "properties": { + "confound_gate_executed": {"const": false}, + "confound_result": {"const": "NOT_RUN"}, + "frozen_stop_conditions": { + "type": "array", + "items": {"enum": ["tier3_concentrated", "extreme_driven", "pos_partitioned"]}, + "minItems": 3, + "uniqueItems": true + }, + "json_schema_document": {"type": "null"}, + "json_schema_exists": {"const": false}, + "reason": {"$ref": "residual-threshold-common.schema.json#/$defs/threshold_state"}, + "schema": {"type": "string", "minLength": 1} + } + }, + "executed": { + "type": "object", + "additionalProperties": false, + "required": [ + "analysis_scope", + "confound_gate_executed", + "confound_result", + "direction_analysis_sha256", + "direction_result", + "frozen_stop_conditions", + "json_schema_document", + "json_schema_exists", + "nuisance_changes_stop_decision", + "nuisance_field_provenance", + "report", + "residual_recomputed", + "rows_excluded", + "schema", + "source_label_hash", + "source_score_hash", + "source_support_join_hash", + "threshold_search_run" + ], + "properties": { + "analysis_scope": {"const": "CONFOUND_THEN_THRESHOLD_IF_PASS"}, + "confound_gate_executed": {"const": true}, + "confound_result": {"enum": ["PASS", "STOP"]}, + "direction_analysis_sha256": {"$ref": "residual-threshold-common.schema.json#/$defs/sha256"}, + "direction_result": {"const": "SUPPORTED_DIRECTION"}, + "frozen_stop_conditions": {"$ref": "#/$defs/stopFlags"}, + "json_schema_document": {"type": "null"}, + "json_schema_exists": {"const": false}, + "nuisance_changes_stop_decision": {"const": false}, + "nuisance_field_provenance": {"type": "object"}, + "report": { + "type": "object", + "required": ["extreme_driven", "nuisance", "pos", "pos_partitioned", "tier3", "tier3_counts"], + "properties": { + "extreme_driven": {"type": "boolean"}, + "nuisance": {"type": "object"}, + "pos": {"type": "object"}, + "pos_partitioned": {"type": "boolean"}, + "tier3": {"type": "object"}, + "tier3_counts": {"type": "object"} + } + }, + "residual_recomputed": {"const": false}, + "rows_excluded": {"const": 0}, + "schema": {"type": "string", "minLength": 1}, + "source_label_hash": {"$ref": "residual-threshold-common.schema.json#/$defs/sha256"}, + "source_score_hash": {"$ref": "residual-threshold-common.schema.json#/$defs/sha256"}, + "source_support_join_hash": {"$ref": "residual-threshold-common.schema.json#/$defs/sha256"}, + "threshold_search_run": {"type": "boolean"} + } + } + } +} diff --git a/specs/007-hyperlexical-model/schemas/residual-threshold/residual-threshold-direction-analysis.schema.json b/specs/007-hyperlexical-model/schemas/residual-threshold/residual-threshold-direction-analysis.schema.json new file mode 100644 index 00000000..08d4c3ec --- /dev/null +++ b/specs/007-hyperlexical-model/schemas/residual-threshold/residual-threshold-direction-analysis.schema.json @@ -0,0 +1,79 @@ +{ + "$schema": "https://json-schema.org/draft/2020-12/schema", + "$id": "https://hyperlex.local/schemas/residual-threshold-direction-analysis.schema.json", + "title": "Residual-threshold direction analysis", + "type": "object", + "additionalProperties": false, + "required": [ + "analysis_scope", + "confound_analysis_run", + "direction_result", + "direction_rule", + "high_distribution", + "high_larger_pairs", + "json_schema_document", + "json_schema_exists", + "labels_changed", + "mann_whitney_u", + "mean_difference", + "median_difference", + "precision_wilson_run", + "rank_biserial", + "readiness_changed", + "ready_high_count", + "ready_quarantine_count", + "ready_reject_count", + "ready_secondary_count", + "reject_quarantine_comparison_run", + "residual_recomputed", + "roc_auc", + "rows_excluded", + "secondary_distribution", + "secondary_larger_pairs", + "source_label_hash", + "source_score_hash", + "source_support_join_hash", + "source_support_join_receipt_hash", + "source_support_reassessment_hash", + "threshold_search_run", + "ties", + "uncertainty" + ], + "properties": { + "analysis_scope": {"const": "DIRECTION_ONLY"}, + "confound_analysis_run": {"const": false}, + "direction_result": { + "enum": ["SUPPORTED_DIRECTION", "INVERTED_DIRECTION", "NO_DIRECTIONAL_SEPARATION"] + }, + "direction_rule": {"type": "string", "minLength": 1}, + "high_distribution": {"$ref": "residual-threshold-common.schema.json#/$defs/distribution"}, + "high_larger_pairs": {"type": "integer", "minimum": 0}, + "json_schema_document": {"type": "null"}, + "json_schema_exists": {"const": false}, + "labels_changed": {"const": false}, + "mann_whitney_u": {"$ref": "residual-threshold-common.schema.json#/$defs/effect_decimal"}, + "mean_difference": {"$ref": "residual-threshold-common.schema.json#/$defs/residual_decimal"}, + "median_difference": {"$ref": "residual-threshold-common.schema.json#/$defs/residual_decimal"}, + "precision_wilson_run": {"const": false}, + "rank_biserial": {"$ref": "residual-threshold-common.schema.json#/$defs/effect_decimal"}, + "readiness_changed": {"const": false}, + "ready_high_count": {"type": "integer", "minimum": 0}, + "ready_quarantine_count": {"type": "integer", "minimum": 0}, + "ready_reject_count": {"type": "integer", "minimum": 0}, + "ready_secondary_count": {"type": "integer", "minimum": 0}, + "reject_quarantine_comparison_run": {"const": false}, + "residual_recomputed": {"const": false}, + "roc_auc": {"$ref": "residual-threshold-common.schema.json#/$defs/effect_decimal"}, + "rows_excluded": {"const": 0}, + "secondary_distribution": {"$ref": "residual-threshold-common.schema.json#/$defs/distribution"}, + "secondary_larger_pairs": {"type": "integer", "minimum": 0}, + "source_label_hash": {"$ref": "residual-threshold-common.schema.json#/$defs/sha256"}, + "source_score_hash": {"$ref": "residual-threshold-common.schema.json#/$defs/sha256"}, + "source_support_join_hash": {"$ref": "residual-threshold-common.schema.json#/$defs/sha256"}, + "source_support_join_receipt_hash": {"$ref": "residual-threshold-common.schema.json#/$defs/sha256"}, + "source_support_reassessment_hash": {"$ref": "residual-threshold-common.schema.json#/$defs/sha256"}, + "threshold_search_run": {"const": false}, + "ties": {"type": "integer", "minimum": 0}, + "uncertainty": {"const": "UNCERTAINTY_INTERVAL_NOT_COMPUTED"} + } +} diff --git a/specs/007-hyperlexical-model/schemas/residual-threshold/residual-threshold-surface-manifest.schema.json b/specs/007-hyperlexical-model/schemas/residual-threshold/residual-threshold-surface-manifest.schema.json new file mode 100644 index 00000000..89bc1419 --- /dev/null +++ b/specs/007-hyperlexical-model/schemas/residual-threshold/residual-threshold-surface-manifest.schema.json @@ -0,0 +1,73 @@ +{ + "$schema": "https://json-schema.org/draft/2020-12/schema", + "$id": "https://hyperlex.local/schemas/residual-threshold-surface-manifest.schema.json", + "title": "Residual-threshold surface manifest row", + "oneOf": [ + {"$ref": "#/$defs/v1Row"}, + {"$ref": "#/$defs/v2Row"} + ], + "$defs": { + "v1Row": { + "type": "object", + "additionalProperties": false, + "required": [ + "calibration_row_id", + "draw_order", + "draw_seed", + "frozen_gloss", + "normalized_text_sha256", + "pos", + "pwn30_synset", + "sampling_rule_id", + "source_identity", + "surface" + ], + "properties": { + "calibration_row_id": {"$ref": "residual-threshold-common.schema.json#/$defs/row_id"}, + "draw_order": {"type": "integer", "minimum": 1}, + "draw_seed": {"type": "null"}, + "frozen_gloss": {"type": "string", "minLength": 1}, + "normalized_text_sha256": {"$ref": "residual-threshold-common.schema.json#/$defs/sha256"}, + "pos": {"$ref": "residual-threshold-common.schema.json#/$defs/pos"}, + "pwn30_synset": {"$ref": "residual-threshold-common.schema.json#/$defs/pwn30_synset"}, + "sampling_rule_id": {"const": "positional_stratified_hash_prefix_v1"}, + "source_identity": {"const": "wordnet-3.0"}, + "surface": {"type": "string", "minLength": 1} + } + }, + "v2Row": { + "type": "object", + "additionalProperties": false, + "required": [ + "allocation_rule_id", + "frozen_gloss", + "global_draw_order", + "normalized_text_sha256", + "pos", + "pwn30_synset", + "row_id", + "source_identity", + "stratum_id", + "surface", + "surface_role", + "token_count", + "within_stratum_order" + ], + "properties": { + "allocation_rule_id": {"const": "positional_stratified_hash_round_robin_quota_v2"}, + "frozen_gloss": {"type": "string", "minLength": 1}, + "global_draw_order": {"type": "integer", "minimum": 1}, + "normalized_text_sha256": {"$ref": "residual-threshold-common.schema.json#/$defs/sha256"}, + "pos": {"$ref": "residual-threshold-common.schema.json#/$defs/pos"}, + "pwn30_synset": {"$ref": "residual-threshold-common.schema.json#/$defs/pwn30_synset"}, + "row_id": {"$ref": "residual-threshold-common.schema.json#/$defs/row_id"}, + "source_identity": {"const": "wordnet-3.0"}, + "stratum_id": {"type": "string", "pattern": "^(noun|verb|adj|adv):[0-9]+$"}, + "surface": {"type": "string", "minLength": 1}, + "surface_role": {"enum": ["CALIBRATION_V2", "MEASUREMENT_V2"]}, + "token_count": {"type": "integer", "minimum": 1}, + "within_stratum_order": {"type": "integer", "minimum": 1} + } + } + } +} diff --git a/specs/007-hyperlexical-model/semantic-evidence-v2.md b/specs/007-hyperlexical-model/semantic-evidence-v2.md new file mode 100644 index 00000000..6261b5cb --- /dev/null +++ b/specs/007-hyperlexical-model/semantic-evidence-v2.md @@ -0,0 +1,37 @@ +# Semantic evidence v2 + +Workstream B is `SEMANTIC_EVIDENCE_SUCCESSOR_DESIGN`. Its rule name is `RUNE.SEMANTIC_COMPOSITIONALITY_EVIDENCE.v2`. It does not repair `RUNE.SEMANTIC_COMPOSITIONALITY_THRESHOLD.v1` or `.v2`. + +v1 remains `CLOSED_CALIBRATION_DESIGN_INSUFFICIENT`. v2 remains `NO_THRESHOLD_PASSES_PRECISION_GATE`. The governing conclusion is `CURRENT_RESIDUAL_REPRESENTATION_INSUFFICIENT_FOR_REQUIRED_PRECISION`, not `THRESHOLD_SEARCH_INSUFFICIENT`. + +Observed on the frozen v2 support join: ready HIGH 13, ready SECONDARY 22, direction HIGH greater than SECONDARY, descriptive AUC 0.580420, rank-biserial 0.160839, precision-floor candidates 2, Wilson-qualified candidates 0, fully qualified candidates 0. The threshold value stays null. Measurement stays sealed. Training stays unauthorized. + +## Research question + +Can a frozen multi-feature semantic evidence function distinguish supplied-sense lexicalized noncompositional expressions from lexicalized or ordinary compositional expressions with enough precision to satisfy the existing Hyperlex safety gates? + +That question is unanswered. This document drafts the experiment. It does not preregister a model revision, draw a surface, or open a threshold lane. + +## Architecture + +The successor starts from the supplied PWN 3.0 parent sense and frozen constituent senses. It abstains when constituent structure is unresolved. It builds an evidence feature vector from whole-expression and constituent-composition views, then, only after that vector is frozen, evaluates an interpretable calibrator. The output vocabulary is YES or UNKNOWN. There is no semantic NO unless a later preregistration adds one. + +REJECT and QUARANTINE stay safety vetoes. They are not negative training classes for the calibrator. + +A single scalar `1 - cosine(whole, normalized mean of constituents)` remains the historical baseline. It is not assumed to be sufficient. + +## Separate state + +This lane does not share a state machine with threshold-artifact schema hardening. The current state is `SEMANTIC_EVIDENCE_V2_SPEC_DRAFTED`. The next named transition is `SEMANTIC_EVIDENCE_V2_PREREGISTERED`. This draft does not authorize it. + +The v2 measurement surface disposition is `RETAIN_FOR_V2_ONLY`. It is not reused here. + +## Non-authorization + +This specification does not authorize a threshold-v2 redesign, redraw, or measurement execution. It does not change the 0.80 precision floor, the Wilson floor, or the safety vetoes. It does not authorize SELECT-005, training, BEST promotion, runtime integration, admission, settlement, or gold mutation. It does not draw `DEVELOPMENT_V2`, `CALIBRATION_V3`, or `MEASUREMENT_V3`. The raw size target of 300 rows per layer is a design note, not a draw. + +Companion contracts: + +- `semantic-evidence-v2-state-machine.md` +- `semantic-evidence-v2-data-contract.md` +- `semantic-evidence-v2-evaluation-contract.md` diff --git a/specs/007-hyperlexical-model/training-dependency-review.md b/specs/007-hyperlexical-model/training-dependency-review.md new file mode 100644 index 00000000..edcdabba --- /dev/null +++ b/specs/007-hyperlexical-model/training-dependency-review.md @@ -0,0 +1,23 @@ +# Training dependency review + +Question: is `RUNE.SEMANTIC_COMPOSITIONALITY_THRESHOLD` a hard dependency of the next training run? + +Answer from the contracts that exist today: no. The recorded finding is `SEMANTIC_THRESHOLD_NOT_A_HARD_TRAINING_DEPENDENCY`, and the lane state is `SEMANTIC_EVIDENCE_LANE_DECOUPLED_FROM_TRAINING`. + +This review does not authorize training, does not seal `HLX-EXP-2026-09-27-SELECT-005`, and does not clear the residual lane's own `BLOCKED_BY_THRESHOLD_V2_CALIBRATION` flag. That flag stays on the threshold tracker as a statement about the threshold lane. Admission does not read it. + +## What SELECT-005 actually requires + +`HLX-EXP-2026-09-27-SELECT-005` is an unsealed training-schedule experiment. The proposed variable is `train_schedule`: control `max_epochs=40` with early stopping off, against a candidate with `max_epochs=12`, minimum 4 scored epochs, patience 4, and early stopping on. Both arms pin `classify_macro_f1_nonnone`. The spec record is in `evaluation-reserve.md` under the SELECT-005 sections. No preregistration, arm directory, reserve binding, or admission receipt exists. Epochs and gradient steps for that id remain 0. `training_launch_authorized` is false. BEST still names `hyperlex-encoder-modernbert-base-seed-select004`. + +`scripts/shadow/hyperlexical/admission.py` admits a run through `experiment_binding`, `launch_gate`, `holdout_reserve`, `pinned_training_input`, `train_reserve_disjointness`, `single_variable`, `best_trunk`, `output_directory`, and `ready`. The reserve slices are classify, classify_observed, classify_non_none, and unbind_clean. The census still has no fresh `EVAL_RESERVE`. Those are the blockers. + +`HLX_THRESHOLD_AUTHORIZATION` is a different object. Its schema is `hyperlex.threshold_authorization.v1`. It seals numeric decision thresholds for the select metric of one experiment id. A missing file stays `BLOCKED_PENDING_OPERATOR_AUTHORIZATION`. It does not mention residual scores, precision 0.80, Wilson bounds, or `RUNE.SEMANTIC_COMPOSITIONALITY_THRESHOLD`. + +`specs/007-hyperlexical-model/training-contracts.md` governs proposed evidence bundles (source, annotation, split, run, evaluation). It does not require a semantic-compositionality threshold. A valid bundle does not train or promote. + +The residual completion module writes `training_readiness: BLOCKED_BY_THRESHOLD_V2_CALIBRATION` onto the threshold tracker and the completion receipt. Nothing in `admission.py` or the training-contract validator consults `residual_threshold_v2_training_readiness`. The semantic threshold is an expansion and evidence lane. It is not a prerequisite for SELECT-005's scientific validity. + +## What remains blocked + +SELECT-005 stays unsealed because it still has no fresh reserve and no sealed admission receipt. Semantic-evidence v2 stays `SEMANTIC_EVIDENCE_V2_SPEC_DRAFTED`. Measurement for threshold v2 stays sealed. This review does not resume a training run. diff --git a/tests/shadow/test_residual_threshold_v2_completion.py b/tests/shadow/test_residual_threshold_v2_completion.py new file mode 100644 index 00000000..78573c0b --- /dev/null +++ b/tests/shadow/test_residual_threshold_v2_completion.py @@ -0,0 +1,91 @@ +"""Calibration completion keeps the frozen confound stop and precision gates.""" + +from __future__ import annotations + +import inspect +from decimal import Decimal +from pathlib import Path + +from hyperlexical.residual_threshold_v1 import ( + MIN_PREDICTED_YES, + PRECISION_FLOOR, + WILSON_LOWER_FLOOR, + _candidate_thresholds, + _comparison, + _ready_rows, +) +from hyperlexical.residual_threshold_v2_calibration import MEASUREMENT_NEXT +from hyperlexical.residual_threshold_v2_completion import ( + COMPLETION_SEARCH_PATH, + EXECUTION_SEARCH, + EXPECTED_EXECUTION_SEARCH, + EXPECTED_MEASUREMENT, + TRAINING_BLOCKED, + TRAINING_PENDING, + confound_stop, + itemized_gate_counts, +) + + +def _row(bucket: str, residual: str, pos: str = "noun", tier3: bool = False) -> dict: + return { + "development_row": False, + "operator_bucket": bucket, + "pos": pos, + "residual_score": residual, + "score_status": "SCORED", + "uses_tier3": tier3, + } + + +def test_only_the_three_frozen_flags_stop_confound_review() -> None: + assert confound_stop( + {"extreme_driven": False, "pos_partitioned": False, "tier3_concentrated": False} + ) is False + assert confound_stop( + {"extreme_driven": True, "pos_partitioned": False, "tier3_concentrated": False} + ) is True + assert confound_stop( + {"extreme_driven": False, "pos_partitioned": True, "tier3_concentrated": False} + ) is True + assert confound_stop( + {"extreme_driven": False, "pos_partitioned": False, "tier3_concentrated": True} + ) is True + source = inspect.getsource(confound_stop) + assert "spearman" not in source + assert "token_count" not in source + + +def test_itemized_counts_use_the_frozen_floors() -> None: + assert MIN_PREDICTED_YES == 8 + assert PRECISION_FLOOR == Decimal("0.80") + assert WILSON_LOWER_FLOOR == Decimal("0.50") + rows = [_row("HIGH", f"0.{index:010d}") for index in range(13)] + rows.extend(_row("SECONDARY", f"0.{index:010d}") for index in range(13, 35)) + rows.extend(_row("REJECT", "0.9000000000") for _ in range(2)) + rows.append(_row("QUARANTINE", "0.9500000000")) + ready = _ready_rows(rows) + comparison = _comparison(ready) + counts = itemized_gate_counts(ready, comparison, _candidate_thresholds(ready)) + assert counts["candidates"] == len(_candidate_thresholds(ready)) + assert counts["reject_veto"] < counts["candidates"] + assert counts["quarantine_veto"] < counts["candidates"] + assert counts["all_gates"] <= counts["leave_one_out"] + + +def test_completion_module_does_not_rescore_or_open_measurement_rows() -> None: + path = Path(inspect.getsourcefile(confound_stop)) + text = path.read_text(encoding="utf-8") + assert EXPECTED_MEASUREMENT in text + assert EXPECTED_EXECUTION_SEARCH in text + assert MEASUREMENT_NEXT == "THRESHOLD_V2_MEASUREMENT_EXECUTION_AUTHORIZATION" + assert TRAINING_BLOCKED in text + assert TRAINING_PENDING in text + assert "encode_needed(" not in text + assert "load_encoder(" not in text + assert "bootstrap_intervals(" not in text + assert "GlossBERT(" not in text + assert COMPLETION_SEARCH_PATH.name != EXECUTION_SEARCH.name + assert "MEASUREMENT_MANIFEST.read_text" not in text + source = inspect.getsource(path and __import__("hyperlexical.residual_threshold_v2_completion", fromlist=["complete"]).complete) + assert source.index("if stopped:") < source.index("_search_body(") From 8f06f05cbb256b5e56fba0b6aa527fd78aa73cb4 Mon Sep 17 00:00:00 2001 From: Daniel Meyer Date: Mon, 28 Sep 2026 15:59:28 -0700 Subject: [PATCH 08/42] feat(007): land SELECT residual and schedule work (8) --- .../residual_threshold_v2_freeze.py | 119 +++++++++++ .../confound-analysis.schema.json | 193 ++++++++++++++++++ .../hyperlex/select/eval-routing.schema.json | 190 +++++++++++++++++ .../select-eval-routing-contract.md | 67 ++++++ .../semantic-evidence-v2-data-contract.md | 68 ++++++ .../threshold-artifact-schema-contract.md | 68 ++++++ .../test_residual_threshold_v2_direction.py | 130 ++++++++++++ .../test_residual_threshold_v2_support.py | 157 ++++++++++++++ ...test_residual_threshold_v2_surface_draw.py | 120 +++++++++++ tests/shadow/test_select_005_supply.py | 156 ++++++++++++++ tests/shadow/test_select_005_threshold.py | 120 +++++++++++ tests/shadow/test_semantic_evidence_v2.py | 156 ++++++++++++++ 12 files changed, 1544 insertions(+) create mode 100644 scripts/shadow/hyperlexical/residual_threshold_v2_freeze.py create mode 100644 specs/007-hyperlexical-model/schemas/hyperlex/residual-threshold/confound-analysis.schema.json create mode 100644 specs/007-hyperlexical-model/schemas/hyperlex/select/eval-routing.schema.json create mode 100644 specs/007-hyperlexical-model/select-eval-routing-contract.md create mode 100644 specs/007-hyperlexical-model/semantic-evidence-v2-data-contract.md create mode 100644 specs/007-hyperlexical-model/threshold-artifact-schema-contract.md create mode 100644 tests/shadow/test_residual_threshold_v2_direction.py create mode 100644 tests/shadow/test_residual_threshold_v2_support.py create mode 100644 tests/shadow/test_residual_threshold_v2_surface_draw.py create mode 100644 tests/shadow/test_select_005_supply.py create mode 100644 tests/shadow/test_select_005_threshold.py create mode 100644 tests/shadow/test_semantic_evidence_v2.py diff --git a/scripts/shadow/hyperlexical/residual_threshold_v2_freeze.py b/scripts/shadow/hyperlexical/residual_threshold_v2_freeze.py new file mode 100644 index 00000000..f793d7a5 --- /dev/null +++ b/scripts/shadow/hyperlexical/residual_threshold_v2_freeze.py @@ -0,0 +1,119 @@ +"""Write the v2 preregistration once. v1 files are read and then rechecked.""" + +from __future__ import annotations + +import hashlib +import json +from pathlib import Path + +from hyperlexical.residual_threshold_v1_calibration import ANCESTORS, PREREGISTRATION, SOURCE, TRACKER +from hyperlexical.residual_threshold_v1_design_closure import CLOSURE_PATH, EXECUTION_SHA256, REVIEW_PATH +from hyperlexical.residual_threshold_v2 import NEXT_TRANSITION, companion_documents, preregistration + +CURRENT_TRACKER = "a2a71c5da91d194337b2cc0de55692b712d5568694debf5350ede5ea0554d6c2" +CLOSURE_SHA = "a5dbdb8ae497c25e711a81a880ad26ec7ef1ace917278c807497af5ec37e5002" +REVIEW_SHA = "3a6c967c3542096714e25b59544bd68d89d7a9bc28ee253dec1b9f224b5a0484" + + +def sha256(path: Path) -> str: + return hashlib.sha256(path.read_bytes()).hexdigest() + + +def refuse(message: str) -> None: + raise SystemExit(message) + + +def _write_json(path: Path, payload: dict) -> str: + if path.exists(): + refuse(f"refusing to rewrite {path.name}") + text = json.dumps(payload, indent=2, sort_keys=True, ensure_ascii=True) + "\n" + path.write_text(text, encoding="utf-8") + path.chmod(0o600) + return hashlib.sha256(text.encode("utf-8")).hexdigest() + + +def _expect(path: Path, expected: str) -> None: + if sha256(path) != expected: + refuse(f"sealed file changed: {path.name}") + + +def verify_v1(*, include_tracker: bool = True) -> None: + for path, expected in PREREGISTRATION.items(): + _expect(path, expected) + for path, expected in EXECUTION_SHA256.items(): + _expect(path, expected) + for path, expected in ANCESTORS.items(): + if path == TRACKER: + continue + _expect(path, expected) + _expect(CLOSURE_PATH, CLOSURE_SHA) + _expect(REVIEW_PATH, REVIEW_SHA) + if include_tracker: + _expect(TRACKER, CURRENT_TRACKER) + + +def freeze() -> dict: + verify_v1() + companions = companion_documents() + for name in companions: + if (SOURCE / name).exists(): + refuse(f"v2 artifact already exists: {name}") + if (SOURCE / "RESIDUAL_THRESHOLD_V2_PREREGISTRATION.json").exists(): + refuse("v2 preregistration already exists") + sibling = {} + for name, payload in companions.items(): + sibling[name] = _write_json(SOURCE / name, payload) + prereg_path = SOURCE / "RESIDUAL_THRESHOLD_V2_PREREGISTRATION.json" + prereg_sha = _write_json(prereg_path, preregistration(sibling)) + tracker = json.loads(TRACKER.read_text(encoding="utf-8")) + if tracker.get("residual_threshold_state") != "CALIBRATION_INSUFFICIENT_SUPPORT": + refuse("v1 execution state drifted") + if tracker.get("residual_threshold_v1_disposition") != "CLOSED_CALIBRATION_DESIGN_INSUFFICIENT": + refuse("v1 disposition drifted") + if tracker.get("residual_threshold_value") is not None or tracker.get("residual_threshold_frozen") is not False: + refuse("v1 threshold drifted") + tracker["previous_tracker_sha256"] = sha256(TRACKER) + tracker["residual_threshold_v2_rule"] = "RUNE.SEMANTIC_COMPOSITIONALITY_THRESHOLD.v2" + tracker["residual_threshold_v2_state"] = "THRESHOLD_PROCEDURE_PREREGISTERED" + tracker["residual_threshold_v2_value"] = None + tracker["residual_threshold_v2_frozen"] = False + tracker["residual_threshold_v2_calibration_draw_size"] = 200 + tracker["residual_threshold_v2_measurement_draw_size"] = 200 + tracker["residual_threshold_v2_calibration_surface_drawn"] = False + tracker["residual_threshold_v2_measurement_surface_drawn"] = False + tracker["residual_threshold_v2_redraw_authorized"] = False + tracker["residual_threshold_v2_preregistration_sha256"] = prereg_sha + for name, digest in sibling.items(): + key = "residual_threshold_v2_" + name.removeprefix("RESIDUAL_THRESHOLD_V2_").removesuffix(".json").lower() + "_sha256" + tracker[key] = digest + tracker["next_legal_transition"] = NEXT_TRANSITION + tracker["next_transition_authorized"] = False + tracker["selected_source"] = "none" + tracker["select_authorized"] = False + tracker["admitted"] = 0 + tracker["settled"] = 0 + tracker["gold"] = 0 + text = json.dumps(tracker, indent=2, sort_keys=True, ensure_ascii=True) + "\n" + TRACKER.write_text(text, encoding="utf-8") + TRACKER.chmod(0o600) + verify_v1(include_tracker=False) + if tracker["residual_threshold_state"] != "CALIBRATION_INSUFFICIENT_SUPPORT": + refuse("v1 execution state was overwritten") + report = { + "next_legal_transition": NEXT_TRANSITION, + "next_transition_authorized": False, + "preregistration_sha256": prereg_sha, + "sibling_sha256": sibling, + "tracker_sha256": sha256(TRACKER), + "v1_execution_state": "CALIBRATION_INSUFFICIENT_SUPPORT", + } + print(json.dumps(report, indent=2, sort_keys=True)) + return report + + +def main() -> None: + freeze() + + +if __name__ == "__main__": + main() diff --git a/specs/007-hyperlexical-model/schemas/hyperlex/residual-threshold/confound-analysis.schema.json b/specs/007-hyperlexical-model/schemas/hyperlex/residual-threshold/confound-analysis.schema.json new file mode 100644 index 00000000..d65ac71c --- /dev/null +++ b/specs/007-hyperlexical-model/schemas/hyperlex/residual-threshold/confound-analysis.schema.json @@ -0,0 +1,193 @@ +{ + "$schema": "https://json-schema.org/draft/2020-12/schema", + "$id": "https://hyperlex.local/schemas/hyperlex/residual-threshold/confound-analysis.schema.json", + "title": "Residual-threshold confound analysis", + "oneOf": [ + { + "$ref": "#/$defs/notRun" + }, + { + "$ref": "#/$defs/executed" + } + ], + "$defs": { + "stopFlags": { + "type": "object", + "additionalProperties": false, + "required": [ + "extreme_driven", + "pos_partitioned", + "tier3_concentrated" + ], + "properties": { + "extreme_driven": { + "type": "boolean" + }, + "pos_partitioned": { + "type": "boolean" + }, + "tier3_concentrated": { + "type": "boolean" + } + } + }, + "notRun": { + "type": "object", + "additionalProperties": false, + "required": [ + "confound_gate_executed", + "confound_result", + "frozen_stop_conditions", + "json_schema_document", + "json_schema_exists", + "reason", + "schema" + ], + "properties": { + "confound_gate_executed": { + "const": false + }, + "confound_result": { + "const": "NOT_RUN" + }, + "frozen_stop_conditions": { + "type": "array", + "items": { + "enum": [ + "tier3_concentrated", + "extreme_driven", + "pos_partitioned" + ] + }, + "minItems": 3, + "uniqueItems": true + }, + "json_schema_document": { + "type": "null" + }, + "json_schema_exists": { + "const": false + }, + "reason": { + "$ref": "common.schema.json#/$defs/threshold_state" + }, + "schema": { + "type": "string", + "minLength": 1 + } + } + }, + "executed": { + "type": "object", + "additionalProperties": false, + "required": [ + "analysis_scope", + "confound_gate_executed", + "confound_result", + "direction_analysis_sha256", + "direction_result", + "frozen_stop_conditions", + "json_schema_document", + "json_schema_exists", + "nuisance_changes_stop_decision", + "nuisance_field_provenance", + "report", + "residual_recomputed", + "rows_excluded", + "schema", + "source_label_hash", + "source_score_hash", + "source_support_join_hash", + "threshold_search_run" + ], + "properties": { + "analysis_scope": { + "const": "CONFOUND_THEN_THRESHOLD_IF_PASS" + }, + "confound_gate_executed": { + "const": true + }, + "confound_result": { + "enum": [ + "PASS", + "STOP" + ] + }, + "direction_analysis_sha256": { + "$ref": "common.schema.json#/$defs/sha256" + }, + "direction_result": { + "const": "SUPPORTED_DIRECTION" + }, + "frozen_stop_conditions": { + "$ref": "#/$defs/stopFlags" + }, + "json_schema_document": { + "type": "null" + }, + "json_schema_exists": { + "const": false + }, + "nuisance_changes_stop_decision": { + "const": false + }, + "nuisance_field_provenance": { + "type": "object" + }, + "report": { + "type": "object", + "required": [ + "extreme_driven", + "nuisance", + "pos", + "pos_partitioned", + "tier3", + "tier3_counts" + ], + "properties": { + "extreme_driven": { + "type": "boolean" + }, + "nuisance": { + "type": "object" + }, + "pos": { + "type": "object" + }, + "pos_partitioned": { + "type": "boolean" + }, + "tier3": { + "type": "object" + }, + "tier3_counts": { + "type": "object" + } + } + }, + "residual_recomputed": { + "const": false + }, + "rows_excluded": { + "const": 0 + }, + "schema": { + "type": "string", + "minLength": 1 + }, + "source_label_hash": { + "$ref": "common.schema.json#/$defs/sha256" + }, + "source_score_hash": { + "$ref": "common.schema.json#/$defs/sha256" + }, + "source_support_join_hash": { + "$ref": "common.schema.json#/$defs/sha256" + }, + "threshold_search_run": { + "type": "boolean" + } + } + } + } +} diff --git a/specs/007-hyperlexical-model/schemas/hyperlex/select/eval-routing.schema.json b/specs/007-hyperlexical-model/schemas/hyperlex/select/eval-routing.schema.json new file mode 100644 index 00000000..06822eac --- /dev/null +++ b/specs/007-hyperlexical-model/schemas/hyperlex/select/eval-routing.schema.json @@ -0,0 +1,190 @@ +{ + "$schema": "https://json-schema.org/draft/2020-12/schema", + "$id": "https://hyperlex.local/schemas/hyperlex.eval_routing.v1", + "title": "SELECT evaluation routing record", + "description": "Derivative of one canonical settlement. It does not rewrite the settlement.", + "type": "object", + "additionalProperties": false, + "required": [ + "class", + "created_from_frozen_artifact", + "derivation_procedure_hash", + "derivation_procedure_id", + "lineage", + "normalized_identity", + "provenance_state", + "rights_state", + "routing_reason_codes", + "routing_status", + "routing_version", + "row_id", + "schema", + "settlement_decision", + "settlement_registry_class", + "slices", + "source_identity", + "source_settlement_hash", + "source_settlement_id", + "task", + "unbind_clean" + ], + "properties": { + "schema": {"const": "hyperlex.eval_routing.v1"}, + "routing_version": {"const": "v1"}, + "row_id": {"type": "string", "minLength": 1}, + "normalized_identity": {"$ref": "#/$defs/sha256"}, + "source_identity": {"type": "string"}, + "source_settlement_id": {"type": "string", "minLength": 1}, + "source_settlement_hash": {"$ref": "#/$defs/sha256"}, + "settlement_decision": { + "enum": ["ACCEPT", "RECLASSIFY", "NONE", "UNRESOLVED"] + }, + "settlement_registry_class": {"$ref": "#/$defs/registry_class"}, + "rights_state": {"type": "string"}, + "provenance_state": {"enum": ["COMPLETE", "INCOMPLETE"]}, + "task": {"$ref": "#/$defs/task"}, + "class": {"$ref": "#/$defs/registry_class"}, + "lineage": {"$ref": "#/$defs/lineage"}, + "unbind_clean": {"type": ["boolean", "null"]}, + "routing_status": {"enum": ["ELIGIBLE", "INELIGIBLE"]}, + "routing_reason_codes": { + "type": "array", + "items": {"$ref": "#/$defs/reason_code"}, + "uniqueItems": true + }, + "derivation_procedure_id": {"const": "select_eval_routing_v1"}, + "derivation_procedure_hash": {"$ref": "#/$defs/sha256"}, + "created_from_frozen_artifact": {"const": true}, + "slices": { + "type": "array", + "items": { + "enum": ["classify", "classify_non_none", "classify_observed", "unbind_clean"] + }, + "uniqueItems": true + } + }, + "$defs": { + "sha256": {"type": "string", "pattern": "^[0-9a-f]{64}$"}, + "task": { + "anyOf": [ + {"type": "null"}, + {"enum": ["classify", "unbind"]} + ] + }, + "registry_class": { + "anyOf": [ + {"type": "null"}, + {"enum": ["INFERRED", "OBSERVED"]} + ] + }, + "lineage": { + "anyOf": [ + {"type": "null"}, + { + "enum": [ + "ai-native", + "approval-disapproval", + "betting-sharp", + "conflict-aggression", + "crypto-degen", + "fashion-aesthetic", + "gaming-meta", + "identity-affiliation", + "internet-slang", + "memetic", + "music-entertainment", + "none", + "politics-civic", + "regional-cultural", + "relationship-dating", + "social-status", + "spiritual-mystic", + "sports-competition", + "technology-ai", + "workplace-career" + ] + } + ] + }, + "reason_code": { + "enum": [ + "CLASS_PROMOTION_REFUSED", + "DUPLICATE_IDENTITY", + "IDENTITY_CONFLICT", + "LINEAGE_ABSENT", + "LINEAGE_CONFLICT", + "NO_SELECT_SLICE", + "PROVENANCE_INCOMPLETE", + "REGISTRY_CLASS_ABSENT", + "RIGHTS_NOT_CLEARED", + "SETTLEMENT_DECISION_NOT_ADMISSIBLE", + "SETTLEMENT_UNRESOLVED", + "SLICE_CLASSIFY", + "SLICE_CLASSIFY_NON_NONE", + "SLICE_CLASSIFY_OBSERVED", + "SLICE_UNBIND_CLEAN", + "TASK_ABSENT", + "UNBIND_CLEAN_DERIVATION_ABSENT", + "UNBIND_CLEAN_TASK_MISMATCH" + ] + } + }, + "allOf": [ + { + "if": { + "properties": {"routing_status": {"const": "ELIGIBLE"}}, + "required": ["routing_status"] + }, + "then": { + "properties": { + "class": {"enum": ["INFERRED", "OBSERVED"]}, + "lineage": {"type": "string", "minLength": 1}, + "provenance_state": {"const": "COMPLETE"}, + "rights_state": {"const": "CC-BY-SA"}, + "routing_reason_codes": {"minItems": 1}, + "slices": {"minItems": 1}, + "source_identity": {"minLength": 1}, + "task": {"enum": ["classify", "unbind"]}, + "unbind_clean": {"type": "boolean"} + } + } + }, + { + "if": { + "properties": {"routing_status": {"const": "INELIGIBLE"}}, + "required": ["routing_status"] + }, + "then": { + "properties": { + "routing_reason_codes": {"minItems": 1}, + "slices": {"maxItems": 0} + } + } + }, + { + "if": { + "properties": { + "slices": {"contains": {"const": "classify_observed"}} + }, + "required": ["slices"] + }, + "then": { + "properties": {"class": {"const": "OBSERVED"}} + } + }, + { + "if": { + "properties": { + "slices": {"contains": {"const": "unbind_clean"}} + }, + "required": ["slices"] + }, + "then": { + "properties": { + "task": {"const": "unbind"}, + "unbind_clean": {"const": true} + } + } + } + ] +} diff --git a/specs/007-hyperlexical-model/select-eval-routing-contract.md b/specs/007-hyperlexical-model/select-eval-routing-contract.md new file mode 100644 index 00000000..0e8f1db6 --- /dev/null +++ b/specs/007-hyperlexical-model/select-eval-routing-contract.md @@ -0,0 +1,67 @@ +# SELECT evaluation routing contract + +`SELECT_005_SETTLEMENT_ROUTING_CONTRACT_EXTENSION_AUTHORIZATION` freezes a derivative path from one canonical settlement to SELECT reserve slices. + +Canonical settlement and SELECT routing stay separate. A routing record references a settlement by `row_id` and `source_settlement_hash`. It does not rewrite `settlement/events.jsonl`, and it is not an identity-ledger event. + +Procedure id: `select_eval_routing_v1`. Schema: `hyperlex.eval_routing.v1`. Routing version: `v1`. + +## State + +`ROUTING_CONTRACT_DRAFTED` names the family. `ROUTING_CONTRACT_FROZEN` is this specification plus the schema. `ROUTING_VALIDATION_COMPLETE` means the reference corpus validates and two reconstructions are byte-identical. + +Failure states are `ROUTING_SCHEMA_FAILURE`, `ROUTING_SEMANTIC_VALIDATION_FAILURE`, and `ROUTING_NONDETERMINISTIC`. + +## Store + +Routing records live in the private evaluation evidence store at `eval-reserve-20260926/routing/events.jsonl`, append-only, mode 0600. They are not mixed into the identity ledger and not appended to `settlement/events.jsonl`. This pass writes no routing events. + +## Fields + +`task` is the training-label task. The canonical vocabulary is `classify` and `unbind`. + +`class` is the registry attest class. The canonical vocabulary is `OBSERVED` and `INFERRED`, the same pair as settlement `attest`. It is not the semantic family and not the operator decision. Routing copies `attest`. A metadata `class` that differs from `attest` is `CLASS_PROMOTION_REFUSED`. `INFERRED` is not promoted to `OBSERVED`. + +`lineage` is the semantic family copied from settlement `semantic_family`. The vocabulary is `none` plus the active evaluation families. `none` is the abstain family. It is not a registry class. + +`unbind_clean` is the boolean already computed by `soft_ceiling.clean_surface`. Routing copies it only when `task` is `unbind` and `unbind_clean_derivation` is `soft_ceiling.clean_surface`. It is not set because a reserve slice is empty. + +`rights_state` is the settlement rights string. Routing does not reinterpret a license. The cleared token is `CC-BY-SA`, from `wiktionary_category` and `wikipedia_prose` only. + +`provenance_state` is `COMPLETE` or `INCOMPLETE`. `COMPLETE` requires a non-empty `source_identity` and a non-empty settlement `provenance`. Missing provenance is not inferred. + +## Slice rules + +The predicate is `identity_ledger.slices_of`. One routing record has one task. That record may enter every classify slice it satisfies. `unbind_clean` does not co-occur with a classify slice on the same record, because those slices require different tasks. + +Settlement exclusions still win before routing. `TRAIN_CONSUMED`, `EVAL_SPENT`, `EVAL_ABANDONED`, the pinned export, unresolved rights, and a non-admissible decision are unchanged. Routing is consulted only when the settlement would otherwise be `SLICE_LABELS_ABSENT`. + +`classify` requires decision `ACCEPT`, `RECLASSIFY`, or `NONE`, rights `CC-BY-SA`, provenance `COMPLETE`, and `task` `classify`. + +`classify_observed` requires `classify` and registry `class` `OBSERVED`. + +`classify_non_none` requires `classify` and `lineage` outside `""` and `none`. + +`unbind_clean` requires `task` `unbind` and the frozen `unbind_clean` flag true. + +`UNRESOLVED` is not routable. `NONE` can enter `classify` and can enter `unbind_clean` when the frozen flag is true. `NONE` does not enter `classify_non_none`, because its family is `none`. `ACCEPT` and `RECLASSIFY` use the same slice predicate. Routing does not repeat the settlement family's proposed-evidence check. + +Rights other than `CC-BY-SA` are `INELIGIBLE` with `RIGHTS_NOT_CLEARED`. Provenance that is not `COMPLETE` is `INELIGIBLE` with `PROVENANCE_INCOMPLETE`. + +## What routing does not read + +Routing refuses `reserve_counts`, `desired_slice`, `routed_counts`, `current_reserve_deficits`, `planning_targets`, `admission_floors`, `prediction`, `model_score`, `candidate_score`, `model_error`, `residual`, `semantic_evidence`, and `threshold_v2`. The same settlement produces the same record whether or not a slice quota is open. Candidate scores, threshold-v2 residuals, and semantic-evidence features are not inputs. + +No RNG. The procedure hash is the sha256 of the canonical rule table. Two calls on the same frozen inputs are byte-identical. + +## Admission + +`census(..., routing_by_row=...)` joins a validated routing record when the settlement itself has no `task`. Omitting the map preserves the previous census. `admit_training_run` still consumes a sealed reserve binding. It does not require routing records, so SELECT-004 bindings stay valid. This pass does not recensus the 339 settlements and does not create a SELECT-005 admission receipt. + +## Other schemas + +`hyperlex.threshold_authorization.v1` and `hyperlex.admission.v1` now have JSON Schema documents. Historical artifacts are `VALIDATE_ONLY`. SELECT-005 metric numbers stay unset. `HLX_THRESHOLD_AUTHORIZATION` remains `BLOCKED_PENDING_OPERATOR_AUTHORIZATION`. + +## Next transition + +`SELECT_005_FRESH_SOURCE_HARVEST_AND_ROUTING_AUTHORIZATION` waits on a box exposure snapshot no older than 6 hours. This pass does not refresh that snapshot and does not harvest. diff --git a/specs/007-hyperlexical-model/semantic-evidence-v2-data-contract.md b/specs/007-hyperlexical-model/semantic-evidence-v2-data-contract.md new file mode 100644 index 00000000..abd6bc39 --- /dev/null +++ b/specs/007-hyperlexical-model/semantic-evidence-v2-data-contract.md @@ -0,0 +1,68 @@ +# Semantic evidence v2 data contract + +The executable definition is `scripts/shadow/hyperlexical/semantic_evidence_v2.py`. Schemas are `specs/007-hyperlexical-model/schemas/hyperlex/semantic-evidence-v2/`. This contract does not encode a model and does not read private surfaces. + +## Templates + +Parent text, frozen before any performance is observed: + +```text +surface: +part_of_speech: +definition: +``` + +Constituent text: + +```text +surface: +definition: +``` + +`template_sha256()` hashes those two format strings. Constituent senses come from an already frozen resolver. The evidence function does not call WSD. + +## Features + +Order is fixed: + +```text +F1 whole_vs_constituent_mean_cosine +F2 whole_vs_constituent_mean_residual +F3 whole_vs_each_constituent_min_similarity +F4 whole_vs_each_constituent_max_similarity +F5 whole_vs_each_constituent_mean_similarity +F6 whole_vs_each_constituent_similarity_variance +F7 constituent_pair_mean_similarity +F8 constituent_pair_min_similarity +F9 constituent_pair_variance +F10 whole_gloss_vs_composed_gloss_similarity +F11 whole_lemma_vs_composed_constituent_similarity +F12 constituent_count +F13 token_count +``` + +F2 is `1 - cosine(parent, normalized mean of constituent vectors)`. Stored similarities and that residual use 10 decimal places, round half even. Cosine is undefined for a zero vector and the record abstains rather than inventing a value. Pairwise variance is the population variance. F12 and F13 are integer controls. They are not semantic predictors unless a later preregistration says so. + +Missing or unresolved constituent structure, fewer than two constituents, or a missing gloss or lemma view yields `evidence_status: UNKNOWN`, `features: null`, and output `UNKNOWN`. + +## Composition + +The preregistered composition set is: + +```text +C1 normalized_mean_v1 +C2 weighted_mean_by_constituent_token_span +C3 pairwise_relation_summary +``` + +C1 is the historical baseline composition. C2 uses positive token-span weights and then L2-normalizes. C3 summarizes constituent pairs. There is no learned attention and no trainable composition layer in this draft. + +## Views + +Definition view compares the parent gloss representation with the composed constituent glosses. Surface-semantic view compares the parent expression representation with constituent representations. Relational view is the pairwise constituent summary, so one cosine does not carry the decision alone. + +## Decision boundary + +Feature generation does not emit YES. YES and UNKNOWN belong to a later calibrator. NO is not an output. REJECT and QUARANTINE are not feature targets. + +A representation freeze, when one is eventually authorized, must record model name, revision, artifact hash, tokenizer hash, template hash, dtype, device class, batch size, seed, threading, and composition-function version. `representation-freeze.schema.json` is that shape. No freeze file is written by this draft. diff --git a/specs/007-hyperlexical-model/threshold-artifact-schema-contract.md b/specs/007-hyperlexical-model/threshold-artifact-schema-contract.md new file mode 100644 index 00000000..a744dc52 --- /dev/null +++ b/specs/007-hyperlexical-model/threshold-artifact-schema-contract.md @@ -0,0 +1,68 @@ +# Threshold artifact schema contract + +Workstream A is `THRESHOLD_ARTIFACT_SCHEMA_HARDENING`. Its state machine is separate from semantic-evidence v2. + +```text +UNSPECIFIED + -> SCHEMA_FAMILY_DRAFTED + -> SCHEMA_FAMILY_FROZEN + -> HISTORICAL_VALIDATION_COMPLETE +``` + +Failure states are `SCHEMA_CONFORMANCE_GAP` and `SCHEMA_SEMANTIC_VALIDATION_FAILURE`. A schema failure does not mutate a frozen historical artifact. + +## Family + +The contract family is `specs/007-hyperlexical-model/schemas/hyperlex/residual-threshold/`. It is not one schema. The earlier retrospective family under `specs/007-hyperlexical-model/schemas/residual-threshold/` remains the emission-matched check from the previous hardening pass. This family is the named contract. Both are validate-only. + +Files: + +- `common.schema.json` +- `surface-manifest-row.schema.json` +- `surface-draw-receipt.schema.json` +- `resolution-row.schema.json` +- `resolution-receipt.schema.json` +- `score-row.schema.json` +- `score-receipt.schema.json` +- `operator-label-row.schema.json` +- `operator-labeling-receipt.schema.json` +- `support-join-row.schema.json` +- `support-reassessment.schema.json` +- `direction-analysis.schema.json` +- `direction-decision.schema.json` +- `confound-analysis.schema.json` +- `confound-decision.schema.json` +- `threshold-search.schema.json` +- `threshold-decision.schema.json` +- `calibration-failure.schema.json` +- `completion-receipt.schema.json` + +## Canonical terms + +`common.schema.json` closes sha256 as `^[0-9a-f]{64}$`. Operator labels are HIGH, SECONDARY, REJECT, QUARANTINE, and UNRESOLVED. Parent row resolution is RESIDUAL_READY or UNKNOWN. Constituent resolution status stays EXACT, RESOLVED, AMBIGUOUS, or UNRESOLVED. Those are different fields. Score status stays SCORED or UNKNOWN. SCORED is the emitted form of a residual-ready parent. + +Threshold state is closed to the emitted lane states: `THRESHOLD_PROCEDURE_PREREGISTERED`, `SURFACES_FROZEN`, `CALIBRATION_INSUFFICIENT_SUPPORT`, `SUPPORT_GATE_PASSED`, `SUPPORTED_DIRECTION`, `NO_DIRECTIONAL_SIGNAL`, `CALIBRATION_CONFOUND_REVIEW`, `NO_THRESHOLD_PASSES_PRECISION_GATE`, and `THRESHOLD_FROZEN`. Direction results keep `INVERTED_DIRECTION` and `NO_DIRECTIONAL_SEPARATION` on their own enum. `NO_DIRECTIONAL_SIGNAL` is the threshold-lane name and is not a substitute for `NO_DIRECTIONAL_SEPARATION`. + +Measurement state allows SEALED, ELIGIBLE, and EXECUTED. The only emitted value is SEALED. When `measurement_state` is SEALED, present execution flags (`measurement_resolved`, `measurement_scored`, `measurement_labeled`, `measurement_touched`, and the GlossBERT and MiniLM run flags) must be false. Historical receipts use false, not null. + +An unfrozen record requires `threshold_value` null. A frozen record requires the 10-decimal residual string, `predicted_yes_support` at least 8, precision, Wilson bounds, HIGH recall, leave-one-out PASS, and zero REJECT and QUARANTINE predicted-YES counts. Those names are the selector's names. No frozen success file was emitted. `threshold-decision.schema.json` holds that branch as a contract only. + +Historical `json_schema_exists` stays false. That field records emission time. + +## Semantic checks + +JSON Schema does not check cross-artifact identity. `scripts/shadow/hyperlexical/residual_threshold_contract.py` checks, for the v2 calibration lane: + +- calibration manifest, operator labels, and support join each have 200 rows and the same row-id sequence +- ready plus unknown is 200 +- calibration synsets are unique, measurement synsets are unique, and the two synset sets are disjoint +- manifest order is the frozen draw order +- label inventory totals 200 +- join, draw, direction, and resolution receipts match the hashes of the files they name +- the measurement manifest hash remains `78ca09ab14912681d028e8b9b1c77a8daf1d0c8c81561434eb9b6ade45a753e5` + +The public report is `CONTRACT_VALIDATION.json`. The current outcome is `HISTORICAL_VALIDATION_COMPLETE`. Gap records contain the artifact, schema, field path, a redacted observed value, and `historical_artifact_mutated: false`. Surface text is not copied into the report. + +## What this workstream does not do + +It does not retune threshold v2, redraw either surface, execute measurement, change the 0.80 precision floor or the Wilson floor, weaken the REJECT or QUARANTINE vetoes, or select a threshold from the failed candidates. diff --git a/tests/shadow/test_residual_threshold_v2_direction.py b/tests/shadow/test_residual_threshold_v2_direction.py new file mode 100644 index 00000000..c1c9e9f2 --- /dev/null +++ b/tests/shadow/test_residual_threshold_v2_direction.py @@ -0,0 +1,130 @@ +"""Direction evaluation compares frozen HIGH and SECONDARY residuals only.""" + +from __future__ import annotations + +import inspect +from pathlib import Path + +import pytest + +from hyperlexical.residual_model_resolved_replay_v1 import ( + INVERTED, + NO_SEPARATION, + SUPPORTED, + direction_result, + pair_comparison, +) +from hyperlexical.residual_threshold_v2_direction import ( + ANALYSIS_SCOPE, + CONFOUND_NEXT, + EXPECTED_JOIN, + EXPECTED_MEASUREMENT, + FROZEN_READY, + UNCERTAINTY, + compare_direction, + ready_residuals, +) + + +def _row(index: int, label: str, status: str, score: str | None) -> dict: + return { + "calibration_row_id": index, + "operator_label": label, + "pos": "noun", + "pwn30_synset": f"noun:{index:08d}", + "residual_score": score, + "row_id": f"row-{index}", + "row_resolution_status": status, + "surface": f"surface {index}", + } + + +def _panel(high: list[str], secondary: list[str]) -> list[dict]: + rows = [] + index = 1 + plan = [ + ("HIGH", "RESIDUAL_READY", high), + ("SECONDARY", "RESIDUAL_READY", secondary), + ("REJECT", "RESIDUAL_READY", ["0.0100000000"] * 24), + ("QUARANTINE", "RESIDUAL_READY", ["0.0200000000"]), + ("HIGH", "UNKNOWN", [None] * 31), + ("SECONDARY", "UNKNOWN", [None] * 60), + ("REJECT", "UNKNOWN", [None] * 47), + ("QUARANTINE", "UNKNOWN", [None] * 2), + ] + for label, status, scores in plan: + for score in scores: + rows.append(_row(index, label, status, score)) + index += 1 + assert len(rows) == 200 + return rows + + +def test_ready_residuals_copy_the_frozen_strings_and_leave_reject_out() -> None: + high = ["0.8100000000"] * 13 + secondary = ["0.2200000000"] * 22 + rows = _panel(high, secondary) + ready = ready_residuals(rows, expect_frozen_counts=True) + assert ready["HIGH"] == high + assert ready["SECONDARY"] == secondary + assert len(ready["REJECT"]) == FROZEN_READY["REJECT"] + assert len(ready["QUARANTINE"]) == FROZEN_READY["QUARANTINE"] + assert ready["HIGH"][0] == "0.8100000000" + + +def test_supported_direction_uses_the_frozen_rule() -> None: + high = ["0.8000000000", "0.9000000000"] + secondary = ["0.1000000000", "0.2000000000"] + report = compare_direction(high, secondary) + assert report["direction_result"] == direction_result(pair_comparison(high, secondary)) + assert report["direction_result"] == SUPPORTED + assert report["high_larger_pairs"] == 4 + assert report["secondary_larger_pairs"] == 0 + assert report["ties"] == 0 + assert report["high_distribution"]["count"] == 2 + assert report["secondary_distribution"]["count"] == 2 + + +def test_inverted_direction_is_preserved() -> None: + high = ["0.1000000000", "0.2000000000"] + secondary = ["0.8000000000", "0.9000000000"] + report = compare_direction(high, secondary) + assert report["direction_result"] == INVERTED + assert report["secondary_larger_pairs"] == 4 + assert report["high_larger_pairs"] == 0 + + +def test_no_directional_separation_is_preserved() -> None: + high = ["0.5000000000"] + secondary = ["0.5000000000"] + report = compare_direction(high, secondary) + assert report["direction_result"] == NO_SEPARATION + assert report["ties"] == 1 + frozen = pair_comparison(high, secondary) + assert report["mean_difference"] == frozen["mean_difference"] + assert report["roc_auc"] == frozen["auc"] == "0.500000" + + +def test_ready_count_drift_is_refused() -> None: + rows = _panel(["0.8100000000"] * 13, ["0.2200000000"] * 22) + rows[0]["operator_label"] = "SECONDARY" + with pytest.raises(SystemExit): + ready_residuals(rows, expect_frozen_counts=True) + + +def test_direction_module_stops_before_confound_and_threshold_search() -> None: + path = Path(inspect.getsourcefile(compare_direction)) + text = path.read_text(encoding="utf-8") + assert EXPECTED_JOIN in text + assert EXPECTED_MEASUREMENT in text + assert UNCERTAINTY in text + assert ANALYSIS_SCOPE in text + assert CONFOUND_NEXT in text + assert "bootstrap_intervals(" not in text + assert "select_threshold(" not in text + assert "_candidate_thresholds(" not in text + assert "_confound(" not in text + assert "extreme_driven" not in text + source = inspect.getsource(compare_direction) + assert "pair_comparison(high_scores, secondary_scores)" in source + assert "REJECT" not in source diff --git a/tests/shadow/test_residual_threshold_v2_support.py b/tests/shadow/test_residual_threshold_v2_support.py new file mode 100644 index 00000000..61becae8 --- /dev/null +++ b/tests/shadow/test_residual_threshold_v2_support.py @@ -0,0 +1,157 @@ +"""Support reassessment counts frozen ready labels and does not search a threshold.""" + +from __future__ import annotations + +import inspect +from pathlib import Path + +import pytest + +from hyperlexical.residual_threshold_v1 import MIN_CALIBRATION_HIGH, MIN_CALIBRATION_SECONDARY +from hyperlexical.residual_threshold_v2_support import ( + EXPECTED_MEASUREMENT, + FAILED, + FROZEN_READY, + FROZEN_UNKNOWN, + JOIN_KEYS, + PASSED, + gate_state, + join_rows, + support_inventory, + support_passed, +) + + +def _manifest(index: int) -> dict: + return { + "global_draw_order": index, + "normalized_text_sha256": f"row-{index}", + "pos": "noun", + "pwn30_synset": f"noun:{index:08d}", + "row_id": f"row-{index}", + "surface": f"surface {index}", + } + + +def _label(index: int, bucket: str) -> dict: + return { + "calibration_row_id": index, + "operator_label": bucket, + "pos": "noun", + "pwn30_synset": f"noun:{index:08d}", + "row_id": f"row-{index}", + "surface": f"surface {index}", + } + + +def _parent(index: int, status: str) -> dict: + return { + "pos": "noun", + "row_id": f"row-{index}", + "row_resolution_status": status, + "surface": f"surface {index}", + "synset": f"noun:{index:08d}", + } + + +def _score(index: int, status: str) -> dict: + row = {"row_id": f"row-{index}", "score_status": "SCORED" if status == "RESIDUAL_READY" else "UNKNOWN"} + if status == "RESIDUAL_READY": + row["residual_score"] = "0.1000000000" + return row + + +def _population() -> tuple[list[dict], list[dict], list[dict], list[dict]]: + manifest, labels, parents, scores = [], [], [], [] + index = 1 + plan = [ + ("HIGH", "RESIDUAL_READY", 12), + ("SECONDARY", "RESIDUAL_READY", 8), + ("REJECT", "RESIDUAL_READY", 20), + ("QUARANTINE", "RESIDUAL_READY", 20), + ("HIGH", "UNKNOWN", 32), + ("SECONDARY", "UNKNOWN", 74), + ("REJECT", "UNKNOWN", 31), + ("QUARANTINE", "UNKNOWN", 3), + ] + for bucket, status, count in plan: + for _ in range(count): + manifest.append(_manifest(index)) + labels.append(_label(index, bucket)) + parents.append(_parent(index, status)) + scores.append(_score(index, status)) + index += 1 + assert index == 201 + return manifest, labels, parents, scores + + +def test_floors_remain_twelve_and_eight() -> None: + assert MIN_CALIBRATION_HIGH == 12 + assert MIN_CALIBRATION_SECONDARY == 8 + assert support_passed(12, 8) is True + assert support_passed(11, 8) is False + assert support_passed(12, 7) is False + + +def test_ready_labels_pass_without_reading_the_residual() -> None: + manifest, labels, parents, scores = _population() + joined = join_rows(manifest, labels, parents, scores) + assert set(joined[0]) == JOIN_KEYS + joined[0]["residual_score"] = "9.9999999999" + inventory = support_inventory(joined) + assert inventory["ready_rows"] == FROZEN_READY + assert inventory["unknown_rows"] == FROZEN_UNKNOWN + assert inventory["ready"] == {"HIGH": 12, "SECONDARY": 8, "REJECT": 20, "QUARANTINE": 20} + assert inventory["ready_unresolved"] == 0 + assert gate_state(inventory) == PASSED + + +def test_short_ready_high_fails_and_unknown_labels_do_not_count() -> None: + manifest, labels, parents, scores = _population() + parents[0]["row_resolution_status"] = "UNKNOWN" + scores[0] = _score(1, "UNKNOWN") + unknown_reject = 166 + assert labels[unknown_reject]["operator_label"] == "REJECT" + parents[unknown_reject]["row_resolution_status"] = "RESIDUAL_READY" + scores[unknown_reject] = _score(unknown_reject + 1, "RESIDUAL_READY") + scores[unknown_reject]["row_id"] = labels[unknown_reject]["row_id"] + joined = join_rows(manifest, labels, parents, scores) + inventory = support_inventory(joined) + assert inventory["ready"]["HIGH"] == 11 + assert inventory["unknown_by_class"]["HIGH"] == 33 + assert inventory["ready_rows"] == FROZEN_READY + assert gate_state(inventory) == FAILED + + +def test_join_keeps_the_frozen_label_and_null_residual() -> None: + joined = join_rows( + [_manifest(1)], + [_label(1, "QUARANTINE")], + [_parent(1, "UNKNOWN")], + [_score(1, "UNKNOWN")], + ) + assert joined[0]["operator_label"] == "QUARANTINE" + assert joined[0]["row_resolution_status"] == "UNKNOWN" + assert joined[0]["residual_score"] is None + + +def test_join_refuses_a_synset_mismatch() -> None: + parent = _parent(1, "UNKNOWN") + parent["synset"] = "noun:00000000" + with pytest.raises(SystemExit): + join_rows([_manifest(1)], [_label(1, "HIGH")], [parent], [_score(1, "UNKNOWN")]) + + +def test_support_module_does_not_search_or_open_measurement_work() -> None: + path = Path(inspect.getsourcefile(support_inventory)) + text = path.read_text(encoding="utf-8") + source = inspect.getsource(support_inventory) + assert EXPECTED_MEASUREMENT in text + assert "residual_score" not in source + assert "_candidate_thresholds(" not in text + assert "_confound(" not in text + assert "select_threshold(" not in text + assert "DIRECTION_NEXT" in text + assert "MEASUREMENT_MANIFEST" not in inspect.getsource(join_rows) + with pytest.raises(SystemExit): + gate_state({"ready": {"HIGH": 12, "SECONDARY": 8}, "ready_rows": 59, "unknown_rows": 141}) diff --git a/tests/shadow/test_residual_threshold_v2_surface_draw.py b/tests/shadow/test_residual_threshold_v2_surface_draw.py new file mode 100644 index 00000000..2bc60947 --- /dev/null +++ b/tests/shadow/test_residual_threshold_v2_surface_draw.py @@ -0,0 +1,120 @@ +"""The v2 surface draw is one label-blind walk with fixed quotas.""" + +from __future__ import annotations + +import inspect +from pathlib import Path + +from hyperlexical.residual_threshold_v2_surface_draw import ( + MANIFEST_FIELDS, + SAMPLING_RULE_ID, + _identity_fences, + allocate_surfaces, + build_universe, + manifest_row, +) + + +def _item(digest: str, pos: str, synset: str, surface: str) -> dict: + return { + "frozen_gloss": f"gloss {surface}", + "normalized_text_sha256": digest, + "operator_bucket": "HIGH", + "pos": pos, + "pwn30_synset": synset, + "surface": surface, + } + + +def _cells() -> dict[tuple[str, int], list[dict]]: + return { + ("noun", 2): [ + _item("n2", "noun", "noun:2", "noun two"), + _item("n0", "noun", "noun:0", "noun zero"), + _item("n1", "noun", "noun:1", "noun one"), + ], + ("adj", 2): [ + _item("a1", "adj", "adj:1", "adj one"), + _item("a0", "adj", "adj:0", "adj zero"), + ], + ("adj", 3): [ + _item("a3", "adj", "adj:3", "adj three"), + ], + } + + +def test_frozen_walk_fills_calibration_before_continuing() -> None: + allocated = allocate_surfaces(_cells(), 3, 3) + assert allocated["filled"] is True + assert allocated["shortage"] is None + calibration = allocated["calibration"] + measurement = allocated["measurement"] + assert [row["row_id"] for row in calibration] == ["a0", "a3", "n0"] + assert [row["row_id"] for row in measurement] == ["a1", "n1", "n2"] + assert [row["global_draw_order"] for row in calibration] == [1, 2, 3] + assert [row["global_draw_order"] for row in measurement] == [4, 5, 6] + assert [row["surface_role"] for row in calibration] == ["CALIBRATION_V2"] * 3 + assert [row["surface_role"] for row in measurement] == ["MEASUREMENT_V2"] * 3 + assert calibration[0]["within_stratum_order"] == 1 + assert measurement[0]["within_stratum_order"] == 2 + assert calibration[0]["stratum_id"] == "adj:2" + assert {row["allocation_rule_id"] for row in calibration + measurement} == {SAMPLING_RULE_ID} + + +def test_same_cell_is_not_alternating_when_the_walk_returns() -> None: + cells = { + ("adj", 2): [_item(f"a{i}", "adj", f"adj:{i}", f"adj {i}") for i in range(4)], + ("noun", 2): [_item(f"n{i}", "noun", f"noun:{i}", f"noun {i}") for i in range(4)], + } + allocated = allocate_surfaces(cells, 3, 3) + assert [row["row_id"] for row in allocated["calibration"]] == ["a0", "n0", "a1"] + assert [row["row_id"] for row in allocated["measurement"]] == ["n1", "a2", "n2"] + + +def test_two_reconstructions_match_and_unsorted_input_is_ordered() -> None: + first = allocate_surfaces(_cells(), 3, 3) + second = allocate_surfaces(_cells(), 3, 3) + assert first == second + assert first["calibration"][0]["row_id"] == "a0" + + +def test_unfilled_quota_does_not_shrink_or_emit_partial_manifests() -> None: + allocated = allocate_surfaces(_cells(), 4, 4) + assert allocated["filled"] is False + assert allocated["calibration"] == [] + assert allocated["measurement"] == [] + shortage = allocated["shortage"] + assert shortage["calibration_quota"] == 4 + assert shortage["measurement_quota"] == 4 + assert shortage["calibration_rows_emitted"] == 4 + assert shortage["measurement_rows_emitted"] == 2 + exhausted = [cell for cell in shortage["cells"] if cell["exhausted"]] + assert len(exhausted) == 3 + + +def test_manifest_rows_omit_labels() -> None: + allocated = allocate_surfaces(_cells(), 2, 2) + for row in allocated["calibration"] + allocated["measurement"]: + assert set(row) == set(MANIFEST_FIELDS) + assert "operator_bucket" not in row + assert row["row_id"] == row["normalized_text_sha256"] + assert row["source_identity"] == "wordnet-3.0" + + +def test_draw_module_does_not_score_or_sample_at_random() -> None: + path = Path(inspect.getsourcefile(allocate_surfaces)) + text = path.read_text(encoding="utf-8") + allocator = "\n".join( + inspect.getsource(function) + for function in (allocate_surfaces, manifest_row, build_universe, _identity_fences) + ) + assert "operator_bucket" not in allocator + assert "select_threshold(" not in text + assert "random" not in text + assert "torch" not in text + assert "GlossBERT" not in text + assert "SentenceTransformer" not in text + assert "0.672078" not in text + assert "0.4040327275" not in text + assert "CALIBRATION_LABELS" not in allocator + assert "CALIBRATION_SCORES" not in allocator diff --git a/tests/shadow/test_select_005_supply.py b/tests/shadow/test_select_005_supply.py new file mode 100644 index 00000000..33314fbe --- /dev/null +++ b/tests/shadow/test_select_005_supply.py @@ -0,0 +1,156 @@ +"""SELECT-005 supply review does not promote inferred rows or seal missing numbers.""" + +from __future__ import annotations + +import ast +from pathlib import Path + +import pytest + +from hyperlexical.select_005_supply import ( + EXCLUDED_BUCKETS, + PREREGISTRATION_INCOMPLETE, + THRESHOLDS_BLOCKED, + THRESHOLD_AUTHORIZATION_JSON_SCHEMA_EXISTS, + UNRESOLVED_SCHEDULE_FIELDS, + extract_review_ready, + manifest_jsonl, + manifest_record, + preregistration_status, + refuse_observed_promotion, + settlement_refusal, + threshold_authorization_proposal, + validate_operator_decision, +) + +MODULE = Path(__file__).resolve().parents[2] / "scripts" / "shadow" / "hyperlexical" / "select_005_supply.py" + + +def _row(bucket, candidate_id="c1", text="alpha"): + return { + "bucket": bucket, + "candidate_id": candidate_id, + "canonical text": text, + "rights basis": "MIT-examples", + "source": "export.jsonl", + "source locator": "line:1", + } + + +def test_review_ready_extraction_drops_blocked_rows_and_is_stable(): + rows = [ + _row("RIGHTS_BLOCKED", "r"), + _row("READY_FOR_OPERATOR_REVIEW", "a", "one"), + _row("PROVENANCE_BLOCKED", "p"), + _row("READY_FOR_OPERATOR_REVIEW", "b", "two"), + _row("DUPLICATE_OR_OVERLAP", "d"), + ] + first = extract_review_ready(rows) + second = extract_review_ready(list(rows)) + assert [row["candidate_id"] for row in first] == ["a", "b"] + assert manifest_jsonl( + [ + manifest_record( + row, + normalized_identity="ab", + source_class="INFERRED", + source_provenance="LINEAGE_REGISTRY:ai-native", + existing_settlement_state="ABSENT", + ) + for row in first + ] + ) == manifest_jsonl( + [ + manifest_record( + row, + normalized_identity="ab", + source_class="INFERRED", + source_provenance="LINEAGE_REGISTRY:ai-native", + existing_settlement_state="ABSENT", + ) + for row in second + ] + ) + assert "quota" not in manifest_jsonl( + [ + manifest_record( + first[0], + normalized_identity="ab", + source_class="INFERRED", + source_provenance="LINEAGE_REGISTRY:ai-native", + existing_settlement_state="ABSENT", + ) + ] + ) + assert EXCLUDED_BUCKETS == {"RIGHTS_BLOCKED", "PROVENANCE_BLOCKED", "DUPLICATE_OR_OVERLAP"} + + +def test_inferred_registry_class_is_not_promoted_to_observed(): + with pytest.raises(SystemExit, match="not promoted to OBSERVED"): + refuse_observed_promotion("INFERRED", "OBSERVED") + validate_operator_decision( + { + "attest": "INFERRED", + "decision": "ACCEPT", + "rights": "MIT-examples", + "semantic_family": "ai-native", + "source_rights": "MIT-examples", + }, + source_class="INFERRED", + ) + with pytest.raises(SystemExit, match="rights state was changed"): + validate_operator_decision( + { + "attest": "INFERRED", + "decision": "ACCEPT", + "rights": "CC-BY-SA", + "semantic_family": "ai-native", + "source_rights": "MIT-examples", + }, + source_class="INFERRED", + ) + + +def test_settlement_apply_refuses_rows_outside_the_held_out_stream(): + message = settlement_refusal( + [{"row_id": "HLX-CAND-not-in-stream", "decision": "UNRESOLVED"}], + [{"row_key": "hs-existing", "text": "other", "source_type": "wiktionary_category"}], + ) + assert "not in the held-out stream" in message + + +def test_preregistration_stays_incomplete_without_inheriting_select_004(): + status = preregistration_status() + assert status["state"] == PREREGISTRATION_INCOMPLETE + assert status["sealed"] is False + assert status["inherited_select_004_hyperparameters"] is False + assert status["unresolved_fields"] == list(UNRESOLVED_SCHEDULE_FIELDS) + assert "2e-5" not in str(status["resolved_schedule"]) + + +def test_threshold_proposal_does_not_seal_numeric_floors(): + proposal = threshold_authorization_proposal() + assert proposal["sealed"] is False + assert proposal["state"] == THRESHOLDS_BLOCKED + assert proposal["decision_thresholds"] == {} + assert proposal["inherited_from_select_004"] is False + assert THRESHOLD_AUTHORIZATION_JSON_SCHEMA_EXISTS is True + assert all(item["numeric_threshold"] is None for item in proposal["metrics"]) + assert [item["metric"] for item in proposal["metrics"]] == [ + "classify_macro_f1_nonnone", + "classification_accuracy", + "observed_label_accuracy", + "unbind_clean_exact", + ] + + +def test_module_does_not_touch_the_residual_lane_or_commit_settlements(): + source = MODULE.read_text(encoding="utf-8") + tree = ast.parse(source) + imported = [] + for node in ast.walk(tree): + if isinstance(node, ast.ImportFrom) and node.module: + imported.append(node.module) + assert not any("residual" in name for name in imported) + assert "commit_settlement" not in source + assert "persist_append" not in source diff --git a/tests/shadow/test_select_005_threshold.py b/tests/shadow/test_select_005_threshold.py new file mode 100644 index 00000000..285e315c --- /dev/null +++ b/tests/shadow/test_select_005_threshold.py @@ -0,0 +1,120 @@ +"""SELECT-005 thresholds seal strict improvement and zero degradation.""" + +import hashlib +from pathlib import Path + +from hyperlexical.select_005_completion import threshold_authorization_proposal +from hyperlexical.select_005_reserve import canonical_json, sha256_text +from hyperlexical.select_005_threshold import ( + SCHEMA_SHA256, + apply_decision, + authorization_sha256, + schema_validation_report, + sealed_authorization, + semantic_errors, + semantic_validation_report, +) + +REPO = Path(__file__).resolve().parents[2] +HARVEST_SHA256 = "8f58f30ca85af2e476064c7afea9498b02a53d71c4890d38b4cdc2baaf83021c" +COMPLETION_SHA256 = "f9728185f171b8d5c3eeea326e30e230e05def80885c43cbf79c13c64f4095ba" + + +def _scores(primary: float, accuracy: float, observed: float, unbind: float) -> dict: + return { + "classification_accuracy": accuracy, + "classify_macro_f1_nonnone": primary, + "observed_label_accuracy": observed, + "unbind_clean_exact": unbind, + } + + +def test_sealed_artifact_has_no_nulls_and_validates(): + payload = sealed_authorization() + assert payload["sealed"] is True + assert payload["experiment_id"] == "HLX-EXP-2026-09-27-SELECT-005" + assert payload["training_launch_authorized"] is False + assert payload["inherited_from_select_004"] is False + assert payload["comparison_baseline"] == "SELECT-005 control arm" + for name, value in payload["decision_thresholds"].items(): + assert value is not None + assert value == 0 + for item in payload["metrics"]: + assert item["numeric_threshold"] is not None + assert schema_validation_report()["result"] == "PASS" + assert semantic_validation_report()["result"] == "PASS" + assert semantic_errors(payload) == [] + assert authorization_sha256() == sha256_text(canonical_json(payload)) + + +def test_primary_equality_fails_and_a_positive_delta_passes_that_gate(): + control = _scores(0.20, 0.50, 0.40, 0.10) + tie = apply_decision(control, _scores(0.20, 0.50, 0.40, 0.10)) + assert tie["pass"] is False + assert tie["outcome"] == "FAIL" + primary = next(gate for gate in tie["gates"] if gate["metric"] == "classify_macro_f1_nonnone") + assert primary["reason"] == "PRIMARY_NOT_STRICT" + improved = apply_decision(control, _scores(0.21, 0.50, 0.40, 0.10)) + assert improved["pass"] is True + assert improved["outcome"] == "PASS" + + +def test_each_preservation_metric_vetoes_alone(): + control = _scores(0.20, 0.50, 0.40, 0.10) + drops = { + "classification_accuracy": _scores(0.21, 0.49, 0.40, 0.10), + "observed_label_accuracy": _scores(0.21, 0.50, 0.39, 0.10), + "unbind_clean_exact": _scores(0.21, 0.50, 0.40, 0.09), + } + for name, candidate in drops.items(): + result = apply_decision(control, candidate) + assert result["pass"] is False + failed = [gate["metric"] for gate in result["gates"] if not gate["pass"]] + assert failed == [name] + + +def test_missing_and_noncomputable_metrics_fail(): + control = _scores(0.20, 0.50, 0.40, 0.10) + candidate = _scores(0.21, 0.50, 0.40, 0.10) + del candidate["observed_label_accuracy"] + missing = apply_decision(control, candidate) + assert missing["pass"] is False + observed = next(gate for gate in missing["gates"] if gate["metric"] == "observed_label_accuracy") + assert observed["reason"] == "MISSING" + bad = _scores(0.21, 0.50, 0.40, 0.10) + bad["unbind_clean_exact"] = float("nan") + nan_result = apply_decision(control, bad) + assert nan_result["pass"] is False + unbind = next(gate for gate in nan_result["gates"] if gate["metric"] == "unbind_clean_exact") + assert unbind["reason"] == "NON_COMPUTABLE" + + +def test_wrong_experiment_and_unsealed_proposal_do_not_authorize(): + payload = sealed_authorization() + payload["experiment_id"] = "HLX-EXP-2026-09-26-SELECT-004" + assert any("experiment_id" in item for item in semantic_errors(payload)) + proposal = threshold_authorization_proposal() + assert proposal["sealed"] is False + assert proposal["decision_thresholds"] == {} + assert any(item["numeric_threshold"] is None for item in proposal["metrics"]) + assert semantic_validation_report(proposal)["result"] == "FAIL" + + +def test_select_004_floors_are_not_the_sealed_numbers(): + thresholds = sealed_authorization()["decision_thresholds"] + assert thresholds["classification_accuracy"] != 0.02 + assert thresholds["observed_label_accuracy"] != 0.05 + assert thresholds["unbind_clean_exact"] != 0.01 + assert "classify_macro_f1_nonnone" in thresholds + + +def test_schema_harvest_and_proposal_module_stay_put(): + schema = ( + REPO + / "specs/007-hyperlexical-model/schemas/hyperlex/select/threshold-authorization.schema.json" + ) + harvest = REPO / "scripts/shadow/hyperlexical/select_005_harvest.py" + completion = REPO / "scripts/shadow/hyperlexical/select_005_completion.py" + assert hashlib.sha256(schema.read_bytes()).hexdigest() == SCHEMA_SHA256 + assert hashlib.sha256(harvest.read_bytes()).hexdigest() == HARVEST_SHA256 + assert hashlib.sha256(completion.read_bytes()).hexdigest() == COMPLETION_SHA256 diff --git a/tests/shadow/test_semantic_evidence_v2.py b/tests/shadow/test_semantic_evidence_v2.py new file mode 100644 index 00000000..ad7a589a --- /dev/null +++ b/tests/shadow/test_semantic_evidence_v2.py @@ -0,0 +1,156 @@ +"""The successor evidence function is deterministic and does not rerun WSD.""" + +from __future__ import annotations + +import ast +import hashlib +from pathlib import Path + +from jsonschema import Draft202012Validator +from referencing import Registry, Resource + +from hyperlexical.semantic_evidence_v2 import ( + BASELINE_COMPOSITION, + BASELINE_FAMILY, + FEATURE_NAMES, + MAX_REPRESENTATION_FAMILIES, + MEASUREMENT_DISPOSITION, + OUTPUT_LABELS, + REPRESENTATION_FAMILIES, + RULE, + STATE, + TRAINING_AUTHORIZED, + baseline_residual, + constituent_text, + evidence_record, + parent_text, + template_sha256, + weighted_mean, +) + + +def _vectors(): + whole = (1.0, 0.0) + constituents = ((1.0, 0.0), (0.0, 1.0)) + gloss_whole = (0.6, 0.8) + gloss_composed = (0.8, 0.6) + lemma_whole = (1.0, 1.0) + lemma_composed = (1.0, 0.0) + return whole, constituents, gloss_whole, gloss_composed, lemma_whole, lemma_composed + + +def _ready(): + whole, constituents, gloss_whole, gloss_composed, lemma_whole, lemma_composed = _vectors() + return evidence_record( + whole=whole, + constituents=constituents, + gloss_whole=gloss_whole, + gloss_composed=gloss_composed, + lemma_whole=lemma_whole, + lemma_composed=lemma_composed, + token_count=2, + resolved=True, + ) + + +def test_encoding_and_feature_generation_are_deterministic(): + assert parent_text("red herring", "noun", "a clue meant to mislead") == parent_text( + "red herring", "noun", "a clue meant to mislead" + ) + assert constituent_text("red", "a color") == "surface: red\ndefinition: a color\n" + assert template_sha256() == hashlib.sha256( + (parent_text("{surface}", "{pos}", "{gloss}") + constituent_text("{surface}", "{gloss}")).encode("utf-8") + ).hexdigest() + first = _ready() + second = _ready() + assert first == second + assert [feature["name"] for feature in first["features"]] == list(FEATURE_NAMES) + + +def test_missing_constituent_structure_is_unknown(): + record = evidence_record( + whole=(1.0, 0.0), + constituents=((1.0, 0.0),), + gloss_whole=(1.0, 0.0), + gloss_composed=(1.0, 0.0), + lemma_whole=(1.0, 0.0), + lemma_composed=(1.0, 0.0), + token_count=2, + resolved=True, + ) + assert record["evidence_status"] == "UNKNOWN" + assert record["features"] is None + assert record["output"] == "UNKNOWN" + unresolved = _ready() + unresolved = evidence_record( + whole=(1.0, 0.0), + constituents=((1.0, 0.0), (0.0, 1.0)), + gloss_whole=(1.0, 0.0), + gloss_composed=(1.0, 0.0), + lemma_whole=(1.0, 0.0), + lemma_composed=(1.0, 0.0), + token_count=2, + resolved=False, + ) + assert unresolved["evidence_status"] == "UNKNOWN" + + +def test_baseline_residual_matches_the_historical_scalar(): + whole, constituents, *_rest = _vectors() + record = _ready() + residual = next(feature["value"] for feature in record["features"] if feature["name"] == "whole_vs_constituent_mean_residual") + assert residual == baseline_residual(whole, constituents) + assert BASELINE_FAMILY == "minilm_baseline" + assert BASELINE_COMPOSITION == "normalized_mean_v1" + weighted = weighted_mean(constituents, (1, 3)) + assert len(weighted) == 2 + + +def test_feature_function_does_not_import_wsd_or_touch_training(): + source = Path(__import__("hyperlexical.semantic_evidence_v2", fromlist=["__file__"]).__file__).read_text(encoding="utf-8") + tree = ast.parse(source) + imported = [] + for node in ast.walk(tree): + if isinstance(node, ast.Import): + imported.extend(alias.name for alias in node.names) + if isinstance(node, ast.ImportFrom) and node.module: + imported.append(node.module) + banned = ("wordnet", "glossbert", "nltk", "sentence_transformers", "residual_threshold") + assert not any(any(token in name for token in banned) for name in imported) + assert _ready()["output"] == "UNKNOWN" + assert OUTPUT_LABELS == ("YES", "UNKNOWN") + assert "NO" not in OUTPUT_LABELS + assert TRAINING_AUTHORIZED is False + assert STATE == "SEMANTIC_EVIDENCE_V2_SPEC_DRAFTED" + assert MEASUREMENT_DISPOSITION == "RETAIN_FOR_V2_ONLY" + assert RULE == "RUNE.SEMANTIC_COMPOSITIONALITY_EVIDENCE.v2" + assert len(REPRESENTATION_FAMILIES) == MAX_REPRESENTATION_FAMILIES + + +def test_evidence_record_matches_its_schema(): + schema_dir = Path(__file__).resolve().parents[2] / "specs/007-hyperlexical-model/schemas/hyperlex/semantic-evidence-v2" + schemas = {path.name: json_load(path) for path in schema_dir.glob("*.schema.json")} + registry = Registry() + for schema in schemas.values(): + registry = registry.with_resource(schema["$id"], Resource.from_contents(schema)) + validator = Draft202012Validator(schemas["evidence-record.schema.json"], registry=registry) + assert validator.is_valid(_ready()) + unknown = evidence_record( + whole=None, + constituents=None, + gloss_whole=None, + gloss_composed=None, + lemma_whole=None, + lemma_composed=None, + token_count=0, + resolved=False, + ) + assert validator.is_valid(unknown) + ready = _ready() + ready["output"] = "YES" + assert not validator.is_valid(ready) + + +def json_load(path: Path): + import json + return json.loads(path.read_text(encoding="utf-8")) From 9108c5b7a7a1a897657d355d355f6dbb1e7155af Mon Sep 17 00:00:00 2001 From: Daniel Meyer Date: Mon, 28 Sep 2026 16:01:35 -0700 Subject: [PATCH 09/42] feat(007): land SELECT residual and schedule work (9) --- .../select_005_exposure_refresh.py | 188 ++++++++++++++++ .../shadow/hyperlexical/select_005_supply.py | 191 ++++++++++++++++ .../hyperlexical/semantic_evidence_v2.py | 208 ++++++++++++++++++ .../residual-threshold-decision.schema.json | 160 ++++++++++++++ ...emantic-evidence-v2-evaluation-contract.md | 26 +++ .../shadow/test_residual_threshold_schema.py | 183 +++++++++++++++ tests/shadow/test_residual_threshold_v1.py | 193 ++++++++++++++++ .../test_select_005_exposure_refresh.py | 154 +++++++++++++ tests/shadow/test_select_005_reserve.py | 185 ++++++++++++++++ 9 files changed, 1488 insertions(+) create mode 100644 scripts/shadow/hyperlexical/select_005_exposure_refresh.py create mode 100644 scripts/shadow/hyperlexical/select_005_supply.py create mode 100644 scripts/shadow/hyperlexical/semantic_evidence_v2.py create mode 100644 specs/007-hyperlexical-model/schemas/residual-threshold/residual-threshold-decision.schema.json create mode 100644 specs/007-hyperlexical-model/semantic-evidence-v2-evaluation-contract.md create mode 100644 tests/shadow/test_residual_threshold_schema.py create mode 100644 tests/shadow/test_residual_threshold_v1.py create mode 100644 tests/shadow/test_select_005_exposure_refresh.py create mode 100644 tests/shadow/test_select_005_reserve.py diff --git a/scripts/shadow/hyperlexical/select_005_exposure_refresh.py b/scripts/shadow/hyperlexical/select_005_exposure_refresh.py new file mode 100644 index 00000000..7daee34d --- /dev/null +++ b/scripts/shadow/hyperlexical/select_005_exposure_refresh.py @@ -0,0 +1,188 @@ +"""SELECT-005 exposure refresh stops when the box-side generator is absent. + +The canonical command is ``heldout-stream-daily`` on the box. It is not on +this host, and its documented second step is ``hs_run.py run``. A Notion +term/event census is not a substitute for that command. This module does not +generate exposure rows, does not copy an old snapshot forward, and does not +harvest. +""" + +from __future__ import annotations + +import re +from typing import Any, Mapping, Sequence + +from .select_005_harvest import STALE, exposure_fence +from .select_005_reserve import EXPERIMENT_ID + +TRANSITION = "SELECT_005_EXPOSURE_SNAPSHOT_REFRESH_AUTHORIZATION" +UNAVAILABLE = "EXPOSURE_REFRESH_WORKFLOW_UNAVAILABLE" +INVALID = "EXPOSURE_SNAPSHOT_INVALID" +CANONICAL_PROCEDURE = ( + "/workspace/deliverables/hyperlex-heldout-stream/bin/heldout-stream-daily" +) +CANONICAL_HOST = "box" +PREVIOUS_SNAPSHOT_ID = "20260925T211351Z" +NOTION_NOT_JEV_HASH_LIST = ( + "notion term and event registries are not the jev-lane hash list" +) +_ID = re.compile(r"^[0-9]{8}T[0-9]{6}Z$") +_VERN = re.compile(r"^vern-texts-([0-9]{8}T[0-9]{6}Z)\.jsonl$") +_BOX = re.compile(r"^box-jev-hashes-([0-9]{8}T[0-9]{6}Z)\.txt$") +_LANE_FLAGS = ( + "candidates_database_present", + "discovery_lane_export_present", + "jev_lane_hash_generator_present", + "observations_database_present", + "obsidian_export_present", +) + + +def workflow_gate(observation: Mapping[str, Any]) -> dict[str, Any]: + """Classify whether a canonical snapshot-only generator can be run. + + A present daily script is not enough: the documented command also starts + ``hs_run.py run``. Notion term and event rows do not supply the box hash + list. This function never executes either step. + """ + missing = [] + if str(observation.get("host") or "") != CANONICAL_HOST: + missing.append("procedure host is not the box") + if observation.get("executable_present") is not True: + missing.append("heldout-stream-daily is absent") + if observation.get("snapshot_only_entrypoint_present") is not True: + missing.append( + "no snapshot-only entrypoint; the documented command also runs hs_run.py run" + ) + source_kind = str(observation.get("vernacular_source_kind") or "") + if source_kind == "notion": + if observation.get("jev_lane_hash_generator_present") is not True: + missing.append(NOTION_NOT_JEV_HASH_LIST) + elif observation.get("vernacular_source_present") is not True: + missing.append("vernacular sqlite source is absent") + unavailable = bool(missing) + return { + "canonical_host": CANONICAL_HOST, + "canonical_procedure": CANONICAL_PROCEDURE, + "experiment_id": EXPERIMENT_ID, + "harvest_run": False, + "missing": missing, + "network_requests": 0, + "previous_pair_overwritten": False, + "previous_pair_reused": False, + "previous_snapshot_id": PREVIOUS_SNAPSHOT_ID, + "reserve_recensus_run": False, + "snapshot_id": None, + "state": UNAVAILABLE if unavailable else "EXPOSURE_REFRESH_NOT_RUN", + "training_run": False, + "transition": TRANSITION, + } + + +def notion_registry_gap(census: Mapping[str, Any]) -> dict[str, Any]: + """Refuse a pair built from Notion term and event counts alone. + + The box hash list is the over-inclusive Jev-lane exposure set. Term and + event registries are a different object. This function does not read + Notion and does not write snapshot bytes. + """ + terms = census.get("terms") + events = census.get("events") + counted = ( + isinstance(terms, int) + and not isinstance(terms, bool) + and isinstance(events, int) + and not isinstance(events, bool) + and terms >= 0 + and events >= 0 + ) + missing = [] + if not counted: + missing.append("notion census is incomplete") + if census.get("candidates_database_present") is not True: + missing.append("notion has no candidates database") + if census.get("observations_database_present") is not True: + missing.append("notion has no observations database") + if census.get("discovery_lane_export_present") is not True: + missing.append("discovery lane export is absent") + if census.get("obsidian_export_present") is not True: + missing.append("obsidian export is absent") + if census.get("jev_lane_hash_generator_present") is not True: + missing.append(NOTION_NOT_JEV_HASH_LIST) + lanes_present = all(census.get(key) is True for key in _LANE_FLAGS) + return { + "events": events if counted else None, + "harvest_run": False, + "missing": missing, + "network_requests": 0, + "pair_written": False, + "previous_pair_overwritten": False, + "previous_pair_reused": False, + "snapshot_id": None, + "state": UNAVAILABLE if missing or not lanes_present else "EXPOSURE_REFRESH_NOT_RUN", + "terms": terms if counted else None, + "transition": TRANSITION, + } + + +def _name_id(path: str, pattern: re.Pattern[str]) -> str | None: + name = path.rstrip("/").split("/")[-1] + found = pattern.fullmatch(name) + if found is None: + return None + snapshot_id = found.group(1) + if _ID.fullmatch(snapshot_id) is None: + return None + return snapshot_id + + +def pair_binding( + snapshots: Sequence[Mapping[str, Any]], + *, + compared_at_unix: str, +) -> dict[str, Any]: + """Bind a Vern file to a box-hash file. Does not read or write their bytes. + + Age uses ``exposure_fence``, the same formula as the harvest stop. + """ + by_kind = {str(item.get("kind") or ""): item for item in snapshots} + vern_id = _name_id(str(by_kind.get("vern", {}).get("path") or ""), _VERN) + box_id = _name_id(str(by_kind.get("boxhash", {}).get("path") or ""), _BOX) + declared = {str(item.get("snapshot_id") or "") for item in snapshots} + ids_match = ( + vern_id is not None + and vern_id == box_id + and declared == {vern_id} + ) + if not ids_match: + return { + "binding_age_hours": None, + "box_snapshot_id": box_id, + "freshness_result": None, + "harvest_run": False, + "pair_binding_status": "MISMATCHED", + "state": INVALID, + "structural_validation": "FAIL", + "vern_snapshot_id": vern_id, + } + fence = exposure_fence(snapshots, compared_at_unix=compared_at_unix) + structural = "PASS" if not fence["missing_kinds"] else "FAIL" + state = fence["state"] if structural == "PASS" else INVALID + return { + "binding_age_hours": fence["worst_age_hours"], + "box_age_hours": next( + row["age_hours"] for row in fence["snapshots"] if row["kind"] == "boxhash" + ), + "box_snapshot_id": box_id, + "comparison_timestamp_unix": str(compared_at_unix), + "freshness_limit_hours": fence["limit_hours"], + "freshness_result": state, + "harvest_run": False, + "pair_binding_status": "BOUND" if structural == "PASS" else "INCOMPLETE", + "state": state, + "structural_validation": structural, + "vern_age_hours": next( + row["age_hours"] for row in fence["snapshots"] if row["kind"] == "vern" + ), + "vern_snapshot_id": vern_id, + } diff --git a/scripts/shadow/hyperlexical/select_005_supply.py b/scripts/shadow/hyperlexical/select_005_supply.py new file mode 100644 index 00000000..5fd65a62 --- /dev/null +++ b/scripts/shadow/hyperlexical/select_005_supply.py @@ -0,0 +1,191 @@ +"""SELECT-005 supply review. Does not train, append the ledger, or seal numbers. + +Operator decisions are validated against the existing settlement enums. +A stored ``INFERRED`` class is not promoted to ``OBSERVED``. +``eval_settlement.settle`` is the only apply path, and it is not committed +when the held-out stream refuses the row. +""" + +from __future__ import annotations + +import hashlib +import json +from typing import Any, Mapping, Sequence + +from .eval_settlement import DECISIONS, settle + +EXPERIMENT_ID = "HLX-EXP-2026-09-27-SELECT-005" +TRANSITION = "SELECT_005_RESERVE_SUPPLY_AND_GOVERNANCE_AUTHORIZATION" +READY_BUCKET = "READY_FOR_OPERATOR_REVIEW" +EXCLUDED_BUCKETS = frozenset( + {"RIGHTS_BLOCKED", "PROVENANCE_BLOCKED", "DUPLICATE_OR_OVERLAP"} +) +PACKET_ID = "HLX-EVAL-REVIEW-2026-09-27-001" +PREREGISTRATION_INCOMPLETE = "SELECT_005_PREREGISTRATION_INCOMPLETE" +THRESHOLDS_BLOCKED = "BLOCKED_PENDING_OPERATOR_AUTHORIZATION" +THRESHOLD_SCHEMA = "hyperlex.threshold_authorization.v1" +SELECT_ADMISSION_JSON_SCHEMA_EXISTS = True +THRESHOLD_AUTHORIZATION_JSON_SCHEMA_EXISTS = True +UNRESOLVED_SCHEDULE_FIELDS = ( + "learning_rate", + "batch_size", + "gradient_accumulation", + "seed", +) +# Named by the unsealed SELECT-005 proposal. Not a sealed preregistration. +RESOLVED_SCHEDULE = { + "candidate_early_stop": "1", + "candidate_max_epochs": "12", + "candidate_min_epochs": "4", + "candidate_patience": "4", + "checkpoint_ties": "keep_earlier", + "control_early_stop": "0", + "control_max_epochs": "40", + "restore_best": True, + "scientific_variable": "train_schedule", + "selection_metric": "classify_macro_f1_nonnone", + "strict_increase": True, + "warm_start_name": "hyperlex-encoder-modernbert-base-seed-morph65", +} + + +def canonical_json(payload: Mapping[str, Any]) -> str: + return json.dumps(payload, indent=2, sort_keys=True, ensure_ascii=True) + "\n" + + +def sha256_text(text: str) -> str: + return hashlib.sha256(text.encode("utf-8")).hexdigest() + + +def extract_review_ready(rows: Sequence[Mapping[str, Any]]) -> list[dict[str, Any]]: + """Packet order. Blocked, provenance-blocked, and duplicate rows stay out.""" + ready: list[dict[str, Any]] = [] + for row in rows: + bucket = str(row.get("bucket") or "") + if bucket in EXCLUDED_BUCKETS: + continue + if bucket != READY_BUCKET: + raise SystemExit(f"REFUSE: unknown review bucket: {bucket}") + ready.append(dict(row)) + return ready + + +def manifest_record( + row: Mapping[str, Any], + *, + normalized_identity: str, + source_class: str, + source_provenance: str, + existing_settlement_state: str, +) -> dict[str, Any]: + """Identity surface only. No slice, quota, expected label, or model output.""" + return { + "existing_settlement_state": existing_settlement_state, + "license_rights_state": str(row.get("rights basis") or ""), + "normalized_identity": normalized_identity, + "provenance_state": source_provenance, + "review_row_id": str(row.get("candidate_id") or ""), + "source": str(row.get("source") or ""), + "source_class": source_class, + "source_identity": str(row.get("source locator") or ""), + "surface": str(row.get("canonical text") or ""), + } + + +def manifest_jsonl(records: Sequence[Mapping[str, Any]]) -> str: + return "".join(json.dumps(record, sort_keys=True, ensure_ascii=True) + "\n" for record in records) + + +def refuse_observed_promotion(source_class: str, attest: str | None) -> None: + """The stored registry class is not promoted to OBSERVED.""" + if source_class == "INFERRED" and attest == "OBSERVED": + raise SystemExit("REFUSE: stored INFERRED class is not promoted to OBSERVED") + + +def validate_operator_decision(record: Mapping[str, Any], *, source_class: str) -> dict[str, Any]: + decision = str(record.get("decision") or "") + if decision not in DECISIONS: + raise SystemExit(f"REFUSE: invalid decision: {decision}") + attest = record.get("attest") + if attest is not None and attest not in ("OBSERVED", "INFERRED"): + raise SystemExit(f"REFUSE: invalid attest: {attest}") + refuse_observed_promotion(source_class, attest if isinstance(attest, str) else None) + if decision == "UNRESOLVED" and (record.get("semantic_family") is not None or attest is not None): + raise SystemExit("REFUSE: UNRESOLVED requires null semantic_family and null attest") + if decision == "NONE" and record.get("semantic_family") != "none": + raise SystemExit("REFUSE: NONE requires semantic_family none") + if decision == "ACCEPT" and not record.get("semantic_family"): + raise SystemExit("REFUSE: ACCEPT requires an explicit semantic_family") + rights = str(record.get("rights") or "") + source_rights = str(record.get("source_rights") or "") + if source_rights and rights != source_rights: + raise SystemExit("REFUSE: rights state was changed") + return dict(record) + + +def settlement_refusal( + decisions: Sequence[Mapping[str, Any]], + stream_rows: Sequence[Mapping[str, Any]], +) -> str: + """Call the canonical validator. It does not append.""" + try: + settle( + stream_rows, + decisions, + batch_id="HLX-EVAL-SETTLE-SELECT-005-SUPPLY-001", + operator="cursor-cloud-agent", + provenance="SELECT-005 supply review", + settled_at="2026-09-28T20:39:00Z", + ) + except SystemExit as exc: + return str(exc) + return "APPLIED" + + +def preregistration_status() -> dict[str, Any]: + """SELECT-005 never states LR, batch, gradient accumulation, or seed. + + Trainer defaults and the SELECT-004 environment are not an inheritance rule. + """ + return { + "experiment_id": EXPERIMENT_ID, + "inherited_select_004_hyperparameters": False, + "resolved_schedule": dict(RESOLVED_SCHEDULE), + "sealed": False, + "state": PREREGISTRATION_INCOMPLETE, + "unresolved_fields": list(UNRESOLVED_SCHEDULE_FIELDS), + } + + +def threshold_authorization_proposal() -> dict[str, Any]: + """Metric names from the SELECT decision contract. Numbers stay unset.""" + names = ( + ("classify_macro_f1_nonnone", "primary_selection", "macro_f1", "higher_than_control"), + ("classification_accuracy", "preservation", "accuracy", "not_below_control_by_more_than_floor"), + ("observed_label_accuracy", "preservation", "accuracy", "not_below_control_by_more_than_floor"), + ("unbind_clean_exact", "preservation", "exact_match", "not_below_control_by_more_than_floor"), + ) + thresholds = [ + { + "authorization_state": THRESHOLDS_BLOCKED, + "derivation": "OPERATOR_JUDGMENT_ABSENT", + "direction": direction, + "metric": metric, + "numeric_threshold": None, + "operator": None, + "role": role, + "units": units, + } + for metric, role, units, direction in names + ] + return { + "decision_thresholds": {}, + "experiment_id": EXPERIMENT_ID, + "inherited_from_select_004": False, + "metrics": thresholds, + "schema": THRESHOLD_SCHEMA, + "sealed": False, + "state": THRESHOLDS_BLOCKED, + "threshold_authorization_json_schema_exists": THRESHOLD_AUTHORIZATION_JSON_SCHEMA_EXISTS, + "training_launch_authorized": False, + } diff --git a/scripts/shadow/hyperlexical/semantic_evidence_v2.py b/scripts/shadow/hyperlexical/semantic_evidence_v2.py new file mode 100644 index 00000000..dbfb8267 --- /dev/null +++ b/scripts/shadow/hyperlexical/semantic_evidence_v2.py @@ -0,0 +1,208 @@ +"""Inspectable semantic-evidence function for the successor lane. + +The function is a contract, not an evaluation. It does not load a model, run +word-sense disambiguation, read a measurement surface, or authorize training. +""" + +from __future__ import annotations + +import hashlib +import math +from decimal import Decimal, ROUND_HALF_EVEN + +RULE = "RUNE.SEMANTIC_COMPOSITIONALITY_EVIDENCE.v2" +STATE = "SEMANTIC_EVIDENCE_V2_SPEC_DRAFTED" +MEASUREMENT_DISPOSITION = "RETAIN_FOR_V2_ONLY" +TRAINING_AUTHORIZED = False +BASELINE_COMPARISON = "BASELINE_COMPARISON_REQUIRED" + +PARENT_TEMPLATE = "surface: {surface}\npart_of_speech: {pos}\ndefinition: {gloss}\n" +CONSTITUENT_TEMPLATE = "surface: {surface}\ndefinition: {gloss}\n" +QUANTUM = Decimal("0.0000000001") + +FEATURE_NAMES = ( + "whole_vs_constituent_mean_cosine", + "whole_vs_constituent_mean_residual", + "whole_vs_each_constituent_min_similarity", + "whole_vs_each_constituent_max_similarity", + "whole_vs_each_constituent_mean_similarity", + "whole_vs_each_constituent_similarity_variance", + "constituent_pair_mean_similarity", + "constituent_pair_min_similarity", + "constituent_pair_variance", + "whole_gloss_vs_composed_gloss_similarity", + "whole_lemma_vs_composed_constituent_similarity", + "constituent_count", + "token_count", +) + +STRUCTURAL_CONTROLS = frozenset({"constituent_count", "token_count"}) +REPRESENTATION_FAMILIES = ( + "minilm_baseline", + "modernbert_gloss_pooling", + "sts_semantic_encoder", +) +MAX_REPRESENTATION_FAMILIES = 3 +BASELINE_FAMILY = "minilm_baseline" +BASELINE_COMPOSITION = "normalized_mean_v1" +COMPOSITION_FUNCTIONS = ( + "normalized_mean_v1", + "weighted_mean_by_constituent_token_span", + "pairwise_relation_summary", +) +OUTPUT_LABELS = ("YES", "UNKNOWN") + + +def template_sha256() -> str: + payload = PARENT_TEMPLATE + CONSTITUENT_TEMPLATE + return hashlib.sha256(payload.encode("utf-8")).hexdigest() + + +def parent_text(surface: str, pos: str, gloss: str) -> str: + return PARENT_TEMPLATE.format(surface=surface, pos=pos, gloss=gloss) + + +def constituent_text(surface: str, gloss: str) -> str: + return CONSTITUENT_TEMPLATE.format(surface=surface, gloss=gloss) + + +def quantize(value: float) -> str: + decimal = Decimal(str(value)).quantize(QUANTUM, rounding=ROUND_HALF_EVEN) + return f"{decimal:.10f}" + + +def _dot(left: tuple[float, ...], right: tuple[float, ...]) -> float: + if len(left) != len(right) or not left: + raise ValueError("vectors must be the same non-empty length") + return sum(a * b for a, b in zip(left, right)) + + +def _norm(vector: tuple[float, ...]) -> float: + return math.sqrt(_dot(vector, vector)) + + +def cosine(left: tuple[float, ...], right: tuple[float, ...]) -> float: + denominator = _norm(left) * _norm(right) + if denominator == 0: + raise ValueError("cosine is undefined for a zero vector") + return _dot(left, right) / denominator + + +def normalized_mean(vectors: tuple[tuple[float, ...], ...]) -> tuple[float, ...]: + if len(vectors) < 2: + raise ValueError("normalized mean requires at least two constituents") + width = len(vectors[0]) + if any(len(vector) != width for vector in vectors): + raise ValueError("constituent vectors must share a width") + mean = tuple(sum(vector[index] for vector in vectors) / len(vectors) for index in range(width)) + scale = _norm(mean) + if scale == 0: + raise ValueError("normalized mean is undefined for a zero mean") + return tuple(value / scale for value in mean) + + +def weighted_mean(vectors: tuple[tuple[float, ...], ...], weights: tuple[int, ...]) -> tuple[float, ...]: + if len(vectors) != len(weights) or len(vectors) < 2: + raise ValueError("weights must match at least two constituents") + if any(weight <= 0 for weight in weights): + raise ValueError("token-span weights must be positive") + width = len(vectors[0]) + total = sum(weights) + mean = tuple( + sum(vector[index] * weight for vector, weight in zip(vectors, weights)) / total + for index in range(width) + ) + scale = _norm(mean) + if scale == 0: + raise ValueError("weighted mean is undefined for a zero mean") + return tuple(value / scale for value in mean) + + +def _population_variance(values: tuple[float, ...]) -> float: + if not values: + raise ValueError("variance requires values") + center = sum(values) / len(values) + return sum((value - center) ** 2 for value in values) / len(values) + + +def pairwise_cosines(vectors: tuple[tuple[float, ...], ...]) -> tuple[float, ...]: + if len(vectors) < 2: + raise ValueError("pairwise summary requires at least two constituents") + return tuple( + cosine(vectors[left], vectors[right]) + for left in range(len(vectors)) + for right in range(left + 1, len(vectors)) + ) + + +def baseline_residual(whole: tuple[float, ...], constituents: tuple[tuple[float, ...], ...]) -> str: + """Historical scalar: 1 - cosine(whole, normalized mean of constituents).""" + return quantize(1 - cosine(whole, normalized_mean(constituents))) + + +def evidence_record( + *, + whole: tuple[float, ...] | None, + constituents: tuple[tuple[float, ...], ...] | None, + gloss_whole: tuple[float, ...] | None, + gloss_composed: tuple[float, ...] | None, + lemma_whole: tuple[float, ...] | None, + lemma_composed: tuple[float, ...] | None, + token_count: int, + resolved: bool, +) -> dict: + """Return UNKNOWN when constituent structure is unresolved or incomplete.""" + if ( + not resolved + or whole is None + or constituents is None + or len(constituents) < 2 + or gloss_whole is None + or gloss_composed is None + or lemma_whole is None + or lemma_composed is None + or token_count < 1 + ): + return { + "evidence_status": "UNKNOWN", + "features": None, + "output": "UNKNOWN", + "rule": RULE, + } + composed = normalized_mean(constituents) + to_each = tuple(cosine(whole, vector) for vector in constituents) + pairs = pairwise_cosines(constituents) + values = { + "whole_vs_constituent_mean_cosine": cosine(whole, composed), + "whole_vs_constituent_mean_residual": 1 - cosine(whole, composed), + "whole_vs_each_constituent_min_similarity": min(to_each), + "whole_vs_each_constituent_max_similarity": max(to_each), + "whole_vs_each_constituent_mean_similarity": sum(to_each) / len(to_each), + "whole_vs_each_constituent_similarity_variance": _population_variance(to_each), + "constituent_pair_mean_similarity": sum(pairs) / len(pairs), + "constituent_pair_min_similarity": min(pairs), + "constituent_pair_variance": _population_variance(pairs), + "whole_gloss_vs_composed_gloss_similarity": cosine(gloss_whole, gloss_composed), + "whole_lemma_vs_composed_constituent_similarity": cosine(lemma_whole, lemma_composed), + } + features = [ + { + "control": name in STRUCTURAL_CONTROLS, + "name": name, + "value": values[name] if name in STRUCTURAL_CONTROLS else quantize(values[name]), + } + for name in FEATURE_NAMES + if name not in STRUCTURAL_CONTROLS + ] + features.extend( + [ + {"control": True, "name": "constituent_count", "value": len(constituents)}, + {"control": True, "name": "token_count", "value": token_count}, + ] + ) + return { + "evidence_status": "READY", + "features": features, + "output": "UNKNOWN", + "rule": RULE, + } diff --git a/specs/007-hyperlexical-model/schemas/residual-threshold/residual-threshold-decision.schema.json b/specs/007-hyperlexical-model/schemas/residual-threshold/residual-threshold-decision.schema.json new file mode 100644 index 00000000..168cab4d --- /dev/null +++ b/specs/007-hyperlexical-model/schemas/residual-threshold/residual-threshold-decision.schema.json @@ -0,0 +1,160 @@ +{ + "$schema": "https://json-schema.org/draft/2020-12/schema", + "$id": "https://hyperlex.local/schemas/residual-threshold-decision.schema.json", + "title": "Residual-threshold decision", + "description": "An unfrozen decision requires a null threshold. A frozen decision requires the numeric threshold and its metrics. No frozen success record has been emitted.", + "allOf": [ + {"$ref": "residual-threshold-common.schema.json#/$defs/unfrozenThresholdInvariant"}, + {"$ref": "residual-threshold-common.schema.json#/$defs/frozenThresholdInvariant"}, + { + "oneOf": [ + {"$ref": "#/$defs/directionDecision"}, + {"$ref": "#/$defs/confoundDecision"}, + {"$ref": "#/$defs/failureDecision"}, + {"$ref": "#/$defs/supportDecision"}, + {"$ref": "#/$defs/closureDecision"} + ] + } + ], + "$defs": { + "directionDecision": { + "type": "object", + "additionalProperties": false, + "required": [ + "admitted", + "analysis_scope", + "analysis_sha256", + "confound_analysis_run", + "direction_result", + "evaluation_id", + "gold", + "json_schema_document", + "json_schema_exists", + "measurement_state", + "next_legal_transition", + "next_transition_authorized", + "redraw_authorized", + "runtime_integration", + "select_005_authorized", + "selected_source", + "settled", + "state", + "threshold_frozen", + "threshold_search_run", + "threshold_value", + "uncertainty" + ], + "properties": { + "admitted": {"const": 0}, + "analysis_scope": {"const": "DIRECTION_ONLY"}, + "analysis_sha256": {"$ref": "residual-threshold-common.schema.json#/$defs/sha256"}, + "confound_analysis_run": {"const": false}, + "direction_result": { + "enum": ["SUPPORTED_DIRECTION", "INVERTED_DIRECTION", "NO_DIRECTIONAL_SEPARATION"] + }, + "evaluation_id": {"type": "string", "minLength": 1}, + "gold": {"const": 0}, + "json_schema_document": {"type": "null"}, + "json_schema_exists": {"const": false}, + "measurement_state": {"$ref": "residual-threshold-common.schema.json#/$defs/measurement_state"}, + "next_legal_transition": {"type": "string"}, + "next_transition_authorized": {"const": false}, + "redraw_authorized": {"const": false}, + "runtime_integration": {"const": false}, + "select_005_authorized": {"const": false}, + "selected_source": {"const": "none"}, + "settled": {"const": 0}, + "state": {"type": "string"}, + "threshold_frozen": {"const": false}, + "threshold_search_run": {"const": false}, + "threshold_value": {"type": "null"}, + "uncertainty": {"const": "UNCERTAINTY_INTERVAL_NOT_COMPUTED"} + } + }, + "confoundDecision": { + "type": "object", + "additionalProperties": false, + "required": [ + "analysis_sha256", + "confound_result", + "evaluation_id", + "frozen_stop_conditions", + "json_schema_document", + "json_schema_exists", + "next_stage_authorized_by_this_pass", + "state", + "threshold_search_run" + ], + "properties": { + "analysis_sha256": {"$ref": "residual-threshold-common.schema.json#/$defs/sha256"}, + "confound_result": {"enum": ["PASS", "STOP"]}, + "evaluation_id": {"type": "string", "minLength": 1}, + "frozen_stop_conditions": { + "type": "array", + "items": {"enum": ["extreme_driven", "pos_partitioned", "tier3_concentrated"]}, + "minItems": 3, + "uniqueItems": true + }, + "json_schema_document": {"type": "null"}, + "json_schema_exists": {"const": false}, + "next_stage_authorized_by_this_pass": {"type": "boolean"}, + "state": {"enum": ["PASS", "CALIBRATION_CONFOUND_REVIEW"]}, + "threshold_search_run": {"type": "boolean"} + } + }, + "failureDecision": { + "type": "object", + "additionalProperties": true, + "required": ["failed_gate", "failure_state", "schema", "threshold_frozen", "threshold_value"], + "properties": { + "failed_gate": {"type": "string", "minLength": 1}, + "failure_state": {"$ref": "residual-threshold-common.schema.json#/$defs/threshold_state"}, + "gate_pass_counts": {"type": "object"}, + "json_schema_document": {"type": "null"}, + "json_schema_exists": {"type": "boolean"}, + "schema": {"type": "string", "minLength": 1}, + "threshold_frozen": {"const": false}, + "threshold_value": {"type": "null"} + }, + "patternProperties": { + "_sha256$": {"$ref": "residual-threshold-common.schema.json#/$defs/sha256"} + } + }, + "supportDecision": { + "type": "object", + "additionalProperties": true, + "required": [ + "schema", + "state", + "support_gate_passed", + "threshold_frozen", + "threshold_value" + ], + "properties": { + "schema": {"type": "string", "minLength": 1}, + "state": {"type": "string"}, + "support_gate_passed": {"type": "boolean"}, + "threshold_frozen": {"const": false}, + "threshold_value": {"type": "null"} + }, + "patternProperties": { + "_sha256$": {"$ref": "residual-threshold-common.schema.json#/$defs/sha256"} + } + }, + "closureDecision": { + "type": "object", + "additionalProperties": true, + "required": ["disposition", "schema", "threshold_frozen", "threshold_value"], + "properties": { + "disposition": {"type": "string", "minLength": 1}, + "preserved_execution_sha256": { + "type": "object", + "additionalProperties": {"$ref": "residual-threshold-common.schema.json#/$defs/sha256"} + }, + "schema": {"type": "string", "minLength": 1}, + "threshold_frozen": {"const": false}, + "threshold_value": {"type": "null"} + } + } + } +} diff --git a/specs/007-hyperlexical-model/semantic-evidence-v2-evaluation-contract.md b/specs/007-hyperlexical-model/semantic-evidence-v2-evaluation-contract.md new file mode 100644 index 00000000..eb419686 --- /dev/null +++ b/specs/007-hyperlexical-model/semantic-evidence-v2-evaluation-contract.md @@ -0,0 +1,26 @@ +# Semantic evidence v2 evaluation contract + +No evaluation is authorized by this draft. The order, once a preregistration exists, is: + +```text +1. coverage +2. directional separation +3. cross-validated calibration stability +4. precision +5. Wilson lower bound +6. HIGH recall +7. REJECT veto +8. QUARANTINE veto +``` + +Support floors stay ready HIGH at least 12 and ready SECONDARY at least 8. Precision floor stays 0.80. Wilson lower bound stays 0.50. REJECT predicted YES and QUARANTINE predicted YES stay 0. HIGH is the positive class and SECONDARY is the negative class. REJECT and QUARANTINE are vetoes after the fit, not standard negatives. + +The comparative requirement against the frozen v2 scalar residual is `BASELINE_COMPARISON_REQUIRED`. A candidate reports baseline, candidate, and delta on the same rows. Beating AUC 0.580420 is not yet a hard gate. A family advances only when direction is supported, the frozen confound stops do not fire, leave-one-out behavior is stable, and the report shows a material improvement with a plausible route to the precision and Wilson floors. Early representation exploration does not have to freeze a threshold. + +Model-combination experiments use leave-one-out on the small ready support. Folds do not select a different representation. Representation-family selection happens outside the final threshold surface. The selection order, to be frozen before execution, is safety eligibility, cross-validated precision potential, Wilson lower-bound potential, directional AUC, then the simpler representation. + +Candidate calibrators, after the feature vector is frozen, are a single-feature threshold baseline, L2 logistic regression, and a monotonic or constrained linear score. Hyperparameters are frozen before calibration evaluation. Measurement is not a tuning set. Deep classifiers are out of this first comparison. + +Every report includes the MiniLM plus `normalized_mean_v1` plus `1 - cosine` baseline. + +Training is not resumed because this lane exists. A later handoff state `SEMANTIC_EVIDENCE_READY_FOR_TRAINING_INTEGRATION` would require the evaluation chain to succeed, and a separate training spec would then decide any effect on unbind source construction, negatives, sampling, curriculum, or loss weighting. Those choices are not in this contract. diff --git a/tests/shadow/test_residual_threshold_schema.py b/tests/shadow/test_residual_threshold_schema.py new file mode 100644 index 00000000..1629105d --- /dev/null +++ b/tests/shadow/test_residual_threshold_schema.py @@ -0,0 +1,183 @@ +"""Retrospective schemas check sealed residual-threshold artifacts without rewriting them.""" + +from __future__ import annotations + +import inspect + +import pytest +from jsonschema import Draft202012Validator + +from hyperlexical import residual_threshold_schema as schema_mod +from hyperlexical.residual_threshold_schema import ( + GAP, + PASS, + SOURCE, + classify, + conformance_report, + evidence_hashes, + load_schemas, + schema_registry, +) + + +def _validator(schema: dict, registry): + Draft202012Validator.check_schema(schema) + return Draft202012Validator(schema, registry=registry) + + +def _schemas(): + loaded = load_schemas() + return loaded, schema_registry(loaded) + + +def _completion(**overrides) -> dict: + document = { + "calibration_redraw": False, + "confound_gate": "PASS", + "direction_gate": "SUPPORTED_DIRECTION", + "json_schema_document": None, + "json_schema_exists": False, + "measurement_eligible": False, + "measurement_state": "SEALED", + "operator_labeling": "LABELS_FROZEN", + "runtime_integration": False, + "select_005_authorized": False, + "selected_source": "none", + "support_gate": "SUPPORT_GATE_PASSED", + "surface_draw": "SURFACES_FROZEN", + "threshold_frozen": False, + "threshold_search": "NO_THRESHOLD_PASSES_PRECISION_GATE", + "threshold_value": None, + "training_authorized": False, + "training_readiness": "BLOCKED_BY_THRESHOLD_V2_CALIBRATION", + } + document.update(overrides) + return document + + +def _join(**overrides) -> dict: + row = { + "calibration_row_id": 1, + "operator_label": "HIGH", + "pos": "noun", + "pwn30_synset": "noun:01234567", + "residual_score": "0.1000000000", + "row_id": "a" * 64, + "row_resolution_status": "RESIDUAL_READY", + "surface": "example phrase", + } + row.update(overrides) + return row + + +def test_schema_family_is_draft_2020_12(): + loaded, _registry = _schemas() + assert set(loaded) == { + "residual-threshold-common.schema.json", + "residual-threshold-completion-receipt.schema.json", + "residual-threshold-confound-analysis.schema.json", + "residual-threshold-decision.schema.json", + "residual-threshold-direction-analysis.schema.json", + "residual-threshold-receipt.schema.json", + "residual-threshold-support-join.schema.json", + "residual-threshold-surface-manifest.schema.json", + "residual-threshold-threshold-search.schema.json", + } + for document in loaded.values(): + Draft202012Validator.check_schema(document) + + +def test_completion_receipt_is_not_a_generic_receipt(): + assert classify("RESIDUAL_THRESHOLD_V2_CALIBRATION_COMPLETION_RECEIPT.json") == "completion" + assert classify("RESIDUAL_THRESHOLD_V2_SURFACE_DRAW_RECEIPT.json") == "receipt" + + +def test_unfrozen_threshold_requires_null_value(): + loaded, registry = _schemas() + validator = _validator(loaded["residual-threshold-completion-receipt.schema.json"], registry) + assert not validator.is_valid(_completion(threshold_value="0.2500000000")) + assert validator.is_valid(_completion()) + + +def test_frozen_threshold_requires_numeric_value_and_metrics(): + loaded, registry = _schemas() + validator = _validator(loaded["residual-threshold-completion-receipt.schema.json"], registry) + frozen = _completion( + threshold_frozen=True, + threshold_search="THRESHOLD_FROZEN", + threshold_value="0.2500000000", + training_readiness="CALIBRATION_COMPLETE_MEASUREMENT_PENDING", + ) + assert not validator.is_valid(frozen) + frozen.update( + { + "false_high": 1, + "high_recall": "0.500000", + "leave_one_out_result": "PASS", + "precision": "0.800000", + "predicted_yes_support": 8, + "quarantine_predicted_yes": 0, + "reject_predicted_yes": 0, + "true_high": 7, + } + ) + assert validator.is_valid(frozen) + + +def test_unknown_join_row_rejects_a_residual(): + loaded, registry = _schemas() + validator = _validator(loaded["residual-threshold-support-join.schema.json"], registry) + assert not validator.is_valid(_join(row_resolution_status="UNKNOWN", residual_score="0.1000000000")) + assert validator.is_valid(_join(row_resolution_status="UNKNOWN", residual_score=None)) + + +def test_ready_join_row_requires_a_residual(): + loaded, registry = _schemas() + validator = _validator(loaded["residual-threshold-support-join.schema.json"], registry) + assert not validator.is_valid(_join(residual_score=None)) + assert validator.is_valid(_join()) + + +def test_sha256_rejects_uppercase_and_short_digests(): + loaded, registry = _schemas() + wrapper = { + "$id": "https://hyperlex.local/schemas/residual-threshold-decision.schema.json", + "$ref": "residual-threshold-common.schema.json#/$defs/sha256", + } + validator = _validator(wrapper, registry) + assert validator.is_valid("a" * 64) + assert not validator.is_valid("A" * 64) + assert not validator.is_valid("ab") + errors = sorted(validator.iter_errors("AB"), key=lambda item: list(item.path)) + rendered = [f"{'/'.join(str(part) for part in error.path)}: {error.validator}" for error in errors] + assert rendered + assert all("AB" not in line for line in rendered) + + +def test_validator_writes_only_the_public_conformance_report(): + source = inspect.getsource(schema_mod) + assert source.count(".write_text") == 1 + assert "open(" not in source + assert "hlx-private" not in str(schema_mod.REPORT_PATH) + assert schema_mod.REPORT_PATH.name == "CONFORMANCE.json" + + +def test_conformance_does_not_change_frozen_evidence(): + if not SOURCE.is_dir(): + pytest.skip("private reserve is absent") + before = evidence_hashes(SOURCE) + report = conformance_report() + after = evidence_hashes(SOURCE) + assert before == after + assert report["calibration_evidence_modified"] is False + assert report["changed_artifacts"] == [] + assert report["outcome"] == PASS + assert report["schema_conformance_gaps"] == [] + assert report["semantic_conformance_gaps"] == [] + assert report["scientific_remediation"] is False + assert report["training_authorized"] is False + statuses = {row["status"] for row in report["results"]} | {row["status"] for row in report["semantic"]} + assert statuses == {PASS} + assert GAP not in statuses + assert len(report["results"]) == 26 + assert len(report["semantic"]) == 18 diff --git a/tests/shadow/test_residual_threshold_v1.py b/tests/shadow/test_residual_threshold_v1.py new file mode 100644 index 00000000..7b682d21 --- /dev/null +++ b/tests/shadow/test_residual_threshold_v1.py @@ -0,0 +1,193 @@ +"""Threshold preregistration. Synthetic rows only. No development scores.""" + +import json +import sys +from decimal import Decimal +from pathlib import Path + +ROOT = Path(__file__).resolve().parents[2] +sys.path.insert(0, str(ROOT / "scripts" / "shadow")) + +from hyperlexical.residual_threshold_v1 import ( + MIN_CALIBRATION_HIGH, + MIN_CALIBRATION_SECONDARY, + MIN_MEASUREMENT_HIGH, + MIN_MEASUREMENT_PREDICTED_YES, + MIN_PREDICTED_YES, + OUTPUT_LABELS, + PRECISION_FLOOR, + WILSON_LOWER_FLOOR, + documents, + measurement_acceptance, + precision_gates, + select_threshold, + semantic_output, + wilson_interval, +) +from hyperlexical.selection_surface import Z95, wilson_interval as selection_wilson + +MODULE = ROOT / "scripts" / "shadow" / "hyperlexical" / "residual_threshold_v1.py" +FREEZE = ROOT / "scripts" / "shadow" / "hyperlexical" / "residual_threshold_v1_freeze.py" + + +def _row(score, bucket, pos="noun", tier3=False, status="SCORED", **extra): + row = { + "glossbert_confidence": extra.pop("glossbert_confidence", "0.100000"), + "operator_bucket": bucket, + "pos": pos, + "residual_score": f"{Decimal(score):.10f}", + "score_status": status, + "uses_tier3": tier3, + } + row.update(extra) + return row + + +def _separated(high="0.90", secondary="0.20", high_n=12, secondary_n=8, reject="0.01"): + rows = [_row(high, "HIGH", pos="noun") for _ in range(high_n)] + rows.extend(_row(secondary, "SECONDARY", pos="noun") for _ in range(secondary_n)) + rows.append(_row(reject, "REJECT", pos="noun")) + return rows + + +def test_wilson_matches_the_existing_selection_surface_interval(): + assert Decimal(str(Z95)) == Decimal("1.959963984540054") + for successes, trials in ((1, 1), (3, 3), (8, 8), (8, 10), (12, 15)): + got = wilson_interval(successes, trials) + raw = selection_wilson(successes, trials) + assert got is not None and raw is not None + assert got[0] == Decimal(raw[0]).quantize(Decimal("0.000001")) + assert got[1] == Decimal(raw[1]).quantize(Decimal("0.000001")) + assert wilson_interval(1, 0) is None + + +def test_one_correct_prediction_does_not_pass_precision(): + assert precision_gates(1, 1, minimum_yes=MIN_PREDICTED_YES, precision_floor=PRECISION_FLOOR, wilson_floor=WILSON_LOWER_FLOOR) is False + assert precision_gates(3, 3, minimum_yes=MIN_PREDICTED_YES, precision_floor=PRECISION_FLOOR, wilson_floor=WILSON_LOWER_FLOOR) is False + assert precision_gates(8, 10, minimum_yes=MIN_PREDICTED_YES, precision_floor=PRECISION_FLOOR, wilson_floor=WILSON_LOWER_FLOOR) is False + + +def test_contracts_freeze_null_threshold_and_yes_unknown_only(): + docs = documents({"development_evidence_sha256": "0" * 64}) + assert set(docs) == { + "RESIDUAL_THRESHOLD_V1_ACCEPTANCE.json", + "RESIDUAL_THRESHOLD_V1_ARTIFACT_SCHEMA.json", + "RESIDUAL_THRESHOLD_V1_CALIBRATION_CONTRACT.json", + "RESIDUAL_THRESHOLD_V1_MEASUREMENT_CONTRACT.json", + "RESIDUAL_THRESHOLD_V1_PREREGISTRATION.json", + "RESIDUAL_THRESHOLD_V1_SELECTION_PROCEDURE.json", + "RESIDUAL_THRESHOLD_V1_SURFACE_ISOLATION_POLICY.json", + } + for name, document in docs.items(): + assert document["threshold_value"] is None + assert document["threshold_frozen"] is False + assert document["calibration_surface_drawn"] is False + assert document["measurement_surface_drawn"] is False + assert document["development_rows_reusable_for_threshold_selection"] is False + assert document["selected_source"] == "none" + assert document["select_005_authorized"] is False + assert document["runtime_integration"] is False + assert document["admitted"] == document["settled"] == document["gold"] == 0 + assert document["json_schema_exists"] is False + assert document["output_labels"] == ["YES", "UNKNOWN"] + assert document["no_output"] == "NO" + encoded = json.dumps(document) + assert "semantic_noncompositional = NO" not in encoded + assert name + schema = docs["RESIDUAL_THRESHOLD_V1_ARTIFACT_SCHEMA.json"] + assert schema["document_kind"] == "artifact_contract" + assert "threshold_value" in schema["future_threshold_artifact_required_fields"] + assert MIN_CALIBRATION_HIGH == 12 + assert MIN_CALIBRATION_SECONDARY == 8 + assert MIN_PREDICTED_YES == 8 + assert MIN_MEASUREMENT_HIGH == 12 + assert MIN_MEASUREMENT_PREDICTED_YES == 8 + assert PRECISION_FLOOR == Decimal("0.80") + assert WILSON_LOWER_FLOOR == Decimal("0.50") + + +def test_highest_recall_wins_and_a_larger_threshold_breaks_ties(): + rows = [_row("0.90", "HIGH") for _ in range(9)] + rows.extend(_row("0.50", "HIGH") for _ in range(3)) + rows.extend(_row("0.10", "SECONDARY") for _ in range(8)) + rows.append(_row("0.01", "REJECT")) + chosen = select_threshold(rows) + assert chosen["calibration_state"] == "THRESHOLD_FROZEN" + assert chosen["threshold_value"] == "0.5000000000" + tied = [_row("0.90", "HIGH") for _ in range(12)] + tied.extend(_row("0.10", "SECONDARY") for _ in range(7)) + tied.append(_row("0.40", "SECONDARY")) + tied.append(_row("0.01", "REJECT")) + tie = select_threshold(tied) + assert tie["calibration_state"] == "THRESHOLD_FROZEN" + assert tie["threshold_value"] == "0.9000000000" + assert select_threshold(rows) == chosen + + +def test_failure_states_leave_the_threshold_null(): + short = _separated(high_n=11) + assert select_threshold(short)["calibration_state"] == "CALIBRATION_INSUFFICIENT_SUPPORT" + assert select_threshold(short)["threshold_value"] is None + inverted = _separated(high="0.10", secondary="0.90") + assert select_threshold(inverted)["calibration_state"] == "NO_DIRECTIONAL_SIGNAL" + blocked = _separated(reject="0.99") + assert select_threshold(blocked)["calibration_state"] == "NO_THRESHOLD_PASSES_PRECISION_GATE" + overlap = [_row("0.90", "HIGH") for _ in range(12)] + overlap.extend(_row("0.10", "SECONDARY") for _ in range(5)) + overlap.extend(_row("0.95", "SECONDARY") for _ in range(3)) + overlap.append(_row("0.01", "REJECT")) + missed = select_threshold(overlap) + assert missed["calibration_state"] == "NO_THRESHOLD_PASSES_PRECISION_GATE" + assert missed["threshold_value"] is None + + +def test_confound_review_and_ignored_provenance_do_not_fit_a_subgroup_threshold(): + split = [_row("0.90", "HIGH", pos="noun") for _ in range(12)] + split.extend(_row("0.20", "SECONDARY", pos="verb") for _ in range(8)) + split.append(_row("0.01", "REJECT", pos="noun")) + assert select_threshold(split)["calibration_state"] == "CALIBRATION_CONFOUND_REVIEW" + left = _separated() + right = _separated() + for row in right: + row["glossbert_confidence"] = "0.990000" + row["glossbert_margin"] = "0.800000" + row["candidate_sense_count"] = 9 + assert select_threshold(left)["threshold_value"] == select_threshold(right)["threshold_value"] + unknown = _separated(high_n=11) + unknown.append(_row("0.99", "HIGH", status="UNKNOWN")) + assert select_threshold(unknown)["calibration_state"] == "CALIBRATION_INSUFFICIENT_SUPPORT" + + +def test_development_rows_are_refused_and_measurement_does_not_refit(): + rows = _separated() + rows[0]["development_row"] = True + try: + select_threshold(rows) + except RuntimeError as exc: + assert "development" in str(exc) + else: + raise AssertionError("development row was accepted") + clean = _separated() + measured = measurement_acceptance(clean, "0.9000000000") + assert measured["measurement_state"] == "MEASUREMENT_ACCEPTED" + assert measured["applied_threshold"] == "0.9000000000" + assert measured["threshold_adjusted"] is False + weak = measurement_acceptance(clean, "0.0100000000") + assert weak["measurement_state"] == "MEASUREMENT_NOT_ACCEPTED" + assert weak["applied_threshold"] == "0.0100000000" + assert semantic_output("SCORED", "0.9000000000", "0.5000000000") == "YES" + assert semantic_output("SCORED", "0.4000000000", "0.5000000000") == "UNKNOWN" + assert semantic_output("UNKNOWN", "0.9000000000", "0.5000000000") == "UNKNOWN" + assert set(OUTPUT_LABELS) == {"YES", "UNKNOWN"} + + +def test_module_does_not_fit_the_development_distribution(): + source = MODULE.read_text(encoding="utf-8") + assert "RESIDUAL_V1_MODEL_RESOLVED_REPLAY_SCORES" not in source + assert "0.672078" not in source + assert "0.4040327275" not in source + assert "semantic_noncompositional = NO" not in source + assert "torch" not in source + freeze = FREEZE.read_text(encoding="utf-8") + assert "select_threshold(" not in freeze + assert "measurement_acceptance(" not in freeze diff --git a/tests/shadow/test_select_005_exposure_refresh.py b/tests/shadow/test_select_005_exposure_refresh.py new file mode 100644 index 00000000..9356acc9 --- /dev/null +++ b/tests/shadow/test_select_005_exposure_refresh.py @@ -0,0 +1,154 @@ +"""Exposure refresh does not invent a snapshot or start a harvest.""" + +import hashlib +from pathlib import Path + +from hyperlexical.select_005_exposure_refresh import ( + PREVIOUS_SNAPSHOT_ID, + UNAVAILABLE, + pair_binding, + workflow_gate, +) +from hyperlexical.select_005_harvest import STALE + +REPO = Path(__file__).resolve().parents[2] +HARVEST_SHA256 = "8f58f30ca85af2e476064c7afea9498b02a53d71c4890d38b4cdc2baaf83021c" + + +def _obs(**overrides): + base = { + "executable_present": False, + "host": "spark-bf46", + "snapshot_only_entrypoint_present": False, + "vernacular_source_present": False, + } + base.update(overrides) + return base + + +def _pair(snapshot_id: str, age_seconds: float, *, vern_id: str | None = None, box_id: str | None = None): + compared = "1790631188.652030" + mtime = f"{float(compared) - age_seconds:.6f}" + vern_snapshot = vern_id or snapshot_id + box_snapshot = box_id or snapshot_id + return [ + { + "kind": "vern", + "mtime_unix": mtime, + "path": f"inbox/vern-texts-{vern_snapshot}.jsonl", + "sha256": "ab" * 32, + "snapshot_id": vern_snapshot, + }, + { + "kind": "boxhash", + "mtime_unix": mtime, + "path": f"inbox/box-jev-hashes-{box_snapshot}.txt", + "sha256": "cd" * 32, + "snapshot_id": box_snapshot, + }, + ], compared + + +def test_missing_workflow_stops_without_a_snapshot_or_harvest(): + record = workflow_gate(_obs()) + assert record["state"] == UNAVAILABLE + assert record["snapshot_id"] is None + assert record["harvest_run"] is False + assert record["reserve_recensus_run"] is False + assert record["training_run"] is False + assert record["network_requests"] == 0 + assert record["previous_pair_overwritten"] is False + assert record["previous_pair_reused"] is False + assert record["previous_snapshot_id"] == PREVIOUS_SNAPSHOT_ID + assert record["missing"] + + +def test_a_daily_script_that_also_harvests_is_not_a_snapshot_entrypoint(): + record = workflow_gate( + _obs( + host="box", + executable_present=True, + vernacular_source_present=True, + snapshot_only_entrypoint_present=False, + ) + ) + assert record["state"] == UNAVAILABLE + assert record["harvest_run"] is False + assert any("hs_run.py run" in item for item in record["missing"]) + + +def test_matching_fresh_ids_bind_and_a_mismatch_or_stale_pair_does_not_pass(): + fresh, compared = _pair("20260928T220000Z", 3600) + bound = pair_binding(fresh, compared_at_unix=compared) + assert bound["pair_binding_status"] == "BOUND" + assert bound["structural_validation"] == "PASS" + assert bound["vern_snapshot_id"] == bound["box_snapshot_id"] == "20260928T220000Z" + assert bound["freshness_result"] == "EXPOSURE_SNAPSHOT_FRESH" + assert float(bound["binding_age_hours"]) <= 6 + assert bound["harvest_run"] is False + + mismatched, compared = _pair( + "20260928T220000Z", + 3600, + box_id="20260928T220100Z", + ) + refused = pair_binding(mismatched, compared_at_unix=compared) + assert refused["pair_binding_status"] == "MISMATCHED" + assert refused["structural_validation"] == "FAIL" + assert refused["state"] == "EXPOSURE_SNAPSHOT_INVALID" + assert refused["freshness_result"] is None + + stale, compared = _pair("20260925T211351Z", 72.32 * 3600) + old = pair_binding(stale, compared_at_unix=compared) + assert old["state"] == STALE + assert old["freshness_result"] == STALE + assert float(old["binding_age_hours"]) > 6 + + +def test_refresh_module_does_not_invoke_harvest_and_harvest_file_is_unchanged(): + text = (REPO / "scripts/shadow/hyperlexical/select_005_exposure_refresh.py").read_text() + for banned in ("subprocess", "urllib", "urlopen", "import hs_run", "Popen"): + assert banned not in text + harvest = REPO / "scripts/shadow/hyperlexical/select_005_harvest.py" + assert hashlib.sha256(harvest.read_bytes()).hexdigest() == HARVEST_SHA256 + + +def test_notion_term_and_event_registries_do_not_bind_a_pair(): + from hyperlexical.select_005_exposure_refresh import ( + NOTION_NOT_JEV_HASH_LIST, + notion_registry_gap, + ) + + gap = notion_registry_gap( + { + "candidates_database_present": False, + "discovery_lane_export_present": False, + "events": 120, + "jev_lane_hash_generator_present": False, + "observations_database_present": False, + "obsidian_export_present": False, + "terms": 44, + } + ) + assert gap["state"] == UNAVAILABLE + assert gap["snapshot_id"] is None + assert gap["pair_written"] is False + assert gap["harvest_run"] is False + assert gap["previous_pair_reused"] is False + assert gap["network_requests"] == 0 + assert gap["terms"] == 44 + assert gap["events"] == 120 + assert NOTION_NOT_JEV_HASH_LIST in gap["missing"] + + record = workflow_gate( + _obs( + vernacular_source_present=True, + vernacular_source_kind="notion", + jev_lane_hash_generator_present=False, + ) + ) + assert record["state"] == UNAVAILABLE + assert record["snapshot_id"] is None + assert record["harvest_run"] is False + assert NOTION_NOT_JEV_HASH_LIST in record["missing"] + assert all("sqlite" not in item for item in record["missing"]) diff --git a/tests/shadow/test_select_005_reserve.py b/tests/shadow/test_select_005_reserve.py new file mode 100644 index 00000000..d022cd6b --- /dev/null +++ b/tests/shadow/test_select_005_reserve.py @@ -0,0 +1,185 @@ +"""SELECT-005 reserve census stays deterministic and does not emit a short reserve.""" + +from __future__ import annotations + +import ast +from pathlib import Path + +from hyperlexical.select_005_reserve import ( + ADMISSION_FLOORS, + EXPERIMENT_ID, + FAILURE_ISOLATION, + FAILURE_QUOTA, + PROPOSED_SCHEDULE, + SELECT_ADMISSION_JSON_SCHEMA_EXISTS, + TRAINING_AUTHORIZED, + census, + exclusion_reason, + schedule_spec_sha256, + spent_reserve_hashes, + unsettled_screen_counts, + _order_key, +) + +MODULE = Path(__file__).resolve().parents[2] / "scripts" / "shadow" / "hyperlexical" / "select_005_reserve.py" + + +def _event(**overrides): + row = { + "class": "OBSERVED", + "decision": "ACCEPT", + "lineage": "internet-slang", + "provenance": "settlement-record", + "rights": "CC-BY-SA", + "row_id": "row-1", + "task": "classify", + "text_hash": "a" * 64, + } + row.update(overrides) + return row + + +def _unbind(**overrides): + return _event( + row_id="row-u", + task="unbind", + text_hash="b" * 64, + unbind_clean=True, + lineage="", + **overrides, + ) + + +def test_schedule_proposal_is_unsealed_and_does_not_invent_trainer_fields(): + assert PROPOSED_SCHEDULE["sealed"] is False + assert PROPOSED_SCHEDULE["experiment_id"] == EXPERIMENT_ID + assert PROPOSED_SCHEDULE["scientific_variable"] == "train_schedule" + assert PROPOSED_SCHEDULE["selection_metric"] == "classify_macro_f1_nonnone" + assert PROPOSED_SCHEDULE["baseline"]["HYPERLEX_TRAIN_EPOCHS"] == "40" + assert PROPOSED_SCHEDULE["candidate"]["HYPERLEX_TRAIN_EPOCHS"] == "12" + assert PROPOSED_SCHEDULE["candidate"]["HYPERLEX_EARLY_STOP_PATIENCE"] == "4" + assert PROPOSED_SCHEDULE["candidate"]["HYPERLEX_EARLY_STOP_MIN_EPOCHS"] == "4" + assert set(PROPOSED_SCHEDULE["unset_fields"]) == { + "batch_size", + "gradient_accumulation", + "learning_rate", + "seed", + } + assert "learning_rate" not in PROPOSED_SCHEDULE + assert schedule_spec_sha256() == schedule_spec_sha256() + assert SELECT_ADMISSION_JSON_SCHEMA_EXISTS is True + assert TRAINING_AUTHORIZED is False + assert ADMISSION_FLOORS == { + "classify": 1, + "classify_observed": 1, + "classify_non_none": 1, + "unbind_clean": 1, + } + + +def test_blocked_ledger_and_unresolved_rights_are_ineligible(): + spent = {"c" * 64: "EVAL_SPENT"} + assert exclusion_reason(_event(text_hash="c" * 64), spent, set()) == "EVAL_SPENT" + assert exclusion_reason(_event(text_hash="d" * 64), {"d" * 64: "TRAIN_CONSUMED"}, set()) == "TRAIN_CONSUMED" + assert exclusion_reason(_event(decision="UNRESOLVED"), {}, set()) == "DECISION_UNRESOLVED" + assert exclusion_reason(_event(rights="RIGHTS_UNRESOLVED"), {}, set()) == "RIGHTS_UNRESOLVED" + assert exclusion_reason(_event(text_hash="e" * 64), {}, {"e" * 64}) == "PINNED_EXPORT" + assert exclusion_reason(_event(row_id=""), {}, set()) == "PROVENANCE_INCOMPLETE" + + +def test_two_reconstructions_match_when_the_quota_can_be_filled(): + events = [_event(), _unbind()] + first = census(events, {}, set()) + second = census(list(reversed(events)), {}, set()) + assert first == second + assert first["determinism"] == "IDENTICAL" + assert first["failure"] is None + assert first["reserve_row_count"] == 2 + assert first["frozen_slice_counts"] == { + "classify": 1, + "classify_observed": 1, + "classify_non_none": 1, + "unbind_clean": 1, + } + assert first["reserve_manifest_sha256"] + assert first["eligible_order"] == sorted(first["eligible_order"], key=_order_key) + + +def test_unbind_alone_does_not_freeze_a_partial_reserve(): + report = census([_unbind()], {}, set()) + assert report["failure"] == FAILURE_QUOTA + assert report["routed_before_freeze_gate"] == 1 + assert report["reserve_row_count"] == 0 + assert report["reserve_manifest_sha256"] is None + assert report["reserve_rows"] == [] + assert report["training_authorized"] is False + + +def test_spent_select_rows_cannot_refill_the_quota(): + digest = "a" * 64 + report = census( + [_event(), _unbind()], + {digest: "EVAL_SPENT", "b" * 64: "EVAL_SPENT"}, + set(), + ) + assert report["failure"] == FAILURE_QUOTA + assert report["eligible_count"] == 0 + assert report["exclusion_counts"]["EVAL_SPENT"] == 2 + assert report["reserve_row_count"] == 0 + + +def test_measurement_overlap_is_an_isolation_failure(): + events = [_event(), _unbind()] + fences = { + "threshold_v2_measurement": { + "hashes": {"b" * 64}, + "row_ids": set(), + "source_ids": set(), + "synsets": set(), + } + } + report = census(events, {}, set(), fences=fences) + assert report["failure"] == FAILURE_ISOLATION + assert report["reserve_row_count"] == 0 + assert report["reserve_manifest_sha256"] is None + assert report["isolation"]["threshold_v2_measurement"]["normalized_identity"] == 1 + + +def test_select_004_spent_reserve_is_the_reserved_flag_not_the_binding_list(): + spent = { + "normalized_text_sha256": "a" * 64, + "evaluation_spent": True, + "evaluation_reserved": True, + "experiment_bindings": [], + } + bound_elsewhere = { + "normalized_text_sha256": "b" * 64, + "training_consumed": True, + "experiment_bindings": ["HLX-EXP-2026-09-26-SELECT-001"], + } + assert spent_reserve_hashes([spent, bound_elsewhere]) == {"a" * 64} + + +def test_review_packet_without_a_decision_is_not_a_settlement(): + counts = unsettled_screen_counts( + [ + {"bucket": "READY_FOR_OPERATOR_REVIEW", "operator_decision": None}, + {"bucket": "RIGHTS_BLOCKED", "operator_decision": ""}, + ] + ) + assert counts["OPERATOR_DECISION_ABSENT"] == 2 + assert counts["rows"] == 2 + + +def test_module_does_not_train_or_read_the_residual_lane(): + tree = ast.parse(MODULE.read_text(encoding="utf-8")) + imported: set[str] = set() + for node in ast.walk(tree): + if isinstance(node, ast.Import): + imported.update(alias.name for alias in node.names) + elif isinstance(node, ast.ImportFrom) and node.module: + imported.add(node.module) + assert not any("residual" in name or name.startswith("torch") for name in imported) + source = MODULE.read_text(encoding="utf-8") + assert "persist_append" not in source + assert "optimizer" not in source From 1b4fb061a2229f46217710304dedd06e42e5ae94 Mon Sep 17 00:00:00 2001 From: Daniel Meyer Date: Mon, 28 Sep 2026 16:04:49 -0700 Subject: [PATCH 10/42] feat(007): land SELECT residual and schedule work (9b) --- scripts/shadow/hyperlexical/select_005_supply.py | 1 + 1 file changed, 1 insertion(+) diff --git a/scripts/shadow/hyperlexical/select_005_supply.py b/scripts/shadow/hyperlexical/select_005_supply.py index 5fd65a62..b04b4899 100644 --- a/scripts/shadow/hyperlexical/select_005_supply.py +++ b/scripts/shadow/hyperlexical/select_005_supply.py @@ -189,3 +189,4 @@ def threshold_authorization_proposal() -> dict[str, Any]: "threshold_authorization_json_schema_exists": THRESHOLD_AUTHORIZATION_JSON_SCHEMA_EXISTS, "training_launch_authorized": False, } + From 69efc40a3c5001bfe2cc0d359896478508cb414a Mon Sep 17 00:00:00 2001 From: Daniel Meyer Date: Mon, 28 Sep 2026 16:06:56 -0700 Subject: [PATCH 11/42] feat(007): land SELECT residual and schedule work (10) --- .../hyperlexical/residual_threshold_schema.py | 247 +++++++++++++ .../residual_threshold_v1_freeze.py | 160 ++++++++ .../hyperlexical/residual_threshold_v2.py | 249 +++++++++++++ .../evidence-record.schema.json | 343 ++++++++++++++++++ .../residual-threshold/CONFORMANCE.json | 305 ++++++++++++++++ tests/shadow/test_select_005_completion.py | 200 ++++++++++ 6 files changed, 1504 insertions(+) create mode 100644 scripts/shadow/hyperlexical/residual_threshold_schema.py create mode 100644 scripts/shadow/hyperlexical/residual_threshold_v1_freeze.py create mode 100644 scripts/shadow/hyperlexical/residual_threshold_v2.py create mode 100644 specs/007-hyperlexical-model/schemas/hyperlex/semantic-evidence-v2/evidence-record.schema.json create mode 100644 specs/007-hyperlexical-model/schemas/residual-threshold/CONFORMANCE.json create mode 100644 tests/shadow/test_select_005_completion.py diff --git a/scripts/shadow/hyperlexical/residual_threshold_schema.py b/scripts/shadow/hyperlexical/residual_threshold_schema.py new file mode 100644 index 00000000..90aafed9 --- /dev/null +++ b/scripts/shadow/hyperlexical/residual_threshold_schema.py @@ -0,0 +1,247 @@ +"""Validate sealed residual-threshold artifacts against the retrospective schemas. + +This module only reads private evidence. A conformance gap is reported. +Frozen bytes are not rewritten. +""" + +from __future__ import annotations + +import hashlib +import json +import sys +from pathlib import Path + +from jsonschema import Draft202012Validator +from referencing import Registry, Resource + +REPO = Path(__file__).resolve().parents[3] +SCHEMA_DIR = REPO / "specs/007-hyperlexical-model/schemas/residual-threshold" +SOURCE = Path("/home/morpheus/hlx-private/eval-reserve-20260926/operator-review/HLX-EVAL-UNBIND-SEMANTIC-EVIDENCE-SOURCE-V1-001") +LEDGER = Path("/home/morpheus/hlx-private/eval-reserve-20260926") +TRACKER = LEDGER / "operator-review/HLX-EVAL-UNBIND-SENSE-SCREEN-V1-HYPOTHESIS-001/HYPOTHESIS.json" +REPORT_PATH = SCHEMA_DIR / "CONFORMANCE.json" + +SCHEMA_FILES = { + "manifest": "residual-threshold-surface-manifest.schema.json", + "receipt": "residual-threshold-receipt.schema.json", + "join": "residual-threshold-support-join.schema.json", + "direction": "residual-threshold-direction-analysis.schema.json", + "confound": "residual-threshold-confound-analysis.schema.json", + "search": "residual-threshold-threshold-search.schema.json", + "decision": "residual-threshold-decision.schema.json", + "completion": "residual-threshold-completion-receipt.schema.json", +} + +EXPECTED_MEASUREMENT = "78ca09ab14912681d028e8b9b1c77a8daf1d0c8c81561434eb9b6ade45a753e5" +EXPECTED_EVENTS = "96b74a92d44f1cf9fe152b18e5207176f161ba3bfce528dac38aa4571a742f9c" +EXPECTED_LEDGER = "77e22433203879b252f7a9e309d2013d7550101d1c4a014b494d2c96df87d0e0" +EXPECTED_TRACKER = "e674c207d9eb160159a673c23f7e2a2e8262b5bfc963ccf81b216d8291dc4b0f" +GAP = "SCHEMA_CONFORMANCE_GAP" +PASS = "PASS" +OPERATOR_LABELS = {"HIGH", "SECONDARY", "REJECT", "QUARANTINE", "UNRESOLVED"} + + +def sha256_file(path: Path) -> str: + return hashlib.sha256(path.read_bytes()).hexdigest() + + +def load_schemas() -> dict[str, dict]: + loaded = {} + for path in sorted(SCHEMA_DIR.glob("*.schema.json")): + loaded[path.name] = json.loads(path.read_text(encoding="utf-8")) + return loaded + + +def schema_registry(schemas: dict[str, dict]) -> Registry: + registry = Registry() + for schema in schemas.values(): + registry = registry.with_resource(schema["$id"], Resource.from_contents(schema)) + return registry + + +def _errors(schema: dict, instance: object, registry: Registry) -> list[str]: + validator = Draft202012Validator(schema, registry=registry) + found = [] + for error in sorted(validator.iter_errors(instance), key=lambda item: list(item.path)): + location = "/" + "/".join(str(part) for part in error.path) + found.append(f"{location}: {error.validator}") + return found + + +def classify(name: str) -> str | None: + if name.endswith("_MANIFEST.jsonl"): + return "manifest" + if name.endswith("_SUPPORT_JOIN.jsonl"): + return "join" + if name.endswith("_COMPLETION_RECEIPT.json"): + return "completion" + if name.endswith("_RECEIPT.json"): + return "receipt" + if name.endswith("_DIRECTION_ANALYSIS.json"): + return "direction" + if name.endswith("_CONFOUND_ANALYSIS.json"): + return "confound" + if name.endswith("_THRESHOLD_SEARCH.json"): + return "search" + if name.endswith("_DIRECTION_DECISION.json") or name.endswith("_CONFOUND_DECISION.json"): + return "decision" + if name.endswith("_FAILURE.json") or name.endswith("_DESIGN_CLOSURE.json"): + return "decision" + if name.endswith("_SUPPORT_REASSESSMENT.json"): + return "decision" + return None + + +def _jsonl(path: Path) -> list[dict]: + return [json.loads(line) for line in path.read_text(encoding="utf-8").splitlines() if line.strip()] + + +def validate_artifact(path: Path, kind: str, schemas: dict[str, dict], registry: Registry) -> dict: + schema = schemas[SCHEMA_FILES[kind]] + errors: list[str] = [] + if path.suffix == ".jsonl": + for index, row in enumerate(_jsonl(path), start=1): + for error in _errors(schema, row, registry): + errors.append(f"line {index} {error}") + else: + errors.extend(_errors(schema, json.loads(path.read_text(encoding="utf-8")), registry)) + return { + "artifact": path.name, + "errors": errors, + "schema": SCHEMA_FILES[kind], + "status": PASS if not errors else GAP, + } + + +def _semantic(source: Path) -> list[dict]: + checks = [] + + def add(name: str, ok: bool, detail: str) -> None: + checks.append({"check": name, "detail": detail, "status": PASS if ok else GAP}) + + manifest = _jsonl(source / "RESIDUAL_THRESHOLD_V2_CALIBRATION_MANIFEST.jsonl") + labels = _jsonl(source / "RESIDUAL_THRESHOLD_V2_OPERATOR_LABELS.jsonl") + join = _jsonl(source / "RESIDUAL_THRESHOLD_V2_CALIBRATION_SUPPORT_JOIN.jsonl") + measurement = _jsonl(source / "RESIDUAL_THRESHOLD_V2_MEASUREMENT_MANIFEST.jsonl") + manifest_ids = {row["row_id"] for row in manifest} + label_ids = {row["row_id"] for row in labels} + join_ids = {row["row_id"] for row in join} + measurement_ids = {row["row_id"] for row in measurement} + ready = sum(row["row_resolution_status"] == "RESIDUAL_READY" for row in join) + unknown = sum(row["row_resolution_status"] == "UNKNOWN" for row in join) + add("support_join_rows", len(join) == 200, f"rows={len(join)}") + add("ready_plus_unknown", ready + unknown == 200, f"ready={ready} unknown={unknown}") + add( + "row_id_sets", + manifest_ids == label_ids == join_ids and len(manifest_ids) == 200, + f"manifest={len(manifest_ids)} labels={len(label_ids)} join={len(join_ids)}", + ) + add( + "measurement_disjoint", + measurement_ids.isdisjoint(manifest_ids) and len(measurement_ids) == 200, + f"measurement_rows={len(measurement_ids)}", + ) + measurement_hash = sha256_file(source / "RESIDUAL_THRESHOLD_V2_MEASUREMENT_MANIFEST.jsonl") + manifest_hash = sha256_file(source / "RESIDUAL_THRESHOLD_V2_CALIBRATION_MANIFEST.jsonl") + add("measurement_manifest_unchanged", measurement_hash == EXPECTED_MEASUREMENT, measurement_hash) + receipt = json.loads((source / "RESIDUAL_THRESHOLD_V2_CALIBRATION_SUPPORT_JOIN_RECEIPT.json").read_text(encoding="utf-8")) + add("join_receipt_manifest_hash", receipt["manifest_hash"] == manifest_hash, receipt["manifest_hash"]) + add( + "join_receipt_label_hash", + receipt["operator_label_hash"] == sha256_file(source / "RESIDUAL_THRESHOLD_V2_OPERATOR_LABELS.jsonl"), + receipt["operator_label_hash"], + ) + add( + "join_receipt_score_hash", + receipt["score_hash"] == sha256_file(source / "RESIDUAL_THRESHOLD_V2_CALIBRATION_SCORES.jsonl"), + receipt["score_hash"], + ) + direction = json.loads((source / "RESIDUAL_THRESHOLD_V2_DIRECTION_ANALYSIS.json").read_text(encoding="utf-8")) + add( + "direction_join_hash", + direction["source_support_join_hash"] == sha256_file(source / "RESIDUAL_THRESHOLD_V2_CALIBRATION_SUPPORT_JOIN.jsonl"), + direction["source_support_join_hash"], + ) + label_values = {row["operator_label"] for row in labels} + add("operator_labels_closed", label_values <= OPERATOR_LABELS, "labels stay inside the emitted enum") + v1_labels = _jsonl(source / "RESIDUAL_THRESHOLD_V1_CALIBRATION_LABELS.jsonl") + unlabeled = all(row["operator_bucket"] is None and row["operator_label_present"] is False for row in v1_labels) + add("v1_labels_unlabeled", unlabeled and len(v1_labels) == 27, f"rows={len(v1_labels)}") + v1_manifest = _jsonl(source / "RESIDUAL_THRESHOLD_V1_CALIBRATION_MANIFEST.jsonl") + v1_identity = all(row["calibration_row_id"] == row["normalized_text_sha256"] for row in v1_manifest) + add("v1_manifest_identity", v1_identity and len(v1_manifest) == 27, f"rows={len(v1_manifest)}") + v2_identity = all(row["row_id"] == row["normalized_text_sha256"] for row in manifest) + add("v2_manifest_identity", v2_identity, "row_id matches normalized_text_sha256") + draw = json.loads((source / "RESIDUAL_THRESHOLD_V2_SURFACE_DRAW_RECEIPT.json").read_text(encoding="utf-8")) + add("draw_receipt_measurement_hash", draw["measurement_manifest_sha256"] == EXPECTED_MEASUREMENT, draw["measurement_manifest_sha256"]) + add("draw_receipt_manifest_hash", draw["calibration_manifest_sha256"] == manifest_hash, draw["calibration_manifest_sha256"]) + add("events_unchanged", sha256_file(LEDGER / "events.jsonl") == EXPECTED_EVENTS, EXPECTED_EVENTS) + add("ledger_unchanged", sha256_file(LEDGER / "ledger.json") == EXPECTED_LEDGER, EXPECTED_LEDGER) + add("tracker_unchanged", sha256_file(TRACKER) == EXPECTED_TRACKER, EXPECTED_TRACKER) + return checks + + +def evidence_hashes(source: Path) -> dict[str, str]: + return {path.name: sha256_file(path) for path in sorted(source.iterdir()) if path.is_file() and path.name.startswith("RESIDUAL_THRESHOLD_")} + + +def conformance_report() -> dict: + schemas = load_schemas() + registry = schema_registry(schemas) + before = evidence_hashes(SOURCE) if SOURCE.is_dir() else {} + results = [] + skipped = [] + if SOURCE.is_dir(): + for path in sorted(SOURCE.iterdir()): + if not path.is_file() or not path.name.startswith("RESIDUAL_THRESHOLD_"): + continue + kind = classify(path.name) + if kind is None: + skipped.append(path.name) + continue + results.append(validate_artifact(path, kind, schemas, registry)) + semantic = _semantic(SOURCE) + else: + semantic = [{"check": "private_reserve", "detail": "reserve is absent", "status": GAP}] + after = evidence_hashes(SOURCE) if SOURCE.is_dir() else {} + changed = sorted(name for name, digest in before.items() if after.get(name) != digest) + schema_gaps = [row["artifact"] for row in results if row["status"] == GAP] + semantic_gaps = [row["check"] for row in semantic if row["status"] == GAP] + return { + "authorization": "THRESHOLD_ARTIFACT_SCHEMA_HARDENING_AUTHORIZATION", + "calibration_evidence_modified": bool(changed), + "calibration_state": "NO_THRESHOLD_PASSES_PRECISION_GATE", + "changed_artifacts": changed, + "json_schema_family": sorted(SCHEMA_FILES.values()) + ["residual-threshold-common.schema.json"], + "outcome": GAP if schema_gaps or semantic_gaps or changed else PASS, + "results": results, + "schema_conformance_gaps": schema_gaps, + "scientific_remediation": False, + "semantic": semantic, + "semantic_conformance_gaps": semantic_gaps, + "skipped_not_applicable": skipped, + "threshold_redesign": False, + "training_authorized": False, + } + + +def render_report(report: dict) -> str: + return json.dumps(report, indent=2, sort_keys=True, ensure_ascii=True) + "\n" + + +def write_public_report(report: dict) -> None: + if "hlx-private" in str(REPORT_PATH) or not str(REPORT_PATH).endswith("/schemas/residual-threshold/CONFORMANCE.json"): + raise RuntimeError("conformance report path is not the public schema directory") + REPORT_PATH.write_text(render_report(report), encoding="utf-8") + + +def main() -> None: + report = conformance_report() + text = render_report(report) + if "--write" in sys.argv: + write_public_report(report) + print(text) + + +if __name__ == "__main__": + main() diff --git a/scripts/shadow/hyperlexical/residual_threshold_v1_freeze.py b/scripts/shadow/hyperlexical/residual_threshold_v1_freeze.py new file mode 100644 index 00000000..33c1c942 --- /dev/null +++ b/scripts/shadow/hyperlexical/residual_threshold_v1_freeze.py @@ -0,0 +1,160 @@ +"""Write the threshold preregistration artifacts. Does not score rows.""" + +from __future__ import annotations + +import hashlib +import json +import os +from pathlib import Path + +from hyperlexical.residual_threshold_v1 import RULE, TRACK_STATE, documents + +ROOT = Path("/home/morpheus/hlx-private/eval-reserve-20260926") +SEM = ROOT / "operator-review" / "HLX-EVAL-UNBIND-SEMANTIC-EVIDENCE-SOURCE-V1-001" +HYP = ROOT / "operator-review" / "HLX-EVAL-UNBIND-SENSE-SCREEN-V1-HYPOTHESIS-001" +MODULE = Path("/home/morpheus/Hyperlex/scripts/shadow/hyperlexical/residual_threshold_v1.py") + +EXPECTED = { + SEM / "RESIDUAL_CANDIDATE_SPEC.json": "39c2914e32557ffe1a456a56f8742ea4fe8f1aaec1dc1da451656cd22f0db32d", + SEM / "RESIDUAL_DEVELOPMENT_SCORES.jsonl": "cea638679faeee1bc1c689823e7c0c08562c4d7ef1f8230bbbf4079239e7c3e7", + SEM / "INTEGRATED_CONSTITUENT_RESOLUTION_V1.jsonl": "0f5dafc3676a4071ce8c889e58958b90203589aa3e91d78111b4e3292bdd87fb", + SEM / "RESIDUAL_V1_MODEL_RESOLVED_REPLAY_SCORES.jsonl": "16c0a9eaa918ac4a6e8223cafcbf4b1918cb212769063e262cf29a279f1048f6", + SEM / "RESIDUAL_V1_MODEL_RESOLVED_REPLAY_RECEIPT.json": "675b1b8b8f1b1e6f19c7d320e7b8fe516eae92b60e966afbce407c1f0482e736", + SEM / "RESIDUAL_V1_MODEL_RESOLVED_DEVELOPMENT_ANALYSIS.json": "4367930648a68c2f84a1fd8e011fa07d9f3bf111303079f3ec07688f7d5425fb", + SEM / "RESIDUAL_V1_MODEL_RESOLVED_CONFOUND_ANALYSIS.json": "d0f2496ca7623050eb5519969abcf7c5e2d0e23e0c1961859c40cae4dcdb7021", + SEM / "RESIDUAL_V1_MODEL_RESOLVED_CANDIDATE_DECISION.json": "ed0296fe6888e7c9fe864a2c6c7ab6cecbd4f490d6b7d650e442dafc0bd0976d", + SEM / "MODEL_BASED_WSD_CANDIDATE_SPEC.json": "c861ff7fff11ae6a790531267229c18d6e6e0a171a9bf6c34cfb6f7e7b14498c", + SEM / "MODEL_BASED_WSD_RESOLUTION.jsonl": "ed945989cf4947ac84633ba2c4aa10c1ba381d2396da0b573a844f83ec367a18", + SEM / "MODEL_BASED_WSD_READINESS_PROJECTION.json": "c75834faf4a84d36e83246244e0aa7c6c7788c3a57cfdb7f77c7628a52023328", + SEM / "CONSTITUENT_SENSE_RESOLUTION_V1_SPEC.json": "176e6219ddc3127814a25d39ad26e3571817f7ea8323d685e081d2e0fd867acb", + SEM / "CONSTITUENT_SENSE_RESOLUTION_V1_REPLAY.jsonl": "a0c707ab55e02f627a698c33ddc0ca398e34aa0bafc19b13e72422bc26d97d0a", + SEM / "MAGPIE_CANDIDATE_DECISION.json": "6eaa968b6260946998dba13e5c423f178d3349cdfe06e5ea401717f5a9bcdd0d", + SEM / "KM_CANDIDATE_DECISION.json": "93a07e3c78b53a69965497410c34ddb52c2a5d3add3fb2f3cd3fd9ca84eb3d9f", + HYP / "CLASSIFICATION_PROCEDURE.v2.json": "3f4071640d0c9f29cf56f53969a88ec25c635444b87765e77e1b9158470e5662", + HYP / "DEVELOPMENT_EVIDENCE.json": "0e9b3c1af9dd573bf6e2034640e468e8ab9074e1e76c90cef1f39f68d607bc03", + HYP / "WORDNET_STRUCTURAL_SOURCE_LIMITATION.json": "3c05cd9d6301fab0791e31b542d767cc757307cf3e304065362b479cc40e964a", + ROOT / "events.jsonl": "96b74a92d44f1cf9fe152b18e5207176f161ba3bfce528dac38aa4571a742f9c", + ROOT / "ledger.json": "77e22433203879b252f7a9e309d2013d7550101d1c4a014b494d2c96df87d0e0", + HYP / "HYPOTHESIS.json": "6500394d24543d1797eb9a2a05ba31868e035ffcbc7ae568f53116d4b016e62a", +} + +OUTPUTS = ( + "RESIDUAL_THRESHOLD_V1_ACCEPTANCE.json", + "RESIDUAL_THRESHOLD_V1_ARTIFACT_SCHEMA.json", + "RESIDUAL_THRESHOLD_V1_CALIBRATION_CONTRACT.json", + "RESIDUAL_THRESHOLD_V1_MEASUREMENT_CONTRACT.json", + "RESIDUAL_THRESHOLD_V1_PREREGISTRATION.json", + "RESIDUAL_THRESHOLD_V1_SELECTION_PROCEDURE.json", + "RESIDUAL_THRESHOLD_V1_SURFACE_ISOLATION_POLICY.json", +) + + +def _sha(path: Path) -> str: + return hashlib.sha256(path.read_bytes()).hexdigest() + + +def _write(path: Path, document: dict) -> str: + if path.exists(): + raise SystemExit(f"REFUSE: {path.name} already exists") + payload = (json.dumps(document, indent=2, sort_keys=True) + "\n").encode("utf-8") + fd = os.open(path, os.O_WRONLY | os.O_CREAT | os.O_EXCL, 0o600) + try: + os.write(fd, payload) + finally: + os.close(fd) + return hashlib.sha256(payload).hexdigest() + + +def main() -> None: + for path, expected in EXPECTED.items(): + got = _sha(path) + if got != expected: + raise SystemExit(f"REFUSE: {path.name} sha {got} != {expected}") + sealed = { + "constituent_resolution_replay_sha256": EXPECTED[SEM / "CONSTITUENT_SENSE_RESOLUTION_V1_REPLAY.jsonl"], + "constituent_resolution_spec_sha256": EXPECTED[SEM / "CONSTITUENT_SENSE_RESOLUTION_V1_SPEC.json"], + "development_evidence_sha256": EXPECTED[HYP / "DEVELOPMENT_EVIDENCE.json"], + "events_sha256": EXPECTED[ROOT / "events.jsonl"], + "integrated_resolution_sha256": EXPECTED[SEM / "INTEGRATED_CONSTITUENT_RESOLUTION_V1.jsonl"], + "km_candidate_decision_sha256": EXPECTED[SEM / "KM_CANDIDATE_DECISION.json"], + "ledger_sha256": EXPECTED[ROOT / "ledger.json"], + "magpie_candidate_decision_sha256": EXPECTED[SEM / "MAGPIE_CANDIDATE_DECISION.json"], + "model_based_wsd_projection_sha256": EXPECTED[SEM / "MODEL_BASED_WSD_READINESS_PROJECTION.json"], + "model_based_wsd_resolution_sha256": EXPECTED[SEM / "MODEL_BASED_WSD_RESOLUTION.jsonl"], + "model_based_wsd_spec_sha256": EXPECTED[SEM / "MODEL_BASED_WSD_CANDIDATE_SPEC.json"], + "original_residual_scores_sha256": EXPECTED[SEM / "RESIDUAL_DEVELOPMENT_SCORES.jsonl"], + "previous_tracker_sha256": EXPECTED[HYP / "HYPOTHESIS.json"], + "procedure_module_sha256": _sha(MODULE), + "procedure_v2_sha256": EXPECTED[HYP / "CLASSIFICATION_PROCEDURE.v2.json"], + "replay_analysis_sha256": EXPECTED[SEM / "RESIDUAL_V1_MODEL_RESOLVED_DEVELOPMENT_ANALYSIS.json"], + "replay_confound_sha256": EXPECTED[SEM / "RESIDUAL_V1_MODEL_RESOLVED_CONFOUND_ANALYSIS.json"], + "replay_decision_sha256": EXPECTED[SEM / "RESIDUAL_V1_MODEL_RESOLVED_CANDIDATE_DECISION.json"], + "replay_receipt_sha256": EXPECTED[SEM / "RESIDUAL_V1_MODEL_RESOLVED_REPLAY_RECEIPT.json"], + "replay_scores_sha256": EXPECTED[SEM / "RESIDUAL_V1_MODEL_RESOLVED_REPLAY_SCORES.jsonl"], + "residual_candidate_spec_sha256": EXPECTED[SEM / "RESIDUAL_CANDIDATE_SPEC.json"], + "wordnet_limitation_sha256": EXPECTED[HYP / "WORDNET_STRUCTURAL_SOURCE_LIMITATION.json"], + } + first = documents(sealed, {}) + sibling = {} + for name in OUTPUTS: + if name.endswith("PREREGISTRATION.json"): + continue + sibling[name] = _write(SEM / name, first[name]) + second = documents(sealed, sibling) + sibling["RESIDUAL_THRESHOLD_V1_PREREGISTRATION.json"] = _write( + SEM / "RESIDUAL_THRESHOLD_V1_PREREGISTRATION.json", + second["RESIDUAL_THRESHOLD_V1_PREREGISTRATION.json"], + ) + tracker_path = HYP / "HYPOTHESIS.json" + tracker = json.loads(tracker_path.read_text(encoding="utf-8")) + if tracker["residual_model_resolved_state"] != "RESIDUAL_DEVELOPMENT_ANALYZED_V2": + raise SystemExit("REFUSE: residual development state changed") + if tracker["residual_model_resolved_candidate_status"] != "CANDIDATE_PROMISING": + raise SystemExit("REFUSE: residual candidate status changed") + if tracker["selected_source"] != "none" or tracker["admitted"] != 0 or tracker["settled"] != 0 or tracker["gold"] != 0: + raise SystemExit("REFUSE: source or settlement state changed") + tracker["previous_tracker_sha256"] = EXPECTED[tracker_path] + tracker["residual_threshold_rule"] = RULE + tracker["residual_threshold_state"] = TRACK_STATE + tracker["residual_threshold_value"] = None + tracker["residual_threshold"] = None + tracker["residual_threshold_calibration_surface_drawn"] = False + tracker["residual_threshold_measurement_surface_drawn"] = False + tracker["residual_threshold_frozen"] = False + tracker["residual_threshold_eligible"] = False + tracker["development_rows_reusable_for_threshold_selection"] = False + tracker["residual_threshold_json_schema_document"] = None + tracker["next_legal_transition"] = "RESIDUAL_CALIBRATION_SURFACE_DRAW_AUTHORIZATION" + tracker["next_transition_authorized"] = False + tracker["residual_threshold_preregistration_sha256"] = sibling["RESIDUAL_THRESHOLD_V1_PREREGISTRATION.json"] + tracker["residual_threshold_acceptance_sha256"] = sibling["RESIDUAL_THRESHOLD_V1_ACCEPTANCE.json"] + tracker["residual_threshold_selection_procedure_sha256"] = sibling["RESIDUAL_THRESHOLD_V1_SELECTION_PROCEDURE.json"] + tracker["residual_threshold_calibration_contract_sha256"] = sibling["RESIDUAL_THRESHOLD_V1_CALIBRATION_CONTRACT.json"] + tracker["residual_threshold_measurement_contract_sha256"] = sibling["RESIDUAL_THRESHOLD_V1_MEASUREMENT_CONTRACT.json"] + tracker["residual_threshold_artifact_schema_sha256"] = sibling["RESIDUAL_THRESHOLD_V1_ARTIFACT_SCHEMA.json"] + tracker["residual_threshold_isolation_policy_sha256"] = sibling["RESIDUAL_THRESHOLD_V1_SURFACE_ISOLATION_POLICY.json"] + payload = (json.dumps(tracker, indent=2, sort_keys=True) + "\n").encode("utf-8") + tmp = tracker_path.with_suffix(".json.tmp") + fd = os.open(tmp, os.O_WRONLY | os.O_CREAT | os.O_EXCL, 0o600) + try: + os.write(fd, payload) + finally: + os.close(fd) + os.replace(tmp, tracker_path) + os.chmod(tracker_path, 0o600) + if _sha(ROOT / "events.jsonl") != EXPECTED[ROOT / "events.jsonl"]: + raise SystemExit("REFUSE: events changed") + if _sha(ROOT / "ledger.json") != EXPECTED[ROOT / "ledger.json"]: + raise SystemExit("REFUSE: ledger changed") + for path, expected in EXPECTED.items(): + if path.name == "HYPOTHESIS.json": + continue + if _sha(path) != expected: + raise SystemExit(f"REFUSE: {path.name} changed during freeze") + print("tracker", hashlib.sha256(payload).hexdigest()) + for name in OUTPUTS: + print(name, sibling[name]) + + +if __name__ == "__main__": + main() diff --git a/scripts/shadow/hyperlexical/residual_threshold_v2.py b/scripts/shadow/hyperlexical/residual_threshold_v2.py new file mode 100644 index 00000000..359a5847 --- /dev/null +++ b/scripts/shadow/hyperlexical/residual_threshold_v2.py @@ -0,0 +1,249 @@ +"""Preregister threshold v2. This module does not draw a surface. + +v1 stays closed. The acceptance gates are the v1 gates. The only new +design choice is the label-blind raw-row quota, taken from a readiness +rate below the lower observed coverage, not from class labels. +""" + +from __future__ import annotations + +from decimal import Decimal, ROUND_CEILING, ROUND_HALF_EVEN + +from hyperlexical.residual_threshold_v1 import ( + ALGORITHM_ID, + MIN_CALIBRATION_HIGH, + MIN_CALIBRATION_SECONDARY, + MIN_MEASUREMENT_HIGH, + MIN_MEASUREMENT_PREDICTED_YES, + MIN_MEASUREMENT_SECONDARY, + MIN_PREDICTED_YES, + MEASUREMENT_PRECISION_FLOOR, + MEASUREMENT_WILSON_LOWER_FLOOR, + PRECISION_FLOOR, + QUARANTINE_PREDICTED_YES_MAX, + REJECT_PREDICTED_YES_MAX, + WILSON_LOWER_FLOOR, + WILSON_Z95, +) + +RULE = "RUNE.SEMANTIC_COMPOSITIONALITY_THRESHOLD.v2" +TRACK_STATE = "THRESHOLD_PROCEDURE_PREREGISTERED" +NEXT_TRANSITION = "THRESHOLD_V2_SURFACE_DRAW_AUTHORIZATION" +SAMPLING_RULE_ID = "positional_stratified_hash_round_robin_quota_v2" + +# Sum of the unchanged ready-HIGH and ready-SECONDARY floors. Not a new gate. +REQUIRED_TARGET_READY = MIN_CALIBRATION_HIGH + MIN_CALIBRATION_SECONDARY +# Below the lower observed readiness, 6/27. The development rate 73/225 is not used. +CONSERVATIVE_READINESS = Decimal("0.20") +SAFETY_FACTOR = Decimal("2.0") + +V1_EXECUTION_STATE = "CALIBRATION_INSUFFICIENT_SUPPORT" +V1_DISPOSITION = "CLOSED_CALIBRATION_DESIGN_INSUFFICIENT" +FAILURE_STATES = ( + "CALIBRATION_INSUFFICIENT_SUPPORT", + "NO_DIRECTIONAL_SIGNAL", + "CALIBRATION_CONFOUND_REVIEW", + "NO_THRESHOLD_PASSES_PRECISION_GATE", +) +QUOTA_UNFILLED = "SURFACE_QUOTA_UNFILLED" + + +def quota_from_readiness(required_ready: int = REQUIRED_TARGET_READY) -> int: + """ceil(required_ready / conservative_readiness * safety_factor).""" + value = (Decimal(required_ready) / CONSERVATIVE_READINESS) * SAFETY_FACTOR + return int(value.to_integral_value(rounding=ROUND_CEILING)) + + +def calibration_draw_size() -> int: + return quota_from_readiness() + + +def measurement_draw_size() -> int: + return quota_from_readiness() + + +def _common() -> dict: + return { + "admitted": 0, + "calibration_draw_size": calibration_draw_size(), + "calibration_surface_drawn": False, + "development_rows_reusable_for_threshold_selection": False, + "gold": 0, + "json_schema_document": None, + "json_schema_exists": False, + "measurement_draw_size": measurement_draw_size(), + "measurement_eligible": False, + "measurement_surface_drawn": False, + "next_legal_transition": NEXT_TRANSITION, + "next_transition_authorized": False, + "no_output": "NO", + "output_labels": ["YES", "UNKNOWN"], + "redraw_authorized": False, + "rule": RULE, + "runtime_integration": False, + "select_005_authorized": False, + "selected_source": "none", + "selection_algorithm_id": ALGORITHM_ID, + "settled": 0, + "state": TRACK_STATE, + "this_pass_draws_surfaces": False, + "this_pass_selects_threshold": False, + "threshold_frozen": False, + "threshold_value": None, + "v1_disposition": V1_DISPOSITION, + "v1_execution_state": V1_EXECUTION_STATE, + "v1_redraw_authorized": False, + "v1_threshold_value": None, + } + + +def sample_size_document() -> dict: + lower = Decimal(6) / Decimal(27) + return { + **_common(), + "conservative_readiness": str(CONSERVATIVE_READINESS), + "development_readiness_not_used": "73/225", + "formula": "ceil(required_target_ready / conservative_readiness * safety_factor)", + "frozen_rate_below_lower_observed": CONSERVATIVE_READINESS < lower, + "lower_observed_readiness": "6/27", + "lower_observed_readiness_display": str(lower.quantize(Decimal("0.0001"), rounding=ROUND_HALF_EVEN)), + "quotas_are_separate": True, + "readiness_inputs_are_label_independent": True, + "required_target_ready": REQUIRED_TARGET_READY, + "safety_factor": str(SAFETY_FACTOR), + "schema": "hyperlex.residual_threshold_v2_sample_size.v1", + "unused_for_quota": [ + "HIGH_prevalence", + "SECONDARY_prevalence", + "residual_distribution", + "AUC", + "threshold_performance", + ], + } + + +def calibration_contract() -> dict: + return { + **_common(), + "draw_seed": None, + "failure_states": list(FAILURE_STATES), + "full_manifest_frozen_before_resolution": True, + "labels_joined_only_after_score_hash": True, + "ordering": "normalized_text_sha256 ascending within cell", + "quota_unfilled_state": QUOTA_UNFILLED, + "replacement": "without_replacement", + "sampling_rule_id": SAMPLING_RULE_ID, + "schema": "hyperlex.residual_threshold_v2_calibration_contract.v1", + "stop_when_support_reached": False, + "stratification": "source_pos x token_count", + "support_floors": { + "precision": str(PRECISION_FLOOR), + "predicted_yes": MIN_PREDICTED_YES, + "quarantine_yes": QUARANTINE_PREDICTED_YES_MAX, + "ready_high": MIN_CALIBRATION_HIGH, + "ready_secondary": MIN_CALIBRATION_SECONDARY, + "reject_yes": REJECT_PREDICTED_YES_MAX, + "wilson_lower": str(WILSON_LOWER_FLOOR), + "wilson_z": str(WILSON_Z95), + }, + "visit_order": "round-robin across cells sorted by source_pos then token_count; skip exhausted cells", + } + + +def measurement_contract() -> dict: + return { + **_common(), + "acceptance_floors": { + "precision": str(MEASUREMENT_PRECISION_FLOOR), + "predicted_yes": MIN_MEASUREMENT_PREDICTED_YES, + "quarantine_yes": QUARANTINE_PREDICTED_YES_MAX, + "ready_high": MIN_MEASUREMENT_HIGH, + "ready_secondary": MIN_MEASUREMENT_SECONDARY, + "reject_yes": REJECT_PREDICTED_YES_MAX, + "wilson_lower": str(MEASUREMENT_WILSON_LOWER_FLOOR), + }, + "partitioned_with_calibration": True, + "schema": "hyperlex.residual_threshold_v2_measurement_contract.v1", + "sealed_before_calibration_labels": True, + "threshold_refit_prohibited": True, + "unresolved_until_later_authorization": True, + } + + +def selection_procedure() -> dict: + return { + **_common(), + "failure_states": list(FAILURE_STATES), + "gates_changed_from_v1": False, + "inadequate_support_authorizes_redraw": False, + "schema": "hyperlex.residual_threshold_v2_selection_procedure.v1", + "threshold_count": 1, + "threshold_scope": "one_global_T_HIGH", + } + + +def isolation_policy() -> dict: + return { + **_common(), + "excluded_surfaces": [ + "development_225", + "v1_calibration_manifest", + ], + "identity_fences": [ + "normalized_text_sha256", + "normalized surface text", + "PWN 3.0 synset key when the offset is non-empty", + ], + "same_synset_on_two_surfaces": False, + "schema": "hyperlex.residual_threshold_v2_surface_isolation.v1", + "v1_labels_and_scores_not_read_for_selection": True, + "v2_calibration_and_measurement_disjoint": True, + } + + +def acceptance() -> dict: + return { + **_common(), + "gates_lowered": False, + "schema": "hyperlex.residual_threshold_v2_acceptance.v1", + } + + +def artifact_schema() -> dict: + return { + **_common(), + "document_kind": "artifact_contract", + "schema": "hyperlex.residual_threshold_v2_artifact_contract.v1", + "schema_note": "No JSON Schema document exists for this threshold family. This contract names the fields. It is not a JSON Schema.", + } + + +def preregistration(sibling_sha256: dict[str, str]) -> dict: + return { + **_common(), + "kept_from_v1": [ + "tier1_tier2_tier3_resolver", + "glossbert_revision_and_abstention", + "minilm_residual", + "normalized_mean_v1", + "one_minus_cosine", + "yes_unknown_only", + "precision_gated_recall", + "development_exclusion_fences", + "no_redraw_after_labels", + ], + "schema": "hyperlex.residual_threshold_v2_preregistration.v1", + "sibling_artifact_sha256": sibling_sha256, + } + + +def companion_documents() -> dict[str, dict]: + return { + "RESIDUAL_THRESHOLD_V2_ACCEPTANCE.json": acceptance(), + "RESIDUAL_THRESHOLD_V2_ARTIFACT_SCHEMA.json": artifact_schema(), + "RESIDUAL_THRESHOLD_V2_CALIBRATION_CONTRACT.json": calibration_contract(), + "RESIDUAL_THRESHOLD_V2_MEASUREMENT_CONTRACT.json": measurement_contract(), + "RESIDUAL_THRESHOLD_V2_SAMPLE_SIZE.json": sample_size_document(), + "RESIDUAL_THRESHOLD_V2_SELECTION_PROCEDURE.json": selection_procedure(), + "RESIDUAL_THRESHOLD_V2_SURFACE_ISOLATION_POLICY.json": isolation_policy(), + } diff --git a/specs/007-hyperlexical-model/schemas/hyperlex/semantic-evidence-v2/evidence-record.schema.json b/specs/007-hyperlexical-model/schemas/hyperlex/semantic-evidence-v2/evidence-record.schema.json new file mode 100644 index 00000000..9d8a4709 --- /dev/null +++ b/specs/007-hyperlexical-model/schemas/hyperlex/semantic-evidence-v2/evidence-record.schema.json @@ -0,0 +1,343 @@ +{ + "title": "Semantic evidence record", + "type": "object", + "additionalProperties": false, + "required": [ + "evidence_status", + "features", + "output", + "rule" + ], + "properties": { + "evidence_status": { + "$ref": "common.schema.json#/$defs/evidence_status" + }, + "features": { + "type": [ + "array", + "null" + ] + }, + "output": { + "$ref": "common.schema.json#/$defs/output_label" + }, + "rule": { + "const": "RUNE.SEMANTIC_COMPOSITIONALITY_EVIDENCE.v2" + } + }, + "allOf": [ + { + "if": { + "properties": { + "evidence_status": { + "const": "UNKNOWN" + } + }, + "required": [ + "evidence_status" + ] + }, + "then": { + "properties": { + "features": { + "type": "null" + }, + "output": { + "const": "UNKNOWN" + } + } + } + }, + { + "if": { + "properties": { + "evidence_status": { + "const": "READY" + } + }, + "required": [ + "evidence_status" + ] + }, + "then": { + "properties": { + "features": { + "type": "array", + "minItems": 13, + "maxItems": 13, + "items": false, + "prefixItems": [ + { + "type": "object", + "additionalProperties": false, + "required": [ + "control", + "name", + "value" + ], + "properties": { + "control": { + "const": false + }, + "name": { + "const": "whole_vs_constituent_mean_cosine" + }, + "value": { + "$ref": "common.schema.json#/$defs/decimal_feature" + } + } + }, + { + "type": "object", + "additionalProperties": false, + "required": [ + "control", + "name", + "value" + ], + "properties": { + "control": { + "const": false + }, + "name": { + "const": "whole_vs_constituent_mean_residual" + }, + "value": { + "$ref": "common.schema.json#/$defs/decimal_feature" + } + } + }, + { + "type": "object", + "additionalProperties": false, + "required": [ + "control", + "name", + "value" + ], + "properties": { + "control": { + "const": false + }, + "name": { + "const": "whole_vs_each_constituent_min_similarity" + }, + "value": { + "$ref": "common.schema.json#/$defs/decimal_feature" + } + } + }, + { + "type": "object", + "additionalProperties": false, + "required": [ + "control", + "name", + "value" + ], + "properties": { + "control": { + "const": false + }, + "name": { + "const": "whole_vs_each_constituent_max_similarity" + }, + "value": { + "$ref": "common.schema.json#/$defs/decimal_feature" + } + } + }, + { + "type": "object", + "additionalProperties": false, + "required": [ + "control", + "name", + "value" + ], + "properties": { + "control": { + "const": false + }, + "name": { + "const": "whole_vs_each_constituent_mean_similarity" + }, + "value": { + "$ref": "common.schema.json#/$defs/decimal_feature" + } + } + }, + { + "type": "object", + "additionalProperties": false, + "required": [ + "control", + "name", + "value" + ], + "properties": { + "control": { + "const": false + }, + "name": { + "const": "whole_vs_each_constituent_similarity_variance" + }, + "value": { + "$ref": "common.schema.json#/$defs/decimal_feature" + } + } + }, + { + "type": "object", + "additionalProperties": false, + "required": [ + "control", + "name", + "value" + ], + "properties": { + "control": { + "const": false + }, + "name": { + "const": "constituent_pair_mean_similarity" + }, + "value": { + "$ref": "common.schema.json#/$defs/decimal_feature" + } + } + }, + { + "type": "object", + "additionalProperties": false, + "required": [ + "control", + "name", + "value" + ], + "properties": { + "control": { + "const": false + }, + "name": { + "const": "constituent_pair_min_similarity" + }, + "value": { + "$ref": "common.schema.json#/$defs/decimal_feature" + } + } + }, + { + "type": "object", + "additionalProperties": false, + "required": [ + "control", + "name", + "value" + ], + "properties": { + "control": { + "const": false + }, + "name": { + "const": "constituent_pair_variance" + }, + "value": { + "$ref": "common.schema.json#/$defs/decimal_feature" + } + } + }, + { + "type": "object", + "additionalProperties": false, + "required": [ + "control", + "name", + "value" + ], + "properties": { + "control": { + "const": false + }, + "name": { + "const": "whole_gloss_vs_composed_gloss_similarity" + }, + "value": { + "$ref": "common.schema.json#/$defs/decimal_feature" + } + } + }, + { + "type": "object", + "additionalProperties": false, + "required": [ + "control", + "name", + "value" + ], + "properties": { + "control": { + "const": false + }, + "name": { + "const": "whole_lemma_vs_composed_constituent_similarity" + }, + "value": { + "$ref": "common.schema.json#/$defs/decimal_feature" + } + } + }, + { + "type": "object", + "additionalProperties": false, + "required": [ + "control", + "name", + "value" + ], + "properties": { + "control": { + "const": true + }, + "name": { + "const": "constituent_count" + }, + "value": { + "type": "integer", + "minimum": 0 + } + } + }, + { + "type": "object", + "additionalProperties": false, + "required": [ + "control", + "name", + "value" + ], + "properties": { + "control": { + "const": true + }, + "name": { + "const": "token_count" + }, + "value": { + "type": "integer", + "minimum": 0 + } + } + } + ] + }, + "output": { + "const": "UNKNOWN" + } + } + } + } + ], + "$schema": "https://json-schema.org/draft/2020-12/schema", + "$id": "https://hyperlex.local/schemas/hyperlex/semantic-evidence-v2/evidence-record.schema.json" +} diff --git a/specs/007-hyperlexical-model/schemas/residual-threshold/CONFORMANCE.json b/specs/007-hyperlexical-model/schemas/residual-threshold/CONFORMANCE.json new file mode 100644 index 00000000..8e41aa96 --- /dev/null +++ b/specs/007-hyperlexical-model/schemas/residual-threshold/CONFORMANCE.json @@ -0,0 +1,305 @@ +{ + "authorization": "THRESHOLD_ARTIFACT_SCHEMA_HARDENING_AUTHORIZATION", + "calibration_evidence_modified": false, + "calibration_state": "NO_THRESHOLD_PASSES_PRECISION_GATE", + "changed_artifacts": [], + "json_schema_family": [ + "residual-threshold-completion-receipt.schema.json", + "residual-threshold-confound-analysis.schema.json", + "residual-threshold-decision.schema.json", + "residual-threshold-direction-analysis.schema.json", + "residual-threshold-receipt.schema.json", + "residual-threshold-support-join.schema.json", + "residual-threshold-surface-manifest.schema.json", + "residual-threshold-threshold-search.schema.json", + "residual-threshold-common.schema.json" + ], + "outcome": "PASS", + "results": [ + { + "artifact": "RESIDUAL_THRESHOLD_V1_CALIBRATION_DESIGN_CLOSURE.json", + "errors": [], + "schema": "residual-threshold-decision.schema.json", + "status": "PASS" + }, + { + "artifact": "RESIDUAL_THRESHOLD_V1_CALIBRATION_DRAW_RECEIPT.json", + "errors": [], + "schema": "residual-threshold-receipt.schema.json", + "status": "PASS" + }, + { + "artifact": "RESIDUAL_THRESHOLD_V1_CALIBRATION_FAILURE.json", + "errors": [], + "schema": "residual-threshold-decision.schema.json", + "status": "PASS" + }, + { + "artifact": "RESIDUAL_THRESHOLD_V1_CALIBRATION_MANIFEST.jsonl", + "errors": [], + "schema": "residual-threshold-surface-manifest.schema.json", + "status": "PASS" + }, + { + "artifact": "RESIDUAL_THRESHOLD_V1_CALIBRATION_SCORE_RECEIPT.json", + "errors": [], + "schema": "residual-threshold-receipt.schema.json", + "status": "PASS" + }, + { + "artifact": "RESIDUAL_THRESHOLD_V1_CALIBRATION_THRESHOLD_SEARCH.json", + "errors": [], + "schema": "residual-threshold-threshold-search.schema.json", + "status": "PASS" + }, + { + "artifact": "RESIDUAL_THRESHOLD_V2_BLIND_OPERATOR_PACKET_RECEIPT.json", + "errors": [], + "schema": "residual-threshold-receipt.schema.json", + "status": "PASS" + }, + { + "artifact": "RESIDUAL_THRESHOLD_V2_CALIBRATION_COMPLETION_FAILURE.json", + "errors": [], + "schema": "residual-threshold-decision.schema.json", + "status": "PASS" + }, + { + "artifact": "RESIDUAL_THRESHOLD_V2_CALIBRATION_COMPLETION_RECEIPT.json", + "errors": [], + "schema": "residual-threshold-completion-receipt.schema.json", + "status": "PASS" + }, + { + "artifact": "RESIDUAL_THRESHOLD_V2_CALIBRATION_CONFOUND_ANALYSIS.json", + "errors": [], + "schema": "residual-threshold-confound-analysis.schema.json", + "status": "PASS" + }, + { + "artifact": "RESIDUAL_THRESHOLD_V2_CALIBRATION_FAILURE.json", + "errors": [], + "schema": "residual-threshold-decision.schema.json", + "status": "PASS" + }, + { + "artifact": "RESIDUAL_THRESHOLD_V2_CALIBRATION_MANIFEST.jsonl", + "errors": [], + "schema": "residual-threshold-surface-manifest.schema.json", + "status": "PASS" + }, + { + "artifact": "RESIDUAL_THRESHOLD_V2_CALIBRATION_RESOLUTION_RECEIPT.json", + "errors": [], + "schema": "residual-threshold-receipt.schema.json", + "status": "PASS" + }, + { + "artifact": "RESIDUAL_THRESHOLD_V2_CALIBRATION_SCORE_RECEIPT.json", + "errors": [], + "schema": "residual-threshold-receipt.schema.json", + "status": "PASS" + }, + { + "artifact": "RESIDUAL_THRESHOLD_V2_CALIBRATION_SUPPORT_JOIN.jsonl", + "errors": [], + "schema": "residual-threshold-support-join.schema.json", + "status": "PASS" + }, + { + "artifact": "RESIDUAL_THRESHOLD_V2_CALIBRATION_SUPPORT_JOIN_RECEIPT.json", + "errors": [], + "schema": "residual-threshold-receipt.schema.json", + "status": "PASS" + }, + { + "artifact": "RESIDUAL_THRESHOLD_V2_CALIBRATION_SUPPORT_REASSESSMENT.json", + "errors": [], + "schema": "residual-threshold-decision.schema.json", + "status": "PASS" + }, + { + "artifact": "RESIDUAL_THRESHOLD_V2_COMPLETION_THRESHOLD_SEARCH.json", + "errors": [], + "schema": "residual-threshold-threshold-search.schema.json", + "status": "PASS" + }, + { + "artifact": "RESIDUAL_THRESHOLD_V2_CONFOUND_ANALYSIS.json", + "errors": [], + "schema": "residual-threshold-confound-analysis.schema.json", + "status": "PASS" + }, + { + "artifact": "RESIDUAL_THRESHOLD_V2_CONFOUND_DECISION.json", + "errors": [], + "schema": "residual-threshold-decision.schema.json", + "status": "PASS" + }, + { + "artifact": "RESIDUAL_THRESHOLD_V2_DIRECTION_ANALYSIS.json", + "errors": [], + "schema": "residual-threshold-direction-analysis.schema.json", + "status": "PASS" + }, + { + "artifact": "RESIDUAL_THRESHOLD_V2_DIRECTION_DECISION.json", + "errors": [], + "schema": "residual-threshold-decision.schema.json", + "status": "PASS" + }, + { + "artifact": "RESIDUAL_THRESHOLD_V2_MEASUREMENT_MANIFEST.jsonl", + "errors": [], + "schema": "residual-threshold-surface-manifest.schema.json", + "status": "PASS" + }, + { + "artifact": "RESIDUAL_THRESHOLD_V2_OPERATOR_LABELING_RECEIPT.json", + "errors": [], + "schema": "residual-threshold-receipt.schema.json", + "status": "PASS" + }, + { + "artifact": "RESIDUAL_THRESHOLD_V2_SURFACE_DRAW_RECEIPT.json", + "errors": [], + "schema": "residual-threshold-receipt.schema.json", + "status": "PASS" + }, + { + "artifact": "RESIDUAL_THRESHOLD_V2_THRESHOLD_SEARCH.json", + "errors": [], + "schema": "residual-threshold-threshold-search.schema.json", + "status": "PASS" + } + ], + "schema_conformance_gaps": [], + "scientific_remediation": false, + "semantic": [ + { + "check": "support_join_rows", + "detail": "rows=200", + "status": "PASS" + }, + { + "check": "ready_plus_unknown", + "detail": "ready=60 unknown=140", + "status": "PASS" + }, + { + "check": "row_id_sets", + "detail": "manifest=200 labels=200 join=200", + "status": "PASS" + }, + { + "check": "measurement_disjoint", + "detail": "measurement_rows=200", + "status": "PASS" + }, + { + "check": "measurement_manifest_unchanged", + "detail": "78ca09ab14912681d028e8b9b1c77a8daf1d0c8c81561434eb9b6ade45a753e5", + "status": "PASS" + }, + { + "check": "join_receipt_manifest_hash", + "detail": "34bc70b93039fc6a5ba4bb58685e0fbfe6f91dd1f86a64fec8326a84013a6465", + "status": "PASS" + }, + { + "check": "join_receipt_label_hash", + "detail": "f9901e334543dac2e9bf258208097cf4c9b48af9dc8b497ff42fe4f1f106e1fc", + "status": "PASS" + }, + { + "check": "join_receipt_score_hash", + "detail": "01c758af570264c190f4af7805d43d734c9e9a36779f0e0b82675709f213e07c", + "status": "PASS" + }, + { + "check": "direction_join_hash", + "detail": "02088379b5ebe0f26106e830a730e8a90b2281d83c37bf1e750e1846bffa128b", + "status": "PASS" + }, + { + "check": "operator_labels_closed", + "detail": "labels stay inside the emitted enum", + "status": "PASS" + }, + { + "check": "v1_labels_unlabeled", + "detail": "rows=27", + "status": "PASS" + }, + { + "check": "v1_manifest_identity", + "detail": "rows=27", + "status": "PASS" + }, + { + "check": "v2_manifest_identity", + "detail": "row_id matches normalized_text_sha256", + "status": "PASS" + }, + { + "check": "draw_receipt_measurement_hash", + "detail": "78ca09ab14912681d028e8b9b1c77a8daf1d0c8c81561434eb9b6ade45a753e5", + "status": "PASS" + }, + { + "check": "draw_receipt_manifest_hash", + "detail": "34bc70b93039fc6a5ba4bb58685e0fbfe6f91dd1f86a64fec8326a84013a6465", + "status": "PASS" + }, + { + "check": "events_unchanged", + "detail": "96b74a92d44f1cf9fe152b18e5207176f161ba3bfce528dac38aa4571a742f9c", + "status": "PASS" + }, + { + "check": "ledger_unchanged", + "detail": "77e22433203879b252f7a9e309d2013d7550101d1c4a014b494d2c96df87d0e0", + "status": "PASS" + }, + { + "check": "tracker_unchanged", + "detail": "e674c207d9eb160159a673c23f7e2a2e8262b5bfc963ccf81b216d8291dc4b0f", + "status": "PASS" + } + ], + "semantic_conformance_gaps": [], + "skipped_not_applicable": [ + "RESIDUAL_THRESHOLD_V1_ACCEPTANCE.json", + "RESIDUAL_THRESHOLD_V1_ARTIFACT_SCHEMA.json", + "RESIDUAL_THRESHOLD_V1_CALIBRATION_ANALYSIS.json", + "RESIDUAL_THRESHOLD_V1_CALIBRATION_CONTRACT.json", + "RESIDUAL_THRESHOLD_V1_CALIBRATION_ISOLATION_REPORT.json", + "RESIDUAL_THRESHOLD_V1_CALIBRATION_LABELS.jsonl", + "RESIDUAL_THRESHOLD_V1_CALIBRATION_LABEL_AVAILABILITY_REVIEW.json", + "RESIDUAL_THRESHOLD_V1_CALIBRATION_RESOLUTION.jsonl", + "RESIDUAL_THRESHOLD_V1_CALIBRATION_SCORES.jsonl", + "RESIDUAL_THRESHOLD_V1_MEASUREMENT_CONTRACT.json", + "RESIDUAL_THRESHOLD_V1_PREREGISTRATION.json", + "RESIDUAL_THRESHOLD_V1_SELECTION_PROCEDURE.json", + "RESIDUAL_THRESHOLD_V1_SURFACE_ISOLATION_POLICY.json", + "RESIDUAL_THRESHOLD_V2_ACCEPTANCE.json", + "RESIDUAL_THRESHOLD_V2_ARTIFACT_SCHEMA.json", + "RESIDUAL_THRESHOLD_V2_BLIND_OPERATOR_PACKET.jsonl", + "RESIDUAL_THRESHOLD_V2_CALIBRATION_ANALYSIS.json", + "RESIDUAL_THRESHOLD_V2_CALIBRATION_CONTRACT.json", + "RESIDUAL_THRESHOLD_V2_CALIBRATION_LABELS.jsonl", + "RESIDUAL_THRESHOLD_V2_CALIBRATION_LABEL_AVAILABILITY_REVIEW.json", + "RESIDUAL_THRESHOLD_V2_CALIBRATION_RESOLUTION.jsonl", + "RESIDUAL_THRESHOLD_V2_CALIBRATION_SCORES.jsonl", + "RESIDUAL_THRESHOLD_V2_MEASUREMENT_CONTRACT.json", + "RESIDUAL_THRESHOLD_V2_OPERATOR_LABELS.jsonl", + "RESIDUAL_THRESHOLD_V2_PREREGISTRATION.json", + "RESIDUAL_THRESHOLD_V2_SAMPLE_SIZE.json", + "RESIDUAL_THRESHOLD_V2_SELECTION_PROCEDURE.json", + "RESIDUAL_THRESHOLD_V2_SURFACE_DISTRIBUTION_REPORT.json", + "RESIDUAL_THRESHOLD_V2_SURFACE_ISOLATION_POLICY.json", + "RESIDUAL_THRESHOLD_V2_SURFACE_ISOLATION_REPORT.json" + ], + "threshold_redesign": false, + "training_authorized": false +} diff --git a/tests/shadow/test_select_005_completion.py b/tests/shadow/test_select_005_completion.py new file mode 100644 index 00000000..5c2217fd --- /dev/null +++ b/tests/shadow/test_select_005_completion.py @@ -0,0 +1,200 @@ +"""SELECT-005 completion: seal the control recipe, do not invent floors or a corpus.""" + +import ast +from pathlib import Path + +import pytest + +from hyperlexical.select_005_completion import ( + BEST_NAME, + EXHAUSTED_STREAM_RUN, + PREREGISTRATION_SEALED, + THRESHOLDS_BLOCKED, + TRANSITION, + WARM_START_NAME, + completion_report, + decision_only_exclusion, + exposure_snapshot_refusal, + fresh_census, + initialization_decision, + label_blind_surface, + sealed_preregistration, + source_contract, + threshold_authorization_proposal, +) +from hyperlexical.select_005_reserve import canonical_json + +MODULE = Path(__file__).resolve().parents[2] / "scripts/shadow/hyperlexical/select_005_completion.py" + + +def test_initialization_is_morph65_not_the_current_best(): + decision = initialization_decision() + assert decision["decision"] == "morph65_new_schedule" + assert decision["rejected_alternative"] == "select004_best_new_schedule" + assert decision["warm_start_name"] == WARM_START_NAME + assert decision["best_name"] == BEST_NAME + assert decision["best_not_moved"] is True + assert decision["trainer_default_did_not_decide"] is True + + +def test_preregistration_seals_the_control_recipe_without_select_004_inheritance(): + sealed = sealed_preregistration() + assert sealed["state"] == PREREGISTRATION_SEALED + assert sealed["sealed"] is True + assert sealed["unset_fields"] == [] + assert sealed["scientific_variable"] == "train_schedule" + held = sealed["held_constant"] + assert held["learning_rate"] == "2e-5" + assert held["batch_size"] == 8 + assert held["gradient_accumulation"] == 1 + assert held["seed"] is None + assert held["seed_policy"] == "HLX_SEED_UNSET_FROZEN" + authority = sealed["held_constant_authority"] + assert authority["inherited_from_select_004"] is False + assert authority["trainer_default_did_not_decide"] is True + assert authority["same_numeric_optimizer_values_as_select_004_candidate_env"] is True + assert authority["select_004_scientific_variable_not_transferred"] == "HLX_SELECT_METRIC" + assert sealed["initialization"]["decision"] == "morph65_new_schedule" + assert sealed["training_authorized"] is False + assert sealed["training_launch_authorized"] is False + + +def test_threshold_proposal_stays_unsealed(): + proposal = threshold_authorization_proposal() + assert proposal["sealed"] is False + assert proposal["state"] == THRESHOLDS_BLOCKED + assert proposal["decision_thresholds"] == {} + assert proposal["inherited_from_select_004"] is False + assert proposal["comparison_baseline"] == "SELECT-005 control arm" + assert proposal["not_seed_morph78"] is True + assert all(item["numeric_threshold"] is None for item in proposal["metrics"]) + assert [item["metric"] for item in proposal["metrics"]] == [ + "classify_macro_f1_nonnone", + "classification_accuracy", + "observed_label_accuracy", + "unbind_clean_exact", + ] + + +def test_exhausted_stream_is_not_a_source_and_a_decision_only_row_has_no_slice(): + contract = source_contract() + assert contract["exhausted_stream_is_a_source"] is False + assert contract["wordnet"]["census_rights_token"] == "not CC-BY-SA" + assert contract["wordnet"]["admitted_alone"] is False + with pytest.raises(SystemExit, match="exhausted settlement universe"): + fresh_census( + [ + { + "decision": "ACCEPT", + "exhausted_universe": True, + "provenance": "old", + "rights": "CC-BY-SA", + "row_id": "old", + "source_run": EXHAUSTED_STREAM_RUN, + "text_hash": "ab" * 32, + } + ], + {}, + set(), + ) + event = { + "decision": "ACCEPT", + "provenance": "fresh", + "rights": "CC-BY-SA", + "row_id": "new", + "source_run": "hs-new", + "text_hash": "cd" * 32, + } + assert decision_only_exclusion(event) == "SLICE_LABELS_ABSENT" + censused = fresh_census([event], {}, set()) + assert censused["failure"] == "RESERVE_QUOTA_UNFILLED" + assert censused["reserve_row_count"] == 0 + assert censused["exhausted_universe_consulted"] is False + assert censused["source_universe_count"] == 1 + + +def test_stale_snapshot_refuses_and_the_empty_surface_is_label_blind(): + refusal = exposure_snapshot_refusal({"vern": 71.7, "boxhash": 71.7}) + assert refusal is not None + assert "71.7h" in refusal + assert exposure_snapshot_refusal({"vern": 1.0, "boxhash": 1.0}) is None + assert exposure_snapshot_refusal({}) == "REFUSE: no box exposure snapshot" + kept = label_blind_surface( + [ + { + "license": "CC BY-SA 3.0", + "row_id": "hs-fresh-example", + "source_type": "wiktionary_category", + "source_url": "https://en.wiktionary.org/wiki/Example", + "text_hash": "ef" * 32, + } + ] + ) + assert kept == [ + { + "license": "CC BY-SA 3.0", + "row_id": "hs-fresh-example", + "source_type": "wiktionary_category", + "source_url": "https://en.wiktionary.org/wiki/Example", + "text_hash": "ef" * 32, + } + ] + with pytest.raises(SystemExit, match="label-blind surface carries task"): + label_blind_surface( + [ + { + "license": "CC-BY-SA", + "row_id": "labeled", + "source_type": "wikipedia_prose", + "source_url": "https://en.wikipedia.org/wiki/Example", + "task": "classify", + "text_hash": "11" * 32, + } + ] + ) + with pytest.raises(SystemExit, match="not rights-cleared"): + label_blind_surface( + [ + { + "license": "MIT-examples", + "row_id": "mit", + "source_type": "mit_examples", + "source_url": "local", + "text_hash": "22" * 32, + } + ] + ) + + +def test_completion_report_is_stable_and_does_not_authorize_training(): + ages = {"boxhash": 71.7, "vern": 72.0} + first = canonical_json(completion_report(ages)) + second = canonical_json(completion_report(ages)) + assert first == second + report = completion_report(ages) + assert report["transition"] == TRANSITION + assert report["rows_acquired"] == 0 + assert report["settlements_appended"] == 0 + assert report["admission_receipt"] is None + assert report["reserve_manifest_created"] is False + assert report["preregistration_state"] == PREREGISTRATION_SEALED + assert report["threshold_state"] == THRESHOLDS_BLOCKED + assert report["training_authorized"] is False + assert report["training_launch_authorized"] is False + assert report["exhausted_universe_consulted"] is False + assert report["label_blind_surface_count"] == 0 + assert report["isolation"] == "NOT_RUN_NO_ROWS" + assert report["provenance"] == "NO_ADMITTED_ROWS" + + +def test_module_does_not_commit_or_touch_the_residual_lane(): + source = MODULE.read_text(encoding="utf-8") + tree = ast.parse(source) + imported = [] + for node in ast.walk(tree): + if isinstance(node, ast.ImportFrom) and node.module: + imported.append(node.module) + assert not any("residual" in name for name in imported) + assert "commit_settlement" not in source + assert "persist_append" not in source + assert "torch" not in imported From 85b35ca8218950cc13cb46f30a2a45211ff181fa Mon Sep 17 00:00:00 2001 From: Daniel Meyer Date: Mon, 28 Sep 2026 16:09:11 -0700 Subject: [PATCH 12/42] feat(007): land SELECT residual and schedule work (11) --- .../residual_threshold_v1_design_closure.py | 263 +++++++++++++ .../hyperlexical/select_005_completion.py | 309 +++++++++++++++ .../hyperlexical/select_005_threshold.py | 344 ++++++++++++++++ .../CONTRACT_VALIDATION.json | 369 ++++++++++++++++++ tests/shadow/test_eval_routing.py | 195 +++++++++ 5 files changed, 1480 insertions(+) create mode 100644 scripts/shadow/hyperlexical/residual_threshold_v1_design_closure.py create mode 100644 scripts/shadow/hyperlexical/select_005_completion.py create mode 100644 scripts/shadow/hyperlexical/select_005_threshold.py create mode 100644 specs/007-hyperlexical-model/schemas/hyperlex/residual-threshold/CONTRACT_VALIDATION.json create mode 100644 tests/shadow/test_eval_routing.py diff --git a/scripts/shadow/hyperlexical/residual_threshold_v1_design_closure.py b/scripts/shadow/hyperlexical/residual_threshold_v1_design_closure.py new file mode 100644 index 00000000..949fc4a4 --- /dev/null +++ b/scripts/shadow/hyperlexical/residual_threshold_v1_design_closure.py @@ -0,0 +1,263 @@ +"""Close threshold v1 after the frozen calibration execution. + +The execution outcome stays CALIBRATION_INSUFFICIENT_SUPPORT. This module +does not redraw, relabel, or select a threshold. It records why that +outcome cannot be repaired on the frozen 27-row surface. +""" + +from __future__ import annotations + +import hashlib +import json +from pathlib import Path + +from hyperlexical.residual_threshold_v1 import MIN_CALIBRATION_HIGH, MIN_CALIBRATION_SECONDARY +from hyperlexical.residual_threshold_v1_calibration import ( + ANALYSIS_PATH, + ANCESTORS, + DRAW_RECEIPT, + EVENTS, + FAILURE_PATH, + ISOLATION_PATH, + LABELS_PATH, + LEDGER_FILE, + MANIFEST_PATH, + PREREGISTRATION, + RESOLUTION_PATH, + SCORE_RECEIPT, + SCORES_PATH, + SEARCH_PATH, + SOURCE, + TRACKER, +) + +EXECUTION_ID = "RESIDUAL_THRESHOLD_V1_CALIBRATION_EXECUTION_001" +EXECUTION_STATE = "CALIBRATION_INSUFFICIENT_SUPPORT" +REVIEW_ID = "CALIBRATION_LABEL_AVAILABILITY_REVIEW" +LABEL_CAUSE = "NOVEL_SURFACE_UNLABELED" +FINDING = "CALIBRATION_DESIGN_SUPPORT_MISMATCH_CONFIRMED" +DISPOSITION = "CLOSED_CALIBRATION_DESIGN_INSUFFICIENT" +NEXT_TRANSITION = "THRESHOLD_V2_PREREGISTRATION_AUTHORIZATION" +RULE = "RUNE.SEMANTIC_COMPOSITIONALITY_THRESHOLD.v1" +DRAWN_ROWS = 27 +RESIDUAL_READY = 6 +CURRENT_TRACKER = "07b911497c4e95d1b251c7bb98f78224f804b857fd61dffd7c198eecdf21636f" + +EXECUTION_SHA256 = { + DRAW_RECEIPT: "7448adb8c9448c3737a7b7c2c9a3116ced23d2bab7e432d011bc41229c3ef283", + MANIFEST_PATH: "ba561935cff5f9f0a36af0b8b24e4b69bce5fb2c61b5ea2236f0a664c56bcf5d", + ISOLATION_PATH: "8743c864ed9e7152a5bccb5d6304b62aff8ffed5e894fb1d40ab270d696da9a5", + RESOLUTION_PATH: "fde102faa317c2bdfd374bc2b2ddda680434bb1847f93b3991e1532da2764dc2", + SCORES_PATH: "f775849b3bb8cdec2ac8eeeea0100e51853fefcebbd88d6b3b4efb33fa8a3895", + SCORE_RECEIPT: "44cd2a9b57587d5330d830e5dda49fdbbc9b17d95553f030b3884feec46c9c29", + LABELS_PATH: "b4c93faed22d3c294d46768a510eb9695c5159f2f84bfb8c4f7ab15b7f2332c2", + ANALYSIS_PATH: "ac17d7e3c634cb5f984251e6a4f4090eecd0ff7e0d809947a48b689d454db625", + SEARCH_PATH: "f92c5aded6822747811da2b18c796e59974de1e673db55b3f7a481a71be89a3c", + FAILURE_PATH: "1d3f1f26609c1f1a47790d94730fba50e9f4d888dd8d7d89e6fa698a924fb3a8", +} + +REVIEW_PATH = SOURCE / "RESIDUAL_THRESHOLD_V1_CALIBRATION_LABEL_AVAILABILITY_REVIEW.json" +CLOSURE_PATH = SOURCE / "RESIDUAL_THRESHOLD_V1_CALIBRATION_DESIGN_CLOSURE.json" + + +def sha256(path: Path) -> str: + return hashlib.sha256(path.read_bytes()).hexdigest() + + +def refuse(message: str) -> None: + raise SystemExit(message) + + +def combined_ready_floor(high_floor: int = MIN_CALIBRATION_HIGH, secondary_floor: int = MIN_CALIBRATION_SECONDARY) -> int: + return high_floor + secondary_floor + + +def threshold_eligibility(ready_rows: int, high_floor: int = MIN_CALIBRATION_HIGH, secondary_floor: int = MIN_CALIBRATION_SECONDARY) -> str: + """Ready-row count alone. Operator labels cannot raise this count.""" + if ready_rows < combined_ready_floor(high_floor, secondary_floor): + return "mathematically_impossible" + return "not_ruled_out_by_ready_count" + + +def _write_json(path: Path, payload: dict) -> str: + if path.exists(): + refuse(f"refusing to rewrite {path.name}") + text = json.dumps(payload, indent=2, sort_keys=True, ensure_ascii=True) + "\n" + path.write_text(text, encoding="utf-8") + path.chmod(0o600) + return hashlib.sha256(text.encode("utf-8")).hexdigest() + + +def _verify_frozen() -> None: + for path, expected in PREREGISTRATION.items(): + if sha256(path) != expected: + refuse(f"preregistration changed: {path.name}") + for path, expected in ANCESTORS.items(): + if path == TRACKER: + continue + if sha256(path) != expected: + refuse(f"sealed ancestor changed: {path.name}") + for path, expected in EXECUTION_SHA256.items(): + if sha256(path) != expected: + refuse(f"calibration execution artifact changed: {path.name}") + if sha256(TRACKER) != CURRENT_TRACKER: + refuse("tracker hash drifted before the design closure") + if sha256(EVENTS) != ANCESTORS[EVENTS] or sha256(LEDGER_FILE) != ANCESTORS[LEDGER_FILE]: + refuse("events or ledger changed") + + +def _observe() -> dict: + manifest = [json.loads(line) for line in MANIFEST_PATH.read_text(encoding="utf-8").splitlines() if line] + scores = [json.loads(line) for line in SCORES_PATH.read_text(encoding="utf-8").splitlines() if line] + labels = [json.loads(line) for line in LABELS_PATH.read_text(encoding="utf-8").splitlines() if line] + if len(manifest) != DRAWN_ROWS or len(scores) != DRAWN_ROWS or len(labels) != DRAWN_ROWS: + refuse("frozen calibration row count drifted") + ready = sum(1 for row in scores if row.get("score_status") == "SCORED") + unknown = sum(1 for row in scores if row.get("score_status") != "SCORED") + labeled = sum(1 for row in labels if row.get("operator_label_present") is True or row.get("operator_bucket") is not None) + if ready != RESIDUAL_READY or unknown != DRAWN_ROWS - RESIDUAL_READY or labeled != 0: + refuse("frozen ready or label counts drifted") + failure = json.loads(FAILURE_PATH.read_text(encoding="utf-8")) + if failure.get("failure_state") != EXECUTION_STATE or failure.get("threshold_value") is not None or failure.get("threshold_frozen") is not False: + refuse("execution failure artifact was reinterpreted") + return {"labeled": labeled, "ready": ready, "unknown": unknown} + + +def seal() -> dict: + if REVIEW_PATH.exists() or CLOSURE_PATH.exists(): + refuse("design closure already exists") + _verify_frozen() + observed = _observe() + eligibility = threshold_eligibility(observed["ready"]) + if eligibility != "mathematically_impossible": + refuse("ready count does not rule out the support gate") + review = { + "admitted": 0, + "blind_labeling_authorized": False, + "cause": LABEL_CAUSE, + "execution_failure_sha256": EXECUTION_SHA256[FAILURE_PATH], + "execution_id": EXECUTION_ID, + "execution_state_unchanged": EXECUTION_STATE, + "gold": 0, + "json_schema_document": None, + "json_schema_exists": False, + "manifest_rows": DRAWN_ROWS, + "manifest_sha256": EXECUTION_SHA256[MANIFEST_PATH], + "measurement_surface_drawn": False, + "observed_existing_labels": f"{observed['labeled']}/{DRAWN_ROWS}", + "operator_packet_created": False, + "redraw_authorized": False, + "relabel_can_rescue_support_gate": False, + "relabel_existing_surface": "potentially_eligible", + "review_id": REVIEW_ID, + "schema": "hyperlex.residual_threshold_v1_calibration_label_availability_review.v1", + "score_sha256": EXECUTION_SHA256[SCORES_PATH], + "select_005_authorized": False, + "selected_source": "none", + "settled": 0, + "threshold_frozen": False, + "threshold_search_authorized": False, + "threshold_value": None, + } + review_sha = _write_json(REVIEW_PATH, review) + closure = { + "admitted": 0, + "disposition": DISPOSITION, + "drawn_rows": DRAWN_ROWS, + "execution_id": EXECUTION_ID, + "execution_state": EXECUTION_STATE, + "finding": FINDING, + "gold": 0, + "independent_of_operator_labels": True, + "json_schema_document": None, + "json_schema_exists": False, + "label_availability_review_sha256": review_sha, + "measurement_authorized": False, + "measurement_surface_drawn": False, + "minimum_ready_high": MIN_CALIBRATION_HIGH, + "minimum_ready_secondary": MIN_CALIBRATION_SECONDARY, + "minimum_required_target_ready_rows": combined_ready_floor(), + "next_legal_transition": NEXT_TRANSITION, + "next_transition_authorized": False, + "observed_existing_labels": observed["labeled"], + "observed_total_ready_rows": observed["ready"], + "observed_unknown_rows": observed["unknown"], + "preserved_execution_sha256": {path.name: digest for path, digest in sorted(EXECUTION_SHA256.items(), key=lambda item: item[0].name)}, + "redraw_authorized": False, + "residual_ready": observed["ready"], + "rule": RULE, + "schema": "hyperlex.residual_threshold_v1_calibration_design_closure.v1", + "select_005_authorized": False, + "selected_source": "none", + "settled": 0, + "threshold_eligibility": eligibility, + "threshold_frozen": False, + "threshold_search_authorized": False, + "threshold_value": None, + "v2_preregistration_authorized": False, + } + closure_sha = _write_json(CLOSURE_PATH, closure) + _update_tracker(review_sha, closure_sha) + preserved = list(PREREGISTRATION.items()) + list(EXECUTION_SHA256.items()) + preserved.extend((path, digest) for path, digest in ANCESTORS.items() if path != TRACKER) + for path, expected in preserved: + if sha256(path) != expected: + refuse(f"frozen file changed during closure: {path.name}") + if sha256(EVENTS) != ANCESTORS[EVENTS] or sha256(LEDGER_FILE) != ANCESTORS[LEDGER_FILE]: + refuse("events or ledger changed during closure") + report = { + "closure_sha256": closure_sha, + "disposition": DISPOSITION, + "execution_state": EXECUTION_STATE, + "finding": FINDING, + "review_sha256": review_sha, + "threshold_eligibility": eligibility, + "threshold_value": None, + "tracker_sha256": sha256(TRACKER), + } + print(json.dumps(report, indent=2, sort_keys=True)) + return report + + +def _update_tracker(review_sha: str, closure_sha: str) -> None: + tracker = json.loads(TRACKER.read_text(encoding="utf-8")) + if tracker.get("residual_threshold_state") != EXECUTION_STATE: + refuse("tracker execution state is not the frozen failure") + if tracker.get("residual_threshold_value") is not None or tracker.get("residual_threshold_frozen") is not False: + refuse("tracker threshold is not null") + prior = sha256(TRACKER) + tracker["previous_tracker_sha256"] = prior + tracker["residual_threshold_execution_id"] = EXECUTION_ID + tracker["residual_threshold_v1_disposition"] = DISPOSITION + tracker["residual_threshold_design_finding"] = FINDING + tracker["residual_threshold_label_availability_cause"] = LABEL_CAUSE + tracker["residual_threshold_existing_operator_labels"] = 0 + tracker["residual_threshold_manifest_rows"] = DRAWN_ROWS + tracker["residual_threshold_residual_ready_rows"] = RESIDUAL_READY + tracker["residual_threshold_redraw_authorized"] = False + tracker["residual_threshold_blind_labeling_authorized"] = False + tracker["residual_threshold_relabel_existing_surface"] = "potentially_eligible" + tracker["residual_threshold_label_availability_review_sha256"] = review_sha + tracker["residual_threshold_design_closure_sha256"] = closure_sha + tracker["next_legal_transition"] = NEXT_TRANSITION + tracker["next_transition_authorized"] = False + tracker["selected_source"] = "none" + tracker["select_authorized"] = False + tracker["admitted"] = 0 + tracker["settled"] = 0 + tracker["gold"] = 0 + tracker["measurement_sample_drawn"] = False + tracker["measurement_eligible"] = False + tracker["residual_threshold_measurement_surface_drawn"] = False + tracker["residual_threshold_json_schema_document"] = None + text = json.dumps(tracker, indent=2, sort_keys=True, ensure_ascii=True) + "\n" + TRACKER.write_text(text, encoding="utf-8") + TRACKER.chmod(0o600) + + +def main() -> None: + seal() + + +if __name__ == "__main__": + main() diff --git a/scripts/shadow/hyperlexical/select_005_completion.py b/scripts/shadow/hyperlexical/select_005_completion.py new file mode 100644 index 00000000..bd8a5897 --- /dev/null +++ b/scripts/shadow/hyperlexical/select_005_completion.py @@ -0,0 +1,309 @@ +"""SELECT-005 source contract and preregistration completion. + +Does not train, append the settlement log, or append the identity ledger. +The exhausted settlement universe is not an input. SELECT-004 numeric +floors are not copied. A stale exposure snapshot is not bypassed. +""" + +from __future__ import annotations + +import json +from typing import Any, Mapping, Sequence + +from .select_005_reserve import ( + CLEARED_RIGHTS, + EXPERIMENT_ID, + FAILURE_QUOTA, + PROPOSED_SCHEDULE, + REQUIRED_SLICES, + canonical_json, + census, + exclusion_reason, +) + +TRANSITION = "SELECT_005_SOURCE_AND_PREREGISTRATION_COMPLETION_AUTHORIZATION" +PREREGISTRATION_SEALED = "SELECT_005_PREREGISTRATION_SEALED" +THRESHOLDS_BLOCKED = "BLOCKED_PENDING_OPERATOR_AUTHORIZATION" +THRESHOLD_SCHEMA = "hyperlex.threshold_authorization.v1" +SELECT_ADMISSION_JSON_SCHEMA_EXISTS = True +THRESHOLD_AUTHORIZATION_JSON_SCHEMA_EXISTS = True +TRAINING_AUTHORIZED = False +EXHAUSTED_STREAM_RUN = "hs-20260925T211358Z" +EXHAUSTED_SETTLEMENT_COUNT = 339 +BOX_SNAPSHOT_LIMIT_H = 6.0 +ADMISSIBLE_SOURCE_TYPES = ("wiktionary_category", "wikipedia_prose") +WARM_START_NAME = "hyperlex-encoder-modernbert-base-seed-morph65" +WARM_START_SHA256 = "96838b9656a84c3fee1773a41fdf2fbbec2f88f3bc948e4acfbe06c194ac5587" +BEST_NAME = "hyperlex-encoder-modernbert-base-seed-select004" +BEST_SHA256 = "9fba0f66b1d5de6492470f53577d1447bfac1d29b9ac03869268abb70bbd97f6" +CONTROL_RECIPE_ENV_SHA256 = "edb7163f388de71c258db2d68d03ac4e6d6a011548236666250d5d0f5d05e0f1" +FRESH_BATCH_ID = "HLX-EVAL-RESERVE-SELECT-005-FRESH-001" +LABEL_BLIND_FORBIDDEN = frozenset( + { + "attest", + "class", + "decision", + "expected_slice", + "lineage", + "model_score", + "semantic_family", + "task", + "unbind_clean", + } +) +METRIC_NAMES = ( + "classify_macro_f1_nonnone", + "classification_accuracy", + "observed_label_accuracy", + "unbind_clean_exact", +) + + +def initialization_decision() -> dict[str, Any]: + """The written proposal warm-starts morph65 while BEST is already select004. + + That sentence is the experiment. The trainer does not choose between them. + """ + return { + "best_name": BEST_NAME, + "best_not_moved": True, + "best_sha256": BEST_SHA256, + "decision": "morph65_new_schedule", + "experiment_id": EXPERIMENT_ID, + "rejected_alternative": "select004_best_new_schedule", + "trainer_default_did_not_decide": True, + "warm_start_name": WARM_START_NAME, + "warm_start_sha256": WARM_START_SHA256, + } + + +def sealed_preregistration() -> dict[str, Any]: + """Freeze the control-arm recipe. Do not transfer SELECT-004's variable.""" + proposed = json_copy(PROPOSED_SCHEDULE) + return { + "experiment_id": EXPERIMENT_ID, + "held_constant": { + "batch_size": 8, + "gradient_accumulation": 1, + "learning_rate": "2e-5", + "seed": None, + "seed_policy": "HLX_SEED_UNSET_FROZEN", + }, + "held_constant_authority": { + "batch_size": "Recorded HYPERLEX_TRAIN_BATCH on the morph65 continuation control recipe.", + "control_recipe_env_sha256": CONTROL_RECIPE_ENV_SHA256, + "document": "specs/007-hyperlexical-model/evaluation-reserve.md", + "gradient_accumulation": ( + "The trainer takes one optimizer step per batch and has no " + "accumulation parameter. 1 freezes that behavior." + ), + "inherited_from_select_004": False, + "learning_rate": "Recorded HYPERLEX_TRAIN_LR on the morph65 continuation control recipe.", + "same_numeric_optimizer_values_as_select_004_candidate_env": True, + "seed": "The reconstructed recipe seals HLX_SEED unset. Both arms share that frozen policy.", + "select_004_scientific_variable_not_transferred": "HLX_SELECT_METRIC", + "trainer_default_did_not_decide": True, + }, + "initialization": initialization_decision(), + "pinned_export_rows": proposed["pinned_export_rows"], + "pinned_export_sha256": proposed["pinned_export_sha256"], + "schedule": { + "baseline": proposed["baseline"], + "candidate": proposed["candidate"], + "checkpoint_rule": proposed["checkpoint_rule"], + "selection_metric": proposed["selection_metric"], + }, + "schema": "hyperlex.preregistration.v1", + "scientific_variable": "train_schedule", + "sealed": True, + "state": PREREGISTRATION_SEALED, + "training_authorized": TRAINING_AUTHORIZED, + "training_launch_authorized": False, + "unset_fields": [], + } + + +def threshold_authorization_proposal() -> dict[str, Any]: + """Name the four metrics. Leave every number unset. + + The schedule design supports a strict improvement of the candidate arm + over the control arm. It does not assign a preservation floor. SELECT-004 + floors do not transfer, and no number is invented here. + """ + metrics = [ + { + "authorization_state": THRESHOLDS_BLOCKED, + "derivation": "OPERATOR_JUDGMENT_ABSENT", + "metric": name, + "numeric_threshold": None, + } + for name in METRIC_NAMES + ] + return { + "comparison_baseline": "SELECT-005 control arm", + "decision_thresholds": {}, + "experiment_id": EXPERIMENT_ID, + "inherited_from_select_004": False, + "metrics": metrics, + "not_seed_morph78": True, + "primary_comparison_supported_by_schedule_design": ( + "candidate classify_macro_f1_nonnone strictly greater than the " + "control arm; equality does not promote" + ), + "reason_unsealed": ( + "No SELECT-005 derivation assigns a number. Predecessor floors " + "do not transfer. A number was not invented." + ), + "schema": THRESHOLD_SCHEMA, + "sealed": False, + "state": THRESHOLDS_BLOCKED, + "threshold_authorization_json_schema_exists": THRESHOLD_AUTHORIZATION_JSON_SCHEMA_EXISTS, + "training_launch_authorized": False, + } + + +def source_contract() -> dict[str, Any]: + """Rights-cleared ingest rules for a reserve that is not the 339.""" + return { + "admissible_source_types": list(ADMISSIBLE_SOURCE_TYPES), + "cleared_rights": CLEARED_RIGHTS, + "exhausted_settlement_count": EXHAUSTED_SETTLEMENT_COUNT, + "exhausted_stream_is_a_source": False, + "exhausted_stream_run": EXHAUSTED_STREAM_RUN, + "experiment_id": EXPERIMENT_ID, + "harvest_fence": ( + "hs_run.py refuses when the box exposure snapshot is older than " + "6 hours. This pass does not bypass that fence and does not fetch." + ), + "label_blind": True, + "required_slices": list(REQUIRED_SLICES), + "selection_label_blind": True, + "settlement_slice_gap": ( + "The settlement record emits no task, class, lineage, or " + "unbind_clean. A decision-only row is SLICE_LABELS_ABSENT. " + "This pass does not attach those fields beside the contract." + ), + "transition": TRANSITION, + "unbind_clean": ( + "A new campaign must include compositional material that can " + "satisfy soft_ceiling.clean_surface. Which rows enter the review " + "surface is chosen before labels. Princeton WordNet is " + "compositional and is not the census rights token." + ), + "wordnet": { + "admitted_alone": False, + "census_rights_token": "not CC-BY-SA", + "license": "Princeton WordNet 3.0", + }, + } + + +def exposure_snapshot_refusal( + ages_hours: Mapping[str, float], + *, + limit_h: float = BOX_SNAPSHOT_LIMIT_H, +) -> str | None: + """Return the harvest refusal, or None when every snapshot is inside the fence.""" + if not ages_hours: + return "REFUSE: no box exposure snapshot" + stale = [age for age in ages_hours.values() if float(age) > limit_h] + if not stale: + return None + worst = max(stale) + return f"REFUSE: box exposure snapshot is {worst:.1f}h old (> {limit_h:g}h)" + + +def label_blind_surface(rows: Sequence[Mapping[str, Any]]) -> list[dict[str, Any]]: + """Identity, rights, and provenance only. Labels are a later operator act.""" + surface = [] + for row in rows: + carried = sorted(LABEL_BLIND_FORBIDDEN.intersection(row)) + if carried: + raise SystemExit("REFUSE: label-blind surface carries " + ",".join(carried)) + source_type = str(row.get("source_type") or "") + if source_type not in ADMISSIBLE_SOURCE_TYPES: + raise SystemExit( + "REFUSE: source_type is not rights-cleared for this contract: " + + (source_type or "missing") + ) + for key in ("row_id", "text_hash", "source_url", "license"): + if not str(row.get(key) or "").strip(): + raise SystemExit(f"REFUSE: provenance field missing: {key}") + license_text = str(row["license"]) + compact = license_text.upper().replace(" ", "").replace("-", "") + if "CCBYSA" not in compact: + raise SystemExit("REFUSE: license is not CC-BY-SA") + surface.append( + { + "license": license_text, + "row_id": str(row["row_id"]), + "source_type": source_type, + "source_url": str(row["source_url"]), + "text_hash": str(row["text_hash"]), + } + ) + return surface + + +def fresh_census( + events: Sequence[Mapping[str, Any]], + ledger_state: Mapping[str, str], + export_hashes: set[str], + *, + fences: Mapping[str, Mapping[str, set[str]]] | None = None, +) -> dict[str, Any]: + """Census a new source. The exhausted stream is refused before routing.""" + for event in events: + run = str(event.get("source_run") or "") + if run == EXHAUSTED_STREAM_RUN or event.get("exhausted_universe") is True: + raise SystemExit("REFUSE: exhausted settlement universe is not a SELECT-005 source") + result = census( + events, + ledger_state, + export_hashes, + fences=fences, + batch_id=FRESH_BATCH_ID, + ) + result["exhausted_universe_consulted"] = False + result["source"] = "fresh" + if result.get("failure") is None and result.get("reserve_row_count"): + result["reserve_status"] = "FROZEN" + else: + result["reserve_status"] = FAILURE_QUOTA if result.get("failure") == FAILURE_QUOTA else result.get("failure") + return result + + +def completion_report(snapshot_ages: Mapping[str, float]) -> dict[str, Any]: + """One reconstruction. Live ages are supplied by the caller.""" + refusal = exposure_snapshot_refusal(snapshot_ages) + body = fresh_census([], {}, set()) + return { + "acquisition_refusal": refusal, + "admission_receipt": None, + "census_failure": body["failure"], + "exhausted_universe_consulted": False, + "experiment_id": EXPERIMENT_ID, + "initialization": initialization_decision(), + "isolation": "NOT_RUN_NO_ROWS", + "label_blind_surface_count": 0, + "preregistration_state": PREREGISTRATION_SEALED, + "provenance": "NO_ADMITTED_ROWS", + "reserve_manifest_created": False, + "reserve_row_count": body["reserve_row_count"], + "rows_acquired": 0, + "settlements_appended": 0, + "snapshot_ages_hours": {key: snapshot_ages[key] for key in sorted(snapshot_ages)}, + "threshold_state": THRESHOLDS_BLOCKED, + "training_authorized": False, + "training_launch_authorized": False, + "transition": TRANSITION, + } + + +def json_copy(payload: Mapping[str, Any]) -> dict[str, Any]: + return json.loads(canonical_json(payload)) + + +def decision_only_exclusion(event: Mapping[str, Any]) -> str: + """Public alias so a settlement-shaped row is judged by the existing census.""" + return exclusion_reason(event, {}, set()) diff --git a/scripts/shadow/hyperlexical/select_005_threshold.py b/scripts/shadow/hyperlexical/select_005_threshold.py new file mode 100644 index 00000000..afac9de0 --- /dev/null +++ b/scripts/shadow/hyperlexical/select_005_threshold.py @@ -0,0 +1,344 @@ +"""SELECT-005 metric thresholds, sealed before any candidate result exists. + +The comparison is the candidate arm minus the control arm. The primary rule +matches ``note_strict_improvement``: a tie does not pass. Preservation allows +no degradation. SELECT-004's 0.02 / 0.05 / 0.01 floors are a different +experiment and are not copied. This module does not train, harvest, or score. +""" + +from __future__ import annotations + +import math +from typing import Any, Mapping + +from .select_005_completion import ( + BEST_NAME, + BEST_SHA256, + THRESHOLD_SCHEMA, + WARM_START_NAME, + WARM_START_SHA256, +) +from .select_005_reserve import EXPERIMENT_ID, canonical_json, sha256_text +from .select_contract_schema import threshold_schema_errors + +TRANSITION = "SELECT_005_THRESHOLD_AUTHORIZATION" +AUTHORIZATION_STATE = "SEALED" +PREREGISTRATION_SHA256 = "c2dcf3b703f50cb8eb80bb6c2c45335071ded3c4ce89cc669e8a869eb3498baf" +SCHEMA_SHA256 = "35a1b70cc805cf4271831a2414fd9ac85dcc15e5d23ec951d2d37665e89109a1" +PARENT_COMMIT = "7b857e33c63f595b05f078749257bf1d586a8ca6" +COMPARISON_BASELINE = "SELECT-005 control arm" +COMPARISON_CANDIDATE = "SELECT-005 candidate arm" +PRIMARY_METRIC = "classify_macro_f1_nonnone" +PRESERVATION_METRICS = ( + "classification_accuracy", + "observed_label_accuracy", + "unbind_clean_exact", +) +REQUIRED_METRICS = (PRIMARY_METRIC, *PRESERVATION_METRICS) +SELECT_004_FLOORS = { + "classification_accuracy": 0.02, + "observed_label_accuracy": 0.05, + "unbind_clean_exact": 0.01, +} + + +def _metric(name: str) -> dict[str, Any]: + primary = name == PRIMARY_METRIC + if primary: + operator = "GT" + direction = "strict_improvement" + rule_kind = "relative_to_control" + why = ( + "The sealed schedule says the candidate must strictly improve " + "classify_macro_f1_nonnone over the control arm. " + "note_strict_improvement keeps a checkpoint only when " + "score > best_value, and a tie keeps the earlier epoch. " + "The numeric threshold is 0 on candidate_minus_control with " + "operator GT, so equality fails. No larger margin is declared." + ) + else: + operator = "GE" + direction = "maximum_allowed_degradation" + rule_kind = "maximum_allowed_degradation" + why = ( + "This metric is a guardrail, not the optimized target. Both arms " + "share initialization, export, scorer, optimizer constants, and " + "seed policy, and the scorer is a ratio of integer counts. " + "Equal counts therefore compare equal. The smallest justified " + "tolerance is no degradation: candidate_minus_control >= 0. " + "SELECT-005 has no sealed reserve size, so a 1/N band cannot be " + "computed. A positive allowance would be a larger margin." + ) + return { + "comparison_basis": "candidate_minus_control", + "derivation": why, + "direction": direction, + "equality": "FAIL" if primary else "PASS", + "evidence_refs": [ + "specs/007-hyperlexical-model/evaluation-reserve.md SELECT-005 preregistration", + "scripts/shadow/hyperlexical/loop.py note_strict_improvement", + "scripts/shadow/hyperlexical/classify_metrics.py accuracy and macro_f1_nonnone", + "SELECT-004 decision receipt ee493b7d73b5e00ae27ec681bb52405bbcf2983a16a0297edad225aec52b7225", + ], + "historical_evidence": ( + "SELECT-004 compared a different variable against seed-morph78 " + "and allowed drops of 0.02, 0.05, and 0.01 on reserve sizes " + "241, 123, and 250. Its one finished reserve run moved unbind " + "clean exact by 0.008. Those sizes and that baseline are not " + "SELECT-005's." + ), + "metric": name, + "not_inherited_because": ( + "SELECT-004 floors were sized to that experiment's sealed " + "reserve and to seed-morph78. SELECT-005's baseline is its own " + "control arm, and its reserve is not frozen." + ), + "numeric_threshold": 0, + "operator": operator, + "rule_kind": rule_kind, + "units": "score_delta", + } + + +def sealed_authorization() -> dict[str, Any]: + """The operator policy for SELECT-005. Every required number is 0 with an operator.""" + metrics = [_metric(name) for name in REQUIRED_METRICS] + return { + "authorization_state": AUTHORIZATION_STATE, + "baseline": { + "comparison_candidate": COMPARISON_CANDIDATE, + "not_seed_morph78": True, + "same_export_sha256": "64b7d3dede25047cb6dd2e5b663f7fa72946ec82ac1a8816ae34622d1aaac430", + "same_initialization": WARM_START_NAME, + "warm_start_sha256": WARM_START_SHA256, + }, + "comparison_baseline": COMPARISON_BASELINE, + "decision_rule": { + "combined": ( + "PASS iff primary delta > 0 AND classification_accuracy " + "delta >= 0 AND observed_label_accuracy delta >= 0 AND " + "unbind_clean_exact delta >= 0" + ), + "compensation": False, + "frozen_before_training": True, + "missing_required_metric": "FAIL", + "non_computable_required_metric": "FAIL", + "preservation_equality_at_floor": "PASS", + "primary_equality": "FAIL", + "reserve_slice_floor_is_not_this_rule": ( + "unbind_clean >= 1 is reserve admission. " + "unbind_clean_exact is the model-quality guardrail." + ), + }, + "decision_thresholds": {name: 0 for name in REQUIRED_METRICS}, + "derivation": ( + "Primary threshold 0 uses operator GT because the preregistered " + "schedule and note_strict_improvement already require a strict " + "increase. Preservation thresholds are 0 with operator GE because " + "the paired arms share one scorer and no SELECT-005 sample size " + "exists from which to justify a positive degradation band." + ), + "direction": "candidate_minus_control", + "experiment_id": EXPERIMENT_ID, + "inherited_from_select_004": False, + "metrics": metrics, + "not_seed_morph78": True, + "operator": { + "decision": "AUTHORIZE_SELECT_005_THRESHOLDS", + "not_a_transfer": ( + "SELECT-004 decision_thresholds 0.02, 0.05, and 0.01 stay on " + "HLX-EXP-2026-09-26-SELECT-004." + ), + "recorded_by": "Hyperlex Cloud Agent", + "scope": EXPERIMENT_ID, + "training_launch_authorized": False, + }, + "preregistration_edited": False, + "preregistration_sha256": PREREGISTRATION_SHA256, + "primary_comparison_supported_by_schedule_design": ( + "candidate classify_macro_f1_nonnone > control " + "classify_macro_f1_nonnone" + ), + "primary_metric": { + "comparison_basis": "candidate_minus_control", + "equality": "FAIL", + "name": PRIMARY_METRIC, + "operator": "GT", + "strict_improvement": True, + "threshold": 0, + "units": "score_delta", + }, + "provenance": ( + "Sealed against parent commit " + PARENT_COMMIT + " before any " + "SELECT-005 training result. BEST remains " + BEST_NAME + " " + + BEST_SHA256 + "." + ), + "schema": THRESHOLD_SCHEMA, + "sealed": True, + "state": AUTHORIZATION_STATE, + "threshold_authorization_json_schema_exists": True, + "training_launch_authorized": False, + } + + +def _finite_number(value: Any) -> float | None: + if isinstance(value, bool) or not isinstance(value, (int, float)): + return None + number = float(value) + if not math.isfinite(number): + return None + return number + + +def apply_decision( + control: Mapping[str, Any], + candidate: Mapping[str, Any], +) -> dict[str, Any]: + """Apply the sealed rule. A failed guardrail vetoes a primary gain.""" + gates = [] + for name in REQUIRED_METRICS: + if name not in control or name not in candidate: + gates.append({"metric": name, "pass": False, "reason": "MISSING"}) + continue + left = _finite_number(control[name]) + right = _finite_number(candidate[name]) + if left is None or right is None: + gates.append({"metric": name, "pass": False, "reason": "NON_COMPUTABLE"}) + continue + delta = right - left + if name == PRIMARY_METRIC: + ok = delta > 0.0 + reason = "PASS" if ok else "PRIMARY_NOT_STRICT" + else: + ok = delta >= 0.0 + reason = "PASS" if ok else "PRESERVATION_DEGRADED" + gates.append( + { + "delta": delta, + "metric": name, + "pass": ok, + "reason": reason, + } + ) + passed = all(gate["pass"] for gate in gates) + return { + "compensation": False, + "gates": gates, + "outcome": "PASS" if passed else "FAIL", + "pass": passed, + } + + +def semantic_errors(payload: Mapping[str, Any]) -> list[str]: + """Schema failures and policy failures stay distinguishable by prefix.""" + errors = [f"schema: {item}" for item in threshold_schema_errors(payload)] + if payload.get("experiment_id") != EXPERIMENT_ID: + errors.append("experiment_id is not SELECT-005") + if payload.get("sealed") is not True: + errors.append("authorization is not sealed") + if payload.get("authorization_state") != AUTHORIZATION_STATE: + errors.append("authorization_state is not SEALED") + if payload.get("training_launch_authorized") is not False: + errors.append("training launch is not false") + if payload.get("inherited_from_select_004") is not False: + errors.append("SELECT-004 inheritance is not false") + if payload.get("not_seed_morph78") is not True: + errors.append("baseline is not marked distinct from seed-morph78") + if payload.get("comparison_baseline") != COMPARISON_BASELINE: + errors.append("comparison baseline is not the SELECT-005 control arm") + if payload.get("preregistration_sha256") != PREREGISTRATION_SHA256: + errors.append("preregistration sha256 does not match the sealed schedule") + thresholds = payload.get("decision_thresholds") + if not isinstance(thresholds, dict): + errors.append("decision_thresholds is absent") + thresholds = {} + for name in REQUIRED_METRICS: + if name not in thresholds: + errors.append(f"missing threshold {name}") + continue + if thresholds[name] is None: + errors.append(f"null threshold {name}") + elif thresholds[name] != 0: + errors.append(f"threshold {name} is not the sealed zero") + metrics = payload.get("metrics") + if not isinstance(metrics, list): + errors.append("metrics are absent") + metrics = [] + by_name = { + item.get("metric"): item + for item in metrics + if isinstance(item, dict) + } + for name in REQUIRED_METRICS: + item = by_name.get(name) + if not isinstance(item, dict): + errors.append(f"missing metric entry {name}") + continue + if item.get("numeric_threshold") is None: + errors.append(f"null numeric_threshold {name}") + expected = "GT" if name == PRIMARY_METRIC else "GE" + if item.get("operator") != expected: + errors.append(f"operator for {name} is not {expected}") + if item.get("comparison_basis") != "candidate_minus_control": + errors.append(f"comparison basis for {name} is not candidate_minus_control") + primary = payload.get("primary_metric") + if not isinstance(primary, dict) or primary.get("operator") != "GT": + errors.append("primary operator is not GT") + if isinstance(primary, dict) and primary.get("equality") != "FAIL": + errors.append("primary equality is not FAIL") + rule = payload.get("decision_rule") + if not isinstance(rule, dict) or rule.get("compensation") is not False: + errors.append("preservation metrics can be compensated") + if isinstance(rule, dict) and rule.get("frozen_before_training") is not True: + errors.append("thresholds are not marked frozen before training") + for name, foreign in SELECT_004_FLOORS.items(): + if isinstance(thresholds, dict) and thresholds.get(name) == foreign: + errors.append(f"SELECT-004 floor copied for {name}") + return errors + + +def schema_validation_report(payload: Mapping[str, Any] | None = None) -> dict[str, Any]: + body = sealed_authorization() if payload is None else payload + errors = threshold_schema_errors(body) + return { + "errors": errors, + "mutated": False, + "result": "PASS" if not errors else "FAIL", + "schema": THRESHOLD_SCHEMA, + "schema_sha256": SCHEMA_SHA256, + } + + +def semantic_validation_report(payload: Mapping[str, Any] | None = None) -> dict[str, Any]: + body = sealed_authorization() if payload is None else payload + errors = semantic_errors(body) + schema_hit = any(item.startswith("schema:") for item in errors) + policy = [item for item in errors if not item.startswith("schema:")] + if errors and schema_hit and not policy: + result = "SCHEMA_FAIL" + elif policy: + result = "FAIL" + else: + result = "PASS" + return {"errors": errors, "result": result} + + +def derivation_report() -> dict[str, Any]: + """Why each zero is the threshold, and why SELECT-004's numbers are not.""" + payload = sealed_authorization() + return { + "experiment_id": EXPERIMENT_ID, + "inherited_from_select_004": False, + "metrics": payload["metrics"], + "rejected_select_004_floors": SELECT_004_FLOORS, + "select_004_not_copied_because": ( + "Those floors are allowed drops versus seed-morph78 on " + "SELECT-004 reserve counts. SELECT-005 compares its candidate " + "with its control and has no sealed reserve count." + ), + "transition": TRANSITION, + } + + +def authorization_sha256() -> str: + return sha256_text(canonical_json(sealed_authorization())) diff --git a/specs/007-hyperlexical-model/schemas/hyperlex/residual-threshold/CONTRACT_VALIDATION.json b/specs/007-hyperlexical-model/schemas/hyperlex/residual-threshold/CONTRACT_VALIDATION.json new file mode 100644 index 00000000..af0f0205 --- /dev/null +++ b/specs/007-hyperlexical-model/schemas/hyperlex/residual-threshold/CONTRACT_VALIDATION.json @@ -0,0 +1,369 @@ +{ + "authorization": "THRESHOLD_ARTIFACT_SCHEMA_HARDENING_AUTHORIZATION", + "calibration_evidence_modified": false, + "changed_artifacts": [], + "historical_artifact_mutated": false, + "mode": "VALIDATE_ONLY", + "outcome": "HISTORICAL_VALIDATION_COMPLETE", + "results": [ + { + "artifact": "RESIDUAL_THRESHOLD_V1_CALIBRATION_DRAW_RECEIPT.json", + "errors": [], + "historical_artifact_mutated": false, + "schema": "surface-draw-receipt.schema.json", + "status": "PASS" + }, + { + "artifact": "RESIDUAL_THRESHOLD_V1_CALIBRATION_FAILURE.json", + "errors": [], + "historical_artifact_mutated": false, + "schema": "calibration-failure.schema.json", + "status": "PASS" + }, + { + "artifact": "RESIDUAL_THRESHOLD_V1_CALIBRATION_LABELS.jsonl", + "errors": [], + "historical_artifact_mutated": false, + "schema": "operator-label-row.schema.json", + "status": "PASS" + }, + { + "artifact": "RESIDUAL_THRESHOLD_V1_CALIBRATION_MANIFEST.jsonl", + "errors": [], + "historical_artifact_mutated": false, + "schema": "surface-manifest-row.schema.json", + "status": "PASS" + }, + { + "artifact": "RESIDUAL_THRESHOLD_V1_CALIBRATION_RESOLUTION.jsonl", + "errors": [], + "historical_artifact_mutated": false, + "schema": "resolution-row.schema.json", + "status": "PASS" + }, + { + "artifact": "RESIDUAL_THRESHOLD_V1_CALIBRATION_SCORES.jsonl", + "errors": [], + "historical_artifact_mutated": false, + "schema": "score-row.schema.json", + "status": "PASS" + }, + { + "artifact": "RESIDUAL_THRESHOLD_V1_CALIBRATION_SCORE_RECEIPT.json", + "errors": [], + "historical_artifact_mutated": false, + "schema": "score-receipt.schema.json", + "status": "PASS" + }, + { + "artifact": "RESIDUAL_THRESHOLD_V1_CALIBRATION_THRESHOLD_SEARCH.json", + "errors": [], + "historical_artifact_mutated": false, + "schema": "threshold-search.schema.json", + "status": "PASS" + }, + { + "artifact": "RESIDUAL_THRESHOLD_V2_CALIBRATION_COMPLETION_FAILURE.json", + "errors": [], + "historical_artifact_mutated": false, + "schema": "calibration-failure.schema.json", + "status": "PASS" + }, + { + "artifact": "RESIDUAL_THRESHOLD_V2_CALIBRATION_COMPLETION_RECEIPT.json", + "errors": [], + "historical_artifact_mutated": false, + "schema": "completion-receipt.schema.json", + "status": "PASS" + }, + { + "artifact": "RESIDUAL_THRESHOLD_V2_CALIBRATION_CONFOUND_ANALYSIS.json", + "errors": [], + "historical_artifact_mutated": false, + "schema": "confound-analysis.schema.json", + "status": "PASS" + }, + { + "artifact": "RESIDUAL_THRESHOLD_V2_CALIBRATION_FAILURE.json", + "errors": [], + "historical_artifact_mutated": false, + "schema": "calibration-failure.schema.json", + "status": "PASS" + }, + { + "artifact": "RESIDUAL_THRESHOLD_V2_CALIBRATION_LABELS.jsonl", + "errors": [], + "historical_artifact_mutated": false, + "schema": "operator-label-row.schema.json", + "status": "PASS" + }, + { + "artifact": "RESIDUAL_THRESHOLD_V2_CALIBRATION_MANIFEST.jsonl", + "errors": [], + "historical_artifact_mutated": false, + "schema": "surface-manifest-row.schema.json", + "status": "PASS" + }, + { + "artifact": "RESIDUAL_THRESHOLD_V2_CALIBRATION_RESOLUTION.jsonl", + "errors": [], + "historical_artifact_mutated": false, + "schema": "resolution-row.schema.json", + "status": "PASS" + }, + { + "artifact": "RESIDUAL_THRESHOLD_V2_CALIBRATION_RESOLUTION_RECEIPT.json", + "errors": [], + "historical_artifact_mutated": false, + "schema": "resolution-receipt.schema.json", + "status": "PASS" + }, + { + "artifact": "RESIDUAL_THRESHOLD_V2_CALIBRATION_SCORES.jsonl", + "errors": [], + "historical_artifact_mutated": false, + "schema": "score-row.schema.json", + "status": "PASS" + }, + { + "artifact": "RESIDUAL_THRESHOLD_V2_CALIBRATION_SCORE_RECEIPT.json", + "errors": [], + "historical_artifact_mutated": false, + "schema": "score-receipt.schema.json", + "status": "PASS" + }, + { + "artifact": "RESIDUAL_THRESHOLD_V2_CALIBRATION_SUPPORT_JOIN.jsonl", + "errors": [], + "historical_artifact_mutated": false, + "schema": "support-join-row.schema.json", + "status": "PASS" + }, + { + "artifact": "RESIDUAL_THRESHOLD_V2_CALIBRATION_SUPPORT_REASSESSMENT.json", + "errors": [], + "historical_artifact_mutated": false, + "schema": "support-reassessment.schema.json", + "status": "PASS" + }, + { + "artifact": "RESIDUAL_THRESHOLD_V2_COMPLETION_THRESHOLD_SEARCH.json", + "errors": [], + "historical_artifact_mutated": false, + "schema": "threshold-search.schema.json", + "status": "PASS" + }, + { + "artifact": "RESIDUAL_THRESHOLD_V2_CONFOUND_ANALYSIS.json", + "errors": [], + "historical_artifact_mutated": false, + "schema": "confound-analysis.schema.json", + "status": "PASS" + }, + { + "artifact": "RESIDUAL_THRESHOLD_V2_CONFOUND_DECISION.json", + "errors": [], + "historical_artifact_mutated": false, + "schema": "confound-decision.schema.json", + "status": "PASS" + }, + { + "artifact": "RESIDUAL_THRESHOLD_V2_DIRECTION_ANALYSIS.json", + "errors": [], + "historical_artifact_mutated": false, + "schema": "direction-analysis.schema.json", + "status": "PASS" + }, + { + "artifact": "RESIDUAL_THRESHOLD_V2_DIRECTION_DECISION.json", + "errors": [], + "historical_artifact_mutated": false, + "schema": "direction-decision.schema.json", + "status": "PASS" + }, + { + "artifact": "RESIDUAL_THRESHOLD_V2_MEASUREMENT_MANIFEST.jsonl", + "errors": [], + "historical_artifact_mutated": false, + "schema": "surface-manifest-row.schema.json", + "status": "PASS" + }, + { + "artifact": "RESIDUAL_THRESHOLD_V2_OPERATOR_LABELING_RECEIPT.json", + "errors": [], + "historical_artifact_mutated": false, + "schema": "operator-labeling-receipt.schema.json", + "status": "PASS" + }, + { + "artifact": "RESIDUAL_THRESHOLD_V2_OPERATOR_LABELS.jsonl", + "errors": [], + "historical_artifact_mutated": false, + "schema": "operator-label-row.schema.json", + "status": "PASS" + }, + { + "artifact": "RESIDUAL_THRESHOLD_V2_SURFACE_DRAW_RECEIPT.json", + "errors": [], + "historical_artifact_mutated": false, + "schema": "surface-draw-receipt.schema.json", + "status": "PASS" + }, + { + "artifact": "RESIDUAL_THRESHOLD_V2_THRESHOLD_SEARCH.json", + "errors": [], + "historical_artifact_mutated": false, + "schema": "threshold-search.schema.json", + "status": "PASS" + } + ], + "schema_conformance_gaps": [], + "scientific_remediation": false, + "semantic": [ + { + "check": "calibration_manifest_rows", + "detail": "rows=200", + "status": "PASS" + }, + { + "check": "operator_label_rows", + "detail": "rows=200", + "status": "PASS" + }, + { + "check": "support_join_rows", + "detail": "rows=200", + "status": "PASS" + }, + { + "check": "ready_plus_unknown", + "detail": "ready=60 unknown=140", + "status": "PASS" + }, + { + "check": "row_set_equality", + "detail": "manifest=200 labels=200 join=200", + "status": "PASS" + }, + { + "check": "manifest_order", + "detail": "calibration global_draw_order", + "status": "PASS" + }, + { + "check": "calibration_synset_unique", + "detail": "synsets=200", + "status": "PASS" + }, + { + "check": "label_inventory_total", + "detail": "total=200", + "status": "PASS" + }, + { + "check": "measurement_rows", + "detail": "rows=200", + "status": "PASS" + }, + { + "check": "measurement_synset_unique", + "detail": "synsets=200", + "status": "PASS" + }, + { + "check": "synset_set_isolation", + "detail": "calibration and measurement synsets", + "status": "PASS" + }, + { + "check": "measurement_row_isolation", + "detail": "calibration and measurement row ids", + "status": "PASS" + }, + { + "check": "measurement_manifest_unchanged", + "detail": "78ca09ab14912681d028e8b9b1c77a8daf1d0c8c81561434eb9b6ade45a753e5", + "status": "PASS" + }, + { + "check": "measurement_order", + "detail": "measurement global_draw_order", + "status": "PASS" + }, + { + "check": "join_receipt_manifest_hash", + "detail": "34bc70b93039fc6a5ba4bb58685e0fbfe6f91dd1f86a64fec8326a84013a6465", + "status": "PASS" + }, + { + "check": "join_receipt_label_hash", + "detail": "f9901e334543dac2e9bf258208097cf4c9b48af9dc8b497ff42fe4f1f106e1fc", + "status": "PASS" + }, + { + "check": "join_receipt_score_hash", + "detail": "01c758af570264c190f4af7805d43d734c9e9a36779f0e0b82675709f213e07c", + "status": "PASS" + }, + { + "check": "join_receipt_resolution_hash", + "detail": "491b854bcf04d20624e560b63c01823b5634716ec266a4d3a7396d5514defd8d", + "status": "PASS" + }, + { + "check": "draw_receipt_measurement_hash", + "detail": "78ca09ab14912681d028e8b9b1c77a8daf1d0c8c81561434eb9b6ade45a753e5", + "status": "PASS" + }, + { + "check": "draw_receipt_manifest_hash", + "detail": "34bc70b93039fc6a5ba4bb58685e0fbfe6f91dd1f86a64fec8326a84013a6465", + "status": "PASS" + }, + { + "check": "direction_join_hash", + "detail": "02088379b5ebe0f26106e830a730e8a90b2281d83c37bf1e750e1846bffa128b", + "status": "PASS" + }, + { + "check": "resolution_parent_totals", + "detail": "parents=200 ready=60 unknown=140", + "status": "PASS" + } + ], + "semantic_conformance_gaps": [], + "skipped_not_applicable": [ + "RESIDUAL_THRESHOLD_V1_ACCEPTANCE.json", + "RESIDUAL_THRESHOLD_V1_ARTIFACT_SCHEMA.json", + "RESIDUAL_THRESHOLD_V1_CALIBRATION_ANALYSIS.json", + "RESIDUAL_THRESHOLD_V1_CALIBRATION_CONTRACT.json", + "RESIDUAL_THRESHOLD_V1_CALIBRATION_DESIGN_CLOSURE.json", + "RESIDUAL_THRESHOLD_V1_CALIBRATION_ISOLATION_REPORT.json", + "RESIDUAL_THRESHOLD_V1_CALIBRATION_LABEL_AVAILABILITY_REVIEW.json", + "RESIDUAL_THRESHOLD_V1_MEASUREMENT_CONTRACT.json", + "RESIDUAL_THRESHOLD_V1_PREREGISTRATION.json", + "RESIDUAL_THRESHOLD_V1_SELECTION_PROCEDURE.json", + "RESIDUAL_THRESHOLD_V1_SURFACE_ISOLATION_POLICY.json", + "RESIDUAL_THRESHOLD_V2_ACCEPTANCE.json", + "RESIDUAL_THRESHOLD_V2_ARTIFACT_SCHEMA.json", + "RESIDUAL_THRESHOLD_V2_BLIND_OPERATOR_PACKET.jsonl", + "RESIDUAL_THRESHOLD_V2_BLIND_OPERATOR_PACKET_RECEIPT.json", + "RESIDUAL_THRESHOLD_V2_CALIBRATION_ANALYSIS.json", + "RESIDUAL_THRESHOLD_V2_CALIBRATION_CONTRACT.json", + "RESIDUAL_THRESHOLD_V2_CALIBRATION_LABELS.jsonl", + "RESIDUAL_THRESHOLD_V2_CALIBRATION_LABEL_AVAILABILITY_REVIEW.json", + "RESIDUAL_THRESHOLD_V2_CALIBRATION_RESOLUTION.jsonl", + "RESIDUAL_THRESHOLD_V2_CALIBRATION_SCORES.jsonl", + "RESIDUAL_THRESHOLD_V2_MEASUREMENT_CONTRACT.json", + "RESIDUAL_THRESHOLD_V2_OPERATOR_LABELS.jsonl", + "RESIDUAL_THRESHOLD_V2_PREREGISTRATION.json", + "RESIDUAL_THRESHOLD_V2_SAMPLE_SIZE.json", + "RESIDUAL_THRESHOLD_V2_SELECTION_PROCEDURE.json", + "RESIDUAL_THRESHOLD_V2_SURFACE_DISTRIBUTION_REPORT.json", + "RESIDUAL_THRESHOLD_V2_SURFACE_ISOLATION_POLICY.json", + "RESIDUAL_THRESHOLD_V2_SURFACE_ISOLATION_REPORT.json" + ], + "threshold_decision_emitted": false, + "threshold_redesign": false, + "training_authorized": false +} diff --git a/tests/shadow/test_eval_routing.py b/tests/shadow/test_eval_routing.py new file mode 100644 index 00000000..18cf164e --- /dev/null +++ b/tests/shadow/test_eval_routing.py @@ -0,0 +1,195 @@ +"""Routing contract: slices come from settlement plus a derivative record.""" + +import ast +from pathlib import Path + +import pytest + +from hyperlexical.eval_routing import ( + CLEARED_RIGHTS, + PROCEDURE_ID, + SCHEMA, + UNBIND_CLEAN_DERIVATION, + canonical_json, + contract_validation, + derive_batch, + derive_routing, + procedure_hash, + reference_pairs, +) +from hyperlexical.select_005_reserve import census + +MODULE = Path(__file__).resolve().parents[2] / "scripts/shadow/hyperlexical/eval_routing.py" + + +def _by_name(): + found = {} + for settlement, metadata in reference_pairs(): + found[settlement["row_id"]] = derive_routing(settlement, metadata) + return found + + +def test_reference_corpus_is_deterministic_and_valid(): + first = contract_validation() + second = contract_validation() + assert first == second + assert first["determinism"] == "IDENTICAL" + assert first["state"] == "ROUTING_VALIDATION_COMPLETE" + assert first["errors"] == [] + assert first["procedure_id"] == PROCEDURE_ID + assert procedure_hash() == procedure_hash() + assert len(procedure_hash()) == 64 + + +def test_classify_slices_follow_the_ledger_predicate(): + rows = _by_name() + observed = rows["route-fixture-accept-observed"] + assert observed["routing_status"] == "ELIGIBLE" + assert observed["slices"] == ["classify", "classify_non_none", "classify_observed"] + assert observed["class"] == "OBSERVED" + assert observed["lineage"] == "ai-native" + inferred = rows["route-fixture-accept-inferred"] + assert inferred["slices"] == ["classify", "classify_non_none"] + assert "classify_observed" not in inferred["slices"] + assert inferred["class"] == "INFERRED" + non_none = rows["route-fixture-accept-non-none"] + assert "classify_non_none" in non_none["slices"] + assert non_none["lineage"] == "politics-civic" + none_row = rows["route-fixture-none-classify"] + assert none_row["settlement_decision"] == "NONE" + assert none_row["slices"] == ["classify"] + assert "classify_non_none" not in none_row["slices"] + reclassified = rows["route-fixture-reclassify"] + assert reclassified["routing_status"] == "ELIGIBLE" + assert reclassified["slices"] == ["classify", "classify_non_none"] + + +def test_unbind_clean_is_a_frozen_flag_and_not_a_classify_alias(): + rows = _by_name() + clean = rows["route-fixture-accept-unbind"] + assert clean["routing_status"] == "ELIGIBLE" + assert clean["task"] == "unbind" + assert clean["unbind_clean"] is True + assert clean["slices"] == ["unbind_clean"] + assert "classify" not in clean["slices"] + blocked = rows["route-fixture-classify-not-unbind"] + assert blocked["routing_status"] == "INELIGIBLE" + assert "UNBIND_CLEAN_TASK_MISMATCH" in blocked["routing_reason_codes"] + assert blocked["slices"] == [] + assert UNBIND_CLEAN_DERIVATION == "soft_ceiling.clean_surface" + + +def test_inferred_is_not_promoted_and_blocks_stay_ineligible(): + rows = _by_name() + promoted = rows["route-fixture-promotion-refused"] + assert promoted["routing_status"] == "INELIGIBLE" + assert promoted["class"] == "INFERRED" + assert "CLASS_PROMOTION_REFUSED" in promoted["routing_reason_codes"] + assert "classify_observed" not in promoted["slices"] + rights = rows["route-fixture-rights-blocked"] + assert rights["routing_status"] == "INELIGIBLE" + assert rights["rights_state"] != CLEARED_RIGHTS + assert "RIGHTS_NOT_CLEARED" in rights["routing_reason_codes"] + provenance = rows["route-fixture-provenance-blocked"] + assert provenance["routing_status"] == "INELIGIBLE" + assert "PROVENANCE_INCOMPLETE" in provenance["routing_reason_codes"] + unresolved = rows["route-fixture-unresolved"] + assert unresolved["routing_status"] == "INELIGIBLE" + assert unresolved["slices"] == [] + assert "SETTLEMENT_UNRESOLVED" in unresolved["routing_reason_codes"] + conflict = rows["route-fixture-identity-conflict"] + assert "IDENTITY_CONFLICT" in conflict["routing_reason_codes"] + + +def test_duplicate_identity_does_not_route_twice(): + settlement, metadata = reference_pairs()[0] + records = derive_batch([(settlement, metadata), (settlement, metadata)]) + assert records[0]["routing_status"] == "ELIGIBLE" + assert records[1]["routing_status"] == "INELIGIBLE" + assert records[1]["routing_reason_codes"] == ["DUPLICATE_IDENTITY"] + assert records[1]["slices"] == [] + + +def test_quota_and_model_output_are_refused(): + settlement, metadata = reference_pairs()[0] + with pytest.raises(SystemExit, match="quota"): + derive_routing(settlement, {**metadata, "reserve_counts": {"unbind_clean": 0}}) + with pytest.raises(SystemExit, match="model"): + derive_routing({**settlement, "prediction": 0.2}, metadata) + deficit = {"unbind_clean": 0} + first = canonical_json(derive_routing(settlement, metadata)) + deficit["unbind_clean"] = 1 + second = canonical_json(derive_routing(settlement, metadata)) + assert first == second + assert deficit["unbind_clean"] == 1 + + +def test_census_reads_routing_without_embedding_slices_in_the_settlement(): + settlement, metadata = reference_pairs()[3] + assert "task" not in settlement + routing = derive_routing(settlement, metadata) + bare = census([settlement], {}, set()) + assert bare["failure"] == "RESERVE_QUOTA_UNFILLED" + assert bare["reserve_row_count"] == 0 + joined = census([settlement], {}, set(), routing_by_row={settlement["row_id"]: routing}) + assert joined["determinism"] == "IDENTICAL" + assert joined["routed_slice_counts"]["unbind_clean"] == 1 + assert joined["routed_slice_counts"]["classify"] == 0 + assert joined["failure"] == "RESERVE_QUOTA_UNFILLED" + forged = dict(routing) + forged["rights_state"] = "RIGHTS_UNRESOLVED" + forged["routing_status"] = "ELIGIBLE" + refused = census([settlement], {}, set(), routing_by_row={settlement["row_id"]: forged}) + assert refused["routed_slice_counts"]["unbind_clean"] == 0 + spent = census( + [settlement], + {settlement["text_hash"]: "EVAL_SPENT"}, + set(), + routing_by_row={settlement["row_id"]: routing}, + ) + assert spent["exclusion_counts"].get("EVAL_SPENT") == 1 + assert spent["eligible_count"] == 0 + + +def test_schema_names_and_module_fences(): + assert SCHEMA == "hyperlex.eval_routing.v1" + source = MODULE.read_text(encoding="utf-8") + tree = ast.parse(source) + imported = [] + for node in ast.walk(tree): + if isinstance(node, ast.ImportFrom) and node.module: + imported.append(node.module) + assert not any("residual" in name or "semantic_evidence" in name for name in imported) + assert "persist_append" not in source + assert "commit_settlement" not in source + assert "PLANNING_TARGETS" not in source + + +def test_threshold_schema_keeps_an_unsealed_map_empty_and_admission_epochs_at_zero(): + from hyperlexical.select_contract_schema import admission_schema_errors, threshold_schema_errors + + unsealed = { + "decision_thresholds": {}, + "experiment_id": "HLX-EXP-2026-09-27-SELECT-005", + "schema": "hyperlex.threshold_authorization.v1", + "sealed": False, + "state": "BLOCKED_PENDING_OPERATOR_AUTHORIZATION", + } + assert threshold_schema_errors(unsealed) == [] + sealed = dict(unsealed) + sealed["sealed"] = True + assert threshold_schema_errors(sealed) + receipt = { + "admission_gate_sequence": ["experiment_binding"] * 9, + "best_moved": False, + "controlled_holdout_contract": "CONTROLLED_RESERVE", + "epochs": 0, + "gradient_steps": 0, + "hlx_allow_no_holdout": False, + "holdout_admitted": False, + "launch_armed": False, + "optimizer_loaded": False, + "schema": "hyperlex.admission.v1", + "training_started": False, + } + assert admission_schema_errors(receipt) == [] From 3ad58ee6cf78bc6b6d80889dfc4b1b985fa1670f Mon Sep 17 00:00:00 2001 From: Daniel Meyer Date: Mon, 28 Sep 2026 16:13:48 -0700 Subject: [PATCH 13/42] feat(007): land SELECT residual and schedule work (12) --- .../hyperlex/residual-threshold/CONTRACT_VALIDATION.json | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/specs/007-hyperlexical-model/schemas/hyperlex/residual-threshold/CONTRACT_VALIDATION.json b/specs/007-hyperlexical-model/schemas/hyperlex/residual-threshold/CONTRACT_VALIDATION.json index af0f0205..7596ed8a 100644 --- a/specs/007-hyperlexical-model/schemas/hyperlex/residual-threshold/CONTRACT_VALIDATION.json +++ b/specs/007-hyperlexical-model/schemas/hyperlex/residual-threshold/CONTRACT_VALIDATION.json @@ -350,12 +350,9 @@ "RESIDUAL_THRESHOLD_V2_BLIND_OPERATOR_PACKET_RECEIPT.json", "RESIDUAL_THRESHOLD_V2_CALIBRATION_ANALYSIS.json", "RESIDUAL_THRESHOLD_V2_CALIBRATION_CONTRACT.json", - "RESIDUAL_THRESHOLD_V2_CALIBRATION_LABELS.jsonl", "RESIDUAL_THRESHOLD_V2_CALIBRATION_LABEL_AVAILABILITY_REVIEW.json", - "RESIDUAL_THRESHOLD_V2_CALIBRATION_RESOLUTION.jsonl", - "RESIDUAL_THRESHOLD_V2_CALIBRATION_SCORES.jsonl", + "RESIDUAL_THRESHOLD_V2_CALIBRATION_SUPPORT_JOIN_RECEIPT.json", "RESIDUAL_THRESHOLD_V2_MEASUREMENT_CONTRACT.json", - "RESIDUAL_THRESHOLD_V2_OPERATOR_LABELS.jsonl", "RESIDUAL_THRESHOLD_V2_PREREGISTRATION.json", "RESIDUAL_THRESHOLD_V2_SAMPLE_SIZE.json", "RESIDUAL_THRESHOLD_V2_SELECTION_PROCEDURE.json", From 0ad20ea6c32fe0820a59000ea9c59df55f258ee4 Mon Sep 17 00:00:00 2001 From: Daniel Meyer Date: Mon, 28 Sep 2026 16:14:52 -0700 Subject: [PATCH 14/42] feat(007): land SELECT residual and schedule work (13) --- .../residual-threshold/common.schema.json | 250 ++++++++++++++++++ .../test_residual_threshold_contract.py | 239 +++++++++++++++++ tests/shadow/test_select_005_harvest.py | 103 ++++++++ 3 files changed, 592 insertions(+) create mode 100644 specs/007-hyperlexical-model/schemas/hyperlex/residual-threshold/common.schema.json create mode 100644 tests/shadow/test_residual_threshold_contract.py create mode 100644 tests/shadow/test_select_005_harvest.py diff --git a/specs/007-hyperlexical-model/schemas/hyperlex/residual-threshold/common.schema.json b/specs/007-hyperlexical-model/schemas/hyperlex/residual-threshold/common.schema.json new file mode 100644 index 00000000..dc6efb19 --- /dev/null +++ b/specs/007-hyperlexical-model/schemas/hyperlex/residual-threshold/common.schema.json @@ -0,0 +1,250 @@ +{ + "title": "Shared residual-threshold contract terms", + "description": "Canonical terms already emitted by the threshold lane. This schema does not migrate frozen artifacts.", + "$defs": { + "sha256": { + "type": "string", + "pattern": "^[0-9a-f]{64}$" + }, + "row_id": { + "$ref": "#/$defs/sha256" + }, + "pwn30_synset": { + "type": "string", + "pattern": "^(noun|verb|adj|adv):[0-9]{8}$" + }, + "pos": { + "enum": [ + "noun", + "verb", + "adj", + "adv" + ] + }, + "operator_label": { + "enum": [ + "HIGH", + "SECONDARY", + "REJECT", + "QUARANTINE", + "UNRESOLVED" + ] + }, + "row_resolution_status": { + "enum": [ + "RESIDUAL_READY", + "UNKNOWN" + ] + }, + "constituent_resolution_status": { + "enum": [ + "EXACT", + "RESOLVED", + "AMBIGUOUS", + "UNRESOLVED" + ] + }, + "score_status": { + "enum": [ + "SCORED", + "UNKNOWN" + ] + }, + "measurement_state": { + "enum": [ + "SEALED", + "ELIGIBLE", + "EXECUTED" + ] + }, + "threshold_state": { + "enum": [ + "THRESHOLD_PROCEDURE_PREREGISTERED", + "SURFACES_FROZEN", + "CALIBRATION_INSUFFICIENT_SUPPORT", + "SUPPORT_GATE_PASSED", + "SUPPORTED_DIRECTION", + "NO_DIRECTIONAL_SIGNAL", + "CALIBRATION_CONFOUND_REVIEW", + "NO_THRESHOLD_PASSES_PRECISION_GATE", + "THRESHOLD_FROZEN" + ] + }, + "direction_result": { + "enum": [ + "SUPPORTED_DIRECTION", + "INVERTED_DIRECTION", + "NO_DIRECTIONAL_SEPARATION" + ] + }, + "residual_decimal": { + "type": "string", + "pattern": "^-?[0-9]+\\.[0-9]{10}$" + }, + "effect_decimal": { + "type": "string", + "pattern": "^-?[0-9]+\\.[0-9]{6}$" + }, + "distribution": { + "type": "object", + "additionalProperties": false, + "required": ["count", "max", "mean", "median", "min", "p10", "p25", "p75", "p90", "status", "std"], + "properties": { + "count": {"type": "integer", "minimum": 0}, + "max": {"$ref": "#/$defs/residual_decimal"}, + "mean": {"$ref": "#/$defs/residual_decimal"}, + "median": {"$ref": "#/$defs/residual_decimal"}, + "min": {"$ref": "#/$defs/residual_decimal"}, + "p10": {"$ref": "#/$defs/residual_decimal"}, + "p25": {"$ref": "#/$defs/residual_decimal"}, + "p75": {"$ref": "#/$defs/residual_decimal"}, + "p90": {"$ref": "#/$defs/residual_decimal"}, + "status": {"enum": ["DESCRIPTIVE", "NOT_COMPUTABLE"]}, + "std": {"$ref": "#/$defs/residual_decimal"} + } + }, + "artifact_reference": { + "type": "object", + "additionalProperties": false, + "required": [ + "name", + "sha256" + ], + "properties": { + "name": { + "type": "string", + "minLength": 1 + }, + "sha256": { + "$ref": "#/$defs/sha256" + } + } + }, + "unfrozenThresholdInvariant": { + "if": { + "properties": { + "threshold_frozen": { + "const": false + } + }, + "required": [ + "threshold_frozen" + ] + }, + "then": { + "properties": { + "threshold_value": { + "type": "null" + } + }, + "required": [ + "threshold_value" + ] + } + }, + "frozenThresholdInvariant": { + "if": { + "properties": { + "threshold_frozen": { + "const": true + } + }, + "required": [ + "threshold_frozen" + ] + }, + "then": { + "required": [ + "false_high", + "high_recall", + "leave_one_out_result", + "precision", + "predicted_yes_support", + "quarantine_predicted_yes", + "reject_predicted_yes", + "threshold_value", + "true_high", + "wilson_lower", + "wilson_upper" + ], + "properties": { + "false_high": { + "type": "integer", + "minimum": 0 + }, + "high_recall": { + "$ref": "#/$defs/effect_decimal" + }, + "leave_one_out_result": { + "const": "PASS" + }, + "precision": { + "$ref": "#/$defs/effect_decimal" + }, + "predicted_yes_support": { + "type": "integer", + "minimum": 8 + }, + "quarantine_predicted_yes": { + "const": 0 + }, + "reject_predicted_yes": { + "const": 0 + }, + "threshold_value": { + "$ref": "#/$defs/residual_decimal" + }, + "true_high": { + "type": "integer", + "minimum": 0 + }, + "wilson_lower": { + "$ref": "#/$defs/effect_decimal" + }, + "wilson_upper": { + "$ref": "#/$defs/effect_decimal" + } + } + } + }, + "sealedMeasurementInvariant": { + "if": { + "properties": { + "measurement_state": { + "const": "SEALED" + } + }, + "required": [ + "measurement_state" + ] + }, + "then": { + "properties": { + "measurement_glossbert_run": { + "const": false + }, + "measurement_labeled": { + "const": false + }, + "measurement_labels_loaded": { + "const": false + }, + "measurement_minilm_run": { + "const": false + }, + "measurement_resolved": { + "const": false + }, + "measurement_scored": { + "const": false + }, + "measurement_touched": { + "const": false + } + } + } + } + }, + "$schema": "https://json-schema.org/draft/2020-12/schema", + "$id": "https://hyperlex.local/schemas/hyperlex/residual-threshold/common.schema.json" +} diff --git a/tests/shadow/test_residual_threshold_contract.py b/tests/shadow/test_residual_threshold_contract.py new file mode 100644 index 00000000..57bf236d --- /dev/null +++ b/tests/shadow/test_residual_threshold_contract.py @@ -0,0 +1,239 @@ +"""Contract schemas and semantic checks do not rewrite frozen threshold artifacts.""" + +from __future__ import annotations + +import inspect +import json +from pathlib import Path + +import pytest +from jsonschema import Draft202012Validator + +from hyperlexical import residual_threshold_contract as contract +from hyperlexical.residual_threshold_contract import ( + EXPECTED_EVENTS, + EXPECTED_LEDGER, + EXPECTED_MEASUREMENT, + EXPECTED_TRACKER, + SOURCE, + classify, + evidence_hashes, + load_schemas, + schema_registry, + semantic_checks, + validate_directory, +) + + +def _validator(name: str): + schemas = load_schemas() + Draft202012Validator.check_schema(schemas[name]) + return Draft202012Validator(schemas[name], registry=schema_registry(schemas)) + + +def test_contract_family_names_match_the_specification(): + assert set(load_schemas()) == { + "calibration-failure.schema.json", + "common.schema.json", + "completion-receipt.schema.json", + "confound-analysis.schema.json", + "confound-decision.schema.json", + "direction-analysis.schema.json", + "direction-decision.schema.json", + "operator-label-row.schema.json", + "operator-labeling-receipt.schema.json", + "resolution-receipt.schema.json", + "resolution-row.schema.json", + "score-receipt.schema.json", + "score-row.schema.json", + "support-join-row.schema.json", + "support-reassessment.schema.json", + "surface-draw-receipt.schema.json", + "surface-manifest-row.schema.json", + "threshold-decision.schema.json", + "threshold-search.schema.json", + } + + +def test_valid_unfrozen_decision_passes_and_missing_field_fails(): + validator = _validator("calibration-failure.schema.json") + document = { + "failed_gate": "precision_gate", + "failure_state": "NO_THRESHOLD_PASSES_PRECISION_GATE", + "schema": "hyperlex.residual_threshold_v2_calibration_completion_failure.v1", + "threshold_frozen": False, + "threshold_value": None, + } + assert validator.is_valid(document) + del document["failed_gate"] + assert not validator.is_valid(document) + + +def test_bad_sha256_and_invalid_enum_fail(): + validator = _validator("support-join-row.schema.json") + row = { + "calibration_row_id": 1, + "operator_label": "HIGH", + "pos": "noun", + "pwn30_synset": "noun:01234567", + "residual_score": None, + "row_id": "a" * 64, + "row_resolution_status": "UNKNOWN", + "surface": "example phrase", + } + assert validator.is_valid(row) + assert not validator.is_valid({**row, "row_id": "A" * 64}) + assert not validator.is_valid({**row, "row_id": "ab"}) + assert not validator.is_valid({**row, "operator_label": "MAYBE"}) + + +def test_unfrozen_threshold_rejects_a_numeric_value(): + validator = _validator("threshold-decision.schema.json") + assert not validator.is_valid( + { + "schema": "hyperlex.residual_threshold_v2_threshold_frozen.v1", + "threshold_frozen": False, + "threshold_value": "0.2500000000", + } + ) + + +def test_frozen_threshold_rejects_null_and_accepts_metrics(): + validator = _validator("threshold-decision.schema.json") + frozen = { + "false_high": 1, + "high_recall": "0.500000", + "leave_one_out_result": "PASS", + "precision": "0.800000", + "predicted_yes_support": 8, + "quarantine_predicted_yes": 0, + "reject_predicted_yes": 0, + "schema": "hyperlex.residual_threshold_v2_threshold_frozen.v1", + "threshold_frozen": True, + "threshold_value": None, + "true_high": 7, + "wilson_lower": "0.500000", + "wilson_upper": "0.900000", + } + assert not validator.is_valid(frozen) + frozen["threshold_value"] = "0.2500000000" + assert validator.is_valid(frozen) + + +def test_completion_receipt_is_not_classified_as_a_generic_receipt(): + assert classify("RESIDUAL_THRESHOLD_V2_CALIBRATION_COMPLETION_RECEIPT.json") == "completion-receipt.schema.json" + assert classify("RESIDUAL_THRESHOLD_V2_SURFACE_DRAW_RECEIPT.json") == "surface-draw-receipt.schema.json" + assert classify("RESIDUAL_THRESHOLD_V2_THRESHOLD_FROZEN.json") == "threshold-decision.schema.json" + + +def _write(path: Path, payload) -> None: + if isinstance(payload, list): + path.write_text("".join(json.dumps(row, sort_keys=True) + "\n" for row in payload), encoding="utf-8") + else: + path.write_text(json.dumps(payload, indent=2, sort_keys=True) + "\n", encoding="utf-8") + + +def _surface(row_id: str, synset: str, order: int) -> dict: + return { + "allocation_rule_id": "positional_stratified_hash_round_robin_quota_v2", + "frozen_gloss": "gloss", + "global_draw_order": order, + "normalized_text_sha256": row_id, + "pos": "noun", + "pwn30_synset": synset, + "row_id": row_id, + "source_identity": "wordnet-3.0", + "stratum_id": "noun:2", + "surface": "example phrase", + "surface_role": "CALIBRATION_V2" if order <= 200 else "MEASUREMENT_V2", + "token_count": 2, + "within_stratum_order": 1, + } + + +def test_semantic_validator_rejects_identity_synset_measurement_hash_and_total_mismatches(tmp_path: Path): + row_ids = [f"{index:064x}" for index in range(200)] + manifest = [_surface(row_ids[index], f"noun:{index:08d}", index + 1) for index in range(200)] + labels = [ + { + "calibration_row_id": index + 1, + "operator_evidence": "STRONG_IDIOM", + "operator_label": "HIGH", + "operator_note": "lexicalized", + "pos": "noun", + "pwn30_synset": f"noun:{index:08d}", + "review_status": "REVIEWED", + "row_id": row_ids[index], + "schema": "hyperlex.residual_threshold_v2_operator_label.v1", + "surface": "example phrase", + } + for index in range(200) + ] + join = [ + { + "calibration_row_id": index + 1, + "operator_label": "HIGH", + "pos": "noun", + "pwn30_synset": f"noun:{index:08d}", + "residual_score": "0.1000000000" if index < 60 else None, + "row_id": row_ids[index], + "row_resolution_status": "RESIDUAL_READY" if index < 60 else "UNKNOWN", + "surface": "example phrase", + } + for index in range(200) + ] + measurement = [_surface(f"{index + 400:064x}", f"verb:{index:08d}", 201 + index) for index in range(200)] + _write(tmp_path / "RESIDUAL_THRESHOLD_V2_CALIBRATION_MANIFEST.jsonl", manifest) + _write(tmp_path / "RESIDUAL_THRESHOLD_V2_OPERATOR_LABELS.jsonl", labels) + _write(tmp_path / "RESIDUAL_THRESHOLD_V2_CALIBRATION_SUPPORT_JOIN.jsonl", join) + _write(tmp_path / "RESIDUAL_THRESHOLD_V2_MEASUREMENT_MANIFEST.jsonl", measurement) + measurement_hash = __import__("hashlib").sha256((tmp_path / "RESIDUAL_THRESHOLD_V2_MEASUREMENT_MANIFEST.jsonl").read_bytes()).hexdigest() + baseline = semantic_checks(tmp_path, measurement_hash) + assert all(row["status"] == "PASS" for row in baseline) + + drifted = [dict(row) for row in labels] + drifted[0]["row_id"] = "b" * 64 + _write(tmp_path / "RESIDUAL_THRESHOLD_V2_OPERATOR_LABELS.jsonl", drifted) + assert any(row["check"] == "row_set_equality" and row["status"] != "PASS" for row in semantic_checks(tmp_path, measurement_hash)) + + _write(tmp_path / "RESIDUAL_THRESHOLD_V2_OPERATOR_LABELS.jsonl", labels) + duplicated = [dict(row) for row in manifest] + duplicated[1]["pwn30_synset"] = duplicated[0]["pwn30_synset"] + _write(tmp_path / "RESIDUAL_THRESHOLD_V2_CALIBRATION_MANIFEST.jsonl", duplicated) + assert any(row["check"] == "calibration_synset_unique" and row["status"] != "PASS" for row in semantic_checks(tmp_path, measurement_hash)) + + _write(tmp_path / "RESIDUAL_THRESHOLD_V2_CALIBRATION_MANIFEST.jsonl", manifest) + assert any(row["check"] == "measurement_manifest_unchanged" and row["status"] != "PASS" for row in semantic_checks(tmp_path, "a" * 64)) + + short = join[:-1] + _write(tmp_path / "RESIDUAL_THRESHOLD_V2_CALIBRATION_SUPPORT_JOIN.jsonl", short) + assert any(row["check"] == "ready_plus_unknown" and row["status"] != "PASS" for row in semantic_checks(tmp_path, measurement_hash)) + + +def test_validator_writes_only_the_public_contract_report(): + source = inspect.getsource(contract) + assert source.count(".write_text") == 1 + assert "open(" not in source + assert "hlx-private" not in str(contract.REPORT_PATH) + + +def test_historical_validation_does_not_mutate_evidence(): + if not SOURCE.is_dir(): + pytest.skip("private reserve is absent") + before = evidence_hashes(SOURCE) + report = validate_directory(SOURCE) + after = evidence_hashes(SOURCE) + assert before == after + assert report["calibration_evidence_modified"] is False + assert report["historical_artifact_mutated"] is False + assert report["training_authorized"] is False + assert report["threshold_decision_emitted"] is False + assert report["outcome"] == "HISTORICAL_VALIDATION_COMPLETE" + assert report["schema_conformance_gaps"] == [] + assert report["semantic_conformance_gaps"] == [] + ledger = SOURCE.parents[1] + assert __import__("hashlib").sha256((ledger / "events.jsonl").read_bytes()).hexdigest() == EXPECTED_EVENTS + assert __import__("hashlib").sha256((ledger / "ledger.json").read_bytes()).hexdigest() == EXPECTED_LEDGER + tracker = ledger / "operator-review/HLX-EVAL-UNBIND-SENSE-SCREEN-V1-HYPOTHESIS-001/HYPOTHESIS.json" + assert __import__("hashlib").sha256(tracker.read_bytes()).hexdigest() == EXPECTED_TRACKER + assert __import__("hashlib").sha256((SOURCE / "RESIDUAL_THRESHOLD_V2_MEASUREMENT_MANIFEST.jsonl").read_bytes()).hexdigest() == EXPECTED_MEASUREMENT diff --git a/tests/shadow/test_select_005_harvest.py b/tests/shadow/test_select_005_harvest.py new file mode 100644 index 00000000..81c86464 --- /dev/null +++ b/tests/shadow/test_select_005_harvest.py @@ -0,0 +1,103 @@ +"""The SELECT-005 harvest stops before fetch when the exposure snapshot is stale.""" + +import hashlib +from pathlib import Path + +import pytest + +from hyperlexical.eval_routing import procedure_hash +from hyperlexical.select_005_harvest import STALE, exposure_fence +from hyperlexical.select_005_reserve import canonical_json + +REPO = Path(__file__).resolve().parents[2] +ROUTING_SPEC = "002e2ac67e5ee00c27ecedd4566b9e7d900852c51e3ac57aac957ac095899d96" +ROUTING_SCHEMA = "147e2787e6e66ba47acd185fa6602a10bd5d57c15ea446f574b2e750277d31a0" +PROCEDURE = "ad187d262eba280dd4b88d2204e03d4dfb255f9b0fb443bd250a0e441a7ea6db" + + +def _pair(age_seconds: float) -> dict: + compared = "1790631188.652030" + mtime = f"{float(compared) - age_seconds:.6f}" + return exposure_fence( + [ + { + "kind": "vern", + "mtime_unix": mtime, + "path": "inbox/vern-texts-example.jsonl", + "sha256": "ab" * 32, + "snapshot_id": "example", + }, + { + "kind": "boxhash", + "mtime_unix": mtime, + "path": "inbox/box-jev-hashes-example.txt", + "sha256": "cd" * 32, + "snapshot_id": "example", + }, + ], + compared_at_unix=compared, + ) + + +def test_stale_snapshot_stops_before_fetch_and_is_deterministic(): + first = _pair(72.32 * 3600) + second = _pair(72.32 * 3600) + assert first["state"] == STALE + assert first["fetch_authorized"] is False + assert first["harvest_executed"] is False + assert first["network_requests"] == 0 + assert first["rows_acquired"] == 0 + assert first["exposure_fence_bypassed"] is False + assert first["stop_before"] == "source_fetch" + assert float(first["worst_age_hours"]) > 6 + assert canonical_json(first) == canonical_json(second) + + +def test_fresh_fence_does_not_fetch(): + record = _pair(3600) + assert record["state"] == "EXPOSURE_SNAPSHOT_FRESH" + assert record["fetch_authorized"] is True + assert record["harvest_executed"] is False + assert record["network_requests"] == 0 + assert record["rows_acquired"] == 0 + assert record["stop_before"] is None + + +def test_missing_snapshot_kind_is_stale(): + record = exposure_fence( + [ + { + "kind": "vern", + "mtime_unix": "1790630000.000000", + "path": "inbox/vern-only.jsonl", + "sha256": "ef" * 32, + "snapshot_id": "partial", + } + ], + compared_at_unix="1790631000.000000", + ) + assert record["state"] == STALE + assert record["missing_kinds"] == ["boxhash"] + assert record["fetch_authorized"] is False + + +def test_widening_the_fence_is_refused(): + with pytest.raises(SystemExit, match="cannot be widened"): + exposure_fence([], compared_at_unix="1.0", limit_h=7) + + +def test_harvester_module_has_no_fetch_client(): + text = (REPO / "scripts/shadow/hyperlexical/select_005_harvest.py").read_text() + for banned in ("urllib", "import requests", "httpx", "import socket", "hs_run", "urlopen"): + assert banned not in text + + +def test_routing_contract_bytes_stay_frozen(): + spec = REPO / "specs/007-hyperlexical-model/select-eval-routing-contract.md" + schema = ( + REPO + / "specs/007-hyperlexical-model/schemas/hyperlex/select/eval-routing.schema.json" + ) + assert hashlib.sha256(spec.read_bytes()).hexdigest() == ROUTING_SPEC + assert hashlib.sha256(schema.read_bytes()).hexdigest() == ROUTING_SCHEMA + assert procedure_hash() == PROCEDURE From 623e2c00ace666e0622f1db589422c5592d73751 Mon Sep 17 00:00:00 2001 From: Daniel Meyer Date: Mon, 28 Sep 2026 16:16:20 -0700 Subject: [PATCH 15/42] feat(007): land SELECT residual and schedule work (14) --- scripts/shadow/hyperlexical/eval_routing.py | 471 ++++++++++++++++++ .../residual_threshold_contract.py | 319 ++++++++++++ 2 files changed, 790 insertions(+) create mode 100644 scripts/shadow/hyperlexical/eval_routing.py create mode 100644 scripts/shadow/hyperlexical/residual_threshold_contract.py diff --git a/scripts/shadow/hyperlexical/eval_routing.py b/scripts/shadow/hyperlexical/eval_routing.py new file mode 100644 index 00000000..592b8e65 --- /dev/null +++ b/scripts/shadow/hyperlexical/eval_routing.py @@ -0,0 +1,471 @@ +"""SELECT reserve routing. Settlement stays untouched. + +A routing record is a derivative. It copies the sealed decision, registry +class, and semantic family, then applies ``identity_ledger.slices_of``. +It does not settle, score, or read reserve deficits. +""" + +from __future__ import annotations + +import hashlib +import json +from typing import Any, Mapping, Sequence + +from .eval_settlement import ABSTAIN, ACTIVE_FAMILIES, ATTESTS, DECISIONS, SETTLED_DECISIONS +from .identity_ledger import slices_of + +SCHEMA = "hyperlex.eval_routing.v1" +ROUTING_VERSION = "v1" +PROCEDURE_ID = "select_eval_routing_v1" +CLEARED_RIGHTS = "CC-BY-SA" +UNBIND_CLEAN_DERIVATION = "soft_ceiling.clean_surface" +TASKS = ("classify", "unbind") +REGISTRY_CLASSES = ATTESTS +LINEAGES = (ABSTAIN, *ACTIVE_FAMILIES) +ROUTING_STORE = ( + "private evaluation evidence store " + "eval-reserve-20260926/routing/events.jsonl" +) +_BANNED = frozenset( + { + "admission_floors", + "candidate_score", + "current_reserve_deficits", + "desired_select_slice", + "desired_slice", + "model_error", + "model_score", + "planning_targets", + "prediction", + "residual", + "reserve_counts", + "routed_counts", + "semantic_evidence", + "threshold_v2", + } +) +_SLICE_CODES = { + "classify": "SLICE_CLASSIFY", + "classify_non_none": "SLICE_CLASSIFY_NON_NONE", + "classify_observed": "SLICE_CLASSIFY_OBSERVED", + "unbind_clean": "SLICE_UNBIND_CLEAN", +} +RULE_TABLE = { + "class_meaning": "registry attest class OBSERVED or INFERRED, not the semantic family", + "cleared_rights": CLEARED_RIGHTS, + "classify": "task classify; decision ACCEPT, RECLASSIFY, or NONE", + "classify_non_none": "classify and lineage not empty and not none", + "classify_observed": "classify and registry class OBSERVED", + "lineage_meaning": "semantic family, including none", + "overlap": "one record may enter every classify slice it satisfies; unbind_clean requires task unbind", + "procedure_id": PROCEDURE_ID, + "routable_decisions": sorted(SETTLED_DECISIONS), + "unbind_clean": "task unbind and frozen soft_ceiling.clean_surface is true", + "unroutable_decisions": ["UNRESOLVED"], +} + + +def canonical_json(payload: Mapping[str, Any]) -> str: + return json.dumps(payload, indent=2, sort_keys=True, ensure_ascii=True) + "\n" + + +def sha256_text(text: str) -> str: + return hashlib.sha256(text.encode("utf-8")).hexdigest() + + +def settlement_hash(record: Mapping[str, Any]) -> str: + return sha256_text(canonical_json(record)) + + +def procedure_hash() -> str: + return sha256_text(canonical_json({"procedure_id": PROCEDURE_ID, "rules": RULE_TABLE})) + + +def _refuse_banned(value: Mapping[str, Any]) -> None: + found = sorted(_BANNED.intersection(value)) + if found: + raise SystemExit( + "REFUSE: routing must not read quota state or model output: " + ",".join(found) + ) + + +def _slices_for(task: str, registry_class: str, lineage: str, unbind_clean: bool) -> list[str]: + label: dict[str, Any] = { + "class": registry_class, + "lineage": lineage, + "task": task, + } + if task == "unbind": + label["unbind_clean"] = unbind_clean + return sorted(slices_of({"labels": [label]})) + + +def _base( + settlement: Mapping[str, Any], + metadata: Mapping[str, Any], + *, + status: str, + reasons: Sequence[str], + slices: Sequence[str], + task: str | None, + registry_class: str | None, + lineage: str | None, + unbind_clean: bool | None, +) -> dict[str, Any]: + text_hash = str(settlement.get("text_hash") or "") + row_id = str(settlement.get("row_id") or "") + return { + "class": registry_class, + "created_from_frozen_artifact": True, + "derivation_procedure_hash": procedure_hash(), + "derivation_procedure_id": PROCEDURE_ID, + "lineage": lineage, + "normalized_identity": text_hash, + "provenance_state": str(metadata.get("provenance_state") or "INCOMPLETE"), + "rights_state": str(settlement.get("rights") or ""), + "routing_reason_codes": sorted(set(reasons)), + "routing_status": status, + "routing_version": ROUTING_VERSION, + "row_id": row_id, + "schema": SCHEMA, + "settlement_decision": str(settlement.get("decision") or ""), + "settlement_registry_class": registry_class, + "slices": list(slices), + "source_identity": str(metadata.get("source_identity") or ""), + "source_settlement_hash": settlement_hash(settlement), + "source_settlement_id": row_id, + "task": task, + "unbind_clean": unbind_clean, + } + + +def derive_routing( + settlement: Mapping[str, Any], + metadata: Mapping[str, Any], +) -> dict[str, Any]: + """One settlement plus frozen metadata. No RNG and no quota read.""" + _refuse_banned(settlement) + _refuse_banned(metadata) + decision = settlement.get("decision") + if decision not in DECISIONS: + raise SystemExit(f"REFUSE: settlement decision is not canonical: {decision}") + text_hash = str(settlement.get("text_hash") or "") + row_id = str(settlement.get("row_id") or "") + if len(text_hash) != 64 or any(ch not in "0123456789abcdef" for ch in text_hash): + raise SystemExit("REFUSE: normalized identity is not sha256") + attest = settlement.get("attest") + family = settlement.get("semantic_family") + reasons: list[str] = [] + meta_row = str(metadata.get("row_id") or "") + if not row_id or meta_row != row_id or str(metadata.get("normalized_identity") or "") != text_hash: + reasons.append("IDENTITY_CONFLICT") + provenance_state = str(metadata.get("provenance_state") or "") + if ( + provenance_state != "COMPLETE" + or not str(metadata.get("source_identity") or "").strip() + or not str(settlement.get("provenance") or "").strip() + ): + reasons.append("PROVENANCE_INCOMPLETE") + if str(settlement.get("rights") or "") != CLEARED_RIGHTS: + reasons.append("RIGHTS_NOT_CLEARED") + if decision == "UNRESOLVED": + reasons.append("SETTLEMENT_UNRESOLVED") + elif decision not in SETTLED_DECISIONS: + reasons.append("SETTLEMENT_DECISION_NOT_ADMISSIBLE") + if "class" in metadata and metadata.get("class") != attest: + reasons.append("CLASS_PROMOTION_REFUSED") + if "lineage" in metadata and metadata.get("lineage") != family: + reasons.append("LINEAGE_CONFLICT") + task = metadata.get("task") + if task not in TASKS: + reasons.append("TASK_ABSENT") + task_out = None + else: + task_out = str(task) + registry_out = attest if attest in REGISTRY_CLASSES else None + lineage_out = family if isinstance(family, str) and family in LINEAGES else None + if decision in SETTLED_DECISIONS and registry_out is None: + reasons.append("REGISTRY_CLASS_ABSENT") + if decision in SETTLED_DECISIONS and lineage_out is None: + reasons.append("LINEAGE_ABSENT") + unbind_flag = metadata.get("unbind_clean") is True + derivation = metadata.get("unbind_clean_derivation") + if task_out == "classify" and unbind_flag: + reasons.append("UNBIND_CLEAN_TASK_MISMATCH") + if task_out == "unbind" and unbind_flag and derivation != UNBIND_CLEAN_DERIVATION: + reasons.append("UNBIND_CLEAN_DERIVATION_ABSENT") + clean_out = False + if task_out == "unbind" and unbind_flag and derivation == UNBIND_CLEAN_DERIVATION: + clean_out = True + if reasons: + return _base( + settlement, + metadata, + status="INELIGIBLE", + reasons=reasons, + slices=[], + task=task_out, + registry_class=registry_out, + lineage=lineage_out, + unbind_clean=None, + ) + assert task_out is not None and registry_out is not None and lineage_out is not None + slices = _slices_for(task_out, registry_out, lineage_out, clean_out) + if not slices: + return _base( + settlement, + metadata, + status="INELIGIBLE", + reasons=["NO_SELECT_SLICE"], + slices=[], + task=task_out, + registry_class=registry_out, + lineage=lineage_out, + unbind_clean=clean_out, + ) + return _base( + settlement, + metadata, + status="ELIGIBLE", + reasons=[_SLICE_CODES[name] for name in slices], + slices=slices, + task=task_out, + registry_class=registry_out, + lineage=lineage_out, + unbind_clean=clean_out, + ) + + +def derive_batch( + pairs: Sequence[tuple[Mapping[str, Any], Mapping[str, Any]]], +) -> list[dict[str, Any]]: + """First row_id wins. A later copy is a duplicate, not a second route.""" + seen: set[str] = set() + records = [] + for settlement, metadata in pairs: + row_id = str(settlement.get("row_id") or "") + if row_id and row_id in seen: + records.append( + _base( + settlement, + metadata, + status="INELIGIBLE", + reasons=["DUPLICATE_IDENTITY"], + slices=[], + task=None, + registry_class=settlement.get("attest") if settlement.get("attest") in REGISTRY_CLASSES else None, + lineage=settlement.get("semantic_family") + if settlement.get("semantic_family") in LINEAGES + else None, + unbind_clean=None, + ) + ) + continue + if row_id: + seen.add(row_id) + records.append(derive_routing(settlement, metadata)) + return records + + +def authorize_for_census(event: Mapping[str, Any], routing: Mapping[str, Any]) -> str: + """Census may use a routing record only when it still matches the settlement.""" + if routing.get("schema") != SCHEMA or routing.get("routing_status") != "ELIGIBLE": + return "ROUTING_INELIGIBLE" + if str(routing.get("row_id") or "") != str(event.get("row_id") or ""): + return "ROUTING_IDENTITY_MISMATCH" + if str(routing.get("normalized_identity") or "") != str(event.get("text_hash") or ""): + return "ROUTING_IDENTITY_MISMATCH" + if routing.get("source_settlement_hash") != settlement_hash(event): + return "ROUTING_HASH_MISMATCH" + if routing.get("rights_state") != CLEARED_RIGHTS: + return "ROUTING_INELIGIBLE" + if routing.get("created_from_frozen_artifact") is not True: + return "ROUTING_REJECTED" + task = routing.get("task") + registry_class = routing.get("class") + lineage = routing.get("lineage") + if task not in TASKS or registry_class not in REGISTRY_CLASSES or lineage not in LINEAGES: + return "ROUTING_SLICE_MISMATCH" + expected = _slices_for(str(task), str(registry_class), str(lineage), routing.get("unbind_clean") is True) + if list(routing.get("slices") or []) != expected or not expected: + return "ROUTING_SLICE_MISMATCH" + return "ELIGIBLE" + + +def reference_pairs() -> list[tuple[dict[str, Any], dict[str, Any]]]: + """Synthetic corpus. Not a harvest and not the 339 settlements.""" + specs = [ + ("accept-observed", "11", "ACCEPT", "ai-native", "OBSERVED", "CC-BY-SA", "COMPLETE", "classify", False, None), + ("accept-inferred", "22", "ACCEPT", "technology-ai", "INFERRED", "CC-BY-SA", "COMPLETE", "classify", False, None), + ("accept-non-none", "33", "ACCEPT", "politics-civic", "INFERRED", "CC-BY-SA", "COMPLETE", "classify", False, None), + ("accept-unbind", "44", "ACCEPT", "none", "INFERRED", "CC-BY-SA", "COMPLETE", "unbind", True, UNBIND_CLEAN_DERIVATION), + ("none-classify", "55", "NONE", "none", "INFERRED", "CC-BY-SA", "COMPLETE", "classify", False, None), + ("unresolved", "66", "UNRESOLVED", None, None, "CC-BY-SA", "COMPLETE", "classify", False, None), + ("reclassify", "77", "RECLASSIFY", "workplace-career", "INFERRED", "CC-BY-SA", "COMPLETE", "classify", False, None), + ("rights-blocked", "88", "ACCEPT", "ai-native", "OBSERVED", "RIGHTS_UNRESOLVED", "COMPLETE", "classify", False, None), + ("provenance-blocked", "99", "ACCEPT", "ai-native", "INFERRED", "CC-BY-SA", "INCOMPLETE", "classify", False, None), + ("identity-conflict", "ab", "ACCEPT", "ai-native", "INFERRED", "CC-BY-SA", "COMPLETE", "classify", False, None), + ] + pairs = [] + for name, nibble, decision, family, attest, rights, provenance_state, task, clean, derivation in specs: + digest = nibble * 32 + settlement: dict[str, Any] = { + "attest": attest, + "decision": decision, + "provenance": "routing-fixture", + "rights": rights, + "row_id": f"route-fixture-{name}", + "schema": "hyperlex.eval_settlement.v1", + "semantic_family": family, + "text_hash": digest, + } + metadata: dict[str, Any] = { + "normalized_identity": digest, + "provenance_state": provenance_state, + "row_id": settlement["row_id"], + "source_identity": f"fixture:{name}", + "task": task, + "unbind_clean": clean, + } + if name == "provenance-blocked": + metadata["source_identity"] = "" + if name == "identity-conflict": + metadata["normalized_identity"] = "cd" * 32 + if derivation is not None: + metadata["unbind_clean_derivation"] = derivation + pairs.append((settlement, metadata)) + inferred = { + "attest": "INFERRED", + "decision": "ACCEPT", + "provenance": "routing-fixture", + "rights": "CC-BY-SA", + "row_id": "route-fixture-promotion-refused", + "schema": "hyperlex.eval_settlement.v1", + "semantic_family": "ai-native", + "text_hash": "ef" * 32, + } + pairs.append( + ( + inferred, + { + "class": "OBSERVED", + "normalized_identity": inferred["text_hash"], + "provenance_state": "COMPLETE", + "row_id": inferred["row_id"], + "source_identity": "fixture:promotion-refused", + "task": "classify", + "unbind_clean": False, + }, + ) + ) + classify_as_unbind = { + "attest": "INFERRED", + "decision": "ACCEPT", + "provenance": "routing-fixture", + "rights": "CC-BY-SA", + "row_id": "route-fixture-classify-not-unbind", + "schema": "hyperlex.eval_settlement.v1", + "semantic_family": "ai-native", + "text_hash": "ba" * 32, + } + pairs.append( + ( + classify_as_unbind, + { + "normalized_identity": classify_as_unbind["text_hash"], + "provenance_state": "COMPLETE", + "row_id": classify_as_unbind["row_id"], + "source_identity": "fixture:classify-not-unbind", + "task": "classify", + "unbind_clean": True, + "unbind_clean_derivation": UNBIND_CLEAN_DERIVATION, + }, + ) + ) + return pairs + + +def semantic_errors(record: Mapping[str, Any], settlement: Mapping[str, Any]) -> list[str]: + """Schema plus the slice, rights, and settlement-hash checks.""" + from .select_contract_schema import routing_schema_errors + + errors = [f"schema: {item}" for item in routing_schema_errors(record)] + if record.get("source_settlement_hash") != settlement_hash(settlement): + errors.append("source settlement hash mismatch") + if record.get("row_id") != settlement.get("row_id"): + errors.append("row_id mismatch") + if record.get("normalized_identity") != settlement.get("text_hash"): + errors.append("normalized identity mismatch") + if record.get("settlement_decision") != settlement.get("decision"): + errors.append("decision mismatch") + status = record.get("routing_status") + if status == "ELIGIBLE": + if record.get("rights_state") != CLEARED_RIGHTS: + errors.append("eligible record is not rights-cleared") + if record.get("provenance_state") != "COMPLETE": + errors.append("eligible record is provenance-incomplete") + if record.get("settlement_decision") not in SETTLED_DECISIONS: + errors.append("eligible record is not a settled decision") + if record.get("class") != record.get("settlement_registry_class"): + errors.append("class drifted from the registry class") + if record.get("class") != settlement.get("attest"): + errors.append("class was promoted away from attest") + if record.get("lineage") != settlement.get("semantic_family"): + errors.append("lineage drifted from semantic_family") + expected = _slices_for( + str(record.get("task")), + str(record.get("class")), + str(record.get("lineage")), + record.get("unbind_clean") is True, + ) + if list(record.get("slices") or []) != expected: + errors.append("slices do not match slices_of") + if "classify_observed" in expected and record.get("class") != "OBSERVED": + errors.append("classify_observed without OBSERVED") + if "unbind_clean" in expected and record.get("unbind_clean") is not True: + errors.append("unbind_clean slice without the frozen flag") + if record.get("class") == "INFERRED" and "classify_observed" in expected: + errors.append("INFERRED entered classify_observed") + elif status == "INELIGIBLE": + if record.get("slices"): + errors.append("ineligible record carries slices") + if not record.get("routing_reason_codes"): + errors.append("ineligible record has no reason") + else: + errors.append("routing_status is not canonical") + return errors + + +def contract_validation( + pairs: Sequence[tuple[Mapping[str, Any], Mapping[str, Any]]] | None = None, +) -> dict[str, Any]: + """Run the reference corpus twice. Schema and semantic failures stay distinct.""" + corpus = list(pairs) if pairs is not None else reference_pairs() + first = [derive_routing(settlement, metadata) for settlement, metadata in corpus] + second = [derive_routing(settlement, metadata) for settlement, metadata in corpus] + if [canonical_json(row) for row in first] != [canonical_json(row) for row in second]: + return { + "determinism": "NOT_IDENTICAL", + "errors": ["reconstructions differed"], + "state": "ROUTING_NONDETERMINISTIC", + } + errors: list[str] = [] + schema_hit = False + for record, (settlement, _metadata) in zip(first, corpus): + found = semantic_errors(record, settlement) + schema_hit = schema_hit or any(item.startswith("schema:") for item in found) + errors.extend(f"{record['row_id']}: {item}" for item in found) + if errors and schema_hit: + state = "ROUTING_SCHEMA_FAILURE" + elif errors: + state = "ROUTING_SEMANTIC_VALIDATION_FAILURE" + else: + state = "ROUTING_VALIDATION_COMPLETE" + return { + "determinism": "IDENTICAL", + "errors": errors, + "procedure_id": PROCEDURE_ID, + "procedure_sha256": procedure_hash(), + "schema": SCHEMA, + "state": state, + } diff --git a/scripts/shadow/hyperlexical/residual_threshold_contract.py b/scripts/shadow/hyperlexical/residual_threshold_contract.py new file mode 100644 index 00000000..2798befe --- /dev/null +++ b/scripts/shadow/hyperlexical/residual_threshold_contract.py @@ -0,0 +1,319 @@ +"""Validate sealed residual-threshold artifacts against the contract schema family. + +VALIDATE_ONLY. A conformance gap is reported. Frozen bytes are not rewritten. +""" + +from __future__ import annotations + +import hashlib +import json +import sys +from pathlib import Path + +from jsonschema import Draft202012Validator +from referencing import Registry, Resource + +REPO = Path(__file__).resolve().parents[3] +SCHEMA_DIR = REPO / "specs/007-hyperlexical-model/schemas/hyperlex/residual-threshold" +SOURCE = Path("/home/morpheus/hlx-private/eval-reserve-20260926/operator-review/HLX-EVAL-UNBIND-SEMANTIC-EVIDENCE-SOURCE-V1-001") +LEDGER = Path("/home/morpheus/hlx-private/eval-reserve-20260926") +TRACKER = LEDGER / "operator-review/HLX-EVAL-UNBIND-SENSE-SCREEN-V1-HYPOTHESIS-001/HYPOTHESIS.json" +REPORT_PATH = SCHEMA_DIR / "CONTRACT_VALIDATION.json" + +EXPECTED_MEASUREMENT = "78ca09ab14912681d028e8b9b1c77a8daf1d0c8c81561434eb9b6ade45a753e5" +EXPECTED_EVENTS = "96b74a92d44f1cf9fe152b18e5207176f161ba3bfce528dac38aa4571a742f9c" +EXPECTED_LEDGER = "77e22433203879b252f7a9e309d2013d7550101d1c4a014b494d2c96df87d0e0" +EXPECTED_TRACKER = "e674c207d9eb160159a673c23f7e2a2e8262b5bfc963ccf81b216d8291dc4b0f" +GAP = "SCHEMA_CONFORMANCE_GAP" +SEMANTIC_GAP = "SCHEMA_SEMANTIC_VALIDATION_FAILURE" +PASS = "PASS" +AUTHORIZATION = "THRESHOLD_ARTIFACT_SCHEMA_HARDENING_AUTHORIZATION" + + +def sha256_file(path: Path) -> str: + return hashlib.sha256(path.read_bytes()).hexdigest() + + +def load_schemas(schema_dir: Path | None = None) -> dict[str, dict]: + directory = schema_dir or SCHEMA_DIR + loaded = {} + for path in sorted(directory.glob("*.schema.json")): + loaded[path.name] = json.loads(path.read_text(encoding="utf-8")) + return loaded + + +def schema_registry(schemas: dict[str, dict]) -> Registry: + registry = Registry() + for schema in schemas.values(): + registry = registry.with_resource(schema["$id"], Resource.from_contents(schema)) + return registry + + +def classify(name: str) -> str | None: + if name.endswith("_MANIFEST.jsonl"): + return "surface-manifest-row.schema.json" + if name.endswith("_SUPPORT_JOIN.jsonl"): + return "support-join-row.schema.json" + if name.endswith("_RESOLUTION.jsonl"): + return "resolution-row.schema.json" + if name.endswith("_SCORES.jsonl"): + return "score-row.schema.json" + if name.endswith("_OPERATOR_LABELS.jsonl") or name.endswith("_CALIBRATION_LABELS.jsonl"): + return "operator-label-row.schema.json" + if name.endswith("_COMPLETION_RECEIPT.json"): + return "completion-receipt.schema.json" + if name.endswith("_OPERATOR_LABELING_RECEIPT.json"): + return "operator-labeling-receipt.schema.json" + if name.endswith("_RESOLUTION_RECEIPT.json"): + return "resolution-receipt.schema.json" + if name.endswith("_SCORE_RECEIPT.json"): + return "score-receipt.schema.json" + if name.endswith("_DRAW_RECEIPT.json"): + return "surface-draw-receipt.schema.json" + if name.endswith("_SUPPORT_REASSESSMENT.json"): + return "support-reassessment.schema.json" + if name.endswith("_DIRECTION_ANALYSIS.json"): + return "direction-analysis.schema.json" + if name.endswith("_DIRECTION_DECISION.json"): + return "direction-decision.schema.json" + if name.endswith("_CONFOUND_ANALYSIS.json"): + return "confound-analysis.schema.json" + if name.endswith("_CONFOUND_DECISION.json"): + return "confound-decision.schema.json" + if name.endswith("_THRESHOLD_SEARCH.json"): + return "threshold-search.schema.json" + if name.endswith("_THRESHOLD_DECISION.json") or name.endswith("_THRESHOLD_FROZEN.json"): + return "threshold-decision.schema.json" + if name.endswith("_FAILURE.json"): + return "calibration-failure.schema.json" + return None + + +def _jsonl(path: Path) -> list[dict]: + return [json.loads(line) for line in path.read_text(encoding="utf-8").splitlines() if line.strip()] + + +def _redact(value: object) -> object: + if value is None or isinstance(value, (bool, int)): + return value + if isinstance(value, str) and " " not in value and len(value) <= 64: + return value + return {"redacted": type(value).__name__} + + +def public_errors(schema: dict, instance: object, registry: Registry) -> list[dict]: + validator = Draft202012Validator(schema, registry=registry) + found = [] + for error in sorted(validator.iter_errors(instance), key=lambda item: list(item.path)): + location = "/" + "/".join(str(part) for part in error.path) + found.append( + { + "expected_contract": error.validator, + "field_path": location, + "observed_value": _redact(error.instance), + } + ) + return found + + +def validate_artifact(path: Path, schema_name: str, schemas: dict[str, dict], registry: Registry) -> dict: + schema = schemas[schema_name] + errors: list[dict] = [] + if path.suffix == ".jsonl": + for index, row in enumerate(_jsonl(path), start=1): + for error in public_errors(schema, row, registry): + error["field_path"] = f"line {index} {error['field_path']}" + errors.append(error) + else: + errors.extend(public_errors(schema, json.loads(path.read_text(encoding="utf-8")), registry)) + return { + "artifact": path.name, + "errors": errors, + "historical_artifact_mutated": False, + "schema": schema_name, + "status": PASS if not errors else GAP, + } + + +def _check(name: str, ok: bool, detail: str) -> dict: + return {"check": name, "detail": detail, "status": PASS if ok else SEMANTIC_GAP} + + +def semantic_checks(source: Path, measurement_hash: str = EXPECTED_MEASUREMENT) -> list[dict]: + checks = [] + manifest_path = source / "RESIDUAL_THRESHOLD_V2_CALIBRATION_MANIFEST.jsonl" + label_path = source / "RESIDUAL_THRESHOLD_V2_OPERATOR_LABELS.jsonl" + join_path = source / "RESIDUAL_THRESHOLD_V2_CALIBRATION_SUPPORT_JOIN.jsonl" + measurement_path = source / "RESIDUAL_THRESHOLD_V2_MEASUREMENT_MANIFEST.jsonl" + if manifest_path.is_file() and label_path.is_file() and join_path.is_file(): + manifest = _jsonl(manifest_path) + labels = _jsonl(label_path) + join = _jsonl(join_path) + manifest_ids = [row["row_id"] for row in manifest] + label_ids = [row["row_id"] for row in labels] + join_ids = [row["row_id"] for row in join] + ready = sum(row["row_resolution_status"] == "RESIDUAL_READY" for row in join) + unknown = sum(row["row_resolution_status"] == "UNKNOWN" for row in join) + checks.append(_check("calibration_manifest_rows", len(manifest) == 200, f"rows={len(manifest)}")) + checks.append(_check("operator_label_rows", len(labels) == 200, f"rows={len(labels)}")) + checks.append(_check("support_join_rows", len(join) == 200, f"rows={len(join)}")) + checks.append(_check("ready_plus_unknown", ready + unknown == 200, f"ready={ready} unknown={unknown}")) + checks.append(_check( + "row_set_equality", + manifest_ids == label_ids == join_ids and len(set(manifest_ids)) == 200, + f"manifest={len(set(manifest_ids))} labels={len(set(label_ids))} join={len(set(join_ids))}", + )) + checks.append(_check( + "manifest_order", + [row["global_draw_order"] for row in manifest] == list(range(1, 201)), + "calibration global_draw_order", + )) + synsets = [row["pwn30_synset"] for row in manifest] + checks.append(_check("calibration_synset_unique", len(synsets) == len(set(synsets)), f"synsets={len(set(synsets))}")) + inventory: dict[str, int] = {} + for row in labels: + inventory[row["operator_label"]] = inventory.get(row["operator_label"], 0) + 1 + checks.append(_check("label_inventory_total", sum(inventory.values()) == 200, f"total={sum(inventory.values())}")) + if measurement_path.is_file() and manifest_path.is_file(): + measurement = _jsonl(measurement_path) + manifest = _jsonl(manifest_path) + measurement_ids = {row["row_id"] for row in measurement} + manifest_ids = {row["row_id"] for row in manifest} + measurement_synsets = {row["pwn30_synset"] for row in measurement} + manifest_synsets = {row["pwn30_synset"] for row in manifest} + digest = sha256_file(measurement_path) + checks.append(_check("measurement_rows", len(measurement) == 200, f"rows={len(measurement)}")) + checks.append(_check("measurement_synset_unique", len(measurement_synsets) == len(measurement), f"synsets={len(measurement_synsets)}")) + checks.append(_check("synset_set_isolation", measurement_synsets.isdisjoint(manifest_synsets), "calibration and measurement synsets")) + checks.append(_check("measurement_row_isolation", measurement_ids.isdisjoint(manifest_ids), "calibration and measurement row ids")) + checks.append(_check("measurement_manifest_unchanged", digest == measurement_hash, digest)) + checks.append(_check( + "measurement_order", + [row["global_draw_order"] for row in measurement] == list(range(201, 401)), + "measurement global_draw_order", + )) + join_receipt_path = source / "RESIDUAL_THRESHOLD_V2_CALIBRATION_SUPPORT_JOIN_RECEIPT.json" + if join_receipt_path.is_file(): + receipt = json.loads(join_receipt_path.read_text(encoding="utf-8")) + pairs = { + "join_receipt_manifest_hash": (receipt.get("manifest_hash"), manifest_path), + "join_receipt_label_hash": (receipt.get("operator_label_hash"), label_path), + "join_receipt_score_hash": (receipt.get("score_hash"), source / "RESIDUAL_THRESHOLD_V2_CALIBRATION_SCORES.jsonl"), + "join_receipt_resolution_hash": (receipt.get("resolution_hash"), source / "RESIDUAL_THRESHOLD_V2_CALIBRATION_RESOLUTION.jsonl"), + } + for name, (claimed, target) in pairs.items(): + actual = sha256_file(target) if target.is_file() else "" + checks.append(_check(name, claimed == actual and actual != "", claimed or "")) + draw_path = source / "RESIDUAL_THRESHOLD_V2_SURFACE_DRAW_RECEIPT.json" + if draw_path.is_file() and manifest_path.is_file(): + draw = json.loads(draw_path.read_text(encoding="utf-8")) + checks.append(_check( + "draw_receipt_measurement_hash", + draw.get("measurement_manifest_sha256") == measurement_hash, + draw.get("measurement_manifest_sha256", ""), + )) + checks.append(_check( + "draw_receipt_manifest_hash", + draw.get("calibration_manifest_sha256") == sha256_file(manifest_path), + draw.get("calibration_manifest_sha256", ""), + )) + direction_path = source / "RESIDUAL_THRESHOLD_V2_DIRECTION_ANALYSIS.json" + if direction_path.is_file() and join_path.is_file(): + direction = json.loads(direction_path.read_text(encoding="utf-8")) + checks.append(_check( + "direction_join_hash", + direction.get("source_support_join_hash") == sha256_file(join_path), + direction.get("source_support_join_hash", ""), + )) + resolution_receipt = source / "RESIDUAL_THRESHOLD_V2_CALIBRATION_RESOLUTION_RECEIPT.json" + if resolution_receipt.is_file(): + payload = json.loads(resolution_receipt.read_text(encoding="utf-8")) + parents = payload.get("parent_rows") + ready = payload.get("residual_ready_rows") + unknown = payload.get("unknown_rows") + checks.append(_check( + "resolution_parent_totals", + isinstance(parents, list) and len(parents) == 200 and ready + unknown == 200, + f"parents={len(parents) if isinstance(parents, list) else 'invalid'} ready={ready} unknown={unknown}", + )) + return checks + + +def evidence_hashes(source: Path) -> dict[str, str]: + if not source.is_dir(): + return {} + return { + path.name: sha256_file(path) + for path in sorted(source.iterdir()) + if path.is_file() and path.name.startswith("RESIDUAL_THRESHOLD_") + } + + +def validate_directory(source: Path, schemas: dict[str, dict] | None = None) -> dict: + loaded = schemas or load_schemas() + registry = schema_registry(loaded) + before = evidence_hashes(source) + results = [] + skipped = [] + if source.is_dir(): + for path in sorted(source.iterdir()): + if not path.is_file() or not path.name.startswith("RESIDUAL_THRESHOLD_"): + continue + schema_name = classify(path.name) + if schema_name is None: + skipped.append(path.name) + continue + results.append(validate_artifact(path, schema_name, loaded, registry)) + semantic = semantic_checks(source) + else: + semantic = [_check("private_reserve", False, "reserve is absent")] + after = evidence_hashes(source) + changed = sorted(name for name, digest in before.items() if after.get(name) != digest) + schema_gaps = [row["artifact"] for row in results if row["status"] != PASS] + semantic_gaps = [row["check"] for row in semantic if row["status"] != PASS] + if changed: + outcome = SEMANTIC_GAP + elif schema_gaps: + outcome = GAP + elif semantic_gaps: + outcome = SEMANTIC_GAP + else: + outcome = "HISTORICAL_VALIDATION_COMPLETE" + return { + "authorization": AUTHORIZATION, + "calibration_evidence_modified": bool(changed), + "changed_artifacts": changed, + "historical_artifact_mutated": False, + "mode": "VALIDATE_ONLY", + "outcome": outcome, + "results": results, + "schema_conformance_gaps": schema_gaps, + "scientific_remediation": False, + "semantic": semantic, + "semantic_conformance_gaps": semantic_gaps, + "skipped_not_applicable": skipped, + "threshold_decision_emitted": any(row["schema"] == "threshold-decision.schema.json" for row in results), + "threshold_redesign": False, + "training_authorized": False, + } + + +def render_report(report: dict) -> str: + return json.dumps(report, indent=2, sort_keys=True, ensure_ascii=True) + "\n" + + +def write_public_report(report: dict) -> None: + if "hlx-private" in str(REPORT_PATH) or REPORT_PATH.name != "CONTRACT_VALIDATION.json": + raise RuntimeError("contract report path is not the public schema directory") + REPORT_PATH.write_text(render_report(report), encoding="utf-8") + + +def main() -> None: + report = validate_directory(SOURCE) + if "--write" in sys.argv: + write_public_report(report) + print(render_report(report)) + + +if __name__ == "__main__": + main() From 92431b6553a05d63df8c428ae52af8721a3a084a Mon Sep 17 00:00:00 2001 From: Daniel Meyer Date: Mon, 28 Sep 2026 16:17:24 -0700 Subject: [PATCH 16/42] feat(007): land SELECT residual and schedule work (15) --- .../shadow/hyperlexical/select_005_reserve.py | 531 ++++++++++++++++++ 1 file changed, 531 insertions(+) create mode 100644 scripts/shadow/hyperlexical/select_005_reserve.py diff --git a/scripts/shadow/hyperlexical/select_005_reserve.py b/scripts/shadow/hyperlexical/select_005_reserve.py new file mode 100644 index 00000000..f1c0101e --- /dev/null +++ b/scripts/shadow/hyperlexical/select_005_reserve.py @@ -0,0 +1,531 @@ +"""SELECT-005 fresh-reserve census. Dry run only. + +This module does not train, score a candidate, append the identity ledger, +or seal a preregistration. ``admit`` remains the writer. A census that cannot +fill every required slice freezes no reserve rows. +""" + +from __future__ import annotations + +import hashlib +import json +from collections import Counter +from pathlib import Path +from typing import Any, Mapping, Sequence + +from .identity_ledger import ( + GENERATION, + PLANNING_TARGETS, + REQUIRED_SLICES, + derived_state, + slices_of, +) + +EXPERIMENT_ID = "HLX-EXP-2026-09-27-SELECT-005" +TRANSITION = "SELECT_005_RESERVE_AND_ADMISSION_PREPARATION_AUTHORIZATION" +BATCH_ID = "HLX-EVAL-RESERVE-SELECT-005-CENSUS-001" +ADMISSIBLE_DECISIONS = frozenset({"ACCEPT", "RECLASSIFY", "NONE"}) +CLEARED_RIGHTS = "CC-BY-SA" +REQUIRED_PROVENANCE = ("row_id", "text_hash", "rights", "decision", "provenance") +ADMISSION_FLOORS = {key: 1 for key in REQUIRED_SLICES} +FAILURE_QUOTA = "RESERVE_QUOTA_UNFILLED" +FAILURE_ISOLATION = "RESERVE_ISOLATION_FAILURE" +FAILURE_PROVENANCE = "RESERVE_PROVENANCE_FAILURE" +SELECT_ADMISSION_JSON_SCHEMA_EXISTS = True +TRAINING_AUTHORIZED = False + +# The written SELECT-005 proposal. Unset trainer fields stay unset. +PROPOSED_SCHEDULE: dict[str, Any] = { + "baseline": { + "HYPERLEX_EARLY_STOP": "0", + "HYPERLEX_EARLY_STOP_MIN_EPOCHS": None, + "HYPERLEX_EARLY_STOP_PATIENCE": None, + "HYPERLEX_TRAIN_EPOCHS": "40", + }, + "candidate": { + "HYPERLEX_EARLY_STOP": "1", + "HYPERLEX_EARLY_STOP_MIN_EPOCHS": "4", + "HYPERLEX_EARLY_STOP_PATIENCE": "4", + "HYPERLEX_TRAIN_EPOCHS": "12", + }, + "checkpoint_rule": { + "restore_best": True, + "strict_increase": True, + "ties": "keep_earlier", + }, + "experiment_id": EXPERIMENT_ID, + "pinned_export_rows": 9150, + "pinned_export_sha256": "64b7d3dede25047cb6dd2e5b663f7fa72946ec82ac1a8816ae34622d1aaac430", + "scientific_variable": "train_schedule", + "sealed": False, + "selection_metric": "classify_macro_f1_nonnone", + "unset_fields": [ + "batch_size", + "gradient_accumulation", + "learning_rate", + "seed", + ], + "warm_start_name": "hyperlex-encoder-modernbert-base-seed-morph65", +} + + +def canonical_json(payload: Mapping[str, Any]) -> str: + return json.dumps(payload, indent=2, sort_keys=True, ensure_ascii=True) + "\n" + + +def sha256_text(text: str) -> str: + return hashlib.sha256(text.encode("utf-8")).hexdigest() + + +def schedule_spec_sha256() -> str: + return sha256_text(canonical_json(PROPOSED_SCHEDULE)) + + +def _order_key(digest: str, batch_id: str = BATCH_ID) -> str: + return hashlib.sha256(f"{GENERATION}|{batch_id}|{digest}".encode("utf-8")).hexdigest() + + +def _slices(event: Mapping[str, Any]) -> set[str]: + """Slice labels only when the row already carries them. Attest is not a class.""" + task = str(event.get("task") or "") + if not task: + return set() + label: dict[str, Any] = { + "task": task, + "class": str(event.get("class") or ""), + "lineage": str(event.get("lineage") or ""), + "split": str(event.get("split") or ""), + } + if task == "unbind": + label["unbind_clean"] = event.get("unbind_clean") is True + return slices_of({"labels": [label]}) + + +def exclusion_reason( + event: Mapping[str, Any], + ledger_state: Mapping[str, str], + export_hashes: set[str], +) -> str: + """One reason. Provenance, then existing consumption, then decision and rights.""" + if any(not str(event.get(key) or "").strip() for key in REQUIRED_PROVENANCE): + return "PROVENANCE_INCOMPLETE" + digest = str(event["text_hash"]) + state = ledger_state.get(digest) + if state: + return state + if digest in export_hashes: + return "PINNED_EXPORT" + decision = str(event.get("decision") or "") + if decision == "UNRESOLVED": + return "DECISION_UNRESOLVED" + rights = str(event.get("rights") or "") + if rights != CLEARED_RIGHTS: + return "RIGHTS_UNRESOLVED" if rights == "RIGHTS_UNRESOLVED" else "RIGHTS_NOT_CLEARED" + if decision not in ADMISSIBLE_DECISIONS: + return "DECISION_NOT_ADMISSIBLE" + if not _slices(event): + return "SLICE_LABELS_ABSENT" + return "ELIGIBLE" + + +def _fence_counts( + rows: Sequence[Mapping[str, Any]], + fences: Mapping[str, Mapping[str, set[str]]], +) -> dict[str, dict[str, int]]: + counts: dict[str, dict[str, int]] = {} + for name, fence in fences.items(): + hashes = fence.get("hashes") or set() + row_ids = fence.get("row_ids") or set() + synsets = fence.get("synsets") or set() + sources = fence.get("source_ids") or set() + hash_hits = 0 + row_hits = 0 + synset_hits = 0 + source_hits = 0 + for row in rows: + if str(row.get("text_hash") or "") in hashes: + hash_hits += 1 + if str(row.get("row_id") or "") in row_ids: + row_hits += 1 + synset = str(row.get("pwn30_synset") or "") + if synset and synset in synsets: + synset_hits += 1 + source = str(row.get("source_identity") or "") + if source and source in sources: + source_hits += 1 + counts[name] = { + "normalized_identity": hash_hits, + "row_id": row_hits, + "source_identity": source_hits, + "synset": synset_hits, + } + return counts + + +def census( + events: Sequence[Mapping[str, Any]], + ledger_state: Mapping[str, str], + export_hashes: set[str], + *, + fences: Mapping[str, Mapping[str, set[str]]] | None = None, + batch_id: str = BATCH_ID, + routing_by_row: Mapping[str, Mapping[str, Any]] | None = None, +) -> dict[str, Any]: + """Route a settlement universe twice and freeze a reserve only when it qualifies. + + ``routing_by_row`` supplies derivative SELECT routing records. Omitting it + preserves the embedded-slice census. Routing is not a quota input. + """ + first = _once(events, ledger_state, export_hashes, fences or {}, batch_id, routing_by_row) + second = _once(events, ledger_state, export_hashes, fences or {}, batch_id, routing_by_row) + if first != second: + failed = dict(first) + failed["determinism"] = "NOT_DETERMINISTIC" + failed["failure"] = "NOT_DETERMINISTIC" + failed["reserve_rows"] = [] + failed["reserve_manifest_sha256"] = None + return failed + first["determinism"] = "IDENTICAL" + return first + + +def _census_reason( + event: Mapping[str, Any], + ledger_state: Mapping[str, str], + export_hashes: set[str], + routing_by_row: Mapping[str, Mapping[str, Any]] | None, +) -> tuple[str, list[str]]: + """Settlement exclusions stay first. Routing fills slices the settlement lacks.""" + reason = exclusion_reason(event, ledger_state, export_hashes) + embedded = sorted(_slices(event)) + if routing_by_row is None: + return reason, embedded + routing = routing_by_row.get(str(event.get("row_id") or "")) + if embedded and routing is not None and list(routing.get("slices") or []) != embedded: + return "ROUTING_SLICE_MISMATCH", [] + if reason != "SLICE_LABELS_ABSENT": + return reason, embedded + if routing is None: + return "SLICE_LABELS_ABSENT", [] + from .eval_routing import authorize_for_census + + routed = authorize_for_census(event, routing) + if routed != "ELIGIBLE": + return routed, [] + return "ELIGIBLE", list(routing.get("slices") or []) + + +def _once( + events: Sequence[Mapping[str, Any]], + ledger_state: Mapping[str, str], + export_hashes: set[str], + fences: Mapping[str, Mapping[str, set[str]]], + batch_id: str, + routing_by_row: Mapping[str, Mapping[str, Any]] | None = None, +) -> dict[str, Any]: + reasons: Counter[str] = Counter() + eligible: list[dict[str, Any]] = [] + for event in events: + reason, slices = _census_reason(event, ledger_state, export_hashes, routing_by_row) + reasons[reason] += 1 + if reason != "ELIGIBLE": + continue + digest = str(event["text_hash"]) + eligible.append( + { + "order_key": _order_key(digest, batch_id), + "pwn30_synset": str(event.get("pwn30_synset") or ""), + "rights": str(event.get("rights") or ""), + "row_id": str(event["row_id"]), + "slices": slices, + "source_identity": str(event.get("source_identity") or ""), + "text_hash": digest, + } + ) + eligible.sort(key=lambda row: (row["order_key"], row["text_hash"], row["row_id"])) + counts = {key: 0 for key in REQUIRED_SLICES} + routed: list[dict[str, Any]] = [] + for row in eligible: + served = set(row["slices"]) + if any(counts[key] < int(PLANNING_TARGETS[key]) for key in served): + routed.append(row) + for key in served: + counts[key] += 1 + fence_counts = _fence_counts(routed, fences) + isolation_hit = any( + value + for item in fence_counts.values() + for value in item.values() + ) + floors_met = all(counts[key] >= ADMISSION_FLOORS[key] for key in REQUIRED_SLICES) + if isolation_hit: + failure = FAILURE_ISOLATION + frozen: list[dict[str, Any]] = [] + frozen_counts = {key: 0 for key in REQUIRED_SLICES} + elif not floors_met: + failure = FAILURE_QUOTA + frozen = [] + frozen_counts = {key: 0 for key in REQUIRED_SLICES} + else: + failure = None + frozen = routed + frozen_counts = counts + manifest = None + if frozen: + manifest = { + "batch_id": batch_id, + "experiment_id": EXPERIMENT_ID, + "rows": [ + { + "rights": row["rights"], + "row_id": row["row_id"], + "slices": row["slices"], + "text_hash": row["text_hash"], + } + for row in frozen + ], + "schema": "hyperlex.select_reserve_manifest.v1", + } + return { + "admission_floors": dict(ADMISSION_FLOORS), + "batch_id": batch_id, + "eligible_count": len(eligible), + "eligible_order": [row["text_hash"] for row in eligible], + "exclusion_counts": dict(sorted(reasons.items())), + "experiment_id": EXPERIMENT_ID, + "failure": failure, + "frozen_slice_counts": frozen_counts, + "isolation": fence_counts, + "planning_targets": dict(PLANNING_TARGETS), + "reserve_manifest_sha256": sha256_text(canonical_json(manifest)) if manifest else None, + "reserve_row_count": len(frozen), + "reserve_rows": [ + {"row_id": row["row_id"], "slices": row["slices"], "text_hash": row["text_hash"]} + for row in frozen + ], + "routed_before_freeze_gate": len(routed), + "routed_slice_counts": counts, + "source_universe_count": len(events), + "training_authorized": TRAINING_AUTHORIZED, + } + + +def spent_reserve_hashes(records: Sequence[Mapping[str, Any]]) -> set[str]: + """EVAL_SPENT identities that still carry ``evaluation_reserved``. + + SELECT-004's 491 identities moved to EVAL_SPENT without a new binding + entry. The historical flag is the membership evidence. SELECT-001 and + SELECT-002 bindings are not this set. + """ + found: set[str] = set() + for record in records: + if derived_state(record) != "EVAL_SPENT" or not record.get("evaluation_reserved"): + continue + digest = str(record.get("normalized_text_sha256") or "") + if digest: + found.add(digest) + return found + + +def ledger_state_index(records: Sequence[Mapping[str, Any]]) -> dict[str, str]: + index: dict[str, str] = {} + for record in records: + digest = str(record.get("normalized_text_sha256") or "") + if digest: + index[digest] = derived_state(record) + return index + + +def unsettled_screen_counts(rows: Sequence[Mapping[str, Any]]) -> dict[str, int]: + """Operator-review packets are not a settlement universe.""" + counts: Counter[str] = Counter() + for row in rows: + if str(row.get("operator_decision") or "").strip(): + counts["OPERATOR_DECISION_PRESENT"] += 1 + else: + counts["OPERATOR_DECISION_ABSENT"] += 1 + bucket = str(row.get("bucket") or "UNBUCKETED") + counts[f"bucket:{bucket}"] += 1 + counts["rows"] = len(rows) + return dict(sorted(counts.items())) + + +def _read_jsonl(path: Path) -> list[dict[str, Any]]: + rows: list[dict[str, Any]] = [] + with path.open(encoding="utf-8") as handle: + for line in handle: + line = line.strip() + if line: + payload = json.loads(line) + if isinstance(payload, dict): + rows.append(payload) + return rows + + +def _identity_fence(rows: Sequence[Mapping[str, Any]]) -> dict[str, set[str]]: + return { + "hashes": {str(row.get("normalized_text_sha256") or "") for row in rows if row.get("normalized_text_sha256")}, + "row_ids": {str(row.get("row_id") or "") for row in rows if row.get("row_id")}, + "source_ids": {str(row.get("source_identity") or "") for row in rows if row.get("source_identity")}, + "synsets": {str(row.get("pwn30_synset") or "") for row in rows if row.get("pwn30_synset")}, + } + + +def _write_private(path: Path, payload: Mapping[str, Any]) -> str: + if "hlx-private" not in path.parts: + raise SystemExit("REFUSE: SELECT-005 census evidence stays under hlx-private") + text = canonical_json(payload) + path.parent.mkdir(mode=0o700, parents=True, exist_ok=True) + path.write_text(text, encoding="utf-8") + path.chmod(0o600) + return sha256_text(text) + + +def write_live_census( + *, + ledger_dir: Path, + settlement_events: Path, + export_path: Path, + review_candidates: Path, + calibration_manifest: Path, + measurement_manifest: Path, + calibration_v1_manifest: Path, + destination: Path, +) -> dict[str, str]: + """Re-census the live settlement stream and freeze the failure. No ledger append.""" + from .holdout_guard import normalized_text_sha256 + + ledger_payload = json.loads(ledger_dir.joinpath("ledger.json").read_text(encoding="utf-8")) + records = ledger_payload["identities"] + states = ledger_state_index(records) + state_totals: Counter[str] = Counter(states.values()) + bound_select = { + str(record.get("normalized_text_sha256") or "") + for record in records + if any("SELECT" in str(item) for item in record.get("experiment_bindings") or []) + and record.get("normalized_text_sha256") + } + spent_reserve = spent_reserve_hashes(records) + export_hashes: set[str] = set() + export_rows = 0 + for row in _read_jsonl(export_path): + export_rows += 1 + export_hashes.add(normalized_text_sha256(str(row.get("text") or ""))) + events = _read_jsonl(settlement_events) + fences = { + "prior_select_reserve": { + "hashes": set(spent_reserve), + "row_ids": set(), + "source_ids": set(), + "synsets": set(), + }, + "select_001_002_bindings": { + "hashes": set(bound_select), + "row_ids": set(), + "source_ids": set(), + "synsets": set(), + }, + "threshold_v1_calibration": _identity_fence(_read_jsonl(calibration_v1_manifest)), + "threshold_v2_calibration": _identity_fence(_read_jsonl(calibration_manifest)), + "threshold_v2_measurement": _identity_fence(_read_jsonl(measurement_manifest)), + "train_export": { + "hashes": set(export_hashes), + "row_ids": set(), + "source_ids": set(), + "synsets": set(), + }, + } + report = census(events, states, export_hashes, fences=fences) + review = unsettled_screen_counts(_read_jsonl(review_candidates)) + # Novel settlements are the rows the quota would have to come from. + novel = [ + row + for row in events + if str(row.get("text_hash") or "") not in states and str(row.get("text_hash") or "") not in export_hashes + ] + novel_fences = _fence_counts(novel, fences) + novel_decision_rights: Counter[str] = Counter( + f"{row.get('decision')}|{row.get('rights')}" for row in novel + ) + body = { + "active_reserve_identities": state_totals.get("EVAL_RESERVE", 0) + state_totals.get("AVAILABLE", 0), + "batch_id": report["batch_id"], + "determinism": report["determinism"], + "eligible_count": report["eligible_count"], + "exclusion_counts": report["exclusion_counts"], + "experiment_id": EXPERIMENT_ID, + "export_rows": export_rows, + "export_unique_hashes": len(export_hashes), + "failure": report["failure"], + "ledger_identity_count": len(states), + "ledger_state_counts": dict(sorted(state_totals.items())), + "novel_decision_rights": dict(sorted(novel_decision_rights.items())), + "novel_settlement_count": len(novel), + "novel_settlement_fence_overlaps": novel_fences, + "planning_targets_are_not_the_floor": True, + "reserve_manifest_sha256": report["reserve_manifest_sha256"], + "reserve_row_count": report["reserve_row_count"], + "review_packet_is_not_a_settlement": review, + "routed_before_freeze_gate": report["routed_before_freeze_gate"], + "routed_slice_counts": report["routed_slice_counts"], + "schema": "hyperlex.select_005_reserve_census.v1", + "select_001_002_bound_identities": len(bound_select), + "select_004_spent_reserve_identities": len(spent_reserve), + "source_universe": "settlement/events.jsonl", + "source_universe_count": report["source_universe_count"], + "training_authorized": False, + "transition": TRANSITION, + "unbind_source_disposition": "WORDNET_NOT_ADMITTED_ALONE", + "wordnet_rows_admitted": 0, + } + isolation = { + "experiment_id": EXPERIMENT_ID, + "failure": report["failure"], + "final_reserve_overlaps": report["isolation"], + "novel_settlement_fence_overlaps": novel_fences, + "reserve_row_count": report["reserve_row_count"], + "schema": "hyperlex.select_005_isolation_report.v1", + "train_overlap_count": 0, + } + provenance = { + "admitted_rows": 0, + "experiment_id": EXPERIMENT_ID, + "required_fields": list(REQUIRED_PROVENANCE), + "result": "NO_ADMITTED_ROWS", + "schema": "hyperlex.select_005_provenance_report.v1", + "source_universe_missing_required_provenance": report["exclusion_counts"].get("PROVENANCE_INCOMPLETE", 0), + } + destination.mkdir(mode=0o700, parents=True, exist_ok=True) + census_hash = _write_private(destination / "RESERVE_CENSUS.json", body) + isolation_hash = _write_private(destination / "ISOLATION_REPORT.json", isolation) + provenance_hash = _write_private(destination / "PROVENANCE_REPORT.json", provenance) + failure = { + "admission_receipt_created": False, + "admission_result": report["failure"], + "census_sha256": census_hash, + "decision_threshold_state": "BLOCKED_PENDING_OPERATOR_AUTHORIZATION", + "experiment_id": EXPERIMENT_ID, + "failure": report["failure"], + "isolation_sha256": isolation_hash, + "next_legal_transition": "NONE", + "preregistration_sealed": False, + "provenance_sha256": provenance_hash, + "reserve_manifest_created": False, + "schedule_sealed": False, + "schedule_spec_sha256": schedule_spec_sha256(), + "schema": "hyperlex.select_005_admission_failure.v1", + "select_admission_json_schema_exists": SELECT_ADMISSION_JSON_SCHEMA_EXISTS, + "status": report["failure"], + "training_authorized": False, + "training_executed": False, + "training_launch_authorized": False, + "transition": TRANSITION, + "weights_mutated": False, + } + failure_hash = _write_private(destination / "ADMISSION_FAILURE.json", failure) + return { + "admission_failure_sha256": failure_hash, + "census_sha256": census_hash, + "isolation_sha256": isolation_hash, + "provenance_sha256": provenance_hash, + } From 18f35bc29cc4d023ea2f5e351d0e79b2db9ed86c Mon Sep 17 00:00:00 2001 From: Daniel Meyer Date: Mon, 28 Sep 2026 16:21:16 -0700 Subject: [PATCH 17/42] feat(007): land SELECT residual and schedule work (16) --- scripts/shadow/hyperlexical/residual_threshold_v2_direction.py | 1 + 1 file changed, 1 insertion(+) create mode 100644 scripts/shadow/hyperlexical/residual_threshold_v2_direction.py diff --git a/scripts/shadow/hyperlexical/residual_threshold_v2_direction.py b/scripts/shadow/hyperlexical/residual_threshold_v2_direction.py new file mode 100644 index 00000000..311c8dd0 --- /dev/null +++ b/scripts/shadow/hyperlexical/residual_threshold_v2_direction.py @@ -0,0 +1 @@ +PLACEHOLDER \ No newline at end of file From 302e5520afe3079a8cfd5fbb5961faa34a6f564c Mon Sep 17 00:00:00 2001 From: Daniel Meyer Date: Mon, 28 Sep 2026 16:24:54 -0700 Subject: [PATCH 18/42] feat(007): land SELECT residual and schedule work (17) --- .../residual_threshold_v2_direction.py | 493 +++++++++++++++++- 1 file changed, 492 insertions(+), 1 deletion(-) diff --git a/scripts/shadow/hyperlexical/residual_threshold_v2_direction.py b/scripts/shadow/hyperlexical/residual_threshold_v2_direction.py index 311c8dd0..c6066ee3 100644 --- a/scripts/shadow/hyperlexical/residual_threshold_v2_direction.py +++ b/scripts/shadow/hyperlexical/residual_threshold_v2_direction.py @@ -1 +1,492 @@ -PLACEHOLDER \ No newline at end of file +"""Apply the frozen direction rule to the sealed v2 support join. + +This pass reads residual strings that are already on the join. It does not +recompute a residual, change a label, or continue into confound review or +threshold search. +""" + +from __future__ import annotations + +import hashlib +import json +import sys +from pathlib import Path + +from hyperlexical.residual_model_resolved_replay_v1 import ( + INVERTED, + NO_SEPARATION, + SUPPORTED, + direction_result, + pair_comparison, +) + +LEDGER = Path("/home/morpheus/hlx-private/eval-reserve-20260926") +SOURCE = LEDGER / "operator-review/HLX-EVAL-UNBIND-SEMANTIC-EVIDENCE-SOURCE-V1-001" +SENSE = LEDGER / "operator-review/HLX-EVAL-UNBIND-SENSE-SCREEN-V1-HYPOTHESIS-001" +EVENTS = LEDGER / "events.jsonl" +LEDGER_FILE = LEDGER / "ledger.json" +TRACKER = SENSE / "HYPOTHESIS.json" +SPEC = Path("/home/morpheus/Hyperlex/specs/007-hyperlexical-model/evaluation-reserve.md") + +JOIN_PATH = SOURCE / "RESIDUAL_THRESHOLD_V2_CALIBRATION_SUPPORT_JOIN.jsonl" +JOIN_RECEIPT_PATH = SOURCE / "RESIDUAL_THRESHOLD_V2_CALIBRATION_SUPPORT_JOIN_RECEIPT.json" +REASSESSMENT_PATH = SOURCE / "RESIDUAL_THRESHOLD_V2_CALIBRATION_SUPPORT_REASSESSMENT.json" +ANALYSIS_PATH = SOURCE / "RESIDUAL_THRESHOLD_V2_DIRECTION_ANALYSIS.json" +DECISION_PATH = SOURCE / "RESIDUAL_THRESHOLD_V2_DIRECTION_DECISION.json" +CALIBRATION_MANIFEST = SOURCE / "RESIDUAL_THRESHOLD_V2_CALIBRATION_MANIFEST.jsonl" +MEASUREMENT_MANIFEST = SOURCE / "RESIDUAL_THRESHOLD_V2_MEASUREMENT_MANIFEST.jsonl" +SCORES_PATH = SOURCE / "RESIDUAL_THRESHOLD_V2_CALIBRATION_SCORES.jsonl" +SCORE_RECEIPT = SOURCE / "RESIDUAL_THRESHOLD_V2_CALIBRATION_SCORE_RECEIPT.json" +RESOLUTION_PATH = SOURCE / "RESIDUAL_THRESHOLD_V2_CALIBRATION_RESOLUTION.jsonl" +RESOLUTION_RECEIPT = SOURCE / "RESIDUAL_THRESHOLD_V2_CALIBRATION_RESOLUTION_RECEIPT.json" +FAILURE_PATH = SOURCE / "RESIDUAL_THRESHOLD_V2_CALIBRATION_FAILURE.json" +LABELS_PATH = SOURCE / "RESIDUAL_THRESHOLD_V2_OPERATOR_LABELS.jsonl" +LABEL_RECEIPT_PATH = SOURCE / "RESIDUAL_THRESHOLD_V2_OPERATOR_LABELING_RECEIPT.json" +PREREGISTRATION = SOURCE / "RESIDUAL_THRESHOLD_V2_PREREGISTRATION.json" +SELECTION_PROCEDURE = SOURCE / "RESIDUAL_THRESHOLD_V2_SELECTION_PROCEDURE.json" + +AUTHORIZATION = "THRESHOLD_V2_DIRECTION_EVALUATION_AUTHORIZATION" +EVALUATION_ID = "THRESHOLD_V2_DIRECTION_EVALUATION_001" +EXECUTION_ID = "THRESHOLD_V2_CALIBRATION_EXECUTION_001" +SUPPORT_STATE = "SUPPORT_GATE_PASSED" +ANALYSIS_SCOPE = "DIRECTION_ONLY" +UNCERTAINTY = "UNCERTAINTY_INTERVAL_NOT_COMPUTED" +CONFOUND_NEXT = "THRESHOLD_V2_CONFOUND_EVALUATION_AUTHORIZATION" +READY = "RESIDUAL_READY" +CLASSES = ("HIGH", "SECONDARY", "REJECT", "QUARANTINE", "UNRESOLVED") +DIRECTION_RESULTS = frozenset({SUPPORTED, INVERTED, NO_SEPARATION}) +JOIN_KEYS = frozenset({ + "calibration_row_id", + "operator_label", + "pos", + "pwn30_synset", + "residual_score", + "row_id", + "row_resolution_status", + "surface", +}) + +FROZEN_READY = { + "HIGH": 13, + "SECONDARY": 22, + "REJECT": 24, + "QUARANTINE": 1, + "UNRESOLVED": 0, +} +FROZEN_UNKNOWN = { + "HIGH": 31, + "SECONDARY": 60, + "REJECT": 47, + "QUARANTINE": 2, + "UNRESOLVED": 0, +} + +EXPECTED_JOIN = "02088379b5ebe0f26106e830a730e8a90b2281d83c37bf1e750e1846bffa128b" +EXPECTED_JOIN_RECEIPT = "05748f812491be4ebe215a9a88224073bb2349b465c09dd2598f98d60e02947d" +EXPECTED_REASSESSMENT = "fe5d1061c1f9aed9cad4a1ad8e1dbabc5f76d13d4b13eacb88cef12c347e560d" +EXPECTED_CALIBRATION = "34bc70b93039fc6a5ba4bb58685e0fbfe6f91dd1f86a64fec8326a84013a6465" +EXPECTED_MEASUREMENT = "78ca09ab14912681d028e8b9b1c77a8daf1d0c8c81561434eb9b6ade45a753e5" +EXPECTED_FAILURE = "946a686d46b5a2fc47bb9968bf6e39c643e93c297d5a64de9e0fe88fc6eb33eb" +EXPECTED_SCORES = "01c758af570264c190f4af7805d43d734c9e9a36779f0e0b82675709f213e07c" +EXPECTED_SCORE_RECEIPT = "28ddb23bce8cfcf74b8046ee2cfd0908b5dddefa2a0c04562f9c2f8c912e88d5" +EXPECTED_RESOLUTION = "491b854bcf04d20624e560b63c01823b5634716ec266a4d3a7396d5514defd8d" +EXPECTED_RESOLUTION_RECEIPT = "461ed206ef3abba6b4a8ba79ed5de72305cbee95a61c96981923c89817c17d2d" +EXPECTED_LABELS = "f9901e334543dac2e9bf258208097cf4c9b48af9dc8b497ff42fe4f1f106e1fc" +EXPECTED_LABEL_RECEIPT = "4ce71e88c40cd617b9b6b502db48216c51e8cfa22ecea02347c9becc44c967fa" +EXPECTED_PACKET = "d9d986ec85a1ebea257859af8011758e9c84df270c9547f7b8bd0e34cec970fe" +EXPECTED_PACKET_RECEIPT = "0aff22b3406d9d954a54eabe080e83d2d4cb5e99df279605d9403e614b61dcf0" +EXPECTED_REVIEW = "8826b141468ec0dd674d2c7a253acfaa6e1d364ab5191506b424198419166da9" +EXPECTED_EXECUTION_LABELS = "5cfeab84f16e5ca77d81bda8f1523a3aa20ae70a9828e1ff04a4d64a1930aa1d" +EXPECTED_V1_FAILURE = "1d3f1f26609c1f1a47790d94730fba50e9f4d888dd8d7d89e6fa698a924fb3a8" +EXPECTED_V1_CLOSURE = "a5dbdb8ae497c25e711a81a880ad26ec7ef1ace917278c807497af5ec37e5002" +EXPECTED_PREREGISTRATION = "1c962703e12c5c48fd279dc766fd4d1d3108c40486a10789fdda112926e98261" +EXPECTED_SELECTION = "1c8a82ef939a33bbbb327f0f760439113432645438361176de36159a8f56c55c" +EXPECTED_TRACKER = "170e4b66cfa6764034367ffef685cff67c88367833eb75b46d05f7b3f10f867c" +EXPECTED_EVENTS = "96b74a92d44f1cf9fe152b18e5207176f161ba3bfce528dac38aa4571a742f9c" +EXPECTED_LEDGER = "77e22433203879b252f7a9e309d2013d7550101d1c4a014b494d2c96df87d0e0" + + +def refuse(message: str) -> None: + raise SystemExit(message) + + +def sha256_file(path: Path) -> str: + return hashlib.sha256(path.read_bytes()).hexdigest() + + +def sha256_text(text: str) -> str: + return hashlib.sha256(text.encode("utf-8")).hexdigest() + + +def render_json(payload: dict) -> str: + return json.dumps(payload, indent=2, sort_keys=True, ensure_ascii=True) + "\n" + + +def write_new(path: Path, text: str) -> str: + if path.exists(): + refuse(f"{path.name} already exists") + path.write_text(text, encoding="utf-8") + path.chmod(0o600) + return sha256_text(text) + + +def load_jsonl(path: Path) -> list[dict]: + rows = [] + for line in path.read_text(encoding="utf-8").splitlines(): + if line.strip(): + rows.append(json.loads(line)) + return rows + + +def sealed_hashes() -> dict[Path, str]: + return { + JOIN_PATH: EXPECTED_JOIN, + JOIN_RECEIPT_PATH: EXPECTED_JOIN_RECEIPT, + REASSESSMENT_PATH: EXPECTED_REASSESSMENT, + CALIBRATION_MANIFEST: EXPECTED_CALIBRATION, + MEASUREMENT_MANIFEST: EXPECTED_MEASUREMENT, + SCORES_PATH: EXPECTED_SCORES, + SCORE_RECEIPT: EXPECTED_SCORE_RECEIPT, + RESOLUTION_PATH: EXPECTED_RESOLUTION, + RESOLUTION_RECEIPT: EXPECTED_RESOLUTION_RECEIPT, + FAILURE_PATH: EXPECTED_FAILURE, + LABELS_PATH: EXPECTED_LABELS, + LABEL_RECEIPT_PATH: EXPECTED_LABEL_RECEIPT, + SOURCE / "RESIDUAL_THRESHOLD_V2_BLIND_OPERATOR_PACKET.jsonl": EXPECTED_PACKET, + SOURCE / "RESIDUAL_THRESHOLD_V2_BLIND_OPERATOR_PACKET_RECEIPT.json": EXPECTED_PACKET_RECEIPT, + SOURCE / "RESIDUAL_THRESHOLD_V2_CALIBRATION_LABEL_AVAILABILITY_REVIEW.json": EXPECTED_REVIEW, + SOURCE / "RESIDUAL_THRESHOLD_V2_CALIBRATION_LABELS.jsonl": EXPECTED_EXECUTION_LABELS, + SOURCE / "RESIDUAL_THRESHOLD_V1_CALIBRATION_FAILURE.json": EXPECTED_V1_FAILURE, + SOURCE / "RESIDUAL_THRESHOLD_V1_CALIBRATION_DESIGN_CLOSURE.json": EXPECTED_V1_CLOSURE, + PREREGISTRATION: EXPECTED_PREREGISTRATION, + SELECTION_PROCEDURE: EXPECTED_SELECTION, + EVENTS: EXPECTED_EVENTS, + LEDGER_FILE: EXPECTED_LEDGER, + } + + +def assert_sealed() -> None: + for path, expected in sealed_hashes().items(): + if sha256_file(path) != expected: + refuse(f"sealed artifact drifted: {path.name}") + + +def assert_measurement_sealed() -> None: + if sha256_file(MEASUREMENT_MANIFEST) != EXPECTED_MEASUREMENT: + refuse("measurement manifest drifted") + for name in ( + "RESIDUAL_THRESHOLD_V2_MEASUREMENT_RESOLUTION.jsonl", + "RESIDUAL_THRESHOLD_V2_MEASUREMENT_SCORES.jsonl", + "RESIDUAL_THRESHOLD_V2_MEASUREMENT_LABELS.jsonl", + "RESIDUAL_THRESHOLD_V2_MEASUREMENT_PREDICTIONS.jsonl", + ): + if (SOURCE / name).exists(): + refuse(f"measurement artifact exists: {name}") + + +def ready_residuals(rows: list[dict], *, expect_frozen_counts: bool) -> dict[str, list[str]]: + """Copy frozen residual strings for ready rows. Order follows the join.""" + if len(rows) != 200: + refuse("support join row count drifted") + ready = {label: [] for label in CLASSES} + unknown = {label: 0 for label in CLASSES} + seen = set() + for row in rows: + if set(row) != JOIN_KEYS: + refuse("support join keys drifted") + row_id = row["row_id"] + if row_id in seen: + refuse("support join repeats a row") + seen.add(row_id) + label = row["operator_label"] + if label not in ready: + refuse("support join has an unknown operator label") + status = row["row_resolution_status"] + score = row["residual_score"] + if status == READY: + if not isinstance(score, str) or score == "": + refuse("ready row is missing its frozen residual string") + ready[label].append(score) + elif status == "UNKNOWN": + if score is not None: + refuse("unknown row carries a residual") + unknown[label] += 1 + else: + refuse("support join has an unexpected resolution status") + if expect_frozen_counts and (ready_counts(ready) != FROZEN_READY or unknown != FROZEN_UNKNOWN): + refuse("support class counts drifted") + return ready + + +def ready_counts(ready: dict[str, list[str]]) -> dict[str, int]: + return {label: len(ready[label]) for label in CLASSES} + + +def compare_direction(high_scores: list[str], secondary_scores: list[str]) -> dict: + """Apply pair_comparison and direction_result to the two comparison classes.""" + if not high_scores or not secondary_scores: + refuse("direction comparison requires ready HIGH and ready SECONDARY") + comparison = pair_comparison(high_scores, secondary_scores) + result = direction_result(comparison) + if result not in DIRECTION_RESULTS: + refuse(f"direction rule returned an unfrozen state: {result}") + if comparison["status"] != "DESCRIPTIVE": + refuse("direction comparison is not descriptive") + return { + "direction_result": result, + "high_distribution": comparison["high"], + "high_larger_pairs": comparison["favorable_pairs"], + "mann_whitney_u": comparison["u_high"], + "mean_difference": comparison["mean_difference"], + "median_difference": comparison["median_difference"], + "rank_biserial": comparison["rank_biserial"], + "roc_auc": comparison["auc"], + "secondary_distribution": comparison["secondary"], + "secondary_larger_pairs": comparison["unfavorable_pairs"], + "ties": comparison["ties"], + } + + +def _analysis(comparison: dict) -> dict: + high = comparison["high_distribution"] + secondary = comparison["secondary_distribution"] + return { + "analysis_scope": ANALYSIS_SCOPE, + "confound_analysis_run": False, + "direction_result": comparison["direction_result"], + "direction_rule": "residual_model_resolved_replay_v1.direction_result", + "high_distribution": high, + "high_larger_pairs": comparison["high_larger_pairs"], + "json_schema_document": None, + "json_schema_exists": False, + "labels_changed": False, + "mann_whitney_u": comparison["mann_whitney_u"], + "mean_difference": comparison["mean_difference"], + "median_difference": comparison["median_difference"], + "precision_wilson_run": False, + "rank_biserial": comparison["rank_biserial"], + "readiness_changed": False, + "ready_high_count": high["count"], + "ready_quarantine_count": FROZEN_READY["QUARANTINE"], + "ready_reject_count": FROZEN_READY["REJECT"], + "ready_secondary_count": secondary["count"], + "reject_quarantine_comparison_run": False, + "residual_recomputed": False, + "roc_auc": comparison["roc_auc"], + "rows_excluded": 0, + "secondary_distribution": secondary, + "secondary_larger_pairs": comparison["secondary_larger_pairs"], + "source_label_hash": EXPECTED_LABELS, + "source_score_hash": EXPECTED_SCORES, + "source_support_join_hash": EXPECTED_JOIN, + "source_support_join_receipt_hash": EXPECTED_JOIN_RECEIPT, + "source_support_reassessment_hash": EXPECTED_REASSESSMENT, + "threshold_search_run": False, + "ties": comparison["ties"], + "uncertainty": UNCERTAINTY, + } + + +def _decision(result: str, analysis_sha: str) -> dict: + supported = result == SUPPORTED + return { + "admitted": 0, + "analysis_scope": ANALYSIS_SCOPE, + "analysis_sha256": analysis_sha, + "confound_analysis_run": False, + "direction_result": result, + "evaluation_id": EVALUATION_ID, + "gold": 0, + "json_schema_document": None, + "json_schema_exists": False, + "measurement_state": "SEALED", + "next_legal_transition": CONFOUND_NEXT if supported else "NONE", + "next_transition_authorized": False, + "redraw_authorized": False, + "runtime_integration": False, + "select_005_authorized": False, + "selected_source": "none", + "settled": 0, + "state": result, + "threshold_frozen": False, + "threshold_search_run": False, + "threshold_value": None, + "uncertainty": UNCERTAINTY, + } + + +def update_tracker(result: str, analysis_sha: str, decision_sha: str) -> str: + if sha256_file(TRACKER) != EXPECTED_TRACKER: + refuse("tracker drifted before the direction update") + tracker = json.loads(TRACKER.read_text(encoding="utf-8")) + if tracker.get("residual_threshold_v2_calibration_execution_state") != "CALIBRATION_INSUFFICIENT_SUPPORT": + refuse("execution 001 is no longer the sealed insufficient-support record") + if tracker.get("residual_threshold_v2_blind_operator_labeling_state") != "LABELS_FROZEN": + refuse("operator labels are no longer frozen") + if tracker.get("residual_threshold_v2_support_reassessment_state") != SUPPORT_STATE: + refuse("support reassessment is no longer passed") + if tracker.get("residual_threshold_v2_support_join_sha256") != EXPECTED_JOIN: + refuse("tracker join hash drifted") + if tracker.get("residual_threshold_v1_disposition") != "CLOSED_CALIBRATION_DESIGN_INSUFFICIENT": + refuse("v1 disposition drifted") + if tracker.get("residual_threshold_v2_value") is not None or tracker.get("residual_threshold_v2_frozen") is not False: + refuse("threshold is no longer null and unfrozen") + tracker["previous_tracker_sha256"] = EXPECTED_TRACKER + tracker["residual_threshold_v2_direction_evaluation_id"] = EVALUATION_ID + tracker["residual_threshold_v2_direction_evaluation_state"] = result + tracker["residual_threshold_v2_direction_analysis_sha256"] = analysis_sha + tracker["residual_threshold_v2_direction_decision_sha256"] = decision_sha + tracker["residual_threshold_v2_state"] = result + tracker["residual_threshold_v2_value"] = None + tracker["residual_threshold_v2_frozen"] = False + tracker["residual_threshold_v2_redraw_authorized"] = False + tracker["residual_threshold_v2_measurement_state"] = "SEALED" + tracker["residual_threshold_v2_measurement_resolved"] = False + tracker["residual_threshold_v2_measurement_scored"] = False + tracker["residual_threshold_v2_measurement_labeled"] = False + tracker["measurement_eligible"] = False + tracker["selected_source"] = "none" + tracker["select_authorized"] = False + tracker["admitted"] = 0 + tracker["settled"] = 0 + tracker["gold"] = 0 + tracker["residual_threshold_v2_disposition"] = None + if result == SUPPORTED: + tracker["next_legal_transition"] = CONFOUND_NEXT + else: + tracker["next_legal_transition"] = "NONE" + tracker["next_transition_authorized"] = False + if tracker["residual_threshold_v2_calibration_execution_state"] != "CALIBRATION_INSUFFICIENT_SUPPORT": + refuse("direction update changed execution 001") + if tracker["residual_threshold_v2_support_reassessment_state"] != SUPPORT_STATE: + refuse("direction update changed the support gate") + text = render_json(tracker) + TRACKER.write_text(text, encoding="utf-8") + return sha256_file(TRACKER) + + +def _distribution_sentence(name: str, distribution: dict) -> str: + return ( + f"{name} count {distribution['count']}, min {distribution['min']}, " + f"p10 {distribution['p10']}, p25 {distribution['p25']}, median {distribution['median']}, " + f"p75 {distribution['p75']}, p90 {distribution['p90']}, max {distribution['max']}, " + f"mean {distribution['mean']}, std {distribution['std']}" + ) + + +def append_spec(analysis: dict, analysis_sha: str, decision_sha: str, tracker_sha: str) -> None: + marker = "## Threshold v2 direction evaluation — 2026-09-28" + text = SPEC.read_text(encoding="utf-8") + result = analysis["direction_result"] + if result == SUPPORTED: + outcome = ( + f"`{EVALUATION_ID}` is `{SUPPORTED}`. The next named transition is " + f"`{CONFOUND_NEXT}`. This pass does not authorize it." + ) + else: + outcome = ( + f"`{EVALUATION_ID}` is `{result}`. The threshold value stays null. " + "Confound review, threshold search, and a redraw were not run." + ) + section = ( + f"{marker}\n" + "\n" + f"`{AUTHORIZATION}` reads residual strings already stored on the frozen support join. " + "The only directional comparison is ready HIGH against ready SECONDARY. " + f"Ready HIGH is {analysis['ready_high_count']} and ready SECONDARY is " + f"{analysis['ready_secondary_count']}. Ready REJECT {analysis['ready_reject_count']} " + f"and ready QUARANTINE {analysis['ready_quarantine_count']} are context counts and are " + "not part of the comparison. No row was trimmed, winsorized, or removed. " + "Residuals, labels, readiness, constituent senses, GlossBERT outputs, and Extended Lesk " + f"decisions were not recomputed. `{EXECUTION_ID}` stays " + "`CALIBRATION_INSUFFICIENT_SUPPORT` and stays sealed. Support reassessment 001 stays " + f"`{SUPPORT_STATE}`.\n" + "\n" + "The distributions use the frozen `full_distribution` convention: linear interpolation " + "on the sorted residuals, rank `(n - 1) * (percent / 100)`, then the residual quantum " + "with round-half-even. " + f"{_distribution_sentence('HIGH', analysis['high_distribution'])}. " + f"{_distribution_sentence('SECONDARY', analysis['secondary_distribution'])}.\n" + "\n" + f"HIGH mean minus SECONDARY mean is {analysis['mean_difference']}. " + f"HIGH median minus SECONDARY median is {analysis['median_difference']}. " + f"Mann-Whitney U for HIGH is {analysis['mann_whitney_u']}. " + f"HIGH-larger pairs are {analysis['high_larger_pairs']}. " + f"SECONDARY-larger pairs are {analysis['secondary_larger_pairs']}. " + f"Ties are {analysis['ties']}. Rank-biserial correlation is {analysis['rank_biserial']}. " + f"Descriptive ROC AUC, with HIGH positive, is {analysis['roc_auc']}. " + "AUC is descriptive and is not a threshold. " + f"Uncertainty is `{UNCERTAINTY}`: the v2 preregistration and the v2 selection procedure " + "do not define a calibration-direction interval, and `direction_result` does not require one. " + f"The frozen rule returns `{result}`.\n" + "\n" + f"{outcome} Direction scope is `{ANALYSIS_SCOPE}`. Confound analysis did not run. " + "Threshold search did not run. No precision or Wilson calculation ran. " + f"Analysis sha256 `{analysis_sha}`. Decision sha256 `{decision_sha}`. " + f"Tracker sha256 `{tracker_sha}`. Support join sha256 remains `{EXPECTED_JOIN}`.\n" + "\n" + "The threshold stays null and unfrozen. Measurement stays `SEALED`, unresolved, " + f"unscored, and unlabeled. Measurement sha256 remains `{EXPECTED_MEASUREMENT}`. " + f"Score sha256 remains `{EXPECTED_SCORES}`. Label sha256 remains `{EXPECTED_LABELS}`. " + f"Resolution sha256 remains `{EXPECTED_RESOLUTION}`. Failure sha256 remains " + f"`{EXPECTED_FAILURE}`. `selected_source` remains `none`. Nothing was integrated into " + "runtime. SELECT-005 remains unauthorized. Admitted, settled, and gold stay 0. " + f"Events sha256 remains `{EXPECTED_EVENTS}`. The ledger was not appended. " + "No JSON Schema document exists for this direction family. v1 remains " + "`CLOSED_CALIBRATION_DESIGN_INSUFFICIENT`.\n" + ) + if marker in text: + start = text.index(marker) + SPEC.write_text(text[:start] + section, encoding="utf-8") + return + if not text.endswith("\n"): + text += "\n" + SPEC.write_text(text + section, encoding="utf-8") + + +def evaluate() -> dict: + assert_sealed() + assert_measurement_sealed() + if sha256_file(TRACKER) != EXPECTED_TRACKER: + refuse("tracker drifted before the direction evaluation") + if sha256_file(JOIN_PATH) != EXPECTED_JOIN: + refuse("support join drifted") + rows = load_jsonl(JOIN_PATH) + ready = ready_residuals(rows, expect_frozen_counts=True) + if ready_counts(ready)["HIGH"] != 13 or ready_counts(ready)["SECONDARY"] != 22: + refuse("directional class counts are not the frozen support counts") + comparison = compare_direction(ready["HIGH"], ready["SECONDARY"]) + if comparison["high_distribution"]["count"] != 13: + refuse("HIGH distribution count drifted") + if comparison["secondary_distribution"]["count"] != 22: + refuse("SECONDARY distribution count drifted") + analysis = _analysis(comparison) + analysis_text = render_json(analysis) + analysis_sha = write_new(ANALYSIS_PATH, analysis_text) + decision_text = render_json(_decision(comparison["direction_result"], analysis_sha)) + decision_sha = write_new(DECISION_PATH, decision_text) + tracker_sha = update_tracker(comparison["direction_result"], analysis_sha, decision_sha) + append_spec(analysis, analysis_sha, decision_sha, tracker_sha) + if sha256_file(JOIN_PATH) != EXPECTED_JOIN: + refuse("support join changed during direction evaluation") + assert_sealed() + assert_measurement_sealed() + if sha256_file(ANALYSIS_PATH) != analysis_sha or sha256_file(DECISION_PATH) != decision_sha: + refuse("direction artifact hash drifted after write") + return { + "analysis_sha256": analysis_sha, + "decision_sha256": decision_sha, + "direction_result": comparison["direction_result"], + "tracker_sha256": tracker_sha, + } + + +def main() -> None: + if len(sys.argv) == 2 and sys.argv[1] == "--evaluate": + print(json.dumps(evaluate(), sort_keys=True)) + return + refuse("usage: residual_threshold_v2_direction.py --evaluate") + + +if __name__ == "__main__": + main() From 2d960455523d1e7e38558d6b9250fffcb3a28930 Mon Sep 17 00:00:00 2001 From: Daniel Meyer Date: Mon, 28 Sep 2026 16:26:01 -0700 Subject: [PATCH 19/42] feat(007): land SELECT residual and schedule work (18) --- .../residual_threshold_v2_labeling.py | 558 ++++++++++++++++++ 1 file changed, 558 insertions(+) create mode 100644 scripts/shadow/hyperlexical/residual_threshold_v2_labeling.py diff --git a/scripts/shadow/hyperlexical/residual_threshold_v2_labeling.py b/scripts/shadow/hyperlexical/residual_threshold_v2_labeling.py new file mode 100644 index 00000000..fb26023a --- /dev/null +++ b/scripts/shadow/hyperlexical/residual_threshold_v2_labeling.py @@ -0,0 +1,558 @@ +"""Blind operator labeling for the frozen threshold v2 calibration surface. + +This pass writes a label-availability review, a blind packet, and operator +labels. It does not join those labels to readiness or residual scores, and it +does not search for a threshold. +""" + +from __future__ import annotations + +import hashlib +import json +import sys +from pathlib import Path + +from hyperlexical.screen_eval import OPERATOR_BUCKETS, REASONS + +LEDGER = Path("/home/morpheus/hlx-private/eval-reserve-20260926") +SOURCE = LEDGER / "operator-review/HLX-EVAL-UNBIND-SEMANTIC-EVIDENCE-SOURCE-V1-001" +SENSE = LEDGER / "operator-review/HLX-EVAL-UNBIND-SENSE-SCREEN-V1-HYPOTHESIS-001" +EVENTS = LEDGER / "events.jsonl" +LEDGER_FILE = LEDGER / "ledger.json" +TRACKER = SENSE / "HYPOTHESIS.json" +SPEC = Path("/home/morpheus/Hyperlex/specs/007-hyperlexical-model/evaluation-reserve.md") + +CALIBRATION_MANIFEST = SOURCE / "RESIDUAL_THRESHOLD_V2_CALIBRATION_MANIFEST.jsonl" +MEASUREMENT_MANIFEST = SOURCE / "RESIDUAL_THRESHOLD_V2_MEASUREMENT_MANIFEST.jsonl" +FAILURE_PATH = SOURCE / "RESIDUAL_THRESHOLD_V2_CALIBRATION_FAILURE.json" +V1_CLOSURE = SOURCE / "RESIDUAL_THRESHOLD_V1_CALIBRATION_DESIGN_CLOSURE.json" + +REVIEW_PATH = SOURCE / "RESIDUAL_THRESHOLD_V2_CALIBRATION_LABEL_AVAILABILITY_REVIEW.json" +PACKET_PATH = SOURCE / "RESIDUAL_THRESHOLD_V2_BLIND_OPERATOR_PACKET.jsonl" +PACKET_RECEIPT_PATH = SOURCE / "RESIDUAL_THRESHOLD_V2_BLIND_OPERATOR_PACKET_RECEIPT.json" +LABELS_PATH = SOURCE / "RESIDUAL_THRESHOLD_V2_OPERATOR_LABELS.jsonl" +LABEL_RECEIPT_PATH = SOURCE / "RESIDUAL_THRESHOLD_V2_OPERATOR_LABELING_RECEIPT.json" + +AUTHORIZATION = "THRESHOLD_V2_BLIND_OPERATOR_LABELING_AUTHORIZATION" +EXECUTION_ID = "THRESHOLD_V2_CALIBRATION_EXECUTION_001" +LABELING_ID = "THRESHOLD_V2_BLIND_OPERATOR_LABELING_001" +NEXT_TRANSITION = "THRESHOLD_V2_CALIBRATION_SUPPORT_REASSESSMENT_AUTHORIZATION" + +EXPECTED_CALIBRATION = "34bc70b93039fc6a5ba4bb58685e0fbfe6f91dd1f86a64fec8326a84013a6465" +EXPECTED_MEASUREMENT = "78ca09ab14912681d028e8b9b1c77a8daf1d0c8c81561434eb9b6ade45a753e5" +EXPECTED_FAILURE = "946a686d46b5a2fc47bb9968bf6e39c643e93c297d5a64de9e0fe88fc6eb33eb" +EXPECTED_SCORES = "01c758af570264c190f4af7805d43d734c9e9a36779f0e0b82675709f213e07c" +EXPECTED_SCORE_RECEIPT = "28ddb23bce8cfcf74b8046ee2cfd0908b5dddefa2a0c04562f9c2f8c912e88d5" +EXPECTED_TRACKER = "50ee4bd40063c57f1f39e466d298d6c08486d0134184da459b9e1ef5de47bea8" +EXPECTED_EVENTS = "96b74a92d44f1cf9fe152b18e5207176f161ba3bfce528dac38aa4571a742f9c" +EXPECTED_LEDGER = "77e22433203879b252f7a9e309d2013d7550101d1c4a014b494d2c96df87d0e0" +EXPECTED_V1_FAILURE = "1d3f1f26609c1f1a47790d94730fba50e9f4d888dd8d7d89e6fa698a924fb3a8" +EXPECTED_V1_CLOSURE = "a5dbdb8ae497c25e711a81a880ad26ec7ef1ace917278c807497af5ec37e5002" +EXPECTED_RESOLUTION = "491b854bcf04d20624e560b63c01823b5634716ec266a4d3a7396d5514defd8d" +EXPECTED_RESOLUTION_RECEIPT = "461ed206ef3abba6b4a8ba79ed5de72305cbee95a61c96981923c89817c17d2d" +EXPECTED_EXECUTION_LABELS = "5cfeab84f16e5ca77d81bda8f1523a3aa20ae70a9828e1ff04a4d64a1930aa1d" +EXPECTED_PREREG = "1c962703e12c5c48fd279dc766fd4d1d3108c40486a10789fdda112926e98261" + +PACKET_KEYS = frozenset({ + "calibration_row_id", + "frozen_gloss", + "pos", + "pwn30_synset", + "row_id", + "schema", + "surface", + "token_count", +}) +LABEL_KEYS = frozenset({ + "calibration_row_id", + "operator_evidence", + "operator_label", + "operator_note", + "pos", + "pwn30_synset", + "review_status", + "row_id", + "schema", + "surface", +}) +FORBIDDEN_KEY_PARTS = ( + "auc", + "candidate", + "confidence", + "constituent", + "cosine", + "embedding", + "floor", + "glossbert", + "lesk", + "margin", + "minilm", + "ready", + "residual", + "resolution", + "score", + "support", + "threshold", + "tier", + "unknown", + "wsd", +) +MIN_READY_HIGH = 12 +MIN_READY_SECONDARY = 8 +MIN_COMBINED = 20 + + +def refuse(message: str) -> None: + raise SystemExit(message) + + +def sha256_file(path: Path) -> str: + return hashlib.sha256(path.read_bytes()).hexdigest() + + +def sha256_text(text: str) -> str: + return hashlib.sha256(text.encode("utf-8")).hexdigest() + + +def render_json(payload: dict) -> str: + return json.dumps(payload, indent=2, sort_keys=True, ensure_ascii=True) + "\n" + + +def render_jsonl(rows: list[dict]) -> str: + return "".join(json.dumps(row, sort_keys=True, ensure_ascii=True) + "\n" for row in rows) + + +def write_new(path: Path, text: str) -> str: + if path.exists(): + refuse(f"{path.name} already exists") + path.write_text(text, encoding="utf-8") + path.chmod(0o600) + return sha256_text(text) + + +def load_jsonl(path: Path) -> list[dict]: + rows = [] + for line in path.read_text(encoding="utf-8").splitlines(): + if line.strip(): + rows.append(json.loads(line)) + return rows + + +def sealed_hashes() -> dict[Path, str]: + return { + CALIBRATION_MANIFEST: EXPECTED_CALIBRATION, + MEASUREMENT_MANIFEST: EXPECTED_MEASUREMENT, + FAILURE_PATH: EXPECTED_FAILURE, + SOURCE / "RESIDUAL_THRESHOLD_V2_CALIBRATION_SCORES.jsonl": EXPECTED_SCORES, + SOURCE / "RESIDUAL_THRESHOLD_V2_CALIBRATION_SCORE_RECEIPT.json": EXPECTED_SCORE_RECEIPT, + SOURCE / "RESIDUAL_THRESHOLD_V2_CALIBRATION_RESOLUTION.jsonl": EXPECTED_RESOLUTION, + SOURCE / "RESIDUAL_THRESHOLD_V2_CALIBRATION_RESOLUTION_RECEIPT.json": EXPECTED_RESOLUTION_RECEIPT, + SOURCE / "RESIDUAL_THRESHOLD_V2_CALIBRATION_LABELS.jsonl": EXPECTED_EXECUTION_LABELS, + SOURCE / "RESIDUAL_THRESHOLD_V1_CALIBRATION_FAILURE.json": EXPECTED_V1_FAILURE, + V1_CLOSURE: EXPECTED_V1_CLOSURE, + SOURCE / "RESIDUAL_THRESHOLD_V2_PREREGISTRATION.json": EXPECTED_PREREG, + EVENTS: EXPECTED_EVENTS, + LEDGER_FILE: EXPECTED_LEDGER, + } + + +def assert_sealed() -> None: + for path, expected in sealed_hashes().items(): + found = sha256_file(path) + if found != expected: + refuse(f"sealed artifact drifted: {path.name}") + + +def forbidden_key(key: str) -> str | None: + lowered = key.casefold() + for part in FORBIDDEN_KEY_PARTS: + if part in lowered: + return part + return None + + +def assert_packet_blind(rows: list[dict]) -> None: + if len(rows) != 200: + refuse(f"packet row count is {len(rows)}") + seen: set[str] = set() + for row in rows: + if set(row) != PACKET_KEYS: + refuse(f"packet keys left the permitted set: {sorted(set(row) - PACKET_KEYS)}") + for key in row: + part = forbidden_key(key) + if part is not None: + refuse(f"packet key exposes {part}") + row_id = row["row_id"] + if row_id in seen: + refuse(f"duplicate packet row {row_id}") + seen.add(row_id) + if not isinstance(row["frozen_gloss"], str) or not row["frozen_gloss"]: + refuse(f"missing frozen gloss for {row_id}") + if not isinstance(row["surface"], str) or not row["surface"]: + refuse(f"missing surface for {row_id}") + + +def packet_rows(manifest: list[dict]) -> list[dict]: + rows = [] + for row in manifest: + rows.append({ + "calibration_row_id": row["global_draw_order"], + "frozen_gloss": row["frozen_gloss"], + "pos": row["pos"], + "pwn30_synset": row["pwn30_synset"], + "row_id": row["row_id"], + "schema": "hyperlex.residual_threshold_v2_blind_operator_row.v1", + "surface": row["surface"], + "token_count": row["token_count"], + }) + return rows + + +def availability_review(failure: dict, closure: dict) -> dict: + support = failure["observed_support"] + if support["unlabeled"] != 200 or support["unlabeled_scored"] != 60 or support["unlabeled_unknown"] != 140: + refuse("frozen failure support is not the recorded 0-label split") + if closure["residual_ready"] != 6 or closure["minimum_required_target_ready_rows"] != MIN_COMBINED: + refuse("v1 closure counts drifted") + v1_possible = closure["residual_ready"] >= MIN_COMBINED + v2_possible = support["unlabeled_scored"] >= MIN_COMBINED + if v1_possible or not v2_possible: + refuse("design-support comparison does not separate v1 from v2") + return { + "admitted": 0, + "authorization": AUTHORIZATION, + "because": "residual_ready_rows 60 >= minimum_combined_target_ready 20", + "calibration_rows": 200, + "design_support_possible": True, + "execution_id": EXECUTION_ID, + "execution_state_unchanged": "CALIBRATION_INSUFFICIENT_SUPPORT", + "existing_operator_labels": 0, + "gold": 0, + "json_schema_document": None, + "json_schema_exists": False, + "minimum_combined_target_ready": MIN_COMBINED, + "minimum_ready_HIGH": MIN_READY_HIGH, + "minimum_ready_SECONDARY": MIN_READY_SECONDARY, + "redraw_authorized": False, + "redraw_required": False, + "residual_ready_rows": 60, + "same_surface_blind_labeling_eligible": True, + "schema": "hyperlex.residual_threshold_v2_calibration_label_availability_review.v1", + "select_005_authorized": False, + "selected_source": "none", + "settled": 0, + "unknown_rows": 140, + "v1_comparison": { + "design_support_possible": False, + "disposition_unchanged": "CLOSED_CALIBRATION_DESIGN_INSUFFICIENT", + "minimum_combined_target_ready": MIN_COMBINED, + "residual_ready": 6, + }, + "v1_modified": False, + } + + +def freeze_packet() -> dict: + assert_sealed() + if sha256_file(TRACKER) != EXPECTED_TRACKER: + refuse("tracker drifted before the packet freeze") + manifest = load_jsonl(CALIBRATION_MANIFEST) + measurement = load_jsonl(MEASUREMENT_MANIFEST) + if len(manifest) != 200 or len(measurement) != 200: + refuse("manifest row count is not 200") + rows = packet_rows(manifest) + assert_packet_blind(rows) + manifest_ids = [row["row_id"] for row in manifest] + packet_ids = [row["row_id"] for row in rows] + if packet_ids != manifest_ids: + refuse("packet order or identity diverged from the calibration manifest") + measurement_ids = {row["row_id"] for row in measurement} + if set(packet_ids) & measurement_ids: + refuse("measurement row entered the operator packet") + failure = json.loads(FAILURE_PATH.read_text(encoding="utf-8")) + closure = json.loads(V1_CLOSURE.read_text(encoding="utf-8")) + review = availability_review(failure, closure) + review_text = render_json(review) + packet_text = render_jsonl(rows) + review_sha = write_new(REVIEW_PATH, review_text) + packet_sha = write_new(PACKET_PATH, packet_text) + receipt = { + "authorization": AUTHORIZATION, + "glossbert_scores_exposed": False, + "json_schema_document": None, + "json_schema_exists": False, + "measurement_rows_included": False, + "packet_rows": 200, + "packet_sha256": packet_sha, + "readiness_exposed": False, + "residual_scores_exposed": False, + "resolver_tiers_exposed": False, + "review_sha256": review_sha, + "schema": "hyperlex.residual_threshold_v2_blind_operator_packet_receipt.v1", + "source_manifest_hash": EXPECTED_CALIBRATION, + "threshold_information_exposed": False, + } + receipt_sha = write_new(PACKET_RECEIPT_PATH, render_json(receipt)) + assert_sealed() + if sha256_file(TRACKER) != EXPECTED_TRACKER: + refuse("tracker changed during the packet freeze") + return { + "packet_sha256": packet_sha, + "receipt_sha256": receipt_sha, + "review_sha256": review_sha, + } + + +def label_rows(packet: list[dict], decisions: list[dict]) -> list[dict]: + if len(decisions) != len(packet): + refuse("decision count does not match the packet") + by_id = {} + for decision in decisions: + row_id = decision.get("row_id") + if row_id in by_id: + refuse(f"duplicate decision {row_id}") + by_id[row_id] = decision + labels = [] + for row in packet: + decision = by_id.get(row["row_id"]) + if decision is None: + refuse(f"missing decision for {row['row_id']}") + if decision.get("calibration_row_id") != row["calibration_row_id"]: + refuse(f"calibration row id mismatch for {row['row_id']}") + label = decision.get("operator_label") + evidence = decision.get("operator_evidence") + note = decision.get("operator_note") + if label not in OPERATOR_BUCKETS: + refuse(f"operator label {label!r} is outside the canonical ontology") + if evidence not in REASONS[label]: + refuse(f"evidence {evidence!r} is not canonical for {label}") + if not isinstance(note, str) or not note.strip(): + refuse(f"missing operator note for {row['row_id']}") + for key in decision: + part = forbidden_key(key) + if part is not None: + refuse(f"decision key exposes {part}") + labels.append({ + "calibration_row_id": row["calibration_row_id"], + "operator_evidence": evidence, + "operator_label": label, + "operator_note": note, + "pos": row["pos"], + "pwn30_synset": row["pwn30_synset"], + "review_status": "REVIEWED", + "row_id": row["row_id"], + "schema": "hyperlex.residual_threshold_v2_operator_label.v1", + "surface": row["surface"], + }) + return labels + + +def inventory(labels: list[dict]) -> dict[str, int]: + counts = {name: 0 for name in OPERATOR_BUCKETS} + for row in labels: + counts[row["operator_label"]] += 1 + if sum(counts.values()) != len(labels): + refuse("label inventory does not cover every row") + return counts + + +def freeze_labels(decisions_path: Path) -> dict: + assert_sealed() + if sha256_file(TRACKER) != EXPECTED_TRACKER: + refuse("tracker drifted before the label freeze") + if not PACKET_RECEIPT_PATH.exists() or not PACKET_PATH.exists(): + refuse("packet receipt was not frozen before labels") + packet = load_jsonl(PACKET_PATH) + assert_packet_blind(packet) + manifest = load_jsonl(CALIBRATION_MANIFEST) + measurement_ids = {row["row_id"] for row in load_jsonl(MEASUREMENT_MANIFEST)} + if [row["row_id"] for row in packet] != [row["row_id"] for row in manifest]: + refuse("packet identity set does not match the calibration manifest") + if {row["row_id"] for row in packet} & measurement_ids: + refuse("measurement row present in the packet") + receipt_sha = sha256_file(PACKET_RECEIPT_PATH) + stored = json.loads(PACKET_RECEIPT_PATH.read_text(encoding="utf-8")) + if stored.get("packet_sha256") != sha256_file(PACKET_PATH): + refuse("packet receipt does not bind the frozen packet") + if stored.get("readiness_exposed") is not False or stored.get("residual_scores_exposed") is not False: + refuse("packet receipt does not deny readiness and residual exposure") + if stored.get("resolver_tiers_exposed") is not False or stored.get("glossbert_scores_exposed") is not False: + refuse("packet receipt does not deny resolver exposure") + if stored.get("threshold_information_exposed") is not False or stored.get("measurement_rows_included") is not False: + refuse("packet receipt does not deny threshold and measurement exposure") + decisions = load_jsonl(decisions_path) + labels = label_rows(packet, decisions) + for row in labels: + if set(row) != LABEL_KEYS: + refuse("label keys left the permitted set") + for key in row: + part = forbidden_key(key) + if part is not None: + refuse(f"label key exposes {part}") + counts = inventory(labels) + label_text = render_jsonl(labels) + label_sha = write_new(LABELS_PATH, label_text) + labeling_receipt = { + "authorization": AUTHORIZATION, + "confound_analysis_performed": False, + "direction_analysis_performed": False, + "duplicate_labels": 0, + "execution_id_unchanged": EXECUTION_ID, + "execution_state_unchanged": "CALIBRATION_INSUFFICIENT_SUPPORT", + "identity_set_matches_manifest": True, + "joined_to_readiness": False, + "joined_to_scores": False, + "json_schema_document": None, + "json_schema_exists": False, + "label_count": 200, + "label_sha256": label_sha, + "labeling_id": LABELING_ID, + "labeling_state": "LABELS_FROZEN", + "measurement_rows": 0, + "measurement_state": "SEALED", + "measurement_unlabeled": True, + "measurement_unresolved": True, + "measurement_unscored": True, + "missing_rows": 0, + "operator_label_inventory": counts, + "packet_receipt_sha256": receipt_sha, + "packet_sha256": stored["packet_sha256"], + "review_status": "REVIEWED", + "runtime_integration": False, + "schema": "hyperlex.residual_threshold_v2_operator_labeling_receipt.v1", + "select_005_authorized": False, + "selected_source": "none", + "support_reassessment_performed": False, + "threshold_frozen": False, + "threshold_search_performed": False, + "threshold_value": None, + "unique_row_ids": 200, + } + labeling_sha = write_new(LABEL_RECEIPT_PATH, render_json(labeling_receipt)) + tracker_sha = update_tracker(label_sha, labeling_sha, stored["packet_sha256"], receipt_sha, stored["review_sha256"]) + assert_sealed() + append_spec(tracker_sha, stored, receipt_sha, label_sha, labeling_sha, counts) + return { + "label_sha256": label_sha, + "labeling_receipt_sha256": labeling_sha, + "operator_label_inventory": counts, + "tracker_sha256": tracker_sha, + } + + +def update_tracker(label_sha: str, labeling_sha: str, packet_sha: str, receipt_sha: str, review_sha: str) -> str: + if sha256_file(TRACKER) != EXPECTED_TRACKER: + refuse("tracker drifted before the labeling update") + tracker = json.loads(TRACKER.read_text(encoding="utf-8")) + if tracker.get("residual_threshold_v2_state") != "CALIBRATION_INSUFFICIENT_SUPPORT": + refuse("v2 execution state drifted") + if tracker.get("residual_threshold_state") != "CALIBRATION_INSUFFICIENT_SUPPORT": + refuse("v1 execution state drifted") + if tracker.get("residual_threshold_v1_disposition") != "CLOSED_CALIBRATION_DESIGN_INSUFFICIENT": + refuse("v1 disposition drifted") + if tracker.get("residual_threshold_v2_value") is not None or tracker.get("residual_threshold_v2_frozen") is not False: + refuse("v2 threshold drifted") + if tracker.get("selected_source") != "none" or tracker.get("select_authorized") is not False: + refuse("source selection drifted") + tracker["previous_tracker_sha256"] = EXPECTED_TRACKER + tracker["residual_threshold_v2_calibration_execution_id"] = EXECUTION_ID + tracker["residual_threshold_v2_calibration_execution_sealed"] = True + tracker["residual_threshold_v2_blind_operator_labeling_id"] = LABELING_ID + tracker["residual_threshold_v2_blind_operator_labeling_state"] = "LABELS_FROZEN" + tracker["residual_threshold_v2_operator_labels_frozen"] = True + tracker["residual_threshold_v2_calibration_label_availability_review_sha256"] = review_sha + tracker["residual_threshold_v2_blind_operator_packet_sha256"] = packet_sha + tracker["residual_threshold_v2_blind_operator_packet_receipt_sha256"] = receipt_sha + tracker["residual_threshold_v2_operator_labels_sha256"] = label_sha + tracker["residual_threshold_v2_operator_labeling_receipt_sha256"] = labeling_sha + tracker["residual_threshold_v2_measurement_state"] = "SEALED" + tracker["residual_threshold_v2_measurement_resolved"] = False + tracker["residual_threshold_v2_measurement_scored"] = False + tracker["residual_threshold_v2_measurement_labeled"] = False + tracker["residual_threshold_v2_redraw_authorized"] = False + tracker["measurement_eligible"] = False + tracker["selected_source"] = "none" + tracker["select_authorized"] = False + tracker["admitted"] = 0 + tracker["settled"] = 0 + tracker["gold"] = 0 + tracker["next_legal_transition"] = NEXT_TRANSITION + tracker["next_transition_authorized"] = False + if tracker["residual_threshold_v2_state"] != "CALIBRATION_INSUFFICIENT_SUPPORT": + refuse("labeling overwrote the calibration execution state") + text = render_json(tracker) + TRACKER.write_text(text, encoding="utf-8") + TRACKER.chmod(0o600) + return sha256_file(TRACKER) + + +def append_spec(tracker_sha: str, packet_receipt: dict, receipt_sha: str, label_sha: str, labeling_sha: str, counts: dict[str, int]) -> None: + marker = "## Threshold v2 blind operator labeling — 2026-09-28" + text = SPEC.read_text(encoding="utf-8") + if marker in text: + refuse("spec section already exists") + if not text.endswith("\n"): + text += "\n" + unresolved = counts["UNRESOLVED"] + section = ( + f"{marker}\n" + "\n" + "`THRESHOLD_V2_BLIND_OPERATOR_LABELING_AUTHORIZATION` freezes operator labels " + "for the same 200-row calibration manifest. " + "`THRESHOLD_V2_CALIBRATION_EXECUTION_001` stays `CALIBRATION_INSUFFICIENT_SUPPORT` " + "and stays sealed. That execution observed ready HIGH 0 and ready SECONDARY 0 " + "because no operator labels existed. This labeling is a successor event. " + "It does not join the new labels to readiness or to residual scores.\n" + "\n" + "The label-availability review separates v2 from v1. v1 had 6 residual-ready rows, " + "below the combined target-ready floor of 20, so design support was impossible and " + "that closure stays `CLOSED_CALIBRATION_DESIGN_INSUFFICIENT`. v2 has 60 residual-ready " + "rows and 140 unknown rows. Sixty is at least 20, so design support is possible and " + "the missing evidence is the operator labels. Existing labels on these rows were 0. " + "Redraw is not required and is not authorized. The same frozen surface is eligible " + f"for blind labeling. Review sha256 `{packet_receipt['review_sha256']}`.\n" + "\n" + "The operator packet contains all 200 calibration rows, in manifest order. Membership " + "does not depend on readiness, residual score, resolution tier, or threshold eligibility. " + "Each row exposes the calibration row id, row id, surface, POS, PWN 3.0 synset, frozen " + "first-sense gloss, and token count. Readiness, residual scores, MiniLM representations, " + "resolver tiers, Extended Lesk, GlossBERT outputs, candidate counts, and threshold floors " + "are absent. No measurement row is included. Measurement sha256 remains " + f"`{EXPECTED_MEASUREMENT}`. Packet sha256 `{packet_receipt['packet_sha256']}`. " + f"Packet receipt sha256 `{receipt_sha}`. The receipt was hashed before the labels were written.\n" + "\n" + "All 200 rows received a canonical review outcome under the existing operator ontology: " + "HIGH, SECONDARY, REJECT, QUARANTINE, and UNRESOLVED. The label inventory is " + f"HIGH {counts['HIGH']}, SECONDARY {counts['SECONDARY']}, REJECT {counts['REJECT']}, " + f"QUARANTINE {counts['QUARANTINE']}, and UNRESOLVED {unresolved}. " + "Those counts are not joined to residual-ready rows. No support reassessment, direction " + "analysis, confound analysis, or threshold search was run. " + f"Label sha256 `{label_sha}`. Labeling receipt sha256 `{labeling_sha}`. " + f"Tracker sha256 `{tracker_sha}`.\n" + "\n" + "The threshold value stays null and the threshold stays unfrozen. Measurement stays " + "`SEALED`, unresolved, unscored, and unlabeled. `selected_source` remains `none`. " + "Nothing was integrated into runtime. SELECT-005 remains unauthorized. Admitted, settled, " + "and gold stay 0. Calibration manifest sha256 remains " + f"`{EXPECTED_CALIBRATION}`. Failure sha256 remains `{EXPECTED_FAILURE}`. " + f"Score sha256 remains `{EXPECTED_SCORES}`. Score receipt sha256 remains " + f"`{EXPECTED_SCORE_RECEIPT}`. Events sha256 remains `{EXPECTED_EVENTS}`. " + "The ledger was not appended. No JSON Schema document exists for this labeling family.\n" + "\n" + f"The next named transition is `{NEXT_TRANSITION}`. This pass does not authorize it.\n" + ) + SPEC.write_text(text + section, encoding="utf-8") + + +def main() -> None: + if len(sys.argv) == 2 and sys.argv[1] == "--packet": + print(json.dumps(freeze_packet(), sort_keys=True)) + return + if len(sys.argv) == 3 and sys.argv[1] == "--labels": + print(json.dumps(freeze_labels(Path(sys.argv[2])), sort_keys=True)) + return + refuse("usage: residual_threshold_v2_labeling.py --packet | --labels DECISIONS.jsonl") + + +if __name__ == "__main__": + main() From 570fa8521dc5a3097493de4ecb098d15b1b0d15c Mon Sep 17 00:00:00 2001 From: Daniel Meyer Date: Mon, 28 Sep 2026 16:27:02 -0700 Subject: [PATCH 20/42] feat(007): land SELECT residual and schedule work (19) --- .../hyperlexical/residual_threshold_v1.py | 609 ++++++++++++++++++ 1 file changed, 609 insertions(+) create mode 100644 scripts/shadow/hyperlexical/residual_threshold_v1.py diff --git a/scripts/shadow/hyperlexical/residual_threshold_v1.py b/scripts/shadow/hyperlexical/residual_threshold_v1.py new file mode 100644 index 00000000..5dcbdb6b --- /dev/null +++ b/scripts/shadow/hyperlexical/residual_threshold_v1.py @@ -0,0 +1,609 @@ +"""Preregistered one-sided residual threshold procedure. + +This module freezes how a later calibration pass may choose one global +``T_HIGH``. It does not read the 225 development rows, does not draw a +surface, and does not freeze a numeric threshold. +""" + +from __future__ import annotations + +import math +from decimal import Decimal, ROUND_HALF_EVEN + +from hyperlexical.residual_model_resolved_replay_v1 import ( + RESIDUAL_QUANTUM, + direction_result, + extreme_driven, + pair_comparison, + pos_partitioned, + tier3_concentration, +) +from hyperlexical.semantic_compositionality_residual import RESIDUAL_QUANTUM as SCORE_QUANTUM + +# Same z as hyperlexical.selection_surface.Z95. Wilson 95%. +WILSON_Z95 = Decimal("1.959963984540054") +RATE_QUANTUM = Decimal("0.000001") + +RULE = "RUNE.SEMANTIC_COMPOSITIONALITY_THRESHOLD.v1" +TRACK_STATE = "THRESHOLD_PROCEDURE_PREREGISTERED" +ALGORITHM_ID = "residual_threshold_v1.precision_gated_recall_v1" + +MIN_CALIBRATION_HIGH = 12 +MIN_CALIBRATION_SECONDARY = 8 +MIN_PREDICTED_YES = 8 +PRECISION_FLOOR = Decimal("0.80") +WILSON_LOWER_FLOOR = Decimal("0.50") +CONFIDENCE_LEVEL = "0.95" +REJECT_PREDICTED_YES_MAX = 0 +QUARANTINE_PREDICTED_YES_MAX = 0 + +# Stated separately from the calibration floors. Measurement success is not +# implied by a calibration pass. The numbers match because both were chosen +# here, before either surface exists. +MIN_MEASUREMENT_HIGH = 12 +MIN_MEASUREMENT_SECONDARY = 8 +MIN_MEASUREMENT_PREDICTED_YES = 8 +MEASUREMENT_PRECISION_FLOOR = Decimal("0.80") +MEASUREMENT_WILSON_LOWER_FLOOR = Decimal("0.50") + +OUTPUT_LABELS = ("YES", "UNKNOWN") +COMPARISON_BUCKETS = frozenset({"HIGH", "SECONDARY"}) +SAFETY_BUCKETS = frozenset({"REJECT", "QUARANTINE"}) +READY = "SCORED" + +INSUFFICIENT_SUPPORT = "CALIBRATION_INSUFFICIENT_SUPPORT" +NO_DIRECTION = "NO_DIRECTIONAL_SIGNAL" +NO_THRESHOLD = "NO_THRESHOLD_PASSES_PRECISION_GATE" +CONFOUND_REVIEW = "CALIBRATION_CONFOUND_REVIEW" +THRESHOLD_FROZEN = "THRESHOLD_FROZEN" + +MEASUREMENT_INSUFFICIENT = "MEASUREMENT_INSUFFICIENT_SUPPORT" +MEASUREMENT_NOT_ACCEPTED = "MEASUREMENT_NOT_ACCEPTED" +MEASUREMENT_ACCEPTED = "MEASUREMENT_ACCEPTED" + +NEXT_TRANSITION = "RESIDUAL_CALIBRATION_SURFACE_DRAW_AUTHORIZATION" + + +def _quantize_rate(value: Decimal) -> Decimal: + return value.quantize(RATE_QUANTUM, rounding=ROUND_HALF_EVEN) + + +def _residual_text(value: Decimal) -> str: + quantized = value.quantize(SCORE_QUANTUM, rounding=ROUND_HALF_EVEN) + return f"{quantized:.10f}" + + +def wilson_interval(successes: int, trials: int) -> tuple[Decimal, Decimal] | None: + """Wilson score interval at 95%. None when the trial count is zero. + + The arithmetic matches ``selection_surface.wilson_interval`` with + ``Z95``. A zero trial count does not invent an interval. + """ + if trials <= 0: + return None + if successes < 0 or successes > trials: + raise ValueError(f"wilson successes={successes} outside 0..{trials}") + z = float(WILSON_Z95) + n = trials + phat = successes / n + z2 = z * z + denom = 1.0 + z2 / n + center = (phat + z2 / (2.0 * n)) / denom + half = z * math.sqrt(phat * (1.0 - phat) / n + z2 / (4.0 * n * n)) / denom + low = max(0.0, center - half) + high = min(1.0, center + half) + if low < 1e-12: + low = 0.0 + if high > 1.0 - 1e-12: + high = 1.0 + return (_quantize_rate(Decimal(low)), _quantize_rate(Decimal(high))) + + +def _ready_rows(rows: list[dict]) -> list[dict]: + ready = [] + for row in rows: + if row.get("development_row") is True: + raise RuntimeError("a development row cannot enter threshold selection") + bucket = row["operator_bucket"] + if bucket not in COMPARISON_BUCKETS | SAFETY_BUCKETS: + raise RuntimeError(f"unexpected operator bucket {bucket}") + if "uses_tier3" not in row or "pos" not in row: + raise RuntimeError("calibration row is missing tier or pos provenance") + status = row.get("score_status", READY) + if status != READY: + continue + text = _residual_text(Decimal(row["residual_score"])) + ready.append({**row, "residual_score": text, "operator_bucket": bucket}) + return ready + + +def _comparison(rows: list[dict]) -> list[dict]: + return [row for row in rows if row["operator_bucket"] in COMPARISON_BUCKETS] + + +def _scores(rows: list[dict], bucket: str) -> list[str]: + return [row["residual_score"] for row in rows if row["operator_bucket"] == bucket] + + +def _predicted_yes(rows: list[dict], threshold: str) -> list[dict]: + cutoff = Decimal(threshold) + return [row for row in rows if Decimal(row["residual_score"]) >= cutoff] + + +def _precision_terms(comparison_rows: list[dict], threshold: str) -> tuple[int, int]: + yes = _predicted_yes(comparison_rows, threshold) + true_high = sum(1 for row in yes if row["operator_bucket"] == "HIGH") + return true_high, len(yes) + + +def precision_gates( + true_high: int, + predicted_yes: int, + *, + minimum_yes: int, + precision_floor: Decimal, + wilson_floor: Decimal, +) -> bool: + """Precision, Wilson lower bound, and minimum YES support. Recall is not a gate.""" + if predicted_yes < minimum_yes or predicted_yes <= 0: + return False + if true_high < 0 or true_high > predicted_yes: + return False + precision = _quantize_rate(Decimal(true_high) / Decimal(predicted_yes)) + if precision < precision_floor: + return False + interval = wilson_interval(true_high, predicted_yes) + if interval is None: + return False + return interval[0] >= wilson_floor + + +def _stable(comparison_rows: list[dict], threshold: str, *, minimum_yes: int, precision_floor: Decimal, wilson_floor: Decimal) -> bool: + """A pass must survive deletion of any one comparison row.""" + if not precision_gates( + *_precision_terms(comparison_rows, threshold), + minimum_yes=minimum_yes, + precision_floor=precision_floor, + wilson_floor=wilson_floor, + ): + return False + for index in range(len(comparison_rows)): + reduced = comparison_rows[:index] + comparison_rows[index + 1 :] + true_high, support = _precision_terms(reduced, threshold) + if not precision_gates( + true_high, + support, + minimum_yes=minimum_yes, + precision_floor=precision_floor, + wilson_floor=wilson_floor, + ): + return False + return True + + +def _safety_counts(rows: list[dict], threshold: str) -> dict[str, int]: + yes = _predicted_yes(rows, threshold) + return { + "quarantine_predicted_yes": sum(1 for row in yes if row["operator_bucket"] == "QUARANTINE"), + "reject_predicted_yes": sum(1 for row in yes if row["operator_bucket"] == "REJECT"), + } + + +def _high_recall(comparison_rows: list[dict], threshold: str) -> Decimal: + high = _scores(comparison_rows, "HIGH") + if not high: + return Decimal(0) + true_high = sum(1 for score in high if Decimal(score) >= Decimal(threshold)) + return _quantize_rate(Decimal(true_high) / Decimal(len(high))) + + +def _metrics(comparison_rows: list[dict], ready: list[dict], threshold: str) -> dict: + true_high, support = _precision_terms(comparison_rows, threshold) + false_high = support - true_high + precision = None if support == 0 else str(_quantize_rate(Decimal(true_high) / Decimal(support))) + interval = wilson_interval(true_high, support) + high_n = len(_scores(comparison_rows, "HIGH")) + abstained = len(comparison_rows) - support + safety = _safety_counts(ready, threshold) + return { + "abstention_count": abstained, + "abstention_rate": str(_quantize_rate(Decimal(abstained) / Decimal(len(comparison_rows)))) if comparison_rows else None, + "false_high": false_high, + "high_recall": str(_high_recall(comparison_rows, threshold)), + "precision": precision, + "precision_interval": None if interval is None else [str(interval[0]), str(interval[1])], + "predicted_yes_support": support, + "quarantine_predicted_yes": safety["quarantine_predicted_yes"], + "reject_predicted_yes": safety["reject_predicted_yes"], + "true_high": true_high, + "true_high_denominator": high_n, + } + + +def _confound(ready: list[dict], high_scores: list[str], secondary_scores: list[str]) -> dict: + views = [ + { + "bucket": row["operator_bucket"], + "pos": row["pos"], + "residual_score": row["residual_score"], + "uses_tier3": row["uses_tier3"], + } + for row in ready + if row["operator_bucket"] in COMPARISON_BUCKETS + ] + tier3 = tier3_concentration(views) + return { + "extreme_driven": extreme_driven(high_scores, secondary_scores), + "pos_partitioned": pos_partitioned(views), + "tier3_concentrated": bool(tier3["concentrated"]), + } + + +def _candidate_thresholds(ready: list[dict]) -> list[str]: + values = sorted({Decimal(row["residual_score"]) for row in ready}) + return [_residual_text(value) for value in values] + + +def _passes_candidate(ready: list[dict], comparison_rows: list[dict], threshold: str) -> bool: + safety = _safety_counts(ready, threshold) + if safety["reject_predicted_yes"] > REJECT_PREDICTED_YES_MAX: + return False + if safety["quarantine_predicted_yes"] > QUARANTINE_PREDICTED_YES_MAX: + return False + return _stable( + comparison_rows, + threshold, + minimum_yes=MIN_PREDICTED_YES, + precision_floor=PRECISION_FLOOR, + wilson_floor=WILSON_LOWER_FLOOR, + ) + + +def select_threshold(rows: list[dict]) -> dict: + """Run the frozen calibration rule. The caller must already have joined labels. + + Development rows are refused. UNKNOWN rows are ignored. No threshold is + read from a file. + """ + ready = _ready_rows(rows) + comparison_rows = _comparison(ready) + high_scores = _scores(comparison_rows, "HIGH") + secondary_scores = _scores(comparison_rows, "SECONDARY") + base = { + "algorithm_id": ALGORITHM_ID, + "high_support": len(high_scores), + "output_labels": list(OUTPUT_LABELS), + "secondary_support": len(secondary_scores), + "threshold_frozen": False, + "threshold_value": None, + } + if len(high_scores) < MIN_CALIBRATION_HIGH or len(secondary_scores) < MIN_CALIBRATION_SECONDARY: + return {**base, "calibration_state": INSUFFICIENT_SUPPORT} + comparison = pair_comparison(high_scores, secondary_scores) + direction = direction_result(comparison) + if direction != "SUPPORTED_DIRECTION": + return {**base, "calibration_state": NO_DIRECTION, "direction": direction} + confound = _confound(ready, high_scores, secondary_scores) + if confound["extreme_driven"] or confound["pos_partitioned"] or confound["tier3_concentrated"]: + return { + **base, + "calibration_state": CONFOUND_REVIEW, + "confound": confound, + "direction": direction, + } + best: tuple[Decimal, Decimal, str] | None = None + best_metrics: dict | None = None + for threshold in _candidate_thresholds(ready): + if not _passes_candidate(ready, comparison_rows, threshold): + continue + recall = _high_recall(comparison_rows, threshold) + rank = (recall, Decimal(threshold)) + if best is None or rank > (best[0], best[1]): + best = (recall, Decimal(threshold), threshold) + best_metrics = _metrics(comparison_rows, ready, threshold) + if best is None: + return {**base, "calibration_state": NO_THRESHOLD, "direction": direction} + return { + **base, + "calibration_state": THRESHOLD_FROZEN, + "direction": direction, + "selection_metrics": best_metrics, + "threshold_frozen": True, + "threshold_value": best[2], + } + + +def semantic_output(score_status: str, residual: str | None, threshold: str) -> str: + """Map one frozen threshold onto YES or UNKNOWN. There is no NO output.""" + if score_status != READY or residual is None: + return "UNKNOWN" + if Decimal(residual) >= Decimal(threshold): + return "YES" + return "UNKNOWN" + + +def measurement_acceptance(rows: list[dict], threshold: str) -> dict: + """Score a frozen threshold. This does not choose a new one.""" + if threshold is None or str(threshold).strip() == "": + raise RuntimeError("measurement requires an already frozen threshold") + ready = _ready_rows(rows) + comparison_rows = _comparison(ready) + high_n = len(_scores(comparison_rows, "HIGH")) + secondary_n = len(_scores(comparison_rows, "SECONDARY")) + cutoff = _residual_text(Decimal(threshold)) + metrics = _metrics(comparison_rows, ready, cutoff) if comparison_rows else None + accepted = ( + high_n >= MIN_MEASUREMENT_HIGH + and secondary_n >= MIN_MEASUREMENT_SECONDARY + and comparison_rows + and _safety_counts(ready, cutoff)["reject_predicted_yes"] <= REJECT_PREDICTED_YES_MAX + and _safety_counts(ready, cutoff)["quarantine_predicted_yes"] <= QUARANTINE_PREDICTED_YES_MAX + and _stable( + comparison_rows, + cutoff, + minimum_yes=MIN_MEASUREMENT_PREDICTED_YES, + precision_floor=MEASUREMENT_PRECISION_FLOOR, + wilson_floor=MEASUREMENT_WILSON_LOWER_FLOOR, + ) + ) + if high_n < MIN_MEASUREMENT_HIGH or secondary_n < MIN_MEASUREMENT_SECONDARY: + state = MEASUREMENT_INSUFFICIENT + elif accepted: + state = MEASUREMENT_ACCEPTED + else: + state = MEASUREMENT_NOT_ACCEPTED + return { + "applied_threshold": cutoff, + "high_support": high_n, + "measurement_state": state, + "metrics": metrics, + "secondary_support": secondary_n, + "threshold_adjusted": False, + } + + +def _common(sealed: dict) -> dict: + return { + "admitted": 0, + "calibration_surface_drawn": False, + "development_rows_reusable_for_threshold_selection": False, + "development_row_count_excluded": 225, + "gold": 0, + "json_schema_document": None, + "json_schema_exists": False, + "measurement_eligible": False, + "measurement_sample_drawn": False, + "measurement_surface_drawn": False, + "next_legal_transition": NEXT_TRANSITION, + "next_transition_authorized": False, + "no_output": "NO", + "output_labels": list(OUTPUT_LABELS), + "rule": RULE, + "runtime_integration": False, + "sealed_ancestors": dict(sorted(sealed.items())), + "select_005_authorized": False, + "selected_source": "none", + "settled": 0, + "state": TRACK_STATE, + "threshold_count": 1, + "threshold_frozen": False, + "threshold_scope": "one_global_T_HIGH", + "threshold_value": None, + "this_pass_selects_threshold": False, + } + + +def documents(sealed: dict, sibling_sha256: dict | None = None) -> dict[str, dict]: + """Return the seven frozen contracts. ``threshold_value`` is always null.""" + common = _common(sealed) + acceptance = { + **common, + "calibration_gates": [ + "resolved HIGH support meets the frozen minimum", + "resolved SECONDARY support meets the frozen minimum", + "HIGH residuals are directionally larger than SECONDARY residuals", + "precision meets the frozen floor", + "predicted YES support meets the frozen minimum", + "Wilson lower bound meets the frozen floor", + "the same precision gates hold after deleting any one comparison row", + "REJECT predicted YES count is 0", + "QUARANTINE predicted YES count is 0", + ], + "confidence_level": CONFIDENCE_LEVEL, + "direction_rule": "median_difference > 0 and rank_biserial > 0 and auc > 0.5", + "direction_rule_source": "residual_model_resolved_replay_v1.direction_result", + "floors_are_not_a_function_of_development_auc": True, + "high_recall_is_a_gate": False, + "interval_method": "wilson_score", + "interval_when_support_is_zero": "no_interval_and_gate_fails", + "minimum_calibration_high": MIN_CALIBRATION_HIGH, + "minimum_calibration_secondary": MIN_CALIBRATION_SECONDARY, + "minimum_predicted_yes": MIN_PREDICTED_YES, + "optimized_objectives_forbidden": ["accuracy", "f1", "youden_j", "auc"], + "precision_denominator": "operator HIGH versus operator SECONDARY among RESIDUAL_READY rows", + "precision_floor": str(PRECISION_FLOOR), + "primary_objective": "highest HIGH recall among thresholds that pass the precision gates", + "quarantine_predicted_yes_maximum": QUARANTINE_PREDICTED_YES_MAX, + "reject_in_precision_denominator": False, + "reject_predicted_yes_maximum": REJECT_PREDICTED_YES_MAX, + "schema": "hyperlex.residual_threshold_v1_acceptance.v1", + "single_row_rule": "deleting any one HIGH or SECONDARY comparison row must leave the precision, Wilson, and minimum-support gates intact", + "wilson_lower_floor": str(WILSON_LOWER_FLOOR), + "wilson_z": str(WILSON_Z95), + } + procedure = { + **common, + "algorithm_id": ALGORITHM_ID, + "candidate_thresholds": "unique RESIDUAL_READY residual values, quantized to 10 decimal places, sorted ascending", + "failure_states": [ + INSUFFICIENT_SUPPORT, + NO_DIRECTION, + CONFOUND_REVIEW, + NO_THRESHOLD, + ], + "prediction_rule": "YES if and only if the row is RESIDUAL_READY and residual >= T_HIGH; otherwise UNKNOWN", + "schema": "hyperlex.residual_threshold_v1_selection_procedure.v1", + "steps": [ + "refuse if any row is marked as a development row", + "ignore rows that are not RESIDUAL_READY", + "stop with CALIBRATION_INSUFFICIENT_SUPPORT when ready HIGH is below 12 or ready SECONDARY is below 8", + "stop with NO_DIRECTIONAL_SIGNAL unless direction_result is SUPPORTED_DIRECTION", + "stop with CALIBRATION_CONFOUND_REVIEW when the frozen tier-3 concentration, extreme-driven, or POS-partition flag is true", + "evaluate each candidate threshold with YES defined as residual >= T", + "keep a threshold only when precision, the Wilson lower bound, minimum YES support, leave-one-out stability, and both safety counts pass", + "among kept thresholds, choose the highest HIGH recall", + "break remaining ties by choosing the larger threshold", + "stop with NO_THRESHOLD_PASSES_PRECISION_GATE when none are kept", + ], + "success_state_of_a_future_run": THRESHOLD_FROZEN, + "tie_break": "larger threshold", + } + calibration = { + **common, + "comparison_buckets": ["HIGH", "SECONDARY"], + "descriptive_checks": [ + "pos", + "token_count", + "character_length", + "content_constituent_count", + "resolution_tier", + "glossbert_involvement", + "candidate_sense_count", + ], + "descriptive_checks_do_not_change_threshold": True, + "eligible_row": "RESIDUAL_READY under the frozen Tier1, Tier2, and Tier3 stack", + "failure_states": procedure["failure_states"], + "glossbert_fields_are_not_threshold_inputs": True, + "labels_joined_only_after_score_hash": True, + "order": [ + "freeze calibration manifest", + "resolve constituents with the frozen stack", + "freeze integrated resolutions", + "compute residuals", + "freeze score file", + "freeze score hash", + "only then join operator labels", + "execute the preregistered threshold algorithm", + "freeze a threshold or freeze a calibration failure", + ], + "preserved_provenance": [ + "tier_1", + "tier_2", + "tier_3", + "glossbert_confidence", + "glossbert_margin", + "candidate_sense_count", + ], + "schema": "hyperlex.residual_threshold_v1_calibration_contract.v1", + "subgroup_thresholds": False, + "unknown_rows_select_threshold": False, + } + measurement = { + **common, + "calibration_success_implies_measurement_success": False, + "confidence_level": CONFIDENCE_LEVEL, + "failure_states": [MEASUREMENT_INSUFFICIENT, MEASUREMENT_NOT_ACCEPTED], + "interval_method": "wilson_score", + "interval_when_support_is_zero": "no_interval_and_acceptance_fails", + "minimum_measurement_high": MIN_MEASUREMENT_HIGH, + "minimum_measurement_secondary": MIN_MEASUREMENT_SECONDARY, + "minimum_predicted_yes": MIN_MEASUREMENT_PREDICTED_YES, + "order": [ + "freeze measurement manifest", + "resolve constituents with the frozen stack", + "compute residuals", + "apply the already frozen T_HIGH", + "freeze predictions", + "only then join operator labels", + "score acceptance without changing T_HIGH", + ], + "outcomes": [ + "YES support", + "YES precision", + "HIGH recall", + "abstention rate", + "false YES count", + "REJECT receiving YES", + "QUARANTINE receiving YES", + ], + "precision_floor": str(MEASUREMENT_PRECISION_FLOOR), + "quarantine_predicted_yes_maximum": QUARANTINE_PREDICTED_YES_MAX, + "reject_predicted_yes_maximum": REJECT_PREDICTED_YES_MAX, + "schema": "hyperlex.residual_threshold_v1_measurement_contract.v1", + "success_state": MEASUREMENT_ACCEPTED, + "threshold_may_be_refit": False, + "wilson_lower_floor": str(MEASUREMENT_WILSON_LOWER_FLOOR), + } + artifact = { + **common, + "document_kind": "artifact_contract", + "future_threshold_artifact_required_fields": [ + "threshold_id", + "residual_candidate_spec_hash", + "resolver_stack_hashes", + "calibration_manifest_hash", + "calibration_score_hash", + "label_hash", + "threshold_value", + "selection_algorithm_id", + "predicted_yes_support", + "true_high", + "false_high", + "precision", + "precision_interval", + "high_recall", + "excluded_reject_count", + "excluded_quarantine_count", + "selection_receipt", + ], + "schema": "hyperlex.residual_threshold_v1_artifact_contract.v1", + "schema_note": "No JSON Schema document exists for this threshold family. This contract names the future fields. It is not a JSON Schema.", + } + isolation = { + **common, + "development_partitions_included": "all 225 reviewed positional rows, including historical v3-v7 partitions stored in the development evidence artifact", + "identity_fences": [ + "row_id, which is hyperlexical.holdout_guard.normalized_text_sha256 of the surface", + "normalized surface text from heldout_census.normalize_group_text", + "PWN 3.0 synset key synset_pos:synset_offset when the offset is non-empty", + ], + "missing_synset_does_not_match_another_missing_synset": True, + "population": "the same positional-unbind universe as the 225-row development inventory", + "primary_existing_fence": "hyperlexical.holdout_guard.normalized_text_sha256", + "provenance_requirements": "unchanged from the evaluation reserve", + "rights_requirements": "unchanged from the evaluation reserve", + "same_synset_on_two_surfaces": False, + "schema": "hyperlex.residual_threshold_v1_surface_isolation.v1", + "synset_justification": "The residual is a property of one synset's resolved constituents. Reusing that synset on another surface is not an independent observation. This exclusion is frozen before any calibration or measurement draw.", + "surfaces": ["DEVELOPMENT", "CALIBRATION", "MEASUREMENT"], + } + preregistration = { + **common, + "asymmetry": "residual >= T_HIGH maps to YES; residual < T_HIGH maps to UNKNOWN; there is no NO path and no lower threshold", + "composition": "normalized_mean_v1", + "distance": "1 - cosine", + "embedding_model": "sentence-transformers/all-MiniLM-L6-v2", + "embedding_revision": "1110a243fdf4706b3f48f1d95db1a4f5529b4d41", + "research_track": RULE, + "residual_track_state_unchanged": "RESIDUAL_DEVELOPMENT_ANALYZED_V2", + "resolver_stack": ["TIER1_STRUCTURAL_OR_UNIQUE", "TIER2_EXTENDED_LESK_V1", "TIER3_GLOSSBERT_FROZEN_CANDIDATE"], + "schema": "hyperlex.residual_threshold_v1_preregistration.v1", + "sibling_artifact_sha256": dict(sorted((sibling_sha256 or {}).items())), + } + body = { + "RESIDUAL_THRESHOLD_V1_ACCEPTANCE.json": acceptance, + "RESIDUAL_THRESHOLD_V1_ARTIFACT_SCHEMA.json": artifact, + "RESIDUAL_THRESHOLD_V1_CALIBRATION_CONTRACT.json": calibration, + "RESIDUAL_THRESHOLD_V1_MEASUREMENT_CONTRACT.json": measurement, + "RESIDUAL_THRESHOLD_V1_PREREGISTRATION.json": preregistration, + "RESIDUAL_THRESHOLD_V1_SELECTION_PROCEDURE.json": procedure, + "RESIDUAL_THRESHOLD_V1_SURFACE_ISOLATION_POLICY.json": isolation, + } + for name, document in body.items(): + if document["threshold_value"] is not None: + raise RuntimeError(f"{name} carries a numeric threshold") + if document["state"] != TRACK_STATE: + raise RuntimeError(f"{name} left the preregistered state") + if RESIDUAL_QUANTUM != SCORE_QUANTUM: + raise RuntimeError("residual quanta disagree") + return body From 09dcf33bab1e8aa785d65c033ef2f034176c0e16 Mon Sep 17 00:00:00 2001 From: Daniel Meyer Date: Mon, 28 Sep 2026 16:28:10 -0700 Subject: [PATCH 21/42] feat(007): land SELECT residual and schedule work (20) --- .../residual_threshold_v2_support.py | 533 ++++++++++++++++++ 1 file changed, 533 insertions(+) create mode 100644 scripts/shadow/hyperlexical/residual_threshold_v2_support.py diff --git a/scripts/shadow/hyperlexical/residual_threshold_v2_support.py b/scripts/shadow/hyperlexical/residual_threshold_v2_support.py new file mode 100644 index 00000000..0d8f816f --- /dev/null +++ b/scripts/shadow/hyperlexical/residual_threshold_v2_support.py @@ -0,0 +1,533 @@ +"""Join frozen v2 labels to frozen calibration readiness and apply the support gate. + +Direction, confound review, and threshold search stay outside this pass. +The join records only the frozen identity, label, row resolution status, and residual. +""" + +from __future__ import annotations + +import hashlib +import json +import sys +from pathlib import Path + +from hyperlexical.residual_threshold_v2_calibration import ( + MIN_CALIBRATION_HIGH, + MIN_CALIBRATION_SECONDARY, + support_passed, +) +from hyperlexical.screen_eval import OPERATOR_BUCKETS + +LEDGER = Path("/home/morpheus/hlx-private/eval-reserve-20260926") +SOURCE = LEDGER / "operator-review/HLX-EVAL-UNBIND-SEMANTIC-EVIDENCE-SOURCE-V1-001" +SENSE = LEDGER / "operator-review/HLX-EVAL-UNBIND-SENSE-SCREEN-V1-HYPOTHESIS-001" +EVENTS = LEDGER / "events.jsonl" +LEDGER_FILE = LEDGER / "ledger.json" +TRACKER = SENSE / "HYPOTHESIS.json" +SPEC = Path("/home/morpheus/Hyperlex/specs/007-hyperlexical-model/evaluation-reserve.md") + +CALIBRATION_MANIFEST = SOURCE / "RESIDUAL_THRESHOLD_V2_CALIBRATION_MANIFEST.jsonl" +MEASUREMENT_MANIFEST = SOURCE / "RESIDUAL_THRESHOLD_V2_MEASUREMENT_MANIFEST.jsonl" +SCORES_PATH = SOURCE / "RESIDUAL_THRESHOLD_V2_CALIBRATION_SCORES.jsonl" +SCORE_RECEIPT = SOURCE / "RESIDUAL_THRESHOLD_V2_CALIBRATION_SCORE_RECEIPT.json" +RESOLUTION_PATH = SOURCE / "RESIDUAL_THRESHOLD_V2_CALIBRATION_RESOLUTION.jsonl" +RESOLUTION_RECEIPT = SOURCE / "RESIDUAL_THRESHOLD_V2_CALIBRATION_RESOLUTION_RECEIPT.json" +FAILURE_PATH = SOURCE / "RESIDUAL_THRESHOLD_V2_CALIBRATION_FAILURE.json" +LABELS_PATH = SOURCE / "RESIDUAL_THRESHOLD_V2_OPERATOR_LABELS.jsonl" +LABEL_RECEIPT_PATH = SOURCE / "RESIDUAL_THRESHOLD_V2_OPERATOR_LABELING_RECEIPT.json" +JOIN_PATH = SOURCE / "RESIDUAL_THRESHOLD_V2_CALIBRATION_SUPPORT_JOIN.jsonl" +JOIN_RECEIPT_PATH = SOURCE / "RESIDUAL_THRESHOLD_V2_CALIBRATION_SUPPORT_JOIN_RECEIPT.json" +RESULT_PATH = SOURCE / "RESIDUAL_THRESHOLD_V2_CALIBRATION_SUPPORT_REASSESSMENT.json" + +AUTHORIZATION = "THRESHOLD_V2_CALIBRATION_SUPPORT_REASSESSMENT_AUTHORIZATION" +EXECUTION_ID = "THRESHOLD_V2_CALIBRATION_EXECUTION_001" +REASSESSMENT_ID = "THRESHOLD_V2_CALIBRATION_SUPPORT_REASSESSMENT_001" +PASSED = "SUPPORT_GATE_PASSED" +FAILED = "CALIBRATION_INSUFFICIENT_SUPPORT" +INTEGRITY_FAILURE = "SUPPORT_REASSESSMENT_INTEGRITY_FAILURE" +DIRECTION_NEXT = "THRESHOLD_V2_DIRECTION_EVALUATION_AUTHORIZATION" +CLASSES = ("HIGH", "SECONDARY", "REJECT", "QUARANTINE") +JOIN_KEYS = frozenset({ + "calibration_row_id", + "operator_label", + "pos", + "pwn30_synset", + "residual_score", + "row_id", + "row_resolution_status", + "surface", +}) + +EXPECTED_CALIBRATION = "34bc70b93039fc6a5ba4bb58685e0fbfe6f91dd1f86a64fec8326a84013a6465" +EXPECTED_MEASUREMENT = "78ca09ab14912681d028e8b9b1c77a8daf1d0c8c81561434eb9b6ade45a753e5" +EXPECTED_FAILURE = "946a686d46b5a2fc47bb9968bf6e39c643e93c297d5a64de9e0fe88fc6eb33eb" +EXPECTED_SCORES = "01c758af570264c190f4af7805d43d734c9e9a36779f0e0b82675709f213e07c" +EXPECTED_SCORE_RECEIPT = "28ddb23bce8cfcf74b8046ee2cfd0908b5dddefa2a0c04562f9c2f8c912e88d5" +EXPECTED_RESOLUTION = "491b854bcf04d20624e560b63c01823b5634716ec266a4d3a7396d5514defd8d" +EXPECTED_RESOLUTION_RECEIPT = "461ed206ef3abba6b4a8ba79ed5de72305cbee95a61c96981923c89817c17d2d" +EXPECTED_LABELS = "f9901e334543dac2e9bf258208097cf4c9b48af9dc8b497ff42fe4f1f106e1fc" +EXPECTED_LABEL_RECEIPT = "4ce71e88c40cd617b9b6b502db48216c51e8cfa22ecea02347c9becc44c967fa" +EXPECTED_PACKET = "d9d986ec85a1ebea257859af8011758e9c84df270c9547f7b8bd0e34cec970fe" +EXPECTED_PACKET_RECEIPT = "0aff22b3406d9d954a54eabe080e83d2d4cb5e99df279605d9403e614b61dcf0" +EXPECTED_REVIEW = "8826b141468ec0dd674d2c7a253acfaa6e1d364ab5191506b424198419166da9" +EXPECTED_TRACKER = "ae73f1b6926cf86a2add088ad99c200f0341aa28c7ecb8b67e6a824cf1ee01d5" +EXPECTED_EVENTS = "96b74a92d44f1cf9fe152b18e5207176f161ba3bfce528dac38aa4571a742f9c" +EXPECTED_LEDGER = "77e22433203879b252f7a9e309d2013d7550101d1c4a014b494d2c96df87d0e0" +EXPECTED_V1_FAILURE = "1d3f1f26609c1f1a47790d94730fba50e9f4d888dd8d7d89e6fa698a924fb3a8" +EXPECTED_V1_CLOSURE = "a5dbdb8ae497c25e711a81a880ad26ec7ef1ace917278c807497af5ec37e5002" +EXPECTED_EXECUTION_LABELS = "5cfeab84f16e5ca77d81bda8f1523a3aa20ae70a9828e1ff04a4d64a1930aa1d" +PRIOR_JOIN = "19457ab6ea99ee8feefe5eb1e62da97f348c1c31a26519f3e01da0cdef8b879a" +PRIOR_RESULT = "dafd4c8242dc08eaad21ae85cacd508eaa672608d3391eb7f828c391ec2a4780" +FROZEN_READY = 60 +FROZEN_UNKNOWN = 140 + + +def refuse(message: str) -> None: + raise SystemExit(message) + + +def sha256_file(path: Path) -> str: + return hashlib.sha256(path.read_bytes()).hexdigest() + + +def sha256_text(text: str) -> str: + return hashlib.sha256(text.encode("utf-8")).hexdigest() + + +def render_json(payload: dict) -> str: + return json.dumps(payload, indent=2, sort_keys=True, ensure_ascii=True) + "\n" + + +def render_jsonl(rows: list[dict]) -> str: + return "".join(json.dumps(row, sort_keys=True, ensure_ascii=True) + "\n" for row in rows) + + +def write_new(path: Path, text: str) -> str: + if path.exists(): + refuse(f"{path.name} already exists") + path.write_text(text, encoding="utf-8") + path.chmod(0o600) + return sha256_text(text) + + +def load_jsonl(path: Path) -> list[dict]: + rows = [] + for line in path.read_text(encoding="utf-8").splitlines(): + if line.strip(): + rows.append(json.loads(line)) + return rows + + +def sealed_hashes() -> dict[Path, str]: + return { + CALIBRATION_MANIFEST: EXPECTED_CALIBRATION, + MEASUREMENT_MANIFEST: EXPECTED_MEASUREMENT, + SCORES_PATH: EXPECTED_SCORES, + SCORE_RECEIPT: EXPECTED_SCORE_RECEIPT, + RESOLUTION_PATH: EXPECTED_RESOLUTION, + RESOLUTION_RECEIPT: EXPECTED_RESOLUTION_RECEIPT, + FAILURE_PATH: EXPECTED_FAILURE, + LABELS_PATH: EXPECTED_LABELS, + LABEL_RECEIPT_PATH: EXPECTED_LABEL_RECEIPT, + SOURCE / "RESIDUAL_THRESHOLD_V2_BLIND_OPERATOR_PACKET.jsonl": EXPECTED_PACKET, + SOURCE / "RESIDUAL_THRESHOLD_V2_BLIND_OPERATOR_PACKET_RECEIPT.json": EXPECTED_PACKET_RECEIPT, + SOURCE / "RESIDUAL_THRESHOLD_V2_CALIBRATION_LABEL_AVAILABILITY_REVIEW.json": EXPECTED_REVIEW, + SOURCE / "RESIDUAL_THRESHOLD_V2_CALIBRATION_LABELS.jsonl": EXPECTED_EXECUTION_LABELS, + SOURCE / "RESIDUAL_THRESHOLD_V1_CALIBRATION_FAILURE.json": EXPECTED_V1_FAILURE, + SOURCE / "RESIDUAL_THRESHOLD_V1_CALIBRATION_DESIGN_CLOSURE.json": EXPECTED_V1_CLOSURE, + EVENTS: EXPECTED_EVENTS, + LEDGER_FILE: EXPECTED_LEDGER, + } + + +def assert_sealed() -> None: + for path, expected in sealed_hashes().items(): + if sha256_file(path) != expected: + refuse(f"sealed artifact drifted: {path.name}") + + +def assert_measurement_sealed() -> None: + if sha256_file(MEASUREMENT_MANIFEST) != EXPECTED_MEASUREMENT: + refuse("measurement manifest drifted") + for name in ( + "RESIDUAL_THRESHOLD_V2_MEASUREMENT_RESOLUTION.jsonl", + "RESIDUAL_THRESHOLD_V2_MEASUREMENT_SCORES.jsonl", + "RESIDUAL_THRESHOLD_V2_MEASUREMENT_LABELS.jsonl", + "RESIDUAL_THRESHOLD_V2_MEASUREMENT_PREDICTIONS.jsonl", + ): + if (SOURCE / name).exists(): + refuse(f"measurement artifact exists: {name}") + + +def join_rows(manifest: list[dict], labels: list[dict], parents: list[dict], scores: list[dict]) -> list[dict]: + """Copy frozen identity, label, parent resolution status, and residual.""" + if not (len(manifest) == len(labels) == len(parents) == len(scores)): + refuse("frozen row counts differ") + joined = [] + for source, label, parent, score in zip(manifest, labels, parents, scores): + row_id = source["row_id"] + if label["row_id"] != row_id or parent["row_id"] != row_id or score["row_id"] != row_id: + refuse(f"row_id join failed for {row_id}") + if source["normalized_text_sha256"] != row_id: + refuse(f"normalized text hash is not the row id for {row_id}") + if label["calibration_row_id"] != source["global_draw_order"]: + refuse(f"calibration row id differs for {row_id}") + if label["pwn30_synset"] != source["pwn30_synset"] or parent["synset"] != source["pwn30_synset"]: + refuse(f"synset differs for {row_id}") + if label["surface"] != source["surface"] or parent["surface"] != source["surface"]: + refuse(f"surface differs for {row_id}") + if label["pos"] != source["pos"] or parent["pos"] != source["pos"]: + refuse(f"pos differs for {row_id}") + if label["operator_label"] not in OPERATOR_BUCKETS: + refuse(f"operator label left the frozen ontology for {row_id}") + status = parent["row_resolution_status"] + if status == "RESIDUAL_READY": + if score.get("score_status") != "SCORED" or not isinstance(score.get("residual_score"), str): + refuse(f"ready row lacks its frozen residual for {row_id}") + residual = score["residual_score"] + elif status == "UNKNOWN": + if score.get("score_status") != "UNKNOWN" or "residual_score" in score: + refuse(f"unknown row carries a residual for {row_id}") + residual = None + else: + refuse(f"row resolution status {status} is outside the frozen vocabulary") + joined.append({ + "calibration_row_id": label["calibration_row_id"], + "operator_label": label["operator_label"], + "pos": source["pos"], + "pwn30_synset": source["pwn30_synset"], + "residual_score": residual, + "row_id": row_id, + "row_resolution_status": status, + "surface": source["surface"], + }) + return joined + + +def _blank_counts() -> dict[str, int]: + return {name: 0 for name in CLASSES} + + +def support_inventory(rows: list[dict]) -> dict: + """Count ready and unknown rows by the frozen operator label. + + The numeric residual is not an input. UNRESOLVED stays outside the four + comparison classes and does not fill the support floors. + """ + ready = _blank_counts() + unknown = _blank_counts() + unresolved = {"RESIDUAL_READY": 0, "UNKNOWN": 0} + inventory = {name: 0 for name in OPERATOR_BUCKETS} + for row in rows: + if set(row) != JOIN_KEYS: + refuse("joined row left the authorized field set") + label = row["operator_label"] + status = row["row_resolution_status"] + inventory[label] += 1 + if status not in {"RESIDUAL_READY", "UNKNOWN"}: + refuse(f"row resolution status {status} is outside the frozen vocabulary") + if label == "UNRESOLVED": + unresolved[status] += 1 + continue + if label not in ready: + refuse(f"operator label {label} is outside the support classes") + target = ready if status == "RESIDUAL_READY" else unknown + target[label] += 1 + ready_rows = sum(ready.values()) + unresolved["RESIDUAL_READY"] + unknown_rows = sum(unknown.values()) + unresolved["UNKNOWN"] + return { + "operator_label_inventory": inventory, + "ready": ready, + "ready_rows": ready_rows, + "ready_unresolved": unresolved["RESIDUAL_READY"], + "unknown_by_class": unknown, + "unknown_rows": unknown_rows, + "unknown_unresolved": unresolved["UNKNOWN"], + } + + +def gate_state(inventory: dict) -> str: + if inventory["ready_rows"] != FROZEN_READY or inventory["unknown_rows"] != FROZEN_UNKNOWN: + refuse("ready split differs from the frozen 60/140 calibration scores") + if inventory["ready_rows"] + inventory["unknown_rows"] != 200: + refuse("joined rows are not 200") + passed = support_passed(inventory["ready"]["HIGH"], inventory["ready"]["SECONDARY"]) + return PASSED if passed else FAILED + + +def _retire_prior(path: Path, expected: str) -> None: + if not path.exists(): + return + found = sha256_file(path) + if found != expected: + refuse(f"{path.name} is not the prior support artifact") + path.unlink() + + +def _integrity_failure(reason: str) -> dict: + payload = { + "authorization": AUTHORIZATION, + "json_schema_document": None, + "json_schema_exists": False, + "measurement_touched": False, + "reason": reason, + "schema": "hyperlex.residual_threshold_v2_support_reassessment_integrity_failure.v1", + "state": INTEGRITY_FAILURE, + } + digest = write_new(SOURCE / "RESIDUAL_THRESHOLD_V2_CALIBRATION_SUPPORT_INTEGRITY_FAILURE.json", render_json(payload)) + return {"integrity_sha256": digest, "state": INTEGRITY_FAILURE} + + +def reassess() -> dict: + assert_sealed() + assert_measurement_sealed() + if sha256_file(TRACKER) != EXPECTED_TRACKER: + refuse("tracker drifted before the support reassessment") + if MIN_CALIBRATION_HIGH != 12 or MIN_CALIBRATION_SECONDARY != 8: + refuse("support floors drifted") + manifest = load_jsonl(CALIBRATION_MANIFEST) + labels = load_jsonl(LABELS_PATH) + scores = load_jsonl(SCORES_PATH) + constituents = load_jsonl(RESOLUTION_PATH) + resolution = json.loads(RESOLUTION_RECEIPT.read_text(encoding="utf-8")) + parents = resolution["parent_rows"] + if len(manifest) != 200 or len(labels) != 200 or len(parents) != 200 or len(scores) != 200: + return _integrity_failure("frozen row count is not 200") + manifest_ids = [row["row_id"] for row in manifest] + if [row["row_id"] for row in labels] != manifest_ids: + return _integrity_failure("label identity set differs from the manifest") + if [row["row_id"] for row in parents] != manifest_ids: + return _integrity_failure("resolution parent identity set differs from the manifest") + if [row["row_id"] for row in scores] != manifest_ids: + return _integrity_failure("score identity set differs from the manifest") + constituent_parents = {row["parent_row_id"] for row in constituents} + if not constituent_parents <= set(manifest_ids): + return _integrity_failure("resolution constituent names a row outside the manifest") + failure = json.loads(FAILURE_PATH.read_text(encoding="utf-8")) + observed = failure["observed_support"] + if observed["unlabeled_scored"] != FROZEN_READY or observed["unlabeled_unknown"] != FROZEN_UNKNOWN: + refuse("execution failure split drifted") + if resolution["residual_ready_rows"] != FROZEN_READY or resolution["unknown_rows"] != FROZEN_UNKNOWN: + refuse("resolution receipt split drifted") + label_receipt = json.loads(LABEL_RECEIPT_PATH.read_text(encoding="utf-8")) + if label_receipt["labeling_state"] != "LABELS_FROZEN" or label_receipt["joined_to_readiness"] is not False: + refuse("label receipt is not the pre-join freeze") + if label_receipt["label_sha256"] != EXPECTED_LABELS: + refuse("label receipt does not bind the frozen labels") + joined = join_rows(manifest, labels, parents, scores) + if [row["operator_label"] for row in joined] != [row["operator_label"] for row in labels]: + refuse("join relabeled a row") + inventory = support_inventory(joined) + if inventory["operator_label_inventory"] != label_receipt["operator_label_inventory"]: + refuse("label inventory changed during the join") + if RESULT_PATH.exists(): + prior = json.loads(RESULT_PATH.read_text(encoding="utf-8")) + if prior.get("ready") != inventory["ready"] or prior.get("unknown_by_class") != inventory["unknown_by_class"]: + refuse("compliant join changed the frozen support counts") + if prior.get("state") != gate_state(inventory): + refuse("compliant join changed the support gate") + state = gate_state(inventory) + _retire_prior(JOIN_PATH, PRIOR_JOIN) + _retire_prior(RESULT_PATH, PRIOR_RESULT) + join_sha = write_new(JOIN_PATH, render_jsonl(joined)) + reread = support_inventory(load_jsonl(JOIN_PATH)) + if reread != inventory or gate_state(reread) != state: + refuse("frozen join does not reproduce the support gate") + join_receipt = { + "authorization": AUTHORIZATION, + "join_identity": "row_id", + "join_sha256": join_sha, + "joined_rows": 200, + "json_schema_document": None, + "json_schema_exists": False, + "labels_modified": False, + "manifest_hash": EXPECTED_CALIBRATION, + "measurement_touched": False, + "operator_label_hash": EXPECTED_LABELS, + "resolution_hash": EXPECTED_RESOLUTION, + "resolution_parent_rows": 200, + "resolution_recomputed": False, + "schema": "hyperlex.residual_threshold_v2_calibration_support_join_receipt.v1", + "score_hash": EXPECTED_SCORES, + "scores_recomputed": False, + } + receipt_sha = write_new(JOIN_RECEIPT_PATH, render_json(join_receipt)) + result = { + "authorization": AUTHORIZATION, + "confound_analysis_performed": False, + "direction_analysis_performed": False, + "execution_id_unchanged": EXECUTION_ID, + "execution_state_unchanged": FAILED, + "floors_changed": False, + "high_floor_passed": inventory["ready"]["HIGH"] >= MIN_CALIBRATION_HIGH, + "join_receipt_sha256": receipt_sha, + "join_sha256": join_sha, + "json_schema_document": None, + "json_schema_exists": False, + "label_sha256": EXPECTED_LABELS, + "measurement_touched": False, + "minimum_ready_high": MIN_CALIBRATION_HIGH, + "minimum_ready_secondary": MIN_CALIBRATION_SECONDARY, + "ready": inventory["ready"], + "ready_rows": inventory["ready_rows"], + "ready_split_unchanged": True, + "ready_unresolved": inventory["ready_unresolved"], + "reassessment_id": REASSESSMENT_ID, + "redraw_authorized": False, + "resolution_recomputed": False, + "residual_recomputed": False, + "rows_relabeled": False, + "runtime_integration": False, + "schema": "hyperlex.residual_threshold_v2_calibration_support_reassessment.v1", + "score_sha256": EXPECTED_SCORES, + "secondary_floor_passed": inventory["ready"]["SECONDARY"] >= MIN_CALIBRATION_SECONDARY, + "select_005_authorized": False, + "selected_source": "none", + "state": state, + "support_gate_passed": state == PASSED, + "threshold_candidates_inspected": False, + "threshold_frozen": False, + "threshold_search_performed": False, + "threshold_value": None, + "unknown_by_class": inventory["unknown_by_class"], + "unknown_rows": inventory["unknown_rows"], + "unknown_unresolved": inventory["unknown_unresolved"], + } + result_sha = write_new(RESULT_PATH, render_json(result)) + tracker_sha = update_tracker(state, join_sha, receipt_sha, result_sha) + assert_sealed() + assert_measurement_sealed() + append_spec(state, inventory, join_sha, receipt_sha, result_sha, tracker_sha) + return { + "join_receipt_sha256": receipt_sha, + "join_sha256": join_sha, + "ready": inventory["ready"], + "ready_unresolved": inventory["ready_unresolved"], + "result_sha256": result_sha, + "state": state, + "tracker_sha256": tracker_sha, + "unknown_by_class": inventory["unknown_by_class"], + "unknown_unresolved": inventory["unknown_unresolved"], + } + + +def update_tracker(state: str, join_sha: str, receipt_sha: str, result_sha: str) -> str: + if sha256_file(TRACKER) != EXPECTED_TRACKER: + refuse("tracker drifted before the support update") + tracker = json.loads(TRACKER.read_text(encoding="utf-8")) + if tracker.get("residual_threshold_v2_calibration_execution_state") != FAILED: + refuse("execution 001 state drifted") + if tracker.get("residual_threshold_v2_blind_operator_labeling_state") != "LABELS_FROZEN": + refuse("label freeze drifted") + if tracker.get("residual_threshold_v1_disposition") != "CLOSED_CALIBRATION_DESIGN_INSUFFICIENT": + refuse("v1 disposition drifted") + if tracker.get("residual_threshold_v2_value") is not None or tracker.get("residual_threshold_v2_frozen") is not False: + refuse("threshold drifted") + tracker["previous_tracker_sha256"] = EXPECTED_TRACKER + tracker["residual_threshold_v2_support_reassessment_id"] = REASSESSMENT_ID + tracker["residual_threshold_v2_support_reassessment_state"] = state + tracker["residual_threshold_v2_support_join_sha256"] = join_sha + tracker["residual_threshold_v2_support_join_receipt_sha256"] = receipt_sha + tracker["residual_threshold_v2_support_reassessment_sha256"] = result_sha + tracker["residual_threshold_v2_state"] = state + tracker["residual_threshold_v2_value"] = None + tracker["residual_threshold_v2_frozen"] = False + tracker["residual_threshold_v2_redraw_authorized"] = False + tracker["residual_threshold_v2_measurement_state"] = "SEALED" + tracker["residual_threshold_v2_measurement_resolved"] = False + tracker["residual_threshold_v2_measurement_scored"] = False + tracker["residual_threshold_v2_measurement_labeled"] = False + tracker["measurement_eligible"] = False + tracker["selected_source"] = "none" + tracker["select_authorized"] = False + tracker["admitted"] = 0 + tracker["settled"] = 0 + tracker["gold"] = 0 + if state == PASSED: + tracker["next_legal_transition"] = DIRECTION_NEXT + tracker["residual_threshold_v2_disposition"] = None + else: + tracker["next_legal_transition"] = "NONE" + tracker["residual_threshold_v2_disposition"] = "CLOSED_CALIBRATION_INSUFFICIENT_SUPPORT" + tracker["next_transition_authorized"] = False + if tracker["residual_threshold_v2_calibration_execution_state"] != FAILED: + refuse("reassessment overwrote execution 001") + text = render_json(tracker) + TRACKER.write_text(text, encoding="utf-8") + TRACKER.chmod(0o600) + return sha256_file(TRACKER) + + +def append_spec(state: str, inventory: dict, join_sha: str, receipt_sha: str, result_sha: str, tracker_sha: str) -> None: + marker = "## Threshold v2 support reassessment — 2026-09-28" + text = SPEC.read_text(encoding="utf-8") + ready = inventory["ready"] + unknown = inventory["unknown_by_class"] + if state == PASSED: + outcome = ( + f"The support gate passes. Ready HIGH is {ready['HIGH']} and ready SECONDARY is " + f"{ready['SECONDARY']}. The floors remain 12 and 8. " + f"The next named transition is `{DIRECTION_NEXT}`. This pass does not authorize it." + ) + else: + outcome = ( + f"The support gate fails. Ready HIGH is {ready['HIGH']} and ready SECONDARY is " + f"{ready['SECONDARY']}. The floors remain 12 and 8. v2 calibration closes as " + "`CALIBRATION_INSUFFICIENT_SUPPORT` without a redraw. The threshold value stays null." + ) + section = ( + f"{marker}\n" + "\n" + f"`{AUTHORIZATION}` joins the frozen operator labels to the frozen calibration " + "parent resolution status and residual scores by `row_id`. It checks calibration row id, " + "normalized text hash, and PWN 3.0 synset for equality. It does not relabel a row, " + "recompute resolution, recompute a residual, or change the 60/140 ready split. " + f"`{EXECUTION_ID}` stays `CALIBRATION_INSUFFICIENT_SUPPORT` and stays sealed. " + "The resolution file stores constituent lines. Its 200 parent rows are the row identity, " + "and that set matches the manifest and the labels.\n" + "\n" + "Each joined row records only the calibration row id, row id, surface, POS, synset, " + "operator label, row resolution status, and residual score. An unknown row has a null residual. " + f"Ready rows are HIGH {ready['HIGH']}, SECONDARY {ready['SECONDARY']}, " + f"REJECT {ready['REJECT']}, QUARANTINE {ready['QUARANTINE']}, and UNRESOLVED " + f"{inventory['ready_unresolved']}. Unknown rows are HIGH {unknown['HIGH']}, " + f"SECONDARY {unknown['SECONDARY']}, REJECT {unknown['REJECT']}, QUARANTINE {unknown['QUARANTINE']}, " + f"and UNRESOLVED {inventory['unknown_unresolved']}. " + f"The raw label inventory remains HIGH {inventory['operator_label_inventory']['HIGH']}, " + f"SECONDARY {inventory['operator_label_inventory']['SECONDARY']}, " + f"REJECT {inventory['operator_label_inventory']['REJECT']}, " + f"QUARANTINE {inventory['operator_label_inventory']['QUARANTINE']}, " + f"and UNRESOLVED {inventory['operator_label_inventory']['UNRESOLVED']}.\n" + "\n" + f"{outcome} Direction analysis, confound review, and threshold search were not run. " + f"Join sha256 `{join_sha}`. Join receipt sha256 `{receipt_sha}`. " + f"Support reassessment sha256 `{result_sha}`. Tracker sha256 `{tracker_sha}`.\n" + "\n" + "The threshold stays unfrozen. Measurement stays `SEALED`, unresolved, unscored, and " + f"unlabeled. Measurement sha256 remains `{EXPECTED_MEASUREMENT}`. Score sha256 remains " + f"`{EXPECTED_SCORES}`. Resolution sha256 remains `{EXPECTED_RESOLUTION}`. " + f"Failure sha256 remains `{EXPECTED_FAILURE}`. Label sha256 remains `{EXPECTED_LABELS}`. " + "`selected_source` remains `none`. Nothing was integrated into runtime. " + "SELECT-005 remains unauthorized. Admitted, settled, and gold stay 0. " + f"Events sha256 remains `{EXPECTED_EVENTS}`. The ledger was not appended. " + "No JSON Schema document exists for this reassessment family. v1 remains " + "`CLOSED_CALIBRATION_DESIGN_INSUFFICIENT`.\n" + ) + if marker in text: + start = text.index(marker) + SPEC.write_text(text[:start] + section, encoding="utf-8") + return + if not text.endswith("\n"): + text += "\n" + SPEC.write_text(text + section, encoding="utf-8") + + +def main() -> None: + if len(sys.argv) == 2 and sys.argv[1] == "--reassess": + print(json.dumps(reassess(), sort_keys=True)) + return + refuse("usage: residual_threshold_v2_support.py --reassess") + + +if __name__ == "__main__": + main() From 817c9ff5d731422912b604173b815320d809788f Mon Sep 17 00:00:00 2001 From: Daniel Meyer Date: Mon, 28 Sep 2026 16:29:36 -0700 Subject: [PATCH 22/42] feat(007): land SELECT residual and schedule work (21) --- .../residual_threshold_v2_completion.py | 752 ++++++++++++++++++ 1 file changed, 752 insertions(+) create mode 100644 scripts/shadow/hyperlexical/residual_threshold_v2_completion.py diff --git a/scripts/shadow/hyperlexical/residual_threshold_v2_completion.py b/scripts/shadow/hyperlexical/residual_threshold_v2_completion.py new file mode 100644 index 00000000..bafcc514 --- /dev/null +++ b/scripts/shadow/hyperlexical/residual_threshold_v2_completion.py @@ -0,0 +1,752 @@ +"""Finish the remaining preregistered v2 calibration gates in one pass. + +Confound evaluation uses the frozen stop flags. Threshold search runs only +after those flags pass, and it calls the frozen precision-gated selector. +Measurement rows are not read. +""" + +from __future__ import annotations + +import hashlib +import json +import sys +from decimal import Decimal +from pathlib import Path + +from hyperlexical.residual_model_resolved_replay_v1 import ( + TIER3_GLOSSBERT, + confound_report, +) +from hyperlexical.residual_threshold_v1 import ( + ALGORITHM_ID, + CONFOUND_REVIEW, + MIN_PREDICTED_YES, + NO_THRESHOLD, + PRECISION_FLOOR, + THRESHOLD_FROZEN, + WILSON_LOWER_FLOOR, + _candidate_thresholds, + _comparison, + _high_recall, + _metrics, + _passes_candidate, + _precision_terms, + _quantize_rate, + _ready_rows, + _residual_text, + _safety_counts, + _stable, + select_threshold, + wilson_interval, +) +from hyperlexical.residual_threshold_v2_calibration import MEASUREMENT_NEXT + +LEDGER = Path("/home/morpheus/hlx-private/eval-reserve-20260926") +SOURCE = LEDGER / "operator-review/HLX-EVAL-UNBIND-SEMANTIC-EVIDENCE-SOURCE-V1-001" +SENSE = LEDGER / "operator-review/HLX-EVAL-UNBIND-SENSE-SCREEN-V1-HYPOTHESIS-001" +EVENTS = LEDGER / "events.jsonl" +LEDGER_FILE = LEDGER / "ledger.json" +TRACKER = SENSE / "HYPOTHESIS.json" +SPEC = Path("/home/morpheus/Hyperlex/specs/007-hyperlexical-model/evaluation-reserve.md") + +JOIN_PATH = SOURCE / "RESIDUAL_THRESHOLD_V2_CALIBRATION_SUPPORT_JOIN.jsonl" +SCORES_PATH = SOURCE / "RESIDUAL_THRESHOLD_V2_CALIBRATION_SCORES.jsonl" +LABELS_PATH = SOURCE / "RESIDUAL_THRESHOLD_V2_OPERATOR_LABELS.jsonl" +MANIFEST_PATH = SOURCE / "RESIDUAL_THRESHOLD_V2_CALIBRATION_MANIFEST.jsonl" +RESOLUTION_RECEIPT = SOURCE / "RESIDUAL_THRESHOLD_V2_CALIBRATION_RESOLUTION_RECEIPT.json" +MEASUREMENT_MANIFEST = SOURCE / "RESIDUAL_THRESHOLD_V2_MEASUREMENT_MANIFEST.jsonl" +DIRECTION_ANALYSIS = SOURCE / "RESIDUAL_THRESHOLD_V2_DIRECTION_ANALYSIS.json" +DIRECTION_DECISION = SOURCE / "RESIDUAL_THRESHOLD_V2_DIRECTION_DECISION.json" +EXECUTION_CONFOUND = SOURCE / "RESIDUAL_THRESHOLD_V2_CALIBRATION_CONFOUND_ANALYSIS.json" +EXECUTION_SEARCH = SOURCE / "RESIDUAL_THRESHOLD_V2_THRESHOLD_SEARCH.json" +EXECUTION_FAILURE = SOURCE / "RESIDUAL_THRESHOLD_V2_CALIBRATION_FAILURE.json" +EXECUTION_ANALYSIS = SOURCE / "RESIDUAL_THRESHOLD_V2_CALIBRATION_ANALYSIS.json" + +CONFOUND_ANALYSIS_PATH = SOURCE / "RESIDUAL_THRESHOLD_V2_CONFOUND_ANALYSIS.json" +CONFOUND_DECISION_PATH = SOURCE / "RESIDUAL_THRESHOLD_V2_CONFOUND_DECISION.json" +COMPLETION_SEARCH_PATH = SOURCE / "RESIDUAL_THRESHOLD_V2_COMPLETION_THRESHOLD_SEARCH.json" +FROZEN_PATH = SOURCE / "RESIDUAL_THRESHOLD_V2_THRESHOLD_FROZEN.json" +COMPLETION_FAILURE_PATH = SOURCE / "RESIDUAL_THRESHOLD_V2_CALIBRATION_COMPLETION_FAILURE.json" +RECEIPT_PATH = SOURCE / "RESIDUAL_THRESHOLD_V2_CALIBRATION_COMPLETION_RECEIPT.json" + +AUTHORIZATION = "THRESHOLD_V2_CALIBRATION_COMPLETION_AUTHORIZATION" +EXECUTION_ID = "THRESHOLD_V2_CALIBRATION_EXECUTION_001" +STOP_FLAGS = ("extreme_driven", "pos_partitioned", "tier3_concentrated") +CLASSES = ("HIGH", "SECONDARY", "REJECT", "QUARANTINE", "UNRESOLVED") +READY_COUNTS = {"HIGH": 13, "SECONDARY": 22, "REJECT": 24, "QUARANTINE": 1, "UNRESOLVED": 0} +UNKNOWN_COUNTS = {"HIGH": 31, "SECONDARY": 60, "REJECT": 47, "QUARANTINE": 2, "UNRESOLVED": 0} +TRAINING_BLOCKED = "BLOCKED_BY_THRESHOLD_V2_CALIBRATION" +TRAINING_PENDING = "CALIBRATION_COMPLETE_MEASUREMENT_PENDING" + +EXPECTED_JOIN = "02088379b5ebe0f26106e830a730e8a90b2281d83c37bf1e750e1846bffa128b" +EXPECTED_SCORES = "01c758af570264c190f4af7805d43d734c9e9a36779f0e0b82675709f213e07c" +EXPECTED_LABELS = "f9901e334543dac2e9bf258208097cf4c9b48af9dc8b497ff42fe4f1f106e1fc" +EXPECTED_MANIFEST = "34bc70b93039fc6a5ba4bb58685e0fbfe6f91dd1f86a64fec8326a84013a6465" +EXPECTED_MEASUREMENT = "78ca09ab14912681d028e8b9b1c77a8daf1d0c8c81561434eb9b6ade45a753e5" +EXPECTED_DIRECTION_ANALYSIS = "b68483acab29fe5918a81281798974fb85000a18af30ec3411c2e30500292783" +EXPECTED_DIRECTION_DECISION = "d408c81ff5766d40ece5c18c2c971ce2e05c23b8163fa9a809f2eded58a852eb" +EXPECTED_EXECUTION_CONFOUND = "3695640fd649aab2e077028aae302c7c59578e2085b0d7de16127c948f19e954" +EXPECTED_EXECUTION_SEARCH = "701ab0858a46490a6df4cf0ba8c41c0b16dc1f6738a0f9b76044812a3bdfb44c" +EXPECTED_EXECUTION_FAILURE = "946a686d46b5a2fc47bb9968bf6e39c643e93c297d5a64de9e0fe88fc6eb33eb" +EXPECTED_EXECUTION_ANALYSIS = "46a8ecb371ec8dfe6ab38875a1500b463fda3db533cffb05129b4d8070963f2a" +EXPECTED_RESOLUTION = "491b854bcf04d20624e560b63c01823b5634716ec266a4d3a7396d5514defd8d" +EXPECTED_RESOLUTION_RECEIPT = "461ed206ef3abba6b4a8ba79ed5de72305cbee95a61c96981923c89817c17d2d" +EXPECTED_REASSESSMENT = "fe5d1061c1f9aed9cad4a1ad8e1dbabc5f76d13d4b13eacb88cef12c347e560d" +EXPECTED_JOIN_RECEIPT = "05748f812491be4ebe215a9a88224073bb2349b465c09dd2598f98d60e02947d" +EXPECTED_V1_FAILURE = "1d3f1f26609c1f1a47790d94730fba50e9f4d888dd8d7d89e6fa698a924fb3a8" +EXPECTED_V1_CLOSURE = "a5dbdb8ae497c25e711a81a880ad26ec7ef1ace917278c807497af5ec37e5002" +EXPECTED_PREREGISTRATION = "1c962703e12c5c48fd279dc766fd4d1d3108c40486a10789fdda112926e98261" +EXPECTED_TRACKER = "7a2e3eb6265ddb90624f2ef5034d77f486d1cec83e2c2792d7659a56bd076abe" +EXPECTED_EVENTS = "96b74a92d44f1cf9fe152b18e5207176f161ba3bfce528dac38aa4571a742f9c" +EXPECTED_LEDGER = "77e22433203879b252f7a9e309d2013d7550101d1c4a014b494d2c96df87d0e0" + + +def refuse(message: str) -> None: + raise SystemExit(message) + + +def sha256_file(path: Path) -> str: + return hashlib.sha256(path.read_bytes()).hexdigest() + + +def sha256_text(text: str) -> str: + return hashlib.sha256(text.encode("utf-8")).hexdigest() + + +def render_json(payload: dict) -> str: + return json.dumps(payload, indent=2, sort_keys=True, ensure_ascii=True) + "\n" + + +def write_new(path: Path, text: str) -> str: + if path.exists(): + refuse(f"{path.name} already exists") + path.write_text(text, encoding="utf-8") + path.chmod(0o600) + return sha256_text(text) + + +def load_jsonl(path: Path) -> list[dict]: + return [json.loads(line) for line in path.read_text(encoding="utf-8").splitlines() if line.strip()] + + +def confound_stop(flags: dict) -> bool: + """The frozen stop is only tier-3 concentration, extreme-driven separation, or a POS partition.""" + return bool(flags["extreme_driven"] or flags["pos_partitioned"] or flags["tier3_concentrated"]) + + +def itemized_gate_counts(ready: list[dict], comparison: list[dict], candidates: list[str]) -> dict[str, int]: + """Count each frozen criterion on its own. Selection still uses the combined rule.""" + counts = { + "all_gates": 0, + "candidates": 0, + "leave_one_out": 0, + "precision_floor": 0, + "predicted_yes_support": 0, + "quarantine_veto": 0, + "reject_veto": 0, + "wilson_lower": 0, + } + for threshold in candidates: + true_high, support = _precision_terms(comparison, threshold) + safety = _safety_counts(ready, threshold) + interval = wilson_interval(true_high, support) if support > 0 else None + precision_ok = False + if support > 0: + precision_ok = _quantize_rate(Decimal(true_high) / Decimal(support)) >= PRECISION_FLOOR + wilson_ok = interval is not None and interval[0] >= WILSON_LOWER_FLOOR + counts["candidates"] += 1 + counts["predicted_yes_support"] += int(support >= MIN_PREDICTED_YES) + counts["precision_floor"] += int(precision_ok) + counts["wilson_lower"] += int(wilson_ok) + counts["leave_one_out"] += int( + _stable( + comparison, + threshold, + minimum_yes=MIN_PREDICTED_YES, + precision_floor=PRECISION_FLOOR, + wilson_floor=WILSON_LOWER_FLOOR, + ) + ) + counts["reject_veto"] += int(safety["reject_predicted_yes"] == 0) + counts["quarantine_veto"] += int(safety["quarantine_predicted_yes"] == 0) + counts["all_gates"] += int(_passes_candidate(ready, comparison, threshold)) + return counts + + +def _sealed() -> dict[Path, str]: + return { + JOIN_PATH: EXPECTED_JOIN, + SOURCE / "RESIDUAL_THRESHOLD_V2_CALIBRATION_SUPPORT_JOIN_RECEIPT.json": EXPECTED_JOIN_RECEIPT, + SOURCE / "RESIDUAL_THRESHOLD_V2_CALIBRATION_SUPPORT_REASSESSMENT.json": EXPECTED_REASSESSMENT, + SCORES_PATH: EXPECTED_SCORES, + LABELS_PATH: EXPECTED_LABELS, + MANIFEST_PATH: EXPECTED_MANIFEST, + MEASUREMENT_MANIFEST: EXPECTED_MEASUREMENT, + DIRECTION_ANALYSIS: EXPECTED_DIRECTION_ANALYSIS, + DIRECTION_DECISION: EXPECTED_DIRECTION_DECISION, + EXECUTION_CONFOUND: EXPECTED_EXECUTION_CONFOUND, + EXECUTION_SEARCH: EXPECTED_EXECUTION_SEARCH, + EXECUTION_FAILURE: EXPECTED_EXECUTION_FAILURE, + EXECUTION_ANALYSIS: EXPECTED_EXECUTION_ANALYSIS, + SOURCE / "RESIDUAL_THRESHOLD_V2_CALIBRATION_RESOLUTION.jsonl": EXPECTED_RESOLUTION, + RESOLUTION_RECEIPT: EXPECTED_RESOLUTION_RECEIPT, + SOURCE / "RESIDUAL_THRESHOLD_V1_CALIBRATION_FAILURE.json": EXPECTED_V1_FAILURE, + SOURCE / "RESIDUAL_THRESHOLD_V1_CALIBRATION_DESIGN_CLOSURE.json": EXPECTED_V1_CLOSURE, + SOURCE / "RESIDUAL_THRESHOLD_V2_PREREGISTRATION.json": EXPECTED_PREREGISTRATION, + EVENTS: EXPECTED_EVENTS, + LEDGER_FILE: EXPECTED_LEDGER, + } + + +def assert_sealed() -> None: + for path, expected in _sealed().items(): + if sha256_file(path) != expected: + refuse(f"sealed artifact drifted: {path.name}") + + +def assert_measurement_sealed() -> None: + if sha256_file(MEASUREMENT_MANIFEST) != EXPECTED_MEASUREMENT: + refuse("measurement manifest drifted") + for name in ( + "RESIDUAL_THRESHOLD_V2_MEASUREMENT_RESOLUTION.jsonl", + "RESIDUAL_THRESHOLD_V2_MEASUREMENT_SCORES.jsonl", + "RESIDUAL_THRESHOLD_V2_MEASUREMENT_LABELS.jsonl", + "RESIDUAL_THRESHOLD_V2_MEASUREMENT_PREDICTIONS.jsonl", + ): + if (SOURCE / name).exists(): + refuse(f"measurement artifact exists: {name}") + + +def _index(rows: list[dict]) -> dict[str, dict]: + found = {} + for row in rows: + row_id = row["row_id"] + if row_id in found: + refuse(f"duplicate row_id {row_id}") + found[row_id] = row + return found + + +def assemble(join_rows: list[dict], scores: list[dict], labels: list[dict], manifest: list[dict], parents: list[dict]) -> dict: + """Copy frozen labels, residuals, tier flags, and the nuisance fields that were stored.""" + if not (len(join_rows) == len(scores) == len(labels) == len(manifest) == len(parents) == 200): + refuse("frozen calibration tables are not 200 rows") + by_score = _index(scores) + by_label = _index(labels) + by_manifest = _index(manifest) + by_parent = _index(parents) + selectable = [] + confound_rows = [] + ready_counts = {name: 0 for name in CLASSES} + unknown_counts = {name: 0 for name in CLASSES} + for joined in join_rows: + row_id = joined["row_id"] + score = by_score[row_id] + label = by_label[row_id] + source = by_manifest[row_id] + parent = by_parent[row_id] + bucket = joined["operator_label"] + if bucket not in ready_counts or label["operator_label"] != bucket: + refuse("operator label drifted from the support join") + if joined["surface"] != source["surface"] or joined["surface"] != score.get("surface", joined["surface"]): + refuse("surface drifted") + if joined["pos"] != source["pos"] or joined["pos"] != parent["pos"]: + refuse("pos drifted") + if joined["pwn30_synset"] != parent["synset"] or joined["pwn30_synset"] != source["pwn30_synset"]: + refuse("synset drifted") + status = joined["row_resolution_status"] + if status == "RESIDUAL_READY": + if score.get("score_status") != "SCORED" or parent["row_resolution_status"] != "RESIDUAL_READY": + refuse("readiness drifted") + residual = joined["residual_score"] + if residual != score["residual_score"]: + refuse("residual string drifted from the score file") + if _residual_text(Decimal(residual)) != residual: + refuse("residual string is not already the frozen quantum") + tiers = list(score["constituent_resolution_tiers"]) + uses_tier3 = score["uses_tier3"] + if uses_tier3 is not (TIER3_GLOSSBERT in tiers): + refuse("frozen uses_tier3 disagrees with frozen tiers") + if len(score["content_constituents"]) != len(parent["selected_constituent_synsets"]): + refuse("frozen constituent count disagrees") + if str(source["token_count"]) != str(len(source["surface"].split())): + refuse("frozen token_count disagrees with the frozen surface") + ready_counts[bucket] += 1 + selectable.append( + { + "development_row": False, + "operator_bucket": bucket, + "pos": joined["pos"], + "residual_score": residual, + "score_status": "SCORED", + "uses_tier3": uses_tier3, + } + ) + confound_rows.append( + { + "bucket": bucket, + "character_length": str(len(source["surface"])), + "content_count": str(len(score["content_constituents"])), + "max_candidate_senses": None, + "min_glossbert_confidence": None, + "min_glossbert_margin": None, + "pos": joined["pos"], + "residual_score": residual, + "row_id": row_id, + "surface": source["surface"], + "synset": joined["pwn30_synset"], + "tiers": tiers, + "token_count": str(source["token_count"]), + "uses_tier3": uses_tier3, + } + ) + elif status == "UNKNOWN": + if score.get("score_status") != "UNKNOWN" or joined["residual_score"] is not None: + refuse("unknown row changed") + if parent["row_resolution_status"] != "UNKNOWN": + refuse("parent readiness drifted") + unknown_counts[bucket] += 1 + else: + refuse("unexpected resolution status") + if ready_counts != READY_COUNTS or unknown_counts != UNKNOWN_COUNTS: + refuse("support counts drifted") + return {"confound_rows": confound_rows, "selectable": selectable} + + +def _flags_from_report(report: dict) -> dict: + return { + "extreme_driven": report["extreme_driven"], + "pos_partitioned": report["pos_partitioned"], + "tier3_concentrated": bool(report["tier3"]["concentrated"]), + } + + +def _nuisance_provenance() -> dict: + return { + "character_length": "FROZEN_SURFACE_LENGTH", + "content_count": "FROZEN_CONTENT_CONSTITUENT_COUNT", + "max_candidate_senses": "NOT_PERSISTED", + "min_glossbert_confidence": "NOT_PERSISTED", + "min_glossbert_margin": "NOT_PERSISTED", + "token_count": "FROZEN_MANIFEST_TOKEN_COUNT", + "uses_tier3": "FROZEN_SCORE_FIELD", + } + + +def _search_body(selectable: list[dict], selection: dict) -> dict: + ready = _ready_rows(selectable) + comparison = _comparison(ready) + candidates = _candidate_thresholds(ready) + counts = itemized_gate_counts(ready, comparison, candidates) + passing = [ + (str(_high_recall(comparison, threshold)), threshold) + for threshold in candidates + if _passes_candidate(ready, comparison, threshold) + ] + selected = selection.get("threshold_value") + if passing: + best = max(passing, key=lambda item: (Decimal(item[0]), Decimal(item[1]))) + if selected != best[1]: + refuse("threshold search disagrees with select_threshold") + elif selected is not None: + refuse("select_threshold returned a value with no passing candidate") + if counts["all_gates"] != len(passing): + refuse("combined gate count disagrees with the passing candidates") + return { + "algorithm_id": ALGORITHM_ID, + "candidate_count": len(candidates), + "candidates_evaluated": True, + "execution_001_search_sha256": EXPECTED_EXECUTION_SEARCH, + "execution_001_search_unchanged": True, + "gate_pass_counts": counts, + "json_schema_document": None, + "json_schema_exists": False, + "precision_denominator": "HIGH positive, SECONDARY negative", + "schema": "hyperlex.residual_threshold_v2_completion_threshold_search.v1", + "search_executed": True, + "selected_threshold": selected, + "selection_algorithm_id": ALGORITHM_ID, + } + + +def _receipt(confound_result: str, threshold_state: str, threshold_value, threshold_frozen: bool, training: str, measurement_eligible: bool) -> dict: + return { + "calibration_redraw": False, + "confound_gate": confound_result, + "direction_gate": "SUPPORTED_DIRECTION", + "json_schema_document": None, + "json_schema_exists": False, + "measurement_eligible": measurement_eligible, + "measurement_state": "SEALED", + "operator_labeling": "LABELS_FROZEN", + "runtime_integration": False, + "select_005_authorized": False, + "selected_source": "none", + "support_gate": "SUPPORT_GATE_PASSED", + "surface_draw": "SURFACES_FROZEN", + "threshold_frozen": threshold_frozen, + "threshold_search": threshold_state, + "threshold_value": threshold_value, + "training_authorized": False, + "training_readiness": training, + } + + +def update_tracker(state: str, threshold_value, threshold_frozen: bool, training: str, measurement_eligible: bool, next_transition: str, digests: dict) -> str: + if sha256_file(TRACKER) != EXPECTED_TRACKER: + refuse("tracker drifted before the completion update") + tracker = json.loads(TRACKER.read_text(encoding="utf-8")) + if tracker.get("residual_threshold_v2_calibration_execution_state") != "CALIBRATION_INSUFFICIENT_SUPPORT": + refuse("execution 001 drifted") + if tracker.get("residual_threshold_v2_direction_evaluation_state") != "SUPPORTED_DIRECTION": + refuse("direction state drifted") + if tracker.get("residual_threshold_v2_support_reassessment_state") != "SUPPORT_GATE_PASSED": + refuse("support state drifted") + if tracker.get("residual_threshold_v1_disposition") != "CLOSED_CALIBRATION_DESIGN_INSUFFICIENT": + refuse("v1 disposition drifted") + tracker["previous_tracker_sha256"] = EXPECTED_TRACKER + tracker["residual_threshold_v2_state"] = state + tracker["residual_threshold_v2_value"] = threshold_value + tracker["residual_threshold_v2_frozen"] = threshold_frozen + tracker["residual_threshold_v2_redraw_authorized"] = False + tracker["residual_threshold_v2_measurement_state"] = "SEALED" + tracker["residual_threshold_v2_measurement_resolved"] = False + tracker["residual_threshold_v2_measurement_scored"] = False + tracker["residual_threshold_v2_measurement_labeled"] = False + tracker["residual_threshold_v2_training_readiness"] = training + tracker["residual_threshold_v2_training_authorized"] = False + tracker["measurement_eligible"] = measurement_eligible + tracker["selected_source"] = "none" + tracker["select_authorized"] = False + tracker["admitted"] = 0 + tracker["settled"] = 0 + tracker["gold"] = 0 + tracker["next_legal_transition"] = next_transition + tracker["next_transition_authorized"] = False + for key, digest in digests.items(): + tracker[key] = digest + if tracker["residual_threshold_v2_calibration_execution_state"] != "CALIBRATION_INSUFFICIENT_SUPPORT": + refuse("completion changed execution 001") + if tracker["residual_threshold_v2_calibration_failure_sha256"] != EXPECTED_EXECUTION_FAILURE: + refuse("completion changed the execution failure hash") + if tracker["residual_threshold_v2_calibration_search_sha256"] != EXPECTED_EXECUTION_SEARCH: + refuse("completion changed the execution search hash") + text = render_json(tracker) + TRACKER.write_text(text, encoding="utf-8") + TRACKER.chmod(0o600) + return sha256_file(TRACKER) + + +def _spearman_line(report: dict) -> str: + parts = [] + for field, body in report["nuisance"].items(): + high = body["HIGH"].get("spearman_with_residual") + secondary = body["SECONDARY"].get("spearman_with_residual") + high_status = body["HIGH"].get("status") + secondary_status = body["SECONDARY"].get("status") + parts.append( + f"{field} HIGH {high_status or high}, SECONDARY {secondary_status or secondary}" + ) + return "; ".join(parts) + + +def append_spec(summary: dict) -> None: + marker = "## Threshold v2 calibration completion — 2026-09-28" + text = SPEC.read_text(encoding="utf-8") + flags = summary["flags"] + section = ( + f"{marker}\n" + "\n" + f"`{AUTHORIZATION}` runs the remaining preregistered calibration gates in order on the " + "frozen support join. It does not recompute residuals, labels, readiness, GlossBERT, " + "MiniLM, or the resolver. It does not redraw. The frozen confound stop remains " + "tier-3 concentration, extreme-driven separation, or a POS partition. Nuisance " + "associations do not add a stop. " + f"Extreme-driven is {flags['extreme_driven']}. POS-partitioned is {flags['pos_partitioned']}. " + f"Tier-3 concentrated is {flags['tier3_concentrated']}. " + f"Confound result is `{summary['confound_result']}`.\n" + "\n" + "Token count, character length, and content-constituent count are copied from the frozen " + "manifest, surface, and score constituents. Candidate-sense count and GlossBERT confidence " + "and margin were not stored on the calibration artifacts, so those associations are " + f"`NOT_COMPUTABLE` and GlossBERT was not rerun. Nuisance Spearman results: {_spearman_line(summary['report'])}.\n" + "\n" + f"{summary['spec_outcome']} " + f"Analysis sha256 `{summary['confound_analysis_sha256']}`. " + f"Decision sha256 `{summary['confound_decision_sha256']}`. " + f"Completion search sha256 `{summary['search_sha256']}`. " + f"Threshold artifact sha256 `{summary['threshold_artifact_sha256']}`. " + f"Receipt sha256 `{summary['receipt_sha256']}`. " + f"Tracker sha256 `{summary['tracker_sha256']}`.\n" + "\n" + f"Training readiness is `{summary['training_readiness']}`. Training stays unauthorized. " + "A frozen threshold does not authorize SELECT-005, BEST replacement, or model promotion. " + f"Measurement stays `SEALED`. Measurement sha256 remains `{EXPECTED_MEASUREMENT}`. " + f"The next named transition is `{summary['next_legal_transition']}`. This pass does not authorize it. " + f"`selected_source` remains `none`. Admitted, settled, and gold stay 0. " + f"Events sha256 remains `{EXPECTED_EVENTS}`. The ledger was not appended. " + "No JSON Schema document exists for this completion family. " + "Execution 001, the direction artifacts, and v1 stay sealed. " + f"`{EXECUTION_ID}` remains `CALIBRATION_INSUFFICIENT_SUPPORT`.\n" + ) + if marker in text: + start = text.index(marker) + SPEC.write_text(text[:start] + section, encoding="utf-8") + return + if not text.endswith("\n"): + text += "\n" + SPEC.write_text(text + section, encoding="utf-8") + + +def _outcome_sentence(selection: dict, search: dict | None, frozen: dict | None) -> str: + state = selection["calibration_state"] + if state == CONFOUND_REVIEW: + return ( + "The confound stop fired. Threshold search did not run. The threshold value stays null. " + f"Training readiness is `{TRAINING_BLOCKED}` because of `{CONFOUND_REVIEW}`." + ) + if state == NO_THRESHOLD: + counts = search["gate_pass_counts"] + return ( + f"Threshold search evaluated {search['candidate_count']} unique ready residuals. " + f"Predicted-YES support passed for {counts['predicted_yes_support']}. " + f"The precision floor passed for {counts['precision_floor']}. " + f"The Wilson lower bound passed for {counts['wilson_lower']}. " + f"Leave-one-out passed for {counts['leave_one_out']}. " + f"The REJECT veto passed for {counts['reject_veto']}. " + f"The QUARANTINE veto passed for {counts['quarantine_veto']}. " + f"Candidates passing every frozen gate: {counts['all_gates']}. " + "No candidate passed. The threshold value stays null. " + f"Training readiness is `{TRAINING_BLOCKED}` because of `{NO_THRESHOLD}`." + ) + metrics = frozen + return ( + f"Threshold search evaluated {search['candidate_count']} unique ready residuals and froze " + f"`T_HIGH` at `{metrics['threshold_value']}`. Predicted YES support is {metrics['predicted_yes_support']}. " + f"True HIGH is {metrics['true_high']}. False HIGH is {metrics['false_high']}. " + f"Precision is {metrics['precision']}. The Wilson interval is {metrics['wilson_lower']} to {metrics['wilson_upper']}. " + f"HIGH recall is {metrics['high_recall']}. Ready REJECT YES is {metrics['reject_predicted_yes']}. " + f"Ready QUARANTINE YES is {metrics['quarantine_predicted_yes']}. Leave-one-out is PASS. " + "Measurement execution is a separate authorization. " + f"`{MEASUREMENT_NEXT}` is required before measurement is resolved, scored, or labeled. " + f"Training readiness is `{TRAINING_PENDING}`." + ) + + +def complete() -> dict: + assert_sealed() + assert_measurement_sealed() + if sha256_file(TRACKER) != EXPECTED_TRACKER: + refuse("tracker drifted before completion") + if MEASUREMENT_NEXT != "THRESHOLD_V2_MEASUREMENT_EXECUTION_AUTHORIZATION": + refuse("measurement handoff name drifted") + join_rows = load_jsonl(JOIN_PATH) + scores = load_jsonl(SCORES_PATH) + labels = load_jsonl(LABELS_PATH) + manifest = load_jsonl(MANIFEST_PATH) + parents = json.loads(RESOLUTION_RECEIPT.read_text(encoding="utf-8"))["parent_rows"] + built = assemble(join_rows, scores, labels, manifest, parents) + report = confound_report(built["confound_rows"]) + flags = _flags_from_report(report) + selection = select_threshold(built["selectable"]) + if selection.get("direction") != "SUPPORTED_DIRECTION": + refuse("selector direction disagrees with the frozen direction decision") + if selection.get("high_support") != 13 or selection.get("secondary_support") != 22: + refuse("selector support disagrees with the frozen counts") + selector_flags = selection.get("confound") + stopped = confound_stop(flags) + if stopped: + if selection["calibration_state"] != CONFOUND_REVIEW or selector_flags != flags: + refuse("confound report disagrees with select_threshold") + elif selection["calibration_state"] == CONFOUND_REVIEW or selector_flags is not None or any(flags.values()): + refuse("confound report disagrees with select_threshold") + analysis = { + "analysis_scope": "CONFOUND_THEN_THRESHOLD_IF_PASS", + "confound_gate_executed": True, + "confound_result": "STOP" if stopped else "PASS", + "direction_analysis_sha256": EXPECTED_DIRECTION_ANALYSIS, + "direction_result": "SUPPORTED_DIRECTION", + "frozen_stop_conditions": flags, + "json_schema_document": None, + "json_schema_exists": False, + "nuisance_changes_stop_decision": False, + "nuisance_field_provenance": _nuisance_provenance(), + "report": report, + "residual_recomputed": False, + "rows_excluded": 0, + "schema": "hyperlex.residual_threshold_v2_confound_analysis.v1", + "source_label_hash": EXPECTED_LABELS, + "source_score_hash": EXPECTED_SCORES, + "source_support_join_hash": EXPECTED_JOIN, + "threshold_search_run": False if stopped else True, + } + decision = { + "confound_result": analysis["confound_result"], + "evaluation_id": "THRESHOLD_V2_CONFOUND_EVALUATION_001", + "frozen_stop_conditions": list(STOP_FLAGS), + "json_schema_document": None, + "json_schema_exists": False, + "next_stage_authorized_by_this_pass": not stopped, + "state": CONFOUND_REVIEW if stopped else "PASS", + "threshold_search_run": not stopped, + } + analysis_sha = write_new(CONFOUND_ANALYSIS_PATH, render_json(analysis)) + decision["analysis_sha256"] = analysis_sha + decision_sha = write_new(CONFOUND_DECISION_PATH, render_json(decision)) + search = None + search_sha = None + frozen = None + failure_sha = None + threshold_value = None + threshold_frozen = False + if stopped: + state = CONFOUND_REVIEW + training = TRAINING_BLOCKED + measurement_eligible = False + next_transition = "NONE" + threshold_search_state = "NOT_RUN" + spec_outcome = _outcome_sentence(selection, None, None) + else: + search = _search_body(built["selectable"], selection) + search_sha = write_new(COMPLETION_SEARCH_PATH, render_json(search)) + state = selection["calibration_state"] + if state == NO_THRESHOLD: + failure = { + "failed_gate": "precision_gate", + "failure_state": NO_THRESHOLD, + "gate_pass_counts": search["gate_pass_counts"], + "json_schema_document": None, + "json_schema_exists": False, + "measurement_touched": False, + "schema": "hyperlex.residual_threshold_v2_calibration_completion_failure.v1", + "search_sha256": search_sha, + "threshold_frozen": False, + "threshold_value": None, + } + failure_sha = write_new(COMPLETION_FAILURE_PATH, render_json(failure)) + training = TRAINING_BLOCKED + measurement_eligible = False + next_transition = "NONE" + threshold_search_state = NO_THRESHOLD + spec_outcome = _outcome_sentence(selection, search, None) + elif state == THRESHOLD_FROZEN: + metrics = selection["selection_metrics"] + frozen = { + "algorithm_id": ALGORITHM_ID, + "confound_result": "PASS", + "confound_sha256": analysis_sha, + "direction_result": "SUPPORTED_DIRECTION", + "false_high": metrics["false_high"], + "high_recall": metrics["high_recall"], + "json_schema_document": None, + "json_schema_exists": False, + "leave_one_out_result": "PASS", + "measurement_execution_authorized": False, + "measurement_eligible": True, + "measurement_state": "SEALED", + "precision": metrics["precision"], + "predicted_yes_support": metrics["predicted_yes_support"], + "quarantine_predicted_yes": metrics["quarantine_predicted_yes"], + "reject_predicted_yes": metrics["reject_predicted_yes"], + "schema": "hyperlex.residual_threshold_v2_threshold_frozen.v1", + "search_sha256": search_sha, + "select_005_authorized": False, + "selected_source": "none", + "selection_algorithm_id": ALGORITHM_ID, + "threshold_frozen": True, + "threshold_id": "T_HIGH", + "threshold_value": selection["threshold_value"], + "true_high": metrics["true_high"], + "wilson_lower": metrics["precision_interval"][0], + "wilson_upper": metrics["precision_interval"][1], + } + frozen_sha = write_new(FROZEN_PATH, render_json(frozen)) + frozen["artifact_sha256"] = frozen_sha + threshold_value = selection["threshold_value"] + threshold_frozen = True + training = TRAINING_PENDING + measurement_eligible = True + next_transition = MEASUREMENT_NEXT + threshold_search_state = THRESHOLD_FROZEN + spec_outcome = _outcome_sentence(selection, search, frozen) + else: + refuse(f"unexpected selector state {state}") + receipt = _receipt( + analysis["confound_result"], + threshold_search_state, + threshold_value, + threshold_frozen, + training, + measurement_eligible, + ) + receipt_sha = write_new(RECEIPT_PATH, render_json(receipt)) + digests = { + "residual_threshold_v2_confound_analysis_sha256": analysis_sha, + "residual_threshold_v2_confound_decision_sha256": decision_sha, + "residual_threshold_v2_calibration_completion_receipt_sha256": receipt_sha, + "residual_threshold_v2_confound_evaluation_state": analysis["confound_result"], + } + if search_sha is not None: + digests["residual_threshold_v2_completion_threshold_search_sha256"] = search_sha + if failure_sha is not None: + digests["residual_threshold_v2_calibration_completion_failure_sha256"] = failure_sha + if frozen is not None: + digests["residual_threshold_v2_threshold_frozen_sha256"] = frozen["artifact_sha256"] + tracker_sha = update_tracker( + state, + threshold_value, + threshold_frozen, + training, + measurement_eligible, + next_transition, + digests, + ) + summary = { + "confound_analysis_sha256": analysis_sha, + "confound_decision_sha256": decision_sha, + "confound_result": analysis["confound_result"], + "flags": flags, + "next_legal_transition": next_transition, + "receipt_sha256": receipt_sha, + "report": report, + "search_sha256": search_sha or "not-run", + "spec_outcome": spec_outcome, + "threshold_artifact_sha256": failure_sha or (None if frozen is None else frozen["artifact_sha256"]) or "not-run", + "tracker_sha256": tracker_sha, + "training_readiness": training, + } + append_spec(summary) + assert_sealed() + assert_measurement_sealed() + if sha256_file(EXECUTION_SEARCH) != EXPECTED_EXECUTION_SEARCH: + refuse("execution 001 search artifact changed") + return { + "calibration_state": state, + "confound_analysis_sha256": analysis_sha, + "confound_decision_sha256": decision_sha, + "confound_result": analysis["confound_result"], + "failure_sha256": failure_sha, + "flags": flags, + "frozen_sha256": None if frozen is None else frozen["artifact_sha256"], + "gate_pass_counts": None if search is None else search["gate_pass_counts"], + "measurement_eligible": measurement_eligible, + "next_legal_transition": next_transition, + "receipt_sha256": receipt_sha, + "search_sha256": search_sha, + "threshold_frozen": threshold_frozen, + "threshold_search_ran": search is not None, + "threshold_value": threshold_value, + "tracker_sha256": tracker_sha, + "training_readiness": training, + } + + +def main() -> None: + if len(sys.argv) == 2 and sys.argv[1] == "--complete": + print(json.dumps(complete(), sort_keys=True)) + return + refuse("usage: residual_threshold_v2_completion.py --complete") + + +if __name__ == "__main__": + main() From 83e37970b9d68c8eed6f93391db3fe41dccdc258 Mon Sep 17 00:00:00 2001 From: Daniel Meyer Date: Mon, 28 Sep 2026 16:31:48 -0700 Subject: [PATCH 23/42] feat(007): land SELECT residual and schedule work (22) --- .../residual_threshold_v2_surface_draw.py | 982 ++++++++++++++++++ 1 file changed, 982 insertions(+) create mode 100644 scripts/shadow/hyperlexical/residual_threshold_v2_surface_draw.py diff --git a/scripts/shadow/hyperlexical/residual_threshold_v2_surface_draw.py b/scripts/shadow/hyperlexical/residual_threshold_v2_surface_draw.py new file mode 100644 index 00000000..40192fe3 --- /dev/null +++ b/scripts/shadow/hyperlexical/residual_threshold_v2_surface_draw.py @@ -0,0 +1,982 @@ +"""One-pass dual-surface draw for residual threshold v2. + +The frozen walk emits round-robin across POS x token-count cells. +The first calibration quota of emissions is CALIBRATION_V2. +The same walk continues for the measurement quota. +There is no RNG. Operator labels are not loaded. Nothing is resolved. +""" + +from __future__ import annotations + +import hashlib +import json +import sys +from collections import Counter +from pathlib import Path + +from hyperlexical.residual_threshold_v2 import ( + SAMPLING_RULE_ID, + calibration_draw_size, + measurement_draw_size, +) + +LEDGER = Path("/home/morpheus/hlx-private/eval-reserve-20260926") +SOURCE = LEDGER / "operator-review/HLX-EVAL-UNBIND-SEMANTIC-EVIDENCE-SOURCE-V1-001" +SENSE = LEDGER / "operator-review/HLX-EVAL-UNBIND-SENSE-SCREEN-V1-HYPOTHESIS-001" +WORDNET = LEDGER / "acquisition/sources/wordnet-3.0/wordnet" +TRAIN = Path("/home/morpheus/hlx-private/d1-spark-tree-20260924T213846Z/morph78-train-export.jsonl") +EVENTS = LEDGER / "events.jsonl" +LEDGER_FILE = LEDGER / "ledger.json" +TRACKER = SENSE / "HYPOTHESIS.json" +EVIDENCE = SENSE / "DEVELOPMENT_EVIDENCE.json" +PROBE = LEDGER / "operator-review/HLX-EVAL-UNBIND-SCREEN-V5-HYPOTHESIS-001/PROBE_SURFACES.json" +V1_MANIFEST = SOURCE / "RESIDUAL_THRESHOLD_V1_CALIBRATION_MANIFEST.jsonl" + +CALIBRATION_MANIFEST = SOURCE / "RESIDUAL_THRESHOLD_V2_CALIBRATION_MANIFEST.jsonl" +MEASUREMENT_MANIFEST = SOURCE / "RESIDUAL_THRESHOLD_V2_MEASUREMENT_MANIFEST.jsonl" +DRAW_RECEIPT = SOURCE / "RESIDUAL_THRESHOLD_V2_SURFACE_DRAW_RECEIPT.json" +ISOLATION_PATH = SOURCE / "RESIDUAL_THRESHOLD_V2_SURFACE_ISOLATION_REPORT.json" +DISTRIBUTION_PATH = SOURCE / "RESIDUAL_THRESHOLD_V2_SURFACE_DISTRIBUTION_REPORT.json" + +AUTHORIZATION = "THRESHOLD_V2_SURFACE_DRAW_AUTHORIZATION" +NEXT_TRANSITION = "THRESHOLD_V2_CALIBRATION_EXECUTION_AUTHORIZATION" +SOURCE_IDENTITY = "wordnet-3.0" +STATE_FROZEN = "SURFACES_FROZEN" +STATE_UNFILLED = "SURFACE_QUOTA_UNFILLED" +STATE_NOT_DETERMINISTIC = "NOT_DETERMINISTIC" +MEASUREMENT_STATE = "SEALED" + +MANIFEST_FIELDS = ( + "allocation_rule_id", + "frozen_gloss", + "global_draw_order", + "normalized_text_sha256", + "pos", + "pwn30_synset", + "row_id", + "source_identity", + "stratum_id", + "surface", + "surface_role", + "token_count", + "within_stratum_order", +) + +EXPECTED_TRACKER = "041f095c5ca0271e075c8db1a67ea401764486930c86fa5ff2bc96df7d8f1f71" +EXPECTED_EVENTS = "96b74a92d44f1cf9fe152b18e5207176f161ba3bfce528dac38aa4571a742f9c" +EXPECTED_LEDGER = "77e22433203879b252f7a9e309d2013d7550101d1c4a014b494d2c96df87d0e0" +EXPECTED_V1_FAILURE = "1d3f1f26609c1f1a47790d94730fba50e9f4d888dd8d7d89e6fa698a924fb3a8" +EXPECTED_V1_MANIFEST = "ba561935cff5f9f0a36af0b8b24e4b69bce5fb2c61b5ea2236f0a664c56bcf5d" + +SEALED = { + SOURCE / "RESIDUAL_THRESHOLD_V2_PREREGISTRATION.json": "1c962703e12c5c48fd279dc766fd4d1d3108c40486a10789fdda112926e98261", + SOURCE / "RESIDUAL_THRESHOLD_V2_SAMPLE_SIZE.json": "46e3d32b4a4c8af73c9197557b09ceed872b1918d41143ed551bb3b4572c6151", + SOURCE / "RESIDUAL_THRESHOLD_V2_CALIBRATION_CONTRACT.json": "51404e675870c3e99ff6bb51f3aa51e95aa585a360fe782e3c89d11927345934", + SOURCE / "RESIDUAL_THRESHOLD_V2_MEASUREMENT_CONTRACT.json": "f96b7ae1f20fbc9850cf3dcc6f6f74ad046bf35755e18ccc7e6747441184adee", + SOURCE / "RESIDUAL_THRESHOLD_V2_SELECTION_PROCEDURE.json": "1c8a82ef939a33bbbb327f0f760439113432645438361176de36159a8f56c55c", + SOURCE / "RESIDUAL_THRESHOLD_V2_SURFACE_ISOLATION_POLICY.json": "a9f5ae333934ff38a75a0a3e7adad5a5abf78b422f370560d0d9735765372189", + SOURCE / "RESIDUAL_THRESHOLD_V2_ACCEPTANCE.json": "5cea52584a420e91e7b5f8e3512fbb657610bfb41d95008e52346b59cbfe3d50", + SOURCE / "RESIDUAL_THRESHOLD_V2_ARTIFACT_SCHEMA.json": "dc6017a49ff4ea14f664a4f11a1f2501658cda177d25bd984d491c04f394e327", + V1_MANIFEST: EXPECTED_V1_MANIFEST, + SOURCE / "RESIDUAL_THRESHOLD_V1_CALIBRATION_SCORES.jsonl": "f775849b3bb8cdec2ac8eeeea0100e51853fefcebbd88d6b3b4efb33fa8a3895", + SOURCE / "RESIDUAL_THRESHOLD_V1_CALIBRATION_FAILURE.json": EXPECTED_V1_FAILURE, + SOURCE / "RESIDUAL_THRESHOLD_V1_CALIBRATION_DESIGN_CLOSURE.json": "a5dbdb8ae497c25e711a81a880ad26ec7ef1ace917278c807497af5ec37e5002", + SOURCE / "RESIDUAL_THRESHOLD_V1_CALIBRATION_LABEL_AVAILABILITY_REVIEW.json": "3a6c967c3542096714e25b59544bd68d89d7a9bc28ee253dec1b9f224b5a0484", + SOURCE / "RESIDUAL_THRESHOLD_V1_CALIBRATION_DRAW_RECEIPT.json": "7448adb8c9448c3737a7b7c2c9a3116ced23d2bab7e432d011bc41229c3ef283", + EVIDENCE: "0e9b3c1af9dd573bf6e2034640e468e8ab9074e1e76c90cef1f39f68d607bc03", + EVENTS: EXPECTED_EVENTS, + LEDGER_FILE: EXPECTED_LEDGER, + SOURCE / "HYPOTHESIS.json": "39127a810d38ede96d7947c33dbc3e5491c9e1cc9b3f76b1064d9e0dd04a7787", +} + +OUTPUTS = ( + CALIBRATION_MANIFEST, + MEASUREMENT_MANIFEST, + DRAW_RECEIPT, + ISOLATION_PATH, + DISTRIBUTION_PATH, +) + +DEVELOPMENT_ROW_KEEP = { + "normalized_identity", + "partition", + "row_id", + "surface", + "synset_offset", + "synset_pos", +} +MAGPIE_KEEP = {"hyperlex_synset", "row_id", "synset"} +V1_KEEP = {"calibration_row_id", "normalized_text_sha256", "pwn30_synset", "surface"} +PROBE_KEEP = {"v3_held_out_errors", "v4_measurement_fallthroughs"} + + +def sha256_bytes(payload: bytes) -> str: + return hashlib.sha256(payload).hexdigest() + + +def sha256_file(path: Path) -> str: + return sha256_bytes(path.read_bytes()) + + +def refuse(message: str) -> None: + raise SystemExit(message) + + +def _loads_kept(text: str, keep: set[str]): + def hook(pairs: list[tuple[str, object]]) -> dict: + return {key: value for key, value in pairs if key in keep} + + return json.loads(text, object_pairs_hook=hook) + + +def _load_development(text: str) -> dict: + """Keep identity fields. Partition names stay on the partitions object.""" + + def hook(pairs: list[tuple[str, object]]) -> dict: + data = dict(pairs) + if "row_id" in data and "surface" in data: + return {key: data[key] for key in DEVELOPMENT_ROW_KEEP if key in data} + if "rows" in data: + return {"partitions": data.get("partitions"), "rows": data["rows"]} + return data + + return json.loads(text, object_pairs_hook=hook) + + +def allocate_surfaces( + cells: dict[tuple[str, int], list[dict]], + calibration_quota: int, + measurement_quota: int, +) -> dict: + """Fill calibration from the start of one walk, then measurement. + + Cells are sorted by source POS, then token count. Each cell is ordered + by normalized_text_sha256. Exhausted cells are skipped. Quotas are not + reduced when the walk runs out of rows. + """ + if calibration_quota < 1 or measurement_quota < 1: + refuse("quotas must be positive") + ordered: dict[tuple[str, int], list[dict]] = {} + for key, group in cells.items(): + copied = sorted(group, key=lambda item: item["normalized_text_sha256"]) + ordered[key] = copied + keys = sorted(ordered) + cursors = {key: 0 for key in keys} + emitted: list[dict] = [] + target = calibration_quota + measurement_quota + while len(emitted) < target: + progressed = False + for key in keys: + index = cursors[key] + group = ordered[key] + if index >= len(group): + continue + item = group[index] + if item["pos"] != key[0]: + refuse("stratum POS does not match the row") + emitted.append( + { + "global_draw_order": len(emitted) + 1, + "item": item, + "stratum": key, + "within_stratum_order": index + 1, + } + ) + cursors[key] = index + 1 + progressed = True + if len(emitted) == target: + break + if not progressed: + break + filled = len(emitted) == target + shortage = None + if not filled: + shortage = { + "calibration_quota": calibration_quota, + "calibration_rows_emitted": min(len(emitted), calibration_quota), + "cells": [ + { + "available": len(ordered[key]), + "exhausted": cursors[key] >= len(ordered[key]), + "source_pos": key[0], + "taken": cursors[key], + "token_count": key[1], + } + for key in keys + ], + "measurement_quota": measurement_quota, + "measurement_rows_emitted": max(0, len(emitted) - calibration_quota), + } + return { + "calibration": [], + "filled": False, + "measurement": [], + "shortage": shortage, + } + calibration = [ + manifest_row(emission, "CALIBRATION_V2") + for emission in emitted[:calibration_quota] + ] + measurement = [ + manifest_row(emission, "MEASUREMENT_V2") + for emission in emitted[calibration_quota:] + ] + return { + "calibration": calibration, + "filled": True, + "measurement": measurement, + "shortage": None, + } + + +def manifest_row(emission: dict, surface_role: str) -> dict: + item = emission["item"] + pos, token_count = emission["stratum"] + digest = item["normalized_text_sha256"] + row = { + "allocation_rule_id": SAMPLING_RULE_ID, + "frozen_gloss": item["frozen_gloss"], + "global_draw_order": emission["global_draw_order"], + "normalized_text_sha256": digest, + "pos": pos, + "pwn30_synset": item["pwn30_synset"], + "row_id": digest, + "source_identity": SOURCE_IDENTITY, + "stratum_id": f"{pos}:{token_count}", + "surface": item["surface"], + "surface_role": surface_role, + "token_count": token_count, + "within_stratum_order": emission["within_stratum_order"], + } + if set(row) != set(MANIFEST_FIELDS): + refuse("manifest row left the structural contract") + return row + + +def _verify_sealed() -> None: + if sha256_file(TRACKER) != EXPECTED_TRACKER: + refuse("tracker hash drifted before the surface draw") + for path, expected in SEALED.items(): + if sha256_file(path) != expected: + refuse(f"sealed artifact changed: {path.name}") + + +def _identity_fences() -> dict: + from hyperlexical.heldout_census import normalize_group_text + from hyperlexical.holdout_guard import normalized_text_sha256 + from hyperlexical.unbind_screen_v4 import normalize_lexical + + evidence = _load_development(EVIDENCE.read_text(encoding="utf-8")) + rows = evidence["rows"] + if len(rows) != 225: + refuse("development evidence row count drifted") + hashes = set() + lexical = set() + grouped = set() + stored = set() + synsets = set() + partitions: Counter[str] = Counter() + for row in rows: + surface = str(row["surface"]) + digest = normalized_text_sha256(surface) + if digest != row["row_id"]: + refuse("development row_id is not normalized_text_sha256") + hashes.add(row["row_id"]) + lexical.add(normalize_lexical(surface)) + grouped.add(normalize_group_text(surface)) + stored.add(str(row["normalized_identity"])) + offset = str(row.get("synset_offset") or "") + if offset: + synsets.add(f"{row['synset_pos']}:{offset}") + partitions[str(row.get("partition") or "")] += 1 + magpie_hashes = set() + magpie_synsets = set() + for name in ("MAGPIE_SEMANTIC_EVIDENCE.jsonl", "KM_HYPERLEX_SEMANTIC_EVIDENCE.jsonl"): + for line in (SOURCE / name).read_text(encoding="utf-8").splitlines(): + if not line: + continue + row = _loads_kept(line, MAGPIE_KEEP) + magpie_hashes.add(row["row_id"]) + for key in ("synset", "hyperlex_synset"): + value = row.get(key) + if value: + magpie_synsets.add(str(value)) + if sha256_file(V1_MANIFEST) != EXPECTED_V1_MANIFEST: + refuse("v1 calibration manifest changed") + v1_hashes = set() + v1_synsets = set() + v1_lexical = set() + v1_grouped = set() + v1_count = 0 + for line in V1_MANIFEST.read_text(encoding="utf-8").splitlines(): + if not line: + continue + row = _loads_kept(line, V1_KEEP) + digest = row["normalized_text_sha256"] + if row["calibration_row_id"] != digest: + refuse("v1 calibration row_id drifted from its hash") + v1_hashes.add(digest) + v1_synsets.add(row["pwn30_synset"]) + v1_lexical.add(normalize_lexical(row["surface"])) + v1_grouped.add(normalize_group_text(row["surface"])) + v1_count += 1 + if v1_count != 27 or len(v1_hashes) != 27 or len(v1_synsets) != 27: + refuse("v1 calibration identity fence is not 27 distinct rows") + probe = _loads_kept(PROBE.read_text(encoding="utf-8"), PROBE_KEEP) + probe_hashes = set() + for key in ("v3_held_out_errors", "v4_measurement_fallthroughs"): + for surface in probe.get(key) or []: + probe_hashes.add(normalized_text_sha256(str(surface))) + reserves = [ + path + for path in (LEDGER / "operator-review").rglob("*") + if path.is_file() and "measurement" in path.name.lower() and "reserve" in path.name.lower() + ] + if reserves: + refuse(f"unexpected measurement reserve: {reserves}") + return { + "development_grouped": grouped, + "development_hashes": hashes, + "development_lexical": lexical, + "development_partitions": dict(sorted(partitions.items())), + "development_stored_identity": stored, + "development_synsets": synsets, + "magpie_km_hashes": magpie_hashes, + "magpie_km_synsets": magpie_synsets, + "probe_hashes_outside_development": probe_hashes - hashes, + "v1_grouped": v1_grouped, + "v1_hashes": v1_hashes, + "v1_lexical": v1_lexical, + "v1_synsets": v1_synsets, + } + + +def _bound_glosses(pending: list[tuple[str, str]]) -> dict[tuple[str, str], str]: + from hyperlexical.unbind_screen_v3 import FILES, load_glosses + + found: dict[tuple[str, str], str] = {} + wanted: dict[str, set[str]] = {} + for pos, offset in pending: + wanted.setdefault(pos, set()).add(offset) + for pos, offsets in wanted.items(): + loaded = load_glosses(WORDNET / FILES[pos][1], offsets) + for offset, gloss in loaded.items(): + found[(pos, offset)] = gloss + return found + + +def build_universe() -> tuple[dict[tuple[str, int], list[dict]], dict]: + """Enumerate the positional-unbind universe and apply frozen fences. + + This function does not read operator labels, residuals, or model output. + """ + from hyperlexical.clean_unbind import ( + WORDNET_LICENSE, + gate_rows, + load_jsonl, + read_wordnet_index, + reason_counts, + rows_from_wordnet_atoms, + ) + from hyperlexical.heldout_census import normalize_group_text + from hyperlexical.holdout_guard import normalized_text_sha256 + from hyperlexical.identity_ledger import IdentityLedger + from hyperlexical.unbind_screen_v3 import load_indexes + from hyperlexical.unbind_screen_v4 import normalize_lexical + + fences = _identity_fences() + atoms = read_wordnet_index(WORDNET) + generated = rows_from_wordnet_atoms(atoms, license=WORDNET_LICENSE) + admissible, rejections, _account = gate_rows( + generated, + train_rows=load_jsonl(TRAIN), + ledger=IdentityLedger.load(LEDGER), + require_settlement=False, + ) + exclusion: Counter[str] = Counter() + exclusion["wordnet_rows"] = len(generated) + exclusion["gate_rejected"] = len(rejections) + for reason, count in sorted(reason_counts(rejections).items()): + exclusion[f"gate_{reason}"] = count + positional = [] + for row in admissible: + if row.get("role_scheme") != "positional": + exclusion["not_positional"] += 1 + continue + positional.append(row) + source_universe = len(positional) + exclusion["admissible_positional"] = source_universe + kept = [] + for row in positional: + surface = str(row.get("text") or "") + digest = normalized_text_sha256(surface) + lexical = normalize_lexical(surface) + grouped = normalize_group_text(surface) + if digest in fences["development_hashes"]: + exclusion["development_row_id"] += 1 + continue + if ( + lexical in fences["development_lexical"] + or grouped in fences["development_grouped"] + or grouped in fences["development_stored_identity"] + or lexical in fences["development_stored_identity"] + ): + exclusion["development_normalized_text"] += 1 + continue + if digest in fences["magpie_km_hashes"]: + exclusion["magpie_km_overlap"] += 1 + continue + if digest in fences["v1_hashes"]: + exclusion["threshold_v1_calibration_row_id"] += 1 + continue + if lexical in fences["v1_lexical"] or grouped in fences["v1_grouped"]: + exclusion["threshold_v1_calibration_normalized_text"] += 1 + continue + if digest in fences["probe_hashes_outside_development"]: + exclusion["historical_review_surface"] += 1 + continue + kept.append(row) + best: dict[str, dict] = {} + for row in kept: + lexical = normalize_lexical(str(row.get("text") or "")) + digest = normalized_text_sha256(str(row.get("text") or "")) + current = best.get(lexical) + if current is None: + best[lexical] = row + continue + exclusion["duplicate_normalized_identity"] += 1 + current_digest = normalized_text_sha256(str(current.get("text") or "")) + if digest < current_digest: + best[lexical] = row + indexes = load_indexes(str(WORDNET)) + pending = [] + for row in best.values(): + surface = str(row["text"]) + pos = str(row["source_pos"]) + fillers = [str(tok) for tok in row.get("fillers") or []] + if " ".join(fillers) != surface: + refuse("positional tokens do not reconstruct the surface") + if not (2 <= len(fillers) <= 6): + refuse("positional universe admitted a token count outside 2..6") + offset = (indexes.get(pos) or {}).get(surface.replace(" ", "_")) + if not offset: + exclusion["missing_or_unbound_synset"] += 1 + continue + pending.append((surface, pos, offset, fillers)) + glosses = _bound_glosses([(pos, offset) for _surface, pos, offset, _fillers in pending]) + synset_best: dict[str, dict] = {} + for surface, pos, offset, fillers in pending: + gloss = glosses.get((pos, offset), "") + synset = f"{pos}:{offset}" + if not gloss: + exclusion["missing_or_unbound_synset"] += 1 + continue + if synset in fences["development_synsets"] or synset in fences["magpie_km_synsets"]: + exclusion["development_synset"] += 1 + continue + if synset in fences["v1_synsets"]: + exclusion["threshold_v1_calibration_synset"] += 1 + continue + item = { + "frozen_gloss": gloss, + "normalized_text_sha256": normalized_text_sha256(surface), + "pos": pos, + "pwn30_synset": synset, + "surface": surface, + "token_count": len(fillers), + } + current = synset_best.get(synset) + if current is None: + synset_best[synset] = item + continue + exclusion["duplicate_synset"] += 1 + if item["normalized_text_sha256"] < current["normalized_text_sha256"]: + synset_best[synset] = item + cells: dict[tuple[str, int], list[dict]] = {} + for item in synset_best.values(): + cells.setdefault((item["pos"], item["token_count"]), []).append(item) + for key in cells: + cells[key].sort(key=lambda item: item["normalized_text_sha256"]) + eligible = sum(len(group) for group in cells.values()) + account = { + "development_partitions_fenced": fences["development_partitions"], + "eligible_after_exclusions": eligible, + "exclusion_counts": {key: int(value) for key, value in sorted(exclusion.items())}, + "historical_probe_hashes_outside_development": len(fences["probe_hashes_outside_development"]), + "measurement_reserve_files": 0, + "source_wordnet_universe_count": source_universe, + "strata_available": [ + { + "available": len(cells[key]), + "source_pos": key[0], + "token_count": key[1], + } + for key in sorted(cells) + ], + "v1_calibration_rows_fenced": 27, + } + membership = { + f"{key[0]}:{key[1]}": [item["normalized_text_sha256"] for item in cells[key]] + for key in sorted(cells) + } + account["cell_membership"] = membership + return cells, account + + +def _fingerprint(account: dict, allocated: dict) -> dict: + return { + "calibration": [row["row_id"] for row in allocated["calibration"]], + "cell_membership": account["cell_membership"], + "eligible_after_exclusions": account["eligible_after_exclusions"], + "exclusion_counts": account["exclusion_counts"], + "measurement": [row["row_id"] for row in allocated["measurement"]], + "measurement_glosses": [row["frozen_gloss"] for row in allocated["measurement"]], + "calibration_glosses": [row["frozen_gloss"] for row in allocated["calibration"]], + "source_wordnet_universe_count": account["source_wordnet_universe_count"], + } + + +def _distribution(rows: list[dict]) -> dict: + pos_counts: Counter[str] = Counter(row["pos"] for row in rows) + token_counts: Counter[int] = Counter(row["token_count"] for row in rows) + cells: Counter[tuple[str, int]] = Counter((row["pos"], row["token_count"]) for row in rows) + return { + "pos_counts": dict(sorted(pos_counts.items())), + "pos_token_cells": [ + {"count": count, "pos": pos, "token_count": token_count} + for (pos, token_count), count in sorted(cells.items()) + ], + "row_count": len(rows), + "token_count_counts": {str(token_count): count for token_count, count in sorted(token_counts.items())}, + } + + +def _row_overlap(left: list[dict], right_hashes: set[str], right_lexical: set[str], right_grouped: set[str], right_synsets: set[str]) -> dict: + from hyperlexical.heldout_census import normalize_group_text + from hyperlexical.unbind_screen_v4 import normalize_lexical + + left_hashes = {row["row_id"] for row in left} + left_synsets = {row["pwn30_synset"] for row in left} + normalized = 0 + for row in left: + lexical = normalize_lexical(row["surface"]) + grouped = normalize_group_text(row["surface"]) + if lexical in right_lexical or grouped in right_grouped: + normalized += 1 + return { + "normalized_text": normalized, + "row_id": len(left_hashes & right_hashes), + "synset": len(left_synsets & right_synsets), + } + + +def _surface_sets(rows: list[dict]) -> dict: + from hyperlexical.heldout_census import normalize_group_text + from hyperlexical.unbind_screen_v4 import normalize_lexical + + return { + "grouped": {normalize_group_text(row["surface"]) for row in rows}, + "hashes": {row["row_id"] for row in rows}, + "lexical": {normalize_lexical(row["surface"]) for row in rows}, + "synsets": {row["pwn30_synset"] for row in rows}, + } + + +def _isolation(calibration: list[dict], measurement: list[dict], fences: dict) -> dict: + development = _row_overlap( + calibration, + fences["development_hashes"], + fences["development_lexical"], + fences["development_grouped"] | fences["development_stored_identity"], + fences["development_synsets"], + ) + development_measurement = _row_overlap( + measurement, + fences["development_hashes"], + fences["development_lexical"], + fences["development_grouped"] | fences["development_stored_identity"], + fences["development_synsets"], + ) + v1_calibration = _row_overlap( + calibration, + fences["v1_hashes"], + fences["v1_lexical"], + fences["v1_grouped"], + fences["v1_synsets"], + ) + v1_measurement = _row_overlap( + measurement, + fences["v1_hashes"], + fences["v1_lexical"], + fences["v1_grouped"], + fences["v1_synsets"], + ) + measurement_sets = _surface_sets(measurement) + calibration_vs_measurement = _row_overlap( + calibration, + measurement_sets["hashes"], + measurement_sets["lexical"], + measurement_sets["grouped"], + measurement_sets["synsets"], + ) + pairs = { + "calibration_v2_vs_development": development, + "calibration_v2_vs_measurement_v2": calibration_vs_measurement, + "calibration_v2_vs_threshold_v1_calibration": v1_calibration, + "measurement_v2_vs_development": development_measurement, + "measurement_v2_vs_threshold_v1_calibration": v1_measurement, + } + synset_sets = { + "development": fences["development_synsets"], + "threshold_v1_calibration": fences["v1_synsets"], + "threshold_v2_calibration": {row["pwn30_synset"] for row in calibration}, + "threshold_v2_measurement": {row["pwn30_synset"] for row in measurement}, + } + names = list(synset_sets) + synset_pairs = [] + for index, left_name in enumerate(names): + for right_name in names[index + 1 :]: + synset_pairs.append( + { + "count": len(synset_sets[left_name] & synset_sets[right_name]), + "left": left_name, + "right": right_name, + } + ) + internal = { + "calibration_duplicate_row_id": len(calibration) - len({row["row_id"] for row in calibration}), + "calibration_duplicate_synset": len(calibration) - len({row["pwn30_synset"] for row in calibration}), + "measurement_duplicate_row_id": len(measurement) - len({row["row_id"] for row in measurement}), + "measurement_duplicate_synset": len(measurement) - len({row["pwn30_synset"] for row in measurement}), + } + total = sum(item[key] for item in pairs.values() for key in ("normalized_text", "row_id", "synset")) + total += sum(internal.values()) + total += sum(item["count"] for item in synset_pairs) + return { + "internal_duplicates": internal, + "pairwise": pairs, + "synset_pairs": synset_pairs, + "total": total, + } + + +def _verify_glosses(calibration: list[dict], measurement: list[dict]) -> None: + from hyperlexical.unbind_screen_v3 import FILES, gloss_for, load_glosses, load_indexes + + rows = calibration + measurement + indexes = load_indexes(str(WORDNET)) + wanted: dict[str, set[str]] = {} + for row in rows: + pos, offset = row["pwn30_synset"].split(":", 1) + lemma = row["surface"].replace(" ", "_") + bound = (indexes.get(row["pos"]) or {}).get(lemma) + if row["pos"] != pos or bound != offset: + refuse(f"source-POS synset drifted: {row['surface']}") + wanted.setdefault(pos, set()).add(offset) + glosses: dict[tuple[str, str], str] = {} + for pos, offsets in wanted.items(): + loaded = load_glosses(WORDNET / FILES[pos][1], offsets) + for offset, gloss in loaded.items(): + glosses[(pos, offset)] = gloss + seen_strata = set() + for row in rows: + pos, offset = row["pwn30_synset"].split(":", 1) + if glosses.get((pos, offset)) != row["frozen_gloss"]: + refuse(f"frozen gloss drifted on rescan: {row['surface']}") + stratum = (row["pos"], row["token_count"]) + if stratum in seen_strata: + continue + seen_strata.add(stratum) + bound_pos, gloss = gloss_for(row["surface"], row["pos"], str(WORDNET)) + if bound_pos != row["pos"] or gloss != row["frozen_gloss"]: + refuse(f"frozen gloss drifted from gloss_for: {row['surface']}") + + +def _render_json(payload: dict) -> str: + return json.dumps(payload, indent=2, sort_keys=True, ensure_ascii=True) + "\n" + + +def _render_jsonl(rows: list[dict]) -> str: + return "".join(json.dumps(row, sort_keys=True, ensure_ascii=True) + "\n" for row in rows) + + +def _write_new(path: Path, text: str) -> None: + if path.exists(): + refuse(f"refusing to rewrite {path.name}") + path.write_text(text, encoding="utf-8") + path.chmod(0o600) + + +def _public_flags() -> dict: + return { + "admitted": 0, + "extended_lesk_run": False, + "glossbert_run": False, + "gold": 0, + "json_schema_document": None, + "json_schema_exists": False, + "measurement_eligible": False, + "minilm_encode": False, + "operator_labels_joined": False, + "operator_labels_loaded": False, + "residual_scores_created": False, + "runtime_integration": False, + "select_005_authorized": False, + "selected_source": "none", + "settled": 0, + "threshold_frozen": False, + "threshold_value": None, + } + + +def _failure_receipt(state: str, detail: dict) -> None: + if DRAW_RECEIPT.exists(): + refuse(state) + payload = { + **_public_flags(), + "allocation_rule_id": SAMPLING_RULE_ID, + "authorization": AUTHORIZATION, + "calibration_surface_frozen": False, + "detail": detail, + "determinism": "IDENTICAL" if state != STATE_NOT_DETERMINISTIC else "NOT_IDENTICAL", + "draw_seed": None, + "measurement_state": "NOT_DRAWN", + "measurement_surface_frozen": False, + "rng_used": False, + "schema": "hyperlex.residual_threshold_v2_surface_draw_receipt.v1", + "state": state, + } + _write_new(DRAW_RECEIPT, _render_json(payload)) + _update_tracker_failure(state) + refuse(state) + + +def _update_tracker_failure(state: str) -> None: + tracker = json.loads(TRACKER.read_text(encoding="utf-8")) + if tracker.get("residual_threshold_state") != "CALIBRATION_INSUFFICIENT_SUPPORT": + refuse("v1 execution state drifted") + if tracker.get("residual_threshold_v1_disposition") != "CLOSED_CALIBRATION_DESIGN_INSUFFICIENT": + refuse("v1 disposition drifted") + tracker["previous_tracker_sha256"] = EXPECTED_TRACKER + tracker["residual_threshold_v2_state"] = state + tracker["residual_threshold_v2_value"] = None + tracker["residual_threshold_v2_frozen"] = False + tracker["residual_threshold_v2_calibration_surface_drawn"] = False + tracker["residual_threshold_v2_measurement_surface_drawn"] = False + tracker["next_legal_transition"] = None + tracker["next_transition_authorized"] = False + tracker["selected_source"] = "none" + tracker["measurement_eligible"] = False + text = json.dumps(tracker, indent=2, sort_keys=True, ensure_ascii=True) + "\n" + TRACKER.write_text(text, encoding="utf-8") + TRACKER.chmod(0o600) + + +def _update_tracker_success(hashes: dict) -> str: + tracker = json.loads(TRACKER.read_text(encoding="utf-8")) + if tracker.get("residual_threshold_state") != "CALIBRATION_INSUFFICIENT_SUPPORT": + refuse("v1 execution state drifted") + if tracker.get("residual_threshold_v1_disposition") != "CLOSED_CALIBRATION_DESIGN_INSUFFICIENT": + refuse("v1 disposition drifted") + if tracker.get("residual_threshold_value") is not None or tracker.get("residual_threshold_frozen") is not False: + refuse("v1 threshold drifted") + if tracker.get("residual_threshold_redraw_authorized") is not False: + refuse("v1 redraw flag drifted") + tracker["previous_tracker_sha256"] = EXPECTED_TRACKER + tracker["residual_threshold_v2_state"] = STATE_FROZEN + tracker["residual_threshold_v2_value"] = None + tracker["residual_threshold_v2_frozen"] = False + tracker["residual_threshold_v2_calibration_surface_drawn"] = True + tracker["residual_threshold_v2_calibration_surface_frozen"] = True + tracker["residual_threshold_v2_calibration_manifest_rows"] = 200 + tracker["residual_threshold_v2_calibration_resolved"] = False + tracker["residual_threshold_v2_calibration_scored"] = False + tracker["residual_threshold_v2_calibration_labeled"] = False + tracker["residual_threshold_v2_calibration_manifest_sha256"] = hashes["calibration_manifest_sha256"] + tracker["residual_threshold_v2_measurement_surface_drawn"] = True + tracker["residual_threshold_v2_measurement_surface_frozen"] = True + tracker["residual_threshold_v2_measurement_manifest_rows"] = 200 + tracker["residual_threshold_v2_measurement_state"] = MEASUREMENT_STATE + tracker["residual_threshold_v2_measurement_resolved"] = False + tracker["residual_threshold_v2_measurement_scored"] = False + tracker["residual_threshold_v2_measurement_labeled"] = False + tracker["residual_threshold_v2_measurement_manifest_sha256"] = hashes["measurement_manifest_sha256"] + tracker["residual_threshold_v2_surface_draw_receipt_sha256"] = hashes["draw_receipt_sha256"] + tracker["residual_threshold_v2_surface_isolation_report_sha256"] = hashes["isolation_sha256"] + tracker["residual_threshold_v2_surface_distribution_report_sha256"] = hashes["distribution_sha256"] + tracker["residual_threshold_v2_redraw_authorized"] = False + tracker["measurement_eligible"] = False + tracker["next_legal_transition"] = NEXT_TRANSITION + tracker["next_transition_authorized"] = False + tracker["selected_source"] = "none" + tracker["select_authorized"] = False + tracker["admitted"] = 0 + tracker["settled"] = 0 + tracker["gold"] = 0 + text = json.dumps(tracker, indent=2, sort_keys=True, ensure_ascii=True) + "\n" + TRACKER.write_text(text, encoding="utf-8") + TRACKER.chmod(0o600) + if tracker["residual_threshold_state"] != "CALIBRATION_INSUFFICIENT_SUPPORT": + refuse("v1 execution state was overwritten") + return sha256_file(TRACKER) + + +def freeze_surfaces() -> dict: + for path in OUTPUTS: + if path.exists(): + refuse(f"surface artifact already exists: {path.name}") + _verify_sealed() + if calibration_draw_size() != 200 or measurement_draw_size() != 200: + refuse("frozen quotas are not 200") + print("draw pass 1", file=sys.stderr, flush=True) + first_cells, first_account = build_universe() + first_allocated = allocate_surfaces(first_cells, 200, 200) + print("draw pass 2", file=sys.stderr, flush=True) + second_cells, second_account = build_universe() + second_allocated = allocate_surfaces(second_cells, 200, 200) + if _fingerprint(first_account, first_allocated) != _fingerprint(second_account, second_allocated): + _failure_receipt(STATE_NOT_DETERMINISTIC, {"draw_passes": 2}) + if not first_allocated["filled"]: + _failure_receipt(STATE_UNFILLED, first_allocated["shortage"]) + calibration = first_allocated["calibration"] + measurement = first_allocated["measurement"] + if len(calibration) != 200 or len(measurement) != 200: + refuse("filled allocation did not emit the quotas") + print("gloss check", file=sys.stderr, flush=True) + _verify_glosses(calibration, measurement) + fences = _identity_fences() + isolation_body = _isolation(calibration, measurement, fences) + if isolation_body["total"] != 0: + _failure_receipt("ISOLATION_FAILURE", isolation_body) + for row in calibration + measurement: + if set(row) != set(MANIFEST_FIELDS): + refuse("manifest row carries a field outside the contract") + if row["row_id"] != row["normalized_text_sha256"]: + refuse("row_id is not normalized_text_sha256") + calibration_text = _render_jsonl(calibration) + measurement_text = _render_jsonl(measurement) + calibration_sha = sha256_bytes(calibration_text.encode("utf-8")) + measurement_sha = sha256_bytes(measurement_text.encode("utf-8")) + distribution = { + **_public_flags(), + "authorization": AUTHORIZATION, + "calibration": _distribution(calibration), + "labels_joined": False, + "measurement": _distribution(measurement), + "schema": "hyperlex.residual_threshold_v2_surface_distribution_report.v1", + } + isolation = { + **_public_flags(), + "authorization": AUTHORIZATION, + "calibration_manifest_sha256": calibration_sha, + "internal_duplicates": isolation_body["internal_duplicates"], + "measurement_manifest_sha256": measurement_sha, + "pairwise": isolation_body["pairwise"], + "schema": "hyperlex.residual_threshold_v2_surface_isolation_report.v1", + "synset_pairs": isolation_body["synset_pairs"], + "total": isolation_body["total"], + } + distribution_text = _render_json(distribution) + isolation_text = _render_json(isolation) + distribution_sha = sha256_bytes(distribution_text.encode("utf-8")) + isolation_sha = sha256_bytes(isolation_text.encode("utf-8")) + receipt = { + **_public_flags(), + "allocation_rule_id": SAMPLING_RULE_ID, + "authorization": AUTHORIZATION, + "calibration_labeled": False, + "calibration_manifest_rows": 200, + "calibration_manifest_sha256": calibration_sha, + "calibration_resolved": False, + "calibration_scored": False, + "calibration_surface_frozen": True, + "determinism": "IDENTICAL", + "development_partitions_fenced": first_account["development_partitions_fenced"], + "distribution_sha256": distribution_sha, + "draw_passes": 2, + "draw_seed": None, + "eligible_after_exclusions": first_account["eligible_after_exclusions"], + "exclusion_counts": first_account["exclusion_counts"], + "historical_probe_hashes_outside_development": first_account["historical_probe_hashes_outside_development"], + "isolation_sha256": isolation_sha, + "measurement_labeled": False, + "measurement_manifest_rows": 200, + "measurement_manifest_sha256": measurement_sha, + "measurement_reserve_files": 0, + "measurement_resolved": False, + "measurement_scored": False, + "measurement_state": MEASUREMENT_STATE, + "measurement_surface_frozen": True, + "ordering": "normalized_text_sha256 ascending within cell", + "replacement": "without_replacement", + "rng_used": False, + "rule": "RUNE.SEMANTIC_COMPOSITIONALITY_THRESHOLD.v2", + "schema": "hyperlex.residual_threshold_v2_surface_draw_receipt.v1", + "source_wordnet_universe_count": first_account["source_wordnet_universe_count"], + "state": STATE_FROZEN, + "strata_available": first_account["strata_available"], + "stratification": "source_pos x token_count", + "v1_calibration_rows_fenced": 27, + "v1_failure_sha256": EXPECTED_V1_FAILURE, + "v1_remains_sealed": True, + "v2_preregistration_sha256": SEALED[SOURCE / "RESIDUAL_THRESHOLD_V2_PREREGISTRATION.json"], + "visit_order": "round-robin across cells sorted by source_pos then token_count; skip exhausted cells; first 200 emissions CALIBRATION_V2; next 200 emissions MEASUREMENT_V2", + "events_sha256": EXPECTED_EVENTS, + "ledger_sha256": EXPECTED_LEDGER, + } + receipt_text = _render_json(receipt) + receipt_sha = sha256_bytes(receipt_text.encode("utf-8")) + _write_new(CALIBRATION_MANIFEST, calibration_text) + _write_new(MEASUREMENT_MANIFEST, measurement_text) + _write_new(ISOLATION_PATH, isolation_text) + _write_new(DISTRIBUTION_PATH, distribution_text) + _write_new(DRAW_RECEIPT, receipt_text) + if sha256_file(CALIBRATION_MANIFEST) != calibration_sha: + refuse("calibration manifest hash drifted on write") + if sha256_file(MEASUREMENT_MANIFEST) != measurement_sha: + refuse("measurement manifest hash drifted on write") + if sha256_file(ISOLATION_PATH) != isolation_sha: + refuse("isolation report hash drifted on write") + if sha256_file(DISTRIBUTION_PATH) != distribution_sha: + refuse("distribution report hash drifted on write") + if sha256_file(DRAW_RECEIPT) != receipt_sha: + refuse("draw receipt hash drifted on write") + _verify_sealed() + tracker_sha = _update_tracker_success( + { + "calibration_manifest_sha256": calibration_sha, + "distribution_sha256": distribution_sha, + "draw_receipt_sha256": receipt_sha, + "isolation_sha256": isolation_sha, + "measurement_manifest_sha256": measurement_sha, + } + ) + for path, expected in SEALED.items(): + if sha256_file(path) != expected: + refuse(f"sealed artifact changed after the draw: {path.name}") + if sha256_file(EVENTS) != EXPECTED_EVENTS or sha256_file(LEDGER_FILE) != EXPECTED_LEDGER: + refuse("events or ledger changed") + report = { + "allocation_rule_id": SAMPLING_RULE_ID, + "calibration_manifest_sha256": calibration_sha, + "distribution_sha256": distribution_sha, + "draw_receipt_sha256": receipt_sha, + "eligible_after_exclusions": first_account["eligible_after_exclusions"], + "exclusion_counts": first_account["exclusion_counts"], + "isolation_sha256": isolation_sha, + "isolation_total": isolation_body["total"], + "measurement_manifest_sha256": measurement_sha, + "source_wordnet_universe_count": first_account["source_wordnet_universe_count"], + "state": STATE_FROZEN, + "tracker_sha256": tracker_sha, + } + print(json.dumps(report, indent=2, sort_keys=True)) + return report + + +def main() -> None: + freeze_surfaces() + + +if __name__ == "__main__": + main() From 0eb7aa82f025b7c761250cbabfc6ec40b476550a Mon Sep 17 00:00:00 2001 From: Daniel Meyer Date: Mon, 28 Sep 2026 16:33:31 -0700 Subject: [PATCH 24/42] feat(007): land SELECT residual and schedule work (23) --- .../residual_threshold_v2_calibration.py | 963 ++++++++++++++++++ 1 file changed, 963 insertions(+) create mode 100644 scripts/shadow/hyperlexical/residual_threshold_v2_calibration.py diff --git a/scripts/shadow/hyperlexical/residual_threshold_v2_calibration.py b/scripts/shadow/hyperlexical/residual_threshold_v2_calibration.py new file mode 100644 index 00000000..9ba4739f --- /dev/null +++ b/scripts/shadow/hyperlexical/residual_threshold_v2_calibration.py @@ -0,0 +1,963 @@ +"""Execute the frozen threshold stack on the v2 calibration surface only. + +Measurement is hashed and otherwise untouched. Operator labels are loaded +only after the calibration score receipt is on disk. +""" + +from __future__ import annotations + +import hashlib +import json +import os +import sys +from collections import Counter +from pathlib import Path + +os.environ["MKL_NUM_THREADS"] = "1" +os.environ["NUMEXPR_NUM_THREADS"] = "1" +os.environ["OMP_NUM_THREADS"] = "1" +os.environ["OPENBLAS_NUM_THREADS"] = "1" +os.environ["TOKENIZERS_PARALLELISM"] = "false" + +from hyperlexical.residual_threshold_v1 import ( + ALGORITHM_ID, + CONFOUND_REVIEW, + INSUFFICIENT_SUPPORT, + MIN_CALIBRATION_HIGH, + MIN_CALIBRATION_SECONDARY, + NO_DIRECTION, + NO_THRESHOLD, + THRESHOLD_FROZEN, +) + +LEDGER = Path("/home/morpheus/hlx-private/eval-reserve-20260926") +SOURCE = LEDGER / "operator-review/HLX-EVAL-UNBIND-SEMANTIC-EVIDENCE-SOURCE-V1-001" +SENSE = LEDGER / "operator-review/HLX-EVAL-UNBIND-SENSE-SCREEN-V1-HYPOTHESIS-001" +WORDNET = LEDGER / "acquisition/sources/wordnet-3.0/wordnet" +EVENTS = LEDGER / "events.jsonl" +LEDGER_FILE = LEDGER / "ledger.json" +TRACKER = SENSE / "HYPOTHESIS.json" +EVIDENCE = SENSE / "DEVELOPMENT_EVIDENCE.json" + +CALIBRATION_MANIFEST = SOURCE / "RESIDUAL_THRESHOLD_V2_CALIBRATION_MANIFEST.jsonl" +MEASUREMENT_MANIFEST = SOURCE / "RESIDUAL_THRESHOLD_V2_MEASUREMENT_MANIFEST.jsonl" +RESOLUTION_PATH = SOURCE / "RESIDUAL_THRESHOLD_V2_CALIBRATION_RESOLUTION.jsonl" +RESOLUTION_RECEIPT = SOURCE / "RESIDUAL_THRESHOLD_V2_CALIBRATION_RESOLUTION_RECEIPT.json" +SCORES_PATH = SOURCE / "RESIDUAL_THRESHOLD_V2_CALIBRATION_SCORES.jsonl" +SCORE_RECEIPT = SOURCE / "RESIDUAL_THRESHOLD_V2_CALIBRATION_SCORE_RECEIPT.json" +LABELS_PATH = SOURCE / "RESIDUAL_THRESHOLD_V2_CALIBRATION_LABELS.jsonl" +ANALYSIS_PATH = SOURCE / "RESIDUAL_THRESHOLD_V2_CALIBRATION_ANALYSIS.json" +CONFOUND_PATH = SOURCE / "RESIDUAL_THRESHOLD_V2_CALIBRATION_CONFOUND_ANALYSIS.json" +SEARCH_PATH = SOURCE / "RESIDUAL_THRESHOLD_V2_THRESHOLD_SEARCH.json" +FROZEN_PATH = SOURCE / "RESIDUAL_THRESHOLD_V2_THRESHOLD_FROZEN.json" +FAILURE_PATH = SOURCE / "RESIDUAL_THRESHOLD_V2_CALIBRATION_FAILURE.json" + +AUTHORIZATION = "THRESHOLD_V2_CALIBRATION_EXECUTION_AUTHORIZATION" +MEASUREMENT_NEXT = "THRESHOLD_V2_MEASUREMENT_EXECUTION_AUTHORIZATION" +OPERATOR_CLASSES = ("HIGH", "SECONDARY", "REJECT", "QUARANTINE") + +EXPECTED_CALIBRATION = "34bc70b93039fc6a5ba4bb58685e0fbfe6f91dd1f86a64fec8326a84013a6465" +EXPECTED_MEASUREMENT = "78ca09ab14912681d028e8b9b1c77a8daf1d0c8c81561434eb9b6ade45a753e5" +EXPECTED_TRACKER = "a4264943f0313dcde85c6da8b833ff0c45c5c23972e1a4d0f2e69fa00c3e09ad" +EXPECTED_EVENTS = "96b74a92d44f1cf9fe152b18e5207176f161ba3bfce528dac38aa4571a742f9c" +EXPECTED_LEDGER = "77e22433203879b252f7a9e309d2013d7550101d1c4a014b494d2c96df87d0e0" +EXPECTED_V1_FAILURE = "1d3f1f26609c1f1a47790d94730fba50e9f4d888dd8d7d89e6fa698a924fb3a8" +EXPECTED_RESIDUAL_SPEC = "39c2914e32557ffe1a456a56f8742ea4fe8f1aaec1dc1da451656cd22f0db32d" +EXPECTED_WSD = "ed945989cf4947ac84633ba2c4aa10c1ba381d2396da0b573a844f83ec367a18" +EXPECTED_PREREG = "1c962703e12c5c48fd279dc766fd4d1d3108c40486a10789fdda112926e98261" +EXPECTED_SAMPLE = "46e3d32b4a4c8af73c9197557b09ceed872b1918d41143ed551bb3b4572c6151" +EXPECTED_DRAW = "b89cbd357138c8b9eabeabeca055695b15fd2163840438e53989a920191d3419" +EXPECTED_ISOLATION = "6ac513d9d7b3dd366f7c6f5ce5ca6821a33c5907d4bf2d1de45dc7a5a932bc38" +EXPECTED_DISTRIBUTION = "51d9a98a849248e40aadafd277c1c44adf0c7a7fcc842864f00d2fc3d8125886" + +SEALED = { + CALIBRATION_MANIFEST: EXPECTED_CALIBRATION, + MEASUREMENT_MANIFEST: EXPECTED_MEASUREMENT, + SOURCE / "RESIDUAL_THRESHOLD_V2_PREREGISTRATION.json": EXPECTED_PREREG, + SOURCE / "RESIDUAL_THRESHOLD_V2_SAMPLE_SIZE.json": EXPECTED_SAMPLE, + SOURCE / "RESIDUAL_THRESHOLD_V2_SURFACE_DRAW_RECEIPT.json": EXPECTED_DRAW, + SOURCE / "RESIDUAL_THRESHOLD_V2_SURFACE_ISOLATION_REPORT.json": EXPECTED_ISOLATION, + SOURCE / "RESIDUAL_THRESHOLD_V2_SURFACE_DISTRIBUTION_REPORT.json": EXPECTED_DISTRIBUTION, + SOURCE / "RESIDUAL_THRESHOLD_V1_CALIBRATION_FAILURE.json": EXPECTED_V1_FAILURE, + SOURCE / "RESIDUAL_THRESHOLD_V1_CALIBRATION_MANIFEST.jsonl": "ba561935cff5f9f0a36af0b8b24e4b69bce5fb2c61b5ea2236f0a664c56bcf5d", + SOURCE / "RESIDUAL_CANDIDATE_SPEC.json": EXPECTED_RESIDUAL_SPEC, + SOURCE / "MODEL_BASED_WSD_RESOLUTION.jsonl": EXPECTED_WSD, + SOURCE / "INTEGRATED_CONSTITUENT_RESOLUTION_V1.jsonl": "0f5dafc3676a4071ce8c889e58958b90203589aa3e91d78111b4e3292bdd87fb", + SOURCE / "RESIDUAL_DEVELOPMENT_SCORES.jsonl": "cea638679faeee1bc1c689823e7c0c08562c4d7ef1f8230bbbf4079239e7c3e7", + EVIDENCE: "0e9b3c1af9dd573bf6e2034640e468e8ab9074e1e76c90cef1f39f68d607bc03", + EVENTS: EXPECTED_EVENTS, + LEDGER_FILE: EXPECTED_LEDGER, + SOURCE / "HYPOTHESIS.json": "39127a810d38ede96d7947c33dbc3e5491c9e1cc9b3f76b1064d9e0dd04a7787", + SENSE / "CLASSIFICATION_PROCEDURE.v2.json": "3f4071640d0c9f29cf56f53969a88ec25c635444b87765e77e1b9158470e5662", +} + +OUTPUTS = ( + RESOLUTION_PATH, + RESOLUTION_RECEIPT, + SCORES_PATH, + SCORE_RECEIPT, + LABELS_PATH, + ANALYSIS_PATH, + CONFOUND_PATH, + SEARCH_PATH, + FROZEN_PATH, + FAILURE_PATH, +) + +MEASUREMENT_EXECUTION_NAMES = { + "RESIDUAL_THRESHOLD_V2_MEASUREMENT_RESOLUTION.jsonl", + "RESIDUAL_THRESHOLD_V2_MEASUREMENT_SCORES.jsonl", + "RESIDUAL_THRESHOLD_V2_MEASUREMENT_LABELS.jsonl", + "RESIDUAL_THRESHOLD_V2_MEASUREMENT_PREDICTIONS.jsonl", +} + + +def sha256_bytes(payload: bytes) -> str: + return hashlib.sha256(payload).hexdigest() + + +def sha256_file(path: Path) -> str: + return sha256_bytes(path.read_bytes()) + + +def refuse(message: str) -> None: + raise SystemExit(message) + + +def support_passed(ready_high: int, ready_secondary: int) -> bool: + return ready_high >= MIN_CALIBRATION_HIGH and ready_secondary >= MIN_CALIBRATION_SECONDARY + + +def lesk_tie_rows(rows: list[dict]) -> list[dict]: + """GlossBERT sees Lesk ties only. A structural pointer conflict stays Tier 1.""" + kept = [] + for row in rows: + if row.get("constituent_index") is None or row.get("resolution_status") != "AMBIGUOUS": + continue + method = row.get("resolution_method") + if method == "STRUCTURAL_EXACT": + continue + if method != "EXTENDED_LESK_V1": + refuse("constituent decision is outside the frozen stack") + kept.append(row) + return kept + + +def _render_json(payload: dict) -> str: + return json.dumps(payload, indent=2, sort_keys=True, ensure_ascii=True) + "\n" + + +def _render_jsonl(rows: list[dict]) -> str: + return "".join(json.dumps(row, sort_keys=True, ensure_ascii=True) + "\n" for row in rows) + + +def _write_new(path: Path, text: str) -> str: + if path.exists(): + refuse(f"refusing to rewrite {path.name}") + path.write_text(text, encoding="utf-8") + path.chmod(0o600) + digest = sha256_file(path) + if digest != sha256_bytes(text.encode("utf-8")): + refuse(f"hash drifted on write: {path.name}") + return digest + + +def _read_jsonl(path: Path) -> list[dict]: + return [json.loads(line) for line in path.read_text(encoding="utf-8").splitlines() if line] + + +def _verify_sealed(include_tracker: bool = True) -> None: + if include_tracker and sha256_file(TRACKER) != EXPECTED_TRACKER: + refuse("tracker hash drifted") + for path, expected in SEALED.items(): + if sha256_file(path) != expected: + refuse(f"sealed artifact changed: {path.name}") + + +def _assert_measurement_sealed() -> None: + if sha256_file(MEASUREMENT_MANIFEST) != EXPECTED_MEASUREMENT: + refuse("measurement manifest changed") + for name in MEASUREMENT_EXECUTION_NAMES: + if (SOURCE / name).exists(): + refuse(f"measurement execution artifact exists: {name}") + + +def _sealed_rows(manifest: list[dict]) -> list[dict]: + rows = [] + for row in manifest: + pos, offset = row["pwn30_synset"].split(":", 1) + if row["row_id"] != row["normalized_text_sha256"]: + refuse("calibration row_id drifted") + rows.append( + { + "gloss": row["frozen_gloss"], + "pos": row["pos"], + "row_id": row["row_id"], + "sense_class": None, + "surface": row["surface"], + "synset_offset": offset, + "synset_pos": pos, + } + ) + return rows + + +def _parent_summaries(sealed: list[dict], by_parent: dict) -> list[dict]: + from hyperlexical.residual_model_resolved_replay_v1 import ( + EXACT, + RESOLVED, + abstention_reason, + projection_token, + row_projection, + ) + + summaries = [] + for row in sealed: + items = by_parent.get(row["row_id"], []) + tokens = [projection_token(item["resolution_tier"], item["resolution_status"]) for item in items] + status = row_projection(tokens) + raw = [] + for item in items: + if item["resolution_status"] in {EXACT, RESOLVED}: + raw.append(RESOLVED if item["resolution_status"] == RESOLVED else EXACT) + else: + raw.append(item["resolution_status"]) + summaries.append( + { + "abstention_reason": None if status == "RESIDUAL_READY" else abstention_reason(raw), + "constituent_resolution_tiers": [item["resolution_tier"] for item in items], + "pos": row["pos"], + "row_id": row["row_id"], + "row_resolution_status": status, + "selected_constituent_synsets": [item["selected_pwn30_synset"] for item in items], + "selected_sense_keys": [item["selected_sense_key_if_available"] for item in items], + "surface": row["surface"], + "synset": f"{row['synset_pos']}:{row['synset_offset']}", + } + ) + return summaries + + +def _resolve(sealed: list[dict]) -> tuple[list[dict], list[dict], dict, int]: + from hyperlexical.constituent_sense_resolution_v1_replay import ( + PROCEDURE_PATH, + SPEC_PATH, + build_catalog, + resolve_once, + ) + from hyperlexical.model_based_wsd_candidate_v1 import MODEL_REVISION + from hyperlexical.model_based_wsd_candidate_v1_replay import ( + import_runtime, + infer_pass, + load_sense_index, + resolution_rows, + ) + from hyperlexical.residual_model_resolved_replay_v1_replay import build_integrated, public_integrated + from hyperlexical.residual_threshold_v1_calibration import _tier3_pool + from hyperlexical.unbind_sense_screen_v1 import load_exceptions + + if MODEL_REVISION != "0cc3b83af5496e27ebcc95ef0cf37ea0a9281a7a": + refuse("GlossBERT revision drifted") + print("resolver catalog", file=sys.stderr, flush=True) + by_id, index = build_catalog() + exceptions = load_exceptions(WORDNET) + spec_sha = sha256_file(SPEC_PATH) + procedure_sha = sha256_file(PROCEDURE_PATH) + wsd_spec_sha = sha256_file(SOURCE / "MODEL_BASED_WSD_CANDIDATE_SPEC.json") + print("resolver pass 1", file=sys.stderr, flush=True) + first = resolve_once(sealed, by_id, index, exceptions, spec_sha, procedure_sha) + print("resolver pass 2", file=sys.stderr, flush=True) + second = resolve_once(sealed, by_id, index, exceptions, spec_sha, procedure_sha) + if first != second: + _freeze_nondeterministic("resolver") + sense_index = load_sense_index(WORDNET / "index.sense") + pool_first = _tier3_pool(lesk_tie_rows(first), by_id, exceptions, sense_index) + pool_second = _tier3_pool(lesk_tie_rows(second), by_id, exceptions, sense_index) + if pool_first != pool_second: + _freeze_nondeterministic("tier3_pool") + model_rows: list[dict] = [] + if pool_first: + from hyperlexical.model_based_wsd_candidate_v1_replay import assert_weights + + assert_weights() + print(f"glossbert pool {len(pool_first)}", file=sys.stderr, flush=True) + torch, tokenizer, model = import_runtime() + raw_first = infer_pass(torch, tokenizer, model, pool_first, wsd_spec_sha) + raw_second = infer_pass(torch, tokenizer, model, pool_second, wsd_spec_sha) + if raw_first != raw_second: + _freeze_nondeterministic("glossbert_scores") + model_first = resolution_rows(pool_first, raw_first) + model_second = resolution_rows(pool_second, raw_second) + if model_first != model_second: + _freeze_nondeterministic("glossbert_resolution") + model_rows = model_first + integrated_first, by_parent = build_integrated(first, model_rows, exceptions, sense_index) + integrated_second, _by_parent_second = build_integrated(second, model_rows, exceptions, sense_index) + if integrated_first != integrated_second: + _freeze_nondeterministic("integrated_resolution") + public_rows = [public_integrated(item) for item in integrated_first] + return public_rows, model_rows, by_parent, len(pool_first) + + +def _score(sealed: list[dict], by_parent: dict, model_rows: list[dict], resolution_sha: str) -> tuple[list[dict], dict]: + from hyperlexical.model_based_wsd_candidate_v1 import MODEL_NAME, MODEL_REVISION + from hyperlexical.residual_model_resolved_replay_v1 import TIER3_GLOSSBERT + from hyperlexical.residual_model_resolved_replay_v1_replay import ( + RESIDUAL_SPEC_SHA, + apply_overflow, + assemble_scores, + encode_needed, + nuisance_for, + prepare_jobs, + score_jobs, + ) + from hyperlexical.semantic_compositionality_residual import MODEL_NAME as RESIDUAL_NAME + from hyperlexical.semantic_compositionality_residual import MODEL_REVISION as RESIDUAL_REVISION + from hyperlexical.semantic_compositionality_residual_replay import build_indexes, load_encoder + from hyperlexical.unbind_sense_screen_v1 import load_exceptions + + if MODEL_NAME != "kanishka/GlossBERT" or MODEL_REVISION != "0cc3b83af5496e27ebcc95ef0cf37ea0a9281a7a": + refuse("GlossBERT identity drifted") + if RESIDUAL_NAME != "sentence-transformers/all-MiniLM-L6-v2": + refuse("residual model drifted") + if RESIDUAL_REVISION != "1110a243fdf4706b3f48f1d95db1a4f5529b4d41": + refuse("residual revision drifted") + if RESIDUAL_SPEC_SHA != EXPECTED_RESIDUAL_SPEC: + refuse("residual spec hash drifted") + exceptions = load_exceptions(WORDNET) + residual_by_id, _residual_index = build_indexes() + model_index = {(row["parent_row_id"], row["constituent_index"]): row for row in model_rows} + jobs, unknown = prepare_jobs(sealed, by_parent, residual_by_id, exceptions, model_index) + encoder = load_encoder() + kept, overflow = apply_overflow(encoder, jobs) + print("residual encode pass 1", file=sys.stderr, flush=True) + vectors_first = encode_needed(encoder, kept) + scored_first = score_jobs(kept, vectors_first, resolution_sha) + print("residual encode pass 2", file=sys.stderr, flush=True) + vectors_second = encode_needed(encoder, kept) + scored_second = score_jobs(kept, vectors_second, resolution_sha) + if vectors_first != vectors_second or scored_first != scored_second: + _freeze_nondeterministic("residual_scores") + scores = assemble_scores(sealed, scored_first, unknown, overflow) + nuisance = {job["row_id"]: nuisance_for(job) for job in kept} + for row in scores: + if "operator_bucket" in row: + refuse("score artifact contains an operator label") + if row["score_status"] == "SCORED": + row["uses_tier3"] = TIER3_GLOSSBERT in row["constituent_resolution_tiers"] + return scores, nuisance + + +def _freeze_nondeterministic(gate: str) -> None: + _assert_measurement_sealed() + if FAILURE_PATH.exists(): + refuse(f"NOT_DETERMINISTIC during {gate}") + payload = { + "failed_gate": gate, + "failure_state": "NOT_DETERMINISTIC", + "json_schema_document": None, + "json_schema_exists": False, + "measurement_touched": False, + "schema": "hyperlex.residual_threshold_v2_calibration_failure.v1", + "threshold_frozen": False, + "threshold_value": None, + } + _write_new(FAILURE_PATH, _render_json(payload)) + _update_tracker("NOT_DETERMINISTIC", None, sha256_file(FAILURE_PATH), {}) + refuse("NOT_DETERMINISTIC") + + +def _label_index(calibration_ids: set[str]) -> dict[str, str | None]: + found: dict[str, str | None] = {} + + def add(row_id: str, bucket: str | None) -> None: + if row_id not in calibration_ids: + return + prior = found.get(row_id) + if prior is not None and prior != bucket: + refuse(f"operator labels disagree for {row_id}") + found[row_id] = bucket + + evidence = json.loads(EVIDENCE.read_text(encoding="utf-8")) + for row in evidence["rows"]: + add(row["row_id"], row.get("operator_bucket")) + root = LEDGER / "operator-review" + for path in sorted(root.rglob("*labels*.jsonl")): + if "MEASUREMENT" in path.name: + continue + for row in _read_jsonl(path): + if "operator_bucket" not in row or "row_id" not in row: + continue + add(row["row_id"], row["operator_bucket"]) + return found + + +def _support(labels: list[dict]) -> dict: + ready = Counter() + unknown = Counter() + unlabeled = {"SCORED": 0, "UNKNOWN": 0} + for row in labels: + bucket = row["operator_bucket"] + status = row["score_status"] + if bucket not in OPERATOR_CLASSES: + unlabeled[status] += 1 + continue + if status == "SCORED": + ready[bucket] += 1 + else: + unknown[bucket] += 1 + return { + "ready": {name: ready[name] for name in OPERATOR_CLASSES}, + "unlabeled": unlabeled["SCORED"] + unlabeled["UNKNOWN"], + "unlabeled_scored": unlabeled["SCORED"], + "unlabeled_unknown": unlabeled["UNKNOWN"], + "unknown_by_class": {name: unknown[name] for name in OPERATOR_CLASSES}, + } + + +def _search_payload(selectable: list[dict], selection: dict) -> dict: + from decimal import Decimal + + from hyperlexical.residual_threshold_v1 import ( + MIN_PREDICTED_YES, + PRECISION_FLOOR, + WILSON_LOWER_FLOOR, + _candidate_thresholds, + _high_recall, + _passes_candidate, + _precision_terms, + _ready_rows, + _safety_counts, + _stable, + precision_gates, + wilson_interval, + ) + + if selection["calibration_state"] in {INSUFFICIENT_SUPPORT, NO_DIRECTION, CONFOUND_REVIEW}: + return { + "candidate_count": None, + "candidates_evaluated": False, + "gate_pass_counts": None, + "json_schema_document": None, + "json_schema_exists": False, + "reason": selection["calibration_state"], + "schema": "hyperlex.residual_threshold_v2_threshold_search.v1", + "search_executed": False, + "selected_threshold": None, + "selection_algorithm_id": ALGORITHM_ID, + } + ready = _ready_rows(selectable) + comparison = [row for row in ready if row["operator_bucket"] in {"HIGH", "SECONDARY"}] + candidates = _candidate_thresholds(ready) + counts: Counter[str] = Counter() + passing = [] + for threshold in candidates: + true_high, support = _precision_terms(comparison, threshold) + precision_ok = precision_gates( + true_high, + support, + minimum_yes=MIN_PREDICTED_YES, + precision_floor=PRECISION_FLOOR, + wilson_floor=WILSON_LOWER_FLOOR, + ) + safety = _safety_counts(ready, threshold) + reject_ok = safety["reject_predicted_yes"] == 0 + quarantine_ok = safety["quarantine_predicted_yes"] == 0 + stable = _stable( + comparison, + threshold, + minimum_yes=MIN_PREDICTED_YES, + precision_floor=PRECISION_FLOOR, + wilson_floor=WILSON_LOWER_FLOOR, + ) + interval = wilson_interval(true_high, support) + wilson_ok = interval is not None and interval[0] >= WILSON_LOWER_FLOOR + support_ok = support >= MIN_PREDICTED_YES + counts["candidates"] += 1 + counts["predicted_yes_support"] += int(support_ok) + counts["precision"] += int(precision_ok and support_ok) + counts["wilson_lower"] += int(wilson_ok) + counts["leave_one_out"] += int(stable) + counts["reject_veto"] += int(reject_ok) + counts["quarantine_veto"] += int(quarantine_ok) + if _passes_candidate(ready, comparison, threshold): + counts["all_gates"] += 1 + passing.append((str(_high_recall(comparison, threshold)), threshold)) + selected = selection.get("threshold_value") + if passing: + best = max(passing, key=lambda item: (Decimal(item[0]), Decimal(item[1]))) + if selected != best[1]: + refuse("threshold search disagrees with select_threshold") + elif selected is not None: + refuse("select_threshold returned a value with no passing candidate") + return { + "candidate_count": len(candidates), + "candidates_evaluated": True, + "gate_pass_counts": dict(sorted(counts.items())), + "json_schema_document": None, + "json_schema_exists": False, + "schema": "hyperlex.residual_threshold_v2_threshold_search.v1", + "search_executed": True, + "selected_threshold": selected, + "selection_algorithm_id": ALGORITHM_ID, + } + + +def _confound_rows(joined: list[dict], nuisance: dict) -> list[dict]: + rows = [] + for row in joined: + if row["score_status"] != "SCORED" or row["operator_bucket"] not in OPERATOR_CLASSES: + continue + extra = nuisance.get(row["row_id"]) + if extra is None: + refuse("ready row is missing nuisance fields") + rows.append( + { + "bucket": row["operator_bucket"], + "character_length": extra["character_length"], + "content_count": extra["content_count"], + "max_candidate_senses": extra["max_candidate_senses"], + "min_glossbert_confidence": extra["min_glossbert_confidence"], + "min_glossbert_margin": extra["min_glossbert_margin"], + "pos": row["pos"], + "residual_score": row["residual_score"], + "row_id": row["row_id"], + "surface": extra["surface"], + "synset": extra["synset"], + "tiers": list(extra["tiers"]), + "token_count": extra["token_count"], + "uses_tier3": row["uses_tier3"], + } + ) + return rows + + +def _judge(nuisance: dict) -> dict: + receipt = json.loads(SCORE_RECEIPT.read_text(encoding="utf-8")) + if receipt["operator_labels_loaded"] is not False or receipt["operator_labels_joined"] is not False: + refuse("score receipt does not record a pre-label freeze") + if receipt["calibration_score_frozen"] is not True or receipt["measurement_touched"] is not False: + refuse("score receipt is not a sealed pre-label freeze") + if sha256_file(SCORES_PATH) != receipt["score_sha256"]: + refuse("score hash drifted before the label join") + scores = _read_jsonl(SCORES_PATH) + manifest = {row["row_id"]: row for row in _read_jsonl(CALIBRATION_MANIFEST)} + labels_by_id = _label_index(set(manifest)) + joined = [] + for score in scores: + row_id = score["row_id"] + manifest_row = manifest[row_id] + bucket = labels_by_id.get(row_id) + if bucket not in OPERATOR_CLASSES: + bucket = None + joined.append( + { + "operator_bucket": bucket, + "operator_label_present": bucket is not None, + "pos": score.get("pos", manifest_row["pos"]), + "residual_score": score.get("residual_score"), + "row_id": row_id, + "score_status": score["score_status"], + "surface": manifest_row["surface"], + "uses_tier3": score.get("uses_tier3"), + } + ) + label_rows = [ + { + "operator_bucket": row["operator_bucket"], + "operator_label_present": row["operator_label_present"], + "row_id": row["row_id"], + "score_status": row["score_status"], + } + for row in joined + ] + label_sha = _write_new(LABELS_PATH, _render_jsonl(label_rows)) + support = _support(joined) + passed = support_passed(support["ready"]["HIGH"], support["ready"]["SECONDARY"]) + analysis = { + "admitted": 0, + "authorization": AUTHORIZATION, + "confound_result": "NOT_RUN", + "direction_result": "NOT_RUN", + "gold": 0, + "json_schema_document": None, + "json_schema_exists": False, + "label_sha256": label_sha, + "measurement_labels_loaded": False, + "measurement_state": "SEALED", + "schema": "hyperlex.residual_threshold_v2_calibration_analysis.v1", + "score_receipt_sha256": sha256_file(SCORE_RECEIPT), + "score_sha256": sha256_file(SCORES_PATH), + "select_005_authorized": False, + "selected_source": "none", + "settled": 0, + "support": support, + "support_gate": { + "minimum_ready_high": MIN_CALIBRATION_HIGH, + "minimum_ready_secondary": MIN_CALIBRATION_SECONDARY, + "passed": passed, + }, + } + if not passed: + analysis["calibration_state"] = INSUFFICIENT_SUPPORT + analysis_sha = _write_new(ANALYSIS_PATH, _render_json(analysis)) + confound = { + "confound_gate_executed": False, + "confound_result": "NOT_RUN", + "frozen_stop_conditions": ["tier3_concentrated", "extreme_driven", "pos_partitioned"], + "json_schema_document": None, + "json_schema_exists": False, + "reason": INSUFFICIENT_SUPPORT, + "schema": "hyperlex.residual_threshold_v2_calibration_confound_analysis.v1", + } + confound_sha = _write_new(CONFOUND_PATH, _render_json(confound)) + search = _search_payload([], {"calibration_state": INSUFFICIENT_SUPPORT}) + search_sha = _write_new(SEARCH_PATH, _render_json(search)) + failure = _failure(INSUFFICIENT_SUPPORT, "support", analysis_sha, label_sha, search_sha, confound_sha, support, None, None, None) + return _finish(INSUFFICIENT_SUPPORT, None, failure, { + "analysis": analysis_sha, + "confound": confound_sha, + "failure": sha256_file(FAILURE_PATH), + "labels": label_sha, + "search": search_sha, + }) + return _select(joined, analysis, label_sha, nuisance) + + +def _failure(state: str, gate: str, analysis_sha: str, label_sha: str, search_sha: str, confound_sha: str, support: dict, direction, confound, candidate_count) -> str: + payload = { + "analysis_sha256": analysis_sha, + "confound_sha256": confound_sha, + "failed_gate": gate, + "failure_state": state, + "json_schema_document": None, + "json_schema_exists": False, + "label_sha256": label_sha, + "measurement_touched": False, + "observed_confounds": confound, + "observed_direction": direction, + "observed_support": support, + "observed_threshold_candidate_count": candidate_count, + "schema": "hyperlex.residual_threshold_v2_calibration_failure.v1", + "search_sha256": search_sha, + "threshold_frozen": False, + "threshold_value": None, + } + return _write_new(FAILURE_PATH, _render_json(payload)) + + +def _select(joined: list[dict], analysis: dict, label_sha: str, nuisance: dict) -> dict: + from hyperlexical.residual_model_resolved_replay_v1 import confound_report + from hyperlexical.residual_threshold_v1 import select_threshold + + selectable = [] + for row in joined: + if row["operator_bucket"] not in OPERATOR_CLASSES: + continue + if row["uses_tier3"] is None or row["pos"] is None: + refuse("labeled calibration row is missing tier or pos provenance") + selectable.append( + { + "development_row": False, + "operator_bucket": row["operator_bucket"], + "pos": row["pos"], + "residual_score": row["residual_score"], + "score_status": row["score_status"], + "uses_tier3": row["uses_tier3"], + } + ) + print("threshold selection pass 1", file=sys.stderr, flush=True) + first = select_threshold(selectable) + print("threshold selection pass 2", file=sys.stderr, flush=True) + second = select_threshold(selectable) + if first != second: + _freeze_nondeterministic("threshold_selection") + state = first["calibration_state"] + analysis["calibration_state"] = state + analysis["direction_result"] = first.get("direction", "NOT_RUN") + analysis["confound_result"] = first.get("confound", "NOT_RUN") + analysis["selection"] = first + analysis_sha = _write_new(ANALYSIS_PATH, _render_json(analysis)) + direction = first.get("direction") + confound_body: dict + if direction != "SUPPORTED_DIRECTION": + confound_body = { + "confound_gate_executed": False, + "confound_result": "NOT_RUN", + "frozen_stop_conditions": ["tier3_concentrated", "extreme_driven", "pos_partitioned"], + "json_schema_document": None, + "json_schema_exists": False, + "reason": state, + "schema": "hyperlex.residual_threshold_v2_calibration_confound_analysis.v1", + } + else: + report = confound_report(_confound_rows(joined, nuisance)) + flags = { + "extreme_driven": report["extreme_driven"], + "pos_partitioned": report["pos_partitioned"], + "tier3_concentrated": bool(report["tier3"]["concentrated"]), + } + selector_flags = first.get("confound") + if selector_flags is not None and selector_flags != flags: + refuse("confound report disagrees with select_threshold") + fired = flags["extreme_driven"] or flags["pos_partitioned"] or flags["tier3_concentrated"] + confound_body = { + "confound_gate_executed": True, + "confound_result": "STOP" if fired else "PASS", + "frozen_stop_conditions": flags, + "json_schema_document": None, + "json_schema_exists": False, + "nuisance_changes_stop_decision": False, + "report": report, + "schema": "hyperlex.residual_threshold_v2_calibration_confound_analysis.v1", + } + confound_sha = _write_new(CONFOUND_PATH, _render_json(confound_body)) + search = _search_payload(selectable, first) + search_sha = _write_new(SEARCH_PATH, _render_json(search)) + support = analysis["support"] + if state != THRESHOLD_FROZEN: + gates = { + INSUFFICIENT_SUPPORT: "support", + NO_DIRECTION: "direction", + CONFOUND_REVIEW: "confound", + NO_THRESHOLD: "precision_gate", + } + failure_sha = _failure( + state, + gates[state], + analysis_sha, + label_sha, + search_sha, + confound_sha, + support, + direction, + first.get("confound"), + search["candidate_count"], + ) + return _finish(state, None, failure_sha, { + "analysis": analysis_sha, + "confound": confound_sha, + "failure": failure_sha, + "labels": label_sha, + "search": search_sha, + }) + metrics = first["selection_metrics"] + frozen = { + "algorithm_id": ALGORITHM_ID, + "calibration_label_sha256": label_sha, + "calibration_manifest_sha256": EXPECTED_CALIBRATION, + "calibration_resolution_sha256": sha256_file(RESOLUTION_PATH), + "calibration_score_sha256": sha256_file(SCORES_PATH), + "confound_result": confound_body["confound_result"], + "confound_sha256": confound_sha, + "direction_result": direction, + "false_high": metrics["false_high"], + "high_recall": metrics["high_recall"], + "json_schema_document": None, + "json_schema_exists": False, + "leave_one_out_result": "PASS", + "measurement_state": "SEALED", + "measurement_touched": False, + "precision": metrics["precision"], + "predicted_yes_support": metrics["predicted_yes_support"], + "quarantine_predicted_yes": metrics["quarantine_predicted_yes"], + "reject_predicted_yes": metrics["reject_predicted_yes"], + "residual_candidate_spec_sha256": EXPECTED_RESIDUAL_SPEC, + "runtime_integration": False, + "schema": "hyperlex.residual_threshold_v2_threshold_frozen.v1", + "search_sha256": search_sha, + "select_005_authorized": False, + "selected_source": "none", + "selection_algorithm_id": ALGORITHM_ID, + "selection_receipt": first, + "threshold_frozen": True, + "threshold_id": "T_HIGH", + "threshold_value": first["threshold_value"], + "true_high": metrics["true_high"], + "wilson_lower": metrics["precision_interval"][0], + "wilson_upper": metrics["precision_interval"][1], + } + frozen_sha = _write_new(FROZEN_PATH, _render_json(frozen)) + return _finish(THRESHOLD_FROZEN, first["threshold_value"], frozen_sha, { + "analysis": analysis_sha, + "confound": confound_sha, + "labels": label_sha, + "search": search_sha, + "threshold": frozen_sha, + }) + + +def _update_tracker(state: str, threshold_value, outcome_sha: str, hashes: dict) -> str: + if sha256_file(TRACKER) != EXPECTED_TRACKER: + refuse("tracker hash drifted before the calibration update") + tracker = json.loads(TRACKER.read_text(encoding="utf-8")) + if tracker.get("residual_threshold_state") != "CALIBRATION_INSUFFICIENT_SUPPORT": + refuse("v1 execution state drifted") + if tracker.get("residual_threshold_v1_disposition") != "CLOSED_CALIBRATION_DESIGN_INSUFFICIENT": + refuse("v1 disposition drifted") + if tracker.get("residual_threshold_value") is not None or tracker.get("residual_threshold_frozen") is not False: + refuse("v1 threshold drifted") + tracker["previous_tracker_sha256"] = EXPECTED_TRACKER + tracker["residual_threshold_v2_state"] = state + tracker["residual_threshold_v2_value"] = threshold_value + tracker["residual_threshold_v2_frozen"] = state == THRESHOLD_FROZEN + tracker["residual_threshold_v2_calibration_executed"] = state != "NOT_DETERMINISTIC" or RESOLUTION_PATH.exists() + tracker["residual_threshold_v2_calibration_resolution_frozen"] = RESOLUTION_PATH.exists() + tracker["residual_threshold_v2_calibration_scores_frozen"] = SCORES_PATH.exists() + tracker["residual_threshold_v2_calibration_labeled"] = LABELS_PATH.exists() + tracker["residual_threshold_v2_measurement_state"] = "SEALED" + tracker["residual_threshold_v2_measurement_resolved"] = False + tracker["residual_threshold_v2_measurement_scored"] = False + tracker["residual_threshold_v2_measurement_labeled"] = False + tracker["residual_threshold_v2_redraw_authorized"] = False + tracker["measurement_eligible"] = False + tracker["selected_source"] = "none" + tracker["select_authorized"] = False + tracker["admitted"] = 0 + tracker["settled"] = 0 + tracker["gold"] = 0 + if state == THRESHOLD_FROZEN: + tracker["next_legal_transition"] = MEASUREMENT_NEXT + else: + tracker["next_legal_transition"] = "NONE" + tracker["next_transition_authorized"] = False + if RESOLUTION_PATH.exists(): + tracker["residual_threshold_v2_calibration_resolution_sha256"] = sha256_file(RESOLUTION_PATH) + if RESOLUTION_RECEIPT.exists(): + tracker["residual_threshold_v2_calibration_resolution_receipt_sha256"] = sha256_file(RESOLUTION_RECEIPT) + if SCORES_PATH.exists(): + tracker["residual_threshold_v2_calibration_scores_sha256"] = sha256_file(SCORES_PATH) + if SCORE_RECEIPT.exists(): + tracker["residual_threshold_v2_calibration_score_receipt_sha256"] = sha256_file(SCORE_RECEIPT) + for key, digest in hashes.items(): + tracker[f"residual_threshold_v2_calibration_{key}_sha256"] = digest + if state == THRESHOLD_FROZEN: + tracker["residual_threshold_v2_threshold_frozen_sha256"] = outcome_sha + elif FAILURE_PATH.exists(): + tracker["residual_threshold_v2_calibration_failure_sha256"] = outcome_sha + text = json.dumps(tracker, indent=2, sort_keys=True, ensure_ascii=True) + "\n" + TRACKER.write_text(text, encoding="utf-8") + TRACKER.chmod(0o600) + if tracker["residual_threshold_state"] != "CALIBRATION_INSUFFICIENT_SUPPORT": + refuse("v1 execution state was overwritten") + return sha256_file(TRACKER) + + +def _finish(state: str, threshold_value, outcome_sha: str, hashes: dict) -> dict: + _assert_measurement_sealed() + _verify_sealed() + tracker_sha = _update_tracker(state, threshold_value, outcome_sha, hashes) + for path, expected in SEALED.items(): + if sha256_file(path) != expected: + refuse(f"sealed artifact changed after calibration: {path.name}") + if sha256_file(EVENTS) != EXPECTED_EVENTS or sha256_file(LEDGER_FILE) != EXPECTED_LEDGER: + refuse("events or ledger changed") + report = { + "determinism": "IDENTICAL", + "outcome_sha256": outcome_sha, + "state": state, + "threshold_value": threshold_value, + "tracker_sha256": tracker_sha, + } + print(json.dumps(report, indent=2, sort_keys=True)) + return report + + +def execute() -> dict: + for path in OUTPUTS: + if path.exists(): + refuse(f"calibration artifact already exists: {path.name}") + _verify_sealed() + _assert_measurement_sealed() + manifest = _read_jsonl(CALIBRATION_MANIFEST) + if len(manifest) != 200 or sha256_file(CALIBRATION_MANIFEST) != EXPECTED_CALIBRATION: + refuse("calibration manifest failed the pre-resolution check") + if sha256_file(MEASUREMENT_MANIFEST) != EXPECTED_MEASUREMENT: + refuse("measurement manifest failed the pre-resolution check") + sealed = _sealed_rows(manifest) + public_rows, model_rows, by_parent, pool_size = _resolve(sealed) + rendered_resolution = _render_jsonl(public_rows) + resolution_sha = sha256_bytes(rendered_resolution.encode("utf-8")) + summaries = _parent_summaries(sealed, by_parent) + if len(summaries) != 200: + refuse("resolution did not cover 200 calibration rows") + ready = sum(1 for row in summaries if row["row_resolution_status"] == "RESIDUAL_READY") + unknown = sum(1 for row in summaries if row["row_resolution_status"] == "UNKNOWN") + if ready + unknown != 200: + refuse("row resolution status left the frozen vocabulary") + resolution_receipt = { + "abstention_reasons_present": True, + "authorization": AUTHORIZATION, + "calibration_rows": 200, + "determinism": "IDENTICAL", + "extended_lesk_run": True, + "glossbert_revision": "0cc3b83af5496e27ebcc95ef0cf37ea0a9281a7a", + "glossbert_run": pool_size > 0, + "json_schema_document": None, + "json_schema_exists": False, + "measurement_glossbert_run": False, + "measurement_labels_loaded": False, + "measurement_minilm_run": False, + "measurement_resolved": False, + "measurement_touched": False, + "operator_labels_joined": False, + "operator_labels_loaded": False, + "parent_rows": summaries, + "residual_ready_rows": ready, + "resolution_passes": 2, + "resolution_sha256": resolution_sha, + "schema": "hyperlex.residual_threshold_v2_calibration_resolution_receipt.v1", + "tier3_pool": pool_size, + "unknown_rows": unknown, + } + _write_new(RESOLUTION_PATH, rendered_resolution) + _write_new(RESOLUTION_RECEIPT, _render_json(resolution_receipt)) + if sha256_file(RESOLUTION_PATH) != resolution_sha: + refuse("resolution hash drifted") + scores, nuisance = _score(sealed, by_parent, model_rows, resolution_sha) + scored_ready = sum(1 for row in scores if row["score_status"] == "SCORED") + if scored_ready != ready: + refuse("scored rows do not match residual-ready rows") + score_text = _render_jsonl(scores) + score_sha = sha256_bytes(score_text.encode("utf-8")) + score_receipt = { + "authorization": AUTHORIZATION, + "calibration_score_frozen": True, + "determinism": "IDENTICAL", + "extended_lesk_run": True, + "glossbert_run": pool_size > 0, + "integrated_resolution_sha256": resolution_sha, + "json_schema_document": None, + "json_schema_exists": False, + "measurement_glossbert_run": False, + "measurement_labels_loaded": False, + "measurement_minilm_run": False, + "measurement_resolved": False, + "measurement_scored": False, + "measurement_touched": False, + "minilm_encode": True, + "operator_labels_joined": False, + "operator_labels_loaded": False, + "ready_rows": scored_ready, + "residual_candidate_spec_sha256": EXPECTED_RESIDUAL_SPEC, + "residual_model_revision": "1110a243fdf4706b3f48f1d95db1a4f5529b4d41", + "schema": "hyperlex.residual_threshold_v2_calibration_score_receipt.v1", + "score_passes": 2, + "score_sha256": score_sha, + "selected_source": "none", + "unknown_rows": 200 - scored_ready, + } + _write_new(SCORES_PATH, score_text) + _write_new(SCORE_RECEIPT, _render_json(score_receipt)) + _assert_measurement_sealed() + if sha256_file(CALIBRATION_MANIFEST) != EXPECTED_CALIBRATION: + refuse("calibration manifest changed during scoring") + return _judge(nuisance) + + +def main() -> None: + execute() + + +if __name__ == "__main__": + main() From 4261701a65c314ee9764871ac5a4ec44bc593207 Mon Sep 17 00:00:00 2001 From: Daniel Meyer Date: Mon, 28 Sep 2026 16:34:34 -0700 Subject: [PATCH 25/42] feat(007): land SELECT residual and schedule work (24) --- .../residual_threshold_v1_calibration.py | 375 ++++++++++++++++++ 1 file changed, 375 insertions(+) create mode 100644 scripts/shadow/hyperlexical/residual_threshold_v1_calibration.py diff --git a/scripts/shadow/hyperlexical/residual_threshold_v1_calibration.py b/scripts/shadow/hyperlexical/residual_threshold_v1_calibration.py new file mode 100644 index 00000000..d2cd0307 --- /dev/null +++ b/scripts/shadow/hyperlexical/residual_threshold_v1_calibration.py @@ -0,0 +1,375 @@ +"""One blind calibration draw for the frozen residual threshold. + +The sample size is the historical positional prefix for this universe +(per cell = 2). It is not computed from operator labels. Operator labels +are read only after the score receipt is hashed. A failed gate is frozen. +This module does not redraw, and it does not draw a measurement surface. +""" + +from __future__ import annotations + +import hashlib +import json +import os +import sys +from collections import Counter +from pathlib import Path + +from hyperlexical.residual_threshold_v1 import ( + MIN_CALIBRATION_HIGH, + MIN_CALIBRATION_SECONDARY, + ALGORITHM_ID, +) + +LEDGER = Path("/home/morpheus/hlx-private/eval-reserve-20260926") +HYPERLEX = Path("/home/morpheus/Hyperlex") +SOURCE = LEDGER / "operator-review/HLX-EVAL-UNBIND-SEMANTIC-EVIDENCE-SOURCE-V1-001" +SENSE = LEDGER / "operator-review/HLX-EVAL-UNBIND-SENSE-SCREEN-V1-HYPOTHESIS-001" +WORDNET = LEDGER / "acquisition/sources/wordnet-3.0/wordnet" +TRAIN = Path("/home/morpheus/hlx-private/d1-spark-tree-20260924T213846Z/morph78-train-export.jsonl") +EVENTS = LEDGER / "events.jsonl" +LEDGER_FILE = LEDGER / "ledger.json" +TRACKER = SENSE / "HYPOTHESIS.json" +EVIDENCE = SENSE / "DEVELOPMENT_EVIDENCE.json" + +SAMPLING_RULE_ID = "positional_stratified_hash_prefix_v1" +PER_CELL = 2 +DRAW_SEED = None +ORDERING = "normalized_text_sha256 ascending" +STRATIFICATION = "source_pos x token_count" +REPLACEMENT = "without_replacement" +SOURCE_IDENTITY = "wordnet-3.0" +AUTHORIZATION = "RESIDUAL_CALIBRATION_SURFACE_DRAW_AUTHORIZATION" + +MANIFEST_FIELDS = ( + "calibration_row_id", + "normalized_text_sha256", + "surface", + "pos", + "pwn30_synset", + "frozen_gloss", + "source_identity", + "draw_order", + "draw_seed", + "sampling_rule_id", +) + +DRAW_RECEIPT = SOURCE / "RESIDUAL_THRESHOLD_V1_CALIBRATION_DRAW_RECEIPT.json" +MANIFEST_PATH = SOURCE / "RESIDUAL_THRESHOLD_V1_CALIBRATION_MANIFEST.jsonl" +ISOLATION_PATH = SOURCE / "RESIDUAL_THRESHOLD_V1_CALIBRATION_ISOLATION_REPORT.json" +RESOLUTION_PATH = SOURCE / "RESIDUAL_THRESHOLD_V1_CALIBRATION_RESOLUTION.jsonl" +SCORES_PATH = SOURCE / "RESIDUAL_THRESHOLD_V1_CALIBRATION_SCORES.jsonl" +SCORE_RECEIPT = SOURCE / "RESIDUAL_THRESHOLD_V1_CALIBRATION_SCORE_RECEIPT.json" +LABELS_PATH = SOURCE / "RESIDUAL_THRESHOLD_V1_CALIBRATION_LABELS.jsonl" +ANALYSIS_PATH = SOURCE / "RESIDUAL_THRESHOLD_V1_CALIBRATION_ANALYSIS.json" +SEARCH_PATH = SOURCE / "RESIDUAL_THRESHOLD_V1_CALIBRATION_THRESHOLD_SEARCH.json" +FROZEN_PATH = SOURCE / "RESIDUAL_THRESHOLD_V1_THRESHOLD_FROZEN.json" +FAILURE_PATH = SOURCE / "RESIDUAL_THRESHOLD_V1_CALIBRATION_FAILURE.json" + +PREREGISTRATION = { + SOURCE / "RESIDUAL_THRESHOLD_V1_PREREGISTRATION.json": "86b4cb5578fa63d3e8f4f2968ed0e32db5b5f571c9b2604e29597bce70fac5e0", + SOURCE / "RESIDUAL_THRESHOLD_V1_ACCEPTANCE.json": "99aa20bdb046a449571f043b8ddb6c246ed610fb7c7cfc454a020a6c9464da6e", + SOURCE / "RESIDUAL_THRESHOLD_V1_SELECTION_PROCEDURE.json": "afa2fe92e3f70756c6a6f5a2f44d1f1a82f291b58bef171db0db8cc39e13ed1a", + SOURCE / "RESIDUAL_THRESHOLD_V1_CALIBRATION_CONTRACT.json": "99c3dc10a5ea6b6990f350163ea975f99b9c1b895504c0260952d0b67a5bd323", + SOURCE / "RESIDUAL_THRESHOLD_V1_MEASUREMENT_CONTRACT.json": "3463caaa22f0fd45342ce9946e1669bb453f91a776a195b14d62a014f898d2c9", + SOURCE / "RESIDUAL_THRESHOLD_V1_ARTIFACT_SCHEMA.json": "87cea8897b709c00c5917f0a00499237f3e9c8923dee974f0184bc98881fa7b4", + SOURCE / "RESIDUAL_THRESHOLD_V1_SURFACE_ISOLATION_POLICY.json": "e24cccdd1a2db315531a98c7468c70b9751083bf2c0f3c4206cad1f6e51b3d26", +} +ANCESTORS = { + EVIDENCE: "0e9b3c1af9dd573bf6e2034640e468e8ab9074e1e76c90cef1f39f68d607bc03", + EVENTS: "96b74a92d44f1cf9fe152b18e5207176f161ba3bfce528dac38aa4571a742f9c", + LEDGER_FILE: "77e22433203879b252f7a9e309d2013d7550101d1c4a014b494d2c96df87d0e0", + SOURCE / "RESIDUAL_CANDIDATE_SPEC.json": "39c2914e32557ffe1a456a56f8742ea4fe8f1aaec1dc1da451656cd22f0db32d", + SOURCE / "RESIDUAL_DEVELOPMENT_SCORES.jsonl": "cea638679faeee1bc1c689823e7c0c08562c4d7ef1f8230bbbf4079239e7c3e7", + SOURCE / "INTEGRATED_CONSTITUENT_RESOLUTION_V1.jsonl": "0f5dafc3676a4071ce8c889e58958b90203589aa3e91d78111b4e3292bdd87fb", + SOURCE / "RESIDUAL_V1_MODEL_RESOLVED_REPLAY_SCORES.jsonl": "16c0a9eaa918ac4a6e8223cafcbf4b1918cb212769063e262cf29a279f1048f6", + SOURCE / "RESIDUAL_V1_MODEL_RESOLVED_REPLAY_RECEIPT.json": "675b1b8b8f1b1e6f19c7d320e7b8fe516eae92b60e966afbce407c1f0482e736", + SOURCE / "RESIDUAL_V1_MODEL_RESOLVED_DEVELOPMENT_ANALYSIS.json": "4367930648a68c2f84a1fd8e011fa07d9f3bf111303079f3ec07688f7d5425fb", + SOURCE / "RESIDUAL_V1_MODEL_RESOLVED_CONFOUND_ANALYSIS.json": "d0f2496ca7623050eb5519969abcf7c5e2d0e23e0c1961859c40cae4dcdb7021", + SOURCE / "RESIDUAL_V1_MODEL_RESOLVED_CANDIDATE_DECISION.json": "ed0296fe6888e7c9fe864a2c6c7ab6cecbd4f490d6b7d650e442dafc0bd0976d", + SOURCE / "MODEL_BASED_WSD_CANDIDATE_SPEC.json": "c861ff7fff11ae6a790531267229c18d6e6e0a171a9bf6c34cfb6f7e7b14498c", + SOURCE / "MODEL_BASED_WSD_RESOLUTION.jsonl": "ed945989cf4947ac84633ba2c4aa10c1ba381d2396da0b573a844f83ec367a18", + SOURCE / "MODEL_BASED_WSD_READINESS_PROJECTION.json": "c75834faf4a84d36e83246244e0aa7c6c7788c3a57cfdb7f77c7628a52023328", + SOURCE / "CONSTITUENT_SENSE_RESOLUTION_V1_SPEC.json": "176e6219ddc3127814a25d39ad26e3571817f7ea8323d685e081d2e0fd867acb", + SOURCE / "CONSTITUENT_SENSE_RESOLUTION_V1_REPLAY.jsonl": "a0c707ab55e02f627a698c33ddc0ca398e34aa0bafc19b13e72422bc26d97d0a", + SOURCE / "MAGPIE_CANDIDATE_DECISION.json": "6eaa968b6260946998dba13e5c423f178d3349cdfe06e5ea401717f5a9bcdd0d", + SOURCE / "KM_CANDIDATE_DECISION.json": "93a07e3c78b53a69965497410c34ddb52c2a5d3add3fb2f3cd3fd9ca84eb3d9f", + SOURCE / "SEMANTIC_RESIDUAL_V1_COVERAGE_LIMITATION.json": "fc8839c15a7638b2bfca1cf0548bfb4d5f433434bae0fea2944a528dd15d6142", + SENSE / "WORDNET_STRUCTURAL_SOURCE_LIMITATION.json": "3c05cd9d6301fab0791e31b542d767cc757307cf3e304065362b479cc40e964a", + SENSE / "CLASSIFICATION_PROCEDURE.v2.json": "3f4071640d0c9f29cf56f53969a88ec25c635444b87765e77e1b9158470e5662", + SOURCE / "HYPOTHESIS.json": "39127a810d38ede96d7947c33dbc3e5491c9e1cc9b3f76b1064d9e0dd04a7787", + TRACKER: "5dda813835ee11424f3d803f50ddc9eab5842cf0185f59ca396da207635a1daf", +} +OPERATOR_CLASSES = ("HIGH", "SECONDARY", "REJECT", "QUARANTINE") +JSON_SCHEMA_DOCUMENT = None + + +def sha256(path: Path) -> str: + return hashlib.sha256(path.read_bytes()).hexdigest() + + +def refuse(message: str) -> None: + raise SystemExit(message) + + +def write_json(path: Path, payload: dict) -> str: + if path.exists(): + refuse(f"refusing to rewrite {path.name}") + text = json.dumps(payload, indent=2, sort_keys=True, ensure_ascii=True) + "\n" + path.write_text(text, encoding="utf-8") + path.chmod(0o600) + return hashlib.sha256(text.encode("utf-8")).hexdigest() + + +def write_jsonl(path: Path, rows: list[dict]) -> str: + if path.exists(): + refuse(f"refusing to rewrite {path.name}") + text = "".join(json.dumps(row, sort_keys=True, ensure_ascii=True) + "\n" for row in rows) + path.write_text(text, encoding="utf-8") + path.chmod(0o600) + return hashlib.sha256(text.encode("utf-8")).hexdigest() + + +def read_jsonl(path: Path) -> list[dict]: + return [json.loads(line) for line in path.read_text(encoding="utf-8").splitlines() if line] + + +def verify_sealed(include_tracker: bool = True) -> None: + for path, expected in PREREGISTRATION.items(): + if sha256(path) != expected: + refuse(f"preregistration changed: {path.name}") + for path, expected in ANCESTORS.items(): + if path == TRACKER and not include_tracker: + continue + if sha256(path) != expected: + refuse(f"sealed ancestor changed: {path.name}") + + +def _identity_fences() -> dict: + from hyperlexical.heldout_census import normalize_group_text + from hyperlexical.holdout_guard import normalized_text_sha256 + from hyperlexical.unbind_screen_v4 import normalize_lexical + + evidence = json.loads(EVIDENCE.read_text(encoding="utf-8")) + rows = evidence["rows"] + if len(rows) != 225: + refuse("development evidence row count drifted") + hashes = set() + lexical = set() + grouped = set() + stored = set() + synsets = set() + for row in rows: + surface = str(row["surface"]) + digest = normalized_text_sha256(surface) + if digest != row["row_id"]: + refuse("development row_id is not normalized_text_sha256") + hashes.add(row["row_id"]) + lexical.add(normalize_lexical(surface)) + grouped.add(normalize_group_text(surface)) + stored.add(str(row["normalized_identity"])) + offset = str(row.get("synset_offset") or "") + if offset: + synsets.add(f"{row['synset_pos']}:{offset}") + magpie_hashes = set() + magpie_synsets = set() + for name in ("MAGPIE_SEMANTIC_EVIDENCE.jsonl", "KM_HYPERLEX_SEMANTIC_EVIDENCE.jsonl"): + for row in read_jsonl(SOURCE / name): + magpie_hashes.add(row["row_id"]) + for key in ("synset", "hyperlex_synset"): + value = row.get(key) + if value: + magpie_synsets.add(str(value)) + reserves = [ + path + for path in (LEDGER / "operator-review").rglob("*") + if path.is_file() and "measurement" in path.name.lower() and "reserve" in path.name.lower() + ] + if reserves: + refuse(f"unexpected measurement reserve: {reserves}") + return { + "development_hashes": hashes, + "development_lexical": lexical, + "development_grouped": grouped, + "development_stored_identity": stored, + "development_synsets": synsets, + "magpie_km_hashes": magpie_hashes, + "magpie_km_synsets": magpie_synsets, + "measurement_reserve_hashes": set(), + "measurement_reserve_files": 0, + } + + +def _bound_glosses(pending: list[tuple[str, str]]) -> dict[tuple[str, str], str]: + """One scan per POS. The gloss is the same first clause gloss_for stores.""" + from hyperlexical.unbind_screen_v3 import FILES, load_glosses + + found = {} + wanted: dict[str, set[str]] = {} + for pos, offset in pending: + wanted.setdefault(pos, set()).add(offset) + for pos, offsets in wanted.items(): + loaded = load_glosses(WORDNET / FILES[pos][1], offsets) + for offset, gloss in loaded.items(): + found[(pos, offset)] = gloss + return found + + +def draw_calibration() -> tuple[list[dict], dict]: + """Enumerate, fence, and draw. This function does not read operator labels.""" + from hyperlexical.clean_unbind import ( + WORDNET_LICENSE, + gate_rows, + load_jsonl, + read_wordnet_index, + rows_from_wordnet_atoms, + ) + from hyperlexical.heldout_census import normalize_group_text + from hyperlexical.holdout_guard import normalized_text_sha256 + from hyperlexical.identity_ledger import IdentityLedger + from hyperlexical.unbind_screen_v3 import load_indexes + from hyperlexical.unbind_screen_v4 import normalize_lexical + + fences = _identity_fences() + atoms = read_wordnet_index(WORDNET) + generated = rows_from_wordnet_atoms(atoms, license=WORDNET_LICENSE) + admissible, rejections, _account = gate_rows( + generated, + train_rows=load_jsonl(TRAIN), + ledger=IdentityLedger.load(LEDGER), + require_settlement=False, + ) + from hyperlexical.clean_unbind import reason_counts + + exclusion = Counter() + exclusion["wordnet_rows"] = len(generated) + exclusion["gate_rejected"] = len(rejections) + for reason, count in sorted(reason_counts(rejections).items()): + exclusion[f"gate_{reason}"] = count + positional = [] + for row in admissible: + if row.get("role_scheme") != "positional": + exclusion["not_positional"] += 1 + continue + positional.append(row) + exclusion["admissible_positional"] = len(positional) + kept = [] + magpie_overlap_with_development = 0 + for row in positional: + surface = str(row.get("text") or "") + digest = normalized_text_sha256(surface) + lexical = normalize_lexical(surface) + grouped = normalize_group_text(surface) + if digest in fences["magpie_km_hashes"] and digest in fences["development_hashes"]: + magpie_overlap_with_development += 1 + if digest in fences["development_hashes"]: + exclusion["development_row_id"] += 1 + continue + if ( + lexical in fences["development_lexical"] + or grouped in fences["development_grouped"] + or grouped in fences["development_stored_identity"] + or lexical in fences["development_stored_identity"] + ): + exclusion["development_normalized_text"] += 1 + continue + if digest in fences["magpie_km_hashes"] or grouped in fences["development_grouped"]: + exclusion["magpie_km_overlap"] += 1 + continue + if digest in fences["measurement_reserve_hashes"]: + exclusion["measurement_reserve"] += 1 + continue + kept.append(row) + best: dict[str, dict] = {} + for row in kept: + lexical = normalize_lexical(str(row.get("text") or "")) + digest = normalized_text_sha256(str(row.get("text") or "")) + current = best.get(lexical) + if current is None: + best[lexical] = row + continue + current_digest = normalized_text_sha256(str(current.get("text") or "")) + if digest < current_digest: + best[lexical] = row + exclusion["duplicate_normalized_identity"] += 1 + indexes = load_indexes(str(WORDNET)) + pending = [] + for row in best.values(): + surface = str(row["text"]) + pos = str(row["source_pos"]) + fillers = [str(tok) for tok in row.get("fillers") or []] + if " ".join(fillers) != surface: + refuse("positional tokens do not reconstruct the surface") + offset = (indexes.get(pos) or {}).get(surface.replace(" ", "_")) + if not offset: + exclusion["missing_or_unbound_synset"] += 1 + continue + pending.append((surface, pos, offset, fillers)) + glosses = _bound_glosses([(pos, offset) for _surface, pos, offset, _fillers in pending]) + synset_best: dict[str, dict] = {} + for surface, pos, offset, fillers in pending: + gloss = glosses.get((pos, offset), "") + synset = f"{pos}:{offset}" + if not gloss: + exclusion["missing_or_unbound_synset"] += 1 + continue + if synset in fences["development_synsets"] or synset in fences["magpie_km_synsets"]: + exclusion["development_synset"] += 1 + continue + item = { + "fillers": fillers, + "frozen_gloss": gloss, + "pos": pos, + "pwn30_synset": synset, + "surface": surface, + "normalized_text_sha256": normalized_text_sha256(surface), + } + current = synset_best.get(synset) + if current is None or item["normalized_text_sha256"] < current["normalized_text_sha256"]: + if current is not None: + exclusion["duplicate_synset"] += 1 + synset_best[synset] = item + else: + exclusion["duplicate_synset"] += 1 + bound_rows = list(synset_best.values()) + cells: dict[tuple[str, int], list[dict]] = {} + for item in bound_rows: + cells.setdefault((item["pos"], len(item["fillers"])), []).append(item) + picked = [] + strata = [] + for key in sorted(cells): + group = sorted(cells[key], key=lambda item: item["normalized_text_sha256"]) + taken = group[:PER_CELL] + picked.extend(taken) + strata.append( + { + "available": len(group), + "source_pos": key[0], + "taken": len(taken), + "token_count": key[1], + } + ) + if not picked: + refuse("calibration draw is empty") + manifest = [] + for draw_order, item in enumerate(picked, start=1): + manifest.append( + { + "calibration_row_id": item["normalized_text_sha256"], + "draw_order": draw_order, + "draw_seed": DRAW_SEED, + "frozen_gloss": item["frozen_gloss"], + "normalized_text_sha256": item["normalized_text_sha256"], + "pos": item["pos"], + "pwn30_synset": item["pwn30_synset"], + "sampling_rule_id": SAMPLING_RULE_ID, + "source_identity": SOURCE_IDENTITY, + "surface": item["surface"], + } + ) + accounting = { + "eligible_before_draw": len(bound_rows), + "exclusion_counts": dict(sorted(exclusion.items())), + "magpie_km_overlap_with_development_row_id": first_account_placeholder, + } + return manifest, accounting From de97a05457648b33994ee52bda787793bd00fb78 Mon Sep 17 00:00:00 2001 From: Daniel Meyer Date: Mon, 28 Sep 2026 16:37:57 -0700 Subject: [PATCH 26/42] feat(007): land SELECT residual and schedule work (25) --- .../residual_threshold_v1_calibration.py | 770 +++++++++++++++++- 1 file changed, 769 insertions(+), 1 deletion(-) diff --git a/scripts/shadow/hyperlexical/residual_threshold_v1_calibration.py b/scripts/shadow/hyperlexical/residual_threshold_v1_calibration.py index d2cd0307..95b37ccc 100644 --- a/scripts/shadow/hyperlexical/residual_threshold_v1_calibration.py +++ b/scripts/shadow/hyperlexical/residual_threshold_v1_calibration.py @@ -370,6 +370,774 @@ def draw_calibration() -> tuple[list[dict], dict]: accounting = { "eligible_before_draw": len(bound_rows), "exclusion_counts": dict(sorted(exclusion.items())), - "magpie_km_overlap_with_development_row_id": first_account_placeholder, + "magpie_km_overlap_with_development_row_id": magpie_overlap_with_development, + "measurement_reserve_files": 0, + "strata": strata, } return manifest, accounting + + +def _isolation(manifest: list[dict]) -> dict: + fences = _identity_fences() + from hyperlexical.heldout_census import normalize_group_text + from hyperlexical.unbind_screen_v4 import normalize_lexical + + hashes = [row["normalized_text_sha256"] for row in manifest] + synsets = [row["pwn30_synset"] for row in manifest] + overlap = { + "development_normalized_text": 0, + "development_row_id": len(set(hashes) & fences["development_hashes"]), + "development_synset": len(set(synsets) & fences["development_synsets"]), + "internal_duplicate_row_id": len(hashes) - len(set(hashes)), + "internal_duplicate_synset": len(synsets) - len(set(synsets)), + "magpie_km_row_id": len(set(hashes) & fences["magpie_km_hashes"]), + "magpie_km_synset": len(set(synsets) & fences["magpie_km_synsets"]), + "measurement_reserve": 0, + } + for row in manifest: + lexical = normalize_lexical(row["surface"]) + grouped = normalize_group_text(row["surface"]) + if ( + lexical in fences["development_lexical"] + or grouped in fences["development_grouped"] + or grouped in fences["development_stored_identity"] + ): + overlap["development_normalized_text"] += 1 + if set(row) != set(MANIFEST_FIELDS): + refuse("manifest row carries a field outside the pre-score contract") + if "operator_bucket" in row: + refuse("manifest contains an operator label") + overlap["total"] = sum(overlap.values()) + return overlap + + +def freeze_draw() -> dict: + if MANIFEST_PATH.exists() or DRAW_RECEIPT.exists() or ISOLATION_PATH.exists(): + refuse("calibration draw already exists; redraw is not authorized") + verify_sealed() + print("draw pass 1", file=sys.stderr, flush=True) + first_manifest, first_account = draw_calibration() + print("draw pass 2", file=sys.stderr, flush=True) + second_manifest, second_account = draw_calibration() + if first_manifest != second_manifest or first_account != second_account: + _write_failure_early("NOT_DETERMINISTIC", "draw_reconstruction") + from hyperlexical.unbind_screen_v3 import gloss_for + + for row in first_manifest: + bound_pos, gloss = gloss_for(row["surface"], row["pos"], str(WORDNET)) + if bound_pos != row["pos"] or gloss != row["frozen_gloss"]: + refuse(f"frozen gloss drifted from gloss_for: {row['surface']}") + overlap = _isolation(first_manifest) + if overlap["total"] != 0: + refuse(f"calibration surface leaks development material: {overlap}") + manifest_sha = write_jsonl(MANIFEST_PATH, first_manifest) + isolation = { + "admitted": 0, + "authorization": AUTHORIZATION, + "gold": 0, + "json_schema_document": JSON_SCHEMA_DOCUMENT, + "json_schema_exists": False, + "manifest_sha256": manifest_sha, + "measurement_surface_drawn": False, + "operator_labels_consulted": False, + "overlap": overlap, + "schema": "hyperlex.residual_threshold_v1_calibration_isolation.v1", + "scores_consulted": False, + "selected_source": "none", + "select_005_authorized": False, + "settled": 0, + } + isolation_sha = write_json(ISOLATION_PATH, isolation) + receipt = { + "admitted": 0, + "authorization": AUTHORIZATION, + "calibration_surface_drawn": True, + "calibration_surface_frozen": True, + "determinism": "IDENTICAL", + "draw_passes": 2, + "draw_seed": DRAW_SEED, + "eligible_universe_before_draw": first_account["eligible_before_draw"], + "exclusion_counts": first_account["exclusion_counts"], + "gold": 0, + "isolation_sha256": isolation_sha, + "json_schema_document": JSON_SCHEMA_DOCUMENT, + "json_schema_exists": False, + "magpie_km_overlap_with_development_row_id": first_account["magpie_km_overlap_with_development_row_id"], + "manifest_row_count": len(first_manifest), + "manifest_sha256": manifest_sha, + "measurement_reserve_files": 0, + "measurement_surface_drawn": False, + "operator_labels_consulted": False, + "ordering": ORDERING, + "per_cell": PER_CELL, + "redraw_authorized": False, + "replacement": REPLACEMENT, + "sample_size_source": "historical positional draw_measurement default; not derived from operator labels", + "sampling_rule_id": SAMPLING_RULE_ID, + "schema": "hyperlex.residual_threshold_v1_calibration_draw_receipt.v1", + "scores_consulted": False, + "select_005_authorized": False, + "selected_source": "none", + "settled": 0, + "strata": first_account["strata"], + "stratification": STRATIFICATION, + "threshold_frozen": False, + "threshold_value": None, + } + receipt_sha = write_json(DRAW_RECEIPT, receipt) + receipt["draw_receipt_sha256"] = receipt_sha + return receipt + + +def _write_failure_early(state: str, gate: str) -> None: + if FAILURE_PATH.exists(): + refuse(state) + payload = { + "failure_state": state, + "failed_gate": gate, + "json_schema_document": JSON_SCHEMA_DOCUMENT, + "observed_confounds": None, + "observed_direction": None, + "observed_support": None, + "observed_threshold_candidate_count": None, + "schema": "hyperlex.residual_threshold_v1_calibration_failure.v1", + "threshold_frozen": False, + "threshold_value": None, + } + write_json(FAILURE_PATH, payload) + refuse(state) + + +def _sealed_rows(manifest: list[dict]) -> list[dict]: + rows = [] + for row in manifest: + pos, offset = row["pwn30_synset"].split(":", 1) + rows.append( + { + "gloss": row["frozen_gloss"], + "pos": row["pos"], + "row_id": row["calibration_row_id"], + "sense_class": None, + "surface": row["surface"], + "synset_offset": offset, + "synset_pos": pos, + } + ) + return rows + + +def _tier3_pool(resolver_rows: list[dict], by_id: dict, exceptions: dict, sense_index: dict) -> list[dict]: + from hyperlexical.model_based_wsd_candidate_v1 import candidate_gloss_text, gloss_lemma, quoted_context + from hyperlexical.model_based_wsd_candidate_v1_replay import sense_keys_for + + pool = [] + for row in resolver_rows: + if row["constituent_index"] is None: + continue + if row["resolution_status"] != "AMBIGUOUS": + continue + if row["resolution_method"] != "EXTENDED_LESK_V1": + refuse("ambiguous constituent is not an extended lesk tie") + pairs = [] + for synset_id in row["candidate_synsets"]: + record = by_id.get(synset_id) + if record is None: + refuse(f"missing candidate synset {synset_id}") + lemma = gloss_lemma(record["lemmas"], row["constituent_surface"], exceptions) + if lemma is None: + refuse(f"candidate lemma missing for {synset_id}") + pairs.append( + { + "gloss_text": candidate_gloss_text(lemma, record["gloss_first"]), + "sense_keys": sense_keys_for(synset_id, row["constituent_surface"], exceptions, sense_index), + "synset": synset_id, + } + ) + pool.append( + { + "candidate_pwn30_synsets": list(row["candidate_synsets"]), + "candidate_sense_keys": {item["synset"]: list(item["sense_keys"]) for item in pairs}, + "constituent_index": row["constituent_index"], + "constituent_pos": row["constituent_pos"], + "constituent_surface": row["constituent_surface"], + "context": quoted_context(row["parent_surface"], row["constituent_index"], row["constituent_surface"]), + "pairs": pairs, + "parent_row_id": row["parent_row_id"], + "parent_surface": row["parent_surface"], + "parent_synset": row["parent_synset"], + "prior_lesk_margin": row["margin"], + "prior_lesk_second_score": row["second_score"], + "prior_lesk_top_score": row["top_score"], + "prior_resolution_status": "AMBIGUOUS", + } + ) + return pool + + +def resolve_and_score() -> dict: + if not MANIFEST_PATH.exists() or not DRAW_RECEIPT.exists(): + refuse("manifest was not frozen before resolution") + if SCORES_PATH.exists() or SCORE_RECEIPT.exists(): + refuse("scores already exist") + verify_sealed() + manifest = read_jsonl(MANIFEST_PATH) + if sha256(MANIFEST_PATH) != json.loads(DRAW_RECEIPT.read_text(encoding="utf-8"))["manifest_sha256"]: + refuse("manifest hash does not match the draw receipt") + from hyperlexical.constituent_sense_resolution_v1_replay import ( + PROCEDURE_PATH, + SPEC_PATH, + build_catalog, + resolve_once, + ) + from hyperlexical.model_based_wsd_candidate_v1_replay import ( + import_runtime, + infer_pass, + load_sense_index, + resolution_rows, + ) + from hyperlexical.residual_model_resolved_replay_v1 import TIER3_GLOSSBERT + from hyperlexical.residual_model_resolved_replay_v1_replay import ( + RESIDUAL_SPEC_SHA, + apply_overflow, + assemble_scores, + build_integrated, + encode_needed, + prepare_jobs, + public_integrated, + score_jobs, + ) + from hyperlexical.semantic_compositionality_residual_replay import build_indexes, load_encoder + from hyperlexical.unbind_sense_screen_v1 import load_exceptions + + sealed = _sealed_rows(manifest) + print("resolver catalog", file=sys.stderr, flush=True) + by_id, index = build_catalog() + exceptions = load_exceptions(WORDNET) + spec_sha = sha256(SPEC_PATH) + procedure_sha = sha256(PROCEDURE_PATH) + wsd_spec_sha = sha256(SOURCE / "MODEL_BASED_WSD_CANDIDATE_SPEC.json") + print("resolver pass 1", file=sys.stderr, flush=True) + resolved_first = resolve_once(sealed, by_id, index, exceptions, spec_sha, procedure_sha) + print("resolver pass 2", file=sys.stderr, flush=True) + resolved_second = resolve_once(sealed, by_id, index, exceptions, spec_sha, procedure_sha) + if resolved_first != resolved_second: + _write_failure_early("NOT_DETERMINISTIC", "integrated_resolution") + sense_index = load_sense_index(WORDNET / "index.sense") + pool_first = _tier3_pool(resolved_first, by_id, exceptions, sense_index) + pool_second = _tier3_pool(resolved_second, by_id, exceptions, sense_index) + if pool_first != pool_second: + _write_failure_early("NOT_DETERMINISTIC", "tier3_pool") + model_rows: list[dict] = [] + if pool_first: + print(f"glossbert pool {len(pool_first)}", file=sys.stderr, flush=True) + torch, tokenizer, model = import_runtime() + raw_first = infer_pass(torch, tokenizer, model, pool_first, wsd_spec_sha) + raw_second = infer_pass(torch, tokenizer, model, pool_second, wsd_spec_sha) + if raw_first != raw_second: + _write_failure_early("NOT_DETERMINISTIC", "glossbert_scores") + model_first = resolution_rows(pool_first, raw_first) + model_second = resolution_rows(pool_second, raw_second) + if model_first != model_second: + _write_failure_early("NOT_DETERMINISTIC", "glossbert_resolution") + model_rows = model_first + integrated_first, by_parent_first = build_integrated(resolved_first, model_rows, exceptions, sense_index) + integrated_second, _by_parent_second = build_integrated(resolved_second, model_rows, exceptions, sense_index) + if integrated_first != integrated_second: + _write_failure_early("NOT_DETERMINISTIC", "integrated_resolution") + public_rows = [public_integrated(item) for item in integrated_first] + if RESOLUTION_PATH.exists(): + if read_jsonl(RESOLUTION_PATH) != public_rows: + _write_failure_early("NOT_DETERMINISTIC", "integrated_resolution") + resolution_sha = sha256(RESOLUTION_PATH) + else: + resolution_sha = write_jsonl(RESOLUTION_PATH, public_rows) + residual_by_id, _residual_index = build_indexes() + # prepare_jobs uses constituent_representation from the replay module, which + # closes over the residual index built above. Rebind by calling prepare_jobs + # after the replay's own index loader is the frozen path: prepare_jobs calls + # constituent_representation, and that function expects by_id from build_indexes. + jobs, unknown = prepare_jobs(sealed, by_parent_first, residual_by_id, exceptions, { + (row["parent_row_id"], row["constituent_index"]): row for row in model_rows + }) + encoder = load_encoder() + kept, overflow = apply_overflow(encoder, jobs) + print("residual encode pass 1", file=sys.stderr, flush=True) + vectors_first = encode_needed(encoder, kept) + scored_first = score_jobs(kept, vectors_first, resolution_sha) + print("residual encode pass 2", file=sys.stderr, flush=True) + vectors_second = encode_needed(encoder, kept) + scored_second = score_jobs(kept, vectors_second, resolution_sha) + if vectors_first != vectors_second or scored_first != scored_second: + RESOLUTION_PATH.unlink() + _write_failure_early("NOT_DETERMINISTIC", "residual_scores") + scores = assemble_scores(sealed, scored_first, unknown, overflow) + for row in scores: + if row["score_status"] == "SCORED" and "constituent_resolution_tiers" in row: + row["uses_tier3"] = TIER3_GLOSSBERT in row["constituent_resolution_tiers"] + if any(row.get("operator_bucket") for row in scores): + refuse("score artifact contains an operator label") + score_sha = write_jsonl(SCORES_PATH, scores) + receipt = { + "admitted": 0, + "determinism": "IDENTICAL", + "gold": 0, + "integrated_resolution_sha256": resolution_sha, + "json_schema_document": JSON_SCHEMA_DOCUMENT, + "json_schema_exists": False, + "manifest_sha256": sha256(MANIFEST_PATH), + "operator_labels_joined": False, + "ready_rows": sum(1 for row in scores if row["score_status"] == "SCORED"), + "residual_candidate_spec_sha256": RESIDUAL_SPEC_SHA, + "schema": "hyperlex.residual_threshold_v1_calibration_score_receipt.v1", + "score_passes": 2, + "score_sha256": score_sha, + "selected_source": "none", + "settled": 0, + "tier3_pool": len(pool_first), + "unknown_rows": sum(1 for row in scores if row["score_status"] != "SCORED"), + } + receipt_sha = write_json(SCORE_RECEIPT, receipt) + if json.loads(SCORE_RECEIPT.read_text(encoding="utf-8"))["operator_labels_joined"] is not False: + refuse("score receipt joined labels early") + receipt["score_receipt_sha256"] = receipt_sha + return receipt + + +def _label_index() -> dict[str, str]: + found: dict[str, str] = {} + + def add(row_id: str, bucket: str) -> None: + prior = found.get(row_id) + if prior is not None and prior != bucket: + refuse(f"operator labels disagree for {row_id}") + found[row_id] = bucket + + evidence = json.loads(EVIDENCE.read_text(encoding="utf-8")) + for row in evidence["rows"]: + add(row["row_id"], row["operator_bucket"]) + root = LEDGER / "operator-review" + for path in sorted(root.rglob("*labels*.jsonl")): + for row in read_jsonl(path): + if "operator_bucket" not in row or "row_id" not in row: + continue + add(row["row_id"], row["operator_bucket"]) + return found + + +def _support(labels: list[dict]) -> dict: + ready = Counter() + unknown = Counter() + unlabeled = {"SCORED": 0, "UNKNOWN": 0} + for row in labels: + bucket = row["operator_bucket"] + status = row["score_status"] + if bucket is None: + unlabeled[status] += 1 + continue + if status == "SCORED": + ready[bucket] += 1 + else: + unknown[bucket] += 1 + return { + "ready": {name: ready[name] for name in OPERATOR_CLASSES}, + "unlabeled_scored": unlabeled["SCORED"], + "unlabeled_unknown": unlabeled["UNKNOWN"], + "unknown_by_class": {name: unknown[name] for name in OPERATOR_CLASSES}, + } + + +def join_and_judge() -> dict: + if not SCORE_RECEIPT.exists() or not SCORES_PATH.exists(): + refuse("scores were not frozen before the label join") + if LABELS_PATH.exists() or ANALYSIS_PATH.exists() or SEARCH_PATH.exists(): + refuse("label join artifacts already exist") + if FROZEN_PATH.exists() or FAILURE_PATH.exists(): + refuse("threshold outcome already exists") + receipt = json.loads(SCORE_RECEIPT.read_text(encoding="utf-8")) + if receipt["operator_labels_joined"] is not False: + refuse("score receipt does not record a pre-label freeze") + if sha256(SCORES_PATH) != receipt["score_sha256"]: + refuse("score hash drifted before the label join") + verify_sealed() + labels_by_id = _label_index() + scores = read_jsonl(SCORES_PATH) + manifest = {row["calibration_row_id"]: row for row in read_jsonl(MANIFEST_PATH)} + joined = [] + for score in scores: + row_id = score["row_id"] + manifest_row = manifest[row_id] + bucket = labels_by_id.get(row_id) + joined.append( + { + "calibration_row_id": row_id, + "development_row": False, + "draw_order": manifest_row["draw_order"], + "operator_bucket": bucket, + "operator_label_present": bucket is not None, + "pos": score.get("pos", manifest_row["pos"]), + "residual_score": score.get("residual_score"), + "score_status": score["score_status"], + "surface": manifest_row["surface"], + "uses_tier3": score.get("uses_tier3"), + } + ) + label_rows = [ + { + "calibration_row_id": row["calibration_row_id"], + "operator_bucket": row["operator_bucket"], + "operator_label_present": row["operator_label_present"], + "score_status": row["score_status"], + } + for row in joined + ] + label_sha = write_jsonl(LABELS_PATH, label_rows) + support = _support(joined) + ready_high = support["ready"]["HIGH"] + ready_secondary = support["ready"]["SECONDARY"] + analysis = { + "admitted": 0, + "confound_result": "NOT_RUN", + "direction_result": "NOT_RUN", + "gold": 0, + "json_schema_document": JSON_SCHEMA_DOCUMENT, + "json_schema_exists": False, + "label_sha256": label_sha, + "measurement_surface_drawn": False, + "schema": "hyperlex.residual_threshold_v1_calibration_analysis.v1", + "score_receipt_sha256": sha256(SCORE_RECEIPT), + "score_sha256": sha256(SCORES_PATH), + "select_005_authorized": False, + "selected_source": "none", + "settled": 0, + "support": support, + "support_gate": { + "minimum_ready_high": MIN_CALIBRATION_HIGH, + "minimum_ready_secondary": MIN_CALIBRATION_SECONDARY, + "passed": ready_high >= MIN_CALIBRATION_HIGH and ready_secondary >= MIN_CALIBRATION_SECONDARY, + }, + } + if not analysis["support_gate"]["passed"]: + analysis["calibration_state"] = "CALIBRATION_INSUFFICIENT_SUPPORT" + analysis_sha = write_json(ANALYSIS_PATH, analysis) + search = { + "candidate_count": None, + "candidates_evaluated": False, + "gate_pass_counts": None, + "json_schema_document": JSON_SCHEMA_DOCUMENT, + "reason": "CALIBRATION_INSUFFICIENT_SUPPORT", + "schema": "hyperlex.residual_threshold_v1_calibration_threshold_search.v1", + "search_executed": False, + "selected_threshold": None, + } + search_sha = write_json(SEARCH_PATH, search) + failure = { + "analysis_sha256": analysis_sha, + "failure_state": "CALIBRATION_INSUFFICIENT_SUPPORT", + "failed_gate": "support", + "json_schema_document": JSON_SCHEMA_DOCUMENT, + "json_schema_exists": False, + "label_sha256": label_sha, + "observed_confounds": None, + "observed_direction": None, + "observed_support": support, + "observed_threshold_candidate_count": None, + "schema": "hyperlex.residual_threshold_v1_calibration_failure.v1", + "search_sha256": search_sha, + "threshold_frozen": False, + "threshold_value": None, + } + failure_sha = write_json(FAILURE_PATH, failure) + return _finish_tracker(failure["failure_state"], None, failure_sha, { + "analysis": analysis_sha, + "failure": failure_sha, + "labels": label_sha, + "search": search_sha, + }) + return _select(joined, analysis, label_sha) + + +def _select(joined: list[dict], analysis: dict, label_sha: str) -> dict: + from hyperlexical.residual_threshold_v1 import select_threshold + + selectable = [] + for row in joined: + if row["operator_bucket"] not in OPERATOR_CLASSES: + continue + if row["uses_tier3"] is None or row["pos"] is None: + refuse("labeled calibration row is missing tier or pos provenance") + selectable.append( + { + "development_row": False, + "operator_bucket": row["operator_bucket"], + "pos": row["pos"], + "residual_score": row["residual_score"], + "score_status": row["score_status"], + "uses_tier3": row["uses_tier3"], + } + ) + print("threshold selection pass 1", file=sys.stderr, flush=True) + first = select_threshold(selectable) + print("threshold selection pass 2", file=sys.stderr, flush=True) + second = select_threshold(selectable) + if first != second: + analysis["calibration_state"] = "NOT_DETERMINISTIC" + analysis_sha = write_json(ANALYSIS_PATH, analysis) + failure = { + "analysis_sha256": analysis_sha, + "failure_state": "NOT_DETERMINISTIC", + "failed_gate": "threshold_selection", + "json_schema_document": JSON_SCHEMA_DOCUMENT, + "observed_confounds": None, + "observed_direction": None, + "observed_support": analysis["support"], + "observed_threshold_candidate_count": None, + "schema": "hyperlex.residual_threshold_v1_calibration_failure.v1", + "threshold_frozen": False, + "threshold_value": None, + } + failure_sha = write_json(FAILURE_PATH, failure) + return _finish_tracker("NOT_DETERMINISTIC", None, failure_sha, {"analysis": analysis_sha, "failure": failure_sha, "labels": label_sha}) + state = first["calibration_state"] + analysis["calibration_state"] = state + analysis["direction_result"] = first.get("direction", "NOT_RUN") + analysis["confound_result"] = first.get("confound", "NOT_RUN") + analysis["selection"] = first + analysis_sha = write_json(ANALYSIS_PATH, analysis) + search, search_sha = _search_artifact(selectable, first) + if state != "THRESHOLD_FROZEN": + failure = { + "analysis_sha256": analysis_sha, + "failure_state": state, + "failed_gate": _failed_gate(state), + "json_schema_document": JSON_SCHEMA_DOCUMENT, + "json_schema_exists": False, + "label_sha256": label_sha, + "observed_confounds": first.get("confound"), + "observed_direction": first.get("direction"), + "observed_support": analysis["support"], + "observed_threshold_candidate_count": search["candidate_count"], + "schema": "hyperlex.residual_threshold_v1_calibration_failure.v1", + "search_sha256": search_sha, + "threshold_frozen": False, + "threshold_value": None, + } + failure_sha = write_json(FAILURE_PATH, failure) + return _finish_tracker(state, None, failure_sha, { + "analysis": analysis_sha, + "failure": failure_sha, + "labels": label_sha, + "search": search_sha, + }) + metrics = first["selection_metrics"] + frozen = { + "algorithm_id": ALGORITHM_ID, + "calibration_label_hash": label_sha, + "calibration_manifest_hash": sha256(MANIFEST_PATH), + "calibration_resolution_hash": sha256(RESOLUTION_PATH), + "calibration_score_hash": sha256(SCORES_PATH), + "confound_result": first.get("confound"), + "direction_result": first.get("direction"), + "false_high": metrics["false_high"], + "high_recall": metrics["high_recall"], + "json_schema_document": JSON_SCHEMA_DOCUMENT, + "json_schema_exists": False, + "leave_one_out_result": "PASS", + "measurement_surface_drawn": False, + "precision": metrics["precision"], + "predicted_yes_support": metrics["predicted_yes_support"], + "ready_quarantine_yes": metrics["quarantine_predicted_yes"], + "ready_reject_yes": metrics["reject_predicted_yes"], + "residual_candidate_spec_hash": json.loads(SCORE_RECEIPT.read_text(encoding="utf-8"))["residual_candidate_spec_sha256"], + "resolver_stack_hashes": { + "constituent_resolution_replay_sha256": ANCESTORS[SOURCE / "CONSTITUENT_SENSE_RESOLUTION_V1_REPLAY.jsonl"], + "constituent_resolution_spec_sha256": ANCESTORS[SOURCE / "CONSTITUENT_SENSE_RESOLUTION_V1_SPEC.json"], + "glossbert_resolution_sha256": ANCESTORS[SOURCE / "MODEL_BASED_WSD_RESOLUTION.jsonl"], + "glossbert_spec_sha256": ANCESTORS[SOURCE / "MODEL_BASED_WSD_CANDIDATE_SPEC.json"], + }, + "runtime_integration": False, + "schema": "hyperlex.residual_threshold_v1_threshold_frozen.v1", + "select_005_authorized": False, + "selected_source": "none", + "selection_algorithm_id": ALGORITHM_ID, + "selection_receipt": first, + "threshold_frozen": True, + "threshold_id": "T_HIGH", + "threshold_value": first["threshold_value"], + "true_high": metrics["true_high"], + "wilson_lower": metrics["precision_interval"][0], + "wilson_upper": metrics["precision_interval"][1], + } + frozen_sha = write_json(FROZEN_PATH, frozen) + return _finish_tracker("THRESHOLD_FROZEN", first["threshold_value"], frozen_sha, { + "analysis": analysis_sha, + "labels": label_sha, + "search": search_sha, + "threshold": frozen_sha, + }) + + +def _failed_gate(state: str) -> str: + return { + "CALIBRATION_INSUFFICIENT_SUPPORT": "support", + "NO_DIRECTIONAL_SIGNAL": "direction", + "CALIBRATION_CONFOUND_REVIEW": "confound", + "NO_THRESHOLD_PASSES_PRECISION_GATE": "precision_gate", + "NOT_DETERMINISTIC": "determinism", + }[state] + + +def _search_artifact(selectable: list[dict], selection: dict) -> tuple[dict, str]: + from decimal import Decimal + + from hyperlexical.residual_threshold_v1 import ( + MIN_PREDICTED_YES, + PRECISION_FLOOR, + WILSON_LOWER_FLOOR, + _candidate_thresholds, + _high_recall, + _passes_candidate, + _precision_terms, + _ready_rows, + _safety_counts, + _stable, + precision_gates, + wilson_interval, + ) + + if selection["calibration_state"] in {"CALIBRATION_INSUFFICIENT_SUPPORT", "NO_DIRECTIONAL_SIGNAL", "CALIBRATION_CONFOUND_REVIEW"}: + payload = { + "candidate_count": None, + "candidates_evaluated": False, + "gate_pass_counts": None, + "json_schema_document": JSON_SCHEMA_DOCUMENT, + "reason": selection["calibration_state"], + "schema": "hyperlex.residual_threshold_v1_calibration_threshold_search.v1", + "search_executed": False, + "selected_threshold": None, + } + return payload, write_json(SEARCH_PATH, payload) + ready = _ready_rows(selectable) + comparison = [row for row in ready if row["operator_bucket"] in {"HIGH", "SECONDARY"}] + candidates = _candidate_thresholds(ready) + counts = Counter() + passing = [] + for threshold in candidates: + true_high, support = _precision_terms(comparison, threshold) + precision_ok = precision_gates( + true_high, + support, + minimum_yes=MIN_PREDICTED_YES, + precision_floor=PRECISION_FLOOR, + wilson_floor=WILSON_LOWER_FLOOR, + ) + safety = _safety_counts(ready, threshold) + reject_ok = safety["reject_predicted_yes"] == 0 + quarantine_ok = safety["quarantine_predicted_yes"] == 0 + stable = _stable( + comparison, + threshold, + minimum_yes=MIN_PREDICTED_YES, + precision_floor=PRECISION_FLOOR, + wilson_floor=WILSON_LOWER_FLOOR, + ) + interval = wilson_interval(true_high, support) + wilson_ok = interval is not None and interval[0] >= WILSON_LOWER_FLOOR + support_ok = support >= MIN_PREDICTED_YES + counts["candidates"] += 1 + counts["predicted_yes_support"] += int(support_ok) + counts["precision"] += int(precision_ok and support_ok) + counts["wilson_lower"] += int(wilson_ok) + counts["leave_one_out"] += int(stable) + counts["reject_veto"] += int(reject_ok) + counts["quarantine_veto"] += int(quarantine_ok) + if _passes_candidate(ready, comparison, threshold): + counts["all_gates"] += 1 + passing.append((str(_high_recall(comparison, threshold)), threshold)) + selected = selection.get("threshold_value") + if passing: + best = max(passing, key=lambda item: (Decimal(item[0]), Decimal(item[1]))) + if selected != best[1]: + refuse("threshold search disagrees with select_threshold") + elif selected is not None: + refuse("select_threshold returned a value with no passing candidate") + payload = { + "candidate_count": len(candidates), + "candidates_evaluated": True, + "gate_pass_counts": dict(sorted(counts.items())), + "json_schema_document": JSON_SCHEMA_DOCUMENT, + "schema": "hyperlex.residual_threshold_v1_calibration_threshold_search.v1", + "search_executed": True, + "selected_threshold": selected, + "selection_algorithm_id": ALGORITHM_ID, + } + return payload, write_json(SEARCH_PATH, payload) + + +def _finish_tracker(state: str, threshold_value, outcome_sha: str, hashes: dict) -> dict: + verify_sealed() + tracker = json.loads(TRACKER.read_text(encoding="utf-8")) + prior = sha256(TRACKER) + if prior != ANCESTORS[TRACKER]: + refuse("tracker hash drifted before the calibration update") + tracker["previous_tracker_sha256"] = prior + tracker["residual_threshold_state"] = state + tracker["residual_threshold_value"] = threshold_value + tracker["residual_threshold_frozen"] = state == "THRESHOLD_FROZEN" + tracker["residual_threshold_calibration_surface_drawn"] = True + tracker["residual_threshold_calibration_surface_frozen"] = True + tracker["residual_threshold_measurement_surface_drawn"] = False + tracker["residual_threshold_redraw_authorized"] = False + tracker["measurement_sample_drawn"] = False + tracker["measurement_eligible"] = False + tracker["selected_source"] = "none" + tracker["select_authorized"] = False + tracker["admitted"] = 0 + tracker["settled"] = 0 + tracker["gold"] = 0 + tracker["next_legal_transition"] = "NONE" + tracker["next_transition_authorized"] = False + tracker["residual_threshold_json_schema_document"] = None + tracker["residual_threshold_calibration_draw_receipt_sha256"] = sha256(DRAW_RECEIPT) + tracker["residual_threshold_calibration_manifest_sha256"] = sha256(MANIFEST_PATH) + tracker["residual_threshold_calibration_isolation_sha256"] = sha256(ISOLATION_PATH) + tracker["residual_threshold_calibration_resolution_sha256"] = sha256(RESOLUTION_PATH) + tracker["residual_threshold_calibration_scores_sha256"] = sha256(SCORES_PATH) + tracker["residual_threshold_calibration_score_receipt_sha256"] = sha256(SCORE_RECEIPT) + tracker["residual_threshold_calibration_labels_sha256"] = hashes["labels"] + tracker["residual_threshold_calibration_analysis_sha256"] = hashes["analysis"] + if "search" in hashes: + tracker["residual_threshold_calibration_search_sha256"] = hashes["search"] + if state == "THRESHOLD_FROZEN": + tracker["residual_threshold_frozen_sha256"] = outcome_sha + else: + tracker["residual_threshold_calibration_failure_sha256"] = outcome_sha + text = json.dumps(tracker, indent=2, sort_keys=True, ensure_ascii=True) + "\n" + TRACKER.write_text(text, encoding="utf-8") + TRACKER.chmod(0o600) + verify_sealed(include_tracker=False) + if sha256(EVENTS) != ANCESTORS[EVENTS] or sha256(LEDGER_FILE) != ANCESTORS[LEDGER_FILE]: + refuse("events or ledger changed") + report = { + "determinism": "IDENTICAL", + "hashes": {key: value for key, value in hashes.items()}, + "outcome_sha256": outcome_sha, + "state": state, + "threshold_value": threshold_value, + "tracker_sha256": sha256(TRACKER), + } + print(json.dumps(report, indent=2, sort_keys=True)) + return report + + +def main() -> None: + os.umask(0o077) + if not MANIFEST_PATH.exists(): + freeze_draw() + if not SCORES_PATH.exists(): + resolve_and_score() + if not FAILURE_PATH.exists() and not FROZEN_PATH.exists(): + join_and_judge() + + +if __name__ == "__main__": + main() From 738dcbca529ac3befc55abbf02318ff3928add49 Mon Sep 17 00:00:00 2001 From: Daniel Meyer Date: Mon, 28 Sep 2026 16:41:33 -0700 Subject: [PATCH 27/42] feat(007): stage evaluation-reserve chunk (00) --- .land-chunks/part-00 | 297 +++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 297 insertions(+) create mode 100644 .land-chunks/part-00 diff --git a/.land-chunks/part-00 b/.land-chunks/part-00 new file mode 100644 index 00000000..e343906c --- /dev/null +++ b/.land-chunks/part-00 @@ -0,0 +1,297 @@ +# Evaluation reserve (GEN-0) + +Text identity is `hyperlexical.holdout_guard.normalized_text_sha256`. +That is SHA-256 of `heldout_census.normalize_group_text`: NFKC, casefold, URL strip, non-alphanumeric to space, whitespace collapse. Empty normalized text still hashes. No second normalizer is an identity. `soft_ceiling.clean_surface` remains the unbind-clean slice predicate only. + +Row ids are historical labels. One text identity may carry many row ids. Contamination, spending, abandonment, and reserve membership attach to the text hash. + +## Exhaustion + +On 2026-09-26 the live store (`01c1b38fd6e926962e47a520228e884ae7d99ea0ad6bbe188dc9980064254ac2`, 5005 rows) had **zero** rows eligible for a fresh holdout after split=test, the pinned morph78 export (`64b7d3dede25047cb6dd2e5b663f7fa72946ec82ac1a8816ae34622d1aaac430`, 9150 rows), spent v2, rc1 unbind rows, and the abandoned SELECT-001 / SELECT-002 holdouts. The 440 `split=test` rows are also training-consumed under the canonical hash, so dropping the split gate does not open a fresh classify set. Classify OBSERVED and classify non-none in that eligible universe are 0. + +SELECT-001 is `CLOSED_AT_LAUNCH_GATE`. SELECT-002 is `EXECUTION_INVALID` (0 epochs, 0 gradient steps). Both holdouts are `UNSCORED_ABANDONED`: never scored, not reusable, not `SCORED_SPENT`. Manifest bytes stay as sealed. Do not reopen either experiment. + +## Lifecycle + +New text is routed **before** training exposure. Records are append-only. Flags that mean consumed, spent, or abandoned only turn on. + +```text +NEW + | + +--> TRAIN_CANDIDATE + | | + | +--> TRAIN_CONSUMED + | + +--> EVAL_RESERVE + | + +--> EVAL_BOUND + | + +--> EVAL_SPENT + | + +--> EVAL_ABANDONED +``` + +`AVAILABLE` is catalogued text with none of those flags. It may still be routed to `TRAIN_CANDIDATE` or `EVAL_RESERVE`. + +Forbidden without a governed generation reset: + +- `EVAL_RESERVE` or `EVAL_BOUND` later entering training +- training-consumed text later used as unseen evaluation +- spent or abandoned text returning to the fresh reserve + +A generation-reset **request** can be recorded. It does not clear flags and it does not create a new baseline. GEN-1 is not opened by this policy. + +## Assignment + +Policy id: `hyperlex.eval_reserve.v1`. Fixed before any candidate output is observed. Model scores, errors, and predictions are refused as admission inputs. + +Within a batch, identities are ordered by `sha256(GEN-0 | batch_id | normalized_text_sha256)`. A new identity is `EVAL_RESERVE` when it still fills at least one open required slice. Otherwise it is `TRAIN_CANDIDATE`. Quotas are unique text identities, not raw rows. A second row id on an existing hash does not increase the evaluation sample size. A new hash that reuses an old row id stays a different identity and is reported as a collision. + +Required slices, because checkpoint selection needs each of them: + +- `classify` (classification accuracy) +- `classify_observed` (OBSERVED-label accuracy) +- `classify_non_none` (`classify_macro_f1_nonnone`) +- `unbind_clean` (unbind clean exact) + +## Coverage classes + +| Claim | Class | +| --- | --- | +| Canonical text hash is the contamination identity | CANONICAL_REQUIREMENT | +| Fresh evaluation text is disjoint from training, spent, and abandoned text | CANONICAL_REQUIREMENT | +| Each required slice is represented before SELECT-003 can be drafted | CANONICAL_REQUIREMENT | +| Statistical minimum n for those metrics | NOT_COMPUTABLE | +| v2 `DRAW_READY_MIN` 470 | HISTORICAL_PRECEDENT (unbind census target; PR #113 sets no v2 size) | +| Name-gate 2500 | CANONICAL_REQUIREMENT for the name gate, not for this holdout | +| rc1 classify_clean 444 / OBSERVED 72 / unbind_clean 306 | HISTORICAL_PRECEDENT (spent test) | +| SELECT-002 eligibility 606 classify / 287 OBSERVED / 604 non-none / 250 clean unbind | HISTORICAL_PRECEDENT and the PLANNING_TARGET | + +The planning target restores that SELECT-002 shape. It is not a validity theorem. Gap versus a statistical minimum is `NOT_COMPUTABLE`. + +## Admission + +```sh +PYTHONPATH=scripts/shadow python -m hyperlexical.identity_ledger admit \ + --ledger /path/to/ledger --rows new.jsonl --batch-id BATCH --source acquire +``` + +A row is accepted only when its canonical hash is absent from `TRAIN_CONSUMED`, `EVAL_SPENT`, `EVAL_ABANDONED`, and the existing reserve. The command reports raw rows and unique canonical text identities. Reserve rows must not be used for training, checkpoint selection, hyperparameter tuning, candidate-specific error review, or repeated scoring. Aggregate census counts are allowed. When `HLX_EVAL_RESERVE_LEDGER` is set, the trainer refuses if a loaded training row is still `EVAL_RESERVE` or `EVAL_BOUND`. + +## SELECT-003 + +Do not draft SELECT-003 until a new reserve exists, the four slices are represented, the reserve is text-disjoint from the pinned training export, the reserve is not spent or abandoned, and this policy stays sealed. The unresolved hypothesis, if a later authorization allows it, is still `HLX_SELECT_METRIC`: `unbind_exact` versus `classify_macro_f1_nonnone`. Authorization is a separate act. Meeting a planning target does not grant it. + +## GEN-0 and GEN-1 + +GEN-0 keeps `seed-morph78` and the pinned 9150-row export as the training baseline. New material is evaluation-only until the reserve slices are filled. Overflow may become `TRAIN_CANDIDATE` and must not be pulled back into the reserve. + +GEN-0 becomes impractical only if, after a real acquisition attempt: + +- new classify evidence cannot represent OBSERVED and non-none together +- new text keeps colliding with the pinned export or with spent/abandoned hashes +- accepted training candidates grow while the reserve slices stay empty +- the reserve cannot hold the four slices at once +- the historical baseline is no longer the system under test + +Convenience is not one of those conditions. This document does not create GEN-1. + +## Ledger + +`scripts/shadow/hyperlexical/build_identity_ledger.py` catalogues receipt-backed artifacts. It does not infer lifecycle from filenames. The populated ledger stays on the host store, not in git, and does not contain raw text. + +## Acquisition batch HLX-EVAL-ACQ-2026-09-26-001 + +Screened local rights-cleared and operator-labeled corpora. The ledger was not mutated. Decision `REJECT_BATCH`. The reserve stays empty. + +- 2026 backfill atoms: 67/67 unique hashes already `TRAIN_CONSUMED`. +- Harvest multiword file: 890/890 unique hashes already `TRAIN_CONSUMED`. +- Civilian seed: 17 novel identities, all lineage `ai-native`. A registry export marked `OBSERVED` is not an operator settlement, so those rows were held. Class imbalance is severe. No balance threshold is declared. +- 2026-09-16 structure gold: the authorized train rows are already consumed. Four novel leftovers carry model predictions and were excluded. +- Agent-memetics seeds: rights are unresolved and task labels are absent. + +New `OBSERVED` coverage is `NOT_COMPUTABLE` until an operator harvest settlement exists. This record does not settle phrases. SELECT-003 stays undrafted. GEN-1 was not created. In-repo settled gold is exhausted. That alone does not reset GEN-0. + +## Acquisition batch HLX-EVAL-ACQ-2026-09-26-002 + +The held-out stream (`hs-20260925T211358Z`, 339 rows) is the shelf that sits beside the Jev lane. Every canonical text hash is absent from the identity ledger. Overlap with the box Jev exposure list is 0. The ledger was not mutated. Decision: not admitted. The reserve stays empty. + +Jev in this batch is the exposure fence. `hs_run.py` and each row policy forbid evaluating Jev, the lineage rule, or any other model on these rows. `JEV_API_KEY` is unset on this host. No vendor call was made. A Jev family call is not an operator settlement. + +- Rights-cleared INFERRED labels: gaming-meta 99, betting-sharp 61, crypto-degen 5, plus 40 encyclopedic `none`. Five families have no independent label. +- 134 rows are `UNLABELLED`. The attest column is empty on all 339 rows, so new `OBSERVED` coverage is `NOT_COMPUTABLE`. +- 16 Reddit and Know Your Meme rows have unresolved rights. +- Class imbalance on the rights-cleared non-none subset is severe. No balance threshold is declared. Admitting it would open `classify_macro_f1_nonnone` on three families. + +SELECT-003 stays undrafted. GEN-1 was not created. Novel yield against `TRAIN_CONSUMED` is 339/339, so GEN-0 is not collision-blocked. The next label step is operator `attest-apply` on the queued sheet. + +## Taxonomy proposal HLX-EVAL-TAXON-2026-09-26-001 + +The next label step is no longer `attest-apply`. The operator directed a taxonomy expansion first. Draft: `label-taxonomy-proposal.md`. Private remap receipt has no row text. The ledger was not mutated. Nothing was admitted. Jev was not called. `attest-apply` was not run. + +The production head stays the nine-way `layout.FAMILIES` list. The proposal adds sixteen non-none names as a draft active set, with `attest`, `register`, and `function` as separate surfaces. `evaluation.enabled` is false on every name. Support minimum is `NOT_COMPUTABLE`. + +Of the 339 stream rows, 204 keep the same family as a `PROPOSED_REMAP` (gaming-meta 98, betting-sharp 61, crypto-degen 5, none 40). 135 are `LABEL_UNRESOLVED`. `brainrot-aura` is not split. Kinship hints are not mapped to `relationship-dating`. Eleven of the sixteen names have no row on this shelf. Row settlement is not ready. SELECT-003 stays undrafted. + +## Taxonomy acceptance HLX-EVAL-TAXON-2026-09-26-002 + +The operator accepted the ontology structure and amended it. Active non-none families are eighteen, including `identity-affiliation` and `politics-civic`. `work-hustle` is renamed `workplace-career`. `brainrot-aura` is not a family. Six names stay candidates. `taxonomy.active` is true and `evaluation.enabled` is false on all eighteen. `none` stays abstain. `source_hint` is evidence, not `semantic_family`. + +Lanes are prepared and unsettled: A 204, B 65, C 21, D 16. Thirty-three Wiktionary hint-only rows sit outside those lanes and stay `LABEL_UNRESOLVED`. Decision cells are empty. `attest-apply` was not run. The current command would force `OBSERVED` and would reject the new names, so it must not be used on these sheets. Vendor calls: 0. Reserve stays 0. SELECT-003 stays undrafted. + + +## Settlement tool HLX-EVAL-SETTLE-2026-09-26-001 + +Evaluation settlement is a separate command, `python -m hyperlexical.identity_ledger settlement-apply`. Production `attest-apply` was not modified and was not run. That command still accepts only the production families plus `none` or `reject`, and it still writes `label_source=OBSERVED` for an accepted value. + +`settlement-apply` reads completed operator cells. It does not fill them. `source_hint`, `semantic_family`, and `attest` are separate fields. `ACCEPT` stores the attest the operator entered and does not promote existing evidence to `OBSERVED`. `RECLASSIFY` requires an explicit family that differs from the proposed evidence. `NONE` stores `semantic_family=none` and is not `reject`. `UNRESOLVED` stores null family and null attest. A second decision for the same row is refused. The log and the receipt are append-only and contain no row text. + +`taxonomy.active`, `evaluation.enabled`, and `production.enabled` are three flags. The eighteen families stay taxonomy-active only. Both enable flags stay false. `layout.FAMILIES` is unchanged. Lanes A–D and the hint-only holding sheet were validated with every decision cell empty (204 / 65 / 21 / 16 / 33). Rows settled: 0. Unresolved decisions: 0. Reserve added: 0. Vendor calls: 0. The identity ledger was not mutated. SELECT-003 stays undrafted. + + +## Operator settlement HLX-EVAL-SETTLE-2026-09-26-002 + +The five private sheets for stream `hs-20260925T211358Z` were filled with an explicit decision on every row, then applied with `python -m hyperlexical.identity_ledger settlement-apply`. Production `attest-apply` was not run. `layout.FAMILIES` was not edited. No row text is in this file. + +Blank and `UNRESOLVED` stay distinct. Blank means the operator has not reviewed the row. `UNRESOLVED` means the operator reviewed the row and declined to settle it. This pass left blank at 0 and `UNRESOLVED` at 84. + +Counts: settled 255 (`ACCEPT` 204, `RECLASSIFY` 32, `NONE` 19), `UNRESOLVED` 84, blank 0. `OBSERVED` 128. `INFERRED` 127. Rights-blocked settled 14. Those 14 stay out of `EVAL_RESERVE`. Rights status was not changed by the semantic decision. + +`OBSERVED` was used only when a stored gloss or the row text directly states the settled family, the atom is slangish or multiword jargon, and the primary stored sense is that family. A category, a topic, or `source_hint` did not set family or attest. Accepting a family did not promote an existing `INFERRED` label. Encyclopedic `none` rows stayed `INFERRED`. + +Settled support by unique canonical text, including zeros: gaming-meta 60 (OBSERVED 38, INFERRED 22, cleared 60, blocked 0); betting-sharp 58 (41, 17, 53, 5); crypto-degen 4 (3, 1, 3, 1); internet-slang 19 (10, 9, 19, 0); memetic 3 (0, 3, 2, 1); social-status 6 (3, 3, 6, 0); relationship-dating 0; approval-disapproval 1 (0, 1, 1, 0); conflict-aggression 0; technology-ai 4 (1, 3, 4, 0); workplace-career 10 (9, 1, 10, 0); sports-competition 1 (0, 1, 0, 1); music-entertainment 1 (0, 1, 1, 0); fashion-aesthetic 0; regional-cultural 0; spiritual-mystic 0; identity-affiliation 16 (13, 3, 16, 0); politics-civic 13 (10, 3, 13, 0); none 59 (0, 59, 53, 6). + +Rights-cleared settled non-none identities: 188. Quantitative support thresholds are `NOT_COMPUTABLE`, so no `UNREPRESENTED` / `LOW_SUPPORT` / `REPRESENTED` flag is assigned. `OBSERVED` total 128, all non-none; `OBSERVED` none is 0. + +One workplace-lane row describes retail product reformulation and price-tier inflation. `finance-retail` remains a candidate and was not activated. That row is `UNRESOLVED`. + +Admission used `identity_ledger admit` under `hyperlex.eval_reserve.v1`, batch `HLX-EVAL-ADMIT-2026-09-26-002`. Admitted to `EVAL_RESERVE`: 241. Rejected existing identities: 0. Slice counts after admission: classify 241, classify_observed 123, classify_non_none 188, unbind_clean 0. `clean_unbind_support` is 0. This stream has no unbind target and none was fabricated. SELECT-003 was not drafted. The gate reports `eligible: false` because `unbind_clean` is unrepresented. Reserve identities are text-disjoint from the pinned train export. + +Receipt `HLX-EVAL-SETTLE-2026-09-26-002` sha256 `1b0fb54a5de0fb708f74ad278037458c15e0abea5a7eb6b4b151b7e7fb34567e`. `settlement-apply` still records `reserve_added: 0`; admission is the separate ledger command. Vendor calls: 0. BEST was not moved. Do not train. + + +## Clean-unbind contract — 2026-09-26 + +Phase: clean-unbind capacity recovery. The 241 classify reserve identities stay protected. This section does not retune `semantic_family`, `attest`, or `evaluation.enabled`. + +Executed shape: `hyperlexical.export._unbind_dual_scheme_rows`. Fillers are the source atom's own tokens. Positional text is those tokens joined by spaces, with roles `pos_0..pos_n-1`. Type-slot text is `TOKEN:` / `SLOT:` / `MARKER:` prefixed by index, and the fillers stay the same tokens. Role schemes are only `positional` and `type_slot`. Token count is 2 through `LIVE_UNBIND_MAX_TOKENS` (6). Atom length is at most `LIVE_UNBIND_MAX_LEN` (80). A gloss is not unbind gold. `class` is copied, not invented; this acquisition uses `INFERRED` and `lineage=none`. + +Contamination identity: `hyperlexical.holdout_guard.normalized_text_sha256` (NFKC, casefold, URL and punctuation stripped, SHA-256). + +Clean predicate: `soft_ceiling.clean_surface` on rows whose `split` is `train`, which is the call in `holdout_eligibility.census`. Definition string: `unbind_clean_definition = soft_ceiling.clean_surface`. The clean predicate is whitespace-collapsed lowercase equality of the row text against train text. It is not the contamination hash. `oov_filler_surface` is a different surface and is not this predicate. + +Scorer, not run in this phase: `hyperlexical.eval_forward.score_unbind_exact`. Gold is the filler list. Empty filler lists are skipped. Metrics are `unbind_exact`, `unbind_token_f1`, `unbind_slot_f1`, and the strict variants, including `by_role_scheme`. Baseline name: `unbind_copy_token`. + +Synthetic row, placeholders only: + +```json +{ + "text": "EXAMPLE_TOKEN_A EXAMPLE_TOKEN_B", + "split": "eval", + "lineage": "none", + "typology": [], + "stage": "noise", + "roles": ["pos_0", "pos_1"], + "fillers": ["EXAMPLE_TOKEN_A", "EXAMPLE_TOKEN_B"], + "role_scheme": "positional", + "task": "unbind", + "provenance": "source:EXAMPLE_LOCATOR", + "class": "INFERRED", + "license": "EXAMPLE_RIGHTS_GRANT", + "target_origin": "source_lemma_tokens" +} +``` + +The type-slot twin uses text `TOKEN:EXAMPLE_TOKEN_A SLOT:EXAMPLE_TOKEN_B`, roles `TOKEN` and `SLOT`, and the same fillers. Unbind settlement decisions are `ACCEPT`, `CORRECT_TARGET`, `REJECT`, and `UNRESOLVED` in `hyperlex.eval_unbind_settlement.v1`. That log is not the classify settlement schema. Admission still goes through `IdentityLedger.admit`. `unbind_clean` is set only for hashes kept by `clean_surface`. + + + +## Clean-unbind admission HLX-EVAL-ADMIT-2026-09-26-003 + +Source: Princeton WordNet 3.0 index lemmas (`index.noun`, `index.verb`, `index.adj`, `index.adv`). Glosses in `data.*` were not read and were not used as targets. Raw artifact sha256 `cbda5ea6eef7f36a97a43d4a75f85e07fccbb4f23657d27b4ccbc93e2646ab59`. License file sha256 `7731175a77952e259390b496fab905e57118b8d19ad3a8383c67eee724ff443f`. Rights: WordNet 3.0 Copyright 2006 by Princeton University, with permission to use, copy, modify, and distribute for any purpose without fee or royalty when the notice is preserved. Unresolved-rights rows were not in this source. + +Fillers are the source lemma tokens (`target_origin=source_lemma_tokens`). Operator settlement `HLX-EVAL-UNBIND-SETTLE-2026-09-26-001` appended 250 `ACCEPT` events on that basis. `CORRECT_TARGET` 0. `REJECT` 0. `UNRESOLVED` 0. Settlement receipt sha256 `3ada2dae58bde22ef1ed1ac5a4004be75d7bf3f52cac590f24900de71015194b`. The classify settlement log was not rewritten. + +Screen of shaped dual-scheme rows: 128233 unique texts. Novel and clean: 128044. Rejected `TRAIN_CONSUMED` 184. Rejected existing `EVAL_RESERVE` 5. Those 5 were not reused. Novelty rate among shaped rows: 0.9985. Planning cap admitted 250 of the admissible set. `IdentityLedger.admit` appended 500 events. Events sha256 before `f5e0008f27a80b11bc7e5b98e48e9e99cada04ee8f9455ed5ece6f99c1de3266`, after `8223ae11bb42bd1a98ebcd739d1cfbc470085e241826b662703faefdfe752da6`. Routed to `TRAIN_CANDIDATE`: 0. Acquisition receipt sha256 `be5671d4cf586b7a9ce3f45b4f5b8b5d0574edd4d1eaeef0c9644ca8ad2678a8`. Admission receipt sha256 `53df5397a13974e03bd60310fca2c29589e7a0fa6236dd576cf4ddf43a75bf15`. Census receipt sha256 `a5e9ae8ef6b65b5c187633e09b8700a5ef800eb1d97bd7a78aa2a9db26cf0a16`. + +Reserve after admission: classify 241, classify_observed 123, classify_non_none 188, unbind_clean 250. The first three did not decrease. Admitted rows are `class=INFERRED`, `lineage=none`. Role schemes: positional 125, type_slot 125. Filler counts: 2 tokens 64, 3 tokens 56, 4 tokens 56, 5 tokens 46, 6 tokens 28. Source-index metadata, not a Hyperlex family: noun 72, verb 68, adv 62, adj 48. Unique filler targets: 125. Each target has 2 surfaces (the two role schemes). Maximum surfaces per target: 2. + +Planning progress, not a validity threshold: classify 241/606, OBSERVED 123/287, non-none 188/604, clean unbind 250/250. Statistical minimum remains `NOT_COMPUTABLE`. + +`select_003_gate.eligible` is true. `training_overlap_identities` is 0. `spent_or_abandoned_in_reserve` is 0. Reserve identities 491. SELECT-003 was not drafted. This is representation completeness, not training readiness. Evaluation quality is still one lexicon, `INFERRED`, `lineage=none`. These rights-cleared active families still have zero settled classify support and were not collected here: relationship-dating, conflict-aggression, sports-competition, fashion-aesthetic, regional-cultural, spiritual-mystic. + +Vendor calls: 0. BEST was not moved. Do not train. + + +## SELECT-003 preregistration HLX-EXP-2026-09-26-SELECT-003 + +Phase: preregistration only. Experiment id `HLX-EXP-2026-09-26-SELECT-003`. Training is not authorized. BEST is not moved. No holdout is scored. Vendor calls: 0. + +Predecessors are not evidence for or against the hypothesis. SELECT-001 closed at the launch gate with the hypothesis `UNTESTED`. SELECT-002 is `EXECUTION_INVALID` with epochs 0 and gradient steps 0, hypothesis `UNTESTED`. + +Hypothesis: with training otherwise equivalent to the reconstructed `seed-morph78` baseline, does selecting checkpoints by `classify_macro_f1_nonnone` improve non-none classification macro-F1 while preserving the established unbind and classification safeguards? + +The single scientific variable is `HLX_SELECT_METRIC`. Baseline: unset, which resolves to `unbind_exact`, with `HYPERLEX_SAVE_BEST_UNBIND=1`. Candidate: `classify_macro_f1_nonnone`. Frozen with the reconstructed recipe: `HYPERLEX_FILLER_FILTER=off`, `HYPERLEX_TASK_ROUTING=legacy_split`, `HYPERLEX_UNBIND_LOSS_WEIGHT=1.0`, init `seed-morph65`, `HLX_SEED` unset, `HLX_E2_DISJOINT` absent, `HLX_VOCAB_TRAIN_ONLY` absent, `HLX_ALLOW_NO_HOLDOUT` unset, `HYPERLEX_RELEASE_SET` absent. Pinned-export execution is infrastructure, not a scientific variable. Checkpoint selection inside the trainer still uses the pinned export's val split. The reserve is the comparison surface for the decision rule. Substituting the reserve for that val split is a second variable and is not part of this experiment. + +Training input: pinned export, 9150 rows, sha256 `64b7d3dede25047cb6dd2e5b663f7fa72946ec82ac1a8816ae34622d1aaac430`. Rows before the reserve filter 9150, after 9150. Reserve/training row-id overlap 0. Reserve/training text-hash overlap 0. Any difference is an admission failure. + +BEST remains `hyperlex-encoder-modernbert-base-seed-morph78`, weights sha256 `fc53676bd347cccd4d0ac9a429f3469c36436f8eb0e09954e0c347c7b133a4a1`. Trunk weights sha256 `340ac08b74eef0d7bdec2d7981a6a3d4249bf0e6aab60634b72ad02c2b8023a9`. `layout.FAMILIES` was not edited. + +The bound GEN-0 reserve is unchanged: classify 241, classify_observed 123, classify_non_none 188, unbind_clean 250, identities 491. Events sha256 `8223ae11bb42bd1a98ebcd739d1cfbc470085e241826b662703faefdfe752da6`. Ledger projection sha256 `d071b7aec8154203ce7f9ae9531639b8d638f86c2ac0c3af38bead9b3c4a48f9`. Spent 0. Abandoned 0. Text-disjoint from training. Rights-cleared. No identities are added or removed under this experiment id. + +Receipts bound with the reserve: classification settlement `HLX-EVAL-SETTLE-2026-09-26-002` canonical sha256 `1b0fb54a5de0fb708f74ad278037458c15e0abea5a7eb6b4b151b7e7fb34567e`; classification admission `HLX-EVAL-ADMIT-2026-09-26-002` sha256 `9898d13140b1adf9e496ce4a7e71f9573d3a0f87e97355ea01de3ecafb66e836`; clean-unbind acquisition `be5671d4cf586b7a9ce3f45b4f5b8b5d0574edd4d1eaeef0c9644ca8ad2678a8`; clean-unbind settlement `3ada2dae58bde22ef1ed1ac5a4004be75d7bf3f52cac590f24900de71015194b`; clean-unbind admission `53df5397a13974e03bd60310fca2c29589e7a0fa6236dd576cf4ddf43a75bf15`; census `a5e9ae8ef6b65b5c187633e09b8700a5ef800eb1d97bd7a78aa2a9db26cf0a16`. + +Primary metric: + +```text +name: classify_macro_f1_nonnone +label_universe_sha256: 227b782011aad7e693fde253e103a24b3ca0bd6b04e090d446656fa943bf0175 +absent_class_policy: omit_when_gold_support_is_zero +scorer: hyperlexical.classify_metrics.macro_f1_nonnone +``` + +The sealed class set is the non-none lineages present on the bound reserve, with identity support: approval-disapproval 1, betting-sharp 53, crypto-degen 3, gaming-meta 60, identity-affiliation 16, internet-slang 19, memetic 2, music-entertainment 1, politics-civic 13, social-status 6, technology-ai 4, workplace-career 10. Sum 188. The scorer's macro is the unweighted mean of per-class F1 over gold labels other than `none` that have support n>0. Classes with gold support 0 are omitted. They are not entered as F1=0. A later taxonomy expansion does not enter this experiment's metric. The six families with zero rights-cleared settled support stay outside the universe: relationship-dating, conflict-aggression, sports-competition, fashion-aesthetic, regional-cultural, spiritual-mystic. + +Slice mapping, one contamination function for every slice (`normalized_text_sha256`): + +- `classify_macro_f1_nonnone` uses the 188 `classify_non_none` identities. Gold is `lineage`. The 53 `none` identities are not in this row set. +- Classification accuracy uses the 241 `classify` identities. Gold is `lineage`, including `none`. Scorer: `accuracy`. +- OBSERVED-label accuracy uses the 123 `classify_observed` identities. Gold is `lineage`. Scorer: `accuracy`. These three slices share classification settlement `HLX-EVAL-SETTLE-2026-09-26-002` and admission `HLX-EVAL-ADMIT-2026-09-26-002`. +- Unbind clean exact uses the 250 `unbind_clean` identities. Gold is the filler list. Scorer: `score_unbind_exact` (`unbind_exact`). Settlement is `HLX-EVAL-UNBIND-SETTLE-2026-09-26-001`, not the classify log. Clean predicate remains `soft_ceiling.clean_surface`. + +Decision thresholds are `BLOCKED_PENDING_OPERATOR_AUTHORIZATION`. Inherited authorization from SELECT-002 is `NOT_COMPUTABLE`. No canonical rule carries numeric margins across an execution-invalid predecessor, and SELECT-002 sealed its margins for that experiment id only. Preservation names are prepared and inactive: unbind clean exact, classification accuracy, OBSERVED-label accuracy. Promote and reject thresholds are not set. + +Representation completeness passes. Training provenance passes. Holdout disjointness passes. Rights and provenance pass. Clean-unbind capacity passes. Evaluation quality is limited and disclosed: non-none support is 188 and imbalanced, and the 250 clean-unbind rows are Princeton WordNet 3.0 only, all `INFERRED`, `lineage=none`, 125 filler targets, 2 surfaces per target. The experiment is not authorized to run. + +## SELECT-003 threshold authorization — 2026-09-26 + +Operator decision for `HLX-EXP-2026-09-26-SELECT-003` only. This is a new authorization. It does not transfer SELECT-001 or SELECT-002 margins. The sealed preregistration file is not edited. Training is not authorized. BEST is not moved. The reserve is not scored. + +Primary metric remains `classify_macro_f1_nonnone`. Label universe sha256 `227b782011aad7e693fde253e103a24b3ca0bd6b04e090d446656fa943bf0175`. Absent-class policy `omit_when_gold_support_is_zero`. The universe stays the twelve supported non-none classes on the sealed reserve. + +Comparison baseline is `hyperlex-encoder-modernbert-base-seed-morph78`, weights sha256 `fc53676bd347cccd4d0ac9a429f3469c36436f8eb0e09954e0c347c7b133a4a1`, scored later on the same GEN-0 reserve: classify 241, classify_observed 123, classify_non_none 188, unbind_clean 250. + +`PROMOTE` requires every condition. Primary: candidate `classify_macro_f1_nonnone` strictly greater than seed-morph78. Equality does not promote. Unbind clean exact on the 250 sealed identities stays within 0.01 below seed-morph78. Classification accuracy on the 241 sealed identities stays within 0.02. OBSERVED-label accuracy on the 123 sealed identities stays within 0.05. Integrity must also pass: one scientific variable, pinned export exact, 9150 rows before and after the reserve filter, row-id overlap 0, canonical text-hash overlap 0, no reserve identity spent or abandoned before scoring, sealed ledger unchanged, sealed class universe unchanged, checkpoint selection follows `HLX_SELECT_METRIC`, complete provenance, no contamination-guard failure, no schema or name-gate failure, and no `CHAR_WINS`. + +`REJECT` if the candidate primary metric is lower, or the unbind delta is below -0.01, or classification accuracy delta is below -0.02, or OBSERVED-label accuracy delta is below -0.05, or any hard failure occurs: `CHAR_WINS`, more than one scientific variable, training input other than the sealed pin, effective training rows other than 9150, reserve binding change, class-universe change, unverifiable reserve or execution provenance, or a hard integrity or contamination guard failure. + +`INCONCLUSIVE` if the primary metrics are equal and the preservation and integrity guards pass. Also `INCONCLUSIVE` when execution and scoring are valid but the sealed rule cannot be applied deterministically, and that reason is not itself a hard integrity failure. An inconclusive result is not resolved by changing thresholds. + +These floors are new SELECT-003 rules. A later promote would mean checkpoint selection by non-none macro-F1 improved the sealed twelve supported families without exceeding the three allowed regressions. It would not mean improvement across the eighteen-family ontology. Families outside the gold universe remain relationship-dating, conflict-aggression, sports-competition, fashion-aesthetic, regional-cultural, and spiritual-mystic. Representation completeness passes. Evaluation quality stays limited. Vendor calls: 0. Do not train. + + +## SELECT-003 execution HLX-EXP-2026-09-26-SELECT-003 + +One launch was authorized from Spark commit `53f128a68a6603a98d9d5a3e56cc357f4a17aa0c` with a clean tree. The sealed preregistration, threshold authorization, candidate environment, and non-launching preflight were not edited. `HYPERLEX_ALLOW_TRAIN=1` was a process overlay only. `HLX_ALLOW_NO_HOLDOUT` stayed unset. + +Container `hlx-train-select003-1790441469` on `lmsysorg/sglang:dev-qwen38-27b-dflash2` (`sha256:616a3e97f45191af975896cfa644279096cb31bd408a071c2e99ca7209c3cafe`) started 2026-09-26T16:51:09Z and exited 1 at 2026-09-26T16:51:12Z. The trainer refused before `load_training_bundle`: `HYPERLEX_ALLOW_TRAIN=1` with no holdout manifest. Epochs 0. Gradient steps 0. No candidate checkpoint. The pinned export was not consumed. The reserve was not scored and was not spent. Decision `EXECUTION_INVALID`. The hypothesis is `UNTESTED`. This is not `PROMOTE`, `REJECT`, or `INCONCLUSIVE`. + +BEST remains `hyperlex-encoder-modernbert-base-seed-morph78`, sha256 `fc53676bd347cccd4d0ac9a429f3469c36436f8eb0e09954e0c347c7b133a4a1`. Vendor calls: 0. Do not retry this experiment id. Do not train. +## SELECT-003 closed — PREFLIGHT_LAUNCH_HOLDOUT_GATE_MISMATCH + +`HLX-EXP-2026-09-26-SELECT-003` is permanently `EXECUTION_INVALID`. Cause: `PREFLIGHT_LAUNCH_HOLDOUT_GATE_MISMATCH`. The non-launching preflight reported `TRAINING_READY` without running `require_holdout_for_training` under `HYPERLEX_ALLOW_TRAIN=1`. The trainer then refused before `load_training_bundle` because the sealed candidate had no `HLX_HOLDOUT_MANIFESTS` and `HLX_ALLOW_NO_HOLDOUT` stayed unset. Epochs 0. Gradient steps 0. Candidate checkpoint: none. Reserve scored: false. `BEST_moved`: false. Hypothesis: `UNTESTED`. The sealed preregistration and threshold authorization are not rewritten. This id is not retried. + +## Controlled-experiment admission — CONTROLLED_RESERVE + +The canonical controlled-experiment holdout contract is `CONTROLLED_RESERVE`. A launch with `HLX_EXPERIMENT_ID` set is admitted by `admit_training_run`, which both preflight and `run_loop` call, in this order: experiment binding, launch gate, sealed reserve, pinned training input, train/reserve disjointness, single scientific variable, BEST and trunk digests, output directory, then ready. + From 22818dd05c72c638dd710d9b826b0bb40fd0cd11 Mon Sep 17 00:00:00 2001 From: Daniel Meyer Date: Mon, 28 Sep 2026 16:42:55 -0700 Subject: [PATCH 28/42] feat(007): stage evaluation-reserve chunk (01) --- .land-chunks/part-01 | 192 +++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 192 insertions(+) create mode 100644 .land-chunks/part-01 diff --git a/.land-chunks/part-01 b/.land-chunks/part-01 new file mode 100644 index 00000000..a961ce55 --- /dev/null +++ b/.land-chunks/part-01 @@ -0,0 +1,192 @@ +A sealed reserve binding satisfies the holdout requirement when the active reserve is `EVAL_RESERVE` for the current experiment, all four slices are present on that active set, the binding digest matches the full `events.jsonl`, and training overlap by row id and by normalized text hash is 0 against that active set. Overlap rejects the run. It does not drop training rows. `HLX_HOLDOUT_MANIFESTS` is not a second requirement. `HLX_ALLOW_NO_HOLDOUT` does not admit a controlled experiment. Legacy launches that are not controlled experiments still use the manifest gate. + +Active membership is the derived lifecycle `EVAL_RESERVE` whose experiment binding is only the current experiment. `evaluation_reserved` stays historical evidence and is not cleared when the lifecycle is `EVAL_SPENT`. Spent identities do not count, do not satisfy overlap, and cannot be reserved again. A ledger with no active identities is a missing active reserve. A live `EVAL_RESERVE` bound to another experiment does not satisfy the current experiment. Slice counts and the binding identity count use the active set. The events digest still covers the append-only log. + +The default scientific variable remains `HLX_SELECT_METRIC`: that key is the only allowed difference. `HLX_SCIENTIFIC_VARIABLE=train_schedule`, set to the same value on the baseline, the candidate, and the process, is the only composite. It normalizes `HYPERLEX_TRAIN_EPOCHS`, `HYPERLEX_EARLY_STOP`, `HYPERLEX_EARLY_STOP_PATIENCE`, and `HYPERLEX_EARLY_STOP_MIN_EPOCHS` into one variable. Both arms must set `HLX_SELECT_METRIC=classify_macro_f1_nonnone`. Any other difference fails. An undeclared schedule difference fails. A selection-metric difference is not part of `train_schedule`. Declaring the variable does not register or authorize an experiment. + +`HYPERLEX_ALLOW_TRAIN=1` is part of the effective environment hash. `HLX_ADMISSION_ONLY=1` is not. With that flag, `python -m hyperlexical.train --run` returns after admission and does not construct an optimizer, enter epoch 0, or take a gradient step. `TRAINING_READY` means that same admission returned `ADMISSION_PASS`. + +```text +RUNE.PREFLIGHT_LAUNCH_PARITY(x) = + effective_environment_hash(preflight) == effective_environment_hash(launch) + AND admission_gate_sequence(preflight) == admission_gate_sequence(launch) + AND admission_result(preflight) == admission_result(launch) +``` + +## SELECT-004 readiness + +`HLX-EXP-2026-09-26-SELECT-004` is not preregistered and is not authorized to run. It may be drafted. SELECT-001, SELECT-002, and SELECT-003 produced no scientific evidence about `HLX_SELECT_METRIC`. A draft may test the same hypothesis: baseline unset, which resolves to `unbind_exact` with `HYPERLEX_SAVE_BEST_UNBIND=1`; candidate `classify_macro_f1_nonnone`. + +No canonical rule carries numeric thresholds across experiments. SELECT-003's floors do not transfer. SELECT-004 decision thresholds are `BLOCKED_PENDING_OPERATOR_AUTHORIZATION` until a fresh authorization is sealed for that id. + +The draft must keep the pinned export sha256 `64b7d3dede25047cb6dd2e5b663f7fa72946ec82ac1a8816ae34622d1aaac430`, 9150 rows, BEST sha256 `fc53676bd347cccd4d0ac9a429f3469c36436f8eb0e09954e0c347c7b133a4a1`, and trunk sha256 `340ac08b74eef0d7bdec2d7981a6a3d4249bf0e6aab60634b72ad02c2b8023a9`. The GEN-0 reserve stays classify 241, classify_observed 123, classify_non_none 188, unbind_clean 250, identities 491. It is not scored and its lifecycle is not changed. Admission uses that reserve. It does not draw another one and it does not attach a legacy manifest. + +Vendor calls: 0. BEST was not moved. Do not train. +## SELECT-004 preregistered — HLX-EXP-2026-09-26-SELECT-004 + +`HLX-EXP-2026-09-26-SELECT-004` is `PREREGISTERED`. Preregistration sha256 `365f7ce7141e8ee899fc52d85584a32c21ecfcb596e316b1438463bd8db85c4a`. That seal records repository commit `493c75981bf443baf2899b71b504fe7c7a529cf9` and a clean tree. Later documentation does not edit the sealed file. + +Predecessors are not evidence. SELECT-001 remains `CLOSED_AT_LAUNCH_GATE`. SELECT-002 remains `EXECUTION_INVALID` with epochs 0 and gradient steps 0. SELECT-003 remains `EXECUTION_INVALID`, cause `PREFLIGHT_LAUNCH_HOLDOUT_GATE_MISMATCH`, epochs 0, gradient steps 0, candidate checkpoint none, reserve not scored. The hypothesis is `UNTESTED`. + +The single scientific variable is `HLX_SELECT_METRIC`. Baseline is unset, which resolves to `unbind_exact` with `HYPERLEX_SAVE_BEST_UNBIND=1`. Candidate is `classify_macro_f1_nonnone`. Experiment diff sha256 `aa6e42e50a9547908edfe4860371a2ec05f7ec20ea87dfa829555d9e7b05bc4c`. `experimental_variable_count` is 1. Metadata differences are the experiment id, the output directory, and the residual-dump path. + +`CONTROLLED_RESERVE` binding sha256 `c16e69559dd6582f687532ce6e2a2e9b52a70db1aa066d11e7e68e723936b371`. Ledger events sha256 `8223ae11bb42bd1a98ebcd739d1cfbc470085e241826b662703faefdfe752da6`. Projection sha256 `d071b7aec8154203ce7f9ae9531639b8d638f86c2ac0c3af38bead9b3c4a48f9`. Counts remain classify 241, classify_observed 123, classify_non_none 188, unbind_clean 250, identities 491, lifecycle `EVAL_RESERVE`. Training row-id overlap is 0. Training canonical-text overlap is 0. The reserve was not scored, spent, abandoned, or relabeled. Historical spent and abandoned identities in the same ledger are not this reserve. + +Training input is the pinned export sha256 `64b7d3dede25047cb6dd2e5b663f7fa72946ec82ac1a8816ae34622d1aaac430`, mode `PINNED_EXPORT`. Declared rows, consumed rows, and effective optimization rows are 9150. No runtime exclusion reduced the set. `HLX_ALLOW_NO_HOLDOUT` stayed unset. No legacy holdout manifest was attached. + +BEST remains `hyperlex-encoder-modernbert-base-seed-morph78`, sha256 `fc53676bd347cccd4d0ac9a429f3469c36436f8eb0e09954e0c347c7b133a4a1`. Trunk sha256 `340ac08b74eef0d7bdec2d7981a6a3d4249bf0e6aab60634b72ad02c2b8023a9`. BEST was not moved. + +Decision thresholds are `BLOCKED_PENDING_OPERATOR_AUTHORIZATION`. SELECT-003 numeric floors do not transfer. The prepared preservation metric names are `unbind_clean_exact`, `classification_accuracy`, and `observed_label_accuracy`. Those names have no active numeric thresholds. + +Admission-only parity passed. Preflight and the trainer entrypoint, both under `HYPERLEX_ALLOW_TRAIN=1` with `HLX_ADMISSION_ONLY=1`, share environment hash `759c591151c88074973c3ba2a555be551d3460c04c8cedf59ab36624de215885`. `admission_result` is `ADMISSION_PASS`. Status is `PREREGISTERED`. `optimizer_loaded` is false. Epochs 0. Gradient steps 0. The SELECT-004 output directory was absent before and after. `HLX_ADMISSION_ONLY` is excluded from the environment hash. + +`TRAINING_READY` exists only when all three are true: the scientific contract is sealed, the decision rule is sealed, and real entrypoint admission passes. SELECT-004 stays below `TRAINING_READY` until a fresh threshold authorization for this experiment id. `training_launch_authorized` is false. + +Representation completeness is `PASS`. Evaluation quality is `LIMITED`. Classification support is 188 rights-cleared non-none identities. Clean unbind is 250 identities, 125 targets, 2 role-scheme surfaces per target, from Princeton WordNet 3.0 only. Unsupported families remain relationship-dating, conflict-aggression, sports-competition, fashion-aesthetic, regional-cultural, and spiritual-mystic. A later result must not claim those families. Label universe sha256 `227b782011aad7e693fde253e103a24b3ca0bd6b04e090d446656fa943bf0175`. Absent-class policy `omit_when_gold_support_is_zero`. + +Vendor calls: 0. Do not train. The next decision is a fresh SELECT-004 threshold authorization. +## SELECT-004 thresholds authorized — HLX-EXP-2026-09-26-SELECT-004 + +Fresh operator authorization for `HLX-EXP-2026-09-26-SELECT-004` only. It does not inherit authority from SELECT-001, SELECT-002, or SELECT-003. The sealed preregistration bytes stay `365f7ce7141e8ee899fc52d85584a32c21ecfcb596e316b1438463bd8db85c4a`. + +`PROMOTE` requires `classify_macro_f1_nonnone` strictly greater than `seed-morph78` on the sealed twelve-class universe, sha256 `227b782011aad7e693fde253e103a24b3ca0bd6b04e090d446656fa943bf0175`, absent-class policy `omit_when_gold_support_is_zero`. Equality does not promote. The new SELECT-004 preservation floors are unbind clean exact within 0.01 on 250 identities, classification accuracy within 0.02 on 241 identities, and OBSERVED-label accuracy within 0.05 on 123 identities. A primary decrease, a preservation breach, or a hard integrity failure is `REJECT`. Equal primary metrics with every guard passing are `INCONCLUSIVE`. An inconclusive result is not resolved by changing thresholds. + +Authorization artifact sha256 `7389783b52a5d5cf14ceca874cc12351d3f6571b6be8a8666c2040be38972625`. Post-authorization admission used `HYPERLEX_ALLOW_TRAIN=1` and `HLX_ADMISSION_ONLY=1`. Preflight and the trainer entrypoint share environment hash `a2b18512be8329ee63ad06e98f894c6138cf7eac05f6e82d53d4132e99a27f5d`. `admission_result` is `ADMISSION_PASS`. Status is `TRAINING_READY` because the scientific contract is sealed, the decision rule is sealed, and the real entrypoint passed. `optimizer_loaded` is false. Epochs 0. Gradient steps 0. Training was not started. Output directory stayed absent. BEST sha256 `fc53676bd347cccd4d0ac9a429f3469c36436f8eb0e09954e0c347c7b133a4a1` was not moved. Trunk sha256 `340ac08b74eef0d7bdec2d7981a6a3d4249bf0e6aab60634b72ad02c2b8023a9`. `training_launch_authorized` is false. `HLX_ALLOW_NO_HOLDOUT` stayed unset. + +Representation completeness is `PASS`. Evaluation quality is `LIMITED`. The decision covers classify 241, classify_observed 123, classify_non_none 188, and unbind_clean 250. It does not establish performance for relationship-dating, conflict-aggression, sports-competition, fashion-aesthetic, regional-cultural, or spiritual-mystic. Clean unbind stays limited to the sealed WordNet slice. Vendor calls: 0. Do not train. The next decision is launch authorization only. + +## SELECT-004 scored — HLX-EXP-2026-09-26-SELECT-004 + +One run finished under the sealed contract. Container `hlx-train-select004-1790448703` exited 0 after 40 epochs. The training schedule was not shortened. Checkpoint selection used `classify_macro_f1_nonnone` on the training-export validation surface. The selected checkpoint is epoch 3 at 0.64448782942204. Epoch 39 scored 0.5864567286803334. Candidate weights sha256 `9fba0f66b1d5de6492470f53577d1447bfac1d29b9ac03869268abb70bbd97f6`. Consumed export sha256 `64b7d3dede25047cb6dd2e5b663f7fa72946ec82ac1a8816ae34622d1aaac430`, 9150 rows, reserve filter removed 0. The reserve ledger was not modified during training. + +Reserve scoring used the sealed twelve-family universe `227b782011aad7e693fde253e103a24b3ca0bd6b04e090d446656fa943bf0175`. seed-morph78 macro-F1 0.022395727019119547, accuracy 0.14107883817427386, OBSERVED accuracy 0.032520325203252036, unbind clean exact 0.092. The candidate macro-F1 0.07245710784313726, accuracy 0.15767634854771784, OBSERVED accuracy 0.08130081300813008, unbind clean exact 0.100. Deltas are +0.05006138082401771, +0.01659751037344398, +0.048780487804878044, and +0.008. The char 3–5 baseline macro-F1 was 0.0, so CHAR_WINS did not occur. Decision **PROMOTE**. State `PROMOTION_ELIGIBLE`. `--apply-best` was not run. BEST remains `seed-morph78` / `fc53676bd347cccd4d0ac9a429f3469c36436f8eb0e09954e0c347c7b133a4a1`. + +Representation completeness is `PASS`. Evaluation quality is `LIMITED`. The production head still emits the nine training families, so nine of the twelve gold families cannot be named and pull the absolute macro-F1 down for both checkpoints. The claim stays inside the sealed slices. Vendor calls: 0. The 40-epoch schedule was left as sealed. A later duration study can measure best-epoch locations; it is not a change to this result. + +## SELECT-004 promoted — HLX-EXP-2026-09-26-SELECT-004 + +Operator authorization applied the selected epoch-3 checkpoint as BEST. The promoted object is `hyperlex-encoder-modernbert-base-seed-select004/model.safetensors`, sha256 `9fba0f66b1d5de6492470f53577d1447bfac1d29b9ac03869268abb70bbd97f6`. `model.final.safetensors` (`d5be46be6611e382d837bab8868bb373cbead4f9caa3a666ca06f2b2cda1925b`) was not promoted. Epoch 39 was not promoted. No training ran. The reserve was not rescored. + +Previous BEST `hyperlex-encoder-modernbert-base-seed-morph78` remains in place, sha256 `fc53676bd347cccd4d0ac9a429f3469c36436f8eb0e09954e0c347c7b133a4a1`. + +Decision receipt sha256 `ee493b7d73b5e00ae27ec681bb52405bbcf2983a16a0297edad225aec52b7225`. Decision `PROMOTE`. Selection metric `classify_macro_f1_nonnone` `0.64448782942204`. Primary macro-F1 baseline `0.022395727019119547`, candidate `0.07245710784313726`, delta `0.05006138082401771`. Preservation deltas: classification accuracy `0.01659751037344398`, OBSERVED accuracy `0.048780487804878044`, unbind clean exact `0.008`. CHAR_WINS did not occur. Integrity `PASS`. + +The 491 sealed reserve identities moved `EVAL_RESERVE` to `EVAL_BOUND` to `EVAL_SPENT` through `IdentityLedger.transition` and `persist_append`. Events sha256 before `8223ae11bb42bd1a98ebcd739d1cfbc470085e241826b662703faefdfe752da6`, after `96b74a92d44f1cf9fe152b18e5207176f161ba3bfce528dac38aa4571a742f9c`. Projection sha256 before `d071b7aec8154203ce7f9ae9531639b8d638f86c2ac0c3af38bead9b3c4a48f9`, after `77e22433203879b252f7a9e309d2013d7550101d1c4a014b494d2c96df87d0e0`. The sealed binding file `c16e69559dd6582f687532ce6e2a2e9b52a70db1aa066d11e7e68e723936b371` was not rewritten. Historical consumed, spent, and abandoned identities outside this reserve were not changed. + +Representation completeness is `PASS`. Evaluation quality is `LIMITED`. This promotion does not establish performance across the eighteen-family ontology. The production head cannot name nine of the twelve sealed gold families. That limitation is separate from the checkpoint pointer. Vendor calls: 0. No further experiment was started. + +Promotion receipt sha256 `2e1f84b476f7355e31380419afad00d0b16d372235a0da6be85adc17fcf92d01`. + +## SELECT-005 blocked — HLX-EXP-2026-09-27-SELECT-005 + +`HLX-EXP-2026-09-27-SELECT-005` is the next unclaimed experiment id. Claimed ids are `HLX-EXP-2026-09-26-SELECT-001` through `HLX-EXP-2026-09-26-SELECT-004`. This id is recorded and not sealed. No preregistration file, arm directory, reserve, or admission receipt was created. + +`TRAINING_BLOCKED`. The proposed variable is the composite `train_schedule`. Control would be `max_epochs=40`, early stopping disabled, restore best. Candidate would be `max_epochs=12`, `minimum_epochs=4` scored epochs through epoch index 3, `early_stopping_patience=4`, strict increase, ties keep the earlier checkpoint, restore best. Both arms would pin `classify_macro_f1_nonnone`, warm start `hyperlex-encoder-modernbert-base-seed-morph65`, and export sha256 `64b7d3dede25047cb6dd2e5b663f7fa72946ec82ac1a8816ae34622d1aaac430` at 9150 rows. + +The canonical trainer does not implement that candidate. `scripts/shadow/hyperlexical/loop.py` scores `for ep in range(epochs)` and never stops early. `score_now > best_macro` already keeps the earlier checkpoint on ties, and best weights are written back after the loop when the selection metric is `classify_macro_f1_nonnone`. `epoch-progress.jsonl` records the metric and not wall-clock. Spark and public `main` have the same `loop.py` sha256 `f51e7aaff69e9033cc9ba16eee7225bfeefcf521e32236bdb791cc7900e130a5`. Spark HEAD `098ece4d9e8ebb27b0b0d3410b1280ed072d4847` was clean. Public `main` is `2f73f30010cc16ee014ed8d88de73131eb80d0a9`. + +The smallest separate change is an optional break in that epoch loop, default off: after a scored epoch, stop when at least 4 epochs have been scored and `epoch - best_epoch >= 4`, still capped by `max_epochs`, and append per-epoch wall-clock seconds to `epoch-progress.jsonl`. Setting `HYPERLEX_TRAIN_EPOCHS=12` is not that schedule. This record does not apply the change. + +SELECT-004 artifacts were not modified. Its reserve stays `EVAL_SPENT` and was not reused. No new reserve was allocated. BEST was not moved. It still names `hyperlex-encoder-modernbert-base-seed-select004`, weights sha256 `9fba0f66b1d5de6492470f53577d1447bfac1d29b9ac03869268abb70bbd97f6`. No optimizer was constructed. Epochs and gradient steps for this id are zero. Launch is not authorized. + +## SELECT-005 still blocked — trainer synced, admission refused + +Spark now carries public `main` `a7d254e8981695072f5be36ab7ebdcc46cbd672c` for the trainer. `scripts/shadow/hyperlexical/loop.py` sha256 `1aa395081d7709be3844bf2568d12100d73d931ecbc51af43c3d6e01dba77e2a`. Early stopping remains default-off. `HLX-EXP-2026-09-27-SELECT-005` is still not sealed. No preregistration file, arm directory, reserve, or admission receipt was created. The private ledger sections above were not replaced by the public projection. + +`TRAINING_BLOCKED`. The trainer can express the candidate schedule. Canonical admission cannot seal it. + +`admit_training_run` allows exactly one scientific difference, and that difference must be `HLX_SELECT_METRIC`. A non-mutating probe pinned both arms to `classify_macro_f1_nonnone` and changed only `HYPERLEX_TRAIN_EPOCHS` (`40` versus `12`) and `HYPERLEX_EARLY_STOP` (`0` versus `1`). Patience and minimum epochs were the same on both arms. The gate `single_variable` failed: `scientific variable count is 2: HYPERLEX_EARLY_STOP,HYPERLEX_TRAIN_EPOCHS`. + +The spent ledger cannot supply a new `EVAL_RESERVE`. Events sha256 remains `96b74a92d44f1cf9fe152b18e5207176f161ba3bfce528dac38aa4571a742f9c`. Live reserve counts are classify 0, classify_observed 0, classify_non_none 0, unbind_clean 0. The same probe failed at `holdout_reserve`: `reserve slice classify is absent`. The reserve scan also includes every identity with `evaluation_reserved` set. That scan is 491 identities, all derived `EVAL_SPENT`. A new reserve written onto this ledger would still fail that lifecycle check. The flag was not cleared. The SELECT-004 reserve was not reused. + +No optimizer was constructed. Epochs and gradient steps for this id are zero. BEST was not moved. It still names `hyperlex-encoder-modernbert-base-seed-select004`, weights sha256 `9fba0f66b1d5de6492470f53577d1447bfac1d29b9ac03869268abb70bbd97f6`. Launch is not authorized. + +The smallest separate change is an admission-contract patch: accept one declared schedule variable while both arms share `classify_macro_f1_nonnone`, and treat historical `EVAL_SPENT` identities as outside the current reserve without clearing `evaluation_reserved`. Do not allocate a reserve before that contract exists. + + +## SELECT-005 still unsealed — admission contract synced, fresh reserve unavailable + +Public `main` is `fab0de03d75e3280dc34feed425c4783ccf7e2b5`, the squash merge of the admission contract. Spark carries that `admission.py` sha256 `6ae7b63ca1b6e0459d9b795c731668b3eff524269d81bcb92181c0c9d407ab78` and `identity_ledger.py` sha256 `ac49b7240a895952b99b3ef9046f7a8185c8647badac7a45c8640d592057d1c5`. `scripts/shadow/hyperlexical/loop.py` remains sha256 `1aa395081d7709be3844bf2568d12100d73d931ecbc51af43c3d6e01dba77e2a`. The private ledger file was not replaced. + +`HLX-EXP-2026-09-27-SELECT-005` is still not sealed. No preregistration file, arm directory, reserve binding, or admission receipt was created. The ledger was not appended. Events sha256 remains `96b74a92d44f1cf9fe152b18e5207176f161ba3bfce528dac38aa4571a742f9c`. `evaluation_reserved` was not cleared. The SELECT-004 reserve stays `EVAL_SPENT` and was not reused. + +A fresh reserve needs all four slices from text that is absent from the ledger and from the pinned export. The ledger has no `EVAL_RESERVE`, `EVAL_BOUND`, or `AVAILABLE` identities. The remaining settled hashes from the held-out stream that are absent from the ledger are `UNRESOLVED`, `NONE`, `RECLASSIFY`, or `ACCEPT` with `RIGHTS_UNRESOLVED`. Unresolved-rights rows stay out of `EVAL_RESERVE`. No novel rights-cleared classify settlement remains. A classify-absent reserve was not written. The WordNet unbind source was not admitted by itself. + +No optimizer was constructed. Epochs and gradient steps for this id are zero. BEST was not moved. It still names `hyperlex-encoder-modernbert-base-seed-select004`, weights sha256 `9fba0f66b1d5de6492470f53577d1447bfac1d29b9ac03869268abb70bbd97f6`. Launch is not authorized. + + +## SELECT-005 census — fresh reserve still unavailable + +A second join of the 339 settlement events to the 7964 ledger identities found no novel rights-cleared classify row. Events sha256 remains `96b74a92d44f1cf9fe152b18e5207176f161ba3bfce528dac38aa4571a742f9c`. The ledger was not appended. `evaluation_reserved` was not cleared. The SELECT-004 reserve was not reused. + +All 6761 unique hashes in the pinned export are already in the ledger. Of the 98 settlement events absent from the ledger, none are in that export. Those 98 are 82 `UNRESOLVED` with cleared rights, 2 `UNRESOLVED` with unresolved rights, 7 `ACCEPT` with unresolved rights, 6 `NONE` with unresolved rights, and 1 `RECLASSIFY` with unresolved rights. `UNRESOLVED` means the operator reviewed the row and declined to settle it. Unresolved-rights rows stay out of `EVAL_RESERVE`. There are 0 novel `CC-BY-SA` rows with decision `ACCEPT`, `RECLASSIFY`, or `NONE`. + +The 28 promoted-accept files are training gold under an older family set and were not remapped. WordNet can still supply `unbind_clean` and was not admitted alone, because a classify-absent reserve fails the slice gate. No preregistration, arm directory, reserve binding, or admission receipt was created. The optimizer was not constructed. Epochs and gradient steps for `HLX-EXP-2026-09-27-SELECT-005` remain 0. `training_launch_authorized` is false. BEST was not moved. + + +## SELECT-005 classify candidates blocked — HLX-EVAL-REVIEW-2026-09-27-001 + +Admission requires each active slice count to be at least 1. The classify floors are classify 1, classify_observed 1, and classify_non_none 1. Planning targets 606, 287, and 604 are not that floor. This pass did not lower the floor. + +Packet `HLX-EVAL-REVIEW-2026-09-27-001` is an operator-review packet, not a reserve. Ready rows: 0. Other screened rows: label unresolved 10, rights blocked 69, provenance blocked 4, cohort duplicate 1. Previously declined rights-cleared rows and unresolved-rights events were not reopened. WordNet and the older promoted-accept files were not used. No operator decision was written. + +Events sha256 remains `96b74a92d44f1cf9fe152b18e5207176f161ba3bfce528dac38aa4571a742f9c`. The ledger was not appended. `HLX-EXP-2026-09-27-SELECT-005` was not sealed. BEST was not moved. + + +## ai-native evaluation family — 2026-09-27 + +Public `main` is `ca9403efe8470d46566abbcd098640f07b33b759`. `ai-native` is taxonomy-active. `evaluation.enabled` stays false. The production head stays nine names. No row was settled. The ledger was not appended. Events sha256 remains `96b74a92d44f1cf9fe152b18e5207176f161ba3bfce528dac38aa4571a742f9c`. + +The review packet `HLX-EVAL-REVIEW-2026-09-27-001` now has 10 ready rows proposing `ai-native`. Their stored class stays `INFERRED`. `classify_observed` is still short by 1 until an operator attests `OBSERVED`. `HLX-EXP-2026-09-27-SELECT-005` is not sealed. + + +## Operator attested the ready rows observed + +The operator attested `OBSERVED` on all 10 ready rows in packet `HLX-EVAL-REVIEW-2026-09-27-001`. The proposed family is `ai-native`. Rights-blocked, provenance-blocked, and duplicate rows were not attested. No `ACCEPT`, `RECLASSIFY`, or `NONE` was written. The ledger was not appended. Events sha256 remains `96b74a92d44f1cf9fe152b18e5207176f161ba3bfce528dac38aa4571a742f9c`. `HLX-EXP-2026-09-27-SELECT-005` is not sealed. + +## Unbind preview refused as slang — 2026-09-27 + +The operator reviewed the first 20 positional surfaces from the novel WordNet unbind pool. None are admitted as slang. Eighteen are refused. `give a damn` and `in one's birthday suit` are quarantined for provenance review. Slang, idiom, colloquialism, and profanity stay distinct. No `ACCEPT`, `REJECT`, `CORRECT_TARGET`, or `UNRESOLVED` was written to the unbind settlement log. The ledger was not appended. Events sha256 remains `96b74a92d44f1cf9fe152b18e5207176f161ba3bfce528dac38aa4571a742f9c`. These rows are not classify gold and must not be trained as slang. `HLX-EXP-2026-09-27-SELECT-005` is not sealed. Disposition packet `HLX-EVAL-UNBIND-PREVIEW-2026-09-27-001`. + +## Unbind preview roles — 2026-09-27 + +Slang classification and structure unbinding stay orthogonal. On the same 20-surface preview, the operator marked 7 high-value unbind candidates, 8 secondary candidates, and 5 rejects. The rejects are `beta vulgaris`, `gulf of oman`, `u. s. air force`, `department of the federal government`, and `court of assize and nisi prius`. The slang disposition is unchanged: none admitted, 18 refused, and `give a damn` plus `in one's birthday suit` quarantined as slang. No unbind settlement decision was written. The 15 candidates are not admitted. The ledger was not appended. Events sha256 remains `96b74a92d44f1cf9fe152b18e5207176f161ba3bfce528dac38aa4571a742f9c`. `HLX-EXP-2026-09-27-SELECT-005` is not sealed. Packet `HLX-EVAL-UNBIND-PREVIEW-2026-09-27-001`. + +## Unbind screen specified — 2026-09-27 + +`RUNE.UNBIND_SCREEN.v1` is a candidate-selection rule, not an admission filter. Specification agreement with the 20-item preview is 20/20. That figure is specification fit, not held-out precision. A held-out validation sample of 32 admissible positional surfaces, excluding those 20, is recorded with proposed buckets and `gold` null. Provisional screen counts on 63882 admissible positional surfaces are high-value 1965, secondary 60559, reject 1358. Those counts are not a draw. No settlement was written. The ledger was not appended. Events sha256 remains `96b74a92d44f1cf9fe152b18e5207176f161ba3bfce528dac38aa4571a742f9c`. `HLX-EXP-2026-09-27-SELECT-005` is not sealed. Packet `HLX-EVAL-UNBIND-SCREEN-2026-09-27-001`. + +## Unbind screen v2 — 2026-09-27 + +`RUNE.UNBIND_SCREEN.v2` replaces the v1 proposal as the screening hypothesis. It is not authorized as the SELECT-005 screen. Surface patterns remain candidate-generation heuristics. Hard exclusions are proper name, titled entity, taxonomy, productive number, and unconstrained free composition. Agreement after the revision is 20/20 on the first preview and 32/32 on the reviewed sample. That agreement is fit, not held-out precision. A new validation sample excludes all 52 reviewed surfaces and carries `gold` null. No settlement was written. The ledger was not appended. Events sha256 remains `96b74a92d44f1cf9fe152b18e5207176f161ba3bfce528dac38aa4571a742f9c`. `HLX-EXP-2026-09-27-SELECT-005` is not sealed. Packet `HLX-EVAL-UNBIND-SCREEN-2026-09-27-002`. + +## Unbind screen v3 — 2026-09-27 + +`RUNE.UNBIND_SCREEN.v3` is a hypothesis. It is not authorized as the SELECT-005 screen. Candidate-generation patterns do not assign high-value. The automatic screen emits reject or unresolved only. v2 pool counts stay frozen at high-value 1977, secondary 55427, reject 6478, over 63882 positional surfaces, and were not recomputed. No settlement was written. The ledger was not appended. Events sha256 remains `96b74a92d44f1cf9fe152b18e5207176f161ba3bfce528dac38aa4571a742f9c`. `HLX-EXP-2026-09-27-SELECT-005` is not sealed. Packet `HLX-EVAL-UNBIND-SCREEN-2026-09-27-003`. + +## Unbind screen v3 held out — 2026-09-27 + +`RUNE.UNBIND_SCREEN.v3` stays a proposed refinement. Fifty-two surfaces are frozen as development data and thirty-two as validation-development data. Agreement on those eighty-four is fit, not held-out precision. A fresh sample of 29 admissible positional surfaces excludes all 84. Predictions were not hand-corrected. Held-out precision is not computed. v2 pool counts stay frozen at high-value 1977, secondary 55427, reject 6478, over 63882 positional surfaces. The v3 screen was not run on that pool. No settlement was written. The ledger was not appended. Events sha256 remains `96b74a92d44f1cf9fe152b18e5207176f161ba3bfce528dac38aa4571a742f9c`. `HLX-EXP-2026-09-27-SELECT-005` is not sealed. Packet `HLX-EVAL-UNBIND-SCREEN-2026-09-27-004`. + +## Unbind held-out frozen — 2026-09-27 + +The 29-row v3 application is frozen. Sample sha256 `8af5644061a7a60fc5620c217e15a4ec8145f170edee9d8ff4e2999e7b86605e`. It was produced by one application of `RUNE.UNBIND_SCREEN.v3` and was not hand-corrected. Operator labels are pending. Held-out precision is `NOT_COMPUTABLE`. v3 was not revised. Development data remain 52 rows. Validation-development data remain 32 rows. Admitted 0. Settled 0. Gold 0. The ledger was not appended. Events sha256 remains `96b74a92d44f1cf9fe152b18e5207176f161ba3bfce528dac38aa4571a742f9c`. `HLX-EXP-2026-09-27-SELECT-005` is not authorized and is not sealed. + +## Unbind screen evaluation lane — 2026-09-27 + +`HLX-EVAL-UNBIND-SCREEN-V3-001` evaluates the frozen 29-row v3 application. Source sample sha256 remains `8af5644061a7a60fc5620c217e15a4ec8145f170edee9d8ff4e2999e7b86605e`. Prediction, operator judgment, gold, admission, and settlement are separate artifacts. The blind review does not carry the predicted bucket. Operator labels are pending. Held-out precision and the confusion matrix are `NOT_COMPUTABLE`. A scored report does not authorize `HLX-EXP-2026-09-27-SELECT-005`. The ledger was not appended. Events sha256 remains `96b74a92d44f1cf9fe152b18e5207176f161ba3bfce528dac38aa4571a742f9c`. + +## Unbind screen held-out scored — 2026-09-27 + +Operator labels for HLX-EVAL-UNBIND-SCREEN-V3-001 are frozen. Label sha256 `4e7bae5986e6345de62086af270a1d1a6902103d69a50d8f0b1e4e0fe01ecde5`. The source sample sha256 remains `8af5644061a7a60fc5620c217e15a4ec8145f170edee9d8ff4e2999e7b86605e`. Resolved accuracy is 16/29. High precision is 1 and recall is 6/13. Secondary precision is 4/17 and recall is 1. Reject precision is 1 and recall is 6/12. Quarantine support is 0. Every error is false secondary: 7 operator-high and 6 operator-reject. False high and false reject are 0. revision_eligible stays false. SELECT-005 is not authorized. The ledger was not appended. Events sha256 remains `96b74a92d44f1cf9fe152b18e5207176f161ba3bfce528dac38aa4571a742f9c`. + +## Unbind screen v4 hypothesis — 2026-09-28 + +The v3 held-out score stays 16/29. All 13 errors are false secondary: 7 operator-high and 6 operator-reject. False high and false reject are 0. Those 29 rows are now v4 development evidence, not a validation set. `RUNE.UNBIND_SCREEN.v4` is drafted and not encoded, applied, or authorized. It would only add coverage around the secondary basin: normalized productive numbers, multi-token personal names, organization glosses, species common names, and medical technical phrases on one side; nonliteral and conventionalized gloss evidence on the other. Existing high and reject decisions stay in place. `revision_eligible` stays false. SELECT-005 is not authorized. The ledger was not appended. Events sha256 remains `96b74a92d44f1cf9fe152b18e5207176f161ba3bfce528dac38aa4571a742f9c`. + +## Unbind screen v4 acceptance frozen — 2026-09-28 + +`RUNE.UNBIND_SCREEN.v4.ACCEPTANCE` is frozen and the screen is not encoded. Acceptance sha256 `ffb39e38784a56ae15bae51718c61b78fc861e48399936dbed57fb7d0754c55b`. v4 may only move additional secondary fall-throughs, and only by semantic evidence classes. It must not reinterpret v3 high or reject logic, redefine secondary, or train on a future validation sample. Phrase-specific exceptions are prohibited. The 113 reviewed surfaces are the later regression set. The next measurement sample must exclude them and be frozen before inspection. `revision_eligible` stays false. SELECT-005 is not authorized. The v3 sample and label hashes are unchanged. The ledger was not appended. Events sha256 remains `96b74a92d44f1cf9fe152b18e5207176f161ba3bfce528dac38aa4571a742f9c`. + +## Unbind screen v4 encoded — 2026-09-28 + From 34e6ec9a89d4e6578549d90d54ec892d9055cd62 Mon Sep 17 00:00:00 2001 From: Daniel Meyer Date: Mon, 28 Sep 2026 16:44:19 -0700 Subject: [PATCH 29/42] feat(007): stage evaluation-reserve chunk (02) --- .land-chunks/part-02 | 140 +++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 140 insertions(+) create mode 100644 .land-chunks/part-02 diff --git a/.land-chunks/part-02 b/.land-chunks/part-02 new file mode 100644 index 00000000..9aa4de80 --- /dev/null +++ b/.land-chunks/part-02 @@ -0,0 +1,140 @@ +`RUNE.UNBIND_SCREEN.v4` is encoded as a wrapper over a frozen v3 bucket. It inspects a row only when that bucket is secondary. Patch A may move secondary to reject. Patch B may move secondary to high. Existing high and reject decisions are not reopened. The acceptance contract is unchanged, sha256 `ffb39e38784a56ae15bae51718c61b78fc861e48399936dbed57fb7d0754c55b`. + +The 113 reviewed surfaces were replayed as a regression suite. Gate A through Gate E passed. High rows unchanged: 39. Reject rows unchanged: 28. Secondary moves: 13 to high and 6 to reject. Each move has one Patch A or Patch B evidence code. No phrase-specific rule fired. Operator conflict on those moves: 0. This replay is not a new precision estimate. + +Success criteria were frozen before the measurement draw, sha256 `4fcbebfad7797eb22393fa94a389f1ef41402612e359b63d3a3a4b031c6cf8be`. High and reject precision floors are the v3 held-out floors of 1. The false-secondary rate must be strictly below 13/29. False high and false reject are not allowed. Perfect accuracy is not required. + +The measurement sample excludes all 113 reviewed surfaces and duplicate normalized lexical identities. It is stratified by source part of speech and token count, two rows from each occupied cell. Occupied cells produced 28 rows. Sample sha256 `dae8851134aa960a13e072ae017428054c68b988c8d7e6f86d8cab2d16c2586b`. v4 was applied once. Hand corrections are 0. Operator labels are absent. Precision is `NOT_COMPUTABLE`. `revision_eligible` stays false. `HLX-EXP-2026-09-27-SELECT-005` is not authorized. The ledger was not appended. Events sha256 remains `96b74a92d44f1cf9fe152b18e5207176f161ba3bfce528dac38aa4571a742f9c`. The v3 sample sha256 `8af5644061a7a60fc5620c217e15a4ec8145f170edee9d8ff4e2999e7b86605e` and label sha256 `4e7bae5986e6345de62086af270a1d1a6902103d69a50d8f0b1e4e0fe01ecde5` are unchanged. + +## Unbind screen v4 measurement scored — 2026-09-28 + +Operator labels for the 28-row v4 measurement sample are frozen. Label sha256 `023691f8349f0dda12c234691f235ae109289fcf9eab86ec20be1e23bfed9463`. The sample sha256 remains `dae8851134aa960a13e072ae017428054c68b988c8d7e6f86d8cab2d16c2586b`. The prediction sha256 remains `a854847e516fbcd37fbb221456e8caf8c420552795ac7fc6225960bb5434084f`. Labels were recorded at `2026-09-28T02:17:00Z`, after the sample freeze at `2026-09-28T01:07:34Z`. Hand corrections are 0. v4 was not applied again. + +Resolved accuracy is 16/28. High precision is 1 (7/7) and recall is 7/13. Reject precision is 1 (5/5) and recall is 5/11. Secondary precision is 4/16 and recall is 4/4. Quarantine support is 0. False high is 0. False reject is 0. False secondary is 12/28, which is below the frozen floor of 13/29. Every error is a secondary fall-through. The pre-registered success criteria pass, so `revision_eligible` is true. `HLX-EXP-2026-09-27-SELECT-005` is not authorized. Admitted 0. Settled 0. Gold 0. The ledger was not appended. Events sha256 remains `96b74a92d44f1cf9fe152b18e5207176f161ba3bfce528dac38aa4571a742f9c`. + +## Unbind screen v5 hypothesis — 2026-09-28 + +The v4 measurement artifact and score receipt stay frozen. Sample sha256 `dae8851134aa960a13e072ae017428054c68b988c8d7e6f86d8cab2d16c2586b`. Prediction sha256 `a854847e516fbcd37fbb221456e8caf8c420552795ac7fc6225960bb5434084f`. Label sha256 `023691f8349f0dda12c234691f235ae109289fcf9eab86ec20be1e23bfed9463`. Acceptance sha256 `ffb39e38784a56ae15bae51718c61b78fc861e48399936dbed57fb7d0754c55b`. Resolved accuracy remains 16/28. High precision remains 1. Reject precision remains 1. False secondary remains 12/28. `revision_eligible` on that measurement remains true. + +Those 28 rows are now v5 development evidence, not a validation set. Evidence sha256 `dcab832038c3209a54a4159b23caf4eecfb94864cef7188af28f1ae3b0ff80c0`. The twelve secondary fall-throughs are two escape routes only: referential or terminological rows that stayed secondary, and lexicalized noncompositional rows that stayed secondary. They are not a fit list. + +`RUNE.UNBIND_SCREEN.v5` is drafted and not encoded, applied, or authorized. It is a wrapper over a frozen v4 bucket. It inspects a row only when that bucket is secondary. One referential/terminological dominance test may move secondary to reject. One lexicalized noncompositionality test may move secondary to high, and only when the surface is conventionalized and the gloss is not compositionally recoverable. A lexicalized and mostly compositional surface stays secondary. Existing high and reject decisions stay in place. A separate rule for each miss is prohibited. `HLX-EXP-2026-09-27-SELECT-005` is not authorized. Admitted 0. Settled 0. Gold 0. The ledger was not appended. Events sha256 remains `96b74a92d44f1cf9fe152b18e5207176f161ba3bfce528dac38aa4571a742f9c`. + +## Unbind screen v5 acceptance frozen — 2026-09-28 + +`RUNE.UNBIND_SCREEN.v5.ACCEPTANCE` is frozen and the screen is not encoded. Acceptance sha256 `752fd459658f636df91a8da9f1a701a0de8e3240d6df12072bbd2b7ac4cc0b7a`. The draft hypothesis sha256 is `c3d3f8081e18582158eab7a1bf154a02aed38614b9801df6a83f7eaf3875da7b`. v5 may only move additional secondary fall-throughs, and only through the two general tests named in the contract. It must not reinterpret v3 or v4 high or reject logic, redefine secondary, redesign the classifier, or train on a future validation sample. Phrase-specific exceptions are prohibited. One executable rule per observed miss is prohibited. + +The later regression replays all 141 reviewed surfaces: 52 development, 32 validation-development, 29 from the v3 held-out application, and 28 from the v4 measurement. The 113-row v4 replay and the 28-row measurement sample are disjoint. Every previously correct high stays high. Every previously correct reject stays reject. Every new move originates from secondary. The next measurement sample must exclude all 141 and be frozen before inspection. v5 is applied once. Operator labels are collected independently. + +The pre-registered bar is high precision 1, reject precision 1, and a false-secondary rate strictly below 12/28. No accuracy-gain target is registered. Perfect accuracy is not required. `revision_eligible` on the v4 measurement stays true and does not authorize `HLX-EXP-2026-09-27-SELECT-005`. The v4 sample, prediction, label, and acceptance hashes are unchanged. The ledger was not appended. Events sha256 remains `96b74a92d44f1cf9fe152b18e5207176f161ba3bfce528dac38aa4571a742f9c`. + +## Unbind screen v5 encoded — 2026-09-28 + +`RUNE.UNBIND_SCREEN.v5` is encoded as a wrapper over a frozen v4 bucket. It inspects a row only when that bucket is secondary. Referential or terminological dominance may move secondary to reject. A conventionalized surface whose gloss is not recoverable from the ordinary first senses of its constituents may move secondary to high. Existing high and reject decisions are not reopened. The acceptance contract is unchanged, sha256 `752fd459658f636df91a8da9f1a701a0de8e3240d6df12072bbd2b7ac4cc0b7a`. + +The 141 reviewed surfaces were replayed as a regression suite. High rows unchanged: 59. Reject rows unchanged: 39. Secondary moves: 4 to high and 7 to reject. Each move has the one evidence code for its patch. No phrase-specific rule fired. Operator conflict on those moves: 0. This replay is not a new precision estimate. Gate report sha256 `c70214ac1ea3f24b0b8c5590b015a64de4f5a7a632cf372f02aa9e88bd5b2977`. + +The pre-registered bar is unchanged: high precision 1, reject precision 1, and a false-secondary rate strictly below 12/28. Criteria sha256 `49e179db537a738bb7370404167c7ec1c7019fcd251d77e6a916f4426a0ecccb`. No accuracy-gain target was added. + +The measurement sample excludes all 141 reviewed surfaces and duplicate normalized lexical identities. It is stratified by source part of speech and token count, two rows from each occupied cell. Occupied cells produced 28 rows. Sample sha256 `a462f08307e62b09fdfe1dfb6e9a86ec3ea207db27e917730b0d244c47c73359`. Prediction sha256 `51bcf051fb1a4b9bf67a28fe7d1235c8922e3a887bb908de32e5ac0d78a6408f`. v5 was applied once. Hand corrections are 0. Operator labels are absent. Precision is `NOT_COMPUTABLE`. State is `MEASUREMENT_ELIGIBLE`. `revision_eligible` on this screen stays false. `revision_eligible` on the v4 measurement stays true. `HLX-EXP-2026-09-27-SELECT-005` is not authorized. Admitted 0. Settled 0. Gold 0. The ledger was not appended. Events sha256 remains `96b74a92d44f1cf9fe152b18e5207176f161ba3bfce528dac38aa4571a742f9c`. + +## Unbind screen v5 measurement scored — 2026-09-28 + +Operator labels for the 28-row v5 measurement sample are frozen. Each label carries only the row identity, the surface, the operator bucket, and the operator reason. Label sha256 `b05dc1c35d3d10eed4b615ca1ee8251a875a450f57560fef4b5a84b3e1fc075f`. The row table is high 11, secondary 8, reject 9, quarantine 0, unresolved 0. The seal line said high 10, secondary 8, reject 10. The frozen artifact follows the row table. + +The sample sha256 remains `a462f08307e62b09fdfe1dfb6e9a86ec3ea207db27e917730b0d244c47c73359`. The prediction sha256 remains `51bcf051fb1a4b9bf67a28fe7d1235c8922e3a887bb908de32e5ac0d78a6408f`. Labels were recorded at `2026-09-28T03:03:27Z`, after the sample freeze at `2026-09-28T02:44:48Z`. Hand corrections are 0. v5 was not applied again. The 141-row regression remains a regression result and is not a generalization estimate. + +Resolved accuracy is 17/28. High precision is 6/7 and recall is 6/11. Reject precision is 5/6 and recall is 5/9. Secondary precision is 6/15 and recall is 6/8. Quarantine support is 0. False high is 1. False reject is 1. False secondary is 9/28: 5 operator-high and 4 operator-reject. Accuracy was recorded and was not part of the bar. + +False high is 1 and false reject is 1, so the outer-bucket bar fails. The false high is `keep out`, which v4 had already marked high and v5 passed through. The false reject is `on the job`, which v5 moved from secondary to reject on referential/terminological dominance. `revision_eligible` stays false. The screen was not retuned. `HLX-EXP-2026-09-27-SELECT-005` is not authorized. Admitted 0. Settled 0. Gold 0. The ledger was not appended. Events sha256 remains `96b74a92d44f1cf9fe152b18e5207176f161ba3bfce528dac38aa4571a742f9c`. `revision_eligible` on the v4 measurement stays true. Acceptance sha256 remains `752fd459658f636df91a8da9f1a701a0de8e3240d6df12072bbd2b7ac4cc0b7a`. + +## Unbind screen v6 hypothesis — 2026-09-28 + +The v5 measurement stays scored as an outer-bucket failure. Sample sha256 `a462f08307e62b09fdfe1dfb6e9a86ec3ea207db27e917730b0d244c47c73359`. Prediction sha256 `51bcf051fb1a4b9bf67a28fe7d1235c8922e3a887bb908de32e5ac0d78a6408f`. Label sha256 `b05dc1c35d3d10eed4b615ca1ee8251a875a450f57560fef4b5a84b3e1fc075f`. False high is 1. False reject is 1. High precision is 6/7. Reject precision is 5/6. False secondary is 9/28. Resolved accuracy is 17/28 and is descriptive only. `revision_eligible` on v5 stays false. + +The error analysis is frozen and authorizes no fix. Analysis sha256 `ecb229dccec627af958d12537ab153d35de9e3e58d71bb8d3f0a8835077b26ac`. One inherited high overreach: `keep out` was already high at v4, and v5 passed it through against an operator secondary label. One new reject overreach: `on the job` moved from secondary to reject under referential/terminological dominance against an operator secondary label. Secondary undercoverage remains 5 operator-high rows and 4 operator-reject rows. These 28 rows are v6 development evidence, not a validation set. Evidence sha256 `daf512dd331216fecb87ec4d4890c83a03afaf78b5fc1ca6dae6e46e1a9f63a9`. + +`RUNE.UNBIND_SCREEN.v6` is drafted and not encoded, applied, or authorized. Draft sha256 `942eeab825d1c89c21e91af84da0d753281a91012e9ff219a633d27fe6648aa5`. It is a structural revision, not another secondary-only wrapper. A frozen v5 bucket is provisional. High may fall to secondary only with compositional recoverability. Reject may fall to secondary only with nonreferential lexical use. A demotion stops at secondary for that application. A provisional secondary row may still move by the two existing evidences: lexicalized noncompositional to high, and referential/terminological dominance to reject. High and reject do not swap directly. The motivating surfaces are not a required fit. `HLX-EXP-2026-09-27-SELECT-005` is not authorized. Admitted 0. Settled 0. Gold 0. The ledger was not appended. Events sha256 remains `96b74a92d44f1cf9fe152b18e5207176f161ba3bfce528dac38aa4571a742f9c`. + +## Unbind screen v6 acceptance frozen — 2026-09-28 + +`RUNE.UNBIND_SCREEN.v6.ACCEPTANCE` is frozen and the screen is not encoded. Acceptance sha256 `68c60dfe9efaf9f79bc445b974c5436d31b60b77fe7374b17103f3b74865610f`. The draft hypothesis sha256 is `942eeab825d1c89c21e91af84da0d753281a91012e9ff219a633d27fe6648aa5`. The error analysis sha256 is `ecb229dccec627af958d12537ab153d35de9e3e58d71bb8d3f0a8835077b26ac`. The development evidence sha256 is `daf512dd331216fecb87ec4d4890c83a03afaf78b5fc1ca6dae6e46e1a9f63a9`. + +The replay set is all 169 reviewed surfaces: 52 development, 32 validation-development, 29 from the v3 held-out application, 28 from the v4 measurement, and 28 from the v5 measurement. The v5 measurement is disjoint from the prior 141. Previously operator-correct rows must remain operator-correct. High falls to secondary only with compositional recoverability. Reject falls to secondary only with nonreferential lexical use. Secondary rises to high only with lexicalized noncompositional evidence. Secondary falls to reject only with referential/terminological dominance. Direct movement between high and reject is prohibited. Phrase-specific rules are prohibited. + +The pre-registered measurement bar is high precision 1 and reject precision 1, with false high and false reject at 0. No false-secondary quota and no accuracy-gain target are registered. The next measurement sample must exclude all 169 reviewed surfaces and be frozen before inspection. `revision_eligible` on v5 stays false. `revision_eligible` on the v4 measurement stays true. `HLX-EXP-2026-09-27-SELECT-005` is not authorized. The v5 sample, prediction, label, and acceptance hashes are unchanged. The ledger was not appended. Events sha256 remains `96b74a92d44f1cf9fe152b18e5207176f161ba3bfce528dac38aa4571a742f9c`. + +## Unbind screen v6 encoded — 2026-09-28 + +`RUNE.UNBIND_SCREEN.v6` is encoded over a frozen v5 bucket. High may fall to secondary only when the gloss is recoverable from the ordinary first senses of at least two constituents and the synset has no unrelated single-word mapping. Reject may fall to secondary only when a pertainym is used as a state or relation whose gloss meets those ordinary senses, and not when the gloss is a relational designation. A demotion stops at secondary for that application. A provisional secondary row may still move by lexicalized noncompositional evidence or referential/terminological dominance. High and reject do not swap. The acceptance contract is unchanged, sha256 `68c60dfe9efaf9f79bc445b974c5436d31b60b77fe7374b17103f3b74865610f`. + +The 169 reviewed surfaces were replayed. Previously correct rows lost: 0. Direct swaps between high and reject: 0. High to secondary: 0. Reject to secondary: 1. That row is `on the job`, evidence `nonreferential_lexical_use`, and the operator label is secondary. Secondary to high: 0. Secondary to reject: 0. No phrase-specific rule fired. `keep out` stays high. Its gloss is not covered by the ordinary first senses of its constituents, and the synset maps it to an unrelated word, so compositional recoverability does not fire. That miss is not a required fit. Unchanged disagreements: 16. This replay is not a precision estimate. Gate report sha256 `66e4f3859738dfaa63a21ec2e69025b568ea097c17f509ceed4277022d979ffb`. Prediction sha256 `7398254bd62253129153e7b57c99122ed7cc7157520788cc60bb0e80f0b356e8`. Diff sha256 `3af430bb9261c1cedc1bd6181f5e928e01eef22bccea0b81c3c435073fabff59`. + +The pre-registered bar is unchanged: high precision 1, reject precision 1, false high 0, and false reject 0. No false-secondary floor and no accuracy target are registered. Criteria sha256 `3a511ffb49f32930ead14ca2f1e2f6639122c47a7ceec9d1bc29815dd7b490dd`. + +The measurement sample excludes all 169 reviewed surfaces and duplicate normalized lexical identities. It is stratified by source part of speech and token count, two rows from each occupied cell. Occupied cells produced 28 rows. Sample sha256 `8ee516423f002a355759eed788bfe3bef81332fad61b1f19c7e46bd0722a29f2`. Prediction sha256 `476c5526b18fcb999f2a5397d0674ec07e29b199d5a5259afb555bf608d245ee`. v6 was applied once. Hand corrections are 0. Operator labels are absent. Precision is `NOT_COMPUTABLE`. State is `MEASUREMENT_ELIGIBLE`. `revision_eligible` on this screen stays false. `revision_eligible` on v5 stays false. `revision_eligible` on the v4 measurement stays true. `HLX-EXP-2026-09-27-SELECT-005` is not authorized. Admitted 0. Settled 0. Gold 0. The ledger was not appended. Events sha256 remains `96b74a92d44f1cf9fe152b18e5207176f161ba3bfce528dac38aa4571a742f9c`. + +## Unbind screen v6 measurement scored — 2026-09-28 + +Operator labels for the 28-row v6 measurement sample are frozen. Each label carries only the row identity, the surface, the operator bucket, and the operator reason. Label sha256 `feed0ee131a513f2801bd1725f553bb6274df1378b28fdea1cb13e238ea9eb11`. The row table is high 12, secondary 4, reject 12, quarantine 0, unresolved 0. The seal line said high 11, secondary 4, reject 13. The frozen artifact follows the row table. + +The sample sha256 remains `8ee516423f002a355759eed788bfe3bef81332fad61b1f19c7e46bd0722a29f2`. The prediction sha256 remains `476c5526b18fcb999f2a5397d0674ec07e29b199d5a5259afb555bf608d245ee`. Labels were recorded at `2026-09-28T03:34:48Z`, after the sample freeze at `2026-09-28T03:25:47Z`. Hand corrections are 0. v6 was not applied again. The 169-row regression remains a regression result and is not a generalization estimate. + +Resolved accuracy is 21/28 and is descriptive only. High precision is 9/10 and recall is 9/12. Reject precision is 1 (9/9) and recall is 9/12. Secondary precision is 3/9 and recall is 3/4. Quarantine support is 0. False high is 1. False reject is 0. False secondary is 6/28: 3 operator-high and 3 operator-reject. Recall and accuracy were not part of the bar. + +A false high or false reject is present, so the outer-bucket bar fails. `revision_eligible` stays false. The screen was not retuned. `HLX-EXP-2026-09-27-SELECT-005` is not authorized. Admitted 0. Settled 0. Gold 0. The ledger was not appended. Events sha256 remains `96b74a92d44f1cf9fe152b18e5207176f161ba3bfce528dac38aa4571a742f9c`. `revision_eligible` on v5 stays false. `revision_eligible` on the v4 measurement stays true. Acceptance sha256 remains `68c60dfe9efaf9f79bc445b974c5436d31b60b77fe7374b17103f3b74865610f`. + +## Unbind screen v7 hypothesis — 2026-09-28 + +The v6 measurement stays scored as an outer-bucket failure. Sample sha256 `8ee516423f002a355759eed788bfe3bef81332fad61b1f19c7e46bd0722a29f2`. Prediction sha256 `476c5526b18fcb999f2a5397d0674ec07e29b199d5a5259afb555bf608d245ee`. Label sha256 `feed0ee131a513f2801bd1725f553bb6274df1378b28fdea1cb13e238ea9eb11`. High precision is 9/10. Reject precision is 1 (9/9). False high is 1. False reject is 0. False secondary is 6/28. Resolved accuracy is 21/28 and is descriptive only. `revision_eligible` on v6 stays false. `revision_eligible` on v5 stays false. `revision_eligible` on the v4 measurement stays true. + +The only false high in that measurement is `to a lesser extent`. The operator bucket is secondary. v3, v4, v5, and v6 are high, and v6 recorded no evidence code. A prior inherited high disagreement, `keep out`, stays high in the 169-row replay. Its operator bucket is secondary and `compositional_recoverability` did not fire. That v6 predicate is not redefined. Secondary fall-throughs are 13/29 at v3, 12/28 at v4, 9/28 at v5, and 6/28 at v6. Coverage is not the next question. + +The error analysis is frozen and authorizes no fix. Analysis sha256 `06a2f10493640d6536b45ef3d9405470c12623bfa787f7fba49b3f64498fb766`. These 28 rows are v7 development evidence, not a validation set. Evidence sha256 `3d457801203c69ceb13c6cefbf5d82fe9cac0daea48f26eb2642d8673585a3a3`. The six false-secondary rows stay unresolved and are not a fit list: `to the letter`, `with child`, `dressed to the nines`, `union jack`, `atomic number 98`, and `law of definite proportions`. Known reviewed inventory is 197: the prior 169 plus these 28. Normalized overlap is 0. + +`RUNE.UNBIND_SCREEN.v7` is specified and not encoded, applied, or authorized. State is `SPEC_FROZEN`. Draft sha256 `c168bf975e26804a032956d570e39a3d2c7078b407ef966da33083adecf5585b`. It keeps the frozen v6 phase order and replaces only the high challenge. A provisional high may fall to secondary only with `ordinary_compositional_derivation`: the WordNet sense can be derived by ordinary grammatical, syntactic, comparative, or phrasal composition without a stored conventionalized or nonliteral lexical binding. The test does not require recovery from only the ordinary first sense of each token. It asks whether lexicalized binding is required at all. A demotion stops at secondary for that application. Reject may still fall to secondary only with `nonreferential_lexical_use`. A provisional secondary row may still move by the unchanged v6 evidences. High and reject do not swap. `to a lesser extent` and `keep out` motivate the question and are not required fits, and neither is special-cased. No SELECT-005 admission surface exists. `HLX-EXP-2026-09-27-SELECT-005` is not authorized. Admitted 0. Settled 0. Gold 0. The ledger was not appended. Events sha256 remains `96b74a92d44f1cf9fe152b18e5207176f161ba3bfce528dac38aa4571a742f9c`. + +## Unbind screen v7 acceptance frozen — 2026-09-28 + +`RUNE.UNBIND_SCREEN.v7.ACCEPTANCE` is frozen and the screen is not encoded. State is `SPEC_FROZEN`. Acceptance sha256 `562c0756b0337e2fb10643f4fd6689ea4421977a345d12fe33d8a1504161cac5`. The draft hypothesis sha256 is `c168bf975e26804a032956d570e39a3d2c7078b407ef966da33083adecf5585b`. The error analysis sha256 is `06a2f10493640d6536b45ef3d9405470c12623bfa787f7fba49b3f64498fb766`. The development evidence sha256 is `3d457801203c69ceb13c6cefbf5d82fe9cac0daea48f26eb2642d8673585a3a3`. + +The replay set is all 197 reviewed surfaces: 52 development, 32 validation-development, 29 from the v3 held-out application, 28 from the v4 measurement, 28 from the v5 measurement, and 28 from the v6 measurement. The v6 measurement is disjoint from the prior 169. Previously operator-correct rows must remain operator-correct. A previously correct high stays high. High falls to secondary only with ordinary compositional derivation. The frozen v6 transitions for secondary to high, secondary to reject, and reject to secondary are unchanged. Direct movement between high and reject is prohibited. A high demotion stops at secondary and is not re-promoted in the same application. Phrase-specific rules, row-id rules, and surface-hash rules are prohibited. + +No measurement sample was drawn. The bar proposed for a later unseen sample is high precision 1 and reject precision 1, with false high and false reject at 0. No false-secondary quota, no accuracy target, and no recall target are registered. That sample must exclude all 197 reviewed normalized identities and be frozen before inspection. `revision_eligible` stays false on v7, v6, and v5. `revision_eligible` on the v4 measurement stays true. `HLX-EXP-2026-09-27-SELECT-005` is not authorized. No admission surface for that experiment exists. The v6 sample, prediction, label, criteria, and acceptance hashes are unchanged. The ledger was not appended. Events sha256 remains `96b74a92d44f1cf9fe152b18e5207176f161ba3bfce528dac38aa4571a742f9c`. + +## Unbind screen v7 encoded — 2026-09-28 + +`RUNE.UNBIND_SCREEN.v7` is encoded over a frozen v6 bucket. The only new predicate is `ordinary_compositional_derivation`. It inspects a provisional high. The recorded sense must be a comparative, syntactic, or phrasal composition, and the synset must not store an unrelated single-word synonym. A gloss that merely shares constituent stems does not fire, and a metaphorical retelling does not fire. A demotion stops at secondary. Reject rows still use the v6 reject challenge. Secondary rows are not reopened, so a demotion v6 already stopped stays stopped. `compositional_recoverability` is not a v7 transition. The acceptance contract is unchanged, sha256 `562c0756b0337e2fb10643f4fd6689ea4421977a345d12fe33d8a1504161cac5`. + +The 197 reviewed surfaces were replayed as development and regression evidence, not as a generalization estimate. Previously correct rows lost: 0. Previously correct high rows demoted: 0. Direct swaps between high and reject: 0. High to secondary: 1. Reject to secondary: 0. Secondary to high: 0. Secondary to reject: 0. One row moves. `to a lesser extent` goes from high to secondary with `ordinary_compositional_derivation`, and the operator bucket is secondary. That firing is not a required fit. `keep out` stays high. No phrase-specific rule fired. This replay is not a precision estimate. Gate report sha256 `78225b68c639694bb4c17342c87320ccb44faac557a9145ea117603da0d545ed`. Prediction sha256 `24f266e16b80da602b011bf7cca13774ce9f76c0c52e81e600a1d9743d61d197`. Diff sha256 `b86ae6611a5fcd233b6e92b3def4cdd0680018902532a0a0d5fb02659ad092ae`. + +State is `REGRESSION_VERIFIED`. `measurement_eligible` stays false. The unseen sample was not drawn. The pre-registered bar remains high precision 1, reject precision 1, false high 0, and false reject 0, with no false-secondary quota, no accuracy target, and no recall target. No measurement sample was drawn. `revision_eligible` stays false on v7, v6, and v5. `revision_eligible` on the v4 measurement stays true. `HLX-EXP-2026-09-27-SELECT-005` is not authorized. Admitted 0. Settled 0. Gold 0. The ledger was not appended. Events sha256 remains `96b74a92d44f1cf9fe152b18e5207176f161ba3bfce528dac38aa4571a742f9c`. + +## Unbind screen v7 measurement frozen — 2026-09-28 + +The 197-row regression stays `REGRESSION_VERIFIED`. One unseen measurement sample excludes those 197 normalized identities. Normalized overlap with the reviewed set is 0. Duplicate normalized identities inside the sample are 0. The draw is deterministic and stratified by source part of speech and token count, two rows from each occupied cell. Occupied cells produced 28 rows. No occupied cell was exhausted. Each occupied cell contributed 2 rows. + +The sample was frozen before v7 was applied. Sample sha256 `2b4414e2a6db3acf2967603e3ef4552c631803285633fbae5f3f3d50451724b1`. v7 was then applied once. Hand corrections are 0. Prediction sha256 `d201affe0c87206227bc211a6071a25e639c0ffb7cbcfdc8dac0572155a4da47`. The blind rows carry surface, part of speech, token count, and gloss. They do not carry a bucket or an evidence code. Operator labels are absent. Precision is `NOT_COMPUTABLE`. State is `MEASUREMENT_ELIGIBLE`. + +The acceptance bar is unchanged: high precision 1, reject precision 1, false high 0, and false reject 0. No false-secondary floor, no accuracy target, and no recall target were added. Criteria sha256 `3b22449874f2384d843e4f16cb7e5c28b6acfc83273aec44542e0bc5b0c3ef37`. `revision_eligible` stays false. `HLX-EXP-2026-09-27-SELECT-005` is not authorized. Admitted 0. Settled 0. Gold 0. The ledger was not appended. Events sha256 remains `96b74a92d44f1cf9fe152b18e5207176f161ba3bfce528dac38aa4571a742f9c`. + +## Unbind screen v7 measurement scored — 2026-09-28 + +Operator labels for the 28-row v7 measurement sample are frozen. Each label carries only the row identity, the surface, the operator bucket, and the operator reason. Label sha256 `0ce39dffad49b28b5adebfbe3514fcf81f9ca39689d3eadf0695975496b6adb5`. The row table is high 14, secondary 5, reject 9, quarantine 0, unresolved 0. The seal line said high 13, secondary 5, reject 10. The frozen artifact follows the row table. + +The sample sha256 remains `2b4414e2a6db3acf2967603e3ef4552c631803285633fbae5f3f3d50451724b1`. The prediction sha256 remains `d201affe0c87206227bc211a6071a25e639c0ffb7cbcfdc8dac0572155a4da47`. Labels were recorded at `2026-09-28T04:14:27Z`, after the sample freeze at `2026-09-28T04:05:23Z`. Hand corrections are 0. v7 was not applied again. The 197-row regression remains a regression result and is not a generalization estimate. + +Resolved accuracy is 21/28 and is descriptive only. High precision is 10/11 and recall is 10/14. Reject precision is 7/8 and recall is 7/9. Secondary precision is 4/9 and recall is 4/5. Quarantine support is 0. False high is 1. False reject is 1. False secondary is 5/28: 3 operator-high and 2 operator-reject. Recall, accuracy, and false secondary were not part of the bar. + +A false high or false reject is present, so the outer-bucket bar fails. `revision_eligible` stays false. The screen was not retuned. `HLX-EXP-2026-09-27-SELECT-005` is not authorized. Admitted 0. Settled 0. Gold 0. The ledger was not appended. Events sha256 remains `96b74a92d44f1cf9fe152b18e5207176f161ba3bfce528dac38aa4571a742f9c`. `revision_eligible` on v6 stays false. `revision_eligible` on v5 stays false. `revision_eligible` on the v4 measurement stays true. Acceptance sha256 remains `562c0756b0337e2fb10643f4fd6689ea4421977a345d12fe33d8a1504161cac5`. + +## Unbind screen lineage retired — 2026-09-28 + +`RUNE.UNBIND_SCREEN.v3` through `RUNE.UNBIND_SCREEN.v7` stay sealed. They are a completed experimental lineage. The lineage showed that further surface-pattern patches are the wrong instrument. No `RUNE.UNBIND_SCREEN.v8` exists. Historical scores are not results for the sense-first screen. v7 remains `SCORED`, outcome `OUTER_BUCKET_FAILURE`, `revision_eligible` false. v7 was not retuned. v6 and v5 `revision_eligible` stay false. The v4 measurement `revision_eligible` stays true. Source sha256 values remain v3 `179d8dcc112214c70566bd3c9a0397e1ebab9131666b0ca1f2a3817973aaccc6`, v4 `f1e86e2f21544655cda6a136885a186b20885d501cb7ea9c75e18b3dd4a42377`, v5 `70504574523f2e8fde0fb974e3027205dded2c96213dd997f44475ea6856f948`, v6 `59699496c15aaedfbe69a7e49b5c6e62d1e543ce5a1e0e9a0255a98a62036fba`, v7 `73335bde8eec262ebecfedfc0d0ecb0a965da5c6b66e53c16f2aee2f38b061ab`. Retirement sha256 `fd5d9ebb94d7a6e6ea69609c4e2125ec9914f6705ae256b780223bbea2e26f6f`. + +The v7 measurement disagreements are not one failure class. The frozen error analysis separates three questions: whether the bound sense designates a referent, whether that sense requires conventionalized lexical binding, and whether that sense is compositionally recoverable. Error analysis sha256 `ebc56d4d4499efee19bc368365b0d6d3a7afc27ede4e78f40fb9d0fd15fcb9c8`. The canonical v7 operator counts from the row artifact are high 14, secondary 5, reject 9. The handwritten seal does not override those rows. + +## Unbind sense screen v1 specified — 2026-09-28 + +`RUNE.UNBIND_SENSE_SCREEN.v1` is `SPEC_FROZEN`. It is a new screen family. It is not encoded, not applied, and no measurement sample was drawn. The unit of analysis is the specific lexical sense: surface, part of speech, WordNet synset, and the frozen first-sense gloss. Another sense of the same surface, a historical v3-v7 bucket, and a referential origin that is not the predicated sense are outside that unit. + From 3e3e20670de04968f2690e5ed9fff2bb27b6691a Mon Sep 17 00:00:00 2001 From: Daniel Meyer Date: Mon, 28 Sep 2026 16:45:38 -0700 Subject: [PATCH 30/42] feat(007): stage evaluation-reserve chunk (03) --- .land-chunks/part-03 | 128 +++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 128 insertions(+) create mode 100644 .land-chunks/part-03 diff --git a/.land-chunks/part-03 b/.land-chunks/part-03 new file mode 100644 index 00000000..1ab4f46e --- /dev/null +++ b/.land-chunks/part-03 @@ -0,0 +1,128 @@ +The five classes are `REFERENTIAL`, `LEXICALIZED_NONCOMPOSITIONAL`, `LEXICALIZED_COMPOSITIONAL`, `ORDINARY_COMPOSITIONAL`, and `AMBIGUOUS`. The procedure assigns exactly one class. Empty or unmatched sense evidence is `AMBIGUOUS`. A figurative gloss is not referential merely because the wording mentions a place or story; `road to damascus` is the protected illustration and is not a required fit. Ordinary syntax, comparison, degree, and phrasal composition are `ORDINARY_COMPOSITIONAL`; a WordNet lemma by itself is not lexicalization. `as far as possible` motivates that distinction and is not a required fit. Insufficient evidence stays `AMBIGUOUS`. + +The frozen mapping is referential to reject, lexicalized noncompositional to high, both compositional classes to secondary, and ambiguous to quarantine. Ordinary composition does not map to reject. That would hide a second classifier. The two compositional classes remain distinct before the mapping. No historical bucket may override the sense class. + +The reviewed positional inventory is 225: the prior 197 plus the 28 v7 measurement rows. Normalized overlap between those sets is 0. The 28 rows are development evidence, not validation, and not a required fit. 225 rows bind a synset offset whose first-sense gloss equals the frozen gloss. 0 rows keep the frozen gloss with no offset. A future measurement must exclude all 225 normalized identities and be frozen before inspection. The proposed pass/fail bar is high precision 1, reject precision 1, false high 0, and false reject 0. Sense-class confusion, bucket confusion, per-class support, the ambiguous rate, high recall, reject recall, and secondary precision and recall are descriptive. This pass does not make them pass/fail criteria. + +Draft hypothesis sha256 `93375446b1f4a1f70c60f747a56b626ae667c8944d0eea54deddb9d57d3d9e38`. Acceptance sha256 `cff6af0f05ec5e12fb29ddfd2ec321addc94c73258c31860345f6d49960065b0`. Development evidence sha256 `0e9b3c1af9dd573bf6e2034640e468e8ab9074e1e76c90cef1f39f68d607bc03`. Tracker sha256 `4145848a2e1368b23f28f645249a9a8c55bf113a43763d31a434f1389b9e1154`. No JSON Schema exists for this hypothesis family. The older unbind sample, label, and report schemas are a different contract and were not applied. The next named transition is `ENCODED`. This pass does not authorize it. + +## Lexeme structure screen v1 architecture — 2026-09-28 + +`RUNE.LEXEME_STRUCTURE_SCREEN.v1` is an architecture note for a sibling lane. It evaluates one orthographic lexeme for internal structure. Candidate classes are atomic, compound, affixed, blend, clipping, respelling, reduplicated, borrowed, and unknown. A single token is not evidence of an atomic lexeme. A string split is not evidence of a valid morphological decomposition. Conceptual illustrations are not a corpus and not a required fit. This lane is not mixed into positional unbind. It is not encoded. No corpus was populated, no sample was drawn, and no gold was created. Architecture sha256 `529defbc2b56152c3290d5b09f309764128b035906797229dab54857cd249df0`. + +`HLX-EXP-2026-09-27-SELECT-005` is not authorized. Admitted 0. Settled 0. Gold 0. The ledger was not appended. Events sha256 remains `96b74a92d44f1cf9fe152b18e5207176f161ba3bfce528dac38aa4571a742f9c`. + +## Unbind sense screen v1 procedure frozen — 2026-09-28 + +`RUNE.UNBIND_SENSE_SCREEN.v1` moves from `SPEC_FROZEN` to `PROCEDURE_FROZEN`. The screen is not encoded and not applied. No development replay was run. No row received a sense class. No measurement sample was drawn. + +The companion artifact is `hyperlex.unbind_sense_screen_v1_classification_procedure.v1`, sha256 `4d9dad77d8d315e810863101041229c53570ed16970074c86abaecd0cc3012ad`. The sealed acceptance stays `cff6af0f05ec5e12fb29ddfd2ec321addc94c73258c31860345f6d49960065b0`. The sealed draft stays `93375446b1f4a1f70c60f747a56b626ae667c8944d0eea54deddb9d57d3d9e38`. The acceptance does not embed this procedure, so its bytes were not revised. The tracker now points at the procedure. Tracker sha256 `f9c4757b6eec558b5e1baf644bcf33c27c949807e7f00cd15df869eb6411de31`. + +WordNet membership is not evidence that a surface is a conventional lexical unit. A conventional unit requires an unrelated single-word co-lemma or a lexical pointer on the whole lemma. A productive frame requires a one-token lemma alternation stored on the synset, or a gloss that starts with the comparative or superlative operator formula. Anything else at that test is `AMBIGUOUS`. A stored unrelated equivalent is the noncompositional mapping. A derivation or pertainym from the whole lemma to one of its constituents, with no unrelated equivalent, is the compositional lexical unit. Referential designation requires an instance-hypernym pointer or a parenthetical four-digit lifespan. A geographic or religious allusion does not qualify, and neither does the capital letter in `road_to_Damascus`. Ordinary composition still maps only to secondary. Ambiguity maps to quarantine and is a normal class. + +The lexeme-structure note stays `529defbc2b56152c3290d5b09f309764128b035906797229dab54857cd249df0`. Reviewed positional inventory remains 225. v3 through v7 sources are unchanged. `HLX-EXP-2026-09-27-SELECT-005` is not authorized. Admitted 0. Settled 0. Gold 0. The ledger was not appended. Events sha256 remains `96b74a92d44f1cf9fe152b18e5207176f161ba3bfce528dac38aa4571a742f9c`. The next named transition is `ENCODE_AUTHORIZATION`. This pass does not authorize it. + +## Unbind sense screen v1 development replay — 2026-09-28 + +`RUNE.UNBIND_SENSE_SCREEN.v1` is encoded and the 225-row development replay is analyzed. The frozen classification procedure is unchanged, sha256 `4d9dad77d8d315e810863101041229c53570ed16970074c86abaecd0cc3012ad`. The sealed acceptance stays `cff6af0f05ec5e12fb29ddfd2ec321addc94c73258c31860345f6d49960065b0`. The sealed draft stays `93375446b1f4a1f70c60f747a56b626ae667c8944d0eea54deddb9d57d3d9e38`. The development manifest stays `0e9b3c1af9dd573bf6e2034640e468e8ab9074e1e76c90cef1f39f68d607bc03`. No sense class was written onto that manifest. + +The encoder follows the frozen tests. An instance-hypernym pointer or a parenthetical four-digit lifespan is referential. An unrelated single-word co-lemma is lexicalized noncompositional. A derivation or pertainym from the whole lemma back to a constituent, with no unrelated equivalent, is lexicalized compositional. A stored one-token lemma alternation, or a gloss that starts with the comparative or superlative operator formula, is ordinary compositional. Anything else is ambiguous. A yes-signal together with a no-signal is a conflict and stays ambiguous. Absence of a signal is not secondary. No gloss-keyword list, technical-term dictionary, or phrase exception was added. A high ambiguous rate was not repaired. + +Each development row was classified once from its frozen synset. The replay is development evidence, not validation. The measurement bar was not applied. Prediction sha256 `69ea6b8714f3cb6105222d636af3f17bd5c5caac7b290c3c3d87e4efaeedd0ef`. Report sha256 `38ada8bc32d8b19361cc974346d5972f6020eb0c32c2ca537abff4d17f66c7f0`. Tracker sha256 `b3690de5c957f019224c7ea980bf6e27b25ba1f1c519347f72a327fa3ea399a2`. + +Sense classes are referential 26, lexicalized noncompositional 72, lexicalized compositional 0, ordinary compositional 25, and ambiguous 102. The ambiguous rate is 102/225. Buckets are high 72, secondary 25, reject 26, and quarantine 102. Evidence codes are referential designation 26, noncompositional semantic mapping 72, compositional lexical unit 0, productive grammatical frame 25, and insufficient record evidence 102. The determinate sources are instance hypernym 26, unrelated single-word co-lemma 72, and productive alternation 25. No lifespan marker and no grammatical-operator gloss fired on this inventory. + +Lexicalized compositional support is 0. One development row stores a pertainym from the whole lemma to a constituent token and also stores a one-token lemma alternation. The frozen conflict rule returns ambiguous. That zero records the frozen test. It is not a missing dictionary. + +Operator labels already on the manifest, joined after classification: of 101 operator-high rows, 32 stay high, 17 go to secondary, and 52 go to quarantine. Of 46 operator-secondary rows, 22 go to high, 3 stay secondary, and 21 go to quarantine. Of 77 operator-reject rows, 18 go to high, 4 go to secondary, 26 stay reject, and 29 go to quarantine. The one operator-quarantine row goes to secondary. Descriptive precision and recall, not pass/fail criteria: high 32/72 and 32/101, secondary 3/25 and 3/46, reject 26/26 and 26/77. Operator quarantine support is 1. False high is 40. False reject is 0. False secondary is 22 and is descriptive only. + +`road to damascus` is ambiguous and quarantined. The bound record has no instance pointer and no lifespan marker. `as far as possible` is ordinary compositional and secondary, from the stored far/much alternation. Neither row is a required fit. + +State is `DEVELOPMENT_ANALYZED`. The path was `PROCEDURE_FROZEN`, `ENCODE_AUTHORIZED`, `ENCODED`, `225_ROW_DEVELOPMENT_REPLAY`, `DEVELOPMENT_ANALYZED`. `measurement_sample_drawn` stays false. `measurement_eligible` stays false. `revision_eligible` stays false. The lexeme-structure note stays `529defbc2b56152c3290d5b09f309764128b035906797229dab54857cd249df0`. v3 through v7 sources are unchanged. `HLX-EXP-2026-09-27-SELECT-005` is not authorized. Admitted 0. Settled 0. Gold 0. The ledger was not appended. Events sha256 remains `96b74a92d44f1cf9fe152b18e5207176f161ba3bfce528dac38aa4571a742f9c`. The next named transition is `MEASUREMENT_AUTHORIZATION`. This pass does not authorize it. + +## Unbind sense screen procedure v2 frozen — 2026-09-28 + +The encoded v1 screen stays `DEVELOPMENT_ANALYZED`. Procedure v1 stays frozen, sha256 `4d9dad77d8d315e810863101041229c53570ed16970074c86abaecd0cc3012ad`. The companion procedure `hyperlex.unbind_sense_screen_v1_classification_procedure.v2` is `PROCEDURE_V2_FROZEN`. It is not encoded and not applied. No v2 development replay was run. No row received a v2 sense class. No measurement sample was drawn. + +The v1 development replay mapped an unrelated single-word co-lemma to lexicalized noncompositional, and therefore to high. All 40 development false highs used that path. A stored whole-expression synonym can show that the phrase is a lexical unit. It does not show that the sense is semantically noncompositional. + +Procedure v2 keeps the five classes and the sealed bucket map. Before the class, it records three states: referential, lexicalized, and compositional. Each state is yes, no, unknown, or conflict. An unknown state is not a no. Referential yes, from an instance-hypernym pointer or a parenthetical four-digit lifespan, remains referential and reject. A co-lemma or a whole-lemma lexical pointer is lexicalized yes. A derivation or pertainym back to a constituent, a stored one-token alternation, or a comparative or superlative operator gloss is compositional yes. The alternation does not set lexicalized to no. The operator gloss sets lexicalized to no and compositional to yes, so when referential is not yes the class is ordinary compositional. Lexicalized yes together with compositional yes, when referential is not yes, is lexicalized compositional. That cell is reachable. The procedure does not force rows into it. + +WordNet 3.0 has no structural noncompositionality field once the co-lemma shortcut is retired. The compositional no-signal set is empty, so the lexicalized-noncompositional cell stays defined and does not fire. A later encoder must not invent a replacement signal. Missing lexicalization evidence stays unknown. On the record already frozen in procedure v1, `as far as possible` is therefore ambiguous under v2, and `road to damascus` stays ambiguous. Neither illustration is a required fit, and neither class was written onto the development manifest. An insufficient record stays ambiguous and maps to quarantine. A lower ambiguous rate is not a success criterion. Referential precision is not traded for coverage, and no technical-term gloss list was added. + +No JSON Schema document exists for procedure v2. The schema name on the artifact is not a JSON Schema file. Older unbind sample, label, and report schemas were not applied. Procedure sha256 `3f4071640d0c9f29cf56f53969a88ec25c635444b87765e77e1b9158470e5662`. v1 error analysis sha256 `471bc27b89f550fae36b3471daaad282a6dd8735414846cb18aafe1195e0a52e`. Change note sha256 `443ce2964d4e4fcd8257055cb1404965faa70b838264b1f623be192d1cae085c`. Tracker sha256 `af11d20ebefec5629718617ebacc07d8b4cc36c61e59e829d153b05b9397ca40`. + +The reviewed positional inventory remains 225. A future encoded replay of those same rows may report lexicalized-compositional support, the false-high count, the ambiguous rate, referential precision and coverage, the sense-class distribution, the evidence-state distribution, and the operator confusion tables. This freeze sets no accuracy threshold. Lexicalized-compositional support above zero would be a structural readiness signal, not a hard pass/fail rule. + +The lexeme-structure note stays `529defbc2b56152c3290d5b09f309764128b035906797229dab54857cd249df0`. The procedure records the shared invariant that lexicalized identity, compositionality, and semantic shift are different questions. That note was not edited, and the lexeme screen was not implemented. v3 through v7 sources are unchanged. The v1 implementation, v1 predictions, and v1 report are unchanged. Acceptance stays `cff6af0f05ec5e12fb29ddfd2ec321addc94c73258c31860345f6d49960065b0`. Prediction sha256 remains `69ea6b8714f3cb6105222d636af3f17bd5c5caac7b290c3c3d87e4efaeedd0ef`. Report sha256 remains `38ada8bc32d8b19361cc974346d5972f6020eb0c32c2ca537abff4d17f66c7f0`. `HLX-EXP-2026-09-27-SELECT-005` is not authorized. Admitted 0. Settled 0. Gold 0. The ledger was not appended. Events sha256 remains `96b74a92d44f1cf9fe152b18e5207176f161ba3bfce528dac38aa4571a742f9c`. `measurement_sample_drawn` stays false. `measurement_eligible` stays false. `revision_eligible` stays false. + +The next named transition is `ENCODE_PROCEDURE_V2_AUTHORIZATION`. This pass does not authorize it. An unseen measurement stays deferred until an encoded v2 replay of these 225 rows has been examined. + +## Unbind sense screen procedure v2 development replay — 2026-09-28 + +Procedure v2 is encoded and the same 225 development rows were replayed once. State is `DEVELOPMENT_ANALYZED_V2`. The path was `PROCEDURE_V2_FROZEN`, `ENCODE_PROCEDURE_V2_AUTHORIZATION`, `ENCODED`, `225_ROW_DEVELOPMENT_REPLAY`, `DEVELOPMENT_ANALYZED_V2`. The encoded v1 screen stays `DEVELOPMENT_ANALYZED`. Procedure v2 bytes stay `3f4071640d0c9f29cf56f53969a88ec25c635444b87765e77e1b9158470e5662`. Procedure v1 stays `4d9dad77d8d315e810863101041229c53570ed16970074c86abaecd0cc3012ad`. No sense class was written onto the development manifest. No measurement sample was drawn. + +The replay asks whether the frozen WordNet record can instantiate the five classes, and `LEXICALIZED_NONCOMPOSITIONAL` in particular, without treating a single-word co-lemma as compositional NO. It does not instantiate that class. High support is 0. Compositional NO count is 0. False high is 0. A co-lemma sets lexicalized YES and leaves the row ambiguous when compositionality is unknown. Ninety-five rows are lexicalized YES. One hundred eighty-two rows are compositionally unknown. + +Sense classes are referential 27, lexicalized noncompositional 0, lexicalized compositional 16, ordinary compositional 0, and ambiguous 182. The ambiguous rate is 182/225. Buckets are high 0, secondary 16, reject 27, and quarantine 182. Of the 16 lexicalized-compositional rows, 15 are a stored one-token alternation on a lexicalized synset and 1 is a derivation or pertainym back to a constituent. That support is a readiness signal. It is not a pass/fail result. Ordinary compositional support is 0 because no row has lexicalized NO. The one grammatical-operator gloss also has an unrelated co-lemma, so lexicalized state is conflict and the class is ambiguous. + +Referential yes remains an instance-hypernym pointer. All 27 referential rows use that pointer. Descriptive reject precision is 27/27 and recall is 27/77. False reject is 0. One of those 27 was quarantined by procedure v1 because the instance pointer shared the synset with a one-token alternation. Procedure v2 keeps the instance pointer decisive and records the alternation as compositional yes. Secondary precision is 7/16 and recall is 7/46. False secondary is 9 and is descriptive only. High precision is not computable, because no row was predicted high. High recall is 0/101. + +Evidence states: referential yes 27, no 97, unknown 101. Lexicalized yes 95, no 0, unknown 129, conflict 1. Compositional yes 43, no 0, unknown 182, conflict 0. `as far as possible` is referential no, lexicalized unknown, compositional yes, and ambiguous. `road to damascus` is unknown on all three states and ambiguous. Neither illustration was written onto the manifest. + +Prediction sha256 `1f7fc03547d24de851326a4848d93f1dbef16714e74e3e9f86d8c8aa6f8aaa8a`. Report sha256 `93d8fb76da8aa7155fb0ce57b0841ca455eca3e904edf50b9f76e595dd095ca5`. Tracker sha256 `6ecb7e16bbe2ea3be2efca51c46cc970f2a5ba8ec8cd9c101eaef8398f5a0e61`. Acceptance stays `cff6af0f05ec5e12fb29ddfd2ec321addc94c73258c31860345f6d49960065b0`. The v1 prediction and report hashes stay `69ea6b8714f3cb6105222d636af3f17bd5c5caac7b290c3c3d87e4efaeedd0ef` and `38ada8bc32d8b19361cc974346d5972f6020eb0c32c2ca537abff4d17f66c7f0`. The development manifest stays `0e9b3c1af9dd573bf6e2034640e468e8ab9074e1e76c90cef1f39f68d607bc03`. The lexeme-structure note stays `529defbc2b56152c3290d5b09f309764128b035906797229dab54857cd249df0`. v3 through v7 sources and the v1 implementation are unchanged. + +These figures are development evidence. No accuracy threshold was applied. `measurement_sample_drawn` stays false. `measurement_eligible` stays false. `revision_eligible` stays false. `HLX-EXP-2026-09-27-SELECT-005` is not authorized. Admitted 0. Settled 0. Gold 0. The ledger was not appended. Events sha256 remains `96b74a92d44f1cf9fe152b18e5207176f161ba3bfce528dac38aa4571a742f9c`. The next named transition is `V2_DEVELOPMENT_RESULT_REVIEW`. This pass does not authorize it, and it does not authorize an unseen measurement. + +## Semantic evidence source v1 specified — 2026-09-28 + +The procedure-v2 development result is reviewed and frozen. WordNet 3.0 structural evidence supports referentiality, whole-expression lexicalization, and some positive compositionality. Under the frozen procedure it does not provide a deterministic structural signal for semantic noncompositionality at useful coverage. That is a source-capability limitation. Procedure v2 was not retuned. + +Observed on the 225 development rows: compositional NO 0, lexicalized noncompositional 0, high 0, ambiguous 182/225, whole-expression lexicalization 95, positive compositionality 43, referential 27. Descriptive reject precision remains 27/27. False reject is 0. False high is 0. No measurement bar was applied. Review sha256 `77ae2c0491def0b75cd4213cc23fdcb6f2eec18dc2d0641764a276a583ee537d`. Limitation sha256 `3c05cd9d6301fab0791e31b542d767cc757307cf3e304065362b479cc40e964a`. Status is `WORDNET_STRUCTURAL_SOURCE_LIMITATION_CONFIRMED`. + +`RUNE.UNBIND_SEMANTIC_EVIDENCE_SOURCE.v1` is `SPEC_FROZEN`. It is an evidence-source evaluation framework, not an unbind classifier. The research question is which additional source can establish semantic noncompositionality independently of WordNet whole-expression lexicalization, with provenance and precision enough to become eligible input to a future sense-first screen. No source is selected. Nothing was encoded or applied. No external resource was downloaded. The only local lexical source is WordNet 3.0, and that source is the one whose limitation was just confirmed. + +Five candidate families are specified and left unselected: explicit lexical-semantic resources, a deterministic comparison of the bound sense with a composition of its parts, a curated linguistic annotation, a model-based judgment, and a later hybrid of WordNet structure plus one independent semantic source. An LLM completion is not runtime truth. A source must pass provenance, target alignment, sense alignment, label independence, reproducibility, abstention, a ban on phrase exceptions, development evaluation before integration, and isolation of any later generalization rows. The future output can be YES, NO, or UNKNOWN. UNKNOWN stays valid. No hard accuracy target is frozen. A high unknown rate is acceptable when false semantic claims stay low. Reducing ambiguity is not a reason to select a source. + +The same 225 rows remain development evidence for a future candidate test. They were not mutated and no source was run on them. False high and false reject stay the safety priority for a later integration specification. That specification does not exist yet, and no unseen-measurement bar was registered. + +A shared primitive, `RUNE.SEMANTIC_COMPOSITIONALITY`, is architecture only. The phrase lane would eventually use constituent words, phrase structure, and the bound sense. The single-lexeme lane would use morphemes or compound constituents and the bound sense. Lexicalized identity, compositionality, and semantic shift remain different questions. `RUNE.LEXEME_STRUCTURE_SCREEN.v1` was not implemented. Its architecture note stays `529defbc2b56152c3290d5b09f309764128b035906797229dab54857cd249df0`. + +No JSON Schema document exists for this source-evaluation family. Hypothesis sha256 `39127a810d38ede96d7947c33dbc3e5491c9e1cc9b3f76b1064d9e0dd04a7787`. Acceptance sha256 `1252c8c20ce3f49fe61ed8aeeec3157df7f4185b3aa7c468938ff47342d81b94`. Evaluation plan sha256 `472b3819c050bbc9b1dd2eec3183cdb27c3659521408c321acb12b9c1b69dc8a`. Architecture sha256 `180b6721c4e19847516364f441ecc2101ed9a7758643889673dfdb7be6f41d36`. Tracker sha256 `521eccb8bd068d4697a3ffdc06e3b44feb4eec3c8a3bd6aebcea11dd288b7dfc`. + +Procedure v2 stays `3f4071640d0c9f29cf56f53969a88ec25c635444b87765e77e1b9158470e5662`, predictions `1f7fc03547d24de851326a4848d93f1dbef16714e74e3e9f86d8c8aa6f8aaa8a`, and report `93d8fb76da8aa7155fb0ce57b0841ca455eca3e904edf50b9f76e595dd095ca5`. Procedure v1, the sense-screen acceptance, and the 225-row manifest are unchanged. The encoded v1 screen stays `DEVELOPMENT_ANALYZED`. Procedure v2 stays `DEVELOPMENT_ANALYZED_V2`. `measurement_sample_drawn` stays false. `measurement_eligible` stays false. `revision_eligible` stays false. `HLX-EXP-2026-09-27-SELECT-005` is not authorized. Admitted 0. Settled 0. Gold 0. The ledger was not appended. Events sha256 remains `96b74a92d44f1cf9fe152b18e5207176f161ba3bfce528dac38aa4571a742f9c`. No procedure v3 was created. + +The next named transition is `CANDIDATE_SOURCE_EVALUATION_AUTHORIZATION`. This pass does not authorize it. Selecting or integrating a source is a later decision. + +## MAGPIE candidate evaluation — 2026-09-28 + +`RUNE.UNBIND_SEMANTIC_EVIDENCE_SOURCE.v1` is `CANDIDATE_SOURCE_EVALUATED` for one candidate, MAGPIE. The candidate remains unselected. `selected_source` is `none`. Runtime integration is false. The encoded sense screen and procedure v2 were not modified. + +The evaluated artifact is the author corpus `MAGPIE_unfiltered.jsonl` from `https://github.com/hslh/magpie-corpus` at commit `7fa677b82b9a772dfa54bbdd0fb414412d73db3b` (2020-06-07T09:57:12Z). The file was acquired from that commit on 2026-09-28T05:50:00Z. It contains 56,622 instances and 1,756 potentially idiomatic expression types. Assigned labels are idiomatic 40,011, literal 16,168, other 436, and unclear 7. The filtered split files were not acquired and were not mixed into the evaluation. No Hugging Face package was used. No other repository named magpie was used. + +Licenses are recorded per artifact. The LREC 2020 PDF, sha256 `8247c926909772ce317d5f33cddb83caa51969eb5ef928bcbadbbcf05e39c979`, states on page 1 that the ELRA proceedings text is licensed under CC-BY-NC. That statement is the publication license. The dataset LICENSE file in the pinned commit is Creative Commons Attribution 4.0 International, sha256 `05ab88f3f9da1d05f9c5bf0a7c45c49a9007f877dd9c237a5bf668276fe04c3b`. That file is also the only code license in the commit. The evaluated jsonl sha256 is `541ee535e93d71eff85351351665115e2a9f22ad736423881da5774a93bc880e`. Provenance sha256 `bf0dd1dd747a6423406d97393f375f99620894a20af6bd4758e2de738d5c82dd`. License receipt sha256 `8813818aa3704ba1e764121d2f66ff1630862a66d6c0c0b959b6afa35e0c3972`. + +Surface comparison is orthographic. Case and separator folding produced 25 exact matches. One normalized match folds a comma: `day in day out` corresponds to the MAGPIE type `day in, day out`. `to a t` casefolds onto `to a T` and is counted as exact. Recorded variant categories such as inflection, dashes, and possessive describe occurrences of a type. They are not alternate expression strings, so variant matches are 0. Ambiguous collisions are 0. Unmatched rows are 199. Surface coverage is 26/225. + +Sense alignment requires equality between a source sense identifier and the supplied WordNet synset. The pinned instances have no synset, sense key, or gloss field. Alignment coverage is 0/225. Every row is UNKNOWN. Semantic noncompositionality is YES 0, NO 0, UNKNOWN 225. Abstention is 225/225. Evidence codes are `magpie_surface_only` 26 and `magpie_no_match` 199. A surface hit does not assign YES. Fifteen matched rows have only idiomatic instance labels, including `throw in the towel` and `dressed to the nines`, and they stay UNKNOWN. Ten matched types contain both literal and idiomatic instances, including `round the bend` and `to the letter`. Those counts stay on the match record. They are not a MIXED sense alignment and they are not semantic YES. `as luck would have it` is an exact match with only idiomatic labels, and the historical operator bucket is secondary. The idiomatic labels were not converted into YES. + +Operator labels were joined after the semantic evidence file was written. They are not runtime evidence. The descriptive proxy, stated as a proxy, treats operator HIGH as the comparison class for semantic YES and operator SECONDARY as the comparison class for semantic NO. YES precision is `NOT_COMPUTABLE`. NO precision is `NOT_COMPUTABLE`. False YES is 0. False NO is 0. Both counts are zero because no YES or NO claim was emitted. Operator REJECT is its own row in the joint table: 77 unknown. It was not read as compositional NO or as semantic YES. The joint table is HIGH 101 unknown, SECONDARY 46 unknown, REJECT 77 unknown, and quarantine 1 unknown. + +Development status is `CANDIDATE_INSUFFICIENT`. Coverage of familiar phrases is not a reason to select the source. The readiness question, non-zero semantic YES with a defensible sense alignment, is not met. Match sha256 `84c847cfa545883de5a31979133fed87b0cdf9a7d13074c74cf227d9bfcadc83`. Sense-alignment sha256 `037b0f4d96d463aa7c5fbecdbef06a530ffbf770735232c92bd6abd0dd71fc66`. Semantic-evidence sha256 `89f7227e1098407c7aaae6d9876b1f5780dbfe5b6a2eb3c3b09357d57822b577`. Evaluation sha256 `74e2174d15c486dc60e9ad6be338199ff66a2d0950ed268105119323711108ba`. Decision sha256 `6eaa968b6260946998dba13e5c423f178d3349cdfe06e5ea401717f5a9bcdd0d`. Tracker sha256 `c3fe6f2fd21d07b8b45d9f26ffeebbcfbe3cb68a1f5a7952dee94d9a2c995936`. + +No JSON Schema document exists for this source-evaluation family. The schema names on the artifacts name those artifacts. Older unbind sample, label, and report schemas were not applied. Procedure v2 stays `3f4071640d0c9f29cf56f53969a88ec25c635444b87765e77e1b9158470e5662`. The 225-row manifest stays `0e9b3c1af9dd573bf6e2034640e468e8ab9074e1e76c90cef1f39f68d607bc03`. No sense class was written onto it. v3 through v7 sources are unchanged. The lexeme-structure note stays `529defbc2b56152c3290d5b09f309764128b035906797229dab54857cd249df0`. `measurement_sample_drawn` stays false. `measurement_eligible` stays false. `revision_eligible` stays false. `HLX-EXP-2026-09-27-SELECT-005` is not authorized. Admitted 0. Settled 0. Gold 0. The ledger was not appended. Events sha256 remains `96b74a92d44f1cf9fe152b18e5207176f161ba3bfce528dac38aa4571a742f9c`. + +The next named transition is `NEXT_CANDIDATE_SOURCE_EVALUATION_AUTHORIZATION`. This pass does not authorize it. A later authorization may name one curated resource whose entries carry a WordNet synset offset or sense key for the expression sense. PARSEME, STREUSLE, PIE, EPIE, and NCS were not downloaded and are not selected. + +## Korkontzelos–Manandhar candidate evaluation — 2026-09-28 + +`RUNE.UNBIND_SEMANTIC_EVIDENCE_SOURCE.v1` stays `CANDIDATE_SOURCE_EVALUATED`. A second candidate, the Korkontzelos–Manandhar 2009 compositionality set, was evaluated and not selected. `selected_source` remains `none`. Runtime integration remains false. MAGPIE stays `CANDIDATE_INSUFFICIENT`. Its artifacts were not modified. + +The pinned source is Table 1 of Korkontzelos and Manandhar, “Detecting Compositionality in Multi-Word Expressions,” ACL-IJCNLP 2009 short papers, pages 65–68, anthology `P09-2017`. Canonical URL `https://aclanthology.org/P09-2017/`. PDF sha256 `046da9fc26cfdf220e41ad914314f0703ea3c84cd86e045b20146b34189113d1`, acquired 2026-09-28T06:41:05Z. The paper states that the items were drawn from WordNet 3.0. The bibliography cites Miller 1995 for WordNet in general. That citation is not a second version number. Reconstruction used the local Princeton WordNet 3.0 data files and no other WordNet release. The suggested counts of about 60 compositional and 56 noncompositional items do not match this table. The verified inventory is 19 compositional and 19 noncompositional. A later 2010 sample by the same authors was not acquired. + +The PDF header reads `c©2009 ACL and AFNLP`. The ACL Anthology states that materials prior to 2016 are licensed under CC-BY-NC-SA 3.0 and that copies may be made for teaching and research. That is the publication license of the acquired PDF. No separate dataset license exists. The evaluation list is the table inside the paper. No code artifact was published with the table, and none was acquired. Bold, underline, and italic marks in the table report system detections. They were not read as gold labels. + +Table 1 preserves the surface and the section label. It does not preserve a synset offset, sense key, lemma key, part of speech, or gloss. Source sense identity is therefore absent on all 38 items. A lookup key folds case, underscore, and apostrophe shape, and it keeps hyphens. That key is not a sense choice. The key matches exactly one PWN 3.0 synset for 30 items: `EXACT_UNIQUE_RECONSTRUCTION`. Those 30 are the 19 compositional items and 11 noncompositional items. Eight noncompositional items match two synsets each: `black maria`, `dead end`, `dutch oven`, `goat's rue`, `green light`, `high jump`, `living rock`, and `prince Albert`. They are `AMBIGUOUS_MULTIPLE_SYNSETS`. No gloss, operator label, or frequency was used to pick one. `EXACT_SOURCE_ID` is 0. `NO_PWN3_MATCH` is 0. `VERSION_CONFLICT` is 0. On the inventory, the conservative map gives semantic YES 11, NO 19, and UNKNOWN 8. Only the unique reconstructions carry YES or NO. + +None of those 30 synsets occurs in the frozen 225 development rows. Surface keys also do not meet any development row. The join is synset identity, so the Hyperlex result is YES 0, NO 0, UNKNOWN 225. Sense-aligned coverage is 0/225. Abstention is 225/225. Evidence codes on the 225 rows are `km_no_match` 225. Operator labels were joined after the evidence file was written. Under the stated proxy, YES precision and NO precision are `NOT_COMPUTABLE`. False YES is 0. False NO is 0. The joint table is HIGH 101 unknown, SECONDARY 46 unknown, REJECT 77 unknown, and quarantine 1 unknown. Reject and quarantine were not read as compositionality. + From 5b9f55d47654962badf24388212ebeb88d42d75d Mon Sep 17 00:00:00 2001 From: Daniel Meyer Date: Mon, 28 Sep 2026 16:49:48 -0700 Subject: [PATCH 31/42] feat(007): stage evaluation-reserve chunk (04) --- .land-chunks/part-04 | 1 + 1 file changed, 1 insertion(+) create mode 100644 .land-chunks/part-04 diff --git a/.land-chunks/part-04 b/.land-chunks/part-04 new file mode 100644 index 00000000..a150fbf1 --- /dev/null +++ b/.land-chunks/part-04 @@ -0,0 +1 @@ +PLACEHOLDER_WILL_NOT_SEND \ No newline at end of file From 8bd1f97179e5168b7b4ed4b7523366ea5753b82c Mon Sep 17 00:00:00 2001 From: Daniel Meyer Date: Mon, 28 Sep 2026 16:53:10 -0700 Subject: [PATCH 32/42] feat(007): stage evaluation-reserve chunk (04) --- .land-chunks/part-04 | 35 ++++++++++++++++++++++++++++++++++- 1 file changed, 34 insertions(+), 1 deletion(-) diff --git a/.land-chunks/part-04 b/.land-chunks/part-04 index a150fbf1..f65cd14b 100644 --- a/.land-chunks/part-04 +++ b/.land-chunks/part-04 @@ -1 +1,34 @@ -PLACEHOLDER_WILL_NOT_SEND \ No newline at end of file +Development status is `CANDIDATE_INSUFFICIENT`. The inventory can reconstruct monosemous PWN 3.0 identity, which MAGPIE could not, and that reconstruction still supplies no YES row on this development set. The stop-condition finding `WORDNET_DERIVED_BUT_SENSE_IDENTITY_NOT_PRESERVED` was not frozen. Eight of 38 items are polysemous, not most of them, and 30 items do reconstruct one synset. The development sample and this table are disjoint. MAGPIE remains surface coverage 26/225, sense-aligned coverage 0/225, YES 0, NO 0, abstention 225/225. This candidate is surface coverage 0/225, sense-aligned coverage 0/225, YES 0, NO 0, abstention 225/225. Coverage is not the comparison. Neither candidate puts a sense-aligned YES on a development row. + +Provenance sha256 `88fbfa077d2394b8ce631ec700c482ad98a0f62f0ab06964f4f43a77d906f9aa`. License receipt sha256 `eb4c9406aab7f9021d346ebd24634cad1dcf0e6076f069e73b1d4b2702dbe2f8`. Inventory sha256 `3a35ddc0b2c04a5386c6112a2bb3cdf22735edbe2fd791f0c2ec542fe9184d81`. PWN 3.0 alignment sha256 `531d13cf1bdbc939fc11d9ef5864c6878a9f58210368c596c0aad08ff75e61c9`. Hyperlex semantic evidence sha256 `4e98f8f06713ffcf02549305aef140e92b9b8b2790f471f3ce1f41fe208b2f7a`. Evaluation sha256 `00a1d1f667635354e20e5002c4ece846fe3a8125a7ca12ebe09bb7e28dedd1a7`. Comparison sha256 `240b3ea468d22a80ac5e5765521cc691b80f914b7baff6bb1ae4819475f54965`. Decision sha256 `93a07e3c78b53a69965497410c34ddb52c2a5d3add3fb2f3cd3fd9ca84eb3d9f`. Tracker sha256 `b3546102410058d3596c4563604998685753a698b2bc533b353e1f039440f704`. + +No JSON Schema document exists for this source-evaluation family. Procedure v2 stays `3f4071640d0c9f29cf56f53969a88ec25c635444b87765e77e1b9158470e5662`. The 225-row manifest stays `0e9b3c1af9dd573bf6e2034640e468e8ab9074e1e76c90cef1f39f68d607bc03`. v3 through v7, the lexeme-structure note, and the WordNet source-limitation finding are unchanged. `measurement_sample_drawn` stays false. `measurement_eligible` stays false. `revision_eligible` stays false. `HLX-EXP-2026-09-27-SELECT-005` is not authorized. Admitted 0. Settled 0. Gold 0. The ledger was not appended. Events sha256 remains `96b74a92d44f1cf9fe152b18e5207176f161ba3bfce528dac38aa4571a742f9c`. + +The next named transition is `NEXT_CANDIDATE_SOURCE_EVALUATION_AUTHORIZATION`. This pass does not authorize it. A later candidate has to carry a PWN synset identity that can meet the development rows. Surface-only MWE corpora are not the next step. `CANDIDATE_SOURCE_SELECTION_REVIEW` is not the next step, because this candidate is not promising and is not selected. + +## Semantic compositionality residual candidate evaluation — 2026-09-28 + +`NEXT_CANDIDATE_SOURCE_EVALUATION_AUTHORIZATION` applies only to `RUNE.SEMANTIC_COMPOSITIONALITY_RESIDUAL.v1`. The candidate is an evaluation of a continuous residual. It is not selected, it is not integrated into `UNBIND_SENSE_SCREEN`, and it does not create procedure v3. Procedure v2 stays `3f4071640d0c9f29cf56f53969a88ec25c635444b87765e77e1b9158470e5662`. No unseen measurement sample is drawn. No training gold is created. No row is admitted or settled. `HLX-EXP-2026-09-27-SELECT-005` is not authorized. MAGPIE and the 2009 Korkontzelos–Manandhar artifacts stay frozen, and both remain `CANDIDATE_INSUFFICIENT`. + +The design was hashed before any development row was encoded. The whole sense is the supplied surface, its POS, and the frozen gloss, in the form `{surface} ({pos}): {gloss}`. A constituent uses the same form on the resolved PWN 3.0 lemma, POS, and first gloss clause. Whitespace tokenization keeps a hyphen inside one token. A frozen closed class of determiners, prepositions, pronouns, auxiliaries, and other structural tokens is ignored. A row needs two content tokens. Sense resolution accepts one derivation or pertainym pointer whose target word number is nonzero and whose lemma matches the constituent or a one-hop exception neighbor. That result is `EXACT`. With no exact target, one lexical synset across noun, verb, adjective, and adverb is `UNIQUE`. Several targets are `AMBIGUOUS`. None is `UNRESOLVED`. A target word number of zero does not name a lemma. The source word number is not a filter. An ambiguous or unresolved content constituent makes the row `UNKNOWN`. The pass does not use an arbitrary first sense, gloss similarity, an embedding to choose a sense, an operator label, or a manual sense choice. Composition is one operator, `normalized_mean_v1`: binary64 L2-normalize each constituent vector, take the mean, and L2-normalize the mean. Duplicate synsets stay, one vector per content token. The residual is `1 - cosine`, recorded with `format(value, '.10f')` and no clamp. A vector hash is sha256 of little-endian binary32 bytes. No semantic-noncompositionality threshold is chosen, and the residual is not turned into YES or NO. + +The pinned model is `sentence-transformers/all-MiniLM-L6-v2`, revision `1110a243fdf4706b3f48f1d95db1a4f5529b4d41`, acquired locally at `2026-09-28T06:52:00Z` from the Hugging Face revision. The model license is Apache-2.0. Weights sha256 `53aa51172d142c89d9012cce15ae4d6cc0ca6895895114379cacb4fab128d9db`. `tokenizer.json` sha256 `be50c3628f2bf5bb5e3a7f17b1f74611b2561a3a27eeab05e5aa30f411572037`. `vocab.txt` sha256 `07eced375cec144d27c900241f3e339478dec958f92fddbc551f295c992038a3`. Pooling is mean tokens into 384 dimensions, and the stack includes a normalize module. Encode uses CPU, float32, `normalize_embeddings=True`, batch size 1, seed 0, one thread, and eval mode. `use_deterministic_algorithms` stays false. The effective maximum sequence length is 256; overflow would abstain rather than truncate, and no row overflowed. Runtime versions are Python 3.12.3, torch `2.14.0+cpu` (Apache-2.0 with additional component licenses), sentence-transformers 6.1.0 (Apache-2.0), transformers 5.17.0 (Apache 2.0), tokenizers 0.23.2 (Apache), and NumPy 2.5.3 (BSD-3-Clause and other component licenses). ONNX, OpenVINO, and TensorFlow weight copies were not acquired. No API embedding service was called. Constituent glosses come from Princeton WordNet 3.0, whose license is separate from the model license. The encoder was run twice and the float32 hashes matched before the score file was written. + +Constituent extraction is `EXTRACTED` 166 and `UNKNOWN` 59. The 59 are rows with fewer than two content tokens. Across content constituents the resolution counts are `EXACT` 4, `UNIQUE` 60, `AMBIGUOUS` 370, and `UNRESOLVED` 70. Row abstentions are ambiguous content 145, fewer than two content tokens 59, and unresolved content 18. Three rows are `SCORED` and 222 are `UNKNOWN`. Ten representation texts were encoded. Six content tokens contain a character outside letters, digits, hyphen, and apostrophe; the extractor did not strip them. The three scored rows are operator `REJECT`: `california fern` residual `0.2139784896`, `monoamine oxidase inhibitor` residual `0.1768095281`, and `.22 caliber` residual `0.1309395496`. Reject is a referential axis, not semantic no. Their descriptive distribution is count 3, min `0.1309395496`, p25 `0.1538745388`, median `0.1768095281`, p75 `0.1953940088`, max `0.2139784896`, mean `0.1739091891`. Operator `HIGH` has 0 scored rows of 101, so the primary comparison is `NOT_COMPUTABLE`. `SECONDARY` has 0 of 46. `QUARANTINE` has 0 of 1. No threshold was fit to these labels. + +Development status is `CANDIDATE_DISTRIBUTION_FROZEN` because the preregistered rule uses that status whenever the scored count is greater than zero. The status freezes the score distribution. It does not say the residual separates noncompositionality, and the empty HIGH distribution supplies no such comparison. `selected_source` remains `none`. Candidate spec sha256 `39c2914e32557ffe1a456a56f8742ea4fe8f1aaec1dc1da451656cd22f0db32d`. Provenance sha256 `2c34a7d0d480cde564bda694dbaa349550814c5fb7c647bfa3bbbc9db5e26886`. License receipt sha256 `323a5bad21ac74d6a1fd94c54dba95a0046b633cd7328dd6680972525abc0909`. Score artifact sha256 `cea638679faeee1bc1c689823e7c0c08562c4d7ef1f8230bbbf4079239e7c3e7`. Evaluation sha256 `ba782622d4c68d23c53ae0ffb5f54f1e43c8cf059b44b7adb34e0eb356ef3896`. Decision sha256 `45922eba294b0b7d7238ce71c6157641259ea292f65743ba2ee1324ff9b52617`. Tracker sha256 `14d4daaab1e4740a596acaef7f4dbd9ed11edaf2182ff22f7aa339325febf591`. Every score row carries the spec hash, and the spec contains no residual. Operator labels were joined only after the score file was hashed. + +No JSON Schema document exists for this source-evaluation family. The 225-row manifest stays `0e9b3c1af9dd573bf6e2034640e468e8ab9074e1e76c90cef1f39f68d607bc03`. Sense classes on those rows stay null. v3 through v7, the lexeme-structure note, and the WordNet source-limitation finding are unchanged. `measurement_sample_drawn` stays false. `measurement_eligible` stays false. `revision_eligible` stays false. Admitted 0. Settled 0. Gold 0. The ledger was not appended. Events sha256 remains `96b74a92d44f1cf9fe152b18e5207176f161ba3bfce528dac38aa4571a742f9c`. + +The next named transition is `RESIDUAL_THRESHOLD_FREEZE_AUTHORIZATION`. This pass does not authorize it. A threshold is not supported by a HIGH distribution, because no HIGH row was scored. `CANDIDATE_SOURCE_SELECTION_REVIEW` is not the next step. `selected_source` remains `none`. + +## Residual v1 coverage limitation — 2026-09-28 + +`RESIDUAL_DEVELOPMENT_RESULT_REVIEW` is authorized for the frozen residual distribution only. `RESIDUAL_THRESHOLD_FREEZE_AUTHORIZATION` is not authorized. The residual has not been falsified. The experiment could not test HIGH against SECONDARY because constituent sense ambiguity collapsed coverage. Scored rows are 3, all operator REJECT. Unknown rows are 222. HIGH scored 0 of 101. SECONDARY scored 0 of 46. REJECT scored 3 of 77. Ambiguous content abstains 145 rows, fewer than two content tokens abstain 59, and unresolved content abstains 18. `threshold_eligible` is false. `source_selection_eligible` is false. The primary limitation is `constituent_sense_resolution`. The finding is `SEMANTIC_RESIDUAL_V1_COVERAGE_LIMITATION_CONFIRMED`: the dominant blocker is deterministic constituent sense resolution, not the residual model. The model, revision, normalized mean, and `1 - cosine` residual stay frozen. No residual score was recomputed. + +## Constituent sense resolution v1 — 2026-09-28 + +`RUNE.CONSTITUENT_SENSE_RESOLUTION.v1` starts from that limitation. The question is whether a frozen deterministic resolver can give enough content constituents a PWN 3.0 sense to make a later residual replay testable. Operator labels, residual scores, unbind buckets, phrase exceptions, manual choices, an LLM, and the residual embedding model are not inputs. The specification and procedure were hashed before the replay. The resolver then ran twice from that specification. The two artifacts matched, so determinism is `IDENTICAL`. + +Tier 1 is structural. A lexical pointer in the frozen set `! + \ ^ * & < $` whose target word number is nonzero and whose lemma matches the constituent, or one exception hop, is structural evidence. One such synset is `EXACT` by `STRUCTURAL_EXACT`. More than one is `AMBIGUOUS` and Extended Lesk does not override it. With no structural target, one lemma synset across noun, verb, adjective, and adverb is `EXACT` by `UNIQUE_LEMMA`. No synset is `UNRESOLVED`. The extra pointer symbols did not raise structural exact above the residual baseline: structural exact stays 4 and unique lemma stays 60. + From f09304093bcc10ab45e1ee116750d753c5f2a302 Mon Sep 17 00:00:00 2001 From: Daniel Meyer Date: Mon, 28 Sep 2026 16:53:59 -0700 Subject: [PATCH 33/42] feat(007): stage evaluation-reserve chunk (05) --- .land-chunks/part-05 | 31 +++++++++++++++++++++++++++++++ 1 file changed, 31 insertions(+) create mode 100644 .land-chunks/part-05 diff --git a/.land-chunks/part-05 b/.land-chunks/part-05 new file mode 100644 index 00000000..cb18e8e6 --- /dev/null +++ b/.land-chunks/part-05 @@ -0,0 +1,31 @@ +Extended Lesk v1 runs only when several lemma synsets remain. Candidate text is the full PWN 3.0 gloss, including quoted examples, plus the first gloss clause of each depth-1 synset reached by `@ + \ & ^ =`. Context is the parent frozen gloss plus the first gloss clause of other constituents in the row that tier 1 already resolved. Lesk output is not reused as context. Normalization casefolds, folds apostrophes, and splits on characters outside letters, digits, apostrophe, and hyphen. Stopwords are the residual v1 structural class. Tokens shorter than two characters are dropped. There is no stemmer and no lemmatizer. The score is the sum of squares of greedy longest contiguous overlaps, and matched tokens are not reused. Scores are integers. The minimum margin is 1, so a tie, including a zero-overlap tie, is `AMBIGUOUS`. A strict win is `RESOLVED`. The parent gloss is local context only. Parent sense is not constituent sense. + +Every one of the 504 content constituents was attempted. Status counts are `EXACT` 64, `RESOLVED` 121, `AMBIGUOUS` 249, and `UNRESOLVED` 70. Against the residual baseline of `EXACT` 4, `UNIQUE` 60, `AMBIGUOUS` 370, and `UNRESOLVED` 70, the 60 unique lemmas are the same senses under the new `EXACT` label, unresolved stays 70, and 121 of the 370 ambiguous constituents become `RESOLVED`. The other 249 stay `AMBIGUOUS`. The tie rate on Lesk attempts is 249/370. Exact proportion is 64/504. Context-resolved proportion is 121/504. Unresolved rate is 70/504. Abstention, ambiguous plus unresolved, is 319/504. There is no constituent-sense gold, so these figures are coverage, determinism, tie behavior, and provenance. They are not accuracy. + +A row is `RESIDUAL_READY` only when it has at least two content constituents and every one is `EXACT` or `RESOLVED`. That holds for 20 rows. The other 205 are `UNKNOWN`. After the resolution file was hashed, operator labels give HIGH 4 ready and 97 unknown, SECONDARY 4 ready and 42 unknown, REJECT 12 ready and 65 unknown, and quarantine 0 ready and 1 unknown. The necessary condition, at least one ready HIGH row and one ready SECONDARY row, is met. It is not sufficient. Because 249 of 370 baseline-ambiguous constituents remain AMBIGUOUS, which is more than half, the preregistered finding is `CONSTITUENT_WSD_COVERAGE_INSUFFICIENT`. No second resolver was added. A projection, not a replay, says 20 rows could enter a later residual pass: HIGH 4, SECONDARY 4, REJECT 12. The residual was not rerun. + +Resolver state is `DEVELOPMENT_ANALYZED`. Candidate status is `COVERAGE_INSUFFICIENT`. `selected_source` remains `none`. Residual state remains `CANDIDATE_DISTRIBUTION_FROZEN` with coverage limitation `CONFIRMED`. Limitation sha256 `fc8839c15a7638b2bfca1cf0548bfb4d5f433434bae0fea2944a528dd15d6142`. Review sha256 `1c1b69856dd88567167fd5c958cd8db6d39ab9ec74a4e0ed3e667a521c82e6fa`. Resolver spec sha256 `176e6219ddc3127814a25d39ad26e3571817f7ea8323d685e081d2e0fd867acb`. Procedure sha256 `9f76b64aa6aac09bd56ca9cc8a847cda12b31a58eea8c4b51426733917d54248`. Replay sha256 `a0c707ab55e02f627a698c33ddc0ca398e34aa0bafc19b13e72422bc26d97d0a`. Analysis sha256 `6d47610014f74094394355a50419fe24441103bec09458b6f25ea392fb57151c`. Decision sha256 `ff8b90ebe5d48151dc68ddbf676e1f27d4cee5e3be2730f999c9b089f1392caa`. Tracker sha256 `c72e55c8096143b8675d4aa995c5d4b19de95d256dd234d32a421ba098bc7d4f`. + +No JSON Schema document exists for this source-evaluation family. No residual threshold was created. No semantic YES or NO was emitted. No unseen sample was drawn. `measurement_sample_drawn` and `measurement_eligible` stay false. SELECT-005 stays unauthorized. Admitted 0. Settled 0. Gold 0. The 225-row manifest, procedure v2, MAGPIE, Korkontzelos–Manandhar, and the residual v1 artifacts are unchanged. The ledger was not appended. Events sha256 remains `96b74a92d44f1cf9fe152b18e5207176f161ba3bfce528dac38aa4571a742f9c`. + +The next named transition is `MODEL_BASED_WSD_CANDIDATE_EVALUATION_AUTHORIZATION`. This pass does not authorize it. `RESIDUAL_REPLAY_WITH_RESOLVED_SENSES_AUTHORIZATION` is not the next step while the constituent-coverage finding stands. `selected_source` remains `none`. + +## Model-based constituent WSD candidate v1 — 2026-09-28 + +`MODEL_BASED_WSD_CANDIDATE_EVALUATION_AUTHORIZATION` evaluates one WordNet-native model on the 249 constituents that Extended Lesk v1 left `AMBIGUOUS`. The model is `kanishka/GlossBERT`, family GlossBERT, canonical source `https://huggingface.co/kanishka/GlossBERT`, revision `0cc3b83af5496e27ebcc95ef0cf37ea0a9281a7a`. It is a BERT sequence classifier fine-tuned on SemCor 3.0. Each decision is a score over a supplied Princeton WordNet 3.0 gloss, so the output is a candidate synset or an abstention. The card license is MIT. The checkpoint is a third-party Hugging Face upload, not the authors' Google Drive file. The original code repository `https://github.com/HSLCY/GlossBERT` is MIT. Inference is local. Device is CPU. Dtype is float32. Batch size is 1. One thread is used and mkldnn is disabled. Runtime is Python 3.12.3, torch 2.14.0+cpu, transformers 5.17.0, tokenizers 0.23.2, and numpy 2.5.3. The positive class is index 1. A non-Hyperlex polarity control fixed that index before any development constituent was scored: a financial sentence selects `noun:08420278`, and a river sentence selects `noun:09213565`. + +The candidate specification was hashed before those 249 constituents were scored. Spec sha256 `c861ff7fff11ae6a790531267229c18d6e6e0a171a9bf6c34cfb6f7e7b14498c`. Weight sha256 `60706c7618f8ccbfa7d0a6d1d1009765a7146ea5f4232924ed9f1c46d521c898`. Vocab sha256 `07eced375cec144d27c900241f3e339478dec958f92fddbc551f295c992038a3`. Tokenizer config sha256 `09e49d0e788d25991da77d37b10eaa6a86a4e94e2127de8bedc94eb45baf2d84`. Each constituent is scored only among its frozen PWN 3.0 candidate synsets. The context is the parent surface with the target content token in double quotes. The paired text is the matched lemma, a colon, and the first gloss clause. The parent gloss is not appended. Operator labels, residual scores, and the residual embedding are not inputs. The score is the class-1 softmax probability, quantized to six decimal places, half even. The abstention rule, frozen before scoring, requires a top probability of at least 0.50 and a top-versus-second margin of at least 0.10. An equal top score stays `AMBIGUOUS`. A sense outside the candidate set would be `INVALID`. The same inference then ran a second time. The two raw artifacts matched, so determinism is `IDENTICAL`. + +Tier 3 attempted 249 constituents. It resolved 153, left 96 ambiguous, and produced 0 invalid outputs and 0 errors. The 153 resolutions use evidence `model_margin`. The 96 abstentions use evidence `model_abstention`. Exact score ties are 0. Top-score bins are 81 below 0.50, 30 from 0.50 to 0.60, 35 from 0.60 to 0.70, 26 from 0.70 to 0.80, 32 from 0.80 to 0.90, and 45 from 0.90 to 1.00. Margin bins are 50 below 0.10, 56 from 0.10 to 0.25, 56 from 0.25 to 0.50, and 87 at 0.50 or more. There is no constituent-sense gold on these rows, so the figures are coverage, validity, determinism, confidence, and abstention. They are not accuracy, precision, recall, or F1. + +Combined with the frozen earlier tiers, constituent counts are `EXACT` 64, `LESK_RESOLVED` 121, `MODEL_RESOLVED` 153, `AMBIGUOUS` 96, and `UNRESOLVED` 70. Tier 3 did not replace a structural exact, a unique lemma, or an Extended Lesk decision that had already cleared its margin. A row is projected `RESIDUAL_READY` only when it has at least two content constituents and every one is exact, Lesk-resolved, or model-resolved. That projection was hashed before operator labels were joined. Projected ready rows are 73. Unknown rows are 152. After the join, HIGH is 28 ready and 73 unknown, SECONDARY is 11 ready and 35 unknown, REJECT is 34 ready and 43 unknown, and quarantine is 0 ready and 1 unknown. Against the lexical baseline of HIGH 4, SECONDARY 4, and total 20, the deltas are +24, +7, and +53. Invalid outputs are 0 and the repeat is identical, so the preregistered coverage gate returns `CANDIDATE_PROMISING`. The gate is a readiness projection. It does not say the selected senses are correct, and it does not replay the residual. + +`selected_source` remains `none`. The model is not integrated into runtime. `RUNE.CONSTITUENT_SENSE_RESOLUTION.v1` stays `DEVELOPMENT_ANALYZED` with lexical status `COVERAGE_INSUFFICIENT`. `RUNE.SEMANTIC_COMPOSITIONALITY_RESIDUAL.v1` stays `CANDIDATE_DISTRIBUTION_FROZEN`. `threshold_eligible` stays false. No residual score was recomputed and no residual threshold was created. No semantic YES or NO was emitted. No unseen sample was drawn. `measurement_sample_drawn` and `measurement_eligible` stay false. SELECT-005 stays unauthorized. Admitted 0. Settled 0. Gold 0. The resolver artifacts, residual artifacts, MAGPIE artifacts, Korkontzelos–Manandhar artifacts, procedure v2, and the 225-row manifest are unchanged. The ledger was not appended. Events sha256 remains `96b74a92d44f1cf9fe152b18e5207176f161ba3bfce528dac38aa4571a742f9c`. + +No JSON Schema document exists for this source-evaluation family. Provenance sha256 `6d91283244a88f44477c836e6549118d5d96a36bb878bf448fcadb13ff765e11`. License receipt sha256 `67d45243959e3e76643a675d49b508a73d0f7f0bf8fbf060ce7b83c9200c621b`. Raw output sha256 `a0e508c225e6db4cdbcae701682202b1d854f3762546dbfe10b84a15e0e9e17c`. Resolution sha256 `ed945989cf4947ac84633ba2c4aa10c1ba381d2396da0b573a844f83ec367a18`. Readiness projection sha256 `c75834faf4a84d36e83246244e0aa7c6c7788c3a57cfdb7f77c7628a52023328`. Analysis sha256 `8ca0d8c8dd7e510a04daef6b3fbd78ace6d20b173e88d2a3d0c2e10fdaafe30b`. Decision sha256 `c8ed0b8acaaa415277c5f9bdbf982d075136b795b5d95fd8813d5a3010072dbb`. Tracker sha256 `6da1e9730c785d2784d23433455515989d312ed8c76f4763b3a2dd6a1f2c4b2d`. + +The next named transition is `RESIDUAL_REPLAY_WITH_MODEL_RESOLVED_SENSES_AUTHORIZATION`. This pass does not authorize it. The residual is not replayed. `selected_source` remains `none`. +## Residual replay with model-resolved senses — 2026-09-28 + +`RESIDUAL_REPLAY_WITH_MODEL_RESOLVED_SENSES_AUTHORIZATION` runs one residual replay on the frozen Tier 1, Tier 2, and Tier 3 constituent senses. The question is whether operator-HIGH rows then show larger frozen semantic-compositionality residuals than operator-SECONDARY rows. This is a development distribution. It is not a classifier, and it does not train a composition function. The residual model stays `sentence-transformers/all-MiniLM-L6-v2` at revision `1110a243fdf4706b3f48f1d95db1a4f5529b4d41`. Settings stay CPU, float32, seed 0, batch size 1, one thread, eval mode, normalized embeddings, and maximum sequence length 256. Composition stays `normalized_mean_v1`. The residual stays `1 - cosine_similarity(whole_sense_vector, normalized_mean(constituent_sense_vectors))`, stored at 10 decimal places. Constituent extraction, structural exact, unique lemma, Extended Lesk v1, and GlossBERT are not retuned. No confidence or margin rule changes. No second embedding, pooling, distance, or weight scheme is tried. + From bfbed776bfb2c72e0be2c8f88697dcb051d90d60 Mon Sep 17 00:00:00 2001 From: Daniel Meyer Date: Mon, 28 Sep 2026 16:54:32 -0700 Subject: [PATCH 34/42] feat(007): stage evaluation-reserve chunk (06) --- .land-chunks/part-06 | 29 +++++++++++++++++++++++++++++ 1 file changed, 29 insertions(+) create mode 100644 .land-chunks/part-06 diff --git a/.land-chunks/part-06 b/.land-chunks/part-06 new file mode 100644 index 00000000..cad3b8d1 --- /dev/null +++ b/.land-chunks/part-06 @@ -0,0 +1,29 @@ +The integrated resolution is built from the frozen resolver replay and the frozen GlossBERT resolution, then checked against the frozen readiness projection before any vector is computed. The projection reproduces: 73 `RESIDUAL_READY` rows and 152 `UNKNOWN` rows. The integrated file is hashed before scoring. Integrated sha256 `0f5dafc3676a4071ce8c889e58958b90203589aa3e91d78111b4e3292bdd87fb`. Only ready rows are scored. Representation text, lemma choice, gloss fields, example inclusion, normalization, and the residual formula are the frozen residual v1 functions. The encoder then runs twice. Row readiness, representation texts, vector hashes, and 10-decimal scores match, so determinism is `IDENTICAL`. No ready row exceeds 256 tokens. Scored rows are 73 and scoring abstentions are 0. The score file is hashed before operator labels are read. The receipt records that sequence with `operator_labels_joined` false. Score sha256 `16c0a9eaa918ac4a6e8223cafcbf4b1918cb212769063e262cf29a279f1048f6`. Receipt sha256 `675b1b8b8f1b1e6f19c7d320e7b8fe516eae92b60e966afbce407c1f0482e736`. + +After that hash, the historical operator labels join. Ready counts are HIGH 28, SECONDARY 11, REJECT 34, and quarantine 0. REJECT stays out of the primary comparison. HIGH residuals have count 28, minimum 0.1169573790, p10 0.2930005820, p25 0.3564342144, median 0.4040327275, p75 0.4404865614, p90 0.5184032344, maximum 0.5908804826, mean 0.3943616308, and sample standard deviation 0.1017789927. SECONDARY residuals have count 11, minimum 0.1768283745, p10 0.2079496807, p25 0.2436642596, median 0.3307873412, p75 0.4000929338, p90 0.4561366947, maximum 0.5587792172, mean 0.3299980313, and sample standard deviation 0.1186719529. The HIGH mean exceeds the SECONDARY mean by 0.0643635995. The HIGH median exceeds the SECONDARY median by 0.0732453863. Mann-Whitney U for HIGH, with half credit for ties, is 207. There are 207 pairs in which the HIGH residual is larger, 101 in which it is smaller, and 0 ties, out of 308 pairs. The rank-biserial correlation is 0.344156. Descriptive ROC AUC, with HIGH as the positive class and a larger residual as the HIGH-like score, is 0.672078. The hypothesized direction `HIGH residual > SECONDARY residual` is `SUPPORTED_DIRECTION` on these point estimates. That direction is not a semantic YES or NO. + +Uncertainty uses the bootstrap frozen before the label join: seed 0 and 10000 resamples, with interpolated 2.5 and 97.5 percentiles. The AUC interval is 0.451299 to 0.870130. The mean-difference interval is -0.0155777495 to 0.1373246447. The median-difference interval is -0.0445612032 to 0.1742972287. Each interval includes a null or reversed value. The preregistered status rule does not require the interval to exclude the null, and no stronger AUC floor is added after seeing the scores. + +Tier 3 dependence, among ready rows, is HIGH with Tier 3: 24, HIGH without Tier 3: 4, SECONDARY with Tier 3: 7, and SECONDARY without Tier 3: 4. The no-Tier-3 group has HIGH n=4 and SECONDARY n=4, median difference 0.1435511609, rank-biserial 0.500000, and AUC 0.750000, direction `SUPPORTED_DIRECTION`. The Tier-3 group has HIGH n=24 and SECONDARY n=7, median difference 0.0595389352, rank-biserial 0.083333, and AUC 0.541667, also `SUPPORTED_DIRECTION`. The gap is not concentrated in the GlossBERT subgroup. The within-Tier-3 separation is small. Dropping the largest HIGH residual and the smallest SECONDARY residual does not remove the overall directional result, so the comparison is not extreme-driven. HIGH and SECONDARY are not confined to disjoint parts of speech. The only part of speech with at least three rows in each class is adverb: HIGH 5 and SECONDARY 6, median difference 0.0797654836, AUC 0.733333. Adjective, noun, and verb cells are `NOT_COMPUTABLE`. Whitespace token count has HIGH median 4 and SECONDARY median 3, with Spearman against the residual of 0.092940 for HIGH and 0.603202 for SECONDARY. Character length has HIGH median 17 and SECONDARY median 12, with Spearman -0.059115 and 0.165145. Content-constituent count is 2 for every SECONDARY row and for almost every HIGH row. Maximum candidate-synset count has Spearman 0.390077 for HIGH and 0.073060 for SECONDARY. Where GlossBERT supplies a constituent, minimum confidence and minimum margin have Spearman near 0 for HIGH. These diagnostics do not retune the residual. + +REJECT is reported separately and is not semantic compositionality NO. REJECT count is 34, minimum 0.1036592522, p10 0.1240175180, p25 0.1569692084, median 0.2174387191, p75 0.2995541264, p90 0.3474576871, maximum 0.5180572821, mean 0.2343664094, and sample standard deviation 0.0963240560. The largest HIGH residual is `on the other hand` (`adv:00119578`, row `1d19d96bfedbade746610820599e28166b4ef6c291fd4d186b95055e0cf22074`) at 0.5908804826, tiers GlossBERT then Extended Lesk. The smallest HIGH residual is `naked as a jaybird` (`adj:00458266`, row `d479d3da8dd2b87a7b30c9710abefd1e87f2706a7ac032dde17fc337bef8ac4c`) at 0.1169573790, tiers Extended Lesk then unique lemma. The largest SECONDARY residual is `at one time` (`adv:00153261`, row `0f1c15024e09410a3336e3910351d2cfe6143b0a98b88ab561b8dedcbe64a5fc`) at 0.5587792172, both constituents GlossBERT. The smallest SECONDARY residual is `sneak thief` (`noun:10616204`, row `00152611a0b327fd51c12b1c9a80ab838874fb68692c7e988e7970e41557c9bb`) at 0.1768283745, tiers Extended Lesk then unique lemma. The largest REJECT residual is `three times` (`adv:00476680`, row `0e17c819392a00600e3089202fef9b2e74036b73b6429724dfe91d14f7334b2b`) at 0.5180572821, both Extended Lesk. The smallest REJECT residual is `family ascaphidae` (`noun:01644542`, row `00181b47ec5143e4625569c7b41909401ccb940250f8138c775be0000ec3ff93`) at 0.1036592522, tiers GlossBERT then unique lemma. These rows are descriptive. They do not become phrase rules. + +All six preregistered conditions hold: readiness reproduces, the replay is deterministic, the HIGH median is larger, the rank separation is positive, AUC is above one half, and the separation is not solely one preregistered confound. Residual development state is `RESIDUAL_DEVELOPMENT_ANALYZED_V2`. Candidate status is `CANDIDATE_PROMISING`. `threshold_eligible` stays false because these 225 rows remain a reused development surface. No threshold is frozen. No row receives `semantic_noncompositional` YES or NO. The original residual state stays `CANDIDATE_DISTRIBUTION_FROZEN`. `MODEL_BASED_WSD_CANDIDATE_V1` stays `CANDIDATE_PROMISING`. `RUNE.CONSTITUENT_SENSE_RESOLUTION.v1` stays `DEVELOPMENT_ANALYZED` with lexical status `COVERAGE_INSUFFICIENT`. `selected_source` remains `none`. There is no runtime integration. `measurement_sample_drawn` and `measurement_eligible` stay false. SELECT-005 stays unauthorized. Admitted 0. Settled 0. Gold 0. + +No JSON Schema document exists for this replay family. Original residual spec sha256 remains `39c2914e32557ffe1a456a56f8742ea4fe8f1aaec1dc1da451656cd22f0db32d`. Original residual scores sha256 remains `cea638679faeee1bc1c689823e7c0c08562c4d7ef1f8230bbbf4079239e7c3e7`. Analysis sha256 `4367930648a68c2f84a1fd8e011fa07d9f3bf111303079f3ec07688f7d5425fb`. Confound analysis sha256 `d0f2496ca7623050eb5519969abcf7c5e2d0e23e0c1961859c40cae4dcdb7021`. Decision sha256 `ed0296fe6888e7c9fe864a2c6c7ab6cecbd4f490d6b7d650e442dafc0bd0976d`. Tracker sha256 `6500394d24543d1797eb9a2a05ba31868e035ffcbc7ae568f53116d4b016e62a`. The 225-row manifest, procedure v2, GlossBERT artifacts, resolver artifacts, MAGPIE artifacts, and Korkontzelos–Manandhar artifacts are unchanged. The ledger was not appended. Events sha256 remains `96b74a92d44f1cf9fe152b18e5207176f161ba3bfce528dac38aa4571a742f9c`. + +The next named transition is `RESIDUAL_THRESHOLD_PREREGISTRATION_AUTHORIZATION`. This pass does not authorize it. A threshold still requires a later preregistered stage. `selected_source` remains `none`. +## Residual threshold preregistration — 2026-09-28 + +`RESIDUAL_THRESHOLD_PREREGISTRATION_AUTHORIZATION` freezes the rules for a later one-sided threshold. It does not choose `T_HIGH`. The development residual track stays `RESIDUAL_DEVELOPMENT_ANALYZED_V2` with candidate status `CANDIDATE_PROMISING`. A new track, `RUNE.SEMANTIC_COMPOSITIONALITY_THRESHOLD.v1`, starts at `THRESHOLD_PROCEDURE_PREREGISTERED`. Its threshold value is null. No calibration surface was drawn. No measurement surface was drawn. `threshold_frozen` is false. + +The frozen development evidence says a larger residual is a candidate for semantic noncompositionality. It does not say a smaller residual is semantic compositionality. The future evidence contract is one-sided. A ready row with residual greater than or equal to `T_HIGH` is `YES`. Every other row is `UNKNOWN`. There is no `NO` output and no lower threshold. `REJECT` is not semantic no. `QUARANTINE` is not semantic no. + +The 225 reviewed positional rows are development-only. `development_rows_reusable_for_threshold_selection` is false. That exclusion covers every partition in the development evidence artifact, including the historical v3 through v7 rows. Calibration and measurement, when a later pass is authorized to draw them, come from the same positional-unbind universe and keep the reserve's existing rights and provenance requirements. They must be disjoint from development and from each other. The identity fence is the one Hyperlex already uses, `hyperlexical.holdout_guard.normalized_text_sha256`, which is also the development `row_id`. The normalized surface text is fenced with it. A concrete PWN 3.0 synset key, `synset_pos:synset_offset`, cannot appear on two of the three surfaces. That synset exclusion is justified here, before any draw: the residual is a property of one synset's resolved constituents, so a repeated synset is not an independent observation. A missing offset does not match another missing offset. + +Only `RESIDUAL_READY` rows may enter threshold selection, under the frozen Tier 1, Tier 2, and Tier 3 stack. Unresolved rows stay `UNKNOWN` and do not move `T_HIGH`. WSD confidence and margin are not retuned to raise coverage. Operator `HIGH` is the positive class and operator `SECONDARY` is the negative class. `REJECT` and `QUARANTINE` stay out of the precision denominator. They are still hard safety gates: a candidate threshold fails if any ready `REJECT` row, or any ready `QUARANTINE` row, would be called `YES`. That tolerance is zero, and it is part of the rule now rather than a later adjustment. + +The objective is precision-first. Accuracy, F1, Youden's J, and AUC are not the objective. Among thresholds that pass the gates, the rule keeps the highest `HIGH` recall. If recall ties, the larger threshold wins. Candidate thresholds are the unique ready residual values, quantized to 10 decimal places and sorted ascending. `YES` means residual greater than or equal to the candidate. The direction gate is the already frozen `direction_result`: median difference, rank-biserial, and AUC must all point the same way. It does not require a bootstrap interval to exclude zero. + +The floors were chosen before any calibration labels existed. They are not a function of the development AUC. Calibration needs at least 12 ready `HIGH` rows and 8 ready `SECONDARY` rows. Predicted `YES` support on the comparison set must be at least 8, so one correct prediction cannot pass. Precision must be at least 0.80. The uncertainty method is the Wilson score interval at 95 percent, with the same z as `selection_surface.wilson_interval` (`1.959963984540054`). The lower bound must be at least 0.50. Zero support has no interval and fails the gate. Deleting any one comparison row must leave those precision, Wilson, and support gates intact, so one row cannot be the sole reason a threshold passes. One `REJECT` or `QUARANTINE` call of `YES` can still veto a threshold. These are not the development counts of 28 and 11. + From 8df9ae35a96ca8d2308420558dff6aa0dd54f326 Mon Sep 17 00:00:00 2001 From: Daniel Meyer Date: Mon, 28 Sep 2026 16:55:06 -0700 Subject: [PATCH 35/42] feat(007): stage evaluation-reserve chunk (07) --- .land-chunks/part-07 | 39 +++++++++++++++++++++++++++++++++++++++ 1 file changed, 39 insertions(+) create mode 100644 .land-chunks/part-07 diff --git a/.land-chunks/part-07 b/.land-chunks/part-07 new file mode 100644 index 00000000..9f445a18 --- /dev/null +++ b/.land-chunks/part-07 @@ -0,0 +1,39 @@ +If ready support is short, the state is `CALIBRATION_INSUFFICIENT_SUPPORT`. If the direction is not supported, the state is `NO_DIRECTIONAL_SIGNAL`. If the frozen Tier 3 concentration flag, the extreme-driven flag, or the POS-partition flag is true, the state is `CALIBRATION_CONFOUND_REVIEW` and no threshold is kept. If no candidate passes, the state is `NO_THRESHOLD_PASSES_PRECISION_GATE`. A future successful calibration run may enter `THRESHOLD_FROZEN`. This pass does not. There is one global `T_HIGH`. There is no POS-specific, tier-specific, or length-specific threshold. GlossBERT confidence, margin, and candidate-sense counts are preserved on future rows and are not inputs to the fit. POS, token count, character length, content-constituent count, resolution tier, GlossBERT involvement, and candidate-sense count are descriptive checks. They do not move the threshold after the fact. + +Future calibration order is: freeze the manifest, resolve constituents, freeze the integrated resolutions, compute residuals, freeze the score file and its hash, and only then join operator labels and run this rule. Future measurement applies the already frozen `T_HIGH` and does not change it. Measurement acceptance is a separate contract. Calibration success does not imply measurement success. Measurement needs at least 12 ready `HIGH` rows, 8 ready `SECONDARY` rows, 8 predicted `YES` rows, precision at least 0.80, a Wilson 95 percent lower bound at least 0.50, the same leave-one-out stability, and zero `YES` calls on ready `REJECT` and `QUARANTINE` rows. Short support is `MEASUREMENT_INSUFFICIENT_SUPPORT`. A failed gate is `MEASUREMENT_NOT_ACCEPTED`. A pass is `MEASUREMENT_ACCEPTED`. + +No JSON Schema document exists for this threshold family. `RESIDUAL_THRESHOLD_V1_ARTIFACT_SCHEMA.json` is the field contract for a future numeric artifact. It is not a JSON Schema. The private artifacts, mode 0600, are preregistration sha256 `86b4cb5578fa63d3e8f4f2968ed0e32db5b5f571c9b2604e29597bce70fac5e0`, acceptance sha256 `99aa20bdb046a449571f043b8ddb6c246ed610fb7c7cfc454a020a6c9464da6e`, selection procedure sha256 `afa2fe92e3f70756c6a6f5a2f44d1f1a82f291b58bef171db0db8cc39e13ed1a`, calibration contract sha256 `99c3dc10a5ea6b6990f350163ea975f99b9c1b895504c0260952d0b67a5bd323`, measurement contract sha256 `3463caaa22f0fd45342ce9946e1669bb453f91a776a195b14d62a014f898d2c9`, artifact contract sha256 `87cea8897b709c00c5917f0a00499237f3e9c8923dee974f0184bc98881fa7b4`, and isolation policy sha256 `e24cccdd1a2db315531a98c7468c70b9751083bf2c0f3c4206cad1f6e51b3d26`. Tracker sha256 `5dda813835ee11424f3d803f50ddc9eab5842cf0185f59ca396da207635a1daf`. The residual specification, residual scores, integrated resolution, GlossBERT artifacts, constituent-resolution artifacts, MAGPIE artifacts, Korkontzelos–Manandhar artifacts, procedure v2, the 225-row manifest, and the WordNet limitation artifact are unchanged. The ledger was not appended. Events sha256 remains `96b74a92d44f1cf9fe152b18e5207176f161ba3bfce528dac38aa4571a742f9c`. + +`selected_source` remains `none`. Nothing was integrated into runtime. `HLX-EXP-2026-09-27-SELECT-005` remains unauthorized. Admitted, settled, and gold stay 0. The next named transition is `RESIDUAL_CALIBRATION_SURFACE_DRAW_AUTHORIZATION`. This pass does not authorize it. +## Residual calibration surface — 2026-09-28 + +`RESIDUAL_CALIBRATION_SURFACE_DRAW_AUTHORIZATION` drew one calibration surface from the positional-unbind universe and froze `CALIBRATION_INSUFFICIENT_SUPPORT`. The threshold value stays null. `threshold_frozen` is false. No measurement surface was drawn. + +The calibration contract does not set a sample size or a seed. The draw receipt froze those parameters before any score or operator label was read. The rule is `positional_stratified_hash_prefix_v1`: the same gated WordNet positional universe used by prior Hyperlex evaluation surfaces, stratified by source POS and token count, ordered by `normalized_text_sha256` ascending, without replacement, at most 2 rows per cell. There is no RNG, so the draw seed is null. The cell size is the historical `draw_measurement` default. It was not computed from operator labels or from the development HIGH/SECONDARY distribution. The identity fence removed the 225 development rows, their normalized text, and their PWN 3.0 synsets. The same synset is not kept on two surfaces. MAGPIE and Korkontzelos–Manandhar rows in this universe are the same 225 development identities. No measurement reserve existed. + +The eligible universe after those fences and before the prefix was 44031 rows. The draw kept 27 rows because the adjective, four-token cell had one remaining row. Two reconstructions of the draw matched. Manifest sha256 `ba561935cff5f9f0a36af0b8b24e4b69bce5fb2c61b5ea2236f0a664c56bcf5d`. Isolation overlap with development identities and synsets is 0. Isolation sha256 `8743c864ed9e7152a5bccb5d6304b62aff8ffed5e894fb1d40ab270d696da9a5`. Draw receipt sha256 `7448adb8c9448c3737a7b7c2c9a3116ced23d2bab7e432d011bc41229c3ef283`. + +Only after that hash did the frozen Tier 1, Tier 2, and Tier 3 stack run. Resolver output, GlossBERT scores, and integrated resolutions were each produced twice and matched. Integrated resolution sha256 `fde102faa317c2bdfd374bc2b2ddda680434bb1847f93b3991e1532da2764dc2`. The frozen residual pipeline then ran twice: `sentence-transformers/all-MiniLM-L6-v2` at revision `1110a243fdf4706b3f48f1d95db1a4f5529b4d41`, composition `normalized_mean_v1`, distance `1 - cosine`, 10 decimal places. Six rows were `RESIDUAL_READY` and scored. Twenty-one rows stayed `UNKNOWN`. Score sha256 `f775849b3bb8cdec2ac8eeeea0100e51853fefcebbd88d6b3b4efb33fa8a3895`. The score receipt records `operator_labels_joined` false. Receipt sha256 `44cd2a9b57587d5330d830e5dda49fdbbc9b17d95553f030b3884feec46c9c29`. + +Operator labels were joined only after that hash. None of the 27 calibration rows has an existing operator-review label. Ready HIGH, SECONDARY, REJECT, and QUARANTINE are all 0. Unknown-by-class counts are all 0. Six scored rows and 21 unknown rows are unlabeled. The support gate requires 12 ready HIGH and 8 ready SECONDARY. It fails. Direction, confound review, and threshold search were not run. The candidate count is null. No threshold was selected. The failure state is `CALIBRATION_INSUFFICIENT_SUPPORT`. Failure sha256 `1d3f1f26609c1f1a47790d94730fba50e9f4d888dd8d7d89e6fa698a924fb3a8`. Label sha256 `b4c93faed22d3c294d46768a510eb9695c5159f2f84bfb8c4f7ab15b7f2332c2`. Analysis sha256 `ac17d7e3c634cb5f984251e6a4f4090eecd0ff7e0d809947a48b689d454db625`. Search sha256 `f92c5aded6822747811da2b18c796e59974de1e673db55b3f7a481a71be89a3c`. + +The draw was not repeated. The precision floor, Wilson rule, safety vetoes, resolver, residual model, and support floors were not changed. There is no `NO` output. There is no subgroup threshold. `selected_source` remains `none`. Nothing was integrated into runtime. `HLX-EXP-2026-09-27-SELECT-005` remains unauthorized. Admitted, settled, and gold stay 0. The ledger was not appended. Events sha256 remains `96b74a92d44f1cf9fe152b18e5207176f161ba3bfce528dac38aa4571a742f9c`. The preregistration artifacts, the 225-row development manifest, development residual scores, and the other sealed development artifacts are unchanged. No JSON Schema document exists for this calibration family. Tracker sha256 `07b911497c4e95d1b251c7bb98f78224f804b857fd61dffd7c198eecdf21636f`. + +The next legal transition is none. The frozen contract does not authorize a redraw, and a null threshold cannot be applied to a measurement surface. +## Threshold v1 calibration closure — 2026-09-28 + +The frozen calibration execution stays `CALIBRATION_INSUFFICIENT_SUPPORT`. Its threshold value stays null and `threshold_frozen` stays false. This pass does not redraw, does not relabel, and does not run threshold search. The 27-row manifest, the integrated resolution, and the residual scores stay byte-for-byte as frozen. Failure sha256 remains `1d3f1f26609c1f1a47790d94730fba50e9f4d888dd8d7d89e6fa698a924fb3a8`. + +The execution stopped because the novel surface had no operator labels: 0 of 27 rows carry HIGH, SECONDARY, REJECT, or QUARANTINE. That cause is `NOVEL_SURFACE_UNLABELED`. It is not a finding that the labeled HIGH/SECONDARY distribution was too small. Label-availability review sha256 `3a6c967c3542096714e25b59544bd68d89d7a9bc28ee253dec1b9f224b5a0484`. Redraw stays unauthorized. Labeling the same 27 rows later would not change their selection or their frozen scores, so that operation stays potentially eligible as a record. It is not authorized here. No operator packet was created. + +A separate count makes threshold calibration impossible on this surface even after perfect labels. The frozen floors still require at least 12 ready HIGH rows and 8 ready SECONDARY rows, 20 target-ready rows together. The frozen score file has 6 `RESIDUAL_READY` rows and 21 `UNKNOWN` rows. Six ready rows cannot be split into 12 HIGH and 8 SECONDARY. The finding is `CALIBRATION_DESIGN_SUPPORT_MISMATCH_CONFIRMED`. The draw policy was frozen without a sample size that could meet the already-frozen post-resolution support floors. That is a preregistration design gap. It is not a license to edit the frozen calibration. + +`RUNE.SEMANTIC_COMPOSITIONALITY_THRESHOLD.v1` is therefore closed as `CLOSED_CALIBRATION_DESIGN_INSUFFICIENT`. The execution id `RESIDUAL_THRESHOLD_V1_CALIBRATION_EXECUTION_001` remains `CALIBRATION_INSUFFICIENT_SUPPORT`. Closure sha256 `a5dbdb8ae497c25e711a81a880ad26ec7ef1ace917278c807497af5ec37e5002`. Tracker sha256 `a2a71c5da91d194337b2cc0de55692b712d5568694debf5350ede5ea0554d6c2`. No JSON Schema document exists for this family. `selected_source` remains `none`. Nothing was integrated into runtime. `HLX-EXP-2026-09-27-SELECT-005` remains unauthorized. Admitted, settled, and gold stay 0. No measurement surface was drawn. The ledger was not appended. Events sha256 remains `96b74a92d44f1cf9fe152b18e5207176f161ba3bfce528dac38aa4571a742f9c`. The preregistration artifacts and the development artifacts are unchanged. + +The next named transition is `THRESHOLD_V2_PREREGISTRATION_AUTHORIZATION`. This pass does not authorize it. A v2 preregistration, if a later pass authorizes one, would need a larger blind calibration surface chosen before labels. It may use previously observed resolution coverage, because readiness does not depend on operator labels. It may not use labels to select rows, and it may not redraw this v1 surface. +## Threshold v2 preregistration — 2026-09-28 + +`THRESHOLD_V2_PREREGISTRATION_AUTHORIZATION` opens `RUNE.SEMANTIC_COMPOSITIONALITY_THRESHOLD.v2` at `THRESHOLD_PROCEDURE_PREREGISTERED`. It does not draw a calibration row, a measurement row, or a threshold. Threshold v1 stays sealed: execution `CALIBRATION_INSUFFICIENT_SUPPORT`, disposition `CLOSED_CALIBRATION_DESIGN_INSUFFICIENT`, threshold value null, redraw prohibited, measurement blocked. Failure sha256 remains `1d3f1f26609c1f1a47790d94730fba50e9f4d888dd8d7d89e6fa698a924fb3a8`. Closure sha256 remains `a5dbdb8ae497c25e711a81a880ad26ec7ef1ace917278c807497af5ec37e5002`. + +v2 changes the raw-surface design. The resolver stack, GlossBERT revision and abstention, MiniLM residual, `normalized_mean_v1`, `1 - cosine`, YES/UNKNOWN output, and the precision-first selector stay as in v1. The selector id remains `residual_threshold_v1.precision_gated_recall_v1`. The gates stay ready HIGH at least 12, ready SECONDARY at least 8, predicted YES at least 8, precision at least 0.80, Wilson 95 percent lower bound at least 0.50 with z `1.959963984540054`, and zero YES calls on ready REJECT and QUARANTINE. Those gates were not lowered. + From a722d9eff5cb3897f8276b186c0e8b304b7dc50f Mon Sep 17 00:00:00 2001 From: Daniel Meyer Date: Mon, 28 Sep 2026 16:55:46 -0700 Subject: [PATCH 36/42] feat(007): stage evaluation-reserve chunk (08) --- .land-chunks/part-08 | 41 +++++++++++++++++++++++++++++++++++++++++ 1 file changed, 41 insertions(+) create mode 100644 .land-chunks/part-08 diff --git a/.land-chunks/part-08 b/.land-chunks/part-08 new file mode 100644 index 00000000..5e5cb525 --- /dev/null +++ b/.land-chunks/part-08 @@ -0,0 +1,41 @@ +The raw quota is `ceil(20 / 0.20 × 2.0) = 200`. Twenty is the sum of the unchanged 12 and 8 floors, not a new gate. The readiness rate 0.20 sits below the lower observed label-independent coverage, 6/27 on the v1 calibration surface. The development coverage 73/225 is recorded and is not the rate used. HIGH prevalence, SECONDARY prevalence, residual shape, AUC, and threshold performance are not quota inputs. Calibration and measurement each have their own quota of 200. They are not one shared pool that is split after results. + +A later draw, if authorized, enumerates the same positional-unbind universe, applies the development fences, and also excludes the frozen v1 calibration identities and synsets. It does not read v1 labels or v1 scores to choose rows. Cells are source POS by token count. Inside a cell the order is `normalized_text_sha256` ascending. Rows are taken round-robin across cells until the calibration quota is filled, then the same walk continues until the measurement quota is filled. There is no RNG. The walk does not stop because support has been reached. If either quota cannot be filled, the state is `SURFACE_QUOTA_UNFILLED` and the quotas are not reduced. Both manifests are frozen before any v2 resolution. Measurement stays unresolved, unscored, and unlabeled until a later authorization after a frozen threshold. Threshold refit on measurement is prohibited. Calibration labels are joined only after the calibration score hash. The v1 failure states remain, and a short v2 sample still does not authorize a redraw. + +No JSON Schema document exists for this family. Preregistration sha256 `1c962703e12c5c48fd279dc766fd4d1d3108c40486a10789fdda112926e98261`. Sample-size sha256 `46e3d32b4a4c8af73c9197557b09ceed872b1918d41143ed551bb3b4572c6151`. Calibration contract sha256 `51404e675870c3e99ff6bb51f3aa51e95aa585a360fe782e3c89d11927345934`. Measurement contract sha256 `f96b7ae1f20fbc9850cf3dcc6f6f74ad046bf35755e18ccc7e6747441184adee`. Selection procedure sha256 `1c8a82ef939a33bbbb327f0f760439113432645438361176de36159a8f56c55c`. Isolation policy sha256 `a9f5ae333934ff38a75a0a3e7adad5a5abf78b422f370560d0d9735765372189`. Acceptance sha256 `5cea52584a420e91e7b5f8e3512fbb657610bfb41d95008e52346b59cbfe3d50`. Artifact contract sha256 `dc6017a49ff4ea14f664a4f11a1f2501658cda177d25bd984d491c04f394e327`. Tracker sha256 `041f095c5ca0271e075c8db1a67ea401764486930c86fa5ff2bc96df7d8f1f71`. `selected_source` remains `none`. Admitted, settled, and gold stay 0. The ledger was not appended. Events sha256 remains `96b74a92d44f1cf9fe152b18e5207176f161ba3bfce528dac38aa4571a742f9c`. + +The next named transition is `THRESHOLD_V2_SURFACE_DRAW_AUTHORIZATION`. This pass does not authorize it. +## Threshold v2 surface draw — 2026-09-28 + +`THRESHOLD_V2_SURFACE_DRAW_AUTHORIZATION` freezes both v2 surfaces in one allocation. `RUNE.SEMANTIC_COMPOSITIONALITY_THRESHOLD.v2` is now `SURFACES_FROZEN`. The calibration manifest has 200 rows and the measurement manifest has 200 rows. Both were frozen before any resolution. Measurement stays `SEALED`, unresolved, unscored, and unlabeled. The threshold value stays null and the threshold stays unfrozen. `selected_source` stays `none`. There is no runtime integration. Measurement is not eligible. SELECT-005 stays unauthorized. Admitted, settled, and gold stay 0. + +The allocation rule is `positional_stratified_hash_round_robin_quota_v2`. The draw seed is null and no RNG was used. The source positional-unbind universe is 63882 rows. After the frozen fences, 44004 rows remain eligible. Cells are source POS by token count, ordered by source POS then token count, and inside a cell by `normalized_text_sha256` ascending. One walk emits round-robin across cells and skips exhausted cells. The first 200 emissions are `CALIBRATION_V2`. The next 200 are `MEASUREMENT_V2`. The walk does not stop on support or labels. Operator labels were not loaded or joined. Extended Lesk, GlossBERT, MiniLM, and residual scoring did not run. Two reconstructions were identical. + +The fences drop 255 `EVAL_SPENT` rows, 184 `TRAIN_CONSUMED` rows, and 63912 non-positional rows. They then drop 225 development row ids, 215 additional rows that carry a development synset, 27 v1 calibration row ids, 29 additional rows that carry a v1 calibration synset, and 19382 duplicate-synset rows. The historical v3–v7 partitions sit inside the 225 development rows. The v5 probe surfaces are inside that same fence. No measurement reserve exists. The single historical adj/4 row was already on the v1 calibration surface, so that cell is empty here. The quotas still fill exactly. No v1 calibration row is reused. + +Calibration and measurement share no row id, normalized text, or synset. Each of those overlaps with development and with the v1 calibration surface is 0. Every synset pair among development, v1 calibration, v2 calibration, and v2 measurement is 0. + +Calibration manifest sha256 `34bc70b93039fc6a5ba4bb58685e0fbfe6f91dd1f86a64fec8326a84013a6465`. Measurement manifest sha256 `78ca09ab14912681d028e8b9b1c77a8daf1d0c8c81561434eb9b6ade45a753e5`. Draw receipt sha256 `b89cbd357138c8b9eabeabeca055695b15fd2163840438e53989a920191d3419`. Isolation report sha256 `6ac513d9d7b3dd366f7c6f5ce5ca6821a33c5907d4bf2d1de45dc7a5a932bc38`. Distribution report sha256 `51d9a98a849248e40aadafd277c1c44adf0c7a7fcc842864f00d2fc3d8125886`. Tracker sha256 `a4264943f0313dcde85c6da8b833ff0c45c5c23972e1a4d0f2e69fa00c3e09ad`. Preregistration sha256 remains `1c962703e12c5c48fd279dc766fd4d1d3108c40486a10789fdda112926e98261`. Sample-size sha256 remains `46e3d32b4a4c8af73c9197557b09ceed872b1918d41143ed551bb3b4572c6151`. The tracker before this draw was `041f095c5ca0271e075c8db1a67ea401764486930c86fa5ff2bc96df7d8f1f71`. v1 failure sha256 remains `1d3f1f26609c1f1a47790d94730fba50e9f4d888dd8d7d89e6fa698a924fb3a8`. Events sha256 remains `96b74a92d44f1cf9fe152b18e5207176f161ba3bfce528dac38aa4571a742f9c`. The ledger was not appended. No JSON Schema document exists for this family. + +The next named transition is `THRESHOLD_V2_CALIBRATION_EXECUTION_AUTHORIZATION`. This pass does not authorize it. +## Threshold v2 calibration execution — 2026-09-28 + +`THRESHOLD_V2_CALIBRATION_EXECUTION_AUTHORIZATION` ran the frozen stack on the 200-row calibration surface. `RUNE.SEMANTIC_COMPOSITIONALITY_THRESHOLD.v2` is now `CALIBRATION_INSUFFICIENT_SUPPORT`. The threshold value stays null and the threshold stays unfrozen. Measurement stays `SEALED`. Its manifest sha256 remains `78ca09ab14912681d028e8b9b1c77a8daf1d0c8c81561434eb9b6ade45a753e5`. No measurement resolution, score, or label artifact exists. + +Resolution was run twice and matched. GlossBERT revision `0cc3b83af5496e27ebcc95ef0cf37ea0a9281a7a` scored 200 Lesk ties. Structural pointer conflicts stayed on Tier 1 and did not enter GlossBERT. Residual scoring used `sentence-transformers/all-MiniLM-L6-v2` revision `1110a243fdf4706b3f48f1d95db1a4f5529b4d41`, composition `normalized_mean_v1`, and `1 - cosine` at 10 decimals. Encoding ran twice and matched. 60 rows are `RESIDUAL_READY` and 140 are `UNKNOWN`. The score receipt was hashed with operator labels unloaded and unjoined, and with measurement untouched, before the label join. + +The frozen operator-review inventory contains no label for these 200 rows. Ready HIGH is 0, ready SECONDARY is 0, ready REJECT is 0, and ready QUARANTINE is 0. Unlabeled rows are 200, of which 60 are residual-ready. The support gate requires ready HIGH at least 12 and ready SECONDARY at least 8. It fails. Direction, confound review, and threshold search were not run. No threshold candidate was evaluated. Redraw is prohibited. No operator packet was created. + +Resolution sha256 `491b854bcf04d20624e560b63c01823b5634716ec266a4d3a7396d5514defd8d`. Resolution receipt sha256 `461ed206ef3abba6b4a8ba79ed5de72305cbee95a61c96981923c89817c17d2d`. Scores sha256 `01c758af570264c190f4af7805d43d734c9e9a36779f0e0b82675709f213e07c`. Score receipt sha256 `28ddb23bce8cfcf74b8046ee2cfd0908b5dddefa2a0c04562f9c2f8c912e88d5`. Labels sha256 `5cfeab84f16e5ca77d81bda8f1523a3aa20ae70a9828e1ff04a4d64a1930aa1d`. Analysis sha256 `46a8ecb371ec8dfe6ab38875a1500b463fda3db533cffb05129b4d8070963f2a`. Confound analysis sha256 `3695640fd649aab2e077028aae302c7c59578e2085b0d7de16127c948f19e954`. Threshold search sha256 `701ab0858a46490a6df4cf0ba8c41c0b16dc1f6738a0f9b76044812a3bdfb44c`. Failure sha256 `946a686d46b5a2fc47bb9968bf6e39c643e93c297d5a64de9e0fe88fc6eb33eb`. Tracker sha256 `50ee4bd40063c57f1f39e466d298d6c08486d0134184da459b9e1ef5de47bea8`. Calibration manifest sha256 remains `34bc70b93039fc6a5ba4bb58685e0fbfe6f91dd1f86a64fec8326a84013a6465`. v1 failure sha256 remains `1d3f1f26609c1f1a47790d94730fba50e9f4d888dd8d7d89e6fa698a924fb3a8`. Preregistration sha256 remains `1c962703e12c5c48fd279dc766fd4d1d3108c40486a10789fdda112926e98261`. Events sha256 remains `96b74a92d44f1cf9fe152b18e5207176f161ba3bfce528dac38aa4571a742f9c`. The ledger was not appended. `selected_source` remains `none`. Admitted, settled, and gold stay 0. SELECT-005 stays unauthorized. No JSON Schema document exists for this family. + +The next named transition is none. This pass does not authorize measurement or a redraw. +## Threshold v2 blind operator labeling — 2026-09-28 + +`THRESHOLD_V2_BLIND_OPERATOR_LABELING_AUTHORIZATION` freezes operator labels for the same 200-row calibration manifest. `THRESHOLD_V2_CALIBRATION_EXECUTION_001` stays `CALIBRATION_INSUFFICIENT_SUPPORT` and stays sealed. That execution observed ready HIGH 0 and ready SECONDARY 0 because no operator labels existed. This labeling is a successor event. It does not join the new labels to readiness or to residual scores. + +The label-availability review separates v2 from v1. v1 had 6 residual-ready rows, below the combined target-ready floor of 20, so design support was impossible and that closure stays `CLOSED_CALIBRATION_DESIGN_INSUFFICIENT`. v2 has 60 residual-ready rows and 140 unknown rows. Sixty is at least 20, so design support is possible and the missing evidence is the operator labels. Existing labels on these rows were 0. Redraw is not required and is not authorized. The same frozen surface is eligible for blind labeling. Review sha256 `8826b141468ec0dd674d2c7a253acfaa6e1d364ab5191506b424198419166da9`. + +The operator packet contains all 200 calibration rows, in manifest order. Membership does not depend on readiness, residual score, resolution tier, or threshold eligibility. Each row exposes the calibration row id, row id, surface, POS, PWN 3.0 synset, frozen first-sense gloss, and token count. Readiness, residual scores, MiniLM representations, resolver tiers, Extended Lesk, GlossBERT outputs, candidate counts, and threshold floors are absent. No measurement row is included. Measurement sha256 remains `78ca09ab14912681d028e8b9b1c77a8daf1d0c8c81561434eb9b6ade45a753e5`. Packet sha256 `d9d986ec85a1ebea257859af8011758e9c84df270c9547f7b8bd0e34cec970fe`. Packet receipt sha256 `0aff22b3406d9d954a54eabe080e83d2d4cb5e99df279605d9403e614b61dcf0`. The receipt was hashed before the labels were written. + +All 200 rows received a canonical review outcome under the existing operator ontology: HIGH, SECONDARY, REJECT, QUARANTINE, and UNRESOLVED. The label inventory is HIGH 44, SECONDARY 82, REJECT 71, QUARANTINE 3, and UNRESOLVED 0. Those counts are not joined to residual-ready rows. No support reassessment, direction analysis, confound analysis, or threshold search was run. Label sha256 `f9901e334543dac2e9bf258208097cf4c9b48af9dc8b497ff42fe4f1f106e1fc`. Labeling receipt sha256 `4ce71e88c40cd617b9b6b502db48216c51e8cfa22ecea02347c9becc44c967fa`. Tracker sha256 `b40bcb75f152c84d37fc96e834b6d0a23e1f4af562e8ec0225f452c3b000f77a`. + From 445c371fc6c404870dee253ec3f834ffbb70ab72 Mon Sep 17 00:00:00 2001 From: Daniel Meyer Date: Mon, 28 Sep 2026 16:56:19 -0700 Subject: [PATCH 37/42] feat(007): stage evaluation-reserve chunk (09) --- .land-chunks/part-09 | 38 ++++++++++++++++++++++++++++++++++++++ 1 file changed, 38 insertions(+) create mode 100644 .land-chunks/part-09 diff --git a/.land-chunks/part-09 b/.land-chunks/part-09 new file mode 100644 index 00000000..41e2134e --- /dev/null +++ b/.land-chunks/part-09 @@ -0,0 +1,38 @@ +The threshold value stays null and the threshold stays unfrozen. Measurement stays `SEALED`, unresolved, unscored, and unlabeled. `selected_source` remains `none`. Nothing was integrated into runtime. SELECT-005 remains unauthorized. Admitted, settled, and gold stay 0. Calibration manifest sha256 remains `34bc70b93039fc6a5ba4bb58685e0fbfe6f91dd1f86a64fec8326a84013a6465`. Failure sha256 remains `946a686d46b5a2fc47bb9968bf6e39c643e93c297d5a64de9e0fe88fc6eb33eb`. Score sha256 remains `01c758af570264c190f4af7805d43d734c9e9a36779f0e0b82675709f213e07c`. Score receipt sha256 remains `28ddb23bce8cfcf74b8046ee2cfd0908b5dddefa2a0c04562f9c2f8c912e88d5`. Events sha256 remains `96b74a92d44f1cf9fe152b18e5207176f161ba3bfce528dac38aa4571a742f9c`. The ledger was not appended. No JSON Schema document exists for this labeling family. + +The next named transition is `THRESHOLD_V2_CALIBRATION_SUPPORT_REASSESSMENT_AUTHORIZATION`. This pass does not authorize it. +## Threshold v2 support reassessment — 2026-09-28 + +`THRESHOLD_V2_CALIBRATION_SUPPORT_REASSESSMENT_AUTHORIZATION` joins the frozen operator labels to the frozen calibration parent resolution status and residual scores by `row_id`. It checks calibration row id, normalized text hash, and PWN 3.0 synset for equality. It does not relabel a row, recompute resolution, recompute a residual, or change the 60/140 ready split. `THRESHOLD_V2_CALIBRATION_EXECUTION_001` stays `CALIBRATION_INSUFFICIENT_SUPPORT` and stays sealed. The resolution file stores constituent lines. Its 200 parent rows are the row identity, and that set matches the manifest and the labels. + +Each joined row records only the calibration row id, row id, surface, POS, synset, operator label, row resolution status, and residual score. An unknown row has a null residual. Ready rows are HIGH 13, SECONDARY 22, REJECT 24, QUARANTINE 1, and UNRESOLVED 0. Unknown rows are HIGH 31, SECONDARY 60, REJECT 47, QUARANTINE 2, and UNRESOLVED 0. The raw label inventory remains HIGH 44, SECONDARY 82, REJECT 71, QUARANTINE 3, and UNRESOLVED 0. + +The support gate passes. Ready HIGH is 13 and ready SECONDARY is 22. The floors remain 12 and 8. The next named transition is `THRESHOLD_V2_DIRECTION_EVALUATION_AUTHORIZATION`. This pass does not authorize it. Direction analysis, confound review, and threshold search were not run. Join sha256 `02088379b5ebe0f26106e830a730e8a90b2281d83c37bf1e750e1846bffa128b`. Join receipt sha256 `05748f812491be4ebe215a9a88224073bb2349b465c09dd2598f98d60e02947d`. Support reassessment sha256 `fe5d1061c1f9aed9cad4a1ad8e1dbabc5f76d13d4b13eacb88cef12c347e560d`. Tracker sha256 `170e4b66cfa6764034367ffef685cff67c88367833eb75b46d05f7b3f10f867c`. + +The threshold stays unfrozen. Measurement stays `SEALED`, unresolved, unscored, and unlabeled. Measurement sha256 remains `78ca09ab14912681d028e8b9b1c77a8daf1d0c8c81561434eb9b6ade45a753e5`. Score sha256 remains `01c758af570264c190f4af7805d43d734c9e9a36779f0e0b82675709f213e07c`. Resolution sha256 remains `491b854bcf04d20624e560b63c01823b5634716ec266a4d3a7396d5514defd8d`. Failure sha256 remains `946a686d46b5a2fc47bb9968bf6e39c643e93c297d5a64de9e0fe88fc6eb33eb`. Label sha256 remains `f9901e334543dac2e9bf258208097cf4c9b48af9dc8b497ff42fe4f1f106e1fc`. `selected_source` remains `none`. Nothing was integrated into runtime. SELECT-005 remains unauthorized. Admitted, settled, and gold stay 0. Events sha256 remains `96b74a92d44f1cf9fe152b18e5207176f161ba3bfce528dac38aa4571a742f9c`. The ledger was not appended. No JSON Schema document exists for this reassessment family. v1 remains `CLOSED_CALIBRATION_DESIGN_INSUFFICIENT`. +## Threshold v2 direction evaluation — 2026-09-28 + +`THRESHOLD_V2_DIRECTION_EVALUATION_AUTHORIZATION` reads residual strings already stored on the frozen support join. The only directional comparison is ready HIGH against ready SECONDARY. Ready HIGH is 13 and ready SECONDARY is 22. Ready REJECT 24 and ready QUARANTINE 1 are context counts and are not part of the comparison. No row was trimmed, winsorized, or removed. Residuals, labels, readiness, constituent senses, GlossBERT outputs, and Extended Lesk decisions were not recomputed. `THRESHOLD_V2_CALIBRATION_EXECUTION_001` stays `CALIBRATION_INSUFFICIENT_SUPPORT` and stays sealed. Support reassessment 001 stays `SUPPORT_GATE_PASSED`. + +The distributions use the frozen `full_distribution` convention: linear interpolation on the sorted residuals, rank `(n - 1) * (percent / 100)`, then the residual quantum with round-half-even. HIGH count 13, min 0.1077481638, p10 0.2022084726, p25 0.3475068269, median 0.3639930562, p75 0.3871338567, p90 0.4030255710, max 0.6656712845, mean 0.3506075686, std 0.1300277836. SECONDARY count 22, min 0.0373950459, p10 0.1220861227, p25 0.2362514965, median 0.3272977478, p75 0.3834981568, p90 0.5219075205, max 0.5535764009, mean 0.3203500904, std 0.1536408042. + +HIGH mean minus SECONDARY mean is 0.0302574782. HIGH median minus SECONDARY median is 0.0366953084. Mann-Whitney U for HIGH is 166.000000. HIGH-larger pairs are 166. SECONDARY-larger pairs are 120. Ties are 0. Rank-biserial correlation is 0.160839. Descriptive ROC AUC, with HIGH positive, is 0.580420. AUC is descriptive and is not a threshold. Uncertainty is `UNCERTAINTY_INTERVAL_NOT_COMPUTED`: the v2 preregistration and the v2 selection procedure do not define a calibration-direction interval, and `direction_result` does not require one. The frozen rule returns `SUPPORTED_DIRECTION`. + +`THRESHOLD_V2_DIRECTION_EVALUATION_001` is `SUPPORTED_DIRECTION`. The next named transition is `THRESHOLD_V2_CONFOUND_EVALUATION_AUTHORIZATION`. This pass does not authorize it. Direction scope is `DIRECTION_ONLY`. Confound analysis did not run. Threshold search did not run. No precision or Wilson calculation ran. Analysis sha256 `b68483acab29fe5918a81281798974fb85000a18af30ec3411c2e30500292783`. Decision sha256 `d408c81ff5766d40ece5c18c2c971ce2e05c23b8163fa9a809f2eded58a852eb`. Tracker sha256 `7a2e3eb6265ddb90624f2ef5034d77f486d1cec83e2c2792d7659a56bd076abe`. Support join sha256 remains `02088379b5ebe0f26106e830a730e8a90b2281d83c37bf1e750e1846bffa128b`. + +The threshold stays null and unfrozen. Measurement stays `SEALED`, unresolved, unscored, and unlabeled. Measurement sha256 remains `78ca09ab14912681d028e8b9b1c77a8daf1d0c8c81561434eb9b6ade45a753e5`. Score sha256 remains `01c758af570264c190f4af7805d43d734c9e9a36779f0e0b82675709f213e07c`. Label sha256 remains `f9901e334543dac2e9bf258208097cf4c9b48af9dc8b497ff42fe4f1f106e1fc`. Resolution sha256 remains `491b854bcf04d20624e560b63c01823b5634716ec266a4d3a7396d5514defd8d`. Failure sha256 remains `946a686d46b5a2fc47bb9968bf6e39c643e93c297d5a64de9e0fe88fc6eb33eb`. `selected_source` remains `none`. Nothing was integrated into runtime. SELECT-005 remains unauthorized. Admitted, settled, and gold stay 0. Events sha256 remains `96b74a92d44f1cf9fe152b18e5207176f161ba3bfce528dac38aa4571a742f9c`. The ledger was not appended. No JSON Schema document exists for this direction family. v1 remains `CLOSED_CALIBRATION_DESIGN_INSUFFICIENT`. +## Threshold v2 calibration completion — 2026-09-28 + +`THRESHOLD_V2_CALIBRATION_COMPLETION_AUTHORIZATION` runs the remaining preregistered calibration gates in order on the frozen support join. It does not recompute residuals, labels, readiness, GlossBERT, MiniLM, or the resolver. It does not redraw. The frozen confound stop remains tier-3 concentration, extreme-driven separation, or a POS partition. Nuisance associations do not add a stop. Extreme-driven is False. POS-partitioned is False. Tier-3 concentrated is False. Confound result is `PASS`. + +Token count, character length, and content-constituent count are copied from the frozen manifest, surface, and score constituents. Candidate-sense count and GlossBERT confidence and margin were not stored on the calibration artifacts, so those associations are `NOT_COMPUTABLE` and GlossBERT was not rerun. Nuisance Spearman results: token_count HIGH -0.111187, SECONDARY 0.356766; character_length HIGH -0.272622, SECONDARY -0.118083; content_count HIGH 0.284901, SECONDARY -0.017198; max_candidate_senses HIGH NOT_COMPUTABLE, SECONDARY NOT_COMPUTABLE; min_glossbert_confidence HIGH NOT_COMPUTABLE, SECONDARY NOT_COMPUTABLE; min_glossbert_margin HIGH NOT_COMPUTABLE, SECONDARY NOT_COMPUTABLE. + +Threshold search evaluated 60 unique ready residuals. Predicted-YES support passed for 50. The precision floor passed for 2. The Wilson lower bound passed for 0. Leave-one-out passed for 0. The REJECT veto passed for 1. The QUARANTINE veto passed for 47. Candidates passing every frozen gate: 0. No candidate passed. The threshold value stays null. Training readiness is `BLOCKED_BY_THRESHOLD_V2_CALIBRATION` because of `NO_THRESHOLD_PASSES_PRECISION_GATE`. Analysis sha256 `8e4c9468537bce4f35f0aee6375c2efd931cea1c54f7a72b9a138bbe8100d930`. Decision sha256 `cff6b3bfbca57b7aa26c5b057ad8e83507ff31d9edb1fd08996ff473670e5991`. Completion search sha256 `2df31d98f4c23b340f0ae1b8747326b6f8b97ed0e82a13ef461c82901c3d56ed`. Threshold artifact sha256 `99ecfdd553f46f34dcc5c3e20c644b0bcbc289ba8f200d9b0407e20874e1a301`. Receipt sha256 `d1221c7c98a926bb04a9ed5ddf5cedf8a3111f0ef0083ec60232eb8709d1b982`. Tracker sha256 `e674c207d9eb160159a673c23f7e2a2e8262b5bfc963ccf81b216d8291dc4b0f`. + +Training readiness is `BLOCKED_BY_THRESHOLD_V2_CALIBRATION`. Training stays unauthorized. A frozen threshold does not authorize SELECT-005, BEST replacement, or model promotion. Measurement stays `SEALED`. Measurement sha256 remains `78ca09ab14912681d028e8b9b1c77a8daf1d0c8c81561434eb9b6ade45a753e5`. The next named transition is `NONE`. This pass does not authorize it. `selected_source` remains `none`. Admitted, settled, and gold stay 0. Events sha256 remains `96b74a92d44f1cf9fe152b18e5207176f161ba3bfce528dac38aa4571a742f9c`. The ledger was not appended. No JSON Schema document exists for this completion family. Execution 001, the direction artifacts, and v1 stay sealed. `THRESHOLD_V2_CALIBRATION_EXECUTION_001` remains `CALIBRATION_INSUFFICIENT_SUPPORT`. +## Threshold artifact schema hardening — 2026-09-28 + +`THRESHOLD_ARTIFACT_SCHEMA_HARDENING_AUTHORIZATION` adds a retrospective JSON Schema family for residual-threshold artifacts that were already emitted. This is contract hardening. It does not repair the calibration. v2 failed because no threshold candidate passed the frozen statistical gates, and that result stays `NO_THRESHOLD_PASSES_PRECISION_GATE`. The threshold value stays null. Frozen artifact bytes and hashes are unchanged. A mismatch is reported as `SCHEMA_CONFORMANCE_GAP` and is not repaired by editing a sealed artifact. + +The schemas describe the contracts as emitted. Historical `json_schema_exists` values stay false. Twenty-six applicable v1 and v2 artifacts validate, and eighteen cross-artifact checks pass. The support join has 200 rows. Ready plus unknown is 200. The calibration manifest, operator labels, and support join share one row-id set of 200. The measurement manifest stays disjoint and its sha256 is unchanged. Referenced manifest, label, score, and support-join hashes match the files they name. v1 labels remain unlabeled. Thirty other residual-threshold files stay outside this family, including preregistrations, the older field contracts, labels, scores, and resolutions. Those field contracts are not JSON Schema and were not rewritten. The conformance outcome is `PASS`. `schema_conformance_gaps` is empty. + From e77c390d7d740c62fa04bc6808f3adaa65322835 Mon Sep 17 00:00:00 2001 From: Daniel Meyer Date: Mon, 28 Sep 2026 16:56:54 -0700 Subject: [PATCH 38/42] feat(007): stage evaluation-reserve chunk (10) --- .land-chunks/part-10 | 38 ++++++++++++++++++++++++++++++++++++++ 1 file changed, 38 insertions(+) create mode 100644 .land-chunks/part-10 diff --git a/.land-chunks/part-10 b/.land-chunks/part-10 new file mode 100644 index 00000000..7b1c3276 --- /dev/null +++ b/.land-chunks/part-10 @@ -0,0 +1,38 @@ +The family is `specs/007-hyperlexical-model/schemas/residual-threshold/residual-threshold-common.schema.json`, `residual-threshold-surface-manifest.schema.json`, `residual-threshold-receipt.schema.json`, `residual-threshold-support-join.schema.json`, `residual-threshold-direction-analysis.schema.json`, `residual-threshold-confound-analysis.schema.json`, `residual-threshold-threshold-search.schema.json`, `residual-threshold-decision.schema.json`, and `residual-threshold-completion-receipt.schema.json`. Shared terms live in `$defs`: sha256, row_id, pwn30_synset, operator_label, row_resolution_status, threshold_state, measurement_state, and artifact_reference. An unfrozen threshold record requires `threshold_value` null. A frozen record requires a numeric threshold and its frozen metrics. No frozen success record was emitted; that branch is contract-only. The public report is `specs/007-hyperlexical-model/schemas/residual-threshold/CONFORMANCE.json`. + +Measurement stays `SEALED`. Measurement sha256 remains `78ca09ab14912681d028e8b9b1c77a8daf1d0c8c81561434eb9b6ade45a753e5`. Tracker sha256 remains `e674c207d9eb160159a673c23f7e2a2e8262b5bfc963ccf81b216d8291dc4b0f`. Events sha256 remains `96b74a92d44f1cf9fe152b18e5207176f161ba3bfce528dac38aa4571a742f9c`. The ledger was not appended. Training stays unauthorized. `selected_source` remains `none`. SELECT-005 remains unauthorized. Admitted, settled, and gold stay 0. The next named transition remains `NONE`. This pass does not authorize measurement, another threshold search, or a new residual design. +## Threshold v2 postmortem and contract split — 2026-09-28 + +`THRESHOLD_V2_POSTMORTEM` records the canonical v2 conclusion without rewriting sealed artifacts. Surface design and support were adequate. Direction was positive and weak: AUC 0.580420, rank-biserial 0.160839, ready HIGH 13, ready SECONDARY 22. Confound passed. Precision and Wilson were insufficient: 2 candidates met the precision floor and 0 met the Wilson floor. The conclusion is `CURRENT_RESIDUAL_REPRESENTATION_INSUFFICIENT_FOR_REQUIRED_PRECISION`. The threshold value stays null. Measurement stays `SEALED` at sha256 `78ca09ab14912681d028e8b9b1c77a8daf1d0c8c81561434eb9b6ade45a753e5`. Tracker sha256 remains `e674c207d9eb160159a673c23f7e2a2e8262b5bfc963ccf81b216d8291dc4b0f`. The full seal is `specs/007-hyperlexical-model/threshold-v2-postmortem.md`. + +Workstream A, the threshold artifact contract, is separate from workstream B. The contract family is `specs/007-hyperlexical-model/schemas/hyperlex/residual-threshold/`. Validation is `VALIDATE_ONLY`. The outcome is `HISTORICAL_VALIDATION_COMPLETE`: 30 applicable artifacts and 22 semantic checks pass, and both gap lists are empty. The contract note is `specs/007-hyperlexical-model/threshold-artifact-schema-contract.md`. The retrospective family from the previous hardening pass is unchanged. + +Workstream B drafts `RUNE.SEMANTIC_COMPOSITIONALITY_EVIDENCE.v2` at state `SEMANTIC_EVIDENCE_V2_SPEC_DRAFTED`. It does not preregister a model, draw a surface, or open a new threshold lane. The v2 measurement disposition is `RETAIN_FOR_V2_ONLY`. Specifications are `semantic-evidence-v2.md`, `semantic-evidence-v2-state-machine.md`, `semantic-evidence-v2-data-contract.md`, and `semantic-evidence-v2-evaluation-contract.md`. + +`TRAINING_DEPENDENCY_REVIEW` finds `SEMANTIC_THRESHOLD_NOT_A_HARD_TRAINING_DEPENDENCY`. SELECT-005 is a schedule experiment blocked by the missing fresh reserve and the unsealed admission receipt, not by the residual threshold. `HLX_THRESHOLD_AUTHORIZATION` seals numeric select-metric thresholds and is a different object. The residual lane flag `BLOCKED_BY_THRESHOLD_V2_CALIBRATION` stays on that lane. This review does not authorize training, SELECT-005, measurement, or a redraw. Events sha256 remains `96b74a92d44f1cf9fe152b18e5207176f161ba3bfce528dac38aa4571a742f9c`. The ledger was not appended. The review is `specs/007-hyperlexical-model/training-dependency-review.md`. +## SELECT-005 reserve and admission preparation — 2026-09-28 + +`SELECT_005_RESERVE_AND_ADMISSION_PREPARATION_AUTHORIZATION` re-censused the live settlement stream for `HLX-EXP-2026-09-27-SELECT-005`. The id stays unsealed. No preregistration file, arm directory, reserve binding, or admission receipt was created. The ledger was not appended. Events sha256 remains `96b74a92d44f1cf9fe152b18e5207176f161ba3bfce528dac38aa4571a742f9c`. Projection sha256 remains `77e22433203879b252f7a9e309d2013d7550101d1c4a014b494d2c96df87d0e0`. + +The proposed schedule is unchanged and is not sealed. Its canonical record sha256 is `ffb7f0399d89cd262319ab216104e60e97cf500efeda76a31b9f47a8262f2fd0`. Control remains `max_epochs=40` with early stopping disabled and restore best. Candidate remains `max_epochs=12`, `minimum_epochs=4`, `early_stopping_patience=4`, strict increase, ties keep the earlier checkpoint, and restore best. Both arms still pin `classify_macro_f1_nonnone`, warm start `hyperlex-encoder-modernbert-base-seed-morph65`, and export sha256 `64b7d3dede25047cb6dd2e5b663f7fa72946ec82ac1a8816ae34622d1aaac430` at 9150 rows. Learning rate, batch size, gradient accumulation, and seed stay unset. They were not filled in. The admission schedule shape already names the four `train_schedule` fields, so this pass does not stop as `SELECT_005_SPEC_INCOMPLETE` and does not seal a preregistration. + +`HLX_THRESHOLD_AUTHORIZATION` for this experiment id is absent. Decision thresholds stay `BLOCKED_PENDING_OPERATOR_AUTHORIZATION`. No numeric floor was derived from the fresh stream or from SELECT-004. SELECT-004 preservation floors do not transfer. No candidate metric was scored. Admission here is pre-training, and there is no candidate checkpoint for this id. + +The source universe is the 339 settlement events in `settlement/events.jsonl`. Eligible identities after the frozen exclusions: 0. Exclusion counts are `EVAL_SPENT` 241, `DECISION_UNRESOLVED` 84, and `RIGHTS_UNRESOLVED` 14. The 98 settlements absent from the ledger and from the pinned export are 82 `UNRESOLVED` with `CC-BY-SA`, 2 `UNRESOLVED` with `RIGHTS_UNRESOLVED`, 7 `ACCEPT` with `RIGHTS_UNRESOLVED`, 6 `NONE` with `RIGHTS_UNRESOLVED`, and 1 `RECLASSIFY` with `RIGHTS_UNRESOLVED`. The pinned export still has 6761 unique hashes, all already in the ledger. Ledger identities remain 7964: `TRAIN_CONSUMED` 6763, `EVAL_SPENT` 949, `EVAL_ABANDONED` 252. Active `EVAL_RESERVE` is 0. The SELECT-004 spent reserve is the 491 identities that still carry `evaluation_reserved`. It was not reused. SELECT-001 and SELECT-002 bindings cover 855 other identities and were not reused. Packet `HLX-EVAL-REVIEW-2026-09-27-001` has 84 rows and 0 operator decisions, so it is not a settlement. WordNet was not admitted alone. Required provenance fields are present on every settlement event, and none of those events are eligible. + +Determinism is `IDENTICAL` across two reconstructions. Frozen reserve rows: 0. No reserve manifest was written. The quota result is `RESERVE_QUOTA_UNFILLED`. Provenance result is `NO_ADMITTED_ROWS`. Final-reserve overlap is 0 for train, the prior SELECT reserve, SELECT-001/002 bindings, threshold-v1 calibration, threshold-v2 calibration, and threshold-v2 measurement, because no row was frozen. The 98 novel settlements also overlap those fences by 0 on normalized identity, row id, source identity, and synset. + +Admission result is `RESERVE_QUOTA_UNFILLED`. An admission receipt was not created. Private failure sha256 `4b69c4756d0ce56a6009b9cf6a22ed002fc108193010e3360fb9f3a61a10d97e`. Census sha256 `bc8152c3935dcc66d157edee8ed8275cd2acd3449318a009cb5db58b1f60df3c`. Isolation sha256 `38e3bdc4210da496597d52cb2a055a5e182e964a1aace5c8c4166ade0862e03e`. Provenance sha256 `d93611e9d73ebf42973da3ba8fbfa663ab598d6af0953b91a4c6f8add4d534de`. `training_authorized` is false. `training_launch_authorized` is false. Epochs and gradient steps remain 0. BEST was not moved. It still names `hyperlex-encoder-modernbert-base-seed-select004`, weights sha256 `9fba0f66b1d5de6492470f53577d1447bfac1d29b9ac03869268abb70bbd97f6`. The named warm start was not modified, weights sha256 `96838b9656a84c3fee1773a41fdf2fbbec2f88f3bc948e4acfbe06c194ac5587`. Trunk sha256 remains `340ac08b74eef0d7bdec2d7981a6a3d4249bf0e6aab60634b72ad02c2b8023a9`. The next legal transition is `NONE`. + +No SELECT admission JSON Schema document exists. `SELECT_ADMISSION_JSON_SCHEMA_EXISTS` is false. The code receipt name `hyperlex.admission.v1` is not that document. Threshold v2 measurement stays `SEALED` at sha256 `78ca09ab14912681d028e8b9b1c77a8daf1d0c8c81561434eb9b6ade45a753e5`. Tracker sha256 remains `e674c207d9eb160159a673c23f7e2a2e8262b5bfc963ccf81b216d8291dc4b0f`. `RUNE.SEMANTIC_COMPOSITIONALITY_EVIDENCE.v2` stays `SEMANTIC_EVIDENCE_V2_SPEC_DRAFTED`. The residual threshold lanes were not modified. +## SELECT-005 reserve supply and governance — 2026-09-28 + +`SELECT_005_RESERVE_SUPPLY_AND_GOVERNANCE_AUTHORIZATION` reviewed the 10 ready rows in `HLX-EVAL-REVIEW-2026-09-27-001` and did not fill the reserve. The 69 rights-blocked rows, 4 provenance-blocked rows, and 1 duplicate stayed out. Review-ready manifest sha256 `8c1c762e43307e31d124e4dd2309adc17ea6243789aee2ea8e694b2b7bbd68e9`. Operator decisions sha256 `a4653ac2c5ee125fea069b3dfbdd4b40950eb8ead2ad032158a502ce886ef7d6`. Decision receipt sha256 `776df14f889098cfa4bfbbbbf5869e5775076806113bdbb53d6f1d44c7793307`. Counts are `ACCEPT` 1, `NONE` 5, `UNRESOLVED` 4, `RECLASSIFY` 0. The stored registry class is `INFERRED`. It was not promoted to `OBSERVED`. + +Settlement apply refused all 10. None are in held-out stream `hs-20260925T211358Z`. `rights_of` has no mapping for `MIT-examples`. Application sha256 `0a29f0c12403fdbd0b794fee323a83c96a20c2195d37ff79bf158a606c0e0724`. The settlement log stays at 339 events. Events sha256 remains `96b74a92d44f1cf9fe152b18e5207176f161ba3bfce528dac38aa4571a742f9c`. Ledger sha256 remains `77e22433203879b252f7a9e309d2013d7550101d1c4a014b494d2c96df87d0e0`. The post-review census is unchanged: universe 339, eligible 0, exclusions `EVAL_SPENT` 241, `DECISION_UNRESOLVED` 84, `RIGHTS_UNRESOLVED` 14. Routed slice counts are 0, 0, 0, 0. The quota result remains `RESERVE_QUOTA_UNFILLED`. Missing slices are `classify`, `classify_observed`, `classify_non_none`, and `unbind_clean`. No reserve manifest and no admission receipt were created. The previous failure sha256 `4b69c4756d0ce56a6009b9cf6a22ed002fc108193010e3360fb9f3a61a10d97e`, census sha256 `bc8152c3935dcc66d157edee8ed8275cd2acd3449318a009cb5db58b1f60df3c`, isolation sha256 `38e3bdc4210da496597d52cb2a055a5e182e964a1aace5c8c4166ade0862e03e`, and provenance sha256 `d93611e9d73ebf42973da3ba8fbfa663ab598d6af0953b91a4c6f8add4d534de` were not overwritten. + +The SELECT-005 preregistration is `SELECT_005_PREREGISTRATION_INCOMPLETE`, sha256 `1cc6701940b5b3458f9dc7ede1864a2fc3b0f7bed526c5a0fb996a620a54f1c0`, sealed false. Unresolved fields are learning rate, batch size, gradient accumulation, and seed. SELECT-004 values were not inherited. `HLX_THRESHOLD_AUTHORIZATION` stays `BLOCKED_PENDING_OPERATOR_AUTHORIZATION`. Proposal sha256 `8bdc85ec1e610ac512aefc9eaeb3f5d6b5caebef04dde959d22b883c3eab4a4b`. Every numeric threshold is null. No SELECT-004 floor was copied. `training_authorized` is false. `training_launch_authorized` is false. Epochs and gradient steps remain 0. BEST was not moved. The next legal transition is `NONE`. + +No SELECT admission JSON Schema exists. No threshold-authorization JSON Schema exists. Threshold v2 measurement stays `SEALED` at sha256 `78ca09ab14912681d028e8b9b1c77a8daf1d0c8c81561434eb9b6ade45a753e5`. Tracker sha256 remains `e674c207d9eb160159a673c23f7e2a2e8262b5bfc963ccf81b216d8291dc4b0f`. `RUNE.SEMANTIC_COMPOSITIONALITY_EVIDENCE.v2` stays `SEMANTIC_EVIDENCE_V2_SPEC_DRAFTED`. +## SELECT-005 source and preregistration completion — 2026-09-28 + From 5f60b47e089e45d316c8a1e13b0f70969243b8f8 Mon Sep 17 00:00:00 2001 From: Daniel Meyer Date: Mon, 28 Sep 2026 16:57:30 -0700 Subject: [PATCH 39/42] feat(007): stage evaluation-reserve chunk (11) --- .land-chunks/part-11 | 30 ++++++++++++++++++++++++++++++ 1 file changed, 30 insertions(+) create mode 100644 .land-chunks/part-11 diff --git a/.land-chunks/part-11 b/.land-chunks/part-11 new file mode 100644 index 00000000..84105409 --- /dev/null +++ b/.land-chunks/part-11 @@ -0,0 +1,30 @@ +`SELECT_005_SOURCE_AND_PREREGISTRATION_COMPLETION_AUTHORIZATION` did not recensus the 339 settlement events. Run `hs-20260925T211358Z` is not a source. No row from that universe was reviewed again. The settlement log was not appended. Its sha256 remains `d8a400b68de95ccacb5a3c4069765182ed0bb1a3099238f5351643e4ed35c377`. Identity-ledger events sha256 remains `96b74a92d44f1cf9fe152b18e5207176f161ba3bfce528dac38aa4571a742f9c`. Projection sha256 remains `77e22433203879b252f7a9e309d2013d7550101d1c4a014b494d2c96df87d0e0`. The previous failure sha256 `4b69c4756d0ce56a6009b9cf6a22ed002fc108193010e3360fb9f3a61a10d97e`, census sha256 `bc8152c3935dcc66d157edee8ed8275cd2acd3449318a009cb5db58b1f60df3c`, isolation sha256 `38e3bdc4210da496597d52cb2a055a5e182e964a1aace5c8c4166ade0862e03e`, and provenance sha256 `d93611e9d73ebf42973da3ba8fbfa663ab598d6af0953b91a4c6f8add4d534de` were not overwritten. The supply-001 preregistration sha256 `1cc6701940b5b3458f9dc7ede1864a2fc3b0f7bed526c5a0fb996a620a54f1c0` stays `SELECT_005_PREREGISTRATION_INCOMPLETE`. + +The training initialization is `morph65_new_schedule`. Both arms warm-start `hyperlex-encoder-modernbert-base-seed-morph65`, weights sha256 `96838b9656a84c3fee1773a41fdf2fbbec2f88f3bc948e4acfbe06c194ac5587`. The rejected alternative is `select004_best_new_schedule`. BEST was not moved. It still names `hyperlex-encoder-modernbert-base-seed-select004`, weights sha256 `9fba0f66b1d5de6492470f53577d1447bfac1d29b9ac03869268abb70bbd97f6`. The trainer default did not choose. Initialization decision sha256 `fcc261039977b6580c6025ff7debb1a240cee9158b2f7a803537c4bbb745ac65`. + +The successor preregistration is `SELECT_005_PREREGISTRATION_SEALED`, sha256 `c2dcf3b703f50cb8eb80bb6c2c45335071ded3c4ce89cc669e8a869eb3498baf`, sealed true. The scientific variable remains `train_schedule`. Control remains `max_epochs=40` with early stopping disabled. Candidate remains `max_epochs=12`, `minimum_epochs=4`, `early_stopping_patience=4`, strict increase, ties keep the earlier checkpoint, and restore best. Both arms still pin `classify_macro_f1_nonnone` and export sha256 `64b7d3dede25047cb6dd2e5b663f7fa72946ec82ac1a8816ae34622d1aaac430` at 9150 rows. Held constant from the morph65 continuation control recipe, environment sha256 `edb7163f388de71c258db2d68d03ac4e6d6a011548236666250d5d0f5d05e0f1`: learning rate `2e-5`, batch size 8, gradient accumulation 1, and seed policy `HLX_SEED_UNSET_FROZEN`. Gradient accumulation 1 records that the trainer takes one optimizer step per batch. The seed stays unset because the reconstructed recipe seals `HLX_SEED` unset, and that unset state is now an explicit shared policy rather than a missing field. `inherited_from_select_004` is false. SELECT-004's scientific variable `HLX_SELECT_METRIC` does not transfer. The same optimizer numbers appear in the SELECT-004 candidate environment; that coincidence is recorded and is not the authority. + +`HLX_THRESHOLD_AUTHORIZATION` stays `BLOCKED_PENDING_OPERATOR_AUTHORIZATION`. Proposal sha256 `dc926d61e16b50ea4f15a73110957d54f3718e28778d7456e5c39bc6bf35755e`. The comparison baseline is the SELECT-005 control arm, not seed-morph78. The schedule design supports strict improvement of `classify_macro_f1_nonnone` and does not assign a number. `classification_accuracy`, `observed_label_accuracy`, and `unbind_clean_exact` stay null. No SELECT-004 floor was copied. `decision_thresholds` is empty. The proposal is not sealed, so admission cannot point `HLX_THRESHOLD_AUTHORIZATION` at it. + +The fresh-reserve contract admits only `wiktionary_category` and `wikipedia_prose`, rights token `CC-BY-SA`. Princeton WordNet 3.0 stays compositional and is not that token, so it is not relabeled and is not admitted alone. The label-blind surface carries identity, license, and source URL, and refuses task, class, lineage, decision, and `unbind_clean`. Contract sha256 `8288c6a4f0618c0880a8bbab9acf072b53c6d7b968a7cc7f64f3cae7d8610c22`. The held-out harvester refuses when the box exposure snapshot is older than 6 hours. The newest snapshots are 71.75 hours old. This pass did not bypass that fence and did not fetch. Rows acquired: 0. The label-blind surface is empty, sha256 `e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855`. Isolation is `NOT_RUN_NO_ROWS`. Provenance is `NO_ADMITTED_ROWS`. Settlements appended: 0. + +A decision-only settlement record still has no task, class, lineage, or `unbind_clean`. The census calls that `SLICE_LABELS_ABSENT` and does not freeze a partial reserve. This pass does not attach those fields beside the settlement contract. The fresh census universe is 0, not 339. Census sha256 `d945bbe02d0aa9640a2d276a274e6fcbe3d856f84cca7a1d4920e4b9e8c2f079`. Routed slice counts are 0, 0, 0, 0. The quota result is `RESERVE_QUOTA_UNFILLED`. No reserve manifest and no admission receipt were created. Completion receipt sha256 `6065baf43db3f4013eb6f39e4e8a7293fb9dead062c17497be03245f4daa6760`. + +`training_authorized` is false. `training_launch_authorized` is false. Epochs and gradient steps remain 0. No optimizer was constructed. Trunk sha256 remains `340ac08b74eef0d7bdec2d7981a6a3d4249bf0e6aab60634b72ad02c2b8023a9`. `loop.py` sha256 remains `1aa395081d7709be3844bf2568d12100d73d931ecbc51af43c3d6e01dba77e2a`. `admission.py` sha256 remains `6ae7b63ca1b6e0459d9b795c731668b3eff524269d81bcb92181c0c9d407ab78`. `identity_ledger.py` sha256 remains `ac49b7240a895952b99b3ef9046f7a8185c8647badac7a45c8640d592057d1c5`. No SELECT admission JSON Schema exists. No threshold-authorization JSON Schema exists. Threshold v2 measurement stays `SEALED` at sha256 `78ca09ab14912681d028e8b9b1c77a8daf1d0c8c81561434eb9b6ade45a753e5`. Tracker sha256 remains `e674c207d9eb160159a673c23f7e2a2e8262b5bfc963ccf81b216d8291dc4b0f`. `RUNE.SEMANTIC_COMPOSITIONALITY_EVIDENCE.v2` stays `SEMANTIC_EVIDENCE_V2_SPEC_DRAFTED`. `semantic_evidence_v2.py` sha256 remains `9f5e5d6d5106d2af709c86486d302fed3c11a0de7cb4cf4f2413897882e66348`. The next legal transition is `NONE` until a fresh box exposure snapshot exists and an operator supplies numeric SELECT-005 thresholds that are not taken from SELECT-004. +## SELECT-005 settlement routing contract — 2026-09-28 + +`SELECT_005_SETTLEMENT_ROUTING_CONTRACT_EXTENSION_AUTHORIZATION` freezes a derivative routing contract. Canonical settlement stays separate from SELECT routing. Existing settlement events were not rewritten. The settlement log sha256 remains `d8a400b68de95ccacb5a3c4069765182ed0bb1a3099238f5351643e4ed35c377`. Identity-ledger events sha256 remains `96b74a92d44f1cf9fe152b18e5207176f161ba3bfce528dac38aa4571a742f9c`. Projection sha256 remains `77e22433203879b252f7a9e309d2013d7550101d1c4a014b494d2c96df87d0e0`. No routing event was written under `eval-reserve-20260926/routing/`. The 339-event census was not rerun. No source was harvested. The six-hour exposure fence was not bypassed. + +The contract is `hyperlex.eval_routing.v1`. Spec sha256 `002e2ac67e5ee00c27ecedd4566b9e7d900852c51e3ac57aac957ac095899d96`. Procedure `select_eval_routing_v1` sha256 `ad187d262eba280dd4b88d2204e03d4dfb255f9b0fb443bd250a0e441a7ea6db`. Routing schema sha256 `147e2787e6e66ba47acd185fa6602a10bd5d57c15ea446f574b2e750277d31a0`. Threshold-authorization schema sha256 `35a1b70cc805cf4271831a2414fd9ac85dcc15e5d23ec951d2d37665e89109a1`. Admission schema sha256 `6f20efe73b74207243474724261c985d4e6dde888df497a1ef723dda02a42077`. Conformance report sha256 `fac5dacf06f1257137bfd72b91c728191840ce291d2c8e953292f4313dddcd2b`. Contract receipt sha256 `65d79c90b5654e01b3dc205600eda349855476766a39f7ebda2d7b66ce7f5e06`. + +`task` is `classify` or `unbind`. `class` is the registry attest, `OBSERVED` or `INFERRED`. `lineage` is the semantic family, including `none`. `classify_observed` requires registry class `OBSERVED`. `classify_non_none` requires a lineage other than empty or `none`. `unbind_clean` is true only when `task` is `unbind` and the frozen derivation `soft_ceiling.clean_surface` is true. One record may enter every classify slice it satisfies. `unbind_clean` does not share a record with those slices because `task` is single-valued. `INFERRED` is not promoted to `OBSERVED`. `ACCEPT` and `RECLASSIFY` are routable. `NONE` can enter `classify` and, when the frozen flag is true, `unbind_clean`, and cannot enter `classify_non_none`. `UNRESOLVED` is ineligible. Rights other than `CC-BY-SA` are ineligible. Incomplete provenance is ineligible. Routing does not read reserve deficits, desired slices, routed counts, or candidate scores. The reference corpus reconstructed twice is `DETERMINISM = IDENTICAL`. State is `ROUTING_VALIDATION_COMPLETE`. + +Census may consult a validated routing record only when the settlement itself would otherwise be `SLICE_LABELS_ABSENT`. Settlement exclusions still win. `admit_training_run` still consumes a sealed reserve binding and does not require routing records, so SELECT-004 bindings stay valid. Historical schema checks are `VALIDATE_ONLY`. Result `SCHEMA_CONFORMANCE_GAP`: `exp-20260926-select-003/threshold-authorization.json` lacks `sealed` and `decision_thresholds`. That file was not rewritten. SELECT-004 admission artifacts and the SELECT-004 and SELECT-005 threshold documents validate. No SELECT-005 numeric threshold was sealed. `HLX_THRESHOLD_AUTHORIZATION` remains `BLOCKED_PENDING_OPERATOR_AUTHORIZATION`. No SELECT-005 admission receipt was created. + +`training_authorized` is false. `training_launch_authorized` is false. Epochs and gradient steps remain 0. No optimizer was constructed. Weights were not mutated. BEST was not changed. Threshold v2 measurement stays `SEALED` at sha256 `78ca09ab14912681d028e8b9b1c77a8daf1d0c8c81561434eb9b6ade45a753e5`. The next productive transition is `SELECT_005_FRESH_SOURCE_HARVEST_AND_ROUTING_AUTHORIZATION`, and only after a box exposure snapshot is no older than 6 hours. The metric-threshold blocker stays separate. +## SELECT-005 fresh harvest stopped — exposure snapshot stale — 2026-09-28 + +`SELECT_005_FRESH_SOURCE_HARVEST_AND_ROUTING_AUTHORIZATION` measured the newest box exposure pair and stopped before any source fetch. Snapshot id `20260925T211351Z`. Comparison timestamp unix `1790631188.652030` (`2026-09-28T21:33:08Z`). The vern snapshot `/home/morpheus/hlx-private/heldout-stream/inbox/vern-texts-20260925T211351Z.jsonl` sha256 `33dee5ccd2162ccfe5d2f200fdf6dd034dcd95aaa9ac4cc2b422957c06b14d17` is 72.319854 hours old. The box-hash snapshot `/home/morpheus/hlx-private/heldout-stream/inbox/box-jev-hashes-20260925T211351Z.txt` sha256 `0fd1e9d9aeedd982c1913d2c8a88b72e2e03c95a6177e7dcbddc9189df6811de` is 72.320210 hours old. The fence is 6 hours. The binding age is 72.320210 hours. Result `EXPOSURE_SNAPSHOT_STALE`. Receipt sha256 `ce20e600ebeb18d1989f4c673c95c2901143be2e780035b3a965b815a212bbdb`. Network requests: 0. Rows acquired: 0. No raw harvest, review surface, settlement append, routing record, reserve manifest, or admission receipt was created. + +The routing contract was not modified. Spec sha256 remains `002e2ac67e5ee00c27ecedd4566b9e7d900852c51e3ac57aac957ac095899d96`. Procedure `select_eval_routing_v1` sha256 remains `ad187d262eba280dd4b88d2204e03d4dfb255f9b0fb443bd250a0e441a7ea6db`. Routing schema sha256 remains `147e2787e6e66ba47acd185fa6602a10bd5d57c15ea446f574b2e750277d31a0`. Settlement log sha256 remains `d8a400b68de95ccacb5a3c4069765182ed0bb1a3099238f5351643e4ed35c377`. Identity-ledger events sha256 remains `96b74a92d44f1cf9fe152b18e5207176f161ba3bfce528dac38aa4571a742f9c`. Projection sha256 remains `77e22433203879b252f7a9e309d2013d7550101d1c4a014b494d2c96df87d0e0`. Preregistration sha256 remains `c2dcf3b703f50cb8eb80bb6c2c45335071ded3c4ce89cc669e8a869eb3498baf`. `HLX_THRESHOLD_AUTHORIZATION` remains `BLOCKED_PENDING_OPERATOR_AUTHORIZATION`. The SELECT-003 schema gap was not repaired. + From 17b32b924f2ca065a8c7bf83198d4e4e08ba1ca8 Mon Sep 17 00:00:00 2001 From: Daniel Meyer Date: Mon, 28 Sep 2026 16:58:00 -0700 Subject: [PATCH 40/42] feat(007): stage evaluation-reserve chunk (12) --- .land-chunks/part-12 | 20 ++++++++++++++++++++ 1 file changed, 20 insertions(+) create mode 100644 .land-chunks/part-12 diff --git a/.land-chunks/part-12 b/.land-chunks/part-12 new file mode 100644 index 00000000..8866a9c6 --- /dev/null +++ b/.land-chunks/part-12 @@ -0,0 +1,20 @@ +`training_authorized` is false. `training_launch_authorized` is false. Epochs and gradient steps remain 0. No optimizer was constructed. Weights were not mutated. BEST remains `hyperlex-encoder-modernbert-base-seed-select004`, sha256 `9fba0f66b1d5de6492470f53577d1447bfac1d29b9ac03869268abb70bbd97f6`. Threshold-v2 measurement stays `SEALED` at sha256 `78ca09ab14912681d028e8b9b1c77a8daf1d0c8c81561434eb9b6ade45a753e5`. The next legal transition is the same harvest authorization, and only after a box exposure snapshot is no older than 6 hours. The metric-threshold blocker stays separate. +## SELECT-005 threshold authorization — 2026-09-28 + +`SELECT_005_THRESHOLD_AUTHORIZATION` seals `HLX_THRESHOLD_AUTHORIZATION` for `HLX-EXP-2026-09-27-SELECT-005`. State `SEALED`. Artifact sha256 `e0d8d81b6392692784d92542d0cc5aa6ee6756b93c44bab86e2167ef57cfbc55`. Receipt sha256 `c444977805717f7548744f7df435cdcd899a5daee1fc1a15967b02740cbfaf9e`. Derivation sha256 `ce6fcfb46b7b73f4b039649f8b182fe90aa8a196db76e46ddbc4a1a6bd0fb860`. Schema validation `PASS`, report sha256 `7e200ae1c6f3619a0aee0c4c29106077f7a647ce74ebca143ff77e815d070772`. Semantic validation `PASS`, report sha256 `1b724ca6e86ef8e158f60169dad9b057fe3eff26af343100c2aa855b22cb21cd`. The JSON Schema sha256 remains `35a1b70cc805cf4271831a2414fd9ac85dcc15e5d23ec951d2d37665e89109a1`. + +The comparison baseline is the SELECT-005 control arm. The comparison candidate is the SELECT-005 candidate arm. Both are scored as candidate minus control. `classify_macro_f1_nonnone` uses operator `GT` and threshold `0`, so a tie fails. `classification_accuracy`, `observed_label_accuracy`, and `unbind_clean_exact` use operator `GE` and threshold `0`, so equality at the floor passes and any degradation fails. A primary gain cannot compensate for a failed guardrail. A missing or non-computable required metric fails. The numbers are zero because the sealed schedule and `note_strict_improvement` already require `score > best_value`, and the paired arms share one count-ratio scorer. SELECT-005 has no sealed reserve size, so a positive degradation band cannot be computed from 1/N. SELECT-004 floors 0.02, 0.05, and 0.01 were not copied. `inherited_from_select_004` is false. The unsealed proposal sha256 `dc926d61e16b50ea4f15a73110957d54f3718e28778d7456e5c39bc6bf35755e` was not overwritten. Preregistration sha256 remains `c2dcf3b703f50cb8eb80bb6c2c45335071ded3c4ce89cc669e8a869eb3498baf`. + +This seal is not a training launch and not an admission receipt. `training_authorized` is false. `training_launch_authorized` is false. Epochs and gradient steps remain 0. No optimizer was constructed. Weights were not mutated. BEST remains `hyperlex-encoder-modernbert-base-seed-select004`, sha256 `9fba0f66b1d5de6492470f53577d1447bfac1d29b9ac03869268abb70bbd97f6`. The reserve remains `RESERVE_QUOTA_UNFILLED`. No harvest ran and the exposure snapshot was not refreshed. Threshold-v2 measurement stays `SEALED` at sha256 `78ca09ab14912681d028e8b9b1c77a8daf1d0c8c81561434eb9b6ade45a753e5`. The SELECT-003 schema gap was not repaired. The next legal transition is `SELECT_005_FRESH_SOURCE_HARVEST_AND_ROUTING_AUTHORIZATION`, and only after a box exposure snapshot is no older than 6 hours. This pass does not run it. +## SELECT-005 exposure snapshot refresh — 2026-09-28 + +`SELECT_005_EXPOSURE_SNAPSHOT_REFRESH_AUTHORIZATION` did not generate a successor snapshot. State `EXPOSURE_REFRESH_WORKFLOW_UNAVAILABLE`. Receipt sha256 `8c5477d61f938cfce6f6838f24bdad104e3d643e0b97bf7399c640e088c03b38`. + +The canonical procedure is `/workspace/deliverables/hyperlex-heldout-stream/bin/heldout-stream-daily`, run on the box. It is absent on `spark-bf46`. `/workspace/deliverables` does not exist here. No SSH host is configured to the box. `vernacular.sqlite3` is not on this host. `hs_run.py` only reads `vern-texts-.jsonl` and `box-jev-hashes-.txt`; it does not generate them. The documented daily command also runs `hs_run.py run`, and this pass did not start that harvest. No snapshot id was minted. The previous pair `20260925T211351Z` was not overwritten and was not reused. Vern sha256 remains `33dee5ccd2162ccfe5d2f200fdf6dd034dcd95aaa9ac4cc2b422957c06b14d17`. Box-hash sha256 remains `0fd1e9d9aeedd982c1913d2c8a88b72e2e03c95a6177e7dcbddc9189df6811de`. + +No harvest, reserve recensus, settlement append, or training occurred. Preregistration sha256 remains `c2dcf3b703f50cb8eb80bb6c2c45335071ded3c4ce89cc669e8a869eb3498baf`. Threshold authorization sha256 remains `e0d8d81b6392692784d92542d0cc5aa6ee6756b93c44bab86e2167ef57cfbc55`. The routing contract was not modified. Settlement log sha256 remains `d8a400b68de95ccacb5a3c4069765182ed0bb1a3099238f5351643e4ed35c377`. Identity-ledger events sha256 remains `96b74a92d44f1cf9fe152b18e5207176f161ba3bfce528dac38aa4571a742f9c`. Projection sha256 remains `77e22433203879b252f7a9e309d2013d7550101d1c4a014b494d2c96df87d0e0`. `training_authorized` is false. `training_launch_authorized` is false. Epochs and gradient steps remain 0. BEST remains `hyperlex-encoder-modernbert-base-seed-select004`, sha256 `9fba0f66b1d5de6492470f53577d1447bfac1d29b9ac03869268abb70bbd97f6`. Threshold-v2 measurement stays `SEALED` at sha256 `78ca09ab14912681d028e8b9b1c77a8daf1d0c8c81561434eb9b6ade45a753e5`. The next legal transition stays `SELECT_005_FRESH_SOURCE_HARVEST_AND_ROUTING_AUTHORIZATION`, and it is not executable until a box-side snapshot-only run produces a new pair no older than 6 hours. This pass does not run it. +## SELECT-005 Notion vernacular source — 2026-09-28 + +The vernacular registries are in Notion. Vernacular Intelligence (`collection://d0ae52bd-a4ef-4ece-b06e-37078f533a7b`) has 44 rows and 43 distinct titles, created 2026-09-07 through 2026-09-27. Vernacular Events (`collection://785c124f-ecd7-41ae-9553-8a43967bac92`) has 120 rows, all source X and itemized, with 0 source URLs. There is no candidates database and no observations database. The box hash list remains the over-inclusive Jev-lane exposure set. Discovery, Obsidian, and the Jev-spike exports are not in these registries, and `heldout-stream-daily` is still absent on `spark-bf46`. Hashing the Notion rows into `box-jev-hashes` would narrow that set. Writing a vern file alone would leave the newest vern id unbound from the newest box id. No successor snapshot was written. State remains `EXPOSURE_REFRESH_WORKFLOW_UNAVAILABLE`. Receipt sha256 `ae37022242000400217cf2d959f0a339fdde9a1a8a4ae213cc348d079afd5e0b`. + +The previous pair `20260925T211351Z` was not overwritten and was not copied forward. Vern sha256 remains `33dee5ccd2162ccfe5d2f200fdf6dd034dcd95aaa9ac4cc2b422957c06b14d17`. Box-hash sha256 remains `0fd1e9d9aeedd982c1913d2c8a88b72e2e03c95a6177e7dcbddc9189df6811de`, the same bytes as `20260925T205240Z` and the audit pin. No harvest, reserve recensus, settlement append, or training occurred. Preregistration sha256 remains `c2dcf3b703f50cb8eb80bb6c2c45335071ded3c4ce89cc669e8a869eb3498baf`. Threshold authorization sha256 remains `e0d8d81b6392692784d92542d0cc5aa6ee6756b93c44bab86e2167ef57cfbc55`. Settlement log sha256 remains `d8a400b68de95ccacb5a3c4069765182ed0bb1a3099238f5351643e4ed35c377`. Identity-ledger events sha256 remains `96b74a92d44f1cf9fe152b18e5207176f161ba3bfce528dac38aa4571a742f9c`. Projection sha256 remains `77e22433203879b252f7a9e309d2013d7550101d1c4a014b494d2c96df87d0e0`. Sense-screen hypothesis sha256 remains `e674c207d9eb160159a673c23f7e2a2e8262b5bfc963ccf81b216d8291dc4b0f`. BEST remains `hyperlex-encoder-modernbert-base-seed-select004`, sha256 `9fba0f66b1d5de6492470f53577d1447bfac1d29b9ac03869268abb70bbd97f6`. `training_authorized` is false. Epochs and gradient steps remain 0. The next legal transition stays `SELECT_005_FRESH_SOURCE_HARVEST_AND_ROUTING_AUTHORIZATION`, and it is not executable until a box-side snapshot-only run produces a new bound pair no older than 6 hours. This pass does not run it. From 02a9180d8d2a3edf965e21066ee0c5e222afd553 Mon Sep 17 00:00:00 2001 From: Daniel Meyer Date: Mon, 28 Sep 2026 16:58:51 -0700 Subject: [PATCH 41/42] feat(007): assemble the evaluation-reserve continuation --- .../workflows/assemble-evaluation-reserve.yml | 55 +++++++++++++++++++ 1 file changed, 55 insertions(+) create mode 100644 .github/workflows/assemble-evaluation-reserve.yml diff --git a/.github/workflows/assemble-evaluation-reserve.yml b/.github/workflows/assemble-evaluation-reserve.yml new file mode 100644 index 00000000..4c75d3f0 --- /dev/null +++ b/.github/workflows/assemble-evaluation-reserve.yml @@ -0,0 +1,55 @@ +name: assemble-evaluation-reserve + +on: + push: + branches: + - cursor/select-001-land-main-41af + paths: + - .github/workflows/assemble-evaluation-reserve.yml + workflow_dispatch: + +permissions: + contents: write + +jobs: + assemble: + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v4 + with: + ref: cursor/select-001-land-main-41af + fetch-depth: 1 + - name: assemble the sealed spec + run: | + set -euo pipefail + cat \ + .land-chunks/part-00 \ + .land-chunks/part-01 \ + .land-chunks/part-02 \ + .land-chunks/part-03 \ + .land-chunks/part-04 \ + .land-chunks/part-05 \ + .land-chunks/part-06 \ + .land-chunks/part-07 \ + .land-chunks/part-08 \ + .land-chunks/part-09 \ + .land-chunks/part-10 \ + .land-chunks/part-11 \ + .land-chunks/part-12 \ + > specs/007-hyperlexical-model/evaluation-reserve.md + python3 - << 'PY' + import hashlib + from pathlib import Path + data = Path("specs/007-hyperlexical-model/evaluation-reserve.md").read_bytes() + blob = hashlib.sha1(f"blob {len(data)}\0".encode() + data).hexdigest() + if len(data) != 227875 or blob != "dd0de93f81b4f02a9458fe7c3e84e72f7582aada": + raise SystemExit(f"spec mismatch len={len(data)} blob={blob}") + print("spec blob", blob) + PY + git config user.name "Hyperlex Cloud Agent" + git config user.email "agent@zer0state.local" + git add specs/007-hyperlexical-model/evaluation-reserve.md + git rm -r .land-chunks + git rm .github/workflows/assemble-evaluation-reserve.yml + git commit -m "feat(007): land the SELECT evaluation-reserve continuation" + git push origin HEAD:cursor/select-001-land-main-41af From aa8ec49e631d5a6d855fec5ec1e7bddb03c61ba3 Mon Sep 17 00:00:00 2001 From: Hyperlex Cloud Agent Date: Mon, 28 Sep 2026 23:59:00 +0000 Subject: [PATCH 42/42] feat(007): land the SELECT evaluation-reserve continuation --- .../workflows/assemble-evaluation-reserve.yml | 55 ---- .land-chunks/part-00 | 297 ------------------ .land-chunks/part-01 | 192 ----------- .land-chunks/part-02 | 140 --------- .land-chunks/part-03 | 128 -------- .land-chunks/part-04 | 34 -- .land-chunks/part-05 | 31 -- .land-chunks/part-06 | 29 -- .land-chunks/part-07 | 39 --- .land-chunks/part-08 | 41 --- .land-chunks/part-09 | 38 --- .land-chunks/part-10 | 38 --- .land-chunks/part-11 | 30 -- .land-chunks/part-12 | 20 -- .../evaluation-reserve.md | 220 +++++++++++++ 15 files changed, 220 insertions(+), 1112 deletions(-) delete mode 100644 .github/workflows/assemble-evaluation-reserve.yml delete mode 100644 .land-chunks/part-00 delete mode 100644 .land-chunks/part-01 delete mode 100644 .land-chunks/part-02 delete mode 100644 .land-chunks/part-03 delete mode 100644 .land-chunks/part-04 delete mode 100644 .land-chunks/part-05 delete mode 100644 .land-chunks/part-06 delete mode 100644 .land-chunks/part-07 delete mode 100644 .land-chunks/part-08 delete mode 100644 .land-chunks/part-09 delete mode 100644 .land-chunks/part-10 delete mode 100644 .land-chunks/part-11 delete mode 100644 .land-chunks/part-12 diff --git a/.github/workflows/assemble-evaluation-reserve.yml b/.github/workflows/assemble-evaluation-reserve.yml deleted file mode 100644 index 4c75d3f0..00000000 --- a/.github/workflows/assemble-evaluation-reserve.yml +++ /dev/null @@ -1,55 +0,0 @@ -name: assemble-evaluation-reserve - -on: - push: - branches: - - cursor/select-001-land-main-41af - paths: - - .github/workflows/assemble-evaluation-reserve.yml - workflow_dispatch: - -permissions: - contents: write - -jobs: - assemble: - runs-on: ubuntu-latest - steps: - - uses: actions/checkout@v4 - with: - ref: cursor/select-001-land-main-41af - fetch-depth: 1 - - name: assemble the sealed spec - run: | - set -euo pipefail - cat \ - .land-chunks/part-00 \ - .land-chunks/part-01 \ - .land-chunks/part-02 \ - .land-chunks/part-03 \ - .land-chunks/part-04 \ - .land-chunks/part-05 \ - .land-chunks/part-06 \ - .land-chunks/part-07 \ - .land-chunks/part-08 \ - .land-chunks/part-09 \ - .land-chunks/part-10 \ - .land-chunks/part-11 \ - .land-chunks/part-12 \ - > specs/007-hyperlexical-model/evaluation-reserve.md - python3 - << 'PY' - import hashlib - from pathlib import Path - data = Path("specs/007-hyperlexical-model/evaluation-reserve.md").read_bytes() - blob = hashlib.sha1(f"blob {len(data)}\0".encode() + data).hexdigest() - if len(data) != 227875 or blob != "dd0de93f81b4f02a9458fe7c3e84e72f7582aada": - raise SystemExit(f"spec mismatch len={len(data)} blob={blob}") - print("spec blob", blob) - PY - git config user.name "Hyperlex Cloud Agent" - git config user.email "agent@zer0state.local" - git add specs/007-hyperlexical-model/evaluation-reserve.md - git rm -r .land-chunks - git rm .github/workflows/assemble-evaluation-reserve.yml - git commit -m "feat(007): land the SELECT evaluation-reserve continuation" - git push origin HEAD:cursor/select-001-land-main-41af diff --git a/.land-chunks/part-00 b/.land-chunks/part-00 deleted file mode 100644 index e343906c..00000000 --- a/.land-chunks/part-00 +++ /dev/null @@ -1,297 +0,0 @@ -# Evaluation reserve (GEN-0) - -Text identity is `hyperlexical.holdout_guard.normalized_text_sha256`. -That is SHA-256 of `heldout_census.normalize_group_text`: NFKC, casefold, URL strip, non-alphanumeric to space, whitespace collapse. Empty normalized text still hashes. No second normalizer is an identity. `soft_ceiling.clean_surface` remains the unbind-clean slice predicate only. - -Row ids are historical labels. One text identity may carry many row ids. Contamination, spending, abandonment, and reserve membership attach to the text hash. - -## Exhaustion - -On 2026-09-26 the live store (`01c1b38fd6e926962e47a520228e884ae7d99ea0ad6bbe188dc9980064254ac2`, 5005 rows) had **zero** rows eligible for a fresh holdout after split=test, the pinned morph78 export (`64b7d3dede25047cb6dd2e5b663f7fa72946ec82ac1a8816ae34622d1aaac430`, 9150 rows), spent v2, rc1 unbind rows, and the abandoned SELECT-001 / SELECT-002 holdouts. The 440 `split=test` rows are also training-consumed under the canonical hash, so dropping the split gate does not open a fresh classify set. Classify OBSERVED and classify non-none in that eligible universe are 0. - -SELECT-001 is `CLOSED_AT_LAUNCH_GATE`. SELECT-002 is `EXECUTION_INVALID` (0 epochs, 0 gradient steps). Both holdouts are `UNSCORED_ABANDONED`: never scored, not reusable, not `SCORED_SPENT`. Manifest bytes stay as sealed. Do not reopen either experiment. - -## Lifecycle - -New text is routed **before** training exposure. Records are append-only. Flags that mean consumed, spent, or abandoned only turn on. - -```text -NEW - | - +--> TRAIN_CANDIDATE - | | - | +--> TRAIN_CONSUMED - | - +--> EVAL_RESERVE - | - +--> EVAL_BOUND - | - +--> EVAL_SPENT - | - +--> EVAL_ABANDONED -``` - -`AVAILABLE` is catalogued text with none of those flags. It may still be routed to `TRAIN_CANDIDATE` or `EVAL_RESERVE`. - -Forbidden without a governed generation reset: - -- `EVAL_RESERVE` or `EVAL_BOUND` later entering training -- training-consumed text later used as unseen evaluation -- spent or abandoned text returning to the fresh reserve - -A generation-reset **request** can be recorded. It does not clear flags and it does not create a new baseline. GEN-1 is not opened by this policy. - -## Assignment - -Policy id: `hyperlex.eval_reserve.v1`. Fixed before any candidate output is observed. Model scores, errors, and predictions are refused as admission inputs. - -Within a batch, identities are ordered by `sha256(GEN-0 | batch_id | normalized_text_sha256)`. A new identity is `EVAL_RESERVE` when it still fills at least one open required slice. Otherwise it is `TRAIN_CANDIDATE`. Quotas are unique text identities, not raw rows. A second row id on an existing hash does not increase the evaluation sample size. A new hash that reuses an old row id stays a different identity and is reported as a collision. - -Required slices, because checkpoint selection needs each of them: - -- `classify` (classification accuracy) -- `classify_observed` (OBSERVED-label accuracy) -- `classify_non_none` (`classify_macro_f1_nonnone`) -- `unbind_clean` (unbind clean exact) - -## Coverage classes - -| Claim | Class | -| --- | --- | -| Canonical text hash is the contamination identity | CANONICAL_REQUIREMENT | -| Fresh evaluation text is disjoint from training, spent, and abandoned text | CANONICAL_REQUIREMENT | -| Each required slice is represented before SELECT-003 can be drafted | CANONICAL_REQUIREMENT | -| Statistical minimum n for those metrics | NOT_COMPUTABLE | -| v2 `DRAW_READY_MIN` 470 | HISTORICAL_PRECEDENT (unbind census target; PR #113 sets no v2 size) | -| Name-gate 2500 | CANONICAL_REQUIREMENT for the name gate, not for this holdout | -| rc1 classify_clean 444 / OBSERVED 72 / unbind_clean 306 | HISTORICAL_PRECEDENT (spent test) | -| SELECT-002 eligibility 606 classify / 287 OBSERVED / 604 non-none / 250 clean unbind | HISTORICAL_PRECEDENT and the PLANNING_TARGET | - -The planning target restores that SELECT-002 shape. It is not a validity theorem. Gap versus a statistical minimum is `NOT_COMPUTABLE`. - -## Admission - -```sh -PYTHONPATH=scripts/shadow python -m hyperlexical.identity_ledger admit \ - --ledger /path/to/ledger --rows new.jsonl --batch-id BATCH --source acquire -``` - -A row is accepted only when its canonical hash is absent from `TRAIN_CONSUMED`, `EVAL_SPENT`, `EVAL_ABANDONED`, and the existing reserve. The command reports raw rows and unique canonical text identities. Reserve rows must not be used for training, checkpoint selection, hyperparameter tuning, candidate-specific error review, or repeated scoring. Aggregate census counts are allowed. When `HLX_EVAL_RESERVE_LEDGER` is set, the trainer refuses if a loaded training row is still `EVAL_RESERVE` or `EVAL_BOUND`. - -## SELECT-003 - -Do not draft SELECT-003 until a new reserve exists, the four slices are represented, the reserve is text-disjoint from the pinned training export, the reserve is not spent or abandoned, and this policy stays sealed. The unresolved hypothesis, if a later authorization allows it, is still `HLX_SELECT_METRIC`: `unbind_exact` versus `classify_macro_f1_nonnone`. Authorization is a separate act. Meeting a planning target does not grant it. - -## GEN-0 and GEN-1 - -GEN-0 keeps `seed-morph78` and the pinned 9150-row export as the training baseline. New material is evaluation-only until the reserve slices are filled. Overflow may become `TRAIN_CANDIDATE` and must not be pulled back into the reserve. - -GEN-0 becomes impractical only if, after a real acquisition attempt: - -- new classify evidence cannot represent OBSERVED and non-none together -- new text keeps colliding with the pinned export or with spent/abandoned hashes -- accepted training candidates grow while the reserve slices stay empty -- the reserve cannot hold the four slices at once -- the historical baseline is no longer the system under test - -Convenience is not one of those conditions. This document does not create GEN-1. - -## Ledger - -`scripts/shadow/hyperlexical/build_identity_ledger.py` catalogues receipt-backed artifacts. It does not infer lifecycle from filenames. The populated ledger stays on the host store, not in git, and does not contain raw text. - -## Acquisition batch HLX-EVAL-ACQ-2026-09-26-001 - -Screened local rights-cleared and operator-labeled corpora. The ledger was not mutated. Decision `REJECT_BATCH`. The reserve stays empty. - -- 2026 backfill atoms: 67/67 unique hashes already `TRAIN_CONSUMED`. -- Harvest multiword file: 890/890 unique hashes already `TRAIN_CONSUMED`. -- Civilian seed: 17 novel identities, all lineage `ai-native`. A registry export marked `OBSERVED` is not an operator settlement, so those rows were held. Class imbalance is severe. No balance threshold is declared. -- 2026-09-16 structure gold: the authorized train rows are already consumed. Four novel leftovers carry model predictions and were excluded. -- Agent-memetics seeds: rights are unresolved and task labels are absent. - -New `OBSERVED` coverage is `NOT_COMPUTABLE` until an operator harvest settlement exists. This record does not settle phrases. SELECT-003 stays undrafted. GEN-1 was not created. In-repo settled gold is exhausted. That alone does not reset GEN-0. - -## Acquisition batch HLX-EVAL-ACQ-2026-09-26-002 - -The held-out stream (`hs-20260925T211358Z`, 339 rows) is the shelf that sits beside the Jev lane. Every canonical text hash is absent from the identity ledger. Overlap with the box Jev exposure list is 0. The ledger was not mutated. Decision: not admitted. The reserve stays empty. - -Jev in this batch is the exposure fence. `hs_run.py` and each row policy forbid evaluating Jev, the lineage rule, or any other model on these rows. `JEV_API_KEY` is unset on this host. No vendor call was made. A Jev family call is not an operator settlement. - -- Rights-cleared INFERRED labels: gaming-meta 99, betting-sharp 61, crypto-degen 5, plus 40 encyclopedic `none`. Five families have no independent label. -- 134 rows are `UNLABELLED`. The attest column is empty on all 339 rows, so new `OBSERVED` coverage is `NOT_COMPUTABLE`. -- 16 Reddit and Know Your Meme rows have unresolved rights. -- Class imbalance on the rights-cleared non-none subset is severe. No balance threshold is declared. Admitting it would open `classify_macro_f1_nonnone` on three families. - -SELECT-003 stays undrafted. GEN-1 was not created. Novel yield against `TRAIN_CONSUMED` is 339/339, so GEN-0 is not collision-blocked. The next label step is operator `attest-apply` on the queued sheet. - -## Taxonomy proposal HLX-EVAL-TAXON-2026-09-26-001 - -The next label step is no longer `attest-apply`. The operator directed a taxonomy expansion first. Draft: `label-taxonomy-proposal.md`. Private remap receipt has no row text. The ledger was not mutated. Nothing was admitted. Jev was not called. `attest-apply` was not run. - -The production head stays the nine-way `layout.FAMILIES` list. The proposal adds sixteen non-none names as a draft active set, with `attest`, `register`, and `function` as separate surfaces. `evaluation.enabled` is false on every name. Support minimum is `NOT_COMPUTABLE`. - -Of the 339 stream rows, 204 keep the same family as a `PROPOSED_REMAP` (gaming-meta 98, betting-sharp 61, crypto-degen 5, none 40). 135 are `LABEL_UNRESOLVED`. `brainrot-aura` is not split. Kinship hints are not mapped to `relationship-dating`. Eleven of the sixteen names have no row on this shelf. Row settlement is not ready. SELECT-003 stays undrafted. - -## Taxonomy acceptance HLX-EVAL-TAXON-2026-09-26-002 - -The operator accepted the ontology structure and amended it. Active non-none families are eighteen, including `identity-affiliation` and `politics-civic`. `work-hustle` is renamed `workplace-career`. `brainrot-aura` is not a family. Six names stay candidates. `taxonomy.active` is true and `evaluation.enabled` is false on all eighteen. `none` stays abstain. `source_hint` is evidence, not `semantic_family`. - -Lanes are prepared and unsettled: A 204, B 65, C 21, D 16. Thirty-three Wiktionary hint-only rows sit outside those lanes and stay `LABEL_UNRESOLVED`. Decision cells are empty. `attest-apply` was not run. The current command would force `OBSERVED` and would reject the new names, so it must not be used on these sheets. Vendor calls: 0. Reserve stays 0. SELECT-003 stays undrafted. - - -## Settlement tool HLX-EVAL-SETTLE-2026-09-26-001 - -Evaluation settlement is a separate command, `python -m hyperlexical.identity_ledger settlement-apply`. Production `attest-apply` was not modified and was not run. That command still accepts only the production families plus `none` or `reject`, and it still writes `label_source=OBSERVED` for an accepted value. - -`settlement-apply` reads completed operator cells. It does not fill them. `source_hint`, `semantic_family`, and `attest` are separate fields. `ACCEPT` stores the attest the operator entered and does not promote existing evidence to `OBSERVED`. `RECLASSIFY` requires an explicit family that differs from the proposed evidence. `NONE` stores `semantic_family=none` and is not `reject`. `UNRESOLVED` stores null family and null attest. A second decision for the same row is refused. The log and the receipt are append-only and contain no row text. - -`taxonomy.active`, `evaluation.enabled`, and `production.enabled` are three flags. The eighteen families stay taxonomy-active only. Both enable flags stay false. `layout.FAMILIES` is unchanged. Lanes A–D and the hint-only holding sheet were validated with every decision cell empty (204 / 65 / 21 / 16 / 33). Rows settled: 0. Unresolved decisions: 0. Reserve added: 0. Vendor calls: 0. The identity ledger was not mutated. SELECT-003 stays undrafted. - - -## Operator settlement HLX-EVAL-SETTLE-2026-09-26-002 - -The five private sheets for stream `hs-20260925T211358Z` were filled with an explicit decision on every row, then applied with `python -m hyperlexical.identity_ledger settlement-apply`. Production `attest-apply` was not run. `layout.FAMILIES` was not edited. No row text is in this file. - -Blank and `UNRESOLVED` stay distinct. Blank means the operator has not reviewed the row. `UNRESOLVED` means the operator reviewed the row and declined to settle it. This pass left blank at 0 and `UNRESOLVED` at 84. - -Counts: settled 255 (`ACCEPT` 204, `RECLASSIFY` 32, `NONE` 19), `UNRESOLVED` 84, blank 0. `OBSERVED` 128. `INFERRED` 127. Rights-blocked settled 14. Those 14 stay out of `EVAL_RESERVE`. Rights status was not changed by the semantic decision. - -`OBSERVED` was used only when a stored gloss or the row text directly states the settled family, the atom is slangish or multiword jargon, and the primary stored sense is that family. A category, a topic, or `source_hint` did not set family or attest. Accepting a family did not promote an existing `INFERRED` label. Encyclopedic `none` rows stayed `INFERRED`. - -Settled support by unique canonical text, including zeros: gaming-meta 60 (OBSERVED 38, INFERRED 22, cleared 60, blocked 0); betting-sharp 58 (41, 17, 53, 5); crypto-degen 4 (3, 1, 3, 1); internet-slang 19 (10, 9, 19, 0); memetic 3 (0, 3, 2, 1); social-status 6 (3, 3, 6, 0); relationship-dating 0; approval-disapproval 1 (0, 1, 1, 0); conflict-aggression 0; technology-ai 4 (1, 3, 4, 0); workplace-career 10 (9, 1, 10, 0); sports-competition 1 (0, 1, 0, 1); music-entertainment 1 (0, 1, 1, 0); fashion-aesthetic 0; regional-cultural 0; spiritual-mystic 0; identity-affiliation 16 (13, 3, 16, 0); politics-civic 13 (10, 3, 13, 0); none 59 (0, 59, 53, 6). - -Rights-cleared settled non-none identities: 188. Quantitative support thresholds are `NOT_COMPUTABLE`, so no `UNREPRESENTED` / `LOW_SUPPORT` / `REPRESENTED` flag is assigned. `OBSERVED` total 128, all non-none; `OBSERVED` none is 0. - -One workplace-lane row describes retail product reformulation and price-tier inflation. `finance-retail` remains a candidate and was not activated. That row is `UNRESOLVED`. - -Admission used `identity_ledger admit` under `hyperlex.eval_reserve.v1`, batch `HLX-EVAL-ADMIT-2026-09-26-002`. Admitted to `EVAL_RESERVE`: 241. Rejected existing identities: 0. Slice counts after admission: classify 241, classify_observed 123, classify_non_none 188, unbind_clean 0. `clean_unbind_support` is 0. This stream has no unbind target and none was fabricated. SELECT-003 was not drafted. The gate reports `eligible: false` because `unbind_clean` is unrepresented. Reserve identities are text-disjoint from the pinned train export. - -Receipt `HLX-EVAL-SETTLE-2026-09-26-002` sha256 `1b0fb54a5de0fb708f74ad278037458c15e0abea5a7eb6b4b151b7e7fb34567e`. `settlement-apply` still records `reserve_added: 0`; admission is the separate ledger command. Vendor calls: 0. BEST was not moved. Do not train. - - -## Clean-unbind contract — 2026-09-26 - -Phase: clean-unbind capacity recovery. The 241 classify reserve identities stay protected. This section does not retune `semantic_family`, `attest`, or `evaluation.enabled`. - -Executed shape: `hyperlexical.export._unbind_dual_scheme_rows`. Fillers are the source atom's own tokens. Positional text is those tokens joined by spaces, with roles `pos_0..pos_n-1`. Type-slot text is `TOKEN:` / `SLOT:` / `MARKER:` prefixed by index, and the fillers stay the same tokens. Role schemes are only `positional` and `type_slot`. Token count is 2 through `LIVE_UNBIND_MAX_TOKENS` (6). Atom length is at most `LIVE_UNBIND_MAX_LEN` (80). A gloss is not unbind gold. `class` is copied, not invented; this acquisition uses `INFERRED` and `lineage=none`. - -Contamination identity: `hyperlexical.holdout_guard.normalized_text_sha256` (NFKC, casefold, URL and punctuation stripped, SHA-256). - -Clean predicate: `soft_ceiling.clean_surface` on rows whose `split` is `train`, which is the call in `holdout_eligibility.census`. Definition string: `unbind_clean_definition = soft_ceiling.clean_surface`. The clean predicate is whitespace-collapsed lowercase equality of the row text against train text. It is not the contamination hash. `oov_filler_surface` is a different surface and is not this predicate. - -Scorer, not run in this phase: `hyperlexical.eval_forward.score_unbind_exact`. Gold is the filler list. Empty filler lists are skipped. Metrics are `unbind_exact`, `unbind_token_f1`, `unbind_slot_f1`, and the strict variants, including `by_role_scheme`. Baseline name: `unbind_copy_token`. - -Synthetic row, placeholders only: - -```json -{ - "text": "EXAMPLE_TOKEN_A EXAMPLE_TOKEN_B", - "split": "eval", - "lineage": "none", - "typology": [], - "stage": "noise", - "roles": ["pos_0", "pos_1"], - "fillers": ["EXAMPLE_TOKEN_A", "EXAMPLE_TOKEN_B"], - "role_scheme": "positional", - "task": "unbind", - "provenance": "source:EXAMPLE_LOCATOR", - "class": "INFERRED", - "license": "EXAMPLE_RIGHTS_GRANT", - "target_origin": "source_lemma_tokens" -} -``` - -The type-slot twin uses text `TOKEN:EXAMPLE_TOKEN_A SLOT:EXAMPLE_TOKEN_B`, roles `TOKEN` and `SLOT`, and the same fillers. Unbind settlement decisions are `ACCEPT`, `CORRECT_TARGET`, `REJECT`, and `UNRESOLVED` in `hyperlex.eval_unbind_settlement.v1`. That log is not the classify settlement schema. Admission still goes through `IdentityLedger.admit`. `unbind_clean` is set only for hashes kept by `clean_surface`. - - - -## Clean-unbind admission HLX-EVAL-ADMIT-2026-09-26-003 - -Source: Princeton WordNet 3.0 index lemmas (`index.noun`, `index.verb`, `index.adj`, `index.adv`). Glosses in `data.*` were not read and were not used as targets. Raw artifact sha256 `cbda5ea6eef7f36a97a43d4a75f85e07fccbb4f23657d27b4ccbc93e2646ab59`. License file sha256 `7731175a77952e259390b496fab905e57118b8d19ad3a8383c67eee724ff443f`. Rights: WordNet 3.0 Copyright 2006 by Princeton University, with permission to use, copy, modify, and distribute for any purpose without fee or royalty when the notice is preserved. Unresolved-rights rows were not in this source. - -Fillers are the source lemma tokens (`target_origin=source_lemma_tokens`). Operator settlement `HLX-EVAL-UNBIND-SETTLE-2026-09-26-001` appended 250 `ACCEPT` events on that basis. `CORRECT_TARGET` 0. `REJECT` 0. `UNRESOLVED` 0. Settlement receipt sha256 `3ada2dae58bde22ef1ed1ac5a4004be75d7bf3f52cac590f24900de71015194b`. The classify settlement log was not rewritten. - -Screen of shaped dual-scheme rows: 128233 unique texts. Novel and clean: 128044. Rejected `TRAIN_CONSUMED` 184. Rejected existing `EVAL_RESERVE` 5. Those 5 were not reused. Novelty rate among shaped rows: 0.9985. Planning cap admitted 250 of the admissible set. `IdentityLedger.admit` appended 500 events. Events sha256 before `f5e0008f27a80b11bc7e5b98e48e9e99cada04ee8f9455ed5ece6f99c1de3266`, after `8223ae11bb42bd1a98ebcd739d1cfbc470085e241826b662703faefdfe752da6`. Routed to `TRAIN_CANDIDATE`: 0. Acquisition receipt sha256 `be5671d4cf586b7a9ce3f45b4f5b8b5d0574edd4d1eaeef0c9644ca8ad2678a8`. Admission receipt sha256 `53df5397a13974e03bd60310fca2c29589e7a0fa6236dd576cf4ddf43a75bf15`. Census receipt sha256 `a5e9ae8ef6b65b5c187633e09b8700a5ef800eb1d97bd7a78aa2a9db26cf0a16`. - -Reserve after admission: classify 241, classify_observed 123, classify_non_none 188, unbind_clean 250. The first three did not decrease. Admitted rows are `class=INFERRED`, `lineage=none`. Role schemes: positional 125, type_slot 125. Filler counts: 2 tokens 64, 3 tokens 56, 4 tokens 56, 5 tokens 46, 6 tokens 28. Source-index metadata, not a Hyperlex family: noun 72, verb 68, adv 62, adj 48. Unique filler targets: 125. Each target has 2 surfaces (the two role schemes). Maximum surfaces per target: 2. - -Planning progress, not a validity threshold: classify 241/606, OBSERVED 123/287, non-none 188/604, clean unbind 250/250. Statistical minimum remains `NOT_COMPUTABLE`. - -`select_003_gate.eligible` is true. `training_overlap_identities` is 0. `spent_or_abandoned_in_reserve` is 0. Reserve identities 491. SELECT-003 was not drafted. This is representation completeness, not training readiness. Evaluation quality is still one lexicon, `INFERRED`, `lineage=none`. These rights-cleared active families still have zero settled classify support and were not collected here: relationship-dating, conflict-aggression, sports-competition, fashion-aesthetic, regional-cultural, spiritual-mystic. - -Vendor calls: 0. BEST was not moved. Do not train. - - -## SELECT-003 preregistration HLX-EXP-2026-09-26-SELECT-003 - -Phase: preregistration only. Experiment id `HLX-EXP-2026-09-26-SELECT-003`. Training is not authorized. BEST is not moved. No holdout is scored. Vendor calls: 0. - -Predecessors are not evidence for or against the hypothesis. SELECT-001 closed at the launch gate with the hypothesis `UNTESTED`. SELECT-002 is `EXECUTION_INVALID` with epochs 0 and gradient steps 0, hypothesis `UNTESTED`. - -Hypothesis: with training otherwise equivalent to the reconstructed `seed-morph78` baseline, does selecting checkpoints by `classify_macro_f1_nonnone` improve non-none classification macro-F1 while preserving the established unbind and classification safeguards? - -The single scientific variable is `HLX_SELECT_METRIC`. Baseline: unset, which resolves to `unbind_exact`, with `HYPERLEX_SAVE_BEST_UNBIND=1`. Candidate: `classify_macro_f1_nonnone`. Frozen with the reconstructed recipe: `HYPERLEX_FILLER_FILTER=off`, `HYPERLEX_TASK_ROUTING=legacy_split`, `HYPERLEX_UNBIND_LOSS_WEIGHT=1.0`, init `seed-morph65`, `HLX_SEED` unset, `HLX_E2_DISJOINT` absent, `HLX_VOCAB_TRAIN_ONLY` absent, `HLX_ALLOW_NO_HOLDOUT` unset, `HYPERLEX_RELEASE_SET` absent. Pinned-export execution is infrastructure, not a scientific variable. Checkpoint selection inside the trainer still uses the pinned export's val split. The reserve is the comparison surface for the decision rule. Substituting the reserve for that val split is a second variable and is not part of this experiment. - -Training input: pinned export, 9150 rows, sha256 `64b7d3dede25047cb6dd2e5b663f7fa72946ec82ac1a8816ae34622d1aaac430`. Rows before the reserve filter 9150, after 9150. Reserve/training row-id overlap 0. Reserve/training text-hash overlap 0. Any difference is an admission failure. - -BEST remains `hyperlex-encoder-modernbert-base-seed-morph78`, weights sha256 `fc53676bd347cccd4d0ac9a429f3469c36436f8eb0e09954e0c347c7b133a4a1`. Trunk weights sha256 `340ac08b74eef0d7bdec2d7981a6a3d4249bf0e6aab60634b72ad02c2b8023a9`. `layout.FAMILIES` was not edited. - -The bound GEN-0 reserve is unchanged: classify 241, classify_observed 123, classify_non_none 188, unbind_clean 250, identities 491. Events sha256 `8223ae11bb42bd1a98ebcd739d1cfbc470085e241826b662703faefdfe752da6`. Ledger projection sha256 `d071b7aec8154203ce7f9ae9531639b8d638f86c2ac0c3af38bead9b3c4a48f9`. Spent 0. Abandoned 0. Text-disjoint from training. Rights-cleared. No identities are added or removed under this experiment id. - -Receipts bound with the reserve: classification settlement `HLX-EVAL-SETTLE-2026-09-26-002` canonical sha256 `1b0fb54a5de0fb708f74ad278037458c15e0abea5a7eb6b4b151b7e7fb34567e`; classification admission `HLX-EVAL-ADMIT-2026-09-26-002` sha256 `9898d13140b1adf9e496ce4a7e71f9573d3a0f87e97355ea01de3ecafb66e836`; clean-unbind acquisition `be5671d4cf586b7a9ce3f45b4f5b8b5d0574edd4d1eaeef0c9644ca8ad2678a8`; clean-unbind settlement `3ada2dae58bde22ef1ed1ac5a4004be75d7bf3f52cac590f24900de71015194b`; clean-unbind admission `53df5397a13974e03bd60310fca2c29589e7a0fa6236dd576cf4ddf43a75bf15`; census `a5e9ae8ef6b65b5c187633e09b8700a5ef800eb1d97bd7a78aa2a9db26cf0a16`. - -Primary metric: - -```text -name: classify_macro_f1_nonnone -label_universe_sha256: 227b782011aad7e693fde253e103a24b3ca0bd6b04e090d446656fa943bf0175 -absent_class_policy: omit_when_gold_support_is_zero -scorer: hyperlexical.classify_metrics.macro_f1_nonnone -``` - -The sealed class set is the non-none lineages present on the bound reserve, with identity support: approval-disapproval 1, betting-sharp 53, crypto-degen 3, gaming-meta 60, identity-affiliation 16, internet-slang 19, memetic 2, music-entertainment 1, politics-civic 13, social-status 6, technology-ai 4, workplace-career 10. Sum 188. The scorer's macro is the unweighted mean of per-class F1 over gold labels other than `none` that have support n>0. Classes with gold support 0 are omitted. They are not entered as F1=0. A later taxonomy expansion does not enter this experiment's metric. The six families with zero rights-cleared settled support stay outside the universe: relationship-dating, conflict-aggression, sports-competition, fashion-aesthetic, regional-cultural, spiritual-mystic. - -Slice mapping, one contamination function for every slice (`normalized_text_sha256`): - -- `classify_macro_f1_nonnone` uses the 188 `classify_non_none` identities. Gold is `lineage`. The 53 `none` identities are not in this row set. -- Classification accuracy uses the 241 `classify` identities. Gold is `lineage`, including `none`. Scorer: `accuracy`. -- OBSERVED-label accuracy uses the 123 `classify_observed` identities. Gold is `lineage`. Scorer: `accuracy`. These three slices share classification settlement `HLX-EVAL-SETTLE-2026-09-26-002` and admission `HLX-EVAL-ADMIT-2026-09-26-002`. -- Unbind clean exact uses the 250 `unbind_clean` identities. Gold is the filler list. Scorer: `score_unbind_exact` (`unbind_exact`). Settlement is `HLX-EVAL-UNBIND-SETTLE-2026-09-26-001`, not the classify log. Clean predicate remains `soft_ceiling.clean_surface`. - -Decision thresholds are `BLOCKED_PENDING_OPERATOR_AUTHORIZATION`. Inherited authorization from SELECT-002 is `NOT_COMPUTABLE`. No canonical rule carries numeric margins across an execution-invalid predecessor, and SELECT-002 sealed its margins for that experiment id only. Preservation names are prepared and inactive: unbind clean exact, classification accuracy, OBSERVED-label accuracy. Promote and reject thresholds are not set. - -Representation completeness passes. Training provenance passes. Holdout disjointness passes. Rights and provenance pass. Clean-unbind capacity passes. Evaluation quality is limited and disclosed: non-none support is 188 and imbalanced, and the 250 clean-unbind rows are Princeton WordNet 3.0 only, all `INFERRED`, `lineage=none`, 125 filler targets, 2 surfaces per target. The experiment is not authorized to run. - -## SELECT-003 threshold authorization — 2026-09-26 - -Operator decision for `HLX-EXP-2026-09-26-SELECT-003` only. This is a new authorization. It does not transfer SELECT-001 or SELECT-002 margins. The sealed preregistration file is not edited. Training is not authorized. BEST is not moved. The reserve is not scored. - -Primary metric remains `classify_macro_f1_nonnone`. Label universe sha256 `227b782011aad7e693fde253e103a24b3ca0bd6b04e090d446656fa943bf0175`. Absent-class policy `omit_when_gold_support_is_zero`. The universe stays the twelve supported non-none classes on the sealed reserve. - -Comparison baseline is `hyperlex-encoder-modernbert-base-seed-morph78`, weights sha256 `fc53676bd347cccd4d0ac9a429f3469c36436f8eb0e09954e0c347c7b133a4a1`, scored later on the same GEN-0 reserve: classify 241, classify_observed 123, classify_non_none 188, unbind_clean 250. - -`PROMOTE` requires every condition. Primary: candidate `classify_macro_f1_nonnone` strictly greater than seed-morph78. Equality does not promote. Unbind clean exact on the 250 sealed identities stays within 0.01 below seed-morph78. Classification accuracy on the 241 sealed identities stays within 0.02. OBSERVED-label accuracy on the 123 sealed identities stays within 0.05. Integrity must also pass: one scientific variable, pinned export exact, 9150 rows before and after the reserve filter, row-id overlap 0, canonical text-hash overlap 0, no reserve identity spent or abandoned before scoring, sealed ledger unchanged, sealed class universe unchanged, checkpoint selection follows `HLX_SELECT_METRIC`, complete provenance, no contamination-guard failure, no schema or name-gate failure, and no `CHAR_WINS`. - -`REJECT` if the candidate primary metric is lower, or the unbind delta is below -0.01, or classification accuracy delta is below -0.02, or OBSERVED-label accuracy delta is below -0.05, or any hard failure occurs: `CHAR_WINS`, more than one scientific variable, training input other than the sealed pin, effective training rows other than 9150, reserve binding change, class-universe change, unverifiable reserve or execution provenance, or a hard integrity or contamination guard failure. - -`INCONCLUSIVE` if the primary metrics are equal and the preservation and integrity guards pass. Also `INCONCLUSIVE` when execution and scoring are valid but the sealed rule cannot be applied deterministically, and that reason is not itself a hard integrity failure. An inconclusive result is not resolved by changing thresholds. - -These floors are new SELECT-003 rules. A later promote would mean checkpoint selection by non-none macro-F1 improved the sealed twelve supported families without exceeding the three allowed regressions. It would not mean improvement across the eighteen-family ontology. Families outside the gold universe remain relationship-dating, conflict-aggression, sports-competition, fashion-aesthetic, regional-cultural, and spiritual-mystic. Representation completeness passes. Evaluation quality stays limited. Vendor calls: 0. Do not train. - - -## SELECT-003 execution HLX-EXP-2026-09-26-SELECT-003 - -One launch was authorized from Spark commit `53f128a68a6603a98d9d5a3e56cc357f4a17aa0c` with a clean tree. The sealed preregistration, threshold authorization, candidate environment, and non-launching preflight were not edited. `HYPERLEX_ALLOW_TRAIN=1` was a process overlay only. `HLX_ALLOW_NO_HOLDOUT` stayed unset. - -Container `hlx-train-select003-1790441469` on `lmsysorg/sglang:dev-qwen38-27b-dflash2` (`sha256:616a3e97f45191af975896cfa644279096cb31bd408a071c2e99ca7209c3cafe`) started 2026-09-26T16:51:09Z and exited 1 at 2026-09-26T16:51:12Z. The trainer refused before `load_training_bundle`: `HYPERLEX_ALLOW_TRAIN=1` with no holdout manifest. Epochs 0. Gradient steps 0. No candidate checkpoint. The pinned export was not consumed. The reserve was not scored and was not spent. Decision `EXECUTION_INVALID`. The hypothesis is `UNTESTED`. This is not `PROMOTE`, `REJECT`, or `INCONCLUSIVE`. - -BEST remains `hyperlex-encoder-modernbert-base-seed-morph78`, sha256 `fc53676bd347cccd4d0ac9a429f3469c36436f8eb0e09954e0c347c7b133a4a1`. Vendor calls: 0. Do not retry this experiment id. Do not train. -## SELECT-003 closed — PREFLIGHT_LAUNCH_HOLDOUT_GATE_MISMATCH - -`HLX-EXP-2026-09-26-SELECT-003` is permanently `EXECUTION_INVALID`. Cause: `PREFLIGHT_LAUNCH_HOLDOUT_GATE_MISMATCH`. The non-launching preflight reported `TRAINING_READY` without running `require_holdout_for_training` under `HYPERLEX_ALLOW_TRAIN=1`. The trainer then refused before `load_training_bundle` because the sealed candidate had no `HLX_HOLDOUT_MANIFESTS` and `HLX_ALLOW_NO_HOLDOUT` stayed unset. Epochs 0. Gradient steps 0. Candidate checkpoint: none. Reserve scored: false. `BEST_moved`: false. Hypothesis: `UNTESTED`. The sealed preregistration and threshold authorization are not rewritten. This id is not retried. - -## Controlled-experiment admission — CONTROLLED_RESERVE - -The canonical controlled-experiment holdout contract is `CONTROLLED_RESERVE`. A launch with `HLX_EXPERIMENT_ID` set is admitted by `admit_training_run`, which both preflight and `run_loop` call, in this order: experiment binding, launch gate, sealed reserve, pinned training input, train/reserve disjointness, single scientific variable, BEST and trunk digests, output directory, then ready. - diff --git a/.land-chunks/part-01 b/.land-chunks/part-01 deleted file mode 100644 index a961ce55..00000000 --- a/.land-chunks/part-01 +++ /dev/null @@ -1,192 +0,0 @@ -A sealed reserve binding satisfies the holdout requirement when the active reserve is `EVAL_RESERVE` for the current experiment, all four slices are present on that active set, the binding digest matches the full `events.jsonl`, and training overlap by row id and by normalized text hash is 0 against that active set. Overlap rejects the run. It does not drop training rows. `HLX_HOLDOUT_MANIFESTS` is not a second requirement. `HLX_ALLOW_NO_HOLDOUT` does not admit a controlled experiment. Legacy launches that are not controlled experiments still use the manifest gate. - -Active membership is the derived lifecycle `EVAL_RESERVE` whose experiment binding is only the current experiment. `evaluation_reserved` stays historical evidence and is not cleared when the lifecycle is `EVAL_SPENT`. Spent identities do not count, do not satisfy overlap, and cannot be reserved again. A ledger with no active identities is a missing active reserve. A live `EVAL_RESERVE` bound to another experiment does not satisfy the current experiment. Slice counts and the binding identity count use the active set. The events digest still covers the append-only log. - -The default scientific variable remains `HLX_SELECT_METRIC`: that key is the only allowed difference. `HLX_SCIENTIFIC_VARIABLE=train_schedule`, set to the same value on the baseline, the candidate, and the process, is the only composite. It normalizes `HYPERLEX_TRAIN_EPOCHS`, `HYPERLEX_EARLY_STOP`, `HYPERLEX_EARLY_STOP_PATIENCE`, and `HYPERLEX_EARLY_STOP_MIN_EPOCHS` into one variable. Both arms must set `HLX_SELECT_METRIC=classify_macro_f1_nonnone`. Any other difference fails. An undeclared schedule difference fails. A selection-metric difference is not part of `train_schedule`. Declaring the variable does not register or authorize an experiment. - -`HYPERLEX_ALLOW_TRAIN=1` is part of the effective environment hash. `HLX_ADMISSION_ONLY=1` is not. With that flag, `python -m hyperlexical.train --run` returns after admission and does not construct an optimizer, enter epoch 0, or take a gradient step. `TRAINING_READY` means that same admission returned `ADMISSION_PASS`. - -```text -RUNE.PREFLIGHT_LAUNCH_PARITY(x) = - effective_environment_hash(preflight) == effective_environment_hash(launch) - AND admission_gate_sequence(preflight) == admission_gate_sequence(launch) - AND admission_result(preflight) == admission_result(launch) -``` - -## SELECT-004 readiness - -`HLX-EXP-2026-09-26-SELECT-004` is not preregistered and is not authorized to run. It may be drafted. SELECT-001, SELECT-002, and SELECT-003 produced no scientific evidence about `HLX_SELECT_METRIC`. A draft may test the same hypothesis: baseline unset, which resolves to `unbind_exact` with `HYPERLEX_SAVE_BEST_UNBIND=1`; candidate `classify_macro_f1_nonnone`. - -No canonical rule carries numeric thresholds across experiments. SELECT-003's floors do not transfer. SELECT-004 decision thresholds are `BLOCKED_PENDING_OPERATOR_AUTHORIZATION` until a fresh authorization is sealed for that id. - -The draft must keep the pinned export sha256 `64b7d3dede25047cb6dd2e5b663f7fa72946ec82ac1a8816ae34622d1aaac430`, 9150 rows, BEST sha256 `fc53676bd347cccd4d0ac9a429f3469c36436f8eb0e09954e0c347c7b133a4a1`, and trunk sha256 `340ac08b74eef0d7bdec2d7981a6a3d4249bf0e6aab60634b72ad02c2b8023a9`. The GEN-0 reserve stays classify 241, classify_observed 123, classify_non_none 188, unbind_clean 250, identities 491. It is not scored and its lifecycle is not changed. Admission uses that reserve. It does not draw another one and it does not attach a legacy manifest. - -Vendor calls: 0. BEST was not moved. Do not train. -## SELECT-004 preregistered — HLX-EXP-2026-09-26-SELECT-004 - -`HLX-EXP-2026-09-26-SELECT-004` is `PREREGISTERED`. Preregistration sha256 `365f7ce7141e8ee899fc52d85584a32c21ecfcb596e316b1438463bd8db85c4a`. That seal records repository commit `493c75981bf443baf2899b71b504fe7c7a529cf9` and a clean tree. Later documentation does not edit the sealed file. - -Predecessors are not evidence. SELECT-001 remains `CLOSED_AT_LAUNCH_GATE`. SELECT-002 remains `EXECUTION_INVALID` with epochs 0 and gradient steps 0. SELECT-003 remains `EXECUTION_INVALID`, cause `PREFLIGHT_LAUNCH_HOLDOUT_GATE_MISMATCH`, epochs 0, gradient steps 0, candidate checkpoint none, reserve not scored. The hypothesis is `UNTESTED`. - -The single scientific variable is `HLX_SELECT_METRIC`. Baseline is unset, which resolves to `unbind_exact` with `HYPERLEX_SAVE_BEST_UNBIND=1`. Candidate is `classify_macro_f1_nonnone`. Experiment diff sha256 `aa6e42e50a9547908edfe4860371a2ec05f7ec20ea87dfa829555d9e7b05bc4c`. `experimental_variable_count` is 1. Metadata differences are the experiment id, the output directory, and the residual-dump path. - -`CONTROLLED_RESERVE` binding sha256 `c16e69559dd6582f687532ce6e2a2e9b52a70db1aa066d11e7e68e723936b371`. Ledger events sha256 `8223ae11bb42bd1a98ebcd739d1cfbc470085e241826b662703faefdfe752da6`. Projection sha256 `d071b7aec8154203ce7f9ae9531639b8d638f86c2ac0c3af38bead9b3c4a48f9`. Counts remain classify 241, classify_observed 123, classify_non_none 188, unbind_clean 250, identities 491, lifecycle `EVAL_RESERVE`. Training row-id overlap is 0. Training canonical-text overlap is 0. The reserve was not scored, spent, abandoned, or relabeled. Historical spent and abandoned identities in the same ledger are not this reserve. - -Training input is the pinned export sha256 `64b7d3dede25047cb6dd2e5b663f7fa72946ec82ac1a8816ae34622d1aaac430`, mode `PINNED_EXPORT`. Declared rows, consumed rows, and effective optimization rows are 9150. No runtime exclusion reduced the set. `HLX_ALLOW_NO_HOLDOUT` stayed unset. No legacy holdout manifest was attached. - -BEST remains `hyperlex-encoder-modernbert-base-seed-morph78`, sha256 `fc53676bd347cccd4d0ac9a429f3469c36436f8eb0e09954e0c347c7b133a4a1`. Trunk sha256 `340ac08b74eef0d7bdec2d7981a6a3d4249bf0e6aab60634b72ad02c2b8023a9`. BEST was not moved. - -Decision thresholds are `BLOCKED_PENDING_OPERATOR_AUTHORIZATION`. SELECT-003 numeric floors do not transfer. The prepared preservation metric names are `unbind_clean_exact`, `classification_accuracy`, and `observed_label_accuracy`. Those names have no active numeric thresholds. - -Admission-only parity passed. Preflight and the trainer entrypoint, both under `HYPERLEX_ALLOW_TRAIN=1` with `HLX_ADMISSION_ONLY=1`, share environment hash `759c591151c88074973c3ba2a555be551d3460c04c8cedf59ab36624de215885`. `admission_result` is `ADMISSION_PASS`. Status is `PREREGISTERED`. `optimizer_loaded` is false. Epochs 0. Gradient steps 0. The SELECT-004 output directory was absent before and after. `HLX_ADMISSION_ONLY` is excluded from the environment hash. - -`TRAINING_READY` exists only when all three are true: the scientific contract is sealed, the decision rule is sealed, and real entrypoint admission passes. SELECT-004 stays below `TRAINING_READY` until a fresh threshold authorization for this experiment id. `training_launch_authorized` is false. - -Representation completeness is `PASS`. Evaluation quality is `LIMITED`. Classification support is 188 rights-cleared non-none identities. Clean unbind is 250 identities, 125 targets, 2 role-scheme surfaces per target, from Princeton WordNet 3.0 only. Unsupported families remain relationship-dating, conflict-aggression, sports-competition, fashion-aesthetic, regional-cultural, and spiritual-mystic. A later result must not claim those families. Label universe sha256 `227b782011aad7e693fde253e103a24b3ca0bd6b04e090d446656fa943bf0175`. Absent-class policy `omit_when_gold_support_is_zero`. - -Vendor calls: 0. Do not train. The next decision is a fresh SELECT-004 threshold authorization. -## SELECT-004 thresholds authorized — HLX-EXP-2026-09-26-SELECT-004 - -Fresh operator authorization for `HLX-EXP-2026-09-26-SELECT-004` only. It does not inherit authority from SELECT-001, SELECT-002, or SELECT-003. The sealed preregistration bytes stay `365f7ce7141e8ee899fc52d85584a32c21ecfcb596e316b1438463bd8db85c4a`. - -`PROMOTE` requires `classify_macro_f1_nonnone` strictly greater than `seed-morph78` on the sealed twelve-class universe, sha256 `227b782011aad7e693fde253e103a24b3ca0bd6b04e090d446656fa943bf0175`, absent-class policy `omit_when_gold_support_is_zero`. Equality does not promote. The new SELECT-004 preservation floors are unbind clean exact within 0.01 on 250 identities, classification accuracy within 0.02 on 241 identities, and OBSERVED-label accuracy within 0.05 on 123 identities. A primary decrease, a preservation breach, or a hard integrity failure is `REJECT`. Equal primary metrics with every guard passing are `INCONCLUSIVE`. An inconclusive result is not resolved by changing thresholds. - -Authorization artifact sha256 `7389783b52a5d5cf14ceca874cc12351d3f6571b6be8a8666c2040be38972625`. Post-authorization admission used `HYPERLEX_ALLOW_TRAIN=1` and `HLX_ADMISSION_ONLY=1`. Preflight and the trainer entrypoint share environment hash `a2b18512be8329ee63ad06e98f894c6138cf7eac05f6e82d53d4132e99a27f5d`. `admission_result` is `ADMISSION_PASS`. Status is `TRAINING_READY` because the scientific contract is sealed, the decision rule is sealed, and the real entrypoint passed. `optimizer_loaded` is false. Epochs 0. Gradient steps 0. Training was not started. Output directory stayed absent. BEST sha256 `fc53676bd347cccd4d0ac9a429f3469c36436f8eb0e09954e0c347c7b133a4a1` was not moved. Trunk sha256 `340ac08b74eef0d7bdec2d7981a6a3d4249bf0e6aab60634b72ad02c2b8023a9`. `training_launch_authorized` is false. `HLX_ALLOW_NO_HOLDOUT` stayed unset. - -Representation completeness is `PASS`. Evaluation quality is `LIMITED`. The decision covers classify 241, classify_observed 123, classify_non_none 188, and unbind_clean 250. It does not establish performance for relationship-dating, conflict-aggression, sports-competition, fashion-aesthetic, regional-cultural, or spiritual-mystic. Clean unbind stays limited to the sealed WordNet slice. Vendor calls: 0. Do not train. The next decision is launch authorization only. - -## SELECT-004 scored — HLX-EXP-2026-09-26-SELECT-004 - -One run finished under the sealed contract. Container `hlx-train-select004-1790448703` exited 0 after 40 epochs. The training schedule was not shortened. Checkpoint selection used `classify_macro_f1_nonnone` on the training-export validation surface. The selected checkpoint is epoch 3 at 0.64448782942204. Epoch 39 scored 0.5864567286803334. Candidate weights sha256 `9fba0f66b1d5de6492470f53577d1447bfac1d29b9ac03869268abb70bbd97f6`. Consumed export sha256 `64b7d3dede25047cb6dd2e5b663f7fa72946ec82ac1a8816ae34622d1aaac430`, 9150 rows, reserve filter removed 0. The reserve ledger was not modified during training. - -Reserve scoring used the sealed twelve-family universe `227b782011aad7e693fde253e103a24b3ca0bd6b04e090d446656fa943bf0175`. seed-morph78 macro-F1 0.022395727019119547, accuracy 0.14107883817427386, OBSERVED accuracy 0.032520325203252036, unbind clean exact 0.092. The candidate macro-F1 0.07245710784313726, accuracy 0.15767634854771784, OBSERVED accuracy 0.08130081300813008, unbind clean exact 0.100. Deltas are +0.05006138082401771, +0.01659751037344398, +0.048780487804878044, and +0.008. The char 3–5 baseline macro-F1 was 0.0, so CHAR_WINS did not occur. Decision **PROMOTE**. State `PROMOTION_ELIGIBLE`. `--apply-best` was not run. BEST remains `seed-morph78` / `fc53676bd347cccd4d0ac9a429f3469c36436f8eb0e09954e0c347c7b133a4a1`. - -Representation completeness is `PASS`. Evaluation quality is `LIMITED`. The production head still emits the nine training families, so nine of the twelve gold families cannot be named and pull the absolute macro-F1 down for both checkpoints. The claim stays inside the sealed slices. Vendor calls: 0. The 40-epoch schedule was left as sealed. A later duration study can measure best-epoch locations; it is not a change to this result. - -## SELECT-004 promoted — HLX-EXP-2026-09-26-SELECT-004 - -Operator authorization applied the selected epoch-3 checkpoint as BEST. The promoted object is `hyperlex-encoder-modernbert-base-seed-select004/model.safetensors`, sha256 `9fba0f66b1d5de6492470f53577d1447bfac1d29b9ac03869268abb70bbd97f6`. `model.final.safetensors` (`d5be46be6611e382d837bab8868bb373cbead4f9caa3a666ca06f2b2cda1925b`) was not promoted. Epoch 39 was not promoted. No training ran. The reserve was not rescored. - -Previous BEST `hyperlex-encoder-modernbert-base-seed-morph78` remains in place, sha256 `fc53676bd347cccd4d0ac9a429f3469c36436f8eb0e09954e0c347c7b133a4a1`. - -Decision receipt sha256 `ee493b7d73b5e00ae27ec681bb52405bbcf2983a16a0297edad225aec52b7225`. Decision `PROMOTE`. Selection metric `classify_macro_f1_nonnone` `0.64448782942204`. Primary macro-F1 baseline `0.022395727019119547`, candidate `0.07245710784313726`, delta `0.05006138082401771`. Preservation deltas: classification accuracy `0.01659751037344398`, OBSERVED accuracy `0.048780487804878044`, unbind clean exact `0.008`. CHAR_WINS did not occur. Integrity `PASS`. - -The 491 sealed reserve identities moved `EVAL_RESERVE` to `EVAL_BOUND` to `EVAL_SPENT` through `IdentityLedger.transition` and `persist_append`. Events sha256 before `8223ae11bb42bd1a98ebcd739d1cfbc470085e241826b662703faefdfe752da6`, after `96b74a92d44f1cf9fe152b18e5207176f161ba3bfce528dac38aa4571a742f9c`. Projection sha256 before `d071b7aec8154203ce7f9ae9531639b8d638f86c2ac0c3af38bead9b3c4a48f9`, after `77e22433203879b252f7a9e309d2013d7550101d1c4a014b494d2c96df87d0e0`. The sealed binding file `c16e69559dd6582f687532ce6e2a2e9b52a70db1aa066d11e7e68e723936b371` was not rewritten. Historical consumed, spent, and abandoned identities outside this reserve were not changed. - -Representation completeness is `PASS`. Evaluation quality is `LIMITED`. This promotion does not establish performance across the eighteen-family ontology. The production head cannot name nine of the twelve sealed gold families. That limitation is separate from the checkpoint pointer. Vendor calls: 0. No further experiment was started. - -Promotion receipt sha256 `2e1f84b476f7355e31380419afad00d0b16d372235a0da6be85adc17fcf92d01`. - -## SELECT-005 blocked — HLX-EXP-2026-09-27-SELECT-005 - -`HLX-EXP-2026-09-27-SELECT-005` is the next unclaimed experiment id. Claimed ids are `HLX-EXP-2026-09-26-SELECT-001` through `HLX-EXP-2026-09-26-SELECT-004`. This id is recorded and not sealed. No preregistration file, arm directory, reserve, or admission receipt was created. - -`TRAINING_BLOCKED`. The proposed variable is the composite `train_schedule`. Control would be `max_epochs=40`, early stopping disabled, restore best. Candidate would be `max_epochs=12`, `minimum_epochs=4` scored epochs through epoch index 3, `early_stopping_patience=4`, strict increase, ties keep the earlier checkpoint, restore best. Both arms would pin `classify_macro_f1_nonnone`, warm start `hyperlex-encoder-modernbert-base-seed-morph65`, and export sha256 `64b7d3dede25047cb6dd2e5b663f7fa72946ec82ac1a8816ae34622d1aaac430` at 9150 rows. - -The canonical trainer does not implement that candidate. `scripts/shadow/hyperlexical/loop.py` scores `for ep in range(epochs)` and never stops early. `score_now > best_macro` already keeps the earlier checkpoint on ties, and best weights are written back after the loop when the selection metric is `classify_macro_f1_nonnone`. `epoch-progress.jsonl` records the metric and not wall-clock. Spark and public `main` have the same `loop.py` sha256 `f51e7aaff69e9033cc9ba16eee7225bfeefcf521e32236bdb791cc7900e130a5`. Spark HEAD `098ece4d9e8ebb27b0b0d3410b1280ed072d4847` was clean. Public `main` is `2f73f30010cc16ee014ed8d88de73131eb80d0a9`. - -The smallest separate change is an optional break in that epoch loop, default off: after a scored epoch, stop when at least 4 epochs have been scored and `epoch - best_epoch >= 4`, still capped by `max_epochs`, and append per-epoch wall-clock seconds to `epoch-progress.jsonl`. Setting `HYPERLEX_TRAIN_EPOCHS=12` is not that schedule. This record does not apply the change. - -SELECT-004 artifacts were not modified. Its reserve stays `EVAL_SPENT` and was not reused. No new reserve was allocated. BEST was not moved. It still names `hyperlex-encoder-modernbert-base-seed-select004`, weights sha256 `9fba0f66b1d5de6492470f53577d1447bfac1d29b9ac03869268abb70bbd97f6`. No optimizer was constructed. Epochs and gradient steps for this id are zero. Launch is not authorized. - -## SELECT-005 still blocked — trainer synced, admission refused - -Spark now carries public `main` `a7d254e8981695072f5be36ab7ebdcc46cbd672c` for the trainer. `scripts/shadow/hyperlexical/loop.py` sha256 `1aa395081d7709be3844bf2568d12100d73d931ecbc51af43c3d6e01dba77e2a`. Early stopping remains default-off. `HLX-EXP-2026-09-27-SELECT-005` is still not sealed. No preregistration file, arm directory, reserve, or admission receipt was created. The private ledger sections above were not replaced by the public projection. - -`TRAINING_BLOCKED`. The trainer can express the candidate schedule. Canonical admission cannot seal it. - -`admit_training_run` allows exactly one scientific difference, and that difference must be `HLX_SELECT_METRIC`. A non-mutating probe pinned both arms to `classify_macro_f1_nonnone` and changed only `HYPERLEX_TRAIN_EPOCHS` (`40` versus `12`) and `HYPERLEX_EARLY_STOP` (`0` versus `1`). Patience and minimum epochs were the same on both arms. The gate `single_variable` failed: `scientific variable count is 2: HYPERLEX_EARLY_STOP,HYPERLEX_TRAIN_EPOCHS`. - -The spent ledger cannot supply a new `EVAL_RESERVE`. Events sha256 remains `96b74a92d44f1cf9fe152b18e5207176f161ba3bfce528dac38aa4571a742f9c`. Live reserve counts are classify 0, classify_observed 0, classify_non_none 0, unbind_clean 0. The same probe failed at `holdout_reserve`: `reserve slice classify is absent`. The reserve scan also includes every identity with `evaluation_reserved` set. That scan is 491 identities, all derived `EVAL_SPENT`. A new reserve written onto this ledger would still fail that lifecycle check. The flag was not cleared. The SELECT-004 reserve was not reused. - -No optimizer was constructed. Epochs and gradient steps for this id are zero. BEST was not moved. It still names `hyperlex-encoder-modernbert-base-seed-select004`, weights sha256 `9fba0f66b1d5de6492470f53577d1447bfac1d29b9ac03869268abb70bbd97f6`. Launch is not authorized. - -The smallest separate change is an admission-contract patch: accept one declared schedule variable while both arms share `classify_macro_f1_nonnone`, and treat historical `EVAL_SPENT` identities as outside the current reserve without clearing `evaluation_reserved`. Do not allocate a reserve before that contract exists. - - -## SELECT-005 still unsealed — admission contract synced, fresh reserve unavailable - -Public `main` is `fab0de03d75e3280dc34feed425c4783ccf7e2b5`, the squash merge of the admission contract. Spark carries that `admission.py` sha256 `6ae7b63ca1b6e0459d9b795c731668b3eff524269d81bcb92181c0c9d407ab78` and `identity_ledger.py` sha256 `ac49b7240a895952b99b3ef9046f7a8185c8647badac7a45c8640d592057d1c5`. `scripts/shadow/hyperlexical/loop.py` remains sha256 `1aa395081d7709be3844bf2568d12100d73d931ecbc51af43c3d6e01dba77e2a`. The private ledger file was not replaced. - -`HLX-EXP-2026-09-27-SELECT-005` is still not sealed. No preregistration file, arm directory, reserve binding, or admission receipt was created. The ledger was not appended. Events sha256 remains `96b74a92d44f1cf9fe152b18e5207176f161ba3bfce528dac38aa4571a742f9c`. `evaluation_reserved` was not cleared. The SELECT-004 reserve stays `EVAL_SPENT` and was not reused. - -A fresh reserve needs all four slices from text that is absent from the ledger and from the pinned export. The ledger has no `EVAL_RESERVE`, `EVAL_BOUND`, or `AVAILABLE` identities. The remaining settled hashes from the held-out stream that are absent from the ledger are `UNRESOLVED`, `NONE`, `RECLASSIFY`, or `ACCEPT` with `RIGHTS_UNRESOLVED`. Unresolved-rights rows stay out of `EVAL_RESERVE`. No novel rights-cleared classify settlement remains. A classify-absent reserve was not written. The WordNet unbind source was not admitted by itself. - -No optimizer was constructed. Epochs and gradient steps for this id are zero. BEST was not moved. It still names `hyperlex-encoder-modernbert-base-seed-select004`, weights sha256 `9fba0f66b1d5de6492470f53577d1447bfac1d29b9ac03869268abb70bbd97f6`. Launch is not authorized. - - -## SELECT-005 census — fresh reserve still unavailable - -A second join of the 339 settlement events to the 7964 ledger identities found no novel rights-cleared classify row. Events sha256 remains `96b74a92d44f1cf9fe152b18e5207176f161ba3bfce528dac38aa4571a742f9c`. The ledger was not appended. `evaluation_reserved` was not cleared. The SELECT-004 reserve was not reused. - -All 6761 unique hashes in the pinned export are already in the ledger. Of the 98 settlement events absent from the ledger, none are in that export. Those 98 are 82 `UNRESOLVED` with cleared rights, 2 `UNRESOLVED` with unresolved rights, 7 `ACCEPT` with unresolved rights, 6 `NONE` with unresolved rights, and 1 `RECLASSIFY` with unresolved rights. `UNRESOLVED` means the operator reviewed the row and declined to settle it. Unresolved-rights rows stay out of `EVAL_RESERVE`. There are 0 novel `CC-BY-SA` rows with decision `ACCEPT`, `RECLASSIFY`, or `NONE`. - -The 28 promoted-accept files are training gold under an older family set and were not remapped. WordNet can still supply `unbind_clean` and was not admitted alone, because a classify-absent reserve fails the slice gate. No preregistration, arm directory, reserve binding, or admission receipt was created. The optimizer was not constructed. Epochs and gradient steps for `HLX-EXP-2026-09-27-SELECT-005` remain 0. `training_launch_authorized` is false. BEST was not moved. - - -## SELECT-005 classify candidates blocked — HLX-EVAL-REVIEW-2026-09-27-001 - -Admission requires each active slice count to be at least 1. The classify floors are classify 1, classify_observed 1, and classify_non_none 1. Planning targets 606, 287, and 604 are not that floor. This pass did not lower the floor. - -Packet `HLX-EVAL-REVIEW-2026-09-27-001` is an operator-review packet, not a reserve. Ready rows: 0. Other screened rows: label unresolved 10, rights blocked 69, provenance blocked 4, cohort duplicate 1. Previously declined rights-cleared rows and unresolved-rights events were not reopened. WordNet and the older promoted-accept files were not used. No operator decision was written. - -Events sha256 remains `96b74a92d44f1cf9fe152b18e5207176f161ba3bfce528dac38aa4571a742f9c`. The ledger was not appended. `HLX-EXP-2026-09-27-SELECT-005` was not sealed. BEST was not moved. - - -## ai-native evaluation family — 2026-09-27 - -Public `main` is `ca9403efe8470d46566abbcd098640f07b33b759`. `ai-native` is taxonomy-active. `evaluation.enabled` stays false. The production head stays nine names. No row was settled. The ledger was not appended. Events sha256 remains `96b74a92d44f1cf9fe152b18e5207176f161ba3bfce528dac38aa4571a742f9c`. - -The review packet `HLX-EVAL-REVIEW-2026-09-27-001` now has 10 ready rows proposing `ai-native`. Their stored class stays `INFERRED`. `classify_observed` is still short by 1 until an operator attests `OBSERVED`. `HLX-EXP-2026-09-27-SELECT-005` is not sealed. - - -## Operator attested the ready rows observed - -The operator attested `OBSERVED` on all 10 ready rows in packet `HLX-EVAL-REVIEW-2026-09-27-001`. The proposed family is `ai-native`. Rights-blocked, provenance-blocked, and duplicate rows were not attested. No `ACCEPT`, `RECLASSIFY`, or `NONE` was written. The ledger was not appended. Events sha256 remains `96b74a92d44f1cf9fe152b18e5207176f161ba3bfce528dac38aa4571a742f9c`. `HLX-EXP-2026-09-27-SELECT-005` is not sealed. - -## Unbind preview refused as slang — 2026-09-27 - -The operator reviewed the first 20 positional surfaces from the novel WordNet unbind pool. None are admitted as slang. Eighteen are refused. `give a damn` and `in one's birthday suit` are quarantined for provenance review. Slang, idiom, colloquialism, and profanity stay distinct. No `ACCEPT`, `REJECT`, `CORRECT_TARGET`, or `UNRESOLVED` was written to the unbind settlement log. The ledger was not appended. Events sha256 remains `96b74a92d44f1cf9fe152b18e5207176f161ba3bfce528dac38aa4571a742f9c`. These rows are not classify gold and must not be trained as slang. `HLX-EXP-2026-09-27-SELECT-005` is not sealed. Disposition packet `HLX-EVAL-UNBIND-PREVIEW-2026-09-27-001`. - -## Unbind preview roles — 2026-09-27 - -Slang classification and structure unbinding stay orthogonal. On the same 20-surface preview, the operator marked 7 high-value unbind candidates, 8 secondary candidates, and 5 rejects. The rejects are `beta vulgaris`, `gulf of oman`, `u. s. air force`, `department of the federal government`, and `court of assize and nisi prius`. The slang disposition is unchanged: none admitted, 18 refused, and `give a damn` plus `in one's birthday suit` quarantined as slang. No unbind settlement decision was written. The 15 candidates are not admitted. The ledger was not appended. Events sha256 remains `96b74a92d44f1cf9fe152b18e5207176f161ba3bfce528dac38aa4571a742f9c`. `HLX-EXP-2026-09-27-SELECT-005` is not sealed. Packet `HLX-EVAL-UNBIND-PREVIEW-2026-09-27-001`. - -## Unbind screen specified — 2026-09-27 - -`RUNE.UNBIND_SCREEN.v1` is a candidate-selection rule, not an admission filter. Specification agreement with the 20-item preview is 20/20. That figure is specification fit, not held-out precision. A held-out validation sample of 32 admissible positional surfaces, excluding those 20, is recorded with proposed buckets and `gold` null. Provisional screen counts on 63882 admissible positional surfaces are high-value 1965, secondary 60559, reject 1358. Those counts are not a draw. No settlement was written. The ledger was not appended. Events sha256 remains `96b74a92d44f1cf9fe152b18e5207176f161ba3bfce528dac38aa4571a742f9c`. `HLX-EXP-2026-09-27-SELECT-005` is not sealed. Packet `HLX-EVAL-UNBIND-SCREEN-2026-09-27-001`. - -## Unbind screen v2 — 2026-09-27 - -`RUNE.UNBIND_SCREEN.v2` replaces the v1 proposal as the screening hypothesis. It is not authorized as the SELECT-005 screen. Surface patterns remain candidate-generation heuristics. Hard exclusions are proper name, titled entity, taxonomy, productive number, and unconstrained free composition. Agreement after the revision is 20/20 on the first preview and 32/32 on the reviewed sample. That agreement is fit, not held-out precision. A new validation sample excludes all 52 reviewed surfaces and carries `gold` null. No settlement was written. The ledger was not appended. Events sha256 remains `96b74a92d44f1cf9fe152b18e5207176f161ba3bfce528dac38aa4571a742f9c`. `HLX-EXP-2026-09-27-SELECT-005` is not sealed. Packet `HLX-EVAL-UNBIND-SCREEN-2026-09-27-002`. - -## Unbind screen v3 — 2026-09-27 - -`RUNE.UNBIND_SCREEN.v3` is a hypothesis. It is not authorized as the SELECT-005 screen. Candidate-generation patterns do not assign high-value. The automatic screen emits reject or unresolved only. v2 pool counts stay frozen at high-value 1977, secondary 55427, reject 6478, over 63882 positional surfaces, and were not recomputed. No settlement was written. The ledger was not appended. Events sha256 remains `96b74a92d44f1cf9fe152b18e5207176f161ba3bfce528dac38aa4571a742f9c`. `HLX-EXP-2026-09-27-SELECT-005` is not sealed. Packet `HLX-EVAL-UNBIND-SCREEN-2026-09-27-003`. - -## Unbind screen v3 held out — 2026-09-27 - -`RUNE.UNBIND_SCREEN.v3` stays a proposed refinement. Fifty-two surfaces are frozen as development data and thirty-two as validation-development data. Agreement on those eighty-four is fit, not held-out precision. A fresh sample of 29 admissible positional surfaces excludes all 84. Predictions were not hand-corrected. Held-out precision is not computed. v2 pool counts stay frozen at high-value 1977, secondary 55427, reject 6478, over 63882 positional surfaces. The v3 screen was not run on that pool. No settlement was written. The ledger was not appended. Events sha256 remains `96b74a92d44f1cf9fe152b18e5207176f161ba3bfce528dac38aa4571a742f9c`. `HLX-EXP-2026-09-27-SELECT-005` is not sealed. Packet `HLX-EVAL-UNBIND-SCREEN-2026-09-27-004`. - -## Unbind held-out frozen — 2026-09-27 - -The 29-row v3 application is frozen. Sample sha256 `8af5644061a7a60fc5620c217e15a4ec8145f170edee9d8ff4e2999e7b86605e`. It was produced by one application of `RUNE.UNBIND_SCREEN.v3` and was not hand-corrected. Operator labels are pending. Held-out precision is `NOT_COMPUTABLE`. v3 was not revised. Development data remain 52 rows. Validation-development data remain 32 rows. Admitted 0. Settled 0. Gold 0. The ledger was not appended. Events sha256 remains `96b74a92d44f1cf9fe152b18e5207176f161ba3bfce528dac38aa4571a742f9c`. `HLX-EXP-2026-09-27-SELECT-005` is not authorized and is not sealed. - -## Unbind screen evaluation lane — 2026-09-27 - -`HLX-EVAL-UNBIND-SCREEN-V3-001` evaluates the frozen 29-row v3 application. Source sample sha256 remains `8af5644061a7a60fc5620c217e15a4ec8145f170edee9d8ff4e2999e7b86605e`. Prediction, operator judgment, gold, admission, and settlement are separate artifacts. The blind review does not carry the predicted bucket. Operator labels are pending. Held-out precision and the confusion matrix are `NOT_COMPUTABLE`. A scored report does not authorize `HLX-EXP-2026-09-27-SELECT-005`. The ledger was not appended. Events sha256 remains `96b74a92d44f1cf9fe152b18e5207176f161ba3bfce528dac38aa4571a742f9c`. - -## Unbind screen held-out scored — 2026-09-27 - -Operator labels for HLX-EVAL-UNBIND-SCREEN-V3-001 are frozen. Label sha256 `4e7bae5986e6345de62086af270a1d1a6902103d69a50d8f0b1e4e0fe01ecde5`. The source sample sha256 remains `8af5644061a7a60fc5620c217e15a4ec8145f170edee9d8ff4e2999e7b86605e`. Resolved accuracy is 16/29. High precision is 1 and recall is 6/13. Secondary precision is 4/17 and recall is 1. Reject precision is 1 and recall is 6/12. Quarantine support is 0. Every error is false secondary: 7 operator-high and 6 operator-reject. False high and false reject are 0. revision_eligible stays false. SELECT-005 is not authorized. The ledger was not appended. Events sha256 remains `96b74a92d44f1cf9fe152b18e5207176f161ba3bfce528dac38aa4571a742f9c`. - -## Unbind screen v4 hypothesis — 2026-09-28 - -The v3 held-out score stays 16/29. All 13 errors are false secondary: 7 operator-high and 6 operator-reject. False high and false reject are 0. Those 29 rows are now v4 development evidence, not a validation set. `RUNE.UNBIND_SCREEN.v4` is drafted and not encoded, applied, or authorized. It would only add coverage around the secondary basin: normalized productive numbers, multi-token personal names, organization glosses, species common names, and medical technical phrases on one side; nonliteral and conventionalized gloss evidence on the other. Existing high and reject decisions stay in place. `revision_eligible` stays false. SELECT-005 is not authorized. The ledger was not appended. Events sha256 remains `96b74a92d44f1cf9fe152b18e5207176f161ba3bfce528dac38aa4571a742f9c`. - -## Unbind screen v4 acceptance frozen — 2026-09-28 - -`RUNE.UNBIND_SCREEN.v4.ACCEPTANCE` is frozen and the screen is not encoded. Acceptance sha256 `ffb39e38784a56ae15bae51718c61b78fc861e48399936dbed57fb7d0754c55b`. v4 may only move additional secondary fall-throughs, and only by semantic evidence classes. It must not reinterpret v3 high or reject logic, redefine secondary, or train on a future validation sample. Phrase-specific exceptions are prohibited. The 113 reviewed surfaces are the later regression set. The next measurement sample must exclude them and be frozen before inspection. `revision_eligible` stays false. SELECT-005 is not authorized. The v3 sample and label hashes are unchanged. The ledger was not appended. Events sha256 remains `96b74a92d44f1cf9fe152b18e5207176f161ba3bfce528dac38aa4571a742f9c`. - -## Unbind screen v4 encoded — 2026-09-28 - diff --git a/.land-chunks/part-02 b/.land-chunks/part-02 deleted file mode 100644 index 9aa4de80..00000000 --- a/.land-chunks/part-02 +++ /dev/null @@ -1,140 +0,0 @@ -`RUNE.UNBIND_SCREEN.v4` is encoded as a wrapper over a frozen v3 bucket. It inspects a row only when that bucket is secondary. Patch A may move secondary to reject. Patch B may move secondary to high. Existing high and reject decisions are not reopened. The acceptance contract is unchanged, sha256 `ffb39e38784a56ae15bae51718c61b78fc861e48399936dbed57fb7d0754c55b`. - -The 113 reviewed surfaces were replayed as a regression suite. Gate A through Gate E passed. High rows unchanged: 39. Reject rows unchanged: 28. Secondary moves: 13 to high and 6 to reject. Each move has one Patch A or Patch B evidence code. No phrase-specific rule fired. Operator conflict on those moves: 0. This replay is not a new precision estimate. - -Success criteria were frozen before the measurement draw, sha256 `4fcbebfad7797eb22393fa94a389f1ef41402612e359b63d3a3a4b031c6cf8be`. High and reject precision floors are the v3 held-out floors of 1. The false-secondary rate must be strictly below 13/29. False high and false reject are not allowed. Perfect accuracy is not required. - -The measurement sample excludes all 113 reviewed surfaces and duplicate normalized lexical identities. It is stratified by source part of speech and token count, two rows from each occupied cell. Occupied cells produced 28 rows. Sample sha256 `dae8851134aa960a13e072ae017428054c68b988c8d7e6f86d8cab2d16c2586b`. v4 was applied once. Hand corrections are 0. Operator labels are absent. Precision is `NOT_COMPUTABLE`. `revision_eligible` stays false. `HLX-EXP-2026-09-27-SELECT-005` is not authorized. The ledger was not appended. Events sha256 remains `96b74a92d44f1cf9fe152b18e5207176f161ba3bfce528dac38aa4571a742f9c`. The v3 sample sha256 `8af5644061a7a60fc5620c217e15a4ec8145f170edee9d8ff4e2999e7b86605e` and label sha256 `4e7bae5986e6345de62086af270a1d1a6902103d69a50d8f0b1e4e0fe01ecde5` are unchanged. - -## Unbind screen v4 measurement scored — 2026-09-28 - -Operator labels for the 28-row v4 measurement sample are frozen. Label sha256 `023691f8349f0dda12c234691f235ae109289fcf9eab86ec20be1e23bfed9463`. The sample sha256 remains `dae8851134aa960a13e072ae017428054c68b988c8d7e6f86d8cab2d16c2586b`. The prediction sha256 remains `a854847e516fbcd37fbb221456e8caf8c420552795ac7fc6225960bb5434084f`. Labels were recorded at `2026-09-28T02:17:00Z`, after the sample freeze at `2026-09-28T01:07:34Z`. Hand corrections are 0. v4 was not applied again. - -Resolved accuracy is 16/28. High precision is 1 (7/7) and recall is 7/13. Reject precision is 1 (5/5) and recall is 5/11. Secondary precision is 4/16 and recall is 4/4. Quarantine support is 0. False high is 0. False reject is 0. False secondary is 12/28, which is below the frozen floor of 13/29. Every error is a secondary fall-through. The pre-registered success criteria pass, so `revision_eligible` is true. `HLX-EXP-2026-09-27-SELECT-005` is not authorized. Admitted 0. Settled 0. Gold 0. The ledger was not appended. Events sha256 remains `96b74a92d44f1cf9fe152b18e5207176f161ba3bfce528dac38aa4571a742f9c`. - -## Unbind screen v5 hypothesis — 2026-09-28 - -The v4 measurement artifact and score receipt stay frozen. Sample sha256 `dae8851134aa960a13e072ae017428054c68b988c8d7e6f86d8cab2d16c2586b`. Prediction sha256 `a854847e516fbcd37fbb221456e8caf8c420552795ac7fc6225960bb5434084f`. Label sha256 `023691f8349f0dda12c234691f235ae109289fcf9eab86ec20be1e23bfed9463`. Acceptance sha256 `ffb39e38784a56ae15bae51718c61b78fc861e48399936dbed57fb7d0754c55b`. Resolved accuracy remains 16/28. High precision remains 1. Reject precision remains 1. False secondary remains 12/28. `revision_eligible` on that measurement remains true. - -Those 28 rows are now v5 development evidence, not a validation set. Evidence sha256 `dcab832038c3209a54a4159b23caf4eecfb94864cef7188af28f1ae3b0ff80c0`. The twelve secondary fall-throughs are two escape routes only: referential or terminological rows that stayed secondary, and lexicalized noncompositional rows that stayed secondary. They are not a fit list. - -`RUNE.UNBIND_SCREEN.v5` is drafted and not encoded, applied, or authorized. It is a wrapper over a frozen v4 bucket. It inspects a row only when that bucket is secondary. One referential/terminological dominance test may move secondary to reject. One lexicalized noncompositionality test may move secondary to high, and only when the surface is conventionalized and the gloss is not compositionally recoverable. A lexicalized and mostly compositional surface stays secondary. Existing high and reject decisions stay in place. A separate rule for each miss is prohibited. `HLX-EXP-2026-09-27-SELECT-005` is not authorized. Admitted 0. Settled 0. Gold 0. The ledger was not appended. Events sha256 remains `96b74a92d44f1cf9fe152b18e5207176f161ba3bfce528dac38aa4571a742f9c`. - -## Unbind screen v5 acceptance frozen — 2026-09-28 - -`RUNE.UNBIND_SCREEN.v5.ACCEPTANCE` is frozen and the screen is not encoded. Acceptance sha256 `752fd459658f636df91a8da9f1a701a0de8e3240d6df12072bbd2b7ac4cc0b7a`. The draft hypothesis sha256 is `c3d3f8081e18582158eab7a1bf154a02aed38614b9801df6a83f7eaf3875da7b`. v5 may only move additional secondary fall-throughs, and only through the two general tests named in the contract. It must not reinterpret v3 or v4 high or reject logic, redefine secondary, redesign the classifier, or train on a future validation sample. Phrase-specific exceptions are prohibited. One executable rule per observed miss is prohibited. - -The later regression replays all 141 reviewed surfaces: 52 development, 32 validation-development, 29 from the v3 held-out application, and 28 from the v4 measurement. The 113-row v4 replay and the 28-row measurement sample are disjoint. Every previously correct high stays high. Every previously correct reject stays reject. Every new move originates from secondary. The next measurement sample must exclude all 141 and be frozen before inspection. v5 is applied once. Operator labels are collected independently. - -The pre-registered bar is high precision 1, reject precision 1, and a false-secondary rate strictly below 12/28. No accuracy-gain target is registered. Perfect accuracy is not required. `revision_eligible` on the v4 measurement stays true and does not authorize `HLX-EXP-2026-09-27-SELECT-005`. The v4 sample, prediction, label, and acceptance hashes are unchanged. The ledger was not appended. Events sha256 remains `96b74a92d44f1cf9fe152b18e5207176f161ba3bfce528dac38aa4571a742f9c`. - -## Unbind screen v5 encoded — 2026-09-28 - -`RUNE.UNBIND_SCREEN.v5` is encoded as a wrapper over a frozen v4 bucket. It inspects a row only when that bucket is secondary. Referential or terminological dominance may move secondary to reject. A conventionalized surface whose gloss is not recoverable from the ordinary first senses of its constituents may move secondary to high. Existing high and reject decisions are not reopened. The acceptance contract is unchanged, sha256 `752fd459658f636df91a8da9f1a701a0de8e3240d6df12072bbd2b7ac4cc0b7a`. - -The 141 reviewed surfaces were replayed as a regression suite. High rows unchanged: 59. Reject rows unchanged: 39. Secondary moves: 4 to high and 7 to reject. Each move has the one evidence code for its patch. No phrase-specific rule fired. Operator conflict on those moves: 0. This replay is not a new precision estimate. Gate report sha256 `c70214ac1ea3f24b0b8c5590b015a64de4f5a7a632cf372f02aa9e88bd5b2977`. - -The pre-registered bar is unchanged: high precision 1, reject precision 1, and a false-secondary rate strictly below 12/28. Criteria sha256 `49e179db537a738bb7370404167c7ec1c7019fcd251d77e6a916f4426a0ecccb`. No accuracy-gain target was added. - -The measurement sample excludes all 141 reviewed surfaces and duplicate normalized lexical identities. It is stratified by source part of speech and token count, two rows from each occupied cell. Occupied cells produced 28 rows. Sample sha256 `a462f08307e62b09fdfe1dfb6e9a86ec3ea207db27e917730b0d244c47c73359`. Prediction sha256 `51bcf051fb1a4b9bf67a28fe7d1235c8922e3a887bb908de32e5ac0d78a6408f`. v5 was applied once. Hand corrections are 0. Operator labels are absent. Precision is `NOT_COMPUTABLE`. State is `MEASUREMENT_ELIGIBLE`. `revision_eligible` on this screen stays false. `revision_eligible` on the v4 measurement stays true. `HLX-EXP-2026-09-27-SELECT-005` is not authorized. Admitted 0. Settled 0. Gold 0. The ledger was not appended. Events sha256 remains `96b74a92d44f1cf9fe152b18e5207176f161ba3bfce528dac38aa4571a742f9c`. - -## Unbind screen v5 measurement scored — 2026-09-28 - -Operator labels for the 28-row v5 measurement sample are frozen. Each label carries only the row identity, the surface, the operator bucket, and the operator reason. Label sha256 `b05dc1c35d3d10eed4b615ca1ee8251a875a450f57560fef4b5a84b3e1fc075f`. The row table is high 11, secondary 8, reject 9, quarantine 0, unresolved 0. The seal line said high 10, secondary 8, reject 10. The frozen artifact follows the row table. - -The sample sha256 remains `a462f08307e62b09fdfe1dfb6e9a86ec3ea207db27e917730b0d244c47c73359`. The prediction sha256 remains `51bcf051fb1a4b9bf67a28fe7d1235c8922e3a887bb908de32e5ac0d78a6408f`. Labels were recorded at `2026-09-28T03:03:27Z`, after the sample freeze at `2026-09-28T02:44:48Z`. Hand corrections are 0. v5 was not applied again. The 141-row regression remains a regression result and is not a generalization estimate. - -Resolved accuracy is 17/28. High precision is 6/7 and recall is 6/11. Reject precision is 5/6 and recall is 5/9. Secondary precision is 6/15 and recall is 6/8. Quarantine support is 0. False high is 1. False reject is 1. False secondary is 9/28: 5 operator-high and 4 operator-reject. Accuracy was recorded and was not part of the bar. - -False high is 1 and false reject is 1, so the outer-bucket bar fails. The false high is `keep out`, which v4 had already marked high and v5 passed through. The false reject is `on the job`, which v5 moved from secondary to reject on referential/terminological dominance. `revision_eligible` stays false. The screen was not retuned. `HLX-EXP-2026-09-27-SELECT-005` is not authorized. Admitted 0. Settled 0. Gold 0. The ledger was not appended. Events sha256 remains `96b74a92d44f1cf9fe152b18e5207176f161ba3bfce528dac38aa4571a742f9c`. `revision_eligible` on the v4 measurement stays true. Acceptance sha256 remains `752fd459658f636df91a8da9f1a701a0de8e3240d6df12072bbd2b7ac4cc0b7a`. - -## Unbind screen v6 hypothesis — 2026-09-28 - -The v5 measurement stays scored as an outer-bucket failure. Sample sha256 `a462f08307e62b09fdfe1dfb6e9a86ec3ea207db27e917730b0d244c47c73359`. Prediction sha256 `51bcf051fb1a4b9bf67a28fe7d1235c8922e3a887bb908de32e5ac0d78a6408f`. Label sha256 `b05dc1c35d3d10eed4b615ca1ee8251a875a450f57560fef4b5a84b3e1fc075f`. False high is 1. False reject is 1. High precision is 6/7. Reject precision is 5/6. False secondary is 9/28. Resolved accuracy is 17/28 and is descriptive only. `revision_eligible` on v5 stays false. - -The error analysis is frozen and authorizes no fix. Analysis sha256 `ecb229dccec627af958d12537ab153d35de9e3e58d71bb8d3f0a8835077b26ac`. One inherited high overreach: `keep out` was already high at v4, and v5 passed it through against an operator secondary label. One new reject overreach: `on the job` moved from secondary to reject under referential/terminological dominance against an operator secondary label. Secondary undercoverage remains 5 operator-high rows and 4 operator-reject rows. These 28 rows are v6 development evidence, not a validation set. Evidence sha256 `daf512dd331216fecb87ec4d4890c83a03afaf78b5fc1ca6dae6e46e1a9f63a9`. - -`RUNE.UNBIND_SCREEN.v6` is drafted and not encoded, applied, or authorized. Draft sha256 `942eeab825d1c89c21e91af84da0d753281a91012e9ff219a633d27fe6648aa5`. It is a structural revision, not another secondary-only wrapper. A frozen v5 bucket is provisional. High may fall to secondary only with compositional recoverability. Reject may fall to secondary only with nonreferential lexical use. A demotion stops at secondary for that application. A provisional secondary row may still move by the two existing evidences: lexicalized noncompositional to high, and referential/terminological dominance to reject. High and reject do not swap directly. The motivating surfaces are not a required fit. `HLX-EXP-2026-09-27-SELECT-005` is not authorized. Admitted 0. Settled 0. Gold 0. The ledger was not appended. Events sha256 remains `96b74a92d44f1cf9fe152b18e5207176f161ba3bfce528dac38aa4571a742f9c`. - -## Unbind screen v6 acceptance frozen — 2026-09-28 - -`RUNE.UNBIND_SCREEN.v6.ACCEPTANCE` is frozen and the screen is not encoded. Acceptance sha256 `68c60dfe9efaf9f79bc445b974c5436d31b60b77fe7374b17103f3b74865610f`. The draft hypothesis sha256 is `942eeab825d1c89c21e91af84da0d753281a91012e9ff219a633d27fe6648aa5`. The error analysis sha256 is `ecb229dccec627af958d12537ab153d35de9e3e58d71bb8d3f0a8835077b26ac`. The development evidence sha256 is `daf512dd331216fecb87ec4d4890c83a03afaf78b5fc1ca6dae6e46e1a9f63a9`. - -The replay set is all 169 reviewed surfaces: 52 development, 32 validation-development, 29 from the v3 held-out application, 28 from the v4 measurement, and 28 from the v5 measurement. The v5 measurement is disjoint from the prior 141. Previously operator-correct rows must remain operator-correct. High falls to secondary only with compositional recoverability. Reject falls to secondary only with nonreferential lexical use. Secondary rises to high only with lexicalized noncompositional evidence. Secondary falls to reject only with referential/terminological dominance. Direct movement between high and reject is prohibited. Phrase-specific rules are prohibited. - -The pre-registered measurement bar is high precision 1 and reject precision 1, with false high and false reject at 0. No false-secondary quota and no accuracy-gain target are registered. The next measurement sample must exclude all 169 reviewed surfaces and be frozen before inspection. `revision_eligible` on v5 stays false. `revision_eligible` on the v4 measurement stays true. `HLX-EXP-2026-09-27-SELECT-005` is not authorized. The v5 sample, prediction, label, and acceptance hashes are unchanged. The ledger was not appended. Events sha256 remains `96b74a92d44f1cf9fe152b18e5207176f161ba3bfce528dac38aa4571a742f9c`. - -## Unbind screen v6 encoded — 2026-09-28 - -`RUNE.UNBIND_SCREEN.v6` is encoded over a frozen v5 bucket. High may fall to secondary only when the gloss is recoverable from the ordinary first senses of at least two constituents and the synset has no unrelated single-word mapping. Reject may fall to secondary only when a pertainym is used as a state or relation whose gloss meets those ordinary senses, and not when the gloss is a relational designation. A demotion stops at secondary for that application. A provisional secondary row may still move by lexicalized noncompositional evidence or referential/terminological dominance. High and reject do not swap. The acceptance contract is unchanged, sha256 `68c60dfe9efaf9f79bc445b974c5436d31b60b77fe7374b17103f3b74865610f`. - -The 169 reviewed surfaces were replayed. Previously correct rows lost: 0. Direct swaps between high and reject: 0. High to secondary: 0. Reject to secondary: 1. That row is `on the job`, evidence `nonreferential_lexical_use`, and the operator label is secondary. Secondary to high: 0. Secondary to reject: 0. No phrase-specific rule fired. `keep out` stays high. Its gloss is not covered by the ordinary first senses of its constituents, and the synset maps it to an unrelated word, so compositional recoverability does not fire. That miss is not a required fit. Unchanged disagreements: 16. This replay is not a precision estimate. Gate report sha256 `66e4f3859738dfaa63a21ec2e69025b568ea097c17f509ceed4277022d979ffb`. Prediction sha256 `7398254bd62253129153e7b57c99122ed7cc7157520788cc60bb0e80f0b356e8`. Diff sha256 `3af430bb9261c1cedc1bd6181f5e928e01eef22bccea0b81c3c435073fabff59`. - -The pre-registered bar is unchanged: high precision 1, reject precision 1, false high 0, and false reject 0. No false-secondary floor and no accuracy target are registered. Criteria sha256 `3a511ffb49f32930ead14ca2f1e2f6639122c47a7ceec9d1bc29815dd7b490dd`. - -The measurement sample excludes all 169 reviewed surfaces and duplicate normalized lexical identities. It is stratified by source part of speech and token count, two rows from each occupied cell. Occupied cells produced 28 rows. Sample sha256 `8ee516423f002a355759eed788bfe3bef81332fad61b1f19c7e46bd0722a29f2`. Prediction sha256 `476c5526b18fcb999f2a5397d0674ec07e29b199d5a5259afb555bf608d245ee`. v6 was applied once. Hand corrections are 0. Operator labels are absent. Precision is `NOT_COMPUTABLE`. State is `MEASUREMENT_ELIGIBLE`. `revision_eligible` on this screen stays false. `revision_eligible` on v5 stays false. `revision_eligible` on the v4 measurement stays true. `HLX-EXP-2026-09-27-SELECT-005` is not authorized. Admitted 0. Settled 0. Gold 0. The ledger was not appended. Events sha256 remains `96b74a92d44f1cf9fe152b18e5207176f161ba3bfce528dac38aa4571a742f9c`. - -## Unbind screen v6 measurement scored — 2026-09-28 - -Operator labels for the 28-row v6 measurement sample are frozen. Each label carries only the row identity, the surface, the operator bucket, and the operator reason. Label sha256 `feed0ee131a513f2801bd1725f553bb6274df1378b28fdea1cb13e238ea9eb11`. The row table is high 12, secondary 4, reject 12, quarantine 0, unresolved 0. The seal line said high 11, secondary 4, reject 13. The frozen artifact follows the row table. - -The sample sha256 remains `8ee516423f002a355759eed788bfe3bef81332fad61b1f19c7e46bd0722a29f2`. The prediction sha256 remains `476c5526b18fcb999f2a5397d0674ec07e29b199d5a5259afb555bf608d245ee`. Labels were recorded at `2026-09-28T03:34:48Z`, after the sample freeze at `2026-09-28T03:25:47Z`. Hand corrections are 0. v6 was not applied again. The 169-row regression remains a regression result and is not a generalization estimate. - -Resolved accuracy is 21/28 and is descriptive only. High precision is 9/10 and recall is 9/12. Reject precision is 1 (9/9) and recall is 9/12. Secondary precision is 3/9 and recall is 3/4. Quarantine support is 0. False high is 1. False reject is 0. False secondary is 6/28: 3 operator-high and 3 operator-reject. Recall and accuracy were not part of the bar. - -A false high or false reject is present, so the outer-bucket bar fails. `revision_eligible` stays false. The screen was not retuned. `HLX-EXP-2026-09-27-SELECT-005` is not authorized. Admitted 0. Settled 0. Gold 0. The ledger was not appended. Events sha256 remains `96b74a92d44f1cf9fe152b18e5207176f161ba3bfce528dac38aa4571a742f9c`. `revision_eligible` on v5 stays false. `revision_eligible` on the v4 measurement stays true. Acceptance sha256 remains `68c60dfe9efaf9f79bc445b974c5436d31b60b77fe7374b17103f3b74865610f`. - -## Unbind screen v7 hypothesis — 2026-09-28 - -The v6 measurement stays scored as an outer-bucket failure. Sample sha256 `8ee516423f002a355759eed788bfe3bef81332fad61b1f19c7e46bd0722a29f2`. Prediction sha256 `476c5526b18fcb999f2a5397d0674ec07e29b199d5a5259afb555bf608d245ee`. Label sha256 `feed0ee131a513f2801bd1725f553bb6274df1378b28fdea1cb13e238ea9eb11`. High precision is 9/10. Reject precision is 1 (9/9). False high is 1. False reject is 0. False secondary is 6/28. Resolved accuracy is 21/28 and is descriptive only. `revision_eligible` on v6 stays false. `revision_eligible` on v5 stays false. `revision_eligible` on the v4 measurement stays true. - -The only false high in that measurement is `to a lesser extent`. The operator bucket is secondary. v3, v4, v5, and v6 are high, and v6 recorded no evidence code. A prior inherited high disagreement, `keep out`, stays high in the 169-row replay. Its operator bucket is secondary and `compositional_recoverability` did not fire. That v6 predicate is not redefined. Secondary fall-throughs are 13/29 at v3, 12/28 at v4, 9/28 at v5, and 6/28 at v6. Coverage is not the next question. - -The error analysis is frozen and authorizes no fix. Analysis sha256 `06a2f10493640d6536b45ef3d9405470c12623bfa787f7fba49b3f64498fb766`. These 28 rows are v7 development evidence, not a validation set. Evidence sha256 `3d457801203c69ceb13c6cefbf5d82fe9cac0daea48f26eb2642d8673585a3a3`. The six false-secondary rows stay unresolved and are not a fit list: `to the letter`, `with child`, `dressed to the nines`, `union jack`, `atomic number 98`, and `law of definite proportions`. Known reviewed inventory is 197: the prior 169 plus these 28. Normalized overlap is 0. - -`RUNE.UNBIND_SCREEN.v7` is specified and not encoded, applied, or authorized. State is `SPEC_FROZEN`. Draft sha256 `c168bf975e26804a032956d570e39a3d2c7078b407ef966da33083adecf5585b`. It keeps the frozen v6 phase order and replaces only the high challenge. A provisional high may fall to secondary only with `ordinary_compositional_derivation`: the WordNet sense can be derived by ordinary grammatical, syntactic, comparative, or phrasal composition without a stored conventionalized or nonliteral lexical binding. The test does not require recovery from only the ordinary first sense of each token. It asks whether lexicalized binding is required at all. A demotion stops at secondary for that application. Reject may still fall to secondary only with `nonreferential_lexical_use`. A provisional secondary row may still move by the unchanged v6 evidences. High and reject do not swap. `to a lesser extent` and `keep out` motivate the question and are not required fits, and neither is special-cased. No SELECT-005 admission surface exists. `HLX-EXP-2026-09-27-SELECT-005` is not authorized. Admitted 0. Settled 0. Gold 0. The ledger was not appended. Events sha256 remains `96b74a92d44f1cf9fe152b18e5207176f161ba3bfce528dac38aa4571a742f9c`. - -## Unbind screen v7 acceptance frozen — 2026-09-28 - -`RUNE.UNBIND_SCREEN.v7.ACCEPTANCE` is frozen and the screen is not encoded. State is `SPEC_FROZEN`. Acceptance sha256 `562c0756b0337e2fb10643f4fd6689ea4421977a345d12fe33d8a1504161cac5`. The draft hypothesis sha256 is `c168bf975e26804a032956d570e39a3d2c7078b407ef966da33083adecf5585b`. The error analysis sha256 is `06a2f10493640d6536b45ef3d9405470c12623bfa787f7fba49b3f64498fb766`. The development evidence sha256 is `3d457801203c69ceb13c6cefbf5d82fe9cac0daea48f26eb2642d8673585a3a3`. - -The replay set is all 197 reviewed surfaces: 52 development, 32 validation-development, 29 from the v3 held-out application, 28 from the v4 measurement, 28 from the v5 measurement, and 28 from the v6 measurement. The v6 measurement is disjoint from the prior 169. Previously operator-correct rows must remain operator-correct. A previously correct high stays high. High falls to secondary only with ordinary compositional derivation. The frozen v6 transitions for secondary to high, secondary to reject, and reject to secondary are unchanged. Direct movement between high and reject is prohibited. A high demotion stops at secondary and is not re-promoted in the same application. Phrase-specific rules, row-id rules, and surface-hash rules are prohibited. - -No measurement sample was drawn. The bar proposed for a later unseen sample is high precision 1 and reject precision 1, with false high and false reject at 0. No false-secondary quota, no accuracy target, and no recall target are registered. That sample must exclude all 197 reviewed normalized identities and be frozen before inspection. `revision_eligible` stays false on v7, v6, and v5. `revision_eligible` on the v4 measurement stays true. `HLX-EXP-2026-09-27-SELECT-005` is not authorized. No admission surface for that experiment exists. The v6 sample, prediction, label, criteria, and acceptance hashes are unchanged. The ledger was not appended. Events sha256 remains `96b74a92d44f1cf9fe152b18e5207176f161ba3bfce528dac38aa4571a742f9c`. - -## Unbind screen v7 encoded — 2026-09-28 - -`RUNE.UNBIND_SCREEN.v7` is encoded over a frozen v6 bucket. The only new predicate is `ordinary_compositional_derivation`. It inspects a provisional high. The recorded sense must be a comparative, syntactic, or phrasal composition, and the synset must not store an unrelated single-word synonym. A gloss that merely shares constituent stems does not fire, and a metaphorical retelling does not fire. A demotion stops at secondary. Reject rows still use the v6 reject challenge. Secondary rows are not reopened, so a demotion v6 already stopped stays stopped. `compositional_recoverability` is not a v7 transition. The acceptance contract is unchanged, sha256 `562c0756b0337e2fb10643f4fd6689ea4421977a345d12fe33d8a1504161cac5`. - -The 197 reviewed surfaces were replayed as development and regression evidence, not as a generalization estimate. Previously correct rows lost: 0. Previously correct high rows demoted: 0. Direct swaps between high and reject: 0. High to secondary: 1. Reject to secondary: 0. Secondary to high: 0. Secondary to reject: 0. One row moves. `to a lesser extent` goes from high to secondary with `ordinary_compositional_derivation`, and the operator bucket is secondary. That firing is not a required fit. `keep out` stays high. No phrase-specific rule fired. This replay is not a precision estimate. Gate report sha256 `78225b68c639694bb4c17342c87320ccb44faac557a9145ea117603da0d545ed`. Prediction sha256 `24f266e16b80da602b011bf7cca13774ce9f76c0c52e81e600a1d9743d61d197`. Diff sha256 `b86ae6611a5fcd233b6e92b3def4cdd0680018902532a0a0d5fb02659ad092ae`. - -State is `REGRESSION_VERIFIED`. `measurement_eligible` stays false. The unseen sample was not drawn. The pre-registered bar remains high precision 1, reject precision 1, false high 0, and false reject 0, with no false-secondary quota, no accuracy target, and no recall target. No measurement sample was drawn. `revision_eligible` stays false on v7, v6, and v5. `revision_eligible` on the v4 measurement stays true. `HLX-EXP-2026-09-27-SELECT-005` is not authorized. Admitted 0. Settled 0. Gold 0. The ledger was not appended. Events sha256 remains `96b74a92d44f1cf9fe152b18e5207176f161ba3bfce528dac38aa4571a742f9c`. - -## Unbind screen v7 measurement frozen — 2026-09-28 - -The 197-row regression stays `REGRESSION_VERIFIED`. One unseen measurement sample excludes those 197 normalized identities. Normalized overlap with the reviewed set is 0. Duplicate normalized identities inside the sample are 0. The draw is deterministic and stratified by source part of speech and token count, two rows from each occupied cell. Occupied cells produced 28 rows. No occupied cell was exhausted. Each occupied cell contributed 2 rows. - -The sample was frozen before v7 was applied. Sample sha256 `2b4414e2a6db3acf2967603e3ef4552c631803285633fbae5f3f3d50451724b1`. v7 was then applied once. Hand corrections are 0. Prediction sha256 `d201affe0c87206227bc211a6071a25e639c0ffb7cbcfdc8dac0572155a4da47`. The blind rows carry surface, part of speech, token count, and gloss. They do not carry a bucket or an evidence code. Operator labels are absent. Precision is `NOT_COMPUTABLE`. State is `MEASUREMENT_ELIGIBLE`. - -The acceptance bar is unchanged: high precision 1, reject precision 1, false high 0, and false reject 0. No false-secondary floor, no accuracy target, and no recall target were added. Criteria sha256 `3b22449874f2384d843e4f16cb7e5c28b6acfc83273aec44542e0bc5b0c3ef37`. `revision_eligible` stays false. `HLX-EXP-2026-09-27-SELECT-005` is not authorized. Admitted 0. Settled 0. Gold 0. The ledger was not appended. Events sha256 remains `96b74a92d44f1cf9fe152b18e5207176f161ba3bfce528dac38aa4571a742f9c`. - -## Unbind screen v7 measurement scored — 2026-09-28 - -Operator labels for the 28-row v7 measurement sample are frozen. Each label carries only the row identity, the surface, the operator bucket, and the operator reason. Label sha256 `0ce39dffad49b28b5adebfbe3514fcf81f9ca39689d3eadf0695975496b6adb5`. The row table is high 14, secondary 5, reject 9, quarantine 0, unresolved 0. The seal line said high 13, secondary 5, reject 10. The frozen artifact follows the row table. - -The sample sha256 remains `2b4414e2a6db3acf2967603e3ef4552c631803285633fbae5f3f3d50451724b1`. The prediction sha256 remains `d201affe0c87206227bc211a6071a25e639c0ffb7cbcfdc8dac0572155a4da47`. Labels were recorded at `2026-09-28T04:14:27Z`, after the sample freeze at `2026-09-28T04:05:23Z`. Hand corrections are 0. v7 was not applied again. The 197-row regression remains a regression result and is not a generalization estimate. - -Resolved accuracy is 21/28 and is descriptive only. High precision is 10/11 and recall is 10/14. Reject precision is 7/8 and recall is 7/9. Secondary precision is 4/9 and recall is 4/5. Quarantine support is 0. False high is 1. False reject is 1. False secondary is 5/28: 3 operator-high and 2 operator-reject. Recall, accuracy, and false secondary were not part of the bar. - -A false high or false reject is present, so the outer-bucket bar fails. `revision_eligible` stays false. The screen was not retuned. `HLX-EXP-2026-09-27-SELECT-005` is not authorized. Admitted 0. Settled 0. Gold 0. The ledger was not appended. Events sha256 remains `96b74a92d44f1cf9fe152b18e5207176f161ba3bfce528dac38aa4571a742f9c`. `revision_eligible` on v6 stays false. `revision_eligible` on v5 stays false. `revision_eligible` on the v4 measurement stays true. Acceptance sha256 remains `562c0756b0337e2fb10643f4fd6689ea4421977a345d12fe33d8a1504161cac5`. - -## Unbind screen lineage retired — 2026-09-28 - -`RUNE.UNBIND_SCREEN.v3` through `RUNE.UNBIND_SCREEN.v7` stay sealed. They are a completed experimental lineage. The lineage showed that further surface-pattern patches are the wrong instrument. No `RUNE.UNBIND_SCREEN.v8` exists. Historical scores are not results for the sense-first screen. v7 remains `SCORED`, outcome `OUTER_BUCKET_FAILURE`, `revision_eligible` false. v7 was not retuned. v6 and v5 `revision_eligible` stay false. The v4 measurement `revision_eligible` stays true. Source sha256 values remain v3 `179d8dcc112214c70566bd3c9a0397e1ebab9131666b0ca1f2a3817973aaccc6`, v4 `f1e86e2f21544655cda6a136885a186b20885d501cb7ea9c75e18b3dd4a42377`, v5 `70504574523f2e8fde0fb974e3027205dded2c96213dd997f44475ea6856f948`, v6 `59699496c15aaedfbe69a7e49b5c6e62d1e543ce5a1e0e9a0255a98a62036fba`, v7 `73335bde8eec262ebecfedfc0d0ecb0a965da5c6b66e53c16f2aee2f38b061ab`. Retirement sha256 `fd5d9ebb94d7a6e6ea69609c4e2125ec9914f6705ae256b780223bbea2e26f6f`. - -The v7 measurement disagreements are not one failure class. The frozen error analysis separates three questions: whether the bound sense designates a referent, whether that sense requires conventionalized lexical binding, and whether that sense is compositionally recoverable. Error analysis sha256 `ebc56d4d4499efee19bc368365b0d6d3a7afc27ede4e78f40fb9d0fd15fcb9c8`. The canonical v7 operator counts from the row artifact are high 14, secondary 5, reject 9. The handwritten seal does not override those rows. - -## Unbind sense screen v1 specified — 2026-09-28 - -`RUNE.UNBIND_SENSE_SCREEN.v1` is `SPEC_FROZEN`. It is a new screen family. It is not encoded, not applied, and no measurement sample was drawn. The unit of analysis is the specific lexical sense: surface, part of speech, WordNet synset, and the frozen first-sense gloss. Another sense of the same surface, a historical v3-v7 bucket, and a referential origin that is not the predicated sense are outside that unit. - diff --git a/.land-chunks/part-03 b/.land-chunks/part-03 deleted file mode 100644 index 1ab4f46e..00000000 --- a/.land-chunks/part-03 +++ /dev/null @@ -1,128 +0,0 @@ -The five classes are `REFERENTIAL`, `LEXICALIZED_NONCOMPOSITIONAL`, `LEXICALIZED_COMPOSITIONAL`, `ORDINARY_COMPOSITIONAL`, and `AMBIGUOUS`. The procedure assigns exactly one class. Empty or unmatched sense evidence is `AMBIGUOUS`. A figurative gloss is not referential merely because the wording mentions a place or story; `road to damascus` is the protected illustration and is not a required fit. Ordinary syntax, comparison, degree, and phrasal composition are `ORDINARY_COMPOSITIONAL`; a WordNet lemma by itself is not lexicalization. `as far as possible` motivates that distinction and is not a required fit. Insufficient evidence stays `AMBIGUOUS`. - -The frozen mapping is referential to reject, lexicalized noncompositional to high, both compositional classes to secondary, and ambiguous to quarantine. Ordinary composition does not map to reject. That would hide a second classifier. The two compositional classes remain distinct before the mapping. No historical bucket may override the sense class. - -The reviewed positional inventory is 225: the prior 197 plus the 28 v7 measurement rows. Normalized overlap between those sets is 0. The 28 rows are development evidence, not validation, and not a required fit. 225 rows bind a synset offset whose first-sense gloss equals the frozen gloss. 0 rows keep the frozen gloss with no offset. A future measurement must exclude all 225 normalized identities and be frozen before inspection. The proposed pass/fail bar is high precision 1, reject precision 1, false high 0, and false reject 0. Sense-class confusion, bucket confusion, per-class support, the ambiguous rate, high recall, reject recall, and secondary precision and recall are descriptive. This pass does not make them pass/fail criteria. - -Draft hypothesis sha256 `93375446b1f4a1f70c60f747a56b626ae667c8944d0eea54deddb9d57d3d9e38`. Acceptance sha256 `cff6af0f05ec5e12fb29ddfd2ec321addc94c73258c31860345f6d49960065b0`. Development evidence sha256 `0e9b3c1af9dd573bf6e2034640e468e8ab9074e1e76c90cef1f39f68d607bc03`. Tracker sha256 `4145848a2e1368b23f28f645249a9a8c55bf113a43763d31a434f1389b9e1154`. No JSON Schema exists for this hypothesis family. The older unbind sample, label, and report schemas are a different contract and were not applied. The next named transition is `ENCODED`. This pass does not authorize it. - -## Lexeme structure screen v1 architecture — 2026-09-28 - -`RUNE.LEXEME_STRUCTURE_SCREEN.v1` is an architecture note for a sibling lane. It evaluates one orthographic lexeme for internal structure. Candidate classes are atomic, compound, affixed, blend, clipping, respelling, reduplicated, borrowed, and unknown. A single token is not evidence of an atomic lexeme. A string split is not evidence of a valid morphological decomposition. Conceptual illustrations are not a corpus and not a required fit. This lane is not mixed into positional unbind. It is not encoded. No corpus was populated, no sample was drawn, and no gold was created. Architecture sha256 `529defbc2b56152c3290d5b09f309764128b035906797229dab54857cd249df0`. - -`HLX-EXP-2026-09-27-SELECT-005` is not authorized. Admitted 0. Settled 0. Gold 0. The ledger was not appended. Events sha256 remains `96b74a92d44f1cf9fe152b18e5207176f161ba3bfce528dac38aa4571a742f9c`. - -## Unbind sense screen v1 procedure frozen — 2026-09-28 - -`RUNE.UNBIND_SENSE_SCREEN.v1` moves from `SPEC_FROZEN` to `PROCEDURE_FROZEN`. The screen is not encoded and not applied. No development replay was run. No row received a sense class. No measurement sample was drawn. - -The companion artifact is `hyperlex.unbind_sense_screen_v1_classification_procedure.v1`, sha256 `4d9dad77d8d315e810863101041229c53570ed16970074c86abaecd0cc3012ad`. The sealed acceptance stays `cff6af0f05ec5e12fb29ddfd2ec321addc94c73258c31860345f6d49960065b0`. The sealed draft stays `93375446b1f4a1f70c60f747a56b626ae667c8944d0eea54deddb9d57d3d9e38`. The acceptance does not embed this procedure, so its bytes were not revised. The tracker now points at the procedure. Tracker sha256 `f9c4757b6eec558b5e1baf644bcf33c27c949807e7f00cd15df869eb6411de31`. - -WordNet membership is not evidence that a surface is a conventional lexical unit. A conventional unit requires an unrelated single-word co-lemma or a lexical pointer on the whole lemma. A productive frame requires a one-token lemma alternation stored on the synset, or a gloss that starts with the comparative or superlative operator formula. Anything else at that test is `AMBIGUOUS`. A stored unrelated equivalent is the noncompositional mapping. A derivation or pertainym from the whole lemma to one of its constituents, with no unrelated equivalent, is the compositional lexical unit. Referential designation requires an instance-hypernym pointer or a parenthetical four-digit lifespan. A geographic or religious allusion does not qualify, and neither does the capital letter in `road_to_Damascus`. Ordinary composition still maps only to secondary. Ambiguity maps to quarantine and is a normal class. - -The lexeme-structure note stays `529defbc2b56152c3290d5b09f309764128b035906797229dab54857cd249df0`. Reviewed positional inventory remains 225. v3 through v7 sources are unchanged. `HLX-EXP-2026-09-27-SELECT-005` is not authorized. Admitted 0. Settled 0. Gold 0. The ledger was not appended. Events sha256 remains `96b74a92d44f1cf9fe152b18e5207176f161ba3bfce528dac38aa4571a742f9c`. The next named transition is `ENCODE_AUTHORIZATION`. This pass does not authorize it. - -## Unbind sense screen v1 development replay — 2026-09-28 - -`RUNE.UNBIND_SENSE_SCREEN.v1` is encoded and the 225-row development replay is analyzed. The frozen classification procedure is unchanged, sha256 `4d9dad77d8d315e810863101041229c53570ed16970074c86abaecd0cc3012ad`. The sealed acceptance stays `cff6af0f05ec5e12fb29ddfd2ec321addc94c73258c31860345f6d49960065b0`. The sealed draft stays `93375446b1f4a1f70c60f747a56b626ae667c8944d0eea54deddb9d57d3d9e38`. The development manifest stays `0e9b3c1af9dd573bf6e2034640e468e8ab9074e1e76c90cef1f39f68d607bc03`. No sense class was written onto that manifest. - -The encoder follows the frozen tests. An instance-hypernym pointer or a parenthetical four-digit lifespan is referential. An unrelated single-word co-lemma is lexicalized noncompositional. A derivation or pertainym from the whole lemma back to a constituent, with no unrelated equivalent, is lexicalized compositional. A stored one-token lemma alternation, or a gloss that starts with the comparative or superlative operator formula, is ordinary compositional. Anything else is ambiguous. A yes-signal together with a no-signal is a conflict and stays ambiguous. Absence of a signal is not secondary. No gloss-keyword list, technical-term dictionary, or phrase exception was added. A high ambiguous rate was not repaired. - -Each development row was classified once from its frozen synset. The replay is development evidence, not validation. The measurement bar was not applied. Prediction sha256 `69ea6b8714f3cb6105222d636af3f17bd5c5caac7b290c3c3d87e4efaeedd0ef`. Report sha256 `38ada8bc32d8b19361cc974346d5972f6020eb0c32c2ca537abff4d17f66c7f0`. Tracker sha256 `b3690de5c957f019224c7ea980bf6e27b25ba1f1c519347f72a327fa3ea399a2`. - -Sense classes are referential 26, lexicalized noncompositional 72, lexicalized compositional 0, ordinary compositional 25, and ambiguous 102. The ambiguous rate is 102/225. Buckets are high 72, secondary 25, reject 26, and quarantine 102. Evidence codes are referential designation 26, noncompositional semantic mapping 72, compositional lexical unit 0, productive grammatical frame 25, and insufficient record evidence 102. The determinate sources are instance hypernym 26, unrelated single-word co-lemma 72, and productive alternation 25. No lifespan marker and no grammatical-operator gloss fired on this inventory. - -Lexicalized compositional support is 0. One development row stores a pertainym from the whole lemma to a constituent token and also stores a one-token lemma alternation. The frozen conflict rule returns ambiguous. That zero records the frozen test. It is not a missing dictionary. - -Operator labels already on the manifest, joined after classification: of 101 operator-high rows, 32 stay high, 17 go to secondary, and 52 go to quarantine. Of 46 operator-secondary rows, 22 go to high, 3 stay secondary, and 21 go to quarantine. Of 77 operator-reject rows, 18 go to high, 4 go to secondary, 26 stay reject, and 29 go to quarantine. The one operator-quarantine row goes to secondary. Descriptive precision and recall, not pass/fail criteria: high 32/72 and 32/101, secondary 3/25 and 3/46, reject 26/26 and 26/77. Operator quarantine support is 1. False high is 40. False reject is 0. False secondary is 22 and is descriptive only. - -`road to damascus` is ambiguous and quarantined. The bound record has no instance pointer and no lifespan marker. `as far as possible` is ordinary compositional and secondary, from the stored far/much alternation. Neither row is a required fit. - -State is `DEVELOPMENT_ANALYZED`. The path was `PROCEDURE_FROZEN`, `ENCODE_AUTHORIZED`, `ENCODED`, `225_ROW_DEVELOPMENT_REPLAY`, `DEVELOPMENT_ANALYZED`. `measurement_sample_drawn` stays false. `measurement_eligible` stays false. `revision_eligible` stays false. The lexeme-structure note stays `529defbc2b56152c3290d5b09f309764128b035906797229dab54857cd249df0`. v3 through v7 sources are unchanged. `HLX-EXP-2026-09-27-SELECT-005` is not authorized. Admitted 0. Settled 0. Gold 0. The ledger was not appended. Events sha256 remains `96b74a92d44f1cf9fe152b18e5207176f161ba3bfce528dac38aa4571a742f9c`. The next named transition is `MEASUREMENT_AUTHORIZATION`. This pass does not authorize it. - -## Unbind sense screen procedure v2 frozen — 2026-09-28 - -The encoded v1 screen stays `DEVELOPMENT_ANALYZED`. Procedure v1 stays frozen, sha256 `4d9dad77d8d315e810863101041229c53570ed16970074c86abaecd0cc3012ad`. The companion procedure `hyperlex.unbind_sense_screen_v1_classification_procedure.v2` is `PROCEDURE_V2_FROZEN`. It is not encoded and not applied. No v2 development replay was run. No row received a v2 sense class. No measurement sample was drawn. - -The v1 development replay mapped an unrelated single-word co-lemma to lexicalized noncompositional, and therefore to high. All 40 development false highs used that path. A stored whole-expression synonym can show that the phrase is a lexical unit. It does not show that the sense is semantically noncompositional. - -Procedure v2 keeps the five classes and the sealed bucket map. Before the class, it records three states: referential, lexicalized, and compositional. Each state is yes, no, unknown, or conflict. An unknown state is not a no. Referential yes, from an instance-hypernym pointer or a parenthetical four-digit lifespan, remains referential and reject. A co-lemma or a whole-lemma lexical pointer is lexicalized yes. A derivation or pertainym back to a constituent, a stored one-token alternation, or a comparative or superlative operator gloss is compositional yes. The alternation does not set lexicalized to no. The operator gloss sets lexicalized to no and compositional to yes, so when referential is not yes the class is ordinary compositional. Lexicalized yes together with compositional yes, when referential is not yes, is lexicalized compositional. That cell is reachable. The procedure does not force rows into it. - -WordNet 3.0 has no structural noncompositionality field once the co-lemma shortcut is retired. The compositional no-signal set is empty, so the lexicalized-noncompositional cell stays defined and does not fire. A later encoder must not invent a replacement signal. Missing lexicalization evidence stays unknown. On the record already frozen in procedure v1, `as far as possible` is therefore ambiguous under v2, and `road to damascus` stays ambiguous. Neither illustration is a required fit, and neither class was written onto the development manifest. An insufficient record stays ambiguous and maps to quarantine. A lower ambiguous rate is not a success criterion. Referential precision is not traded for coverage, and no technical-term gloss list was added. - -No JSON Schema document exists for procedure v2. The schema name on the artifact is not a JSON Schema file. Older unbind sample, label, and report schemas were not applied. Procedure sha256 `3f4071640d0c9f29cf56f53969a88ec25c635444b87765e77e1b9158470e5662`. v1 error analysis sha256 `471bc27b89f550fae36b3471daaad282a6dd8735414846cb18aafe1195e0a52e`. Change note sha256 `443ce2964d4e4fcd8257055cb1404965faa70b838264b1f623be192d1cae085c`. Tracker sha256 `af11d20ebefec5629718617ebacc07d8b4cc36c61e59e829d153b05b9397ca40`. - -The reviewed positional inventory remains 225. A future encoded replay of those same rows may report lexicalized-compositional support, the false-high count, the ambiguous rate, referential precision and coverage, the sense-class distribution, the evidence-state distribution, and the operator confusion tables. This freeze sets no accuracy threshold. Lexicalized-compositional support above zero would be a structural readiness signal, not a hard pass/fail rule. - -The lexeme-structure note stays `529defbc2b56152c3290d5b09f309764128b035906797229dab54857cd249df0`. The procedure records the shared invariant that lexicalized identity, compositionality, and semantic shift are different questions. That note was not edited, and the lexeme screen was not implemented. v3 through v7 sources are unchanged. The v1 implementation, v1 predictions, and v1 report are unchanged. Acceptance stays `cff6af0f05ec5e12fb29ddfd2ec321addc94c73258c31860345f6d49960065b0`. Prediction sha256 remains `69ea6b8714f3cb6105222d636af3f17bd5c5caac7b290c3c3d87e4efaeedd0ef`. Report sha256 remains `38ada8bc32d8b19361cc974346d5972f6020eb0c32c2ca537abff4d17f66c7f0`. `HLX-EXP-2026-09-27-SELECT-005` is not authorized. Admitted 0. Settled 0. Gold 0. The ledger was not appended. Events sha256 remains `96b74a92d44f1cf9fe152b18e5207176f161ba3bfce528dac38aa4571a742f9c`. `measurement_sample_drawn` stays false. `measurement_eligible` stays false. `revision_eligible` stays false. - -The next named transition is `ENCODE_PROCEDURE_V2_AUTHORIZATION`. This pass does not authorize it. An unseen measurement stays deferred until an encoded v2 replay of these 225 rows has been examined. - -## Unbind sense screen procedure v2 development replay — 2026-09-28 - -Procedure v2 is encoded and the same 225 development rows were replayed once. State is `DEVELOPMENT_ANALYZED_V2`. The path was `PROCEDURE_V2_FROZEN`, `ENCODE_PROCEDURE_V2_AUTHORIZATION`, `ENCODED`, `225_ROW_DEVELOPMENT_REPLAY`, `DEVELOPMENT_ANALYZED_V2`. The encoded v1 screen stays `DEVELOPMENT_ANALYZED`. Procedure v2 bytes stay `3f4071640d0c9f29cf56f53969a88ec25c635444b87765e77e1b9158470e5662`. Procedure v1 stays `4d9dad77d8d315e810863101041229c53570ed16970074c86abaecd0cc3012ad`. No sense class was written onto the development manifest. No measurement sample was drawn. - -The replay asks whether the frozen WordNet record can instantiate the five classes, and `LEXICALIZED_NONCOMPOSITIONAL` in particular, without treating a single-word co-lemma as compositional NO. It does not instantiate that class. High support is 0. Compositional NO count is 0. False high is 0. A co-lemma sets lexicalized YES and leaves the row ambiguous when compositionality is unknown. Ninety-five rows are lexicalized YES. One hundred eighty-two rows are compositionally unknown. - -Sense classes are referential 27, lexicalized noncompositional 0, lexicalized compositional 16, ordinary compositional 0, and ambiguous 182. The ambiguous rate is 182/225. Buckets are high 0, secondary 16, reject 27, and quarantine 182. Of the 16 lexicalized-compositional rows, 15 are a stored one-token alternation on a lexicalized synset and 1 is a derivation or pertainym back to a constituent. That support is a readiness signal. It is not a pass/fail result. Ordinary compositional support is 0 because no row has lexicalized NO. The one grammatical-operator gloss also has an unrelated co-lemma, so lexicalized state is conflict and the class is ambiguous. - -Referential yes remains an instance-hypernym pointer. All 27 referential rows use that pointer. Descriptive reject precision is 27/27 and recall is 27/77. False reject is 0. One of those 27 was quarantined by procedure v1 because the instance pointer shared the synset with a one-token alternation. Procedure v2 keeps the instance pointer decisive and records the alternation as compositional yes. Secondary precision is 7/16 and recall is 7/46. False secondary is 9 and is descriptive only. High precision is not computable, because no row was predicted high. High recall is 0/101. - -Evidence states: referential yes 27, no 97, unknown 101. Lexicalized yes 95, no 0, unknown 129, conflict 1. Compositional yes 43, no 0, unknown 182, conflict 0. `as far as possible` is referential no, lexicalized unknown, compositional yes, and ambiguous. `road to damascus` is unknown on all three states and ambiguous. Neither illustration was written onto the manifest. - -Prediction sha256 `1f7fc03547d24de851326a4848d93f1dbef16714e74e3e9f86d8c8aa6f8aaa8a`. Report sha256 `93d8fb76da8aa7155fb0ce57b0841ca455eca3e904edf50b9f76e595dd095ca5`. Tracker sha256 `6ecb7e16bbe2ea3be2efca51c46cc970f2a5ba8ec8cd9c101eaef8398f5a0e61`. Acceptance stays `cff6af0f05ec5e12fb29ddfd2ec321addc94c73258c31860345f6d49960065b0`. The v1 prediction and report hashes stay `69ea6b8714f3cb6105222d636af3f17bd5c5caac7b290c3c3d87e4efaeedd0ef` and `38ada8bc32d8b19361cc974346d5972f6020eb0c32c2ca537abff4d17f66c7f0`. The development manifest stays `0e9b3c1af9dd573bf6e2034640e468e8ab9074e1e76c90cef1f39f68d607bc03`. The lexeme-structure note stays `529defbc2b56152c3290d5b09f309764128b035906797229dab54857cd249df0`. v3 through v7 sources and the v1 implementation are unchanged. - -These figures are development evidence. No accuracy threshold was applied. `measurement_sample_drawn` stays false. `measurement_eligible` stays false. `revision_eligible` stays false. `HLX-EXP-2026-09-27-SELECT-005` is not authorized. Admitted 0. Settled 0. Gold 0. The ledger was not appended. Events sha256 remains `96b74a92d44f1cf9fe152b18e5207176f161ba3bfce528dac38aa4571a742f9c`. The next named transition is `V2_DEVELOPMENT_RESULT_REVIEW`. This pass does not authorize it, and it does not authorize an unseen measurement. - -## Semantic evidence source v1 specified — 2026-09-28 - -The procedure-v2 development result is reviewed and frozen. WordNet 3.0 structural evidence supports referentiality, whole-expression lexicalization, and some positive compositionality. Under the frozen procedure it does not provide a deterministic structural signal for semantic noncompositionality at useful coverage. That is a source-capability limitation. Procedure v2 was not retuned. - -Observed on the 225 development rows: compositional NO 0, lexicalized noncompositional 0, high 0, ambiguous 182/225, whole-expression lexicalization 95, positive compositionality 43, referential 27. Descriptive reject precision remains 27/27. False reject is 0. False high is 0. No measurement bar was applied. Review sha256 `77ae2c0491def0b75cd4213cc23fdcb6f2eec18dc2d0641764a276a583ee537d`. Limitation sha256 `3c05cd9d6301fab0791e31b542d767cc757307cf3e304065362b479cc40e964a`. Status is `WORDNET_STRUCTURAL_SOURCE_LIMITATION_CONFIRMED`. - -`RUNE.UNBIND_SEMANTIC_EVIDENCE_SOURCE.v1` is `SPEC_FROZEN`. It is an evidence-source evaluation framework, not an unbind classifier. The research question is which additional source can establish semantic noncompositionality independently of WordNet whole-expression lexicalization, with provenance and precision enough to become eligible input to a future sense-first screen. No source is selected. Nothing was encoded or applied. No external resource was downloaded. The only local lexical source is WordNet 3.0, and that source is the one whose limitation was just confirmed. - -Five candidate families are specified and left unselected: explicit lexical-semantic resources, a deterministic comparison of the bound sense with a composition of its parts, a curated linguistic annotation, a model-based judgment, and a later hybrid of WordNet structure plus one independent semantic source. An LLM completion is not runtime truth. A source must pass provenance, target alignment, sense alignment, label independence, reproducibility, abstention, a ban on phrase exceptions, development evaluation before integration, and isolation of any later generalization rows. The future output can be YES, NO, or UNKNOWN. UNKNOWN stays valid. No hard accuracy target is frozen. A high unknown rate is acceptable when false semantic claims stay low. Reducing ambiguity is not a reason to select a source. - -The same 225 rows remain development evidence for a future candidate test. They were not mutated and no source was run on them. False high and false reject stay the safety priority for a later integration specification. That specification does not exist yet, and no unseen-measurement bar was registered. - -A shared primitive, `RUNE.SEMANTIC_COMPOSITIONALITY`, is architecture only. The phrase lane would eventually use constituent words, phrase structure, and the bound sense. The single-lexeme lane would use morphemes or compound constituents and the bound sense. Lexicalized identity, compositionality, and semantic shift remain different questions. `RUNE.LEXEME_STRUCTURE_SCREEN.v1` was not implemented. Its architecture note stays `529defbc2b56152c3290d5b09f309764128b035906797229dab54857cd249df0`. - -No JSON Schema document exists for this source-evaluation family. Hypothesis sha256 `39127a810d38ede96d7947c33dbc3e5491c9e1cc9b3f76b1064d9e0dd04a7787`. Acceptance sha256 `1252c8c20ce3f49fe61ed8aeeec3157df7f4185b3aa7c468938ff47342d81b94`. Evaluation plan sha256 `472b3819c050bbc9b1dd2eec3183cdb27c3659521408c321acb12b9c1b69dc8a`. Architecture sha256 `180b6721c4e19847516364f441ecc2101ed9a7758643889673dfdb7be6f41d36`. Tracker sha256 `521eccb8bd068d4697a3ffdc06e3b44feb4eec3c8a3bd6aebcea11dd288b7dfc`. - -Procedure v2 stays `3f4071640d0c9f29cf56f53969a88ec25c635444b87765e77e1b9158470e5662`, predictions `1f7fc03547d24de851326a4848d93f1dbef16714e74e3e9f86d8c8aa6f8aaa8a`, and report `93d8fb76da8aa7155fb0ce57b0841ca455eca3e904edf50b9f76e595dd095ca5`. Procedure v1, the sense-screen acceptance, and the 225-row manifest are unchanged. The encoded v1 screen stays `DEVELOPMENT_ANALYZED`. Procedure v2 stays `DEVELOPMENT_ANALYZED_V2`. `measurement_sample_drawn` stays false. `measurement_eligible` stays false. `revision_eligible` stays false. `HLX-EXP-2026-09-27-SELECT-005` is not authorized. Admitted 0. Settled 0. Gold 0. The ledger was not appended. Events sha256 remains `96b74a92d44f1cf9fe152b18e5207176f161ba3bfce528dac38aa4571a742f9c`. No procedure v3 was created. - -The next named transition is `CANDIDATE_SOURCE_EVALUATION_AUTHORIZATION`. This pass does not authorize it. Selecting or integrating a source is a later decision. - -## MAGPIE candidate evaluation — 2026-09-28 - -`RUNE.UNBIND_SEMANTIC_EVIDENCE_SOURCE.v1` is `CANDIDATE_SOURCE_EVALUATED` for one candidate, MAGPIE. The candidate remains unselected. `selected_source` is `none`. Runtime integration is false. The encoded sense screen and procedure v2 were not modified. - -The evaluated artifact is the author corpus `MAGPIE_unfiltered.jsonl` from `https://github.com/hslh/magpie-corpus` at commit `7fa677b82b9a772dfa54bbdd0fb414412d73db3b` (2020-06-07T09:57:12Z). The file was acquired from that commit on 2026-09-28T05:50:00Z. It contains 56,622 instances and 1,756 potentially idiomatic expression types. Assigned labels are idiomatic 40,011, literal 16,168, other 436, and unclear 7. The filtered split files were not acquired and were not mixed into the evaluation. No Hugging Face package was used. No other repository named magpie was used. - -Licenses are recorded per artifact. The LREC 2020 PDF, sha256 `8247c926909772ce317d5f33cddb83caa51969eb5ef928bcbadbbcf05e39c979`, states on page 1 that the ELRA proceedings text is licensed under CC-BY-NC. That statement is the publication license. The dataset LICENSE file in the pinned commit is Creative Commons Attribution 4.0 International, sha256 `05ab88f3f9da1d05f9c5bf0a7c45c49a9007f877dd9c237a5bf668276fe04c3b`. That file is also the only code license in the commit. The evaluated jsonl sha256 is `541ee535e93d71eff85351351665115e2a9f22ad736423881da5774a93bc880e`. Provenance sha256 `bf0dd1dd747a6423406d97393f375f99620894a20af6bd4758e2de738d5c82dd`. License receipt sha256 `8813818aa3704ba1e764121d2f66ff1630862a66d6c0c0b959b6afa35e0c3972`. - -Surface comparison is orthographic. Case and separator folding produced 25 exact matches. One normalized match folds a comma: `day in day out` corresponds to the MAGPIE type `day in, day out`. `to a t` casefolds onto `to a T` and is counted as exact. Recorded variant categories such as inflection, dashes, and possessive describe occurrences of a type. They are not alternate expression strings, so variant matches are 0. Ambiguous collisions are 0. Unmatched rows are 199. Surface coverage is 26/225. - -Sense alignment requires equality between a source sense identifier and the supplied WordNet synset. The pinned instances have no synset, sense key, or gloss field. Alignment coverage is 0/225. Every row is UNKNOWN. Semantic noncompositionality is YES 0, NO 0, UNKNOWN 225. Abstention is 225/225. Evidence codes are `magpie_surface_only` 26 and `magpie_no_match` 199. A surface hit does not assign YES. Fifteen matched rows have only idiomatic instance labels, including `throw in the towel` and `dressed to the nines`, and they stay UNKNOWN. Ten matched types contain both literal and idiomatic instances, including `round the bend` and `to the letter`. Those counts stay on the match record. They are not a MIXED sense alignment and they are not semantic YES. `as luck would have it` is an exact match with only idiomatic labels, and the historical operator bucket is secondary. The idiomatic labels were not converted into YES. - -Operator labels were joined after the semantic evidence file was written. They are not runtime evidence. The descriptive proxy, stated as a proxy, treats operator HIGH as the comparison class for semantic YES and operator SECONDARY as the comparison class for semantic NO. YES precision is `NOT_COMPUTABLE`. NO precision is `NOT_COMPUTABLE`. False YES is 0. False NO is 0. Both counts are zero because no YES or NO claim was emitted. Operator REJECT is its own row in the joint table: 77 unknown. It was not read as compositional NO or as semantic YES. The joint table is HIGH 101 unknown, SECONDARY 46 unknown, REJECT 77 unknown, and quarantine 1 unknown. - -Development status is `CANDIDATE_INSUFFICIENT`. Coverage of familiar phrases is not a reason to select the source. The readiness question, non-zero semantic YES with a defensible sense alignment, is not met. Match sha256 `84c847cfa545883de5a31979133fed87b0cdf9a7d13074c74cf227d9bfcadc83`. Sense-alignment sha256 `037b0f4d96d463aa7c5fbecdbef06a530ffbf770735232c92bd6abd0dd71fc66`. Semantic-evidence sha256 `89f7227e1098407c7aaae6d9876b1f5780dbfe5b6a2eb3c3b09357d57822b577`. Evaluation sha256 `74e2174d15c486dc60e9ad6be338199ff66a2d0950ed268105119323711108ba`. Decision sha256 `6eaa968b6260946998dba13e5c423f178d3349cdfe06e5ea401717f5a9bcdd0d`. Tracker sha256 `c3fe6f2fd21d07b8b45d9f26ffeebbcfbe3cb68a1f5a7952dee94d9a2c995936`. - -No JSON Schema document exists for this source-evaluation family. The schema names on the artifacts name those artifacts. Older unbind sample, label, and report schemas were not applied. Procedure v2 stays `3f4071640d0c9f29cf56f53969a88ec25c635444b87765e77e1b9158470e5662`. The 225-row manifest stays `0e9b3c1af9dd573bf6e2034640e468e8ab9074e1e76c90cef1f39f68d607bc03`. No sense class was written onto it. v3 through v7 sources are unchanged. The lexeme-structure note stays `529defbc2b56152c3290d5b09f309764128b035906797229dab54857cd249df0`. `measurement_sample_drawn` stays false. `measurement_eligible` stays false. `revision_eligible` stays false. `HLX-EXP-2026-09-27-SELECT-005` is not authorized. Admitted 0. Settled 0. Gold 0. The ledger was not appended. Events sha256 remains `96b74a92d44f1cf9fe152b18e5207176f161ba3bfce528dac38aa4571a742f9c`. - -The next named transition is `NEXT_CANDIDATE_SOURCE_EVALUATION_AUTHORIZATION`. This pass does not authorize it. A later authorization may name one curated resource whose entries carry a WordNet synset offset or sense key for the expression sense. PARSEME, STREUSLE, PIE, EPIE, and NCS were not downloaded and are not selected. - -## Korkontzelos–Manandhar candidate evaluation — 2026-09-28 - -`RUNE.UNBIND_SEMANTIC_EVIDENCE_SOURCE.v1` stays `CANDIDATE_SOURCE_EVALUATED`. A second candidate, the Korkontzelos–Manandhar 2009 compositionality set, was evaluated and not selected. `selected_source` remains `none`. Runtime integration remains false. MAGPIE stays `CANDIDATE_INSUFFICIENT`. Its artifacts were not modified. - -The pinned source is Table 1 of Korkontzelos and Manandhar, “Detecting Compositionality in Multi-Word Expressions,” ACL-IJCNLP 2009 short papers, pages 65–68, anthology `P09-2017`. Canonical URL `https://aclanthology.org/P09-2017/`. PDF sha256 `046da9fc26cfdf220e41ad914314f0703ea3c84cd86e045b20146b34189113d1`, acquired 2026-09-28T06:41:05Z. The paper states that the items were drawn from WordNet 3.0. The bibliography cites Miller 1995 for WordNet in general. That citation is not a second version number. Reconstruction used the local Princeton WordNet 3.0 data files and no other WordNet release. The suggested counts of about 60 compositional and 56 noncompositional items do not match this table. The verified inventory is 19 compositional and 19 noncompositional. A later 2010 sample by the same authors was not acquired. - -The PDF header reads `c©2009 ACL and AFNLP`. The ACL Anthology states that materials prior to 2016 are licensed under CC-BY-NC-SA 3.0 and that copies may be made for teaching and research. That is the publication license of the acquired PDF. No separate dataset license exists. The evaluation list is the table inside the paper. No code artifact was published with the table, and none was acquired. Bold, underline, and italic marks in the table report system detections. They were not read as gold labels. - -Table 1 preserves the surface and the section label. It does not preserve a synset offset, sense key, lemma key, part of speech, or gloss. Source sense identity is therefore absent on all 38 items. A lookup key folds case, underscore, and apostrophe shape, and it keeps hyphens. That key is not a sense choice. The key matches exactly one PWN 3.0 synset for 30 items: `EXACT_UNIQUE_RECONSTRUCTION`. Those 30 are the 19 compositional items and 11 noncompositional items. Eight noncompositional items match two synsets each: `black maria`, `dead end`, `dutch oven`, `goat's rue`, `green light`, `high jump`, `living rock`, and `prince Albert`. They are `AMBIGUOUS_MULTIPLE_SYNSETS`. No gloss, operator label, or frequency was used to pick one. `EXACT_SOURCE_ID` is 0. `NO_PWN3_MATCH` is 0. `VERSION_CONFLICT` is 0. On the inventory, the conservative map gives semantic YES 11, NO 19, and UNKNOWN 8. Only the unique reconstructions carry YES or NO. - -None of those 30 synsets occurs in the frozen 225 development rows. Surface keys also do not meet any development row. The join is synset identity, so the Hyperlex result is YES 0, NO 0, UNKNOWN 225. Sense-aligned coverage is 0/225. Abstention is 225/225. Evidence codes on the 225 rows are `km_no_match` 225. Operator labels were joined after the evidence file was written. Under the stated proxy, YES precision and NO precision are `NOT_COMPUTABLE`. False YES is 0. False NO is 0. The joint table is HIGH 101 unknown, SECONDARY 46 unknown, REJECT 77 unknown, and quarantine 1 unknown. Reject and quarantine were not read as compositionality. - diff --git a/.land-chunks/part-04 b/.land-chunks/part-04 deleted file mode 100644 index f65cd14b..00000000 --- a/.land-chunks/part-04 +++ /dev/null @@ -1,34 +0,0 @@ -Development status is `CANDIDATE_INSUFFICIENT`. The inventory can reconstruct monosemous PWN 3.0 identity, which MAGPIE could not, and that reconstruction still supplies no YES row on this development set. The stop-condition finding `WORDNET_DERIVED_BUT_SENSE_IDENTITY_NOT_PRESERVED` was not frozen. Eight of 38 items are polysemous, not most of them, and 30 items do reconstruct one synset. The development sample and this table are disjoint. MAGPIE remains surface coverage 26/225, sense-aligned coverage 0/225, YES 0, NO 0, abstention 225/225. This candidate is surface coverage 0/225, sense-aligned coverage 0/225, YES 0, NO 0, abstention 225/225. Coverage is not the comparison. Neither candidate puts a sense-aligned YES on a development row. - -Provenance sha256 `88fbfa077d2394b8ce631ec700c482ad98a0f62f0ab06964f4f43a77d906f9aa`. License receipt sha256 `eb4c9406aab7f9021d346ebd24634cad1dcf0e6076f069e73b1d4b2702dbe2f8`. Inventory sha256 `3a35ddc0b2c04a5386c6112a2bb3cdf22735edbe2fd791f0c2ec542fe9184d81`. PWN 3.0 alignment sha256 `531d13cf1bdbc939fc11d9ef5864c6878a9f58210368c596c0aad08ff75e61c9`. Hyperlex semantic evidence sha256 `4e98f8f06713ffcf02549305aef140e92b9b8b2790f471f3ce1f41fe208b2f7a`. Evaluation sha256 `00a1d1f667635354e20e5002c4ece846fe3a8125a7ca12ebe09bb7e28dedd1a7`. Comparison sha256 `240b3ea468d22a80ac5e5765521cc691b80f914b7baff6bb1ae4819475f54965`. Decision sha256 `93a07e3c78b53a69965497410c34ddb52c2a5d3add3fb2f3cd3fd9ca84eb3d9f`. Tracker sha256 `b3546102410058d3596c4563604998685753a698b2bc533b353e1f039440f704`. - -No JSON Schema document exists for this source-evaluation family. Procedure v2 stays `3f4071640d0c9f29cf56f53969a88ec25c635444b87765e77e1b9158470e5662`. The 225-row manifest stays `0e9b3c1af9dd573bf6e2034640e468e8ab9074e1e76c90cef1f39f68d607bc03`. v3 through v7, the lexeme-structure note, and the WordNet source-limitation finding are unchanged. `measurement_sample_drawn` stays false. `measurement_eligible` stays false. `revision_eligible` stays false. `HLX-EXP-2026-09-27-SELECT-005` is not authorized. Admitted 0. Settled 0. Gold 0. The ledger was not appended. Events sha256 remains `96b74a92d44f1cf9fe152b18e5207176f161ba3bfce528dac38aa4571a742f9c`. - -The next named transition is `NEXT_CANDIDATE_SOURCE_EVALUATION_AUTHORIZATION`. This pass does not authorize it. A later candidate has to carry a PWN synset identity that can meet the development rows. Surface-only MWE corpora are not the next step. `CANDIDATE_SOURCE_SELECTION_REVIEW` is not the next step, because this candidate is not promising and is not selected. - -## Semantic compositionality residual candidate evaluation — 2026-09-28 - -`NEXT_CANDIDATE_SOURCE_EVALUATION_AUTHORIZATION` applies only to `RUNE.SEMANTIC_COMPOSITIONALITY_RESIDUAL.v1`. The candidate is an evaluation of a continuous residual. It is not selected, it is not integrated into `UNBIND_SENSE_SCREEN`, and it does not create procedure v3. Procedure v2 stays `3f4071640d0c9f29cf56f53969a88ec25c635444b87765e77e1b9158470e5662`. No unseen measurement sample is drawn. No training gold is created. No row is admitted or settled. `HLX-EXP-2026-09-27-SELECT-005` is not authorized. MAGPIE and the 2009 Korkontzelos–Manandhar artifacts stay frozen, and both remain `CANDIDATE_INSUFFICIENT`. - -The design was hashed before any development row was encoded. The whole sense is the supplied surface, its POS, and the frozen gloss, in the form `{surface} ({pos}): {gloss}`. A constituent uses the same form on the resolved PWN 3.0 lemma, POS, and first gloss clause. Whitespace tokenization keeps a hyphen inside one token. A frozen closed class of determiners, prepositions, pronouns, auxiliaries, and other structural tokens is ignored. A row needs two content tokens. Sense resolution accepts one derivation or pertainym pointer whose target word number is nonzero and whose lemma matches the constituent or a one-hop exception neighbor. That result is `EXACT`. With no exact target, one lexical synset across noun, verb, adjective, and adverb is `UNIQUE`. Several targets are `AMBIGUOUS`. None is `UNRESOLVED`. A target word number of zero does not name a lemma. The source word number is not a filter. An ambiguous or unresolved content constituent makes the row `UNKNOWN`. The pass does not use an arbitrary first sense, gloss similarity, an embedding to choose a sense, an operator label, or a manual sense choice. Composition is one operator, `normalized_mean_v1`: binary64 L2-normalize each constituent vector, take the mean, and L2-normalize the mean. Duplicate synsets stay, one vector per content token. The residual is `1 - cosine`, recorded with `format(value, '.10f')` and no clamp. A vector hash is sha256 of little-endian binary32 bytes. No semantic-noncompositionality threshold is chosen, and the residual is not turned into YES or NO. - -The pinned model is `sentence-transformers/all-MiniLM-L6-v2`, revision `1110a243fdf4706b3f48f1d95db1a4f5529b4d41`, acquired locally at `2026-09-28T06:52:00Z` from the Hugging Face revision. The model license is Apache-2.0. Weights sha256 `53aa51172d142c89d9012cce15ae4d6cc0ca6895895114379cacb4fab128d9db`. `tokenizer.json` sha256 `be50c3628f2bf5bb5e3a7f17b1f74611b2561a3a27eeab05e5aa30f411572037`. `vocab.txt` sha256 `07eced375cec144d27c900241f3e339478dec958f92fddbc551f295c992038a3`. Pooling is mean tokens into 384 dimensions, and the stack includes a normalize module. Encode uses CPU, float32, `normalize_embeddings=True`, batch size 1, seed 0, one thread, and eval mode. `use_deterministic_algorithms` stays false. The effective maximum sequence length is 256; overflow would abstain rather than truncate, and no row overflowed. Runtime versions are Python 3.12.3, torch `2.14.0+cpu` (Apache-2.0 with additional component licenses), sentence-transformers 6.1.0 (Apache-2.0), transformers 5.17.0 (Apache 2.0), tokenizers 0.23.2 (Apache), and NumPy 2.5.3 (BSD-3-Clause and other component licenses). ONNX, OpenVINO, and TensorFlow weight copies were not acquired. No API embedding service was called. Constituent glosses come from Princeton WordNet 3.0, whose license is separate from the model license. The encoder was run twice and the float32 hashes matched before the score file was written. - -Constituent extraction is `EXTRACTED` 166 and `UNKNOWN` 59. The 59 are rows with fewer than two content tokens. Across content constituents the resolution counts are `EXACT` 4, `UNIQUE` 60, `AMBIGUOUS` 370, and `UNRESOLVED` 70. Row abstentions are ambiguous content 145, fewer than two content tokens 59, and unresolved content 18. Three rows are `SCORED` and 222 are `UNKNOWN`. Ten representation texts were encoded. Six content tokens contain a character outside letters, digits, hyphen, and apostrophe; the extractor did not strip them. The three scored rows are operator `REJECT`: `california fern` residual `0.2139784896`, `monoamine oxidase inhibitor` residual `0.1768095281`, and `.22 caliber` residual `0.1309395496`. Reject is a referential axis, not semantic no. Their descriptive distribution is count 3, min `0.1309395496`, p25 `0.1538745388`, median `0.1768095281`, p75 `0.1953940088`, max `0.2139784896`, mean `0.1739091891`. Operator `HIGH` has 0 scored rows of 101, so the primary comparison is `NOT_COMPUTABLE`. `SECONDARY` has 0 of 46. `QUARANTINE` has 0 of 1. No threshold was fit to these labels. - -Development status is `CANDIDATE_DISTRIBUTION_FROZEN` because the preregistered rule uses that status whenever the scored count is greater than zero. The status freezes the score distribution. It does not say the residual separates noncompositionality, and the empty HIGH distribution supplies no such comparison. `selected_source` remains `none`. Candidate spec sha256 `39c2914e32557ffe1a456a56f8742ea4fe8f1aaec1dc1da451656cd22f0db32d`. Provenance sha256 `2c34a7d0d480cde564bda694dbaa349550814c5fb7c647bfa3bbbc9db5e26886`. License receipt sha256 `323a5bad21ac74d6a1fd94c54dba95a0046b633cd7328dd6680972525abc0909`. Score artifact sha256 `cea638679faeee1bc1c689823e7c0c08562c4d7ef1f8230bbbf4079239e7c3e7`. Evaluation sha256 `ba782622d4c68d23c53ae0ffb5f54f1e43c8cf059b44b7adb34e0eb356ef3896`. Decision sha256 `45922eba294b0b7d7238ce71c6157641259ea292f65743ba2ee1324ff9b52617`. Tracker sha256 `14d4daaab1e4740a596acaef7f4dbd9ed11edaf2182ff22f7aa339325febf591`. Every score row carries the spec hash, and the spec contains no residual. Operator labels were joined only after the score file was hashed. - -No JSON Schema document exists for this source-evaluation family. The 225-row manifest stays `0e9b3c1af9dd573bf6e2034640e468e8ab9074e1e76c90cef1f39f68d607bc03`. Sense classes on those rows stay null. v3 through v7, the lexeme-structure note, and the WordNet source-limitation finding are unchanged. `measurement_sample_drawn` stays false. `measurement_eligible` stays false. `revision_eligible` stays false. Admitted 0. Settled 0. Gold 0. The ledger was not appended. Events sha256 remains `96b74a92d44f1cf9fe152b18e5207176f161ba3bfce528dac38aa4571a742f9c`. - -The next named transition is `RESIDUAL_THRESHOLD_FREEZE_AUTHORIZATION`. This pass does not authorize it. A threshold is not supported by a HIGH distribution, because no HIGH row was scored. `CANDIDATE_SOURCE_SELECTION_REVIEW` is not the next step. `selected_source` remains `none`. - -## Residual v1 coverage limitation — 2026-09-28 - -`RESIDUAL_DEVELOPMENT_RESULT_REVIEW` is authorized for the frozen residual distribution only. `RESIDUAL_THRESHOLD_FREEZE_AUTHORIZATION` is not authorized. The residual has not been falsified. The experiment could not test HIGH against SECONDARY because constituent sense ambiguity collapsed coverage. Scored rows are 3, all operator REJECT. Unknown rows are 222. HIGH scored 0 of 101. SECONDARY scored 0 of 46. REJECT scored 3 of 77. Ambiguous content abstains 145 rows, fewer than two content tokens abstain 59, and unresolved content abstains 18. `threshold_eligible` is false. `source_selection_eligible` is false. The primary limitation is `constituent_sense_resolution`. The finding is `SEMANTIC_RESIDUAL_V1_COVERAGE_LIMITATION_CONFIRMED`: the dominant blocker is deterministic constituent sense resolution, not the residual model. The model, revision, normalized mean, and `1 - cosine` residual stay frozen. No residual score was recomputed. - -## Constituent sense resolution v1 — 2026-09-28 - -`RUNE.CONSTITUENT_SENSE_RESOLUTION.v1` starts from that limitation. The question is whether a frozen deterministic resolver can give enough content constituents a PWN 3.0 sense to make a later residual replay testable. Operator labels, residual scores, unbind buckets, phrase exceptions, manual choices, an LLM, and the residual embedding model are not inputs. The specification and procedure were hashed before the replay. The resolver then ran twice from that specification. The two artifacts matched, so determinism is `IDENTICAL`. - -Tier 1 is structural. A lexical pointer in the frozen set `! + \ ^ * & < $` whose target word number is nonzero and whose lemma matches the constituent, or one exception hop, is structural evidence. One such synset is `EXACT` by `STRUCTURAL_EXACT`. More than one is `AMBIGUOUS` and Extended Lesk does not override it. With no structural target, one lemma synset across noun, verb, adjective, and adverb is `EXACT` by `UNIQUE_LEMMA`. No synset is `UNRESOLVED`. The extra pointer symbols did not raise structural exact above the residual baseline: structural exact stays 4 and unique lemma stays 60. - diff --git a/.land-chunks/part-05 b/.land-chunks/part-05 deleted file mode 100644 index cb18e8e6..00000000 --- a/.land-chunks/part-05 +++ /dev/null @@ -1,31 +0,0 @@ -Extended Lesk v1 runs only when several lemma synsets remain. Candidate text is the full PWN 3.0 gloss, including quoted examples, plus the first gloss clause of each depth-1 synset reached by `@ + \ & ^ =`. Context is the parent frozen gloss plus the first gloss clause of other constituents in the row that tier 1 already resolved. Lesk output is not reused as context. Normalization casefolds, folds apostrophes, and splits on characters outside letters, digits, apostrophe, and hyphen. Stopwords are the residual v1 structural class. Tokens shorter than two characters are dropped. There is no stemmer and no lemmatizer. The score is the sum of squares of greedy longest contiguous overlaps, and matched tokens are not reused. Scores are integers. The minimum margin is 1, so a tie, including a zero-overlap tie, is `AMBIGUOUS`. A strict win is `RESOLVED`. The parent gloss is local context only. Parent sense is not constituent sense. - -Every one of the 504 content constituents was attempted. Status counts are `EXACT` 64, `RESOLVED` 121, `AMBIGUOUS` 249, and `UNRESOLVED` 70. Against the residual baseline of `EXACT` 4, `UNIQUE` 60, `AMBIGUOUS` 370, and `UNRESOLVED` 70, the 60 unique lemmas are the same senses under the new `EXACT` label, unresolved stays 70, and 121 of the 370 ambiguous constituents become `RESOLVED`. The other 249 stay `AMBIGUOUS`. The tie rate on Lesk attempts is 249/370. Exact proportion is 64/504. Context-resolved proportion is 121/504. Unresolved rate is 70/504. Abstention, ambiguous plus unresolved, is 319/504. There is no constituent-sense gold, so these figures are coverage, determinism, tie behavior, and provenance. They are not accuracy. - -A row is `RESIDUAL_READY` only when it has at least two content constituents and every one is `EXACT` or `RESOLVED`. That holds for 20 rows. The other 205 are `UNKNOWN`. After the resolution file was hashed, operator labels give HIGH 4 ready and 97 unknown, SECONDARY 4 ready and 42 unknown, REJECT 12 ready and 65 unknown, and quarantine 0 ready and 1 unknown. The necessary condition, at least one ready HIGH row and one ready SECONDARY row, is met. It is not sufficient. Because 249 of 370 baseline-ambiguous constituents remain AMBIGUOUS, which is more than half, the preregistered finding is `CONSTITUENT_WSD_COVERAGE_INSUFFICIENT`. No second resolver was added. A projection, not a replay, says 20 rows could enter a later residual pass: HIGH 4, SECONDARY 4, REJECT 12. The residual was not rerun. - -Resolver state is `DEVELOPMENT_ANALYZED`. Candidate status is `COVERAGE_INSUFFICIENT`. `selected_source` remains `none`. Residual state remains `CANDIDATE_DISTRIBUTION_FROZEN` with coverage limitation `CONFIRMED`. Limitation sha256 `fc8839c15a7638b2bfca1cf0548bfb4d5f433434bae0fea2944a528dd15d6142`. Review sha256 `1c1b69856dd88567167fd5c958cd8db6d39ab9ec74a4e0ed3e667a521c82e6fa`. Resolver spec sha256 `176e6219ddc3127814a25d39ad26e3571817f7ea8323d685e081d2e0fd867acb`. Procedure sha256 `9f76b64aa6aac09bd56ca9cc8a847cda12b31a58eea8c4b51426733917d54248`. Replay sha256 `a0c707ab55e02f627a698c33ddc0ca398e34aa0bafc19b13e72422bc26d97d0a`. Analysis sha256 `6d47610014f74094394355a50419fe24441103bec09458b6f25ea392fb57151c`. Decision sha256 `ff8b90ebe5d48151dc68ddbf676e1f27d4cee5e3be2730f999c9b089f1392caa`. Tracker sha256 `c72e55c8096143b8675d4aa995c5d4b19de95d256dd234d32a421ba098bc7d4f`. - -No JSON Schema document exists for this source-evaluation family. No residual threshold was created. No semantic YES or NO was emitted. No unseen sample was drawn. `measurement_sample_drawn` and `measurement_eligible` stay false. SELECT-005 stays unauthorized. Admitted 0. Settled 0. Gold 0. The 225-row manifest, procedure v2, MAGPIE, Korkontzelos–Manandhar, and the residual v1 artifacts are unchanged. The ledger was not appended. Events sha256 remains `96b74a92d44f1cf9fe152b18e5207176f161ba3bfce528dac38aa4571a742f9c`. - -The next named transition is `MODEL_BASED_WSD_CANDIDATE_EVALUATION_AUTHORIZATION`. This pass does not authorize it. `RESIDUAL_REPLAY_WITH_RESOLVED_SENSES_AUTHORIZATION` is not the next step while the constituent-coverage finding stands. `selected_source` remains `none`. - -## Model-based constituent WSD candidate v1 — 2026-09-28 - -`MODEL_BASED_WSD_CANDIDATE_EVALUATION_AUTHORIZATION` evaluates one WordNet-native model on the 249 constituents that Extended Lesk v1 left `AMBIGUOUS`. The model is `kanishka/GlossBERT`, family GlossBERT, canonical source `https://huggingface.co/kanishka/GlossBERT`, revision `0cc3b83af5496e27ebcc95ef0cf37ea0a9281a7a`. It is a BERT sequence classifier fine-tuned on SemCor 3.0. Each decision is a score over a supplied Princeton WordNet 3.0 gloss, so the output is a candidate synset or an abstention. The card license is MIT. The checkpoint is a third-party Hugging Face upload, not the authors' Google Drive file. The original code repository `https://github.com/HSLCY/GlossBERT` is MIT. Inference is local. Device is CPU. Dtype is float32. Batch size is 1. One thread is used and mkldnn is disabled. Runtime is Python 3.12.3, torch 2.14.0+cpu, transformers 5.17.0, tokenizers 0.23.2, and numpy 2.5.3. The positive class is index 1. A non-Hyperlex polarity control fixed that index before any development constituent was scored: a financial sentence selects `noun:08420278`, and a river sentence selects `noun:09213565`. - -The candidate specification was hashed before those 249 constituents were scored. Spec sha256 `c861ff7fff11ae6a790531267229c18d6e6e0a171a9bf6c34cfb6f7e7b14498c`. Weight sha256 `60706c7618f8ccbfa7d0a6d1d1009765a7146ea5f4232924ed9f1c46d521c898`. Vocab sha256 `07eced375cec144d27c900241f3e339478dec958f92fddbc551f295c992038a3`. Tokenizer config sha256 `09e49d0e788d25991da77d37b10eaa6a86a4e94e2127de8bedc94eb45baf2d84`. Each constituent is scored only among its frozen PWN 3.0 candidate synsets. The context is the parent surface with the target content token in double quotes. The paired text is the matched lemma, a colon, and the first gloss clause. The parent gloss is not appended. Operator labels, residual scores, and the residual embedding are not inputs. The score is the class-1 softmax probability, quantized to six decimal places, half even. The abstention rule, frozen before scoring, requires a top probability of at least 0.50 and a top-versus-second margin of at least 0.10. An equal top score stays `AMBIGUOUS`. A sense outside the candidate set would be `INVALID`. The same inference then ran a second time. The two raw artifacts matched, so determinism is `IDENTICAL`. - -Tier 3 attempted 249 constituents. It resolved 153, left 96 ambiguous, and produced 0 invalid outputs and 0 errors. The 153 resolutions use evidence `model_margin`. The 96 abstentions use evidence `model_abstention`. Exact score ties are 0. Top-score bins are 81 below 0.50, 30 from 0.50 to 0.60, 35 from 0.60 to 0.70, 26 from 0.70 to 0.80, 32 from 0.80 to 0.90, and 45 from 0.90 to 1.00. Margin bins are 50 below 0.10, 56 from 0.10 to 0.25, 56 from 0.25 to 0.50, and 87 at 0.50 or more. There is no constituent-sense gold on these rows, so the figures are coverage, validity, determinism, confidence, and abstention. They are not accuracy, precision, recall, or F1. - -Combined with the frozen earlier tiers, constituent counts are `EXACT` 64, `LESK_RESOLVED` 121, `MODEL_RESOLVED` 153, `AMBIGUOUS` 96, and `UNRESOLVED` 70. Tier 3 did not replace a structural exact, a unique lemma, or an Extended Lesk decision that had already cleared its margin. A row is projected `RESIDUAL_READY` only when it has at least two content constituents and every one is exact, Lesk-resolved, or model-resolved. That projection was hashed before operator labels were joined. Projected ready rows are 73. Unknown rows are 152. After the join, HIGH is 28 ready and 73 unknown, SECONDARY is 11 ready and 35 unknown, REJECT is 34 ready and 43 unknown, and quarantine is 0 ready and 1 unknown. Against the lexical baseline of HIGH 4, SECONDARY 4, and total 20, the deltas are +24, +7, and +53. Invalid outputs are 0 and the repeat is identical, so the preregistered coverage gate returns `CANDIDATE_PROMISING`. The gate is a readiness projection. It does not say the selected senses are correct, and it does not replay the residual. - -`selected_source` remains `none`. The model is not integrated into runtime. `RUNE.CONSTITUENT_SENSE_RESOLUTION.v1` stays `DEVELOPMENT_ANALYZED` with lexical status `COVERAGE_INSUFFICIENT`. `RUNE.SEMANTIC_COMPOSITIONALITY_RESIDUAL.v1` stays `CANDIDATE_DISTRIBUTION_FROZEN`. `threshold_eligible` stays false. No residual score was recomputed and no residual threshold was created. No semantic YES or NO was emitted. No unseen sample was drawn. `measurement_sample_drawn` and `measurement_eligible` stay false. SELECT-005 stays unauthorized. Admitted 0. Settled 0. Gold 0. The resolver artifacts, residual artifacts, MAGPIE artifacts, Korkontzelos–Manandhar artifacts, procedure v2, and the 225-row manifest are unchanged. The ledger was not appended. Events sha256 remains `96b74a92d44f1cf9fe152b18e5207176f161ba3bfce528dac38aa4571a742f9c`. - -No JSON Schema document exists for this source-evaluation family. Provenance sha256 `6d91283244a88f44477c836e6549118d5d96a36bb878bf448fcadb13ff765e11`. License receipt sha256 `67d45243959e3e76643a675d49b508a73d0f7f0bf8fbf060ce7b83c9200c621b`. Raw output sha256 `a0e508c225e6db4cdbcae701682202b1d854f3762546dbfe10b84a15e0e9e17c`. Resolution sha256 `ed945989cf4947ac84633ba2c4aa10c1ba381d2396da0b573a844f83ec367a18`. Readiness projection sha256 `c75834faf4a84d36e83246244e0aa7c6c7788c3a57cfdb7f77c7628a52023328`. Analysis sha256 `8ca0d8c8dd7e510a04daef6b3fbd78ace6d20b173e88d2a3d0c2e10fdaafe30b`. Decision sha256 `c8ed0b8acaaa415277c5f9bdbf982d075136b795b5d95fd8813d5a3010072dbb`. Tracker sha256 `6da1e9730c785d2784d23433455515989d312ed8c76f4763b3a2dd6a1f2c4b2d`. - -The next named transition is `RESIDUAL_REPLAY_WITH_MODEL_RESOLVED_SENSES_AUTHORIZATION`. This pass does not authorize it. The residual is not replayed. `selected_source` remains `none`. -## Residual replay with model-resolved senses — 2026-09-28 - -`RESIDUAL_REPLAY_WITH_MODEL_RESOLVED_SENSES_AUTHORIZATION` runs one residual replay on the frozen Tier 1, Tier 2, and Tier 3 constituent senses. The question is whether operator-HIGH rows then show larger frozen semantic-compositionality residuals than operator-SECONDARY rows. This is a development distribution. It is not a classifier, and it does not train a composition function. The residual model stays `sentence-transformers/all-MiniLM-L6-v2` at revision `1110a243fdf4706b3f48f1d95db1a4f5529b4d41`. Settings stay CPU, float32, seed 0, batch size 1, one thread, eval mode, normalized embeddings, and maximum sequence length 256. Composition stays `normalized_mean_v1`. The residual stays `1 - cosine_similarity(whole_sense_vector, normalized_mean(constituent_sense_vectors))`, stored at 10 decimal places. Constituent extraction, structural exact, unique lemma, Extended Lesk v1, and GlossBERT are not retuned. No confidence or margin rule changes. No second embedding, pooling, distance, or weight scheme is tried. - diff --git a/.land-chunks/part-06 b/.land-chunks/part-06 deleted file mode 100644 index cad3b8d1..00000000 --- a/.land-chunks/part-06 +++ /dev/null @@ -1,29 +0,0 @@ -The integrated resolution is built from the frozen resolver replay and the frozen GlossBERT resolution, then checked against the frozen readiness projection before any vector is computed. The projection reproduces: 73 `RESIDUAL_READY` rows and 152 `UNKNOWN` rows. The integrated file is hashed before scoring. Integrated sha256 `0f5dafc3676a4071ce8c889e58958b90203589aa3e91d78111b4e3292bdd87fb`. Only ready rows are scored. Representation text, lemma choice, gloss fields, example inclusion, normalization, and the residual formula are the frozen residual v1 functions. The encoder then runs twice. Row readiness, representation texts, vector hashes, and 10-decimal scores match, so determinism is `IDENTICAL`. No ready row exceeds 256 tokens. Scored rows are 73 and scoring abstentions are 0. The score file is hashed before operator labels are read. The receipt records that sequence with `operator_labels_joined` false. Score sha256 `16c0a9eaa918ac4a6e8223cafcbf4b1918cb212769063e262cf29a279f1048f6`. Receipt sha256 `675b1b8b8f1b1e6f19c7d320e7b8fe516eae92b60e966afbce407c1f0482e736`. - -After that hash, the historical operator labels join. Ready counts are HIGH 28, SECONDARY 11, REJECT 34, and quarantine 0. REJECT stays out of the primary comparison. HIGH residuals have count 28, minimum 0.1169573790, p10 0.2930005820, p25 0.3564342144, median 0.4040327275, p75 0.4404865614, p90 0.5184032344, maximum 0.5908804826, mean 0.3943616308, and sample standard deviation 0.1017789927. SECONDARY residuals have count 11, minimum 0.1768283745, p10 0.2079496807, p25 0.2436642596, median 0.3307873412, p75 0.4000929338, p90 0.4561366947, maximum 0.5587792172, mean 0.3299980313, and sample standard deviation 0.1186719529. The HIGH mean exceeds the SECONDARY mean by 0.0643635995. The HIGH median exceeds the SECONDARY median by 0.0732453863. Mann-Whitney U for HIGH, with half credit for ties, is 207. There are 207 pairs in which the HIGH residual is larger, 101 in which it is smaller, and 0 ties, out of 308 pairs. The rank-biserial correlation is 0.344156. Descriptive ROC AUC, with HIGH as the positive class and a larger residual as the HIGH-like score, is 0.672078. The hypothesized direction `HIGH residual > SECONDARY residual` is `SUPPORTED_DIRECTION` on these point estimates. That direction is not a semantic YES or NO. - -Uncertainty uses the bootstrap frozen before the label join: seed 0 and 10000 resamples, with interpolated 2.5 and 97.5 percentiles. The AUC interval is 0.451299 to 0.870130. The mean-difference interval is -0.0155777495 to 0.1373246447. The median-difference interval is -0.0445612032 to 0.1742972287. Each interval includes a null or reversed value. The preregistered status rule does not require the interval to exclude the null, and no stronger AUC floor is added after seeing the scores. - -Tier 3 dependence, among ready rows, is HIGH with Tier 3: 24, HIGH without Tier 3: 4, SECONDARY with Tier 3: 7, and SECONDARY without Tier 3: 4. The no-Tier-3 group has HIGH n=4 and SECONDARY n=4, median difference 0.1435511609, rank-biserial 0.500000, and AUC 0.750000, direction `SUPPORTED_DIRECTION`. The Tier-3 group has HIGH n=24 and SECONDARY n=7, median difference 0.0595389352, rank-biserial 0.083333, and AUC 0.541667, also `SUPPORTED_DIRECTION`. The gap is not concentrated in the GlossBERT subgroup. The within-Tier-3 separation is small. Dropping the largest HIGH residual and the smallest SECONDARY residual does not remove the overall directional result, so the comparison is not extreme-driven. HIGH and SECONDARY are not confined to disjoint parts of speech. The only part of speech with at least three rows in each class is adverb: HIGH 5 and SECONDARY 6, median difference 0.0797654836, AUC 0.733333. Adjective, noun, and verb cells are `NOT_COMPUTABLE`. Whitespace token count has HIGH median 4 and SECONDARY median 3, with Spearman against the residual of 0.092940 for HIGH and 0.603202 for SECONDARY. Character length has HIGH median 17 and SECONDARY median 12, with Spearman -0.059115 and 0.165145. Content-constituent count is 2 for every SECONDARY row and for almost every HIGH row. Maximum candidate-synset count has Spearman 0.390077 for HIGH and 0.073060 for SECONDARY. Where GlossBERT supplies a constituent, minimum confidence and minimum margin have Spearman near 0 for HIGH. These diagnostics do not retune the residual. - -REJECT is reported separately and is not semantic compositionality NO. REJECT count is 34, minimum 0.1036592522, p10 0.1240175180, p25 0.1569692084, median 0.2174387191, p75 0.2995541264, p90 0.3474576871, maximum 0.5180572821, mean 0.2343664094, and sample standard deviation 0.0963240560. The largest HIGH residual is `on the other hand` (`adv:00119578`, row `1d19d96bfedbade746610820599e28166b4ef6c291fd4d186b95055e0cf22074`) at 0.5908804826, tiers GlossBERT then Extended Lesk. The smallest HIGH residual is `naked as a jaybird` (`adj:00458266`, row `d479d3da8dd2b87a7b30c9710abefd1e87f2706a7ac032dde17fc337bef8ac4c`) at 0.1169573790, tiers Extended Lesk then unique lemma. The largest SECONDARY residual is `at one time` (`adv:00153261`, row `0f1c15024e09410a3336e3910351d2cfe6143b0a98b88ab561b8dedcbe64a5fc`) at 0.5587792172, both constituents GlossBERT. The smallest SECONDARY residual is `sneak thief` (`noun:10616204`, row `00152611a0b327fd51c12b1c9a80ab838874fb68692c7e988e7970e41557c9bb`) at 0.1768283745, tiers Extended Lesk then unique lemma. The largest REJECT residual is `three times` (`adv:00476680`, row `0e17c819392a00600e3089202fef9b2e74036b73b6429724dfe91d14f7334b2b`) at 0.5180572821, both Extended Lesk. The smallest REJECT residual is `family ascaphidae` (`noun:01644542`, row `00181b47ec5143e4625569c7b41909401ccb940250f8138c775be0000ec3ff93`) at 0.1036592522, tiers GlossBERT then unique lemma. These rows are descriptive. They do not become phrase rules. - -All six preregistered conditions hold: readiness reproduces, the replay is deterministic, the HIGH median is larger, the rank separation is positive, AUC is above one half, and the separation is not solely one preregistered confound. Residual development state is `RESIDUAL_DEVELOPMENT_ANALYZED_V2`. Candidate status is `CANDIDATE_PROMISING`. `threshold_eligible` stays false because these 225 rows remain a reused development surface. No threshold is frozen. No row receives `semantic_noncompositional` YES or NO. The original residual state stays `CANDIDATE_DISTRIBUTION_FROZEN`. `MODEL_BASED_WSD_CANDIDATE_V1` stays `CANDIDATE_PROMISING`. `RUNE.CONSTITUENT_SENSE_RESOLUTION.v1` stays `DEVELOPMENT_ANALYZED` with lexical status `COVERAGE_INSUFFICIENT`. `selected_source` remains `none`. There is no runtime integration. `measurement_sample_drawn` and `measurement_eligible` stay false. SELECT-005 stays unauthorized. Admitted 0. Settled 0. Gold 0. - -No JSON Schema document exists for this replay family. Original residual spec sha256 remains `39c2914e32557ffe1a456a56f8742ea4fe8f1aaec1dc1da451656cd22f0db32d`. Original residual scores sha256 remains `cea638679faeee1bc1c689823e7c0c08562c4d7ef1f8230bbbf4079239e7c3e7`. Analysis sha256 `4367930648a68c2f84a1fd8e011fa07d9f3bf111303079f3ec07688f7d5425fb`. Confound analysis sha256 `d0f2496ca7623050eb5519969abcf7c5e2d0e23e0c1961859c40cae4dcdb7021`. Decision sha256 `ed0296fe6888e7c9fe864a2c6c7ab6cecbd4f490d6b7d650e442dafc0bd0976d`. Tracker sha256 `6500394d24543d1797eb9a2a05ba31868e035ffcbc7ae568f53116d4b016e62a`. The 225-row manifest, procedure v2, GlossBERT artifacts, resolver artifacts, MAGPIE artifacts, and Korkontzelos–Manandhar artifacts are unchanged. The ledger was not appended. Events sha256 remains `96b74a92d44f1cf9fe152b18e5207176f161ba3bfce528dac38aa4571a742f9c`. - -The next named transition is `RESIDUAL_THRESHOLD_PREREGISTRATION_AUTHORIZATION`. This pass does not authorize it. A threshold still requires a later preregistered stage. `selected_source` remains `none`. -## Residual threshold preregistration — 2026-09-28 - -`RESIDUAL_THRESHOLD_PREREGISTRATION_AUTHORIZATION` freezes the rules for a later one-sided threshold. It does not choose `T_HIGH`. The development residual track stays `RESIDUAL_DEVELOPMENT_ANALYZED_V2` with candidate status `CANDIDATE_PROMISING`. A new track, `RUNE.SEMANTIC_COMPOSITIONALITY_THRESHOLD.v1`, starts at `THRESHOLD_PROCEDURE_PREREGISTERED`. Its threshold value is null. No calibration surface was drawn. No measurement surface was drawn. `threshold_frozen` is false. - -The frozen development evidence says a larger residual is a candidate for semantic noncompositionality. It does not say a smaller residual is semantic compositionality. The future evidence contract is one-sided. A ready row with residual greater than or equal to `T_HIGH` is `YES`. Every other row is `UNKNOWN`. There is no `NO` output and no lower threshold. `REJECT` is not semantic no. `QUARANTINE` is not semantic no. - -The 225 reviewed positional rows are development-only. `development_rows_reusable_for_threshold_selection` is false. That exclusion covers every partition in the development evidence artifact, including the historical v3 through v7 rows. Calibration and measurement, when a later pass is authorized to draw them, come from the same positional-unbind universe and keep the reserve's existing rights and provenance requirements. They must be disjoint from development and from each other. The identity fence is the one Hyperlex already uses, `hyperlexical.holdout_guard.normalized_text_sha256`, which is also the development `row_id`. The normalized surface text is fenced with it. A concrete PWN 3.0 synset key, `synset_pos:synset_offset`, cannot appear on two of the three surfaces. That synset exclusion is justified here, before any draw: the residual is a property of one synset's resolved constituents, so a repeated synset is not an independent observation. A missing offset does not match another missing offset. - -Only `RESIDUAL_READY` rows may enter threshold selection, under the frozen Tier 1, Tier 2, and Tier 3 stack. Unresolved rows stay `UNKNOWN` and do not move `T_HIGH`. WSD confidence and margin are not retuned to raise coverage. Operator `HIGH` is the positive class and operator `SECONDARY` is the negative class. `REJECT` and `QUARANTINE` stay out of the precision denominator. They are still hard safety gates: a candidate threshold fails if any ready `REJECT` row, or any ready `QUARANTINE` row, would be called `YES`. That tolerance is zero, and it is part of the rule now rather than a later adjustment. - -The objective is precision-first. Accuracy, F1, Youden's J, and AUC are not the objective. Among thresholds that pass the gates, the rule keeps the highest `HIGH` recall. If recall ties, the larger threshold wins. Candidate thresholds are the unique ready residual values, quantized to 10 decimal places and sorted ascending. `YES` means residual greater than or equal to the candidate. The direction gate is the already frozen `direction_result`: median difference, rank-biserial, and AUC must all point the same way. It does not require a bootstrap interval to exclude zero. - -The floors were chosen before any calibration labels existed. They are not a function of the development AUC. Calibration needs at least 12 ready `HIGH` rows and 8 ready `SECONDARY` rows. Predicted `YES` support on the comparison set must be at least 8, so one correct prediction cannot pass. Precision must be at least 0.80. The uncertainty method is the Wilson score interval at 95 percent, with the same z as `selection_surface.wilson_interval` (`1.959963984540054`). The lower bound must be at least 0.50. Zero support has no interval and fails the gate. Deleting any one comparison row must leave those precision, Wilson, and support gates intact, so one row cannot be the sole reason a threshold passes. One `REJECT` or `QUARANTINE` call of `YES` can still veto a threshold. These are not the development counts of 28 and 11. - diff --git a/.land-chunks/part-07 b/.land-chunks/part-07 deleted file mode 100644 index 9f445a18..00000000 --- a/.land-chunks/part-07 +++ /dev/null @@ -1,39 +0,0 @@ -If ready support is short, the state is `CALIBRATION_INSUFFICIENT_SUPPORT`. If the direction is not supported, the state is `NO_DIRECTIONAL_SIGNAL`. If the frozen Tier 3 concentration flag, the extreme-driven flag, or the POS-partition flag is true, the state is `CALIBRATION_CONFOUND_REVIEW` and no threshold is kept. If no candidate passes, the state is `NO_THRESHOLD_PASSES_PRECISION_GATE`. A future successful calibration run may enter `THRESHOLD_FROZEN`. This pass does not. There is one global `T_HIGH`. There is no POS-specific, tier-specific, or length-specific threshold. GlossBERT confidence, margin, and candidate-sense counts are preserved on future rows and are not inputs to the fit. POS, token count, character length, content-constituent count, resolution tier, GlossBERT involvement, and candidate-sense count are descriptive checks. They do not move the threshold after the fact. - -Future calibration order is: freeze the manifest, resolve constituents, freeze the integrated resolutions, compute residuals, freeze the score file and its hash, and only then join operator labels and run this rule. Future measurement applies the already frozen `T_HIGH` and does not change it. Measurement acceptance is a separate contract. Calibration success does not imply measurement success. Measurement needs at least 12 ready `HIGH` rows, 8 ready `SECONDARY` rows, 8 predicted `YES` rows, precision at least 0.80, a Wilson 95 percent lower bound at least 0.50, the same leave-one-out stability, and zero `YES` calls on ready `REJECT` and `QUARANTINE` rows. Short support is `MEASUREMENT_INSUFFICIENT_SUPPORT`. A failed gate is `MEASUREMENT_NOT_ACCEPTED`. A pass is `MEASUREMENT_ACCEPTED`. - -No JSON Schema document exists for this threshold family. `RESIDUAL_THRESHOLD_V1_ARTIFACT_SCHEMA.json` is the field contract for a future numeric artifact. It is not a JSON Schema. The private artifacts, mode 0600, are preregistration sha256 `86b4cb5578fa63d3e8f4f2968ed0e32db5b5f571c9b2604e29597bce70fac5e0`, acceptance sha256 `99aa20bdb046a449571f043b8ddb6c246ed610fb7c7cfc454a020a6c9464da6e`, selection procedure sha256 `afa2fe92e3f70756c6a6f5a2f44d1f1a82f291b58bef171db0db8cc39e13ed1a`, calibration contract sha256 `99c3dc10a5ea6b6990f350163ea975f99b9c1b895504c0260952d0b67a5bd323`, measurement contract sha256 `3463caaa22f0fd45342ce9946e1669bb453f91a776a195b14d62a014f898d2c9`, artifact contract sha256 `87cea8897b709c00c5917f0a00499237f3e9c8923dee974f0184bc98881fa7b4`, and isolation policy sha256 `e24cccdd1a2db315531a98c7468c70b9751083bf2c0f3c4206cad1f6e51b3d26`. Tracker sha256 `5dda813835ee11424f3d803f50ddc9eab5842cf0185f59ca396da207635a1daf`. The residual specification, residual scores, integrated resolution, GlossBERT artifacts, constituent-resolution artifacts, MAGPIE artifacts, Korkontzelos–Manandhar artifacts, procedure v2, the 225-row manifest, and the WordNet limitation artifact are unchanged. The ledger was not appended. Events sha256 remains `96b74a92d44f1cf9fe152b18e5207176f161ba3bfce528dac38aa4571a742f9c`. - -`selected_source` remains `none`. Nothing was integrated into runtime. `HLX-EXP-2026-09-27-SELECT-005` remains unauthorized. Admitted, settled, and gold stay 0. The next named transition is `RESIDUAL_CALIBRATION_SURFACE_DRAW_AUTHORIZATION`. This pass does not authorize it. -## Residual calibration surface — 2026-09-28 - -`RESIDUAL_CALIBRATION_SURFACE_DRAW_AUTHORIZATION` drew one calibration surface from the positional-unbind universe and froze `CALIBRATION_INSUFFICIENT_SUPPORT`. The threshold value stays null. `threshold_frozen` is false. No measurement surface was drawn. - -The calibration contract does not set a sample size or a seed. The draw receipt froze those parameters before any score or operator label was read. The rule is `positional_stratified_hash_prefix_v1`: the same gated WordNet positional universe used by prior Hyperlex evaluation surfaces, stratified by source POS and token count, ordered by `normalized_text_sha256` ascending, without replacement, at most 2 rows per cell. There is no RNG, so the draw seed is null. The cell size is the historical `draw_measurement` default. It was not computed from operator labels or from the development HIGH/SECONDARY distribution. The identity fence removed the 225 development rows, their normalized text, and their PWN 3.0 synsets. The same synset is not kept on two surfaces. MAGPIE and Korkontzelos–Manandhar rows in this universe are the same 225 development identities. No measurement reserve existed. - -The eligible universe after those fences and before the prefix was 44031 rows. The draw kept 27 rows because the adjective, four-token cell had one remaining row. Two reconstructions of the draw matched. Manifest sha256 `ba561935cff5f9f0a36af0b8b24e4b69bce5fb2c61b5ea2236f0a664c56bcf5d`. Isolation overlap with development identities and synsets is 0. Isolation sha256 `8743c864ed9e7152a5bccb5d6304b62aff8ffed5e894fb1d40ab270d696da9a5`. Draw receipt sha256 `7448adb8c9448c3737a7b7c2c9a3116ced23d2bab7e432d011bc41229c3ef283`. - -Only after that hash did the frozen Tier 1, Tier 2, and Tier 3 stack run. Resolver output, GlossBERT scores, and integrated resolutions were each produced twice and matched. Integrated resolution sha256 `fde102faa317c2bdfd374bc2b2ddda680434bb1847f93b3991e1532da2764dc2`. The frozen residual pipeline then ran twice: `sentence-transformers/all-MiniLM-L6-v2` at revision `1110a243fdf4706b3f48f1d95db1a4f5529b4d41`, composition `normalized_mean_v1`, distance `1 - cosine`, 10 decimal places. Six rows were `RESIDUAL_READY` and scored. Twenty-one rows stayed `UNKNOWN`. Score sha256 `f775849b3bb8cdec2ac8eeeea0100e51853fefcebbd88d6b3b4efb33fa8a3895`. The score receipt records `operator_labels_joined` false. Receipt sha256 `44cd2a9b57587d5330d830e5dda49fdbbc9b17d95553f030b3884feec46c9c29`. - -Operator labels were joined only after that hash. None of the 27 calibration rows has an existing operator-review label. Ready HIGH, SECONDARY, REJECT, and QUARANTINE are all 0. Unknown-by-class counts are all 0. Six scored rows and 21 unknown rows are unlabeled. The support gate requires 12 ready HIGH and 8 ready SECONDARY. It fails. Direction, confound review, and threshold search were not run. The candidate count is null. No threshold was selected. The failure state is `CALIBRATION_INSUFFICIENT_SUPPORT`. Failure sha256 `1d3f1f26609c1f1a47790d94730fba50e9f4d888dd8d7d89e6fa698a924fb3a8`. Label sha256 `b4c93faed22d3c294d46768a510eb9695c5159f2f84bfb8c4f7ab15b7f2332c2`. Analysis sha256 `ac17d7e3c634cb5f984251e6a4f4090eecd0ff7e0d809947a48b689d454db625`. Search sha256 `f92c5aded6822747811da2b18c796e59974de1e673db55b3f7a481a71be89a3c`. - -The draw was not repeated. The precision floor, Wilson rule, safety vetoes, resolver, residual model, and support floors were not changed. There is no `NO` output. There is no subgroup threshold. `selected_source` remains `none`. Nothing was integrated into runtime. `HLX-EXP-2026-09-27-SELECT-005` remains unauthorized. Admitted, settled, and gold stay 0. The ledger was not appended. Events sha256 remains `96b74a92d44f1cf9fe152b18e5207176f161ba3bfce528dac38aa4571a742f9c`. The preregistration artifacts, the 225-row development manifest, development residual scores, and the other sealed development artifacts are unchanged. No JSON Schema document exists for this calibration family. Tracker sha256 `07b911497c4e95d1b251c7bb98f78224f804b857fd61dffd7c198eecdf21636f`. - -The next legal transition is none. The frozen contract does not authorize a redraw, and a null threshold cannot be applied to a measurement surface. -## Threshold v1 calibration closure — 2026-09-28 - -The frozen calibration execution stays `CALIBRATION_INSUFFICIENT_SUPPORT`. Its threshold value stays null and `threshold_frozen` stays false. This pass does not redraw, does not relabel, and does not run threshold search. The 27-row manifest, the integrated resolution, and the residual scores stay byte-for-byte as frozen. Failure sha256 remains `1d3f1f26609c1f1a47790d94730fba50e9f4d888dd8d7d89e6fa698a924fb3a8`. - -The execution stopped because the novel surface had no operator labels: 0 of 27 rows carry HIGH, SECONDARY, REJECT, or QUARANTINE. That cause is `NOVEL_SURFACE_UNLABELED`. It is not a finding that the labeled HIGH/SECONDARY distribution was too small. Label-availability review sha256 `3a6c967c3542096714e25b59544bd68d89d7a9bc28ee253dec1b9f224b5a0484`. Redraw stays unauthorized. Labeling the same 27 rows later would not change their selection or their frozen scores, so that operation stays potentially eligible as a record. It is not authorized here. No operator packet was created. - -A separate count makes threshold calibration impossible on this surface even after perfect labels. The frozen floors still require at least 12 ready HIGH rows and 8 ready SECONDARY rows, 20 target-ready rows together. The frozen score file has 6 `RESIDUAL_READY` rows and 21 `UNKNOWN` rows. Six ready rows cannot be split into 12 HIGH and 8 SECONDARY. The finding is `CALIBRATION_DESIGN_SUPPORT_MISMATCH_CONFIRMED`. The draw policy was frozen without a sample size that could meet the already-frozen post-resolution support floors. That is a preregistration design gap. It is not a license to edit the frozen calibration. - -`RUNE.SEMANTIC_COMPOSITIONALITY_THRESHOLD.v1` is therefore closed as `CLOSED_CALIBRATION_DESIGN_INSUFFICIENT`. The execution id `RESIDUAL_THRESHOLD_V1_CALIBRATION_EXECUTION_001` remains `CALIBRATION_INSUFFICIENT_SUPPORT`. Closure sha256 `a5dbdb8ae497c25e711a81a880ad26ec7ef1ace917278c807497af5ec37e5002`. Tracker sha256 `a2a71c5da91d194337b2cc0de55692b712d5568694debf5350ede5ea0554d6c2`. No JSON Schema document exists for this family. `selected_source` remains `none`. Nothing was integrated into runtime. `HLX-EXP-2026-09-27-SELECT-005` remains unauthorized. Admitted, settled, and gold stay 0. No measurement surface was drawn. The ledger was not appended. Events sha256 remains `96b74a92d44f1cf9fe152b18e5207176f161ba3bfce528dac38aa4571a742f9c`. The preregistration artifacts and the development artifacts are unchanged. - -The next named transition is `THRESHOLD_V2_PREREGISTRATION_AUTHORIZATION`. This pass does not authorize it. A v2 preregistration, if a later pass authorizes one, would need a larger blind calibration surface chosen before labels. It may use previously observed resolution coverage, because readiness does not depend on operator labels. It may not use labels to select rows, and it may not redraw this v1 surface. -## Threshold v2 preregistration — 2026-09-28 - -`THRESHOLD_V2_PREREGISTRATION_AUTHORIZATION` opens `RUNE.SEMANTIC_COMPOSITIONALITY_THRESHOLD.v2` at `THRESHOLD_PROCEDURE_PREREGISTERED`. It does not draw a calibration row, a measurement row, or a threshold. Threshold v1 stays sealed: execution `CALIBRATION_INSUFFICIENT_SUPPORT`, disposition `CLOSED_CALIBRATION_DESIGN_INSUFFICIENT`, threshold value null, redraw prohibited, measurement blocked. Failure sha256 remains `1d3f1f26609c1f1a47790d94730fba50e9f4d888dd8d7d89e6fa698a924fb3a8`. Closure sha256 remains `a5dbdb8ae497c25e711a81a880ad26ec7ef1ace917278c807497af5ec37e5002`. - -v2 changes the raw-surface design. The resolver stack, GlossBERT revision and abstention, MiniLM residual, `normalized_mean_v1`, `1 - cosine`, YES/UNKNOWN output, and the precision-first selector stay as in v1. The selector id remains `residual_threshold_v1.precision_gated_recall_v1`. The gates stay ready HIGH at least 12, ready SECONDARY at least 8, predicted YES at least 8, precision at least 0.80, Wilson 95 percent lower bound at least 0.50 with z `1.959963984540054`, and zero YES calls on ready REJECT and QUARANTINE. Those gates were not lowered. - diff --git a/.land-chunks/part-08 b/.land-chunks/part-08 deleted file mode 100644 index 5e5cb525..00000000 --- a/.land-chunks/part-08 +++ /dev/null @@ -1,41 +0,0 @@ -The raw quota is `ceil(20 / 0.20 × 2.0) = 200`. Twenty is the sum of the unchanged 12 and 8 floors, not a new gate. The readiness rate 0.20 sits below the lower observed label-independent coverage, 6/27 on the v1 calibration surface. The development coverage 73/225 is recorded and is not the rate used. HIGH prevalence, SECONDARY prevalence, residual shape, AUC, and threshold performance are not quota inputs. Calibration and measurement each have their own quota of 200. They are not one shared pool that is split after results. - -A later draw, if authorized, enumerates the same positional-unbind universe, applies the development fences, and also excludes the frozen v1 calibration identities and synsets. It does not read v1 labels or v1 scores to choose rows. Cells are source POS by token count. Inside a cell the order is `normalized_text_sha256` ascending. Rows are taken round-robin across cells until the calibration quota is filled, then the same walk continues until the measurement quota is filled. There is no RNG. The walk does not stop because support has been reached. If either quota cannot be filled, the state is `SURFACE_QUOTA_UNFILLED` and the quotas are not reduced. Both manifests are frozen before any v2 resolution. Measurement stays unresolved, unscored, and unlabeled until a later authorization after a frozen threshold. Threshold refit on measurement is prohibited. Calibration labels are joined only after the calibration score hash. The v1 failure states remain, and a short v2 sample still does not authorize a redraw. - -No JSON Schema document exists for this family. Preregistration sha256 `1c962703e12c5c48fd279dc766fd4d1d3108c40486a10789fdda112926e98261`. Sample-size sha256 `46e3d32b4a4c8af73c9197557b09ceed872b1918d41143ed551bb3b4572c6151`. Calibration contract sha256 `51404e675870c3e99ff6bb51f3aa51e95aa585a360fe782e3c89d11927345934`. Measurement contract sha256 `f96b7ae1f20fbc9850cf3dcc6f6f74ad046bf35755e18ccc7e6747441184adee`. Selection procedure sha256 `1c8a82ef939a33bbbb327f0f760439113432645438361176de36159a8f56c55c`. Isolation policy sha256 `a9f5ae333934ff38a75a0a3e7adad5a5abf78b422f370560d0d9735765372189`. Acceptance sha256 `5cea52584a420e91e7b5f8e3512fbb657610bfb41d95008e52346b59cbfe3d50`. Artifact contract sha256 `dc6017a49ff4ea14f664a4f11a1f2501658cda177d25bd984d491c04f394e327`. Tracker sha256 `041f095c5ca0271e075c8db1a67ea401764486930c86fa5ff2bc96df7d8f1f71`. `selected_source` remains `none`. Admitted, settled, and gold stay 0. The ledger was not appended. Events sha256 remains `96b74a92d44f1cf9fe152b18e5207176f161ba3bfce528dac38aa4571a742f9c`. - -The next named transition is `THRESHOLD_V2_SURFACE_DRAW_AUTHORIZATION`. This pass does not authorize it. -## Threshold v2 surface draw — 2026-09-28 - -`THRESHOLD_V2_SURFACE_DRAW_AUTHORIZATION` freezes both v2 surfaces in one allocation. `RUNE.SEMANTIC_COMPOSITIONALITY_THRESHOLD.v2` is now `SURFACES_FROZEN`. The calibration manifest has 200 rows and the measurement manifest has 200 rows. Both were frozen before any resolution. Measurement stays `SEALED`, unresolved, unscored, and unlabeled. The threshold value stays null and the threshold stays unfrozen. `selected_source` stays `none`. There is no runtime integration. Measurement is not eligible. SELECT-005 stays unauthorized. Admitted, settled, and gold stay 0. - -The allocation rule is `positional_stratified_hash_round_robin_quota_v2`. The draw seed is null and no RNG was used. The source positional-unbind universe is 63882 rows. After the frozen fences, 44004 rows remain eligible. Cells are source POS by token count, ordered by source POS then token count, and inside a cell by `normalized_text_sha256` ascending. One walk emits round-robin across cells and skips exhausted cells. The first 200 emissions are `CALIBRATION_V2`. The next 200 are `MEASUREMENT_V2`. The walk does not stop on support or labels. Operator labels were not loaded or joined. Extended Lesk, GlossBERT, MiniLM, and residual scoring did not run. Two reconstructions were identical. - -The fences drop 255 `EVAL_SPENT` rows, 184 `TRAIN_CONSUMED` rows, and 63912 non-positional rows. They then drop 225 development row ids, 215 additional rows that carry a development synset, 27 v1 calibration row ids, 29 additional rows that carry a v1 calibration synset, and 19382 duplicate-synset rows. The historical v3–v7 partitions sit inside the 225 development rows. The v5 probe surfaces are inside that same fence. No measurement reserve exists. The single historical adj/4 row was already on the v1 calibration surface, so that cell is empty here. The quotas still fill exactly. No v1 calibration row is reused. - -Calibration and measurement share no row id, normalized text, or synset. Each of those overlaps with development and with the v1 calibration surface is 0. Every synset pair among development, v1 calibration, v2 calibration, and v2 measurement is 0. - -Calibration manifest sha256 `34bc70b93039fc6a5ba4bb58685e0fbfe6f91dd1f86a64fec8326a84013a6465`. Measurement manifest sha256 `78ca09ab14912681d028e8b9b1c77a8daf1d0c8c81561434eb9b6ade45a753e5`. Draw receipt sha256 `b89cbd357138c8b9eabeabeca055695b15fd2163840438e53989a920191d3419`. Isolation report sha256 `6ac513d9d7b3dd366f7c6f5ce5ca6821a33c5907d4bf2d1de45dc7a5a932bc38`. Distribution report sha256 `51d9a98a849248e40aadafd277c1c44adf0c7a7fcc842864f00d2fc3d8125886`. Tracker sha256 `a4264943f0313dcde85c6da8b833ff0c45c5c23972e1a4d0f2e69fa00c3e09ad`. Preregistration sha256 remains `1c962703e12c5c48fd279dc766fd4d1d3108c40486a10789fdda112926e98261`. Sample-size sha256 remains `46e3d32b4a4c8af73c9197557b09ceed872b1918d41143ed551bb3b4572c6151`. The tracker before this draw was `041f095c5ca0271e075c8db1a67ea401764486930c86fa5ff2bc96df7d8f1f71`. v1 failure sha256 remains `1d3f1f26609c1f1a47790d94730fba50e9f4d888dd8d7d89e6fa698a924fb3a8`. Events sha256 remains `96b74a92d44f1cf9fe152b18e5207176f161ba3bfce528dac38aa4571a742f9c`. The ledger was not appended. No JSON Schema document exists for this family. - -The next named transition is `THRESHOLD_V2_CALIBRATION_EXECUTION_AUTHORIZATION`. This pass does not authorize it. -## Threshold v2 calibration execution — 2026-09-28 - -`THRESHOLD_V2_CALIBRATION_EXECUTION_AUTHORIZATION` ran the frozen stack on the 200-row calibration surface. `RUNE.SEMANTIC_COMPOSITIONALITY_THRESHOLD.v2` is now `CALIBRATION_INSUFFICIENT_SUPPORT`. The threshold value stays null and the threshold stays unfrozen. Measurement stays `SEALED`. Its manifest sha256 remains `78ca09ab14912681d028e8b9b1c77a8daf1d0c8c81561434eb9b6ade45a753e5`. No measurement resolution, score, or label artifact exists. - -Resolution was run twice and matched. GlossBERT revision `0cc3b83af5496e27ebcc95ef0cf37ea0a9281a7a` scored 200 Lesk ties. Structural pointer conflicts stayed on Tier 1 and did not enter GlossBERT. Residual scoring used `sentence-transformers/all-MiniLM-L6-v2` revision `1110a243fdf4706b3f48f1d95db1a4f5529b4d41`, composition `normalized_mean_v1`, and `1 - cosine` at 10 decimals. Encoding ran twice and matched. 60 rows are `RESIDUAL_READY` and 140 are `UNKNOWN`. The score receipt was hashed with operator labels unloaded and unjoined, and with measurement untouched, before the label join. - -The frozen operator-review inventory contains no label for these 200 rows. Ready HIGH is 0, ready SECONDARY is 0, ready REJECT is 0, and ready QUARANTINE is 0. Unlabeled rows are 200, of which 60 are residual-ready. The support gate requires ready HIGH at least 12 and ready SECONDARY at least 8. It fails. Direction, confound review, and threshold search were not run. No threshold candidate was evaluated. Redraw is prohibited. No operator packet was created. - -Resolution sha256 `491b854bcf04d20624e560b63c01823b5634716ec266a4d3a7396d5514defd8d`. Resolution receipt sha256 `461ed206ef3abba6b4a8ba79ed5de72305cbee95a61c96981923c89817c17d2d`. Scores sha256 `01c758af570264c190f4af7805d43d734c9e9a36779f0e0b82675709f213e07c`. Score receipt sha256 `28ddb23bce8cfcf74b8046ee2cfd0908b5dddefa2a0c04562f9c2f8c912e88d5`. Labels sha256 `5cfeab84f16e5ca77d81bda8f1523a3aa20ae70a9828e1ff04a4d64a1930aa1d`. Analysis sha256 `46a8ecb371ec8dfe6ab38875a1500b463fda3db533cffb05129b4d8070963f2a`. Confound analysis sha256 `3695640fd649aab2e077028aae302c7c59578e2085b0d7de16127c948f19e954`. Threshold search sha256 `701ab0858a46490a6df4cf0ba8c41c0b16dc1f6738a0f9b76044812a3bdfb44c`. Failure sha256 `946a686d46b5a2fc47bb9968bf6e39c643e93c297d5a64de9e0fe88fc6eb33eb`. Tracker sha256 `50ee4bd40063c57f1f39e466d298d6c08486d0134184da459b9e1ef5de47bea8`. Calibration manifest sha256 remains `34bc70b93039fc6a5ba4bb58685e0fbfe6f91dd1f86a64fec8326a84013a6465`. v1 failure sha256 remains `1d3f1f26609c1f1a47790d94730fba50e9f4d888dd8d7d89e6fa698a924fb3a8`. Preregistration sha256 remains `1c962703e12c5c48fd279dc766fd4d1d3108c40486a10789fdda112926e98261`. Events sha256 remains `96b74a92d44f1cf9fe152b18e5207176f161ba3bfce528dac38aa4571a742f9c`. The ledger was not appended. `selected_source` remains `none`. Admitted, settled, and gold stay 0. SELECT-005 stays unauthorized. No JSON Schema document exists for this family. - -The next named transition is none. This pass does not authorize measurement or a redraw. -## Threshold v2 blind operator labeling — 2026-09-28 - -`THRESHOLD_V2_BLIND_OPERATOR_LABELING_AUTHORIZATION` freezes operator labels for the same 200-row calibration manifest. `THRESHOLD_V2_CALIBRATION_EXECUTION_001` stays `CALIBRATION_INSUFFICIENT_SUPPORT` and stays sealed. That execution observed ready HIGH 0 and ready SECONDARY 0 because no operator labels existed. This labeling is a successor event. It does not join the new labels to readiness or to residual scores. - -The label-availability review separates v2 from v1. v1 had 6 residual-ready rows, below the combined target-ready floor of 20, so design support was impossible and that closure stays `CLOSED_CALIBRATION_DESIGN_INSUFFICIENT`. v2 has 60 residual-ready rows and 140 unknown rows. Sixty is at least 20, so design support is possible and the missing evidence is the operator labels. Existing labels on these rows were 0. Redraw is not required and is not authorized. The same frozen surface is eligible for blind labeling. Review sha256 `8826b141468ec0dd674d2c7a253acfaa6e1d364ab5191506b424198419166da9`. - -The operator packet contains all 200 calibration rows, in manifest order. Membership does not depend on readiness, residual score, resolution tier, or threshold eligibility. Each row exposes the calibration row id, row id, surface, POS, PWN 3.0 synset, frozen first-sense gloss, and token count. Readiness, residual scores, MiniLM representations, resolver tiers, Extended Lesk, GlossBERT outputs, candidate counts, and threshold floors are absent. No measurement row is included. Measurement sha256 remains `78ca09ab14912681d028e8b9b1c77a8daf1d0c8c81561434eb9b6ade45a753e5`. Packet sha256 `d9d986ec85a1ebea257859af8011758e9c84df270c9547f7b8bd0e34cec970fe`. Packet receipt sha256 `0aff22b3406d9d954a54eabe080e83d2d4cb5e99df279605d9403e614b61dcf0`. The receipt was hashed before the labels were written. - -All 200 rows received a canonical review outcome under the existing operator ontology: HIGH, SECONDARY, REJECT, QUARANTINE, and UNRESOLVED. The label inventory is HIGH 44, SECONDARY 82, REJECT 71, QUARANTINE 3, and UNRESOLVED 0. Those counts are not joined to residual-ready rows. No support reassessment, direction analysis, confound analysis, or threshold search was run. Label sha256 `f9901e334543dac2e9bf258208097cf4c9b48af9dc8b497ff42fe4f1f106e1fc`. Labeling receipt sha256 `4ce71e88c40cd617b9b6b502db48216c51e8cfa22ecea02347c9becc44c967fa`. Tracker sha256 `b40bcb75f152c84d37fc96e834b6d0a23e1f4af562e8ec0225f452c3b000f77a`. - diff --git a/.land-chunks/part-09 b/.land-chunks/part-09 deleted file mode 100644 index 41e2134e..00000000 --- a/.land-chunks/part-09 +++ /dev/null @@ -1,38 +0,0 @@ -The threshold value stays null and the threshold stays unfrozen. Measurement stays `SEALED`, unresolved, unscored, and unlabeled. `selected_source` remains `none`. Nothing was integrated into runtime. SELECT-005 remains unauthorized. Admitted, settled, and gold stay 0. Calibration manifest sha256 remains `34bc70b93039fc6a5ba4bb58685e0fbfe6f91dd1f86a64fec8326a84013a6465`. Failure sha256 remains `946a686d46b5a2fc47bb9968bf6e39c643e93c297d5a64de9e0fe88fc6eb33eb`. Score sha256 remains `01c758af570264c190f4af7805d43d734c9e9a36779f0e0b82675709f213e07c`. Score receipt sha256 remains `28ddb23bce8cfcf74b8046ee2cfd0908b5dddefa2a0c04562f9c2f8c912e88d5`. Events sha256 remains `96b74a92d44f1cf9fe152b18e5207176f161ba3bfce528dac38aa4571a742f9c`. The ledger was not appended. No JSON Schema document exists for this labeling family. - -The next named transition is `THRESHOLD_V2_CALIBRATION_SUPPORT_REASSESSMENT_AUTHORIZATION`. This pass does not authorize it. -## Threshold v2 support reassessment — 2026-09-28 - -`THRESHOLD_V2_CALIBRATION_SUPPORT_REASSESSMENT_AUTHORIZATION` joins the frozen operator labels to the frozen calibration parent resolution status and residual scores by `row_id`. It checks calibration row id, normalized text hash, and PWN 3.0 synset for equality. It does not relabel a row, recompute resolution, recompute a residual, or change the 60/140 ready split. `THRESHOLD_V2_CALIBRATION_EXECUTION_001` stays `CALIBRATION_INSUFFICIENT_SUPPORT` and stays sealed. The resolution file stores constituent lines. Its 200 parent rows are the row identity, and that set matches the manifest and the labels. - -Each joined row records only the calibration row id, row id, surface, POS, synset, operator label, row resolution status, and residual score. An unknown row has a null residual. Ready rows are HIGH 13, SECONDARY 22, REJECT 24, QUARANTINE 1, and UNRESOLVED 0. Unknown rows are HIGH 31, SECONDARY 60, REJECT 47, QUARANTINE 2, and UNRESOLVED 0. The raw label inventory remains HIGH 44, SECONDARY 82, REJECT 71, QUARANTINE 3, and UNRESOLVED 0. - -The support gate passes. Ready HIGH is 13 and ready SECONDARY is 22. The floors remain 12 and 8. The next named transition is `THRESHOLD_V2_DIRECTION_EVALUATION_AUTHORIZATION`. This pass does not authorize it. Direction analysis, confound review, and threshold search were not run. Join sha256 `02088379b5ebe0f26106e830a730e8a90b2281d83c37bf1e750e1846bffa128b`. Join receipt sha256 `05748f812491be4ebe215a9a88224073bb2349b465c09dd2598f98d60e02947d`. Support reassessment sha256 `fe5d1061c1f9aed9cad4a1ad8e1dbabc5f76d13d4b13eacb88cef12c347e560d`. Tracker sha256 `170e4b66cfa6764034367ffef685cff67c88367833eb75b46d05f7b3f10f867c`. - -The threshold stays unfrozen. Measurement stays `SEALED`, unresolved, unscored, and unlabeled. Measurement sha256 remains `78ca09ab14912681d028e8b9b1c77a8daf1d0c8c81561434eb9b6ade45a753e5`. Score sha256 remains `01c758af570264c190f4af7805d43d734c9e9a36779f0e0b82675709f213e07c`. Resolution sha256 remains `491b854bcf04d20624e560b63c01823b5634716ec266a4d3a7396d5514defd8d`. Failure sha256 remains `946a686d46b5a2fc47bb9968bf6e39c643e93c297d5a64de9e0fe88fc6eb33eb`. Label sha256 remains `f9901e334543dac2e9bf258208097cf4c9b48af9dc8b497ff42fe4f1f106e1fc`. `selected_source` remains `none`. Nothing was integrated into runtime. SELECT-005 remains unauthorized. Admitted, settled, and gold stay 0. Events sha256 remains `96b74a92d44f1cf9fe152b18e5207176f161ba3bfce528dac38aa4571a742f9c`. The ledger was not appended. No JSON Schema document exists for this reassessment family. v1 remains `CLOSED_CALIBRATION_DESIGN_INSUFFICIENT`. -## Threshold v2 direction evaluation — 2026-09-28 - -`THRESHOLD_V2_DIRECTION_EVALUATION_AUTHORIZATION` reads residual strings already stored on the frozen support join. The only directional comparison is ready HIGH against ready SECONDARY. Ready HIGH is 13 and ready SECONDARY is 22. Ready REJECT 24 and ready QUARANTINE 1 are context counts and are not part of the comparison. No row was trimmed, winsorized, or removed. Residuals, labels, readiness, constituent senses, GlossBERT outputs, and Extended Lesk decisions were not recomputed. `THRESHOLD_V2_CALIBRATION_EXECUTION_001` stays `CALIBRATION_INSUFFICIENT_SUPPORT` and stays sealed. Support reassessment 001 stays `SUPPORT_GATE_PASSED`. - -The distributions use the frozen `full_distribution` convention: linear interpolation on the sorted residuals, rank `(n - 1) * (percent / 100)`, then the residual quantum with round-half-even. HIGH count 13, min 0.1077481638, p10 0.2022084726, p25 0.3475068269, median 0.3639930562, p75 0.3871338567, p90 0.4030255710, max 0.6656712845, mean 0.3506075686, std 0.1300277836. SECONDARY count 22, min 0.0373950459, p10 0.1220861227, p25 0.2362514965, median 0.3272977478, p75 0.3834981568, p90 0.5219075205, max 0.5535764009, mean 0.3203500904, std 0.1536408042. - -HIGH mean minus SECONDARY mean is 0.0302574782. HIGH median minus SECONDARY median is 0.0366953084. Mann-Whitney U for HIGH is 166.000000. HIGH-larger pairs are 166. SECONDARY-larger pairs are 120. Ties are 0. Rank-biserial correlation is 0.160839. Descriptive ROC AUC, with HIGH positive, is 0.580420. AUC is descriptive and is not a threshold. Uncertainty is `UNCERTAINTY_INTERVAL_NOT_COMPUTED`: the v2 preregistration and the v2 selection procedure do not define a calibration-direction interval, and `direction_result` does not require one. The frozen rule returns `SUPPORTED_DIRECTION`. - -`THRESHOLD_V2_DIRECTION_EVALUATION_001` is `SUPPORTED_DIRECTION`. The next named transition is `THRESHOLD_V2_CONFOUND_EVALUATION_AUTHORIZATION`. This pass does not authorize it. Direction scope is `DIRECTION_ONLY`. Confound analysis did not run. Threshold search did not run. No precision or Wilson calculation ran. Analysis sha256 `b68483acab29fe5918a81281798974fb85000a18af30ec3411c2e30500292783`. Decision sha256 `d408c81ff5766d40ece5c18c2c971ce2e05c23b8163fa9a809f2eded58a852eb`. Tracker sha256 `7a2e3eb6265ddb90624f2ef5034d77f486d1cec83e2c2792d7659a56bd076abe`. Support join sha256 remains `02088379b5ebe0f26106e830a730e8a90b2281d83c37bf1e750e1846bffa128b`. - -The threshold stays null and unfrozen. Measurement stays `SEALED`, unresolved, unscored, and unlabeled. Measurement sha256 remains `78ca09ab14912681d028e8b9b1c77a8daf1d0c8c81561434eb9b6ade45a753e5`. Score sha256 remains `01c758af570264c190f4af7805d43d734c9e9a36779f0e0b82675709f213e07c`. Label sha256 remains `f9901e334543dac2e9bf258208097cf4c9b48af9dc8b497ff42fe4f1f106e1fc`. Resolution sha256 remains `491b854bcf04d20624e560b63c01823b5634716ec266a4d3a7396d5514defd8d`. Failure sha256 remains `946a686d46b5a2fc47bb9968bf6e39c643e93c297d5a64de9e0fe88fc6eb33eb`. `selected_source` remains `none`. Nothing was integrated into runtime. SELECT-005 remains unauthorized. Admitted, settled, and gold stay 0. Events sha256 remains `96b74a92d44f1cf9fe152b18e5207176f161ba3bfce528dac38aa4571a742f9c`. The ledger was not appended. No JSON Schema document exists for this direction family. v1 remains `CLOSED_CALIBRATION_DESIGN_INSUFFICIENT`. -## Threshold v2 calibration completion — 2026-09-28 - -`THRESHOLD_V2_CALIBRATION_COMPLETION_AUTHORIZATION` runs the remaining preregistered calibration gates in order on the frozen support join. It does not recompute residuals, labels, readiness, GlossBERT, MiniLM, or the resolver. It does not redraw. The frozen confound stop remains tier-3 concentration, extreme-driven separation, or a POS partition. Nuisance associations do not add a stop. Extreme-driven is False. POS-partitioned is False. Tier-3 concentrated is False. Confound result is `PASS`. - -Token count, character length, and content-constituent count are copied from the frozen manifest, surface, and score constituents. Candidate-sense count and GlossBERT confidence and margin were not stored on the calibration artifacts, so those associations are `NOT_COMPUTABLE` and GlossBERT was not rerun. Nuisance Spearman results: token_count HIGH -0.111187, SECONDARY 0.356766; character_length HIGH -0.272622, SECONDARY -0.118083; content_count HIGH 0.284901, SECONDARY -0.017198; max_candidate_senses HIGH NOT_COMPUTABLE, SECONDARY NOT_COMPUTABLE; min_glossbert_confidence HIGH NOT_COMPUTABLE, SECONDARY NOT_COMPUTABLE; min_glossbert_margin HIGH NOT_COMPUTABLE, SECONDARY NOT_COMPUTABLE. - -Threshold search evaluated 60 unique ready residuals. Predicted-YES support passed for 50. The precision floor passed for 2. The Wilson lower bound passed for 0. Leave-one-out passed for 0. The REJECT veto passed for 1. The QUARANTINE veto passed for 47. Candidates passing every frozen gate: 0. No candidate passed. The threshold value stays null. Training readiness is `BLOCKED_BY_THRESHOLD_V2_CALIBRATION` because of `NO_THRESHOLD_PASSES_PRECISION_GATE`. Analysis sha256 `8e4c9468537bce4f35f0aee6375c2efd931cea1c54f7a72b9a138bbe8100d930`. Decision sha256 `cff6b3bfbca57b7aa26c5b057ad8e83507ff31d9edb1fd08996ff473670e5991`. Completion search sha256 `2df31d98f4c23b340f0ae1b8747326b6f8b97ed0e82a13ef461c82901c3d56ed`. Threshold artifact sha256 `99ecfdd553f46f34dcc5c3e20c644b0bcbc289ba8f200d9b0407e20874e1a301`. Receipt sha256 `d1221c7c98a926bb04a9ed5ddf5cedf8a3111f0ef0083ec60232eb8709d1b982`. Tracker sha256 `e674c207d9eb160159a673c23f7e2a2e8262b5bfc963ccf81b216d8291dc4b0f`. - -Training readiness is `BLOCKED_BY_THRESHOLD_V2_CALIBRATION`. Training stays unauthorized. A frozen threshold does not authorize SELECT-005, BEST replacement, or model promotion. Measurement stays `SEALED`. Measurement sha256 remains `78ca09ab14912681d028e8b9b1c77a8daf1d0c8c81561434eb9b6ade45a753e5`. The next named transition is `NONE`. This pass does not authorize it. `selected_source` remains `none`. Admitted, settled, and gold stay 0. Events sha256 remains `96b74a92d44f1cf9fe152b18e5207176f161ba3bfce528dac38aa4571a742f9c`. The ledger was not appended. No JSON Schema document exists for this completion family. Execution 001, the direction artifacts, and v1 stay sealed. `THRESHOLD_V2_CALIBRATION_EXECUTION_001` remains `CALIBRATION_INSUFFICIENT_SUPPORT`. -## Threshold artifact schema hardening — 2026-09-28 - -`THRESHOLD_ARTIFACT_SCHEMA_HARDENING_AUTHORIZATION` adds a retrospective JSON Schema family for residual-threshold artifacts that were already emitted. This is contract hardening. It does not repair the calibration. v2 failed because no threshold candidate passed the frozen statistical gates, and that result stays `NO_THRESHOLD_PASSES_PRECISION_GATE`. The threshold value stays null. Frozen artifact bytes and hashes are unchanged. A mismatch is reported as `SCHEMA_CONFORMANCE_GAP` and is not repaired by editing a sealed artifact. - -The schemas describe the contracts as emitted. Historical `json_schema_exists` values stay false. Twenty-six applicable v1 and v2 artifacts validate, and eighteen cross-artifact checks pass. The support join has 200 rows. Ready plus unknown is 200. The calibration manifest, operator labels, and support join share one row-id set of 200. The measurement manifest stays disjoint and its sha256 is unchanged. Referenced manifest, label, score, and support-join hashes match the files they name. v1 labels remain unlabeled. Thirty other residual-threshold files stay outside this family, including preregistrations, the older field contracts, labels, scores, and resolutions. Those field contracts are not JSON Schema and were not rewritten. The conformance outcome is `PASS`. `schema_conformance_gaps` is empty. - diff --git a/.land-chunks/part-10 b/.land-chunks/part-10 deleted file mode 100644 index 7b1c3276..00000000 --- a/.land-chunks/part-10 +++ /dev/null @@ -1,38 +0,0 @@ -The family is `specs/007-hyperlexical-model/schemas/residual-threshold/residual-threshold-common.schema.json`, `residual-threshold-surface-manifest.schema.json`, `residual-threshold-receipt.schema.json`, `residual-threshold-support-join.schema.json`, `residual-threshold-direction-analysis.schema.json`, `residual-threshold-confound-analysis.schema.json`, `residual-threshold-threshold-search.schema.json`, `residual-threshold-decision.schema.json`, and `residual-threshold-completion-receipt.schema.json`. Shared terms live in `$defs`: sha256, row_id, pwn30_synset, operator_label, row_resolution_status, threshold_state, measurement_state, and artifact_reference. An unfrozen threshold record requires `threshold_value` null. A frozen record requires a numeric threshold and its frozen metrics. No frozen success record was emitted; that branch is contract-only. The public report is `specs/007-hyperlexical-model/schemas/residual-threshold/CONFORMANCE.json`. - -Measurement stays `SEALED`. Measurement sha256 remains `78ca09ab14912681d028e8b9b1c77a8daf1d0c8c81561434eb9b6ade45a753e5`. Tracker sha256 remains `e674c207d9eb160159a673c23f7e2a2e8262b5bfc963ccf81b216d8291dc4b0f`. Events sha256 remains `96b74a92d44f1cf9fe152b18e5207176f161ba3bfce528dac38aa4571a742f9c`. The ledger was not appended. Training stays unauthorized. `selected_source` remains `none`. SELECT-005 remains unauthorized. Admitted, settled, and gold stay 0. The next named transition remains `NONE`. This pass does not authorize measurement, another threshold search, or a new residual design. -## Threshold v2 postmortem and contract split — 2026-09-28 - -`THRESHOLD_V2_POSTMORTEM` records the canonical v2 conclusion without rewriting sealed artifacts. Surface design and support were adequate. Direction was positive and weak: AUC 0.580420, rank-biserial 0.160839, ready HIGH 13, ready SECONDARY 22. Confound passed. Precision and Wilson were insufficient: 2 candidates met the precision floor and 0 met the Wilson floor. The conclusion is `CURRENT_RESIDUAL_REPRESENTATION_INSUFFICIENT_FOR_REQUIRED_PRECISION`. The threshold value stays null. Measurement stays `SEALED` at sha256 `78ca09ab14912681d028e8b9b1c77a8daf1d0c8c81561434eb9b6ade45a753e5`. Tracker sha256 remains `e674c207d9eb160159a673c23f7e2a2e8262b5bfc963ccf81b216d8291dc4b0f`. The full seal is `specs/007-hyperlexical-model/threshold-v2-postmortem.md`. - -Workstream A, the threshold artifact contract, is separate from workstream B. The contract family is `specs/007-hyperlexical-model/schemas/hyperlex/residual-threshold/`. Validation is `VALIDATE_ONLY`. The outcome is `HISTORICAL_VALIDATION_COMPLETE`: 30 applicable artifacts and 22 semantic checks pass, and both gap lists are empty. The contract note is `specs/007-hyperlexical-model/threshold-artifact-schema-contract.md`. The retrospective family from the previous hardening pass is unchanged. - -Workstream B drafts `RUNE.SEMANTIC_COMPOSITIONALITY_EVIDENCE.v2` at state `SEMANTIC_EVIDENCE_V2_SPEC_DRAFTED`. It does not preregister a model, draw a surface, or open a new threshold lane. The v2 measurement disposition is `RETAIN_FOR_V2_ONLY`. Specifications are `semantic-evidence-v2.md`, `semantic-evidence-v2-state-machine.md`, `semantic-evidence-v2-data-contract.md`, and `semantic-evidence-v2-evaluation-contract.md`. - -`TRAINING_DEPENDENCY_REVIEW` finds `SEMANTIC_THRESHOLD_NOT_A_HARD_TRAINING_DEPENDENCY`. SELECT-005 is a schedule experiment blocked by the missing fresh reserve and the unsealed admission receipt, not by the residual threshold. `HLX_THRESHOLD_AUTHORIZATION` seals numeric select-metric thresholds and is a different object. The residual lane flag `BLOCKED_BY_THRESHOLD_V2_CALIBRATION` stays on that lane. This review does not authorize training, SELECT-005, measurement, or a redraw. Events sha256 remains `96b74a92d44f1cf9fe152b18e5207176f161ba3bfce528dac38aa4571a742f9c`. The ledger was not appended. The review is `specs/007-hyperlexical-model/training-dependency-review.md`. -## SELECT-005 reserve and admission preparation — 2026-09-28 - -`SELECT_005_RESERVE_AND_ADMISSION_PREPARATION_AUTHORIZATION` re-censused the live settlement stream for `HLX-EXP-2026-09-27-SELECT-005`. The id stays unsealed. No preregistration file, arm directory, reserve binding, or admission receipt was created. The ledger was not appended. Events sha256 remains `96b74a92d44f1cf9fe152b18e5207176f161ba3bfce528dac38aa4571a742f9c`. Projection sha256 remains `77e22433203879b252f7a9e309d2013d7550101d1c4a014b494d2c96df87d0e0`. - -The proposed schedule is unchanged and is not sealed. Its canonical record sha256 is `ffb7f0399d89cd262319ab216104e60e97cf500efeda76a31b9f47a8262f2fd0`. Control remains `max_epochs=40` with early stopping disabled and restore best. Candidate remains `max_epochs=12`, `minimum_epochs=4`, `early_stopping_patience=4`, strict increase, ties keep the earlier checkpoint, and restore best. Both arms still pin `classify_macro_f1_nonnone`, warm start `hyperlex-encoder-modernbert-base-seed-morph65`, and export sha256 `64b7d3dede25047cb6dd2e5b663f7fa72946ec82ac1a8816ae34622d1aaac430` at 9150 rows. Learning rate, batch size, gradient accumulation, and seed stay unset. They were not filled in. The admission schedule shape already names the four `train_schedule` fields, so this pass does not stop as `SELECT_005_SPEC_INCOMPLETE` and does not seal a preregistration. - -`HLX_THRESHOLD_AUTHORIZATION` for this experiment id is absent. Decision thresholds stay `BLOCKED_PENDING_OPERATOR_AUTHORIZATION`. No numeric floor was derived from the fresh stream or from SELECT-004. SELECT-004 preservation floors do not transfer. No candidate metric was scored. Admission here is pre-training, and there is no candidate checkpoint for this id. - -The source universe is the 339 settlement events in `settlement/events.jsonl`. Eligible identities after the frozen exclusions: 0. Exclusion counts are `EVAL_SPENT` 241, `DECISION_UNRESOLVED` 84, and `RIGHTS_UNRESOLVED` 14. The 98 settlements absent from the ledger and from the pinned export are 82 `UNRESOLVED` with `CC-BY-SA`, 2 `UNRESOLVED` with `RIGHTS_UNRESOLVED`, 7 `ACCEPT` with `RIGHTS_UNRESOLVED`, 6 `NONE` with `RIGHTS_UNRESOLVED`, and 1 `RECLASSIFY` with `RIGHTS_UNRESOLVED`. The pinned export still has 6761 unique hashes, all already in the ledger. Ledger identities remain 7964: `TRAIN_CONSUMED` 6763, `EVAL_SPENT` 949, `EVAL_ABANDONED` 252. Active `EVAL_RESERVE` is 0. The SELECT-004 spent reserve is the 491 identities that still carry `evaluation_reserved`. It was not reused. SELECT-001 and SELECT-002 bindings cover 855 other identities and were not reused. Packet `HLX-EVAL-REVIEW-2026-09-27-001` has 84 rows and 0 operator decisions, so it is not a settlement. WordNet was not admitted alone. Required provenance fields are present on every settlement event, and none of those events are eligible. - -Determinism is `IDENTICAL` across two reconstructions. Frozen reserve rows: 0. No reserve manifest was written. The quota result is `RESERVE_QUOTA_UNFILLED`. Provenance result is `NO_ADMITTED_ROWS`. Final-reserve overlap is 0 for train, the prior SELECT reserve, SELECT-001/002 bindings, threshold-v1 calibration, threshold-v2 calibration, and threshold-v2 measurement, because no row was frozen. The 98 novel settlements also overlap those fences by 0 on normalized identity, row id, source identity, and synset. - -Admission result is `RESERVE_QUOTA_UNFILLED`. An admission receipt was not created. Private failure sha256 `4b69c4756d0ce56a6009b9cf6a22ed002fc108193010e3360fb9f3a61a10d97e`. Census sha256 `bc8152c3935dcc66d157edee8ed8275cd2acd3449318a009cb5db58b1f60df3c`. Isolation sha256 `38e3bdc4210da496597d52cb2a055a5e182e964a1aace5c8c4166ade0862e03e`. Provenance sha256 `d93611e9d73ebf42973da3ba8fbfa663ab598d6af0953b91a4c6f8add4d534de`. `training_authorized` is false. `training_launch_authorized` is false. Epochs and gradient steps remain 0. BEST was not moved. It still names `hyperlex-encoder-modernbert-base-seed-select004`, weights sha256 `9fba0f66b1d5de6492470f53577d1447bfac1d29b9ac03869268abb70bbd97f6`. The named warm start was not modified, weights sha256 `96838b9656a84c3fee1773a41fdf2fbbec2f88f3bc948e4acfbe06c194ac5587`. Trunk sha256 remains `340ac08b74eef0d7bdec2d7981a6a3d4249bf0e6aab60634b72ad02c2b8023a9`. The next legal transition is `NONE`. - -No SELECT admission JSON Schema document exists. `SELECT_ADMISSION_JSON_SCHEMA_EXISTS` is false. The code receipt name `hyperlex.admission.v1` is not that document. Threshold v2 measurement stays `SEALED` at sha256 `78ca09ab14912681d028e8b9b1c77a8daf1d0c8c81561434eb9b6ade45a753e5`. Tracker sha256 remains `e674c207d9eb160159a673c23f7e2a2e8262b5bfc963ccf81b216d8291dc4b0f`. `RUNE.SEMANTIC_COMPOSITIONALITY_EVIDENCE.v2` stays `SEMANTIC_EVIDENCE_V2_SPEC_DRAFTED`. The residual threshold lanes were not modified. -## SELECT-005 reserve supply and governance — 2026-09-28 - -`SELECT_005_RESERVE_SUPPLY_AND_GOVERNANCE_AUTHORIZATION` reviewed the 10 ready rows in `HLX-EVAL-REVIEW-2026-09-27-001` and did not fill the reserve. The 69 rights-blocked rows, 4 provenance-blocked rows, and 1 duplicate stayed out. Review-ready manifest sha256 `8c1c762e43307e31d124e4dd2309adc17ea6243789aee2ea8e694b2b7bbd68e9`. Operator decisions sha256 `a4653ac2c5ee125fea069b3dfbdd4b40950eb8ead2ad032158a502ce886ef7d6`. Decision receipt sha256 `776df14f889098cfa4bfbbbbf5869e5775076806113bdbb53d6f1d44c7793307`. Counts are `ACCEPT` 1, `NONE` 5, `UNRESOLVED` 4, `RECLASSIFY` 0. The stored registry class is `INFERRED`. It was not promoted to `OBSERVED`. - -Settlement apply refused all 10. None are in held-out stream `hs-20260925T211358Z`. `rights_of` has no mapping for `MIT-examples`. Application sha256 `0a29f0c12403fdbd0b794fee323a83c96a20c2195d37ff79bf158a606c0e0724`. The settlement log stays at 339 events. Events sha256 remains `96b74a92d44f1cf9fe152b18e5207176f161ba3bfce528dac38aa4571a742f9c`. Ledger sha256 remains `77e22433203879b252f7a9e309d2013d7550101d1c4a014b494d2c96df87d0e0`. The post-review census is unchanged: universe 339, eligible 0, exclusions `EVAL_SPENT` 241, `DECISION_UNRESOLVED` 84, `RIGHTS_UNRESOLVED` 14. Routed slice counts are 0, 0, 0, 0. The quota result remains `RESERVE_QUOTA_UNFILLED`. Missing slices are `classify`, `classify_observed`, `classify_non_none`, and `unbind_clean`. No reserve manifest and no admission receipt were created. The previous failure sha256 `4b69c4756d0ce56a6009b9cf6a22ed002fc108193010e3360fb9f3a61a10d97e`, census sha256 `bc8152c3935dcc66d157edee8ed8275cd2acd3449318a009cb5db58b1f60df3c`, isolation sha256 `38e3bdc4210da496597d52cb2a055a5e182e964a1aace5c8c4166ade0862e03e`, and provenance sha256 `d93611e9d73ebf42973da3ba8fbfa663ab598d6af0953b91a4c6f8add4d534de` were not overwritten. - -The SELECT-005 preregistration is `SELECT_005_PREREGISTRATION_INCOMPLETE`, sha256 `1cc6701940b5b3458f9dc7ede1864a2fc3b0f7bed526c5a0fb996a620a54f1c0`, sealed false. Unresolved fields are learning rate, batch size, gradient accumulation, and seed. SELECT-004 values were not inherited. `HLX_THRESHOLD_AUTHORIZATION` stays `BLOCKED_PENDING_OPERATOR_AUTHORIZATION`. Proposal sha256 `8bdc85ec1e610ac512aefc9eaeb3f5d6b5caebef04dde959d22b883c3eab4a4b`. Every numeric threshold is null. No SELECT-004 floor was copied. `training_authorized` is false. `training_launch_authorized` is false. Epochs and gradient steps remain 0. BEST was not moved. The next legal transition is `NONE`. - -No SELECT admission JSON Schema exists. No threshold-authorization JSON Schema exists. Threshold v2 measurement stays `SEALED` at sha256 `78ca09ab14912681d028e8b9b1c77a8daf1d0c8c81561434eb9b6ade45a753e5`. Tracker sha256 remains `e674c207d9eb160159a673c23f7e2a2e8262b5bfc963ccf81b216d8291dc4b0f`. `RUNE.SEMANTIC_COMPOSITIONALITY_EVIDENCE.v2` stays `SEMANTIC_EVIDENCE_V2_SPEC_DRAFTED`. -## SELECT-005 source and preregistration completion — 2026-09-28 - diff --git a/.land-chunks/part-11 b/.land-chunks/part-11 deleted file mode 100644 index 84105409..00000000 --- a/.land-chunks/part-11 +++ /dev/null @@ -1,30 +0,0 @@ -`SELECT_005_SOURCE_AND_PREREGISTRATION_COMPLETION_AUTHORIZATION` did not recensus the 339 settlement events. Run `hs-20260925T211358Z` is not a source. No row from that universe was reviewed again. The settlement log was not appended. Its sha256 remains `d8a400b68de95ccacb5a3c4069765182ed0bb1a3099238f5351643e4ed35c377`. Identity-ledger events sha256 remains `96b74a92d44f1cf9fe152b18e5207176f161ba3bfce528dac38aa4571a742f9c`. Projection sha256 remains `77e22433203879b252f7a9e309d2013d7550101d1c4a014b494d2c96df87d0e0`. The previous failure sha256 `4b69c4756d0ce56a6009b9cf6a22ed002fc108193010e3360fb9f3a61a10d97e`, census sha256 `bc8152c3935dcc66d157edee8ed8275cd2acd3449318a009cb5db58b1f60df3c`, isolation sha256 `38e3bdc4210da496597d52cb2a055a5e182e964a1aace5c8c4166ade0862e03e`, and provenance sha256 `d93611e9d73ebf42973da3ba8fbfa663ab598d6af0953b91a4c6f8add4d534de` were not overwritten. The supply-001 preregistration sha256 `1cc6701940b5b3458f9dc7ede1864a2fc3b0f7bed526c5a0fb996a620a54f1c0` stays `SELECT_005_PREREGISTRATION_INCOMPLETE`. - -The training initialization is `morph65_new_schedule`. Both arms warm-start `hyperlex-encoder-modernbert-base-seed-morph65`, weights sha256 `96838b9656a84c3fee1773a41fdf2fbbec2f88f3bc948e4acfbe06c194ac5587`. The rejected alternative is `select004_best_new_schedule`. BEST was not moved. It still names `hyperlex-encoder-modernbert-base-seed-select004`, weights sha256 `9fba0f66b1d5de6492470f53577d1447bfac1d29b9ac03869268abb70bbd97f6`. The trainer default did not choose. Initialization decision sha256 `fcc261039977b6580c6025ff7debb1a240cee9158b2f7a803537c4bbb745ac65`. - -The successor preregistration is `SELECT_005_PREREGISTRATION_SEALED`, sha256 `c2dcf3b703f50cb8eb80bb6c2c45335071ded3c4ce89cc669e8a869eb3498baf`, sealed true. The scientific variable remains `train_schedule`. Control remains `max_epochs=40` with early stopping disabled. Candidate remains `max_epochs=12`, `minimum_epochs=4`, `early_stopping_patience=4`, strict increase, ties keep the earlier checkpoint, and restore best. Both arms still pin `classify_macro_f1_nonnone` and export sha256 `64b7d3dede25047cb6dd2e5b663f7fa72946ec82ac1a8816ae34622d1aaac430` at 9150 rows. Held constant from the morph65 continuation control recipe, environment sha256 `edb7163f388de71c258db2d68d03ac4e6d6a011548236666250d5d0f5d05e0f1`: learning rate `2e-5`, batch size 8, gradient accumulation 1, and seed policy `HLX_SEED_UNSET_FROZEN`. Gradient accumulation 1 records that the trainer takes one optimizer step per batch. The seed stays unset because the reconstructed recipe seals `HLX_SEED` unset, and that unset state is now an explicit shared policy rather than a missing field. `inherited_from_select_004` is false. SELECT-004's scientific variable `HLX_SELECT_METRIC` does not transfer. The same optimizer numbers appear in the SELECT-004 candidate environment; that coincidence is recorded and is not the authority. - -`HLX_THRESHOLD_AUTHORIZATION` stays `BLOCKED_PENDING_OPERATOR_AUTHORIZATION`. Proposal sha256 `dc926d61e16b50ea4f15a73110957d54f3718e28778d7456e5c39bc6bf35755e`. The comparison baseline is the SELECT-005 control arm, not seed-morph78. The schedule design supports strict improvement of `classify_macro_f1_nonnone` and does not assign a number. `classification_accuracy`, `observed_label_accuracy`, and `unbind_clean_exact` stay null. No SELECT-004 floor was copied. `decision_thresholds` is empty. The proposal is not sealed, so admission cannot point `HLX_THRESHOLD_AUTHORIZATION` at it. - -The fresh-reserve contract admits only `wiktionary_category` and `wikipedia_prose`, rights token `CC-BY-SA`. Princeton WordNet 3.0 stays compositional and is not that token, so it is not relabeled and is not admitted alone. The label-blind surface carries identity, license, and source URL, and refuses task, class, lineage, decision, and `unbind_clean`. Contract sha256 `8288c6a4f0618c0880a8bbab9acf072b53c6d7b968a7cc7f64f3cae7d8610c22`. The held-out harvester refuses when the box exposure snapshot is older than 6 hours. The newest snapshots are 71.75 hours old. This pass did not bypass that fence and did not fetch. Rows acquired: 0. The label-blind surface is empty, sha256 `e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855`. Isolation is `NOT_RUN_NO_ROWS`. Provenance is `NO_ADMITTED_ROWS`. Settlements appended: 0. - -A decision-only settlement record still has no task, class, lineage, or `unbind_clean`. The census calls that `SLICE_LABELS_ABSENT` and does not freeze a partial reserve. This pass does not attach those fields beside the settlement contract. The fresh census universe is 0, not 339. Census sha256 `d945bbe02d0aa9640a2d276a274e6fcbe3d856f84cca7a1d4920e4b9e8c2f079`. Routed slice counts are 0, 0, 0, 0. The quota result is `RESERVE_QUOTA_UNFILLED`. No reserve manifest and no admission receipt were created. Completion receipt sha256 `6065baf43db3f4013eb6f39e4e8a7293fb9dead062c17497be03245f4daa6760`. - -`training_authorized` is false. `training_launch_authorized` is false. Epochs and gradient steps remain 0. No optimizer was constructed. Trunk sha256 remains `340ac08b74eef0d7bdec2d7981a6a3d4249bf0e6aab60634b72ad02c2b8023a9`. `loop.py` sha256 remains `1aa395081d7709be3844bf2568d12100d73d931ecbc51af43c3d6e01dba77e2a`. `admission.py` sha256 remains `6ae7b63ca1b6e0459d9b795c731668b3eff524269d81bcb92181c0c9d407ab78`. `identity_ledger.py` sha256 remains `ac49b7240a895952b99b3ef9046f7a8185c8647badac7a45c8640d592057d1c5`. No SELECT admission JSON Schema exists. No threshold-authorization JSON Schema exists. Threshold v2 measurement stays `SEALED` at sha256 `78ca09ab14912681d028e8b9b1c77a8daf1d0c8c81561434eb9b6ade45a753e5`. Tracker sha256 remains `e674c207d9eb160159a673c23f7e2a2e8262b5bfc963ccf81b216d8291dc4b0f`. `RUNE.SEMANTIC_COMPOSITIONALITY_EVIDENCE.v2` stays `SEMANTIC_EVIDENCE_V2_SPEC_DRAFTED`. `semantic_evidence_v2.py` sha256 remains `9f5e5d6d5106d2af709c86486d302fed3c11a0de7cb4cf4f2413897882e66348`. The next legal transition is `NONE` until a fresh box exposure snapshot exists and an operator supplies numeric SELECT-005 thresholds that are not taken from SELECT-004. -## SELECT-005 settlement routing contract — 2026-09-28 - -`SELECT_005_SETTLEMENT_ROUTING_CONTRACT_EXTENSION_AUTHORIZATION` freezes a derivative routing contract. Canonical settlement stays separate from SELECT routing. Existing settlement events were not rewritten. The settlement log sha256 remains `d8a400b68de95ccacb5a3c4069765182ed0bb1a3099238f5351643e4ed35c377`. Identity-ledger events sha256 remains `96b74a92d44f1cf9fe152b18e5207176f161ba3bfce528dac38aa4571a742f9c`. Projection sha256 remains `77e22433203879b252f7a9e309d2013d7550101d1c4a014b494d2c96df87d0e0`. No routing event was written under `eval-reserve-20260926/routing/`. The 339-event census was not rerun. No source was harvested. The six-hour exposure fence was not bypassed. - -The contract is `hyperlex.eval_routing.v1`. Spec sha256 `002e2ac67e5ee00c27ecedd4566b9e7d900852c51e3ac57aac957ac095899d96`. Procedure `select_eval_routing_v1` sha256 `ad187d262eba280dd4b88d2204e03d4dfb255f9b0fb443bd250a0e441a7ea6db`. Routing schema sha256 `147e2787e6e66ba47acd185fa6602a10bd5d57c15ea446f574b2e750277d31a0`. Threshold-authorization schema sha256 `35a1b70cc805cf4271831a2414fd9ac85dcc15e5d23ec951d2d37665e89109a1`. Admission schema sha256 `6f20efe73b74207243474724261c985d4e6dde888df497a1ef723dda02a42077`. Conformance report sha256 `fac5dacf06f1257137bfd72b91c728191840ce291d2c8e953292f4313dddcd2b`. Contract receipt sha256 `65d79c90b5654e01b3dc205600eda349855476766a39f7ebda2d7b66ce7f5e06`. - -`task` is `classify` or `unbind`. `class` is the registry attest, `OBSERVED` or `INFERRED`. `lineage` is the semantic family, including `none`. `classify_observed` requires registry class `OBSERVED`. `classify_non_none` requires a lineage other than empty or `none`. `unbind_clean` is true only when `task` is `unbind` and the frozen derivation `soft_ceiling.clean_surface` is true. One record may enter every classify slice it satisfies. `unbind_clean` does not share a record with those slices because `task` is single-valued. `INFERRED` is not promoted to `OBSERVED`. `ACCEPT` and `RECLASSIFY` are routable. `NONE` can enter `classify` and, when the frozen flag is true, `unbind_clean`, and cannot enter `classify_non_none`. `UNRESOLVED` is ineligible. Rights other than `CC-BY-SA` are ineligible. Incomplete provenance is ineligible. Routing does not read reserve deficits, desired slices, routed counts, or candidate scores. The reference corpus reconstructed twice is `DETERMINISM = IDENTICAL`. State is `ROUTING_VALIDATION_COMPLETE`. - -Census may consult a validated routing record only when the settlement itself would otherwise be `SLICE_LABELS_ABSENT`. Settlement exclusions still win. `admit_training_run` still consumes a sealed reserve binding and does not require routing records, so SELECT-004 bindings stay valid. Historical schema checks are `VALIDATE_ONLY`. Result `SCHEMA_CONFORMANCE_GAP`: `exp-20260926-select-003/threshold-authorization.json` lacks `sealed` and `decision_thresholds`. That file was not rewritten. SELECT-004 admission artifacts and the SELECT-004 and SELECT-005 threshold documents validate. No SELECT-005 numeric threshold was sealed. `HLX_THRESHOLD_AUTHORIZATION` remains `BLOCKED_PENDING_OPERATOR_AUTHORIZATION`. No SELECT-005 admission receipt was created. - -`training_authorized` is false. `training_launch_authorized` is false. Epochs and gradient steps remain 0. No optimizer was constructed. Weights were not mutated. BEST was not changed. Threshold v2 measurement stays `SEALED` at sha256 `78ca09ab14912681d028e8b9b1c77a8daf1d0c8c81561434eb9b6ade45a753e5`. The next productive transition is `SELECT_005_FRESH_SOURCE_HARVEST_AND_ROUTING_AUTHORIZATION`, and only after a box exposure snapshot is no older than 6 hours. The metric-threshold blocker stays separate. -## SELECT-005 fresh harvest stopped — exposure snapshot stale — 2026-09-28 - -`SELECT_005_FRESH_SOURCE_HARVEST_AND_ROUTING_AUTHORIZATION` measured the newest box exposure pair and stopped before any source fetch. Snapshot id `20260925T211351Z`. Comparison timestamp unix `1790631188.652030` (`2026-09-28T21:33:08Z`). The vern snapshot `/home/morpheus/hlx-private/heldout-stream/inbox/vern-texts-20260925T211351Z.jsonl` sha256 `33dee5ccd2162ccfe5d2f200fdf6dd034dcd95aaa9ac4cc2b422957c06b14d17` is 72.319854 hours old. The box-hash snapshot `/home/morpheus/hlx-private/heldout-stream/inbox/box-jev-hashes-20260925T211351Z.txt` sha256 `0fd1e9d9aeedd982c1913d2c8a88b72e2e03c95a6177e7dcbddc9189df6811de` is 72.320210 hours old. The fence is 6 hours. The binding age is 72.320210 hours. Result `EXPOSURE_SNAPSHOT_STALE`. Receipt sha256 `ce20e600ebeb18d1989f4c673c95c2901143be2e780035b3a965b815a212bbdb`. Network requests: 0. Rows acquired: 0. No raw harvest, review surface, settlement append, routing record, reserve manifest, or admission receipt was created. - -The routing contract was not modified. Spec sha256 remains `002e2ac67e5ee00c27ecedd4566b9e7d900852c51e3ac57aac957ac095899d96`. Procedure `select_eval_routing_v1` sha256 remains `ad187d262eba280dd4b88d2204e03d4dfb255f9b0fb443bd250a0e441a7ea6db`. Routing schema sha256 remains `147e2787e6e66ba47acd185fa6602a10bd5d57c15ea446f574b2e750277d31a0`. Settlement log sha256 remains `d8a400b68de95ccacb5a3c4069765182ed0bb1a3099238f5351643e4ed35c377`. Identity-ledger events sha256 remains `96b74a92d44f1cf9fe152b18e5207176f161ba3bfce528dac38aa4571a742f9c`. Projection sha256 remains `77e22433203879b252f7a9e309d2013d7550101d1c4a014b494d2c96df87d0e0`. Preregistration sha256 remains `c2dcf3b703f50cb8eb80bb6c2c45335071ded3c4ce89cc669e8a869eb3498baf`. `HLX_THRESHOLD_AUTHORIZATION` remains `BLOCKED_PENDING_OPERATOR_AUTHORIZATION`. The SELECT-003 schema gap was not repaired. - diff --git a/.land-chunks/part-12 b/.land-chunks/part-12 deleted file mode 100644 index 8866a9c6..00000000 --- a/.land-chunks/part-12 +++ /dev/null @@ -1,20 +0,0 @@ -`training_authorized` is false. `training_launch_authorized` is false. Epochs and gradient steps remain 0. No optimizer was constructed. Weights were not mutated. BEST remains `hyperlex-encoder-modernbert-base-seed-select004`, sha256 `9fba0f66b1d5de6492470f53577d1447bfac1d29b9ac03869268abb70bbd97f6`. Threshold-v2 measurement stays `SEALED` at sha256 `78ca09ab14912681d028e8b9b1c77a8daf1d0c8c81561434eb9b6ade45a753e5`. The next legal transition is the same harvest authorization, and only after a box exposure snapshot is no older than 6 hours. The metric-threshold blocker stays separate. -## SELECT-005 threshold authorization — 2026-09-28 - -`SELECT_005_THRESHOLD_AUTHORIZATION` seals `HLX_THRESHOLD_AUTHORIZATION` for `HLX-EXP-2026-09-27-SELECT-005`. State `SEALED`. Artifact sha256 `e0d8d81b6392692784d92542d0cc5aa6ee6756b93c44bab86e2167ef57cfbc55`. Receipt sha256 `c444977805717f7548744f7df435cdcd899a5daee1fc1a15967b02740cbfaf9e`. Derivation sha256 `ce6fcfb46b7b73f4b039649f8b182fe90aa8a196db76e46ddbc4a1a6bd0fb860`. Schema validation `PASS`, report sha256 `7e200ae1c6f3619a0aee0c4c29106077f7a647ce74ebca143ff77e815d070772`. Semantic validation `PASS`, report sha256 `1b724ca6e86ef8e158f60169dad9b057fe3eff26af343100c2aa855b22cb21cd`. The JSON Schema sha256 remains `35a1b70cc805cf4271831a2414fd9ac85dcc15e5d23ec951d2d37665e89109a1`. - -The comparison baseline is the SELECT-005 control arm. The comparison candidate is the SELECT-005 candidate arm. Both are scored as candidate minus control. `classify_macro_f1_nonnone` uses operator `GT` and threshold `0`, so a tie fails. `classification_accuracy`, `observed_label_accuracy`, and `unbind_clean_exact` use operator `GE` and threshold `0`, so equality at the floor passes and any degradation fails. A primary gain cannot compensate for a failed guardrail. A missing or non-computable required metric fails. The numbers are zero because the sealed schedule and `note_strict_improvement` already require `score > best_value`, and the paired arms share one count-ratio scorer. SELECT-005 has no sealed reserve size, so a positive degradation band cannot be computed from 1/N. SELECT-004 floors 0.02, 0.05, and 0.01 were not copied. `inherited_from_select_004` is false. The unsealed proposal sha256 `dc926d61e16b50ea4f15a73110957d54f3718e28778d7456e5c39bc6bf35755e` was not overwritten. Preregistration sha256 remains `c2dcf3b703f50cb8eb80bb6c2c45335071ded3c4ce89cc669e8a869eb3498baf`. - -This seal is not a training launch and not an admission receipt. `training_authorized` is false. `training_launch_authorized` is false. Epochs and gradient steps remain 0. No optimizer was constructed. Weights were not mutated. BEST remains `hyperlex-encoder-modernbert-base-seed-select004`, sha256 `9fba0f66b1d5de6492470f53577d1447bfac1d29b9ac03869268abb70bbd97f6`. The reserve remains `RESERVE_QUOTA_UNFILLED`. No harvest ran and the exposure snapshot was not refreshed. Threshold-v2 measurement stays `SEALED` at sha256 `78ca09ab14912681d028e8b9b1c77a8daf1d0c8c81561434eb9b6ade45a753e5`. The SELECT-003 schema gap was not repaired. The next legal transition is `SELECT_005_FRESH_SOURCE_HARVEST_AND_ROUTING_AUTHORIZATION`, and only after a box exposure snapshot is no older than 6 hours. This pass does not run it. -## SELECT-005 exposure snapshot refresh — 2026-09-28 - -`SELECT_005_EXPOSURE_SNAPSHOT_REFRESH_AUTHORIZATION` did not generate a successor snapshot. State `EXPOSURE_REFRESH_WORKFLOW_UNAVAILABLE`. Receipt sha256 `8c5477d61f938cfce6f6838f24bdad104e3d643e0b97bf7399c640e088c03b38`. - -The canonical procedure is `/workspace/deliverables/hyperlex-heldout-stream/bin/heldout-stream-daily`, run on the box. It is absent on `spark-bf46`. `/workspace/deliverables` does not exist here. No SSH host is configured to the box. `vernacular.sqlite3` is not on this host. `hs_run.py` only reads `vern-texts-.jsonl` and `box-jev-hashes-.txt`; it does not generate them. The documented daily command also runs `hs_run.py run`, and this pass did not start that harvest. No snapshot id was minted. The previous pair `20260925T211351Z` was not overwritten and was not reused. Vern sha256 remains `33dee5ccd2162ccfe5d2f200fdf6dd034dcd95aaa9ac4cc2b422957c06b14d17`. Box-hash sha256 remains `0fd1e9d9aeedd982c1913d2c8a88b72e2e03c95a6177e7dcbddc9189df6811de`. - -No harvest, reserve recensus, settlement append, or training occurred. Preregistration sha256 remains `c2dcf3b703f50cb8eb80bb6c2c45335071ded3c4ce89cc669e8a869eb3498baf`. Threshold authorization sha256 remains `e0d8d81b6392692784d92542d0cc5aa6ee6756b93c44bab86e2167ef57cfbc55`. The routing contract was not modified. Settlement log sha256 remains `d8a400b68de95ccacb5a3c4069765182ed0bb1a3099238f5351643e4ed35c377`. Identity-ledger events sha256 remains `96b74a92d44f1cf9fe152b18e5207176f161ba3bfce528dac38aa4571a742f9c`. Projection sha256 remains `77e22433203879b252f7a9e309d2013d7550101d1c4a014b494d2c96df87d0e0`. `training_authorized` is false. `training_launch_authorized` is false. Epochs and gradient steps remain 0. BEST remains `hyperlex-encoder-modernbert-base-seed-select004`, sha256 `9fba0f66b1d5de6492470f53577d1447bfac1d29b9ac03869268abb70bbd97f6`. Threshold-v2 measurement stays `SEALED` at sha256 `78ca09ab14912681d028e8b9b1c77a8daf1d0c8c81561434eb9b6ade45a753e5`. The next legal transition stays `SELECT_005_FRESH_SOURCE_HARVEST_AND_ROUTING_AUTHORIZATION`, and it is not executable until a box-side snapshot-only run produces a new pair no older than 6 hours. This pass does not run it. -## SELECT-005 Notion vernacular source — 2026-09-28 - -The vernacular registries are in Notion. Vernacular Intelligence (`collection://d0ae52bd-a4ef-4ece-b06e-37078f533a7b`) has 44 rows and 43 distinct titles, created 2026-09-07 through 2026-09-27. Vernacular Events (`collection://785c124f-ecd7-41ae-9553-8a43967bac92`) has 120 rows, all source X and itemized, with 0 source URLs. There is no candidates database and no observations database. The box hash list remains the over-inclusive Jev-lane exposure set. Discovery, Obsidian, and the Jev-spike exports are not in these registries, and `heldout-stream-daily` is still absent on `spark-bf46`. Hashing the Notion rows into `box-jev-hashes` would narrow that set. Writing a vern file alone would leave the newest vern id unbound from the newest box id. No successor snapshot was written. State remains `EXPOSURE_REFRESH_WORKFLOW_UNAVAILABLE`. Receipt sha256 `ae37022242000400217cf2d959f0a339fdde9a1a8a4ae213cc348d079afd5e0b`. - -The previous pair `20260925T211351Z` was not overwritten and was not copied forward. Vern sha256 remains `33dee5ccd2162ccfe5d2f200fdf6dd034dcd95aaa9ac4cc2b422957c06b14d17`. Box-hash sha256 remains `0fd1e9d9aeedd982c1913d2c8a88b72e2e03c95a6177e7dcbddc9189df6811de`, the same bytes as `20260925T205240Z` and the audit pin. No harvest, reserve recensus, settlement append, or training occurred. Preregistration sha256 remains `c2dcf3b703f50cb8eb80bb6c2c45335071ded3c4ce89cc669e8a869eb3498baf`. Threshold authorization sha256 remains `e0d8d81b6392692784d92542d0cc5aa6ee6756b93c44bab86e2167ef57cfbc55`. Settlement log sha256 remains `d8a400b68de95ccacb5a3c4069765182ed0bb1a3099238f5351643e4ed35c377`. Identity-ledger events sha256 remains `96b74a92d44f1cf9fe152b18e5207176f161ba3bfce528dac38aa4571a742f9c`. Projection sha256 remains `77e22433203879b252f7a9e309d2013d7550101d1c4a014b494d2c96df87d0e0`. Sense-screen hypothesis sha256 remains `e674c207d9eb160159a673c23f7e2a2e8262b5bfc963ccf81b216d8291dc4b0f`. BEST remains `hyperlex-encoder-modernbert-base-seed-select004`, sha256 `9fba0f66b1d5de6492470f53577d1447bfac1d29b9ac03869268abb70bbd97f6`. `training_authorized` is false. Epochs and gradient steps remain 0. The next legal transition stays `SELECT_005_FRESH_SOURCE_HARVEST_AND_ROUTING_AUTHORIZATION`, and it is not executable until a box-side snapshot-only run produces a new bound pair no older than 6 hours. This pass does not run it. diff --git a/specs/007-hyperlexical-model/evaluation-reserve.md b/specs/007-hyperlexical-model/evaluation-reserve.md index d86a06ce..dd0de93f 100644 --- a/specs/007-hyperlexical-model/evaluation-reserve.md +++ b/specs/007-hyperlexical-model/evaluation-reserve.md @@ -835,3 +835,223 @@ All six preregistered conditions hold: readiness reproduces, the replay is deter No JSON Schema document exists for this replay family. Original residual spec sha256 remains `39c2914e32557ffe1a456a56f8742ea4fe8f1aaec1dc1da451656cd22f0db32d`. Original residual scores sha256 remains `cea638679faeee1bc1c689823e7c0c08562c4d7ef1f8230bbbf4079239e7c3e7`. Analysis sha256 `4367930648a68c2f84a1fd8e011fa07d9f3bf111303079f3ec07688f7d5425fb`. Confound analysis sha256 `d0f2496ca7623050eb5519969abcf7c5e2d0e23e0c1961859c40cae4dcdb7021`. Decision sha256 `ed0296fe6888e7c9fe864a2c6c7ab6cecbd4f490d6b7d650e442dafc0bd0976d`. Tracker sha256 `6500394d24543d1797eb9a2a05ba31868e035ffcbc7ae568f53116d4b016e62a`. The 225-row manifest, procedure v2, GlossBERT artifacts, resolver artifacts, MAGPIE artifacts, and Korkontzelos–Manandhar artifacts are unchanged. The ledger was not appended. Events sha256 remains `96b74a92d44f1cf9fe152b18e5207176f161ba3bfce528dac38aa4571a742f9c`. The next named transition is `RESIDUAL_THRESHOLD_PREREGISTRATION_AUTHORIZATION`. This pass does not authorize it. A threshold still requires a later preregistered stage. `selected_source` remains `none`. +## Residual threshold preregistration — 2026-09-28 + +`RESIDUAL_THRESHOLD_PREREGISTRATION_AUTHORIZATION` freezes the rules for a later one-sided threshold. It does not choose `T_HIGH`. The development residual track stays `RESIDUAL_DEVELOPMENT_ANALYZED_V2` with candidate status `CANDIDATE_PROMISING`. A new track, `RUNE.SEMANTIC_COMPOSITIONALITY_THRESHOLD.v1`, starts at `THRESHOLD_PROCEDURE_PREREGISTERED`. Its threshold value is null. No calibration surface was drawn. No measurement surface was drawn. `threshold_frozen` is false. + +The frozen development evidence says a larger residual is a candidate for semantic noncompositionality. It does not say a smaller residual is semantic compositionality. The future evidence contract is one-sided. A ready row with residual greater than or equal to `T_HIGH` is `YES`. Every other row is `UNKNOWN`. There is no `NO` output and no lower threshold. `REJECT` is not semantic no. `QUARANTINE` is not semantic no. + +The 225 reviewed positional rows are development-only. `development_rows_reusable_for_threshold_selection` is false. That exclusion covers every partition in the development evidence artifact, including the historical v3 through v7 rows. Calibration and measurement, when a later pass is authorized to draw them, come from the same positional-unbind universe and keep the reserve's existing rights and provenance requirements. They must be disjoint from development and from each other. The identity fence is the one Hyperlex already uses, `hyperlexical.holdout_guard.normalized_text_sha256`, which is also the development `row_id`. The normalized surface text is fenced with it. A concrete PWN 3.0 synset key, `synset_pos:synset_offset`, cannot appear on two of the three surfaces. That synset exclusion is justified here, before any draw: the residual is a property of one synset's resolved constituents, so a repeated synset is not an independent observation. A missing offset does not match another missing offset. + +Only `RESIDUAL_READY` rows may enter threshold selection, under the frozen Tier 1, Tier 2, and Tier 3 stack. Unresolved rows stay `UNKNOWN` and do not move `T_HIGH`. WSD confidence and margin are not retuned to raise coverage. Operator `HIGH` is the positive class and operator `SECONDARY` is the negative class. `REJECT` and `QUARANTINE` stay out of the precision denominator. They are still hard safety gates: a candidate threshold fails if any ready `REJECT` row, or any ready `QUARANTINE` row, would be called `YES`. That tolerance is zero, and it is part of the rule now rather than a later adjustment. + +The objective is precision-first. Accuracy, F1, Youden's J, and AUC are not the objective. Among thresholds that pass the gates, the rule keeps the highest `HIGH` recall. If recall ties, the larger threshold wins. Candidate thresholds are the unique ready residual values, quantized to 10 decimal places and sorted ascending. `YES` means residual greater than or equal to the candidate. The direction gate is the already frozen `direction_result`: median difference, rank-biserial, and AUC must all point the same way. It does not require a bootstrap interval to exclude zero. + +The floors were chosen before any calibration labels existed. They are not a function of the development AUC. Calibration needs at least 12 ready `HIGH` rows and 8 ready `SECONDARY` rows. Predicted `YES` support on the comparison set must be at least 8, so one correct prediction cannot pass. Precision must be at least 0.80. The uncertainty method is the Wilson score interval at 95 percent, with the same z as `selection_surface.wilson_interval` (`1.959963984540054`). The lower bound must be at least 0.50. Zero support has no interval and fails the gate. Deleting any one comparison row must leave those precision, Wilson, and support gates intact, so one row cannot be the sole reason a threshold passes. One `REJECT` or `QUARANTINE` call of `YES` can still veto a threshold. These are not the development counts of 28 and 11. + +If ready support is short, the state is `CALIBRATION_INSUFFICIENT_SUPPORT`. If the direction is not supported, the state is `NO_DIRECTIONAL_SIGNAL`. If the frozen Tier 3 concentration flag, the extreme-driven flag, or the POS-partition flag is true, the state is `CALIBRATION_CONFOUND_REVIEW` and no threshold is kept. If no candidate passes, the state is `NO_THRESHOLD_PASSES_PRECISION_GATE`. A future successful calibration run may enter `THRESHOLD_FROZEN`. This pass does not. There is one global `T_HIGH`. There is no POS-specific, tier-specific, or length-specific threshold. GlossBERT confidence, margin, and candidate-sense counts are preserved on future rows and are not inputs to the fit. POS, token count, character length, content-constituent count, resolution tier, GlossBERT involvement, and candidate-sense count are descriptive checks. They do not move the threshold after the fact. + +Future calibration order is: freeze the manifest, resolve constituents, freeze the integrated resolutions, compute residuals, freeze the score file and its hash, and only then join operator labels and run this rule. Future measurement applies the already frozen `T_HIGH` and does not change it. Measurement acceptance is a separate contract. Calibration success does not imply measurement success. Measurement needs at least 12 ready `HIGH` rows, 8 ready `SECONDARY` rows, 8 predicted `YES` rows, precision at least 0.80, a Wilson 95 percent lower bound at least 0.50, the same leave-one-out stability, and zero `YES` calls on ready `REJECT` and `QUARANTINE` rows. Short support is `MEASUREMENT_INSUFFICIENT_SUPPORT`. A failed gate is `MEASUREMENT_NOT_ACCEPTED`. A pass is `MEASUREMENT_ACCEPTED`. + +No JSON Schema document exists for this threshold family. `RESIDUAL_THRESHOLD_V1_ARTIFACT_SCHEMA.json` is the field contract for a future numeric artifact. It is not a JSON Schema. The private artifacts, mode 0600, are preregistration sha256 `86b4cb5578fa63d3e8f4f2968ed0e32db5b5f571c9b2604e29597bce70fac5e0`, acceptance sha256 `99aa20bdb046a449571f043b8ddb6c246ed610fb7c7cfc454a020a6c9464da6e`, selection procedure sha256 `afa2fe92e3f70756c6a6f5a2f44d1f1a82f291b58bef171db0db8cc39e13ed1a`, calibration contract sha256 `99c3dc10a5ea6b6990f350163ea975f99b9c1b895504c0260952d0b67a5bd323`, measurement contract sha256 `3463caaa22f0fd45342ce9946e1669bb453f91a776a195b14d62a014f898d2c9`, artifact contract sha256 `87cea8897b709c00c5917f0a00499237f3e9c8923dee974f0184bc98881fa7b4`, and isolation policy sha256 `e24cccdd1a2db315531a98c7468c70b9751083bf2c0f3c4206cad1f6e51b3d26`. Tracker sha256 `5dda813835ee11424f3d803f50ddc9eab5842cf0185f59ca396da207635a1daf`. The residual specification, residual scores, integrated resolution, GlossBERT artifacts, constituent-resolution artifacts, MAGPIE artifacts, Korkontzelos–Manandhar artifacts, procedure v2, the 225-row manifest, and the WordNet limitation artifact are unchanged. The ledger was not appended. Events sha256 remains `96b74a92d44f1cf9fe152b18e5207176f161ba3bfce528dac38aa4571a742f9c`. + +`selected_source` remains `none`. Nothing was integrated into runtime. `HLX-EXP-2026-09-27-SELECT-005` remains unauthorized. Admitted, settled, and gold stay 0. The next named transition is `RESIDUAL_CALIBRATION_SURFACE_DRAW_AUTHORIZATION`. This pass does not authorize it. +## Residual calibration surface — 2026-09-28 + +`RESIDUAL_CALIBRATION_SURFACE_DRAW_AUTHORIZATION` drew one calibration surface from the positional-unbind universe and froze `CALIBRATION_INSUFFICIENT_SUPPORT`. The threshold value stays null. `threshold_frozen` is false. No measurement surface was drawn. + +The calibration contract does not set a sample size or a seed. The draw receipt froze those parameters before any score or operator label was read. The rule is `positional_stratified_hash_prefix_v1`: the same gated WordNet positional universe used by prior Hyperlex evaluation surfaces, stratified by source POS and token count, ordered by `normalized_text_sha256` ascending, without replacement, at most 2 rows per cell. There is no RNG, so the draw seed is null. The cell size is the historical `draw_measurement` default. It was not computed from operator labels or from the development HIGH/SECONDARY distribution. The identity fence removed the 225 development rows, their normalized text, and their PWN 3.0 synsets. The same synset is not kept on two surfaces. MAGPIE and Korkontzelos–Manandhar rows in this universe are the same 225 development identities. No measurement reserve existed. + +The eligible universe after those fences and before the prefix was 44031 rows. The draw kept 27 rows because the adjective, four-token cell had one remaining row. Two reconstructions of the draw matched. Manifest sha256 `ba561935cff5f9f0a36af0b8b24e4b69bce5fb2c61b5ea2236f0a664c56bcf5d`. Isolation overlap with development identities and synsets is 0. Isolation sha256 `8743c864ed9e7152a5bccb5d6304b62aff8ffed5e894fb1d40ab270d696da9a5`. Draw receipt sha256 `7448adb8c9448c3737a7b7c2c9a3116ced23d2bab7e432d011bc41229c3ef283`. + +Only after that hash did the frozen Tier 1, Tier 2, and Tier 3 stack run. Resolver output, GlossBERT scores, and integrated resolutions were each produced twice and matched. Integrated resolution sha256 `fde102faa317c2bdfd374bc2b2ddda680434bb1847f93b3991e1532da2764dc2`. The frozen residual pipeline then ran twice: `sentence-transformers/all-MiniLM-L6-v2` at revision `1110a243fdf4706b3f48f1d95db1a4f5529b4d41`, composition `normalized_mean_v1`, distance `1 - cosine`, 10 decimal places. Six rows were `RESIDUAL_READY` and scored. Twenty-one rows stayed `UNKNOWN`. Score sha256 `f775849b3bb8cdec2ac8eeeea0100e51853fefcebbd88d6b3b4efb33fa8a3895`. The score receipt records `operator_labels_joined` false. Receipt sha256 `44cd2a9b57587d5330d830e5dda49fdbbc9b17d95553f030b3884feec46c9c29`. + +Operator labels were joined only after that hash. None of the 27 calibration rows has an existing operator-review label. Ready HIGH, SECONDARY, REJECT, and QUARANTINE are all 0. Unknown-by-class counts are all 0. Six scored rows and 21 unknown rows are unlabeled. The support gate requires 12 ready HIGH and 8 ready SECONDARY. It fails. Direction, confound review, and threshold search were not run. The candidate count is null. No threshold was selected. The failure state is `CALIBRATION_INSUFFICIENT_SUPPORT`. Failure sha256 `1d3f1f26609c1f1a47790d94730fba50e9f4d888dd8d7d89e6fa698a924fb3a8`. Label sha256 `b4c93faed22d3c294d46768a510eb9695c5159f2f84bfb8c4f7ab15b7f2332c2`. Analysis sha256 `ac17d7e3c634cb5f984251e6a4f4090eecd0ff7e0d809947a48b689d454db625`. Search sha256 `f92c5aded6822747811da2b18c796e59974de1e673db55b3f7a481a71be89a3c`. + +The draw was not repeated. The precision floor, Wilson rule, safety vetoes, resolver, residual model, and support floors were not changed. There is no `NO` output. There is no subgroup threshold. `selected_source` remains `none`. Nothing was integrated into runtime. `HLX-EXP-2026-09-27-SELECT-005` remains unauthorized. Admitted, settled, and gold stay 0. The ledger was not appended. Events sha256 remains `96b74a92d44f1cf9fe152b18e5207176f161ba3bfce528dac38aa4571a742f9c`. The preregistration artifacts, the 225-row development manifest, development residual scores, and the other sealed development artifacts are unchanged. No JSON Schema document exists for this calibration family. Tracker sha256 `07b911497c4e95d1b251c7bb98f78224f804b857fd61dffd7c198eecdf21636f`. + +The next legal transition is none. The frozen contract does not authorize a redraw, and a null threshold cannot be applied to a measurement surface. +## Threshold v1 calibration closure — 2026-09-28 + +The frozen calibration execution stays `CALIBRATION_INSUFFICIENT_SUPPORT`. Its threshold value stays null and `threshold_frozen` stays false. This pass does not redraw, does not relabel, and does not run threshold search. The 27-row manifest, the integrated resolution, and the residual scores stay byte-for-byte as frozen. Failure sha256 remains `1d3f1f26609c1f1a47790d94730fba50e9f4d888dd8d7d89e6fa698a924fb3a8`. + +The execution stopped because the novel surface had no operator labels: 0 of 27 rows carry HIGH, SECONDARY, REJECT, or QUARANTINE. That cause is `NOVEL_SURFACE_UNLABELED`. It is not a finding that the labeled HIGH/SECONDARY distribution was too small. Label-availability review sha256 `3a6c967c3542096714e25b59544bd68d89d7a9bc28ee253dec1b9f224b5a0484`. Redraw stays unauthorized. Labeling the same 27 rows later would not change their selection or their frozen scores, so that operation stays potentially eligible as a record. It is not authorized here. No operator packet was created. + +A separate count makes threshold calibration impossible on this surface even after perfect labels. The frozen floors still require at least 12 ready HIGH rows and 8 ready SECONDARY rows, 20 target-ready rows together. The frozen score file has 6 `RESIDUAL_READY` rows and 21 `UNKNOWN` rows. Six ready rows cannot be split into 12 HIGH and 8 SECONDARY. The finding is `CALIBRATION_DESIGN_SUPPORT_MISMATCH_CONFIRMED`. The draw policy was frozen without a sample size that could meet the already-frozen post-resolution support floors. That is a preregistration design gap. It is not a license to edit the frozen calibration. + +`RUNE.SEMANTIC_COMPOSITIONALITY_THRESHOLD.v1` is therefore closed as `CLOSED_CALIBRATION_DESIGN_INSUFFICIENT`. The execution id `RESIDUAL_THRESHOLD_V1_CALIBRATION_EXECUTION_001` remains `CALIBRATION_INSUFFICIENT_SUPPORT`. Closure sha256 `a5dbdb8ae497c25e711a81a880ad26ec7ef1ace917278c807497af5ec37e5002`. Tracker sha256 `a2a71c5da91d194337b2cc0de55692b712d5568694debf5350ede5ea0554d6c2`. No JSON Schema document exists for this family. `selected_source` remains `none`. Nothing was integrated into runtime. `HLX-EXP-2026-09-27-SELECT-005` remains unauthorized. Admitted, settled, and gold stay 0. No measurement surface was drawn. The ledger was not appended. Events sha256 remains `96b74a92d44f1cf9fe152b18e5207176f161ba3bfce528dac38aa4571a742f9c`. The preregistration artifacts and the development artifacts are unchanged. + +The next named transition is `THRESHOLD_V2_PREREGISTRATION_AUTHORIZATION`. This pass does not authorize it. A v2 preregistration, if a later pass authorizes one, would need a larger blind calibration surface chosen before labels. It may use previously observed resolution coverage, because readiness does not depend on operator labels. It may not use labels to select rows, and it may not redraw this v1 surface. +## Threshold v2 preregistration — 2026-09-28 + +`THRESHOLD_V2_PREREGISTRATION_AUTHORIZATION` opens `RUNE.SEMANTIC_COMPOSITIONALITY_THRESHOLD.v2` at `THRESHOLD_PROCEDURE_PREREGISTERED`. It does not draw a calibration row, a measurement row, or a threshold. Threshold v1 stays sealed: execution `CALIBRATION_INSUFFICIENT_SUPPORT`, disposition `CLOSED_CALIBRATION_DESIGN_INSUFFICIENT`, threshold value null, redraw prohibited, measurement blocked. Failure sha256 remains `1d3f1f26609c1f1a47790d94730fba50e9f4d888dd8d7d89e6fa698a924fb3a8`. Closure sha256 remains `a5dbdb8ae497c25e711a81a880ad26ec7ef1ace917278c807497af5ec37e5002`. + +v2 changes the raw-surface design. The resolver stack, GlossBERT revision and abstention, MiniLM residual, `normalized_mean_v1`, `1 - cosine`, YES/UNKNOWN output, and the precision-first selector stay as in v1. The selector id remains `residual_threshold_v1.precision_gated_recall_v1`. The gates stay ready HIGH at least 12, ready SECONDARY at least 8, predicted YES at least 8, precision at least 0.80, Wilson 95 percent lower bound at least 0.50 with z `1.959963984540054`, and zero YES calls on ready REJECT and QUARANTINE. Those gates were not lowered. + +The raw quota is `ceil(20 / 0.20 × 2.0) = 200`. Twenty is the sum of the unchanged 12 and 8 floors, not a new gate. The readiness rate 0.20 sits below the lower observed label-independent coverage, 6/27 on the v1 calibration surface. The development coverage 73/225 is recorded and is not the rate used. HIGH prevalence, SECONDARY prevalence, residual shape, AUC, and threshold performance are not quota inputs. Calibration and measurement each have their own quota of 200. They are not one shared pool that is split after results. + +A later draw, if authorized, enumerates the same positional-unbind universe, applies the development fences, and also excludes the frozen v1 calibration identities and synsets. It does not read v1 labels or v1 scores to choose rows. Cells are source POS by token count. Inside a cell the order is `normalized_text_sha256` ascending. Rows are taken round-robin across cells until the calibration quota is filled, then the same walk continues until the measurement quota is filled. There is no RNG. The walk does not stop because support has been reached. If either quota cannot be filled, the state is `SURFACE_QUOTA_UNFILLED` and the quotas are not reduced. Both manifests are frozen before any v2 resolution. Measurement stays unresolved, unscored, and unlabeled until a later authorization after a frozen threshold. Threshold refit on measurement is prohibited. Calibration labels are joined only after the calibration score hash. The v1 failure states remain, and a short v2 sample still does not authorize a redraw. + +No JSON Schema document exists for this family. Preregistration sha256 `1c962703e12c5c48fd279dc766fd4d1d3108c40486a10789fdda112926e98261`. Sample-size sha256 `46e3d32b4a4c8af73c9197557b09ceed872b1918d41143ed551bb3b4572c6151`. Calibration contract sha256 `51404e675870c3e99ff6bb51f3aa51e95aa585a360fe782e3c89d11927345934`. Measurement contract sha256 `f96b7ae1f20fbc9850cf3dcc6f6f74ad046bf35755e18ccc7e6747441184adee`. Selection procedure sha256 `1c8a82ef939a33bbbb327f0f760439113432645438361176de36159a8f56c55c`. Isolation policy sha256 `a9f5ae333934ff38a75a0a3e7adad5a5abf78b422f370560d0d9735765372189`. Acceptance sha256 `5cea52584a420e91e7b5f8e3512fbb657610bfb41d95008e52346b59cbfe3d50`. Artifact contract sha256 `dc6017a49ff4ea14f664a4f11a1f2501658cda177d25bd984d491c04f394e327`. Tracker sha256 `041f095c5ca0271e075c8db1a67ea401764486930c86fa5ff2bc96df7d8f1f71`. `selected_source` remains `none`. Admitted, settled, and gold stay 0. The ledger was not appended. Events sha256 remains `96b74a92d44f1cf9fe152b18e5207176f161ba3bfce528dac38aa4571a742f9c`. + +The next named transition is `THRESHOLD_V2_SURFACE_DRAW_AUTHORIZATION`. This pass does not authorize it. +## Threshold v2 surface draw — 2026-09-28 + +`THRESHOLD_V2_SURFACE_DRAW_AUTHORIZATION` freezes both v2 surfaces in one allocation. `RUNE.SEMANTIC_COMPOSITIONALITY_THRESHOLD.v2` is now `SURFACES_FROZEN`. The calibration manifest has 200 rows and the measurement manifest has 200 rows. Both were frozen before any resolution. Measurement stays `SEALED`, unresolved, unscored, and unlabeled. The threshold value stays null and the threshold stays unfrozen. `selected_source` stays `none`. There is no runtime integration. Measurement is not eligible. SELECT-005 stays unauthorized. Admitted, settled, and gold stay 0. + +The allocation rule is `positional_stratified_hash_round_robin_quota_v2`. The draw seed is null and no RNG was used. The source positional-unbind universe is 63882 rows. After the frozen fences, 44004 rows remain eligible. Cells are source POS by token count, ordered by source POS then token count, and inside a cell by `normalized_text_sha256` ascending. One walk emits round-robin across cells and skips exhausted cells. The first 200 emissions are `CALIBRATION_V2`. The next 200 are `MEASUREMENT_V2`. The walk does not stop on support or labels. Operator labels were not loaded or joined. Extended Lesk, GlossBERT, MiniLM, and residual scoring did not run. Two reconstructions were identical. + +The fences drop 255 `EVAL_SPENT` rows, 184 `TRAIN_CONSUMED` rows, and 63912 non-positional rows. They then drop 225 development row ids, 215 additional rows that carry a development synset, 27 v1 calibration row ids, 29 additional rows that carry a v1 calibration synset, and 19382 duplicate-synset rows. The historical v3–v7 partitions sit inside the 225 development rows. The v5 probe surfaces are inside that same fence. No measurement reserve exists. The single historical adj/4 row was already on the v1 calibration surface, so that cell is empty here. The quotas still fill exactly. No v1 calibration row is reused. + +Calibration and measurement share no row id, normalized text, or synset. Each of those overlaps with development and with the v1 calibration surface is 0. Every synset pair among development, v1 calibration, v2 calibration, and v2 measurement is 0. + +Calibration manifest sha256 `34bc70b93039fc6a5ba4bb58685e0fbfe6f91dd1f86a64fec8326a84013a6465`. Measurement manifest sha256 `78ca09ab14912681d028e8b9b1c77a8daf1d0c8c81561434eb9b6ade45a753e5`. Draw receipt sha256 `b89cbd357138c8b9eabeabeca055695b15fd2163840438e53989a920191d3419`. Isolation report sha256 `6ac513d9d7b3dd366f7c6f5ce5ca6821a33c5907d4bf2d1de45dc7a5a932bc38`. Distribution report sha256 `51d9a98a849248e40aadafd277c1c44adf0c7a7fcc842864f00d2fc3d8125886`. Tracker sha256 `a4264943f0313dcde85c6da8b833ff0c45c5c23972e1a4d0f2e69fa00c3e09ad`. Preregistration sha256 remains `1c962703e12c5c48fd279dc766fd4d1d3108c40486a10789fdda112926e98261`. Sample-size sha256 remains `46e3d32b4a4c8af73c9197557b09ceed872b1918d41143ed551bb3b4572c6151`. The tracker before this draw was `041f095c5ca0271e075c8db1a67ea401764486930c86fa5ff2bc96df7d8f1f71`. v1 failure sha256 remains `1d3f1f26609c1f1a47790d94730fba50e9f4d888dd8d7d89e6fa698a924fb3a8`. Events sha256 remains `96b74a92d44f1cf9fe152b18e5207176f161ba3bfce528dac38aa4571a742f9c`. The ledger was not appended. No JSON Schema document exists for this family. + +The next named transition is `THRESHOLD_V2_CALIBRATION_EXECUTION_AUTHORIZATION`. This pass does not authorize it. +## Threshold v2 calibration execution — 2026-09-28 + +`THRESHOLD_V2_CALIBRATION_EXECUTION_AUTHORIZATION` ran the frozen stack on the 200-row calibration surface. `RUNE.SEMANTIC_COMPOSITIONALITY_THRESHOLD.v2` is now `CALIBRATION_INSUFFICIENT_SUPPORT`. The threshold value stays null and the threshold stays unfrozen. Measurement stays `SEALED`. Its manifest sha256 remains `78ca09ab14912681d028e8b9b1c77a8daf1d0c8c81561434eb9b6ade45a753e5`. No measurement resolution, score, or label artifact exists. + +Resolution was run twice and matched. GlossBERT revision `0cc3b83af5496e27ebcc95ef0cf37ea0a9281a7a` scored 200 Lesk ties. Structural pointer conflicts stayed on Tier 1 and did not enter GlossBERT. Residual scoring used `sentence-transformers/all-MiniLM-L6-v2` revision `1110a243fdf4706b3f48f1d95db1a4f5529b4d41`, composition `normalized_mean_v1`, and `1 - cosine` at 10 decimals. Encoding ran twice and matched. 60 rows are `RESIDUAL_READY` and 140 are `UNKNOWN`. The score receipt was hashed with operator labels unloaded and unjoined, and with measurement untouched, before the label join. + +The frozen operator-review inventory contains no label for these 200 rows. Ready HIGH is 0, ready SECONDARY is 0, ready REJECT is 0, and ready QUARANTINE is 0. Unlabeled rows are 200, of which 60 are residual-ready. The support gate requires ready HIGH at least 12 and ready SECONDARY at least 8. It fails. Direction, confound review, and threshold search were not run. No threshold candidate was evaluated. Redraw is prohibited. No operator packet was created. + +Resolution sha256 `491b854bcf04d20624e560b63c01823b5634716ec266a4d3a7396d5514defd8d`. Resolution receipt sha256 `461ed206ef3abba6b4a8ba79ed5de72305cbee95a61c96981923c89817c17d2d`. Scores sha256 `01c758af570264c190f4af7805d43d734c9e9a36779f0e0b82675709f213e07c`. Score receipt sha256 `28ddb23bce8cfcf74b8046ee2cfd0908b5dddefa2a0c04562f9c2f8c912e88d5`. Labels sha256 `5cfeab84f16e5ca77d81bda8f1523a3aa20ae70a9828e1ff04a4d64a1930aa1d`. Analysis sha256 `46a8ecb371ec8dfe6ab38875a1500b463fda3db533cffb05129b4d8070963f2a`. Confound analysis sha256 `3695640fd649aab2e077028aae302c7c59578e2085b0d7de16127c948f19e954`. Threshold search sha256 `701ab0858a46490a6df4cf0ba8c41c0b16dc1f6738a0f9b76044812a3bdfb44c`. Failure sha256 `946a686d46b5a2fc47bb9968bf6e39c643e93c297d5a64de9e0fe88fc6eb33eb`. Tracker sha256 `50ee4bd40063c57f1f39e466d298d6c08486d0134184da459b9e1ef5de47bea8`. Calibration manifest sha256 remains `34bc70b93039fc6a5ba4bb58685e0fbfe6f91dd1f86a64fec8326a84013a6465`. v1 failure sha256 remains `1d3f1f26609c1f1a47790d94730fba50e9f4d888dd8d7d89e6fa698a924fb3a8`. Preregistration sha256 remains `1c962703e12c5c48fd279dc766fd4d1d3108c40486a10789fdda112926e98261`. Events sha256 remains `96b74a92d44f1cf9fe152b18e5207176f161ba3bfce528dac38aa4571a742f9c`. The ledger was not appended. `selected_source` remains `none`. Admitted, settled, and gold stay 0. SELECT-005 stays unauthorized. No JSON Schema document exists for this family. + +The next named transition is none. This pass does not authorize measurement or a redraw. +## Threshold v2 blind operator labeling — 2026-09-28 + +`THRESHOLD_V2_BLIND_OPERATOR_LABELING_AUTHORIZATION` freezes operator labels for the same 200-row calibration manifest. `THRESHOLD_V2_CALIBRATION_EXECUTION_001` stays `CALIBRATION_INSUFFICIENT_SUPPORT` and stays sealed. That execution observed ready HIGH 0 and ready SECONDARY 0 because no operator labels existed. This labeling is a successor event. It does not join the new labels to readiness or to residual scores. + +The label-availability review separates v2 from v1. v1 had 6 residual-ready rows, below the combined target-ready floor of 20, so design support was impossible and that closure stays `CLOSED_CALIBRATION_DESIGN_INSUFFICIENT`. v2 has 60 residual-ready rows and 140 unknown rows. Sixty is at least 20, so design support is possible and the missing evidence is the operator labels. Existing labels on these rows were 0. Redraw is not required and is not authorized. The same frozen surface is eligible for blind labeling. Review sha256 `8826b141468ec0dd674d2c7a253acfaa6e1d364ab5191506b424198419166da9`. + +The operator packet contains all 200 calibration rows, in manifest order. Membership does not depend on readiness, residual score, resolution tier, or threshold eligibility. Each row exposes the calibration row id, row id, surface, POS, PWN 3.0 synset, frozen first-sense gloss, and token count. Readiness, residual scores, MiniLM representations, resolver tiers, Extended Lesk, GlossBERT outputs, candidate counts, and threshold floors are absent. No measurement row is included. Measurement sha256 remains `78ca09ab14912681d028e8b9b1c77a8daf1d0c8c81561434eb9b6ade45a753e5`. Packet sha256 `d9d986ec85a1ebea257859af8011758e9c84df270c9547f7b8bd0e34cec970fe`. Packet receipt sha256 `0aff22b3406d9d954a54eabe080e83d2d4cb5e99df279605d9403e614b61dcf0`. The receipt was hashed before the labels were written. + +All 200 rows received a canonical review outcome under the existing operator ontology: HIGH, SECONDARY, REJECT, QUARANTINE, and UNRESOLVED. The label inventory is HIGH 44, SECONDARY 82, REJECT 71, QUARANTINE 3, and UNRESOLVED 0. Those counts are not joined to residual-ready rows. No support reassessment, direction analysis, confound analysis, or threshold search was run. Label sha256 `f9901e334543dac2e9bf258208097cf4c9b48af9dc8b497ff42fe4f1f106e1fc`. Labeling receipt sha256 `4ce71e88c40cd617b9b6b502db48216c51e8cfa22ecea02347c9becc44c967fa`. Tracker sha256 `b40bcb75f152c84d37fc96e834b6d0a23e1f4af562e8ec0225f452c3b000f77a`. + +The threshold value stays null and the threshold stays unfrozen. Measurement stays `SEALED`, unresolved, unscored, and unlabeled. `selected_source` remains `none`. Nothing was integrated into runtime. SELECT-005 remains unauthorized. Admitted, settled, and gold stay 0. Calibration manifest sha256 remains `34bc70b93039fc6a5ba4bb58685e0fbfe6f91dd1f86a64fec8326a84013a6465`. Failure sha256 remains `946a686d46b5a2fc47bb9968bf6e39c643e93c297d5a64de9e0fe88fc6eb33eb`. Score sha256 remains `01c758af570264c190f4af7805d43d734c9e9a36779f0e0b82675709f213e07c`. Score receipt sha256 remains `28ddb23bce8cfcf74b8046ee2cfd0908b5dddefa2a0c04562f9c2f8c912e88d5`. Events sha256 remains `96b74a92d44f1cf9fe152b18e5207176f161ba3bfce528dac38aa4571a742f9c`. The ledger was not appended. No JSON Schema document exists for this labeling family. + +The next named transition is `THRESHOLD_V2_CALIBRATION_SUPPORT_REASSESSMENT_AUTHORIZATION`. This pass does not authorize it. +## Threshold v2 support reassessment — 2026-09-28 + +`THRESHOLD_V2_CALIBRATION_SUPPORT_REASSESSMENT_AUTHORIZATION` joins the frozen operator labels to the frozen calibration parent resolution status and residual scores by `row_id`. It checks calibration row id, normalized text hash, and PWN 3.0 synset for equality. It does not relabel a row, recompute resolution, recompute a residual, or change the 60/140 ready split. `THRESHOLD_V2_CALIBRATION_EXECUTION_001` stays `CALIBRATION_INSUFFICIENT_SUPPORT` and stays sealed. The resolution file stores constituent lines. Its 200 parent rows are the row identity, and that set matches the manifest and the labels. + +Each joined row records only the calibration row id, row id, surface, POS, synset, operator label, row resolution status, and residual score. An unknown row has a null residual. Ready rows are HIGH 13, SECONDARY 22, REJECT 24, QUARANTINE 1, and UNRESOLVED 0. Unknown rows are HIGH 31, SECONDARY 60, REJECT 47, QUARANTINE 2, and UNRESOLVED 0. The raw label inventory remains HIGH 44, SECONDARY 82, REJECT 71, QUARANTINE 3, and UNRESOLVED 0. + +The support gate passes. Ready HIGH is 13 and ready SECONDARY is 22. The floors remain 12 and 8. The next named transition is `THRESHOLD_V2_DIRECTION_EVALUATION_AUTHORIZATION`. This pass does not authorize it. Direction analysis, confound review, and threshold search were not run. Join sha256 `02088379b5ebe0f26106e830a730e8a90b2281d83c37bf1e750e1846bffa128b`. Join receipt sha256 `05748f812491be4ebe215a9a88224073bb2349b465c09dd2598f98d60e02947d`. Support reassessment sha256 `fe5d1061c1f9aed9cad4a1ad8e1dbabc5f76d13d4b13eacb88cef12c347e560d`. Tracker sha256 `170e4b66cfa6764034367ffef685cff67c88367833eb75b46d05f7b3f10f867c`. + +The threshold stays unfrozen. Measurement stays `SEALED`, unresolved, unscored, and unlabeled. Measurement sha256 remains `78ca09ab14912681d028e8b9b1c77a8daf1d0c8c81561434eb9b6ade45a753e5`. Score sha256 remains `01c758af570264c190f4af7805d43d734c9e9a36779f0e0b82675709f213e07c`. Resolution sha256 remains `491b854bcf04d20624e560b63c01823b5634716ec266a4d3a7396d5514defd8d`. Failure sha256 remains `946a686d46b5a2fc47bb9968bf6e39c643e93c297d5a64de9e0fe88fc6eb33eb`. Label sha256 remains `f9901e334543dac2e9bf258208097cf4c9b48af9dc8b497ff42fe4f1f106e1fc`. `selected_source` remains `none`. Nothing was integrated into runtime. SELECT-005 remains unauthorized. Admitted, settled, and gold stay 0. Events sha256 remains `96b74a92d44f1cf9fe152b18e5207176f161ba3bfce528dac38aa4571a742f9c`. The ledger was not appended. No JSON Schema document exists for this reassessment family. v1 remains `CLOSED_CALIBRATION_DESIGN_INSUFFICIENT`. +## Threshold v2 direction evaluation — 2026-09-28 + +`THRESHOLD_V2_DIRECTION_EVALUATION_AUTHORIZATION` reads residual strings already stored on the frozen support join. The only directional comparison is ready HIGH against ready SECONDARY. Ready HIGH is 13 and ready SECONDARY is 22. Ready REJECT 24 and ready QUARANTINE 1 are context counts and are not part of the comparison. No row was trimmed, winsorized, or removed. Residuals, labels, readiness, constituent senses, GlossBERT outputs, and Extended Lesk decisions were not recomputed. `THRESHOLD_V2_CALIBRATION_EXECUTION_001` stays `CALIBRATION_INSUFFICIENT_SUPPORT` and stays sealed. Support reassessment 001 stays `SUPPORT_GATE_PASSED`. + +The distributions use the frozen `full_distribution` convention: linear interpolation on the sorted residuals, rank `(n - 1) * (percent / 100)`, then the residual quantum with round-half-even. HIGH count 13, min 0.1077481638, p10 0.2022084726, p25 0.3475068269, median 0.3639930562, p75 0.3871338567, p90 0.4030255710, max 0.6656712845, mean 0.3506075686, std 0.1300277836. SECONDARY count 22, min 0.0373950459, p10 0.1220861227, p25 0.2362514965, median 0.3272977478, p75 0.3834981568, p90 0.5219075205, max 0.5535764009, mean 0.3203500904, std 0.1536408042. + +HIGH mean minus SECONDARY mean is 0.0302574782. HIGH median minus SECONDARY median is 0.0366953084. Mann-Whitney U for HIGH is 166.000000. HIGH-larger pairs are 166. SECONDARY-larger pairs are 120. Ties are 0. Rank-biserial correlation is 0.160839. Descriptive ROC AUC, with HIGH positive, is 0.580420. AUC is descriptive and is not a threshold. Uncertainty is `UNCERTAINTY_INTERVAL_NOT_COMPUTED`: the v2 preregistration and the v2 selection procedure do not define a calibration-direction interval, and `direction_result` does not require one. The frozen rule returns `SUPPORTED_DIRECTION`. + +`THRESHOLD_V2_DIRECTION_EVALUATION_001` is `SUPPORTED_DIRECTION`. The next named transition is `THRESHOLD_V2_CONFOUND_EVALUATION_AUTHORIZATION`. This pass does not authorize it. Direction scope is `DIRECTION_ONLY`. Confound analysis did not run. Threshold search did not run. No precision or Wilson calculation ran. Analysis sha256 `b68483acab29fe5918a81281798974fb85000a18af30ec3411c2e30500292783`. Decision sha256 `d408c81ff5766d40ece5c18c2c971ce2e05c23b8163fa9a809f2eded58a852eb`. Tracker sha256 `7a2e3eb6265ddb90624f2ef5034d77f486d1cec83e2c2792d7659a56bd076abe`. Support join sha256 remains `02088379b5ebe0f26106e830a730e8a90b2281d83c37bf1e750e1846bffa128b`. + +The threshold stays null and unfrozen. Measurement stays `SEALED`, unresolved, unscored, and unlabeled. Measurement sha256 remains `78ca09ab14912681d028e8b9b1c77a8daf1d0c8c81561434eb9b6ade45a753e5`. Score sha256 remains `01c758af570264c190f4af7805d43d734c9e9a36779f0e0b82675709f213e07c`. Label sha256 remains `f9901e334543dac2e9bf258208097cf4c9b48af9dc8b497ff42fe4f1f106e1fc`. Resolution sha256 remains `491b854bcf04d20624e560b63c01823b5634716ec266a4d3a7396d5514defd8d`. Failure sha256 remains `946a686d46b5a2fc47bb9968bf6e39c643e93c297d5a64de9e0fe88fc6eb33eb`. `selected_source` remains `none`. Nothing was integrated into runtime. SELECT-005 remains unauthorized. Admitted, settled, and gold stay 0. Events sha256 remains `96b74a92d44f1cf9fe152b18e5207176f161ba3bfce528dac38aa4571a742f9c`. The ledger was not appended. No JSON Schema document exists for this direction family. v1 remains `CLOSED_CALIBRATION_DESIGN_INSUFFICIENT`. +## Threshold v2 calibration completion — 2026-09-28 + +`THRESHOLD_V2_CALIBRATION_COMPLETION_AUTHORIZATION` runs the remaining preregistered calibration gates in order on the frozen support join. It does not recompute residuals, labels, readiness, GlossBERT, MiniLM, or the resolver. It does not redraw. The frozen confound stop remains tier-3 concentration, extreme-driven separation, or a POS partition. Nuisance associations do not add a stop. Extreme-driven is False. POS-partitioned is False. Tier-3 concentrated is False. Confound result is `PASS`. + +Token count, character length, and content-constituent count are copied from the frozen manifest, surface, and score constituents. Candidate-sense count and GlossBERT confidence and margin were not stored on the calibration artifacts, so those associations are `NOT_COMPUTABLE` and GlossBERT was not rerun. Nuisance Spearman results: token_count HIGH -0.111187, SECONDARY 0.356766; character_length HIGH -0.272622, SECONDARY -0.118083; content_count HIGH 0.284901, SECONDARY -0.017198; max_candidate_senses HIGH NOT_COMPUTABLE, SECONDARY NOT_COMPUTABLE; min_glossbert_confidence HIGH NOT_COMPUTABLE, SECONDARY NOT_COMPUTABLE; min_glossbert_margin HIGH NOT_COMPUTABLE, SECONDARY NOT_COMPUTABLE. + +Threshold search evaluated 60 unique ready residuals. Predicted-YES support passed for 50. The precision floor passed for 2. The Wilson lower bound passed for 0. Leave-one-out passed for 0. The REJECT veto passed for 1. The QUARANTINE veto passed for 47. Candidates passing every frozen gate: 0. No candidate passed. The threshold value stays null. Training readiness is `BLOCKED_BY_THRESHOLD_V2_CALIBRATION` because of `NO_THRESHOLD_PASSES_PRECISION_GATE`. Analysis sha256 `8e4c9468537bce4f35f0aee6375c2efd931cea1c54f7a72b9a138bbe8100d930`. Decision sha256 `cff6b3bfbca57b7aa26c5b057ad8e83507ff31d9edb1fd08996ff473670e5991`. Completion search sha256 `2df31d98f4c23b340f0ae1b8747326b6f8b97ed0e82a13ef461c82901c3d56ed`. Threshold artifact sha256 `99ecfdd553f46f34dcc5c3e20c644b0bcbc289ba8f200d9b0407e20874e1a301`. Receipt sha256 `d1221c7c98a926bb04a9ed5ddf5cedf8a3111f0ef0083ec60232eb8709d1b982`. Tracker sha256 `e674c207d9eb160159a673c23f7e2a2e8262b5bfc963ccf81b216d8291dc4b0f`. + +Training readiness is `BLOCKED_BY_THRESHOLD_V2_CALIBRATION`. Training stays unauthorized. A frozen threshold does not authorize SELECT-005, BEST replacement, or model promotion. Measurement stays `SEALED`. Measurement sha256 remains `78ca09ab14912681d028e8b9b1c77a8daf1d0c8c81561434eb9b6ade45a753e5`. The next named transition is `NONE`. This pass does not authorize it. `selected_source` remains `none`. Admitted, settled, and gold stay 0. Events sha256 remains `96b74a92d44f1cf9fe152b18e5207176f161ba3bfce528dac38aa4571a742f9c`. The ledger was not appended. No JSON Schema document exists for this completion family. Execution 001, the direction artifacts, and v1 stay sealed. `THRESHOLD_V2_CALIBRATION_EXECUTION_001` remains `CALIBRATION_INSUFFICIENT_SUPPORT`. +## Threshold artifact schema hardening — 2026-09-28 + +`THRESHOLD_ARTIFACT_SCHEMA_HARDENING_AUTHORIZATION` adds a retrospective JSON Schema family for residual-threshold artifacts that were already emitted. This is contract hardening. It does not repair the calibration. v2 failed because no threshold candidate passed the frozen statistical gates, and that result stays `NO_THRESHOLD_PASSES_PRECISION_GATE`. The threshold value stays null. Frozen artifact bytes and hashes are unchanged. A mismatch is reported as `SCHEMA_CONFORMANCE_GAP` and is not repaired by editing a sealed artifact. + +The schemas describe the contracts as emitted. Historical `json_schema_exists` values stay false. Twenty-six applicable v1 and v2 artifacts validate, and eighteen cross-artifact checks pass. The support join has 200 rows. Ready plus unknown is 200. The calibration manifest, operator labels, and support join share one row-id set of 200. The measurement manifest stays disjoint and its sha256 is unchanged. Referenced manifest, label, score, and support-join hashes match the files they name. v1 labels remain unlabeled. Thirty other residual-threshold files stay outside this family, including preregistrations, the older field contracts, labels, scores, and resolutions. Those field contracts are not JSON Schema and were not rewritten. The conformance outcome is `PASS`. `schema_conformance_gaps` is empty. + +The family is `specs/007-hyperlexical-model/schemas/residual-threshold/residual-threshold-common.schema.json`, `residual-threshold-surface-manifest.schema.json`, `residual-threshold-receipt.schema.json`, `residual-threshold-support-join.schema.json`, `residual-threshold-direction-analysis.schema.json`, `residual-threshold-confound-analysis.schema.json`, `residual-threshold-threshold-search.schema.json`, `residual-threshold-decision.schema.json`, and `residual-threshold-completion-receipt.schema.json`. Shared terms live in `$defs`: sha256, row_id, pwn30_synset, operator_label, row_resolution_status, threshold_state, measurement_state, and artifact_reference. An unfrozen threshold record requires `threshold_value` null. A frozen record requires a numeric threshold and its frozen metrics. No frozen success record was emitted; that branch is contract-only. The public report is `specs/007-hyperlexical-model/schemas/residual-threshold/CONFORMANCE.json`. + +Measurement stays `SEALED`. Measurement sha256 remains `78ca09ab14912681d028e8b9b1c77a8daf1d0c8c81561434eb9b6ade45a753e5`. Tracker sha256 remains `e674c207d9eb160159a673c23f7e2a2e8262b5bfc963ccf81b216d8291dc4b0f`. Events sha256 remains `96b74a92d44f1cf9fe152b18e5207176f161ba3bfce528dac38aa4571a742f9c`. The ledger was not appended. Training stays unauthorized. `selected_source` remains `none`. SELECT-005 remains unauthorized. Admitted, settled, and gold stay 0. The next named transition remains `NONE`. This pass does not authorize measurement, another threshold search, or a new residual design. +## Threshold v2 postmortem and contract split — 2026-09-28 + +`THRESHOLD_V2_POSTMORTEM` records the canonical v2 conclusion without rewriting sealed artifacts. Surface design and support were adequate. Direction was positive and weak: AUC 0.580420, rank-biserial 0.160839, ready HIGH 13, ready SECONDARY 22. Confound passed. Precision and Wilson were insufficient: 2 candidates met the precision floor and 0 met the Wilson floor. The conclusion is `CURRENT_RESIDUAL_REPRESENTATION_INSUFFICIENT_FOR_REQUIRED_PRECISION`. The threshold value stays null. Measurement stays `SEALED` at sha256 `78ca09ab14912681d028e8b9b1c77a8daf1d0c8c81561434eb9b6ade45a753e5`. Tracker sha256 remains `e674c207d9eb160159a673c23f7e2a2e8262b5bfc963ccf81b216d8291dc4b0f`. The full seal is `specs/007-hyperlexical-model/threshold-v2-postmortem.md`. + +Workstream A, the threshold artifact contract, is separate from workstream B. The contract family is `specs/007-hyperlexical-model/schemas/hyperlex/residual-threshold/`. Validation is `VALIDATE_ONLY`. The outcome is `HISTORICAL_VALIDATION_COMPLETE`: 30 applicable artifacts and 22 semantic checks pass, and both gap lists are empty. The contract note is `specs/007-hyperlexical-model/threshold-artifact-schema-contract.md`. The retrospective family from the previous hardening pass is unchanged. + +Workstream B drafts `RUNE.SEMANTIC_COMPOSITIONALITY_EVIDENCE.v2` at state `SEMANTIC_EVIDENCE_V2_SPEC_DRAFTED`. It does not preregister a model, draw a surface, or open a new threshold lane. The v2 measurement disposition is `RETAIN_FOR_V2_ONLY`. Specifications are `semantic-evidence-v2.md`, `semantic-evidence-v2-state-machine.md`, `semantic-evidence-v2-data-contract.md`, and `semantic-evidence-v2-evaluation-contract.md`. + +`TRAINING_DEPENDENCY_REVIEW` finds `SEMANTIC_THRESHOLD_NOT_A_HARD_TRAINING_DEPENDENCY`. SELECT-005 is a schedule experiment blocked by the missing fresh reserve and the unsealed admission receipt, not by the residual threshold. `HLX_THRESHOLD_AUTHORIZATION` seals numeric select-metric thresholds and is a different object. The residual lane flag `BLOCKED_BY_THRESHOLD_V2_CALIBRATION` stays on that lane. This review does not authorize training, SELECT-005, measurement, or a redraw. Events sha256 remains `96b74a92d44f1cf9fe152b18e5207176f161ba3bfce528dac38aa4571a742f9c`. The ledger was not appended. The review is `specs/007-hyperlexical-model/training-dependency-review.md`. +## SELECT-005 reserve and admission preparation — 2026-09-28 + +`SELECT_005_RESERVE_AND_ADMISSION_PREPARATION_AUTHORIZATION` re-censused the live settlement stream for `HLX-EXP-2026-09-27-SELECT-005`. The id stays unsealed. No preregistration file, arm directory, reserve binding, or admission receipt was created. The ledger was not appended. Events sha256 remains `96b74a92d44f1cf9fe152b18e5207176f161ba3bfce528dac38aa4571a742f9c`. Projection sha256 remains `77e22433203879b252f7a9e309d2013d7550101d1c4a014b494d2c96df87d0e0`. + +The proposed schedule is unchanged and is not sealed. Its canonical record sha256 is `ffb7f0399d89cd262319ab216104e60e97cf500efeda76a31b9f47a8262f2fd0`. Control remains `max_epochs=40` with early stopping disabled and restore best. Candidate remains `max_epochs=12`, `minimum_epochs=4`, `early_stopping_patience=4`, strict increase, ties keep the earlier checkpoint, and restore best. Both arms still pin `classify_macro_f1_nonnone`, warm start `hyperlex-encoder-modernbert-base-seed-morph65`, and export sha256 `64b7d3dede25047cb6dd2e5b663f7fa72946ec82ac1a8816ae34622d1aaac430` at 9150 rows. Learning rate, batch size, gradient accumulation, and seed stay unset. They were not filled in. The admission schedule shape already names the four `train_schedule` fields, so this pass does not stop as `SELECT_005_SPEC_INCOMPLETE` and does not seal a preregistration. + +`HLX_THRESHOLD_AUTHORIZATION` for this experiment id is absent. Decision thresholds stay `BLOCKED_PENDING_OPERATOR_AUTHORIZATION`. No numeric floor was derived from the fresh stream or from SELECT-004. SELECT-004 preservation floors do not transfer. No candidate metric was scored. Admission here is pre-training, and there is no candidate checkpoint for this id. + +The source universe is the 339 settlement events in `settlement/events.jsonl`. Eligible identities after the frozen exclusions: 0. Exclusion counts are `EVAL_SPENT` 241, `DECISION_UNRESOLVED` 84, and `RIGHTS_UNRESOLVED` 14. The 98 settlements absent from the ledger and from the pinned export are 82 `UNRESOLVED` with `CC-BY-SA`, 2 `UNRESOLVED` with `RIGHTS_UNRESOLVED`, 7 `ACCEPT` with `RIGHTS_UNRESOLVED`, 6 `NONE` with `RIGHTS_UNRESOLVED`, and 1 `RECLASSIFY` with `RIGHTS_UNRESOLVED`. The pinned export still has 6761 unique hashes, all already in the ledger. Ledger identities remain 7964: `TRAIN_CONSUMED` 6763, `EVAL_SPENT` 949, `EVAL_ABANDONED` 252. Active `EVAL_RESERVE` is 0. The SELECT-004 spent reserve is the 491 identities that still carry `evaluation_reserved`. It was not reused. SELECT-001 and SELECT-002 bindings cover 855 other identities and were not reused. Packet `HLX-EVAL-REVIEW-2026-09-27-001` has 84 rows and 0 operator decisions, so it is not a settlement. WordNet was not admitted alone. Required provenance fields are present on every settlement event, and none of those events are eligible. + +Determinism is `IDENTICAL` across two reconstructions. Frozen reserve rows: 0. No reserve manifest was written. The quota result is `RESERVE_QUOTA_UNFILLED`. Provenance result is `NO_ADMITTED_ROWS`. Final-reserve overlap is 0 for train, the prior SELECT reserve, SELECT-001/002 bindings, threshold-v1 calibration, threshold-v2 calibration, and threshold-v2 measurement, because no row was frozen. The 98 novel settlements also overlap those fences by 0 on normalized identity, row id, source identity, and synset. + +Admission result is `RESERVE_QUOTA_UNFILLED`. An admission receipt was not created. Private failure sha256 `4b69c4756d0ce56a6009b9cf6a22ed002fc108193010e3360fb9f3a61a10d97e`. Census sha256 `bc8152c3935dcc66d157edee8ed8275cd2acd3449318a009cb5db58b1f60df3c`. Isolation sha256 `38e3bdc4210da496597d52cb2a055a5e182e964a1aace5c8c4166ade0862e03e`. Provenance sha256 `d93611e9d73ebf42973da3ba8fbfa663ab598d6af0953b91a4c6f8add4d534de`. `training_authorized` is false. `training_launch_authorized` is false. Epochs and gradient steps remain 0. BEST was not moved. It still names `hyperlex-encoder-modernbert-base-seed-select004`, weights sha256 `9fba0f66b1d5de6492470f53577d1447bfac1d29b9ac03869268abb70bbd97f6`. The named warm start was not modified, weights sha256 `96838b9656a84c3fee1773a41fdf2fbbec2f88f3bc948e4acfbe06c194ac5587`. Trunk sha256 remains `340ac08b74eef0d7bdec2d7981a6a3d4249bf0e6aab60634b72ad02c2b8023a9`. The next legal transition is `NONE`. + +No SELECT admission JSON Schema document exists. `SELECT_ADMISSION_JSON_SCHEMA_EXISTS` is false. The code receipt name `hyperlex.admission.v1` is not that document. Threshold v2 measurement stays `SEALED` at sha256 `78ca09ab14912681d028e8b9b1c77a8daf1d0c8c81561434eb9b6ade45a753e5`. Tracker sha256 remains `e674c207d9eb160159a673c23f7e2a2e8262b5bfc963ccf81b216d8291dc4b0f`. `RUNE.SEMANTIC_COMPOSITIONALITY_EVIDENCE.v2` stays `SEMANTIC_EVIDENCE_V2_SPEC_DRAFTED`. The residual threshold lanes were not modified. +## SELECT-005 reserve supply and governance — 2026-09-28 + +`SELECT_005_RESERVE_SUPPLY_AND_GOVERNANCE_AUTHORIZATION` reviewed the 10 ready rows in `HLX-EVAL-REVIEW-2026-09-27-001` and did not fill the reserve. The 69 rights-blocked rows, 4 provenance-blocked rows, and 1 duplicate stayed out. Review-ready manifest sha256 `8c1c762e43307e31d124e4dd2309adc17ea6243789aee2ea8e694b2b7bbd68e9`. Operator decisions sha256 `a4653ac2c5ee125fea069b3dfbdd4b40950eb8ead2ad032158a502ce886ef7d6`. Decision receipt sha256 `776df14f889098cfa4bfbbbbf5869e5775076806113bdbb53d6f1d44c7793307`. Counts are `ACCEPT` 1, `NONE` 5, `UNRESOLVED` 4, `RECLASSIFY` 0. The stored registry class is `INFERRED`. It was not promoted to `OBSERVED`. + +Settlement apply refused all 10. None are in held-out stream `hs-20260925T211358Z`. `rights_of` has no mapping for `MIT-examples`. Application sha256 `0a29f0c12403fdbd0b794fee323a83c96a20c2195d37ff79bf158a606c0e0724`. The settlement log stays at 339 events. Events sha256 remains `96b74a92d44f1cf9fe152b18e5207176f161ba3bfce528dac38aa4571a742f9c`. Ledger sha256 remains `77e22433203879b252f7a9e309d2013d7550101d1c4a014b494d2c96df87d0e0`. The post-review census is unchanged: universe 339, eligible 0, exclusions `EVAL_SPENT` 241, `DECISION_UNRESOLVED` 84, `RIGHTS_UNRESOLVED` 14. Routed slice counts are 0, 0, 0, 0. The quota result remains `RESERVE_QUOTA_UNFILLED`. Missing slices are `classify`, `classify_observed`, `classify_non_none`, and `unbind_clean`. No reserve manifest and no admission receipt were created. The previous failure sha256 `4b69c4756d0ce56a6009b9cf6a22ed002fc108193010e3360fb9f3a61a10d97e`, census sha256 `bc8152c3935dcc66d157edee8ed8275cd2acd3449318a009cb5db58b1f60df3c`, isolation sha256 `38e3bdc4210da496597d52cb2a055a5e182e964a1aace5c8c4166ade0862e03e`, and provenance sha256 `d93611e9d73ebf42973da3ba8fbfa663ab598d6af0953b91a4c6f8add4d534de` were not overwritten. + +The SELECT-005 preregistration is `SELECT_005_PREREGISTRATION_INCOMPLETE`, sha256 `1cc6701940b5b3458f9dc7ede1864a2fc3b0f7bed526c5a0fb996a620a54f1c0`, sealed false. Unresolved fields are learning rate, batch size, gradient accumulation, and seed. SELECT-004 values were not inherited. `HLX_THRESHOLD_AUTHORIZATION` stays `BLOCKED_PENDING_OPERATOR_AUTHORIZATION`. Proposal sha256 `8bdc85ec1e610ac512aefc9eaeb3f5d6b5caebef04dde959d22b883c3eab4a4b`. Every numeric threshold is null. No SELECT-004 floor was copied. `training_authorized` is false. `training_launch_authorized` is false. Epochs and gradient steps remain 0. BEST was not moved. The next legal transition is `NONE`. + +No SELECT admission JSON Schema exists. No threshold-authorization JSON Schema exists. Threshold v2 measurement stays `SEALED` at sha256 `78ca09ab14912681d028e8b9b1c77a8daf1d0c8c81561434eb9b6ade45a753e5`. Tracker sha256 remains `e674c207d9eb160159a673c23f7e2a2e8262b5bfc963ccf81b216d8291dc4b0f`. `RUNE.SEMANTIC_COMPOSITIONALITY_EVIDENCE.v2` stays `SEMANTIC_EVIDENCE_V2_SPEC_DRAFTED`. +## SELECT-005 source and preregistration completion — 2026-09-28 + +`SELECT_005_SOURCE_AND_PREREGISTRATION_COMPLETION_AUTHORIZATION` did not recensus the 339 settlement events. Run `hs-20260925T211358Z` is not a source. No row from that universe was reviewed again. The settlement log was not appended. Its sha256 remains `d8a400b68de95ccacb5a3c4069765182ed0bb1a3099238f5351643e4ed35c377`. Identity-ledger events sha256 remains `96b74a92d44f1cf9fe152b18e5207176f161ba3bfce528dac38aa4571a742f9c`. Projection sha256 remains `77e22433203879b252f7a9e309d2013d7550101d1c4a014b494d2c96df87d0e0`. The previous failure sha256 `4b69c4756d0ce56a6009b9cf6a22ed002fc108193010e3360fb9f3a61a10d97e`, census sha256 `bc8152c3935dcc66d157edee8ed8275cd2acd3449318a009cb5db58b1f60df3c`, isolation sha256 `38e3bdc4210da496597d52cb2a055a5e182e964a1aace5c8c4166ade0862e03e`, and provenance sha256 `d93611e9d73ebf42973da3ba8fbfa663ab598d6af0953b91a4c6f8add4d534de` were not overwritten. The supply-001 preregistration sha256 `1cc6701940b5b3458f9dc7ede1864a2fc3b0f7bed526c5a0fb996a620a54f1c0` stays `SELECT_005_PREREGISTRATION_INCOMPLETE`. + +The training initialization is `morph65_new_schedule`. Both arms warm-start `hyperlex-encoder-modernbert-base-seed-morph65`, weights sha256 `96838b9656a84c3fee1773a41fdf2fbbec2f88f3bc948e4acfbe06c194ac5587`. The rejected alternative is `select004_best_new_schedule`. BEST was not moved. It still names `hyperlex-encoder-modernbert-base-seed-select004`, weights sha256 `9fba0f66b1d5de6492470f53577d1447bfac1d29b9ac03869268abb70bbd97f6`. The trainer default did not choose. Initialization decision sha256 `fcc261039977b6580c6025ff7debb1a240cee9158b2f7a803537c4bbb745ac65`. + +The successor preregistration is `SELECT_005_PREREGISTRATION_SEALED`, sha256 `c2dcf3b703f50cb8eb80bb6c2c45335071ded3c4ce89cc669e8a869eb3498baf`, sealed true. The scientific variable remains `train_schedule`. Control remains `max_epochs=40` with early stopping disabled. Candidate remains `max_epochs=12`, `minimum_epochs=4`, `early_stopping_patience=4`, strict increase, ties keep the earlier checkpoint, and restore best. Both arms still pin `classify_macro_f1_nonnone` and export sha256 `64b7d3dede25047cb6dd2e5b663f7fa72946ec82ac1a8816ae34622d1aaac430` at 9150 rows. Held constant from the morph65 continuation control recipe, environment sha256 `edb7163f388de71c258db2d68d03ac4e6d6a011548236666250d5d0f5d05e0f1`: learning rate `2e-5`, batch size 8, gradient accumulation 1, and seed policy `HLX_SEED_UNSET_FROZEN`. Gradient accumulation 1 records that the trainer takes one optimizer step per batch. The seed stays unset because the reconstructed recipe seals `HLX_SEED` unset, and that unset state is now an explicit shared policy rather than a missing field. `inherited_from_select_004` is false. SELECT-004's scientific variable `HLX_SELECT_METRIC` does not transfer. The same optimizer numbers appear in the SELECT-004 candidate environment; that coincidence is recorded and is not the authority. + +`HLX_THRESHOLD_AUTHORIZATION` stays `BLOCKED_PENDING_OPERATOR_AUTHORIZATION`. Proposal sha256 `dc926d61e16b50ea4f15a73110957d54f3718e28778d7456e5c39bc6bf35755e`. The comparison baseline is the SELECT-005 control arm, not seed-morph78. The schedule design supports strict improvement of `classify_macro_f1_nonnone` and does not assign a number. `classification_accuracy`, `observed_label_accuracy`, and `unbind_clean_exact` stay null. No SELECT-004 floor was copied. `decision_thresholds` is empty. The proposal is not sealed, so admission cannot point `HLX_THRESHOLD_AUTHORIZATION` at it. + +The fresh-reserve contract admits only `wiktionary_category` and `wikipedia_prose`, rights token `CC-BY-SA`. Princeton WordNet 3.0 stays compositional and is not that token, so it is not relabeled and is not admitted alone. The label-blind surface carries identity, license, and source URL, and refuses task, class, lineage, decision, and `unbind_clean`. Contract sha256 `8288c6a4f0618c0880a8bbab9acf072b53c6d7b968a7cc7f64f3cae7d8610c22`. The held-out harvester refuses when the box exposure snapshot is older than 6 hours. The newest snapshots are 71.75 hours old. This pass did not bypass that fence and did not fetch. Rows acquired: 0. The label-blind surface is empty, sha256 `e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855`. Isolation is `NOT_RUN_NO_ROWS`. Provenance is `NO_ADMITTED_ROWS`. Settlements appended: 0. + +A decision-only settlement record still has no task, class, lineage, or `unbind_clean`. The census calls that `SLICE_LABELS_ABSENT` and does not freeze a partial reserve. This pass does not attach those fields beside the settlement contract. The fresh census universe is 0, not 339. Census sha256 `d945bbe02d0aa9640a2d276a274e6fcbe3d856f84cca7a1d4920e4b9e8c2f079`. Routed slice counts are 0, 0, 0, 0. The quota result is `RESERVE_QUOTA_UNFILLED`. No reserve manifest and no admission receipt were created. Completion receipt sha256 `6065baf43db3f4013eb6f39e4e8a7293fb9dead062c17497be03245f4daa6760`. + +`training_authorized` is false. `training_launch_authorized` is false. Epochs and gradient steps remain 0. No optimizer was constructed. Trunk sha256 remains `340ac08b74eef0d7bdec2d7981a6a3d4249bf0e6aab60634b72ad02c2b8023a9`. `loop.py` sha256 remains `1aa395081d7709be3844bf2568d12100d73d931ecbc51af43c3d6e01dba77e2a`. `admission.py` sha256 remains `6ae7b63ca1b6e0459d9b795c731668b3eff524269d81bcb92181c0c9d407ab78`. `identity_ledger.py` sha256 remains `ac49b7240a895952b99b3ef9046f7a8185c8647badac7a45c8640d592057d1c5`. No SELECT admission JSON Schema exists. No threshold-authorization JSON Schema exists. Threshold v2 measurement stays `SEALED` at sha256 `78ca09ab14912681d028e8b9b1c77a8daf1d0c8c81561434eb9b6ade45a753e5`. Tracker sha256 remains `e674c207d9eb160159a673c23f7e2a2e8262b5bfc963ccf81b216d8291dc4b0f`. `RUNE.SEMANTIC_COMPOSITIONALITY_EVIDENCE.v2` stays `SEMANTIC_EVIDENCE_V2_SPEC_DRAFTED`. `semantic_evidence_v2.py` sha256 remains `9f5e5d6d5106d2af709c86486d302fed3c11a0de7cb4cf4f2413897882e66348`. The next legal transition is `NONE` until a fresh box exposure snapshot exists and an operator supplies numeric SELECT-005 thresholds that are not taken from SELECT-004. +## SELECT-005 settlement routing contract — 2026-09-28 + +`SELECT_005_SETTLEMENT_ROUTING_CONTRACT_EXTENSION_AUTHORIZATION` freezes a derivative routing contract. Canonical settlement stays separate from SELECT routing. Existing settlement events were not rewritten. The settlement log sha256 remains `d8a400b68de95ccacb5a3c4069765182ed0bb1a3099238f5351643e4ed35c377`. Identity-ledger events sha256 remains `96b74a92d44f1cf9fe152b18e5207176f161ba3bfce528dac38aa4571a742f9c`. Projection sha256 remains `77e22433203879b252f7a9e309d2013d7550101d1c4a014b494d2c96df87d0e0`. No routing event was written under `eval-reserve-20260926/routing/`. The 339-event census was not rerun. No source was harvested. The six-hour exposure fence was not bypassed. + +The contract is `hyperlex.eval_routing.v1`. Spec sha256 `002e2ac67e5ee00c27ecedd4566b9e7d900852c51e3ac57aac957ac095899d96`. Procedure `select_eval_routing_v1` sha256 `ad187d262eba280dd4b88d2204e03d4dfb255f9b0fb443bd250a0e441a7ea6db`. Routing schema sha256 `147e2787e6e66ba47acd185fa6602a10bd5d57c15ea446f574b2e750277d31a0`. Threshold-authorization schema sha256 `35a1b70cc805cf4271831a2414fd9ac85dcc15e5d23ec951d2d37665e89109a1`. Admission schema sha256 `6f20efe73b74207243474724261c985d4e6dde888df497a1ef723dda02a42077`. Conformance report sha256 `fac5dacf06f1257137bfd72b91c728191840ce291d2c8e953292f4313dddcd2b`. Contract receipt sha256 `65d79c90b5654e01b3dc205600eda349855476766a39f7ebda2d7b66ce7f5e06`. + +`task` is `classify` or `unbind`. `class` is the registry attest, `OBSERVED` or `INFERRED`. `lineage` is the semantic family, including `none`. `classify_observed` requires registry class `OBSERVED`. `classify_non_none` requires a lineage other than empty or `none`. `unbind_clean` is true only when `task` is `unbind` and the frozen derivation `soft_ceiling.clean_surface` is true. One record may enter every classify slice it satisfies. `unbind_clean` does not share a record with those slices because `task` is single-valued. `INFERRED` is not promoted to `OBSERVED`. `ACCEPT` and `RECLASSIFY` are routable. `NONE` can enter `classify` and, when the frozen flag is true, `unbind_clean`, and cannot enter `classify_non_none`. `UNRESOLVED` is ineligible. Rights other than `CC-BY-SA` are ineligible. Incomplete provenance is ineligible. Routing does not read reserve deficits, desired slices, routed counts, or candidate scores. The reference corpus reconstructed twice is `DETERMINISM = IDENTICAL`. State is `ROUTING_VALIDATION_COMPLETE`. + +Census may consult a validated routing record only when the settlement itself would otherwise be `SLICE_LABELS_ABSENT`. Settlement exclusions still win. `admit_training_run` still consumes a sealed reserve binding and does not require routing records, so SELECT-004 bindings stay valid. Historical schema checks are `VALIDATE_ONLY`. Result `SCHEMA_CONFORMANCE_GAP`: `exp-20260926-select-003/threshold-authorization.json` lacks `sealed` and `decision_thresholds`. That file was not rewritten. SELECT-004 admission artifacts and the SELECT-004 and SELECT-005 threshold documents validate. No SELECT-005 numeric threshold was sealed. `HLX_THRESHOLD_AUTHORIZATION` remains `BLOCKED_PENDING_OPERATOR_AUTHORIZATION`. No SELECT-005 admission receipt was created. + +`training_authorized` is false. `training_launch_authorized` is false. Epochs and gradient steps remain 0. No optimizer was constructed. Weights were not mutated. BEST was not changed. Threshold v2 measurement stays `SEALED` at sha256 `78ca09ab14912681d028e8b9b1c77a8daf1d0c8c81561434eb9b6ade45a753e5`. The next productive transition is `SELECT_005_FRESH_SOURCE_HARVEST_AND_ROUTING_AUTHORIZATION`, and only after a box exposure snapshot is no older than 6 hours. The metric-threshold blocker stays separate. +## SELECT-005 fresh harvest stopped — exposure snapshot stale — 2026-09-28 + +`SELECT_005_FRESH_SOURCE_HARVEST_AND_ROUTING_AUTHORIZATION` measured the newest box exposure pair and stopped before any source fetch. Snapshot id `20260925T211351Z`. Comparison timestamp unix `1790631188.652030` (`2026-09-28T21:33:08Z`). The vern snapshot `/home/morpheus/hlx-private/heldout-stream/inbox/vern-texts-20260925T211351Z.jsonl` sha256 `33dee5ccd2162ccfe5d2f200fdf6dd034dcd95aaa9ac4cc2b422957c06b14d17` is 72.319854 hours old. The box-hash snapshot `/home/morpheus/hlx-private/heldout-stream/inbox/box-jev-hashes-20260925T211351Z.txt` sha256 `0fd1e9d9aeedd982c1913d2c8a88b72e2e03c95a6177e7dcbddc9189df6811de` is 72.320210 hours old. The fence is 6 hours. The binding age is 72.320210 hours. Result `EXPOSURE_SNAPSHOT_STALE`. Receipt sha256 `ce20e600ebeb18d1989f4c673c95c2901143be2e780035b3a965b815a212bbdb`. Network requests: 0. Rows acquired: 0. No raw harvest, review surface, settlement append, routing record, reserve manifest, or admission receipt was created. + +The routing contract was not modified. Spec sha256 remains `002e2ac67e5ee00c27ecedd4566b9e7d900852c51e3ac57aac957ac095899d96`. Procedure `select_eval_routing_v1` sha256 remains `ad187d262eba280dd4b88d2204e03d4dfb255f9b0fb443bd250a0e441a7ea6db`. Routing schema sha256 remains `147e2787e6e66ba47acd185fa6602a10bd5d57c15ea446f574b2e750277d31a0`. Settlement log sha256 remains `d8a400b68de95ccacb5a3c4069765182ed0bb1a3099238f5351643e4ed35c377`. Identity-ledger events sha256 remains `96b74a92d44f1cf9fe152b18e5207176f161ba3bfce528dac38aa4571a742f9c`. Projection sha256 remains `77e22433203879b252f7a9e309d2013d7550101d1c4a014b494d2c96df87d0e0`. Preregistration sha256 remains `c2dcf3b703f50cb8eb80bb6c2c45335071ded3c4ce89cc669e8a869eb3498baf`. `HLX_THRESHOLD_AUTHORIZATION` remains `BLOCKED_PENDING_OPERATOR_AUTHORIZATION`. The SELECT-003 schema gap was not repaired. + +`training_authorized` is false. `training_launch_authorized` is false. Epochs and gradient steps remain 0. No optimizer was constructed. Weights were not mutated. BEST remains `hyperlex-encoder-modernbert-base-seed-select004`, sha256 `9fba0f66b1d5de6492470f53577d1447bfac1d29b9ac03869268abb70bbd97f6`. Threshold-v2 measurement stays `SEALED` at sha256 `78ca09ab14912681d028e8b9b1c77a8daf1d0c8c81561434eb9b6ade45a753e5`. The next legal transition is the same harvest authorization, and only after a box exposure snapshot is no older than 6 hours. The metric-threshold blocker stays separate. +## SELECT-005 threshold authorization — 2026-09-28 + +`SELECT_005_THRESHOLD_AUTHORIZATION` seals `HLX_THRESHOLD_AUTHORIZATION` for `HLX-EXP-2026-09-27-SELECT-005`. State `SEALED`. Artifact sha256 `e0d8d81b6392692784d92542d0cc5aa6ee6756b93c44bab86e2167ef57cfbc55`. Receipt sha256 `c444977805717f7548744f7df435cdcd899a5daee1fc1a15967b02740cbfaf9e`. Derivation sha256 `ce6fcfb46b7b73f4b039649f8b182fe90aa8a196db76e46ddbc4a1a6bd0fb860`. Schema validation `PASS`, report sha256 `7e200ae1c6f3619a0aee0c4c29106077f7a647ce74ebca143ff77e815d070772`. Semantic validation `PASS`, report sha256 `1b724ca6e86ef8e158f60169dad9b057fe3eff26af343100c2aa855b22cb21cd`. The JSON Schema sha256 remains `35a1b70cc805cf4271831a2414fd9ac85dcc15e5d23ec951d2d37665e89109a1`. + +The comparison baseline is the SELECT-005 control arm. The comparison candidate is the SELECT-005 candidate arm. Both are scored as candidate minus control. `classify_macro_f1_nonnone` uses operator `GT` and threshold `0`, so a tie fails. `classification_accuracy`, `observed_label_accuracy`, and `unbind_clean_exact` use operator `GE` and threshold `0`, so equality at the floor passes and any degradation fails. A primary gain cannot compensate for a failed guardrail. A missing or non-computable required metric fails. The numbers are zero because the sealed schedule and `note_strict_improvement` already require `score > best_value`, and the paired arms share one count-ratio scorer. SELECT-005 has no sealed reserve size, so a positive degradation band cannot be computed from 1/N. SELECT-004 floors 0.02, 0.05, and 0.01 were not copied. `inherited_from_select_004` is false. The unsealed proposal sha256 `dc926d61e16b50ea4f15a73110957d54f3718e28778d7456e5c39bc6bf35755e` was not overwritten. Preregistration sha256 remains `c2dcf3b703f50cb8eb80bb6c2c45335071ded3c4ce89cc669e8a869eb3498baf`. + +This seal is not a training launch and not an admission receipt. `training_authorized` is false. `training_launch_authorized` is false. Epochs and gradient steps remain 0. No optimizer was constructed. Weights were not mutated. BEST remains `hyperlex-encoder-modernbert-base-seed-select004`, sha256 `9fba0f66b1d5de6492470f53577d1447bfac1d29b9ac03869268abb70bbd97f6`. The reserve remains `RESERVE_QUOTA_UNFILLED`. No harvest ran and the exposure snapshot was not refreshed. Threshold-v2 measurement stays `SEALED` at sha256 `78ca09ab14912681d028e8b9b1c77a8daf1d0c8c81561434eb9b6ade45a753e5`. The SELECT-003 schema gap was not repaired. The next legal transition is `SELECT_005_FRESH_SOURCE_HARVEST_AND_ROUTING_AUTHORIZATION`, and only after a box exposure snapshot is no older than 6 hours. This pass does not run it. +## SELECT-005 exposure snapshot refresh — 2026-09-28 + +`SELECT_005_EXPOSURE_SNAPSHOT_REFRESH_AUTHORIZATION` did not generate a successor snapshot. State `EXPOSURE_REFRESH_WORKFLOW_UNAVAILABLE`. Receipt sha256 `8c5477d61f938cfce6f6838f24bdad104e3d643e0b97bf7399c640e088c03b38`. + +The canonical procedure is `/workspace/deliverables/hyperlex-heldout-stream/bin/heldout-stream-daily`, run on the box. It is absent on `spark-bf46`. `/workspace/deliverables` does not exist here. No SSH host is configured to the box. `vernacular.sqlite3` is not on this host. `hs_run.py` only reads `vern-texts-.jsonl` and `box-jev-hashes-.txt`; it does not generate them. The documented daily command also runs `hs_run.py run`, and this pass did not start that harvest. No snapshot id was minted. The previous pair `20260925T211351Z` was not overwritten and was not reused. Vern sha256 remains `33dee5ccd2162ccfe5d2f200fdf6dd034dcd95aaa9ac4cc2b422957c06b14d17`. Box-hash sha256 remains `0fd1e9d9aeedd982c1913d2c8a88b72e2e03c95a6177e7dcbddc9189df6811de`. + +No harvest, reserve recensus, settlement append, or training occurred. Preregistration sha256 remains `c2dcf3b703f50cb8eb80bb6c2c45335071ded3c4ce89cc669e8a869eb3498baf`. Threshold authorization sha256 remains `e0d8d81b6392692784d92542d0cc5aa6ee6756b93c44bab86e2167ef57cfbc55`. The routing contract was not modified. Settlement log sha256 remains `d8a400b68de95ccacb5a3c4069765182ed0bb1a3099238f5351643e4ed35c377`. Identity-ledger events sha256 remains `96b74a92d44f1cf9fe152b18e5207176f161ba3bfce528dac38aa4571a742f9c`. Projection sha256 remains `77e22433203879b252f7a9e309d2013d7550101d1c4a014b494d2c96df87d0e0`. `training_authorized` is false. `training_launch_authorized` is false. Epochs and gradient steps remain 0. BEST remains `hyperlex-encoder-modernbert-base-seed-select004`, sha256 `9fba0f66b1d5de6492470f53577d1447bfac1d29b9ac03869268abb70bbd97f6`. Threshold-v2 measurement stays `SEALED` at sha256 `78ca09ab14912681d028e8b9b1c77a8daf1d0c8c81561434eb9b6ade45a753e5`. The next legal transition stays `SELECT_005_FRESH_SOURCE_HARVEST_AND_ROUTING_AUTHORIZATION`, and it is not executable until a box-side snapshot-only run produces a new pair no older than 6 hours. This pass does not run it. +## SELECT-005 Notion vernacular source — 2026-09-28 + +The vernacular registries are in Notion. Vernacular Intelligence (`collection://d0ae52bd-a4ef-4ece-b06e-37078f533a7b`) has 44 rows and 43 distinct titles, created 2026-09-07 through 2026-09-27. Vernacular Events (`collection://785c124f-ecd7-41ae-9553-8a43967bac92`) has 120 rows, all source X and itemized, with 0 source URLs. There is no candidates database and no observations database. The box hash list remains the over-inclusive Jev-lane exposure set. Discovery, Obsidian, and the Jev-spike exports are not in these registries, and `heldout-stream-daily` is still absent on `spark-bf46`. Hashing the Notion rows into `box-jev-hashes` would narrow that set. Writing a vern file alone would leave the newest vern id unbound from the newest box id. No successor snapshot was written. State remains `EXPOSURE_REFRESH_WORKFLOW_UNAVAILABLE`. Receipt sha256 `ae37022242000400217cf2d959f0a339fdde9a1a8a4ae213cc348d079afd5e0b`. + +The previous pair `20260925T211351Z` was not overwritten and was not copied forward. Vern sha256 remains `33dee5ccd2162ccfe5d2f200fdf6dd034dcd95aaa9ac4cc2b422957c06b14d17`. Box-hash sha256 remains `0fd1e9d9aeedd982c1913d2c8a88b72e2e03c95a6177e7dcbddc9189df6811de`, the same bytes as `20260925T205240Z` and the audit pin. No harvest, reserve recensus, settlement append, or training occurred. Preregistration sha256 remains `c2dcf3b703f50cb8eb80bb6c2c45335071ded3c4ce89cc669e8a869eb3498baf`. Threshold authorization sha256 remains `e0d8d81b6392692784d92542d0cc5aa6ee6756b93c44bab86e2167ef57cfbc55`. Settlement log sha256 remains `d8a400b68de95ccacb5a3c4069765182ed0bb1a3099238f5351643e4ed35c377`. Identity-ledger events sha256 remains `96b74a92d44f1cf9fe152b18e5207176f161ba3bfce528dac38aa4571a742f9c`. Projection sha256 remains `77e22433203879b252f7a9e309d2013d7550101d1c4a014b494d2c96df87d0e0`. Sense-screen hypothesis sha256 remains `e674c207d9eb160159a673c23f7e2a2e8262b5bfc963ccf81b216d8291dc4b0f`. BEST remains `hyperlex-encoder-modernbert-base-seed-select004`, sha256 `9fba0f66b1d5de6492470f53577d1447bfac1d29b9ac03869268abb70bbd97f6`. `training_authorized` is false. Epochs and gradient steps remain 0. The next legal transition stays `SELECT_005_FRESH_SOURCE_HARVEST_AND_ROUTING_AUTHORIZATION`, and it is not executable until a box-side snapshot-only run produces a new bound pair no older than 6 hours. This pass does not run it.