Playbook: Pinbox24 W4 — HR wniosek (holidays-apply-process) submission failures — activate_trigger bug class
Incident date: 2026-08-06
Resolved: 2026-08-06 — three independent, pre-existing bugs found + fixed in pinbox24/p24-back-ts (GitLab)
Severity: P2 — user-visible “cannot submit HR wniosek in W4” (holiday/leave application), silent since ~2024 (43 error instances on bug 1)
Repo (code): pinbox24/p24-back-ts on GitLab — the W4 backend (v42-prod). Not p24-infra.
Service affected: v42-prod process engine, holidays-apply-process → activate_trigger.ts flow
GH tracking: #5764 (investigation) · follow-ups #5755 (Mailgun key reconciliation), #4948 (INTEGRATION_AUTH_TOKEN — expedited same-day, closed)
GitLab MRs (code-level fixes):
p24-back-ts!801 + repair !802 — bug 1 (processDocnot passed to task context)p24-back-ts!804 — bug 2 (numeric0misclassified as empty)p24-back-ts!806 — bug 3 (dead hardcoded Mailgun API key)
Why this playbook lives in p24-infra, not GitLab. The fixes are in the GitLab W4 backend (sys-admin / infra-task-owned W3/W4 stack — see
../w3-w4-stack-operations.md). This repo captures the investigation + bug class so it is recoverable and auditable from the infra side. A p24-infradev-codercannot open MRs to that GitLab repo; the code fixes were made through the sanctioned W4 path.
Symptoms
- User reports: “cannot submit HR wniosek (holiday/leave application) in W4”.
- The
holidays-apply-processnever advanced past itsactivate_triggerstep for the affected submission. - On investigation,
activate_trigger.ts’sgetNextAutoTrans()never found an auto-transition even when a real one was configured —isAutoTransPresentwas alwaysfalse. - ~43 historical error instances for bug 1, silent since ~2024 (no user-facing crash, the process just stalled), so it had never been isolated before.
Root causes
All three sit in the holidays-apply-process activate_trigger flow. They are independent — each
was uncovered only after the previous one was fixed and the flow advanced one step further.
Bug 1 — processDoc not passed to the task context (!801 / repair !802)
processButtonClickedHelper resolved the full process document but passed the bare processId
ObjectId as the task context’s process field. activate_trigger.ts’s getNextAutoTrans() then
read data.process.transitions — always undefined on a bare ObjectId — so isAutoTransPresent
was always false regardless of whether a real auto-transition existed.
Fix: pass the already-fetched processDoc object (which carries .transitions) into the task
context instead of the raw ID.
Tooling footgun (recorded so it does not recur): MR !801’s first push corrupted the target file to a single line 1 because of a PowerShell/JSON newline-encoding bug in the push tooling — this broke
back-end-buildonmasterfor ~15 min until !802 repaired it. No live impact (the deploy never ran on the broken commit). This is the same newline-encoding hazard called out for SOPS writes in CLAUDE.md — never write repo files with>/Out-File/Set-Contentfrom PowerShell; use[System.IO.File]::WriteAllText(..., UTF8 no-BOM).
Bug 2 — numeric 0 misclassified as “empty” (falsy-zero) (!804)
Same file. if (!valueToCheck) treated a legitimate empActualYearCheck count of 0 as empty
(JavaScript falsy), skipping the process’s explicit {key:"0", activitie:48} routing rule and falling
through to the (correctly absent, in this case) auto-transition fallback. A commented-out prior fix
attempt for this exact issue was found on the line directly above — someone had hit it before and
backed out.
Fix: introduce an explicit emptiness test and use it consistently:
const valueIsEmpty = value === null || value === undefined || value === "";so a real 0 is routed by the {key:"0", …} rule instead of being swallowed as “empty”.
Bug class — audit target.
!value/if (!x)as an “is it empty?” test is wrong for any field whose legitimate value can be0,"", orfalse. The issue notes this pattern is not specific toholidays-apply-process— otheractivate_triggerusages across Pinbox24 should be audited for the same falsy-zero mistake. See Follow-ups below.
Bug 3 — dead hardcoded Mailgun API key (!806)
src/constants/common.const.ts’s MAILGUN_SETTINGS.API_KEY was a hardcoded literal, confirmed dead
(401 against the Mailgun EU API). The live v42-prod container already had a valid, unused
MAILGUN_API_KEY env var (sourced from SOPS secrets/pinbox24-w4.env.sops key V42_MAILGUN_API_KEY,
wired via an earlier out-of-band hotfix — history in #4169 / #4306 / #4416).
Fix: de-hardcode to process.env.MAILGUN_API_KEY. Verified via a real test send: env-var key → 200
delivered; hardcoded literal → 401.
Secret hygiene. Reference the key NAME only (
V42_MAILGUN_API_KEY/MAILGUN_API_KEY) — never quote the value. Secrets live in SOPS+age (secrets/pinbox24-w4.env.sops). Any rotation or reconciliation is asecret-manageraction, notdev-coder.
Verification method (read-only, no value ever printed)
End-to-end verification after each fix used read-only MongoDB queries against
w4_db.process.instances with the w4_app role (not admin) plus PM2 log inspection on bms-1
(94.23.26.113) — see the app-user vs admin split in
../w3-w4-stack-operations.md.
Final confirmed instance 6a74bd2d3fe1ec003537b2cd (2026-08-06T16:58:37Z) reached:
state: 50— “Pracownik nie zarejestrowany w rejestrze hr na aktualny rok”stateTaskStage: pending
This is the correct terminal state for an employee not yet in the HR registry for the current year — not an error. User confirmed submission then succeeded.
Also surfaced this session (context, tracked separately)
- #4948 —
INTEGRATION_AUTH_TOKENgap (unrelated live incident). Deliberately deferred to the 2026-08-07 window, then expedited same-day after confirming live user impact (v42-prod/s3-v2-v42-prodhad zero relevant env vars since 2026-08-04 23:59). Wired viasecrets/pinbox24-w4.env.sopskeyV42_INTEGRATION_AUTH_TOKEN;secrets-sync.ymlrecreated both containers; verified live. Closed by the secret-manager follow-up. - #5755 — Mailgun key three-way divergence (new, open). SOPS
V42_MAILGUN_API_KEY, the former master-hardcoded literal, and the live container’sMAILGUN_API_KEYare three different values, only two of which (SOPS, live container) are currently valid. A futuresecrets-sync+ container recreate would silently swap the live working key for the SOPS one with no documented reconciliation. Needs asecret-manager/human decision on which value is canonical before that is safe.
Follow-ups
- #5755 —
secret-manager/human decision on the canonical Mailgun key value (see above) before the nextsecrets-syncrecreate touchesv42-prod/s3-v2-v42-prod. - Audit other
activate_triggerusages across Pinbox24 for the bug-2 falsy-zero pattern (!valueused as an emptiness test where0is a legitimate value). This bug class is not specific toholidays-apply-process. Open adev-issueagainstpinbox24/p24-back-ts(GitLab) via the W3/W4 stack path if/when scoped.
Related playbooks
../w3-w4-stack-operations.md— W3/W4 ownership, permission matrix, MongoDBw4_appvs admin splitpinbox24-w3-w4-health-verification.md— health-check / verify-after-deploy methodw4-s3v2-mailgun-password-verify-first.md— prior Mailgun dead-credential verify-first patternpinbox24-mailgun-duplicate-check-fix.md— earlier Mailgun/process-engine fix with the same GitLab-MR-as-source-of-truth shape