Repository navigation
FEAT: repeat program cases and leave disagreeing runs undetermined - #4264
Open
WatchTree-19 wants to merge 1 commit into
Open
WatchTree-19 wants to merge 1 commit into
WatchTree-19 wants to merge 1 commit into
Conversation
Answers open question 6 in the scorer contract design. ProgramBehaves takes an optional repetitions count, default 1. Each case then runs that many times in one acquisition, and a case is settled only when every run reaches the same verdict. Runs that disagree, or a repeated case with a missing run, leave the verdict undetermined. Only a difference that changes a case's verdict counts as nondeterminism: output the case does not check, and duration, never count. Repetitions share one runtime profile, so platform and architecture are compared under separate profiles. Replay still judges stored runs and never repeats execution. With the default, invocations and rationales are unchanged, and conditions stored before this change load with one run.
Contributor
There was a problem hiding this comment.
🟢 Approval recommended
No active review issues were identified for the optional repeated-execution scoring change.
No open issues 🎉
What changed in this PR
Adds optional repeated execution to PyRIT’s program-behavior scoring, allowing cases with inconsistent verdicts to remain undetermined while preserving single-run behavior by default.
Changes:
- Adds a positive
repetitionssetting toProgramBehaves, repeating invocations in case order. - Settles repeated cases only when all runs agree; disagreement or missing runs leave the case undetermined.
- Adds validation, scoring, and replay tests, plus documentation clarifying that replay reuses stored evidence without executing again.
| File | Description |
|---|---|
tests/unit/score/test_program_execution_scorer.py |
Tests repeated verdicts, missing runs, case indexing, and replay behavior. |
tests/unit/models/test_program.py |
Tests repetition ordering, validation, serialization, and backward-compatible defaults. |
pyrit/score/true_false/program_execution_scorer.py |
Evaluates repeated runs per case and updates verdict rationales. |
pyrit/models/score/condition.py |
Adds repetitions and expands each case’s invocations. |
doc/code/scoring/7_program_execution_scorer.py |
Documents repetition semantics and replay limitations. |
doc/code/scoring/7_program_execution_scorer.ipynb |
Mirrors the replay documentation in the notebook. |
Review effort: Balanced
This branch has not been deployed
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Description
This answers open question 6 in the scorer contract design: "Which execution differences count as nondeterminism?"
The observation records one exact run, and replay repeats judgment, not execution, so a single stored run cannot show whether a program is deterministic. A program that reads the clock or a random source can pass once and fail the next time, and today that single pass becomes a confident
true.The answer this change takes: a difference counts as nondeterminism only when it changes a case's verdict. Output the case does not check, and duration, never count. That keeps the question inside the existing matcher instead of adding a second notion of "same output".
ProgramBehavestakesrepetitions: int = 1(at least 1). Each case runs that many times in one acquisition, as consecutive invocations, so the existing source and sandbox need no change.case 0 gave different results across runs (2 of 3 failed). A repeated case with a missing run is also undetermined.The
ProgramBehavesandProgramExecutionScorerdocstrings and the replay section of the program execution doc page (.pyand.ipynb) describe the option.Tests and Documentation
tests/unit/models/test_program.py: invocations repeat per case in case order and round-trip throughCondition; the default is one run and a stored condition without the field loads; zero and negative counts are rejected.tests/unit/score/test_program_execution_scorer.py: agreeing runs pass or fail as before; disagreeing runs in either direction are undetermined with the count; a missing run is undetermined; each case is read from its own runs (one variant passes only with the right index mapping); end to end, a drifting program is undetermined with repetitions, passes with one run, replays as undetermined without executing, and refuses replay under a condition with a different repetition count; output a case does not check is not treated as a difference.How it was tested
Rebased on current main (dd3f593). The new and touched tests pass; the only failures in that run are 57 that fail the same way on main in this environment. ruff is clean, and ty reports nothing new in the touched
pyrit/files compared with main. The doc page's markdown cells were checked to match between.pyand.ipynb; the page's code was not executed here because it needs the Docker daemon.Each guard was removed in turn to confirm the tests catch it:
index + run)repetitionsThis is independent of the other two program execution PRs; each applies to current main on its own.