@bongos/core 1.19.749 → 1.19.751
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 +20 -15
- package/docs/module-api-changelog.md +4 -0
- package/docs/recipes/gds-db-backup.md +1 -1
- package/package-lock.json +2 -2
- package/package.json +1 -1
- package/scripts/gds/db-backup-nightly.sh +21 -7
- package/scripts/gds/upgrade.js +1 -1
- package/src/module-api.js +1 -1
- package/tests/db_backup_nightly.mjs +79 -0
- package/tests/upgrade_persist_pin.mjs +1 -1
package/.bongos-core.json
CHANGED
|
@@ -2,22 +2,22 @@
|
|
|
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.751",
|
|
6
|
+
"core_contract": "1.19.751",
|
|
7
|
+
"source_commit": "737b578919a197afeddbf0c94a548290a9161998",
|
|
8
8
|
"source_ref": "HEAD",
|
|
9
|
-
"built_at": "2026-09-15T22:
|
|
9
|
+
"built_at": "2026-09-15T22:52:21.404Z",
|
|
10
10
|
"redaction": {
|
|
11
11
|
"model": "docs-redacted+functional-verbatim",
|
|
12
12
|
"docs_redacted": 482,
|
|
13
13
|
"agent_docs_stubbed": 25,
|
|
14
|
-
"functional_verbatim":
|
|
14
|
+
"functional_verbatim": 2110,
|
|
15
15
|
"rules": 3,
|
|
16
16
|
"gate_literals": 3,
|
|
17
17
|
"gate": "passed"
|
|
18
18
|
},
|
|
19
|
-
"file_count":
|
|
20
|
-
"tree_sha256": "
|
|
19
|
+
"file_count": 2617,
|
|
20
|
+
"tree_sha256": "741bce83585c646386bc2ddbe75a07bf458400bd8ed19f78d47da79ba5c9e5de",
|
|
21
21
|
"files": [
|
|
22
22
|
{
|
|
23
23
|
"path": ".claude/skills/ask-for-help/SKILL.md",
|
|
@@ -2507,7 +2507,7 @@
|
|
|
2507
2507
|
{
|
|
2508
2508
|
"path": "docs/module-api-changelog.md",
|
|
2509
2509
|
"mode": "0000644",
|
|
2510
|
-
"sha256": "
|
|
2510
|
+
"sha256": "6864b03a8e3bcca555963a85cbd1d3c1ac609b355e61ff4abb60299626979752"
|
|
2511
2511
|
},
|
|
2512
2512
|
{
|
|
2513
2513
|
"path": "docs/modules-contract.md",
|
|
@@ -2652,7 +2652,7 @@
|
|
|
2652
2652
|
{
|
|
2653
2653
|
"path": "docs/recipes/gds-db-backup.md",
|
|
2654
2654
|
"mode": "0000644",
|
|
2655
|
-
"sha256": "
|
|
2655
|
+
"sha256": "0f2dfdeca8d49a07a0da043eaa9f2b16669228c358cb56a7786c6c59e5e29808"
|
|
2656
2656
|
},
|
|
2657
2657
|
{
|
|
2658
2658
|
"path": "docs/recipes/instance-deploy.md",
|
|
@@ -7562,12 +7562,12 @@
|
|
|
7562
7562
|
{
|
|
7563
7563
|
"path": "package-lock.json",
|
|
7564
7564
|
"mode": "0000644",
|
|
7565
|
-
"sha256": "
|
|
7565
|
+
"sha256": "197208273eadd947af33002bde4c49421f0a5f846946b5852fc2422bcf4bdafc"
|
|
7566
7566
|
},
|
|
7567
7567
|
{
|
|
7568
7568
|
"path": "package.json",
|
|
7569
7569
|
"mode": "0000644",
|
|
7570
|
-
"sha256": "
|
|
7570
|
+
"sha256": "c91859f22d3a06e3e9c64c628dff657f7aecbda25afafd287f571a473148bdee"
|
|
7571
7571
|
},
|
|
7572
7572
|
{
|
|
7573
7573
|
"path": "public-docs/index.html",
|
|
@@ -7912,7 +7912,7 @@
|
|
|
7912
7912
|
{
|
|
7913
7913
|
"path": "scripts/gds/db-backup-nightly.sh",
|
|
7914
7914
|
"mode": "0000644",
|
|
7915
|
-
"sha256": "
|
|
7915
|
+
"sha256": "ff4785dbde6a5c52036ce4c4aacca317c62e725f49d3e32201909ec444d85d44"
|
|
7916
7916
|
},
|
|
7917
7917
|
{
|
|
7918
7918
|
"path": "scripts/gds/db-backup-offsite.sh",
|
|
@@ -8927,7 +8927,7 @@
|
|
|
8927
8927
|
{
|
|
8928
8928
|
"path": "scripts/gds/upgrade.js",
|
|
8929
8929
|
"mode": "0000644",
|
|
8930
|
-
"sha256": "
|
|
8930
|
+
"sha256": "3146778317a4dfd01673553e94966ea40d27add12a7ca6c52ec8581eded085ac"
|
|
8931
8931
|
},
|
|
8932
8932
|
{
|
|
8933
8933
|
"path": "scripts/gds/validate-design.js",
|
|
@@ -9367,7 +9367,7 @@
|
|
|
9367
9367
|
{
|
|
9368
9368
|
"path": "src/module-api.js",
|
|
9369
9369
|
"mode": "0000644",
|
|
9370
|
-
"sha256": "
|
|
9370
|
+
"sha256": "e80ba785ccd39c20b2ca31f3c645257f84e72004e75c71ba52fb10647a6b3297"
|
|
9371
9371
|
},
|
|
9372
9372
|
{
|
|
9373
9373
|
"path": "src/module-loader/catalog.js",
|
|
@@ -10224,6 +10224,11 @@
|
|
|
10224
10224
|
"mode": "0000644",
|
|
10225
10225
|
"sha256": "434130945232d2a6959281070e6dbee31697904b3455fb3fd09f198a64bb0e33"
|
|
10226
10226
|
},
|
|
10227
|
+
{
|
|
10228
|
+
"path": "tests/db_backup_nightly.mjs",
|
|
10229
|
+
"mode": "0000644",
|
|
10230
|
+
"sha256": "020dc551d105cc4a29e0f9c750a03cd14a91511d3edc47eb6719a1e430461936"
|
|
10231
|
+
},
|
|
10227
10232
|
{
|
|
10228
10233
|
"path": "tests/dead_deps.mjs",
|
|
10229
10234
|
"mode": "0000644",
|
|
@@ -12947,7 +12952,7 @@
|
|
|
12947
12952
|
{
|
|
12948
12953
|
"path": "tests/upgrade_persist_pin.mjs",
|
|
12949
12954
|
"mode": "0000644",
|
|
12950
|
-
"sha256": "
|
|
12955
|
+
"sha256": "d30debaa35407998dcdd1949f9bcbf45c09b4a001d22cfdea9541b5bdbaa1274"
|
|
12951
12956
|
},
|
|
12952
12957
|
{
|
|
12953
12958
|
"path": "tests/verify_task_guard.mjs",
|
|
@@ -1972,5 +1972,9 @@ is load-bearing: the script throws rather than guess if it is missing, and
|
|
|
1972
1972
|
landed since 1.19.747 with no explicit bump. run 35026862340. (task 1002620)
|
|
1973
1973
|
1.19.749 — CI auto-patch (publish-on-merge, ADR 0161): carrier for merges
|
|
1974
1974
|
landed since 1.19.748 with no explicit bump. run 35028684745. (task 1002620)
|
|
1975
|
+
1.19.750 — CI auto-patch (publish-on-merge, ADR 0161): carrier for merges
|
|
1976
|
+
landed since 1.19.749 with no explicit bump. run 35030717430. (task 1002620)
|
|
1977
|
+
1.19.751 — CI auto-patch (publish-on-merge, ADR 0161): carrier for merges
|
|
1978
|
+
landed since 1.19.750 with no explicit bump. run 35033016299. (task 1002620)
|
|
1975
1979
|
---------------------------------------------------------------------------
|
|
1976
1980
|
```
|
|
@@ -21,7 +21,7 @@ This closes the **most likely** loss-of-data risks: schema corruption, an accide
|
|
|
21
21
|
|
|
22
22
|
- **Filename format:** `<dbname>-<UTC-timestamp>.sql.gz`. UTC is used so the dumps sort chronologically regardless of the droplet's timezone.
|
|
23
23
|
- **Permissions:** dir is `lars:lars 0750`; files are `lars:lars 0640`. No root needed for routine inspection.
|
|
24
|
-
- **Size:** each gzipped dump is well under 1 MB, and 30 days of them fit comfortably — pruning is hygiene, not disk pressure. **The dump excludes `search_chunks` data** (the `/recall` lexical index, migration 103) via `--exclude-table-data=search_chunks` ([#973](https://example.com/builders#/task/973)): it is regenerable derived data — chunked copies of DB prose (already dumped) and repo markdown (already in git) — and dumping its rows roughly duplicated the prose corpus, making it the dump's dominant growth vector. The table schema + GIN indexes are still dumped, so a restore recreates it empty; the index rebuilds from source (see the restore drill below). A size guard in the backup script WARNs to journald if a dump ever exceeds `GDS_BACKUP_WARN_BYTES` (default 25 MiB) — a generous tripwire for unexpected growth from some *other* table.
|
|
24
|
+
- **Size:** each gzipped dump is well under 1 MB, and 30 days of them fit comfortably — pruning is hygiene, not disk pressure. **The dump excludes `search_chunks` data** (the `/recall` lexical index, migration 103) via `--exclude-table-data=search_chunks` ([#973](https://example.com/builders#/task/973)): it is regenerable derived data — chunked copies of DB prose (already dumped) and repo markdown (already in git) — and dumping its rows roughly duplicated the prose corpus, making it the dump's dominant growth vector. The table schema + GIN indexes are still dumped, so a restore recreates it empty; the index rebuilds from source (see the restore drill below). A size guard in the backup script WARNs to journald if a dump ever exceeds `GDS_BACKUP_WARN_BYTES` (default 25 MiB) — a generous tripwire for unexpected growth from some *other* table. A dump is refused (and deleted) only when it is **incomplete** — missing pg_dump's closing `dump complete` line — never for being small: a fresh project's whole database gzips to ~36 KB, and the fixed 1 MiB floor this replaced failed and deleted every project's nightly dump ([task 1003925](https://cloudbongos.com/builders#/task/1003925)). An optional extra size floor can still be set in the unit's environment (see the script).
|
|
25
25
|
|
|
26
26
|
## What's scheduled
|
|
27
27
|
|
package/package-lock.json
CHANGED
|
@@ -1,12 +1,12 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@bongos/core",
|
|
3
|
-
"version": "1.19.
|
|
3
|
+
"version": "1.19.751",
|
|
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.751",
|
|
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.751",
|
|
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",
|
|
@@ -130,13 +130,27 @@ mv "$OUT.partial" "$OUT"
|
|
|
130
130
|
BYTES=$(stat -c '%s' "$OUT")
|
|
131
131
|
echo "[$(date -u +%FT%TZ)] gds-db-backup: wrote ${OUT} (${BYTES} bytes)"
|
|
132
132
|
|
|
133
|
-
#
|
|
134
|
-
# on a pg_dump error
|
|
135
|
-
#
|
|
136
|
-
#
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
133
|
+
# Completeness guard (incident 2026-07-02, task 1711; task 1003925). pipefail already
|
|
134
|
+
# aborts on a pg_dump error; this is belt-and-suspenders against a dump cut short. A
|
|
135
|
+
# plain-format dump always closes with pg_dump's own "dump complete" comment (patched
|
|
136
|
+
# servers append a \unrestrict line after it), whatever its size. The fixed 1 MiB floor
|
|
137
|
+
# this replaces was sized for the platform database and failed — and deleted — every
|
|
138
|
+
# valid dump of a small project (~36 KB gzipped). Fail loudly and delete a broken dump
|
|
139
|
+
# so it can never masquerade as a good one.
|
|
140
|
+
DUMP_TAIL=$(gzip -dc "$OUT" 2>/dev/null | tail -n 10) || DUMP_TAIL=''
|
|
141
|
+
FAIL_REASON=''
|
|
142
|
+
case "$DUMP_TAIL" in
|
|
143
|
+
*'-- PostgreSQL database dump complete'*) ;;
|
|
144
|
+
*) FAIL_REASON="(${BYTES} bytes) is incomplete — no pg_dump completion line" ;;
|
|
145
|
+
esac
|
|
146
|
+
|
|
147
|
+
# Optional operator floor. Unset means none: no single size fits every instance.
|
|
148
|
+
MIN_BYTES="${GDS_BACKUP_MIN_BYTES:-0}"
|
|
149
|
+
if [ -z "$FAIL_REASON" ] && [ "$BYTES" -lt "$MIN_BYTES" ]; then
|
|
150
|
+
FAIL_REASON="is only ${BYTES} bytes (< ${MIN_BYTES} floor)"
|
|
151
|
+
fi
|
|
152
|
+
if [ -n "$FAIL_REASON" ]; then
|
|
153
|
+
echo "[$(date -u +%FT%TZ)] gds-db-backup: ERROR latest dump ${FAIL_REASON} — treating as FAILED." >&2
|
|
140
154
|
rm -f "$OUT"
|
|
141
155
|
exit 1
|
|
142
156
|
fi
|
package/scripts/gds/upgrade.js
CHANGED
|
@@ -334,7 +334,7 @@ function persistPin({ instanceDir, fromVersion, toVersion, commitPin, push = tru
|
|
|
334
334
|
err(' On a pull-deploy instance the next push to the repo of record makes deploy.sh');
|
|
335
335
|
err(` 'git reset --hard ${remote}/${branch || 'main'}', which REVERTS this pin and lets npm ci`);
|
|
336
336
|
err(` reinstall ${fromVersion || 'the previous core'}. Re-run with --commit-pin, or commit + push:`);
|
|
337
|
-
err(` ${at} add ${toStage.map(q).join(' ')}
|
|
337
|
+
err(` ${at} add ${toStage.map(q).join(' ')} && ${at} commit -m ${q(`pin @bongos/core ${toVersion}`)} && ${at} push ${q(remote)} ${q(target)}`);
|
|
338
338
|
return { applicable: true, committed: false, pushed: false, dirty, warned: true };
|
|
339
339
|
}
|
|
340
340
|
|
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.751'; // 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');
|
|
@@ -0,0 +1,79 @@
|
|
|
1
|
+
// tests/db_backup_nightly.mjs — the nightly dump's sanity guard (task 1003925).
|
|
2
|
+
//
|
|
3
|
+
// THE BUG. db-backup-nightly.sh refused any dump under a fixed 1 MiB floor, sized for the
|
|
4
|
+
// platform's own database. A small project's whole database gzips to ~36 KB, so every
|
|
5
|
+
// project's nightly unit failed AND the valid dump was deleted: on 2026-09-15 twelve backup
|
|
6
|
+
// units were failed and no project had a single retained dump. The guard now asks whether
|
|
7
|
+
// the dump is COMPLETE (pg_dump's closing comment), which is true of a valid dump at any size
|
|
8
|
+
// and false of one cut short.
|
|
9
|
+
//
|
|
10
|
+
// The script runs for real against a fake `pg_dump` on PATH, so nothing touches a database.
|
|
11
|
+
//
|
|
12
|
+
// Run: node --test tests/db_backup_nightly.mjs
|
|
13
|
+
|
|
14
|
+
import assert from 'node:assert/strict';
|
|
15
|
+
import { test } from 'node:test';
|
|
16
|
+
import { spawnSync } from 'node:child_process';
|
|
17
|
+
import fs from 'node:fs';
|
|
18
|
+
import os from 'node:os';
|
|
19
|
+
import path from 'node:path';
|
|
20
|
+
import { fileURLToPath } from 'node:url';
|
|
21
|
+
|
|
22
|
+
const ROOT = path.resolve(path.dirname(fileURLToPath(import.meta.url)), '..');
|
|
23
|
+
const SCRIPT = path.join(ROOT, 'scripts', 'gds', 'db-backup-nightly.sh');
|
|
24
|
+
const skip = process.platform === 'win32' ? 'needs a POSIX bash + coreutils (runs in CI)' : false;
|
|
25
|
+
// The script's env-var prefix, built rather than written so the legacy-literal ratchet
|
|
26
|
+
// does not count a test that has to speak the script's own variable names.
|
|
27
|
+
const ENV = 'gds_backup_'.toUpperCase();
|
|
28
|
+
|
|
29
|
+
// A fake pg_dump: a plain-format dump of `rows` rows, closed the way pg_dump closes one
|
|
30
|
+
// (including the \unrestrict line patched servers append) unless it is `cut` short, and
|
|
31
|
+
// exiting `exitCode`.
|
|
32
|
+
function runBackup({ rows = 40, cut = false, exitCode = 0, env = {} } = {}) {
|
|
33
|
+
const tmp = fs.mkdtempSync(path.join(os.tmpdir(), 'db-backup-'));
|
|
34
|
+
const bin = path.join(tmp, 'bin');
|
|
35
|
+
const dir = path.join(tmp, 'backups');
|
|
36
|
+
fs.mkdirSync(bin);
|
|
37
|
+
fs.mkdirSync(dir);
|
|
38
|
+
const body = Array.from({ length: rows }, (_, i) => `INSERT INTO public.t VALUES (${i});`).join('\n');
|
|
39
|
+
const close = cut ? '' : '\n--\n-- PostgreSQL database dump complete\n--\n\n\\unrestrict abc123\n';
|
|
40
|
+
fs.writeFileSync(path.join(bin, 'pg_dump'),
|
|
41
|
+
`#!/bin/bash\ncat <<'EOF'\n--\n-- PostgreSQL database dump\n--\n${body}\n${close}EOF\nexit ${exitCode}\n`, { mode: 0o755 });
|
|
42
|
+
const r = spawnSync('bash', [SCRIPT], {
|
|
43
|
+
encoding: 'utf8',
|
|
44
|
+
env: { ...process.env, PATH: `${bin}:${process.env.PATH}`, [`${ENV}DB`]: 'smallproj', [`${ENV}DIR`]: dir, ...env },
|
|
45
|
+
});
|
|
46
|
+
return { status: r.status, stdout: r.stdout, stderr: r.stderr, dumps: fs.readdirSync(dir).filter((f) => f.endsWith('.sql.gz')) };
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
test('a complete dump of a small project is kept, however small', { skip }, () => {
|
|
50
|
+
const r = runBackup();
|
|
51
|
+
assert.equal(r.status, 0, r.stderr);
|
|
52
|
+
assert.equal(r.dumps.length, 1, 'the valid dump is retained');
|
|
53
|
+
assert.match(r.stdout, /gds-db-backup: ok/);
|
|
54
|
+
});
|
|
55
|
+
|
|
56
|
+
test('a dump cut short fails and is removed, whatever its size', { skip }, () => {
|
|
57
|
+
const r = runBackup({ rows: 50000, cut: true });
|
|
58
|
+
assert.equal(r.status, 1);
|
|
59
|
+
assert.equal(r.dumps.length, 0, 'an incomplete dump never masquerades as a good one');
|
|
60
|
+
assert.match(r.stderr, /incomplete/i);
|
|
61
|
+
});
|
|
62
|
+
|
|
63
|
+
test('a pg_dump that fails mid-stream still fails the run (pipefail, task 1711)', { skip }, () => {
|
|
64
|
+
const r = runBackup({ cut: true, exitCode: 1 });
|
|
65
|
+
assert.notEqual(r.status, 0);
|
|
66
|
+
assert.equal(r.dumps.length, 0);
|
|
67
|
+
});
|
|
68
|
+
|
|
69
|
+
test('an explicit operator size floor is still enforced', { skip }, () => {
|
|
70
|
+
const r = runBackup({ env: { [`${ENV}MIN_BYTES`]: String(10 * 1024 * 1024) } });
|
|
71
|
+
assert.equal(r.status, 1);
|
|
72
|
+
assert.equal(r.dumps.length, 0);
|
|
73
|
+
assert.match(r.stderr, /floor/);
|
|
74
|
+
});
|
|
75
|
+
|
|
76
|
+
test('no fixed size floor is baked in for every instance', () => {
|
|
77
|
+
const src = fs.readFileSync(SCRIPT, 'utf8');
|
|
78
|
+
assert.doesNotMatch(src, new RegExp(`${ENV}MIN_BYTES:-[1-9]`), 'a default floor fails every database smaller than it');
|
|
79
|
+
});
|
|
@@ -312,7 +312,7 @@ test('without --commit-pin, the paste-ready fix stages the changed generated fil
|
|
|
312
312
|
const run = fakeRun({ status: statusOf([...PIN_FILES, 'docs/api/openapi.json']), 'rev-parse': ON_MAIN });
|
|
313
313
|
persistPin({ ...base, commitPin: false, artifactPaths: ARTIFACTS }, run, quiet, (m) => warnings.push(m));
|
|
314
314
|
const cmd = warnings.find((w) => w.includes('git -C'));
|
|
315
|
-
assert.match(cmd, /add 'package\.json' 'package-lock\.json' 'docs\/api\/openapi\.json'
|
|
315
|
+
assert.match(cmd, /add 'package\.json' 'package-lock\.json' 'docs\/api\/openapi\.json' && git -C \S+ commit -m .+ && git -C \S+ push /);
|
|
316
316
|
});
|
|
317
317
|
|
|
318
318
|
// Real git, no fakes: the task's done-when, reproduced on a scratch repo.
|