feat: list and show tracker tasks that routing refused #202

Closed
ryangr0 wants to merge 3 commits from ryangr0/ploeg-route-refusals into development AGit
Owner

VIK-1632 Ploeg: list recent routing refusals on the operator API

https://vikunja.webgrip.dev/tasks/1632

  • New GET /api/v1/operator/route-refusals returns {schemaVersion, generatedAt, windowDays: 14, refusals}. It lists the newest refusal of each task, newest first, at most 50, within the consumer's teams. A task drops out when a later work_item.queued or work_item.refreshed audit exists for it, or when its refusal is older than 14 days. Any query parameter answers 400 invalid_request.
  • target.Refusal now carries a stable Code (label_missing, label_not_allowed, label_unregistered, multiple_labels, labels_unread, no_board_rule, target_not_ready) and Allowed, the repo/ labels of the matched board rule. Both are stored in the audit detail. Rows written before this change answer unclassified with allowedLabels: []. The tracker comment is unchanged, and a test pins its exact text.
  • Migration 0036_route_refusals.sql adds a partial index on audit_log (at) for this action.
  • Schema operator-api.v1 gains routeRefusalsResponse. The contracts README and route-a-multi-repo-board.md name the endpoint.

VIK-1633 Vloer: show tasks Ploeg could not start under Needs you

https://vikunja.webgrip.dev/tasks/1633

  • PloegClient.now reads route-refusals with added: true. A 404 from an older Ploeg becomes an empty list with no notice, and any other failure becomes errors.refused. Rows are filtered to the user's teams. The board name and the task link come from the task connection whose provider and project match (sourceTaskUrl). Without a match the link falls back to ploeg.trackerUrl, and without that there is no button.
  • Now shows a Could not start group first inside Needs you. Each code has its own plain sentence; unclassified shows Ploeg's reason. The primary button reads Open in Vikunja. A failed read shows an inline notice with Try again.
  • The demo has one illustrative refusal and calls nothing. The site demo replay is re-recorded in a separate chore(site) commit (ADR-0015).

Tests

  • Ploeg: new tests in pkg/target/refusal_test.go, pkg/store/route_refusals_test.go and pkg/httpapi/route_refusals_test.go. go test -count=1 ./... passed and go vet ./... reported nothing.
  • Vloer: new cases in test/now-view.test.mjs and test/ploeg.test.ts. npm test passed all 707 tests, and npm run check passed.
  • mise run verify passed every gate: vloer, demo-replay, vloer-extension, ploeg, brand, site, site-demo, helm, release, integration and docs.

Checks left to CI

None. Every gate ran locally.

🤖 Generated with Claude Code

## VIK-1632 Ploeg: list recent routing refusals on the operator API https://vikunja.webgrip.dev/tasks/1632 - New `GET /api/v1/operator/route-refusals` returns `{schemaVersion, generatedAt, windowDays: 14, refusals}`. It lists the newest refusal of each task, newest first, at most 50, within the consumer's teams. A task drops out when a later `work_item.queued` or `work_item.refreshed` audit exists for it, or when its refusal is older than 14 days. Any query parameter answers 400 `invalid_request`. - `target.Refusal` now carries a stable `Code` (label_missing, label_not_allowed, label_unregistered, multiple_labels, labels_unread, no_board_rule, target_not_ready) and `Allowed`, the repo/ labels of the matched board rule. Both are stored in the audit detail. Rows written before this change answer `unclassified` with `allowedLabels: []`. The tracker comment is unchanged, and a test pins its exact text. - Migration **0036_route_refusals.sql** adds a partial index on `audit_log (at)` for this action. - Schema `operator-api.v1` gains `routeRefusalsResponse`. The contracts README and `route-a-multi-repo-board.md` name the endpoint. ## VIK-1633 Vloer: show tasks Ploeg could not start under Needs you https://vikunja.webgrip.dev/tasks/1633 - `PloegClient.now` reads route-refusals with `added: true`. A 404 from an older Ploeg becomes an empty list with no notice, and any other failure becomes `errors.refused`. Rows are filtered to the user's teams. The board name and the task link come from the task connection whose provider and project match (`sourceTaskUrl`). Without a match the link falls back to `ploeg.trackerUrl`, and without that there is no button. - Now shows a **Could not start** group first inside Needs you. Each code has its own plain sentence; `unclassified` shows Ploeg's reason. The primary button reads **Open in Vikunja**. A failed read shows an inline notice with Try again. - The demo has one illustrative refusal and calls nothing. The site demo replay is re-recorded in a separate `chore(site)` commit (ADR-0015). ## Tests - Ploeg: new tests in `pkg/target/refusal_test.go`, `pkg/store/route_refusals_test.go` and `pkg/httpapi/route_refusals_test.go`. `go test -count=1 ./...` passed and `go vet ./...` reported nothing. - Vloer: new cases in `test/now-view.test.mjs` and `test/ploeg.test.ts`. `npm test` passed all 707 tests, and `npm run check` passed. - `mise run verify` passed every gate: vloer, demo-replay, vloer-extension, ploeg, brand, site, site-demo, helm, release, integration and docs. ## Checks left to CI None. Every gate ran locally. 🤖 Generated with [Claude Code](https://claude.com/claude-code)
GET /api/v1/operator/route-refusals returns the tracker tasks that
ADR-0038 routing refused in the last 14 days: the newest refusal per
task, newest first, at most 50, within the consumer's teams. A task
queued or refreshed after its newest refusal drops out.

Every refusal now carries a stable code (label_missing,
label_not_allowed, label_unregistered, multiple_labels, labels_unread,
no_board_rule, target_not_ready) and the repo/ labels the board allows;
both are stored in the audit detail. Rows written before this answer
code "unclassified" with no allowed labels. The tracker comment is
unchanged. Migration 0036 adds a partial index for the read.

VIK-1632
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Now reads Ploeg's route-refusals list and shows a "Could not start"
group first inside Needs you. Each row names the tracker task, says in
plain words why routing refused it, with the repo/ labels its board
allows, and opens the task in its tracker. Vloer builds the link from
the task connection whose provider and project match, else the
configured tracker root, else offers no button.

A failed read shows an inline notice with Try again and leaves the other
sections alone; an older Ploeg that answers 404 shows nothing. The demo
shows one illustrative refusal and calls nothing.

VIK-1633
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
chore(site): re-record the demo replay with the Could not start group
Some checks failed
[Workflow] On Pull Request / release-policy (pull_request) Waiting to run
[Workflow] On Pull Request / checks (pull_request) Has been cancelled
[Workflow] On Pull Request / warnings (pull_request) Has been cancelled
ee5446c894
VIK-1633
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
ryangr0 closed this pull request 2026-10-04 08:41:43 +00:00
Some checks failed
[Workflow] On Pull Request / release-policy (pull_request) Waiting to run
[Workflow] On Pull Request / checks (pull_request) Has been cancelled
[Workflow] On Pull Request / warnings (pull_request) Has been cancelled

Pull request closed

Sign in to join this conversation.
No reviewers
No labels
No milestone
No project
No assignees
1 participant
Notifications
Due date
The due date is invalid or out of range. Please use the format "yyyy-mm-dd".

No due date set.

Dependencies

No dependencies set.

Reference
webgrip/unfold!202
No description provided.