v2.0.0next
HTTP API

Ops for each draft area

The ops each kind of draft takes, one entry per op, with the answer each gives, and a work item's own draft.

These are the ops a draft takes in each area, one entry per op, and the routes and ops of a work item's own draft. They are the Templates and Settings editors' own protocol, so a script rarely needs them. The routes that carry them are on Drafts, and like the other board routes they can change in any release, as Filing, reading and changing work items says.

An op that cannot apply is refused with 422 by POST /api/drafts/{area}/{key}/ops, and nothing is saved. Each entry below names its own reasons.

On every chain, library and settings area, an op name that does not exist, a field the op does not take, or a required field that is missing is refused with 422 too, and fails the whole request. A field is required unless its entry marks it optional.

The settings areas (harnesses, repos, policy, intake) write over each file's own text, so comments stay. An op whose result the file's layout cannot take back is refused with 422, and the message says to edit the file by hand.

Op or routeAreaWhat it does
add_nodechainsInserts a node.
add_stepchains, libraryInserts an empty step.
add_taskchains, libraryAppends a task, or fills a named slot.
add_handlerchains, libraryAdds an on_failure, fix_loop or on_conflict handler.
remove_handlerchains, libraryRemoves a handler.
movechains, libraryMoves a node, step or task.
removechains, libraryRemoves a component.
extendchains, libraryMakes a node extend a library node.
change_basechains, libraryChanges the library node a node extends.
new_chainchainsStarts the key's chain.
delete_chainchainsDeletes the chain on publish.
set_fieldchains, librarySets one field.
reset_fieldchains, libraryRemoves an override, so the inherited value applies.
renamechains, libraryRenames a component, or the chain.
set_fragmentchains, libraryReplaces a component with YAML.
move_to_librarychainsMoves a node or task into library.yaml.
add_componentlibraryAdds an empty library component.
set_accessharnessesSets a harness to available, override or never.
set_allowed_toolsharnessesSets the tool ceiling.
set_escalationharnessesSets where escalation runs and what it may do.
set_harnessharnessesEdits a harness's own fields.
add_profileharnessesAdds an agent profile.
rename_profileharnessesRenames an agent profile.
remove_profileharnessesRemoves an agent profile.
set_profileharnessesEdits an agent profile.
add_reporeposAdds a repo entry.
set_reporeposEdits a repo entry.
remove_reporeposDrops a repo entry.
connect_detectedreposConnects a detected repo.
set_valuepolicySets one setting of policy.yaml.
set_looppolicySets one fix loop's caps.
remove_looppolicyDrops a fix loop's entry.
set_intakeintakeSets the intake settings.
add_scheduleintakeAppends a schedule.
set_scheduleintakeChanges a schedule.
remove_scheduleintakeDrops a schedule.
GET /api/work-items/{id}/draftitemReads an item's draft.
PUT /api/work-items/{id}/draftitemReplaces an item's op list.
DELETE /api/work-items/{id}/draftitemDiscards an item's draft.
POST /api/work-items/{id}/draft/applyitemApplies an item's draft.
overrideitemSets what a retry override may set.
add_node on an item draftitemAdds a node from library components.
remove_nodeitemTakes a node out of the chain.
skipitemSkips a step or a task.

Each area's resolved answer is described at the end of its section: chains and library, harnesses, repos, policy, intake. The item draft's is The item draft answer.

Chain and library ops

These ops edit a chains or library draft. An op edits the chain's model rather than its text, so it writes the whole file back: the file's comments are dropped, and warnings says so until a PUT of that file.

  • A path is a component's canonical path (draft_merge_request.open.open, verification.fix_loop.judge).
  • A container is a node or one of its handlers (spec, spec.on_failure).
  • A position (at, to) counts from 0. One outside the list is refused.
  • An id is a lowercase letter, then lowercase letters, digits, _ or -. It is refused when it is main, on_failure, fix_loop, judge, escalation, on_base_changed or on_conflict, or when its container already holds it.

On the library draft, a path starts with the component's section: nodes.<name>[.<step>[.<task>]], steps.<name>[.<task>], tasks.<name> or steering.<name>. Every op applies there except add_node, new_chain, delete_chain, move_to_library and a rename of the chain, and add_component applies only there. An op on the wrong kind of draft is refused.

add_node

Inserts a node into the chain. Chain drafts only.

  • Fields: at (integer position), id (string), kind (exec or gate).
  • Answers nothing. An exec node starts with no step, which is a problem until it has one.
  • Refused when id is not allowed, kind is neither value, or at is outside the list.

