@bongos/core 1.19.486 → 1.19.488
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 +39 -34
- package/docs/api/openapi.json +1 -1
- package/docs/copy-inventory.md +55 -55
- package/docs/copy-registry.json +63 -63
- package/docs/module-api-changelog.md +4 -0
- package/modules/hall-ui/public/dom-utils.js +23 -1
- package/modules/hall-ui/public/hall-render.js +7 -8
- package/modules/hall-ui/public/settings.js +8 -7
- package/modules/lifecycle/db-claim-reads.js +16 -3
- package/modules/lifecycle/help-requests.js +3 -3
- package/modules/lifecycle/migrations/lifecycle_006_help_requests_ui_craft.sql +38 -0
- package/modules/lifecycle/ship-card.js +4 -1
- package/modules/onboarding/newcomer-restock.js +53 -22
- package/modules/ui-design/module.json +1 -1
- package/package-lock.json +2 -2
- package/package.json +1 -1
- package/scripts/gds/newcomer-floor.config.json +2 -1
- package/scripts/gds/newcomer-floor.js +7 -1
- package/scripts/gds/newcomer-floor.templates.json +43 -1
- package/scripts/gds/ship-grade-steps.js +23 -1
- package/scripts/gds/ship.js +4 -0
- package/src/bongos/db.js +3 -3
- package/src/bongos/routes/me.js +2 -1
- package/src/module-api.js +1 -1
- package/tests/grader_regrade_delta.mjs +57 -0
- package/tests/help_requests.mjs +21 -2
- package/tests/module_contributions.mjs +67 -0
- package/tests/modules.mjs +19 -1
- package/tests/newcomer_restock.mjs +76 -0
package/tests/modules.mjs
CHANGED
|
@@ -140,6 +140,21 @@ test('disciplines: vanilla offers engineer + ideator (core); art-pipeline adds a
|
|
|
140
140
|
assert.deepEqual(enabledDisciplines(m), ['engineer', 'ideator', 'artist']);
|
|
141
141
|
});
|
|
142
142
|
|
|
143
|
+
test('disciplines: ui-design contributes `ui` — the manifest key IS the unlock (task 1003487)', () => {
|
|
144
|
+
// enabledDisciplines() already merged contributes.disciplines; ui-design simply
|
|
145
|
+
// never declared any, so the craft existed in the DB CHECK, in discipline-modes
|
|
146
|
+
// and on tasks while staying unreachable from every surface a builder chooses with.
|
|
147
|
+
if (!EFFECTIVE['ui-design']) return; // only meaningful where the module is installed
|
|
148
|
+
assert.deepEqual(EFFECTIVE['ui-design'].contributes.disciplines, ['ui'],
|
|
149
|
+
'the module declares the craft it owns');
|
|
150
|
+
const on = resolveModules({ instance: { modules: { 'ui-design': true } }, registry: EFFECTIVE });
|
|
151
|
+
assert.ok(enabledDisciplines(on).includes('ui'),
|
|
152
|
+
'a builder can save it, because updateBuilderDisciplines validates against this very list');
|
|
153
|
+
// and it withdraws with the module, exactly as artist does with art-pipeline
|
|
154
|
+
const off = resolveModules({ instance: { modules: { 'ui-design': false } }, registry: EFFECTIVE });
|
|
155
|
+
assert.ok(!enabledDisciplines(off).includes('ui'), 'module off => craft withdrawn');
|
|
156
|
+
});
|
|
157
|
+
|
|
143
158
|
test('clientModules is a safe boolean projection covering every known module + disciplines', () => {
|
|
144
159
|
const c = clientModules(resolveModules({ instance: OTB, registry: SAMPLE_REG }));
|
|
145
160
|
for (const k of KNOWN_MODULES) assert.equal(typeof c.enabled[k], 'boolean');
|
|
@@ -180,5 +195,8 @@ test('a branded sibling (GDS_BRANDING_FILE set, no GDS_MODULES_FILE) defaults to
|
|
|
180
195
|
// A non-default brand must fall through to the neutral (all-off) module set.
|
|
181
196
|
const m = loadModules({ env: { GDS_BRANDING_FILE: 'config/branding.cloudbongos.json' } });
|
|
182
197
|
for (const k of KNOWN_MODULES) assert.equal(m[k], false, `${k} must be off for a branded sibling`);
|
|
183
|
-
|
|
198
|
+
// The two core crafts, plus 'ui' from ui-design — which is default-on for EVERY
|
|
199
|
+
// instance ("every instance has a face") and is not one of KNOWN_MODULES, so the
|
|
200
|
+
// all-off assertion above never reaches it (task 1003487).
|
|
201
|
+
assert.deepEqual(enabledDisciplines(m), ['engineer', 'ideator', 'ui']);
|
|
184
202
|
});
|
|
@@ -68,6 +68,82 @@ t('loadFloors: real config keeps floor >= 2 per discipline (a spare on every cla
|
|
|
68
68
|
}
|
|
69
69
|
});
|
|
70
70
|
|
|
71
|
+
// ---- the ui craft is stocked, so a ui-only newcomer is never starved (task 1003487) ----
|
|
72
|
+
t('ui: the craft is offered, has chores, and none of them needs a browser', () => {
|
|
73
|
+
assert.ok(r.validDisciplines().has('ui'),
|
|
74
|
+
'ui-design is default-on, so enabledDisciplines() offers ui and restockAll will stamp it');
|
|
75
|
+
assert.ok(r.templateDisciplines().has('ui'), 'ui has chores defined, so restockDiscipline accepts it');
|
|
76
|
+
const chores = r.loadTemplates().ui;
|
|
77
|
+
assert.ok(Array.isArray(chores) && chores.length >= 1, 'at least one ui chore exists');
|
|
78
|
+
for (const c of chores) {
|
|
79
|
+
assert.ok(c.template_id && c.version_id && c.title, `${c.template_id || '?'} has id/version/title`);
|
|
80
|
+
assert.equal(c.discipline, 'ui');
|
|
81
|
+
assert.ok(c.est_minutes <= 60, `${c.template_id} is inside the newcomer scope ceiling`);
|
|
82
|
+
// Xenos-safe: a design chore necessarily READS the branding pack, so the
|
|
83
|
+
// property that matters is that every one of them says, in its done_when,
|
|
84
|
+
// that the world and the pipeline core are not what gets edited.
|
|
85
|
+
const dw = (c.done_when || []).join(' ');
|
|
86
|
+
assert.ok(/NOT modified|were untouched|untouched/i.test(dw),
|
|
87
|
+
`${c.template_id} states what must stay untouched`);
|
|
88
|
+
assert.ok(/permission|infra|migration/i.test(dw),
|
|
89
|
+
`${c.template_id} names the protected core as off-limits`);
|
|
90
|
+
}
|
|
91
|
+
// The kit needs Chrome/Edge, which a newcomer's machine may not have — so at
|
|
92
|
+
// least one ui chore must be verifiable without a browser, or the craft starves
|
|
93
|
+
// exactly the people it was opened for.
|
|
94
|
+
assert.ok(chores.some((c) => !/kit\/render\.js/.test((c.done_when || []).join(' '))),
|
|
95
|
+
'a ui newcomer with no browser still has a chore they can finish');
|
|
96
|
+
});
|
|
97
|
+
|
|
98
|
+
t('ui: the floor config stocks the craft', () => {
|
|
99
|
+
assert.ok(r.loadFloors().ui >= 1, 'ui declares a standing pool, so the shelf refills');
|
|
100
|
+
});
|
|
101
|
+
|
|
102
|
+
// ---- one file read per operation (task 1003487 re-grade: the efficiency finding) ----
|
|
103
|
+
// The templates file was being re-read and re-parsed by every helper on the path —
|
|
104
|
+
// templateDisciplines() and loadTemplates() back to back per call, and once per
|
|
105
|
+
// craft inside restockAll. These pin the single-read contract so it cannot regress.
|
|
106
|
+
{
|
|
107
|
+
const nodefs = require('node:fs');
|
|
108
|
+
const realRead = nodefs.readFileSync;
|
|
109
|
+
let reads = 0;
|
|
110
|
+
nodefs.readFileSync = (p, ...rest) => {
|
|
111
|
+
if (String(p).includes('newcomer-floor.templates.json')) reads += 1;
|
|
112
|
+
return realRead(p, ...rest);
|
|
113
|
+
};
|
|
114
|
+
try {
|
|
115
|
+
reads = 0;
|
|
116
|
+
await r.restockForClaim({ source: 'not-a-chore', discipline: 'ui' });
|
|
117
|
+
assert.equal(reads, 0, 'a non-chore task is rejected before any file read');
|
|
118
|
+
|
|
119
|
+
reads = 0;
|
|
120
|
+
const byDisc = r.loadTemplates();
|
|
121
|
+
assert.equal(reads, 1, 'loadTemplates reads once');
|
|
122
|
+
r.templateDisciplines(byDisc);
|
|
123
|
+
assert.equal(reads, 1, 'templateDisciplines re-uses an already-loaded map');
|
|
124
|
+
await r.restockDiscipline('bogus-craft', byDisc);
|
|
125
|
+
assert.equal(reads, 1, 'restockDiscipline re-uses it too');
|
|
126
|
+
} finally {
|
|
127
|
+
nodefs.readFileSync = realRead;
|
|
128
|
+
}
|
|
129
|
+
passed += 1;
|
|
130
|
+
console.log(' PASS restock reads newcomer-floor.templates.json once per operation');
|
|
131
|
+
}
|
|
132
|
+
|
|
133
|
+
t('restockForClaim threads its loaded map into the no-match fallback', () => {
|
|
134
|
+
// The read-counting block above drives the helpers directly; this pins the real
|
|
135
|
+
// call chain it cannot reach without a database — the fallback branch taken when
|
|
136
|
+
// a chore's source_ref does not resolve to a known template.
|
|
137
|
+
const src = require('node:fs').readFileSync(
|
|
138
|
+
new URL('../modules/onboarding/newcomer-restock.js', import.meta.url), 'utf8');
|
|
139
|
+
const body = src.slice(src.indexOf('async function restockForClaim'),
|
|
140
|
+
src.indexOf('async function restockAll'));
|
|
141
|
+
assert.ok(/const byDiscipline = loadTemplates\(\);/.test(body), 'it loads the templates once');
|
|
142
|
+
assert.ok(!/loadTemplates\(\)\[/.test(body), 'and never re-indexes a second fresh load');
|
|
143
|
+
assert.ok(/restockDiscipline\(task\.discipline, byDiscipline\)/.test(body),
|
|
144
|
+
'the fallback re-uses that map rather than letting the default param re-read the file');
|
|
145
|
+
});
|
|
146
|
+
|
|
71
147
|
// ---- restockForClaim: skip path is DB-free (awaited) ----
|
|
72
148
|
// (returns an array of per-template results; the skip path is a 1-element array.)
|
|
73
149
|
assert.deepEqual(await r.restockForClaim(null), [{ skipped: true }]);
|