O.G.A.S. Русский

Methods of the graph

The methods of the function of the graph: processes and programs, runs, variables, questions, groups. Needed by whoever calls the graph from the window, a script or another function.

1 The methods of the function of the graph

The document is a part of the contract Exchange with the kernel: the methods with which the workshop and other parts change a process, switch a program on and lead a run. The meaning of the graph, the program, the run, variables and questions — The program: graph and runs. The wrapper of a message — Exchange with the kernel, section 3, the refusal codes — Error codes.

Table 1 — The methods of the function of the graph

Method Type Parameters Result
process.create command name { process_id }; a new process already holds the pulpit event run and the variable input of type text — it can be switched on and run at once, and a field of the pulpit writes the variable. The pulpit and the values of the settings of the process are made by their owners, the graph of the process is written last
process.export command process_id, optionally include_state { path, process_id, modules, calls, links_left_out } — the export folder data/exports/<process_id>/, the former one is replaced; with include_state: true the saved memory of the process goes into the pack — only with the mark of saving and only that which a module that is up now does not serve (module.state.export); calls — the called processes that went into the pack, modules — the modules of all processes of the pack. A called process without a graph is a refusal of the export (document_not_found). The documents of other owners the graph takes from them: the pulpit — canvas.get, and its pictures and looks — canvas.files.export, the values of the process level — settings.get with level: "process", the versions of modules — module.lock.get
process.delete command process_id { process_id, deleted: true }: the folder of the process is deleted whole — the graph, the pulpit, runs and their files, remembered values, the memory of nodes, the Python environment; the event process.changed with command: "process.delete". A program that is on or going is not deleted — process_running; a program another one calls — process_in_use, and names names the callers (The workshop, section 4)
process.inspect query path of an export { path, name, nodes, documents, calls, modules, state } — what the import will bring, writing nothing; calls — the called processes { process_id, name, nodes }, modules — as at module.inspect, state — the modules whose saved states lie in the pack
process.import command path of an export: an ogas.export/3 folder { process_id, calls, modules_added, modules_skipped, state }; calls — the former id of each called process → its new id; state — the modules whose saved states from the pack became states of the new process (module.state.import). The called processes are put as new processes before the caller, the calls are moved to their new ids. The documents of other owners they take themselves: the versions of modules — module.lock.import, the pulpit — canvas.save, the values of the process level — settings.set; what is not in the export is done as for a new process. The graph of the process is written last: a refusal of an owner is a refusal of the import, and there is no process. The pictures and looks of the pulpit, if the pack carries them, the owner takes after the graph — canvas.files.import
process.open query process_id process.json
process.save command process_id, document { generation }
process.validate query a document or process_id { ok, warnings } or the error graph_invalid; checks the structure, the keys of substitutions, that links go through ports the nodes have (The program: graph and runs, section 4), and the compatibility of the types of ports. warnings — what does not refuse the document: { node_id, warning }, that is no_hot_input (The program: graph and runs, section 6)
process.space query — { parts: [{ key, title_key, bytes, clear_method? }] } — the parts of the room and how much each takes on the disk; title_key — the name of the part in the dictionary of the owner, clear_method — the command that takes away what can be restored in this part (called with { part: key }): the programs with their files and the memory of nodes, and the runs with their results; the Python environment of a process is counted by the function of modules
process.list query — { processes }; a process — { process_id, name, call }, where call is what the process is for a node that calls it: { inputs, outputs, variables }, or null if it cannot be called (The program: graph and runs, section 18)
process.summary query — { processes } for the library: for a program — { process_id, name, description, nodes, modules, bytes, environment_bytes, runs, changed, outline }: modules — the modules of its nodes, bytes — its folder without the Python environment, environment_bytes — the environment .venv, runs — how many past runs it keeps, changed — when its document was written, outline — the outline of the graph for a preview: { nodes: [[x, y]], edges: [[from, to]] }, no more than 400 nodes, the links by the numbers of the nodes. Writes nothing
process.kinds query — { kinds, variables }: the kinds of nodes and the types of a variable of a process — { type, empty }, where empty is the value a variable of this type begins with; the window takes them from here and keeps no list of its own
process.node.template query kind { kind, template, about } — everything from the description of the kind in its folder functions/graph/src/kinds/<kind>/kind.json (rule 2.10). template — the node a new one begins with: kind, parameters and, for a module node, an empty module_id (the ports and parameters of a module node the window takes from module.list). about — what the kind says of itself and what is not copied into the document of the node: family — the family of the kind, by which the canvas colours the head of the node: start, work, flow, data or wait (Kinds of nodes, Appendix A); inputs, outputs — named as in the manifest of a module, and each port has type — for a node of the platform always json (The program: graph and runs, section 4), so the window reads the type of a port of a kind and of a module alike and keeps no rule of its own (for a module node — the output failed, which the graph adds to the ports of the manifest, The program: graph and runs, 12.4), fields — what the person sets for the node: { path, how, choices }, where how is how to read what is typed: text, number, json, value, choice (one of choices), variable (a variable of the program), process (one of the processes that can be called) and values (a value for each variable of the called process, under the path of the field); calls — for a kind whose node calls a process: the name of the parameter the node names it with (process_id) — by this sign the window takes the ports of the node from the call of the called process, node_fields — what the person sets for the node itself besides its parameters (File formats, 5.1): { name, how, choices, module, words }. First come the fields a node of any kind has — the name of the node name and its description description (how: long — a text of several lines); their words lie in the dictionary of the function of the graph itself, and words names what they begin with (graph.node.field.name, graph.node.field.description). After them — the fields the kind adds; module names what the manifest of the module of the node must say for the field to make sense — for overlap it is { "process": "service" }. cold_source: true — a node of the kind gives a value, and a link from it is not waited for: the canvas draws it dashed, variable_use — for a kind whose node names a variable: what the node does with it, reads, writes or both (a kind that did not say this reads); which parameter names the variable is said by the field of fields with how: "variable"; by this sign the canvas puts the variable on the node as a row of an input or an output (The workshop, 5.3), short_name — the beginning of the id of a new node, glyph — the sign of the kind (a path on a 16×16 grid), if the kind has one, objects — the kinds of shared objects shown with the sign of this kind: for an event event, for a signal signal, for a variable var; by this sign, not by the name of the kind, the window knows which sign to show an object with (rule 2.6), cases_name_outputs: true — the outputs of the kind are its cases, outputs holds only other, the rest the window takes from the cases of the node (The program: graph and runs, section 8), frame: { body, returns } — for a frame the output through which a packet enters the body and the inputs through which the body returns (The program: graph and runs, section 11), outside_frames: true — a node of the kind does not lie in a frame (an event, The program: graph and runs, section 4), names_module: true — a node of the kind names a module (module_id), and its ports, parameters and step come from the manifest of this module (The program: graph and runs, section 12); by this sign, not by the name module, both the graph and the window recognise a module node (rule 2.6). The words of a kind lie in its folder under the keys graph.kind.<kind>.name, .description, .input.<port>.title and .help, .output.<port>.title and .help (as for a module, Modules, table 2) and .param.<field>.title, .help, .choice.<value>; for a field of the node itself — .field.<field>.title, .help, .choice.<value>
process.node.candidates query node_id, process_id or document { node_id, existing, available, behind_faces, refused } — what this node can be linked with (The workshop, 5.2). A row of the list is a port: { direction: "into"\|"out_of", node_id, kind, module_id, their_port, their_type, my_port, my_type, occupied, cold }; a row of behind_faces — the same and with group_id; a row of refused — an input of another node an output of this one cannot be brought to, and why: { node_id, port, my_port, reason }
process.objects query process_id; an optional document — the graph the person is editing: then the objects are named by it, not by the written document { objects, renamed } — among the objects also out:<node_id>/<output> for each output of each node (node_id, port, for an output of a module — type by the manifest) and progress:<node_id> for each module node, in:<node_id>/<input> for each input (sources — where its data come from: { output, node_id, parts? } of each link) and edge:<edge_id> for each link (node_id and port — where it leads, source — where from) — the shared objects of the program a pulpit is built of (File formats, section 6): an object is { object_id, kind } and, depending on its sort, node_id or variable_id with type; renamed — the former object_id → the new one by the record renamed_objects of the document of the process (File formats, section 5). The list is not kept: it is derived each time from the nodes and variables
process.insert query process_id, source_process_id, optionally document { document, nodes, groups, edges, variables, skipped_events } — the document with the graph of another process added
process.group.wrap query process_id, node_ids and (or) group_ids of one level, optionally name, document { process_id, group_id, document } — the document with a new group around what is named (The program: graph and runs, 17.1)
process.group.unwrap query process_id, group_id, optionally document { process_id, document } — the document without the named group: what lay in it lies where it lay
process.group.move query process_id, node_ids and (or) group_ids, optionally group_id — where to, without it — out of all groups, to the level of their place — document { process_id, document } — the document where what is named lies in the group group_id; the faces follow the links (The program: graph and runs, section 17)
process.group.face query process_id, group_id — or, for a mark ahead on what lies at the top level, without it: of_group — the group whose row of the face or variable is marked — otherwise node_id — the node whose port or variable — what is marked — variable or node_id, port and side (in | out) — shown (true by default), optionally name, document { process_id, group_id, document } — the document with the face of the group changed; a port through which a link crosses the border is not taken off the face — graph_face_port_used
process.node.show command process_id, node_id, shown — the names of the parameters of a module or the paths of the fields of a kind the node shows on the canvas { process_id, node_id, shown }; the field shown_parameters of the node is written into the saved document at once, with the program on too: a run does not read it, the generation does not change. The node is not in the saved document — graph_invalid
process.group.detach command process_id, group_id, optionally name, document { process_id, node_id, made, variables, document } — the group became the process made: { process_id, name }, which is already written; document — the document with the call node node_id in the place of the group, variables — the variables that went into the new process (The program: graph and runs, 17.1)
process.variable.rename query process_id, variable_id, name, optionally document { process_id, variable_id, nodes, groups, callers, document } — the document with the variable renamed; nodes and groups — the nodes and groups that named it; callers — { process_id, name, node_id } of the calls in other processes that give a value to the variable under its former name
process.node.copy query process_id, node_ids and (or) group_ids, optionally document { process_id, nodes, groups, document } — the document with copies of what is named beside it; nodes and groups — the identifiers of the copies of the named nodes and groups, in the order of the request
process.runs.list query optionally process_id { runs, bytes, keep }; a run — { run_id, process_id, status, resumable, kept, active, modified_ms, bytes, event_node, error, stopped_by }, the newest first; bytes — the service folder of the run together with its results; event_node — the event that began the run (null for a check of a part), error — the cause of the refusal from the last checkpoint, stopped_by — who stopped a run with the outcome cancelled
process.runs.keep command run_id, keep (true by default) { run_id, kept }; an unknown run — run_not_found
process.runs.clear command optionally process_id { removed, bytes, failed } — the deleted runs, the room freed and the runs that could not be deleted, each with run_id, error_code and message; the marked ones and the one going stay
process.program.on command process_id { process_id, on: true } — the program is on (The program: graph and runs, section 2)
process.program.off command process_id { process_id, on: false, runs_stopped } — runs stopped, modules unloaded
process.program.list query — { programs }; a program — { process_id, on, runs_active }, one for each process of the instance
process.event.fire command process_id, node_id of an event with source: pult (without it — the only pulpit event), data — the data of an element of the pulpit; by_hand: true — a trial run from the canvas of an event of any source: without data the event gets what the source would give — the time for a clock, {} for the others (File formats, 16.6) { run_id, attempt_id, trace_id }, { queued: true, waiting } or { dropped: true } by the overlap rule (The program: graph and runs, section 5)
process.event.cancel command process_id, optionally node_id of an event { cancelled } — the stopped runs of this event, without node_id — all runs of the program
process.variable.list query process_id, variable_id — optional { process_id, values } — the current values of the variables of a program that is on, of one that is off — the initial and remembered ones; with variable_id — the value of this one variable: so the window reads a value that did not fit into an event
process.variable.set command process_id, variable_id, value { variable_id, value }; the value takes the place of the former; a value not of its type or an unknown variable — graph_invalid, a program that is off — program_off
process.variable.reset command process_id, variable_ids — a non-empty list { process_id, values } — each named variable got its initial value, as from a «Write» node with mode: reset, and the events of a change hear it as writing by the person; all names are checked before the first write: an unknown variable or a variable of a run — graph_invalid, and none changes; a program that is off — program_off
process.show.get query process_id; node_id — what one node showed { process_id, values, outputs } — what the «Show» nodes of a program that is on showed: { <node_id>: { value, cut, run_id, missing? } }; outputs — what each output of each node gave last, { "<node_id>/<output>": { value, cut, run_id, at } }, cut the same way; at — the order in which the outputs gave their values: of the links of one input the last to come is the one whose source has the larger at (with node_id — the outputs of this node); after the outputs of a step the window hears show.changed with outputs: true; cut — not everything is shown, missing — a field that was not in the packet; for a program that is off it is empty (File formats, 16.5)
process.file.put command process_id, name, media_type, upload_id, offset, last, data (base64), place = files | run and run_id a part of a file the person gives a process: { upload_id, received }, and for the last part — the reference { file, name, media_type, size }. The parts go in order, each from the byte the former one ended at; a refusal — file_invalid (File formats, 16.4)
process.file.take command process_id, path — a full path of Linux to the file, place = files | run and run_id { file, name, media_type, size } — the reference to a copy of the file lying at the path: the function copies it whole beside its place and then into it, under a name of its own; not a file, no file, larger than graph.file_max_bytes — a refusal; no room on the disk — the kernel's refusal (space_budget_exceeded, space_disk_full)
process.file.read query process_id, file — the field of a reference; for a file of a run run_id; offset, length { data, offset, size, last } — a piece of a file in base64, no more than 2 MiB at a time; a reference to a file that is not there — file_not_found
process.results.list query process_id, run_id { run_id, files } — the files among the results of a run by name, each as a reference { file, name, media_type, size }, read by process.file.read with the same run_id: what the modules of the run made and what was given to the run. media_type — by the ending of the name. A part of a file not yet given is not in the list; a run without results has an empty list
process.ask.list query process_id { process_id, asks } — the open questions of a program that is on: { ask_id, run_id, node_id, question, kind, choices, fill } — the same fields as in the event ask.opened (Exchange with the kernel, section 7)
process.ask.answer command run_id, optionally node_id, answer or declined: true { ok, ask_id }; the question is already closed — ask_closed (The program: graph and runs, section 9)
process.signal.send command process_id, the name of a signal or the node_id of a node that waits for it, optional key and data. With node_id the name is taken from the node, and without key the key is read from data by the path key of the node, as for the node «Send»; a node that is not signal — graph_invalid { delivered: true, wait_id, run_id, node_id } — the branch that has waited for this name with this key longest was woken; { delivered: false, reason: "nobody_waits" } — nobody waits, the signal is not kept (The program: graph and runs, section 22). The event signal.sent is published in both cases
process.message.send command process_id, the name of a message, optionally data { delivered: true, events } — the message began a run of each event of the program with source: "message" and this name, or stood in its queue, by the overlap rule of the event; { delivered: false, reason } — program_off, no_receiver or dropped (The program: graph and runs, section 23). The event message.sent is published in both cases
process.run.start command command_id, process_id, target=group\|node, target_id, events { run_id, attempt_id, trace_id } — a trial run of a part of the graph of a program that is on (The program: graph and runs, 12.1); a group is started with everything inside it, nested groups and the bodies of its frames included (17.1 there). events — what is typed in the fields of events: { <event id>: packet }, null — what the source would give. What an input of the part gets from outside and when the start is refused (graph_no_entry with message_key graph.part.input_cut or graph.part.event_unfit and names) — File formats, 16.7
process.run.status query run_id { run_id, status, active, steps } — both values at one moment: running happens only for an active run, and a run is active while its modules are being unloaded (module.release); an inactive run answers with its outcome. The status of a run whose end the function of the graph did not see — for example a run of an earlier start of the platform — is taken from its checkpoint; a run without a checkpoint — run_not_found. steps — the steps of modules that in an active run now wait their turn at the module or go: { node_id, execution_id, status: "queued"\|"running" }; for an inactive one — empty
process.run.stop command run_id; to stop one group — target=group and the target_id of the group, without run_id in all going runs the status; for a group — { status, run_id, target, target_id }. The group and the groups inside it stop getting steps, the run goes on with the other branches
process.run.retry command process_id, optionally node_id, confirm_non_idempotent; for a module node — run_id, input without node_id — the only pulpit event, with the node_id of an event — this event: the last failed run of the event repeats the step it failed on and goes on with the same run_id and a new attempt_id (The program: graph and runs, table 5). No failed run — run_not_found. A refused step is repeated when the person orders it; a step with an unknown outcome — a refusal of a module module_non_idempotent_unknown, or a step of a module non_idempotent or unknown broken off by a stop or ended without an answer of the module (module_process_lost, module_timeout) — is refused with the same code without confirm_non_idempotent. With the node_id of a module node — as process.node.step
process.run.resume command run_id, confirmation a new attempt_id
process.node.step command process_id, run_id, node_id one step
process.snapshot query run_id the checkpoint of the run

