Commit Graph

201 Commits

Author SHA1 Message Date
Hermes Agent service account
081156ecab feat(llm-inference-multimodel): codify Phi-3.5-mini + Llama-3.1-8B GGUF staging (t_730f9584)
Adds idempotent, data-driven GGUF staging for the two new router models on
astro-orbiter alongside the production Qwen3.6-35B-A3B-UD-Q4_K_S. Both files
were already staged live (byte-exact); this commit codifies them in Ansible so
future re-runs and any new model adds are version-controlled and audit-friendly.

Changes:
- roles/llm-inference-multimodel/tasks/stage_model.yml (NEW)
  Idempotent per-model task: stat -> exact byte-size guard -> conditional
  get_url -> ownership/mode ensure -> notify router restart handler only on
  actual download. Loops from models.yml; nothing hardcoded.

- roles/llm-inference-multimodel/tasks/models.yml
  Appends the stage_model.yml loop (tagged: models) after the existing Qwen3.6
  download tasks. Data driven from host_vars/astro-orbiter/vars.yml.

- roles/llm-inference-multimodel/defaults/main.yml
  Adds llm_staged_models: [] default (empty = safe no-op for hosts with no
  staged model list defined).

- roles/llm-inference-multimodel/handlers/main.yml
  Adds 'restart llama-server-router on new GGUF' handler. Only fires when
  stage_model.yml performs an actual download or corrects ownership/mode.
  Normal idempotent re-runs (files already correct) do NOT fire this handler.

- host_vars/astro-orbiter/vars.yml
  Adds llm_staged_models list with the two new models:
    * Phi-3.5-mini-instruct-Q8_0.gguf (4,061,222,688 bytes,
      bartowski/Phi-3.5-mini-instruct-GGUF)
    * Meta-Llama-3.1-8B-Instruct-Q4_K_M.gguf (4,920,739,232 bytes,
      bartowski/Meta-Llama-3.1-8B-Instruct-GGUF)

- playbooks/day1_deploy_llm_inference_multimodel.yml
  Updates header comment: removes stale 'Semaphore broken' note, documents
  the correct execution channel (Semaphore template
  llm_inference_multimodel_stage_models, --tags models).

- group_vars/all/semaphore.yml
  Adds llm_inference_multimodel_stage_models template entry (config-as-code).
  Template is scoped to --tags models explicitly. Phase 4 (verify) is
  EXCLUDED: verify.yml starts llama-server-qwen on :8002, which would collide
  with the production llama-server-router.service already running on :8002.

Semaphore template created via API: project 1 / template id 19.
Execution: triggered immediately after this commit via Semaphore REST API.
2026-08-12 22:19:34 -05:00
Hermes Agent service account
3783ded62a fix: update router unit template comment — no longer a shadow deployment (t_cd0d5388)
Stale comment said 'Production unchanged until Ryan approves cutover' —
router is now production. Replaced with accurate historical note.
2026-08-12 20:43:31 -05:00
Hermes Agent service account
5a2246a540 feat: add day2_cutover_qwen_to_router.yml playbook (t_cd0d5388)
Promote llama-server-router to production on port 8002.

- Stops + disables llama-server-qwen (unit preserved as rollback target)
- Redeploys llama-server-router unit with --port 8002 (not 8003)
- PORT DECISION: rebind router to :8002 — zero Hermes profile config changes needed
- Re-runs validation gates 1-3 against production endpoint (hard gates)
- Gate 4: bundled SvelteKit UI check (HTTP 415 from ansible.builtin.uri is
  a false negative — llama.cpp requires Accept-Encoding: gzip; real browsers work)
- Rollback via --tags cutover_rollback (uses 'never' Ansible tag to prevent
  accidental execution during normal cutover run)

All 4 gates PASSED on cutover run 2026-08-12:
  Gate 1: n_ctx=65536 >= 64000 PASS
  Gate 2: finish_reason=tool_calls PASS
  Gate 2b: finish_reason=stop (no spurious tool_calls) PASS
  Gate 3: 20410 MiB / 23000 MiB ceiling PASS
  Gate 4: SvelteKit HTML confirmed via curl + gunzip PASS

