瀏覽 1.1.1 文件
1.1.1

Binary DB v0:工作流程記錄

展開 Task、Change、worktree、Line、Stash、Tag、Land 以及內聯工作流程連結記錄的佈局與不變數。

適用對象: 核心實作者與工作流程整合方

這些 layout 是本地與遠端工作流程狀態的定長記錄和關聯記錄。閱讀每個位元組佈局時,要連同後面的身份、變更、檢查碼遺留轉換規則一起讀;僅憑記錄大小常量並不構成完整契約。

Task 記錄#

程式碼 · text
LOCAL_TASK_RECORD_SIZE = 64

LocalTaskRecord — task.bin:
u8  task_meta
u8  local_meta
u16 payload_len
u64 payload_offset
u32 origin_plan_revision_index_plus1
u32 plan_item_index_plus1
u32 published_remote_task_index
u64 created_at_s
u64 updated_at_s
u64 plan_linked_at_s
u64 published_at_s
u64 closed_at_s
程式碼 · text
REMOTE_TASK_RECORD_SIZE = 60

RemoteTaskRecord — task.bin:
u8  task_meta
u8  remote_meta
u16 payload_len
u64 payload_offset
u32 origin_plan_revision_index_plus1
u32 plan_item_index_plus1
u64 created_at_s
u64 updated_at_s
u64 plan_linked_at_s
u64 fetched_at_s
u64 closed_at_s

Task 的身份是推匯出來的,不是儲存的:

程式碼 · text
task_index = record_number
task_seq   = task_index + 1
task_id    = derive(authority_scope, repo_namespace, "T", task_seq)

Task 記錄只追加,絕不能在物理上重排。遺留的 SQLite 匯入可以對稀疏的遺留序列做 一次壓緊;它不得透過插入填充記錄來保留空洞。

closed_at_sLocalTaskRecordRemoteTaskRecord 的固定尾部欄位, 不是 payload,也不是單獨的記錄或檔案。零表示:要麼有效的 Task 狀態不是終態, 要麼離線遺留轉換對某個終態 Task 沒有拿到源的關閉時間。後一種情況下 Task 已 關閉,而它的歷史關閉時間未知。非零值即關閉時間;當且僅當在 TaskRecord.task_meta 之下(對本地權威還要加上 LocalTaskRecord.local_meta)有效的 Task 狀態為終態時,該值才有效。後續的 發布或關聯更新可以改變 updated_at_s,但絕不替換關閉時間。

建立 Task 時寫入 closed_at_s = 0。關閉操作先寫入並 fsync closed_at_s, 之後才寫入並 fsync 作為提交標記的終態狀態位。原生寫入方絕不會清除終態狀態來 重新開啟一個 Task。讀取方把非終態 Task 上殘留的非零時間視為缺失,恢復流程會把 它修復為零。原生 v0 寫入方絕不能建立關閉時間為零的終態 Task。由於不存在遷移 來源位或欄位,讀取方、校驗器和恢復流程把"終態加零"接受為未知的歷史關閉時間, 不把這種表示歸類為損壞。

修正前的 layout-1 本地 40 位元組和遠端 36 位元組 Task 記錄只能作為離線轉換輸入。 轉換器必須顯式收到源記錄大小,絕不能僅憑檔案大小可整除性來推斷。在獨佔的權威 鎖之下,它用 44 位元組或 40 位元組記錄把完整的 task.bin 重寫到一個單獨的檔案, 並保留源權威提供的每一個關閉時間。對於遺留源格式可證明沒有記錄關閉時間的終態 Task,它寫入 closed_at_s = 0,不得以建立時間、更新時間、轉換時間或任何其他 推斷出的時間戳來替代。定義了關閉時間但提供了非法值的源,仍然失敗拒絕。轉換器 校驗整個重寫後的檔案,並在啟用之前原子地替換舊檔案。一個活動的 task.bin 絕不混用修正前和修正後的記錄大小。

