@supersuit/hyperspec 0.4.0 → 0.6.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.
Files changed (35) hide show
  1. package/CHANGELOG.md +143 -0
  2. package/README.md +50 -2
  3. package/SPEC.md +5 -5
  4. package/WRITING.md +510 -17
  5. package/bin/hyperspec.mjs +177 -2
  6. package/examples/writing/dna/essay-new-managers-teach/features.json +56 -0
  7. package/examples/writing/dna/essay-new-managers-teach/goldens/README.md +14 -0
  8. package/examples/writing/dna/essay-new-managers-teach/goldens/close.md +9 -0
  9. package/examples/writing/dna/essay-new-managers-teach/goldens/opening.md +9 -0
  10. package/examples/writing/dna/essay-new-managers-teach/goldens/status.md +10 -0
  11. package/examples/writing/dna/essay-new-managers-teach/scope.md +11 -0
  12. package/examples/writing/essay/claims.jsonl +9 -0
  13. package/examples/writing/essay/draft.md +82 -0
  14. package/examples/writing/essay/materials/interview-notes.md.segments.jsonl +2 -2
  15. package/examples/writing/essay.hyperspec.md +32 -13
  16. package/examples/writing/story/claims.jsonl +9 -0
  17. package/examples/writing/story/draft.md +267 -0
  18. package/examples/writing/story.hyperspec.md +20 -5
  19. package/package.json +1 -1
  20. package/src/check.mjs +245 -0
  21. package/src/dna.mjs +474 -0
  22. package/src/stations/claims.mjs +150 -0
  23. package/src/stations/dna.mjs +126 -0
  24. package/src/stations/form.mjs +115 -0
  25. package/src/stations/index.mjs +27 -0
  26. package/src/stations/links.mjs +275 -0
  27. package/src/stations/private.mjs +117 -0
  28. package/src/stations/quotes.mjs +168 -0
  29. package/src/stations/terms.mjs +131 -0
  30. package/src/stations/util.mjs +99 -0
  31. package/src/writing-exports.mjs +8 -3
  32. package/src/writing-fields.mjs +197 -1
  33. package/src/writing-template.mjs +8 -0
  34. package/examples/writing/essay/goldens/close.md +0 -2
  35. package/examples/writing/essay/goldens/opening.md +0 -2
package/CHANGELOG.md CHANGED
@@ -1,5 +1,148 @@
1
1
  # Changelog
2
2
 
