aharrar commited on
Commit
11933f8
·
verified ·
1 Parent(s): 8fa0427

Add complete operator node declarations

Browse files
Files changed (1) hide show
  1. README.md +253 -2
README.md CHANGED
@@ -69,10 +69,251 @@ legality, state commits, and side effects.
69
  └─────────────┘ └─────────────┘
70
  ```
71
 
72
- The same graph expressed as NeoSyntropy code:
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
73
 
74
  ```python
75
- from neosyntropy import END, FSM, edge_deterministic
76
 
77
  def signal_is(expected):
78
  return lambda state: state.get("signal") == expected
@@ -94,6 +335,7 @@ graph = FSM(
94
  repair,
95
  compress_memory,
96
  success,
 
97
  ],
98
  edges=[
99
  edge_deterministic("Understand", "Decompose", guard=signal_is("CONTINUE")),
@@ -113,6 +355,15 @@ graph = FSM(
113
  edge_deterministic("Repair", "Retrieve", guard=signal_is("NEED_INFORMATION")),
114
  edge_deterministic("Compress", "Decompose", guard=signal_is("CONTINUE")),
115
  edge_deterministic("Success", END),
 
 
 
 
 
 
 
 
 
116
  ],
117
  )
118
  ```
 
69
  └─────────────┘ └─────────────┘
70
  ```
71
 
