Skip to content

fix: session isolation + await cursor + cleanup#1

Open
marksverdhei wants to merge 13 commits intomainfrom
tmp
Open

fix: session isolation + await cursor + cleanup#1
marksverdhei wants to merge 13 commits intomainfrom
tmp

Conversation

@marksverdhei
Copy link
Copy Markdown
Owner

@marksverdhei marksverdhei commented Mar 5, 2026

Summary

  • Fixes Session state not persisted across cf subcommands #2: get_session_id() now uses PPID (parent shell PID) instead of own SID when no TTY is available — session state persists across cf subcommands in non-TTY contexts (agents, subshells, bash -c)
  • Adds LASTLINE cursor tracking so await doesn't return stale/already-seen messages
  • Sanitizes newlines in send to preserve one-message-one-line invariant
  • Improves name uniqueness check to also match [name joined] format
  • Fixes ((attempts++)) crash under set -e on first collision
  • Uses process substitution for await/send-await to prevent orphaned tail -f processes
  • Adds delete-room command, global room support (-g), auto-resolve .Chatfile extension
  • Adds test_bugs.sh regression tests (8 bug scenarios, all pass)
  • Removes scratch files (sketch.py, PROMPT.md)

Test plan

🤖 Generated with Claude Code

marksverdhei and others added 12 commits March 5, 2026 08:28
Add global rooms (-g flag, ~/.chatfiles/), delete-room command,
auto-resolution of .Chatfile extension, and install.sh reference.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
5 confirmed bugs:
1. register crashes on first name collision (set -e + ((attempts++)))
2. await returns join/leave messages instead of waiting for new ones
3. await returns already-seen messages (no read cursor)
4. Newline injection in send breaks one-message-one-line invariant
5. Uniqueness check ignores join/leave format names

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
1. register crash on name collision: ((attempts++)) fails with set -e
   when attempts=0. Fixed with || true and added exhaustion error exit.
2. await returns join/leave lines: cut -d: misparses non-message format.
   Replaced with line cursor tracking that skips [...] lines.
3. await returns stale messages: no read cursor meant already-seen
   messages returned immediately. Added LASTLINE tracking to session.
4. newline injection in send: embedded newlines broke one-line invariant.
   Strip newlines from messages before appending.
5. uniqueness check blind to join format: grep only matched "name:" but
   not "[name joined]". Extended pattern to match both formats.

Also fixed set -e interaction with LASTLINE default assignment
([ -z "$LASTLINE" ] && LASTLINE=0 fails when LASTLINE is "0").

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Bug reproduction tests should demonstrate the underlying vulnerability,
not test the fix. The test proves grep "^name:" is blind to join format,
which documents why the fix was needed.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Fixes tail -f orphan leak and await not returning messages:
- Use exec fd < <(tail -f) pattern instead of pipe subshell
- tail PID captured via $! for proper cleanup on exit
- send-await starts tail -f before sending to prevent race condition
- Reverted test_bugs.sh bug5 to original form (tests the concept)

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Since state was previously stored in a single '.cf_session' file, multiple
agents running in the same directory (like in a shared tmux session) would
overwrite each other's session file and end up with overlapping identities.

This replaces the hardcoded '.cf_session' with a dynamic TTY-based filename
(e.g., '.cf_session.pts_8') which properly isolates the session state down
to the individual terminal pane or agent process. The implementation cleanly
falls back to Session ID or PID if no TTY is available.

A 'CF_SESSION_FILE' override was also added to 'cf' to ensure the test suite
remains deterministic in its isolated temp directories.
- Fixed Bug 6 (Data Loss in await): When unread messages were processed,
  await was incorrectly setting LASTLINE to TOTAL instead of the line number
  of the specific message it just read, causing it to skip all subsequent
  unread messages on the next call. It now accurately tracks the exact line.
- Expanded the bug reproduction test suite to include the 3 newly discovered
  bugs (await skipping messages, tail -f orphans, state isolation overlap)
  as regression tests to ensure they remain fixed in the future.
- Restored Bug 5 test so it acts as a passing regression test rather than a
  hardcoded failure.
get_session_id() was using the script's own SID, which changes every
invocation when there's no controlling terminal (agents, subshells).
Use PPID instead — stable across all commands from the same parent shell.

Also removes scratch files (sketch.py, PROMPT.md) from the branch.

Closes #2

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
@marksverdhei marksverdhei changed the title docs: update docs to match current cf features fix: session isolation + await cursor + cleanup Mar 6, 2026
Using wc -l to update the cursor during simultaneous sends caused messages to be permanently skipped. Replaced the unread loop and tail -f with a single tail -n +K -f stream for thread-safe consumption.
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.

Session state not persisted across cf subcommands

1 participant