@bongos/core 1.19.336 → 1.19.338
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 +30 -30
- package/modules/builder-settings/routes/settings.js +18 -16
- package/modules/dev-box/routes/box.js +25 -23
- package/modules/discord/discord-bot.js +37 -35
- package/modules/discord/discord-role-sync.js +14 -12
- package/modules/government/routes/government.js +15 -13
- package/modules/ideas/routes/inbox.js +16 -14
- package/modules/lifecycle/routes/claims.js +11 -9
- package/modules/lifecycle/routes/goals.js +25 -23
- package/modules/lifecycle/routes/task-write-routes.js +17 -15
- package/modules/lifecycle/routes/tasks.js +21 -19
- package/modules/memory/routes/memory.js +18 -16
- package/modules/platform-identity/routes/sso.js +13 -11
- package/modules/provisioning/routes/provisioning.js +29 -18
- package/package-lock.json +2 -2
- package/package.json +1 -1
- package/scripts/gds/fitness.js +6 -0
- package/src/bongos/auth-admission.js +9 -6
- package/src/bongos/routes/auth.js +20 -17
- package/src/bongos/routes/backup.js +7 -4
- package/src/bongos/routes/builders.js +17 -14
- package/src/bongos/routes/public.js +9 -6
- package/src/bongos/uptime-poller.js +8 -5
- package/src/module-api.js +44 -6
- package/tests/logger.mjs +67 -0
- package/tests/provisioning_github_app_routes.mjs +40 -1
|
@@ -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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
});
|