add_step

Inserts an empty step into a container.

  • Fields: container (path), at (integer position), id (string, optional).
  • Answers {path}, the new step's. The default id is step_<n>, where n is one more than the number of steps the container holds. A container holding only main renames it step_1 first.
  • Refused when id is not allowed, at is outside the list, or nothing is at container.

add_task

Appends a task to a step, or fills a named slot of a node. Give the step form (container and step) or the slot form (slot and node), and kind or extends.

FieldTypeDefaultMeaning
containerpathnoneStep form: the node or handler that holds the step.
stepstringnoneStep form: the step's id.
slotstringnoneSlot form: judge (the node needs a fix loop), escalation or auto_review.
nodepathnoneSlot form: the node that holds the slot.
kindstringnoneThe task kind: builtin, agent, subprocess or forge.
extendsstringnoneA library task to extend.
idstringThe kind or library name, made unique with _2, _3…Step form only. The slot form's id is judge, escalate or reviewer.
  • Answers {path}, the new task's: <container>.<step>.<id>, <node>.fix_loop.judge, <node>.escalation.escalate or <node>.auto_review.
  • Refused when neither form is complete, both or neither of kind and extends is given, kind is not a task kind, slot is not one of the three, a judge slot has no fix loop, or id is not allowed.

add_handler

Adds a handler holding one empty step, main. An on_conflict handler is kept under on_base_changed.

  • Fields: path (the node), kind (on_failure, fix_loop or on_conflict).
  • Answers {path}, the handler's.
  • Refused when kind is none of the three, or nothing is at path.

remove_handler

Removes a handler: it is written null when the node's library base supplies one, and deleted otherwise.

  • Fields: path, kind, as for add_handler.
  • Answers nothing.
  • Refused when kind is none of the three, or nothing is at path.

move

Moves a node, a step, or a task to position to. A task also moves to the step to_step beside its own.

  • Fields: path, to (integer position), to_step (a step id, optional).
  • Answers nothing.
  • Refused when nothing is at path, to is outside the list, or to_step is not a step beside the task's own. It is also refused when the destination step already holds a task of that id, or when a node move adds an order problem: a reject_to or restart_from target after the node naming it, or a gate before the node producing its document.

remove

Removes a node, gate, step, task, fix_loop, judge, escalation or auto_review. On the library, it removes a component such as tasks.implementer.

  • Field: path.
  • Answers {broken: [...]}, the references the removal leaves dangling. They stay as problems. Removing a node lists each reject_to and restart_from naming it, as {path, field}. Removing a library component lists each extends or steering entry naming it, as {file, path, field}. Removing anything else answers [].
  • Refused when nothing is at path.

extend

Makes the node extend library node base, dropping its own kind, steps, on_failure and fix_loop.

  • Fields: node, base.
  • Answers {dropped: [...]}, the keys among steps, on_failure and fix_loop that held something.
  • Refused when the library has no node base, or nothing is at node.

change_base

Changes the library node a node extends, keeping each override the new base can take.

  • Fields: node, base.
  • Answers {kept: [...], dropped: [{key, why}]}. With ?preview=1 on the route, the answer shows what a change would drop and saves nothing.
  • Refused when the library has no node base, or nothing is at node.

new_chain

Starts the key's chain, empty or as a copy of chain from under this id. Chain drafts only, and only for a key with no file and no draft.

  • Field: from (optional): a chain id, taken from that chain's draft when it has one, else its published file.
  • Answers nothing.
  • Refused when the key already has a chain, from is not a chain id or names no chain, or any other field is given.

delete_chain

Deletes the chain on publish. Chain drafts only. A repo whose default it is makes that a problem.

  • Fields: none.
  • Answers nothing.
  • Refused when the key has no chain.

set_field

Sets one field of a component. path: "" is the chain.

  • Fields: path, field (a dotted string), value (any JSON value).
  • Answers nothing.
  • Refused when field is id ("rename it with rename"), or nothing is at path.

How the field is written:

  • field is dotted: policy.max_attempts, on_base_changed.restart_from, wait.polling.initial_interval.
  • null or "" removes the key, and so does a value equal to the one the component inherits.
  • Setting profile removes model and effort, and setting either removes profile.
  • An empty on_base_changed.restart_from removes on_base_changed (written null when inherited), an empty artifact removes artifact_required with it, and artifact_required: false removes it.
  • icon is set like any other field.

