@delurio/cli 0.1.0 → 0.2.0

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.
@@ -0,0 +1,346 @@
1
+ {
2
+ "$schema": "https://json-schema.org/draft/2020-12/schema",
3
+ "$id": "https://unpkg.com/@delurio/cli@0.2.0/schemas/flow.schema.json",
4
+ "title": "delurio flow graph (flow.yaml)",
5
+ "description": "The behaviour of one flow in delurio-graph/0 (SPEC.md §2): states, events, transitions, a finite context, and the guarantees the checker proves. Its meaning is a subset of SCXML.",
6
+ "type": "object",
7
+ "required": ["schema", "kind", "id", "initial", "states"],
8
+ "additionalProperties": false,
9
+ "properties": {
10
+ "schema": { "$ref": "#/$defs/formatVersion" },
11
+ "kind": { "description": "The kind of graph file. A flow.yaml is `flow`.", "const": "flow" },
12
+ "id": { "$ref": "#/$defs/graphId", "description": "The flow's id, by which other flows start it (`starts`) or wait for it (`invoke: { flow }`), and by which its ui.yaml names it.", "examples": ["qauth.authorize", "learning.enrollment"] },
13
+ "title": { "type": "string", "description": "The flow in plain words, for people, in the graph's language." },
14
+ "summary": { "type": "string", "description": "What the flow is for, in a few plain sentences." },
15
+ "status": { "$ref": "#/$defs/status" },
16
+ "source": { "$ref": "#/$defs/sourceAnchor" },
17
+ "domain": { "$ref": "#/$defs/graphId", "description": "The id of the domain graph (domain.yaml) this flow's `invoke` actions and refusal codes come from; a shared domain sits in its own directory." },
18
+ "ui": { "$ref": "#/$defs/graphId", "description": "The id of the UI graph (ui.yaml) of this flow's screens. A flow with no screen (an endpoint, a webhook, an MCP tool, a CI job) has none." },
19
+ "scope": { "$ref": "#/$defs/scope" },
20
+ "decisions": {
21
+ "type": "object",
22
+ "description": "The questions still open in this flow, by id (D1, D2, ...), each with a recommendation and the states it affects.",
23
+ "additionalProperties": { "$ref": "#/$defs/decision" }
24
+ },
25
+ "context": {
26
+ "type": "object",
27
+ "description": "The context: a set of variables, each with a finite list of values. Keep it small: a variable stands for the few cases that change behaviour, not for the data itself.",
28
+ "propertyNames": { "$ref": "#/$defs/identifier" },
29
+ "additionalProperties": { "$ref": "#/$defs/contextVariable" }
30
+ },
31
+ "guards": {
32
+ "type": "object",
33
+ "description": "Named guards: each an expression (SPEC.md §3) that may name context variables, other guards and `state`.",
34
+ "propertyNames": { "$ref": "#/$defs/identifier" },
35
+ "additionalProperties": { "$ref": "#/$defs/expression" }
36
+ },
37
+ "effects": {
38
+ "type": "object",
39
+ "description": "Named effects: each changes the context when a transition that lists it is taken.",
40
+ "propertyNames": { "$ref": "#/$defs/identifier" },
41
+ "additionalProperties": { "$ref": "#/$defs/effect" }
42
+ },
43
+ "failures": {
44
+ "type": "object",
45
+ "description": "What each `FAIL.<class>` event stands for, per invoking state: the domain action it comes from, and per class the refusal codes of that action.",
46
+ "propertyNames": { "$ref": "#/$defs/identifier" },
47
+ "additionalProperties": { "$ref": "#/$defs/failureClasses" }
48
+ },
49
+ "main": { "$ref": "#/$defs/main" },
50
+ "initial": { "$ref": "#/$defs/stateRef", "description": "The state the flow starts in." },
51
+ "states": {
52
+ "type": "object",
53
+ "description": "The states of the flow, by id. Nested, parallel and history states are not part of v0.",
54
+ "minProperties": 1,
55
+ "propertyNames": { "$ref": "#/$defs/identifier" },
56
+ "additionalProperties": { "$ref": "#/$defs/state" }
57
+ },
58
+ "assertions": {
59
+ "type": "array",
60
+ "description": "The guarantees the checker proves: each holds when no reachable configuration (a state and a value for every context variable) satisfies its `never` expression. Under --strict a violated assertion exits 2.",
61
+ "items": { "$ref": "#/$defs/assertion" }
62
+ }
63
+ },
64
+ "$defs": {
65
+ "formatVersion": {
66
+ "description": "The format and its version. Every graph file starts with it.",
67
+ "const": "delurio-graph/0"
68
+ },
69
+ "status": {
70
+ "description": "`proposed`: written before or apart from the code, nobody has approved it. `captured`: read from existing code (capture mode, every element carries `source`). `approved`: the owner reviewed and accepted it.",
71
+ "enum": ["proposed", "captured", "approved"]
72
+ },
73
+ "graphId": {
74
+ "type": "string",
75
+ "description": "The id of a graph.",
76
+ "pattern": "^[A-Za-z0-9][A-Za-z0-9._-]*$"
77
+ },
78
+ "identifier": {
79
+ "type": "string",
80
+ "description": "An identifier of the graph: English camelCase (SPEC.md §1, Text).",
81
+ "pattern": "^[a-z][A-Za-z0-9]*$"
82
+ },
83
+ "stateRef": {
84
+ "type": "string",
85
+ "description": "The id of a state of this flow.",
86
+ "pattern": "^[a-z][A-Za-z0-9]*$"
87
+ },
88
+ "eventName": {
89
+ "type": "string",
90
+ "description": "An event: UPPER_SNAKE (SPEC.md §1). A state with `invoke` ends with `DONE` (success) or `FAIL.<class>`, the class being a key under `failures` for that state. `NAVIGATE` is the person leaving the page.",
91
+ "pattern": "^(FAIL\\.[a-z][A-Za-z0-9]*|[A-Z][A-Z0-9_]*)$",
92
+ "examples": ["DONE", "FAIL.server", "NAVIGATE", "ALLOW"]
93
+ },
94
+ "expression": {
95
+ "type": "string",
96
+ "description": "An expression of delurio's own language (SPEC.md §3), interpreted, never evaluated as code: or, and, not, ==, !=, in, not in, ( ), [lists], 'strings', numbers, true, false, null, and names (a context variable, a named guard or `state`).",
97
+ "minLength": 1,
98
+ "examples": ["prompt == 'none'", "not hasSession or (not fresh and prompt == 'login')", "state in ['login', 'consent']"]
99
+ },
100
+ "sourceAnchor": {
101
+ "type": "string",
102
+ "description": "A source anchor (SPEC.md §9): `<repo-relative path>:<line>`, or a bare `<path>` for a whole file, naming the code that makes the element true. In capture mode every element carries one; in create mode an element carries one when the code already exists.",
103
+ "pattern": "^[^\\s:]+(:[0-9]+)?$",
104
+ "examples": ["src/authorize/consent.ts:42", "app/page.tsx"]
105
+ },
106
+ "scalar": {
107
+ "description": "A value of a context variable: a string, a number or a boolean.",
108
+ "type": ["string", "number", "boolean"]
109
+ },
110
+ "scope": {
111
+ "type": "object",
112
+ "description": "What the view shows and how to read what it does not draw.",
113
+ "additionalProperties": false,
114
+ "properties": {
115
+ "shows": { "type": "string", "description": "What this view shows, in words." },
116
+ "world": {
117
+ "description": "`open`: what is not drawn is not ruled out; only `assertions` and `outOfView` are binding. `closed`: the flow is complete.",
118
+ "enum": ["open", "closed"]
119
+ },
120
+ "outOfView": { "type": "array", "description": "What this view leaves out, each in words.", "items": { "type": "string" } }
121
+ }
122
+ },
123
+ "decision": {
124
+ "type": "object",
125
+ "description": "A question still open in this flow.",
126
+ "required": ["question", "recommend", "affects"],
127
+ "additionalProperties": false,
128
+ "properties": {
129
+ "question": { "type": "string", "description": "The question, in words." },
130
+ "recommend": { "type": "string", "description": "The recommended answer." },
131
+ "affects": { "type": "array", "description": "The states the decision affects.", "items": { "$ref": "#/$defs/stateRef" } }
132
+ }
133
+ },
134
+ "contextVariable": {
135
+ "type": "object",
136
+ "description": "A context variable with a finite list of values.",
137
+ "required": ["values", "initial"],
138
+ "additionalProperties": false,
139
+ "properties": {
140
+ "values": {
141
+ "type": "array",
142
+ "description": "Every value the variable can take: a finite list, so the checker can visit every reachable configuration.",
143
+ "minItems": 1,
144
+ "uniqueItems": true,
145
+ "items": { "$ref": "#/$defs/scalar" }
146
+ },
147
+ "initial": {
148
+ "description": "`any` makes the checker start from every value; a fixed value (one of `values`) starts from that one.",
149
+ "type": ["string", "number", "boolean"],
150
+ "examples": ["any", false]
151
+ },
152
+ "say": {
153
+ "type": "object",
154
+ "description": "Each value in plain words, for the sentence that describes a starting situation; keyed by the value as text (\"true\", \"false\", ...).",
155
+ "additionalProperties": { "type": "string" }
156
+ }
157
+ }
158
+ },
159
+ "effect": {
160
+ "type": "object",
161
+ "description": "A change to the context, made when a transition that lists this effect is taken.",
162
+ "additionalProperties": false,
163
+ "properties": {
164
+ "set": {
165
+ "type": "object",
166
+ "description": "Variables and the values they are given.",
167
+ "propertyNames": { "$ref": "#/$defs/identifier" },
168
+ "additionalProperties": { "$ref": "#/$defs/scalar" }
169
+ },
170
+ "havoc": {
171
+ "type": "array",
172
+ "description": "Variables that may take any value of their domain (the checker branches into each): a change made elsewhere that the flow cannot know in advance.",
173
+ "items": { "$ref": "#/$defs/identifier" }
174
+ }
175
+ }
176
+ },
177
+ "failureClasses": {
178
+ "type": "object",
179
+ "description": "The failure classes of one invoking state: `from` names the domain action, and each other key is a class (`FAIL.<class>`) listing the refusal codes of that action it stands for.",
180
+ "required": ["from"],
181
+ "properties": {
182
+ "from": { "type": "string", "description": "The domain action whose refusals the classes stand for." }
183
+ },
184
+ "propertyNames": { "$ref": "#/$defs/identifier" },
185
+ "additionalProperties": {
186
+ "type": "array",
187
+ "description": "The refusal codes of the action that this class stands for.",
188
+ "items": { "type": "string" }
189
+ }
190
+ },
191
+ "main": {
192
+ "description": "The main story: the story the page tells first and the path a tutorial and a replay take by default. The events in order from the `given` start; a plain list of events means no `given`. Under --strict it fails when the events cannot be walked.",
193
+ "anyOf": [
194
+ { "$ref": "#/$defs/events" },
195
+ {
196
+ "type": "object",
197
+ "required": ["events"],
198
+ "additionalProperties": false,
199
+ "properties": {
200
+ "given": {
201
+ "type": "object",
202
+ "description": "The starting situation: a value for context variables whose `initial` is `any`.",
203
+ "propertyNames": { "$ref": "#/$defs/identifier" },
204
+ "additionalProperties": { "$ref": "#/$defs/scalar" }
205
+ },
206
+ "events": { "$ref": "#/$defs/events" }
207
+ }
208
+ }
209
+ ]
210
+ },
211
+ "events": {
212
+ "type": "array",
213
+ "description": "Events, in the order they happen.",
214
+ "items": { "$ref": "#/$defs/eventName" }
215
+ },
216
+ "state": {
217
+ "type": "object",
218
+ "description": "A state of the flow: a step, a screen or an exit.",
219
+ "additionalProperties": false,
220
+ "properties": {
221
+ "title": { "type": "string", "description": "The state in words; the id is used when it is missing." },
222
+ "description": { "type": "string", "description": "What happens in the state, in words." },
223
+ "by": {
224
+ "description": "Who does the step: `human`, `ai`, `agent` or `ci`. A server step with no actor has none.",
225
+ "enum": ["human", "ai", "agent", "ci"]
226
+ },
227
+ "goal": { "type": "boolean", "description": "`true` on the state the flow is for, the one it keeps returning to (for flows that never end)." },
228
+ "includes": { "type": "array", "description": "What the step covers beyond its title, each in words.", "items": { "type": "string" } },
229
+ "invoke": { "$ref": "#/$defs/invoke" },
230
+ "on": {
231
+ "type": "object",
232
+ "description": "Event to transition. Not on a final state.",
233
+ "propertyNames": { "$ref": "#/$defs/eventName" },
234
+ "additionalProperties": { "$ref": "#/$defs/transitionSpec" }
235
+ },
236
+ "always": {
237
+ "type": "array",
238
+ "description": "An ordered list of transitions taken at once, with no event (SCXML's eventless transitions). The first whose guard holds is taken; the list must cover every context, or the flow does not load. Not on a final state.",
239
+ "minItems": 1,
240
+ "items": { "$ref": "#/$defs/transition" }
241
+ },
242
+ "type": {
243
+ "description": "`final`: an exit; it has no transitions.",
244
+ "const": "final"
245
+ },
246
+ "redirect": {
247
+ "description": "On an exit of a web protocol flow: `success` redirects back with what it carries, `error` redirects with an error code, `never` shows a page and does not redirect.",
248
+ "enum": ["success", "error", "never"]
249
+ },
250
+ "code": { "type": "string", "description": "On an exit of a web protocol flow: the error code, or several separated by `|`.", "examples": ["access_denied", "login_required | consent_required"] },
251
+ "carries": { "type": "array", "description": "On a success exit of a web protocol flow: what it carries back.", "items": { "type": "string" }, "examples": [["code", "state"]] },
252
+ "leavesPage": { "type": "boolean", "description": "On an exit: `true` when the person left (closed the page, went elsewhere)." },
253
+ "response": { "$ref": "#/$defs/response" },
254
+ "source": { "$ref": "#/$defs/sourceAnchor" }
255
+ },
256
+ "allOf": [
257
+ {
258
+ "$comment": "an exit (type: final) has no transitions and runs nothing",
259
+ "if": { "properties": { "type": { "const": "final" } }, "required": ["type"] },
260
+ "then": { "properties": { "on": false, "always": false, "invoke": false } }
261
+ },
262
+ {
263
+ "$comment": "redirect, code, carries and leavesPage are only on an exit (type: final)",
264
+ "if": { "not": { "properties": { "type": { "const": "final" } }, "required": ["type"] } },
265
+ "then": { "properties": { "redirect": false, "code": false, "carries": false, "leavesPage": false } }
266
+ }
267
+ ]
268
+ },
269
+ "invoke": {
270
+ "$comment": "one of action, flow or external",
271
+ "type": "object",
272
+ "description": "The step runs something, and ends with `DONE` or `FAIL.<class>`: one of `action`, `flow` or `external`.",
273
+ "minProperties": 1,
274
+ "maxProperties": 1,
275
+ "additionalProperties": false,
276
+ "properties": {
277
+ "action": { "type": "string", "description": "An action of the flow's domain (domain.yaml)." },
278
+ "flow": { "$ref": "#/$defs/graphId", "description": "Another flow, by id: it runs and this one waits for its outcome." },
279
+ "external": { "type": "string", "description": "Something outside the graphs, by name." }
280
+ }
281
+ },
282
+ "transitionSpec": {
283
+ "description": "A transition: a target state id, a transition object, or a list of those, tried in document order (the first whose guard holds is taken).",
284
+ "anyOf": [
285
+ { "$ref": "#/$defs/stateRef" },
286
+ { "$ref": "#/$defs/transition" },
287
+ {
288
+ "type": "array",
289
+ "minItems": 1,
290
+ "items": { "anyOf": [{ "$ref": "#/$defs/stateRef" }, { "$ref": "#/$defs/transition" }] }
291
+ }
292
+ ]
293
+ },
294
+ "transition": {
295
+ "type": "object",
296
+ "description": "A transition (an SCXML transition: a guard is a `cond`, an effect an `<assign>`).",
297
+ "required": ["target"],
298
+ "additionalProperties": false,
299
+ "properties": {
300
+ "target": { "$ref": "#/$defs/stateRef", "description": "The state the transition goes to." },
301
+ "guard": { "$ref": "#/$defs/expression", "description": "The transition is taken only when this holds: an expression, or the name of a guard." },
302
+ "effect": { "type": "array", "description": "The effects applied, in order, when the transition is taken.", "items": { "$ref": "#/$defs/identifier" } },
303
+ "say": { "type": "string", "description": "The transition in words." },
304
+ "starts": { "$ref": "#/$defs/starts" },
305
+ "source": { "$ref": "#/$defs/sourceAnchor" }
306
+ }
307
+ },
308
+ "starts": {
309
+ "type": "object",
310
+ "description": "This flow starts another one and does not wait for it (SPEC.md §2, Several flows). The checker finds the started flow by id among the graphs beside this one.",
311
+ "required": ["flow"],
312
+ "additionalProperties": false,
313
+ "properties": {
314
+ "flow": { "$ref": "#/$defs/graphId", "description": "The id of the flow started." },
315
+ "given": {
316
+ "type": "object",
317
+ "description": "What the started flow begins with: per variable of the started flow, an expression over this flow's context, read where the transition is taken, after its effects. A constant is quoted (`from: \"'repo'\"`).",
318
+ "additionalProperties": { "type": ["string", "number", "boolean"], "description": "An expression over this flow's context, or a constant (a number, a boolean, or a quoted string)." }
319
+ }
320
+ }
321
+ },
322
+ "response": {
323
+ "type": "object",
324
+ "description": "On a step with no screen: what the outside world sees, as data: `status`, `code`, `redirect`, `headers`, `body`, or the fields of a tool's answer. The page shows it beside the step.",
325
+ "properties": {
326
+ "status": { "type": ["integer", "string"], "description": "The HTTP status." },
327
+ "code": { "type": "string", "description": "The error or refusal code answered." },
328
+ "redirect": { "type": "string", "description": "Where the answer redirects." },
329
+ "headers": { "type": "object", "description": "The headers of the answer.", "additionalProperties": { "type": "string" } },
330
+ "body": { "description": "The body of the answer: its words, or its fields." }
331
+ }
332
+ },
333
+ "assertion": {
334
+ "type": "object",
335
+ "description": "A guarantee the checker proves.",
336
+ "required": ["id", "never"],
337
+ "additionalProperties": false,
338
+ "properties": {
339
+ "id": { "type": "string", "description": "The assertion's id, unique in the flow.", "examples": ["deny-never-issues"] },
340
+ "never": { "$ref": "#/$defs/expression", "description": "An expression that no reachable configuration may satisfy." },
341
+ "say": { "type": "string", "description": "The guarantee in words." },
342
+ "source": { "$ref": "#/$defs/sourceAnchor" }
343
+ }
344
+ }
345
+ }
346
+ }
@@ -0,0 +1,292 @@
1
+ {
2
+ "$schema": "https://json-schema.org/draft/2020-12/schema",
3
+ "$id": "https://unpkg.com/@delurio/cli@0.2.0/schemas/nav.schema.json",
4
+ "title": "delurio navigation graph (nav.yaml, nav.extracted.yaml)",
5
+ "description": "The pages of an app, the ways between them, and who may open them, in delurio-graph/0 (SPEC.md §6). A nav.yaml (`kind: nav`) may `extends` a generated nav.extracted.yaml (`kind: nav-extracted`), whose pages, guards and entries come first.",
6
+ "type": "object",
7
+ "required": ["kind"],
8
+ "properties": {
9
+ "kind": {
10
+ "description": "`nav` for a navigation graph (nav.yaml); `nav-extracted` for the file the extractor generates from the application source (nav.extracted.yaml), which a nav.yaml extends.",
11
+ "enum": ["nav", "nav-extracted"]
12
+ }
13
+ },
14
+ "if": { "properties": { "kind": { "const": "nav-extracted" } }, "required": ["kind"] },
15
+ "then": { "$ref": "#/$defs/extracted" },
16
+ "else": { "$ref": "#/$defs/nav" },
17
+ "$defs": {
18
+ "nav": {
19
+ "type": "object",
20
+ "description": "A navigation graph (nav.yaml).",
21
+ "required": ["schema", "kind", "id", "locale", "catalog", "frames", "personas"],
22
+ "additionalProperties": false,
23
+ "properties": {
24
+ "schema": { "$ref": "#/$defs/formatVersion" },
25
+ "kind": { "description": "The kind of graph file. A nav.yaml is `nav`.", "const": "nav" },
26
+ "id": { "$ref": "#/$defs/graphId", "description": "The navigation graph's id.", "examples": ["shop-landing"] },
27
+ "title": { "type": "string", "description": "The app or the part of it this graph covers, in plain words." },
28
+ "summary": { "type": "string", "description": "What the graph shows, in a few plain sentences." },
29
+ "status": { "$ref": "#/$defs/status" },
30
+ "source": { "$ref": "#/$defs/graphSource" },
31
+ "extends": { "type": "string", "description": "A generated file this one extends, relative to it: its pages, guards and entries come first, and this file adds to them or overrides single fields of a page.", "examples": ["nav.extracted.yaml"] },
32
+ "extract": { "$ref": "#/$defs/extract" },
33
+ "locale": { "$ref": "#/$defs/locale", "description": "The locale of the catalog the page uses, a BCP 47 tag." },
34
+ "catalog": {
35
+ "type": "string",
36
+ "description": "The catalog of page titles and entry labels, relative to this file: `catalog/<locale>.json`.",
37
+ "pattern": "\\.json$",
38
+ "examples": ["catalog/tr.json"]
39
+ },
40
+ "frames": {
41
+ "type": "object",
42
+ "description": "The chrome around pages (a menu, a top bar), by id. An entry `from: frame:<f>` is on every page of that frame.",
43
+ "minProperties": 1,
44
+ "additionalProperties": { "$ref": "#/$defs/frame" }
45
+ },
46
+ "facts": {
47
+ "type": "object",
48
+ "description": "What is true of the person, not of the page, by name. Every persona gives a value for each fact.",
49
+ "additionalProperties": { "$ref": "#/$defs/fact" }
50
+ },
51
+ "sets": {
52
+ "type": "object",
53
+ "description": "Named lists of values, which a persona may give for a fact with `of`.",
54
+ "additionalProperties": { "type": "array", "description": "The values of the set.", "items": { "type": "string" } }
55
+ },
56
+ "guards": { "$ref": "#/$defs/guards" },
57
+ "personas": {
58
+ "type": "object",
59
+ "description": "Kinds of user, by id: each a fixed set of facts, so the tool works out each persona's view, menu and reachable pages with no search.",
60
+ "additionalProperties": { "$ref": "#/$defs/persona" }
61
+ },
62
+ "pages": { "$ref": "#/$defs/pages" },
63
+ "entries": { "$ref": "#/$defs/entries" },
64
+ "goals": {
65
+ "type": "array",
66
+ "description": "Things people come to do: each is checked for every persona it names (the page is reached within the clicks).",
67
+ "items": { "$ref": "#/$defs/goal" }
68
+ },
69
+ "notModeled": {
70
+ "type": "array",
71
+ "description": "What the graph leaves out, each in words. Quote an item that contains `: `.",
72
+ "items": { "type": "string" }
73
+ }
74
+ },
75
+ "allOf": [
76
+ {
77
+ "$comment": "without extends, the pages are all here: a page called start, and every page gives its route, kind and frame",
78
+ "if": { "not": { "required": ["extends"] } },
79
+ "then": {
80
+ "required": ["pages"],
81
+ "properties": {
82
+ "pages": {
83
+ "type": "object",
84
+ "required": ["start"],
85
+ "additionalProperties": { "type": "object", "required": ["route", "kind", "frame"] }
86
+ }
87
+ }
88
+ }
89
+ }
90
+ ]
91
+ },
92
+ "extracted": {
93
+ "type": "object",
94
+ "description": "The generated half of a navigation graph (nav.extracted.yaml), written by `extract` from the application source; never by hand. A person reviews its diff when the application changes.",
95
+ "required": ["schema", "kind"],
96
+ "additionalProperties": false,
97
+ "properties": {
98
+ "schema": { "$ref": "#/$defs/formatVersion" },
99
+ "kind": { "description": "The kind of graph file. A nav.extracted.yaml is `nav-extracted`.", "const": "nav-extracted" },
100
+ "source": { "$ref": "#/$defs/graphSource" },
101
+ "guards": { "$ref": "#/$defs/guards" },
102
+ "pages": {
103
+ "type": "object",
104
+ "$ref": "#/$defs/pages",
105
+ "additionalProperties": { "type": "object", "required": ["route", "kind", "frame"] }
106
+ },
107
+ "entries": { "$ref": "#/$defs/entries" }
108
+ }
109
+ },
110
+ "formatVersion": {
111
+ "description": "The format and its version. Every graph file starts with it.",
112
+ "const": "delurio-graph/0"
113
+ },
114
+ "status": {
115
+ "description": "`proposed`: written before or apart from the code, nobody has approved it. `captured`: read from existing code (capture mode, every element carries `source`). `approved`: the owner reviewed and accepted it.",
116
+ "enum": ["proposed", "captured", "approved"]
117
+ },
118
+ "graphId": {
119
+ "type": "string",
120
+ "description": "The id of a graph.",
121
+ "pattern": "^[A-Za-z0-9][A-Za-z0-9._-]*$"
122
+ },
123
+ "locale": {
124
+ "type": "string",
125
+ "description": "A BCP 47 language tag.",
126
+ "pattern": "^[A-Za-z]{2,3}(-[A-Za-z0-9]{1,8})*$",
127
+ "examples": ["en", "tr", "pt-BR"]
128
+ },
129
+ "expression": {
130
+ "type": "string",
131
+ "description": "An expression of delurio's own language (SPEC.md §3) over facts: or, and, not, ==, !=, in, not in, ( ), [lists], 'strings', numbers, true, false, null, and names (a fact or a named guard).",
132
+ "minLength": 1,
133
+ "examples": ["role == 'admin'", "'course.edit' in held"]
134
+ },
135
+ "sourceAnchor": {
136
+ "type": "string",
137
+ "description": "A source anchor (SPEC.md §9): `<repo-relative path>:<line>`, or a bare `<path>` for a whole file, naming the code that makes the element true.",
138
+ "pattern": "^[^\\s:]+(:[0-9]+)?$",
139
+ "examples": ["app/(global)/hesap/page.tsx", "apps/nazar/features/shell/scope.ts:206"]
140
+ },
141
+ "graphSource": {
142
+ "type": "object",
143
+ "description": "Where the graph was read from: the repository, the commit, and the files that hold the pages, the menus and the words. The navigation page shows the repository and the commit.",
144
+ "properties": {
145
+ "repo": { "type": "string", "description": "The application's repository.", "examples": ["acme/shop"] },
146
+ "commit": { "type": "string", "description": "The commit the extractor read." },
147
+ "app": { "type": "string", "description": "The application's folder in the repository." },
148
+ "menu": { "type": "string", "description": "The file that holds the menus." },
149
+ "locales": { "type": "string", "description": "The application's translation files; `{locale}` stands for the locale." }
150
+ },
151
+ "additionalProperties": { "type": "string", "description": "Another file or folder of the application the graph was read from." }
152
+ },
153
+ "extract": {
154
+ "type": "object",
155
+ "description": "How the extractor reads the application: its adapter, the names it gives to pages that no menu entry names, the titles it takes, and the locales it copies into catalog/.",
156
+ "additionalProperties": false,
157
+ "properties": {
158
+ "adapter": { "type": "string", "description": "The extractor adapter for this application." },
159
+ "names": { "type": "object", "description": "Route to page id, for pages that no menu entry names.", "additionalProperties": { "type": "string", "description": "The page id." } },
160
+ "titles": { "type": "object", "description": "Page id to the catalog key of its title.", "additionalProperties": { "type": "string", "description": "The catalog key." } },
161
+ "locales": { "type": "array", "description": "The locales copied into catalog/, as BCP 47 tags.", "items": { "$ref": "#/$defs/locale" } }
162
+ }
163
+ },
164
+ "guards": {
165
+ "type": "object",
166
+ "description": "Named guards: each an expression over facts and other guards. A guard may not be written in both the extracted file and nav.yaml with different text.",
167
+ "additionalProperties": { "$ref": "#/$defs/expression" }
168
+ },
169
+ "frame": {
170
+ "type": "object",
171
+ "description": "The chrome drawn around a page.",
172
+ "required": ["title"],
173
+ "additionalProperties": false,
174
+ "properties": {
175
+ "title": { "type": "string", "description": "The frame in plain words." },
176
+ "menu": { "type": "string", "description": "The menu the frame carries, by name; a frame without one has none." },
177
+ "shows": {
178
+ "type": "object",
179
+ "description": "Its pages draw the menu of the frame that the person's fact names (a page outside any scope shows the menu of the scope the person lands in).",
180
+ "required": ["fact"],
181
+ "additionalProperties": false,
182
+ "properties": { "fact": { "type": "string", "description": "The fact whose value names the frame." } }
183
+ },
184
+ "source": { "$ref": "#/$defs/sourceAnchor" }
185
+ }
186
+ },
187
+ "fact": {
188
+ "type": "object",
189
+ "description": "A fact about the person.",
190
+ "additionalProperties": false,
191
+ "properties": {
192
+ "values": { "type": "array", "description": "The values the fact can take.", "minItems": 1, "items": { "type": ["string", "number", "boolean"] } },
193
+ "of": { "type": "array", "description": "The fact is a list, drawn from these values (permission codes held, for one); a persona gives a list or the name of a set.", "items": { "type": "string" } },
194
+ "say": { "type": "string", "description": "The fact in plain words." }
195
+ }
196
+ },
197
+ "persona": {
198
+ "type": "object",
199
+ "description": "A kind of user: a fixed set of facts.",
200
+ "required": ["title", "say", "facts"],
201
+ "additionalProperties": false,
202
+ "properties": {
203
+ "title": { "type": "string", "description": "The persona's name, in the graph's language." },
204
+ "say": { "type": "string", "description": "Who they are, in plain words." },
205
+ "facts": {
206
+ "type": "object",
207
+ "description": "A value for every fact: one of its `values`, or for a fact with `of` a list of its values or the name of a set.",
208
+ "additionalProperties": {
209
+ "description": "The persona's value of the fact.",
210
+ "type": ["string", "number", "boolean", "array"],
211
+ "items": { "type": "string" }
212
+ }
213
+ }
214
+ }
215
+ },
216
+ "pages": {
217
+ "type": "object",
218
+ "description": "The pages of the app, by id. A page called `start` is required (in nav.yaml or the file it extends).",
219
+ "additionalProperties": { "$ref": "#/$defs/page" }
220
+ },
221
+ "page": {
222
+ "type": "object",
223
+ "description": "A page. In a nav.yaml that extends a generated file, a page of that file may give single fields to override.",
224
+ "additionalProperties": false,
225
+ "properties": {
226
+ "route": { "type": "string", "description": "The page's route; `{id}` stands for a path parameter.", "examples": ["/", "/ders/{id}/celseler"] },
227
+ "title": { "type": "string", "description": "The page's title: a catalog key, or plain words." },
228
+ "kind": {
229
+ "description": "`screen`, `placeholder` (says it is not ready), `missing` (the server answers not found), `redirect` or `external`.",
230
+ "enum": ["screen", "placeholder", "missing", "redirect", "external"]
231
+ },
232
+ "frame": { "type": "string", "description": "The frame drawn around the page." },
233
+ "opensFor": { "$ref": "#/$defs/expression", "description": "The guard under which the page opens; it refuses everyone else." },
234
+ "refusal": { "type": "string", "description": "How the page says no, in words." },
235
+ "pageCodes": { "type": "string", "description": "Generated: the name of the permission codes that open the page, as the application's permission table names them." },
236
+ "evidence": { "type": "string", "description": "The code that shows the page's kind or gate, in words." },
237
+ "source": { "$ref": "#/$defs/sourceAnchor" }
238
+ }
239
+ },
240
+ "entries": {
241
+ "type": "array",
242
+ "description": "The ways from page to page.",
243
+ "items": { "$ref": "#/$defs/entry" }
244
+ },
245
+ "entry": {
246
+ "type": "object",
247
+ "description": "A way from page to page.",
248
+ "required": ["from", "to", "how"],
249
+ "additionalProperties": false,
250
+ "properties": {
251
+ "id": { "type": "string", "description": "The entry's id; `<from>><to>#<line>` when left out." },
252
+ "from": {
253
+ "description": "The page it starts at, `frame:<frame>` for every page of a frame, or a list of those.",
254
+ "type": ["string", "array"],
255
+ "items": { "type": "string" },
256
+ "examples": ["start", "frame:site"]
257
+ },
258
+ "to": { "type": "string", "description": "The page it opens." },
259
+ "how": {
260
+ "description": "`auto` (a redirect, no click), `menu`, `picker` (a scope picker), `user` (the person row), `link` or `button`.",
261
+ "enum": ["auto", "menu", "picker", "user", "link", "button"]
262
+ },
263
+ "label": { "type": "string", "description": "Its words: a catalog key, or plain words. Every entry but an `auto` one has a label." },
264
+ "when": { "$ref": "#/$defs/expression", "description": "The guard under which the entry is offered." },
265
+ "clicks": { "type": "integer", "minimum": 0, "description": "How many clicks it takes; 0 for `auto`, else 1 when left out." },
266
+ "data": { "type": "boolean", "description": "`true` when it needs an item to exist (a row to click)." },
267
+ "back": { "type": "boolean", "description": "`true` for a way back." },
268
+ "source": { "$ref": "#/$defs/sourceAnchor" }
269
+ },
270
+ "allOf": [
271
+ {
272
+ "$comment": "every entry but an auto one has a label",
273
+ "if": { "not": { "properties": { "how": { "const": "auto" } } } },
274
+ "then": { "required": ["label"] }
275
+ }
276
+ ]
277
+ },
278
+ "goal": {
279
+ "type": "object",
280
+ "description": "Something people come to do.",
281
+ "required": ["id", "say", "page", "for", "within"],
282
+ "additionalProperties": false,
283
+ "properties": {
284
+ "id": { "type": "string", "description": "The goal's id." },
285
+ "say": { "type": "string", "description": "The goal in plain words." },
286
+ "page": { "type": "string", "description": "The page that does it." },
287
+ "for": { "type": "array", "description": "The personas it is checked for.", "items": { "type": "string" } },
288
+ "within": { "type": "integer", "minimum": 0, "description": "The most clicks it may take from `start`." }
289
+ }
290
+ }
291
+ }
292
+ }