@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.
- package/.claude-plugin/marketplace.json +1 -1
- package/.claude-plugin/plugin.json +1 -1
- package/CHANGELOG.md +372 -0
- package/README.md +78 -11
- package/dist/briefs.d.ts +45 -0
- package/dist/briefs.js +73 -0
- package/dist/briefs.js.map +1 -1
- package/dist/cli.d.ts +1 -1
- package/dist/cli.js +384 -84
- package/dist/cli.js.map +1 -1
- package/dist/commands.js +7 -2
- package/dist/commands.js.map +1 -1
- package/dist/config.d.ts +15 -1
- package/dist/config.js +16 -3
- package/dist/config.js.map +1 -1
- package/dist/configure.d.ts +142 -32
- package/dist/configure.js +316 -89
- package/dist/configure.js.map +1 -1
- package/dist/fs.d.ts +150 -3
- package/dist/fs.js +459 -17
- package/dist/fs.js.map +1 -1
- package/dist/git.d.ts +85 -9
- package/dist/git.js +140 -16
- package/dist/git.js.map +1 -1
- package/dist/hooks.d.ts +111 -0
- package/dist/hooks.js +172 -16
- package/dist/hooks.js.map +1 -1
- package/dist/round.d.ts +8 -0
- package/dist/round.js +32 -9
- package/dist/round.js.map +1 -1
- package/dist/sandbox.js +4 -3
- package/dist/sandbox.js.map +1 -1
- package/dist/server.d.ts +25 -1
- package/dist/server.js +194 -27
- package/dist/server.js.map +1 -1
- package/dist/setup.d.ts +6 -3
- package/dist/setup.js +74 -23
- package/dist/setup.js.map +1 -1
- package/dist/siblings.d.ts +42 -7
- package/dist/siblings.js +135 -30
- package/dist/siblings.js.map +1 -1
- package/dist/types.d.ts +5 -0
- package/dist/types.js.map +1 -1
- package/dist/usage.d.ts +66 -12
- package/dist/usage.js +134 -21
- package/dist/usage.js.map +1 -1
- package/dist/workspace.d.ts +80 -1
- package/dist/workspace.js +188 -8
- package/dist/workspace.js.map +1 -1
- package/hooks/hooks.json +16 -0
- package/package.json +2 -2
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "spec-harness",
|
|
3
|
-
"version": "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
|