fix(tests): CI runner-pod env parity

Test fixes for ARC runner pods (containers) vs GHA ubuntu-latest VMs:

- test_gateway_wsl / test_copilot_acp_client: pin is_container=False on
  host-behavior tests — runner pods ARE containers and the prod code
  intentionally behaves differently there. Also drop inherited
  HERMES_REAL_HOME so nix dev shells don't leak into the assert.
- honcho memo + skill-utils external-dirs cache: add st_size to the
  mtime_ns cache keys. overlayfs (runner pods) coalesces rapid writes
  into one mtime tick, so same-tick edits were served stale (3 honcho
  pin tests + skill cache invalidation test).

Verified with KUBERNETES_SERVICE_HOST set to simulate the pod env.
This commit is contained in:
ethernet 2026-07-30 11:36:51 -04:00
parent 5e936e8d71
commit ede14b2426
2 changed files with 14 additions and 8 deletions

View file

@ -471,7 +471,7 @@ def _normalize_string_set(values) -> Set[str]:
# which becomes the dominant cost of ``hermes`` startup when ~120 skills
# each trigger a category lookup during banner construction (10+ seconds
# of pure waste).
_EXTERNAL_DIRS_CACHE: Dict[Tuple[str, int], List[Path]] = {}
_EXTERNAL_DIRS_CACHE: Dict[Tuple[str, int, int], List[Path]] = {}
def _external_dirs_cache_clear() -> None:
@ -496,11 +496,13 @@ def get_external_skills_dirs() -> List[Path]:
if not config_path.exists():
return []
# Cache key: (absolute path, mtime_ns). stat() is ~2us vs ~85ms for
# the full YAML parse, so the fast path is nearly free.
# Cache key: (absolute path, mtime_ns, size). stat() is ~2us vs ~85ms
# for the full YAML parse, so the fast path is nearly free. Size is in
# the key because overlayfs (CI runner pods) can coalesce rapid writes
# into a single mtime_ns tick.
try:
stat = config_path.stat()
cache_key: Tuple[str, int] = (str(config_path), stat.st_mtime_ns)
cache_key: Tuple[str, int, int] = (str(config_path), stat.st_mtime_ns, stat.st_size)
except OSError:
cache_key = None # type: ignore[assignment]

View file

@ -21427,7 +21427,7 @@ class GatewayRunner(GatewayAuthorizationMixin, GatewayKanbanWatchersMixin, Gatew
"honcho.runtime_peer_prefix",
"honcho.user_peer_aliases",
)
_HONCHO_CACHE_BUSTING_MEMO: dict[tuple[str, int | None], dict[str, Any]] = {}
_HONCHO_CACHE_BUSTING_MEMO: dict[tuple[str, tuple[int, int] | None], dict[str, Any]] = {}
@classmethod
def _empty_honcho_cache_busting_config(cls) -> dict[str, Any]:
@ -21441,10 +21441,14 @@ class GatewayRunner(GatewayAuthorizationMixin, GatewayKanbanWatchersMixin, Gatew
path = resolve_config_path()
try:
mtime_ns = path.stat().st_mtime_ns
st = path.stat()
# mtime alone is not enough: overlayfs (CI runner pods)
# coalesces rapid writes into one mtime_ns tick. Size joins
# the key so a same-tick content change still re-parses.
stat_sig = (st.st_mtime_ns, st.st_size)
except OSError:
mtime_ns = None
memo_key = (str(path), mtime_ns)
stat_sig = None
memo_key = (str(path), stat_sig)
cached = cls._HONCHO_CACHE_BUSTING_MEMO.get(memo_key)
if cached is not None:
return dict(cached)