fifony 0.1.42 → 0.1.43

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 (58) hide show
  1. package/app/dist/assets/{CommandPalette-DNR5umI1.js → CommandPalette-M4VAMxCU.js} +1 -1
  2. package/app/dist/assets/{KeyboardShortcutsHelp-Dpl19F20.js → KeyboardShortcutsHelp-DkvPUXQq.js} +1 -1
  3. package/app/dist/assets/OnboardingWizard-B7V9hoCR.js +1 -0
  4. package/app/dist/assets/analytics.lazy-zVJdF880.js +1 -0
  5. package/app/dist/assets/{api-ChEctgc5.js → api-CkVfYg_m.js} +1 -1
  6. package/app/dist/assets/{createLucideIcon-R47sXufx.js → createLucideIcon-Dfk_Hxud.js} +1 -1
  7. package/app/dist/assets/index-BpiCi7Ew.css +1 -0
  8. package/app/dist/assets/index-D2INW0zc.js +47 -0
  9. package/app/dist/assets/vendor-BEoYbFV1.js +9 -0
  10. package/app/dist/index.html +5 -5
  11. package/app/dist/service-worker.js +9 -4
  12. package/bin/fifony.js +3 -0
  13. package/dist/agent/pty-daemon.js +177 -0
  14. package/dist/agent/run-local.js +177 -43
  15. package/dist/{agent-NNGZEKZH.js → agent-RMQTTUEC.js} +37 -16
  16. package/dist/analytics-broadcaster-O6YBP66L.js +145 -0
  17. package/dist/chunk-3NE23NYW.js +82 -0
  18. package/dist/chunk-42AMQAJG.js +404 -0
  19. package/dist/{chunk-H5N7O5NP.js → chunk-AILXZ2TD.js} +79 -147
  20. package/dist/{chunk-I2UHVKHS.js → chunk-BRSR26VK.js} +2 -2
  21. package/dist/chunk-E2EWEYA4.js +1302 -0
  22. package/dist/chunk-ESWHDHH6.js +102 -0
  23. package/dist/{chunk-NB44PCD2.js → chunk-FJNH3G2Z.js} +1061 -1138
  24. package/dist/chunk-MVTGAKQK.js +493 -0
  25. package/dist/chunk-QQQLP3PL.js +155 -0
  26. package/dist/chunk-SOBLO4YZ.js +2016 -0
  27. package/dist/chunk-YRSH2CLW.js +13784 -0
  28. package/dist/cli.js +335 -44
  29. package/dist/{issue-state-machine-GPQNZYUZ.js → fsm-issue-YGGF7SIL.js} +9 -5
  30. package/dist/helpers-L7NYO5XS.js +53 -0
  31. package/dist/issue-log-broadcaster-WZAHISYB.js +84 -0
  32. package/dist/{issues-MZLRSXD6.js → issues-3QRR7KM6.js} +10 -8
  33. package/dist/log-analyzer-K7MXQB4T.js +287 -0
  34. package/dist/mcp/server.js +109 -137
  35. package/dist/parallel-executor-6INE6NDO.js +118 -0
  36. package/dist/pid-manager-UBWXVSMD.js +21 -0
  37. package/dist/queue-workers-XFZK3TT5.js +32 -0
  38. package/dist/replan-issue.command-4UCWYHGZ.js +15 -0
  39. package/dist/scheduler-ZP7GOZDW.js +26 -0
  40. package/dist/{settings-NGY33WQE.js → settings-ZAWDCFP2.js} +32 -8
  41. package/dist/settings.resource-5CW456AZ.js +24 -0
  42. package/dist/store-M6NCKMZY.js +97 -0
  43. package/dist/{web-push-CRVDJKWR.js → web-push-AX5IIK3P.js} +2 -2
  44. package/dist/{workspace-D3F3XGSI.js → workspace-CJTWFWTJ.js} +5 -4
  45. package/package.json +8 -7
  46. package/app/dist/assets/OnboardingWizard-CijMhJDW.js +0 -1
  47. package/app/dist/assets/analytics.lazy-Dq90a756.js +0 -1
  48. package/app/dist/assets/index-Dy_fM427.js +0 -54
  49. package/app/dist/assets/index-Q9jBP0Pz.css +0 -1
  50. package/app/dist/assets/vendor-DkWeBvNl.js +0 -9
  51. package/dist/chunk-2CVTK5F2.js +0 -288
  52. package/dist/chunk-37N5OFHM.js +0 -125
  53. package/dist/chunk-JTKUWIQD.js +0 -8406
  54. package/dist/chunk-RBDBGU2C.js +0 -303
  55. package/dist/issue-runner-CMZPSVC7.js +0 -16
  56. package/dist/queue-workers-XZ6DGH4W.js +0 -23
  57. package/dist/scheduler-NVE6L3P7.js +0 -22
  58. package/dist/store-4HCGBN4L.js +0 -65
