Skip to Content

table line

table 是其他 line kinds 的父資源。它把作者選定的穩定 ref 對應到 live table ID;顯示名稱可以改,ref 才是 IaC identity。

JSONL Line

{"kind":"table","ref":"orders","spec":{"name":"Orders","description":"Fulfilment queue","key":"order_no","settings":{"default_permissions":{"can_read":"all","can_insert":false,"can_edit":"none"}}}}

欄位契約

頂層對應 IacTableLine:

欄位必填精確契約
kind是固定 "table"
ref是authored ref pattern,最多 64 字元
state否"present" 或 "absent",預設 "present"
renamed_from否舊 table ref;同一 authored ref pattern;可為 null
spec條件式IacTableSpec object;present 通常提供,absent 可提供以採用後刪除

spec 只接受:

欄位必填精確契約
name是非空 string,最多 64 字元
description否string,預設 "",最多 512 字元
key否本 table 的 column ref 或 null;省略時套用位置預設,見自然鍵
settings否object 或 null;可承載 table create settings,例如 default_permissions、column_acl、資料表的 write_policy(any 或 commands_only),以及 agent 寫入治理的 opt-in agent_writes_via_commands_only——後者在既有資料表上只能經由此處設定,因為 REST 的 table update 只能改 name 與 description。見 settings 照寫入內容保存
moderators否原始 user ID 或 $user:<username> token 的陣列,也可為 null;最多 50 個;與順序無關;PUT 語意。禁止 $dept: / $smc: / $room:

自然鍵

key 指定 record line 比對時使用的欄位,也是 link cell 尋找目標 record 的依據。Table line 省略它時,plan 會採用該表第一個宣告、型別既非 computed、也不是 boolean、json 或 interval 的 column line;只要該表有 record line,table line 上就會出現警告:table relies on the positional-default key column for record matching; set key explicitly。

Key 欄位能不能當 key,只有在文件中有該表的 record line 時才會檢查。此時 boolean、json 或 interval key 會讓每一條 record line 以 key column '{ref}' is a {type} column and cannot be a natural key (…) 失敗;找不到可用欄位的表則以 table '{ref}' has no resolvable key column for record matching 失敗。沒有 record line 時,任何 key 都能通過。

Apply 用的預設並不完全相同。對省略 key 的 table line,apply 記進 state 的 key 是第一個宣告的非 computed 欄位,不論型別。若該欄位是 boolean、json 或 interval,plan 與 apply 就會不一致:apply 以該欄位比對這張表的 record line,永遠找不到 live row,於是不論 plan 怎麼說,每一條 record line 都會新增一列;下一次 plan 讀到記錄下來的 key,該表的每一條 record line 都會驗證失敗,table line 也會警告 key changed (implicit (positional re-default)) from '{recorded}' to '{planned}' — …。凡是有 record line 的表,都請明確宣告 key。Export 一律寫出 key。

Settings 照寫入內容保存

settings.column_mapping、settings.iac 與 settings.legal_hold 由伺服器擁有,禁止直接宣告。retention_policy 會依 {minimum_days} 驗證(嚴格整數 0–36,500,預設 0,不接受額外 key);法律保全請用受治理的 Command step。詳見保留期限與證據。IaC 也驗證 default_permissions.audience(見下文)、write_policy 與 column_acl。其他 settings 照內容保存:格式錯誤的 default_permissions 欄位或未知 key,可能略過 REST default-permissions route 的檢查。Apply 把宣告的 key 逐一合併進 live settings,每個宣告 key 整個取代,未宣告 key 維持不動。

座標索引狀態 settings._coordinate_cells_v1 同樣由伺服器擁有,但帶有它的 spec 不會被拒絕。Backend 5.21.0 會把它寫進含座標欄位之資料表的 export,所以自後端 5.22.0 起,plan、apply 與 package install 背後的文件 parser 都會先把 table spec 裡的這個 key 丟掉,其他程式碼讀不到它。用戶端帶來的那份不會被儲存、比對,也不會出現在 plan 的回應裡:5.21.0 匯出的文件仍可透過 plan 與 apply 匯入,其他內容都沒變的資料表會規劃為 noop。5.22.0 之前上傳的 package input 是例外:它儲存的 semantic digest 是連同這個 key 一起算出來的,所以 table 行帶有這個 key 的 iac_document 或 resource input,在 POST .../packages/plan(以及之後解析它的 apply 或 activation)會回 422、ct.package_incompatible 與 Input semantic commitment does not match its bytes。請以新的 request_idempotency_key 重新上傳該文件(沿用舊的 key 時,內容相同會重播已儲存的 input,其 digest 仍是舊值;內容不同則回 409 ct.request_key_reuse),並在新的 manifest 中引用新的 input;若是 resource input,還要把新 input 的 semantic digest 宣告為它的 resource_digest,因為 manifest 中宣告的 digest 也會與重新算出的值比對。Export 不再寫出這個 key。

