fifony 0.1.43 → 0.1.47
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/app/dist/assets/{CommandPalette-M4VAMxCU.js → CommandPalette-CL8p78lG.js} +1 -1
- package/app/dist/assets/{KeyboardShortcutsHelp-DkvPUXQq.js → KeyboardShortcutsHelp-CqEFfGcE.js} +1 -1
- package/app/dist/assets/OnboardingWizard-BmI50ZUv.js +1 -0
- package/app/dist/assets/analytics.lazy-CXGjZabc.js +1 -0
- package/app/dist/assets/{api-CkVfYg_m.js → api-CEr_D4e5.js} +1 -1
- package/app/dist/assets/{createLucideIcon-Dfk_Hxud.js → createLucideIcon-luywpIq4.js} +1 -1
- package/app/dist/assets/index-CEaccpYh.js +96 -0
- package/app/dist/assets/index-CzzWGzux.css +1 -0
- package/app/dist/assets/vendor-uqBx3VSC.js +9 -0
- package/app/dist/index.html +12 -12
- package/app/dist/service-worker.js +15 -5
- package/dist/agent/pty-daemon.js +3 -2
- package/dist/agent/run-local.js +71 -52
- package/dist/{agent-RMQTTUEC.js → agent-DFSFG6DG.js} +18 -12
- package/dist/{analytics-broadcaster-O6YBP66L.js → analytics-broadcaster-O4AE3RUK.js} +21 -14
- package/dist/approve-plan.command-QGQZZXTQ.js +17 -0
- package/dist/{chunk-E2EWEYA4.js → chunk-2PRRKBG6.js} +20 -10
- package/dist/chunk-5AMWD66T.js +38 -0
- package/dist/{chunk-QQQLP3PL.js → chunk-7TXZYZR5.js} +9 -37
- package/dist/chunk-AAVROEQC.js +859 -0
- package/dist/{chunk-ESWHDHH6.js → chunk-AAZKYWOY.js} +4 -4
- package/dist/chunk-EBCSQFPR.js +682 -0
- package/dist/{chunk-BRSR26VK.js → chunk-FH7HUPZX.js} +2 -2
- package/dist/chunk-HOIOVUHI.js +35 -0
- package/dist/{chunk-AILXZ2TD.js → chunk-JRLWLZOD.js} +20 -13
- package/dist/{chunk-YRSH2CLW.js → chunk-K36BWMUV.js} +1741 -1216
- package/dist/chunk-N4KFNX2G.js +370 -0
- package/dist/chunk-PACI3T4I.js +125 -0
- package/dist/{chunk-FJNH3G2Z.js → chunk-PI7Y77R3.js} +38 -663
- package/dist/{chunk-DVU3CXWA.js → chunk-PXTIWKLQ.js} +2 -1
- package/dist/{chunk-SOBLO4YZ.js → chunk-QH6VCTET.js} +316 -127
- package/dist/{chunk-MVTGAKQK.js → chunk-QHISYRXJ.js} +2 -2
- package/dist/{chunk-42AMQAJG.js → chunk-VM5QAYP5.js} +2 -2
- package/dist/cli.js +17 -11
- package/dist/create-issue.command-VAKYRECC.js +24 -0
- package/dist/{fsm-issue-YGGF7SIL.js → fsm-issue-EHTSKMFN.js} +9 -8
- package/dist/fsm-service-7O4AJG2R.js +32 -0
- package/dist/{helpers-L7NYO5XS.js → helpers-ON2S7UEF.js} +2 -2
- package/dist/{issue-log-broadcaster-WZAHISYB.js → issue-log-broadcaster-FZGVEEIX.js} +20 -13
- package/dist/{issues-3QRR7KM6.js → issues-3YNNTB4U.js} +10 -7
- package/dist/{log-analyzer-K7MXQB4T.js → log-analyzer-EIX6R6PP.js} +82 -18
- package/dist/logger-IFLXTQPS.js +11 -0
- package/dist/mcp/server.js +2 -2
- package/dist/merge-workspace.command-T2NIGR4M.js +24 -0
- package/dist/{parallel-executor-6INE6NDO.js → parallel-executor-DWESCNX3.js} +20 -14
- package/dist/queue-workers-V57BYXAY.js +38 -0
- package/dist/replan-issue.command-2GQ3QXCR.js +17 -0
- package/dist/retry-issue.command-GJBUUYDJ.js +17 -0
- package/dist/scheduler-KYILMWLD.js +32 -0
- package/dist/{settings-ZAWDCFP2.js → settings-SOTIS6ZD.js} +32 -12
- package/dist/settings.resource-JMD3JQOS.js +30 -0
- package/dist/{store-M6NCKMZY.js → store-S3NAYZ3S.js} +18 -12
- package/dist/{web-push-AX5IIK3P.js → web-push-QCTLS7EJ.js} +3 -3
- package/dist/websocket-T2Y3BY4B.js +61 -0
- package/dist/{workspace-CJTWFWTJ.js → workspace-OS7GPMCN.js} +7 -6
- package/package.json +8 -5
- package/app/dist/assets/OnboardingWizard-B7V9hoCR.js +0 -1
- package/app/dist/assets/analytics.lazy-zVJdF880.js +0 -1
- package/app/dist/assets/index-BpiCi7Ew.css +0 -1
- package/app/dist/assets/index-D2INW0zc.js +0 -47
- package/app/dist/assets/vendor-BEoYbFV1.js +0 -9
- package/dist/queue-workers-XFZK3TT5.js +0 -32
- package/dist/replan-issue.command-4UCWYHGZ.js +0 -15
- package/dist/scheduler-ZP7GOZDW.js +0 -26
- package/dist/settings.resource-5CW456AZ.js +0 -24
|
@@ -1,12 +1,17 @@
|
|
|
1
1
|
import {
|
|
2
|
-
|
|
3
|
-
|
|
2
|
+
buildReviewRouteKey,
|
|
3
|
+
deriveReviewProfile,
|
|
4
|
+
recommendReviewRouteForIssue
|
|
5
|
+
} from "./chunk-EBCSQFPR.js";
|
|
4
6
|
import {
|
|
5
7
|
renderPrompt
|
|
6
|
-
} from "./chunk-
|
|
8
|
+
} from "./chunk-AAZKYWOY.js";
|
|
7
9
|
import {
|
|
8
10
|
sleep
|
|
9
|
-
} from "./chunk-
|
|
11
|
+
} from "./chunk-VM5QAYP5.js";
|
|
12
|
+
import {
|
|
13
|
+
logger
|
|
14
|
+
} from "./chunk-PXTIWKLQ.js";
|
|
10
15
|
|
|
11
16
|
// src/persistence/dirty-tracker.ts
|
|
12
17
|
var dirtyIssueIds = /* @__PURE__ */ new Set();
|
|
@@ -1346,655 +1351,6 @@ import { existsSync as existsSync6, readFileSync as readFileSync3 } from "fs";
|
|
|
1346
1351
|
import { join as join5 } from "path";
|
|
1347
1352
|
import { homedir as homedir2 } from "os";
|
|
1348
1353
|
|
|
1349
|
-
// src/agents/review-profile.ts
|
|
1350
|
-
var UI_EXTENSIONS = [".jsx", ".tsx", ".css", ".scss", ".vue", ".svelte", ".html"];
|
|
1351
|
-
function collectCandidatePaths(issue) {
|
|
1352
|
-
const planPaths = issue.plan?.suggestedPaths ?? [];
|
|
1353
|
-
const issuePaths = issue.paths ?? [];
|
|
1354
|
-
const contractAreas = issue.plan?.executionContract?.focusAreas ?? [];
|
|
1355
|
-
return [.../* @__PURE__ */ new Set([...issuePaths, ...planPaths, ...contractAreas])];
|
|
1356
|
-
}
|
|
1357
|
-
function collectCandidateText(issue) {
|
|
1358
|
-
return [
|
|
1359
|
-
issue.title,
|
|
1360
|
-
issue.description,
|
|
1361
|
-
issue.issueType,
|
|
1362
|
-
...issue.labels ?? [],
|
|
1363
|
-
...issue.plan?.suggestedPaths ?? []
|
|
1364
|
-
].filter(Boolean).join(" ").toLowerCase();
|
|
1365
|
-
}
|
|
1366
|
-
function hasPath(paths, pattern) {
|
|
1367
|
-
return paths.some((path) => pattern.test(path));
|
|
1368
|
-
}
|
|
1369
|
-
function hasCategory(criteria, category) {
|
|
1370
|
-
return criteria.some((criterion) => criterion.category === category);
|
|
1371
|
-
}
|
|
1372
|
-
function includesAny(text, terms) {
|
|
1373
|
-
return terms.some((term) => text.includes(term));
|
|
1374
|
-
}
|
|
1375
|
-
function buildFocusAreas(paths, fallback) {
|
|
1376
|
-
const combined = [...paths, ...fallback].filter(Boolean);
|
|
1377
|
-
return [...new Set(combined)].slice(0, 6);
|
|
1378
|
-
}
|
|
1379
|
-
function deriveReviewProfile(issue) {
|
|
1380
|
-
const paths = collectCandidatePaths(issue);
|
|
1381
|
-
const text = collectCandidateText(issue);
|
|
1382
|
-
const criteria = issue.plan?.acceptanceCriteria ?? [];
|
|
1383
|
-
const scores = [
|
|
1384
|
-
{ name: "general-quality", score: 1, rationale: ["Fallback profile for broad correctness, regression risk, and code quality review."] },
|
|
1385
|
-
{ name: "ui-polish", score: 0, rationale: [] },
|
|
1386
|
-
{ name: "workflow-fsm", score: 0, rationale: [] },
|
|
1387
|
-
{ name: "integration-safety", score: 0, rationale: [] },
|
|
1388
|
-
{ name: "api-contract", score: 0, rationale: [] },
|
|
1389
|
-
{ name: "security-hardening", score: 0, rationale: [] }
|
|
1390
|
-
];
|
|
1391
|
-
const uiScore = scores.find((entry) => entry.name === "ui-polish");
|
|
1392
|
-
if (paths.some((path) => UI_EXTENSIONS.some((ext) => path.endsWith(ext)))) {
|
|
1393
|
-
uiScore.score += 4;
|
|
1394
|
-
uiScore.rationale.push("Touched frontend files that can regress visual polish, interaction flow, or responsiveness.");
|
|
1395
|
-
}
|
|
1396
|
-
if (hasCategory(criteria, "design") || includesAny(text, ["frontend", "ui", "ux", "drawer", "onboarding", "layout", "mobile"])) {
|
|
1397
|
-
uiScore.score += 3;
|
|
1398
|
-
uiScore.rationale.push("Issue signals UI/UX work that needs stronger product-behavior and visual scrutiny.");
|
|
1399
|
-
}
|
|
1400
|
-
const workflowScore = scores.find((entry) => entry.name === "workflow-fsm");
|
|
1401
|
-
if (hasPath(paths, /src\/persistence\/plugins\/fsm-|src\/commands\/|src\/domains\/issues\.ts|src\/agents\//)) {
|
|
1402
|
-
workflowScore.score += 5;
|
|
1403
|
-
workflowScore.rationale.push("Touched workflow/FSM/orchestration code where lifecycle invariants and retry semantics are fragile.");
|
|
1404
|
-
}
|
|
1405
|
-
if (includesAny(text, ["fsm", "workflow", "queue", "review gate", "lifecycle", "orchestration", "agent"])) {
|
|
1406
|
-
workflowScore.score += 3;
|
|
1407
|
-
workflowScore.rationale.push("Issue description or labels indicate orchestration semantics rather than isolated implementation.");
|
|
1408
|
-
}
|
|
1409
|
-
const integrationScore = scores.find((entry) => entry.name === "integration-safety");
|
|
1410
|
-
if (hasPath(paths, /workspace|merge|push|rebase|git|dirty-tracker|services?|store\.ts/)) {
|
|
1411
|
-
integrationScore.score += 5;
|
|
1412
|
-
integrationScore.rationale.push("Touched integration or git/workspace code where destructive behavior and state drift must be caught.");
|
|
1413
|
-
}
|
|
1414
|
-
if (hasCategory(criteria, "integration") || hasCategory(criteria, "regression")) {
|
|
1415
|
-
integrationScore.score += 2;
|
|
1416
|
-
integrationScore.rationale.push("Acceptance criteria explicitly call out integration or regression guarantees.");
|
|
1417
|
-
}
|
|
1418
|
-
const apiScore = scores.find((entry) => entry.name === "api-contract");
|
|
1419
|
-
if (hasPath(paths, /src\/routes\/|src\/persistence\/resources\/|src\/mcp\//)) {
|
|
1420
|
-
apiScore.score += 4;
|
|
1421
|
-
apiScore.rationale.push("Touched API/resource surface that can drift from contract or persistence schema.");
|
|
1422
|
-
}
|
|
1423
|
-
if (includesAny(text, ["api", "route", "http", "endpoint", "resource", "schema"])) {
|
|
1424
|
-
apiScore.score += 2;
|
|
1425
|
-
apiScore.rationale.push("Issue language implies request/response or schema contract changes.");
|
|
1426
|
-
}
|
|
1427
|
-
const securityScore = scores.find((entry) => entry.name === "security-hardening");
|
|
1428
|
-
if (hasCategory(criteria, "security") || includesAny(text, ["auth", "security", "token", "permission", "secret"])) {
|
|
1429
|
-
securityScore.score += 5;
|
|
1430
|
-
securityScore.rationale.push("Security-sensitive behavior or criteria are present and should be treated as blocking by default.");
|
|
1431
|
-
}
|
|
1432
|
-
if (hasPath(paths, /auth|permission|secret|credential|shell|command-executor/)) {
|
|
1433
|
-
securityScore.score += 3;
|
|
1434
|
-
securityScore.rationale.push("Touched code paths that can introduce auth, privilege, or command-execution risk.");
|
|
1435
|
-
}
|
|
1436
|
-
const ranked = [...scores].sort((a, b) => b.score - a.score);
|
|
1437
|
-
const primary = ranked[0];
|
|
1438
|
-
const secondary = ranked.filter((entry) => entry.name !== primary.name && entry.score >= 3).slice(0, 2).map((entry) => entry.name);
|
|
1439
|
-
const byName = {
|
|
1440
|
-
"general-quality": {
|
|
1441
|
-
focusAreas: buildFocusAreas(paths, ["Correctness under real usage", "Regression risk", "Code quality and maintainability"]),
|
|
1442
|
-
failureModes: [
|
|
1443
|
-
"Partial implementations that look complete but leave core behavior stubbed",
|
|
1444
|
-
"Missing validation, tests, or evidence for blocking criteria",
|
|
1445
|
-
"Code that technically works but introduces obvious maintainability debt"
|
|
1446
|
-
],
|
|
1447
|
-
evidencePriorities: [
|
|
1448
|
-
"Run or inspect the most relevant validation commands",
|
|
1449
|
-
"Trace the dominant code path end to end",
|
|
1450
|
-
"Call out unverified assumptions explicitly instead of hand-waving them"
|
|
1451
|
-
],
|
|
1452
|
-
severityBias: "Bias toward FAIL when behavior is only implied rather than demonstrated."
|
|
1453
|
-
},
|
|
1454
|
-
"ui-polish": {
|
|
1455
|
-
focusAreas: buildFocusAreas(paths, ["Primary interaction flow", "Responsive layout", "Accessibility and clarity of actions"]),
|
|
1456
|
-
failureModes: [
|
|
1457
|
-
"Broken or unintuitive interaction flow, especially onboarding, drawers, and primary actions",
|
|
1458
|
-
"Visual regressions, overflow, spacing collapse, or inaccessible controls",
|
|
1459
|
-
"Interfaces that technically render but feel unfinished or confusing in use"
|
|
1460
|
-
],
|
|
1461
|
-
evidencePriorities: [
|
|
1462
|
-
"Navigate the affected UI and describe what users can and cannot do",
|
|
1463
|
-
"Verify mobile-width and edge-state behavior, not just the happy path",
|
|
1464
|
-
"Use Playwright evidence when visible behavior is part of the contract"
|
|
1465
|
-
],
|
|
1466
|
-
severityBias: "Treat usability breaks and visually misleading states as blocking defects, not polish nits."
|
|
1467
|
-
},
|
|
1468
|
-
"workflow-fsm": {
|
|
1469
|
-
focusAreas: buildFocusAreas(paths, ["State transitions", "Retry semantics", "Lifecycle invariants", "Counter reset behavior"]),
|
|
1470
|
-
failureModes: [
|
|
1471
|
-
"Illegal transitions that bypass approval, review, or terminal-state rules",
|
|
1472
|
-
"Retry and checkpoint flows that jump to the wrong phase or double-increment counters",
|
|
1473
|
-
"State cleanup/reset bugs that leave stale error, checkpoint, or lifecycle metadata behind"
|
|
1474
|
-
],
|
|
1475
|
-
evidencePriorities: [
|
|
1476
|
-
"Trace the exact state path for the critical scenario, including failure paths",
|
|
1477
|
-
"Verify counters and lifecycle fields are reset or preserved intentionally",
|
|
1478
|
-
"Treat ambiguous transition behavior as a defect until proven safe"
|
|
1479
|
-
],
|
|
1480
|
-
severityBias: "Any lifecycle inconsistency that can misroute an issue or bypass a gate is blocking."
|
|
1481
|
-
},
|
|
1482
|
-
"integration-safety": {
|
|
1483
|
-
focusAreas: buildFocusAreas(paths, ["Git/worktree operations", "Persistence side effects", "Idempotency and cleanup"]),
|
|
1484
|
-
failureModes: [
|
|
1485
|
-
"Destructive workspace behavior that can delete user work or dirty target branches",
|
|
1486
|
-
"Cross-system drift between runtime state, resources, and filesystem artifacts",
|
|
1487
|
-
"Merge/push/service-management flows that work only in the happy path and break under dirty state"
|
|
1488
|
-
],
|
|
1489
|
-
evidencePriorities: [
|
|
1490
|
-
"Verify failure handling, not just success path behavior",
|
|
1491
|
-
"Check idempotency and cleanup paths explicitly",
|
|
1492
|
-
"Call out any command or filesystem side effect that is not safely guarded"
|
|
1493
|
-
],
|
|
1494
|
-
severityBias: "Prefer FAIL when integration code assumes a clean environment or safe side effects without enforcing them."
|
|
1495
|
-
},
|
|
1496
|
-
"api-contract": {
|
|
1497
|
-
focusAreas: buildFocusAreas(paths, ["Route handlers", "Resource schema", "Input/output contract"]),
|
|
1498
|
-
failureModes: [
|
|
1499
|
-
"HTTP/API behavior that no longer matches route or resource contract",
|
|
1500
|
-
"Schema drift between persisted fields, normalization, and route responses",
|
|
1501
|
-
"Missing validation or status-code mismatches that break downstream callers"
|
|
1502
|
-
],
|
|
1503
|
-
evidencePriorities: [
|
|
1504
|
-
"Read the route/resource code and trace request-to-response behavior",
|
|
1505
|
-
"Verify persisted fields, normalization, and response shape stay aligned",
|
|
1506
|
-
"Treat silent contract drift as blocking even if the implementation compiles"
|
|
1507
|
-
],
|
|
1508
|
-
severityBias: "Contract drift is blocking because it breaks automation and downstream clients silently."
|
|
1509
|
-
},
|
|
1510
|
-
"security-hardening": {
|
|
1511
|
-
focusAreas: buildFocusAreas(paths, ["Authorization boundaries", "Secret handling", "Shell/command safety"]),
|
|
1512
|
-
failureModes: [
|
|
1513
|
-
"Authorization bypass, over-broad permissions, or unsafe defaults",
|
|
1514
|
-
"Leaked secrets, credentials, or unsafe command composition",
|
|
1515
|
-
"Security-sensitive criteria marked as effectively optional or unverified"
|
|
1516
|
-
],
|
|
1517
|
-
evidencePriorities: [
|
|
1518
|
-
"Look for privilege escalation and shell/filepath injection opportunities",
|
|
1519
|
-
"Verify security checks with concrete evidence, not inference alone",
|
|
1520
|
-
"Escalate uncertainty instead of allowing a soft PASS on security-sensitive paths"
|
|
1521
|
-
],
|
|
1522
|
-
severityBias: "Security uncertainty should fail closed; do not grant benefit of the doubt."
|
|
1523
|
-
}
|
|
1524
|
-
};
|
|
1525
|
-
return {
|
|
1526
|
-
primary: primary.name,
|
|
1527
|
-
secondary,
|
|
1528
|
-
rationale: primary.rationale.length ? primary.rationale : ["Selected as the highest-risk profile based on touched code and acceptance criteria."],
|
|
1529
|
-
...byName[primary.name]
|
|
1530
|
-
};
|
|
1531
|
-
}
|
|
1532
|
-
|
|
1533
|
-
// src/agents/harness-policy.ts
|
|
1534
|
-
var HIGH_RISK_PROFILES = /* @__PURE__ */ new Set([
|
|
1535
|
-
"workflow-fsm",
|
|
1536
|
-
"integration-safety",
|
|
1537
|
-
"api-contract",
|
|
1538
|
-
"security-hardening"
|
|
1539
|
-
]);
|
|
1540
|
-
var HIGH_CHECKPOINT_PROFILES = /* @__PURE__ */ new Set([
|
|
1541
|
-
"workflow-fsm",
|
|
1542
|
-
"integration-safety",
|
|
1543
|
-
"security-hardening"
|
|
1544
|
-
]);
|
|
1545
|
-
var ROUTE_AFFINITY = {
|
|
1546
|
-
"general-quality": { claude: 2.4, codex: 1.8, gemini: 1.4 },
|
|
1547
|
-
"ui-polish": { claude: 3.2, codex: 1.8, gemini: 1.6 },
|
|
1548
|
-
"workflow-fsm": { codex: 3.1, claude: 2, gemini: 1 },
|
|
1549
|
-
"integration-safety": { codex: 3, claude: 2, gemini: 1 },
|
|
1550
|
-
"api-contract": { codex: 3.1, claude: 1.9, gemini: 1.2 },
|
|
1551
|
-
"security-hardening": { claude: 2.6, codex: 2.6, gemini: 0.8 }
|
|
1552
|
-
};
|
|
1553
|
-
function rate(numerator, denominator) {
|
|
1554
|
-
return denominator > 0 ? numerator / denominator : null;
|
|
1555
|
-
}
|
|
1556
|
-
function isCompletedIssue(issue) {
|
|
1557
|
-
return issue.state === "Approved" || issue.state === "Merged";
|
|
1558
|
-
}
|
|
1559
|
-
function hadReviewRework(issue) {
|
|
1560
|
-
return (issue.previousAttemptSummaries ?? []).some((summary) => summary.phase === "review");
|
|
1561
|
-
}
|
|
1562
|
-
function resolveEffectiveReviewProfile(issue) {
|
|
1563
|
-
return issue.reviewProfile ?? deriveReviewProfile(issue);
|
|
1564
|
-
}
|
|
1565
|
-
function serializeReviewRouteSnapshot(route) {
|
|
1566
|
-
const providerLabel = `${route.provider}${route.model ? `/${route.model}` : ""}`;
|
|
1567
|
-
const effortLabel = route.reasoningEffort ? `[${route.reasoningEffort}]` : "";
|
|
1568
|
-
const overlayLabel = route.overlays?.length ? `overlays:${[...route.overlays].sort().join(",")}` : "";
|
|
1569
|
-
return [providerLabel, effortLabel, overlayLabel].filter(Boolean).join(" | ");
|
|
1570
|
-
}
|
|
1571
|
-
function buildReviewRouteKey(candidate) {
|
|
1572
|
-
return serializeReviewRouteSnapshot({
|
|
1573
|
-
provider: candidate.provider,
|
|
1574
|
-
model: candidate.model,
|
|
1575
|
-
reasoningEffort: candidate.reasoningEffort,
|
|
1576
|
-
overlays: candidate.overlays ?? []
|
|
1577
|
-
});
|
|
1578
|
-
}
|
|
1579
|
-
function applyHarnessModeToPlan(plan, mode) {
|
|
1580
|
-
plan.harnessMode = mode;
|
|
1581
|
-
if (mode !== "contractual") {
|
|
1582
|
-
plan.executionContract.checkpointPolicy = "final_only";
|
|
1583
|
-
} else if (plan.executionContract.checkpointPolicy !== "checkpointed") {
|
|
1584
|
-
plan.executionContract.checkpointPolicy = "final_only";
|
|
1585
|
-
}
|
|
1586
|
-
}
|
|
1587
|
-
function applyCheckpointPolicyToPlan(plan, checkpointPolicy) {
|
|
1588
|
-
plan.executionContract.checkpointPolicy = plan.harnessMode === "contractual" ? checkpointPolicy : "final_only";
|
|
1589
|
-
}
|
|
1590
|
-
function resolveLatestCompletedReviewRun(issue, scope = "final") {
|
|
1591
|
-
const reviewRuns = Array.isArray(issue.reviewRuns) ? issue.reviewRuns : [];
|
|
1592
|
-
const completed = reviewRuns.filter((entry) => entry.status === "completed");
|
|
1593
|
-
const matchingScope = completed.filter((entry) => entry.scope === scope);
|
|
1594
|
-
const pool = matchingScope.length > 0 ? matchingScope : completed;
|
|
1595
|
-
if (pool.length === 0) return null;
|
|
1596
|
-
return [...pool].sort((left, right) => {
|
|
1597
|
-
const leftAt = Date.parse(left.completedAt ?? left.startedAt);
|
|
1598
|
-
const rightAt = Date.parse(right.completedAt ?? right.startedAt);
|
|
1599
|
-
if (!Number.isNaN(leftAt) && !Number.isNaN(rightAt) && leftAt !== rightAt) return rightAt - leftAt;
|
|
1600
|
-
if ((left.planVersion ?? 0) !== (right.planVersion ?? 0)) return (right.planVersion ?? 0) - (left.planVersion ?? 0);
|
|
1601
|
-
return (right.attempt ?? 0) - (left.attempt ?? 0);
|
|
1602
|
-
})[0] ?? null;
|
|
1603
|
-
}
|
|
1604
|
-
function resolveLatestCompletedContractNegotiationRuns(issue) {
|
|
1605
|
-
const runs = Array.isArray(issue.contractNegotiationRuns) ? issue.contractNegotiationRuns : [];
|
|
1606
|
-
const completed = runs.filter((entry) => entry.status === "completed");
|
|
1607
|
-
if (completed.length === 0) return [];
|
|
1608
|
-
const latestPlanVersion = completed.reduce((maxPlanVersion, entry) => Math.max(maxPlanVersion, entry.planVersion ?? 0), 0);
|
|
1609
|
-
return completed.filter((entry) => (entry.planVersion ?? 0) === latestPlanVersion).sort((left, right) => {
|
|
1610
|
-
if ((left.attempt ?? 0) !== (right.attempt ?? 0)) return (left.attempt ?? 0) - (right.attempt ?? 0);
|
|
1611
|
-
const leftAt = Date.parse(left.completedAt ?? left.startedAt);
|
|
1612
|
-
const rightAt = Date.parse(right.completedAt ?? right.startedAt);
|
|
1613
|
-
if (!Number.isNaN(leftAt) && !Number.isNaN(rightAt) && leftAt !== rightAt) return leftAt - rightAt;
|
|
1614
|
-
return left.id.localeCompare(right.id);
|
|
1615
|
-
});
|
|
1616
|
-
}
|
|
1617
|
-
function resolveLatestCompletedScopedReviewRuns(issue, scope) {
|
|
1618
|
-
const reviewRuns = Array.isArray(issue.reviewRuns) ? issue.reviewRuns : [];
|
|
1619
|
-
const completed = reviewRuns.filter((entry) => entry.status === "completed" && entry.scope === scope);
|
|
1620
|
-
if (completed.length === 0) return [];
|
|
1621
|
-
const latestPlanVersion = completed.reduce((maxPlanVersion, entry) => Math.max(maxPlanVersion, entry.planVersion ?? 0), 0);
|
|
1622
|
-
return completed.filter((entry) => (entry.planVersion ?? 0) === latestPlanVersion).sort((left, right) => {
|
|
1623
|
-
if ((left.attempt ?? 0) !== (right.attempt ?? 0)) return (left.attempt ?? 0) - (right.attempt ?? 0);
|
|
1624
|
-
const leftAt = Date.parse(left.completedAt ?? left.startedAt);
|
|
1625
|
-
const rightAt = Date.parse(right.completedAt ?? right.startedAt);
|
|
1626
|
-
if (!Number.isNaN(leftAt) && !Number.isNaN(rightAt) && leftAt !== rightAt) return leftAt - rightAt;
|
|
1627
|
-
return left.id.localeCompare(right.id);
|
|
1628
|
-
});
|
|
1629
|
-
}
|
|
1630
|
-
function computeHarnessModeStats(issues, profileName) {
|
|
1631
|
-
const buckets = {
|
|
1632
|
-
solo: { reviewedIssues: 0, completedReviewedIssues: 0, gatePasses: 0, firstPassPasses: 0, reworkIssues: 0 },
|
|
1633
|
-
standard: { reviewedIssues: 0, completedReviewedIssues: 0, gatePasses: 0, firstPassPasses: 0, reworkIssues: 0 },
|
|
1634
|
-
contractual: { reviewedIssues: 0, completedReviewedIssues: 0, gatePasses: 0, firstPassPasses: 0, reworkIssues: 0 }
|
|
1635
|
-
};
|
|
1636
|
-
for (const issue of issues) {
|
|
1637
|
-
const reviewRun = resolveLatestCompletedReviewRun(issue, "final");
|
|
1638
|
-
if (!reviewRun) continue;
|
|
1639
|
-
const effectiveProfile = reviewRun.reviewProfile ?? resolveEffectiveReviewProfile(issue);
|
|
1640
|
-
if (effectiveProfile.primary !== profileName) continue;
|
|
1641
|
-
const mode = issue.plan?.harnessMode ?? "standard";
|
|
1642
|
-
const bucket = buckets[mode];
|
|
1643
|
-
bucket.reviewedIssues += 1;
|
|
1644
|
-
if (isCompletedIssue(issue)) bucket.completedReviewedIssues += 1;
|
|
1645
|
-
if (reviewRun.blockingVerdict === "PASS") bucket.gatePasses += 1;
|
|
1646
|
-
if ((issue.reviewAttempt ?? 0) <= 1 && !hadReviewRework(issue) && isCompletedIssue(issue)) bucket.firstPassPasses += 1;
|
|
1647
|
-
if (hadReviewRework(issue)) bucket.reworkIssues += 1;
|
|
1648
|
-
}
|
|
1649
|
-
return {
|
|
1650
|
-
solo: {
|
|
1651
|
-
...buckets.solo,
|
|
1652
|
-
gatePassRate: rate(buckets.solo.gatePasses, buckets.solo.reviewedIssues),
|
|
1653
|
-
firstPassPassRate: rate(buckets.solo.firstPassPasses, buckets.solo.completedReviewedIssues),
|
|
1654
|
-
reviewReworkRate: rate(buckets.solo.reworkIssues, buckets.solo.reviewedIssues)
|
|
1655
|
-
},
|
|
1656
|
-
standard: {
|
|
1657
|
-
...buckets.standard,
|
|
1658
|
-
gatePassRate: rate(buckets.standard.gatePasses, buckets.standard.reviewedIssues),
|
|
1659
|
-
firstPassPassRate: rate(buckets.standard.firstPassPasses, buckets.standard.completedReviewedIssues),
|
|
1660
|
-
reviewReworkRate: rate(buckets.standard.reworkIssues, buckets.standard.reviewedIssues)
|
|
1661
|
-
},
|
|
1662
|
-
contractual: {
|
|
1663
|
-
...buckets.contractual,
|
|
1664
|
-
gatePassRate: rate(buckets.contractual.gatePasses, buckets.contractual.reviewedIssues),
|
|
1665
|
-
firstPassPassRate: rate(buckets.contractual.firstPassPasses, buckets.contractual.completedReviewedIssues),
|
|
1666
|
-
reviewReworkRate: rate(buckets.contractual.reworkIssues, buckets.contractual.reviewedIssues)
|
|
1667
|
-
}
|
|
1668
|
-
};
|
|
1669
|
-
}
|
|
1670
|
-
function computeCheckpointPolicyStats(issues, profileName) {
|
|
1671
|
-
const buckets = {
|
|
1672
|
-
final_only: {
|
|
1673
|
-
reviewedIssues: 0,
|
|
1674
|
-
completedReviewedIssues: 0,
|
|
1675
|
-
gatePasses: 0,
|
|
1676
|
-
firstPassPasses: 0,
|
|
1677
|
-
reworkIssues: 0,
|
|
1678
|
-
checkpointFailures: 0,
|
|
1679
|
-
checkpointPasses: 0,
|
|
1680
|
-
checkpointRuns: 0
|
|
1681
|
-
},
|
|
1682
|
-
checkpointed: {
|
|
1683
|
-
reviewedIssues: 0,
|
|
1684
|
-
completedReviewedIssues: 0,
|
|
1685
|
-
gatePasses: 0,
|
|
1686
|
-
firstPassPasses: 0,
|
|
1687
|
-
reworkIssues: 0,
|
|
1688
|
-
checkpointFailures: 0,
|
|
1689
|
-
checkpointPasses: 0,
|
|
1690
|
-
checkpointRuns: 0
|
|
1691
|
-
}
|
|
1692
|
-
};
|
|
1693
|
-
for (const issue of issues) {
|
|
1694
|
-
if ((issue.plan?.harnessMode ?? "standard") !== "contractual") continue;
|
|
1695
|
-
const finalReviewRun = resolveLatestCompletedReviewRun(issue, "final");
|
|
1696
|
-
if (!finalReviewRun) continue;
|
|
1697
|
-
const effectiveProfile = finalReviewRun.reviewProfile ?? resolveEffectiveReviewProfile(issue);
|
|
1698
|
-
if (effectiveProfile.primary !== profileName) continue;
|
|
1699
|
-
const checkpointPolicy = issue.plan?.executionContract?.checkpointPolicy === "checkpointed" ? "checkpointed" : "final_only";
|
|
1700
|
-
const bucket = buckets[checkpointPolicy];
|
|
1701
|
-
bucket.reviewedIssues += 1;
|
|
1702
|
-
if (isCompletedIssue(issue)) bucket.completedReviewedIssues += 1;
|
|
1703
|
-
if (finalReviewRun.blockingVerdict === "PASS") bucket.gatePasses += 1;
|
|
1704
|
-
if ((issue.reviewAttempt ?? 0) <= 1 && !hadReviewRework(issue) && isCompletedIssue(issue)) bucket.firstPassPasses += 1;
|
|
1705
|
-
if (hadReviewRework(issue)) bucket.reworkIssues += 1;
|
|
1706
|
-
if (checkpointPolicy === "checkpointed") {
|
|
1707
|
-
const checkpointRuns = resolveLatestCompletedScopedReviewRuns(issue, "checkpoint");
|
|
1708
|
-
bucket.checkpointRuns += checkpointRuns.length;
|
|
1709
|
-
if (checkpointRuns.some((entry) => entry.blockingVerdict === "FAIL")) bucket.checkpointFailures += 1;
|
|
1710
|
-
if (checkpointRuns.some((entry) => entry.blockingVerdict === "PASS")) bucket.checkpointPasses += 1;
|
|
1711
|
-
}
|
|
1712
|
-
}
|
|
1713
|
-
return {
|
|
1714
|
-
final_only: {
|
|
1715
|
-
...buckets.final_only,
|
|
1716
|
-
gatePassRate: rate(buckets.final_only.gatePasses, buckets.final_only.reviewedIssues),
|
|
1717
|
-
firstPassPassRate: rate(buckets.final_only.firstPassPasses, buckets.final_only.completedReviewedIssues),
|
|
1718
|
-
reviewReworkRate: rate(buckets.final_only.reworkIssues, buckets.final_only.reviewedIssues),
|
|
1719
|
-
checkpointFailureRate: rate(buckets.final_only.checkpointFailures, buckets.final_only.reviewedIssues),
|
|
1720
|
-
checkpointPassRate: rate(buckets.final_only.checkpointPasses, buckets.final_only.reviewedIssues),
|
|
1721
|
-
avgCheckpointRunsPerIssue: rate(buckets.final_only.checkpointRuns, buckets.final_only.reviewedIssues)
|
|
1722
|
-
},
|
|
1723
|
-
checkpointed: {
|
|
1724
|
-
...buckets.checkpointed,
|
|
1725
|
-
gatePassRate: rate(buckets.checkpointed.gatePasses, buckets.checkpointed.reviewedIssues),
|
|
1726
|
-
firstPassPassRate: rate(buckets.checkpointed.firstPassPasses, buckets.checkpointed.completedReviewedIssues),
|
|
1727
|
-
reviewReworkRate: rate(buckets.checkpointed.reworkIssues, buckets.checkpointed.reviewedIssues),
|
|
1728
|
-
checkpointFailureRate: rate(buckets.checkpointed.checkpointFailures, buckets.checkpointed.reviewedIssues),
|
|
1729
|
-
checkpointPassRate: rate(buckets.checkpointed.checkpointPasses, buckets.checkpointed.reviewedIssues),
|
|
1730
|
-
avgCheckpointRunsPerIssue: rate(buckets.checkpointed.checkpointRuns, buckets.checkpointed.reviewedIssues)
|
|
1731
|
-
}
|
|
1732
|
-
};
|
|
1733
|
-
}
|
|
1734
|
-
function computeContractNegotiationStats(issues, profileName) {
|
|
1735
|
-
const bucket = {
|
|
1736
|
-
negotiatedIssues: 0,
|
|
1737
|
-
approvedIssues: 0,
|
|
1738
|
-
firstPassApprovals: 0,
|
|
1739
|
-
revisedIssues: 0,
|
|
1740
|
-
blockingConcernIssues: 0,
|
|
1741
|
-
totalRounds: 0
|
|
1742
|
-
};
|
|
1743
|
-
for (const issue of issues) {
|
|
1744
|
-
const planRuns = resolveLatestCompletedContractNegotiationRuns(issue);
|
|
1745
|
-
if (planRuns.length === 0) continue;
|
|
1746
|
-
const latestRun = planRuns[planRuns.length - 1];
|
|
1747
|
-
const effectiveProfile = latestRun.reviewProfile ?? resolveEffectiveReviewProfile(issue);
|
|
1748
|
-
if (effectiveProfile.primary !== profileName) continue;
|
|
1749
|
-
bucket.negotiatedIssues += 1;
|
|
1750
|
-
bucket.totalRounds += planRuns.length;
|
|
1751
|
-
if (latestRun.decisionStatus === "approved") bucket.approvedIssues += 1;
|
|
1752
|
-
if (planRuns.length === 1 && planRuns[0]?.decisionStatus === "approved") bucket.firstPassApprovals += 1;
|
|
1753
|
-
if (planRuns.some((entry) => entry.decisionStatus === "revise" || entry.appliedRefinement)) bucket.revisedIssues += 1;
|
|
1754
|
-
if (planRuns.some((entry) => (entry.blockingConcernsCount ?? 0) > 0)) bucket.blockingConcernIssues += 1;
|
|
1755
|
-
}
|
|
1756
|
-
return {
|
|
1757
|
-
...bucket,
|
|
1758
|
-
approvalRate: rate(bucket.approvedIssues, bucket.negotiatedIssues),
|
|
1759
|
-
firstPassApprovalRate: rate(bucket.firstPassApprovals, bucket.negotiatedIssues),
|
|
1760
|
-
revisionRate: rate(bucket.revisedIssues, bucket.negotiatedIssues),
|
|
1761
|
-
blockingConcernRate: rate(bucket.blockingConcernIssues, bucket.negotiatedIssues),
|
|
1762
|
-
avgRoundsPerIssue: rate(bucket.totalRounds, bucket.negotiatedIssues)
|
|
1763
|
-
};
|
|
1764
|
-
}
|
|
1765
|
-
function recommendCheckpointPolicyForIssue(issues, issue, currentCheckpointPolicy, minSamples = 3) {
|
|
1766
|
-
if (issue.plan?.harnessMode !== "contractual") {
|
|
1767
|
-
if (currentCheckpointPolicy !== "final_only") {
|
|
1768
|
-
const profile2 = resolveEffectiveReviewProfile(issue);
|
|
1769
|
-
return {
|
|
1770
|
-
checkpointPolicy: "final_only",
|
|
1771
|
-
profile: profile2,
|
|
1772
|
-
basis: "heuristic",
|
|
1773
|
-
rationale: "Non-contractual plans must not request checkpoint review."
|
|
1774
|
-
};
|
|
1775
|
-
}
|
|
1776
|
-
return null;
|
|
1777
|
-
}
|
|
1778
|
-
const profile = resolveEffectiveReviewProfile(issue);
|
|
1779
|
-
const complexity = issue.plan?.estimatedComplexity ?? "medium";
|
|
1780
|
-
const lowScope = complexity === "trivial" || complexity === "low";
|
|
1781
|
-
const highCheckpointRisk = HIGH_CHECKPOINT_PROFILES.has(profile.primary) && !lowScope;
|
|
1782
|
-
const stats = computeCheckpointPolicyStats(issues, profile.primary);
|
|
1783
|
-
const finalOnly = stats.final_only;
|
|
1784
|
-
const checkpointed = stats.checkpointed;
|
|
1785
|
-
const checkpointedSamplesReady = checkpointed.reviewedIssues >= minSamples;
|
|
1786
|
-
const finalOnlySamplesReady = finalOnly.reviewedIssues >= minSamples;
|
|
1787
|
-
if (currentCheckpointPolicy !== "checkpointed" && highCheckpointRisk) {
|
|
1788
|
-
if (checkpointedSamplesReady && (checkpointed.checkpointFailureRate ?? 0) >= 0.15) {
|
|
1789
|
-
return {
|
|
1790
|
-
checkpointPolicy: "checkpointed",
|
|
1791
|
-
profile,
|
|
1792
|
-
basis: "historical",
|
|
1793
|
-
rationale: `Enabled checkpoint review for ${profile.primary}: checkpointed runs caught blocking issues before final review in ${Math.round((checkpointed.checkpointFailureRate ?? 0) * 100)}% of ${checkpointed.reviewedIssues} comparable issue(s).`
|
|
1794
|
-
};
|
|
1795
|
-
}
|
|
1796
|
-
if (!checkpointedSamplesReady) {
|
|
1797
|
-
return {
|
|
1798
|
-
checkpointPolicy: "checkpointed",
|
|
1799
|
-
profile,
|
|
1800
|
-
basis: "heuristic",
|
|
1801
|
-
rationale: `Enabled checkpoint review because ${profile.primary} changes are high-risk enough to benefit from an intermediate gate before final review.`
|
|
1802
|
-
};
|
|
1803
|
-
}
|
|
1804
|
-
}
|
|
1805
|
-
if (checkpointedSamplesReady && finalOnlySamplesReady) {
|
|
1806
|
-
const gateLift = (checkpointed.gatePassRate ?? 0) - (finalOnly.gatePassRate ?? 0);
|
|
1807
|
-
const firstPassLift = (checkpointed.firstPassPassRate ?? 0) - (finalOnly.firstPassPassRate ?? 0);
|
|
1808
|
-
const checkpointCatchRate = checkpointed.checkpointFailureRate ?? 0;
|
|
1809
|
-
if (currentCheckpointPolicy !== "checkpointed" && (checkpointCatchRate >= 0.18 || gateLift >= 0.08 || firstPassLift >= 0.1)) {
|
|
1810
|
-
return {
|
|
1811
|
-
checkpointPolicy: "checkpointed",
|
|
1812
|
-
profile,
|
|
1813
|
-
basis: "historical",
|
|
1814
|
-
rationale: `Enabled checkpoint review for ${profile.primary}: checkpointed runs show ${Math.round(checkpointCatchRate * 100)}% checkpoint catch rate, ${Math.round(gateLift * 100)}pp final gate lift, and ${Math.round(firstPassLift * 100)}pp first-pass lift over final-only contractual runs.`
|
|
1815
|
-
};
|
|
1816
|
-
}
|
|
1817
|
-
if (currentCheckpointPolicy === "checkpointed" && !highCheckpointRisk && checkpointCatchRate <= 0.05 && (finalOnly.gatePassRate ?? 0) >= (checkpointed.gatePassRate ?? 0) - 0.05 && (finalOnly.firstPassPassRate ?? 0) >= (checkpointed.firstPassPassRate ?? 0) - 0.05) {
|
|
1818
|
-
return {
|
|
1819
|
-
checkpointPolicy: "final_only",
|
|
1820
|
-
profile,
|
|
1821
|
-
basis: "historical",
|
|
1822
|
-
rationale: `Disabled checkpoint review for ${profile.primary}: checkpointed runs almost never catch issues before final review (${Math.round(checkpointCatchRate * 100)}%), while final-only contractual runs stay within 5pp on final gate and first-pass outcomes.`
|
|
1823
|
-
};
|
|
1824
|
-
}
|
|
1825
|
-
}
|
|
1826
|
-
if (currentCheckpointPolicy === "checkpointed" && lowScope) {
|
|
1827
|
-
return {
|
|
1828
|
-
checkpointPolicy: "final_only",
|
|
1829
|
-
profile,
|
|
1830
|
-
basis: "heuristic",
|
|
1831
|
-
rationale: `Disabled checkpoint review because ${complexity} contractual work should keep the contract gate but avoid an intermediate review pass.`
|
|
1832
|
-
};
|
|
1833
|
-
}
|
|
1834
|
-
return null;
|
|
1835
|
-
}
|
|
1836
|
-
function recommendHarnessModeForIssue(issues, issue, currentMode, minSamples = 3) {
|
|
1837
|
-
const profile = resolveEffectiveReviewProfile(issue);
|
|
1838
|
-
const complexity = issue.plan?.estimatedComplexity ?? "medium";
|
|
1839
|
-
const stats = computeHarnessModeStats(issues, profile.primary);
|
|
1840
|
-
const negotiation = computeContractNegotiationStats(issues, profile.primary);
|
|
1841
|
-
const highRisk = HIGH_RISK_PROFILES.has(profile.primary);
|
|
1842
|
-
const lowScope = complexity === "trivial" || complexity === "low";
|
|
1843
|
-
const negotiationSamplesReady = negotiation.negotiatedIssues >= minSamples;
|
|
1844
|
-
const highRiskNegotiationPressure = negotiationSamplesReady && ((negotiation.blockingConcernRate ?? 0) >= 0.15 || (negotiation.revisionRate ?? 0) >= 0.3);
|
|
1845
|
-
const generalNegotiationPressure = negotiationSamplesReady && !lowScope && ((negotiation.blockingConcernRate ?? 0) >= 0.25 || (negotiation.revisionRate ?? 0) >= 0.4 || (negotiation.avgRoundsPerIssue ?? 0) >= 1.6);
|
|
1846
|
-
const negotiationLowValue = negotiationSamplesReady && (negotiation.firstPassApprovalRate ?? 0) >= 0.9 && (negotiation.blockingConcernRate ?? 1) <= 0.08 && (negotiation.revisionRate ?? 1) <= 0.15;
|
|
1847
|
-
if (highRisk && currentMode !== "contractual") {
|
|
1848
|
-
if (highRiskNegotiationPressure) {
|
|
1849
|
-
return {
|
|
1850
|
-
mode: "contractual",
|
|
1851
|
-
profile,
|
|
1852
|
-
basis: "historical",
|
|
1853
|
-
rationale: `Switched to contractual for ${profile.primary}: contract negotiation found blocking concerns in ${Math.round((negotiation.blockingConcernRate ?? 0) * 100)}% of ${negotiation.negotiatedIssues} comparable issue(s) and forced revisions in ${Math.round((negotiation.revisionRate ?? 0) * 100)}%.`
|
|
1854
|
-
};
|
|
1855
|
-
}
|
|
1856
|
-
const contractual2 = stats.contractual;
|
|
1857
|
-
if (contractual2.reviewedIssues >= minSamples) {
|
|
1858
|
-
return {
|
|
1859
|
-
mode: "contractual",
|
|
1860
|
-
profile,
|
|
1861
|
-
basis: "historical",
|
|
1862
|
-
rationale: `Switched to contractual for ${profile.primary}: historical gate pass ${Math.round((contractual2.gatePassRate ?? 0) * 100)}% across ${contractual2.reviewedIssues} reviewed issue(s).`
|
|
1863
|
-
};
|
|
1864
|
-
}
|
|
1865
|
-
return {
|
|
1866
|
-
mode: "contractual",
|
|
1867
|
-
profile,
|
|
1868
|
-
basis: "heuristic",
|
|
1869
|
-
rationale: `Switched to contractual because ${profile.primary} is a high-risk profile and needs stronger contract negotiation plus skeptical review semantics.`
|
|
1870
|
-
};
|
|
1871
|
-
}
|
|
1872
|
-
if (profile.primary === "general-quality" && lowScope) {
|
|
1873
|
-
const solo = stats.solo;
|
|
1874
|
-
if (solo.reviewedIssues >= minSamples && (solo.gatePassRate ?? 0) >= 0.95 && (solo.reviewReworkRate ?? 1) <= 0.1) {
|
|
1875
|
-
if (currentMode !== "solo") {
|
|
1876
|
-
return {
|
|
1877
|
-
mode: "solo",
|
|
1878
|
-
profile,
|
|
1879
|
-
basis: "historical",
|
|
1880
|
-
rationale: `Downgraded to solo for low-scope general work: solo gate pass ${Math.round((solo.gatePassRate ?? 0) * 100)}% with low rework over ${solo.reviewedIssues} reviewed issue(s).`
|
|
1881
|
-
};
|
|
1882
|
-
}
|
|
1883
|
-
return null;
|
|
1884
|
-
}
|
|
1885
|
-
}
|
|
1886
|
-
if (currentMode !== "contractual" && generalNegotiationPressure) {
|
|
1887
|
-
return {
|
|
1888
|
-
mode: "contractual",
|
|
1889
|
-
profile,
|
|
1890
|
-
basis: "historical",
|
|
1891
|
-
rationale: `Switched to contractual for ${profile.primary}: contract negotiation found blocking concerns in ${Math.round((negotiation.blockingConcernRate ?? 0) * 100)}% of ${negotiation.negotiatedIssues} comparable issue(s), with revisions required in ${Math.round((negotiation.revisionRate ?? 0) * 100)}% and ${Math.round((negotiation.avgRoundsPerIssue ?? 0) * 10) / 10} rounds per issue on average.`
|
|
1892
|
-
};
|
|
1893
|
-
}
|
|
1894
|
-
const standard = stats.standard;
|
|
1895
|
-
const contractual = stats.contractual;
|
|
1896
|
-
const contractualSamplesReady = contractual.reviewedIssues >= minSamples;
|
|
1897
|
-
const standardSamplesReady = standard.reviewedIssues >= minSamples;
|
|
1898
|
-
if (contractualSamplesReady && standardSamplesReady) {
|
|
1899
|
-
const contractualGateLift = (contractual.gatePassRate ?? 0) - (standard.gatePassRate ?? 0);
|
|
1900
|
-
const contractualFirstPassLift = (contractual.firstPassPassRate ?? 0) - (standard.firstPassPassRate ?? 0);
|
|
1901
|
-
if (currentMode !== "contractual" && (contractualGateLift >= 0.12 || contractualFirstPassLift >= 0.15)) {
|
|
1902
|
-
return {
|
|
1903
|
-
mode: "contractual",
|
|
1904
|
-
profile,
|
|
1905
|
-
basis: "historical",
|
|
1906
|
-
rationale: `Switched to contractual for ${profile.primary}: first-pass lift ${Math.round(contractualFirstPassLift * 100)}pp and gate lift ${Math.round(contractualGateLift * 100)}pp over standard.`
|
|
1907
|
-
};
|
|
1908
|
-
}
|
|
1909
|
-
if (currentMode === "contractual" && !highRisk && !lowScope && negotiationLowValue && (standard.gatePassRate ?? 0) >= (contractual.gatePassRate ?? 0) - 0.05 && (standard.firstPassPassRate ?? 0) >= (contractual.firstPassPassRate ?? 0) - 0.05) {
|
|
1910
|
-
return {
|
|
1911
|
-
mode: "standard",
|
|
1912
|
-
profile,
|
|
1913
|
-
basis: "historical",
|
|
1914
|
-
rationale: `Downgraded to standard for ${profile.primary}: contract negotiation approved on first pass in ${Math.round((negotiation.firstPassApprovalRate ?? 0) * 100)}% of ${negotiation.negotiatedIssues} comparable issue(s), and standard review performance stays within 5pp of contractual.`
|
|
1915
|
-
};
|
|
1916
|
-
}
|
|
1917
|
-
}
|
|
1918
|
-
if (!highRisk && currentMode === "solo" && !lowScope) {
|
|
1919
|
-
return {
|
|
1920
|
-
mode: "standard",
|
|
1921
|
-
profile,
|
|
1922
|
-
basis: "heuristic",
|
|
1923
|
-
rationale: `Upgraded from solo to standard because ${complexity} complexity should keep an automated reviewer in the loop.`
|
|
1924
|
-
};
|
|
1925
|
-
}
|
|
1926
|
-
return null;
|
|
1927
|
-
}
|
|
1928
|
-
function computeReviewRouteStats(issues, profileName) {
|
|
1929
|
-
const buckets = {};
|
|
1930
|
-
for (const issue of issues) {
|
|
1931
|
-
const reviewRun = resolveLatestCompletedReviewRun(issue, "final");
|
|
1932
|
-
if (!reviewRun) continue;
|
|
1933
|
-
const effectiveProfile = reviewRun.reviewProfile ?? resolveEffectiveReviewProfile(issue);
|
|
1934
|
-
if (effectiveProfile.primary !== profileName) continue;
|
|
1935
|
-
const routeKey = serializeReviewRouteSnapshot(reviewRun.routing);
|
|
1936
|
-
const bucket = buckets[routeKey] ||= {
|
|
1937
|
-
reviewedIssues: 0,
|
|
1938
|
-
completedReviewedIssues: 0,
|
|
1939
|
-
gatePasses: 0,
|
|
1940
|
-
blockingFailRuns: 0,
|
|
1941
|
-
advisoryFailRuns: 0
|
|
1942
|
-
};
|
|
1943
|
-
bucket.reviewedIssues += 1;
|
|
1944
|
-
if (isCompletedIssue(issue)) bucket.completedReviewedIssues += 1;
|
|
1945
|
-
if (reviewRun.blockingVerdict === "PASS") bucket.gatePasses += 1;
|
|
1946
|
-
if (reviewRun.blockingVerdict === "FAIL") bucket.blockingFailRuns += 1;
|
|
1947
|
-
if ((reviewRun.advisoryFailedCriteriaCount ?? 0) > 0) bucket.advisoryFailRuns += 1;
|
|
1948
|
-
}
|
|
1949
|
-
return Object.fromEntries(
|
|
1950
|
-
Object.entries(buckets).map(([routeKey, bucket]) => [
|
|
1951
|
-
routeKey,
|
|
1952
|
-
{
|
|
1953
|
-
...bucket,
|
|
1954
|
-
gatePassRate: rate(bucket.gatePasses, bucket.reviewedIssues),
|
|
1955
|
-
blockingFailRate: rate(bucket.blockingFailRuns, bucket.reviewedIssues)
|
|
1956
|
-
}
|
|
1957
|
-
])
|
|
1958
|
-
);
|
|
1959
|
-
}
|
|
1960
|
-
function recommendReviewRouteForIssue(issues, issue, candidates, minSamples = 3) {
|
|
1961
|
-
if (candidates.length === 0) return null;
|
|
1962
|
-
const profile = resolveEffectiveReviewProfile(issue);
|
|
1963
|
-
const routeStats = computeReviewRouteStats(issues, profile.primary);
|
|
1964
|
-
const scored = candidates.map((candidate) => {
|
|
1965
|
-
const routeKey = buildReviewRouteKey(candidate);
|
|
1966
|
-
const stats = routeStats[routeKey];
|
|
1967
|
-
const affinity = ROUTE_AFFINITY[profile.primary][candidate.provider] ?? 0;
|
|
1968
|
-
const historicalScore = stats ? (stats.gatePassRate ?? 0) * 4 - (stats.blockingFailRate ?? 0) * 3 + Math.min(stats.reviewedIssues, 6) * 0.15 : 0;
|
|
1969
|
-
return {
|
|
1970
|
-
candidate,
|
|
1971
|
-
routeKey,
|
|
1972
|
-
stats,
|
|
1973
|
-
score: affinity + historicalScore,
|
|
1974
|
-
affinity
|
|
1975
|
-
};
|
|
1976
|
-
}).sort((left, right) => {
|
|
1977
|
-
if (right.score !== left.score) return right.score - left.score;
|
|
1978
|
-
return left.routeKey.localeCompare(right.routeKey);
|
|
1979
|
-
});
|
|
1980
|
-
const current = scored[0];
|
|
1981
|
-
if (!current) return null;
|
|
1982
|
-
if (current.stats && current.stats.reviewedIssues >= minSamples) {
|
|
1983
|
-
return {
|
|
1984
|
-
candidate: current.candidate,
|
|
1985
|
-
profile,
|
|
1986
|
-
basis: "historical",
|
|
1987
|
-
rationale: `Adaptive reviewer route for ${profile.primary}: ${current.routeKey} has ${Math.round((current.stats.gatePassRate ?? 0) * 100)}% gate pass over ${current.stats.reviewedIssues} reviewed issue(s).`
|
|
1988
|
-
};
|
|
1989
|
-
}
|
|
1990
|
-
return {
|
|
1991
|
-
candidate: current.candidate,
|
|
1992
|
-
profile,
|
|
1993
|
-
basis: "heuristic",
|
|
1994
|
-
rationale: `Adaptive reviewer route for ${profile.primary}: preferred ${current.candidate.provider} based on profile affinity while historical samples are still sparse.`
|
|
1995
|
-
};
|
|
1996
|
-
}
|
|
1997
|
-
|
|
1998
1354
|
// src/agents/model-discovery.ts
|
|
1999
1355
|
import { execFileSync } from "child_process";
|
|
2000
1356
|
import { existsSync as existsSync5, readFileSync as readFileSync2, realpathSync } from "fs";
|
|
@@ -2244,6 +1600,20 @@ var REVIEW_PROFILE_MIN_EFFORT = {
|
|
|
2244
1600
|
"api-contract": "high",
|
|
2245
1601
|
"security-hardening": "extra-high"
|
|
2246
1602
|
};
|
|
1603
|
+
var COMPLEXITY_MAX_EFFORT = {
|
|
1604
|
+
trivial: "low",
|
|
1605
|
+
low: "medium",
|
|
1606
|
+
medium: "high",
|
|
1607
|
+
high: "extra-high"
|
|
1608
|
+
};
|
|
1609
|
+
function capEffortByComplexity(effort, complexity) {
|
|
1610
|
+
if (!effort || !complexity) return effort;
|
|
1611
|
+
const cap = COMPLEXITY_MAX_EFFORT[complexity];
|
|
1612
|
+
if (!cap) return effort;
|
|
1613
|
+
const effortIdx = EFFORT_ORDER.indexOf(effort);
|
|
1614
|
+
const capIdx = EFFORT_ORDER.indexOf(cap);
|
|
1615
|
+
return effortIdx > capIdx ? cap : effort;
|
|
1616
|
+
}
|
|
2247
1617
|
var REVIEW_PROFILE_OVERLAYS = {
|
|
2248
1618
|
"general-quality": [],
|
|
2249
1619
|
"ui-polish": ["impeccable", "frontend-design"],
|
|
@@ -2260,6 +1630,9 @@ function maxEffort(left, right) {
|
|
|
2260
1630
|
function stageToRole(stage) {
|
|
2261
1631
|
if (stage === "plan") return "planner";
|
|
2262
1632
|
if (stage === "review") return "reviewer";
|
|
1633
|
+
if (stage === "enhance") return "enhancer";
|
|
1634
|
+
if (stage === "chat") return "chatter";
|
|
1635
|
+
if (stage === "services") return "services-analyst";
|
|
2263
1636
|
return "executor";
|
|
2264
1637
|
}
|
|
2265
1638
|
function buildStageProvider(state, issue, stage, workflowConfig) {
|
|
@@ -2286,7 +1659,9 @@ function buildStageProvider(state, issue, stage, workflowConfig) {
|
|
|
2286
1659
|
function specializeReviewerProvider(baseProvider, issue) {
|
|
2287
1660
|
const reviewProfile = issue.reviewProfile ?? deriveReviewProfile(issue);
|
|
2288
1661
|
const minEffort = REVIEW_PROFILE_MIN_EFFORT[reviewProfile.primary];
|
|
2289
|
-
const
|
|
1662
|
+
const complexity = issue.plan?.estimatedComplexity;
|
|
1663
|
+
const floored = maxEffort(baseProvider.reasoningEffort, minEffort);
|
|
1664
|
+
const reasoningEffort = capEffortByComplexity(floored, complexity);
|
|
2290
1665
|
const overlays = [.../* @__PURE__ */ new Set([...baseProvider.overlays ?? [], ...REVIEW_PROFILE_OVERLAYS[reviewProfile.primary]])];
|
|
2291
1666
|
const command = getProviderDefaultCommand(baseProvider.provider, reasoningEffort, baseProvider.model) || baseProvider.command;
|
|
2292
1667
|
return {
|
|
@@ -2294,11 +1669,11 @@ function specializeReviewerProvider(baseProvider, issue) {
|
|
|
2294
1669
|
command,
|
|
2295
1670
|
reasoningEffort,
|
|
2296
1671
|
overlays,
|
|
2297
|
-
selectionReason: `Reviewer specialized for ${reviewProfile.primary};
|
|
1672
|
+
selectionReason: `Reviewer specialized for ${reviewProfile.primary}; effort ${reasoningEffort ?? "default"}${complexity && complexity !== "medium" ? ` (capped by ${complexity} complexity)` : ""}.`
|
|
2298
1673
|
};
|
|
2299
1674
|
}
|
|
2300
1675
|
function resolveSynchronousProviderModel(provider, workflowConfig) {
|
|
2301
|
-
const stages = workflowConfig ? [workflowConfig.review, workflowConfig.execute, workflowConfig.plan] : [];
|
|
1676
|
+
const stages = workflowConfig ? [workflowConfig.review, workflowConfig.execute, workflowConfig.plan, workflowConfig.enhance, workflowConfig.chat, workflowConfig.services] : [];
|
|
2302
1677
|
const fromWorkflow = stages.find((stage) => stage?.provider === provider)?.model;
|
|
2303
1678
|
if (fromWorkflow) return fromWorkflow;
|
|
2304
1679
|
if (provider === "codex") return readCodexConfig().model;
|
|
@@ -2352,6 +1727,12 @@ function roleToStageKey(role) {
|
|
|
2352
1727
|
return "execute";
|
|
2353
1728
|
case "reviewer":
|
|
2354
1729
|
return "review";
|
|
1730
|
+
case "enhancer":
|
|
1731
|
+
return "enhance";
|
|
1732
|
+
case "chatter":
|
|
1733
|
+
return "chat";
|
|
1734
|
+
case "services-analyst":
|
|
1735
|
+
return "services";
|
|
2355
1736
|
}
|
|
2356
1737
|
}
|
|
2357
1738
|
function getExecutionProviders(state, issue, workflowConfig) {
|
|
@@ -2385,12 +1766,6 @@ export {
|
|
|
2385
1766
|
markAllMilestonesDirty,
|
|
2386
1767
|
markAllIssuePlansDirty,
|
|
2387
1768
|
markAllEventsDirty,
|
|
2388
|
-
deriveReviewProfile,
|
|
2389
|
-
serializeReviewRouteSnapshot,
|
|
2390
|
-
applyHarnessModeToPlan,
|
|
2391
|
-
applyCheckpointPolicyToPlan,
|
|
2392
|
-
recommendCheckpointPolicyForIssue,
|
|
2393
|
-
recommendHarnessModeForIssue,
|
|
2394
1769
|
normalizeAcceptanceCriteria,
|
|
2395
1770
|
deriveExecutionContract,
|
|
2396
1771
|
buildFullPlanPrompt,
|
|
@@ -2411,4 +1786,4 @@ export {
|
|
|
2411
1786
|
getReviewProvider,
|
|
2412
1787
|
getSessionProvidersForIssue
|
|
2413
1788
|
};
|
|
2414
|
-
//# sourceMappingURL=chunk-
|
|
1789
|
+
//# sourceMappingURL=chunk-PI7Y77R3.js.map
|