@reportforge/playwright-pdf 0.19.1 → 0.20.1
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/CHANGELOG.md +31 -0
- package/README.md +3 -2
- package/dist/cli/index.js +1005 -176
- package/dist/index.d.mts +38 -2
- package/dist/index.d.ts +38 -2
- package/dist/index.js +327 -151
- package/dist/index.mjs +402 -226
- package/package.json +1 -1
package/dist/cli/index.js
CHANGED
|
@@ -1588,13 +1588,13 @@ var require_ast = __commonJS({
|
|
|
1588
1588
|
helperExpression: function helperExpression(node) {
|
|
1589
1589
|
return node.type === "SubExpression" || (node.type === "MustacheStatement" || node.type === "BlockStatement") && !!(node.params && node.params.length || node.hash);
|
|
1590
1590
|
},
|
|
1591
|
-
scopedId: function scopedId(
|
|
1592
|
-
return /^\.|this\b/.test(
|
|
1591
|
+
scopedId: function scopedId(path14) {
|
|
1592
|
+
return /^\.|this\b/.test(path14.original);
|
|
1593
1593
|
},
|
|
1594
1594
|
// an ID is simple if it only has one part, and that part is not
|
|
1595
1595
|
// `..` or `this`.
|
|
1596
|
-
simpleId: function simpleId(
|
|
1597
|
-
return
|
|
1596
|
+
simpleId: function simpleId(path14) {
|
|
1597
|
+
return path14.parts.length === 1 && !AST.helpers.scopedId(path14) && !path14.depth;
|
|
1598
1598
|
}
|
|
1599
1599
|
}
|
|
1600
1600
|
};
|
|
@@ -2668,12 +2668,12 @@ var require_helpers2 = __commonJS({
|
|
|
2668
2668
|
loc
|
|
2669
2669
|
};
|
|
2670
2670
|
}
|
|
2671
|
-
function prepareMustache(
|
|
2671
|
+
function prepareMustache(path14, params, hash, open, strip, locInfo) {
|
|
2672
2672
|
var escapeFlag = open.charAt(3) || open.charAt(2), escaped = escapeFlag !== "{" && escapeFlag !== "&";
|
|
2673
2673
|
var decorator = /\*/.test(open);
|
|
2674
2674
|
return {
|
|
2675
2675
|
type: decorator ? "Decorator" : "MustacheStatement",
|
|
2676
|
-
path:
|
|
2676
|
+
path: path14,
|
|
2677
2677
|
params,
|
|
2678
2678
|
hash,
|
|
2679
2679
|
escaped,
|
|
@@ -2993,9 +2993,9 @@ var require_compiler = __commonJS({
|
|
|
2993
2993
|
},
|
|
2994
2994
|
DecoratorBlock: function DecoratorBlock(decorator) {
|
|
2995
2995
|
var program = decorator.program && this.compileProgram(decorator.program);
|
|
2996
|
-
var params = this.setupFullMustacheParams(decorator, program, void 0),
|
|
2996
|
+
var params = this.setupFullMustacheParams(decorator, program, void 0), path14 = decorator.path;
|
|
2997
2997
|
this.useDecorators = true;
|
|
2998
|
-
this.opcode("registerDecorator", params.length,
|
|
2998
|
+
this.opcode("registerDecorator", params.length, path14.original);
|
|
2999
2999
|
},
|
|
3000
3000
|
PartialStatement: function PartialStatement(partial) {
|
|
3001
3001
|
this.usePartial = true;
|
|
@@ -3059,46 +3059,46 @@ var require_compiler = __commonJS({
|
|
|
3059
3059
|
}
|
|
3060
3060
|
},
|
|
3061
3061
|
ambiguousSexpr: function ambiguousSexpr(sexpr, program, inverse) {
|
|
3062
|
-
var
|
|
3063
|
-
this.opcode("getContext",
|
|
3062
|
+
var path14 = sexpr.path, name = path14.parts[0], isBlock = program != null || inverse != null;
|
|
3063
|
+
this.opcode("getContext", path14.depth);
|
|
3064
3064
|
this.opcode("pushProgram", program);
|
|
3065
3065
|
this.opcode("pushProgram", inverse);
|
|
3066
|
-
|
|
3067
|
-
this.accept(
|
|
3066
|
+
path14.strict = true;
|
|
3067
|
+
this.accept(path14);
|
|
3068
3068
|
this.opcode("invokeAmbiguous", name, isBlock);
|
|
3069
3069
|
},
|
|
3070
3070
|
simpleSexpr: function simpleSexpr(sexpr) {
|
|
3071
|
-
var
|
|
3072
|
-
|
|
3073
|
-
this.accept(
|
|
3071
|
+
var path14 = sexpr.path;
|
|
3072
|
+
path14.strict = true;
|
|
3073
|
+
this.accept(path14);
|
|
3074
3074
|
this.opcode("resolvePossibleLambda");
|
|
3075
3075
|
},
|
|
3076
3076
|
helperSexpr: function helperSexpr(sexpr, program, inverse) {
|
|
3077
|
-
var params = this.setupFullMustacheParams(sexpr, program, inverse),
|
|
3077
|
+
var params = this.setupFullMustacheParams(sexpr, program, inverse), path14 = sexpr.path, name = path14.parts[0];
|
|
3078
3078
|
if (this.options.knownHelpers[name]) {
|
|
3079
3079
|
this.opcode("invokeKnownHelper", params.length, name);
|
|
3080
3080
|
} else if (this.options.knownHelpersOnly) {
|
|
3081
3081
|
throw new _exception2["default"]("You specified knownHelpersOnly, but used the unknown helper " + name, sexpr);
|
|
3082
3082
|
} else {
|
|
3083
|
-
|
|
3084
|
-
|
|
3085
|
-
this.accept(
|
|
3086
|
-
this.opcode("invokeHelper", params.length,
|
|
3083
|
+
path14.strict = true;
|
|
3084
|
+
path14.falsy = true;
|
|
3085
|
+
this.accept(path14);
|
|
3086
|
+
this.opcode("invokeHelper", params.length, path14.original, _ast2["default"].helpers.simpleId(path14));
|
|
3087
3087
|
}
|
|
3088
3088
|
},
|
|
3089
|
-
PathExpression: function PathExpression(
|
|
3090
|
-
this.addDepth(
|
|
3091
|
-
this.opcode("getContext",
|
|
3092
|
-
var name =
|
|
3089
|
+
PathExpression: function PathExpression(path14) {
|
|
3090
|
+
this.addDepth(path14.depth);
|
|
3091
|
+
this.opcode("getContext", path14.depth);
|
|
3092
|
+
var name = path14.parts[0], scoped = _ast2["default"].helpers.scopedId(path14), blockParamId = !path14.depth && !scoped && this.blockParamIndex(name);
|
|
3093
3093
|
if (blockParamId) {
|
|
3094
|
-
this.opcode("lookupBlockParam", blockParamId,
|
|
3094
|
+
this.opcode("lookupBlockParam", blockParamId, path14.parts);
|
|
3095
3095
|
} else if (!name) {
|
|
3096
3096
|
this.opcode("pushContext");
|
|
3097
|
-
} else if (
|
|
3097
|
+
} else if (path14.data) {
|
|
3098
3098
|
this.options.data = true;
|
|
3099
|
-
this.opcode("lookupData",
|
|
3099
|
+
this.opcode("lookupData", path14.depth, path14.parts, path14.strict);
|
|
3100
3100
|
} else {
|
|
3101
|
-
this.opcode("lookupOnContext",
|
|
3101
|
+
this.opcode("lookupOnContext", path14.parts, path14.falsy, path14.strict, scoped);
|
|
3102
3102
|
}
|
|
3103
3103
|
},
|
|
3104
3104
|
StringLiteral: function StringLiteral(string) {
|
|
@@ -3454,16 +3454,16 @@ var require_util = __commonJS({
|
|
|
3454
3454
|
}
|
|
3455
3455
|
exports2.urlGenerate = urlGenerate;
|
|
3456
3456
|
function normalize(aPath) {
|
|
3457
|
-
var
|
|
3457
|
+
var path14 = aPath;
|
|
3458
3458
|
var url = urlParse(aPath);
|
|
3459
3459
|
if (url) {
|
|
3460
3460
|
if (!url.path) {
|
|
3461
3461
|
return aPath;
|
|
3462
3462
|
}
|
|
3463
|
-
|
|
3463
|
+
path14 = url.path;
|
|
3464
3464
|
}
|
|
3465
|
-
var isAbsolute = exports2.isAbsolute(
|
|
3466
|
-
var parts =
|
|
3465
|
+
var isAbsolute = exports2.isAbsolute(path14);
|
|
3466
|
+
var parts = path14.split(/\/+/);
|
|
3467
3467
|
for (var part, up = 0, i = parts.length - 1; i >= 0; i--) {
|
|
3468
3468
|
part = parts[i];
|
|
3469
3469
|
if (part === ".") {
|
|
@@ -3480,15 +3480,15 @@ var require_util = __commonJS({
|
|
|
3480
3480
|
}
|
|
3481
3481
|
}
|
|
3482
3482
|
}
|
|
3483
|
-
|
|
3484
|
-
if (
|
|
3485
|
-
|
|
3483
|
+
path14 = parts.join("/");
|
|
3484
|
+
if (path14 === "") {
|
|
3485
|
+
path14 = isAbsolute ? "/" : ".";
|
|
3486
3486
|
}
|
|
3487
3487
|
if (url) {
|
|
3488
|
-
url.path =
|
|
3488
|
+
url.path = path14;
|
|
3489
3489
|
return urlGenerate(url);
|
|
3490
3490
|
}
|
|
3491
|
-
return
|
|
3491
|
+
return path14;
|
|
3492
3492
|
}
|
|
3493
3493
|
exports2.normalize = normalize;
|
|
3494
3494
|
function join(aRoot, aPath) {
|
|
@@ -6291,8 +6291,8 @@ var require_printer = __commonJS({
|
|
|
6291
6291
|
return this.accept(sexpr.path) + " " + params + hash;
|
|
6292
6292
|
};
|
|
6293
6293
|
PrintVisitor.prototype.PathExpression = function(id) {
|
|
6294
|
-
var
|
|
6295
|
-
return (id.data ? "@" : "") + "PATH:" +
|
|
6294
|
+
var path14 = id.parts.join("/");
|
|
6295
|
+
return (id.data ? "@" : "") + "PATH:" + path14;
|
|
6296
6296
|
};
|
|
6297
6297
|
PrintVisitor.prototype.StringLiteral = function(string) {
|
|
6298
6298
|
return '"' + string.value + '"';
|
|
@@ -6333,8 +6333,8 @@ var require_lib = __commonJS({
|
|
|
6333
6333
|
handlebars.print = printer.print;
|
|
6334
6334
|
module2.exports = handlebars;
|
|
6335
6335
|
function extension(module3, filename) {
|
|
6336
|
-
var
|
|
6337
|
-
var templateString =
|
|
6336
|
+
var fs14 = require("fs");
|
|
6337
|
+
var templateString = fs14.readFileSync(filename, "utf8");
|
|
6338
6338
|
module3.exports = handlebars.compile(templateString);
|
|
6339
6339
|
}
|
|
6340
6340
|
if (typeof require !== "undefined" && require.extensions) {
|
|
@@ -6854,8 +6854,8 @@ var init_engine = __esm({
|
|
|
6854
6854
|
};
|
|
6855
6855
|
return map[v] ?? (typeof v === "string" ? v.toUpperCase() : v);
|
|
6856
6856
|
});
|
|
6857
|
-
this.handlebars.registerHelper("coverageBand", (
|
|
6858
|
-
const n = Number(
|
|
6857
|
+
this.handlebars.registerHelper("coverageBand", (pct2) => {
|
|
6858
|
+
const n = Number(pct2);
|
|
6859
6859
|
return n >= 80 ? "high" : n >= 50 ? "mid" : "low";
|
|
6860
6860
|
});
|
|
6861
6861
|
this.handlebars.registerHelper("addOne", (n) => (n ?? 0) + 1);
|
|
@@ -7005,7 +7005,7 @@ var require_util2 = __commonJS({
|
|
|
7005
7005
|
"node_modules/chrome-finder/lib/util.js"(exports2, module2) {
|
|
7006
7006
|
"use strict";
|
|
7007
7007
|
init_cjs_shims();
|
|
7008
|
-
var
|
|
7008
|
+
var fs14 = require("fs");
|
|
7009
7009
|
var newLineRegex = /\r?\n/;
|
|
7010
7010
|
function sort(installations, priorities) {
|
|
7011
7011
|
const defaultPriority = 10;
|
|
@@ -7023,7 +7023,7 @@ var require_util2 = __commonJS({
|
|
|
7023
7023
|
return false;
|
|
7024
7024
|
}
|
|
7025
7025
|
try {
|
|
7026
|
-
|
|
7026
|
+
fs14.accessSync(file);
|
|
7027
7027
|
return true;
|
|
7028
7028
|
} catch (e) {
|
|
7029
7029
|
return false;
|
|
@@ -7034,7 +7034,7 @@ var require_util2 = __commonJS({
|
|
|
7034
7034
|
return false;
|
|
7035
7035
|
}
|
|
7036
7036
|
try {
|
|
7037
|
-
var stat =
|
|
7037
|
+
var stat = fs14.statSync(file);
|
|
7038
7038
|
return stat && typeof stat.isFile === "function" && stat.isFile();
|
|
7039
7039
|
} catch (e) {
|
|
7040
7040
|
return false;
|
|
@@ -7054,7 +7054,7 @@ var require_win32 = __commonJS({
|
|
|
7054
7054
|
"node_modules/chrome-finder/lib/win32.js"(exports2, module2) {
|
|
7055
7055
|
"use strict";
|
|
7056
7056
|
init_cjs_shims();
|
|
7057
|
-
var
|
|
7057
|
+
var path14 = require("path");
|
|
7058
7058
|
var { canAccess } = require_util2();
|
|
7059
7059
|
function win32() {
|
|
7060
7060
|
const installations = [];
|
|
@@ -7068,7 +7068,7 @@ var require_win32 = __commonJS({
|
|
|
7068
7068
|
const prefixes = [process.env.LOCALAPPDATA, process.env.PROGRAMFILES, process.env["PROGRAMFILES(X86)"]];
|
|
7069
7069
|
prefixes.forEach((prefix) => suffixes.forEach((suffix) => {
|
|
7070
7070
|
if (prefix) {
|
|
7071
|
-
const chromePath =
|
|
7071
|
+
const chromePath = path14.join(prefix, suffix);
|
|
7072
7072
|
if (canAccess(chromePath)) {
|
|
7073
7073
|
installations.push(chromePath);
|
|
7074
7074
|
}
|
|
@@ -7086,7 +7086,7 @@ var require_darwin = __commonJS({
|
|
|
7086
7086
|
"use strict";
|
|
7087
7087
|
init_cjs_shims();
|
|
7088
7088
|
var { execSync: execSync2 } = require("child_process");
|
|
7089
|
-
var
|
|
7089
|
+
var path14 = require("path");
|
|
7090
7090
|
var { canAccess, newLineRegex, sort } = require_util2();
|
|
7091
7091
|
function darwin() {
|
|
7092
7092
|
const suffixes = ["/Contents/MacOS/Google Chrome Canary", "/Contents/MacOS/Google Chrome", "/Contents/MacOS/Chromium"];
|
|
@@ -7096,7 +7096,7 @@ var require_darwin = __commonJS({
|
|
|
7096
7096
|
${LSREGISTER} -dump | grep -E -i -o '/.+(google chrome( canary)?|chromium)\\.app(\\s|$)' | grep -E -v 'Caches|TimeMachine|Temporary|/Volumes|\\.Trash'
|
|
7097
7097
|
`).toString().split(newLineRegex).forEach((inst) => {
|
|
7098
7098
|
suffixes.forEach((suffix) => {
|
|
7099
|
-
const execPath =
|
|
7099
|
+
const execPath = path14.join(inst.trim(), suffix);
|
|
7100
7100
|
if (canAccess(execPath)) {
|
|
7101
7101
|
installations.push(execPath);
|
|
7102
7102
|
}
|
|
@@ -7125,8 +7125,8 @@ var require_linux = __commonJS({
|
|
|
7125
7125
|
"use strict";
|
|
7126
7126
|
init_cjs_shims();
|
|
7127
7127
|
var { execSync: execSync2, execFileSync: execFileSync2 } = require("child_process");
|
|
7128
|
-
var
|
|
7129
|
-
var
|
|
7128
|
+
var path14 = require("path");
|
|
7129
|
+
var fs14 = require("fs");
|
|
7130
7130
|
var { canAccess, sort, isExecutable, newLineRegex } = require_util2();
|
|
7131
7131
|
function findChromeExecutablesForLinuxDesktop(folder) {
|
|
7132
7132
|
const argumentsRegex = /(^[^ ]+).*/;
|
|
@@ -7143,7 +7143,7 @@ var require_linux = __commonJS({
|
|
|
7143
7143
|
function linux() {
|
|
7144
7144
|
let installations = [];
|
|
7145
7145
|
const desktopInstallationFolders = [
|
|
7146
|
-
|
|
7146
|
+
path14.join(require("os").homedir(), ".local/share/applications/"),
|
|
7147
7147
|
"/usr/share/applications/"
|
|
7148
7148
|
];
|
|
7149
7149
|
desktopInstallationFolders.forEach((folder) => {
|
|
@@ -7169,7 +7169,7 @@ var require_linux = __commonJS({
|
|
|
7169
7169
|
].map((possiblePath) => {
|
|
7170
7170
|
try {
|
|
7171
7171
|
const chromePathToTest = possiblePath + "/" + executable;
|
|
7172
|
-
if (
|
|
7172
|
+
if (fs14.existsSync(chromePathToTest) && canAccess(chromePathToTest) && isExecutable(chromePathToTest)) {
|
|
7173
7173
|
installations.push(chromePathToTest);
|
|
7174
7174
|
return chromePathToTest;
|
|
7175
7175
|
}
|
|
@@ -8302,8 +8302,8 @@ var init_parseUtil = __esm({
|
|
|
8302
8302
|
init_errors();
|
|
8303
8303
|
init_en();
|
|
8304
8304
|
makeIssue = (params) => {
|
|
8305
|
-
const { data, path:
|
|
8306
|
-
const fullPath = [...
|
|
8305
|
+
const { data, path: path14, errorMaps, issueData } = params;
|
|
8306
|
+
const fullPath = [...path14, ...issueData.path || []];
|
|
8307
8307
|
const fullIssue = {
|
|
8308
8308
|
...issueData,
|
|
8309
8309
|
path: fullPath
|
|
@@ -8617,11 +8617,11 @@ var init_types = __esm({
|
|
|
8617
8617
|
init_parseUtil();
|
|
8618
8618
|
init_util();
|
|
8619
8619
|
ParseInputLazyPath = class {
|
|
8620
|
-
constructor(parent, value,
|
|
8620
|
+
constructor(parent, value, path14, key) {
|
|
8621
8621
|
this._cachedPath = [];
|
|
8622
8622
|
this.parent = parent;
|
|
8623
8623
|
this.data = value;
|
|
8624
|
-
this._path =
|
|
8624
|
+
this._path = path14;
|
|
8625
8625
|
this._key = key;
|
|
8626
8626
|
}
|
|
8627
8627
|
get path() {
|
|
@@ -12054,9 +12054,17 @@ var init_schema = __esm({
|
|
|
12054
12054
|
// Qualitative floor (not a numeric % — consistent with the match-strength design).
|
|
12055
12055
|
minStrength: external_exports.enum(["weak", "moderate", "strong"]).default("weak"),
|
|
12056
12056
|
maxFailuresToAnalyse: external_exports.number().int().min(1).default(500),
|
|
12057
|
-
// Consumed by the Plan 3 feedback collector; inert until then.
|
|
12058
12057
|
collectUnclassified: external_exports.boolean().default(true),
|
|
12059
|
-
|
|
12058
|
+
// What the feedback collector stores (while collectUnclassified is on):
|
|
12059
|
+
// 'blind-spots' = unknown/low-confidence only; 'all' = every model-scored
|
|
12060
|
+
// failure, so wrong-but-confident verdicts can be corrected by labeling.
|
|
12061
|
+
collectScope: external_exports.enum(["blind-spots", "all"]).default("blind-spots"),
|
|
12062
|
+
autoUpdateModel: external_exports.boolean().default(true),
|
|
12063
|
+
// Use the on-device trained model (when present and its evaluation gate passed).
|
|
12064
|
+
localModel: external_exports.boolean().default(true),
|
|
12065
|
+
// Override the local model file location (default ~/.reportforge/model-local.json).
|
|
12066
|
+
// Point at a repo-committed file to share a team-trained model with ephemeral CI.
|
|
12067
|
+
localModelPath: external_exports.string().optional()
|
|
12060
12068
|
}).default({});
|
|
12061
12069
|
captureConfigSchema = external_exports.object({
|
|
12062
12070
|
steps: external_exports.boolean().default(false),
|
|
@@ -12153,7 +12161,57 @@ var init_model_bundle = __esm({
|
|
|
12153
12161
|
"src/analysis/model-bundle.ts"() {
|
|
12154
12162
|
"use strict";
|
|
12155
12163
|
init_cjs_shims();
|
|
12156
|
-
MODEL = { "alpha": 1, "labels": ["assertion", "env-config", "flaky-by-retry", "locator-not-found", "navigation", "network", "timeout"], "logLikelihoods": { "assertion": { "NUM": -4.574710978503383, "assertionerror": -4.980176086611547, "be": -4.980176086611547, "cart": -4.980176086611547, "checkout": -4.980176086611547, "com": -4.980176086611547, "dashboard": -4.980176086611547, "deep": -4.980176086611547, "equal": -4.980176086611547, "equality": -4.980176086611547, "error": -4.980176086611547, "expect": -3.5938817254916566, "expected": -3.1083739097099556, "failed": -4.980176086611547, "false": -4.980176086611547, "got": -4.980176086611547, "id": -4.574710978503383, "john": -4.980176086611547, "locator": -4.574710978503383, "login": -4.980176086611547, "must": -4.980176086611547, "page": -4.980176086611547, "pattern": -4.980176086611547, "received": -3.275427994373122, "string": -4.980176086611547, "to": -4.980176086611547, "tobe": -4.980176086611547, "tobetruthy": -4.980176086611547, "toequal": -4.980176086611547, "tohavecount": -4.980176086611547, "tohavetext": -4.980176086611547, "tohavetitle": -4.980176086611547, "tohavevalue": -4.980176086611547, "truthy": -4.980176086611547, "value": -4.980176086611547 }, "env-config": { "NUM": -4.997212273764115, "api": -4.997212273764115, "at": -4.997212273764115, "back": -4.997212273764115, "base": -4.997212273764115, "baseurl": -4.997212273764115, "browsers": -4.997212273764115, "cannot": -4.997212273764115, "ci": -4.997212273764115, "config": -4.304065093204169, "directory": -4.591747165655951, "dotenv": -4.997212273764115, "enoent": -4.997212273764115, "env": -4.591747165655951, "environment": -4.591747165655951, "error": -4.08092154188996, "existent": -4.997212273764115, "failed": -4.997212273764115, "falling": -4.997212273764115, "file": -4.997212273764115, "find": -4.997212273764115, "from": -4.997212273764115, "in": -4.591747165655951, "invalid": -4.997212273764115, "is": -4.997212273764115, "json": -4.591747165655951, "key": -4.997212273764115, "load": -4.997212273764115, "missing": -4.997212273764115, "module": -4.997212273764115, "no": -4.997212273764115, "non": -4.997212273764115, "not": -4.997212273764115, "open": -4.997212273764115, "or": -4.997212273764115, "path": -4.997212273764115, "playwright": -4.591747165655951, "points": -4.997212273764115, "position": -4.997212273764115, "required": -4.997212273764115, "set": -4.997212273764115, "spec": -4.997212273764115, "such": -4.997212273764115, "syntaxerror": -4.997212273764115, "test": -4.997212273764115, "to": -4.304065093204169, "token": -4.997212273764115, "ts": -4.997212273764115, "tsconfig": -4.997212273764115, "undefined": -4.591747165655951, "unexpected": -4.997212273764115, "url": -4.997212273764115, "values": -4.997212273764115, "variable": -4.591747165655951 }, "flaky-by-retry": { "after": -4.58836306767171, "and": -4.993828175779875, "animation": -4.993828175779875, "attempt": -4.07753744390572, "between": -4.993828175779875, "blip": -4.993828175779875, "cause": -4.993828175779875, "change": -4.993828175779875, "click": -4.993828175779875, "code": -4.993828175779875, "commit": -4.993828175779875, "condition": -4.993828175779875, "dependency": -4.993828175779875, "detached": -4.993828175779875, "deterministic": -4.993828175779875, "element": -4.993828175779875, "failed": -4.300680995219929, "fails": -4.993828175779875, "flaky": -4.07753744390572, "green": -4.993828175779875, "in": -4.993828175779875, "intermittent": -4.993828175779875, "locator": -4.993828175779875, "marked": -4.993828175779875, "network": -4.993828175779875, "no": -4.993828175779875, "non": -4.993828175779875, "on": -3.895215887111765, "order": -4.993828175779875, "passed": -3.895215887111765, "quarantined": -4.993828175779875, "race": -4.993828175779875, "reproducible": -4.993828175779875, "rerun": -4.993828175779875, "retry": -4.07753744390572, "runs": -4.993828175779875, "same": -4.993828175779875, "second": -4.993828175779875, "stale": -4.993828175779875, "succeeded": -4.993828175779875, "test": -4.58836306767171, "the": -4.993828175779875, "then": -4.993828175779875, "timeout": -4.993828175779875, "unstable": -4.993828175779875, "with": -4.993828175779875, "without": -4.993828175779875 }, "locator-not-found": { "an": -4.993828175779875, "attached": -4.993828175779875, "btn": -4.993828175779875, "button": -4.58836306767171, "cart": -4.993828175779875, "checkout": -4.993828175779875, "click": -4.993828175779875, "data": -4.993828175779875, "div": -4.993828175779875, "dom": -4.993828175779875, "element": -4.07753744390572, "elements": -4.300680995219929, "error": -4.58836306767171, "find": -4.993828175779875, "for": -4.58836306767171, "found": -4.58836306767171, "getbyrole": -4.993828175779875, "getbytext": -4.993828175779875, "icon": -4.993828175779875, "id": -4.993828175779875, "is": -4.993828175779875, "locator": -4.07753744390572, "matched": -4.993828175779875, "matches": -4.993828175779875, "missing": -4.993828175779875, "mode": -4.58836306767171, "name": -4.993828175779875, "no": -4.58836306767171, "node": -4.993828175779875, "not": -4.58836306767171, "resolved": -4.58836306767171, "save": -4.993828175779875, "selector": -4.58836306767171, "strict": -4.58836306767171, "submit": -4.993828175779875, "testid": -4.993828175779875, "textcontent": -4.993828175779875, "the": -4.58836306767171, "to": -4.07753744390572, "total": -4.993828175779875, "unable": -4.993828175779875, "violation": -4.58836306767171, "waiting": -4.993828175779875, "with": -4.993828175779875, "xpath": -4.993828175779875 }, "navigation": { "NUM": -4.983606621708336, "aborted": -4.983606621708336, "another": -4.983606621708336, "back": -4.983606621708336, "because": -4.578141513600172, "by": -4.578141513600172, "checkout": -4.983606621708336, "context": -4.983606621708336, "crashed": -4.983606621708336, "dashboard": -4.983606621708336, "destroyed": -4.983606621708336, "detached": -4.983606621708336, "entry": -4.983606621708336, "err": -4.578141513600172, "exceeded": -4.983606621708336, "execution": -4.983606621708336, "failed": -4.983606621708336, "frame": -4.578141513600172, "goback": -4.983606621708336, "goto": -4.290459441148391, "history": -4.983606621708336, "home": -4.983606621708336, "interrupted": -4.578141513600172, "is": -4.983606621708336, "likely": -4.983606621708336, "login": -4.983606621708336, "many": -4.983606621708336, "maybe": -4.983606621708336, "most": -4.983606621708336, "navigate": -4.983606621708336, "navigating": -4.983606621708336, "navigation": -3.7308436532129683, "net": -4.578141513600172, "no": -4.983606621708336, "of": -4.983606621708336, "page": -3.884994333040227, "redirects": -4.983606621708336, "timeout": -4.983606621708336, "to": -3.7308436532129683, "too": -4.983606621708336, "waitforurl": -4.983606621708336, "was": -4.578141513600172 }, "network": { "NUM": -4.57126863431241, "URL": -4.283586561860629, "again": -4.976733742420574, "api": -4.976733742420574, "apirequestcontext": -4.976733742420574, "at": -4.976733742420574, "body": -4.976733742420574, "connect": -4.976733742420574, "connection": -4.57126863431241, "disconnected": -4.976733742420574, "eai": -4.976733742420574, "econnrefused": -4.976733742420574, "err": -3.8781214537524646, "error": -4.976733742420574, "failed": -4.283586561860629, "fetch": -4.976733742420574, "fetching": -4.976733742420574, "from": -4.976733742420574, "get": -4.976733742420574, "getaddrinfo": -4.976733742420574, "goto": -4.976733742420574, "hang": -4.976733742420574, "internet": -4.976733742420574, "loading": -4.976733742420574, "name": -4.976733742420574, "net": -3.8781214537524646, "not": -4.976733742420574, "on": -4.976733742420574, "out": -4.976733742420574, "page": -4.57126863431241, "products": -4.976733742420574, "protocol": -4.976733742420574, "reading": -4.976733742420574, "reason": -4.976733742420574, "refused": -4.976733742420574, "request": -4.57126863431241, "resolved": -4.976733742420574, "resource": -4.976733742420574, "response": -4.976733742420574, "route": -4.976733742420574, "socket": -4.976733742420574, "ssl": -4.976733742420574, "timed": -4.976733742420574, "to": -4.976733742420574, "up": -4.976733742420574, "upstream": -4.976733742420574, "while": -4.976733742420574 }, "timeout": { "NUM": -3.4965075614664802, "after": -5.000584958242754, "be": -4.59511985013459, "cart": -5.000584958242754, "click": -5.000584958242754, "element": -5.000584958242754, "event": -5.000584958242754, "exceeded": -3.6142905971228636, "expect": -5.000584958242754, "fill": -5.000584958242754, "for": -3.7478219897473863, "function": -5.000584958242754, "load": -5.000584958242754, "locator": -4.084294226368599, "navigation": -5.000584958242754, "of": -5.000584958242754, "out": -5.000584958242754, "page": -4.59511985013459, "running": -5.000584958242754, "selector": -5.000584958242754, "submit": -5.000584958242754, "test": -4.59511985013459, "timed": -5.000584958242754, "timeout": -3.6142905971228636, "timeouterror": -4.59511985013459, "to": -4.59511985013459, "tobevisible": -5.000584958242754, "visible": -4.59511985013459, "waitforloadstate": -5.000584958242754, "waitforselector": -5.000584958242754, "waiting": -3.7478219897473863, "while": -5.000584958242754 } }, "playwrightVersion": "1.59.1", "priors": { "assertion": -1.9636097261547143, "env-config": -1.9636097261547143, "flaky-by-retry": -1.9636097261547143, "locator-not-found": -1.9636097261547143, "navigation": -1.9636097261547143, "network": -1.9636097261547143, "timeout": -1.845826690498331 }, "totals": { "assertion": 64, "env-config": 69, "flaky-by-retry": 68, "locator-not-found": 68, "navigation": 65, "network": 63, "timeout": 70 }, "version": 1, "vocabSize": 227 };
|
|
12164
|
+
MODEL = { "alpha": 1, "labels": ["assertion", "env-config", "flaky-by-retry", "locator-not-found", "navigation", "network", "timeout"], "logLikelihoods": { "assertion": { "13": -6.424058322604399, "16": -6.424058322604399, "17": -6.424058322604399, "20": -6.424058322604399, "21": -6.018593214496234, "22": -6.424058322604399, "23": -6.424058322604399, "25": -6.424058322604399, "30": -6.424058322604399, "41": -6.424058322604399, "43": -6.424058322604399, "61": -6.424058322604399, "00": -6.018593214496234, "03": -6.424058322604399, "12t07": -6.424058322604399, "6s": -6.424058322604399, "NUM": -4.283992159108128, "URL": -6.424058322604399, "actual": -6.018593214496234, "am": -6.424058322604399, "assertionerror": -6.424058322604399, "assertionname": -6.424058322604399, "async": -6.424058322604399, "at": -5.507767590730244, "await": -5.32544603393629, "basic": -5.507767590730244, "be": -5.730911142044453, "below": -6.424058322604399, "button": -6.424058322604399, "call": -5.507767590730244, "cart": -6.424058322604399, "checkout": -6.424058322604399, "com": -6.424058322604399, "const": -6.424058322604399, "count": -5.730911142044453, "dashboard": -6.424058322604399, "deep": -6.424058322604399, "downloads": -6.018593214496234, "equal": -6.424058322604399, "equality": -6.424058322604399, "error": -5.32544603393629, "example": -5.171295354109031, "expect": -4.283992159108128, "expected": -3.9817112872351945, "failed": -6.018593214496234, "false": -6.424058322604399, "field": -6.424058322604399, "for": -5.507767590730244, "getbytext": -6.018593214496234, "getting": -6.424058322604399, "ggorlen": -6.424058322604399, "got": -6.424058322604399, "goto": -6.424058322604399, "id": -6.018593214496234, "in": -6.018593214496234, "inner": -6.018593214496234, "internal": -6.018593214496234, "isnot": -6.018593214496234, "john": -6.424058322604399, "js": -5.32544603393629, "lib": -6.424058322604399, "locator": -4.478148173549085, "log": -5.507767590730244, "logged": -6.018593214496234, "login": -6.424058322604399, "matcherhint": -6.424058322604399, "matcherresult": -5.507767590730244, "modules": -6.424058322604399, "must": -6.424058322604399, "my": -6.424058322604399, "name": -6.018593214496234, "navbar": -5.507767590730244, "node": -5.507767590730244, "not": -6.424058322604399, "npx": -6.424058322604399, "page": -5.171295354109031, "pattern": -6.424058322604399, "playwright": -5.32544603393629, "printexpected": -6.018593214496234, "printreceived": -6.018593214496234, "process": -6.424058322604399, "processimmediate": -6.424058322604399, "queues": -6.424058322604399, "received": -4.22683374526818, "reply": -6.424058322604399, "return": -6.424058322604399, "runnextticks": -6.424058322604399, "running": -6.424058322604399, "runtestwithbeforehooks": -6.424058322604399, "selector": -6.424058322604399, "should": -6.018593214496234, "spec": -5.32544603393629, "string": -5.037763961484508, "task": -6.424058322604399, "test": -4.814620410170298, "tests": -5.507767590730244, "text": -6.424058322604399, "this": -5.171295354109031, "timeout": -5.730911142044453, "timers": -6.424058322604399, "title": -5.507767590730244, "to": -6.424058322604399, "tobe": -6.424058322604399, "tobetruthy": -6.424058322604399, "tobevisible": -6.018593214496234, "toequal": -6.424058322604399, "tohavecount": -6.424058322604399, "tohavetext": -4.919980925828125, "tohavetitle": -6.424058322604399, "tohavevalue": -6.424058322604399, "truthy": -6.424058322604399, "ts": -6.424058322604399, "undefined": -6.018593214496234, "users": -6.018593214496234, "using": -6.424058322604399, "utils": -5.32544603393629, "value": -6.424058322604399, "vu": -6.018593214496234, "waiting": -5.507767590730244, "with": -5.730911142044453, "worker": -6.424058322604399, "workerrunner": -6.018593214496234 }, "env-config": { "16": -6.687108607866515, "44": -6.687108607866515, "64": -6.687108607866515, "66": -6.687108607866515, "70": -5.77081787599236, "00": -6.28164349975835, "05": -6.687108607866515, "16t15": -6.687108607866515, "51z": -6.687108607866515, "8hauptrolle": -6.687108607866515, "NUM": -5.18303121109024, "activity": -6.687108607866515, "add": -6.687108607866515, "additional": -6.687108607866515, "aimed": -6.687108607866515, "alderoberge": -6.687108607866515, "alejo": -6.687108607866515, "also": -5.588496319198405, "and": -5.588496319198405, "any": -6.28164349975835, "anyone": -6.28164349975835, "api": -6.687108607866515, "assist": -6.687108607866515, "at": -5.434345639371147, "available": -6.28164349975835, "awb": -6.28164349975835, "back": -6.687108607866515, "base": -6.687108607866515, "baseurl": -6.687108607866515, "be": -6.28164349975835, "believe": -6.687108607866515, "binary": -6.687108607866515, "box": -6.28164349975835, "bridge": -6.687108607866515, "browser": -6.687108607866515, "browsername": -5.588496319198405, "browsers": -5.18303121109024, "browsertype": -4.9823605156280895, "but": -6.28164349975835, "by": -6.687108607866515, "cache": -5.77081787599236, "can": -6.28164349975835, "cannot": -6.687108607866515, "channel": -5.077670695432414, "cheron": -6.687108607866515, "chrome": -5.18303121109024, "chromium": -4.9823605156280895, "ci": -6.687108607866515, "cmu": -4.9823605156280895, "cmulex": -6.687108607866515, "command": -5.993961427306569, "commented": -6.687108607866515, "config": -5.993961427306569, "default": -6.687108607866515, "dependencies": -5.588496319198405, "deps": -5.993961427306569, "despite": -6.687108607866515, "difference": -6.687108607866515, "different": -6.687108607866515, "directory": -6.28164349975835, "displays": -6.687108607866515, "distros": -5.993961427306569, "do": -6.28164349975835, "docker": -6.687108607866515, "doesn": -5.434345639371147, "dotenv": -6.687108607866515, "download": -6.28164349975835, "edit": -6.687108607866515, "effort": -6.687108607866515, "elementary": -6.687108607866515, "enoent": -6.687108607866515, "env": -6.28164349975835, "environment": -6.28164349975835, "error": -4.741198458811201, "example": -6.687108607866515, "exec": -6.687108607866515, "executable": -5.77081787599236, "exist": -5.77081787599236, "existent": -6.687108607866515, "explain": -6.28164349975835, "eyes": -6.687108607866515, "failed": -6.687108607866515, "fak3": -6.687108607866515, "falling": -6.687108607866515, "file": -6.28164349975835, "files": -6.687108607866515, "find": -6.687108607866515, "firefox": -6.28164349975835, "fix": -6.687108607866515, "fixing": -6.687108607866515, "following": -6.28164349975835, "for": -5.588496319198405, "from": -6.687108607866515, "gnu": -6.687108607866515, "goes": -6.687108607866515, "goto": -6.687108607866515, "grapheme": -6.28164349975835, "guess": -6.28164349975835, "has": -6.28164349975835, "have": -6.28164349975835, "having": -6.28164349975835, "home": -6.687108607866515, "hopefully": -6.687108607866515, "host": -5.77081787599236, "if": -6.28164349975835, "image": -6.687108607866515, "in": -6.28164349975835, "incompatible": -6.687108607866515, "indic": -6.28164349975835, "information": -6.687108607866515, "insights": -6.687108607866515, "install": -5.588496319198405, "installed": -5.993961427306569, "instead": -6.687108607866515, "into": -6.687108607866515, "invalid": -6.687108607866515, "is": -5.077670695432414, "isn": -6.687108607866515, "issue": -5.588496319198405, "it": -4.9823605156280895, "js": -6.687108607866515, "json": -6.28164349975835, "just": -5.588496319198405, "kal": -6.687108607866515, "kal16": -6.687108607866515, "keep": -6.687108607866515, "key": -6.687108607866515, "kunukn": -6.687108607866515, "lang": -6.28164349975835, "launch": -4.9823605156280895, "leo": -6.687108607866515, "lex": -6.28164349975835, "lib": -6.687108607866515, "libatk": -6.28164349975835, "libatspi": -6.687108607866515, "libcups": -6.687108607866515, "libdbus": -6.687108607866515, "libdrm": -6.687108607866515, "libexpat": -6.687108607866515, "libflite": -4.741198458811201, "libgio": -6.687108607866515, "libglib": -6.687108607866515, "libgobject": -6.687108607866515, "libgstcodecparsers": -6.687108607866515, "libicu70": -6.687108607866515, "libicudata": -6.687108607866515, "libicui18n": -6.687108607866515, "libicuio": -6.687108607866515, "libicuuc": -6.687108607866515, "libnspr4": -6.687108607866515, "libnss3": -6.687108607866515, "libnssutil3": -6.687108607866515, "libpthread": -6.687108607866515, "libraries": -5.77081787599236, "libsmime3": -6.687108607866515, "libx264": -6.687108607866515, "libxcb": -6.687108607866515, "libxkbcommon": -6.687108607866515, "like": -5.77081787599236, "linux": -4.895349138638459, "load": -6.687108607866515, "looks": -5.993961427306569, "lookup": -6.687108607866515, "lot": -6.687108607866515, "make": -6.687108607866515, "malaver": -6.687108607866515, "matching": -6.687108607866515, "may": -6.28164349975835, "meant": -6.687108607866515, "message": -6.687108607866515, "minibrowser": -6.687108607866515, "mint": -5.77081787599236, "missing": -5.077670695432414, "module": -6.687108607866515, "more": -6.687108607866515, "most": -6.28164349975835, "ms": -5.77081787599236, "must": -6.687108607866515, "my": -6.687108607866515, "name": -6.687108607866515, "need": -6.687108607866515, "new": -6.28164349975835, "newer": -6.687108607866515, "no": -6.687108607866515, "non": -6.687108607866515, "not": -5.993961427306569, "npx": -6.28164349975835, "of": -5.434345639371147, "old": -6.687108607866515, "on": -5.18303121109024, "one": -6.28164349975835, "open": -6.687108607866515, "or": -5.77081787599236, "other": -6.687108607866515, "out": -5.77081787599236, "package": -6.687108607866515, "page": -6.687108607866515, "parameter": -6.687108607866515, "path": -6.687108607866515, "platforms": -6.687108607866515, "playwright": -4.435816809260019, "please": -6.28164349975835, "pnpm": -6.687108607866515, "points": -6.687108607866515, "popular": -6.28164349975835, "position": -6.687108607866515, "prompt": -6.687108607866515, "provided": -6.687108607866515, "quick": -6.687108607866515, "react": -6.687108607866515, "related": -6.687108607866515, "replace": -6.687108607866515, "reply": -6.687108607866515, "repositories": -6.28164349975835, "required": -6.687108607866515, "rms": -6.687108607866515, "root": -5.993961427306569, "run": -5.300814246746624, "running": -5.993961427306569, "safari": -6.28164349975835, "same": -5.77081787599236, "seems": -6.687108607866515, "set": -6.687108607866515, "should": -6.28164349975835, "simensbat": -6.687108607866515, "simply": -6.687108607866515, "slt": -6.687108607866515, "so": -3.742669628700074, "some": -6.28164349975835, "spec": -6.28164349975835, "such": -6.687108607866515, "super": -6.687108607866515, "support": -6.687108607866515, "symbol": -6.28164349975835, "syntaxerror": -6.687108607866515, "system": -5.588496319198405, "team": -6.687108607866515, "test": -6.28164349975835, "tests": -6.687108607866515, "the": -4.547042444370244, "there": -5.77081787599236, "this": -5.77081787599236, "time": -6.687108607866515, "title": -6.687108607866515, "tls": -6.687108607866515, "to": -4.489884030530295, "token": -6.687108607866515, "tried": -6.28164349975835, "troubleshooting": -6.687108607866515, "ts": -6.687108607866515, "tsconfig": -6.687108607866515, "ubuntu": -5.588496319198405, "undefined": -5.993961427306569, "unexpected": -6.687108607866515, "unsupported": -5.588496319198405, "updated": -6.28164349975835, "url": -6.687108607866515, "us": -5.588496319198405, "use": -6.687108607866515, "usenglish": -6.687108607866515, "uses": -6.28164349975835, "using": -6.28164349975835, "usr": -6.687108607866515, "value": -6.687108607866515, "values": -6.687108607866515, "variable": -6.28164349975835, "version": -6.28164349975835, "vsts": -6.687108607866515, "was": -5.993961427306569, "way": -6.687108607866515, "webkit": -5.77081787599236, "which": -6.28164349975835, "why": -6.28164349975835, "with": -5.300814246746624, "work": -5.77081787599236, "worker": -6.687108607866515, "works": -6.28164349975835, "would": -6.687108607866515, "x86": -6.687108607866515, "you": -6.28164349975835 }, "flaky-by-retry": { "after": -5.786897381366708, "and": -6.192362489474872, "animation": -6.192362489474872, "attempt": -5.276071757600717, "between": -6.192362489474872, "blip": -6.192362489474872, "cause": -6.192362489474872, "change": -6.192362489474872, "click": -6.192362489474872, "code": -6.192362489474872, "commit": -6.192362489474872, "condition": -6.192362489474872, "dependency": -6.192362489474872, "detached": -6.192362489474872, "deterministic": -6.192362489474872, "element": -6.192362489474872, "failed": -5.499215308914927, "fails": -6.192362489474872, "flaky": -5.276071757600717, "green": -6.192362489474872, "in": -6.192362489474872, "intermittent": -6.192362489474872, "locator": -6.192362489474872, "marked": -6.192362489474872, "network": -6.192362489474872, "no": -6.192362489474872, "non": -6.192362489474872, "on": -5.093750200806762, "order": -6.192362489474872, "passed": -5.093750200806762, "quarantined": -6.192362489474872, "race": -6.192362489474872, "reproducible": -6.192362489474872, "rerun": -6.192362489474872, "retry": -5.276071757600717, "runs": -6.192362489474872, "same": -6.192362489474872, "second": -6.192362489474872, "stale": -6.192362489474872, "succeeded": -6.192362489474872, "test": -5.786897381366708, "the": -6.192362489474872, "then": -6.192362489474872, "timeout": -6.192362489474872, "unstable": -6.192362489474872, "with": -6.192362489474872, "without": -6.192362489474872 }, "locator-not-found": { "25": -6.459904454377535, "30": -5.3612921657094255, "46": -6.459904454377535, "06": -6.459904454377535, "NUM": -5.5436137225033795, "aka": -4.588102277475944, "alert": -6.459904454377535, "all": -6.459904454377535, "an": -6.459904454377535, "api": -6.459904454377535, "aria": -6.05443934626937, "async": -6.459904454377535, "attached": -6.459904454377535, "based": -6.459904454377535, "being": -6.459904454377535, "birth": -6.459904454377535, "btn": -6.459904454377535, "button": -4.588102277475944, "by": -6.459904454377535, "captura": -6.459904454377535, "cart": -6.459904454377535, "carteiras": -6.05443934626937, "cell": -6.05443934626937, "checkout": -6.459904454377535, "checks": -6.459904454377535, "click": -5.5436137225033795, "cns": -5.766757273817589, "columns": -6.459904454377535, "const": -6.459904454377535, "contains": -6.459904454377535, "copy": -6.05443934626937, "count": -6.459904454377535, "cpf": -5.766757273817589, "da": -6.05443934626937, "data": -6.05443934626937, "de": -6.459904454377535, "div": -5.2071414858821665, "does": -6.459904454377535, "dom": -6.459904454377535, "easiest": -6.459904454377535, "element": -5.5436137225033795, "elements": -4.755156362139109, "error": -4.513994305322221, "even": -6.459904454377535, "exact": -5.766757273817589, "expected": -6.459904454377535, "fail": -6.459904454377535, "field": -6.459904454377535, "filtering": -6.459904454377535, "find": -6.459904454377535, "first": -5.766757273817589, "for": -6.05443934626937, "found": -6.05443934626937, "frame": -6.459904454377535, "from": -6.459904454377535, "getbyrole": -5.3612921657094255, "getbytext": -5.073610093257644, "icon": -6.459904454377535, "id": -5.766757273817589, "if": -6.459904454377535, "iframe": -6.459904454377535, "in": -5.766757273817589, "into": -6.459904454377535, "is": -5.5436137225033795, "issue": -6.05443934626937, "it": -5.766757273817589, "javascript": -6.459904454377535, "just": -6.459904454377535, "kb": -6.459904454377535, "label": -6.05443934626937, "licenses": -6.459904454377535, "link": -6.459904454377535, "list": -6.459904454377535, "locator": -4.2626798770413155, "match": -6.459904454377535, "matched": -6.459904454377535, "matches": -6.459904454377535, "mean": -6.459904454377535, "method": -6.459904454377535, "missing": -6.459904454377535, "mode": -4.66814498514948, "mother": -6.459904454377535, "munic": -6.05443934626937, "municipality": -6.459904454377535, "my": -6.459904454377535, "name": -5.2071414858821665, "nascimento": -6.05443934626937, "new": -6.05443934626937, "no": -6.05443934626937, "node": -6.459904454377535, "nome": -5.5436137225033795, "not": -6.05443934626937, "note": -6.459904454377535, "nov": -6.05443934626937, "novalistagem": -6.459904454377535, "nth": -6.459904454377535, "of": -6.459904454377535, "or": -5.766757273817589, "page": -6.459904454377535, "passes": -6.459904454377535, "pio": -6.05443934626937, "playwright": -6.05443934626937, "position": -5.766757273817589, "question": -6.459904454377535, "resolve": -6.459904454377535, "resolved": -4.955827057601261, "response": -6.459904454377535, "rest": -6.459904454377535, "result": -5.766757273817589, "results": -6.459904454377535, "role": -6.459904454377535, "run": -6.459904454377535, "save": -6.459904454377535, "selection": -6.459904454377535, "selector": -6.05443934626937, "simplest": -6.459904454377535, "solution": -6.459904454377535, "span": -5.766757273817589, "specify": -6.459904454377535, "specifying": -6.459904454377535, "state": -6.459904454377535, "strict": -4.66814498514948, "submit": -6.459904454377535, "tabindex": -6.05443934626937, "table": -6.459904454377535, "take": -6.459904454377535, "td": -6.459904454377535, "tela": -6.459904454377535, "test": -6.05443934626937, "testid": -6.459904454377535, "text": -5.766757273817589, "textcontent": -6.459904454377535, "that": -6.05443934626937, "the": -4.513994305322221, "thead": -6.459904454377535, "this": -5.5436137225033795, "title": -6.459904454377535, "to": -4.513994305322221, "total": -6.459904454377535, "tr": -6.459904454377535, "true": -5.766757273817589, "type": -6.05443934626937, "uf": -6.05443934626937, "unable": -6.459904454377535, "validate": -6.459904454377535, "validatefieldvalue": -6.459904454377535, "validation": -6.459904454377535, "violation": -4.755156362139109, "waitfor": -6.459904454377535, "waiting": -6.459904454377535, "we": -6.459904454377535, "what": -6.459904454377535, "when": -6.459904454377535, "whether": -6.459904454377535, "will": -6.459904454377535, "with": -6.459904454377535, "xpath": -6.459904454377535 }, "navigation": { "14": -6.304448802421981, "19": -6.709913910530146, "25": -6.709913910530146, "27": -6.304448802421981, "39": -6.709913910530146, "49": -6.709913910530146, "66": -6.709913910530146, "0ms": -6.304448802421981, "6ms": -6.709913910530146, "NUM": -4.569847747033875, "URL": -5.793623178655991, "abort": -6.0167667299702, "aborted": -4.91815444130209, "acting": -6.709913910530146, "additional": -6.709913910530146, "adjusting": -6.709913910530146, "after": -6.0167667299702, "an": -6.304448802421981, "and": -6.304448802421981, "another": -6.709913910530146, "api": -5.611301621862036, "appends": -6.709913910530146, "are": -6.709913910530146, "argument": -6.709913910530146, "as": -6.709913910530146, "async": -6.0167667299702, "at": -5.457150942034778, "avoid": -6.709913910530146, "await": -4.91815444130209, "back": -6.709913910530146, "base": -6.709913910530146, "be": -6.709913910530146, "because": -5.457150942034778, "before": -6.304448802421981, "behavior": -6.709913910530146, "being": -6.709913910530146, "both": -6.709913910530146, "browser": -6.0167667299702, "bug": -6.304448802421981, "but": -6.304448802421981, "by": -6.0167667299702, "call": -5.457150942034778, "callback": -6.709913910530146, "called": -6.709913910530146, "calling": -6.709913910530146, "cancelled": -6.709913910530146, "cannot": -6.709913910530146, "caused": -6.709913910530146, "causes": -6.709913910530146, "certain": -6.709913910530146, "chain": -6.304448802421981, "channel": -6.709913910530146, "checkout": -6.709913910530146, "chrome": -6.709913910530146, "click": -6.709913910530146, "close": -6.709913910530146, "closed": -6.709913910530146, "code": -6.709913910530146, "comment": -6.709913910530146, "commented": -6.709913910530146, "common": -6.304448802421981, "complicated": -6.709913910530146, "connection": -6.709913910530146, "const": -6.709913910530146, "context": -6.304448802421981, "copy": -6.0167667299702, "crashed": -5.793623178655991, "create": -6.709913910530146, "dashboard": -6.0167667299702, "debugging": -6.709913910530146, "describe": -6.709913910530146, "destroyed": -6.0167667299702, "detached": -5.100475998096045, "developer": -6.709913910530146, "didn": -6.709913910530146, "different": -6.709913910530146, "directly": -6.709913910530146, "document": -6.709913910530146, "does": -6.709913910530146, "domcontentloaded": -6.304448802421981, "dowrite": -6.709913910530146, "during": -6.304448802421981, "en": -6.709913910530146, "entry": -6.709913910530146, "err": -4.695010889987881, "error": -4.838111733628554, "event": -6.304448802421981, "example": -6.709913910530146, "exceeded": -6.304448802421981, "execution": -6.709913910530146, "expect": -6.709913910530146, "expected": -6.709913910530146, "facing": -6.709913910530146, "failed": -5.611301621862036, "file": -5.323619549410255, "fine": -6.304448802421981, "fired": -6.304448802421981, "firefox": -6.709913910530146, "for": -5.793623178655991, "frame": -4.6304723688503096, "frames": -6.709913910530146, "frequent": -6.709913910530146, "from": -6.709913910530146, "frompromise": -6.709913910530146, "full": -6.709913910530146, "future": -6.709913910530146, "getbyrole": -6.709913910530146, "getting": -6.709913910530146, "given": -6.709913910530146, "goback": -6.709913910530146, "going": -6.709913910530146, "goto": -4.312018637731775, "handler": -6.709913910530146, "happens": -6.709913910530146, "have": -6.709913910530146, "headless": -6.709913910530146, "helper": -6.0167667299702, "hint": -6.709913910530146, "history": -6.709913910530146, "home": -5.323619549410255, "html": -6.0167667299702, "impl": -6.709913910530146, "in": -5.457150942034778, "include": -6.709913910530146, "info": -6.709913910530146, "initial": -6.709913910530146, "innerhtml": -6.709913910530146, "inside": -6.709913910530146, "instead": -6.709913910530146, "internal": -6.709913910530146, "interrupted": -6.304448802421981, "is": -6.304448802421981, "issue": -5.457150942034778, "it": -6.304448802421981, "its": -6.709913910530146, "js": -5.611301621862036, "jul": -6.304448802421981, "lang": -6.709913910530146, "last": -6.304448802421981, "lib": -5.793623178655991, "likely": -6.709913910530146, "line": -5.457150942034778, "link": -5.793623178655991, "load": -5.793623178655991, "loads": -6.304448802421981, "locals": -5.793623178655991, "log": -6.0167667299702, "login": -6.0167667299702, "logs": -6.304448802421981, "main": -6.709913910530146, "many": -6.304448802421981, "maxschmitt": -6.709913910530146, "may": -6.709913910530146, "maybe": -5.100475998096045, "means": -6.709913910530146, "message": -6.709913910530146, "method": -6.709913910530146, "mistake": -6.709913910530146, "mjs": -6.709913910530146, "mobile": -6.709913910530146, "mode": -6.709913910530146, "more": -6.709913910530146, "most": -6.0167667299702, "my": -6.709913910530146, "name": -6.304448802421981, "navigate": -6.709913910530146, "navigates": -6.709913910530146, "navigatetojobs": -6.709913910530146, "navigating": -5.323619549410255, "navigation": -4.695010889987881, "net": -4.838111733628554, "new": -5.205836513753871, "next": -6.709913910530146, "no": -6.304448802421981, "node": -6.0167667299702, "non": -6.709913910530146, "none": -6.709913910530146, "oauth": -6.709913910530146, "obj": -6.709913910530146, "of": -5.457150942034778, "on": -6.0167667299702, "open": -6.304448802421981, "options": -6.709913910530146, "or": -6.304448802421981, "other": -6.709913910530146, "out": -6.709913910530146, "packages": -5.793623178655991, "page": -3.9065535296236105, "pages": -6.0167667299702, "params": -6.304448802421981, "per": -6.709913910530146, "playwright": -4.91815444130209, "pretty": -6.709913910530146, "process": -6.709913910530146, "promises": -6.709913910530146, "pw": -5.793623178655991, "py": -5.457150942034778, "python3": -5.793623178655991, "randomly": -6.709913910530146, "rebuilds": -6.709913910530146, "received": -6.709913910530146, "recent": -6.304448802421981, "redirect": -6.0167667299702, "redirects": -6.709913910530146, "reload": -6.304448802421981, "removed": -6.304448802421981, "removes": -6.709913910530146, "replaced": -6.709913910530146, "replaces": -6.709913910530146, "replicate": -6.709913910530146, "request": -6.709913910530146, "resolved": -6.709913910530146, "response": -6.709913910530146, "result": -6.709913910530146, "retrieving": -6.709913910530146, "return": -6.709913910530146, "returns": -6.709913910530146, "root": -6.709913910530146, "route": -6.0167667299702, "run": -6.709913910530146, "same": -6.709913910530146, "script": -6.304448802421981, "selector": -6.709913910530146, "self": -5.611301621862036, "send": -6.304448802421981, "sends": -6.709913910530146, "settings": -6.304448802421981, "site": -5.793623178655991, "skip": -6.709913910530146, "socket": -6.709913910530146, "some": -6.304448802421981, "somepage": -6.709913910530146, "spa": -6.304448802421981, "started": -6.709913910530146, "states": -6.709913910530146, "stcpage": -6.304448802421981, "stcunregisteredpage": -6.709913910530146, "stephen100mwangi": -6.709913910530146, "stream": -5.793623178655991, "suddenly": -6.709913910530146, "suspects": -6.709913910530146, "test": -5.457150942034778, "tests": -6.709913910530146, "than": -6.709913910530146, "that": -5.793623178655991, "the": -4.695010889987881, "then": -6.709913910530146, "there": -6.709913910530146, "thing": -6.709913910530146, "this": -6.709913910530146, "through": -6.709913910530146, "timeout": -6.304448802421981, "to": -4.4073288175361, "too": -6.709913910530146, "totally": -6.709913910530146, "traceback": -6.0167667299702, "tried": -6.709913910530146, "triggers": -6.709913910530146, "triggeruncaughtexception": -6.709913910530146, "true": -6.709913910530146, "trying": -6.709913910530146, "uid": -6.709913910530146, "unhandledpromiserejectionwarning": -6.709913910530146, "until": -5.793623178655991, "url": -5.611301621862036, "users": -6.709913910530146, "v18": -6.709913910530146, "variant": -6.304448802421981, "venv": -5.793623178655991, "wait": -6.709913910530146, "waitforurl": -6.304448802421981, "waiting": -6.0167667299702, "waituntil": -6.709913910530146, "was": -4.6304723688503096, "way": -6.709913910530146, "website": -6.709913910530146, "were": -6.709913910530146, "when": -6.0167667299702, "which": -6.709913910530146, "while": -6.304448802421981, "with": -6.0167667299702, "worked": -6.709913910530146, "works": -6.709913910530146, "writable": -6.0167667299702, "write": -6.304448802421981, "writeorbuffer": -6.709913910530146, "your": -6.709913910530146 }, "network": { "10": -5.903316160331277, "11": -5.680172609017068, "12": -4.804703871663167, "13": -6.190998232783058, "14": -4.345171542284727, "15": -4.345171542284727, "16": -5.343700372395855, "17": -4.345171542284727, "19": -6.596463340891223, "20": -6.596463340891223, "21": -6.190998232783058, "23": -6.596463340891223, "31": -5.210168979771332, "34": -6.596463340891223, "48": -6.596463340891223, "51": -6.596463340891223, "61": -6.596463340891223, "64": -6.596463340891223, "65": -6.596463340891223, "69": -6.596463340891223, "77": -6.596463340891223, "95": -6.596463340891223, "98": -6.596463340891223, "NUM": -5.210168979771332, "URL": -4.345171542284727, "access": -6.190998232783058, "again": -6.596463340891223, "am": -6.596463340891223, "and": -6.190998232783058, "anonymous": -6.596463340891223, "api": -6.190998232783058, "apirequestcontext": -6.596463340891223, "applicationurl": -6.596463340891223, "async": -6.190998232783058, "at": -4.070734696582967, "await": -5.903316160331277, "basepage": -6.596463340891223, "before": -6.190998232783058, "beforeeach": -6.596463340891223, "body": -6.596463340891223, "bundle": -6.190998232783058, "capture": -6.596463340891223, "capturestacktrace": -6.596463340891223, "channelowner": -6.190998232783058, "check": -5.680172609017068, "checkout": -6.596463340891223, "chromium": -6.596463340891223, "client": -5.497851052223113, "clientrequest": -6.190998232783058, "close": -6.596463340891223, "code": -6.190998232783058, "common": -6.596463340891223, "connect": -6.596463340891223, "connection": -4.8917152486527975, "const": -6.596463340891223, "dashboardpage": -5.343700372395855, "day": -6.596463340891223, "debug": -6.596463340891223, "deliver": -6.596463340891223, "disconnected": -6.596463340891223, "domcontentloaded": -6.190998232783058, "each": -6.596463340891223, "eai": -6.596463340891223, "econnrefused": -6.596463340891223, "econnreset": -6.596463340891223, "emit": -6.190998232783058, "environment": -6.596463340891223, "err": -4.650553191835909, "error": -4.581560320348958, "events": -6.190998232783058, "failed": -5.903316160331277, "fetch": -6.596463340891223, "fetching": -6.596463340891223, "for": -6.596463340891223, "frame": -5.680172609017068, "from": -6.596463340891223, "get": -6.596463340891223, "getaddrinfo": -6.596463340891223, "git": -6.190998232783058, "goto": -4.650553191835909, "gotosearchproduct": -6.596463340891223, "handler": -6.596463340891223, "hang": -6.190998232783058, "hook": -6.596463340891223, "http": -6.596463340891223, "internet": -6.596463340891223, "js": -4.581560320348958, "lib": -5.343700372395855, "load": -6.190998232783058, "loadapplication": -6.596463340891223, "loading": -6.596463340891223, "login": -5.680172609017068, "logs": -5.092385944114948, "modules": -4.724661163989631, "name": -5.497851052223113, "navigating": -5.210168979771332, "net": -4.517021799211387, "networkidle": -5.680172609017068, "new": -5.903316160331277, "node": -4.724661163989631, "not": -5.903316160331277, "note": -6.596463340891223, "object": -6.596463340891223, "on": -6.190998232783058, "out": -6.190998232783058, "page": -4.456397177394951, "pages": -6.596463340891223, "personal": -6.190998232783058, "playwright": -4.154116305522018, "pnpm": -5.343700372395855, "product": -6.596463340891223, "producteditpage": -6.190998232783058, "products": -6.596463340891223, "productsearchpage": -6.190998232783058, "project": -6.190998232783058, "promises": -6.596463340891223, "protocol": -6.596463340891223, "proxy": -6.596463340891223, "proxyerror": -6.596463340891223, "public": -6.596463340891223, "pw": -6.596463340891223, "reading": -6.596463340891223, "reason": -6.596463340891223, "recording": -6.596463340891223, "refused": -5.343700372395855, "request": -6.190998232783058, "resolved": -5.903316160331277, "resource": -6.596463340891223, "response": -6.596463340891223, "route": -6.596463340891223, "runner": -6.190998232783058, "same": -6.596463340891223, "sauce": -6.190998232783058, "saucelabintegration": -6.596463340891223, "scheduleedit": -6.596463340891223, "sendmessagetoserver": -6.596463340891223, "server": -6.596463340891223, "settings": -6.596463340891223, "single": -6.596463340891223, "socket": -5.680172609017068, "socketcloselistener": -6.596463340891223, "spec": -6.190998232783058, "ssl": -6.596463340891223, "stacktrace": -6.596463340891223, "started": -6.596463340891223, "starting": -6.596463340891223, "tcp": -6.596463340891223, "test": -6.596463340891223, "tests": -5.680172609017068, "this": -6.596463340891223, "timed": -6.190998232783058, "timeouts": -6.596463340891223, "to": -4.987025428457122, "token": -6.190998232783058, "trace": -6.596463340891223, "ts": -6.596463340891223, "until": -5.210168979771332, "up": -6.190998232783058, "update": -6.190998232783058, "upstream": -6.596463340891223, "url": -6.190998232783058, "use": -6.596463340891223, "user": -5.903316160331277, "utils": -6.596463340891223, "valueof": -6.596463340891223, "variable": -6.596463340891223, "waiting": -5.210168979771332, "waituntil": -6.596463340891223, "while": -6.596463340891223, "wo": -6.596463340891223, "work": -5.343700372395855, "wrapapicall": -6.596463340891223, "your": -6.596463340891223 }, "timeout": { "10": -6.434546518787453, "15": -6.840011626895618, "16": -6.840011626895618, "17": -6.840011626895618, "18": -6.840011626895618, "19": -6.840011626895618, "20": -6.434546518787453, "21": -6.434546518787453, "22": -6.840011626895618, "23": -6.840011626895618, "30": -5.741399338227508, "31": -6.840011626895618, "54": -6.840011626895618, "58": -6.840011626895618, "59": -6.840011626895618, "60": -6.146864446335672, "61": -6.434546518787453, "62": -6.840011626895618, "63": -6.840011626895618, "64": -6.840011626895618, "73": -6.840011626895618, "79": -6.840011626895618, "83": -6.840011626895618, "00": -6.434546518787453, "NUM": -3.7719586917620003, "URL": -6.434546518787453, "action": -5.453717265775727, "actiontimeout": -6.840011626895618, "actual": -6.146864446335672, "after": -6.840011626895618, "afterall": -6.840011626895618, "all": -6.840011626895618, "amount": -6.840011626895618, "an": -5.741399338227508, "and": -5.135263534657192, "angle": -6.840011626895618, "anush": -6.434546518787453, "anzeigen": -6.840011626895618, "api": -5.923720895021463, "app": -6.840011626895618, "appeared": -6.840011626895618, "apply": -6.840011626895618, "are": -6.840011626895618, "areas": -6.840011626895618, "aren": -6.840011626895618, "as": -6.840011626895618, "assertions": -6.840011626895618, "async": -6.840011626895618, "at": -4.968209449994026, "attempt": -6.840011626895618, "attempting": -6.434546518787453, "attributetopage": -6.840011626895618, "auth": -6.146864446335672, "av": -6.434546518787453, "available": -6.840011626895618, "await": -5.453717265775727, "baseurl": -6.434546518787453, "basic": -6.146864446335672, "be": -5.741399338227508, "been": -5.923720895021463, "beforeall": -6.840011626895618, "beforeeach": -6.434546518787453, "between": -6.840011626895618, "bin": -6.840011626895618, "binding": -6.840011626895618, "br": -4.825108606353353, "browser": -5.58724865840025, "browsertype": -6.840011626895618, "btn": -6.840011626895618, "but": -6.840011626895618, "button": -5.923720895021463, "by": -6.434546518787453, "cache": -6.434546518787453, "call": -6.434546518787453, "can": -6.840011626895618, "capture": -6.840011626895618, "cart": -6.840011626895618, "case": -6.434546518787453, "certificates": -6.840011626895618, "chromium": -5.335934230119343, "class": -6.840011626895618, "click": -5.135263534657192, "closed": -5.230573714461517, "config": -5.58724865840025, "configured": -6.840011626895618, "const": -5.923720895021463, "consume": -6.840011626895618, "context": -5.923720895021463, "copy": -6.840011626895618, "core20": -6.840011626895618, "correlation": -6.840011626895618, "could": -6.146864446335672, "ctaheader": -6.840011626895618, "current": -6.840011626895618, "data": -6.146864446335672, "debug": -6.840011626895618, "default": -6.434546518787453, "defer": -6.840011626895618, "describe": -6.840011626895618, "deutschland": -6.434546518787453, "div": -6.146864446335672, "domcontentloaded": -6.434546518787453, "done": -6.840011626895618, "down": -6.840011626895618, "dropdown": -6.840011626895618, "during": -6.434546518787453, "each": -6.434546518787453, "element": -5.741399338227508, "enabled": -5.58724865840025, "entire": -6.840011626895618, "environment": -6.434546518787453, "err": -6.840011626895618, "error": -4.825108606353353, "event": -6.146864446335672, "exceeded": -4.699945463399347, "expect": -6.146864446335672, "fail": -6.840011626895618, "fill": -6.840011626895618, "filter": -6.840011626895618, "fired": -6.840011626895618, "first": -6.840011626895618, "fixture": -6.146864446335672, "for": -4.588719828289123, "force": -6.840011626895618, "form": -5.741399338227508, "frame": -6.434546518787453, "frames": -6.434546518787453, "from": -6.146864446335672, "function": -6.840011626895618, "glibc": -6.840011626895618, "global": -6.434546518787453, "globaltimeout": -6.840011626895618, "gnu": -6.840011626895618, "goto": -5.230573714461517, "gtk": -6.840011626895618, "has": -5.741399338227508, "hastext": -6.434546518787453, "have": -6.434546518787453, "helper": -6.840011626895618, "hold": -6.434546518787453, "home": -6.146864446335672, "hook": -6.146864446335672, "httpcredentials": -6.434546518787453, "icon": -6.840011626895618, "id": -6.840011626895618, "if": -6.146864446335672, "in": -4.825108606353353, "inaccurate": -6.146864446335672, "indexof": -6.840011626895618, "individual": -6.840011626895618, "init": -6.840011626895618, "insert": -6.840011626895618, "inspector": -6.840011626895618, "into": -6.434546518787453, "invalid": -6.840011626895618, "is": -5.048252157667562, "isn": -6.840011626895618, "issues": -6.840011626895618, "it": -6.434546518787453, "javascript": -6.840011626895618, "js": -5.230573714461517, "just": -5.923720895021463, "label": -6.840011626895618, "launch": -6.840011626895618, "league": -6.840011626895618, "leagues": -6.840011626895618, "lib": -5.335934230119343, "libc": -6.840011626895618, "libpthread": -6.840011626895618, "likely": -6.840011626895618, "link": -6.840011626895618, "linux": -6.840011626895618, "load": -6.434546518787453, "loading": -6.840011626895618, "locator": -4.699945463399347, "log": -6.434546518787453, "logs": -5.923720895021463, "lookup": -6.840011626895618, "may": -6.840011626895618, "message": -6.840011626895618, "minibrowser": -6.434546518787453, "minutes": -6.840011626895618, "module": -6.840011626895618, "modules": -5.453717265775727, "more": -6.840011626895618, "ms": -5.923720895021463, "my": -6.840011626895618, "navigated": -6.840011626895618, "navigating": -6.840011626895618, "navigation": -5.335934230119343, "navigationtimeout": -6.840011626895618, "needed": -6.840011626895618, "network": -6.840011626895618, "new": -6.840011626895618, "newcontext": -6.840011626895618, "news": -6.840011626895618, "ng": -6.434546518787453, "no": -5.453717265775727, "node": -5.335934230119343, "not": -5.923720895021463, "note": -6.840011626895618, "nth": -6.840011626895618, "occur": -6.840011626895618, "of": -5.230573714461517, "on": -6.434546518787453, "open": -6.146864446335672, "option": -6.840011626895618, "options": -5.923720895021463, "or": -5.741399338227508, "out": -6.840011626895618, "override": -6.434546518787453, "page": -4.1658629774690885, "pages": -6.840011626895618, "password": -5.923720895021463, "pbutton": -6.840011626895618, "pid": -6.434546518787453, "pipe": -6.840011626895618, "playwright": -4.642787049559399, "private": -6.840011626895618, "problematic": -6.840011626895618, "progress": -6.840011626895618, "progresscontroller": -6.840011626895618, "protocol": -6.840011626895618, "pthread": -6.840011626895618, "pw": -6.434546518787453, "question": -6.840011626895618, "rather": -6.840011626895618, "reason": -6.146864446335672, "regexp": -6.840011626895618, "release": -6.434546518787453, "rerun": -6.840011626895618, "resolved": -6.434546518787453, "retrieve": -6.840011626895618, "retry": -6.840011626895618, "run": -6.146864446335672, "runnavigationtask": -6.840011626895618, "running": -6.434546518787453, "say": -6.840011626895618, "scope": -6.840011626895618, "scrolling": -6.434546518787453, "seconds": -6.840011626895618, "select": -6.146864446335672, "selector": -5.923720895021463, "self": -6.840011626895618, "session": -6.840011626895618, "set": -5.58724865840025, "sets": -6.840011626895618, "settimeout": -6.840011626895618, "sh": -6.840011626895618, "share": -6.434546518787453, "show": -6.840011626895618, "sidebar": -6.434546518787453, "signed": -6.840011626895618, "similar": -6.840011626895618, "single": -6.840011626895618, "site": -6.146864446335672, "slow": -6.840011626895618, "snap": -6.840011626895618, "so": -6.840011626895618, "sometimes": -6.840011626895618, "span": -6.840011626895618, "spec": -6.434546518787453, "specific": -6.434546518787453, "src": -6.840011626895618, "ssl": -6.840011626895618, "stable": -6.146864446335672, "startup": -6.840011626895618, "statement": -6.840011626895618, "status": -6.840011626895618, "stuck": -6.146864446335672, "submit": -6.840011626895618, "symbol": -6.434546518787453, "target": -4.825108606353353, "teaser": -6.840011626895618, "test": -4.642787049559399, "tests": -6.146864446335672, "text": -6.434546518787453, "that": -5.923720895021463, "the": -4.314282982587362, "there": -5.923720895021463, "this": -5.923720895021463, "throw": -6.840011626895618, "throws": -6.840011626895618, "time": -6.840011626895618, "timed": -6.840011626895618, "timeout": -3.9222408948113383, "timeouterror": -5.923720895021463, "timeouts": -5.923720895021463, "to": -4.588719828289123, "tobevisible": -6.840011626895618, "tried": -6.840011626895618, "ts": -6.434546518787453, "undefined": -6.840011626895618, "unhandledpromiserejectionwarning": -6.840011626895618, "until": -6.840011626895618, "url": -5.923720895021463, "use": -5.923720895021463, "user": -6.434546518787453, "username": -6.434546518787453, "using": -6.840011626895618, "usr": -6.840011626895618, "variable": -6.840011626895618, "version": -6.840011626895618, "view": -6.840011626895618, "visible": -5.453717265775727, "wait": -6.840011626895618, "waitforloadstate": -6.840011626895618, "waitforselector": -6.434546518787453, "waitfortimeout": -6.434546518787453, "waiting": -4.894101477840304, "waituntil": -6.840011626895618, "wamdata": -6.840011626895618, "was": -5.58724865840025, "way": -6.840011626895618, "we": -6.840011626895618, "webkit": -6.434546518787453, "when": -6.840011626895618, "while": -6.146864446335672, "whole": -6.840011626895618, "will": -6.840011626895618, "window": -6.840011626895618, "with": -6.146864446335672, "wrapper": -5.923720895021463, "x86": -6.840011626895618, "yet": -6.840011626895618, "your": -6.434546518787453 } }, "playwrightVersion": "1.61.1", "priors": { "assertion": -2.2472349979108808, "env-config": -1.6766901394432678, "flaky-by-retry": -2.7327428136925813, "locator-not-found": -2.0395956331326364, "navigation": -1.7211419020141017, "network": -2.1041341542702074, "timeout": -1.5540878173509354 }, "totals": { "assertion": 323, "env-config": 694, "flaky-by-retry": 68, "locator-not-found": 368, "navigation": 731, "network": 555, "timeout": 959 }, "version": 3, "vocabSize": 910 };
|
|
12165
|
+
}
|
|
12166
|
+
});
|
|
12167
|
+
|
|
12168
|
+
// src/analysis/tokenize.ts
|
|
12169
|
+
function tokenize(text) {
|
|
12170
|
+
return text.toLowerCase().replace(/https?:\/\/\S+/gi, " URL ").replace(/[^a-zA-Z0-9\s]/g, " ").replace(/\b\d{3,}[a-z]*\b/g, " NUM ").split(/\s+/).filter((t) => t.length > 1);
|
|
12171
|
+
}
|
|
12172
|
+
var init_tokenize = __esm({
|
|
12173
|
+
"src/analysis/tokenize.ts"() {
|
|
12174
|
+
"use strict";
|
|
12175
|
+
init_cjs_shims();
|
|
12176
|
+
}
|
|
12177
|
+
});
|
|
12178
|
+
|
|
12179
|
+
// src/analysis/NaiveBayesClassifier.ts
|
|
12180
|
+
function classify(model, text) {
|
|
12181
|
+
return classifyTokens(model, tokenize(text));
|
|
12182
|
+
}
|
|
12183
|
+
function classifyTokens(model, tokens) {
|
|
12184
|
+
const vocabTokens = tokens.filter(
|
|
12185
|
+
(tok) => tok.length > 0 && model.labels.some((l) => model.logLikelihoods[l]?.[tok] !== void 0)
|
|
12186
|
+
);
|
|
12187
|
+
if (vocabTokens.length === 0) {
|
|
12188
|
+
return { label: "unknown", margin: 0 };
|
|
12189
|
+
}
|
|
12190
|
+
const scores = {};
|
|
12191
|
+
for (const label of model.labels) {
|
|
12192
|
+
let s = model.priors[label];
|
|
12193
|
+
const oov = Math.log(model.alpha / (model.totals[label] + model.alpha * model.vocabSize));
|
|
12194
|
+
const ll = model.logLikelihoods[label];
|
|
12195
|
+
for (const tok of vocabTokens) {
|
|
12196
|
+
s += ll[tok] ?? oov;
|
|
12197
|
+
}
|
|
12198
|
+
scores[label] = s;
|
|
12199
|
+
}
|
|
12200
|
+
const ranked = Object.entries(scores).sort((a, b) => b[1] - a[1]);
|
|
12201
|
+
const topLog = ranked[0][1];
|
|
12202
|
+
const secondLog = ranked[1]?.[1] ?? topLog;
|
|
12203
|
+
const margin = (topLog - secondLog) / vocabTokens.length;
|
|
12204
|
+
return { label: ranked[0][0], margin };
|
|
12205
|
+
}
|
|
12206
|
+
function isValidNaiveBayesModel(m) {
|
|
12207
|
+
const x = m;
|
|
12208
|
+
return !!x && Array.isArray(x.labels) && x.labels.length > 0 && typeof x.version === "number" && !!x.priors && !!x.totals && !!x.logLikelihoods && typeof x.vocabSize === "number" && typeof x.alpha === "number";
|
|
12209
|
+
}
|
|
12210
|
+
var init_NaiveBayesClassifier = __esm({
|
|
12211
|
+
"src/analysis/NaiveBayesClassifier.ts"() {
|
|
12212
|
+
"use strict";
|
|
12213
|
+
init_cjs_shims();
|
|
12214
|
+
init_tokenize();
|
|
12157
12215
|
}
|
|
12158
12216
|
});
|
|
12159
12217
|
|
|
@@ -12178,10 +12236,6 @@ function verifyModelSignature(modelJson, signatureB64url) {
|
|
|
12178
12236
|
return false;
|
|
12179
12237
|
}
|
|
12180
12238
|
}
|
|
12181
|
-
function schemaValid(m) {
|
|
12182
|
-
const x = m;
|
|
12183
|
-
return !!x && Array.isArray(x.labels) && x.labels.length > 0 && typeof x.version === "number" && !!x.priors && !!x.totals && !!x.logLikelihoods && typeof x.vocabSize === "number" && typeof x.alpha === "number";
|
|
12184
|
-
}
|
|
12185
12239
|
function readCache() {
|
|
12186
12240
|
try {
|
|
12187
12241
|
const raw = import_node_fs.default.readFileSync(cacheFile(), "utf8");
|
|
@@ -12210,7 +12264,7 @@ function loadModel(autoUpdate) {
|
|
|
12210
12264
|
}
|
|
12211
12265
|
return MODEL;
|
|
12212
12266
|
}
|
|
12213
|
-
var import_node_fs, import_node_os, import_node_path, import_node_crypto, MODEL_SIG_DOMAIN, testDeps;
|
|
12267
|
+
var import_node_fs, import_node_os, import_node_path, import_node_crypto, MODEL_SIG_DOMAIN, testDeps, schemaValid;
|
|
12214
12268
|
var init_ModelStore = __esm({
|
|
12215
12269
|
"src/analysis/ModelStore.ts"() {
|
|
12216
12270
|
"use strict";
|
|
@@ -12221,62 +12275,114 @@ var init_ModelStore = __esm({
|
|
|
12221
12275
|
import_node_crypto = require("crypto");
|
|
12222
12276
|
init_pubkey_bundle();
|
|
12223
12277
|
init_model_bundle();
|
|
12278
|
+
init_NaiveBayesClassifier();
|
|
12224
12279
|
init_logger();
|
|
12225
12280
|
MODEL_SIG_DOMAIN = "reportforge-model-sig:v1\n";
|
|
12226
12281
|
testDeps = null;
|
|
12282
|
+
schemaValid = isValidNaiveBayesModel;
|
|
12227
12283
|
}
|
|
12228
12284
|
});
|
|
12229
12285
|
|
|
12230
|
-
// src/analysis/
|
|
12231
|
-
function
|
|
12232
|
-
return
|
|
12286
|
+
// src/analysis/LocalModelStore.ts
|
|
12287
|
+
function defaultLocalModelPath() {
|
|
12288
|
+
return testDeps2?.file ?? import_node_path2.default.join(import_node_os2.default.homedir(), ".reportforge", "model-local.json");
|
|
12233
12289
|
}
|
|
12234
|
-
|
|
12235
|
-
|
|
12290
|
+
function loadLocalModel(overridePath) {
|
|
12291
|
+
const file = overridePath ?? defaultLocalModelPath();
|
|
12292
|
+
const explicit = overridePath !== void 0;
|
|
12293
|
+
const reject = (why) => {
|
|
12294
|
+
const msg = `local model at ${file} not used: ${why}`;
|
|
12295
|
+
if (explicit) logger.warn(msg);
|
|
12296
|
+
else logger.debug(msg);
|
|
12297
|
+
return null;
|
|
12298
|
+
};
|
|
12299
|
+
let parsed;
|
|
12300
|
+
try {
|
|
12301
|
+
parsed = JSON.parse(import_node_fs2.default.readFileSync(file, "utf8"));
|
|
12302
|
+
} catch (e) {
|
|
12303
|
+
return explicit ? reject(`unreadable (${e.message})`) : null;
|
|
12304
|
+
}
|
|
12305
|
+
if (parsed.formatVersion !== 1) return reject(`unsupported formatVersion ${String(parsed.formatVersion)}`);
|
|
12306
|
+
if (!isValidNaiveBayesModel(parsed.model)) return reject("embedded model failed schema validation");
|
|
12307
|
+
const meta = parsed.meta;
|
|
12308
|
+
if (!meta || typeof meta.gatePassed !== "boolean") return reject("missing meta");
|
|
12309
|
+
if (!meta.gatePassed) return reject("its evaluation gate did not pass - run train-model after labeling more feedback");
|
|
12310
|
+
if (typeof meta.acceptThreshold !== "number") return reject("gate passed but no calibrated threshold (corrupt meta)");
|
|
12311
|
+
return {
|
|
12312
|
+
model: parsed.model,
|
|
12313
|
+
acceptThreshold: meta.acceptThreshold,
|
|
12314
|
+
strongThreshold: typeof meta.strongThreshold === "number" ? meta.strongThreshold : null,
|
|
12315
|
+
meta
|
|
12316
|
+
};
|
|
12317
|
+
}
|
|
12318
|
+
function saveLocalModel(fileContents, outPath) {
|
|
12319
|
+
const file = outPath ?? defaultLocalModelPath();
|
|
12320
|
+
import_node_fs2.default.mkdirSync(import_node_path2.default.dirname(file), { recursive: true });
|
|
12321
|
+
const tmp = `${file}.${process.pid}.tmp`;
|
|
12322
|
+
import_node_fs2.default.writeFileSync(tmp, JSON.stringify(fileContents, null, 2), { mode: 384 });
|
|
12323
|
+
import_node_fs2.default.renameSync(tmp, file);
|
|
12324
|
+
return file;
|
|
12325
|
+
}
|
|
12326
|
+
function readLocalModelFile(overridePath) {
|
|
12327
|
+
try {
|
|
12328
|
+
const parsed = JSON.parse(import_node_fs2.default.readFileSync(overridePath ?? defaultLocalModelPath(), "utf8"));
|
|
12329
|
+
return parsed.formatVersion === 1 && isValidNaiveBayesModel(parsed.model) ? parsed : null;
|
|
12330
|
+
} catch {
|
|
12331
|
+
return null;
|
|
12332
|
+
}
|
|
12333
|
+
}
|
|
12334
|
+
var import_node_fs2, import_node_os2, import_node_path2, testDeps2;
|
|
12335
|
+
var init_LocalModelStore = __esm({
|
|
12336
|
+
"src/analysis/LocalModelStore.ts"() {
|
|
12236
12337
|
"use strict";
|
|
12237
12338
|
init_cjs_shims();
|
|
12339
|
+
import_node_fs2 = __toESM(require("fs"));
|
|
12340
|
+
import_node_os2 = __toESM(require("os"));
|
|
12341
|
+
import_node_path2 = __toESM(require("path"));
|
|
12342
|
+
init_NaiveBayesClassifier();
|
|
12343
|
+
init_logger();
|
|
12344
|
+
testDeps2 = null;
|
|
12238
12345
|
}
|
|
12239
12346
|
});
|
|
12240
12347
|
|
|
12241
|
-
// src/analysis/
|
|
12242
|
-
function
|
|
12243
|
-
const
|
|
12244
|
-
|
|
12245
|
-
(tok) => model.labels.some((l) => model.logLikelihoods[l]?.[tok] !== void 0)
|
|
12246
|
-
);
|
|
12247
|
-
if (vocabTokens.length === 0) {
|
|
12248
|
-
return { label: "unknown", margin: 0 };
|
|
12249
|
-
}
|
|
12250
|
-
const scores = {};
|
|
12251
|
-
for (const label of model.labels) {
|
|
12252
|
-
let s = model.priors[label];
|
|
12253
|
-
const oov = Math.log(model.alpha / (model.totals[label] + model.alpha * model.vocabSize));
|
|
12254
|
-
const ll = model.logLikelihoods[label];
|
|
12255
|
-
for (const tok of vocabTokens) {
|
|
12256
|
-
s += ll[tok] ?? oov;
|
|
12257
|
-
}
|
|
12258
|
-
scores[label] = s;
|
|
12259
|
-
}
|
|
12260
|
-
const ranked = Object.entries(scores).sort((a, b) => b[1] - a[1]);
|
|
12261
|
-
const topLog = ranked[0][1];
|
|
12262
|
-
const secondLog = ranked[1]?.[1] ?? topLog;
|
|
12263
|
-
const margin = (topLog - secondLog) / vocabTokens.length;
|
|
12264
|
-
return { label: ranked[0][0], margin };
|
|
12348
|
+
// src/analysis/redact.ts
|
|
12349
|
+
function redactForStorage(raw) {
|
|
12350
|
+
const redacted = raw.replace(URI_RE, " URL ").replace(EMAIL_RE, " EMAIL ").replace(SECRET_RE, " SECRET ").replace(IP_RE, " IP ").replace(WIN_PATH_RE, " PATH ").replace(UNIX_PATH_RE, " PATH ");
|
|
12351
|
+
return tokenize(redacted).join(" ");
|
|
12265
12352
|
}
|
|
12266
|
-
|
|
12267
|
-
"
|
|
12353
|
+
function localInferenceTokens(message) {
|
|
12354
|
+
return redactForStorage(message).split(" ").filter(Boolean);
|
|
12355
|
+
}
|
|
12356
|
+
var URI_RE, EMAIL_RE, SECRET_RE, IP_RE, WIN_PATH_RE, UNIX_PATH_RE;
|
|
12357
|
+
var init_redact = __esm({
|
|
12358
|
+
"src/analysis/redact.ts"() {
|
|
12268
12359
|
"use strict";
|
|
12269
12360
|
init_cjs_shims();
|
|
12270
12361
|
init_tokenize();
|
|
12362
|
+
URI_RE = /[a-z][\w+.-]*:\/\/\S+/gi;
|
|
12363
|
+
EMAIL_RE = /\b[\w.+-]+@[\w.-]+\.\w+\b/g;
|
|
12364
|
+
SECRET_RE = /\b(?=[A-Za-z0-9_+/=-]*[0-9])(?=[A-Za-z0-9_+/=-]*[A-Za-z])[A-Za-z0-9_+/=-]{16,}\b/g;
|
|
12365
|
+
IP_RE = /\b\d{1,3}(?:\.\d{1,3}){3}\b/g;
|
|
12366
|
+
WIN_PATH_RE = /[A-Za-z]:\\[^\s'"]+/g;
|
|
12367
|
+
UNIX_PATH_RE = /(?:\/[\w.-]+){2,}/g;
|
|
12271
12368
|
}
|
|
12272
12369
|
});
|
|
12273
12370
|
|
|
12274
12371
|
// src/analysis/constants.ts
|
|
12275
|
-
var STRENGTH, STRENGTH_LEVELS, OVERRIDE_MARGIN;
|
|
12372
|
+
var CATEGORIES, STRENGTH, STRENGTH_LEVELS, OVERRIDE_MARGIN, LOCAL_TRAINING;
|
|
12276
12373
|
var init_constants = __esm({
|
|
12277
12374
|
"src/analysis/constants.ts"() {
|
|
12278
12375
|
"use strict";
|
|
12279
12376
|
init_cjs_shims();
|
|
12377
|
+
CATEGORIES = [
|
|
12378
|
+
"assertion",
|
|
12379
|
+
"env-config",
|
|
12380
|
+
"flaky-by-retry",
|
|
12381
|
+
"locator-not-found",
|
|
12382
|
+
"navigation",
|
|
12383
|
+
"network",
|
|
12384
|
+
"timeout"
|
|
12385
|
+
];
|
|
12280
12386
|
STRENGTH = {
|
|
12281
12387
|
unknownMaxMargin: 0.05,
|
|
12282
12388
|
collectMaxMargin: 0.1,
|
|
@@ -12286,6 +12392,23 @@ var init_constants = __esm({
|
|
|
12286
12392
|
};
|
|
12287
12393
|
STRENGTH_LEVELS = ["weak", "moderate", "strong"];
|
|
12288
12394
|
OVERRIDE_MARGIN = 999;
|
|
12395
|
+
LOCAL_TRAINING = {
|
|
12396
|
+
/** NB priors from fewer docs are noise, and LOOCV estimates below this are junk. */
|
|
12397
|
+
minLabeled: 10,
|
|
12398
|
+
/** A margin needs a runner-up label to exist at all. */
|
|
12399
|
+
minClasses: 2,
|
|
12400
|
+
/** Precision estimated on fewer accepted predictions is noise — refuse to calibrate. */
|
|
12401
|
+
minAccepted: 5,
|
|
12402
|
+
/** The local layer only ANSWERS when its held-out precision at the chosen threshold hits this. */
|
|
12403
|
+
acceptPrecision: 0.9,
|
|
12404
|
+
/** Bar for the local layer to render as 'strong' (else capped at 'moderate'). */
|
|
12405
|
+
strongPrecision: 0.95,
|
|
12406
|
+
/** Below this n, leave-one-out (5-fold folds would be too small to train on). */
|
|
12407
|
+
cvSwitchoverN: 50,
|
|
12408
|
+
folds: 5,
|
|
12409
|
+
/** Fixed PRNG seed — evaluation must be reproducible run-to-run. */
|
|
12410
|
+
seed: 1234
|
|
12411
|
+
};
|
|
12289
12412
|
}
|
|
12290
12413
|
});
|
|
12291
12414
|
|
|
@@ -12306,7 +12429,7 @@ function errorHeader(message) {
|
|
|
12306
12429
|
return beforeCallLog.split("\n").filter((line) => !/^\s*[-+]?\s*(?:Expected|Received)\b/i.test(line)).join("\n");
|
|
12307
12430
|
}
|
|
12308
12431
|
function isAssertionTimeout(message) {
|
|
12309
|
-
return /Expect "[^"]+" with timeout/.test(message) && /resolved to [1-9]\d* element/.test(message);
|
|
12432
|
+
return /(?:Expect "[^"]+"|expect\.\w+) with timeout/.test(message) && /resolved to [1-9]\d* element/.test(message);
|
|
12310
12433
|
}
|
|
12311
12434
|
function isNetwork(message) {
|
|
12312
12435
|
return /net::ERR_(CONNECTION|NAME_NOT_RESOLVED|INTERNET_DISCONNECTED|SSL|TIMED_OUT|ADDRESS|NETWORK_CHANGED|EMPTY_RESPONSE)|\bECONN(?:REFUSED|RESET)\b|\bENOTFOUND\b|\bEAI_AGAIN\b|getaddrinfo|socket hang up|fetch failed/.test(errorHeader(message));
|
|
@@ -12337,26 +12460,37 @@ function topFrame(stack) {
|
|
|
12337
12460
|
function representativeMessage(message) {
|
|
12338
12461
|
return clampWithEllipsis(message.split("\n")[0], MAX_MESSAGE_CHARS);
|
|
12339
12462
|
}
|
|
12340
|
-
function classifyOne(f,
|
|
12463
|
+
function classifyOne(f, chain) {
|
|
12341
12464
|
const frame = topFrame(f.error.stack);
|
|
12342
12465
|
if (f.retryHistory.some((r) => r.status === "passed")) {
|
|
12343
|
-
return { category: "flaky-by-retry", margin: OVERRIDE_MARGIN, failure: f, frame };
|
|
12466
|
+
return { category: "flaky-by-retry", margin: OVERRIDE_MARGIN, strength: "strong", source: "retry", failure: f, frame };
|
|
12344
12467
|
}
|
|
12345
12468
|
for (const [match, category] of MESSAGE_RULES) {
|
|
12346
|
-
if (match(f.error.message))
|
|
12469
|
+
if (match(f.error.message)) {
|
|
12470
|
+
return { category, margin: OVERRIDE_MARGIN, strength: "strong", source: "rule", failure: f, frame };
|
|
12471
|
+
}
|
|
12472
|
+
}
|
|
12473
|
+
if (chain.local) {
|
|
12474
|
+
const { model, acceptThreshold, strongThreshold } = chain.local;
|
|
12475
|
+
const r = classifyTokens(model, localInferenceTokens(f.error.message));
|
|
12476
|
+
if (r.label !== "unknown" && r.margin >= acceptThreshold) {
|
|
12477
|
+
const strength = strongThreshold !== null && r.margin >= strongThreshold ? "strong" : "moderate";
|
|
12478
|
+
return { category: r.label, margin: r.margin, strength, source: "local", failure: f, frame };
|
|
12479
|
+
}
|
|
12347
12480
|
}
|
|
12348
|
-
const { label, margin } = classify(
|
|
12349
|
-
return { category: label, margin, failure: f, frame };
|
|
12481
|
+
const { label, margin } = classify(chain.base, classifyInput(f));
|
|
12482
|
+
return { category: label, margin, strength: strengthOf(margin), source: "base", failure: f, frame };
|
|
12350
12483
|
}
|
|
12351
|
-
function classifyAll(failures,
|
|
12352
|
-
return failures.map((f) => classifyOne(f,
|
|
12484
|
+
function classifyAll(failures, chain) {
|
|
12485
|
+
return failures.map((f) => classifyOne(f, chain));
|
|
12353
12486
|
}
|
|
12354
12487
|
function perTestClassifications(classified) {
|
|
12355
12488
|
return classified.map((c) => ({
|
|
12356
12489
|
testId: c.failure.testId,
|
|
12357
12490
|
category: c.category,
|
|
12358
12491
|
message: representativeMessage(c.failure.error.message),
|
|
12359
|
-
strength:
|
|
12492
|
+
strength: c.strength
|
|
12493
|
+
// resolved at classification time, per producing source
|
|
12360
12494
|
}));
|
|
12361
12495
|
}
|
|
12362
12496
|
function clusterClassified(classified) {
|
|
@@ -12367,13 +12501,16 @@ function clusterClassified(classified) {
|
|
|
12367
12501
|
if (bucket) bucket.push(c);
|
|
12368
12502
|
else groups.set(key, [c]);
|
|
12369
12503
|
}
|
|
12504
|
+
const rank = Object.fromEntries(
|
|
12505
|
+
STRENGTH_LEVELS.map((level, i) => [level, i])
|
|
12506
|
+
);
|
|
12370
12507
|
const clusters = [];
|
|
12371
12508
|
for (const members of groups.values()) {
|
|
12372
|
-
const
|
|
12509
|
+
const strongest = members.reduce((a, b) => rank[b.strength] > rank[a.strength] ? b : a);
|
|
12373
12510
|
clusters.push({
|
|
12374
12511
|
category: members[0].category,
|
|
12375
|
-
strength:
|
|
12376
|
-
margin,
|
|
12512
|
+
strength: strongest.strength,
|
|
12513
|
+
margin: Math.max(...members.map((m) => m.margin)),
|
|
12377
12514
|
count: members.length,
|
|
12378
12515
|
tests: members.slice(0, MAX_TESTS_PER_CLUSTER).map((m) => m.failure.testTitle),
|
|
12379
12516
|
testsTotal: members.length,
|
|
@@ -12389,6 +12526,7 @@ var init_FailureClusterer = __esm({
|
|
|
12389
12526
|
"use strict";
|
|
12390
12527
|
init_cjs_shims();
|
|
12391
12528
|
init_NaiveBayesClassifier();
|
|
12529
|
+
init_redact();
|
|
12392
12530
|
init_constants();
|
|
12393
12531
|
init_truncate();
|
|
12394
12532
|
MAX_TESTS_PER_CLUSTER = 20;
|
|
@@ -12404,100 +12542,209 @@ var init_FailureClusterer = __esm({
|
|
|
12404
12542
|
|
|
12405
12543
|
// src/analysis/feedback-csv.ts
|
|
12406
12544
|
function parseFeedbackRows(content) {
|
|
12407
|
-
|
|
12545
|
+
const lines = content.trim().split("\n").filter(Boolean);
|
|
12546
|
+
if (lines.length === 0) return [];
|
|
12547
|
+
const header = lines[0].trim();
|
|
12548
|
+
const isV2 = header === FEEDBACK_HEADER;
|
|
12549
|
+
if (!isV2 && header !== LEGACY_FEEDBACK_HEADER) return [];
|
|
12550
|
+
const minParts = isV2 ? 4 : 3;
|
|
12551
|
+
const rows = [];
|
|
12552
|
+
for (const line of lines.slice(1)) {
|
|
12408
12553
|
const parts = line.split(",");
|
|
12409
|
-
|
|
12410
|
-
|
|
12554
|
+
if (parts.length < minParts) continue;
|
|
12555
|
+
const row = isV2 ? { category: parts[0], margin: parts[1], label: parts[2], text: parts.slice(3).join(",") } : { category: parts[0], margin: parts[1], label: "", text: parts.slice(2).join(",") };
|
|
12556
|
+
if (row.text.trim().length === 0) continue;
|
|
12557
|
+
rows.push(row);
|
|
12558
|
+
}
|
|
12559
|
+
return rows;
|
|
12560
|
+
}
|
|
12561
|
+
function serializeFeedbackRow(r) {
|
|
12562
|
+
const label = r.label.replace(/[,\r\n]/g, "").trim();
|
|
12563
|
+
return `${r.category},${r.margin},${label},${r.text}`;
|
|
12411
12564
|
}
|
|
12412
12565
|
function feedbackIdentity(category, text) {
|
|
12413
12566
|
return `${category} ${text}`;
|
|
12414
12567
|
}
|
|
12415
|
-
|
|
12568
|
+
function isTrainableLabel(label) {
|
|
12569
|
+
return CATEGORIES.includes(label);
|
|
12570
|
+
}
|
|
12571
|
+
var FEEDBACK_HEADER, LEGACY_FEEDBACK_HEADER;
|
|
12416
12572
|
var init_feedback_csv = __esm({
|
|
12417
12573
|
"src/analysis/feedback-csv.ts"() {
|
|
12418
12574
|
"use strict";
|
|
12419
12575
|
init_cjs_shims();
|
|
12420
|
-
|
|
12576
|
+
init_constants();
|
|
12577
|
+
FEEDBACK_HEADER = "category,margin,label,text";
|
|
12578
|
+
LEGACY_FEEDBACK_HEADER = "category,margin,text";
|
|
12421
12579
|
}
|
|
12422
12580
|
});
|
|
12423
12581
|
|
|
12424
|
-
// src/analysis/
|
|
12425
|
-
function
|
|
12426
|
-
const
|
|
12427
|
-
|
|
12582
|
+
// src/analysis/FeedbackStore.ts
|
|
12583
|
+
function readAllFeedback(home = import_node_os3.default.homedir()) {
|
|
12584
|
+
const root = import_node_path3.default.join(home, ".reportforge");
|
|
12585
|
+
if (!import_node_fs3.default.existsSync(root)) return [];
|
|
12586
|
+
const out = [];
|
|
12587
|
+
for (const entry of import_node_fs3.default.readdirSync(root).sort()) {
|
|
12588
|
+
const dir = import_node_path3.default.join(root, entry);
|
|
12589
|
+
const file = import_node_path3.default.join(dir, FILENAME);
|
|
12590
|
+
if (!import_node_fs3.default.existsSync(file)) continue;
|
|
12591
|
+
out.push({
|
|
12592
|
+
file,
|
|
12593
|
+
rows: parseFeedbackRows(import_node_fs3.default.readFileSync(file, "utf8")),
|
|
12594
|
+
project: readProjectMarker(dir)
|
|
12595
|
+
});
|
|
12596
|
+
}
|
|
12597
|
+
return out;
|
|
12598
|
+
}
|
|
12599
|
+
function readProjectMarker(projectDir) {
|
|
12600
|
+
try {
|
|
12601
|
+
const parsed = JSON.parse(import_node_fs3.default.readFileSync(import_node_path3.default.join(projectDir, MARKER_FILENAME), "utf8"));
|
|
12602
|
+
return typeof parsed?.cwd === "string" ? parsed : null;
|
|
12603
|
+
} catch {
|
|
12604
|
+
return null;
|
|
12605
|
+
}
|
|
12606
|
+
}
|
|
12607
|
+
function writeProjectMarker(projectDir, marker) {
|
|
12608
|
+
try {
|
|
12609
|
+
atomicWrite(import_node_path3.default.join(projectDir, MARKER_FILENAME), JSON.stringify(marker));
|
|
12610
|
+
} catch {
|
|
12611
|
+
}
|
|
12612
|
+
}
|
|
12613
|
+
function labeledSamples(files) {
|
|
12614
|
+
const samples = [];
|
|
12615
|
+
let invalidLabelCount = 0;
|
|
12616
|
+
let unlabeledCount = 0;
|
|
12617
|
+
for (const f of files) {
|
|
12618
|
+
for (const r of f.rows) {
|
|
12619
|
+
if (r.label === "") {
|
|
12620
|
+
unlabeledCount++;
|
|
12621
|
+
continue;
|
|
12622
|
+
}
|
|
12623
|
+
if (!isTrainableLabel(r.label)) {
|
|
12624
|
+
invalidLabelCount++;
|
|
12625
|
+
continue;
|
|
12626
|
+
}
|
|
12627
|
+
const tokens = r.text.split(" ").filter(Boolean);
|
|
12628
|
+
if (tokens.length === 0) continue;
|
|
12629
|
+
samples.push({ label: r.label, tokens });
|
|
12630
|
+
}
|
|
12631
|
+
}
|
|
12632
|
+
return { samples, invalidLabelCount, unlabeledCount };
|
|
12633
|
+
}
|
|
12634
|
+
function rewriteFeedbackFile(file, rows) {
|
|
12635
|
+
const body = rows.map(serializeFeedbackRow).join("\n");
|
|
12636
|
+
atomicWrite(file, `${FEEDBACK_HEADER}
|
|
12637
|
+
${body}${body ? "\n" : ""}`);
|
|
12428
12638
|
}
|
|
12429
|
-
function
|
|
12430
|
-
|
|
12639
|
+
function atomicWrite(file, content) {
|
|
12640
|
+
import_node_fs3.default.mkdirSync(import_node_path3.default.dirname(file), { recursive: true });
|
|
12641
|
+
const tmp = `${file}.${process.pid}.tmp`;
|
|
12642
|
+
import_node_fs3.default.writeFileSync(tmp, content, "utf8");
|
|
12643
|
+
import_node_fs3.default.renameSync(tmp, file);
|
|
12644
|
+
}
|
|
12645
|
+
var import_node_fs3, import_node_os3, import_node_path3, FILENAME, MARKER_FILENAME;
|
|
12646
|
+
var init_FeedbackStore = __esm({
|
|
12647
|
+
"src/analysis/FeedbackStore.ts"() {
|
|
12648
|
+
"use strict";
|
|
12649
|
+
init_cjs_shims();
|
|
12650
|
+
import_node_fs3 = __toESM(require("fs"));
|
|
12651
|
+
import_node_os3 = __toESM(require("os"));
|
|
12652
|
+
import_node_path3 = __toESM(require("path"));
|
|
12653
|
+
init_feedback_csv();
|
|
12654
|
+
FILENAME = "unclassified.csv";
|
|
12655
|
+
MARKER_FILENAME = "project.json";
|
|
12656
|
+
}
|
|
12657
|
+
});
|
|
12658
|
+
|
|
12659
|
+
// src/analysis/UnclassifiedCollector.ts
|
|
12660
|
+
function shouldCollect(c, scope) {
|
|
12661
|
+
if (c.source !== "base") return false;
|
|
12662
|
+
if (c.category === "unknown" || c.margin <= STRENGTH.collectMaxMargin) return true;
|
|
12663
|
+
return scope === "all";
|
|
12431
12664
|
}
|
|
12432
12665
|
function tildeify(p) {
|
|
12433
|
-
const home =
|
|
12666
|
+
const home = import_node_os4.default.homedir();
|
|
12434
12667
|
return p.startsWith(home) ? `~${p.slice(home.length)}` : p;
|
|
12435
12668
|
}
|
|
12436
|
-
function collectUnclassified(classified, projectDir) {
|
|
12437
|
-
const
|
|
12669
|
+
function collectUnclassified(classified, projectDir, opts = {}) {
|
|
12670
|
+
const scope = opts.scope ?? "blind-spots";
|
|
12671
|
+
const rows = classified.filter((c) => shouldCollect(c, scope)).map((c) => ({ text: redactForStorage(c.failure.error.message), category: c.category, margin: c.margin })).filter((r) => r.text.length > 0);
|
|
12438
12672
|
if (rows.length === 0) return;
|
|
12439
|
-
const file =
|
|
12440
|
-
const firstTime = !
|
|
12673
|
+
const file = import_node_path4.default.join(projectDir, FILENAME2);
|
|
12674
|
+
const firstTime = !import_node_fs4.default.existsSync(file);
|
|
12441
12675
|
const byIdentity = /* @__PURE__ */ new Map();
|
|
12442
12676
|
if (!firstTime) {
|
|
12443
|
-
for (const r of parseFeedbackRows(
|
|
12444
|
-
byIdentity.set(feedbackIdentity(r.category, r.text), r
|
|
12677
|
+
for (const r of parseFeedbackRows(import_node_fs4.default.readFileSync(file, "utf8"))) {
|
|
12678
|
+
byIdentity.set(feedbackIdentity(r.category, r.text), r);
|
|
12445
12679
|
}
|
|
12446
12680
|
}
|
|
12447
|
-
let added = false;
|
|
12448
12681
|
for (const r of rows) {
|
|
12449
12682
|
const id = feedbackIdentity(r.category, r.text);
|
|
12683
|
+
const label = byIdentity.get(id)?.label ?? "";
|
|
12450
12684
|
byIdentity.delete(id);
|
|
12451
|
-
byIdentity.set(id,
|
|
12452
|
-
|
|
12453
|
-
|
|
12454
|
-
|
|
12455
|
-
|
|
12456
|
-
|
|
12457
|
-
|
|
12458
|
-
|
|
12459
|
-
`, "utf8");
|
|
12685
|
+
byIdentity.set(id, { category: r.category, margin: String(r.margin), label, text: r.text });
|
|
12686
|
+
}
|
|
12687
|
+
const all = evictOldestUnlabeledFirst([...byIdentity.values()], MAX_ROWS);
|
|
12688
|
+
import_node_fs4.default.mkdirSync(projectDir, { recursive: true });
|
|
12689
|
+
rewriteFeedbackFile(file, all);
|
|
12690
|
+
if (opts.project) {
|
|
12691
|
+
writeProjectMarker(projectDir, { cwd: opts.project.cwd, outputFile: opts.project.outputFile, updatedAt: Date.now() });
|
|
12692
|
+
}
|
|
12460
12693
|
if (firstTime) {
|
|
12461
12694
|
logger.info(
|
|
12462
12695
|
`Unrecognized failures collected locally at ${tildeify(file)} to improve offline classification. Local only - never sent anywhere. Disable: failureAnalysis.collectUnclassified=false`
|
|
12463
12696
|
);
|
|
12464
12697
|
}
|
|
12465
12698
|
}
|
|
12466
|
-
|
|
12699
|
+
function evictOldestUnlabeledFirst(rows, max) {
|
|
12700
|
+
const excess = rows.length - max;
|
|
12701
|
+
if (excess <= 0) return rows;
|
|
12702
|
+
let unlabeledToDrop = Math.min(excess, rows.filter((r) => r.label === "").length);
|
|
12703
|
+
let labeledToDrop = excess - unlabeledToDrop;
|
|
12704
|
+
const out = [];
|
|
12705
|
+
for (const r of rows) {
|
|
12706
|
+
if (r.label === "" && unlabeledToDrop > 0) {
|
|
12707
|
+
unlabeledToDrop--;
|
|
12708
|
+
continue;
|
|
12709
|
+
}
|
|
12710
|
+
if (r.label !== "" && labeledToDrop > 0) {
|
|
12711
|
+
labeledToDrop--;
|
|
12712
|
+
continue;
|
|
12713
|
+
}
|
|
12714
|
+
out.push(r);
|
|
12715
|
+
}
|
|
12716
|
+
return out;
|
|
12717
|
+
}
|
|
12718
|
+
var import_node_fs4, import_node_os4, import_node_path4, MAX_ROWS, FILENAME2;
|
|
12467
12719
|
var init_UnclassifiedCollector = __esm({
|
|
12468
12720
|
"src/analysis/UnclassifiedCollector.ts"() {
|
|
12469
12721
|
"use strict";
|
|
12470
12722
|
init_cjs_shims();
|
|
12471
|
-
|
|
12472
|
-
|
|
12473
|
-
|
|
12474
|
-
init_tokenize();
|
|
12723
|
+
import_node_fs4 = __toESM(require("fs"));
|
|
12724
|
+
import_node_os4 = __toESM(require("os"));
|
|
12725
|
+
import_node_path4 = __toESM(require("path"));
|
|
12475
12726
|
init_constants();
|
|
12476
12727
|
init_feedback_csv();
|
|
12728
|
+
init_redact();
|
|
12729
|
+
init_FeedbackStore();
|
|
12477
12730
|
init_logger();
|
|
12478
12731
|
MAX_ROWS = 500;
|
|
12479
|
-
|
|
12480
|
-
URI_RE = /[a-z][\w+.-]*:\/\/\S+/gi;
|
|
12481
|
-
EMAIL_RE = /\b[\w.+-]+@[\w.-]+\.\w+\b/g;
|
|
12482
|
-
SECRET_RE = /\b(?=[A-Za-z0-9_+/=-]*[0-9])(?=[A-Za-z0-9_+/=-]*[A-Za-z])[A-Za-z0-9_+/=-]{16,}\b/g;
|
|
12483
|
-
IP_RE = /\b\d{1,3}(?:\.\d{1,3}){3}\b/g;
|
|
12484
|
-
WIN_PATH_RE = /[A-Za-z]:\\[^\s'"]+/g;
|
|
12485
|
-
UNIX_PATH_RE = /(?:\/[\w.-]+){2,}/g;
|
|
12732
|
+
FILENAME2 = "unclassified.csv";
|
|
12486
12733
|
}
|
|
12487
12734
|
});
|
|
12488
12735
|
|
|
12489
12736
|
// src/utils/paths.ts
|
|
12490
12737
|
function resolveProjectDir(options, cwd) {
|
|
12491
12738
|
const projectKey = import_node_crypto2.default.createHash("sha256").update(cwd + (options.outputFile ?? "")).digest("hex").slice(0, 8);
|
|
12492
|
-
return
|
|
12739
|
+
return import_node_path5.default.join(import_node_os5.default.homedir(), ".reportforge", projectKey);
|
|
12493
12740
|
}
|
|
12494
|
-
var
|
|
12741
|
+
var import_node_os5, import_node_path5, import_node_crypto2;
|
|
12495
12742
|
var init_paths = __esm({
|
|
12496
12743
|
"src/utils/paths.ts"() {
|
|
12497
12744
|
"use strict";
|
|
12498
12745
|
init_cjs_shims();
|
|
12499
|
-
|
|
12500
|
-
|
|
12746
|
+
import_node_os5 = __toESM(require("os"));
|
|
12747
|
+
import_node_path5 = __toESM(require("path"));
|
|
12501
12748
|
import_node_crypto2 = __toESM(require("crypto"));
|
|
12502
12749
|
}
|
|
12503
12750
|
});
|
|
@@ -12530,27 +12777,49 @@ function runFailureAnalysis(reportData, options, cwd) {
|
|
|
12530
12777
|
if (reportData.stats.failed === 0 || !fa.enabled) return;
|
|
12531
12778
|
const cap = fa.maxFailuresToAnalyse;
|
|
12532
12779
|
const analysed = reportData.failures.slice(0, cap);
|
|
12533
|
-
const
|
|
12534
|
-
const
|
|
12780
|
+
const base = loadModel(fa.autoUpdateModel);
|
|
12781
|
+
const local = fa.localModel ? loadLocalModel(fa.localModelPath) ?? void 0 : void 0;
|
|
12782
|
+
if (local) {
|
|
12783
|
+
logger.debug(
|
|
12784
|
+
`local model active (trained ${local.meta.trainedAt}, ${local.meta.labeledCount} labeled rows, accept margin >= ${local.acceptThreshold})`
|
|
12785
|
+
);
|
|
12786
|
+
if (local.meta.baseVersionAtEval !== base.version) {
|
|
12787
|
+
logger.debug(
|
|
12788
|
+
`local model was gate-checked against base v${local.meta.baseVersionAtEval} but base v${base.version} is active - re-run train-model to re-validate against the current base`
|
|
12789
|
+
);
|
|
12790
|
+
}
|
|
12791
|
+
}
|
|
12792
|
+
const classified = classifyAll(analysed, { base, local });
|
|
12793
|
+
logger.debug(`failure analysis: ${classified.length} classified - ${sourceTally(classified)}`);
|
|
12535
12794
|
reportData.classifications = perTestClassifications(classified);
|
|
12536
12795
|
if (templatesConsumeAnalysis(options)) {
|
|
12537
12796
|
reportData.analysis = analyseClassified(classified, reportData.failures.length, fa);
|
|
12538
12797
|
}
|
|
12539
12798
|
if (fa.collectUnclassified) {
|
|
12540
|
-
collectUnclassified(classified, resolveProjectDir(options, cwd)
|
|
12799
|
+
collectUnclassified(classified, resolveProjectDir(options, cwd), {
|
|
12800
|
+
scope: fa.collectScope,
|
|
12801
|
+
project: { cwd, outputFile: options.outputFile }
|
|
12802
|
+
});
|
|
12541
12803
|
}
|
|
12542
12804
|
}
|
|
12805
|
+
function sourceTally(classified) {
|
|
12806
|
+
const counts = /* @__PURE__ */ new Map();
|
|
12807
|
+
for (const c of classified) counts.set(c.source, (counts.get(c.source) ?? 0) + 1);
|
|
12808
|
+
return ["rule", "retry", "local", "base"].filter((s) => counts.has(s)).map((s) => `${counts.get(s)} ${s}`).join(" / ");
|
|
12809
|
+
}
|
|
12543
12810
|
var DEFAULTS, STRENGTH_RANK;
|
|
12544
12811
|
var init_analysis = __esm({
|
|
12545
12812
|
"src/analysis/index.ts"() {
|
|
12546
12813
|
"use strict";
|
|
12547
12814
|
init_cjs_shims();
|
|
12548
12815
|
init_ModelStore();
|
|
12816
|
+
init_LocalModelStore();
|
|
12549
12817
|
init_FailureClusterer();
|
|
12550
12818
|
init_SummaryWriter();
|
|
12551
12819
|
init_constants();
|
|
12552
12820
|
init_UnclassifiedCollector();
|
|
12553
12821
|
init_paths();
|
|
12822
|
+
init_logger();
|
|
12554
12823
|
DEFAULTS = { maxClusters: 10, minStrength: "weak", maxFailuresToAnalyse: 500 };
|
|
12555
12824
|
STRENGTH_RANK = Object.fromEntries(
|
|
12556
12825
|
STRENGTH_LEVELS.map((level, i) => [level, i])
|
|
@@ -12582,7 +12851,7 @@ async function run() {
|
|
|
12582
12851
|
console.log(` Template : ${template}`);
|
|
12583
12852
|
console.log(` Output : ${outputFile}`);
|
|
12584
12853
|
const reportData = buildDemoReportData(template);
|
|
12585
|
-
const options = parseOptions({ template, outputFile, failureAnalysis: { collectUnclassified: false } });
|
|
12854
|
+
const options = parseOptions({ template, outputFile, failureAnalysis: { collectUnclassified: false, localModel: false } });
|
|
12586
12855
|
const generator = new PdfGenerator();
|
|
12587
12856
|
runFailureAnalysis(reportData, options, process.cwd());
|
|
12588
12857
|
reportData.briefSentence = buildBriefSentence(reportData.stats, reportData.analysis, reportData.charts.trend);
|
|
@@ -12646,22 +12915,12 @@ __export(export_feedback_exports, {
|
|
|
12646
12915
|
exportFeedbackCli: () => exportFeedbackCli
|
|
12647
12916
|
});
|
|
12648
12917
|
function exportFeedback(deps = {}) {
|
|
12649
|
-
const home = deps.home ??
|
|
12918
|
+
const home = deps.home ?? import_node_os6.default.homedir();
|
|
12650
12919
|
const cwd = deps.cwd ?? process.cwd();
|
|
12651
|
-
const
|
|
12652
|
-
const rows = [];
|
|
12653
|
-
if (import_node_fs3.default.existsSync(root)) {
|
|
12654
|
-
for (const entry of import_node_fs3.default.readdirSync(root)) {
|
|
12655
|
-
const file = import_node_path4.default.join(root, entry, "unclassified.csv");
|
|
12656
|
-
if (!import_node_fs3.default.existsSync(file)) continue;
|
|
12657
|
-
for (const r of parseFeedbackRows(import_node_fs3.default.readFileSync(file, "utf8"))) {
|
|
12658
|
-
rows.push(r.raw);
|
|
12659
|
-
}
|
|
12660
|
-
}
|
|
12661
|
-
}
|
|
12920
|
+
const rows = readAllFeedback(home).flatMap((f) => f.rows.map(serializeFeedbackRow));
|
|
12662
12921
|
if (rows.length === 0) return { rowCount: 0, outFile: null };
|
|
12663
|
-
const outFile =
|
|
12664
|
-
|
|
12922
|
+
const outFile = import_node_path6.default.join(cwd, "reportforge-feedback.csv");
|
|
12923
|
+
import_node_fs5.default.writeFileSync(outFile, `${FEEDBACK_HEADER}
|
|
12665
12924
|
${rows.join("\n")}
|
|
12666
12925
|
`, "utf8");
|
|
12667
12926
|
return { rowCount: rows.length, outFile };
|
|
@@ -12678,15 +12937,558 @@ Review it before sharing; there is no upload.
|
|
|
12678
12937
|
);
|
|
12679
12938
|
}
|
|
12680
12939
|
}
|
|
12681
|
-
var
|
|
12940
|
+
var import_node_fs5, import_node_os6, import_node_path6;
|
|
12682
12941
|
var init_export_feedback = __esm({
|
|
12683
12942
|
"src/cli/export-feedback.ts"() {
|
|
12684
12943
|
"use strict";
|
|
12685
12944
|
init_cjs_shims();
|
|
12686
|
-
|
|
12687
|
-
|
|
12688
|
-
|
|
12945
|
+
import_node_fs5 = __toESM(require("fs"));
|
|
12946
|
+
import_node_os6 = __toESM(require("os"));
|
|
12947
|
+
import_node_path6 = __toESM(require("path"));
|
|
12689
12948
|
init_feedback_csv();
|
|
12949
|
+
init_FeedbackStore();
|
|
12950
|
+
}
|
|
12951
|
+
});
|
|
12952
|
+
|
|
12953
|
+
// src/cli/label-feedback.ts
|
|
12954
|
+
var label_feedback_exports = {};
|
|
12955
|
+
__export(label_feedback_exports, {
|
|
12956
|
+
labelFeedback: () => labelFeedback,
|
|
12957
|
+
labelFeedbackCli: () => labelFeedbackCli
|
|
12958
|
+
});
|
|
12959
|
+
async function labelFeedback(deps) {
|
|
12960
|
+
const { io } = deps;
|
|
12961
|
+
if (!io.isTTY) {
|
|
12962
|
+
io.write(`${nonInteractiveHelp()}
|
|
12963
|
+
`);
|
|
12964
|
+
return { labeled: 0, skipped: 0, remaining: countUnlabeled(deps.home), nonInteractive: true };
|
|
12965
|
+
}
|
|
12966
|
+
const files = readAllFeedback(deps.home ?? import_node_os7.default.homedir());
|
|
12967
|
+
const menu = CATEGORIES.map((c, i) => ` ${i + 1}) ${c}`).join("\n");
|
|
12968
|
+
let labeled = 0;
|
|
12969
|
+
let skipped = 0;
|
|
12970
|
+
let quit = false;
|
|
12971
|
+
io.write(`${CONVENTIONS}
|
|
12972
|
+
|
|
12973
|
+
`);
|
|
12974
|
+
for (const f of files) {
|
|
12975
|
+
if (quit) break;
|
|
12976
|
+
for (let i = 0; i < f.rows.length && !quit; i++) {
|
|
12977
|
+
const row = f.rows[i];
|
|
12978
|
+
if (row.label !== "") continue;
|
|
12979
|
+
io.write(
|
|
12980
|
+
`
|
|
12981
|
+
project: ${f.project?.cwd ?? "(unknown - marker written on next run)"}
|
|
12982
|
+
guess: ${row.category} @ margin ${row.margin}
|
|
12983
|
+
tokens: ${row.text}
|
|
12984
|
+
${menu}
|
|
12985
|
+
s) skip q) quit
|
|
12986
|
+
`
|
|
12987
|
+
);
|
|
12988
|
+
for (; ; ) {
|
|
12989
|
+
const answer = (await io.question("label> ")).trim().toLowerCase();
|
|
12990
|
+
if (answer === "q") {
|
|
12991
|
+
quit = true;
|
|
12992
|
+
break;
|
|
12993
|
+
}
|
|
12994
|
+
if (answer === "s") {
|
|
12995
|
+
skipped++;
|
|
12996
|
+
break;
|
|
12997
|
+
}
|
|
12998
|
+
const idx = Number.parseInt(answer, 10) - 1;
|
|
12999
|
+
if (Number.isInteger(idx) && idx >= 0 && idx < CATEGORIES.length) {
|
|
13000
|
+
row.label = CATEGORIES[idx];
|
|
13001
|
+
rewriteFeedbackFile(f.file, f.rows);
|
|
13002
|
+
labeled++;
|
|
13003
|
+
break;
|
|
13004
|
+
}
|
|
13005
|
+
io.write(` enter 1-${CATEGORIES.length}, s, or q
|
|
13006
|
+
`);
|
|
13007
|
+
}
|
|
13008
|
+
}
|
|
13009
|
+
}
|
|
13010
|
+
const remaining = files.flatMap((f) => f.rows).filter((r) => r.label === "").length;
|
|
13011
|
+
io.write(
|
|
13012
|
+
`
|
|
13013
|
+
labeled ${labeled}, skipped ${skipped}, ${remaining} still unlabeled.
|
|
13014
|
+
` + (labeled > 0 ? "next: npx @reportforge/playwright-pdf train-model\n" : "")
|
|
13015
|
+
);
|
|
13016
|
+
return { labeled, skipped, remaining, nonInteractive: false };
|
|
13017
|
+
}
|
|
13018
|
+
function countUnlabeled(home) {
|
|
13019
|
+
return readAllFeedback(home ?? import_node_os7.default.homedir()).flatMap((f) => f.rows).filter((r) => r.label === "").length;
|
|
13020
|
+
}
|
|
13021
|
+
async function labelFeedbackCli() {
|
|
13022
|
+
const { createInterface } = await import("readline/promises");
|
|
13023
|
+
const rl = createInterface({ input: process.stdin, output: process.stdout });
|
|
13024
|
+
const isTTY = Boolean(process.stdin.isTTY && process.stdout.isTTY);
|
|
13025
|
+
const io = {
|
|
13026
|
+
question: (p) => rl.question(p),
|
|
13027
|
+
// Interactive chatter → stdout; the non-interactive help is an ERROR message → stderr.
|
|
13028
|
+
write: (s) => void (isTTY ? process.stdout : process.stderr).write(s),
|
|
13029
|
+
isTTY
|
|
13030
|
+
};
|
|
13031
|
+
try {
|
|
13032
|
+
const res = await labelFeedback({ io });
|
|
13033
|
+
if (res.nonInteractive) process.exitCode = 1;
|
|
13034
|
+
} finally {
|
|
13035
|
+
rl.close();
|
|
13036
|
+
}
|
|
13037
|
+
}
|
|
13038
|
+
var import_node_os7, CONVENTIONS, nonInteractiveHelp;
|
|
13039
|
+
var init_label_feedback = __esm({
|
|
13040
|
+
"src/cli/label-feedback.ts"() {
|
|
13041
|
+
"use strict";
|
|
13042
|
+
init_cjs_shims();
|
|
13043
|
+
import_node_os7 = __toESM(require("os"));
|
|
13044
|
+
init_FeedbackStore();
|
|
13045
|
+
init_constants();
|
|
13046
|
+
CONVENTIONS = [
|
|
13047
|
+
"Labeling conventions:",
|
|
13048
|
+
' - a timed-out page.goto / "Navigation timeout exceeded" is a TIMEOUT (navigation = aborted/interrupted/crashed, not slow)',
|
|
13049
|
+
' - "Test timeout ... waiting for locator" is a TIMEOUT when the element existed but the test clock ran out,',
|
|
13050
|
+
" and LOCATOR-NOT-FOUND when the element demonstrably never existed (resolved to 0 / no such element)",
|
|
13051
|
+
" - skip anything you are not sure about - a wrong label is worse than no label"
|
|
13052
|
+
].join("\n");
|
|
13053
|
+
nonInteractiveHelp = () => [
|
|
13054
|
+
"label-feedback needs an interactive terminal (TTY) - run it directly, not through a pipe or CI.",
|
|
13055
|
+
`Hand-edit alternative: open ~/.reportforge/<project>/unclassified.csv and fill the empty 'label'`,
|
|
13056
|
+
`column with one of: ${CATEGORIES.join(" | ")}`,
|
|
13057
|
+
"Then run: npx @reportforge/playwright-pdf train-model"
|
|
13058
|
+
].join("\n");
|
|
13059
|
+
}
|
|
13060
|
+
});
|
|
13061
|
+
|
|
13062
|
+
// src/analysis/LocalTrainer.ts
|
|
13063
|
+
function trainNaiveBayes(samples, alpha = 1) {
|
|
13064
|
+
if (samples.length === 0) throw new Error("trainNaiveBayes: no samples");
|
|
13065
|
+
const labels = [...new Set(samples.map((s) => s.label))].sort();
|
|
13066
|
+
const counts = new Map(labels.map((l) => [l, /* @__PURE__ */ new Map()]));
|
|
13067
|
+
const totals = Object.fromEntries(labels.map((l) => [l, 0]));
|
|
13068
|
+
const labelDocs = Object.fromEntries(labels.map((l) => [l, 0]));
|
|
13069
|
+
const vocab = /* @__PURE__ */ new Set();
|
|
13070
|
+
for (const { label, tokens } of samples) {
|
|
13071
|
+
labelDocs[label]++;
|
|
13072
|
+
const c = counts.get(label);
|
|
13073
|
+
for (const tok of tokens) {
|
|
13074
|
+
c.set(tok, (c.get(tok) ?? 0) + 1);
|
|
13075
|
+
totals[label]++;
|
|
13076
|
+
vocab.add(tok);
|
|
13077
|
+
}
|
|
13078
|
+
}
|
|
13079
|
+
const n = samples.length;
|
|
13080
|
+
const vocabSize = vocab.size;
|
|
13081
|
+
const priors = {};
|
|
13082
|
+
const logLikelihoods = {};
|
|
13083
|
+
for (const l of labels) {
|
|
13084
|
+
priors[l] = Math.log(labelDocs[l] / n);
|
|
13085
|
+
const ll = {};
|
|
13086
|
+
for (const [tok, c] of counts.get(l)) {
|
|
13087
|
+
ll[tok] = Math.log((c + alpha) / (totals[l] + alpha * vocabSize));
|
|
13088
|
+
}
|
|
13089
|
+
logLikelihoods[l] = ll;
|
|
13090
|
+
}
|
|
13091
|
+
return {
|
|
13092
|
+
labels,
|
|
13093
|
+
priors,
|
|
13094
|
+
logLikelihoods,
|
|
13095
|
+
totals,
|
|
13096
|
+
vocabSize,
|
|
13097
|
+
alpha,
|
|
13098
|
+
version: 0,
|
|
13099
|
+
playwrightVersion: "local"
|
|
13100
|
+
};
|
|
13101
|
+
}
|
|
13102
|
+
var init_LocalTrainer = __esm({
|
|
13103
|
+
"src/analysis/LocalTrainer.ts"() {
|
|
13104
|
+
"use strict";
|
|
13105
|
+
init_cjs_shims();
|
|
13106
|
+
}
|
|
13107
|
+
});
|
|
13108
|
+
|
|
13109
|
+
// src/analysis/LocalEvaluator.ts
|
|
13110
|
+
function evaluateChain(samples, base, opts = {}) {
|
|
13111
|
+
const seed = opts.seed ?? LOCAL_TRAINING.seed;
|
|
13112
|
+
const method = samples.length < LOCAL_TRAINING.cvSwitchoverN ? "loocv" : "stratified-5fold";
|
|
13113
|
+
const folds = method === "loocv" ? samples.map((_, i) => i) : stratifiedFolds(samples, LOCAL_TRAINING.folds, seed);
|
|
13114
|
+
const foldCount = method === "loocv" ? samples.length : LOCAL_TRAINING.folds;
|
|
13115
|
+
const oof = samples.map((s) => ({
|
|
13116
|
+
trueLabel: s.label,
|
|
13117
|
+
base: pick(classifyTokens(base, s.tokens)),
|
|
13118
|
+
local: { label: "unknown", margin: 0 }
|
|
13119
|
+
// filled below
|
|
13120
|
+
}));
|
|
13121
|
+
for (let k = 0; k < foldCount; k++) {
|
|
13122
|
+
const trainRows = samples.filter((_, i) => folds[i] !== k);
|
|
13123
|
+
if (trainRows.length === 0) continue;
|
|
13124
|
+
const foldModel = trainNaiveBayes(trainRows);
|
|
13125
|
+
samples.forEach((s, i) => {
|
|
13126
|
+
if (folds[i] === k) oof[i].local = pick(classifyTokens(foldModel, s.tokens));
|
|
13127
|
+
});
|
|
13128
|
+
}
|
|
13129
|
+
const reasons = [];
|
|
13130
|
+
const acceptThreshold = calibrate(oof, LOCAL_TRAINING.acceptPrecision);
|
|
13131
|
+
const strongThreshold = acceptThreshold === null ? null : calibrate(oof, LOCAL_TRAINING.strongPrecision, acceptThreshold);
|
|
13132
|
+
const classes = [...new Set(samples.map((s) => s.label))].sort();
|
|
13133
|
+
const baseMetrics = computeMetrics(oof.map((p) => ({ trueLabel: p.trueLabel, pred: p.base.label })), classes);
|
|
13134
|
+
const chainMetrics = computeMetrics(
|
|
13135
|
+
oof.map((p) => ({
|
|
13136
|
+
trueLabel: p.trueLabel,
|
|
13137
|
+
pred: acceptThreshold !== null && p.local.label !== "unknown" && p.local.margin >= acceptThreshold ? p.local.label : p.base.label
|
|
13138
|
+
})),
|
|
13139
|
+
classes
|
|
13140
|
+
);
|
|
13141
|
+
let gatePassed = false;
|
|
13142
|
+
if (acceptThreshold === null) {
|
|
13143
|
+
reasons.push(
|
|
13144
|
+
`gate FAILED: no margin threshold reaches ${LOCAL_TRAINING.acceptPrecision * 100}% precision with >= ${LOCAL_TRAINING.minAccepted} accepted held-out predictions - the local model is never confident enough to accept safely`
|
|
13145
|
+
);
|
|
13146
|
+
} else if (chainMetrics.macroF1 <= baseMetrics.macroF1) {
|
|
13147
|
+
reasons.push(
|
|
13148
|
+
`gate FAILED: chain macro-F1 ${fmt(chainMetrics.macroF1)} does not beat base ${fmt(baseMetrics.macroF1)} (tie keeps the champion)`
|
|
13149
|
+
);
|
|
13150
|
+
} else {
|
|
13151
|
+
gatePassed = true;
|
|
13152
|
+
reasons.push(
|
|
13153
|
+
`gate passed: chain macro-F1 ${fmt(chainMetrics.macroF1)} > base ${fmt(baseMetrics.macroF1)}; the local layer answers only above its calibrated margin threshold` + (strongThreshold !== null ? "" : " and never renders 'strong'")
|
|
13154
|
+
);
|
|
13155
|
+
}
|
|
13156
|
+
return {
|
|
13157
|
+
method,
|
|
13158
|
+
n: samples.length,
|
|
13159
|
+
classes,
|
|
13160
|
+
base: baseMetrics,
|
|
13161
|
+
chain: chainMetrics,
|
|
13162
|
+
acceptThreshold,
|
|
13163
|
+
strongThreshold,
|
|
13164
|
+
gatePassed,
|
|
13165
|
+
reasons,
|
|
13166
|
+
oofLocal: oof.map((p) => p.local)
|
|
13167
|
+
};
|
|
13168
|
+
}
|
|
13169
|
+
function transferThresholds(result, samples, finalModel) {
|
|
13170
|
+
const finalMarginsOver = (oofThreshold) => result.oofLocal.map((p, i) => ({ p, i })).filter(({ p }) => p.label !== "unknown" && p.margin >= oofThreshold).map(({ i }) => classifyTokens(finalModel, samples[i].tokens)).filter((r) => r.label !== "unknown").map((r) => r.margin);
|
|
13171
|
+
if (result.acceptThreshold === null) return { acceptThreshold: null, strongThreshold: null };
|
|
13172
|
+
const accept = finalMarginsOver(result.acceptThreshold);
|
|
13173
|
+
if (accept.length === 0) return { acceptThreshold: null, strongThreshold: null };
|
|
13174
|
+
const acceptThreshold = Math.min(...accept);
|
|
13175
|
+
if (result.strongThreshold === null) return { acceptThreshold, strongThreshold: null };
|
|
13176
|
+
const strong = finalMarginsOver(result.strongThreshold);
|
|
13177
|
+
return {
|
|
13178
|
+
acceptThreshold,
|
|
13179
|
+
// 'strong' must never be easier to reach than acceptance itself.
|
|
13180
|
+
strongThreshold: strong.length === 0 ? null : Math.max(Math.min(...strong), acceptThreshold)
|
|
13181
|
+
};
|
|
13182
|
+
}
|
|
13183
|
+
function calibrate(oof, target, atLeast = -Infinity) {
|
|
13184
|
+
const scored = oof.filter((p) => p.local.label !== "unknown");
|
|
13185
|
+
const candidates = [...new Set(scored.map((p) => p.local.margin))].filter((t) => t >= atLeast).sort((a, b) => a - b);
|
|
13186
|
+
for (const t of candidates) {
|
|
13187
|
+
const accepted = scored.filter((p) => p.local.margin >= t);
|
|
13188
|
+
if (accepted.length < LOCAL_TRAINING.minAccepted) break;
|
|
13189
|
+
const correct = accepted.filter((p) => p.local.label === p.trueLabel).length;
|
|
13190
|
+
if (correct / accepted.length >= target) return t;
|
|
13191
|
+
}
|
|
13192
|
+
return null;
|
|
13193
|
+
}
|
|
13194
|
+
function computeMetrics(preds, classes) {
|
|
13195
|
+
var _a;
|
|
13196
|
+
const confusion = {};
|
|
13197
|
+
for (const p of preds) {
|
|
13198
|
+
(confusion[_a = p.trueLabel] ?? (confusion[_a] = {}))[p.pred] = (confusion[p.trueLabel][p.pred] ?? 0) + 1;
|
|
13199
|
+
}
|
|
13200
|
+
const perClass = {};
|
|
13201
|
+
let f1Sum = 0;
|
|
13202
|
+
for (const c of classes) {
|
|
13203
|
+
const tp = preds.filter((p) => p.trueLabel === c && p.pred === c).length;
|
|
13204
|
+
const fp = preds.filter((p) => p.trueLabel !== c && p.pred === c).length;
|
|
13205
|
+
const fn = preds.filter((p) => p.trueLabel === c && p.pred !== c).length;
|
|
13206
|
+
const precision = tp + fp === 0 ? 0 : tp / (tp + fp);
|
|
13207
|
+
const recall = tp + fn === 0 ? 0 : tp / (tp + fn);
|
|
13208
|
+
const f1 = precision + recall === 0 ? 0 : 2 * precision * recall / (precision + recall);
|
|
13209
|
+
perClass[c] = { precision, recall, f1, support: tp + fn };
|
|
13210
|
+
f1Sum += f1;
|
|
13211
|
+
}
|
|
13212
|
+
return {
|
|
13213
|
+
accuracy: preds.length === 0 ? 0 : preds.filter((p) => p.pred === p.trueLabel).length / preds.length,
|
|
13214
|
+
macroF1: classes.length === 0 ? 0 : f1Sum / classes.length,
|
|
13215
|
+
perClass,
|
|
13216
|
+
confusion
|
|
13217
|
+
};
|
|
13218
|
+
}
|
|
13219
|
+
function stratifiedFolds(samples, folds, seed) {
|
|
13220
|
+
const rng = mulberry32(seed);
|
|
13221
|
+
const byLabel = /* @__PURE__ */ new Map();
|
|
13222
|
+
samples.forEach((s, i) => {
|
|
13223
|
+
const arr = byLabel.get(s.label);
|
|
13224
|
+
if (arr) arr.push(i);
|
|
13225
|
+
else byLabel.set(s.label, [i]);
|
|
13226
|
+
});
|
|
13227
|
+
const assignment = new Array(samples.length);
|
|
13228
|
+
for (const label of [...byLabel.keys()].sort()) {
|
|
13229
|
+
const indices = shuffle(byLabel.get(label), rng);
|
|
13230
|
+
indices.forEach((rowIdx, j) => {
|
|
13231
|
+
assignment[rowIdx] = j % folds;
|
|
13232
|
+
});
|
|
13233
|
+
}
|
|
13234
|
+
return assignment;
|
|
13235
|
+
}
|
|
13236
|
+
function shuffle(arr, rng) {
|
|
13237
|
+
const a = [...arr];
|
|
13238
|
+
for (let i = a.length - 1; i > 0; i--) {
|
|
13239
|
+
const j = Math.floor(rng() * (i + 1));
|
|
13240
|
+
[a[i], a[j]] = [a[j], a[i]];
|
|
13241
|
+
}
|
|
13242
|
+
return a;
|
|
13243
|
+
}
|
|
13244
|
+
function mulberry32(seed) {
|
|
13245
|
+
let a = seed >>> 0;
|
|
13246
|
+
return () => {
|
|
13247
|
+
a |= 0;
|
|
13248
|
+
a = a + 1831565813 | 0;
|
|
13249
|
+
let t = Math.imul(a ^ a >>> 15, 1 | a);
|
|
13250
|
+
t = t + Math.imul(t ^ t >>> 7, 61 | t) ^ t;
|
|
13251
|
+
return ((t ^ t >>> 14) >>> 0) / 4294967296;
|
|
13252
|
+
};
|
|
13253
|
+
}
|
|
13254
|
+
var pick, fmt;
|
|
13255
|
+
var init_LocalEvaluator = __esm({
|
|
13256
|
+
"src/analysis/LocalEvaluator.ts"() {
|
|
13257
|
+
"use strict";
|
|
13258
|
+
init_cjs_shims();
|
|
13259
|
+
init_NaiveBayesClassifier();
|
|
13260
|
+
init_LocalTrainer();
|
|
13261
|
+
init_constants();
|
|
13262
|
+
pick = (r) => ({ label: r.label, margin: r.margin });
|
|
13263
|
+
fmt = (x) => x.toFixed(3);
|
|
13264
|
+
}
|
|
13265
|
+
});
|
|
13266
|
+
|
|
13267
|
+
// src/cli/model-cli-shared.ts
|
|
13268
|
+
function gatherSamples(deps = {}) {
|
|
13269
|
+
const home = deps.home ?? import_node_os8.default.homedir();
|
|
13270
|
+
const cwd = deps.cwd ?? process.cwd();
|
|
13271
|
+
let files = readAllFeedback(home);
|
|
13272
|
+
const warnings = [];
|
|
13273
|
+
if (deps.projectOnly) {
|
|
13274
|
+
const unmarked = files.filter((f) => f.project === null).length;
|
|
13275
|
+
files = files.filter((f) => f.project?.cwd === cwd);
|
|
13276
|
+
if (unmarked > 0) {
|
|
13277
|
+
warnings.push(
|
|
13278
|
+
`${unmarked} feedback dir(s) have no project marker yet (written on the next reporter run) - --project cannot match them`
|
|
13279
|
+
);
|
|
13280
|
+
}
|
|
13281
|
+
}
|
|
13282
|
+
const { samples, invalidLabelCount, unlabeledCount } = labeledSamples(files);
|
|
13283
|
+
if (invalidLabelCount > 0) {
|
|
13284
|
+
warnings.push(
|
|
13285
|
+
`${invalidLabelCount} row(s) have unrecognized labels and were skipped (valid: ${CATEGORIES.join(", ")})`
|
|
13286
|
+
);
|
|
13287
|
+
}
|
|
13288
|
+
if (unlabeledCount > 0) {
|
|
13289
|
+
warnings.push(`${unlabeledCount} collected row(s) are still unlabeled - label them via label-feedback`);
|
|
13290
|
+
}
|
|
13291
|
+
const classes = new Set(samples.map((s) => s.label));
|
|
13292
|
+
let refusal = null;
|
|
13293
|
+
if (samples.length < LOCAL_TRAINING.minLabeled || classes.size < LOCAL_TRAINING.minClasses) {
|
|
13294
|
+
const tally = [...classes].sort().map((c) => `${c}: ${samples.filter((s) => s.label === c).length}`).join(", ") || "none";
|
|
13295
|
+
refusal = `not enough labeled feedback to train safely: ${samples.length} labeled row(s) across ${classes.size} class(es) (${tally}). Need >= ${LOCAL_TRAINING.minLabeled} rows across >= ${LOCAL_TRAINING.minClasses} classes - run your tests, then label collected failures via: npx @reportforge/playwright-pdf label-feedback`;
|
|
13296
|
+
}
|
|
13297
|
+
return { samples, files, warnings, refusal };
|
|
13298
|
+
}
|
|
13299
|
+
function formatEvalReport(r, baseVersion) {
|
|
13300
|
+
const lines = [];
|
|
13301
|
+
lines.push(`Evaluation: ${r.method} over ${r.n} labeled row(s), ${r.classes.length} class(es) [base model v${baseVersion}]`);
|
|
13302
|
+
lines.push("");
|
|
13303
|
+
lines.push(pad(["arm", "accuracy", "macro-F1"], [10, 10, 10]));
|
|
13304
|
+
lines.push(pad(["base", pct(r.base.accuracy), num(r.base.macroF1)], [10, 10, 10]));
|
|
13305
|
+
lines.push(pad(["chain", pct(r.chain.accuracy), num(r.chain.macroF1)], [10, 10, 10]));
|
|
13306
|
+
lines.push("");
|
|
13307
|
+
lines.push(pad(["class", "support", "base F1", "chain F1", "chain P", "chain R"], [20, 8, 9, 9, 8, 8]));
|
|
13308
|
+
for (const c of r.classes) {
|
|
13309
|
+
const b = r.base.perClass[c];
|
|
13310
|
+
const ch = r.chain.perClass[c];
|
|
13311
|
+
lines.push(pad([c, String(ch.support), num(b.f1), num(ch.f1), num(ch.precision), num(ch.recall)], [20, 8, 9, 9, 8, 8]));
|
|
13312
|
+
}
|
|
13313
|
+
lines.push("");
|
|
13314
|
+
lines.push(formatConfusion(r.chain, "chain confusion (true -> predicted)"));
|
|
13315
|
+
lines.push("");
|
|
13316
|
+
lines.push(
|
|
13317
|
+
`accept threshold: ${r.acceptThreshold === null ? "none reachable" : num(r.acceptThreshold)} strong threshold: ${r.strongThreshold === null ? "n/a" : num(r.strongThreshold)}`
|
|
13318
|
+
);
|
|
13319
|
+
for (const reason of r.reasons) lines.push(reason);
|
|
13320
|
+
return lines.join("\n");
|
|
13321
|
+
}
|
|
13322
|
+
function formatConfusion(m, title) {
|
|
13323
|
+
const predicted = [...new Set(Object.values(m.confusion).flatMap((row) => Object.keys(row)))].sort();
|
|
13324
|
+
const lines = [title, pad(["true \\ pred", ...predicted], [20, ...predicted.map(() => 12)])];
|
|
13325
|
+
for (const t of Object.keys(m.confusion).sort()) {
|
|
13326
|
+
lines.push(pad([t, ...predicted.map((p) => String(m.confusion[t][p] ?? 0))], [20, ...predicted.map(() => 12)]));
|
|
13327
|
+
}
|
|
13328
|
+
return lines.join("\n");
|
|
13329
|
+
}
|
|
13330
|
+
function pad(cells, widths) {
|
|
13331
|
+
return cells.map((c, i) => c.padEnd(widths[i])).join(" ").trimEnd();
|
|
13332
|
+
}
|
|
13333
|
+
function flagValue(argv, flag) {
|
|
13334
|
+
const hit = argv.find((a) => a.startsWith(`${flag}=`));
|
|
13335
|
+
return hit?.slice(flag.length + 1);
|
|
13336
|
+
}
|
|
13337
|
+
function hasFlag(argv, flag) {
|
|
13338
|
+
return argv.includes(flag);
|
|
13339
|
+
}
|
|
13340
|
+
var import_node_os8, pct, num;
|
|
13341
|
+
var init_model_cli_shared = __esm({
|
|
13342
|
+
"src/cli/model-cli-shared.ts"() {
|
|
13343
|
+
"use strict";
|
|
13344
|
+
init_cjs_shims();
|
|
13345
|
+
import_node_os8 = __toESM(require("os"));
|
|
13346
|
+
init_FeedbackStore();
|
|
13347
|
+
init_constants();
|
|
13348
|
+
pct = (x) => `${(x * 100).toFixed(1)}%`;
|
|
13349
|
+
num = (x) => x.toFixed(3);
|
|
13350
|
+
}
|
|
13351
|
+
});
|
|
13352
|
+
|
|
13353
|
+
// src/cli/train-model.ts
|
|
13354
|
+
var train_model_exports = {};
|
|
13355
|
+
__export(train_model_exports, {
|
|
13356
|
+
trainModel: () => trainModel,
|
|
13357
|
+
trainModelCli: () => trainModelCli
|
|
13358
|
+
});
|
|
13359
|
+
function trainModel(deps = {}) {
|
|
13360
|
+
const gathered = gatherSamples(deps);
|
|
13361
|
+
if (gathered.refusal) {
|
|
13362
|
+
return { outcome: "insufficient-data", warnings: gathered.warnings, detail: gathered.refusal };
|
|
13363
|
+
}
|
|
13364
|
+
const base = loadModel(true);
|
|
13365
|
+
const evalResult = evaluateChain(gathered.samples, base);
|
|
13366
|
+
const classes = new Set(gathered.samples.map((s) => s.label));
|
|
13367
|
+
const finalModel = trainNaiveBayes(gathered.samples);
|
|
13368
|
+
const transferred = transferThresholds(evalResult, gathered.samples, finalModel);
|
|
13369
|
+
let gatePassed = evalResult.gatePassed;
|
|
13370
|
+
if (gatePassed && transferred.acceptThreshold === null) {
|
|
13371
|
+
gatePassed = false;
|
|
13372
|
+
evalResult.reasons.push(
|
|
13373
|
+
"gate FAILED after threshold transfer: the OOF-accepted rows are not confidently classified by the full-data model"
|
|
13374
|
+
);
|
|
13375
|
+
}
|
|
13376
|
+
const file = {
|
|
13377
|
+
formatVersion: 1,
|
|
13378
|
+
model: finalModel,
|
|
13379
|
+
meta: {
|
|
13380
|
+
trainedAt: (/* @__PURE__ */ new Date()).toISOString(),
|
|
13381
|
+
labeledCount: gathered.samples.length,
|
|
13382
|
+
classCount: classes.size,
|
|
13383
|
+
baseVersionAtEval: base.version,
|
|
13384
|
+
gatePassed,
|
|
13385
|
+
acceptThreshold: transferred.acceptThreshold,
|
|
13386
|
+
strongThreshold: transferred.strongThreshold,
|
|
13387
|
+
metrics: { method: evalResult.method, base: evalResult.base, chain: evalResult.chain }
|
|
13388
|
+
}
|
|
13389
|
+
};
|
|
13390
|
+
const outFile = saveLocalModel(file, deps.outFile);
|
|
13391
|
+
evalResult.acceptThreshold = transferred.acceptThreshold ?? evalResult.acceptThreshold;
|
|
13392
|
+
evalResult.strongThreshold = transferred.strongThreshold;
|
|
13393
|
+
return {
|
|
13394
|
+
outcome: gatePassed ? "trained" : "gate-failed",
|
|
13395
|
+
evalResult,
|
|
13396
|
+
baseVersion: base.version,
|
|
13397
|
+
outFile,
|
|
13398
|
+
warnings: gathered.warnings,
|
|
13399
|
+
detail: gatePassed ? `local model ACTIVE - saved to ${outFile}` : `local model saved to ${outFile} but INACTIVE (gate failed) - the reporter stays on the base model`
|
|
13400
|
+
};
|
|
13401
|
+
}
|
|
13402
|
+
function trainModelCli(argv) {
|
|
13403
|
+
const out = (s) => process.stdout.write(`${s}
|
|
13404
|
+
`);
|
|
13405
|
+
const outFile = flagValue(argv, "--out");
|
|
13406
|
+
const projectOnly = hasFlag(argv, "--project");
|
|
13407
|
+
if (outFile !== void 0 && !projectOnly) {
|
|
13408
|
+
out(
|
|
13409
|
+
"[reportforge] note: --out without --project trains on feedback from ALL local projects. If this file will be committed to one repo, consider adding --project so other projects\u2019 token statistics do not ride along."
|
|
13410
|
+
);
|
|
13411
|
+
}
|
|
13412
|
+
const res = trainModel({ outFile, projectOnly });
|
|
13413
|
+
for (const w of res.warnings) out(`[reportforge] warn: ${w}`);
|
|
13414
|
+
if (res.outcome === "insufficient-data") {
|
|
13415
|
+
out(`[reportforge] ${res.detail}`);
|
|
13416
|
+
process.exitCode = 1;
|
|
13417
|
+
return;
|
|
13418
|
+
}
|
|
13419
|
+
out("");
|
|
13420
|
+
out(formatEvalReport(res.evalResult, res.baseVersion));
|
|
13421
|
+
out("");
|
|
13422
|
+
out(`[reportforge] ${res.detail}`);
|
|
13423
|
+
if (res.outcome === "trained" && res.outFile === defaultLocalModelPath()) {
|
|
13424
|
+
out("[reportforge] the reporter picks it up automatically on the next run (failureAnalysis.localModel).");
|
|
13425
|
+
}
|
|
13426
|
+
process.exitCode = res.outcome === "trained" ? 0 : 1;
|
|
13427
|
+
}
|
|
13428
|
+
var init_train_model = __esm({
|
|
13429
|
+
"src/cli/train-model.ts"() {
|
|
13430
|
+
"use strict";
|
|
13431
|
+
init_cjs_shims();
|
|
13432
|
+
init_LocalTrainer();
|
|
13433
|
+
init_LocalEvaluator();
|
|
13434
|
+
init_ModelStore();
|
|
13435
|
+
init_LocalModelStore();
|
|
13436
|
+
init_model_cli_shared();
|
|
13437
|
+
}
|
|
13438
|
+
});
|
|
13439
|
+
|
|
13440
|
+
// src/cli/evaluate-model.ts
|
|
13441
|
+
var evaluate_model_exports = {};
|
|
13442
|
+
__export(evaluate_model_exports, {
|
|
13443
|
+
evaluateModel: () => evaluateModel,
|
|
13444
|
+
evaluateModelCli: () => evaluateModelCli
|
|
13445
|
+
});
|
|
13446
|
+
function evaluateModel(deps = {}) {
|
|
13447
|
+
const lines = [];
|
|
13448
|
+
const base = loadModel(true);
|
|
13449
|
+
if (deps.modelPath !== void 0) {
|
|
13450
|
+
const stored = readLocalModelFile(deps.modelPath);
|
|
13451
|
+
if (!stored) {
|
|
13452
|
+
lines.push(`stored model at ${deps.modelPath}: unreadable or invalid`);
|
|
13453
|
+
} else {
|
|
13454
|
+
const m = stored.meta;
|
|
13455
|
+
lines.push(
|
|
13456
|
+
`stored model at ${deps.modelPath}: trained ${m.trainedAt}, ${m.labeledCount} rows / ${m.classCount} classes, gate ${m.gatePassed ? "PASSED" : "FAILED"}, recorded chain macro-F1 ${m.metrics.chain.macroF1.toFixed(3)} vs base ${m.metrics.base.macroF1.toFixed(3)}`
|
|
13457
|
+
);
|
|
13458
|
+
if (m.baseVersionAtEval !== base.version) {
|
|
13459
|
+
lines.push(
|
|
13460
|
+
`warn: that gate was checked against base v${m.baseVersionAtEval}; base v${base.version} is active now - re-run train-model to re-validate`
|
|
13461
|
+
);
|
|
13462
|
+
}
|
|
13463
|
+
}
|
|
13464
|
+
lines.push("");
|
|
13465
|
+
}
|
|
13466
|
+
const gathered = gatherSamples(deps);
|
|
13467
|
+
for (const w of gathered.warnings) lines.push(`warn: ${w}`);
|
|
13468
|
+
if (gathered.refusal) {
|
|
13469
|
+
lines.push(gathered.refusal);
|
|
13470
|
+
return { ok: false, lines };
|
|
13471
|
+
}
|
|
13472
|
+
lines.push(formatEvalReport(evaluateChain(gathered.samples, base), base.version));
|
|
13473
|
+
lines.push("(nothing was written - run train-model to produce/update the artifact)");
|
|
13474
|
+
return { ok: true, lines };
|
|
13475
|
+
}
|
|
13476
|
+
function evaluateModelCli(argv) {
|
|
13477
|
+
const res = evaluateModel({
|
|
13478
|
+
modelPath: flagValue(argv, "--model"),
|
|
13479
|
+
projectOnly: hasFlag(argv, "--project")
|
|
13480
|
+
});
|
|
13481
|
+
process.stdout.write(res.lines.map((l) => `[reportforge] ${l}`).join("\n") + "\n");
|
|
13482
|
+
process.exitCode = res.ok ? 0 : 1;
|
|
13483
|
+
}
|
|
13484
|
+
var init_evaluate_model = __esm({
|
|
13485
|
+
"src/cli/evaluate-model.ts"() {
|
|
13486
|
+
"use strict";
|
|
13487
|
+
init_cjs_shims();
|
|
13488
|
+
init_LocalEvaluator();
|
|
13489
|
+
init_ModelStore();
|
|
13490
|
+
init_LocalModelStore();
|
|
13491
|
+
init_model_cli_shared();
|
|
12690
13492
|
}
|
|
12691
13493
|
});
|
|
12692
13494
|
|
|
@@ -12704,6 +13506,12 @@ Commands:
|
|
|
12704
13506
|
--template=minimal|detailed|executive (default: detailed)
|
|
12705
13507
|
--output=<path> (default: playwright-report/demo-report.pdf)
|
|
12706
13508
|
export-feedback Export locally-collected unrecognized failures (tokenized, local-only).
|
|
13509
|
+
label-feedback Interactively label locally-collected failures for on-device training (local-only).
|
|
13510
|
+
train-model Train + evaluate a personal classifier layer from your labeled feedback (local-only).
|
|
13511
|
+
--out=<path> write the model file elsewhere (default: ~/.reportforge/model-local.json)
|
|
13512
|
+
--project train only on feedback from the current directory's project
|
|
13513
|
+
evaluate-model Compare base vs. base+local on your labeled feedback without writing anything.
|
|
13514
|
+
--model=<path> also print a stored model file's metadata and metrics
|
|
12707
13515
|
|
|
12708
13516
|
Run "npx @reportforge/playwright-pdf demo" to see the report ReportForge produces.`;
|
|
12709
13517
|
function pickCommand(arg) {
|
|
@@ -12712,6 +13520,12 @@ function pickCommand(arg) {
|
|
|
12712
13520
|
return "demo";
|
|
12713
13521
|
case "export-feedback":
|
|
12714
13522
|
return "export-feedback";
|
|
13523
|
+
case "label-feedback":
|
|
13524
|
+
return "label-feedback";
|
|
13525
|
+
case "train-model":
|
|
13526
|
+
return "train-model";
|
|
13527
|
+
case "evaluate-model":
|
|
13528
|
+
return "evaluate-model";
|
|
12715
13529
|
default:
|
|
12716
13530
|
return "help";
|
|
12717
13531
|
}
|
|
@@ -12745,6 +13559,21 @@ async function main() {
|
|
|
12745
13559
|
exportFeedbackCli2();
|
|
12746
13560
|
return;
|
|
12747
13561
|
}
|
|
13562
|
+
case "label-feedback": {
|
|
13563
|
+
const { labelFeedbackCli: labelFeedbackCli2 } = await Promise.resolve().then(() => (init_label_feedback(), label_feedback_exports));
|
|
13564
|
+
await labelFeedbackCli2();
|
|
13565
|
+
return;
|
|
13566
|
+
}
|
|
13567
|
+
case "train-model": {
|
|
13568
|
+
const { trainModelCli: trainModelCli2 } = await Promise.resolve().then(() => (init_train_model(), train_model_exports));
|
|
13569
|
+
trainModelCli2(process.argv.slice(3));
|
|
13570
|
+
return;
|
|
13571
|
+
}
|
|
13572
|
+
case "evaluate-model": {
|
|
13573
|
+
const { evaluateModelCli: evaluateModelCli2 } = await Promise.resolve().then(() => (init_evaluate_model(), evaluate_model_exports));
|
|
13574
|
+
evaluateModelCli2(process.argv.slice(3));
|
|
13575
|
+
return;
|
|
13576
|
+
}
|
|
12748
13577
|
case "help": {
|
|
12749
13578
|
const { text, stream, code } = helpOutput(arg);
|
|
12750
13579
|
(stream === "stdout" ? process.stdout : process.stderr).write(text);
|