fix(ci): arangodb probe can never pass (401) + stress-test fake/impossible steps#640
Conversation
…calhost
`tests.yml` probed the arangodb:3.11 service with
`curl -f http://localhost:8529/_api/version`. That could never succeed, for
two independent reasons, both verified empirically against the exact image:
1. `curl` is NOT installed in arangodb:3.11.
Present instead: wget, nc, arangosh. (curl/python3/openssl all MISSING.)
2. `localhost` inside the container resolves to `::1` first (/etc/hosts maps
both `127.0.0.1 localhost` and `::1 localhost`), but ArangoDB binds IPv4
only. Probing `localhost` returns "Connection refused"; probing
`127.0.0.1` returns {"server":"arango","version":"3.11.14"}.
Net effect: the server logged "ArangoDB ... is ready for business" and was
then killed by Actions with "Failed to initialize container arangodb:3.11 /
One or more containers failed to start". This is why `Integration Tests` and
`Haskell Integration Tests` were red on main — not a code fault.
Fix: use wget (present in the image) against 127.0.0.1 (actually listening).
Applied to all three arangodb service definitions in this workflow.
Verified end-to-end locally with the real image, not by inspection:
- old curl probe -> command absent
- wget + localhost -> unhealthy after 10 retries (Connection refused)
- wget + 127.0.0.1 -> healthy in 15s <- shipped
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Self-inflicted regression from the previous commit, caught by CI.
I documented the health-check reasoning with `#` comments placed INSIDE the
`options: >-` block. In a YAML folded scalar `#` is not a comment — it is
literal text — so the prose was folded into the docker arguments:
docker create ... # `curl` is NOT installed in arangodb:3.11 ... --health-cmd ...
invalid reference format
##[error]Exit code 1 returned from process: file name '/usr/bin/docker'
The container never even got created, so `Integration Tests` and
`Haskell Integration Tests` failed earlier than before rather than passing.
Comments moved above the `options:` key, where they are real comments.
Verified by parsing the workflow and asserting on the FOLDED VALUE — the
check I should have run before pushing, rather than eyeballing the source:
[integration-tests/arangodb] --health-cmd "wget -q --spider
http://127.0.0.1:8529/_api/version || exit 1" --health-interval 10s
--health-timeout 5s --health-retries 10
... 5/5 service option strings start with a flag and contain no '#'.
RESULT: CLEAN
The underlying fix (wget + 127.0.0.1, verified healthy in 15s against the
real image) is unchanged.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
… start-up
Two further faults, both found by reading the real CI run of the previous
commit rather than assuming it was done.
1. `Integration Tests` died with **exit 127 — command not found**.
The job installs the RUST toolchain (dtolnay/rust-toolchain), then runs
`mix test --include integration`. There is no erlef/setup-beam anywhere in
that job, so `mix` was simply not on PATH. Added setup-beam + `mix deps.get`
before the Elixir step, pinned identically to the E2E job (OTP 27.0 /
Elixir 1.17) so the two jobs cannot drift apart.
2. `Haskell Integration Tests` still reported "Failed to initialize container
arangodb:3.11" **despite carrying the corrected health probe**. In the same
run, `Integration Tests` reported "arangodb service is healthy" at 07:37:03,
so the probe itself is right — the Haskell job's instance simply did not
finish starting inside the retry budget while both jobs contended for the
runner. ArangoDB's first boot is two-phase: it initialises, logs "server
will now shut down due to upgrade, database initialization or admin
restoration", exits, and only then restarts for real. Failures during that
window were being counted against --health-retries.
Added `--health-start-period 40s` to all three arangodb services, which is
exactly what that flag is for: failures inside the start period do not count
toward the retry budget.
Verified:
* flag accepted by docker against the real image; healthy in 15s
* folded-scalar assertion re-run — 5/5 service option strings start with a
flag and contain no '#'; start-period present on all three arangodb
services (this is the check that caught the comment-folding regression
two commits ago, so it now runs every time)
* step order asserted programmatically: setup-beam at index 9 precedes
`mix test` at index 11
Evidence the previous commit DID work, for the record: that run reached
"arangodb service is healthy" and "dragonfly service is healthy", and ArangoDB
accepted writes ({"error":false,"code":201}). The container barrier is cleared;
these are the next two faults behind it.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
…mpossible
Two independent CI faults, both diagnosed from real job logs and verified
against the real artefacts rather than inferred.
## 1. ArangoDB health probe: 401, always
`/_api/version` requires authentication whenever ARANGO_ROOT_PASSWORD is
set — and busybox wget (the only HTTP client in arangodb:3.11) has no
--user/--password option at all, so the probe *cannot* be authenticated.
Measured against the real image, polling once a second for 25s:
/_api/version exit 1 at EVERY timepoint (401)
/_admin/server/availability exit 1 for 3s, then exit 0 onwards
So the probe never succeeded, on any job, ever. What varied was whether
the runner's own health poll happened to read .State.Health.Status before
Docker had recorded any health state — reading empty and reporting
"healthy". That is why one job logged "arangodb service is healthy"
*before* the container logged "Initializing root user", while another job
in the same run went starting -> starting -> unhealthy and failed.
The earlier "verified healthy in CI" on this branch was therefore a won
race, not a working probe. Switching to /_admin/server/availability (the
unauthenticated liveness endpoint) removes the race entirely.
## 2. stress-test: one real step, two fakes, one impossible
Confirmed by matching ##[error] line numbers to ##[group] boundaries:
- "Concurrent operations stress test" globbed `./target/release/*`, which
matches build directories (build/, deps/, examples/, incremental/). It
printed "Permission denied" 100 times out of 100 and still reported
SUCCESS, because a bare `wait` returns 0 regardless of its jobs.
- "Memory pressure test" ran `ulimit -v 512000; cargo test --release`.
500MB of virtual address space cannot host rustc's LLD linker; it
aborts with "Resource temporarily unavailable" / "ld terminated with
signal 6". The same build scripts compiled fine in the immediately
preceding step, which set no ulimit — that in-log A/B is the proof.
This step failed on 6 of 6 recent runs. It could never pass.
- "Long-running scenario test" ran valgrind over the same directory glob
and ended in `|| true`, so it neither ran nor could fail. Removed
rather than left reporting success without testing anything.
Replaced with steps that resolve real bin targets from cargo metadata
(so they cannot rot when a binary is added or renamed), assert every
concurrent launch exits 0, and apply the memory cap to the BINARY rather
than to the compiler.
## Verified locally against the real binaries, not asserted
cargo metadata resolved: cicd-fixer cii-registrar forge-adapter hyper
all four --help -> exit 0
all four under a 1GiB VA cap -> exit 0
100 concurrent launches -> 0 failures
plus a YAML check that every service `options` folds to a value starting
with a flag and containing no '#', and that no arangodb service still
references /_api/version.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Correction: a third fault, and my "verified in CI" claim was wrongTwo things in this PR as originally written need retracting. 1. There is a THIRD fault, and it is the one that actually matteredThe body above lists two faults ( Measured against the real image, polling once per second for 25 seconds from container start:
So the probe never succeeded on any job, ever. 2. My earlier "verified healthy in CI" was a won race, not a working probeI previously reported this fix as confirmed because a CI run logged The runner polled Do not read FixSwitch all three arangodb services to Also in this push, a separate fault in the same workflow: Status: pushed, CI not yet green. I am not calling this verified until the run completes. |
`haskell-integration` ran `cabal build all` / `cabal test all` with
`defaults.run.working-directory: registry`, and cached on
`hashFiles('registry/hypatia.cabal')`.
`registry/` does not exist. It was deliberately deleted in f2fe2df
(2026-03-06), whose own commit message says:
Remove registry/ (30 files - Haskell, superseded by Elixir)
The CI job was left behind. It has failed EVERY run since: 11 failures
and 1 cancellation across the last 12 runs, never once green. The repo
now contains 0 .cabal files, no cabal.project, no stack.yaml, and one
.hs file — test/soundness/fixtures/code_safety/unsafe_coerce.hs — which
is a scanner test fixture, not a Haskell project.
Until this branch it was masked: the job died at container startup
because the arangodb health probe could never pass. With that fixed the
container comes up healthy and the real error surfaces:
##[error]An error occurred trying to start process '/usr/bin/bash'
with working directory '.../hypatia/registry'. No such file or directory
Removed the job and its two references in the `integration-status`
roll-up (`needs:` entry and the `needs.haskell-integration.result`
expression), which is what kept that gate red as well.
Each run had been installing GHC 9.6 + cabal 3.10 and standing up an
ArangoDB service container before failing at its first `run:` step, so
this also stops burning those runner minutes.
Verified: YAML parses; `haskell-integration` absent from jobs; zero
dangling `needs:` entries and zero dangling `needs.<job>.result`
expressions anywhere in the file; service-options and arango-probe
assertions still pass.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
…646) ## Context `mise.toml` printed roughly **25 warning lines on every shell entry** in this repo, while installing none of the tools it warned about. It is developer-ergonomics only — CI does not consume it — but it made every command in this repo, human or bot, arrive buried in noise. ## Three faults **1. Thirteen tool names that do not exist in the mise registry.** `denojs`, `pip`, `cargo`, `go-task`, `gofmt`, `isort`, `git`, `gnu-sed`, `gnu-grep`, `gnu-tar`, `vitest`, `pytest`, `jest`. Every one failed to resolve, so every one was pure noise. Removed, grouped by reason in the file itself: | reason | names | |---|---| | already provided by a listed tool | `cargo` (rust), `gofmt` (go), `pip` (python) | | project deps, not system tools | `vitest`, `jest`, `pytest`, `isort` | | base system binaries | `git`, `gnu-sed`, `gnu-grep`, `gnu-tar` | | simply misnamed | `denojs` → `deno` | `go-task` is **dropped, not renamed**. The bare name `task` does not work either: `mise registry` lists it as `aqua:go-task/task`, but `task@latest` still fails to resolve. That is a trap worth recording — *appearing in `mise registry` output is not sufficient to conclude a name resolves.* I initially "verified" all names against registry listing and it passed `task`; only running mise caught it. **2. `erlang` and `elixir` were absent entirely** — despite Elixir being the language of `lib/` and all 71 test files. Now pinned to **erlang 27.0 / elixir 1.17**, matching what `tests.yml` passes to `erlef/setup-beam`, so local `mix test` and CI run the same toolchain. **3. The `[alias]` block never did anything.** ```toml [alias] build = "cargo build --release || npm run build || go build" test = "cargo test || npm test || go test ./..." ``` mise's `[alias]` maps an alias to a **tool plugin** — it does not define tasks, so none of these ever ran (mise also warns it is deprecated in favour of `[tool_alias]`). Deliberately **not** re-added as `[tasks]`: the `Justfile` already defines `build`, `test`, `fmt`, `fmt-check`, `lint`, `doctor` and `heal` properly for the languages this repo actually uses. Two task runners sharing verb names — one of which silently falls through `||` chains into a different language's build — is worse than one. ## Verification Controlled comparison, one machine, one shell, both files read (the fixed copy in a worktree, the unfixed copy in the parent checkout): ``` warnings citing the FIXED file : 0 warnings citing the UNFIXED file : 14 ``` Every retained name re-checked with `mise ls-remote` — resolution, not registry listing. ## Expected remaining output ``` mise WARN missing: erlang@27.0 elixir@1.17 ``` until someone runs `mise install`. That warning is **correct and actionable** — it reports a genuine local/CI toolchain gap, unlike the previous unfixable typos. ## Acceptance criteria - [x] `mise.toml` parses as valid TOML - [x] Zero mise warnings attributable to this file - [x] Every tool name resolves via `mise ls-remote` - [x] No `[alias]` section - [ ] Reviewer confirms dropping `go-task` is acceptable (nothing in-repo invokes `task`) ## Model tier **Haiku/Sonnet** — a single config file, no code paths, fully mechanical once the registry facts are established. Recorded here so the pattern can be reused: several other estate repos carry the same copied `mise.toml`. ## Related - #640 — CI fixes in `tests.yml` (separate concern, separate branch) Co-authored-by: Claude Opus 4.8 <noreply@anthropic.com>
Final state of this branch — measured, run
|
| job | before | after |
|---|---|---|
stress-test |
failure (11 of last 12 runs) | ✅ success — twice consecutively |
Build Test Images |
success | ✅ success |
criterion + baseline gate |
success | ✅ success |
Aspect — Rule Module Coverage |
success | ✅ success |
E2E — Rust CLI Scan |
success | ✅ success |
Haskell Integration Tests |
failure (never once green) | removed — orphan, see 1dd31af |
E2E — Elixir Scanner Pipeline |
failure | ❌ still failing — real Elixir test failures |
Integration Tests |
failure (exit 127, no BEAM) | ❌ still failing — but now for an honest reason |
Integration Status |
failure | ❌ roll-up of the above |
ArangoDB now transitions properly in both remaining service jobs:
starting → healthy, rather than the instant pre-init "healthy" that a broken probe used
to produce by winning a race. Rust: 16 test suites ok, 0 failed.
The two remaining failures are not plumbing
Integration Tests previously died at exit 127 — the job installed the Rust toolchain
and then ran mix test with no BEAM on the runner at all. With erlef/setup-beam added it
now runs the suite and reports genuine failures. That is the intended outcome of this PR:
the CI was hiding the state of the test suite, and now it shows it.
Those failures are tracked separately and are not in scope here:
- test: 7 DETERMINISTIC Elixir failures (fail in isolation) — separate from the #643 order-dependent cluster #645 — 7 deterministic failures (identical count in isolation and in combination)
- test(isolation): 20 reflexive tests fail in the full suite but pass alone — Hypatia.Supervisor is torn down mid-run #643 — the ~66 order-dependent ones, plus a note that CI does not pin the ExUnit
seed, which makes the failure count non-comparable between runs (73 @ seed 20912 vs
79 @ seed 406044, with zero Elixir source changed in between)
What this PR contains
arangodbprobe →/_admin/server/availabilityon all three services (the previous
/_api/versionprobe 401s underARANGO_ROOT_PASSWORDand busybox wget cannot
authenticate, so it could never pass — see the correction comment above)erlef/setup-beamadded tointegration-tests(fixes exit 127)stress-testrewritten: two steps that could not fail and one that could not pass,
replaced with assertions verified locally against the real binarieshaskell-integrationremoved — itsregistry/project was deleted inf2fe2df
(2026-03-06, "superseded by Elixir") and the job has failed every run since
Ready for review. I have not marked it non-draft because the workflow still ends red, by
design — the red is now #645/#643, not this file.
…en grant still required) (#641) ## Context `verisimdb-data` is the estate fact-store — 302 repos of panic-attack scan output that the whole neurosymbolic pipeline (`PatternRegistry → TriangleRouter → FleetDispatcher`) reasons over. It **stopped updating on 2026-07-06** and was still stale on **2026-07-21 (15 days)**. Nothing alerted, because `estate-rescan.yml` was busy retrying the failure. ## Root cause The push step retried *any* push failure with backoff (2/4/8/16s), on the reasonable assumption of writer contention. But the actual failure was: ``` remote: Permission to hyperpolymath/verisimdb-data.git denied to hyperpolymath. fatal: unable to access '...' The requested URL returned error: 403 ``` `HYPATIA_DISPATCH_PAT` has **read** on `verisimdb-data` (the clone step at line 141 succeeds) but not **Contents:write**. A permission error can never clear by retrying — so the loop converted a hard, actionable auth failure into five rounds of flaky-looking noise, and the fact-store froze silently. This is a monitoring failure as much as a permissions one: *the retry hid the signal.* ## Changes `.github/workflows/estate-rescan.yml` — narrow the retry rather than remove it. Contention retry is still correct and is preserved; auth/permission now fails fast with a `::error` naming the exact missing grant. ## Verification Exercised with a stubbed `git` on `PATH` covering all four paths: | Scenario | Attempts | Exit | vs before | |---|---|---|---| | 403 permission denied | **1** | **78** | was 5 attempts, exit 128 | | Contention (`! [rejected]`) | 5 | 1 | unchanged | | Immediate success | 1 | 0 | unchanged | | Transient ×2 then success | 3 | 0 | unchanged — still self-heals | `shellcheck` clean; workflow YAML re-parsed (`jobs: ['rescan']`). ##⚠️ This makes the failure legible — it does not fix it **Action still required:** grant `HYPATIA_DISPATCH_PAT` `Contents: write` on `hyperpolymath/verisimdb-data`. Until that lands, this workflow will now fail *fast and clearly* on the first attempt instead of silently wasting 30s and going stale. After the grant, verify recovery with: ``` gh workflow run estate-rescan.yml -R hyperpolymath/hypatia gh api repos/hyperpolymath/verisimdb-data --jq .pushed_at # must advance past 2026-07-06 ``` ## Related Companion to #640 (arangodb health check). Both are Phase 0 foundation repair: the estate audit cannot be trusted while hypatia's own tests are red and its fact-store is frozen. 🤖 Generated with [Claude Code](https://claude.com/claude-code) --------- Co-authored-by: Claude Opus 4.8 <noreply@anthropic.com>
Context
tests.ymlwas red onmainwith 4 failing jobs. Two of them — Integration Tests andHaskell Integration Tests — fail with:
…immediately after ArangoDB itself logs
ArangoDB (version 3.11.14 [linux]) is ready for business.That paradox is the tell: the server is fine, the health probe is impossible.
Root cause — two independent faults, both measured
The probe was
curl -f http://localhost:8529/_api/version || exit 1.arangodb:3.11image)curlis not in the imagecurl MISSING,python3 MISSING,openssl MISSING;wget,nc,arangoshpresentlocalhost→ IPv6, Arango binds IPv4/etc/hostsmaps both127.0.0.1 localhostand::1 localhost; probinglocalhost→Connection refused, probing127.0.0.1→{"server":"arango","license":"community","version":"3.11.14"}Either fault alone is fatal, so fixing only one would not have worked — and indeed a
wget+localhostvariant was tried first and still went unhealthy after 10 retries.Changes
.github/workflows/tests.yml— all three arangodb service definitions(
integration-tests,haskell-integration-tests, and the third stack job):The
redis-cli pingprobes on the dragonfly services are correct and untouched.Verification — end-to-end against the real image, not by inspection
curl+localhost(old)wget+localhostConnection refused)wget+127.0.0.1(shipped)YAML re-parsed clean: 11 jobs, 3/3 probes replaced, 0
curl+localhostoccurrences left.Scope note
An estate-wide sweep found only 2 repos using curl-on-localhost health checks: this one
(proven broken) and
echidna/s4-loop.yml(ghcr.io/hyperpolymath/verisimdb-api:latest,unverified — worth checking whether that image ships curl). Every other service probe in
the estate correctly uses
pg_isready/redis-cli. This is therefore a small class, nottemplate rot.
Remaining red jobs (not addressed here — deliberately out of scope)
E2E — Elixir Scanner Pipeline—PASS=7 FAIL=1; the failing check ismix test(
tests/e2e.sh:118). Separate, real test failure.stress-test— not yet diagnosed.Draft until CI confirms the two integration jobs go green on a real runner.
🤖 Generated with Claude Code