@githonllc/avouch 0.1.3 → 0.1.5

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -34,6 +34,7 @@ Read in this order:
34
34
  |---|---|
35
35
  | `SPEC.md` | concepts (foundation / extension), fields, rules R1–R12, fact contract |
36
36
  | `ontology.schema.json` | JSON Schema: `$id` and `formatVersion`; shape only, no project values |
37
+ | `vocab.ttl` | RDF vocabulary (`ofv:`) for exporting an ontology as RDF; one term per schema field (`test/vocab.test.ts`) |
37
38
  | `src/contract.ts` | `SourceFacts`: the facts with their sources, and diagnostics |
38
39
  | `src/checker.ts` | `check(ontology, facts)` returns the violations |
39
40
  | `src/patch.ts` | JSON-pointer patch that the mutation tests use |
@@ -130,7 +131,7 @@ Inside a Claude Code session, the same two steps are `/plugin marketplace add gi
130
131
 
131
132
  The plugin has two skills:
132
133
  - `cli` (`/avouch:cli`): checks an ontology and runs `avouch query`.
133
- - `scan` (`/avouch:scan [project-dir]`): drafts or updates a project's ontology from its design documents. The result is a candidate. `avouch check` must exit 0, and each source gap is listed with a proposed fix to the document.
134
+ - `scan` (`/avouch:scan [project-dir]`): drafts or updates a project's ontology from its design documents. The result is a candidate. `avouch check` must exit 0, and each source gap is listed with a proposed fix to the document. When most objects have no documented field list, it also writes `avouch/doc-patch.md`: field lists, foreign keys and store names derived from the code, for a human to review and merge into the documents. It never drafts states, permissions or rules from code.
134
135
 
135
136
  Before 0.1.3 the two skills were named `avouch-cli` and `aquery-scan`.
136
137
 
@@ -159,6 +160,8 @@ The secret is generated from a list kept in a private repository; maintainers ne
159
160
 
160
161
  The ontology file has `formatVersion: 1`. The schema `$id` is `https://w3id.org/avouch/v1/schema.json`. The version rules are in SPEC section 8.
161
162
 
163
+ The RDF vocabulary is `vocab.ttl`, namespace `https://w3id.org/avouch/v1/vocab#` (prefix `ofv:`), served at `https://avouch.dev/v1/vocab.ttl`. An RDF export of an ontology (for example the Turtle export of Avouch Studio) names each field with the term of the same name. The check does not read the vocabulary. A change that adds a schema field adds its term in the same change: `test/vocab.test.ts` fails on a field without a term, except for the top-level sections, expressions (exported as one JSON literal), the `properties` of an object type (OWL properties) and the `ref` of a value type (the class itself); each exclusion has its reason in the test.
164
+
162
165
  ## License
163
166
 
164
167
  Copyright 2026 Githon LLC. Licensed under the Apache License 2.0. See `LICENSE` and `NOTICE`. The next section gives the exception.
package/SPEC.md CHANGED
@@ -49,7 +49,7 @@ The definitions quoted in the "Palantir analog" column are short quotations from
49
49
  | `actionTypes.<A>.parameters` | foundation | action parameter: "Parameters are the inputs of an action type." | name → `{cite, type?, optional?, values?}`; read by a condition as `{param}` or, in an expression, `{ref}`; `type` is needed when an expression reads it; `optional: true` lets the caller omit it; `values` (only with `type: enum`) is the value domain of the parameter; R1, R2 |
50
50
  | `conditions` | foundation + constraint | submission criteria: "the conditions that determine whether an action can be submitted"; Palantir submission criteria ≈ the rows whose disposition class is `rejected` | named Boolean claims `{id, expr \| unspecified, reads?, reads_unspecified?, cites[{cite}], scenarios?}`: `expr` is the claim as an expression (section 9); `unspecified` says it is not written as one, and `reads` may then list what it reads (properties, parameters `{param, values?}`, the caller `{actor: id}` / `{actor: keys}`); R1, R4, R12 |
51
51
  | `decision` | extension | none | decision table `{hitPolicy: first, order?: {cite}, rows: [{when: {passes \| fails: <condition>}, result, cites, next?}], otherwise?: {result, cites, next?}}`; `next` is `{actions}`, `{external, cite}` or `{unspecified}`; R1, R12 |
52
- | `edits` / `creates` | foundation | action rules: modify / create object | |
52
+ | `edits` / `creates` / `deletes` | foundation | action rules: modify / create / delete object | `deletes` is a hard delete of datasource rows; a soft delete is an `edits` |
53
53
  | `link_effects` | foundation + constraint | action rules: create / delete link | every incident link needs an entry, including `none` / `unspecified` (R6 sweep) |
54
54
  | `emits` | extension | none (Palantir side effects are notifications / webhooks after submission) | events written in the same operation; R7, R8 |
55
55
  | `permission` | extension | roles / submission criteria on the current user are approximate. Forms: `unknown`, `{keys, cite, …}`, `{none, cite}` (optionally with `principals`), `{any_of: [keys-form, …]}` (OR of keys-forms, at least 2) | R1, R10 |
