@bongos/core 1.19.28 → 1.19.29
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/.bongos-core.json +10 -10
- package/modules/grading/grader.js +50 -9
- package/package-lock.json +2 -2
- package/package.json +1 -1
- package/src/module-api.js +3 -1
- package/tests/grader.mjs +159 -0
package/.bongos-core.json
CHANGED
|
@@ -2,11 +2,11 @@
|
|
|
2
2
|
"artifact": "bongos-core",
|
|
3
3
|
"manifest_schema": 1,
|
|
4
4
|
"generator": "scripts/gds/package-core.js",
|
|
5
|
-
"core_version": "1.19.
|
|
6
|
-
"core_contract": "1.19.
|
|
7
|
-
"source_commit": "
|
|
5
|
+
"core_version": "1.19.29",
|
|
6
|
+
"core_contract": "1.19.29",
|
|
7
|
+
"source_commit": "653afa8efb0b5167d51ed1510c45977a56a575dd",
|
|
8
8
|
"source_ref": "HEAD",
|
|
9
|
-
"built_at": "2026-08-11T14:
|
|
9
|
+
"built_at": "2026-08-11T14:19:03.503Z",
|
|
10
10
|
"redaction": {
|
|
11
11
|
"model": "docs-redacted+functional-verbatim",
|
|
12
12
|
"docs_redacted": 285,
|
|
@@ -17,7 +17,7 @@
|
|
|
17
17
|
"gate": "passed"
|
|
18
18
|
},
|
|
19
19
|
"file_count": 1511,
|
|
20
|
-
"tree_sha256": "
|
|
20
|
+
"tree_sha256": "0076ae91d3bb7f715ca250d05367d1eae9a660b5c015b88c0a6687d7a01a9794",
|
|
21
21
|
"files": [
|
|
22
22
|
{
|
|
23
23
|
"path": ".claude/skills/blocker-review/SKILL.md",
|
|
@@ -2882,7 +2882,7 @@
|
|
|
2882
2882
|
{
|
|
2883
2883
|
"path": "modules/grading/grader.js",
|
|
2884
2884
|
"mode": "0000644",
|
|
2885
|
-
"sha256": "
|
|
2885
|
+
"sha256": "3461862dc1af26875958697291128fbf4d914fff9b00b7920e5f25dae73f6ba4"
|
|
2886
2886
|
},
|
|
2887
2887
|
{
|
|
2888
2888
|
"path": "modules/grading/module.json",
|
|
@@ -4002,12 +4002,12 @@
|
|
|
4002
4002
|
{
|
|
4003
4003
|
"path": "package-lock.json",
|
|
4004
4004
|
"mode": "0000644",
|
|
4005
|
-
"sha256": "
|
|
4005
|
+
"sha256": "7a14171b353dbfb6da83df462c031ecce9d4c97b085504845035072e5e97bbfa"
|
|
4006
4006
|
},
|
|
4007
4007
|
{
|
|
4008
4008
|
"path": "package.json",
|
|
4009
4009
|
"mode": "0000644",
|
|
4010
|
-
"sha256": "
|
|
4010
|
+
"sha256": "3ff6057a4a6120421ce79d9593bdf54e424fdd143bd0a20fc616ba92582ee910"
|
|
4011
4011
|
},
|
|
4012
4012
|
{
|
|
4013
4013
|
"path": "public-docs/index.html",
|
|
@@ -5627,7 +5627,7 @@
|
|
|
5627
5627
|
{
|
|
5628
5628
|
"path": "src/module-api.js",
|
|
5629
5629
|
"mode": "0000644",
|
|
5630
|
-
"sha256": "
|
|
5630
|
+
"sha256": "576d6afdcdf097366f36831d4b56ba300f71beefdc29c853605f9c0788cd260c"
|
|
5631
5631
|
},
|
|
5632
5632
|
{
|
|
5633
5633
|
"path": "src/module-loader/loader.js",
|
|
@@ -6597,7 +6597,7 @@
|
|
|
6597
6597
|
{
|
|
6598
6598
|
"path": "tests/grader.mjs",
|
|
6599
6599
|
"mode": "0000644",
|
|
6600
|
-
"sha256": "
|
|
6600
|
+
"sha256": "4832bcffc7385cdd8deb0c1ff9bb44542190a3da106d2f00ccacd823c90103ec"
|
|
6601
6601
|
},
|
|
6602
6602
|
{
|
|
6603
6603
|
"path": "tests/grader_bypass.mjs",
|
|
@@ -365,6 +365,19 @@ function isNum(n) {
|
|
|
365
365
|
return Number.isFinite(n);
|
|
366
366
|
}
|
|
367
367
|
|
|
368
|
+
// #1002653 corroboration helpers for validateAndScore's null-ff lens branch.
|
|
369
|
+
// A worker entry is untrusted input — w.issues may be any shape. Absent issues
|
|
370
|
+
// on an errored worker is the normal outage shape (treated as none); a truthy
|
|
371
|
+
// NON-ARRAY issues is malformed and must DISQUALIFY corroboration (fail-closed
|
|
372
|
+
// schema_error downstream), never TypeError into a 500 on grade submission.
|
|
373
|
+
const isNarcErroredClean = (w) => {
|
|
374
|
+
if (!w || w.kind !== 'narc' || !w.error) return false;
|
|
375
|
+
const issues = w.issues == null ? [] : w.issues;
|
|
376
|
+
if (!Array.isArray(issues)) return false;
|
|
377
|
+
return !issues.some((i) => i && (i.severity === 'blocker' || i.severity === 'major'));
|
|
378
|
+
};
|
|
379
|
+
const isNarcHealthy = (w) => !!w && w.kind === 'narc' && !w.error;
|
|
380
|
+
|
|
368
381
|
// Validates ONE shot's subagent JSON and produces a per-shot grade-result.
|
|
369
382
|
// Never throws — malformed inputs produce a passed=false result with notes_md.
|
|
370
383
|
// The downstream aggregateShots collapses N of these into a final verdict.
|
|
@@ -394,7 +407,25 @@ function validateAndScore(rawSubagent, meta = {}) {
|
|
|
394
407
|
};
|
|
395
408
|
}
|
|
396
409
|
|
|
397
|
-
|
|
410
|
+
// #1002653: Number(null) === 0, so an errored ADVISORY Narc forwarding an
|
|
411
|
+
// EXPLICIT ff:null fabricated "FAIL — functional_fidelity below 6" at
|
|
412
|
+
// (0+cq)/2. Explicit null = the lens did not run (the #395 confidence
|
|
413
|
+
// precedent): gate/score on the axes that ran. A bare null is NOT enough —
|
|
414
|
+
// a prompt-injected Narc emitting null must stay fail-closed — so the branch
|
|
415
|
+
// needs POSITIVE corroboration: workers[] carries an errored
|
|
416
|
+
// no-substantive-finding Narc (#473 missing-signal semantics) and no healthy
|
|
417
|
+
// Narc. Empty/narc-less/absent workers, ABSENT ff, and every other shape
|
|
418
|
+
// fall through to the fail-closed schema_error below. Quality has no such
|
|
419
|
+
// branch — gating lens, absence stays fail-closed.
|
|
420
|
+
// Trust note: workers[] is client-supplied like every SR-9 grade input — a
|
|
421
|
+
// forger gains nothing here they cannot get by sending ff:9 directly; the
|
|
422
|
+
// server-derivable-signals redesign is tracked separately (SR-9 residual).
|
|
423
|
+
const metaWorkers = Array.isArray(meta.workers) ? meta.workers : null;
|
|
424
|
+
const narcMissingSignal = !!metaWorkers &&
|
|
425
|
+
metaWorkers.some(isNarcErroredClean) && !metaWorkers.some(isNarcHealthy);
|
|
426
|
+
const ffExplicitNull = parsed.functional_fidelity === null;
|
|
427
|
+
const ffLensUnavailable = ffExplicitNull && narcMissingSignal;
|
|
428
|
+
const ff = ffExplicitNull ? null : Number(parsed.functional_fidelity);
|
|
398
429
|
const cq = Number(parsed.code_quality);
|
|
399
430
|
const economyRaw = Number(parsed.economy);
|
|
400
431
|
const economy =
|
|
@@ -427,8 +458,9 @@ function validateAndScore(rawSubagent, meta = {}) {
|
|
|
427
458
|
|
|
428
459
|
signals.confidence = confidence;
|
|
429
460
|
signals.issue_count = issues.length;
|
|
461
|
+
if (ffLensUnavailable) signals.ff_lens_unavailable = true;
|
|
430
462
|
|
|
431
|
-
if (!
|
|
463
|
+
if ((!ffLensUnavailable && (!isNum(ff) || ff < 0 || ff > 10)) || !isNum(cq) || cq < 0 || cq > 10) {
|
|
432
464
|
return {
|
|
433
465
|
score: 0,
|
|
434
466
|
passed: false,
|
|
@@ -437,12 +469,15 @@ function validateAndScore(rawSubagent, meta = {}) {
|
|
|
437
469
|
grader_version: VERSION,
|
|
438
470
|
rubric_scores: {},
|
|
439
471
|
signals: { ...signals, schema_error: true },
|
|
440
|
-
notes_md: `Subagent grader ${VERSION}: subagent returned scores outside [0,10] or non-numeric. Got functional_fidelity=${parsed.functional_fidelity}, code_quality=${parsed.code_quality}
|
|
472
|
+
notes_md: `Subagent grader ${VERSION}: subagent returned scores outside [0,10] or non-numeric. Got functional_fidelity=${parsed.functional_fidelity}, code_quality=${parsed.code_quality}.`
|
|
473
|
+
+ (ffExplicitNull && !narcMissingSignal
|
|
474
|
+
? ' Explicit null functional_fidelity without a corroborated Narc outage in signals.workers is fail-closed.'
|
|
475
|
+
: ''),
|
|
441
476
|
issues,
|
|
442
477
|
};
|
|
443
478
|
}
|
|
444
479
|
|
|
445
|
-
const ffR = r1(clamp(ff, 0, 10));
|
|
480
|
+
const ffR = ffLensUnavailable ? null : r1(clamp(ff, 0, 10));
|
|
446
481
|
const cqR = r1(clamp(cq, 0, 10));
|
|
447
482
|
const economyR = economy != null ? r1(clamp(economy, 0, 10)) : null;
|
|
448
483
|
const securityR = securityScore != null ? r1(clamp(securityScore, 0, 10)) : null;
|
|
@@ -450,7 +485,9 @@ function validateAndScore(rawSubagent, meta = {}) {
|
|
|
450
485
|
const economyPenalty =
|
|
451
486
|
economyR != null ? Math.max(0, (THRESHOLD - economyR) / 2) : 0;
|
|
452
487
|
const cqEffective = economyR != null ? r1(clamp(cqR - economyPenalty, 0, 10)) : cqR;
|
|
453
|
-
|
|
488
|
+
// With the ff lens out, the score reflects only the axis that ran — never a
|
|
489
|
+
// blend with a synthetic 0 (the fabricated 4.5/10) or a synthetic threshold.
|
|
490
|
+
const blended = ffLensUnavailable ? cqR : r1((ffR + cqR) / 2);
|
|
454
491
|
|
|
455
492
|
// confidence must be present (not null) for a PASS. A subagent that omits
|
|
456
493
|
// confidence entirely silently bypassed the low-confidence gate under the
|
|
@@ -461,7 +498,11 @@ function validateAndScore(rawSubagent, meta = {}) {
|
|
|
461
498
|
// so aggregateShots can never reach unanimousPASS with a null confidenceMedian.
|
|
462
499
|
const confidenceAbsent = confidence === null;
|
|
463
500
|
const lowConfidence = !confidenceAbsent && confidence < LOW_CONFIDENCE;
|
|
464
|
-
|
|
501
|
+
// ffR is null when the lens is unavailable — it must not fail the gate
|
|
502
|
+
// (null < THRESHOLD is true via JS coercion), and must not appear in the
|
|
503
|
+
// verdict's below-threshold branches.
|
|
504
|
+
const ffBelow = !ffLensUnavailable && ffR < THRESHOLD;
|
|
505
|
+
const bothPass = !ffBelow && cqR >= THRESHOLD;
|
|
465
506
|
const passed = bothPass && !lowConfidence && !confidenceAbsent;
|
|
466
507
|
|
|
467
508
|
const verdict = passed
|
|
@@ -472,15 +513,15 @@ function validateAndScore(rawSubagent, meta = {}) {
|
|
|
472
513
|
? 'FAIL — low confidence'
|
|
473
514
|
: bothPass
|
|
474
515
|
? 'FAIL'
|
|
475
|
-
:
|
|
516
|
+
: ffBelow && cqR < THRESHOLD
|
|
476
517
|
? `FAIL — both axes below ${THRESHOLD}`
|
|
477
|
-
:
|
|
518
|
+
: ffBelow
|
|
478
519
|
? `FAIL — functional_fidelity below ${THRESHOLD}`
|
|
479
520
|
: `FAIL — code_quality below ${THRESHOLD}`;
|
|
480
521
|
|
|
481
522
|
const notesLines = [];
|
|
482
523
|
notesLines.push(`Shot ${signals.shot_index != null ? signals.shot_index : '?'} (${signals.model}) — ${verdict}`);
|
|
483
|
-
notesLines.push(` ff=${ffR}/10 cq=${cqR}/10${economyR != null ? ` econ=${economyR}/10 (advisory)` : ''} conf=${confidence ?? '?'}`);
|
|
524
|
+
notesLines.push(` ff=${ffLensUnavailable ? 'n/a (lens unavailable)' : `${ffR}/10`} cq=${cqR}/10${economyR != null ? ` econ=${economyR}/10 (advisory)` : ''} conf=${confidence ?? '?'}`);
|
|
484
525
|
if (notesFromSubagent) {
|
|
485
526
|
notesLines.push('');
|
|
486
527
|
notesLines.push(notesFromSubagent.trim());
|
package/package-lock.json
CHANGED
|
@@ -1,12 +1,12 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@bongos/core",
|
|
3
|
-
"version": "1.19.
|
|
3
|
+
"version": "1.19.29",
|
|
4
4
|
"lockfileVersion": 3,
|
|
5
5
|
"requires": true,
|
|
6
6
|
"packages": {
|
|
7
7
|
"": {
|
|
8
8
|
"name": "@bongos/core",
|
|
9
|
-
"version": "1.19.
|
|
9
|
+
"version": "1.19.29",
|
|
10
10
|
"license": "AGPL-3.0-or-later",
|
|
11
11
|
"dependencies": {
|
|
12
12
|
"express": "^4.21.2",
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@bongos/core",
|
|
3
|
-
"version": "1.19.
|
|
3
|
+
"version": "1.19.29",
|
|
4
4
|
"description": "Cloud Bongos — the AI-first build platform core (GDS + platform surfaces + module system), installed as a versioned dependency (ADR 0108).",
|
|
5
5
|
"license": "AGPL-3.0-or-later",
|
|
6
6
|
"main": "src/platform-server.js",
|
package/src/module-api.js
CHANGED
|
@@ -483,8 +483,10 @@ const { validateOrRespond, LIMITS, parseId, asyncHandler, corsPublicGet, parsePa
|
|
|
483
483
|
// landed since 1.19.26 with no explicit bump. run 31497212656. (task 1002620)
|
|
484
484
|
// 1.19.28 — CI auto-patch (publish-on-merge, ADR 0161): carrier for merges
|
|
485
485
|
// landed since 1.19.27 with no explicit bump. run 31499228797. (task 1002620)
|
|
486
|
+
// 1.19.29 — CI auto-patch (publish-on-merge, ADR 0161): carrier for merges
|
|
487
|
+
// landed since 1.19.28 with no explicit bump. run 31500810303. (task 1002620)
|
|
486
488
|
// ---------------------------------------------------------------------------
|
|
487
|
-
const CORE_VERSION = '1.19.
|
|
489
|
+
const CORE_VERSION = '1.19.29'; // CI auto-patch carrier (ADR 0161); see changelog above
|
|
488
490
|
|
|
489
491
|
// A namespaced logger so a module's log lines are attributable + consistent.
|
|
490
492
|
// Usage: const log = api.logger('dev-box'); log.info('mounted');
|
package/tests/grader.mjs
CHANGED
|
@@ -415,6 +415,165 @@ test('#489 validateAndScore: explicit null confidence → FAIL (same as absent)'
|
|
|
415
415
|
assert.match(r.notes_md, /confidence absent/i);
|
|
416
416
|
});
|
|
417
417
|
|
|
418
|
+
// Task 1002653: an errored ADVISORY Narc forwards an EXPLICIT
|
|
419
|
+
// functional_fidelity: null on an otherwise-passing panel (advisory since
|
|
420
|
+
// #481). Number(null) === 0 used to fabricate "FAIL — functional_fidelity
|
|
421
|
+
// below 6" at (0+cq)/2. Explicit null = the ff lens did not run: gate + score
|
|
422
|
+
// on the axes that DID run — but ONLY when signals.workers corroborates the
|
|
423
|
+
// Narc as missing-signal (errored, no substantive finding). A bare null with a
|
|
424
|
+
// healthy Narc (the prompt-injection shape this ship's own Hacker flagged), an
|
|
425
|
+
// ABSENT ff, or no workers signal at all stays fail-closed.
|
|
426
|
+
const NARC_ERRORED_META = {
|
|
427
|
+
workers: [
|
|
428
|
+
{ kind: 'narc', verdict: 'fail', error: 'subprocess_error', issues: [] },
|
|
429
|
+
{ kind: 'quality', verdict: 'pass', scores: { code_quality: 8 }, issues: [] },
|
|
430
|
+
],
|
|
431
|
+
};
|
|
432
|
+
|
|
433
|
+
test('#1002653 validateAndScore: null ff + corroborated Narc outage + passing cq → PASS on the axes that ran', () => {
|
|
434
|
+
const r = grader.validateAndScore({
|
|
435
|
+
functional_fidelity: null,
|
|
436
|
+
code_quality: 8,
|
|
437
|
+
notes_md: 'narc errored; quality passed',
|
|
438
|
+
confidence: 0.8,
|
|
439
|
+
issues: [],
|
|
440
|
+
}, NARC_ERRORED_META);
|
|
441
|
+
assert.equal(r.passed, true);
|
|
442
|
+
assert.equal(r.score, 8); // cq only — never blended with a synthetic 0
|
|
443
|
+
assert.equal(r.rubric_scores.functional_fidelity, null);
|
|
444
|
+
assert.equal(r.signals.ff_lens_unavailable, true);
|
|
445
|
+
assert.match(r.notes_md, /lens unavailable/);
|
|
446
|
+
assert.doesNotMatch(r.notes_md, /functional_fidelity below/);
|
|
447
|
+
});
|
|
448
|
+
|
|
449
|
+
test('#1002653 validateAndScore: null ff with a HEALTHY Narc in signals → fail-closed (injection shape)', () => {
|
|
450
|
+
const r = grader.validateAndScore({
|
|
451
|
+
functional_fidelity: null,
|
|
452
|
+
code_quality: 9,
|
|
453
|
+
notes_md: '',
|
|
454
|
+
confidence: 0.9,
|
|
455
|
+
issues: [],
|
|
456
|
+
}, { workers: [
|
|
457
|
+
{ kind: 'narc', verdict: 'pass', scores: { functional_fidelity: null }, issues: [] },
|
|
458
|
+
{ kind: 'quality', verdict: 'pass', scores: { code_quality: 9 }, issues: [] },
|
|
459
|
+
] });
|
|
460
|
+
assert.equal(r.passed, false);
|
|
461
|
+
assert.equal(r.signals.schema_error, true);
|
|
462
|
+
assert.match(r.notes_md, /without a corroborated Narc outage/);
|
|
463
|
+
});
|
|
464
|
+
|
|
465
|
+
test('#1002653 validateAndScore: null ff with NO workers signal at all → fail-closed', () => {
|
|
466
|
+
const r = grader.validateAndScore({
|
|
467
|
+
functional_fidelity: null,
|
|
468
|
+
code_quality: 8,
|
|
469
|
+
notes_md: '',
|
|
470
|
+
confidence: 0.8,
|
|
471
|
+
issues: [],
|
|
472
|
+
});
|
|
473
|
+
assert.equal(r.passed, false);
|
|
474
|
+
assert.equal(r.signals.schema_error, true);
|
|
475
|
+
});
|
|
476
|
+
|
|
477
|
+
test('#1002653 validateAndScore: null ff with EMPTY workers array → fail-closed (no vacuous truth)', () => {
|
|
478
|
+
const r = grader.validateAndScore({
|
|
479
|
+
functional_fidelity: null,
|
|
480
|
+
code_quality: 8,
|
|
481
|
+
notes_md: '',
|
|
482
|
+
confidence: 0.8,
|
|
483
|
+
issues: [],
|
|
484
|
+
}, { workers: [] });
|
|
485
|
+
assert.equal(r.passed, false);
|
|
486
|
+
assert.equal(r.signals.schema_error, true);
|
|
487
|
+
});
|
|
488
|
+
|
|
489
|
+
test('#1002653 validateAndScore: null ff with a narc-less workers array → fail-closed (positive corroboration only)', () => {
|
|
490
|
+
const r = grader.validateAndScore({
|
|
491
|
+
functional_fidelity: null,
|
|
492
|
+
code_quality: 8,
|
|
493
|
+
notes_md: '',
|
|
494
|
+
confidence: 0.8,
|
|
495
|
+
issues: [],
|
|
496
|
+
}, { workers: [{ kind: 'quality', verdict: 'pass', scores: { code_quality: 8 }, issues: [] }] });
|
|
497
|
+
assert.equal(r.passed, false);
|
|
498
|
+
assert.equal(r.signals.schema_error, true);
|
|
499
|
+
});
|
|
500
|
+
|
|
501
|
+
test('#1002653 validateAndScore: malformed issues shape on the errored narc → fail-closed, no throw', () => {
|
|
502
|
+
const r = grader.validateAndScore({
|
|
503
|
+
functional_fidelity: null,
|
|
504
|
+
code_quality: 8,
|
|
505
|
+
notes_md: '',
|
|
506
|
+
confidence: 0.8,
|
|
507
|
+
issues: [],
|
|
508
|
+
}, { workers: [{ kind: 'narc', verdict: 'fail', error: 'subprocess_error', issues: 'none' }] });
|
|
509
|
+
assert.equal(r.passed, false);
|
|
510
|
+
assert.equal(r.signals.schema_error, true);
|
|
511
|
+
});
|
|
512
|
+
|
|
513
|
+
test('#1002653 validateAndScore: an errored narc alongside a HEALTHY narc does not excuse null ff', () => {
|
|
514
|
+
const r = grader.validateAndScore({
|
|
515
|
+
functional_fidelity: null,
|
|
516
|
+
code_quality: 8,
|
|
517
|
+
notes_md: '',
|
|
518
|
+
confidence: 0.8,
|
|
519
|
+
issues: [],
|
|
520
|
+
}, { workers: [
|
|
521
|
+
{ kind: 'narc', verdict: 'fail', error: 'subprocess_error', issues: [] },
|
|
522
|
+
{ kind: 'narc', verdict: 'pass', scores: { functional_fidelity: 8 }, issues: [] },
|
|
523
|
+
] });
|
|
524
|
+
assert.equal(r.passed, false);
|
|
525
|
+
assert.equal(r.signals.schema_error, true);
|
|
526
|
+
});
|
|
527
|
+
|
|
528
|
+
test('#1002653 validateAndScore: null ff does NOT rescue a failing cq (verdict names cq alone)', () => {
|
|
529
|
+
const r = grader.validateAndScore({
|
|
530
|
+
functional_fidelity: null,
|
|
531
|
+
code_quality: 4,
|
|
532
|
+
notes_md: '',
|
|
533
|
+
confidence: 0.9,
|
|
534
|
+
issues: [{ severity: 'major', file: 'x.js', summary: 'real problem' }],
|
|
535
|
+
}, NARC_ERRORED_META);
|
|
536
|
+
assert.equal(r.passed, false);
|
|
537
|
+
assert.match(r.notes_md, /code_quality below/);
|
|
538
|
+
assert.doesNotMatch(r.notes_md, /both axes below/);
|
|
539
|
+
});
|
|
540
|
+
|
|
541
|
+
test('#1002653 validateAndScore: ABSENT ff stays a schema_error FAIL (omission cannot dodge the gate)', () => {
|
|
542
|
+
const r = grader.validateAndScore({
|
|
543
|
+
code_quality: 9,
|
|
544
|
+
notes_md: '',
|
|
545
|
+
confidence: 0.9,
|
|
546
|
+
issues: [],
|
|
547
|
+
}, NARC_ERRORED_META);
|
|
548
|
+
assert.equal(r.passed, false);
|
|
549
|
+
assert.equal(r.signals.schema_error, true);
|
|
550
|
+
});
|
|
551
|
+
|
|
552
|
+
test('#1002653 validateAndScore: a judged ff of 0 is still a real FAIL (only corroborated null is a lens outage)', () => {
|
|
553
|
+
const r = grader.validateAndScore({
|
|
554
|
+
functional_fidelity: 0,
|
|
555
|
+
code_quality: 9,
|
|
556
|
+
notes_md: '',
|
|
557
|
+
confidence: 0.9,
|
|
558
|
+
issues: [],
|
|
559
|
+
}, NARC_ERRORED_META);
|
|
560
|
+
assert.equal(r.passed, false);
|
|
561
|
+
assert.match(r.notes_md, /functional_fidelity below/);
|
|
562
|
+
assert.notEqual(r.signals.ff_lens_unavailable, true);
|
|
563
|
+
});
|
|
564
|
+
|
|
565
|
+
test('#1002653 validateAndScore: null ff + null confidence still FAILs on confidence', () => {
|
|
566
|
+
const r = grader.validateAndScore({
|
|
567
|
+
functional_fidelity: null,
|
|
568
|
+
code_quality: 9,
|
|
569
|
+
notes_md: '',
|
|
570
|
+
confidence: null,
|
|
571
|
+
issues: [],
|
|
572
|
+
}, NARC_ERRORED_META);
|
|
573
|
+
assert.equal(r.passed, false);
|
|
574
|
+
assert.match(r.notes_md, /confidence absent/i);
|
|
575
|
+
});
|
|
576
|
+
|
|
418
577
|
// #489 aggregateShots safety: a unanimousPASS result can never produce a null
|
|
419
578
|
// confidenceMedian, because any shot that passes must now have non-null confidence.
|
|
420
579
|
test('#489 aggregateShots unanimousPASS always has non-null median confidence', () => {
|