Files
command-center/agent/managed-skills/new-service-deploy-gate-census/SKILL.md
T

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.sh MUST run from a checkout literally on main (branch-name gate) with a clean tree. A detached worktree pointed at the main ref fails (branch gate / dirty index). The primary checkout ~/Projects/serverAppConfig is the deploy host; switching it to main needs explicit user OK when workspace policy blocks checkout.
  • After merging, delete nothing by git add -A blind: a stray nested dir (e.g. a failed git worktree add stub) lands as an embedded-repo gitlink in the commit; git rm --cached <dir> && git commit --amend to 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.