對於 bin 到 bin 的轉換,源的 planning 狀態 unplannedexplicit_unplanned 都編碼為 TaskRecord.task_meta 位 0 未置位;planned 編碼為位 0 置位。這就是 v0 完整的 planning 狀態編碼:v0 有意不保留這兩種 unplanned 源寫法之間的第三種區分。數值形式的 Plan revision 與 item 繫結會被 獨立解析並保留。只有當文字形式的 plan_section_refplan_drift_state 缺失、或者能被精確推導時,才在不新增欄位的情況下被接受:section ref 來自解析 出的 Plan Item 的 heading_path_bytes,而 drift 狀態來自數值繫結以及目前的 Plan/Plan-revision 後設資料。非推導得出的值或有歧義的值失敗拒絕。

被接納的源 Task 狀態 abandoned 對映到 TaskRecord.task_meta 位 7 canceled。它不會新建另一個 Task 狀態欄位,也不會 從服務端源推斷 LocalTaskRecord.local_meta 位 1。在單獨列舉的、確切的鎖定源 門禁之下,遺留源寫法 canceled 對映到同一個已有位,不對映到其他任何狀態。 顯式攜帶獨立的本地放棄狀態的本地源,按其既有語義保留該本地位。

對於從可證明沒有更新時間或抓取時間的遺留 Remote Task 格式做的離線轉換, 轉換器相應地寫入 updated_at_s = 0fetched_at_s = 0。每個零表示未知的 歷史時間,而不是轉換時間。提供的合法時間被原樣保留;定義了其中任一欄位卻提供 非法值的源格式失敗拒絕。原生 v0 的 Remote Task 寫入方繼續寫入它們實際的事件 時間,不使用這條遺留例外。

Change 記錄#

程式碼 · text
TASK_CHANGE_INDEX_RECORD_SIZE = 8

TaskChangeIndexRecord — task_change_index.bin:
u32 latest_change_index_plus1
u16 change_count
u8  next_change_ordinal
u8  reserved0
程式碼 · text
LOCAL_CHANGE_RECORD_SIZE = 68

LocalChangeRecord — change.bin:
u8  change_meta
u8  local_meta
u16 payload_len
u8  change_ordinal
u8  change_state
u16 reserved1
u64 payload_offset
u32 task_index
u32 previous_change_index_plus1
u32 fork_snapshot_index_plus1
u8  published_remote_change_ordinal_plus1
u8  reserved2
u16 reserved3
u64 created_at_s
u64 updated_at_s
u64 published_at_s
u32 base_line_index_plus1
u64 archived_at_s
程式碼 · text
REMOTE_CHANGE_RECORD_SIZE = 68

RemoteChangeRecord — change.bin:
u8  change_meta
u8  remote_meta
u16 payload_len
u8  change_ordinal
u8  change_state
u16 reserved1
u64 payload_offset
u32 task_index
u32 previous_change_index_plus1
u32 selected_patchset_index_plus1
u32 fork_snapshot_index_plus1
u64 created_at_s
u64 updated_at_s
u64 fetched_at_s
u32 base_line_index_plus1
u64 archived_at_s

Change 記錄既不存放文字形式的 Change ID,也不存放文字形式的 Task ID。它的 身份是 (authority_scope, task_index, change_ordinal)

已發布的 Local Change 的 Remote 身份,是由其所屬 Local Task 的 published_remote_task_indexpublished_remote_change_ordinal_plus1 - 1 組成的二元組。Task 欄位選定確切的 Remote Task ordinal;Change 欄位在該 Task 內部選定 C-01..C-64。 沒有任何 Local Change 儲存或需要一個全域性的 Remote change_index

對於 Remote Change, ChangePatchsetIndexRecord.latest_patchset_index_plus1 是最新/目前的 Patchset,而 selected_patchset_index_plus1 是唯一持久化的、可變的 selected-Patchset 權威,供評審、就緒判定和 Land 使用。選定是 Change 的狀態, 不是 Patchset 的不可變屬性。零表示沒有選定 Patchset。非零的 selected 指標必須 引用一個由該 Change 擁有的存活 Patchset。 源的 current_patchset_number 透過 latest 索引解析; 源的 selected_patchset_number 透過 selected 指標解析。兩者可以不同,絕不能 被合併成一個值。選定操作更新這個已有的指標,不改動任何 Patchset 記錄。這只是 對已有四位元組槽位的一次語義重新命名,不會移動該槽位,也不會移動 44 位元組字首中的 任何欄位。後來的內聯生命週期擴充套件只追加兩個已宣告的尾部欄位。

