Skip to content

results_stale scoped to the base frontiers; scenario set flags on its own marker - #142

Merged
cafzal merged 2 commits into
mainfrom
claude/scenario-stale-echo
Aug 4, 2026
Merged

results_stale scoped to the base frontiers; scenario set flags on its own marker#142
cafzal merged 2 commits into
mainfrom
claude/scenario-stale-echo

Conversation

@cafzal

@cafzal cafzal commented Aug 4, 2026

Copy link
Copy Markdown
Owner

Defect

A scenario-only model update (only scenario_config changed; objectives/options/scores/constraints untouched) echoed status.results_stale: true — read as "re-solve the base run" although both base frontiers still matched the model, and apparently contradicting the staleness comment near the update handler.

Which way the evidence pointed

Both ways — and the flag's scope is the real defect. The echo was "correct" under the code's blanket semantics (_results_stale folded the scenario set's composite stamp — #130 — into one boolean spanning every stored frontier), and two tests asserted that blanket behavior. But:

  • engine/models.py's Scenario-side staleness: the fingerprint treatment base runs already had #130 design comment already states the intent: "a scenarios-only edit must not read as staleness on the base run".
  • The blanket flag's consumers treat it as "a BASE frontier predates the edit". Reproduced casualties: after a scenario-only edit, the next base solve dropped a still-valid exact overlay (dropped_frontier_note, certification lost), and fill_gaps refused a legitimate fill.

So the fix scopes the flag rather than re-wording the comment.

Fix

  • _results_stale reads the base frontiers only (exploratory run + exact overlay, each against the inputs it reads). A scenario-only edit leaves it false.
  • The scenario set keeps its own Scenario-side staleness: the fingerprint treatment base runs already had #130 marker (models.scenario_results_stale) — now echoed beside the base flag in model update's status and model load's status, same present-only-when-it-fires convention as model get and the scenario reads. A scenario-only edit reports results_stale: false + scenario_results_stale: true: the echo says exactly which re-solve is owed.
  • The frontier-drop and fill_gaps guards become correct with no change of their own — the flag now means what they assume.
  • Two tests asserting blanket semantics re-pointed to the split; test_run_scenarios_does_not_vouch_for_a_stale_base_frontier passes unchanged (a base frontier predating an edit still flags). Docs: update-handler comments, model docstring, architecture.md update row.

Tests

2 new in tests/test_scenario_staleness.py, written first and failing on the prior behavior:

  • test_scenario_only_edit_echo_stales_the_scenario_set_not_the_base (the reported echo; also asserts a base-input edit still flips both)
  • test_scenario_only_edit_does_not_cost_a_base_frontier_on_the_next_solve (failed on main with the exact overlay discarded)

Full suite: 1049 passed, 2 skipped; ui 37 passed.

cafzal added 2 commits August 3, 2026 19:20
… its own marker

A scenario-only model update (only scenario_config changed) echoed
status.results_stale: true while both base frontiers still matched the
model — read as "re-solve the base run" when the only stale side was the
scenario set. The evidence pointed at the flag's scope, not the echo
mechanics: _results_stale folded the scenario set's composite stamp into
one blanket boolean, and its consumers treat that boolean as "a BASE
frontier predates the edit" — so a scenario-only edit ALSO made the next
base solve drop a still-valid exact overlay (dropped_frontier_note) and
made fill_gaps refuse a legitimate fill. models.py's design comment
(#130) already stated the intent: "a scenarios-only edit must not read
as staleness on the base run".

Fix: _results_stale reads the base runs only (exploratory + exact
overlay, each against the inputs it reads). The scenario set keeps its
own #130 marker — models.scenario_results_stale — now echoed beside the
base flag in model update's status (same present-only-when-it-fires
convention as model get and the scenario reads) and in model load's
status. The frontier-drop and fill_gaps guards become correct with no
change of their own, since the flag now means what they assume.

Two tests asserting the blanket semantics were re-pointed to the split
(scenario staleness on its marker, base flag clear); the run_scenarios
does-not-vouch test passes unchanged — a base frontier that predates an
edit still flags.

Tests: 2 new in tests/test_scenario_staleness.py (status echo split; a
scenario-only edit costs no frontier on the next solve), each failing on
the prior behavior — the drop repro failed with the exact overlay
discarded. Full suite: 1049 passed, 2 skipped; ui 37 passed.
With NO base runs, the scoped flag hit the nothing-to-vouch-for branch and
stayed true forever — a fresh, successful run_scenarios on a problem that
deliberately has no base run echoed results_stale: true (with no scenario
marker), misdirecting to "re-solve the base", and flipped model load's
has_results gate to solve guidance on a solved scenario-only bundle.

With no base run, the scenario-only workflow decides: a populated scenario
set means solved (its freshness stays the scenario marker's question);
a problem with no stored results anywhere still flags on a structural edit.

Test: scenario-only run_scenarios lands at results_stale false / no scenario
marker, never-solved stays flagged, and a later scenario edit flags the
scenario marker with the base flag still clear — fails pre-fix.

Full suite: 1050 passed, 2 skipped; ui 37 passed.
@cafzal
cafzal force-pushed the claude/scenario-stale-echo branch from e550a79 to 08a6aa0 Compare August 4, 2026 02:21
@cafzal
cafzal merged commit c4d406b into main Aug 4, 2026
2 checks 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.

1 participant