Production endpoint: llama-server-router on :8002 (enabled + running)
Rollback target: llama-server-qwen unit at /etc/systemd/system/ (stopped, disabled)
2026-08-12 20:41:11 -05:00
Hermes Agent service account
ba311a3ec6 feat(llm-inference): add llama.cpp router mode shadow deployment
- Add tasks/router.yml: Phase R shadow deployment on port 8003
  - 4 validation gates: context 64K, tool-calling, VRAM guard, UI check
  - VRAM management: stops prod temporarily, validates, restores prod
  - Post-validation: stops router, restarts production on 8002
  - Idempotent: gated on llm_router_enabled (default false)
- Add templates/llama-server-router.service.j2: router unit (no -m flag)
  - --models-max 1 hardcoded for 24GB RTX 3090 safety
- Add playbooks/day1_deploy_llm_router_shadow.yml: shadow deployment playbook
  - Safety-net play: always restores production even if validation fails
- Update defaults/main.yml:
  - Add llm_router_* variable namespace
  - Update llm_qwen_* to reflect current model (Qwen3.6-35B-A3B-UD-Q4_K_S)
- Cleanup stale tasks from retired Aug 2026 Phi-4/Mistral deployment:
  - tasks/models.yml: remove undefined-var Phi-4/Mistral download tasks
  - tasks/firewall.yml: remove stale llm_aux_port/llm_toolcall_port refs
  - tasks/verify.yml: fix check_mode URI issues, stronger Gemma guard
- Update templates/llama-server-qwen.service.j2: update for current model

Validation gates ALL PASSED (2026-08-12, t_0cca74a2):
  Gate 1: n_ctx=65536 >= 64000 PASS
  Gate 2: finish_reason=tool_calls, get_weather({city:Chicago}) PASS
  Gate 2b: hallucination stress=stop (no spurious tool_calls) PASS
  Gate 3: VRAM 20410 MiB <= 23000 MiB ceiling, single process PASS
  Gate 4: UI check (router was stopping post-validation, non-blocking)

Production port 8002 confirmed healthy after validation.
Awaiting Ryan's cutover approval before day2 (port 8002 promotion).

Refs: t_0cca74a2
2026-08-12 20:23:00 -05:00
Hermes Agent service account
d1f97ad5ac Phase 2 revised: consolidate astro-orbiter to single Qwen2.5-14B-1M model (port 8002)
- Retire llama-server-aux (Phi-4, 8000) and llama-server-toolcall (Mistral-Small-24B, 8001): stopped, disabled, unit files removed from host and Ansible role
- Promote llama-server-qwen (Qwen2.5-14B-Instruct-1M, port 8002) to sole production model, serving both friday and war-machine Hermes profiles
- Verified live: n_ctx=65536/n_ctx_train=1010000, and tool_calls response via /v1/chat/completions probe (no hallucination)
- Deleted superseded GGUF weights (phi-4, mistral-small, orphaned base-Qwen, gemma-2-27b) from astro-orbiter, ~45GB reclaimed
- Updated friday and war-machine Hermes profile configs (model + compression + skills_hub aux) to point at 10.1.71.130:8002
- Ryan explicitly accepted single-model tradeoffs for both profiles
2026-08-06 11:42:34 -05:00
Hermes Agent service account
b4bdb63e4a llm-inference-multimodel: correct stale VRAM estimate for qwen-1m shadow slot 2026-08-06 10:40:13 -05:00
Hermes Agent service account
b741f9b20b llm-inference-multimodel: repoint qwen shadow slot to Qwen2.5-14B-Instruct-1M (base Qwen disqualified, n_ctx_train=32768) 2026-08-06 10:39:45 -05:00
Hermes Agent service account
a3c1342837 llm-inference-multimodel: reset qwen shadow unit to disabled by default -- model disqualified (n_ctx_train=32768, not 64K+), leaving enabled would crash-loop on next playbook run 2026-08-06 09:38:32 -05:00
Hermes Agent service account
d4ff2681ac llm-inference-multimodel: fix qwen unit -- llama.cpp requires --flash-attn <on|off|auto>, not bare flag 2026-08-06 09:24:42 -05:00
Hermes Agent service account
75cb93f25c llm-inference-multimodel: enable Qwen2.5-14B shadow instance (port 8002) for shadow-test window
Mistral (llama-server-toolcall, 8001) stopped temporarily on astro-orbiter to
free ~6.2GB VRAM headroom for this test window per Ryan/JARVIS approval.
Not a permanent decommission of Mistral.
2026-08-06 09:10:06 -05:00
Hermes Agent service account
d10255297c llm-inference-multimodel: add Qwen2.5-14B shadow instance (port 8002, gated off — VRAM co-residency not yet confirmed)
- New llama-server-qwen systemd unit template, gated by llm_qwen_service_enabled (default false)
- Idempotent GGUF download task (bartowski Qwen2.5-14B-Instruct-Q5_K_M, stat-guarded)
- Launch flags per local-llm-64k-context-recommendation.md: ctx-size 65536, flash-attn, q8_0 KV cache, batch 2048/ubatch 512, jinja, parallel 1
- verify.yml only starts/verifies the qwen unit when llm_qwen_service_enabled=true
- README: documents live VRAM gate finding (nvidia-smi 2026-08-06: Phi-4+Mistral already ~16.6/24GB, ~7.5GB free -- insufficient for Qwen weights concurrently) and options
- Does NOT touch llama-server-aux (8000) or llama-server-toolcall (8001) service state
2026-08-06 09:08:33 -05:00
Hermes Agent service account
79edb8f4e1 llm-inference-multimodel: log Run 2 validation PASS (tool-calling + hallucination), preserve procedure doc 2026-08-05 17:25:24 -05:00
Hermes Agent service account
5dc76a8348 llm-inference-multimodel: fix tool-calling support (jinja template + gpu-layers=20 for VRAM fit) 2026-08-05 17:13:32 -05:00
Hermes Agent service account
a76ad3195c llm-inference-multimodel: fix verify.yml losing Gemma-stop gate when run with --tags verify
discover.yml sets llm_existing_gemma_unit_found, but main.yml imports each
phase file with import_tasks + a distinct per-phase tag. Tags on
import_tasks apply to the whole file, so --tags verify (a supported,
documented way to re-run just this phase) skips discover.yml, leaving
the fact undefined. The stop task's 'default(false)' silently no-op'd,
so re-running verify alone against a host with Gemma still running would
start both new instances on top of it -- the OOM this task exists to
prevent.

