Files
noitu/plans/260908-2056-wiktionary-dump-corpus-and-meanings/phase-04-meanings-on-the-wire.md
T

109 lines
4.8 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
---
phase: 4
title: "Phase 4: Meanings on the wire"
status: completed
priority: P1
effort: "3h"
dependencies: [3]
---
# Phase 4: Meanings on the wire
## Overview
Carry a word's senses in the two messages that introduce a word to the client —
`PlayedWord` and `GameStarted` — regenerate the Go and JS types and the binary fixtures,
and have the room attach the senses where it already renders those messages.
## Requirements
- Functional: `proto/noitu/v1/game.proto` gains
```proto
// One definition of a word as Wiktionary gives it: the part of speech it
// sits under, in Vietnamese ("danh từ"), empty when the heading was not one
// the builder knows, and the definition stripped of markup at at most 200
// characters. Plain text both: the client renders them as text, never as
// markup.
message Sense {
string pos = 1;
string gloss = 2;
}
message PlayedWord {
…
// At most five. Empty when the dictionary has no definition for the word.
repeated Sense meanings = 7;
}
message GameStarted {
…
repeated Sense opening_meanings = 9;
}
```
(Validation session 1: `Sense` instead of a bare string, so the label is a field.)
- Functional: `wsapi.Dictionary` gains `Meanings(word string) []dictionary.Sense`.
`game.Dictionary` does not change; the engine never reads a meaning. `convert.go` gains
`Senses([]dictionary.Sense) []*noituv1.Sense`.
- Functional: `convert.PlayedWord` takes the senses (`PlayedWord(m game.Move, byMe bool,
meanings []dictionary.Sense)`) and its one caller, `sendTurnUpdate` (`room.go:903`, also
used by the resume path), passes `r.dict.Meanings(move.Word)`; `sendGameStarted` fills
`OpeningMeanings` from `r.dict.Meanings(r.opening)`. Both paths are per recipient already;
the lookup happens once per move, before the per-seat loop.
- Functional: the reconnect path (`GameStarted` + last `TurnUpdate`) carries meanings
without further change; a test asserts a resumed client sees the opening's and last word's
senses.
- Functional: `buf generate` regenerates `server/gen/` and `web/src/lib/proto/`;
`go test ./internal/wsapi -update` regenerates `proto/testdata/`; the JS decode test reads
the new field.
- Functional: the hand-built dictionaries in `wsapi` tests implement `Meanings`; at least
one returns senses so the assertion is not vacuous.
- Non-functional: frame size grows by up to ~1 KB per move. Nothing in the client or server
caps frames near that.
## Architecture
```
room.handleSubmit ── engine.Submit ──► Move ──► meanings := r.dict.Meanings(move.Word)
for each seat: PlayedWord(move, byMe, meanings)
room.sendGameStarted ─────────────────────────► OpeningMeanings: r.dict.Meanings(r.opening)
```
The bot room path renders through the same `sendTurnUpdate`, so bot moves carry meanings
without a second change.
## Related Code Files
- Modify: `proto/noitu/v1/game.proto`
- Regenerate: `server/gen/noitu/v1/game.pb.go`, `web/src/lib/proto/**`, `proto/testdata/*`
- Modify: `server/internal/wsapi/room.go` — `Dictionary` interface, `sendGameStarted`,
the `PlayedWord(` call in `sendTurnUpdate`
- Modify: `server/internal/wsapi/convert.go` — `PlayedWord` signature, `Senses`
- Modify: `server/internal/wsapi/*_test.go` — fake dictionaries gain `Meanings`; new
assertions on `Played.Meanings` and `OpeningMeanings`, including after a resume
- Modify: `web/tests/game-wire.test.js` — the fixture decode asserts the new fields
## Implementation Steps
1. Edit the schema; `buf lint`; `buf generate`.
2. Interface and room changes; fix compile errors in tests by adding `Meanings` to fakes.
3. Add the assertions; `go test ./internal/wsapi -update` then `go test ./... -race`.
4. `cd web && npm test` — the wire test decodes the regenerated fixtures.
5. `make proto-check` — committed generated code matches the schema.
## Success Criteria
- [x] `buf lint` clean; `make proto-check` clean.
- [x] A bot game and a room game deliver `Played.Meanings` with `Pos` and `Gloss` for a word
the fake dictionary has senses for, and an empty list for one it does not.
- [x] A resumed session's `GameStarted.OpeningMeanings` and replayed `TurnUpdate.Played.Meanings`
are populated.
- [x] `proto/testdata/` fixtures regenerated and the JS suite decodes them.
## Risk Assessment
**Field numbers.** `PlayedWord` uses 1–6, `GameStarted` 1–8; 7 and 9 are free and nothing
is reserved in either. Signal: `buf breaking` complaint. Response: none expected; adding a
repeated field is wire-compatible, and there is one client.
**Meanings on `PlayerEliminated.suggestions`.** Out of scope by the plan's non-goals; a
reviewer may ask. Response: the suggestions are shown to somebody who just lost the turn,
a list of words, and a meaning under each would be a second UI. Not this plan.