reset_field

Removes the key so the inherited or default value applies. With no field, it removes every override of a component that extends a library one, except id, extends, icon and on_failure.

  • Fields: path, field (optional).
  • Answers nothing.
  • Refused when nothing is at path, or no field is given and the component extends nothing.

rename

Renames a component, or the chain.

  • Fields: path, id.
  • Answers {updated: [...]}. Renaming to the id it already has changes nothing and answers [].
  • Refused when nothing is at path, id is not allowed or taken, or, for the chain, is not a chain id or names a chain that exists.

What each kind of path does:

  • A node or gate also rewrites every reject_to and on_base_changed.restart_from naming it, and answers each as {path, field}.
  • A step, task, judge, escalation or reviewer changes its id only.
  • The chain (path: "") moves to chains/<id>.yaml, its old file deleted on publish. A repo whose default is the old id makes that a problem.
  • A library component (path: tasks.implementer) also rewrites every extends: or steering: entry naming it, in the library and in each chain file, which joins the draft. It answers each as {file, path, field}.
  • A gate's artifact names a document kind, not a node, so a rename never changes it.

set_fragment

Replaces the component at path with the YAML mapping, as the component's YAML tab writes it.

  • Fields: path, yaml (the fragment, as text).
  • Answers nothing.
  • Refused when nothing is at path, the YAML does not parse (the 422 adds line and col, counted in the fragment), it is not a mapping, or it changes the component's id ("rename it with rename").

move_to_library

Moves the exec node or task at path into library.yaml as nodes.<name> or tasks.<name>, leaving {id, extends: <name>} in the chain. library.yaml joins the draft. Chain drafts only.

  • Fields: path, name.
  • Answers {path}, the component's in the library.
  • Refused when path is a step or a gate, nothing is at path, or name is not allowed or its section already has it.

add_component

Adds an empty component to a section of the library. Library drafts only.

  • Fields: section (nodes, steps, tasks or steering), name, kind (optional).
  • Answers {path}, such as tasks.reviewer. A nodes component is an exec node with no step, or a gate for kind: gate. A tasks component has the kind, when given. A steering component has empty instructions, a problem until written.
  • Refused when section is none of the four, name is not allowed or taken, kind is not a node or task kind, or kind is given for steps or steering.

Harnesses ops

These ops edit the harnesses draft (harnesses.yaml and policy.yaml). Each harness is available, override or never. That is two allowed_harnesses lists in policy.yaml:

  • available is in defaults and maxima.
  • override is only in maxima.
  • never is in neither.

An absent list means no bound. A list is written again only when one was there to begin with, or when it leaves a harness out.

A task that selects a never harness, or falls back to one, is a problem that names it, so the draft cannot publish until the task or the state changes. An override harness is allowed only for a task whose own policy: widens it.

set_access

Moves a harness to available, override or never.

  • Fields: harness, state.
  • Answers nothing.
  • Refused when the harness is not in harnesses.yaml, or state is none of the three.

set_allowed_tools

Sets maxima.allowed_tools, or removes it with null.

  • Field: tools (a list of names, or null).
  • Answers nothing.
  • Refused when tools is neither.

set_escalation

