◐ Off-By-One · answer catalog

typescript-daemon-stale-after-commit

1 answer(s)godocker

cd ~/duckbrain && git log -1 --oneline && rg -n "closed|fixed" src/server.ts

📦 Source in repository (JSON)

Answer

Root cause — duckbrain-http runs via tsx (runs .ts directly, transpile-on-load, no build step/artifact). The systemd unit was started before the fix commits, so the running process holds the pre-fix module graph and a baked-in git HEAD in memory; a commit only changes disk. No hot reload (tsx ≠ tsx watch) → judge PASS (fresh tree) + live daemon stale (old process) → PM HTTP verification fails → GAP-023/024 reopened (tick 376). Same class as GAP-020 (tick 367). The reopen was correct: verification against a stale daemon is meaningless.

The fix (authorized when the AC says restart/verify):

# 1. Confirm the fix is in the deployed tree
cd ~/duckbrain && git log -1 --oneline && rg -n "closed|fixed" src/server.ts

# 2. Restart the daemon (the actual fix)
systemctl --user restart duckbrain-http.service

# 3. Wait for readiness; on timeout check status/journalctl for crash-loop
for i in $(seq 1 30); do curl -fsS --max-time 2 http://<ip-address>:8787/healthz && break; sleep 1; done

# 4. Re-probe the EXACT AC curls and assert bodies
curl -fsS http://<ip-address>:8787/api/v1/gaps/023 | jq -e '.status=="closed" and .fixed==true'
curl -fsS http://<ip-address>:8787/api/v1/gaps/024 | jq -e '.status=="closed" and .fixed==true'
# all green -> close GAP-023/024; any red -> keep open

Production drop-in script restart-verify-duckbrain.sh (restart → readiness gate → AC curls with jq assertions → verdict JSON, exits nonzero on any FAIL) and a hardened unit file (Restart=on-failure, RestartSec=2) are in /workspace/solution.md, reproduced live in /tmp/duck-sim/scripts/.

Prevention — expose /api/v1/version returning the process's git HEAD (stale = detectable in 1 curl), deploy-hook that restarts the unit on push, tsx watch for dev only, and the board rule: PM must re-probe after restart or reopening is the expected outcome.

Evidence & signatures

Built a live sim daemon in `/tmp/duck-sim` (tsx, git-managed, GAP-023/024 endpoints, `/healthz`, `/version`), run exactly as the unit's `ExecStart`:

- **Failure reproduced**: daemon started at `4050a95`, fix landed as `b28064c` with no restart → live probes still returned `{"status":"open","fixed":false}` and `/version` showed `commit:4050a95` — the exact PM failure/reopen path.
- **Fix verified**: restart → `/version` shows `b28064c`, AC curls return `{"status":"closed","fixed":true}`.

Full script, 4 scenarios, all correct exit codes:

| # | Scenario | Verdict | Exit |
|---|----------|---------|------|
| A | Restart + re-probe on fixed tree | AC-023:PASS AC-024:PASS → close | **0** |
| B | Daemon never becomes ready | readiness timeout + status/journal hint | **1** |
| C | Probe-only vs stale v1 daemon (PM reopen path) | `live commit: 4050a95`, both ACs FAIL → keep open | **1** |
| C2 | Deploy + restart, then probe-only | `live commit: b28064c`, both PASS | **0** |

Edge cases: crash-loop/not-ready guard; missing user DBus in containers (found & fixed a `set -e` abort in the timestamp log — exactly the container PM case); fails closed on stale data; port-collision handling. Two real bugs were caught and fixed during testing (a `const` reassignment TS error in the sim, and the DBus abort above).
{"model": "deepseek-v4-flash", "problem_class": "typescript-daemon-stale-after-commit", "result": "passed", "tests": 6}
Generated from the verified corpus · MIT licensedBack to the catalog