@bongos/core 1.19.337 → 1.19.339

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.
@@ -62,6 +62,8 @@ const JOIN_REQUEST_RESPONSE_ACTIONS = ['approve', 'deny']; // an owner/manager/A
62
62
  // membership half of the wall; the protected-module rank check is enforced per-route
63
63
  // (scope is never self-granted).
64
64
  const { authorizeCreateJoinRequest, authorizeGoalTaskCreate, authorizeInvitationAccept, authorizeInvitationResponse, authorizeJoinRequestResponse, authorizeMembershipKindChange, authorizeOwnershipTransfer, authorizeWithdrawJoinRequest, decorateGoalScope, isMemberOrArchon, protectedScopeAdmissionBlock } = require('./goal-route-authz.js');
65
+ // task 1003208: structured logging (pino via the doorway) — was console.*.
66
+ const log = api.logger('lifecycle');
65
67
  module.exports = function buildGoalsRouter() {
66
68
  const router = express.Router();
67
69
 
@@ -118,7 +120,7 @@ module.exports = function buildGoalsRouter() {
118
120
  }
119
121
  return res.status(201).json({ ok: true, goal, succeeds_goal_id: succeedsId });
120
122
  } catch (err) {
121
- console.error('[gds] POST /goals', err);
123
+ log.error('[gds] POST /goals', err);
122
124
  return res.fail('goal_create_failed', { status: 500, message: 'internal error' });
123
125
  }
124
126
  });
@@ -134,7 +136,7 @@ module.exports = function buildGoalsRouter() {
134
136
  const goals = await db.listGoals({ versionId: version || null, status: status || null });
135
137
  return res.json({ goals: goals.map(decorateGoalScope) });
136
138
  } catch (err) {
137
- console.error('[gds] GET /goals', err);
139
+ log.error('[gds] GET /goals', err);
138
140
  return res.fail('list_failed', { status: 500, message: 'internal error' });
139
141
  }
140
142
  });
@@ -165,7 +167,7 @@ module.exports = function buildGoalsRouter() {
165
167
  if (err && err.code === 'SET_MISMATCH') {
166
168
  return res.fail('set_mismatch', { status: 400, message: err.message });
167
169
  }
168
- console.error('[gds] POST /goals/reorder', err);
170
+ log.error('[gds] POST /goals/reorder', err);
169
171
  return res.fail('reorder_failed', { status: 500, message: 'internal error' });
170
172
  }
171
173
  });
@@ -190,7 +192,7 @@ module.exports = function buildGoalsRouter() {
190
192
  const inbox = await db.listInboxFor(req.builder.id);
191
193
  return res.json(inbox);
192
194
  } catch (err) {
193
- console.error('[gds] GET /goals/inbox', err);
195
+ log.error('[gds] GET /goals/inbox', err);
194
196
  return res.fail('inbox_failed', { status: 500, message: 'internal error' });
195
197
  }
196
198
  });
@@ -210,7 +212,7 @@ module.exports = function buildGoalsRouter() {
210
212
  ]);
211
213
  return res.json({ goal: decorateGoalScope(goal), members, criteria, dependencies });
212
214
  } catch (err) {
213
- console.error('[gds] GET /goals/:id', err);
215
+ log.error('[gds] GET /goals/:id', err);
214
216
  return res.fail('fetch_failed', { status: 500, message: 'internal error' });
215
217
  }
216
218
  });
@@ -254,7 +256,7 @@ module.exports = function buildGoalsRouter() {
254
256
  const member = await db.addGoalMember({ goalId: id, builderId: req.builder.id, membershipKind: 'member', resolvedBy: req.builder.id });
255
257
  return res.status(201).json({ ok: true, member });
256
258
  } catch (err) {
257
- console.error('[gds] POST /goals/:id/join', err);
259
+ log.error('[gds] POST /goals/:id/join', err);
258
260
  return res.fail('join_failed', { status: 500, message: 'internal error' });
259
261
  }
260
262
  });
@@ -269,7 +271,7 @@ module.exports = function buildGoalsRouter() {
269
271
  if (!removed) return res.fail('not_a_member', 409);
270
272
  return res.json({ ok: true });
271
273
  } catch (err) {
272
- console.error('[gds] POST /goals/:id/leave', err);
274
+ log.error('[gds] POST /goals/:id/leave', err);
273
275
  return res.fail('leave_failed', { status: 500, message: 'internal error' });
274
276
  }
275
277
  });
@@ -309,7 +311,7 @@ module.exports = function buildGoalsRouter() {
309
311
  if (err && err.code === 'PENDING_EXISTS') {
310
312
  return res.fail('request_pending', { status: 409, message: 'You already have a pending request or invitation for this goal.' });
311
313
  }
312
- console.error('[gds] POST /goals/:id/requests', err);
314
+ log.error('[gds] POST /goals/:id/requests', err);
313
315
  return res.fail('request_failed', { status: 500, message: 'internal error' });
314
316
  }
315
317
  });
@@ -371,7 +373,7 @@ module.exports = function buildGoalsRouter() {
371
373
  if (!outcome) return res.fail('not_pending', { status: 409, message: 'The request changed underneath this response — re-read and retry.' });
372
374
  return res.json({ ok: true, action, request: outcome.request, member: outcome.member });
373
375
  } catch (err) {
374
- console.error('[gds] POST /goals/:id/requests/:reqId/respond', err);
376
+ log.error('[gds] POST /goals/:id/requests/:reqId/respond', err);
375
377
  return res.fail('request_respond_failed', { status: 500, message: 'internal error' });
376
378
  }
377
379
  });
@@ -430,7 +432,7 @@ module.exports = function buildGoalsRouter() {
430
432
  if (err && err.code === 'PENDING_EXISTS') {
431
433
  return res.fail('invite_pending', { status: 409, message: 'That builder already has a pending invitation or request for this goal.' });
432
434
  }
433
- console.error('[gds] POST /goals/:id/invitations', err);
435
+ log.error('[gds] POST /goals/:id/invitations', err);
434
436
  return res.fail('invite_failed', { status: 500, message: 'internal error' });
435
437
  }
436
438
  });
@@ -478,7 +480,7 @@ module.exports = function buildGoalsRouter() {
478
480
  if (!outcome) return res.fail('not_pending', { status: 409, message: 'The invitation changed underneath this response — re-read and retry.' });
479
481
  return res.json({ ok: true, action, request: outcome.request, member: outcome.member });
480
482
  } catch (err) {
481
- console.error('[gds] POST /goals/:id/invitations/:reqId/respond', err);
483
+ log.error('[gds] POST /goals/:id/invitations/:reqId/respond', err);
482
484
  return res.fail('invitation_respond_failed', { status: 500, message: 'internal error' });
483
485
  }
484
486
  });
@@ -506,7 +508,7 @@ module.exports = function buildGoalsRouter() {
506
508
  const requests = await db.listPendingRequestsForGoal(id);
507
509
  return res.json({ requests });
508
510
  } catch (err) {
509
- console.error('[gds] GET /goals/:id/requests', err);
511
+ log.error('[gds] GET /goals/:id/requests', err);
510
512
  return res.fail('fetch_failed', { status: 500, message: 'internal error' });
511
513
  }
512
514
  });
@@ -529,7 +531,7 @@ module.exports = function buildGoalsRouter() {
529
531
  if (!resolved) return res.fail('not_pending', { status: 409, message: 'The request changed underneath this withdrawal — re-read and retry.' });
530
532
  return res.json({ ok: true, request: resolved });
531
533
  } catch (err) {
532
- console.error('[gds] POST /goals/:id/requests/:reqId/withdraw', err);
534
+ log.error('[gds] POST /goals/:id/requests/:reqId/withdraw', err);
533
535
  return res.fail('withdraw_failed', { status: 500, message: 'internal error' });
534
536
  }
535
537
  });
@@ -598,7 +600,7 @@ module.exports = function buildGoalsRouter() {
598
600
  const member = await db.addGoalMember({ goalId: id, builderId, membershipKind, resolvedBy: req.builder.id });
599
601
  return res.status(201).json({ ok: true, member });
600
602
  } catch (err) {
601
- console.error('[gds] POST /goals/:id/members', err);
603
+ log.error('[gds] POST /goals/:id/members', err);
602
604
  return res.fail('add_member_failed', { status: 500, message: 'internal error' });
603
605
  }
604
606
  });
