@bongos/core 1.19.360 → 1.19.362

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.
@@ -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
@@ -25,14 +25,21 @@
25
25
  // gated server-side on the `government.manage` atom (archon floor). On 401 →
26
26
  // web sign-in.
27
27
  //
28
- // SINCE R15 (goal 1000069, ADR 0175) this page carries the three-section IA —
29
- // Constitution · Board Room · Ranks & Permissions — because Government is the
30
- // page that answers "how is this project governed", and administration is only
31
- // the third part of that answer. Tabs are OTBKit.makeTabs with hash deep links
32
- // (#constitution / #board-room / #ranks). The first two sections are FRAMES
33
- // until their build-out lands: R18 fills Constitution with the live values +
34
- // change history, R19 adds the charter library, R16/R17 fill the Board Room
35
- // with open items and the vote. Until then each frame states what belongs there
28
+ // SINCE R15 (goal 1000069, ADR 0175) this page carries a three-section IA,
29
+ // because Government is the page that answers "how is this project governed"
30
+ // and administration is only one part of that answer. Task 1003269 reordered
31
+ // the tabs to Permissions · Board Room · Constitution (the administering Archon
32
+ // works from the first of those, and it used to sit last) and shortened the
33
+ // "Ranks & Permissions" label to "Permissions". Tabs are OTBKit.makeTabs with
34
+ // hash deep links, and the HASH IDS DID NOT CHANGE with that reorder
35
+ // (#constitution / #board-room / #ranks) — they are linked from Discord and
36
+ // docs.
37
+ //
38
+ // The Constitution and Board Room sections began as FRAMES (named here by
39
+ // section, not by position, so a reorder like 1003269's cannot make this
40
+ // comment lie): R18 filled Constitution with the live values + change history,
41
+ // R19 added the charter library, and R16/R17 filled the Board Room with open
42
+ // items and the vote. Until a frame is filled it states what belongs there
36
43
  // and shows an honest kit empty state — never a fake or a hardcoded value that
37
44
  // could drift from the real config.
38
45
 
@@ -412,19 +419,29 @@
412
419
 
413
420
  // ---- the three-section IA (R15) -------------------------------------------
414
421
 
415
- // Tab order is the spec's — Constitution · Board Room · Ranks & Permissions.
416
- // A non-archon lands on the Constitution (the page's IA claim: this is the
417
- // page that answers "how is this project governed"); the Archon still lands
418
- // on Ranks, their working surface.
422
+ // Tab order puts Permissions FIRST (task 1003269, owner request): it is the
423
+ // surface an administering Archon actually works from, and it sat last. The
424
+ // label is "Permissions" — the words the owner uses for it — not the compound
425
+ // "Ranks & Permissions"; ranks are still what the panel lists, but permissions
426
+ // are the atom (ADR 0151) and the shorter label is the clearer one.
427
+ //
428
+ // THE HASH IDS ARE LOAD-BEARING AND MUST NOT CHANGE: the Discord board
429
+ // announcement deep-links ?item=<id>#board-room (board-broadcast.js, and the
430
+ // reader in loadBoard below), and #constitution/#ranks are linked from docs.
431
+ // Reorder and relabel freely; the ids are the contract.
432
+ //
433
+ // A non-archon still lands on the Constitution (the page's IA claim: this is
434
+ // the page that answers "how is this project governed"); the Archon still
435
+ // lands on their working surface, which is now also the first tab.
419
436
  function wireTabs({ isArchon }) {
420
437
  window.OTBKit.makeTabs({
421
438
  mount: document.getElementById('gov-tabs'),
422
439
  hash: true,
423
440
  initial: isArchon ? 'ranks' : 'constitution',
424
441
  tabs: [
425
- { id: 'constitution', label: 'Constitution', panel: 'gov-panel-constitution' },
442
+ { id: 'ranks', label: 'Permissions', panel: 'gov-panel-ranks' },
426
443
  { id: 'board-room', label: 'Board Room', panel: 'gov-panel-board' },
427
- { id: 'ranks', label: 'Ranks & Permissions', panel: 'gov-panel-ranks' },
444
+ { id: 'constitution', label: 'Constitution', panel: 'gov-panel-constitution' },
428
445
  ],
429
446
  });
430
447
  }
@@ -843,15 +860,16 @@
843
860
  }
844
861
 
845
862
  // Since R16 the page shell admits page.view.government (metic+), and only the
846
- // RANKS tab is the archon admin surface — so the seal narrowed from the whole
847
- // page to that one panel. Cosmetic defence-in-depth as ever: every
863
+ // Permissions tab (panel id gov-panel-ranks) is the archon admin surface — so
864
+ // the seal narrowed from the whole page to that one panel. Cosmetic
865
+ // defence-in-depth as ever: every
848
866
  // /government/* CRUD endpoint still gates on government.manage server-side,
849
867
  // and the board read carries its own atom.
850
868
  function sealRanksPanel() {
851
869
  const panel = document.getElementById('gov-panel-ranks');
852
870
  const body = document.getElementById('gov-body');
853
871
  // Replace the admin surface with the sealed note, inside its own tab only.
854
- panel.innerHTML = '<p class="gov-sealed">Ranks & Permissions is the Archon administration surface. The Constitution and the Board Room are yours to read.</p>';
872
+ panel.innerHTML = '<p class="gov-sealed">Permissions is the Archon administration surface. The Constitution and the Board Room are yours to read.</p>';
855
873
  body.hidden = false;
856
874
  }
857
875
 
@@ -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.362",
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.362",
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.362",
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');