返回 DeepSeek-Reasonix
review_report.go
根目录 / internal / agent / review_report.go
1 package agent
2
3 import (
4 "context"
5 "encoding/json"
6 "fmt"
7 "path/filepath"
8 "strings"
9
10 "reasonix/internal/evidence"
11 "reasonix/internal/tool"
12 )
13
14 // ReviewReportTool is visible only inside review/security_review subagent
15 // registries. It submits a structured review result the host uses for
16 // Delivery risk gates. It is never registered on the parent agent tool surface.
17 type ReviewReportTool struct{}
18
19 func NewReviewReportTool() *ReviewReportTool { return &ReviewReportTool{} }
20
21 func (*ReviewReportTool) Name() string { return "review_report" }
22
23 func (*ReviewReportTool) Description() string {
24 return "Submit a structured review result for the parent delivery gate. Call once when the review is complete. kind is review or security; verdict is pass, warn, or block; reviewed_paths must cover the production paths you inspected; findings list severity/summary/path/line."
25 }
26
27 func (*ReviewReportTool) ReadOnly() bool { return true }
28
29 func (*ReviewReportTool) Schema() json.RawMessage {
30 // Fixed schema — stable for review subagents only.
31 return json.RawMessage(`{
32 "type":"object",
33 "properties":{
34 "kind":{"type":"string","description":"review | security"},
35 "verdict":{"type":"string","description":"pass | warn | block"},
36 "reviewed_paths":{"type":"array","items":{"type":"string"},"description":"Production paths covered by this review"},
37 "findings":{"type":"array","items":{"type":"object","properties":{
38 "severity":{"type":"string"},
39 "summary":{"type":"string"},
40 "path":{"type":"string"},
41 "line":{"type":"integer"}
42 },"required":["severity","summary"]}}
43 },
44 "required":["kind","verdict","reviewed_paths"]
45 }`)
46 }
47
48 func (*ReviewReportTool) Execute(ctx context.Context, args json.RawMessage) (string, error) {
49 report, err := evidence.ParseReviewReport(args)
50 if err != nil {
51 return "", err
52 }
53 // reviewed_paths is a host-verified claim, not a model attestation: every
54 // path must be backed by a successful read/diff receipt in this subagent's
55 // own evidence ledger. Without that check a subagent could "cover" files
56 // it never opened and the parent delivery gate would trust it.
57 led, ok := evidence.FromContext(ctx)
58 if !ok {
59 return "", fmt.Errorf("review_report requires the host evidence ledger; submit it from inside a review subagent run")
60 }
61 var unread []string
62 for _, p := range report.ReviewedPaths {
63 if led.HasReadEvidenceForPath(p) {
64 continue
65 }
66 // Slash-canonical display keeps the message (and tests) identical
67 // across OSes even though ParseReviewReport normalized with the
68 // platform separator.
69 unread = append(unread, filepath.ToSlash(p))
70 }
71 if len(unread) > 0 {
72 return "", fmt.Errorf("review_report rejected: no host-observed read evidence for: %s — read these files (or run git diff on them) before reporting them as reviewed", strings.Join(unread, ", "))
73 }
74 // Evidence is recorded by the agent host from the tool call args; this
75 // result is a human-readable confirmation for the subagent transcript.
76 msg := fmt.Sprintf("review_report accepted: kind=%s verdict=%s paths=%d findings=%d",
77 report.Kind, report.Verdict, len(report.ReviewedPaths), len(report.Findings))
78 if report.HasBlockingFinding() {
79 msg += " (blocking — parent delivery will require fixes and re-review)"
80 }
81 return msg, nil
82 }
83
84 // ReviewReportKindForSkill maps a review-capable skill name to the report kind
85 // its subagent must submit before finishing; empty means no requirement.
86 func ReviewReportKindForSkill(name string) evidence.ReviewKind {
87 switch name {
88 case "review":
89 return evidence.ReviewKindReview
90 case "security-review", "security_review":
91 return evidence.ReviewKindSecurity
92 }
93 return ""
94 }
95
96 var _ tool.Tool = (*ReviewReportTool)(nil)
97
98 // AttachReviewReportTool adds review_report to a subagent registry used by
99 // review / security_review skills only.
100 func AttachReviewReportTool(reg *tool.Registry) {
101 if reg == nil {
102 return
103 }
104 reg.Add(NewReviewReportTool())
105 }
106
107 // HasSuccessfulReviewReport reports whether this agent's evidence ledger holds
108 // a successful review_report of the given kind.
109 func (a *Agent) HasSuccessfulReviewReport(kind evidence.ReviewKind) bool {
110 if a == nil || a.evidence == nil {
111 return false
112 }
113 return a.evidence.HasSuccessfulReviewReportOfKind(kind)
114 }
115
115 lines GO