Skip to content

Pin the voyage event roll on the module docks actually calls (CI red on main) - #202

Merged
dmccoystephenson merged 1 commit into
mainfrom
feature/pin-voyage-event-roll-at-the-call-site
Sep 22, 2026
Merged

dmccoystephenson merged 1 commit into
mainfrom
feature/pin-voyage-event-roll-at-the-call-site

Conversation

@dmccoystephenson

Copy link
Copy Markdown
Member

Summary

  • main has been red for its last two runs (35554330178, 35556530705) on tests/location/test_docks.py::test_a_voyage_that_founders_ends_early, the flake that Pin the event roll in the foundering-voyage test (flake) #201 was meant to fix.
  • Pin the event roll in the foundering-voyage test (flake) #201's pin was applied to src.business.adventures.rollEvent, but src/location/docks.py imports from business import adventures. Because pytest.ini puts both . and src on the path, src.business.adventures and business.adventures are two separate module objects, so the patch never reached the call site and the real random.choice roll kept running. (The random.randint / random.random patches were unaffected: random is one shared module either way.)
  • The roll is now pinned with patch.object(docks.adventures, "rollEvent", ...) — the module docks actually resolves — and the event is taken from that module's own EVENTS.
  • The day assertion is tightened from < startingDay + legs to == startingDay + 1: with 51% of hull and a 99-point leak the boat founders on the first leg exactly. An unpinned roll now fails five runs in seven rather than one in forty, so a regression of this kind would surface on the first CI run rather than the fortieth.

No tracking issue — #200 was closed by #201; this is the gap found at triage (CI red on main). No open issues were skipped: the backlog was empty at triage time.

Test-only change; no production code, schema, or persistence path is touched.

Test plan

  • Adversarial probe: with the real random.choice forced to draw driftwood every leg, the test on main fails and the test on this branch passes (the stash-and-run check for the rubric's Tests-fix item).
  • 200 consecutive plain runs of the test: 0 failures on this branch, 2 failures on main's version.
  • Full suite under the dummy SDL drivers: 910 passed.
  • black on the changed file: unchanged.
  • CI on this PR head.

This PR description was drafted during a Gardener session (https://github.com/Stephenson-Software/gardener).


drafted by Claude on behalf of Daniel Stephenson

🤖 Generated with Claude Code

#201 patched rollEvent on src.business.adventures, but docks imports
adventures as business.adventures - pytest.ini puts both . and src on
the path, so those are two module objects and the patch never reached
the call site. The flake it meant to fix survived it: main went red
again on the very next run (35556530705).

The roll is now pinned with patch.object on docks.adventures, and the
day assertion is exact (she founders on leg one), so a roll that isn't
pinned fails five runs in seven instead of one in forty. Forcing the
real roll to driftwood every leg fails on main's version of the test
and passes here; 200 consecutive plain runs, 0 failures.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
@dmccoystephenson

Copy link
Copy Markdown
Member Author

Self-review rubric (posted as a comment; the formal Review API is not available to this session):

  • Scope: PASS — one file, tests/location/test_docks.py; the only hunks are the failing test's pin, its explanatory comment, the tightened day assertion, and the plan local that assertion no longer needs.
  • Tests-new: no signal — no new public method or function.
  • Tests-fix: PASS — empirical. With the fix stashed, a probe that forces the real random.choice to draw driftwood every leg makes the test FAIL, and 200 plain runs produce 2 failures; with the fix restored the adversarial probe PASSES and 200 plain runs produce 0 failures.
  • Sibling structure: PASS — patch.object(<module the code under test resolves>, ...) is the pattern tests/business/test_boats.py:58 already uses (patch.object(boats.fish, "rollFishType", ...)); the # prepare / # call / # check layout is unchanged.
  • Sibling renames: no signal — nothing renamed.
  • Docs: PASS — README, schemas/*.json, PLANNING.md, and version.txt are unaffected by a test-only change; none describes test internals.
  • Issue resolution: no signal — no Closes reference; Flaky test: test_a_voyage_that_founders_ends_early fails about one run in forty #200 was already closed by Pin the event roll in the foundering-voyage test (flake) #201 and this PR fixes the gap Pin the event roll in the foundering-voyage test (flake) #201 left, as the body states.
  • CI: PASS — run 35710298793 green on head cc52cd3 (test job, 1m05s).
  • Schema-sync: no signal — no persisted field touched.
  • Money-format: no signal — no money display touched.
  • Deterministic-tests: PASS — every random draw the test reaches is now pinned at the module docks actually calls (rollEvent) or on the shared random module (randint, random); loseCrew's random.choice is not reached because the leak's first choice only damages.
  • Headless-pygame: no signal — no pygame path touched.
  • camelCase: PASS — the one new local is sailedAdventures.

One observation outside the diff, not acted on here: the same dual-module trap (src.x vs x both importable via pytest.ini) would silently neutralise any future patch("src.<pkg>.<module>.<name>") where the caller resolved <pkg>.<module>. A sweep of tests/ found no other instance today — the remaining src.-prefixed patches target either the shared random/shutil modules or a function the test itself calls through the same src. module — so it was left as a note rather than an issue.

tests/location/test_docks.py:1520 — the == startingDay + 1 assertion is deliberately exact rather than <; it is what turns an unpinned roll from a one-in-forty flake into a five-in-seven failure, so a regression of this kind would show on the first CI run.

This comment was drafted during a Gardener session (https://github.com/Stephenson-Software/gardener).


drafted by Claude on behalf of Daniel Stephenson

@dmccoystephenson
dmccoystephenson merged commit 852e784 into main Sep 22, 2026
1 check passed
@dmccoystephenson
dmccoystephenson deleted the feature/pin-voyage-event-roll-at-the-call-site branch September 22, 2026 09:28
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