@descent-vtt/spec-harness 0.19.0 → 0.21.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 (50) hide show
  1. package/.claude-plugin/marketplace.json +1 -1
  2. package/.claude-plugin/plugin.json +1 -1
  3. package/CHANGELOG.md +325 -0
  4. package/README.md +39 -18
  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 +308 -142
  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 +13 -1
  14. package/dist/config.js +14 -3
  15. package/dist/config.js.map +1 -1
  16. package/dist/configure.d.ts +94 -3
  17. package/dist/configure.js +204 -30
  18. package/dist/configure.js.map +1 -1
  19. package/dist/fs.d.ts +104 -1
  20. package/dist/fs.js +383 -11
  21. package/dist/fs.js.map +1 -1
  22. package/dist/git.d.ts +88 -2
  23. package/dist/git.js +155 -8
  24. package/dist/git.js.map +1 -1
  25. package/dist/guard.d.ts +20 -1
  26. package/dist/guard.js +31 -0
  27. package/dist/guard.js.map +1 -1
  28. package/dist/hooks.d.ts +59 -0
  29. package/dist/hooks.js +76 -3
  30. package/dist/hooks.js.map +1 -1
  31. package/dist/round.d.ts +27 -1
  32. package/dist/round.js +75 -17
  33. package/dist/round.js.map +1 -1
  34. package/dist/sandbox.js +4 -3
  35. package/dist/sandbox.js.map +1 -1
  36. package/dist/server.d.ts +25 -1
  37. package/dist/server.js +230 -28
  38. package/dist/server.js.map +1 -1
  39. package/dist/setup.js +40 -10
  40. package/dist/setup.js.map +1 -1
  41. package/dist/siblings.d.ts +29 -6
  42. package/dist/siblings.js +152 -39
  43. package/dist/siblings.js.map +1 -1
  44. package/dist/usage.d.ts +42 -7
  45. package/dist/usage.js +65 -6
  46. package/dist/usage.js.map +1 -1
  47. package/dist/workspace.d.ts +45 -3
  48. package/dist/workspace.js +143 -13
  49. package/dist/workspace.js.map +1 -1
  50. package/package.json +3 -3
@@ -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.19.0"
13
+ "version": "0.21.0"
14
14
  }
15
15
  ]