process.save checks the document the same way as process.validate and writes only process.json. A memory lives while a node names it: after writing, the function of the graph asks module.state.clear for a memory that no module node of the saved document names — the own memory of a deleted node or a shared one nobody names any more; a refusal of the clearing is told with the event function.work_failed and does not undo the saving (The program: graph and runs, section 13). While the program is on, saving is refused with process_running; other processes are saved as usual. The pulpit is written by canvas.save, the settings by settings.set / settings.ensure, the versions of modules by module.lock.sync.

process.node.candidates writes nothing and gives no recommendations: it lists only those pairs of ports that will pass the check of the graph — the types agree (The program: graph and runs, section 4), nothing enters an event, the border of a frame is not broken, there is no ring outside a frame, hot and cold are not mixed and there is no such link yet. occupied — this input already has a source, cold — the link will be cold (The program: graph and runs, section 6). An exact match of types comes before what json allowed. refused names for each output of the node the inputs of other nodes it cannot be brought to, and the cause — the last word of the key of its words graph.join.<cause> in the dictionary of the function of the graph: types — the types do not agree, itself — the same node, no_input — no links enter the node, drawn — such a link exists, frame — the border of a frame, ring — a ring outside a frame, hot_and_cold — hot and cold at one input.

process.insert writes nothing: it returns the document with the graph added, and the workshop saves it with an ordinary process.save. Without document the saved document of the process is taken, with it — the edit from the window. What happens on inserting:

