@supersuit/hyperspec 0.2.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/bin/hyperspec.mjs CHANGED
@@ -1,9 +1,12 @@
1
1
  #!/usr/bin/env node
2
- import { existsSync, writeFileSync } from "node:fs";
2
+ import { existsSync, statSync, writeFileSync } from "node:fs";
3
+ import { dirname, resolve } from "node:path";
3
4
  import { loadSpec } from "../src/load.mjs";
4
5
  import { lintSpec } from "../src/rules.mjs";
5
6
  import { score, exitCode } from "../src/score.mjs";
6
7
  import { template } from "../src/template.mjs";
8
+ import { writingTemplate } from "../src/writing-template.mjs";
9
+ import { PROFILES, knownProfile } from "../src/profiles.mjs";
7
10
  import { readRecipe, checkRecipe } from "../src/recipe.mjs";
8
11
  import { approve } from "../src/writer.mjs";
9
12
  import { reproduce } from "../src/reproduce.mjs";
@@ -15,6 +18,17 @@ const HELP = `hyperspec <command> [options]
15
18
  lint <file...> [--json] score each hyperspec against the nine tests
16
19
  exit 0 pass, 1 a test fails, 3 blocked on an open decision, 2 usage
17
20
  init <file> [--title T] [--kind K] write a new hyperspec skeleton (refuses to overwrite)
21
+ init <file> --profile writing [--title T] [--form F] [--fiction]
22
+ write a writing-profile skeleton: every required block (materials,
23
+ dna, persona, audience, goal, form, spine, sources) shown in full
24
+ with placeholder values, dna/persona/audience/goal also carrying an
25
+ open decision naming the question only the operator can answer;
26
+ --fiction adds one character, same treatment; the skeleton never
27
+ passes until its placeholders and open decisions are replaced with
28
+ real content; exit 2 for a --profile with no value or one this
29
+ linter does not know, --fiction or --form without --profile
30
+ writing, --kind with it (use --form), or a folder that does not
31
+ exist
18
32
 
19
33
  recipe check <output-or-recipe> [--json]
20
34
  check a recipe's completeness (a path not ending .recipe.json
@@ -47,11 +61,39 @@ const cmd = argv[0];
47
61
 
48
62
  if (!cmd || cmd === "--help" || cmd === "-h") { console.log(HELP); process.exit(cmd ? 0 : 2); }
49
63
 
64
+ // Each profile that wants its own init skeleton adds one entry here; a profile absent from this
65
+ // map still lints (via PROFILES in profiles.mjs) but init falls back to the plain template for it.
66
+ const PROFILE_TEMPLATES = { writing: writingTemplate };
67
+
50
68
  if (cmd === "init") {
51
69
  const file = argv[1];
52
70
  if (!file || file.startsWith("--")) { console.error("init needs a file path"); process.exit(2); }
53
71
  if (existsSync(file)) { console.error(`refusing to overwrite ${file}`); process.exit(2); }
54
- writeFileSync(file, template({ title: flag("--title"), kind: flag("--kind") }));
72
+ const usage = (msg) => { console.error(msg); process.exit(2); };
73
+ const given = (name) => argv.includes(name);
74
+ // A value flag with nothing after it, or another flag after it, has no value.
75
+ const value = (name) => { const v = flag(name); return v === undefined || v.startsWith("--") ? undefined : v; };
76
+ const profileName = value("--profile");
77
+ if (given("--profile") && profileName === undefined) usage("--profile needs a value; known profiles: " + Object.keys(PROFILES).join(", "));
78
+ if (profileName !== undefined && !knownProfile(profileName)) {
79
+ usage(`unknown profile: ${profileName}; known profiles: ${Object.keys(PROFILES).join(", ") || "(none)"}`);
80
+ }
81
+ const writeTemplate = profileName !== undefined && Object.hasOwn(PROFILE_TEMPLATES, profileName) ? PROFILE_TEMPLATES[profileName] : undefined;
82
+ // The writing flags mean nothing to the plain template, and --kind means nothing to the writing
83
+ // one (its kind comes from --form); either would be silently dropped, so both are refused.
84
+ if (!writeTemplate) {
85
+ for (const f of ["--fiction", "--form"]) if (given(f)) usage(`${f} only applies with --profile writing`);
86
+ } else if (given("--kind")) {
87
+ usage("--kind does not apply with --profile writing; use --form, which sets kind and writing.form.name together");
88
+ }
89
+ if (given("--form") && value("--form") === undefined) usage("--form needs a value");
90
+ const folder = dirname(resolve(file));
91
+ if (!existsSync(folder) || !statSync(folder).isDirectory()) usage(`the folder ${dirname(file)} does not exist; create it first`);
92
+ const content = writeTemplate
93
+ ? writeTemplate({ title: flag("--title"), form: value("--form"), fiction: given("--fiction") })
94
+ : template({ title: flag("--title"), kind: flag("--kind") });
95
+ try { writeFileSync(file, content); }
96
+ catch (e) { usage(`could not write ${file}: ${e.code === "EACCES" ? "permission denied" : e.message}`); }
55
97
  console.log(`wrote ${file}; run: hyperspec lint ${file}`);
56
98
  process.exit(0);
57
99
  }
@@ -79,6 +121,7 @@ if (cmd === "lint") {
79
121
  else for (const r of reports) {
80
122
  if (r.error) { console.log(`${r.file}: ${r.error}`); continue; }
81
123
  console.log(`${r.file}: ${r.status} (${r.passed}/9)${r.open.length ? `, open: ${r.open.join(", ")}` : ""}`);
124
+ if (r.profile) console.log(` ${r.profile.name}: ${r.profile.complete}/${r.profile.total} blocks complete`);
82
125
  for (const t of r.tests) if (!t.pass) console.log(` ✗ ${t.n}. ${t.name}`);
83
126
  for (const f of r.findings) console.log(` ${f.severity === "fail" ? "fail" : "warn"} [${f.test}] ${f.message}\n fix: ${f.fix}`);
84
127
  }
@@ -7,7 +7,7 @@ decisions:
7
7
  state: decided
8
8
  value: the operator, reading on a phone
9
9
  source: interview A2
10
- author: gary-sheng
10
+ author: example-author
11
11
  chosen_by: human
12
12
  - id: length
13
13
  state: delegated
@@ -22,7 +22,7 @@ requirements:
22
22
  check:
23
23
  rubric: ask the simulated reader to define the term; pass only on a correct definition
24
24
  source: design doc, audience block
25
- author: gary-sheng
25
+ author: example-author
26
26
  rejects:
27
27
  - hype words about AI
28
28
  examples:
@@ -0,0 +1,2 @@
1
+ So write the three questions on a card. Ask the first one. Then wait, longer than feels polite,
2
+ because the first answer is the one they rehearsed and the second one is the one you came for.
@@ -0,0 +1,2 @@
1
+ Your first one-on-one with a new report is the only meeting on your calendar where they should
2
+ set the agenda. Everything else you run. This one you hand over.
@@ -0,0 +1,12 @@
1
+ Interview notes, a thirty-minute call with an engineering manager of eight years, taken by the
2
+ author during the call. Considered: the manager reviewed these notes afterwards and corrected two
3
+ lines.
4
+
5
+ - Her rule: the report owns the agenda. She keeps a shared document per person; they add items
6
+ before the meeting, and she adds hers last, at the bottom.
7
+ - "If I have something urgent, it is not a one-on-one topic. I send it the day it happens."
8
+ - The first one-on-one with a new report is always the same: she asks how they like to receive
9
+ feedback, in writing or out loud, right away or at the end of the week.
10
+ - Her warning: new managers treat silence as a problem. "Wait. Count to five. The real answer is
11
+ the second one."
12
+ - She cancels a one-on-one only when the report asks her to, never on her own.
@@ -0,0 +1,7 @@
1
+ Summary of an internal survey the author's team ran in the spring, 41 responses, figures checked
2
+ against the raw export by a second person. Verified.
3
+
4
+ - 29 of 41 said their most useful one-on-one in the last quarter was one where they brought the
5
+ first topic.
6
+ - 11 of 41 said at least one of their one-on-ones in the last quarter was mostly project status.
7
+ - The most common free-text request, in 9 responses: "ask me what I want to work on next."
@@ -0,0 +1,18 @@
1
+ Voice memo transcript, recorded by the author on a walk, lightly cleaned. Raw thinking.
2
+
3
+ My first one-on-one as a manager was a disaster, and the reason was simple: I ran it. I had a
4
+ list. I went down the list. Project status, blockers, the thing from Tuesday. Thirty minutes
5
+ later my report said "cool, thanks" and left, and I had learned nothing I could not have read in
6
+ the tracker.
7
+
8
+ What I wish someone had told me: the first one-on-one is the only meeting where they get to set
9
+ the agenda. If you set it, you have told them what the meeting is for, and it is for you.
10
+
11
+ The three questions I use now. What is taking more of your energy than it should? What do you
12
+ want to be doing more of in six months? What should I stop doing, or start doing, that would
13
+ make your week easier? Then I shut up.
14
+
15
+ Status goes in the tracker. If a one-on-one is a status meeting, cancel it and read the tracker.
16
+
17
+ Aside, probably not for this piece: my second manager used to walk the one-on-ones outside. I
18
+ liked it but I do not think it is the point.
File without changes
@@ -0,0 +1,217 @@
1
+ ---
2
+ hyperspec: "0.1"
3
+ title: Hand your first one-on-one to the person you manage
4
+ kind: essay
5
+ profile: writing
6
+ decisions:
7
+ - id: kind
8
+ state: decided
9
+ value: an essay of 700 to 1,100 words for a newsletter read by people in their first year of managing
10
+ source: essay/materials/voice-memo.md
11
+ author: example-author
12
+ chosen_by: human
13
+ - id: agenda-card
14
+ state: decided
15
+ value: the essay ends on the three questions, written so a reader can copy them onto a card
16
+ source: essay/materials/voice-memo.md, the three questions
17
+ author: example-author
18
+ chosen_by: human
19
+ - id: publish-venue
20
+ state: delegated
21
+ rule: publish where the audience block's reads_on line says the reader already is, and nowhere else
22
+ source: publishing checklist
23
+ author: agent:claude
24
+ chosen_by: agent
25
+ requirements:
26
+ - id: r1
27
+ text: the opening line tells the reader who sets the agenda of a first one-on-one
28
+ fails_when: a reader shown only the first two sentences cannot say who should set the agenda
29
+ check:
30
+ rubric: show the simulated reader the first two sentences and ask who sets the agenda; pass only on "the report"
31
+ source: essay/goldens/opening.md
32
+ author: example-author
33
+ - id: r2
34
+ text: the three questions appear word for word as the voice memo states them
35
+ fails_when: any of the three questions differs from essay/materials/voice-memo.md by a word
36
+ check:
37
+ station: verbatim match of each question against the voice memo
38
+ source: essay/materials/voice-memo.md
39
+ author: example-author
40
+ - id: r3
41
+ text: every survey figure in the draft matches the verified survey summary
42
+ fails_when: a figure in the draft has no entry in the claims ledger pointing at essay/materials/team-survey.md, or differs from it
43
+ check:
44
+ station: every factual claim in the ledger points at a source span
45
+ source: sourcing pass
46
+ author: agent:claude
47
+ - id: r4
48
+ text: the draft argues only the four claims in the spine, in order
49
+ fails_when: a paragraph advances a point that traces to none of c1 to c4, or c3 lands before c2
50
+ check:
51
+ rubric: map each paragraph to a spine claim; fail on any paragraph that maps to none or out of order
52
+ source: spine interview
53
+ author: example-author
54
+ - id: r5
55
+ text: the draft tells the reader what to do with silence in the meeting
56
+ fails_when: the draft never says to wait after asking a question
57
+ check:
58
+ rubric: ask the simulated reader what to do after asking the first question; pass only on "wait"
59
+ source: essay/materials/interview-notes.md
60
+ author: example-author
61
+ - id: r6
62
+ text: the draft stays inside its length envelope
63
+ fails_when: the word count is under 700 or over 1,100
64
+ check:
65
+ station: word count against form.length
66
+ source: form decision
67
+ author: example-author
68
+ rejects:
69
+ - a list of more than three questions
70
+ - advice to use the one-on-one for project status
71
+ - any claim about what most managers do that the survey does not support
72
+ - the walking one-on-one aside from the voice memo
73
+ examples:
74
+ - path: essay/goldens/opening.md
75
+ why: the claim lands in the first sentence, and the second sentence turns it into an instruction
76
+ resume:
77
+ next_action: mark the three materials into labeled segments, then outline the four spine claims against the form's required parts
78
+ feedback:
79
+ issues: https://github.com/SupersuitUp/hyperspec/issues
80
+ fork: MIT; fork it for your own purposes
81
+ improvement:
82
+ ledger: essay/runs.jsonl
83
+ writing:
84
+ materials:
85
+ items:
86
+ - id: voice-memo
87
+ path: essay/materials/voice-memo.md
88
+ produced_by: example-author
89
+ captured: "2026-09-12"
90
+ how: voice memo, transcribed
91
+ trust: raw
92
+ - id: interview
93
+ path: essay/materials/interview-notes.md
94
+ produced_by: example-author
95
+ captured: "2026-09-15"
96
+ how: notes taken during a call, reviewed by the person interviewed
97
+ trust: considered
98
+ - id: survey
99
+ path: essay/materials/team-survey.md
100
+ produced_by: example-author
101
+ captured: "2026-05-30"
102
+ how: survey summary, figures checked against the raw export by a second person
103
+ trust: verified
104
+ check:
105
+ station: every segment of every material carries a label from the closed set
106
+ source: capture step
107
+ author: agent:claude
108
+ dna:
109
+ writer: example-author
110
+ scope:
111
+ form: essay
112
+ audience: new managers
113
+ purpose: teach
114
+ rules: style-rules.md
115
+ goldens:
116
+ - path: essay/goldens/opening.md
117
+ why: one plain claim, then a second sentence that turns it into something to do
118
+ - path: essay/goldens/close.md
119
+ why: ends on an instruction and gives the reason for it in the same sentence
120
+ check:
121
+ rubric: blind lineup within this scope; a judge shown the generated opening beside the two goldens cannot pick it out
122
+ source: goldens marked on the review page
123
+ author: example-author
124
+ persona:
125
+ identity: self
126
+ stance: mentor
127
+ may_assert:
128
+ - what the author did in their own first one-on-ones and what happened
129
+ - the three questions the author uses now
130
+ will_not_say:
131
+ - a claim about what most managers do, beyond the survey's own figures
132
+ - the name of anyone on the author's team
133
+ facts_from: sources
134
+ check:
135
+ rubric: persona-consistency judge; the mentor stance holds, and no fact appears that is not in the claims ledger
136
+ source: persona interview
137
+ author: example-author
138
+ audience:
139
+ who: someone in their first three months of managing, who was promoted from the team they now lead
140
+ funnel_now: has a first one-on-one with a new report on the calendar this week
141
+ knows:
142
+ - one-on-one
143
+ - report
144
+ - tracker
145
+ believes_now: a one-on-one is where a manager catches up on how the work is going
146
+ wants: a plan for the first meeting that will not waste either person's half hour
147
+ reads_on: a phone, in the ten minutes before the meeting
148
+ reader: person
149
+ check:
150
+ station: term check against knows; any other term is defined on first use
151
+ rubric: simulated reader reports where it got lost and where it stopped reading
152
+ source: audience interview
153
+ author: example-author
154
+ goal:
155
+ from: plans to run the first one-on-one from their own list
156
+ to: hands the first one-on-one to the report and asks the three questions
157
+ next_if_worked: copies the three questions into their calendar invite
158
+ change:
159
+ kind: action
160
+ text: the reader asks the three questions in their next one-on-one and waits after each
161
+ conditions: [r1, r2, r3, r4, r5, r6]
162
+ check:
163
+ rubric: the doctor grades the draft against every condition; the simulated reader is asked whether it would copy the questions now
164
+ source: goal interview
165
+ author: example-author
166
+ form:
167
+ name: essay
168
+ length:
169
+ min: 700
170
+ max: 1100
171
+ unit: words
172
+ required_parts:
173
+ - an opening that states the claim
174
+ - the story of the author's first one-on-one
175
+ - the three questions
176
+ - what to do with the answers
177
+ - a close the reader can act on
178
+ stations:
179
+ - the three questions render as a numbered list
180
+ check:
181
+ station: structure and length check against required_parts and length
182
+ source: form decision
183
+ author: example-author
184
+ spine:
185
+ kind: primer
186
+ claims:
187
+ - id: c1
188
+ text: the first one-on-one is the one meeting where the report should set the agenda
189
+ materials: [voice-memo, interview]
190
+ - id: c2
191
+ text: status belongs in the tracker, and a one-on-one spent on it teaches the manager nothing new
192
+ materials: [voice-memo, survey]
193
+ - id: c3
194
+ text: three questions are enough to hand the meeting over
195
+ materials: [voice-memo]
196
+ - id: c4
197
+ text: the answer worth having comes after a silence the manager does not fill
198
+ materials: [interview]
199
+ check:
200
+ rubric: each claim lands, in order, and the draft argues nothing outside the chain
201
+ source: spine interview
202
+ author: example-author
203
+ sources:
204
+ ledger: essay/claims.jsonl
205
+ unsourced_claim: fail
206
+ check:
207
+ station: every factual claim in the ledger points at a source span; every quote matches its source verbatim
208
+ source: sourcing pass
209
+ author: agent:claude
210
+ fiction: false
211
+ ---
212
+
213
+ # Hand your first one-on-one to the person you manage
214
+
215
+ A worked example of the writing profile: an essay for new managers, specified before a word of
216
+ it is drafted. Every file this spec names ships beside it. `essay/claims.jsonl` does not exist
217
+ yet, because the claims ledger is written during drafting.
@@ -0,0 +1,3 @@
1
+ "You're late," she said. I wasn't. She knew I wasn't.
2
+ "The bus," I said anyway, because that is how we start.
3
+ "Flour first. Then you can talk."
@@ -0,0 +1,3 @@
1
+ The alarm at the bakery goes at 3:20, but Ines is always there first, so I have never once
2
+ heard it. What I hear is the mixer. It has a knock in it, a slow one, like it is trying to
3
+ remember something.
@@ -0,0 +1,8 @@
1
+ Notes from a morning spent at a working bakery, 3:30 to 7:30, with the owner's permission.
2
+ Considered: the owner read these notes and corrected the proofing times.
3
+
4
+ - First mix at 3:45. Rye sourdough proofs about three hours at room temperature in winter.
5
+ - Trays are turned halfway through the bake because the back left of a deck oven runs hot.
6
+ - The doors open to customers at 7:00. The first person in is usually a regular.
7
+ - The owner does not talk while shaping. Talking happens at the mixer and at the till.
8
+ - Flour is weighed, never scooped. Water temperature is checked every batch.
@@ -0,0 +1,14 @@
1
+ Author's notes for the story, typed over two evenings. Raw thinking.
2
+
3
+ A bakery on its last morning before the sale closes. Two people: the owner, who has run it for
4
+ thirty-one years, and the apprentice she took on at sixteen, now nineteen. She has not told him
5
+ it is sold. He has not told her he got into a baking school in another city and leaves in the
6
+ autumn. Each thinks they are protecting the other.
7
+
8
+ The story is four scenes, one morning, 3:40 to 7:00. The oven has a noise. The rye is the thing
9
+ she has never let him do alone.
10
+
11
+ What it is about, I think: a craft outlives the room it was practiced in. And: people who love
12
+ each other in a work setting say it through the work.
13
+
14
+ The last line should be about bread, never about feelings.
@@ -0,0 +1,7 @@
1
+ Scene list, agreed with the editor before drafting. Considered.
2
+
3
+ - scene-1, 3:40: Theo arrives. Ines is already mixing. The oven noise. No one says anything real.
4
+ - scene-2, 4:30: shaping. Ines lets Theo shape the rye for the first time, and does not say why.
5
+ - scene-3, 5:50: the bake. Ines tells Theo the bakery is sold, while turning a tray.
6
+ - scene-4, 6:55: before the doors open. Theo tells Ines about the school. She hands him the
7
+ starter jar.
File without changes