7435b2c5d2
Brings in every phase-2 fix without regressing the phase-3 surface.
Resolutions:
* `apps/webapp/app/v3/mollifier/mollifierDrainer.server.ts` — kept
phase-3's `MollifierDrainer<MollifierSnapshot>` generic and the
`createDrainerHandler({ engine, prisma })` + `isRetryablePgError`
wiring; added phase-2's `MollifierConfigurationError` class and
switched every env read to `TRIGGER_MOLLIFIER_*`. The doc comment on
`getMollifierDrainer` now points at `mollifierDrainerWorker.server.ts`
instead of the legacy `worker.server.ts`.
* `apps/webapp/app/runEngine/services/triggerTask.server.ts` — kept
phase-3's gate inputs (`options: { debounce, oneTimeUseToken,
parentTaskRunId, resumeParentOnCompletion }` for the C1/C3/F4
bypasses) and the `#buildEngineTriggerInput` refactor; wrapped the
gate call in phase-2's `this.isMollifierGloballyEnabled()` short-
circuit so a trigger with the feature off skips the
`GateInputs` allocation + `mollifier.decisions{outcome=pass_through}`
increment. `mollifierOutcome` is `GateOutcome | null`, downstream
access uses optional chaining. Phase-2's phase-1 dual-write block is
dropped — phase-3's mollify branch already writes via
`mollifyTrigger` and returns a synthetic result.
Phase-2 fixes preserved (verified post-merge):
- `TRIGGER_MOLLIFIER_*` env-var prefix on every consumer
- `TRIGGER_MOLLIFIER_DRAINER_ENABLED` separate per-replica switch
- `MollifierConfigurationError` rethrow in the drainer bootstrap
- Factory create + bootstrap start split (`drainer.start()` only
runs after SIGTERM handlers register)
- `signalsEmitter.on` instead of `process.once`
- `MollifierDrainer.stop` deadline-timer `clearTimeout` in finally
- `vi.fn` handler spies removed from `drainer.test.ts`
- `isMollifierGloballyEnabled` DI hook on `RunEngineTriggerTaskService`
- Legacy `worker.server.ts` header
- New `mollifierDrainerWorker.server.ts` error-classification test
- Updated `.server-changes/mollifier-burst-protection.md`
Pre-existing on phase-3, NOT introduced by this merge (verified by
`git diff origin/mollifier-phase-3 -- apps/webapp/test/engine/triggerTask.test.ts`
showing only `isMollifierGloballyEnabled: () => true` additions):
- `mollifier · mollify action triggers dual-write` asserts
`prisma.taskRun.findFirst` returns a row, but phase-3's
`mollifyTrigger` writes to the buffer and returns a synthetic
result without invoking `engine.trigger`. Test name + assertions
still reflect phase-1 dual-write semantics.
- `mollifier · engine.trigger throwing AFTER buffer.accept` expects
the call to reject; phase-3 returns synthetic before
`engine.trigger` is reached, so the injected throw never fires.
- `mollifier · debounce match produces an orphan buffer entry` —
phase-3 explicitly skips the buffer branch when debounce is set
(`\!body.options?.debounce` guard), so no orphan accept happens.
These three tests describe phase-1 monitoring behaviour on phase-3
code that abandoned dual-write. Updating them is phase-3's own tech
debt, separate from this merge.