Skip to content

fix: admin healthcheck IPv6 + single-word distractor prompt#233

Merged
mrviduus merged 1 commit into
mainfrom
fix/admin-healthcheck-and-distractor-prompt
May 8, 2026
Merged

fix: admin healthcheck IPv6 + single-word distractor prompt#233
mrviduus merged 1 commit into
mainfrom
fix/admin-healthcheck-and-distractor-prompt

Conversation

@mrviduus
Copy link
Copy Markdown
Owner

@mrviduus mrviduus commented May 7, 2026

Two narrow fixes surfaced after the gemma4:e4b deploy

1. `admin` container has been (unhealthy) since deploy

Symptom: `textstack_admin_prod` shows `(unhealthy)` with `FailingStreak=241` (= 2 hours of consecutive 30s checks failing). Healthcheck output: `wget: can't connect to remote host: Connection refused`.

Root cause: healthcheck does `wget http://localhost:81\`. In Alpine `localhost` resolves to `::1` (IPv6) first. Vite binds only to `0.0.0.0` (IPv4) per `pnpm dev --host 0.0.0.0`. wget tries IPv6 → connection refused.

Fix: `localhost` → `127.0.0.1` (single line in `docker-compose.yml`).

Verified on prod:
```
docker exec textstack_admin_prod wget -qO- http://localhost:81/ # → Connection refused
docker exec textstack_admin_prod wget -qO- http://127.0.0.1:81/ # → returns HTML
```

API healthcheck has the same `localhost` text but is fine because Kestrel binds both IPv4+IPv6 by default. Only Vite is affected.

2. Gemma4 distractor output gets stripped

Symptom: smoke tested `gemma4:e4b` for "eventual consistency" — it returned `["strong consistency", "read-after-write", "data loss", ...]`. After `ParseWordList`'s `!w.Contains(' ')` filter, only `read-after-write` and `causality` survive. `distractors.Count >= 3` fails → fallback to random vocab pool words.

Root cause: Gemma4 prefers phrasal answers; the parser is built for single tokens (existing constraint, not introduced by the model swap — but the swap surfaces it).

Fix: prompt now explicitly demands SINGLE-WORD distractors with concrete examples (`linearizability` not `strong consistency`). Hyphens still allowed since the parser permits them.

This keeps the existing parser logic intact (no risk of letting phrasal noise through) while giving the LLM clear guidance.

Test plan

  • `dotnet build` — green.
  • `dotnet test tests/TextStack.UnitTests` — 216/216.
  • After merge + deploy: `textstack_admin_prod` flips to `(healthy)` within 30–90s.
  • After merge + deploy: save a vocab word, verify 5 distractors appear in DB (not fallback).

🤖 Generated with Claude Code

Two narrow fixes surfaced after the gemma4:e4b deploy.

1. admin healthcheck used localhost which Alpine resolves to ::1 (IPv6)
   first; vite binds only to 0.0.0.0 (IPv4) so wget got "Connection
   refused" forever. textstack_admin_prod has been (unhealthy) with
   FailingStreak=241 since deploy. Switched to 127.0.0.1.

2. DistractorGenerator's parser drops multi-word entries
   (`!w.Contains(' ')`). Gemma4 likes phrasal distractors ("strong
   consistency", "data loss") so most of its output gets stripped and
   the 5-distractor minimum fails — falls back to random vocab words.
   Added explicit single-word instruction with concrete examples.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
@mrviduus mrviduus merged commit d73d132 into main May 8, 2026
5 checks passed
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