This repository was archived by the owner on Jul 16, 2026. It is now read-only.
Repository navigation
feat(discord-config): shared owner-resolution helper (Phase 5.10) - #78
Merged
Merged
Conversation
Three coordinated changes that close the OSS->stando port of the owner-resolution unification started in Phase 5.9. Pre-Phase-5.10 both `dm-result.py:_resolve_owner_id` and `discord-bridge.py:_poll_proactive` carried inline copies of the same env -> tierMap -> bot-filter walk chain. The drift class @liususan091219 flagged on OSS PR sonichi#1029 — and that bit production in sonichi#846 — was waiting to recur. This PR funnels both sites through one helper. 1. src/discord_config.py (new, verbatim port from OSS sutando) - load_config / save_config / resolve_owner_id / auto_seed_if_missing. - resolve_owner_id chain: env override -> discord-config.json[owner] -> discord-config.json[tierMap] -> access.json[owner] (legacy) -> access.json[tierMap] (legacy sonichi#846 path). Returns None when exhausted; helper deliberately does NOT fall through to allowFrom[0] (the sonichi#1147 bug path) — callers do that step paired with their own bot-filter walk. - auto_seed_if_missing: idempotent. On first boot writes <workspace>/state/discord-config.json seeded from access.json. Emits WARN when seed has to fall back to allowFrom[0] so the operator catches a mis-seed. 2. src/dm-result.py: rewire _resolve_owner_id - Replaces the ~57 line inline chain with a delegate-to-helper pattern. Helper handles steps 1-5; bot-filter REST walk stays here (helper is sync-only, so async/sync split between the two callers stays clean). 3. src/discord-bridge.py: rewire _poll_proactive owner block + boot - on_ready now calls discord_config.auto_seed_if_missing on the parsed access.json. Idempotent; non-fatal on error. - Owner-resolution block in poll_proactive replaces ~42 inline lines with a discord_config.resolve_owner_id call followed by the existing async bot-filter walk. Preserves the list-order fix (`load_allowed()` set->list change from the 2026-05-18 msze_/qingyunwu mis-route). Net effect on diff with OSS upstream ------------------------------------ - discord-bridge.py: -42 +30 in the proactive block (closer to OSS). - dm-result.py: -57 +30 in the resolver (closer to OSS). - discord_config.py: byte-identical to OSS (zero conflict surface going forward). - Total: ~70 lines of stando-specific inline chain replaced with delegate-to-shared-helper calls. The diff against upstream SHRINKS on the two existing files. Pattern matches Phase 5.9's send_allowlist extract. Tests ----- - tests/discord-config.test.py (new, 15 cases): resolution chain semantics (env / workspace owner / workspace tierMap / legacy owner / legacy tierMap / exhaustion -> None / stale-tier-for- delisted-user safety), file IO safety (load on missing, load on corrupt JSON, save_config atomic replace, auto_seed idempotency, auto_seed legacy-owner path, auto_seed allowFrom[0]-fallback WARN), and drift guards that both consumers import the helper AND call the right entry points. - tests/dm-result-send-dm.test.py (updated): `_with_access_json` now also isolates `discord_config.config_path()` so the resolver doesn't consult the dev's live workspace discord-config.json (which would short-circuit the legacy chain the tests pin). Verification ------------ - python3 -m py_compile on all three modified source files: OK. - New tests: 15 cases, all green. - Full Phase 5 regression sweep: 23 Python tests green (discord-bridge-{allowlist, file-markers, file-send-allowlist, pending-replies, prose-marker, reply-chunks, state-prefetch, access-tier, discord-state-detection, welcome-on-first-post, reply-directive, mod-judge, mod-judge-actions}, dm-result-{send-dm, upload}, discord-config, send-allowlist, bridge-marker-no-leak, result-markers, discord-chunker, single-instance, agent-api-result-archive, health-check-phase-57). - `pnpm typecheck` clean. Phase 5.10 of the OSS->private sync. Behavioral note: stando's workspace already contains a `discord-config.json` (OSS code seeded it during an earlier run); the helper picks it up immediately. For fresh installs, auto_seed_if_missing runs on the first bridge on_ready and seeds the file idempotently. 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
Phase 5.10 of the OSS→private sync. Closes the deferred half of #77 by funneling both owner-resolution sites (
dm-result.pyREST fallback anddiscord-bridge.pylive proactive loop) through one shared helper. Pre-Phase-5.10 both files inlined the same env → tierMap → bot-filter walk; that's the drift class @liususan091219 flagged on OSS PR sonichi#1029 and that bit production in sonichi#846.src/discord_config.py(new)src/dm-result.py:_resolve_owner_idsrc/discord-bridge.py:_poll_proactive+on_ready<workspace>/state/discord-config.jsonon first bootResolution chain (encoded in
discord_config.resolve_owner_id)$SUTANDO_DM_OWNER_IDenv var (operator escape hatch)discord-config.json[\"owner\"](workspace, Sutando-owned)discord-config.json[\"tierMap\"][uid] == \"owner\"(workspace)access.json[\"owner\"](legacy plugin-territory)access.json[\"tierMap\"][uid] == \"owner\"(legacy fix(discord-bridge): proactive DM honors tierMap/list order, not set iteration (privacy) sonichi/sutando#846 path)client.fetch_user(async, bridge) or_discord_api(\"GET\", /users/...)(sync, dm-result)Helper deliberately does NOT fall through to
allowFrom[0]— that's the sonichi#1147 bug path. Callers do that step paired with their own I/O-bound bot-filter walk.Minimal-divergence accounting
discord_config.py: byte-identical to OSS. Zero conflict surface against future OSS pulls.dm-result.py: 57 lines of stando-specific inline chain replaced with ~30 lines of delegate-to-helper code. Diff against upstream shrinks.discord-bridge.py: 42 lines of stando-specific inline chain replaced with ~30 lines of delegate code + 13 lines for the auto-seed boot call. Diff against upstream shrinks.Behavioral notes
discord-config.json(OSS code seeded it during an earlier run before stando ignored that file). The helper picks it up immediately on first run after merge — make sure theownerfield is set correctly.auto_seed_if_missingruns on the first bridgeon_readyand seeds the file idempotently fromaccess.json. If the seed falls through toallowFrom[0], a WARN line is emitted to stderr (per Lucy's discord-bridge: move Sutando-specific config (tierMap, owner) out of plugin-managed access.json into workspace/state/discord-config.json sonichi/sutando#1147 watch-point Merge/upstream main 2026 05 12 #2) so the operator catches a mis-seed.Pattern continuity
Same shape as Phase 5.9's
send_allowlist.pyextract — new module, byte-identical to OSS, two consumers refactored to delegate. This is the canonical "shrink-the-diff" minimal-divergence move.Test plan
New tests
tests/discord-config.test.py— 15 cases covering:Updated test
tests/dm-result-send-dm.test.py—_with_access_jsonnow also isolatesdiscord_config.config_path()so the resolver doesn't consult the dev's live workspacediscord-config.json. The 5 existing dm-result cases that pin tierMap behavior all pass against the new chain.Regression sweep (all green)
discord-bridge-{allowlist, file-markers, file-send-allowlist, pending-replies, prose-marker, reply-chunks, state-prefetch, access-tier, discord-state-detection, welcome-on-first-post, reply-directive, mod-judge, mod-judge-actions},dm-result-{send-dm, upload},discord-config,send-allowlist,bridge-marker-no-leak,result-markers,discord-chunker,single-instance,agent-api-result-archive,health-check-phase-57.pnpm typecheckclean.Made with Cursor