@bongos/core 1.19.11 → 1.19.13
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 +10 -10
- package/package-lock.json +2 -2
- package/package.json +1 -1
- package/scripts/gds/ship.js +65 -19
- package/src/module-api.js +5 -1
- package/tests/ship_ci_deploy.mjs +36 -0
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.13",
|
|
6
|
+
"core_contract": "1.19.13",
|
|
7
|
+
"source_commit": "9871ae1c84d380c804f27a2a8f2c8b31a1c0eae2",
|
|
8
8
|
"source_ref": "HEAD",
|
|
9
|
-
"built_at": "2026-08-08T20:
|
|
9
|
+
"built_at": "2026-08-08T20:54:03.577Z",
|
|
10
10
|
"redaction": {
|
|
11
11
|
"model": "docs-redacted+functional-verbatim",
|
|
12
12
|
"docs_redacted": 284,
|
|
@@ -17,7 +17,7 @@
|
|
|
17
17
|
"gate": "passed"
|
|
18
18
|
},
|
|
19
19
|
"file_count": 1508,
|
|
20
|
-
"tree_sha256": "
|
|
20
|
+
"tree_sha256": "ee0b74c675d9eae8a9eacfe1e32c2c3b89f6b30cde100b786e5c4dd04f859ede",
|
|
21
21
|
"files": [
|
|
22
22
|
{
|
|
23
23
|
"path": ".claude/skills/blocker-review/SKILL.md",
|
|
@@ -3997,12 +3997,12 @@
|
|
|
3997
3997
|
{
|
|
3998
3998
|
"path": "package-lock.json",
|
|
3999
3999
|
"mode": "0000644",
|
|
4000
|
-
"sha256": "
|
|
4000
|
+
"sha256": "e651e8a285a3591a06a4562fef0fe2b92a412da95e879f12a891d743358f4dd9"
|
|
4001
4001
|
},
|
|
4002
4002
|
{
|
|
4003
4003
|
"path": "package.json",
|
|
4004
4004
|
"mode": "0000644",
|
|
4005
|
-
"sha256": "
|
|
4005
|
+
"sha256": "4649829e05fa95e32332b71cdc37037d194f4f15b7c47af6618eb1f00dd2f785"
|
|
4006
4006
|
},
|
|
4007
4007
|
{
|
|
4008
4008
|
"path": "public-docs/index.html",
|
|
@@ -5192,7 +5192,7 @@
|
|
|
5192
5192
|
{
|
|
5193
5193
|
"path": "scripts/gds/ship.js",
|
|
5194
5194
|
"mode": "0000644",
|
|
5195
|
-
"sha256": "
|
|
5195
|
+
"sha256": "bea8a5d8698abf30064c005076c069d470203c86a37a1ff7a3aebc4a5ded19a6"
|
|
5196
5196
|
},
|
|
5197
5197
|
{
|
|
5198
5198
|
"path": "scripts/gds/smoke-batch-claim.sh",
|
|
@@ -5622,7 +5622,7 @@
|
|
|
5622
5622
|
{
|
|
5623
5623
|
"path": "src/module-api.js",
|
|
5624
5624
|
"mode": "0000644",
|
|
5625
|
-
"sha256": "
|
|
5625
|
+
"sha256": "138d3c390e59579f2f8b68f2bed364df776af9e1374c62ce6d12dc9a0fe8a43d"
|
|
5626
5626
|
},
|
|
5627
5627
|
{
|
|
5628
5628
|
"path": "src/module-loader/loader.js",
|
|
@@ -7367,7 +7367,7 @@
|
|
|
7367
7367
|
{
|
|
7368
7368
|
"path": "tests/ship_ci_deploy.mjs",
|
|
7369
7369
|
"mode": "0000644",
|
|
7370
|
-
"sha256": "
|
|
7370
|
+
"sha256": "de59e8ed0d1a0e30b52994fe1bf3e1864706613a1329d967f74eeae277c1948e"
|
|
7371
7371
|
},
|
|
7372
7372
|
{
|
|
7373
7373
|
"path": "tests/ship_preflight.mjs",
|
package/package-lock.json
CHANGED
|
@@ -1,12 +1,12 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@bongos/core",
|
|
3
|
-
"version": "1.19.
|
|
3
|
+
"version": "1.19.13",
|
|
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.13",
|
|
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.13",
|
|
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",
|
package/scripts/gds/ship.js
CHANGED
|
@@ -831,27 +831,58 @@ function parseDeployMode(envVal, configRaw) {
|
|
|
831
831
|
return 'laptop';
|
|
832
832
|
}
|
|
833
833
|
|
|
834
|
-
// effectiveDeployMode — credential-aware adjustment of the parsed
|
|
835
|
-
// exported for the regression test). The 'laptop' land path pushes
|
|
836
|
-
// the droplet FROM THIS MACHINE, so it requires a local GitHub push
|
|
837
|
-
// A machine without one — a dev box, or a fresh/stale local clone that
|
|
838
|
-
// `git pull` (token-less origin, ADR 0053) — physically cannot run it;
|
|
839
|
-
// ship via the 'ci' server-mediated publish path instead
|
|
840
|
-
// ADR 0055), which lets the GDS server push with its own
|
|
841
|
-
// parsed mode is 'laptop' but the machine can't push
|
|
842
|
-
// pinned by an explicit OTB_DEPLOY_MODE, fall back to
|
|
843
|
-
// every ship at merge. This is the scootermack 2026-06-22
|
|
844
|
-
// at the pre-cutover 'laptop' default that can't pull
|
|
845
|
-
// attempting a push it had no credential for. An
|
|
846
|
-
// wins (tests/staging may force 'laptop')
|
|
847
|
-
//
|
|
848
|
-
//
|
|
849
|
-
//
|
|
850
|
-
|
|
834
|
+
// effectiveDeployMode — credential- and protection-aware adjustment of the parsed
|
|
835
|
+
// mode (pure; exported for the regression test). The 'laptop' land path pushes
|
|
836
|
+
// main + SSHes the droplet FROM THIS MACHINE, so it requires a local GitHub push
|
|
837
|
+
// credential. A machine without one — a dev box, or a fresh/stale local clone that
|
|
838
|
+
// can't even `git pull` (token-less origin, ADR 0053) — physically cannot run it;
|
|
839
|
+
// it must ship via the 'ci' server-mediated publish path instead
|
|
840
|
+
// (pushVia()==='server', ADR 0055), which lets the GDS server push with its own
|
|
841
|
+
// credential. So when the parsed mode is 'laptop' but the machine can't push
|
|
842
|
+
// locally AND the mode was NOT pinned by an explicit OTB_DEPLOY_MODE, fall back to
|
|
843
|
+
// 'ci' rather than strand every ship at merge. This is the scootermack 2026-06-22
|
|
844
|
+
// case: a checkout left at the pre-cutover 'laptop' default that can't pull
|
|
845
|
+
// forward to 'ci', so it kept attempting a push it had no credential for. An
|
|
846
|
+
// explicit env override always wins (tests/staging may force 'laptop'). Mirrors
|
|
847
|
+
// pushVia()'s existing credential-awareness — the asymmetry (pushVia smart,
|
|
848
|
+
// resolveDeployMode blind) WAS the bug. (task 1449)
|
|
849
|
+
//
|
|
850
|
+
// Second input, same shape (task 1002629): a machine WITH push credentials but a
|
|
851
|
+
// branch-PROTECTED main also cannot run the laptop land — GitHub refuses the
|
|
852
|
+
// direct `git push origin main` no matter whose credential it is. When task
|
|
853
|
+
// 1002619 armed protection (required `unit` check), every ship resolved 'laptop'
|
|
854
|
+
// and pushed into the wall, stranding graded-PASS tasks at confirmed (tasks
|
|
855
|
+
// 1002608/1002609; learning 1000177). mainProtected must be STRICTLY true to
|
|
856
|
+
// trigger — null/undefined means the probe couldn't answer (no gh, offline, no
|
|
857
|
+
// permission) and keeps today's behavior rather than rerouting on a guess.
|
|
858
|
+
function effectiveDeployMode(mode, { canPushLocally, envForced, mainProtected } = {}) {
|
|
851
859
|
if (mode === 'laptop' && !envForced && !canPushLocally) return 'ci';
|
|
860
|
+
if (mode === 'laptop' && !envForced && mainProtected === true) return 'ci';
|
|
852
861
|
return mode;
|
|
853
862
|
}
|
|
854
863
|
|
|
864
|
+
// mainIsProtected — true/false when GitHub answers, null when it can't. The
|
|
865
|
+
// `gh api` resolves {owner}/{repo} from the checkout's origin; any failure
|
|
866
|
+
// (gh missing, offline, non-200) is null = unknown, which effectiveDeployMode
|
|
867
|
+
// treats as "no change". Same spawn discipline as ghAvailableAndAuthed().
|
|
868
|
+
function mainIsProtected() {
|
|
869
|
+
try {
|
|
870
|
+
const r = spawnSync('gh', ['api', 'repos/{owner}/{repo}/branches/main', '--jq', '.protected'], {
|
|
871
|
+
encoding: 'utf8',
|
|
872
|
+
shell: process.platform === 'win32',
|
|
873
|
+
windowsHide: true,
|
|
874
|
+
timeout: 15000,
|
|
875
|
+
});
|
|
876
|
+
if (!r || r.error || r.status !== 0) return null;
|
|
877
|
+
const out = String(r.stdout || '').trim();
|
|
878
|
+
if (out === 'true') return true;
|
|
879
|
+
if (out === 'false') return false;
|
|
880
|
+
return null;
|
|
881
|
+
} catch (_) {
|
|
882
|
+
return null;
|
|
883
|
+
}
|
|
884
|
+
}
|
|
885
|
+
|
|
855
886
|
function resolveDeployMode() {
|
|
856
887
|
const configRaw = readDeployConfigRaw();
|
|
857
888
|
const mode = parseDeployMode(process.env.OTB_DEPLOY_MODE, configRaw);
|
|
@@ -860,9 +891,17 @@ function resolveDeployMode() {
|
|
|
860
891
|
);
|
|
861
892
|
// Probe `gh` ONLY when it can change the answer (resolved 'laptop', no env
|
|
862
893
|
// override) — the happy 'ci' path and an env-pinned mode spawn no extra gh,
|
|
863
|
-
// keeping the documented spawn discipline (the line-1019 note).
|
|
894
|
+
// keeping the documented spawn discipline (the line-1019 note). The protection
|
|
895
|
+
// probe runs only when the credential check kept us on 'laptop' (a
|
|
896
|
+
// credential-less machine already falls back without it).
|
|
864
897
|
if (mode === 'laptop' && !envForced) {
|
|
865
|
-
|
|
898
|
+
const canPushLocally = ghAvailableAndAuthed();
|
|
899
|
+
const mainProtected = canPushLocally ? mainIsProtected() : null;
|
|
900
|
+
const effective = effectiveDeployMode(mode, { canPushLocally, envForced, mainProtected });
|
|
901
|
+
if (effective === 'ci' && mainProtected === true) {
|
|
902
|
+
console.log(' deploy mode: main is branch-protected (direct pushes refused) — landing via the ci PR path');
|
|
903
|
+
}
|
|
904
|
+
return effective;
|
|
866
905
|
}
|
|
867
906
|
return mode;
|
|
868
907
|
}
|
|
@@ -2486,6 +2525,13 @@ async function autoMerge(taskId, valueSummary, { dbOnly = false } = {}) {
|
|
|
2486
2525
|
// cross-platform reason as the other node spawns here (#660).
|
|
2487
2526
|
if (!shellOk(`"${process.execPath}" ${JSON.stringify(path.join(__dirname, 'push-main.js'))}`, { cwd: REPO_ROOT })) {
|
|
2488
2527
|
console.log(' auto-merge: push failed; task stays at confirmed');
|
|
2528
|
+
// task 1002629: a branch-protected main refuses EVERY direct push, so
|
|
2529
|
+
// retrying here can never work — resolveDeployMode normally reroutes to
|
|
2530
|
+
// the ci PR path before this point; reaching this with protection on
|
|
2531
|
+
// means an env-forced 'laptop' or a failed protection probe.
|
|
2532
|
+
if (mainIsProtected() === true) {
|
|
2533
|
+
return landBailed('main is branch-protected — the direct push can never land; unset OTB_DEPLOY_MODE (the ci PR path lands it)', `node scripts/gds/ship.js ${taskId}`);
|
|
2534
|
+
}
|
|
2489
2535
|
return landBailed('could not push the merged main to origin', '/merge-mode');
|
|
2490
2536
|
}
|
|
2491
2537
|
|
package/src/module-api.js
CHANGED
|
@@ -449,8 +449,12 @@ const { validateOrRespond, LIMITS, parseId, asyncHandler, corsPublicGet, parsePa
|
|
|
449
449
|
// landed since 1.19.9 with no explicit bump. run 31277644352. (task 1002620)
|
|
450
450
|
// 1.19.11 — CI auto-patch (publish-on-merge, ADR 0161): carrier for merges
|
|
451
451
|
// landed since 1.19.10 with no explicit bump. run 31277735279. (task 1002620)
|
|
452
|
+
// 1.19.12 — CI auto-patch (publish-on-merge, ADR 0161): carrier for merges
|
|
453
|
+
// landed since 1.19.11 with no explicit bump. run 31277905072. (task 1002620)
|
|
454
|
+
// 1.19.13 — CI auto-patch (publish-on-merge, ADR 0161): carrier for merges
|
|
455
|
+
// landed since 1.19.12 with no explicit bump. run 31277989346. (task 1002620)
|
|
452
456
|
// ---------------------------------------------------------------------------
|
|
453
|
-
const CORE_VERSION = '1.19.
|
|
457
|
+
const CORE_VERSION = '1.19.13'; // CI auto-patch carrier (ADR 0161); see changelog above
|
|
454
458
|
|
|
455
459
|
// A namespaced logger so a module's log lines are attributable + consistent.
|
|
456
460
|
// Usage: const log = api.logger('dev-box'); log.info('mounted');
|
package/tests/ship_ci_deploy.mjs
CHANGED
|
@@ -544,6 +544,42 @@ test('effectiveDeployMode: tolerates a missing options object (defaults to no fa
|
|
|
544
544
|
assert.equal(effectiveDeployMode('laptop'), 'ci'); // canPushLocally undefined ⇒ falsy ⇒ fall back
|
|
545
545
|
});
|
|
546
546
|
|
|
547
|
+
// ---- 10b. effectiveDeployMode: a PROTECTED main never strands a push-capable laptop (task 1002629) ----
|
|
548
|
+
//
|
|
549
|
+
// The wave-4 strand: task 1002619 armed branch protection on main (required
|
|
550
|
+
// `unit` check), so the laptop land's direct `git push origin main` is refused
|
|
551
|
+
// by GitHub for EVERY ship. A machine WITH push credentials resolved 'laptop'
|
|
552
|
+
// and pushed straight into the wall — tasks 1002608/1002609 graded PASS, then
|
|
553
|
+
// stranded at confirmed (learning 1000177). Credential-awareness (task 1449,
|
|
554
|
+
// section 10) cannot see this: the credential is fine, the REF is protected.
|
|
555
|
+
// Same fix shape, second input. mainProtected must be STRICTLY true to trigger
|
|
556
|
+
// — unknown (null/undefined: no gh, offline, no permission) keeps today's
|
|
557
|
+
// behavior rather than silently rerouting on a failed probe.
|
|
558
|
+
|
|
559
|
+
test('effectiveDeployMode: laptop + CAN push + main PROTECTED + not env-forced → ci (the fix)', () => {
|
|
560
|
+
assert.equal(effectiveDeployMode('laptop', { canPushLocally: true, envForced: false, mainProtected: true }), 'ci');
|
|
561
|
+
});
|
|
562
|
+
|
|
563
|
+
test('effectiveDeployMode: laptop + main protected but ENV-FORCED → laptop (explicit override wins)', () => {
|
|
564
|
+
assert.equal(effectiveDeployMode('laptop', { canPushLocally: true, envForced: true, mainProtected: true }), 'laptop');
|
|
565
|
+
});
|
|
566
|
+
|
|
567
|
+
test('effectiveDeployMode: laptop + protection UNKNOWN → laptop (a failed probe must not reroute)', () => {
|
|
568
|
+
assert.equal(effectiveDeployMode('laptop', { canPushLocally: true, envForced: false, mainProtected: null }), 'laptop');
|
|
569
|
+
assert.equal(effectiveDeployMode('laptop', { canPushLocally: true, envForced: false, mainProtected: undefined }), 'laptop');
|
|
570
|
+
assert.equal(effectiveDeployMode('laptop', { canPushLocally: true, envForced: false }), 'laptop');
|
|
571
|
+
});
|
|
572
|
+
|
|
573
|
+
test('effectiveDeployMode: ci stays ci for every protection value', () => {
|
|
574
|
+
assert.equal(effectiveDeployMode('ci', { canPushLocally: true, envForced: false, mainProtected: true }), 'ci');
|
|
575
|
+
assert.equal(effectiveDeployMode('ci', { canPushLocally: true, envForced: true, mainProtected: false }), 'ci');
|
|
576
|
+
assert.equal(effectiveDeployMode('ci', { canPushLocally: false, envForced: false, mainProtected: null }), 'ci');
|
|
577
|
+
});
|
|
578
|
+
|
|
579
|
+
test('effectiveDeployMode: both fallbacks compose — credential rule fires even when protection is unknown', () => {
|
|
580
|
+
assert.equal(effectiveDeployMode('laptop', { canPushLocally: false, envForced: false, mainProtected: null }), 'ci');
|
|
581
|
+
});
|
|
582
|
+
|
|
547
583
|
// ---- task 1002606: the success-pending banner carries the strand-detection story ----
|
|
548
584
|
// Finding P-3: the banner ended at "This is NOT a failure", so when a land DID die
|
|
549
585
|
// (hand-written conflict, dead PR, red check) the builder's last signal said all was
|