write_policy 也會被驗證:any 或 commands_only(沒有這個 key 就是 any);其他任何值,包括 null,都是 parse 階段的 400(settings.write_policy must be one of ['any', 'commands_only'])。文件就是用它讓既有資料表納入 action policy,與 PATCH …/write-policy route 並行;export 與其他 settings key 一樣照原樣輸出,所以 round trip 會保留它。有效 policy 為 commands_only 的資料表——文件自己的 table 行有宣告該 key 時以它為準,否則看 live 資料表——會讓每一行 record 行在 plan 時以 governed_context_required 被拒絕。

column_acl 接受同表邏輯 column ref、沒有歧義的顯示名稱 alias,或有效的 live 內部 col_<hex> key。Plan 會對照 live 與新宣告欄位解析;apply 在 record seed 前綁定 native key。Policy 採 read: "all" | "managers" 與 write: "all" | "managers" | "none"。Export 把 native key 反轉為 symbolic ref,讓 ACL 能隨套件搬到另一個 scope。未知參照、有歧義的 alias、兩個 alias 指向同一欄位與無效 policy 分別以 iac_column_acl_reference、iac_column_acl_ambiguous、iac_column_acl_duplicate 與 iac_column_acl_invalid 失敗即關閉。可攜套件優先使用邏輯 ref;native key 仍只指名它自己的 live 欄位。

Client access 必須使用 client_access line。IaC 不會拒絕 table line 上的 settings.client_access:它會把該 object 原封不動併入 settings——不做 passphrase 雜湊,也不驗證權限——而 plan 會比較並顯示原始保存的 object。絕對不要在那裡宣告它。

Moderator 是取代,不是新增

moderators 宣告的是整個 moderator 集合。Apply 會整批取代,而不是合併,因此一份宣告了 moderator 的文件會靜默移除任何在別處加入的人。三種狀態是真的不同:

  • 省略——moderator 不受管理,IaC 完全不碰。
  • []——清空 moderator 集合。
  • 一個清單——moderator 集合變成剛好等於該清單。

重新排序是 noop:兩邊都會正規化成排序後的 ID 清單。Live 集合若偏離 last-applied spec,會像其他欄位一樣以 moderators 出現在 drifted_fields。

Export 只在 live 集合非空時才輸出 moderators,因此 export → import 的往返永遠無法清空 moderator;要清空必須手寫 moderators: []。

每一項可以是供同環境往返使用的原始 user ID,也可以是可攜的 $user:<username> token。其他 identity-token family 不合法,因為 department、social client 與 chatroom 都不能成為 table moderator。Token 在 plan 與 apply 都以文件所屬公司為錨點解析,失敗即關閉;authored token 永不持久化,last-applied state 儲存 canonical user ID,而 export 在有 username 時輸出 $user:<username>,否則退回原始 ID。詳見可攜身分 token。

每個解析後的 moderator 都必須是該表格所屬公司中仍存在、未刪除的真人使用者;部門服務帳號會被拒絕。這項檢查現在於 plan 就執行、早於建立任何表格,屬於 validate-phase 錯誤並帶 code: "invalid_moderator_target",所以不合法的集合會以 422 擋下整份文件,而不是在表格已建立後才讓某一行失敗。Apply 會重複檢查;若使用者在規劃後被刪除或移走,該行會以相同代碼回報:

moderators not found, deleted, or outside this table's company service_account_managed_by_department cannot resolve this table's company for moderator validation

無法解析的 $user: token 會以它自己的解析訊息回報,代碼相同。舊的 moderators not found (or deleted): [...] 與 moderators outside this table's company: [...](會列出有問題的 id)已不存在。

settings.default_permissions.audience 只在 department scope 合法

default_permissions 新增了 audience 鍵:"scope"(預設)或 "company"。在 department scope 的資料表上,"company" 會把該表的 defaults 從「只有所屬部門」放寬到「同公司的每一個 internal principal」。在 chatroom 或 company scope 的表上這個值沒有意義——resolver 會忽略它——所以 IaC 選擇拒寫,而不是留下一個「看起來像全公司共享」的 settings 回音。

這道閘對每一個非 state: "absent" 且帶 spec 的 table line 執行,而且會擋兩次:

  • 在 plan,以 validate 階段錯誤掛在該行上。Plan 不 applyable,apply 回 422 且什麼都不執行。
  • 在 apply,改對live 資料表自身的 scope 重驗,而不是文件宣告的 scope。直接呼叫 apply、或拿一份表搬走後已 stale、不再與 live state 相符的 plan,都繞不過去。此時它是 200 內該行的 apply 錯誤,其他每一行照常執行。
