@gobing-ai/spur 0.4.4 → 0.4.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.
@@ -47,8 +47,6 @@ export declare interface ParsedScanArgs {
47
47
  spurBin?: string;
48
48
  root: string;
49
49
  tmpDir: string;
50
- /** The --review-answer <path> flag: the review answer the review-gate mode grades (task 1204). */
51
- reviewAnswer?: string;
52
50
  }
53
51
  export declare const ALLOW_PRAGMA: 'residual-scan:allow';
54
52
  export declare function recordedVerdictPath(runDir: string, wbs: string, fs: Pick<typeof import('node:fs'), 'lstatSync' | 'existsSync'>): string;
@@ -70,21 +68,10 @@ export declare function scanResiduals(inputs: ResidualScanInputs): ResidualArtif
70
68
  export declare function parseDeferralEntries(raw: unknown): Deferral[];
71
69
  export declare function blockingReviewFindings(taskContent: string, deferrals: Deferral[]): ResidualItem[];
72
70
  export declare function reviewContractViolations(taskContent: string): Array<{ location: string; text: string }>;
73
- export declare function reviewConfidenceViolation(
74
- reviewAnswer: string,
75
- ackLowConfidence: string | undefined,
76
- ): { location: string; text: string } | null;
77
- export declare function reviewGradingInput(
78
- root: string,
79
- reviewAnswer: string | undefined,
80
- ackLowConfidence: string | undefined,
81
- fs: Pick<typeof import('node:fs'), 'readFileSync' | 'statSync'>,
82
- ): { answer: string; ackLowConfidence: string | undefined } | undefined;
83
71
  export declare function buildReviewGateArtifact(
84
72
  wbs: string,
85
73
  taskContent: string,
86
74
  deferrals: Deferral[],
87
- review?: { answer: string; ackLowConfidence: string | undefined },
88
75
  ): { artifact: ResidualArtifact; note: string };
