@boxxy/closeout 0.2.1 → 0.2.2
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/README.md +7 -5
- package/dist/index.d.ts +3 -2
- package/dist/phases/create-review-phase.d.ts +2 -0
- package/dist/review/build-round-cause.d.ts +2 -0
- package/dist/review/count-unrelated-lines.d.ts +2 -0
- package/dist/types.d.ts +0 -4
- package/package.json +5 -6
- package/src/build-schema-parser.ts +6 -0
- package/src/close-out-limits.ts +0 -1
- package/src/index.ts +3 -2
- package/src/phase-kinds.ts +3 -3
- package/src/phases/build.ts +13 -6
- package/src/phases/create-review-phase.ts +89 -0
- package/src/review/build-round-cause.ts +20 -0
- package/src/review/count-unrelated-lines.ts +13 -0
- package/src/types.ts +0 -9
- package/dist/noslop/build-noslop-summary.d.ts +0 -3
- package/dist/noslop/noslop-report-schema.d.ts +0 -9
- package/dist/phases/review.d.ts +0 -1
- package/dist/test-utils/build-mock-noslop-report.d.ts +0 -2
- package/src/noslop/build-noslop-summary.ts +0 -25
- package/src/noslop/noslop-report-schema.ts +0 -13
- package/src/phases/review.ts +0 -50
- package/src/test-utils/build-mock-noslop-report.ts +0 -30
package/README.md
CHANGED
|
@@ -29,11 +29,13 @@ The package runs on [Bun](https://bun.sh) 1.3 or newer.
|
|
|
29
29
|
|
|
30
30
|
## Features
|
|
31
31
|
|
|
32
|
-
- `triage`, `design
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
32
|
+
- `triage`, `design` and `build`, and `phaseKinds`, which lists them for the rules loader. Each
|
|
33
|
+
parser returns the stored close-out, or every problem with it in one pass.
|
|
34
|
+
- `createReviewPhase`, which builds the review phase kind on the review gate's `buildCommand`. The
|
|
35
|
+
server adds it to `phaseKinds`.
|
|
36
|
+
- `countUnrelatedLines`, the changed lines of a later review in files that had no open finding.
|
|
37
|
+
- `closeOutLimits`, the byte limit of a close-out, and `measureJsonBytes`, which measures a value as
|
|
38
|
+
stored.
|
|
37
39
|
- `dedupeOpenItems`, which matches each open item the agent reports to an open item with the same
|
|
38
40
|
title.
|
|
39
41
|
|
package/dist/index.d.ts
CHANGED
|
@@ -4,6 +4,7 @@ export { measureJsonBytes } from './measure-json-bytes.ts';
|
|
|
4
4
|
export { phaseKinds } from './phase-kinds.ts';
|
|
5
5
|
export { build } from './phases/build.ts';
|
|
6
6
|
export { design } from './phases/design.ts';
|
|
7
|
-
export {
|
|
7
|
+
export { createReviewPhase } from './phases/create-review-phase.ts';
|
|
8
8
|
export { triage } from './phases/triage.ts';
|
|
9
|
-
export
|
|
9
|
+
export { countUnrelatedLines } from './review/count-unrelated-lines.ts';
|
|
10
|
+
export type { CloseOutLimits } from './types.ts';
|
package/dist/types.d.ts
CHANGED
|
@@ -1,7 +1,3 @@
|
|
|
1
|
-
import type { z } from 'zod';
|
|
2
|
-
import type { noslopReportSchema } from './noslop/noslop-report-schema.ts';
|
|
3
1
|
export interface CloseOutLimits {
|
|
4
2
|
readonly closeOutBytes: 120_000;
|
|
5
|
-
readonly noslopReportBytes: 32_768;
|
|
6
3
|
}
|
|
7
|
-
export type NoslopReport = z.output<typeof noslopReportSchema>;
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@boxxy/closeout",
|
|
3
|
-
"version": "0.2.
|
|
3
|
+
"version": "0.2.2",
|
|
4
4
|
"description": "boxxy's phase kinds: the input, the close-out parser and the settle rule of triage, design, build and review",
|
|
5
5
|
"homepage": "https://github.com/zgeoff/boxxy/tree/main/modules/closeout#readme",
|
|
6
6
|
"bugs": "https://github.com/zgeoff/boxxy/issues",
|
|
@@ -20,8 +20,7 @@
|
|
|
20
20
|
".": {
|
|
21
21
|
"types": "./dist/index.d.ts",
|
|
22
22
|
"default": "./src/index.ts"
|
|
23
|
-
}
|
|
24
|
-
"./test-utils/*": "./src/test-utils/*.ts"
|
|
23
|
+
}
|
|
25
24
|
},
|
|
26
25
|
"publishConfig": {
|
|
27
26
|
"access": "public"
|
|
@@ -31,14 +30,14 @@
|
|
|
31
30
|
"typecheck": "tsrs --noEmit"
|
|
32
31
|
},
|
|
33
32
|
"dependencies": {
|
|
34
|
-
"@boxxy/model": "0.2.
|
|
33
|
+
"@boxxy/model": "0.2.2",
|
|
35
34
|
"tiny-invariant": "1.3.3",
|
|
36
35
|
"ts-pattern": "5.9.2",
|
|
37
36
|
"zod": "4.6.7"
|
|
38
37
|
},
|
|
39
38
|
"devDependencies": {
|
|
40
|
-
"@boxxy/planning": "0.2.
|
|
41
|
-
"@boxxy/rules": "0.2.
|
|
39
|
+
"@boxxy/planning": "0.2.2",
|
|
40
|
+
"@boxxy/rules": "0.2.2",
|
|
42
41
|
"@faker-js/faker": "10.6.0"
|
|
43
42
|
},
|
|
44
43
|
"peerDependencies": {
|
|
@@ -71,6 +71,12 @@ function splitFriction(raw: unknown): FrictionSplit {
|
|
|
71
71
|
}
|
|
72
72
|
|
|
73
73
|
const { frictionDropped: _, ...rest } = raw;
|
|
74
|
+
|
|
75
|
+
// a body with no friction list, such as a review's, keeps no `friction` key for a strict schema
|
|
76
|
+
if (!('friction' in rest)) {
|
|
77
|
+
return { candidate: rest, keptRawIndexes: [], dropped: 0 };
|
|
78
|
+
}
|
|
79
|
+
|
|
74
80
|
const entries: readonly unknown[] = Array.isArray(rest['friction']) ? rest['friction'] : [];
|
|
75
81
|
const keptRawIndexes = entries.flatMap((entry, index) => (isUntargeted(entry) ? [] : [index]));
|
|
76
82
|
|
package/src/close-out-limits.ts
CHANGED
package/src/index.ts
CHANGED
|
@@ -4,6 +4,7 @@ export { measureJsonBytes } from './measure-json-bytes.ts';
|
|
|
4
4
|
export { phaseKinds } from './phase-kinds.ts';
|
|
5
5
|
export { build } from './phases/build.ts';
|
|
6
6
|
export { design } from './phases/design.ts';
|
|
7
|
-
export {
|
|
7
|
+
export { createReviewPhase } from './phases/create-review-phase.ts';
|
|
8
8
|
export { triage } from './phases/triage.ts';
|
|
9
|
-
export
|
|
9
|
+
export { countUnrelatedLines } from './review/count-unrelated-lines.ts';
|
|
10
|
+
export type { CloseOutLimits } from './types.ts';
|
package/src/phase-kinds.ts
CHANGED
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
import type { PhaseKind } from '@boxxy/model';
|
|
2
2
|
import { build } from './phases/build.ts';
|
|
3
3
|
import { design } from './phases/design.ts';
|
|
4
|
-
import { review } from './phases/review.ts';
|
|
5
4
|
import { triage } from './phases/triage.ts';
|
|
6
5
|
|
|
7
|
-
// The phase kinds that
|
|
8
|
-
|
|
6
|
+
// The phase kinds that need no port, in pipeline order. The server adds the review phase kind,
|
|
7
|
+
// which it builds on its review gate.
|
|
8
|
+
export const phaseKinds: readonly PhaseKind[] = [triage, design, build];
|
package/src/phases/build.ts
CHANGED
|
@@ -7,12 +7,13 @@ import type {
|
|
|
7
7
|
Effect,
|
|
8
8
|
ItemView,
|
|
9
9
|
PhaseInputContext,
|
|
10
|
+
SentBack,
|
|
10
11
|
} from '@boxxy/model';
|
|
11
12
|
import {
|
|
12
13
|
brandSchemas,
|
|
13
14
|
buildCloseOutSchema,
|
|
14
15
|
definePhase,
|
|
15
|
-
|
|
16
|
+
storedReviewCloseOutSchema,
|
|
16
17
|
toJSONValue,
|
|
17
18
|
} from '@boxxy/model';
|
|
18
19
|
import { match } from 'ts-pattern';
|
|
@@ -59,19 +60,25 @@ export const build = definePhase({
|
|
|
59
60
|
}),
|
|
60
61
|
});
|
|
61
62
|
|
|
62
|
-
// The findings come from the latest review, and only when it asked for changes: a build after
|
|
63
|
-
// a passed review, sent back by the owner, gets none.
|
|
64
63
|
function buildBuildInput(ctx: PhaseInputContext): BuildInput {
|
|
65
|
-
const review = findLatestCloseOut(ctx.history, 'review', reviewCloseOutSchema);
|
|
66
|
-
|
|
67
64
|
return {
|
|
68
65
|
acceptance: buildCriterionTexts(ctx.item),
|
|
69
66
|
design: findLatestDesign(ctx.history),
|
|
70
|
-
|
|
67
|
+
sentBack: findReviewSentBack(ctx),
|
|
71
68
|
pullRequest: findPullRequestLink(ctx.pullRequests),
|
|
72
69
|
};
|
|
73
70
|
}
|
|
74
71
|
|
|
72
|
+
// Only a latest review that asked for changes sends the build its findings: a build after a
|
|
73
|
+
// passed review, sent back by the owner, gets none.
|
|
74
|
+
function findReviewSentBack(ctx: PhaseInputContext): SentBack | null {
|
|
75
|
+
const reading = findLatestCloseOut(ctx.history, 'review', storedReviewCloseOutSchema)?.reading;
|
|
76
|
+
|
|
77
|
+
return reading?.status === 'blocked' && reading.why === 'open-findings'
|
|
78
|
+
? { from: 'review', head: reading.head, open: reading.open, answerHint: reading.answerHint }
|
|
79
|
+
: null;
|
|
80
|
+
}
|
|
81
|
+
|
|
75
82
|
function collectBuildProblems(closeOut: BuildCloseOut, ctx: CloseOutContext): CloseOutProblem[] {
|
|
76
83
|
const acceptance = new Set(ctx.item.acceptance.map((criterion) => criterion.id));
|
|
77
84
|
const own = new Set(ctx.evidence.map((row) => row.factId));
|
|
@@ -0,0 +1,89 @@
|
|
|
1
|
+
import type {
|
|
2
|
+
GateReading,
|
|
3
|
+
PhaseInputContext,
|
|
4
|
+
ReviewCloseOut,
|
|
5
|
+
ReviewGate,
|
|
6
|
+
ReviewInput,
|
|
7
|
+
} from '@boxxy/model';
|
|
8
|
+
import {
|
|
9
|
+
brandSchemas,
|
|
10
|
+
collectSettled,
|
|
11
|
+
definePhase,
|
|
12
|
+
reviewCloseOutSchema,
|
|
13
|
+
toJSONValue,
|
|
14
|
+
} from '@boxxy/model';
|
|
15
|
+
import { match } from 'ts-pattern';
|
|
16
|
+
import { buildSchemaParser } from '../build-schema-parser.ts';
|
|
17
|
+
import { buildRoundCause } from '../review/build-round-cause.ts';
|
|
18
|
+
import { findPullRequestLink } from '../utils/find-pull-request-link.ts';
|
|
19
|
+
|
|
20
|
+
// The server passes the gate's pure `buildCommand`, so the module never imports an adapter. The
|
|
21
|
+
// session runs the command and sends the tool's raw result; the server builds the stored
|
|
22
|
+
// close-out from it, and this parser reads that.
|
|
23
|
+
export function createReviewPhase(deps: Pick<ReviewGate, 'buildCommand'>) {
|
|
24
|
+
return definePhase({
|
|
25
|
+
name: brandSchemas.phaseName.parse('review'),
|
|
26
|
+
version: 2,
|
|
27
|
+
worker: brandSchemas.workerKindName.parse('atc.session'),
|
|
28
|
+
outcomes: ['passed', 'changes-requested', 'failed'],
|
|
29
|
+
needsAcceptance: true,
|
|
30
|
+
input: (ctx) => toJSONValue(buildReviewInput(ctx, deps)),
|
|
31
|
+
closeOut: buildSchemaParser({
|
|
32
|
+
schema: reviewCloseOutSchema,
|
|
33
|
+
toBody: (raw): ReviewCloseOut => raw,
|
|
34
|
+
check: () => [],
|
|
35
|
+
}),
|
|
36
|
+
settle: (input) => {
|
|
37
|
+
const outcome = pickReviewOutcome(input.closeOut.reading);
|
|
38
|
+
|
|
39
|
+
return {
|
|
40
|
+
state: outcome === 'failed' ? 'failed' : 'succeeded',
|
|
41
|
+
failure: outcome === 'failed' ? 'review-failed' : null,
|
|
42
|
+
outcome,
|
|
43
|
+
effects: [],
|
|
44
|
+
frictionDropped: 0,
|
|
45
|
+
};
|
|
46
|
+
},
|
|
47
|
+
});
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
// The round counts the reviews since the item's latest retry that asked for changes.
|
|
51
|
+
function buildReviewInput(
|
|
52
|
+
ctx: PhaseInputContext,
|
|
53
|
+
deps: Pick<ReviewGate, 'buildCommand'>,
|
|
54
|
+
): ReviewInput {
|
|
55
|
+
const since = ctx.retries.at(-1)?.at ?? Number.NEGATIVE_INFINITY;
|
|
56
|
+
const settled = collectSettled(ctx.history).filter((attempt) => attempt.endedAt > since);
|
|
57
|
+
|
|
58
|
+
const round =
|
|
59
|
+
1 +
|
|
60
|
+
settled.filter(
|
|
61
|
+
(attempt) => attempt.phase === 'review' && attempt.outcome === 'changes-requested',
|
|
62
|
+
).length;
|
|
63
|
+
|
|
64
|
+
const cause = buildRoundCause(settled);
|
|
65
|
+
const link = findPullRequestLink(ctx.pullRequests);
|
|
66
|
+
|
|
67
|
+
if (link === null) {
|
|
68
|
+
return { pullRequest: null, round, cause, command: null };
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
const pullRequest = { repo: link.repo, number: link.number };
|
|
72
|
+
|
|
73
|
+
return {
|
|
74
|
+
pullRequest,
|
|
75
|
+
round,
|
|
76
|
+
cause,
|
|
77
|
+
command: deps.buildCommand({ attempt: ctx.attempt, pullRequest, round, cause }),
|
|
78
|
+
};
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
// A failed call, an unreadable result or a reading that covers no head is `failed`: boxxy cannot
|
|
82
|
+
// tell whether the head is clear.
|
|
83
|
+
function pickReviewOutcome(reading: GateReading): 'passed' | 'changes-requested' | 'failed' {
|
|
84
|
+
return match(reading)
|
|
85
|
+
.with({ status: 'passed' }, () => 'passed' as const)
|
|
86
|
+
.with({ status: 'blocked', why: 'open-findings' }, () => 'changes-requested' as const)
|
|
87
|
+
.with({ status: 'blocked' }, { status: 'not-covered' }, () => 'failed' as const)
|
|
88
|
+
.exhaustive();
|
|
89
|
+
}
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
import type { RoundCause, Settled } from '@boxxy/model';
|
|
2
|
+
|
|
3
|
+
// Why a review round runs, from the item's settled attempts since its latest retry, oldest
|
|
4
|
+
// first. `rework`: the latest settled attempt that is not a build is a review that asked for
|
|
5
|
+
// changes. `recheck`: the item came back from another phase, or the last review failed.
|
|
6
|
+
export function buildRoundCause(history: readonly Settled[]): RoundCause {
|
|
7
|
+
const reviews = history.filter(
|
|
8
|
+
(settled) => settled.phase === 'review' && settled.outcome !== 'lost',
|
|
9
|
+
);
|
|
10
|
+
|
|
11
|
+
if (reviews.length === 0) {
|
|
12
|
+
return 'first';
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
const latestOther = history.findLast((settled) => settled.phase !== 'build');
|
|
16
|
+
|
|
17
|
+
return latestOther?.phase === 'review' && latestOther.outcome === 'changes-requested'
|
|
18
|
+
? 'rework'
|
|
19
|
+
: 'recheck';
|
|
20
|
+
}
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
import type { ChangedFile } from '@boxxy/model';
|
|
2
|
+
|
|
3
|
+
// The changed lines in the files that had no open finding at the earlier review.
|
|
4
|
+
export function countUnrelatedLines(
|
|
5
|
+
changed: readonly ChangedFile[],
|
|
6
|
+
findingPaths: readonly string[],
|
|
7
|
+
): number {
|
|
8
|
+
const withFindings = new Set(findingPaths);
|
|
9
|
+
|
|
10
|
+
return changed
|
|
11
|
+
.filter((file) => !withFindings.has(file.path))
|
|
12
|
+
.reduce((sum, file) => sum + file.changedLines, 0);
|
|
13
|
+
}
|
package/src/types.ts
CHANGED
|
@@ -1,13 +1,4 @@
|
|
|
1
|
-
import type { z } from 'zod';
|
|
2
|
-
import type { noslopReportSchema } from './noslop/noslop-report-schema.ts';
|
|
3
|
-
|
|
4
1
|
export interface CloseOutLimits {
|
|
5
2
|
// a close-out as stored, in any phase
|
|
6
3
|
readonly closeOutBytes: 120_000;
|
|
7
|
-
|
|
8
|
-
// the most of noslop's result that a review close-out keeps whole
|
|
9
|
-
readonly noslopReportBytes: 32_768;
|
|
10
4
|
}
|
|
11
|
-
|
|
12
|
-
// What `noslop review --json --report json` printed, as the review session sends it
|
|
13
|
-
export type NoslopReport = z.output<typeof noslopReportSchema>;
|
|
@@ -1,9 +0,0 @@
|
|
|
1
|
-
import { z } from 'zod';
|
|
2
|
-
export declare const noslopReportSchema: z.ZodObject<{
|
|
3
|
-
version: z.ZodNumber;
|
|
4
|
-
reviewID: z.ZodString;
|
|
5
|
-
head: z.core.$ZodBranded<z.ZodString, "CommitSha", "out">;
|
|
6
|
-
findings: z.ZodArray<z.ZodUnknown>;
|
|
7
|
-
failedCalls: z.ZodArray<z.ZodUnknown>;
|
|
8
|
-
fixChecks: z.ZodExactOptional<z.ZodArray<z.ZodUnknown>>;
|
|
9
|
-
}, z.core.$loose>;
|
package/dist/phases/review.d.ts
DELETED
|
@@ -1 +0,0 @@
|
|
|
1
|
-
export declare const review: import("@boxxy/model").PhaseKind<unknown, import("@boxxy/content-hash").JsonValue, readonly string[]>;
|
|
@@ -1,25 +0,0 @@
|
|
|
1
|
-
import type { NoslopSummary } from '@boxxy/model';
|
|
2
|
-
import { toJSONValue } from '@boxxy/model';
|
|
3
|
-
import { closeOutLimits } from '../close-out-limits.ts';
|
|
4
|
-
import { measureJsonBytes } from '../measure-json-bytes.ts';
|
|
5
|
-
import type { NoslopReport } from '../types.ts';
|
|
6
|
-
|
|
7
|
-
// Keeps the counts of noslop's result, and the result whole only when it fits the limit: a cut
|
|
8
|
-
// part of the JSON would be no report at all.
|
|
9
|
-
export function buildNoslopSummary(report: NoslopReport): NoslopSummary {
|
|
10
|
-
const whole = toJSONValue(report);
|
|
11
|
-
const reportBytes = measureJsonBytes(whole);
|
|
12
|
-
|
|
13
|
-
return {
|
|
14
|
-
version: report.version,
|
|
15
|
-
reviewID: report.reviewID,
|
|
16
|
-
head: report.head,
|
|
17
|
-
counts: {
|
|
18
|
-
findings: report.findings.length,
|
|
19
|
-
failedCalls: report.failedCalls.length,
|
|
20
|
-
fixChecks: report.fixChecks?.length ?? 0,
|
|
21
|
-
},
|
|
22
|
-
reportBytes,
|
|
23
|
-
report: reportBytes <= closeOutLimits.noslopReportBytes ? whole : null,
|
|
24
|
-
};
|
|
25
|
-
}
|
|
@@ -1,13 +0,0 @@
|
|
|
1
|
-
import { brandSchemas } from '@boxxy/model';
|
|
2
|
-
import { z } from 'zod';
|
|
3
|
-
|
|
4
|
-
// The fields of `noslop review --json --report json` that the summary reads. Every other field passes
|
|
5
|
-
// through unread, and a list's entries are not checked, so a newer noslop still parses.
|
|
6
|
-
export const noslopReportSchema = z.looseObject({
|
|
7
|
-
version: z.number().int().positive(),
|
|
8
|
-
reviewID: z.string().min(1),
|
|
9
|
-
head: brandSchemas.commitSha,
|
|
10
|
-
findings: z.array(z.unknown()),
|
|
11
|
-
failedCalls: z.array(z.unknown()),
|
|
12
|
-
fixChecks: z.array(z.unknown()).exactOptional(),
|
|
13
|
-
});
|
package/src/phases/review.ts
DELETED
|
@@ -1,50 +0,0 @@
|
|
|
1
|
-
import type { PhaseInputContext, ReviewCloseOut, ReviewInput } from '@boxxy/model';
|
|
2
|
-
import { brandSchemas, definePhase, reviewCloseOutSchema, toJSONValue } from '@boxxy/model';
|
|
3
|
-
import type { z } from 'zod';
|
|
4
|
-
import { buildSchemaParser } from '../build-schema-parser.ts';
|
|
5
|
-
import { buildNoslopSummary } from '../noslop/build-noslop-summary.ts';
|
|
6
|
-
import { noslopReportSchema } from '../noslop/noslop-report-schema.ts';
|
|
7
|
-
import { buildCriterionTexts } from '../utils/build-criterion-texts.ts';
|
|
8
|
-
import { findLatestDesign } from '../utils/find-latest-design.ts';
|
|
9
|
-
import { findPullRequestLink } from '../utils/find-pull-request-link.ts';
|
|
10
|
-
|
|
11
|
-
// The session sends noslop's whole result; the stored close-out holds its summary instead.
|
|
12
|
-
const reviewBodySchema = reviewCloseOutSchema
|
|
13
|
-
.omit({ frictionDropped: true })
|
|
14
|
-
.extend({ noslop: noslopReportSchema });
|
|
15
|
-
|
|
16
|
-
export const review = definePhase({
|
|
17
|
-
name: brandSchemas.phaseName.parse('review'),
|
|
18
|
-
version: 1,
|
|
19
|
-
worker: brandSchemas.workerKindName.parse('atc.session'),
|
|
20
|
-
outcomes: ['passed', 'changes-requested'],
|
|
21
|
-
needsAcceptance: true,
|
|
22
|
-
input: (ctx) => toJSONValue(buildReviewInput(ctx)),
|
|
23
|
-
closeOut: buildSchemaParser({
|
|
24
|
-
schema: reviewBodySchema,
|
|
25
|
-
toBody: (raw: z.output<typeof reviewBodySchema>, frictionDropped): ReviewCloseOut => ({
|
|
26
|
-
...raw,
|
|
27
|
-
noslop: buildNoslopSummary(raw.noslop),
|
|
28
|
-
frictionDropped,
|
|
29
|
-
}),
|
|
30
|
-
check: (closeOut) =>
|
|
31
|
-
closeOut.verdict.kind === 'changes-requested' && closeOut.verdict.findings.length === 0
|
|
32
|
-
? [{ kind: 'phase-rule', message: 'changes-requested needs at least one finding' }]
|
|
33
|
-
: [],
|
|
34
|
-
}),
|
|
35
|
-
settle: (input) => ({
|
|
36
|
-
state: 'succeeded',
|
|
37
|
-
failure: null,
|
|
38
|
-
outcome: input.closeOut.verdict.kind,
|
|
39
|
-
effects: input.closeOut.friction.map((friction) => ({ kind: 'record-proposal', friction })),
|
|
40
|
-
frictionDropped: input.closeOut.frictionDropped,
|
|
41
|
-
}),
|
|
42
|
-
});
|
|
43
|
-
|
|
44
|
-
function buildReviewInput(ctx: PhaseInputContext): ReviewInput {
|
|
45
|
-
return {
|
|
46
|
-
acceptance: buildCriterionTexts(ctx.item),
|
|
47
|
-
design: findLatestDesign(ctx.history),
|
|
48
|
-
pullRequest: findPullRequestLink(ctx.pullRequests),
|
|
49
|
-
};
|
|
50
|
-
}
|
|
@@ -1,30 +0,0 @@
|
|
|
1
|
-
import { brandSchemas } from '@boxxy/model';
|
|
2
|
-
import { faker } from '@faker-js/faker';
|
|
3
|
-
import type { NoslopReport } from '../types.ts';
|
|
4
|
-
|
|
5
|
-
// A review result as noslop's schema version 7 prints it, with no findings.
|
|
6
|
-
export function buildMockNoslopReport(overrides: Partial<NoslopReport> = {}): NoslopReport {
|
|
7
|
-
return {
|
|
8
|
-
version: 7,
|
|
9
|
-
reviewID: faker.string.uuid(),
|
|
10
|
-
repo: `${faker.word.noun()}/${faker.word.noun()}`,
|
|
11
|
-
pullRequest: null,
|
|
12
|
-
base: faker.git.commitSha(),
|
|
13
|
-
head: brandSchemas.commitSha.parse(faker.git.commitSha()),
|
|
14
|
-
title: faker.lorem.sentence(),
|
|
15
|
-
depth: 'standard',
|
|
16
|
-
special: null,
|
|
17
|
-
riskClasses: [],
|
|
18
|
-
lenses: [],
|
|
19
|
-
calls: [],
|
|
20
|
-
findings: [],
|
|
21
|
-
dropped: [],
|
|
22
|
-
failedCalls: [],
|
|
23
|
-
notReviewed: [],
|
|
24
|
-
notReviewedByLens: [],
|
|
25
|
-
configHash: faker.string.hexadecimal({ length: 64, casing: 'lower', prefix: '' }),
|
|
26
|
-
startedAt: '2026-11-02T10:00:00.000Z',
|
|
27
|
-
endedAt: '2026-11-02T10:04:00.000Z',
|
|
28
|
-
...overrides,
|
|
29
|
-
};
|
|
30
|
-
}
|