@supersuit/hyperspec 0.1.0 → 0.3.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 +102 -0
- package/README.md +105 -4
- package/SPEC.md +205 -13
- package/WRITING.md +426 -0
- package/bin/hyperspec.mjs +322 -2
- package/examples/minimal.hyperspec.md +2 -2
- package/examples/recipe/doctor.mjs +11 -0
- package/examples/recipe/essay.hyperspec.md +50 -0
- package/examples/recipe/factory.mjs +29 -0
- package/examples/recipe/materials/call-2.md +2 -0
- package/examples/recipe/materials/call.md +3 -0
- package/examples/recipe/materials/notes.md +3 -0
- package/examples/recipe/runner.mjs +20 -0
- package/examples/recipe/runs.jsonl +0 -0
- package/examples/recipe/stages.mjs +31 -0
- package/examples/writing/essay/goldens/close.md +2 -0
- package/examples/writing/essay/goldens/opening.md +2 -0
- package/examples/writing/essay/materials/interview-notes.md +12 -0
- package/examples/writing/essay/materials/team-survey.md +7 -0
- package/examples/writing/essay/materials/voice-memo.md +18 -0
- package/examples/writing/essay/runs.jsonl +0 -0
- package/examples/writing/essay.hyperspec.md +217 -0
- package/examples/writing/story/goldens/dialogue.md +3 -0
- package/examples/writing/story/goldens/opening.md +3 -0
- package/examples/writing/story/materials/bakery-visit.md +8 -0
- package/examples/writing/story/materials/notes.md +14 -0
- package/examples/writing/story/materials/scene-list.md +7 -0
- package/examples/writing/story/runs.jsonl +0 -0
- package/examples/writing/story.hyperspec.md +285 -0
- package/examples/writing/style-rules.md +19 -0
- package/package.json +8 -2
- package/runs.jsonl +0 -0
- package/src/blobs.mjs +77 -0
- package/src/compare.mjs +189 -0
- package/src/fsutil.mjs +33 -0
- package/src/hash.mjs +26 -0
- package/src/placeholder.mjs +20 -0
- package/src/profiles.mjs +50 -0
- package/src/recipe.mjs +164 -0
- package/src/regenerate.mjs +318 -0
- package/src/reproduce.mjs +156 -0
- package/src/rules.mjs +58 -19
- package/src/score.mjs +7 -1
- package/src/template.mjs +15 -3
- package/src/writer.mjs +125 -0
- package/src/writing-fields.mjs +317 -0
- package/src/writing-template.mjs +192 -0
- package/src/writing.mjs +181 -0
package/WRITING.md
ADDED
|
@@ -0,0 +1,426 @@
|
|
|
1
|
+
# The writing profile
|
|
2
|
+
|
|
3
|
+
A hyperspec for a piece of writing. An essay, a chapter, a letter, a story: anything an agent
|
|
4
|
+
drafts and a person reads.
|
|
5
|
+
|
|
6
|
+
Writing is where the average does the most damage. Asked for "an essay on X", an agent fills
|
|
7
|
+
every gap you left with the most typical choice: the typical reader, the typical argument, the
|
|
8
|
+
typical voice. Each choice is reasonable and the sum reads like nobody in particular wrote it.
|
|
9
|
+
The writing profile names the gaps a piece of writing has, so each one is filled on purpose, by
|
|
10
|
+
someone you can name, and checked before a draft reaches you.
|
|
11
|
+
|
|
12
|
+
A profile adds rules for one kind of work without adding a test. Every writing finding reports
|
|
13
|
+
under one of the nine tests in [SPEC.md](SPEC.md), with an id that starts `writing-`, and the
|
|
14
|
+
score is still out of nine. Everything in the core format (decisions, requirements, rejects,
|
|
15
|
+
examples, resume, feedback, improvement) still applies and is still linted.
|
|
16
|
+
|
|
17
|
+
## Opting in
|
|
18
|
+
|
|
19
|
+
Add `profile: writing` at the top level of the frontmatter, and a `writing:` map holding the
|
|
20
|
+
blocks. `hyperspec lint` then prints one more line under the score:
|
|
21
|
+
|
|
22
|
+
```
|
|
23
|
+
essay.hyperspec.md: pass (9/9)
|
|
24
|
+
writing: 9/9 blocks complete
|
|
25
|
+
```
|
|
26
|
+
|
|
27
|
+
A block counts as complete when it is present with no failing finding. `characters` also counts
|
|
28
|
+
as complete when the piece is not fiction, since nothing requires it. A block deferred to a
|
|
29
|
+
decision (see [Deferring a block](#deferring-a-block)) does not count as complete, because
|
|
30
|
+
nothing has been written in it yet. `--json` carries the same count on each file as
|
|
31
|
+
`"profile": { "name": "writing", "complete": 9, "total": 9 }`.
|
|
32
|
+
|
|
33
|
+
A `profile:` this linter does not know is a warning under test 7, naming the profile and saying
|
|
34
|
+
its rules were not checked.
|
|
35
|
+
|
|
36
|
+
## The ten components
|
|
37
|
+
|
|
38
|
+
These are the parts you cannot remove without the writing sliding back to the middle. Nine are
|
|
39
|
+
blocks under `writing:`. The tenth, progress, is deliberately never written down.
|
|
40
|
+
|
|
41
|
+
### 1. Materials: what goes in, and how it is marked
|
|
42
|
+
|
|
43
|
+
Brain dumps, transcripts, interview answers, notes, prior pieces, research. Each material
|
|
44
|
+
records who produced it, when it was captured, how, and how far it can be trusted: `raw` for
|
|
45
|
+
thinking out loud, `considered` for something someone has reviewed, `verified` for something
|
|
46
|
+
checked against its source. A brain dump is the most valuable input and the least structured, so
|
|
47
|
+
marking it is the step that turns thinking into something an agent can cite. Each material is
|
|
48
|
+
split into segments and each segment gets a label saying what it may be used as (see
|
|
49
|
+
[Materials labels](#materials-labels)).
|
|
50
|
+
|
|
51
|
+
### 2. Writer DNA: who is writing, and how they sound, for this purpose
|
|
52
|
+
|
|
53
|
+
A writer does not have one voice. The same person writes differently for a theology journal and
|
|
54
|
+
a landing page, so their DNA is scoped by **form, audience and purpose**, and the scope is the
|
|
55
|
+
whole design of this block.
|
|
56
|
+
|
|
57
|
+
- **Rules** are the one layer that applies everywhere: what the writer never does and always
|
|
58
|
+
does. They live in your style rules file, and every piece in every scope points at it.
|
|
59
|
+
- **Goldens** are real passages the writer has marked as right, each filed under its scope. A
|
|
60
|
+
golden feeds only work that shares that scope, so a sermon golden can never leak into a sales
|
|
61
|
+
email.
|
|
62
|
+
- **Every golden carries a note on why it is golden.** A golden without its reason teaches the
|
|
63
|
+
surface; the reason teaches the move.
|
|
64
|
+
|
|
65
|
+
DNA is proven by a blind lineup within its scope: a judge sees a generated passage beside real
|
|
66
|
+
goldens of the same kind and tries to pick it out. Every writer has their own DNA, and nobody's
|
|
67
|
+
scope feeds anybody else's.
|
|
68
|
+
|
|
69
|
+
### 3. Persona: who the piece speaks as
|
|
70
|
+
|
|
71
|
+
The identity the piece speaks as (the writer as themselves, a role, or a character), its stance
|
|
72
|
+
toward the reader, what it may assert, and what it will not say. Research on persona prompting
|
|
73
|
+
finds that a persona shapes voice and alignment and can cost accuracy
|
|
74
|
+
([PRISM](https://arxiv.org/html/2603.18507v1)), so here the persona governs voice and stance only.
|
|
75
|
+
**Facts come from marked sources, never from the persona.** That separation is a field,
|
|
76
|
+
`facts_from: sources`, and the linter refuses any other value.
|
|
77
|
+
|
|
78
|
+
The persona is recorded in the spec, where it belongs. The piece itself says nothing to the
|
|
79
|
+
reader about who is writing.
|
|
80
|
+
|
|
81
|
+
### 4. Audience: who is reading
|
|
82
|
+
|
|
83
|
+
A named person or a specified archetype: where they are on their path right now, which words
|
|
84
|
+
they already have, what they believe now, what they want, where they will read it (a phone in
|
|
85
|
+
ninety seconds, aloud, in print), and whether the reader is a person or another agent. Checked
|
|
86
|
+
by a term station (every word outside their vocabulary is defined on first use) and by a
|
|
87
|
+
simulated reader, a model playing that exact reader, which reports where it got lost and where
|
|
88
|
+
it would have stopped.
|
|
89
|
+
|
|
90
|
+
### 5. Goal: what the piece changes
|
|
91
|
+
|
|
92
|
+
The one step down the funnel: where this reader is now, where the piece moves them, and what
|
|
93
|
+
they do next if it worked. Then the change that step needs in them (a belief, an action or a
|
|
94
|
+
feeling), and five to ten requirements that would prove it happened, each written to be failable.
|
|
95
|
+
This is the block a grader works against, and the simulated reader is asked the one question that
|
|
96
|
+
matters: would you take the next step now?
|
|
97
|
+
|
|
98
|
+
### 6. Form: what kind of thing it is
|
|
99
|
+
|
|
100
|
+
Essay, chapter, wiki article, email, text, talk, letter, story. A form supplies its required
|
|
101
|
+
parts, its length envelope, and any extra checks it needs: a chapter checks continuity with the
|
|
102
|
+
chapters around it, a wiki article checks its links, a text message checks bubble length.
|
|
103
|
+
|
|
104
|
+
### 7. Spine: what it argues
|
|
105
|
+
|
|
106
|
+
The kind of argument (thesis, testimony, primer, letter, story; the set is open) and the claim
|
|
107
|
+
chain: the three to seven claims the piece has to land, in order, each pointing at the materials
|
|
108
|
+
that support it.
|
|
109
|
+
|
|
110
|
+
### 8. Sources and claims: what lets another agent pick it up
|
|
111
|
+
|
|
112
|
+
A claims ledger beside the piece: every factual claim in the draft points at a source and the
|
|
113
|
+
span in it, and every quote is matched word for word against its source. A writer's own brain
|
|
114
|
+
dump counts as a source for their opinions and their stories, never for an outside fact. By
|
|
115
|
+
default a claim with no source fails the check rather than passing with a warning.
|
|
116
|
+
|
|
117
|
+
### 9. Characters: everyone who speaks inside the work
|
|
118
|
+
|
|
119
|
+
The persona is who the piece speaks as. A character is someone who speaks inside it, and the
|
|
120
|
+
same rules apply one level down. Required when `fiction: true`. Each character carries:
|
|
121
|
+
|
|
122
|
+
- **Speech DNA**: the words they use, the ones they never would, and their rhythm.
|
|
123
|
+
- **Wants, fears, and the thing they hide**, because dialogue is someone trying to get something
|
|
124
|
+
while hiding something.
|
|
125
|
+
- **What they know, and when.** A knowledge timeline by chapter or scene, so a character never
|
|
126
|
+
says what they could not know yet. This is the most common way generated dialogue breaks a
|
|
127
|
+
story, and it is fully checkable.
|
|
128
|
+
- **How they speak to each person that matters**, since speech shifts by relationship.
|
|
129
|
+
- **Arc state**: where they are in their change at this point in the work.
|
|
130
|
+
- **Golden lines and rejected lines**: real lines that sound exactly like them, and lines that
|
|
131
|
+
sound close but wrong.
|
|
132
|
+
|
|
133
|
+
Checked by a blind attribution test (a judge sees a line with the speaker hidden and has to name
|
|
134
|
+
who said it), a knowledge-leak check against the timeline, and a consistency check against the
|
|
135
|
+
golden and rejected lines. If your characters already live as entities in a world database, the
|
|
136
|
+
optional `entity` field points at that record, so one record can say how a character looks and
|
|
137
|
+
how they speak.
|
|
138
|
+
|
|
139
|
+
### 10. Progress: where it is
|
|
140
|
+
|
|
141
|
+
Derived from disk, never stored. Which blocks are filled, which drafts exist, which checks
|
|
142
|
+
passed, what the last review said, and the next action are all things a second agent can read
|
|
143
|
+
off the folder itself. A saved progress field goes stale the first time a session dies mid-arc,
|
|
144
|
+
and from then on it lies to every agent that trusts it. So the linter refuses one: a
|
|
145
|
+
`writing.progress` key fails test 7, and `resume.next_action` stays the only thing the spec
|
|
146
|
+
says about what happens next.
|
|
147
|
+
|
|
148
|
+
## The schema
|
|
149
|
+
|
|
150
|
+
Every field below is required unless its comment says otherwise, and a required list needs at
|
|
151
|
+
least one entry. A path resolves relative to the spec file, the same way `examples` does, and a
|
|
152
|
+
path the linter checks must name a file that exists.
|
|
153
|
+
|
|
154
|
+
```yaml
|
|
155
|
+
profile: writing
|
|
156
|
+
fiction: false # optional, true or false; absent means false. true requires writing.characters
|
|
157
|
+
writing:
|
|
158
|
+
materials:
|
|
159
|
+
items: # at least one
|
|
160
|
+
- id: voice-memo # unique across items
|
|
161
|
+
path: materials/voice-memo.md
|
|
162
|
+
produced_by: example-author
|
|
163
|
+
captured: "2026-09-12"
|
|
164
|
+
how: voice memo, transcribed
|
|
165
|
+
trust: raw # raw | considered | verified
|
|
166
|
+
check:
|
|
167
|
+
station: every segment of every material carries a label from the closed set
|
|
168
|
+
source: capture step
|
|
169
|
+
author: agent:claude
|
|
170
|
+
dna:
|
|
171
|
+
writer: example-author
|
|
172
|
+
scope:
|
|
173
|
+
form: essay
|
|
174
|
+
audience: new managers
|
|
175
|
+
purpose: teach
|
|
176
|
+
rules: style-rules.md # your style rules file, the always-on layer
|
|
177
|
+
goldens: # at least one
|
|
178
|
+
- path: goldens/opening.md
|
|
179
|
+
why: one plain claim, then a second sentence that turns it into something to do
|
|
180
|
+
check:
|
|
181
|
+
rubric: blind lineup within this scope
|
|
182
|
+
source: goldens marked on the review page
|
|
183
|
+
author: example-author
|
|
184
|
+
persona:
|
|
185
|
+
identity: self # self | role:<name> | character:<id>
|
|
186
|
+
stance: mentor # peer | mentor | witness | guide; anything else warns
|
|
187
|
+
may_assert:
|
|
188
|
+
- what the author did in their own first one-on-ones
|
|
189
|
+
will_not_say:
|
|
190
|
+
- the name of anyone on the author's team
|
|
191
|
+
facts_from: sources # must be exactly "sources"
|
|
192
|
+
check:
|
|
193
|
+
rubric: persona-consistency judge
|
|
194
|
+
source: persona interview
|
|
195
|
+
author: example-author
|
|
196
|
+
audience:
|
|
197
|
+
who: someone in their first three months of managing
|
|
198
|
+
funnel_now: has a first one-on-one on the calendar this week
|
|
199
|
+
knows:
|
|
200
|
+
- one-on-one
|
|
201
|
+
- report
|
|
202
|
+
believes_now: a one-on-one is where a manager catches up on the work
|
|
203
|
+
wants: a plan for the first meeting
|
|
204
|
+
reads_on: a phone, in the ten minutes before the meeting
|
|
205
|
+
reader: person # person | agent
|
|
206
|
+
check:
|
|
207
|
+
station: term check against knows
|
|
208
|
+
rubric: simulated reader reports where it got lost
|
|
209
|
+
source: audience interview
|
|
210
|
+
author: example-author
|
|
211
|
+
goal:
|
|
212
|
+
from: plans to run the meeting from their own list
|
|
213
|
+
to: hands the meeting to the report
|
|
214
|
+
next_if_worked: copies the three questions into the invite
|
|
215
|
+
change:
|
|
216
|
+
kind: action # belief | action | feeling
|
|
217
|
+
text: the reader asks the three questions and waits
|
|
218
|
+
conditions: # 5 to 10 distinct ids of top-level requirements, each once
|
|
219
|
+
- r1
|
|
220
|
+
- r2
|
|
221
|
+
- r3
|
|
222
|
+
- r4
|
|
223
|
+
- r5
|
|
224
|
+
check:
|
|
225
|
+
rubric: grade the draft against every condition
|
|
226
|
+
source: goal interview
|
|
227
|
+
author: example-author
|
|
228
|
+
form:
|
|
229
|
+
name: essay # open set
|
|
230
|
+
length: # whole numbers, at least 1, min no more than max
|
|
231
|
+
min: 700
|
|
232
|
+
max: 1100
|
|
233
|
+
unit: words
|
|
234
|
+
required_parts:
|
|
235
|
+
- an opening that states the claim
|
|
236
|
+
- the three questions
|
|
237
|
+
- a close
|
|
238
|
+
stations: # may be empty
|
|
239
|
+
- the three questions render as a numbered list
|
|
240
|
+
check:
|
|
241
|
+
station: structure and length check
|
|
242
|
+
source: form decision
|
|
243
|
+
author: example-author
|
|
244
|
+
spine:
|
|
245
|
+
kind: primer # open set
|
|
246
|
+
claims: # 3 to 7, in order, each with its own id
|
|
247
|
+
- id: c1
|
|
248
|
+
text: the first one-on-one is the one meeting the report should set the agenda for
|
|
249
|
+
materials: # ids from materials.items; voice-memo#segment is accepted
|
|
250
|
+
- voice-memo
|
|
251
|
+
- id: c2
|
|
252
|
+
text: status belongs in the tracker
|
|
253
|
+
materials:
|
|
254
|
+
- voice-memo
|
|
255
|
+
- id: c3
|
|
256
|
+
text: three questions are enough to hand the meeting over
|
|
257
|
+
materials:
|
|
258
|
+
- voice-memo
|
|
259
|
+
check:
|
|
260
|
+
rubric: each claim lands, in order, and nothing is argued outside the chain
|
|
261
|
+
source: spine interview
|
|
262
|
+
author: example-author
|
|
263
|
+
sources:
|
|
264
|
+
ledger: claims.jsonl # need not exist before drafting
|
|
265
|
+
unsourced_claim: fail # fail | warn; warn is reported as a warning
|
|
266
|
+
check:
|
|
267
|
+
station: every factual claim points at a source span
|
|
268
|
+
source: sourcing pass
|
|
269
|
+
author: agent:claude
|
|
270
|
+
characters: # required when fiction: true; ids unique
|
|
271
|
+
- id: ines
|
|
272
|
+
entity: world/ines.json # optional; if given, the file must exist
|
|
273
|
+
speech:
|
|
274
|
+
uses:
|
|
275
|
+
- instructions in the imperative
|
|
276
|
+
never:
|
|
277
|
+
- an apology in words
|
|
278
|
+
rhythm: short, flat sentences # optional
|
|
279
|
+
wants: to leave the bakery in hands she trusts
|
|
280
|
+
fears: that he will stay in a trade with no bakery to do it in
|
|
281
|
+
hides: that she sold the bakery two weeks ago
|
|
282
|
+
knowledge: # at least one; each entry needs by and knows
|
|
283
|
+
- by: scene-1
|
|
284
|
+
knows: the sale closes on Friday
|
|
285
|
+
relationships: # optional
|
|
286
|
+
- to: theo
|
|
287
|
+
how: gives him instructions where another person would give praise
|
|
288
|
+
arc_state: has let go of the bakery, and has not yet let go of him
|
|
289
|
+
golden_lines: # at least one
|
|
290
|
+
- Flour first. Then you can talk.
|
|
291
|
+
rejected_lines: # at least one, and none of them also golden
|
|
292
|
+
- I'm so sorry I didn't tell you sooner.
|
|
293
|
+
check:
|
|
294
|
+
rubric: blind attribution test, knowledge-leak check, consistency against golden and rejected lines
|
|
295
|
+
source: notes.md
|
|
296
|
+
author: example-author
|
|
297
|
+
```
|
|
298
|
+
|
|
299
|
+
Every block, and every character, carries `check` (with `station:` for a deterministic check or
|
|
300
|
+
`rubric:` for what a grader applies, the same shape a requirement's check has), `source` and
|
|
301
|
+
`author`. `characters` is a list, so there each entry carries its own.
|
|
302
|
+
|
|
303
|
+
Write every map in block style, one key per line, as above. The YAML reader hyperspec uses reads
|
|
304
|
+
a flow list such as `[r1, r2]`, but it reads an inline map such as `{ station: ... }` as a plain
|
|
305
|
+
string, and a flow list followed by a comment on the same line as a string too.
|
|
306
|
+
|
|
307
|
+
A placeholder counts as missing, here and everywhere else in a hyperspec, so a scaffolded field
|
|
308
|
+
cannot pass a presence check. A placeholder is a whole value, trimmed and in any case, of `todo`,
|
|
309
|
+
`tbd`, `fixme`, `xxx`, `placeholder`, `<placeholder>`, `n/a`, a run of dashes, a run of question
|
|
310
|
+
marks, or an ellipsis, optionally followed by a trailing `.`, `:` or `!`. Real text that starts
|
|
311
|
+
with one of those, such as `TODO: write the opening`, counts as present, and so does `none`.
|
|
312
|
+
|
|
313
|
+
## The test mapping
|
|
314
|
+
|
|
315
|
+
Each row lists what the writing profile adds to that test. The core conditions in
|
|
316
|
+
[SPEC.md](SPEC.md#the-test-to-field-map) still apply alongside them.
|
|
317
|
+
|
|
318
|
+
| Test | A writing spec fails it when |
|
|
319
|
+
|---|---|
|
|
320
|
+
| 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 `stance` outside the four is a warning, and so is `unsourced_claim: warn` |
|
|
321
|
+
| 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 |
|
|
322
|
+
| 3 every requirement names its check | a block or a character has no `check` with a `station` or a `rubric` |
|
|
323
|
+
| 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` |
|
|
324
|
+
| 5 negative space is specified | `persona.will_not_say` is empty; `persona.facts_from` is anything other than `sources` |
|
|
325
|
+
| 6 examples outrank adjectives | a golden has no `why`; a material, `dna.rules`, golden or character `entity` path does not exist or is not a file; a character has no golden lines or no rejected lines, or has the same line in both (compared trimmed and case-folded) |
|
|
326
|
+
| 7 a stranger can resume it | `writing.progress` exists. An unknown `profile:` is a warning |
|
|
327
|
+
| 8 its adopters can push back on it | nothing further; the core rule applies |
|
|
328
|
+
| 9 it improves itself | nothing further; the core rule applies |
|
|
329
|
+
|
|
330
|
+
## Closed sets
|
|
331
|
+
|
|
332
|
+
| Field | Allowed values |
|
|
333
|
+
|---|---|
|
|
334
|
+
| `materials.items[].trust` | `raw`, `considered`, `verified` |
|
|
335
|
+
| `persona.identity` | `self`, `role:<name>`, `character:<id>` |
|
|
336
|
+
| `persona.stance` | `peer`, `mentor`, `witness`, `guide`; any other value warns |
|
|
337
|
+
| `persona.facts_from` | `sources` |
|
|
338
|
+
| `audience.reader` | `person`, `agent` |
|
|
339
|
+
| `goal.change.kind` | `belief`, `action`, `feeling` |
|
|
340
|
+
| `sources.unsourced_claim` | `fail`, `warn` |
|
|
341
|
+
| `fiction` | `true`, `false`; absent means `false` |
|
|
342
|
+
|
|
343
|
+
`form.name` and `spine.kind` are open: name the form and the kind of argument in your own words.
|
|
344
|
+
|
|
345
|
+
## Materials labels
|
|
346
|
+
|
|
347
|
+
Before a material is used, it is split into segments, and each segment gets one of seven labels.
|
|
348
|
+
The label decides what the segment may become in the draft.
|
|
349
|
+
|
|
350
|
+
| Label | Means | May be used as |
|
|
351
|
+
|---|---|---|
|
|
352
|
+
| `claim` | a statement of fact about the world | only with a source, or as the author's own claim said as such |
|
|
353
|
+
| `story` | something that happened, told by someone who was there | testimony, with the teller named |
|
|
354
|
+
| `quote` | words someone said, verbatim | quoted exactly, never paraphrased inside quotation marks |
|
|
355
|
+
| `stance` | an opinion or conviction | the author's position |
|
|
356
|
+
| `question` | something open | a prompt for the interview, never an assertion |
|
|
357
|
+
| `aside` | true but off the thread | held back unless the spine needs it |
|
|
358
|
+
| `private` | not for this audience | never used; kept for context |
|
|
359
|
+
|
|
360
|
+
The linter defines the set once, as `MATERIAL_LABELS` in `src/writing.mjs`. This release checks
|
|
361
|
+
the materials list itself; it does not yet read segment files or check their labels. A spine
|
|
362
|
+
claim may point at a segment as `m1#segment`, and today only the material id before the `#` is
|
|
363
|
+
checked.
|
|
364
|
+
|
|
365
|
+
## Deferring a block
|
|
366
|
+
|
|
367
|
+
A block can be deferred, never silently missing. A required block that is absent fails test 1
|
|
368
|
+
unless a decision with the id `writing-<block>` stands in for it:
|
|
369
|
+
|
|
370
|
+
```yaml
|
|
371
|
+
decisions:
|
|
372
|
+
- id: writing-audience
|
|
373
|
+
state: open
|
|
374
|
+
question: who reads this, and what do they already believe?
|
|
375
|
+
source: kickoff call
|
|
376
|
+
author: agent:claude
|
|
377
|
+
chosen_by: agent
|
|
378
|
+
```
|
|
379
|
+
|
|
380
|
+
- **`open`** defers the block to a question only a person can answer. The spec is blocked on it,
|
|
381
|
+
exactly like any open decision: `lint` exits 3 once every test passes.
|
|
382
|
+
- **`delegated` with a `rule`** defers the block to a standing rule the agent follows. The spec
|
|
383
|
+
can pass. A delegated decision with no `rule` defers nothing, so the missing block still fails.
|
|
384
|
+
|
|
385
|
+
Either way the deferred block does not count toward `writing: k/9 blocks complete`.
|
|
386
|
+
|
|
387
|
+
## Starting a writing spec
|
|
388
|
+
|
|
389
|
+
```bash
|
|
390
|
+
npx @supersuit/hyperspec init essay.hyperspec.md --profile writing --title "Your title" --form essay
|
|
391
|
+
npx @supersuit/hyperspec init story.hyperspec.md --profile writing --form "short story" --fiction
|
|
392
|
+
```
|
|
393
|
+
|
|
394
|
+
The skeleton shows every required block in schema order with every field present as a `TODO`
|
|
395
|
+
placeholder. `dna`, `persona`, `audience` and `goal` also carry an open decision whose question
|
|
396
|
+
says what you have to answer before the placeholder means anything. `--form` sets both `kind:`
|
|
397
|
+
and `writing.form.name`, and defaults to `essay`. `--fiction` sets `fiction: true` and adds one
|
|
398
|
+
character with the same treatment. The skeleton never passes: it lints `fail`, with
|
|
399
|
+
`writing: 1/9 blocks complete` (or `0/9` with `--fiction`), until the placeholders and the open
|
|
400
|
+
decisions are replaced with real content.
|
|
401
|
+
|
|
402
|
+
`init` refuses, with exit 2 and a plain message, anything it would otherwise have to ignore:
|
|
403
|
+
`--profile` with no value or one this linter does not know, `--fiction` or `--form` without
|
|
404
|
+
`--profile writing`, `--kind` with it (the form sets the kind), and a file in a folder that does
|
|
405
|
+
not exist.
|
|
406
|
+
|
|
407
|
+
## Worked examples
|
|
408
|
+
|
|
409
|
+
Two complete specs ship in [`examples/writing/`](examples/writing/), each with every file it
|
|
410
|
+
names:
|
|
411
|
+
|
|
412
|
+
- `essay.hyperspec.md`: an essay for new managers on running a first one-on-one. Three materials
|
|
413
|
+
at three trust levels, scoped DNA with two annotated goldens, a four-claim spine.
|
|
414
|
+
- `story.hyperspec.md`: a short story, `fiction: true`, narrated by one of its two characters.
|
|
415
|
+
Each character has speech rules, a knowledge timeline by scene, and golden and rejected lines
|
|
416
|
+
in a voice you can tell apart from the other's.
|
|
417
|
+
|
|
418
|
+
Both lint `pass (9/9)` with `writing: 9/9 blocks complete` and no findings. A test runs them on
|
|
419
|
+
every release, so they cannot drift from the linter.
|
|
420
|
+
|
|
421
|
+
## What later versions add
|
|
422
|
+
|
|
423
|
+
This release is the schema and its lint. Later versions build on it in order: marking materials
|
|
424
|
+
(a brain dump or transcript in, labeled segments out, with the labels above enforced), scoped
|
|
425
|
+
DNA with annotated goldens filed by form, audience and purpose, and the stations themselves,
|
|
426
|
+
running the checks each block names and grading drafts against the goal.
|