From 3dd6b74228c2e213dedc1c941588026519dae8f5 Mon Sep 17 00:00:00 2001 From: song Date: Thu, 17 Sep 2026 15:07:05 +0800 Subject: [PATCH] chore(semantics): lock two inventory ratchets and disclose budget slack conflicting_values 18->16 and conflicting_definitions 59->55 pin the measured values #4499 earned; both sides of each budget move in this one diff so the equality anchor holds. multi_value_twins stays 19 here: the multi-value single-source batch (#4617) owns that counter and locks it to 13. The inventory report line now discloses unlocked headroom (slack=key=N). The guard only fails on overflow, so a merge that reverts a tightened budget (registry and anchor move back together, the merge-trap shape) used to pass silently; now the reopened headroom is visible in the smoke output and in the PR diff of any run after it. Signed-off-by: song --- examples/semantic-vocabulary-drift-smoke.py | 12 +++++-- loopx/semantics/vocabulary_v0.json | 6 ++-- .../test_semantic_vocabulary_drift.py | 31 +++++++++++++++++++ 3 files changed, 44 insertions(+), 5 deletions(-) diff --git a/examples/semantic-vocabulary-drift-smoke.py b/examples/semantic-vocabulary-drift-smoke.py index 687dcd3d0d..ab830e1298 100755 --- a/examples/semantic-vocabulary-drift-smoke.py +++ b/examples/semantic-vocabulary-drift-smoke.py @@ -147,8 +147,8 @@ BUDGET_ANCHOR = { "same_runtime_forks": 25, "same_runtime_fork_definitions": 58, - "conflicting_values": 18, - "conflicting_definitions": 59, + "conflicting_values": 16, + "conflicting_definitions": 55, "schema_version_same_runtime_forks": 7, "multi_value_twins": 19, "multi_value_forks": 4, @@ -692,9 +692,17 @@ def check_inventory(registry: dict[str, Any], sources: list[SourceFile]) -> tupl for item in review['magnitude_regressions']] require(review['ok'], '; '.join(failures)) parts = [] + slack = [] for key in RATCHET_KEYS: actual = semantic_multi_value_forks if key == "multi_value_forks_semantic" else summary[key] parts.append(f"{key}={actual}/{ratchets[key]}") + if ratchets[key] > actual: + # Disclose unlocked headroom so a merge that reverts a tightened + # budget shows up as new slack in this line instead of passing + # silently (the guard only fails on overflow, never on slack). + slack.append(f"{key}={ratchets[key] - actual}") + if slack: + parts.append("slack=" + ",".join(slack)) if review['reviewed_exception_count']: parts.append(f"reviewed_inventory_exceptions={review['reviewed_exception_count']}") return inventory, " ".join(parts) diff --git a/loopx/semantics/vocabulary_v0.json b/loopx/semantics/vocabulary_v0.json index b1fa138ffa..a071290010 100644 --- a/loopx/semantics/vocabulary_v0.json +++ b/loopx/semantics/vocabulary_v0.json @@ -888,8 +888,8 @@ "meaning": "Counts read from the generated inventory. A same-runtime fork is one constant name with one value defined in two or more modules of the same runtime; a conflicting value is one name with different values. Both the number of affected names and the number of definitions are budgets, so a third spelling of an already-conflicting name is still a regression.", "same_runtime_forks": 25, "same_runtime_fork_definitions": 58, - "conflicting_values": 18, - "conflicting_definitions": 59, + "conflicting_values": 16, + "conflicting_definitions": 55, "schema_version_same_runtime_forks": 7, "multi_value_twins": 19, "multi_value_forks": 4, @@ -924,4 +924,4 @@ ] } } -} +} \ No newline at end of file diff --git a/tests/architecture/test_semantic_vocabulary_drift.py b/tests/architecture/test_semantic_vocabulary_drift.py index 760c38d9aa..16eafc34b5 100644 --- a/tests/architecture/test_semantic_vocabulary_drift.py +++ b/tests/architecture/test_semantic_vocabulary_drift.py @@ -268,3 +268,34 @@ def test_live_inventory_ignores_missing_or_stale_reports(tmp_path, monkeypatch, for name in ("first", "second")] with pytest.raises(smoke["Drift"], match="same_runtime_forks grew"): smoke["check_inventory"](registry, sources + duplicate) + + +def test_inventory_report_discloses_budget_slack(monkeypatch): + """Budget slack (budget above the measured value) must be disclosed. + + The guard only fails on overflow (measured > budget), so a merge that + reverts a tightened budget passes silently unless the report line shows + the reopened headroom. See the same_runtime_forks hunk straddle when + merging two budget-tightening branches. + """ + import copy + + smoke = runpy.run_path(str(SMOKE)) + registry = smoke["load_registry"]() + sources = smoke["load_sources"](REPO_ROOT) + # Pinned budgets disclose no slack for the counters this change locks + # (the multi_value_twins slack belongs to the multi-value single-source + # batch, not this one). + _, pinned = smoke["check_inventory"](registry, sources) + assert "slack=conflicting_values" not in pinned, pinned + assert "slack=conflicting_definitions" not in pinned, pinned + # A two-file budget revert (the merge-trap shape: registry and anchor + # move back together, so the equality anchor stays satisfied) must show + # up as disclosed slack instead of passing silently. + widened = copy.deepcopy(registry) + widened["inventory_ratchets"]["conflicting_values"] += 2 + monkeypatch.setitem( + smoke["BUDGET_ANCHOR"], "conflicting_values", widened["inventory_ratchets"]["conflicting_values"], + ) + _, line = smoke["check_inventory"](widened, sources) + assert "slack=conflicting_values=2" in line, line