@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.
- package/.bongos-core.json +72 -42
- package/clients/bongos-client/README.md +1 -1
- package/clients/bongos-client/bongos-client.global.js +8 -0
- package/clients/bongos-client/index.cjs +8 -0
- package/clients/bongos-client/index.d.ts +11 -0
- package/clients/bongos-client/index.mjs +8 -0
- package/docs/api/openapi.json +170 -3
- package/docs/api-reference.md +9 -1
- package/docs/file-map.md +2 -0
- package/modules/autonomy/routes/autonomy.js +19 -0
- package/modules/dev-box/app/src/config.js +13 -1
- package/modules/dev-box/app/src/vendor/bongos-client.cjs +8 -0
- package/modules/economy/achievements.js +21 -0
- package/modules/economy/routes/cost.js +16 -0
- package/modules/economy/routes/leaderboard.js +12 -0
- package/modules/ideas/routes/blockers.js +24 -3
- package/modules/ideas/routes/inbox.js +34 -3
- package/modules/lifecycle/closeness-weights.json +64 -0
- package/modules/lifecycle/closeness.js +363 -0
- package/modules/lifecycle/module.json +1 -0
- package/modules/lifecycle/routes/claims.js +22 -0
- package/modules/lifecycle/routes/closeness.js +118 -0
- package/modules/lifecycle/routes/tasks.js +22 -0
- package/modules/lifecycle/routes/versions.js +18 -0
- package/modules/memory/routes/learnings.js +21 -0
- package/modules/platform-identity/platform-identity.js +28 -0
- package/modules/platform-identity/routes/sso.js +4 -0
- package/package-lock.json +2 -2
- package/package.json +1 -1
- package/scripts/gds/claude-md-template.js +1 -0
- package/scripts/gds/doc-comment-coverage.js +139 -0
- package/scripts/gds/fitness.js +5 -0
- package/scripts/gds/me.js +10 -1
- package/src/bongos/routes/auth.js +28 -0
- package/src/bongos/routes/builders.js +24 -0
- package/src/bongos/routes/healthz.js +10 -0
- package/src/bongos/routes/me.js +21 -0
- package/src/bongos/routes/optimizer.js +12 -0
- package/src/bongos/routes/public.js +19 -1
- package/src/module-api.js +5 -1
- package/tests/closeness.mjs +324 -0
- package/tests/doc_comment_coverage.mjs +87 -0
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@bongos/core",
|
|
3
|
-
"version": "1.19.
|
|
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 };
|
package/scripts/gds/fitness.js
CHANGED
|
@@ -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
|
-
//
|
|
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
|
|
package/src/bongos/routes/me.js
CHANGED
|
@@ -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
|
-
//
|
|
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
|
@@ -1148,8 +1148,12 @@ const { validateOrRespond, LIMITS, parseId, asyncHandler, corsPublicGet, parsePa
|
|
|
1148
1148
|
// landed since 1.19.357 with no explicit bump. run 32872551850. (task 1002620)
|
|
1149
1149
|
// 1.19.359 — CI auto-patch (publish-on-merge, ADR 0161): carrier for merges
|
|
1150
1150
|
// landed since 1.19.358 with no explicit bump. run 32877754251. (task 1002620)
|
|
1151
|
+
// 1.19.360 — CI auto-patch (publish-on-merge, ADR 0161): carrier for merges
|
|
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)
|
|
1151
1155
|
// ---------------------------------------------------------------------------
|
|
1152
|
-
const CORE_VERSION = '1.19.
|
|
1156
|
+
const CORE_VERSION = '1.19.361'; // CI auto-patch carrier (ADR 0161); see changelog above
|
|
1153
1157
|
|
|
1154
1158
|
// A namespaced logger so a module's log lines are attributable + consistent.
|
|
1155
1159
|
// Usage: const log = api.logger('dev-box'); log.info('mounted');
|