Repository navigation
feat(review): require an executed witness on every confirmed Critical #9065
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Changes from all commits
8c4df33
4855a8a
55ac5a2
b6e8f34
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -83,6 +83,13 @@ export interface Finding { | |
| shortSummary: string; | ||
| /** The concrete trigger and wrong outcome — the finding's evidence. */ | ||
| failureScenario: string; | ||
| /** | ||
| * The executed evidence that settled the verdict (a probe's two sides, an | ||
| * A/B's quoted pair, a sweep count) — or the verifier's | ||
| * `not run — <reason>` line. Carried as data so the report and the comment | ||
| * bodies quote one recorded string instead of transcribing it twice more. | ||
| */ | ||
| witness?: string; | ||
| suggestedFix?: string; | ||
| /** Free-form kebab-case tag (`correctness`, `security`, `test-coverage`, …). */ | ||
| category?: string; | ||
|
|
@@ -351,6 +358,11 @@ export function validateFindings(raw: unknown): Finding[] { | |
| const shortSummary = | ||
| asString(o, 'shortSummary') ?? asString(o, 'short_summary'); | ||
|
|
||
| // `witness` round-trips for the same reason `outcomeNote` does: the Step 4 | ||
| // witness rule attaches it once, and the report and the comment bodies read | ||
| // it back out of the artifact instead of transcribing the evidence again. | ||
| const witness = asString(o, 'witness'); | ||
|
|
||
| return { | ||
| id, | ||
| severity, | ||
|
|
@@ -361,6 +373,7 @@ export function validateFindings(raw: unknown): Finding[] { | |
| ? compressSummary(shortSummary) | ||
| : compressSummary(summary), | ||
| failureScenario, | ||
| ...(witness ? { witness } : {}), | ||
|
Collaborator
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Fixed in 4855a8a: |
||
| ...(asString(o, 'suggestedFix') || asString(o, 'suggested_fix') | ||
| ? { | ||
| suggestedFix: (asString(o, 'suggestedFix') ?? | ||
|
|
@@ -507,6 +520,45 @@ export function holdCriticalsFailingOnBase( | |
| return { findings: out, held, readjudicated }; | ||
| } | ||
|
|
||
| /** | ||
| * The witness rule's machine half. Step 4 demands that a confirmed Critical | ||
| * carry its executed evidence — the `witness` field, holding either the | ||
| * observed output or the verifier's `not run — <reason>` line — and promises | ||
| * the demotion is mechanical. This is the mechanism, in the same place the | ||
| * test-delta holdback lives: a high-confidence Critical from the one | ||
| * non-deterministic source that arrives with no witness is filed at low | ||
| * confidence — terminal-only, never posted. Only `source: 'review'` is | ||
| * judged: a `[build]`/`[test]`/`[lint]`/`[probe]` finding IS a run's output, | ||
| * so its witness is constitutive, not an attachment. Nothing is deleted and | ||
| * nothing is raised; the appended sentence names the rule that moved it and | ||
| * the way back (attach the witness, or say why none could run). Idempotent by | ||
| * construction — a demoted finding re-fed through `--input` is already low | ||
| * confidence and is not touched again. | ||
| */ | ||
| export function holdUnwitnessedCriticals(findings: readonly Finding[]): { | ||
| findings: Finding[]; | ||
| unwitnessed: string[]; | ||
| } { | ||
| const unwitnessed: string[] = []; | ||
| const out = findings.map((f) => { | ||
| if ( | ||
| f.severity !== 'Critical' || | ||
| f.confidence !== 'high' || | ||
| f.source !== 'review' || | ||
| f.witness !== undefined | ||
| ) { | ||
| return f; | ||
| } | ||
| unwitnessed.push(f.id); | ||
| return { | ||
| ...f, | ||
| confidence: 'low' as Confidence, | ||
| failureScenario: `${f.failureScenario}\n\nFiled at low confidence by the witness rule: this confirmed Critical arrived with neither a witness (the executed evidence that settled the verdict) nor a \`not run — <reason>\` line. Attach either and it stands at high confidence again.`, | ||
| }; | ||
| }); | ||
| return { findings: out, unwitnessed }; | ||
| } | ||
|
|
||
| const WORKSPACE_IN_COMMAND_RE = /--workspace="([^"]+)"/; | ||
|
|
||
| /** | ||
|
|
@@ -888,6 +940,8 @@ export const findingsCommand: CommandModule = { | |
| shared, | ||
| )); | ||
| } | ||
| const witnessHold = holdUnwitnessedCriticals(findings); | ||
| findings = witnessHold.findings; | ||
|
Comment on lines
+943
to
+944
Collaborator
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. [Suggestion] Missing handler-level integration test for the witness rule — the Failure scenario: If the 中文说明建议 缺少 witness 规则的处理程序级别集成测试—— 失败场景:如果 — deepseek-v4-flash via Qwen Code /review (v0.21.10)
Collaborator
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Added in b6e8f34: the handler-boundary test feeds an unwitnessed Critical and a witnessed sibling through |
||
| const report = buildReport(findings); | ||
|
|
||
| const target = resolve(out); | ||
|
|
@@ -909,6 +963,14 @@ export const findingsCommand: CommandModule = { | |
| `findings: ${h.id} held back from Critical — test-delta measured ${h.file} as failing on the merge base too`, | ||
| ); | ||
| } | ||
| // The witness rule's demotions get the same disclosure: a confidence this | ||
| // command lowered must name the finding and the rule, or the demotion | ||
| // reads as the reviewer's own judgement. | ||
| for (const id of witnessHold.unwitnessed) { | ||
| writeStderrLine( | ||
| `findings: ${id} filed at low confidence — a confirmed Critical carried neither a witness nor a 'not run' reason (Step 4's witness rule)`, | ||
| ); | ||
| } | ||
| // A hold that was weighed and reversed is a decision, and a decision this | ||
| // command declined to overrule is exactly as reportable as one it made. | ||
| for (const r of readjudicated) { | ||
|
|
||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Fixed in 4855a8a, at the boundary you named as local precedent:
qwen review findingsnow runsholdUnwitnessedCriticalsbeside the test-delta holdback — a high-confidence[review]-source Critical with nowitnessis demoted to low confidence at canonicalization, each named on stderr, and the appended sentence tells the reader which rule moved it and the way back. Deterministic sources are exempt (their witness is constitutive), and the hold is idempotent on re-feed. SKILL.md and DESIGN.md now describe the machine half instead of only promising one; acompose-review-level cap stays follow-up.(已修:findings 规范化处代码强制降级,逐条 stderr 披露;确定性来源豁免;重复喂入幂等。)