@@ -659,7 +661,7 @@ module.exports = function buildGoalsRouter() {
659
661
  }
660
662
  return res.json({ ok: true, member, changed: true });
661
663
  } catch (err) {
662
- console.error('[gds] PATCH /goals/:id/members/:builderId', err);
664
+ log.error('[gds] PATCH /goals/:id/members/:builderId', err);
663
665
  return res.fail('membership_change_failed', { status: 500, message: 'internal error' });
664
666
  }
665
667
  });
@@ -704,7 +706,7 @@ module.exports = function buildGoalsRouter() {
704
706
  }
705
707
  return res.json({ ok: true, goal: updated, changed: true });
706
708
  } catch (err) {
707
- console.error('[gds] POST /goals/:id/transfer', err);
709
+ log.error('[gds] POST /goals/:id/transfer', err);
708
710
  return res.fail('transfer_failed', { status: 500, message: 'internal error' });
709
711
  }
710
712
  });
@@ -749,7 +751,7 @@ module.exports = function buildGoalsRouter() {
749
751
  cross_goals_considered: crossGoal.map((c) => ({ id: Number(c.goal.id), title: c.goal.title })),
750
752
  });
751
753
  } catch (err) {
752
- console.error('[gds] GET /goals/:id/conflicts', err);
754
+ log.error('[gds] GET /goals/:id/conflicts', err);
753
755
  return res.fail('conflicts_failed', { status: 500, message: 'internal error' });
754
756
  }
755
757
  });
@@ -891,7 +893,7 @@ module.exports = function buildGoalsRouter() {
891
893
  requires_rank: updated.requires_rank,
892
894
  });
893
895
  } catch (err) {
894
- console.error('[gds] POST /goals/:id/conflicts/resolve', err);
896
+ log.error('[gds] POST /goals/:id/conflicts/resolve', err);
895
897
  return res.fail('conflict_resolve_failed', { status: 500, message: 'internal error' });
896
898
  }
897
899
  });
@@ -915,7 +917,7 @@ module.exports = function buildGoalsRouter() {
915
917
  const updated = await db.setGoalStatus({ goalId: id, status: 'archived' });
916
918
  return res.json({ ok: true, goal: updated });
917
919
  } catch (err) {
918
- console.error('[gds] POST /goals/:id/archive', err);
920
+ log.error('[gds] POST /goals/:id/archive', err);
919
921
  return res.fail('archive_failed', { status: 500, message: 'internal error' });
920
922
  }
921
923
  });
@@ -938,7 +940,7 @@ module.exports = function buildGoalsRouter() {
938
940
  const updated = await db.setGoalStatus({ goalId: id, status: 'open' });
939
941
  return res.json({ ok: true, goal: updated });
940
942
  } catch (err) {
941
- console.error('[gds] POST /goals/:id/reopen', err);
943
+ log.error('[gds] POST /goals/:id/reopen', err);
942
944
  return res.fail('reopen_failed', { status: 500, message: 'internal error' });
943
945
  }
944
946
  });
@@ -982,7 +984,7 @@ module.exports = function buildGoalsRouter() {
982
984
  const updated = await db.addGoalScopeModules({ goalId: id, modules: add });
983
985
  return res.json({ ok: true, goal: updated });
984
986
  } catch (err) {
985
- console.error('[gds] POST /goals/:id/scope', err);
987
+ log.error('[gds] POST /goals/:id/scope', err);
986
988
  return res.fail('scope_widen_failed', { status: 500, message: 'internal error' });
987
989
  }
988
990
  });
@@ -1116,7 +1118,7 @@ module.exports = function buildGoalsRouter() {
1116
1118
  const updated = await hierarchy.attachParent(task.id, parentTaskId);
1117
1119
  attachedParentId = updated.parent_task_id != null ? Number(updated.parent_task_id) : null;
1118
1120
  } catch (err) {
1119
- console.error('[gds] POST /goals/:id/tasks attachParent', err);
1121
+ log.error('[gds] POST /goals/:id/tasks attachParent', err);
1120
1122
  return res.fail('parent_attach_failed', { status: 409, message: `Task #${task.id} was created under the goal, but attaching it under parent #${parentTaskId} failed (${(err && err.code) || 'unknown'}).`, details: { code: err && err.code, task } });
1121
1123
  }