process.group.move writes nothing either: the rows of what the group no longer holds are taken off the faces, the ports through which links now cross the borders are put on the faces; a group that came to lie across the border of a frame or into itself is refused with graph_invalid.

process.group.wrap writes nothing either:

process.group.detach is the only one of the queries about a group that writes: the new process is made the same way as at process.create, with its own pulpit, settings and lock of modules, and its graph passes the same check before writing as on saving; a refusal leaves nothing. The name of the process is name, without it — the name of the group, without a name — its identifier. The document of the caller is not written: it is returned to editing. The call node gets the identifier call_<n>, the inputs of the new process in_<n>, the outputs out_<n>. When the group stays in place, the refusal is graph_invalid with its own message_key and a list names of what it is about: graph.detach.own (events and results of this process in the group, names — nodes), graph.detach.variable (a variable that is worked with outside the group too or that stands on the pulpit — the pulpit is asked with the request canvas.get; names — variables), graph.detach.cold (cold links across the border, names — links), graph.detach.setting (nodes taking settings of the process) and graph.detach.face (there is no input or output on the face).

call in the answer of process.list is the face of a process as a node: inputs and outputs — the inputs and outputs, named and typed like ports in the manifest of a module ({ name, type }, the type is always json): an input for each event with source: "call" and an output for each node result, under their names, without a name — input and success; variables — the variables of the process, { id, type, initial }, to which a call gives values with the parameter values. For a process without an input call is null. process.node.candidates takes the ports of a call node from the same place, so the lists of links hold the named inputs and outputs of the called process.

