Files
imbad0202__academic-researc…/scripts/test_check_distribution_surface_claims.py
T

226 lines
6.8 KiB
Python
Raw Normal View History

Align distribution-surface claims with evidence ceilings (#753) (#766) * fix(claims): align distribution-surface claims with evidence ceilings (#753) - plugin.json / marketplace.json: drop 'Production-grade' / '39-agent ensemble' for matrix-licensed wording ('contract-audited', '39 prompt roles (3 plugin-exposed agents; the rest run inline by default)') - academic-pipeline/SKILL.md: no-bypass prose rewritten to the actual mechanism (mandatory checkpoints; overrides require recorded user reasoning); #528 content-lock hash updated in the same commit - shared/cross_model_verification.md: 31%->5-10% relabeled as an unvalidated working hypothesis - shared/ground_truth_isolation_pattern.md: gold-labels rule rewritten to the intended boundary (no unconditional loading into operational agent context) - version-consistency invariant 8: binds 'N prompt roles' spelling too, checks every count token (finditer) - new scripts/check_distribution_surface_claims.py (D1-D5, 20 mutation tests, CI-wired): fail-closed manifest load, shared claim vocabulary imported from check_stage_capability_matrix, percentage refusal, mandatory bindable count token, plugin-exposed count bound to MIRRORS Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_011n3WG8Z3Us8fX8UhXS51Ki * fix(claims): codex R1 — integrity-family must-PASS sweep + lint case/boundary fixes (#753) - integrity 'must PASS with zero issues' absolutes now name the recorded 3-round FAIL-loop exit (integrity_review_protocol, reinforcement_content, team_collaboration_protocol, integrity_verification_agent, SKILL.md flow row); 'recorded with reasoning' weakened to 'recorded user decision' (rationale escalates per compliance override ladder) - D3 percent check lowercases input (matrix caller parity) - D5 plugin-exposed regex case-insensitive - AGENT_CLAIM_RE gains trailing boundaries (39-agentic / singular 'prompt role' no longer count as bound); 4 new mutation tests (20 -> 24) - SKILL.md #528 content-lock hash rebumped Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_011n3WG8Z3Us8fX8UhXS51Ki * fix(claims): codex R2 — Stage 2.5 routing parity, passport-state honesty, gold-set scope, strict JSON (#753) - Stage 2.5 flow row + both state-machine checkpoint triggers name the recorded FAIL-loop exit (SKILL.md + pipeline_state_machine.md, both content-lock hashes rebumped) - team protocol handoff checklist: FAIL-loop continuation keeps passport verification_status UNVERIFIED; VERIFIED only on zero-issue PASS - ground-truth gold exception scoped to synthetic/public-safe content; live-reviewer calibration sets stay runtime-supplied - D1 rejects non-standard JSON constants (NaN/Infinity) via parse_constant; 2 new tests (24 -> 26) Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_011n3WG8Z3Us8fX8UhXS51Ki * fix(claims): codex R3 — prerequisite checker + handoff materials accept the recorded FAIL-loop route (#753) - state_tracker_agent prerequisite table: Stage 3 / Stage 5 entry rows accept a recorded Integrity Check FAIL Loop resolution (previously the documented continuation route was unreachable at the checker) - SKILL.md handoff lines 2.5->3 and 4.5->5 no longer mislabel a FAIL-loop continuation draft as verified; team protocol Materials/Approval rows aligned the same way - SKILL.md + state_tracker_agent content-lock hashes rebumped Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_011n3WG8Z3Us8fX8UhXS51Ki * fix(claims): codex R4 — orchestrator transfer rows + advisory dispatch accept the recorded FAIL-loop route (#753) - orchestrator 2.5->3 and 4.5->5 transfer rows no longer require a 'Verified'-labeled draft on a recorded FAIL-loop continuation - #660/#672 advisory dispatch anchors to the Stage 4.5 terminal resolution (PASS, or recorded FAIL-loop continuation) instead of exact PASS only - orchestrator content-lock hash rebumped Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_011n3WG8Z3Us8fX8UhXS51Ki --------- Co-authored-by: Claude Fable 5 <noreply@anthropic.com>
2026-08-17 20:02:59 +08:00
"""Mutation tests for check_distribution_surface_claims.py (#753)."""
from __future__ import annotations
import json
import sys
from pathlib import Path
REPO_ROOT = Path(__file__).resolve().parent.parent
if str(REPO_ROOT / "scripts") not in sys.path:
sys.path.insert(0, str(REPO_ROOT / "scripts"))
from check_distribution_surface_claims import run # noqa: E402
CLEAN_PLUGIN = {
"name": "fixture",
"description": "Contract-audited academic research pipeline. "
"4 skills, 27 modes, 39 prompt roles.",
}
CLEAN_MARKETPLACE = {
"name": "fixture",
"description": "Contract-audited research and writing skills.",
"plugins": [
{"name": "fixture", "description": "4 skills + 27 modes."},
],
}
def _write(root: Path, plugin=CLEAN_PLUGIN, marketplace=CLEAN_MARKETPLACE,
plugin_raw: str | None = None) -> Path:
d = root / ".claude-plugin"
d.mkdir(parents=True, exist_ok=True)
if plugin is not None or plugin_raw is not None:
(d / "plugin.json").write_text(
plugin_raw if plugin_raw is not None else json.dumps(plugin),
encoding="utf-8",
)
if marketplace is not None:
(d / "marketplace.json").write_text(
json.dumps(marketplace), encoding="utf-8"
)
return root
def _fires(errors: list[str], fragment: str) -> None:
assert any(fragment in e for e in errors), (
f"expected an error containing {fragment!r}, got: {errors}"
)
def test_shipped_manifests_pass():
assert run(REPO_ROOT) == []
def test_clean_synthetic_manifests_pass(tmp_path):
assert run(_write(tmp_path)) == []
def test_missing_plugin_json_fails_closed(tmp_path):
_write(tmp_path, plugin=None)
_fires(run(tmp_path), "D1")
def test_missing_marketplace_json_fails_closed(tmp_path):
_write(tmp_path, marketplace=None)
_fires(run(tmp_path), "D1")
def test_unparseable_plugin_json_fails_closed(tmp_path):
_write(tmp_path, plugin_raw="{not json", plugin=None)
_fires(run(tmp_path), "D1")
def test_non_object_top_level_fails_closed(tmp_path):
_write(tmp_path, plugin_raw='["a list"]', plugin=None)
_fires(run(tmp_path), "D1")
def test_nonstandard_json_nan_fails_closed(tmp_path):
# json.loads accepts NaN by default; strict consumers reject it
_write(tmp_path, plugin=None,
plugin_raw='{"description": "39 prompt roles", "score": NaN}')
_fires(run(tmp_path), "D1")
def test_nonstandard_json_infinity_fails_closed(tmp_path):
_write(tmp_path, plugin=None,
plugin_raw='{"description": "39 prompt roles", "x": Infinity}')
_fires(run(tmp_path), "D1")
def test_missing_description_fires_d2(tmp_path):
_write(tmp_path, plugin={"name": "fixture"})
_fires(run(tmp_path), "D2")
def test_empty_description_fires_d2(tmp_path):
_write(tmp_path, plugin={"name": "fixture", "description": " "})
_fires(run(tmp_path), "D2")
def test_empty_plugins_list_fires_d2(tmp_path):
_write(tmp_path, marketplace={"name": "f", "description": "ok",
"plugins": []})
_fires(run(tmp_path), "D2")
def test_production_grade_fires_d3(tmp_path):
_write(tmp_path, plugin={
"name": "f", "description": "Production-grade research pipeline."
})
_fires(run(tmp_path), "production-grade")
def test_production_grade_space_spelling_fires_d3(tmp_path):
_write(tmp_path, plugin={
"name": "f", "description": "A production grade pipeline."
})
_fires(run(tmp_path), "production-grade")
def test_agent_ensemble_fires_d3(tmp_path):
_write(tmp_path, plugin={
"name": "f", "description": "A 39-agent ensemble for research."
})
_fires(run(tmp_path), "agent ensemble")
def test_effectiveness_stems_fire_d3(tmp_path):
for phrase in ("guaranteed accuracy", "proven results",
"state-of-the-art pipeline", "improves error rates",
"outperforms manual review"):
errors = run(_write(tmp_path, plugin={
"name": "f", "description": f"A pipeline with {phrase}."
}))
_fires(errors, "D3")
def test_provenance_does_not_false_fire(tmp_path):
# word boundary: "proven" must not fire inside "provenance"
assert run(_write(tmp_path, plugin={
"name": "f",
"description": "Citation provenance tracking — 39 prompt roles.",
})) == []
def test_marketplace_plugin_entry_description_covered(tmp_path):
_write(tmp_path, marketplace={
"name": "f", "description": "ok",
"plugins": [{"name": "f",
"description": "Production-grade skills."}],
})
_fires(run(tmp_path), "plugins[0]")
def test_percentage_figure_fires_d3(tmp_path):
# the T-3 class: "31% -> ~5-10%" style numbers on a marketing surface
_write(tmp_path, plugin={
"name": "f",
"description": "Cuts citation errors by 31% — 39 prompt roles.",
})
_fires(run(tmp_path), "percentage")
def test_missing_count_token_fires_d4(tmp_path):
_write(tmp_path, plugin={
"name": "f", "description": "Contract-audited research pipeline."
})
_fires(run(tmp_path), "D4")
def test_unbindable_count_spelling_fires_d4(tmp_path):
# "39 agents" is a spelling invariant 8 cannot bind — the number would
# silently detach from the tree count
_write(tmp_path, plugin={
"name": "f", "description": "A pipeline with 39 agents."
})
_fires(run(tmp_path), "D4")
def test_plugin_exposed_count_drift_fires_d5(tmp_path):
_write(tmp_path, plugin={
"name": "f",
"description": "39 prompt roles (4 plugin-exposed agents).",
})
_fires(run(tmp_path), "D5")
def test_plugin_exposed_count_matching_passes(tmp_path):
assert run(_write(tmp_path, plugin={
"name": "f",
"description": "39 prompt roles (3 plugin-exposed agents).",
})) == []
def test_percentage_case_variant_fires_d3(tmp_path):
# "_PERCENT_RE" is lowercase-only; the caller must lowercase first
_write(tmp_path, plugin={
"name": "f",
"description": "A 31 Percent reduction — 39 prompt roles.",
})
_fires(run(tmp_path), "percentage")
def test_plugin_exposed_case_variant_fires_d5(tmp_path):
_write(tmp_path, plugin={
"name": "f",
"description": "39 prompt roles (4 Plugin-Exposed agents).",
})
_fires(run(tmp_path), "D5")
def test_near_miss_spelling_agentic_fires_d4(tmp_path):
# "39-agentic" must not count as a bound "N-agent" token
_write(tmp_path, plugin={
"name": "f", "description": "A 39-agentic workflow suite."
})
_fires(run(tmp_path), "D4")
def test_near_miss_spelling_singular_role_fires_d4(tmp_path):
# "39 prompt role" (singular) is not the licensed plural spelling
_write(tmp_path, plugin={
"name": "f", "description": "A suite with 39 prompt role."
})
_fires(run(tmp_path), "D4")