@bongos/core 1.19.487 → 1.19.489
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 +31 -21
- package/docs/copy-inventory.md +43 -33
- package/docs/copy-registry.json +167 -57
- package/docs/module-api-changelog.md +4 -0
- package/modules/hall-ui/public/profile-charts.js +461 -0
- package/modules/hall-ui/public/profile.css +90 -1
- package/modules/hall-ui/public/profile.html +146 -97
- package/modules/hall-ui/public/profile.js +164 -31
- package/modules/hall-ui/public/profile.states.json +109 -8
- package/modules/lifecycle/db-claim-reads.js +16 -3
- package/package-lock.json +2 -2
- package/package.json +1 -1
- package/scripts/gds/ship-grade-steps.js +23 -1
- package/scripts/gds/ship.js +4 -0
- package/src/module-api.js +1 -1
- package/tests/grader_regrade_delta.mjs +57 -0
- package/tests/hall_profile_world.mjs +342 -0
|
@@ -1,15 +1,116 @@
|
|
|
1
1
|
{
|
|
2
|
-
"_": "The hall's PROFILE page through modules/ui-design/kit/render.js (task 1003308). The identity band, the
|
|
2
|
+
"_": "The hall's PROFILE page through modules/ui-design/kit/render.js (task 1003308; four screens behind the section rail since task 1003489 — overview, works, activity, connections). The identity band, the charts, the works ledger and the activity ledgers only exist once a profile has LOADED, so the floors run against the hall-preview harness (see settings.states.json for the two commands; pass --base-in/--base-out at it). One rail key renders at a time, so each key is its own state. `out` is a real state here rather than a redirect: a signed-out visitor with no ?id= gets the empty state, not a bounce — under the harness's --fixture-me the /me read still answers, so `out` renders the own profile there and the empty state only under the kit's own stub.",
|
|
3
3
|
"page": "/profile",
|
|
4
4
|
"surface": "hall-ui",
|
|
5
|
-
"stub": {
|
|
5
|
+
"stub": {
|
|
6
|
+
"prefix": "/builders/"
|
|
7
|
+
},
|
|
6
8
|
"modeQuery": false,
|
|
7
|
-
"ignoreRequests": [
|
|
8
|
-
|
|
9
|
+
"ignoreRequests": [
|
|
10
|
+
"/api/(gds|bongos)/"
|
|
11
|
+
],
|
|
12
|
+
"ignoreConsole": [
|
|
13
|
+
"/api/(gds|bongos)/",
|
|
14
|
+
"\\b401\\b",
|
|
15
|
+
"\\b404\\b",
|
|
16
|
+
"Unauthorized",
|
|
17
|
+
"Not Found",
|
|
18
|
+
"load failed"
|
|
19
|
+
],
|
|
9
20
|
"states": {
|
|
10
|
-
"own": {
|
|
11
|
-
|
|
12
|
-
|
|
21
|
+
"own": {
|
|
22
|
+
"auth": true,
|
|
23
|
+
"actions": [
|
|
24
|
+
[
|
|
25
|
+
"wait",
|
|
26
|
+
1200
|
|
27
|
+
]
|
|
28
|
+
],
|
|
29
|
+
"expect": {
|
|
30
|
+
"visible": [
|
|
31
|
+
"#profile-h1",
|
|
32
|
+
"#profile-rail",
|
|
33
|
+
"#profile-head",
|
|
34
|
+
"#numbers-body",
|
|
35
|
+
"#charts-controls",
|
|
36
|
+
"#charts-grid",
|
|
37
|
+
"#ach-grid"
|
|
38
|
+
]
|
|
39
|
+
}
|
|
40
|
+
},
|
|
41
|
+
"works": {
|
|
42
|
+
"auth": true,
|
|
43
|
+
"url": "/profile#works",
|
|
44
|
+
"actions": [
|
|
45
|
+
[
|
|
46
|
+
"wait",
|
|
47
|
+
1200
|
|
48
|
+
]
|
|
49
|
+
],
|
|
50
|
+
"expect": {
|
|
51
|
+
"visible": [
|
|
52
|
+
"#profile-rail",
|
|
53
|
+
"#works-table"
|
|
54
|
+
]
|
|
55
|
+
}
|
|
56
|
+
},
|
|
57
|
+
"activity": {
|
|
58
|
+
"auth": true,
|
|
59
|
+
"url": "/profile#activity",
|
|
60
|
+
"actions": [
|
|
61
|
+
[
|
|
62
|
+
"wait",
|
|
63
|
+
1200
|
|
64
|
+
]
|
|
65
|
+
],
|
|
66
|
+
"expect": {
|
|
67
|
+
"visible": [
|
|
68
|
+
"#profile-rail",
|
|
69
|
+
"#activity-tabs",
|
|
70
|
+
"#activity-tasks"
|
|
71
|
+
]
|
|
72
|
+
}
|
|
73
|
+
},
|
|
74
|
+
"other": {
|
|
75
|
+
"auth": true,
|
|
76
|
+
"url": "/profile?id=1000025",
|
|
77
|
+
"actions": [
|
|
78
|
+
[
|
|
79
|
+
"wait",
|
|
80
|
+
1200
|
|
81
|
+
]
|
|
82
|
+
],
|
|
83
|
+
"expect": {
|
|
84
|
+
"visible": [
|
|
85
|
+
"#profile-head",
|
|
86
|
+
"#charts-grid",
|
|
87
|
+
"#ach-grid"
|
|
88
|
+
]
|
|
89
|
+
}
|
|
90
|
+
},
|
|
91
|
+
"out": {
|
|
92
|
+
"auth": false,
|
|
93
|
+
"actions": [
|
|
94
|
+
[
|
|
95
|
+
"wait",
|
|
96
|
+
600
|
|
97
|
+
]
|
|
98
|
+
],
|
|
99
|
+
"expect": {
|
|
100
|
+
"visible": [
|
|
101
|
+
"#profile-h1",
|
|
102
|
+
"#profile-head"
|
|
103
|
+
]
|
|
104
|
+
}
|
|
105
|
+
}
|
|
13
106
|
},
|
|
14
|
-
"reducedMotion": {
|
|
107
|
+
"reducedMotion": {
|
|
108
|
+
"auth": true,
|
|
109
|
+
"actions": [
|
|
110
|
+
[
|
|
111
|
+
"wait",
|
|
112
|
+
1200
|
|
113
|
+
]
|
|
114
|
+
]
|
|
115
|
+
}
|
|
15
116
|
}
|
|
@@ -140,9 +140,19 @@ async function getLastClaimHolder(taskId) {
|
|
|
140
140
|
|
|
141
141
|
// getTaskGrade — fetch the current grade row for a task (V4.R08 diff-hash
|
|
142
142
|
// reuse). Returns { diff_hash, passed, score, threshold, grader_kind,
|
|
143
|
-
// grader_version, rubric_scores, signals, notes_md } or null when no
|
|
144
|
-
// exists yet. Used by ship.js before the grade step to detect "same diff →
|
|
143
|
+
// grader_version, rubric_scores, signals, notes_md, issues } or null when no
|
|
144
|
+
// grade exists yet. Used by ship.js before the grade step to detect "same diff →
|
|
145
145
|
// skip the panel".
|
|
146
|
+
//
|
|
147
|
+
// `issues` is LIFTED out of the signals jsonb (task 1003488). The panel's
|
|
148
|
+
// findings are stored there because `task_grades` has no column for them
|
|
149
|
+
// (db-grade.js), but every in-memory grade carries them at the top level, so a
|
|
150
|
+
// reader that did not know the storage detail saw an empty `grade.issues` on a
|
|
151
|
+
// row that in fact held findings — which is how the delta re-grade was silently
|
|
152
|
+
// running with no prior-round context, and how a builder following
|
|
153
|
+
// /grade-recover reads a failed grade as having no findings at all. Lifting is
|
|
154
|
+
// additive: `signals.issues` stays exactly where it is for anything already
|
|
155
|
+
// reading it.
|
|
146
156
|
async function getTaskGrade(taskId) {
|
|
147
157
|
const { rows } = await pool.query(
|
|
148
158
|
`SELECT diff_hash, passed, score, threshold, grader_kind, grader_version,
|
|
@@ -152,7 +162,10 @@ async function getTaskGrade(taskId) {
|
|
|
152
162
|
LIMIT 1`,
|
|
153
163
|
[taskId]
|
|
154
164
|
);
|
|
155
|
-
|
|
165
|
+
const row = rows[0] ?? null;
|
|
166
|
+
if (!row) return null;
|
|
167
|
+
const fromSignals = row.signals && row.signals.issues;
|
|
168
|
+
return { ...row, issues: Array.isArray(fromSignals) ? fromSignals : [] };
|
|
156
169
|
}
|
|
157
170
|
|
|
158
171
|
module.exports = {
|
package/package-lock.json
CHANGED
|
@@ -1,12 +1,12 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@bongos/core",
|
|
3
|
-
"version": "1.19.
|
|
3
|
+
"version": "1.19.489",
|
|
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.489",
|
|
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.489",
|
|
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",
|
|
@@ -94,10 +94,31 @@ async function logGraderPanelCost(taskId, gradeResult) {
|
|
|
94
94
|
// audit — the compact regrade block recorded into the new grade's signals
|
|
95
95
|
// so grade_attempts rounds read as remediation history (the
|
|
96
96
|
// task-1002661 reader consumes this).
|
|
97
|
+
// The prior round's findings, from EITHER grade shape (task 1003488).
|
|
98
|
+
//
|
|
99
|
+
// A grade exists in two shapes and this function is handed both. The in-memory
|
|
100
|
+
// result `validateAndScore`/`serverSideGuards` return carries `issues` at the top
|
|
101
|
+
// level; a grade READ BACK from the API does not, because `task_grades` has no
|
|
102
|
+
// `issues` column and `db-grade.js` deliberately writes them into the signals
|
|
103
|
+
// jsonb instead. `--regrade` passes the DB row (`ship-flow.js`: `tRes.data.grade`),
|
|
104
|
+
// so reading only the top-level key silently yielded [] on every re-grade — the
|
|
105
|
+
// panel was told it was on round N while being handed nothing about round N-1,
|
|
106
|
+
// and the "answered findings can only re-veto with NEW evidence" protection
|
|
107
|
+
// (task 1002662) cannot hold when the answered findings are invisible.
|
|
108
|
+
//
|
|
109
|
+
// Top level wins when present so an in-memory result keeps its exact behaviour;
|
|
110
|
+
// `signals.issues` is the stored fallback. Mirrors how the caller already reaches
|
|
111
|
+
// for `signals.graded_head_sha` three lines from where it passes this row in.
|
|
112
|
+
function priorFindings(storedGrade) {
|
|
113
|
+
if (Array.isArray(storedGrade.issues)) return storedGrade.issues;
|
|
114
|
+
const fromSignals = storedGrade.signals && storedGrade.signals.issues;
|
|
115
|
+
return Array.isArray(fromSignals) ? fromSignals : [];
|
|
116
|
+
}
|
|
117
|
+
|
|
97
118
|
const PRIOR_ROUND_TEXT_CAP = 4000;
|
|
98
119
|
function buildPriorRoundContext({ storedGrade, responseText = '', interimCommits = '', interimDiffStat = '' }) {
|
|
99
120
|
if (!storedGrade || typeof storedGrade !== 'object') return null;
|
|
100
|
-
const issues =
|
|
121
|
+
const issues = priorFindings(storedGrade);
|
|
101
122
|
const notes = typeof storedGrade.notes_md === 'string' ? storedGrade.notes_md.trim() : '';
|
|
102
123
|
if (issues.length === 0 && !notes) return null;
|
|
103
124
|
const verdictLine = `Prior verdict: ${storedGrade.passed ? 'PASS' : 'FAIL'} · score ${storedGrade.score ?? '?'}/10 · ${issues.length} finding(s)`;
|
|
@@ -436,6 +457,7 @@ function printCreditsAndAchievements({ postGradeStatus, credits, creditsRaw, cre
|
|
|
436
457
|
module.exports = {
|
|
437
458
|
buildGradePayload,
|
|
438
459
|
buildPriorRoundContext,
|
|
460
|
+
priorFindings,
|
|
439
461
|
computeGradeResult,
|
|
440
462
|
formatGradeOutcome,
|
|
441
463
|
gradeAndRecord,
|
package/scripts/gds/ship.js
CHANGED
|
@@ -145,6 +145,7 @@ const {
|
|
|
145
145
|
buildGradePayload,
|
|
146
146
|
buildPriorRoundContext,
|
|
147
147
|
computeGradeResult,
|
|
148
|
+
priorFindings,
|
|
148
149
|
formatGradeOutcome,
|
|
149
150
|
gradeAndRecord,
|
|
150
151
|
} = require('./ship-grade-steps.js');
|
|
@@ -195,6 +196,9 @@ module.exports = {
|
|
|
195
196
|
// task 1002662: delta-aware re-grade — the prior-round payload builder,
|
|
196
197
|
// exported pure so the panel/audit shapes are pinned without git/network.
|
|
197
198
|
buildPriorRoundContext,
|
|
199
|
+
// task 1003488: the two-shape finding reader behind it (a stored grade keeps
|
|
200
|
+
// its findings in signals.issues; an in-memory one at the top level).
|
|
201
|
+
priorFindings,
|
|
198
202
|
// #943: pure grade-outcome formatter — exported so the grader-unavailable vs
|
|
199
203
|
// FAIL display branch is unit-tested without capturing stdout.
|
|
200
204
|
formatGradeOutcome,
|
package/src/module-api.js
CHANGED
|
@@ -45,7 +45,7 @@ const { validateOrRespond, LIMITS, parseId, asyncHandler, corsPublicGet, parsePa
|
|
|
45
45
|
// there. scripts/gds/bump-version.js still rewrites the literal below; it appends
|
|
46
46
|
// the entry to that file. Look for a version's history there, not here.
|
|
47
47
|
// ---------------------------------------------------------------------------
|
|
48
|
-
const CORE_VERSION = '1.19.
|
|
48
|
+
const CORE_VERSION = '1.19.489'; // CI auto-patch carrier (ADR 0161); changelog: docs/module-api-changelog.md
|
|
49
49
|
|
|
50
50
|
// A namespaced logger so a module's log lines are attributable + consistent.
|
|
51
51
|
// Usage: const log = api.logger('dev-box'); log.info('mounted');
|
|
@@ -184,5 +184,62 @@ test('buildPriorRoundContext caps oversized notes/response/interim text', () =>
|
|
|
184
184
|
assert.ok(ctx.forPanel.interim_md.length < 5000, 'interim capped');
|
|
185
185
|
});
|
|
186
186
|
|
|
187
|
+
// ---- task 1003488: the prior round is read from a STORED grade, not just an
|
|
188
|
+
// ---- in-memory one. A grade exists in two shapes and this function is handed
|
|
189
|
+
// ---- both; reading only the top-level key made every --regrade run blind.
|
|
190
|
+
|
|
191
|
+
test('prior round: a STORED grade carries its findings under signals.issues and they still reach the panel', () => {
|
|
192
|
+
// exactly what ship-flow.js passes on --regrade: tRes.data.grade, a DB row.
|
|
193
|
+
// task_grades has no issues column, so db-grade.js writes them into signals.
|
|
194
|
+
const dbRow = {
|
|
195
|
+
passed: false, score: 5, notes_md: '',
|
|
196
|
+
signals: {
|
|
197
|
+
issue_count: 2,
|
|
198
|
+
issues: [
|
|
199
|
+
{ severity: 'major', worker: 'efficiency', file: 'a.js', summary: 'redundant sync reads' },
|
|
200
|
+
{ severity: 'nit', worker: 'quality', file: 'b.js', summary: 'label drift' },
|
|
201
|
+
],
|
|
202
|
+
},
|
|
203
|
+
};
|
|
204
|
+
const ctx = ship.buildPriorRoundContext({ storedGrade: dbRow });
|
|
205
|
+
assert.ok(ctx, 'a stored grade with findings is delta-aware context, not null');
|
|
206
|
+
assert.equal(ctx.audit.prior_findings_count, 2, 'the count is the stored findings, not 0');
|
|
207
|
+
assert.match(ctx.forPanel.findings_md, /2 finding\(s\)/);
|
|
208
|
+
assert.match(ctx.forPanel.findings_md, /redundant sync reads/, 'the finding text rides to the panel');
|
|
209
|
+
assert.match(ctx.forPanel.findings_md, /\[major\] efficiency · a\.js/, 'severity, worker and file survive');
|
|
210
|
+
assert.ok(!/no per-finding rows/.test(ctx.forPanel.findings_md),
|
|
211
|
+
'it must not fall through to the "no rows" placeholder');
|
|
212
|
+
});
|
|
213
|
+
|
|
214
|
+
test('prior round: an IN-MEMORY grade keeps its exact top-level behaviour', () => {
|
|
215
|
+
const inMemory = {
|
|
216
|
+
passed: false, score: 5, notes_md: '',
|
|
217
|
+
issues: [{ severity: 'major', worker: 'quality', file: 'c.js', summary: 'top level wins' }],
|
|
218
|
+
signals: { issues: [{ severity: 'nit', worker: 'x', file: 'd.js', summary: 'must not be preferred' }] },
|
|
219
|
+
};
|
|
220
|
+
const ctx = ship.buildPriorRoundContext({ storedGrade: inMemory });
|
|
221
|
+
assert.equal(ctx.audit.prior_findings_count, 1);
|
|
222
|
+
assert.match(ctx.forPanel.findings_md, /top level wins/);
|
|
223
|
+
assert.ok(!/must not be preferred/.test(ctx.forPanel.findings_md),
|
|
224
|
+
'top-level issues take precedence over signals.issues');
|
|
225
|
+
});
|
|
226
|
+
|
|
227
|
+
test('priorFindings: reads either shape, and never throws on a malformed one', () => {
|
|
228
|
+
const f = ship.priorFindings;
|
|
229
|
+
assert.deepEqual(f({ issues: [{ severity: 'nit' }] }).length, 1, 'top level');
|
|
230
|
+
assert.deepEqual(f({ signals: { issues: [{ severity: 'nit' }, { severity: 'major' }] } }).length, 2, 'stored');
|
|
231
|
+
for (const bad of [{}, { issues: null }, { signals: null }, { signals: {} },
|
|
232
|
+
{ signals: { issues: 'nope' } }, { issues: 'nope', signals: { issues: [{ severity: 'nit' }] } }]) {
|
|
233
|
+
const out = f(bad);
|
|
234
|
+
assert.ok(Array.isArray(out), `array for ${JSON.stringify(bad)}`);
|
|
235
|
+
}
|
|
236
|
+
// a non-array top level must fall through to the stored shape, not swallow it
|
|
237
|
+
assert.equal(f({ issues: 'nope', signals: { issues: [{ severity: 'nit' }] } }).length, 1);
|
|
238
|
+
});
|
|
239
|
+
|
|
240
|
+
test('prior round: a stored grade with NO findings anywhere is still null (unchanged)', () => {
|
|
241
|
+
assert.equal(ship.buildPriorRoundContext({ storedGrade: { passed: true, notes_md: '', signals: {} } }), null);
|
|
242
|
+
});
|
|
243
|
+
|
|
187
244
|
console.log(`\n${passed} passed, ${failed} failed`);
|
|
188
245
|
process.exit(failed === 0 ? 0 : 1);
|