返回 DeepSeek-Reasonix
cold_session_diagnostics.go
根目录 / internal / control / cold_session_diagnostics.go
1 package control
2
3 import (
4 "context"
5 "encoding/json"
6 "errors"
7 "io"
8 "sort"
9
10 "reasonix/internal/secrets"
11 "reasonix/internal/session"
12 )
13
14 // WriteColdSessionDiagnostics serializes the durable evidence of a fixed
15 // snapshot without starting or taking ownership of a session runtime.
16 func WriteColdSessionDiagnostics(ctx context.Context, dst io.Writer, query *session.Query, snapshot session.ExportSnapshot, metadata GoalDiagnosticMetadata, extra map[string]any) error {
17 if query == nil || snapshot.Ref.SessionID == "" {
18 return errors.New("cold session diagnostics require a canonical snapshot")
19 }
20 if err := query.ValidateExportSourceForRef(snapshot.Ref, snapshot); err != nil {
21 return err
22 }
23 if metadata.Capabilities == nil {
24 metadata.Capabilities = []string{}
25 }
26 fillGoalDiagnosticBuildMetadata(&metadata)
27 unavailable := []string{"runtime, submissionDiagnostics, shellDiagnostics and process-local lifecycle history are unavailable for a cold session"}
28 if snapshot.ReadIncomplete {
29 unavailable = append(unavailable, "snapshot capture encountered unreadable durable events; only the readable prefix is available")
30 }
31 if _, err := io.WriteString(dst, "{\n"); err != nil {
32 return err
33 }
34 fields := []struct {
35 name string
36 value any
37 }{
38 {"schemaVersion", 1},
39 {"exportedAt", snapshot.CapturedAt},
40 {"metadata", metadata},
41 {"runtime", nil},
42 {"observation", nil},
43 {"submissionDiagnostics", nil},
44 {"shellDiagnostics", nil},
45 {"lifecycleDiagnostics", nil},
46 }
47 for _, field := range fields {
48 if err := writeGoalDiagnosticField(dst, field.name, field.value, true); err != nil {
49 return err
50 }
51 }
52 names := make([]string, 0, len(extra))
53 for name := range extra {
54 names = append(names, name)
55 }
56 sort.Strings(names)
57 for _, name := range names {
58 encoded, err := json.Marshal(extra[name])
59 if err != nil {
60 return err
61 }
62 redacted := json.RawMessage(secrets.Redact(string(encoded)))
63 if !json.Valid(redacted) {
64 return errors.New("invalid redacted diagnostic field")
65 }
66 if err = writeGoalDiagnosticField(dst, name, redacted, true); err != nil {
67 return err
68 }
69 }
70 through, traversalErr, err := writeColdDiagnosticCommits(ctx, dst, query, snapshot)
71 if err != nil {
72 return err
73 }
74 if traversalErr != nil {
75 unavailable = append(unavailable, "durable event traversal failed: "+secrets.RedactError(traversalErr))
76 }
77 if err = writeGoalDiagnosticField(dst, "activationChanges", []goalDiagnosticTransition{}, true); err != nil {
78 return err
79 }
80 if err = writeGoalDiagnosticField(dst, "acceptedThrough", through, true); err != nil {
81 return err
82 }
83 if err = writeGoalDiagnosticField(dst, "durableThrough", through, true); err != nil {
84 return err
85 }
86 if err = writeGoalDiagnosticField(dst, "unavailable", unavailable, false); err != nil {
87 return err
88 }
89 _, err = io.WriteString(dst, "}\n")
90 return err
91 }
92
93 func writeColdDiagnosticCommits(ctx context.Context, dst io.Writer, query *session.Query, snapshot session.ExportSnapshot) (through uint64, traversalErr, err error) {
94 if _, err = io.WriteString(dst, " \"commits\": ["); err != nil {
95 return 0, nil, err
96 }
97 first := true
98 var destinationError error
99 traversalErr = query.StreamExportCommits(ctx, snapshot, func(commit session.Commit) error {
100 if commit.LastSequence() > snapshot.SnapshotSequence {
101 return nil
102 }
103 encoded, marshalErr := json.MarshalIndent(commit, " ", " ")
104 if marshalErr != nil {
105 return marshalErr
106 }
107 encoded = []byte(secrets.Redact(string(encoded)))
108 if !json.Valid(encoded) {
109 return errors.New("redacted cold diagnostic commit is not valid JSON")
110 }
111 separator := "\n "
112 if !first {
113 separator = ",\n "
114 }
115 if _, writeErr := io.WriteString(dst, separator); writeErr != nil {
116 destinationError = writeErr
117 return writeErr
118 }
119 if _, writeErr := dst.Write(encoded); writeErr != nil {
120 destinationError = writeErr
121 return writeErr
122 }
123 first = false
124 through = commit.LastSequence()
125 return nil
126 })
127 if destinationError != nil {
128 return 0, nil, destinationError
129 }
130 if err = ctx.Err(); err != nil {
131 return 0, nil, err
132 }
133 if !first {
134 if _, err = io.WriteString(dst, "\n "); err != nil {
135 return 0, nil, err
136 }
137 }
138 if _, err = io.WriteString(dst, "],\n"); err != nil {
139 return 0, nil, err
140 }
141 return through, traversalErr, nil
142 }
143
143 lines GO