bug: vikunja-webhook-to-kanban idempotency collapse misses 2/3 ingress shapes #37

Closed
opened 2026-08-21 22:31:25 +00:00 by Mark · 0 comments
Owner

Source: Vikunja task #118 (https://vikunja.tail018ac4.ts.net/tasks/118) / PM round-3 analysis, kanban t_dc01ca35 (2026-08-21).

The bridge from Vikunja to the tasker kanban produces duplicate cards for the same source task. Observed live for Vikunja tasks #114/#115/#116 (and prior #112): each source task now has 2 ready cards on the tasker board (e.g. t_b7f7b115 rich + t_536c04a4 shell). The dispatcher claims both; the loser wastes a kilo-gsd or tasker-worker cycle.

Root cause: workers/vikunja-webhook-to-kanban.sh (lines ~145-169, match at line 163 if "/tasks/" + tid in body) only matches cards whose footer carries source: https://vikunja.tail018ac4.ts.net/tasks/<id> — the wrapper's own output shape. The same Vikunja task can be ingested by THREE ingress paths:

  1. Wrapper (footer source: <url> + project: <p>) — /tasks/<id> substring matches. OK.
  2. Decomposer (footer source: <id> bare integer, no URL) — substring fails. BUG.
  3. Manual hermes kanban create from a Vikunja body dump (carries <!-- vikunja-task-id: <id> --> HTML marker at body end) — substring fails. BUG.

Bounded acceptance:

  1. Pre-create collapse accepts ALL three body shapes and refreshes the existing card (comment + unblock + promote) instead of creating a duplicate. Match returns truthy if ANY of: (a) /tasks/<id> substring present (wrapper footer), OR (b) <!-- vikunja-task-id: <id> --> HTML comment marker (manual ingress), OR (c) body contains **Source:** Vikunja task #<id>. Tie-break multiple matches: prefer card in ready/running/review status over done; most-recently-updated among same-status.
  2. Add unit test in tests/test_vikunja_webhook_to_kanban.py mocking the SSH kanban list JSON for each of the three body shapes, asserting the match function returns the expected card id.
  3. Live e2e: create a new Vikunja task, fire its webhook, then create a SECOND card manually with the same Vikunja body, then update the Vikunja task again — verify the wrapper refreshes the EXISTING card (comment + unblock + promote) rather than creating a third. Document in the PR.
  4. Open PR against Mark/org_mode; PR description links to this Vikunja task and restates the acceptance criteria.
**Source:** Vikunja task #118 (https://vikunja.tail018ac4.ts.net/tasks/118) / PM round-3 analysis, kanban t_dc01ca35 (2026-08-21). The bridge from Vikunja to the tasker kanban produces duplicate cards for the same source task. Observed live for Vikunja tasks #114/#115/#116 (and prior #112): each source task now has 2 ready cards on the tasker board (e.g. t_b7f7b115 rich + t_536c04a4 shell). The dispatcher claims both; the loser wastes a kilo-gsd or tasker-worker cycle. **Root cause:** workers/vikunja-webhook-to-kanban.sh (lines ~145-169, match at line 163 `if "/tasks/" + tid in body`) only matches cards whose footer carries `source: https://vikunja.tail018ac4.ts.net/tasks/<id>` — the wrapper's own output shape. The same Vikunja task can be ingested by THREE ingress paths: 1. Wrapper (footer `source: <url>` + `project: <p>`) — `/tasks/<id>` substring matches. OK. 2. Decomposer (footer `source: <id>` bare integer, no URL) — substring fails. BUG. 3. Manual `hermes kanban create` from a Vikunja body dump (carries `<!-- vikunja-task-id: <id> -->` HTML marker at body end) — substring fails. BUG. **Bounded acceptance:** 1. Pre-create collapse accepts ALL three body shapes and refreshes the existing card (comment + unblock + promote) instead of creating a duplicate. Match returns truthy if ANY of: (a) `/tasks/<id>` substring present (wrapper footer), OR (b) `<!-- vikunja-task-id: <id> -->` HTML comment marker (manual ingress), OR (c) body contains `**Source:** Vikunja task #<id>`. Tie-break multiple matches: prefer card in ready/running/review status over done; most-recently-updated among same-status. 2. Add unit test in tests/test_vikunja_webhook_to_kanban.py mocking the SSH `kanban list` JSON for each of the three body shapes, asserting the match function returns the expected card id. 3. Live e2e: create a new Vikunja task, fire its webhook, then create a SECOND card manually with the same Vikunja body, then update the Vikunja task again — verify the wrapper refreshes the EXISTING card (comment + unblock + promote) rather than creating a third. Document in the PR. 4. Open PR against Mark/org_mode; PR description links to this Vikunja task and restates the acceptance criteria.
Mark 2026-08-21 22:31:25 +00:00
  • closed this issue
  • added the
    bug
    infra
    labels
Sign in to join this conversation.
No description provided.