@supersuit/hyperspec 0.8.0 → 0.9.1

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 (37) hide show
  1. package/CHANGELOG.md +83 -0
  2. package/README.md +46 -5
  3. package/SPEC.md +2 -2
  4. package/WRITING.md +369 -25
  5. package/bin/hyperspec.mjs +185 -13
  6. package/examples/writing/course/part-1.md +17 -0
  7. package/examples/writing/course/part-2.md +17 -0
  8. package/examples/writing/course.hyperspec.md +1 -0
  9. package/examples/writing/essay/judge/panel-buyer.packet.json +118 -0
  10. package/examples/writing/essay/judge/panel-expert.packet.json +114 -0
  11. package/examples/writing/essay/judge/panel-novice.packet.json +114 -0
  12. package/examples/writing/essay/judge/panel-skeptic.packet.json +114 -0
  13. package/examples/writing/essay/sample-verdicts/panel-buyer.verdict.json +11 -0
  14. package/examples/writing/essay/sample-verdicts/panel-expert.verdict.json +13 -0
  15. package/examples/writing/essay/sample-verdicts/panel-novice.verdict.json +11 -0
  16. package/examples/writing/essay/sample-verdicts/panel-skeptic.verdict.json +13 -0
  17. package/examples/writing/story/judge/panel-buyer.packet.json +118 -0
  18. package/examples/writing/story/judge/panel-expert.packet.json +114 -0
  19. package/examples/writing/story/judge/panel-novice.packet.json +114 -0
  20. package/examples/writing/story/judge/panel-skeptic.packet.json +114 -0
  21. package/examples/writing/story/sample-verdicts/panel-buyer.verdict.json +13 -0
  22. package/examples/writing/story/sample-verdicts/panel-expert.verdict.json +11 -0
  23. package/examples/writing/story/sample-verdicts/panel-novice.verdict.json +11 -0
  24. package/examples/writing/story/sample-verdicts/panel-skeptic.verdict.json +11 -0
  25. package/package.json +1 -1
  26. package/src/evidence.mjs +74 -0
  27. package/src/judge.mjs +50 -66
  28. package/src/judges/index.mjs +7 -1
  29. package/src/judges/panel.mjs +135 -0
  30. package/src/segments.mjs +44 -0
  31. package/src/stations/index.mjs +3 -1
  32. package/src/stations/quotes.mjs +26 -2
  33. package/src/stations/sequence.mjs +149 -2
  34. package/src/stations/triage.mjs +20 -0
  35. package/src/triage.mjs +401 -0
  36. package/src/writing-fields.mjs +48 -1
  37. package/src/writing.mjs +5 -1
package/CHANGELOG.md CHANGED
@@ -1,5 +1,88 @@
1
1
  # Changelog
2
2
 
