@mhingston5/lasso 0.1.0

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.
Files changed (124) hide show
  1. package/README.md +707 -0
  2. package/docs/agent-wrangling.png +0 -0
  3. package/package.json +26 -0
  4. package/src/capabilities/matcher.ts +25 -0
  5. package/src/capabilities/registry.ts +103 -0
  6. package/src/capabilities/types.ts +15 -0
  7. package/src/cir/lower.ts +253 -0
  8. package/src/cir/optimize.ts +251 -0
  9. package/src/cir/types.ts +131 -0
  10. package/src/cir/validate.ts +265 -0
  11. package/src/compiler/compile.ts +601 -0
  12. package/src/compiler/feedback.ts +471 -0
  13. package/src/compiler/runtime-helpers.ts +455 -0
  14. package/src/composition/chain.ts +58 -0
  15. package/src/composition/conditional.ts +76 -0
  16. package/src/composition/parallel.ts +75 -0
  17. package/src/composition/types.ts +105 -0
  18. package/src/environment/analyzer.ts +56 -0
  19. package/src/environment/discovery.ts +179 -0
  20. package/src/environment/types.ts +68 -0
  21. package/src/failures/classifiers.ts +134 -0
  22. package/src/failures/generator.ts +421 -0
  23. package/src/failures/map-reference-failures.ts +23 -0
  24. package/src/failures/ontology.ts +210 -0
  25. package/src/failures/recovery.ts +214 -0
  26. package/src/failures/types.ts +14 -0
  27. package/src/index.ts +67 -0
  28. package/src/memory/advisor.ts +132 -0
  29. package/src/memory/extractor.ts +166 -0
  30. package/src/memory/store.ts +107 -0
  31. package/src/memory/types.ts +53 -0
  32. package/src/metaharness/engine.ts +256 -0
  33. package/src/metaharness/predictor.ts +168 -0
  34. package/src/metaharness/types.ts +40 -0
  35. package/src/mutation/derive.ts +308 -0
  36. package/src/mutation/diff.ts +52 -0
  37. package/src/mutation/engine.ts +256 -0
  38. package/src/mutation/types.ts +84 -0
  39. package/src/pi/command-input.ts +209 -0
  40. package/src/pi/commands.ts +351 -0
  41. package/src/pi/extension.ts +16 -0
  42. package/src/planner/synthesize.ts +83 -0
  43. package/src/planner/template-rules.ts +183 -0
  44. package/src/planner/types.ts +42 -0
  45. package/src/reference/catalog.ts +128 -0
  46. package/src/reference/patch-validation-strategies.ts +170 -0
  47. package/src/reference/patch-validation.ts +174 -0
  48. package/src/reference/pr-review-merge.ts +155 -0
  49. package/src/reference/strategies.ts +126 -0
  50. package/src/reference/types.ts +33 -0
  51. package/src/replanner/risk-rules.ts +161 -0
  52. package/src/replanner/runtime.ts +308 -0
  53. package/src/replanner/synthesize.ts +619 -0
  54. package/src/replanner/types.ts +73 -0
  55. package/src/spec/schema.ts +254 -0
  56. package/src/spec/types.ts +319 -0
  57. package/src/spec/validate.ts +296 -0
  58. package/src/state/snapshots.ts +43 -0
  59. package/src/state/types.ts +12 -0
  60. package/src/synthesis/graph-builder.ts +267 -0
  61. package/src/synthesis/harness-builder.ts +113 -0
  62. package/src/synthesis/intent-ir.ts +63 -0
  63. package/src/synthesis/policy-builder.ts +320 -0
  64. package/src/synthesis/risk-analyzer.ts +182 -0
  65. package/src/synthesis/skill-parser.ts +441 -0
  66. package/src/verification/engine.ts +230 -0
  67. package/src/versioning/file-store.ts +103 -0
  68. package/src/versioning/history.ts +43 -0
  69. package/src/versioning/store.ts +16 -0
  70. package/src/versioning/types.ts +31 -0
  71. package/test/capabilities/matcher.test.ts +67 -0
  72. package/test/capabilities/registry.test.ts +136 -0
  73. package/test/capabilities/synthesis.test.ts +264 -0
  74. package/test/cir/lower.test.ts +417 -0
  75. package/test/cir/optimize.test.ts +266 -0
  76. package/test/cir/validate.test.ts +368 -0
  77. package/test/compiler/adaptive-runtime.test.ts +157 -0
  78. package/test/compiler/compile.test.ts +1198 -0
  79. package/test/compiler/feedback.test.ts +784 -0
  80. package/test/compiler/guardrails.test.ts +191 -0
  81. package/test/compiler/trace.test.ts +404 -0
  82. package/test/composition/chain.test.ts +328 -0
  83. package/test/composition/conditional.test.ts +241 -0
  84. package/test/composition/parallel.test.ts +215 -0
  85. package/test/environment/analyzer.test.ts +204 -0
  86. package/test/environment/discovery.test.ts +149 -0
  87. package/test/failures/classifiers.test.ts +287 -0
  88. package/test/failures/generator.test.ts +203 -0
  89. package/test/failures/ontology.test.ts +439 -0
  90. package/test/failures/recovery.test.ts +300 -0
  91. package/test/helpers/createFixtureRepo.ts +84 -0
  92. package/test/helpers/createPatchValidationFixture.ts +144 -0
  93. package/test/helpers/runCompiledWorkflow.ts +208 -0
  94. package/test/memory/advisor.test.ts +332 -0
  95. package/test/memory/extractor.test.ts +295 -0
  96. package/test/memory/store.test.ts +244 -0
  97. package/test/metaharness/engine.test.ts +575 -0
  98. package/test/metaharness/predictor.test.ts +436 -0
  99. package/test/mutation/derive-failure.test.ts +209 -0
  100. package/test/mutation/engine.test.ts +622 -0
  101. package/test/package-smoke.test.ts +29 -0
  102. package/test/pi/command-input.test.ts +153 -0
  103. package/test/pi/commands.test.ts +623 -0
  104. package/test/planner/classify-template.test.ts +32 -0
  105. package/test/planner/synthesize.test.ts +901 -0
  106. package/test/reference/PatchValidation.failures.test.ts +137 -0
  107. package/test/reference/PatchValidation.test.ts +326 -0
  108. package/test/reference/PrReviewMerge.failures.test.ts +121 -0
  109. package/test/reference/PrReviewMerge.test.ts +55 -0
  110. package/test/reference/catalog-open.test.ts +70 -0
  111. package/test/replanner/runtime.test.ts +207 -0
  112. package/test/replanner/synthesize.test.ts +303 -0
  113. package/test/spec/validate.test.ts +1056 -0
  114. package/test/state/snapshots.test.ts +264 -0
  115. package/test/synthesis/custom-workflow.test.ts +264 -0
  116. package/test/synthesis/graph-builder.test.ts +370 -0
  117. package/test/synthesis/harness-builder.test.ts +128 -0
  118. package/test/synthesis/policy-builder.test.ts +149 -0
  119. package/test/synthesis/risk-analyzer.test.ts +230 -0
  120. package/test/synthesis/skill-parser.test.ts +796 -0
  121. package/test/verification/engine.test.ts +509 -0
  122. package/test/versioning/history.test.ts +144 -0
  123. package/test/versioning/store.test.ts +254 -0
  124. package/vitest.config.ts +9 -0
