@iceinvein/agent-skills 0.23.9 → 0.23.13
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/package.json +1 -1
- package/skills/index.json +2 -2
- package/skills/magpie/SKILL.md +21 -20
- package/skills/magpie/bin/magpie.ts +63 -2
- package/skills/magpie/evals/quality/README.md +74 -1
- package/skills/magpie/evals/quality/__tests__/probe-specialist.test.ts +498 -0
- package/skills/magpie/evals/quality/__tests__/replay-full.test.ts +9 -0
- package/skills/magpie/evals/quality/probe-specialist.ts +672 -0
- package/skills/magpie/evals/quality/replay-full.ts +6 -2
- package/skills/magpie/fixtures/fake-gh-with-lockfile.sh +37 -2
- package/skills/magpie/fixtures/fake-gh.sh +49 -2
- package/skills/magpie/references/critic.md +20 -4
- package/skills/magpie/references/proof.md +102 -0
- package/skills/magpie/references/scout.md +6 -1
- package/skills/magpie/references/specialists.md +15 -9
- package/skills/magpie/scripts/__tests__/cleanup-cmd.test.ts +103 -7
- package/skills/magpie/scripts/__tests__/cli.test.ts +69 -0
- package/skills/magpie/scripts/__tests__/critic-cmd.test.ts +46 -0
- package/skills/magpie/scripts/__tests__/critic.test.ts +11 -1
- package/skills/magpie/scripts/__tests__/dedupe-cmd.test.ts +20 -1
- package/skills/magpie/scripts/__tests__/git-diff.test.ts +23 -4
- package/skills/magpie/scripts/__tests__/handoff-cmd.test.ts +68 -0
- package/skills/magpie/scripts/__tests__/history.test.ts +232 -0
- package/skills/magpie/scripts/__tests__/impact-cmd.test.ts +477 -0
- package/skills/magpie/scripts/__tests__/impact-render.test.ts +365 -0
- package/skills/magpie/scripts/__tests__/impact.test.ts +915 -0
- package/skills/magpie/scripts/__tests__/incremental.test.ts +59 -8
- package/skills/magpie/scripts/__tests__/pipeline-e2e.test.ts +1 -1
- package/skills/magpie/scripts/__tests__/post-cmd.test.ts +58 -0
- package/skills/magpie/scripts/__tests__/pr-activity.test.ts +281 -0
- package/skills/magpie/scripts/__tests__/preview-cmd.test.ts +4 -4
- package/skills/magpie/scripts/__tests__/previous-findings.test.ts +267 -0
- package/skills/magpie/scripts/__tests__/proof-cmd.test.ts +260 -0
- package/skills/magpie/scripts/__tests__/proof.test.ts +226 -0
- package/skills/magpie/scripts/__tests__/refresh.test.ts +71 -0
- package/skills/magpie/scripts/__tests__/render-annotation.test.ts +34 -0
- package/skills/magpie/scripts/__tests__/render-findings.test.ts +75 -0
- package/skills/magpie/scripts/__tests__/render-progress.test.ts +9 -0
- package/skills/magpie/scripts/__tests__/setup-cmd.test.ts +376 -1
- package/skills/magpie/scripts/__tests__/shard-coverage.test.ts +79 -0
- package/skills/magpie/scripts/__tests__/skill-lint.test.ts +48 -2
- package/skills/magpie/scripts/__tests__/status-cmd.test.ts +20 -8
- package/skills/magpie/scripts/__tests__/types.test.ts +46 -0
- package/skills/magpie/scripts/cleanup-cmd.ts +88 -0
- package/skills/magpie/scripts/critic-cmd.ts +2 -0
- package/skills/magpie/scripts/critic.ts +4 -0
- package/skills/magpie/scripts/dedupe-cmd.ts +4 -0
- package/skills/magpie/scripts/evidence-filter.ts +10 -0
- package/skills/magpie/scripts/git-diff.ts +50 -19
- package/skills/magpie/scripts/handoff-cmd.ts +42 -3
- package/skills/magpie/scripts/history.ts +143 -0
- package/skills/magpie/scripts/impact-cmd.ts +202 -0
- package/skills/magpie/scripts/impact-render.ts +158 -0
- package/skills/magpie/scripts/impact.ts +622 -0
- package/skills/magpie/scripts/incremental.ts +44 -5
- package/skills/magpie/scripts/labels-cmd.ts +7 -3
- package/skills/magpie/scripts/post-cmd.ts +46 -0
- package/skills/magpie/scripts/pr-activity.ts +243 -0
- package/skills/magpie/scripts/preview-cmd.ts +3 -0
- package/skills/magpie/scripts/previous-findings.ts +181 -0
- package/skills/magpie/scripts/proof-cmd.ts +226 -0
- package/skills/magpie/scripts/proof.ts +150 -0
- package/skills/magpie/scripts/refresh.ts +11 -0
- package/skills/magpie/scripts/render-annotation.ts +17 -0
- package/skills/magpie/scripts/render-cmd.ts +12 -0
- package/skills/magpie/scripts/render-findings.ts +37 -1
- package/skills/magpie/scripts/render-progress.ts +3 -0
- package/skills/magpie/scripts/setup-cmd.ts +142 -11
- package/skills/magpie/scripts/shard-coverage.ts +53 -0
- package/skills/magpie/scripts/status-cmd.ts +2 -0
- package/skills/magpie/scripts/types.ts +39 -0
- package/skills/magpie/skill.json +1 -1
- package/skills/magpie/templates/styles.css +30 -0
- package/skills/sluice/scripts/status.sh +32 -13
- package/skills/sluice/skill.json +1 -1
|
@@ -0,0 +1,226 @@
|
|
|
1
|
+
import { expect, test } from 'bun:test'
|
|
2
|
+
import {
|
|
3
|
+
applyProofResults,
|
|
4
|
+
buildProofPrompts,
|
|
5
|
+
type ProofResult,
|
|
6
|
+
parseProofResult,
|
|
7
|
+
selectForProof,
|
|
8
|
+
} from '../proof.ts'
|
|
9
|
+
import type { ReviewFinding, Risk } from '../types.ts'
|
|
10
|
+
|
|
11
|
+
function finding(id: string, risk: Risk): ReviewFinding {
|
|
12
|
+
return {
|
|
13
|
+
id,
|
|
14
|
+
file: 'src/a.ts',
|
|
15
|
+
line: 10,
|
|
16
|
+
severity: 'high',
|
|
17
|
+
risk,
|
|
18
|
+
title: `title ${id}`,
|
|
19
|
+
description: `description ${id}`,
|
|
20
|
+
domain: 'bugs',
|
|
21
|
+
evidence: 'return cache[key]',
|
|
22
|
+
}
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
const HIGH_SHOULD: Risk = {
|
|
26
|
+
impact: 'high',
|
|
27
|
+
likelihood: 'possible',
|
|
28
|
+
confidence: 'medium',
|
|
29
|
+
action: 'should-fix',
|
|
30
|
+
}
|
|
31
|
+
const CRITICAL_MUST: Risk = {
|
|
32
|
+
impact: 'critical',
|
|
33
|
+
likelihood: 'likely',
|
|
34
|
+
confidence: 'high',
|
|
35
|
+
action: 'must-fix',
|
|
36
|
+
}
|
|
37
|
+
const MEDIUM_SHOULD: Risk = { ...HIGH_SHOULD, impact: 'medium' }
|
|
38
|
+
const HIGH_CONSIDER: Risk = { ...HIGH_SHOULD, action: 'consider' }
|
|
39
|
+
|
|
40
|
+
const REPRO = {
|
|
41
|
+
path: 'src/__tests__/magpie-proof-a.test.ts',
|
|
42
|
+
command: 'bun test src/__tests__/magpie-proof-a.test.ts',
|
|
43
|
+
source: "test('x', () => expect(get('k')).toBe(1))",
|
|
44
|
+
output: 'expected 1, received undefined',
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
test('findings marked must-fix or should-fix are selected whatever their impact', () => {
|
|
48
|
+
const selected = selectForProof(
|
|
49
|
+
[
|
|
50
|
+
finding('high-should', HIGH_SHOULD),
|
|
51
|
+
finding('medium-should', MEDIUM_SHOULD),
|
|
52
|
+
finding('low-should', { ...HIGH_SHOULD, impact: 'low' }),
|
|
53
|
+
finding('high-consider', HIGH_CONSIDER),
|
|
54
|
+
finding('high-optional', { ...HIGH_SHOULD, action: 'optional' }),
|
|
55
|
+
finding('critical-must', CRITICAL_MUST),
|
|
56
|
+
],
|
|
57
|
+
8,
|
|
58
|
+
)
|
|
59
|
+
expect(selected.map((f) => f.id)).toEqual([
|
|
60
|
+
'critical-must',
|
|
61
|
+
'high-should',
|
|
62
|
+
'medium-should',
|
|
63
|
+
'low-should',
|
|
64
|
+
])
|
|
65
|
+
})
|
|
66
|
+
|
|
67
|
+
test('selection keeps the highest-scoring findings when there are more than the cap', () => {
|
|
68
|
+
const selected = selectForProof(
|
|
69
|
+
[
|
|
70
|
+
finding('low-score', { ...HIGH_SHOULD, likelihood: 'edge-case' }),
|
|
71
|
+
finding('top', CRITICAL_MUST),
|
|
72
|
+
finding('middle', HIGH_SHOULD),
|
|
73
|
+
],
|
|
74
|
+
2,
|
|
75
|
+
)
|
|
76
|
+
expect(selected.map((f) => f.id)).toEqual(['top', 'middle'])
|
|
77
|
+
})
|
|
78
|
+
|
|
79
|
+
const TEMPLATE = 'F=<<FINDING>> W=<<WORKTREE>> D=<<DIFF_PATH>> O=<<OUTPUT_PATH>>'
|
|
80
|
+
|
|
81
|
+
test('one prompt per finding, each naming its own output file', () => {
|
|
82
|
+
const prompts = buildProofPrompts({
|
|
83
|
+
template: TEMPLATE,
|
|
84
|
+
findings: [finding('a', HIGH_SHOULD)],
|
|
85
|
+
worktree: '/wt',
|
|
86
|
+
diffPath: '/run/diff.patch',
|
|
87
|
+
runDir: '/run',
|
|
88
|
+
})
|
|
89
|
+
expect(prompts).toHaveLength(1)
|
|
90
|
+
expect(prompts[0]?.id).toBe('a')
|
|
91
|
+
expect(prompts[0]?.outputPath).toBe('/run/proof-a.json')
|
|
92
|
+
expect(prompts[0]?.prompt).toBe(
|
|
93
|
+
`F=${JSON.stringify(
|
|
94
|
+
{
|
|
95
|
+
id: 'a',
|
|
96
|
+
file: 'src/a.ts',
|
|
97
|
+
line: 10,
|
|
98
|
+
risk: HIGH_SHOULD,
|
|
99
|
+
domain: 'bugs',
|
|
100
|
+
title: 'title a',
|
|
101
|
+
description: 'description a',
|
|
102
|
+
evidence: 'return cache[key]',
|
|
103
|
+
},
|
|
104
|
+
null,
|
|
105
|
+
2,
|
|
106
|
+
)} W=/wt D=/run/diff.patch O=/run/proof-a.json`,
|
|
107
|
+
)
|
|
108
|
+
})
|
|
109
|
+
|
|
110
|
+
test('a template missing a placeholder is rejected, naming it', () => {
|
|
111
|
+
expect(() =>
|
|
112
|
+
buildProofPrompts({
|
|
113
|
+
template: 'F=<<FINDING>> W=<<WORKTREE>> D=<<DIFF_PATH>>',
|
|
114
|
+
findings: [],
|
|
115
|
+
worktree: '/wt',
|
|
116
|
+
diffPath: '/d',
|
|
117
|
+
runDir: '/run',
|
|
118
|
+
}),
|
|
119
|
+
).toThrow('<<OUTPUT_PATH>>')
|
|
120
|
+
})
|
|
121
|
+
|
|
122
|
+
test('an id that is not safe in a file name is rejected', () => {
|
|
123
|
+
expect(() =>
|
|
124
|
+
buildProofPrompts({
|
|
125
|
+
template: TEMPLATE,
|
|
126
|
+
findings: [finding('../x', HIGH_SHOULD)],
|
|
127
|
+
worktree: '/wt',
|
|
128
|
+
diffPath: '/d',
|
|
129
|
+
runDir: '/run',
|
|
130
|
+
}),
|
|
131
|
+
).toThrow('../x')
|
|
132
|
+
})
|
|
133
|
+
|
|
134
|
+
test('a reproduced result carries its repro', () => {
|
|
135
|
+
expect(
|
|
136
|
+
parseProofResult(
|
|
137
|
+
{ id: 'a', outcome: 'reproduced', reason: 'fails as described', repro: REPRO },
|
|
138
|
+
'proof-a.json',
|
|
139
|
+
),
|
|
140
|
+
).toEqual({ id: 'a', outcome: 'reproduced', reason: 'fails as described', repro: REPRO })
|
|
141
|
+
})
|
|
142
|
+
|
|
143
|
+
test('a not-proven result needs only a reason', () => {
|
|
144
|
+
expect(
|
|
145
|
+
parseProofResult({ id: 'a', outcome: 'not-proven', reason: 'no test runner' }, 'proof-a.json'),
|
|
146
|
+
).toEqual({ id: 'a', outcome: 'not-proven', reason: 'no test runner' })
|
|
147
|
+
})
|
|
148
|
+
|
|
149
|
+
test('a reproduced or disproved result without a repro is rejected', () => {
|
|
150
|
+
expect(() =>
|
|
151
|
+
parseProofResult({ id: 'a', outcome: 'reproduced', reason: 'r' }, 'proof-a.json'),
|
|
152
|
+
).toThrow('proof-a.json: reproduced needs a repro with path, command, source and output')
|
|
153
|
+
expect(() =>
|
|
154
|
+
parseProofResult(
|
|
155
|
+
{ id: 'a', outcome: 'disproved', reason: 'r', repro: { ...REPRO, output: '' } },
|
|
156
|
+
'proof-a.json',
|
|
157
|
+
),
|
|
158
|
+
).toThrow('proof-a.json: disproved needs a repro with path, command, source and output')
|
|
159
|
+
})
|
|
160
|
+
|
|
161
|
+
test('an unknown outcome is rejected', () => {
|
|
162
|
+
expect(() =>
|
|
163
|
+
parseProofResult({ id: 'a', outcome: 'maybe', reason: 'r' }, 'proof-a.json'),
|
|
164
|
+
).toThrow('proof-a.json: outcome must be reproduced, disproved or not-proven')
|
|
165
|
+
})
|
|
166
|
+
|
|
167
|
+
test('applying results marks reproduced findings, raises their confidence and keeps the repro', () => {
|
|
168
|
+
// high impact (0.4 x 7) + possible (0.25 x 6) + high confidence (0.2 x 10) + must-fix (0.15 x 10)
|
|
169
|
+
const risk: Risk = { ...HIGH_SHOULD, action: 'must-fix' }
|
|
170
|
+
const a = finding('a', risk)
|
|
171
|
+
const result = applyProofResults({
|
|
172
|
+
findings: [a],
|
|
173
|
+
results: [{ id: 'a', outcome: 'reproduced', reason: 'fails as described', repro: REPRO }],
|
|
174
|
+
})
|
|
175
|
+
expect(result.findings).toEqual([
|
|
176
|
+
{
|
|
177
|
+
...a,
|
|
178
|
+
risk: { ...risk, confidence: 'high' },
|
|
179
|
+
score: 7.8,
|
|
180
|
+
proof: { status: 'reproduced', reason: 'fails as described', repro: REPRO },
|
|
181
|
+
},
|
|
182
|
+
])
|
|
183
|
+
expect(result.dropped).toEqual([])
|
|
184
|
+
})
|
|
185
|
+
|
|
186
|
+
test('a not-proven finding keeps its risk and records why', () => {
|
|
187
|
+
const a = finding('a', HIGH_SHOULD)
|
|
188
|
+
const result = applyProofResults({
|
|
189
|
+
findings: [a],
|
|
190
|
+
results: [{ id: 'a', outcome: 'not-proven', reason: 'needs a live database' }],
|
|
191
|
+
})
|
|
192
|
+
expect(result.findings).toEqual([
|
|
193
|
+
{ ...a, proof: { status: 'not-proven', reason: 'needs a live database' } },
|
|
194
|
+
])
|
|
195
|
+
})
|
|
196
|
+
|
|
197
|
+
test('a disproved finding is removed and recorded with its repro', () => {
|
|
198
|
+
const result = applyProofResults({
|
|
199
|
+
findings: [finding('a', HIGH_SHOULD), finding('b', MEDIUM_SHOULD)],
|
|
200
|
+
results: [{ id: 'a', outcome: 'disproved', reason: 'the guard at a.ts:4 holds', repro: REPRO }],
|
|
201
|
+
})
|
|
202
|
+
expect(result.findings.map((f) => f.id)).toEqual(['b'])
|
|
203
|
+
expect(result.dropped).toEqual([
|
|
204
|
+
{
|
|
205
|
+
id: 'a',
|
|
206
|
+
file: 'src/a.ts',
|
|
207
|
+
line: 10,
|
|
208
|
+
title: 'title a',
|
|
209
|
+
reason: 'the guard at a.ts:4 holds',
|
|
210
|
+
repro: REPRO,
|
|
211
|
+
},
|
|
212
|
+
])
|
|
213
|
+
})
|
|
214
|
+
|
|
215
|
+
test('findings with no result pass through unchanged', () => {
|
|
216
|
+
const b = finding('b', MEDIUM_SHOULD)
|
|
217
|
+
const result = applyProofResults({ findings: [b], results: [] })
|
|
218
|
+
expect(result.findings).toEqual([b])
|
|
219
|
+
})
|
|
220
|
+
|
|
221
|
+
test('a result for an id not in the findings is rejected', () => {
|
|
222
|
+
const results: ProofResult[] = [{ id: 'ghost', outcome: 'not-proven', reason: 'r' }]
|
|
223
|
+
expect(() => applyProofResults({ findings: [], results })).toThrow(
|
|
224
|
+
'proof result for unknown id: ghost',
|
|
225
|
+
)
|
|
226
|
+
})
|
|
@@ -151,3 +151,74 @@ test('refreshFindings rejects on a truncated events line and leaves the previous
|
|
|
151
151
|
const kept = await readFile(join(runDir, 'screen', 'findings.html'), 'utf8')
|
|
152
152
|
expect(kept).toBe('<html>OLD</html>')
|
|
153
153
|
})
|
|
154
|
+
|
|
155
|
+
test('refreshFindings keeps the disproved-by-test panel on re-render', async () => {
|
|
156
|
+
await writeFile(join(runDir, 'findings.final.json'), JSON.stringify([sampleFinding]))
|
|
157
|
+
await writeFile(
|
|
158
|
+
join(runDir, 'proof-dropped.json'),
|
|
159
|
+
JSON.stringify([
|
|
160
|
+
{
|
|
161
|
+
id: 'b',
|
|
162
|
+
file: 'src/y.ts',
|
|
163
|
+
line: 4,
|
|
164
|
+
title: 'Cache never evicts',
|
|
165
|
+
reason: 'evict runs on every put',
|
|
166
|
+
repro: { path: 'p', command: 'bun test p', source: 's', output: 'o' },
|
|
167
|
+
},
|
|
168
|
+
]),
|
|
169
|
+
)
|
|
170
|
+
await refreshFindings(runDir)
|
|
171
|
+
const fresh = await readFile(join(runDir, 'screen', 'findings.html'), 'utf8')
|
|
172
|
+
expect(fresh).toContain('Disproved by test (1)')
|
|
173
|
+
expect(fresh).toContain('Cache never evicts: evict runs on every put')
|
|
174
|
+
})
|
|
175
|
+
|
|
176
|
+
test('refreshFindings keeps the skipped-shards note on re-render', async () => {
|
|
177
|
+
await writeFile(join(runDir, 'findings.final.json'), JSON.stringify([sampleFinding]))
|
|
178
|
+
await mkdir(join(runDir, 'shards'))
|
|
179
|
+
await writeFile(
|
|
180
|
+
join(runDir, 'shards', 'manifest.json'),
|
|
181
|
+
JSON.stringify({
|
|
182
|
+
budget: 6000,
|
|
183
|
+
maxFiles: 80,
|
|
184
|
+
totalFiles: 2,
|
|
185
|
+
totalLines: 20,
|
|
186
|
+
shards: [
|
|
187
|
+
{ id: 1, path: 'shards/shard-1.patch', files: ['src/x.ts'], lines: 10 },
|
|
188
|
+
{ id: 2, path: 'shards/shard-2.patch', files: ['src/z.ts'], lines: 10 },
|
|
189
|
+
],
|
|
190
|
+
}),
|
|
191
|
+
)
|
|
192
|
+
await writeFile(
|
|
193
|
+
join(runDir, 'log.jsonl'),
|
|
194
|
+
`${JSON.stringify({ stage: 'shard-coverage', status: 'partial', reviewed: [1], skipped: [2] })}\n`,
|
|
195
|
+
)
|
|
196
|
+
await refreshFindings(runDir)
|
|
197
|
+
const fresh = await readFile(join(runDir, 'screen', 'findings.html'), 'utf8')
|
|
198
|
+
expect(fresh).toContain('Not reviewed: shard 2 (1 file)')
|
|
199
|
+
})
|
|
200
|
+
|
|
201
|
+
test('refreshFindings keeps the previous-run line on a re-review', async () => {
|
|
202
|
+
await writeFile(join(runDir, 'findings.final.json'), JSON.stringify([sampleFinding]))
|
|
203
|
+
await writeFile(
|
|
204
|
+
join(runDir, 'previous-findings.json'),
|
|
205
|
+
JSON.stringify({
|
|
206
|
+
previousRunId: 'pr-7-100',
|
|
207
|
+
findings: [
|
|
208
|
+
{
|
|
209
|
+
id: 'bugs-1',
|
|
210
|
+
fromRunId: 'pr-7-100',
|
|
211
|
+
file: 'src/x.ts',
|
|
212
|
+
line: 1,
|
|
213
|
+
title: 't',
|
|
214
|
+
domain: 'bugs',
|
|
215
|
+
label: 'posted',
|
|
216
|
+
anchor: 'changed',
|
|
217
|
+
},
|
|
218
|
+
],
|
|
219
|
+
}),
|
|
220
|
+
)
|
|
221
|
+
await refreshFindings(runDir)
|
|
222
|
+
const fresh = await readFile(join(runDir, 'screen', 'findings.html'), 'utf8')
|
|
223
|
+
expect(fresh).toContain('Last review: 1 posted · 1 on changed code · 0 on unchanged code')
|
|
224
|
+
})
|
|
@@ -221,4 +221,38 @@ describe('renderAnnotation', () => {
|
|
|
221
221
|
expect(reasons).toEqual(['wrong', 'not-worth-it', 'duplicate', 'style'])
|
|
222
222
|
expect(html).not.toContain('data-dismissed=')
|
|
223
223
|
})
|
|
224
|
+
test('a reproduced finding shows a reproduced chip and its repro', () => {
|
|
225
|
+
const html = renderAnnotation(
|
|
226
|
+
{
|
|
227
|
+
...finding,
|
|
228
|
+
proof: {
|
|
229
|
+
status: 'reproduced',
|
|
230
|
+
reason: 'fails as described',
|
|
231
|
+
repro: {
|
|
232
|
+
path: 'cache.test.ts',
|
|
233
|
+
command: 'bun test cache.test.ts',
|
|
234
|
+
source: 'expect(a).toBe(b)',
|
|
235
|
+
output: 'expected <b>',
|
|
236
|
+
},
|
|
237
|
+
},
|
|
238
|
+
},
|
|
239
|
+
{ checked: false, posted: false, asCard: true, highlighter: hl },
|
|
240
|
+
)
|
|
241
|
+
expect(html).toContain(
|
|
242
|
+
'<span class="proof-chip reproduced" title="fails as described">REPRODUCED</span>',
|
|
243
|
+
)
|
|
244
|
+
expect(html).toContain('<code class="inline-code">bun test cache.test.ts</code>')
|
|
245
|
+
expect(html).toContain('<pre class="repro">expect(a).toBe(b)</pre>')
|
|
246
|
+
expect(html).toContain('<pre class="repro">expected <b></pre>')
|
|
247
|
+
})
|
|
248
|
+
|
|
249
|
+
test('a not-proven finding shows a not-proven chip with the reason', () => {
|
|
250
|
+
const html = renderAnnotation(
|
|
251
|
+
{ ...finding, proof: { status: 'not-proven', reason: 'needs a live database' } },
|
|
252
|
+
{ checked: false, posted: false, asCard: true, highlighter: hl },
|
|
253
|
+
)
|
|
254
|
+
expect(html).toContain(
|
|
255
|
+
'<span class="proof-chip not-proven" title="needs a live database">NOT PROVEN</span>',
|
|
256
|
+
)
|
|
257
|
+
})
|
|
224
258
|
})
|
|
@@ -381,6 +381,81 @@ test('findings header notes a locally-built diff', () => {
|
|
|
381
381
|
expect(html).toContain('aaaaaaaaaaaa')
|
|
382
382
|
})
|
|
383
383
|
|
|
384
|
+
test('findings header carries the previous-run line on a re-review', () => {
|
|
385
|
+
const html = renderFindingsHtml({
|
|
386
|
+
findings: [],
|
|
387
|
+
postStatus: {},
|
|
388
|
+
pr: { number: 7, branch: 'feature-x', headSha: 'abc123abc123' },
|
|
389
|
+
previousRun: 'Last review: 3 posted · 2 on changed code · 1 on unchanged code',
|
|
390
|
+
dismissed: new Map(),
|
|
391
|
+
topN: DEFAULT_TOP_N,
|
|
392
|
+
highlighter: hl,
|
|
393
|
+
})
|
|
394
|
+
expect(html).toContain('Last review: 3 posted · 2 on changed code · 1 on unchanged code')
|
|
395
|
+
})
|
|
396
|
+
|
|
397
|
+
test('the page lists findings a passing test disproved, folded, with reason and repro', () => {
|
|
398
|
+
const html = renderFindingsHtml({
|
|
399
|
+
findings: [],
|
|
400
|
+
postStatus: {},
|
|
401
|
+
pr: { number: 7, branch: 'feature-x', headSha: 'abc123abc123' },
|
|
402
|
+
disproved: [
|
|
403
|
+
{
|
|
404
|
+
id: 'bugs-2',
|
|
405
|
+
file: 'src/cache.ts',
|
|
406
|
+
line: 40,
|
|
407
|
+
title: 'Cache <never> evicts',
|
|
408
|
+
reason: 'evict() runs on every put at cache.ts:12',
|
|
409
|
+
repro: {
|
|
410
|
+
path: 'cache.magpie-proof-bugs-2.test.ts',
|
|
411
|
+
command: 'bun test cache.magpie-proof-bugs-2.test.ts',
|
|
412
|
+
source: 'expect(size()).toBe(1)',
|
|
413
|
+
output: '1 pass',
|
|
414
|
+
},
|
|
415
|
+
},
|
|
416
|
+
],
|
|
417
|
+
dismissed: new Map(),
|
|
418
|
+
topN: DEFAULT_TOP_N,
|
|
419
|
+
highlighter: hl,
|
|
420
|
+
})
|
|
421
|
+
expect(html).toContain(
|
|
422
|
+
'<details class="pr-brief disproved"><summary class="brief-summary">Disproved by test (1)</summary>',
|
|
423
|
+
)
|
|
424
|
+
expect(html).toContain(
|
|
425
|
+
'<li><code>src/cache.ts:40</code> Cache <never> evicts: evict() runs on every put at cache.ts:12',
|
|
426
|
+
)
|
|
427
|
+
expect(html).toContain('<code>bun test cache.magpie-proof-bugs-2.test.ts</code>')
|
|
428
|
+
expect(html).toContain('<pre class="repro">expect(size()).toBe(1)</pre>')
|
|
429
|
+
expect(html).toContain('<pre class="repro">1 pass</pre>')
|
|
430
|
+
})
|
|
431
|
+
|
|
432
|
+
test('the page has no disproved panel when proof dropped nothing', () => {
|
|
433
|
+
const html = renderFindingsHtml({
|
|
434
|
+
findings: [],
|
|
435
|
+
postStatus: {},
|
|
436
|
+
pr: { number: 7, branch: 'feature-x', headSha: 'abc123abc123' },
|
|
437
|
+
dismissed: new Map(),
|
|
438
|
+
topN: DEFAULT_TOP_N,
|
|
439
|
+
highlighter: hl,
|
|
440
|
+
})
|
|
441
|
+
expect(html).not.toContain('Disproved by test')
|
|
442
|
+
})
|
|
443
|
+
|
|
444
|
+
test('findings header names the shards nobody reviewed, with their files on hover', () => {
|
|
445
|
+
const html = renderFindingsHtml({
|
|
446
|
+
findings: [],
|
|
447
|
+
postStatus: {},
|
|
448
|
+
pr: { number: 7, branch: 'feature-x', headSha: 'abc123abc123' },
|
|
449
|
+
unreviewed: { skipped: [{ id: 3, files: ['docs/d.md', 'src/<x>.ts'] }] },
|
|
450
|
+
dismissed: new Map(),
|
|
451
|
+
topN: DEFAULT_TOP_N,
|
|
452
|
+
highlighter: hl,
|
|
453
|
+
})
|
|
454
|
+
expect(html).toContain(
|
|
455
|
+
'<span class="diff-source" title="docs/d.md, src/<x>.ts">Not reviewed: shard 3 (2 files)</span>',
|
|
456
|
+
)
|
|
457
|
+
})
|
|
458
|
+
|
|
384
459
|
test('findings header stays quiet when the diff came from gh', () => {
|
|
385
460
|
const html = renderFindingsHtml({
|
|
386
461
|
findings: [],
|
|
@@ -13,6 +13,7 @@ test('renderProgressHtml shows all stages with status classes and PR header', ()
|
|
|
13
13
|
dedupe: 'pending',
|
|
14
14
|
critic: 'pending',
|
|
15
15
|
'peer-review': 'pending',
|
|
16
|
+
proof: 'pending',
|
|
16
17
|
report: 'pending',
|
|
17
18
|
post: 'pending',
|
|
18
19
|
},
|
|
@@ -44,6 +45,7 @@ test('renderProgressHtml renders empty specialists-line before any focus reports
|
|
|
44
45
|
dedupe: 'pending',
|
|
45
46
|
critic: 'pending',
|
|
46
47
|
'peer-review': 'pending',
|
|
48
|
+
proof: 'pending',
|
|
47
49
|
report: 'pending',
|
|
48
50
|
post: 'pending',
|
|
49
51
|
},
|
|
@@ -64,6 +66,7 @@ test('pipeline carries a --done-count style for the filling connector line', ()
|
|
|
64
66
|
dedupe: 'running',
|
|
65
67
|
critic: 'pending',
|
|
66
68
|
'peer-review': 'pending',
|
|
69
|
+
proof: 'pending',
|
|
67
70
|
report: 'pending',
|
|
68
71
|
post: 'pending',
|
|
69
72
|
},
|
|
@@ -85,6 +88,7 @@ test('each pipeline stage exposes a short sublabel so first-timers can learn wha
|
|
|
85
88
|
dedupe: 'pending',
|
|
86
89
|
critic: 'pending',
|
|
87
90
|
'peer-review': 'pending',
|
|
91
|
+
proof: 'pending',
|
|
88
92
|
report: 'pending',
|
|
89
93
|
post: 'pending',
|
|
90
94
|
},
|
|
@@ -107,6 +111,7 @@ test('renderProgressHtml includes the archived banner element', () => {
|
|
|
107
111
|
dedupe: 'pending',
|
|
108
112
|
critic: 'pending',
|
|
109
113
|
'peer-review': 'pending',
|
|
114
|
+
proof: 'pending',
|
|
110
115
|
report: 'pending',
|
|
111
116
|
post: 'pending',
|
|
112
117
|
},
|
|
@@ -127,6 +132,7 @@ test('emits the shared pr-header chrome', () => {
|
|
|
127
132
|
dedupe: 'pending',
|
|
128
133
|
critic: 'pending',
|
|
129
134
|
'peer-review': 'pending',
|
|
135
|
+
proof: 'pending',
|
|
130
136
|
report: 'pending',
|
|
131
137
|
post: 'pending',
|
|
132
138
|
},
|
|
@@ -149,6 +155,7 @@ test('progress-pane block replaces diff-pane on the progress page', () => {
|
|
|
149
155
|
dedupe: 'pending',
|
|
150
156
|
critic: 'pending',
|
|
151
157
|
'peer-review': 'pending',
|
|
158
|
+
proof: 'pending',
|
|
152
159
|
report: 'pending',
|
|
153
160
|
post: 'pending',
|
|
154
161
|
},
|
|
@@ -170,6 +177,7 @@ test('progress names the shard count while specialists run', () => {
|
|
|
170
177
|
dedupe: 'pending',
|
|
171
178
|
critic: 'pending',
|
|
172
179
|
'peer-review': 'pending',
|
|
180
|
+
proof: 'pending',
|
|
173
181
|
report: 'pending',
|
|
174
182
|
post: 'pending',
|
|
175
183
|
},
|
|
@@ -191,6 +199,7 @@ test('progress keeps the single-shard wording when there is one shard', () => {
|
|
|
191
199
|
dedupe: 'pending',
|
|
192
200
|
critic: 'pending',
|
|
193
201
|
'peer-review': 'pending',
|
|
202
|
+
proof: 'pending',
|
|
194
203
|
report: 'pending',
|
|
195
204
|
post: 'pending',
|
|
196
205
|
},
|