Double dispatch on forgejo issues.labeled — yaml forgejo-issues.labeled and sqlite triage-approved-to-kanban both fire the ingress wrapper #39

Closed
opened 2026-08-21 23:01:22 +00:00 by Mark · 3 comments
Owner

Summary

Two automation-registry triggers match the same event (forgejo / issues.labeled) and both dispatch forgejo-kanban-ingress-wrapper.sh per label event. The idempotency key prevents duplicate kanban cards, but every triage-approved labeled event still burns 2 worker runs (recorded as separate daemon runs, e.g. 45beaf6b + 87fe1fa3 at 22:53:50 on 2026-08-21).

Pre-existing — documented pitfall, not a regression from the #29 fix.

Reproduction

  1. ssh unstable "python3 -c \"import sqlite3; db = sqlite3.connect('/home/b/.hermes/org-subscriber-automation.db'); [print(r) for r in db.execute(\"\\\"SELECT name, event_type, script, active, origin, match_conditions FROM automation_triggers WHERE event_type='issues.labeled'\"\\\")]\"" →
    • forgejo-issues.labeled | issues.labeled | forgejo-kanban-ingress-wrapper.sh | active=1 | origin=yaml | match_conditions=NULL
    • triage-approved-to-kanban | issues.labeled | forgejo-kanban-ingress-wrapper.sh | active=1 | origin=sqlite | match_conditions={"payload.label.name": "triage-approved"}
  2. Apply triage-approved label to any issue → both triggers match → both dispatch the wrapper.
  3. Confirm in daemon run ledger: two wrapper runs per event (idempotency-key keeps kanban card count at 1).

Actual behavior

  • 2 worker dispatches per issues.labeled event; each burns a slot against max_parallel and writes a duplicated run-ledger entry. Label events for non-triage-approved labels also trigger the yaml trigger (no match_conditions) — only the sqlite one narrows to triage-approved.

Expected behavior

  • Exactly one trigger fires the ingress wrapper per event. Options: (a) deactivate/remove the yaml forgejo-issues.labeled trigger (sqlite one already carries the triage-approved match_condition and the wrapper itself is idempotent), or (b) add the same match_condition to the yaml trigger and drop the sqlite duplicate. Keep exactly one.

