@bongos/core 1.19.1001 → 1.19.1003

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.1001",
6
- "core_contract": "1.19.1001",
7
- "source_commit": "e3841cdcf855bc3c78235929cb9b991fe6771293",
5
+ "core_version": "1.19.1003",
6
+ "core_contract": "1.19.1003",
7
+ "source_commit": "145db63a197eedb2b567473f622e1b208dc0fe00",
8
8
  "source_ref": "HEAD",
9
- "built_at": "2026-09-24T14:59:23.930Z",
9
+ "built_at": "2026-09-24T15:36:25.098Z",
10
10
  "redaction": {
11
11
  "model": "docs-redacted+functional-verbatim",
12
12
  "docs_redacted": 521,
@@ -17,7 +17,7 @@
17
17
  "gate": "passed"
18
18
  },
19
19
  "file_count": 2957,
20
- "tree_sha256": "316e3f355b647effab194c60ce7bd7be3e67446b6c021d7bf3c163987ac4a2a6",
20
+ "tree_sha256": "30960b9b7c273ac36ba63d380f5b2c8f31e832f5f702f57215ca4e9c27f50db2",
21
21
  "files": [
22
22
  {
23
23
  "path": ".claude/skills/ask-for-help/SKILL.md",
@@ -2722,7 +2722,7 @@
2722
2722
  {
2723
2723
  "path": "docs/module-api-changelog.md",
2724
2724
  "mode": "0000644",
2725
- "sha256": "9c37bc89b41410876a2d54f99781ff465a2cd106fd3cd8ef6502bb097e832afe"
2725
+ "sha256": "bfcc1b1653c36c240cd5793aa5d483f7bd740c5681f2ed0600f01217c5d8937d"
2726
2726
  },
2727
2727
  {
2728
2728
  "path": "docs/modules-contract.md",
@@ -8207,12 +8207,12 @@
8207
8207
  {
8208
8208
  "path": "package-lock.json",
8209
8209
  "mode": "0000644",
8210
- "sha256": "ef44492854e8c7dc0bffd40525911500bb56a175d59d82d1807dc1e55c58d0bc"
8210
+ "sha256": "f509ecfaca00e821fa21984c54d36121ff0cf2d40057b0bf98c2442edc6b74ab"
8211
8211
  },
8212
8212
  {
8213
8213
  "path": "package.json",
8214
8214
  "mode": "0000644",
8215
- "sha256": "2687fb0dcd9a64ca3bc3d4d39f634ec22490bbc935df1d7aa1fc02826d8e76ca"
8215
+ "sha256": "f2e41698e557ef6542dcc15b564de5cee94bfa8736097add3523fa5d665c60a6"
8216
8216
  },
8217
8217
  {
8218
8218
  "path": "public-docs/index.html",
@@ -8862,7 +8862,7 @@
8862
8862
  {
8863
8863
  "path": "scripts/gds/gen-file-map.js",
8864
8864
  "mode": "0000644",
8865
- "sha256": "a8531787384fc528eb75dde142ca8d24982e47e226c934d6e692c53e6e738a0a"
8865
+ "sha256": "ac2308077104c2ff899f4b12ff9da7baa965a9c0d3e4ef2f92e6bdf9d3613fb6"
8866
8866
  },
8867
8867
  {
8868
8868
  "path": "scripts/gds/gen-freshness.js",
@@ -10257,7 +10257,7 @@
10257
10257
  {
10258
10258
  "path": "src/module-api.js",
10259
10259
  "mode": "0000644",
10260
- "sha256": "499b3d104f70faa2b9cc4252419f7ff3f08a645fcdf329ec9a26761a25289e65"
10260
+ "sha256": "edd1b0444e04d5511e536f5991e87c8fc271b7a18f2ee3f3f6d2dc6d1fd88486"
10261
10261
  },
10262
10262
  {
10263
10263
  "path": "src/module-loader/catalog.js",
@@ -11522,7 +11522,7 @@
11522
11522
  {
11523
11523
  "path": "tests/file_map.mjs",
11524
11524
  "mode": "0000644",
11525
- "sha256": "e426eea6852d2ea63b79d27e17b71c53031ca65ee22644192eb4807df26f7482"
11525
+ "sha256": "c6a7da467336246652fae6db5b5d7faa147e71ac1db89a66ec143dcf03bf8013"
11526
11526
  },
11527
11527
  {
11528
11528
  "path": "tests/filing_abuse_matrix.mjs",
@@ -2476,5 +2476,9 @@ is load-bearing: the script throws rather than guess if it is missing, and
2476
2476
  landed since 1.19.999 with no explicit bump. run 35943151697. (task 1002620)
2477
2477
  1.19.1001 — CI auto-patch (publish-on-merge, ADR 0161): carrier for merges
2478
2478
  landed since 1.19.1000 with no explicit bump. run 36016738965. (task 1002620)
2479
+ 1.19.1002 — CI auto-patch (publish-on-merge, ADR 0161): carrier for merges
2480
+ landed since 1.19.1001 with no explicit bump. run 36019839845. (task 1002620)
2481
+ 1.19.1003 — CI auto-patch (publish-on-merge, ADR 0161): carrier for merges
2482
+ landed since 1.19.1002 with no explicit bump. run 36021332931. (task 1002620)
2479
2483
  ---------------------------------------------------------------------------
2480
2484
  ```
package/package-lock.json CHANGED
@@ -1,12 +1,12 @@
1
1
  {
2
2
  "name": "@bongos/core",
3
- "version": "1.19.1001",
3
+ "version": "1.19.1003",
4
4
  "lockfileVersion": 3,
5
5
  "requires": true,
6
6
  "packages": {
7
7
  "": {
8
8
  "name": "@bongos/core",
9
- "version": "1.19.1001",
9
+ "version": "1.19.1003",
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.1001",
3
+ "version": "1.19.1003",
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",
@@ -13,7 +13,7 @@
13
13
  // and .claude/scheduled-tasks/ — are now GENERATED from what is on disk. The per-entry one-line
14
14
  // notes (the prose after the ←, which no directory walk can know) live in a sidecar,
15
15
  // docs/file-map.notes.json, the ONE place a human writes them. The contract enforced by --check:
16
- // • every skill/routine dir on disk MUST have a note → a new skill can't land undescribed;
16
+ // • every TRACKED skill/routine dir MUST have a note → a new skill can't land undescribed;
17
17
  // • every note MUST map to a dir that exists → the map can't carry ghosts;
18
18
  // • the rendered block MUST match what's committed → no stale tree.
19
19
  // MODULE COVERAGE (task 1004066). The same honor system that let a skill land unmapped let a whole
@@ -41,6 +41,7 @@
41
41
 
42
42
  const fs = require('node:fs');
43
43
  const path = require('node:path');
44
+ const { execFileSync } = require('node:child_process');
44
45
  const { resolveInstanceRoot } = require('../../src/instance-config'); // ADR 0108 §1: configurable-root
45
46
  const { sameContent } = require('./gen-freshness'); // eol-insensitive freshness (CRLF checkouts)
46
47
 
@@ -87,6 +88,21 @@ function loadNotes() {
87
88
  const { readModuleSkillsManifest } = require('./claude-materialize.js');
88
89
  function materializedNames(absDir) { return new Set(Object.keys(readModuleSkillsManifest(absDir).skills)); }
89
90
 
91
+ // The subdirs of `relDir` holding at least one TRACKED file (task 1004213). An untracked one is the
92
+ // builder's own — a locally-installed plugin skill, or the empty husk a plugin extract leaves — and
93
+ // listing it would publish one machine's tooling into a shared doc on every post-merge regen.
94
+ // null when `root` is not the top of a git work tree (a packaged install, ADR 0108 §1): nothing to ask.
95
+ function trackedSubdirs(root, relDir) {
96
+ const git = (args) => execFileSync('git', args, { cwd: root, encoding: 'utf8', stdio: ['ignore', 'pipe', 'ignore'] });
97
+ try {
98
+ if (git(['rev-parse', '--show-prefix']).trim() !== '') return null;
99
+ return new Set(git(['ls-files', '-z', '--', relDir]).split('\0')
100
+ .map((p) => p.slice(relDir.length + 1).split('/'))
101
+ .filter((segs) => segs.length > 1)
102
+ .map((segs) => segs[0]));
103
+ } catch { return null; }
104
+ }
105
+
90
106
  // Immediate subdirectories of an absolute dir, sorted by name (deterministic); materialised
91
107
  // module copies excluded.
92
108
  function listSubdirs(absDir) {
@@ -103,7 +119,9 @@ function listSubdirs(absDir) {
103
119
  // classes the freshness contract enforces (a dir with no note; a note with no dir).
104
120
  function buildSection(section, notes) {
105
121
  const absDir = path.join(REPO_ROOT, section.dir);
106
- const names = listSubdirs(absDir);
122
+ const present = listSubdirs(absDir);
123
+ const tracked = trackedSubdirs(REPO_ROOT, section.dir);
124
+ const names = tracked ? present.filter((n) => tracked.has(n)) : present;
107
125
  const sectionNotes = (notes && notes[section.notesKey]) || {};
108
126
  const entries = names.map((name) => {
109
127
  // Skills + scheduled-tasks each carry a SKILL.md; label it explicitly when present (matches the
@@ -114,7 +132,8 @@ function buildSection(section, notes) {
114
132
  return { name, label, note };
115
133
  });
116
134
  const missing = entries.filter((e) => e.note == null).map((e) => `${section.dir}/${e.name}`);
117
- const onDisk = new Set(names);
135
+ // A note is a ghost only when its dir is gone from disk — one not yet `git add`ed is not.
136
+ const onDisk = new Set(present);
118
137
  const stale = Object.keys(sectionNotes).filter((k) => !onDisk.has(k)).map((k) => `${section.notesKey}.${k}`);
119
138
  return { ...section, absDir, entries, missing, stale };
120
139
  }
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.1001'; // CI auto-patch carrier (ADR 0161); changelog: docs/module-api-changelog.md
74
+ const CORE_VERSION = '1.19.1003'; // 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');
@@ -3,7 +3,8 @@
3
3
  import assert from 'node:assert/strict';
4
4
  import { test } from 'node:test';
5
5
  import { execFileSync } from 'node:child_process';
6
- import { readFileSync, existsSync, readdirSync, mkdirSync, writeFileSync, rmSync } from 'node:fs';
6
+ import { readFileSync, existsSync, readdirSync, mkdirSync, writeFileSync, rmSync, mkdtempSync } from 'node:fs';
7
+ import { tmpdir } from 'node:os';
7
8
  import { createRequire } from 'node:module';
8
9
  import { fileURLToPath } from 'node:url';
9
10
  import path from 'node:path';
@@ -40,8 +41,11 @@ test('EVERY skill + scheduled-task dir on disk is listed AND has a note — no s
40
41
  // A module skill materialised into this checkout (task 1003330) is an untracked copy the
41
42
  // module documents; the generator skips it by the manifest beside it, and so does this.
42
43
  const materialised = gen.materializedNames(path.join(ROOT, section.dir));
44
+ // An untracked dir is the builder's own (a plugin install, an empty husk — task 1004213).
45
+ const tracked = new Set(execFileSync('git', ['ls-files', '-z', '--', section.dir], { cwd: ROOT, encoding: 'utf8' })
46
+ .split('\0').map((p) => p.slice(section.dir.length + 1).split('/')).filter((s) => s.length > 1).map((s) => s[0]));
43
47
  const onDisk = readdirSync(path.join(ROOT, section.dir), { withFileTypes: true })
44
- .filter((e) => e.isDirectory() && !e.name.startsWith('.') && !materialised.has(e.name))
48
+ .filter((e) => e.isDirectory() && !e.name.startsWith('.') && !materialised.has(e.name) && tracked.has(e.name))
45
49
  .map((e) => e.name)
46
50
  .sort();
47
51
  const listed = section.entries.map((e) => e.name).sort();
@@ -69,20 +73,68 @@ test('the generated blocks live between markers and carry one ← note per entry
69
73
  }
70
74
  });
71
75
 
72
- test('--check FAILS (exit 2) when a new skill has no note — a skill can no longer land undescribed', () => {
73
- const probe = path.join(ROOT, '.claude', 'skills', '__filemap_test__');
76
+ // A throwaway instance root (task 1004213). `new-skill` is staged, so git tracks it; `local-plugin`
77
+ // is untracked with a SKILL.md, as a real plugin install is; `empty-husk` is the bare dir a plugin
78
+ // extract leaves behind. The generator falls back to cwd for its root, so no env is needed.
79
+ function fixtureRoot({ git }) {
80
+ const root = mkdtempSync(path.join(tmpdir(), 'file-map-'));
81
+ const skills = path.join(root, '.claude', 'skills');
82
+ for (const n of ['new-skill', 'local-plugin']) {
83
+ mkdirSync(path.join(skills, n), { recursive: true });
84
+ writeFileSync(path.join(skills, n, 'SKILL.md'), `---\nname: ${n}\n---\n`);
85
+ }
86
+ mkdirSync(path.join(skills, 'empty-husk'));
87
+ if (git) {
88
+ execFileSync('git', ['init', '-q'], { cwd: root, stdio: 'ignore' });
89
+ execFileSync('git', ['add', '.claude/skills/new-skill'], { cwd: root, stdio: 'ignore' });
90
+ }
91
+ return root;
92
+ }
93
+ const skillsSectionAt = (cwd) => JSON.parse(execFileSync(process.execPath,
94
+ ['-e', `process.stdout.write(JSON.stringify(require(${JSON.stringify(GEN)}).buildModel()))`],
95
+ { cwd, encoding: 'utf8' })).find((s) => s.id === 'skills');
96
+ const skillNamesAt = (cwd) => skillsSectionAt(cwd).entries.map((e) => e.name);
97
+
98
+ test('--check FAILS (exit 2) when a new tracked skill has no note — a skill can no longer land undescribed', () => {
99
+ const root = fixtureRoot({ git: true });
74
100
  try {
75
- mkdirSync(probe, { recursive: true });
76
- writeFileSync(path.join(probe, 'SKILL.md'), '---\nname: __filemap_test__\n---\n');
77
- const r = run(['--check']);
101
+ let r;
102
+ try { execFileSync(process.execPath, [GEN, '--check'], { cwd: root, encoding: 'utf8', stdio: 'pipe' }); r = { code: 0, stderr: '' }; }
103
+ catch (e) { r = { code: e.status, stderr: String(e.stderr) }; }
78
104
  assert.equal(r.code, 2, 'an undescribed skill must fail the gate');
79
- assert.match(r.stderr, /notes MISSING/);
80
- assert.match(r.stderr, /__filemap_test__/);
105
+ assert.match(r.stderr, /notes MISSING for: \.claude\/skills\/new-skill(?! *,)/, 'the tracked skill, and only it, is missing a note');
106
+ assert.doesNotMatch(r.stderr, /local-plugin|empty-husk/);
81
107
  } finally {
82
- rmSync(probe, { recursive: true, force: true });
108
+ rmSync(root, { recursive: true, force: true });
109
+ }
110
+ });
111
+
112
+ test('an untracked skill dir is the builder\'s own — a plugin install and an empty husk never reach the map', () => {
113
+ const root = fixtureRoot({ git: true });
114
+ try {
115
+ assert.deepEqual(skillNamesAt(root), ['new-skill']);
116
+ // A note written before `git add` is not a ghost: its dir is on disk.
117
+ mkdirSync(path.join(root, 'docs'));
118
+ writeFileSync(path.join(root, 'docs', 'file-map.notes.json'), JSON.stringify({ skills: { 'local-plugin': 'x', gone: 'y' } }));
119
+ assert.deepEqual(skillsSectionAt(root).stale, ['skills.gone']);
120
+ } finally {
121
+ rmSync(root, { recursive: true, force: true });
122
+ }
123
+ });
124
+
125
+ test('outside a git work tree (a packaged install) every dir on disk is still listed', () => {
126
+ const root = fixtureRoot({ git: false });
127
+ const parent = fixtureRoot({ git: true });
128
+ const nested = path.join(parent, 'instance');
129
+ try {
130
+ assert.deepEqual(skillNamesAt(root), ['empty-husk', 'local-plugin', 'new-skill']);
131
+ // A root that is a SUBDIR of some repo is not that repo's top: its files are not the repo's to judge.
132
+ mkdirSync(path.join(nested, '.claude', 'skills', 'inner'), { recursive: true });
133
+ assert.deepEqual(skillNamesAt(nested), ['inner']);
134
+ } finally {
135
+ rmSync(root, { recursive: true, force: true });
136
+ rmSync(parent, { recursive: true, force: true });
83
137
  }
84
- // and clean again once removed
85
- assert.equal(run(['--check']).code, 0, 'gate is clean after the probe dir is removed');
86
138
  });
87
139
 
88
140
  test('generation is deterministic + idempotent (a second run is a no-op)', () => {