Skip to content

vein: chat builder gets graph_query, rollback, run control, and validate_workflow - #1647

Merged
Evanfeenstra merged 4 commits into
mainfrom
graph-query-tool
Sep 4, 2026
Merged

vein: chat builder gets graph_query, rollback, run control, and validate_workflow#1647
Evanfeenstra merged 4 commits into
mainfrom
graph-query-tool

Conversation

@Evanfeenstra

@Evanfeenstra Evanfeenstra commented Sep 4, 2026

Copy link
Copy Markdown
Collaborator

What

Four additions to the AI builder's toolbelt:

  1. graph_query — read-only raw Cypher against the vein graph, so the builder can verify what a workflow's graph/* steps actually wrote (counts by type, exact properties, edge fan-out) or inspect graph-backed workspace state.
  2. set_active_version(kind, name, version) — rollback for workflows and custom steps. edit_step/edit_workflow already promised "prior versions kept for rollback"; this is the tool that delivers it instead of republishing old source as a fresh version.
  3. cancel_run / pause_run / resume_run — control a run that is live in this process. run_workflow auto-detaches long runs, so the builder could launch but never abort.
  4. validate_workflow(yaml) — static check before publishing, so a typo doesn't become a version.

graph_query design

  • Read-only, enforced twice. A write-keyword pre-check (string literals / backticks / comments stripped, so n.data_set or '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.
  • Capped. Streamed row cap (default 100, max 1000 — the remainder is never fetched), server-side tx timeout (15s), long strings truncated, embedding vectors collapsed to [vector: N numbers].
  • Gated like bash/web_search. Offered only when AiDeps.graph is wired; server.ts passes the backend behind its graph-backed workspace via new VeinOptions.graph (type-only import — neo4j-driver stays lazy). Tool description carries the deployment namespace so the model filters on it.
  • Chat tool, deliberately not a step. Raw Cypher in published YAML would bypass the schema validation + embeddings-on-write the graph/* steps exist for.

Run control wiring

AiDeps.controlRun is wired by createVein over the same controllers map the HTTP endpoints use; the cancel/pause routes were refactored to share one controlLiveRun helper 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; depends on 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); when without depends; loop without until; foreach without items; malformed 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.

Publishing is gated on it. create_workflow / edit_workflow run 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)", the path: message list, "fix these and call again — validate_workflow re-checks without publishing"). Warnings never block; they ride along on the success result. A YAML without name: still passes (the publish stamps it in).

Loop/foreach bodies are checked with $current/$index in scope, onError steps with $error. expr.ts gains exprRoots() (tokenizer-only root extraction; member names and lambda params excluded) and templateExprs(). 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.ts covers the publish gate (refusal text, nothing written, warnings on success, edit leaves the version untouched).

Full npm test green (663); tsc --noEmit clean.

🤖 Generated with Claude Code

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>
Evanfeenstra and others added 2 commits September 4, 2026 09:25
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>
@Evanfeenstra Evanfeenstra changed the title vein: read-only graph_query tool for the chat builder vein: chat builder gets graph_query, rollback, run control, and validate_workflow Sep 4, 2026
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>
@Evanfeenstra
Evanfeenstra merged commit 6232346 into main Sep 4, 2026
6 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant