@bongos/core 1.19.858 → 1.19.859

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.858",
6
- "core_contract": "1.19.858",
7
- "source_commit": "afc4e781c80a292d23180b498c8a15f9483bcedb",
5
+ "core_version": "1.19.859",
6
+ "core_contract": "1.19.859",
7
+ "source_commit": "88f0482054c54e919261f2a12807d600816ed82a",
8
8
  "source_ref": "HEAD",
9
- "built_at": "2026-09-17T22:01:14.430Z",
9
+ "built_at": "2026-09-17T22:12:12.572Z",
10
10
  "redaction": {
11
11
  "model": "docs-redacted+functional-verbatim",
12
12
  "docs_redacted": 498,
@@ -17,7 +17,7 @@
17
17
  "gate": "passed"
18
18
  },
19
19
  "file_count": 2804,
20
- "tree_sha256": "34a2816607bed641f30b7a13611a257ff4330ca477e9b6fb6e03ed7137e20ad5",
20
+ "tree_sha256": "b78ad9f6f954e75b5baea41887323131be92e116c1966a2199cca319f8b0b875",
21
21
  "files": [
22
22
  {
23
23
  "path": ".claude/skills/ask-for-help/SKILL.md",
@@ -1277,7 +1277,7 @@
1277
1277
  {
1278
1278
  "path": "docs/adr/0161-publish-on-merge.md",
1279
1279
  "mode": "0000644",
1280
- "sha256": "c96d62b3b9eb4945cd7e55ccc361c230f5c5ca559d187d6156c330e0f9f30e49"
1280
+ "sha256": "c3da608c2b3d17ceb9208abfadd8ed218654fcc67cc545528683da8a624834a0"
1281
1281
  },
1282
1282
  {
1283
1283
  "path": "docs/adr/0162-review-gates-retired-rank-consistent-ci-only.md",
@@ -2062,12 +2062,12 @@
2062
2062
  {
2063
2063
  "path": "docs/copy-inventory.md",
2064
2064
  "mode": "0000644",
2065
- "sha256": "4f6e17550f44d1974e98338d2ab1bd59bce9681c741ebbcdad7c35d5105d1c10"
2065
+ "sha256": "ae1018c0897a5358ea2c5a37aeab4a693400a5c2e3ac8e02eb8fbc805dbd0af0"
2066
2066
  },
2067
2067
  {
2068
2068
  "path": "docs/copy-registry.json",
2069
2069
  "mode": "0000644",
2070
- "sha256": "cdfb4dc21b70a0b6ddba18951431a44e103e95be84ebb07e6fcba3ad2030379a"
2070
+ "sha256": "34b45e28825cdd2cc46087b6aa96fae19931da5561275ba2df7f5121daf23d6a"
2071
2071
  },
2072
2072
  {
2073
2073
  "path": "docs/design/apex-pass-2-direction.md",
@@ -2592,7 +2592,7 @@
2592
2592
  {
2593
2593
  "path": "docs/module-api-changelog.md",
2594
2594
  "mode": "0000644",
2595
- "sha256": "56c034df0c7eb96ada2eed6aa17ce5e3485716901c6215c72eb247276a091ca9"
2595
+ "sha256": "1e68c4750a4f88d910f87d06d8f99acaabe7c8c0c393a3da8b94ecd9b3b32ca8"
2596
2596
  },
2597
2597
  {
2598
2598
  "path": "docs/modules-contract.md",
@@ -4952,7 +4952,7 @@
4952
4952
  {
4953
4953
  "path": "modules/hall-ui/public/goals-page.js",
4954
4954
  "mode": "0000644",
4955
- "sha256": "70846002f577ca160ea9f819e1ea550b7c79f80b4f0f29780e959613f388d46e"
4955
+ "sha256": "22511ab390f5b4664f6754b9c025d8949b5708b0a106e978f32aa7b49bb93938"
4956
4956
  },
4957
4957
  {
4958
4958
  "path": "modules/hall-ui/public/goals-render.js",
@@ -5472,7 +5472,7 @@
5472
5472
  {
5473
5473
  "path": "modules/hall-ui/public/work.js",
5474
5474
  "mode": "0000644",
5475
- "sha256": "6e72e2bdb5016937ccace00ec085265b0e71488d98f33883e2b4e0cc01117ce8"
5475
+ "sha256": "ea864a92f67de2a6d715a7372bd7b4ed5d05b90e4f6864b634b71342ed2debe8"
5476
5476
  },
5477
5477
  {
5478
5478
  "path": "modules/hall-ui/public/work.states.json",
@@ -7917,12 +7917,12 @@
7917
7917
  {
7918
7918
  "path": "package-lock.json",
7919
7919
  "mode": "0000644",
7920
- "sha256": "66f32ef74e691565c2cb563b55bf7276e51a0fb60ac2e993a92ed490e4124191"
7920
+ "sha256": "2d544ea0fed5368d5c140a2e44b18b8265be912369bd47dcf78b5fcfaef6d739"
7921
7921
  },
7922
7922
  {
7923
7923
  "path": "package.json",
7924
7924
  "mode": "0000644",
7925
- "sha256": "52c154531637a295f97e1352c9506ab2096351f701953d257097a94bef201ccf"
7925
+ "sha256": "3aa07b12e96b6217ec805ae73ff8d535d1ac92c4c5d00d6074d8e92afd2672cb"
7926
7926
  },
7927
7927
  {
7928
7928
  "path": "public-docs/index.html",
@@ -8192,7 +8192,7 @@
8192
8192
  {
8193
8193
  "path": "scripts/gds/bump-version.js",
8194
8194
  "mode": "0000644",
8195
- "sha256": "18cdfd285c4037da0f60d53bf8736aa6e6185022c79533ed81a4c9aa9a370392"
8195
+ "sha256": "62c5350636e3606c577fb134150db464ddc2eb3c5ce22b063a13dae3748a500e"
8196
8196
  },
8197
8197
  {
8198
8198
  "path": "scripts/gds/capture.js",
@@ -9332,7 +9332,7 @@
9332
9332
  {
9333
9333
  "path": "scripts/gds/smoke-dependencies.sh",
9334
9334
  "mode": "0000644",
9335
- "sha256": "451d843d1bba784dff10979a8fe7fd25bd5cdb2a0a00cc4e6e00eb84896f9aee"
9335
+ "sha256": "de4dd849f6b4b641f33c4df0672f42c83c3012b8458c618af1c6add9dfb32734"
9336
9336
  },
9337
9337
  {
9338
9338
  "path": "scripts/gds/smoke-gds.js",
@@ -9867,7 +9867,7 @@
9867
9867
  {
9868
9868
  "path": "src/module-api.js",
9869
9869
  "mode": "0000644",
9870
- "sha256": "afc74def8759f073e583a0ec5677587d849e1d8534f704bd1560cb018a16dc10"
9870
+ "sha256": "6c6509130bcd722c09a1aca7f9b4f9bcd82200c2e47b9b68659d0b831ac606e5"
9871
9871
  },
9872
9872
  {
9873
9873
  "path": "src/module-loader/catalog.js",
@@ -10502,7 +10502,7 @@
10502
10502
  {
10503
10503
  "path": "tests/bump_version.mjs",
10504
10504
  "mode": "0000644",
10505
- "sha256": "1a96e58f266d58dfc0f5fc9381df4b3348171b5cfe81e91161d884b34208ee6c"
10505
+ "sha256": "6540646400fb4464ee823b841f908f8577d170f60c4b4928fbbfc2a3746b76d4"
10506
10506
  },
10507
10507
  {
10508
10508
  "path": "tests/canonical_profile_url.mjs",
@@ -11652,7 +11652,7 @@
11652
11652
  {
11653
11653
  "path": "tests/hall_error_envelope.mjs",
11654
11654
  "mode": "0000644",
11655
- "sha256": "1d9c1d345a6fe25eacf810703a3e7f9f3b33f8e982aef49364e19eb4469de668"
11655
+ "sha256": "57a013b7955cdfe0edf19fcdf24dbfdfda60be625c94d2c32f258772cbd5e140"
11656
11656
  },
11657
11657
  {
11658
11658
  "path": "tests/hall_kit_charts.mjs",
@@ -22,6 +22,12 @@ The consumption side (ADR 0136's subscription) was never the problem; supply was
22
22
 
23
23
  1. **Every merge to `main` publishes.** A `publish` workflow fires on the `unit` check succeeding on a main push (never on a red suite), builds via `package-core` — the fail-closed no-leak gate and `--self-verify` pin check run inside the build — publishes (restricted), and tags `core-v<version>`.
24
24
  2. **CI bumps PATCH only.** When a merge lands without a human bump, `scripts/gds/bump-version.js` bumps the patch and writes the changelog line (shape-preserving; pinned by `tests/bump_version.mjs`). A **minor or major stays a deliberate in-task human edit** — it is a statement about the doorway surface, and it decides what patch-channel subscribers receive unattended (ADR 0136). CI is not allowed to make that call.
25
+
26
+ **The promise was unkeepable until task 1003526 (2026-09-17).** A branch that hand-bumped had no commit shape the lane recognised, so `tests/bump_version.mjs`'s LIVE check failed its own PR's `unit` check by construction — that is what killed task 1003044's first ship (PR #391) — and had one merged, `publish.yml`'s record step (point 5) fires only when *CI* bumped, so the hand-cut version would have been re-packed by every later merge and **nothing new would ever have published again**. A stalled lane, not just a blocked PR. The owner chose to teach the lane the human shape rather than retire the promise.
27
+
28
+ A human release is now a first-class commit: `release: core X.Y.Z — <reason>`, matched by `HUMAN_RELEASE_COMMIT_RE` — deliberately the complement of the CI carrier's `RELEASE_COMMIT_RE`, so no subject reads as both. The two predicates now answer **different questions**, which is the correction: `isReleaseCommit` (the cascade terminator) says **no** to a human carrier, because that version is new and still needs packaging; `laneReleasedVersions` says **yes**, because the number is claimed and the lane must not publish onto it. Forcing them to agree was the defect.
29
+
30
+ The cost is one version number: declaring 1.20.0 publishes **1.20.1**, because `publish.yml` reads `taken=true`, bumps one patch past the claim, and records its own carrier — which is exactly what keeps the lane moving. Publishing the declared number *exactly* would mean teaching the record step to fire without a bump, and `.github/` is a protected surface on the lane that already minted-and-lost 1.19.716–723; the digit is not worth that edit. The minor/major signal downstream consumers actually read is preserved.
25
31
  3. **Token custody reverses, knowingly.** A standing npm **write** Automation token (`NPM_TOKEN`) and a fine-grained **contents:write** PAT (`RELEASE_PUSH_TOKEN`, for the bump commit — the default `GITHUB_TOKEN` cannot push past the required `unit` check) live as GitHub Actions secrets. This is exactly the surface 0137 declined; the owner accepted it with the incidents as evidence. **Superseded for the npm half at task 1003202 (2026-08-25):** the write token is gone — the lane publishes via OIDC trusted publishing (ADR 0180), so no long-lived npm credential exists to steal. `RELEASE_PUSH_TOKEN` remains, as the bump commit still needs contents:write. Mitigations: the lane is **default-inert** behind the repo variable `PUBLISH_ON_MERGE=1` (an owner kill-switch symmetrical to `gating.js`), packages publish `restricted`, and the no-leak gate structurally cannot upload a leaking artifact.
26
32
  4. **Idempotent by version-compare.** The bump commit re-triggers `unit` → `publish`, which then finds HEAD's version already on npm and no-ops. npm's duplicate-version rejection is the backstop; concurrent merges queue behind a no-cancel concurrency group.
27
33
  5. **The bump commit is pushed only after the tarball is on the registry** (amended at task 1003914, 2026-09-13). The bump is *committed* before `package-core` runs — it stamps the version found at the ref — but the **push is the last step**, after `npm publish` returns. Originally it ran first, which made a pack or publish failure permanently lossy: `main` declared a version the registry never had, and the cascade terminator (point 4) then no-opped every following run, so only a fresh work merge could move on — bumping *past* the hole. Versions 1.19.716–723 were minted and lost exactly that way during a three-hour leak-gate fault (task 1003873). Pushing last makes the failure discard a purely local commit, and the next run retries the **same** version.
@@ -54,7 +54,7 @@ Identical copy within one surface. Sometimes right (a repeated button), sometime
54
54
  | `e57ec24e13ef` | 4x | Sign in with GitHub | modules/hall-ui/public/hall-render.js:275, modules/hall-ui/public/hall-render.js:346, modules/hall-ui/public/hall-render.js:415 |
55
55
  | `2ae36be1c531` | 4x | Start a project | modules/public-landing/public/index.html:727, modules/public-landing/public/index.html:891, modules/public-landing/public/projects.html:509 |
56
56
  | `eff70e59d12e` | 4x | Task {…} | modules/hall-ui/public/task.js:494, modules/hall-ui/public/task.js:530, modules/hall-ui/public/task.js:959 |
57
- | `b31fc908c962` | 4x | You claimed #{…}. Open a terminal to start work. | modules/hall-ui/public/goals-page.js:418, modules/hall-ui/public/goals-page.js:515, modules/hall-ui/public/work.js:904 |
57
+ | `b31fc908c962` | 4x | You claimed #{…}. Open a terminal to start work. | modules/hall-ui/public/goals-page.js:418, modules/hall-ui/public/goals-page.js:517, modules/hall-ui/public/work.js:904 |
58
58
  | `1de02052abf2` | 3x | Available from the {…} rank up. | modules/hall-ui/public/gate.js:222, modules/hall-ui/public/government.js:882, modules/hall-ui/public/harbor.js:340 |
59
59
  | `08afb1257b71` | 3x | Back to home | modules/hall-ui/public/drachmae.html:87, modules/hall-ui/public/primer.html:90, modules/hall-ui/public/primer.js:283 |
60
60
  | `118026595c16` | 3x | Board Room | modules/hall-ui/public/board-room.html:19, modules/hall-ui/public/board-room.html:19, modules/hall-ui/public/board-room.html:31 |
@@ -65,7 +65,7 @@ Identical copy within one surface. Sometimes right (a repeated button), sometime
65
65
  | `f6f68ba322be` | 3x | Open {…} | modules/hall-ui/public/goal-inbox.js:53, modules/hall-ui/public/goal-inbox.js:69, modules/hall-ui/public/goals.js:67 |
66
66
  | `e08628967f2f` | 3x | Project settings | modules/hall-ui/public/project-settings.html:15, modules/hall-ui/public/project-settings.html:15, modules/hall-ui/public/project-settings.html:26 |
67
67
  | `45f7bea67776` | 3x | Reading the project’s settings… | modules/public-landing/public/projects.html:791, modules/public-landing/public/projects.html:806, modules/public-landing/public/projects.html:825 |
68
- | `aa4db91b5732` | 3x | Released #{…}. It returns to the queue. | modules/hall-ui/public/goals-page.js:538, modules/hall-ui/public/work.js:945, modules/hall-ui/public/work.js:998 |
68
+ | `aa4db91b5732` | 3x | Released #{…}. It returns to the queue. | modules/hall-ui/public/goals-page.js:540, modules/hall-ui/public/work.js:945, modules/hall-ui/public/work.js:998 |
69
69
  | `df38941b25a6` | 3x | Sign in with GitHub | modules/public-landing/public/projects.html:621, modules/public-landing/public/projects.html:693, modules/public-landing/public/projects.html:994 |
70
70
  | `e9a366866022` | 3x | This goal is not open. | modules/hall-ui/public/goals-render.js:1279, modules/hall-ui/public/goals-render.js:1301, modules/hall-ui/public/goals-render.js:1317 |
71
71
  | `ed394c66d49c` | 3x | What the choice changes | modules/public-landing/public/projects.html:849, modules/public-landing/public/projects.html:908, modules/public-landing/public/projects.html:920 |
@@ -487,7 +487,7 @@ Identical copy within one surface. Sometimes right (a repeated button), sometime
487
487
  | `9e70797b7e97` | certain | Goal id | `modules/hall-ui/public/goals-render.js:824` |
488
488
  | `529460e82058` | certain | Goal invitations to you | `modules/hall-ui/public/goal-inbox.js:113` |
489
489
  | `e2a9e33abe60` | certain | Goal tasks | `modules/hall-ui/public/goals.html:136` |
490
- | `e0bc28d10785` | certain | Goals could not be loaded. Refresh to try again. | `modules/hall-ui/public/goals-page.js:896` |
490
+ | `e0bc28d10785` | certain | Goals could not be loaded. Refresh to try again. | `modules/hall-ui/public/goals-page.js:898` |
491
491
  | `4517f2253d0f` | certain | Goals couldn’t be loaded just now. Refresh to try again. | `modules/hall-ui/public/goals.js:121` |
492
492
  | `6a85997bda21` | certain | Goals created | `modules/hall-ui/public/profile.html:145` |
493
493
  | `2ed2cb8fbffd` | certain | Goals page | `modules/hall-ui/public/goals.js:94` |
@@ -546,7 +546,7 @@ Identical copy within one surface. Sometimes right (a repeated button), sometime
546
546
  | `5c2e2bcbd5fb` | certain | In Progress | `modules/hall-ui/public/work.html:95` |
547
547
  | `75cfcdd41661` | certain | In this primer | `modules/hall-ui/public/primer.html:58` |
548
548
  | `3102d1d90c98` | certain | Intel Mac? | `modules/hall-ui/public/settings.html:227` |
549
- | `dad3388b49c9` | certain | Invitation rescinded. | `modules/hall-ui/public/goals-page.js:692` |
549
+ | `dad3388b49c9` | certain | Invitation rescinded. | `modules/hall-ui/public/goals-page.js:694` |
550
550
  | `12561be47da9` | certain | Invitation sent — they join once they accept. | `modules/hall-ui/public/goals-render.js:1261` |
551
551
  | `24f9e88c21f6` | certain | Invite by GitHub username | `modules/hall-ui/public/watch.js:295` |
552
552
  | `b9cb720ad5d1` | certain | Invited? Sign in | `modules/hall-ui/public/hall-render.js:286` |
@@ -821,10 +821,10 @@ Identical copy within one surface. Sometimes right (a repeated button), sometime
821
821
  | `dba9d3f3af13` | certain | Recorded sessions | `modules/hall-ui/public/sessions.html:55` |
822
822
  | `b40c4a61070e` | certain | Records your decision only — there is no marketplace yet, and nothing is listed anywhere. | `modules/hall-ui/public/settings.html:326` |
823
823
  | `58af483d7a84` | certain | Release task {…} | `modules/hall-ui/public/goals-render.js:348` |
824
- | `6f86e17007c6` | certain | Release your claim on #{…}? It returns to the queue and no {…} are earned. | `modules/hall-ui/public/goals-page.js:533` |
824
+ | `6f86e17007c6` | certain | Release your claim on #{…}? It returns to the queue and no {…} are earned. | `modules/hall-ui/public/goals-page.js:535` |
825
825
  | `6f86e17007c6` | certain | Release your claim on #{…}? It returns to the queue and no {…} are earned. | `modules/hall-ui/public/work.js:990` |
826
826
  | `975ffb256651` | certain | Release {…}? {…} to the queue and no {…} are earned. | `modules/hall-ui/public/work.js:939` |
827
- | `aa4db91b5732` | certain | Released #{…}. It returns to the queue. | `modules/hall-ui/public/goals-page.js:538` |
827
+ | `aa4db91b5732` | certain | Released #{…}. It returns to the queue. | `modules/hall-ui/public/goals-page.js:540` |
828
828
  | `aa4db91b5732` | certain | Released #{…}. It returns to the queue. | `modules/hall-ui/public/work.js:945` |
829
829
  | `aa4db91b5732` | certain | Released #{…}. It returns to the queue. | `modules/hall-ui/public/work.js:998` |
830
830
  | `c49f9b9f8864` | certain | Remake or tweak | `modules/hall-ui/public/studio.js:182` |
@@ -918,7 +918,7 @@ Identical copy within one surface. Sometimes right (a repeated button), sometime
918
918
  | `9bea6404e6e1` | certain | Sign in | `modules/hall-ui/public/profile.js:208` |
919
919
  | `9bea6404e6e1` | certain | Sign in | `modules/hall-ui/public/settings.html:42` |
920
920
  | `2f12d46f96ca` | certain | Sign in from the app. | `modules/hall-ui/public/hall-render.js:779` |
921
- | `19ab56cc3932` | certain | Sign in to filter to your goals | `modules/hall-ui/public/goals-page.js:873` |
921
+ | `19ab56cc3932` | certain | Sign in to filter to your goals | `modules/hall-ui/public/goals-page.js:875` |
922
922
  | `969f95f183ca` | certain | Sign in to the platform once to activate your profile before connecting. | `modules/hall-ui/public/profile-connections.js:220` |
923
923
  | `b057dfbf49c5` | certain | Sign in when Claude prompts you. | `modules/hall-ui/public/hall-render.js:779` |
924
924
  | `e57ec24e13ef` | certain | Sign in with GitHub | `modules/hall-ui/public/hall-render.js:275` |
@@ -1156,7 +1156,7 @@ Identical copy within one surface. Sometimes right (a repeated button), sometime
1156
1156
  | `52e93bbc07a6` | certain | You are already a member of this goal. | `modules/hall-ui/public/goals-render.js:1360` |
1157
1157
  | `46b1265ef172` | certain | You are not a member of this goal. | `modules/hall-ui/public/goals-render.js:1316` |
1158
1158
  | `b31fc908c962` | certain | You claimed #{…}. Open a terminal to start work. | `modules/hall-ui/public/goals-page.js:418` |
1159
- | `b31fc908c962` | certain | You claimed #{…}. Open a terminal to start work. | `modules/hall-ui/public/goals-page.js:515` |
1159
+ | `b31fc908c962` | certain | You claimed #{…}. Open a terminal to start work. | `modules/hall-ui/public/goals-page.js:517` |
1160
1160
  | `b31fc908c962` | certain | You claimed #{…}. Open a terminal to start work. | `modules/hall-ui/public/work.js:904` |
1161
1161
  | `b31fc908c962` | certain | You claimed #{…}. Open a terminal to start work. | `modules/hall-ui/public/work.js:974` |
1162
1162
  | `bcf33947aff6` | certain | You created this task and it is already retired. | `modules/hall-ui/public/task.js:767` |
@@ -282,7 +282,7 @@
282
282
  "occurrences": 4,
283
283
  "at": [
284
284
  "modules/hall-ui/public/goals-page.js:418",
285
- "modules/hall-ui/public/goals-page.js:515",
285
+ "modules/hall-ui/public/goals-page.js:517",
286
286
  "modules/hall-ui/public/work.js:904",
287
287
  "modules/hall-ui/public/work.js:974"
288
288
  ]
@@ -403,7 +403,7 @@
403
403
  "text": "Released #{…}. It returns to the queue.",
404
404
  "occurrences": 3,
405
405
  "at": [
406
- "modules/hall-ui/public/goals-page.js:538",
406
+ "modules/hall-ui/public/goals-page.js:540",
407
407
  "modules/hall-ui/public/work.js:945",
408
408
  "modules/hall-ui/public/work.js:998"
409
409
  ]
@@ -1270,7 +1270,7 @@
1270
1270
  "text": "Release your claim on #{…}? It returns to the queue and no {…} are earned.",
1271
1271
  "occurrences": 2,
1272
1272
  "at": [
1273
- "modules/hall-ui/public/goals-page.js:533",
1273
+ "modules/hall-ui/public/goals-page.js:535",
1274
1274
  "modules/hall-ui/public/work.js:990"
1275
1275
  ]
1276
1276
  },
@@ -5476,7 +5476,7 @@
5476
5476
  "surface": "builders-hall",
5477
5477
  "text": "Goals could not be loaded. Refresh to try again.",
5478
5478
  "file": "modules/hall-ui/public/goals-page.js",
5479
- "line": 896,
5479
+ "line": 898,
5480
5480
  "origin": "js-text-assign",
5481
5481
  "confidence": "certain"
5482
5482
  },
@@ -6007,7 +6007,7 @@
6007
6007
  "surface": "builders-hall",
6008
6008
  "text": "Invitation rescinded.",
6009
6009
  "file": "modules/hall-ui/public/goals-page.js",
6010
- "line": 692,
6010
+ "line": 694,
6011
6011
  "origin": "js-notice",
6012
6012
  "confidence": "certain"
6013
6013
  },
@@ -8482,7 +8482,7 @@
8482
8482
  "surface": "builders-hall",
8483
8483
  "text": "Release your claim on #{…}? It returns to the queue and no {…} are earned.",
8484
8484
  "file": "modules/hall-ui/public/goals-page.js",
8485
- "line": 533,
8485
+ "line": 535,
8486
8486
  "origin": "js-notice",
8487
8487
  "confidence": "certain"
8488
8488
  },
@@ -8509,7 +8509,7 @@
8509
8509
  "surface": "builders-hall",
8510
8510
  "text": "Released #{…}. It returns to the queue.",
8511
8511
  "file": "modules/hall-ui/public/goals-page.js",
8512
- "line": 538,
8512
+ "line": 540,
8513
8513
  "origin": "js-notice",
8514
8514
  "confidence": "certain"
8515
8515
  },
@@ -9355,7 +9355,7 @@
9355
9355
  "surface": "builders-hall",
9356
9356
  "text": "Sign in to filter to your goals",
9357
9357
  "file": "modules/hall-ui/public/goals-page.js",
9358
- "line": 873,
9358
+ "line": 875,
9359
9359
  "origin": "js-text-assign",
9360
9360
  "confidence": "certain"
9361
9361
  },
@@ -11497,7 +11497,7 @@
11497
11497
  "surface": "builders-hall",
11498
11498
  "text": "You claimed #{…}. Open a terminal to start work.",
11499
11499
  "file": "modules/hall-ui/public/goals-page.js",
11500
- "line": 515,
11500
+ "line": 517,
11501
11501
  "origin": "js-notice",
11502
11502
  "confidence": "certain"
11503
11503
  },
@@ -2190,5 +2190,7 @@ is load-bearing: the script throws rather than guess if it is missing, and
2190
2190
  landed since 1.19.856 with no explicit bump. run 35278916322. (task 1002620)
2191
2191
  1.19.858 — CI auto-patch (publish-on-merge, ADR 0161): carrier for merges
2192
2192
  landed since 1.19.857 with no explicit bump. run 35279781206. (task 1002620)
2193
+ 1.19.859 — CI auto-patch (publish-on-merge, ADR 0161): carrier for merges
2194
+ landed since 1.19.858 with no explicit bump. run 35280770718. (task 1002620)
2193
2195
  ---------------------------------------------------------------------------
2194
2196
  ```
@@ -428,8 +428,10 @@
428
428
  // or the flat legacy shape, via the same normalizers friendlyClaimError uses.
429
429
  const det = errDetails(err.data);
430
430
  if (err.status === 409 && errCode(err.data) === 'batch_failed') {
431
- const failures = Array.isArray(det.failures) ? det.failures
432
- : (Array.isArray(err.data && err.data.failures) ? err.data.failures : []);
431
+ // One read covers both envelopes: errDetails returns error.details on the ADR
432
+ // 0116 shape and the raw body on the flat legacy one, so a second
433
+ // `err.data.failures` fallback here is unreachable (task 1003657).
434
+ const failures = Array.isArray(det.failures) ? det.failures : [];
433
435
  const lines = failures.map((f) => `#${f.task_id} (${friendlyBatchReason(f)})`);
434
436
  toast(lines.length
435
437
  ? `The batch could not be claimed — ${lines.join('; ')}.`
@@ -914,9 +914,9 @@
914
914
  // against an object, so an all-or-nothing batch refusal rendered as the generic toast
915
915
  // and the per-task reasons were dropped on the floor.
916
916
  if (err.status === 409 && errCode(err.data) === 'batch_failed') {
917
+ // One read covers both envelopes — errDetails falls back to the body (task 1003657).
917
918
  const bdet = errDetails(err.data);
918
- const failures = Array.isArray(bdet.failures) ? bdet.failures
919
- : (Array.isArray(err.data && err.data.failures) ? err.data.failures : []);
919
+ const failures = Array.isArray(bdet.failures) ? bdet.failures : [];
920
920
  const lines = failures.map((f) => `#${f.task_id} (${friendlyBatchReason(f)})`);
921
921
  toast(lines.length
922
922
  ? `The batch could not be claimed — ${lines.join('; ')}.`
package/package-lock.json CHANGED
@@ -1,12 +1,12 @@
1
1
  {
2
2
  "name": "@bongos/core",
3
- "version": "1.19.858",
3
+ "version": "1.19.859",
4
4
  "lockfileVersion": 3,
5
5
  "requires": true,
6
6
  "packages": {
7
7
  "": {
8
8
  "name": "@bongos/core",
9
- "version": "1.19.858",
9
+ "version": "1.19.859",
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.858",
3
+ "version": "1.19.859",
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",
@@ -152,16 +152,70 @@ function isReleaseCommit(subject) {
152
152
  // so a failed publish left main declaring a version the registry never had, the
153
153
  // cascade terminator no-opped every following run, and only a fresh work merge
154
154
  // moved past it. That is how 1.19.716-723 were minted and lost (2026-09-13).
155
+ // HUMAN_RELEASE_COMMIT_RE — a release commit a PERSON wrote (task 1003526).
156
+ //
157
+ // ADR 0161 §2 promises "a minor or major stays a deliberate in-task human edit".
158
+ // The lane could not keep that promise: a branch that hand-bumped to 1.20.0 had no
159
+ // commit shape the lane recognised, so the LIVE test below failed its own PR's
160
+ // `unit` check by construction (that is what killed task 1003044's first ship,
161
+ // PR #391) and, had it merged, publish.yml would have packaged 1.20.0 forever
162
+ // without ever recording it — its record step fires only when CI itself bumped,
163
+ // so every later merge would re-pack the same taken version and NOTHING NEW WOULD
164
+ // EVER PUBLISH AGAIN. A stalled lane, not just a blocked PR.
165
+ //
166
+ // So a human release is now a first-class shape: `release: core X.Y.Z — <reason>`
167
+ // that is NOT this lane's own carrier phrase. Deliberately the complement of
168
+ // RELEASE_COMMIT_RE, so no subject can ever be read as both.
169
+ const HUMAN_RELEASE_COMMIT_RE = /^release: core \d+\.\d+\.\d+ \u2014 (?!CI auto-patch carrier \(publish-on-merge)\S/;
170
+
171
+ function isHumanReleaseCommit(subject) {
172
+ return HUMAN_RELEASE_COMMIT_RE.test(String(subject == null ? '' : subject));
173
+ }
174
+
175
+ // laneReleasedVersions answers "is this version SPOKEN FOR?" — which is a different
176
+ // question from isReleaseCommit's "did this lane already publish everything at this
177
+ // HEAD?", and task 1003526 is where the difference started to matter.
178
+ //
179
+ // They used to share RELEASE_COMMIT_RE, and the old comment treated that as the
180
+ // invariant. It is not: the two callers want OPPOSITE answers for a human release
181
+ // commit, and keeping them identical is exactly what made a human minor impossible.
182
+ //
183
+ // • the cascade terminator (isReleaseCommit) must say NO — a human-declared
184
+ // version is new and still needs publishing. Saying yes would no-op the run and
185
+ // the version would be bumped but never packaged (the 1.19.0 hole).
186
+ // • this function must say YES — the number is claimed, so the lane must not try
187
+ // to publish onto it. publish.yml reads that as taken=true, auto-bumps one patch
188
+ // past it, publishes THAT, and records its own carrier. The lane keeps moving
189
+ // and the human still gets their minor: 1.19.x → (declare 1.20.0) → 1.20.1.
190
+ //
191
+ // A human therefore spends a version number to change the minor/major digit. That is
192
+ // the whole cost of keeping this inside bump-version.js: publishing 1.20.0 EXACTLY
193
+ // would mean teaching publish.yml's record step to fire without a bump, and that file
194
+ // is a protected surface on the lane that already minted-and-lost 1.19.716-723. Not
195
+ // worth it for a digit.
196
+ //
197
+ // BOTH failure directions stay safe, which is what makes this acceptable here. A
198
+ // subject wrongly counted → the lane bumps PAST a free version (a gap, harmless). A
199
+ // real release missed → npm refuses the duplicate and publish.yml's already-present
200
+ // branch records it (task 1003914). Neither direction can double-publish.
155
201
  function laneReleasedVersions(subjects = []) {
156
202
  const out = new Set();
157
203
  for (const subject of subjects) {
158
204
  const line = String(subject == null ? '' : subject);
159
- if (!RELEASE_COMMIT_RE.test(line)) continue;
205
+ if (!RELEASE_COMMIT_RE.test(line) && !HUMAN_RELEASE_COMMIT_RE.test(line)) continue;
160
206
  const m = /^release: core (\d+\.\d+\.\d+) /.exec(line);
161
207
  if (m) out.add(m[1]);
162
208
  }
163
209
  return out;
164
210
  }
165
211
 
166
- module.exports = { bumpPatch, CONST_RE, RELEASE_COMMIT_RE, isReleaseCommit, laneReleasedVersions };
212
+ module.exports = {
213
+ bumpPatch,
214
+ CONST_RE,
215
+ RELEASE_COMMIT_RE,
216
+ HUMAN_RELEASE_COMMIT_RE,
217
+ isReleaseCommit,
218
+ isHumanReleaseCommit,
219
+ laneReleasedVersions,
220
+ };
167
221
  if (require.main === module) main();
@@ -12,7 +12,7 @@
12
12
  # 7. Claim B, ship B (active → completed → confirmed → shipped)
13
13
  # 8. Verify A auto-promoted backlog→ready (or stays ready if step 5 ran)
14
14
  # 9. Claim A — succeeds
15
- # 10. Release A + clean up
15
+ # 10. Release A, abandon A (B is shipped and cannot be abandoned)
16
16
  #
17
17
  # Run from any environment with a valid CLI session (/builder-setup).
18
18
  # Requires curl.
@@ -77,8 +77,38 @@ echo " A status: $A_STATUS"
77
77
  echo "=== 8. Claim A — must succeed now ==="
78
78
  node scripts/gds/claim.js "$A_ID" --worktree "smoke-$SUFFIX-A"
79
79
 
80
- echo "=== 9. Cleanup (release A; mark both abandoned) ==="
80
+ echo "=== 9. Cleanup — abandon A; B is shipped and stays ==="
81
+ # task 1004026: this step used to ANNOUNCE "release A; mark both abandoned" and then
82
+ # only release A. Neither task reached a terminal state, so every run leaked two
83
+ # __smoke__ rows into the live tasks table — invisibly, because anyone grepping the
84
+ # cleanup step read a line saying both were handled.
85
+ #
86
+ # "mark both abandoned" was never possible. B is SHIPPED by step 6, and the abandon
87
+ # route refuses shipped work (cannot_abandon_shipped) because shipped rows are
88
+ # immutable history. Only A can reach a terminal state here, so the echo now states
89
+ # what the code does instead of the reverse.
90
+ #
91
+ # Follows scripts/gds/smoke-write.sh's cleanup (task 1002018): abandon is the right
92
+ # terminal state for a smoke row, and cleanup is STRICTLY best-effort. task.abandon is
93
+ # floor-metic (ADR 0152), so a Xenos/Thetes token gets a 403 here while every assertion
94
+ # above genuinely passed — a cleanup that cannot run must never turn a passing smoke
95
+ # into a failing one. Hence the `|| true` and the reported-not-raised status codes.
96
+ # Reaching this line at all means every assertion passed: the script is `set -e` and
97
+ # each check above exits non-zero on failure, so a FAILED run stops earlier and leaves
98
+ # both rows in place for inspection, which is the same deliberate choice smoke-write makes.
81
99
  node scripts/gds/release.js "$A_ID" --reason "smoke cleanup" || true
82
100
 
101
+ A_CLEANUP_CODE=$(curl -sS -o /dev/null -w '%{http_code}' -X POST \
102
+ -H "Authorization: Bearer $TOKEN" -H "Content-Type: application/json" \
103
+ --data '{"reason":"smoke cleanup (task 1004026) - dependency smoke passed; the throwaway row has served its purpose"}' \
104
+ "$BASE/api/bongos/tasks/$A_ID/abandon") || true
105
+ case "${A_CLEANUP_CODE:-000}" in
106
+ 2*) echo " PASS cleaned up throwaway task #$A_ID (abandoned)" ;;
107
+ 403) echo " SKIP cleanup — task.abandon needs Metic+ (ADR 0152); task #$A_ID left in the ledger" ;;
108
+ 000) echo " SKIP cleanup — POST /tasks/$A_ID/abandon got no response; task #$A_ID left in the ledger" ;;
109
+ *) echo " SKIP cleanup — POST /tasks/$A_ID/abandon returned HTTP $A_CLEANUP_CODE; task #$A_ID left in the ledger" ;;
110
+ esac
111
+ echo " NOTE task #$B_ID is SHIPPED and is deliberately left in the ledger — the abandon route refuses shipped work (cannot_abandon_shipped)."
112
+
83
113
  echo
84
114
  echo "ALL SMOKE STEPS PASSED."
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.858'; // CI auto-patch carrier (ADR 0161); changelog: docs/module-api-changelog.md
74
+ const CORE_VERSION = '1.19.859'; // 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');
@@ -276,14 +276,21 @@ test('laneReleasedVersions reads the versions this lane announced', () => {
276
276
  assert.equal(v.has('1.19.369'), false, 'a version never announced must read as free');
277
277
  });
278
278
 
279
- test('a HUMAN release commit is NOT counted — that version still needs publishing', () => {
280
- // The same distinction RELEASE_COMMIT_RE was written to make for the cascade
281
- // terminator. Counting it here would skip the publish of a hand-cut release.
279
+ test('a HUMAN release commit IS counted — the lane bumps past it rather than onto it', () => {
280
+ // REVERSED by task 1003526. This test used to assert the opposite, reasoning that
281
+ // "counting it here would skip the publish of a hand-cut release". The skip is real
282
+ // but it is not a loss: publish.yml reads taken=true, bumps ONE PATCH past the
283
+ // declared number, publishes that, and records its own carrier. The human's minor
284
+ // ships as 1.20.1 and the lane keeps moving.
285
+ //
286
+ // Not counting it is what actually lost releases. publish.yml's record step fires
287
+ // only when CI itself bumped, so a published-but-unrecorded 1.20.0 would be re-packed
288
+ // by every later merge and nothing new would ever reach the registry again.
282
289
  const v = bump.laneReleasedVersions([
283
290
  'release: core 1.19.2 — carrier for the init-blocker fixes',
284
291
  'release: core 1.20.0 — hand-cut minor',
285
292
  ]);
286
- assert.equal(v.size, 0);
293
+ assert.deepEqual([...v].sort(), ['1.19.2', '1.20.0']);
287
294
  });
288
295
 
289
296
  test('malformed and empty input yields an empty set, never a throw', () => {
@@ -295,19 +302,49 @@ test('malformed and empty input yields an empty set, never a throw', () => {
295
302
  assert.equal(bump.laneReleasedVersions([null, undefined, '', 42]).size, 0);
296
303
  });
297
304
 
298
- test('laneReleasedVersions and isReleaseCommit agree on what a lane release looks like', () => {
299
- // They share RELEASE_COMMIT_RE on purpose: if one ever recognises a subject the
300
- // other does not, the cascade terminator and the version check disagree about
301
- // the same commit, and the lane either double-publishes or silently no-ops.
305
+ test('a human release commit is SPOKEN FOR but does not terminate the cascade', () => {
306
+ // task 1003526. These two predicates used to share RELEASE_COMMIT_RE, and the old
307
+ // test asserted they must always agree. That assertion was the bug: the two callers
308
+ // need OPPOSITE answers for a human release, and forcing them to agree is precisely
309
+ // what made ADR 0161 §2's promised minor/major impossible to land.
310
+ const human = 'release: core 1.20.0 — minor: new doorway export (task 1003526)';
311
+
312
+ // NO: it is not this lane's own carrier, so publish.yml must NOT no-op — the
313
+ // version is new and still has to be packaged. Saying yes here is the 1.19.0 hole.
314
+ assert.equal(bump.isReleaseCommit(human), false);
315
+ assert.equal(bump.isHumanReleaseCommit(human), true);
316
+
317
+ // YES: the number is claimed, so the lane must not publish onto it. publish.yml
318
+ // reads taken=true, bumps one patch past it, and records its own carrier — which
319
+ // is what keeps the lane moving instead of re-packing a taken version forever.
320
+ assert.equal(bump.laneReleasedVersions([human]).has('1.20.0'), true);
321
+ });
322
+
323
+ test('the CI carrier still both terminates the cascade and counts as released', () => {
324
+ const ci = 'release: core 1.19.368 — CI auto-patch carrier (publish-on-merge, ADR 0161)';
325
+ assert.equal(bump.isReleaseCommit(ci), true);
326
+ assert.equal(bump.isHumanReleaseCommit(ci), false, 'the two shapes must never both match');
327
+ assert.equal(bump.laneReleasedVersions([ci]).has('1.19.368'), true);
328
+ });
329
+
330
+ test('an ordinary commit is neither shape', () => {
331
+ const no = 'chore: not a release at all';
332
+ assert.equal(bump.isReleaseCommit(no), false);
333
+ assert.equal(bump.isHumanReleaseCommit(no), false);
334
+ assert.equal(bump.laneReleasedVersions([no]).size, 0);
335
+ });
336
+
337
+ test('a hand-bumped branch passes the LIVE check its own PR runs', () => {
338
+ // The regression that killed task 1003044's first ship (PR #391): the branch bumped
339
+ // CORE_VERSION to a minor, no commit shape declared it, and the LIVE test below
340
+ // failed the PR's own `unit` check by construction. With the human carrier in the
341
+ // branch's log the same check now passes.
302
342
  const subjects = [
343
+ 'release: core 1.20.0 — minor: new doorway export (task 1003526)',
344
+ 'feat: the doorway export itself',
303
345
  'release: core 1.19.368 — CI auto-patch carrier (publish-on-merge, ADR 0161)',
304
- 'release: core 1.19.2 — carrier for the init-blocker fixes',
305
- 'chore: not a release at all',
306
346
  ];
307
- for (const s of subjects) {
308
- const counted = bump.laneReleasedVersions([s]).size === 1;
309
- assert.equal(counted, bump.isReleaseCommit(s), `disagreement on: ${s}`);
310
- }
347
+ assert.equal(bump.laneReleasedVersions(subjects).has('1.20.0'), true);
311
348
  });
312
349
 
313
350
  test('LIVE: the version HEAD declares is one this lane already released', () => {
@@ -108,6 +108,23 @@ await t('errDetails takes error.details, else falls back to the BODY (flat shape
108
108
  assert.deepEqual(errDetails({ error: 'c', conflicts: [{ title: 'B' }] }).conflicts, [{ title: 'B' }]);
109
109
  });
110
110
 
111
+ await t('one errDetails read covers batch failures on BOTH shapes (task 1003657)', () => {
112
+ const { errDetails } = loadOTB();
113
+ const failures = [{ task_id: 7, reason: 'ALREADY_CLAIMED' }];
114
+ // This is the assertion that makes the second fallback deletable. work.js and
115
+ // goals-page.js used to read `det.failures` and THEN `err.data.failures`; the
116
+ // second branch can never run, because errDetails returns error.details on the
117
+ // ADR 0116 shape and the raw BODY on the flat legacy one — so `.failures` is
118
+ // reachable through one read either way. If a future errDetails stops falling
119
+ // back to the body, this fails and the callers need their fallback back.
120
+ assert.deepEqual(errDetails({ error: { code: 'batch_failed', details: { failures } } }).failures, failures);
121
+ assert.deepEqual(errDetails({ error: 'batch_failed', failures }).failures, failures);
122
+ // And the shape that would have made the fallback reachable — nested details
123
+ // alongside a top-level failures list — is not one the server can emit:
124
+ // src/bongos/api-errors.js envelope() puts nothing but `error` at the top level.
125
+ assert.equal(errDetails({ error: { code: 'batch_failed', details: {} } }).failures, undefined);
126
+ });
127
+
111
128
  await t('the readers survive hostile / empty input without throwing', () => {
112
129
  const { errCode, errMessage, errDetails } = loadOTB();
113
130
  for (const bad of [undefined, null, {}, { error: null }, { error: 0 }, { error: [] }, 'str', 42]) {