3.6 KiB
3.6 KiB
name, description
| name | description |
|---|---|
| new-service-deploy-gate-census | When a newly added serverAppConfigs fleet service fails to deploy ("is not deployed on target" or "Unit svc.service not found"), run the full gate census; use pre-flight when adding any new service. |
Adding a deployable service to serverAppConfigs touches SIX independent gates. PR #69 (blackbox-exporter, 2026-10-21) shipped with three of them missing and failed three separate deploys before all were found. Run this census pre-flight (new service) or as a debug ladder (deploy failure).
The six gates, in failure order
| # | Gate | File | Failure signature |
|---|---|---|---|
| 1 | client allow-list valid_service() |
scripts/deploy.sh |
Service 'X' is not deployed on <target>. (client-side, before anything runs) |
| 2 | remote allow-list service_is_allowed() |
scripts/deploy-remote.sh |
same message from the remote runner |
| 3 | dispatch case in deploy_service() |
scripts/deploy-remote.sh |
falls to catch-all deploy_compose_service which HARD-FAILS on dankclaw (no compose since 2026-09-16): no quadlet handler |
| 4 | planner entry + SERVICE_ORDER |
scripts/deploy-plan.py |
service silently absent from auto-plans (explicit --service still reaches gates 1-3) |
| 5 | quadlet unit validity | <host>/.../quadlet/X.container |
Failed to restart X.service: Unit X.service not found — quadlet refuses to GENERATE a unit without Image=; also check every key the unit's comments claim (e.g. Network=host declared, not just argued) |
| 6 | health gate in the dispatch tail | deploy-remote.sh wait_for_health |
deploy runs but hangs/fails at health |
Pre-flight commands
# gates 1-3 in one shot (replace TARGET/SVC):
grep -c "$TARGET:$SVC" scripts/deploy.sh scripts/deploy-remote.sh # both allow-lists > 0
sed -n '/^deploy_service()/,/^}/p' scripts/deploy-remote.sh | grep "$SVC)" # dispatch case
python3 scripts/deploy-plan.py --target "$TARGET" --paths <the-service-paths> # gate 4
grep '^Image=' <host>/<svc>/quadlet/$SVC.container # gate 5 minimum
Test seams that pin the class (exist post-2026-10-21)
tests/deployment_flow.bats"the client and remote allowlists agree per target" — drift only; blind to a service missing from BOTH.tests/deployment_flow.bats"every gated service is dispatched" — gate 3 (red-proof: plant a gated, undispatched name in a sandbox copy).tests/observability_quadlet.bats"every observability unit declares an Image" — gate 5.
Deploy-execution traps (same tranche)
deploy.shMUST run from a checkout literally onmain(branch-name gate) with a clean tree. A detached worktree pointed at themainref fails (branch gate / dirty index). The primary checkout~/Projects/serverAppConfigis the deploy host; switching it tomainneeds explicit user OK when workspace policy blocks checkout.- After merging, delete nothing by
git add -Ablind: a stray nested dir (e.g. a failedgit worktree addstub) lands as an embedded-repo gitlink in the commit;git rm --cached <dir> && git commit --amendto excise, move debris to~/.AGENTTRASH/.
Gitea merge invocation (consumed probe turns twice)
POST /api/v1/repos/<o>/<r>/pulls/<n>/merge with JSON body {"Do":"merge"} (capital D) + Content-Type: application/json. Form-encoded Do=merge 422s [Do]: Required on this Gitea despite older lore. Credential: ~/.config/secrets/gitea holds http://user:PW@host%3aport (percent-encoded colon) — use Basic auth with user:PW; Authorization: token 401s. Never echo the password: build the header inside python from the file.