@orangepro/orangepro-mcp 0.1.0 → 0.2.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,182 @@
1
+ //go:build ignore
2
+
3
+ // go-mutate.go — AST-based body replacer for the Go dynamic-proof spike (G-1).
4
+ //
5
+ // Locates ONE free function `func Name(params) rets { ... }` by exact name and
6
+ // replaces its BODY with a signature-derived sentinel, then writes the mutated
7
+ // file. It is a spike helper only: it writes no product artifacts and is not
8
+ // wired into prove/RTM/mint. G-1 scope is FREE FUNCTIONS ONLY — methods
9
+ // (`func (r Recv) M()`) are recognized and refused as out-of-scope (G-2).
10
+ //
11
+ // Modes:
12
+ // sentinel — replace body with a type-compatible, deliberately-wrong value
13
+ // (zero values). Zero-valued returns can only ever cause a FALSE
14
+ // SURVIVE, never a false Proven, so the trust bias is safe.
15
+ // equivalent — leave the body semantically unchanged (re-print identical
16
+ // statements) so a value-only test still passes -> the
17
+ // orchestrator classifies it associated_survived.
18
+ //
19
+ // Exit codes (distinct, so the Node orchestrator can classify precisely):
20
+ // 0 ok, mutated file written
21
+ // 2 usage / IO / parse error
22
+ // 3 ambiguous: more than one free function with that name
23
+ // 4 not found: no free function with that name
24
+ // 5 out of scope: a METHOD with that name exists (G-2, refused in G-1)
25
+ // 6 not mutable: the function has no return values (no signature-derived
26
+ // sentinel is possible) -> fail closed, never mutated
27
+ package main
28
+
29
+ import (
30
+ "bytes"
31
+ "flag"
32
+ "fmt"
33
+ "go/ast"
34
+ "go/parser"
35
+ "go/printer"
36
+ "go/token"
37
+ "os"
38
+ )
39
+
40
+ // fail prints a stable, machine-readable marker plus a human message and exits
41
+ // with the given code. The marker matters because callers run this via
42
+ // `go run`, which collapses any non-zero child status to 1 — so the Node
43
+ // orchestrator classifies on the MUTATE_ERROR:<code> line, not the exit code.
44
+ // The distinct os.Exit code is still set for direct (compiled) callers.
45
+ func fail(code int, format string, args ...any) {
46
+ fmt.Fprintf(os.Stderr, "MUTATE_ERROR:%d\n", code)
47
+ fmt.Fprintf(os.Stderr, format+"\n", args...)
48
+ os.Exit(code)
49
+ }
50
+
51
+ func main() {
52
+ file := flag.String("file", "", "path to the Go source file to mutate")
53
+ fn := flag.String("func", "", "exact name of the free function to mutate")
54
+ out := flag.String("out", "", "path to write the mutated file (defaults to --file)")
55
+ mode := flag.String("mode", "sentinel", "sentinel | equivalent")
56
+ flag.Parse()
57
+
58
+ if *file == "" || *fn == "" {
59
+ fail(2, "usage: go run go-mutate.go --file <path> --func <name> [--out <path>] [--mode sentinel|equivalent]")
60
+ }
61
+ if *mode != "sentinel" && *mode != "equivalent" {
62
+ fail(2, "--mode must be sentinel or equivalent")
63
+ }
64
+ dst := *out
65
+ if dst == "" {
66
+ dst = *file
67
+ }
68
+
69
+ fset := token.NewFileSet()
70
+ astFile, err := parser.ParseFile(fset, *file, nil, parser.ParseComments)
71
+ if err != nil {
72
+ fail(2, "parse error: %v", err)
73
+ }
74
+
75
+ var freeMatches []*ast.FuncDecl
76
+ methodMatch := false
77
+ for _, decl := range astFile.Decls {
78
+ fd, ok := decl.(*ast.FuncDecl)
79
+ if !ok || fd.Name == nil || fd.Name.Name != *fn {
80
+ continue
81
+ }
82
+ if fd.Recv != nil { // method -> out of scope for G-1
83
+ methodMatch = true
84
+ continue
85
+ }
86
+ freeMatches = append(freeMatches, fd)
87
+ }
88
+
89
+ if len(freeMatches) > 1 {
90
+ fail(3, "ambiguous: %d free functions named %q", len(freeMatches), *fn)
91
+ }
92
+ if len(freeMatches) == 0 {
93
+ if methodMatch {
94
+ fail(5, "out of scope: %q is a method (G-2); G-1 handles free functions only", *fn)
95
+ }
96
+ fail(4, "not found: no free function named %q", *fn)
97
+ }
98
+
99
+ target := freeMatches[0]
100
+ results := target.Type.Results
101
+ if results == nil || len(results.List) == 0 {
102
+ fail(6, "not mutable: %q has no return values; no signature-derived sentinel is possible", *fn)
103
+ }
104
+
105
+ if *mode == "sentinel" {
106
+ target.Body = sentinelBody(results)
107
+ }
108
+ // equivalent mode: leave target.Body untouched (semantically identical).
109
+
110
+ var buf bytes.Buffer
111
+ cfg := printer.Config{Mode: printer.UseSpaces | printer.TabIndent, Tabwidth: 8}
112
+ if err := cfg.Fprint(&buf, fset, astFile); err != nil {
113
+ fail(2, "print error: %v", err)
114
+ }
115
+ if err := os.WriteFile(dst, buf.Bytes(), 0o644); err != nil {
116
+ fail(2, "write error: %v", err)
117
+ }
118
+ }
119
+
120
+ // sentinelBody builds `{ return <zero>, <zero>, ... }` matching the function's
121
+ // result signature so the mutant COMPILES. Zero values are type-derived and
122
+ // deliberately wrong for any function whose real return is non-zero; when a
123
+ // function legitimately returns a zero value the sentinel merely SURVIVES
124
+ // (safe: never a false Proven).
125
+ func sentinelBody(results *ast.FieldList) *ast.BlockStmt {
126
+ var exprs []ast.Expr
127
+ for _, field := range results.List {
128
+ n := len(field.Names)
129
+ if n == 0 {
130
+ n = 1 // an unnamed result still contributes one value
131
+ }
132
+ for i := 0; i < n; i++ {
133
+ exprs = append(exprs, zeroValue(field.Type))
134
+ }
135
+ }
136
+ return &ast.BlockStmt{
137
+ List: []ast.Stmt{
138
+ &ast.ReturnStmt{Results: exprs},
139
+ },
140
+ }
141
+ }
142
+
143
+ // zeroValue returns an expression that is the zero value of the given type.
144
+ // It covers the common cases; anything it cannot name concretely falls back to
145
+ // a type-conversion of nil-like zero via a composite/`*new(T)` form that always
146
+ // compiles.
147
+ func zeroValue(t ast.Expr) ast.Expr {
148
+ switch tt := t.(type) {
149
+ case *ast.Ident:
150
+ switch tt.Name {
151
+ case "int", "int8", "int16", "int32", "int64",
152
+ "uint", "uint8", "uint16", "uint32", "uint64", "uintptr",
153
+ "byte", "rune", "float32", "float64", "complex64", "complex128":
154
+ return &ast.BasicLit{Kind: token.INT, Value: "0"}
155
+ case "string":
156
+ return &ast.BasicLit{Kind: token.STRING, Value: `""`}
157
+ case "bool":
158
+ return ast.NewIdent("false")
159
+ case "error", "any":
160
+ return ast.NewIdent("nil")
161
+ default:
162
+ // A named type (struct/interface alias). *new(T) is the universal
163
+ // zero value that always compiles.
164
+ return newZero(tt)
165
+ }
166
+ case *ast.StarExpr, *ast.ArrayType, *ast.MapType, *ast.ChanType,
167
+ *ast.FuncType, *ast.InterfaceType:
168
+ return ast.NewIdent("nil")
169
+ default:
170
+ return newZero(t)
171
+ }
172
+ }
173
+
174
+ // newZero produces `*new(T)` — a universal, always-compiling zero value.
175
+ func newZero(t ast.Expr) ast.Expr {
176
+ return &ast.StarExpr{
177
+ X: &ast.CallExpr{
178
+ Fun: ast.NewIdent("new"),
179
+ Args: []ast.Expr{t},
180
+ },
181
+ }
182
+ }