@bongos/core 1.21.176 → 1.21.178

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 (214) hide show
  1. package/.bongos-core.json +186 -496
  2. package/.claude/skills/planning-session/SKILL.md +2 -2
  3. package/clients/bongos-client/README.md +1 -1
  4. package/clients/bongos-client/bongos-client.global.js +0 -6
  5. package/clients/bongos-client/index.cjs +0 -6
  6. package/clients/bongos-client/index.d.ts +0 -6
  7. package/clients/bongos-client/index.mjs +0 -6
  8. package/config/scheduled-routines.json +3 -8
  9. package/docs/adr/0042-builder-self-deploy-ci-auto-merge.md +3 -2
  10. package/docs/adr/0091-bounding-the-kernel-and-db-carve.md +1 -1
  11. package/docs/adr/0093-tranche-2-core-carve-sequence.md +1 -1
  12. package/docs/api/openapi.json +1 -87
  13. package/docs/api-reference.md +1 -8
  14. package/docs/canonical-permissions.md +4 -4
  15. package/docs/copy-inventory.md +25 -39
  16. package/docs/copy-registry.json +73 -200
  17. package/docs/file-map.md +6 -11
  18. package/docs/handoff-template.md +1 -1
  19. package/docs/module-api-changelog.md +4 -0
  20. package/docs/modules-contract.md +2 -2
  21. package/docs/onboarding/diagrams/README.md +3 -7
  22. package/docs/onboarding/diagrams/assertions.json +3 -3
  23. package/docs/page-inventory.json +4 -7
  24. package/docs/page-readings.json +1520 -1569
  25. package/docs/recipes/ops-gotchas.md +3 -1
  26. package/modules/autonomy/db.js +9 -0
  27. package/modules/autonomy/module.json +2 -2
  28. package/modules/autonomy/pollers/daily-checks.js +262 -0
  29. package/modules/autonomy/pollers/routine-timers.js +7 -5
  30. package/modules/autonomy/rank-roles.js +64 -0
  31. package/modules/autonomy/server-schedule.js +37 -0
  32. package/modules/government/catalog.js +10 -7
  33. package/modules/government/decision-board.js +0 -1
  34. package/modules/government/decision-rules.js +0 -1
  35. package/modules/government/docket.js +0 -1
  36. package/modules/government/protected-surfaces.js +1 -1
  37. package/modules/government/protected-surfaces.json +2 -3
  38. package/modules/hall-ui/public/atlas.html +1 -1
  39. package/modules/hall-ui/public/blockers.html +1 -1
  40. package/modules/hall-ui/public/board-room.html +1 -1
  41. package/modules/hall-ui/public/city.html +1 -1
  42. package/modules/hall-ui/public/collab.html +1 -1
  43. package/modules/hall-ui/public/commands.html +1 -1
  44. package/modules/hall-ui/public/copy-desk.html +1 -1
  45. package/modules/hall-ui/public/deploy.html +1 -1
  46. package/modules/hall-ui/public/diagrams.html +1 -1
  47. package/modules/hall-ui/public/drachmae.html +1 -1
  48. package/modules/hall-ui/public/fleet.html +1 -1
  49. package/modules/hall-ui/public/gate.css +4 -26
  50. package/modules/hall-ui/public/gate.html +12 -27
  51. package/modules/hall-ui/public/gate.js +26 -176
  52. package/modules/hall-ui/public/gate.states.json +4 -22
  53. package/modules/hall-ui/public/goal-map.html +1 -1
  54. package/modules/hall-ui/public/goals.html +1 -1
  55. package/modules/hall-ui/public/government.html +1 -1
  56. package/modules/hall-ui/public/idea.html +1 -1
  57. package/modules/hall-ui/public/ideas.html +1 -1
  58. package/modules/hall-ui/public/index.html +1 -1
  59. package/modules/hall-ui/public/modules.html +1 -1
  60. package/modules/hall-ui/public/my-agents.html +1 -1
  61. package/modules/hall-ui/public/primer.html +1 -1
  62. package/modules/hall-ui/public/profile.html +1 -1
  63. package/modules/hall-ui/public/project-settings.html +1 -1
  64. package/modules/hall-ui/public/ranks.html +1 -1
  65. package/modules/hall-ui/public/roadmap.html +1 -1
  66. package/modules/hall-ui/public/roster.html +1 -1
  67. package/modules/hall-ui/public/sessions.html +1 -1
  68. package/modules/hall-ui/public/settings.html +1 -1
  69. package/modules/hall-ui/public/shell.js +1 -1
  70. package/modules/hall-ui/public/street-card.js +5 -9
  71. package/modules/hall-ui/public/street-desk.js +2 -2
  72. package/modules/hall-ui/public/street-inside.js +1 -1
  73. package/modules/hall-ui/public/street.html +1 -1
  74. package/modules/hall-ui/public/studio.html +1 -1
  75. package/modules/hall-ui/public/task.html +1 -1
  76. package/modules/hall-ui/public/thinking.html +1 -1
  77. package/modules/hall-ui/public/tweak-editor.html +1 -1
  78. package/modules/hall-ui/public/watch.html +1 -1
  79. package/modules/hall-ui/public/work.html +1 -1
  80. package/modules/lifecycle/matter-acts.js +0 -6
  81. package/modules/lifecycle/module.json +0 -1
  82. package/package-lock.json +2 -2
  83. package/package.json +1 -1
  84. package/release-notes.json +36 -0
  85. package/scripts/bongos/adopt-preflight.js +3 -3
  86. package/scripts/bongos/api-alias-caller-scan.js +2 -2
  87. package/scripts/bongos/audit-authorship.sh +1 -8
  88. package/scripts/bongos/audit-rank-roles.js +15 -56
  89. package/scripts/bongos/bump-version.js +46 -9
  90. package/scripts/bongos/codemod-rename-scripts-gds.js +0 -1
  91. package/scripts/bongos/diagram-facts.js +1 -1
  92. package/scripts/bongos/do-api.js +1 -1
  93. package/scripts/bongos/doc-cli-guard.js +2 -0
  94. package/scripts/bongos/doc-control-claims.js +2 -2
  95. package/scripts/bongos/fitness-checks-identity.js +4 -15
  96. package/scripts/bongos/gen-diagrams.js +3 -3
  97. package/scripts/bongos/government-walk.js +3 -9
  98. package/scripts/bongos/hard-floor.js +52 -0
  99. package/scripts/bongos/publish-manifest.js +3 -3
  100. package/scripts/bongos/pulse.js +5 -3
  101. package/scripts/bongos/routine-schedule.js +38 -3
  102. package/scripts/bongos/run-unit-tests.js +17 -5
  103. package/scripts/bongos/ship-regen.js +2 -3
  104. package/scripts/gds/{goal-names.js → hard-floor.js} +3 -4
  105. package/scripts/security/dep-audit.js +50 -124
  106. package/src/bongos/module-scope-map.js +2 -2
  107. package/src/bongos/permission-path-check.js +1 -1
  108. package/src/bongos/route-rank-check.js +1 -8
  109. package/src/bongos/routes.js +1 -2
  110. package/src/module-api.js +1 -1
  111. package/src/modules.js +1 -1
  112. package/tests/acclaim_feed_evidence_db.mjs +42 -23
  113. package/tests/adr_0089_scope.mjs +7 -5
  114. package/tests/autonomy_daily_checks.mjs +201 -0
  115. package/tests/bump_version.mjs +52 -18
  116. package/tests/claude_materialize.mjs +12 -12
  117. package/tests/copy_desk_flags_db.mjs +22 -14
  118. package/tests/effective_visibility_predicate.mjs +0 -18
  119. package/tests/gating_review_retired.mjs +1 -1
  120. package/tests/government_act_alone_buildings.mjs +1 -3
  121. package/tests/government_board_membership_db.mjs +35 -22
  122. package/tests/government_protected_surfaces.mjs +29 -13
  123. package/tests/governor_docket.mjs +0 -2
  124. package/tests/governor_matter_acts.mjs +6 -8
  125. package/tests/hall_city.mjs +1 -1
  126. package/tests/hall_matter_card.mjs +2 -2
  127. package/tests/hall_oversight_world.mjs +16 -12
  128. package/tests/hall_street_desk.mjs +4 -3
  129. package/tests/hall_street_inside.mjs +1 -1
  130. package/tests/helpers.mjs +34 -0
  131. package/tests/howto_state.mjs +33 -56
  132. package/tests/howto_state_db.mjs +68 -0
  133. package/tests/idea_credit_streams_db.mjs +11 -3
  134. package/tests/integration_parked_ratchet.mjs +7 -1
  135. package/tests/nav_permission_atoms.mjs +1 -1
  136. package/tests/page_notes_db.mjs +12 -8
  137. package/tests/page_tweak_no_rearm.mjs +50 -0
  138. package/tests/publish_manifest.mjs +64 -0
  139. package/tests/routine_capture_kind.mjs +18 -7
  140. package/tests/routine_schedule.mjs +42 -0
  141. package/tests/search_isolation.mjs +5 -103
  142. package/tests/search_isolation_db.mjs +140 -0
  143. package/tests/session_earnings_mirror_db.mjs +2 -0
  144. package/tests/workflow_exec_paths.mjs +9 -11
  145. package/modules/lifecycle/routes/gate-approvals.js +0 -299
  146. package/scripts/bongos/artifact-staleness.js +0 -267
  147. package/scripts/bongos/ci-grade.js +0 -82
  148. package/scripts/bongos/classify-discipline.js +0 -153
  149. package/scripts/bongos/codemod-rename-src-gds.js +0 -145
  150. package/scripts/bongos/coreB-carve-proof.sh +0 -725
  151. package/scripts/bongos/gate-review.js +0 -330
  152. package/scripts/bongos/goal-names.js +0 -102
  153. package/scripts/bongos/rename-goals.js +0 -109
  154. package/scripts/bongos/seed-adopt-tasks.js +0 -75
  155. package/scripts/bongos/seed-api-contract-goal-tasks.js +0 -141
  156. package/scripts/bongos/seed-board-room-tasks.js +0 -196
  157. package/scripts/bongos/seed-bongos-app-tasks.js +0 -294
  158. package/scripts/bongos/seed-bongos-coreB-tranche1-tasks.js +0 -81
  159. package/scripts/bongos/seed-bongos-coreB-tranche2-tasks.js +0 -100
  160. package/scripts/bongos/seed-bongos-goal-hierarchy-tasks.js +0 -307
  161. package/scripts/bongos/seed-bongos-module-system-tasks.js +0 -346
  162. package/scripts/bongos/seed-bongos-tasks.js +0 -214
  163. package/scripts/bongos/seed-bongos-v1-seo-tasks.js +0 -104
  164. package/scripts/bongos/seed-bv1-community-tasks.js +0 -131
  165. package/scripts/bongos/seed-bv1-privacy-invites-tasks.js +0 -133
  166. package/scripts/bongos/seed-bv1-profile-page-tasks.js +0 -161
  167. package/scripts/bongos/seed-bv1-r01-delta-tasks.js +0 -154
  168. package/scripts/bongos/seed-bv2-tweak-mode-tasks.js +0 -277
  169. package/scripts/bongos/seed-gds-v4-tasks.js +0 -624
  170. package/scripts/bongos/seed-governance-tasks.js +0 -340
  171. package/scripts/bongos/seed-medusa-tasks.js +0 -440
  172. package/scripts/bongos/seed-module-upstreaming.js +0 -219
  173. package/scripts/bongos/seed-productization-tasks.js +0 -235
  174. package/scripts/bongos/seed-project-admin-console-tasks.js +0 -88
  175. package/scripts/bongos/seed-provisioning-tasks.js +0 -83
  176. package/scripts/bongos/seed-strict-versioning-tasks.js +0 -275
  177. package/scripts/bongos/seed-v2-world-tasks.js +0 -283
  178. package/scripts/gds/artifact-staleness.js +0 -7
  179. package/scripts/gds/ci-grade.js +0 -7
  180. package/scripts/gds/classify-discipline.js +0 -7
  181. package/scripts/gds/codemod-rename-src-gds.js +0 -7
  182. package/scripts/gds/coreB-carve-proof.sh +0 -8
  183. package/scripts/gds/gate-review.js +0 -7
  184. package/scripts/gds/rename-goals.js +0 -7
  185. package/scripts/gds/seed-adopt-tasks.js +0 -7
  186. package/scripts/gds/seed-api-contract-goal-tasks.js +0 -7
  187. package/scripts/gds/seed-board-room-tasks.js +0 -6
  188. package/scripts/gds/seed-bongos-app-tasks.js +0 -7
  189. package/scripts/gds/seed-bongos-coreB-tranche1-tasks.js +0 -7
  190. package/scripts/gds/seed-bongos-coreB-tranche2-tasks.js +0 -7
  191. package/scripts/gds/seed-bongos-goal-hierarchy-tasks.js +0 -7
  192. package/scripts/gds/seed-bongos-module-system-tasks.js +0 -7
  193. package/scripts/gds/seed-bongos-tasks.js +0 -7
  194. package/scripts/gds/seed-bongos-v1-seo-tasks.js +0 -6
  195. package/scripts/gds/seed-bv1-community-tasks.js +0 -6
  196. package/scripts/gds/seed-bv1-privacy-invites-tasks.js +0 -6
  197. package/scripts/gds/seed-bv1-profile-page-tasks.js +0 -6
  198. package/scripts/gds/seed-bv1-r01-delta-tasks.js +0 -6
  199. package/scripts/gds/seed-bv2-tweak-mode-tasks.js +0 -6
  200. package/scripts/gds/seed-gds-v4-tasks.js +0 -7
  201. package/scripts/gds/seed-governance-tasks.js +0 -7
  202. package/scripts/gds/seed-medusa-tasks.js +0 -7
  203. package/scripts/gds/seed-module-upstreaming.js +0 -7
  204. package/scripts/gds/seed-productization-tasks.js +0 -7
  205. package/scripts/gds/seed-project-admin-console-tasks.js +0 -6
  206. package/scripts/gds/seed-provisioning-tasks.js +0 -7
  207. package/scripts/gds/seed-strict-versioning-tasks.js +0 -7
  208. package/scripts/gds/seed-v2-world-tasks.js +0 -7
  209. package/scripts/migrate_cost_ledger.sh +0 -178
  210. package/tests/artifact_staleness.mjs +0 -207
  211. package/tests/gate_approvals.mjs +0 -611
  212. package/tests/gate_review.mjs +0 -152
  213. package/tests/goal_names.mjs +0 -120
  214. package/tests/newcomer_generator.mjs +0 -48
