From 52971cc818fdd11e8b63960c8e674bd43ca16c6f Mon Sep 17 00:00:00 2001 From: Dong1017 <55723162+Dong1017@users.noreply.github.com> Date: Sat, 31 Oct 2026 22:35:13 +0800 Subject: [PATCH 1/2] feat: make nonpaid SkillEval plans actionable for authors Signed-off-by: Codex --- docs/skill-eval-pr.md | 48 +++- tests/test_skill_eval_planned_guidance.py | 206 ++++++++++++++ tests/test_skill_eval_pr.py | 2 +- tests/test_skill_eval_pr_summary.py | 26 +- tools/skill_eval_pr.py | 319 +++++++++++++++++++--- 5 files changed, 551 insertions(+), 50 deletions(-) create mode 100644 tests/test_skill_eval_planned_guidance.py diff --git a/docs/skill-eval-pr.md b/docs/skill-eval-pr.md index 147ac8cf4..09140a172 100644 --- a/docs/skill-eval-pr.md +++ b/docs/skill-eval-pr.md @@ -48,10 +48,56 @@ Plans and reports have one identifiable bot comment beginning with ``. A new head/base, trusted implementation, mapping, exam, model or budget produces a new plan. Earlier authorization and results are stale. The planner replaces the summary with the current plan and -missing pairs. A publisher checks live revisions twice and refuses older writer +explicit unexecuted status (not zero-valued pseudo-results). A publisher checks live revisions twice and refuses older writer runs, so delayed jobs cannot replace a newer summary. All plan/execute/publish runs for one PR share non-cancelling Actions concurrency. +### Nonpaid author handoff + +A **PLANNED** comment is a work instruction, not a test result. Its front section +identifies the current head, next action, unresolved blockers and required evidence. +The collapsed **Author verification steps** contain the actual path-map trigger, +existing task purpose/scope, required answer fields and concrete check instructions. +Known fixed snapshot refusals remain precise; recognized dynamic errors expose only +the fixed category, never arbitrary exception/YAML excerpts. Unknown errors receive +an explicit generic preparation failure. The original plan/exception bytes are not +rewritten by this public display projection. A mapped path does **not** establish +that its task tests the changed claim. For example, a blocked-request diagnostic +case is not a source-fidelity check merely because a skill metadata file changed. +The author and independent reviewer must disposition that relevance explicitly. + +For canonical library or generated-index changes, the planner may show the existing +`python tools/build_skill_index.py --source-revision --check` +with an **actual** immutable pin. It reads the known generated README as bounded +Git data, requires one declared source commit, and checks that the entire canonical +library has identical Git bytes/modes at that pin and the PR head. It does not +execute the generator or establish that generated records are correct. At the +identified PR checkout, with the reviewed tool and documented development/skills +extras, the author can run that no-model check and retain its exit code plus full +success/drift output. An absent, unavailable or mismatched pin yields an explicit +source-selection action instead; HEAD is never silently substituted. A successful +index check establishes projection consistency, not source-claim truth, behavior, +hardware compatibility or skill benefit. + +For successfully prepared mappings, the displayed `python -m json.tool` commands +inspect the retained tasks and schema from the downloaded `skill-eval-plan` +artifact directory; they do not grade anything or call a model. Produce the required +answer/artifact only after confirming applicability. Use the existing +[comparison contract](skill-eval-comparison.md) or, for an explicitly selected +engineering task, [local-author handoff](local-skill-evaluation.md). No paid +execute command is a nonpaid fallback, and no task or mapping is selected on the +author's behalf when the existing mapping supplies none. + +Return the current PR head, exact check/task and source pin, commands, environment, +exit status, bounded input/output evidence and failures. Distinguish real author +work from synthetic hand-authored/scripted controls. List claims still untested; +an independent reviewer records verified/refuted/unverified dispositions. Path +counts are grouped by canonical/generated/other **location**, never treated as +claim counts or proof of a metadata-only change. Full paths, frozen task/grader +identities, budget and execution controls remain in collapsed audit details and +the retained plan. Older plans lacking task metadata say so instead of borrowing +new metadata or rebinding old evidence. Unsupported snapshot safety is unchanged. + ### Refresh a plan without execution Dispatch **Skill PR evaluation** on **main** with `action=plan` and the PR number diff --git a/tests/test_skill_eval_planned_guidance.py b/tests/test_skill_eval_planned_guidance.py new file mode 100644 index 000000000..bd0a11144 --- /dev/null +++ b/tests/test_skill_eval_planned_guidance.py @@ -0,0 +1,206 @@ +"""Nonpaid planner guidance: raw Git/data inspection, never candidate/model execution.""" + +import copy +import json +from unittest.mock import Mock + +import pytest + +from inferrsi.infra.artifacts import write_json +from tests.fixtures.skill_eval_compare_support import git +from tests.fixtures.skill_eval_pr_support import setup +from tests.test_skill_eval_pr_summary import visible +from tools import skill_eval_pr as pr + + +def render(plan): + return pr.summary(plan, [], "https://example.invalid/plan", 100, 1) + + +def test_prepared_task_purpose_trigger_and_needed_outputs_are_retained(tmp_path, monkeypatch): + repo, mapping, identity = setup(tmp_path, ids=("one",)) + tasks = json.loads((tmp_path / "cases.json").read_text()) + tasks[0].update(request="Audit every request against the fixed workload contract.") + assert "arbitrary_private_note" not in pr.task_context( + [{**tasks[0], "arbitrary_private_note": "DO-NOT-PROJECT"}])[0] + write_json(tmp_path / "cases.json", tasks) + never_execute = Mock(side_effect=AssertionError("No candidate or model execution")) + monkeypatch.setattr(pr.compare, "execute", never_execute) + plan = pr.prepare(repo, mapping, identity, tmp_path / "plan") + item = plan["comparisons"][0] + selection = item["selection"] + assert selection["basis"] == "trusted_path_mapping" + assert selection["matched_paths"] == [c["path"] for c in plan["coverage"] + if c["coverage"] == "covered"] + assert selection["tasks"][0]["request"] == tasks[0]["request"] + assert "arbitrary_private_note" not in selection["tasks"][0] + schema = json.loads((tmp_path / "plan/selected/plan/schema.json").read_text()) + assert selection["required_answer_fields"] == schema["required"] + text, main = render(plan), visible(render(plan)) + for expected in (tasks[0]["request"], "http_fixed_workload", "Required answer fields:", + "python -m json.tool selected/plan/tasks.json", "**not grading**", + "Independent check: frozen audit", "git rev-parse HEAD", + "verified, refuted or unverified"): + assert expected in text + assert item["exam"]["grader"] in text and "DO-NOT-PROJECT" not in text + assert "Outcomes:" not in text and "--execute" not in main and "--stage-local" not in main + never_execute.assert_not_called() + + +def test_unsupported_and_mapping_only_keep_task_purpose_without_fake_commands(tmp_path): + repo, mapping, identity = setup(tmp_path, ids=("one",)) + git(repo, "update-index", "--chmod=+x", "library/selected/resource.txt") + git(repo, "commit", "--quiet", "-m", "Synthetic unsupported executable snapshot") + identity["head"] = git(repo, "rev-parse", "HEAD") + plan = pr.prepare(repo, mapping, identity, tmp_path / "unsupported") + main = visible(render(plan)) + assert "Executable snapshot entries are not supported" in main + assert plan["comparisons"][0]["selection"]["tasks"] + assert "python -m json.tool selected" not in main + blocked = pr.prepare(repo, mapping, identity, tmp_path / "blocked", + planning_blocked="unavailable configuration") + assert blocked["comparisons"][0]["selection"]["tasks"] + assert "Mapping-only scope; provider unavailable" in visible(render(blocked)) + assert "No supported automatic comparison" in render(plan) + + +def index_fixture(tmp_path): + repo = tmp_path / "repo" + repo.mkdir() + git(repo, "init", "--quiet") + canonical = repo / pr.LIBRARY_PATH / "skill-index.json" + canonical.parent.mkdir(parents=True) + canonical.write_text('{"fixture":"not an efficacy test"}\n') + git(repo, "add", ".") + git(repo, "commit", "--quiet", "-m", "Synthetic canonical source") + pin = git(repo, "rev-parse", "HEAD") + readme = repo / pr.PROJECTION_PATH / "README.md" + readme.parent.mkdir(parents=True) + readme.write_text(f"Source snapshot: [{pin}](https://github.com/example/fixture/tree/" + f"{pin}/{pr.LIBRARY_PATH}).\n") + git(repo, "add", ".") + git(repo, "commit", "--quiet", "-m", "Synthetic declared projection pin") + identity = {"repository": "example/fixture", "base": pin, + "head": git(repo, "rev-parse", "HEAD")} + changes = pr.changed_files(repo, identity["base"], identity["head"]) + return repo, readme, canonical, pin, identity, changes + + +def test_index_pin_check_uses_declared_source_not_head_and_does_not_execute(tmp_path): + repo, _, _, pin, identity, changes = index_fixture(tmp_path) + check = pr.projection_check(repo, identity, changes) + assert check == {"status": "ready", "source_revision": pin, + "declared_in": pr.PROJECTION_PATH + "/README.md"} + assert pin != identity["head"] + minimal = {**identity, "version": "d" * 40, "estimated_calls": 0, "projection_check": check, + "comparisons": [], "coverage": [{**c, "coverage": "uncovered"} for c in changes]} + text = "\n".join(pr.author_actions(minimal)) + assert f"python tools/build_skill_index.py --source-revision {pin} --check" in text + assert "command has **not run**" in text and "not claim truth or skill efficacy" in text + assert "exit code and full drift/success output" in text + assert "Generated projection paths: 1" in text + + +@pytest.mark.parametrize("mutation", ["missing", "malformed", "duplicate", "mismatch", "mode", + "symlink", "oversized", "unknown_commit", "missing_library"]) +def test_unusable_projection_pin_never_yields_a_concrete_check(tmp_path, mutation): + repo, readme, canonical, pin, identity, changes = index_fixture(tmp_path) + if mutation == "missing": + readme.unlink() + elif mutation == "malformed": + readme.write_text("Source snapshot: $(echo unsafe)\n") + elif mutation == "duplicate": + readme.write_text(readme.read_text() * 2) + elif mutation == "mismatch": + canonical.write_text("changed canonical input\n") + elif mutation == "mode": + readme.chmod(0o755) + elif mutation == "symlink": + readme.unlink() + readme.symlink_to(canonical) + elif mutation == "oversized": + readme.write_text(readme.read_text() + "x" * 65536) + elif mutation == "unknown_commit": + readme.write_text(readme.read_text().replace(pin, "f" * 40)) + else: + canonical.unlink() + git(repo, "add", "-A") + git(repo, "commit", "--quiet", "-m", "Synthetic unavailable source") + identity["head"] = git(repo, "rev-parse", "HEAD") + check = pr.projection_check(repo, identity, changes) + assert check["status"] == "unavailable" and "source_revision" not in check + if mutation == "mismatch": + assert "differs from current canonical" in check["reason"] + + +def test_hostile_metadata_is_escaped_and_raw_plan_is_not_mutated(tmp_path): + repo, mapping, identity = setup(tmp_path, ids=("one",)) + plan = pr.prepare(repo, mapping, identity, tmp_path / "plan") + hostile = '\n```bash\n$(touch bad)' + item = plan["comparisons"][0] + item["selection"]["tasks"][0].update(request=hostile, provenance=hostile) + item["selection"]["matched_paths"] = [hostile] + item["selection"]["required_answer_fields"] = [hostile] + plan["coverage"].append({"path": hostile, "coverage": "uncovered"}) + original = copy.deepcopy(plan) + text = render(plan) + assert text.count("") == 3 + assert "