@humanbased/crosscheck 1.2.0-beta.78 → 1.2.0-beta.80
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 +1 -1
- package/crosscheck.config.example.yml +1 -1
- package/dist/__tests__/repository-guidance.test.js +1 -1
- package/dist/__tests__/repository-guidance.test.js.map +1 -1
- package/dist/__tests__/review-models.test.js +5 -2
- package/dist/__tests__/review-models.test.js.map +1 -1
- package/dist/__tests__/review-strategy.test.d.ts +2 -0
- package/dist/__tests__/review-strategy.test.d.ts.map +1 -0
- package/dist/__tests__/review-strategy.test.js +195 -0
- package/dist/__tests__/review-strategy.test.js.map +1 -0
- package/dist/config/review-model-tiers.json +3 -3
- package/dist/config/review-strategy.json +202 -0
- package/dist/lib/review-models.d.ts +15 -2
- package/dist/lib/review-models.d.ts.map +1 -1
- package/dist/lib/review-models.js +19 -6
- package/dist/lib/review-models.js.map +1 -1
- package/dist/lib/review-strategy.d.ts +62 -0
- package/dist/lib/review-strategy.d.ts.map +1 -0
- package/dist/lib/review-strategy.js +235 -0
- package/dist/lib/review-strategy.js.map +1 -0
- package/docs/dynamic-thoroughness.md +286 -0
- package/get-started.md +1 -1
- package/package.json +4 -3
|
@@ -14,25 +14,38 @@ const rawReviewModelTierConfig = require('../config/review-model-tiers.json');
|
|
|
14
14
|
const reviewModelTierConfig = ReviewModelTierConfigSchema.parse(rawReviewModelTierConfig);
|
|
15
15
|
export const CLAUDE_TIER_MODELS = reviewModelTierConfig.claude;
|
|
16
16
|
export const CODEX_TIER_MODELS_API = reviewModelTierConfig.codex_api;
|
|
17
|
-
|
|
17
|
+
/**
|
|
18
|
+
* The tier in force for one call. Under `quality.mode: 'smart'` the per-PR
|
|
19
|
+
* strategy wins; otherwise the single configured tier applies. Falls back to the
|
|
20
|
+
* configured tier whenever no strategy resolved — one-shot commands, or a PR
|
|
21
|
+
* whose file list could not be read.
|
|
22
|
+
*/
|
|
23
|
+
export function effectiveTier(quality, strategy) {
|
|
24
|
+
if (quality.mode === 'smart' && strategy?.tier)
|
|
25
|
+
return strategy.tier;
|
|
26
|
+
return quality.tier;
|
|
27
|
+
}
|
|
28
|
+
export function resolveClaudeModel(quality, vendor, strategy) {
|
|
18
29
|
// An explicit vendors.claude.model wins over the tier mapping. The claude CLI
|
|
19
30
|
// accepts --model under both subscription and api-key auth, so we honor it
|
|
20
31
|
// regardless of auth instead of silently dropping it.
|
|
21
32
|
if (vendor?.model)
|
|
22
33
|
return vendor.model;
|
|
23
|
-
return CLAUDE_TIER_MODELS[quality
|
|
34
|
+
return CLAUDE_TIER_MODELS[effectiveTier(quality, strategy)] ?? CLAUDE_TIER_MODELS.balanced;
|
|
24
35
|
}
|
|
25
|
-
export function resolveCodexModel(quality, vendor) {
|
|
36
|
+
export function resolveCodexModel(quality, vendor, strategy) {
|
|
37
|
+
const tier = effectiveTier(quality, strategy);
|
|
26
38
|
// An explicitly configured model wins under either auth — the codex CLI
|
|
27
39
|
// accepts -c model= with subscription (ChatGPT) auth too. Only the built-in
|
|
28
40
|
// tier mapping stays api-key-only: with nothing configured, subscription
|
|
29
|
-
// users keep the CLI's own default model.
|
|
30
|
-
|
|
41
|
+
// users keep the CLI's own default model, because the gpt-5.6-* IDs need
|
|
42
|
+
// codex >= 0.147.0 and would 400 on an older CLI.
|
|
43
|
+
const explicit = vendor.model || vendor.model_tiers?.[tier];
|
|
31
44
|
if (explicit)
|
|
32
45
|
return explicit;
|
|
33
46
|
if (vendor.auth !== 'api-key')
|
|
34
47
|
return 'default';
|
|
35
|
-
return CODEX_TIER_MODELS_API[
|
|
48
|
+
return CODEX_TIER_MODELS_API[tier] || CODEX_TIER_MODELS_API.balanced;
|
|
36
49
|
}
|
|
37
50
|
// Derives a display name from the regular claude model ID shape:
|
|
38
51
|
// claude-{family}-{major}[-{minor}][-YYYYMMDD]. New models then render
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"review-models.js","sourceRoot":"","sources":["../../src/lib/review-models.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,aAAa,EAAE,MAAM,QAAQ,CAAA;AACtC,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAA;AAGvB,MAAM,OAAO,GAAG,aAAa,CAAC,MAAM,CAAC,IAAI,CAAC,GAAG,CAAC,CAAA;AAE9C,MAAM,eAAe,GAAG,CAAC,CAAC,MAAM,CAAC;IAC/B,IAAI,EAAE,CAAC,CAAC,MAAM,EAAE;IAChB,QAAQ,EAAE,CAAC,CAAC,MAAM,EAAE;IACpB,QAAQ,EAAE,CAAC,CAAC,MAAM,EAAE;CACrB,CAAC,CAAA;AAEF,MAAM,2BAA2B,GAAG,CAAC,CAAC,MAAM,CAAC;IAC3C,MAAM,EAAE,eAAe;IACvB,SAAS,EAAE,eAAe;CAC3B,CAAC,CAAA;AAEF,MAAM,wBAAwB,GAAY,OAAO,CAAC,mCAAmC,CAAC,CAAA;AACtF,MAAM,qBAAqB,GAAG,2BAA2B,CAAC,KAAK,CAAC,wBAAwB,CAAC,CAAA;AAEzF,MAAM,CAAC,MAAM,kBAAkB,GAA0C,qBAAqB,CAAC,MAAM,CAAA;AACrG,MAAM,CAAC,MAAM,qBAAqB,GAA0C,qBAAqB,CAAC,SAAS,CAAA;AAE3G,MAAM,UAAU,
|
|
1
|
+
{"version":3,"file":"review-models.js","sourceRoot":"","sources":["../../src/lib/review-models.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,62 @@
|
|
|
1
|
+
import type { QualityConfig } from '../config/schema.js';
|
|
2
|
+
export type Tier = QualityConfig['tier'];
|
|
3
|
+
export type Domain = 'frontend' | 'backend';
|
|
4
|
+
export interface PRContext {
|
|
5
|
+
files: string[];
|
|
6
|
+
additions: number;
|
|
7
|
+
deletions: number;
|
|
8
|
+
labels?: string[];
|
|
9
|
+
title?: string;
|
|
10
|
+
baseRef?: string;
|
|
11
|
+
defaultBranch?: string;
|
|
12
|
+
}
|
|
13
|
+
export interface ResolvedStrategy {
|
|
14
|
+
/** Strategy file version, stamped into every comment so a past review stays explicable. */
|
|
15
|
+
version: string;
|
|
16
|
+
classId: string;
|
|
17
|
+
classLabel: string;
|
|
18
|
+
/** Human-readable rationale, quoted verbatim in the PR comment. */
|
|
19
|
+
reason: string;
|
|
20
|
+
/** null = skip this PR entirely. */
|
|
21
|
+
tier: Tier | null;
|
|
22
|
+
effort: string | null;
|
|
23
|
+
steps: string[];
|
|
24
|
+
focus?: string;
|
|
25
|
+
domain: Domain;
|
|
26
|
+
}
|
|
27
|
+
/**
|
|
28
|
+
* Frontend vs backend, by file-extension majority. `mixed` and `unknown` both
|
|
29
|
+
* fall back to backend — the conservative default, because the backend
|
|
30
|
+
* preference list is `measured` while the frontend list is still a hypothesis.
|
|
31
|
+
*/
|
|
32
|
+
export declare function detectDomain(files: string[]): Domain;
|
|
33
|
+
/** Classifies a PR against the strategy's ordered class list. First match wins. */
|
|
34
|
+
export declare function resolveReviewStrategy(pr: PRContext): ResolvedStrategy;
|
|
35
|
+
/** Effort levels a model accepts. Empty array = the model has no effort control. */
|
|
36
|
+
export declare function effortLevelsFor(model: string): string[];
|
|
37
|
+
export declare function isModelBanned(model: string): boolean;
|
|
38
|
+
export declare function strategyModelFor(vendor: string, tier: Tier): string | null;
|
|
39
|
+
export declare function strategyVersion(): string;
|
|
40
|
+
export declare function ladderLimits(): {
|
|
41
|
+
maxRounds: number;
|
|
42
|
+
maxBlocking: number;
|
|
43
|
+
maxWallClockMin: number;
|
|
44
|
+
};
|
|
45
|
+
/**
|
|
46
|
+
* Escalation for round N. Raises effort where the model supports it; where it
|
|
47
|
+
* does not (most open-weight models expose no effort ladder), degrades to a
|
|
48
|
+
* tier promotion instead — `ladder.effort_fallback`.
|
|
49
|
+
*
|
|
50
|
+
* The model never weakens across rounds. Scope narrows instead: a recheck
|
|
51
|
+
* examines the delta plus the open findings, not the whole PR again.
|
|
52
|
+
*/
|
|
53
|
+
export declare function escalate(base: {
|
|
54
|
+
tier: Tier;
|
|
55
|
+
effort: string | null;
|
|
56
|
+
}, round: number, model: string): {
|
|
57
|
+
tier: Tier;
|
|
58
|
+
effort: string | null;
|
|
59
|
+
};
|
|
60
|
+
/** Clamps a requested effort to what the model actually accepts. */
|
|
61
|
+
export declare function clampEffort(model: string, effort: string | null): string | null;
|
|
62
|
+
//# sourceMappingURL=review-strategy.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"review-strategy.d.ts","sourceRoot":"","sources":["../../src/lib/review-strategy.ts"],"names":[],"mappings":"AAcA,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,qBAAqB,CAAA;AAIxD,MAAM,MAAM,IAAI,GAAG,aAAa,CAAC,MAAM,CAAC,CAAA;AACxC,MAAM,MAAM,MAAM,GAAG,UAAU,GAAG,SAAS,CAAA;AA2E3C,MAAM,WAAW,SAAS;IACxB,KAAK,EAAE,MAAM,EAAE,CAAA;IACf,SAAS,EAAE,MAAM,CAAA;IACjB,SAAS,EAAE,MAAM,CAAA;IACjB,MAAM,CAAC,EAAE,MAAM,EAAE,CAAA;IACjB,KAAK,CAAC,EAAE,MAAM,CAAA;IACd,OAAO,CAAC,EAAE,MAAM,CAAA;IAChB,aAAa,CAAC,EAAE,MAAM,CAAA;CACvB;AAED,MAAM,WAAW,gBAAgB;IAC/B,2FAA2F;IAC3F,OAAO,EAAE,MAAM,CAAA;IACf,OAAO,EAAE,MAAM,CAAA;IACf,UAAU,EAAE,MAAM,CAAA;IAClB,mEAAmE;IACnE,MAAM,EAAE,MAAM,CAAA;IACd,oCAAoC;IACpC,IAAI,EAAE,IAAI,GAAG,IAAI,CAAA;IACjB,MAAM,EAAE,MAAM,GAAG,IAAI,CAAA;IACrB,KAAK,EAAE,MAAM,EAAE,CAAA;IACf,KAAK,CAAC,EAAE,MAAM,CAAA;IACd,MAAM,EAAE,MAAM,CAAA;CACf;AAUD;;;;GAIG;AACH,wBAAgB,YAAY,CAAC,KAAK,EAAE,MAAM,EAAE,GAAG,MAAM,CAIpD;AAoDD,mFAAmF;AACnF,wBAAgB,qBAAqB,CAAC,EAAE,EAAE,SAAS,GAAG,gBAAgB,CAerE;AAED,oFAAoF;AACpF,wBAAgB,eAAe,CAAC,KAAK,EAAE,MAAM,GAAG,MAAM,EAAE,CAEvD;AAED,wBAAgB,aAAa,CAAC,KAAK,EAAE,MAAM,GAAG,OAAO,CAEpD;AAED,wBAAgB,gBAAgB,CAAC,MAAM,EAAE,MAAM,EAAE,IAAI,EAAE,IAAI,GAAG,MAAM,GAAG,IAAI,CAE1E;AAED,wBAAgB,eAAe,IAAI,MAAM,CAExC;AAED,wBAAgB,YAAY,IAAI;IAAE,SAAS,EAAE,MAAM,CAAC;IAAC,WAAW,EAAE,MAAM,CAAC;IAAC,eAAe,EAAE,MAAM,CAAA;CAAE,CAMlG;AAED;;;;;;;GAOG;AACH,wBAAgB,QAAQ,CACtB,IAAI,EAAE;IAAE,IAAI,EAAE,IAAI,CAAC;IAAC,MAAM,EAAE,MAAM,GAAG,IAAI,CAAA;CAAE,EAC3C,KAAK,EAAE,MAAM,EACb,KAAK,EAAE,MAAM,GACZ;IAAE,IAAI,EAAE,IAAI,CAAC;IAAC,MAAM,EAAE,MAAM,GAAG,IAAI,CAAA;CAAE,CAmBvC;AAED,oEAAoE;AACpE,wBAAgB,WAAW,CAAC,KAAK,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,GAAG,IAAI,GAAG,MAAM,GAAG,IAAI,CAa/E"}
|
|
@@ -0,0 +1,235 @@
|
|
|
1
|
+
// Resolves the per-PR review strategy: which class a PR falls into, which tier
|
|
2
|
+
// and effort that class earns, and which steps run. Reads the versioned policy
|
|
3
|
+
// in config/review-strategy.json so the routing decision and the explanation
|
|
4
|
+
// posted on the PR come from one place and cannot drift apart.
|
|
5
|
+
//
|
|
6
|
+
// Two rules constrain what classification may conclude (see
|
|
7
|
+
// docs/dynamic-thoroughness.md §6.8):
|
|
8
|
+
// - it may set a FLOOR, or promote on CONSEQUENCE (a security path is
|
|
9
|
+
// reviewed thoroughly because a miss there is expensive);
|
|
10
|
+
// - it may NOT predict that a PR will be hard. Static features do not support
|
|
11
|
+
// that inference — difficulty is discovered by reviewing, so escalation is
|
|
12
|
+
// driven by measured non-convergence instead (see escalate()).
|
|
13
|
+
import { createRequire } from 'module';
|
|
14
|
+
import { z } from 'zod';
|
|
15
|
+
const require = createRequire(import.meta.url);
|
|
16
|
+
/** Ordered effort ladder. Index-based so escalation is a step, not a lookup. */
|
|
17
|
+
const EFFORT_LADDER = ['none', 'low', 'medium', 'high', 'xhigh', 'max'];
|
|
18
|
+
const MatchSchema = z.object({
|
|
19
|
+
all_files_match: z.array(z.string()).optional(),
|
|
20
|
+
any_path_matches: z.array(z.string()).optional(),
|
|
21
|
+
or_labels: z.array(z.string()).optional(),
|
|
22
|
+
or_hotfix_to_default_branch: z.boolean().optional(),
|
|
23
|
+
additions_max: z.number().optional(),
|
|
24
|
+
requires_deletions: z.boolean().optional(),
|
|
25
|
+
doc_fraction_min: z.number().optional(),
|
|
26
|
+
config_fraction_min: z.number().optional(),
|
|
27
|
+
source_files_max: z.number().optional(),
|
|
28
|
+
files_max: z.number().optional(),
|
|
29
|
+
src_churn_max: z.number().optional(),
|
|
30
|
+
});
|
|
31
|
+
const PRClassSchema = z.object({
|
|
32
|
+
id: z.string(),
|
|
33
|
+
label: z.string(),
|
|
34
|
+
match: MatchSchema,
|
|
35
|
+
tier: z.enum(['fast', 'balanced', 'thorough']).nullable(),
|
|
36
|
+
// Not z.string(): a typo ("higgh") would validate, then read as -1 in
|
|
37
|
+
// clampEffort and silently drop the whole class to the model's lowest level.
|
|
38
|
+
effort: z.enum(EFFORT_LADDER).nullable(),
|
|
39
|
+
steps: z.array(z.string()),
|
|
40
|
+
focus: z.string().optional(),
|
|
41
|
+
reason: z.string(),
|
|
42
|
+
});
|
|
43
|
+
const StrategySchema = z.object({
|
|
44
|
+
version: z.string(),
|
|
45
|
+
updated: z.string(),
|
|
46
|
+
models: z.record(z.string(), z.object({
|
|
47
|
+
vendor: z.string(),
|
|
48
|
+
effort_levels: z.array(z.string()),
|
|
49
|
+
}).passthrough()),
|
|
50
|
+
banned_models: z.array(z.object({ model: z.string(), reason: z.string() })).default([]),
|
|
51
|
+
vendors: z.record(z.string(), z.object({
|
|
52
|
+
tiers: z.record(z.string(), z.string()),
|
|
53
|
+
}).passthrough()),
|
|
54
|
+
domains: z.record(z.string(), z.unknown()),
|
|
55
|
+
// .min(1): resolveReviewStrategy falls back to the last entry, which is
|
|
56
|
+
// undefined — and throws on `cls.id` — for an empty list.
|
|
57
|
+
pr_classes: z.array(PRClassSchema).min(1),
|
|
58
|
+
ladder: z.object({
|
|
59
|
+
max_blocking_findings: z.number(),
|
|
60
|
+
max_rounds: z.number(),
|
|
61
|
+
max_wall_clock_min: z.number(),
|
|
62
|
+
effort_fallback: z.string(),
|
|
63
|
+
}).passthrough(),
|
|
64
|
+
}).passthrough();
|
|
65
|
+
const raw = require('../config/review-strategy.json');
|
|
66
|
+
const STRATEGY = StrategySchema.parse(raw);
|
|
67
|
+
const TIER_LADDER = ['fast', 'balanced', 'thorough'];
|
|
68
|
+
/** Position on EFFORT_LADDER; -1 for null or an unrecognised level. */
|
|
69
|
+
function effortIndex(effort) {
|
|
70
|
+
return effort === null ? -1 : EFFORT_LADDER.indexOf(effort);
|
|
71
|
+
}
|
|
72
|
+
const FRONTEND_EXT = /\.(tsx|jsx|vue|svelte|css|scss|less|html)$/i;
|
|
73
|
+
const BACKEND_EXT = /\.(py|go|rs|java|rb|php|sql|ts)$/i;
|
|
74
|
+
const DOC_EXT = /\.(md|mdx|rst|adoc)$/i;
|
|
75
|
+
const CONFIG_EXT = /(\.(json|ya?ml|toml|ini|lock|txt|csv|svg|png|jpe?g)$|^\.github\/)/i;
|
|
76
|
+
// Anchored on path segments and extensions. Unanchored, `spec` matched
|
|
77
|
+
// src/lib/pr-spec.ts and `test` matched any latest.ts, routing ordinary source
|
|
78
|
+
// PRs to test_only with a test-focused prompt.
|
|
79
|
+
const TEST_PATH = /((^|\/)(__tests__|tests?|e2e|fixtures?)(\/|$)|\.(test|spec)\.)/i;
|
|
80
|
+
const GENERATED = /((^|\/)(dist|build|node_modules)\/|\.(lock|sum)$|package-lock|pnpm-lock|yarn\.lock|\.(pb|_pb2)\.[a-z]+$|(^|\/)generated\/)/i;
|
|
81
|
+
function srcChurnFraction(files) {
|
|
82
|
+
if (files.length === 0)
|
|
83
|
+
return { doc: 0, config: 0, source: 0 };
|
|
84
|
+
const doc = files.filter(f => DOC_EXT.test(f)).length / files.length;
|
|
85
|
+
const config = files.filter(f => CONFIG_EXT.test(f)).length / files.length;
|
|
86
|
+
const source = files.filter(f => !DOC_EXT.test(f) && !CONFIG_EXT.test(f) && !GENERATED.test(f) && !TEST_PATH.test(f)).length;
|
|
87
|
+
return { doc, config, source };
|
|
88
|
+
}
|
|
89
|
+
/**
|
|
90
|
+
* Frontend vs backend, by file-extension majority. `mixed` and `unknown` both
|
|
91
|
+
* fall back to backend — the conservative default, because the backend
|
|
92
|
+
* preference list is `measured` while the frontend list is still a hypothesis.
|
|
93
|
+
*/
|
|
94
|
+
export function detectDomain(files) {
|
|
95
|
+
const fe = files.filter(f => FRONTEND_EXT.test(f)).length;
|
|
96
|
+
const be = files.filter(f => BACKEND_EXT.test(f)).length;
|
|
97
|
+
return fe > be ? 'frontend' : 'backend';
|
|
98
|
+
}
|
|
99
|
+
function matches(cls, pr) {
|
|
100
|
+
const m = cls.match;
|
|
101
|
+
const { files, additions, deletions } = pr;
|
|
102
|
+
// An empty match object is the fallthrough class.
|
|
103
|
+
if (Object.keys(m).length === 0)
|
|
104
|
+
return true;
|
|
105
|
+
// Never classify an empty file list as anything but the fallthrough.
|
|
106
|
+
if (files.length === 0)
|
|
107
|
+
return false;
|
|
108
|
+
if (m.all_files_match) {
|
|
109
|
+
const res = m.all_files_match.map(p => new RegExp(p, 'i'));
|
|
110
|
+
if (!files.every(f => res.some(r => r.test(f))))
|
|
111
|
+
return false;
|
|
112
|
+
}
|
|
113
|
+
// `any_path_matches` is an OR-group with labels and the hotfix rule: any one
|
|
114
|
+
// of the three promotes. Only evaluated when the class declares them.
|
|
115
|
+
if (m.any_path_matches || m.or_labels || m.or_hotfix_to_default_branch) {
|
|
116
|
+
const pathHit = m.any_path_matches
|
|
117
|
+
? files.some(f => m.any_path_matches.some(p => new RegExp(p, 'i').test(f)))
|
|
118
|
+
: false;
|
|
119
|
+
const labelHit = m.or_labels ? (pr.labels ?? []).some(l => m.or_labels.includes(l)) : false;
|
|
120
|
+
const hotfixHit = m.or_hotfix_to_default_branch === true
|
|
121
|
+
&& /^hotfix/i.test(pr.title ?? '')
|
|
122
|
+
&& pr.baseRef !== undefined
|
|
123
|
+
&& pr.baseRef === pr.defaultBranch;
|
|
124
|
+
if (!pathHit && !labelHit && !hotfixHit)
|
|
125
|
+
return false;
|
|
126
|
+
}
|
|
127
|
+
if (m.additions_max !== undefined && additions > m.additions_max)
|
|
128
|
+
return false;
|
|
129
|
+
if (m.requires_deletions === true && deletions <= 0)
|
|
130
|
+
return false;
|
|
131
|
+
const frac = srcChurnFraction(files);
|
|
132
|
+
if (m.doc_fraction_min !== undefined && frac.doc < m.doc_fraction_min)
|
|
133
|
+
return false;
|
|
134
|
+
if (m.config_fraction_min !== undefined && frac.config < m.config_fraction_min)
|
|
135
|
+
return false;
|
|
136
|
+
if (m.source_files_max !== undefined && frac.source > m.source_files_max)
|
|
137
|
+
return false;
|
|
138
|
+
if (m.files_max !== undefined && files.length > m.files_max)
|
|
139
|
+
return false;
|
|
140
|
+
if (m.src_churn_max !== undefined) {
|
|
141
|
+
const srcFiles = files.filter(f => !GENERATED.test(f) && !CONFIG_EXT.test(f));
|
|
142
|
+
// Churn is only attributable per-file with a full diff; approximate with the
|
|
143
|
+
// PR total when the change is entirely source, which is the case that matters.
|
|
144
|
+
// Fails CLOSED when churn cannot be attributed: falling back to 0 passed
|
|
145
|
+
// every cap, so one stray config file routed a 5,000-line change to the
|
|
146
|
+
// core runner into `trivial` — fast tier, no recheck.
|
|
147
|
+
const churn = srcFiles.length === files.length ? additions + deletions : Infinity;
|
|
148
|
+
if (churn > m.src_churn_max)
|
|
149
|
+
return false;
|
|
150
|
+
}
|
|
151
|
+
return true;
|
|
152
|
+
}
|
|
153
|
+
/** Classifies a PR against the strategy's ordered class list. First match wins. */
|
|
154
|
+
export function resolveReviewStrategy(pr) {
|
|
155
|
+
const cls = STRATEGY.pr_classes.find(c => matches(c, pr))
|
|
156
|
+
?? STRATEGY.pr_classes[STRATEGY.pr_classes.length - 1];
|
|
157
|
+
return {
|
|
158
|
+
version: STRATEGY.version,
|
|
159
|
+
classId: cls.id,
|
|
160
|
+
classLabel: cls.label,
|
|
161
|
+
reason: cls.reason,
|
|
162
|
+
tier: cls.tier,
|
|
163
|
+
effort: cls.effort,
|
|
164
|
+
steps: cls.steps,
|
|
165
|
+
...(cls.focus !== undefined && { focus: cls.focus }),
|
|
166
|
+
domain: detectDomain(pr.files),
|
|
167
|
+
};
|
|
168
|
+
}
|
|
169
|
+
/** Effort levels a model accepts. Empty array = the model has no effort control. */
|
|
170
|
+
export function effortLevelsFor(model) {
|
|
171
|
+
return STRATEGY.models[model]?.effort_levels ?? [];
|
|
172
|
+
}
|
|
173
|
+
export function isModelBanned(model) {
|
|
174
|
+
return STRATEGY.banned_models.some(b => b.model === model);
|
|
175
|
+
}
|
|
176
|
+
export function strategyModelFor(vendor, tier) {
|
|
177
|
+
return STRATEGY.vendors[vendor]?.tiers[tier] ?? null;
|
|
178
|
+
}
|
|
179
|
+
export function strategyVersion() {
|
|
180
|
+
return STRATEGY.version;
|
|
181
|
+
}
|
|
182
|
+
export function ladderLimits() {
|
|
183
|
+
return {
|
|
184
|
+
maxRounds: STRATEGY.ladder.max_rounds,
|
|
185
|
+
maxBlocking: STRATEGY.ladder.max_blocking_findings,
|
|
186
|
+
maxWallClockMin: STRATEGY.ladder.max_wall_clock_min,
|
|
187
|
+
};
|
|
188
|
+
}
|
|
189
|
+
/**
|
|
190
|
+
* Escalation for round N. Raises effort where the model supports it; where it
|
|
191
|
+
* does not (most open-weight models expose no effort ladder), degrades to a
|
|
192
|
+
* tier promotion instead — `ladder.effort_fallback`.
|
|
193
|
+
*
|
|
194
|
+
* The model never weakens across rounds. Scope narrows instead: a recheck
|
|
195
|
+
* examines the delta plus the open findings, not the whole PR again.
|
|
196
|
+
*/
|
|
197
|
+
export function escalate(base, round, model) {
|
|
198
|
+
if (round <= 1)
|
|
199
|
+
return base;
|
|
200
|
+
// Clamp the target to what the model accepts, then compare it against what the
|
|
201
|
+
// round before already asked for. An exact `levels.includes(target)` test made
|
|
202
|
+
// round 3 miss on any model whose ladder tops out below xhigh (deepseek:
|
|
203
|
+
// none/high/max) and return base.effort — weaker than round 2.
|
|
204
|
+
const target = round >= 3 ? 'xhigh' : 'high';
|
|
205
|
+
const clamped = clampEffort(model, target);
|
|
206
|
+
const previous = round >= 3 ? clampEffort(model, 'high') ?? base.effort : base.effort;
|
|
207
|
+
if (clamped !== null && effortIndex(clamped) > effortIndex(previous)) {
|
|
208
|
+
return { tier: base.tier, effort: clamped };
|
|
209
|
+
}
|
|
210
|
+
// Effort cannot rise on this model — promote a tier instead (the strategy's
|
|
211
|
+
// ladder.effort_fallback), capped at thorough.
|
|
212
|
+
const idx = TIER_LADDER.indexOf(base.tier);
|
|
213
|
+
const promoted = TIER_LADDER[Math.min(idx + 1, TIER_LADDER.length - 1)];
|
|
214
|
+
return { tier: promoted, effort: clamped ?? base.effort };
|
|
215
|
+
}
|
|
216
|
+
/** Clamps a requested effort to what the model actually accepts. */
|
|
217
|
+
export function clampEffort(model, effort) {
|
|
218
|
+
if (effort === null)
|
|
219
|
+
return null;
|
|
220
|
+
const levels = effortLevelsFor(model);
|
|
221
|
+
if (levels.length === 0)
|
|
222
|
+
return null;
|
|
223
|
+
if (levels.includes(effort))
|
|
224
|
+
return effort;
|
|
225
|
+
// Snap down to the nearest supported level rather than failing the call. When
|
|
226
|
+
// the model supports nothing at or below the request, snap UP to its lowest
|
|
227
|
+
// level instead — sending an effort the model rejects fails the call outright.
|
|
228
|
+
const want = EFFORT_LADDER.indexOf(effort);
|
|
229
|
+
for (let i = want; i >= 0; i--) {
|
|
230
|
+
if (levels.includes(EFFORT_LADDER[i]))
|
|
231
|
+
return EFFORT_LADDER[i];
|
|
232
|
+
}
|
|
233
|
+
return levels[0] ?? null;
|
|
234
|
+
}
|
|
235
|
+
//# sourceMappingURL=review-strategy.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"review-strategy.js","sourceRoot":"","sources":["../../src/lib/review-strategy.ts"],"names":[],"mappings":"AAAA,+EAA+E;AAC/E,+EAA+E;AAC/E,6EAA6E;AAC7E,+DAA+D;AAC/D,EAAE;AACF,4DAA4D;AAC5D,sCAAsC;AACtC,wEAAwE;AACxE,8DAA8D;AAC9D,gFAAgF;AAChF,+EAA+E;AAC/E,mEAAmE;AACnE,OAAO,EAAE,aAAa,EAAE,MAAM,QAAQ,CAAA;AACtC,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAA;AAGvB,MAAM,OAAO,GAAG,aAAa,CAAC,MAAM,CAAC,IAAI,CAAC,GAAG,CAAC,CAAA;AAK9C,gFAAgF;AAChF,MAAM,aAAa,GAAG,CAAC,MAAM,EAAE,KAAK,EAAE,QAAQ,EAAE,MAAM,EAAE,OAAO,EAAE,KAAK,CAAU,CAAA;AAEhF,MAAM,WAAW,GAAG,CAAC,CAAC,MAAM,CAAC;IAC3B,eAAe,EAAE,CAAC,CAAC,KAAK,CAAC,CAAC,CAAC,MAAM,EAAE,CAAC,CAAC,QAAQ,EAAE;IAC/C,gBAAgB,EAAE,CAAC,CAAC,KAAK,CAAC,CAAC,CAAC,MAAM,EAAE,CAAC,CAAC,QAAQ,EAAE;IAChD,SAAS,EAAE,CAAC,CAAC,KAAK,CAAC,CAAC,CAAC,MAAM,EAAE,CAAC,CAAC,QAAQ,EAAE;IACzC,2BAA2B,EAAE,CAAC,CAAC,OAAO,EAAE,CAAC,QAAQ,EAAE;IACnD,aAAa,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC,QAAQ,EAAE;IACpC,kBAAkB,EAAE,CAAC,CAAC,OAAO,EAAE,CAAC,QAAQ,EAAE;IAC1C,gBAAgB,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC,QAAQ,EAAE;IACvC,mBAAmB,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC,QAAQ,EAAE;IAC1C,gBAAgB,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC,QAAQ,EAAE;IACvC,SAAS,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC,QAAQ,EAAE;IAChC,aAAa,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC,QAAQ,EAAE;CACrC,CAAC,CAAA;AAEF,MAAM,aAAa,GAAG,CAAC,CAAC,MAAM,CAAC;IAC7B,EAAE,EAAE,CAAC,CAAC,MAAM,EAAE;IACd,KAAK,EAAE,CAAC,CAAC,MAAM,EAAE;IACjB,KAAK,EAAE,WAAW;IAClB,IAAI,EAAE,CAAC,CAAC,IAAI,CAAC,CAAC,MAAM,EAAE,UAAU,EAAE,UAAU,CAAC,CAAC,CAAC,QAAQ,EAAE;IACzD,sEAAsE;IACtE,6EAA6E;IAC7E,MAAM,EAAE,CAAC,CAAC,IAAI,CAAC,aAAa,CAAC,CAAC,QAAQ,EAAE;IACxC,KAAK,EAAE,CAAC,CAAC,KAAK,CAAC,CAAC,CAAC,MAAM,EAAE,CAAC;IAC1B,KAAK,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC,QAAQ,EAAE;IAC5B,MAAM,EAAE,CAAC,CAAC,MAAM,EAAE;CACnB,CAAC,CAAA;AAEF,MAAM,cAAc,GAAG,CAAC,CAAC,MAAM,CAAC;IAC9B,OAAO,EAAE,CAAC,CAAC,MAAM,EAAE;IACnB,OAAO,EAAE,CAAC,CAAC,MAAM,EAAE;IACnB,MAAM,EAAE,CAAC,CAAC,MAAM,CAAC,CAAC,CAAC,MAAM,EAAE,EAAE,CAAC,CAAC,MAAM,CAAC;QACpC,MAAM,EAAE,CAAC,CAAC,MAAM,EAAE;QAClB,aAAa,EAAE,CAAC,CAAC,KAAK,CAAC,CAAC,CAAC,MAAM,EAAE,CAAC;KACnC,CAAC,CAAC,WAAW,EAAE,CAAC;IACjB,aAAa,EAAE,CAAC,CAAC,KAAK,CAAC,CAAC,CAAC,MAAM,CAAC,EAAE,KAAK,EAAE,CAAC,CAAC,MAAM,EAAE,EAAE,MAAM,EAAE,CAAC,CAAC,MAAM,EAAE,EAAE,CAAC,CAAC,CAAC,OAAO,CAAC,EAAE,CAAC;IACvF,OAAO,EAAE,CAAC,CAAC,MAAM,CAAC,CAAC,CAAC,MAAM,EAAE,EAAE,CAAC,CAAC,MAAM,CAAC;QACrC,KAAK,EAAE,CAAC,CAAC,MAAM,CAAC,CAAC,CAAC,MAAM,EAAE,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC;KACxC,CAAC,CAAC,WAAW,EAAE,CAAC;IACjB,OAAO,EAAE,CAAC,CAAC,MAAM,CAAC,CAAC,CAAC,MAAM,EAAE,EAAE,CAAC,CAAC,OAAO,EAAE,CAAC;IAC1C,wEAAwE;IACxE,0DAA0D;IAC1D,UAAU,EAAE,CAAC,CAAC,KAAK,CAAC,aAAa,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC;IACzC,MAAM,EAAE,CAAC,CAAC,MAAM,CAAC;QACf,qBAAqB,EAAE,CAAC,CAAC,MAAM,EAAE;QACjC,UAAU,EAAE,CAAC,CAAC,MAAM,EAAE;QACtB,kBAAkB,EAAE,CAAC,CAAC,MAAM,EAAE;QAC9B,eAAe,EAAE,CAAC,CAAC,MAAM,EAAE;KAC5B,CAAC,CAAC,WAAW,EAAE;CACjB,CAAC,CAAC,WAAW,EAAE,CAAA;AAEhB,MAAM,GAAG,GAAY,OAAO,CAAC,gCAAgC,CAAC,CAAA;AAC9D,MAAM,QAAQ,GAAG,cAAc,CAAC,KAAK,CAAC,GAAG,CAAC,CAAA;AAE1C,MAAM,WAAW,GAAW,CAAC,MAAM,EAAE,UAAU,EAAE,UAAU,CAAC,CAAA;AAE5D,uEAAuE;AACvE,SAAS,WAAW,CAAC,MAAqB;IACxC,OAAO,MAAM,KAAK,IAAI,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,aAAa,CAAC,OAAO,CAAC,MAAwC,CAAC,CAAA;AAC/F,CAAC;AAED,MAAM,YAAY,GAAG,6CAA6C,CAAA;AAClE,MAAM,WAAW,GAAG,mCAAmC,CAAA;AACvD,MAAM,OAAO,GAAG,uBAAuB,CAAA;AACvC,MAAM,UAAU,GAAG,oEAAoE,CAAA;AACvF,uEAAuE;AACvE,+EAA+E;AAC/E,+CAA+C;AAC/C,MAAM,SAAS,GAAG,iEAAiE,CAAA;AACnF,MAAM,SAAS,GAAG,6HAA6H,CAAA;AA2B/I,SAAS,gBAAgB,CAAC,KAAe;IACvC,IAAI,KAAK,CAAC,MAAM,KAAK,CAAC;QAAE,OAAO,EAAE,GAAG,EAAE,CAAC,EAAE,MAAM,EAAE,CAAC,EAAE,MAAM,EAAE,CAAC,EAAE,CAAA;IAC/D,MAAM,GAAG,GAAG,KAAK,CAAC,MAAM,CAAC,CAAC,CAAC,EAAE,CAAC,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC,CAAC,MAAM,GAAG,KAAK,CAAC,MAAM,CAAA;IACpE,MAAM,MAAM,GAAG,KAAK,CAAC,MAAM,CAAC,CAAC,CAAC,EAAE,CAAC,UAAU,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC,CAAC,MAAM,GAAG,KAAK,CAAC,MAAM,CAAA;IAC1E,MAAM,MAAM,GAAG,KAAK,CAAC,MAAM,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC,IAAI,CAAC,UAAU,CAAC,IAAI,CAAC,CAAC,CAAC,IAAI,CAAC,SAAS,CAAC,IAAI,CAAC,CAAC,CAAC,IAAI,CAAC,SAAS,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC,CAAC,MAAM,CAAA;IAC5H,OAAO,EAAE,GAAG,EAAE,MAAM,EAAE,MAAM,EAAE,CAAA;AAChC,CAAC;AAED;;;;GAIG;AACH,MAAM,UAAU,YAAY,CAAC,KAAe;IAC1C,MAAM,EAAE,GAAG,KAAK,CAAC,MAAM,CAAC,CAAC,CAAC,EAAE,CAAC,YAAY,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC,CAAC,MAAM,CAAA;IACzD,MAAM,EAAE,GAAG,KAAK,CAAC,MAAM,CAAC,CAAC,CAAC,EAAE,CAAC,WAAW,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC,CAAC,MAAM,CAAA;IACxD,OAAO,EAAE,GAAG,EAAE,CAAC,CAAC,CAAC,UAAU,CAAC,CAAC,CAAC,SAAS,CAAA;AACzC,CAAC;AAED,SAAS,OAAO,CAAC,GAAkC,EAAE,EAAa;IAChE,MAAM,CAAC,GAAG,GAAG,CAAC,KAAK,CAAA;IACnB,MAAM,EAAE,KAAK,EAAE,SAAS,EAAE,SAAS,EAAE,GAAG,EAAE,CAAA;IAE1C,kDAAkD;IAClD,IAAI,MAAM,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC,MAAM,KAAK,CAAC;QAAE,OAAO,IAAI,CAAA;IAC5C,qEAAqE;IACrE,IAAI,KAAK,CAAC,MAAM,KAAK,CAAC;QAAE,OAAO,KAAK,CAAA;IAEpC,IAAI,CAAC,CAAC,eAAe,EAAE,CAAC;QACtB,MAAM,GAAG,GAAG,CAAC,CAAC,eAAe,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,CAAC,IAAI,MAAM,CAAC,CAAC,EAAE,GAAG,CAAC,CAAC,CAAA;QAC1D,IAAI,CAAC,KAAK,CAAC,KAAK,CAAC,CAAC,CAAC,EAAE,CAAC,GAAG,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC,CAAC;YAAE,OAAO,KAAK,CAAA;IAC/D,CAAC;IAED,6EAA6E;IAC7E,sEAAsE;IACtE,IAAI,CAAC,CAAC,gBAAgB,IAAI,CAAC,CAAC,SAAS,IAAI,CAAC,CAAC,2BAA2B,EAAE,CAAC;QACvE,MAAM,OAAO,GAAG,CAAC,CAAC,gBAAgB;YAChC,CAAC,CAAC,KAAK,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,gBAAiB,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,CAAC,IAAI,MAAM,CAAC,CAAC,EAAE,GAAG,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC,CAAC;YAC5E,CAAC,CAAC,KAAK,CAAA;QACT,MAAM,QAAQ,GAAG,CAAC,CAAC,SAAS,CAAC,CAAC,CAAC,CAAC,EAAE,CAAC,MAAM,IAAI,EAAE,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,SAAU,CAAC,QAAQ,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,KAAK,CAAA;QAC5F,MAAM,SAAS,GAAG,CAAC,CAAC,2BAA2B,KAAK,IAAI;eACnD,UAAU,CAAC,IAAI,CAAC,EAAE,CAAC,KAAK,IAAI,EAAE,CAAC;eAC/B,EAAE,CAAC,OAAO,KAAK,SAAS;eACxB,EAAE,CAAC,OAAO,KAAK,EAAE,CAAC,aAAa,CAAA;QACpC,IAAI,CAAC,OAAO,IAAI,CAAC,QAAQ,IAAI,CAAC,SAAS;YAAE,OAAO,KAAK,CAAA;IACvD,CAAC;IAED,IAAI,CAAC,CAAC,aAAa,KAAK,SAAS,IAAI,SAAS,GAAG,CAAC,CAAC,aAAa;QAAE,OAAO,KAAK,CAAA;IAC9E,IAAI,CAAC,CAAC,kBAAkB,KAAK,IAAI,IAAI,SAAS,IAAI,CAAC;QAAE,OAAO,KAAK,CAAA;IAEjE,MAAM,IAAI,GAAG,gBAAgB,CAAC,KAAK,CAAC,CAAA;IACpC,IAAI,CAAC,CAAC,gBAAgB,KAAK,SAAS,IAAI,IAAI,CAAC,GAAG,GAAG,CAAC,CAAC,gBAAgB;QAAE,OAAO,KAAK,CAAA;IACnF,IAAI,CAAC,CAAC,mBAAmB,KAAK,SAAS,IAAI,IAAI,CAAC,MAAM,GAAG,CAAC,CAAC,mBAAmB;QAAE,OAAO,KAAK,CAAA;IAC5F,IAAI,CAAC,CAAC,gBAAgB,KAAK,SAAS,IAAI,IAAI,CAAC,MAAM,GAAG,CAAC,CAAC,gBAAgB;QAAE,OAAO,KAAK,CAAA;IACtF,IAAI,CAAC,CAAC,SAAS,KAAK,SAAS,IAAI,KAAK,CAAC,MAAM,GAAG,CAAC,CAAC,SAAS;QAAE,OAAO,KAAK,CAAA;IACzE,IAAI,CAAC,CAAC,aAAa,KAAK,SAAS,EAAE,CAAC;QAClC,MAAM,QAAQ,GAAG,KAAK,CAAC,MAAM,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,SAAS,CAAC,IAAI,CAAC,CAAC,CAAC,IAAI,CAAC,UAAU,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC,CAAA;QAC7E,6EAA6E;QAC7E,+EAA+E;QAC/E,yEAAyE;QACzE,wEAAwE;QACxE,sDAAsD;QACtD,MAAM,KAAK,GAAG,QAAQ,CAAC,MAAM,KAAK,KAAK,CAAC,MAAM,CAAC,CAAC,CAAC,SAAS,GAAG,SAAS,CAAC,CAAC,CAAC,QAAQ,CAAA;QACjF,IAAI,KAAK,GAAG,CAAC,CAAC,aAAa;YAAE,OAAO,KAAK,CAAA;IAC3C,CAAC;IAED,OAAO,IAAI,CAAA;AACb,CAAC;AAED,mFAAmF;AACnF,MAAM,UAAU,qBAAqB,CAAC,EAAa;IACjD,MAAM,GAAG,GAAG,QAAQ,CAAC,UAAU,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,CAAC,OAAO,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC;WACpD,QAAQ,CAAC,UAAU,CAAC,QAAQ,CAAC,UAAU,CAAC,MAAM,GAAG,CAAC,CAAC,CAAA;IAExD,OAAO;QACL,OAAO,EAAE,QAAQ,CAAC,OAAO;QACzB,OAAO,EAAE,GAAG,CAAC,EAAE;QACf,UAAU,EAAE,GAAG,CAAC,KAAK;QACrB,MAAM,EAAE,GAAG,CAAC,MAAM;QAClB,IAAI,EAAE,GAAG,CAAC,IAAI;QACd,MAAM,EAAE,GAAG,CAAC,MAAM;QAClB,KAAK,EAAE,GAAG,CAAC,KAAK;QAChB,GAAG,CAAC,GAAG,CAAC,KAAK,KAAK,SAAS,IAAI,EAAE,KAAK,EAAE,GAAG,CAAC,KAAK,EAAE,CAAC;QACpD,MAAM,EAAE,YAAY,CAAC,EAAE,CAAC,KAAK,CAAC;KAC/B,CAAA;AACH,CAAC;AAED,oFAAoF;AACpF,MAAM,UAAU,eAAe,CAAC,KAAa;IAC3C,OAAO,QAAQ,CAAC,MAAM,CAAC,KAAK,CAAC,EAAE,aAAa,IAAI,EAAE,CAAA;AACpD,CAAC;AAED,MAAM,UAAU,aAAa,CAAC,KAAa;IACzC,OAAO,QAAQ,CAAC,aAAa,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,KAAK,KAAK,KAAK,CAAC,CAAA;AAC5D,CAAC;AAED,MAAM,UAAU,gBAAgB,CAAC,MAAc,EAAE,IAAU;IACzD,OAAO,QAAQ,CAAC,OAAO,CAAC,MAAM,CAAC,EAAE,KAAK,CAAC,IAAI,CAAC,IAAI,IAAI,CAAA;AACtD,CAAC;AAED,MAAM,UAAU,eAAe;IAC7B,OAAO,QAAQ,CAAC,OAAO,CAAA;AACzB,CAAC;AAED,MAAM,UAAU,YAAY;IAC1B,OAAO;QACL,SAAS,EAAE,QAAQ,CAAC,MAAM,CAAC,UAAU;QACrC,WAAW,EAAE,QAAQ,CAAC,MAAM,CAAC,qBAAqB;QAClD,eAAe,EAAE,QAAQ,CAAC,MAAM,CAAC,kBAAkB;KACpD,CAAA;AACH,CAAC;AAED;;;;;;;GAOG;AACH,MAAM,UAAU,QAAQ,CACtB,IAA2C,EAC3C,KAAa,EACb,KAAa;IAEb,IAAI,KAAK,IAAI,CAAC;QAAE,OAAO,IAAI,CAAA;IAE3B,+EAA+E;IAC/E,+EAA+E;IAC/E,yEAAyE;IACzE,+DAA+D;IAC/D,MAAM,MAAM,GAAG,KAAK,IAAI,CAAC,CAAC,CAAC,CAAC,OAAO,CAAC,CAAC,CAAC,MAAM,CAAA;IAC5C,MAAM,OAAO,GAAG,WAAW,CAAC,KAAK,EAAE,MAAM,CAAC,CAAA;IAC1C,MAAM,QAAQ,GAAG,KAAK,IAAI,CAAC,CAAC,CAAC,CAAC,WAAW,CAAC,KAAK,EAAE,MAAM,CAAC,IAAI,IAAI,CAAC,MAAM,CAAC,CAAC,CAAC,IAAI,CAAC,MAAM,CAAA;IACrF,IAAI,OAAO,KAAK,IAAI,IAAI,WAAW,CAAC,OAAO,CAAC,GAAG,WAAW,CAAC,QAAQ,CAAC,EAAE,CAAC;QACrE,OAAO,EAAE,IAAI,EAAE,IAAI,CAAC,IAAI,EAAE,MAAM,EAAE,OAAO,EAAE,CAAA;IAC7C,CAAC;IAED,4EAA4E;IAC5E,+CAA+C;IAC/C,MAAM,GAAG,GAAG,WAAW,CAAC,OAAO,CAAC,IAAI,CAAC,IAAI,CAAC,CAAA;IAC1C,MAAM,QAAQ,GAAG,WAAW,CAAC,IAAI,CAAC,GAAG,CAAC,GAAG,GAAG,CAAC,EAAE,WAAW,CAAC,MAAM,GAAG,CAAC,CAAC,CAAC,CAAA;IACvE,OAAO,EAAE,IAAI,EAAE,QAAQ,EAAE,MAAM,EAAE,OAAO,IAAI,IAAI,CAAC,MAAM,EAAE,CAAA;AAC3D,CAAC;AAED,oEAAoE;AACpE,MAAM,UAAU,WAAW,CAAC,KAAa,EAAE,MAAqB;IAC9D,IAAI,MAAM,KAAK,IAAI;QAAE,OAAO,IAAI,CAAA;IAChC,MAAM,MAAM,GAAG,eAAe,CAAC,KAAK,CAAC,CAAA;IACrC,IAAI,MAAM,CAAC,MAAM,KAAK,CAAC;QAAE,OAAO,IAAI,CAAA;IACpC,IAAI,MAAM,CAAC,QAAQ,CAAC,MAAM,CAAC;QAAE,OAAO,MAAM,CAAA;IAC1C,8EAA8E;IAC9E,4EAA4E;IAC5E,+EAA+E;IAC/E,MAAM,IAAI,GAAG,aAAa,CAAC,OAAO,CAAC,MAAwC,CAAC,CAAA;IAC5E,KAAK,IAAI,CAAC,GAAG,IAAI,EAAE,CAAC,IAAI,CAAC,EAAE,CAAC,EAAE,EAAE,CAAC;QAC/B,IAAI,MAAM,CAAC,QAAQ,CAAC,aAAa,CAAC,CAAC,CAAC,CAAC;YAAE,OAAO,aAAa,CAAC,CAAC,CAAC,CAAA;IAChE,CAAC;IACD,OAAO,MAAM,CAAC,CAAC,CAAC,IAAI,IAAI,CAAA;AAC1B,CAAC"}
|