@bongos/core 1.19.947 → 1.19.949

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,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.947",
6
- "core_contract": "1.19.947",
7
- "source_commit": "37a9f1c16fc7a96d1a1a5a2a65caf044f3026012",
5
+ "core_version": "1.19.949",
6
+ "core_contract": "1.19.949",
7
+ "source_commit": "b167833627e1c0360613322d616a6e288ea083a0",
8
8
  "source_ref": "HEAD",
9
- "built_at": "2026-09-22T00:19:14.818Z",
9
+ "built_at": "2026-09-22T01:20:15.264Z",
10
10
  "redaction": {
11
11
  "model": "docs-redacted+functional-verbatim",
12
- "docs_redacted": 512,
12
+ "docs_redacted": 513,
13
13
  "agent_docs_stubbed": 26,
14
- "functional_verbatim": 2349,
14
+ "functional_verbatim": 2347,
15
15
  "rules": 3,
16
16
  "gate_literals": 3,
17
17
  "gate": "passed"
18
18
  },
19
- "file_count": 2887,
20
- "tree_sha256": "bda269845896de352c55f40f7ce0700fd273326dd76309102c37fd6b9afda131",
19
+ "file_count": 2886,
20
+ "tree_sha256": "76d08b0cd0d4045e4ecadf3d9571fbcbc63cf5aebc7d33edf787c30a0c7978de",
21
21
  "files": [
22
22
  {
23
23
  "path": ".claude/skills/ask-for-help/SKILL.md",
@@ -377,7 +377,7 @@
377
377
  {
378
378
  "path": "clients/bongos-client/package.json",
379
379
  "mode": "0000644",
380
- "sha256": "d299252221daca415cf982b61fe5e3d9c3707c672a7d47adc052a9ee8166223f"
380
+ "sha256": "f2a46cacecfdfff5632a6fbc7a9c5a7ac09a5b8d45bc48f579e91db4ff1cd7bd"
381
381
  },
382
382
  {
383
383
  "path": "config/branding.neutral.json",
@@ -2109,10 +2109,15 @@
2109
2109
  "mode": "0000644",
2110
2110
  "sha256": "8fa6ef943e3fc0855d15f83baefaabf810994b80da2ecdbbcc28d752ed0498f3"
2111
2111
  },
2112
+ {
2113
+ "path": "docs/adr/0324-the-public-npm-cli-channel-is-retired.md",
2114
+ "mode": "0000644",
2115
+ "sha256": "c1993ab3ca464c5aa7256301343e7b963914d085043638faa46c440e0511de08"
2116
+ },
2112
2117
  {
2113
2118
  "path": "docs/adr/README.md",
2114
2119
  "mode": "0000644",
2115
- "sha256": "eef598f074ff3e22a4748b9092eb83d04235205c34e66db6bbe76547f050843d"
2120
+ "sha256": "cd7aa240e31f48b0f0fa897af0ee90a4e51a0bf670bfec3071e31eae7caea6de"
2116
2121
  },
2117
2122
  {
2118
2123
  "path": "docs/api-reference.md",
@@ -2672,7 +2677,7 @@
2672
2677
  {
2673
2678
  "path": "docs/file-map.md",
2674
2679
  "mode": "0000644",
2675
- "sha256": "d3afe58f6c7608ae2c56744c86567f24347b71cb20a7ee268e44651d87206ec8"
2680
+ "sha256": "ca43a028309885e4375a3fa216fc2636d33262d3038bf9e65a15f6e8b68f354d"
2676
2681
  },
2677
2682
  {
2678
2683
  "path": "docs/handoff-template.md",
@@ -2682,7 +2687,7 @@
2682
2687
  {
2683
2688
  "path": "docs/module-api-changelog.md",
2684
2689
  "mode": "0000644",
2685
- "sha256": "d9c0cae827a25e58be07c384558b2752a027cd7736ce560af83ee2e2286f8fd8"
2690
+ "sha256": "29e0651fa0f5cb469d61cc13ff096ef772a07006f2e8d53e2d6119b3b3f194fb"
2686
2691
  },
2687
2692
  {
2688
2693
  "path": "docs/modules-contract.md",
@@ -2937,7 +2942,7 @@
2937
2942
  {
2938
2943
  "path": "docs/recipes/windows-builders.md",
2939
2944
  "mode": "0000644",
2940
- "sha256": "dba962c19bf22e5773fd05eb6667581d15f62ecda7292a37f83b12227ad14049"
2945
+ "sha256": "2608bea40745e61407454eb73ff3dc19d2c088a986741fb243001a1a0848ee00"
2941
2946
  },
2942
2947
  {
2943
2948
  "path": "docs/research/cursor-competitive-analysis.md",
@@ -5687,7 +5692,7 @@
5687
5692
  {
5688
5693
  "path": "modules/lifecycle/criterion-ref.js",
5689
5694
  "mode": "0000644",
5690
- "sha256": "c79a34e577df14c4c54d426e60789c5e30bc2e81fd4005a283f21936b9f0aba7"
5695
+ "sha256": "5ae49c40d6f7ec8a1bb62f038b405e7c4e69b844ea204a50bded2eddef29faae"
5691
5696
  },
5692
5697
  {
5693
5698
  "path": "modules/lifecycle/criterion-suggest.js",
@@ -5712,7 +5717,7 @@
5712
5717
  {
5713
5718
  "path": "modules/lifecycle/db-deps-criteria.js",
5714
5719
  "mode": "0000644",
5715
- "sha256": "e921d48860af27f836252fa7e44e1bdae25be5b940c25e9272f1cf368805d132"
5720
+ "sha256": "80c0d3ab5d905880309282446d1cd59645868e7de8aae962e01f46997fe5e469"
5716
5721
  },
5717
5722
  {
5718
5723
  "path": "modules/lifecycle/db-executor.js",
@@ -8037,12 +8042,12 @@
8037
8042
  {
8038
8043
  "path": "package-lock.json",
8039
8044
  "mode": "0000644",
8040
- "sha256": "299be5b7d35c653f16b1789ea0a36c9bfa31d0f67ed7f6aebdff81ba17f379d0"
8045
+ "sha256": "c2e5171010f3ceab561e381ba14d2d76649b8bca170450a8db18edde40bf6228"
8041
8046
  },
8042
8047
  {
8043
8048
  "path": "package.json",
8044
8049
  "mode": "0000644",
8045
- "sha256": "875f023498acdbdb7f1c746c2e12390fe77b92fa2ccfadc66204c5271659c4c4"
8050
+ "sha256": "f7988eef8fb6aceffb3357949ab178209d7e340281f00f926334fd3456ef57c1"
8046
8051
  },
8047
8052
  {
8048
8053
  "path": "public-docs/index.html",
@@ -8344,11 +8349,6 @@
8344
8349
  "mode": "0000644",
8345
8350
  "sha256": "6a696f9291da2d071d8a8aa0f92bf0be08445569a73196f9204f696bf6cb6cb5"
8346
8351
  },
8347
- {
8348
- "path": "scripts/gds/build-cli-package.js",
8349
- "mode": "0000644",
8350
- "sha256": "a7a19db7b156f7e411e2a77146c2f9418b5b9ff7fa52be1e3dca56848307f98d"
8351
- },
8352
8352
  {
8353
8353
  "path": "scripts/gds/bump-version.js",
8354
8354
  "mode": "0000644",
@@ -8407,7 +8407,7 @@
8407
8407
  {
8408
8408
  "path": "scripts/gds/cli-lib.js",
8409
8409
  "mode": "0000644",
8410
- "sha256": "cbe71309f4e100b096eee7d91b90a4cc9e549caa3df51473f2ca3029ca4c8ba5"
8410
+ "sha256": "a6c486243cf675fe9929997b09160616cbd05607423b1b6a823d2d57efba7619"
8411
8411
  },
8412
8412
  {
8413
8413
  "path": "scripts/gds/client-baseurl-guard.js",
@@ -8667,7 +8667,7 @@
8667
8667
  {
8668
8668
  "path": "scripts/gds/gen-api-client.js",
8669
8669
  "mode": "0000644",
8670
- "sha256": "a023a19001d63b671409efe1afeae400dae96ea50debd6e3a49a7d10dffb55b0"
8670
+ "sha256": "6274411b4b43d0fc5ef8aa072d22123745d21268748fb32e55646aa2cb4fd01c"
8671
8671
  },
8672
8672
  {
8673
8673
  "path": "scripts/gds/gen-api-docs.js",
@@ -9087,7 +9087,7 @@
9087
9087
  {
9088
9088
  "path": "scripts/gds/release.js",
9089
9089
  "mode": "0000644",
9090
- "sha256": "997c4c4f45d360b765877e3583038be21b52267112b7b33e9c673ab245d682fc"
9090
+ "sha256": "b7de35b15fef0cd61da00c4c7263920c82fcc0606d9ff787951be6fc1377406e"
9091
9091
  },
9092
9092
  {
9093
9093
  "path": "scripts/gds/rename-goals.js",
@@ -10047,7 +10047,7 @@
10047
10047
  {
10048
10048
  "path": "src/module-api.js",
10049
10049
  "mode": "0000644",
10050
- "sha256": "e710dd0a8764f9b78f20168fa83b3817d190e5436e384055ffab4352ef0095f0"
10050
+ "sha256": "3fbfb8061f3fdf5fbc1d7426db7eb5dd6b8ec4acaab02eab9489c0d443792b09"
10051
10051
  },
10052
10052
  {
10053
10053
  "path": "src/module-loader/catalog.js",
@@ -10257,7 +10257,7 @@
10257
10257
  {
10258
10258
  "path": "tests/api_client.mjs",
10259
10259
  "mode": "0000644",
10260
- "sha256": "c1563c043f34e84aa6c0eec722244b24c51dc1254750fe1a27d3664c327bacd1"
10260
+ "sha256": "4f9a52f3f3eb998b87d06cb127fa79e21f7488281911159a159e0d856ea3f04c"
10261
10261
  },
10262
10262
  {
10263
10263
  "path": "tests/api_docs.mjs",
@@ -10849,11 +10849,6 @@
10849
10849
  "mode": "0000644",
10850
10850
  "sha256": "d0ea5b273c24527190f6ed01555ddb6b022a6ea94f3efcc7bdbd17f834c7d2ad"
10851
10851
  },
10852
- {
10853
- "path": "tests/cli_package.mjs",
10854
- "mode": "0000644",
10855
- "sha256": "e7839e6ac47b12fe4decd50e31d37fba3bad7ac475e8f24e948bb49b52a3e83e"
10856
- },
10857
10852
  {
10858
10853
  "path": "tests/cli_sessions.mjs",
10859
10854
  "mode": "0000644",
@@ -10862,7 +10857,7 @@
10862
10857
  {
10863
10858
  "path": "tests/cli_surface.mjs",
10864
10859
  "mode": "0000644",
10865
- "sha256": "cc10440e148e19a157cd18f0325e121f5492484e09550578e98870dba61090a9"
10860
+ "sha256": "90427212f57df834e9e64e6423d38f2b2878eb8bea16c3874b3e30159db1ebd9"
10866
10861
  },
10867
10862
  {
10868
10863
  "path": "tests/cli_token_reissue.mjs",
@@ -12032,7 +12027,7 @@
12032
12027
  {
12033
12028
  "path": "tests/home_isolation_guard.mjs",
12034
12029
  "mode": "0000644",
12035
- "sha256": "b42c0338476c2fc7be72888b2eec1a79fcfb3ff1c27e85eff605d3248b2780e4"
12030
+ "sha256": "193381e177d20129516ab0cc6482b24934bc3461f113c63cd3eca2ae6b6188d2"
12036
12031
  },
12037
12032
  {
12038
12033
  "path": "tests/hook_core_resolve.mjs",
@@ -12492,7 +12487,7 @@
12492
12487
  {
12493
12488
  "path": "tests/main_worktree_busy_reason.mjs",
12494
12489
  "mode": "0000644",
12495
- "sha256": "f74a3764214b67cb560e1e349624ac5e2ce419a6ae9af7389af4f0e70c8459f2"
12490
+ "sha256": "097cca33e5d9973726a19a29e2105bf40edf4e1712d3128eeae1bd838e00ec2b"
12496
12491
  },
12497
12492
  {
12498
12493
  "path": "tests/maintenance_goal.mjs",
@@ -12872,7 +12867,7 @@
12872
12867
  {
12873
12868
  "path": "tests/plain_cards.mjs",
12874
12869
  "mode": "0000644",
12875
- "sha256": "7549965b77f59c63fc2ac5fe5eb6cb4d292da9140bff5d589c47f99b784ab579"
12870
+ "sha256": "e7a255bdb636adb6206c9e34bafa007eb7c815a9fe65337aefd79fcc719e3182"
12876
12871
  },
12877
12872
  {
12878
12873
  "path": "tests/planning_session_skill.mjs",
@@ -25,5 +25,6 @@
25
25
  "README.md"
26
26
  ],
27
27
  "license": "AGPL-3.0-or-later",
28
+ "private": true,
28
29
  "sideEffects": false
29
30
  }
@@ -0,0 +1,89 @@
1
+ # ADR 0324 — The public npm CLI channel is retired
2
+
3
+ - **Status:** accepted
4
+ - **Date:** 2026-09-22
5
+ - **Task:** [task 1004144](https://cloudbongos.com/builders#/task/1004144) (goal 1000091 — working area 5, Module distribution & economy)
6
+ - **Deciders:** Lars (Archon) — the owner deleted the package and confirmed the retirement; Claude scoped the consequences and executed the cleanup.
7
+ - **Supersedes:** [ADR 0258](0258-the-public-cli-is-a-generated-client-package-not-the-published-core.md) · [ADR 0292](0292-both-install-paths-stay-and-vendoring-is-the-production-one.md) §2
8
+ - **Related:** [ADR 0134](0134-private-first-npm-distribution.md) · [ADR 0283](0283-the-core-publishes-publicly-because-access-was-never-the-thing-protecting-it.md) (the public flip that removed this package's original reason) · [ADR 0269](0269-the-cli-session-store-is-host-keyed-at-a-fixed-anchor.md) (cites the package in an example)
9
+
10
+ ## Context
11
+
12
+ `@cloudbongos/cli` was a generated, server-free npm package — the public front door, so a
13
+ newcomer could reach an instance's board with `npx` and no checkout, no credential and no
14
+ 46 MB core download.
15
+
16
+ **The owner removed it from the registry on 2026-09-22.** Verified: `npm view
17
+ @cloudbongos/cli` answers E404, and `npm access list packages @cloudbongos` returns an
18
+ empty scope.
19
+
20
+ This ADR exists because that reverses a decision the project had already argued and
21
+ settled — twice, in opposite directions:
22
+
23
+ - **[ADR 0258](0258-the-public-cli-is-a-generated-client-package-not-the-published-core.md)**
24
+ established the channel: a generated client package rather than the published core,
25
+ deliberately excluding `claim` and `ship` because "a half-working claim that cannot
26
+ materialize into a checkout is worse than one that explains where to go".
27
+ - **[ADR 0292](0292-both-install-paths-stay-and-vendoring-is-the-production-one.md) §2** is
28
+ titled *"`@cloudbongos/cli` STAYS, on a new justification"*. It was written **because**
29
+ task 1003872 proposed removing it once [ADR 0283](0283-the-core-publishes-publicly-because-access-was-never-the-thing-protecting-it.md)
30
+ made the core public and dissolved the original "no private-registry credential" reason.
31
+ 0292 agreed the original reason was gone and kept the package anyway, on a measured one:
32
+ roughly half a megabyte for the front door, versus pulling the whole core — a web server
33
+ and a Postgres driver included — to print a task list.
34
+
35
+ **That measured argument is still true.** Nothing about it was found wrong. What changed is
36
+ the owner's decision about carrying a public distribution channel at all, which is a
37
+ judgement about what this project wants to maintain and support, not a correction to 0292's
38
+ arithmetic. The record should say that plainly rather than retro-fitting a technical fault.
39
+
40
+ ## Decision
41
+
42
+ ### 1. The public npm channel is retired. Nothing in the repo generates or advertises it.
43
+
44
+ `scripts/gds/build-cli-package.js` and `tests/cli_package.mjs` are deleted. No document
45
+ offers `npx @cloudbongos/cli` as a route in.
46
+
47
+ ### 2. The way in is the hall, or a checkout.
48
+
49
+ A newcomer reaches an instance at its own address — `<origin>/builders` — which needs
50
+ nothing installed at all and was always the lower-friction path for *reading* the board.
51
+ Writing code still needs a checkout of the project repo, where the full CLI already is.
52
+ This is the same answer [ADR 0258](0258-the-public-cli-is-a-generated-client-package-not-the-published-core.md)'s
53
+ `CHECKOUT_ONLY` refusal gave for `claim` and `ship`; it is now the answer for every verb.
54
+
55
+ ### 3. The COMPILED binary channel is untouched, and is a different thing.
56
+
57
+ Two separate artifacts were both called "the bongos CLI", and conflating them would delete
58
+ a live surface:
59
+
60
+ | | retired here | untouched |
61
+ | --- | --- | --- |
62
+ | built by | `scripts/gds/build-cli-package.js` | `scripts/gds/build-bongos.js` |
63
+ | artifact | the `@cloudbongos/cli` npm package | per-platform compiled binaries |
64
+ | served by | npm | `src/bongos-downloads.js` → `/downloads/bongos/*`, from `config/bongos-cli.json` |
65
+
66
+ `/downloads/bongos/*`, `src/bongos-downloads.js`, `build-bongos.js` and
67
+ `config/bongos-cli.json` are all unaffected. (Task 1003889 flagged this same near-twin when
68
+ it warned "Keep `src/bongos-downloads.js`" while deleting the dev-box download surface.)
69
+
70
+ ### 4. Nothing is republished to fill the gap.
71
+
72
+ Publishing is the owner's action ([ADR 0258](0258-the-public-cli-is-a-generated-client-package-not-the-published-core.md)),
73
+ and no replacement package is planned. If a public channel is ever wanted again, it needs a
74
+ new ADR and a fresh justification — 0292 §2's is spent.
75
+
76
+ ## Consequences
77
+
78
+ **Positive.** One fewer artifact to build, version, redact and publish, and one fewer way for
79
+ the repo to describe a thing that does not exist — which it had been doing since the deletion.
80
+ It also closes a class: `@bongos/client` (removed 2026-09-09) and now `@cloudbongos/cli` both
81
+ outlived their registry entries in the repo's prose, and neither leaves a generator behind.
82
+
83
+ **Negative.** The zero-install on-ramp is gone. A newcomer with only a terminal can no longer
84
+ reach the board without either a browser or a checkout, and the ~0.5 MB versus 46 MB gap 0292
85
+ measured is a real cost now being paid. Accepted with open eyes.
86
+
87
+ **Neutral.** No test fetched the registry, so nothing in CI depended on the package existing;
88
+ three tests used its FILES manifest as a stand-in for "what a terminal user can run" and now
89
+ read `bin/bongos.js`'s verb table, which is what that set always actually meant.
@@ -415,3 +415,4 @@ This keeps the decision history honest and traceable.
415
415
  | 0321 | [**Brightness is a payout-weighted share, and a removal's targets are rows** ([task 1004099](https://cloudbongos.com/builders#/task/1004099), goal 1000095 — BONGOS-V2). Size is an idea's total ideator payout and never falls; brightness is how much of that work is still alive, and the pair is what lets a thought whose version was torn out draw LARGE AND DARK rather than merging with one that never mattered. Two things had to be settled. **The removal facts were stored in the wrong direction**: [ADR 0319](0319-a-removal-verdict-is-a-row-beside-the-question.md) gave a confirmed removal a row carrying the verdict and `ship_task_id` — the task whose merge DELETED the files — and deliberately left out the tasks whose work was removed, saying brightness would design its own storage when it needed one. Those are different rows under different ideas, so reading one as the other dims the wrong star while looking like a working query. `<redacted>` stores `(proposal_task_id, removed_task_id, paths)` at PROPOSAL time, from the SAME derivation objects the proposal body renders as prose — one derivation, two renderings, so they cannot disagree and `removal-proposals.js` keeps its refusal to parse a body back. Proposal time and not verdict time because the attribution comes from capped, rate-limited GitHub commit-history reads that only exist while the cascade runs. NOT a runner fan-out ([ADR 0242](0242-a-cascade-is-a-declaration-table-on-the-event-that-already-exists.md)): the rule still files one task; this records a side fact about it. `ON CONFLICT DO NOTHING`, never `DO UPDATE`, since a degraded second walk finds FEWER paths and must not erase a good first read — and the writer runs on the cascade's SKIPPED row too, so a re-ship repairs what a derivation outage missed. **The share is weighted by PAYOUT, not by task count**, or deleting a one-line fix dims a star as hard as deleting the feature; weighting by what each hop earned also makes 'large and dead' exact, and makes the two channels a share of ONE number rather than two unrelated measurements. Basis is paid + estimated to match size, which required `priceDescentForIdea`/`ideaSizes` to return a per-task `byTask` summing EXACTLY to `credits` — including ledger payments for tasks the descent no longer reaches, which `idea-size.js` keeps in `credits` so size cannot fall and which would otherwise make the parts sum to less than the whole. Three edges, each silent: an idea that earned nothing is `share: 1, hasWork: false`, because zero over zero is not zero and a protostar is not a remnant (the BV2.R19 distinction); an unreadable removal port gives `share: null, attributable: false`, because 'could not look' is not 'nothing was removed'; and a CONFIRMED removal naming no target dims nothing at all — brightness cannot know which idea — so `countUnattributedConfirmed()` exists to say the gap out loud instead of quietly brightening the sky. Brightness lives in `economy` beside size and reaches lifecycle through the new `lifecycle.removals` port, resolved OPTIONALLY and NOT declared in `consumes` (declaring it would turn 'lifecycle off' into 'instance will not start' — the `ideas.descent` precedent). **No credit is clawed back and nothing here writes**: ADR 0313 stands, `shipped` is terminal, only the picture changes. Rejected: parsing the proposal body (ADR 0319 already refused it — a human-facing line shape fails silently when reworded); re-deriving at verdict time; treating `ship_task_id` as the removed task; counting tasks rather than payouts; clamping the share at the top, which would hide the anomaly it claims to prevent.](0321-brightness-is-a-weighted-share-and-the-targets-are-a-row.md) | ideas / economy |
416
416
  | 0322 | [**The five states are derived once, and a remnant needs evidence** ([task 1004103](https://cloudbongos.com/builders#/task/1004103), goal 1000095 — BONGOS-V2). The ideator surface draws a thought as one of five objects — star, protostar, satellite, remnant, cold body — and three surfaces are about to draw them (the room, the descent, the sky). Every fact already existed: `idea_inbox.status` (`core_195`), [ADR 0321](0321-brightness-is-a-weighted-share-and-the-targets-are-a-row.md)’s `hasWork` / `share` / `attributable`, and `tasks.goal_id` + `tasks.status`. What was missing was ONE place that reads them: a state inferred in three renderers is three states, and the sky can say a thought went out while the room beside it still calls it alive, with nothing erroring. **Decision:** one pure resolver (`modules/ideas/idea-state.js`), brightness exposed on the `reward` port (ideas may not reach `credit_log` any other way — [ADR 0083](0083-modular-architecture-strangler-build-plan.md) rule 1 — and already declares `reward` in `consumes`), and the ORDER of the tests IS the decision. **MERGED wins over everything:** a folded thought keeps its own money (`trail-size.js` is emphatic) but is no longer its own point in the sky, so letting work win draws one idea twice, beside the thought that absorbed it. **WORK beats a DISCARD:** a cold body “never ignited, so it has no size at all” is a stated PROPERTY, and testing work first makes it true by construction — triage really can discard a duplicate whose twin already routed and paid, and `updateIdea` never touches the ledger. **A REMNANT needs EVIDENCE, never the absence of an answer:** `share: 0` is a confirmed removal, `share: null` is an unreadable one, and ADR 0321 pays for that distinction upstream — so an idea with work and no readable facts is a STAR carrying `attributable: false`, because reading a falsy share as death turns one unmounted module into a sky of hollow rings. **A PROTOSTAR is the absence of WORK, not of a status:** ignition is being PAID for something, so a promoted-but-unshipped idea is still unignited, which is exactly what the nod queue exists for. **Position is a second axis** (`well` / `open-space` / `awaitingNod`) because the design says the object alone cannot carry it, and the nod is asked of EVERY task, not only the one that named the well. **`suggested_goal_id` is deliberately NOT a well** — `projection.js` records it as strictly advisory, the routing hint on a filing that did not convert, so reading it would put a thought in a nod queue with no task to promote; the binding `tasks.goal_id` now rides on both of the trail’s task joins at no extra round trip. The page reads the ledger ONCE: the state attach hands economy back the `byTask` breakdown size just computed, all of them or none (a partial map reads a missing entry as “no work” and draws a paid star as a protostar). An absent `reward` port does not empty the sky — states still resolve, flagged unattributable. Nothing writes; ADR 0313 stands. Rejected: resolving in each renderer; deriving from `share` alone (it merges a protostar with a remnant, the exact BV2.R19 distinction); treating an unreadable share as zero; status-first ordering; reading the hint as a home; a sixth object for “unknown brightness” (the vocabulary is the owner’s and is five — the uncertainty is a flag on the answer, not a new thing in the sky).](0322-a-remnant-needs-evidence-and-merged-wins.md) | ideas / economy |
417
417
  | 0323 | [**Hosting is three shapes, and we are not the landlord** ([task 1004140](https://cloudbongos.com/builders#/task/1004140), goal 1000106 — BONGOS-V2). Five projects ran as one Node process each on the control-plane droplet, about $0.80/month apiece — the cheapest arrangement available, bought with ~4,600 lines of `provision*.js` that write systemd units, create unix accounts, render Caddy snippets, `createdb`, install backup timers and count memory against a ceiling. That is the cost nobody priced: **we are a landlord.** The owner reframed the question — build on what others maintain, and let an owner keep the deployment platform they already use. **Seven decisions.** The shapes become `byo-host` (the owner runs it), `cloud-host` (we stand it up for them) and `dedicated-cloud-host` (the same path, larger plan); `co-tenant` is **retired, not renamed**, because it means *a process on our box from our checkout* — the opposite of bring-your-own-host, so reusing the name would make every existing comment lie. **DigitalOcean hosts hubs only**, which closes [ADR 0145](0145-free-hosted-project-tier-isolation-and-domain-separation.md) Finding 1 by removing its precondition rather than hardening the cookie: with no customer code on a `cloudbongos.com` subdomain there is no tenant to receive the hub session. **Render is the default** — its Postgres carries PITR and automated backups on every paid tier; Fly's managed Postgres starts ~$38/mo and Railway's is explicitly unmanaged, which is the exact responsibility being shed. **Cloudflare is the front door, not the compute**: Workers is a V8-isolate runtime that would require rewriting Express and `pg`, and Containers is built for bursty workloads that sleep, which a hall with background sweeps and a live event stream does not survive. **No deployment-platform adapter is built at all** — their application already deploys itself from the repository, so the platform we do not talk to is the platform we do not have to add. **Secrets: the platform is the system of record and Bongos stores names, never values** — a manifest of name, purpose and environment, no application secret, no platform API token, no "is it set?" probe, because each of those makes us custodian of other companies' credentials for convenience the manifest already delivers; `byo-host` marks the manifest unverifiable rather than opening an exception. **The free tier becomes a capped trial**, since ~$14/project cannot be subsidised indefinitely. Per-project cost rises ~17×, which is the price of not being a landlord and the reason metering stops being deferred work. Mostly a DELETION: [ADR 0306](0306-instance-supervision-is-a-seam-with-systemd-as-the-default.md)'s supervision seam is not extended (Render supervises its own processes), [ADR 0285](0285-a-shared-box-holds-about-twelve-projects-per-gb-and-memory-is-the-wall.md) stays true and stops being load-bearing with its queued resize held rather than spent, and the hub's offsite backup becomes the highest-consequence open item. Rejected: AWS/GCP (2–3× the cost for a bare instance, plus an IAM and billing surface to learn — maintenance added where the goal is to remove it); keeping the shared box and fixing only the cookie (cheaper, closes the finding, leaves the landlord runner and answers nothing else); a Vercel/Netlify/Render adapter layer (speculative pluggability); Bongos as the secrets vault pushing values through platform APIs.](0323-hosting-is-three-shapes-and-we-are-not-the-landlord.md) | hosting / provisioning / cost |
418
+ | 0324 | [**The public npm CLI channel is retired** ([task 1004144](https://cloudbongos.com/builders#/task/1004144), goal 1000091 — BONGOS-V2). The owner deleted `@cloudbongos/cli` from the registry on 2026-09-22 (`npm view` → E404; the `@cloudbongos` scope is empty). This supersedes [ADR 0258](0258-the-public-cli-is-a-generated-client-package-not-the-published-core.md) and [ADR 0292](0292-both-install-paths-stay-and-vendoring-is-the-production-one.md) §2 — the latter titled "`@cloudbongos/cli` STAYS, on a new justification" and written *because* task 1003872 proposed removing it once the public core flip dissolved its original reason. 0292's measured argument (~0.5 MB front door vs a 46 MB core pull to print a task list) is still true and is NOT what changed; the owner's decision about carrying a public channel at all is. `scripts/gds/build-cli-package.js` + `tests/cli_package.mjs` are deleted, no doc offers `npx @cloudbongos/cli`, and the way in is the hall at `<origin>/builders` or a checkout. The COMPILED binary channel (`build-bongos.js` → `/downloads/bongos/*` via `config/bongos-cli.json`) is a different artifact and is untouched — the same near-twin task 1003889 warned about. Three tests used the package's FILES manifest as a stand-in for "what a terminal user can run" and now read `bin/bongos.js`'s verb table, which is what that set always meant.](0324-the-public-npm-cli-channel-is-retired.md) | distribution / CLI |
package/docs/file-map.md CHANGED
@@ -174,7 +174,6 @@
174
174
  │ ├── ship.js ← resolve claim as shipped, award credits (`/builder-ship`); also uploads a secret-scrubbed session digest to the corpus (6D.1, ADR 0027); on a dev box runs the sandbox-first review gate before resolving (#927, ADR 0046)
175
175
  │ ├── ship-visual.js ← the OPTIONAL `--visual <image> [--visual-alt "…"]` leg of a ship (task 1003109): screens the file BEFORE the claim resolves (bad input costs no claim), uploads it AFTER (a failed picture never fails a ship)
176
176
  │ ├── package-core.js ← package the core as a versioned, installable artifact + pin manifest (`bongos package-core`; R84, ADR 0100 §1, [#1688](https://example.com/builders#/task/1688)): isPublishable() selection + mirror redaction + fail-closed no-leak gate → dist/bongos-core-<version>.{tgz,manifest.json}; version = src/module-api CORE_VERSION. Recipe: docs/recipes/packaging-the-core.md
177
- │ ├── build-cli-package.js ← generate the PUBLIC, client-only `@cloudbongos/cli` npm package from this repo (task 1003679, ADR 0258): declared FILES manifest (no server/routes/auth/pool/provisioning) + generated dispatcher, README and package.json whose `files` array is DERIVED from the manifest; refuses to emit on a relative `import()` the manifest omits, and on a redaction hit (non-loopback IPv4, token/key shapes, instance domains + owner login read from config/branding.json). Sibling of package-core.js, opposite audience: that one packages the private core for an instance, this one packages the client surface for the public. Proven by tests/cli_package.mjs, which installs the packed tarball into an empty dir and runs every verb. `npm publish` is the OWNER's step, never a builder's → dist/cloudbongos-cli/
178
177
  │ ├── surface.js ← who the CLI output is FOR: agent (default) or terminal (task 1003680). The `bongos` dispatcher sets BONGOS_SURFACE=terminal when a person types a verb; the skills and the card hook run `node scripts/gds/<verb>.js` directly and keep the agent card byte-identical. Never inferred from isTTY — a session's stdout is not a TTY either, so sniffing would invert it. Helpers: action() (/builder-ship N vs bongos ship N), taskRef(), strong()/em()/code(), table() (markdown vs aligned columns). Guarded by tests/cli_surface.mjs
179
178
  │ ├── sandbox-stage.js ← stage the working tree on the live game preview (box OR local) for browser review before ship; resolvePreviewContext picks the context (`/builder-stage`; #927, ADR 0046; local: task 1056)
180
179
  │ ├── local-preview.js ← builder CLI for the LOCAL sandbox: start/stop/status/restart/logs/url of the game-only preview at http://localhost:3100 (`npm run preview`; task 1056)
@@ -2368,5 +2368,9 @@ is load-bearing: the script throws rather than guess if it is missing, and
2368
2368
  landed since 1.19.945 with no explicit bump. run 35669244197. (task 1002620)
2369
2369
  1.19.947 — CI auto-patch (publish-on-merge, ADR 0161): carrier for merges
2370
2370
  landed since 1.19.946 with no explicit bump. run 35671409094. (task 1002620)
2371
+ 1.19.948 — CI auto-patch (publish-on-merge, ADR 0161): carrier for merges
2372
+ landed since 1.19.947 with no explicit bump. run 35671807404. (task 1002620)
2373
+ 1.19.949 — CI auto-patch (publish-on-merge, ADR 0161): carrier for merges
2374
+ landed since 1.19.948 with no explicit bump. run 35675336642. (task 1002620)
2371
2375
  ---------------------------------------------------------------------------
2372
2376
  ```
@@ -220,7 +220,7 @@ caught a real bug during task 1003548, so read its assertion before blaming the
220
220
  ## Four Windows traps that make a test lie — all four found in one pass (task 1003529)
221
221
 
222
222
  Four of the suites that pass turned green came back to **real product bugs, not test noise** —
223
- including `build-cli-package.js`, which could not build at all on Windows. When a suite fails only
223
+ including the since-retired `build-cli-package.js` (ADR 0324), which could not build at all on Windows. When a suite fails only
224
224
  here, suspect these before assuming the platform is at fault:
225
225
 
226
226
  - **`fs.realpathSync` does NOT expand 8.3 short names; `fs.realpathSync.native` does.** Where TEMP
@@ -231,7 +231,7 @@ here, suspect these before assuming the platform is at fault:
231
231
  - **A REPO-relative path must be POSIX on every platform** — it goes into a task's `touches[]`, a
232
232
  version row, or a generated manifest. `path.join`/`path.relative` emit `\` here. Use
233
233
  `path.posix.*`, or the house spelling `.split(path.sep).join('/')`. This was the bug in
234
- `version-close.js`, the Figma adapter and `build-cli-package.js`; the giveaway is a function
234
+ `version-close.js` and the Figma adapter; the giveaway is a function
235
235
  whose own fallback branch hard-codes `/`.
236
236
  - **Node 24 refuses to spawn a `.cmd`**: bare `npm` is `ENOENT` and `npm.cmd` is `EINVAL` (the
237
237
  CVE-2024-27980 mitigation), both surfacing as an inscrutable `null !== 0` from `spawnSync`.
@@ -3,8 +3,8 @@
3
3
  //
4
4
  // WHY THIS FILE EXISTS. `parseCriterionRef` lived inside db-deps-criteria.js,
5
5
  // which requires ../../src/module-api and therefore a database pool. The read
6
- // side — scripts/gds/status.js, which is public, unauthenticated, and shipped in
7
- // the @cloudbongos/cli package with no server present — could not require it, so
6
+ // side — scripts/gds/status.js, which is public and unauthenticated — could not
7
+ // require it, so
8
8
  // it grew its own parser: `Number(ref.slice(1))`, positional only. The system
9
9
  // ended up PRINTING an identifier its own write path refuses as unsafe, because
10
10
  // the two sides never shared a definition of what a ref is. Carving the pure
@@ -282,8 +282,7 @@ async function removeDependency(fromKind, fromId, toKind, toId) {
282
282
  //
283
283
  // The DEFINITION lives in ./criterion-ref.js, which requires nothing at all —
284
284
  // because the READ side needs the same vocabulary and cannot pay for a pool.
285
- // scripts/gds/status.js is public, unauthenticated and shipped inside the
286
- // server-free @cloudbongos/cli, so while this classifier sat behind
285
+ // scripts/gds/status.js is public and unauthenticated, so while this classifier sat behind
287
286
  // ../../src/module-api it was unreachable from there and the CLI grew its own
288
287
  // positional-only parser (`Number(ref.slice(1))`). The system ended up PRINTING
289
288
  // and REQUIRING the very ref shape resolveCriterionIds below refuses as unsafe
package/package-lock.json CHANGED
@@ -1,12 +1,12 @@
1
1
  {
2
2
  "name": "@bongos/core",
3
- "version": "1.19.947",
3
+ "version": "1.19.949",
4
4
  "lockfileVersion": 3,
5
5
  "requires": true,
6
6
  "packages": {
7
7
  "": {
8
8
  "name": "@bongos/core",
9
- "version": "1.19.947",
9
+ "version": "1.19.949",
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.947",
3
+ "version": "1.19.949",
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",
@@ -272,7 +272,8 @@ async function loadSession() {
272
272
  //
273
273
  // WHY A FIXED ANCHOR. configHome() is brand-derived, and the brand is read out of whatever checkout
274
274
  // the process is standing in. That gave the CLI exactly one session slot, and the slot MOVED: run
275
- // standalone (the public @cloudbongos/cli's whole situation) the brand cannot resolve, so every
275
+ // standalone (the then-public @cloudbongos/cli's whole situation; ADR 0324 retired it) the brand
276
+ // cannot resolve, so every
276
277
  // instance shared one gds-session.json and signing into a second DESTROYED the first; run from
277
278
  // inside an instance repo and the session landed in ~/.config/<slug>/ where the standalone CLI
278
279
  // could never find it. The owner's own config dir carries a hand-made `hermeslines-clobber` backup
@@ -374,14 +374,29 @@ function renderPackageJson(spec) {
374
374
  },
375
375
  files: ['index.mjs', 'index.cjs', 'index.d.ts', 'bongos-client.global.js', 'README.md'],
376
376
  license: 'AGPL-3.0-or-later',
377
+ // `private: true` is the ENFORCEMENT of the paragraph below (task 1004143). Task
378
+ // 1003742 removed the publishConfig, which stopped the repo CLAIMING this was a
379
+ // published package — but nothing stopped it BECOMING one again: `npm publish` in
380
+ // clients/bongos-client/ succeeded and would have recreated @bongos/client@0.0.2,
381
+ // the exact package the owner deleted from the registry on 2026-09-09. npm now
382
+ // refuses with EPRIVATE, so that removal cannot be silently undone by anyone who
383
+ // runs publish in the wrong directory.
384
+ //
385
+ // This is not the publish posture the note below calls fiction. That was declaring
386
+ // HOW it publishes (access: restricted) for something never published; this
387
+ // declares that it does not publish, which is the same fact the note states in
388
+ // prose. It does not affect `main`/`types`/`exports`, so the vendored copy and a
389
+ // workspace link resolve exactly as before.
390
+ private: true,
377
391
  // NO publishConfig — this client is NOT an npm package (task 1003742).
378
392
  //
379
393
  // It was published once, 2026-07-07 at 0.0.2, under ADR 0134's private-first plan, and never
380
394
  // updated while the API it is generated FROM moved 500+ core versions. It had zero consumers
381
395
  // the whole time: nothing across the core or any instance repo ever declared it as a
382
- // dependency — every consumer VENDORS a copy instead (modules/dev-box/app/src/vendor/, and
383
- // build-cli-package.js into @cloudbongos/cli, which could not depend on it anyway). The owner
384
- // removed it from the registry on 2026-09-09.
396
+ // dependency — every consumer VENDORS a copy instead. That was two places when this was
397
+ // written; task 1003891 deleted the Dev Box Electron app and ADR 0324 retired
398
+ // @cloudbongos/cli, so there is now no vendoring consumer left at all. The
399
+ // owner removed it from the registry on 2026-09-09.
385
400
  //
386
401
  // The manifest itself stays: `main`/`types`/`exports` are what make the vendored copy and a
387
402
  // workspace link resolve. Declaring a publish posture is the part that was fiction.
@@ -22,11 +22,13 @@ const { plainCardLines } = require('./plain-cards');
22
22
  // string. (Found by the grader on this task, and it was right: the first cut
23
23
  // interpolated the path into a shell string.)
24
24
  //
25
- // Kept local rather than imported from ship-git.js on purpose: release.js ships
26
- // in the PUBLIC client-only @cloudbongos/cli package, and nothing in the ship-*
27
- // family is in that manifest. tests/cli_package.mjs installs the tarball and RUNS
28
- // every verb, so an import from outside it is a crash at require time, not a
29
- // lint-level nit — that is exactly how this file broke once already.
25
+ // Kept local rather than imported from ship-git.js. The original reason was that
26
+ // release.js shipped in the public @cloudbongos/cli package, which carried no ship-*
27
+ // file, so an import from that family crashed at require time — that is how this file
28
+ // broke once already. That package was retired (ADR 0324), so the constraint is no
29
+ // longer enforced by anything; the duplication is left as-is rather than quietly
30
+ // re-coupled inside a packaging change. Re-joining them is now a free choice, not a
31
+ // forbidden one.
30
32
  function gitAt(args, cwd) {
31
33
  try {
32
34
  return execFileSync('git', args, { cwd, encoding: 'utf8', stdio: ['ignore', 'pipe', 'pipe'] }).trim();
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.947'; // CI auto-patch carrier (ADR 0161); changelog: docs/module-api-changelog.md
74
+ const CORE_VERSION = '1.19.949'; // 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');
@@ -303,3 +303,40 @@ test('--check reads a CRLF working copy as fresh, and a real edit as stale (task
303
303
  fs.rmSync(root, { recursive: true, force: true });
304
304
  }
305
305
  });
306
+
307
+ // ── the generated manifest must refuse to become an npm package (task 1004143) ──
308
+ //
309
+ // @bongos/client was published once (2026-07-07, 0.0.2), gathered zero consumers —
310
+ // everyone vendors a copy — and the owner deleted it from the registry on
311
+ // 2026-09-09. Task 1003742 removed the publishConfig so the repo stopped CLAIMING
312
+ // it was published, but nothing stopped it BECOMING published again: `npm publish`
313
+ // in clients/bongos-client/ succeeded and would have recreated it.
314
+ //
315
+ // WHY THIS IS ASSERTED ON THE MANIFEST AND NOT BY RUNNING npm. `npm publish
316
+ // --dry-run` is NOT a test of this and passes with `private: true` set: npm's
317
+ // dry-run path returns in lib/commands/publish.js before it ever calls
318
+ // libnpmpublish, and libnpmpublish/lib/publish.js is where the real guard lives —
319
+ // `if (manifest.private) throw EPRIVATE` as its first statement, unconditional and
320
+ // ahead of any network call. So a real publish IS blocked; a dry-run simply never
321
+ // reaches the check. Asserting the field is the honest test at this layer, and it
322
+ // is the part this repo actually controls.
323
+ test('the generated client manifest is private, so it cannot be republished', () => {
324
+ const pkg = JSON.parse(fs.readFileSync(
325
+ path.join(ROOT, 'clients', 'bongos-client', 'package.json'), 'utf8'));
326
+ assert.equal(pkg.private, true,
327
+ 'clients/bongos-client/package.json lost `private: true` — npm publish in that directory '
328
+ + 'would recreate @bongos/client, the package the owner removed from the registry.');
329
+ // The manifest still has to RESOLVE: 1003742 kept these on purpose, because the
330
+ // vendored copy and a workspace link go through them. `private` must not cost that.
331
+ for (const field of ['main', 'types', 'exports']) {
332
+ assert.ok(pkg[field], `the manifest lost \`${field}\` — the vendored copy resolves through it`);
333
+ }
334
+ });
335
+
336
+ test('the GENERATOR emits it, not just the committed file', () => {
337
+ // Fixing only the output is a fix that the next regeneration undoes.
338
+ const src = fs.readFileSync(path.join(ROOT, 'scripts', 'gds', 'gen-api-client.js'), 'utf8');
339
+ assert.match(src, /private:\s*true/,
340
+ 'gen-api-client.js no longer emits `private: true` — the next regeneration would '
341
+ + 'make clients/bongos-client publishable again.');
342
+ });
@@ -132,9 +132,8 @@ test('only the dispatchers declare the terminal surface', () => {
132
132
  assert.ok(execLine, 'could not find the exec spawn');
133
133
  assert.ok(!execLine.includes('BONGOS_SURFACE'), '`bongos exec` must stay on the agent surface');
134
134
 
135
- const builder = fs.readFileSync(path.join(REPO_ROOT, 'scripts', 'gds', 'build-cli-package.js'), 'utf8');
136
- assert.match(builder, /BONGOS_SURFACE: 'terminal'/, 'the generated dispatcher must mark spawns as terminal');
137
- assert.match(builder, /'scripts\/gds\/surface\.js'/, 'surface.js must ship in the package');
135
+ // The generated public dispatcher was asserted here too; @cloudbongos/cli was retired
136
+ // (task 1004144), so bin/bongos.js above is now the only dispatcher there is.
138
137
  });
139
138
 
140
139
  test('the skills still invoke the scripts directly, which is what keeps agents on the agent surface', () => {
@@ -154,13 +153,27 @@ test('the skills still invoke the scripts directly, which is what keeps agents o
154
153
  import { spawnSync } from 'node:child_process';
155
154
  import fs from 'node:fs';
156
155
 
157
- // Every .js the public package ships, read out of the build manifest so a newly-shipped script
158
- // is covered the day it is added rather than the day someone remembers to list it here.
156
+ // Every script a TERMINAL reader can actually invoke, read out of bin/bongos.js's verb
157
+ // table so a newly-added verb is covered the day it lands.
158
+ //
159
+ // This used to read the FILES manifest of scripts/gds/build-cli-package.js. That package
160
+ // (@cloudbongos/cli) was retired with the manifest (task 1004144), but the rule it fed
161
+ // did not go with it: a script that prints a `/builder-*` slash command to someone in a
162
+ // terminal is telling them to run something they do not have.
163
+ //
164
+ // The verb table is the right heir, not `readdirSync` of scripts/gds/. What made the old
165
+ // set meaningful was never "these files were published" — it was "a terminal user can
166
+ // reach these", and the dispatcher's VERBS is exactly that set today. Scanning the whole
167
+ // directory instead looks stricter and is actually a different test: it immediately
168
+ // flagged six scripts (backfill-sessions, box-auth-check, box, local-preview,
169
+ // sandbox-stage, ship-flow) that no terminal reader can invoke, which is six unrelated
170
+ // findings imported into a packaging task rather than a stronger guard.
159
171
  function shippedScripts() {
160
- const src = fs.readFileSync(path.join(REPO_ROOT, 'scripts', 'gds', 'build-cli-package.js'), 'utf8');
161
- const block = src.match(/const FILES = \[([\s\S]*?)\n\];/);
162
- assert.ok(block, 'could not read the FILES manifest out of build-cli-package.js');
163
- return [...block[1].matchAll(/'([^']+\.js)'/g)].map((m) => m[1]);
172
+ const src = fs.readFileSync(path.join(REPO_ROOT, 'bin', 'bongos.js'), 'utf8');
173
+ const out = [...new Set([...src.matchAll(/script:\s*'([A-Za-z0-9._-]+\.js)'/g)].map((m) => `scripts/gds/${m[1]}`))].sort();
174
+ // A scan that matches nothing passes every assertion below for free.
175
+ assert.ok(out.length > 10, `only ${out.length} verb script(s) found in bin/bongos.js — the scan is reading the wrong shape`);
176
+ return out;
164
177
  }
165
178
 
166
179
  // Run one cli-lib guidance printer in a child process (they all call process.exit) and return
@@ -86,10 +86,13 @@ test('every allowlisted file exists and still means HOME differently', () => {
86
86
  }
87
87
  });
88
88
 
89
- test('the two sites this task fixed are actually fixed', () => {
90
- const src = readFileSync(path.join(REPO_ROOT, 'tests', 'cli_package.mjs'), 'utf8');
91
- assert.deepEqual(offendingLines(src), []);
92
- assert.match(src, /USERPROFILE/, 'cli_package must set USERPROFILE alongside HOME');
89
+ // Task 1003763 fixed TWO sites. tests/cli_package.mjs was the other one; it was deleted
90
+ // with the @cloudbongos/cli package it tested (ADR 0324), so only the surviving site is
91
+ // pinned here. The rule itself is unchanged — a test that points HOME at a sandbox
92
+ // without also setting USERPROFILE isolates nothing on win32 — and the allowlist test
93
+ // above still watches every other file that touches HOME.
94
+ test('the site this task fixed is actually fixed', () => {
93
95
  const surface = readFileSync(path.join(REPO_ROOT, 'tests', 'cli_surface.mjs'), 'utf8');
96
+ assert.match(surface, /HOME/, 'cli_surface no longer sets HOME — this guard is watching the wrong file');
94
97
  assert.deepEqual(offendingLines(surface), []);
95
98
  });