Skip to content

fix(quinn/bug_triage): handler is non-idempotent — re-triages and creates duplicate board features on every workstacean restart #3503

@mabry1985

Description

@mabry1985

Symptom

Every workstacean container restart triggers Quinn to re-triage every open issue with the `status: needs-triage` label, posting a duplicate "Triage — Quinn (QA)" comment and creating a NEW board feature each time. Same issue gets re-filed under multiple `feature-...-...` IDs.

Evidence (as of 2026-04-20 03:00 UTC):

Issue Quinn comments Board features created
protoMaker#3482 6 5+ separate `feature-...` IDs (issue is now CLOSED, label still present)
protoMaker#3483 5 5+
protoMaker#3484 6 6+

Bursts cluster at 02:10, 02:13, 02:47, 02:55 UTC — each maps 1:1 to a workstacean container recreate during today's audit work in protoWorkstacean.

Root cause

Two cooperating bugs:

1. `bug_triage` is not idempotent. The protoWorkstacean github plugin (`lib/plugins/github.ts:665-693` in `protoLabsAI/protoWorkstacean`) explicitly documents the dedup contract:

"Walk every monitored repo's open issues and re-dispatch any with the sweep label as synthetic inbound events. Runs on install and on the `github.triage.sweep` bus topic. Idempotent on the Ava side — the bug_triage skill checks for existing board features by issue number."

The data above proves Quinn isn't honoring that contract — she creates a new board feature on every dispatch instead of looking up by URL/number and no-oping.

2. Quinn doesn't transition the label after triaging. Every issue Quinn has triaged still carries `status: needs-triage`. So on the next workstacean restart, `_runTriageSweep` correctly identifies it as un-triaged and re-fires.

Neither bug alone causes the spam, but together they create a guaranteed "every restart = N duplicate triages" loop where N = open needs-triage issue count.

Reproduction

  1. File a GitHub issue with the `status: needs-triage` label
  2. Wait for Quinn to triage (one comment, one board feature created — correct)
  3. `docker compose restart workstacean` (or any deploy that recreates the container)
  4. Observe Quinn fire a SECOND comment + create a SECOND board feature on the same issue
  5. Repeat — each restart adds another comment and board feature

Suggested fix (in apps/server, ~/dev/ava)

Two changes to Quinn's `bug_triage` handler — both in the protoMaker server. Pick the order based on what's easier to implement first; ideally both ship.

Primary: dedup at the handler. Before posting a triage comment / creating a board feature, query the board for an existing feature whose source URL == the incoming issue URL (or whose linked GitHub issue number matches). If found:

  • No new feature
  • No new comment, OR a single dedup-guarded comment like `"Already triaged — see feature {id}. No new action."` (note: gating that comment with a 24-hour cooldown prevents this from becoming its own form of spam)

Secondary: label transition. After successful triage, remove `status: needs-triage` and add `status: triaged` (or whatever board-linked label makes sense). This makes the label state machine truthful and means `_runTriageSweep` naturally skips already-handled issues. Also: when an issue closes (manual or via PR), the label should also be cleared if still present.

Why this matters

Quinn's triage is genuinely valuable — it converts user-filed bug reports into board features and routes them to the right project. The bug isn't "Quinn shouldn't triage"; it's "Quinn should triage exactly once per issue, then know it's done." Without this fix, every deploy is a noise event and the protoMaker board accumulates duplicate work items that look like a real backlog but aren't.

Companion cleanup (already in flight in protoWorkstacean)

A separate PR removes `subscribesTo: message.inbound.github.#` from the new `protomaker` entry in protoWorkstacean's `workspace/agents.yaml` (added in protoWorkstacean#433). That subscription was overscoped — protoMaker is an action target reached via explicit `targets: [protomaker]` GOAP dispatches, it shouldn't also broadcast-subscribe to all GitHub inbound events. Removing it doesn't fix the duplication root cause (which is in Quinn's handler) but removes one extra path that could amplify the issue under future config changes.

Metadata

Metadata

Assignees

No one assigned

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions