返回 DeepSeek-Reasonix
types.go
根目录 / internal / checkpoint / types.go
1 package checkpoint
2
3 import (
4 "time"
5
6 fileenc "reasonix/internal/fileutil/encoding"
7 )
8
9 // Schema versions for on-disk checkpoint JSON.
10 const (
11 SchemaV1 = 1
12 SchemaV2 = 2
13 )
14
15 // Coverage describes how completely a checkpoint captured workspace mutations.
16 type Coverage string
17
18 const (
19 CoverageComplete Coverage = "complete"
20 CoveragePartial Coverage = "partial"
21 CoverageNone Coverage = "none"
22 CoverageLegacy Coverage = "legacy"
23 )
24
25 // CoverageGap records why a checkpoint cannot guarantee full file restore.
26 type CoverageGap struct {
27 Reason string `json:"reason"`
28 Detail string `json:"detail,omitempty"`
29 Tool string `json:"tool,omitempty"`
30 Path string `json:"path,omitempty"`
31 }
32
33 // Common coverage-gap reasons.
34 const (
35 GapBashSideEffect = "bash_side_effect"
36 GapHookWrite = "hook_write"
37 GapMCPExternal = "mcp_external"
38 GapOutsideWorkspace = "outside_workspace"
39 GapSymlink = "symlink"
40 GapHardlink = "hardlink"
41 GapUnreadable = "unreadable"
42 GapOversized = "oversized"
43 GapBackgroundWriter = "background_writer_cross_turn"
44 GapLegacyUnverified = "legacy_unverified"
45 GapCaptureFailed = "capture_failed"
46 GapExpiredPayload = "expired_file_payload"
47 )
48
49 // CaptureSource identifies how a preimage was obtained.
50 type CaptureSource string
51
52 const (
53 CapturePreviewer CaptureSource = "previewer"
54 CaptureBeforeMutation CaptureSource = "before_mutation"
55 CaptureAfterMutation CaptureSource = "after_mutation"
56 CaptureLegacy CaptureSource = "legacy"
57 CaptureManual CaptureSource = "manual"
58 )
59
60 // FileRevision is the v2 per-file preimage plus last Reasonix-owned after fingerprint.
61 type FileRevision struct {
62 Path string `json:"path"`
63 Existed bool `json:"existed"`
64 Mode uint32 `json:"mode,omitempty"`
65 Encoding *fileenc.Kind `json:"encoding,omitempty"`
66 SHA256 string `json:"sha256,omitempty"`
67 BlobRef string `json:"blobRef,omitempty"`
68 CaptureSource CaptureSource `json:"captureSource,omitempty"`
69 // AfterSHA256 is the fingerprint of the file after the last Reasonix-owned write.
70 // Empty means "no after fingerprint" (legacy or never observed).
71 AfterSHA256 string `json:"afterSha256,omitempty"`
72 AfterExisted *bool `json:"afterExisted,omitempty"`
73 AfterMode uint32 `json:"afterMode,omitempty"`
74 // Inline content is only used for in-memory stores without a blob dir, and
75 // for legacy v1 migration paths. Persisted v2 checkpoints prefer BlobRef.
76 Content *string `json:"content,omitempty"`
77 }
78
79 // MutationRecord tracks one observed mutation for ownership and conflict detection.
80 type MutationRecord struct {
81 Seq int64 `json:"seq"`
82 Path string `json:"path"`
83 Tool string `json:"tool,omitempty"`
84 Source CaptureSource `json:"source,omitempty"`
85 WriterID string `json:"writerId,omitempty"`
86 Turn int `json:"turn"`
87 BeforeSHA string `json:"beforeSha,omitempty"`
88 AfterSHA string `json:"afterSha,omitempty"`
89 Time time.Time `json:"time,omitempty"`
90 }
91
92 // ActiveWriter describes a background writer that still owns open mutations.
93 type ActiveWriter struct {
94 ID string `json:"id"`
95 Turn int `json:"turn"`
96 StartedAt time.Time `json:"startedAt,omitempty"`
97 Kind string `json:"kind,omitempty"` // "background_subagent", ...
98 }
99
100 // RewindScope selects what a rewind restores. Mirrors control.RewindScope without
101 // importing control (checkpoint is a lower layer).
102 type RewindScope int
103
104 const (
105 RewindCode RewindScope = iota // files only
106 RewindConversation // message log only
107 RewindBoth // both
108 )
109
110 // RewindConflict describes a file that cannot be safely restored.
111 type RewindConflict struct {
112 Path string `json:"path"`
113 Reason string `json:"reason"`
114 CheckpointSHA string `json:"checkpointSha,omitempty"`
115 LastOwnedSHA string `json:"lastOwnedSha,omitempty"`
116 CurrentSHA string `json:"currentSha,omitempty"`
117 CheckpointMode uint32 `json:"checkpointMode,omitempty"`
118 CurrentMode uint32 `json:"currentMode,omitempty"`
119 CurrentExisted bool `json:"currentExisted"`
120 CheckpointExist bool `json:"checkpointExisted"`
121 }
122
123 // Conflict reason constants.
124 const (
125 ConflictManualEdit = "manual_edit"
126 ConflictExternalChange = "external_change"
127 ConflictDeletedRecreate = "deleted_and_recreated"
128 ConflictTypeChange = "type_change"
129 ConflictModeChange = "mode_change"
130 ConflictMissingPayload = "missing_payload"
131 ConflictPathUnsafe = "path_unsafe"
132 ConflictBusyWriter = "active_writer"
133 ConflictStalePlan = "stale_plan"
134 ConflictBoundaryInvalid = "boundary_invalid"
135 ConflictCoverageLegacy = "legacy_unverified"
136 ConflictExpired = "expired_payload"
137 )
138
139 // FileStage records per-file progress through a rewind transaction.
140 type FileStage struct {
141 Path string `json:"path"`
142 Phase string `json:"phase"` // precheck|prepare|commit|compensate|done|skipped
143 Action string `json:"action,omitempty"` // write|delete|restore
144 Error string `json:"error,omitempty"`
145 Compensated bool `json:"compensated,omitempty"`
146 CompError string `json:"compensateError,omitempty"`
147 }
148
149 // RewindPlan is the structured precheck result returned to the controller/UI.
150 type RewindPlan struct {
151 PlanID string `json:"planId"`
152 Turn int `json:"turn"`
153 Scope RewindScope `json:"scope"`
154 Coverage Coverage `json:"coverage"`
155 CoverageGaps []CoverageGap `json:"coverageGaps,omitempty"`
156 Legacy bool `json:"legacy,omitempty"`
157 ExpiredFilePayload bool `json:"expiredFilePayload,omitempty"`
158 CanFiles bool `json:"canFiles"`
159 CanConversation bool `json:"canConversation"`
160 DisabledReason string `json:"disabledReason,omitempty"`
161 Conflicts []RewindConflict `json:"conflicts,omitempty"`
162 Files []string `json:"files,omitempty"`
163 FileCount int `json:"fileCount"`
164 ActiveWriters []ActiveWriter `json:"activeWriters,omitempty"`
165 SessionRevision int64 `json:"sessionRevision"`
166 WorkspaceToken string `json:"workspaceToken,omitempty"`
167 BoundaryIndex int `json:"boundaryIndex,omitempty"`
168 HasBoundary bool `json:"hasBoundary"`
169 CreatedAt time.Time `json:"createdAt"`
170 // Single-file revert extras.
171 Path string `json:"path,omitempty"`
172 ConflictResolution string `json:"conflictResolution,omitempty"`
173 }
174
175 // RewindResult is returned after commit or undo.
176 type RewindResult struct {
177 OK bool `json:"ok"`
178 TransactionID string `json:"transactionId,omitempty"`
179 UndoAvailable bool `json:"undoAvailable"`
180 Written []string `json:"written,omitempty"`
181 Deleted []string `json:"deleted,omitempty"`
182 Files []FileStage `json:"files,omitempty"`
183 ConversationOK bool `json:"conversationOk,omitempty"`
184 Error string `json:"error,omitempty"`
185 Conflicts []RewindConflict `json:"conflicts,omitempty"`
186 Coverage Coverage `json:"coverage,omitempty"`
187 CoverageGaps []CoverageGap `json:"coverageGaps,omitempty"`
188 }
189
190 // ConflictResolution chooses how to handle a single-file conflict on commit.
191 type ConflictResolution string
192
193 const (
194 // ResolveKeepCurrent leaves the on-disk file alone.
195 ResolveKeepCurrent ConflictResolution = "keep_current"
196 // ResolveOverwriteCheckpoint force-writes the checkpoint preimage after
197 // the user explicitly confirmed in the single-file UI.
198 ResolveOverwriteCheckpoint ConflictResolution = "overwrite_checkpoint"
199 )
200
201 // TransactionState is the durable lifecycle of a rewind transaction.
202 type TransactionState string
203
204 const (
205 TxPrepared TransactionState = "prepared"
206 TxCommitting TransactionState = "committing"
207 TxCommitted TransactionState = "committed"
208 TxAborted TransactionState = "aborted"
209 TxUndone TransactionState = "undone"
210 )
211
212 // TransactionTarget is one file's forward/restore payload inside a transaction.
213 type TransactionTarget struct {
214 Path string `json:"path"`
215 AbsPath string `json:"absPath"`
216 // Restore: what to write (or delete) to reach checkpoint state.
217 RestoreExisted bool `json:"restoreExisted"`
218 RestoreMode uint32 `json:"restoreMode,omitempty"`
219 RestoreSHA string `json:"restoreSha,omitempty"`
220 RestoreBlob string `json:"restoreBlob,omitempty"`
221 RestoreInline []byte `json:"restoreInline,omitempty"`
222 RestoreEncoding *fileenc.Kind `json:"restoreEncoding,omitempty"`
223 // Forward: current on-disk state at prepare time (for compensate / undo).
224 ForwardExisted bool `json:"forwardExisted"`
225 ForwardMode uint32 `json:"forwardMode,omitempty"`
226 ForwardSHA string `json:"forwardSha,omitempty"`
227 ForwardBlob string `json:"forwardBlob,omitempty"`
228 ForwardInline []byte `json:"forwardInline,omitempty"`
229 // Staging paths are transaction-unique siblings of AbsPath so publish and
230 // compensation never cross filesystems.
231 PublishTmp string `json:"publishTmp,omitempty"`
232 BackupPath string `json:"backupPath,omitempty"`
233 // Published is a durable "may have published" intent. It is persisted before
234 // the first rename so crash recovery conservatively inspects this target.
235 Published bool `json:"published"`
236 // Action describes the intended commit action.
237 Action string `json:"action"` // write|delete
238 }
239
240 // TransactionManifest is the durable description of a rewind/undo transaction.
241 type TransactionManifest struct {
242 SchemaVersion int `json:"schemaVersion"`
243 ID string `json:"id"`
244 SessionID string `json:"sessionId,omitempty"`
245 WorkspaceRoot string `json:"workspaceRoot"`
246 State TransactionState `json:"state"`
247 Kind string `json:"kind"` // rewind|undo|file_revert
248 Turn int `json:"turn"`
249 Scope RewindScope `json:"scope"`
250 Path string `json:"path,omitempty"` // single-file
251 CreatedAt time.Time `json:"createdAt"`
252 UpdatedAt time.Time `json:"updatedAt"`
253 SessionRevision int64 `json:"sessionRevision"`
254 WorkspaceToken string `json:"workspaceToken,omitempty"`
255 Coverage Coverage `json:"coverage,omitempty"`
256 CoverageGaps []CoverageGap `json:"coverageGaps,omitempty"`
257 Targets []TransactionTarget `json:"targets,omitempty"`
258 // ConversationForward holds a JSON-encoded message snapshot when conversation
259 // is part of the transaction. Opaque to this package so it can stay free of
260 // provider imports; the controller supplies and applies it.
261 ConversationForward []byte `json:"conversationForward,omitempty"`
262 BoundaryIndex int `json:"boundaryIndex,omitempty"`
263 HasBoundary bool `json:"hasBoundary"`
264 // TruncateFrom is the checkpoint turn to drop after a successful conversation rewind.
265 TruncateFrom int `json:"truncateFrom,omitempty"`
266 // CheckpointTurns holds serialized future checkpoints for undo.
267 CheckpointBackup []byte `json:"checkpointBackup,omitempty"`
268 // ParentTransaction is set for undo transactions that reverse a committed rewind.
269 ParentTransaction string `json:"parentTransaction,omitempty"`
270 Error string `json:"error,omitempty"`
271 }
272
273 // Default retention for file payloads.
274 const (
275 DefaultRetainCheckpoints = 100
276 DefaultBlobQuotaBytes = 1 << 30 // 1 GiB
277 DefaultMaxFileBytes = 32 << 20 // 32 MiB per file capture
278 )
279
279 lines GO