Sets defaults.escalation_harness and escalation_grants. null removes each.

  • Fields: harness (a harness id, item for the harness the item's own work ran on, or null), grants (a list of names, or null).
  • Answers nothing.
  • Refused when harness is not text or null, or grants is neither a list of names nor null.

set_harness

Edits a harness's own fields in harnesses.yaml.

  • Fields: id, patch (a mapping).
  • Answers nothing.
  • Refused when id is not in harnesses.yaml, patch is empty or not a mapping, it holds a key other than the three below, or a value has the wrong type (enabled is not a boolean, executable is blank or not text, defaults is not a mapping of option to text or null).

patch may hold:

  • enabled, a boolean.
  • executable, a command name or path; null clears it.
  • defaults, which merges per option; null or an empty text removes one.

An option the provider does not accept is a problem, not a refusal.

add_profile

Adds an agent profile, empty or a copy of another.

  • Fields: name, copy_from (optional).
  • Answers nothing.
  • Refused when name is not allowed or taken, or copy_from names no profile.

rename_profile

Renames a profile and retargets the fallback: entries of harnesses.yaml that name it.

  • Fields: name, to.
  • Answers {broken: [{chain, path}]}: the library or chain tasks that name the old profile. They are left dangling, as problems.
  • Refused when name is no profile, or to is not allowed or taken.

remove_profile

Removes a profile.

  • Field: name.
  • Answers nothing.
  • Refused when name is no profile, or a task or another profile's fallback: still names it.

set_profile

Edits one profile.

  • Fields: name, patch (a mapping).
  • Answers nothing.
  • Refused when name is no profile, patch is empty, not a mapping or holds an unknown key, or one patch mixes providers with model or effort. A model or effort patch to a profile written with providers is refused too, and so is a providers entry that is not {model, effort?} or a fallback that is neither a list nor null.

patch may hold:

  • providers: per provider {model, effort?}; null removes one. An older profile becomes this shape.
  • model and effort, for an older profile: model is a mapping by provider, and null removes an entry or effort.
  • fallback: replaces the list, or null removes it.

The resolved answer for harnesses

The answer's resolved lists:

  • harnesses: id, state, executable_found, and from harnesses.yaml its provider, enabled, executable and defaults. Each has tasks: the agent tasks that select it, {chain, path, profile, fallback: false}, and those whose fallback: list lands on it, with fallback: true.
  • allowed_tools.
  • escalation: what the file says, {harness, grants}.
  • escalation_effective: {harness, set}, the harness a launch uses, claude when the key is unset.
  • profiles: per name {providers, effort, tasks, used_by_fallback}: its providers, the one effort they share (else null), the tasks that use it, and the profiles whose fallback: names it.

A problem about a task of a chain carries chain, and that task's path where the message names one. A launch problem adds profile and provider. Escalation running on a never harness is a problem at defaults.escalation_harness in policy.yaml.

Repos ops

These ops edit the repos draft (repos.yaml). DELETE /api/repos?path= answers 409, naming the count, while any work item that has not ended (any status but completed or abandoned) uses the repo, as remove_repo reports it as a problem.

add_repo

Adds a repo entry, disabled and managed, with fields laid over {path, name, default_chain: "default", enabled: false, managed: true}.

  • Fields: path, fields (a mapping, optional).
  • Answers nothing. The path is not probed: a path that is not a git repository is a problem.
  • Refused when path is already in the draft, or is a git repository with no commit.

set_repo

Sets fields of an entry. Like PATCH /api/repos, it marks the entry managed.

  • Fields: path, patch (a mapping of entry fields; null clears one).
  • Answers nothing.
  • Refused when path is not in the draft.

remove_repo

Drops the entry. It is a problem while the repo has items that have not ended.

  • Field: path.
  • Answers nothing.
  • Refused when path is not in the draft.

connect_detected

Makes a detected (managed: false) entry managed: true.

  • Field: path.
  • Answers nothing.
  • Refused when path is not in the draft, or is not a detected entry.

The resolved answer for repos

The answer's resolved has repos and detected: one {path, name, managed, entry, resolved, sources} per entry, entry being the entry as written. resolved and sources each have steering, deny_tools, models and each policy key. The steering source is repo or library (the chain's steering), and the rest are repo or default.

Problems carry repo. They are:

  • a policy: that relaxes the instance's safety layer (the same refusal a run gives);
  • a path that is not a directory or not a git repository;
  • a default_chain that names no chain in the library; and
  • a removed repo that still has items that have not ended.

Policy ops

These ops edit the policy draft (policy.yaml).

set_value

Sets one setting by its dotted key in policy.yaml. null, or no value, removes it and any section it leaves empty.

  • Fields: scope, key, value (any JSON value, optional).
  • Answers nothing.
  • Refused when key is not one of the keys of that scope. The harnesses scope, and any other scope, is refused. So is setting a value under a parent key of policy.yaml that is not a mapping.
scopeKeys
limitsbudget.work_item_usd, budget.daily_usd, defaults|maxima.max_attempts, defaults|maxima.timeout_minutes and defaults|maxima.<level>.<cap>. A <level> is work_item, nodes, steps or tasks, and a <cap> is time_cap_minutes, total_time_cap_minutes, token_budget or budget_usd.
escalationauto_escalate_stuck, auto_escalate_stuck_cap, auto_escalate_delay_s and auto_review_attempts.
retriesrate_limit_retries and forge_cli_timeout_s.
loopsdefault.attempts and default.wall_clock_s (the default: block).
housekeepingmax_concurrent and archive.after_days.
findingsfindings.loop_severities (a list of critical, important, minor).

set_loop