16
16
  }
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "spec-harness",
3
- "version": "0.19.0",
3
+ "version": "0.21.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,331 @@ 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.21.0
13
+
14
+ A path through a link is decided as it reads and as the system writes it,
15
+ and the stricter answer stands; a run of the hook has one budget, 45
16
+ seconds, for every process it starts; and the server's `check_path` answers
17
+ each path as the hook decides a write of it. `doctor` passes the branch that
18
+ archived its brief, a round's last commit, where git says the branch
19
+ archived it, so a CI job that runs it passes a round's last pull request;
20
+ and it fails for a hook's line whose script is not there and for a brief the
21
+ branch names that spec-brief cannot be asked about
22
+ ([ADR-0005](https://github.com/DescentVTT/spec-harness/blob/main/docs/adr/0005-a-guard-is-a-guardrail.md),
23
+ [ADR-0004](https://github.com/DescentVTT/spec-harness/blob/main/docs/adr/0004-the-active-brief-is-named-not-guessed.md)).
24
+
25
+ ### Changed
26
+
27
+ - **A path through a link is decided as it reads and as the system writes
28
+ it, and the stricter answer stands.** On Linux and macOS a `..` after a
29
+ link is written after the link: `deep/../schema.ts`, `deep` a link to
30
+ `lib/x`, reads as `schema.ts` and is written to `lib/schema.ts`, and the
31
+ hook, `guard` and the server's `check_path` decided it as `schema.ts`.
32
+ A link to nothing was decided as the link, where a write makes the file
33
+ it names, and a path outside the repository whose `..` after a link
34
+ leads into it as outside. Each such path is now decided both ways, each
35
+ file by the work tree that holds it, and a refusal says where the link
36
+ leads. On Windows, which reads the `..` first, no answer changes
37
+ ([ADR-0005](https://github.com/DescentVTT/spec-harness/blob/main/docs/adr/0005-a-guard-is-a-guardrail.md)).
38
+ Upgrading: nothing to change. A write through a link to a file the brief
39
+ protects, which passed, is refused, and `guard` exits 1 for it.
40
+ - **A run of the hook has one budget, 45 seconds, for every process it
41
+ starts, and holds the write at it.** Each process the hook asked a
42
+ question of had 20 seconds, and several that answered slowly, none of
43
+ them at that, passed the 60 seconds `init` and the plugin give the
44
+ hook's entry, where Claude Code cancels the hook and lets the write go
45
+ ahead. A process is now given its 20 seconds or what is left of the
46
+ run's 45, whichever is less, and one stopped at the budget says so:
47
+ `cannot check this write: spec-brief did not answer within the 45
48
+ seconds the hook's run is given, and was stopped`, exit 2. `doctor`
49
+ notes an entry of the hook whose `timeout` is below the 45 seconds, and
50
+ `--format json` has the note as each line's `timeout`
51
+ ([ADR-0002](https://github.com/DescentVTT/spec-harness/blob/main/docs/adr/0002-siblings-through-their-command-lines.md)).
52
+ Upgrading: nothing to change where the hook's entry has the 60 seconds
53
+ `init` writes, or no timeout; an entry below 45 is noted, and fails
54
+ nothing.
55
+ - **The server's `check_path` answers each path as the hook decides a
56
+ write of it.** A file outside every work tree of the repository is
57
+ outside the repository whatever names the brief, where a brief that
58
+ spec-brief does not list or has archived failed the call; a path inside
59
+ that such a brief holds is `deny`, with the reason `held` and the
60
+ hook's words; and with no brief named each path is answered as no
61
+ brief's, where the call was a tool error. spec-brief is asked only where
62
+ a brief is named and a path is inside. The report at the end of a turn
63
+ says that every write is held but one to a file outside every work tree
64
+ ([ADR-0008](https://github.com/DescentVTT/spec-harness/blob/main/docs/adr/0008-one-server-for-rounds.md),
65
+ [ADR-0005](https://github.com/DescentVTT/spec-harness/blob/main/docs/adr/0005-a-guard-is-a-guardrail.md)).
66
+ Upgrading: a client that read a tool error from `check_path` for a
67
+ brief's problem or for no brief named reads an answer for each path, and
68
+ `held` is a new `reason` of a decision.
69
+ - **`doctor` fails nothing for the branch that archived its brief, a
70
+ round's last commit.** A round archives its brief on its own branch
71
+ before the branch merges, and since 0.20.0 `doctor` exited 1 there, so a
72
+ CI job that runs it failed every round's last pull request. Where git
73
+ says the brief's archived file is in HEAD and not at the merge base of
74
+ the base and HEAD, `doctor` says so in a note and exits 0 for it; where
75
+ the file is at the merge base, a closed round's branch used again, it
76
+ exits 1 as before, and so where git cannot say. `--format json` has the
77
+ answer as `closedRound`. The hook holds every write on such a branch as
78
+ before, and says the round is archived and ends when the branch merges
79
+ ([ADR-0004](https://github.com/DescentVTT/spec-harness/blob/main/docs/adr/0004-the-active-brief-is-named-not-guessed.md)).
80
+ Upgrading: a CI job that runs `doctor` fetches the base under the name it
81
+ is configured by, with its history (`git fetch origin main:main`, in a
82
+ clone that is not shallow); without it, a base that names no commit or a
83
+ shallow history, a round's last pull request still fails `doctor`. Run
84
+ `doctor` in CI without `--strict`, which fails where the release of
85
+ Claude Code cannot be told.
86
+ - **`doctor` fails for a line of Claude Code's hook whose script is not
87
+ there**, which Claude Code cannot start and lets every write past: a
88
+ guard that is off, or, under `"onFailure": "block"` that the Claude
89
+ Code on `PATH` reads, one that holds every write. The script is looked
90
+ for where `${CLAUDE_PROJECT_DIR}` can point, a linked worktree's main
91
+ checkout among them; a relative path, npx's package and a word a shell
92
+ reads are not
93
+ ([ADR-0005](https://github.com/DescentVTT/spec-harness/blob/main/docs/adr/0005-a-guard-is-a-guardrail.md)).
94
+ Upgrading: a project whose hook's line names an install that is not
95
+ there has `doctor` exit 1 where it exited 0: install
96
+ `@descent-vtt/spec-harness`, or mend the line. `claudeCode.lines` has a
97
+ line for each way an entry names the harness.
98
+ - **`doctor` fails where the branch names a brief spec-brief cannot be
99
+ asked about**, not installed, too old, or failing as it lists the
100
+ briefs, since the hook holds every write there, and says why on its
101
+ `brief` line; a hook's line that names a brief where spec-brief cannot
102
+ be run is said as one that holds every write
103
+ ([ADR-0004](https://github.com/DescentVTT/spec-harness/blob/main/docs/adr/0004-the-active-brief-is-named-not-guessed.md)).
104
+ Upgrading: exit 1 where it was 0; the flag and `SPEC_BRIEF` still fail
105
+ nothing.
106
+
107
+ ### Added
108
+
109
+ - **`doctor` notes a report at the end of a turn that nothing runs**:
110
+ `"turnReport"` on, and no settings file starting the hook as a turn
111
+ ends. It fails nothing; `--format json` has `claudeCode.turnEnd`
112
+ ([ADR-0005](https://github.com/DescentVTT/spec-harness/blob/main/docs/adr/0005-a-guard-is-a-guardrail.md)).
113
+
114
+ ### Fixed
115
+
116
+ - **A `tools` command that cannot be started is said in one way on every
117
+ host**: `cannot start "<command>": name the tool's script, such as
118
+ ["node", ...]`, the words a name no program has was given. On Windows a
119
+ script with no `node` before it, or a `.cmd`, ended a command on
120
+ `unexpected error: Error: spawn EFTYPE` (or `EINVAL`) and a stack trace,
121
+ and on Linux a file that may not be run was `exited -1` and nothing more;
122
+ the hook holds the write as it did
123
+ ([ADR-0002](https://github.com/DescentVTT/spec-harness/blob/main/docs/adr/0002-siblings-through-their-command-lines.md)).
124
+
125
+ ## 0.20.0
126
+
127
+ The hook decides each path by the work tree that holds it, a third
128
+ worktree's and the MCP server's included, and finds siblings in the main
129
+ checkout's install wherever a work tree has none; every process it starts
130
+ has a time limit, and it reads only a file where a file is expected, so a
131
+ named pipe no longer holds it without end; it says a guard that is off with
132
+ exit 0, and `init --block-on-failure` asks Claude Code to hold a write when
133
+ the hook cannot start. It gives a session spec-brief's answer again where
134
+ nothing that answer was read from has changed, and it can report, at the end
135
+ of a turn and where the configuration asks for it, what the work tree holds
136
+ that the brief protects or that is outside its scope
137
+ ([ADR-0005](https://github.com/DescentVTT/spec-harness/blob/main/docs/adr/0005-a-guard-is-a-guardrail.md),
138
+ [ADR-0002](https://github.com/DescentVTT/spec-harness/blob/main/docs/adr/0002-siblings-through-their-command-lines.md)).
139
+
140
+ A configuration that does not load lets a write to itself through, so
141
+ that a session can mend it; a write outside every work tree of the
142
+ repository is held by no brief and no configuration; and `doctor` fails
143
+ where the branch names a brief no round can work under
144
+ ([ADR-0005](https://github.com/DescentVTT/spec-harness/blob/main/docs/adr/0005-a-guard-is-a-guardrail.md),
145
+ [ADR-0004](https://github.com/DescentVTT/spec-harness/blob/main/docs/adr/0004-the-active-brief-is-named-not-guessed.md)).
146
+
147
+ `doctor` says a line of Claude Code's hook that asks for the help or the
148
+ version as a guard that is off, fails `--strict` where git cannot say
149
+ where it runs its hooks, and notes in a linked worktree the release the
150
+ hook's line runs; a `.spec-harness.json` that cannot be read is said as
151
+ that; and on Windows a drive letter is one in either case
152
+ ([ADR-0005](https://github.com/DescentVTT/spec-harness/blob/main/docs/adr/0005-a-guard-is-a-guardrail.md)).
153
+
154
+ ### Added
155
+
156
+ - **A report at the end of a turn, where `.spec-harness.json` asks for
157
+ one.** With `"turnReport": true`, `hook claude` answers Claude Code's
158
+ `Stop`: with one `git status`, every path the work tree holds changed
159
+ that the brief protects or that is outside its scope, a shell's writes
160
+ among them, in the words a write of it is given, as one `systemMessage`,
161
+ exit 0, and never a field that has Claude go on. `init` writes the entry,
162
+ in the background, where the key is on, and Claude reads the report as the
163
+ next turn begins; the plugin carries none, and with it on `init` says
164
+ what to add
165
+ ([ADR-0005](https://github.com/DescentVTT/spec-harness/blob/main/docs/adr/0005-a-guard-is-a-guardrail.md),
166
+ [ADR-0012](https://github.com/DescentVTT/spec-harness/blob/main/docs/adr/0012-one-way-into-claude-code.md)).
167
+ Upgrading: off by default, so nothing changes until the key is turned on
168
+ and `spec-harness init --write` run again. A `Stop` entry aimed at the
169
+ hook exited 1, `the hook input names no event or tool`, and now exits 0;
170
+ a configuration's unknown key is refused with `turnReport` among the keys
171
+ it names.
172
+ - **`init --block-on-failure` writes `"onFailure": "block"` on its hook
173
+ entries before and after a write and as a session starts**, and on those
174
+ an earlier run wrote, so that Claude Code 2.1.295 or later holds a write
175
+ where the hook cannot start, as in a worktree with no install, or runs
176
+ past its timeout. It is written only
177
+ where asked for, and the plugin does not carry it: no page says what an
178
+ older release does with the key. `doctor` advises it in a note, failing
179
+ nothing, for a line that guards without it where the Claude Code on
180
+ `PATH` reads the field
181
+ ([ADR-0012](https://github.com/DescentVTT/spec-harness/blob/main/docs/adr/0012-one-way-into-claude-code.md)).
182
+ The entry at the end of a turn is left without it: it runs in the
183
+ background, where the hooks reference gives the field no effect.
184
+ - **`doctor`, run in a linked worktree, notes the release the hook's line
185
+ runs.** The line names its script under `${CLAUDE_PROJECT_DIR}`, which
186
+ in a worktree session of the desktop app is the main checkout. Where the
187
+ main checkout's install of spec-harness and the worktree's are not one
188
+ release, a note names both; it fails nothing.
189
+
190
+ ### Changed
191
+
192
+ - **The hook gives a session spec-brief's list of the briefs again, where
193
+ nothing it was read from has changed.** On a brief's branch the hook
194
+ started spec-brief before every write, most of what a write waited for on
195
+ Windows. Where spec-brief's `list --format json` names everything its
196
+ answer was read from, its `inputs`, the hook keeps the answer for the
197
+ session under the git common directory, in `spec-harness/answers/`, and
198
+ gives it again to the session's later runs while every file, directory
199
+ and index on the list reads as listed, with no process started; anything
200
+ else starts spec-brief as before. No answer changes
201
+ ([ADR-0002](https://github.com/DescentVTT/spec-harness/blob/main/docs/adr/0002-siblings-through-their-command-lines.md),
202
+ [ADR-0003](https://github.com/DescentVTT/spec-harness/blob/main/docs/adr/0003-state-outside-the-work-tree.md)).
203
+ Upgrading: nothing to change. A spec-brief whose `list` names no
204
+ `inputs`, 0.9.0 and every release before it, is started before every
205
+ write as it was.
206
+ - **A process the harness asks a question of is stopped after 20 seconds,
207
+ and the hook holds the write there.** git had no limit and spec-brief
208
+ ten minutes, where Claude Code cancels the hook at the 60 seconds its
209
+ entry gives it, and before a write lets the write go ahead: a named pipe
210
+ at `.spec-brief.json` or `.git/config` kept the hook, `doctor` and
211
+ every command from coming back. git's reads and spec-brief's list are
212
+ now stopped at 20 seconds and said, `spec-brief did not answer within
213
+ 20 seconds, and was stopped`; before a write that is `cannot check this
214
+ write: ...`, exit 2, which holds it. A round's work, a diff, a probe's
215
+ worktree, a sibling's check, keeps ten minutes
216
+ ([ADR-0002](https://github.com/DescentVTT/spec-harness/blob/main/docs/adr/0002-siblings-through-their-command-lines.md)).
217
+ Upgrading: where git or spec-brief takes longer than 20 seconds to
218
+ answer, which none measured does, writes are held and commands exit 2
219
+ with the line: find what it waits on.
220
+ - **A path is decided by the work tree that holds it.** A path in a work
221
+ tree of the repository that was neither the one the project names nor
222
+ the one the question's `cwd` is in was read from the project's top, as a
223
+ file of `.claude/worktrees/<name>/` under the project's branch or as one
224
+ outside the project, and a write its own brief protects passed with no
225
+ word. That work tree now decides it, by its branch, briefs and
226
+ configuration; a nested repository's file is decided as before
227
+ ([ADR-0005](https://github.com/DescentVTT/spec-harness/blob/main/docs/adr/0005-a-guard-is-a-guardrail.md)).
228
+ Upgrading: a session that writes into another worktree has the write
229
+ refused where that worktree's brief protects it.
230
+ - **Every command finds the siblings in the main work tree's install** where
231
+ a linked worktree has none, as git's hook and `doctor` did: `guard`,
232
+ `context`, `audit`, `probe` and the rest exited 2, `spec-brief is not
233
+ installed here`.
234
+ Upgrading: in such a worktree these commands run, and `guard` and
235
+ `audit` can exit 1 where they exited 2.
236
+ - **The server reads the work tree a path is in, and the round in the one
237
+ its client names.** `check_path` read every path as the project's, and
238
+ every tool the project's branch, so a worktree's file read as
239
+ `.claude/worktrees/<name>/...` and its brief went unnamed. `check_path`
240
+ decides an absolute path by the work tree of the repository that holds
241
+ it, and `start_round`, `check_path`, `request_escalation` and
242
+ `audit_round` read the round in the work tree a new `directory`
243
+ argument names, else the one the client's roots name where they name
244
+ exactly one of the repository's, else the project's, and say which as
245
+ `workTree`
246
+ ([ADR-0008](https://github.com/DescentVTT/spec-harness/blob/main/docs/adr/0008-one-server-for-rounds.md)).
247
+ Upgrading: a client that declares roots naming a worktree has its calls
248
+ answered for that worktree; a script reading the tools' structured
249
+ answers finds `workTree`.
250
+ - **The hook says the guard is off with exit 0, where it exited 1.** A
251
+ question it cannot read and a line of its own that turns the guard off
252
+ (one it cannot read, a `--root` that is no directory) were one line on
253
+ stderr and exit 1, which Claude Code counts as a hook that failed: under
254
+ `"onFailure": "block"` that held every write of the session, the one
255
+ that mends the line among them. Each is now the same line to the person,
256
+ as `systemMessage`, and exit 0, and the write goes on as it did, so
257
+ `onFailure` holds only a hook that did not run
258
+ ([ADR-0005](https://github.com/DescentVTT/spec-harness/blob/main/docs/adr/0005-a-guard-is-a-guardrail.md)).
259
+ Upgrading: a script that read the hook's exit 1 or its stderr reads the
260
+ line as `systemMessage` on stdout, with exit 0.
261
+ - **A write to `.spec-harness.json` itself goes on while it does not
262
+ load.** Every write was held, exit 2, the one that mends the file among
263
+ them, so a session could not mend a typo in it. Before a write to that
264
+ file and to nothing else, the hook now decides nothing, exit 0, so the
265
+ person's own permission prompt stands, and says why every other write is
266
+ held, to the person and to Claude. A file of that name elsewhere, a link
267
+ to it, a second name of it and the configuration of another work tree
268
+ are held as before, and so is a configuration that is a directory, a
269
+ named pipe, or a link to a file of the repository. As a session starts
270
+ the hook says the same.
271
+ Upgrading: a brief that protects `.spec-harness.json` does not hold that
272
+ write while the file does not load, since none of it is read; the audit
273
+ and spec-brief's archive still measure the change.
274
+ - **`doctor` exits 1 where the branch names a brief spec-brief does not
275
+ know, or has archived.** The hook holds every write on such a branch,
276
+ and `doctor` said so on its `brief` line and exited 0. One the flag or
277
+ `SPEC_BRIEF` names still fails nothing.
278
+ Upgrading: a branch whose name only reads as a brief's, as
279
+ `fix/brief-list-order` names `list` under the default `branches`, fails
280
+ `doctor` as it holds every write: rename the branch, or set `branches` in
281
+ `.spec-harness.json`.
282
+ - **A write outside every work tree of the repository is not held by what
283
+ holds the writes inside.** A brief that is archived, or that spec-brief
284
+ does not list, whichever names it, and a configuration that does not
285
+ load held every write, a file outside the repository among them, and
286
+ `guard` refused such a path, exit 2. Such a write passes as one outside
287
+ the repository does, with no word, and spec-brief is not asked about it;
288
+ one inside any work tree of the repository, a linked worktree's among
289
+ them, is decided as before, by the work tree that holds it, and a path
290
+ with a `..` in it is held as before.
291
+ Upgrading: `guard` of a path outside the repository exits 0 under such
292
+ a brief, where it exited 2.
293
+ - **A hook line that asks for the help or the version is a guard that is
294
+ off.** `hook claude --help`, `--version`, `-h` and `-v` print the help
295
+ or the version, exit 0, and check no write, which Claude Code takes for a
296
+ hook that succeeded; `doctor` said such a line guards, and said a
297
+ `--brief 404` beside it held every write. It says the line as one under
298
+ which no write is checked, whatever else is on it, and exits 1.
299
+ Upgrading: take the option off the hook's line; `doctor` exits 1 until
300
+ it is.
301
+ - **`--help` or `--version` beside a `--root` that is not a directory is
302
+ a line the hook cannot read.** `hook claude --help --root nowhere`
303
+ printed the help, exit 0, where without `--help` the hook says the
304
+ root. It says `the hook's line cannot be read (--help is given beside a
305
+ fault, --root is "nowhere", which is not a directory, ...)` as it says
306
+ every line it cannot read, to the person with exit 0, before a write and
307
+ after one, and holds no write; as a session starts it says the same.
308
+ `doctor` says the line in the same words.
309
+ Upgrading: mend the line; no line a release wrote carries either.
310
+ - **`doctor --strict` fails where git cannot say where it runs its
311
+ hooks.** Under a `core.hooksPath` that names a file, `where git runs its
312
+ hooks cannot be told` failed nothing, under `--strict` or not, where a
313
+ release, a hook's line, a plugin or a hook file that cannot be told
314
+ fails `--strict`.
315
+ Upgrading: point `core.hooksPath` at a directory, or leave it unset.
316
+
317
+ ### Fixed
318
+
319
+ - **A named pipe where the harness reads a file** no longer keeps a
320
+ command from coming back: a document a brief cites, a sibling's
321
+ `package.json`, what the run before a write kept, git's configuration
322
+ and a worktree's `commondir` as the hook reads them, a request waiting
323
+ for a ruling, a probe's report. Only a regular file is read, and what
324
+ else stands there is read as a file that is not there
325
+ ([ADR-0002](https://github.com/DescentVTT/spec-harness/blob/main/docs/adr/0002-siblings-through-their-command-lines.md)).
326
+ - **A `.spec-harness.json` that cannot be read is said as that.** One the
327
+ account may not read, or one another process holds open on Windows, was
328
+ `.spec-harness.json is not valid JSON: EACCES: ...`. It is
329
+ `.spec-harness.json cannot be read: EACCES: ...`, exit 2 as before, and
330
+ a write to the file itself goes on, as to one that does not load.
331
+ - **On Windows `--root C:\work` beside `--root c:\work` is one root.**
332
+ Every command refused the two as two directories, exit 2, and the hook
333
+ read such a line as one it cannot read, under which it checked no write;
334
+ Windows names a volume by its letter in either case. The hook now guards
335
+ under that root, and the rest of the path is compared as before.
336
+
12
337
  ## 0.19.0
13
338
 
14
339
  An option that takes one value is given one, and a line that is wrong is