@supersuit/hyperspec 0.7.0 → 0.9.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.
- package/CHANGELOG.md +93 -0
- package/README.md +76 -9
- package/SPEC.md +2 -2
- package/WRITING.md +463 -18
- package/bin/hyperspec.mjs +137 -6
- package/examples/writing/course/claims.jsonl +0 -0
- package/examples/writing/course/goldens/lesson.md +1 -0
- package/examples/writing/course/materials/brief.md +9 -0
- package/examples/writing/course/materials/brief.md.segments.jsonl +6 -0
- package/examples/writing/course/outline.md +11 -0
- package/examples/writing/course/part-1.md +64 -0
- package/examples/writing/course/part-2.md +57 -0
- package/examples/writing/course/runs.jsonl +0 -0
- package/examples/writing/course.hyperspec.md +206 -0
- package/examples/writing/essay/judge/panel-buyer.packet.json +118 -0
- package/examples/writing/essay/judge/panel-expert.packet.json +114 -0
- package/examples/writing/essay/judge/panel-novice.packet.json +114 -0
- package/examples/writing/essay/judge/panel-skeptic.packet.json +114 -0
- package/examples/writing/essay/sample-verdicts/panel-buyer.verdict.json +11 -0
- package/examples/writing/essay/sample-verdicts/panel-expert.verdict.json +13 -0
- package/examples/writing/essay/sample-verdicts/panel-novice.verdict.json +11 -0
- package/examples/writing/essay/sample-verdicts/panel-skeptic.verdict.json +13 -0
- package/examples/writing/story/judge/panel-buyer.packet.json +118 -0
- package/examples/writing/story/judge/panel-expert.packet.json +114 -0
- package/examples/writing/story/judge/panel-novice.packet.json +114 -0
- package/examples/writing/story/judge/panel-skeptic.packet.json +114 -0
- package/examples/writing/story/sample-verdicts/panel-buyer.verdict.json +13 -0
- package/examples/writing/story/sample-verdicts/panel-expert.verdict.json +11 -0
- package/examples/writing/story/sample-verdicts/panel-novice.verdict.json +11 -0
- package/examples/writing/story/sample-verdicts/panel-skeptic.verdict.json +11 -0
- package/package.json +1 -1
- package/src/check.mjs +25 -7
- package/src/evidence.mjs +74 -0
- package/src/judge.mjs +50 -66
- package/src/judges/index.mjs +7 -1
- package/src/judges/panel.mjs +135 -0
- package/src/sequence-draft.mjs +75 -0
- package/src/stations/index.mjs +5 -1
- package/src/stations/links.mjs +11 -3
- package/src/stations/quotes.mjs +26 -2
- package/src/stations/sequence.mjs +388 -0
- package/src/stations/triage.mjs +20 -0
- package/src/triage.mjs +401 -0
- package/src/writing-fields.mjs +81 -0
- package/src/writing.mjs +5 -1
package/WRITING.md
CHANGED
|
@@ -245,6 +245,11 @@ writing:
|
|
|
245
245
|
- a close
|
|
246
246
|
stations: # may be empty
|
|
247
247
|
- the three questions render as a numbered list
|
|
248
|
+
sequence: # optional: a work read in order; see Sequential works
|
|
249
|
+
files: # optional: its files in reading order; a * in a file name matches
|
|
250
|
+
- course/part-*.md
|
|
251
|
+
outline: course/outline.md # optional: the outline that promises each unit's terms
|
|
252
|
+
quiz: Check yourself # optional: the quiz heading; turns on the quiz rules
|
|
248
253
|
check:
|
|
249
254
|
station: structure and length check
|
|
250
255
|
source: form decision
|
|
@@ -276,6 +281,14 @@ writing:
|
|
|
276
281
|
station: every factual claim points at a source span
|
|
277
282
|
source: sourcing pass
|
|
278
283
|
author: agent:claude
|
|
284
|
+
quotes: # optional; not a block, so no check, source or author
|
|
285
|
+
examples: true # true | false | a list of phrasings; see quotes under Checking a draft
|
|
286
|
+
panel: # optional: the panel's readers; the audience's reader joins as buyer
|
|
287
|
+
- id: skeptic # a lower-case slug, unique, never buyer
|
|
288
|
+
who: a manager who has run one-on-ones for years and doubts that a list is the problem
|
|
289
|
+
knows: # optional
|
|
290
|
+
- one-on-one
|
|
291
|
+
lens: what the essay asserts without showing
|
|
279
292
|
characters: # required when fiction: true; ids unique
|
|
280
293
|
- id: ines
|
|
281
294
|
entity: world/ines.json # optional; if given, the file must exist
|
|
@@ -337,12 +350,12 @@ Each row lists what the writing profile adds to that test. The core conditions i
|
|
|
337
350
|
|
|
338
351
|
| Test | A writing spec fails it when |
|
|
339
352
|
|---|---|
|
|
340
|
-
| 1 every decision is accounted for | a required block is missing and not deferred; a required field is missing; a closed-set value is outside its set (`trust`, `reader`, `change.kind`, the shape of `identity`, `unsourced_claim`); `identity: character:<id>` names a character that is not in `writing.characters`; `fiction` is present and is anything other than `true` or `false`; two materials, two spine claims or two characters share an id; `form.length.min` or `max` is not a whole number of at least 1, or `min` is greater than `max`; `spine.claims` has fewer than three or more than seven distinct claims; a character has no knowledge entry, or an entry lacks `by` or `knows`; a material has no text, or no `segments` field, or its segments file is missing, malformed, labels a segment outside the seven (`unlabeled` included), repeats a segment id, or has segments that overlap or leave text uncovered; `dna.scope_dir` is present and is a placeholder; with `dna.scope_dir`, its `scope.md` is missing, unreadable or lacks a field, its writer, form, audience or purpose differs from the spec's, or its `goldens/` folder is missing or empty, or holds a golden that cannot be read, whose frontmatter never closes, or that has no passage; `audience.terms`, when present, holds a non-string entry or has no real entries at all. A `stance` outside the four is a warning, and so is `unsourced_claim: warn` |
|
|
353
|
+
| 1 every decision is accounted for | a required block is missing and not deferred; a required field is missing; a closed-set value is outside its set (`trust`, `reader`, `change.kind`, the shape of `identity`, `unsourced_claim`); `identity: character:<id>` names a character that is not in `writing.characters`; `fiction` is present and is anything other than `true` or `false`; two materials, two spine claims or two characters share an id; `form.length.min` or `max` is not a whole number of at least 1, or `min` is greater than `max`; `spine.claims` has fewer than three or more than seven distinct claims; a character has no knowledge entry, or an entry lacks `by` or `knows`; a material has no text, or no `segments` field, or its segments file is missing, malformed, labels a segment outside the seven (`unlabeled` included), repeats a segment id, or has segments that overlap or leave text uncovered; `dna.scope_dir` is present and is a placeholder; with `dna.scope_dir`, its `scope.md` is missing, unreadable or lacks a field, its writer, form, audience or purpose differs from the spec's, or its `goldens/` folder is missing or empty, or holds a golden that cannot be read, whose frontmatter never closes, or that has no passage; `audience.terms`, when present, holds a non-string entry or has no real entries at all; `form.sequence` is present and is not a map, or has `unit`, `terms_section`, `teaser` or `outline` empty or a placeholder, or has `files`, `sections` or `knows` that is not a list of real entries, or `quiz` empty or a placeholder; `quotes` is present and is not a map, or its `examples` is not `true`, `false` or a list of real phrasings; `panel` is present and is not a list of readers, or a reader has no `id`, `who` or `lens`, an `id` that is not a lower-case slug, an `id` used twice or `buyer`, or `knows` that is not a list of real entries. A `stance` outside the four is a warning, and so is `unsourced_claim: warn` |
|
|
341
354
|
| 2 every requirement can fail | `goal.conditions` lists fewer than five or more than ten distinct ids, lists an id twice, or names an id that is not a top-level requirement |
|
|
342
355
|
| 3 every requirement names its check | a block or a character has no `check` with a `station` or a `rubric` |
|
|
343
356
|
| 4 every field says where it came from and who wrote it | a block or a character has no `source` or no `author`; a spine claim names no materials, or names a material id that is not in `materials.items`, or a segment that is not in that material's segments file; a segment's text does not match its material word for word; a material changed after it was marked; a claim segment has no `source` and no `own`, a story no `teller`, a quote no `speaker`; with `dna.scope_dir`, a golden in the scope has no `approved_by`, an approver that starts `agent:`, or no `source` |
|
|
344
357
|
| 5 negative space is specified | `persona.will_not_say` is empty; `persona.facts_from` is anything other than `sources`; a spine claim cites a `private` or a `question` segment; with `dna.scope_dir`, a golden the spec lists is not one of the scope's goldens (it lives outside the scope's `goldens/` folder, is a symlink that resolves outside it, or sits in a subfolder, is `README.md` or is not a `.md` file), or the scope's `goldens/` folder resolves outside the scope |
|
|
345
|
-
| 6 examples outrank adjectives | a golden has no `why`; a material, `dna.rules`, golden
|
|
358
|
+
| 6 examples outrank adjectives | a golden has no `why`; a material, `dna.rules`, golden, character `entity` or `form.sequence.outline` path does not exist or is not a file; a `form.sequence.files` entry matches no file; a character has no golden lines or no rejected lines, or has the same line in both (compared trimmed and case-folded); with `dna.scope_dir`, a golden in the scope has no `why`, or the scope's `features.json` is missing or is not what `dna measure` would write now |
|
|
346
359
|
| 7 a stranger can resume it | `writing.progress` exists. An unknown `profile:` is a warning |
|
|
347
360
|
| 8 its adopters can push back on it | nothing further; the core rule applies |
|
|
348
361
|
| 9 it improves itself | nothing further; the core rule applies |
|
|
@@ -810,7 +823,7 @@ npx @supersuit/hyperspec check essay.hyperspec.md --draft essay/draft.md
|
|
|
810
823
|
|
|
811
824
|
It lints the spec first. A spec that fails lint, or is blocked on an open decision, runs no
|
|
812
825
|
station and exits with lint's own code, because a draft cannot be checked against a spec that is
|
|
813
|
-
not ready. Then it runs
|
|
826
|
+
not ready. Then it runs nine stations in a fixed order and prints one line for each: `pass`,
|
|
814
827
|
`fail` with its findings, or `skip` with the reason. A warning prints under its station and never
|
|
815
828
|
fails it. This is the essay example's draft:
|
|
816
829
|
|
|
@@ -824,6 +837,8 @@ dna: pass
|
|
|
824
837
|
warn [station-dna-drift] first_person_singular_rate is 22.892 in the draft; the scope's goldens measure 0, band 0 to 5
|
|
825
838
|
fix: Bring first_person_singular_rate back inside the band, or, if the scope no longer describes this writer, re-measure it with better goldens.
|
|
826
839
|
links: pass
|
|
840
|
+
sequence: skip (the spec declares no writing.form.sequence)
|
|
841
|
+
triage: skip (no triage file at essay/triage.jsonl yet; a recorded panel verdict or a triage import starts one)
|
|
827
842
|
verdict: one-shot
|
|
828
843
|
```
|
|
829
844
|
|
|
@@ -832,10 +847,13 @@ marked partial (see [The runs ledger](#the-runs-ledger)). `--json` prints the wh
|
|
|
832
847
|
finding included; a spec that is not ready prints lint's result with `lintBlocked: true` instead,
|
|
833
848
|
and a usage error prints `{ "spec", "draft", "error" }`. A finding names the draft line it points
|
|
834
849
|
at where there is one, quotes at most 80 characters of the draft, and never prints an absolute
|
|
835
|
-
path. A UTF-8 byte order mark at the start of the draft is ignored.
|
|
850
|
+
path. A UTF-8 byte order mark at the start of the draft is ignored. A spec that lists
|
|
851
|
+
`form.sequence.files` needs no `--draft`: its files, joined in reading order, are the draft, and a
|
|
852
|
+
finding names the file and its own line in it (see [Sequential works](#sequential-works)).
|
|
836
853
|
|
|
837
854
|
Exit codes: **0** every station that ran passed (a skip or a warning does not fail it); **1** a
|
|
838
|
-
station failed; **2** usage: no spec path, no `--draft
|
|
855
|
+
station failed; **2** usage: no spec path, no `--draft` for a spec that lists no
|
|
856
|
+
`form.sequence.files`, a draft that cannot be read, a spec
|
|
839
857
|
without `profile: writing`, or an `--only` that names no known station; and lint's own **1** or
|
|
840
858
|
**3** when the spec is not ready.
|
|
841
859
|
|
|
@@ -917,6 +935,22 @@ the station: a character's dialogue is invented rather than quoted from a materi
|
|
|
917
935
|
`attribution` judge tests it against each character's own lines instead (see
|
|
918
936
|
[Judging a draft](#judging-a-draft)).
|
|
919
937
|
|
|
938
|
+
A primer shows its reader what to type or say, and "write the update for Dana" in quotation marks
|
|
939
|
+
is an example of a request, not a quotation of anyone. No material holds it, so the station would
|
|
940
|
+
fail it. `writing.quotes.examples` says which quoted spans are examples:
|
|
941
|
+
|
|
942
|
+
```yaml
|
|
943
|
+
quotes:
|
|
944
|
+
examples: true
|
|
945
|
+
```
|
|
946
|
+
|
|
947
|
+
With `true`, a span whose sentence names no speaker is read as an example and passes unmatched. A
|
|
948
|
+
quotation that names its speaker is still held to the materials, so `Dana said "..."` must still be
|
|
949
|
+
in one of Dana's quote segments. With a list, only the phrasings listed pass, each compared the
|
|
950
|
+
way a span is matched, and every other span is checked as before. The list is the stricter
|
|
951
|
+
choice: under `true`, a made-up quotation that names no one passes too. `false`, or no `quotes`
|
|
952
|
+
key, checks every span.
|
|
953
|
+
|
|
920
954
|
| Id | Severity | Meaning |
|
|
921
955
|
|---|---|---|
|
|
922
956
|
| `station-quotes-unmatched` | fail | a quoted span is in no quote or story segment |
|
|
@@ -973,6 +1007,53 @@ the network, so it cannot tell you a URL is live.
|
|
|
973
1007
|
| `station-links-root-relative` | warn | a link starting with `/`, which cannot be resolved without the site |
|
|
974
1008
|
| `station-links-undefined-reference` | fail | a full or collapsed reference link whose label has no definition |
|
|
975
1009
|
|
|
1010
|
+
### sequence
|
|
1011
|
+
|
|
1012
|
+
Runs when the spec declares `form.sequence`, and holds a work read in order to what its reader
|
|
1013
|
+
depends on: every lesson carries its sections, every term is defined once, no lesson uses a term
|
|
1014
|
+
before the lesson that defines it, each lesson defines what the outline promises, and, when the
|
|
1015
|
+
spec names a quiz heading, the quizzes test every term in order. How a unit and a quiz are read,
|
|
1016
|
+
and each guard, are under [Sequential works](#sequential-works). With no `form.sequence` the
|
|
1017
|
+
station skips. It reads words, never meaning: a term used in another sense still counts as a use.
|
|
1018
|
+
|
|
1019
|
+
| Id | Severity | Meaning |
|
|
1020
|
+
|---|---|---|
|
|
1021
|
+
| `station-sequence-no-units` | fail | the draft has no `<unit> <n>` heading |
|
|
1022
|
+
| `station-sequence-numbering` | fail | a unit's number is not greater than the one before it |
|
|
1023
|
+
| `station-sequence-missing-section` | fail | a unit lacks one of `sections`; names the unit and the section |
|
|
1024
|
+
| `station-sequence-defined-twice` | fail | a term is defined in two units' terms sections; names both |
|
|
1025
|
+
| `station-sequence-used-before-defined` | fail | a unit uses a term before the unit that defines it; points at the first use |
|
|
1026
|
+
| `station-sequence-outline-unreadable` | fail | `outline` is set and the file cannot be read |
|
|
1027
|
+
| `station-sequence-outline-unkept` | fail | the outline promises a term in a unit that does not define it |
|
|
1028
|
+
| `station-sequence-forward-pointer` | warn | a unit mentions a later unit by number, once per pair |
|
|
1029
|
+
| `station-sequence-quiz-missing` | fail | `quiz` is set and the draft has no quiz heading |
|
|
1030
|
+
| `station-sequence-quiz-untagged` | fail | a numbered line in a quiz names no unit it tests |
|
|
1031
|
+
| `station-sequence-quiz-answer` | fail | a question has no answer on its quiz's answers line, or its answer is not one of its options |
|
|
1032
|
+
| `station-sequence-quiz-used-before-defined` | fail | a question uses a term a later unit than its tagged one defines; points at the question |
|
|
1033
|
+
| `station-sequence-quiz-untested` | fail | no question uses a defined term, or its simple plural; points at the defining unit |
|
|
1034
|
+
|
|
1035
|
+
### triage
|
|
1036
|
+
|
|
1037
|
+
Runs when the spec's triage file exists (`triage.jsonl`, beside the runs ledger), and holds every
|
|
1038
|
+
finding in it to its answer: a finding nobody answered fails, a `taken` or `already-true` answer
|
|
1039
|
+
fails unless the passage it quotes is in the draft as it is now, and a `kept` answer fails without
|
|
1040
|
+
its reason. An `open` finding is a warning, since it is a decision for the operator and the draft
|
|
1041
|
+
can ship while it waits. So is a `kept` or `open` answer about a passage that has since left the
|
|
1042
|
+
draft. How findings arrive and how they are answered are under [Triage](#triage-1). With no triage
|
|
1043
|
+
file the station skips. It checks that an answer's evidence is in the draft, never whether the
|
|
1044
|
+
passage does what the finding asked.
|
|
1045
|
+
|
|
1046
|
+
| Id | Severity | Meaning |
|
|
1047
|
+
|---|---|---|
|
|
1048
|
+
| `station-triage-unreadable` | fail | a line of the triage file is not a finding: not JSON, or no `finding_id` or `text` |
|
|
1049
|
+
| `station-triage-untriaged` | fail | a finding has no disposition; names it, at its evidence's line |
|
|
1050
|
+
| `station-triage-disposition` | fail | a finding's disposition is not `taken`, `kept`, `already-true` or `open` |
|
|
1051
|
+
| `station-triage-evidence-missing` | fail | a `taken` or `already-true` answer quotes no passage of at least three words |
|
|
1052
|
+
| `station-triage-evidence-not-found` | fail | a `taken` or `already-true` answer quotes a passage that is not in the draft |
|
|
1053
|
+
| `station-triage-reason-missing` | fail | a `kept` answer gives no reason |
|
|
1054
|
+
| `station-triage-open` | warn | a finding is open, a decision still to make |
|
|
1055
|
+
| `station-triage-stale` | warn | a `kept` or `open` answer is about a passage that is no longer in the draft |
|
|
1056
|
+
|
|
976
1057
|
### Any station
|
|
977
1058
|
|
|
978
1059
|
| Id | Severity | Meaning |
|
|
@@ -985,11 +1066,13 @@ Each `check` appends one line to the spec's `improvement.ledger`, the same file
|
|
|
985
1066
|
reads:
|
|
986
1067
|
|
|
987
1068
|
```json
|
|
988
|
-
{"at":"2026-09-29T13:21:37.330Z","kind":"check","draft":"essay/draft.md","draft_sha256":"<sha256 of the draft>","spec_sha256":"<sha256 of the spec>","stations":{"form":"pass","terms":"pass","claims":"pass","quotes":"pass","private":"pass","dna":"pass","links":"pass"},"verdict":"one-shot"}
|
|
1069
|
+
{"at":"2026-09-29T13:21:37.330Z","kind":"check","draft":"essay/draft.md","draft_sha256":"<sha256 of the draft>","spec_sha256":"<sha256 of the spec>","stations":{"form":"pass","terms":"pass","claims":"pass","quotes":"pass","private":"pass","dna":"pass","links":"pass","sequence":"skip","triage":"skip"},"verdict":"one-shot"}
|
|
989
1070
|
```
|
|
990
1071
|
|
|
991
1072
|
`draft` is the draft's path relative to the spec's folder, however you spelled it, so one draft
|
|
992
|
-
has one history.
|
|
1073
|
+
has one history. A work checked from `form.sequence.files` is keyed by that list as the spec writes
|
|
1074
|
+
it, joined with ", ", so the work keeps one history as parts are added, and its `draft_sha256`
|
|
1075
|
+
covers every file's name and bytes. `draft_sha256` and `spec_sha256` hash the two files' bytes; the files the spec
|
|
993
1076
|
names (materials, the claims ledger, a scope folder) are not hashed, so "changed" below means the
|
|
994
1077
|
draft or the spec. `stations` holds each station's status.
|
|
995
1078
|
|
|
@@ -1010,6 +1093,131 @@ the same draft:
|
|
|
1010
1093
|
|
|
1011
1094
|
A ledger path that leads outside the spec's folder is not written, and `check` prints a warning.
|
|
1012
1095
|
|
|
1096
|
+
## Sequential works
|
|
1097
|
+
|
|
1098
|
+
A course, a primer, a textbook, a book of lessons: a work read in order makes a promise no single
|
|
1099
|
+
piece can check. Lesson 5 is written for someone who has read Lessons 1 to 4 and nothing else, so
|
|
1100
|
+
every word it uses was defined there. That promise lives across pieces, and it breaks one edit at a
|
|
1101
|
+
time: a term moves, a lesson is reordered, a sentence borrows a word from a lesson the reader has
|
|
1102
|
+
not reached. Declare the work a sequence and the `sequence` station checks the promise on every run.
|
|
1103
|
+
|
|
1104
|
+
### Declaring a sequence
|
|
1105
|
+
|
|
1106
|
+
Add `sequence:` to `writing.form`. Every key is optional; this is the course example's, with the
|
|
1107
|
+
four that have defaults written out:
|
|
1108
|
+
|
|
1109
|
+
```yaml
|
|
1110
|
+
sequence:
|
|
1111
|
+
unit: Lesson
|
|
1112
|
+
files:
|
|
1113
|
+
- course/part-*.md
|
|
1114
|
+
sections:
|
|
1115
|
+
- After this lesson you can
|
|
1116
|
+
- New terms
|
|
1117
|
+
- Try this
|
|
1118
|
+
terms_section: New terms
|
|
1119
|
+
outline: course/outline.md
|
|
1120
|
+
teaser: Next,
|
|
1121
|
+
quiz: Check yourself
|
|
1122
|
+
```
|
|
1123
|
+
|
|
1124
|
+
| Key | Default | What it is |
|
|
1125
|
+
|---|---|---|
|
|
1126
|
+
| `unit` | `Lesson` | the word each unit's heading starts with: `## Lesson 3: Title` |
|
|
1127
|
+
| `files` | none | the work's files in reading order, relative to the spec. A `*` in a file name matches within its folder, sorted by number, so `part-2.md` comes before `part-10.md` and a new part is picked up without editing the spec |
|
|
1128
|
+
| `sections` | the three shown | what every unit carries, each as a `**Label:**` line or a heading |
|
|
1129
|
+
| `terms_section` | `New terms` | the section whose list defines the unit's terms |
|
|
1130
|
+
| `outline` | none | an outline whose numbered items promise each unit's terms as `*Terms: a, b.*` |
|
|
1131
|
+
| `knows` | none | words a unit may use before a unit defines them, beside `audience.knows` |
|
|
1132
|
+
| `teaser` | `Next,` | a closing line starting `**<teaser>` names what is coming; it and everything after it in the unit are exempt from the order guard and the pointer warning |
|
|
1133
|
+
| `quiz` | none | the heading each quiz starts with; set, it turns on the quiz rules (see How a quiz is read) |
|
|
1134
|
+
|
|
1135
|
+
### How a unit is read
|
|
1136
|
+
|
|
1137
|
+
A unit starts at an ATX heading that begins with `unit` and a number, and runs to the next unit
|
|
1138
|
+
heading, the next heading of its own level or above, or the end of its file. So a part's own
|
|
1139
|
+
heading and introduction belong to no lesson, and neither does a file's YAML frontmatter, which is
|
|
1140
|
+
blanked before anything reads the file. A heading inside fenced code is not a heading.
|
|
1141
|
+
|
|
1142
|
+
The terms section is its label line and the list under it, to the first blank line after the list.
|
|
1143
|
+
Each item defines every bold term before its first `:**`, so `- **Claude Code**, **Codex** and
|
|
1144
|
+
**Claude Cowork:** ...` defines three. A term is compared in lower case, with code and emphasis
|
|
1145
|
+
marks and any parenthetical dropped. An item that says `(from Lesson 3)` reminds the reader of an
|
|
1146
|
+
earlier term and defines nothing.
|
|
1147
|
+
|
|
1148
|
+
A use is a whole-word match, ignoring case, where a hyphen joins a word: "context-aware" does not
|
|
1149
|
+
use "context", and "skills" does not use "skill". Code is not prose: fenced blocks and inline code
|
|
1150
|
+
never count as a use, so `@supersuit/superskill` does not use "supersuit". A unit's own terms
|
|
1151
|
+
section is not a use either. Listing a word in `knows` or `audience.knows` is a decision that the
|
|
1152
|
+
reader already has it, and it is the only way a unit may use a word before the unit that defines it.
|
|
1153
|
+
|
|
1154
|
+
### How a quiz is read
|
|
1155
|
+
|
|
1156
|
+
With `quiz` set, the station also holds the work's quizzes to the promise a quiz makes: it tests
|
|
1157
|
+
what the lessons taught, and only what the reader has been taught by the lesson it names. A quiz
|
|
1158
|
+
starts at a heading that begins with `quiz`, in any case, and runs to the next heading of its level
|
|
1159
|
+
or above or the end of its file, so a quiz between two lessons belongs to neither. These are the
|
|
1160
|
+
first two questions of the course example's first quiz, under its heading `## Check yourself: Part 1`:
|
|
1161
|
+
|
|
1162
|
+
```markdown
|
|
1163
|
+
1. *(Lesson 1)* Five hundred grams of flour and four hundred of water: what is the hydration?
|
|
1164
|
+
- a) Forty percent
|
|
1165
|
+
- b) Eighty percent
|
|
1166
|
+
2. *(Lesson 1)* When is flour and water a dough?
|
|
1167
|
+
- a) When no dry flour is left
|
|
1168
|
+
- b) When it has doubled
|
|
1169
|
+
```
|
|
1170
|
+
|
|
1171
|
+
A question is a numbered line tagged `*(<unit> N)*`, the unit it tests. It runs to the next
|
|
1172
|
+
numbered line, and its options are the indented `- a) ` lines inside it. A quiz ends with one
|
|
1173
|
+
`**Answers:**` line of number and letter pairs, `**Answers:** 1 b · 2 a`, in any separator. Every
|
|
1174
|
+
term a unit defines must be used by some question, where a simple plural counts ("doughs" tests
|
|
1175
|
+
"dough"); no question, options included, may use a term a later unit defines than the one it is
|
|
1176
|
+
tagged with, except a word in `knows`; every numbered line in a quiz must carry a tag; and every
|
|
1177
|
+
question's answer must name one of its options. Code in a question is not a use, as in a lesson.
|
|
1178
|
+
The rules and the question shape are those of the checker the first book written this way used,
|
|
1179
|
+
promoted here so every sequential work gets them.
|
|
1180
|
+
|
|
1181
|
+
### Checking a sequence
|
|
1182
|
+
|
|
1183
|
+
With `files` listed, `check` needs no `--draft`: the files, joined in reading order, are the draft,
|
|
1184
|
+
and every station reads them together. A finding names the file and its own line.
|
|
1185
|
+
|
|
1186
|
+
```bash
|
|
1187
|
+
npx @supersuit/hyperspec check course.hyperspec.md
|
|
1188
|
+
```
|
|
1189
|
+
|
|
1190
|
+
```
|
|
1191
|
+
form: pass
|
|
1192
|
+
terms: skip (writing.audience.terms is empty or not set)
|
|
1193
|
+
claims: pass
|
|
1194
|
+
quotes: pass
|
|
1195
|
+
private: pass
|
|
1196
|
+
dna: skip (writing.dna.scope_dir is not set)
|
|
1197
|
+
links: pass
|
|
1198
|
+
sequence: pass
|
|
1199
|
+
warn [station-sequence-forward-pointer] Lesson 1 points forward to Lesson 4 (course/part-1.md line 25)
|
|
1200
|
+
fix: Keep it a pointer ("more in Lesson 4"): Lesson 1 must make sense to a reader who has not read Lesson 4.
|
|
1201
|
+
warn [station-sequence-forward-pointer] Lesson 3 points forward to Lesson 4 (course/part-2.md line 24)
|
|
1202
|
+
fix: Keep it a pointer ("more in Lesson 4"): Lesson 3 must make sense to a reader who has not read Lesson 4.
|
|
1203
|
+
triage: skip (no triage file at course/triage.jsonl yet; a recorded panel verdict or a triage import starts one)
|
|
1204
|
+
verdict: one-shot
|
|
1205
|
+
```
|
|
1206
|
+
|
|
1207
|
+
`--draft` still works on a sequence spec: it names one file, which may hold every lesson under
|
|
1208
|
+
repeated headings. Checked from `files`, `form.length` and `required_parts` apply to the whole work,
|
|
1209
|
+
so write `required_parts` as the part headings. A relative link resolves beside the file that holds
|
|
1210
|
+
it.
|
|
1211
|
+
|
|
1212
|
+
The outline guard checks only the units the draft holds, so a work is checked while it is being
|
|
1213
|
+
written: an outline that promises Lessons 1 to 28 checks a draft of Lessons 1 to 20 without
|
|
1214
|
+
complaint. A pointer to a later unit is a warning, never a failure: "more in Lesson 12" is fine as
|
|
1215
|
+
long as the lesson makes sense without it.
|
|
1216
|
+
|
|
1217
|
+
It cannot tell whether a definition is good, whether a term is used in the sense it was defined in,
|
|
1218
|
+
or whether a question that uses a term tests understanding of it rather than only naming it. The
|
|
1219
|
+
judges still take one `--draft` file.
|
|
1220
|
+
|
|
1013
1221
|
## Judging a draft
|
|
1014
1222
|
|
|
1015
1223
|
`check` runs the stations that are plain functions of the spec and the draft. The rest of a
|
|
@@ -1033,6 +1241,10 @@ essay/judge/lineup.packet.json
|
|
|
1033
1241
|
essay/judge/lineup.key.json
|
|
1034
1242
|
essay/judge/reader.packet.json
|
|
1035
1243
|
essay/judge/persona.packet.json
|
|
1244
|
+
essay/judge/panel-skeptic.packet.json
|
|
1245
|
+
essay/judge/panel-novice.packet.json
|
|
1246
|
+
essay/judge/panel-expert.packet.json
|
|
1247
|
+
essay/judge/panel-buyer.packet.json
|
|
1036
1248
|
attribution: skip (the spec is not fiction; attribution applies only with fiction: true)
|
|
1037
1249
|
knowledge: skip (the spec is not fiction; knowledge applies only with fiction: true)
|
|
1038
1250
|
```
|
|
@@ -1040,8 +1252,9 @@ knowledge: skip (the spec is not fiction; knowledge applies only with fiction: t
|
|
|
1040
1252
|
Like `check`, it lints the spec first: a spec that fails lint, or is blocked on an open
|
|
1041
1253
|
decision, gets no packet, and `prepare` exits with lint's own code. Then it writes one
|
|
1042
1254
|
`<station>.packet.json` for each station that applies, in a fixed order (`doctor, lineup,
|
|
1043
|
-
reader, persona, attribution, knowledge`), and prints a `skip` line with the reason for each
|
|
1044
|
-
that does not.
|
|
1255
|
+
reader, persona, attribution, knowledge, panel`), and prints a `skip` line with the reason for each
|
|
1256
|
+
that does not. The panel writes one packet per reader, `panel-<reader>.packet.json`. `--only
|
|
1257
|
+
doctor,reader` prepares just those. The `--out` folder must already
|
|
1045
1258
|
exist. `prepare` refuses to overwrite any file it would write, naming every one, and then writes
|
|
1046
1259
|
nothing; `--force` replaces them. The worked examples ship the packets this writes, so add
|
|
1047
1260
|
`--force` to write them again there. The same spec and draft, with the same goldens and claims
|
|
@@ -1096,7 +1309,8 @@ it cannot find the spec and exits 2.
|
|
|
1096
1309
|
### The evidence rule
|
|
1097
1310
|
|
|
1098
1311
|
Every verdict field that cites the draft (the doctor's `evidence`, the reader's `lost_at` and
|
|
1099
|
-
`stopped_at`, the persona's `breaks`, the knowledge `leaks
|
|
1312
|
+
`stopped_at`, the persona's `breaks`, the knowledge `leaks`, every panel item) is a span copied
|
|
1313
|
+
from the draft. A
|
|
1100
1314
|
span counts only when:
|
|
1101
1315
|
|
|
1102
1316
|
- it has at least three words, where a word is a run of letters and digits (so "It's" is two);
|
|
@@ -1329,6 +1543,59 @@ them by order. Write `by` as something a reader of the draft can find.
|
|
|
1329
1543
|
| `judge-knowledge-leak` | fail | a character knows something too early, at its evidence's line |
|
|
1330
1544
|
| `judge-knowledge-character-unknown` | invalid | a leak names a character with no timeline in the packet |
|
|
1331
1545
|
|
|
1546
|
+
### panel
|
|
1547
|
+
|
|
1548
|
+
The draft read by several readers at once, each through their own lens: the pressure test a
|
|
1549
|
+
draft gets by hand before it ships, made part of the run. Every other station asks one question
|
|
1550
|
+
with a right answer; the panel asks each reader what works, what to improve, what is missing and
|
|
1551
|
+
what to remove, and each thing they would change is a finding somebody has to answer (see
|
|
1552
|
+
[Triage](#triage-1)). Applies when `writing.audience` is written and its check has a rubric, the
|
|
1553
|
+
reader station's own condition, because one of the readers is always the audience's own.
|
|
1554
|
+
|
|
1555
|
+
The readers are the ones `writing.panel` lists, each with an `id`, `who`, what they already `knows`
|
|
1556
|
+
and the `lens` they read for:
|
|
1557
|
+
|
|
1558
|
+
```yaml
|
|
1559
|
+
panel:
|
|
1560
|
+
- id: skeptic
|
|
1561
|
+
who: a manager who has run one-on-ones for years and doubts that a list is the problem
|
|
1562
|
+
lens: what the essay asserts without showing
|
|
1563
|
+
```
|
|
1564
|
+
|
|
1565
|
+
With no `panel` declared, three readers read it: a `skeptic`, who doubts the central claim and
|
|
1566
|
+
reads for what is asserted without support; a `novice`, new to the subject, reading for every
|
|
1567
|
+
term, step or assumption left unexplained; and an `expert` in the subject, reading for what is
|
|
1568
|
+
wrong, out of date or oversimplified. Declared or not, the audience's own reader joins last as
|
|
1569
|
+
`buyer`, built from `audience.who`, `audience.knows` and `audience.wants`: a panel that never
|
|
1570
|
+
includes the person the piece is for tests everything except whether it works for them.
|
|
1571
|
+
|
|
1572
|
+
There is one packet per reader. Inputs: `reader` (`id`, `who`, `knows`, `lens`) and the `draft`;
|
|
1573
|
+
the rubric is the audience block's. The verdict is `{ good, improve, missing, remove }`, four
|
|
1574
|
+
lists, any of them empty, where every item is `{ evidence, note }`: a span copied from the draft,
|
|
1575
|
+
and what and why in a sentence. A missing item quotes the passage nearest where the missing thing
|
|
1576
|
+
belongs. The panel never fails a draft. Each improve, missing and remove item is a warning at its
|
|
1577
|
+
evidence's line, and goes to the spec's triage file as a finding to answer; what works is counted
|
|
1578
|
+
and never triaged, since there is nothing to answer. The summary line counts all four and the
|
|
1579
|
+
findings added:
|
|
1580
|
+
|
|
1581
|
+
```
|
|
1582
|
+
panel: pass
|
|
1583
|
+
skeptic: 1 good, 1 to improve, 0 missing, 0 to remove; 1 added to triage (story/triage.jsonl)
|
|
1584
|
+
warn [judge-panel-improve] skeptic would improve this: The red ring is planted hard; a doubting reader sees the sale coming a scene before Ines says it. (line 63)
|
|
1585
|
+
fix: Answer it in the triage file: take it, keep the passage with a reason, show it is already true, or leave it open for a decision.
|
|
1586
|
+
verdict: one-shot
|
|
1587
|
+
```
|
|
1588
|
+
|
|
1589
|
+
Each reader has its own history in the runs ledger: a panel judge line carries `reader` after
|
|
1590
|
+
`station`, and is compared only with that reader's earlier lines. Recording the same verdict twice
|
|
1591
|
+
adds nothing to triage, since a finding's id is a hash of what it says.
|
|
1592
|
+
|
|
1593
|
+
| Id | Kind | Meaning |
|
|
1594
|
+
|---|---|---|
|
|
1595
|
+
| `judge-panel-improve` | warn | the reader would change this passage, and why |
|
|
1596
|
+
| `judge-panel-missing` | warn | the reader needs something the draft does not give, near this passage |
|
|
1597
|
+
| `judge-panel-remove` | warn | the reader would cut this passage, and why |
|
|
1598
|
+
|
|
1332
1599
|
### Any judgment station
|
|
1333
1600
|
|
|
1334
1601
|
| Id | Kind | Meaning |
|
|
@@ -1399,10 +1666,181 @@ copy on every release.
|
|
|
1399
1666
|
| story | persona | fail | three process details (the deck oven's heat-up time, the rolls' bake time, how the starter is fed) are in no claim, and the rubric allows none outside the ledger |
|
|
1400
1667
|
| story | attribution | pass | all 19 lines named right by voice alone |
|
|
1401
1668
|
| story | knowledge | pass | neither character knows anything early |
|
|
1669
|
+
| essay | panel-skeptic | pass | the claim rests on a count; "three is enough" is asserted, and who answered the survey is never said |
|
|
1670
|
+
| essay | panel-novice | pass | the one term a newcomer lacks is defined where it appears; the running agenda needs a first step |
|
|
1671
|
+
| essay | panel-expert | pass | the follow-through is right; say what to do when the silence runs on, and cut the opening disaster story |
|
|
1672
|
+
| essay | panel-buyer | pass | it ends on the card; it misses what to do when the report says nothing, at the same passage as the expert |
|
|
1673
|
+
| story | panel-skeptic | pass | the letter is carried by what Theo does not do; the red ring round Friday gives the sale away |
|
|
1674
|
+
| story | panel-novice | pass | the deck oven is shown where it is named; the proving cabinet is not |
|
|
1675
|
+
| story | panel-expert | pass | the flour is weighed, as in a real bakery; the starter is never shown being fed |
|
|
1676
|
+
| story | panel-buyer | pass | the first line of Ines's speech holds the reader; the red ring again, and a radio that goes nowhere |
|
|
1402
1677
|
|
|
1403
1678
|
Both examples pass every station of `check`. Each failure here is something no deterministic
|
|
1404
1679
|
station can see.
|
|
1405
1680
|
|
|
1681
|
+
## Triage
|
|
1682
|
+
|
|
1683
|
+
A panel verdict, or an outside review, is a list of findings, and a finding is only useful once
|
|
1684
|
+
someone has decided what to do about it. Triage is where that happens: every finding lands in one
|
|
1685
|
+
file, each gets one of four answers, `check` holds every answer to the draft as it is now, and a
|
|
1686
|
+
reply to the reviewer is written from the answers. Four answers, because a finding ends in one of
|
|
1687
|
+
four places:
|
|
1688
|
+
|
|
1689
|
+
| Disposition | Means | Needs |
|
|
1690
|
+
|---|---|---|
|
|
1691
|
+
| `taken` | the draft now does what the finding asked | `--evidence`: the passage of the current draft that does it |
|
|
1692
|
+
| `kept` | the passage stays as it is, on purpose | `--reason`: why |
|
|
1693
|
+
| `already-true` | the draft already did it | `--evidence`: the passage that does it |
|
|
1694
|
+
| `open` | a decision for the operator | `--reason`, optionally: what is to decide |
|
|
1695
|
+
|
|
1696
|
+
Evidence follows [the evidence rule](#the-evidence-rule) a judge's quotes follow, against the
|
|
1697
|
+
draft as it is when the answer is given. Nothing in triage reads meaning: it holds an answer's
|
|
1698
|
+
evidence to the draft word for word, and the person answering decides whether the passage does
|
|
1699
|
+
what the finding asked.
|
|
1700
|
+
|
|
1701
|
+
### The triage file
|
|
1702
|
+
|
|
1703
|
+
Findings live in `triage.jsonl` beside the spec's runs ledger (`improvement.ledger`), so the
|
|
1704
|
+
story example's is `story/triage.jsonl`. Recording a panel verdict adds the reader's improve,
|
|
1705
|
+
missing and remove items to it, and `triage import` adds an outside review's. Each line is one
|
|
1706
|
+
finding:
|
|
1707
|
+
|
|
1708
|
+
```json
|
|
1709
|
+
{"finding_id":"panel-skeptic-aeb09835","source":"panel","reader":"skeptic","kind":"improve","text":"The red ring is planted hard; a doubting reader sees the sale coming a scene before Ines says it.","evidence":"somebody had drawn a ring round Friday in red pen","draft_sha256":"fdf8dd1ee311a376e45cca31b50f0b01f1157a889cb332620cbd178e9c510306","disposition":null,"answer":null}
|
|
1710
|
+
```
|
|
1711
|
+
|
|
1712
|
+
`finding_id` is `panel-<reader>-` or `import-` and eight characters of a hash of what the finding
|
|
1713
|
+
says, so the same finding recorded twice is one finding. `source` is `panel` or where an imported
|
|
1714
|
+
review came from, `reader` the panel reader or the review's heading, `kind` one of `improve`,
|
|
1715
|
+
`missing` and `remove`, or `note` for an imported point under no such label. `evidence` is the
|
|
1716
|
+
passage the finding is about, and `draft_sha256` the draft it was raised against. `disposition` and
|
|
1717
|
+
`answer` are null until the finding is answered; then `answer` holds `evidence` or `reason` as
|
|
1718
|
+
given, the draft's `draft_sha256` and the time, `at`.
|
|
1719
|
+
|
|
1720
|
+
### Answering a finding
|
|
1721
|
+
|
|
1722
|
+
Record the four panel samples of the story (see [panel](#panel)), then answer each finding:
|
|
1723
|
+
|
|
1724
|
+
```bash
|
|
1725
|
+
npx @supersuit/hyperspec triage answer story.hyperspec.md panel-novice-47ce4719 already-true --draft story/draft.md --evidence "Ines will not put it in the proving cabinet. She says the cabinet is for the white."
|
|
1726
|
+
npx @supersuit/hyperspec triage answer story.hyperspec.md panel-buyer-23aee52f kept --draft story/draft.md --reason "the radio nobody turns on is the silence the bench scene depends on"
|
|
1727
|
+
npx @supersuit/hyperspec triage answer story.hyperspec.md panel-expert-ef15b7b2 kept --draft story/draft.md --reason "the starter is the one thing Theo is never allowed to touch, so he never sees it fed"
|
|
1728
|
+
npx @supersuit/hyperspec triage answer story.hyperspec.md panel-skeptic-aeb09835 open --draft story/draft.md --reason "how early to plant the sale is the author's call"
|
|
1729
|
+
npx @supersuit/hyperspec triage answer story.hyperspec.md panel-buyer-57810b16 open --draft story/draft.md --reason "the same call as the skeptic's"
|
|
1730
|
+
npx @supersuit/hyperspec triage status story.hyperspec.md --draft story/draft.md
|
|
1731
|
+
```
|
|
1732
|
+
|
|
1733
|
+
Each answer prints `<finding>: <disposition> (story/triage.jsonl)`. `status` counts the answers,
|
|
1734
|
+
lists the passages two or more readers raised findings about, and runs the triage station's rules:
|
|
1735
|
+
|
|
1736
|
+
```
|
|
1737
|
+
story/triage.jsonl: 5 findings; 0 taken, 2 kept, 1 already true, 2 open, 0 not answered
|
|
1738
|
+
shared by two or more readers:
|
|
1739
|
+
line 63: "somebody had drawn a ring round Friday in red pen"
|
|
1740
|
+
skeptic (improve): The red ring is planted hard; a doubting reader sees the sale coming a scene before Ines says it.
|
|
1741
|
+
buyer (improve): I guessed Friday before the reveal, which took some of the weight out of the oven scene.
|
|
1742
|
+
triage: pass
|
|
1743
|
+
warn [station-triage-open] panel-skeptic-aeb09835 (skeptic, improve) is open: "The red ring is planted hard; a doubting reader sees the sale coming a scene be…" (how early to plant the sale is the author's call) (line 63)
|
|
1744
|
+
fix: A decision for the operator; answer it once it is made.
|
|
1745
|
+
warn [station-triage-open] panel-buyer-57810b16 (buyer, improve) is open: "I guessed Friday before the reveal, which took some of the weight out of the ov…" (the same call as the skeptic's) (line 63)
|
|
1746
|
+
fix: A decision for the operator; answer it once it is made.
|
|
1747
|
+
```
|
|
1748
|
+
|
|
1749
|
+
The shared passages are the synthesis of a panel: where two or more readers point at overlapping
|
|
1750
|
+
passages of the draft, the passage is worth reading first. It groups by where, never by meaning,
|
|
1751
|
+
so two readers who say the same thing about different passages are not grouped.
|
|
1752
|
+
|
|
1753
|
+
`answer` refuses, and writes nothing, when a `taken` or `already-true` answer's evidence is missing,
|
|
1754
|
+
under three words or not in the draft, or a `kept` answer has no reason. It may be given again: the
|
|
1755
|
+
last answer stands. Like `check`, every triage command takes `--draft`, or reads the files of a
|
|
1756
|
+
spec that lists `writing.form.sequence.files`, and names a finding's line by the file that holds
|
|
1757
|
+
it.
|
|
1758
|
+
|
|
1759
|
+
### Importing an outside review
|
|
1760
|
+
|
|
1761
|
+
A review written anywhere else, as markdown or plain text, comes in as findings with `triage
|
|
1762
|
+
import`, so it gets the same answers and the same check as the panel. This is a short review of the
|
|
1763
|
+
story:
|
|
1764
|
+
|
|
1765
|
+
```markdown
|
|
1766
|
+
#### The magazine's editor
|
|
1767
|
+
|
|
1768
|
+
**Good:** the opening puts the reader in the kitchen at once.
|
|
1769
|
+
|
|
1770
|
+
**Improve:**
|
|
1771
|
+
|
|
1772
|
+
- The oven noise comes twice; the second, "It made the noise while I was weighing the second batch", could go.
|
|
1773
|
+
|
|
1774
|
+
**Missing:**
|
|
1775
|
+
|
|
1776
|
+
- Theo never says what he wants: "I did not look at the coat. I looked at the dough." carries it, but only just.
|
|
1777
|
+
|
|
1778
|
+
#### A first-time reader
|
|
1779
|
+
|
|
1780
|
+
- Remove: the line about "the bakery closing on a Tuesday" felt out of place.
|
|
1781
|
+
```
|
|
1782
|
+
|
|
1783
|
+
```bash
|
|
1784
|
+
npx @supersuit/hyperspec triage import story.hyperspec.md story/review.md --draft story/draft.md --source "the editor's review"
|
|
1785
|
+
```
|
|
1786
|
+
|
|
1787
|
+
```
|
|
1788
|
+
the editor's review: 3 findings added to triage (story/triage.jsonl); 1 item of praise, not triaged
|
|
1789
|
+
warn [triage-import-quote-not-found] a finding quotes text that is not in the current draft: "the bakery closing on a Tuesday" (the line about "the bakery closing on a Tuesday" felt out o…)
|
|
1790
|
+
fix: The review may have read another copy of the draft. Check the finding against the draft as it is now before answering it.
|
|
1791
|
+
```
|
|
1792
|
+
|
|
1793
|
+
A heading names the reader of the points under it. A label (`Good`, `Improve`, `Improvement`,
|
|
1794
|
+
`Missing` or `Remove`, as a heading, a bold line, or a word and a colon starting a line or a list
|
|
1795
|
+
item) sets the kind of what follows. Each list item, with its indented lines, is one finding, and
|
|
1796
|
+
so is a paragraph under a label; an introduction under no label is not. What is good is counted
|
|
1797
|
+
and left out, since there is nothing to answer. A finding's evidence is the first quoted span, in
|
|
1798
|
+
double quotation marks or a `>` quotation block, of three words or more that is in the draft. A
|
|
1799
|
+
finding that quotes only text the draft does not hold is imported with no evidence and a warning:
|
|
1800
|
+
a review of a stale or partial copy is the usual cause, and the finding is still answered.
|
|
1801
|
+
`--source` names the review, by default its file's path, so a reply can be written to it alone.
|
|
1802
|
+
|
|
1803
|
+
### Replying to the reviewer
|
|
1804
|
+
|
|
1805
|
+
```bash
|
|
1806
|
+
npx @supersuit/hyperspec triage answer story.hyperspec.md import-6c60f92a kept --draft story/draft.md --reason "the second time is when Theo starts to listen to it"
|
|
1807
|
+
npx @supersuit/hyperspec triage answer story.hyperspec.md import-919ea916 already-true --draft story/draft.md --evidence "I did not look at the coat. I looked at the dough."
|
|
1808
|
+
npx @supersuit/hyperspec triage answer story.hyperspec.md import-ad3d7fe9 kept --draft story/draft.md --reason "no line about a Tuesday closing is in the draft; the review read another copy"
|
|
1809
|
+
npx @supersuit/hyperspec triage reply story.hyperspec.md --draft story/draft.md --source "the editor's review"
|
|
1810
|
+
```
|
|
1811
|
+
|
|
1812
|
+
```
|
|
1813
|
+
Thank you for the review. Here is what happened to each point.
|
|
1814
|
+
|
|
1815
|
+
Kept as it is:
|
|
1816
|
+
- The magazine's editor: The oven noise comes twice; the second, "It made the noise while I was weighing the second batch", could go. Why: the second time is when Theo starts to listen to it
|
|
1817
|
+
- A first-time reader: the line about "the bakery closing on a Tuesday" felt out of place. Why: no line about a Tuesday closing is in the draft; the review read another copy
|
|
1818
|
+
|
|
1819
|
+
Already in the draft:
|
|
1820
|
+
- The magazine's editor: Theo never says what he wants: "I did not look at the coat. I looked at the dough." carries it, but only just. It is here: "I did not look at the coat. I looked at the dough."
|
|
1821
|
+
```
|
|
1822
|
+
|
|
1823
|
+
`reply` prints plain text, one line per finding, under Taken, Kept as it is, Already in the draft
|
|
1824
|
+
and Still open, leaving out an empty heading, and anything not answered yet under a last heading
|
|
1825
|
+
of its own. It never sends anything: it is a draft for the operator to read and send, or not. It
|
|
1826
|
+
exits 1, with a line on stderr, when the triage station would fail, so a reply with a gap in it is
|
|
1827
|
+
not sent by accident. `--source` limits it to one review; without it every finding is included.
|
|
1828
|
+
|
|
1829
|
+
Exit codes: `status` **0** when the triage station would pass, **1** when it would fail; `answer`
|
|
1830
|
+
**0** written, **1** refused; `import` **0** imported, **1** the review holds no finding; `reply`
|
|
1831
|
+
**0** ready, **1** not; all four **2** on usage: no spec, a spec without `profile: writing`, no
|
|
1832
|
+
draft, no triage file for `answer` and `reply`, an unknown finding or disposition, or a review that
|
|
1833
|
+
cannot be read. `--json` prints the whole result.
|
|
1834
|
+
|
|
1835
|
+
| Id | Kind | Meaning |
|
|
1836
|
+
|---|---|---|
|
|
1837
|
+
| `triage-evidence-missing` | invalid | a `taken` or `already-true` answer has no `--evidence` |
|
|
1838
|
+
| `triage-evidence-too-short` | invalid | the answer's evidence has fewer than three words |
|
|
1839
|
+
| `triage-evidence-not-found` | invalid | the answer's evidence is not in the draft |
|
|
1840
|
+
| `triage-reason-missing` | invalid | a `kept` answer has no `--reason` |
|
|
1841
|
+
| `triage-import-quote-not-found` | warn | an imported finding quotes text, and none of it is in the draft |
|
|
1842
|
+
| `triage-import-empty` | invalid | the review holds no finding to answer |
|
|
1843
|
+
|
|
1406
1844
|
## Learning from edits
|
|
1407
1845
|
|
|
1408
1846
|
A factory writes a first draft, and a person edits it into the draft they approve. Every edit is
|
|
@@ -1583,7 +2021,7 @@ not exist.
|
|
|
1583
2021
|
|
|
1584
2022
|
## Worked examples
|
|
1585
2023
|
|
|
1586
|
-
|
|
2024
|
+
Three complete specs ship in [`examples/writing/`](examples/writing/), each with every file it
|
|
1587
2025
|
names:
|
|
1588
2026
|
|
|
1589
2027
|
- `essay.hyperspec.md`: an essay for new managers on running a first one-on-one. Three materials
|
|
@@ -1592,8 +2030,12 @@ names:
|
|
|
1592
2030
|
- `story.hyperspec.md`: a short story, `fiction: true`, narrated by one of its two characters.
|
|
1593
2031
|
Each character has speech rules, a knowledge timeline by scene, and golden and rejected lines
|
|
1594
2032
|
in a voice you can tell apart from the other's.
|
|
2033
|
+
- `course.hyperspec.md`: a four-lesson course in two part files, declared a sequence (see
|
|
2034
|
+
[Sequential works](#sequential-works)), with an outline promising each lesson's terms and a
|
|
2035
|
+
quiz closing each part. `check` reads the parts with no `--draft` and passes them, with two
|
|
2036
|
+
forward-pointer warnings.
|
|
1595
2037
|
|
|
1596
|
-
Every material in
|
|
2038
|
+
Every material in all three is marked. Between them the essay and the story use all seven labels, each with
|
|
1597
2039
|
the field it needs, and every spine claim cites the segments that support it. Each segments file
|
|
1598
2040
|
keeps the boundaries `segments init` wrote, in paragraph mode for prose and sentence mode for
|
|
1599
2041
|
bulleted notes, so you can re-run it and compare.
|
|
@@ -1602,21 +2044,24 @@ Both lint `pass (9/9)` with `writing: 9/9 blocks complete` and no findings. Each
|
|
|
1602
2044
|
draft written to it, `essay/draft.md` and `story/draft.md`, with its claims ledger beside it, and
|
|
1603
2045
|
both drafts pass every station of `check`: the essay with one dna warning, described under
|
|
1604
2046
|
[dna](#dna), and the story with dna skipped, since it names no scope folder, and quotes skipped,
|
|
1605
|
-
since it is fiction.
|
|
1606
|
-
specs and checks
|
|
2047
|
+
since it is fiction. The course lints the same way and its parts pass every station. A test
|
|
2048
|
+
lints all three specs and checks their drafts on every release, so they cannot drift from the tool.
|
|
1607
2049
|
|
|
1608
2050
|
Each also ships the packets `judge prepare` writes for its draft, in `essay/judge/` and
|
|
1609
2051
|
`story/judge/`, and one sample verdict per packet in `essay/sample-verdicts/` and
|
|
1610
|
-
`story/sample-verdicts/`, filled in by hand and marked as samples
|
|
1611
|
-
[The worked examples](#the-worked-examples)
|
|
2052
|
+
`story/sample-verdicts/`, filled in by hand and marked as samples, four panel readers' among
|
|
2053
|
+
them; what each found is under [The worked examples](#the-worked-examples), and the story's panel
|
|
2054
|
+
findings are triaged under [Triage](#triage-1). The essay adds a learn pair in `essay/learn/`: a
|
|
1612
2055
|
first draft, the packet `learn prepare` writes comparing it with `essay/draft.md`, and a sample
|
|
1613
2056
|
learn verdict (see [Learning from edits](#learning-from-edits)). A test checks that every packet
|
|
1614
2057
|
is what `prepare` writes now and records every sample.
|
|
1615
2058
|
|
|
1616
2059
|
## What later versions add
|
|
1617
2060
|
|
|
1618
|
-
This release is the schema, its lint, marked materials, scoped DNA, `check` with
|
|
1619
|
-
deterministic stations
|
|
2061
|
+
This release is the schema, its lint, marked materials, scoped DNA, `check` with nine
|
|
2062
|
+
deterministic stations (sequential works and their quizzes among them, and triage last), seven
|
|
2063
|
+
judgment stations written as packets for an outside judge (the panel among them), triage, and
|
|
2064
|
+
learn.
|
|
1620
2065
|
Next: lineups over several passages of one draft, so that one lucky pick carries less weight, and
|
|
1621
2066
|
a learn step that reads the runs ledger across drafts for the stations that keep failing and the
|
|
1622
2067
|
changes that made them pass, beside what one pair of drafts shows.
|