@supersuit/hyperspec 0.2.0 → 0.4.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (43) hide show
  1. package/CHANGELOG.md +119 -0
  2. package/README.md +53 -3
  3. package/SPEC.md +24 -10
  4. package/WRITING.md +597 -0
  5. package/bin/hyperspec.mjs +94 -3
  6. package/examples/minimal.hyperspec.md +2 -2
  7. package/examples/writing/essay/goldens/close.md +2 -0
  8. package/examples/writing/essay/goldens/opening.md +2 -0
  9. package/examples/writing/essay/materials/interview-notes.md +12 -0
  10. package/examples/writing/essay/materials/interview-notes.md.segments.jsonl +10 -0
  11. package/examples/writing/essay/materials/team-survey.md +7 -0
  12. package/examples/writing/essay/materials/team-survey.md.segments.jsonl +6 -0
  13. package/examples/writing/essay/materials/voice-memo.md +18 -0
  14. package/examples/writing/essay/materials/voice-memo.md.segments.jsonl +7 -0
  15. package/examples/writing/essay/runs.jsonl +0 -0
  16. package/examples/writing/essay.hyperspec.md +220 -0
  17. package/examples/writing/story/goldens/dialogue.md +3 -0
  18. package/examples/writing/story/goldens/opening.md +3 -0
  19. package/examples/writing/story/materials/bakery-visit.md +9 -0
  20. package/examples/writing/story/materials/bakery-visit.md.segments.jsonl +13 -0
  21. package/examples/writing/story/materials/notes.md +16 -0
  22. package/examples/writing/story/materials/notes.md.segments.jsonl +7 -0
  23. package/examples/writing/story/materials/scene-list.md +7 -0
  24. package/examples/writing/story/materials/scene-list.md.segments.jsonl +14 -0
  25. package/examples/writing/story/runs.jsonl +0 -0
  26. package/examples/writing/story.hyperspec.md +288 -0
  27. package/examples/writing/style-rules.md +19 -0
  28. package/package.json +4 -2
  29. package/src/blobs.mjs +1 -1
  30. package/src/compare.mjs +6 -6
  31. package/src/fsutil.mjs +1 -1
  32. package/src/labels.mjs +6 -0
  33. package/src/placeholder.mjs +20 -0
  34. package/src/profiles.mjs +50 -0
  35. package/src/reproduce.mjs +5 -5
  36. package/src/rules.mjs +25 -13
  37. package/src/score.mjs +7 -1
  38. package/src/segments.mjs +407 -0
  39. package/src/template.mjs +4 -1
  40. package/src/writing-exports.mjs +6 -0
  41. package/src/writing-fields.mjs +418 -0
  42. package/src/writing-template.mjs +199 -0
  43. package/src/writing.mjs +181 -0
package/bin/hyperspec.mjs CHANGED
@@ -1,20 +1,47 @@
1
1
  #!/usr/bin/env node
2
- import { existsSync, writeFileSync } from "node:fs";
2
+ import { existsSync, statSync, writeFileSync, readFileSync } 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";
10
13
  import { regenerate } from "../src/regenerate.mjs";
11
14
  import { compare } from "../src/compare.mjs";
15
+ import { splitSegments } from "../src/segments.mjs";
16
+ import { sha256 } from "../src/hash.mjs";
12
17
 