@@ -0,0 +1,493 @@
1
+ import {
2
+ serializeReviewRouteSnapshot
3
+ } from "./chunk-FJNH3G2Z.js";
4
+
5
+ // src/domains/metrics.ts
6
+ function resolveAnalyticsReviewRun(issue) {
7
+ const reviewRuns = Array.isArray(issue.reviewRuns) ? issue.reviewRuns : [];
8
+ const completed = reviewRuns.filter((entry) => entry.status === "completed");
9
+ const candidates = completed.filter((entry) => entry.scope === "final");
10
+ const pool = candidates.length > 0 ? candidates : completed;
11
+ if (pool.length === 0) return null;
12
+ return [...pool].sort((left, right) => {
13
+ const leftAt = Date.parse(left.completedAt ?? left.startedAt);
14
+ const rightAt = Date.parse(right.completedAt ?? right.startedAt);
15
+ if (!Number.isNaN(leftAt) && !Number.isNaN(rightAt) && leftAt !== rightAt) return rightAt - leftAt;
16
+ return right.attempt - left.attempt;
17
+ })[0] ?? null;
18
+ }
19
+ function formatReviewerRoute(reviewRun) {
20
+ if (!reviewRun) return "unknown";
21
+ return serializeReviewRouteSnapshot(reviewRun.routing);
22
+ }
23
+ function resolveAnalyticsContractNegotiationRuns(issue) {
24
+ const runs = Array.isArray(issue.contractNegotiationRuns) ? issue.contractNegotiationRuns : [];
25
+ const completed = runs.filter((entry) => entry.status === "completed");
26
+ if (completed.length === 0) return [];
27
+ const latestPlanVersion = completed.reduce((maxPlanVersion, entry) => Math.max(maxPlanVersion, entry.planVersion ?? 0), 0);
28
+ return completed.filter((entry) => (entry.planVersion ?? 0) === latestPlanVersion).sort((left, right) => {
29
+ if ((left.attempt ?? 0) !== (right.attempt ?? 0)) return (left.attempt ?? 0) - (right.attempt ?? 0);
30
+ const leftAt = Date.parse(left.completedAt ?? left.startedAt);
31
+ const rightAt = Date.parse(right.completedAt ?? right.startedAt);
32
+ if (!Number.isNaN(leftAt) && !Number.isNaN(rightAt) && leftAt !== rightAt) return leftAt - rightAt;
33
+ return left.id.localeCompare(right.id);
34
+ });
35
+ }
36
+ function resolveAnalyticsCheckpointReviewRuns(issue) {
37
+ const reviewRuns = Array.isArray(issue.reviewRuns) ? issue.reviewRuns : [];
38
+ const completed = reviewRuns.filter((entry) => entry.status === "completed" && entry.scope === "checkpoint");
39
+ if (completed.length === 0) return [];
40
+ const latestPlanVersion = completed.reduce((maxPlanVersion, entry) => Math.max(maxPlanVersion, entry.planVersion ?? 0), 0);
41
+ return completed.filter((entry) => (entry.planVersion ?? 0) === latestPlanVersion).sort((left, right) => {
42
+ if ((left.attempt ?? 0) !== (right.attempt ?? 0)) return (left.attempt ?? 0) - (right.attempt ?? 0);
43
+ const leftAt = Date.parse(left.completedAt ?? left.startedAt);
44
+ const rightAt = Date.parse(right.completedAt ?? right.startedAt);
45
+ if (!Number.isNaN(leftAt) && !Number.isNaN(rightAt) && leftAt !== rightAt) return leftAt - rightAt;
46
+ return left.id.localeCompare(right.id);
47
+ });
48
+ }
49
+ function computeMetrics(issues) {
50
+ let planning = 0;
51
+ let queued = 0;
52
+ let inProgress = 0;
53
+ let blocked = 0;
54
+ let done = 0;
55
+ let merged = 0;
56
+ let cancelled = 0;
57
+ const completionTimes = [];
58
+ for (const issue of issues) {
59
+ if (issue.state === "Merged") {
60
+ const duration = issue.durationMs;
61
+ const candidate = typeof duration === "number" && Number.isFinite(duration) ? duration : Number.isFinite(Date.parse(issue.startedAt ?? "")) && Number.isFinite(Date.parse(issue.completedAt ?? "")) ? Date.parse(issue.completedAt) - Date.parse(issue.startedAt) : NaN;
62
+ if (Number.isFinite(candidate) && candidate >= 0) {
63
+ completionTimes.push(candidate);
64
+ }
65
+ }
66
+ switch (issue.state) {
67
+ case "Planning":
68
+ planning += 1;
69
+ break;
70
+ case "PendingApproval":
71
+ queued += 1;
72
+ break;
73
+ case "Queued":
74
+ case "Running":
75
+ case "Reviewing":
76
+ case "PendingDecision":
77
+ inProgress += 1;
78
+ break;
79
+ case "Blocked":
80
+ blocked += 1;
81
+ break;
82
+ case "Approved":
83
+ done += 1;
84
+ break;
85
+ case "Merged":
86
+ merged += 1;
87
+ break;
88
+ case "Cancelled":
89
+ cancelled += 1;
90
+ break;
91
+ }
92
+ }
93
+ if (completionTimes.length === 0) {
94
+ return {
95
+ total: issues.length,
96
+ planning,
97
+ queued,
98
+ inProgress,
99
+ blocked,
100
+ done,
101
+ merged,
102
+ cancelled,
103
+ activeWorkers: 0
104
+ };
105
+ }
106
+ const sortedCompletionTimes = completionTimes.slice().sort((a, b) => a - b);
107
+ const totalCompletionMs = sortedCompletionTimes.reduce((acc, value) => acc + value, 0);
108
+ const mid = Math.floor(sortedCompletionTimes.length / 2);
109
+ const medianCompletionMs = sortedCompletionTimes.length % 2 === 1 ? sortedCompletionTimes[mid] : Math.round((sortedCompletionTimes[mid - 1] + sortedCompletionTimes[mid]) / 2);
110
+ return {
111
+ total: issues.length,
112
+ planning,
113
+ queued,
114
+ inProgress,
115
+ blocked,
116
+ done,
117
+ merged,
118
+ cancelled,
119
+ activeWorkers: 0,
120
+ avgCompletionMs: Math.round(totalCompletionMs / completionTimes.length),
121
+ medianCompletionMs,
122
+ fastestCompletionMs: sortedCompletionTimes[0],
123
+ slowestCompletionMs: sortedCompletionTimes[sortedCompletionTimes.length - 1]
124
+ };
125
+ }
126
+ function computeQualityGateMetrics(issues) {
127
+ const reviewedIssues = issues.filter(
128
+ (issue) => (issue.reviewAttempt ?? 0) > 0 || !!issue.gradingReport || !!issue.reviewingAt
129
+ );
130
+ const completedReviewedIssues = reviewedIssues.filter((issue) => issue.state === "Approved" || issue.state === "Merged");
131
+ const reviewReworkIssues = reviewedIssues.filter(
132
+ (issue) => (issue.previousAttemptSummaries ?? []).some((summary) => summary.phase === "review")
133
+ );
134
+ const firstPassPasses = completedReviewedIssues.filter(
135
+ (issue) => (issue.reviewAttempt ?? 0) <= 1 && !(issue.previousAttemptSummaries ?? []).some((summary) => summary.phase === "review")
136
+ );
137
+ const criteriaByCategory = {};
138
+ const byReviewerRoute = {};
139
+ const byReviewProfile = {
140
+ "general-quality": { reviewedIssues: 0, completedReviewedIssues: 0, failedCriteria: 0, blockingFailedCriteria: 0, advisoryFailedCriteria: 0 },
141
+ "ui-polish": { reviewedIssues: 0, completedReviewedIssues: 0, failedCriteria: 0, blockingFailedCriteria: 0, advisoryFailedCriteria: 0 },
142
+ "workflow-fsm": { reviewedIssues: 0, completedReviewedIssues: 0, failedCriteria: 0, blockingFailedCriteria: 0, advisoryFailedCriteria: 0 },
143
+ "integration-safety": { reviewedIssues: 0, completedReviewedIssues: 0, failedCriteria: 0, blockingFailedCriteria: 0, advisoryFailedCriteria: 0 },
144
+ "api-contract": { reviewedIssues: 0, completedReviewedIssues: 0, failedCriteria: 0, blockingFailedCriteria: 0, advisoryFailedCriteria: 0 },
145
+ "security-hardening": { reviewedIssues: 0, completedReviewedIssues: 0, failedCriteria: 0, blockingFailedCriteria: 0, advisoryFailedCriteria: 0 },
146
+ unknown: { reviewedIssues: 0, completedReviewedIssues: 0, failedCriteria: 0, blockingFailedCriteria: 0, advisoryFailedCriteria: 0 }
147
+ };
148
+ const contractNegotiationByProfile = {
149
+ "general-quality": { negotiatedIssues: 0, approvedIssues: 0, firstPassApprovals: 0, revisedIssues: 0, blockingConcernIssues: 0, totalRounds: 0 },
150
+ "ui-polish": { negotiatedIssues: 0, approvedIssues: 0, firstPassApprovals: 0, revisedIssues: 0, blockingConcernIssues: 0, totalRounds: 0 },
151
+ "workflow-fsm": { negotiatedIssues: 0, approvedIssues: 0, firstPassApprovals: 0, revisedIssues: 0, blockingConcernIssues: 0, totalRounds: 0 },
152
+ "integration-safety": { negotiatedIssues: 0, approvedIssues: 0, firstPassApprovals: 0, revisedIssues: 0, blockingConcernIssues: 0, totalRounds: 0 },
153
+ "api-contract": { negotiatedIssues: 0, approvedIssues: 0, firstPassApprovals: 0, revisedIssues: 0, blockingConcernIssues: 0, totalRounds: 0 },
154
+ "security-hardening": { negotiatedIssues: 0, approvedIssues: 0, firstPassApprovals: 0, revisedIssues: 0, blockingConcernIssues: 0, totalRounds: 0 },
155
+ unknown: { negotiatedIssues: 0, approvedIssues: 0, firstPassApprovals: 0, revisedIssues: 0, blockingConcernIssues: 0, totalRounds: 0 }
156
+ };
157
+ const byHarnessMode = {
158
+ solo: { reviewedIssues: 0, completedReviewedIssues: 0, reworkIssues: 0, firstPassPasses: 0, totalCriteria: 0, failedCriteria: 0, advisoryFailedCriteria: 0, blockingFailedCriteria: 0 },
159
+ standard: { reviewedIssues: 0, completedReviewedIssues: 0, reworkIssues: 0, firstPassPasses: 0, totalCriteria: 0, failedCriteria: 0, advisoryFailedCriteria: 0, blockingFailedCriteria: 0 },
160
+ contractual: { reviewedIssues: 0, completedReviewedIssues: 0, reworkIssues: 0, firstPassPasses: 0, totalCriteria: 0, failedCriteria: 0, advisoryFailedCriteria: 0, blockingFailedCriteria: 0 }
161
+ };
162
+ const byCheckpointPolicy = {
163
+ final_only: {
164
+ reviewedIssues: 0,
165
+ completedReviewedIssues: 0,
166
+ gatePasses: 0,
167
+ firstPassPasses: 0,
168
+ reworkIssues: 0,
169
+ checkpointRuns: 0,
170
+ checkpointPassedIssues: 0,
171
+ checkpointFailedIssues: 0
172
+ },
173
+ checkpointed: {
174
+ reviewedIssues: 0,
175
+ completedReviewedIssues: 0,
176
+ gatePasses: 0,
177
+ firstPassPasses: 0,
178
+ reworkIssues: 0,
179
+ checkpointRuns: 0,
180
+ checkpointPassedIssues: 0,
181
+ checkpointFailedIssues: 0
182
+ }
183
+ };
184
+ let totalCriteria = 0;
185
+ let failedCriteria = 0;
186
+ let blockingFailedCriteria = 0;
187
+ let advisoryFailedCriteria = 0;
188
+ const contractNegotiationSummary = {
189
+ negotiatedIssues: 0,
190
+ approvedIssues: 0,
191
+ firstPassApprovals: 0,
192
+ revisedIssues: 0,
193
+ blockingConcernIssues: 0,
194
+ totalRounds: 0
195
+ };
196
+ const policyDecisionSummary = {
197
+ total: 0,
198
+ harnessModeChanges: 0,
199
+ checkpointPolicyChanges: 0,
200
+ reviewRecoveryReplans: 0,
201
+ byKind: {
202
+ "harness-mode": 0,
203
+ "checkpoint-policy": 0,
204
+ "review-recovery": 0
205
+ },
206
+ byBasis: {
207
+ historical: 0,
208
+ heuristic: 0,
209
+ runtime: 0
210
+ }
211
+ };
212
+ const memoryPipeline = {
213
+ issuesWithMemoryFlushes: 0,
214
+ totalMemoryFlushes: 0,
215
+ issuesWithContextReports: 0,
216
+ issuesWithStageReports: 0,
217
+ issuesWithCompaction: 0,
218
+ byRole: {
219
+ planner: { reports: 0, selectedHits: 0, totalHits: 0, discardedHits: 0 },
220
+ executor: { reports: 0, selectedHits: 0, totalHits: 0, discardedHits: 0 },
221
+ reviewer: { reports: 0, selectedHits: 0, totalHits: 0, discardedHits: 0 }
222
+ },
223
+ byLayer: {
224
+ bootstrap: { hitCount: 0, selectedHitCount: 0, discardedHitCount: 0 },
225
+ "workspace-memory": { hitCount: 0, selectedHitCount: 0, discardedHitCount: 0 },
226
+ "issue-memory": { hitCount: 0, selectedHitCount: 0, discardedHitCount: 0 },
227
+ retrieval: { hitCount: 0, selectedHitCount: 0, discardedHitCount: 0 }
228
+ },
229
+ byStage: {
230
+ ingest: { reports: 0, completed: 0, skipped: 0, totalDurationMs: 0, totalInputCount: 0, totalOutputCount: 0, budgetedRuns: 0, totalBudgetLimit: 0 },
231
+ "flush-memory": { reports: 0, completed: 0, skipped: 0, totalDurationMs: 0, totalInputCount: 0, totalOutputCount: 0, budgetedRuns: 0, totalBudgetLimit: 0 },
232
+ retrieve: { reports: 0, completed: 0, skipped: 0, totalDurationMs: 0, totalInputCount: 0, totalOutputCount: 0, budgetedRuns: 0, totalBudgetLimit: 0 },
233
+ budget: { reports: 0, completed: 0, skipped: 0, totalDurationMs: 0, totalInputCount: 0, totalOutputCount: 0, budgetedRuns: 0, totalBudgetLimit: 0 },
234
+ compact: { reports: 0, completed: 0, skipped: 0, totalDurationMs: 0, totalInputCount: 0, totalOutputCount: 0, budgetedRuns: 0, totalBudgetLimit: 0 },
235
+ assemble: { reports: 0, completed: 0, skipped: 0, totalDurationMs: 0, totalInputCount: 0, totalOutputCount: 0, budgetedRuns: 0, totalBudgetLimit: 0 }
236
+ }
237
+ };
238
+ for (const issue of reviewedIssues) {
239
+ const harnessMode = issue.plan?.harnessMode ?? "standard";
240
+ const harnessBucket = byHarnessMode[harnessMode];
241
+ const checkpointPolicy = issue.plan?.executionContract?.checkpointPolicy === "checkpointed" ? "checkpointed" : "final_only";
242
+ const checkpointBucket = byCheckpointPolicy[checkpointPolicy];
243
+ const reviewProfile = issue.reviewProfile?.primary ?? "unknown";
244
+ const reviewProfileBucket = byReviewProfile[reviewProfile];
245
+ const reviewRouteKey = formatReviewerRoute(resolveAnalyticsReviewRun(issue));
246
+ const reviewRouteBucket = byReviewerRoute[reviewRouteKey] ||= {
247
+ reviewedIssues: 0,
248
+ completedReviewedIssues: 0,
249
+ failedCriteria: 0,
250
+ blockingFailedCriteria: 0,
251
+ advisoryFailedCriteria: 0
252
+ };
253
+ harnessBucket.reviewedIssues += 1;
254
+ if (harnessMode === "contractual") checkpointBucket.reviewedIssues += 1;
255
+ reviewProfileBucket.reviewedIssues += 1;
256
+ reviewRouteBucket.reviewedIssues += 1;
257
+ if (issue.state === "Approved" || issue.state === "Merged") harnessBucket.completedReviewedIssues += 1;
258
+ if ((issue.state === "Approved" || issue.state === "Merged") && harnessMode === "contractual") checkpointBucket.completedReviewedIssues += 1;
259
+ if (issue.state === "Approved" || issue.state === "Merged") reviewProfileBucket.completedReviewedIssues += 1;
260
+ if (issue.state === "Approved" || issue.state === "Merged") reviewRouteBucket.completedReviewedIssues += 1;
261
+ if ((issue.previousAttemptSummaries ?? []).some((summary) => summary.phase === "review")) harnessBucket.reworkIssues += 1;
262
+ if ((issue.previousAttemptSummaries ?? []).some((summary) => summary.phase === "review") && harnessMode === "contractual") checkpointBucket.reworkIssues += 1;
263
+ if ((issue.reviewAttempt ?? 0) <= 1 && !(issue.previousAttemptSummaries ?? []).some((summary) => summary.phase === "review")) {
264
+ harnessBucket.firstPassPasses += 1;
265
+ if (harnessMode === "contractual" && (issue.state === "Approved" || issue.state === "Merged")) checkpointBucket.firstPassPasses += 1;
266
+ }
267
+ const finalReviewRun = resolveAnalyticsReviewRun(issue);
268
+ if (finalReviewRun?.blockingVerdict === "PASS" && harnessMode === "contractual") checkpointBucket.gatePasses += 1;
269
+ if (harnessMode === "contractual" && checkpointPolicy === "checkpointed") {
270
+ const checkpointRuns = resolveAnalyticsCheckpointReviewRuns(issue);
271
+ checkpointBucket.checkpointRuns += checkpointRuns.length;
272
+ if (checkpointRuns.some((entry) => entry.blockingVerdict === "PASS")) checkpointBucket.checkpointPassedIssues += 1;
273
+ if (checkpointRuns.some((entry) => entry.blockingVerdict === "FAIL")) checkpointBucket.checkpointFailedIssues += 1;
274
+ }
275
+ for (const criterion of issue.gradingReport?.criteria ?? []) {
276
+ totalCriteria += 1;
277
+ harnessBucket.totalCriteria += 1;
278
+ if (criterion.result === "FAIL") {
279
+ failedCriteria += 1;
280
+ harnessBucket.failedCriteria += 1;
281
+ reviewProfileBucket.failedCriteria += 1;
282
+ reviewRouteBucket.failedCriteria += 1;
283
+ if (criterion.blocking) {
284
+ blockingFailedCriteria += 1;
285
+ harnessBucket.blockingFailedCriteria += 1;
286
+ reviewProfileBucket.blockingFailedCriteria += 1;
287
+ reviewRouteBucket.blockingFailedCriteria += 1;
288
+ } else {
289
+ advisoryFailedCriteria += 1;
290
+ harnessBucket.advisoryFailedCriteria += 1;
291
+ reviewProfileBucket.advisoryFailedCriteria += 1;
292
+ reviewRouteBucket.advisoryFailedCriteria += 1;
293
+ }
294
+ }
295
+ const bucket = criteriaByCategory[criterion.category] ||= { pass: 0, fail: 0, skip: 0 };
296
+ if (criterion.result === "PASS") bucket.pass += 1;
297
+ if (criterion.result === "FAIL") bucket.fail += 1;
298
+ if (criterion.result === "SKIP") bucket.skip += 1;
299
+ }
300
+ }
301
+ for (const issue of issues) {
302
+ if ((issue.memoryFlushCount ?? 0) > 0) {
303
+ memoryPipeline.issuesWithMemoryFlushes += 1;
304
+ memoryPipeline.totalMemoryFlushes += issue.memoryFlushCount ?? 0;
305
+ }
306
+ const contextReportsByRole = issue.contextReportsByRole ?? {};
307
+ const roles = Object.entries(contextReportsByRole).filter(([, report]) => report && typeof report === "object");
308
+ if (roles.length > 0) {
309
+ memoryPipeline.issuesWithContextReports += 1;
310
+ }
311
+ let stageReportsRecorded = false;
312
+ let compactionObserved = false;
313
+ for (const [role, report] of roles) {
314
+ if (!(role in memoryPipeline.byRole) || !report) continue;
315
+ const roleBucket = memoryPipeline.byRole[role];
316
+ roleBucket.reports += 1;
317
+ roleBucket.selectedHits += report.selectedHits ?? 0;
318
+ roleBucket.totalHits += report.totalHits ?? 0;
319
+ roleBucket.discardedHits += report.discardedHits ?? 0;
320
+ for (const layer of report.layers ?? []) {
321
+ if (!(layer.name in memoryPipeline.byLayer)) continue;
322
+ const layerBucket = memoryPipeline.byLayer[layer.name];
323
+ layerBucket.hitCount += layer.hitCount ?? 0;
324
+ layerBucket.selectedHitCount += layer.selectedHitCount ?? 0;
325
+ layerBucket.discardedHitCount += layer.discardedHitCount ?? 0;
326
+ }
327
+ for (const stage of report.stages ?? []) {
328
+ if (!(stage.name in memoryPipeline.byStage)) continue;
329
+ const stageBucket = memoryPipeline.byStage[stage.name];
330
+ stageBucket.reports += 1;
331
+ if (stage.status === "completed") stageBucket.completed += 1;
332
+ if (stage.status === "skipped") stageBucket.skipped += 1;
333
+ stageBucket.totalDurationMs += stage.durationMs ?? 0;
334
+ stageBucket.totalInputCount += stage.inputCount ?? 0;
335
+ stageBucket.totalOutputCount += stage.outputCount ?? 0;
336
+ if (typeof stage.budgetLimit === "number") {
337
+ stageBucket.budgetedRuns += 1;
338
+ stageBucket.totalBudgetLimit += stage.budgetLimit;
339
+ }
340
+ stageReportsRecorded = true;
341
+ if (stage.name === "compact" && stage.status === "completed") compactionObserved = true;
342
+ }
343
+ }
344
+ if (stageReportsRecorded) {
345
+ memoryPipeline.issuesWithStageReports += 1;
346
+ }
347
+ if (compactionObserved) {
348
+ memoryPipeline.issuesWithCompaction += 1;
349
+ }
350
+ const negotiationRuns = resolveAnalyticsContractNegotiationRuns(issue);
351
+ if (negotiationRuns.length === 0) continue;
352
+ const latestRun = negotiationRuns[negotiationRuns.length - 1];
353
+ const profileName = latestRun.reviewProfile?.primary ?? "unknown";
354
+ const bucket = contractNegotiationByProfile[profileName];
355
+ contractNegotiationSummary.negotiatedIssues += 1;
356
+ contractNegotiationSummary.totalRounds += negotiationRuns.length;
357
+ bucket.negotiatedIssues += 1;
358
+ bucket.totalRounds += negotiationRuns.length;
359
+ if (latestRun.decisionStatus === "approved") {
360
+ contractNegotiationSummary.approvedIssues += 1;
361
+ bucket.approvedIssues += 1;
362
+ }
363
+ if (negotiationRuns.length === 1 && negotiationRuns[0]?.decisionStatus === "approved") {
364
+ contractNegotiationSummary.firstPassApprovals += 1;
365
+ bucket.firstPassApprovals += 1;
366
+ }
367
+ if (negotiationRuns.some((entry) => entry.decisionStatus === "revise" || entry.appliedRefinement)) {
368
+ contractNegotiationSummary.revisedIssues += 1;
369
+ bucket.revisedIssues += 1;
370
+ }
371
+ if (negotiationRuns.some((entry) => (entry.blockingConcernsCount ?? 0) > 0)) {
372
+ contractNegotiationSummary.blockingConcernIssues += 1;
373
+ bucket.blockingConcernIssues += 1;
374
+ }
375
+ }
376
+ for (const issue of issues) {
377
+ for (const decision of issue.policyDecisions ?? []) {
378
+ policyDecisionSummary.total += 1;
379
+ if (decision.kind === "harness-mode") policyDecisionSummary.harnessModeChanges += 1;
380
+ if (decision.kind === "checkpoint-policy") policyDecisionSummary.checkpointPolicyChanges += 1;
381
+ if (decision.kind === "review-recovery" && decision.to === "replan") {
382
+ policyDecisionSummary.reviewRecoveryReplans += 1;
383
+ }
384
+ if (decision.kind in policyDecisionSummary.byKind) {
385
+ policyDecisionSummary.byKind[decision.kind] += 1;
386
+ }
387
+ if (decision.basis in policyDecisionSummary.byBasis) {
388
+ policyDecisionSummary.byBasis[decision.basis] += 1;
389
+ }
390
+ }
391
+ }
392
+ const average = (values) => values.length ? values.reduce((sum, value) => sum + value, 0) / values.length : null;
393
+ const finalizeContractBucket = (bucket) => ({
394
+ ...bucket,
395
+ approvalRate: bucket.negotiatedIssues ? bucket.approvedIssues / bucket.negotiatedIssues : null,
396
+ firstPassApprovalRate: bucket.negotiatedIssues ? bucket.firstPassApprovals / bucket.negotiatedIssues : null,
397
+ revisionRate: bucket.negotiatedIssues ? bucket.revisedIssues / bucket.negotiatedIssues : null,
398
+ blockingConcernRate: bucket.negotiatedIssues ? bucket.blockingConcernIssues / bucket.negotiatedIssues : null,
399
+ avgRoundsPerIssue: bucket.negotiatedIssues ? bucket.totalRounds / bucket.negotiatedIssues : null
400
+ });
401
+ const finalizeCheckpointBucket = (bucket) => ({
402
+ ...bucket,
403
+ gatePassRate: bucket.reviewedIssues ? bucket.gatePasses / bucket.reviewedIssues : null,
404
+ firstPassReviewPassRate: bucket.completedReviewedIssues ? bucket.firstPassPasses / bucket.completedReviewedIssues : null,
405
+ reviewReworkRate: bucket.reviewedIssues ? bucket.reworkIssues / bucket.reviewedIssues : null,
406
+ checkpointCatchRate: bucket.reviewedIssues ? bucket.checkpointFailedIssues / bucket.reviewedIssues : null,
407
+ checkpointPassRate: bucket.reviewedIssues ? bucket.checkpointPassedIssues / bucket.reviewedIssues : null,
408
+ avgCheckpointRunsPerIssue: bucket.reviewedIssues ? bucket.checkpointRuns / bucket.reviewedIssues : null
409
+ });
410
+ const finalizeStageBucket = (bucket) => ({
411
+ ...bucket,
412
+ completionRate: bucket.reports ? bucket.completed / bucket.reports : null,
413
+ avgDurationMs: bucket.reports ? bucket.totalDurationMs / bucket.reports : null,
414
+ avgInputCount: bucket.reports ? bucket.totalInputCount / bucket.reports : null,
415
+ avgOutputCount: bucket.reports ? bucket.totalOutputCount / bucket.reports : null,
416
+ avgBudgetLimit: bucket.budgetedRuns ? bucket.totalBudgetLimit / bucket.budgetedRuns : null
417
+ });
418
+ return {
419
+ reviewedIssues: reviewedIssues.length,
420
+ completedReviewedIssues: completedReviewedIssues.length,
421
+ reviewReworkRate: reviewedIssues.length ? reviewReworkIssues.length / reviewedIssues.length : null,
422
+ firstPassReviewPassRate: completedReviewedIssues.length ? firstPassPasses.length / completedReviewedIssues.length : null,
423
+ avgReviewAttempts: average(reviewedIssues.map((issue) => issue.reviewAttempt ?? 0)),
424
+ avgExecuteAttempts: average(reviewedIssues.map((issue) => issue.executeAttempt ?? 0)),
425
+ totalCriteria,
426
+ failedCriteria,
427
+ blockingFailedCriteria,
428
+ advisoryFailedCriteria,
429
+ criteriaFailureRate: totalCriteria ? failedCriteria / totalCriteria : null,
430
+ criteriaByCategory,
431
+ contractNegotiation: {
432
+ ...finalizeContractBucket(contractNegotiationSummary),
433
+ byReviewProfile: {
434
+ "general-quality": finalizeContractBucket(contractNegotiationByProfile["general-quality"]),
435
+ "ui-polish": finalizeContractBucket(contractNegotiationByProfile["ui-polish"]),
436
+ "workflow-fsm": finalizeContractBucket(contractNegotiationByProfile["workflow-fsm"]),
437
+ "integration-safety": finalizeContractBucket(contractNegotiationByProfile["integration-safety"]),
438
+ "api-contract": finalizeContractBucket(contractNegotiationByProfile["api-contract"]),
439
+ "security-hardening": finalizeContractBucket(contractNegotiationByProfile["security-hardening"]),
440
+ unknown: finalizeContractBucket(contractNegotiationByProfile.unknown)
441
+ }
442
+ },
443
+ checkpointPolicy: {
444
+ final_only: finalizeCheckpointBucket(byCheckpointPolicy.final_only),
445
+ checkpointed: finalizeCheckpointBucket(byCheckpointPolicy.checkpointed)
446
+ },
447
+ memoryPipeline: {
448
+ ...memoryPipeline,
449
+ memoryFlushCoverageRate: issues.length ? memoryPipeline.issuesWithMemoryFlushes / issues.length : null,
450
+ contextReportCoverageRate: issues.length ? memoryPipeline.issuesWithContextReports / issues.length : null,
451
+ stageReportCoverageRate: issues.length ? memoryPipeline.issuesWithStageReports / issues.length : null,
452
+ compactionCoverageRate: issues.length ? memoryPipeline.issuesWithCompaction / issues.length : null,
453
+ avgFlushesPerIssueWithMemory: memoryPipeline.issuesWithMemoryFlushes ? memoryPipeline.totalMemoryFlushes / memoryPipeline.issuesWithMemoryFlushes : null,
454
+ byStage: {
455
+ ingest: finalizeStageBucket(memoryPipeline.byStage.ingest),
456
+ "flush-memory": finalizeStageBucket(memoryPipeline.byStage["flush-memory"]),
457
+ retrieve: finalizeStageBucket(memoryPipeline.byStage.retrieve),
458
+ budget: finalizeStageBucket(memoryPipeline.byStage.budget),
459
+ compact: finalizeStageBucket(memoryPipeline.byStage.compact),
460
+ assemble: finalizeStageBucket(memoryPipeline.byStage.assemble)
461
+ }
462
+ },
463
+ policyDecisions: policyDecisionSummary,
464
+ byReviewProfile,
465
+ byReviewerRoute,
466
+ byHarnessMode: {
467
+ solo: {
468
+ ...byHarnessMode.solo,
469
+ reviewReworkRate: byHarnessMode.solo.reviewedIssues ? byHarnessMode.solo.reworkIssues / byHarnessMode.solo.reviewedIssues : null,
470
+ firstPassReviewPassRate: byHarnessMode.solo.completedReviewedIssues ? byHarnessMode.solo.firstPassPasses / byHarnessMode.solo.completedReviewedIssues : null,
471
+ criteriaFailureRate: byHarnessMode.solo.totalCriteria ? byHarnessMode.solo.failedCriteria / byHarnessMode.solo.totalCriteria : null
472
+ },
473
+ standard: {
474
+ ...byHarnessMode.standard,
475
+ reviewReworkRate: byHarnessMode.standard.reviewedIssues ? byHarnessMode.standard.reworkIssues / byHarnessMode.standard.reviewedIssues : null,
476
+ firstPassReviewPassRate: byHarnessMode.standard.completedReviewedIssues ? byHarnessMode.standard.firstPassPasses / byHarnessMode.standard.completedReviewedIssues : null,
477
+ criteriaFailureRate: byHarnessMode.standard.totalCriteria ? byHarnessMode.standard.failedCriteria / byHarnessMode.standard.totalCriteria : null
478
+ },
479
+ contractual: {
480
+ ...byHarnessMode.contractual,
481
+ reviewReworkRate: byHarnessMode.contractual.reviewedIssues ? byHarnessMode.contractual.reworkIssues / byHarnessMode.contractual.reviewedIssues : null,
482
+ firstPassReviewPassRate: byHarnessMode.contractual.completedReviewedIssues ? byHarnessMode.contractual.firstPassPasses / byHarnessMode.contractual.completedReviewedIssues : null,
483
+ criteriaFailureRate: byHarnessMode.contractual.totalCriteria ? byHarnessMode.contractual.failedCriteria / byHarnessMode.contractual.totalCriteria : null
484
+ }
485
+ }
486
+ };
487
+ }
488
+
489
+ export {
490
+ computeMetrics,
491
+ computeQualityGateMetrics
492
+ };
493
+ //# sourceMappingURL=chunk-MVTGAKQK.js.map
@@ -0,0 +1,155 @@
1
+ import {
2
+ ISSUE_STATE_MACHINE_ID,
3
+ findIssueStateMachineTransitionPath,
4
+ getIssueResourceStateApi,
5
+ getIssueStateMachinePlugin,
6
+ getIssueTransitionHistory,
7
+ getStateMachineTransitions,
8
+ visualizeStateMachine
9
+ } from "./chunk-AILXZ2TD.js";
10
+ import {
11
+ transitionIssue
12
+ } from "./chunk-E2EWEYA4.js";
13
+ import {
14
+ markIssueDirty
15
+ } from "./chunk-FJNH3G2Z.js";
16
+ import {
17
+ logger
18
+ } from "./chunk-DVU3CXWA.js";
19
+ import {
20
+ TERMINAL_STATES,
21
+ now
22
+ } from "./chunk-42AMQAJG.js";
23
+
24
+ // src/domains/issue-state.ts
25
+ function syncIssueStateInMemory(issue, targetState, options = {}) {
26
+ const previousState = issue.state;
27
+ if (previousState === targetState) {
28
+ return {
29
+ changed: false,
30
+ previousState,
31
+ currentState: issue.state
32
+ };
33
+ }
34
+ issue.state = targetState;
35
+ issue.updatedAt = now();
36
+ issue.history.push(
37
+ `[${issue.updatedAt}] ${options.reason ?? `Issue state synchronized in memory from ${previousState} to ${targetState}.`}`
38
+ );
39
+ markIssueDirty(issue.id);
40
+ return {
41
+ changed: true,
42
+ previousState,
43
+ currentState: targetState
44
+ };
45
+ }
46
+ async function syncIssueStateFromFsm(issue, options = {}) {
47
+ const sourceState = await getIssueStateMachineState(issue.id);
48
+ if (!sourceState) {
49
+ return {
50
+ changed: false,
51
+ previousState: issue.state,
52
+ currentState: issue.state
53
+ };
54
+ }
55
+ return syncIssueStateInMemory(issue, sourceState, {
56
+ reason: options.reason ?? `Issue state synchronized from FSM source of truth to ${sourceState}.`
57
+ });
58
+ }
59
+ function getIssueStateMachineTransitions() {
60
+ return getStateMachineTransitions();
61
+ }
62
+ function getIssueStateMachineVisualization() {
63
+ return visualizeStateMachine();
64
+ }
65
+ async function getIssueTransitionHistoryForIssue(issueId, options) {
66
+ return getIssueTransitionHistory(issueId, options);
67
+ }
68
+ function getIssueStateMachineTransitionPath(currentState, targetState) {
69
+ return findIssueStateMachineTransitionPath(null, currentState, targetState);
70
+ }
71
+ async function getIssueStateMachineState(issueId) {
72
+ const plugin = getIssueStateMachinePlugin();
73
+ if (!plugin?.getState) return null;
74
+ try {
75
+ const state = await plugin.getState(ISSUE_STATE_MACHINE_ID, issueId);
76
+ if (typeof state === "string") {
77
+ return state;
78
+ }
79
+ } catch {
80
+ return null;
81
+ }
82
+ return null;
83
+ }
84
+ async function deleteIssueStateMachineResourceState(issueId) {
85
+ const fsmApi = getIssueResourceStateApi();
86
+ if (!fsmApi?.delete) return false;
87
+ await fsmApi.delete(issueId);
88
+ return true;
89
+ }
90
+
91
+ // src/commands/transition-issue.command.ts
92
+ async function transitionIssueCommand(input, _deps) {
93
+ const { issue, target, note } = input;
94
+ let currentState = issue.state;
95
+ const syncResult = await syncIssueStateFromFsm(issue, {
96
+ reason: "Transition command reconciled issue state from FSM source of truth."
97
+ });
98
+ if (syncResult.changed) {
99
+ logger.debug(
100
+ {
101
+ issueId: issue.id,
102
+ memoryState: syncResult.previousState,
103
+ fsmState: syncResult.currentState
104
+ },
105
+ "[Transition] Syncing stale in-memory state with FSM"
106
+ );
107
+ currentState = syncResult.currentState;
108
+ }
109
+ if (currentState === target) return;
110
+ const path = getIssueStateMachineTransitionPath(currentState, target);
111
+ if (!path || path.length === 0) {
112
+ throw new Error(`State machine does not allow transition from '${currentState}' to '${target}' for issue ${issue.id}.`);
113
+ }
114
+ for (const event of path) {
115
+ await transitionIssue(issue, event, { note });
116
+ }
117
+ }
118
+
119
+ // src/commands/replan-issue.command.ts
120
+ async function replanIssueCommand(input, deps) {
121
+ const { issue } = input;
122
+ if (issue.planningStatus === "planning") {
123
+ throw new Error("Cannot replan while planning is in progress.");
124
+ }
125
+ if (TERMINAL_STATES.has(issue.state)) {
126
+ throw new Error(`Cannot replan issue in terminal state ${issue.state}.`);
127
+ }
128
+ if (issue.state === "Queued") {
129
+ throw new Error(`Cannot replan issue in ${issue.state} state \u2014 move it out of the execution queue first.`);
130
+ }
131
+ issue.plan = void 0;
132
+ issue.planVersion = (issue.planVersion ?? 0) + 1;
133
+ issue.executeAttempt = 0;
134
+ issue.reviewAttempt = 0;
135
+ await transitionIssueCommand(
136
+ { issue, target: "Planning", note: "Replan requested." },
137
+ deps
138
+ );
139
+ issue.planningStatus = "idle";
140
+ issue.planningError = void 0;
141
+ issue.planningStartedAt = void 0;
142
+ deps.eventStore.addEvent(issue.id, "manual", `Replan requested for ${issue.identifier} \u2014 now at plan v${issue.planVersion}.`);
143
+ }
144
+
145
+ export {
146
+ syncIssueStateInMemory,
147
+ syncIssueStateFromFsm,
148
+ getIssueStateMachineTransitions,
149
+ getIssueStateMachineVisualization,
150
+ getIssueTransitionHistoryForIssue,
151
+ deleteIssueStateMachineResourceState,
152
+ transitionIssueCommand,
153
+ replanIssueCommand
154
+ };
155
+ //# sourceMappingURL=chunk-QQQLP3PL.js.map