Required card revisions
This release is based on public source commit 628671e. Application commit df0a364183046a0b295af402ca15fea5d545c880 passed exact-commit CI and was tested on hosted Ignite version 8. See the scoped live evidence and remaining limits; this does not establish every client or deployment.
Contract
Every client-selected edit of an existing card requires expected_revision equal to the revision of the card the caller actually read:
| Web request | MCP action |
|---|---|
PATCH /api/cards/{id} (metadata, assignment, checklist) | update_card, assign_card, update_checklist |
POST /api/cards/{id}/move | move_card |
POST /api/cards/{id}/blockers | set_blockers |
The field is required StrictInt in every public input schema. Omission, null, booleans, strings, floats (including 1.0) and containers are rejected, not coerced. The application layer independently requires an exact Python integer, so calling a use case directly cannot bypass the check. Its legacy default arguments remain only to return a controlled domain error after authorization; they no longer permit a revision-free write.
Authorized callers receive HTTP 422 for missing/malformed input and 409 for a well-formed nonmatching revision. MCP returns a tool error (isError), not a success or partial save. Revisions of zero, negative integers and future integers do not match a persisted card and conflict. A valid matching no-op retains the card revision and adds no history; a stale no-op still conflicts.
Inside the existing atomic transaction, card lookup checks visibility, takes the board aggregate guard and rereads the card. The use case then checks edit permission and the supplied revision before validating the proposed fields or handling a no-op. Web/MCP schema validation can reject malformed requests before entering the use case; those validation errors reveal no card data. Well-formed unauthorized requests retain 401/403/404 behavior rather than exposing current revisions.
Creation has no pre-existing card revision and is unchanged. Owner-directed membership removal/demotion remains a separate atomic permission operation: it must revoke access and clear affected assignments based on the locked current state, not depend on a caller supplying a revision for every card. It increments each affected card revision and records each unassignment, invalidating older card drafts. This patch does not introduce board/membership optimistic-concurrency tokens or durable create idempotency.
Client upgrade and compatibility
This is an intentional breaking API change for integrations that omitted revisions or sent coercible values. Existing clients already sending the observed integer revision remain compatible. There are no schema, data, migration, dependency or tool-name changes.
- Read with
show_boardor resolve the unique card withresolve_card; never guess a candidate or revision. Replacements such as checklists need the actual existing content fromshow_board, not just lookup metadata. - Send the observed revision as a JSON integer alongside the intended change. Use the successful response's returned revision for a subsequent action on that same known snapshot.
- On a conflict, reread and review the newer state before choosing whether/how to reapply the intent. Do not simply replace the revision and replay stale replacement content.
- On a timeout or uncertain response, read the card/activity before deciding what happened. A revision conflict is not proof that an earlier request failed; creation retries can still duplicate records.
- MCP clients that cache
tools/listshould refresh the tool schemas. Old open browser tabs should reload the updated assets; an old tab's stale action should be treated as an error, never retried blindly.
Example after reading a card at revision 7:
{"card_id":"canonical-card-id","list_name":"Done","expected_revision":7}
The bundled browser already sent revisions on edit, assignment, checklist and dependency saves. It now also captures the revision the user saw for a Move-menu or drag action, rather than looking up a newer board snapshot at submission time. Missing/invalid snapshots are refused locally. Card drafts stay revision-bound when a board refreshes; conflicts preserve the draft, do not pretend success and do not auto-retry. A successful dependency save advances only the same editor instance, preserving unsaved field/checklist drafts. A failed later dependency save clears an earlier success message.
Rolling deployment must not expose a mixture of permissive old application instances and strict new ones as a universal guarantee. An authorized rollout should update server and browser together, refresh client schemas and verify the exact serving version. Rolling back code needs no database rollback, but restores the older permissive API behavior. No database migration is required for this rollout.
Evidence and limits
Local verification of this release:
- 489 Python tests passed, including 266 new parameterized revision-contract cases and 44 additional recording SQL-adapter cases. Coverage includes omission, malformed/coerced revisions, stale/future/nonpositive values, permissions, exact whole-state/history preservation on refusal, successful writes, no-ops and one-winner concurrency simulations across every card action.
- Recording SQL-adapter checks verify the revision after the aggregate lock/reread, rollback on refusal with no card or history statements, and one connection/transaction for a successful card change plus history. They are not real-engine locking/rollback proof.
- All three simulated-DOM suites passed. New cases cover stale retained Move menus, drag-across-refresh, opened-editor revisions, dependency results, duplicate suppression, draft preservation, no automatic retries and missing/invalid snapshot refusal. They are not real-browser/layout proof.
- JavaScript syntax, Python compilation, dependency consistency and local template-structure checks passed. Installed Python package versions match both lockfiles. One upstream Starlette/AnyIO deprecation warning remains.
Hosted owner browser/DataK3 acceptance passed for current edits, dependency saves, drag/menu moves, three stale rejection paths and restoration of the original demo business fields. The linked verification record distinguishes these live results from the local tests. A later connected ChatGPT owner test passed reads and a revision-bound move/readback/restore, with exactly two retained audit events. OAuth protocol internals, broader MCP actions and other roles through that client, genuinely fresh-organization remix, byte-for-byte serving-asset verification and device-layout coverage remain unverified; see the scoped connected-client evidence. No schema changes or paid services were required; no contest outcome is guaranteed.