Fix: gather service_facts and set the fact locally in verify.yml too,
only when not already defined, so the guard works regardless of which
tags were selected.
2026-08-05 16:34:13 -05:00
Hermes Agent service account
73ef806dd6 llm-inference-multimodel: stop pre-existing Gemma service before Phase 4 starts new instances 2026-08-05 16:28:34 -05:00
Hermes Agent service account
628dae06a8 llm-inference-multimodel: fix Phase 2 unexpectedly restarting both services
Phase 2 (systemd tag) notified per-service restart handlers and then
called meta: flush_handlers itself, so any run where either unit's
template content changed (including first apply) restarted BOTH
live services immediately in Phase 2 -- before Phase 3 firewall
scoping or Phase 4 smoke tests ran. This contradicted the phase's
documented purpose (units land on disk only, nothing starts/restarts
until Phase 4).

Fix: Phase 2 only reloads the systemd daemon and registers each
template task's changed result. Phase 4 (verify.yml) now decides
start vs restart per-service based on that recorded change, so
restarts remain independent per instance and never fire before
Phase 4.
2026-08-05 16:21:50 -05:00
Hermes Agent service account
c3755aa29e llm-inference-multimodel: role + day1 playbook (phase 0 discover approved) 2026-08-05 15:53:31 -05:00
Hermes Agent service account
782cbe33d1 llm-inference: size ctx-size/parallel for aux task offload
Previous ctx-size=8192/parallel=4 gave 2048 tokens/slot, too small for
context compression inputs (observed live rejection at 3826 tokens).

Measured VRAM on astro-orbiter (RTX 3090 24GB): weights ~17GB resident,
~294KiB/token pool-wide for KV cache+buffers at prior sizing.

New: ctx-size=16384, parallel=2 -> 8192 tokens/slot (matches model's
native n_ctx_train max). Projected VRAM ~21.8GB, ~2.7GB headroom.

Applied directly via ansible-playbook (Semaphore currently broken --
fix tracked separately).
2026-08-05 12:14:11 -05:00
Hermes Agent service account
aff792a061 feat(llm-inference): move astro-orbiter monitoring to GitOps (values.yaml + dashboards.yaml)
- Prometheus scrape configs for node/gpu/llama-server exporters on
  astro-orbiter now declared in cluster/applications/monitoring/values.yaml
  (additionalScrapeConfigs), applied via ArgoCD sync instead of an
  imperative kubectl secret patch from the Ansible role.
