develcustomization/cib sync across three repos (vuer_oss, vuer_css, portal_css) in preparation for CIB release 1.9.11.102. Eleven months of drift, 334 commits, 212 tickets, and zero partner commits since the last release — this is a pure core-update release. The merges were textually manageable; the work was the semantic sweep, and it turned up a new class of partner-merge break: devel removing dependencies that partner code still imports.

For Agents

  • Ticket: FKITDEV-9197“CIB - CIB Release 1.9.11.102 előkészítés”. State In-progress, assignee andras.lederer, reporter Bence Varga, created 2026-08-05, empty description.
  • Parent: ASSCIB-166 — State Submitted, Bence Varga. Body still reads TODO: release changelog / TODO: core update változtatások, pending on exactly this work.
  • Branch, all three repos: chore/FKITDEV-9197-cib-devel-update, off customization/cib.
  • Pushed 2026-08-12 as three PRs targeting customization/cib. Validated locally (Node v24.18.0), then in the product images on fk-dev (Node v24.12.0), then in CI — see §12–§14.
  • OUTCOME 2026-09-04 — the vuer-release cut cib@1 FAILED at the Harbor publish step (images built, login OK, docker push rejected). Delivery is BLOCKED pending a Harbor admin. Also open: the .102 changelog was never written in either repo. See vuer-release cut — cib@1 (2026-09-04).
  • OUTCOME 2026-09-03 — release cib-1.9.11.102 is TAGGED. vuer_oss #8126 + vuer_css #3152 squash-merged, portal_css #712 left open and out of the train. See §17 and the release hub cib-1.9.11.102. The squash means devel is no longer an ancestor of customization/cibsquash-merge-erases-partner-devel-ancestry.
  • Procedure followed: devel-update-and-release-flow. This round added Phase 1.4 check (e), and §15 corrected two more of its steps.
  • §16 is the broadest finding of the ticket and is NOT about CIB: three devel-wide test-reliability defects affecting every partner → vuer-oss-unit-tests-green-is-weak-evidence.
RepoWorktreeMerge commitParents (partner + devel)PR
vuer_ossvuer_oss/vuer_oss-FKITDEV-9197nested inside the repo, not a siblinge191f3f53f (amended from b0b987d463)690721c316 + 435e520ee8#8126
vuer_cssvuer_css-FKITDEV-9197680a6256cb043527c7 + cda6c80b9#3152
portal_cssportal_css-FKITDEV-9197b1a4bc94bf6dfbf8 + 56a63bd0#712

All solo-authored, no Co-Authored-By, real merge commits (devel → partner, never the reverse).

Portal versioning ≠ the vuer release train

The release is CIB 1.9.11.102, but portal_css’s own last CIB tag is cib-1.4.0.74 (2026-04-21). Do not look for a cib-1.9.11.* tag in that repo, and do not assume the vuer number applies. Phase 3 baseline-tag resolution must use the cib-1.4.0. prefix there and cib-1.9.11. in the vuer repos.


1. This is a pure core-update release

Nothing was committed directly to customization/cib since the last release. In all three repos the branch tip was the release commit:

RepoPre-merge tipTagDate
vuer_oss690721c316cib-1.9.11.1012026-05-29
vuer_cssb043527c7cib-1.9.11.1012026-05-29
portal_cssbf6dfbf8cib-1.4.0.742026-04-21

The only partner-requested ticket in scope — ASSCIB-161 = FKITDEV-8887 (iOS Safari WebRTC audio recovery) — was already on devel (6bdf66d16, vuer_css only) and arrives via the merge. Verified present post-merge with git merge-base --is-ancestor.

Same commit, second release

6bdf66d16 is the identical commit that is the headline item of Generali 1.9.11.19 (FKITDEV-9194 / ASSGRALI-63). A pure-core fix ships to several partners on whichever devel merge each one takes next — and, per Phase 3.3, the ticket-collection tool cannot see it in either release, because it never touched customization/.

Consequence for the TjK and the changelog: bucket 1 (“landed directly on the customization branch”) is empty. Everything shippable in .102 came from core.

2. Eleven months of drift — 334 commits, 212 tickets

Last devel sync: 2025-10-01 (vuer_oss / vuer_css), 2025-09-11 (portal_css).

Full inventory with authors: /Users/levander/coding/facekom/out/FKITDEV-9197-cib-1.9.11.102-tickets.md

Top contributors across the range — the people to contact for TjK section text (Phase 3.4):

AuthorTickets
Makkai Krisztián44
Balázs Horváth37
Szecsődi Imre16
Szekeres Tamás15

3. The dependency-removal trap — the main reusable lesson

This is the finding that generalises. Full write-up: devel-dependency-removal-breaks-partner-customization.

