fix(managementapi): fail closed on bulk-worker kinds without an arm

Why

The Phase 4 plan's Approach requires each bulk worker to read the re-resolved repository's kind and fail closed on any kind it carries no arm for: a typed error that leaves the job to River's retry, never a completed no-op (docs/plans/2026-08-10-s17-phase4-artifact-writes.md, Approach). All three family workers shipped the opposite, test-pinned: a non-hosted-kind batch completed as a logged skip.

The hazard is cross-binary over the shared job queue. Once Phase 6 widens the bulk routes, a new pod can enqueue a remote batch during a rolling deploy, an old pod claims it and completes it empty, and the 202'd eviction is silently lost. With this conversion the old binary errors instead, and River retries until an armed binary claims the job, which is what lets the routes widen with no deploy-order hazard.

npm and maven are the two workers named in Bulk workers no-op on non-hosted kinds where th... (#723 - closed) • Hayley Swimelar • 19.4. The container worker shipped the same no-op arm, despite its plan step mandating the fail-closed read, so it takes the same conversion here.

What

Kind splits out of each worker's no-op narrowing: marked and wrong-format rows stay whole-batch no-ops (states no retry and no binary can serve), and a non-hosted kind returns a per-family retryable sentinel, never river.JobCancel. The kind read stays ahead of the child-row find, keeping the Find*RepositoryID caller contract (parent narrowed before the probe) intact.

Test plan

  • Flipped pinning subtests per worker: the non-hosted-kind cases (virtual and remote both) assert the typed error, no JobCancel, zero composer and store calls, and zero events. Marked and wrong-format subtests still assert no-ops.
  • Mutation-checked: restoring the container no-op arm fails the new test.
  • Gates all clean: go build ./..., go vet ./... plus go vet -tags=integration ./cmd/..., the internal/managementapi unit suite, and golangci-lint 2.12.2 with --build-tags=integration.

No e2e catalog scenario is added or affected: the fail-closed arm is unreachable until Phase 6 widens the bulk routes past hosted repositories, so no scenario can drive it.

Closes #723 (closed)

Merge request reports

Loading
Loading