Skip to content

Add proof-from-scratch evaluator contract - #71

Merged
Qian-Cheng-nju merged 9 commits into
specula-org:mainfrom
munimthahmid:feat/proof-from-scratch-evaluator
Jul 29, 2026
Merged

Add proof-from-scratch evaluator contract#71
Qian-Cheng-nju merged 9 commits into
specula-org:mainfrom
munimthahmid:feat/proof-from-scratch-evaluator

Conversation

@munimthahmid

@munimthahmid munimthahmid commented Jul 21, 2026

Copy link
Copy Markdown
Contributor

Important

This remains a draft until the proof-from-scratch generator emits manifest.json and the editable-region markers. Runs fail closed without that producer-side contract.

What changed

  • Make the manifest the sole source of proof-from-scratch task discovery and context, with strict validation and no heuristic fallback.
  • Restrict agent edits to marked helper and proof regions, preserve the remaining scaffold byte-for-byte, and reject forbidden or unproved helper declarations.
  • Keep context files read-only during execution and grade the submitted target against a fresh canonical context that was never exposed to the agent.
  • Apply the same contract to one-shot responses and continuation rounds, and update the prompts and usage documentation.

@munimthahmid munimthahmid changed the title WIP: Add proof-from-scratch evaluator contract Add proof-from-scratch evaluator contract Jul 24, 2026
@munimthahmid
munimthahmid force-pushed the feat/proof-from-scratch-evaluator branch from 884ce5b to d4a3905 Compare July 24, 2026 15:52
@munimthahmid
munimthahmid marked this pull request as ready for review July 27, 2026 19:01
@munimthahmid

Copy link
Copy Markdown
Contributor Author

@sukanya1426 Can you also review this PR when you get a chance? Since #80 depends on it, we should make sure both PRs stay in sync.

@sukanya1426

Copy link
Copy Markdown
Contributor

Sure.

context_paths.append(context_path)

resolved_paths = tuple(context_paths)
_validate_module_files(task_key, ((task_key, task_path), *context_entries))

@sukanya1426 sukanya1426 Jul 28, 2026

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The validator checks paths, symlinks, duplicate basenames, and module names, but it never verifies that the context covers the task's full EXTENDS/INSTANCE closure. I tested this by removing a context module, and the run ended up as [SANY-INVALID] / GATES-FAILED: A, identity, so a generator bug gets scored as agent tampering. Since the manifest is now the only source of context, it should validate the closure here and raise ManifestError instead.


resolved_paths = tuple(context_paths)
_validate_module_files(task_key, ((task_key, task_path), *context_entries))
boundaries[task_key] = TaskBoundary(

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Another small improvement: call parse_editable_regions while loading tasks. Missing markers currently slip through loading and only surface as an exit-3 ERROR during grading. Better to catch them early.

Comment thread src/evaluator/runner.py Outdated
if not cut_short:
grading_canonical_dir = run.canonical_dir
if getattr(mode, "canonical_replay_required", False):
grading_canonical_dir = _make_canonical_dir(

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This snapshot is taken after the agent finishes and reads from item.benchmark_path in the repo. In --no-container mode the agent has host write access, so it can edit the source and the "fresh" snapshot picks up the tampered bytes, which also feeds get_baseline_text, so nothing independently catches it. Take it once before _run_backend_with_retries and reuse it for grading.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Good catch. Fixed. In --no-container mode, we now save the original target and context files in memory before starting the agents. I

Comment thread src/common/check_proof.py
return staged_file


def check_editable_region_integrity(filepath, benchmark_dir):

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Byte exact comparison means even a trailing newline at EOF or a CRLF rewrite gets flagged as CHEAT-DETECTED, scaffold_unchanged (gate A). I confirmed both cases. _reconstruct_marked_solution already treats these as harmless retyping for one-shot, but agentic backends don't. We should either normalize line endings and trailing newlines before comparing, or give these their own non-cheat reason code. Otherwise, editor noise will inflate the reported cheating rate.

return False
return resolved in self._boundaries_by_path

def get_benchmark_files(self, filter_pattern: str | None = None) -> list[str]:

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

main() calls this at runner.py:1721 with no try/except, so a missing or malformed manifest escapes as a raw ManifestError traceback and never reaches the parser.exit(2, ...) handling on the next line. Catch it in main() or handle it here, so the user gets an error message instead of a stack trace.

@munimthahmid
munimthahmid force-pushed the feat/proof-from-scratch-evaluator branch from 71792cd to 31874c9 Compare July 28, 2026 10:29
@munimthahmid
munimthahmid requested a review from sukanya1426 July 28, 2026 10:34
Comment thread src/common/check_proof.py

if submitted_regions.fixed_segments != canonical_regions.fixed_segments:

def normalize_format(regions):

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

One thing I noticed is that this only handles a single trailing newline, so the common case of an editor adding a newline still gets reported as SCAFFOLD_MODIFIED/CHEAT-DETECTED. Switching to suffix = suffix.rstrip("\n") fixes that without affecting real scaffold modifications.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Fixed. Extra newlines at EOF are now ignored.

@munimthahmid
munimthahmid requested a review from sukanya1426 July 28, 2026 15:17

@sukanya1426 sukanya1426 left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Seems alright now. Thanks for addressing the issues. Over to @Qian-Cheng-nju , can you please take a look and validate ?

@Qian-Cheng-nju

Copy link
Copy Markdown
Member

LGTM!

@Qian-Cheng-nju
Qian-Cheng-nju merged commit 69eeca3 into specula-org:main Jul 29, 2026
1 check passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants