1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
;;; Copyright (c) 2026 Nicholas Vermeulen
;;; SPDX-License-Identifier: AGPL-3.0-or-later
;; synth.lisp — sketch-based program synthesis, CEGIS-style (Phase 4.2).
;;
;; A SKETCH is a quoted program containing holes — (?? id) markers.
;; HOLES maps each id to a finite list of candidate expressions:
;; ((id (expr expr ...)) ...)
;; SPEC is the 2.1 format verify-candidate already understands:
;; ((pure #t) (domains (d1 d2 ...)) (invariant (lambda (f args...) ...)))
;;
;; (synth-fill sketch holes spec) enumerates fillings in deterministic
;; order (first hole cycles fastest) and gates each candidate the 2.1 way —
;; static checks first, exhaustive checking only after — with a CEGIS
;; twist: every counterexample the exhaustive oracle finds is added to a
;; cheap up-front filter, so later candidates die on a handful of concrete
;; inputs instead of a full exhaustive run.
;;
;; => (ok filled-expr bindings tried N cexs M) on success
;; (no-solution tried N) domains exhausted
;; (search-capped tried N) hit *synth-max-tries*
;; ── Sketch surgery ────────────────────────────────────────────────────────
;; ── Deterministic enumeration (odometer over hole domains) ───────────────
;; ── CEGIS helpers ─────────────────────────────────────────────────────────
;; ── The solver ────────────────────────────────────────────────────────────
;; ── Proposer-driven synthesis: the LLM + constraint-solver loop ──────────
;; A PROPOSER (attempt feedback) → bindings ((id expr)...). The constraint
;; side is unchanged — verify-candidate accepts or the reason loops back as
;; feedback. Any deterministic function works (the golden test scripts
;; one); llm-hole-proposer below asks a live local model.
;; ── Robust bindings extraction from prose replies ────────────────────────
;; std.lisp's extract-sexp (first "(" to last ")") is useless on a reply
;; that reasons out loud — it spans the whole essay. Instead: scan for
;; every balanced top-level sexp, parse each as DATA, and keep the LAST
;; one that validates as a bindings alist over the sketch's hole ids
;; (models put the answer at the end).
;; Ask a local model to fill the holes. The reply is parsed as DATA and
;; never executed before verification gates it — same discipline as 2.1's
;; llm-proposer. A reply with no valid bindings costs one attempt (the
;; raise is caught by synth-with-proposer's try-catch).