@mestreyoda/fabrica 0.2.39 → 0.2.40
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/dist/index.js +211 -26
- package/package.json +1 -1
package/dist/index.js
CHANGED
|
@@ -113905,8 +113905,8 @@ import fsSync from "node:fs";
|
|
|
113905
113905
|
import path5 from "node:path";
|
|
113906
113906
|
import { fileURLToPath as fileURLToPath3 } from "node:url";
|
|
113907
113907
|
function getCurrentVersion() {
|
|
113908
|
-
if ("0.2.
|
|
113909
|
-
return "0.2.
|
|
113908
|
+
if ("0.2.40") {
|
|
113909
|
+
return "0.2.40";
|
|
113910
113910
|
}
|
|
113911
113911
|
try {
|
|
113912
113912
|
const pkgPath = path5.join(THIS_DIR, "..", "..", "package.json");
|
|
@@ -131390,6 +131390,51 @@ init_workflow();
|
|
|
131390
131390
|
init_context3();
|
|
131391
131391
|
init_labels();
|
|
131392
131392
|
|
|
131393
|
+
// lib/services/doctor-snapshot.ts
|
|
131394
|
+
init_audit();
|
|
131395
|
+
async function captureIssueDoctorSnapshot(opts) {
|
|
131396
|
+
try {
|
|
131397
|
+
const result = await runIssueDoctor({
|
|
131398
|
+
workspacePath: opts.workspaceDir,
|
|
131399
|
+
projectSlug: opts.projectSlug,
|
|
131400
|
+
issueId: opts.issueId,
|
|
131401
|
+
runCommand: opts.runCommand,
|
|
131402
|
+
pluginConfig: opts.pluginConfig
|
|
131403
|
+
});
|
|
131404
|
+
await log(opts.workspaceDir, opts.event, {
|
|
131405
|
+
projectSlug: opts.projectSlug,
|
|
131406
|
+
issueId: opts.issueId,
|
|
131407
|
+
trigger: opts.trigger,
|
|
131408
|
+
summary: result.recommendation.summary,
|
|
131409
|
+
likelyNextAction: result.recommendation.likelyNextAction,
|
|
131410
|
+
doctor: {
|
|
131411
|
+
artifact: result.hasArtifact,
|
|
131412
|
+
progressState: result.lifecycle.progressState,
|
|
131413
|
+
dispatchCycleId: result.lifecycle.dispatchCycleId,
|
|
131414
|
+
dispatchRunId: result.lifecycle.dispatchRunId,
|
|
131415
|
+
prUrl: result.pr?.url ?? null,
|
|
131416
|
+
prState: result.pr?.state ?? null,
|
|
131417
|
+
labels: result.issue?.labels ?? [],
|
|
131418
|
+
convergenceCause: result.convergence.cause,
|
|
131419
|
+
convergenceAction: result.convergence.action,
|
|
131420
|
+
convergenceRetryCount: result.convergence.retryCount,
|
|
131421
|
+
convergenceHeadSha: result.convergence.headSha,
|
|
131422
|
+
headShaChangedSinceLastConvergence: result.convergence.headShaChangedSinceLastConvergence
|
|
131423
|
+
},
|
|
131424
|
+
...opts.extra ?? {}
|
|
131425
|
+
});
|
|
131426
|
+
} catch (error48) {
|
|
131427
|
+
await log(opts.workspaceDir, `${opts.event}_failed`, {
|
|
131428
|
+
projectSlug: opts.projectSlug,
|
|
131429
|
+
issueId: opts.issueId,
|
|
131430
|
+
trigger: opts.trigger,
|
|
131431
|
+
error: error48 instanceof Error ? error48.message : String(error48),
|
|
131432
|
+
...opts.extra ?? {}
|
|
131433
|
+
}).catch(() => {
|
|
131434
|
+
});
|
|
131435
|
+
}
|
|
131436
|
+
}
|
|
131437
|
+
|
|
131393
131438
|
// lib/services/post-pr-convergence.ts
|
|
131394
131439
|
init_types3();
|
|
131395
131440
|
function classifyConvergenceCause(reason) {
|
|
@@ -132054,6 +132099,23 @@ ${validationReason}`;
|
|
|
132054
132099
|
lastConvergenceHeadSha: convergence.progressHeadSha
|
|
132055
132100
|
}).catch(() => {
|
|
132056
132101
|
});
|
|
132102
|
+
if (convergenceIssueRuntime?.currentPrUrl || convergence.action === "escalate_human") {
|
|
132103
|
+
await captureIssueDoctorSnapshot({
|
|
132104
|
+
workspaceDir: opts.workspaceDir,
|
|
132105
|
+
projectSlug: context2.projectSlug,
|
|
132106
|
+
issueId: context2.issueId,
|
|
132107
|
+
runCommand: opts.runCommand,
|
|
132108
|
+
pluginConfig: opts.pluginConfig,
|
|
132109
|
+
event: "doctor_snapshot",
|
|
132110
|
+
trigger: convergence.action === "escalate_human" ? "worker_completion_escalated" : "worker_completion_blocked_with_artifact",
|
|
132111
|
+
extra: {
|
|
132112
|
+
convergenceCause: convergence.cause,
|
|
132113
|
+
convergenceAction: convergence.action,
|
|
132114
|
+
convergenceRetryCount: convergence.retryCount
|
|
132115
|
+
}
|
|
132116
|
+
}).catch(() => {
|
|
132117
|
+
});
|
|
132118
|
+
}
|
|
132057
132119
|
await executeCompletion({
|
|
132058
132120
|
workspaceDir: opts.workspaceDir,
|
|
132059
132121
|
projectSlug: context2.projectSlug,
|
|
@@ -132741,6 +132803,22 @@ async function checkWorkerHealth(opts) {
|
|
|
132741
132803
|
toLabel: convergence.targetLabel,
|
|
132742
132804
|
deliveryState
|
|
132743
132805
|
});
|
|
132806
|
+
if (runCommand) {
|
|
132807
|
+
await captureIssueDoctorSnapshot({
|
|
132808
|
+
workspaceDir,
|
|
132809
|
+
projectSlug,
|
|
132810
|
+
issueId: issueIdNum,
|
|
132811
|
+
runCommand,
|
|
132812
|
+
event: "doctor_snapshot",
|
|
132813
|
+
trigger: convergence.action === "escalate_human" ? "completion_recovery_escalated" : "completion_recovery_requeued",
|
|
132814
|
+
extra: {
|
|
132815
|
+
convergenceCause: convergence.cause,
|
|
132816
|
+
convergenceAction: convergence.action,
|
|
132817
|
+
convergenceRetryCount: convergence.retryCount
|
|
132818
|
+
}
|
|
132819
|
+
}).catch(() => {
|
|
132820
|
+
});
|
|
132821
|
+
}
|
|
132744
132822
|
}
|
|
132745
132823
|
}
|
|
132746
132824
|
fixes.push(fix);
|
|
@@ -133109,6 +133187,23 @@ async function checkWorkerHealth(opts) {
|
|
|
133109
133187
|
idleMinutes: Math.round(quietMinutes),
|
|
133110
133188
|
deliveryState
|
|
133111
133189
|
});
|
|
133190
|
+
if (runCommand) {
|
|
133191
|
+
await captureIssueDoctorSnapshot({
|
|
133192
|
+
workspaceDir,
|
|
133193
|
+
projectSlug,
|
|
133194
|
+
issueId: issueIdNum,
|
|
133195
|
+
runCommand,
|
|
133196
|
+
event: "doctor_snapshot",
|
|
133197
|
+
trigger: convergence.action === "escalate_human" ? "stalled_with_artifact_escalated" : "stalled_with_artifact_requeued",
|
|
133198
|
+
extra: {
|
|
133199
|
+
convergenceCause: convergence.cause,
|
|
133200
|
+
convergenceAction: convergence.action,
|
|
133201
|
+
convergenceRetryCount: convergence.retryCount,
|
|
133202
|
+
idleMinutes: Math.round(quietMinutes)
|
|
133203
|
+
}
|
|
133204
|
+
}).catch(() => {
|
|
133205
|
+
});
|
|
133206
|
+
}
|
|
133112
133207
|
}
|
|
133113
133208
|
}
|
|
133114
133209
|
fixes.push(fix);
|
|
@@ -149129,12 +149224,29 @@ async function readAuditLines(filePath) {
|
|
|
149129
149224
|
}
|
|
149130
149225
|
return entries;
|
|
149131
149226
|
}
|
|
149227
|
+
function keyFor(entry) {
|
|
149228
|
+
const projectSlug = entry.projectSlug ?? entry.project ?? null;
|
|
149229
|
+
const issueId = entry.issueId ?? entry.issue ?? null;
|
|
149230
|
+
if (!projectSlug || issueId == null) return null;
|
|
149231
|
+
return `${projectSlug}:${issueId}`;
|
|
149232
|
+
}
|
|
149233
|
+
function normalizeCause(entry) {
|
|
149234
|
+
const cause = entry.convergenceCause ?? entry.reason ?? null;
|
|
149235
|
+
return cause ? String(cause) : null;
|
|
149236
|
+
}
|
|
149132
149237
|
async function computeMetrics(workspaceDir) {
|
|
149133
149238
|
const auditLogPath = join4(workspaceDir, DATA_DIR, "log", "audit.log");
|
|
149239
|
+
const bakEntries3 = await readAuditLines(`${auditLogPath}.3.bak`);
|
|
149134
149240
|
const bakEntries2 = await readAuditLines(`${auditLogPath}.2.bak`);
|
|
149135
149241
|
const bakEntries = await readAuditLines(`${auditLogPath}.bak`);
|
|
149136
149242
|
const currentEntries = await readAuditLines(auditLogPath);
|
|
149137
|
-
|
|
149243
|
+
const entries = [...bakEntries3, ...bakEntries2, ...bakEntries, ...currentEntries];
|
|
149244
|
+
const projectsData = await readProjects(workspaceDir).catch(() => ({ projects: {} }));
|
|
149245
|
+
const stackByProject = /* @__PURE__ */ new Map();
|
|
149246
|
+
for (const [slug, project] of Object.entries(projectsData.projects ?? {})) {
|
|
149247
|
+
const stack = project.stack ?? project.environment?.stack ?? null;
|
|
149248
|
+
if (stack) stackByProject.set(slug, String(stack));
|
|
149249
|
+
}
|
|
149138
149250
|
const entriesScanned = entries.length;
|
|
149139
149251
|
let dispatches = 0;
|
|
149140
149252
|
let completionsTotal = 0;
|
|
@@ -149144,16 +149256,40 @@ async function computeMetrics(workspaceDir) {
|
|
|
149144
149256
|
let completionsOther = 0;
|
|
149145
149257
|
let conflictsDetected = 0;
|
|
149146
149258
|
let sessionBudgetResets = 0;
|
|
149259
|
+
let humanEscalations = 0;
|
|
149260
|
+
const causeCounts = {};
|
|
149147
149261
|
const dispatchTimes = /* @__PURE__ */ new Map();
|
|
149262
|
+
const firstPrTimes = /* @__PURE__ */ new Map();
|
|
149148
149263
|
const completionDeltas = [];
|
|
149264
|
+
const firstPrDeltas = [];
|
|
149265
|
+
const stackMetrics = /* @__PURE__ */ new Map();
|
|
149266
|
+
function stackBucket(entry) {
|
|
149267
|
+
const slug = entry.projectSlug ?? entry.project ?? null;
|
|
149268
|
+
const stack = entry.stack ?? (slug ? stackByProject.get(String(slug)) : null) ?? "unknown";
|
|
149269
|
+
if (!stackMetrics.has(String(stack))) {
|
|
149270
|
+
stackMetrics.set(String(stack), {
|
|
149271
|
+
issues: /* @__PURE__ */ new Set(),
|
|
149272
|
+
dispatches: 0,
|
|
149273
|
+
escalations: 0,
|
|
149274
|
+
causeCounts: {},
|
|
149275
|
+
completionDeltas: [],
|
|
149276
|
+
firstPrDeltas: []
|
|
149277
|
+
});
|
|
149278
|
+
}
|
|
149279
|
+
return String(stack);
|
|
149280
|
+
}
|
|
149149
149281
|
for (const entry of entries) {
|
|
149282
|
+
const issueKey = keyFor(entry);
|
|
149283
|
+
const stack = stackBucket(entry);
|
|
149284
|
+
const stackBucketState = stackMetrics.get(stack);
|
|
149285
|
+
if (issueKey) stackBucketState.issues.add(issueKey);
|
|
149150
149286
|
switch (entry.event) {
|
|
149151
|
-
case "dispatch":
|
|
149287
|
+
case "dispatch": {
|
|
149152
149288
|
dispatches++;
|
|
149153
|
-
|
|
149154
|
-
|
|
149155
|
-
}
|
|
149289
|
+
stackBucketState.dispatches++;
|
|
149290
|
+
if (issueKey) dispatchTimes.set(issueKey, Date.parse(entry.ts));
|
|
149156
149291
|
break;
|
|
149292
|
+
}
|
|
149157
149293
|
case "work_finish": {
|
|
149158
149294
|
completionsTotal++;
|
|
149159
149295
|
const result = String(entry.result ?? "");
|
|
@@ -149161,14 +149297,13 @@ async function computeMetrics(workspaceDir) {
|
|
|
149161
149297
|
else if (result === "pass") completionsPass++;
|
|
149162
149298
|
else if (result === "fail") completionsFail++;
|
|
149163
149299
|
else completionsOther++;
|
|
149164
|
-
if (
|
|
149165
|
-
const
|
|
149166
|
-
const
|
|
149167
|
-
if (dispatchTime !== void 0) {
|
|
149168
|
-
const
|
|
149169
|
-
|
|
149170
|
-
|
|
149171
|
-
}
|
|
149300
|
+
if (issueKey) {
|
|
149301
|
+
const dispatchTime = dispatchTimes.get(issueKey);
|
|
149302
|
+
const completionTime = Date.parse(entry.ts);
|
|
149303
|
+
if (dispatchTime !== void 0 && !Number.isNaN(completionTime) && completionTime > dispatchTime) {
|
|
149304
|
+
const delta = (completionTime - dispatchTime) / 6e4;
|
|
149305
|
+
completionDeltas.push(delta);
|
|
149306
|
+
stackBucketState.completionDeltas.push(delta);
|
|
149172
149307
|
}
|
|
149173
149308
|
}
|
|
149174
149309
|
break;
|
|
@@ -149182,9 +149317,47 @@ async function computeMetrics(workspaceDir) {
|
|
|
149182
149317
|
case "session_budget_reset":
|
|
149183
149318
|
sessionBudgetResets++;
|
|
149184
149319
|
break;
|
|
149320
|
+
case "pr_discovered_via_polling":
|
|
149321
|
+
case "pr_updated_via_polling": {
|
|
149322
|
+
if (issueKey && !firstPrTimes.has(issueKey)) {
|
|
149323
|
+
const prTime = Date.parse(entry.ts);
|
|
149324
|
+
const dispatchTime = dispatchTimes.get(issueKey);
|
|
149325
|
+
firstPrTimes.set(issueKey, prTime);
|
|
149326
|
+
if (dispatchTime !== void 0 && !Number.isNaN(prTime) && prTime > dispatchTime) {
|
|
149327
|
+
const delta = (prTime - dispatchTime) / 6e4;
|
|
149328
|
+
firstPrDeltas.push(delta);
|
|
149329
|
+
stackBucketState.firstPrDeltas.push(delta);
|
|
149330
|
+
}
|
|
149331
|
+
}
|
|
149332
|
+
break;
|
|
149333
|
+
}
|
|
149334
|
+
case "worker_completion_skipped":
|
|
149335
|
+
case "doctor_snapshot":
|
|
149336
|
+
case "health_fix_applied": {
|
|
149337
|
+
const cause = normalizeCause(entry);
|
|
149338
|
+
if (cause) {
|
|
149339
|
+
causeCounts[cause] = (causeCounts[cause] ?? 0) + 1;
|
|
149340
|
+
stackBucketState.causeCounts[cause] = (stackBucketState.causeCounts[cause] ?? 0) + 1;
|
|
149341
|
+
}
|
|
149342
|
+
if (entry.convergenceAction === "escalate_human" || entry.action === "escalate_human") {
|
|
149343
|
+
humanEscalations++;
|
|
149344
|
+
stackBucketState.escalations++;
|
|
149345
|
+
}
|
|
149346
|
+
break;
|
|
149347
|
+
}
|
|
149185
149348
|
}
|
|
149186
149349
|
}
|
|
149187
|
-
const
|
|
149350
|
+
const avg = (values) => values.length ? values.reduce((a, b) => a + b, 0) / values.length : null;
|
|
149351
|
+
const stackMetricsObject = Object.fromEntries(
|
|
149352
|
+
[...stackMetrics.entries()].map(([stack, data]) => [stack, {
|
|
149353
|
+
issues: data.issues.size,
|
|
149354
|
+
dispatches: data.dispatches,
|
|
149355
|
+
escalations: data.escalations,
|
|
149356
|
+
causeCounts: data.causeCounts,
|
|
149357
|
+
avgDispatchToCompletionMinutes: avg(data.completionDeltas),
|
|
149358
|
+
avgDispatchToFirstPrMinutes: avg(data.firstPrDeltas)
|
|
149359
|
+
}])
|
|
149360
|
+
);
|
|
149188
149361
|
return {
|
|
149189
149362
|
entriesScanned,
|
|
149190
149363
|
dispatches,
|
|
@@ -149195,28 +149368,40 @@ async function computeMetrics(workspaceDir) {
|
|
|
149195
149368
|
fail: completionsFail,
|
|
149196
149369
|
other: completionsOther
|
|
149197
149370
|
},
|
|
149198
|
-
avgDispatchToCompletionMinutes,
|
|
149371
|
+
avgDispatchToCompletionMinutes: avg(completionDeltas),
|
|
149372
|
+
avgDispatchToFirstPrMinutes: avg(firstPrDeltas),
|
|
149199
149373
|
conflictsDetected,
|
|
149200
149374
|
sessionBudgetResets,
|
|
149375
|
+
humanEscalations,
|
|
149376
|
+
causeCounts,
|
|
149377
|
+
stackMetrics: stackMetricsObject,
|
|
149201
149378
|
auditLogPath
|
|
149202
149379
|
};
|
|
149203
149380
|
}
|
|
149204
149381
|
function formatMetrics(metrics2) {
|
|
149205
149382
|
const lines = [
|
|
149206
|
-
`Fabrica \u2014
|
|
149383
|
+
`Fabrica \u2014 M\xE9tricas (${metrics2.entriesScanned} entradas do audit.log)`,
|
|
149207
149384
|
` Dispatches: ${metrics2.dispatches}`
|
|
149208
149385
|
];
|
|
149209
149386
|
const c = metrics2.completions;
|
|
149210
|
-
lines.push(
|
|
149211
|
-
|
|
149212
|
-
);
|
|
149213
|
-
if (metrics2.avgDispatchToCompletionMinutes !== null) {
|
|
149214
|
-
lines.push(` Tempo medio dispatch \u2192 completion: ${metrics2.avgDispatchToCompletionMinutes.toFixed(1)} min`);
|
|
149215
|
-
} else {
|
|
149216
|
-
lines.push(` Tempo medio dispatch \u2192 completion: n/a`);
|
|
149217
|
-
}
|
|
149387
|
+
lines.push(` Conclus\xF5es: ${c.total} (done: ${c.done}, pass: ${c.pass}, fail: ${c.fail}${c.other > 0 ? `, other: ${c.other}` : ""})`);
|
|
149388
|
+
lines.push(` Tempo m\xE9dio dispatch \u2192 completion: ${metrics2.avgDispatchToCompletionMinutes?.toFixed(1) ?? "n/a"} min`);
|
|
149389
|
+
lines.push(` Tempo m\xE9dio dispatch \u2192 primeira PR: ${metrics2.avgDispatchToFirstPrMinutes?.toFixed(1) ?? "n/a"} min`);
|
|
149218
149390
|
lines.push(` Conflitos detectados: ${metrics2.conflictsDetected}`);
|
|
149219
149391
|
lines.push(` Session budget resets: ${metrics2.sessionBudgetResets}`);
|
|
149392
|
+
lines.push(` Escalonamentos humanos: ${metrics2.humanEscalations}`);
|
|
149393
|
+
if (Object.keys(metrics2.causeCounts).length > 0) {
|
|
149394
|
+
lines.push(" Causas tipadas:");
|
|
149395
|
+
for (const [cause, count] of Object.entries(metrics2.causeCounts).sort((a, b) => b[1] - a[1])) {
|
|
149396
|
+
lines.push(` - ${cause}: ${count}`);
|
|
149397
|
+
}
|
|
149398
|
+
}
|
|
149399
|
+
if (Object.keys(metrics2.stackMetrics).length > 0) {
|
|
149400
|
+
lines.push(" Por stack:");
|
|
149401
|
+
for (const [stack, data] of Object.entries(metrics2.stackMetrics)) {
|
|
149402
|
+
lines.push(` - ${stack}: issues=${data.issues}, dispatches=${data.dispatches}, escalations=${data.escalations}, avgPR=${data.avgDispatchToFirstPrMinutes?.toFixed(1) ?? "n/a"}m, avgDone=${data.avgDispatchToCompletionMinutes?.toFixed(1) ?? "n/a"}m`);
|
|
149403
|
+
}
|
|
149404
|
+
}
|
|
149220
149405
|
return lines.join("\n");
|
|
149221
149406
|
}
|
|
149222
149407
|
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@mestreyoda/fabrica",
|
|
3
|
-
"version": "0.2.
|
|
3
|
+
"version": "0.2.40",
|
|
4
4
|
"description": "Autonomous software engineering pipeline for OpenClaw. Turns ideas into deployed code via intake, dispatch, review, test, and merge.",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "./dist/index.js",
|