process.variable.rename writes nothing: the renamed document is returned to editing. The name changes in the declaration of the variable, in the parameter variable of a node whose kind names a variable, in each parameter taken from the variable — an object of one field { "variable": "<name>" } at any depth, but not in the parameters themselves: the parameter of a module named variable is its own — in the lists variables of groups and in renamed_objects (File formats, section 5). A name that is not an identifier and the name of another variable — graph_invalid with message_key graph.variable.rename.name and graph.variable.rename.taken; its own name is neither a renaming nor a refusal. The calls in other processes are not edited: they are other documents.

process.group.unwrap, process.group.face and process.node.copy write nothing either: each returns the document to editing, and a document in which the groups do not pass the check after this is not returned.

process.group.unwrap removes a group and leaves its content: the nodes get the group_id of the group the taken-apart one lay in and go into its node_ids, and at the top level they lose the field; the groups that lay in the taken-apart one get its group_id_parent. The faces of the other groups do not change.

process.group.face puts on the face of a group a port of a node lying in it at any depth, or a declared variable, and takes them off it. Without group_id the request puts and takes off a mark ahead on a node or a group of the top level (face_marks of a node, marked of a row and face_marks of a group, File formats, section 5): a node marks its ports and the variables it works with, a group — the rows and variables of its face; what lies in a group is marked on its face, and the request refuses this (graph_invalid). A port shown again with name gets this name for its row; an empty name returns the name of the port to the row. A port through which a link crosses the border of the group cannot be taken off the face — graph_face_port_used: the link is removed first. A port of a node outside the group, a port the node does not have, and an undeclared variable — graph_invalid.