@@ -1,41 +1,33 @@
1
1
  #!/usr/bin/env node
2
- // Dependency vulnerability scan — single source of truth for both CI and the
3
- // droplet nightly cron (V3.R37 / #258).
2
+ // Dependency vulnerability scan — single source of truth for the CLI and the
3
+ // server's daily check (V3.R37 / #258; in-process since task 1004878).
4
4
  //
5
- // Two scans are run, in order:
6
- // 1. `npm audit --audit-level=high --json` against the root package.json.
7
- // 2. `python3 -m pip_audit -r art/pipeline/requirements.txt --format json`.
5
+ // One scan: `npm audit --audit-level=high --json` against the root package.json.
6
+ // (A second scan, pip-audit over art/pipeline/requirements.txt, went with the art
7
+ // pipeline it audited — task 1004860 deleted that pipeline, task 1004878 the scan.)
8
8
  //
9
- // The script parses both outputs and emits ONE deterministic JSON report on
10
- // stdout containing every high/critical finding. A short, builder-readable
11
- // summary goes to stderr so a CI log is skim-able.
9
+ // The script parses the output and emits ONE deterministic JSON report on stdout
10
+ // containing every high/critical finding. A short, builder-readable summary goes
11
+ // to stderr so a log is skim-able.
12
12
  //
