vouchington-tooling 0.13.3 → 0.14.0
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/ast-grep/rules/no-three-sequential-awaits-tsx.yml +178 -0
- package/ast-grep/rules/no-three-sequential-awaits.yml +169 -0
- package/dist/coverage-transport/outcome.d.mts +6 -5
- package/dist/coverage-transport/outcome.mjs +6 -8
- package/package.json +1 -1
- package/skills/agent-workflow/SKILL.md +4 -1
- package/skills/agent-workflow/references/review-response.md +32 -0
|
@@ -12,6 +12,10 @@ ignores:
|
|
|
12
12
|
- 'web/test-helpers/**'
|
|
13
13
|
rule:
|
|
14
14
|
all:
|
|
15
|
+
- not:
|
|
16
|
+
any:
|
|
17
|
+
- matches: async-disposable-acquisition
|
|
18
|
+
- matches: async-disposable-commit
|
|
15
19
|
- not:
|
|
16
20
|
follows:
|
|
17
21
|
stopBy: neighbor
|
|
@@ -72,6 +76,10 @@ rule:
|
|
|
72
76
|
pattern: $W = await Promise.all($$$)
|
|
73
77
|
- not:
|
|
74
78
|
pattern: $W = await Promise.allSettled($$$)
|
|
79
|
+
not:
|
|
80
|
+
any:
|
|
81
|
+
- matches: async-disposable-acquisition
|
|
82
|
+
- matches: async-disposable-commit
|
|
75
83
|
- any:
|
|
76
84
|
- all:
|
|
77
85
|
- pattern:
|
|
@@ -189,8 +197,16 @@ rule:
|
|
|
189
197
|
pattern: $Y = await Promise.all($$$)
|
|
190
198
|
- not:
|
|
191
199
|
pattern: $Y = await Promise.allSettled($$$)
|
|
200
|
+
- not:
|
|
201
|
+
any:
|
|
202
|
+
- matches: async-disposable-acquisition
|
|
203
|
+
- matches: async-disposable-commit
|
|
192
204
|
- precedes:
|
|
193
205
|
stopBy: neighbor
|
|
206
|
+
not:
|
|
207
|
+
any:
|
|
208
|
+
- matches: async-disposable-acquisition
|
|
209
|
+
- matches: async-disposable-commit
|
|
194
210
|
any:
|
|
195
211
|
- all:
|
|
196
212
|
- pattern:
|
|
@@ -261,6 +277,69 @@ constraints:
|
|
|
261
277
|
P:
|
|
262
278
|
not:
|
|
263
279
|
regex: '^Promise\.all(Settled)?\b'
|
|
280
|
+
utils:
|
|
281
|
+
async-disposable-acquisition:
|
|
282
|
+
all:
|
|
283
|
+
- pattern:
|
|
284
|
+
context: |
|
|
285
|
+
async function run() {
|
|
286
|
+
await using $RESOURCE = await $ACQUIRE
|
|
287
|
+
}
|
|
288
|
+
selector: expression_statement
|
|
289
|
+
- precedes:
|
|
290
|
+
# Pair only with the nearest lifecycle endpoint to the right.
|
|
291
|
+
# Overlapping-resource policy belongs to a separate lifecycle rule.
|
|
292
|
+
stopBy:
|
|
293
|
+
any:
|
|
294
|
+
- pattern:
|
|
295
|
+
context: |
|
|
296
|
+
async function run() {
|
|
297
|
+
await using $_ = await $_
|
|
298
|
+
}
|
|
299
|
+
selector: expression_statement
|
|
300
|
+
- pattern:
|
|
301
|
+
context: |
|
|
302
|
+
async function run() {
|
|
303
|
+
await $_.commit($$$)
|
|
304
|
+
}
|
|
305
|
+
selector: expression_statement
|
|
306
|
+
pattern:
|
|
307
|
+
context: |
|
|
308
|
+
async function run() {
|
|
309
|
+
await $RESOURCE.commit($$$)
|
|
310
|
+
}
|
|
311
|
+
selector: expression_statement
|
|
312
|
+
async-disposable-commit:
|
|
313
|
+
all:
|
|
314
|
+
- pattern:
|
|
315
|
+
context: |
|
|
316
|
+
async function run() {
|
|
317
|
+
await $RESOURCE.commit($$$)
|
|
318
|
+
}
|
|
319
|
+
selector: expression_statement
|
|
320
|
+
- follows:
|
|
321
|
+
# Pair only with the nearest lifecycle endpoint to the left.
|
|
322
|
+
# Overlapping-resource policy belongs to a separate lifecycle rule.
|
|
323
|
+
stopBy:
|
|
324
|
+
any:
|
|
325
|
+
- pattern:
|
|
326
|
+
context: |
|
|
327
|
+
async function run() {
|
|
328
|
+
await using $_ = await $_
|
|
329
|
+
}
|
|
330
|
+
selector: expression_statement
|
|
331
|
+
- pattern:
|
|
332
|
+
context: |
|
|
333
|
+
async function run() {
|
|
334
|
+
await $_.commit($$$)
|
|
335
|
+
}
|
|
336
|
+
selector: expression_statement
|
|
337
|
+
pattern:
|
|
338
|
+
context: |
|
|
339
|
+
async function run() {
|
|
340
|
+
await using $RESOURCE = await $ACQUIRE
|
|
341
|
+
}
|
|
342
|
+
selector: expression_statement
|
|
264
343
|
examples:
|
|
265
344
|
- code: |
|
|
266
345
|
async function run() {
|
|
@@ -286,3 +365,102 @@ examples:
|
|
|
286
365
|
}
|
|
287
366
|
isValid: true
|
|
288
367
|
file: 'web/app/example.tsx'
|
|
368
|
+
- code: |
|
|
369
|
+
async function run() {
|
|
370
|
+
await using transaction = await beginTransaction()
|
|
371
|
+
await applyChange(transaction)
|
|
372
|
+
await transaction.commit()
|
|
373
|
+
return <div />
|
|
374
|
+
}
|
|
375
|
+
isValid: true
|
|
376
|
+
file: 'web/app/example.tsx'
|
|
377
|
+
- code: |
|
|
378
|
+
async function run() {
|
|
379
|
+
await using transaction = await beginBoundedTransaction({ timeoutMs: 1000 })
|
|
380
|
+
await applyFirstChange(transaction)
|
|
381
|
+
await applySecondChange(transaction)
|
|
382
|
+
await transaction.commit()
|
|
383
|
+
return <div />
|
|
384
|
+
}
|
|
385
|
+
isValid: true
|
|
386
|
+
file: 'web/app/example.tsx'
|
|
387
|
+
- code: |
|
|
388
|
+
async function run() {
|
|
389
|
+
await using transaction = await beginTransaction()
|
|
390
|
+
await applyFirstChange(transaction)
|
|
391
|
+
await applySecondChange(transaction)
|
|
392
|
+
await applyThirdChange(transaction)
|
|
393
|
+
await transaction.commit()
|
|
394
|
+
return <div />
|
|
395
|
+
}
|
|
396
|
+
isValid: false
|
|
397
|
+
file: 'web/app/example.tsx'
|
|
398
|
+
- code: |
|
|
399
|
+
async function run() {
|
|
400
|
+
await unrelatedFirst()
|
|
401
|
+
await unrelatedSecond()
|
|
402
|
+
await unrelatedThird()
|
|
403
|
+
await using transaction = await beginTransaction()
|
|
404
|
+
await applyChange(transaction)
|
|
405
|
+
await transaction.commit()
|
|
406
|
+
return <div />
|
|
407
|
+
}
|
|
408
|
+
isValid: false
|
|
409
|
+
file: 'web/app/example.tsx'
|
|
410
|
+
- code: |
|
|
411
|
+
async function run() {
|
|
412
|
+
await using transaction = await beginTransaction()
|
|
413
|
+
await applyFirstChange(transaction)
|
|
414
|
+
await applySecondChange(transaction)
|
|
415
|
+
await other.commit()
|
|
416
|
+
return <div />
|
|
417
|
+
}
|
|
418
|
+
isValid: false
|
|
419
|
+
file: 'web/app/example.tsx'
|
|
420
|
+
- code: |
|
|
421
|
+
async function run() {
|
|
422
|
+
await using transaction = await beginTransaction()
|
|
423
|
+
await applyChange(transaction)
|
|
424
|
+
await transaction.commit()
|
|
425
|
+
await afterFirst()
|
|
426
|
+
await afterSecond()
|
|
427
|
+
await afterThird()
|
|
428
|
+
return <div />
|
|
429
|
+
}
|
|
430
|
+
isValid: false
|
|
431
|
+
file: 'web/app/example.tsx'
|
|
432
|
+
- code: |
|
|
433
|
+
async function run() {
|
|
434
|
+
await using outer = await acquire()
|
|
435
|
+
{
|
|
436
|
+
await using inner = await acquire()
|
|
437
|
+
await innerFirst(inner)
|
|
438
|
+
await innerSecond(inner)
|
|
439
|
+
await innerThird(inner)
|
|
440
|
+
await inner.commit()
|
|
441
|
+
}
|
|
442
|
+
await outer.commit()
|
|
443
|
+
return <div />
|
|
444
|
+
}
|
|
445
|
+
isValid: false
|
|
446
|
+
file: 'web/app/example.tsx'
|
|
447
|
+
- code: |
|
|
448
|
+
async function run() {
|
|
449
|
+
await using transaction = await beginTransaction()
|
|
450
|
+
await applyFirstChange(transaction)
|
|
451
|
+
await applySecondChange(transaction)
|
|
452
|
+
await transaction.rollback()
|
|
453
|
+
return <div />
|
|
454
|
+
}
|
|
455
|
+
isValid: false
|
|
456
|
+
file: 'web/app/example.tsx'
|
|
457
|
+
- code: |
|
|
458
|
+
async function run() {
|
|
459
|
+
await using resource = await acquire()
|
|
460
|
+
await useFirst(resource)
|
|
461
|
+
await useSecond(resource)
|
|
462
|
+
await resource.dispose()
|
|
463
|
+
return <div />
|
|
464
|
+
}
|
|
465
|
+
isValid: false
|
|
466
|
+
file: 'web/app/example.tsx'
|
|
@@ -33,6 +33,10 @@ ignores:
|
|
|
33
33
|
- 'backend/scripts/explain-analyze/**'
|
|
34
34
|
rule:
|
|
35
35
|
all:
|
|
36
|
+
- not:
|
|
37
|
+
any:
|
|
38
|
+
- matches: async-disposable-acquisition
|
|
39
|
+
- matches: async-disposable-commit
|
|
36
40
|
- not:
|
|
37
41
|
follows:
|
|
38
42
|
stopBy: neighbor
|
|
@@ -73,6 +77,10 @@ rule:
|
|
|
73
77
|
pattern: $W = await Promise.all($$$)
|
|
74
78
|
- not:
|
|
75
79
|
pattern: $W = await Promise.allSettled($$$)
|
|
80
|
+
not:
|
|
81
|
+
any:
|
|
82
|
+
- matches: async-disposable-acquisition
|
|
83
|
+
- matches: async-disposable-commit
|
|
76
84
|
- any:
|
|
77
85
|
- all:
|
|
78
86
|
- pattern:
|
|
@@ -150,8 +158,16 @@ rule:
|
|
|
150
158
|
pattern: $Y = await Promise.all($$$)
|
|
151
159
|
- not:
|
|
152
160
|
pattern: $Y = await Promise.allSettled($$$)
|
|
161
|
+
- not:
|
|
162
|
+
any:
|
|
163
|
+
- matches: async-disposable-acquisition
|
|
164
|
+
- matches: async-disposable-commit
|
|
153
165
|
- precedes:
|
|
154
166
|
stopBy: neighbor
|
|
167
|
+
not:
|
|
168
|
+
any:
|
|
169
|
+
- matches: async-disposable-acquisition
|
|
170
|
+
- matches: async-disposable-commit
|
|
155
171
|
any:
|
|
156
172
|
- all:
|
|
157
173
|
- pattern:
|
|
@@ -202,6 +218,69 @@ constraints:
|
|
|
202
218
|
P:
|
|
203
219
|
not:
|
|
204
220
|
regex: '^Promise\.all(Settled)?\b'
|
|
221
|
+
utils:
|
|
222
|
+
async-disposable-acquisition:
|
|
223
|
+
all:
|
|
224
|
+
- pattern:
|
|
225
|
+
context: |
|
|
226
|
+
async function run() {
|
|
227
|
+
await using $RESOURCE = await $ACQUIRE
|
|
228
|
+
}
|
|
229
|
+
selector: expression_statement
|
|
230
|
+
- precedes:
|
|
231
|
+
# Pair only with the nearest lifecycle endpoint to the right.
|
|
232
|
+
# Overlapping-resource policy belongs to a separate lifecycle rule.
|
|
233
|
+
stopBy:
|
|
234
|
+
any:
|
|
235
|
+
- pattern:
|
|
236
|
+
context: |
|
|
237
|
+
async function run() {
|
|
238
|
+
await using $_ = await $_
|
|
239
|
+
}
|
|
240
|
+
selector: expression_statement
|
|
241
|
+
- pattern:
|
|
242
|
+
context: |
|
|
243
|
+
async function run() {
|
|
244
|
+
await $_.commit($$$)
|
|
245
|
+
}
|
|
246
|
+
selector: expression_statement
|
|
247
|
+
pattern:
|
|
248
|
+
context: |
|
|
249
|
+
async function run() {
|
|
250
|
+
await $RESOURCE.commit($$$)
|
|
251
|
+
}
|
|
252
|
+
selector: expression_statement
|
|
253
|
+
async-disposable-commit:
|
|
254
|
+
all:
|
|
255
|
+
- pattern:
|
|
256
|
+
context: |
|
|
257
|
+
async function run() {
|
|
258
|
+
await $RESOURCE.commit($$$)
|
|
259
|
+
}
|
|
260
|
+
selector: expression_statement
|
|
261
|
+
- follows:
|
|
262
|
+
# Pair only with the nearest lifecycle endpoint to the left.
|
|
263
|
+
# Overlapping-resource policy belongs to a separate lifecycle rule.
|
|
264
|
+
stopBy:
|
|
265
|
+
any:
|
|
266
|
+
- pattern:
|
|
267
|
+
context: |
|
|
268
|
+
async function run() {
|
|
269
|
+
await using $_ = await $_
|
|
270
|
+
}
|
|
271
|
+
selector: expression_statement
|
|
272
|
+
- pattern:
|
|
273
|
+
context: |
|
|
274
|
+
async function run() {
|
|
275
|
+
await $_.commit($$$)
|
|
276
|
+
}
|
|
277
|
+
selector: expression_statement
|
|
278
|
+
pattern:
|
|
279
|
+
context: |
|
|
280
|
+
async function run() {
|
|
281
|
+
await using $RESOURCE = await $ACQUIRE
|
|
282
|
+
}
|
|
283
|
+
selector: expression_statement
|
|
205
284
|
examples:
|
|
206
285
|
- code: |
|
|
207
286
|
async function run() {
|
|
@@ -240,3 +319,93 @@ examples:
|
|
|
240
319
|
}
|
|
241
320
|
isValid: true
|
|
242
321
|
file: 'backend/services/example.mts'
|
|
322
|
+
- code: |
|
|
323
|
+
async function run() {
|
|
324
|
+
await using transaction = await beginTransaction()
|
|
325
|
+
await applyChange(transaction)
|
|
326
|
+
await transaction.commit()
|
|
327
|
+
}
|
|
328
|
+
isValid: true
|
|
329
|
+
file: 'backend/services/example.mts'
|
|
330
|
+
- code: |
|
|
331
|
+
async function run() {
|
|
332
|
+
await using transaction = await beginBoundedTransaction({ timeoutMs: 1000 })
|
|
333
|
+
await applyFirstChange(transaction)
|
|
334
|
+
await applySecondChange(transaction)
|
|
335
|
+
await transaction.commit()
|
|
336
|
+
}
|
|
337
|
+
isValid: true
|
|
338
|
+
file: 'backend/services/example.mts'
|
|
339
|
+
- code: |
|
|
340
|
+
async function run() {
|
|
341
|
+
await using transaction = await beginTransaction()
|
|
342
|
+
await applyFirstChange(transaction)
|
|
343
|
+
await applySecondChange(transaction)
|
|
344
|
+
await applyThirdChange(transaction)
|
|
345
|
+
await transaction.commit()
|
|
346
|
+
}
|
|
347
|
+
isValid: false
|
|
348
|
+
file: 'backend/services/example.mts'
|
|
349
|
+
- code: |
|
|
350
|
+
async function run() {
|
|
351
|
+
await unrelatedFirst()
|
|
352
|
+
await unrelatedSecond()
|
|
353
|
+
await unrelatedThird()
|
|
354
|
+
await using transaction = await beginTransaction()
|
|
355
|
+
await applyChange(transaction)
|
|
356
|
+
await transaction.commit()
|
|
357
|
+
}
|
|
358
|
+
isValid: false
|
|
359
|
+
file: 'backend/services/example.mts'
|
|
360
|
+
- code: |
|
|
361
|
+
async function run() {
|
|
362
|
+
await using transaction = await beginTransaction()
|
|
363
|
+
await applyFirstChange(transaction)
|
|
364
|
+
await applySecondChange(transaction)
|
|
365
|
+
await other.commit()
|
|
366
|
+
}
|
|
367
|
+
isValid: false
|
|
368
|
+
file: 'backend/services/example.mts'
|
|
369
|
+
- code: |
|
|
370
|
+
async function run() {
|
|
371
|
+
await using transaction = await beginTransaction()
|
|
372
|
+
await applyChange(transaction)
|
|
373
|
+
await transaction.commit()
|
|
374
|
+
await afterFirst()
|
|
375
|
+
await afterSecond()
|
|
376
|
+
await afterThird()
|
|
377
|
+
}
|
|
378
|
+
isValid: false
|
|
379
|
+
file: 'backend/services/example.mts'
|
|
380
|
+
- code: |
|
|
381
|
+
async function run() {
|
|
382
|
+
await using outer = await acquire()
|
|
383
|
+
{
|
|
384
|
+
await using inner = await acquire()
|
|
385
|
+
await innerFirst(inner)
|
|
386
|
+
await innerSecond(inner)
|
|
387
|
+
await innerThird(inner)
|
|
388
|
+
await inner.commit()
|
|
389
|
+
}
|
|
390
|
+
await outer.commit()
|
|
391
|
+
}
|
|
392
|
+
isValid: false
|
|
393
|
+
file: 'backend/services/example.mts'
|
|
394
|
+
- code: |
|
|
395
|
+
async function run() {
|
|
396
|
+
await using transaction = await beginTransaction()
|
|
397
|
+
await applyFirstChange(transaction)
|
|
398
|
+
await applySecondChange(transaction)
|
|
399
|
+
await transaction.rollback()
|
|
400
|
+
}
|
|
401
|
+
isValid: false
|
|
402
|
+
file: 'backend/services/example.mts'
|
|
403
|
+
- code: |
|
|
404
|
+
async function run() {
|
|
405
|
+
await using resource = await acquire()
|
|
406
|
+
await useFirst(resource)
|
|
407
|
+
await useSecond(resource)
|
|
408
|
+
await resource.dispose()
|
|
409
|
+
}
|
|
410
|
+
isValid: false
|
|
411
|
+
file: 'backend/services/example.mts'
|
|
@@ -14,15 +14,16 @@ export type BlobPrimaryState = 'true' | 'false' | 'skipped';
|
|
|
14
14
|
export declare function isBlobPrimaryState(value: string | undefined): value is BlobPrimaryState;
|
|
15
15
|
/**
|
|
16
16
|
* Sibling to `assertCoverageTransportOutcome` for the Vitest blob. `true`/`false` are the S3
|
|
17
|
-
* upload step's `blob` output; `skipped` means that step never ran.
|
|
18
|
-
*
|
|
17
|
+
* upload step's `blob` output; `skipped` means that step never ran. Workflows gate the GitHub
|
|
18
|
+
* fallback on `blob != 'true'`, so a persisted S3 primary with no GitHub artifact attempt is the
|
|
19
|
+
* expected steady state, not a degraded one.
|
|
19
20
|
*/
|
|
20
21
|
export declare function assertCoverageTransportBlobOutcome(suite: string, primaryPersisted: BlobPrimaryState, artifactAttempt1: StepOutcome, artifactAttempt2: StepOutcome, emit?: (line: string) => void): boolean;
|
|
21
22
|
export type AppendOutput = (path: string, data: string) => void;
|
|
22
23
|
/**
|
|
23
|
-
* Writes `blob=true|false` to `$GITHUB_OUTPUT` for outcome reporting.
|
|
24
|
-
*
|
|
25
|
-
*
|
|
24
|
+
* Writes `blob=true|false` to `$GITHUB_OUTPUT` for outcome reporting. Workflows gate the
|
|
25
|
+
* GitHub-fallback blob upload on this signal (`blob != 'true'`). The upload subcommand's exit
|
|
26
|
+
* code tracks the coverage pair, not the blob. A no-op outside CI (`githubOutputPath` unset).
|
|
26
27
|
*/
|
|
27
28
|
export declare function writeUploadOutcomeOutput(outcome: {
|
|
28
29
|
readonly blob: boolean;
|
|
@@ -41,15 +41,13 @@ export function isBlobPrimaryState(value) {
|
|
|
41
41
|
}
|
|
42
42
|
/**
|
|
43
43
|
* Sibling to `assertCoverageTransportOutcome` for the Vitest blob. `true`/`false` are the S3
|
|
44
|
-
* upload step's `blob` output; `skipped` means that step never ran.
|
|
45
|
-
*
|
|
44
|
+
* upload step's `blob` output; `skipped` means that step never ran. Workflows gate the GitHub
|
|
45
|
+
* fallback on `blob != 'true'`, so a persisted S3 primary with no GitHub artifact attempt is the
|
|
46
|
+
* expected steady state, not a degraded one.
|
|
46
47
|
*/
|
|
47
48
|
export function assertCoverageTransportBlobOutcome(suite, primaryPersisted, artifactAttempt1, artifactAttempt2, emit = (line) => process.stderr.write(`${line}\n`)) {
|
|
48
49
|
const artifactSucceeded = artifactAttempt1 === 'success' || artifactAttempt2 === 'success';
|
|
49
50
|
if (primaryPersisted === 'true') {
|
|
50
|
-
if (!artifactSucceeded) {
|
|
51
|
-
emit(`::warning::Vitest blob persisted only to S3 for suite=${suite}; GitHub artifact fallback is degraded.`);
|
|
52
|
-
}
|
|
53
51
|
return true;
|
|
54
52
|
}
|
|
55
53
|
if (artifactSucceeded) {
|
|
@@ -62,9 +60,9 @@ export function assertCoverageTransportBlobOutcome(suite, primaryPersisted, arti
|
|
|
62
60
|
return false;
|
|
63
61
|
}
|
|
64
62
|
/**
|
|
65
|
-
* Writes `blob=true|false` to `$GITHUB_OUTPUT` for outcome reporting.
|
|
66
|
-
*
|
|
67
|
-
*
|
|
63
|
+
* Writes `blob=true|false` to `$GITHUB_OUTPUT` for outcome reporting. Workflows gate the
|
|
64
|
+
* GitHub-fallback blob upload on this signal (`blob != 'true'`). The upload subcommand's exit
|
|
65
|
+
* code tracks the coverage pair, not the blob. A no-op outside CI (`githubOutputPath` unset).
|
|
68
66
|
*/
|
|
69
67
|
export function writeUploadOutcomeOutput(outcome, githubOutputPath, appendOutput = appendFileSync) {
|
|
70
68
|
if (githubOutputPath)
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "vouchington-tooling",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.14.0",
|
|
4
4
|
"description": "Vouchington CLI and extractable tooling libraries.",
|
|
5
5
|
"homepage": "https://github.com/vouchington/vouchington-tooling/tree/main/packages/vouchington-tooling#readme",
|
|
6
6
|
"bugs": {
|
|
@@ -22,9 +22,12 @@ branching, review, and release policy.
|
|
|
22
22
|
checks with a concrete reason.
|
|
23
23
|
6. Review the diff for accidental files, secrets, generated output, broken documentation links,
|
|
24
24
|
and assumptions that belong in local instructions instead.
|
|
25
|
+
7. When feedback arrives on an open change, give every surfaced item a disposition and drive it to
|
|
26
|
+
closure before hand-off; do not leave items unaddressed or silently absorb or defer them.
|
|
25
27
|
|
|
26
28
|
For portable implementation and review checks, read [implementation](references/implementation.md),
|
|
27
|
-
[review](references/review.md),
|
|
29
|
+
[review](references/review.md), [review response](references/review-response.md), and
|
|
30
|
+
[evidence sweep](references/evidence-sweep.md). The older
|
|
28
31
|
[implementation and review](references/implementation-and-review.md) remains a compact overview.
|
|
29
32
|
Local instructions remain authoritative for commands, commits, review systems, and release policy.
|
|
30
33
|
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
# Review response
|
|
2
|
+
|
|
3
|
+
Applies when responding to feedback on an open change — a human reviewer, an automated reviewer, or
|
|
4
|
+
a shepherding tool that surfaces both. This is the response side of [review](review.md); its closing
|
|
5
|
+
line, to record unresolved risk as an explicit decision or follow-up and never as an unnoticed gap,
|
|
6
|
+
is what the ladder below operationalizes.
|
|
7
|
+
|
|
8
|
+
Give every surfaced item exactly one disposition before its resolution runs, recorded where the
|
|
9
|
+
review conversation lives. Invalid — wrong, already satisfied by the current diff, or out of an
|
|
10
|
+
already-settled scope — gets a reason and closes with no code change and no follow-up. Blocking —
|
|
11
|
+
correctness, security, data safety, or a gap against a linked requirement — gets fixed, pushed, and
|
|
12
|
+
confirmed on the change's head commit before closing; closing first can leave an unfixed commit
|
|
13
|
+
behind a closed conversation. Non-blocking gets folded into an already-planned push when the fix is
|
|
14
|
+
cheap and low-risk, and otherwise gets recorded as a follow-up only when leaving it undone would
|
|
15
|
+
change behavior, structure, or risk: reuse or extend an existing follow-up before opening a new one,
|
|
16
|
+
and group related items from the same round into one. A correct item that clears none of those bars
|
|
17
|
+
— a style preference, a restatement, polish the change is fine without — is declined with a reason
|
|
18
|
+
and no follow-up; that is the expected outcome for a minor suggestion, not a lapse. Escalate — work
|
|
19
|
+
the change cannot absorb as feedback, such as a large architectural or ownership change — is
|
|
20
|
+
recorded where decisions are tracked and reported for direction rather than implemented or silently
|
|
21
|
+
downgraded to a follow-up.
|
|
22
|
+
|
|
23
|
+
Read every outstanding item before editing and drain the round locally; the cost of iterating is the
|
|
24
|
+
push, not the commit, because a push re-runs checks and re-triggers automated reviewers. Declining a
|
|
25
|
+
round without pushing is what lets it converge instead of regenerating the same suggestions on every
|
|
26
|
+
cycle. By hand-off, every item carries a disposition and reaches closure — an escalation through its
|
|
27
|
+
decision record rather than an open conversation — except where the review surface itself withholds
|
|
28
|
+
the capability to close an item; say so and leave it rather than forcing a resolution it never
|
|
29
|
+
authorized.
|
|
30
|
+
|
|
31
|
+
This skill supplies no review system, resolution mechanism, issue tracker, label, or severity
|
|
32
|
+
vocabulary; a consumer wrapper owns those.
|