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.
Files changed (65) hide show
  1. package/app/dist/assets/{CommandPalette-M4VAMxCU.js → CommandPalette-CL8p78lG.js} +1 -1
  2. package/app/dist/assets/{KeyboardShortcutsHelp-DkvPUXQq.js → KeyboardShortcutsHelp-CqEFfGcE.js} +1 -1
  3. package/app/dist/assets/OnboardingWizard-BmI50ZUv.js +1 -0
  4. package/app/dist/assets/analytics.lazy-CXGjZabc.js +1 -0
  5. package/app/dist/assets/{api-CkVfYg_m.js → api-CEr_D4e5.js} +1 -1
  6. package/app/dist/assets/{createLucideIcon-Dfk_Hxud.js → createLucideIcon-luywpIq4.js} +1 -1
  7. package/app/dist/assets/index-CEaccpYh.js +96 -0
  8. package/app/dist/assets/index-CzzWGzux.css +1 -0
  9. package/app/dist/assets/vendor-uqBx3VSC.js +9 -0
  10. package/app/dist/index.html +12 -12
  11. package/app/dist/service-worker.js +15 -5
  12. package/dist/agent/pty-daemon.js +3 -2
  13. package/dist/agent/run-local.js +71 -52
  14. package/dist/{agent-RMQTTUEC.js → agent-DFSFG6DG.js} +18 -12
  15. package/dist/{analytics-broadcaster-O6YBP66L.js → analytics-broadcaster-O4AE3RUK.js} +21 -14
  16. package/dist/approve-plan.command-QGQZZXTQ.js +17 -0
  17. package/dist/{chunk-E2EWEYA4.js → chunk-2PRRKBG6.js} +20 -10
  18. package/dist/chunk-5AMWD66T.js +38 -0
  19. package/dist/{chunk-QQQLP3PL.js → chunk-7TXZYZR5.js} +9 -37
  20. package/dist/chunk-AAVROEQC.js +859 -0
  21. package/dist/{chunk-ESWHDHH6.js → chunk-AAZKYWOY.js} +4 -4
  22. package/dist/chunk-EBCSQFPR.js +682 -0
  23. package/dist/{chunk-BRSR26VK.js → chunk-FH7HUPZX.js} +2 -2
  24. package/dist/chunk-HOIOVUHI.js +35 -0
  25. package/dist/{chunk-AILXZ2TD.js → chunk-JRLWLZOD.js} +20 -13
  26. package/dist/{chunk-YRSH2CLW.js → chunk-K36BWMUV.js} +1741 -1216
  27. package/dist/chunk-N4KFNX2G.js +370 -0
  28. package/dist/chunk-PACI3T4I.js +125 -0
  29. package/dist/{chunk-FJNH3G2Z.js → chunk-PI7Y77R3.js} +38 -663
  30. package/dist/{chunk-DVU3CXWA.js → chunk-PXTIWKLQ.js} +2 -1
  31. package/dist/{chunk-SOBLO4YZ.js → chunk-QH6VCTET.js} +316 -127
  32. package/dist/{chunk-MVTGAKQK.js → chunk-QHISYRXJ.js} +2 -2
  33. package/dist/{chunk-42AMQAJG.js → chunk-VM5QAYP5.js} +2 -2
  34. package/dist/cli.js +17 -11
  35. package/dist/create-issue.command-VAKYRECC.js +24 -0
  36. package/dist/{fsm-issue-YGGF7SIL.js → fsm-issue-EHTSKMFN.js} +9 -8
  37. package/dist/fsm-service-7O4AJG2R.js +32 -0
  38. package/dist/{helpers-L7NYO5XS.js → helpers-ON2S7UEF.js} +2 -2
  39. package/dist/{issue-log-broadcaster-WZAHISYB.js → issue-log-broadcaster-FZGVEEIX.js} +20 -13
  40. package/dist/{issues-3QRR7KM6.js → issues-3YNNTB4U.js} +10 -7
  41. package/dist/{log-analyzer-K7MXQB4T.js → log-analyzer-EIX6R6PP.js} +82 -18
  42. package/dist/logger-IFLXTQPS.js +11 -0
  43. package/dist/mcp/server.js +2 -2
  44. package/dist/merge-workspace.command-T2NIGR4M.js +24 -0
  45. package/dist/{parallel-executor-6INE6NDO.js → parallel-executor-DWESCNX3.js} +20 -14
  46. package/dist/queue-workers-V57BYXAY.js +38 -0
  47. package/dist/replan-issue.command-2GQ3QXCR.js +17 -0
  48. package/dist/retry-issue.command-GJBUUYDJ.js +17 -0
  49. package/dist/scheduler-KYILMWLD.js +32 -0
  50. package/dist/{settings-ZAWDCFP2.js → settings-SOTIS6ZD.js} +32 -12
  51. package/dist/settings.resource-JMD3JQOS.js +30 -0
  52. package/dist/{store-M6NCKMZY.js → store-S3NAYZ3S.js} +18 -12
  53. package/dist/{web-push-AX5IIK3P.js → web-push-QCTLS7EJ.js} +3 -3
  54. package/dist/websocket-T2Y3BY4B.js +61 -0
  55. package/dist/{workspace-CJTWFWTJ.js → workspace-OS7GPMCN.js} +7 -6
  56. package/package.json +8 -5
  57. package/app/dist/assets/OnboardingWizard-B7V9hoCR.js +0 -1
  58. package/app/dist/assets/analytics.lazy-zVJdF880.js +0 -1
  59. package/app/dist/assets/index-BpiCi7Ew.css +0 -1
  60. package/app/dist/assets/index-D2INW0zc.js +0 -47
  61. package/app/dist/assets/vendor-BEoYbFV1.js +0 -9
  62. package/dist/queue-workers-XFZK3TT5.js +0 -32
  63. package/dist/replan-issue.command-4UCWYHGZ.js +0 -15
  64. package/dist/scheduler-ZP7GOZDW.js +0 -26
  65. package/dist/settings.resource-5CW456AZ.js +0 -24
@@ -1,12 +1,17 @@
1
1
  import {
2
- logger
3
- } from "./chunk-DVU3CXWA.js";
2
+ buildReviewRouteKey,
3
+ deriveReviewProfile,
4
+ recommendReviewRouteForIssue
5
+ } from "./chunk-EBCSQFPR.js";
4
6
  import {
5
7
  renderPrompt
6
- } from "./chunk-ESWHDHH6.js";
8
+ } from "./chunk-AAZKYWOY.js";
7
9
  import {
8
10
  sleep
9
- } from "./chunk-42AMQAJG.js";
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 reasoningEffort = maxEffort(baseProvider.reasoningEffort, minEffort);
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}; raised scrutiny with ${reasoningEffort ?? "default"} effort.`
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-FJNH3G2Z.js.map
1789
+ //# sourceMappingURL=chunk-PI7Y77R3.js.map