13
13
  // Two modes:
14
14
  //
15
- // --report-mode=ci (default; used by .github/workflows/dep-audit.yml)
16
- // exit 1 if ANY finding is high or critical.
17
- // exit 0 if both scans clean.
15
+ // --report-mode=ci (default) exit 1 if ANY finding is high or critical,
16
+ // exit 0 if the scan is clean, exit 2 if npm is missing.
18
17
  //
19
- // --report-mode=nightly (used by .claude/scheduled-tasks/dep-audit-nightly)
20
- // ALWAYS exit 0 — a nightly cron must not block the
21
- // droplet's other routines. The skill consumes the
22
- // JSON, posts to the team chat-inbox, and creates a
23
- // vulnerability_report row in the Archon security
24
- // view via src/bongos/security.js.
18
+ // --report-mode=nightly ALWAYS exit 0 — the report is the product.
25
19
  //
26
- // Tool resolution:
27
- // - npm is required (CI image has it; droplet has it).
28
- // - pip-audit is resolved as `python3 -m pip_audit` (works whether installed
29
- // via pip, pipx, or pip --user — module form is the most portable).
30
- // If neither is found, the script reports `tool_missing: pip-audit` and the
31
- // mode decides whether to fail.
20
+ // WHERE IT RUNS ON A SCHEDULE. Nowhere through this CLI: the always-on server runs
21
+ // the same scan daily in-process (modules/autonomy/pollers/daily-checks.js), with an
22
+ // ASYNC spawn — spawnSync here would freeze every request for the whole scan — and
23
+ // hands the output to parseNpmAudit below, so the two cannot disagree about what a
24
+ // finding is.
32
25
  //