1122
1124
  }
@@ -1130,7 +1132,7 @@ module.exports = function buildGoalsRouter() {
1130
1132
  requires_rank: task.requires_rank,
1131
1133
  });
1132
1134
  } catch (err) {
1133
- console.error('[gds] POST /goals/:id/tasks', err);
1135
+ log.error('[gds] POST /goals/:id/tasks', err);
1134
1136
  return res.fail('goal_task_create_failed', { status: 500, message: 'internal error' });
1135
1137
  }
1136
1138
  });
@@ -26,6 +26,8 @@ const { classifyTaskKind, VALID_KINDS, VALID_DISCIPLINES, VOTABLE_TASK_STATUSES,
26
26
  const { isModuleKey, moduleKeys } = api.moduleScopeMap;
27
27
  const { validateOrRespond, LIMITS, parseId } = api;
28
28
  const { badKind, detectsMigration } = require('./task-route-helpers.js');
29
+ // task 1003208: structured logging (pino via the doorway) — was console.*.
30
+ const log = api.logger('lifecycle');
29
31
 
30
32
  module.exports = function registerTaskWriteRoutes(router) {
31
33
  // Task create/promote/edit (plan/scope mutation) is Metic+ as of ADR 0090
@@ -225,7 +227,7 @@ module.exports = function registerTaskWriteRoutes(router) {
225
227
  { limit: 3 }
226
228
  );
227
229
  } catch (e) {
228
- console.error('[gds] POST /tasks suggest-criteria (non-blocking)', e && e.message);
230
+ log.error('[gds] POST /tasks suggest-criteria (non-blocking)', e && e.message);
229
231
  }
230
232
  }
231
233
  // task 1002539 — the orphan-task advisory (the /goal-create auto-pickup
@@ -241,7 +243,7 @@ module.exports = function registerTaskWriteRoutes(router) {
241
243
  goal_advisory, est_advisory: await require('../est-advisory').buildEstAdvisory({ db, task }), // task 1003148
242
244
  });
243
245
  } catch (err) {
244
- console.error('[gds] POST /tasks', err);
246
+ log.error('[gds] POST /tasks', err);
245
247
  res.fail('create_failed', { status: 500, message: 'internal error' });
246
248
  }
247
249
  });
@@ -386,7 +388,7 @@ module.exports = function registerTaskWriteRoutes(router) {
386
388
  return res.fail('goal_version_mismatch', { status: 409, message: `Goal #${goalId} is in version '${goal.version_id}' but task #${id} is in '${task.version_id}'. A task can only join a goal in its own version.` });
387
389
  }
388
390
  } catch (err) {
389
- console.error('[gds] PATCH /tasks/:id goal_id', err);
391
+ log.error('[gds] PATCH /tasks/:id goal_id', err);
390
392
  return res.fail('patch_failed', { status: 500, message: 'internal error' });
391
393
  }
392
394
  }
@@ -424,7 +426,7 @@ module.exports = function registerTaskWriteRoutes(router) {
424
426
  updated = await db.updateTaskParallelSafe(id, body.parallel_safe);
425
427
  if (!updated) return res.fail('task_not_found', 404);
426
428
  } catch (err) {
427
- console.error('[gds] PATCH /tasks/:id parallel_safe', err);
429
+ log.error('[gds] PATCH /tasks/:id parallel_safe', err);
428
430
  return res.fail('patch_failed', { status: 500, message: 'internal error' });
429
431
  }
430
432
  }
@@ -433,7 +435,7 @@ module.exports = function registerTaskWriteRoutes(router) {
433
435
  updated = await db.updateTaskSecuritySensitive(id, body.security_sensitive);
434
436
  if (!updated) return res.fail('task_not_found', 404);
435
437
  } catch (err) {
436
- console.error('[gds] PATCH /tasks/:id security_sensitive', err);
438
+ log.error('[gds] PATCH /tasks/:id security_sensitive', err);
437
439
  return res.fail('patch_failed', { status: 500, message: 'internal error' });
438
440
  }
439
441
  }