13
18
  const HELP = `hyperspec <command> [options]
14
19
 
15
20
  lint <file...> [--json] score each hyperspec against the nine tests
16
21
  exit 0 pass, 1 a test fails, 3 blocked on an open decision, 2 usage
17
22
  init <file> [--title T] [--kind K] write a new hyperspec skeleton (refuses to overwrite)
23
+ init <file> --profile writing [--title T] [--form F] [--fiction]
24
+ write a writing-profile skeleton: every required block (materials,
25
+ dna, persona, audience, goal, form, spine, sources) shown in full
26
+ with placeholder values, dna/persona/audience/goal also carrying an
27
+ open decision naming the question only the operator can answer;
28
+ --fiction adds one character, same treatment; the skeleton never
29
+ passes until its placeholders and open decisions are replaced with
30
+ real content; exit 2 for a --profile with no value or one this
31
+ linter does not know, --fiction or --form without --profile
32
+ writing, --kind with it (use --form), or a folder that does not
33
+ exist
34
+
35
+ segments init <material> --id <mid> [--out <file>] [--by paragraph|sentence]
36
+ split a material into candidate segments, written as JSONL to
37
+ <material>.segments.jsonl by default; every segment starts label:
38
+ unlabeled, never valid in lint; label each one by hand (claim,
39
+ story, quote, stance, question, aside, private), then run hyperspec
40
+ lint on the spec; --by sentence also starts a segment at each list
41
+ item (-, *, +, 1. or 1) then a space); refuses to overwrite an
42
+ existing file (exit 2); exit 2 for a missing material, a material
43
+ with nothing in it, an --out folder that does not exist, or a --by
44
+ outside paragraph/sentence
18
45
 
19
46
  recipe check <output-or-recipe> [--json]
20
47
  check a recipe's completeness (a path not ending .recipe.json
@@ -47,15 +74,78 @@ const cmd = argv[0];
47
74
 
48
75
  if (!cmd || cmd === "--help" || cmd === "-h") { console.log(HELP); process.exit(cmd ? 0 : 2); }
49
76
 
77
+ // Each profile that wants its own init skeleton adds one entry here; a profile absent from this
78
+ // map still lints (via PROFILES in profiles.mjs) but init falls back to the plain template for it.
79
+ const PROFILE_TEMPLATES = { writing: writingTemplate };
80
+
50
81
  if (cmd === "init") {
51
82
  const file = argv[1];
52
83
  if (!file || file.startsWith("--")) { console.error("init needs a file path"); process.exit(2); }
53
84
  if (existsSync(file)) { console.error(`refusing to overwrite ${file}`); process.exit(2); }
54
- writeFileSync(file, template({ title: flag("--title"), kind: flag("--kind") }));
85
+ const usage = (msg) => { console.error(msg); process.exit(2); };
86
+ const given = (name) => argv.includes(name);
87
+ // A value flag with nothing after it, or another flag after it, has no value.
88
+ const value = (name) => { const v = flag(name); return v === undefined || v.startsWith("--") ? undefined : v; };
89
+ const profileName = value("--profile");
90
+ if (given("--profile") && profileName === undefined) usage("--profile needs a value; known profiles: " + Object.keys(PROFILES).join(", "));
91
+ if (profileName !== undefined && !knownProfile(profileName)) {
92
+ usage(`unknown profile: ${profileName}; known profiles: ${Object.keys(PROFILES).join(", ") || "(none)"}`);
93
+ }
94
+ const writeTemplate = profileName !== undefined && Object.hasOwn(PROFILE_TEMPLATES, profileName) ? PROFILE_TEMPLATES[profileName] : undefined;
95
+ // The writing flags mean nothing to the plain template, and --kind means nothing to the writing
96
+ // one (its kind comes from --form); either would be silently dropped, so both are refused.
97
+ if (!writeTemplate) {
98
+ for (const f of ["--fiction", "--form"]) if (given(f)) usage(`${f} only applies with --profile writing`);
99
+ } else if (given("--kind")) {
100
+ usage("--kind does not apply with --profile writing; use --form, which sets kind and writing.form.name together");
101
+ }
102
+ if (given("--form") && value("--form") === undefined) usage("--form needs a value");
103
+ const folder = dirname(resolve(file));
104
+ if (!existsSync(folder) || !statSync(folder).isDirectory()) usage(`the folder ${dirname(file)} does not exist; create it first`);
105
+ const content = writeTemplate
106
+ ? writeTemplate({ title: flag("--title"), form: value("--form"), fiction: given("--fiction") })
107
+ : template({ title: flag("--title"), kind: flag("--kind") });
108
+ try { writeFileSync(file, content); }
109
+ catch (e) { usage(`could not write ${file}: ${e.code === "EACCES" ? "permission denied" : e.message}`); }
55
110
  console.log(`wrote ${file}; run: hyperspec lint ${file}`);
56
111
  process.exit(0);
57
112
  }
58
113
 
114
+ if (cmd === "segments") {
115
+ const sub = argv[1];
116
+
117
+ if (sub === "init") {
118
+ const parsed = parseArgs(argv.slice(2), { valueFlags: ["--id", "--out", "--by"] });
119
+ if (parsed.error) { console.error(parsed.error); process.exit(2); }
120
+ const [material] = parsed.positionals;
121
+ if (!material) { console.error("segments init needs a material path"); process.exit(2); }
122
+ if (!parsed.values["--id"]) { console.error("segments init needs --id <material id>"); process.exit(2); }
123
+ const id = parsed.values["--id"];
124
+ const by = parsed.values["--by"] ?? "paragraph";
125
+ if (by !== "paragraph" && by !== "sentence") { console.error(`--by must be paragraph or sentence, not "${by}"`); process.exit(2); }
126
+ let materialStat;
127
+ try { materialStat = statSync(material); } catch { materialStat = null; }
128
+ if (!materialStat || !materialStat.isFile()) { console.error(`material not found: ${material}`); process.exit(2); }
129
+ const out = parsed.values["--out"] ?? `${material}.segments.jsonl`;
130
+ if (existsSync(out)) { console.error(`refusing to overwrite ${out}`); process.exit(2); }
131
+ const outFolder = dirname(resolve(out));
132
+ if (!existsSync(outFolder) || !statSync(outFolder).isDirectory()) { console.error(`folder does not exist: ${dirname(out)}; create it first`); process.exit(2); }
133
+
134
+ const buf = readFileSync(material);
135
+ const text = buf.toString("utf8");
136
+ if (!/\S/.test(text)) { console.error(`nothing to mark: ${material} has no text`); process.exit(2); }
137
+ const segments = splitSegments(text, { by });
138
+ const header = { material: id, path: material, sha256: sha256(buf) };
139
+ const lines = [JSON.stringify(header), ...segments.map((s) => JSON.stringify(s))];
140
+ writeFileSync(out, `${lines.join("\n")}\n`);
141
+ console.log(`${segments.length} segments written to ${out}. Label every segment (claim, story, quote, stance, question, aside, private), then run hyperspec lint on the spec.`);
142
+ process.exit(0);
143
+ }
144
+
145
+ console.error(`unknown segments subcommand: ${sub}\n\n${HELP}`);
146
+ process.exit(2);
147
+ }
148
+
59
149
  if (cmd === "lint") {
60
150
  const json = argv.includes("--json");
61
151
  // Lint takes one flag, --json, and no flag takes a value, so a flag is dropped on its own and
@@ -79,6 +169,7 @@ if (cmd === "lint") {
79
169
  else for (const r of reports) {
80
170
  if (r.error) { console.log(`${r.file}: ${r.error}`); continue; }
81
171
  console.log(`${r.file}: ${r.status} (${r.passed}/9)${r.open.length ? `, open: ${r.open.join(", ")}` : ""}`);
172
+ if (r.profile) console.log(` ${r.profile.name}: ${r.profile.complete}/${r.profile.total} blocks complete`);
82
173
  for (const t of r.tests) if (!t.pass) console.log(` ✗ ${t.n}. ${t.name}`);
83
174
  for (const f of r.findings) console.log(` ${f.severity === "fail" ? "fail" : "warn"} [${f.test}] ${f.message}\n fix: ${f.fix}`);
84
175
  }
@@ -316,7 +407,7 @@ if (cmd === "compare") {
316
407
  if (json) console.log(JSON.stringify(result, null, 2));
317
408
 
318
409
  // Both a usage error and a check-failed error (missing output, escaping path, ...) map to
319
- // 2 here — compare's own ok:false without a usage flag is still "could not grade", i.e. an
410
+ // 2 here: compare's own ok:false without a usage flag is still "could not grade", i.e. an
320
411
  // unreadable-input class failure, not a graded-but-worse-1 class one.
321
412
  if (!result.ok) {
322
413
  if (!json) console.error(result.error);
@@ -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,10 @@
1
+ {"material":"interview","path":"essay/materials/interview-notes.md","sha256":"3c4fd3ff19435027b1bb9584e648128d59feb3184f67eeaf6b09ed9fa8bcfaeb"}
2
+ {"id":"s1","start":0,"end":118,"label":"aside","text":"Interview notes, a thirty-minute call with an engineering manager of eight years, taken by the\nauthor during the call."}
3
+ {"id":"s2","start":119,"end":199,"label":"aside","text":"Considered: the manager reviewed these notes afterwards and corrected two\nlines."}
4
+ {"id":"s3","start":201,"end":240,"label":"claim","source":"interview with an engineering manager of eight years","text":"- Her rule: the report owns the agenda."}
5
+ {"id":"s4","start":241,"end":356,"label":"claim","source":"interview with an engineering manager of eight years","text":"She keeps a shared document per person; they add items\n before the meeting, and she adds hers last, at the bottom."}
6
+ {"id":"s5","start":357,"end":448,"label":"quote","speaker":"the engineering manager interviewed","text":"- \"If I have something urgent, it is not a one-on-one topic. I send it the day it happens.\""}
7
+ {"id":"s6","start":449,"end":617,"label":"claim","source":"interview with an engineering manager of eight years","text":"- The first one-on-one with a new report is always the same: she asks how they like to receive\n feedback, in writing or out loud, right away or at the end of the week."}
8
+ {"id":"s7","start":618,"end":673,"label":"claim","source":"interview with an engineering manager of eight years","text":"- Her warning: new managers treat silence as a problem."}
9
+ {"id":"s8","start":674,"end":733,"label":"quote","speaker":"the engineering manager interviewed","text":"\"Wait. Count to five. The real answer is\n the second one.\""}
10
+ {"id":"s9","start":734,"end":812,"label":"claim","source":"interview with an engineering manager of eight years","text":"- 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,6 @@
1
+ {"material":"survey","path":"essay/materials/team-survey.md","sha256":"ae2c821b1731ad02eb3280c28872aa9d2163728b868f262554d0cbfd3da19b97"}
2
+ {"id":"s1","start":0,"end":139,"label":"aside","text":"Summary of an internal survey the author's team ran in the spring, 41 responses, figures checked\nagainst the raw export by a second person."}
3
+ {"id":"s2","start":140,"end":149,"label":"aside","text":"Verified."}
4
+ {"id":"s3","start":151,"end":261,"label":"claim","source":"team survey, spring, 41 responses, figures checked against the raw export","text":"- 29 of 41 said their most useful one-on-one in the last quarter was one where they brought the\n first topic."}
5
+ {"id":"s4","start":262,"end":358,"label":"claim","source":"team survey, spring, 41 responses, figures checked against the raw export","text":"- 11 of 41 said at least one of their one-on-ones in the last quarter was mostly project status."}
6
+ {"id":"s5","start":359,"end":449,"label":"claim","source":"team survey, spring, 41 responses, figures checked against the raw export","text":"- 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.
@@ -0,0 +1,7 @@
1
+ {"material":"voice-memo","path":"essay/materials/voice-memo.md","sha256":"c1e07e7d6886f0f3abe638500b614a94883b66f259d272b213f7fe63e70afdc8"}
2
+ {"id":"s1","start":0,"end":87,"label":"aside","text":"Voice memo transcript, recorded by the author on a walk, lightly cleaned. Raw thinking."}
3
+ {"id":"s2","start":89,"end":385,"label":"story","teller":"example-author","text":"My first one-on-one as a manager was a disaster, and the reason was simple: I ran it. I had a\nlist. I went down the list. Project status, blockers, the thing from Tuesday. Thirty minutes\nlater my report said \"cool, thanks\" and left, and I had learned nothing I could not have read in\nthe tracker."}
4
+ {"id":"s3","start":387,"end":572,"label":"claim","own":true,"text":"What I wish someone had told me: the first one-on-one is the only meeting where they get to set\nthe agenda. If you set it, you have told them what the meeting is for, and it is for you."}
5
+ {"id":"s4","start":574,"end":799,"label":"claim","own":true,"text":"The three questions I use now. What is taking more of your energy than it should? What do you\nwant to be doing more of in six months? What should I stop doing, or start doing, that would\nmake your week easier? Then I shut up."}
6
+ {"id":"s5","start":801,"end":897,"label":"stance","text":"Status goes in the tracker. If a one-on-one is a status meeting, cancel it and read the tracker."}
7
+ {"id":"s6","start":899,"end":1037,"label":"aside","text":"Aside, probably not for this piece: my second manager used to walk the one-on-ones outside. I\nliked it but I do not think it is the point."}
File without changes
@@ -0,0 +1,220 @@
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: outline the four spine claims against the form's required parts, citing the segments each claim points at
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
+ segments: essay/materials/voice-memo.md.segments.jsonl
89
+ produced_by: example-author
90
+ captured: "2026-09-12"
91
+ how: voice memo, transcribed
92
+ trust: raw
93
+ - id: interview
94
+ path: essay/materials/interview-notes.md
95
+ segments: essay/materials/interview-notes.md.segments.jsonl
96
+ produced_by: example-author
97
+ captured: "2026-09-15"
98
+ how: notes taken during a call, reviewed by the person interviewed
99
+ trust: considered
100
+ - id: survey
101
+ path: essay/materials/team-survey.md
102
+ segments: essay/materials/team-survey.md.segments.jsonl
103
+ produced_by: example-author
104
+ captured: "2026-05-30"
105
+ how: survey summary, figures checked against the raw export by a second person
106
+ trust: verified
107
+ check:
108
+ station: every segment of every material carries a label from the closed set, matches its source verbatim, and the markings are current
109
+ source: capture step
110
+ author: agent:claude
111
+ dna:
112
+ writer: example-author
113
+ scope:
114
+ form: essay
115
+ audience: new managers
116
+ purpose: teach
117
+ rules: style-rules.md
118
+ goldens:
119
+ - path: essay/goldens/opening.md
120
+ why: one plain claim, then a second sentence that turns it into something to do
121
+ - path: essay/goldens/close.md
122
+ why: ends on an instruction and gives the reason for it in the same sentence
123
+ check:
124
+ rubric: blind lineup within this scope; a judge shown the generated opening beside the two goldens cannot pick it out
125
+ source: goldens marked on the review page
126
+ author: example-author
127
+ persona:
128
+ identity: self
129
+ stance: mentor
130
+ may_assert:
131
+ - what the author did in their own first one-on-ones and what happened
132
+ - the three questions the author uses now
133
+ will_not_say:
134
+ - a claim about what most managers do, beyond the survey's own figures
135
+ - the name of anyone on the author's team
136
+ facts_from: sources
137
+ check:
138
+ rubric: persona-consistency judge; the mentor stance holds, and no fact appears that is not in the claims ledger
139
+ source: persona interview
140
+ author: example-author
141
+ audience:
142
+ who: someone in their first three months of managing, who was promoted from the team they now lead
143
+ funnel_now: has a first one-on-one with a new report on the calendar this week
144
+ knows:
145
+ - one-on-one
146
+ - report
147
+ - tracker
148
+ believes_now: a one-on-one is where a manager catches up on how the work is going
149
+ wants: a plan for the first meeting that will not waste either person's half hour
150
+ reads_on: a phone, in the ten minutes before the meeting
151
+ reader: person
152
+ check:
153
+ station: term check against knows; any other term is defined on first use
154
+ rubric: simulated reader reports where it got lost and where it stopped reading
155
+ source: audience interview
156
+ author: example-author
157
+ goal:
158
+ from: plans to run the first one-on-one from their own list
159
+ to: hands the first one-on-one to the report and asks the three questions
160
+ next_if_worked: copies the three questions into their calendar invite
161
+ change:
162
+ kind: action
163
+ text: the reader asks the three questions in their next one-on-one and waits after each
164
+ conditions: [r1, r2, r3, r4, r5, r6]
165
+ check:
166
+ rubric: the doctor grades the draft against every condition; the simulated reader is asked whether it would copy the questions now
167
+ source: goal interview
168
+ author: example-author
169
+ form:
170
+ name: essay
171
+ length:
172
+ min: 700
173
+ max: 1100
174
+ unit: words
175
+ required_parts:
176
+ - an opening that states the claim
177
+ - the story of the author's first one-on-one
178
+ - the three questions
179
+ - what to do with the answers
180
+ - a close the reader can act on
181
+ stations:
182
+ - the three questions render as a numbered list
183
+ check:
184
+ station: structure and length check against required_parts and length
185
+ source: form decision
186
+ author: example-author
187
+ spine:
188
+ kind: primer
189
+ claims:
190
+ - id: c1
191
+ text: the first one-on-one is the one meeting where the report should set the agenda
192
+ materials: [voice-memo#s3, interview#s3]
193
+ - id: c2
194
+ text: status belongs in the tracker, and a one-on-one spent on it teaches the manager nothing new
195
+ materials: [voice-memo#s2, voice-memo#s5, survey#s4]
196
+ - id: c3
197
+ text: three questions are enough to hand the meeting over
198
+ materials: [voice-memo#s4]
199
+ - id: c4
200
+ text: the answer worth having comes after a silence the manager does not fill
201
+ materials: [interview#s7, interview#s8]
202
+ check:
203
+ rubric: each claim lands, in order, and the draft argues nothing outside the chain
204
+ source: spine interview
205
+ author: example-author
206
+ sources:
207
+ ledger: essay/claims.jsonl
208
+ unsourced_claim: fail
209
+ check:
210
+ station: every factual claim in the ledger points at a source span; every quote matches its source verbatim
211
+ source: sourcing pass
212
+ author: agent:claude
213
+ fiction: false
214
+ ---
215
+
216
+ # Hand your first one-on-one to the person you manage
217
+
218
+ A worked example of the writing profile: an essay for new managers, specified before a word of
219
+ it is drafted. Every file this spec names ships beside it. `essay/claims.jsonl` does not exist
220
+ 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,9 @@
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.
9
+ - Said in confidence, not for the story: the lease ends next spring, and she has not told her staff.
@@ -0,0 +1,13 @@
1
+ {"material":"bakery-visit","path":"story/materials/bakery-visit.md","sha256":"5eb09f7c0ad8836378c549392d939f0b070669e972c9e4d94e1f81f27465a19b"}
2
+ {"id":"s1","start":0,"end":90,"label":"aside","text":"Notes from a morning spent at a working bakery, 3:30 to 7:30, with the owner's permission."}
3
+ {"id":"s2","start":91,"end":163,"label":"aside","text":"Considered: the owner read these notes and corrected the proofing times."}
4
+ {"id":"s3","start":165,"end":185,"label":"claim","source":"a morning observed at a working bakery, notes corrected by the owner","text":"- First mix at 3:45."}
5
+ {"id":"s4","start":186,"end":255,"label":"claim","source":"a morning observed at a working bakery, notes corrected by the owner","text":"Rye sourdough proofs about three hours at room temperature in winter."}
6
+ {"id":"s5","start":256,"end":346,"label":"claim","source":"a morning observed at a working bakery, notes corrected by the owner","text":"- Trays are turned halfway through the bake because the back left of a deck oven runs hot."}
7
+ {"id":"s6","start":347,"end":385,"label":"claim","source":"a morning observed at a working bakery, notes corrected by the owner","text":"- The doors open to customers at 7:00."}
8
+ {"id":"s7","start":386,"end":427,"label":"claim","source":"a morning observed at a working bakery, notes corrected by the owner","text":"The first person in is usually a regular."}
9
+ {"id":"s8","start":428,"end":468,"label":"claim","source":"a morning observed at a working bakery, notes corrected by the owner","text":"- The owner does not talk while shaping."}
10
+ {"id":"s9","start":469,"end":514,"label":"claim","source":"a morning observed at a working bakery, notes corrected by the owner","text":"Talking happens at the mixer and at the till."}
11
+ {"id":"s10","start":515,"end":549,"label":"claim","source":"a morning observed at a working bakery, notes corrected by the owner","text":"- Flour is weighed, never scooped."}
12
+ {"id":"s11","start":550,"end":591,"label":"claim","source":"a morning observed at a working bakery, notes corrected by the owner","text":"Water temperature is checked every batch."}
13
+ {"id":"s12","start":592,"end":692,"label":"private","text":"- Said in confidence, not for the story: the lease ends next spring, and she has not told her staff."}
@@ -0,0 +1,16 @@
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
+ Open question: does she tell him about the sale before she lets him shape the rye, or after?
12
+
13
+ What it is about, I think: a craft outlives the room it was practiced in. And: people who love
14
+ each other in a work setting say it through the work.
15
+
16
+ The last line should be about bread, never about feelings.
@@ -0,0 +1,7 @@
1
+ {"material":"notes","path":"story/materials/notes.md","sha256":"d2af0977ca30dc1f666d219e3c5d2d0f063e91d9d07f14cf703f848ca260ada8"}
2
+ {"id":"s1","start":0,"end":68,"label":"aside","text":"Author's notes for the story, typed over two evenings. Raw thinking."}
3
+ {"id":"s2","start":70,"end":405,"label":"claim","own":true,"text":"A bakery on its last morning before the sale closes. Two people: the owner, who has run it for\nthirty-one years, and the apprentice she took on at sixteen, now nineteen. She has not told him\nit is sold. He has not told her he got into a baking school in another city and leaves in the\nautumn. Each thinks they are protecting the other."}
4
+ {"id":"s3","start":407,"end":534,"label":"claim","own":true,"text":"The story is four scenes, one morning, 3:40 to 7:00. The oven has a noise. The rye is the thing\nshe has never let him do alone."}
5
+ {"id":"s4","start":536,"end":628,"label":"question","text":"Open question: does she tell him about the sale before she lets him shape the rye, or after?"}
6
+ {"id":"s5","start":630,"end":778,"label":"stance","text":"What it is about, I think: a craft outlives the room it was practiced in. And: people who love\neach other in a work setting say it through the work."}
7
+ {"id":"s6","start":780,"end":838,"label":"stance","text":"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.
@@ -0,0 +1,14 @@
1
+ {"material":"scene-list","path":"story/materials/scene-list.md","sha256":"8181cdb35cd1afad01809367de15b3233f24fc7bede5b7b89c022f05e3d655e0"}
2
+ {"id":"s1","start":0,"end":51,"label":"aside","text":"Scene list, agreed with the editor before drafting."}
3
+ {"id":"s2","start":52,"end":63,"label":"aside","text":"Considered."}
4
+ {"id":"s3","start":65,"end":95,"label":"claim","own":true,"text":"- scene-1, 3:40: Theo arrives."}
5
+ {"id":"s4","start":96,"end":119,"label":"claim","own":true,"text":"Ines is already mixing."}
6
+ {"id":"s5","start":120,"end":135,"label":"claim","own":true,"text":"The oven noise."}
7
+ {"id":"s6","start":136,"end":162,"label":"claim","own":true,"text":"No one says anything real."}
8
+ {"id":"s7","start":163,"end":188,"label":"claim","own":true,"text":"- scene-2, 4:30: shaping."}
9
+ {"id":"s8","start":189,"end":259,"label":"claim","own":true,"text":"Ines lets Theo shape the rye for the first time, and does not say why."}
10
+ {"id":"s9","start":260,"end":286,"label":"claim","own":true,"text":"- scene-3, 5:50: the bake."}
11
+ {"id":"s10","start":287,"end":344,"label":"claim","own":true,"text":"Ines tells Theo the bakery is sold, while turning a tray."}
12
+ {"id":"s11","start":345,"end":384,"label":"claim","own":true,"text":"- scene-4, 6:55: before the doors open."}
13
+ {"id":"s12","start":385,"end":418,"label":"claim","own":true,"text":"Theo tells Ines about the school."}
14
+ {"id":"s13","start":419,"end":451,"label":"claim","own":true,"text":"She hands him the\n starter jar."}
File without changes