hermes-agent/tests/docker
Ben ad5fdab092 feat(service_manager): add S6ServiceManager for runtime gateway supervision
Phase 3 of the s6-overlay supervision plan. Implements the runtime-
registration surface from D4 — only the s6 backend supports
register_profile_gateway / unregister_profile_gateway /
list_profile_gateways; host backends continue to raise
NotImplementedError. No caller yet (Phase 4 wires in the profile
create/delete hooks).

Key implementation notes:

  - Service directory shape: /run/service/gateway-<profile>/{type,run,log/run}.
    Atomic register: write to gateway-<profile>.tmp, fsync via
    os.rename. Cleanup on rescan failure.

  - Run script uses #!/command/with-contenv sh so HERMES_HOME and any
    extra_env arrive at exec time. The hermes -p <profile> gateway
    start --foreground --port <port> command is wrapped in
    s6-setuidgid hermes for the per-service privilege drop (OQ2-A).

  - Log script (OQ8-C): persists via s6-log to
    ${HERMES_HOME}/logs/gateways/<profile>/. CRITICAL — HERMES_HOME is
    a runtime env-var expansion in the rendered script, NOT a Python
    f-string substitution. Negative-asserted in
    test_s6_register_creates_service_dir_and_triggers_scan so
    regressions are caught.

  - PATH gotcha: /command/ is only on PATH for processes spawned by
    the supervision tree (services, cont-init.d). `docker exec` and
    profile-create hooks don't get it. S6ServiceManager calls all
    s6-* binaries via absolute path through the new _S6_BIN_DIR
    constant so callers don't have to fix up env vars.

  - validate_profile_name rejects path-traversal, leading-dash (s6
    would parse as a flag), uppercase, whitespace, and names >251
    chars (s6-svscan default name_max).

Test coverage:
  - 13 new unit tests in tests/hermes_cli/test_service_manager.py
    (kind detection, run-script content, env quoting, register
    rollback on rescan failure, unregister idempotence, list filter,
    lifecycle dispatch, svstat parsing). Total: 36 passing.
  - 2 new in-container integration tests in
    tests/docker/test_s6_profile_gateway_integration.py validating
    end-to-end registration against a real s6 supervision tree.

Docker harness: 14 passed, 2 xfailed (Phase 4 target unchanged).

Refs: docs/plans/2026-05-07-s6-overlay-dynamic-subagent-gateways.md
2026-05-22 11:47:41 +10:00
..
__init__.py test(docker): add conftest fixtures for docker harness 2026-05-22 11:46:52 +10:00
conftest.py test(docker): apply 180s timeout to docker harness tests 2026-05-22 11:46:52 +10:00
test_dashboard.py feat(docker)!: replace tini with s6-overlay as PID 1 2026-05-22 11:47:41 +10:00
test_main_invocation.py test(docker): lock baseline behavior for Phase 0 harness 2026-05-22 11:46:52 +10:00
test_profile_gateway.py test(docker): lock baseline behavior for Phase 0 harness 2026-05-22 11:46:52 +10:00
test_s6_profile_gateway_integration.py feat(service_manager): add S6ServiceManager for runtime gateway supervision 2026-05-22 11:47:41 +10:00
test_tui_passthrough.py test(docker): lock baseline behavior for Phase 0 harness 2026-05-22 11:46:52 +10:00
test_zombie_reaping.py test(docker): lock baseline behavior for Phase 0 harness 2026-05-22 11:46:52 +10:00