5.4 KiB
Guarded Batch Draft Creation And Updates
Use this fast path for 2-50 routine-content records on one explicit site. It supports news, products, series, industries, application cases, downloads, about-culture items, recruitment cards, welfare items, and staff activities. It does not support homepage blocks, column copy, relations, media registration, publication, mixed sites, structure, settings, arbitrary endpoints, or SQL.
Every workflow has a non-mutating prepare command and a separate execution command. Packets are bound to the canonical origin, literal site, schema-approved normalized fields, validation warnings, versions/diffs where applicable, and a review hash. They expire after 24 hours. Never edit a packet; prepare a new one.
Create Unpublished Drafts
Create a plan outside the skill folder:
{
"site": "en",
"items": [
{
"path": "/api/ai/products",
"fields": {"title": "Example A", "content": "Reviewed copy"}
},
{
"path": "/api/ai/products",
"fields": {"title": "Example B", "content": "Reviewed copy"}
}
]
}
Run:
python scripts/cms_content_batch.py prepare-create PLAN.json --packet-out PACKET.json [--report-out REPORT.json]
python scripts/cms_content_batch.py create-drafts PACKET.json [--report-out REPORT.json]
prepare-create reads each required schema once, rejects protected/unknown fields and dangerous markup locally, calls every create validator, and searches the same-site collection for an exact title/name duplicate. It performs no write. If the API returns warnings, review them and set accept_warnings=true only on that plan item before preparing a new packet.
create-drafts repeats every schema, validation, warning, and duplicate check before the first write. Only when the whole batch still matches does it create drafts sequentially with stable unique idempotency keys, require the same-site unpublished response/version/audit ID, and read back every normalized field. It never publishes or deletes a draft. A mid-batch failure stops immediately and reports created IDs and the failed/unexecuted positions; inspect them before any identical replay.
All referenced media must already be registered through the CMS media workflow and represented by its returned cms_item. A batch packet does not authorize media upload or registration.
Update Existing Content
Create the smallest schema-approved patches:
{
"site": "en",
"items": [
{
"path": "/api/ai/products/254",
"patch": {"content": "Reviewed replacement copy"}
},
{
"path": "/api/ai/series/79",
"patch": {"title": "Reviewed series title"}
}
]
}
Run:
python scripts/cms_content_batch.py prepare-update PLAN.json --packet-out PACKET.json [--report-out REPORT.json]
python scripts/cms_content_batch.py apply-update PACKET.json [--confirmed] [--report-out REPORT.json]
prepare-update reads each schema once, then performs same-site detail → exact current version → update validation for every item. It binds publication state, normalized patch, diff, warnings, and no-op state. A no-op remains an observation and is never PATCHed. Product/series category moves still require the explicit validated move_category=true control.
Before apply-update, ensure the compact prepared item IDs, publication states, changed fields, warnings, and review hash remain within an explicit human authorization. If any non-no-op item is published, pass --confirmed only under that authorization. Do not ask again merely because preparation produced a hash or fresh version; ask when material fields, targets, warnings, or action drifted. The command rejects a missing --confirmed before every HTTP request.
Execution re-reads and revalidates the complete packet before the first PATCH. Any version, publication state, normalized patch, diff, warning, or no-op drift stops the batch before all writes. Writes remain sequential. Each update idempotency key binds the packet review hash, resource type, and item ID so equal numeric IDs in different resources cannot collide. Each success requires a new version and exact same-site readback. Live updates send confirmed_live_update=true; after success, perform the returned live-item public fetch checks using the ordinary lifecycle workflow. A partial live-update batch is not automatically rolled back: the result lists updated, failed, and unexecuted IDs, and work stops for same-site detail/audit inspection.
Update packets use the current format version and are not forward-migrated. After a batch-tool format upgrade, run prepare-update again instead of editing or applying an older packet; this preserves the reviewed request-to-idempotency-key binding.
Progress And Private Run Report
stdout contains only the final JSON result. stderr streams credential-free JSON progress with preflight, validate, write, readback, and complete phases, counters, optional IDs, and relative elapsed milliseconds. Library calls are silent unless a progress callback is supplied.
The optional --report-out path must be new, non-symbolic, and inside an existing directory. It is reserved before network work and written 0600 on success or handled failure. Reports contain only site, review hash, relative phase timings, IDs, HTTP request count, and generic outcome/error classification. They never contain the token, request bodies, normalized patches, full CMS copy, idempotency keys, or public HTML. Do not commit reports or packets.