vein: chat builder gets graph_query, rollback, run control, and validate_workflow - #1647
Merged
Conversation
Lets the assistant verify what a workflow's graph/* steps actually wrote (counts by type, exact properties, edge fan-out) — questions the typed read steps can't answer (search is ranked+limited; get/neighbors need a ref_id). - src/graph/query.ts: readQuery() — write-keyword pre-check (literals and comments stripped) for a clear error, then a READ-mode transaction as the real guarantee; streamed row cap (default 100, max 1000), server-side tx timeout, long strings truncated, embedding vectors collapsed. - ai/tools.ts: graph_query tool, offered only when AiDeps.graph is wired (same gating pattern as bash/web_search); description carries the deployment namespace so the model filters on it. - createVein: VeinOptions.graph (type-only import); server.ts passes the backend behind its graph-backed workspace. - prompts.ts: tool doc + "verify with graph_query after graph-writing runs". - Chat tool only, deliberately not a workflow step: raw Cypher in YAML would bypass the graph/* steps' schema validation + embeddings-on-write, and the workspace itself lives in this graph. Tests: pure guard/compaction cases, tool gating, and live cases against the throwaway Neo4j (caps, params, server-side write rejection). Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Two gaps the store/API already supported but the assistant couldn't reach: - set_active_version(kind, name, version): make a prior workflow or custom step version the active one without publishing. edit_step/edit_workflow already promised "prior versions kept for rollback" — this is the tool that delivers it (the alternative was republishing old source as a fresh version). Step switches refresh the registry; refused when publishing is disabled, mirroring the HTTP route. - cancel_run / pause_run / resume_run: control a run that is live in this process. run_workflow auto-detaches long runs, so a builder could launch but never abort. Gated on AiDeps.controlRun, wired by createVein over the same controllers map as the HTTP endpoints — the cancel/pause routes now share one controlLiveRun helper with the chat tool. Tests: workflow + step rollback round trips (history kept, bad version / missing name errors, publishing-disabled refusal), run-control tool gating and relay. Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Every publish is a version, so the builder had no way to find a typo except by publishing it. validate_workflow(yaml) is the compile step: pure, no I/O (src/validate.ts), fed the registry + workflow list by the tool. Errors (would fail or hang at run time): YAML / unquoted-template problems, missing/duplicate/unreferenceable step ids, unknown step types, `depends` on unknown ids (the runner would start the step early with an undefined input), self-deps and dependency cycles (the run would hang forever), template references to unknown roots, token-level template syntax errors, config that fails the step's input schema (template-valued fields skipped — they resolve at run time), `when` without depends, loop without `until`, foreach without `items`, bad `retry`, subflow targets/versions that don't exist. Warnings: unknown config fields, `when` without an `if` gate among its depends, template refs to steps that aren't upstream dependencies. Loop/foreach bodies are checked with $current/$index in scope, onError steps with $error; a loop's own `until` sees $current. expr.ts gains exprRoots() (scope roots an expression reads — member names and lambda params excluded; tokenizer only) and templateExprs(). Prompt: step 4 is now "validate, fix errors, then create_workflow". Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Same check as validate_workflow, run before the publish. On errors the tool
returns { error, validation } and writes nothing — the error is one
readable paragraph: "Not published: the workflow YAML has N validation
errors (nothing was written; no version was created)", the path: message
list, and "fix these and call again — validate_workflow re-checks without
publishing". Warnings never block; they ride along on the success result.
validateWorkflowYaml gains opts.name (the publish name): a YAML without
`name:` passes, as the publish stamps it in, and a subflow may reference
the workflow itself before its first publish.
Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
What
Four additions to the AI builder's toolbelt:
graph_query— read-only raw Cypher against the vein graph, so the builder can verify what a workflow'sgraph/*steps actually wrote (counts by type, exact properties, edge fan-out) or inspect graph-backed workspace state.set_active_version(kind, name, version)— rollback for workflows and custom steps.edit_step/edit_workflowalready promised "prior versions kept for rollback"; this is the tool that delivers it instead of republishing old source as a fresh version.cancel_run/pause_run/resume_run— control a run that is live in this process.run_workflowauto-detaches long runs, so the builder could launch but never abort.validate_workflow(yaml)— static check before publishing, so a typo doesn't become a version.graph_query design
n.data_setor'reset'don't trip it) gives a clear error; the real guarantee is a READ-mode transaction, which the server rejects writes in. Matters more than usual since vein: graph workspace by default (VEIN_WORKSPACE_BACKEND=fs to opt out) #1632: the workspace itself lives in this graph.[vector: N numbers].bash/web_search. Offered only whenAiDeps.graphis wired;server.tspasses the backend behind its graph-backed workspace via newVeinOptions.graph(type-only import —neo4j-driverstays lazy). Tool description carries the deployment namespace so the model filters on it.graph/*steps exist for.Run control wiring
AiDeps.controlRunis wired bycreateVeinover the samecontrollersmap the HTTP endpoints use; the cancel/pause routes were refactored to share onecontrolLiveRunhelper with the chat tool (no behavior change — same status codes, same control-event markers).validate_workflow
Pure module (
src/validate.ts), fed the registry + workflow list by the tool.Errors (would fail or hang at run time): YAML / unquoted-template problems; missing, duplicate, or unreferenceable step ids; unknown step types;
dependson unknown ids (the runner would start the step early with an undefined input); self-deps and dependency cycles (the run hangs forever); template refs to unknown roots; token-level template syntax errors; config that fails the step's input schema (template-valued fields are skipped — they resolve at run time);whenwithout depends; loop withoutuntil; foreach withoutitems; malformedretry; subflow targets/versions that don't exist.Warnings: unknown config fields;
whenwithout anifgate among its depends; template refs to steps that aren't upstream dependencies.Publishing is gated on it.
create_workflow/edit_workflowrun the same check first; on errors they return{ error, validation }and write nothing — the error is one readable paragraph ("Not published: … N validation errors (nothing was written; no version was created)", thepath: messagelist, "fix these and call again — validate_workflow re-checks without publishing"). Warnings never block; they ride along on the success result. A YAML withoutname:still passes (the publish stamps it in).Loop/foreach bodies are checked with
$current/$indexin scope,onErrorsteps with$error.expr.tsgainsexprRoots()(tokenizer-only root extraction; member names and lambda params excluded) andtemplateExprs(). The system prompt's step 4 is now "validate, fix errors, then create_workflow".Tests
src/graph/query.test.ts— pure guard/compaction + tool gating (always run); live cases against the throwaway Neo4j (params, row cap, default cap, pre-check rejection, server-side write rejection, syntax errors, empty-result columns).src/ai-integration.test.ts— workflow + step rollback round trips (history kept, bad version / missing name errors, publishing-disabled refusal); run-control tool gating and relay.src/validate.test.ts— exprRoots/templateExprs; a full well-formed workflow (if-gate, loop, foreach, subflow, onError) validates clean; each error/warning class has a case;ai-integration.test.tscovers the publish gate (refusal text, nothing written, warnings on success, edit leaves the version untouched).Full
npm testgreen (663);tsc --noEmitclean.🤖 Generated with Claude Code