@bongos/core 1.19.128 → 1.19.130

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.128",
6
- "core_contract": "1.19.128",
7
- "source_commit": "15671cf5128a6b644791170f9b76c0637bcc0c2a",
5
+ "core_version": "1.19.130",
6
+ "core_contract": "1.19.130",
7
+ "source_commit": "9cfc165fbc5b116976ad07e249f0ed45287f3bcf",
8
8
  "source_ref": "HEAD",
9
- "built_at": "2026-08-15T01:56:43.213Z",
9
+ "built_at": "2026-08-15T03:15:08.211Z",
10
10
  "redaction": {
11
11
  "model": "docs-redacted+functional-verbatim",
12
12
  "docs_redacted": 303,
@@ -17,7 +17,7 @@
17
17
  "gate": "passed"
18
18
  },
19
19
  "file_count": 1570,
20
- "tree_sha256": "4df41f4612e88057c6058199bd929bb309af81367a8fd7435309497c2456ac1a",
20
+ "tree_sha256": "9c3f424caabdd8755729a2c59370b4ea6c2c0e14f591297b3769f3f18ae3da41",
21
21
  "files": [
22
22
  {
23
23
  "path": ".claude/skills/blocker-review/SKILL.md",
@@ -4172,12 +4172,12 @@
4172
4172
  {
4173
4173
  "path": "package-lock.json",
4174
4174
  "mode": "0000644",
4175
- "sha256": "573c516fc294514ce5ed281eb161de21132ddbfd775c5eb63ae1636bd89fc854"
4175
+ "sha256": "d530560bb0e2b69c73bba650529a2d45f3bb4c09728c54e012987f4340681c93"
4176
4176
  },
4177
4177
  {
4178
4178
  "path": "package.json",
4179
4179
  "mode": "0000644",
4180
- "sha256": "bfe73c49720864c119018c0ce9a8889271e7f67c4708ca4dbeaf5af26d277b11"
4180
+ "sha256": "1c4c6038789240a0e60015e8501ad5aa572b1c630d0754ee65147acb5a10bb5b"
4181
4181
  },
4182
4182
  {
4183
4183
  "path": "public-docs/index.html",
@@ -4992,7 +4992,7 @@
4992
4992
  {
4993
4993
  "path": "scripts/gds/mirror-lineage-check.js",
4994
4994
  "mode": "0000644",
4995
- "sha256": "ec187c4a4ad91a3758ff4635baa5ad32dcad7fbf0b61e4f8d3fbc81784254833"
4995
+ "sha256": "31296eda794e640ce5f7191d166d08710b35290be67878826a2c7209f0216e54"
4996
4996
  },
4997
4997
  {
4998
4998
  "path": "scripts/gds/mirror-redact.js",
@@ -5762,7 +5762,7 @@
5762
5762
  {
5763
5763
  "path": "src/module-api.js",
5764
5764
  "mode": "0000644",
5765
- "sha256": "e0b338840f48746ba4ad9bccd7dd4b80c36b844c2e16ea12bdf42f22ceb2eef5"
5765
+ "sha256": "d7733ec4964e2aff924d904ee73784843f81ea72c3e2eeb6f3c7ea816c62ceb6"
5766
5766
  },
5767
5767
  {
5768
5768
  "path": "src/module-loader/catalog.js",
@@ -7052,12 +7052,12 @@
7052
7052
  {
7053
7053
  "path": "tests/mirror_lineage_fulltree.mjs",
7054
7054
  "mode": "0000644",
7055
- "sha256": "ce44c9b95988aa587164f2bfbd002046a188be34f14d968635b5d88bb8e98c56"
7055
+ "sha256": "0482cd900a7c45a18ece314deb977eef852060b50d56b814d58ac631bc568426"
7056
7056
  },
7057
7057
  {
7058
7058
  "path": "tests/mirror_lineage_proof.mjs",
7059
7059
  "mode": "0000644",
7060
- "sha256": "5c9ec605b7e34b046f6df9b1d9f6efa532d394d1134e69990542dde2e86e4ffe"
7060
+ "sha256": "74301070c93c5cb5b4f31d32e508336c96ba199064db9fdf810f07f8d431a98d"
7061
7061
  },
7062
7062
  {
7063
7063
  "path": "tests/mirror_redact.mjs",
package/package-lock.json CHANGED
@@ -1,12 +1,12 @@
1
1
  {
2
2
  "name": "@bongos/core",
3
- "version": "1.19.128",
3
+ "version": "1.19.130",
4
4
  "lockfileVersion": 3,
5
5
  "requires": true,
6
6
  "packages": {
7
7
  "": {
8
8
  "name": "@bongos/core",
9
- "version": "1.19.128",
9
+ "version": "1.19.130",
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.128",
3
+ "version": "1.19.130",
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,10 +13,11 @@
13
13
  // export does (mirror-redact + secret-scrub, via the SHARED redactBlob) and
14
14
  // assert ZERO residual instance literals, public IPs, or secrets survive.
15
15
  // Same detector the export gates on — "one detector, two consumers."
16
- // 2. MIGRATION NUMBERING — the set of core migrations/NNN_*.sql numbers that
17
- // reach the mirror is IDENTICAL to the private set (redaction never renames
18
- // a file), so the claim-time next-number allocator computes the SAME next
19
- // migration on both sides. A dropped/renumbered migration = divergence = fail.
16
+ // 2. MIGRATION NUMBERING — the set of core migration numbers (bare NNN_*.sql and
17
+ // namespaced core_NNN_*.sql alike) that reach the mirror is IDENTICAL to the
18
+ // private set (redaction never renames a file), so the claim-time next-number
19
+ // allocator computes the SAME next migration on both sides. A dropped or
20
+ // renumbered migration = divergence = fail.
20
21
  // 3. ENUM VALUES — for every publishable migration, the CHECK (... IN (...))
21
22
  // value-lists are byte-identical before and after redaction. Redaction may
22
23
  // touch a domain in a comment; it must NEVER alter a schema enum value.
@@ -46,6 +47,7 @@ const {
46
47
  const { isBinary } = require(path.join(REPO_ROOT, 'scripts/public-mirror-export.js'));
47
48
  const { scrubSecrets, collectEnvSecrets, SECRET_PATTERNS } = require(path.join(REPO_ROOT, 'modules/security/secret-scrub.js'));
48
49
  const { nextFree } = require(path.join(REPO_ROOT, 'scripts/gds/next-number.js'));
50
+ const { migrationNumberOf } = require(path.join(REPO_ROOT, 'scripts/gds/migration-namespace.js'));
49
51
 
50
52
  // Known secret SHAPES as NON-GLOBAL clones. We scan each file with .test(), and a
51
53
  // `/g` regex carries lastIndex state across .test() calls — reused in a loop over
@@ -55,30 +57,41 @@ const { nextFree } = require(path.join(REPO_ROOT, 'scripts/gds/next-number.js'))
55
57
  // deterministic regardless of file order.
56
58
  const KNOWN_SECRET_SHAPES = SECRET_PATTERNS.map((re) => new RegExp(re.source, re.flags.replace('g', '')));
57
59
 
58
- // Core migrations are migrations/NNN_slug.sql (3+ digit prefix) DIRECTLY under
59
- // migrations/. Module migrations live under modules/<key>/migrations/ and instance
60
- // SEEDS under migrations/instance/ (ADR 0105) — both numbered independently and
61
- // excluded from the publishable mirror; the claim-time allocator only reads the
62
- // top-level core dir, so only core numbering can diverge.
63
- const MIGRATION_BASENAME_RE = /^(\d{3,})_.*\.sql$/;
64
-
65
60
  // ---- pure helpers (exported for tests) ------------------------------------
66
61
 
62
+ // The CORE migration number for a repo-relative path, or null if it is not one.
63
+ //
64
+ // Core migrations sit DIRECTLY under migrations/ as either the legacy bare NNN_slug.sql
65
+ // or the namespaced core_NNN_slug.sql every NEW core migration must use (ADR 0108 §5).
66
+ // The prefix namespaces the FILENAME (so a core stem can't collide with an instance/
67
+ // module one across repos), NOT the number line — core_188 continues the bare 001–187
68
+ // sequence as ONE sequence. Hence the shared migrationNumberOf, the SAME parser the
69
+ // claim-time allocator (next-number.js) counts from: this proof and the allocator can
70
+ // never disagree about which numbers are taken. (Task 1002527 — the bare-regex version
71
+ // was blind to core_188+, leaving BOTH halves equally blind and the gate falsely green.)
72
+ //
73
+ // Module migrations (modules/<key>/migrations/) and instance SEEDS
74
+ // (migrations/instance/, ADR 0105) are numbered independently and excluded from the
75
+ // publishable mirror; the allocator only reads the top-level core dir, so only core
76
+ // numbering can diverge.
77
+ function coreMigrationNumber(repoRelPath) {
78
+ const s = String(repoRelPath);
79
+ if (!s.startsWith('migrations/')) return null; // the migrations dir …
80
+ // … and ONLY its top level. Counting migrations/instance/NNN_*.sql put every instance
81
+ // seed in the PRIVATE set but not the MIRROR set (they are unpublishable, task 2140) —
82
+ // a false private⇄mirror divergence that red-lined the unit gate (task 2152). A path
83
+ // with a further slash is a subdir, never a core migration.
84
+ const rest = s.slice('migrations/'.length);
85
+ if (rest.includes('/')) return null;
86
+ return migrationNumberOf(rest);
87
+ }
88
+
67
89
  // The set of CORE migration numbers among a list of repo-relative paths.
68
90
  function migrationNumbersFromPaths(paths) {
69
91
  const out = new Set();
70
92
  for (const p of paths || []) {
71
- const s = String(p);
72
- if (!s.startsWith('migrations/')) continue; // the migrations dir …
73
- // … and ONLY its top level. migrations/instance/NNN_*.sql are this instance's host
74
- // seeds (ADR 0105), excluded from the publishable mirror (task 2140) — counting them
75
- // here put every instance seed in the PRIVATE set but not the MIRROR set, a false
76
- // private⇄mirror divergence that red-lined the unit gate (task 2152). A path with a
77
- // further slash is a subdir, never a core migration.
78
- const rest = s.slice('migrations/'.length);
79
- if (rest.includes('/')) continue;
80
- const m = MIGRATION_BASENAME_RE.exec(rest);
81
- if (m) out.add(parseInt(m[1], 10));
93
+ const n = coreMigrationNumber(p);
94
+ if (n != null) out.add(n);
82
95
  }
83
96
  return out;
84
97
  }
@@ -178,10 +191,12 @@ function proveLineage({ publishableFiles, privateMigrationNumbers, ctx }) {
178
191
  for (const f of publishableFiles) {
179
192
  const { redactedText, leaks: fileLeaks } = scanFile(f.buf, ctx);
180
193
  for (const l of fileLeaks) leaks.push({ path: f.path, label: l.label, sample: l.sample });
181
- if (f.path.startsWith('migrations/') && MIGRATION_BASENAME_RE.test(f.path.split('/').pop())) {
194
+ // Same predicate as the private half (migrationNumbersFromPaths) — the two sets are
195
+ // only comparable if both are built by the identical parser.
196
+ const migNum = coreMigrationNumber(f.path);
197
+ if (migNum != null) {
182
198
  migFiles.push({ path: f.path, original: f.buf.toString('utf8'), redacted: redactedText != null ? redactedText : f.buf.toString('utf8') });
183
- const mm = MIGRATION_BASENAME_RE.exec(f.path.split('/').pop());
184
- mirrorNums.add(parseInt(mm[1], 10));
199
+ mirrorNums.add(migNum);
185
200
  }
186
201
  }
187
202
 
@@ -317,7 +332,7 @@ function main() {
317
332
  if (require.main === module) main();
318
333
 
319
334
  module.exports = {
320
- MIGRATION_BASENAME_RE,
335
+ coreMigrationNumber,
321
336
  migrationNumbersFromPaths,
322
337
  parseEnumChecks,
323
338
  numberingDivergence,
package/src/module-api.js CHANGED
@@ -683,8 +683,12 @@ const { validateOrRespond, LIMITS, parseId, asyncHandler, corsPublicGet, parsePa
683
683
  // landed since 1.19.126 with no explicit bump. run 31854730552. (task 1002620)
684
684
  // 1.19.128 — CI auto-patch (publish-on-merge, ADR 0161): carrier for merges
685
685
  // landed since 1.19.127 with no explicit bump. run 31857842473. (task 1002620)
686
+ // 1.19.129 — CI auto-patch (publish-on-merge, ADR 0161): carrier for merges
687
+ // landed since 1.19.128 with no explicit bump. run 31858158416. (task 1002620)
688
+ // 1.19.130 — CI auto-patch (publish-on-merge, ADR 0161): carrier for merges
689
+ // landed since 1.19.129 with no explicit bump. run 31861289412. (task 1002620)
686
690
  // ---------------------------------------------------------------------------
687
- const CORE_VERSION = '1.19.128'; // CI auto-patch carrier (ADR 0161); see changelog above
691
+ const CORE_VERSION = '1.19.130'; // CI auto-patch carrier (ADR 0161); see changelog above
688
692
 
689
693
  // A namespaced logger so a module's log lines are attributable + consistent.
690
694
  // Usage: const log = api.logger('dev-box'); log.info('mounted');
@@ -12,11 +12,18 @@
12
12
  // and asserts zero residual leaks + private⇄public migration-numbering/enum stability.
13
13
  // Filesystem + git (git ls-files) are the only dependencies — no DB, no network.
14
14
  import assert from 'node:assert/strict';
15
+ import fs from 'node:fs';
16
+ import path from 'node:path';
17
+ import { fileURLToPath } from 'node:url';
15
18
  import { test } from 'node:test';
16
19
  import { createRequire } from 'node:module';
17
20
 
18
21
  const require = createRequire(import.meta.url);
19
22
  const { runSimulation } = require('../scripts/gds/mirror-lineage-check.js');
23
+ const { migrationNumberOf } = require('../scripts/gds/migration-namespace.js');
24
+ const { nextFree } = require('../scripts/gds/next-number.js');
25
+
26
+ const MIGRATIONS_DIR = path.join(path.dirname(fileURLToPath(import.meta.url)), '..', 'migrations');
20
27
 
21
28
  test('C9 full-tree simulation: the redacted publishable mirror leaks nothing', () => {
22
29
  const report = runSimulation();
@@ -43,3 +50,24 @@ test('C9 full-tree simulation: the redacted publishable mirror leaks nothing', (
43
50
  `redaction altered enum value-list(s) in ${report.drift.length} migration(s): ${report.drift.map((d) => d.path).join(', ')}`
44
51
  );
45
52
  });
53
+
54
+ // The proof is only worth its green tick if it actually SEES every core migration.
55
+ // Before task 1002527 it parsed the bare `NNN_` form only, so every `core_NNN_` file
56
+ // (ADR 0108 §5) was invisible to BOTH halves — equally blind, therefore silently
57
+ // "consistent". This asserts the simulation's coverage against the real tree so the
58
+ // gate can't quietly stop covering new core migrations again.
59
+ test('C9 full-tree simulation covers namespaced core_NNN_ migrations too (task 1002527)', () => {
60
+ const basenames = fs.readdirSync(MIGRATIONS_DIR).filter((n) => migrationNumberOf(n) != null);
61
+ const namespaced = basenames.filter((n) => !/^\d/.test(n));
62
+ assert.ok(namespaced.length > 0, 'expected at least one core_NNN_ migration on disk — update this test if the namespace discipline changed');
63
+
64
+ const report = runSimulation();
65
+ assert.equal(
66
+ report.counts.migrations,
67
+ basenames.length,
68
+ `the proof saw ${report.counts.migrations} core migrations but migrations/ holds ${basenames.length} — a naming form is not being parsed`
69
+ );
70
+ // Bare and namespaced core files share ONE number line, so the proof's next-free must
71
+ // match what next-number.js hands a claim; if they disagree a number gets re-used.
72
+ assert.equal(report.nextPrivate, nextFree(new Set(basenames.map(migrationNumberOf))));
73
+ });
@@ -8,7 +8,9 @@ import { createRequire } from 'node:module';
8
8
  const require = createRequire(import.meta.url);
9
9
  const {
10
10
  migrationNumbersFromPaths, parseEnumChecks, numberingDivergence, enumDrift,
11
+ proveLineage, buildRedactionContext,
11
12
  } = require('../scripts/gds/mirror-lineage-check.js');
13
+ const { nextFree } = require('../scripts/gds/next-number.js');
12
14
 
13
15
  test('migrationNumbersFromPaths picks CORE migrations only (not module/ADR numbers)', () => {
14
16
  const set = migrationNumbersFromPaths([
@@ -31,9 +33,61 @@ test('migrationNumbersFromPaths excludes migrations/instance/ seeds (task 2152)'
31
33
  'migrations/080_core_thing.sql', // bare core → counted
32
34
  'migrations/instance/080_gds_v4_planning_seed.sql', // instance seed → NOT core
33
35
  'migrations/instance/186_governance_seed.sql', // instance seed → NOT core
34
- 'migrations/core_192_namespaced.sql', // core_ prefix ≠ bare NNN_ form → not counted either
36
+ 'migrations/core_192_namespaced.sql', // namespaced core (ADR 0108 §5) → counted
35
37
  ]);
36
- assert.deepEqual([...set].sort((a, b) => a - b), [80]);
38
+ assert.deepEqual([...set].sort((a, b) => a - b), [80, 192]);
39
+ });
40
+
41
+ // ---- namespaced core migrations (ADR 0108 §5 / task 1002527) ---------------
42
+ // Core migrations above LEGACY_BARE_CORE_MAX are `core_NNN_`-prefixed so their stem
43
+ // can't collide with an instance/module migration across repos. The prefix namespaces
44
+ // the FILENAME, not the NUMBER LINE: core_188 continues the bare 001–187 sequence as
45
+ // ONE sequence (migration-namespace.js pins the cutoff so no bare core file can ever
46
+ // exceed it, and next-number.js unions both forms into a single max+1). The C9 proof
47
+ // must parse both forms or it silently stops covering every new core migration.
48
+
49
+ test('migrationNumbersFromPaths parses namespaced core_NNN_ names as ONE sequence with the bare ones (task 1002527)', () => {
50
+ const set = migrationNumbersFromPaths([
51
+ 'migrations/003_pms.sql', // legacy bare core
52
+ 'migrations/187_goal_sort_order.sql', // the last bare core number
53
+ 'migrations/core_188_goal_visibility.sql',
54
+ 'migrations/core_219_grade_attempts_idx.sql',
55
+ 'modules/game/migrations/game_001_world.sql', // module migration — still excluded
56
+ ]);
57
+ assert.deepEqual([...set].sort((a, b) => a - b), [3, 187, 188, 219]);
58
+ // ONE sequence, so the next free core number continues past the namespaced files —
59
+ // the same number next-number.js hands a claim (188 here would re-use core_188).
60
+ assert.equal(nextFree(set), 220);
61
+ });
62
+
63
+ test('proveLineage counts namespaced core_NNN_ migrations in the MIRROR set (task 1002527)', () => {
64
+ const ctx = buildRedactionContext();
65
+ const file = (p, sql) => ({ path: p, buf: Buffer.from(sql, 'utf8') });
66
+ const publishableFiles = [
67
+ file('migrations/003_pms.sql', "CREATE TABLE a (s TEXT CHECK (s IN ('x', 'y')));\n"),
68
+ file('migrations/core_188_goal_visibility.sql', "CREATE TABLE b (s TEXT CHECK (s IN ('p', 'q')));\n"),
69
+ file('src/bongos/db.js', 'module.exports = {};\n'), // not a migration
70
+ ];
71
+ const report = proveLineage({ publishableFiles, privateMigrationNumbers: new Set([3, 188]), ctx });
72
+
73
+ // Both halves must agree: the private set (migrationNumbersFromPaths) and the mirror
74
+ // set (proveLineage) parse the SAME names, so a namespaced file is never a false
75
+ // "missing" divergence — the failure mode that would red-line the C9 gate.
76
+ assert.equal(report.counts.migrations, 2);
77
+ assert.deepEqual(report.numbering, { missing: [], extra: [], consistent: true });
78
+ assert.equal(report.nextMirror, 189);
79
+ assert.equal(report.nextPrivate, 189);
80
+ });
81
+
82
+ test('proveLineage still CATCHES a genuinely dropped namespaced core migration (task 1002527)', () => {
83
+ const ctx = buildRedactionContext();
84
+ const publishableFiles = [
85
+ { path: 'migrations/003_pms.sql', buf: Buffer.from('SELECT 1;\n', 'utf8') },
86
+ ];
87
+ // core_188 exists privately but never reached the mirror → a real divergence.
88
+ const report = proveLineage({ publishableFiles, privateMigrationNumbers: new Set([3, 188]), ctx });
89
+ assert.equal(report.numbering.consistent, false);
90
+ assert.deepEqual(report.numbering.missing, [188]);
37
91
  });
38
92
 
39
93
  test('parseEnumChecks extracts the quoted value-lists from CHECK (... IN (...))', () => {