僅對被接納的遺留服務端格式而言,一個已 land 且有一個或多個存活 Patchset 的 Change,可能提供 current_patchset_number = 0,儘管 latest/current 仍然可以 重建。只有當源的 selected 指標非零,且解析到該 Change 擁有的、patch_ordinal 最大的那個存活 Patchset 時,轉換才接受這個零;此時它把那個 Patchset 寫為 ChangePatchsetIndexRecord.latest_patchset_index_plus1。其他任何為零、缺失、 有歧義、非本 Change 擁有、被省略或互相矛盾的 latest 證據,都失敗拒絕。selected 指標仍然獨立儲存,不會從這條例外推斷出來。

ChangeRecord.change_state 位 0 是 canceled;位 1 到位 7 保留且為零。 canceled 位只有在 archived 生命週期下才有效。源的 Change 狀態 abandoned 對映為 archived 生命週期、canceled 置位、以及已有的 superseded 位未置位。 它不會新建另一條 Change 記錄或 payload。對於可證明沒有抓取時間的遺留 Remote Change 格式,轉換寫入 fetched_at_s = 0;提供的合法值被保留,提供的非法值 失敗拒絕。

published_remote_change_ordinal_plus1 = 0 表示缺失。值 1..64 編碼 Remote Change 的 ordinal 0..63,所以 C-01 存為 1C-64 存為 64;值 65..255 非法。reserved2reserved3 必須為零。當且僅當 ordinal 欄位為零時,LocalChangeRecord.local_meta 位 0 才未置位。該位置位時,ordinal 欄位和 published_at_s 都非零,並且所屬的 Local Task 也必須置位它的 published 位。Remote 的 ordinal 可以與 Local 的 change_ordinal 不同;發布時存的是 Remote 權威傳回的、確切的 Task 作用域 ordinal。

讀取遺留的顯式文字 published_change_id 的離線轉換器,解析出確切的 C-01..C-64 字尾,並直接寫入它的 ordinal 加一的值;它絕不偽造全域性的 Remote Change 索引。按舊有的固定 u32 published_remote_change_index 解釋來讀取的轉換器,必須顯式選擇那個源 schema, 並在寫入本欄位之前,把被引用的 Remote 記錄解析到它所屬的 Task 及 Task 作用域 的 ordinal。發布權威缺失、不匹配或有歧義時失敗拒絕。

內聯 Change 生命週期欄位#

base_line_index_plus1archived_at_s 是每條 68 位元組的 Local 或 Remote Change 記錄中必需的固定欄位。它們不是 payload,不是單獨的記錄,也不是單獨的 檔案。 base_line_index_plus1 是必需的,引用建立該 Change 時所依據的那條確切的 LineRecord。Change 絕不重複存放 base-Line 名稱或文字形式的 Line ID。若 fork_snapshot_index_plus1 非零,它引用建立該 Change 時被觀測為該 base Line 的 head 的那個確切的存活 Snapshot。Snapshot 的 line_index_plus1 是它不可變的 創作 Line 身份,而不是 Line-head 歸屬;當在另一條 Line 上創作的 Snapshot 成為 base Line 的 head 之後,它可能與 base_line_index_plus1 不同,並且不得被重寫, 也不得拿來做相等比較。公開的 forked_from_line 值由數值形式的 base-Line 身份 推導而來。

當且僅當 Change 的生命週期為 archived 時,archived_at_s 才非零;例外是:從 可證明沒有記錄歸檔時間的遺留源格式轉換而來的 archived Change,保留零作為未知 的歷史歸檔時間。ChangeRecord.change_meta 位 7 在該 archived 生命週期內區分 出被取代的 Change,而 ChangeRecord.change_state 位 0 區分取消。歸檔和重新 開啟各自重寫並持久提交一條完整的 68 位元組 Change 記錄;生命週期位與 archived_at_s 一起變化,不涉及旁路檔案排序,也不涉及記錄內部的狀態最後寫入 協議。恢復流程拒絕非 archived 卻帶有非零歸檔時間的 Change。原生 v0 的歸檔寫入 要求歸檔時間非零。由於沒有新增遷移位,讀取方、校驗器和恢復流程把"archived 加零"接受為未知的遺留時間。公開的 review 狀態是 active 生命週期加上 review_pendinglanded_at 由成功的 Land 記錄的 updated_at_s 推導,不會在 Change 上再存一份。