package/README.md ADDED
@@ -0,0 +1,707 @@
1
+ # Lasso
2
+
3
+ <p align="center">
4
+ <img src="docs/agent-wrangling.png" width="320" alt="Agent Wrangling" />
5
+ </p>
6
+
7
+ Lasso is a dynamic harness engine built on `pi-duroxide`. It goes from intent to
8
+ executable workflow — and repairs the harness while it runs.
9
+
10
+ ```text
11
+ Intent
12
+ → Environment discovery (tools, resources, constraints)
13
+ → Memory query (past patterns, what worked/failed)
14
+ → Graph synthesis (planner + capabilities)
15
+ → Failure prediction (auth, tool, network, resource)
16
+ → Policy synthesis (mutations: add verification, retry, approval)
17
+ → Compilation (validate → lower → optimize → execute)
18
+ → Runtime adaptation (trace → mutate → continueAsNew)
19
+ ```
20
+
21
+ If `pi-duroxide` is the durable runtime engine, **Lasso is the layer that
22
+ generates, optimizes, and repairs the harnesses that run on it**.
23
+
24
+ ## What is Lasso?
25
+
26
+ Lasso is a **pi coding agent extension** — a TypeScript package that plugs into
27
+ pi via the `pi` field in `package.json`. When installed, it:
28
+
29
+ 1. Boots `pi-duroxide` (the durable workflow runtime)
30
+ 2. Registers 5 slash commands (`/lasso:plan`, `/lasso:run`, etc.)
31
+ 3. Exports a library API for programmatic use
32
+
33
+ There are two ways to use it:
34
+
35
+ | Mode | How | When |
36
+ | --- | --- | --- |
37
+ | **Chat mode** | Slash commands inside pi's coding agent UI | Interactive workflow planning and execution |
38
+ | **Library mode** | `import { compileHarnessSpec } from "lasso"` | Building custom tooling, CI pipelines, or other extensions |
39
+
40
+ ## Quick start
41
+
42
+ ### Install
43
+
44
+ ```bash
45
+ # From this repository
46
+ pi install .
47
+
48
+ # Or from npm (once published)
49
+ pi install @mhingston5/lasso
50
+ ```
51
+
52
+ ### Chat mode (inside pi)
53
+
54
+ ```bash
55
+ # 1. Plan a workflow from a freeform brief
56
+ /lasso:plan Validate that the bug fix in fix.patch works against main
57
+
58
+ # 2. Run it (paste the JSON output from step 1)
59
+ /lasso:run {"workflow":"patch-validation","input":{...}}
60
+
61
+ # 3. Inspect what happened
62
+ /lasso:inspect
63
+ ```
64
+
65
+ ### Library mode (TypeScript)
66
+
67
+ ```typescript
68
+ import { compileHarnessSpec, mutateHarness, classifyFailure } from "lasso";
69
+
70
+ const compiled = compileHarnessSpec(spec);
71
+ const signature = classifyFailure(error, { nodeId: "deploy" });
72
+ ```
73
+
74
+ > **Safety:** Lasso checks out refs, applies patches, and merges branches in the
75
+ > target repo. Use a throwaway clone or disposable worktree, not your primary
76
+ > checkout.
77
+
78
+ ## Table of contents
79
+
80
+ - [What is Lasso?](#what-is-lasso)
81
+ - [Why Lasso exists](#why-lasso-exists)
82
+ - [What Lasso does](#what-lasso-does)
83
+ - [How it works](#how-it-works)
84
+ - [Slash commands](#slash-commands)
85
+ - [Bundled workflows](#bundled-workflows)
86
+ - [Request examples](#request-examples)
87
+ - [Custom workflows](#custom-workflows)
88
+ - [HarnessSpec reference](#harnessspec-reference)
89
+ - [Library API](#library-api)
90
+ - [Compiler](#compiler)
91
+ - [Compiler feedback](#compiler-feedback)
92
+ - [Verification engine](#verification-engine)
93
+ - [Compiler optimizations](#compiler-optimizations)
94
+ - [Harness mutations](#harness-mutations)
95
+ - [Guardrails](#guardrails)
96
+ - [Failure mode generation](#failure-mode-generation)
97
+ - [Adaptive runtime](#adaptive-runtime)
98
+ - [Lineage persistence](#lineage-persistence)
99
+ - [Harness memory](#harness-memory)
100
+ - [Environment model](#environment-model)
101
+ - [Failure ontology](#failure-ontology)
102
+ - [Capabilities](#capabilities)
103
+ - [Meta-harness](#meta-harness)
104
+ - [Multi-harness composition](#multi-harness-composition)
105
+ - [How Lasso fits with pi-duroxide](#how-lasso-fits-with-pi-duroxide)
106
+ - [Non-goals](#non-goals)
107
+
108
+ ---
109
+
110
+ ## Why Lasso exists
111
+
112
+ `pi-duroxide` gives you a durable workflow runtime. That is the right layer when
113
+ you already know what workflow you want to run.
114
+
115
+ Lasso sits one level higher. It:
116
+
117
+ 1. **discovers** the execution environment (available tools, auth, constraints)
118
+ 2. **synthesizes** a workflow graph from intent
119
+ 3. **predicts** failures before they happen
120
+ 4. **mutates** the harness to prevent them
121
+ 5. **compiles** into a replay-safe durable workflow
122
+ 6. **repairs** the harness at runtime based on observed failures
123
+ 7. **remembers** what worked across sessions
124
+
125
+ Use Lasso when you want workflow automation that is:
126
+
127
+ - more reusable than an ad hoc prompt
128
+ - more inspectable than hidden agent logic
129
+ - safer to validate before execution
130
+ - adaptive — it repairs itself when things go wrong
131
+ - aware of its environment — it knows what tools are available
132
+
133
+ ## What Lasso does
134
+
135
+ Lasso takes a declarative `HarnessSpec`, validates it, lowers it to CIR,
136
+ optimizes it, and compiles it into a replay-safe workflow that runs on
137
+ `pi-duroxide`.
138
+
139
+ Out of the box, it ships with:
140
+
141
+ - **Two bundled workflows** — `patch-validation` and `pr-review-merge`
142
+ - **Slash commands** — `/lasso:plan`, `/lasso:replan`, `/lasso:compile`, `/lasso:run`, `/lasso:inspect`
143
+ - **A library API** — for programmatic use from TypeScript
144
+
145
+ ## How it works
146
+
147
+ ### The generation pipeline
148
+
149
+ ```text
150
+ Intent (brief or skill markdown)
151
+
152
+ parsePromptOrSkill() → IntentIR
153
+
154
+ buildTaskGraph() → TaskGraph
155
+
156
+ analyzeRisks() → RiskModel
157
+
158
+ synthesizePolicy() → PolicyBundle
159
+
160
+ synthesizeHarness() → HarnessSpec
161
+
162
+ compileHarnessSpec() → CompiledWorkflow → pi-duroxide
163
+ ```
164
+
165
+ ### The adaptation loop
166
+
167
+ ```text
168
+ Workflow executes
169
+
170
+ Execution trace captured (timestamps, I/O snapshots, failures)
171
+
172
+ deriveMutationsFromTrace() → HarnessMutation[]
173
+
174
+ mutateHarness(spec, mutations) → new spec
175
+
176
+ prepareRuntimeReplan() → continue_as_new / needs_operator_input / stop
177
+
178
+ New version with repaired harness
179
+ ```
180
+
181
+ ### The feedback loop
182
+
183
+ ```text
184
+ compileHarnessSpec()
185
+
186
+ analyzeCompiledWorkflow()
187
+ → CostEstimate (LLM calls, duration, USD)
188
+ → RiskAssessment (cost, failure, quality, complexity)
189
+ → HarnessMutation[] (executable, with triggers)
190
+
191
+ mutateHarness(spec, mutations)
192
+ → replace expensive models
193
+ → add retry policies
194
+ → add verification hooks
195
+
196
+ Recompile with improvements
197
+ ```
198
+
199
+ ---
200
+
201
+ ## Slash commands
202
+
203
+ | Command | Use it when | What it does |
204
+ | --- | --- | --- |
205
+ | `/lasso:plan <brief>` | You have an English brief and want a draft request | Returns a draft JSON request or a clarification result |
206
+ | `/lasso:replan <JSON>` | You have a previous request plus a real outcome | Returns a revised draft, `needs_operator_input`, or `stop` |
207
+ | `/lasso:compile <input>` | You want to inspect what Lasso will register | Compiles and stores the artifact in memory |
208
+ | `/lasso:run <input>` | You want to execute a workflow locally | Compiles, registers, and starts the workflow |
209
+ | `/lasso:inspect [name]` | You want to see compiled spec, CIR, and runtime state | Shows the latest or named compiled workflow |
210
+
211
+ ### `/lasso:plan`
212
+
213
+ Deterministic, draft-only. Classifies a brief into `patch-validation`,
214
+ `pr-review-merge`, or `custom` and returns a draft JSON envelope you can pass to
215
+ `/lasso:compile` or `/lasso:run`. Does not compile, register, or run anything.
216
+
217
+ For the two bundled families, it extracts structured fields with strict
218
+ validation. For custom families (via skill markdown with an explicit `workflow`
219
+ name), it builds a sequential graph from parsed steps.
220
+
221
+ ### `/lasso:replan`
222
+
223
+ Deterministic, draft-only. Accepts the original request plus an
224
+ `observedOutcome` and returns one of:
225
+
226
+ 1. a revised draft request
227
+ 2. `needs_operator_input` — human must provide new facts
228
+ 3. `stop` — auto-retrying would be wrong
229
+
230
+ ### Custom compile/run input shapes
231
+
232
+ `/lasso:compile` and `/lasso:run` accept four input forms:
233
+
234
+ 1. **Bundled workflow request JSON** — `{ "workflow": "patch-validation", "input": {...} }`
235
+ 2. **Raw `HarnessSpec` JSON** — the full spec
236
+ 3. **Envelope with spec or specPath** — `{ "spec": {...}, "input": {...} }` or `{ "specPath": "/path/to/spec.json" }`
237
+ 4. **Direct path** — `/tmp/custom-spec.json`
238
+
239
+ ---
240
+
241
+ ## Bundled workflows
242
+
243
+ Both operate entirely against a **local** repository or worktree.
244
+
245
+ ### `patch-validation`
246
+
247
+ Validates a candidate fix against a known-bad baseline:
248
+
249
+ 1. Check out `baselineRef` and run `reproduceCommands` to confirm the bug
250
+ 2. Apply the candidate from `candidateSource`
251
+ 3. Re-run `reproduceCommands` — expect them to pass
252
+ 4. Run `verificationCommands` as a broader regression check
253
+ 5. Optionally route to human approval
254
+
255
+ Terminal outcomes: `validated-fix`, `not-reproduced`, `apply-failed`, `candidate-failed`, `rejected`
256
+
257
+ ### `pr-review-merge`
258
+
259
+ Local rehearsal of a review-and-merge flow:
260
+
261
+ 1. Inspect the repo
262
+ 2. Run verification commands
263
+ 3. Generate an LLM review summary
264
+ 4. Route through human approval
265
+ 5. Perform local merge
266
+ 6. Re-run verification after merge
267
+
268
+ ---
269
+
270
+ ## Request examples
271
+
272
+ ### `patch-validation`
273
+
274
+ ```json
275
+ {
276
+ "workflow": "patch-validation",
277
+ "input": {
278
+ "repoPath": "/absolute/path/to/disposable-worktree",
279
+ "baselineRef": "main",
280
+ "candidateSource": { "kind": "patchFile", "value": "/path/to/fix.patch" },
281
+ "reproduceCommands": ["npm test -- --grep 'the broken test'"],
282
+ "verificationCommands": ["npm test"],
283
+ "reviewInstructions": "Approve if the patch applies cleanly and verification passes.",
284
+ "approvalRequired": false
285
+ }
286
+ }
287
+ ```
288
+
289
+ ### `pr-review-merge`
290
+
291
+ ```json
292
+ {
293
+ "workflow": "pr-review-merge",
294
+ "input": {
295
+ "repoPath": "/absolute/path/to/disposable-worktree",
296
+ "sourceBranch": "feature/pr-change",
297
+ "targetBranch": "main",
298
+ "reviewInstructions": "Approve only if verification passes and the diff looks safe.",
299
+ "verificationCommands": ["node -e \"process.exit(0)\""]
300
+ }
301
+ }
302
+ ```
303
+
304
+ ### `replan`
305
+
306
+ ```json
307
+ {
308
+ "workflow": "patch-validation",
309
+ "originalRequest": {
310
+ "workflow": "patch-validation",
311
+ "input": { "..." : "..." }
312
+ },
313
+ "observedOutcome": {
314
+ "terminalNodeId": "validated-fix",
315
+ "notes": ["prod hotfix"]
316
+ }
317
+ }
318
+ ```
319
+
320
+ For aborted attempts: `{ "aborted": true, "abortReason": "retry-exhaustion" }`
321
+
322
+ ### Custom `HarnessSpec` compile
323
+
324
+ ```json
325
+ {
326
+ "name": "custom-echo",
327
+ "graph": {
328
+ "entryNodeId": "echo",
329
+ "nodes": [
330
+ { "id": "echo", "kind": "tool", "tool": "bash", "args": ["-lc", "echo hello"] }
331
+ ],
332
+ "edges": []
333
+ }
334
+ }
335
+ ```
336
+
337
+ ---
338
+
339
+ ## Custom workflows
340
+
341
+ Use `/lasso:compile` and `/lasso:run` with any `HarnessSpec`, or use Lasso as
342
+ a library:
343
+
344
+ ```typescript
345
+ import { validateHarnessSpec, lowerHarnessSpecToCir, compileHarnessSpec } from "lasso";
346
+
347
+ validateHarnessSpec(spec); // structural validation
348
+ lowerHarnessSpecToCir(spec); // inspect lowered IR
349
+ compileHarnessSpec(spec); // produce replay-safe workflow
350
+ ```
351
+
352
+ Arbitrary workflow families are supported. The planner accepts custom families
353
+ via skill markdown with an explicit `workflow` name.
354
+
355
+ ---
356
+
357
+ ## HarnessSpec reference
358
+
359
+ Canonical sources: `src/spec/types.ts`, `src/spec/schema.ts`, `src/spec/validate.ts`
360
+
361
+ ### Top-level shape
362
+
363
+ ```json
364
+ {
365
+ "name": "workflow-name",
366
+ "graph": { "entryNodeId": "start", "nodes": [], "edges": [] },
367
+ "executionPolicy": {},
368
+ "humanPolicy": {},
369
+ "observabilityPolicy": {}
370
+ }
371
+ ```
372
+
373
+ | Field | Required | Type | Notes |
374
+ | --- | --- | --- | --- |
375
+ | `name` | Yes | `string` | Unique workflow name |
376
+ | `graph` | Yes | `object` | Contains `entryNodeId`, `nodes`, `edges` |
377
+ | `executionPolicy` | No | `object` | Global execution settings |
378
+ | `humanPolicy` | No | `object` | Human interaction defaults |
379
+ | `observabilityPolicy` | No | `object` | Trace / metrics / logging |
380
+
381
+ All top-level objects are **strict**. Unknown fields are rejected.
382
+
383
+ ### Node kinds
384
+
385
+ | Kind | Key fields | Maps to |
386
+ | --- | --- | --- |
387
+ | `tool` | `tool`, `args`, `env`, `cwd` | `ctx.pi.tool()` |
388
+ | `llm` | `provider`, `model`, `prompt`, `system` | `ctx.pi.llm()` |
389
+ | `human` | `prompt`, `interactionType`, `options` | `ctx.waitForEvent()` |
390
+ | `condition` | `condition`, `thenNodeId`, `elseNodeId` | Branch evaluation |
391
+ | `merge` | `waitFor`, `strategy` | Fork-join synchronization |
392
+ | `subworkflow` | `specRef`, `inputs` | `ctx.scheduleSubOrchestration()` |
393
+
394
+ ### Validation rules
395
+
396
+ 1. Node IDs must be unique
397
+ 2. `entryNodeId` must exist
398
+ 3. Every edge `from`/`to` must reference an existing node
399
+ 4. `condition.thenNodeId` and `condition.elseNodeId` must exist
400
+ 5. `merge.waitFor` must not be empty
401
+ 6. `human` nodes with `interactionType: "choice"` must have `options`
402
+ 7. Unreachable nodes are rejected
403
+ 8. `retryPolicy` only on `tool`, `llm`, `subworkflow`
404
+ 9. Verification rules cannot reference missing nodes
405
+ 10. Circular verification dependencies are rejected
406
+
407
+ ---
408
+
409
+ ## Library API
410
+
411
+ ### Compiler
412
+
413
+ ```typescript
414
+ import { compileHarnessSpec, type CompiledHarnessWorkflow } from "lasso";
415
+
416
+ const compiled = compileHarnessSpec(spec);
417
+ // compiled.name, compiled.spec, compiled.cir, compiled.optimizations
418
+ // compiled.register(pi) — registers with pi-duroxide
419
+ ```
420
+
421
+ Pipeline: validate → lower → optimize → validate CIR → build generator.
422
+
423
+ ### Compiler feedback
424
+
425
+ Analyzes compiled workflows and emits **executable mutations** (not just
426
+ advisory suggestions):
427
+
428
+ ```typescript
429
+ import { analyzeCompiledWorkflow, mutateHarness } from "lasso";
430
+
431
+ const analysis = analyzeCompiledWorkflow(compiled);
432
+ // analysis.cost — LLM calls, duration, USD estimate
433
+ // analysis.risk — cost, failure, quality, complexity
434
+ // analysis.mutations — executable HarnessMutation[] with triggers
435
+
436
+ // Apply mutations directly
437
+ const { spec: improvedSpec } = mutateHarness(spec, analysis.mutations);
438
+ ```
439
+
440
+ Each mutation carries a `trigger` (why it was emitted) and `description`
441
+ (human-readable reason):
442
+
443
+ | Trigger | Mutation | Effect |
444
+ | --- | --- | --- |
445
+ | `cost_high` | `replace-node` | Swap expensive model for cheaper one |
446
+ | `retry_exhausted` | `modify-node` | Add retry policy with exponential backoff |
447
+ | `verification_failed` | `add-verification` | Add verification hook |
448
+ | `loop_detected` | `modify-node` | Flag adjacent nodes for merge |
449
+
450
+ ### Guardrails
451
+
452
+ Enforce execution limits at runtime. The compiler stops execution when limits
453
+ are exceeded, throwing a `GuardrailExceededError` with a descriptive message.
454
+
455
+ ```json
456
+ {
457
+ "name": "limited-workflow",
458
+ "executionPolicy": {
459
+ "maxSteps": 25,
460
+ "costLimitUsd": 0.25,
461
+ "timeout": 300000
462
+ },
463
+ "graph": { "..." : "..." }
464
+ }
465
+ ```
466
+
467
+ | Field | Type | Enforcement |
468
+ | --- | --- | --- |
469
+ | `maxSteps` | `number` (positive integer) | Stops after N node executions |
470
+ | `costLimitUsd` | `number` (positive) | Stops when estimated LLM cost exceeds limit |
471
+ | `timeout` | `number` (ms) | Stops after wall-clock time |
472
+
473
+ Step count resets on `continueAsNew` (adaptive evolution). Cost accumulates
474
+ across versions.
475
+
476
+ ### Failure mode generation
477
+
478
+ Before execution, Lasso generates plausible failure modes from the task
479
+ description and environment. This answers "Where am I likely to fail?" before
480
+ acting.
481
+
482
+ ```typescript
483
+ import { generateFailureModes } from "lasso";
484
+
485
+ const generation = generateFailureModes("Deploy my app to staging", env);
486
+ // generation.failureModes — array of FailureMode
487
+ // generation.riskSummary — "HIGH RISK: auth failures likely (env constraint detected)"
488
+ ```
489
+
490
+ | Task keyword | Generated failure modes |
491
+ | --- | --- |
492
+ | `deploy` | auth expiry, network timeout, config drift |
493
+ | `test` | flaky tests, timeout, environment mismatch |
494
+ | `build` | dependency failure, disk full, OOM |
495
+ | `merge` | conflict, verification failure |
496
+ | `database` | connection timeout, migration failure |
497
+ | `api` | rate limit, auth expiry, schema mismatch |
498
+ | `file` | permission denied, disk full, path not found |
499
+
500
+ Failure modes are cross-referenced with environment constraints: if auth
501
+ constraint detected, auth failure probability is boosted. Each mode includes
502
+ triggers, mitigations, and recovery actions.
503
+
504
+ ### Verification engine
505
+
506
+ Standalone module with compositional strategies:
507
+
508
+ ```typescript
509
+ import { runVerification } from "lasso/verification/engine";
510
+
511
+ // Strategies: "all-must-pass" (default), "first-pass", "any-block"
512
+ const report = yield* runVerification(nodeId, hooks, nodeMap, state, ctx, "first-pass");
513
+ // report.overallStatus — "pass" | "warn" | "block"
514
+ // report.hookResults — per-hook outcome + duration
515
+ ```
516
+
517
+ ### Compiler optimizations
518
+
519
+ Three passes between lowering and CIR validation:
520
+
521
+ 1. **Dead-node elimination** — removes unreachable nodes
522
+ 2. **Single-branch merge elision** — simplifies single-branch merges
523
+ 3. **Tool-node fusion** — merges adjacent `bash`/`sh` nodes
524
+
525
+ ```typescript
526
+ import { optimizeCirWorkflow } from "lasso/cir/optimize";
527
+
528
+ const { optimized, passes } = optimizeCirWorkflow(cir);
529
+ // passes — ["dead-node-elimination", "merge-elision", "tool-fusion"]
530
+ ```
531
+
532
+ ### Harness mutations
533
+
534
+ Structural spec modifications from execution traces or compiler feedback:
535
+
536
+ ```typescript
537
+ import { deriveMutationsFromTrace, deriveMutationsFromFailure, mutateHarness } from "lasso";
538
+
539
+ // From execution trace
540
+ const mutations = deriveMutationsFromTrace(trace, spec);
541
+
542
+ // From classified failure
543
+ const mutations = deriveMutationsFromFailure(signature, spec, { nodeId: "deploy" });
544
+
545
+ // Apply
546
+ const { spec: newSpec, diff } = mutateHarness(spec, mutations);
547
+ ```
548
+
549
+ Mutation types: `add-node`, `remove-node`, `modify-node`, `add-edge`,
550
+ `toggle-approval`, `add-verification`, `replace-node`, `tighten-guardrail`
551
+
552
+ Triggers: `node_failed`, `confidence_low`, `cost_high`, `loop_detected`,
553
+ `retry_exhausted`, `verification_failed`, `tool_missing`, `auth_expired`
554
+
555
+ ### Adaptive runtime
556
+
557
+ Reference workflows get automatic version evolution:
558
+
559
+ ```typescript
560
+ import { prepareRuntimeReplan, MAX_ADAPTIVE_VERSIONS } from "lasso";
561
+
562
+ const decision = await prepareRuntimeReplan(metadata, input, result);
563
+ // decision.type — "continue_as_new" | "needs_operator_input" | "stop"
564
+ ```
565
+
566
+ Capped at 5 versions (`MAX_ADAPTIVE_VERSIONS`). Each version records a
567
+ `HarnessVersion` with full lineage.
568
+
569
+ ### Lineage persistence
570
+
571
+ ```typescript
572
+ import { FileLineageStore } from "lasso";
573
+
574
+ const store = new FileLineageStore("/path/to/store");
575
+ await store.saveVersion(version);
576
+ await store.saveLineage(entry);
577
+
578
+ const chain = await store.getLineageChain(3);
579
+ const recent = await store.queryLineage({ terminalNodeId: "validated-fix", limit: 10 });
580
+ ```
581
+
582
+ ### Harness memory
583
+
584
+ Tracks patterns across sessions:
585
+
586
+ ```typescript
587
+ import { FileMemoryStore, adviseFromMemory } from "lasso";
588
+
589
+ const store = new FileMemoryStore("/path/to/memory");
590
+ const advice = await adviseFromMemory("deploy-staging", store);
591
+ // advice.suggestions — "Previously, auth-check-before-deploy improved success rate"
592
+ // advice.warnings — "Pattern deploy-without-auth failed 6 times"
593
+ ```
594
+
595
+ ### Environment model
596
+
597
+ Discovers execution environment before generating a harness:
598
+
599
+ ```typescript
600
+ import { discoverEnvironment, analyzeEnvironment } from "lasso";
601
+
602
+ const env = await discoverEnvironment("/path/to/repo");
603
+ // env.tools — bash, git, node, etc.
604
+ // env.constraints — auth, network, rate-limit
605
+ // env.repoState — branch, uncommitted changes, remotes
606
+
607
+ const analysis = analyzeEnvironment(env, ["git", "node"]);
608
+ // analysis.readinessScore — 0-100
609
+ // analysis.preparatorySteps — actionable prep steps
610
+ ```
611
+
612
+ ### Failure ontology
613
+
614
+ 7 failure classes with evidence and recovery:
615
+
616
+ ```typescript
617
+ import { classifyFailure, suggestRecovery } from "lasso";
618
+
619
+ const signature = classifyFailure(error, { nodeId: "deploy" });
620
+ // signature.class — "auth" | "tool" | "resource" | "semantic" | "human" | "environment-drift" | "network" | "unknown"
621
+ // signature.confidence — 0-1
622
+ // signature.suggestedRecovery — actionable steps
623
+
624
+ const recovery = suggestRecovery(signature);
625
+ ```
626
+
627
+ ### Capabilities
628
+
629
+ Dynamic graph generation from required tools:
630
+
631
+ ```typescript
632
+ import { DefaultCapabilityRegistry, planWorkflowRequest } from "lasso";
633
+
634
+ const registry = new DefaultCapabilityRegistry();
635
+ // Pre-registered: bash, git, node, llm-review, human-approval
636
+
637
+ const result = planWorkflowRequest(brief, registry);
638
+ ```
639
+
640
+ ### Meta-harness
641
+
642
+ Full generation pipeline — discover, predict, synthesize, compile:
643
+
644
+ ```typescript
645
+ import { DefaultMetaHarness, DefaultCapabilityRegistry, FileMemoryStore } from "lasso";
646
+
647
+ const meta = new DefaultMetaHarness({
648
+ capabilityRegistry: new DefaultCapabilityRegistry(),
649
+ memoryStore: new FileMemoryStore("/path/to/memory"),
650
+ });
651
+
652
+ const result = await meta.generateHarness("Deploy my app to staging");
653
+ // result.spec — generated HarnessSpec
654
+ // result.environmentAnalysis — tool/resource availability
655
+ // result.predictedFailures — anticipated failures with confidence
656
+ // result.compilerAnalysis — cost, risk, mutations
657
+ // result.readinessScore — 0-100
658
+ // result.appliedMutations — what was changed
659
+ ```
660
+
661
+ ### Multi-harness composition
662
+
663
+ ```typescript
664
+ // Sequential chain
665
+ const chained = meta.composeHarnesses([
666
+ { name: "research", spec: researchSpec },
667
+ { name: "plan", spec: planSpec },
668
+ { name: "execute", spec: executeSpec },
669
+ ]);
670
+
671
+ // Parallel execution
672
+ const parallel = meta.composeParallel([verificationSpec, notificationSpec]);
673
+
674
+ // Conditional branching
675
+ const conditional = meta.composeConditional("isProduction", prodSpec, stagingSpec);
676
+ ```
677
+
678
+ Node IDs are prefixed with stage names to avoid collisions.
679
+
680
+ ---
681
+
682
+ ## How Lasso fits with pi-duroxide
683
+
684
+ Lasso is distributed as a **pi extension** (`package.json` has a `"pi"` field
685
+ pointing to `./src/index.ts`). When you `pi install` it:
686
+
687
+ 1. pi loads `src/index.ts`, which exports a default extension function
688
+ 2. That function (`src/pi/extension.ts`) first boots `pi-duroxide`
689
+ 3. Then it registers the 5 slash commands with pi's `ExtensionAPI`
690
+
691
+ The layering:
692
+
693
+ - `pi-duroxide` owns workflow lifecycle, replay, timers, events, and runtime registration
694
+ - Lasso owns spec validation, CIR lowering, optimization, compilation, and operator-facing commands
695
+
696
+ In other words: `pi-duroxide` is the durable runtime engine; Lasso is the
697
+ harness generation, optimization, and adaptation layer built on top of it.
698
+
699
+ ## Non-goals
700
+
701
+ Lasso does **not** currently aim to provide:
702
+
703
+ - live GitHub or `gh` integration
704
+ - autonomous code authoring or patch generation
705
+ - LLM-backed planning or replanning (all planning is deterministic)
706
+ - automatic compile/run behavior from `/lasso:plan` or `/lasso:replan`
707
+ - arbitrary generated TypeScript
Binary file