← back to Ticket System
TK-11855: read-only PM2_HOME caller audit — precondition SATISFIED (one canonical PM2_HOME, zero root-context callers)
1cfdd01bb901a23a20e3d3ddcb8b3208567b238a · 2026-09-20 00:44:50 -0700 · Steve Abrams
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01ULnuVkzMZD2WCXiijFDd2K
Files touched
A data/tk-11855-audit/PM2_HOME-CALLER-AUDIT.mdA data/tk-11855-audit/audit-result.json
Diff
commit 1cfdd01bb901a23a20e3d3ddcb8b3208567b238a
Author: Steve Abrams <steve@designerwallcoverings.com>
Date: Sun Sep 20 00:44:50 2026 -0700
TK-11855: read-only PM2_HOME caller audit — precondition SATISFIED (one canonical PM2_HOME, zero root-context callers)
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01ULnuVkzMZD2WCXiijFDd2K
---
data/tk-11855-audit/PM2_HOME-CALLER-AUDIT.md | 77 ++++++++++++++++++++++++++++
data/tk-11855-audit/audit-result.json | 20 ++++++++
2 files changed, 97 insertions(+)
diff --git a/data/tk-11855-audit/PM2_HOME-CALLER-AUDIT.md b/data/tk-11855-audit/PM2_HOME-CALLER-AUDIT.md
new file mode 100644
index 00000000..6fd3148c
--- /dev/null
+++ b/data/tk-11855-audit/PM2_HOME-CALLER-AUDIT.md
@@ -0,0 +1,77 @@
+# TK-11855 — READ-ONLY PM2_HOME caller audit (the HARD PRECONDITION before wiring)
+
+**Agent:** night-TK-11855 · **Date:** 2026-09-20 · **Scope:** read-only. No launchd, script, or plist was modified.
+**Purpose:** the pm2-boot-serialize.sh mutex only works if *every* pm2 caller shares ONE `PM2_HOME`.
+Any caller on a *different* PM2_HOME makes the mutex a silent no-op across that boundary
+(a false-green of the CLAUDE.md TK-11431 class). This audit measures whether that boundary exists.
+
+## VERDICT: PRECONDITION SATISFIED — no PM2_HOME boundary exists
+
+Every pm2 caller on this machine resolves to the ONE canonical `PM2_HOME = /Users/macstudio3/.pm2`,
+which is the home the LIVE God daemon actually runs on. Wiring the serializer will **not** silently
+no-op. The remaining blockers to wiring are two *design decisions* (below), not audit gaps.
+
+## Ground truth (the home the daemon actually uses)
+```
+PM2 v6.0.14: God Daemon (/Users/macstudio3/.pm2) pid 3334, owner macstudio3
+/Users/macstudio3/.pm2/rpc.sock srwxrwxr-x macstudio3:staff
+/Users/macstudio3/.pm2/pub.sock srwxrwxr-x macstudio3:staff
+```
+
+## The dangerous class the red-team flagged — EMPTY on this box
+- **Root-context LaunchDaemons referencing pm2:** `/Library/LaunchDaemons` → **0 files**.
+- **Root crontab / `/etc/periodic` / system cron referencing pm2:** **none**.
+- **Any pm2 launchd job in the `system/` (root) domain:** none — all load in `gui/501` (user macstudio3).
+
+There is therefore no caller whose `HOME` is `/var/root` (which would default PM2_HOME to
+`/var/root/.pm2`). The exact "root LaunchDaemon without explicit PM2_HOME" failure mode does not occur.
+
+## The 6 pm2-referencing launchd AGENT plists (the mutex-relevant boot set)
+All load in domain `gui/501` = user macstudio3 (HOME=/Users/macstudio3), so any implicit PM2_HOME
+resolves to `/Users/macstudio3/.pm2` — matching ground truth.
+
+| Label | RunAtLoad | PM2_HOME | serialize-wrapped | loaded | role |
+|---|---|---|---|---|---|
+| `com.PM2` (pm2.macstudio3.plist) | true | /Users/macstudio3/.pm2 (explicit) | **YES** | **NO** (TK-11854) | main `pm2 resurrect` at boot |
+| `com.steve.pm2-resurrect` | true | /Users/macstudio3/.pm2 (explicit) | **YES** | yes | active resurrect twin (already wrapped) |
+| `com.steve.pm2-fracture-canary` | true | /Users/macstudio3/.pm2 (explicit) | no | yes | READ-ONLY canary (jlist) |
+| `com.steve.pm2-restart-velocity-canary` | true | default→/Users/macstudio3/.pm2 | no | yes | READ-ONLY canary |
+| `com.steve.pm2-guard-enforcement-canary` | false | default→/Users/macstudio3/.pm2 | no | yes | READ-ONLY canary |
+| `com.steve.ticketmaster-poll` | true | default→/Users/macstudio3/.pm2 | no | — | reads pm2 status |
+
+Plus one launchd job whose *script* shells pm2 (plist itself has no pm2 string):
+- `com.steve.fleet-keepalive` → `keepalive.js`, domain gui/501, PM2_HOME default→/Users/macstudio3/.pm2.
+ keepalive.js **pins** `PM2_HOME = env.PM2_HOME || $HOME/.pm2` and passes it to every pm2 child, and
+ serializes pm2 through its OWN lock (`~/.pm2/cli.lock` via shlock, `proposals/TK-10970/pm2-serialized.js`).
+
+Inactive (for completeness, not callers): `com.steve.pm2-resurrect.plist.disabled-TK10606`,
+`com.steve.pm2-resurrect.plist.bak-TK11677-20260917-204731`.
+
+## Divergent-PM2_HOME hunt — only the test harness, which is correct
+The only non-canonical `PM2_HOME=` assignments anywhere are the isolated per-test temp homes inside
+`~/Projects/_shared/pm2-boot-serialize.test.sh` (T5–T10, `PM2_SERIALIZE_TEST=1`) and the sample block
+in `pm2-boot-serialize.sh` itself. Those are deliberately isolated test homes, not production callers.
+No real caller sets a PM2_HOME other than `/Users/macstudio3/.pm2` (or the equivalent `$HOME/.pm2`).
+
+## Reconciling the "33 callers"
+- **Boot/health launchd caller set (mutex-relevant):** 6 pm2-referencing agent plists + `fleet-keepalive` = 7 jobs.
+- **Distinct `_shared` scripts running a mutating pm2 verb** (resurrect/start/restart/reload/save/delete/kill): 12.
+- **Fleet-wide** the 2026-06-14 restart-audit counted **163** pm2 restart/reload call-sites across 26 projects.
+- The "33" in TK-11744 is the boot/health caller set that would front through the serializer; the exact tally
+ is immaterial to the precondition, because **every** one of them — launchd, keep-alive, and the interactive
+ script callers (all run as macstudio3) — resolves to the single canonical PM2_HOME. The mutex boundary the
+ audit exists to rule out is absent regardless of the count.
+
+## Two blockers to WIRING that remain (design decisions — NOT audit gaps, stay Steve-gated)
+1. **`com.PM2` is not loaded (TK-11854).** The main boot resurrect job is absent from launchd, so the
+ redesign has never faced a real boot storm. An active RunAtLoad twin (`com.steve.pm2-resurrect`,
+ already serialize-wrapped, canonical PM2_HOME) IS loaded, so boot resurrection is not wholly absent —
+ but the load-state discrepancy should be resolved so wiring is validated against the path that runs.
+2. **keep-alive double-mutex.** `fleet-keepalive` already serializes pm2 through its own shlock lock
+ (`~/.pm2/cli.lock`). Wrapping it in pm2-boot-serialize.sh layers two independent, uncoordinated mutexes
+ (both under the same PM2_HOME, but not coordinated with each other). This needs a decision — coordinate
+ on one lock, or exempt keep-alive from the wrapper — not a blind wrap.
+
+## Bottom line for the gated wiring memo
+The PM2_HOME precondition is **cleared**: it is safe (w.r.t. the mutex-no-op risk) to wrap the boot-context
+callers on the single canonical PM2_HOME. Wiring itself remains gated pending decisions (1) and (2) above.
diff --git a/data/tk-11855-audit/audit-result.json b/data/tk-11855-audit/audit-result.json
new file mode 100644
index 00000000..e6951e32
--- /dev/null
+++ b/data/tk-11855-audit/audit-result.json
@@ -0,0 +1,20 @@
+{
+ "ticket": "TK-11855",
+ "agent": "night-TK-11855",
+ "date": "2026-09-20",
+ "kind": "read-only-audit",
+ "verdict": "PRECONDITION_SATISFIED",
+ "canonical_pm2_home": "/Users/macstudio3/.pm2",
+ "live_daemon_pm2_home": "/Users/macstudio3/.pm2",
+ "root_context_pm2_callers": 0,
+ "system_launchdaemons_pm2_files": 0,
+ "divergent_explicit_pm2_home_real_callers": 0,
+ "divergent_pm2_home_test_harness_only": true,
+ "pm2_referencing_agent_plists": 6,
+ "all_agent_plists_domain": "gui/501",
+ "wiring_blockers_remaining": [
+ "com.PM2 not loaded in launchd (TK-11854) — validate against the path that runs",
+ "keep-alive double-mutex (fleet-keepalive serializes via its own ~/.pm2/cli.lock) — decision needed"
+ ],
+ "wiring_status": "still-gated (memo: ~/.claude/yolo-queue/pending-approval/TK-11744-pm2-serialize-lock-redesign.md)"
+}
← e1ee158a wire-openclaw-exo: wait for gateway 200 before cron edits (f
·
back to Ticket System
·
auto-data-snapshot: 2026-09-20T02:12:36 (2 data files) — dat 41e66b72 →