返回 DeepSeek-Reasonix
topic_removal.go
根目录 / desktop / internal / workspacestate / topic_removal.go
1 package workspacestate
2
3 import (
4 "context"
5 "encoding/json"
6
7 "reasonix/internal/fileutil"
8 )
9
10 // TopicRemoval journals metadata-only removals independently of session
11 // operations. Previous registry readers preserve this optional collection.
12 type TopicRemoval struct {
13 ID string `json:"id"`
14 WorkspaceID string `json:"workspaceId"`
15 TopicID string `json:"topicId"`
16 Token string `json:"token"`
17 SessionToken string `json:"sessionToken,omitempty"`
18 Disposition string `json:"disposition"`
19 Phase string `json:"phase"`
20 ArchivedAt int64 `json:"archivedAt"`
21 Snapshot json.RawMessage `json:"snapshot,omitempty"`
22 Metadata json.RawMessage `json:"metadata,omitempty"`
23 RestoreRevision int64 `json:"restoreRevision,omitempty"`
24 extra map[string]json.RawMessage
25 }
26
27 // TransitionTopicRemoval holds the registry's cross-process writer lock across
28 // validation and external metadata writes. checkpoint durably saves intent
29 // BEFORE an external effect; errors retain that intent for a fenced retry.
30 // Neither callback may call this Store. Callers follow registry -> projects ->
31 // topic-store lock order inside the transaction.
32 func (s *Store) TransitionTopicRemoval(ctx context.Context, id string, transaction func(State, *TopicRemoval, func() error) error) error {
33 return s.mutate(ctx, func(state *State) error {
34 item := state.TopicRemovals[id]
35 checkpoint := func() error {
36 state.TopicRemovals[id] = item
37 state.Generation++
38 state.Initialized = true
39 if err := validate(*state); err != nil {
40 return err
41 }
42 body, err := json.Marshal(state)
43 if err != nil {
44 return err
45 }
46 return fileutil.AtomicWriteFileStrict(s.path, append(body, '\n'), 0o600)
47 }
48 if err := transaction(*state, &item, checkpoint); err != nil {
49 return err
50 }
51 state.TopicRemovals[id] = item
52 return nil
53 })
54 }
55
56 func (r *TopicRemoval) UnmarshalJSON(body []byte) error {
57 type plain TopicRemoval
58 var decoded plain
59 if err := json.Unmarshal(body, &decoded); err != nil {
60 return err
61 }
62 var fields map[string]json.RawMessage
63 if err := json.Unmarshal(body, &fields); err != nil {
64 return err
65 }
66 for _, key := range []string{"id", "workspaceId", "topicId", "token", "sessionToken", "disposition", "phase", "archivedAt", "snapshot", "metadata", "restoreRevision"} {
67 delete(fields, key)
68 }
69 *r = TopicRemoval(decoded)
70 r.extra = fields
71 return nil
72 }
73
74 func (r TopicRemoval) MarshalJSON() ([]byte, error) {
75 type plain TopicRemoval
76 body, err := json.Marshal(plain(r))
77 if err != nil {
78 return nil, err
79 }
80 return mergeUnknown(body, r.extra)
81 }
82
82 lines GO