git merge-tree reported the relevant files as clean. devel had deleted request and request-promise-native from vuer_oss’s package.json — a tracked security action, not an accident: CIB’s own security/*.md files logged “Remove of request and request-promise-native scheduled” every month since 2025-03. CIB customization/ code still required them.

Merges clean · lints clean · dies at require-time on boot

Nothing between git merge and process start says a word. The two sides never touched the same line, so there is no conflict; the failure is a Cannot find module the first time the module graph is loaded.

The generalised check — and it cuts both ways

Diff the dependency sets between customization/<partner> and devel, then decide per package. There are three outcomes, and only one of them is “restore the line”:

SituationCorrect action
devel removed it deliberately (security / EOL retirement) and partner code imports itPort the partner code off it. Restoring the dependency re-introduces exactly what devel retired.
devel removed it as merely unused in core, partner code imports itRestore the version the partner declared pre-merge.
Partner-only dependency devel never had, still importedKEEP it. Naively “taking devel’s package.json” during conflict resolution silently deletes it.

The third row is the one that has no detector at all — it is a conflict resolution mistake, not a merge artefact.

Real instances this round

RepoPackagesOutcome
vuer_ossrequest, request-promise-nativedevel’s deliberate removal ⇒ ported to fetch (see §4, §5)
vuer_ossclamscan, soap, xml-formatter, short-uuid, uuid, zodpartner-only ⇒ KEPT
vuer_cssnode-fetch, passport, passport-oauth2partner-only, the CIB corporate-portal SSO path ⇒ KEPT
portal_cssmulter, uuiddevel’s 7a42894a chore(FKQA-304): remove unused libs (#675)restored

The quiet half is the worse half

In portal_css, post-merge require.resolve('multer') genuinely failedcustomization/api/document-upload.js would have thrown on load. That is the loud failure. uuid was worse: it resolved only transitively off devel’s hoisted copy (uuid@14.0.1 against a declared ^10.0.0), i.e. working by accident, at a major nobody chose, and would break with zero partner-side changes the day the hoisting dependency moves. require.resolve() succeeding is not a pass. eslint n/no-extraneous-require is the free detector — do not silence it.


4. Prior art existed, and it was buggy

Two earlier attempts by horvathbalazshbal sit abandoned and unmerged:

  • update/customization/cib-2025-11-12
  • update/customization/cib-2025-12-08 — tip 4df6666d4f “chore: core update afterworks”

They had already ported 4 files to fetch, which made them a genuinely useful template. They also contained two real bugs:

  1. Silently drops agentOptions — killing Infocert mutual TLS. InfocertRestAPI passes agentOptions: { pfx, passphrase } at 6 sites. A port that just deletes the option compiles, lints, and sends no client certificate.
  2. The multipart port cannot run at all. formData.getHeaders() does not exist on the web FormData, and fs.createReadStream cannot be appended to one.

Both were fixed rather than copied: fs.openAsBlob() for the file part, and fetch left to set its own boundary.

Mine abandoned branches, then verify them

An abandoned update branch is the cheapest source of “which files need touching” you will ever get. It is not a source of correct diffs — nobody reviewed it, and it was abandoned for a reason. Read it for the file list; re-derive the changes.


5. mTLS through native fetch needs undici

Node’s global fetch ignores agent. The supported route for client certificates is dispatcher + connect. This is the trap already catalogued in vuer-oss-global-fetch-ignores-agent-mtls (found while scoping FKITDEV-8947) — CIB is the round where it was actually implemented and proven.

  • undici ^6.28.0 declared as a new direct dependency of vuer_oss. It was already present transitively (via cheerio), so this is a declaration, not new code weight.
  • Flagged as reversible via node:https if the new direct dep is unwelcome — see open items.
  • Proven with a harness against a real client-certificate HTTPS server: 11/11 cases pass, including verified mTLS (clientCN: "client" observed server-side).

6. CI pipeline inheritance — third confirmed instance, and a security win

The customization-branch-ci-pipeline-inheritance trap fired on all three repos — after Cofidis (FKITDEV-9059, where it cost 5 failure clusters) and Generali (FKITDEV-9194, where it did not fire).

RepoPre-merge CI on customization/cib
vuer_osslegacy single job lint-and-build, workflow blob ec0a1244
vuer_csslegacy single job lint-and-build, workflow blob ec0a1244
portal_cssno pull-request.yaml at all

Blob ec0a1244 is byte-identical to the pre-merge Cofidis branch — the legacy workflow is genuinely one shared artefact frozen across partner branches, not a per-partner variant. The merge brings devel’s 6-job pipeline (lint / test / audit / depcheck / sonar / build) onto all three.

For portal_css the inheritance is a security win, not a tax

Proven against a base worktree at bf6dfbf8:

GateBase (pre-merge)Merged
eslintexit 1 — 15 problemsexit 0
audit25 CRITICAL0

CIB’s portal had been shipping 25 critical advisories unmeasuredtar via semantic-release > npm, handlebars, twig > locutus (×2), browserify > shell-quoteprecisely because the branch had no audit gate. The usual framing of this trap is “expect a wave of newly-enforced pre-existing breakage”. Here the same mechanism surfaced, and the merge simultaneously fixed, real vulnerabilities.

7. CIB is not an Oracle partner

No db.options override, no oracledb dependency ⇒ Postgres. Therefore the FKITDEV-8279 behavioural-swap risk (the @techteamer/sequelize fork’s Oracle reserved-word patch, load-bearing for bb / kh / mkb-instant under quoteIdentifiers: false) does not apply to CIB.

The audit gate is still red on the fork’s CRITICAL GHSA-v8fg-2rw7-q452, and build has needs: audit, so the CI build job will be blocked. This is pre-existing on every vuer_oss branch — say so explicitly in the PR body or it reads as merge damage.


8. Gate results (all Node v24.18.0, local)

vuer_oss

GateResult
lint0
build0
depcheckclean
yarn install --frozen-lockfileok
audit1 CRITICAL — sequelize fork, expected (FKITDEV-8279)
test:unit3546 passed / 3 failed — all proven pre-existing

Failure classification, against real controls (not assumed):

  • translations.test.js is CIB-only and fails identically on the pre-merge tip__infoText__ missing from translations.map.json, PortalData.trans.js:978.

    This measurement stands, but "passing" never meant "translations validated" — see §16.1 not evidence that the suite ever checked anything: translations.test.js registers zero tests for any partner (vuer-oss-unit-tests-green-is-weak-evidence). Green here means "the file loaded", never "the translations are valid".

    The suite genuinely did throw at import on CIB and not elsewhere, so the CIB-only classification above is correct. It is

  • converter, vuer-cv-service, pdf fail on clean devel too. (pdf is now known to be flaky, not deterministically red — §16.3.)

Import sweep: 1219 specifiers across 703 files, 0 unresolved.

vuer_css

GateResult
lint0 (independently re-verified)
buildok
yarn install --frozen-lockfileok
depcheckclean
audit0 critical
test:unit1066 passed / 2 failed — pre-existing

53 conflicts resolved. 722 relative imports + 302 twig include targets all resolve.

The devel tip merged here is the socket-token fix

vuer_css’s devel parent is cda6c80b9 — the commit that kebab-cased the data-socket-token twig attributes and fixed the FKITDEV-9199 regression upstream. CIB therefore takes the fixed state, not the broken window.

portal_css

All gates green — and that means less than it looks.

Green ≠ covered: portal_css has no test signal at all

test/tests/ holds only a 0-byte .gitkeep, and test.sequencer.js’s empty CORE_TEST_ORDER is a second, independent kill-switch. yarn jest finds zero tests and would still find zero if you wrote some. Full mechanism: portal-css-jest-runs-zero-tests.

multer is the proof that real breakage hides there — an unresolvable require in a live endpoint, with a green test job. On this repo only yarn lint and yarn build carry information.


9. Two methodology corrections for the recipe

Both of these produced confident wrong answers during this round, and both are now folded into devel-update-and-release-flow.

Do not symlink the merged node_modules into the base worktree — it contaminates the control

When the merge changed dependencies, a symlinked node_modules gives the base commit the merged dependency tree. vuer-cv-service duly failed in the base worktree with Cannot find module 'request-promise-native' — a failure caused entirely by the shortcut, proving nothing about the base commit. Give the control its own real yarn install.

Calling jest directly drops the flags the npm script supplies

Invoking jest directly to dodge RTK’s yarn rewriting silently loses whatever the package.json script adds. Omitting --experimental-vm-modules manufactured 4 phantom failures in vuer_oss. If you must bypass the script runner, copy the full flag list out of package.json first.


10. portal_css findings that are pre-existing on devel

Recorded here so a future round does not re-diagnose them or misclassify them as merge-caused. Neither was fixed on this branch.

10.1 devel’s eslint 9 migration kills partner eslint-disable directives

Full write-up: eslint9-flat-config-dead-disable-directives.

devel’s eslint.config.mjs dropped compat.extends('standard', 'plugin:n/recommended', 'plugin:jest-formatting/strict') for n.configs['flat/recommended'] + js.configs.recommended, turning off no-empty, no-unused-vars, no-redeclare, no-useless-assignment. Partner // eslint-disable-next-line <that rule> comments then report “Unused eslint-disable directive” — a warning — and yarn lint --max-warnings 0 promotes it to a failure. 9 dead directives across 6 CIB customization/ files had to be deleted.

eslint --fix does not delete them

It replaces each directive with a whitespace-only line. Delete the lines properly.

10.2 devel’s eslint.config.mjs has a dead rule reference

65ac214c feat: eslint 9 FKITDEV-6045 removed the eslint-plugin-jest-formatting dependency but left 'jest-formatting/padding-around-all': 'warn' in the config. Inert only because that block is scoped to files: ['test/*', 'test/**/*'] and yarn lint runs --ignore-pattern "test/*". Latent landmine the moment test linting is enabled — the portal_css twin of the vuer_css residue.


