@bongos/core 1.19.359 → 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.
Files changed (42) hide show
  1. package/.bongos-core.json +72 -42
  2. package/clients/bongos-client/README.md +1 -1
  3. package/clients/bongos-client/bongos-client.global.js +8 -0
  4. package/clients/bongos-client/index.cjs +8 -0
  5. package/clients/bongos-client/index.d.ts +11 -0
  6. package/clients/bongos-client/index.mjs +8 -0
  7. package/docs/api/openapi.json +170 -3
  8. package/docs/api-reference.md +9 -1
  9. package/docs/file-map.md +2 -0
  10. package/modules/autonomy/routes/autonomy.js +19 -0
  11. package/modules/dev-box/app/src/config.js +13 -1
  12. package/modules/dev-box/app/src/vendor/bongos-client.cjs +8 -0
  13. package/modules/economy/achievements.js +21 -0
  14. package/modules/economy/routes/cost.js +16 -0
  15. package/modules/economy/routes/leaderboard.js +12 -0
  16. package/modules/ideas/routes/blockers.js +24 -3
  17. package/modules/ideas/routes/inbox.js +34 -3
  18. package/modules/lifecycle/closeness-weights.json +64 -0
  19. package/modules/lifecycle/closeness.js +363 -0
  20. package/modules/lifecycle/module.json +1 -0
  21. package/modules/lifecycle/routes/claims.js +22 -0
  22. package/modules/lifecycle/routes/closeness.js +118 -0
  23. package/modules/lifecycle/routes/tasks.js +22 -0
  24. package/modules/lifecycle/routes/versions.js +18 -0
  25. package/modules/memory/routes/learnings.js +21 -0
  26. package/modules/platform-identity/platform-identity.js +28 -0
  27. package/modules/platform-identity/routes/sso.js +4 -0
  28. package/package-lock.json +2 -2
  29. package/package.json +1 -1
  30. package/scripts/gds/claude-md-template.js +1 -0
  31. package/scripts/gds/doc-comment-coverage.js +139 -0
  32. package/scripts/gds/fitness.js +5 -0
  33. package/scripts/gds/me.js +10 -1
  34. package/src/bongos/routes/auth.js +28 -0
  35. package/src/bongos/routes/builders.js +24 -0
  36. package/src/bongos/routes/healthz.js +10 -0
  37. package/src/bongos/routes/me.js +21 -0
  38. package/src/bongos/routes/optimizer.js +12 -0
  39. package/src/bongos/routes/public.js +19 -1
  40. package/src/module-api.js +5 -1
  41. package/tests/closeness.mjs +324 -0
  42. package/tests/doc_comment_coverage.mjs +87 -0
@@ -2,7 +2,7 @@
2
2
 
3
3
  # Bongos API reference
4
4
 
5
- > **Generated from the live route files** — the route file is authoritative. 312 endpoints across 51 route files.
5
+ > **Generated from the live route files** — the route file is authoritative. 315 endpoints across 52 route files.
6
6
  > Machine-readable spec: [`docs/api/openapi.json`](api/openapi.json) (OpenAPI 3.1). Rendered docs site: **`/docs`** (e.g. `cloudbongos.com/docs`).
7
7
 
8
8
  Base path: `/api/bongos`. Ranks (enforced server-side, [ADR 0016](adr/<redacted>.md)): `public` < `any-builder` < `metic+archon` < `archon`.
