Skip to main content

Module plan

Module plan 

Source
Expand description

Plans as a script language: a stored ik:Process graph is a script whose language is plan.

A plan is the most analyzable script there is: finite (no conditionals, no loops; a branch is a step that calls a language), so it is validated before it is stored, every step’s target is known in advance, and the capability it needs is DERIVED from its steps’ own contracts rather than declared by its author. It gets everything a Lisp or SPARQL script gets (versions, the three authorities, run records, the manifold) and three things of its own, none of which is ever taken from a caller’s argument:

  • validation at publish: the plan is a sub-request to urn:plan:validate (the SHACL shapes of the process vocabulary plus the executor’s own checks), and one that does not conform is refused, naming the shape it broke. See validate().
  • derived authority: a sub-request to urn:plan:requires, which reads each step’s target contract (Description::required_scopes for the step’s verb) without invoking it. The union is stored with the version; a requires= that says anything else is refused, naming the derived set. See derive().
  • read or write, from the steps: a plan whose every step is a Source, Exists or Meta runs as a READ at …:result; one with any Sink or Delete step anywhere in the graph is a WRITE, run at …:runs. See Analysis::mutates.

A run is a sub-request to urn:plan:eval with the plan as in and the plan’s own parameters (ik:input) by name, issued under the runner’s capability narrowed exactly as every script’s is (see crate::authority). The three names are the HOST’s: this crate links no plan runner, as it links no Lisp interpreter and no SPARQL evaluator.

§The shallow read

analyze reads a plan with a Turtle parser for exactly three things this crate must decide itself and cannot ask a resource for without running something: the process node, the verb of every step (read or write), and the declared parameters (the arguments of the script’s own catalog entry). Everything about whether the plan is well formed is urn:plan:validate’s, and publish asks it first.

use ikigai_script::plan::analyze;

let plan = r#"
@prefix ik: <https://ikigai-rs.dev/ns#> .
<urn:plan:hello> a ik:Process ;
    ik:input <urn:plan:hello:input:who> ;
    ik:step <urn:plan:hello:step:1> ;
    ik:result <urn:plan:hello:step:1> .
<urn:plan:hello:input:who> ik:inputName "who" ; ik:required false ; ik:default "world" .
<urn:plan:hello:step:1> a ik:Step ; ik:verb "Source" ; ik:resolves <urn:example:greet> .
"#;
let analysis = analyze(plan).unwrap();
assert_eq!(analysis.process, "urn:plan:hello");
assert!(!analysis.mutates()); // every step a Source: a read, at `…:result`
assert_eq!(analysis.parameters[0].name, "who");
assert_eq!(analysis.parameters[0].default.as_deref(), Some("world"));

§Preference order

When assembling anything: a query, then a plan, then Lisp. A query and a plan both derive their authority and can be checked before they run; Lisp declares its authority and can do anything its capability allows in any order. Use Lisp for control flow a plan cannot express, and preferably as ONE step inside a plan.

Structs§

Analysis
What a plan says about itself, read from its graph.
Parameter
One of a plan’s declared parameters (an ik:input ArgSpec node).
Step
One step, as far as this crate reads it.

Constants§

EVAL
The host’s plan evaluator: runs a plan’s steps as sub-requests under the caller’s capability.
MAX_PLAN_BYTES
The longest plan accepted, in bytes. A bound on the work a publish does (two sub-requests and a parse over the text), refused rather than truncated. The Turtle parser keeps an explicit stack, so nesting is not a claim on the thread’s stack.
REQUIRES
The host’s derivation of a plan’s capability from its steps’ contracts.
RESERVED_PARAMETERS
Parameter names the doors already use: in and as are urn:plan:eval‘s own, name is the script’s binding, content is …:runs’ piped body, and the rest are the transports’ stamps (crate::TRANSPORT_ARGUMENTS), which a run ignores. A plan declaring one could never be given it.
VALIDATE
The host’s plan validator: the SHACL report plus the executor’s checks.

Functions§

analyze
Read a plan for what this crate decides itself: its process node, the verb of every step and its declared parameters. Every refusal is an InvalidArgument on content.
check_bound
Refuse a plan over MAX_PLAN_BYTES (never truncate it), before anything is parsed or sent anywhere.
derive
A plan’s capability, DERIVED by the host’s urn:plan:requires from its steps’ contracts (read with Meta, so nothing is invoked).
eval_request
The request a run issues: urn:plan:eval with the plan as in, each given parameter by its own name, and as when the caller asked for a face.
expand_families
The scopes a plan run is issued under, with every derived FAMILY (prefix*) turned into exact grants.
markers
The family markers a run’s keep still carries after expand_families: what it holds only as “some grant under this prefix”, never a member. Empty for a run every step of which can name its grants exactly. For a SCOPED runner every family left is one the runner holds itself (a namespace grant), so a host explaining a refusal asks this of a root run.
validate
Validate a plan through the host’s urn:plan:validate, under the publisher’s capability. A plan that does not conform is refused with InvalidArgument on content, naming every shape (or executor check) it broke, the node, and the shape’s message.