v3.38.6 — Codex stdin fix, intelligence cache staleness fix, daemon config restore fix
- DualModeOrchestrator left every Codex worker's stdin open, causing codex exec to hang waiting for EOF; now closes stdin immediately after spawn for both platforms
- Editing a MEMORY.md section's body with the same ID and entry count served stale cached content; added storeFingerprint() helper to gate cache hits and persist changes based on content rather than entry count
- .claude-flow/config.json resourceThresholds overrides were silently lost to a stale daemon-state.json on restart; now checks per-field overrides against persisted state
- readDaemonConfigFromFile() logged diagnostic messages before this.config existed, causing logs to fail to reach daemon.log; fixed logging to occur after config initialization
- Added retry-with-backoff composite action for npm ci to address recurring sharp/libvips socket hang up flakes in CI workflows
Fixes
#2947 — DualModeOrchestrator left every Codex worker's stdin open, causing codex exec to hang waiting for EOF. Both Claude and Codex workers receive their prompt positionally/via a flag — never over stdin — but the pipe was never written to or closed. claude -p ignores the unused pipe, but codex exec blocks in resolve_root_prompt waiting for stdin EOF that never arrives, hanging every real Codex worker until the orchestrator's own multi-minute timeout kills it. Now closes stdin immediately after spawn for both platforms.
#2920 — Editing a MEMORY.md section's body (same ID, same entry count) served stale cached content instead of refreshing it. Two related bugs in the intelligence helper's graph/store caching, both keyed on entry count instead of content: init()'s graph-cache-hit gate compared only node count, so a same-ID content edit was invisible to it and ranked-context.json never refreshed; consolidate()'s persist gate only fired on insight creation or a dedup-driven shrink, so an in-memory content change (e.g. assigning an id to a previously id-less entry) was silently never written back to disk. Added a storeFingerprint() helper folding every entry's content into one aggregate value, used to gate the cache hit and to make consolidate() always persist.
#2935 — .claude-flow/config.json resourceThresholds overrides were silently lost to a stale daemon-state.json. The daemon's stale-state restoration guard only checked the constructor arg (originalConfig), not a value set via config.json — so an explicit override (e.g. minFreeMemoryPercent: 0, a workaround for Darwin's os.freemem() undercounting reclaimable memory) kept losing to whatever a daemon-state.json from before the override existed had persisted, on every restart. Same bug class as #2661 (aiWorkersEnabled), fixed the same way, per-field. Also fixed a secondary diagnostic bug: readDaemonConfigFromFile() logged "Daemon config loaded from ..." from inside the constructor before this.config existed, so the log call threw inside its own try/catch and never reached daemon.log — made "is the config file even being read?" impossible to answer from the log. Does not address this issue's primary Darwin os.freemem() memory-gate report — that needs vm_stat/memory_pressure-based measurement, left for a follow-up.
CI infrastructure
Fixed a recurring sharp/libvips "socket hang up" / 503 flake in npm ci across CI workflows (confirmed on 8+ unrelated PRs this session) by adding a retry-with-backoff composite action and wiring it into every npm ci call site, including the two OS-conditional fallback blocks initially missed.