@@ -442,7 +444,7 @@ module.exports = function registerTaskWriteRoutes(router) {
442
444
  updated = await db.updateTaskAutomationTag(id, body.automation_tag);
443
445
  if (!updated) return res.fail('task_not_found', 404);
444
446
  } catch (err) {
445
- console.error('[gds] PATCH /tasks/:id automation_tag', err);
447
+ log.error('[gds] PATCH /tasks/:id automation_tag', err);
446
448
  return res.fail('patch_failed', { status: 500, message: 'internal error' });
447
449
  }
448
450
  }
@@ -451,7 +453,7 @@ module.exports = function registerTaskWriteRoutes(router) {
451
453
  updated = await db.updateTaskNeedsMigration(id, body.needs_migration);
452
454
  if (!updated) return res.fail('task_not_found', 404);
453
455
  } catch (err) {
454
- console.error('[gds] PATCH /tasks/:id needs_migration', err);
456
+ log.error('[gds] PATCH /tasks/:id needs_migration', err);
455
457
  return res.fail('patch_failed', { status: 500, message: 'internal error' });
456
458
  }
457
459
  }
@@ -460,7 +462,7 @@ module.exports = function registerTaskWriteRoutes(router) {
460
462
  updated = await db.updateTaskModuleKey(id, body.module_key ?? null);
461
463
  if (!updated) return res.fail('task_not_found', 404);
462
464
  } catch (err) {
463
- console.error('[gds] PATCH /tasks/:id module_key', err);
465
+ log.error('[gds] PATCH /tasks/:id module_key', err);
464
466
  return res.fail('patch_failed', { status: 500, message: 'internal error' });
465
467
  }
466
468
  }
@@ -469,7 +471,7 @@ module.exports = function registerTaskWriteRoutes(router) {
469
471
  updated = await db.updateTaskDescription(id, body.description);
470
472
  if (!updated) return res.fail('task_not_found', 404);
471
473
  } catch (err) {
472
- console.error('[gds] PATCH /tasks/:id description', err);
474
+ log.error('[gds] PATCH /tasks/:id description', err);
473
475
  return res.fail('patch_failed', { status: 500, message: 'internal error' });
474
476
  }
475
477
  }
@@ -478,7 +480,7 @@ module.exports = function registerTaskWriteRoutes(router) {
478
480
  updated = await db.updateTaskGoal(id, body.goal_id);
479
481
  if (!updated) return res.fail('task_not_found', 404);
480
482
  } catch (err) {
481
- console.error('[gds] PATCH /tasks/:id goal_id', err);
483
+ log.error('[gds] PATCH /tasks/:id goal_id', err);
482
484
  return res.fail('patch_failed', { status: 500, message: 'internal error' });
483
485
  }
484
486
  }
@@ -487,7 +489,7 @@ module.exports = function registerTaskWriteRoutes(router) {
487
489
  updated = await db.updateTaskRequiresRank(id, body.requires_rank);
488
490
  if (!updated) return res.fail('task_not_found', 404);
489
491
  } catch (err) {
490
- console.error('[gds] PATCH /tasks/:id requires_rank', err);
492
+ log.error('[gds] PATCH /tasks/:id requires_rank', err);
491
493
  return res.fail('patch_failed', { status: 500, message: 'internal error' });
492
494
  }
493
495
  }
@@ -496,7 +498,7 @@ module.exports = function registerTaskWriteRoutes(router) {
496
498
  updated = await db.updateTaskKind(id, body.kind);
497
499
  if (!updated) return res.fail('task_not_found', 404);
498
500
  } catch (err) {
499
- console.error('[gds] PATCH /tasks/:id kind', err);
501
+ log.error('[gds] PATCH /tasks/:id kind', err);
500
502
  return res.fail('patch_failed', { status: 500, message: 'internal error' });
501
503
  }
502
504
  }
@@ -505,7 +507,7 @@ module.exports = function registerTaskWriteRoutes(router) {
505
507
  updated = await db.updateTaskDiscipline(id, body.discipline);
506
508
  if (!updated) return res.fail('task_not_found', 404);
507
509
  } catch (err) {
508
- console.error('[gds] PATCH /tasks/:id discipline', err);
510
+ log.error('[gds] PATCH /tasks/:id discipline', err);
509
511
  return res.fail('patch_failed', { status: 500, message: 'internal error' });
510
512
  }
511
513
  }
