Allocator MCP lacks fail-closed pre-rank exclusions and coerces structured candidates_json incorrectly #776

Closed
opened 2026-07-20 21:35:15 -05:00 by jcwalker3 · 0 comments
Owner

Summary

Grok canary evidence (job 019f81f73d3c, now deleted) showed the controller-owned allocator cannot fail-closed exclude protected issues before ranking, and native MCP candidates_json transport is broken by string/list coercion. Fire 1 incorrectly leased protected Issue #617; Fire 2 released that lease without evidence mutation; Fire 4 proved service-layer filtering works only when #617 is removed from the candidate list entirely. PR #775 / Issue #617 are terminal; this tracks the durable allocator/MCP fix.

Parent / Depends / Blocks

Acceptance criteria

AC1 — Native exclusions

Expose a typed exclude_issue_numbers list on the canonical allocation MCP surface (gitea_allocate_next_work). Calls that omit it retain existing behavior.

AC2 — Pre-rank enforcement

Remove excluded issue numbers before ranking, selection, assignment, or lease creation. A highest-ranked excluded issue must never receive a lease.

AC3 — Structured transport

Normalize candidates_json correctly when received as:

  • an already-decoded list/object from MCP transport;
  • a valid JSON string for backward compatibility.

Reject malformed JSON, scalars, invalid candidate records, booleans-as-integers, and unsupported types with structured fail-closed errors.

AC4 — Dry-run/apply parity

Dry-run and apply must use the same normalized exclusions and candidate semantics. Return a stable candidate-set fingerprint (or equivalent CAS proof); apply must reject material selection/ownership drift instead of silently leasing a different candidate.

AC5 — Live ownership

Immediately before apply, re-evaluate live leases/claims. Never override a foreign owner. If an excluded issue already has a live same-owner lease, return a structured resume/release blocker rather than silently stranding it.

AC6 — Accounting

Return excluded candidates in structured skipped accounting with a stable reason such as excluded_by_controller. Distinguish exclusions from foreign claims, invalid candidates, and task-local blockers.

AC7 — All excluded

If every otherwise-eligible candidate is excluded, return the canonical WAIT/no-candidate result and create no assignment or lease.

AC8 — Public MCP regression

Through the public MCP allocation tool, prove a candidate list containing #617 cannot select or lease #617 when exclude_issue_numbers=[617].

AC9 — Compatibility

Existing allocation calls without exclusions, foreign-lease handling, same-owner resume, dashboard projections, and persisted controller ownership must remain compatible.

Non-goals

Canary evidence (context)

  • Canary job 019f81f73d3c deleted
  • Fire 1 leased protected #617 incorrectly
  • Fire 2 released lease without touching evidence
  • Fire 4: filtering works when #617 removed from candidates
  • No native exclude_issue_numbers input
  • Native MCP candidates_json string/list coercion broken
## Summary Grok canary evidence (job 019f81f73d3c, now deleted) showed the controller-owned allocator cannot fail-closed exclude protected issues before ranking, and native MCP `candidates_json` transport is broken by string/list coercion. Fire 1 incorrectly leased protected Issue #617; Fire 2 released that lease without evidence mutation; Fire 4 proved service-layer filtering works only when #617 is removed from the candidate list entirely. PR #775 / Issue #617 are terminal; this tracks the durable allocator/MCP fix. ## Parent / Depends / Blocks * Parent: #600 · Depends: #765, #758 · Blocks: none ## Acceptance criteria ### AC1 — Native exclusions Expose a typed `exclude_issue_numbers` list on the canonical allocation MCP surface (`gitea_allocate_next_work`). Calls that omit it retain existing behavior. ### AC2 — Pre-rank enforcement Remove excluded issue numbers **before** ranking, selection, assignment, or lease creation. A highest-ranked excluded issue must never receive a lease. ### AC3 — Structured transport Normalize `candidates_json` correctly when received as: - an already-decoded list/object from MCP transport; - a valid JSON string for backward compatibility. Reject malformed JSON, scalars, invalid candidate records, booleans-as-integers, and unsupported types with structured fail-closed errors. ### AC4 — Dry-run/apply parity Dry-run and apply must use the same normalized exclusions and candidate semantics. Return a stable candidate-set fingerprint (or equivalent CAS proof); apply must reject material selection/ownership drift instead of silently leasing a different candidate. ### AC5 — Live ownership Immediately before apply, re-evaluate live leases/claims. Never override a foreign owner. If an excluded issue already has a live same-owner lease, return a structured resume/release blocker rather than silently stranding it. ### AC6 — Accounting Return excluded candidates in structured skipped accounting with a stable reason such as `excluded_by_controller`. Distinguish exclusions from foreign claims, invalid candidates, and task-local blockers. ### AC7 — All excluded If every otherwise-eligible candidate is excluded, return the canonical WAIT/no-candidate result and create no assignment or lease. ### AC8 — Public MCP regression Through the public MCP allocation tool, prove a candidate list containing #617 cannot select or lease #617 when `exclude_issue_numbers=[617]`. ### AC9 — Compatibility Existing allocation calls without exclusions, foreign-lease handling, same-owner resume, dashboard projections, and persisted controller ownership must remain compatible. ## Non-goals - Re-opening or re-working terminal #617 / PR #775 - Changing dashboard product UI beyond allocator accounting compatibility - Controller canary reschedule (reviewer/merger/controller after author handoff only) ## Canary evidence (context) - Canary job 019f81f73d3c deleted - Fire 1 leased protected #617 incorrectly - Fire 2 released lease without touching evidence - Fire 4: filtering works when #617 removed from candidates - No native `exclude_issue_numbers` input - Native MCP `candidates_json` string/list coercion broken
jcwalker3 added status:pr-open and removed status:in-progress labels 2026-07-20 21:41:44 -05:00
sysadmin removed the status:pr-open label 2026-07-20 22:40:15 -05:00
Sign in to join this conversation.
1 Participants
Notifications
Due Date
No due date set.
Dependencies

No dependencies set.

Reference: Scaled-Tech-Consulting/Gitea-Tools#776