Repository navigation
W3-8: keep the log cursor valid across any location change; drop two hook suppressions; typed materialization fixtures - #85
Merged
Conversation
…uppressions The perception log cursor is an index into one location's log. PR #75 re-anchored it on travel arrival only, so a GM move (PATCH creature location_id), the session player_location setter or any other direct location change left it indexing the old location's log: the player missed the first events at the destination, or read the destination's history from before it arrived. The cursor now remembers which location it indexes; EventLog.append (the one writer of the entities log, now also used for ROUND_START) re-anchors every moved creature before the first event after the move, and readers treat a cursor for another location as "nothing new here". The explicit travel-arrival reset is subsumed. EventLog: the auto-scroll effect lists the stable virtualizer and sticky ref as deps. SchemaForm: the reset-on-initialValues effect reads the latest defaults through useEffectEvent, so it still runs only when initialValues changes. test_materialization fixtures use Race, CharClass, Ability and DamageComponent instead of raw strings, and the file passes strict mypy.
Owner
Author
|
Independent review: PASS
|
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.
Card W3-8. On main at 2f79530, none of the three items had been fixed yet.
1.
react-hooks/exhaustive-depssuppressions (frontend)EventLog.tsx(FullLog auto-scroll). The effect usedvirtualizerandstickyRefwithout listing them as deps. Both are stable.useVirtualizercreates one instance per mount withuseState(() => new Virtualizer), andstickyRefis a ref. So they are now listed as deps and the suppression is gone. The effect still runs only whenentryCountchanges.SchemaForm.tsx(reset oninitialValues).formDefaultsis rebuilt on every render. The suppression was there so the form resets only wheninitialValueschanges, not on every render and not whenschemaorlangchange (that would wipe the user's edits). The effect now callsuseEffectEvent(() => reset(formDefaults))and lists only[initialValues]as a dep. Behaviour is unchanged. React 19.2 and eslint-plugin-react-hooks 7 both support this.Tests. These guard the behaviour the suppressions protected, so they pass on main as well. I checked that they fail against a naive fix:
EventLog.autoscroll.test.tsx(new file; spies onscrollToIndex):SchemaForm.test.tsx:initialValues(newonSubmit, new but equalschema);langchange;initialValueschanges.schema, langto the SchemaForm deps makes 2 tests fail.Note. With the suppression gone, the React Compiler lint now analyses
FullLog. It reports one warning (not an error):react-hooks/incompatible-libraryforuseVirtualizer(). The React Compiler is not enabled in the Vite build, and before this change the suppression made the lint skipFullLoganyway, so nothing changes at runtime.eslint src/exits 0. I did not add a new suppression to hide the warning.2.
tests/unit/test_materialization.pyfixtures (tests only)The fixtures now use
Race.HUMAN,CharClass.FIGHTER,Ability.STRand(DamageComponent("1d4", DamageType.PIERCING),)instead of raw strings. The old values were also wrong for their types:ability="strength"is not a validAbilityvalue (the enum uses"str"), anddamage="1d4"should be a tuple ofDamageComponent.To make "mypy strict catches drift" actually true for this file, I also:
QueryFn/EmitFn;entitiesaslist[Entity];type: ignore;isinstance(info.value, SquadInfo)narrowing.The file now passes
MYPYPATH=src mypy tests/unit/test_materialization.py(31 errors before). Butmake typecheckonly runsmypy src/, so CI does not type-check tests at all. See the findings below.3. Log cursor not reset on a GM move of the player (backend)
Symptom. A GM moves the player with
PATCH /api/master/sessions/{sid}/creatures/{id}and{"location_id": ...}. The player then misses the first events at the destination, or reads that location's history from before they arrived.Root cause.
_last_seen_log_indexis an index into one location's log, but nothing recorded which location. PR #75 re-anchored it only inActivationManager._record_arrival(travel). Every other location change kept the old index:commands_creatures.patch_creature;GameSession.player_locationsetter;If the old index was larger than the destination's log, the player missed events; if smaller, they read old ones.
Fix (one central place).
Entitynow has_log_cursor_location, set to the entity's starting location in__post_init__.EventLog.append(location_id, event)is now the only way the entities layer writes its log.record()uses it, and so doeslog_round_start, which used to append directly. Before each append it re-anchors the cursor of every creature that has moved since the log last saw it. Anything already logged where the creature now stands predates its arrival, so the cursor starts at the end of that log. A creature that left the location gets re-anchored at its new location.perceived_events,get_new_perceived_events,get_new_raw_events) shareunread_events/mark_log_read. If the cursor points at another location, the reader treats it as "nothing new here".location_id. The explicit travel reset from PR Log how long a flee or journey took and where it ended #75 is now redundant and removed; the PR Log how long a flee or journey took and where it ended #75 tests still pass.Regression test.
tests/unit/test_log_cursor_relocation.pyhas 7 tests; I verified all 7 fail on main (source reverted, tests kept) and pass with the fix:session.player_locationsetter;ROUND_STARTat the destination is read;No other real bugs turned up while writing the tests.
Checks
make check: green. Backend 2946 passed; frontend 46 files / 359 tests passed; lint and typecheck clean apart from the one warning above.docker-compose.test.ymlsettings (DND_DICE_SEED=42,DND_CONTENT_DIR=tests/integration/content, uvicorn on :8001) and ranBACKEND_URL=http://localhost:8001 uv run pytest tests/integration/: 184 passed.Found but not fixed (out of scope)
make typecheck=mypy src/). Running mypy strict overtests/would needMYPYPATH=src(or apy.typedmarker), and many existing test files would have to be cleaned up first.CombatManager._record_directly(the fallback used when norecord_eventis injected, which is only in standalone unit tests) still appends to the log directly, bypassingEventLog.append. It is not used in the wired layer.