@@ -515,7 +517,7 @@ module.exports = function registerTaskWriteRoutes(router) {
515
517
  updated = await db.updateTaskNewcomerFriendly(id, incoming);
516
518
  if (!updated) return res.fail('task_not_found', 404);
517
519
  } catch (err) {
518
- console.error('[gds] PATCH /tasks/:id newcomer_friendly', err);
520
+ log.error('[gds] PATCH /tasks/:id newcomer_friendly', err);
519
521
  return res.fail('patch_failed', { status: 500, message: 'internal error' });
520
522
  }
521
523
  }
@@ -535,7 +537,7 @@ module.exports = function registerTaskWriteRoutes(router) {
535
537
  500;
536
538
  return res.fail(err.code, status, { ...err });
537
539
  }
538
- console.error('[gds] PATCH /tasks/:id', err);
540
+ log.error('[gds] PATCH /tasks/:id', err);
539
541
  res.fail('patch_failed', { status: 500, message: 'internal error' });
540
542
  }
541
543
  });
@@ -42,6 +42,8 @@ const PUBLISH_BUNDLE_MAX_BYTES = 20 * 1024 * 1024; // 20 MB decoded bundle
42
42
  // Mirrors ship.js sanitizeGitHubRefs (which lives in scripts/ and isn't requirable
43
43
  // here).
44
44
  const { MIGRATION_KEYWORD_RE, VALID_FITS, badKind, deHashRefs, detectsMigration, gateTaskOwnership, graderBypassEnabled, isVerifyTask, peerVoteNet, postGateAuthorTrust, rankAttestsGateTrust } = require('./task-route-helpers.js');
45
+ // task 1003208: structured logging (pino via the doorway) — was console.*.
46
+ const log = api.logger('lifecycle');
45
47
  const registerTaskWriteRoutes = require('./task-write-routes.js'); // task 1003206 — POST /tasks + PATCH /tasks/:id
46
48
  module.exports = function buildTasksRouter() {
47
49
  const router = express.Router();
@@ -146,7 +148,7 @@ module.exports = function buildTasksRouter() {
146
148
  }
147
149
  res.json({ disciplines });
148
150
  } catch (err) {
149
- console.error('[gds] GET /tasks/newcomer-floor', err);
151
+ log.error('[gds] GET /tasks/newcomer-floor', err);
150
152
  res.fail('newcomer_floor_failed', 500);
151
153
  }
152
154
  });
@@ -167,7 +169,7 @@ module.exports = function buildTasksRouter() {
167
169
  const stamped = results.reduce((n, r) => n + (Array.isArray(r.stamped) ? r.stamped.length : 0), 0);
168
170
  res.json({ ok: true, stamped, results });
169
171
  } catch (err) {
170
- console.error('[gds] POST /tasks/newcomer-restock', err);
172
+ log.error('[gds] POST /tasks/newcomer-restock', err);
171
173
  res.fail('newcomer_restock_failed', { status: 500, message: 'internal error' });
172
174
  }
173
175
  });
@@ -470,7 +472,7 @@ module.exports = function buildTasksRouter() {
470
472
  if (err.code === 'TASK_NOT_CONFIRMED') {
471
473
  return res.fail('task_not_confirmed', 409);
472
474
  }
473
- console.error('[gds] POST /tasks/:id/ship', err);
475
+ log.error('[gds] POST /tasks/:id/ship', err);
474
476
  res.fail('ship_failed', { status: 500, message: 'internal error' });
475
477
  }
476
478
  });
@@ -522,7 +524,7 @@ module.exports = function buildTasksRouter() {
522
524
  `actually passed, with { "verified": true }. Re-run /builder-ship ${id} --verified once it has.` });
523
525
  }
524
526
  if (graderBypassEnabled()) {
525
- console.log(`[gds] grader-bypass confirm: builder ${req.builder.id} (${req.builder.rank}) confirming task ${id} with GRADER_BYPASS_ENABLED on`);
527
+ log.info(`[gds] grader-bypass confirm: builder ${req.builder.id} (${req.builder.rank}) confirming task ${id} with GRADER_BYPASS_ENABLED on`);
526
528
  }
