VantagePeers Docs

bulk_complete_tasks

Bulk-close cron-spam tasks safely with dry-run default and RBAC gate.

bulk_complete_tasks

Bulk-close tasks that match a filter in one atomic mutation. Introduced in PR-F (merged commit 4c068d2 after Eta REVISE round addressing blast-radius / scope / caller-gate hardening). Designed to safely drain cron-spam backlogs accumulated from auto-generated check-messages polling tasks.

dryRun defaults to true. The tool never mutates the database unless you explicitly pass dryRun: false. Always preview first to confirm the count, then call again with dryRun: false to commit. Closed tasks are irreversible — status is permanently set to done.

Safety contract (iter-2 hardening)

The mutation enforces three guardrails before any write:

GuardrailThrowsWhen
Reductive filter requiredBULK_FILTER_TOO_BROADNeither filter.autoGeneratedOnly: true nor filter.assignedTo set — match-all is forbidden.
Caller required for live commitBULK_CALLER_REQUIREDdryRun: false with no callerOrchestrator — default-deny on the destructive path.
Blast-radius capBULK_HARD_CAP_EXCEEDEDMatched count exceeds BULK_COMPLETE_HARD_CAP = 500 — narrow the filter and retry.

Backing implementation uses a withIndex("by_status") iterator with early-stop at cap+1 to count without scanning the full table.

Args

ArgTypeDefaultDescription
filterobject(required)Filter object controlling which tasks are matched. MUST contain at least one reductive predicate (autoGeneratedOnly: true OR assignedTo: "<role>") — else throws BULK_FILTER_TOO_BROAD.
filter.autoGeneratedOnlybooleanfalseWhen true, matches tasks where createdBy matches /^cron-/i OR title matches /^\/?check-messages$/i.
filter.assignedTostringWhen set, narrows matches to tasks whose assignedTo equals this role. Combined with autoGeneratedOnly via AND.
dryRunbooleantrueSafety default. true returns a preview without mutating. Pass false explicitly to commit (requires callerOrchestrator).
completionNoteTemplatestring(see below)Template string written as completionNote on each closed task. Supports {{day}}, {{bulkRunId}}, {{executedAt}} interpolation. Default: "bulk-cleanup: cron-spam day {{day}} runId={{bulkRunId}} executedAt={{executedAt}}".
callerOrchestratorstringCaller identity for RBAC. Required for dryRun: false (default-deny). When provided and not "system", every matched task must have createdBy or assignedTo equal to the caller.

Returns

dryRun=true (preview — default)

{
  count: number,        // number of tasks that would be closed (≤ BULK_COMPLETE_HARD_CAP)
  sampleIds: string[],  // up to 10 matching task IDs
  bulkRunId: string,    // unique run ID (Day-76 evidence token, pre-generated)
  cappedAt?: number     // present iff matched count was truncated at the 500-cap; caller must narrow filter
}

dryRun=false (commit)

{
  count: number,        // number of tasks closed
  sampleIds: string[],  // up to 10 closed task IDs
  bulkRunId: string,    // unique run ID used in every completionNote
  executedAt: number    // epoch ms when the mutation ran
}

Examples

Dry-run preview (default behavior)

// call — dryRun=true is the default; this call never mutates
{
  "filter": { "autoGeneratedOnly": true },
  "callerOrchestrator": "system"
}

// response
{
  "count": 152,
  "sampleIds": ["k17abc...", "k17def...", "k17ghi..."],
  "bulkRunId": "bulk-1782050000000-a3f2"
}

Live bulk close with custom completion note template

// call — explicit dryRun=false with custom template
{
  "filter": { "autoGeneratedOnly": true },
  "dryRun": false,
  "completionNoteTemplate": "bulk-cleanup: cron-spam day {{day}} runId={{bulkRunId}}",
  "callerOrchestrator": "system"
}

// response
{
  "count": 152,
  "sampleIds": ["k17abc...", "k17def...", "k17ghi..."],
  "bulkRunId": "bulk-1782050000000-a3f2",
  "executedAt": 1782050000000
}

RBAC-gated call (orchestrator-scoped)

// call — pi can only close tasks it created or was assigned
{
  "filter": { "autoGeneratedOnly": true },
  "dryRun": false,
  "callerOrchestrator": "pi"
}

// response (all matched tasks belong to pi)
{
  "count": 8,
  "sampleIds": ["k17jkl...", "k17mno..."],
  "bulkRunId": "bulk-1782050000001-c9d4",
  "executedAt": 1782050000001
}

Cron contract

The autoGeneratedOnly filter matches tasks that satisfy either predicate below. This is the same contract used by list_tasks excludeAutoGenerated (PR-E).

PredicatePatternExample matchesExample non-matches
createdBy/^cron-/i (dash mandatory)cron-bot, cron-dailycronus, cron (no dash)
title/^\/?check-messages$/i (whole-string, optional leading slash)check-messages, /check-messages, CHECK-MESSAGEScheck-messages-v2, run check-messages

Filter placement: applied in-memory against all non-done tasks, before any mutation is executed.

Day-76 evidence-bound completionNote

Every task closed by bulk_complete_tasks receives a completionNote that satisfies the Day-76 Evidence-Bound Done doctrine. The default template injects two verifiable proof tokens:

  • {{day}} — project day number computed from epoch 2026-03-06 UTC. Unique per calendar day.
  • {{bulkRunId}} — format bulk-<epochMs>-<randomHex4>. Unique per run, consistent across all tasks closed in that run.
  • {{executedAt}} — epoch ms timestamp of the mutation.

Default note written to each task: "bulk-cleanup: cron-spam day {{day}} runId={{bulkRunId}} executedAt={{executedAt}}" (with values interpolated).

This means every closed task carries a traceable, auditable proof token — the bulkRunId links the batch, and day scopes it to a human-readable project timeline.

Callouts

Blast-radius cap: the iterator uses withIndex("by_status") to scan only non-done tasks with early-stop at BULK_COMPLETE_HARD_CAP + 1 = 501. Matched count above 500 throws BULK_HARD_CAP_EXCEEDED — narrow the filter (e.g. add assignedTo) and retry. Dry-run output carries cappedAt: 500 when truncation applies.

Reductive filter required: a filter with neither autoGeneratedOnly: true nor assignedTo set is rejected with BULK_FILTER_TOO_BROAD. Match-all is forbidden by design — destructive surface must always be scoped.

RBAC + caller gate: dryRun: false without callerOrchestrator throws BULK_CALLER_REQUIRED (default-deny on the destructive path). When provided and not "system", every matched task must have createdBy or assignedTo equal to the caller, else the entire mutation throws RBAC_DENIED — no partial close. Use "system" to bypass RBAC for fleet-wide cleanup.

Why this matters: before PR-F, cron-spam tasks could only be listed (with excludeAutoGenerated) but not closed in bulk. Pi's queue accumulated 152 cron-spawned tasks (audit section 13) that required individual complete_task calls to clear. bulk_complete_tasks drains the full backlog in two calls — one preview, one commit — with a verifiable audit trail via bulkRunId.

On this page