settings.default_permissions.audience "company" is only valid on department-scoped tables settings.default_permissions.audience must be "scope" or "company", got '{value}'

第二則訊息中的值是以 repr 輸出,因此錯誤字串會帶引號。兩則都出自與 REST 相同的共用驗證器,所以文字逐字相同:POST .../tables 兩則都可能以 400 拋出;PATCH .../default-permissions 只會以 422 拋出「僅部門層有效」那一則——因為該 payload 的 audience 是型別化 literal,超出 scope|company 的值會先被 request validation 以一般的 pydantic literal_error 擋掉。

Key-level settings 合併帶來兩個後果。Export 會原樣輸出 settings,只扣掉 column_mapping、iac、client_access、legal_hold,以及(自後端 5.22.0 起)座標索引狀態,因此 authored 的 audience 能撐過 export → 重新匯入的往返,並重新規劃為 noop。但 apply 是以整個鍵取代 default_permissions,不是逐欄合併:用一個省略 audience 的 default_permissions 物件重新 author 這張表,下一次 apply 就會靜默撤銷該共享。

Ref 與身分規則

ref 與 renamed_from 必須符合 ^[a-z0-9][a-z0-9_-]{0,63}$。它們是 internal-key refs:以小寫 slug 跨環境穩定解析,不是 table display name,也不是 UUID。Table refs 在文件內必須唯一。

Child line 用 table: "orders" 指回這個 ref。Column、rule、trigger、view 與 public_read 的 child ref,加上固定的 client_access identity,再形成 orders.{child};它們在該 qualified namespace 內跨 kind 不可碰撞。Table spec.key 同樣填 column ref,例如 order_no,不得填 col_a1b2c3 internal key,而且不能是 boolean、json 或 interval 欄位(在該表有 record line 時檢查);decimal key 以正規化後的字串形式比對。

生命週期與規劃

  • 沒有 state claim 且沒有可唯一採用的 live table:create。
  • Scope 中恰好一張未管理的 live table 名為 spec.name:adopt。採用的那次 apply 只記錄 state;宣告與 live 的差異由下一次 apply 寫入(見 adoption)。
  • renamed_from 指向既有 state:move,並連帶更新 child qualified refs。兩個 ref 都沒有 state row,或兩者都有時,該行改帶 diff 階段錯誤;只有新 ref 有 state row 時,代表搬移早已完成,該行直接以新 ref 規劃。見 claim 與搬移錯誤。
  • Owned fields 不同:update;一致:noop。
  • state: "absent":delete。Table 是唯一可在 absent 時帶 spec 的 kind,以支援 adopt-then-delete;刪除是 soft delete,仍須審查依賴 warning。

單純從文件省略 table line不會刪除它。column_mapping 由伺服器在欄位變更時維護,不屬於 last-applied authored spec。

驗證錯誤

缺少 ref:

[{"type": "missing", "loc": ["table", "ref"], "msg": "Field required", "url": "https://errors.pydantic.dev/<pydantic-version>/v/missing"}]

不合法 ref(以 Bad ref 為輸入):

[{"type": "string_pattern_mismatch", "loc": ["table", "ref"], "msg": "String should match pattern '^[a-z0-9][a-z0-9_-]{0,63}$'", "url": "https://errors.pydantic.dev/<pydantic-version>/v/string_pattern_mismatch"}]

Moderator 項目既不是原始 ID、也不是 $user: token 時(例如 $dept:Front Desk),會在 ["table", "spec", "moderators", {index}] 以一般的 string_pattern_mismatch 失敗;超過 50 個項目則以 too_long 失敗。

重複 ref 與伺服器擁有 settings:

duplicate table ref 'orders' settings.column_mapping is server-owned and cannot be set via IaC settings.iac is server-owned and cannot be set via IaC settings.write_policy must be one of ['any', 'commands_only']

後三類來自 spec 之 settings 上同一個丟出一般 ValueError 的 validator,它只回報遇到的第一個違規(先檢查伺服器擁有的 key,再檢查 write_policy)。觸發它的文件在 plan 與 apply 都以 parse 階段的 400 回應,帶 loc 為 ["table", "spec", "settings"] 的 typed value_error issue(見 Validator 訊息類別);工作台顯示相同的 detail。精確 detail 來源:components/iac/parse.ts。

動手試試

在 IaC 工作台貼上範例,先把 key 改成尚未宣告的 ref,再加入 settings.column_mapping。修正本機錯誤後,對測試 scope plan 並核對 table action。

Last updated on