@descent-vtt/spec-harness 0.18.0 → 0.20.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 (51) hide show
  1. package/.claude-plugin/marketplace.json +1 -1
  2. package/.claude-plugin/plugin.json +1 -1
  3. package/CHANGELOG.md +372 -0
  4. package/README.md +78 -11
  5. package/dist/briefs.d.ts +45 -0
  6. package/dist/briefs.js +73 -0
  7. package/dist/briefs.js.map +1 -1
  8. package/dist/cli.d.ts +1 -1
  9. package/dist/cli.js +384 -84
  10. package/dist/cli.js.map +1 -1
  11. package/dist/commands.js +7 -2
  12. package/dist/commands.js.map +1 -1
  13. package/dist/config.d.ts +15 -1
  14. package/dist/config.js +16 -3
  15. package/dist/config.js.map +1 -1
  16. package/dist/configure.d.ts +142 -32
  17. package/dist/configure.js +316 -89
  18. package/dist/configure.js.map +1 -1
  19. package/dist/fs.d.ts +150 -3
  20. package/dist/fs.js +459 -17
  21. package/dist/fs.js.map +1 -1
  22. package/dist/git.d.ts +85 -9
  23. package/dist/git.js +140 -16
  24. package/dist/git.js.map +1 -1
  25. package/dist/hooks.d.ts +111 -0
  26. package/dist/hooks.js +172 -16
  27. package/dist/hooks.js.map +1 -1
  28. package/dist/round.d.ts +8 -0
  29. package/dist/round.js +32 -9
  30. package/dist/round.js.map +1 -1
  31. package/dist/sandbox.js +4 -3
  32. package/dist/sandbox.js.map +1 -1
  33. package/dist/server.d.ts +25 -1
  34. package/dist/server.js +194 -27
  35. package/dist/server.js.map +1 -1
  36. package/dist/setup.d.ts +6 -3
  37. package/dist/setup.js +74 -23
  38. package/dist/setup.js.map +1 -1
  39. package/dist/siblings.d.ts +42 -7
  40. package/dist/siblings.js +135 -30
  41. package/dist/siblings.js.map +1 -1
  42. package/dist/types.d.ts +5 -0
  43. package/dist/types.js.map +1 -1
  44. package/dist/usage.d.ts +66 -12
  45. package/dist/usage.js +134 -21
  46. package/dist/usage.js.map +1 -1
  47. package/dist/workspace.d.ts +80 -1
  48. package/dist/workspace.js +188 -8
  49. package/dist/workspace.js.map +1 -1
  50. package/hooks/hooks.json +16 -0
  51. package/package.json +2 -2
@@ -10,7 +10,7 @@
10
10
  "name": "spec-harness",
11
11
  "source": "./",
12
12
  "description": "Rounds of agent work under a brief.",
13
- "version": "0.18.0"
13
+ "version": "0.20.0"
14
14
  }
15
15
  ]
16
16
  }
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "spec-harness",
3
- "version": "0.18.0",
3
+ "version": "0.20.0",
4
4
  "description": "Rounds of agent work under a brief: context, a guard on every write, escalations a person signs, an audit at the end. Needs Claude Code 2.1.139 or later, and @descent-vtt/spec-harness and @descent-vtt/spec-brief 0.2.0 or later installed in the project.",