89
76
  export declare function parseScanArgs(
90
77
  argv: readonly (string | undefined)[],
@@ -21,14 +21,6 @@ function hasClaimMarker(evidence) {
21
21
  function hasCitationForm(evidence) {
22
22
  return CITATION_FORMS.some((re) => re.test(evidence));
23
23
  }
24
- var CONFIDENCE_LEVELS = ["HIGH", "MEDIUM", "LOW"];
25
- function extractAnswerConfidence(text) {
26
- const matches = [...text.matchAll(/^\s*Confidence:\s*(\S+)\s*$/gim)];
27
- if (matches.length !== 1)
28
- return;
29
- const value = (matches[0]?.[1] ?? "").toUpperCase();
30
- return CONFIDENCE_LEVELS.includes(value) ? value : undefined;
31
- }
32
24
 
33
25
  // packages/app/src/services/residual-scan.ts
34
26
  var MARKER_PATTERN = /TODO|FIXME|XXX|HACK/;
@@ -202,13 +194,6 @@ function blockingReviewFindings(taskContent, deferrals) {
202
194
  }
203
195
  function reviewContractViolations(taskContent) {
204
196
  const violations = [];
205
- const body = reviewBody(taskContent);
206
- if (body !== null && extractAnswerConfidence(body.replace(/\*\*/g, "")) === undefined) {
207
- violations.push({
208
- location: "task-file:Review",
209
- text: "Review report carries no single valid `**Confidence:** HIGH | MEDIUM | LOW` line — " + "state the level the report can defend (HIGH: every requirement row MET, every finding " + "dispositioned, evidence cited; MEDIUM/LOW: name the gap)."
210
- });
211
- }
212
197
  for (const row of parseReviewFindings(taskContent)) {
213
198
  if (hasClaimMarker(row.text) && !hasCitationForm(row.text)) {
214
199
  violations.push({
@@ -219,32 +204,8 @@ function reviewContractViolations(taskContent) {
219
204
  }
220
205
  return violations;
221
206
  }
222
- function reviewConfidenceViolation(reviewAnswer, ackLowConfidence) {
223
- if (ackLowConfidence === "true")
224
- return null;
225
- const level = extractAnswerConfidence(reviewAnswer.replace(/\*\*/g, ""));
226
- if (level === "HIGH" || level === "MEDIUM")
227
- return null;
228
- return {
229
- location: "review-answer:Confidence",
230
- text: level === undefined ? "The review answer carries no valid `**Confidence:** HIGH | MEDIUM | LOW` line — a " + "PASS must be graded, or the operator must set ackLowConfidence" : `The review answer graded its PASS ${level} — HIGH/MEDIUM cross unadorned, ` + "LOW/absent needs the operator’s ackLowConfidence"
231
- };
232
- }
233
- function reviewGradingInput(root, reviewAnswer, ackLowConfidence, fs) {
234
- if (reviewAnswer === undefined)
235
- return;
236
- const path = join(root, reviewAnswer);
237
- let isFile = false;
238
- try {
239
- isFile = fs.statSync(path).isFile();
240
- } catch {
241
- isFile = false;
242
- }
243
- return { answer: isFile ? fs.readFileSync(path, "utf8") : "", ackLowConfidence };
244
- }
245
- function buildReviewGateArtifact(wbs, taskContent, deferrals, review) {
246
- const confidence = review === undefined ? null : reviewConfidenceViolation(review.answer, review.ackLowConfidence);
247
- const contractViolations = [...reviewContractViolations(taskContent), ...confidence === null ? [] : [confidence]];
207
+ function buildReviewGateArtifact(wbs, taskContent, deferrals) {
208
+ const contractViolations = reviewContractViolations(taskContent);
248
209
  const items = [
249
210
  ...blockingReviewFindings(taskContent, deferrals),
250
211
  ...contractViolations.map((v) => ({
@@ -380,7 +341,6 @@ function parseScanArgs(argv, cwd, defaultTmpDir) {
380
341
  let spurBin;
381
342
  let root = cwd;
382
343
  let tmpDir = defaultTmpDir;
383
- let reviewAnswer;
384
344
  for (let i = 0;i < argv.length; i++) {
385
345
  const a = argv[i];
386
346
  if (a === undefined)
@@ -391,8 +351,6 @@ function parseScanArgs(argv, cwd, defaultTmpDir) {
391
351
  root = argv[++i] ?? root;
392
352
  else if (a === "--tmp-dir")
393
353
  tmpDir = argv[++i] ?? tmpDir;
394
- else if (a === "--review-answer")
395
- reviewAnswer = argv[++i];
396
354
  else if (a === "--help" || a === "-h")
397
355
  return null;
398
356
  else if (mode === "")
@@ -400,7 +358,7 @@ function parseScanArgs(argv, cwd, defaultTmpDir) {
400
358
  else if (wbs === "")
401
359
  wbs = a;
402
360
  }
403
- return mode === "" || wbs === "" ? null : { mode, wbs, spurBin, root, tmpDir, reviewAnswer };
361
+ return mode === "" || wbs === "" ? null : { mode, wbs, spurBin, root, tmpDir };
404
362
  }
405
363
  export {
406
364
  ALLOW_PRAGMA,
@@ -419,9 +377,7 @@ export {
419
377
  parseScanArgs,
420
378
  recordedVerdictPath,
421
379
  renderReport,
422
- reviewConfidenceViolation,
423
380
  reviewContractViolations,
424
- reviewGradingInput,
425
381
  scanResiduals,
426
382
  verdictDisagreementNote
427
383
  };
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "sp",
3
- "version": "0.4.4",
3
+ "version": "0.4.5",
4
4
  "description": "Spur — a local-first harness engineering toolkit that wraps mainstream coding agents with constraint checking, workflow orchestration, and history analytics.",
5
5
  "extensions": {
6
6
  "pi": ["./hooks/pi/guard-extension.ts"]
@@ -33,14 +33,6 @@ function hasClaimMarker(evidence) {
33
33
  function hasCitationForm(evidence) {
34
34
  return CITATION_FORMS.some((re) => re.test(evidence));
35
35
  }
36
- var CONFIDENCE_LEVELS = ["HIGH", "MEDIUM", "LOW"];
37
- function extractAnswerConfidence(text) {
38
- const matches = [...text.matchAll(/^\s*Confidence:\s*(\S+)\s*$/gim)];
39
- if (matches.length !== 1)
40
- return;
41
- const value = (matches[0]?.[1] ?? "").toUpperCase();
42
- return CONFIDENCE_LEVELS.includes(value) ? value : undefined;
43
- }
44
36
  var MARKER_PATTERN = /TODO|FIXME|XXX|HACK/;
45
37
  var PRIORITY_PATTERN = /^P[1-4]/;
46
38
  var RANGE_PRIORITY = /^P[1-4]\s*[\u2013\u2014-]\s*P?[1-4]/;
@@ -212,13 +204,6 @@ function blockingReviewFindings(taskContent, deferrals) {
212
204
  }
213
205
  function reviewContractViolations(taskContent) {
214
206
  const violations = [];
215
- const body = reviewBody(taskContent);
216
- if (body !== null && extractAnswerConfidence(body.replace(/\*\*/g, "")) === undefined) {
217
- violations.push({
218
- location: "task-file:Review",
219
- text: "Review report carries no single valid `**Confidence:** HIGH | MEDIUM | LOW` line — " + "state the level the report can defend (HIGH: every requirement row MET, every finding " + "dispositioned, evidence cited; MEDIUM/LOW: name the gap)."
220
- });
221
- }
222
207
  for (const row of parseReviewFindings(taskContent)) {
223
208
  if (hasClaimMarker(row.text) && !hasCitationForm(row.text)) {
224
209
  violations.push({
@@ -229,32 +214,8 @@ function reviewContractViolations(taskContent) {
229
214
  }
230
215
  return violations;
231
216
  }
232
- function reviewConfidenceViolation(reviewAnswer, ackLowConfidence) {
233
- if (ackLowConfidence === "true")
234
- return null;
235
- const level = extractAnswerConfidence(reviewAnswer.replace(/\*\*/g, ""));
236
- if (level === "HIGH" || level === "MEDIUM")
237
- return null;
238
- return {
239
- location: "review-answer:Confidence",
240
- text: level === undefined ? "The review answer carries no valid `**Confidence:** HIGH | MEDIUM | LOW` line — a " + "PASS must be graded, or the operator must set ackLowConfidence" : `The review answer graded its PASS ${level} — HIGH/MEDIUM cross unadorned, ` + "LOW/absent needs the operator’s ackLowConfidence"
241
- };
242
- }
243
- function reviewGradingInput(root, reviewAnswer, ackLowConfidence, fs) {
244
- if (reviewAnswer === undefined)
245
- return;
246
- const path = join(root, reviewAnswer);
247
- let isFile = false;
248
- try {
249
- isFile = fs.statSync(path).isFile();
250
- } catch {
251
- isFile = false;
252
- }
253
- return { answer: isFile ? fs.readFileSync(path, "utf8") : "", ackLowConfidence };
254
- }
255
- function buildReviewGateArtifact(wbs, taskContent, deferrals, review) {
256
- const confidence = review === undefined ? null : reviewConfidenceViolation(review.answer, review.ackLowConfidence);
257
- const contractViolations = [...reviewContractViolations(taskContent), ...confidence === null ? [] : [confidence]];
217
+ function buildReviewGateArtifact(wbs, taskContent, deferrals) {
218
+ const contractViolations = reviewContractViolations(taskContent);
258
219
  const items = [
259
220
  ...blockingReviewFindings(taskContent, deferrals),
260
221
  ...contractViolations.map((v) => ({
@@ -390,7 +351,6 @@ function parseScanArgs(argv, cwd, defaultTmpDir) {
390
351
  let spurBin;
391
352
  let root = cwd;
392
353
  let tmpDir = defaultTmpDir;
393
- let reviewAnswer;
394
354
  for (let i = 0;i < argv.length; i++) {
395
355
  const a = argv[i];
396
356
  if (a === undefined)
@@ -401,8 +361,6 @@ function parseScanArgs(argv, cwd, defaultTmpDir) {
401
361
  root = argv[++i] ?? root;
402
362
  else if (a === "--tmp-dir")
403
363
  tmpDir = argv[++i] ?? tmpDir;
404
- else if (a === "--review-answer")
405
- reviewAnswer = argv[++i];
406
364
  else if (a === "--help" || a === "-h")
407
365
  return null;
408
366
  else if (mode === "")
@@ -410,7 +368,7 @@ function parseScanArgs(argv, cwd, defaultTmpDir) {
410
368
  else if (wbs === "")
411
369
  wbs = a;
412
370
  }
413
- return mode === "" || wbs === "" ? null : { mode, wbs, spurBin, root, tmpDir, reviewAnswer };
371
+ return mode === "" || wbs === "" ? null : { mode, wbs, spurBin, root, tmpDir };
414
372
  }
415
373
 
416
374
  // plugins/sp/lib/spur-bin.ts
@@ -420,7 +378,7 @@ function spurCommand(spurBin) {
420
378
  }
421
379
 
422
380
  // plugins/sp/scripts/residual-scan.ts
423
- var RESIDUAL_SCAN_USAGE = "usage: residual-scan.ts <scan|fold|settle|report|review-gate> <wbs> [--spur-bin <bin>] [--root <dir>] [--tmp-dir <dir>] [--review-answer <path>]";
381
+ var RESIDUAL_SCAN_USAGE = "usage: residual-scan.ts <scan|fold|settle|report|review-gate> <wbs> [--spur-bin <bin>] [--root <dir>] [--tmp-dir <dir>]";
424
382
  function run(cmd, args, cwd) {
425
383
  const result = spawnSync(cmd, args, { cwd, encoding: "utf8" });
426
384
  if (result.error !== undefined)
@@ -515,8 +473,7 @@ function reviewGateMode(opts, env, io) {
515
473
  const runDir = join2(opts.root, ".spur", "run");
516
474
  fs.mkdirSync(runDir, { recursive: true });
517
475
  const { content } = loadTask(env, opts.spurBin, opts.wbs, opts.root);
518
- const grading = reviewGradingInput(opts.root, opts.reviewAnswer, env.ackLowConfidence, fs);
519
- const gate = buildReviewGateArtifact(opts.wbs, content, readDeferrals(runDir, opts.wbs), grading);
476
+ const gate = buildReviewGateArtifact(opts.wbs, content, readDeferrals(runDir, opts.wbs));
520
477
  fs.writeFileSync(join2(runDir, `${opts.wbs}-residuals.json`), `${JSON.stringify(gate.artifact, null, 2)}
521
478
  `);
522
479
  io.out(gate.note);
@@ -671,9 +628,7 @@ export {
671
628
  parseScanArgs,
672
629
  recordedVerdictPath,
673
630
  renderReport,
674
- reviewConfidenceViolation,
675
631
  reviewContractViolations,
676
- reviewGradingInput,
677
632
  scanResiduals2 as scanResiduals,
678
633
  verdictDisagreementNote
679
634
  };
@@ -10,7 +10,7 @@ import { spurCommand } from '../lib/spur-bin';
10
10
 
11
11
  export * from '../lib/residual-scan.generated.mjs';
12
12
  export const RESIDUAL_SCAN_USAGE =
13
- 'usage: residual-scan.ts <scan|fold|settle|report|review-gate> <wbs> [--spur-bin <bin>] [--root <dir>] [--tmp-dir <dir>] [--review-answer <path>]';
13
+ 'usage: residual-scan.ts <scan|fold|settle|report|review-gate> <wbs> [--spur-bin <bin>] [--root <dir>] [--tmp-dir <dir>]';
14
14
  export type ScanEnv = { spurBin?: string } & Record<string, string | undefined>;
15
15
  export type ScanIo = { out: (line: string) => void; err: (line: string) => void };
16
16
  export type ScanOptions = { cwd?: string; io?: ScanIo };
@@ -106,16 +106,14 @@ function scanMode(opts: ParsedArgs, env: ScanEnv, io: ScanIo): number {
106
106
  /**
107
107
  * Task 1122 R2: the review-time gate — the record sweep's review-finding slice
108
108
  * (core.buildReviewGateArtifact) at the review PASS edges; writes the same `<wbs>-residuals.json`
109
- * the test-fix hop reads. Exit 1 on an open P1-P3 finding, or on a review answer graded LOW or left
110
- * ungraded without `env.ackLowConfidence` (task 1204, graded via `core.reviewGradingInput` — it
111
- * cannot sit inline in the edge because ADR-115 caps a guard at 5 logical commands).
109
+ * the test-fix hop reads. Exit 1 on an open P1-P3 finding. The answer-grade rule (1204) was
110
+ * retired with the asserted confidence machinery (F511); findings stay the gate.
112
111
  */
113
112
  function reviewGateMode(opts: ParsedArgs, env: ScanEnv, io: ScanIo): number {
114
113
  const runDir = join(opts.root, '.spur', 'run');
115
114
  fs.mkdirSync(runDir, { recursive: true });
116
115
  const { content } = loadTask(env, opts.spurBin, opts.wbs, opts.root);
117
- const grading = core.reviewGradingInput(opts.root, opts.reviewAnswer, env.ackLowConfidence, fs);
118
- const gate = core.buildReviewGateArtifact(opts.wbs, content, readDeferrals(runDir, opts.wbs), grading);
116
+ const gate = core.buildReviewGateArtifact(opts.wbs, content, readDeferrals(runDir, opts.wbs));
119
117
  fs.writeFileSync(join(runDir, `${opts.wbs}-residuals.json`), `${JSON.stringify(gate.artifact, null, 2)}\n`);
120
118
  io.out(gate.note);
121
119
  return gate.artifact.counts.blocking > 0 ? 1 : 0;
@@ -341,7 +341,6 @@ Verdict: PASS (or PARTIAL / FAIL)
341
341
 
342
342
  ```markdown
343
343
  Verdict: PASS
344
- Confidence: HIGH
345
344
 
346
345
  ### Per-Requirement Traceability
347
346
  | Req | Status | Evidence |
@@ -360,7 +359,7 @@ Confidence: HIGH
360
359
  ```
361
360
 
362
361
  The per-requirement traceability table MUST use `| Req | Status | Evidence |` (exactly this header, no `R#`/`R`/`Requirement` variant — `Status` in column 2 — and no extra columns between Req and Status). The Acceptance Criteria table MUST use `| AC | Status | Evidence Type | Evidence |`.
363
- The answer MUST carry exactly one `Confidence: HIGH|MEDIUM|LOW` line (task 1068): the verifier's stated confidence in the verdict — HIGH = fresh executable evidence + re-read anchors this run; MEDIUM = partial reliance on prior evidence, external/delegated outcomes, or an unstable tree; LOW = key evidence not re-executed. A missing or out-of-vocabulary line fails lint (`confidence-missing` / `confidence-value`) and blocks derivation; the value lands in the verdict artifact's `confidence` field and the recorded `## Testing` section.
362
+ The answer carries no `Confidence:` line (F511, task 1207): model-asserted confidence is not verification — a verifier that can write a PASS can write `HIGH`. What the old level compressed now lives in the evidence cells themselves: cite fresh executable evidence and name residual gaps explicitly. Legacy answers that still carry a confidence line parse (the value is ignored); no lint code reads it and the artifact and recorded `## Testing` section carry no confidence value.
364
363
  The answer MUST cite a source for external API/library claims in Requirement/AC evidence cells: a cell asserting a scoped-package (`@scope/name`), `node_modules/`, semver-version (`x.y.z`), or backticked function-call (`` `name(args)` ``) claim is rejected with `evidence-citation` unless the SAME cell carries a citation — a repo `path:line(-end)` (backticked or bare), the external named-origin form (@origin `path` line N), or a URL. Cross-cell references ("receipt above") do not count; marker-free receipts ("bun test 398 pass / 0 fail") need no citation.
365
364
  **MUST NOT:** place a `Severity` column between `Req` and `Status`. The parser tolerates variants
366
365
  as defense-in-depth; this contract is canonical.
@@ -35,42 +35,21 @@ interface VerifyVerdict {
35
35
  status: 'pass' | 'fail' | 'warn';
36
36
  evidence: string;
37
37
  }>;
38
- /** Verifier confidence (task 1068 R3). REQUIRED for PASS — the done gate denies
39
- * a confidence-less PASS. Accepted case-insensitively, normalized to uppercase. */
40
- confidence?: 'HIGH' | 'MEDIUM' | 'LOW';
38
+ // F511 (task 1207): no asserted `confidence` field. Legacy artifacts that still carry one
39
+ // parse with the value ignored — unknown legacy keys are tolerated on read.
41
40
  }
42
41
  ```
43
42
 
44
- ## Confidence level (task 1068 R3)
43
+ ## No asserted confidence (F511, task 1207)
45
44
 
46
- A PASS verdict must state the verifier's confidence: `"confidence": "HIGH" | "MEDIUM" | "LOW"`.
47
- The value is case-insensitive on read (normalized to uppercase), but a present value outside the
48
- enum invalidates the artifact, and a PASS with the field absent is denied at the `* → done`
49
- transition with a remediation naming the field (`--force-done` remains the documented escape).
50
- The verify answer's `Confidence:` lint rule (producer side) already guarantees the field for
51
- pipeline-produced artifacts; this closes the hand-authored / pre-1068 artifact hole.
52
-
53
- ### Coherence: the level must be earned (task 1133)
54
-
55
- Presence and vocabulary are not enough — the level has to match the rows in the same answer, so the
56
- lint rejects a level that contradicts its own evidence. Two rejection classes, both addressed to the
57
- `Confidence:` line and both capped by the shared `ANSWER_LINT_MAX_FINDINGS` budget:
58
-
59
- - `confidence-unwarranted` — `HIGH` while any requirement row normalizes to `PARTIAL`/`UNMET`, any
60
- acceptance-criteria row does not normalize to `MET`, or any `MET` row's evidence is hedged.
61
- A `HIGH` claim says the evidence needs no re-check; a non-MET or hedged row says the opposite.
62
- - `confidence-understated` — `LOW` while at least one requirement row exists and every requirement
63
- and acceptance-criteria row normalizes to `MET`. The evidence already did the work, so `LOW`
64
- misreports the state as unproven.
65
-
66
- `MEDIUM` carries no coherence rule in either direction: it is the level a verifier uses for a caveat
67
- with no row to point at, and closing it would force either an overclaimed `HIGH` or an unusable
68
- `LOW`. The level is normalized to uppercase before the decision, so `high` is judged as `HIGH`.
69
-
70
- Set the level from the rows, not the mood: all rows `MET` with fresh evidence is `HIGH`; a
71
- `PARTIAL`/`UNMET` row or a hedge means `MEDIUM`, not `HIGH`. An unwarranted level does not merely
72
- warn — the lint rejects the answer and no verdict artifact is written, so the level is worth
73
- choosing deliberately before the verdict line is finalized.
45
+ Verdict artifacts carry no `confidence` field and verify answers no `Confidence:` line. The
46
+ 1068/1133 machinery that required, linted and coherence-checked a model-asserted
47
+ `HIGH | MEDIUM | LOW` was retired: a verifier that can write a PASS can write `HIGH`, so the grade
48
+ verifies nothing. What the level compressed belongs in the evidence cells — cite fresh executable
49
+ evidence and name residual gaps explicitly. The done gate no longer denies a confidence-less PASS:
50
+ it checks the proof digests, which are the part a hand-edited artifact cannot fake. The DERIVED
51
+ confidence family is a different thing and stays: feature-verification receipts (ADR-131, 1196),
52
+ the decision subsystem threshold, history-anatomy reading.
74
53
 
75
54
  ## Compatibility alias: `scenario` row key
76
55
 
@@ -171,17 +171,13 @@ Two branches reach the bounded repair hop, not one (session finding after 1088):
171
171
  an on-graph, bounded path to remediation instead of dead-ending the run, and the fix re-enters
172
172
  quality → review → verify on a freshly captured proof digest.
173
173
 
174
- The completion gate also reads the verifier's confidence (session finding after 1088): the verify
175
- answer's `Confidence: HIGH|MEDIUM|LOW` line reaches the verdict artifact, and both `verify → record`
176
- and `record → done` refuse a `LOW` (or absent) level unless the run was launched with
177
- `--vars '{"ackLowConfidence":"true"}'`. A PASS the verifier would not stand behind is an operator
178
- risk decision, so it takes an operator acknowledgement — not a silent certification.
179
- The CLI-layer done gate (`done-transition-guard`, task 1068 R3) separately requires the artifact to *state* a
180
- level at all: a PASS artifact whose `confidence` is missing is denied there too, and a present but
181
- out-of-vocabulary value fails the read closed (`invalid confidence field`), so a hand-driven
182
- `task update <wbs> done` — which is how this pipeline itself reaches `done` (`--no-lifecycle`) —
183
- cannot certify a verdict that carries no confidence. `LOW` is accepted at that gate; the
184
- acknowledgement rule above stays the pipeline's.
174
+ The completion gate reads deterministic evidence only (F511, task 1207): both `verify → record` and
175
+ `record → done` check the proof digests in `.spur/run/<wbs>-verdict.json` — verdict PASS, matching
176
+ run/definition digests, review stage completed. No confidence is read, stated or acknowledged; the
177
+ asserted `Confidence:` line and the `ackLowConfidence` operator var were retired because a model
178
+ that can write a PASS can write `HIGH` — the grade verified nothing. What protects a hand-driven
179
+ `task update <wbs> done` is unchanged: the digests must match the run's captured proof, and the
180
+ review stage must actually be completed in the artifact.
185
181
 
186
182
  Agentic steps use the pure slash inputs declared by each YAML `agent.run` action (ADR-043). The
187
183
  workflow engine dispatches them; the interactive driver invokes their backing skills in-session.
@@ -844,9 +844,10 @@ leave no `.spur/run/<wbs>-check-receipt.json`. The deciding artifacts are then
844
844
  1. **Drive the pipeline** for anything that must be certified: only a digest-bound full receipt
845
845
  satisfies a review/verify/record boundary (`sp-spur-check`), and the `test` hop owns the capture.
846
846
  2. **When a host-driven pass is unavoidable, say so in the verdict** — name the status file and gate
847
- log as the evidence and grade confidence `MEDIUM`. A `HIGH` claim needs re-runnable evidence from a
848
- checked path (the `sp-code-verification` ladder); claiming a receipt that was never written is the
849
- failure mode this note exists to prevent (observed across four A91 tasks, 2026-10-12).
847
+ log as the evidence, and mark the reliance explicitly in the evidence cell (a host-driven gate is
848
+ not re-runnable proof from a checked path; the `sp-code-verification` ladder is). Claiming a
849
+ receipt that was never written is the failure mode this note exists to prevent (observed across
850
+ four A91 tasks, 2026-10-12).
850
851
 
851
852
  **A mismatch names the drifted paths (R5).** At gate entry the pipeline snapshots the non-corpus tree
852
853
  state (`git status --porcelain=v1 -uall` plus `git diff --name-only HEAD`, excluding
@@ -977,14 +978,14 @@ Each item below cost a driver cycle to discover. They are contracts, not tips.
977
978
  `.spur/run/<wbs>-diffstat.json` and never on command output. Never the bare unguarded
978
979
  source-repo path form — the script-contract check forbids it on a shipped surface because it
979
980
  shadows the installed twin in a consumer.
980
- - **The verify answer grammar is exact.** One `Verdict:` line, one `Confidence:` line, then
981
+ - **The verify answer grammar is exact.** One `Verdict:` line, then
981
982
  `### Per-Requirement Traceability` (`| Req | Status | Evidence |`) and
982
983
  `### Acceptance Criteria Verification` (`| AC | Status | Evidence Type | Evidence |`). Requirement
983
984
  statuses are `MET | PARTIAL | UNMET`; AC statuses add `N/A`; every AC row id must match a declared
984
985
  scenario title verbatim, including its `(req: ...)` suffix. A `MET` row whose evidence hedges
985
986
  (`likely`, `probably`, `seems`, `appears`, `possibly`, `presumably`) is rejected as
986
- `evidence-hedged`, and a level the rows do not earn is rejected as `confidence-unwarranted` or
987
- `confidence-understated`. `spur task verdict` lints before writing, so a rejected answer leaves no
987
+ `evidence-hedged`. No `Confidence:` line exists (F511, task 1207) — legacy answers that carry one
988
+ parse with the value ignored. `spur task verdict` lints before writing, so a rejected answer leaves no
988
989
  artifact and exits non-zero.
989
990
  - **Harness tooling refuses direct task-file writes.** Resolve a merge conflict in a task file by
990
991
  choosing a git side, then applying the corpus delta through `spur task update` (for example