# Bug: stale merged proposals incorrectly linked to unrelated issues ## Observed Issue #111 ("MWP-8: bust fetch-mpack cache on repair and force-push") shows a linked proposal it has no relationship to: - **Proposal:** `sha256:e83b2606f750a38f5064d2adb51473e2d6afa4b936e328aa9924f61e01b2d873` - **Proposal title:** "Asyncpg Caps Query Parameters" - **Proposal state:** merged - **Proposal branch:** `feat/adding-debug-logs-to-staging` - **Proposal created:** 2026-06-11 (≈ 20 days before this was noticed) - **Issue #111 created:** 2026-06-26 The proposal is about asyncpg query-parameter capping during a debug-logging session. Issue #111 is about cache invalidation in `musehub_fetch_mpack_cache` after repair endpoints and force-push. There is no shared vocabulary in their titles, bodies, or branch names. ## Why this is a bug A merged proposal that predates an issue by two weeks should never be auto-linked to that issue. Specifically: 1. **Already merged.** Merged proposals are closed artifacts — they should not be retroactively associated with issues filed after they merged. 2. **No explicit reference.** The issue body contains no mention of the proposal ID, the branch name `feat/adding-debug-logs-to-staging`, or any asyncpg-related keyword. The proposal body does not reference `#111`. 3. **Wrong direction.** If the linking heuristic is symbol-overlap (both touched musehub service/DB files), that signal is too weak to establish a link — nearly every musehub change touches the same small set of service files. ## Hypothesised cause The most likely explanation is a symbol-anchor or file-overlap heuristic that fires when: - An issue is created or updated with `symbolAnchors` pointing to files that were also changed in some proposal's diff, **and** - The heuristic does not filter out already-merged proposals. Issue #111's `symbolAnchors` include: ``` musehub/services/musehub_gc.py::gc_fetch_mpack_cache musehub/services/musehub_wire_push.py::wire_repair_commit musehub/services/musehub_wire_push.py::wire_push_unpack_mpack musehub/services/musehub_wire_fetch.py::wire_fetch_mpack musehub/db/musehub_repo_models.py::MusehubFetchMPackCache ``` The asyncpg proposal (`feat/adding-debug-logs-to-staging`) likely touched one or more of these files (asyncpg connection setup touches the DB layer). If the overlap threshold is "any file in common," false positives are guaranteed on a small codebase where every change goes through the same service layer. A secondary hypothesis: the link was written at issue-update time (when the issue body was last pushed), not at issue-creation time, and the heuristic rescans all open+merged proposals rather than only open ones. ## Expected behaviour A proposal should be linked to an issue only when at least one of the following holds: - The proposal body or commit message contains an explicit `#` or `issue/` reference. - A user or agent explicitly calls a "link proposal → issue" API. - The proposal was opened *after* the issue was filed and shares a branch that descends from the issue's anchor commit. Merged proposals should be excluded from auto-linking entirely unless there is an explicit reference — their diff is historical and the relationship would need to be intentional. ## Reproduction steps 1. Open https://staging.musehub.ai/gabriel/musehub/issues/111 in a browser. 2. Observe the linked proposal `sha256:e83b2606...` ("Asyncpg Caps Query Parameters"). 3. Open the proposal — note it is merged, predates the issue, and shares no explicit reference with #111. ## Impact - Incorrect UI signal: developers following the proposal link expect to find related work and instead find an unrelated merged PR. - Trust erosion: auto-linking that fires on weak signals makes the feature indistinguishable from noise. - Potential data integrity: if linked proposals gate any workflow (e.g., issue close requires linked proposal to be merged), false links could block or prematurely unblock issue state transitions. ## Suggested fix surface `musehub/api/routes/` or `musehub/services/` — wherever proposal→issue association is written. Look for: - The write path that creates a `proposal_issue_link` (or equivalent) row. - Whether it filters `state == "open"` proposals before matching. - The overlap threshold used for symbol/file heuristics. A conservative fix: only link a proposal to an issue when the proposal body or any of its commit messages contain an explicit `#` reference that resolves to the issue number in the same repo.