@bongos/core 1.19.271 → 1.19.272

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.271",
6
- "core_contract": "1.19.271",
7
- "source_commit": "eeda0eaa782f69e7843920d6ba6027133ffaf889",
5
+ "core_version": "1.19.272",
6
+ "core_contract": "1.19.272",
7
+ "source_commit": "1ddcabf5f0c390df6bf2db273da05131d7c5555d",
8
8
  "source_ref": "HEAD",
9
- "built_at": "2026-08-21T01:46:51.577Z",
9
+ "built_at": "2026-08-21T01:58:30.188Z",
10
10
  "redaction": {
11
11
  "model": "docs-redacted+functional-verbatim",
12
12
  "docs_redacted": 318,
@@ -17,7 +17,7 @@
17
17
  "gate": "passed"
18
18
  },
19
19
  "file_count": 1709,
20
- "tree_sha256": "552e1f0cff6b4913becf9550cc1d368053a56c1022f8ed7c6f373f3028ae69fe",
20
+ "tree_sha256": "7e96896dddb704ffea439416bad0d7ba6ad81ead9d79ed34212de45a2df50f71",
21
21
  "files": [
22
22
  {
23
23
  "path": ".claude/skills/blocker-review/SKILL.md",
@@ -4472,12 +4472,12 @@
4472
4472
  {
4473
4473
  "path": "package-lock.json",
4474
4474
  "mode": "0000644",
4475
- "sha256": "289d96460bbde068311ad18505bfca432c201de66af827055b8ce2ed31ac81be"
4475
+ "sha256": "b77fc259ddf810fa62ea5e745751449a5dc49847311ddf4199a997df9dcd06ed"
4476
4476
  },
4477
4477
  {
4478
4478
  "path": "package.json",
4479
4479
  "mode": "0000644",
4480
- "sha256": "2ec9e8fb9b470df255d1de4adc52597ca9f6dac5a301adbdecd0cf249547938d"
4480
+ "sha256": "725dd9f78abd547ef32e6d9a9b886ecf447fa1bd62a747df49943e2a1e047949"
4481
4481
  },
4482
4482
  {
4483
4483
  "path": "public-docs/index.html",
@@ -4997,7 +4997,7 @@
4997
4997
  {
4998
4998
  "path": "scripts/gds/context-pack.js",
4999
4999
  "mode": "0000644",
5000
- "sha256": "8ca6ee391837d7e1a45e69fc62c4a051f5e3d6060db507cdcbc7a343e2b03bd3"
5000
+ "sha256": "caa5dd1628b00569a5593964f1831641ac12c9a85b8ec132964a1baf67c77350"
5001
5001
  },
5002
5002
  {
5003
5003
  "path": "scripts/gds/control-manifest.js",
@@ -6122,7 +6122,7 @@
6122
6122
  {
6123
6123
  "path": "src/module-api.js",
6124
6124
  "mode": "0000644",
6125
- "sha256": "bc19b591ad45c693adebe95bf3df3e480b174bac7b109df27dd45fb088e78537"
6125
+ "sha256": "67c1067ae657f640493d8bfa3f7001ce64f416d0176afd6e84be387349a9578e"
6126
6126
  },
6127
6127
  {
6128
6128
  "path": "src/module-loader/catalog.js",
@@ -6652,7 +6652,7 @@
6652
6652
  {
6653
6653
  "path": "tests/context_pack.mjs",
6654
6654
  "mode": "0000644",
6655
- "sha256": "55bd70efd8067e39d6220d14f78c743ce80d8a8ff37988acf00136b5108dc004"
6655
+ "sha256": "09f956c01f169ab7384204a5d43deefa3e57a7d57924072dfa8215f0942c2513"
6656
6656
  },
6657
6657
  {
6658
6658
  "path": "tests/control_manifest.mjs",
package/package-lock.json CHANGED
@@ -1,12 +1,12 @@
1
1
  {
2
2
  "name": "@bongos/core",
3
- "version": "1.19.271",
3
+ "version": "1.19.272",
4
4
  "lockfileVersion": 3,
5
5
  "requires": true,
6
6
  "packages": {
7
7
  "": {
8
8
  "name": "@bongos/core",
9
- "version": "1.19.271",
9
+ "version": "1.19.272",
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.271",
3
+ "version": "1.19.272",
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",
@@ -25,6 +25,7 @@
25
25
  // and must NEVER throw or block a claim.
26
26
 
27
27
  const { execFileSync } = require('node:child_process');
28
+ const path = require('node:path');
28
29
 
29
30
  // The live validators. task-classifier is a pure module (no DB, no network),
30
31
  // so importing it from a CLI is safe — same precedent as next-number.js reading
@@ -61,14 +62,70 @@ function apiContract() {
61
62
 
62
63
  // Resolve the absolute worktree root. runGit throws → fall back to cwd (which is
63
64
  // still better than nothing; the builder at least sees the directory they're in).
65
+ //
66
+ // task 1002573: also resolve WHICH KIND of tree this is. The pack used to print
67
+ // the root and then assert "this is the session's tree, not the main checkout"
68
+ // unconditionally — so claiming from the main checkout produced a sentence that
69
+ // was flatly false, and the tooling told the builder to do the one thing that
70
+ // makes the ship unmergeable (ship.js's local auto-merge has nowhere to merge
71
+ // INTO when the receiving tree is the one holding the branch — task 1002572).
72
+ //
73
+ // The signal is the same one claim.js detectWorktreeName() already uses: in the
74
+ // main checkout `--git-dir` and `--git-common-dir` resolve to the same path; in a
75
+ // linked worktree they diverge. isMainCheckout is TRI-STATE — null means "could
76
+ // not tell", and the guidance hedges rather than guessing, because this is a UX
77
+ // surface that must never block a claim.
64
78
  function resolveWorktreeRoot({ runGit, cwd }) {
79
+ let root = null;
80
+ let degraded = false;
65
81
  try {
66
- const root = runGit(['rev-parse', '--show-toplevel']).trim();
67
- if (root) return { root, degraded: false };
82
+ root = runGit(['rev-parse', '--show-toplevel']).trim() || null;
68
83
  } catch {
69
84
  /* fall through */
70
85
  }
71
- return { root: cwd, degraded: true };
86
+ if (!root) {
87
+ root = cwd;
88
+ degraded = true;
89
+ }
90
+ let isMainCheckout = null;
91
+ try {
92
+ const gitDir = runGit(['rev-parse', '--git-dir']).trim();
93
+ const commonDir = runGit(['rev-parse', '--git-common-dir']).trim();
94
+ if (gitDir && commonDir) {
95
+ // Both can come back RELATIVE ('.git' in the main checkout), so resolve
96
+ // against the tree we are describing rather than the process cwd.
97
+ const base = degraded ? cwd : root;
98
+ isMainCheckout = path.resolve(base, gitDir) === path.resolve(base, commonDir);
99
+ }
100
+ } catch {
101
+ /* unknown — the guidance hedges */
102
+ }
103
+ return { root, degraded, isMainCheckout };
104
+ }
105
+
106
+ // task 1002573: the guidance line(s) for the resolved tree. Pure, and exported so
107
+ // the "these two must not read the same" property is unit-tested rather than
108
+ // eyeballed. Returns an array of lines (the warning needs more than one).
109
+ function worktreeGuidance(worktree) {
110
+ const isMain = worktree ? worktree.isMainCheckout : null;
111
+ if (isMain === true) {
112
+ return [
113
+ ' ⚠ THIS IS THE MAIN CHECKOUT — not a per-claim worktree.',
114
+ ' Working the claim here parks the main checkout off `main`, and the local',
115
+ ' auto-merge then has nowhere to merge INTO: the ship strands at `confirmed`',
116
+ ' until the server reconciler (or /merge-mode) catches it. Automation that',
117
+ ' resets this tree mid-session can also take your uncommitted work with it.',
118
+ ' Prefer a worktree: git worktree add .claude/worktrees/<name> -b <branch>',
119
+ ];
120
+ }
121
+ if (isMain === false) {
122
+ return [' → edit files + run git HERE. This is the session\'s tree, not the main checkout (the #1 wrong-path footgun).'];
123
+ }
124
+ return [
125
+ ' → edit files + run git HERE.',
126
+ ' ⚠ Could not tell whether this is the main checkout or a linked worktree. If it is',
127
+ ' the main checkout, prefer a per-claim worktree — see the warning in /builder-claim.',
128
+ ];
72
129
  }
73
130
 
74
131
  // OS toolchain facts the corpus showed sessions tripping over. `which` returns a
@@ -148,7 +205,7 @@ function formatContextPack(pack) {
148
205
  const lines = [];
149
206
  lines.push('🧭 Context pack (so this session starts knowing, not hunting):');
150
207
  lines.push(` Worktree root: ${worktree.root}`);
151
- lines.push(' → edit files + run git HERE. This is the session\'s tree, not the main checkout (the #1 wrong-path footgun).');
208
+ lines.push(...worktreeGuidance(worktree)); // task 1002573 — tells the TRUTH about which tree this is
152
209
  if (worktree.degraded) {
153
210
  lines.push(' ⚠ git toplevel unreadable — showing cwd; double-check before writing.');
154
211
  }
@@ -217,6 +274,7 @@ module.exports = {
217
274
  API_PREFIX,
218
275
  apiContract,
219
276
  resolveWorktreeRoot,
277
+ worktreeGuidance, // task 1002573
220
278
  osFacts,
221
279
  normalizeClaims,
222
280
  buildContextPack,
package/src/module-api.js CHANGED
@@ -969,8 +969,10 @@ const { validateOrRespond, LIMITS, parseId, asyncHandler, corsPublicGet, parsePa
969
969
  // landed since 1.19.269 with no explicit bump. run 32428659648. (task 1002620)
970
970
  // 1.19.271 — CI auto-patch (publish-on-merge, ADR 0161): carrier for merges
971
971
  // landed since 1.19.270 with no explicit bump. run 32437463473. (task 1002620)
972
+ // 1.19.272 — CI auto-patch (publish-on-merge, ADR 0161): carrier for merges
973
+ // landed since 1.19.271 with no explicit bump. run 32438140524. (task 1002620)
972
974
  // ---------------------------------------------------------------------------
973
- const CORE_VERSION = '1.19.271'; // CI auto-patch carrier (ADR 0161); see changelog above
975
+ const CORE_VERSION = '1.19.272'; // CI auto-patch carrier (ADR 0161); see changelog above
974
976
 
975
977
  // A namespaced logger so a module's log lines are attributable + consistent.
976
978
  // Usage: const log = api.logger('dev-box'); log.info('mounted');
@@ -17,6 +17,7 @@ const require = createRequire(import.meta.url);
17
17
  const {
18
18
  apiContract,
19
19
  resolveWorktreeRoot,
20
+ worktreeGuidance,
20
21
  osFacts,
21
22
  normalizeClaims,
22
23
  buildContextPack,
@@ -177,6 +178,104 @@ t('buildContextPack: composes worktree + claims + api + os from injected runners
177
178
  assert.equal(pack.os.platform, 'linux');
178
179
  });
179
180
 
181
+ // ---- task 1002573: which tree is this, and does the guidance tell the truth ----
182
+ //
183
+ // The pack used to print the root and then assert "this is the session's tree,
184
+ // not the main checkout" UNCONDITIONALLY. Claiming from the main checkout
185
+ // therefore produced a flatly false sentence, and the tooling told the builder to
186
+ // do the one thing that makes the ship unmergeable (the receiving tree is the one
187
+ // holding the branch — task 1002572). The signal was already computed elsewhere
188
+ // (claim.js detectWorktreeName) and simply never surfaced.
189
+ //
190
+ // Stubs mirror real git: in the MAIN checkout `--git-dir` and `--git-common-dir`
191
+ // both answer '.git'; in a LINKED worktree they diverge.
192
+ const gitMain = (args) => {
193
+ const a = args.join(' ');
194
+ if (a.includes('--show-toplevel')) return '/repo\n';
195
+ if (a.includes('--git-common-dir')) return '.git\n';
196
+ if (a.includes('--git-dir')) return '.git\n';
197
+ throw new Error(`unexpected git call: ${a}`);
198
+ };
199
+ const gitLinked = (args) => {
200
+ const a = args.join(' ');
201
+ if (a.includes('--show-toplevel')) return '/repo/.claude/worktrees/task-1\n';
202
+ if (a.includes('--git-common-dir')) return '/repo/.git\n';
203
+ if (a.includes('--git-dir')) return '/repo/.git/worktrees/task-1\n';
204
+ throw new Error(`unexpected git call: ${a}`);
205
+ };
206
+
207
+ t('resolveWorktreeRoot: identical git-dir and common-dir ⇒ the MAIN checkout', () => {
208
+ const r = resolveWorktreeRoot({ runGit: gitMain, cwd: '/elsewhere' });
209
+ assert.equal(r.root, '/repo');
210
+ assert.equal(r.degraded, false);
211
+ assert.equal(r.isMainCheckout, true);
212
+ });
213
+
214
+ t('resolveWorktreeRoot: diverging git-dir ⇒ a LINKED worktree', () => {
215
+ const r = resolveWorktreeRoot({ runGit: gitLinked, cwd: '/elsewhere' });
216
+ assert.equal(r.root, '/repo/.claude/worktrees/task-1');
217
+ assert.equal(r.isMainCheckout, false);
218
+ });
219
+
220
+ t('resolveWorktreeRoot: unresolvable tree kind is UNKNOWN (null), never a guess', () => {
221
+ // toplevel works, but the dir probes fail — the kind must not default to either.
222
+ const partial = (args) => {
223
+ if (args.join(' ').includes('--show-toplevel')) return '/repo\n';
224
+ throw new Error('no');
225
+ };
226
+ assert.equal(resolveWorktreeRoot({ runGit: partial, cwd: '/c' }).isMainCheckout, null);
227
+ // And a total git failure still degrades to cwd without throwing.
228
+ const dead = resolveWorktreeRoot({ runGit: () => { throw new Error('no git'); }, cwd: '/here' });
229
+ assert.equal(dead.root, '/here');
230
+ assert.equal(dead.degraded, true);
231
+ assert.equal(dead.isMainCheckout, null);
232
+ });
233
+
234
+ t('worktreeGuidance: the main-checkout text DIFFERS from the worktree text', () => {
235
+ // The task's own verify criterion: the guidance must not read the same in both
236
+ // trees. Anything else means the builder is being told the same thing whether
237
+ // it is true or not.
238
+ const mainText = worktreeGuidance({ isMainCheckout: true }).join('\n');
239
+ const wtText = worktreeGuidance({ isMainCheckout: false }).join('\n');
240
+ assert.notEqual(mainText, wtText);
241
+ // The old, wrong sentence must NOT appear in the main checkout.
242
+ assert.doesNotMatch(mainText, /not the main checkout/i,
243
+ 'the main checkout must never be told it is not the main checkout');
244
+ assert.match(wtText, /not the main checkout/i, 'a real worktree keeps the original line');
245
+ });
246
+
247
+ t('worktreeGuidance: the main-checkout warning names the CONSEQUENCE, not just the fact', () => {
248
+ const text = worktreeGuidance({ isMainCheckout: true }).join('\n');
249
+ assert.match(text, /MAIN CHECKOUT/);
250
+ assert.match(text, /auto-merge/i, 'says what breaks');
251
+ assert.match(text, /confirmed/, 'and where the ship strands');
252
+ assert.match(text, /git worktree add/, 'and gives the command that avoids it');
253
+ });
254
+
255
+ t('worktreeGuidance: unknown tree kind hedges instead of asserting either', () => {
256
+ for (const w of [{ isMainCheckout: null }, {}, null, undefined]) {
257
+ const text = worktreeGuidance(w).join('\n');
258
+ assert.match(text, /Could not tell/i, `hedged for ${JSON.stringify(w)}`);
259
+ assert.doesNotMatch(text, /THIS IS THE MAIN CHECKOUT/, 'never assert main');
260
+ assert.doesNotMatch(text, /This is the session's tree/, 'never assert worktree');
261
+ }
262
+ });
263
+
264
+ t('formatContextPack: renders the main-checkout WARNING when that is the truth', () => {
265
+ const pack = buildContextPack({
266
+ activeClaims: [{ task_id: 942, title: 'x' }],
267
+ currentTaskId: 942,
268
+ cwd: '/fallback',
269
+ platform: 'linux',
270
+ runGit: gitMain,
271
+ runWhich: fakeWhich({ timeout: '/usr/bin/timeout', psql: '/usr/bin/psql' }),
272
+ });
273
+ const out = formatContextPack(pack);
274
+ assert.match(out, /THIS IS THE MAIN CHECKOUT/);
275
+ assert.doesNotMatch(out, /not the main checkout/i);
276
+ assert.ok(!/undefined/.test(out), 'no undefined leaked into the rendered pack');
277
+ });
278
+
180
279
  // ---- formatting ------------------------------------------------------------
181
280
  t('formatClaims: empty vs held, with the (this one) marker', () => {
182
281
  assert.equal(formatClaims([]), '(none held)');
@@ -194,16 +293,20 @@ t('formatOs: names the macOS no-timeout case in prose', () => {
194
293
  });
195
294
 
196
295
  t('formatContextPack: includes worktree root, prefix, a real kind, and warns the wrong-tree footgun', () => {
296
+ // task 1002573: uses a LINKED-worktree stub. It previously passed an
297
+ // args-ignoring stub, which returned the same path for every probe — i.e. the
298
+ // main checkout — while asserting the "not the main checkout" line. The test
299
+ // was encoding the bug.
197
300
  const pack = buildContextPack({
198
301
  activeClaims: [{ task_id: 942, title: 'x' }],
199
302
  currentTaskId: 942,
200
303
  cwd: '/fallback',
201
304
  platform: 'linux',
202
- runGit: () => '/work/tree\n',
305
+ runGit: gitLinked,
203
306
  runWhich: fakeWhich({ timeout: '/usr/bin/timeout', psql: '/usr/bin/psql' }),
204
307
  });
205
308
  const out = formatContextPack(pack);
206
- assert.match(out, /\/work\/tree/);
309
+ assert.match(out, /task-1/);
207
310
  assert.match(out, /\/api\/bongos/);
208
311
  assert.match(out, /feature/); // a real kind from VALID_KINDS
209
312
  assert.match(out, /not the main checkout/i);