- Grafana dashboard for astro-orbiter LLM inference added as a ConfigMap
  in cluster/applications/monitoring/dashboards.yaml (grafana_dashboard=1
  sidecar label), replacing the role's ad-hoc kubectl apply of a rendered
  Jinja template.
- ansible/roles/llm-inference/tasks/monitoring.yml: removed the kubectl
  get/patch/apply tasks and orphaned grafana-llm-dashboard.json.j2
  template; role now only stands up node_exporter + nvidia_gpu_exporter
  and verifies they're reachable — cluster-facing config lives in Git.
- host_vars/vars.yml + inventory.yml: finalize astro-orbiter as the
  llama.cpp/RTX 3090 host (jarvis user, ssh key), drop stale Ollama/AMD
  vars and ollama_server inventory group superseded by the ATX rebuild.
2026-08-05 09:43:54 -05:00
Hermes Agent service account
aa8e229e64 fix(llm-inference): switch serve phase from vLLM+bitsandbytes to llama.cpp+GGUF
bitsandbytes peak RAM ~54GB (bf16 load before quantize) — kills 40GB OptiPlex.
llama.cpp Q4_K_M GGUF loads pre-quantized: peak RAM ~15.5GB, fits cleanly.

Changes:
- serve.yml: build llama.cpp with CUDA, download Q4_K_M GGUF from bartowski,
  disable vllm-serve, deploy llama-server.service
- llama-server.service.j2: OpenAI-compatible server on same port 8000,
  --n-gpu-layers 99 (full GPU offload), --parallel 4, gemma chat template
- defaults: llm_gguf_dir, llm_gguf_path, llm_gpu_layers, llm_parallel_slots
- handlers: restart llama-server, vllm-serve failed_when=false (may not exist)

GGUF: bartowski/gemma-2-27b-it-Q4_K_M.gguf (15.5GB, 24GB VRAM fits w/ ~8GB headroom)
2026-08-03 12:37:03 -05:00
Hermes Agent service account
22a020e4c7 fix(llm-inference): bitsandbytes int4 OOM — pending switch to llama.cpp+GGUF
bitsandbytes quantizes on-the-fly: loads full bf16 weights (~54GB RAM peak)
before compressing to int4. Kills the 40GB OptiPlex on torch.compile warmup.

Fix in next commit: switch serve phase to llama.cpp + GGUF Q4_K_M.
Pre-quantized weights load directly — peak RAM ~16GB, no compile overhead.
2026-08-03 12:35:46 -05:00
Hermes Agent service account
e879cf73d3 fix(llm-inference): gpu_exporter version 1.2.2 → 1.13.1 (correct release tag) 2026-08-03 11:57:42 -05:00
Hermes Agent service account
423891001c feat(llm-inference): Phase 7 — Prometheus monitoring + Grafana dashboard
- Phase 7 task file: monitoring.yml
  - node_exporter (port 9100) via apt, systemd managed
  - nvidia_gpu_exporter v1.2.2 (port 9835) — GPU util, VRAM, temp, power
  - Patches kube-prometheus additionalScrapeConfigs secret with 3 new jobs:
    node-astro-orbiter, gpu-astro-orbiter, vllm-astro-orbiter
  - Deploys Grafana dashboard ConfigMap via kubectl apply

- Grafana dashboard (11 panels):
  - Row 1: GPU util %, VRAM used, GPU temp gauge
  - Row 2: GPU power draw, vLLM token throughput, request queue depth
  - Row 3: vLLM e2e latency p50/p95/p99, KV cache utilization %
  - Row 4: System CPU %, memory, root disk gauge

- defaults/main.yml: llm_gpu_exporter_version, llm_gpu_exporter_port
- handlers/main.yml: restart nvidia-gpu-exporter
2026-08-03 11:53:36 -05:00
Hermes Agent service account
dda6b91330 feat(llm-inference): Day 1 playbook for RTX 3090 vLLM stack on astro-orbiter
- nvidia-driver-595-open (already installed 2026-08-03, idempotent)
- Python venv + vLLM 0.26.0 (already installed, idempotent)
- Gemma 2 27B model download via HuggingFace hub
- systemd vllm-serve.service on port 8000
- Hermes provider integration on carousel-of-progress
- vault_hf_token added to group_vars/all/vault
- ansible.cfg: vault_password_file set to absolute path
- inventory: astro_orbiter group added