3
+ ## 0.6.0 (2026-09-29)
4
+
5
+ A spec can now check a draft. Until this release hyperspec could tell you whether a writing spec
6
+ was ready; it could not tell you whether the piece written from it met the spec. `hyperspec
7
+ check` runs seven deterministic stations against a draft: the length and required parts, the
8
+ terms the reader needs defined, the claims ledger, quotations, private material, the writer's
9
+ measured style, and links. None of them calls a model or touches the network, so the same draft
10
+ and spec always give the same answer. Each run leaves one line in the spec's runs ledger saying
11
+ whether the draft passed first time, improved, or did not, and why.
12
+
13
+ **No behavior change for lint.** Every spec without `writing.audience.terms` passes and fails
14
+ exactly as it did in 0.5.0, and no lint finding id changed. That field is the one schema
15
+ addition, and it is optional.
16
+
17
+ - `hyperspec check <spec> --draft <file> [--json] [--only a,b]` needs a writing spec
18
+ (`profile: writing`) and lints it first: a spec that fails lint, or is blocked on an open
19
+ decision, runs no station and exits with lint's code. Then it runs every station in a fixed
20
+ order, `form, terms, claims, quotes, private, dna, links`, and prints each one's `pass`, `fail`
21
+ with findings, or `skip` with the reason. Warnings print under their station and never fail it.
22
+ Exit 0 when every station that ran passed, 1 when one failed, 2 on usage (no spec, no
23
+ `--draft`, an unreadable draft, a spec without the writing profile, an `--only` that names no
24
+ known station); under `--json` a usage error is one document, `{ spec, draft, error }`.
25
+ `--only` runs a subset, still in the fixed order. A finding names the draft line it points at
26
+ where there is one, quotes at most 80 characters of the draft, and never prints an absolute
27
+ path. A byte order mark at the start of the draft is ignored. A station that throws becomes one
28
+ failing finding, `station-<name>-crashed`, and the rest still run.
29
+ - `form`: word count against `writing.form.length` (only `unit: words` is measured; another unit
30
+ skips the station), and every `required_parts` entry present as an ATX heading of that text
31
+ (indented up to three spaces, closing `#`s allowed), or as a line starting `part:`, outside
32
+ code blocks.
33
+ - `terms`: every term in the new optional `writing.audience.terms`, other than those in `knows`,
34
+ is defined at its first appearance: in that sentence or the next, the term followed within six
35
+ words by `is`, `means`, `refers to` or a colon, or by a parenthesis. A mechanical proxy for a
36
+ definition, and documented as one. No `terms` list: skip.
37
+ - `claims`: every claim in the JSONL ledger at `writing.sources.ledger` (`text`, `source`,
38
+ optional `span`) still appears in the draft word for word, and has a real source; unsourced
39
+ claims warn instead under `unsourced_claim: warn`, and point at the draft line where the claim
40
+ appears. A missing ledger fails. The ledger is the list of claims: the station does not decide
41
+ what counts as one.
42
+ - `quotes`: every double-quoted span of four words or more appears word for word in a `quote` or
43
+ `story` segment of a marked material, never a private one; when the sentence names a quote
44
+ segment's speaker, by the full name or by its first word (when that word has two or more
45
+ letters and is not a common function word such as "the"), the span must come from that
46
+ speaker. A spec with `fiction: true` skips the station, since a character's dialogue is
47
+ invented rather than quoted.
48
+ - `private`: no run of eight words from a `private` segment appears in the draft. Segments of four
49
+ to seven words are checked whole; shorter ones are counted in one warning and never quoted.
50
+ - `dna`: with a current `writing.dna.scope_dir`, the draft is measured the way goldens are and
51
+ each feature compared with the scope's, from v ÷ 1.5 to the larger of v × 1.5 and v + 5; drift,
52
+ and an em dash where the scope has none (pointing at the first one), are warnings. No scope
53
+ folder: skip.
54
+ - `links`: inline, reference and bare links are well-formed http, https or mailto (a bare
55
+ `https://` with no host fails); relative links resolve to a file beside the draft; a `/` link
56
+ warns, since there is no site root to resolve it against; a full or collapsed reference needs
57
+ its definition. A bare `[label]` is a link only when its label is defined, so `[sic]`, `[x]` and
58
+ `[1]` are text. No network access.
59
+ - The runs ledger: each check appends `{ at, kind: "check", draft, draft_sha256, spec_sha256,
60
+ stations, verdict }` to `improvement.ledger`, with `draft` relative to the spec's folder. A run
61
+ with `--only` adds `partial: true`, is `not-improved` with the reason `partial run: <stations>`,
62
+ and is ignored by later verdicts. A full run is compared with the last full line for the same
63
+ draft: `one-shot` when there is none and every station passes; `improved` when that line failed
64
+ and every station passes now, with `change` naming exactly the stations that failed then and
65
+ pass now; otherwise `not-improved`, with a `reason` that says which case it is, such as "no
66
+ change since the last passing check" or "spec changed; failing stations: terms". The lines keep
67
+ lint's test 9 passing.
68
+ - New optional field `writing.audience.terms`: a list of real strings when present (test 1,
69
+ `writing-audience-terms`, naming a scalar that is not a list, an empty list, and each entry that
70
+ is not a string, is empty or is a placeholder). Absent, nothing changes.
71
+ - The worked examples each ship a draft that passes every station, with its claims ledger:
72
+ `examples/writing/essay/draft.md` and `examples/writing/story/draft.md`. Both specs now list
73
+ `audience.terms`, and their `required_parts` are the drafts' headings. The essay's interview
74
+ quotes now name their speaker `dana, an engineering manager`, and the essay links the survey
75
+ summary it cites. A test runs `check` on both. WRITING.md gains "Checking a draft": each station, what it cannot check, a findings table
76
+ per station held by a test to the ids the stations raise, and the ledger line.
77
+
78
+ ## 0.5.0 (2026-09-29)
79
+
80
+ Scoped writer DNA. A writer does not have one voice: the same person writes differently for a
81
+ theology journal and a landing page. So a writer's voice is now kept per scope, a form, an
82
+ audience and a purpose, as a folder of goldens: real passages a person approved, each with a note
83
+ on the move it teaches and where it came from. A golden feeds only work that shares its scope, so
84
+ a passage that is right for one kind of writing never teaches its moves to another. hyperspec
85
+ measures each scope's style from its goldens (sentence and paragraph length, punctuation,
86
+ pronouns, signature words), counts and never judges, and still calls no model.
87
+
88
+ **No behavior change for existing specs.** Scoped DNA is opt-in through a new optional field,
89
+ `writing.dna.scope_dir`. A spec without it passes and fails exactly as it did in 0.4.0, and no
90
+ finding id changed: every id below is new.
91
+
92
+ - `hyperspec dna init <scope-dir> --writer W --form F --audience A --purpose P` writes a scope
93
+ folder: `scope.md` (writer, form, audience, purpose, optional notes) and a `goldens/` folder
94
+ holding a README on the golden file shape. It refuses to overwrite an existing `scope.md`, never
95
+ replaces a `goldens/README.md` that is already there, and
96
+ exits 2 with a plain message on a missing flag, a flag whose value is a placeholder, or a
97
+ parent folder that does not exist. Paths print as you gave them.
98
+ - A golden is one `.md` file directly in `goldens/`, other than `README.md`: frontmatter `why`
99
+ (the move it teaches), `approved_by` (a person; an approver starting `agent:` is refused,
100
+ because golden means a human approved it) and `source` are required, `approved_on` is optional,
101
+ and the body is the passage, verbatim. A `goldens/` folder that resolves outside its scope, such
102
+ as a symlink to another scope's goldens, is refused under test 5, naming where it leads.
103
+ - `hyperspec dna measure <scope-dir> [--json]` checks every golden and writes
104
+ `<scope-dir>/features.json`: the scope, each golden's path and SHA-256, and the features. If
105
+ the scope or any golden fails a check it writes nothing and exits 1, so a hollow or borrowed
106
+ golden is never measured in. The same goldens always produce the same bytes.
107
+ - The features: word count; sentence length in words (mean, median, 90th percentile); paragraph
108
+ length in sentences and in words; per-1000-word rates of commas, semicolons, colons, em dashes,
109
+ en dashes, exclamation marks, question marks, parentheses and quotation marks; contraction,
110
+ first-person singular, first-person plural and second-person rates; mean word length; and up to
111
+ 15 signature words. Sentences and paragraphs are split the same way `segments init` splits
112
+ them.
113
+ - With `writing.dna.scope_dir`, `lint` checks that `scope.md` matches the spec's writer, form,
114
+ audience and purpose (test 1); that every golden the spec lists is one of the scope's goldens,
115
+ after following any symlink, and never a passage in a subfolder, in `README.md` or in another
116
+ kind of file (test 5); that every golden in the folder has its `why` (test 6), a person's
117
+ approval and a source (test 4) and a passage (test 1); and that `features.json` is exactly what
118
+ `dna measure` would write now (test 6). The stale finding names what differs: goldens added,
119
+ removed or changed, a changed `scope.md` field, an unknown format version, or a number edited by
120
+ hand. A `scope_dir` that is present but a placeholder fails test 1.
121
+ - New finding ids, all starting `writing-dna-`: `scope-dir`, `scope-missing`,
122
+ `scope-file-<field>` (a scope's `scope.md` lacks writer, form, audience or purpose),
123
+ `scope-mismatch-<field>`, `goldens-missing`, `goldens-empty`, `goldens-outside`,
124
+ `golden-unreadable`, `golden-frontmatter`, `golden-empty`, `golden-approved-by`,
125
+ `golden-approved-by-agent`, `golden-source`, `golden-leak`, `golden-why`, `features-missing` and
126
+ `features-stale`. Every existing id is unchanged; in particular a spec whose own
127
+ `writing.dna.scope` lacks a field still reports `writing-dna-scope-form` (and `-audience`,
128
+ `-purpose`) as in 0.4.0. Every message names the scope folder as the spec wrote it and the
129
+ golden by its path inside the folder, never a folder on your machine. WRITING.md lists every one
130
+ with its test.
131
+ - `hyperspec init --profile writing` shows `scope_dir: TODO` in the `dna` block, which fails until
132
+ it names a scope folder or is deleted.
133
+ - Two new exports from `@supersuit/hyperspec/writing`: `readScope`, which reads a scope folder
134
+ and returns `{ scope, goldens, findings }` without throwing, and `measureFeatures`, which takes
135
+ an array of passages and returns the features `dna measure` writes.
136
+ - The essay example takes its voice from a scope folder,
137
+ `examples/writing/dna/essay-new-managers-teach/`, with three goldens and a measured
138
+ `features.json`. Its two goldens moved there from `examples/writing/essay/goldens/`, and a third
139
+ was added. The story example keeps its goldens in the spec with no scope folder, and still
140
+ passes.
141
+ - WRITING.md gains a Scoped DNA section: why a writer's DNA is scoped, the folder shape, the
142
+ golden file, both commands with their output, what each feature measures and what it is for,
143
+ staleness, naming a scope in a spec, every finding with its test, and the exports. README and
144
+ SPEC.md point at it.
145
+
3
146
  ## 0.4.0 (2026-09-29)
4
147
 
5
148
  Marking materials. Before a writing spec can pass, every material it draws on (a brain dump, a
package/README.md CHANGED
@@ -26,13 +26,16 @@ improvement ledger. Every test is defined in [SPEC.md](SPEC.md).
26
26
  | `hyperspec init <file> [--title T] [--kind K]` | Write a new hyperspec skeleton. Refuses to overwrite an existing file. |
27
27
  | `hyperspec init <file> --profile writing [--title T] [--form F] [--fiction]` | Write a writing-spec skeleton, every block shown with placeholders. |
28
28
  | `hyperspec segments init <material> --id <mid> [--out F] [--by paragraph\|sentence]` | Split a material into segments to label. Refuses to overwrite an existing file. |
29
+ | `hyperspec dna init <scope-dir> --writer W --form F --audience A --purpose P` | Start a writer-DNA scope folder. Refuses to overwrite an existing `scope.md`. |
30
+ | `hyperspec dna measure <scope-dir>` | Check every golden in a scope and write its measured features. |
31
+ | `hyperspec check <spec> --draft <file> [--only a,b]` | Run a writing spec's deterministic stations against a draft. |
29
32
  | `hyperspec recipe check <output-or-recipe>` | Check that a recipe records everything the standard asks for. |
30
33
  | `hyperspec recipe approve <recipe> --by <slug>` | Record who approved the output. |
31
34
  | `hyperspec reproduce <recipe> [--restore]` | Re-check every hash the recipe recorded. Never runs a model. |
32
35
  | `hyperspec regenerate <recipe> --out <path> --clicker <slug> <one change> [--run cmd]` | Make a child recipe from a parent and one named change, rerunning only the stages it reaches. |
33
36
  | `hyperspec compare <child-recipe> --doctor cmd` | Grade a child and its parent through one doctor against one spec. |
34
37
 
35
- Every command except `init` and `segments init` takes `--json`. `hyperspec --help` prints every flag.
38
+ Every command except `init`, `segments init` and `dna init` takes `--json`. `hyperspec --help` prints every flag.
36
39
 
37
40
  ## Exit codes
38
41
 
@@ -40,6 +43,10 @@ Every command except `init` and `segments init` takes `--json`. `hyperspec --hel
40
43
  test fails, 3 when every test passes but a decision is still open (blocked), and 2 on a usage
41
44
  error or a file that cannot be read, has broken frontmatter, or is not a hyperspec.
42
45
 
46
+ `hyperspec check` exits 0 when every station it ran passed, 1 when one failed, and 2 on a usage
47
+ error, a draft that cannot be read, or a spec without `profile: writing`. A spec that is not ready to check against exits with lint's
48
+ own code, 1 or 3, and no station runs.
49
+
43
50
  The recipe commands use the same numbers: 0 ok, 1 a check failed or the child regressed, 2
44
51
  usage or unreadable input, 3 pending, when `regenerate` has stages waiting for a runner.
45
52
  `regenerate` also exits 1 when a stage it ran reported a failing verdict, or none. A stage it
@@ -130,7 +137,7 @@ The skeleton fails until every placeholder is real and its four open questions (
130
137
  who speaks, who reads, what changes) are answered. The blocks, every field, and which test
131
138
  each rule reports under are in [WRITING.md](WRITING.md). Two complete specs that pass with
132
139
  nothing to warn ship in `examples/writing/`: an essay for new managers, and a short story with
133
- two characters whose voices a judge can tell apart.
140
+ two characters whose voices a judge can tell apart. Each comes with a draft written to it.
134
141
 
135
142
  ### Marking materials
136
143
 
@@ -152,6 +159,47 @@ segment. The file format, the labels, and every finding are in
152
159
  [WRITING.md](WRITING.md#marking-materials). A tool of your own can run the same check with
153
160
  `import { readSegments } from "@supersuit/hyperspec/writing"`.
154
161
 
162
+ ### Scoped DNA
163
+
164
+ A writer sounds different in a theology essay and on a landing page, so a writer's voice is kept
165
+ per scope (a form, an audience and a purpose), and each scope is a folder of goldens. A golden is a real
166
+ passage a person approved, with a note on the move it teaches and where it came from, and it
167
+ feeds only work that shares its scope.
168
+
169
+ ```bash
170
+ mkdir -p dna
171
+ npx @supersuit/hyperspec dna init dna/essay-new-managers-teach --writer example-author --form essay --audience "new managers" --purpose teach
172
+ npx @supersuit/hyperspec dna measure dna/essay-new-managers-teach
173
+ ```
174
+
175
+ `dna init` writes `scope.md` and a `goldens/` folder holding only a README. Add one file per
176
+ golden, then `dna measure` checks each golden and writes `features.json`: sentence and paragraph length, punctuation,
177
+ pronouns and signature words, measured and never judged. Name the folder in a spec as
178
+ `writing.dna.scope_dir` and `lint` checks that the scope matches the spec, that no golden comes
179
+ from another scope, and that the measurements are current. Without `scope_dir`, a spec lints as
180
+ it did in 0.4. The folder shape, every feature, and every finding are in
181
+ [WRITING.md](WRITING.md#scoped-dna); `readScope` and `measureFeatures` are exported from
182
+ `@supersuit/hyperspec/writing`.
183
+
184
+ ### Checking a draft
185
+
186
+ Once a draft exists, `check` holds it to its spec with seven stations, none of which calls a
187
+ model or touches the network: `form` (length and required parts), `terms` (every word in the new
188
+ optional `writing.audience.terms` is defined where it first appears), `claims` (the claims
189
+ ledger still matches the draft, and every claim has a source), `quotes` (in nonfiction, every quotation of four
190
+ words or more is word for word in a marked quote), `private` (no run of eight words from a
191
+ private segment), `dna` (the draft's measured style beside its scope's, as warnings) and `links`
192
+ (well-formed, and relative links resolve).
193
+
194
+ ```bash
195
+ npx @supersuit/hyperspec check essay.hyperspec.md --draft essay/draft.md
196
+ ```
197
+
198
+ It lints the spec first, prints each station's pass, fail or skip, and appends one line to the
199
+ spec's runs ledger with a verdict. Both examples ship a draft that passes. What each station
200
+ checks and cannot check, and every finding, are in
201
+ [WRITING.md](WRITING.md#checking-a-draft).
202
+
155
203
  ## The format
156
204
 
157
205
  A hyperspec is a markdown file with a YAML frontmatter block: `decisions`, `requirements`,
package/SPEC.md CHANGED
@@ -97,7 +97,7 @@ examples:
97
97
  - path: examples/minimal.hyperspec.md
98
98
  why: the smallest spec that passes all nine tests
99
99
  resume:
100
- next_action: collect adopter issues on 0.4, materials marking included, and cut 0.5 from them
100
+ next_action: collect adopter issues on 0.6, the check command included, and cut 0.7 from them
101
101
  feedback:
102
102
  issues: https://github.com/SupersuitUp/hyperspec/issues
103
103
  fork: MIT; fork it for your own purposes and say so in your SPEC
@@ -109,7 +109,7 @@ improvement:
109
109
 
110
110
  A person writing for another person leaves most of the specification unsaid, because the other person fills the gaps from shared context. An agent has none of that context, so it fills every gap with the average, and the average is what reads as middling. Hyperspecification is writing down the gaps. It is a level of detail that would feel like overkill between two people and is exactly enough for an agent: every decision the agent would otherwise guess is either decided, delegated with the rule for deciding it, or marked open, so the work stops instead of guessing.
111
111
 
112
- **Version 0.4.0** (2026-09-29)
112
+ **Version 0.6.0** (2026-09-29)
113
113
 
114
114
  ## What makes a spec a hyperspec
115
115
 
@@ -220,7 +220,7 @@ Each row lists every condition under which `hyperspec lint` fails that test. A w
220
220
  | 8 its adopters can push back on it | `feedback.issues` or `feedback.fork` missing |
221
221
  | 9 it improves itself | `improvement.ledger` missing; a ledger path that exists and is not a readable file; if the ledger file exists, a line that is not a JSON object, a `verdict` outside one-shot, improved or not-improved, `improved` without `change`, `not-improved` without `reason`. A declared ledger that does not exist yet is a warning |
222
222
 
223
- A profile adds its own conditions to these rows. The writing profile's are in [WRITING.md](WRITING.md#the-test-mapping), including the checks on each material's segments file: every material marked, every segment labeled from the closed set and matching its material word for word, the marking current (tests 1 and 4), and no spine claim citing a private or question segment (test 5).
223
+ A profile adds its own conditions to these rows. The writing profile's are in [WRITING.md](WRITING.md#the-test-mapping), including the checks on each material's segments file: every material marked, every segment labeled from the closed set and matching its material word for word, the marking current (tests 1 and 4), and no spine claim citing a private or question segment (test 5). A writing spec that names a writer-DNA scope folder with `writing.dna.scope_dir` is also checked against it: the scope matches the spec (test 1), every golden has a person's approval and a source (test 4), no golden comes from outside the scope (test 5), and every golden has its why and the scope's measured features are current (test 6). `scope_dir` is optional, and without it nothing changes. The writing profile also has an optional `writing.audience.terms`, the words a piece uses that its reader may not know: when present it must list real terms (test 1), and `hyperspec check` reads it to require each term's definition where the draft first uses it. Without it nothing changes.
224
224
 
225
225
  ## Exit codes
226
226
 
@@ -236,7 +236,7 @@ A profile adds its own conditions to these rows. The writing profile's are in [W
236
236
  Every run of a skill that works from a hyperspec writes one line to the ledger named in `improvement.ledger`, one JSON object per line, with a `verdict`:
237
237
 
238
238
  - **one-shot**: no intervention, nothing to learn.
239
- - **improved**: the skill, the spec template, or a component library changed, and the line carries `change`, naming what changed.
239
+ - **improved**: the skill, the spec template, or a component library changed, and the line carries `change`, naming what changed. On a `kind: "check"` line, which `hyperspec check` writes, it means the draft now passes every station after the last full check of it failed, and `change` names those stations.
240
240
  - **not-improved**: nothing changed, and the line carries `reason`, a reason a later session can argue with, such as "the correction was about this piece only" or "the fix belongs to a shipped skill and was filed as an issue".
241
241
 
242
242
  Silence is not a verdict. A run that learned nothing has to say so and why, and a ledger line with none of the three verdicts fails the ninth test.
@@ -245,7 +245,7 @@ Silence is not a verdict. A run that learned nothing has to say so and why, and
245
245
 
246
246
  A profile adds the rules for one kind of work on top of the nine tests. A spec opts in with a top-level `profile:` naming it. A profile never adds a tenth test: every finding it raises reports under one of the nine, with an id that starts with the profile's name, and the score stays out of nine. `lint` prints one more line for a profiled spec, how many of the profile's blocks are complete. A `profile` this linter does not know is a warning under test 7, and none of its rules are checked.
247
247
 
248
- One profile ships: `writing`, for essays, chapters, letters, stories and anything else an agent drafts for a person to read. Its blocks, its fields, which test each rule reports under, `hyperspec init --profile writing`, and marking materials with `hyperspec segments init` are in [WRITING.md](WRITING.md).
248
+ One profile ships: `writing`, for essays, chapters, letters, stories and anything else an agent drafts for a person to read. Its blocks, its fields, which test each rule reports under, `hyperspec init --profile writing`, marking materials with `hyperspec segments init`, and scoped writer DNA with `hyperspec dna init` and `hyperspec dna measure` are in [WRITING.md](WRITING.md).
249
249
 
250
250
  ## Recipes
251
251