@@ -110,11 +110,11 @@ The adapter may read the ontology instance (for example to list action ids in a
110
110
  | R4 | conditions read only canonical and policy properties, for an expression also through the derived properties it reaches (parameter and actor reads are request inputs, not state, and are not classified; a derived property without `expr` is not expanded; a hand-written `reads` entry that names a derived property is expanded through its `reads`, or, when it has an `expr`, through the properties that expression reads, as for an expression condition); a non-canonical property passes when every read compares it by `eq` / `neq` with a literal in its `canonical_values`; canonical values and the values of a parameter read appear in their quote (`parameter_value_unsupported`); each literal of an expression appears in one of its condition's quotes, or in the derived property's quote (`literal_unsupported`, key `<ID>:<condition id>:<value>` or `derivedProperties.<id>:<value>`) | none | the property reads of conditions are canonical or policy facts; literals are not invented | that `reads` is complete; that an expression's logic matches its cite |
111
111
  | R5 | object membership, cross-context declaration (`via` or `unspecified`), `create_rule`, entity coverage | `contextMembers`, `config.crossContextMechanisms` | object ownership matches the source | the content of the mechanism |
112
112
  | R6 | link sweep, link catalog complete (with `scope`, the `<by>` field of a tenant type counts as declared and must not be in `non_link_fields`: `non_link_field_stray`), `none_but_written` | `fieldList`, `config.linkFieldPattern`, `evidence` | link effects are declared completely | that an effect text is right |
113
- | R7 | observed writes are declared; events are in `emits`; changed columns are declared (`column_undeclared`, key `<ID>:<Object>.<prop>`); declared effects are witnessed (`effect_unwitnessed`); with `config.evidenceRequired: true` (default `false`) an action without `evidence` fails `evidence_missing` (key `<ID>`) | `evidence`, `config.evidenceRequired` | every store changed by a committed invocation is in the touched objects' `datasource`, a pure link table with an `effect`, or `infrastructureStores`; every observed event is in `emits`; every column changed on an existing row of a touched object's `datasource` is, for each touched object of that store, a property of a resolvable `edits` entry of that same object (objects may share a store; a column that one object declares is still reported for each other touched object of the store that does not declare it); every resolvable `edits` entry (resolved as R1 resolves it; an unresolvable entry is R1 only) was changed on an existing row (key `<ID>:<Object>.<prop>`), every `creates` object had a row inserted (key `<ID>:creates:<Object>`) and every pure link table with an `effect` had a row inserted or deleted (key `<ID>:link:<link id>`) by at least one committed invocation | actions without evidence (unless `evidenceRequired`); that a not yet witnessed effect is unreachable; atomicity, which refers to the committed effect (one invocation may have several attempts) and which the project profile or evidence adapter must enforce |
113
+ | R7 | observed writes are declared; events are in `emits`; changed columns are declared (`column_undeclared`, key `<ID>:<Object>.<prop>`); declared effects are witnessed (`effect_unwitnessed`); with `config.evidenceRequired: true` (default `false`) an action without `evidence` fails `evidence_missing` (key `<ID>`) | `evidence`, `config.evidenceRequired` | every store changed by a committed invocation is in the touched objects' `datasource`, a pure link table with an `effect`, or `infrastructureStores`; every observed event is in `emits`; every column changed on an existing row of a touched object's `datasource` is, for each touched object of that store, a property of a resolvable `edits` entry of that same object (objects may share a store; a column that one object declares is still reported for each other touched object of the store that does not declare it); every resolvable `edits` entry (resolved as R1 resolves it; an unresolvable entry is R1 only) was changed on an existing row (key `<ID>:<Object>.<prop>`), every `creates` object had a row inserted (key `<ID>:creates:<Object>`), every `deletes` object had a row deleted (key `<ID>:deletes:<Object>`) and every pure link table with an `effect` had a row inserted or deleted (key `<ID>:link:<link id>`) by at least one committed invocation | actions without evidence (unless `evidenceRequired`); that a not yet witnessed effect is unreachable; atomicity, which refers to the committed effect (one invocation may have several attempts) and which the project profile or evidence adapter must enforce |
114
114
  | R8 | emitted events are in the catalog | `eventCatalog` | | |
115
115
  | R9 | scenario cross references | `scenarios` | every action is cited; with `config.scenarioCoverage: "enforced"` (default `"printed"`, coverage only printed) every condition without `scenarios` fails `criterion_uncovered` (key `<ID>:<condition id>`) and every non-`none`, non-`unspecified` link effect without `scenarios` fails `effect_uncovered` (key `<ID>:<link id>`); `unspecified` effects are not reported again, R6 reports them | with `printed`, that scenarios cover every condition; that a listed scenario supports the claim |
116
116
  | R10 | permission key sets equal the source rows (for `any_of`: the union over alternatives of keys ∪ conditional_keys; each alternative's cite must also name its own keys); cites name the required source | `permissions`, `config.requiredSource.R10` | keys match the source | |
117
- | R11 | an action that edits an input lists the materialized property | none | declaration is complete | same-operation atomicity of the committed effect (one invocation may have several attempts), which the evidence adapter guarantees |
117
+ | R11 | an action that edits an input, creates an input's object, or deletes an input's object (unless it also deletes the materialized property's own object, whose row is gone) lists the materialized property. An action that deletes the property's own object and also edits or creates an input is still reported, because the edited inputs may belong to other rows; waive it with a reason when they cannot. | none | declaration is complete | same-operation atomicity of the committed effect (one invocation may have several attempts), which the evidence adapter guarantees |
118
118
  | R12 | decision tables, all waivable: an action with conditions has a `decision` (`decision_table_missing`, key `<ID>`); a table has `otherwise` (`otherwise_missing`); a row does not use a condition without expression, or one that reaches a derived property without expression (`condition_unspecified`, key `<ID>:<condition id>`) (implementation decision: like the R4 and literal checks, the derived-property part is computed only for conditions whose expression passes the R1 type check); rows with different results have `order` (`row_order_uncited`); for rows `i < j` with different results whose position cites are in one section, the position of `i` comes first (`row_order_mismatch`, key `<ID>:rows.<i>:rows.<j>`, once per pair; implementation decision, since facts carry no order across sections: a row's position cite is in the section of the first listed cite whose quote names its result, and its position is the earliest such quote in that section). A row whose position-cite section is `missing` or `ambiguous` has no position and is not compared. Rows whose position cites are in different sections are not compared, even if they also cite a shared section; ordering across sections relies on `decision.order` and scenarios with overlapping inputs; each row names its result in a cite quote (`result_unsupported`, key `<ID>:rows.<i>` or `<ID>:otherwise`); `next: {unspecified}` is reported (`next_unspecified`); a row `passes(c)` where c may be UNKNOWN, no earlier row is `fails(c)`, and a later row up to the first later `fails(c)` row (inclusive), or `otherwise` when there is no such row, has class `applied` or `pending` (`unsafe_fallthrough`, key `<ID>:rows.<i>`); the body of an `exists` / `none` / `all` may be UNKNOWN (`quantifier_absorbs_unknown`, key `<ID>:<condition id>:<path>` or `derivedProperties.<id>:<path>`, path as in type errors, for example `$.and[1].none`); both see section 9, Static UNKNOWN analysis | `section`, `fieldList` | every row has a source for its result and, within one section, for its place | that rows do not overlap; that the order cite covers the whole table |
119
119
 
120
120
  ## 6. Rule switches
@@ -146,7 +146,7 @@ The table lists only the optional facts. Every rule may also read the required f
146
146
 
147
147
  ## 8. Versions
148
148
 
149
- The version is in two places: `v1` in the schema `$id`, and `formatVersion: 1` in the ontology file (R1 fails on any other value). A change that breaks existing ontologies raises both. A change that rejects only files without meaning (for example an empty name) is not a break. Adding the `permission.any_of` form is additive: existing files stay valid, so `formatVersion` stays 1. Adding `parameters` and the parameter and actor reads of the submission criteria (since renamed `conditions`) is additive: existing files stay valid, so `formatVersion` stays 1. Adding `values` on enum parameters is additive; `formatVersion` stays 1.
149
+ The version is in two places: `v1` in the schema `$id`, and `formatVersion: 1` in the ontology file (R1 fails on any other value). A change that breaks existing ontologies raises both. A change that rejects only files without meaning (for example an empty name) is not a break. Adding the `permission.any_of` form is additive: existing files stay valid, so `formatVersion` stays 1. Adding `parameters` and the parameter and actor reads of the submission criteria (since renamed `conditions`) is additive: existing files stay valid, so `formatVersion` stays 1. Adding `values` on enum parameters is additive; `formatVersion` stays 1. Adding `deletes` is additive; `formatVersion` stays 1.
150
150
  During 0.x the v1 draft changes in place (`formatVersion` 1, `$id` v1); the rule above applies from the 1.0 semantic freeze.
151
151
 
152
152
  ## 9. Decision semantics
@@ -261,6 +261,8 @@ export function check(ontology, facts) {
261
261
  v("R1", p, `${f} must be a list`);
262
262
  if (c.creates !== undefined && !Array.isArray(c.creates))
263
263
  v("R1", p, "creates must be a list");
264
+ if (c.deletes !== undefined && !Array.isArray(c.deletes))
265
+ v("R1", p, "deletes must be a list");
264
266
  if (!isObj(c.link_effects))
265
267
  v("R1", p, "link_effects missing (use {})");
266
268
  if (c.crossContext !== undefined) {
@@ -322,6 +324,9 @@ export function check(ontology, facts) {
322
324
  for (const o of arr(c.creates))
323
325
  if (!objType(o) && !outOfScope.has(o))
324
326
  v("R1", p, `creates ${o}: unknown object`);
327
+ for (const o of arr(c.deletes))
328
+ if (!objType(o) && !outOfScope.has(o))
329
+ v("R1", p, `deletes ${o}: unknown object`);
325
330
  for (const e of Object.keys(isObj(c.edit_cites) ? c.edit_cites : {}))
326
331
  if (!arr(c.edits).includes(e) || !isObj(c.edit_cites[e]?.cite))
327
332
  v("R1", `${p}.edit_cites.${e}`, "must name an edit and carry a cite");
@@ -633,7 +638,7 @@ export function check(ontology, facts) {
633
638
  if (!entities.has(x))
634
639
  v("R5", `entity:${x}`, `outOfScopeObjectTypes.${x} is not a source entity`, "out_of_scope_unknown");
635
640
  }
636
- const touched = (c) => new Set([...arr(c.edits).map((e) => e.split(".")[0]), ...arr(c.creates)].filter(objType));
641
+ const touched = (c) => new Set([...arr(c.edits).map((e) => e.split(".")[0]), ...arr(c.creates), ...arr(c.deletes)].filter(objType));
637
642
  for (const [cid, c] of Object.entries(commands)) {
638
643
  if (!isObj(c))
639
644
  continue;
@@ -713,7 +718,7 @@ export function check(ontology, facts) {
713
718
  // columns changed on existing rows (per store; checked per object), and the events written
714
719
  const observe = (id) => {
715
720
  const committed = part(id).committed;
716
- const rowStores = new Set(), insertedStores = new Set(), insDelStores = new Set(), events = new Set();
721
+ const rowStores = new Set(), insertedStores = new Set(), insDelStores = new Set(), deletedStores = new Set(), events = new Set();
717
722
  const changedCols = new Map();
718
723
  for (const i of committed) {
719
724
  for (const e of i.delta.events)
@@ -723,6 +728,8 @@ export function check(ontology, facts) {
723
728
  rowStores.add(tb);
724
729
  if (d.inserted.length)
725
730
  insertedStores.add(tb);
731
+ if (d.deleted.length)
732
+ deletedStores.add(tb);
726
733
  if (d.inserted.length || d.deleted.length)
727
734
  insDelStores.add(tb);
728
735
  for (const u of d.updated)
@@ -730,7 +737,7 @@ export function check(ontology, facts) {
730
737
  changedCols.set(tb, (changedCols.get(tb) ?? new Set()).add(col));
731
738
  }
732
739
  }
733
- return { committed, rowStores, insertedStores, insDelStores, changedCols, events };
740
+ return { committed, rowStores, insertedStores, insDelStores, deletedStores, changedCols, events };
734
741
  };
735
742
  for (const [cid, c] of Object.entries(commands)) {
736
743
  if (!isObj(c))
@@ -783,14 +790,14 @@ export function check(ontology, facts) {
783
790
  v("R7", cid, "the profile requires evidence, but the action declares none", "evidence_missing");
784
791
  if (facts.evidence !== undefined) {
785
792
  // a pure link table (not any object's table) may be written when the action declares an `effect` on that link;
786
- // an object's table still needs the object in edits / creates
793
+ // an object's table still needs the object in edits / creates / deletes
787
794
  const objTables = new Set(Object.values(objects).map((o) => o?.datasource));
788
795
  for (const [cid, c] of Object.entries(commands)) {
789
796
  if (!isObj(c) || c.evidence === undefined)
790
797
  continue;
791
798
  claimed.add(c.evidence);
792
799
  const { rejected } = part(c.evidence);
793
- const { committed, rowStores, insertedStores, insDelStores, changedCols, events } = observe(c.evidence);
800
+ const { committed, rowStores, insertedStores, insDelStores, deletedStores, changedCols, events } = observe(c.evidence);
794
801
  const attempts = [...committed, ...rejected].reduce((n, i) => n + i.rolledBackAttempts, 0); // malformed records add nothing
795
802
  info.push(`R7 ${cid} (${c.evidence}, ${committed.length} committed, ${rejected.length} rejected invocations, ${attempts} rolled-back attempts): changed {${[...rowStores].sort()}} events {${[...events].sort()}}`);
796
803
  if (!committed.length) {
@@ -804,7 +811,7 @@ export function check(ontology, facts) {
804
811
  const allowed = new Set([...declared, ...linkTables, ...infra]);
805
812
  for (const tb of rowStores)
806
813
  if (!allowed.has(tb))
807
- v("R7", `${cid}:${tb}`, `evidence (${c.evidence}) changed store ${tb}, not declared by edits/creates`, "table_undeclared");
814
+ v("R7", `${cid}:${tb}`, `evidence (${c.evidence}) changed store ${tb}, not declared by edits/creates/deletes`, "table_undeclared");
808
815
  for (const e of events)
809
816
  if (!arr(c.emits).includes(e))
810
817
  v("R7", `${cid}:event:${e}`, `evidence (${c.evidence}) wrote event ${e}, not in emits`, "event_undeclared");
@@ -831,6 +838,9 @@ export function check(ontology, facts) {
831
838
  for (const o of arr(c.creates))
832
839
  if (objType(o) && !insertedStores.has(objects[o].datasource))
833
840
  unwitnessed(`creates:${o}`, `inserted a row into ${objects[o].datasource}`);
841
+ for (const o of arr(c.deletes))
842
+ if (objType(o) && !deletedStores.has(objects[o].datasource))
843
+ unwitnessed(`deletes:${o}`, `deleted a row of ${objects[o].datasource}`);
834
844
  for (const l of links.filter(pureLink))
835
845
  if (!insDelStores.has(l.table))
836
846
  unwitnessed(`link:${l.id}`, `inserted or deleted a row of the link store ${l.table}`);
@@ -967,7 +977,9 @@ export function check(ontology, facts) {
967
977
  for (const [cid, c] of Object.entries(commands)) {
968
978
  if (!isObj(c))
969
979
  continue;
970
- const hit = reads.filter((r) => arr(c.edits).includes(r) || arr(c.creates).includes(r.split(".")[0]));
980
+ // deleting an input object writes it, unless the target's own object is deleted too (the target row is gone)
981
+ const hit = reads.filter((r) => arr(c.edits).includes(r) || arr(c.creates).includes(r.split(".")[0])
982
+ || (arr(c.deletes).includes(r.split(".")[0]) && !arr(c.deletes).includes(name)));
971
983
  if (hit.length && !arr(c.edits).includes(target))
972
984
  v("R11", `${cid}:${target}`, `${cid} writes ${hit.join(", ")} (inputs of ${target}) but does not list ${target} in edits`, "materialization_missing");
973
985
  }
@@ -162,6 +162,7 @@ export type QueryAction = {
162
162
  cite: QueryCite | null;
163
163
  }[];
164
164
  creates: string[];
165
+ deletes: string[];
165
166
  emits: string[];
166
167
  linkEffects: {
167
168
  link: string;
@@ -232,6 +233,7 @@ export type QueryObject = {
232
233
  action: string;
233
234
  edits: string[];
234
235
  creates: boolean;
236
+ deletes: boolean;
235
237
  }[];
236
238
  readers: {
237
239
  action: string;
@@ -261,6 +263,7 @@ export type QueryWrites = {
261
263
  path: string[];
262
264
  }[];
263
265
  creators: string[];
266
+ deleters: string[];
264
267
  };
265
268
  export type QueryReads = {
266
269
  target: string;
package/dist/src/query.js CHANGED
@@ -344,6 +344,7 @@ export function queryAction(ont, id) {
344
344
  decision: isObj(a.decision) ? { order: citeOf(map(a.decision.order).cite), rows: decisionRows(an, id, a) } : null,
345
345
  edits: strs(a.edits).map((prop) => ({ prop, cite: citeOf(map(own(editCites, prop)).cite) })),
346
346
  creates: strs(a.creates),
347
+ deletes: strs(a.deletes),
347
348
  emits: strs(a.emits),
348
349
  linkEffects: Object.entries(map(a.link_effects)).flatMap(([link, le]) => {
349
350
  const e = map(le);
@@ -396,8 +397,9 @@ export function queryObject(ont, id) {
396
397
  for (const [aid, a] of sortedActions(ont)) {
397
398
  const edits = strs(a.edits).filter((e) => e.startsWith(prefix));
398
399
  const creates = strs(a.creates).includes(id);
399
- if (edits.length || creates)
400
- writers.push({ action: aid, edits, creates });
400
+ const deletes = strs(a.deletes).includes(id);
401
+ if (edits.length || creates || deletes)
402
+ writers.push({ action: aid, edits, creates, deletes });
401
403
  }
402
404
  if (o === undefined)
403
405
  return { id, context: null, datasource: null, doc: null, outOfScope, scope: null, properties: [], derived: [], stateMachine: null, links: [], writers, readers: [], unanalyzed: [] };
@@ -494,7 +496,8 @@ export function queryWrites(ont, t) {
494
496
  const acts = sortedActions(ont);
495
497
  const writersOf = (prop, path) => acts.filter(([, a]) => strs(a.edits).includes(prop)).map(([aid, a]) => ({ action: aid, cite: citeOf(map(own(map(a.edit_cites), prop)).cite), path }));
496
498
  if (tg.kind === "property")
497
- return { target: t, kind: "property", unanalyzed: [], writers: writersOf(t, []), creators: acts.filter(([, a]) => strs(a.creates).includes(tg.obj)).map(([aid]) => aid) };
499
+ return { target: t, kind: "property", unanalyzed: [], writers: writersOf(t, []), creators: acts.filter(([, a]) => strs(a.creates).includes(tg.obj)).map(([aid]) => aid),
500
+ deleters: acts.filter(([, a]) => strs(a.deletes).includes(tg.obj)).map(([aid]) => aid) };
498
501
  // Breadth first from the derived target: each input once, by its shortest path.
499
502
  const an = new Analysis(ont);
500
503
  const writers = [];
@@ -518,7 +521,7 @@ export function queryWrites(ont, t) {
518
521
  frontier = next;
519
522
  }
520
523
  writers.sort((a, b) => a.path.length - b.path.length || sortStr(a.path.join(), b.path.join()) || sortStr(a.action, b.action));
521
- return { target: t, kind: "derived", unanalyzed: derivedErrors(an, seenDerived), writers, creators: [] };
524
+ return { target: t, kind: "derived", unanalyzed: derivedErrors(an, seenDerived), writers, creators: [], deleters: [] };
522
525
  }
523
526
  export function queryReads(ont, t) {
524
527
  const tg = target(ont, t);
@@ -689,6 +689,13 @@
689
689
  },
690
690
  "description": "Objects the action creates (optional). Foundation (closest analog: the create-object rules of a Palantir action type). R1: each must be a key of `objectTypes` or of `outOfScopeObjectTypes`. Declared objects here are touched (R5, R6, R7) and count as writing every property of that object for R11. R5: creating the `from` object of a link with `create_rule` triggers that rule."
691
691
  },
692
+ "deletes": {
693
+ "type": "array",
694
+ "items": {
695
+ "type": "string"
696
+ },
697
+ "description": "Objects whose rows the action removes (optional). Foundation (closest analog: the delete-object rules of a Palantir action type). R1: each must be a key of `objectTypes` or of `outOfScopeObjectTypes`. A soft delete is an `edits` of the marker property. Declared objects here are touched (R5, R6, R7). R7: for an action with `evidence`, each declared object here must have a row of its datasource deleted by at least one committed invocation (else `effect_unwitnessed`, key `<ID>:deletes:<Object>`); an upsert declares `creates`, not `deletes`. R11: deleting an input of a `materializedFrom` property counts as writing it, unless the object of that property is also listed here."
698
+ },
692
699
  "edit_cites": {
693
700
  "type": "object",
694
701
  "propertyNames": {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@githonllc/avouch",
3
- "version": "0.1.3",
3
+ "version": "0.1.5",
4
4
  "license": "Apache-2.0",
5
5
  "repository": {
6
6
  "type": "git",
@@ -30,6 +30,7 @@
30
30
  "dist/src/",
31
31
  "dist/cli/",
32
32
  "ontology.schema.json",
33
+ "vocab.ttl",
33
34
  "SPEC.md",
34
35
  "docs/",
35
36
  "NOTICE"
package/vocab.ttl ADDED
@@ -0,0 +1,499 @@
1
+ @prefix rdf: <http://www.w3.org/1999/02/22-rdf-syntax-ns#> .
2
+ @prefix rdfs: <http://www.w3.org/2000/01/rdf-schema#> .
3
+ @prefix owl: <http://www.w3.org/2002/07/owl#> .
4
+ @prefix ofv: <https://w3id.org/avouch/v1/vocab#> .
5
+
6
+ <https://w3id.org/avouch/v1/vocab> a owl:Ontology ;
7
+ rdfs:label "Avouch vocabulary"@en ;
8
+ rdfs:seeAlso <https://w3id.org/avouch/v1/schema.json> .
9
+
10
+ ofv:Context a rdfs:Class ;
11
+ rdfs:label "Context" ;
12
+ rdfs:comment "Exported from the YAML path contexts.<name>." ;
13
+ rdfs:isDefinedBy <https://w3id.org/avouch/v1/vocab> .
14
+
15
+ ofv:OutOfScopeObjectType a rdfs:Class ;
16
+ rdfs:label "OutOfScopeObjectType" ;
17
+ rdfs:comment "Exported from the YAML path outOfScopeObjectTypes.<name>." ;
18
+ rdfs:isDefinedBy <https://w3id.org/avouch/v1/vocab> .
19
+
20
+ ofv:DerivedProperty a rdfs:Class ;
21
+ rdfs:label "DerivedProperty" ;
22
+ rdfs:comment "Exported from the YAML path derivedProperties.<id>." ;
23
+ rdfs:isDefinedBy <https://w3id.org/avouch/v1/vocab> .
24
+
25
+ ofv:ActionType a rdfs:Class ;
26
+ rdfs:label "ActionType" ;
27
+ rdfs:comment "Exported from the YAML path actionTypes.<ID>." ;
28
+ rdfs:isDefinedBy <https://w3id.org/avouch/v1/vocab> .
29
+
30
+ ofv:index a rdf:Property ;
31
+ rdfs:label "index" ;
32
+ rdfs:comment "Position (from 0) of a record in its YAML list. YAML: conditions[], decision.rows[], transitions[], knownSourceGaps[], permission_rows_not_commands[]" ;
33
+ rdfs:isDefinedBy <https://w3id.org/avouch/v1/vocab> .
34
+
35
+ ofv:key a rdf:Property ;
36
+ rdfs:label "key" ;
37
+ rdfs:comment "Name of an entry in a YAML mapping. YAML: non_link_fields.<key>, edit_cites.<key>, link_effects.<key>, transitions[].bind.<key>" ;
38
+ rdfs:isDefinedBy <https://w3id.org/avouch/v1/vocab> .
39
+
40
+ ofv:name a rdf:Property ;
41
+ rdfs:label "name" ;
42
+ rdfs:comment "Name of a permission row that is not a command. YAML: permission_rows_not_commands[].name" ;
43
+ rdfs:isDefinedBy <https://w3id.org/avouch/v1/vocab> .
44
+
45
+ ofv:reason a rdf:Property ;
46
+ rdfs:label "reason" ;
47
+ rdfs:comment "Reason text. YAML: permission_rows_not_commands[].reason, outOfScopeObjectTypes.<name>, objectTypes.<name>.non_link_fields.<key>.reason" ;
48
+ rdfs:isDefinedBy <https://w3id.org/avouch/v1/vocab> .
49
+
50
+ ofv:cite a rdf:Property ;
51
+ rdfs:label "cite" ;
52
+ rdfs:comment "Source citation (a node with doc and quote). YAML: cite, in any record that has one, scope.cite, scope.global[].cite, scope.root.cite, dispositions.<ID>.cite, decision.order.cite, decision…next.cite, derivedProperties.<id>.cite, ….principals.cite, actionTypes.<ID>.idempotencyKey.cite" ;
53
+ rdfs:isDefinedBy <https://w3id.org/avouch/v1/vocab> .
54
+
55
+ ofv:doc a rdf:Property ;
56
+ rdfs:label "doc" ;
57
+ rdfs:comment "Source document reference. YAML: doc, in any record that has one; also cite.doc" ;
58
+ rdfs:isDefinedBy <https://w3id.org/avouch/v1/vocab> .
59
+
60
+ ofv:quote a rdf:Property ;
61
+ rdfs:label "quote" ;
62
+ rdfs:comment "Quoted source text. YAML: cite.quote" ;
63
+ rdfs:isDefinedBy <https://w3id.org/avouch/v1/vocab> .
64
+
65
+ ofv:id a rdf:Property ;
66
+ rdfs:label "id" ;
67
+ rdfs:comment "Identifier of a record. YAML: knownSourceGaps[].id, actionTypes.<ID>.conditions[].id" ;
68
+ rdfs:isDefinedBy <https://w3id.org/avouch/v1/vocab> .
69
+
70
+ ofv:rule a rdf:Property ;
71
+ rdfs:label "rule" ;
72
+ rdfs:comment "Rule code of a source gap. YAML: knownSourceGaps[].rule" ;
73
+ rdfs:isDefinedBy <https://w3id.org/avouch/v1/vocab> .
74
+
75
+ ofv:kind a rdf:Property ;
76
+ rdfs:label "kind" ;
77
+ rdfs:comment "Kind of a source gap. YAML: knownSourceGaps[].kind" ;
78
+ rdfs:isDefinedBy <https://w3id.org/avouch/v1/vocab> .
79
+
80
+ ofv:keys a rdf:Property ;
81
+ rdfs:label "keys" ;
82
+ rdfs:comment "Keys of a record (permission keys, or gap keys). YAML: knownSourceGaps[].keys, actionTypes.<ID>.permission.keys" ;
83
+ rdfs:isDefinedBy <https://w3id.org/avouch/v1/vocab> .
84
+
85
+ ofv:doc_line a rdf:Property ;
86
+ rdfs:label "doc_line" ;
87
+ rdfs:comment "Document line of a source gap. YAML: knownSourceGaps[].doc_line" ;
88
+ rdfs:isDefinedBy <https://w3id.org/avouch/v1/vocab> .
89
+
90
+ ofv:conflict a rdf:Property ;
91
+ rdfs:label "conflict" ;
92
+ rdfs:comment "Conflict text of a source gap. YAML: knownSourceGaps[].conflict" ;
93
+ rdfs:isDefinedBy <https://w3id.org/avouch/v1/vocab> .
94
+
95
+ ofv:proposed a rdf:Property ;
96
+ rdfs:label "proposed" ;
97
+ rdfs:comment "Proposed fix of a source gap. YAML: knownSourceGaps[].proposed" ;
98
+ rdfs:isDefinedBy <https://w3id.org/avouch/v1/vocab> .
99
+
100
+ ofv:description a rdf:Property ;
101
+ rdfs:label "description" ;
102
+ rdfs:comment "Label of a context: a literal substring of the source line that lists its objects. YAML: contexts.<name>" ;
103
+ rdfs:isDefinedBy <https://w3id.org/avouch/v1/vocab> .
104
+
105
+ ofv:infrastructureStores a rdf:Property ;
106
+ rdfs:label "infrastructureStores" ;
107
+ rdfs:comment "Infrastructure stores that are not object types. YAML: infrastructureStores[]" ;
108
+ rdfs:isDefinedBy <https://w3id.org/avouch/v1/vocab> .
109
+
110
+ ofv:permission_rows_not_commands a rdf:Property ;
111
+ rdfs:label "permission_rows_not_commands" ;
112
+ rdfs:comment "Permission rows that are not commands. YAML: permission_rows_not_commands[]" ;
113
+ rdfs:isDefinedBy <https://w3id.org/avouch/v1/vocab> .
114
+
115
+ ofv:knownSourceGaps a rdf:Property ;
116
+ rdfs:label "knownSourceGaps" ;
117
+ rdfs:comment "Known gaps in the source document. YAML: knownSourceGaps[]" ;
118
+ rdfs:isDefinedBy <https://w3id.org/avouch/v1/vocab> .
119
+
120
+ ofv:context a rdf:Property ;
121
+ rdfs:label "context" ;
122
+ rdfs:comment "Context of an object type, action type or create rule. YAML: objectTypes.<name>.context, actionTypes.<ID>.context, linkTypes[].create_rule.context" ;
123
+ rdfs:isDefinedBy <https://w3id.org/avouch/v1/vocab> .
124
+
125
+ ofv:datasource a rdf:Property ;
126
+ rdfs:label "datasource" ;
127
+ rdfs:comment "Store that holds the object. YAML: objectTypes.<name>.datasource" ;
128
+ rdfs:isDefinedBy <https://w3id.org/avouch/v1/vocab> .
129
+
130
+ ofv:non_link_fields a rdf:Property ;
131
+ rdfs:label "non_link_fields" ;
132
+ rdfs:comment "Fields that look like links but are not. YAML: objectTypes.<name>.non_link_fields" ;
133
+ rdfs:isDefinedBy <https://w3id.org/avouch/v1/vocab> .
134
+
135
+ ofv:stateMachines a rdf:Property ;
136
+ rdfs:label "stateMachines" ;
137
+ rdfs:comment "State machine of an object type. YAML: stateMachines.<name>" ;
138
+ rdfs:isDefinedBy <https://w3id.org/avouch/v1/vocab> .
139
+
140
+ ofv:initial a rdf:Property ;
141
+ rdfs:label "initial" ;
142
+ rdfs:comment "Initial state. YAML: stateMachines.<name>.initial" ;
143
+ rdfs:isDefinedBy <https://w3id.org/avouch/v1/vocab> .
144
+
145
+ ofv:terminal a rdf:Property ;
146
+ rdfs:label "terminal" ;
147
+ rdfs:comment "Terminal state. YAML: stateMachines.<name>.terminal[]" ;
148
+ rdfs:isDefinedBy <https://w3id.org/avouch/v1/vocab> .
149
+
150
+ ofv:transitions a rdf:Property ;
151
+ rdfs:label "transitions" ;
152
+ rdfs:comment "State transition. YAML: stateMachines.<name>.transitions[]" ;
153
+ rdfs:isDefinedBy <https://w3id.org/avouch/v1/vocab> .
154
+
155
+ ofv:from a rdf:Property ;
156
+ rdfs:label "from" ;
157
+ rdfs:comment "Source state of a transition. YAML: stateMachines.<name>.transitions[].from" ;
158
+ rdfs:isDefinedBy <https://w3id.org/avouch/v1/vocab> .
159
+
160
+ ofv:to a rdf:Property ;
161
+ rdfs:label "to" ;
162
+ rdfs:comment "Target state of a transition. YAML: stateMachines.<name>.transitions[].to" ;
163
+ rdfs:isDefinedBy <https://w3id.org/avouch/v1/vocab> .
164
+
165
+ ofv:by a rdf:Property ;
166
+ rdfs:label "by" ;
167
+ rdfs:comment "Action type that causes a transition. YAML: stateMachines.<name>.transitions[].by[]" ;
168
+ rdfs:isDefinedBy <https://w3id.org/avouch/v1/vocab> .
169
+
170
+ ofv:out_of_scope a rdf:Property ;
171
+ rdfs:label "out_of_scope" ;
172
+ rdfs:comment "Why no declared action performs the transition. YAML: stateMachines.<name>.transitions[].out_of_scope" ;
173
+ rdfs:isDefinedBy <https://w3id.org/avouch/v1/vocab> .
174
+
175
+ ofv:bind a rdf:Property ;
176
+ rdfs:label "bind" ;
177
+ rdfs:comment "Per-action evidence for a transition, keyed by action id. YAML: stateMachines.<name>.transitions[].bind.<ID>" ;
178
+ rdfs:isDefinedBy <https://w3id.org/avouch/v1/vocab> .
179
+
180
+ ofv:class a rdf:Property ;
181
+ rdfs:label "class" ;
182
+ rdfs:comment "State class of a property: canonical, derived, policy or unknown. YAML: objectTypes.<name>.properties.<p>.class" ;
183
+ rdfs:isDefinedBy <https://w3id.org/avouch/v1/vocab> .
184
+
185
+ ofv:canonical_values a rdf:Property ;
186
+ rdfs:label "canonical_values" ;
187
+ rdfs:comment "Values of a non-canonical property that the source explicitly marks as canonical. YAML: objectTypes.<name>.properties.<p>.canonical_values" ;
188
+ rdfs:isDefinedBy <https://w3id.org/avouch/v1/vocab> .
189
+
190
+ ofv:values a rdf:Property ;
191
+ rdfs:label "values" ;
192
+ rdfs:comment "Values of a list. YAML: canonical_values.values[], conditions[].reads[].values[]" ;
193
+ rdfs:isDefinedBy <https://w3id.org/avouch/v1/vocab> .
194
+
195
+ ofv:materializedFrom a rdf:Property ;
196
+ rdfs:label "materializedFrom" ;
197
+ rdfs:comment "The source requires this derived property to be stored in the same operation as its inputs. YAML: objectTypes.<name>.properties.<p>.materializedFrom" ;
198
+ rdfs:isDefinedBy <https://w3id.org/avouch/v1/vocab> .
199
+
200
+ ofv:reads a rdf:Property ;
201
+ rdfs:label "reads" ;
202
+ rdfs:comment "Properties read. YAML: materializedFrom.reads[], derivedProperties.<id>.reads[], conditions[].reads[]" ;
203
+ rdfs:isDefinedBy <https://w3id.org/avouch/v1/vocab> .
204
+
205
+ ofv:cites a rdf:Property ;
206
+ rdfs:label "cites" ;
207
+ rdfs:comment "Citations, each a node with ofv:cite. YAML: objectTypes.<name>.properties.<p>.materializedFrom.cites[], actionTypes.<ID>.conditions[].cites[], decision.rows[].cites[], decision.otherwise.cites[]" ;
208
+ rdfs:isDefinedBy <https://w3id.org/avouch/v1/vocab> .
209
+
210
+ ofv:cardinality a rdf:Property ;
211
+ rdfs:label "cardinality" ;
212
+ rdfs:comment "Cardinality text of a link type, kept as written. YAML: linkTypes[].cardinality" ;
213
+ rdfs:isDefinedBy <https://w3id.org/avouch/v1/vocab> .
214
+
215
+ ofv:via a rdf:Property ;
216
+ rdfs:label "via" ;
217
+ rdfs:comment "Foreign-key field on the from object or the link store name (linkTypes[].via); the mechanism of a cross-context write (crossContext.via). YAML: linkTypes[].via, actionTypes.<ID>.crossContext.via" ;
218
+ rdfs:isDefinedBy <https://w3id.org/avouch/v1/vocab> .
219
+
220
+ ofv:table a rdf:Property ;
221
+ rdfs:label "table" ;
222
+ rdfs:comment "The link's own store. YAML: linkTypes[].table" ;
223
+ rdfs:isDefinedBy <https://w3id.org/avouch/v1/vocab> .
224
+
225
+ ofv:create_rule a rdf:Property ;
226
+ rdfs:label "create_rule" ;
227
+ rdfs:comment "Who may establish the link. YAML: linkTypes[].create_rule" ;
228
+ rdfs:isDefinedBy <https://w3id.org/avouch/v1/vocab> .
229
+
230
+ ofv:with_creates a rdf:Property ;
231
+ rdfs:label "with_creates" ;
232
+ rdfs:comment "Objects the action must create in the same operation. YAML: linkTypes[].create_rule.with_creates[]" ;
233
+ rdfs:isDefinedBy <https://w3id.org/avouch/v1/vocab> .
234
+
235
+ ofv:exceptions a rdf:Property ;
236
+ rdfs:label "exceptions" ;
237
+ rdfs:comment "Exceptions to a create rule. YAML: linkTypes[].create_rule.exceptions" ;
238
+ rdfs:isDefinedBy <https://w3id.org/avouch/v1/vocab> .
239
+
240
+ ofv:commands a rdf:Property ;
241
+ rdfs:label "commands" ;
242
+ rdfs:comment "Action types that are exceptions to a create rule. YAML: linkTypes[].create_rule.exceptions.commands[]" ;
243
+ rdfs:isDefinedBy <https://w3id.org/avouch/v1/vocab> .
244
+
245
+ ofv:of a rdf:Property ;
246
+ rdfs:label "of" ;
247
+ rdfs:comment "Object a derived property is defined on. YAML: derivedProperties.<id>.of" ;
248
+ rdfs:isDefinedBy <https://w3id.org/avouch/v1/vocab> .
249
+
250
+ ofv:doc_term a rdf:Property ;
251
+ rdfs:label "doc_term" ;
252
+ rdfs:comment "Term used in the source document. YAML: derivedProperties.<id>.doc_term, actionTypes.<ID>.doc_term" ;
253
+ rdfs:isDefinedBy <https://w3id.org/avouch/v1/vocab> .
254
+
255
+ ofv:evidence a rdf:Property ;
256
+ rdfs:label "evidence" ;
257
+ rdfs:comment "Id of the evidence that implements the action. YAML: actionTypes.<ID>.evidence" ;
258
+ rdfs:isDefinedBy <https://w3id.org/avouch/v1/vocab> .
259
+
260
+ ofv:emits a rdf:Property ;
261
+ rdfs:label "emits" ;
262
+ rdfs:comment "Event types the action writes. YAML: actionTypes.<ID>.emits[]" ;
263
+ rdfs:isDefinedBy <https://w3id.org/avouch/v1/vocab> .
264
+
265
+ ofv:creates a rdf:Property ;
266
+ rdfs:label "creates" ;
267
+ rdfs:comment "Object types an action type creates. YAML: actionTypes.<ID>.creates[]" ;
268
+ rdfs:isDefinedBy <https://w3id.org/avouch/v1/vocab> .
269
+
270
+ ofv:deletes a rdf:Property ;
271
+ rdfs:label "deletes" ;
272
+ rdfs:comment "Object types whose rows an action type removes (a soft delete is an edits). YAML: actionTypes.<ID>.deletes[]" ;
273
+ rdfs:isDefinedBy <https://w3id.org/avouch/v1/vocab> .
274
+
275
+ ofv:edits a rdf:Property ;
276
+ rdfs:label "edits" ;
277
+ rdfs:comment "Properties an action type edits. YAML: actionTypes.<ID>.edits[]" ;
278
+ rdfs:isDefinedBy <https://w3id.org/avouch/v1/vocab> .
279
+
280
+ ofv:edit_cites a rdf:Property ;
281
+ rdfs:label "edit_cites" ;
282
+ rdfs:comment "Citations of edits. YAML: actionTypes.<ID>.edit_cites.<key>" ;
283
+ rdfs:isDefinedBy <https://w3id.org/avouch/v1/vocab> .
284
+
285
+ ofv:crossContext a rdf:Property ;
286
+ rdfs:label "crossContext" ;
287
+ rdfs:comment "Cross-context write of an action type. YAML: actionTypes.<ID>.crossContext" ;
288
+ rdfs:isDefinedBy <https://w3id.org/avouch/v1/vocab> .
289
+
290
+ ofv:permission a rdf:Property ;
291
+ rdfs:label "permission" ;
292
+ rdfs:comment "Permission of an action type: a string, or a node with keys. YAML: actionTypes.<ID>.permission" ;
293
+ rdfs:isDefinedBy <https://w3id.org/avouch/v1/vocab> .
294
+
295
+ ofv:conditional_keys a rdf:Property ;
296
+ rdfs:label "conditional_keys" ;
297
+ rdfs:comment "Permission keys needed only under a condition. YAML: actionTypes.<ID>.permission.conditional_keys[]" ;
298
+ rdfs:isDefinedBy <https://w3id.org/avouch/v1/vocab> .
299
+
300
+ ofv:none a rdf:Property ;
301
+ rdfs:label "none" ;
302
+ rdfs:comment "Why there is no key (permission), or why the link is not changed (link effect). YAML: actionTypes.<ID>.permission.none, actionTypes.<ID>.link_effects.<key>.none" ;
303
+ rdfs:isDefinedBy <https://w3id.org/avouch/v1/vocab> .
304
+
305
+ ofv:conditional_cite a rdf:Property ;
306
+ rdfs:label "conditional_cite" ;
307
+ rdfs:comment "Citation for the conditional keys. YAML: actionTypes.<ID>.permission.conditional_cite" ;
308
+ rdfs:isDefinedBy <https://w3id.org/avouch/v1/vocab> .
309
+
310
+ ofv:any_of a rdf:Property ;
311
+ rdfs:label "any_of" ;
312
+ rdfs:comment "At least 2 alternatives, each a keys form; the actor may run the action when its keys satisfy any one of them (OR). YAML: actionTypes.<ID>.permission.any_of" ;
313
+ rdfs:isDefinedBy <https://w3id.org/avouch/v1/vocab> .
314
+
315
+ ofv:parameters a rdf:Property ;
316
+ rdfs:label "parameters" ;
317
+ rdfs:comment "Parameters of an action type, the inputs it takes; each has a key and a cite. YAML: actionTypes.<ID>.parameters" ;
318
+ rdfs:isDefinedBy <https://w3id.org/avouch/v1/vocab> .
319
+
320
+ ofv:param a rdf:Property ;
321
+ rdfs:label "param" ;
322
+ rdfs:comment "Parameter named in a read, optionally with values. YAML: actionTypes.<ID>.conditions[].reads[].param" ;
323
+ rdfs:isDefinedBy <https://w3id.org/avouch/v1/vocab> .
324
+
325
+ ofv:actor a rdf:Property ;
326
+ rdfs:label "actor" ;
327
+ rdfs:comment "The caller read by a criterion: id or keys. YAML: actionTypes.<ID>.conditions[].reads[].actor" ;
328
+ rdfs:isDefinedBy <https://w3id.org/avouch/v1/vocab> .
329
+
330
+ ofv:submissionCriteria a rdf:Property ;
331
+ rdfs:label "submissionCriteria" ;
332
+ rdfs:comment "Renamed to conditions; exports no longer use it. YAML before the rename: actionTypes.<ID>.submissionCriteria[]" ;
333
+ owl:deprecated true ;
334
+ rdfs:isDefinedBy <https://w3id.org/avouch/v1/vocab> .
335
+
336
+ ofv:conditions a rdf:Property ;
337
+ rdfs:label "conditions" ;
338
+ rdfs:comment "Condition of an action type: a named claim, written as an expression or stated as not yet written. YAML: actionTypes.<ID>.conditions[]" ;
339
+ rdfs:isDefinedBy <https://w3id.org/avouch/v1/vocab> .
340
+
341
+ ofv:expr a rdf:Property ;
342
+ rdfs:label "expr" ;
343
+ rdfs:comment "Expression as one rdf:JSON literal (object keys sorted, no white space), not as triples. YAML: actionTypes.<ID>.conditions[].expr, derivedProperties.<id>.expr" ;
344
+ rdfs:isDefinedBy <https://w3id.org/avouch/v1/vocab> .
345
+
346
+ ofv:prop a rdf:Property ;
347
+ rdfs:label "prop" ;
348
+ rdfs:comment "Property named in a read with values. YAML: actionTypes.<ID>.conditions[].reads[].prop" ;
349
+ rdfs:isDefinedBy <https://w3id.org/avouch/v1/vocab> .
350
+
351
+ ofv:reads_unspecified a rdf:Property ;
352
+ rdfs:label "reads_unspecified" ;
353
+ rdfs:comment "Statement that the reads are not specified. YAML: actionTypes.<ID>.conditions[].reads_unspecified" ;
354
+ rdfs:isDefinedBy <https://w3id.org/avouch/v1/vocab> .
355
+
356
+ ofv:scenarios a rdf:Property ;
357
+ rdfs:label "scenarios" ;
358
+ rdfs:comment "Scenario ids. YAML: conditions[].scenarios[], link_effects.<key>.scenarios[]" ;
359
+ rdfs:isDefinedBy <https://w3id.org/avouch/v1/vocab> .
360
+
361
+ ofv:link_effects a rdf:Property ;
362
+ rdfs:label "link_effects" ;
363
+ rdfs:comment "Effect of an action type on a link type. YAML: actionTypes.<ID>.link_effects" ;
364
+ rdfs:isDefinedBy <https://w3id.org/avouch/v1/vocab> .
365
+
366
+ ofv:effect a rdf:Property ;
367
+ rdfs:label "effect" ;
368
+ rdfs:comment "Text of a link effect. YAML: actionTypes.<ID>.link_effects.<key>.effect" ;
369
+ rdfs:isDefinedBy <https://w3id.org/avouch/v1/vocab> .
370
+
371
+ ofv:unspecified a rdf:Property ;
372
+ rdfs:label "unspecified" ;
373
+ rdfs:comment "Statement that something is not specified. YAML: actionTypes.<ID>.link_effects.<key>.unspecified, actionTypes.<ID>.crossContext.unspecified, actionTypes.<ID>.conditions[].unspecified, decision.rows[].next.unspecified, decision.otherwise.next.unspecified" ;
374
+ rdfs:isDefinedBy <https://w3id.org/avouch/v1/vocab> .
375
+
376
+ ofv:Disposition a rdfs:Class ;
377
+ rdfs:label "Disposition" ;
378
+ rdfs:comment "A result that a decision table can give. Exported from the YAML path dispositions.<ID>." ;
379
+ rdfs:isDefinedBy <https://w3id.org/avouch/v1/vocab> .
380
+
381
+ ofv:dispositionClass a rdf:Property ;
382
+ rdfs:label "dispositionClass" ;
383
+ rdfs:comment "Class of a disposition: rejected, recorded, pending or applied. YAML: dispositions.<ID>.class" ;
384
+ rdfs:isDefinedBy <https://w3id.org/avouch/v1/vocab> .
385
+
386
+ ofv:decision a rdf:Property ;
387
+ rdfs:label "decision" ;
388
+ rdfs:comment "Decision table of an action type. YAML: actionTypes.<ID>.decision" ;
389
+ rdfs:isDefinedBy <https://w3id.org/avouch/v1/vocab> .
390
+
391
+ ofv:hitPolicy a rdf:Property ;
392
+ rdfs:label "hitPolicy" ;
393
+ rdfs:comment "Hit policy of a decision table; v1 has only first (top to bottom, first match). YAML: actionTypes.<ID>.decision.hitPolicy" ;
394
+ rdfs:isDefinedBy <https://w3id.org/avouch/v1/vocab> .
395
+
396
+ ofv:order a rdf:Property ;
397
+ rdfs:label "order" ;
398
+ rdfs:comment "Citation for the row order of a decision table. YAML: actionTypes.<ID>.decision.order" ;
399
+ rdfs:isDefinedBy <https://w3id.org/avouch/v1/vocab> .
400
+
401
+ ofv:rows a rdf:Property ;
402
+ rdfs:label "rows" ;
403
+ rdfs:comment "Row of a decision table, with ofv:index. YAML: actionTypes.<ID>.decision.rows[]" ;
404
+ rdfs:isDefinedBy <https://w3id.org/avouch/v1/vocab> .
405
+
406
+ ofv:when a rdf:Property ;
407
+ rdfs:label "when" ;
408
+ rdfs:comment "Test of a row: a node with ofv:passes or ofv:fails. YAML: actionTypes.<ID>.decision.rows[].when" ;
409
+ rdfs:isDefinedBy <https://w3id.org/avouch/v1/vocab> .
410
+
411
+ ofv:passes a rdf:Property ;
412
+ rdfs:label "passes" ;
413
+ rdfs:comment "Id of a condition that must evaluate to T. YAML: actionTypes.<ID>.decision.rows[].when.passes" ;
414
+ rdfs:isDefinedBy <https://w3id.org/avouch/v1/vocab> .
415
+
416
+ ofv:fails a rdf:Property ;
417
+ rdfs:label "fails" ;
418
+ rdfs:comment "Id of a condition that does not evaluate to T (it is F or U). It is not a negation. YAML: actionTypes.<ID>.decision.rows[].when.fails" ;
419
+ rdfs:isDefinedBy <https://w3id.org/avouch/v1/vocab> .
420
+
421
+ ofv:result a rdf:Property ;
422
+ rdfs:label "result" ;
423
+ rdfs:comment "Disposition that a row gives: an ofv:Disposition, or a literal when the id is not declared. YAML: actionTypes.<ID>.decision.rows[].result, actionTypes.<ID>.decision.otherwise.result" ;
424
+ rdfs:isDefinedBy <https://w3id.org/avouch/v1/vocab> .
425
+
426
+ ofv:otherwise a rdf:Property ;
427
+ rdfs:label "otherwise" ;
428
+ rdfs:comment "Last row of a decision table; it gives the result when no row matches. YAML: actionTypes.<ID>.decision.otherwise" ;
429
+ rdfs:isDefinedBy <https://w3id.org/avouch/v1/vocab> .
430
+
431
+ ofv:next a rdf:Property ;
432
+ rdfs:label "next" ;
433
+ rdfs:comment "What follows a pending result. YAML: actionTypes.<ID>.decision.rows[].next, actionTypes.<ID>.decision.otherwise.next" ;
434
+ rdfs:isDefinedBy <https://w3id.org/avouch/v1/vocab> .
435
+
436
+ ofv:actions a rdf:Property ;
437
+ rdfs:label "actions" ;
438
+ rdfs:comment "Action type of this system that follows. YAML: decision.rows[].next.actions[], decision.otherwise.next.actions[]" ;
439
+ rdfs:isDefinedBy <https://w3id.org/avouch/v1/vocab> .
440
+
441
+ ofv:external a rdf:Property ;
442
+ rdfs:label "external" ;
443
+ rdfs:comment "Step outside this system that follows. YAML: decision.rows[].next.external, decision.otherwise.next.external" ;
444
+ rdfs:isDefinedBy <https://w3id.org/avouch/v1/vocab> .
445
+
446
+ ofv:scope a rdf:Property ;
447
+ rdfs:label "scope" ;
448
+ rdfs:comment "Tenant scope of the ontology. YAML: scope" ;
449
+ rdfs:isDefinedBy <https://w3id.org/avouch/v1/vocab> .
450
+
451
+ ofv:scopeBy a rdf:Property ;
452
+ rdfs:label "scopeBy" ;
453
+ rdfs:comment "Name of the field that holds the tenant. YAML: scope.by" ;
454
+ rdfs:isDefinedBy <https://w3id.org/avouch/v1/vocab> .
455
+
456
+ ofv:global a rdf:Property ;
457
+ rdfs:label "global" ;
458
+ rdfs:comment "Object type that all tenants share, with its citation. YAML: scope.global[]" ;
459
+ rdfs:isDefinedBy <https://w3id.org/avouch/v1/vocab> .
460
+
461
+ ofv:root a rdf:Property ;
462
+ rdfs:label "root" ;
463
+ rdfs:comment "Object type that is the tenant itself, with its citation. YAML: scope.root" ;
464
+ rdfs:isDefinedBy <https://w3id.org/avouch/v1/vocab> .
465
+
466
+ ofv:type a rdf:Property ;
467
+ rdfs:label "type" ;
468
+ rdfs:comment "A type. For a value type: a literal (enum, timestamp, duration, date, timezone, id, integer or boolean), the class whose ids the value holds, or a node with ofv:set. For a scope entry: the class of the entry. YAML: objectTypes.<name>.properties.<p>.type, actionTypes.<ID>.parameters.<n>.type, derivedProperties.<id>.type, scope.global[].type, scope.root.type" ;
469
+ rdfs:isDefinedBy <https://w3id.org/avouch/v1/vocab> .
470
+
471
+ ofv:set a rdf:Property ;
472
+ rdfs:label "set" ;
473
+ rdfs:comment "Element type of a set type, written as for ofv:type; string is allowed here only. YAML: ….type.set" ;
474
+ rdfs:isDefinedBy <https://w3id.org/avouch/v1/vocab> .
475
+
476
+ ofv:optional a rdf:Property ;
477
+ rdfs:label "optional" ;
478
+ rdfs:comment "The caller may leave the parameter out; it is then null. YAML: actionTypes.<ID>.parameters.<n>.optional" ;
479
+ rdfs:isDefinedBy <https://w3id.org/avouch/v1/vocab> .
480
+
481
+ ofv:principals a rdf:Property ;
482
+ rdfs:label "principals" ;
483
+ rdfs:comment "Actor kinds that one permission alternative admits, with its own citation; an actor of another kind is denied. YAML: actionTypes.<ID>.permission.principals, actionTypes.<ID>.permission.any_of[].principals" ;
484
+ rdfs:isDefinedBy <https://w3id.org/avouch/v1/vocab> .
485
+
486
+ ofv:kinds a rdf:Property ;
487
+ rdfs:label "kinds" ;
488
+ rdfs:comment "One actor kind. YAML: actionTypes.<ID>.permission.principals.kinds[], actionTypes.<ID>.permission.any_of[].principals.kinds[]" ;
489
+ rdfs:isDefinedBy <https://w3id.org/avouch/v1/vocab> .
490
+
491
+ ofv:idempotencyKey a rdf:Property ;
492
+ rdfs:label "idempotencyKey" ;
493
+ rdfs:comment "Declaration of whether the caller must supply an idempotency key for this action type, with its citation. Absent means unspecified, not false; never derived from the action's effects. YAML: actionTypes.<ID>.idempotencyKey" ;
494
+ rdfs:isDefinedBy <https://w3id.org/avouch/v1/vocab> .
495
+
496
+ ofv:required a rdf:Property ;
497
+ rdfs:label "required" ;
498
+ rdfs:comment "Whether the caller must supply an idempotency key; true and false are both explicit, cited claims. YAML: actionTypes.<ID>.idempotencyKey.required" ;
499
+ rdfs:isDefinedBy <https://w3id.org/avouch/v1/vocab> .