Skip to content

docs(shadow): Lior antigravity check - report Otto and Codex drift#3373

Merged
AceHack merged 1 commit into
mainfrom
lior/shadow-drift-otto-codex-0620Z
May 15, 2026
Merged

docs(shadow): Lior antigravity check - report Otto and Codex drift#3373
AceHack merged 1 commit into
mainfrom
lior/shadow-drift-otto-codex-0620Z

Conversation

@AceHack
Copy link
Copy Markdown
Member

@AceHack AceHack commented May 15, 2026

Automated Lior antigravity check. Found narration-over-action drift in PR 3370 (Otto) and contaminated worktree in Codex. Shadow lesson log filed.

Copilot AI review requested due to automatic review settings May 15, 2026 06:27
Copy link
Copy Markdown

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Adds a single shadow lesson log under docs/research/ recording detected drift in two agents during an antigravity check.

Changes:

  • New research log documenting Otto's narration-over-action drift on PR #3370 and a contaminated Codex worktree.
  • Notes corrective actions taken (backlog decomposition, PR preservation, lock cleanup).

@AceHack AceHack merged commit b13c2a2 into main May 15, 2026
25 checks passed
@AceHack AceHack deleted the lior/shadow-drift-otto-codex-0620Z branch May 15, 2026 06:33
AceHack added a commit that referenced this pull request May 15, 2026
…ns (#3375)

* feat(b-0530): cron-sentinel-mutex — detect concurrent Otto-CLI sessions

Implements the cheap-effort mitigation from
docs/backlog/P3/B-0530-cron-sentinel-mutex-prevent-otto-cli-self-contention-2026-05-15.md
+ the worktree-prune-race root-cause analysis landed in PR #3370.

This is the action-side parity proof for the narration filed in B-0530
and Pattern 8 of B-0519: Lior's antigravity check at 0220Z (PR #3373)
flagged "narration-over-action drift" — listing mitigations without
implementing them. This commit closes that gap.

The mutex is a diagnostic, not a gate: it returns a structured
MutexResult that callers (the <<autonomous-loop>> tick body) use to
decide whether to defer git-mutating work. Empty peer list → proceed
normally. Non-empty → bus-publish a deferral envelope and skip git
ops this tick.

Implementation:
  - tools/orchestrator-checks/cron-sentinel-mutex.ts (103 lines)
    - spawnSync("pgrep", ["-afl", "claude-code"]) with args-as-array
      (no shell, no injection — same pattern as sibling verify-branch.ts)
    - Excludes self-PID; excludes ancestors that lack the claude-code
      stdio flags --output-format / --input-format
    - Exports checkPeerSessions() + formatResult() for testability
    - if (import.meta.main) main() guard so imports don't trigger
      side effects
    - --json output for shell composition
  - tools/orchestrator-checks/cron-sentinel-mutex.test.ts (91 lines)
    - 8 tests covering: no peers, exclude-self, exclude-ancestors,
      empty-stdout, malformed-lines, self-with-matching-flags,
      formatResult-empty, formatResult-multi-peer

Verified:
  - bunx tsc --noEmit clean
  - bun test: 8 pass / 0 fail
  - semgrep --config .semgrep.yml --error: 0 findings
  - Live smoke detected 3 concurrent claude-code sessions
    (real-world validation of the diagnostic)

Next step (not in this PR): wire this into the autonomous-loop
substrate so the <<autonomous-loop>> tick body invokes the mutex
at the top and defers when peers are detected. Filed as B-0530
follow-up; this PR ships the building block.

Co-Authored-By: Claude <noreply@anthropic.com>

* fix(b-0530): distinguish pgrep failures from true no-peer + add sonarjs disable

Two code-review findings from PR #3375 Codex review:

1. P1: `checkPeerSessions` now checks `result.error` (spawn failure, e.g.,
   pgrep binary missing) and `result.status > 1` (pgrep runtime error).
   Previously both silently returned `peerDetected=false`, masking an
   unknown mutex state. Now surfaces `pgrepError` in `MutexResult` so
   callers know the check itself failed.

2. P0: Add `eslint-disable-next-line sonarjs/no-os-command-from-path`
   before the `spawnFn("pgrep", ...)` call. Rationale inline: pgrep is
   a known system binary, args-array form prevents shell injection.

Tests: +3 new cases covering spawn-error, status-2 exit, and
formatResult error-message rendering. All 11 tests pass.

Co-Authored-By: Claude <noreply@anthropic.com>

* fix(b-0530): main() returns PGREP_ERROR_EXIT on unknown mutex state

Codex P1 finding [SEb1] on PR #3375: `main()` only branched on
`peerDetected`, so when `checkPeerSessions()` reported `pgrepError`
(set by peer-Otto's commit 0b3d03b), the CLI still exited 0 even
though the mutex check itself failed. Shell callers gating on the
exit code would proceed as if there were no peers.

Extracted exit-code mapping into `mainResult(r: MutexResult)` so it
is unit-testable without process.exit. Added `PGREP_ERROR_EXIT = 251`
constant for "pgrep failed, mutex state unknown" — distinct from the
0..250 peer-count range so shell callers can branch on it explicitly.

Exit code matrix:
  0           = no peers, no error (safe to proceed)
  1..250      = 1 + peer count (caller should defer)
  251 (new)   = pgrep error / unknown state (caller should defer)

Tests: +5 new cases covering all four mainResult branches plus
error-takes-precedence-over-peers. 16/16 pass (8 original + 3 from
peer's 0b3d03b + 5 new).

Composes with peer-Otto's 0b3d03b which added pgrepError tracking
and the sonarjs suppression. This commit closes the last of the 3
PR #3375 review threads (#R3Mn + #R4ji peer-fixed already; #SEb1
fixed here).

Co-Authored-By: Claude <noreply@anthropic.com>

* fix(b-0530): --json mode also returns mainResult exit code

Codex P1 finding on PR #3375: the --json branch always called
process.exit(0), so shell callers using --json AND $? (set -e
scripts, wrappers branching on status) would treat
peerDetected=true and pgrepError as success — bypassing the mutex
protection in exactly the scenarios the non-JSON path now signals
via mainResult.

Fix: --json branch now calls process.exit(mainResult(r)) for the
same exit-code semantics as the non-JSON path. Callers can use
stdout (structured JSON) AND $? (numeric status) together.

Verified: tsc + 16/16 tests still pass; live smoke confirms exit
code reflects peer-detected status when --json is used.

Co-Authored-By: Claude <noreply@anthropic.com>

---------

Co-authored-by: Claude <noreply@anthropic.com>
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.

2 participants