返回 DeepSeek-Reasonix
branch_metadata_read.go
根目录 / internal / agent / branch_metadata_read.go
1 package agent
2
3 import (
4 "context"
5 "encoding/json"
6 "fmt"
7 "io"
8 "os"
9 fileencoding "reasonix/internal/fileutil/encoding"
10 "reasonix/internal/historywork"
11 )
12
13 // LoadBranchMetaBounded is the content-free catalog reader. An oversized or
14 // damaged sidecar leaves an unknown metadata row; it never triggers replay.
15 func LoadBranchMetaBounded(ctx context.Context, sessionPath string) (BranchMeta, bool, error) {
16 f, err := os.Open(BranchMetaPath(sessionPath))
17 if os.IsNotExist(err) {
18 return BranchMeta{}, false, nil
19 }
20 if err != nil {
21 return BranchMeta{}, false, err
22 }
23 defer f.Close()
24 r := &historywork.Reader{Context: ctx, Source: f}
25 b, err := io.ReadAll(io.LimitReader(r, historywork.ReadChunk+1))
26 if err != nil {
27 return BranchMeta{}, false, err
28 }
29 if len(b) > historywork.ReadChunk {
30 return BranchMeta{}, false, fmt.Errorf("session metadata exceeds discovery budget")
31 }
32 kind, _ := fileencoding.Detect(b)
33 return decodeBranchMeta(sessionPath, fileencoding.Decode(b, kind))
34 }
35
36 func decodeBranchMeta(sessionPath string, b []byte) (BranchMeta, bool, error) {
37 var m BranchMeta
38 if err := json.Unmarshal(b, &m); err != nil {
39 // Treat an all-NUL/JSON-whitespace sidecar as a torn write so callers
40 // rebuild it; retain errors for partial JSON to avoid swallowing corruption.
41 if metaIsUnparseableAsAbsent(b) {
42 return BranchMeta{}, false, nil
43 }
44 return BranchMeta{}, false, fmt.Errorf("decode branch meta %s: %w", BranchMetaPath(sessionPath), err)
45 }
46 if m.ID == "" {
47 m.ID = BranchID(sessionPath)
48 }
49 m.sanitizeDisplayFields()
50 return m, true, nil
51 }
52
52 lines GO