527
529
  try {
528
530
  const result = await db.confirmTask({ taskId: id, builderId: req.builder.id });
@@ -531,7 +533,7 @@ module.exports = function buildTasksRouter() {
531
533
  if (err.code === 'TASK_NOT_COMPLETED') {
532
534
  return res.fail('task_not_completed', 409);
533
535
  }
534
- console.error('[gds] POST /tasks/:id/confirm', err);
536
+ log.error('[gds] POST /tasks/:id/confirm', err);
535
537
  res.fail('confirm_failed', { status: 500, message: 'internal error' });
536
538
  }
537
539
  });
@@ -569,7 +571,7 @@ module.exports = function buildTasksRouter() {
569
571
  if (err.code === 'TASK_HAS_ACTIVE_CLAIM') {
570
572
  return res.fail('task_has_active_claim', { status: 409, message: 'release the active claim before abandoning' });
571
573
  }
572
- console.error('[gds] POST /tasks/:id/abandon', err);
574
+ log.error('[gds] POST /tasks/:id/abandon', err);
573
575
  res.fail('abandon_failed', { status: 500, message: 'internal error' });
574
576
  }
575
577
  });
@@ -615,7 +617,7 @@ module.exports = function buildTasksRouter() {
615
617
  } catch (err) {
616
618
  // Log the detail server-side; don't return err.message — a raw Postgres
617
619
  // error can leak schema internals (table/column/constraint names).
618
- console.error('[gds] GET /tasks/peer-votes/feed', err);
620
+ log.error('[gds] GET /tasks/peer-votes/feed', err);
619
621
  res.fail('feed_failed', 500);
620
622
  }
621
623
  });
@@ -636,7 +638,7 @@ module.exports = function buildTasksRouter() {
636
638
  const result = await db.tallyPeerVotes();
637
639
  res.json(result);
638
640
  } catch (err) {
639
- console.error('[gds] POST /tasks/peer-votes/tally', err);
641
+ log.error('[gds] POST /tasks/peer-votes/tally', err);
640
642
  res.fail('tally_failed', 500);
641
643
  }
642
644
  });
@@ -717,7 +719,7 @@ module.exports = function buildTasksRouter() {
717
719
  const { net, total } = await peerVoteNet(id);
718
720
  res.json({ ok: true, task_id: id, my_vote: vote === 0 ? null : vote, net_votes: net, total_votes: total });
719
721
  } catch (err) {
720
- console.error('[gds] POST /tasks/:id/vote', err);
722
+ log.error('[gds] POST /tasks/:id/vote', err);
721
723
  res.fail('vote_failed', 500);
722
724
  }
723
725
  });
@@ -819,7 +821,7 @@ module.exports = function buildTasksRouter() {
819
821
  if (err.code === 'TASK_NOT_GRADABLE') {
820
822
  return res.fail('task_not_gradable', 409, { current_status: err.current_status });
821
823
  }
822
- console.error('[gds] POST /tasks/:id/grade', err);
824
+ log.error('[gds] POST /tasks/:id/grade', err);
823
825
  res.fail('grade_failed', { status: 500, message: 'internal error' });
824
826
  }
825
827
  });
@@ -860,7 +862,7 @@ module.exports = function buildTasksRouter() {
860
862
  if (err.code === 'OPEN_REQUEST_EXISTS') {
861
863
  return res.fail('open_request_exists', 409);
862
864
  }
863
- console.error('[gds] POST /tasks/:id/override-request', err);
865
+ log.error('[gds] POST /tasks/:id/override-request', err);
864
866
  res.fail('override_request_failed', { status: 500, message: 'internal error' });
865
867
  }
866
868
  });
