Skip to content

feat(session-lock): identify Codex sessions by a root-bound thread-writer flock - #3469

Open
choppakatlakoushik001 wants to merge 6 commits into
kunchenguid:mainfrom
choppakatlakoushik001:fm/codex-lock-r4
Open

feat(session-lock): identify Codex sessions by a root-bound thread-writer flock#3469
choppakatlakoushik001 wants to merge 6 commits into
kunchenguid:mainfrom
choppakatlakoushik001:fm/codex-lock-r4

Conversation

@choppakatlakoushik001

Copy link
Copy Markdown

Intent

Ship only the core Codex session-lock identity capability on the updated firstmate baseline. Codex sandboxing can hide harness ancestry, so a confirmed-held Codex thread-writer flock is positive live-session evidence and must supply the home-lock identity. Bind both the verified thread id and the resolved Codex state root into a strict, unambiguous persisted identity; liveness and ownership of a recorded identity must use its bound root rather than re-resolving the checking process environment, and the same thread id under different roots must not collide. The settled three-state semantics are required: a missing or free writer lock is stale state 1, genuinely unevaluable state is uncertain state 2, and uncertainty remains live for contention so it cannot authorize lock theft. A confirmed-held Codex writer lock outranks inherited Claude, Pi, and Grok markers; uncertain Codex state does not win and falls back to the existing marker or ancestry path, with behavioral tests. Preserve the numeric non-Codex ancestry identity and liveness behavior. Keep direct consumers and dedicated behavioral tests needed for this core contract. This run explicitly defers rejecting relative Codex roots, macOS or no-flock portability work for the writer-lock test, the lease comment correction, and fm-lock identity-label deduplication; do not add those follow-ups here. The updated baseline has 15 documented pre-existing failing test scripts outside this core change; report but do not repair them. Keep park/codex-session-lock untouched, keep .no-mistakes untracked, keep bin scripts shellcheck-clean, add no agent co-author, ship a PR through no-mistakes, and never merge it.

What Changed

  • Added Codex identity helpers to bin/fm-session-lock-lib.sh: resolve the Codex state root (CODEX_HOME, else $HOME/.codex), build/parse a strict codex:<thread-id>:<state-root> lock record, and classify the thread-writer-locks/<thread-id>.lock flock into three states — held → live (stale-state 1 when absent/free, uncertain-state 2 when unevaluable, with uncertainty treated as live during contention). New fm_session_identity, fm_session_identity_alive, fm_session_identity_valid, and the codex:* branch of fm_session_lock_owned_by_self prefer a confirmed-held Codex writer lock over inherited Claude/Pi/Grok markers, bind liveness/ownership checks to the record's own root instead of re-resolving the current environment, and fall back to the unchanged numeric harness-ancestry path when Codex state is uncertain.
  • bin/fm-harness.sh now detects a Codex session from CODEX_THREAD_ID/CODEX_SESSION_ID plus a held writer flock; bin/fm-spawn.sh strips CODEX_THREAD_ID/CODEX_SESSION_ID from spawned child harnesses and the cursor launch template so a child cannot publish the parent's still-live identity.
  • Routed fm-lock.sh, fm-session-start.sh, fm-sessionstart-nudge.sh, fm-sessionstart-run.sh, fm-bootstrap.sh, fm-startup-network.sh, and fm-trace-context-lib.sh through the shared session-identity helpers with codex:*-aware status/error wording; tightened fm-lease.sh lock parsing to trim whitespace then require numeric. Updated architecture/scripts/nudge/verification docs and the Codex harness reference, and extended behavioral tests (fm-session-lock-ancestry, fm-session-start, fm-sessionstart-nudge, fm-spawn-dispatch-profile, fm-startup-network, fm-kimi-harness, fm-branch-supervision, fm-claude-stop-autoarm).

Risk Assessment

✅ Low: Well-bounded, strictly additive Codex identity path gated on a flock proof with fail-closed uncertain handling; non-Codex path unchanged; consumers treat the token as opaque validated string; cross-root behavior is test-covered.

Testing

