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. Seevalidate(). - derived authority: a sub-request to
urn:plan:requires, which reads each step’s target contract (Description::required_scopesfor the step’s verb) without invoking it. The union is stored with the version; arequires=that says anything else is refused, naming the derived set. Seederive(). - read or write, from the steps: a plan whose every step is a
Source,ExistsorMetaruns as a READ at…:result; one with anySinkorDeletestep anywhere in the graph is a WRITE, run at…:runs. SeeAnalysis::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:inputArgSpec 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:
inandasareurn:plan:eval‘s own,nameis the script’s binding,contentis…: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
InvalidArgumentoncontent. - 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:requiresfrom its steps’ contracts (read withMeta, so nothing is invoked). - eval_
request - The request a run issues:
urn:plan:evalwith the plan asin, each given parameter by its own name, andaswhen 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
keepstill carries afterexpand_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 withInvalidArgumentoncontent, naming every shape (or executor check) it broke, the node, and the shape’s message.