5
5
  "author": {
6
6
  "name": "DescentVTT",
package/CHANGELOG.md CHANGED
@@ -9,6 +9,378 @@ only fixes, reports less or documents, so `^0.7.0` takes only releases that
9
9
  cannot turn a run red. The family's policy is
10
10
  [spec-core's ADR-0009](https://github.com/DescentVTT/spec-core/blob/main/docs/adr/0009-versions-before-1-0.md).
11
11
 
12
+ ## 0.20.0
13
+
14
+ The hook decides each path by the work tree that holds it, a third
15
+ worktree's and the MCP server's included, and finds siblings in the main
16
+ checkout's install wherever a work tree has none; every process it starts
17
+ has a time limit, and it reads only a file where a file is expected, so a
18
+ named pipe no longer holds it without end; it says a guard that is off with
19
+ exit 0, and `init --block-on-failure` asks Claude Code to hold a write when
20
+ the hook cannot start. It gives a session spec-brief's answer again where
21
+ nothing that answer was read from has changed, and it can report, at the end
22
+ of a turn and where the configuration asks for it, what the work tree holds
23
+ that the brief protects or that is outside its scope
24
+ ([ADR-0005](https://github.com/DescentVTT/spec-harness/blob/main/docs/adr/0005-a-guard-is-a-guardrail.md),
25
+ [ADR-0002](https://github.com/DescentVTT/spec-harness/blob/main/docs/adr/0002-siblings-through-their-command-lines.md)).
26
+
27
+ A configuration that does not load lets a write to itself through, so
28
+ that a session can mend it; a write outside every work tree of the
29
+ repository is held by no brief and no configuration; and `doctor` fails
30
+ where the branch names a brief no round can work under
31
+ ([ADR-0005](https://github.com/DescentVTT/spec-harness/blob/main/docs/adr/0005-a-guard-is-a-guardrail.md),
32
+ [ADR-0004](https://github.com/DescentVTT/spec-harness/blob/main/docs/adr/0004-the-active-brief-is-named-not-guessed.md)).
33
+
34
+ `doctor` says a line of Claude Code's hook that asks for the help or the
35
+ version as a guard that is off, fails `--strict` where git cannot say
36
+ where it runs its hooks, and notes in a linked worktree the release the
37
+ hook's line runs; a `.spec-harness.json` that cannot be read is said as
38
+ that; and on Windows a drive letter is one in either case
39
+ ([ADR-0005](https://github.com/DescentVTT/spec-harness/blob/main/docs/adr/0005-a-guard-is-a-guardrail.md)).
40
+
41
+ ### Added
42
+
43
+ - **A report at the end of a turn, where `.spec-harness.json` asks for
44
+ one.** With `"turnReport": true`, `hook claude` answers Claude Code's
45
+ `Stop`: with one `git status`, every path the work tree holds changed
46
+ that the brief protects or that is outside its scope, a shell's writes
47
+ among them, in the words a write of it is given, as one `systemMessage`,
48
+ exit 0, and never a field that has Claude go on. `init` writes the entry,
49
+ in the background, where the key is on, and Claude reads the report as the
50
+ next turn begins; the plugin carries none, and with it on `init` says
51
+ what to add
52
+ ([ADR-0005](https://github.com/DescentVTT/spec-harness/blob/main/docs/adr/0005-a-guard-is-a-guardrail.md),
53
+ [ADR-0012](https://github.com/DescentVTT/spec-harness/blob/main/docs/adr/0012-one-way-into-claude-code.md)).
54
+ Upgrading: off by default, so nothing changes until the key is turned on
55
+ and `spec-harness init --write` run again. A `Stop` entry aimed at the
56
+ hook exited 1, `the hook input names no event or tool`, and now exits 0;
57
+ a configuration's unknown key is refused with `turnReport` among the keys
58
+ it names.
59
+ - **`init --block-on-failure` writes `"onFailure": "block"` on its hook
60
+ entries before and after a write and as a session starts**, and on those
61
+ an earlier run wrote, so that Claude Code 2.1.295 or later holds a write
62
+ where the hook cannot start, as in a worktree with no install, or runs
63
+ past its timeout. It is written only
64
+ where asked for, and the plugin does not carry it: no page says what an
65
+ older release does with the key. `doctor` advises it in a note, failing
66
+ nothing, for a line that guards without it where the Claude Code on
67
+ `PATH` reads the field
68
+ ([ADR-0012](https://github.com/DescentVTT/spec-harness/blob/main/docs/adr/0012-one-way-into-claude-code.md)).
69
+ The entry at the end of a turn is left without it: it runs in the
70
+ background, where the hooks reference gives the field no effect.
71
+ - **`doctor`, run in a linked worktree, notes the release the hook's line
72
+ runs.** The line names its script under `${CLAUDE_PROJECT_DIR}`, which
73
+ in a worktree session of the desktop app is the main checkout. Where the
74
+ main checkout's install of spec-harness and the worktree's are not one
75
+ release, a note names both; it fails nothing.
76
+
77
+ ### Changed
78
+
79
+ - **The hook gives a session spec-brief's list of the briefs again, where
80
+ nothing it was read from has changed.** On a brief's branch the hook
81
+ started spec-brief before every write, most of what a write waited for on
82
+ Windows. Where spec-brief's `list --format json` names everything its
83
+ answer was read from, its `inputs`, the hook keeps the answer for the
84
+ session under the git common directory, in `spec-harness/answers/`, and
85
+ gives it again to the session's later runs while every file, directory
86
+ and index on the list reads as listed, with no process started; anything
87
+ else starts spec-brief as before. No answer changes
88
+ ([ADR-0002](https://github.com/DescentVTT/spec-harness/blob/main/docs/adr/0002-siblings-through-their-command-lines.md),
89
+ [ADR-0003](https://github.com/DescentVTT/spec-harness/blob/main/docs/adr/0003-state-outside-the-work-tree.md)).
90
+ Upgrading: nothing to change. A spec-brief whose `list` names no
91
+ `inputs`, 0.9.0 and every release before it, is started before every
92
+ write as it was.
93
+ - **A process the harness asks a question of is stopped after 20 seconds,
94
+ and the hook holds the write there.** git had no limit and spec-brief
95
+ ten minutes, where Claude Code cancels the hook at the 60 seconds its
96
+ entry gives it, and before a write lets the write go ahead: a named pipe
97
+ at `.spec-brief.json` or `.git/config` kept the hook, `doctor` and
98
+ every command from coming back. git's reads and spec-brief's list are
99
+ now stopped at 20 seconds and said, `spec-brief did not answer within
100
+ 20 seconds, and was stopped`; before a write that is `cannot check this
101
+ write: ...`, exit 2, which holds it. A round's work, a diff, a probe's
102
+ worktree, a sibling's check, keeps ten minutes
103
+ ([ADR-0002](https://github.com/DescentVTT/spec-harness/blob/main/docs/adr/0002-siblings-through-their-command-lines.md)).
104
+ Upgrading: where git or spec-brief takes longer than 20 seconds to
105
+ answer, which none measured does, writes are held and commands exit 2
106
+ with the line: find what it waits on.
107
+ - **A path is decided by the work tree that holds it.** A path in a work
108
+ tree of the repository that was neither the one the project names nor
109
+ the one the question's `cwd` is in was read from the project's top, as a
110
+ file of `.claude/worktrees/<name>/` under the project's branch or as one
111
+ outside the project, and a write its own brief protects passed with no
112
+ word. That work tree now decides it, by its branch, briefs and
113
+ configuration; a nested repository's file is decided as before
114
+ ([ADR-0005](https://github.com/DescentVTT/spec-harness/blob/main/docs/adr/0005-a-guard-is-a-guardrail.md)).
115
+ Upgrading: a session that writes into another worktree has the write
116
+ refused where that worktree's brief protects it.
117
+ - **Every command finds the siblings in the main work tree's install** where
118
+ a linked worktree has none, as git's hook and `doctor` did: `guard`,
119
+ `context`, `audit`, `probe` and the rest exited 2, `spec-brief is not
120
+ installed here`.
121
+ Upgrading: in such a worktree these commands run, and `guard` and
122
+ `audit` can exit 1 where they exited 2.
123
+ - **The server reads the work tree a path is in, and the round in the one
124
+ its client names.** `check_path` read every path as the project's, and
125
+ every tool the project's branch, so a worktree's file read as
126
+ `.claude/worktrees/<name>/...` and its brief went unnamed. `check_path`
127
+ decides an absolute path by the work tree of the repository that holds
128
+ it, and `start_round`, `check_path`, `request_escalation` and
129
+ `audit_round` read the round in the work tree a new `directory`
130
+ argument names, else the one the client's roots name where they name
131
+ exactly one of the repository's, else the project's, and say which as
132
+ `workTree`
133
+ ([ADR-0008](https://github.com/DescentVTT/spec-harness/blob/main/docs/adr/0008-one-server-for-rounds.md)).
134
+ Upgrading: a client that declares roots naming a worktree has its calls
135
+ answered for that worktree; a script reading the tools' structured
136
+ answers finds `workTree`.
137
+ - **The hook says the guard is off with exit 0, where it exited 1.** A
138
+ question it cannot read and a line of its own that turns the guard off
139
+ (one it cannot read, a `--root` that is no directory) were one line on
140
+ stderr and exit 1, which Claude Code counts as a hook that failed: under
141
+ `"onFailure": "block"` that held every write of the session, the one
142
+ that mends the line among them. Each is now the same line to the person,
143
+ as `systemMessage`, and exit 0, and the write goes on as it did, so
144
+ `onFailure` holds only a hook that did not run
145
+ ([ADR-0005](https://github.com/DescentVTT/spec-harness/blob/main/docs/adr/0005-a-guard-is-a-guardrail.md)).
146
+ Upgrading: a script that read the hook's exit 1 or its stderr reads the
147
+ line as `systemMessage` on stdout, with exit 0.
148
+ - **A write to `.spec-harness.json` itself goes on while it does not
149
+ load.** Every write was held, exit 2, the one that mends the file among
150
+ them, so a session could not mend a typo in it. Before a write to that
151
+ file and to nothing else, the hook now decides nothing, exit 0, so the
152
+ person's own permission prompt stands, and says why every other write is
153
+ held, to the person and to Claude. A file of that name elsewhere, a link
154
+ to it, a second name of it and the configuration of another work tree
155
+ are held as before, and so is a configuration that is a directory, a
156
+ named pipe, or a link to a file of the repository. As a session starts
157
+ the hook says the same.
158
+ Upgrading: a brief that protects `.spec-harness.json` does not hold that
159
+ write while the file does not load, since none of it is read; the audit
160
+ and spec-brief's archive still measure the change.
161
+ - **`doctor` exits 1 where the branch names a brief spec-brief does not
162
+ know, or has archived.** The hook holds every write on such a branch,
163
+ and `doctor` said so on its `brief` line and exited 0. One the flag or
164
+ `SPEC_BRIEF` names still fails nothing.
165
+ Upgrading: a branch whose name only reads as a brief's, as
166
+ `fix/brief-list-order` names `list` under the default `branches`, fails
167
+ `doctor` as it holds every write: rename the branch, or set `branches` in
168
+ `.spec-harness.json`.
169
+ - **A write outside every work tree of the repository is not held by what
170
+ holds the writes inside.** A brief that is archived, or that spec-brief
171
+ does not list, whichever names it, and a configuration that does not
172
+ load held every write, a file outside the repository among them, and
173
+ `guard` refused such a path, exit 2. Such a write passes as one outside
174
+ the repository does, with no word, and spec-brief is not asked about it;
175
+ one inside any work tree of the repository, a linked worktree's among
176
+ them, is decided as before, by the work tree that holds it, and a path
177
+ with a `..` in it is held as before.
178
+ Upgrading: `guard` of a path outside the repository exits 0 under such
179
+ a brief, where it exited 2.
180
+ - **A hook line that asks for the help or the version is a guard that is
181
+ off.** `hook claude --help`, `--version`, `-h` and `-v` print the help
182
+ or the version, exit 0, and check no write, which Claude Code takes for a
183
+ hook that succeeded; `doctor` said such a line guards, and said a
184
+ `--brief 404` beside it held every write. It says the line as one under
185
+ which no write is checked, whatever else is on it, and exits 1.
186
+ Upgrading: take the option off the hook's line; `doctor` exits 1 until
187
+ it is.
188
+ - **`--help` or `--version` beside a `--root` that is not a directory is
189
+ a line the hook cannot read.** `hook claude --help --root nowhere`
190
+ printed the help, exit 0, where without `--help` the hook says the
191
+ root. It says `the hook's line cannot be read (--help is given beside a
192
+ fault, --root is "nowhere", which is not a directory, ...)` as it says
193
+ every line it cannot read, to the person with exit 0, before a write and
194
+ after one, and holds no write; as a session starts it says the same.
195
+ `doctor` says the line in the same words.
196
+ Upgrading: mend the line; no line a release wrote carries either.
197
+ - **`doctor --strict` fails where git cannot say where it runs its
198
+ hooks.** Under a `core.hooksPath` that names a file, `where git runs its
199
+ hooks cannot be told` failed nothing, under `--strict` or not, where a
200
+ release, a hook's line, a plugin or a hook file that cannot be told
201
+ fails `--strict`.
202
+ Upgrading: point `core.hooksPath` at a directory, or leave it unset.
203
+
204
+ ### Fixed
205
+
206
+ - **A named pipe where the harness reads a file** no longer keeps a
207
+ command from coming back: a document a brief cites, a sibling's
208
+ `package.json`, what the run before a write kept, git's configuration
209
+ and a worktree's `commondir` as the hook reads them, a request waiting
210
+ for a ruling, a probe's report. Only a regular file is read, and what
211
+ else stands there is read as a file that is not there
212
+ ([ADR-0002](https://github.com/DescentVTT/spec-harness/blob/main/docs/adr/0002-siblings-through-their-command-lines.md)).
213
+ - **A `.spec-harness.json` that cannot be read is said as that.** One the
214
+ account may not read, or one another process holds open on Windows, was
215
+ `.spec-harness.json is not valid JSON: EACCES: ...`. It is
216
+ `.spec-harness.json cannot be read: EACCES: ...`, exit 2 as before, and
217
+ a write to the file itself goes on, as to one that does not load.
218
+ - **On Windows `--root C:\work` beside `--root c:\work` is one root.**
219
+ Every command refused the two as two directories, exit 2, and the hook
220
+ read such a line as one it cannot read, under which it checked no write;
221
+ Windows names a volume by its letter in either case. The hook now guards
222
+ under that root, and the rest of the path is compared as before.
223
+
224
+ ## 0.19.0
225
+
226
+ An option that takes one value is given one, and a line that is wrong is
227
+ refused with `--help` or `--version` on it, as the family's contract has
228
+ it from 2026-10-18 (spec-core's ADR-0005, and
229
+ [ADR-0005](https://github.com/DescentVTT/spec-harness/blob/main/docs/adr/0005-a-guard-is-a-guardrail.md)
230
+ here).
231
+
232
+ `doctor` reads a hyphen in a matcher as the Claude Code on `PATH` reads
233
+ it, and no command ends on a stack or fails to come back for what stands
234
+ where a repository or a settings file is read
235
+ ([ADR-0005](https://github.com/DescentVTT/spec-harness/blob/main/docs/adr/0005-a-guard-is-a-guardrail.md)).
236
+
237
+ The hook decides a write in the work tree Claude works in, where a write a
238
+ worktree's brief protects went through unchecked in a worktree session, and
239
+ names the brief of a branch beside a tag of its name; it answers after a
240
+ write from what it decided before it, and says as a session starts what
241
+ would hold every write of it
242
+ ([ADR-0005](https://github.com/DescentVTT/spec-harness/blob/main/docs/adr/0005-a-guard-is-a-guardrail.md),
243
+ [ADR-0002](https://github.com/DescentVTT/spec-harness/blob/main/docs/adr/0002-siblings-through-their-command-lines.md)).
244
+
245
+ ### Changed
246
+
247
+ - **Two values for an option that takes one are refused.** The parser kept
248
+ the last and passed over the earlier with no word: `doctor --root nowhere
249
+ --root .` ran in `.`, `--brief 404 --brief 001` was brief 001, and
250
+ `probe --at sideways --at head` ran at the head. Every command but
251
+ Claude Code's hook now exits 2 with one line that names both, as
252
+ `--root is given twice, as "nowhere" and as ".", and one of the two would
253
+ go unread; give it one`. The two are compared as the option reads them,
254
+ so one directory spelt two ways, or `--brief 12` beside `--brief 012`,
255
+ is the option once. `--path` and `--option` are lists, as before.
256
+ Upgrading: give each such option once; a script that builds a line by
257
+ adding a second `--root` or `--format` to override the first exits 2.
258
+ - **Claude Code's hook says two values for one option on its own line.**
259
+ `hook claude --root nowhere --root .` guarded in `.`, and `hook claude
260
+ --brief 001 --brief 404` held every write for 404. It is a line the hook
261
+ cannot read, said in one line on stderr with exit 1, before a write and
262
+ after one, and holds no write: the guard is off until the line is
263
+ mended. `doctor` says the line in the same words and exits 1. No line a
264
+ release wrote gives an option two values.
265
+ Upgrading: a hook's line with an option given twice, which only a person
266
+ writes, is said and checks no write; give the option once.
267
+ - **A wrong line is refused beside `--help` or `--version`, and the two
268
+ beside each other.** `doctor --help --root a --root b`, `doctor --help
269
+ --note x`, `doctor --help extra`, `doctor --help --root nowhere` and
270
+ `bogus --help` printed the help, exit 0, and `--help --version` printed
271
+ the version. Each is now exit 2 and the line it is refused with without
272
+ either; `--help` beside `--version`, in either order, is
273
+ `--help is given beside --version, and one of the two answers would go
274
+ unread; give one of them`. On `hook claude`'s line it is a line the
275
+ hook cannot read, exit 1. A line that is right answers as before, `guard
276
+ --help` among them.
277
+ Upgrading: ask for one of the two, on a line that is right.
278
+ - **A matcher of names with a hyphen in one is read as the release on
279
+ `PATH` reads it.** By Claude Code's changelog such a matcher matched as a
280
+ substring before 2.1.195, and the changelog does not say how the rest of
281
+ it was read. Under an older release, or one that cannot be told, a line
282
+ under `Edit|Write|my-tool` is *cannot tell* where it said the line
283
+ guards a write by `Edit` and `Write`, and `init` writes no hook beside
284
+ it.
285
+ Upgrading: `doctor --strict` fails such a line under Claude Code 2.1.139
286
+ to 2.1.194 or with no `claude` on `PATH`: take the hyphenated name out of
287
+ the matcher, or update Claude Code.
288
+ - **A hook file the account may not read is said, where `doctor` and `init`
289
+ ended on EACCES and a stack.** One git may not execute is skipped by git,
290
+ and is said and fails nothing; one it may is *cannot tell*, since git runs
291
+ it and a script it cannot read stops every commit. `--format json` has
292
+ the `gitHook.state` `unreadable`.
293
+ Upgrading: `doctor` exits 0, or 1 under `--strict`, and `init` 0, where
294
+ each exited 2: make the file readable.
295
+ - **A write in a worktree is decided by the worktree's brief.** In a
296
+ worktree session started from the desktop app, and after Claude enters a
297
+ worktree, Claude Code names the main checkout as the project and hands the
298
+ hook the worktree as the question's `cwd`; the hook read the main
299
+ checkout's branch, which named no brief, and a write the worktree's brief
300
+ protects passed with no word. Where `cwd` is in another work tree of the
301
+ same repository, that work tree's branch, briefs and configuration now
302
+ decide the paths it holds, with its siblings looked for in its own install
303
+ and then the project's; any other path, and a `cwd` in another repository
304
+ or in none, is decided as before
305
+ ([ADR-0005](https://github.com/DescentVTT/spec-harness/blob/main/docs/adr/0005-a-guard-is-a-guardrail.md)).
306
+ Upgrading: in a worktree session a write the worktree's brief protects is refused, and one outside its scope warned of by that brief; nothing on the hook's line changes.
307
+ - **A branch beside a tag of its name names its brief.** The branch was read
308
+ by git's short name, `heads/<name>` where a tag or a ref `refs/<name>` has
309
+ the name, which no template reads: the hook passed a protected write,
310
+ git's hook a protected commit, and `doctor` said `(none named)`. It is
311
+ read as HEAD names it; a base `init` writes for such a branch is still the
312
+ name git reads as the branch, `heads/main` beside a tag `main`.
313
+ Upgrading: on such a branch the guard refuses what its brief protects.
314
+ - **git's hook in a linked worktree with no install runs the main work
315
+ tree's.** The hook `init` writes refused the commit there; it now reads
316
+ the script from the repository's main work tree after the worktree's own,
317
+ and `hook git` and `doctor` find the siblings there too, `doctor` saying
318
+ whose install each is (`elsewhere` in `--format json`).
319
+ Upgrading: `init` writes the hook once, so one an earlier release wrote still stops such a commit: take it away and run `spec-harness init --git-hook --write`.
320
+ - **The hook answers after a write from what it decided before it.** Claude
321
+ Code starts the hook before a write and after it, and the run after
322
+ decided the write again, at what the run before costs: git, and spec-brief
323
+ on a brief's branch. The run before now keeps what the run after would say
324
+ under the git common directory, named by the question's `session_id` and
325
+ `tool_use_id`, and the run after answers from it with no process started;
326
+ one that finds nothing kept for its call, or another call's, decides as
327
+ before. Under `outOfScope: "ask"` or `"deny"`, where nothing was kept, it
328
+ says nothing with no process started
329
+ ([ADR-0002](https://github.com/DescentVTT/spec-harness/blob/main/docs/adr/0002-siblings-through-their-command-lines.md)).
330
+ Upgrading: nothing to change. What another process changes between the two runs of one write, the configuration or what spec-brief reads, is read at the next write rather than after this one.
331
+ - **The hook says, as a session starts, what would hold every write of it.**
332
+ A SessionStart entry aimed at `hook claude` exited 1, `the hook input
333
+ names no event or tool`. It now reads the question, however the session
334
+ starts, and says in one line, to the person and to Claude, a configuration
335
+ that does not load, a brief named that spec-brief does not list,
336
+ spec-brief missing or too old where a brief is named, or a hook's line it
337
+ cannot read; nothing where all is well, and exit 0 whatever it finds.
338
+ `init` writes the entry beside its two, the plugin's `hooks/hooks.json`
339
+ carries it, and `doctor` notes a settings file that guards writes and has
340
+ none
341
+ ([ADR-0005](https://github.com/DescentVTT/spec-harness/blob/main/docs/adr/0005-a-guard-is-a-guardrail.md),
342
+ [ADR-0012](https://github.com/DescentVTT/spec-harness/blob/main/docs/adr/0012-one-way-into-claude-code.md)).
343
+ Upgrading: `init` writes its lines once, so run `spec-harness init
344
+ --write` again to add the entry to a `.claude/settings.json` an earlier
345
+ release wrote; a plugin brings it with this release. A script reading
346
+ `doctor --format json` finds `claudeCode.sessionStart`.
347
+
348
+ ### Fixed
349
+
350
+ - **A named pipe where a settings file, `.mcp.json` or `.spec-harness.json`
351
+ is read** no longer keeps `doctor`, `init` or every command from coming
352
+ back: the first two are read as files that cannot be read as JSON, and
353
+ `.spec-harness.json` is `neither a file nor a directory, not a
354
+ configuration file`, exit 2, as a directory there is.
355
+ - **`escalate` and `rule` say a path under git's directory, where the
356
+ harness keeps its state, that is no directory**, exit 2, in a line, where
357
+ they ended on Node's ENOTDIR and a stack.
358
+ - **A `core.hooksPath` that names a file is said in git's own words**, where
359
+ `doctor` and `init` gave the reason for a git older than 2.31.
360
+ - **`doctor`'s first line advises an array that unloads the project's
361
+ settings** before `init --write`, which writes no hook while it stands
362
+ and only advised the array in turn. `--format json` gains
363
+ `claudeCode.unloaded`.
364
+
365
+ ### Documentation
366
+
367
+ - **Worktrees, the branches an app makes, and sessions that write through a
368
+ shell.** Two sections of the README, under *A round*. One says which
369
+ branch names the default templates read (`claude/brief-042-rotate-tokens`
370
+ is brief 042; `*` does not cross `/`; `claude --worktree <name>` makes
371
+ `worktree-<name>`, which needs a template of its own), and how a worktree
372
+ is checked: by its own branch and briefs, with the project's install where
373
+ it has none, at the commit as before the write; and that where a session's
374
+ project is a worktree with no install, the hook cannot start and the write
375
+ goes ahead. The other has, layer by layer, what the guard, the check as a
376
+ session starts, git's pre-commit hook, the audit and spec-brief's archive
377
+ each catch and let through, each run, and what every layer lets through:
378
+ an edit of the brief, a shell's write until the commit, `git commit
379
+ --no-verify`, and a Claude Code mod that approves a call a hook blocked.
380
+ It says why a shell `PostToolUse` hook and `"protected": "ask"` were not
381
+ built (ADR-0005, ADR-0006).
382
+ Upgrading: nothing to change; it describes this release.
383
+
12
384
  ## 0.18.0
13
385
 
14
386
  `doctor` and `init` say more of what looks like a guard and checks