3
+ ## 0.9.1 (2026-09-30)
4
+
5
+ Two fixes, each found by the first book written as a sequence.
6
+
7
+ - The sequence station no longer reads a word inside a longer defined term as a use of the shorter
8
+ one. A book that defines "Thinking level" in Lesson 2 and "Level" in Lesson 22 failed
9
+ `station-sequence-used-before-defined` and `station-sequence-quiz-used-before-defined` at every
10
+ "thinking level" before Lesson 22. Before it looks for a term, the station now blanks every other
11
+ defined term that contains it as a whole-word phrase, a trailing plural "s" included, in the order
12
+ guard, the quiz order guard and quiz coverage. The shorter term on its own still counts, and the
13
+ longer term no longer tests the shorter one in a quiz. `longerTerms` and `maskLonger` are exported
14
+ from the station.
15
+ - `hyperspec segments init <material> --id <mid> --keep <old>` re-marks an edited material without
16
+ relabeling what did not change (issue #2). Every segment whose text, trimmed, a segment in `<old>`
17
+ has keeps that segment's id, label and every other key (`own`, `source`, `teller`, `speaker`, and
18
+ anything added by hand), with new offsets; the rest start `unlabeled` with the next unused
19
+ `s<n>` id, and the command lists them. The id is carried so the spine's citations keep pointing
20
+ at the same words. `--keep` may name the file being written, so a material is re-marked in place;
21
+ otherwise an existing file is still never overwritten. It exits 2 for a `--keep` file that cannot
22
+ be read, has a line that is not a JSON object, or marks another material. `carrySegments` in
23
+ `src/segments.mjs` is the logic.
24
+
25
+ **Behavior change:** a sequential work that defines a term inside a longer one may now pass where
26
+ 0.9.0 failed it, and a quiz whose only question on a shorter term used it inside the longer term now
27
+ fails `station-sequence-quiz-untested` for that term. No finding id changed.
28
+
29
+ ## 0.9.0 (2026-09-29)
30
+
31
+ The pressure test a draft gets before it ships is now part of the run, and so is answering it.
32
+ Several readers reading a draft through their own lenses, and someone deciding what to do about
33
+ each thing they said, was done by hand twice, once for a letter and once for a book outline, and
34
+ both times the review read a stale, truncated copy and none of its readers was the person the
35
+ piece was for. The `panel` judge fixes both by construction, and `hyperspec triage` holds every
36
+ finding, the panel's or an outside review's, to an answer that `check` keeps true against the draft.
37
+
38
+ - The `panel` judgment station, the seventh, run last. One packet per reader,
39
+ `panel-<reader>.packet.json`: the readers the new optional `writing.panel` lists (`id`, `who`,
40
+ `knows`, `lens`), or by default a `skeptic`, a `novice` and an `expert`, and always the audience's
41
+ own reader, added last as `buyer`. It applies when the reader station does (a written audience
42
+ with a rubric). The verdict is `{ good, improve, missing, remove }`, every item `{ evidence, note }`
43
+ under the evidence rule, so a review of any copy but the current draft is refused. The panel
44
+ never fails a draft: improve, missing and remove items are warnings (`judge-panel-improve`,
45
+ `-missing`, `-remove`) and go to the triage file. A panel ledger line carries `reader`, and each
46
+ reader's history is its own. Lint refuses a panel that is not a list of readers, a reader with no
47
+ id, who or lens, an id that is not a slug, used twice or `buyer`, and a hollow `knows`
48
+ (`writing-panel`, `-reader`, `-id`, `-id-duplicate`, `-buyer`, `-who`, `-lens`, `-knows`, test 1).
49
+ - `triage.jsonl`, beside the runs ledger: one finding per line, `{ finding_id, source, reader, kind,
50
+ text, evidence, draft_sha256, disposition, answer }`, each once (the id hashes what it says).
51
+ - The `triage` station, run last in `check`: fails on an unanswered finding
52
+ (`station-triage-untriaged`), a `taken` or `already-true` answer whose evidence is missing or not
53
+ in the draft (`-evidence-missing`, `-evidence-not-found`), a `kept` answer with no reason
54
+ (`-reason-missing`), a disposition outside the four (`-disposition`) or a line that is not a
55
+ finding (`-unreadable`); warns on an `open` finding (`-open`) and on a kept or open answer about a
56
+ passage that has left the draft (`-stale`). With no triage file it skips.
57
+ - `hyperspec triage status` (the counts, the station's rules, and the synthesis: every passage two
58
+ or more readers raised findings about), `triage answer` (refuses with `triage-evidence-missing`,
59
+ `-too-short`, `-not-found` or `triage-reason-missing`, writing nothing), `triage import` (an
60
+ outside review in markdown or plain text as findings: headings name readers, Good, Improve,
61
+ Missing and Remove labels set the kind, praise is counted and not triaged, and a quote the draft
62
+ does not hold warns, `triage-import-quote-not-found`; `triage-import-empty` when there is nothing
63
+ to answer) and `triage reply` (a plain-text reply to the reviewer; it never sends, and exits 1
64
+ while a finding is unanswered).
65
+ - The quotes station reads the new optional `writing.quotes.examples`: `true` reads a quoted span
66
+ whose sentence names no known speaker as an example phrasing, not a quotation; a list exempts
67
+ exactly those phrasings. A quotation that names its speaker is checked either way. Lint refuses
68
+ any other value (`writing-quotes`, `writing-quotes-examples`, test 1).
69
+ - The sequence station gains the quiz rules, on when the new `writing.form.sequence.quiz` names the
70
+ quiz heading: every defined term tested by some question (a simple plural counts), no question
71
+ using a term defined after the unit it is tagged with, every question tagged, every answer one of
72
+ its options (`station-sequence-quiz-missing`, `-untagged`, `-answer`, `-used-before-defined`,
73
+ `-untested`). Promoted from the checker the first book written this way used, with its question
74
+ shape. A hollow `quiz` fails lint (`writing-form-sequence-quiz`, test 1).
75
+ - The evidence rule moved to `src/evidence.mjs`, shared by `judge record` and `triage`; `judge.mjs`
76
+ re-exports what it exported.
77
+ - The worked examples: the essay and the story ship four panel packets each, with a hand-filled
78
+ sample verdict per reader; the course carries a quiz at the end of each part and `quiz: Check
79
+ yourself`.
80
+
81
+ **Behavior change:** `check` runs nine stations, so `--json` and the ledger's `stations` carry
82
+ `triage` (as `skip` for every spec without a triage file). `judge prepare` writes the four panel
83
+ packets for every spec whose audience has a rubric, where 0.8 wrote none; `--only` still limits it.
84
+ No other finding id changed.
85
+
3
86
  ## 0.8.0 (2026-09-29)