Baseline pipeline commands plus this phase: I ran the smallest relevant automated set — the dedicated fm-session-lock-ancestry suite and every modified session-identity consumer suite (claude-stop-autoarm, branch-supervision, kimi-harness, startup-network, spawn-dispatch-profile, trace-context-lib) — and all core-contract assertions pass, including the two new Codex tests. Automated pass alone is not sufficient, so I also drove the real bin/fm-lock.sh, bin/fm-harness.sh and bin/fm-session-lock-lib.sh end-to-end under a genuinely held flock and captured a CLI transcript showing a Codex session acquiring the home lock with hidden ancestry, the persisted codex:<thread>:<root> identity, live/stale status wording, codex harness detection, cross-root non-collision and lock-theft refusal, root-bound liveness from a foreign environment, and the unchanged non-Codex numeric identity. Two suites (fm-sessionstart-nudge, fm-session-start) each have one failing assertion; I traced both to host-environment causes (Node 22.22.1 cannot dynamically import a .ts file; a real /usr/bin/node defeats a fixture that deletes node from a fake PATH), confirmed both test functions are byte-identical at the base commit and touch no Codex session-lock code, so they are pre-existing baseline failures the intent says to report but not repair. This is a shell/CLI change with no rendered UI surface, so visual evidence does not apply; the CLI transcript is the reviewer-visible artifact. Overall result: the user intent is demonstrated working end-to-end with no regressions attributable to this change.

Evidence: Codex session-lock identity — end-to-end CLI transcript

Source: Codex session-lock identity — end-to-end CLI transcript

# 2. fm-lock.sh: Codex session A acquires the per-home lock despite hidden ancestry lock acquired: session codex:thread-9f3a2b:/tmp/.../codex-state-A # 3. persisted identity binds thread id AND resolved Codex state root codex:thread-9f3a2b:/tmp/.../codex-state-A # 4. status -> lock: held by live session codex:thread-9f3a2b:/tmp/.../codex-state-A # 5. fm-harness.sh -> codex (ancestry hidden, verified by writer flock) # 6. same thread id under root B -> codex:thread-9f3a2b:/tmp/.../codex-state-B (distinct, no collision) # 7. session B (root B) -> error: another live firstmate session holds the lock (session codex:thread-9f3a2b:/tmp/.../codex-state-A) (exit 1) # 8. fm_session_identity_alive of A's identity, checked from root B env -> LIVE (uses bound root) # 9. after releasing A's writer flock -> lock: stale (session codex:thread-9f3a2b:/tmp/.../codex-state-A writer lock is free) # 10. non-Codex path -> numeric harness pid identity (unchanged)


