有序子集合
宣告 ownership
於既有 company/department/chatroom table URL,GET/PUT child-collections 讀取/設定 collections 陣列。每張參與表都要求現行管理權限。定義以穩定 key 綁定同 scope 子表、cardinality-one parent link 與 integer position 欄位:
{
"collections": [{
"key": "lines",
"child_table_id": "<child-table-uuid>",
"parent_link_column": "<parent-link-key>",
"position_column": "<position-key>",
"delete_policy": "restrict"
}]
}placeholder 須換成既有身分。delete_policy 為 cascade、restrict(預設)或 detach。ownership 不可成環;live binding/schema 不能以一般 metadata 編輯變更。parent 與 position cell 由伺服器擁有。
使用已儲存 v2 Command 寫入
POST records/bundle、PATCH/DELETE records/{record_id}/bundle、POST records/bundle/import 要求已儲存 Command UUID、actor 範圍 idempotency key 與 bundle。v2 program 包含 child_collection step:table、選定 collections、operation: "save" | "delete"、bundle: "$input.bundle"。
以下示意新主表 body 不是 raw record CRUD:
{
"command_id": "<persisted-command-uuid>",
"idempotency_key": "invoice-bundle-001",
"bundle": {
"data": {"<header-name-key>": "Document 001"},
"children": {"lines": [{"data": {"<line-name-key>": "First line"}}]}
}
}- 新主表省略
record_id/expected_version,既有主表兩者都要;新明細省略id/expected_version,既有明細兩者都要。 - 提供陣列會取代成員集合;省略集合 key 保留成員。陣列順序持久保存,重排保留既有 ID。
- 上限:16 個集合、每集合 200 筆明細、每 bundle 800 筆明細。
- 每個效果仍檢查 Command 權限、action policy、rules、aggregates、periods、sequences 與 effect identity。
commands_only沒有 raw-CRUD 例外。 - 失敗回滾整組;審批凍結完整 bundle。原生 Review 核准與對話同意仍是不同 gate。
匯入(import)建立新身分,不是通用 upsert。匯出(export)移除身分、生成/計算值及 ownership cell,保留允許的原生連結。一般主表刪除也遵守集合生命週期控制。
讀取、歷史及可攜介面
GET records/{record_id}/bundle 投影主表及可見、有序明細。GET records/{record_id}/bundle/as-of 的 UUID change_id 與含時區 as_of 恰選一個;時間先解析持久分組 change,再讀取精確 captured version。一般單列歷史及 lookup pick.as_of 不變。
目前/歷史讀取都套用現行 ACL、欄位限制及 link target 存活檢查。隱藏成員省略,不洩露身分、數量或存在提示;不虛構歷史權限。
IaC child_collection 綁定符號子表/欄位 ref;record_bundle 綁定符號 Command 及 nested cell,沿用 scalar/link/principal resolver。export 把普通連結目標 record 放在 nested bundle 前。需要 approval hold 的 IaC 以 approval_staging_unavailable 拒絕,不產生業務效果。
CT Agent/Analyst 讀取使用 custom_tables_get_bundle/custom_tables_get_bundle_as_of;MCP 使用 child_collections_get、需確認的 child_collections_configure、records_bundle/records_bundle_as_of。寫入保留已儲存 Command 的 prepare/consent/token-execute。MCP 設定 dry-run 提供 typed preview/confirmation token,commit 重驗權限。原生 REST bundle route 為 JWT-only,仍不開放 API key。
衝突使用 child_collection_restrict、child_collection_version_conflict、child_collection_schema_conflict、child_collection_membership_conflict;無效形狀為 typed 422。共用 Command 錯誤不變。
審批與法律保留
審批。 bundle 寫入就是一般的 Command 寫入,所以 require_approval 是依它實際產生的 header 與子表寫入(insert、update、delete,含 cascade 與 detach 效果)來判定,與同樣的一般 step 相同(見 Command 審批)。整個 bundle 是一個單位:只寫入一張受審批閘門管轄資料表的 plan 會整份凍結,release 時 header 與所有明細一起落地。其他事件的規則或已停用的規則,既不會 hold 住 bundle,也不會增加要求;例如子表上只在 update 觸發的 mapped 規則,不會影響只插入新明細的 header save。同一事件的規則即使 when 為 false,也不會 hold 住 bundle,但仍可能要求能讀取它的 mapped 來源(見下)。透過 Command 自己的 …/execute,被 hold 住的 plan 會回一般的 409 approval_required。
當 Command 有 proposal_policy,或 header 資料表、或其任一集合的子表帶有啟用中的 require_approval 規則時,REST bundle route 會走凍結提案的流程。它們先 prepare 提案,不需審批時立即 post,否則維持 hold 住;此時回應帶的是 proposal(post 之後再加上 operation),而不是 execution。
把 Review 表單輸入對應進來的規則(field_map)沿用一般 step 的規則,唯獨 mapped 來源何時做讀取檢查不同(見下)。當這類規則符合 bundle 的其中一次寫入時,plan 若做出多於一次寫入,會以 409 approval_mapping_batch_unsupported 整筆拒絕。觸發的規則其來源資料表不在該 Command 的 tag 內時,回 422 command_tables_outside_tag 並附資料表 id;自後端 5.22.0 起,不含 child_collection step 的 Command 自己的 /execute 也回同樣的結果(以前是 500 command_execution_failed)。mapped 來源的讀取檢查依進入點而定。在 prepare(REST bundle route 走的就是這條路,見上)時,同一事件的規則即使 when 為 false 也算數:位於其他資料表、而呼叫者讀不到的 mapped 來源,會在任何同意或寫入之前以不含值的 ct.permission_denied 拒絕(回 403 並標示 do_not_retry)。自後端 5.22.0 起,這項檢查也涵蓋 Command 自己會寫入的 mapped 資料表:呼叫者可以對某個 mapped 子表 insert、卻不能讀取它時,bundle 的 prepare 會回同樣的 403;以前它會簽發一份 proposal,之後重複 prepare、讀取與 operation 全都回 409 ct.proposal_stale。透過 Command 自己的 …/execute 時,bundle 只在規則觸發時才檢查 mapped 來源,而一般的 insert、update、delete step 則不論 when 為何,都會事先檢查。
法律保留。set_legal_hold 不是資料列事件,不能與 child_collection step 並用。建立、更新與 /validate 都會拒絕這樣的 definition(順序不拘,save 與 delete 皆然),回既有的 422 value_error,位置為 loc ["body","definition"],其 msg 為 Value error, set_legal_hold cannot be mixed with record writes or staged row proposals。請拆成兩個 Command。在 backend 5.21.0 之前就已儲存、同時含這兩種 step 的 Command 維持原狀,但已無法執行:執行時回 409 schema_dependency_changed,再次儲存時也會被同一項檢查拒絕;trigger 相依刷新會略過它並記錄 ct.command_legacy_legal_hold_collection_mix。見保留期限與證據。
來源為 backend #1861、CT tool-roster 修正 #1877 與 #2120。核心更新補齊記錄來源/部署界線。