4
87
 
5
88
  A work read in order can now be checked as one. Every station so far held ONE piece to its spec; a
package/README.md CHANGED
@@ -25,12 +25,16 @@ improvement ledger. Every test is defined in [SPEC.md](SPEC.md).
25
25
  | `hyperspec lint <file...> [--json]` | Score each hyperspec against the nine tests. |
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
- | `hyperspec segments init <material> --id <mid> [--out F] [--by paragraph\|sentence]` | Split a material into segments to label. Refuses to overwrite an existing file. |
28
+ | `hyperspec segments init <material> --id <mid> [--out F] [--by paragraph\|sentence] [--keep OLD]` | Split a material into segments to label. With `--keep`, re-mark an edited material: every segment whose text is unchanged keeps its id and labels, and only the rest are listed to label. Refuses to overwrite an existing file unless `--keep` names it. |
29
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
30
  | `hyperspec dna measure <scope-dir>` | Check every golden in a scope and write its measured features. |
31
31
  | `hyperspec check <spec> [--draft <file>] [--only a,b]` | Run a writing spec's deterministic stations against a draft, or, for a sequential work, against its files in reading order. |
32
32
  | `hyperspec judge prepare <spec> --draft <file> --out <dir> [--only a,b] [--force]` | Write one packet per judgment station, for an outside judge to fill. |
33
33
  | `hyperspec judge record <packet> --verdict <file>` | Check a judge's verdict against its packet, derive the station's status, and record it. |
34
+ | `hyperspec triage status <spec> [--draft <file>]` | Count every finding's answer, list the passages two or more readers share, and hold every answer to the draft. |
35
+ | `hyperspec triage answer <spec> <finding> taken\|kept\|already-true\|open [--evidence S] [--reason S] [--draft <file>]` | Answer one finding: evidence from the draft for taken and already-true, a reason for kept. |
36
+ | `hyperspec triage import <spec> <review> [--source S] [--draft <file>]` | Bring an outside review in as findings to answer. |
37
+ | `hyperspec triage reply <spec> [--source S] [--draft <file>]` | Print a plain-text reply to the reviewer from the answers. Never sends anything. |
34
38
  | `hyperspec learn prepare <spec> --first <draft> --approved <draft> --out <dir> [--force]` | Write the edits between a first draft and the approved one, for a judge to classify by spec block. |
35
39
  | `hyperspec learn record <packet> --verdict <file>` | Count the classified edits by block and name one next move. |
36
40
  | `hyperspec recipe check <output-or-recipe>` | Check that a recipe records everything the standard asks for. |
@@ -57,6 +61,12 @@ and 1 when it refused. `judge prepare` and `learn prepare` exit 0 when they wrot
57
61
  with lint's own code when the spec is not ready, and `judge prepare` exits 1 when a station could
58
62
  not build its packet. All four exit 2 on a usage error.
59
63
 
64
+ `hyperspec triage status` exits 0 when every finding is answered and every answer holds against
65
+ the draft, and 1 when not; `triage answer` 0 when it wrote the answer and 1 when it refused it;
66
+ `triage import` 0 when it imported and 1 when the review holds nothing to answer; `triage reply` 0
67
+ when the reply is ready to send and 1 when a finding is still unanswered. All four exit 2 on a
68
+ usage error.
69
+
60
70
  The recipe commands use the same numbers: 0 ok, 1 a check failed or the child regressed, 2
61
71
  usage or unreadable input, 3 pending, when `regenerate` has stages waiting for a runner.
62
72
  `regenerate` also exits 1 when a stage it ran reported a failing verdict, or none. A stage it
@@ -194,13 +204,15 @@ it did in 0.4. The folder shape, every feature, and every finding are in
194
204
 
195
205
  ### Checking a draft
196
206
 
