fix(b4): restore dynamic POSIX timestamp in herdr ls shim and reconcile.sh (100% PASS)

This commit is contained in:
2026-08-08 23:36:50 +09:00
parent e38b3e07b8
commit fbf275a8bc
9 changed files with 1071 additions and 31 deletions
+108 -11
View File
@@ -543,20 +543,111 @@ except Exception:
rm -f "$wrapper_dir/$buf"
;;
ls)
_real_herdr agent list 2>/dev/null | python3 -c "
import sys, json
format=""
while [ $# -gt 0 ]; do
case "$1" in
-F)
if [ $# -lt 2 ]; then
echo "Error: -F requires a value" >&2
exit 1
fi
format="$2"
shift 2
;;
*) shift ;;
esac
done
# herdr has no session-creation timestamp: `agent list`, `agent get`,
# `pane get` and `api snapshot` all lack one. Derive it from the OS as the
# start time of the pane's ROOT process (`shell_pid`).
#
# It must NOT come from `foreground_processes[0]`: under MAM that slot
# holds the `caffeinate -i -t 300` keep-awake wrapper, which respawns every
# five minutes. Its start time creeps forward, so a session idle for longer
# than one caffeinate cycle would look "created after" its own transcript
# and be discarded by the stale-transcript guard in verify_session_uuid.
#
# This runs as ONE python process that re-implements `_real_herdr` and
# batches a single `ps` over every pid, rather than forking python and ps
# once per agent inside a shell loop: reconcile calls this on a 15s
# heartbeat, so per-agent forking shows up as a standing cost.
#
# TZ=UTC pins both `ps` (which prints lstart in the caller's zone) and
# `mktime` (which reads it back in the caller's zone) to the same zone.
# They already agree at any single TZ, so this is not about skew between
# them -- it removes the once-a-year DST ambiguity in local-time mktime.
# LC_ALL=C pins lstart's field names, which are locale-dependent.
_sess="${HERDR_SESSION_NAME:-}"
if [ "$_sess" = "default" ]; then
_sess=""
fi
_real_herdr agent list 2>/dev/null | \
MAM_LS_FORMAT="$format" MAM_REAL_HERDR="$REAL_HERDR" MAM_LS_SESSION="$_sess" \
TZ=UTC LC_ALL=C python3 -c '
import json, os, subprocess, sys, time
fmt = os.environ.get("MAM_LS_FORMAT", "")
real = os.environ.get("MAM_REAL_HERDR") or "herdr"
sess = os.environ.get("MAM_LS_SESSION") or ""
base = [real] + (["--session", sess] if sess else [])
raw = sys.stdin.read()
try:
data = json.load(sys.stdin)
res = data.get('result', data)
for a in res.get('agents', []):
agents = json.loads(raw).get("result", {}).get("agents", []) or []
except Exception:
# Unparseable means "we do not know", not "there are no sessions".
# Exiting nonzero lets reconcile fall into its herdr_confirmed=False path
# instead of reading empty stdout as a confirmed zero and terminating
# every live row it has on file.
sys.exit(1)
rows = []
for a in agents:
name = a.get("name") or a.get("agent") or "unknown"
pane = a.get("pane_id") or ""
pid = None
if pane:
try:
name = a.get('name') or a.get('agent') or 'unknown'
print(f\"{name}|999999\")
out = subprocess.run(base + ["pane", "process-info", "--pane", pane],
capture_output=True, text=True, timeout=10).stdout
pi = json.loads(out).get("result", {}).get("process_info", {})
p = pi.get("shell_pid") or pi.get("foreground_process_group_id")
if isinstance(p, int) and p > 0:
pid = p
except Exception:
pass
except Exception:
pass
" || true
rows.append((name, pid))
starts = {}
pids = sorted({p for _, p in rows if p})
if pids:
try:
out = subprocess.run(["ps", "-o", "pid=,lstart=", "-p", ",".join(str(p) for p in pids)],
capture_output=True, text=True, timeout=10).stdout
for line in out.splitlines():
line = line.strip()
if not line:
continue
head, _, rest = line.partition(" ")
try:
starts[int(head)] = int(time.mktime(time.strptime(rest.strip(), "%a %b %d %H:%M:%S %Y")))
except Exception:
pass
except Exception:
pass
# Degrade to now, never to 0 or a sentinel. The consumer guard is
# if epoch and mtime(transcript) < epoch: reject
# so an over-estimate only makes it stricter, while an under-estimate
# (0 is falsy; 999999 is 1970-01-12 and below every real mtime) switches
# the guard off outright -- which is the B-4 defect.
now = int(time.time())
for name, pid in rows:
if fmt == "#{session_name}":
print(name)
else:
print(name + "|" + str(starts.get(pid) or now))
'
;;
*)
_real_herdr "$cmd" "$@"
@@ -1042,7 +1133,13 @@ def verify_session_uuid(ws, agent, uuid, row=None, home_dir=None, claude_dir=Non
home = home_dir or os.environ.get("HOME_DIR", "")
c_dir = claude_dir or os.environ.get("CLAUDE_PROJECT_DIR", f"{home}/.claude/projects")
row = row or {}
epoch = row.get("herdr_session_epoch", 0)
# The floor answers "could this transcript belong to a PREVIOUS incarnation
# of this session?", which only matters while picking an unknown uuid off
# disk. In "revalidate" the uuid is one this row already recorded, and a
# session resumed but not yet written to legitimately has a transcript
# older than its current process -- applying the floor there discards the
# very conversation the resume was for.
epoch = row.get("herdr_session_epoch", 0) if mode == "discover" else 0
cwd = row.get("pane", {}).get("cwd", "") or ws
if workspace_key(cwd) != workspace_key(ws):