Skip to content

Key stop_on_success into the experiment identity and record it - #52

Closed
RoldSI wants to merge 2 commits into
mainfrom
fix/stop-on-success-identity
Closed

RoldSI wants to merge 2 commits into
mainfrom
fix/stop-on-success-identity

Conversation

@RoldSI

@RoldSI RoldSI commented Aug 27, 2026 •

Copy link
Copy Markdown
Collaborator

Follow-up to #49, which is merged. That PR added stop_on_success as a Controller argument but never added it to ExperimentMeta, so it reached neither identity_hash() nor experiment_block().

Why this is a blocker, not a tidiness fix

The flag's whole purpose is the opt-out: stop_on_success=False is what an experiment measuring attack reliability needs (how many of N attempts succeed, not whether any did). That mode is currently a no-op.

Two Controllers differing only in this flag produce the same identity_hash(), so they resolve to the same experiment directory. _status_of maps a won task to status "success", and "success" is in _KEPT_STATUSES, so plan_resume keeps every task from the prior tree. A reliability run therefore resumes the stop-on-win directory and executes zero runs, returning the stop-on-win record verbatim. Nothing warns, because from resume's point of view every task is already complete.

Reproduced: pass 1 (stop_on_success=True, max_runs_per_task=3) writes a directory with one run at stop_reason="success". Pass 2 (stop_on_success=False, same labels) resolves to that same directory, len(runs) == 0, and reports the earlier run's n_runs=1.

Keyed only when disabled

identity_hash() hashes a dict, so adding a key changes the digest even at the default value. The first version of this branch keyed it unconditionally, which re-keyed every results tree ever written: the same meta hashed ef26fece before and 5b18e4e4 after, so dirname() resolves somewhere new, plan_resume sees a fresh experiment, and finished work is recomputed. The certified 843-hour tree would have been unresumable.

Keying it only when False gets the property the flag needs while leaving every default run on its historical hash:

if not self.stop_on_success:
    identity["stop_on_success"] = False

Verified: default and explicit-True both hash ef26fece (unchanged from main); False hashes 0f2b58f6.

The regression test whose absence let that through

The suite had five tests asserting a field changes the hash and three asserting one does not, but none asserting what the hash is — so the tree-breaking version was 596/596 green.

test_default_identity_hash_is_frozen now pins ef26fece, and its docstring says not to update the constant when it fails: key the new field conditionally instead.

Also

  • stop_on_success is recorded in experiment_block(), so a stored tree says which regime produced its run counts. Without it, two trees with very different mean runs-per-task are indistinguishable after the fact.
  • reference/results.md's documented identity list corrected; it omitted the field.

Checks

598 passed (594 before, plus 4 new) · ruff check/format clean · mypy clean on 25 source files.

RoldSI added 2 commits August 27, 2026 16:48
#49 added `stop_on_success` but did not add it to `ExperimentMeta`, so it
reaches neither `identity_hash()` nor `experiment_block()`. Two Controllers
differing only in that flag resolve to the same experiment directory.

That makes the opt-out silently useless, which is the mode it exists for.
`_status_of` maps a won task to status "success", and "success" is in
`_KEPT_STATUSES`, so `plan_resume` keeps every task from the prior tree. A
reliability run (`stop_on_success=False`, meant to measure how many of N
attempts succeed) therefore resumes the stop-on-win directory and executes
zero runs, returning the stop-on-win record verbatim. Nothing warns, because
from resume's point of view every task is already complete.

Reproduced: pass 1 with `stop_on_success=True` writes a directory and one run
with `stop_reason="success"`; pass 2 with `stop_on_success=False` and the same
labels resolves to that same directory, runs nothing, and reports the earlier
run.

`include_feedback` is keyed for exactly this reason and this is the same kind
of parameter: it changes what is measured, not merely how fast. Key it, and
record it in the experiment block so a stored tree says which regime produced
its run counts.

Also fixes the documented identity list in reference/results.md, which omitted
it.
The first version of this branch added the key unconditionally, which changes
the digest even at the default value: the same ExperimentMeta hashed ef26fece
before and 5b18e4e4 after. Every results tree written before this would have
been orphaned, since dirname() resolves somewhere new and plan_resume then sees
a fresh experiment and recomputes finished work. The certified 843-hour tree
would have been unresumable.

Keying it only when False gets the property the flag needs (the two regimes
must not share a directory, because a won task persists with a KEPT status and
a reliability run would otherwise resume the stop-on-win tree and execute
nothing) while leaving every default run on its historical hash.

Add the regression test whose absence let this through. The suite had five
tests asserting a field CHANGES the hash and three asserting one does NOT, but
none asserting what the hash IS, so the tree-breaking version was 596/596
green. test_default_identity_hash_is_frozen pins ef26fece and says in its
docstring not to update the constant when it fails.
@RoldSI

RoldSI commented Aug 28, 2026

Copy link
Copy Markdown
Collaborator Author

Superseded by the branch that removes the flag entirely: with stop-on-success as the only behaviour there is no second regime to distinguish, so the identity key this PR added (and then had to make conditional to avoid re-keying every existing results tree) is unnecessary. See the replacement PR.

@RoldSI RoldSI closed this Aug 28, 2026
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.

1 participant