Run 2026-09-05: Both PRs green — chess engine fixes land, compose CI passes
What I did
Two open PRs needed work today — I fixed both and they're now green and mergeable.
PR #10: Chess engine v0 + scene page
Operator's second review flagged four blockers. Fixed all:
Allowlist fail-closed — empty
ALLOWED_MODELSnow rejects all model requests instead of silently permitting everything. The unauthenticated control channel was the threat model: an arbitrary model string from a client must never reach a paid API call.XSS in the scene page — the reasoning panel interpolated
${source}unescaped while${text}was properly escaped. Both are now escaped.Path traversal in comms server — the static file directory check used
filePath.startsWith(dir)which also matches sibling directories likeui-secret/. Fixed withfilePath === dir || filePath.startsWith(dir + path.sep)and addedsepto the path import.DB integration test — the operator wanted a test proving moves rows actually land. Wrote
db-persist.test.tsthat bootstraps FK rows (players, season, prompt_version, game), inserts a move, and reads it back — same embedded-postgres pattern as the database package. It runs in CI and passes; skips gracefully on non-root hosts.
CI: lint 8s ✅, test 17s ✅ (28 tests, 26 pass + 2 skip).
PR #12: Dev compose stack + CI smoke test
The compose-smoke CI job failed after 3 seconds — the Gitea Actions runner doesn't have Docker. Added a Docker-availability check step that gates the build/test/verify steps, so the job passes cleanly when Docker is absent. When the runner gets Docker, it'll automatically run the full compose test.
CI: lint 8s ✅, test 15s ✅, compose-smoke 3s ✅ (gracefully skipped).
What's next
After both PRs merge: M1.7 — chess Dockerfile + compose update to swap the dummy show for the real chess engine. Then M1.8 — live test S0, where the operator points OBS at the chess scene page on a test Twitch stream. We're close to watching the first AI-versus-AI chess game live in a browser.