建立 Change 時,透過精確的 payload 比較來解析歸一化的 base-Line 名稱,把該 Line 目前的 head 觀測為可選的 fork Snapshot,然後追加完整的 Change 提交記錄。 對於已提交的 Change,恢復流程會拒絕:base Line 為零或缺失、Line 缺失或有歧義、 fork Snapshot 缺失或已打墓碑、生命週期與時間不匹配,或 superseded 位非法。 恢復流程不會把 fork Snapshot 的創作 Line 與 Change 的 base Line 做比較。

緊鄰的上一版活動 layout-1 表示,即 44 位元組的 change.bin 加上按 ordinal 對齊 的 8 位元組 change_lifecycle.bin,只能作為離線轉換輸入。轉換器必須顯式收到那個 源表示,要求兩個檔案的記錄數相同,保留每個 Change 完整的 44 位元組字首,追加 配對的確切 base-Line 索引和歸檔時間,校驗得到的完整 52 位元組檔案,並在啟用的 目標中省略 change_lifecycle.bin。它不得推斷缺失的 base Line,也不得按任何 文字身份來對齊行。

更早的、旁路檔案出現之前的 layout-1 generation,只有在它宣告的源 schema 獨立 提供確切的 base_line 時,才同樣只能作為離線轉換輸入。轉換把該值解析為權威 本地的 line_index,要求任何提供的 forked_from_line 都等於那條 base Line, 並保留確切引用的存活 fork Snapshot,不比較也不重寫它的創作 Line。它保留提供的 合法源歸檔時間,只有當被接納的源格式可證明沒有歸檔時間欄位時,才寫入 archived_at_s = 0。提供的時間非法,或任何必需的非時間值不可得或有歧義,都會 在物化完整的 68 位元組 Change 檔案之前失敗拒絕。

Worktree 遊標#

程式碼 · text
WORKTREE_CURSOR_RECORD_SIZE = 28

WorktreeCursorRecord — .ait-worktree/cursor.bin:
u8  cursor_meta
u8  reserved0
u16 reserved1
u32 task_index
u32 selected_change_index_plus1
u32 pending_pre_land_target_snapshot_index_plus1
u32 pending_landed_snapshot_index_plus1
u64 updated_at_s

該遊標只在本地存在,是可丟棄的臨時狀態。它不是工作流程歷史,必須排除在內容 snapshot 之外。

Line 與 Stash 記錄#

程式碼 · text
LINE_RECORD_SIZE = 40

LineRecord — line.bin:
u8  line_meta
u8  reserved0
u16 line_name_len
u64 line_name_offset
u32 head_snapshot_index_plus1
u64 created_at_s
u64 updated_at_s
u64 archived_at_s
程式碼 · text
STASH_RECORD_SIZE = 8

StashRecord — stash.bin:
u8  stash_meta
u8  reserved0
u16 reserved1
u32 stash_snapshot_index

Stash 儲存只在本地。stash_snapshot_index 必須引用一個種類為 stash 的內容 snapshot。刪除 Stash 會給記錄打上墓碑,絕不移動後面的索引。

本地 Tag 記錄#

程式碼 · text
TAG_RECORD_SIZE = 24

TagRecord — tag.bin:
u8  tag_meta
u8  reserved0
u16 payload_len
u64 payload_offset
u32 snapshot_index
u64 created_at_s

tag_index 是穩定的內部 Tag 身份,也就是 TagRecordtag.bin 中從零開始 的 ordinal。公開的 Tag 身份仍然是它歸一化的、非空的 UTF-8 名稱。 snapshot_index 引用一個存活的內容 Snapshot。