11. RTK output mangling — dangerous, and it nearly cost a wrong resolution

Beyond the known pipe-count zeroing (rtk-mangles-curl-and-pipes), this round hit five further modes, all producing plausible-looking wrong answers rather than errors:

CommandWhat RTK didConsequence
git show <ref>:<file> | grep …Garbled output so real matches read as absentNearly caused a wrong conflict-resolution decision
diff -uUnreadable line-offset view and a wrong exit codeCannot tell same-vs-different
git diff --no-indexRendered as if the whole file were newDiff is meaningless
git log -1 right after committing the mergeShowed devel’s tip 56a63bd0 instead of the new merge commit b1a4bc94You believe the commit didn’t happen, or happened wrong
${PIPESTATUS[0]}Blanked the exit codeEvery pipeline-based pass/fail gate is silently unverifiable

The git log -1 case is rtk-git-log-hides-merge-commits — the merge commit is filtered out, so -1 surfaces the next visible commit (here, the second parent).

rtk proxy <cmd>                    # raw, unfiltered — the reliable escape hatch
git grep <pattern> <ref> -- <path> # instead of `git show <ref>:<file> | grep`
shasum -a 256 <a> <b>              # same-vs-different, immune to diff rendering
python3 -c "import difflib; ..."   # generate the diff yourself