@@ -962,7 +964,7 @@ module.exports = function buildTasksRouter() {
962
964
  try {
963
965
  await db.setClaimPublishedSha({ taskId: id, builderId: req.builder.id, sha: pushedSha });
964
966
  } catch (e) {
965
- console.error('[gds] setClaimPublishedSha (non-blocking)', id, e && e.message);
967
+ log.error('[gds] setClaimPublishedSha (non-blocking)', id, e && e.message);
966
968
  }
967
969
  return res.status(201).json({
968
970
  ok: true, pr_url: pr.url, pr_number: pr.number, head_sha: pushedSha, auto_merge: autoMerge,
@@ -983,7 +985,7 @@ module.exports = function buildTasksRouter() {
983
985
  // is a one-line diagnosis, not a multi-hour opaque-`publish_failed` mystery
984
986
  // (the BV1.R04/R05 incident: ~2h of bare GIT_FAILED with no cause). Return the
985
987
  // step + a short scrubbed reason to the (ownership-gated) builder too.
986
- console.error(
988
+ log.error(
987
989
  '[gds] POST /tasks/:id/publish-branch', e.code || e.message,
988
990
  e.git_step ? `step=${e.git_step}` : '',
989
991
  e.stderr ? `stderr=${e.stderr}` : ''
@@ -1026,7 +1028,7 @@ module.exports = function buildTasksRouter() {
1026
1028
  const proof = await githubPush.landProvenForTask({ taskId: id, publishedSha, status });
1027
1029
  if (!proof.proven) {
1028
1030
  reconcile_blocked = proof.reason;
1029
- console.warn(`[gds] publish-status reconcile BLOCKED task ${id}: ${proof.reason}`);
1031
+ log.warn(`[gds] publish-status reconcile BLOCKED task ${id}: ${proof.reason}`);
1030
1032
  }
1031
1033
  }
1032
1034
  if (githubPush.landConfirmed(status) && !reconcile_blocked) {
@@ -1062,14 +1064,14 @@ module.exports = function buildTasksRouter() {
1062
1064
  try {
1063
1065
  merge_driver = await githubPush.mergeIfGreen({ branch });
1064
1066
  } catch (e) {
1065
- console.error('[gds] publish-status merge-driver (non-blocking):', e && e.message);
1067
+ log.error('[gds] publish-status merge-driver (non-blocking):', e && e.message);
1066
1068
  }
1067
1069
  }
1068
1070
  return res.json({ ok: true, ...status, reconciled, reconcile_blocked, merge_driver });
1069
1071
  } catch (e) {
1070
1072
  if (e.code === 'NO_REPO_INFO') return res.fail('no_repo_info', 500);
1071
1073
  if (e.code === 'APP_NOT_INSTALLED') return res.fail('push_unconfigured', { status: 503, message: appNotInstalledMessage(e) });
1072
- console.error('[gds] GET /tasks/:id/publish-status', e.code || e.message);
1074
+ log.error('[gds] GET /tasks/:id/publish-status', e.code || e.message);
1073
1075
  return res.fail('status_failed', 502);
1074
1076
  }
1075
1077
  });
@@ -1107,7 +1109,7 @@ module.exports = function buildTasksRouter() {
1107
1109
  try {
1108
1110
  await db.setClaimPublishedSha({ taskId: id, builderId: req.builder.id, sha: headSha });
1109
1111
  } catch (e) {
1110
- console.error('[gds] setClaimPublishedSha via attest-gate (non-blocking)', id, e && e.message);
1112
+ log.error('[gds] setClaimPublishedSha via attest-gate (non-blocking)', id, e && e.message);
1111
1113
  }
1112
1114
  if (!githubPush.isConfigured()) {
1113
1115
  return res.fail('push_unconfigured', { status: 503, message: 'No server push credential (GitHub App / GITHUB_PUSH_TOKEN) to post the gate attestation. ' +
@@ -1129,7 +1131,7 @@ module.exports = function buildTasksRouter() {
1129
1131
  const rows = await db.listOverrideRequests({ status });
1130
1132
  res.json({ requests: rows });
1131
1133
  } catch (err) {
1132
- console.error('[gds] GET /override-requests', err);
1134
+ log.error('[gds] GET /override-requests', err);
1133
1135
  res.fail('list_failed', { status: 500, message: 'internal error' });
1134
1136
  }
1135
1137
  });
@@ -1169,7 +1171,7 @@ module.exports = function buildTasksRouter() {
1169
1171
  // misattribute. 409 — the request is left open for inspection.
1170
1172
  return res.fail('override_no_requester', { status: 409, message: 'internal error' });
1171
1173
  }
1172
- console.error('[gds] POST /override-requests/:id/decide', err);
1174
+ log.error('[gds] POST /override-requests/:id/decide', err);
1173
1175
  res.fail('decide_failed', { status: 500, message: 'internal error' });
1174
1176
  }
1175
1177
  });