process.node.copy copies the named nodes and groups. A group is copied with everything that lies in it and with the groups inside; a frame — with its body and with the groups lying wholly in its body. The copy of a node gets the identifier of the original with the next free number after _ (paint → paint_2, module_1 → module_2); the copy of a group — the same, if its identifier ends with a number after _ (node_1 → node_2), otherwise with the next free number after - (cut → cut-2). Free is an identifier that neither a node nor a group has. The copy lies where the original does: the group_id and region of the copy name the group and frame of the original, or their copies, if they are copied too. Links both ends of which are copied are copied; if such a link crosses the border of a group not copied, the port of the copy goes on its face beside the port of the original. The face of the copy of a group shows the copies of what the face of the original showed, under the same names. The name of a shared memory (memory) the copy keeps, except a memory named by the identifier of a copied node: it is named by the identifier of its copy. The copy is shifted by 48 to the right and down.

process.node.candidates puts into behind_faces the links that only the face of a group hinders: the types agree and the other rules are kept, but the link would cross the border of a group through a port that is not on its face. group_id names this group — the one nearest to the end of the link. existing holds no such links: there is only what can be drawn now.

process.validate and process.save check also what shared objects come from (The program: graph and runs, section 16): a variable has an identifier, a type from the set and an initial value of its type, the ids do not repeat; the nodes variable, write and the parameters { "variable": … } refer to declared variables; an event has source and overlap from their sets; a frame has a body and returns; hot and cold inputs are not mixed (The program: graph and runs, section 6); groups are a tree, each lies in one place, its face shows what exists, and links cross its border only through the face (17.1 there). A breach is graph_invalid with the name of the node, variable or group.