Kanban origin: card t_99b7d377 (post-deploy probe of Mark/org_mode#29).

## Summary Two automation-registry triggers match the same event (forgejo / issues.labeled) and both dispatch `forgejo-kanban-ingress-wrapper.sh` per label event. The idempotency key prevents duplicate kanban cards, but every triage-approved labeled event still burns **2 worker runs** (recorded as separate daemon runs, e.g. 45beaf6b + 87fe1fa3 at 22:53:50 on 2026-08-21). **Pre-existing** — documented pitfall, not a regression from the #29 fix. ## Reproduction 1. `ssh unstable "python3 -c \"import sqlite3; db = sqlite3.connect('/home/b/.hermes/org-subscriber-automation.db'); [print(r) for r in db.execute(\"\\\"SELECT name, event_type, script, active, origin, match_conditions FROM automation_triggers WHERE event_type='issues.labeled'\"\\\")]\""` → - `forgejo-issues.labeled` | issues.labeled | forgejo-kanban-ingress-wrapper.sh | active=1 | origin=yaml | match_conditions=NULL - `triage-approved-to-kanban` | issues.labeled | forgejo-kanban-ingress-wrapper.sh | active=1 | origin=sqlite | match_conditions={"payload.label.name": "triage-approved"} 2. Apply `triage-approved` label to any issue → both triggers match → both dispatch the wrapper. 3. Confirm in daemon run ledger: two wrapper runs per event (idempotency-key keeps kanban card count at 1). ## Actual behavior - 2 worker dispatches per issues.labeled event; each burns a slot against max_parallel and writes a duplicated run-ledger entry. Label events for non-triage-approved labels also trigger the yaml trigger (no match_conditions) — only the sqlite one narrows to triage-approved. ## Expected behavior - Exactly one trigger fires the ingress wrapper per event. Options: (a) deactivate/remove the yaml `forgejo-issues.labeled` trigger (sqlite one already carries the triage-approved match_condition and the wrapper itself is idempotent), or (b) add the same match_condition to the yaml trigger and drop the sqlite duplicate. Keep exactly one. Kanban origin: card t_99b7d377 (post-deploy probe of Mark/org_mode#29).
Author
Owner

Pre-dispatch gate BLOCKED the kilo-gsd.py launcher for this issue — the Mark/org_mode worktree at /home/b/Projects/org_mode has uncommitted changes outside .gsd/.venv/ (M AGENTS.md, M docs/kanban-design.md).

Diagnosis:

  • git stash list shows stash@{0}: preserve unrelated documentation changes for pre-dispatch gate — operator-intentional state held aside to dodge pre-dispatch.
  • The current worktree-dirty docs/kanban-design.md overlaps with stash@{0} content (kanban-design.md is in both). AGENTS.md carries the substrate-authority block from the #37 kilo-gsd commit with follow-on edits.
  • No live tmux/pid for #39, no other .gsd/in-progress run on the repo, no in-progress label on this issue — only the worktree dirt is blocking.

Resolution options for the operator:

  1. git stash pop stash@{0} and resolve the conflicts (commit the doc changes if they belong, or discard if they do not).
  2. git stash the worktree dirt to rejoin it with stash@{0}, then drop both.
  3. git checkout -- AGENTS.md docs/kanban-design.md to discard the worktree changes (keeps stash@{0} intact).

Once the worktree is clean, re-dispatch the kilo-gsd card and the gate will pass. The launcher profile will not run git reset / git clean / git stash itself; substrate authority over git state belongs to the operator.

Pre-dispatch gate BLOCKED the kilo-gsd.py launcher for this issue — the Mark/org_mode worktree at /home/b/Projects/org_mode has uncommitted changes outside .gsd/.venv/ (M AGENTS.md, M docs/kanban-design.md). Diagnosis: - `git stash list` shows `stash@{0}: preserve unrelated documentation changes for pre-dispatch gate` — operator-intentional state held aside to dodge pre-dispatch. - The current worktree-dirty `docs/kanban-design.md` overlaps with stash@{0} content (kanban-design.md is in both). `AGENTS.md` carries the substrate-authority block from the #37 kilo-gsd commit with follow-on edits. - No live tmux/pid for #39, no other .gsd/in-progress run on the repo, no `in-progress` label on this issue — only the worktree dirt is blocking. Resolution options for the operator: 1. `git stash pop stash@{0}` and resolve the conflicts (commit the doc changes if they belong, or discard if they do not). 2. `git stash` the worktree dirt to rejoin it with stash@{0}, then drop both. 3. `git checkout -- AGENTS.md docs/kanban-design.md` to discard the worktree changes (keeps stash@{0} intact). Once the worktree is clean, re-dispatch the kilo-gsd card and the gate will pass. The launcher profile will not run `git reset` / `git clean` / `git stash` itself; substrate authority over git state belongs to the operator.
Author
Owner

Verification — fix is in effect on unstable.tail018ac4.ts.net.

The bug (double-dispatch of forgejo-kanban-ingress-wrapper.sh on issues.labeled) was an operator-action / config-state fix, not a code commit — the fix lives in the org-subscriber-daemon's automation registry on the unstable host (/home/b/.hermes/org-subscriber-automation.db), not in this repo's git history. git log origin/main --grep='#39' returns no commit referencing #39 (as expected).

Verified state on unstable (queried via ssh, 2026-09-18):

forgejo-issues.labeled       issues.labeled  forgejo-kanban-ingress-wrapper.sh  active=0  origin=yaml   ← deactivated
quickfix-on-label            issues.labeled  quickfix-runner                    active=1  origin=sqlite  (different script, not a duplicate)
triage-approved-to-kanban    issues.labeled  forgejo-kanban-ingress-wrapper.sh  active=1  origin=sqlite  (correct narrowing: payload.label.name == triage-approved)

Exactly one active trigger (triage-approved-to-kanban) dispatches the wrapper, with the correct match_conditions narrowing.

Pre-fix vs post-fix double-dispatch signature in org-subscriber-ledger.db:

  • Pre-fix paired runs have 2-7ms deltas (e.g. e6aae87f+1e45cb67 Δ=3ms at 2026-09-15T16:11:23) — the bug.
  • Post-fix (≥ 2026-09-15T16:13 UTC): 288 wrapper runs across 189 distinct seconds; zero sub-10ms paired runs. Where multiple runs land in the same second, deltas are 100-450ms — clearly separate events, not double-dispatch.

Test event from the original fix (2026-09-15T16:12:55 UTC) confirmed: run ef862d37-63d5-4e75-8654-380156f5b518 exists in the ledger, created kanban card t_6c8000a0 exactly once via idempotency key.

Verdict: this fix is operator-action and verifier is leaving the issue open per the verify task's operator-action branch (Close the Forgejo issue only after operator confirms deployment (or leave open + comment)). Operator: confirm whether to close this issue — the deactivation has held since 2026-09-15 16:13 UTC with no regression.

— verifier card t_72d1bccd

**Verification — fix is in effect on `unstable.tail018ac4.ts.net`.** The bug (double-dispatch of `forgejo-kanban-ingress-wrapper.sh` on `issues.labeled`) was an **operator-action / config-state fix**, not a code commit — the fix lives in the org-subscriber-daemon's automation registry on the `unstable` host (`/home/b/.hermes/org-subscriber-automation.db`), not in this repo's git history. `git log origin/main --grep='#39'` returns no commit referencing #39 (as expected). Verified state on `unstable` (queried via ssh, 2026-09-18): ``` forgejo-issues.labeled issues.labeled forgejo-kanban-ingress-wrapper.sh active=0 origin=yaml ← deactivated quickfix-on-label issues.labeled quickfix-runner active=1 origin=sqlite (different script, not a duplicate) triage-approved-to-kanban issues.labeled forgejo-kanban-ingress-wrapper.sh active=1 origin=sqlite (correct narrowing: payload.label.name == triage-approved) ``` Exactly **one** active trigger (`triage-approved-to-kanban`) dispatches the wrapper, with the correct `match_conditions` narrowing. **Pre-fix vs post-fix double-dispatch signature in `org-subscriber-ledger.db`:** - Pre-fix paired runs have 2-7ms deltas (e.g. e6aae87f+1e45cb67 Δ=3ms at 2026-09-15T16:11:23) — the bug. - Post-fix (≥ 2026-09-15T16:13 UTC): 288 wrapper runs across 189 distinct seconds; **zero sub-10ms paired runs.** Where multiple runs land in the same second, deltas are 100-450ms — clearly separate events, not double-dispatch. **Test event from the original fix (2026-09-15T16:12:55 UTC) confirmed**: run `ef862d37-63d5-4e75-8654-380156f5b518` exists in the ledger, created kanban card `t_6c8000a0` exactly once via idempotency key. **Verdict**: this fix is operator-action and verifier is leaving the issue open per the verify task's operator-action branch (`Close the Forgejo issue only after operator confirms deployment (or leave open + comment)`). Operator: confirm whether to close this issue — the deactivation has held since 2026-09-15 16:13 UTC with no regression. — verifier card t_72d1bccd
Author
Owner

kilo-gsd-launcher pre-dispatch blocked on dirty worktree (unrelated to #39).

bash /home/b/Projects/org_mode/.gsd/gates/pre-dispatch.sh 39 exits 1 with three untracked operator drafts that predate this issue by 10+ days:

  • docs/operational/2026-09-11-ops-review.md (Sep 11, 229 lines, bblhd ops review)
  • docs/triage-aware-runner-proposal.md (Sep 8, 141 lines, draft proposal)
  • workers/lib/hermes-gateway-log-scan.py (Sep 15, 202 lines, hermes-config#22 helper)

None of these are related to the double-dispatch bug — they are operator-authored drafts that landed in the working tree but never got committed. Substrate-fix fork: sibling fixit card t_7758d138 (kilo-gsd-runner) carries the recipe (commit OR move to .gsd/scratch/ OR rm). Once it lands, the #39 dispatch will auto-promote via parent linkage.

Re-launching the runner today would just hit the same gate failure. Tracking: t_cea132d5 (parent), t_7758d138 (cleanup child).

kilo-gsd-launcher pre-dispatch blocked on dirty worktree (unrelated to #39). `bash /home/b/Projects/org_mode/.gsd/gates/pre-dispatch.sh 39` exits 1 with three untracked operator drafts that predate this issue by 10+ days: - docs/operational/2026-09-11-ops-review.md (Sep 11, 229 lines, bblhd ops review) - docs/triage-aware-runner-proposal.md (Sep 8, 141 lines, draft proposal) - workers/lib/hermes-gateway-log-scan.py (Sep 15, 202 lines, hermes-config#22 helper) None of these are related to the double-dispatch bug — they are operator-authored drafts that landed in the working tree but never got committed. Substrate-fix fork: sibling fixit card **t_7758d138** (kilo-gsd-runner) carries the recipe (commit OR move to `.gsd/scratch/` OR `rm`). Once it lands, the #39 dispatch will auto-promote via parent linkage. Re-launching the runner today would just hit the same gate failure. Tracking: t_cea132d5 (parent), t_7758d138 (cleanup child).
Mark closed this issue 2026-09-25 09:09:32 +00:00
Sign in to join this conversation.
No description provided.