@popovych.co/witness 0.14.3 → 0.14.5
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/dist/verdict.js +18 -4
- package/dist/version.js +6 -2
- package/package.json +1 -1
- package/plugin/.claude-plugin/plugin.json +1 -1
- package/plugin/commands/witness.md +1 -1
- package/plugin/hooks/session-dashboard.sh +1 -1
- package/plugin/skills/witness-brainstorm/SKILL.md +1 -1
- package/plugin/skills/witness-decompose/SKILL.md +1 -1
- package/plugin/skills/witness-design/SKILL.md +1 -1
- package/plugin/skills/witness-implement/SKILL.md +1 -1
- package/plugin/skills/witness-plan/SKILL.md +1 -1
- package/plugin/skills/witness-ship/SKILL.md +1 -1
- package/prompts/code-reviewer.md +1 -1
- package/prompts/drift-reviewer.md +1 -1
- package/prompts/plan-critic.md +1 -1
- package/prompts/pr-test.md +1 -1
- package/prompts/silent-failure-hunter.md +1 -1
- package/prompts/slicing-critic.md +1 -1
- package/prompts/type-design.md +1 -1
package/dist/verdict.js
CHANGED
|
@@ -103,7 +103,7 @@ function resolveDocPath(segments, body) {
|
|
|
103
103
|
function safeRel(rel) {
|
|
104
104
|
return !isAbsolute(rel) && !normalize(rel).split(/[\\/]/).includes('..');
|
|
105
105
|
}
|
|
106
|
-
function resolveCodeAnchor(anchor, root, canon) {
|
|
106
|
+
function resolveCodeAnchor(anchor, root, files, canon) {
|
|
107
107
|
if (/[:#]L?\d+$/.test(anchor))
|
|
108
108
|
return 'line numbers refused — they drift across revisions; use file#symbol';
|
|
109
109
|
const [file = '', symbol] = anchor.split('#', 2);
|
|
@@ -114,8 +114,18 @@ function resolveCodeAnchor(anchor, root, canon) {
|
|
|
114
114
|
// from two trees. Code paths keep the reviewed tree, which is the thing under judgment.
|
|
115
115
|
const isCanon = canon !== undefined && canon.dirs.some((d) => file === d || file.startsWith(`${d}/`));
|
|
116
116
|
const abs = join(isCanon ? canon.root : root, file);
|
|
117
|
-
if (!existsSync(abs) || !statSync(abs).isFile())
|
|
118
|
-
|
|
117
|
+
if (!existsSync(abs) || !statSync(abs).isFile()) {
|
|
118
|
+
// D162 (issue #29). The coverage floor counts every path the diff lists, so every one
|
|
119
|
+
// of them must be anchorable or the floor is unsatisfiable by construction: a submodule
|
|
120
|
+
// pointer is a directory, a deleted file is nothing, a symlink can point at a directory
|
|
121
|
+
// — and each is reviewed content, its hunk in the prompt. The bare path resolves; a
|
|
122
|
+
// symbol still needs a file body to be found in.
|
|
123
|
+
if (!files.includes(file))
|
|
124
|
+
return `no file ${file} in the reviewed tree`;
|
|
125
|
+
if (symbol === undefined)
|
|
126
|
+
return undefined;
|
|
127
|
+
return `${file} is a changed path with no file body in the reviewed tree (a submodule pointer, a deleted file, or a directory) — anchor it by bare path`;
|
|
128
|
+
}
|
|
119
129
|
if (symbol !== undefined) {
|
|
120
130
|
const re = new RegExp(`\\b${symbol.replace(/[.*+?^${}()|[\]\\]/g, '\\$&')}\\b`);
|
|
121
131
|
if (!re.test(readFileSync(abs, 'utf8')))
|
|
@@ -172,6 +182,10 @@ export function resolveAnchor(anchor, reviewed) {
|
|
|
172
182
|
return undefined;
|
|
173
183
|
if (!safeRel(scope))
|
|
174
184
|
return `omission scope escapes the reviewed tree: ${scope}`;
|
|
185
|
+
// D162: a changed path is reviewed content even when nothing is left on disk (a deleted
|
|
186
|
+
// file's un-migrated export is an omission scoped exactly there).
|
|
187
|
+
if (reviewed.kind === 'tree' && reviewed.files.includes(scope))
|
|
188
|
+
return undefined;
|
|
175
189
|
return existsSync(join(reviewed.root, scope))
|
|
176
190
|
? undefined
|
|
177
191
|
: `omission scope "${scope}" is no file or directory in the reviewed tree`;
|
|
@@ -180,7 +194,7 @@ export function resolveAnchor(anchor, reviewed) {
|
|
|
180
194
|
const canon = reviewed.canonRoot !== undefined && reviewed.canonDirs !== undefined
|
|
181
195
|
? { root: reviewed.canonRoot, dirs: reviewed.canonDirs }
|
|
182
196
|
: undefined;
|
|
183
|
-
return resolveCodeAnchor(anchor, reviewed.root, canon);
|
|
197
|
+
return resolveCodeAnchor(anchor, reviewed.root, reviewed.files, canon);
|
|
184
198
|
}
|
|
185
199
|
const segments = anchor.split(' > ');
|
|
186
200
|
if (!segments[0].startsWith('#')) {
|
package/dist/version.js
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { readFileSync } from 'node:fs';
|
|
2
|
-
import {
|
|
2
|
+
import { fileURLToPath } from 'node:url';
|
|
3
3
|
// The one home for "which version of witness is which". install.ts parsed the pin to
|
|
4
4
|
// decide whether a payload was only a pin apart (row 102 killed that rule) and check.ts
|
|
5
5
|
// re-declared the same regex inline — one question answered in two places, which is the
|
|
@@ -44,7 +44,11 @@ export const NPX_LATEST = 'npx -y @popovych.co/witness@latest';
|
|
|
44
44
|
// reader itself lived in the shell — the same split rows 93, 95 and 96 keep naming. The
|
|
45
45
|
// single readFileSync is the exception to this file's no-I/O rule and the only one it
|
|
46
46
|
// may ever have.
|
|
47
|
+
//
|
|
48
|
+
// Row 163. The path goes through fileURLToPath, never `URL.pathname`: a pathname is URL
|
|
49
|
+
// syntax — `/C:/…` on Windows, which readFileSync resolved to `C:\C:\…`, and `%20` for a
|
|
50
|
+
// space on every platform — so the read threw ENOENT and every journal stamp died with it.
|
|
47
51
|
export function version() {
|
|
48
|
-
const pkg = JSON.parse(readFileSync(
|
|
52
|
+
const pkg = JSON.parse(readFileSync(fileURLToPath(new URL('../package.json', import.meta.url)), 'utf8'));
|
|
49
53
|
return pkg.version;
|
|
50
54
|
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@popovych.co/witness",
|
|
3
|
-
"version": "0.14.
|
|
3
|
+
"version": "0.14.5",
|
|
4
4
|
"description": "Evidence-gated spec-driven development pipeline for AI coding agents: brainstorm to PR in six stages, where specs are state, plans are motion, and no transition passes without evidence. Drives from Claude Code or Pi.",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"spec-driven-development",
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "witness",
|
|
3
3
|
"description": "Spec-driven development with evidence gates: brainstorm → PR through six stages and five gates. Skills author, the pinned witness CLI writes state and witnesses evidence; gates stop where judgment is owed.",
|
|
4
|
-
"version": "0.14.
|
|
4
|
+
"version": "0.14.5",
|
|
5
5
|
"author": {
|
|
6
6
|
"name": "Kostiantyn Popovych"
|
|
7
7
|
},
|
|
@@ -10,7 +10,7 @@ The CLI decides, you act. If `$ARGUMENTS` contains `--manual`, append `--manual`
|
|
|
10
10
|
Resolve the CLI once:
|
|
11
11
|
|
|
12
12
|
```bash
|
|
13
|
-
WITNESS="${WITNESS_BIN:-npx -y @popovych.co/witness@0.14.
|
|
13
|
+
WITNESS="${WITNESS_BIN:-npx -y @popovych.co/witness@0.14.5}"
|
|
14
14
|
```
|
|
15
15
|
|
|
16
16
|
If the repo has no `witness.config.yaml`, run `$WITNESS init` first (one bootstrap commit), then proceed. If init or any verb refuses on a dirty tree, the human commits or cleans it — **never `git commit` yourself, anywhere, at any stage**: the CLI makes its own state commits, and the ship phase makes the sole code commit.
|
|
@@ -3,4 +3,4 @@
|
|
|
3
3
|
# context). Silent everywhere that isn't a witness repo; failures swallowed:
|
|
4
4
|
# session start must never hang or die on this.
|
|
5
5
|
[ -f witness.config.yaml ] || exit 0
|
|
6
|
-
${WITNESS_BIN:-npx -y @popovych.co/witness@0.14.
|
|
6
|
+
${WITNESS_BIN:-npx -y @popovych.co/witness@0.14.5} 2>/dev/null || true
|
|
@@ -15,7 +15,7 @@ witness: ends in a confirmed scope recap, writes no document. See NOTICE.md. -->
|
|
|
15
15
|
Resolve the CLI once per session:
|
|
16
16
|
|
|
17
17
|
```bash
|
|
18
|
-
WITNESS="${WITNESS_BIN:-npx -y @popovych.co/witness@0.14.
|
|
18
|
+
WITNESS="${WITNESS_BIN:-npx -y @popovych.co/witness@0.14.5}"
|
|
19
19
|
```
|
|
20
20
|
|
|
21
21
|
- **Render the CLI's decision output verbatim and in full — every line, unmodified.** Never print a command set you remember; never recompose, reformat, summarise or reorder what the CLI emitted. Which decisions are live, how they rank, and what each costs are the CLI's answers, and they change with the round, the bound, the repair grant and the content sha — a remembered set is wrong in more states than it is right.
|
|
@@ -10,7 +10,7 @@ description: Slice a witness effort's confirmed recap into vertical spec slices,
|
|
|
10
10
|
Resolve the CLI once per session:
|
|
11
11
|
|
|
12
12
|
```bash
|
|
13
|
-
WITNESS="${WITNESS_BIN:-npx -y @popovych.co/witness@0.14.
|
|
13
|
+
WITNESS="${WITNESS_BIN:-npx -y @popovych.co/witness@0.14.5}"
|
|
14
14
|
```
|
|
15
15
|
|
|
16
16
|
- **Render the CLI's decision output verbatim and in full — every line, unmodified.** Never print a command set you remember; never recompose, reformat, summarise or reorder what the CLI emitted. Which decisions are live, how they rank, and what each costs are the CLI's answers, and they change with the round, the bound, the repair grant and the content sha — a remembered set is wrong in more states than it is right.
|
|
@@ -16,7 +16,7 @@ No text copied verbatim; the design-critic lens is new material. -->
|
|
|
16
16
|
Resolve the CLI once per session:
|
|
17
17
|
|
|
18
18
|
```bash
|
|
19
|
-
WITNESS="${WITNESS_BIN:-npx -y @popovych.co/witness@0.14.
|
|
19
|
+
WITNESS="${WITNESS_BIN:-npx -y @popovych.co/witness@0.14.5}"
|
|
20
20
|
```
|
|
21
21
|
|
|
22
22
|
- **Render the CLI's decision output verbatim and in full — every line, unmodified.** Never print a command set you remember; never recompose, reformat, summarise or reorder what the CLI emitted. Which decisions are live, how they rank, and what each costs are the CLI's answers, and they change with the round, the bound, the repair grant and the content sha — a remembered set is wrong in more states than it is right.
|
|
@@ -15,7 +15,7 @@ tests carry the parent spec's tag in their NAME. See NOTICE.md. -->
|
|
|
15
15
|
Resolve the CLI once per session:
|
|
16
16
|
|
|
17
17
|
```bash
|
|
18
|
-
WITNESS="${WITNESS_BIN:-npx -y @popovych.co/witness@0.14.
|
|
18
|
+
WITNESS="${WITNESS_BIN:-npx -y @popovych.co/witness@0.14.5}"
|
|
19
19
|
```
|
|
20
20
|
|
|
21
21
|
- **Render the CLI's decision output verbatim and in full — every line, unmodified.** Never print a command set you remember; never recompose, reformat, summarise or reorder what the CLI emitted. Which decisions are live, how they rank, and what each costs are the CLI's answers, and they change with the round, the bound, the repair grant and the content sha — a remembered set is wrong in more states than it is right.
|
|
@@ -15,7 +15,7 @@ parent's criteria. See NOTICE.md. -->
|
|
|
15
15
|
Resolve the CLI once per session:
|
|
16
16
|
|
|
17
17
|
```bash
|
|
18
|
-
WITNESS="${WITNESS_BIN:-npx -y @popovych.co/witness@0.14.
|
|
18
|
+
WITNESS="${WITNESS_BIN:-npx -y @popovych.co/witness@0.14.5}"
|
|
19
19
|
```
|
|
20
20
|
|
|
21
21
|
- **Render the CLI's decision output verbatim and in full — every line, unmodified.** Never print a command set you remember; never recompose, reformat, summarise or reorder what the CLI emitted. Which decisions are live, how they rank, and what each costs are the CLI's answers, and they change with the round, the bound, the repair grant and the content sha — a remembered set is wrong in more states than it is right.
|
|
@@ -15,7 +15,7 @@ model can. See NOTICE.md. -->
|
|
|
15
15
|
Resolve the CLI once per session:
|
|
16
16
|
|
|
17
17
|
```bash
|
|
18
|
-
WITNESS="${WITNESS_BIN:-npx -y @popovych.co/witness@0.14.
|
|
18
|
+
WITNESS="${WITNESS_BIN:-npx -y @popovych.co/witness@0.14.5}"
|
|
19
19
|
```
|
|
20
20
|
|
|
21
21
|
- **Render the CLI's decision output verbatim and in full — every line, unmodified.** Never print a command set you remember; never recompose, reformat, summarise or reorder what the CLI emitted. Which decisions are live, how they rank, and what each costs are the CLI's answers, and they change with the round, the bound, the repair grant and the content sha — a remembered set is wrong in more states than it is right.
|
package/prompts/code-reviewer.md
CHANGED
|
@@ -51,7 +51,7 @@ Respond with ONLY a JSON object — no prose before or after it (a single ```jso
|
|
|
51
51
|
Anchor grammar — every anchor must resolve against the reviewed content, or your whole verdict is rejected as malformed:
|
|
52
52
|
|
|
53
53
|
- Document reviews: a heading path, ` > `-separated — `## Behavior`, or `auth-refresh > ## Behavior` where a first segment without `#` names a reviewed document.
|
|
54
|
-
- Code reviews: `path/to/file` or `path/to/file#symbol` (the symbol must occur in that file). Never line numbers — `file.ts:42` rejects the verdict.
|
|
54
|
+
- Code reviews: `path/to/file` or `path/to/file#symbol` (the symbol must occur in that file). A changed path with no file body — a submodule pointer, a deleted file — is anchored by its bare path; the diff hunk is the evidence. Never line numbers — `file.ts:42` rejects the verdict.
|
|
55
55
|
- Something missing entirely: the finding's `"anchor"` value itself becomes `{ "kind": "omission", "scope": "<doc id, heading path, file, directory, or .>" }` — e.g. a full finding reads `{ "blocking": true, "anchor": { "kind": "omission", "scope": "tests/" }, "claim": "..." }`. Never place `kind`/`scope` as siblings of `blocking`/`claim` — they replace the `anchor` value, not sit beside it. The scope must exist; blocking omissions stop the gate like any other finding.
|
|
56
56
|
|
|
57
57
|
Coverage is proof of reading, required even when `findings` is empty:
|
|
@@ -27,7 +27,7 @@ Respond with ONLY a JSON object — no prose before or after it (a single ```jso
|
|
|
27
27
|
Anchor grammar — every anchor must resolve against the reviewed content, or your whole verdict is rejected as malformed:
|
|
28
28
|
|
|
29
29
|
- Document reviews: a heading path, ` > `-separated — `## Behavior`, or `auth-refresh > ## Behavior` where a first segment without `#` names a reviewed document. A `## Valid anchors` menu precedes the reviewed content — copy anchors from it verbatim; never paraphrase a heading.
|
|
30
|
-
- Code reviews: `path/to/file` or `path/to/file#symbol` (the symbol must occur in that file). Never line numbers — `file.ts:42` rejects the verdict.
|
|
30
|
+
- Code reviews: `path/to/file` or `path/to/file#symbol` (the symbol must occur in that file). A changed path with no file body — a submodule pointer, a deleted file — is anchored by its bare path; the diff hunk is the evidence. Never line numbers — `file.ts:42` rejects the verdict.
|
|
31
31
|
- Something missing entirely: the finding's `"anchor"` value itself becomes `{ "kind": "omission", "scope": "<doc id, heading path, file, directory, or .>" }` — e.g. a full finding reads `{ "blocking": true, "anchor": { "kind": "omission", "scope": "tests/" }, "claim": "..." }`. Never place `kind`/`scope` as siblings of `blocking`/`claim` — they replace the `anchor` value, not sit beside it. The scope must exist; blocking omissions stop the gate like any other finding.
|
|
32
32
|
|
|
33
33
|
Coverage is proof of reading, required even when `findings` is empty:
|
package/prompts/plan-critic.md
CHANGED
|
@@ -24,7 +24,7 @@ Respond with ONLY a JSON object — no prose before or after it (a single ```jso
|
|
|
24
24
|
Anchor grammar — every anchor must resolve against the reviewed content, or your whole verdict is rejected as malformed:
|
|
25
25
|
|
|
26
26
|
- Document reviews: a heading path, ` > `-separated — `## Behavior`, or `auth-refresh > ## Behavior` where a first segment without `#` names a reviewed document. A `## Valid anchors` menu precedes the reviewed content — copy anchors from it verbatim; never paraphrase a heading.
|
|
27
|
-
- Code reviews: `path/to/file` or `path/to/file#symbol` (the symbol must occur in that file). Never line numbers — `file.ts:42` rejects the verdict.
|
|
27
|
+
- Code reviews: `path/to/file` or `path/to/file#symbol` (the symbol must occur in that file). A changed path with no file body — a submodule pointer, a deleted file — is anchored by its bare path; the diff hunk is the evidence. Never line numbers — `file.ts:42` rejects the verdict.
|
|
28
28
|
- Something missing entirely: the finding's `"anchor"` value itself becomes `{ "kind": "omission", "scope": "<doc id, heading path, file, directory, or .>" }` — e.g. a full finding reads `{ "blocking": true, "anchor": { "kind": "omission", "scope": "tests/" }, "claim": "..." }`. Never place `kind`/`scope` as siblings of `blocking`/`claim` — they replace the `anchor` value, not sit beside it. The scope must exist; blocking omissions stop the gate like any other finding.
|
|
29
29
|
|
|
30
30
|
Coverage is proof of reading, required even when `findings` is empty:
|
package/prompts/pr-test.md
CHANGED
|
@@ -84,7 +84,7 @@ Respond with ONLY a JSON object — no prose before or after it (a single ```jso
|
|
|
84
84
|
Anchor grammar — every anchor must resolve against the reviewed content, or your whole verdict is rejected as malformed:
|
|
85
85
|
|
|
86
86
|
- Document reviews: a heading path, ` > `-separated — `## Behavior`, or `auth-refresh > ## Behavior` where a first segment without `#` names a reviewed document.
|
|
87
|
-
- Code reviews: `path/to/file` or `path/to/file#symbol` (the symbol must occur in that file). Never line numbers — `file.ts:42` rejects the verdict.
|
|
87
|
+
- Code reviews: `path/to/file` or `path/to/file#symbol` (the symbol must occur in that file). A changed path with no file body — a submodule pointer, a deleted file — is anchored by its bare path; the diff hunk is the evidence. Never line numbers — `file.ts:42` rejects the verdict.
|
|
88
88
|
- Something missing entirely: the finding's `"anchor"` value itself becomes `{ "kind": "omission", "scope": "<doc id, heading path, file, directory, or .>" }` — e.g. a full finding reads `{ "blocking": true, "anchor": { "kind": "omission", "scope": "tests/" }, "claim": "..." }`. Never place `kind`/`scope` as siblings of `blocking`/`claim` — they replace the `anchor` value, not sit beside it. The scope must exist; blocking omissions stop the gate like any other finding.
|
|
89
89
|
|
|
90
90
|
Coverage is proof of reading, required even when `findings` is empty:
|
|
@@ -132,7 +132,7 @@ Respond with ONLY a JSON object — no prose before or after it (a single ```jso
|
|
|
132
132
|
Anchor grammar — every anchor must resolve against the reviewed content, or your whole verdict is rejected as malformed:
|
|
133
133
|
|
|
134
134
|
- Document reviews: a heading path, ` > `-separated — `## Behavior`, or `auth-refresh > ## Behavior` where a first segment without `#` names a reviewed document.
|
|
135
|
-
- Code reviews: `path/to/file` or `path/to/file#symbol` (the symbol must occur in that file). Never line numbers — `file.ts:42` rejects the verdict.
|
|
135
|
+
- Code reviews: `path/to/file` or `path/to/file#symbol` (the symbol must occur in that file). A changed path with no file body — a submodule pointer, a deleted file — is anchored by its bare path; the diff hunk is the evidence. Never line numbers — `file.ts:42` rejects the verdict.
|
|
136
136
|
- Something missing entirely: the finding's `"anchor"` value itself becomes `{ "kind": "omission", "scope": "<doc id, heading path, file, directory, or .>" }` — e.g. a full finding reads `{ "blocking": true, "anchor": { "kind": "omission", "scope": "tests/" }, "claim": "..." }`. Never place `kind`/`scope` as siblings of `blocking`/`claim` — they replace the `anchor` value, not sit beside it. The scope must exist; blocking omissions stop the gate like any other finding.
|
|
137
137
|
|
|
138
138
|
Coverage is proof of reading, required even when `findings` is empty:
|
|
@@ -33,7 +33,7 @@ Respond with ONLY a JSON object — no prose before or after it (a single ```jso
|
|
|
33
33
|
Anchor grammar — every anchor must resolve against the reviewed content, or your whole verdict is rejected as malformed:
|
|
34
34
|
|
|
35
35
|
- Document reviews: a heading path, ` > `-separated — `## Behavior`, or `auth-refresh > ## Behavior` where a first segment without `#` names a reviewed document. A `## Valid anchors` menu precedes the reviewed content — copy anchors from it verbatim; never paraphrase a heading.
|
|
36
|
-
- Code reviews: `path/to/file` or `path/to/file#symbol` (the symbol must occur in that file). Never line numbers — `file.ts:42` rejects the verdict.
|
|
36
|
+
- Code reviews: `path/to/file` or `path/to/file#symbol` (the symbol must occur in that file). A changed path with no file body — a submodule pointer, a deleted file — is anchored by its bare path; the diff hunk is the evidence. Never line numbers — `file.ts:42` rejects the verdict.
|
|
37
37
|
- Something missing entirely: the finding's `"anchor"` value itself becomes `{ "kind": "omission", "scope": "<doc id, heading path, file, directory, or .>" }` — e.g. a full finding reads `{ "blocking": true, "anchor": { "kind": "omission", "scope": "tests/" }, "claim": "..." }`. Never place `kind`/`scope` as siblings of `blocking`/`claim` — they replace the `anchor` value, not sit beside it. The scope must exist; blocking omissions stop the gate like any other finding.
|
|
38
38
|
|
|
39
39
|
Coverage is proof of reading, required even when `findings` is empty:
|
package/prompts/type-design.md
CHANGED
|
@@ -100,7 +100,7 @@ Respond with ONLY a JSON object — no prose before or after it (a single ```jso
|
|
|
100
100
|
Anchor grammar — every anchor must resolve against the reviewed content, or your whole verdict is rejected as malformed:
|
|
101
101
|
|
|
102
102
|
- Document reviews: a heading path, ` > `-separated — `## Behavior`, or `auth-refresh > ## Behavior` where a first segment without `#` names a reviewed document.
|
|
103
|
-
- Code reviews: `path/to/file` or `path/to/file#symbol` (the symbol must occur in that file). Never line numbers — `file.ts:42` rejects the verdict.
|
|
103
|
+
- Code reviews: `path/to/file` or `path/to/file#symbol` (the symbol must occur in that file). A changed path with no file body — a submodule pointer, a deleted file — is anchored by its bare path; the diff hunk is the evidence. Never line numbers — `file.ts:42` rejects the verdict.
|
|
104
104
|
- Something missing entirely: the finding's `"anchor"` value itself becomes `{ "kind": "omission", "scope": "<doc id, heading path, file, directory, or .>" }` — e.g. a full finding reads `{ "blocking": true, "anchor": { "kind": "omission", "scope": "tests/" }, "claim": "..." }`. Never place `kind`/`scope` as siblings of `blocking`/`claim` — they replace the `anchor` value, not sit beside it. The scope must exist; blocking omissions stop the gate like any other finding.
|
|
105
105
|
|
|
106
106
|
Coverage is proof of reading, required even when `findings` is empty:
|