Sets the loops: entry for a fix loop, <node id>.fix_loop, starting from the entry's own values, else the default: block. A key naming no loop is kept and shown as a problem.

  • Fields: key, max_attempts (integer, optional, written as attempts), wall_clock_s (integer, optional).
  • Answers nothing.
  • Refused when loops in the file is not a mapping.

remove_loop

Drops the loops: entry.

  • Field: key.
  • Answers nothing.
  • Refused when key is not in loops.

The resolved answer for policy

The answer's resolved is null when the draft's policy.yaml does not load. Otherwise it has the keys below. Each value is {value, source}, source being policy or default, unless a row says otherwise.

KeyHolds
limitscaps, work_item_usd, daily_usd, and max_attempts and timeout_minutes. Described below.
loopsdefault ({attempts, wall_clock_s}), entries ([{key, attempts, wall_clock_s, live}]) and live, below.
escalation, retriesOne value per key named under set_value.
housekeepingmax_concurrent and archive_after_days.
findingsloop_severities.
harnesses, otherRead-only: the harness fields of defaults and maxima, and the findings, archive and max_concurrent values.

limits holds:

  • caps: per level and cap, {default, maximum, below}. default and maximum are {value, source}, source being the level (work_item, nodes, steps, tasks) that sets it. An unset value is {value: null, source: null}.
  • below is every scope of the published library's chains that sets that cap itself, {layer, chain, path, via, value, exceeds}, and [] when none does.
  • In below, layer is chain (the chain file) or library (a component it extends, named in via). path is the scope's canonical path (the chain's id for the chain itself), and exceeds is whether value is past the draft's maximum at that level.
  • work_item_usd also has binding: {key, value}, the lowest of the dollar caps (budget.work_item_usd, maxima.work_item.budget_usd, defaults.work_item.budget_usd) and the key that sets it, or null when none is set.
  • max_attempts and timeout_minutes are each {default, maximum}.

loops.live lists every fix loop in the library, {key, chain, node, attempts, wall_clock_s}. attempts and wall_clock_s are each {value, source}, source being the layer that wins: loop, node, chain, defaults or maxima, loops, or default. A wall clock has no loop layer. A work item's own override is not in it.

Problems carry scope (the group) and, for a cap, level. A loops: key naming no loop in the library is a problem, and so is a limit a chain already exceeds, which names the chain.

Intake ops

These ops edit the intake draft (intake.yaml, its schedules: included).

set_intake

Sets intake settings in intake.yaml, which gets its default for any of the four keys it did not have: enabled: false, interval_s: 300, priority_ceiling: 2 and repos: [].

  • Field: patch (a mapping) holding any of enabled, interval_s, priority_ceiling and repos.
  • Answers nothing. The op does not check values: an interval_s under 30 or a priority_ceiling outside 0 to 4 is a problem.
  • Refused when patch holds any other key. max_concurrent is the policy draft's.

add_schedule

Appends a schedules: entry.

FieldTypeDefaultMeaning
cronstringnoneThe cron expression.
repostringnoneThe repo's path.
chainstringnoneThe chain the item runs.
titlestringnoneThe item's title.
descriptionstring""The item's description.

Answers nothing. Refused when schedules in the file is not a list, or a required field is missing.

set_schedule

Changes fields of the entry at index.

  • Fields: index (integer), patch (a mapping of any of cron, repo, chain, title, description).
  • Answers nothing.
  • Refused when there is no schedule at index, or patch holds a key that is no schedule field.

remove_schedule

Drops the entry at index.

  • Field: index (integer).
  • Answers nothing.
  • Refused when there is no schedule at index.

The resolved answer for intake

The answer's resolved is {enabled, interval_s, priority_ceiling, repos, schedules}, each schedule {index, cron, repo, chain, title, description}. A schedule whose repo is not connected, whose chain is not in the library, or whose cron is invalid is a problem carrying its schedule index.

A publish applies without a restart: the poller is replaced, and the new interval and schedules are live.

Item drafts

A work item's own chain has one draft too: a list of ops a person has not applied yet, kept until it is applied or discarded.

Every route here is refused with 403 for a worker's own item (a worker cannot read or change its own item's draft), 404 for an unknown work item, and 409 for an ended item.

An escalation turn is not a worker, so it may use these routes on the item it is helping with. GET, PUT and apply are also refused with 409 for an item with no materialized chain; a PUT has stored its ops by then, so the draft is kept.

GET /api/work-items/{id}/draft