建立 Tag 時先追加它帶型別的 payload,再寫它的固定記錄。強制替換一個已有名稱會 保留 tag_index,追加一份新的 payload,然後更新固定記錄;替換過程被打斷時, 可能只留下一份無人引用的 payload。刪除會設定墓碑位,絕不刪除被引用的 Snapshot, 也不移動 Tag 的 ordinal。重新建立同一個名稱只會清除墓碑位,同時保留同一個 tag_index

Land 記錄#

程式碼 · text
TASK_LAND_INDEX_RECORD_SIZE = 8

TaskLandIndexRecord — task_land_index.bin:
u32 latest_land_index_plus1
u16 land_count
u16 reserved0

CHANGE_LAND_INDEX_RECORD_SIZE = 8

ChangeLandIndexRecord — change_land_index.bin:
u32 latest_land_index_plus1
u16 land_count
u8  next_land_ordinal
u8  reserved0
程式碼 · text
LOCAL_LAND_RECORD_SIZE = 44

LocalLandRecord — land.bin:
u8  land_meta
u8  land_ordinal
u8  change_ordinal
u8  failure_kind
u32 change_index
u32 previous_task_land_index_plus1
u32 previous_change_land_index_plus1
u32 pre_land_target_snapshot_index_plus1
u32 landed_snapshot_index_plus1
u64 submitted_at_s
u64 updated_at_s
u32 target_line_index_plus1
程式碼 · text
SERVER_LAND_RECORD_SIZE = 48

ServerLandRecord — land.bin:
u8  land_meta
u8  land_ordinal
u8  change_ordinal
u8  failure_kind
u32 change_index
u32 patchset_index
u32 previous_task_land_index_plus1
u32 previous_change_land_index_plus1
u32 pre_land_target_snapshot_index_plus1
u32 landed_snapshot_index_plus1
u64 submitted_at_s
u64 updated_at_s
u32 target_line_index_plus1

本地 Land 權威就是儲存的那對 snapshot。服務端 Land 權威還儲存確切的被接受的 Patchset。保持同一個 L-## 的 Land 狀態更新,採用狀態最後寫入的兩階段寫。

land_ordinal(change_index, land_ordinal) 內唯一。值 0..63 在完整的 所屬 Change 身份之下渲染為 L-01..L-64。服務端 Land 的 patchset_index 是那次嘗試所接受的不可變 Patchset;它不會讓 Land 身份變成 Patchset 作用域的。因此一個 Change 可以同時保留針對某個 Patchset 的 L-01 和針對之後某個 Patchset 的 L-02ChangeLandIndexRecord 負責下一個 ordinal 的分配,它的 latest/previous 連結 遵循 Change 作用域的 Land ordinal。TaskLandIndexRecordprevious_task_land_index_plus1 只保留物理上的 Task 清單。

Land 模式存放在 land_meta 的位 5 到位 6。源的 directmergeff-only 對映到下面的固定編碼;未知模式失敗拒絕。源的結果物件不會變成 payload。目標 Line 身份以及 pre-land/landed 的 Snapshot 值來自固定的 Land 記錄;被阻塞結果的 blocker_class 對映到 failure_kindBASE_STALEPOLICY_BLOCKED 分別對映到 base_stalepolicy_blocked。任何無法從該 固定記錄精確推匯出來的、無法識別的 blocker 或結果值,都失敗拒絕。

原生 v0 的 Land 提交要求 submitted_at_s 非零。僅在離線遺留轉換中,一個已 land 的 Change,如果其源權威記錄了確切的 land 結果卻從未記錄提交時間,可以把 它第一個也是唯一一個成功的 Land 物化為:land_ordinal = 0、兩個 previous-Land 連結均為零、submitted_at_s = 0,以及 updated_at_s 等於被保留的源 landed_at。零表示未知的歷史提交時間;轉換器不得用 Change 的建立/更新時間或 轉換時間來替代。當目標 Line、landed Snapshot、必要時的被接受 Patchset,或者 land 時間缺失或有歧義時,本規則不允許重建;原生 v0 寫入方也絕不能建立提交時間 為零的新 Land。

內聯 Land 目標 Line 權威#

所屬 Land 記錄的 target_line_index_plus1 是必需的,引用提交 Land 時選定的那 條確切的目標 LineRecord。它屬於固定 schema,不是 payload。Land 絕不重複存放 目標 Line 名稱或文字形式的 Line ID。

