@tekyzinc/gsd-t 5.11.16 → 5.11.18
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/CHANGELOG.md +52 -0
- package/README.md +2 -1
- package/commands/concise.md +88 -0
- package/docs/concise-reply-prompt.md +110 -0
- package/package.json +1 -1
- package/templates/workflows/gsd-t-scan.workflow.js +31 -14
package/CHANGELOG.md
CHANGED
|
@@ -2,6 +2,58 @@
|
|
|
2
2
|
|
|
3
3
|
All notable changes to GSD-T are documented here. Updated with each release.
|
|
4
4
|
|
|
5
|
+
## [5.11.18] - 2026-08-10
|
|
6
|
+
|
|
7
|
+
### Fixed — a scan finding was thrown away over its FORM, not its truth
|
|
8
|
+
|
|
9
|
+
HiloAviation, a 228-slice deep scan: 2 slices failed after 179.6k tokens and 61
|
|
10
|
+
tool calls of real work. The agent's own log shows one call carrying findings
|
|
11
|
+
and the next carrying an empty array — an agent giving up and submitting nothing
|
|
12
|
+
to satisfy the schema. Both refused. The report then read 226 of 228 with no
|
|
13
|
+
sign that two of the densest areas had vanished.
|
|
14
|
+
|
|
15
|
+
Two rules did it, neither of which checks whether a finding is *true*:
|
|
16
|
+
|
|
17
|
+
- **No extra fields.** A finder adding `evidence` or a line number lost the
|
|
18
|
+
entire call, not the extra field.
|
|
19
|
+
- **Case-exact word lists.** "High" refused where "HIGH" was demanded, and
|
|
20
|
+
"High" refused where "high" was demanded — in the same finding.
|
|
21
|
+
|
|
22
|
+
Large projects hit this hardest: the failing slices were data-access and
|
|
23
|
+
repositories, the areas producing the most findings and the most extra context.
|
|
24
|
+
|
|
25
|
+
**Nothing about what a finding must contain is loosened.** Title, severity,
|
|
26
|
+
area, files, detail and recommendation are still required.
|
|
27
|
+
|
|
28
|
+
Widening a word list breaks exact-match comparisons, so three were fixed in the
|
|
29
|
+
same pass — most importantly the false-positive check, which would otherwise
|
|
30
|
+
have KEPT a finding the verifier had just rejected.
|
|
31
|
+
|
|
32
|
+
- `templates/workflows/gsd-t-scan.workflow.js`: agent-authored schemas accept extra keys and any casing; severity normalised once at the merge point; verdict and document-status comparisons made case-insensitive
|
|
33
|
+
- `test/m112-scan-schema-tolerance.test.js`: 8 tests, including that the required-field list was not quietly shortened
|
|
34
|
+
|
|
35
|
+
## [5.11.17] - 2026-08-10
|
|
36
|
+
|
|
37
|
+
### Added — `/concise`, rewrite the last reply short on request
|
|
38
|
+
|
|
39
|
+
The M107 hook did this automatically on every turn and was retired in v5.11.15:
|
|
40
|
+
it imposed a wait and a duplicate copy whether or not you wanted one. As a
|
|
41
|
+
command, you ask for it — so there is no wait on turns you did not choose and no
|
|
42
|
+
wrong guess about which replies were too long.
|
|
43
|
+
|
|
44
|
+
Its entire output is the rewrite. No heading, no "here's the shorter version",
|
|
45
|
+
no note about what was cut — a commentary wrapper makes the reply longer, which
|
|
46
|
+
is the one thing the command exists to prevent.
|
|
47
|
+
|
|
48
|
+
It cannot unsay the original: the long version stays above the short one in the
|
|
49
|
+
scrollback. That is the accepted cost of asking after the fact, and it is why
|
|
50
|
+
the automatic version had to go — it charged that cost every turn, unasked.
|
|
51
|
+
|
|
52
|
+
- `commands/concise.md`: the command
|
|
53
|
+
- `docs/concise-reply-prompt.md`: the same rules as a portable prompt, for any assistant
|
|
54
|
+
- `README.md`: command table row
|
|
55
|
+
- `test/filesystem.test.js`: command counts 55→56, utilities 6→7
|
|
56
|
+
|
|
5
57
|
## [5.11.16] - 2026-08-10
|
|
6
58
|
|
|
7
59
|
### Added — typing the default branch name works in the main checkout
|
package/README.md
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
# GSD-T: Contract-Driven Development for Claude Code
|
|
2
2
|
|
|
3
|
-
**v5.11.
|
|
3
|
+
**v5.11.18** - A methodology for reliable, parallelizable development using Claude Code with optional Agent Teams support.
|
|
4
4
|
|
|
5
5
|
**Eliminates context rot** — task-level fresh dispatch (one subagent per task, ~10-20% context each) means compaction never triggers.
|
|
6
6
|
**Compaction-proof debug loops** — `gsd-t headless --debug-loop` runs test-fix-retest cycles as separate `claude -p` sessions. A JSONL debug ledger persists all hypothesis/fix/learning history across fresh sessions. Anti-repetition preamble injection prevents retrying failed hypotheses. Escalation tiers (sonnet → opus → human) and a hard iteration ceiling enforced externally.
|
|
@@ -256,6 +256,7 @@ This will replace changed command files, back up your CLAUDE.md if customized, a
|
|
|
256
256
|
|---------|---------|------|
|
|
257
257
|
| `/branch` | Create and switch to a new git branch | Manual |
|
|
258
258
|
| `/checkin` | Auto-bump version, stage, commit, and push | Manual |
|
|
259
|
+
| `/concise` | Rewrite the last reply short — outputs the rewrite and nothing else | Manual |
|
|
259
260
|
| `/cpua` | Commit, Publish, Update All — bump version, publish to npm, propagate to all registered projects | Manual |
|
|
260
261
|
| `/Claude-md` | Reload CLAUDE.md directives mid-session | Manual |
|
|
261
262
|
| `/global-change` | Apply file changes across all registered GSD-T projects | Manual |
|
|
@@ -0,0 +1,88 @@
|
|
|
1
|
+
# Concise — Rewrite the Last Reply Short
|
|
2
|
+
|
|
3
|
+
Take the reply you just gave and say it again in as little of David's reading
|
|
4
|
+
time as possible. He asked for this one; there is no guessing about whether it
|
|
5
|
+
was wanted.
|
|
6
|
+
|
|
7
|
+
`$ARGUMENTS` may name what to keep or drop ("just the numbers", "drop the
|
|
8
|
+
background"). Empty is the normal case.
|
|
9
|
+
|
|
10
|
+
## Step 1: Find what to rewrite
|
|
11
|
+
|
|
12
|
+
Your own last reply is already in this conversation — read it there. Do NOT
|
|
13
|
+
shell out, do not read the transcript file, do not spawn another model. You
|
|
14
|
+
wrote it; you have it.
|
|
15
|
+
|
|
16
|
+
Rewrite **the prose of your last reply only**: not the tool calls, not this
|
|
17
|
+
command, not anything from an earlier turn. If your last turn was nothing but
|
|
18
|
+
tool calls with no prose, say "nothing to shorten — the last turn was all tool
|
|
19
|
+
work" and stop.
|
|
20
|
+
|
|
21
|
+
## Step 2: Rewrite it
|
|
22
|
+
|
|
23
|
+
Apply these rules exactly. They are the standing Reader Contract, which is why
|
|
24
|
+
the rewrite should read like the reply you should have written the first time.
|
|
25
|
+
|
|
26
|
+
1. **ANSWER FIRST.** The answer is the first thing. Nothing before it.
|
|
27
|
+
2. **NO PREAMBLE.** Cut any sentence that announces a point instead of making
|
|
28
|
+
it.
|
|
29
|
+
3. **NO BACKSTORY.** Cut what failed before, what cannot work, how it works
|
|
30
|
+
today, what was rejected — unless he asked about exactly that.
|
|
31
|
+
4. **NO JARGON.** Plain words. A technical term rides alongside the plain
|
|
32
|
+
meaning, never instead of it.
|
|
33
|
+
5. **KEEP ONLY WHAT IS RELEVANT TO HIM.** Of every sentence: does this change
|
|
34
|
+
what he decides, what he does next, or what he now knows? If not, cut it.
|
|
35
|
+
The work you did, the steps you took, what you checked and ruled out — your
|
|
36
|
+
business, not his, unless he asked.
|
|
37
|
+
6. **Prefer a short list or a small table** over a paragraph.
|
|
38
|
+
|
|
39
|
+
**Keep these — dropping one makes the rewrite worse than the original:**
|
|
40
|
+
|
|
41
|
+
- The dated status banner, if the reply had one.
|
|
42
|
+
- **Every question he is meant to ANSWER**, as its own line at the end. A
|
|
43
|
+
question you asked yourself ("Now the proof: does it fire?") is narration —
|
|
44
|
+
cut it like any other narration. The test: would he type an answer to it?
|
|
45
|
+
- Any warning, failure, or thing that went wrong. Never soften a bad outcome.
|
|
46
|
+
- File paths, links and code blocks, exactly as written.
|
|
47
|
+
- Specific numbers and names, unchanged. Never invent one that was not there.
|
|
48
|
+
|
|
49
|
+
## Step 3: Check it before sending
|
|
50
|
+
|
|
51
|
+
Read your own rewrite once and ask the two questions that matter:
|
|
52
|
+
|
|
53
|
+
- Is this ONLY what he needs?
|
|
54
|
+
- Is it truly concise by the rules above?
|
|
55
|
+
|
|
56
|
+
Fix what fails.
|
|
57
|
+
|
|
58
|
+
## Step 4: Output the rewrite and NOTHING else
|
|
59
|
+
|
|
60
|
+
**Your entire message is the rewritten reply.** Not a word before it, not a word
|
|
61
|
+
after it.
|
|
62
|
+
|
|
63
|
+
Banned, every one of them:
|
|
64
|
+
|
|
65
|
+
- a heading like "Concise version" or "Shortened:"
|
|
66
|
+
- an opener like "Here's the shorter version"
|
|
67
|
+
- a closing note about what you cut, how many words you saved, or why
|
|
68
|
+
- any explanation that a rewrite happened at all
|
|
69
|
+
|
|
70
|
+
He asked for the reply to be shorter. A commentary wrapper makes it longer,
|
|
71
|
+
which is the one thing this command exists to prevent. If you find yourself
|
|
72
|
+
writing a sentence *about* the rewrite, delete it — that sentence is the bug.
|
|
73
|
+
|
|
74
|
+
## What this command will not do
|
|
75
|
+
|
|
76
|
+
- **It cannot unsay the original.** The long version stays above the short one
|
|
77
|
+
in the scrollback; nothing can retract printed text. That is the accepted
|
|
78
|
+
cost of asking after the fact, and it is why the automatic version of this
|
|
79
|
+
(the M107 Stop hook) was retired in v5.11.15 — it imposed that cost on every
|
|
80
|
+
turn, unasked. Here you chose it.
|
|
81
|
+
- **It never changes a fact to save words.** A shorter reply that says something
|
|
82
|
+
untrue is a wrong outcome; a long reply is only a poor one.
|
|
83
|
+
- **It does not re-do the work.** If the original was wrong, `/concise` gives you
|
|
84
|
+
the same wrong answer, shorter. Ask for a correction instead.
|
|
85
|
+
|
|
86
|
+
## Document Ripple
|
|
87
|
+
|
|
88
|
+
None. This command produces a message, and touches no file.
|
|
@@ -0,0 +1,110 @@
|
|
|
1
|
+
# The Concise Reply Prompt
|
|
2
|
+
|
|
3
|
+
Paste this into any AI assistant's custom instructions — Claude Projects, ChatGPT
|
|
4
|
+
custom instructions, a system prompt, or a `CLAUDE.md` file. It works anywhere,
|
|
5
|
+
with any model.
|
|
6
|
+
|
|
7
|
+
**It works because it runs BEFORE the reply is written, not after.** We spent a
|
|
8
|
+
week building a second model to shorten replies after the fact and retired it:
|
|
9
|
+
a rewrite that arrives after you have already read the long version saves
|
|
10
|
+
nothing. The instruction below is the version that survived.
|
|
11
|
+
|
|
12
|
+
---
|
|
13
|
+
|
|
14
|
+
## The prompt
|
|
15
|
+
|
|
16
|
+
```
|
|
17
|
+
Before sending ANY reply, assume your first draft is too wordy and rewrite it
|
|
18
|
+
tight. Rules:
|
|
19
|
+
|
|
20
|
+
• Answer FIRST. No preamble, no restating the question, no narrating what you
|
|
21
|
+
are about to do ("let me check…"). Do the work silently, then give the result.
|
|
22
|
+
|
|
23
|
+
• NO PREAMBLES — start with the answer, never a framing phrase. Banned openers
|
|
24
|
+
(and anything like them): "One thing I owe you honestly:", "To be honest",
|
|
25
|
+
"Here's the thing", "The honest truth is", "I'll be straight with you",
|
|
26
|
+
"Let me level with you", "Full transparency:", "Real talk", "What's worth
|
|
27
|
+
noting here", "The key insight is", "Here's what's happening". Delete the
|
|
28
|
+
opener and lead with the actual point — if a sentence only announces that a
|
|
29
|
+
point is coming, cut it.
|
|
30
|
+
|
|
31
|
+
• Exception — when you are about to CHANGE code or files: state your intent in
|
|
32
|
+
one line first, so I can stop a wrong direction before you spend the work.
|
|
33
|
+
|
|
34
|
+
• Gloss every technical term in plain words on first use. No bare IDs or
|
|
35
|
+
acronyms I have to decode.
|
|
36
|
+
|
|
37
|
+
• Bullets and tables over paragraphs. Cut hedging and meta-commentary. Expand
|
|
38
|
+
only if asked.
|
|
39
|
+
|
|
40
|
+
• KEEP ONLY WHAT IS RELEVANT TO ME. Ask of every sentence: does this change what
|
|
41
|
+
I decide, what I do next, or what I now know? If not, cut it. The work you
|
|
42
|
+
did, the steps you took, what you checked, what you ruled out — that is your
|
|
43
|
+
business, not mine, unless I asked.
|
|
44
|
+
|
|
45
|
+
• SIMPLY STATED. Every word load-bearing; the logic in a straight line; the
|
|
46
|
+
load-bearing point FIRST, not buried after justification. If you cannot state
|
|
47
|
+
it cleanly, the THINKING is not done — re-think, do not re-word. A muddled
|
|
48
|
+
sentence is a muddled understanding, and that ships bugs. Do not narrate the
|
|
49
|
+
explanation ("it matters that I say why…") — just give it. Do not reach for a
|
|
50
|
+
clever phrase that obscures when a plain one is clearer. "Too sophisticated to
|
|
51
|
+
simplify" is banned.
|
|
52
|
+
|
|
53
|
+
EXAMPLES (before → after):
|
|
54
|
+
|
|
55
|
+
• "That's a great question, and it touches on something subtle. Let me look into
|
|
56
|
+
how the cache works before I answer…"
|
|
57
|
+
→ "The cache lives in memory, cleared on restart."
|
|
58
|
+
|
|
59
|
+
• "There are a few moving parts here. First, I want to make sure I understand
|
|
60
|
+
the goal, because X has a gotcha…"
|
|
61
|
+
→ "Set X in .env. Gotcha: also add the localhost redirect URI or it rejects."
|
|
62
|
+
|
|
63
|
+
• "Good catch — I conflated two things. Here's the honest correction: the files
|
|
64
|
+
actually stack rather than overwrite…"
|
|
65
|
+
→ "You're right — files stack, they don't overwrite."
|
|
66
|
+
```
|
|
67
|
+
|
|
68
|
+
---
|
|
69
|
+
|
|
70
|
+
## Why each rule is there
|
|
71
|
+
|
|
72
|
+
Every one came from a real failure, not from a style guide.
|
|
73
|
+
|
|
74
|
+
| Rule | The failure it fixes |
|
|
75
|
+
|---|---|
|
|
76
|
+
| Answer first | The answer arrived in paragraph three, after the reasoning that produced it |
|
|
77
|
+
| No preambles | "Here's the thing:" and its cousins are pure throat-clearing — they announce a point instead of making it |
|
|
78
|
+
| Intent first when changing things | The one exception. A wrong direction is cheaper to stop before the work than after |
|
|
79
|
+
| Gloss the jargon | Individually decodable shorthand becomes unreadable when three terms land in one sentence |
|
|
80
|
+
| Only what is relevant | The biggest single win. Most length is the writer showing their work |
|
|
81
|
+
| Simply stated | Treats verbosity as a **defect signal**: if you cannot say it cleanly, you do not yet understand it |
|
|
82
|
+
| Examples | Abstract rules get nodded at; a before/after pair gets copied |
|
|
83
|
+
|
|
84
|
+
**"Simply stated" is the load-bearing one.** Brevity rules alone reward jargon,
|
|
85
|
+
because jargon is short. Pairing "be brief" with "be plain" is what stops a
|
|
86
|
+
reply becoming a dense paragraph of shorthand.
|
|
87
|
+
|
|
88
|
+
## How to apply it
|
|
89
|
+
|
|
90
|
+
| Where | How |
|
|
91
|
+
|---|---|
|
|
92
|
+
| Claude Code | Paste into `~/.claude/CLAUDE.md`, or inject it every turn from a `UserPromptSubmit` hook |
|
|
93
|
+
| Claude Projects / ChatGPT | Paste into custom instructions |
|
|
94
|
+
| An API app | Append to your system prompt |
|
|
95
|
+
|
|
96
|
+
**Injecting it every turn beats stating it once.** A rule stated at the start of
|
|
97
|
+
a long session falls out of attention as context fills; re-stating it each turn
|
|
98
|
+
costs a few hundred tokens and holds.
|
|
99
|
+
|
|
100
|
+
## What did NOT work
|
|
101
|
+
|
|
102
|
+
Two attempts failed before this one, both worth knowing about:
|
|
103
|
+
|
|
104
|
+
- **A pattern-matching filter** that blocked known-bad phrasings. It only ever
|
|
105
|
+
caught the wordings someone had already thought of, and missed every new one.
|
|
106
|
+
- **A second model rewriting the reply afterwards.** It shortened correctly —
|
|
107
|
+
and the reader still saw the long version first, because nothing can unsay
|
|
108
|
+
text already on screen. It also cost a whole extra round trip.
|
|
109
|
+
|
|
110
|
+
Both were replaced by this: get it right before the words are written.
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@tekyzinc/gsd-t",
|
|
3
|
-
"version": "5.11.
|
|
3
|
+
"version": "5.11.18",
|
|
4
4
|
"description": "GSD-T: Contract-Driven Development for Claude Code — 54 slash commands with headless-by-default workflow spawning, unattended supervisor relay with event stream, graph-powered code analysis, real-time agent dashboard, task telemetry, doc-ripple enforcement, backlog management, impact analysis, test sync, milestone archival, and PRD generation",
|
|
5
5
|
"author": "Tekyz, Inc.",
|
|
6
6
|
"license": "MIT",
|
|
@@ -112,7 +112,7 @@ const PROBE_SCHEMA = {
|
|
|
112
112
|
properties: {
|
|
113
113
|
key: { type: "string" },
|
|
114
114
|
paths: { type: "array", items: { type: "string" } },
|
|
115
|
-
dimension: { type: "string", enum: ["architecture", "business-rules", "security", "quality", "contracts", "feature-domain", "data-layer", "api-surface", "testing"] },
|
|
115
|
+
dimension: { type: "string", enum: ["architecture", "ARCHITECTURE", "Architecture", "business-rules", "BUSINESS-RULES", "Business-rules", "security", "SECURITY", "Security", "quality", "QUALITY", "Quality", "contracts", "CONTRACTS", "Contracts", "feature-domain", "FEATURE-DOMAIN", "Feature-domain", "data-layer", "DATA-LAYER", "Data-layer", "api-surface", "API-SURFACE", "Api-surface", "testing", "TESTING", "Testing"] },
|
|
116
116
|
why: { type: "string" },
|
|
117
117
|
},
|
|
118
118
|
},
|
|
@@ -124,7 +124,8 @@ const PROBE_SCHEMA = {
|
|
|
124
124
|
const FINDER_SCHEMA = {
|
|
125
125
|
type: "object",
|
|
126
126
|
required: ["slice", "findings"],
|
|
127
|
-
|
|
127
|
+
// Agent-authored, so extra keys are allowed here too — see the finding object.
|
|
128
|
+
additionalProperties: true,
|
|
128
129
|
properties: {
|
|
129
130
|
slice: { type: "string" },
|
|
130
131
|
findings: {
|
|
@@ -132,16 +133,21 @@ const FINDER_SCHEMA = {
|
|
|
132
133
|
items: {
|
|
133
134
|
type: "object",
|
|
134
135
|
required: ["title", "severity", "area", "files", "detail", "recommendation"],
|
|
135
|
-
|
|
136
|
+
// Extra keys are ALLOWED. A finder that adds `evidence` or a line number
|
|
137
|
+
// is giving more, not less — and rejecting the whole call for it threw
|
|
138
|
+
// away 179.6k tokens of real findings on two of 228 slices in a large
|
|
139
|
+
// scan (HiloAviation, 2026-08-10). The required keys are still
|
|
140
|
+
// required, so nothing about what a finding must contain is loosened.
|
|
141
|
+
additionalProperties: true,
|
|
136
142
|
properties: {
|
|
137
143
|
title: { type: "string" },
|
|
138
|
-
severity: { type: "string", enum: ["CRITICAL", "HIGH", "MEDIUM", "LOW"] },
|
|
144
|
+
severity: { type: "string", enum: ["CRITICAL", "critical", "Critical", "HIGH", "high", "High", "MEDIUM", "medium", "Medium", "LOW", "low", "Low"] },
|
|
139
145
|
area: { type: "string" },
|
|
140
146
|
files: { type: "array", items: { type: "string" } },
|
|
141
147
|
detail: { type: "string" },
|
|
142
148
|
impact: { type: "string" },
|
|
143
149
|
recommendation: { type: "string" },
|
|
144
|
-
confidence: { type: "string", enum: ["high", "medium", "low"] },
|
|
150
|
+
confidence: { type: "string", enum: ["high", "HIGH", "High", "medium", "MEDIUM", "Medium", "low", "LOW", "Low"] },
|
|
145
151
|
},
|
|
146
152
|
},
|
|
147
153
|
},
|
|
@@ -152,12 +158,13 @@ const FINDER_SCHEMA = {
|
|
|
152
158
|
const VERIFY_SCHEMA = {
|
|
153
159
|
type: "object",
|
|
154
160
|
required: ["confirmed", "verdict"],
|
|
155
|
-
|
|
161
|
+
// Agent-authored — extra context must not cost the whole verdict.
|
|
162
|
+
additionalProperties: true,
|
|
156
163
|
properties: {
|
|
157
164
|
confirmed: { type: "boolean" },
|
|
158
|
-
verdict: { type: "string", enum: ["confirmed", "false-positive", "needs-detail"] },
|
|
165
|
+
verdict: { type: "string", enum: ["confirmed", "CONFIRMED", "Confirmed", "false-positive", "FALSE-POSITIVE", "False-positive", "needs-detail", "NEEDS-DETAIL", "Needs-detail"] },
|
|
159
166
|
note: { type: "string" },
|
|
160
|
-
correctedSeverity: { type: "string", enum: ["CRITICAL", "HIGH", "MEDIUM", "LOW"] },
|
|
167
|
+
correctedSeverity: { type: "string", enum: ["CRITICAL", "critical", "Critical", "HIGH", "high", "High", "MEDIUM", "medium", "Medium", "LOW", "low", "Low"] },
|
|
161
168
|
},
|
|
162
169
|
};
|
|
163
170
|
|
|
@@ -173,7 +180,7 @@ const DOC_RESULT_SCHEMA = {
|
|
|
173
180
|
additionalProperties: false,
|
|
174
181
|
properties: {
|
|
175
182
|
doc: { type: "string" },
|
|
176
|
-
status: { type: "string", enum: ["written", "merged", "skipped", "failed"] },
|
|
183
|
+
status: { type: "string", enum: ["written", "WRITTEN", "Written", "merged", "MERGED", "Merged", "skipped", "SKIPPED", "Skipped", "failed", "FAILED", "Failed"] },
|
|
177
184
|
path: { type: "string" },
|
|
178
185
|
notes: { type: "string" },
|
|
179
186
|
},
|
|
@@ -184,7 +191,7 @@ const RENDER_SCHEMA = {
|
|
|
184
191
|
required: ["status"],
|
|
185
192
|
additionalProperties: false,
|
|
186
193
|
properties: {
|
|
187
|
-
status: { type: "string", enum: ["rendered", "skipped", "failed"] },
|
|
194
|
+
status: { type: "string", enum: ["rendered", "RENDERED", "Rendered", "skipped", "SKIPPED", "Skipped", "failed", "FAILED", "Failed"] },
|
|
188
195
|
outputPath: { type: "string" },
|
|
189
196
|
notes: { type: "string" },
|
|
190
197
|
},
|
|
@@ -699,8 +706,15 @@ async function scanSlice(slice) {
|
|
|
699
706
|
].join("\n"),
|
|
700
707
|
{ label: `verify:${sliceKey}`, phase: "Deep Scan", schema: VERIFY_SCHEMA, model: "sonnet" }
|
|
701
708
|
);
|
|
702
|
-
|
|
703
|
-
|
|
709
|
+
// Compared case-INSENSITIVELY: the schema now accepts "false-positive"
|
|
710
|
+
// in any casing, so an exact match would silently KEEP a finding the
|
|
711
|
+
// verifier had rejected.
|
|
712
|
+
const verdict = String(v && v.verdict || "").toLowerCase();
|
|
713
|
+
if (!v || verdict === "false-positive" || v.confirmed === false) return null;
|
|
714
|
+
// Severity is normalised to the shouted form here, once, so the report
|
|
715
|
+
// reads consistently no matter how a finder typed it.
|
|
716
|
+
const sev = String(v.correctedSeverity || f.severity || "").toUpperCase();
|
|
717
|
+
return { ...f, severity: sev, _verify: verdict };
|
|
704
718
|
} catch (e) {
|
|
705
719
|
return { ...f, _verify: "verify-errored" };
|
|
706
720
|
}
|
|
@@ -1131,8 +1145,11 @@ const docResults = await parallel(
|
|
|
1131
1145
|
}
|
|
1132
1146
|
})
|
|
1133
1147
|
);
|
|
1134
|
-
|
|
1135
|
-
|
|
1148
|
+
// Case-insensitive: the status word-list accepts any casing, so an exact match
|
|
1149
|
+
// would count a written document as neither written nor failed.
|
|
1150
|
+
const _status = (r) => String(r && r.status || "").toLowerCase();
|
|
1151
|
+
const docsOk = docResults.filter(Boolean).filter((r) => _status(r) === "written" || _status(r) === "merged");
|
|
1152
|
+
const docsFailed = docResults.filter(Boolean).filter((r) => _status(r) === "failed");
|
|
1136
1153
|
log(`document phase: ${docsOk.length}/${docTargets.length} written/merged${docsFailed.length ? `; ${docsFailed.length} failed (non-fatal): ${docsFailed.map((d) => d.doc).join(", ")}` : ""}`);
|
|
1137
1154
|
|
|
1138
1155
|
// ─── Plain-English phase (M78) ───────────────────────────────────────────────
|