72
+ ### Complete node declarations
73
+
74
+ The following definitions make every symbol in the graph explicit. The three
75
+ tool names are application integrations: `search_repository`, `apply_plan`, and
76
+ `run_tests`. Learned operators use schema-constrained model calls; execution and
77
+ verification stay in trusted Python handlers.
78
+
79
+ ```python
80
+ from typing import Any, Literal
81
+
82
+ from pydantic import BaseModel, ConfigDict
83
+
84
+ from neosyntropy import NodeContext, OpenInput, SchemaNode, TextOutput, node
85
+
86
+ Signal = Literal[
87
+ "CONTINUE",
88
+ "NEED_INFORMATION",
89
+ "EXECUTED",
90
+ "FAILED_TEST",
91
+ "WRONG_PLAN",
92
+ "MEMORY_PRESSURE",
93
+ "COMPLETE",
94
+ ]
95
+
96
+
97
+ class StrictModel(BaseModel):
98
+ model_config = ConfigDict(extra="forbid")
99
+
100
+
101
+ class TaskInput(StrictModel):
102
+ repository: str
103
+ issue: str
104
+
105
+
106
+ class UnderstandOutput(StrictModel):
107
+ requirements: list[str]
108
+ unknowns: list[str]
109
+ signal: Literal["CONTINUE", "NEED_INFORMATION"]
110
+
111
+
112
+ class DecomposeOutput(StrictModel):
113
+ task_tree: list[str]
114
+ search_queries: list[str]
115
+ signal: Literal["CONTINUE", "NEED_INFORMATION"]
116
+
117
+
118
+ class RetrieveOutput(StrictModel):
119
+ retrieved: list[str]
120
+ signal: Literal["CONTINUE", "NEED_INFORMATION"]
121
+
122
+
123
+ class PlanOutput(StrictModel):
124
+ current_plan: str
125
+ signal: Literal["CONTINUE", "NEED_INFORMATION"]
126
+
127
+
128
+ class ExecuteOutput(StrictModel):
129
+ executed: bool
130
+ summary: str
131
+ signal: Literal["EXECUTED"]
132
+
133
+
134
+ class ObserveOutput(StrictModel):
135
+ observations: list[str]
136
+ errors: list[str]
137
+ signal: Literal["CONTINUE", "MEMORY_PRESSURE"]
138
+
139
+
140
+ class Verification(StrictModel):
141
+ passed: bool
142
+ summary: str
143
+
144
+
145
+ class VerifyOutput(StrictModel):
146
+ verification: Verification
147
+ evidence: list[str]
148
+ signal: Literal[
149
+ "COMPLETE", "FAILED_TEST", "NEED_INFORMATION", "WRONG_PLAN"
150
+ ]
151
+
152
+
153
+ class CompressOutput(StrictModel):
154
+ observations: list[str]
155
+ errors: list[str]
156
+ history: list[str]
157
+ signal: Literal["CONTINUE"]
158
+
159
+
160
+ class SuccessOutput(StrictModel):
161
+ outcome: Literal["satisfies_spec"]
162
+
163
+
164
+ # Learned operator: infer explicit requirements without inventing facts.
165
+ understand = SchemaNode(
166
+ id="Understand",
167
+ input_schema=TaskInput,
168
+ output_schema=UnderstandOutput,
169
+ prompt=(
170
+ "<OPERATOR:UNDERSTAND> Read the repository issue and current state. "
171
+ "Return explicit requirements, unresolved unknowns, and a legal signal."
172
+ ),
173
+ metadata={"operator": "UNDERSTAND", "model_role": "runtime-structure"},
174
+ )
175
+
176
+
177
+ # Learned operator: turn requirements into an ordered task tree and searches.
178
+ decompose = SchemaNode(
179
+ id="Decompose",
180
+ input_schema=OpenInput,
181
+ output_schema=DecomposeOutput,
182
+ prompt=(
183
+ "<OPERATOR:DECOMPOSE> Decompose the goal into ordered, testable subgoals. "
184
+ "Generate only repository searches needed by those subgoals."
185
+ ),
186
+ prerequisites=("Understand",),
187
+ metadata={"operator": "DECOMPOSE", "model_role": "runtime-deterministic-reasoning"},
188
+ )
189
+
190
+
191
+ # Trusted tool operator: execute only repository searches already in state.
192
+ @node(
193
+ id="Retrieve",
194
+ input_schema=OpenInput,
195
+ output_schema=RetrieveOutput,
196
+ tools=("search_repository",),
197
+ )
198
+ def retrieve(ctx: NodeContext):
199
+ facts: list[str] = []
200
+ for query in ctx.state.get("search_queries", []):
201
+ result = ctx.tools.invoke("search_repository", {"query": query})
202
+ facts.extend(result if isinstance(result, list) else [str(result)])
203
+ signal = "CONTINUE" if facts else "NEED_INFORMATION"
204
+ output = {"retrieved": facts, "signal": signal}
205
+ return ctx.result(output=output, state_updates=output)
206
+
207
+
208
+ # Learned operator: propose one bounded implementation plan from evidence.
209
+ propose = SchemaNode(
210
+ id="Propose",
211
+ input_schema=OpenInput,
212
+ output_schema=PlanOutput,
213
+ prompt=(
214
+ "<OPERATOR:PROPOSE> Use requirements, task_tree, and retrieved evidence. "
215
+ "Return one implementation plan grounded in repository symbols."
216
+ ),
217
+ metadata={"operator": "PROPOSE", "model_role": "runtime-stochastic-reasoning"},
218
+ )
219
+
220
+
221
+ # Trusted tool operator: apply the selected plan and run the real test harness.
222
+ @node(
223
+ id="Execute",
224
+ input_schema=OpenInput,
225
+ output_schema=ExecuteOutput,
226
+ tools=("apply_plan", "run_tests"),
227
+ )
228
+ def execute(ctx: NodeContext):
229
+ plan = ctx.state["current_plan"]
230
+ ctx.tools.invoke("apply_plan", {"plan": plan})
231
+ last_run = ctx.tools.invoke("run_tests", {})
232
+ output = {
233
+ "executed": True,
234
+ "summary": str(last_run),
235
+ "signal": "EXECUTED",
236
+ }
237
+ return ctx.result(output=output, state_updates={**output, "last_run": last_run})
238
+
239
+
240
+ # Trusted operator: normalize tool output into observations and errors.
241
+ @node(id="Observe", input_schema=OpenInput, output_schema=ObserveOutput)
242
+ def observe(ctx: NodeContext):
243
+ run = ctx.state.get("last_run", {})
244
+ errors = list(run.get("errors", [])) if isinstance(run, dict) else []
245
+ observations = [str(run)]
246
+ signal = "MEMORY_PRESSURE" if len(observations) > 20 else "CONTINUE"
247
+ output = {"observations": observations, "errors": errors, "signal": signal}
248
+ return ctx.result(output=output, state_updates=output)
249
+
250
+
251
+ # Trusted gate: only real test evidence may produce COMPLETE.
252
+ @node(id="Verify", input_schema=OpenInput, output_schema=VerifyOutput)
253
+ def verify(ctx: NodeContext):
254
+ run = ctx.state.get("last_run", {})
255
+ passed = bool(isinstance(run, dict) and run.get("passed") is True)
256
+ summary = "All required tests passed." if passed else "Required tests failed."
257
+ output = {
258
+ "verification": {"passed": passed, "summary": summary},
259
+ "evidence": [str(run)],
260
+ "signal": "COMPLETE" if passed else "FAILED_TEST",
261
+ }
262
+ return ctx.result(output=output, state_updates=output)
263
+
264
+
265
+ # Learned operator: repair the plan using concrete failure evidence.
266
+ repair = SchemaNode(
267
+ id="Repair",
268
+ input_schema=OpenInput,
269
+ output_schema=PlanOutput,
270
+ prompt=(
271
+ "<OPERATOR:REPAIR> Read current_plan, errors, and verification evidence. "
272
+ "Return a corrected plan that addresses the observed failure only."
273
+ ),
274
+ metadata={"operator": "REPAIR", "model_role": "runtime-stochastic-reasoning"},
275
+ )
276
+
277
+
278
+ # Trusted operator: bound state growth without rewriting facts.
279
+ @node(id="Compress", input_schema=OpenInput, output_schema=CompressOutput)
280
+ def compress_memory(ctx: NodeContext):
281
+ output = {
282
+ "observations": list(ctx.state.get("observations", []))[-4:],
283
+ "errors": list(ctx.state.get("errors", []))[-4:],
284
+ "history": list(ctx.state.get("history", []))[-12:],
285
+ "signal": "CONTINUE",
286
+ }
287
+ return ctx.result(output=output, state_updates=output)
288
+
289
+
290
+ # Terminal node: reachable only through the verified_complete graph guard.
291
+ @node(id="Success", input_schema=OpenInput, output_schema=SuccessOutput)
292
+ def success(ctx: NodeContext):
293
+ output = {"outcome": "satisfies_spec"}
294
+ return ctx.result(output=output, state_updates=output)
295
+
296
+
297
+ @node(
298
+ id="OperatorFallback",
299
+ input_schema=OpenInput,
300
+ output_schema=TextOutput,
301
+ is_fallback=True,
302
+ )
303
+ def operator_fallback(ctx: NodeContext):
304
+ return ctx.result(output={"message": "No legal operator transition."})
305
+ ```
306
+
307
+ Schema-node outputs are validated before their fields are proposed as state
308
+ updates. Python handlers explicitly return `state_updates`; mutating `ctx.state`
309
+ does not commit anything.
310
+
311
+ ### Graph wiring
312
+
313
+ The same nodes are connected as a NeoSyntropy FSM:
314
 