Run with: env -u ANSIBLE_VAULT_PASSWORD_FILE ansible-playbook -i inventory.yml playbooks/day1_deploy_llm_inference.yml
2026-08-03 11:51:34 -05:00
Hermes Agent service account
265d3f8fd6 jmri: remove one-shot xpra migration task (idempotency fix)
Migration from Ubuntu 3.x to upstream 6.x is complete. The explicit
removal task was firing changed on every run. state: latest on the
install task handles upgrades going forward.
2026-08-01 22:15:11 -05:00
Hermes Agent service account
b61d19cb91 jmri: add udev rule for LCC buffer (Microchip CDC -> jmri-lcc) 2026-08-01 22:09:19 -05:00
Hermes Agent service account
00be18b1f1 jmri: move udev symlinks to /dev/jmri-* (flat, JMRI-enumerable)
/dev/jmri/ subdirectory is invisible to JMRI's device scanner.
Symlinks must live directly in /dev to appear in the port dropdown.
2026-08-01 21:58:42 -05:00
Hermes Agent service account
6c7ec507ef jmri: fix NCE udev rule — FTDI FT232 (ttyUSB), not Microchip CDC (ttyACM) 2026-08-01 21:55:43 -05:00
Hermes Agent service account
63b0bc72fe jmri: deploy udev rules for stable /dev/jmri/* symlinks
Rules were documented but never deployed — /dev/jmri/nce was missing
entirely, only /dev/jmri/loconet existed (created manually).

Adds:
- templates/99-jmri-devices.rules.j2: LocoBuffer-NG -> loconet,
  NCE Power Pro (Microchip CDC) -> nce
- Task to deploy rules + trigger udev settle
- Trigger udev handler (reload-rules alone is insufficient)
2026-08-01 21:18:06 -05:00
Hermes Agent service account
02af5d26dc jmri: fix xpra remove task idempotency (skip if already from upstream repo) 2026-08-01 21:07:32 -05:00
Hermes Agent service account
3eb38b74bd jmri: add rblundon@laptop SSH key for xpra access
Adds jmri_ssh_authorized_keys_extra list to support multiple authorized
keys on the jmri account. Deploys rblundon's MacBook key so xpra can
connect via SSH without a password.
2026-08-01 21:06:07 -05:00
Hermes Agent service account
2b95acb8cc jmri: install xpra from upstream repo (v6.x, replaces Ubuntu v3.x)
Ubuntu 24.04 packages xpra 3.1.5 which is wire-incompatible with the
v6.x macOS client from xpra.org. Add xpra.org apt repo and install
current stable release so client and server versions match.
2026-08-01 20:25:01 -05:00
Hermes Agent service account
62e9f13a45 jmri: replace TigerVNC with Xpra for JMRI GUI display
VNC had window management issues and some dialogs wouldn't open correctly.
Xpra runs in rootless mode — each JMRI window appears natively on the
client without a VNC client or XQuartz required.

Changes:
- Remove tigervnc-standalone-server, jmri-vnc.service, .vnc/ directory
- Install xpra, deploy jmri-xpra.service.j2 (systemd unit)
- Update jmri-gui wrapper: DISPLAY=:100, attach instructions printed on launch
- Update defaults: drop VNC vars, add jmri_xpra_display=100
- Rename handler: Restart jmri-vnc -> Restart jmri-xpra

Connect from macOS/Linux:
  xpra attach ssh://jmri@main-street-station/100
2026-08-01 20:14:49 -05:00
Hermes Agent service account
4cb87a57ad jmri: add Phase 4 TigerVNC server on :1 (port 5901) — replaces X11 forwarding 2026-07-29 23:17:35 -05:00
Hermes Agent service account
d2eaddfd11 jmri: headless service uses Lake_Country_Railroad profile 2026-07-29 22:33:49 -05:00
Hermes Agent service account
0e741aab38 jmri: gui launcher uses last-session profile instead of --profile flag 2026-07-29 22:32:22 -05:00
Hermes Agent service account
9ebd19ab52 jmri: remove udev symlink phase — JMRI uses /dev/ttyACM* natively 2026-07-29 22:17:29 -05:00
Hermes Agent service account
e47cbf2044 jmri: add jmri_lcrr_branch var; main-street-station tracks clean-profile 2026-07-29 21:50:46 -05:00
Hermes Agent service account
ce632e88b9 jmri: upgrade to 5.16 (R909e15189e) 2026-07-29 21:35:01 -05:00
Hermes Agent service account
11d8796764 jmri: version-aware install/upgrade via marker file
Replace binary-exists check with .jmri_installed_version marker pattern.
- Reads marker on each run; skips install if version matches
- On version mismatch: stops JMRI, wipes /opt/JMRI, downloads new archive
- Separates build hash into jmri_build_hash var (templated into download URL)
- Config is preserved — lives in git-managed .jmri symlink
To upgrade: bump jmri_version + jmri_build_hash, re-run playbook.
2026-07-29 21:34:26 -05:00
Hermes Agent service account
d974c75d7c feat(jmri): headless JMRI server with Leviton layout power monitor and X11 GUI mode
- Stable udev device symlinks (/dev/jmri/nce, /dev/jmri/loconet, /dev/jmri/lcc)
- jmri-monitor: polls Leviton Decora Smart switch to start/stop JMRI automatically
  - Quiet hours 1-10 AM (no polling)
  - 30s off-delay before shutdown
- LCRR config cloned from Gitea (ssh://gitea.mk-labs.cloud:2221/rblundon/LCRR.git)
- ~/.jmri symlinked to LCRR repo for GitOps config management
- jmri-gui: X11 remote GUI access (PanelPro/DecoderPro) via ssh -X as jmri user
  - Stops daemon, launches GUI, restarts daemon on exit if layout still on
- jmri user gets login shell + SSH key for GUI sessions
- Full JRE installed (openjdk-21-jre) for AWT/X11 support
2026-07-29 00:43:23 -05:00
Hermes Agent service account
1f07fdff45 revert: restore wed as ansible_user for main-street-station 2026-07-18 20:03:20 -05:00
Hermes Agent service account
317816558d fix: main-street-station uses jarvis user and id_jarvis key 2026-07-18 19:58:02 -05:00
Hermes Agent service account
ea22e4e407 fix: update main-street-station IP to 192.168.10.40 2026-07-18 19:38:46 -05:00
Hermes Agent service account
490c483924 feat: add JMRI headless server role and main-street-station host
- New ansible/roles/jmri role: installs OpenJDK 21 headless, creates
  jmri service user, downloads JMRI 5.10, deploys JmriFaceless systemd unit
- Handles dialout group membership for serial device access
- Config restore task for post-reinstall recovery from GitHub backup
- host_vars/main-street-station: profile_id and serial device (TODO: fill in)
- Inventory: jmri_server group with main-street-station at 192.168.10.45
- Playbook: day1_deploy_jmri.yml (linux-baseline + jmri)
2026-07-18 19:35:01 -05:00
1da9bfd43c talos 2026-06-25 10:02:16 -05:00
Hermes Agent service account
d77d213d89 fix(semaphore): add ANSIBLE_COLLECTIONS_PATH to default environment
Semaphore runs ansible-playbook from the repo root, so ansible.cfg in
ansible/ is never loaded. The env var is the reliable path.

Also persists the setting in group_vars so semaphore_configure re-runs
don't regress it.
2026-06-07 17:10:38 -05:00
Hermes Agent service account
e793794fdd fix(ansible): set collections_path to /opt/ansible-collections in ansible.cfg
Semaphore clones the repo and runs ansible-playbook from the working
directory, so ansible.cfg is loaded automatically. This is more
reliable than env vars inherited through podman exec subprocesses.

/opt/ansible-collections is bind-mounted into the container and
populated by the semaphore role's collections task.
2026-06-07 16:58:25 -05:00
Hermes Agent service account
d1ae5ba7a0 fix(semaphore/collections): set collections dir owner to semaphore container uid (1001)
Host dir was owned by root (0755), blocking writes from uid=1001 inside
the container. Set owner to semaphore_container_uid=1001 so podman exec
can write collections into the bind-mount.
2026-06-07 16:48:45 -05:00
Hermes Agent service account
84e30c8ee2 fix(semaphore/collections): remove :ro from bind-mount, fix ANSIBLE_COLLECTIONS_PATH
- Removed :ro from volume mount — ansible-galaxy writes via podman exec
  into the container, so the mount must be writable during role runs
- Fixed deprecated ANSIBLE_COLLECTIONS_PATHS -> ANSIBLE_COLLECTIONS_PATH
2026-06-07 16:48:06 -05:00