Never trust RTK-rendered git output for a verification claim

Anything about to be asserted as evidence — “the merge commit is X”, “the file does not contain Y”, “these two files are identical”, “the command exited 0” — must be re-verified with rtk proxy, git grep, or a hash. RTK failures here were silent and confident, not error-shaped.


12. Opening the PRs — the default title fails the ruleset

All three PRs were opened from the GitHub UI, which derives the default title from the branch name:

Chore/fkitdev 9197 cib devel update      ← GitHub's default
chore: [fkitdev-9197] merge devel into customization/cib   ← what was needed

The default fails both TechTeamer ruleset regexes at once — no type: prefix (it is Chore/, capitalised, with a slash) and no bracketed lowercase ticket. All three had to be edited after creation.

A PR opened from the GitHub UI on a chore/FKITDEV-… branch always needs its title fixed

The branch-name convention the rulesets require produces a default PR title the rulesets reject. This is not a one-off — it will happen on every partner devel-update PR. Make it a checklist item: open the PR, then immediately fix the title. See techteamer-commit-message-ruleset.

gh pr edit resolves the repo from the working directory

Always pass -R <org>/<repo> explicitly. In a multi-repo round with worktrees nested in odd places (vuer_oss’s is inside the repo), cwd inference is a coin flip.

gh pr edit <n> -R TechTeamer/vuer_css \
  --title 'chore: [fkitdev-9197] merge devel into customization/cib'

13. Phase 2.5 — validation inside the real product images

Executed on the fk-dev Tailscale VM (dev-build-host), in throwaway containers off the shipped images, source delivered with git archive and mounted. The running NÚSZ stack was never touched.

Imagesharbor.techteamer.com/facekom-devel/{vuer_oss,vuer_css,portal_css}:2026.1.UBI9.1-20220315
Node in imagev24.12.0
Gates runyarn install --frozen-lockfile, yarn lint, yarn build
Resultgreen in all three repos

The product image sets NODE_ENV=dev — this MASKS an entire class of failure

jest defaults to NODE_ENV=test. The image runs as dev. Any breakage that depends on which config file gets loaded is therefore invisible in-image and visible in CI — in-image results read greener than reality.

This is not hypothetical: portal-client.test.js passed in-image and failed in CI for exactly this reason (§14.2). In-image validation is a strong check on native build and lint; it is a weak check on anything config-loading-dependent.

fk-dev access — what actually works (tested 2026-08-12)

  • ops and root both work. levander, ubuntu, admin and facekom are refused — tailnet policy does not permit you to SSH as user "<u>". The canonical string stays command ssh ops@fk-dev.taild4189d.ts.net (dev-build-host).

  • Connect by name, not by IP. fk-dev and fk-dev.taild4189d.ts.net are in known_hosts; 100.91.108.61 is not, and the IP form fails with Host key verification failed.

  • ssh is aliased to _kaku_wrapped_ssh, not loaded non-interactively — use command ssh.