315
  ```python
316
+ from neosyntropy import END, FSM, edge_deterministic, edge_fallback
317
 
318
  def signal_is(expected):
319
  return lambda state: state.get("signal") == expected
 
335
  repair,
336
  compress_memory,
337
  success,
338
+ operator_fallback,
339
  ],
340
  edges=[
341
  edge_deterministic("Understand", "Decompose", guard=signal_is("CONTINUE")),
 
355
  edge_deterministic("Repair", "Retrieve", guard=signal_is("NEED_INFORMATION")),
356
  edge_deterministic("Compress", "Decompose", guard=signal_is("CONTINUE")),
357
  edge_deterministic("Success", END),
358
+ edge_fallback("Understand", "OperatorFallback"),
359
+ edge_fallback("Decompose", "OperatorFallback"),
360
+ edge_fallback("Retrieve", "OperatorFallback"),
361
+ edge_fallback("Propose", "OperatorFallback"),
362
+ edge_fallback("Execute", "OperatorFallback"),
363
+ edge_fallback("Observe", "OperatorFallback"),
364
+ edge_fallback("Verify", "OperatorFallback"),
365
+ edge_fallback("Repair", "OperatorFallback"),
366
+ edge_fallback("Compress", "OperatorFallback"),
367
  ],
368
  )
369
  ```