@bongos/core 1.19.30 → 1.19.31
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 +23 -18
- package/modules/grading/grader-rubric.json +4 -3
- package/modules/grading/grader-workers/efficiency.js +3 -3
- package/modules/grading/grader-workers/hacker.js +5 -0
- package/modules/grading/grader-workers/narc.js +5 -3
- package/modules/grading/grader-workers/quality.js +3 -3
- package/modules/grading/grader-workers/worker-prompt.js +14 -0
- package/modules/grading/grader-workers/worker-verdict.js +30 -1
- package/package-lock.json +2 -2
- package/package.json +1 -1
- package/src/module-api.js +3 -1
- package/tests/grader.mjs +12 -7
- package/tests/grader_severity_gate.mjs +156 -0
package/.bongos-core.json
CHANGED
|
@@ -2,22 +2,22 @@
|
|
|
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.31",
|
|
6
|
+
"core_contract": "1.19.31",
|
|
7
|
+
"source_commit": "2e9b2abd8b182334b2eeb52e9815f499f340a3e3",
|
|
8
8
|
"source_ref": "HEAD",
|
|
9
|
-
"built_at": "2026-08-11T14:
|
|
9
|
+
"built_at": "2026-08-11T14:47:58.633Z",
|
|
10
10
|
"redaction": {
|
|
11
11
|
"model": "docs-redacted+functional-verbatim",
|
|
12
12
|
"docs_redacted": 286,
|
|
13
13
|
"agent_docs_stubbed": 21,
|
|
14
|
-
"functional_verbatim":
|
|
14
|
+
"functional_verbatim": 1207,
|
|
15
15
|
"rules": 3,
|
|
16
16
|
"gate_literals": 3,
|
|
17
17
|
"gate": "passed"
|
|
18
18
|
},
|
|
19
|
-
"file_count":
|
|
20
|
-
"tree_sha256": "
|
|
19
|
+
"file_count": 1514,
|
|
20
|
+
"tree_sha256": "46bd790b34d0d3e1c2884f43eecf20fa747ddd57fb76ad0331f24dfb362fc6a2",
|
|
21
21
|
"files": [
|
|
22
22
|
{
|
|
23
23
|
"path": ".claude/skills/blocker-review/SKILL.md",
|
|
@@ -2837,12 +2837,12 @@
|
|
|
2837
2837
|
{
|
|
2838
2838
|
"path": "modules/grading/grader-rubric.json",
|
|
2839
2839
|
"mode": "0000644",
|
|
2840
|
-
"sha256": "
|
|
2840
|
+
"sha256": "6917875f2be5c39248b44caf7bf8d50f68cbe8effbc2bfdef3ba11e5ac17fc6c"
|
|
2841
2841
|
},
|
|
2842
2842
|
{
|
|
2843
2843
|
"path": "modules/grading/grader-workers/efficiency.js",
|
|
2844
2844
|
"mode": "0000644",
|
|
2845
|
-
"sha256": "
|
|
2845
|
+
"sha256": "9b8bbb51c5d8641d1c145d90ad8d0d96d19466abd8777bd10d5819630a6b7066"
|
|
2846
2846
|
},
|
|
2847
2847
|
{
|
|
2848
2848
|
"path": "modules/grading/grader-workers/extract-json.js",
|
|
@@ -2857,7 +2857,7 @@
|
|
|
2857
2857
|
{
|
|
2858
2858
|
"path": "modules/grading/grader-workers/hacker.js",
|
|
2859
2859
|
"mode": "0000644",
|
|
2860
|
-
"sha256": "
|
|
2860
|
+
"sha256": "99f64187efc56c2edf5a41626f87ee4083e83da43f1a00628d3506929d42e3c2"
|
|
2861
2861
|
},
|
|
2862
2862
|
{
|
|
2863
2863
|
"path": "modules/grading/grader-workers/index.js",
|
|
@@ -2867,12 +2867,12 @@
|
|
|
2867
2867
|
{
|
|
2868
2868
|
"path": "modules/grading/grader-workers/narc.js",
|
|
2869
2869
|
"mode": "0000644",
|
|
2870
|
-
"sha256": "
|
|
2870
|
+
"sha256": "4e881e6d04bafcf6093be0e1c653979d00c174b6f7966fb9cd3d833a994f48d3"
|
|
2871
2871
|
},
|
|
2872
2872
|
{
|
|
2873
2873
|
"path": "modules/grading/grader-workers/quality.js",
|
|
2874
2874
|
"mode": "0000644",
|
|
2875
|
-
"sha256": "
|
|
2875
|
+
"sha256": "de6003eb2af2abca2e6d68ebab729c8a3e18f0b8514d9f14e65b9442c21df253"
|
|
2876
2876
|
},
|
|
2877
2877
|
{
|
|
2878
2878
|
"path": "modules/grading/grader-workers/rubric.js",
|
|
@@ -2882,12 +2882,12 @@
|
|
|
2882
2882
|
{
|
|
2883
2883
|
"path": "modules/grading/grader-workers/worker-prompt.js",
|
|
2884
2884
|
"mode": "0000644",
|
|
2885
|
-
"sha256": "
|
|
2885
|
+
"sha256": "f691b0a42984a9e768477ab694bf7d7bc92264d4bb6d5f50ea52fc0e79abbfb1"
|
|
2886
2886
|
},
|
|
2887
2887
|
{
|
|
2888
2888
|
"path": "modules/grading/grader-workers/worker-verdict.js",
|
|
2889
2889
|
"mode": "0000644",
|
|
2890
|
-
"sha256": "
|
|
2890
|
+
"sha256": "9c3abb069cfdee124b4fd5c521fe1c11b44177f7af941c9ab7bbb2a4a3718bab"
|
|
2891
2891
|
},
|
|
2892
2892
|
{
|
|
2893
2893
|
"path": "modules/grading/grader.js",
|
|
@@ -4012,12 +4012,12 @@
|
|
|
4012
4012
|
{
|
|
4013
4013
|
"path": "package-lock.json",
|
|
4014
4014
|
"mode": "0000644",
|
|
4015
|
-
"sha256": "
|
|
4015
|
+
"sha256": "bb741853a2f1a9a62c27bfa769492a653a975103458dd872c4e51f7a7e4e6b9a"
|
|
4016
4016
|
},
|
|
4017
4017
|
{
|
|
4018
4018
|
"path": "package.json",
|
|
4019
4019
|
"mode": "0000644",
|
|
4020
|
-
"sha256": "
|
|
4020
|
+
"sha256": "1d0695ec7d2636aab3ca123aff5dfb89e5ccc6ddfd3cb3692b4570fd114e4f97"
|
|
4021
4021
|
},
|
|
4022
4022
|
{
|
|
4023
4023
|
"path": "public-docs/index.html",
|
|
@@ -5637,7 +5637,7 @@
|
|
|
5637
5637
|
{
|
|
5638
5638
|
"path": "src/module-api.js",
|
|
5639
5639
|
"mode": "0000644",
|
|
5640
|
-
"sha256": "
|
|
5640
|
+
"sha256": "1b4208d337a8dd0b88ba82cb42f5685537be1e4d12824c4972564cecbf84ce32"
|
|
5641
5641
|
},
|
|
5642
5642
|
{
|
|
5643
5643
|
"path": "src/module-loader/loader.js",
|
|
@@ -6607,7 +6607,7 @@
|
|
|
6607
6607
|
{
|
|
6608
6608
|
"path": "tests/grader.mjs",
|
|
6609
6609
|
"mode": "0000644",
|
|
6610
|
-
"sha256": "
|
|
6610
|
+
"sha256": "9c8525b85be898be30d6034902b34fcdedb16cf4b37fa8d79fbd5d1fb89898cb"
|
|
6611
6611
|
},
|
|
6612
6612
|
{
|
|
6613
6613
|
"path": "tests/grader_bypass.mjs",
|
|
@@ -6624,6 +6624,11 @@
|
|
|
6624
6624
|
"mode": "0000644",
|
|
6625
6625
|
"sha256": "fa155f3c5faa3763d1a51d56b7746ad870e826e3249f5e9b08108a3cfbfcbd72"
|
|
6626
6626
|
},
|
|
6627
|
+
{
|
|
6628
|
+
"path": "tests/grader_severity_gate.mjs",
|
|
6629
|
+
"mode": "0000644",
|
|
6630
|
+
"sha256": "956384c2334742858dba987fe042b6c26581875ed5981a7565d43e46b55dfc49"
|
|
6631
|
+
},
|
|
6627
6632
|
{
|
|
6628
6633
|
"path": "tests/hall_audit.mjs",
|
|
6629
6634
|
"mode": "0000644",
|
|
@@ -32,17 +32,18 @@
|
|
|
32
32
|
"concerns": "ANY worker.verdict='concerns' (and no fails) → overall passed=false, forwarded confidence = fallthrough_confidence (manual-confirm available).",
|
|
33
33
|
"pass": "ALL workers.verdict='pass' → overall passed=true, advance to confirmed."
|
|
34
34
|
},
|
|
35
|
+
"severity_gate_policy": "Task 1002663: style findings (comment style/length/placement, naming conventions, doc placement, test-name conventions) are ADVISORY — severity nit, recorded in issues[] for the audit trail, never a verdict driver. Enforced deterministically in grader-workers/worker-verdict.js applySeverityGatePolicy for narc/quality/efficiency: a worker 'fail' whose only basis is a sub-threshold score with NO major/blocker finding is an inconsistent self-report and downgrades to 'concerns' (manual-confirm pause) — a worker whose findings are all nit/minor can never verdict fail. The HACKER is deliberately EXEMPT: the security lens stays fail-closed on an unbacked sub-threshold score (a real finding mislabeled minor must still hard-fail). Untouched everywhere: blocker-driven fails, non-score fail triggers (Narc missing-AC), error short-circuits. Every worker prompt carries the matching STYLE_ADVISORY paragraph (worker-prompt.js).",
|
|
35
36
|
"active_workers": {
|
|
36
37
|
"narc": {
|
|
37
38
|
"specialty": "semantic completion (description vs diff, line-level)",
|
|
38
39
|
"scores_reported": ["functional_fidelity"],
|
|
39
|
-
"verdict_rules": "fail if any blocker issue, any AC marked 'missing', or ff < 6; concerns if any AC 'partial' OR confidence < 0.4; pass otherwise",
|
|
40
|
+
"verdict_rules": "fail if any blocker issue, any AC marked 'missing', or ff < 6; concerns if any AC 'partial' OR confidence < 0.4; pass otherwise; severity_gate_policy: an ff < 6 with no major/blocker finding downgrades fail → concerns (style findings are advisory)",
|
|
40
41
|
"prompt_lives_in": "modules/grading/grader-workers/narc.js"
|
|
41
42
|
},
|
|
42
43
|
"quality": {
|
|
43
44
|
"specialty": "code quality (idiom, dead code, tests, invariants) + economy (advisory)",
|
|
44
45
|
"scores_reported": ["code_quality", "economy"],
|
|
45
|
-
"verdict_rules": "fail if any blocker issue or cq < 6; concerns if confidence < 0.4, economy < 6 (advisory nudge), or any major issue; pass otherwise",
|
|
46
|
+
"verdict_rules": "fail if any blocker issue or cq < 6; concerns if confidence < 0.4, economy < 6 (advisory nudge), or any major issue; pass otherwise; severity_gate_policy: a cq < 6 with no major/blocker finding downgrades fail → concerns (style findings are advisory)",
|
|
46
47
|
"prompt_lives_in": "modules/grading/grader-workers/quality.js"
|
|
47
48
|
},
|
|
48
49
|
"hacker": {
|
|
@@ -55,7 +56,7 @@
|
|
|
55
56
|
"efficiency": {
|
|
56
57
|
"specialty": "runtime cost / hot-path performance (N+1, O(n²), sync I/O, unbounded queries)",
|
|
57
58
|
"scores_reported": ["efficiency_score"],
|
|
58
|
-
"verdict_rules": "fail if any blocker (critical/high) runtime-cost issue or efficiency_score < 6; concerns if confidence < 0.4 or any major (medium-severity) finding; pass otherwise",
|
|
59
|
+
"verdict_rules": "fail if any blocker (critical/high) runtime-cost issue or efficiency_score < 6; concerns if confidence < 0.4 or any major (medium-severity) finding; pass otherwise; severity_gate_policy: an efficiency_score < 6 with no major/blocker finding downgrades fail → concerns (style findings are advisory)",
|
|
59
60
|
"distinct_from_economy": "Quality's `economy` axis measures DIFF SIZE (was this change bigger than the task needed?). Efficiency Monger's `efficiency_score` measures RUNTIME COST (will this code be expensive when it runs?). Both axes can be high (small AND fast); either can be low (small but slow, or large but fast). The panel records both separately.",
|
|
60
61
|
"prompt_lives_in": "modules/grading/grader-workers/efficiency.js"
|
|
61
62
|
}
|
|
@@ -17,7 +17,7 @@ const { THRESHOLD, LOW_CONF } = require('./rubric');
|
|
|
17
17
|
// SR-15 / G1 (#995): scrub + fence all builder-controlled inputs. The shared
|
|
18
18
|
// scaffold (worker-prompt) owns fenceBlock/scrubUntrusted/NOFOLLOW_PREAMBLE.
|
|
19
19
|
const { buildWorkerPrompt, LLM_PROJECT } = require('./worker-prompt');
|
|
20
|
-
const { failResult, parseCommon, finalizeVerdict } = require('./worker-verdict');
|
|
20
|
+
const { failResult, parseCommon, finalizeVerdict, applySeverityGatePolicy } = require('./worker-verdict');
|
|
21
21
|
|
|
22
22
|
const KIND = 'efficiency';
|
|
23
23
|
const NAME = 'Efficiency Monger';
|
|
@@ -191,7 +191,7 @@ function computeVerdict(parsed, meta = {}) {
|
|
|
191
191
|
notesPrefix = `Efficiency Monger — PASS (eff=${effR}/10, ${patternsChecked.length} pattern${patternsChecked.length === 1 ? '' : 's'} checked, conf=${conf ?? '?'})`;
|
|
192
192
|
}
|
|
193
193
|
|
|
194
|
-
return finalizeVerdict({
|
|
194
|
+
return applySeverityGatePolicy(finalizeVerdict({
|
|
195
195
|
verdict,
|
|
196
196
|
scores: { efficiency_score: effR },
|
|
197
197
|
issues,
|
|
@@ -199,7 +199,7 @@ function computeVerdict(parsed, meta = {}) {
|
|
|
199
199
|
notesPrefix,
|
|
200
200
|
notesFromSubagent,
|
|
201
201
|
confidence: conf,
|
|
202
|
-
});
|
|
202
|
+
}), { threshold: THRESHOLD });
|
|
203
203
|
}
|
|
204
204
|
|
|
205
205
|
module.exports = {
|
|
@@ -217,6 +217,11 @@ function computeVerdict(parsed, meta = {}) {
|
|
|
217
217
|
notesPrefix = `Hacker — PASS (sec=${secR}/10, ${surfacesChecked.length} surface${surfacesChecked.length === 1 ? '' : 's'} checked, conf=${conf ?? '?'})`;
|
|
218
218
|
}
|
|
219
219
|
|
|
220
|
+
// #1002663: the Hacker is deliberately EXEMPT from applySeverityGatePolicy.
|
|
221
|
+
// The security lens stays fail-closed on an unbacked sub-threshold score —
|
|
222
|
+
// a real finding the model mislabels as minor must still hard-fail, and the
|
|
223
|
+
// nit-churn evidence that motivated the policy was style/Quality-side, never
|
|
224
|
+
// security-score-driven.
|
|
220
225
|
return finalizeVerdict({
|
|
221
226
|
verdict,
|
|
222
227
|
scores: { security_score: secR },
|
|
@@ -16,7 +16,7 @@ const { THRESHOLD, LOW_CONF } = require('./rubric');
|
|
|
16
16
|
// block the model is told to treat as DATA, never instructions. The shared
|
|
17
17
|
// scaffold (worker-prompt) owns fenceBlock/scrubUntrusted/NOFOLLOW_PREAMBLE.
|
|
18
18
|
const { buildWorkerPrompt, LLM_PROJECT } = require('./worker-prompt');
|
|
19
|
-
const { failResult, parseCommon, finalizeVerdict } = require('./worker-verdict');
|
|
19
|
+
const { failResult, parseCommon, finalizeVerdict, applySeverityGatePolicy } = require('./worker-verdict');
|
|
20
20
|
|
|
21
21
|
const KIND = 'narc';
|
|
22
22
|
const NAME = 'Narc';
|
|
@@ -185,7 +185,7 @@ function computeVerdict(parsed, meta = {}) {
|
|
|
185
185
|
notesPrefix = `Narc — PASS (ff=${ffR}/10, ${verifiedStr}, conf=${conf ?? '?'})`;
|
|
186
186
|
}
|
|
187
187
|
|
|
188
|
-
return finalizeVerdict({
|
|
188
|
+
return applySeverityGatePolicy(finalizeVerdict({
|
|
189
189
|
verdict,
|
|
190
190
|
scores: { functional_fidelity: ffR },
|
|
191
191
|
issues,
|
|
@@ -193,7 +193,9 @@ function computeVerdict(parsed, meta = {}) {
|
|
|
193
193
|
notesPrefix,
|
|
194
194
|
notesFromSubagent,
|
|
195
195
|
confidence: conf,
|
|
196
|
-
|
|
196
|
+
// A missing AC is a substantive non-score fail basis — it routinely
|
|
197
|
+
// co-occurs with a sub-threshold ff and must never be downgraded.
|
|
198
|
+
}), { threshold: THRESHOLD, substantiveFail: missingCount > 0 });
|
|
197
199
|
}
|
|
198
200
|
|
|
199
201
|
module.exports = {
|
|
@@ -14,7 +14,7 @@ const { THRESHOLD, LOW_CONF } = require('./rubric');
|
|
|
14
14
|
// SR-15 / G1 (#995): scrub + fence all builder-controlled inputs. The shared
|
|
15
15
|
// scaffold (worker-prompt) owns fenceBlock/scrubUntrusted/NOFOLLOW_PREAMBLE.
|
|
16
16
|
const { buildWorkerPrompt, LLM_PROJECT } = require('./worker-prompt');
|
|
17
|
-
const { failResult, parseCommon, finalizeVerdict } = require('./worker-verdict');
|
|
17
|
+
const { failResult, parseCommon, finalizeVerdict, applySeverityGatePolicy } = require('./worker-verdict');
|
|
18
18
|
|
|
19
19
|
const KIND = 'quality';
|
|
20
20
|
const NAME = 'Quality';
|
|
@@ -174,7 +174,7 @@ function computeVerdict(parsed, meta = {}) {
|
|
|
174
174
|
+ `, conf=${conf ?? '?'})`;
|
|
175
175
|
}
|
|
176
176
|
|
|
177
|
-
return finalizeVerdict({
|
|
177
|
+
return applySeverityGatePolicy(finalizeVerdict({
|
|
178
178
|
verdict,
|
|
179
179
|
scores: {
|
|
180
180
|
code_quality: cqR,
|
|
@@ -184,7 +184,7 @@ function computeVerdict(parsed, meta = {}) {
|
|
|
184
184
|
notesPrefix,
|
|
185
185
|
notesFromSubagent,
|
|
186
186
|
confidence: conf,
|
|
187
|
-
});
|
|
187
|
+
}), { threshold: THRESHOLD });
|
|
188
188
|
}
|
|
189
189
|
|
|
190
190
|
module.exports = {
|
|
@@ -29,6 +29,18 @@ const LLM_PROJECT = (() => {
|
|
|
29
29
|
catch { return 'this project'; }
|
|
30
30
|
})();
|
|
31
31
|
|
|
32
|
+
// #1002663 severity-gate policy, stated to every worker: style findings are
|
|
33
|
+
// advisory and must never drive the verdict. Enforced deterministically in
|
|
34
|
+
// worker-verdict.js applySeverityGatePolicy — this paragraph keeps the model's
|
|
35
|
+
// self-reports consistent with that gate so downgrades stay rare.
|
|
36
|
+
const STYLE_ADVISORY = [
|
|
37
|
+
'## Severity policy — style findings are ADVISORY',
|
|
38
|
+
'',
|
|
39
|
+
'Comment style/length/placement, naming conventions, documentation placement, and test-name conventions are severity `nit`, always. Record them in `issues[]` for the audit trail, but:',
|
|
40
|
+
'- never report a style finding at `major` or `blocker`;',
|
|
41
|
+
'- never let style concerns depress a numeric score below the pass threshold — a sub-threshold score MUST be backed by at least one `major` or `blocker` finding, or the verdict machinery downgrades your fail to concerns as an inconsistent self-report.',
|
|
42
|
+
].join('\n');
|
|
43
|
+
|
|
32
44
|
function buildWorkerPrompt({
|
|
33
45
|
header, task, valueSummary, diff, changedFiles,
|
|
34
46
|
taskHeader, descLabel, emptyFilesText, readFilesHint, finalInstruction,
|
|
@@ -82,6 +94,8 @@ function buildWorkerPrompt({
|
|
|
82
94
|
lines.push('');
|
|
83
95
|
lines.push(...fenceBlock('UNIFIED DIFF', diffText));
|
|
84
96
|
lines.push('');
|
|
97
|
+
lines.push(STYLE_ADVISORY);
|
|
98
|
+
lines.push('');
|
|
85
99
|
lines.push(finalInstruction);
|
|
86
100
|
return lines.join('\n');
|
|
87
101
|
}
|
|
@@ -47,4 +47,33 @@ function finalizeVerdict({ verdict, scores, issues, extra = {}, notesPrefix, not
|
|
|
47
47
|
};
|
|
48
48
|
}
|
|
49
49
|
|
|
50
|
-
|
|
50
|
+
// #1002663 severity-gate policy: style findings are advisory, and a
|
|
51
|
+
// sub-threshold score must be BACKED by a substantive finding. Every worker
|
|
52
|
+
// prompt already demands "a score below threshold must be accompanied by a
|
|
53
|
+
// major-or-blocker issue"; this makes that contract deterministic instead of
|
|
54
|
+
// advisory prose the model can ignore. A 'fail' whose only basis is a
|
|
55
|
+
// sub-threshold score with NO major/blocker finding is an inconsistent
|
|
56
|
+
// self-report (style gripes leaking into the score channel — the task-1002653
|
|
57
|
+
// regrade-churn class) and downgrades to 'concerns', which routes to the
|
|
58
|
+
// manual-confirm pause rather than a hard panel FAIL. Deliberately untouched:
|
|
59
|
+
// blocker-driven fails, non-score fail triggers (e.g. Narc's missing-AC),
|
|
60
|
+
// and the error short-circuits — no per-worker security trigger is flattened.
|
|
61
|
+
// `substantiveFail`: a worker whose fail has a NON-SCORE basis the policy
|
|
62
|
+
// cannot see in issues[] (Narc's missing-AC — which routinely CO-OCCURS with a
|
|
63
|
+
// sub-threshold ff) passes true so the downgrade never touches it. Without
|
|
64
|
+
// this, missing-AC + low ff + no major issue read as an unbacked score fail.
|
|
65
|
+
function applySeverityGatePolicy(result, { threshold, substantiveFail = false }) {
|
|
66
|
+
if (!result || result.error || result.verdict !== 'fail' || substantiveFail) return result;
|
|
67
|
+
const issues = Array.isArray(result.issues) ? result.issues : [];
|
|
68
|
+
if (issues.some((i) => i && (i.severity === 'blocker' || i.severity === 'major'))) return result;
|
|
69
|
+
const reported = Object.values(result.scores || {}).filter((v) => Number.isFinite(v));
|
|
70
|
+
if (!reported.some((v) => v < threshold)) return result;
|
|
71
|
+
return {
|
|
72
|
+
...result,
|
|
73
|
+
verdict: 'concerns',
|
|
74
|
+
policy_downgrade: 'subthreshold_score_without_substantive_finding',
|
|
75
|
+
notes_md: `${result.notes_md}\n[severity-gate] sub-threshold score with no major/blocker finding — fail downgraded to concerns (style findings are advisory)`.trim(),
|
|
76
|
+
};
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
module.exports = { failResult, parseCommon, finalizeVerdict, applySeverityGatePolicy };
|
package/package-lock.json
CHANGED
|
@@ -1,12 +1,12 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@bongos/core",
|
|
3
|
-
"version": "1.19.
|
|
3
|
+
"version": "1.19.31",
|
|
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.31",
|
|
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.31",
|
|
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
|
@@ -487,8 +487,10 @@ const { validateOrRespond, LIMITS, parseId, asyncHandler, corsPublicGet, parsePa
|
|
|
487
487
|
// landed since 1.19.28 with no explicit bump. run 31500810303. (task 1002620)
|
|
488
488
|
// 1.19.30 — CI auto-patch (publish-on-merge, ADR 0161): carrier for merges
|
|
489
489
|
// landed since 1.19.29 with no explicit bump. run 31502880105. (task 1002620)
|
|
490
|
+
// 1.19.31 — CI auto-patch (publish-on-merge, ADR 0161): carrier for merges
|
|
491
|
+
// landed since 1.19.30 with no explicit bump. run 31503461332. (task 1002620)
|
|
490
492
|
// ---------------------------------------------------------------------------
|
|
491
|
-
const CORE_VERSION = '1.19.
|
|
493
|
+
const CORE_VERSION = '1.19.31'; // CI auto-patch carrier (ADR 0161); see changelog above
|
|
492
494
|
|
|
493
495
|
// A namespaced logger so a module's log lines are attributable + consistent.
|
|
494
496
|
// Usage: const log = api.logger('dev-box'); log.info('mounted');
|
package/tests/grader.mjs
CHANGED
|
@@ -1267,12 +1267,15 @@ test('v4 narc: any partial AC → concerns', () => {
|
|
|
1267
1267
|
assert.equal(v.verdict, 'concerns');
|
|
1268
1268
|
});
|
|
1269
1269
|
|
|
1270
|
-
|
|
1270
|
+
// #1002663 severity-gate policy: a sub-threshold score must be BACKED by a
|
|
1271
|
+
// major/blocker finding to hard-fail; unbacked it downgrades to concerns
|
|
1272
|
+
// (tests/grader_severity_gate.mjs covers the downgrade shapes).
|
|
1273
|
+
test('v4 narc: ff < threshold backed by a major finding → fail', () => {
|
|
1271
1274
|
const v = grader.WORKERS.narc.computeVerdict({
|
|
1272
1275
|
functional_fidelity: 4,
|
|
1273
1276
|
confidence: 0.9,
|
|
1274
1277
|
ac_checklist: [{ ac: 'A', status: 'satisfied' }],
|
|
1275
|
-
issues: [],
|
|
1278
|
+
issues: [{ severity: 'major', file: 'x.js', summary: 'scope gap' }],
|
|
1276
1279
|
notes_md: 'low score',
|
|
1277
1280
|
}, {});
|
|
1278
1281
|
assert.equal(v.verdict, 'fail');
|
|
@@ -1316,9 +1319,10 @@ test('v4 quality: blocker issue → fail', () => {
|
|
|
1316
1319
|
assert.equal(v.verdict, 'fail');
|
|
1317
1320
|
});
|
|
1318
1321
|
|
|
1319
|
-
test('v4 quality: cq < threshold → fail', () => {
|
|
1322
|
+
test('v4 quality: cq < threshold backed by a major issue → fail (#1002663 policy)', () => {
|
|
1320
1323
|
const v = grader.WORKERS.quality.computeVerdict({
|
|
1321
|
-
code_quality: 4, economy: 7, confidence: 0.9,
|
|
1324
|
+
code_quality: 4, economy: 7, confidence: 0.9,
|
|
1325
|
+
issues: [{ severity: 'major', file: 'x.js', summary: 'dead code path' }], notes_md: '',
|
|
1322
1326
|
}, {});
|
|
1323
1327
|
assert.equal(v.verdict, 'fail');
|
|
1324
1328
|
});
|
|
@@ -1685,7 +1689,7 @@ test('v4 hacker: blocker (critical/high) issue → fail', () => {
|
|
|
1685
1689
|
assert.equal(v.verdict, 'fail');
|
|
1686
1690
|
});
|
|
1687
1691
|
|
|
1688
|
-
test('v4 hacker: security_score below threshold → fail', () => {
|
|
1692
|
+
test('v4 hacker: security_score below threshold → fail (EXEMPT from the #1002663 downgrade — fail-closed)', () => {
|
|
1689
1693
|
const v = grader.WORKERS.hacker.computeVerdict({
|
|
1690
1694
|
security_score: 4, confidence: 0.9, surfaces_checked: [], issues: [], notes_md: '',
|
|
1691
1695
|
}, {});
|
|
@@ -1821,9 +1825,10 @@ test('v4 efficiency: blocker (N+1, O(n²), sync-I/O-on-hot-path) → fail', () =
|
|
|
1821
1825
|
assert.equal(v.verdict, 'fail');
|
|
1822
1826
|
});
|
|
1823
1827
|
|
|
1824
|
-
test('v4 efficiency: efficiency_score below threshold → fail', () => {
|
|
1828
|
+
test('v4 efficiency: efficiency_score below threshold backed by a major finding → fail (#1002663 policy)', () => {
|
|
1825
1829
|
const v = grader.WORKERS.efficiency.computeVerdict({
|
|
1826
|
-
efficiency_score: 4, confidence: 0.9, expensive_patterns_checked: [],
|
|
1830
|
+
efficiency_score: 4, confidence: 0.9, expensive_patterns_checked: [],
|
|
1831
|
+
issues: [{ severity: 'major', file: 'x.js', summary: 'N+1 in list view' }], notes_md: '',
|
|
1827
1832
|
}, {});
|
|
1828
1833
|
assert.equal(v.verdict, 'fail');
|
|
1829
1834
|
});
|
|
@@ -0,0 +1,156 @@
|
|
|
1
|
+
// tests/grader_severity_gate.mjs
|
|
2
|
+
//
|
|
3
|
+
// Unit tests for task 1002663 — the severity-gate policy: style findings are
|
|
4
|
+
// ADVISORY, and a sub-threshold score must be BACKED by a major/blocker
|
|
5
|
+
// finding. Evidence class (task 1002653, 2026-08-11): panel rounds hung on
|
|
6
|
+
// comment-style nits, and the prompt line "a score below 6 must be accompanied
|
|
7
|
+
// by a major-or-blocker issue" was advisory prose the model could ignore.
|
|
8
|
+
// applySeverityGatePolicy (grader-workers/worker-verdict.js) makes it
|
|
9
|
+
// deterministic: an unbacked sub-threshold 'fail' downgrades to 'concerns'
|
|
10
|
+
// (manual-confirm pause), never a hard panel FAIL — while blocker-driven
|
|
11
|
+
// fails, non-score triggers (Narc's missing-AC), and error short-circuits
|
|
12
|
+
// stay exactly as they were.
|
|
13
|
+
//
|
|
14
|
+
// Run: node tests/grader_severity_gate.mjs
|
|
15
|
+
|
|
16
|
+
import { strict as assert } from 'node:assert';
|
|
17
|
+
import { createRequire } from 'node:module';
|
|
18
|
+
|
|
19
|
+
const require = createRequire(import.meta.url);
|
|
20
|
+
const { applySeverityGatePolicy, failResult } = require('../modules/grading/grader-workers/worker-verdict.js');
|
|
21
|
+
const narc = require('../modules/grading/grader-workers/narc.js');
|
|
22
|
+
const quality = require('../modules/grading/grader-workers/quality.js');
|
|
23
|
+
const hacker = require('../modules/grading/grader-workers/hacker.js');
|
|
24
|
+
const efficiency = require('../modules/grading/grader-workers/efficiency.js');
|
|
25
|
+
|
|
26
|
+
const THRESHOLD = 6;
|
|
27
|
+
|
|
28
|
+
let passed = 0;
|
|
29
|
+
let failed = 0;
|
|
30
|
+
function test(name, fn) {
|
|
31
|
+
try { fn(); passed++; console.log(` ok ${name}`); }
|
|
32
|
+
catch (err) { failed++; console.log(` FAIL ${name}`); console.log(` ${err.message}`); }
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
const nit = { severity: 'nit', file: 'x.js', summary: 'comment style' };
|
|
36
|
+
const minor = { severity: 'minor', file: 'x.js', summary: 'small thing' };
|
|
37
|
+
const major = { severity: 'major', file: 'x.js', summary: 'real problem' };
|
|
38
|
+
const blocker = { severity: 'blocker', file: 'x.js', summary: 'must fix' };
|
|
39
|
+
|
|
40
|
+
// ---- applySeverityGatePolicy unit ----------------------------------------
|
|
41
|
+
|
|
42
|
+
test('unbacked sub-threshold fail (only nits/minors) → concerns with policy_downgrade', () => {
|
|
43
|
+
const r = applySeverityGatePolicy(
|
|
44
|
+
{ verdict: 'fail', scores: { code_quality: 5 }, issues: [nit, minor], notes_md: 'n', confidence: 0.9, error: null },
|
|
45
|
+
{ threshold: THRESHOLD }
|
|
46
|
+
);
|
|
47
|
+
assert.equal(r.verdict, 'concerns');
|
|
48
|
+
assert.equal(r.policy_downgrade, 'subthreshold_score_without_substantive_finding');
|
|
49
|
+
assert.match(r.notes_md, /severity-gate/);
|
|
50
|
+
});
|
|
51
|
+
|
|
52
|
+
test('sub-threshold fail BACKED by a major stays fail', () => {
|
|
53
|
+
const r = applySeverityGatePolicy(
|
|
54
|
+
{ verdict: 'fail', scores: { code_quality: 5 }, issues: [major], notes_md: 'n', confidence: 0.9, error: null },
|
|
55
|
+
{ threshold: THRESHOLD }
|
|
56
|
+
);
|
|
57
|
+
assert.equal(r.verdict, 'fail');
|
|
58
|
+
assert.equal(r.policy_downgrade, undefined);
|
|
59
|
+
});
|
|
60
|
+
|
|
61
|
+
test('blocker-driven fail with passing scores stays fail (no security trigger flattened)', () => {
|
|
62
|
+
const r = applySeverityGatePolicy(
|
|
63
|
+
{ verdict: 'fail', scores: { security_score: 9 }, issues: [blocker], notes_md: 'n', confidence: 0.9, error: null },
|
|
64
|
+
{ threshold: THRESHOLD }
|
|
65
|
+
);
|
|
66
|
+
assert.equal(r.verdict, 'fail');
|
|
67
|
+
});
|
|
68
|
+
|
|
69
|
+
test('non-score fail trigger (no sub-threshold score, no issues) stays fail — the Narc missing-AC shape', () => {
|
|
70
|
+
const r = applySeverityGatePolicy(
|
|
71
|
+
{ verdict: 'fail', scores: { functional_fidelity: 7 }, issues: [], notes_md: 'n', confidence: 0.9, error: null },
|
|
72
|
+
{ threshold: THRESHOLD }
|
|
73
|
+
);
|
|
74
|
+
assert.equal(r.verdict, 'fail');
|
|
75
|
+
});
|
|
76
|
+
|
|
77
|
+
test('error short-circuits are untouched', () => {
|
|
78
|
+
const r = applySeverityGatePolicy(failResult({ notesMd: 'boom', error: 'subprocess_error' }), { threshold: THRESHOLD });
|
|
79
|
+
assert.equal(r.verdict, 'fail');
|
|
80
|
+
assert.equal(r.error, 'subprocess_error');
|
|
81
|
+
});
|
|
82
|
+
|
|
83
|
+
test('pass and concerns verdicts are untouched', () => {
|
|
84
|
+
for (const verdict of ['pass', 'concerns']) {
|
|
85
|
+
const r = applySeverityGatePolicy(
|
|
86
|
+
{ verdict, scores: { code_quality: 5 }, issues: [nit], notes_md: 'n', confidence: 0.9, error: null },
|
|
87
|
+
{ threshold: THRESHOLD }
|
|
88
|
+
);
|
|
89
|
+
assert.equal(r.verdict, verdict);
|
|
90
|
+
}
|
|
91
|
+
});
|
|
92
|
+
|
|
93
|
+
// ---- worker integration ---------------------------------------------------
|
|
94
|
+
|
|
95
|
+
test('quality: cq=5 with only nits → concerns (not fail)', () => {
|
|
96
|
+
const r = quality.computeVerdict({ code_quality: 5, economy: 8, confidence: 0.9, issues: [nit], notes_md: '' });
|
|
97
|
+
assert.equal(r.verdict, 'concerns');
|
|
98
|
+
assert.equal(r.policy_downgrade, 'subthreshold_score_without_substantive_finding');
|
|
99
|
+
});
|
|
100
|
+
|
|
101
|
+
test('quality: cq=5 with a major → fail stays', () => {
|
|
102
|
+
const r = quality.computeVerdict({ code_quality: 5, economy: 8, confidence: 0.9, issues: [major], notes_md: '' });
|
|
103
|
+
assert.equal(r.verdict, 'fail');
|
|
104
|
+
});
|
|
105
|
+
|
|
106
|
+
test('quality: blocker with cq=9 → fail stays', () => {
|
|
107
|
+
const r = quality.computeVerdict({ code_quality: 9, economy: 8, confidence: 0.9, issues: [blocker], notes_md: '' });
|
|
108
|
+
assert.equal(r.verdict, 'fail');
|
|
109
|
+
});
|
|
110
|
+
|
|
111
|
+
test('hacker is EXEMPT: security_score=5 with only a nit stays a hard fail (security lens fail-closed)', () => {
|
|
112
|
+
const r = hacker.computeVerdict({ security_score: 5, confidence: 0.9, issues: [nit], notes_md: '', surfaces_checked: [] });
|
|
113
|
+
assert.equal(r.verdict, 'fail');
|
|
114
|
+
assert.equal(r.policy_downgrade, undefined);
|
|
115
|
+
});
|
|
116
|
+
|
|
117
|
+
test('efficiency: eff=5 with only a minor → concerns (not fail)', () => {
|
|
118
|
+
const r = efficiency.computeVerdict({ efficiency_score: 5, confidence: 0.9, issues: [minor], notes_md: '', expensive_patterns_checked: [] });
|
|
119
|
+
assert.equal(r.verdict, 'concerns');
|
|
120
|
+
});
|
|
121
|
+
|
|
122
|
+
test('narc: missing AC with ff=7 and no issues → fail stays (non-score trigger untouched)', () => {
|
|
123
|
+
const r = narc.computeVerdict({
|
|
124
|
+
functional_fidelity: 7, confidence: 0.9, issues: [], notes_md: '',
|
|
125
|
+
ac_checklist: [{ ac: 'does the thing', status: 'missing' }],
|
|
126
|
+
});
|
|
127
|
+
assert.equal(r.verdict, 'fail');
|
|
128
|
+
});
|
|
129
|
+
|
|
130
|
+
test('narc: missing AC CO-OCCURRING with sub-threshold ff and no issues → fail stays (the common shape)', () => {
|
|
131
|
+
const r = narc.computeVerdict({
|
|
132
|
+
functional_fidelity: 4, confidence: 0.9, issues: [], notes_md: '',
|
|
133
|
+
ac_checklist: [{ ac: 'does the thing', status: 'missing' }],
|
|
134
|
+
});
|
|
135
|
+
assert.equal(r.verdict, 'fail');
|
|
136
|
+
assert.equal(r.policy_downgrade, undefined);
|
|
137
|
+
});
|
|
138
|
+
|
|
139
|
+
test('narc: ff=5 with only nits → concerns (not fail)', () => {
|
|
140
|
+
const r = narc.computeVerdict({ functional_fidelity: 5, confidence: 0.9, issues: [nit], notes_md: '', ac_checklist: [] });
|
|
141
|
+
assert.equal(r.verdict, 'concerns');
|
|
142
|
+
});
|
|
143
|
+
|
|
144
|
+
// ---- prompt carries the policy -------------------------------------------
|
|
145
|
+
|
|
146
|
+
test('every worker prompt carries the STYLE_ADVISORY paragraph', () => {
|
|
147
|
+
const args = { task: { id: 1, title: 't', description: 'd', kind: 'bug', discipline: 'engineer' }, valueSummary: 'v', diff: '+ x', changedFiles: ['x.js'] };
|
|
148
|
+
for (const w of [narc, quality, hacker, efficiency]) {
|
|
149
|
+
const p = w.buildPrompt(args);
|
|
150
|
+
assert.match(p, /Severity policy — style findings are ADVISORY/, `${w.KIND || 'worker'} prompt must carry the policy`);
|
|
151
|
+
assert.match(p, /never report a style finding at `major`/);
|
|
152
|
+
}
|
|
153
|
+
});
|
|
154
|
+
|
|
155
|
+
console.log(`\n${passed} passed, ${failed} failed`);
|
|
156
|
+
process.exit(failed === 0 ? 0 : 1);
|