Correction — an earlier version of this note claimed root was the only permitted user That was an inference from four refusals (levander / ubuntu / admin / facekom), not an observation: ops was never tested. It was retested on 2026-08-12 and ops works. Kept here because the shape of the error is worth recognising — "these four failed, therefore only the one I used works" is a generalisation from a non-exhaustive sample, the same failure mode as §15's "read the actual error text".

14. CI outcome and the three fixes

PRResult
portal_css #7127/7 green on the first run. No fixes needed.
vuer_css #31526/6 green after two fixes (§14.1, §14.2).
vuer_oss #8126Unit Tests / Lint / Depcheck / SonarQube green after one fix (§14.3). Audit red, SonarCloud quality gate red, Build skipped.

Why a red test job costs you two more jobs

On the first vuer_css run, Build and SonarQube did not fail — they were skipped, because they needs: the test job. A single red gate hides the state of everything downstream of it, so “4 green, 1 red, 1 skipped” is not 5 known-good jobs. Re-read the gate graph before reporting a run.

The two vuer_oss reds are the known override cases — do NOT try to fix them on a partner branch

  • Audit — npm advisory 1114318, “Sequelize: SQL Injection (Oracle DB)”, via the @techteamer/sequelize fork. Pre-existing on every vuer_oss branch (FKITDEV-8279). build has needs: audit, so Build was skipped, not failed.
  • SonarCloud — the quality gate grades an eleven-month devel merge as “New Code”. 334 commits of core arrive as if freshly written. Note the split: the workflow’s own SonarQube job was green; it is the SonarCloud quality-gate status check that is red.

Both need an override/merge decision from a human, not a code change. Say so in the PR body or they read as merge damage — see customization-branch-ci-pipeline-inheritance.

14.1 f434bf9db (vuer_css) — a test that was right for six weeks, then stale for 20 months

test/tests/unit/sso-login-endpoint.test.js asserted 400; the middleware returns 401.

CommitDateWhat it did
b6ea3b7162024-10-18FKITDEV-4987 — created the guard, returning 400 {'Missing parameter'}. The test was correct.
c3256bc102024-12-03FKITDEV-5777 — swept all three error exits to 401 {INVALID_AUTH_CREDENTIALS}, created constants.js in the same commit to hold the enum, and renamed routes/sso/sso-login.jsroutes/external/corporate-portal-login.js. It updated only the test’s require line and left the assertions behind.

So the test was red from 2024-12-03 — and nobody knew, because customization/cib had no test job until this merge. The CI-inheritance trap (customization-branch-ci-pipeline-inheritance) did not create this failure; it revealed a 20-month-old one.

Fix: expectation → 401, plus a res.json body assertion (the original asserted only the status), plus de-duplication of two identically-named tests.

"Was this test ever right?" is the question that decides the fix side

If the test was never right, fix the test. If it was right and the code changed underneath it, you have to find out whether the code change was intentional. Here it plainly was — one commit changed all three exits and introduced a shared constant for them. That is what makes 401 the answer rather than “restore the 400”.

14.2 83e4adbd2 (vuer_css) — a module-scope require that only CIB has

portal-client.test.js failed at import with No config files found.

CIB’s PortalClient.js carries const config = require('../../../config') at line 1 — added for getPortalRedirectUrl. devel’s and Generali’s copies do not touch config at module scope at all. config/ holds only dev.json and docker.json, so under jest’s default NODE_ENV=test the loader finds nothing and throws before a single test runs.

Three tempting fixes, all rejected:

OptionWhy not
Fork the test file for CIBThe test blob is byte-identical on devel, Generali and CIB. Forking it buys a permanent merge conflict (the recurring-tax amplifier).
Add config/test.jsonA change to the deployed repo purely to satisfy a test runner.
Set NODE_ENV in jest.config-unit.jsThat file is currently identical to devel’s; diverging it has the same cost as forking the test.

Fix: move the require inside the method.

Why the lazy require is safe here — the argument, not just the conclusion

config.js is not side-effect-free: it bootstraps spring cloud config, mutates CORS defaults, and creates config.loaded at module scope. So “just require it later” is not automatically fine.

