fix: wire the deletion barrier into the schedules service adapter; fail fast on missing deps

Codex (review of 17534d13a) caught the under-wiring trap recurring in the commit that was
meant to fix it: the JS adapter (api/server/services/Schedules) constructs
createSchedulesService WITHOUT isUserDeleting, so engineDeps.isOwnerDeleting ->
deps.isUserDeleting(userId) would throw "is not a function" on every scheduled fire. The
dispatch-boundary barrier check was therefore not just inert but crashing. The TS type
requires the dep; the JS adapter is not typechecked against it, so tsc never saw it.

Fix: pass methods.isUserDeleting into the adapter. Plus a guardrail so this whole class
cannot recur silently: createSchedulesService now validates its required deps at
CONSTRUCTION and throws a clear boot-time error, instead of surfacing a missing dep as a
cryptic failure deep inside a live fire.
This commit is contained in:
Danny Avila 2026-07-23 16:28:34 -04:00
parent fe56ea4863
commit 3a101430f7
2 changed files with 22 additions and 0 deletions

View file

@ -17,6 +17,9 @@ const service = createSchedulesService({
{ upsert: true, new: true },
).lean(),
resolveAgentFireAccess,
// Durable account-deletion barrier consulted at the fire dispatch boundary. Without
// this, engineDeps.isOwnerDeleting would throw on every scheduled fire.
isUserDeleting: methods.isUserDeleting,
});
module.exports = {

View file

@ -176,6 +176,25 @@ export interface SchedulesService {
export function createSchedulesService(deps: SchedulesServiceDeps): SchedulesService {
const { methods } = deps;
// Fail LOUDLY at construction, not per-fire. The JS adapter (api/server/services/
// Schedules) is not typechecked against SchedulesServiceDeps, so a missing dep would
// otherwise surface only as a `deps.X is not a function` deep inside a live fire —
// which is exactly how the deletion-barrier probe shipped unwired twice.
const REQUIRED_DEPS: Array<keyof SchedulesServiceDeps> = [
'methods',
'getAppConfig',
'findUserById',
'findBalance',
'upsertBalance',
'resolveAgentFireAccess',
'isUserDeleting',
];
for (const key of REQUIRED_DEPS) {
if (deps[key] == null) {
throw new Error(`createSchedulesService: missing required dependency "${key}"`);
}
}
/**
* Resolves schedule limits, honoring per-principal (role/user) config overrides
* when a user is supplied (routes pass req.user, the fire path passes the owner).