@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 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.867",
6
- "core_contract": "1.19.867",
7
- "source_commit": "3aa05b2cc6a4d35e89f51b1d0f8b30f65821c28e",
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:34:21.175Z",
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": "e71de0cc075fabe7cf7ec76a180926220f46264e5c4a7a00dc81e6c3857fb318",
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": "6f8b541208105ca92349cd4af1706a3d9847b2aee2e2fd18a1f4eca67162aedc"
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": "da9e13ca079e6f4c0ff3f52a67503acdd25c85f36f51f9a9776227b504cdbb1b"
7930
+ "sha256": "fae81f32534c62d0ed9a54fdd6a2671f4bcc1b4d16fc3ecb3dbfbb98b23e9fef"
7931
7931
  },
7932
7932
  {
7933
7933
  "path": "package.json",
7934
7934
  "mode": "0000644",
7935
- "sha256": "98ed92aea9cb7b7152b4d24d7b304f3363ae7e2174614ac7ee416a132ea47a77"
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": "488a2a6c55c794625c47480def16c3b3fee81be4bd73d36a21f11a7feab741fc"
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": "54d261c7d782f4214c4d75d777690d43fdcb305c57b8161b018745e6284eb143"
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": "ac4c44006a151190a5ead99c7a32241cf95e21b3eca397ea7c0d4bf26b0b7de9"
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": "f1cb5f34e9ecbd072b56840facdc57ca2b9d2fa6efc48427ce738cc65a72011f"
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.867",
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.867",
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.867",
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
- function transformSettings(srcPath) {
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 { json = JSON.parse(raw); } catch { return bongosify(raw); } // malformed → best-effort text rewrite
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(transformSettings(settingsSrc)));
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
- log(` ${dryRun ? '[dry-run] would write' : '✓ wrote'} .claude/settings.json (hook commands + permission rules→bongos, identity rebranded)`);
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.867'; // CI auto-patch carrier (ADR 0161); changelog: docs/module-api-changelog.md
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);
@@ -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
- const body = dbSrc.slice(start, end > start ? end : start + 6000);
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
- const body = tasksRouteSrc.slice(start, end > start ? end : start + 13000);
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