存在時,pre_land_target_snapshot_index_plus1 引用目標 Line 更新之前被觀測為 其 head 的那個確切的存活 Snapshot,而 landed_snapshot_index_plus1 引用被裝入 為該 Line 新 head 的那個確切的存活 Snapshot。被引用 Snapshot 的 line_index_plus1 仍然是它不可變的創作 Line 身份,可能與 target_line_index_plus1 不同;Land 絕不給它重新貼標籤,也不把這兩個 Line 索引拿來做相等比較。已 land 的 Change 的公開 target_linelanded_at 值, 由它成功的那次 Land 的目標 Line 和 updated_at_s 推導;排隊中、執行中、被 阻塞、失敗、已取消和更新中的 Land 嘗試,即使兩個 Snapshot 都還不存在,也保留 它們的目標 Line。

被接納的遺留服務端格式可能為一個 Change 保留不止一次成功的 Land,也可能在一次 成功之後追加被阻塞、失敗、已取消或更新中的嘗試。轉換保留每一行 Land 及其確切 的被接受 Patchset。只要有任何一次存活的 Land 成功過,無論重複的源 Change 狀態 如何,Change 的生命週期都歸一化為 landed。land_ordinal 最大的那次成功 Land 提供公開的目標 Line 和 land 時間;之後未成功的嘗試仍然是不可變的歷史,不會 撤銷已完成的 land。在那次成功之後建立的 selected/current Patchset,仍然是獨立 的可變 Change 權威,不必等於被接受的 Patchset。聲稱已 land 卻沒有可證明的成功 Land 的源 Change,只能遵循下文明確的重建規則或點名的省略規則;它絕不會被隱式 地補上一個偽造的 Land。

當存在確切的 Land 權威時,遺留 Change 的 landed_at 是一份非權威的重複投影。 轉換會把提供的值作為時間戳來校驗,但不要求它等於任何 Land 時間,也不儲存它; 權威的公開時間是最近一次成功 Land 的 updated_at_s。同樣地,提交之後,Change 的可變 selected-Patchset 指標與某次歷史 Land 確切的被接受 Patchset 是各自獨立 的權威。轉換保留兩者,並不要求它們保持相等。

提交 Land 時透過精確的 payload 比較來解析歸一化的 Line 名稱,並追加和 fsync 一條完整的 Land 提交記錄,其中包含必需的目標 Line 引用。對於已提交的 Land, 恢復流程會拒絕:目標 Line 為零或缺失、被引用的 Snapshot 缺失或已打墓碑,或者 狀態與 Snapshot 存在與否不一致。恢復流程不會把被引用 Snapshot 的創作 Line 與 Land 的目標 Line 做比較。

緊鄰的上一版 layout-1 表示,即 32 位元組的 Local 或 36 位元組的 Server Land 記錄, 加上按 ordinal 對齊的 4 位元組 land_target_line.bin,只能作為離線轉換輸入。 轉換要求頭部匹配且記錄數完全一致,把每個確切的目標 Line 值追加到未改動的 Land 字首之後,校驗每一條加寬後的記錄,省略退役的旁路檔案,並在輸入為零、缺失、 多餘、錯位或有歧義時失敗拒絕。原始檔不被修改。

更早的、旁路檔案出現之前、沒有 land_target_line.bin 的 layout-1 generation, 同樣只能作為離線轉換輸入。轉換把確切的源 target_line 解析為權威本地的 line_index,保留每一處確切儲存的存活 Snapshot 引用,不比較也不重寫它的創作 Line;如果在物化完整的加寬 Land 記錄之前,目標或某個被引用的 Snapshot 不可得 或有歧義,則失敗拒絕。

版本權威

對照 1.1.1 原始碼逐條核對

這一頁是公開文件,不是第二份產品契約。要確認發布權威,請以確切的原始碼和分發契約為準。

所屬元件 Snapshot
  • ait-coreSNP-ED7593DBF982
  • ait-serverSNP-0CCD7DD2A077
  • ait-runnerSNP-35C9C133D2EE
  • ait-pythonSNP-756C731A4CC0
  • ait-nodeSNP-55E90D0A81F1