It is fine here because config is already required at boot by server/web/WebServer.js:13, server/web/routes.js:2, server/bootstrap/connection/rabbitmq.js:2, customization/server/service/CIBSSOService.js:2 and four customization/listeners/*. And the only caller of getPortalRedirectUrl is an HTTP route — customization/server/web/routes/device-change-redirect.endpoint.js:34 — which is necessarily post-boot. The moved require is therefore a require-cache hit on the same object, never a first load.

The residual risk is that this rests on an argument rather than an assertion — see open items.

14.3 acaa597d83 (vuer_oss) — the translations harness calls your callback with no arguments

translations.test.js failed to run at all. The mechanism: the test invokes any translation callback not registered in test/tests/unit/translations.map.json with zero arguments. CIB’s __infoText__ does property.split('.') and threw on undefined.

Fix: register the portal infoText / helpText / placeholder argument maps. No source change — this is a test-fixture gap, not a defect.

This closes the CIB-only pre-existing failure recorded in §8 (__infoText__ missing from translations.map.json, PortalData.trans.js:978).

…but it did NOT make the suite validate anything — see §16

The fix is real: it stopped the suite throwing at import, which is what made the job runnable. It did not buy any coverage, because translations.test.js registers zero tests for anyone (§16.1). Do not let “translations green” be read as “translations checked”.

15. False histories and failed experiments — the highest-value corrections of the round

Both of the corrections below produced confident wrong answers that were asserted to the user before being caught. A third, in §13, took the same shape — see the pattern at the end of this section.

git log -S does NOT follow renames

Running git log -S "status(400)" -- <post-rename path> returned nothing, from which I concluded — and stated — that 400 had never existed in that middleware. That was a false history: the string lived in the file under its old name (routes/sso/sso-login.js), and -S with a pathspec stops at the rename. An agent corrected it.

git log --follow -S '<string>' -- <path>     # follows the rename
git log -L <start>,<end>:<file>              # better: one line's ENTIRE history, across renames

This matters precisely in devel-update work, because “was this test ever right?” is what decides whether you fix the test or the code (§14.1). A false negative here sends you to fix the wrong side.

macOS cannot load vuer_css's config.js with NODE_ENV=dev unless DEV_DOMAIN is set

config.js:43 does fs.readFileSync('/etc/hostname') on the dev / travisci path. macOS has no /etc/hostnameENOENT.

The damage was not the ENOENT, it was the reasoning around it. An agent set NODE_ENV=dev to test the §14.2 hypothesis, saw the suite still fail, concluded “NODE_ENV is not the variable”, and misattributed a genuine CI failure to a “nested worktree artefact”. The hypothesis was right; the test of it was broken by an unrelated platform gap.

The general lesson: when a hypothesis test fails, read the actual error text. Do not generalise from a failure you have not identified. The distinguishing move here was running the same tree in the Linux product image on fk-dev, where /etc/hostname exists.

Three environments gave three different results for the same tree

macOS local, fk-dev in-image, and CI each disagreed — for structural reasons, not flakiness: macOS lacks /etc/hostname, the image pins NODE_ENV=dev, CI runs NODE_ENV=test.

Treat “it passes locally” and “it passes in-image” as weak evidence about CI. Neither is a substitute for a real run, and the in-image pass is the more seductive of the two because it looks like production.

The pattern — three corrections this round, one failure mode

Counting the SSH-user retraction in §13, three claims were asserted and then withdrawn in this ticket, and all three are the same error: generalising from an experiment whose failure was never identified.

EvidenceClaim assertedWhat the failure actually was
Four SSH users refused”only root works”ops was never tested
NODE_ENV=dev still failed”NODE_ENV isn’t the variable”it failed on a missing /etc/hostname — an unrelated cause, never read
git log -S returned empty”400 never existed”the pathspec didn’t follow the rename

Each was caught only because something independent contradicted it — a retest, a Linux container, a git log -L. None would have been caught by looking harder at the original evidence.

That is the operative point, because “be more careful” is the useless lesson here. The useful one: when a negative result is about to carry weight, change instrument. A null result tells you about your instrument at least as often as it tells you about the world.

16. Three devel-wide test-reliability defects — the round’s broadest finding

Full write-up: vuer-oss-unit-tests-green-is-weak-evidence.

These are not CIB findings. All three are on devel, so they affect every partner branch, and none were changed here — deliberately, because each belongs upstream.

A green Unit Tests job on vuer_oss is weaker evidence than it looks

One shared suite registers no tests at all; two others flake non-deterministically in CI. Anyone validating a devel update should know that before treating CI green as proof.

16.1 translations.test.js registers ZERO tests — the whole check is dead code

test/tests/unit/translations.test.js:181:

it.each(testSummary.entries(), (file, error) => { if (error) throw new Error(error) })

it.each(table) returns a function that must then be invoked with (name, fn). The callback is passed as each()’s second argument and the returned function is discarded, so no test is ever registered. The suite’s only registered tests are its it.skip(...) calls.

Verified in node directly, and from CI output on a passing run:

Test Suites: 2 skipped, 345 passed, 345 of 347 total
Tests:       147 skipped, 10 todo, 3515 passed, 3672 total

Every wrongKeys / missingLanguage finding the suite computes is therefore silently discarded, for every partner. A probe replicating the suite’s own logic found 42 findings on the CIB branch that CI cannot see — mostly flow_task_name / flow_task_instructions / flow_input_option returning undefined across customization/flow/cib-*.flow.trans.js.

Fixing it is a breaking change with a budget, not a cleanup

Repairing the it.each converts those 42 into hard failures on CIB alone, plus an unknown number per partner. It must be a deliberate upstream change — never a drive-by alongside a scan() fix.

This is what §14.3 did and did not buy. acaa597d83 stopped the suite throwing at import — real, and the reason the job runs at all. It bought no coverage, because the suite validates nothing for anyone.

16.2 The scan() relative-path bug is ACTIVELY FLAKY on devel

Line 28’s cwd is absolute (path.join(__dirname, '../../../')) and used for require and the map read — but includedDirectories entries go into scan() relative and hit fs.existsSync(startPath) at line 77. Suite loading depends on process.cwd() at module-evaluation time.

Firing in production CI, same commit, same nightly “Checks for Long-lived branches” run (cron * 20 * * *), customization/mbh, 2026-08-11:

RunTimeResult
3153181083220:13passed
3153321507020:29failedDirectory not found: client/features
3153662408021:10passed

Both runners produce both outcomes ⇒ not one bad machine. client/features is real and git-tracked (215 files). Also failed the NÚSZ devel-update PR (31485303651) while barion (31475164402) and generali (31489547911) passed. Does not reproduce on macOS.

devel bfd1311aab (sonar code smells, PR #8000) changed line 28 from process.cwd() to the __dirname form — fixing the require half only. The flake predates and survives it.

The cwd perturbation itself is UNPROVEN

No process.chdir in repo code. The only one reachable in the dependency tree is node_modules/cross-spawn/lib/util/resolveCommand.js:18, which chdirs before which.sync and restores in a finallya plausible leak window, not a demonstrated cause.

The obvious one-liner breaks the suite — silently

path.join(cwd, startPath) alone makes every returned translationFile absolute (because scan() builds results from startPath), and three downstream consumers key off relative paths: require(path.join(cwd, abs)) yields a doubled path, excludedFiles.includes(abs) goes false so exclusions stop working, and argMap[abs] is undefined so every arg map is lost. Underneath that: path.join concatenates; it does not resolve a leading / in a later segment — that is path.resolve.

Minimal correct fix, keeping the returns relative:

scan(path.join(cwd, startPath), ['.trans.js', 'translations.js']).map((f) => path.relative(cwd, f))

16.3 A second, independent flake — and this round’s green needed a re-run

server/util/pdf.test.js › printImage › place sample PNG image does a byte-for-byte PDF comparison and drifted by 3 bytes of stream length. It failed on the first CI run after the push, passes on the parent commit and 3/3 locally, and cannot be reached by the CIB change (translations.map.json is read by exactly one file, translations.test.js:73). Re-running the job with no content change went green.

Say it explicitly: vuer_oss's "Unit Tests green" in §14 was NOT first-attempt

With two known flakes in this suite, one red run is not evidence of a regression — and one green run is not evidence of its absence.


17. Outcome — merged and tagged 2026-09-03

Release 1.9.11.102 was cut. Full release hub, with all evidence: cib-1.9.11.102. Everything in this section was live-verified via the GitHub API on 2026-09-03.

17.1 Two of the three PRs merged; portal_css deliberately left out

RepoPRMerged (UTC)Outcome
vuer_oss#812613:10:49Zmerged (squash)
vuer_css#315212:55:36Zmerged (squash)
portal_css#712still OPEN, out of the release train

This resolves the §“Open items” question “Does portal_css ship in .102 at all?”: no. Per ASSCIB-166 the components are vuer_oss + vuer_css only. #712 stays open on its own schedule and its own version train (cib-1.4.0.*).

17.2 Tags

Both annotated, tagger Andras Lederer, message the bare version string 1.9.11.102\n:

RepoTagTarget
TechTeamer/vuer_osscib-1.9.11.10212a8a9e328221829ae6d383fd5e23eda9cf81a38
TechTeamer/vuer_csscib-1.9.11.102ca60fac34ac95b661336587b455924ab55e52def

Created through the GitHub API rather than a local push, on purpose — the vuer_css clone’s narrowed fetch refspec makes locally-created tags untrustworthy (narrowed-fetch-refspec-stale-devel-merge). This matches cib-1.9.11.101 exactly (annotated, 1.9.11.101\n, Szabó Márton, 2026-05-29). Note cib-1.9.11.100 was lightweight — the convention changed at .101, so do not pattern-match on .100.

17.3 Squash-merged — devel is no longer an ancestor of customization/cib

The most consequential fact of the cut

Neither PR produced a merge commit. Each result has exactly one parent:

  • vuer_oss 12a8a9e3 → parent 8abcc4c733
  • vuer_css ca60fac3 → parent b043527c72 (itself the cib-1.9.11.101 target for css)

So the 334 commits / ~11 months catalogued in §2 collapsed into one commit per repo, and devel is not reachable from customization/cib.

Consequence: the next devel → CIB update will not see these eleven months as merged. It will re-present them as new changes, with heavy conflicts against content that is already there. Generalised, with detection commands and three mitigations: squash-merge-erases-partner-devel-ancestry.

Content was verified safe before tagging. The squash commits’ trees are byte-identical to the reviewed PR head trees — vuer_oss df853239b1755be56983cd691b5f980905b81bca (12a8a9e3 == PR head 86f7dab01d), vuer_css 38079f89c7d524185a5c0b265f646c2c6798e634 (ca60fac3 == PR head 83e4adbd2c). The tagged bits are exactly what §8/§13/§14 validated. Only the history shape differs.

NOT verified

Whether earlier CIB releases used real merge commits rather than squashes was not checked. Do not assert that this is a departure from CIB’s prior practice until someone looks.

17.4 FKITDEV-9230 evidence can only be the spreadsheet

The CIB advanced report page /reports-advanced has no chart and no on-screen table — source-verified in vuer_oss customization/ui/pages/reports-advanced/. The template renders only the filter form; reports-advanced.script.js handleDownload() always sends download: 'true' and its only success path is window.location = /download/${res}. The xlsx export is the sole output.

ASSCIB-166’s phrase “a riport számai, a diagram adatai, valamint a XLSX export” lists three places in the computed data where the FKITDEV-9230 async-filter bug surfaced — all of which land in the workbooknot three UI surfaces. So test evidence for 9230 is a spreadsheet, never an application screenshot. Detail: cib-reports-advanced-is-export-only.

17.5 Still open after tagging

  • Docker image builds — from vuer_build main: ./build.sh -b cib-1.9.11.102 -i vuer_oss (and the vuer_css equivalent).
  • TjK incomplete — tester/verifier name fields blank, and a date mismatch: the document says 2026.08.13, the evidence is 2026.08.17.
  • portal_css #712 still open.

Open items for a human

Status 2026-09-03 — merged and tagged

The merge/tag item below is DONE for vuer_oss + vuer_css (§17); portal_css #712 remains open by design. The remaining bullets are still open unless noted.

Decisions and follow-ups this round deliberately did not make

  • Review + merge the three PRsdone 2026-09-03 for #8126 + #3152 (squash-merged, see §17). #712 intentionally left open. vuer_oss needed an override decision on the red audit + SonarCloud gates (§14).
  • getPortalRedirectUrl has no test coverage at all. The §14.2 change moves a require on a code path with zero assertions over it; its safety rests on the require-cache argument. A device-change-redirect smoke on fk-dev would close this properly.
  • The now-green SSO suite only exercises the dev-mock branch. mockSsoServer is true in dev.json and false in docker.json — so production CIB’s passport / OAuth2 + jwt.verify + processId path has no cover whatsoever. §14.1 made the suite green; it did not make it meaningful.
  • The 401-on-missing-credentials contract with CIB’s portal client is undocumented. Live since 2024-12-03 (c3256bc10), never written down anywhere, and nobody has confirmed the portal client handles 401 the way it handled 400.
  • Three upstream devel tickets are owed (§16) — the dead it.each, the scan() cwd flake, and the pdf.test.js byte-comparison flake. Deliberately not fixed on the partner branch. The it.each one needs its fallout budgeted first: 42 findings become failures on CIB alone.
  • Does portal_css ship in .102 at all?ANSWERED 2026-09-03: no. ASSCIB-166’s Komponensek field lists only vuer_oss + vuer_css, and only those two were merged and tagged. #712 is still open and out of the train (§17.1). The portal merge exists and is green either way.
  • undici as a new direct dependency of vuer_oss (§5). Reversible via node:https if rejected.
  • vuer_css branding call: page-focus-visible set to cib-green-700 over devel’s blue-primary. That is a site-wide focus ring and there is no CIB precedent for it. Separately, the videochat ScreenSign panel was left on devel’s generic palette.
  • The system-check browser-incompatibility guard is now gone. devel’s FKITDEV-6036 deleted it deliberately; CIB had re-acquired it through an earlier merge resolution. Post-merge, unsupported browsers render an error report instead of hard-failing. Behaviour change worth a changelog line.
  • CV v2 → v3 swap auto-merged ungated in self-service-v2.js, while CIB config says identificationLimits.compat: true (“CV 4.6”). Unreconciled.
  • REST ports are unit-proven, not endpoint-proven. Needs a staging smoke of Infocert process-start plus one CORPO call.
  • CIB’s TjK shape has never been verifiedpartners.json has priorShape: null, and ASSCIB-166 links a Google-Doc “TJK Sablon”. Resolve before generating (tesztjegyzokonyv-generation-flow).
  • Changelog goes in customization/RELEASE.MD.