Reads the item's draft. With no draft, ops is [] and nodes is the item's chain. Answers The item draft answer.

Also refused as under Item drafts: 403 for a worker's own item, 404 for an unknown item, 409 for an ended one.

PUT /api/work-items/{id}/draft

Replaces the whole op list. An empty list deletes the draft.

Body: {"ops": [{"op": "skip", "path": "…"}]}. Answers as GET /api/work-items/{id}/draft does.

Refused with 422 for a malformed op: one the item draft ops do not know, or with a field they do not take.

Also refused as under Item drafts: 403 for a worker's own item, 404 for an unknown item, 409 for an ended one.

DELETE /api/work-items/{id}/draft

Discards the draft. Answers 204 with no body. Refused with 404 when there is no draft.

Also refused as under Item drafts: 403 for a worker's own item, 404 for an unknown item, 409 for an ended one.

POST /api/work-items/{id}/draft/apply

Applies every op at once, in one transaction. No body.

On success it answers as GET /api/work-items/{id}/draft does, with the draft gone. The revised chain replaces the item's, recorded as chain_revised with source: "draft", each skip is recorded as scope_skipped, and the draft is deleted. A draft of only skip ops revises no chain. On a running item it takes effect at the next node boundary: the node running finishes as it started.

StatusWhen
404The item has no draft.
409An op is passed: the item has moved past some of the draft's ops. Nothing is written. The body is below.
422An op has a problem. The body is {detail, problems}.
{"detail": "the item has moved past some of this draft's ops; remove them or move them after the current node",
 "passed": [0]}

Nothing is moved for you: drop a passed op from the list, or change it to act after the current node.

Also refused as under Item drafts: 403 for a worker's own item, 404 for an unknown item, 409 for an ended one.

The item draft answer

{
  "ops": [{"op": "skip", "path": "verification.main.lint", "passed": false}],
  "problems": [],
  "checks": {"budget": {"spent_usd": 1.2, "cap_usd": 5.0}, "added": []},
  "nodes": ["…"],
  "base_seq": 41,
  "updated_at": "2026-10-07T09:12:44.123456+00:00"
}
FieldMeaning
opsThe draft's ops as stored, each with passed: true once the item has reached the node it acts on (for add_node, gone past the node it follows). A passed op is not applied.
problems[{op, message}], op being the op's index.
checks{budget, added}. See checks.
nodesThe chain, in the chain_definition node shape, with every op applied that is neither passed nor a problem.
base_seqThe item's latest event number when the draft was created, kept across later writes. null with no draft.
updated_atThe draft's last write, or null with no draft.

checks

  • budget is {spent_usd, cap_usd}, the item's spend against its cap, as budget_cap on GET /api/work-items/{id} reports it.
  • added has one {op, node, harnesses, estimate_usd, estimate_runs} for each add_node that is not passed and has no problem.
  • harnesses are the harnesses its agent tasks run on. One outside the policy's allowed_harnesses is a problem on that op, so these are allowed.
  • estimate_usd is what a node of that id has cost per item, averaged over estimate_runs items of the instance. Both are null when none has run.

The estimate is information and blocks nothing.

Item draft ops

Each op below is an element of the ops list of an item draft. Its refusals are problems entries on that op, except a malformed op, which is the PUT's 422.

override

Sets what a retry's override may set at a path, within the same bounds: no structural field, policy only tightened or kept under the maxima, and a harness inside allowed_harnesses.

  • Fields: path, task_config (a mapping, optional), policy (a mapping, optional).
  • Problem when the override is outside those bounds. For one outside its policy maximum, the message starts with the field.

add_node on an item draft

Adds a node built from library components only, as a chain revision adds one. It is always an exec node.

  • Fields: after (the id of the node it follows), node (an object).
  • node is {id, extends}, {id, tasks} or {id, steps}, of library tasks. It holds exactly one of the three.
  • Problem when the library cannot build the node.

remove_node

Takes the node out of the chain.

  • Field: node.
  • Problem when the node is a gate, is not skippable, or lands the merge request.

skip

Skips a step or a task. Applying records it as scope_skipped, so the walk passes it.

  • Field: path.
  • Problem when path names nothing, is a node (use remove_node), or does not allow skipping.
  • Drafts: the routes that carry these ops, and the result every draft answers.
  • Templates and Settings: the screens whose edits become these ops.
  • HTTP API: who may call, and which routes are stable.
Copyright © 2026