This repository was archived by the owner on Jul 16, 2026. It is now read-only.
Repository navigation
fix(slack-bridge): prime access cache at startup so #899 fix survives restarts (Phase 5.14) - #82
Merged
Conversation
…urvives restarts (Phase 5.14) Completes the Phase 5.13 sonichi#899 cache port. The cache machinery only protects against external deletion of `access.json` if the cache is actually populated. Phase 5.13 added the cache + restore plumbing but missed the 1-line OSS pairing that primes the cache on bridge startup — without it, `_access_cache` stays `None` until the first successful `load_allowed()` call. If that first call happens to be the access check for an inbound DM that arrives AFTER an external deletion (file deleted while the bridge was down, then a message lands), the cache is still `None`, `_restore_access_from_cache()` returns False, and `tofu_onboard()` silently overwrites the prior owner — exactly the regression sonichi#899 was meant to prevent. OSS sutando has had this priming since the original sonichi#899 fix; the Phase 5.13 PR ported the cache surface but not the startup wiring. Changes: - `src/slack-bridge.py`: in `main()`, call `load_allowed()` immediately after `_recover_orphan_sending_files()` and before any threads spin up. Reads access.json into the in-memory cache while the file is still on disk, so the very first inbound DM after a restart can detect and recover from external deletions. - `tests/slack-bridge-access-cache.test.py`: new `test_main_primes_access_cache_at_startup` structural guard. Verifies `main()` contains `load_allowed()` AND that it appears AFTER `_recover_orphan_sending_files()` in source order. A future PR that drops the priming or reorders it would silently regress sonichi#899. Audit summary: - Original Phase 5.14 scope was a `send_allowlist` helper extract for slack-bridge. Audit found OSS sutando still has the SAME inline allowlist as stando — the extract was done only for the discord family (discord-bridge + dm-result). Extracting slack would have ADDED divergence, violating the minimal-divergence rule. Scope was redirected to the actual remaining gap: the missing sonichi#899 priming call. After this PR, slack-bridge has converged with OSS structurally — no extant net-portable wins. Verification: - 56/56 Python tests pass (no test count change; cache-test file grew by 1 net-new test). - TypeScript typecheck clean. - `python3 -m py_compile src/slack-bridge.py` clean. Phase 5.14 of the OSS → private sync. Closes the slack-bridge backlog. Co-authored-by: Cursor <cursoragent@cursor.com>
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 subscribe to this conversation on GitHub.
Already have an account?
Sign in.
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.
Summary
Completes Phase 5.13's sonichi#899 cache port. The cache machinery only protects against external deletion of
access.jsonif the cache is populated. Phase 5.13 added the cache + restore plumbing but missed the 1-line OSS pairing that primes the cache on bridge startup.Why the priming matters
Without it,
_access_cachestaysNoneuntil the first successfulload_allowed()call. The hostile race:access.jsondeleted externally while the bridge was down (Sutando.app Settings revoke flow, accidental rm, sync conflict).load_allowed()to validate sender → file missing → returnsNone, cache staysNone.tofu_onboard()._restore_access_from_cache()returnsFalse(cache isNone).OSS sutando has had this priming since the original sonichi#899 fix; Phase 5.13 ported the cache surface but not the startup wiring. This PR closes that gap.
Changes
src/slack-bridge.py— inmain(), callload_allowed()immediately after_recover_orphan_sending_files()and before any threads spin up. Readsaccess.jsoninto the in-memory cache while the file is still on disk, so the very first inbound DM after a restart can detect and recover from external deletions.tests/slack-bridge-access-cache.test.py— newtest_main_primes_access_cache_at_startupstructural guard. Verifiesmain()containsload_allowed()AND that it appears AFTER_recover_orphan_sending_files()in source order. A future PR that drops the priming or reorders it would silently regress sonichi#899.Audit note on original Phase 5.14 scope
The original Phase 5.14 plan was a
send_allowlisthelper extract for slack-bridge (parallel to Phase 5.9 for discord). Audit found OSS sutando still has the SAME inline allowlist as stando — the extract was done only for the discord family (discord-bridge + dm-result). Extracting slack would have added divergence, violating the minimal-divergence rule.Scope was redirected to the actual remaining gap: the missing sonichi#899 priming call. After this PR, slack-bridge has converged with OSS structurally — same function set, same top-level constants, same startup ordering. No remaining net-portable wins for slack-bridge.
Test plan
python3 tests/slack-bridge-access-cache.test.py— 11/11 pass (+1 net-new)pnpm -s typecheckcleanpython3 -m py_compile src/slack-bridge.pycleanRoadmap update
This closes the slack-bridge backlog from Phase 5.11/5.12/5.13/5.14. Future Phase 5 work will pick from the remaining inventory items (none currently identified as high-confidence net-portable; this may be a natural stopping point for the bridge-family sync).
Made with Cursor