Stage 14: solver & dictionary split — consume published module + DAWG artifact (TODO-1/TODO-2)
- backend/go.mod pins gitea.iliadenisov.ru/developer/scrabble-solver v1.0.0; the engine's imports use the published module path; go.work drops the solver replace (GOPRIVATE fetches it directly from Gitea). The solver's wordlist/dictdawg are now public packages. - CI (go-unit, integration): drop the solver sibling-clone, set GOPRIVATE, and download the dictionary DAWG release artifact (scrabble-dawg-<DICT_VERSION>.tar.gz from the new scrabble-dictionary repo) for BACKEND_DICT_DIR. - Docs: ARCHITECTURE §5/§11/§13/§14 + backend/README updated to the published-module + release-artifact model. PLAN.md re-scoped Stage 14 to the split and added Stages 15 (deploy infra & test contour), 16 (prod contour), 17 (dual Telegram bots); TODO-1/TODO-2 marked done.
This commit is contained in:
+21
-17
@@ -154,8 +154,11 @@ internal/connector/ # backend gRPC client to the Telegram connector (operator b
|
||||
|
||||
```sh
|
||||
docker run -d --name scrabble-pg -e POSTGRES_PASSWORD=dev -p 5432:5432 postgres:17-alpine
|
||||
# DAWGs: extract the dictionary release artifact (or point at a local scrabble-solver/dawg):
|
||||
mkdir -p /tmp/dawg && curl -fsSL https://gitea.iliadenisov.ru/developer/scrabble-dictionary/releases/download/v1.0.0/scrabble-dawg-v1.0.0.tar.gz | tar xz -C /tmp/dawg
|
||||
BACKEND_POSTGRES_DSN='postgres://postgres:dev@localhost:5432/postgres?search_path=backend&sslmode=disable' \
|
||||
BACKEND_DICT_DIR=../../scrabble-solver/dawg \
|
||||
BACKEND_DICT_DIR=/tmp/dawg \
|
||||
GOPRIVATE='gitea.iliadenisov.ru/*' \
|
||||
go run ./cmd/backend
|
||||
```
|
||||
|
||||
@@ -178,19 +181,18 @@ go run ./cmd/jetgen # rewrites internal/postgres/jet against a temp containe
|
||||
|
||||
## Engine & dictionaries
|
||||
|
||||
`internal/engine` consumes the sibling `scrabble-solver` module in-process. Its
|
||||
bare module path (`scrabble-solver`, not a URL) cannot be fetched via VCS, so the
|
||||
workspace `go.work` carries `replace scrabble-solver => ../scrabble-solver` and
|
||||
the build must run from the repository root (the workspace), not from this module
|
||||
in isolation. `github.com/iliadenisov/dafsa` (the DAWG loader) is a direct
|
||||
dependency. CI clones the public solver repository into `../scrabble-solver`
|
||||
before building (see `.gitea/workflows/`); locally, check it out next to this
|
||||
repository. Committed dictionaries (`en_sowpods.dawg`, `ru_scrabble.dawg`,
|
||||
`ru_erudit.dawg`) live in the solver's `dawg/` directory; the engine loads them
|
||||
by `(variant, dict_version)` from a directory path. Since Stage 3 the backend
|
||||
loads them at startup from the **required** `BACKEND_DICT_DIR` (a missing
|
||||
dictionary aborts the boot); the future versioned-artifact direction is recorded
|
||||
in [`../PLAN.md`](../PLAN.md) TODO-2.
|
||||
`internal/engine` consumes `scrabble-solver` in-process as a **published, versioned
|
||||
module** (`gitea.iliadenisov.ru/developer/scrabble-solver`, pinned in `go.mod`). Set
|
||||
`GOPRIVATE=gitea.iliadenisov.ru/*` so go fetches it directly from this Gitea (skipping
|
||||
the public proxy/checksum DB); no sibling checkout or `go.work` replace is needed (for
|
||||
local solver co-development you may add a temporary replace — see `go.work`).
|
||||
`github.com/iliadenisov/dafsa` (the DAWG loader) is a direct dependency. The dictionaries
|
||||
(`en_sowpods.dawg`, `ru_scrabble.dawg`, `ru_erudit.dawg`) ship as a **release artifact**
|
||||
from the [`scrabble-dictionary`](https://gitea.iliadenisov.ru/developer/scrabble-dictionary)
|
||||
repo (one semver per set); the engine loads them by `(variant, dict_version)` from
|
||||
`BACKEND_DICT_DIR`. Since Stage 3 the backend loads them at startup as a hard dependency
|
||||
(a missing dictionary aborts the boot). See [`../PLAN.md`](../PLAN.md) Stage 14
|
||||
(TODO-1/TODO-2).
|
||||
|
||||
## Tests
|
||||
|
||||
@@ -201,6 +203,8 @@ go test -tags=integration -count=1 -p=1 ./... # Postgres-backed (needs Docker)
|
||||
|
||||
Integration tests are guarded by the `integration` build tag and run against a
|
||||
throwaway `postgres:17-alpine` container; they fail loudly when Docker is absent
|
||||
rather than skipping. The `internal/engine` tests load the committed DAWGs from
|
||||
`BACKEND_DICT_DIR` (defaulting to the sibling `../scrabble-solver/dawg`) and fail
|
||||
loudly when that directory is absent.
|
||||
rather than skipping. The `internal/engine` tests load the DAWGs from
|
||||
`BACKEND_DICT_DIR` (CI sets it to the extracted dictionary release artifact; locally it
|
||||
defaults to a `scrabble-solver/dawg` sibling checkout) and fail loudly when that directory
|
||||
is absent. `GOPRIVATE=gitea.iliadenisov.ru/*` is needed for go to fetch the pinned solver
|
||||
module.
|
||||
|
||||
+1
-1
@@ -3,6 +3,7 @@ module scrabble/backend
|
||||
go 1.26.3
|
||||
|
||||
require (
|
||||
gitea.iliadenisov.ru/developer/scrabble-solver v1.0.0
|
||||
github.com/XSAM/otelsql v0.42.0
|
||||
github.com/gin-gonic/gin v1.12.0
|
||||
github.com/go-jet/jet/v2 v2.14.1
|
||||
@@ -20,7 +21,6 @@ require (
|
||||
go.opentelemetry.io/otel/sdk/metric v1.43.0
|
||||
go.opentelemetry.io/otel/trace v1.43.0
|
||||
go.uber.org/zap v1.27.1
|
||||
scrabble-solver v0.0.0-00010101000000-000000000000
|
||||
)
|
||||
|
||||
require (
|
||||
|
||||
@@ -3,7 +3,7 @@ package engine
|
||||
import (
|
||||
"math/rand"
|
||||
|
||||
"scrabble-solver/rules"
|
||||
"gitea.iliadenisov.ru/developer/scrabble-solver/rules"
|
||||
)
|
||||
|
||||
// blankTile marks a blank tile in a hand or in the bag, matching the
|
||||
|
||||
@@ -5,7 +5,7 @@ import (
|
||||
"slices"
|
||||
"testing"
|
||||
|
||||
"scrabble-solver/rules"
|
||||
"gitea.iliadenisov.ru/developer/scrabble-solver/rules"
|
||||
)
|
||||
|
||||
// allTiles returns the full multiset of tiles a bag is filled from, in ruleset
|
||||
|
||||
@@ -3,9 +3,9 @@ package engine
|
||||
import (
|
||||
"fmt"
|
||||
|
||||
"scrabble-solver/board"
|
||||
"scrabble-solver/rules"
|
||||
"scrabble-solver/scrabble"
|
||||
"gitea.iliadenisov.ru/developer/scrabble-solver/board"
|
||||
"gitea.iliadenisov.ru/developer/scrabble-solver/rules"
|
||||
"gitea.iliadenisov.ru/developer/scrabble-solver/scrabble"
|
||||
)
|
||||
|
||||
// ActionKind classifies a turn in the move log.
|
||||
|
||||
@@ -3,7 +3,7 @@ package engine
|
||||
import (
|
||||
"testing"
|
||||
|
||||
"scrabble-solver/scrabble"
|
||||
"gitea.iliadenisov.ru/developer/scrabble-solver/scrabble"
|
||||
)
|
||||
|
||||
// blankCellFlag is the bit board cells set for a blank tile (board.go encoding).
|
||||
|
||||
@@ -3,7 +3,7 @@ package engine
|
||||
import (
|
||||
"fmt"
|
||||
|
||||
"scrabble-solver/scrabble"
|
||||
"gitea.iliadenisov.ru/developer/scrabble-solver/scrabble"
|
||||
)
|
||||
|
||||
// blankLetter is how a blank tile is written in the decoded, domain-facing API:
|
||||
|
||||
@@ -17,7 +17,7 @@ import (
|
||||
"errors"
|
||||
"fmt"
|
||||
|
||||
"scrabble-solver/rules"
|
||||
"gitea.iliadenisov.ru/developer/scrabble-solver/rules"
|
||||
)
|
||||
|
||||
// Variant identifies a Scrabble variant the backend offers. Each maps to a
|
||||
|
||||
@@ -3,10 +3,10 @@ package engine
|
||||
import (
|
||||
"fmt"
|
||||
|
||||
"scrabble-solver/board"
|
||||
"scrabble-solver/rack"
|
||||
"scrabble-solver/rules"
|
||||
"scrabble-solver/scrabble"
|
||||
"gitea.iliadenisov.ru/developer/scrabble-solver/board"
|
||||
"gitea.iliadenisov.ru/developer/scrabble-solver/rack"
|
||||
"gitea.iliadenisov.ru/developer/scrabble-solver/rules"
|
||||
"gitea.iliadenisov.ru/developer/scrabble-solver/scrabble"
|
||||
)
|
||||
|
||||
// scorelessLimit is the number of consecutive scoreless turns (passes and
|
||||
|
||||
@@ -4,8 +4,8 @@ import (
|
||||
"errors"
|
||||
"testing"
|
||||
|
||||
"scrabble-solver/board"
|
||||
"scrabble-solver/scrabble"
|
||||
"gitea.iliadenisov.ru/developer/scrabble-solver/board"
|
||||
"gitea.iliadenisov.ru/developer/scrabble-solver/scrabble"
|
||||
)
|
||||
|
||||
// newEnglishGame starts a two-player English game with the given seed.
|
||||
|
||||
@@ -7,8 +7,8 @@ import (
|
||||
"runtime"
|
||||
"testing"
|
||||
|
||||
"scrabble-solver/rules"
|
||||
"scrabble-solver/scrabble"
|
||||
"gitea.iliadenisov.ru/developer/scrabble-solver/rules"
|
||||
"gitea.iliadenisov.ru/developer/scrabble-solver/scrabble"
|
||||
)
|
||||
|
||||
// testVersion labels the single dictionary version the tests register.
|
||||
|
||||
@@ -9,7 +9,7 @@ import (
|
||||
|
||||
dawg "github.com/iliadenisov/dafsa"
|
||||
|
||||
"scrabble-solver/scrabble"
|
||||
"gitea.iliadenisov.ru/developer/scrabble-solver/scrabble"
|
||||
)
|
||||
|
||||
// dictFiles maps each variant to its committed DAWG filename, as built by
|
||||
|
||||
@@ -4,8 +4,8 @@ import (
|
||||
"errors"
|
||||
"testing"
|
||||
|
||||
"scrabble-solver/board"
|
||||
"scrabble-solver/scrabble"
|
||||
"gitea.iliadenisov.ru/developer/scrabble-solver/board"
|
||||
"gitea.iliadenisov.ru/developer/scrabble-solver/scrabble"
|
||||
)
|
||||
|
||||
// TestRegistryOpensEveryVariant checks that Open loads all three variants at the
|
||||
|
||||
Reference in New Issue
Block a user