soustack 0.4.5 → 0.4.7
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/package.json +1 -1
- package/src/stacks/compute.schema 3.json +0 -7
- package/src/stacks/dietary.schema 3.json +0 -45
- package/src/stacks/dietary@1 3.md +0 -24
- package/src/stacks/illustrated@1 3.md +0 -24
- package/src/stacks/prep.schema 3.json +0 -76
- package/src/stacks/prep@1 3.md +0 -276
- package/src/stacks/referenced.schema 3.json +0 -96
- package/src/stacks/scaling.schema 3.json +0 -99
- package/src/stacks/scaling@1 3.md +0 -238
- package/src/stacks/storage.schema 3.json +0 -132
- package/src/stacks/structured.schema 3.json +0 -48
- package/src/stacks/substitutions@1 3.md +0 -24
- package/src/stacks/timed@1 3.md +0 -23
package/package.json
CHANGED
|
@@ -1,45 +0,0 @@
|
|
|
1
|
-
{
|
|
2
|
-
"$schema": "https://json-schema.org/draft/2020-12/schema",
|
|
3
|
-
"$id": "https://soustack.spec/stacks/dietary.schema.json",
|
|
4
|
-
"title": "Dietary Stack",
|
|
5
|
-
"type": "object",
|
|
6
|
-
"properties": {
|
|
7
|
-
"dietary": {
|
|
8
|
-
"type": "object",
|
|
9
|
-
"properties": {
|
|
10
|
-
"basis": { "type": "string", "enum": ["perServing", "perRecipe"] },
|
|
11
|
-
"calories": { "type": "number", "minimum": 0 },
|
|
12
|
-
"macros": {
|
|
13
|
-
"type": "object",
|
|
14
|
-
"properties": {
|
|
15
|
-
"protein": { "type": "number", "minimum": 0 },
|
|
16
|
-
"fat": { "type": "number", "minimum": 0 },
|
|
17
|
-
"carbohydrates": { "type": "number", "minimum": 0 },
|
|
18
|
-
"metadata": { "type": "object", "additionalProperties": true }
|
|
19
|
-
},
|
|
20
|
-
"minProperties": 1,
|
|
21
|
-
"additionalProperties": false,
|
|
22
|
-
"patternProperties": {
|
|
23
|
-
"^x-": { "$ref": "../defs/common.schema.json#/properties/extensionLaneValue" }
|
|
24
|
-
}
|
|
25
|
-
},
|
|
26
|
-
"diets": { "type": "array", "items": { "type": "string" } },
|
|
27
|
-
"allergens": { "type": "array", "items": { "type": "string" } },
|
|
28
|
-
"metadata": { "type": "object", "additionalProperties": true }
|
|
29
|
-
},
|
|
30
|
-
"required": ["basis"],
|
|
31
|
-
"additionalProperties": false,
|
|
32
|
-
"anyOf": [
|
|
33
|
-
{ "required": ["calories"] },
|
|
34
|
-
{ "required": ["macros"] },
|
|
35
|
-
{ "required": ["diets"] },
|
|
36
|
-
{ "required": ["allergens"] }
|
|
37
|
-
],
|
|
38
|
-
"patternProperties": {
|
|
39
|
-
"^x-": { "$ref": "../defs/common.schema.json#/properties/extensionLaneValue" }
|
|
40
|
-
}
|
|
41
|
-
}
|
|
42
|
-
},
|
|
43
|
-
"required": ["dietary"],
|
|
44
|
-
"additionalProperties": false
|
|
45
|
-
}
|
|
@@ -1,24 +0,0 @@
|
|
|
1
|
-
# dietary@1
|
|
2
|
-
|
|
3
|
-
## Purpose
|
|
4
|
-
The `dietary@1` stack enables recipes to declare nutritional information, dietary classifications, and allergen information.
|
|
5
|
-
|
|
6
|
-
## Adds
|
|
7
|
-
- Top-level `dietary` object with `basis` field (perServing or perRecipe).
|
|
8
|
-
- Optional `calories`, `macros` (protein, fat, carbohydrates), `diets`, and `allergens` fields.
|
|
9
|
-
- At least one of calories, macros, diets, or allergens must be present.
|
|
10
|
-
|
|
11
|
-
## Requires
|
|
12
|
-
- None
|
|
13
|
-
|
|
14
|
-
## Semantics
|
|
15
|
-
- MUST: The `dietary` object must include a `basis` field.
|
|
16
|
-
- MUST: At least one of `calories`, `macros`, `diets`, or `allergens` must be present.
|
|
17
|
-
- NOTE: Dietary tags and allergen lists are freeform strings in v1 (no controlled vocabulary).
|
|
18
|
-
|
|
19
|
-
## Composition Notes
|
|
20
|
-
- This stack is monotonic: it adds requirements or fields without removing expressiveness.
|
|
21
|
-
- Interaction: No dependencies on other stacks. Can be combined with any other stack.
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
@@ -1,24 +0,0 @@
|
|
|
1
|
-
# illustrated@1
|
|
2
|
-
|
|
3
|
-
## Purpose
|
|
4
|
-
The `illustrated@1` stack enables recipes to include media (images and videos) at the recipe and step levels for visual guidance.
|
|
5
|
-
|
|
6
|
-
## Adds
|
|
7
|
-
- Top-level `images` and `videos` arrays (optional).
|
|
8
|
-
- Step objects may include `images` and `videos` arrays.
|
|
9
|
-
- Steps are structured objects with `id` fields.
|
|
10
|
-
|
|
11
|
-
## Requires
|
|
12
|
-
- None
|
|
13
|
-
|
|
14
|
-
## Semantics
|
|
15
|
-
- MUST: At least one media item (image or video) must be present at the recipe level or in at least one step.
|
|
16
|
-
- MUST: Step objects must include `id` fields (steps are structured).
|
|
17
|
-
- NOTE: Media URIs must be valid according to the URI schema definition.
|
|
18
|
-
|
|
19
|
-
## Composition Notes
|
|
20
|
-
- This stack is monotonic: it adds requirements or fields without removing expressiveness.
|
|
21
|
-
- Interaction: Works with `structured@1` for step-level media. No dependencies on other stacks.
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
@@ -1,76 +0,0 @@
|
|
|
1
|
-
{
|
|
2
|
-
"$schema": "https://json-schema.org/draft/2020-12/schema",
|
|
3
|
-
"$id": "https://soustack.spec/stacks/prep.schema.json",
|
|
4
|
-
"title": "Prep Stack",
|
|
5
|
-
"type": "object",
|
|
6
|
-
"properties": {
|
|
7
|
-
"miseEnPlace": {
|
|
8
|
-
"type": "array",
|
|
9
|
-
"minItems": 1,
|
|
10
|
-
"items": {
|
|
11
|
-
"$ref": "#/$defs/miseEnPlaceTask"
|
|
12
|
-
}
|
|
13
|
-
}
|
|
14
|
-
},
|
|
15
|
-
"required": ["miseEnPlace"],
|
|
16
|
-
"$defs": {
|
|
17
|
-
"miseEnPlaceTask": {
|
|
18
|
-
"type": "object",
|
|
19
|
-
"properties": {
|
|
20
|
-
"id": {
|
|
21
|
-
"type": "string",
|
|
22
|
-
"pattern": "^[A-Za-z0-9._-]+$"
|
|
23
|
-
},
|
|
24
|
-
"text": {
|
|
25
|
-
"type": "string",
|
|
26
|
-
"minLength": 1
|
|
27
|
-
},
|
|
28
|
-
"inputs": {
|
|
29
|
-
"type": "array",
|
|
30
|
-
"items": {
|
|
31
|
-
"type": "string"
|
|
32
|
-
},
|
|
33
|
-
"uniqueItems": true,
|
|
34
|
-
"minItems": 1
|
|
35
|
-
},
|
|
36
|
-
"usesEquipment": {
|
|
37
|
-
"type": "array",
|
|
38
|
-
"items": {
|
|
39
|
-
"type": "string",
|
|
40
|
-
"pattern": "^[A-Za-z0-9._-]+$"
|
|
41
|
-
},
|
|
42
|
-
"uniqueItems": true,
|
|
43
|
-
"minItems": 1
|
|
44
|
-
}
|
|
45
|
-
},
|
|
46
|
-
"required": ["text"],
|
|
47
|
-
"additionalProperties": false,
|
|
48
|
-
"patternProperties": {
|
|
49
|
-
"^x-": {
|
|
50
|
-
"$ref": "../defs/common.schema.json#/properties/extensionLaneValue"
|
|
51
|
-
}
|
|
52
|
-
}
|
|
53
|
-
},
|
|
54
|
-
"prepItem": {
|
|
55
|
-
"type": "object",
|
|
56
|
-
"properties": {
|
|
57
|
-
"verb": {
|
|
58
|
-
"type": "string",
|
|
59
|
-
"minLength": 1
|
|
60
|
-
},
|
|
61
|
-
"detail": {
|
|
62
|
-
"type": "string"
|
|
63
|
-
}
|
|
64
|
-
},
|
|
65
|
-
"required": ["verb"],
|
|
66
|
-
"additionalProperties": false,
|
|
67
|
-
"patternProperties": {
|
|
68
|
-
"^x-": {
|
|
69
|
-
"$ref": "../defs/common.schema.json#/properties/extensionLaneValue"
|
|
70
|
-
}
|
|
71
|
-
}
|
|
72
|
-
}
|
|
73
|
-
},
|
|
74
|
-
"additionalProperties": false
|
|
75
|
-
}
|
|
76
|
-
|
package/src/stacks/prep@1 3.md
DELETED
|
@@ -1,276 +0,0 @@
|
|
|
1
|
-
#prep@1
|
|
2
|
-
|
|
3
|
-
---
|
|
4
|
-
|
|
5
|
-
## Purpose
|
|
6
|
-
|
|
7
|
-
The `prep@1` stack enables recipes to include preparation guidance at both the ingredient level and as explicit mise en place tasks. This makes recipes more operational by providing clear prep instructions and planning checklists.
|
|
8
|
-
|
|
9
|
-
This stack is adoption-first: ingredient prep can be simple strings for minimal friction, or structured objects for more detailed guidance.
|
|
10
|
-
|
|
11
|
-
---
|
|
12
|
-
|
|
13
|
-
## Requirements
|
|
14
|
-
|
|
15
|
-
A document that declares `prep@1`:
|
|
16
|
-
|
|
17
|
-
• MUST satisfy all structural rules enabled by this stack.
|
|
18
|
-
• MUST satisfy the semantic rules described below.
|
|
19
|
-
|
|
20
|
-
---
|
|
21
|
-
|
|
22
|
-
## Data Model
|
|
23
|
-
|
|
24
|
-
When `prep@1` is declared, the document MUST include a top-level `miseEnPlace` array.
|
|
25
|
-
|
|
26
|
-
### Mise En Place Array
|
|
27
|
-
|
|
28
|
-
The `miseEnPlace` array contains one or more tasks. Each task is a structured object that describes a prep step.
|
|
29
|
-
|
|
30
|
-
### Ingredient-Level Prep
|
|
31
|
-
|
|
32
|
-
Ingredient objects MAY include an optional `prep` field that describes how the ingredient should be prepared. The `prep` field supports multiple formats:
|
|
33
|
-
|
|
34
|
-
• A single string (simple prep phrase)
|
|
35
|
-
• An array of strings (multiple prep notes)
|
|
36
|
-
• An array of structured prep items
|
|
37
|
-
|
|
38
|
-
---
|
|
39
|
-
|
|
40
|
-
## Ingredient Prep Formats
|
|
41
|
-
|
|
42
|
-
### String Format
|
|
43
|
-
|
|
44
|
-
A simple string describing the prep:
|
|
45
|
-
|
|
46
|
-
```json
|
|
47
|
-
{
|
|
48
|
-
"id": "onion",
|
|
49
|
-
"name": "Onion",
|
|
50
|
-
"prep": "finely diced"
|
|
51
|
-
}
|
|
52
|
-
```
|
|
53
|
-
|
|
54
|
-
### Array of Strings
|
|
55
|
-
|
|
56
|
-
Multiple prep notes:
|
|
57
|
-
|
|
58
|
-
```json
|
|
59
|
-
{
|
|
60
|
-
"id": "garlic",
|
|
61
|
-
"name": "Garlic",
|
|
62
|
-
"prep": ["peeled", "minced"]
|
|
63
|
-
}
|
|
64
|
-
```
|
|
65
|
-
|
|
66
|
-
### Structured Prep Items
|
|
67
|
-
|
|
68
|
-
More detailed prep instructions with verbs and details:
|
|
69
|
-
|
|
70
|
-
```json
|
|
71
|
-
{
|
|
72
|
-
"id": "tomato",
|
|
73
|
-
"name": "Tomato",
|
|
74
|
-
"prep": [
|
|
75
|
-
{ "verb": "dice", "detail": "fine" },
|
|
76
|
-
{ "verb": "reserve", "detail": "half for garnish" }
|
|
77
|
-
]
|
|
78
|
-
}
|
|
79
|
-
```
|
|
80
|
-
|
|
81
|
-
Each prep item object includes:
|
|
82
|
-
|
|
83
|
-
• `verb` (required): string describing the action (freeform; no controlled vocabulary in v1)
|
|
84
|
-
• `detail` (optional): string providing additional context
|
|
85
|
-
|
|
86
|
-
---
|
|
87
|
-
|
|
88
|
-
## Mise En Place Tasks
|
|
89
|
-
|
|
90
|
-
### Minimal Task
|
|
91
|
-
|
|
92
|
-
A task with only required text:
|
|
93
|
-
|
|
94
|
-
```json
|
|
95
|
-
{
|
|
96
|
-
"miseEnPlace": [
|
|
97
|
-
{ "text": "Finely dice the onion" },
|
|
98
|
-
{ "text": "Mince the garlic" }
|
|
99
|
-
]
|
|
100
|
-
}
|
|
101
|
-
```
|
|
102
|
-
|
|
103
|
-
### Task with ID
|
|
104
|
-
|
|
105
|
-
Tasks may include an optional `id` for cross-referencing:
|
|
106
|
-
|
|
107
|
-
```json
|
|
108
|
-
{
|
|
109
|
-
"miseEnPlace": [
|
|
110
|
-
{ "id": "dice-onion", "text": "Finely dice the onion" },
|
|
111
|
-
{ "id": "mince-garlic", "text": "Mince the garlic" }
|
|
112
|
-
]
|
|
113
|
-
}
|
|
114
|
-
```
|
|
115
|
-
|
|
116
|
-
Task IDs MUST be unique within the `miseEnPlace` array (semantic validation).
|
|
117
|
-
|
|
118
|
-
### Task with Ingredient References
|
|
119
|
-
|
|
120
|
-
When `referenced@1` is present, tasks may reference ingredient IDs:
|
|
121
|
-
|
|
122
|
-
```json
|
|
123
|
-
{
|
|
124
|
-
"miseEnPlace": [
|
|
125
|
-
{
|
|
126
|
-
"text": "Prepare the vegetables",
|
|
127
|
-
"inputs": ["onion", "garlic"]
|
|
128
|
-
}
|
|
129
|
-
]
|
|
130
|
-
}
|
|
131
|
-
```
|
|
132
|
-
|
|
133
|
-
Each id in `inputs` MUST exist in the ingredients array (semantic validation when both stacks are present).
|
|
134
|
-
|
|
135
|
-
### Task with Equipment References
|
|
136
|
-
|
|
137
|
-
When `equipment@1` is present, tasks may reference equipment IDs:
|
|
138
|
-
|
|
139
|
-
```json
|
|
140
|
-
{
|
|
141
|
-
"miseEnPlace": [
|
|
142
|
-
{
|
|
143
|
-
"text": "Sharpen the knife",
|
|
144
|
-
"usesEquipment": ["knife"]
|
|
145
|
-
}
|
|
146
|
-
]
|
|
147
|
-
}
|
|
148
|
-
```
|
|
149
|
-
|
|
150
|
-
Each id in `usesEquipment` MUST exist in the equipment array (semantic validation when both stacks are present).
|
|
151
|
-
|
|
152
|
-
### Combined References
|
|
153
|
-
|
|
154
|
-
Tasks may include both `inputs` and `usesEquipment`:
|
|
155
|
-
|
|
156
|
-
```json
|
|
157
|
-
{
|
|
158
|
-
"miseEnPlace": [
|
|
159
|
-
{
|
|
160
|
-
"text": "Dice the onion with a sharp knife",
|
|
161
|
-
"inputs": ["onion"],
|
|
162
|
-
"usesEquipment": ["knife"]
|
|
163
|
-
}
|
|
164
|
-
]
|
|
165
|
-
}
|
|
166
|
-
```
|
|
167
|
-
|
|
168
|
-
---
|
|
169
|
-
|
|
170
|
-
## Semantics
|
|
171
|
-
|
|
172
|
-
### Prep Field
|
|
173
|
-
|
|
174
|
-
The ingredient `prep` field is descriptive and freeform. In v1, there is no controlled vocabulary for prep verbs or details. Tools may interpret these as hints for display or planning, but validation does not enforce specific values.
|
|
175
|
-
|
|
176
|
-
### Mise En Place
|
|
177
|
-
|
|
178
|
-
The `miseEnPlace` array is an explicit checklist of prep tasks. Tools may render this as a prep plan, separate from the main cooking instructions. Tasks are ordered and may be presented as a sequential checklist.
|
|
179
|
-
|
|
180
|
-
---
|
|
181
|
-
|
|
182
|
-
## Composition
|
|
183
|
-
|
|
184
|
-
The prep stack is composable with other stacks:
|
|
185
|
-
|
|
186
|
-
• No hard dependency on `referenced@1` or `equipment@1` (references are optional and only validated when those stacks are present)
|
|
187
|
-
• Works with `structured@1` to enable ingredient object definitions
|
|
188
|
-
• Remains monotonic: does not close objects needed by other stacks
|
|
189
|
-
• Ingredient `prep` is optional even when the stack is present
|
|
190
|
-
|
|
191
|
-
---
|
|
192
|
-
|
|
193
|
-
## Semantic Validation Rules (Normative)
|
|
194
|
-
|
|
195
|
-
Validators MUST enforce the following rules:
|
|
196
|
-
|
|
197
|
-
1. Mise en place task ID uniqueness
|
|
198
|
-
If tasks include `id` values, all task IDs MUST be unique within the `miseEnPlace` array.
|
|
199
|
-
|
|
200
|
-
2. Ingredient reference resolution
|
|
201
|
-
If both `prep@1` and `referenced@1` are present, and a task includes `inputs`, all referenced ingredient IDs MUST exist in the ingredients array.
|
|
202
|
-
|
|
203
|
-
3. Equipment reference resolution
|
|
204
|
-
If both `prep@1` and `equipment@1` are present, and a task includes `usesEquipment`, all referenced equipment IDs MUST exist in the equipment array (as object ids, not string items).
|
|
205
|
-
|
|
206
|
-
---
|
|
207
|
-
|
|
208
|
-
## Examples
|
|
209
|
-
|
|
210
|
-
Minimal mise en place
|
|
211
|
-
|
|
212
|
-
```json
|
|
213
|
-
{
|
|
214
|
-
"miseEnPlace": [
|
|
215
|
-
{ "text": "Finely dice the onion" },
|
|
216
|
-
{ "text": "Mince the garlic" }
|
|
217
|
-
]
|
|
218
|
-
}
|
|
219
|
-
```
|
|
220
|
-
|
|
221
|
-
Ingredient prep with strings
|
|
222
|
-
|
|
223
|
-
```json
|
|
224
|
-
{
|
|
225
|
-
"ingredients": [
|
|
226
|
-
{
|
|
227
|
-
"id": "onion",
|
|
228
|
-
"name": "Onion",
|
|
229
|
-
"prep": "finely diced"
|
|
230
|
-
},
|
|
231
|
-
{
|
|
232
|
-
"id": "garlic",
|
|
233
|
-
"name": "Garlic",
|
|
234
|
-
"prep": ["peeled", "minced"]
|
|
235
|
-
}
|
|
236
|
-
]
|
|
237
|
-
}
|
|
238
|
-
```
|
|
239
|
-
|
|
240
|
-
Structured prep items
|
|
241
|
-
|
|
242
|
-
```json
|
|
243
|
-
{
|
|
244
|
-
"ingredients": [
|
|
245
|
-
{
|
|
246
|
-
"id": "tomato",
|
|
247
|
-
"name": "Tomato",
|
|
248
|
-
"prep": [
|
|
249
|
-
{ "verb": "dice", "detail": "fine" },
|
|
250
|
-
{ "verb": "reserve", "detail": "half for garnish" }
|
|
251
|
-
]
|
|
252
|
-
}
|
|
253
|
-
]
|
|
254
|
-
}
|
|
255
|
-
```
|
|
256
|
-
|
|
257
|
-
Mise en place with references
|
|
258
|
-
|
|
259
|
-
```json
|
|
260
|
-
{
|
|
261
|
-
"equipment": [
|
|
262
|
-
{ "id": "knife", "name": "Chef's knife" }
|
|
263
|
-
],
|
|
264
|
-
"ingredients": [
|
|
265
|
-
{ "id": "onion", "name": "Onion" }
|
|
266
|
-
],
|
|
267
|
-
"miseEnPlace": [
|
|
268
|
-
{
|
|
269
|
-
"text": "Dice the onion with a sharp knife",
|
|
270
|
-
"inputs": ["onion"],
|
|
271
|
-
"usesEquipment": ["knife"]
|
|
272
|
-
}
|
|
273
|
-
]
|
|
274
|
-
}
|
|
275
|
-
```
|
|
276
|
-
|
|
@@ -1,96 +0,0 @@
|
|
|
1
|
-
{
|
|
2
|
-
"$schema": "https://json-schema.org/draft/2020-12/schema",
|
|
3
|
-
"$id": "https://soustack.spec/stacks/referenced.schema.json",
|
|
4
|
-
"title": "Referenced Stack",
|
|
5
|
-
"type": "object",
|
|
6
|
-
"properties": {
|
|
7
|
-
"ingredients": {
|
|
8
|
-
"type": "array",
|
|
9
|
-
"items": {
|
|
10
|
-
"anyOf": [
|
|
11
|
-
{ "$ref": "#/$defs/ingredient" },
|
|
12
|
-
{ "$ref": "#/$defs/ingredientSection" }
|
|
13
|
-
]
|
|
14
|
-
}
|
|
15
|
-
},
|
|
16
|
-
"instructions": {
|
|
17
|
-
"type": "array",
|
|
18
|
-
"items": {
|
|
19
|
-
"anyOf": [
|
|
20
|
-
{ "$ref": "#/$defs/referencedStep" },
|
|
21
|
-
{ "$ref": "#/$defs/referencedSection" }
|
|
22
|
-
]
|
|
23
|
-
}
|
|
24
|
-
}
|
|
25
|
-
},
|
|
26
|
-
"required": ["ingredients", "instructions"],
|
|
27
|
-
"allOf": [
|
|
28
|
-
{ "$ref": "./structured.schema.json" }
|
|
29
|
-
],
|
|
30
|
-
"$defs": {
|
|
31
|
-
"ingredient": {
|
|
32
|
-
"allOf": [
|
|
33
|
-
{ "$ref": "../defs/entities.schema.json#/$defs/IngredientBase" },
|
|
34
|
-
{ "required": ["id"] }
|
|
35
|
-
]
|
|
36
|
-
},
|
|
37
|
-
"ingredientSection": {
|
|
38
|
-
"type": "object",
|
|
39
|
-
"properties": {
|
|
40
|
-
"section": { "type": "string" },
|
|
41
|
-
"ingredients": {
|
|
42
|
-
"type": "array",
|
|
43
|
-
"items": {
|
|
44
|
-
"anyOf": [
|
|
45
|
-
{ "$ref": "#/$defs/ingredient" },
|
|
46
|
-
{ "$ref": "#/$defs/ingredientSection" }
|
|
47
|
-
]
|
|
48
|
-
}
|
|
49
|
-
},
|
|
50
|
-
"metadata": { "type": "object", "additionalProperties": true }
|
|
51
|
-
},
|
|
52
|
-
"required": ["section", "ingredients"],
|
|
53
|
-
"additionalProperties": false,
|
|
54
|
-
"patternProperties": {
|
|
55
|
-
"^x-": { "$ref": "../defs/common.schema.json#/properties/extensionLaneValue" }
|
|
56
|
-
}
|
|
57
|
-
},
|
|
58
|
-
"referencedStep": {
|
|
59
|
-
"allOf": [
|
|
60
|
-
{ "$ref": "../defs/entities.schema.json#/$defs/StepBase" },
|
|
61
|
-
{
|
|
62
|
-
"properties": {
|
|
63
|
-
"inputs": {
|
|
64
|
-
"allOf": [
|
|
65
|
-
{ "$ref": "../defs/entities.schema.json#/$defs/StepBase/properties/inputs" },
|
|
66
|
-
{ "minItems": 1 }
|
|
67
|
-
]
|
|
68
|
-
}
|
|
69
|
-
},
|
|
70
|
-
"required": ["id", "inputs"]
|
|
71
|
-
}
|
|
72
|
-
]
|
|
73
|
-
},
|
|
74
|
-
"referencedSection": {
|
|
75
|
-
"type": "object",
|
|
76
|
-
"properties": {
|
|
77
|
-
"section": { "type": "string" },
|
|
78
|
-
"steps": {
|
|
79
|
-
"type": "array",
|
|
80
|
-
"items": {
|
|
81
|
-
"anyOf": [
|
|
82
|
-
{ "$ref": "#/$defs/referencedStep" },
|
|
83
|
-
{ "$ref": "#/$defs/referencedSection" }
|
|
84
|
-
]
|
|
85
|
-
}
|
|
86
|
-
},
|
|
87
|
-
"metadata": { "type": "object", "additionalProperties": true }
|
|
88
|
-
},
|
|
89
|
-
"required": ["section", "steps"],
|
|
90
|
-
"additionalProperties": false,
|
|
91
|
-
"patternProperties": {
|
|
92
|
-
"^x-": { "$ref": "../defs/common.schema.json#/properties/extensionLaneValue" }
|
|
93
|
-
}
|
|
94
|
-
}
|
|
95
|
-
}
|
|
96
|
-
}
|
|
@@ -1,99 +0,0 @@
|
|
|
1
|
-
{
|
|
2
|
-
"$schema": "https://json-schema.org/draft/2020-12/schema",
|
|
3
|
-
"$id": "https://soustack.spec/stacks/scaling.schema.json",
|
|
4
|
-
"title": "Scaling Stack",
|
|
5
|
-
"type": "object",
|
|
6
|
-
"properties": {
|
|
7
|
-
"ingredients": {
|
|
8
|
-
"type": "array",
|
|
9
|
-
"items": {
|
|
10
|
-
"anyOf": [
|
|
11
|
-
{ "$ref": "#/$defs/ingredient" },
|
|
12
|
-
{ "$ref": "#/$defs/ingredientSection" }
|
|
13
|
-
]
|
|
14
|
-
}
|
|
15
|
-
},
|
|
16
|
-
"scaling": {
|
|
17
|
-
"type": "object",
|
|
18
|
-
"properties": {
|
|
19
|
-
"discrete": {
|
|
20
|
-
"type": "object",
|
|
21
|
-
"properties": {
|
|
22
|
-
"min": { "type": "integer", "minimum": 1 },
|
|
23
|
-
"max": { "type": "integer", "minimum": 1 },
|
|
24
|
-
"step": { "type": "integer", "minimum": 1 },
|
|
25
|
-
"metadata": { "type": "object", "additionalProperties": true }
|
|
26
|
-
},
|
|
27
|
-
"required": ["min", "max"],
|
|
28
|
-
"additionalProperties": false,
|
|
29
|
-
"patternProperties": {
|
|
30
|
-
"^x-": { "$ref": "../defs/common.schema.json#/properties/extensionLaneValue" }
|
|
31
|
-
}
|
|
32
|
-
},
|
|
33
|
-
"metadata": { "type": "object", "additionalProperties": true }
|
|
34
|
-
},
|
|
35
|
-
"required": ["discrete"],
|
|
36
|
-
"additionalProperties": false,
|
|
37
|
-
"patternProperties": {
|
|
38
|
-
"^x-": { "$ref": "../defs/common.schema.json#/properties/extensionLaneValue" }
|
|
39
|
-
}
|
|
40
|
-
}
|
|
41
|
-
},
|
|
42
|
-
"required": ["ingredients", "scaling"],
|
|
43
|
-
"$defs": {
|
|
44
|
-
"ingredient": {
|
|
45
|
-
"type": "object",
|
|
46
|
-
"properties": {
|
|
47
|
-
"id": { "type": "string" },
|
|
48
|
-
"name": { "type": "string" },
|
|
49
|
-
"quantity": { "$ref": "../defs/quantity.schema.json" },
|
|
50
|
-
"temperature": { "$ref": "../defs/temperature.schema.json" },
|
|
51
|
-
"notes": { "type": "string" },
|
|
52
|
-
"prep": {
|
|
53
|
-
"oneOf": [
|
|
54
|
-
{ "type": "string", "minLength": 1 },
|
|
55
|
-
{
|
|
56
|
-
"type": "array",
|
|
57
|
-
"minItems": 1,
|
|
58
|
-
"items": {
|
|
59
|
-
"anyOf": [
|
|
60
|
-
{ "type": "string", "minLength": 1 },
|
|
61
|
-
{ "$ref": "../stacks/prep.schema.json#/$defs/prepItem" }
|
|
62
|
-
]
|
|
63
|
-
}
|
|
64
|
-
}
|
|
65
|
-
]
|
|
66
|
-
},
|
|
67
|
-
"metadata": { "type": "object", "additionalProperties": true },
|
|
68
|
-
"scaling": { "$ref": "../defs/scalingRule.schema.json" }
|
|
69
|
-
},
|
|
70
|
-
"required": ["id", "name", "quantity"],
|
|
71
|
-
"additionalProperties": false,
|
|
72
|
-
"patternProperties": {
|
|
73
|
-
"^x-": { "$ref": "../defs/common.schema.json#/properties/extensionLaneValue" }
|
|
74
|
-
}
|
|
75
|
-
},
|
|
76
|
-
"ingredientSection": {
|
|
77
|
-
"type": "object",
|
|
78
|
-
"properties": {
|
|
79
|
-
"section": { "type": "string" },
|
|
80
|
-
"ingredients": {
|
|
81
|
-
"type": "array",
|
|
82
|
-
"items": {
|
|
83
|
-
"anyOf": [
|
|
84
|
-
{ "$ref": "#/$defs/ingredient" },
|
|
85
|
-
{ "$ref": "#/$defs/ingredientSection" }
|
|
86
|
-
]
|
|
87
|
-
}
|
|
88
|
-
},
|
|
89
|
-
"metadata": { "type": "object", "additionalProperties": true }
|
|
90
|
-
},
|
|
91
|
-
"required": ["section", "ingredients"],
|
|
92
|
-
"additionalProperties": false,
|
|
93
|
-
"patternProperties": {
|
|
94
|
-
"^x-": { "$ref": "../defs/common.schema.json#/properties/extensionLaneValue" }
|
|
95
|
-
}
|
|
96
|
-
}
|
|
97
|
-
},
|
|
98
|
-
"additionalProperties": false
|
|
99
|
-
}
|
|
@@ -1,238 +0,0 @@
|
|
|
1
|
-
#scaling@1
|
|
2
|
-
|
|
3
|
-
---
|
|
4
|
-
|
|
5
|
-
## Purpose
|
|
6
|
-
|
|
7
|
-
The `scaling@1` stack standardizes how ingredient quantities change when a recipe is scaled, so independent implementations produce consistent results.
|
|
8
|
-
|
|
9
|
-
This stack is intended to support the **Scalable** profile.
|
|
10
|
-
|
|
11
|
-
---
|
|
12
|
-
|
|
13
|
-
## Requirements
|
|
14
|
-
|
|
15
|
-
A document that declares `scaling@1`:
|
|
16
|
-
|
|
17
|
-
• MUST declare `quantified@1`.
|
|
18
|
-
• SHOULD declare `structured@1` (recommended for stable ingredient IDs).
|
|
19
|
-
• MUST satisfy all structural rules enabled by this stack.
|
|
20
|
-
• MUST satisfy the semantic rules described below.
|
|
21
|
-
|
|
22
|
-
---
|
|
23
|
-
|
|
24
|
-
## Data Model
|
|
25
|
-
|
|
26
|
-
When `scaling@1` is declared, quantified ingredients MAY include an optional `scaling` field.
|
|
27
|
-
|
|
28
|
-
Field
|
|
29
|
-
ingredient.scaling : ScalingRule
|
|
30
|
-
|
|
31
|
-
ScalingRule is defined in `defs/scalingRule.schema.json` and is a closed object with a required `mode`.
|
|
32
|
-
|
|
33
|
-
---
|
|
34
|
-
|
|
35
|
-
## Scaling Modes
|
|
36
|
-
|
|
37
|
-
### linear
|
|
38
|
-
|
|
39
|
-
Multiply the ingredient quantity by the scale factor.
|
|
40
|
-
|
|
41
|
-
mode = "linear"
|
|
42
|
-
|
|
43
|
-
If `scaling` is omitted, implementations MUST treat the ingredient as linear by default.
|
|
44
|
-
|
|
45
|
-
---
|
|
46
|
-
|
|
47
|
-
### fixed
|
|
48
|
-
|
|
49
|
-
Do not scale this ingredient quantity.
|
|
50
|
-
|
|
51
|
-
Examples:
|
|
52
|
-
• 1 bay leaf
|
|
53
|
-
• 1 cinnamon stick
|
|
54
|
-
• 1 pan
|
|
55
|
-
|
|
56
|
-
mode = "fixed"
|
|
57
|
-
|
|
58
|
-
---
|
|
59
|
-
|
|
60
|
-
### discrete
|
|
61
|
-
|
|
62
|
-
Scale using discrete increments (for example, whole eggs), with optional rounding and bounds.
|
|
63
|
-
|
|
64
|
-
Fields
|
|
65
|
-
• mode = "discrete"
|
|
66
|
-
• step (optional, > 0): discrete increment size
|
|
67
|
-
• rounding (optional): nearest | ceil | floor
|
|
68
|
-
• min (optional): minimum allowed result
|
|
69
|
-
• max (optional): maximum allowed result
|
|
70
|
-
|
|
71
|
-
Semantic defaults
|
|
72
|
-
• If step is not present, default is 1
|
|
73
|
-
• If rounding is not present, default is nearest
|
|
74
|
-
|
|
75
|
-
---
|
|
76
|
-
|
|
77
|
-
### toTaste
|
|
78
|
-
|
|
79
|
-
Quantity is informational and MUST NOT be mechanically scaled.
|
|
80
|
-
|
|
81
|
-
Examples:
|
|
82
|
-
• salt to taste
|
|
83
|
-
• pepper to taste
|
|
84
|
-
|
|
85
|
-
mode = "toTaste"
|
|
86
|
-
|
|
87
|
-
---
|
|
88
|
-
|
|
89
|
-
### bakersPercent
|
|
90
|
-
|
|
91
|
-
Represents the ingredient quantity as a baker’s percentage of a base ingredient (typically flour).
|
|
92
|
-
|
|
93
|
-
Fields
|
|
94
|
-
• mode = "bakersPercent"
|
|
95
|
-
• percent (> 0): percentage value (for example, 70 for 70%)
|
|
96
|
-
• of (string): ingredient id of the base ingredient
|
|
97
|
-
|
|
98
|
-
---
|
|
99
|
-
|
|
100
|
-
## Scale Factor
|
|
101
|
-
|
|
102
|
-
Scaling is applied using a scale factor F.
|
|
103
|
-
|
|
104
|
-
How F is selected depends on the consumer implementation:
|
|
105
|
-
|
|
106
|
-
• Some consumers may accept an explicit factor (for example, “2x”).
|
|
107
|
-
• Some consumers may compute F from yield changes (for example, “scale from 4 servings to 6 servings”).
|
|
108
|
-
|
|
109
|
-
This stack standardizes how ingredient rules respond to F, not how F is chosen.
|
|
110
|
-
|
|
111
|
-
---
|
|
112
|
-
|
|
113
|
-
## Semantic Validation Rules (Normative)
|
|
114
|
-
|
|
115
|
-
Validators MUST enforce the following rules:
|
|
116
|
-
|
|
117
|
-
1. Prerequisite
|
|
118
|
-
If `scaling@1` is declared, `quantified@1` MUST also be declared.
|
|
119
|
-
|
|
120
|
-
2. Baker’s percent reference
|
|
121
|
-
For mode = "bakersPercent", the `of` field MUST resolve to an existing ingredient id.
|
|
122
|
-
|
|
123
|
-
3. Discrete sanity
|
|
124
|
-
For mode = "discrete", if both `min` and `max` are present, min MUST be less than or equal to max.
|
|
125
|
-
|
|
126
|
-
4. Closed rule objects
|
|
127
|
-
ScalingRule MUST NOT contain unspecified fields. This is enforced by schema.
|
|
128
|
-
|
|
129
|
-
Consumers MUST apply the following semantic defaults:
|
|
130
|
-
|
|
131
|
-
• Missing `scaling` ⇒ treat as linear
|
|
132
|
-
• discrete.step default = 1
|
|
133
|
-
• discrete.rounding default = nearest
|
|
134
|
-
|
|
135
|
-
---
|
|
136
|
-
|
|
137
|
-
## Scaling Behavior (Normative)
|
|
138
|
-
|
|
139
|
-
Given an ingredient with base quantity amount A and scale factor F:
|
|
140
|
-
|
|
141
|
-
• linear
|
|
142
|
-
scaled amount = A × F
|
|
143
|
-
|
|
144
|
-
• fixed
|
|
145
|
-
scaled amount = A
|
|
146
|
-
|
|
147
|
-
• toTaste
|
|
148
|
-
scaled amount = A (informational only)
|
|
149
|
-
Consumers MAY hide numeric scaling UI for this item.
|
|
150
|
-
|
|
151
|
-
• discrete
|
|
152
|
-
|
|
153
|
-
1. raw = A × F
|
|
154
|
-
2. if step is present, units = raw ÷ step
|
|
155
|
-
3. apply rounding to units
|
|
156
|
-
4. scaled = units × step
|
|
157
|
-
5. clamp to min / max if present
|
|
158
|
-
|
|
159
|
-
• bakersPercent
|
|
160
|
-
|
|
161
|
-
1. let B be the amount of the ingredient referenced by `of`
|
|
162
|
-
2. scaled amount = B × (percent ÷ 100)
|
|
163
|
-
|
|
164
|
-
---
|
|
165
|
-
|
|
166
|
-
## Examples
|
|
167
|
-
|
|
168
|
-
Linear (default)
|
|
169
|
-
|
|
170
|
-
Ingredient
|
|
171
|
-
id: i_sugar
|
|
172
|
-
name: Sugar
|
|
173
|
-
quantity: 100 g
|
|
174
|
-
(no scaling field → treated as linear)
|
|
175
|
-
|
|
176
|
-
---
|
|
177
|
-
|
|
178
|
-
Fixed
|
|
179
|
-
|
|
180
|
-
Ingredient
|
|
181
|
-
id: i_bayleaf
|
|
182
|
-
name: Bay leaf
|
|
183
|
-
quantity: 1 leaf
|
|
184
|
-
scaling: mode = fixed
|
|
185
|
-
|
|
186
|
-
---
|
|
187
|
-
|
|
188
|
-
Discrete (eggs)
|
|
189
|
-
|
|
190
|
-
Ingredient
|
|
191
|
-
id: i_eggs
|
|
192
|
-
name: Eggs
|
|
193
|
-
quantity: 2 egg
|
|
194
|
-
scaling: mode = discrete, rounding = ceil
|
|
195
|
-
|
|
196
|
-
---
|
|
197
|
-
|
|
198
|
-
To taste
|
|
199
|
-
|
|
200
|
-
Ingredient
|
|
201
|
-
id: i_salt
|
|
202
|
-
name: Salt
|
|
203
|
-
quantity: 1 tsp
|
|
204
|
-
scaling: mode = toTaste
|
|
205
|
-
|
|
206
|
-
---
|
|
207
|
-
|
|
208
|
-
Baker’s percent
|
|
209
|
-
|
|
210
|
-
Ingredient A
|
|
211
|
-
id: i_flour
|
|
212
|
-
name: Flour
|
|
213
|
-
quantity: 500 g
|
|
214
|
-
|
|
215
|
-
Ingredient B
|
|
216
|
-
id: i_water
|
|
217
|
-
name: Water
|
|
218
|
-
quantity: 350 g
|
|
219
|
-
scaling: mode = bakersPercent, percent = 70, of = i_flour
|
|
220
|
-
|
|
221
|
-
---
|
|
222
|
-
|
|
223
|
-
## Fixtures
|
|
224
|
-
|
|
225
|
-
Implementations SHOULD include fixtures covering:
|
|
226
|
-
|
|
227
|
-
Valid cases
|
|
228
|
-
• linear default (no scaling rule)
|
|
229
|
-
• discrete with rounding
|
|
230
|
-
• fixed
|
|
231
|
-
• toTaste
|
|
232
|
-
• bakersPercent with resolvable `of`
|
|
233
|
-
|
|
234
|
-
Invalid cases
|
|
235
|
-
• scaling declared without quantified
|
|
236
|
-
• bakersPercent with missing or invalid `of`
|
|
237
|
-
• invalid rounding value
|
|
238
|
-
• discrete with min greater than max
|
|
@@ -1,132 +0,0 @@
|
|
|
1
|
-
{
|
|
2
|
-
"$schema": "https://json-schema.org/draft/2020-12/schema",
|
|
3
|
-
"$id": "https://soustack.spec/stacks/storage.schema.json",
|
|
4
|
-
"title": "Storage Stack",
|
|
5
|
-
"type": "object",
|
|
6
|
-
"properties": {
|
|
7
|
-
"storage": {
|
|
8
|
-
"type": "object",
|
|
9
|
-
"properties": {
|
|
10
|
-
"roomTemp": { "$ref": "#/$defs/storageMethod" },
|
|
11
|
-
"refrigerated": { "$ref": "#/$defs/storageMethod" },
|
|
12
|
-
"frozen": { "$ref": "#/$defs/storageMethod" },
|
|
13
|
-
"leftovers": { "$ref": "#/$defs/leftovers" },
|
|
14
|
-
"metadata": { "type": "object", "additionalProperties": true }
|
|
15
|
-
},
|
|
16
|
-
"anyOf": [
|
|
17
|
-
{ "required": ["roomTemp"] },
|
|
18
|
-
{ "required": ["refrigerated"] },
|
|
19
|
-
{ "required": ["frozen"] }
|
|
20
|
-
],
|
|
21
|
-
"additionalProperties": false,
|
|
22
|
-
"patternProperties": {
|
|
23
|
-
"^x-": { "$ref": "../defs/common.schema.json#/properties/extensionLaneValue" }
|
|
24
|
-
}
|
|
25
|
-
}
|
|
26
|
-
},
|
|
27
|
-
"required": ["storage"],
|
|
28
|
-
"$defs": {
|
|
29
|
-
"storageMethod": {
|
|
30
|
-
"type": "object",
|
|
31
|
-
"properties": {
|
|
32
|
-
"duration": { "$ref": "../defs/duration.schema.json#/properties/StorageDuration" },
|
|
33
|
-
"notes": { "type": "string" },
|
|
34
|
-
"metadata": { "type": "object", "additionalProperties": true }
|
|
35
|
-
},
|
|
36
|
-
"required": ["duration"],
|
|
37
|
-
"additionalProperties": false,
|
|
38
|
-
"patternProperties": {
|
|
39
|
-
"^x-": { "$ref": "../defs/common.schema.json#/properties/extensionLaneValue" }
|
|
40
|
-
}
|
|
41
|
-
},
|
|
42
|
-
"reheatDuration": {
|
|
43
|
-
"type": "object",
|
|
44
|
-
"properties": {
|
|
45
|
-
"minMinutes": { "type": "integer", "minimum": 0 },
|
|
46
|
-
"maxMinutes": { "type": "integer", "minimum": 0 }
|
|
47
|
-
},
|
|
48
|
-
"anyOf": [
|
|
49
|
-
{ "required": ["minMinutes"] },
|
|
50
|
-
{ "required": ["maxMinutes"] }
|
|
51
|
-
],
|
|
52
|
-
"additionalProperties": false,
|
|
53
|
-
"patternProperties": {
|
|
54
|
-
"^x-": { "$ref": "../defs/common.schema.json#/properties/extensionLaneValue" }
|
|
55
|
-
}
|
|
56
|
-
},
|
|
57
|
-
"reheatInstruction": {
|
|
58
|
-
"type": "object",
|
|
59
|
-
"properties": {
|
|
60
|
-
"method": { "type": "string", "minLength": 1 },
|
|
61
|
-
"temp": {
|
|
62
|
-
"type": "object",
|
|
63
|
-
"properties": {
|
|
64
|
-
"value": { "type": "number" },
|
|
65
|
-
"unit": { "type": "string", "enum": ["F", "C"] }
|
|
66
|
-
},
|
|
67
|
-
"required": ["value", "unit"],
|
|
68
|
-
"additionalProperties": false,
|
|
69
|
-
"patternProperties": {
|
|
70
|
-
"^x-": { "$ref": "../defs/common.schema.json#/properties/extensionLaneValue" }
|
|
71
|
-
}
|
|
72
|
-
},
|
|
73
|
-
"duration": { "$ref": "#/$defs/reheatDuration" },
|
|
74
|
-
"notes": { "type": "string" }
|
|
75
|
-
},
|
|
76
|
-
"required": ["method"],
|
|
77
|
-
"additionalProperties": false,
|
|
78
|
-
"patternProperties": {
|
|
79
|
-
"^x-": { "$ref": "../defs/common.schema.json#/properties/extensionLaneValue" }
|
|
80
|
-
}
|
|
81
|
-
},
|
|
82
|
-
"portioning": {
|
|
83
|
-
"type": "object",
|
|
84
|
-
"properties": {
|
|
85
|
-
"notes": { "type": "string" },
|
|
86
|
-
"recommendedPortion": {
|
|
87
|
-
"type": "object",
|
|
88
|
-
"properties": {
|
|
89
|
-
"quantity": { "type": "number" },
|
|
90
|
-
"unit": { "type": "string" }
|
|
91
|
-
},
|
|
92
|
-
"required": ["quantity", "unit"],
|
|
93
|
-
"additionalProperties": false,
|
|
94
|
-
"patternProperties": {
|
|
95
|
-
"^x-": { "$ref": "../defs/common.schema.json#/properties/extensionLaneValue" }
|
|
96
|
-
}
|
|
97
|
-
}
|
|
98
|
-
},
|
|
99
|
-
"required": ["notes"],
|
|
100
|
-
"additionalProperties": false,
|
|
101
|
-
"patternProperties": {
|
|
102
|
-
"^x-": { "$ref": "../defs/common.schema.json#/properties/extensionLaneValue" }
|
|
103
|
-
}
|
|
104
|
-
},
|
|
105
|
-
"leftovers": {
|
|
106
|
-
"type": "object",
|
|
107
|
-
"properties": {
|
|
108
|
-
"notes": { "type": "string" },
|
|
109
|
-
"reheat": {
|
|
110
|
-
"oneOf": [
|
|
111
|
-
{
|
|
112
|
-
"type": "array",
|
|
113
|
-
"items": { "type": "string" },
|
|
114
|
-
"minItems": 1
|
|
115
|
-
},
|
|
116
|
-
{
|
|
117
|
-
"type": "array",
|
|
118
|
-
"items": { "$ref": "#/$defs/reheatInstruction" },
|
|
119
|
-
"minItems": 1
|
|
120
|
-
}
|
|
121
|
-
]
|
|
122
|
-
},
|
|
123
|
-
"portioning": { "$ref": "#/$defs/portioning" }
|
|
124
|
-
},
|
|
125
|
-
"additionalProperties": false,
|
|
126
|
-
"patternProperties": {
|
|
127
|
-
"^x-": { "$ref": "../defs/common.schema.json#/properties/extensionLaneValue" }
|
|
128
|
-
}
|
|
129
|
-
}
|
|
130
|
-
},
|
|
131
|
-
"additionalProperties": false
|
|
132
|
-
}
|
|
@@ -1,48 +0,0 @@
|
|
|
1
|
-
{
|
|
2
|
-
"$schema": "https://json-schema.org/draft/2020-12/schema",
|
|
3
|
-
"$id": "https://soustack.spec/stacks/structured.schema.json",
|
|
4
|
-
"title": "Structured Stack",
|
|
5
|
-
"type": "object",
|
|
6
|
-
"properties": {
|
|
7
|
-
"instructions": {
|
|
8
|
-
"type": "array",
|
|
9
|
-
"items": {
|
|
10
|
-
"anyOf": [
|
|
11
|
-
{ "$ref": "#/$defs/step" },
|
|
12
|
-
{ "$ref": "#/$defs/stepSection" }
|
|
13
|
-
]
|
|
14
|
-
}
|
|
15
|
-
}
|
|
16
|
-
},
|
|
17
|
-
"required": ["instructions"],
|
|
18
|
-
"$defs": {
|
|
19
|
-
"step": {
|
|
20
|
-
"allOf": [
|
|
21
|
-
{ "$ref": "../defs/entities.schema.json#/$defs/StepBase" },
|
|
22
|
-
{ "required": ["id"] }
|
|
23
|
-
]
|
|
24
|
-
},
|
|
25
|
-
"stepSection": {
|
|
26
|
-
"type": "object",
|
|
27
|
-
"properties": {
|
|
28
|
-
"section": { "type": "string" },
|
|
29
|
-
"steps": {
|
|
30
|
-
"type": "array",
|
|
31
|
-
"items": {
|
|
32
|
-
"anyOf": [
|
|
33
|
-
{ "$ref": "#/$defs/step" },
|
|
34
|
-
{ "$ref": "#/$defs/stepSection" }
|
|
35
|
-
]
|
|
36
|
-
}
|
|
37
|
-
},
|
|
38
|
-
"metadata": { "type": "object", "additionalProperties": true }
|
|
39
|
-
},
|
|
40
|
-
"required": ["section", "steps"],
|
|
41
|
-
"additionalProperties": false,
|
|
42
|
-
"patternProperties": {
|
|
43
|
-
"^x-": { "$ref": "../defs/common.schema.json#/properties/extensionLaneValue" }
|
|
44
|
-
}
|
|
45
|
-
}
|
|
46
|
-
},
|
|
47
|
-
"additionalProperties": false
|
|
48
|
-
}
|
|
@@ -1,24 +0,0 @@
|
|
|
1
|
-
# substitutions@1
|
|
2
|
-
|
|
3
|
-
## Purpose
|
|
4
|
-
The `substitutions@1` stack enables recipes to declare ingredient substitutions with alternatives and ratios.
|
|
5
|
-
|
|
6
|
-
## Adds
|
|
7
|
-
- Top-level `substitutions` array with substitution objects.
|
|
8
|
-
- Each substitution includes `for` (ingredient ID) and `alternatives` array.
|
|
9
|
-
- Each alternative includes `name` and `ratio` fields.
|
|
10
|
-
|
|
11
|
-
## Requires
|
|
12
|
-
- `referenced@1`
|
|
13
|
-
|
|
14
|
-
## Semantics
|
|
15
|
-
- MUST: The `for` field must reference an ingredient ID that exists in the ingredients array.
|
|
16
|
-
- MUST: Each substitution must include at least one alternative.
|
|
17
|
-
- NOTE: This stack requires `referenced@1` to ensure ingredient IDs are available for reference.
|
|
18
|
-
|
|
19
|
-
## Composition Notes
|
|
20
|
-
- This stack is monotonic: it adds requirements or fields without removing expressiveness.
|
|
21
|
-
- Interaction: Requires `referenced@1` for ingredient ID resolution. Substitutions are informational and do not affect recipe structure.
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
package/src/stacks/timed@1 3.md
DELETED
|
@@ -1,23 +0,0 @@
|
|
|
1
|
-
# timed@1
|
|
2
|
-
|
|
3
|
-
## Purpose
|
|
4
|
-
The `timed@1` stack enables recipes to include timing information for steps, supporting scheduling and time-based planning.
|
|
5
|
-
|
|
6
|
-
## Adds
|
|
7
|
-
- Step objects must include a `timing` field with `activity` (active/passive) and either `duration` or `completionCue`.
|
|
8
|
-
- Timing may include duration ranges (minMinutes/maxMinutes) or completion cues.
|
|
9
|
-
|
|
10
|
-
## Requires
|
|
11
|
-
- `structured@1`
|
|
12
|
-
|
|
13
|
-
## Semantics
|
|
14
|
-
- MUST: Each step must include a `timing` object with `activity` field.
|
|
15
|
-
- MUST: Each timing object must include either `duration` or `completionCue`.
|
|
16
|
-
- NOTE: This stack implies `structured@1` (steps are objects with IDs).
|
|
17
|
-
|
|
18
|
-
## Composition Notes
|
|
19
|
-
- This stack is monotonic: it adds requirements or fields without removing expressiveness.
|
|
20
|
-
- Interaction: Required by `compute@1` for deterministic scheduling. Works with `quantified@1` for computational recipe planning.
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|