33
26
  // Output schema (stdout, always valid JSON, even on failure):
34
27
  // {
35
28
  // "ran_at": "<ISO timestamp>",
36
29
  // "mode": "ci" | "nightly",
37
30
  // "npm": { "ok": true|false, "tool_missing": false|true, "findings": [...] },
38
- // "pip": { "ok": true|false, "tool_missing": false|true, "findings": [...] },
39
31
  // "high_or_critical_count": <int>,
40
32
  // "exit_code": <int>
41
33
  // }
@@ -46,10 +38,8 @@
46
38
 
47
39
  const { spawnSync } = require('node:child_process');
48
40
  const path = require('node:path');
49
- const fs = require('node:fs');
50
41
 
51
42
  const REPO_ROOT = path.resolve(__dirname, '..', '..');
52
- const PIP_REQUIREMENTS = path.join(REPO_ROOT, 'art', 'pipeline', 'requirements.txt');
53
43
 
54
44
  // CLI args ----------------------------------------------------------------
55
45
 
@@ -77,33 +67,47 @@ function printHelp() {
77
67
 
78
68
  // npm audit ---------------------------------------------------------------
79
69
 
80
- // npm audit emits a JSON document. `--audit-level=high` controls the EXIT CODE
81
- // only — the JSON still contains every level. We parse the full set, then we
82
- // filter to high+critical ourselves so the threshold is consistent across both
83
- // scanners.
84
- function runNpmAudit() {
85
- const res = spawnSync('npm', ['audit', '--audit-level=high', '--json'], {
86
- cwd: REPO_ROOT,
70
+ // The npm invocation, shared with the server's async spawn. `--audit-level=high`
71
+ // controls the EXIT CODE only — the JSON still contains every level, and
72
+ // extractNpmFindings filters to high+critical itself.
73
+ const NPM_AUDIT_ARGS = ['audit', '--audit-level=high', '--json'];
74
+
75
+ function runNpmAudit({ cwd = REPO_ROOT } = {}) {
76
+ const res = spawnSync('npm', NPM_AUDIT_ARGS, {
77
+ cwd,
87
78
  encoding: 'utf8',
88
79
  maxBuffer: 32 * 1024 * 1024,
89
80
  });
90
- // npm audit exits 1 when findings >= the threshold, 0 otherwise. Both cases
91
- // still produce JSON on stdout. We don't trust the exit code; we read the
92
- // JSON and decide for ourselves.
93
81
  if (res.error) {
94
82
  return { ok: false, tool_missing: true, findings: [], raw_error: String(res.error.message || res.error) };
95
83
  }
84
+ return parseNpmAudit(res.stdout, res.stderr);
85
+ }
86
+
87
+ // parseNpmAudit — npm audit's raw output → { ok, tool_missing, findings }. Pure.
88
+ // npm audit exits 1 when findings >= the threshold, 0 otherwise, and both cases
89
+ // still produce JSON on stdout — so the exit code is never read; the JSON decides.
90
+ function parseNpmAudit(stdout, stderr = '') {
96
91
  let parsed;
97
92
  try {
98
- parsed = JSON.parse(res.stdout || '{}');
93
+ parsed = JSON.parse(stdout || '{}');
99
94
  } catch (e) {
100
95
  return {
101
96
  ok: false, tool_missing: false, findings: [],
102
- raw_error: `npm audit JSON parse failed: ${e.message}; stderr=${res.stderr.slice(0, 500)}`,
97
+ raw_error: `npm audit JSON parse failed: ${e.message}; stderr=${String(stderr || '').slice(0, 500)}`,
103
98
  };
104
99
  }
105
- const findings = extractNpmFindings(parsed);
106
- return { ok: true, tool_missing: false, findings };
100
+ // npm reports its own failures (no lockfile, registry unreachable) as JSON with
101
+ // an `error` object and no vulnerabilities. That is "could not audit", never a
102
+ // clean bill of health.
103
+ if (parsed && parsed.error && !parsed.vulnerabilities) {
104
+ const e = parsed.error;
105
+ return {
106
+ ok: false, tool_missing: false, findings: [],
107
+ raw_error: `npm audit failed: ${(e && (e.summary || e.code)) || 'unknown error'}`,
108
+ };
109
+ }
110
+ return { ok: true, tool_missing: false, findings: extractNpmFindings(parsed) };
107
111
  }
108
112
 
109
113
  // npm audit v7+ JSON shape: `vulnerabilities` is an object keyed by package
@@ -143,81 +147,6 @@ function extractNpmFindings(parsed) {
143
147
  return out;
144
148
  }
145
149
 
146
- // pip-audit ---------------------------------------------------------------
147
-
148
- function runPipAudit() {
149
- if (!fs.existsSync(PIP_REQUIREMENTS)) {
150
- return {
151
- ok: false, tool_missing: false, findings: [],
152
- raw_error: `requirements file not found: ${PIP_REQUIREMENTS}`,
153
- };
154
- }
155
- const res = spawnSync('python3', [
156
- '-m', 'pip_audit',
157
- '-r', PIP_REQUIREMENTS,
158
- '--format', 'json',
159
- '--vulnerability-service', 'pypi',
160
- ], {
161
- cwd: REPO_ROOT,
162
- encoding: 'utf8',
163
- maxBuffer: 32 * 1024 * 1024,
164
- });
165
- if (res.error) {
166
- return { ok: false, tool_missing: true, findings: [], raw_error: String(res.error.message || res.error) };
167
- }
168
- if (res.status !== 0 && res.status !== 1) {
169
- // pip-audit exits 0 on clean, 1 on findings. Anything else (missing module,
170
- // bad requirements file) is a tool problem.
171
- const stderr = (res.stderr || '').slice(0, 1000);
172
- const missing = /No module named ['"]?pip_audit/.test(stderr);
173
- return {
174
- ok: false, tool_missing: missing, findings: [],
175
- raw_error: `pip-audit exit=${res.status}; stderr=${stderr}`,
176
- };
177
- }
178
- let parsed;
179
- try {
180
- parsed = JSON.parse(res.stdout || '{}');
181
- } catch (e) {
182
- return {
183
- ok: false, tool_missing: false, findings: [],
184
- raw_error: `pip-audit JSON parse failed: ${e.message}; stderr=${res.stderr.slice(0, 500)}`,
185
- };
186
- }
187
- const findings = extractPipFindings(parsed);
188
- return { ok: true, tool_missing: false, findings };
189
- }
190
-
191
- // pip-audit JSON shape: { "dependencies": [ { "name", "version",
192
- // "vulns": [ { "id", "fix_versions": [], "description", "aliases": [] } ] } ] }
193
- // pip-audit does NOT classify severity itself — PyPI/OSV advisories carry it
194
- // in `aliases` (CVE IDs) plus a `description` field. To stay conservative, we
195
- // treat EVERY pip-audit finding as high — by the time PyPI's advisory DB has
196
- // picked it up, the project's posture is "fix or document." This is the same
197
- // stance the GitHub `pip-audit-action` takes by default.
198
- function extractPipFindings(parsed) {
199
- const out = [];
200
- const deps = (parsed && parsed.dependencies) || [];
201
- for (const dep of deps) {
202
- const vulns = Array.isArray(dep.vulns) ? dep.vulns : [];
203
- for (const v of vulns) {
204
- out.push({
205
- ecosystem: 'pypi',
206
- package: dep.name,
207
- version: dep.version,
208
- severity: 'high', // see note above
209
- id: v.id || (Array.isArray(v.aliases) && v.aliases[0]) || null,
210
- summary: (v.description || '').split('\n')[0].slice(0, 240) || `${dep.name} ${dep.version}: ${v.id}`,
211
- fix: Array.isArray(v.fix_versions) && v.fix_versions.length
212
- ? `upgrade to ${v.fix_versions.join(' / ')}`
213
- : 'no fix available',
214
- url: v.id ? `https://osv.dev/vulnerability/${v.id}` : null,
215
- });
216
- }
217
- }
218
- return out;
219
- }
220
-
221
150
  // Main --------------------------------------------------------------------
222
151
 
223
152
  function main() {
@@ -226,14 +155,13 @@ function main() {
226
155
 
227
156
  const ranAt = new Date().toISOString();
228
157
  const npm = runNpmAudit();
229
- const pip = runPipAudit();
230
158
 
231
- const allFindings = [...npm.findings, ...pip.findings];
159
+ const allFindings = npm.findings;
232
160
  const highCount = allFindings.filter(f => f.severity === 'high' || f.severity === 'critical').length;
233
161
 
234
162
  // CI mode: a tool being entirely missing in a CI environment is a build
235
163
  // problem, not a security problem — fail loudly so we don't ship blind.
236
- const toolProblem = (npm.tool_missing || pip.tool_missing) && args.mode === 'ci';
164
+ const toolProblem = npm.tool_missing && args.mode === 'ci';
237
165
 
238
166
  let exitCode = 0;
239
167
  if (toolProblem) exitCode = 2;
@@ -244,7 +172,6 @@ function main() {
244
172
  ran_at: ranAt,
245
173
  mode: args.mode,
246
174
  npm,
247
- pip,
248
175
  high_or_critical_count: highCount,
249
176
  findings: allFindings,
250
177
  exit_code: exitCode,
@@ -253,8 +180,7 @@ function main() {
253
180
  // Human summary to stderr.
254
181
  const lines = [];
255
182
  lines.push(`dep-audit [${args.mode}] @ ${ranAt}`);
256
- lines.push(` npm: ${npm.tool_missing ? 'TOOL MISSING' : (npm.ok ? `${npm.findings.length} high/critical` : `ERROR — ${npm.raw_error}`)}`);
257
- lines.push(` pip-audit: ${pip.tool_missing ? 'TOOL MISSING' : (pip.ok ? `${pip.findings.length} high/critical` : `ERROR — ${pip.raw_error}`)}`);
183
+ lines.push(` npm: ${npm.tool_missing ? 'TOOL MISSING' : (npm.ok ? `${npm.findings.length} high/critical` : `ERROR — ${npm.raw_error}`)}`);
258
184
  if (allFindings.length) {
259
185
  lines.push(` findings:`);
260
186
  for (const f of allFindings) {
@@ -271,6 +197,6 @@ function main() {
271
197
 
272
198
  if (require.main === module) main();
273
199
 
274
- // Exported for unit testing / for the nightly skill if it ever wants to drive
275
- // the scan in-process instead of via spawn.
276
- module.exports = { runNpmAudit, runPipAudit, extractNpmFindings, extractPipFindings };
200
+ // Exported for unit testing and for the server's in-process daily check, which
201
+ // spawns NPM_AUDIT_ARGS itself (asynchronously) and parses with parseNpmAudit.
202
+ module.exports = { runNpmAudit, parseNpmAudit, extractNpmFindings, NPM_AUDIT_ARGS };
@@ -93,7 +93,7 @@ const MODULE_GLOBS = {
93
93
  ],
94
94
  // --- the build state machine + ship/grade/deploy orchestration + schema ---
95
95
  // Carved into modules/lifecycle/ (BV1.R86 / ADR 0093 §1,§3): the task/claim/
96
- // version/goal/done-when/dependency/gate-approvals routes + the db slice, the
96
+ // version/goal/done-when/dependency routes + the db slice, the
97
97
  // task-classifier + hierarchy + done-when libs, github-push + ship-card +
98
98
  // ship-preflight + merge-lock + conflict-resolve + publish-reconciler +
99
99
  // version-close, and the `lifecycle` kernel PORT — all under the
@@ -115,7 +115,7 @@ const MODULE_GLOBS = {
115
115
  'scripts/bongos/ship-regen.js', 'scripts/bongos/ship-resume.js', 'scripts/bongos/ship-session-upload.js',
116
116
  'scripts/bongos/ship-state.js', 'scripts/bongos/ship-surprise.js', 'scripts/bongos/ship-visual.js',
117
117
  'scripts/bongos/merge-mode.js', 'scripts/bongos/confirm.js', 'scripts/bongos/main-audit.js',
118
- 'scripts/bongos/gate-review.js', 'scripts/bongos/push-main.js', 'scripts/bongos/ci-grade.js',
118
+ 'scripts/bongos/hard-floor.js', 'scripts/bongos/push-main.js',
119
119
  'scripts/bongos/merge-lock.js', 'scripts/bongos/version-close.js',
120
120
  'migrations/',
121
121
 
@@ -43,7 +43,7 @@ const RANK_TIER = { xenos: 1, thetes: 2, metic: 3, archon: 4 };
43
43
  // modules/government/protected-surfaces.json — no longer a constant baked into
44
44
  // this file. Re-mapping a floor there is a POLICY edit. It cannot move
45
45
  // enforcement into the app: the three layers above (git pre-push hook, grader
46
- // pre-pass, post-push main audit) plus gate-review.js and the goal-scope check
46
+ // pre-pass, post-push main audit) plus the goal-scope check
47
47
  // read the registry out of band — no DB, no server, no HTTP — and remain the
48
48
  // authority (ADR 0043). Making the request path the enforcer is the exact
49
49
  // regression this task exists to prevent.
@@ -220,7 +220,7 @@ const EXPECTED_RANKS = {
220
220
  'GET /builders/directory': 'any-builder',
221
221
  // (the lifecycle fleet pins — task authoring/promotion/abandonment, override
222
222
  // adjudication, polymorphic dependency edges, goal tier + ask-to-widen,
223
- // criterion confirmation, and gate-approvals — moved to MODULE_EXPECTED_RANKS
223
+ // and criterion confirmation — moved to MODULE_EXPECTED_RANKS
224
224
  // ['lifecycle'] below in BV1.R86. They live in the lifecycle module now
225
225
  // (modules/lifecycle/routes/…), so they're verified only when that module is in
226
226
  // scope, not unconditionally from this core list — the pins follow the routes.)
@@ -500,13 +500,6 @@ const MODULE_EXPECTED_RANKS = {
500
500
  'POST /done-when/:criterionId/uat/recording': 'perm:criterion.review',
501
501
  'POST /done-when/:criterionId/uat': 'perm:criterion.review',
502
502
  'PUT /done-when/:criterionId/backend-only': 'perm:criterion.create',
503
- // gate-surface PR approval from the hall (task 1179, ADR 0058 amendment). ADR 0157
504
- // SPLIT these two: the list route is `gate_approval.read` (metic — the Gate page
505
- // opens to the working rank), while APPROVING still clears the gate-review hard
506
- // floor and stays `gate_approval.decide` (archon, system). The split is the reason
507
- // the page is usable at all — see the atom's note in catalog.js.
508
- 'GET /gate-approvals': 'perm:gate_approval.read',
509
- 'POST /gate-approvals/:pr/approve': 'perm:gate_approval.decide',
510
503
  },
511
504
  // Ratifying a criterion proposal WRITES scope-truth (it creates a done-when
512
505
  // criterion). Metic+ may file the proposal; ADR 0157 also lets Metic ratify it
@@ -31,7 +31,7 @@ const buildMySessionsRouter = require('./routes/my-sessions');
31
31
  const buildMyPasskeysRouter = require('./routes/my-passkeys');
32
32
  const buildBuildersRouter = require('./routes/builders');
33
33
  // The LIFECYCLE routes (tasks · claims · versions · done-when · goals ·
34
- // dependencies · gate-approvals · analytics) carved into modules/lifecycle/
34
+ // dependencies · analytics) carved into modules/lifecycle/
35
35
  // (BV1.R86 / ADR 0093 §1,§3); moduleLoader.mountModuleRoutes (below) mounts them
36
36
  // via the lifecycle module's route factories — no core require. The `/tasks/:id/
37
37
  // publish-branch` body-parser bypass below STAYS (the route carries its own larger
@@ -187,7 +187,6 @@ function buildGdsRouter() {
187
187
  // and discord (with its bug-attachments route) have both graduated
188
188
  // into real modules (modules/<key>/) — they are discovered + mounted by
189
189
  // moduleLoader.mountModuleRoutes below, not here (BV1.R43 / R48 / R47 / R72).
190
- // gate-approvals likewise moved to the lifecycle module (BV1.R86).
191
190
  router.use(buildLlmCacheRouter());
192
191
 
193
192
  // Loader-mounted module routers (ADR 0083 / BV1.R41). Discovers + mounts every
package/src/module-api.js CHANGED
@@ -75,7 +75,7 @@ const { responsibilityFor, ROLE_RESPONSIBILITIES } = require('./role-responsibil
75
75
  // MAJOR (see allowBoxScope below): passes the request through untouched.
76
76
  function deprecatedNoopMiddleware(_req, _res, next) { next(); }
77
77
 
78
- const CORE_VERSION = '1.21.176'; // CI auto-patch carrier (ADR 0161); changelog: docs/module-api-changelog.md
78
+ const CORE_VERSION = '1.21.178'; // CI auto-patch carrier (ADR 0161); changelog: docs/module-api-changelog.md
79
79
 
80
80
  // A namespaced logger so a module's log lines are attributable + consistent.
81
81
  // Usage: const log = api.logger('discord'); log.info('mounted');
package/src/modules.js CHANGED
@@ -44,7 +44,7 @@ const INSTANCE_PATH = path.join(resolveInstanceRoot(), 'config', 'modules.json')
44
44
  //
45
45
  // CORE (always on, never a module): auth, builders, tasks, claims, versions,
46
46
  // done-when, credits/cost, inbox, blockers, leaderboard, analytics,
47
- // audit-log, security, access-requests, sessions, gate-approvals, healthz,
47
+ // audit-log, security, access-requests, sessions, healthz,
48
48
  // public. These mount unconditionally. The optional feature modules AND the
49
49
  // first TWO carved CORE-domain modules have graduated to modules/<key>/ (discord
50
50
  // BV1.R47, art-pipeline BV1.R48, game BV1.R49, memory BV1.R72 —
@@ -16,21 +16,22 @@
16
16
  // - a ship with several cost rows must sum them, not pick one;
17
17
  // - the two LATERALs and the grades join must not multiply the row out.
18
18
  //
19
- // Runs the route's own query text — sliced out of the shipped source, never
20
- // retyped — so a future edit to the route is tested rather than a copy of it.
19
+ // Calls the REAL route handler (GET /tasks/peer-votes/feed from the lifecycle
20
+ // router) against the database, so a future edit to the route is tested rather
21
+ // than a copy of it. It used to slice the query text out of the route's source and
22
+ // re-interpolate it by hand; the route grew a second interpolation (the helper
23
+ // join), the copy stopped resolving, and the test failed on its own scaffolding
24
+ // rather than on the route (task 1004863). Driving the handler cannot drift that way.
21
25
  // Self-skips without a reachable Postgres; in the INTEGRATION set, so the
22
26
  // DB-free unit gate never runs it and .github/workflows/integration.yml does.
23
27
  //
24
28
  // Run: DATABASE_URL=... node tests/acclaim_feed_evidence_db.mjs
25
29
  import { createRequire } from 'node:module';
26
30
  import { strict as assert } from 'node:assert';
27
- import fs from 'node:fs';
28
- import path from 'node:path';
29
- import { fileURLToPath } from 'node:url';
31
+ import { insertTestBuilders } from './helpers.mjs';
30
32
 
31
33
  const require = createRequire(import.meta.url);
32
34
  const { Pool } = require('pg');
33
- const ROOT = path.resolve(path.dirname(fileURLToPath(import.meta.url)), '..');
34
35
 
35
36
  const pool = new Pool({ connectionString: process.env.DATABASE_URL || undefined });
36
37
  try {
@@ -41,18 +42,30 @@ try {
41
42
  process.exit(0);
42
43
  }
43
44
 
44
- // The query under test, taken from the shipped route. gradeQualityWhere is
45
- // interpolated by the route at call time, so interpolate it the same way.
46
- const { gradeQualityWhere } = require('../modules/lifecycle/db-shared.js');
47
- const routeSrc = fs.readFileSync(path.join(ROOT, 'modules/lifecycle/routes/tasks.js'), 'utf8');
48
- const start = routeSrc.indexOf('`SELECT t.id, t.title, t.value_summary, t.version_id, t.status,');
49
- assert.ok(start > 0, 'the peer-votes feed query is still where this test slices it from');
50
- const end = routeSrc.indexOf('LIMIT $2`', start);
51
- assert.ok(end > start, 'the feed query still ends at its LIMIT');
52
- const SQL = routeSrc
53
- .slice(start + 1, end + 'LIMIT $2'.length)
54
- .replace(/\$\{gradeQualityWhere\('g'\)\}/g, gradeQualityWhere('g'));
55
- assert.ok(!SQL.includes('${'), 'every interpolation in the sliced query was resolved');
45
+ // The handler under test: the last stack entry of the shipped route (the first is
46
+ // auth.requireBuilder, which needs a session; the handler reads req.builder, which
47
+ // we supply). The router's pool is the core pool, which reads DATABASE_URL — the
48
+ // same database this file writes its fixtures to.
49
+ const buildTasksRouter = require('../modules/lifecycle/routes/tasks.js');
50
+ const feedLayer = buildTasksRouter().stack
51
+ .find((l) => l.route && l.route.path === '/tasks/peer-votes/feed' && l.route.methods.get);
52
+ assert.ok(feedLayer, 'the lifecycle router still serves GET /tasks/peer-votes/feed');
53
+ const feedHandler = feedLayer.route.stack[feedLayer.route.stack.length - 1].handle;
54
+
55
+ // Call the route as `builder` and return its JSON body; a failure surfaces as a throw.
56
+ async function feed(builder, limit = 100) {
57
+ let body = null;
58
+ let failure = null;
59
+ const res = {
60
+ json(b) { body = b; return res; },
61
+ fail(code, opts) { failure = { code, opts }; return res; },
62
+ status() { return res; },
63
+ };
64
+ await feedHandler({ builder, query: { limit: String(limit) }, params: {}, headers: {}, get: () => undefined }, res);
65
+ if (failure) throw new Error(`the feed route failed: ${JSON.stringify(failure)}`);
66
+ assert.ok(body && Array.isArray(body.tasks), 'the feed answered with a task list');
67
+ return body;
68
+ }
56
69
 
57
70
  const VOTER = 90000801;
58
71
  const SHIPPER = 90000802;
@@ -82,10 +95,13 @@ const check = (name, fn) => {
82
95
  try {
83
96
  await cleanup();
84
97
 
85
- await q("INSERT INTO versions (id, name, status) VALUES ($1, 'acclaim evidence fixture', 'planning')", [VER]);
86
- for (const [id, login] of [[VOTER, 'zz-acc-voter'], [SHIPPER, 'zz-acc-shipper']]) {
87
- await q("INSERT INTO builders (id, github_login, display_name, rank) VALUES ($1, $2, $2, 'metic')", [id, login]);
88
- }
98
+ // 'building', not 'planning': versions_one_planning_idx allows ONE planning row and
99
+ // the seeds own it — the collision that parked this test (task 1004042).
100
+ await q("INSERT INTO versions (id, name, status) VALUES ($1, 'acclaim evidence fixture', 'building')", [VER]);
101
+ await insertTestBuilders(pool, [
102
+ { id: VOTER, login: 'zz-acc-voter', rank: 'metic' },
103
+ { id: SHIPPER, login: 'zz-acc-shipper', rank: 'metic' },
104
+ ], { onConflict: '' });
89
105
  for (const [id, title] of [
90
106
  [T.GOOD, '__acc__ graded and costed'],
91
107
  [T.OUTAGE, '__acc__ grader outage'],
@@ -123,7 +139,10 @@ try {
123
139
  await cost(T.FREE, '0');
124
140
  // T.OUTAGE and T.UNGRADED get no cost row at all.
125
141
 
126
- const rows = await q(SQL, [VOTER, 100]);
142
+ // A metic caller: cost reaches only the ranks that can vote, so this is the view
143
+ // that carries every figure under test.
144
+ const body = await feed({ id: VOTER, rank: 'metic' });
145
+ const rows = body.tasks;
127
146
  const byId = new Map(rows.map((r) => [String(r.id), r]));
128
147
  const got = (id) => {
129
148
  const row = byId.get(String(id));
@@ -28,9 +28,11 @@ const adr = read('docs/adr/0089-modular-multi-model-adversarial-grader.md');
28
28
  const section = adr.slice(adr.indexOf('### 8. Scope: what the engine does NOT own'), adr.indexOf('## Consequences'))
29
29
  .replaceAll('scripts/gds/', 'scripts/bongos/'); // rename-keep
30
30
 
31
+ // §8 also names scripts/bongos/gate-review.js. Task 1004859 deleted it (the CI check it
32
+ // drove never ran), so there is nothing left to hold to the boundary; the ADR keeps the
33
+ // name as history.
31
34
  const SURFACES = [
32
35
  'scripts/bongos/session-eval.js',
33
- 'scripts/bongos/gate-review.js',
34
36
  'scripts/bongos/main-audit.js',
35
37
  'scripts/bongos/architect-audit.js',
36
38
  ];
@@ -40,7 +42,7 @@ test('ADR 0089 carries the §8 scope section, before Consequences', () => {
40
42
  assert.ok(section.length > 200, 'the section sits between §7 and Consequences');
41
43
  });
42
44
 
43
- test('§8 names each of the four surfaces by path, and each path exists', () => {
45
+ test('§8 names each of the remaining surfaces by path, and each path exists', () => {
44
46
  for (const p of SURFACES) {
45
47
  assert.ok(section.includes(p), `§8 names ${p}`);
46
48
  assert.ok(fs.existsSync(new URL(`../${p}`, import.meta.url)), `${p} exists`);
@@ -56,14 +58,14 @@ test('the Architect uses exactly the three grader helpers §8 names — nothing
56
58
  for (const name of used) assert.ok(section.includes(name), `§8 names ${name}`);
57
59
  });
58
60
 
59
- test('session-eval, gate-review and main-audit import no grading module', () => {
61
+ test('session-eval and main-audit import no grading module', () => {
60
62
  for (const p of SURFACES.filter((s) => !s.endsWith('architect-audit.js'))) {
61
63
  assert.doesNotMatch(read(p), /require\([^)]*modules\/grading/, `${p} must not require grading code`);
62
64
  }
63
65
  });
64
66
 
65
- test('main-audit and gate-review share only the rank-permission checker §8 names', () => {
66
- for (const p of ['scripts/bongos/main-audit.js', 'scripts/bongos/gate-review.js']) {
67
+ test('main-audit reads the rank-permission checker §8 names', () => {
68
+ for (const p of ['scripts/bongos/main-audit.js']) {
67
69
  assert.match(read(p), /permission-path-check/, `${p} reads the checker`);
68
70
  }
69
71
  assert.ok(section.includes('src/bongos/permission-path-check.js'), '§8 names the shared checker');