@bongos/core 1.19.1028 → 1.19.1029

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.1028",
6
- "core_contract": "1.19.1028",
7
- "source_commit": "56463d9a30a9ee0f32df461962daa7af33212ae5",
5
+ "core_version": "1.19.1029",
6
+ "core_contract": "1.19.1029",
7
+ "source_commit": "9061b1b634a11e0d3737336b0d59fe110d2a7a17",
8
8
  "source_ref": "HEAD",
9
- "built_at": "2026-09-25T20:58:56.481Z",
9
+ "built_at": "2026-09-25T21:07:08.900Z",
10
10
  "redaction": {
11
11
  "model": "docs-redacted+functional-verbatim",
12
12
  "docs_redacted": 529,
@@ -17,7 +17,7 @@
17
17
  "gate": "passed"
18
18
  },
19
19
  "file_count": 3014,
20
- "tree_sha256": "3ca110c17a46fb54f2debd7630fb29f210243604a8c10d3e7cef945f794019eb",
20
+ "tree_sha256": "1305b1deb2512746e9b12451812ed65ff0e0364e416b1cbdc3ffacb9d12b8851",
21
21
  "files": [
22
22
  {
23
23
  "path": ".claude/skills/ask-for-help/SKILL.md",
@@ -2757,7 +2757,7 @@
2757
2757
  {
2758
2758
  "path": "docs/module-api-changelog.md",
2759
2759
  "mode": "0000644",
2760
- "sha256": "0857762db09c3257ba652f6ddc7210e09cda9663c470b79f81441090e2d64f7f"
2760
+ "sha256": "bc6a32426c475f26a5257ae634f5cf8c9c0863c53edd9b8981c411323b2f21e4"
2761
2761
  },
2762
2762
  {
2763
2763
  "path": "docs/modules-contract.md",
@@ -5882,7 +5882,7 @@
5882
5882
  {
5883
5883
  "path": "modules/lifecycle/db-goals.js",
5884
5884
  "mode": "0000644",
5885
- "sha256": "a36a23b4f67dc43848bc0798e7b9724aee1290799dc8fa1b22e5260268bf5c70"
5885
+ "sha256": "e15bf5a3f2ec8ede9b5a8c90c3e00c472c229f351b809333beebfeb90dad2bcc"
5886
5886
  },
5887
5887
  {
5888
5888
  "path": "modules/lifecycle/db-grade.js",
@@ -6192,7 +6192,7 @@
6192
6192
  {
6193
6193
  "path": "modules/lifecycle/routes/goals.js",
6194
6194
  "mode": "0000644",
6195
- "sha256": "654addc8813a815d862da35677bb823a5d98a3685d03653fdbcb58a64aca38f4"
6195
+ "sha256": "520b63212462c762fdf23e40ffdb084c22f786616d3a5694b4dc182e334eeaa7"
6196
6196
  },
6197
6197
  {
6198
6198
  "path": "modules/lifecycle/routes/help-requests.js",
@@ -8362,12 +8362,12 @@
8362
8362
  {
8363
8363
  "path": "package-lock.json",
8364
8364
  "mode": "0000644",
8365
- "sha256": "8a41416b983d8c2f82e4cdb0c64856dd5a304ca8414a723f648ea9b072a8f076"
8365
+ "sha256": "8608f78a458012a57ffc19e813402909f783c77bcc50f3d45780e4f8b2e3f759"
8366
8366
  },
8367
8367
  {
8368
8368
  "path": "package.json",
8369
8369
  "mode": "0000644",
8370
- "sha256": "c5a4583b1eb2bd9fafb13ecb68661a6e1f81fbbbcf4444155864af2782a0232d"
8370
+ "sha256": "32e6fc0b4953d6f15b41649ea9df77918f37ac8a8ed7168ce17d9040d04bf5c6"
8371
8371
  },
8372
8372
  {
8373
8373
  "path": "public-docs/index.html",
@@ -10412,7 +10412,7 @@
10412
10412
  {
10413
10413
  "path": "src/module-api.js",
10414
10414
  "mode": "0000644",
10415
- "sha256": "af6c6577a4ca4d909583bebf81192b552bc96b47dc43f387e0468fb7bdf306bd"
10415
+ "sha256": "9763f23d6375a13a9ce55193be5689f74c04cd9170079735bca1956524f007c7"
10416
10416
  },
10417
10417
  {
10418
10418
  "path": "src/module-loader/catalog.js",
@@ -11982,7 +11982,7 @@
11982
11982
  {
11983
11983
  "path": "tests/goal_member_kinds.mjs",
11984
11984
  "mode": "0000644",
11985
- "sha256": "3bb40fceeeeaa835db715b724ac73475be2a9ae166c740e9daa78eafc6957b03"
11985
+ "sha256": "5ae48afae42839d5b8ea6002b5d9c892d027be87eda2e60bded481437a3306a6"
11986
11986
  },
11987
11987
  {
11988
11988
  "path": "tests/goal_membership_requests.mjs",
@@ -2535,5 +2535,7 @@ is load-bearing: the script throws rather than guess if it is missing, and
2535
2535
  landed since 1.19.1026 with no explicit bump. run 36187742785. (task 1002620)
2536
2536
  1.19.1028 — CI auto-patch (publish-on-merge, ADR 0161): carrier for merges
2537
2537
  landed since 1.19.1027 with no explicit bump. run 36188894725. (task 1002620)
2538
+ 1.19.1029 — CI auto-patch (publish-on-merge, ADR 0161): carrier for merges
2539
+ landed since 1.19.1028 with no explicit bump. run 36189693139. (task 1002620)
2538
2540
  ---------------------------------------------------------------------------
2539
2541
  ```
@@ -942,8 +942,14 @@ async function setGoalMembershipKind({ goalId, builderId, membershipKind }, deps
942
942
  // "drops to manager" — exactly the handoff semantics the route promises. The
943
943
  // route gates WHO may call this (owner | Archon, target Metic+); this helper
944
944
  // just performs the move. Returns the updated goal row, or null for unknown id.
945
+ //
946
+ // The new owner arriving as 'lead' is a membership arrival like any other, so it
947
+ // also closes that builder's still-pending ask/invite on this goal (task
948
+ // 1003661, the third path after addGoalMember — see its comment for why
949
+ // 'cancelled'). Same statement, so the cancel commits with the transfer or not
950
+ // at all; resolvedBy is the ACTOR handing the goal over.
945
951
 
946
- async function transferGoalOwnership({ goalId, newOwnerId }, deps = {}) {
952
+ async function transferGoalOwnership({ goalId, newOwnerId, resolvedBy = null }, deps = {}) {
947
953
  const activePool = (deps.pool && process.env.NODE_ENV === 'test') ? deps.pool : pool;
948
954
  const { rows } = await activePool.query(
949
955
  `WITH g AS (
@@ -955,9 +961,15 @@ async function transferGoalOwnership({ goalId, newOwnerId }, deps = {}) {
955
961
  INSERT INTO goal_members (goal_id, builder_id, membership_kind)
956
962
  SELECT g.id, $2, 'lead' FROM g
957
963
  ON CONFLICT (goal_id, builder_id) DO UPDATE SET membership_kind = 'lead'
964
+ ), cancelled AS (
965
+ UPDATE goal_membership_requests
966
+ SET status = 'cancelled', resolved_by = $3, resolved_at = now(), updated_at = now()
967
+ WHERE goal_id = $1 AND builder_id = $2 AND status = 'pending'
968
+ AND EXISTS (SELECT 1 FROM g)
969
+ RETURNING id
958
970
  )
959
971
  SELECT * FROM g`,
960
- [goalId, newOwnerId]
972
+ [goalId, newOwnerId, resolvedBy]
961
973
  );
962
974
  return rows[0] ?? null;
963
975
  }
@@ -899,7 +899,7 @@ module.exports = function buildGoalsRouter() {
899
899
  if (decision.noop) {
900
900
  return res.json({ ok: true, goal, changed: false });
901
901
  }
902
- const updated = await db.transferGoalOwnership({ goalId: id, newOwnerId });
902
+ const updated = await db.transferGoalOwnership({ goalId: id, newOwnerId, resolvedBy: req.builder.id });
903
903
  if (!updated) {
904
904
  return res.fail('transfer_conflict', { status: 409, message: 'The goal changed underneath this request — re-read it and retry.' });
905
905
  }
package/package-lock.json CHANGED
@@ -1,12 +1,12 @@
1
1
  {
2
2
  "name": "@bongos/core",
3
- "version": "1.19.1028",
3
+ "version": "1.19.1029",
4
4
  "lockfileVersion": 3,
5
5
  "requires": true,
6
6
  "packages": {
7
7
  "": {
8
8
  "name": "@bongos/core",
9
- "version": "1.19.1028",
9
+ "version": "1.19.1029",
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.1028",
3
+ "version": "1.19.1029",
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/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.1028'; // CI auto-patch carrier (ADR 0161); changelog: docs/module-api-changelog.md
74
+ const CORE_VERSION = '1.19.1029'; // 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');
@@ -400,7 +400,7 @@ await test('transferGoalOwnership: created_by moves and the new owner upserts to
400
400
  const out = await transferGoalOwnership({ goalId: 7, newOwnerId: 5 }, { pool });
401
401
  assert.deepEqual(out, GOAL_ROW);
402
402
  const upd = find(pool.queries, /UPDATE goals SET created_by = \$2/);
403
- assert.deepEqual(upd.params, [7, 5]);
403
+ assert.deepEqual(upd.params, [7, 5, null], 'resolvedBy defaults to null for a non-route caller');
404
404
  const sql = String(upd.sql).replace(/\s+/g, ' ');
405
405
  assert.match(sql, /INSERT INTO goal_members \(goal_id, builder_id, membership_kind\)/,
406
406
  'the new owner\'s membership rides in the same statement');
@@ -409,6 +409,29 @@ await test('transferGoalOwnership: created_by moves and the new owner upserts to
409
409
  assert.match(sql, /updated_at = now\(\)/);
410
410
  });
411
411
 
412
+ // task 1003661 — the third arrival path (after join / direct-add, task 1002888):
413
+ // handing a goal to a builder who still has a pending ask outstanding on it must
414
+ // close that ask in the SAME statement, or the stale row lingers in the inbox.
415
+ await test('transferGoalOwnership: cancels the new owner\'s pending ask in the same statement, attributed to the actor', async () => {
416
+ const pool = fakePool((s) => (/UPDATE goals SET created_by = \$2/.test(s) ? { rows: [GOAL_ROW] } : null));
417
+ await transferGoalOwnership({ goalId: 7, newOwnerId: 5, resolvedBy: 99 }, { pool });
418
+ assert.equal(pool.queries.length, 1, 'ONE statement — the cancel commits with the transfer or not at all');
419
+ const upd = find(pool.queries, /UPDATE goals SET created_by = \$2/);
420
+ assert.deepEqual(upd.params, [7, 5, 99], 'goal, new owner, actor');
421
+ const sql = String(upd.sql).replace(/\s+/g, ' ');
422
+ assert.match(sql, /UPDATE goal_membership_requests SET status = 'cancelled', resolved_by = \$3/);
423
+ assert.match(sql, /WHERE goal_id = \$1 AND builder_id = \$2 AND status = 'pending'/,
424
+ 'scoped to this goal + new owner, pending rows only');
425
+ assert.doesNotMatch(sql, /status = 'accepted'/);
426
+ assert.match(sql, /AND EXISTS \(SELECT 1 FROM g\)/, 'an unknown goal (no g row) must cancel nothing');
427
+ });
428
+
429
+ await test('routes: POST /goals/:id/transfer passes resolvedBy to transferGoalOwnership', async () => {
430
+ const { readFileSync } = await import('node:fs');
431
+ const src = readFileSync(new URL('../modules/lifecycle/routes/goals.js', import.meta.url), 'utf8');
432
+ assert.match(src, /db\.transferGoalOwnership\(\{[^}]*resolvedBy:\s*req\.builder\.id/);
433
+ });
434
+
412
435
  await test('transferGoalOwnership: unknown goal → null', async () => {
413
436
  const pool = fakePool(() => ({ rows: [] }));
414
437
  assert.equal(await transferGoalOwnership({ goalId: 999, newOwnerId: 5 }, { pool }), null);