�[1m# 1. Codex session A holds its thread-writer flock (as codex-cli does for the active writer)�[0m
flock held on /tmp/tmp.We2QSLv4w7/codex-state-A/thread-writer-locks/thread-9f3a2b.lock

�[1m# 2. fm-lock.sh: Codex session A acquires the per-home lock despite hidden ancestry�[0m
$ CODEX_HOME='/tmp/tmp.We2QSLv4w7/codex-state-A' CODEX_THREAD_ID=thread-9f3a2b FM_HOME='/tmp/tmp.We2QSLv4w7/fm-home' '~/.no-mistakes/worktrees/5b66d61025b2/01M1F8J5NJQFS14DRAM3Y8YAT1/bin/fm-lock.sh'
lock acquired: session codex:thread-9f3a2b:/tmp/tmp.We2QSLv4w7/codex-state-A
(exit 0)

�[1m# 3. The persisted identity binds BOTH the thread id and the resolved Codex state root�[0m
$ cat '/tmp/tmp.We2QSLv4w7/fm-home/state/.lock'
codex:thread-9f3a2b:/tmp/tmp.We2QSLv4w7/codex-state-A
(exit 0)

�[1m# 4. fm-lock.sh status: the recorded identity reads as a live session�[0m
$ CODEX_HOME='/tmp/tmp.We2QSLv4w7/codex-state-A' CODEX_THREAD_ID=thread-9f3a2b FM_HOME='/tmp/tmp.We2QSLv4w7/fm-home' '~/.no-mistakes/worktrees/5b66d61025b2/01M1F8J5NJQFS14DRAM3Y8YAT1/bin/fm-lock.sh' status
lock: held by live session codex:thread-9f3a2b:/tmp/tmp.We2QSLv4w7/codex-state-A
(exit 0)

�[1m# 5. fm-harness.sh: the verified writer flock identifies the harness as codex with ancestry hidden�[0m
$ CODEX_HOME='/tmp/tmp.We2QSLv4w7/codex-state-A' CODEX_THREAD_ID=thread-9f3a2b FM_CONFIG_OVERRIDE='/tmp/tmp.We2QSLv4w7/none' '~/.no-mistakes/worktrees/5b66d61025b2/01M1F8J5NJQFS14DRAM3Y8YAT1/bin/fm-harness.sh'
codex
(exit 0)

�[1m# 6. A DIFFERENT Codex root, SAME thread id, also holding its own flock -> distinct identity, no collision�[0m
flock held on /tmp/tmp.We2QSLv4w7/codex-state-B/thread-writer-locks/thread-9f3a2b.lock
$ CODEX_HOME='/tmp/tmp.We2QSLv4w7/codex-state-B' CODEX_THREAD_ID=thread-9f3a2b FM_HOME='/tmp/tmp.We2QSLv4w7/other-home' bash -c '. "~/.no-mistakes/worktrees/5b66d61025b2/01M1F8J5NJQFS14DRAM3Y8YAT1/bin/fm-session-lock-lib.sh"; fm_session_identity'
codex:thread-9f3a2b:/tmp/tmp.We2QSLv4w7/codex-state-B
(exit 0)

�[1m# 7. Session B under root B is REFUSED the home lock that session A (root A) holds�[0m
$ CODEX_HOME='/tmp/tmp.We2QSLv4w7/codex-state-B' CODEX_THREAD_ID=thread-9f3a2b FM_HOME='/tmp/tmp.We2QSLv4w7/fm-home' '~/.no-mistakes/worktrees/5b66d61025b2/01M1F8J5NJQFS14DRAM3Y8YAT1/bin/fm-lock.sh'
error: another live firstmate session holds the lock (session codex:thread-9f3a2b:/tmp/tmp.We2QSLv4w7/codex-state-A); operate read-only until resolved
(exit 1)

�[1m# 8. Liveness of A's recorded identity uses A's BOUND root, even when checked from root B's environment�[0m
$ CODEX_HOME='/tmp/tmp.We2QSLv4w7/codex-state-B' CODEX_THREAD_ID=thread-9f3a2b bash -c '. "~/.no-mistakes/worktrees/5b66d61025b2/01M1F8J5NJQFS14DRAM3Y8YAT1/bin/fm-session-lock-lib.sh"; fm_session_identity_alive "$(cat "/tmp/tmp.We2QSLv4w7/fm-home/state/.lock")" && echo LIVE || echo STALE'
LIVE
(exit 0)

�[1m# 9. Release A's writer flock -> A's recorded identity now reads stale, lock is reclaimable�[0m
$ CODEX_HOME='/tmp/tmp.We2QSLv4w7/codex-state-A' CODEX_THREAD_ID=thread-9f3a2b FM_HOME='/tmp/tmp.We2QSLv4w7/fm-home' '~/.no-mistakes/worktrees/5b66d61025b2/01M1F8J5NJQFS14DRAM3Y8YAT1/bin/fm-lock.sh' status
lock: stale (session codex:thread-9f3a2b:/tmp/tmp.We2QSLv4w7/codex-state-A writer lock is free)
(exit 0)

�[1m# 10. Non-Codex ancestry path is untouched: a plain numeric harness pid identity still works�[0m
$ FM_HOME='/tmp/tmp.We2QSLv4w7/claude-home' FM_FAKE_HARNESS_PID=1686478 bash -c 'mkdir -p "/tmp/tmp.We2QSLv4w7/claude-home/state"; . "~/.no-mistakes/worktrees/5b66d61025b2/01M1F8J5NJQFS14DRAM3Y8YAT1/bin/fm-session-lock-lib.sh"; fm_session_identity'
1686767
(exit 0)
- Outcome: ⚠️ 2 warnings across 1 run (11m20s)

Pipeline

Updates from git push no-mistakes

✅ **intent** - passed

✅ No issues found.

✅ **Rebase** - passed

✅ No issues found.

✅ **Review** - passed

✅ No issues found.

⚠️ **Test** - 2 warnings
  • ⚠️ tests/fm-sessionstart-nudge.test.sh:397 - tests/fm-sessionstart-nudge.test.sh fails at 'Pi continuation classification' (expected exit 0, got 1). Pre-existing and unrelated to this change: the assertion runs node --input-type=module that dynamically import()s a .pi/extensions/*.ts file, and Node v22.22.1 on this host rejects it with 'Unknown file extension .ts'. The test function is byte-identical at base commit 355f46f and exercises no session-lock/Codex-identity code. Matches the intent's documented pre-existing baseline failures; report only, do not repair.
  • ⚠️ tests/fm-session-start.test.sh:996 - tests/fm-session-start.test.sh fails at test_output_ordering_diagnostics_lead ('MISSING diagnostic did not appear at all'). Pre-existing and unrelated to this change: the test does rm -f "$fakebin/node" to force a 'MISSING: node' bootstrap line, but this host has /usr/bin/node on the test's BASE_PATH, so command -v node still succeeds and the diagnostic is never emitted. This test function is byte-identical at base commit 355f46f (the branch only edited unrelated helper functions in this file) and does not touch Codex session-lock identity. Matches the intent's documented pre-existing baseline failures; report only, do not repair.
  • bin/fm-test-run.sh tests/fm-session-lock-ancestry.test.sh — PASS (incl. new test_codex_writer_lock_supplies_identity_when_ancestry_is_hidden)
  • bin/fm-test-run.sh tests/fm-claude-stop-autoarm.test.sh tests/fm-branch-supervision.test.sh tests/fm-kimi-harness.test.sh tests/fm-sessionstart-nudge.test.sh tests/fm-session-start.test.sh tests/fm-startup-network.test.sh tests/fm-spawn-dispatch-profile.test.sh tests/fm-trace-context-lib.test.sh — 6 PASS, 2 pre-existing env failures (fm-sessionstart-nudge 'Pi continuation classification', fm-session-start test_output_ordering_diagnostics_lead)
  • Manual e2e against real bin/fm-lock.sh + bin/fm-harness.sh + bin/fm-session-lock-lib.sh with a real held flock: Codex acquire with hidden ancestry -> 'lock acquired: session codex:<thread>:<root>'; state/.lock content check; fm-lock.sh status live/stale wording; fm-harness.sh -> codex; same thread id under a second Codex root -> distinct identity; second root REFUSED the first root's home lock; fm_session_identity_alive resolves against the recorded root from a foreign env; writer-flock release -> stale; non-Codex fm_session_identity still returns a numeric pid
  • Verified both failing test functions are byte-identical at base commit 355f46fe via git show 355f46fe:tests/... and reproduced their host-specific root causes (Node 22.22.1 rejects .ts dynamic import; /usr/bin/node present on BASE_PATH)
  • Confirmed working tree clean, .no-mistakes/ untracked, no agent co-author trailers on the 5 branch commits
✅ **Document** - passed

✅ No issues found.

⚠️ **Lint** - 1 warning
  • ⚠️ linter found issues (exit code 1)
✅ **Push** - passed

✅ No issues found.

@greptile-apps

greptile-apps Bot commented Sep 1, 2026

Copy link
Copy Markdown

Confidence Score: 5/5

The PR appears safe to merge within its explicitly stated scope, with no unacknowledged actionable defects identified.

The changed consumers consistently treat the new Codex token as a validated opaque identity, ownership and contention preserve fail-closed semantics, and child launches clear inherited Codex markers; the relative-root limitation is explicitly deferred.

Reviews (1): Last reviewed commit: "no-mistakes(document): align session-loc..." | Re-trigger Greptile

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant