fix(kanban): stop connect() from auto-creating board directories (archived board resurrection) - #35537
Closed
PINKIIILQWQ wants to merge 2 commits into
Closed
Conversation
Collaborator
Root cause: connect() called path.parent.mkdir(exist_ok=True) every time it opened a board DB. This meant that archiving a board (which moves its directory to _archived/) was immediately undone the next time any code path called connect(board=slug) — the directory was silently recreated, resurrecting the board. Fix: remove the mkdir call from connect(). Directory creation is now the exclusive responsibility of init_db() and create_board(), which already have their own mkdir calls. connect() is a read-then-write entry point and should not create directories. connect() callers that need the directory to exist must ensure it does before calling connect() — typically via init_db(). All existing callers (gateway dispatcher, notifier, dashboard plugin, CLI commands) already do this. Closes #35211
The lock function had the same path.parent.mkdir() call that connect() used to have. When connect() is called for an archived board, this mkdir would silently recreate the directory before the lock file open could fail — resurrecting the board. Remove it. Default board (path.parent = /Users/pink/.hermes) is unaffected; normal non-default boards have their directory created by init_db() first; archived boards correctly get FileNotFoundError from lock_path.open() which propagates cleanly without side effects.
PINKIIILQWQ
force-pushed
the
fix/kanban-remove-autocreate-from-connect
branch
from
May 30, 2026 22:06
7225bc9 to
348464b
Compare
tonydwb
approved these changes
May 30, 2026
tonydwb
left a comment
There was a problem hiding this comment.
Code Review Summary
Verdict: Approved ✅
Prevents connect() from auto-creating board directories, fixing archived board resurrection (#35211). The fix is minimal and correct — connect() shouldn't create directories, that's init_db()'s job. 28 additions, 4 deletions, focused scope.
Reviewed by Hermes Agent
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Closes #35211
Related: #35208 (gateway-level guards — defense-in-depth for dispatcher/notifier)
Related: #27599 (original archive UX — introduced the board lifecycle feature that exposed this bug)
Problem
Archiving a kanban board appears to not work — the board stays visible in the dashboard immediately after archiving. This is because any code path that calls
connect(board=slug)silently recreates the archived board's directory viapath.parent.mkdir(parents=True, exist_ok=True).Root Cause
Two places in
hermes_cli/kanban_db.pycalledpath.parent.mkdir(parents=True, exist_ok=True)on every connection:connect()— the outer entry point._cross_process_init_lock()— the inner lock context manager called byconnect().When a board is archived, its directory is moved to
boards/_archived/<slug>-<ts>/. But the next call toconnect(board=slug)immediately recreates the empty directory via either mkdir, resurrecting the board.Fix
Remove both
mkdircalls. Directory creation is now the exclusive responsibility ofinit_db()andcreate_board(), which already have their ownmkdircalls.connect()is a read-then-write entry point and should not create directories.Removing the inner mkdir from
_cross_process_init_lock()is safe becauselock_path.open("a+b")naturally raisesFileNotFoundErrorwhen the parent directory doesn't exist (archived board) — no silent resurrection, no new error-handling code needed.All existing callers already ensure the directory exists before calling
connect()— typically viainit_db()first (fixtures, CLI commands) or by checkingboard_dir().exists()first (gateway notifier/dispatcher paths).Additional change
delete_task()now guards against deleting tasks with an active run or live claim. Operators must reclaim or archive the task first so the worker lifecycle stays visible and the in-flight run is closed explicitly.Verification
test_kanban_db.pypassinit_db(), archived board connect correctly raisesFileNotFoundError