process.snapshot reads runs/{run_id}/checkpoint.json — this is a snapshot of the run; the process itself is read by process.open.

After each run the function of the graph clears the old runs of this process by graph.runs_keep (The program: graph and runs, 12.6). An error of the clearing does not fail the run.

process.run.start answers at once { "run_id", "attempt_id", "trace_id" }; the carrying out goes on. run_id is the local date and time of the start of the run, 2026-10-08_07-29-12, and two runs never have the same one; a run lies in the folder of its process (The program: graph and runs, 12.6). The event run.changed — { run_id, trace_id, process_id, status } and event_node, if an event began the run — comes first with the status running, then with the outcome; the outcome failed also carries the cause: error_code, message and, if the run stopped on a node, node_id; the outcome cancelled — stopped_by (The program: graph and runs, 12.5). process.run.status reads the same outcome: running, completed, failed, cancelled; for a run without a checkpoint — run_not_found. Switching a program on is refused if modules.lock.json does not match the installed versions (module_version_mismatch) or the graph does not pass the check (graph_invalid). A run begins only in a program that is on.

process.run.resume is refused with run_active if the run is going now: the checkpoint of a going run already allows resuming — it is written in case of a failure — but a run begun from it beside itself would carry out steps twice. process.run.resume is refused with process_changed if the process was saved after the checkpoint: a run taken from one version of the graph does not go on in another; a new run remains. With an undefined result of a step of a module non_idempotent or unknown, process.run.resume answers module_non_idempotent_unknown until the parameters hold an explicit confirmation { "confirm_non_idempotent": true }. In a run interrupted the steps of modules from the queued of the checkpoint count as undefined; their semantics is the field execution_semantics from module.list. The confirmation does not open resuming for completed, for a step the module refused without a retry (refused), and for other causes of a ban — there the answer is resume_not_allowed; a refused step is repeated by process.run.retry.