Skip to content

Add bulk entry updates as a job and the get_job tool - #2644

Merged
pournasserian merged 12 commits into
v2from
2539-bulk-operations
Oct 26, 2026
Merged

pournasserian merged 12 commits into
v2from
2539-bulk-operations

Conversation

@pournasserian

@pournasserian pournasserian commented Oct 19, 2026 •

Copy link
Copy Markdown
Member

Closes #2539

What

Bulk operations (MCP-08) on the job queue. A bulk update returns a change set id and a job id at once, and the work continues in the background, with progress the agent can read.

  • bulk_update_entries: a list of {id, base_version, fields} for entries or entities, with a rationale and sources.
    • It checks the list's shape, naming each malformed item by its position.
    • It refuses a list longer than the agent's items-per-change-set cap before anything is written, naming the cap. The check is a new IAgentPermissions.CheckNewChangeSetSizeAsync, since there is no set yet to name; its security event names the credential.
    • It then opens a change set and enqueues one job in the same unit of work, and returns the set id and the job, so an agent never gets a set without its job or the other way round.
  • bulk.entry_updates (BulkEntryUpdates, a job handler):
    • It applies the items in order, each update with its operation in one unit of work, and reports progress after each item.
    • The job runs as the agent that asked, so its permissions, scopes and caps apply to every item.
    • An item that fails, such as a stale version, an invalid field or a scope refusal, is recorded once with its position, id and every reason, and the rest go on. Progress counts items, not errors.
    • The job succeeds when every item is in the set. Otherwise it fails with the count of failed items first, then the first 20, each reason cut to 100 characters so all of it fits in the job's last error.
    • An item already in the set is skipped, so a retried job repeats nothing. An item that fails because a worker that lost its lease committed it meanwhile is found in the set and not counted as failed. A payload it can't read is a failure, not an exception to retry.
  • Submitting while the job runs: submit_change_set refuses an open set while a job other than its checks is queued or running on it, with change_set_busy, so a reviewer never gets part of a bulk update.
  • get_job: a job's type, state, subject, progress, attempts and last error. An agent sees only its own jobs.
  • ToolArgumentsReader.RequiredArray. ToolJson.JobRecord sits beside the checks lane's shorter ToolJson.Job.
  • Coverage: MCP-08 moves to built in McpCoverageTests. CONTRIBUTING.md describes the tools.

Why

MCP-08 asks that bulk tool calls return a change set id and continue in the background. The epic's case: "a bulk update of 180 entities returns a change set id immediately, and progress can be read until the set is ready for checks".

Choices worth a look:

  • One unit of work per item, not one for the whole list, so a stale item doesn't throw away the 179 that worked, and progress is real.
  • The job runs as the agent, not as an engine: every rule an agent's own call meets applies to each item, and the audit log names the agent.
  • A retry skips what is already in the set, using the set's operations, so it needs no state of its own and is safe after a crash halfway.
  • The set is not submitted by the job. The agent submits it with submit_change_set once the job has ended, as with any set.
  • The submit guard is general: it refuses any unfinished job whose subject is the set, other than the check run, rather than naming bulk updates, so later jobs that fill a set get it too.
  • Not in this PR: bulk creates and deletes, bulk schema writes, and a desk view of jobs.

Tests

Integration, each on SQLite and PostgreSQL unless noted:

  • bulk_update_entries with 180 entity updates returns a set id and a queued job at once. get_job shows the progress only move up, to 180 of 180, the set holds 180 operations, and the job succeeded.
  • An item with a stale base version and one with a problem in every field are each listed once by id, the progress says 2 of 4 failed, and the other items are in the set.
  • A job whose first attempt throws after two items is retried by the worker and adds each item once. A job run again after its lease was lost does too.
  • A submit while the bulk job is still filling the set is refused with change_set_busy, and goes through once the job has ended.
  • With 30 failed items, the last error starts with the count and lists items 0 to 19.
  • A list past the cap is refused before any set is opened, with a message that names no set and a security event on the credential. One with malformed items is refused with each item's problem named.
  • get_job shows an agent its own job and refuses another agent's.

Unit: four payloads the handler can't read each fail with payload_invalid. Both tool providers pass the tool conformance suite.

The full suite ran locally against a postgres:17 container as well as SQLite, and markdownlint passes. No new package and no new project reference.

Checklist

  • Linked to an issue a maintainer has confirmed
  • Targets v2
  • Tests or docs updated where behaviour changed
  • No tool-generated co-author trailers or footers

# Conflicts:
#	src/FluentCMS.Core/Builtin/Tools/ToolJson.cs
#	tests/FluentCMS.IntegrationTests/Mcp/McpAgents.cs
#	tests/FluentCMS.Tests/Tools/ToolProviderConformanceTests.cs
# Conflicts:
#	CONTRIBUTING.md
#	tests/FluentCMS.IntegrationTests/Mcp/ConnectedAgent.cs
#	tests/FluentCMS.IntegrationTests/Mcp/McpAgents.cs
#	tests/FluentCMS.Tests/Tools/McpCoverageTests.cs
#	tests/FluentCMS.Tests/Tools/ToolProviderConformanceTests.cs
@pournasserian pournasserian added this to the M1 Governance core milestone Oct 19, 2026
@pournasserian pournasserian added type:feature New capability v2 FluentCMS v2 (v2 branch) area:mcp MCP server and agent credentials (MCP) labels Oct 19, 2026
@pournasserian
pournasserian merged commit 39c5d95 into v2 Oct 26, 2026
2 checks passed
@pournasserian
pournasserian deleted the 2539-bulk-operations branch October 26, 2026 01:19
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

area:mcp MCP server and agent credentials (MCP) type:feature New capability v2 FluentCMS v2 (v2 branch)

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Bulk operations: bulk entry and entity updates as a job, returning the change set id at once, with get_job for progress (MCP-08)

1 participant