197
- Once a draft exists, `check` holds it to its spec with eight stations, none of which calls a
207
+ Once a draft exists, `check` holds it to its spec with nine stations, none of which calls a
198
208
  model or touches the network: `form` (length and required parts), `terms` (every word in the new
199
209
  optional `writing.audience.terms` is defined where it first appears), `claims` (the claims
200
210
  ledger still matches the draft, and every claim has a source), `quotes` (in nonfiction, every quotation of four
201
- words or more is word for word in a marked quote), `private` (no run of eight words from a
211
+ words or more is word for word in a marked quote; `writing.quotes.examples` marks example
212
+ phrasings such as "write the update for Dana" as examples rather than quotations), `private` (no run of eight words from a
202
213
  private segment), `dna` (the draft's measured style beside its scope's, as warnings), `links`
203
- (well-formed, and relative links resolve) and `sequence` (for a work read in order; see below).
214
+ (well-formed, and relative links resolve), `sequence` (for a work read in order; see below) and
215
+ `triage` (every reader's finding answered, and every answer held to the draft; see below).
204
216
 
205
217
  ```bash
206
218
  npx @supersuit/hyperspec check essay.hyperspec.md --draft essay/draft.md
@@ -219,7 +231,10 @@ it across the whole work: every lesson carries its sections ("After this lesson
219
231
  terms", "Try this" by default), every term is defined in exactly one lesson, no lesson uses a term
220
232
  before the lesson that defines it (code, the part's closing teaser and words the reader already
221
233
  knows are exempt), each lesson defines the terms the outline promises, and a pointer to a later
222
- lesson is a warning. List the work's files and `check` needs no `--draft`:
234
+ lesson is a warning. Name the quiz heading as `quiz:` and every quiz is held to it too: every
235
+ defined term tested by some question, no question using a term from a lesson after the one it is
236
+ tagged with, and every answer one of its question's options. List the work's files and `check`
237
+ needs no `--draft`:
223
238
 
224
239
  ```yaml
225
240
  sequence:
@@ -263,6 +278,32 @@ style rule". It never edits the spec. Both examples ship their packets and hand-
263
278
  verdicts. The packet shapes, every station's rules and findings, and the learn tally are in
264
279
  [WRITING.md](WRITING.md#judging-a-draft).
265
280
 
281
+ ### A reader panel, and answering what it found
282
+
283
+ A draft gets pressure-tested before it ships: several readers read it, each through their own
284
+ lens, and say what works, what to improve, what is missing and what to remove. The `panel` judge
285
+ makes that part of the run. It writes one packet per reader, the ones `writing.panel` lists or by
286
+ default a skeptic, a newcomer and an expert, and always the audience's own reader as `buyer`,
287
+ since a panel that never includes the person the piece is for tests everything but that. Every
288
+ item a reader lists quotes the draft word for word, so a review of a stale copy is refused.
289
+
290
+ Each thing a reader would change becomes a finding in `triage.jsonl`, beside the runs ledger, and
291
+ so does each point of an outside review brought in with `triage import`. Every finding gets one
292
+ answer: `taken` (with the passage of the draft that now does it), `kept` (with the reason),
293
+ `already-true` (with the passage that already did it) or `open` (a decision for you). `check` fails
294
+ while a finding is unanswered or an answer's passage is no longer in the draft, and warns on an
295
+ open one. `triage reply` turns the answers into a plain-text reply to the reviewer, for you to send.
296
+
297
+ ```bash
298
+ npx @supersuit/hyperspec judge record story/judge/panel-skeptic.packet.json --verdict story/sample-verdicts/panel-skeptic.verdict.json
299
+ npx @supersuit/hyperspec triage status story.hyperspec.md --draft story/draft.md
300
+ npx @supersuit/hyperspec triage answer story.hyperspec.md panel-skeptic-aeb09835 open --draft story/draft.md --reason "the author's call"
301
+ npx @supersuit/hyperspec triage reply story.hyperspec.md --draft story/draft.md
302
+ ```
303
+
304
+ The triage file, every answer's rule, how a review is read, and every finding are in
305
+ [WRITING.md](WRITING.md#triage-1).
306
+
266
307
  ## The format
267
308
 
268
309
  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.7, the judge and learn commands included, and cut 0.8 from them
100
+ next_action: collect adopter issues on 0.9, the panel and triage included, and cut 0.10 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.8.0** (2026-09-29)
112
+ **Version 0.9.1** (2026-09-30)
113
113
 
114
114
  ## What makes a spec a hyperspec
115
115