Skip to content

fix: keep seen-id watermarks out of /tmp, where a reboot wipes them - #102

Merged
ThinkOffApp merged 1 commit into
mainfrom
fix/durable-seen-files
Sep 13, 2026
Merged

fix: keep seen-id watermarks out of /tmp, where a reboot wipes them#102
ThinkOffApp merged 1 commit into
mainfrom
fix/durable-seen-files

Conversation

@ThinkOffApp

Copy link
Copy Markdown
Owner

Every seen-file default lived in /tmp: poller, dm_poller, automation, comments, discord. macOS clears /tmp on boot, so each reboot silently reset every watermark and the next poll replayed room history as new.

This happened for real on 2026-09-13. Restarting iak-mcp-daemon on a box whose /tmp had been cleared dumped 48 messages from seven rooms into one agent's notification file, the oldest from March.

Noise is the mild failure. The real one is that the replay carried live-sounding instructions — "get them trading before they close today" (6 Mar) and a request to delete a set of messages — which an agent reading them as new would have acted on. A watermark is not a convenience; it is what stops an agent obeying a six-month-old order.

Changes

  • Defaults move to $XDG_STATE_HOME/iak or ~/.local/state/iak.
  • stateFile() adopts an existing /tmp watermark once, so the upgrade itself does not cause the single replay this change exists to prevent.
  • Both writers mkdir -p first; room-poller.mjs carried its own hardcoded /tmp default, moved too.
  • The config test now asserts the property (not in /tmp, correct filename) rather than a literal path, since the state dir varies by home and XDG_STATE_HOME.

Verified

  • 276/276 tests pass.
  • A default config resolves to the state dir, and adoption copied 1053 real ids out of the legacy file on this machine.

🤖 Generated with Claude Code

Every seen-file default lived in /tmp: poller, dm_poller, automation,
comments, discord. macOS clears /tmp on boot, so each reboot silently reset
every watermark and the next poll replayed room history as new.

That happened for real on 2026-09-13. Restarting iak-mcp-daemon on a box whose
/tmp had been cleared dumped 48 messages from seven rooms into one agent's
notification file, the oldest from March. Noise is the mild failure. The real
one is that the replay included live-sounding instructions -- "get them
trading before they close today" (6 Mar) and a request to delete messages --
which an agent reading them as new would have acted on.

- defaults move to $XDG_STATE_HOME/iak or ~/.local/state/iak
- stateFile() adopts an existing /tmp watermark once, so upgrading does not
  itself cause the replay this prevents
- both writers mkdir -p first; room-poller's own hardcoded default moved too
- config test asserts the property (not in /tmp, right filename) rather than a
  literal path, since the state dir varies by home and XDG_STATE_HOME

Verified: 276/276 tests pass; a default config resolves to the state dir and
adopted 1053 real ids from the legacy file.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
@chatgpt-codex-connector

chatgpt-codex-connector Bot commented Sep 13, 2026

Copy link
Copy Markdown

Codex Review Summary

This comment shows the latest Codex review activity on this pull request.

Review Status Commit Review trigger
📝 Code Review Completed 2026-09-13T07:56:44.963560Z d4fbfaf PR opened
ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review" or "@codex security review".

Codex reacts with 👀 while any review is running, comments if it has suggestions, and reacts with 👍 once all reviews finish with no findings.

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: d4fbfaf442

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Comment thread src/config.mjs
Comment on lines +28 to +31
if (!existsSync(target)) {
const legacy = `/tmp/iak-${name === 'seen-ids.txt' ? 'seen-ids' : name.replace(/\.txt$/, '')}.txt`;
mkdirSync(STATE_DIR, { recursive: true });
if (existsSync(legacy)) copyFileSync(legacy, target);

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

P1 Badge Migrate explicitly configured legacy paths

When an existing JSON config explicitly contains the former default, such as poller.seen_file: "/tmp/iak-seen-ids.txt", this copies the watermark to the new target but loadConfig subsequently spreads raw.poller over the defaults and continues using the /tmp path. Since the repository's examples encouraged explicit per-agent /tmp paths, affected upgrades still lose their watermark and replay old messages after reboot; translate configured legacy paths during config normalization rather than only changing the omitted-value default.

Useful? React with 👍 / 👎.

Comment thread src/config.mjs
Comment on lines +33 to +37
} catch {
// A read-only or unwritable home is not a reason to fail config load;
// the poller will fall back to an empty watermark and simply be noisy once.
}
return target;

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

P1 Badge Fall back when the state directory is unwritable

When XDG_STATE_HOME or the account's home is read-only or unavailable, the caught mkdirSync failure is followed by returning the same unwritable target. The active team-relay poller then calls the shared saveSeenIds, whose mkdirSync/openSync is uncaught during initial seeding, so rooms watch exits instead of merely producing the one noisy restart described here; this regresses service/container accounts that could previously write to /tmp, so return a writable fallback or reject the configuration explicitly.

Useful? React with 👍 / 👎.

@ThinkOffApp
ThinkOffApp merged commit a35462a into main Sep 13, 2026
3 checks passed
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