@bongos/core 1.19.360 → 1.19.361

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.360",
6
- "core_contract": "1.19.360",
7
- "source_commit": "5bf79048217d490a0d571e749290b110875a0bc8",
5
+ "core_version": "1.19.361",
6
+ "core_contract": "1.19.361",
7
+ "source_commit": "52986b2ce513fb2d4550b8ad303333c6f02daafb",
8
8
  "source_ref": "HEAD",
9
- "built_at": "2026-08-25T17:58:51.325Z",
9
+ "built_at": "2026-08-25T18:21:25.463Z",
10
10
  "redaction": {
11
11
  "model": "docs-redacted+functional-verbatim",
12
12
  "docs_redacted": 330,
13
13
  "agent_docs_stubbed": 22,
14
- "functional_verbatim": 1523,
14
+ "functional_verbatim": 1525,
15
15
  "rules": 3,
16
16
  "gate_literals": 3,
17
17
  "gate": "passed"
18
18
  },
19
- "file_count": 1875,
20
- "tree_sha256": "15871fd570e735b51bbfd212b53fcc55faa1479569e51e6d2c7179f407bb58d3",
19
+ "file_count": 1877,
20
+ "tree_sha256": "ec6c330506400cf439253a53651300f6be6176f28375130cc28ba0949823736c",
21
21
  "files": [
22
22
  {
23
23
  "path": ".claude/skills/blocker-review/SKILL.md",
@@ -1597,7 +1597,7 @@
1597
1597
  {
1598
1598
  "path": "docs/file-map.md",
1599
1599
  "mode": "0000644",
1600
- "sha256": "74102dbb050a3e9df3ade05ef25d8704319e7b1fac801b784f1ed8c3ea1b7566"
1600
+ "sha256": "1cda168698104ff12fbe75a28a5f76fd11a2d7311c8ba32ba985874dbff65570"
1601
1601
  },
1602
1602
  {
1603
1603
  "path": "docs/handoff-template.md",
@@ -2537,7 +2537,7 @@
2537
2537
  {
2538
2538
  "path": "modules/autonomy/routes/autonomy.js",
2539
2539
  "mode": "0000644",
2540
- "sha256": "aa0c84ab532bbc946d80cf625d130b9ef2ba8e2e54cd320945aa1dda6b210488"
2540
+ "sha256": "ae9aff3f47f2b5801f9839493549b912424e8b22c153e91afcf3376a1cc4fb4d"
2541
2541
  },
2542
2542
  {
2543
2543
  "path": "modules/builder-settings/CLAUDE.md",
@@ -2827,7 +2827,7 @@
2827
2827
  {
2828
2828
  "path": "modules/dev-box/app/src/config.js",
2829
2829
  "mode": "0000644",
2830
- "sha256": "a29e9b2ac8353ec20e572597a288078c1e31339651cfdf396f71f3ae47495209"
2830
+ "sha256": "689935bb1901be4f31877207e7885be21630b9eafa6afe5fa025b1ff1dce4f83"
2831
2831
  },
2832
2832
  {
2833
2833
  "path": "modules/dev-box/app/src/gds-client.js",
@@ -3032,7 +3032,7 @@
3032
3032
  {
3033
3033
  "path": "modules/economy/achievements.js",
3034
3034
  "mode": "0000644",
3035
- "sha256": "fc270efbe9439442d2f51490a8a292313e3f66c7b5d33c28cd9e7f929f56b307"
3035
+ "sha256": "000cb76c8bfd4a7a6f3544f4fae923691a7596b6caed0af3b324df91ff72dfcb"
3036
3036
  },
3037
3037
  {
3038
3038
  "path": "modules/economy/cost-classify.js",
@@ -3077,12 +3077,12 @@
3077
3077
  {
3078
3078
  "path": "modules/economy/routes/cost.js",
3079
3079
  "mode": "0000644",
3080
- "sha256": "7b85120b73500c91e98e8a580709d7f6a0fddad784d19d4ea75b2828cff508c7"
3080
+ "sha256": "4113b736359f09d6869795800d52394603823b570263dd1852281b35e6bb2485"
3081
3081
  },
3082
3082
  {
3083
3083
  "path": "modules/economy/routes/leaderboard.js",
3084
3084
  "mode": "0000644",
3085
- "sha256": "36d91233de89844b129f08a5fe2e6f5510b0811a1e2ebc99b9d2e12d878bad65"
3085
+ "sha256": "e8c6baf3e5bbd54c9dc79f74a9a562a6c550e75952bbd1010a11bd26dec17833"
3086
3086
  },
3087
3087
  {
3088
3088
  "path": "modules/economy/routes/reward.js",
@@ -3902,12 +3902,12 @@
3902
3902
  {
3903
3903
  "path": "modules/ideas/routes/blockers.js",
3904
3904
  "mode": "0000644",
3905
- "sha256": "2247bb4499e6296fca8ccde3614b6726a50435786fc3cf634eaacd35982ef3f4"
3905
+ "sha256": "61c7692449f16ce103762d703f2eb5b0412cf1ba393d9a4d3724a14830747ab5"
3906
3906
  },
3907
3907
  {
3908
3908
  "path": "modules/ideas/routes/inbox.js",
3909
3909
  "mode": "0000644",
3910
- "sha256": "0215d0eadab112c023af4e67a1fa81060b0844334d29e3943f8c388f64cda533"
3910
+ "sha256": "cfe139439ef93d42718354fd10781e0aa11f5f8a730d64dbf09c7232305daaaf"
3911
3911
  },
3912
3912
  {
3913
3913
  "path": "modules/ideas/routing.js",
@@ -4127,7 +4127,7 @@
4127
4127
  {
4128
4128
  "path": "modules/lifecycle/routes/claims.js",
4129
4129
  "mode": "0000644",
4130
- "sha256": "fb5dda00e0c378e2fb8b3190fdeaf4562be65097895bd67b68e67428a14c8297"
4130
+ "sha256": "6bc32c6ae4029748d6099701f25fc17d5875b2ff005de5ad2cacbc023092c32c"
4131
4131
  },
4132
4132
  {
4133
4133
  "path": "modules/lifecycle/routes/closeness.js",
@@ -4187,12 +4187,12 @@
4187
4187
  {
4188
4188
  "path": "modules/lifecycle/routes/tasks.js",
4189
4189
  "mode": "0000644",
4190
- "sha256": "b29d9bffce9c81e0f5e02ea2da387e15dc6f0bf8d96820931c29275861eeaf66"
4190
+ "sha256": "e27a8ce945d8d7e68d618e60b3cac72af9722588affb876fa22df0f525ec3dc5"
4191
4191
  },
4192
4192
  {
4193
4193
  "path": "modules/lifecycle/routes/versions.js",
4194
4194
  "mode": "0000644",
4195
- "sha256": "044ca86e5605c19dd8e523da9eb061b4a90ed0af199ec1288c3c5f1b1bb8f46b"
4195
+ "sha256": "77183a22af7bd1f506f2b3d2cba176e89af578ce5aa2da6abe0ee353e3ec8cbd"
4196
4196
  },
4197
4197
  {
4198
4198
  "path": "modules/lifecycle/routes/visuals.js",
@@ -4267,7 +4267,7 @@
4267
4267
  {
4268
4268
  "path": "modules/memory/routes/learnings.js",
4269
4269
  "mode": "0000644",
4270
- "sha256": "1772c54a56e3ceb4fe10b96d036c4b51ea102f5a5c989db0ad188bbf2adc25d6"
4270
+ "sha256": "c205d67b5fe840af28cd7f6b9a02fa2f8a8287a992bd009bbcd6f7bcf733043b"
4271
4271
  },
4272
4272
  {
4273
4273
  "path": "modules/memory/routes/memory.js",
@@ -4847,12 +4847,12 @@
4847
4847
  {
4848
4848
  "path": "package-lock.json",
4849
4849
  "mode": "0000644",
4850
- "sha256": "26d9ebcd21d9fa6f7634d877e028131c6beb6e144d3055645e159d81e94c64a3"
4850
+ "sha256": "f1a896b55fbf77414ebf57311585f596768d6278a3543e0e3cc2b3b29e9ddc5e"
4851
4851
  },
4852
4852
  {
4853
4853
  "path": "package.json",
4854
4854
  "mode": "0000644",
4855
- "sha256": "2f3161d67426337fc350354987df123ccc867ebbb15ae4bf28e3082f80edbdcd"
4855
+ "sha256": "dd516fa3534fbadfa1dc22fabae2226206d88e1857b5a346dad8cbfa45c89ca9"
4856
4856
  },
4857
4857
  {
4858
4858
  "path": "public-docs/index.html",
@@ -5352,7 +5352,7 @@
5352
5352
  {
5353
5353
  "path": "scripts/gds/claude-md-template.js",
5354
5354
  "mode": "0000644",
5355
- "sha256": "81c4f4795291f9daef800dcc17b65aec8b82f6281434b0f3e52655789a481072"
5355
+ "sha256": "68f2c65ac9297328f44927ccb9a40a0fa536b91d9056aaa980a92149a64ce47c"
5356
5356
  },
5357
5357
  {
5358
5358
  "path": "scripts/gds/cleanup-orphans.js",
@@ -5479,6 +5479,11 @@
5479
5479
  "mode": "0000644",
5480
5480
  "sha256": "9cf6c8ead8129799d17a18eadedf0423828cb457fa07fd317dcf2853254861e9"
5481
5481
  },
5482
+ {
5483
+ "path": "scripts/gds/doc-comment-coverage.js",
5484
+ "mode": "0000644",
5485
+ "sha256": "17d0fbc570b6dea3dfcf2425058510cd70b23f066fdf1ed751072852713284a3"
5486
+ },
5482
5487
  {
5483
5488
  "path": "scripts/gds/docs-entropy.js",
5484
5489
  "mode": "0000644",
@@ -5537,7 +5542,7 @@
5537
5542
  {
5538
5543
  "path": "scripts/gds/fitness.js",
5539
5544
  "mode": "0000644",
5540
- "sha256": "91b125de588406e6b93f8cc29d4d53891932a52744a6e92824459c734f747f16"
5545
+ "sha256": "e38069a6eee1072438d745634cdae405c57c20b8261f133d4afa0440b1d3d545"
5541
5546
  },
5542
5547
  {
5543
5548
  "path": "scripts/gds/gate-review.js",
@@ -5732,7 +5737,7 @@
5732
5737
  {
5733
5738
  "path": "scripts/gds/me.js",
5734
5739
  "mode": "0000644",
5735
- "sha256": "f7246b3d527692913b164c07b6040013e9152302da9c7bff7274de8cb1295da5"
5740
+ "sha256": "e2591cd7a867120cbf5bda0ba7460508ab0342c3dfcdaecec88a29290fec1566"
5736
5741
  },
5737
5742
  {
5738
5743
  "path": "scripts/gds/memory-map.js",
@@ -6602,7 +6607,7 @@
6602
6607
  {
6603
6608
  "path": "src/bongos/routes/auth.js",
6604
6609
  "mode": "0000644",
6605
- "sha256": "be7b68b01d82fb3ac714d92f22b6adbc1788fb47b9ce8345d4cf14bd1fb92e94"
6610
+ "sha256": "39392e56faf8fb7efb52686a006d3dd5dbb4b965dd81402d6a4d7a7c173923bb"
6606
6611
  },
6607
6612
  {
6608
6613
  "path": "src/bongos/routes/backup.js",
@@ -6612,7 +6617,7 @@
6612
6617
  {
6613
6618
  "path": "src/bongos/routes/builders.js",
6614
6619
  "mode": "0000644",
6615
- "sha256": "afc0f563da646c6a2d7c3b1adeaeaaf19cf2db48251005f6f003511d2dba65e9"
6620
+ "sha256": "38760c782a379b7a977b9ed4e0af00726d4080ebedf9ea071cefe8d90ceb69b1"
6616
6621
  },
6617
6622
  {
6618
6623
  "path": "src/bongos/routes/github.js",
@@ -6622,7 +6627,7 @@
6622
6627
  {
6623
6628
  "path": "src/bongos/routes/healthz.js",
6624
6629
  "mode": "0000644",
6625
- "sha256": "26f4992c484d2fc5882131c695c4173db950e4c815424fad68385bfa64fa7ab8"
6630
+ "sha256": "5d22031a1acd21a9a244d9f68809450e811fe6a6c98a2f4e2330452515757c37"
6626
6631
  },
6627
6632
  {
6628
6633
  "path": "src/bongos/routes/instance.js",
@@ -6642,7 +6647,7 @@
6642
6647
  {
6643
6648
  "path": "src/bongos/routes/me.js",
6644
6649
  "mode": "0000644",
6645
- "sha256": "1d9e11f3ca4d08033bc116e79ccf66e901ec4e8c48b61d969eeeceface1f9da1"
6650
+ "sha256": "0006bf8ec2430532a72c194bcc7517b4008d00b8fa4a73e9a77bc2c6a10c6d26"
6646
6651
  },
6647
6652
  {
6648
6653
  "path": "src/bongos/routes/modules.js",
@@ -6652,12 +6657,12 @@
6652
6657
  {
6653
6658
  "path": "src/bongos/routes/optimizer.js",
6654
6659
  "mode": "0000644",
6655
- "sha256": "6021253819fcd629e1a14e2027bdf80a2006d9fcb83cf90dbd891d6e542a94ec"
6660
+ "sha256": "22cc9a90dd67c358ec6f23aed86a3053548fbd5f0dfdce09eefc48e215b63a06"
6656
6661
  },
6657
6662
  {
6658
6663
  "path": "src/bongos/routes/public.js",
6659
6664
  "mode": "0000644",
6660
- "sha256": "b4f0f1d10bebd66259b48bb72af121ae9ef94e774c8b840456aa7c2e0aabdf52"
6665
+ "sha256": "0127967b6e345e93cb576702f472534d7985d7f2d8a0d7031a865c5c1d815576"
6661
6666
  },
6662
6667
  {
6663
6668
  "path": "src/bongos/routes/security.js",
@@ -6712,7 +6717,7 @@
6712
6717
  {
6713
6718
  "path": "src/module-api.js",
6714
6719
  "mode": "0000644",
6715
- "sha256": "4cb8b8e962fd1891d9fbbb446048dba025754a73b1a3c313e2e586f14f843b13"
6720
+ "sha256": "71ef74c90f0f0d5d092a1c3fa586f564a4fec242da73acf1d310a7323e960cce"
6716
6721
  },
6717
6722
  {
6718
6723
  "path": "src/module-loader/catalog.js",
@@ -7519,6 +7524,11 @@
7519
7524
  "mode": "0000644",
7520
7525
  "sha256": "4e68c67489daa51167b8d738a36f7c0187016b342e8a57731d14241563262878"
7521
7526
  },
7527
+ {
7528
+ "path": "tests/doc_comment_coverage.mjs",
7529
+ "mode": "0000644",
7530
+ "sha256": "f51d3c747fbb3201ed3388abb1386d0547c3a3be70d7ad7237b9a0f5fdd2347b"
7531
+ },
7522
7532
  {
7523
7533
  "path": "tests/docs_entropy.mjs",
7524
7534
  "mode": "0000644",
package/docs/file-map.md CHANGED
@@ -45,6 +45,7 @@
45
45
  │ │ └── 0001…0120-*.md ← 133 ADR files (a handful of numbers collide — see README.md's disambiguation note; README.md has the live list)
46
46
  │ ├── architecture.md ← §10 detail: live infra, app stack, DB schema, Bongos API (split out V3.R86)
47
47
  │ ├── canonical-permissions.md ← rank ladder + trust boundary reference (read-only mirror; DB enforces)
48
+ │ ├── code-conventions.md ← ADR 0095 (task 1001654): the doc-comment convention — every module a header, every public symbol a line, saying WHAT it does and WHY it exists. The findability layer agents grep. Advisory only; the nudge is scripts/gds/doc-comment-coverage.js
48
49
  │ ├── design/ ← game design docs (V2 visual redesign cluster)
49
50
  │ │ ├── container-exterior.md ← (#641) container building exterior: two-storey stacked-container structure, glass facade, deck, rooftop
50
51
  │ │ └── container-interior.md ← (#645) container building interior: layered industrial shell + aqua wave floor + boutique display; palette reconciled to ADR 0044
@@ -180,6 +181,7 @@
180
181
  │ ├── worktree.js ← (task 1002809) junction-safe worktree add/remove/list — outside-repo worktrees get a node_modules junction via node (no shell quoting), removed BEFORE `git worktree remove`
181
182
  │ ├── learning-capture.js ← file a `learnings` row (DB-backed short-form rule capture, replacing hand-edited recipe files for a single gotcha)
182
183
  │ ├── docs-entropy.js ← ([#…] docs-entropy) markdown-corpus linter: broken internal links + cross-file duplicate-prose detector across every tracked `.md`; `--verbose` / `--file-ideas`
184
+ │ ├── doc-comment-coverage.js ← ADR 0095 / task 1001654: the ADVISORY doc-comment nudge (fitness Check 28) — reports source files under src/·scripts/·modules/ that open with no module header. Never hard-fails: a doc-comment gate that blocks a ship buys filler prose, and making it blocking needs owner sign-off + its own ADR. CRLF-safe (unlike docs-entropy.js); an empty enumeration throws as a scan defect
183
185
  │ ├── copy-inventory.js ← R01 of goal 1000074 (the artist's loop): the COPY REGISTRY — every user-facing string across the hall / status / landing surfaces, each row naming the render path that shows it (html-text · html-attr · js-markup · js-notice · js-text-assign) + a stable id that survives a move. Writes docs/copy-registry.json + docs/copy-inventory.md; `--check` fails on drift. Precision over recall: a JS literal with no render path is not listed
184
186
  │ ├── gen-diagrams.js ← generates the 5 onboarding diagrams (`.mmd`/`.svg`/`.png` + `assertions.json`) from templates in this file — never hand-edit a `.mmd`/assertions.json; runs on deploy
185
187
  │ ├── gen-api-docs.js ← ADR 0109: generates `docs/api/openapi.json` + `docs/api-reference.md` from the live route files via `route-rank-check.js` introspection, so the API reference cannot drift from what the server actually mounts
@@ -1,3 +1,22 @@
1
+ // modules/autonomy/routes/autonomy.js — GET /autonomy/precheck: should an
2
+ // autonomous run start at all? (ADR 0095 backfill).
3
+ //
4
+ // WHY. The nightly routines boot real models that spend real money, so the
5
+ // go/no-go decision must be made BEFORE any model starts — and must itself be
6
+ // free. This is Step 0 of the Multi-Agent System (ADR 0024, System 3): a $0
7
+ // deterministic gate over live signals (is there claimable work, is there budget
8
+ // left, has anything actually changed since the last `go`), exposed over HTTP so
9
+ // an operator can ask the same question the cron asks.
10
+ //
11
+ // TWO SAFETY PROPERTIES worth preserving: it is INERT until `OTB_AUTONOMY_ENABLED=1`
12
+ // (an un-armed instance always answers `go`, so nothing here can start work by
13
+ // accident), and it is Archon-gated, because the answer decides autonomous spend.
14
+ //
15
+ // The decision logic is not here — it lives in the gate CLI
16
+ // `scripts/gds/autonomy-gate.js`, deliberately OUTSIDE the module boundary, since
17
+ // gathering signals means reading ideas/economy/lifecycle, which a modules/ file
18
+ // may not do. This route is a thin wrapper over its `precheck`.
19
+
1
20
  const express = require('express');
2
21
  // The published doorway — requireBuilder / requireRank live on the doorway surface
3
22
  // (a module never reaches into core auth directly; the one-way boundary, ADR 0083).
@@ -1,4 +1,16 @@
1
- // devbox-app/src/config.js — runtime configuration resolution.
1
+ // modules/dev-box/app/src/config.js — where the desktop app decides which server
2
+ // it is talking to, and what version of itself it is (ADR 0095 backfill).
3
+ //
4
+ // WHY. The tray app ships as a built artifact to builders' machines, so it cannot
5
+ // be configured by editing a file next to it — yet it still has to point at a dev
6
+ // or staging API during development, and at the origin a builder actually signed
7
+ // in to afterwards. `apiBase` resolves that in a fixed precedence (explicit env →
8
+ // the base recorded in the session file at sign-in → the branded production
9
+ // default) so a developer override is possible without ever overriding a real
10
+ // builder's session.
11
+ //
12
+ // The production default comes from `./brand`, which is GENERATED from the
13
+ // instance's branding at build time — this file must not hardcode an origin.
2
14
 
3
15
  const brand = require('./brand');
4
16
 
@@ -1,5 +1,26 @@
1
1
  'use strict';
2
2
 
3
+ // modules/economy/achievements.js — what a builder has unlocked, and the reads that
4
+ // display it (ADR 0095 backfill).
5
+ //
6
+ // WHY. Credits pay for work; achievements recognise SHAPES of work that a
7
+ // per-task payout cannot see — a streak sustained, a first ship, a change that
8
+ // removed more than it added. They are the gamification half of the economy.
9
+ //
10
+ // THE ONE STRUCTURAL RULE, and the reason the evaluators look the way they do:
11
+ // an unlock must be ATOMIC with the thing that earned it. Each `evaluateAfter*`
12
+ // therefore takes the CALLER'S transaction `client` as its first argument and runs
13
+ // inside the lifecycle's existing `withTx` — it never opens its own. A separate
14
+ // transaction could commit an unlock for a ship that then rolled back, or drop one
15
+ // for a ship that succeeded. Callers reach these through the `reward` kernel port,
16
+ // never by importing this file (core may not require a module — ADR 0093).
17
+ //
18
+ // The GETTER slice below (`listAchievements`, `getBuilderAchievements`,
19
+ // `getLeaderboardWithAchievements`, `getLockedAchievementsProgress`,
20
+ // `renderProgressText`) is the opposite posture: plain reads for the hall and the
21
+ // public leaderboard, reaching the shared pool through the doorway rather than a
22
+ // caller's tx.
23
+
3
24
  const { SUBTRACTOR_THRESHOLD } = require('./policy');
4
25
 
5
26
  // Achievement evaluation — runs inside existing DB transactions so unlocks
@@ -1,3 +1,19 @@
1
+ // modules/economy/routes/cost.js — POST /cost: record what a session actually spent
2
+ // (ADR 0095 backfill).
3
+ //
4
+ // WHY. Builder pay is cost-plus (ADR 0054: `round(true_cost_usd × 1.20)`), so the
5
+ // cost ledger is not telemetry — it is the input to real money. A session that
6
+ // fails to log its spend under-pays its builder and under-states the project's
7
+ // burn; one that logs twice over-pays. That is why the write is IDEMPOTENT: a
8
+ // retried or resumed ship must not book the same spend again.
9
+ //
10
+ // This route is only the doorway; `../cost.js` owns `logCost` and is the single
11
+ // writer to `cost_log`. Keep it that way — a second writer is how two sources of
12
+ // truth for money start.
13
+ //
14
+ // MODULE BOUNDARY: reaches core only through the published doorway
15
+ // `src/module-api.js`; `../cost` is an intra-module sibling.
16
+
1
17
  const express = require('express');
2
18
  // Carved into modules/economy/ (BV1.R77 / ADR 0093 §1-2): auth gates + request
3
19
  // validators reach through the published doorway; the cost_log writer is an
@@ -1,3 +1,15 @@
1
+ // modules/economy/routes/leaderboard.js — GET /leaderboard: who has earned what
2
+ // (ADR 0095 backfill).
3
+ //
4
+ // WHY. The instance publishes its build openly, and the leaderboard is the
5
+ // human-readable face of the credit ledger — it is read by the PUBLIC status
6
+ // dashboard, so it carries no auth gate. Kept deliberately tiny: the ordering and
7
+ // the join live in `../credits.js` beside the ledger they describe, so the ranking
8
+ // shown here can never drift from the balances it claims to rank.
9
+ //
10
+ // MODULE BOUNDARY: reaches core only through the published doorway
11
+ // `src/module-api.js`; `../credits` is an intra-module sibling.
12
+
1
13
  const express = require('express');
2
14
  // Carved into modules/economy/ (BV1.R77 / ADR 0093 §1-2): the leaderboard query
3
15
  // is an intra-module require — a module never requires a core internal. The
@@ -1,7 +1,28 @@
1
+ // modules/ideas/routes/blockers.js — the queue of things stuck on a human
2
+ // (ADR 0095 backfill).
3
+ //
4
+ // WHY. An AI agent can build almost anything in this repo except the things only
5
+ // the owner can decide or provide — a credential, a purchase, a product call. Those
6
+ // used to stall silently inside a session nobody re-read. A blocker makes that
7
+ // stall a first-class, reviewable record (`/blocker-review`), linked to the tasks
8
+ // it holds up.
9
+ //
10
+ // THE PAYOFF IS IN `/resolve`: `blockers.resolveBlocker` leans on a DB TRIGGER on
11
+ // `blockers.status` to auto-promote every linked `blocked` task back to ready. So
12
+ // answering the owner's question un-sticks the work automatically — nobody has to
13
+ // remember which tasks were waiting on it. Do not reimplement that promotion here;
14
+ // the trigger is the single writer.
15
+ //
16
+ // `GET /blockers` is PUBLIC (the status dashboard renders it — being open about
17
+ // what the project is stuck on is the point). Filing is open to any builder;
18
+ // resolving and linking are Metic+.
19
+ //
20
+ // MODULE BOUNDARY (carved into modules/ideas/ — BV1.R78 / ADR 0093 §1): auth gates
21
+ // + request validators reach through the published doorway `src/module-api.js`;
22
+ // `blockers` is an intra-module sibling. Never a core internal, never another
23
+ // module.
24
+
1
25
  const express = require('express');
2
- // Carved into modules/ideas/ (BV1.R78 / ADR 0093 §1): auth gates + request
3
- // validators reach through the published doorway; `blockers` is an intra-module
4
- // sibling — a module never requires a core internal.
5
26
  const api = require('../../../src/module-api');
6
27
  const blockers = require('../blockers');
7
28
  const { validateOrRespond, LIMITS, parseId } = api;
@@ -1,7 +1,38 @@
1
+ // modules/ideas/routes/inbox.js — where a thought enters the system and becomes
2
+ // work, or doesn't (ADR 0095 backfill).
3
+ //
4
+ // WHY. Ideas arrive mid-session, from Discord, from anyone — and an idea that has
5
+ // to wait for the right moment to be filed is an idea that gets lost. So capture is
6
+ // cheap and open to every rank, and JUDGEMENT happens later at triage
7
+ // (`/idea-triage`: promote → task, discard, merge, defer). This file is the whole
8
+ // of that intake surface. Its factory also REGISTERS the `ideas.capture` kernel
9
+ // port — the codebase's first module→module port, which is how the Discord inbound
10
+ // files an idea without importing this module.
11
+ //
12
+ // The three grades are the spine (see ../../CLAUDE.md for the full rules):
13
+ // quick — a title and a description; turning a thought into a task fast.
14
+ // spark — a quick idea whose AUTHOR marked it as wanting development. That mark
15
+ // is consent, which is why `/:id/spark` is author-gated and `/:id/develop`
16
+ // (picking one up) is open to ANY builder — gating pickup would make the
17
+ // queue unpickable.
18
+ // full — carries the template's five fields and a deterministic completeness
19
+ // score, which feeds credit and the ratification board.
20
+ //
21
+ // TWO THINGS THAT BITE, both proven by tests:
22
+ // - ROUTE ORDER IS LOAD-BEARING. Express matches in declaration order, so every
23
+ // literal path (`/inbox/sparks`, `/inbox/template`, `/inbox/awaiting-nod`) MUST
24
+ // be declared before `GET /inbox/:id` — otherwise `:id` swallows it, `parseId`
25
+ // 400s, and an entire feature disappears with no error that names it.
26
+ // - `/inbox/template` and `/inbox/score` are PURE (no DB, no idea id). They exist
27
+ // so the hall's composer renders and scores from the REAL declaration and the
28
+ // REAL scorer rather than a second copy of the rules in the browser.
29
+ //
30
+ // MODULE BOUNDARY (carved into modules/ideas/ — BV1.R78 / ADR 0093 §1+§5): auth
31
+ // gates + request validators reach through the published doorway
32
+ // `src/module-api.js`; `inbox` is an intra-module sibling. Never a core internal,
33
+ // never another module.
34
+
1
35
  const express = require('express');
2
- // Carved into modules/ideas/ (BV1.R78 / ADR 0093 §1+§5): auth gates + request
3
- // validators reach through the published doorway; `inbox` is an intra-module
4
- // sibling — a module never requires a core internal.
5
36
  const api = require('../../../src/module-api');
6
37
  const inbox = require('../inbox');
7
38
  // Full Idea capture (task 1002919 / BV1.R143): the template validator (R141)
@@ -1,3 +1,25 @@
1
+ // modules/lifecycle/routes/claims.js — locking a task to one builder's session,
2
+ // and letting go of it again (ADR 0095 backfill).
3
+ //
4
+ // WHY. The claim is what makes PARALLEL builder sessions safe. Several agents work
5
+ // this repo at once; without an atomic lock two of them pick the same task, write
6
+ // the same files, and one loses the work at merge. `POST /claims` is that lock, and
7
+ // it must stay atomic — it refuses with a typed reason rather than racing: already
8
+ // claimed, task not ready, touches conflict, or 409 `DEPS_NOT_SHIPPED` when the
9
+ // dependency graph has not opened the task yet.
10
+ //
11
+ // Every claim ends deliberately, never by drifting: `/claims/:id/resolve` carries
12
+ // both the ship path (credits land) and the release path (no credits, task returns
13
+ // to `ready`). `release-on-behalf` is the privileged escape hatch for a claim whose
14
+ // builder is gone — a crashed session must not strand a task forever.
15
+ //
16
+ // The batch routes serve the multi-claim session flow; `batch/validate` is a DRY
17
+ // RUN, so a builder can see which of several tasks would be refused before locking
18
+ // any of them.
19
+ //
20
+ // MODULE BOUNDARY: reaches core only through the published doorway
21
+ // `src/module-api.js`; never a core internal, never another module.
22
+
1
23
  const express = require('express');
2
24
  // Intra-module + the published doorway — a module never requires a core internal.
3
25
  const api = require('../../../src/module-api');
@@ -1,3 +1,25 @@
1
+ // modules/lifecycle/routes/tasks.js — the HTTP surface of the task itself: what
2
+ // work exists, what is claimable, and how a task moves through its statuses
3
+ // (ADR 0095 backfill).
4
+ //
5
+ // WHY. A task is the unit the whole methodology rests on — "every change is backed
6
+ // by a claimed task" only means something if the task's shape and its status
7
+ // transitions are enforced in ONE place. This is that place. It owns the
8
+ // backlog→ready→active→completed→confirmed→shipped walk (`/promote`, `/demote`,
9
+ // `/ship`, `/confirm`, `/abandon`), the DEPENDENCY graph that gates claimability,
10
+ // and the done-when criteria a version's progress rolls up from.
11
+ //
12
+ // Two invariants that are easy to break from here:
13
+ // - `/tasks/claimable` computes parallel-safety against everyone's ACTIVE claims.
14
+ // It is what makes concurrent builder sessions safe; a filter loosened here
15
+ // hands two builders the same work.
16
+ // - Dependencies are load-bearing; the `V##.R##` rank prefix is decorative.
17
+ // Shipping a task auto-promotes its now-unblocked dependents backlog→ready
18
+ // (ADR 0015), so the graph — not a human — decides what opens up next.
19
+ //
20
+ // MODULE BOUNDARY: reaches core only through the published doorway
21
+ // `src/module-api.js`; never a core internal, never another module.
22
+
1
23
  const express = require('express');
2
24
  const fs = require('node:fs');
3
25
  const os = require('node:os');
@@ -1,3 +1,21 @@
1
+ // modules/lifecycle/routes/versions.js — the version as a container of scope, and
2
+ // the question "is it done yet?" (ADR 0095 backfill).
3
+ //
4
+ // WHY. The project ships version-by-version, so a version is not a label — it is
5
+ // the unit scope is committed to and closed against. These routes answer the two
6
+ // things anyone asks about one: what is IN it (`GET /versions`, `POST /versions`)
7
+ // and how far along it is (`/progress`, `/:id/progress` — the roll-up `/status`
8
+ // renders: each done-when criterion → its gating tasks → what remains, ADR 0025).
9
+ //
10
+ // `/versions/close-suggestions` exists because a version that is *actually*
11
+ // finished rarely announces itself — work drifts, criteria get met out of order,
12
+ // and a version can sit open long after its last real task shipped. Delegated to
13
+ // `../version-close.js`, it surfaces the candidates so closing is a decision
14
+ // someone makes rather than one nobody remembers to make.
15
+ //
16
+ // MODULE BOUNDARY: reaches core only through the published doorway
17
+ // `src/module-api.js`; never a core internal, never another module.
18
+
1
19
  const express = require('express');
2
20
  // Intra-module + the published doorway — a module never requires a core internal.
3
21
  const api = require('../../../src/module-api');
@@ -1,3 +1,24 @@
1
+ // modules/memory/routes/learnings.js — GET/POST /learnings: pay for a discovery
2
+ // once (ADR 0095 backfill).
3
+ //
4
+ // WHY. A hard-won diagnosis — the kind that costs half an hour of a session — is
5
+ // worth exactly nothing if the next agent has to rediscover it. Learnings are the
6
+ // short-form half of that cure (the long-form half is a recipe under
7
+ // `docs/recipes/`): one durable, searchable rule per finding, captured via
8
+ // `node scripts/gds/learning-capture.js` and surfaced again through `/recall`.
9
+ //
10
+ // Capture is open to any signed-in builder on purpose. The agent that just paid
11
+ // for the discovery is the one holding it, and a rank gate on writing down what
12
+ // you learned would simply mean it never gets written down.
13
+ //
14
+ // PRIVACY: bodies pass through `stripPrivate()` before persisting, so anything
15
+ // fenced in `<private>…</private>` never enters the store or the recall index
16
+ // (ADR 0095 C4). The fence is fail-closed — an unbalanced marker strips to the end
17
+ // rather than leaking what it was meant to hide.
18
+ //
19
+ // MODULE BOUNDARY: reaches core only through the published doorway
20
+ // `src/module-api.js`; `../learnings` is an intra-module sibling.
21
+
1
22
  const express = require('express');
2
23
  // Carved into modules/memory/ (BV1.R72 / ADR 0091 §2): auth + the shared request
3
24
  // validators (validateOrRespond/LIMITS) via the doorway; the learnings lib is an
package/package-lock.json CHANGED
@@ -1,12 +1,12 @@
1
1
  {
2
2
  "name": "@bongos/core",
3
- "version": "1.19.360",
3
+ "version": "1.19.361",
4
4
  "lockfileVersion": 3,
5
5
  "requires": true,
6
6
  "packages": {
7
7
  "": {
8
8
  "name": "@bongos/core",
9
- "version": "1.19.360",
9
+ "version": "1.19.361",
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.360",
3
+ "version": "1.19.361",
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",
@@ -60,6 +60,7 @@ ${ident}
60
60
  - **\`bongos claim <id>\`** locks a task to your session. If it refuses (already claimed, not ready, dependencies not shipped), pick another.
61
61
  - **\`bongos ship\`** runs the smoke + grade, then merges and deploys; **\`bongos release <id>\`** drops a claim with no credit. Ship or release before you sign off.
62
62
  - **Dependencies gate everything** — a task becomes claimable only once every task it depends on has shipped.
63
+ - **Code you write or touch carries a doc-comment** — each module a header, each public function/class a line or two, saying *what* it does and *why* it exists (never how), in the words someone would search for. Agents find code by searching text, so meaning written into the source is what makes it findable.
63
64
 
64
65
  ## Where things live
65
66
 
@@ -0,0 +1,139 @@
1
+ #!/usr/bin/env node
2
+ 'use strict';
3
+ // scripts/gds/doc-comment-coverage.js — the ADVISORY nudge behind the ADR 0095
4
+ // doc-comment convention (task 1001654).
5
+ //
6
+ // WHY. ADR 0095 ratified one convention: every module and public symbol carries a
7
+ // short doc-comment saying WHAT it does and WHY it exists, because agents navigate
8
+ // this repo by literal text search and meaning written into the source is the only
9
+ // findability layer with no index to keep fresh. A convention nobody measures decays
10
+ // silently, so this reports the one part of it a machine can honestly check — does a
11
+ // source file open with a module header at all.
12
+ //
13
+ // IT WARNS; IT NEVER FAILS. That ceiling is deliberate and is stated in the ADR: a
14
+ // doc-comment gate that blocks a ship trains builders to write a filler line to get
15
+ // past it, which is worse than no comment because it LOOKS like documentation.
16
+ // Promoting this to hard-fail is an owner decision and needs its own ADR.
17
+ //
18
+ // What it cannot check: whether the prose is any good. Presence is a proxy. The
19
+ // convention itself — what to write, and the worked examples — lives in
20
+ // docs/code-conventions.md.
21
+
22
+ const fs = require('fs');
23
+ const path = require('path');
24
+ const { execFileSync } = require('child_process');
25
+
26
+ const ROOT = path.resolve(__dirname, '..', '..');
27
+
28
+ // First-party server/CLI/module source. Deliberately NOT scanned:
29
+ // *.test.js — a test's subject is named by its filename and its cases
30
+ // **/public/** — browser assets; a worthwhile but separate sweep
31
+ // vendor / node_modules / *.min.js / *.generated.js — not ours to annotate
32
+ const INCLUDE_RE = /^(src|scripts|modules)\//;
33
+ const EXCLUDE_RE = /(^|\/)(node_modules|vendor|public|public-docs)\/|\.test\.js$|\.min\.js$|\.generated\.js$/;
34
+
35
+ // A header is prose that appears BEFORE any code. A shebang and a 'use strict'
36
+ // prologue are allowed to precede it — they are not code a reader learns from.
37
+ const SKIP_PROLOGUE_RE = /^(#!.*|['"]use strict['"];?)?$/;
38
+
39
+ // Minimum words for a header to count as saying something. A bare
40
+ // `// config.js — config` restates the filename and teaches nobody anything;
41
+ // the threshold is low on purpose so the check stays about ABSENCE, not style.
42
+ const MIN_WORDS = 12;
43
+
44
+ // Enumerate the files to scan. The glob goes to git as an argv ARRAY, never a
45
+ // shell string — quoting a glob as `'*.js'` is literal on cmd.exe and matches
46
+ // nothing, which is exactly how docs-entropy.js once "passed" on every Windows
47
+ // checkout by scanning zero files (task 1002526).
48
+ //
49
+ // A zero-file result is therefore treated as a SCAN DEFECT and thrown, not
50
+ // returned: this repo tracks hundreds of source files, so an empty enumeration is
51
+ // never real, and a lint that silently proves nothing is worse than no lint.
52
+ function trackedSourceFiles({ run } = {}) {
53
+ const exec = run || ((args) => execFileSync('git', args, { cwd: ROOT, encoding: 'utf8' }));
54
+ const out = exec(['ls-files', '*.js']);
55
+ const all = String(out).split('\n').map((s) => s.trim()).filter(Boolean);
56
+ if (!all.length) {
57
+ throw new Error('doc-comment-coverage scan defect: git ls-files returned no tracked .js files — the enumeration is broken, not the repo.');
58
+ }
59
+ return all.filter((f) => INCLUDE_RE.test(f) && !EXCLUDE_RE.test(f));
60
+ }
61
+
62
+ // Extract the leading comment block of a file, or '' when the first real token is
63
+ // code. Handles both `//` runs and a `/* … */` block, and is CRLF-safe (every line
64
+ // is trimmed before it is classified).
65
+ function leadingComment(text) {
66
+ const lines = text.replace(/\r\n/g, '\n').split('\n');
67
+ let i = 0;
68
+ while (i < lines.length && SKIP_PROLOGUE_RE.test(lines[i].trim())) i++;
69
+
70
+ const block = [];
71
+ if (lines[i] && lines[i].trim().startsWith('/*')) {
72
+ for (; i < lines.length; i++) {
73
+ block.push(lines[i]);
74
+ if (lines[i].includes('*/')) break;
75
+ }
76
+ } else {
77
+ for (; i < lines.length && lines[i].trim().startsWith('//'); i++) block.push(lines[i]);
78
+ }
79
+ return block.join('\n');
80
+ }
81
+
82
+ function wordCount(comment) {
83
+ return comment.replace(/[/*]/g, ' ').trim().split(/\s+/).filter(Boolean).length;
84
+ }
85
+
86
+ // Returns { scanned, missing:[rel], thin:[{file,words}] }.
87
+ function scanHeaders(files = trackedSourceFiles(), read = (rel) => fs.readFileSync(path.join(ROOT, rel), 'utf8')) {
88
+ const missing = [];
89
+ const thin = [];
90
+ for (const f of files) {
91
+ let words;
92
+ try {
93
+ words = wordCount(leadingComment(read(f)));
94
+ } catch (_) {
95
+ continue; // a file listed by git but unreadable here is not this check's problem
96
+ }
97
+ if (words === 0) missing.push(f);
98
+ else if (words < MIN_WORDS) thin.push({ file: f, words });
99
+ }
100
+ return { scanned: files.length, missing, thin };
101
+ }
102
+
103
+ const MAX_LISTED = 15; // keep the warning readable; the count carries the rest
104
+
105
+ // Fitness-check entry point. ALWAYS returns ok:true / hardFail:false — see the
106
+ // header. Findings surface as warnings so a red board still means something real.
107
+ function checkDocCommentCoverage() {
108
+ const { scanned, missing, thin } = scanHeaders();
109
+ const warnings = [];
110
+
111
+ if (missing.length) {
112
+ const shown = missing.slice(0, MAX_LISTED).join(', ');
113
+ const more = missing.length > MAX_LISTED ? ` (+${missing.length - MAX_LISTED} more)` : '';
114
+ warnings.push(`${missing.length} source file(s) open with no module header — give each a line saying what it does and WHY it exists (docs/code-conventions.md, ADR 0095): ${shown}${more}`);
115
+ }
116
+ if (thin.length) {
117
+ const shown = thin.slice(0, MAX_LISTED).map((t) => `${t.file} (${t.words}w)`).join(', ');
118
+ const more = thin.length > MAX_LISTED ? ` (+${thin.length - MAX_LISTED} more)` : '';
119
+ warnings.push(`${thin.length} header(s) under ${MIN_WORDS} words — likely a restatement of the filename rather than the reason the file exists: ${shown}${more}`);
120
+ }
121
+
122
+ const covered = scanned - missing.length;
123
+ return {
124
+ name: 'module header doc-comments (ADR 0095)',
125
+ ok: true,
126
+ hardFail: false, // ADVISORY BY DECISION — promoting this to blocking needs owner sign-off (ADR 0095)
127
+ violations: [],
128
+ warnings,
129
+ note: `${covered}/${scanned} first-party source file(s) under src/·scripts/·modules/ open with a module header saying what they do and why (ADR 0095, task 1001654). ADVISORY — this check never fails the build; tests, browser assets and vendored code are out of scope.`,
130
+ };
131
+ }
132
+
133
+ if (require.main === module) {
134
+ const r = checkDocCommentCoverage();
135
+ process.stdout.write(`${r.note}\n`);
136
+ for (const w of r.warnings) process.stdout.write(`! ${w}\n`);
137
+ }
138
+
139
+ module.exports = { checkDocCommentCoverage, scanHeaders, leadingComment, wordCount, MIN_WORDS };
@@ -1308,6 +1308,11 @@ const CHECKS = [
1308
1308
  require('./routine-schedule.js').checkRoutineCadencesScheduled, // Check 25 — task 1003209: a routine that declares a cadence must have a scheduler firing it AT that period (ADR 0188)
1309
1309
  require('./baseline-staleness.js').checkBaselineFloors, // Check 26 — task 1003210: a shrink-only baseline that never shrinks is unmanaged debt; a floor exempting one must say why
1310
1310
  require('./client-baseurl-guard.js').checkClientBaseUrl, // Check 27 — task 1003263: a page must not combine baseUrl:'' with a generated namespaced call (that pairing 404s every request; three pages shipped it, two of them dead in prod)
1311
+ // Check 28 — task 1001654 / ADR 0095: does each source file open with a module
1312
+ // header saying what it does and why. ADVISORY ONLY (never hardFail) — a
1313
+ // doc-comment gate that blocks a ship buys filler prose, not understanding.
1314
+ // Making it blocking is an owner decision and needs its own ADR.
1315
+ require('./doc-comment-coverage.js').checkDocCommentCoverage,
1311
1316
  ];
1312
1317
 
1313
1318
  function runAll() {
package/scripts/gds/me.js CHANGED
@@ -1,5 +1,14 @@
1
1
  #!/usr/bin/env node
2
- // Show the current builder profile, leaderboard top 10, and active claim.
2
+ // scripts/gds/me.js — "who am I, and what am I holding?" from the terminal
3
+ // (ADR 0095 backfill).
4
+ //
5
+ // WHY. A builder's identity lives on the SERVER (rank is read fresh from the DB on
6
+ // every request — ADR 0016), so a local file can never answer this honestly. This
7
+ // CLI asks the API instead: profile + rank, the leaderboard top 10, and — the part
8
+ // that actually matters mid-session — the ACTIVE CLAIM, so an agent resuming work
9
+ // can confirm what it is allowed to be changing before it changes anything.
10
+ //
11
+ // Read-only. It prints; it never mutates a claim (that is `claim.js`/`ship.js`).
3
12
 
4
13
  const { cliClient, requireSession } = require('./cli-lib');
5
14
 
@@ -1,3 +1,31 @@
1
+ // src/bongos/routes/auth.js — every way a builder or a device proves who they are
2
+ // (ADR 0095 backfill).
3
+ //
4
+ // WHY. This is the front door to the trust boundary (ADR 0016): authority lives in
5
+ // `builders.rank` on the DB and is checked server-side per request, but a request
6
+ // can only be checked once it carries an identity — minting that identity is this
7
+ // file's whole job. Four flows land here, and they exist separately because the
8
+ // caller's shape differs each time:
9
+ //
10
+ // - /auth/device/* the CLI on a builder's machine (GitHub Device Flow, plus
11
+ // the IdP `exchange` variant) — no browser to redirect.
12
+ // - /auth/web/* the builders' hall in a browser (OAuth redirect + cookie).
13
+ // - /auth/discord/* LINKING an existing builder to a Discord id for role sync.
14
+ // It is NOT a way to sign in — Discord sits OUTSIDE the trust
15
+ // boundary (ADR 0032), so it can never mint a session.
16
+ // - /auth/app-pair/* the Dev Box desktop app pairing to its builder (ADR 0045).
17
+ //
18
+ // TWO INVARIANTS worth keeping in view before editing:
19
+ // - `/auth/cli-token/issue` mints a CLI bearer from a browser COOKIE, never from a
20
+ // bearer. A leaked CLI token must not be able to extend its own life.
21
+ // - Logout/revoke are session-destroying and must stay synchronous with rank
22
+ // changes: a demotion calls `revokeAllSessionsForBuilder` so a stale token
23
+ // cannot outlive the authority it was issued under.
24
+ //
25
+ // `escapeHtml` + `renderAuthErrorPage` below render the OAuth failure branches as a
26
+ // small standalone page (no template engine, no runtime deps) so a visitor who
27
+ // fails auth sees something coherent instead of a text/plain stack trace.
28
+
1
29
  const express = require('express');
2
30
  const crypto = require('node:crypto');
3
31
 
@@ -1,3 +1,27 @@
1
+ // src/bongos/routes/builders.js — the roster, the public profile, and the rank
2
+ // mutations (ADR 0095 backfill).
3
+ //
4
+ // WHY. Two very different things share this file because they share a subject —
5
+ // the builder as an IDENTITY, distinct from the work they do:
6
+ //
7
+ // - READS — /builders/roster · /builders/directory · /builders/:id/profile ·
8
+ // per-builder claims, grades and onboarding. Who is here, what have
9
+ // they shipped, how are they doing.
10
+ // - WRITES — PATCH /builders/:id/rank · /status · /budget. These are the
11
+ // highest-authority mutations in the platform: rank IS permission
12
+ // (ADR 0016), so a bug here is a privilege-escalation bug, not a
13
+ // display bug. They stay in CORE db (`setBuilderRank`,
14
+ // `deactivateBuilder`) precisely because identity and authority are
15
+ // kernel concerns and must not be reachable through a module.
16
+ //
17
+ // Promotion is deliberately single-rung (`singleRungPromotionError`) and every
18
+ // change writes an audit row — a builder cannot be walked from newcomer to Archon
19
+ // in one call, and no rank change is untraceable.
20
+ //
21
+ // The work-shaped reads (claims, grade breakdown, shipped counts) belong to the
22
+ // lifecycle module (BV1.R86) and are resolved through the `lifecycle` kernel port
23
+ // (`lc()`), never a direct module require — the core↔module boundary rule.
24
+
1
25
  const path = require('node:path');
2
26
  const { execFile } = require('node:child_process');
3
27
  const express = require('express');
@@ -1,3 +1,13 @@
1
+ // src/bongos/routes/healthz.js — the unauthenticated liveness probe (ADR 0095 backfill).
2
+ //
3
+ // WHY. Every uptime checker, the status dashboard, and the deploy smoke need one
4
+ // endpoint they can hit with no credential to answer "is the process up, and did
5
+ // it come up with auth wired?". It is deliberately the thinnest router in the
6
+ // tree: no DB call, no rank gate, no branding read — anything it touches becomes
7
+ // a new way for the health check itself to go down. `auth_configured` is the one
8
+ // piece of state it reports, because a process that boots WITHOUT working auth
9
+ // (missing IdP creds) is live but useless, and that failure is otherwise silent.
10
+
1
11
  const express = require('express');
2
12
  const auth = require('../auth');
3
13
 
@@ -1,3 +1,24 @@
1
+ // src/bongos/routes/me.js — GET /me, the one call that tells a builder everything
2
+ // about themselves (ADR 0095 backfill).
3
+ //
4
+ // WHY. The hall's first paint needs identity, rank, credits, streak, achievements,
5
+ // onboarding checklist, active claims, grade trend and preferences all at once.
6
+ // Fetching those as eight round-trips made the hall slow and the client
7
+ // order-dependent, so `/me` is a deliberate AGGREGATOR — one request, one shape.
8
+ //
9
+ // THE COST OF THAT CHOICE, and the thing to respect when editing: most of the
10
+ // slices it returns are no longer core. They live in modules carved out per ADR
11
+ // 0093 (`reward` → economy, `builder-settings` → prefs/needs, `onboarding` →
12
+ // checklist, `lifecycle` → claims/estimation/grades), and core may never require a
13
+ // module file directly. Every slice therefore resolves a kernel PORT through
14
+ // `module-seams`, per call. Use `resolveOptional` and degrade gracefully: an
15
+ // instance running without that module must still render `/me`, just without the
16
+ // slice — which is why a missing provider yields the unset/default shape rather
17
+ // than a 500.
18
+ //
19
+ // The per-builder `/me/*-prefs` write routes are NOT here — they moved into
20
+ // modules/builder-settings/ (BV1.R80). This file kept only the read aggregation.
21
+
1
22
  const express = require('express');
2
23
  const auth = require('../auth');
3
24
  const db = require('../db');
@@ -1,3 +1,15 @@
1
+ // src/bongos/routes/optimizer.js — POST /session/optimize: recommend a session's
2
+ // worth of work to one builder (ADR 0095 backfill).
3
+ //
4
+ // WHY. A builder asking "what should I do with the next two hours?" would
5
+ // otherwise read the whole claimable list and guess. This hands the question to
6
+ // `../optimizer.js`, which picks a parallel-safe set against a time budget.
7
+ //
8
+ // READ-ONLY BY DESIGN: it recommends, it never claims. Claiming stays an explicit
9
+ // builder act through the lifecycle (`/builder-claim`), so a recommendation can
10
+ // never quietly lock work to someone. That is why the gate here is only
11
+ // `requireBuilder` — suggesting work to yourself needs no rank.
12
+
1
13
  const express = require('express');
2
14
  const auth = require('../auth');
3
15
  const optimizer = require('../optimizer');
@@ -1,4 +1,22 @@
1
- // Read-only public surface for status.amazonprimea.com. No auth.
1
+ // src/bongos/routes/public.js — the anonymous, read-only surface that powers the
2
+ // status dashboard (ADR 0095 backfill).
3
+ //
4
+ // WHY. The instance publishes its build progress to the world — versions, shipped
5
+ // work, cost, leaderboard, grades, repo health — and that audience has no account.
6
+ // So this router carries NO auth gate at all, which makes it the one place where a
7
+ // projection bug leaks private work state to the public internet.
8
+ //
9
+ // THE RULE THAT KEEPS IT SAFE: nothing here returns a raw DB row. Every response
10
+ // goes through an explicit `projectPublic*` allowlist (`projectPublicTask`,
11
+ // `projectPublicBlocker`, `projectPublicIdea`) that names the fields it will
12
+ // disclose, so a column added to `tasks` later is private by default rather than
13
+ // silently published. A task/idea/blocker is only visible when its own `public`
14
+ // flag is set. Rate-limited (`publicReadRateLimitMiddleware`) and CORS-scoped
15
+ // (`corsPublicGet`) because an unauthenticated surface is also the cheapest one to
16
+ // scrape.
17
+ //
18
+ // The host it serves is NOT hardcoded — the dashboard origin comes from branding
19
+ // (`domains.statusOrigin`); this file must stay brand-neutral (ADR 0062 §3).
2
20
 
3
21
  const express = require('express');
4
22
  const db = require('../db');
package/src/module-api.js CHANGED
@@ -1150,8 +1150,10 @@ const { validateOrRespond, LIMITS, parseId, asyncHandler, corsPublicGet, parsePa
1150
1150
  // landed since 1.19.358 with no explicit bump. run 32877754251. (task 1002620)
1151
1151
  // 1.19.360 — CI auto-patch (publish-on-merge, ADR 0161): carrier for merges
1152
1152
  // landed since 1.19.359 with no explicit bump. run 32881040669. (task 1002620)
1153
+ // 1.19.361 — CI auto-patch (publish-on-merge, ADR 0161): carrier for merges
1154
+ // landed since 1.19.360 with no explicit bump. run 32883230235. (task 1002620)
1153
1155
  // ---------------------------------------------------------------------------
1154
- const CORE_VERSION = '1.19.360'; // CI auto-patch carrier (ADR 0161); see changelog above
1156
+ const CORE_VERSION = '1.19.361'; // CI auto-patch carrier (ADR 0161); see changelog above
1155
1157
 
1156
1158
  // A namespaced logger so a module's log lines are attributable + consistent.
1157
1159
  // Usage: const log = api.logger('dev-box'); log.info('mounted');
@@ -0,0 +1,87 @@
1
+ // tests/doc_comment_coverage.mjs — the ADR 0095 doc-comment nudge detects real
2
+ // gaps, stays advisory, and survives CRLF (task 1001654).
3
+ //
4
+ // Three things are pinned here, each a way this check could quietly become useless:
5
+ // 1. It must stay ADVISORY. The ADR says a doc-comment gate must not block a ship
6
+ // without owner sign-off, because a blocking gate buys filler prose. A future
7
+ // edit that flips hardFail should fail this test and force that decision.
8
+ // 2. It must be CRLF-safe. The sibling scanner docs-entropy.js finds ZERO
9
+ // markdown headings on a Windows checkout because its regex breaks on a
10
+ // trailing \r; this check reads source files the same way and must not
11
+ // repeat it.
12
+ // 3. An empty enumeration is a scan defect, not a clean pass (the task 1002526
13
+ // lesson: a lint that scans nothing reports success forever).
14
+ import assert from 'node:assert/strict';
15
+ import { test } from 'node:test';
16
+ import { createRequire } from 'node:module';
17
+ import { fileURLToPath } from 'node:url';
18
+ import path from 'node:path';
19
+
20
+ const require = createRequire(import.meta.url);
21
+ const ROOT = path.resolve(path.dirname(fileURLToPath(import.meta.url)), '..');
22
+ const { checkDocCommentCoverage, scanHeaders, leadingComment, wordCount, MIN_WORDS } =
23
+ require(path.join(ROOT, 'scripts', 'gds', 'doc-comment-coverage.js'));
24
+
25
+ const FILES = {
26
+ 'src/no-header.js': "const x = require('y');\nmodule.exports = x;\n",
27
+ 'src/code-first.js': 'const a = 1;\n// a comment AFTER code is not a header\n',
28
+ 'src/thin.js': '// config.js — config.\nconst a = 1;\n',
29
+ 'src/good.js': '// src/good.js — does the thing.\n//\n// WHY. It exists because the other thing could not do it safely.\nconst a = 1;\n',
30
+ 'src/prologue.js': "#!/usr/bin/env node\n'use strict';\n// src/prologue.js — a real header stating what this does and why it exists.\nconst a = 1;\n",
31
+ 'src/block.js': '/*\n * src/block.js — a block-comment header that explains what this file does\n * and the reason it exists at all.\n */\nconst a = 1;\n',
32
+ };
33
+ const read = (rel) => FILES[rel];
34
+
35
+ test('a file with no leading comment is reported missing', () => {
36
+ const { missing } = scanHeaders(Object.keys(FILES), read);
37
+ assert.ok(missing.includes('src/no-header.js'));
38
+ });
39
+
40
+ test('a comment that follows code is NOT a header', () => {
41
+ // Otherwise any stray inline note anywhere in the file would count as coverage.
42
+ assert.equal(leadingComment(FILES['src/code-first.js']), '');
43
+ const { missing } = scanHeaders(Object.keys(FILES), read);
44
+ assert.ok(missing.includes('src/code-first.js'));
45
+ });
46
+
47
+ test('a header that only restates the filename is flagged thin, not missing', () => {
48
+ const { thin, missing } = scanHeaders(Object.keys(FILES), read);
49
+ assert.ok(!missing.includes('src/thin.js'));
50
+ assert.ok(thin.some((t) => t.file === 'src/thin.js' && t.words < MIN_WORDS));
51
+ });
52
+
53
+ test('a shebang + use-strict prologue may precede the header', () => {
54
+ const { missing, thin } = scanHeaders(['src/prologue.js'], read);
55
+ assert.deepEqual(missing, [], 'a CLI opening with #! still has a header');
56
+ assert.deepEqual(thin, []);
57
+ });
58
+
59
+ test('block-comment headers count too', () => {
60
+ const { missing, thin } = scanHeaders(['src/block.js'], read);
61
+ assert.deepEqual(missing, []);
62
+ assert.deepEqual(thin, []);
63
+ });
64
+
65
+ test('CRLF files are parsed identically to LF — the docs-entropy trap', () => {
66
+ // docs-entropy.js finds zero headings on a CRLF checkout because `.` does not
67
+ // match \r and `$` (no /m) only matches end-of-input. Reading source the same
68
+ // naive way would make this check report full coverage on Windows regardless of
69
+ // the truth, so LF and CRLF must agree exactly.
70
+ const lf = '// src/x.js — states what this file does and the reason it exists.\nconst a = 1;\n';
71
+ const crlf = lf.replace(/\n/g, '\r\n');
72
+ assert.equal(wordCount(leadingComment(crlf)), wordCount(leadingComment(lf)));
73
+ assert.ok(wordCount(leadingComment(crlf)) >= MIN_WORDS, 'a real CRLF header must not read as thin');
74
+ });
75
+
76
+ test('the check is ADVISORY — it never fails the build (ADR 0095)', () => {
77
+ const r = checkDocCommentCoverage();
78
+ assert.equal(r.hardFail, false, 'promoting this to blocking needs owner sign-off + its own ADR');
79
+ assert.equal(r.ok, true);
80
+ assert.deepEqual(r.violations, [], 'findings surface as warnings, never violations');
81
+ });
82
+
83
+ test('the live repo scan actually reads files (never a silent empty scan)', () => {
84
+ const r = checkDocCommentCoverage();
85
+ const scanned = Number(/^(\d+)\/(\d+)/.exec(r.note)?.[2] ?? 0);
86
+ assert.ok(scanned > 100, `expected hundreds of scanned files, got ${scanned} — an empty scan proves nothing`);
87
+ });