@bongos/core 1.19.134 → 1.19.136
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-workers/worker-prompt.js +11 -2
- package/modules/grading/grader.js +62 -6
- package/modules/hall-ui/public/my-projects.css +40 -0
- package/modules/hall-ui/public/my-projects.html +55 -65
- package/modules/hall-ui/public/my-projects.js +3 -3
- package/modules/hall-ui/public/scouting.html +40 -34
- package/modules/hall-ui/public/scouting.js +11 -11
- package/package-lock.json +2 -2
- package/package.json +1 -1
- package/src/module-api.js +5 -1
- package/tests/grader.mjs +68 -0
- package/tests/hall_audit.mjs +42 -8
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.136",
|
|
6
|
+
"core_contract": "1.19.136",
|
|
7
|
+
"source_commit": "e8baa6b4bb7636ec78d0d37a48fafb861e716dad",
|
|
8
8
|
"source_ref": "HEAD",
|
|
9
|
-
"built_at": "2026-08-
|
|
9
|
+
"built_at": "2026-08-15T14:17:27.560Z",
|
|
10
10
|
"redaction": {
|
|
11
11
|
"model": "docs-redacted+functional-verbatim",
|
|
12
12
|
"docs_redacted": 303,
|
|
13
13
|
"agent_docs_stubbed": 21,
|
|
14
|
-
"functional_verbatim":
|
|
14
|
+
"functional_verbatim": 1250,
|
|
15
15
|
"rules": 3,
|
|
16
16
|
"gate_literals": 3,
|
|
17
17
|
"gate": "passed"
|
|
18
18
|
},
|
|
19
|
-
"file_count":
|
|
20
|
-
"tree_sha256": "
|
|
19
|
+
"file_count": 1574,
|
|
20
|
+
"tree_sha256": "c4c465a8bb9c113e17dd02666d74907f5d4057d06743e14d73bcc57ff7482290",
|
|
21
21
|
"files": [
|
|
22
22
|
{
|
|
23
23
|
"path": ".claude/skills/blocker-review/SKILL.md",
|
|
@@ -3012,7 +3012,7 @@
|
|
|
3012
3012
|
{
|
|
3013
3013
|
"path": "modules/grading/grader-workers/worker-prompt.js",
|
|
3014
3014
|
"mode": "0000644",
|
|
3015
|
-
"sha256": "
|
|
3015
|
+
"sha256": "986c7b039f392704613160c7ee10738030ef47972150d867e0972f6dfb761ae4"
|
|
3016
3016
|
},
|
|
3017
3017
|
{
|
|
3018
3018
|
"path": "modules/grading/grader-workers/worker-verdict.js",
|
|
@@ -3022,7 +3022,7 @@
|
|
|
3022
3022
|
{
|
|
3023
3023
|
"path": "modules/grading/grader.js",
|
|
3024
3024
|
"mode": "0000644",
|
|
3025
|
-
"sha256": "
|
|
3025
|
+
"sha256": "49ae9286fea73fbf67a89e42bf10a14f9fb04d7857582accaab402ad966fafd6"
|
|
3026
3026
|
},
|
|
3027
3027
|
{
|
|
3028
3028
|
"path": "modules/grading/module.json",
|
|
@@ -3239,15 +3239,20 @@
|
|
|
3239
3239
|
"mode": "0000644",
|
|
3240
3240
|
"sha256": "9df48fd541deabb6765cb622fc00f829edc4d6605c746f28a6d76cbfc07e22ad"
|
|
3241
3241
|
},
|
|
3242
|
+
{
|
|
3243
|
+
"path": "modules/hall-ui/public/my-projects.css",
|
|
3244
|
+
"mode": "0000644",
|
|
3245
|
+
"sha256": "5c702a9c2054287a8523b6901cd46e7caa1cb9580b7e8f04a3898b2c8bd82fd0"
|
|
3246
|
+
},
|
|
3242
3247
|
{
|
|
3243
3248
|
"path": "modules/hall-ui/public/my-projects.html",
|
|
3244
3249
|
"mode": "0000644",
|
|
3245
|
-
"sha256": "
|
|
3250
|
+
"sha256": "49c63ef48e0fc6450ba15d76743c8220b5d5f27702843be71fa077551d92c35b"
|
|
3246
3251
|
},
|
|
3247
3252
|
{
|
|
3248
3253
|
"path": "modules/hall-ui/public/my-projects.js",
|
|
3249
3254
|
"mode": "0000644",
|
|
3250
|
-
"sha256": "
|
|
3255
|
+
"sha256": "30d22956e21c9ac371d989d8b295631e7bd5df89647457e81a71e0dfffe07cce"
|
|
3251
3256
|
},
|
|
3252
3257
|
{
|
|
3253
3258
|
"path": "modules/hall-ui/public/not-ready.html",
|
|
@@ -3337,12 +3342,12 @@
|
|
|
3337
3342
|
{
|
|
3338
3343
|
"path": "modules/hall-ui/public/scouting.html",
|
|
3339
3344
|
"mode": "0000644",
|
|
3340
|
-
"sha256": "
|
|
3345
|
+
"sha256": "4f1656ab1deab1a748d294e70bf705173516254dbe3cb8a560a2ea811d5afcb0"
|
|
3341
3346
|
},
|
|
3342
3347
|
{
|
|
3343
3348
|
"path": "modules/hall-ui/public/scouting.js",
|
|
3344
3349
|
"mode": "0000644",
|
|
3345
|
-
"sha256": "
|
|
3350
|
+
"sha256": "0109d6715ba097f2c4329fc01d7e2378a09be728d4cc829354cad3c5af1d8e89"
|
|
3346
3351
|
},
|
|
3347
3352
|
{
|
|
3348
3353
|
"path": "modules/hall-ui/public/sessions.css",
|
|
@@ -4172,12 +4177,12 @@
|
|
|
4172
4177
|
{
|
|
4173
4178
|
"path": "package-lock.json",
|
|
4174
4179
|
"mode": "0000644",
|
|
4175
|
-
"sha256": "
|
|
4180
|
+
"sha256": "8f0f435bc22d3f90b635703d6255ab5707be608c2263b7d62d2a22f84b239401"
|
|
4176
4181
|
},
|
|
4177
4182
|
{
|
|
4178
4183
|
"path": "package.json",
|
|
4179
4184
|
"mode": "0000644",
|
|
4180
|
-
"sha256": "
|
|
4185
|
+
"sha256": "f67d85ba93fc5babadfce8e44653c771fc0ac07471c0f5ffdcb0b13b0fab244c"
|
|
4181
4186
|
},
|
|
4182
4187
|
{
|
|
4183
4188
|
"path": "public-docs/index.html",
|
|
@@ -5762,7 +5767,7 @@
|
|
|
5762
5767
|
{
|
|
5763
5768
|
"path": "src/module-api.js",
|
|
5764
5769
|
"mode": "0000644",
|
|
5765
|
-
"sha256": "
|
|
5770
|
+
"sha256": "096a5ae6bd847f8cfff0cb8e12ebeae0c99f3116f0420babdaea9e683a2b9fd3"
|
|
5766
5771
|
},
|
|
5767
5772
|
{
|
|
5768
5773
|
"path": "src/module-loader/catalog.js",
|
|
@@ -6792,7 +6797,7 @@
|
|
|
6792
6797
|
{
|
|
6793
6798
|
"path": "tests/grader.mjs",
|
|
6794
6799
|
"mode": "0000644",
|
|
6795
|
-
"sha256": "
|
|
6800
|
+
"sha256": "e97bbe1f1d9bc1b602adbae26e5144675a9324c5b729d0046ffa43407a88922c"
|
|
6796
6801
|
},
|
|
6797
6802
|
{
|
|
6798
6803
|
"path": "tests/grader_bypass.mjs",
|
|
@@ -6822,7 +6827,7 @@
|
|
|
6822
6827
|
{
|
|
6823
6828
|
"path": "tests/hall_audit.mjs",
|
|
6824
6829
|
"mode": "0000644",
|
|
6825
|
-
"sha256": "
|
|
6830
|
+
"sha256": "a048f93e0962bf30869c797e53be26954af276522adba4644a5b233d870e5286"
|
|
6826
6831
|
},
|
|
6827
6832
|
{
|
|
6828
6833
|
"path": "tests/hall_nav.mjs",
|
|
@@ -115,12 +115,21 @@ function buildWorkerPrompt({
|
|
|
115
115
|
lines.push('---');
|
|
116
116
|
lines.push(`## Post-change file contents (${contextBundle.files.length} of ${(changedFiles || []).length} changed files, bundled)`);
|
|
117
117
|
lines.push('');
|
|
118
|
-
lines.push('The post-change contents of the changed files below are included inline — use your Read tool ONLY for files not bundled here (listed after the blocks) or for unchanged context files.');
|
|
118
|
+
lines.push('The post-change contents of the changed files below are included inline — use your Read tool ONLY for files not bundled here (listed after the blocks), for any block marked TRUNCATED, or for unchanged context files.');
|
|
119
|
+
lines.push('');
|
|
120
|
+
// task 1002905: a TRUNCATED block is a head PREFIX, not the file. A worker
|
|
121
|
+
// that searched the prefix, missed a change 62k chars in, and reported it as
|
|
122
|
+
// absent produced five fabricated blockers on a correct ship — so say what
|
|
123
|
+
// truncation means and forbid the absence claim it invites.
|
|
124
|
+
lines.push('A block marked TRUNCATED shows only the first N characters of that file — everything past that point is ABSENT FROM THIS PROMPT, not absent from the file. Never report code as missing, unexported, unchanged, or "not applied" on the strength of a truncated block: Read the file first, and if you cannot, say so rather than asserting absence.');
|
|
119
125
|
lines.push('');
|
|
120
126
|
for (const f of contextBundle.files) {
|
|
121
127
|
// The filename is builder-controlled — scrub it like every other
|
|
122
128
|
// untrusted string that lands OUTSIDE a fence (panel hacker finding).
|
|
123
|
-
const
|
|
129
|
+
const truncNote = f.truncated
|
|
130
|
+
? ` (TRUNCATED — first ${f.content.length} of ${f.chars_total ?? 'unknown'} chars; the rest is NOT shown)`
|
|
131
|
+
: '';
|
|
132
|
+
const label = `FILE: ${scrubUntrusted(String(f.file))}${truncNote}`;
|
|
124
133
|
lines.push(...fenceBlock(label, f.content));
|
|
125
134
|
lines.push('');
|
|
126
135
|
}
|
|
@@ -1166,22 +1166,59 @@ const CONTEXT_BUNDLE_MAX_FILE_CHARS = 16_000;
|
|
|
1166
1166
|
// Binary-ish extensions never worth inlining (the diff already names them).
|
|
1167
1167
|
const CONTEXT_BUNDLE_SKIP_RE = /\.(?:png|jpe?g|gif|webp|ico|svgz|woff2?|ttf|eot|zip|gz|tgz|tar|pdf|mp[34]|wav|ogg|bin|exe|dll|node)$/i;
|
|
1168
1168
|
|
|
1169
|
+
// Added lines per file, from the unified diff. Only lines long enough to be a
|
|
1170
|
+
// meaningful fingerprint (a bare `}` or `});` matches anywhere) — used to prove
|
|
1171
|
+
// a truncated bundle prefix actually SHOWS the change (task 1002905).
|
|
1172
|
+
const BUNDLE_EVIDENCE_MIN_CHARS = 12;
|
|
1173
|
+
function diffAddedLinesByFile(diff) {
|
|
1174
|
+
const byFile = new Map();
|
|
1175
|
+
let cur = null;
|
|
1176
|
+
let prevWasOldHeader = false;
|
|
1177
|
+
for (const line of String(diff || '').split('\n')) {
|
|
1178
|
+
// A '+++ ' header only counts right after its '--- ' partner: a content line
|
|
1179
|
+
// reading '++ x' arrives as '+++ x' and must not be mistaken for a header.
|
|
1180
|
+
if (prevWasOldHeader && line.startsWith('+++ ')) {
|
|
1181
|
+
prevWasOldHeader = false;
|
|
1182
|
+
const p = line.slice(4).trim().replace(/^b\//, '');
|
|
1183
|
+
cur = p === '/dev/null' ? null : p;
|
|
1184
|
+
continue;
|
|
1185
|
+
}
|
|
1186
|
+
prevWasOldHeader = line.startsWith('--- ');
|
|
1187
|
+
if (prevWasOldHeader || line.startsWith('diff --git')) continue;
|
|
1188
|
+
if (!cur || !line.startsWith('+')) continue;
|
|
1189
|
+
const t = line.slice(1).trim();
|
|
1190
|
+
if (t.length < BUNDLE_EVIDENCE_MIN_CHARS) continue;
|
|
1191
|
+
if (!byFile.has(cur)) byFile.set(cur, []);
|
|
1192
|
+
byFile.get(cur).push(t);
|
|
1193
|
+
}
|
|
1194
|
+
return byFile;
|
|
1195
|
+
}
|
|
1196
|
+
|
|
1169
1197
|
// Pure-ish (fs reads only, injectable): returns { files: [{file, content,
|
|
1170
|
-
// truncated}], skipped: [file...], total_chars } for the post-change
|
|
1171
|
-
// of changedFiles under cwd. Deleted/unreadable files land in skipped —
|
|
1198
|
+
// truncated, chars_total}], skipped: [file...], total_chars } for the post-change
|
|
1199
|
+
// contents of changedFiles under cwd. Deleted/unreadable files land in skipped —
|
|
1172
1200
|
// their absence from the bundle is visible, never silent.
|
|
1173
1201
|
//
|
|
1174
|
-
// Security (panel hacker finding,
|
|
1202
|
+
// Security (panel hacker finding, task 1002660): changedFiles is builder-
|
|
1175
1203
|
// controlled and git commits symlinks, so a naive readFileSync would follow a
|
|
1176
1204
|
// committed link OUT of the repo (SSH keys, session tokens) and inject the
|
|
1177
1205
|
// target into worker prompts + the stored grade. lstat-refuse symlinks and
|
|
1178
1206
|
// non-files, and require the resolved path to stay under cwd (containment) —
|
|
1179
1207
|
// a refused entry lands in skipped, visible like every other exclusion.
|
|
1180
|
-
function buildContextBundle({ changedFiles, cwd, fsImpl = fs } = {}) {
|
|
1208
|
+
function buildContextBundle({ changedFiles, cwd, diff, fsImpl = fs } = {}) {
|
|
1181
1209
|
const files = [];
|
|
1182
1210
|
const skipped = [];
|
|
1183
1211
|
let total = 0;
|
|
1184
1212
|
const root = path.resolve(cwd || process.cwd());
|
|
1213
|
+
// task 1002905: the fabricated all-blocker FAIL. A file over the per-file cap
|
|
1214
|
+
// is truncated from the HEAD, so on a large file (ship.js: 250k chars) a change
|
|
1215
|
+
// at char 62k lands entirely outside the bundle — and the prompt presents what
|
|
1216
|
+
// IS bundled as "the post-change contents". A worker then searches the prefix,
|
|
1217
|
+
// doesn't find the new code, and reports it as absent (5 blockers, every claim
|
|
1218
|
+
// exactly the pre-change state). A prefix that contains none of the change is
|
|
1219
|
+
// worse than no bundle at all, so refuse it: skipped[] already tells the worker
|
|
1220
|
+
// to Read the file.
|
|
1221
|
+
const addedByFile = diffAddedLinesByFile(diff);
|
|
1185
1222
|
for (const rel of (Array.isArray(changedFiles) ? changedFiles : [])) {
|
|
1186
1223
|
if (files.length >= CONTEXT_BUNDLE_MAX_FILES || total >= CONTEXT_BUNDLE_MAX_TOTAL_CHARS) {
|
|
1187
1224
|
skipped.push(rel);
|
|
@@ -1196,14 +1233,19 @@ function buildContextBundle({ changedFiles, cwd, fsImpl = fs } = {}) {
|
|
|
1196
1233
|
if (!st.isFile() || st.isSymbolicLink()) { skipped.push(rel); continue; }
|
|
1197
1234
|
content = fsImpl.readFileSync(abs, 'utf8');
|
|
1198
1235
|
} catch (_) { skipped.push(rel); continue; } // deleted by the diff, or unreadable
|
|
1236
|
+
const charsTotal = content.length;
|
|
1199
1237
|
let truncated = false;
|
|
1200
1238
|
if (content.length > CONTEXT_BUNDLE_MAX_FILE_CHARS) {
|
|
1201
1239
|
content = content.slice(0, CONTEXT_BUNDLE_MAX_FILE_CHARS);
|
|
1202
1240
|
truncated = true;
|
|
1203
1241
|
}
|
|
1242
|
+
if (truncated) {
|
|
1243
|
+
const added = addedByFile.get(rel);
|
|
1244
|
+
if (added && added.length && !added.some((a) => content.includes(a))) { skipped.push(rel); continue; }
|
|
1245
|
+
}
|
|
1204
1246
|
if (total + content.length > CONTEXT_BUNDLE_MAX_TOTAL_CHARS) { skipped.push(rel); continue; }
|
|
1205
1247
|
total += content.length;
|
|
1206
|
-
files.push({ file: rel, content, truncated });
|
|
1248
|
+
files.push({ file: rel, content, truncated, chars_total: charsTotal });
|
|
1207
1249
|
}
|
|
1208
1250
|
return { files, skipped, total_chars: total };
|
|
1209
1251
|
}
|
|
@@ -1727,7 +1769,9 @@ async function gradeSubagent({ task, valueSummary, diff, changedFiles, diffStats
|
|
|
1727
1769
|
|
|
1728
1770
|
// task 1002660: one read of the changed files, shared by all four workers —
|
|
1729
1771
|
// kills the per-worker re-exploration that dominated panel cost.
|
|
1730
|
-
|
|
1772
|
+
// diff (task 1002905): lets the bundle prove a truncated prefix actually shows
|
|
1773
|
+
// the change instead of handing a worker a head fragment as "post-change".
|
|
1774
|
+
const contextBundle = buildContextBundle({ changedFiles, cwd: runOpts.cwd, diff });
|
|
1731
1775
|
// task 1002660: cache-wrapped spawns. Same prompt + model replays at $0
|
|
1732
1776
|
// (llm-cache, System 5) — the prompt embeds the diff + bundle, so the key
|
|
1733
1777
|
// needs no separate scope sha. Absent llm-cache → plain runSubagent.
|
|
@@ -1767,6 +1811,17 @@ async function gradeSubagent({ task, valueSummary, diff, changedFiles, diffStats
|
|
|
1767
1811
|
builder_model_assumed: builderModel || ASSUMED_BUILDER_MODEL,
|
|
1768
1812
|
grader_model: graderModel,
|
|
1769
1813
|
total_ms: totalMs,
|
|
1814
|
+
// task 1002905: what the panel could actually SEE, recorded on every grade.
|
|
1815
|
+
// Diagnosing the fabricated all-blocker FAIL cost a session because the grade
|
|
1816
|
+
// record said nothing about the read root or how much of each file the workers
|
|
1817
|
+
// were shown; a "the panel read the wrong tree" claim is now one field away.
|
|
1818
|
+
context_bundle: {
|
|
1819
|
+
read_root: path.resolve(runOpts.cwd || process.cwd()),
|
|
1820
|
+
bundled: contextBundle.files.map((f) => ({
|
|
1821
|
+
file: f.file, chars: f.content.length, chars_total: f.chars_total ?? null, truncated: f.truncated,
|
|
1822
|
+
})),
|
|
1823
|
+
skipped: contextBundle.skipped,
|
|
1824
|
+
},
|
|
1770
1825
|
});
|
|
1771
1826
|
}
|
|
1772
1827
|
|
|
@@ -1997,6 +2052,7 @@ module.exports = {
|
|
|
1997
2052
|
selectGraderModel,
|
|
1998
2053
|
workerModelFor, // task 1002660: per-worker tier override (cross-model-guarded)
|
|
1999
2054
|
buildContextBundle, // task 1002660: shared post-change file contents, read once per panel
|
|
2055
|
+
diffAddedLinesByFile, // task 1002905: added-line fingerprints proving a truncated prefix shows the change
|
|
2000
2056
|
validateAndScore,
|
|
2001
2057
|
aggregateShots,
|
|
2002
2058
|
runSubagent,
|
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
/* my-projects.css — page styles for the cross-project view (task 1002868).
|
|
2
|
+
Every color derives from the style.css tokens (pack-driven); this file owns
|
|
3
|
+
only the page's own layout — rows, the join form, the inline notes. */
|
|
4
|
+
|
|
5
|
+
.row {
|
|
6
|
+
display: flex;
|
|
7
|
+
align-items: center;
|
|
8
|
+
gap: 10px;
|
|
9
|
+
background: var(--surface);
|
|
10
|
+
border: 1px solid var(--border);
|
|
11
|
+
border-radius: var(--radius-sm);
|
|
12
|
+
padding: 10px 12px;
|
|
13
|
+
margin-bottom: 8px;
|
|
14
|
+
}
|
|
15
|
+
.row .meta { flex: 1; min-width: 0; }
|
|
16
|
+
.row .name { font-weight: 600; }
|
|
17
|
+
.row .origin { color: var(--accent-text); font-size: 12.5px; word-break: break-all; }
|
|
18
|
+
.row .owner-pill {
|
|
19
|
+
font-size: 11px;
|
|
20
|
+
background: var(--accent);
|
|
21
|
+
color: #fff;
|
|
22
|
+
border-radius: 999px;
|
|
23
|
+
padding: 2px 8px;
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
.empty { margin: 0; color: var(--ink-soft); font-size: 13.5px; }
|
|
27
|
+
|
|
28
|
+
.join { display: flex; gap: 8px; }
|
|
29
|
+
.join input {
|
|
30
|
+
flex: 1;
|
|
31
|
+
font: inherit;
|
|
32
|
+
font-size: 13px;
|
|
33
|
+
padding: 0 10px;
|
|
34
|
+
height: 30px;
|
|
35
|
+
border: 1px solid var(--border-strong);
|
|
36
|
+
border-radius: var(--radius-sm);
|
|
37
|
+
background: var(--surface);
|
|
38
|
+
color: var(--ink);
|
|
39
|
+
}
|
|
40
|
+
.note { margin: 10px 0 0; color: var(--ink-soft); font-size: 13px; min-height: 1.2em; }
|
|
@@ -3,76 +3,66 @@
|
|
|
3
3
|
<head>
|
|
4
4
|
<meta charset="utf-8">
|
|
5
5
|
<meta name="viewport" content="width=device-width, initial-scale=1">
|
|
6
|
-
<title>My Projects</title>
|
|
7
|
-
<
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
margin: 0; padding: 2.5rem 1.25rem 4rem; background: var(--bg); color: var(--ink);
|
|
15
|
-
font-family: "Quicksand", system-ui, -apple-system, "Segoe UI", sans-serif; line-height: 1.5;
|
|
16
|
-
}
|
|
17
|
-
.wrap { max-width: 820px; margin: 0 auto; }
|
|
18
|
-
h1 { font-size: 1.9rem; margin: 0 0 .3rem; }
|
|
19
|
-
.sub { color: var(--ink-soft); margin: 0 0 2rem; }
|
|
20
|
-
h2 { font-size: 1.1rem; margin: 2rem 0 .75rem; border-bottom: 1px solid var(--rule); padding-bottom: .35rem; }
|
|
21
|
-
.row {
|
|
22
|
-
display: flex; align-items: center; gap: .75rem; background: var(--card);
|
|
23
|
-
border: 1px solid var(--rule); border-radius: 12px; padding: .8rem 1rem; margin-bottom: .6rem;
|
|
24
|
-
}
|
|
25
|
-
.row .meta { flex: 1; min-width: 0; }
|
|
26
|
-
.row .name { font-weight: 600; }
|
|
27
|
-
.row .origin { color: var(--accent); font-size: .82rem; word-break: break-all; }
|
|
28
|
-
.row .owner-pill { font-size: .72rem; background: var(--accent); color: #fff; border-radius: 999px; padding: .1rem .55rem; }
|
|
29
|
-
button {
|
|
30
|
-
font: inherit; cursor: pointer; border: 1px solid var(--rule); background: #fff; color: var(--ink);
|
|
31
|
-
border-radius: 9px; padding: .4rem .8rem;
|
|
32
|
-
}
|
|
33
|
-
button:hover { border-color: var(--accent); color: var(--accent-deep); }
|
|
34
|
-
.join { display: flex; gap: .5rem; margin-top: .75rem; }
|
|
35
|
-
.join input { flex: 1; font: inherit; padding: .5rem .7rem; border: 1px solid var(--rule); border-radius: 9px; }
|
|
36
|
-
.empty { color: var(--ink-soft); }
|
|
37
|
-
.note { margin-top: .5rem; color: var(--ink-soft); font-size: .9rem; min-height: 1.2em; }
|
|
38
|
-
a.home { color: var(--accent); text-decoration: none; font-size: .9rem; }
|
|
39
|
-
@media (prefers-color-scheme: dark) {
|
|
40
|
-
:root { --bg: #1a1410; --card: #241c16; --ink: #f5ece3; --ink-soft: #b9a998; --rule: #4a3728; }
|
|
41
|
-
button { background: #241c16; }
|
|
42
|
-
.join input { background: #241c16; color: inherit; }
|
|
43
|
-
}
|
|
44
|
-
</style>
|
|
6
|
+
<title>My Projects — {{worldName}}</title>
|
|
7
|
+
<meta name="description" content="Your cross-project view: the projects you own and build on across {{worldName}}.">
|
|
8
|
+
<link rel="icon" href="data:image/svg+xml;utf8,<svg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 32 32'><text x='16' y='24' font-size='24' text-anchor='middle' font-family='sans-serif'>⌂</text></svg>">
|
|
9
|
+
<link rel="preconnect" href="https://fonts.googleapis.com">
|
|
10
|
+
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin>
|
|
11
|
+
<link href="https://fonts.googleapis.com/css2?family=Inter:wght@400;500;600;700&display=swap" rel="stylesheet">
|
|
12
|
+
<link rel="stylesheet" href="/builders/style.css?v=2026-07-02-shell1">
|
|
13
|
+
<link rel="stylesheet" href="/builders/my-projects.css">
|
|
45
14
|
</head>
|
|
46
|
-
<body>
|
|
47
|
-
<div class="wrap">
|
|
48
|
-
<a class="home" href="/builders">← Back to the hall</a>
|
|
49
|
-
<h1>My Projects</h1>
|
|
50
|
-
<p class="sub">Every project you build on across the platform.</p>
|
|
15
|
+
<body data-page="my-projects" data-page-title="My Projects">
|
|
51
16
|
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
17
|
+
<div class="app">
|
|
18
|
+
<aside class="app-sidebar" id="app-sidebar" aria-label="Primary"></aside>
|
|
19
|
+
<div class="app-scrim" id="app-scrim" hidden></div>
|
|
20
|
+
<div class="app-body">
|
|
21
|
+
<header class="app-topbar" id="app-topbar"></header>
|
|
22
|
+
<div class="app-content">
|
|
23
|
+
<div class="page-head">
|
|
24
|
+
<p class="page-head__sub">Every project you build on across the platform.</p>
|
|
25
|
+
</div>
|
|
56
26
|
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
27
|
+
<main>
|
|
28
|
+
<section class="scroll" aria-labelledby="owned-h">
|
|
29
|
+
<h2 id="owned-h" class="scroll__h">Projects you own</h2>
|
|
30
|
+
<div id="owned"><p class="empty">—</p></div>
|
|
31
|
+
</section>
|
|
61
32
|
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
33
|
+
<section class="scroll" id="invites-section" hidden aria-labelledby="invites-h">
|
|
34
|
+
<h2 id="invites-h" class="scroll__h">Invitations</h2>
|
|
35
|
+
<div id="invites"></div>
|
|
36
|
+
</section>
|
|
66
37
|
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
38
|
+
<section class="scroll" aria-labelledby="building-h">
|
|
39
|
+
<h2 id="building-h" class="scroll__h">Projects you build on</h2>
|
|
40
|
+
<div id="building"><p class="empty">—</p></div>
|
|
41
|
+
</section>
|
|
42
|
+
|
|
43
|
+
<section class="scroll" aria-labelledby="join-h">
|
|
44
|
+
<h2 id="join-h" class="scroll__h">Join a project</h2>
|
|
45
|
+
<div class="join">
|
|
46
|
+
<input id="join-origin" type="url" placeholder="https://project.cloudbongos.com" autocomplete="off">
|
|
47
|
+
<button id="join-btn" type="button" class="btn-ghost">Request to join</button>
|
|
48
|
+
</div>
|
|
49
|
+
<p id="join-note" class="note"></p>
|
|
50
|
+
</section>
|
|
51
|
+
</main>
|
|
52
|
+
|
|
53
|
+
<footer class="app-foot" id="app-foot"></footer>
|
|
54
|
+
</div>
|
|
75
55
|
</div>
|
|
76
|
-
|
|
56
|
+
</div>
|
|
57
|
+
|
|
58
|
+
<div id="toast" class="toast" role="status" aria-live="polite" data-visible="0"></div>
|
|
59
|
+
|
|
60
|
+
<!-- The app shell (sidebar + top bar). Dependency-free; loaded FIRST so the
|
|
61
|
+
chrome exists before the page script runs. -->
|
|
62
|
+
<script src="/builders/shell.js?v=2026-07-02-shell1"></script>
|
|
63
|
+
<!-- jump palette (Ctrl/Cmd-K). Optional by design: without it the shell's slot stays hidden. -->
|
|
64
|
+
<script src="/builders/palette.js" defer></script>
|
|
65
|
+
<script src="/builders/brand-apply.js"></script>
|
|
66
|
+
<script src="/builders/my-projects.js"></script>
|
|
77
67
|
</body>
|
|
78
68
|
</html>
|
|
@@ -46,7 +46,7 @@
|
|
|
46
46
|
row.appendChild(pill);
|
|
47
47
|
}
|
|
48
48
|
if (opts.leavable) {
|
|
49
|
-
var btn = el('button');
|
|
49
|
+
var btn = el('button', 'btn-ghost');
|
|
50
50
|
btn.type = 'button';
|
|
51
51
|
btn.textContent = 'Leave';
|
|
52
52
|
btn.addEventListener('click', function () { leave(m.client_id, row, btn); });
|
|
@@ -91,8 +91,8 @@
|
|
|
91
91
|
var host = safeOriginHost(inv.origin);
|
|
92
92
|
if (host) { var o = el('div', 'origin'); o.textContent = host; meta.appendChild(o); }
|
|
93
93
|
row.appendChild(meta);
|
|
94
|
-
var accept = el('button'); accept.type = 'button'; accept.textContent = 'Accept';
|
|
95
|
-
var decline = el('button'); decline.type = 'button'; decline.textContent = 'Decline';
|
|
94
|
+
var accept = el('button', 'btn-ghost'); accept.type = 'button'; accept.textContent = 'Accept';
|
|
95
|
+
var decline = el('button', 'btn-ghost'); decline.type = 'button'; decline.textContent = 'Decline';
|
|
96
96
|
accept.addEventListener('click', function () { resolveInvite(inv.client_id, true, row, [accept, decline]); });
|
|
97
97
|
decline.addEventListener('click', function () { resolveInvite(inv.client_id, false, row, [accept, decline]); });
|
|
98
98
|
row.appendChild(accept); row.appendChild(decline);
|
|
@@ -3,43 +3,49 @@
|
|
|
3
3
|
<head>
|
|
4
4
|
<meta charset="utf-8">
|
|
5
5
|
<meta name="viewport" content="width=device-width, initial-scale=1">
|
|
6
|
-
<title>Scouting —
|
|
6
|
+
<title>Scouting — {{worldName}}</title>
|
|
7
|
+
<meta name="description" content="Cross-project builder activity — an internal talent view for project curators.">
|
|
8
|
+
<link rel="icon" href="data:image/svg+xml;utf8,<svg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 32 32'><text x='16' y='24' font-size='24' text-anchor='middle' font-family='sans-serif'>⌂</text></svg>">
|
|
9
|
+
<link rel="preconnect" href="https://fonts.googleapis.com">
|
|
10
|
+
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin>
|
|
11
|
+
<link href="https://fonts.googleapis.com/css2?family=Inter:wght@400;500;600;700&display=swap" rel="stylesheet">
|
|
12
|
+
<link rel="stylesheet" href="/builders/style.css?v=2026-07-02-shell1">
|
|
7
13
|
<style>
|
|
8
|
-
:
|
|
9
|
-
--bg: #fff7ed; --card: #ffffff; --ink: #2a1f17; --ink-soft: #6b5d52;
|
|
10
|
-
--rule: #fed7aa; --accent: #ea580c;
|
|
11
|
-
}
|
|
12
|
-
* { box-sizing: border-box; }
|
|
13
|
-
body {
|
|
14
|
-
margin: 0; padding: 2.5rem 1.25rem 4rem; background: var(--bg); color: var(--ink);
|
|
15
|
-
font-family: "Quicksand", system-ui, -apple-system, "Segoe UI", sans-serif; line-height: 1.5;
|
|
16
|
-
}
|
|
17
|
-
.wrap { max-width: 960px; margin: 0 auto; }
|
|
18
|
-
a.home { color: var(--accent); text-decoration: none; font-size: .9rem; }
|
|
19
|
-
h1 { font-size: 1.9rem; margin: .4rem 0 .3rem; }
|
|
20
|
-
.sub { color: var(--ink-soft); margin: 0 0 1.75rem; }
|
|
21
|
-
.badge { display: inline-block; font-size: .7rem; text-transform: uppercase; letter-spacing: .06em;
|
|
22
|
-
background: var(--accent); color: #fff; border-radius: 999px; padding: .12rem .6rem; vertical-align: middle; }
|
|
23
|
-
table { width: 100%; border-collapse: collapse; background: var(--card); border: 1px solid var(--rule); border-radius: 12px; overflow: hidden; }
|
|
24
|
-
th, td { text-align: left; padding: .6rem .8rem; border-bottom: 1px solid var(--rule); font-size: .92rem; }
|
|
25
|
-
th { color: var(--ink-soft); font-weight: 600; }
|
|
26
|
-
td.num, th.num { text-align: right; font-variant-numeric: tabular-nums; }
|
|
27
|
-
tr:last-child td { border-bottom: none; }
|
|
28
|
-
.who { font-weight: 600; }
|
|
29
|
-
.note { color: var(--ink-soft); padding: 2.5rem 1rem; text-align: center; }
|
|
30
|
-
.scroll { overflow-x: auto; }
|
|
31
|
-
@media (prefers-color-scheme: dark) {
|
|
32
|
-
:root { --bg: #1a1410; --card: #241c16; --ink: #f5ece3; --ink-soft: #b9a998; --rule: #4a3728; }
|
|
33
|
-
}
|
|
14
|
+
#body .who { font-weight: 600; }
|
|
34
15
|
</style>
|
|
35
16
|
</head>
|
|
36
|
-
<body>
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
17
|
+
<body data-page="scouting" data-page-title="Scouting">
|
|
18
|
+
|
|
19
|
+
<div class="app">
|
|
20
|
+
<aside class="app-sidebar" id="app-sidebar" aria-label="Primary"></aside>
|
|
21
|
+
<div class="app-scrim" id="app-scrim" hidden></div>
|
|
22
|
+
<div class="app-body">
|
|
23
|
+
<header class="app-topbar" id="app-topbar"></header>
|
|
24
|
+
<div class="app-content">
|
|
25
|
+
<div class="page-head">
|
|
26
|
+
<p class="page-head__sub">Builders active across all hosted projects — an internal talent view. Descriptive only; it grants no authority anywhere.</p>
|
|
27
|
+
</div>
|
|
28
|
+
|
|
29
|
+
<main>
|
|
30
|
+
<section class="scroll" aria-labelledby="scouting-h">
|
|
31
|
+
<h2 id="scouting-h" class="scroll__h">Cross-project builders</h2>
|
|
32
|
+
<div id="body" class="profile__placeholder">Loading…</div>
|
|
33
|
+
</section>
|
|
34
|
+
</main>
|
|
35
|
+
|
|
36
|
+
<footer class="app-foot" id="app-foot"></footer>
|
|
37
|
+
</div>
|
|
42
38
|
</div>
|
|
43
|
-
|
|
39
|
+
</div>
|
|
40
|
+
|
|
41
|
+
<div id="toast" class="toast" role="status" aria-live="polite" data-visible="0"></div>
|
|
42
|
+
|
|
43
|
+
<!-- The app shell (sidebar + top bar). Dependency-free; loaded FIRST so the
|
|
44
|
+
chrome exists before the page script runs. -->
|
|
45
|
+
<script src="/builders/shell.js?v=2026-07-02-shell1"></script>
|
|
46
|
+
<!-- jump palette (Ctrl/Cmd-K). Optional by design: without it the shell's slot stays hidden. -->
|
|
47
|
+
<script src="/builders/palette.js" defer></script>
|
|
48
|
+
<script src="/builders/brand-apply.js"></script>
|
|
49
|
+
<script src="/builders/scouting.js"></script>
|
|
44
50
|
</body>
|
|
45
51
|
</html>
|
|
@@ -25,15 +25,16 @@
|
|
|
25
25
|
body.className = '';
|
|
26
26
|
body.textContent = '';
|
|
27
27
|
if (!builders || builders.length === 0) {
|
|
28
|
-
body.className = '
|
|
28
|
+
body.className = 'profile__placeholder';
|
|
29
29
|
body.textContent = 'No cross-project activity reported yet.';
|
|
30
30
|
return;
|
|
31
31
|
}
|
|
32
|
-
|
|
33
|
-
|
|
32
|
+
// table.ledger, appended straight into #body: the design system's table
|
|
33
|
+
// styling, and #body becomes the :has(> .ledger) phone scroll strip.
|
|
34
|
+
var table = el('table', 'ledger');
|
|
34
35
|
var thead = el('thead');
|
|
35
36
|
var hr = el('tr');
|
|
36
|
-
[['Builder', ''], ['Projects', '
|
|
37
|
+
[['Builder', ''], ['Projects', 'ledger__num'], ['Credits', 'ledger__num'], ['Ships', 'ledger__num'], ['Karma', 'ledger__num'], ['Disciplines', ''], ['Last active', '']]
|
|
37
38
|
.forEach(function (h) { var th = el('th', h[1]); th.textContent = h[0]; hr.appendChild(th); });
|
|
38
39
|
thead.appendChild(hr); table.appendChild(thead);
|
|
39
40
|
var tbody = el('tbody');
|
|
@@ -44,17 +45,16 @@
|
|
|
44
45
|
name.textContent = b.github_login || b.display_name || ('#' + b.github_id);
|
|
45
46
|
who.appendChild(name);
|
|
46
47
|
tr.appendChild(who);
|
|
47
|
-
tr.appendChild(td(String(b.project_count != null ? b.project_count : '—'), '
|
|
48
|
-
tr.appendChild(td(String(b.total_credits != null ? b.total_credits : 0), '
|
|
49
|
-
tr.appendChild(td(String(b.total_tasks_shipped != null ? b.total_tasks_shipped : 0), '
|
|
50
|
-
tr.appendChild(td(String(b.total_karma != null ? b.total_karma : 0), '
|
|
48
|
+
tr.appendChild(td(String(b.project_count != null ? b.project_count : '—'), 'ledger__num'));
|
|
49
|
+
tr.appendChild(td(String(b.total_credits != null ? b.total_credits : 0), 'ledger__num'));
|
|
50
|
+
tr.appendChild(td(String(b.total_tasks_shipped != null ? b.total_tasks_shipped : 0), 'ledger__num'));
|
|
51
|
+
tr.appendChild(td(String(b.total_karma != null ? b.total_karma : 0), 'ledger__num'));
|
|
51
52
|
tr.appendChild(td(Array.isArray(b.disciplines) ? b.disciplines.join(', ') : '—'));
|
|
52
53
|
tr.appendChild(td(fmtRecency(b.last_active)));
|
|
53
54
|
tbody.appendChild(tr);
|
|
54
55
|
});
|
|
55
56
|
table.appendChild(tbody);
|
|
56
|
-
|
|
57
|
-
body.appendChild(scroll);
|
|
57
|
+
body.appendChild(table);
|
|
58
58
|
}
|
|
59
59
|
|
|
60
60
|
fetch('/api/gds/scouting', { headers: { Accept: 'application/json' } })
|
|
@@ -65,7 +65,7 @@
|
|
|
65
65
|
})
|
|
66
66
|
.then(function (data) { render(data && data.builders); })
|
|
67
67
|
.catch(function (e) {
|
|
68
|
-
body.className = '
|
|
68
|
+
body.className = 'profile__placeholder';
|
|
69
69
|
body.textContent = (e && e.archon)
|
|
70
70
|
? 'This view is Archon-only.'
|
|
71
71
|
: 'Couldn’t load the scouting view right now.';
|
package/package-lock.json
CHANGED
|
@@ -1,12 +1,12 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@bongos/core",
|
|
3
|
-
"version": "1.19.
|
|
3
|
+
"version": "1.19.136",
|
|
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.136",
|
|
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.136",
|
|
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
|
@@ -695,8 +695,12 @@ const { validateOrRespond, LIMITS, parseId, asyncHandler, corsPublicGet, parsePa
|
|
|
695
695
|
// landed since 1.19.132 with no explicit bump. run 31862484011. (task 1002620)
|
|
696
696
|
// 1.19.134 — CI auto-patch (publish-on-merge, ADR 0161): carrier for merges
|
|
697
697
|
// landed since 1.19.133 with no explicit bump. run 31862749526. (task 1002620)
|
|
698
|
+
// 1.19.135 — CI auto-patch (publish-on-merge, ADR 0161): carrier for merges
|
|
699
|
+
// landed since 1.19.134 with no explicit bump. run 31863235867. (task 1002620)
|
|
700
|
+
// 1.19.136 — CI auto-patch (publish-on-merge, ADR 0161): carrier for merges
|
|
701
|
+
// landed since 1.19.135 with no explicit bump. run 31889474899. (task 1002620)
|
|
698
702
|
// ---------------------------------------------------------------------------
|
|
699
|
-
const CORE_VERSION = '1.19.
|
|
703
|
+
const CORE_VERSION = '1.19.136'; // CI auto-patch carrier (ADR 0161); see changelog above
|
|
700
704
|
|
|
701
705
|
// A namespaced logger so a module's log lines are attributable + consistent.
|
|
702
706
|
// Usage: const log = api.logger('dev-box'); log.info('mounted');
|
package/tests/grader.mjs
CHANGED
|
@@ -908,6 +908,74 @@ test('buildContextBundle truncates oversized files and marks them', () => {
|
|
|
908
908
|
assert.ok(bundle.files[0].content.length <= 16_000);
|
|
909
909
|
});
|
|
910
910
|
|
|
911
|
+
// task 1002905 — the fabricated all-blocker FAIL.
|
|
912
|
+
//
|
|
913
|
+
// Task 1001916 shipped a 2-file diff from a feature worktree. ship.js is
|
|
914
|
+
// 250,432 chars; the change (a new helper, its call site, its export) sits at
|
|
915
|
+
// char ~62,000 — far past the 16,000-char per-file bundle cap. The bundle
|
|
916
|
+
// therefore carried ONLY lines 1-265 of ship.js, and the prompt told the worker
|
|
917
|
+
// to Read only files NOT bundled. The Quality worker searched the prefix, did
|
|
918
|
+
// not find the new symbols, and returned 5 blockers asserting the change "does
|
|
919
|
+
// not exist" — every claim exactly the pre-change state. (Three sibling workers
|
|
920
|
+
// in the SAME process with the SAME cwd read the real files and passed, which
|
|
921
|
+
// rules out the spawn cwd.) A truncated prefix that contains none of the change
|
|
922
|
+
// must never be presented as "the post-change contents".
|
|
923
|
+
test('buildContextBundle REFUSES a truncated prefix that shows none of the change (task 1002905)', () => {
|
|
924
|
+
const head = '// preamble\n'.repeat(2_000); // ~24k chars, all before the change
|
|
925
|
+
const big = `${head}function formatPublishFailure(data, status, branch) {\n return 'x';\n}\n`;
|
|
926
|
+
const diff = [
|
|
927
|
+
'diff --git a/scripts/gds/ship.js b/scripts/gds/ship.js',
|
|
928
|
+
'--- a/scripts/gds/ship.js',
|
|
929
|
+
'+++ b/scripts/gds/ship.js',
|
|
930
|
+
'@@ -1226,0 +1226,3 @@',
|
|
931
|
+
'+function formatPublishFailure(data, status, branch) {',
|
|
932
|
+
"+ return 'x';",
|
|
933
|
+
'+}',
|
|
934
|
+
].join('\n');
|
|
935
|
+
const bundle = grader.buildContextBundle({
|
|
936
|
+
changedFiles: ['scripts/gds/ship.js'], cwd: '/r', diff,
|
|
937
|
+
fsImpl: { lstatSync: () => plainFileStat, readFileSync: () => big },
|
|
938
|
+
});
|
|
939
|
+
assert.deepEqual(bundle.files, [], 'a prefix with none of the change is not bundled');
|
|
940
|
+
assert.deepEqual(bundle.skipped, ['scripts/gds/ship.js'], 'it lands in skipped, where the prompt tells the worker to Read it');
|
|
941
|
+
});
|
|
942
|
+
|
|
943
|
+
test('buildContextBundle still bundles a truncated file when the change IS visible in the prefix (task 1002905)', () => {
|
|
944
|
+
const big = `const NEW_HELPER_MARKER = 1;\n${'// filler\n'.repeat(4_000)}`;
|
|
945
|
+
const diff = [
|
|
946
|
+
'--- a/src/a.js',
|
|
947
|
+
'+++ b/src/a.js',
|
|
948
|
+
'@@ -1,0 +1,1 @@',
|
|
949
|
+
'+const NEW_HELPER_MARKER = 1;',
|
|
950
|
+
].join('\n');
|
|
951
|
+
const bundle = grader.buildContextBundle({
|
|
952
|
+
changedFiles: ['src/a.js'], cwd: '/r', diff,
|
|
953
|
+
fsImpl: { lstatSync: () => plainFileStat, readFileSync: () => big },
|
|
954
|
+
});
|
|
955
|
+
assert.equal(bundle.files.length, 1, 'the cost saving survives when the prefix carries the change');
|
|
956
|
+
assert.equal(bundle.files[0].truncated, true);
|
|
957
|
+
assert.equal(bundle.files[0].chars_total, big.length, 'the full size rides along so the prompt can state the extent');
|
|
958
|
+
});
|
|
959
|
+
|
|
960
|
+
test('worker prompts mark a TRUNCATED block and exempt it from the do-not-Read rule (task 1002905)', () => {
|
|
961
|
+
const prompt = grader.WORKERS.quality.buildPrompt({
|
|
962
|
+
task: { id: 1, title: 't', description: 'd' },
|
|
963
|
+
valueSummary: 'v',
|
|
964
|
+
diff: 'diff --git a/x b/x',
|
|
965
|
+
changedFiles: ['src/big.js'],
|
|
966
|
+
contextBundle: {
|
|
967
|
+
files: [{ file: 'src/big.js', content: 'const a = 1;', truncated: true, chars_total: 250_432 }],
|
|
968
|
+
skipped: [],
|
|
969
|
+
total_chars: 12,
|
|
970
|
+
},
|
|
971
|
+
});
|
|
972
|
+
assert.ok(/FILE: src\/big\.js \(TRUNCATED/.test(prompt), 'the label says TRUNCATED, not a quiet "(truncated)"');
|
|
973
|
+
assert.ok(prompt.includes('250432'), 'the label states how much of the file is missing');
|
|
974
|
+
assert.ok(/TRUNCATED/.test(prompt.split('Post-change file contents')[1].split('```')[0]),
|
|
975
|
+
'the lead instruction names truncated blocks as a Read exemption');
|
|
976
|
+
assert.ok(/never report code as missing/i.test(prompt), 'absence claims from a truncated block are forbidden');
|
|
977
|
+
});
|
|
978
|
+
|
|
911
979
|
test('buildContextBundle REFUSES symlinks — a committed link cannot pull out-of-repo content into prompts (hacker finding)', () => {
|
|
912
980
|
const files = { 'src/a.js': 'ok', 'evil-link.js': 'should never be read' };
|
|
913
981
|
const bundle = grader.buildContextBundle({
|
package/tests/hall_audit.mjs
CHANGED
|
@@ -30,14 +30,8 @@ const pages = () => fs.readdirSync(HALL).filter((f) => f.endsWith('.html'));
|
|
|
30
30
|
// reader whether promoting it would actually fix anything.
|
|
31
31
|
const INLINE_STYLE_BUDGET = {
|
|
32
32
|
'task.html': 27, // the task-751 detail-page block, deliberate
|
|
33
|
-
// my-projects + scouting
|
|
34
|
-
//
|
|
35
|
-
// it IS their whole stylesheet, down to a hardcoded palette. Moving those
|
|
36
|
-
// bytes into a .css file would change nothing that matters — what they
|
|
37
|
-
// actually need is adoption into the shell + the branding contract, which is
|
|
38
|
-
// a far larger change than a CSS relocation.
|
|
39
|
-
'my-projects.html': 36,
|
|
40
|
-
'scouting.html': 26,
|
|
33
|
+
// (my-projects + scouting were carried here as standalone surfaces until task
|
|
34
|
+
// 1002868 adopted them into the shell; their exceptions are gone with them.)
|
|
41
35
|
// Genuinely page-local, and already token-derived. One line over the default;
|
|
42
36
|
// a whole stylesheet for the legend strip is not worth the extra request, and
|
|
43
37
|
// diagram-viewer.css is shared with primer.html so it is the wrong home.
|
|
@@ -221,3 +215,43 @@ test('the module doc still records where the 2179 density pass landed', () => {
|
|
|
221
215
|
assert.match(doc, /97cbef0/, 'names the commit that actually shipped part 1');
|
|
222
216
|
assert.match(doc, /--space-1/, 'and an anchor a reader can go verify it against');
|
|
223
217
|
});
|
|
218
|
+
|
|
219
|
+
// ---- 8. every page rides the shell + the design system (task 1002868) -----
|
|
220
|
+
|
|
221
|
+
// my-projects + scouting were the last two standalone surfaces: no style.css,
|
|
222
|
+
// no shell.js, a private hardcoded palette that silently kept the WCAG-failing
|
|
223
|
+
// accent when the pack moved (idea 1000600). Task 1002868 adopted them, so the
|
|
224
|
+
// rule is now universal and exception-free.
|
|
225
|
+
test('every hall page loads style.css and shell.js', () => {
|
|
226
|
+
const missing = [];
|
|
227
|
+
for (const f of pages()) {
|
|
228
|
+
const html = read(f);
|
|
229
|
+
if (!/<link[^>]+href="[^"]*\bstyle\.css[^"]*"/.test(html)) missing.push(`${f}: no style.css link`);
|
|
230
|
+
if (!/<script[^>]+src="[^"]*\bshell\.js[^"]*"/.test(html)) missing.push(`${f}: no shell.js`);
|
|
231
|
+
}
|
|
232
|
+
assert.deepEqual(missing, [], 'standalone hall surfaces are gone (task 1002868) — adopt the shell, do not fork it');
|
|
233
|
+
});
|
|
234
|
+
|
|
235
|
+
// A page that re-declares a pack color var carries a private palette: it keeps
|
|
236
|
+
// rendering its baked-in colors however the instance pack (or the neutral
|
|
237
|
+
// defaults) move. Definitions only — `var(--accent)` USAGE is the design system
|
|
238
|
+
// working as intended. The 13 names are style.css's theming contract (§3 above
|
|
239
|
+
// pins the same list on the defining side).
|
|
240
|
+
const PACK_COLOR_VARS = [
|
|
241
|
+
'bg', 'bg-card', 'bg-deep', 'bg-frame', 'ink', 'ink-soft', 'ink-faint',
|
|
242
|
+
'rule', 'rule-soft', 'accent', 'accent-deep', 'sea', 'terracotta',
|
|
243
|
+
];
|
|
244
|
+
|
|
245
|
+
test('no page declares a private copy of a pack color var', () => {
|
|
246
|
+
const offenders = [];
|
|
247
|
+
for (const f of pages()) {
|
|
248
|
+
for (const m of read(f).matchAll(/<style[^>]*>([\s\S]*?)<\/style>/g)) {
|
|
249
|
+
for (const v of PACK_COLOR_VARS) {
|
|
250
|
+
// exact-name declaration: `--accent :` never matches `--accent-deep:`
|
|
251
|
+
// (next char is `-`) nor `var(--accent)` (next char is `)`).
|
|
252
|
+
if (new RegExp(`--${v}\\s*:`).test(m[1])) offenders.push(`${f}: defines --${v}`);
|
|
253
|
+
}
|
|
254
|
+
}
|
|
255
|
+
}
|
|
256
|
+
assert.deepEqual(offenders, [], 'palette comes from style.css + the instance pack (themeMarkup), never from a page');
|
|
257
|
+
});
|