@bongos/core 1.19.867 → 1.19.869
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 +12 -12
- package/docs/module-api-changelog.md +4 -0
- package/package-lock.json +2 -2
- package/package.json +1 -1
- package/scripts/gds/claude-materialize.js +50 -7
- package/src/module-api.js +1 -1
- package/tests/claude_materialize.mjs +60 -0
- package/tests/reward_gate.mjs +14 -2
package/.bongos-core.json
CHANGED
|
@@ -2,11 +2,11 @@
|
|
|
2
2
|
"artifact": "bongos-core",
|
|
3
3
|
"manifest_schema": 1,
|
|
4
4
|
"generator": "scripts/gds/package-core.js",
|
|
5
|
-
"core_version": "1.19.
|
|
6
|
-
"core_contract": "1.19.
|
|
7
|
-
"source_commit": "
|
|
5
|
+
"core_version": "1.19.869",
|
|
6
|
+
"core_contract": "1.19.869",
|
|
7
|
+
"source_commit": "1c05e3dc3377bbcf20a40718645967ba72233f33",
|
|
8
8
|
"source_ref": "HEAD",
|
|
9
|
-
"built_at": "2026-09-18T18:
|
|
9
|
+
"built_at": "2026-09-18T18:53:22.567Z",
|
|
10
10
|
"redaction": {
|
|
11
11
|
"model": "docs-redacted+functional-verbatim",
|
|
12
12
|
"docs_redacted": 500,
|
|
@@ -17,7 +17,7 @@
|
|
|
17
17
|
"gate": "passed"
|
|
18
18
|
},
|
|
19
19
|
"file_count": 2812,
|
|
20
|
-
"tree_sha256": "
|
|
20
|
+
"tree_sha256": "95583ff799fe688c99189403a7dd43769bb9fcd6497b578ff5ddd7704fc3c15e",
|
|
21
21
|
"files": [
|
|
22
22
|
{
|
|
23
23
|
"path": ".claude/skills/ask-for-help/SKILL.md",
|
|
@@ -2602,7 +2602,7 @@
|
|
|
2602
2602
|
{
|
|
2603
2603
|
"path": "docs/module-api-changelog.md",
|
|
2604
2604
|
"mode": "0000644",
|
|
2605
|
-
"sha256": "
|
|
2605
|
+
"sha256": "76f1f5bc881e852c64c7b486e017ff4d1b12d28b341b1a5de39b652c1cf7f4d0"
|
|
2606
2606
|
},
|
|
2607
2607
|
{
|
|
2608
2608
|
"path": "docs/modules-contract.md",
|
|
@@ -7927,12 +7927,12 @@
|
|
|
7927
7927
|
{
|
|
7928
7928
|
"path": "package-lock.json",
|
|
7929
7929
|
"mode": "0000644",
|
|
7930
|
-
"sha256": "
|
|
7930
|
+
"sha256": "fae81f32534c62d0ed9a54fdd6a2671f4bcc1b4d16fc3ecb3dbfbb98b23e9fef"
|
|
7931
7931
|
},
|
|
7932
7932
|
{
|
|
7933
7933
|
"path": "package.json",
|
|
7934
7934
|
"mode": "0000644",
|
|
7935
|
-
"sha256": "
|
|
7935
|
+
"sha256": "4bb8ccfe89e010be6ddde0cb7a301dc02d876b953679f175e28d5ca87b3192c1"
|
|
7936
7936
|
},
|
|
7937
7937
|
{
|
|
7938
7938
|
"path": "public-docs/index.html",
|
|
@@ -8242,7 +8242,7 @@
|
|
|
8242
8242
|
{
|
|
8243
8243
|
"path": "scripts/gds/claude-materialize.js",
|
|
8244
8244
|
"mode": "0000644",
|
|
8245
|
-
"sha256": "
|
|
8245
|
+
"sha256": "30fa12a96df4e9eda7e81f5abe3886caa0fb7dd277e37c5f6388c9acc429950e"
|
|
8246
8246
|
},
|
|
8247
8247
|
{
|
|
8248
8248
|
"path": "scripts/gds/claude-md-template.js",
|
|
@@ -9882,7 +9882,7 @@
|
|
|
9882
9882
|
{
|
|
9883
9883
|
"path": "src/module-api.js",
|
|
9884
9884
|
"mode": "0000644",
|
|
9885
|
-
"sha256": "
|
|
9885
|
+
"sha256": "c67aa62fe4d84efbcdfd2658102ee5d2d929f9bb479d6e33e1acee5ddb84055e"
|
|
9886
9886
|
},
|
|
9887
9887
|
{
|
|
9888
9888
|
"path": "src/module-loader/catalog.js",
|
|
@@ -10617,7 +10617,7 @@
|
|
|
10617
10617
|
{
|
|
10618
10618
|
"path": "tests/claude_materialize.mjs",
|
|
10619
10619
|
"mode": "0000644",
|
|
10620
|
-
"sha256": "
|
|
10620
|
+
"sha256": "36c52641c98217d42c38853c5dd78d4d4eb51ee97005380318db5adb2aa71d28"
|
|
10621
10621
|
},
|
|
10622
10622
|
{
|
|
10623
10623
|
"path": "tests/clawback_confirmed_backlog.mjs",
|
|
@@ -13087,7 +13087,7 @@
|
|
|
13087
13087
|
{
|
|
13088
13088
|
"path": "tests/reward_gate.mjs",
|
|
13089
13089
|
"mode": "0000644",
|
|
13090
|
-
"sha256": "
|
|
13090
|
+
"sha256": "038940af4f863bb3efc096bf522351900fbcd393c0c117f3ac75fe3a0f05ffe7"
|
|
13091
13091
|
},
|
|
13092
13092
|
{
|
|
13093
13093
|
"path": "tests/reward_mode_cost_plus_only.mjs",
|
|
@@ -2208,5 +2208,9 @@ is load-bearing: the script throws rather than guess if it is missing, and
|
|
|
2208
2208
|
landed since 1.19.865 with no explicit bump. run 35355019469. (task 1002620)
|
|
2209
2209
|
1.19.867 — CI auto-patch (publish-on-merge, ADR 0161): carrier for merges
|
|
2210
2210
|
landed since 1.19.866 with no explicit bump. run 35380895321. (task 1002620)
|
|
2211
|
+
1.19.868 — CI auto-patch (publish-on-merge, ADR 0161): carrier for merges
|
|
2212
|
+
landed since 1.19.867 with no explicit bump. run 35381947323. (task 1002620)
|
|
2213
|
+
1.19.869 — CI auto-patch (publish-on-merge, ADR 0161): carrier for merges
|
|
2214
|
+
landed since 1.19.868 with no explicit bump. run 35382740500. (task 1002620)
|
|
2211
2215
|
---------------------------------------------------------------------------
|
|
2212
2216
|
```
|
package/package-lock.json
CHANGED
|
@@ -1,12 +1,12 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@bongos/core",
|
|
3
|
-
"version": "1.19.
|
|
3
|
+
"version": "1.19.869",
|
|
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.869",
|
|
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.869",
|
|
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",
|
|
@@ -389,10 +389,37 @@ function renderStarterSettings() {
|
|
|
389
389
|
}, null, 2)}\n`;
|
|
390
390
|
}
|
|
391
391
|
|
|
392
|
-
|
|
392
|
+
// readSettingsTransform — the transform, as a result rather than a string:
|
|
393
|
+
// { text, parsed, reason }. transformSettings below is the string face of it,
|
|
394
|
+
// kept because that is what the tests and the historical callers use.
|
|
395
|
+
//
|
|
396
|
+
// WHY A PARSE FAILURE IS COPIED THROUGH UNTOUCHED (task 1004023). The old
|
|
397
|
+
// fallback was `bongosify(raw)` — a best-effort regex rewrite over the whole
|
|
398
|
+
// file. The script-invocation regex ends in an optional quote so it can swallow the
|
|
399
|
+
// closing quote of a QUOTED PATH inside a command; run over raw JSON text it
|
|
400
|
+
// swallows the JSON string's own closing quote instead:
|
|
401
|
+
//
|
|
402
|
+
// bongosify('{"command": "node scripts/gds/strand-watch.js", }')
|
|
403
|
+
// -> {"command": "bongos exec scripts/gds/strand-watch.js, }
|
|
404
|
+
//
|
|
405
|
+
// So a file that was one trailing comma from valid was written to the instance
|
|
406
|
+
// MORE broken than it arrived, with its strings running into each other. A
|
|
407
|
+
// rewrite of text we could not parse is not worth a worse file: the raw bytes
|
|
408
|
+
// are copied through unchanged and the caller is handed a reason to report. The
|
|
409
|
+
// operator fixes one comma instead of unpicking mangled strings.
|
|
410
|
+
//
|
|
411
|
+
// The parse failure is NOT made fatal: the core's own settings.json is valid
|
|
412
|
+
// JSON (pinned by a test), so reaching this means the operator has edited
|
|
413
|
+
// theirs, and refusing the whole materialize over one file would be a worse
|
|
414
|
+
// trade than copying it and saying so.
|
|
415
|
+
function readSettingsTransform(srcPath) {
|
|
393
416
|
const raw = fs.readFileSync(srcPath, 'utf8');
|
|
394
417
|
let json;
|
|
395
|
-
try {
|
|
418
|
+
try {
|
|
419
|
+
json = JSON.parse(raw);
|
|
420
|
+
} catch (err) {
|
|
421
|
+
return { text: raw, parsed: false, reason: (err && err.message) || 'not valid JSON' };
|
|
422
|
+
}
|
|
396
423
|
const hooks = json && json.hooks;
|
|
397
424
|
if (hooks && typeof hooks === 'object') {
|
|
398
425
|
for (const event of Object.values(hooks)) {
|
|
@@ -416,7 +443,14 @@ function transformSettings(srcPath) {
|
|
|
416
443
|
perms[key] = [...new Set(rules.flatMap((r) => (typeof r === 'string' ? spellings(r) : [r])))];
|
|
417
444
|
}
|
|
418
445
|
}
|
|
419
|
-
return JSON.stringify(json, null, 2) + '\n';
|
|
446
|
+
return { text: JSON.stringify(json, null, 2) + '\n', parsed: true, reason: null };
|
|
447
|
+
}
|
|
448
|
+
|
|
449
|
+
// transformSettings — the string face of readSettingsTransform. A caller that
|
|
450
|
+
// only wants the bytes keeps working; one that needs to know the file could not
|
|
451
|
+
// be parsed calls readSettingsTransform and reads `parsed`.
|
|
452
|
+
function transformSettings(srcPath) {
|
|
453
|
+
return readSettingsTransform(srcPath).text;
|
|
420
454
|
}
|
|
421
455
|
|
|
422
456
|
// Materialize the .claude/ surface from `coreRoot` into `instanceDir`. Returns a summary.
|
|
@@ -426,7 +460,7 @@ function transformSettings(srcPath) {
|
|
|
426
460
|
const HOOK_TEXT_EXT = new Set(['.js', '.mjs', '.cjs', '.json', '.sh', '.md', '.txt']); // hooks are .js — rebrand them too
|
|
427
461
|
|
|
428
462
|
function materializeClaude({ coreRoot, instanceDir, dryRun = false, overwrite = true, log = () => {} }) {
|
|
429
|
-
const summary = { skipped: false, skills: 0, moduleSkills: 0, settings: false, hooks: 0, scheduledTasks: 0, skippedFiles: [], excludedSkills: [], withdrawnSkills: [], rebrandRules: 0, claudeMd: false, writtenPaths: [], notes: [] };
|
|
463
|
+
const summary = { skipped: false, skills: 0, moduleSkills: 0, settings: false, settingsUnparsed: false, hooks: 0, scheduledTasks: 0, skippedFiles: [], excludedSkills: [], withdrawnSkills: [], rebrandRules: 0, claudeMd: false, writtenPaths: [], notes: [] };
|
|
430
464
|
// Every instance-relative path this run WROTE or REMOVED (task 1003957). `bongos upgrade`
|
|
431
465
|
// hands these to persistPin as artifactPaths, exactly as task 1003926 already does for
|
|
432
466
|
// regenerateApiArtifacts' output: committing only the pin left the materialized files dirty,
|
|
@@ -607,13 +641,22 @@ function materializeClaude({ coreRoot, instanceDir, dryRun = false, overwrite =
|
|
|
607
641
|
summary.skippedFiles.push('settings.json');
|
|
608
642
|
log(' • .claude/settings.json already present — left untouched (adopt merges nothing here yet).');
|
|
609
643
|
} else {
|
|
644
|
+
// task 1004023: an unparseable settings.json is copied through BYTE FOR
|
|
645
|
+
// BYTE — no bongosify, no rebrand. Both are regex passes written for the
|
|
646
|
+
// strings inside a parsed file, and over raw JSON text they damage it.
|
|
647
|
+
// The instance gets what the core had, plus a note naming the parse error.
|
|
648
|
+
const settingsOut = readSettingsTransform(settingsSrc);
|
|
610
649
|
if (!dryRun) {
|
|
611
650
|
fs.mkdirSync(destClaude, { recursive: true });
|
|
612
|
-
fs.writeFileSync(settingsDest, rebrand(
|
|
651
|
+
fs.writeFileSync(settingsDest, settingsOut.parsed ? rebrand(settingsOut.text) : settingsOut.text);
|
|
613
652
|
}
|
|
614
653
|
addWritten('.claude/settings.json');
|
|
615
654
|
summary.settings = true;
|
|
616
|
-
|
|
655
|
+
if (!settingsOut.parsed) {
|
|
656
|
+
summary.settingsUnparsed = true;
|
|
657
|
+
summary.notes.push(`.claude/settings.json is not valid JSON (${settingsOut.reason}) — copied through UNCHANGED: no hook-command or permission-rule rewrite to bongos, and no identity rebrand. Fix the JSON at the core root and materialize again, or edit the instance copy by hand.`);
|
|
658
|
+
}
|
|
659
|
+
log(` ${dryRun ? '[dry-run] would write' : '✓ wrote'} .claude/settings.json ${settingsOut.parsed ? '(hook commands + permission rules→bongos, identity rebranded)' : '(UNCHANGED — not valid JSON, nothing rewritten)'}`);
|
|
617
660
|
}
|
|
618
661
|
} else {
|
|
619
662
|
// Package-only install: the core root carries no settings.json to copy, so
|
|
@@ -802,4 +845,4 @@ if (require.main === module) {
|
|
|
802
845
|
process.exit(s.refused.length ? 1 : 0);
|
|
803
846
|
}
|
|
804
847
|
|
|
805
|
-
module.exports = { scriptToVerb, bongosify, transformSettings, renderStarterSettings, STARTER_ALLOW, copyTreeTransformed, materializeClaude, materializeModuleSkillsIntoCore, isCoreCheckoutRoot, readModuleSkillsManifest, MODULE_SKILLS_MANIFEST, readInstanceSkillsManifest, INSTANCE_SKILLS_MANIFEST, GDS_INVOCATION_RE, buildRebrandRules, rebrandText, disabledModuleSkills, moduleSkillRoster, moduleSkillSources, vendoredSkillGate, SKILL_NAME_RE, VENDOR_DEFAULT_POLICY };
|
|
848
|
+
module.exports = { scriptToVerb, bongosify, transformSettings, readSettingsTransform, renderStarterSettings, STARTER_ALLOW, copyTreeTransformed, materializeClaude, materializeModuleSkillsIntoCore, isCoreCheckoutRoot, readModuleSkillsManifest, MODULE_SKILLS_MANIFEST, readInstanceSkillsManifest, INSTANCE_SKILLS_MANIFEST, GDS_INVOCATION_RE, buildRebrandRules, rebrandText, disabledModuleSkills, moduleSkillRoster, moduleSkillSources, vendoredSkillGate, SKILL_NAME_RE, VENDOR_DEFAULT_POLICY };
|
package/src/module-api.js
CHANGED
|
@@ -71,7 +71,7 @@ const { responsibilityFor, ROLE_RESPONSIBILITIES } = require('./role-responsibil
|
|
|
71
71
|
// there. scripts/gds/bump-version.js still rewrites the literal below; it appends
|
|
72
72
|
// the entry to that file. Look for a version's history there, not here.
|
|
73
73
|
// ---------------------------------------------------------------------------
|
|
74
|
-
const CORE_VERSION = '1.19.
|
|
74
|
+
const CORE_VERSION = '1.19.869'; // CI auto-patch carrier (ADR 0161); changelog: docs/module-api-changelog.md
|
|
75
75
|
|
|
76
76
|
// A namespaced logger so a module's log lines are attributable + consistent.
|
|
77
77
|
// Usage: const log = api.logger('dev-box'); log.info('mounted');
|
|
@@ -832,5 +832,65 @@ t("an instance's OWN settings.json is never clobbered", () => {
|
|
|
832
832
|
rmSync(inst, { recursive: true, force: true });
|
|
833
833
|
});
|
|
834
834
|
|
|
835
|
+
// ---- an unparseable settings.json is copied through, not mangled (task 1004023) ----
|
|
836
|
+
// THE DEFECT. The fallback was `bongosify(raw)` — the command-string rewrite run over
|
|
837
|
+
// the whole FILE. The script-invocation regex ends in an optional quote so it can swallow the
|
|
838
|
+
// closing quote of a quoted PATH; over raw JSON text it swallows the JSON string's own
|
|
839
|
+
// closing quote, and a file one trailing comma from valid was written to the instance
|
|
840
|
+
// with its strings running into each other. The load-bearing assertion is therefore the
|
|
841
|
+
// BYTE-EQUALITY one: the fix is not "rewrite it better", it is "do not rewrite what you
|
|
842
|
+
// could not parse".
|
|
843
|
+
const BROKEN_SETTINGS = '{"command": "node scripts/gds/strand-watch.js", }'; // one trailing comma from valid
|
|
844
|
+
|
|
845
|
+
t('readSettingsTransform: malformed JSON is reported, not rewritten', () => {
|
|
846
|
+
const dir = mkdtempSync(join(tmpdir(), 'w3-bad-'));
|
|
847
|
+
const p = join(dir, 'settings.json');
|
|
848
|
+
writeFileSync(p, BROKEN_SETTINGS);
|
|
849
|
+
const out = m.readSettingsTransform(p);
|
|
850
|
+
assert.equal(out.parsed, false, 'a trailing comma must be reported as unparseable');
|
|
851
|
+
assert.equal(out.text, BROKEN_SETTINGS, 'the bytes must come through untouched');
|
|
852
|
+
assert.ok(out.reason && out.reason.length > 0, 'the caller needs something to print');
|
|
853
|
+
// The specific corruption from the bug report must not be reachable any more.
|
|
854
|
+
assert.ok(!out.text.includes('bongos exec'), 'the command rewrite ran over text it could not parse');
|
|
855
|
+
assert.equal((out.text.match(/"/g) || []).length, (BROKEN_SETTINGS.match(/"/g) || []).length,
|
|
856
|
+
'a quote was eaten — this is the exact shape of the original defect');
|
|
857
|
+
rmSync(dir, { recursive: true, force: true });
|
|
858
|
+
});
|
|
859
|
+
|
|
860
|
+
t('readSettingsTransform: valid JSON still gets the full rewrite', () => {
|
|
861
|
+
const dir = mkdtempSync(join(tmpdir(), 'w3-good-'));
|
|
862
|
+
const p = join(dir, 'settings.json');
|
|
863
|
+
writeFileSync(p, JSON.stringify({ permissions: { allow: ['Bash(node scripts/gds/ship.js:*)'] } }, null, 2));
|
|
864
|
+
const out = m.readSettingsTransform(p);
|
|
865
|
+
assert.equal(out.parsed, true);
|
|
866
|
+
assert.ok(out.text.includes('bongos'), 'the parseable path must still rewrite to the bongos bin');
|
|
867
|
+
assert.equal(out.reason, null);
|
|
868
|
+
rmSync(dir, { recursive: true, force: true });
|
|
869
|
+
});
|
|
870
|
+
|
|
871
|
+
t('transformSettings keeps its string contract for existing callers', () => {
|
|
872
|
+
const dir = mkdtempSync(join(tmpdir(), 'w3-str-'));
|
|
873
|
+
const p = join(dir, 'settings.json');
|
|
874
|
+
writeFileSync(p, BROKEN_SETTINGS);
|
|
875
|
+
assert.equal(typeof m.transformSettings(p), 'string');
|
|
876
|
+
assert.equal(m.transformSettings(p), BROKEN_SETTINGS);
|
|
877
|
+
rmSync(dir, { recursive: true, force: true });
|
|
878
|
+
});
|
|
879
|
+
|
|
880
|
+
t('materializeClaude: a broken core settings.json reaches the instance unchanged, with a note', () => {
|
|
881
|
+
const core = mkdtempSync(join(tmpdir(), 'bad-core-'));
|
|
882
|
+
const inst = mkdtempSync(join(tmpdir(), 'bad-inst-'));
|
|
883
|
+
mkdirSync(join(core, '.claude'), { recursive: true });
|
|
884
|
+
writeFileSync(join(core, '.claude', 'settings.json'), BROKEN_SETTINGS);
|
|
885
|
+
const summary = m.materializeClaude({ coreRoot: core, instanceDir: inst, dryRun: false });
|
|
886
|
+
assert.equal(readFileSync(join(inst, '.claude', 'settings.json'), 'utf8'), BROKEN_SETTINGS,
|
|
887
|
+
'the instance must receive exactly what the core had, not a worse file');
|
|
888
|
+
assert.equal(summary.settingsUnparsed, true, 'the summary must record that nothing was rewritten');
|
|
889
|
+
assert.ok(summary.notes.some((n) => n.includes('not valid JSON')),
|
|
890
|
+
'a silent copy-through is how an operator never learns their hooks were not rewritten');
|
|
891
|
+
rmSync(core, { recursive: true, force: true });
|
|
892
|
+
rmSync(inst, { recursive: true, force: true });
|
|
893
|
+
});
|
|
894
|
+
|
|
835
895
|
console.log(`\nclaude_materialize: ${passed} passed, ${failed} failed`);
|
|
836
896
|
process.exit(failed ? 1 : 0);
|
package/tests/reward_gate.mjs
CHANGED
|
@@ -261,8 +261,16 @@ t('createTask logs a [creation] audit line on auto-assign', () => {
|
|
|
261
261
|
// rather than depending on a magic window size. A fixed 4800-char window
|
|
262
262
|
// silently broke when task 1763 added the goal_id fallback before the INSERT,
|
|
263
263
|
// sliding the [creation] audit line past its end.
|
|
264
|
+
// The `end > start ? end : <bytes>` ternary this used to carry defeated its own
|
|
265
|
+
// purpose: a renamed or reordered boundary symbol did not fail, it quietly
|
|
266
|
+
// reverted to exactly the magic window the anchor was introduced to remove,
|
|
267
|
+
// while the comment above still told the next reader the slice was anchored
|
|
268
|
+
// (task 1004016). Assert the boundary instead.
|
|
264
269
|
const end = dbSrc.indexOf('async function findTaskBySourceRef', start);
|
|
265
|
-
|
|
270
|
+
assert.ok(start >= 0, 'createTask not found in the lifecycle data layer');
|
|
271
|
+
assert.ok(end > start,
|
|
272
|
+
'findTaskBySourceRef boundary not found — re-anchor this slice to whatever function now follows createTask');
|
|
273
|
+
const body = dbSrc.slice(start, end);
|
|
266
274
|
assert.match(body, /suggestCreditsReward/, 'createTask must reuse the capped helper');
|
|
267
275
|
assert.match(body, /\[reward-gate\] AUTO-ASSIGN.*\[creation\]/s, 'createTask must log a [creation] audit line');
|
|
268
276
|
});
|
|
@@ -277,8 +285,12 @@ t('POST /tasks surfaces the assigned credits_reward in the 201 response', () =>
|
|
|
277
285
|
// `credits_reward: task.credits_reward` response line to offset 11027, 27 chars
|
|
278
286
|
// past the window — failing on main and reddening every PR's `unit` check
|
|
279
287
|
// (task 2026; supersedes the never-pushed task 1967, same brittleness).
|
|
288
|
+
// Same defect as the createTask sibling above, same fix (task 1004016): the
|
|
289
|
+
// fallback window made a lost boundary invisible.
|
|
280
290
|
const end = tasksRouteSrc.indexOf('router.', start + 'router.post'.length);
|
|
281
|
-
|
|
291
|
+
assert.ok(end > start,
|
|
292
|
+
'next router registration not found — re-anchor this slice to whatever follows the POST /tasks handler');
|
|
293
|
+
const body = tasksRouteSrc.slice(start, end);
|
|
282
294
|
assert.match(body, /credits_reward: task\.credits_reward/, 'response must surface the born-with reward');
|
|
283
295
|
});
|
|
284
296
|
|