feat(jobs): node-local job ownership and capability-gated job__* visibility

Graph LLM nodes now own the jobs they start, on every exit path. A new
node_job_scope on RequestContext records job ids started while a node
runs: the turn-end guardrail nags only about the node's own jobs
(parallel branches no longer see each other's), and the node executor
reaps — cancels and deregisters — anything left registered when the
node exits, including error, timeout, and retry-exhaustion paths.
Cross-node job handoff is no longer possible; a crashed node takes its
in-flight jobs with it.

With inheritance gone, job__* declarations are gated on capability:
the family is only declared when at least one declared tool would pass
job__start's whitelist (shared predicate: is_backgroundable_tool). One
carve-out — while a context still owns registered jobs (job started,
tool disabled mid-session), the lifecycle verbs stay declared so a
running job can never become unreachable; job__start alone disappears.
A graph node with tools: [] now sees no job__* tools at all.

Prompt instructions, tool declarations, and graph.example.yaml updated
to the node-local semantics; +7 tests, 8 visibility pins rewritten.
This commit is contained in:
2026-08-26 13:43:05 -06:00
parent bfb8105682
commit 404a45a311
7 changed files with 311 additions and 45 deletions
+4 -4
View File
@@ -201,10 +201,10 @@ pub(in crate::config) const DEFAULT_JOB_INSTRUCTIONS: &str = indoc! {"
jobs with `job__list`. Collected results over 50,000 chars are tail-capped; collecting is
consume-once, so when you need the complete output pass `full_result: true` (or have the
command write to a file). Collect or cancel every job you started before ending your turn. In
graph LLM nodes, collect or cancel your jobs before ending your final node turn — an
uncollected job at node turn-end burns node iterations via the guardrail and can fail the
node. Jobs run against a snapshot of the current config/environment and do not survive
coyote exiting.
graph LLM nodes, jobs are node-local: collect or cancel every job you start before the node
ends — an uncollected job burns node iterations via the guardrail, and anything still
running when the node exits is cancelled with its result discarded. Jobs run against a
snapshot of the current config/environment and do not survive coyote exiting.
"
};