Skip to content

fix: cross-instance store locking, prompt size preflight, windows graceful stop - #157

Merged
jkyberneees merged 7 commits into
mainfrom
fix/open-issues
Oct 17, 2026
Merged

jkyberneees merged 7 commits into
mainfrom
fix/open-issues

Conversation

@jkyberneees

@jkyberneees jkyberneees commented Oct 17, 2026 •

Copy link
Copy Markdown
Contributor

Follow-up to #156: this closes the issues that round left open.

  • Cross-instance store writes.

    • Two bodek instances could interleave between a store's merge-read and its rename, which dropped updates. A test lost half of 80 tokens and half of 60 workspaces.
    • New internal/filelock adds an advisory lock on <path>.lock (flock on unix, LockFileEx on windows). It wraps the read-merge-write cycle in the token, workspace and settings stores.
    • The lock is best-effort and gives up after 2s, so persistence never hangs the TUI.
  • Symlinked stores. Saves used to replace a dotfile-managed symlink with a regular file. They now write through the link.

  • Oversized prompts.

    • A prompt over odek's 8 MiB frame cap used to be committed before it was refused. That added a transcript card, set busy, cleared the attached files and stalled the queue.
    • Submit, queue sends and queue drains now check the frame size first (client.CheckPromptSize). Everything stays in place behind a note explaining why the prompt was not sent.
  • Windows graceful stop.

    • odek now starts in its own console process group and gets CTRL_BREAK, which Go delivers as os.Interrupt. Before, every quit waited 12s and then force-killed.
    • Because the new group hides console Ctrl+C from odek, and Windows has no watchdog, the child is assigned to a kill-on-close Job Object. bodek holds its only handle, so a bodek that dies for any reason takes odek with it.
    • CI's Windows job now runs these process tests and the filelock suite. Until now it only built and vetted.
  • Docs. AGENTS.md now describes the approval bare-key fallback as implemented: Backspace or ^W empties the draft. Before, it wrongly said ^U does.

  • Adversarial review follow-ups.

    • The lock now retries only on contention, so a mount without locking no longer stalls every save by 2s. It also creates the store directory first and keys on the resolved path.
    • A refused queue head is noted once rather than on every heartbeat, and /unattach drains it right away.
    • The Windows guard now runs before the reaper, so a recycled pid can't land in the job.

Checks: make fmt, make vet, make lint (0 issues) and make test (race) pass on macOS. On windows-latest CI, the filelock suite and the Windows process tests pass: CTRL_BREAK reaches the child as os.Interrupt, and closing the job kills the child.

🤖 Generated with Claude Code

jkyberneees and others added 4 commits October 17, 2026 16:50
The token, workspace and settings stores merge the file before replacing
it, but two bodek instances could still interleave between the read and
the rename and drop each other's update (a test lost half of 80 tokens).
A new internal/filelock takes an advisory lock on <path>.lock around
the cycle — best-effort, released by the OS if the holder dies, and
skipped after 2s so persistence never hangs the TUI.

Saves also wrote the staged file over a symlinked store, replacing a
dotfile-managed link with a regular file; they now write through it.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
An oversized prompt was committed (transcript card, busy state, staged
files cleared) before the client refused the frame, and its errMsg
never drained the queue. Submit, queue sends and drains now preflight
the frame size with client.CheckPromptSize and leave everything in
place behind an explaining note.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
AGENTS.md promised ^U clears the draft over an approval card; the code,
README and tests have ^U page the transcript there. Document the real
path (Backspace / ^W) and the find/drawer-editor swallow rule.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Process.Signal(os.Interrupt) is unsupported on windows, so every quit
waited out the 12s grace and force-killed odek mid-cleanup. The child
now starts in its own console process group and gets CTRL_BREAK, which
Go delivers as os.Interrupt. The new group also hides console Ctrl+C
from odek, and windows has no re-exec watchdog, so the child is placed
in a kill-on-close Job Object whose only handle bodek holds: a bodek
that dies for any reason takes odek with it. CI's windows leg now runs
these process tests and the filelock suite.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
@cloudflare-workers-and-pages

cloudflare-workers-and-pages Bot commented Oct 17, 2026 •

Copy link
Copy Markdown

Deploying with  Cloudflare Workers  Cloudflare Workers

The latest updates on your project. Learn more about integrating Git with Workers.

Status Name Latest Commit Preview URL Updated (UTC)
✅ Deployment successful!
View logs
bodek c1c3410 Commit Preview URL

Branch Preview URL
Oct 17 2026, 03:03 PM

jkyberneees and others added 3 commits October 17, 2026 17:02
tryLock treated every error as a busy peer, so a mount without locking
(NFS nolocks, some SMB/FUSE) stalled each save — workspace persists run
on the update loop — by the full 2s. Only EWOULDBLOCK / lock violation
retries now. Lock also creates the store directory (the first write
ran unlocked) and keys on the resolved path, so two links to one store
share its lock.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
The drain runs after every idle event, so an oversized queue head was
re-encoded and re-noted on each heartbeat. The refusal is remembered
until the head or the staged files change, /unattach re-drains at once,
and the note says when the text alone is too long.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
guardChild opened the child by pid after the reaper's Wait could have
released the handle, so a child that exited at once risked a recycled
pid landing in the kill-on-close job. It now runs first. The CI step
that runs the windows process and filelock tests was also missing from
the previous commit.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
@jkyberneees
jkyberneees marked this pull request as ready for review October 17, 2026 15:06
@jkyberneees
jkyberneees merged commit db5fde6 into main Oct 17, 2026
9 checks passed
@jkyberneees
jkyberneees deleted the fix/open-issues branch October 17, 2026 15:17
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