@@ -143,6 +143,14 @@ Base path: `/api/bongos`. Ranks (enforced server-side, [ADR 0016](adr/<redacted>
143
143
  | POST | `/api/bongos/claims/batch/release` | `any-builder` | `claim_ids`, `force` | Bulk release (task 1758) — the batch twin of resolve(outcome='abandoned'). |
144
144
  | POST | `/api/bongos/claims/batch/validate` | `any-builder` | `task_ids` | Dry-run validate (task 1758) — read-only twin of claimTasksBatch's pre-flight checks. |
145
145
 
146
+ ## `closeness` (3)
147
+
148
+ | Method | Path | Rank | Body | Description |
149
+ |---|---|---|---|---|
150
+ | GET | `/api/bongos/closeness/for-me` | `any-builder` | — | , and OWN-SCOPED — your own graph, never someone else's. |
151
+ | GET | `/api/bongos/closeness/weights` | `any-builder` | — | the MODEL is not private, and publishing it is the whole point of the weights being data. |
152
+ | GET | `/api/bongos/closeness/with/:builderId` | `any-builder` | — | One pair — "why am I close to THEM", asked directly rather than by paging the graph. |
153
+
146
154
  ## `copy-desk` (4)
147
155
 
148
156
  | Method | Path | Rank | Body | Description |
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
 
@@ -251,6 +251,14 @@ function createClient(opts = {}) {
251
251
  // POST /claims/batch/validate — rank: any-builder — POST /claims/batch/validate
252
252
  postClaimsBatchValidate: (args) => request("POST", "/claims/batch/validate", { hasBody: true }, args),
253
253
  },
254
+ "closeness": {
255
+ // GET /closeness/for-me — rank: any-builder — GET /closeness/for-me
256
+ getClosenessForMe: (args) => request("GET", "/closeness/for-me", { hasBody: false }, args),
257
+ // GET /closeness/weights — rank: any-builder — GET /closeness/weights
258
+ getClosenessWeights: (args) => request("GET", "/closeness/weights", { hasBody: false }, args),
259
+ // GET /closeness/with/{builderId} — rank: any-builder — GET /closeness/with/:builderId
260
+ getClosenessWithBuilderId: (args) => request("GET", "/closeness/with/{builderId}", { hasBody: false }, args),
261
+ },
254
262
  "copyDesk": {
255
263
  // POST /copy-desk/flags — rank: any-builder — POST /copy-desk/flags
256
264
  postCopyDeskFlags: (args) => request("POST", "/copy-desk/flags", { hasBody: true }, args),
@@ -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)
@@ -0,0 +1,64 @@
1
+ {
2
+ "_comment": [
3
+ "The closeness scoring model, as DATA (task 1003174 / C04, goal 1000075; criterion",
4
+ "collab-closeness-consented, ADR 0187 §2). The owner's decision was explicit that the",
5
+ "weights are data rather than constants buried in a function, so they can be reviewed",
6
+ "and changed without a code-archaeology dig. This file IS that review surface, and it",
7
+ "is served verbatim by GET /closeness/weights so the number can always be audited",
8
+ "against the model that produced it.",
9
+ "",
10
+ "HOW A SCORE IS BUILT. Each signal counts real events between two builders. That count",
11
+ "is divided by the signal's `saturation` and clamped to 1, so a signal is 'full' once",
12
+ "it has happened `saturation` times and no single runaway count can dominate. The",
13
+ "closeness score is the weighted sum of those clamped values, expressed 0..1.",
14
+ "",
15
+ "WEIGHTS ARE RENORMALISED OVER AVAILABLE SIGNALS. A signal with `available: false`",
16
+ "cannot be measured yet, and its weight is redistributed across the rest rather than",
17
+ "counted as zero. Counting it as zero would depress every pair's score identically and,",
18
+ "worse, the explanation would read as 'you never talk' when the truth is 'nothing here",
19
+ "can see whether you talk'. The explanation names every excluded signal for this reason.",
20
+ "",
21
+ "CHANGING A WEIGHT is a product decision about how the system describes real people to",
22
+ "each other. Weights should sum to 1 across ALL signals (the test asserts it); the",
23
+ "renormalisation handles the available subset at read time."
24
+ ],
25
+
26
+ "version": 1,
27
+ "scale": "0..1, rendered as 0..100 on any surface that shows a number",
28
+
29
+ "signals": [
30
+ {
31
+ "key": "co_claim",
32
+ "label": "Worked the same goal at the same time",
33
+ "description": "You each held an active claim on a task in the same goal, and those claims overlapped in time. This is the live version of working alongside someone: you were both holding work in the same place at once.",
34
+ "weight": 0.3,
35
+ "saturation": 5,
36
+ "available": true
37
+ },
38
+ {
39
+ "key": "co_build_session",
40
+ "label": "Sat down to it in the same stretch",
41
+ "description": "You logged work sessions on tasks in the same goal that overlapped in time. A claim can sit open for days; a session is the hours actually spent, so this is the closer measure of being present together. The owner's decision (ADR 0187 §3) is that the unit of collaboration is the GOAL, never a shared claim — nobody co-runs a claim, and this signal does not pretend they do.",
42
+ "weight": 0.3,
43
+ "saturation": 5,
44
+ "available": true
45
+ },
46
+ {
47
+ "key": "shipped_together_goal",
48
+ "label": "Shipped into the same goal",
49
+ "description": "Distinct goals where you have each shipped at least one task. Unlike the two above, this ignores timing entirely — it is the durable record that you both built the same thing, even months apart.",
50
+ "weight": 0.25,
51
+ "saturation": 3,
52
+ "available": true
53
+ },
54
+ {
55
+ "key": "comms_mention",
56
+ "label": "Talked to or mentioned each other",
57
+ "description": "Messages and mentions between you. NOT MEASURABLE YET: this needs the Discord-into-the-hall mirror, which is C06 of this goal (task 1003176) and is still an open decision. Until it lands this signal is unavailable and its weight is redistributed across the others — it is never silently counted as zero, because 'we cannot see your conversations' and 'you never speak' are different facts and only one of them is true.",
58
+ "weight": 0.15,
59
+ "saturation": 20,
60
+ "available": false,
61
+ "blocked_by": "task 1003176 (C06 — Discord chat mirrors INTO the hall)"
62
+ }
63
+ ]
64
+ }