Command Center Bulk Actions
The apps/v1 API exposes backend-authoritative bulk deletion for asset
categories, portfolios, and portfolio groups. These routes implement the
Command Center SDK v1 resource discovery, execution, and preflight contracts
pinned to command-center-sdk 0.1.13, commit
f11c0ea8c5d3fc267997e476aa1522c798fdaced.
Resource boundaries
| Resource | Discovery | Preflight | Execution |
|---|---|---|---|
| Asset categories | GET /api/v1/asset-category/discovery/ |
POST /api/v1/asset-category/bulk-delete/preflight/ |
POST /api/v1/asset-category/bulk-delete/ |
| Portfolios | GET /api/v1/portfolio/discovery/ |
POST /api/v1/portfolio/bulk-delete/preflight/ |
POST /api/v1/portfolio/bulk-delete/ |
| Portfolio groups | GET /api/v1/portfolio-group/discovery/ |
POST /api/v1/portfolio-group/bulk-delete/preflight/ |
POST /api/v1/portfolio-group/bulk-delete/ |
Each command-center.resource_discovery@v1 response advertises one destructive action with explicit
selection. all_matching is deliberately not advertised. Discovery supplies
the execution endpoint, preflight endpoint, confirmation copy, tone, supported
selection modes, and options.
Canonical request
Preflight and execution receive the same
command-center.bulk_action_execution@v1 body:
{
"selection": {
"mode": "explicit",
"uids": ["7dc962fe-58e6-4a04-9cc6-20b44d678d42"]
},
"options": {}
}
The selected resources use UUID identities and currently advertise no options.
Unknown options, numeric identifiers, and unadvertised selection modes are
rejected. The former { "uids": [...], "select_all": false } HTTP payload is
not part of this contract.
Preflight and execution
Preflight returns command-center.bulk_action_preflight@v1. It resolves every
selected UID and reports allowed, matched_count, blockers, and warnings
without mutating data. Portfolio preflight also evaluates protected
VirtualFund and target-position references.
Each bulk-delete preflight also reports what deleting rows from the resource's
table reaches through foreign keys. It asks MetaTables
(MetaTable.get_impact(action="delete_rows")) and adds:
impact: the MetaTables impact graph. Nodes are the tables the delete reaches, with their effects and whether the caller may write each one. Edges are classified ascascade_delete,cascade_update,set_null,set_default,restrictorreads. MetaTables' blockers are included.warnings: one sentence for each foreign-key action the delete triggers, for example "PortfolioGroupMembership rows that reference deleted PortfolioGroup rows are deleted too (ON DELETE CASCADE)." Command Center shows warnings without custom rendering.blockers: MetaTables' reasons the caller may not run the delete, prefixed with the table: the caller cannot write the table, the table does not accept writes, or its DataSource is read-only. The preflight then answersallowed: falseand execution answers409before the database refuses the delete. Cascades never block: write on a table depends only on its own grants, because a foreign key's action belongs to the referencing table.
Deleting portfolios or portfolio groups cascades to their
PortfolioGroupMembership rows; deleting asset categories cascades to their
AssetCategoryMembership rows. If MetaTables cannot compute the impact,
impact is null and a warning says so. The preflight is not blocked, because
the database still applies and enforces every foreign-key action.
On the local SQLite runtime, MetaTables documents one limitation: its authorizer also checks the reads and writes of foreign-key actions, so a delete that cascades fails unless the caller can access every referencing table. The preflight cannot detect this. PostgreSQL runs these actions as the referencing table's owner.
Execution repeats preflight immediately before invoking domain deletion. A
blocked selection returns HTTP 409 and does not call the delete service. The
underlying delete operation still performs its own conflict checks, protecting
against authorization or reference changes between preflight and execution.
Request validation errors return 422. Unsupported modes or options return
400. A preflight that discovers missing or protected rows returns 200 with
allowed: false; execution of that same selection returns 409.
Provider discovery
The discovery and preflight operation IDs are also exposed through
/.well-known/command-center/connection-contract as resource operations. The
three execution operation IDs remain mutation operations:
discoverAssetCategories/preflightBulkDeleteAssetCategories/bulkDeleteAssetCategoriesdiscoverPortfolios/preflightBulkDeletePortfolios/bulkDeletePortfoliosdiscoverPortfolioGroups/preflightBulkDeletePortfolioGroups/bulkDeletePortfolioGroups
The language-neutral manifest, schemas, and fixtures in command-center-sdk are
the contract authority. Provider projects, including this repository's FastAPI
app, import the implementing Python models and helpers from msm.api.http.