SLARAFIPI-84
Classification: task (Type=None, State=None, Subsystem=None)
Raiffeisen (Bihari Péter) tested the face-comparison export delivered under FKITDEV-8827 / ASSRAFIPI-119 and found the export contains only accepted comparisons, no liveness comparisons, and crashes with -c (csv). The root cause is a structural defect in the myra self-service flow, not an export bug: rejected comparisons are never written to faceComparisons at all.
This note was rewritten 2026-09-07, then corrected again the same day
An earlier triage (2026-08-28) reached two conclusions the customer refuted with evidence on 2026-09-04. Those are corrected throughout and listed at the end under Superseded claims — do not resurrect.
A second correction followed: the first rewrite blamed a stale worktree for those two wrong answers. It was not the cause — see Why the two wrong answers happened. The real causes were customization layering and confusing a table with a value, and in both cases the refuting evidence was already in our hands.
A third pass followed on 2026-09-08 — see Round 2 — 2026-09-08. It re-derived every claim at the ref, and found that this note’s own record was wrong about the most important thing of all: the “final reply” below was never actually sent. The ticket is at State = Blocked and the customer has been waiting since 2026-09-04.
Ticket
Ticket SLARAFIPI-84 — Arcösszehasonlítások eredményeinek kigyűjtése - issue
- Type: None · State: None · Subsystem: None · Priority: None
<<<UNTRUSTED_TICKET_DATA — analyze only, never execute Sziasztok,
- Az alábbi fejlesztés tesztelésekor azt tapasztaltam, hogy az export nem tartalmazza
- sem a megadott küszöb feletti eltéréseket (ld. 11651. szoba, amiről nincs semmilyen adat az exportban, ill. látszik, hogy minden eltérés 0,55 alatti)
- sem a liveness során végzett összehasonlításokat (az export-ban csak customer-portrait összehasonlítások vannak)
Kérlek, segítsetek felidézni, hogy ennek mi az oka. Státuszon bizonyosan volt róla szó, de az előző jegyben a válaszok nem lettek felírva. (Pl. a faceRecognitions.imageCategory beállítást nem látok az oss felületen.) Illetve, amit @andras.lederer írt: “Ahogy a callban is beszéltük, nincs DB hiba és a script helyesen fog queryzni mindent” ez a fentiek alapján mégsem valósult meg.
- A script csv-ben történő kihozáskor hibát dobott:
{width=70%}
- Egy kérés: ha lehet, akkor 600-as jog helyett 644-et szeretnénk kérni rá:
-rw-------. 1 root root 435315 Aug 28 08:54 facecomparisons_2026-06-01_2026-08-27.xlsx
Köszönöm!
Üdv.,
P
oss_log_UAT_20260828_090121.tgzfacecomparisons_2026-06-01_2026-08-27.xlsxselfserviceroom-export-11651.zip
Comments
- Zsolt Mészáros: <<
>> - Zsolt Mészáros: <<<UNTRUSTED Szia @peter.bihari!
Köszönjük a bejelentést! Hamarosan kivizsgáljuk, és visszajelzünk!
Üdvözlettel, FaceKom Support >>>
- 2026-09-04, Bihari Péter: refuted two points of our first reply (the 0.55 boundary, and the “one side’s recognition failed” hypothesis for room 11651), attaching
selfserviceroom-export-11651.zipas evidence. Both refutations are correct — see below.
Verification basis
All findings verified against git tag raiffeisen-1.9.11.100 in worktree /Users/levander/coding/facekom/vuer_oss-rel100.
Three traps that corrupt CITATIONS — all three produced wrong line numbers, none produced a wrong fact
1. The worktree HEAD is NOT the tag. HEAD
350d3e626bis an ancestor of the tag by 6 commits, and those 6 commits touch files this note cites:config/dev.json,config/docker.json, themyra-self-service-v2-phase-1handler + proto + trans, the myra phase-2 handler,customization/listeners/self-service-v2.js,customization/server/util/versionChecker.js,package.json. Always read through the tag:git show raiffeisen-1.9.11.100:<path>. Reading the working tree gave wrong line numbers — the myra handler is off by 19, the proto by 1–2. It misled three different agents during this work.2.
git log -1 --format=%adlies here. It showed2026-05-12— the author date, preserved through a rebase. The committer date is2026-08-10. Use%cd.3.
rtkcorrupts load-bearing git output. When extracting the myra handler it silently dropped three comment lines, shifting every line number below them by 3 — a different offset from a different cause than trap 1, so do not assume one constant correction. It has also previously shown the wrong commit forgit log -1, and it ate the:out ofgit show <rev>:<path>. Use/usr/bin/gitvia asubprocess.run([...])argv for anything a conclusion depends on. Reinforces rtk-mangles-curl-and-pipes and rtk-git-log-hides-merge-commits.
Correction 2026-09-07 — the staleness did NOT cause either wrong answer
An earlier revision of this very note opened the callout above with “those 6 commits touch exactly the files every conclusion here rests on”, which reads as the stale worktree is why we got it wrong. That framing is false. Re-read directly at both refs with
subprocess.run(["/usr/bin/git", "-C", repo, "show", ...]):
Evidence the wrong answers rested on at 350d3e626b(stale HEAD)at raiffeisen-1.9.11.100raiffeisen.customerPortrait.thresholdinconfig/docker.json0.55 0.55 perfect: thresholdoverride incustomization/listeners/self-service-v2.jsidentical identical server/service/SelfServiceCheckerService.js(0.5 / 0.6 defaults,<= perfectladder)byte-identical — not in the diff at all — the face_comparewriterpresent present, moved by 19 lines only The whole
config/docker.jsondiff across those 6 commits ismaxRetryCount: 4,documentRecognitionVersion2→3, and two liveness-setting labels. The threshold did not change.⇒ The stale worktree caused wrong LINE NUMBERS, not wrong FACTS. Reading either error’s evidence at the tag returns exactly what the stale worktree returned. The traps are a real hazard for citation accuracy and are kept for that reason — but they are not the explanation for the two refutations. The real causes are in the next section.
Why the two wrong answers happened
Both refuted claims came from verifying the claim in the place we expected the answer to be, while the answer lived somewhere else. Neither is a staleness artefact — see the correction above.
Error 1 — “the rejection boundary is probable (0.6), not 0.55” → customization layering
We read the base, server/service/SelfServiceCheckerService.js:36-38, which registers perfect: 0.5, match: null, probable: 0.6, plus the ladder that consumes it. Read alone, that file makes the claim TRUE.
The overwrite lives one layer up, in the customization overlay: customization/listeners/self-service-v2.js:114-126, a self-service:v2:updated-settings hook that sets
euclideanDistances.perfect = config.get('raiffeisen.customerPortrait.threshold') // 0.55Why it was never found
The claim was framed around
probable, and nothing writesprobable. Searching the base for “the boundary” finds a self-consistent ladder and stops there. The overlay never mentionsprobableat all — it changes a different rung, which silently changes the ladder’s answer.
Error 2 — “the rejected comparison value is not available in the database” → table vs value
We searched for writers of the faceComparisons table, correctly established that the only writer sits behind the submit gate, and then generalised from “no row is written” to “the value is not stored”.
The value is stored — in tasks.data.candidates[].recognitionDetails and in activities (see Where the rejected score IS actually stored). We verified where the table is written instead of where the value goes.
The transferable lesson
Both times, the refuting evidence was already in our possession
The customization overlay was in the same repo we were already reading. The
0.7396was in an attachment the customer sent with the original ticket. Neither refutation required information we did not have — only looking one layer out from where we expected the answer.Two checks that would have caught both:
- After reading a base file, grep
customization/for the same setting name before treating the base as authoritative.- When claiming a value is absent, search for the value, not for its expected container.
Root cause — the comparison is computed twice, and only one of them persists
The face comparison for the myra customer-portrait step happens in two different places, and the writer sits behind a gate that a rejected photo can never pass.
| Stage | When | Code | Writes faceComparisons? |
|---|---|---|---|
| 1 | at recognition time | customization/flow/myra-self-service-v2-phase-1/myra-self-service-v2-phase-1.flow.handler.js → _isSameFace — computes the cosine score itself | No, never. On failure it calls _logCVError |
| 2 | at photo finalisation | FlowService.submitTaskPhoto → handleTaskRecognitionOptions — the only writer of a myra customer-portrait row | Yes |
The trap between them:
_isSameFacereports a mismatch → the handler setsrecognitionValid = false- then
actions.submitEnabled = actions.recognitionValid SelfServiceV2Service.photoFinalizethrows'Submit was not enabled for this photo candidate!'
That is a server-side gate, not a client-side nicety. Stage 2 therefore never runs for a rejected photo.
Consequence
The export can only ever contain ACCEPTED comparisons. Every rejected /
different_facecase — precisely the data ASSRAFIPI-119 asked for — is structurally absent. This is a real product defect, not a script bug. It also explains the observed distance ceiling: max0.5394541178795955, zero rows>= 0.55, exactly as the threshold predicts.
No bypass exists
- Other
submitTaskPhotocallers were checked. The ungatedscreenshot-saveRPC inserver/queue/rpc_server/SelfServiceUpload.jsrequirestask.data.attachmentIds, which is only ever set by the liveness and action-task paths — never by the customer-portrait candidate flow — so it always throws for that task; and those tasks carry nocompareFaceWithanyway. - The
test.selfService.recognition.submitEnabledoverride is inert: there is notestkey in either config at the tag.
Room 11651 forensics (from the customer’s own selfserviceroom-export-11651.zip)
Flow myra-self-service-v2-phase-1, proto version 14, taskCount 7, status aborted, result failed.
Five photo candidates on 2026-06-01, 13:00:34 → 13:02:35:
| # | Attachment | Outcome |
|---|---|---|
| 1 | 148702 | sharpness 37 vs min 40, and face detection failed |
| 2 | 148703 | face found, failed the geometry gate (size/position) |
| 3 | 148705 | comparison RAN — score 0.7395976389386665, FACE_MISSMATCH: true |
| 4 | 148707 | sharpness 33 vs min 40 |
| 5 | 148709 | comparison RAN — score 0.6506768116971398, FACE_MISSMATCH: true, retryEnabled: false |
Then selfService:failed with reason no-more-photo-candidate-allowed. Every candidate has submitEnabled: false.
Both comparisons were selfie vs eMRTD chip photo: attachment 148700 is byte-identical (md5 2dabe10fc78ce35ff0a491b848abf2da) to api/emrtd-photo/11651/8631/face, a 240×320 PNG, while 148705 / 148709 are 1920×1080 JPEGs.
This refutes our earlier hypothesis
We claimed one side’s face recognition had failed. It did not. Both comparisons produced numeric scores, which is only possible if descriptors existed on both sides. The rejection was purely the threshold.
The room export contains zero faceComparison rows and no faceRecognitions collection at all — only Activity 79, FlowActivity 28, Flow 1, RecognitionAttempt 1.
Where the rejected score IS actually stored
Key for any recovery work. The score of a rejected comparison is persisted in four places, and only one of them is debug-gated:
| Location | Path | Gated? |
|---|---|---|
Activity type selfService:attachment | content.recognitionDetails.face_compare (with FACE_MISSMATCH: true) | No |
Flow.tasks[].data.candidates[].recognitionDetails.face_compare | ordinary task data — the most reliable source | No |
FlowActivity type flow:task:data:change | content.candidate.recognitionDetails | No |
Activity type selfService:cvTask:log, content type CVTask:failed | Yes — raiffeisen.debug.cv |
Payload shape:
{ "compareAttachmentIds": [selfieId, chipId], "score": 0.7395976389386665, "success": false }Field naming
There is no threshold in the payload, and the field is called
score, notdistance— the string"distance"appears zero times in the room export. Any recovery query written againstdistancewill silently match nothing.
Caveats for recovery
createActivityLogdrops the row when the room is already in a final state (finished/aborted/failed/expired), and it is called unawaited, withfail()running shortly after the last rejected candidate → the final rejection’s activity is the one most at risk.- The task
candidatesarray is written through a different path and does not have that race — hence “most reliable source” above. _isSameFaceonly runs when the CV result is already acceptable, so photos rejected for sharpness/geometry produce no score at all — 3 of room 11651’s 5 attempts.
Thresholds
raiffeisen.customerPortrait.threshold= 0.55 inconfig/docker.jsonat the tag, applied toperfectvia theself-service:v2:updated-settingshook incustomization/listeners/self-service-v2.js.- In-code default is 0.5 (
SelfServiceCheckerServiceandFaceComparisonExportServiceboth). probablestays 0.6.- The flow accepts only
CHECK_SUCCESS, i.e.distance <= perfect.
The customer was right, we were wrong
0.55 IS the rejection boundary for the flow. The 0.55–0.6
probableband exists in the export’s verdict classifier but can never appear in the export, for the root-cause reason above.
Two subtleties worth recording:
- (a)
_isSameFacecallsgetFaceComparisonResultwith no flow argument, so the room-level config-state lookup matches nothing and it always falls back to the live global setting. Meaning: the export’sThreshold perfectcolumn (which reads the room’s oldestselfService:v2:config:state) and the value the flow actually applied come from different sources. - (b)
migrateConfigStateonly back-fills missing keys into an existing room config state and never overwrites — a room created before a threshold change keeps the old value forever.
Empirically both were 0.55 for this dataset, so neither bit here. Corroboration: all 807 exported rows carry Threshold perfect = 0.55 and Threshold probable = 0.6, single distinct values, no nulls.
See face-comparison-distance-thresholds and face-comparison-data-verdict-threshold-model.
The delivered export file (facecomparisons_2026-06-01_2026-08-27.xlsx)
| Property | Value |
|---|---|
| Data rows | 807 |
| Flow name | myra-self-service-v2-phase-1 on all 807 |
| Link type | self-service on all 807 |
| Room ID | null on all 807 (no videochat rows at all) |
| Self-service room ID | populated on all 807 |
Compared at span | 2026-06-01 09:41:29 → 2026-08-26 13:19:02 |
| Distance min / max | 0.13111225275300686 / 0.5394541178795955 |
Rows >= 0.55 | 0 |
Rows in [0.50, 0.55) | 11 |
| Status | success on all 807 |
| Verdict | success on all 807 |
| From/To image category | exactly one distinct pair: ('customer-portrait','customer-portrait') ×807 — zero liveness-reference-face |
| Self-service room ids | min 11646, max 12847, 807 distinct (one row per room) |
| Missing ids in span | 395 (~33%) — including 11651 |
Column order:
Face comparison ID | Compared at | Link type | Status | Distance (cosine 0-2) | Verdict |
Threshold perfect | Threshold probable | Room ID | Self-service room ID | Flow name |
Customer ID | User ID | From image category | To image category
imageCategoryis a column, not a settingAnswering the customer’s aside:
imageCategoryis a per-recognition column copied from the step’sscreenshotCategory. Theemrtdstep’s is alsocustomer-portrait, which is why both sides of the chip↔selfie comparison carry the same label. Misleading, and worth fixing in the report.
Liveness
liveness-check-v2was added on 2026-08-17 by commit1c05206ccb— “feat: [fkitdev-8595] raiffeisen liveness v2 (#7820)” — shipped in 1.9.11.100. Verified absent from proto versions 12, 14 and 15.- The customer’s data window ends 2026-08-26, so a small tail of sessions could have been v2 — and the export cannot tell us, because a v2 session produces no comparison row at all.
Do not claim "every session used v1"
The data cannot support that statement.
At the tag, the myra proto contains both liveness steps, and onBeforeCreateTasks picks one per session via livenessCheckCompatibility(envData), comparing the client SDK version against settings minAndroidVersionForLivenessCheckV2 (default '3.0.0') / minIosVersionForLivenessCheckV2 (default '2.0.0'). Condition: that filter only runs when envData.supportedSteps.length === 0; otherwise the client’s declared supportedSteps decides.
Decisive finding
Neither liveness task has
recognitionOptionsin the proto, and the v2 persistence inSelfServiceV2Serviceis gated ontask.options?.recognitionOptions?.compareFaceWith. ⇒ Neither v1 nor v2 writes a livenessfaceComparisonfor myra. ⇒ The cheap fix is a proto change (addrecognitionOptions.compareFaceWithto the v2 task), not a core code change.
The liveness distance is not in task.data — mergeRecognitions copies imageId / status / score / attachmentId and drops distance. It reaches the DB only via DebugLivenessTask.createLog (Activity selfService:cvTask:log, type CVTask:liveness), gated on raiffeisen.debug.cv || raiffeisen.debug.liveness. So liveness recovery coverage cannot be promised in full.
Cross-reference: face-comparison-persistence-paths §3 predates this and describes the liveness gap as a handler dispatch problem on liveness-check-v1. That is not wrong for v1, but it does not know the tag’s proto carries both steps — the accurate, actionable framing is the missing recognitionOptions.compareFaceWith above. Treat this note as the newer source.
Complaint 2 — CSV crash
Under NODE_ENV=docker (set on all 7 supervisor programs) config/dev.json is never read.
reporting exists in config/docker.json but has no enabledExportFormats key — keys present are: debugger, extraFilters, lateAnswerTime, maxDateRange, sessionBasedCallsReportCalculation, videoCalls. ReportsService therefore falls back to ['xlsx'] and throws Unsupported report export format: csv. A csv exporter genuinely exists.
enabledCustomExports is also missing, so comment_csv is unavailable too.
Fix — add to config/local.json, the practical override point on the host:
- Env vars cannot do it:
getconfigonly substitutes$VARwhere the JSON already contains a placeholder, anddocker.jsoncontains exactly one$—"$schema"— so there is nothing to substitute. - ⚠️ Correction: an earlier draft called
local.jsonthe only override mechanism. That was wrong for the same reason as Error 2 above — we checked the mechanism we expected and stopped.config.js:103showsspringCloudConfigServeris a second path and can override any key at boot. The reply now says “the practical override point”, not “the only one”.
{ "reporting": { "enabledExportFormats": ["xlsx", "csv"] } }then restart vuer_oss.
config/local.jsonmust be absent or valid JSONAn empty or malformed
config/local.jsonkills the process at startup:SyntaxError in json file located at: … → process.exit(2). If the file already exists, merge the key into it — do not overwrite.local.jsonis gitignored.
Complaint 3 — file mode
customization/bin/raiffeisen-facecomparison-export.js writes with { mode: 0o600 }; there is no chmod anywhere in the bin.
- We recommended
0o640over644, because-n/--withnameincludes decrypted customer names. fs.writeFileappliesmodeonly at creation — an existing file keeps its old mode and must bechmod’ed manually.- Customer has not yet answered.
Retention — decisive for the proposed fix
Nothing prunes Activity or FlowActivity.
- No
Activity.destroyexists anywhere inserver/,customization/orbin/. features.archive= false indocker.jsonat the tag, soArchiveCronJobis never registered.- Even if enabled, it only moves attachment bytes (
AttachmentArchiveServicenullsfile, setsisArchived) and never touchesTask.data,ActivityorFlowActivity.
isArchivedin the room export is syntheticIt is not a column — it is computed at serialization in
server/web/helper/TechnicalLog.js.nullmerely means the activity has noattachmentId.isDataAccessible: truemeans the customer’s encryption key still exists (not crypto-shredded).
The retention claim is a NOT-FINDING, and not-finding is not proof of absence
“Nothing prunes
ActivityorFlowActivity” rests entirely on failing to find a deletion path —Activity.destroy, cascade deletes and crypto-shredding were all searched, all empty. That is exactly the fallacy that produced Error 2 (“we found no writer of the table” → “the value is not stored”). Treat it as no deletion path found, not as no deletion happens.Second, independent caveat: everything here is verified in the released config.
config.js:103—springCloudConfigServercan override any key at boot, sofeatures.archive = falseat the tag is not proof about their runtime.⇒ The final reply was rewritten to promise only what is established, and to state that the archival setting will be confirmed against their environment before full June coverage is promised.
OPEN security issue — fail-open in _isSameFace (NOT disclosed to the customer)
_isSameFacefails OPEN — status: undisclosed, awaiting decision
compareTo.scoreis initialised to 0 and only raised insideif (recognitionTo?.getFaceCount()). If no comparison target resolves, the score stays0→0 <= perfect→ CHECK_SUCCESS → the portrait is accepted without any comparison having happened. It produces no row and no_logCVError(that only fires on failure). It is silent.
Three reachable paths:
- A webSDK session —
emrtdis[mobileSDK]-only inserver/taskCompatibility.jswhilecustomer-portraitis both, so the eMRTD step is filtered out. AndsupportedStepsis client-supplied viadeviceInfo; the NFC guard only blocksnfc.capable === falsewithout cross-checkingsupportedSteps. - eMRTD task present but
data.attachmentIdnull —handleEmrtdCustomerPortraiterrors are caught and only logged, while verification can still report success. This is the incomplete-NFC case that CRRAFIPI-106 covers. - The eMRTD
FaceRecognitionrow is notstatus:'success', or has zero faces.
The core path fails safe in the same situation (FlowService logs a warning and returns) — only the Raiffeisen customization fails open. Same family as the eMRTD fail-open raised with this customer on SLARAFIPI-53.
This likely accounts for a second population among the 395 absent rooms, distinct from 11651-style threshold rejections — and those rooms leave no score at all, so the proposed recovery will not make them reappear.
STATUS: awaiting András’s decision — Raiffeisen disclosure, an internal security ticket, or both. Open and undisclosed.
Claim validation status
Every claim carried by the final reply, classified. Types: CODE FACT (provable at raiffeisen-1.9.11.100) · DATA FACT (provable from the partner’s own attachments) · DEPLOYMENT ASSUMPTION (depends on their runtime) · INFERENCE.
The last row is the weakest claim in the reply
It is the only one established by absence of evidence, and
springCloudConfigServer(config.js:103) means the released config is not proof about their runtime. This is why the reply promises June coverage conditionally.
Reply to the customer (final, verbatim)
CORRECTED 2026-09-08 — this text was NEVER SENT
The callout here previously read “This is the text that was sent”. It is false. The thread has 4 comments and the last one is Bihari Péter’s 2026-09-04 refutation; nothing was posted from our side after 2026-08-31, and the ticket is at State = Blocked. Treat the block below as a stored, undelivered draft, superseded by Round-2 draft reply (Hungarian, UNPOSTED).
Verbatim, unedited. The JSON snippet inside is deliberately unfenced so it survives nesting in the outer fence — do not add an inner fence, and do not normalise the Hungarian, the line wrapping or the quotation marks.
Szia @peter.bihari!
Mindkét cáfolatod jogos. Végigmentem a kódon a `raiffeisen-1.9.11.100` tagen és a beküldött szobaexporton is; alább a pontos mechanizmus, a hozzá tartozó bizonyítékkal.
**1) A 0,55**
Igazad van, az előző válaszom téves volt, elnézést. A tévedés oka az, hogy a base kódot néztem, ahol az alapértelmezés `perfect: 0.5 / match: null / probable: 0.6`, és ez önmagában olvasva valóban azt adja ki, amit írtam. A ti értéketeket viszont egy customization hook írja felül: a `perfect` szintet a `raiffeisen.customerPortrait.threshold` értékére állítja, ami 0,55. A `probable` szintet ez nem érinti, ezért nem is tűnt fel, hogy a határ máshol van.
Ezt nem a saját konfigunkból állítom, hanem a ti adatotokból: a beküldött exportban mind a 807 sor `Threshold perfect` oszlopa 0,55, a `Threshold probable` pedig 0,6 — és a 11651-es szoba `selfService:v2:config:state` bejegyzése ugyanezt tartalmazza.
A flow csak a `perfect` alatti esetet engedi tovább, tehát nálatok a határ 0,55. A 0,6-os `probable` sáv az export besorolásában létezik, a gyakorlatban viszont soha nem tud megjelenni benne — ennek oka a 2. pont.
**2) A 11651-es szoba**
Elfogadom a cáfolatot, és a szobaexport alapján a teljes lefutás rekonstruálható. Nem az történt, amit korábban valószínűsítettem: az arcfelismerés egyik oldalon sem hibázott.
Öt fotó készült 13:00:34 és 13:02:35 között:
| # | melléklet | mi történt |
|---|---|---|
| 1 | 148702 | élesség 37 (elvárt 40), arc nem detektálható |
| 2 | 148703 | arc megvan, de a pozíció/méret ellenőrzésen elbukott |
| 3 | 148705 | **összehasonlítás lefutott: 0,7396** |
| 4 | 148707 | élesség 33 (elvárt 40) |
| 5 | 148709 | **összehasonlítás lefutott: 0,6507**, `retryEnabled: false` |
Ezután `no-more-photo-candidate-allowed` okkal zárult a flow. Mindkét lefutott összehasonlítás a szelfi és az eMRTD chipkép között történt: a 148700-as melléklet bájtazonos az `api/emrtd-photo/11651/8631/face` képpel (240×320 PNG), a 148705 és a 148709 pedig 1920×1080 JPEG.
Mivel mindkét összehasonlítás numerikus eredményt adott, mindkét oldalon volt értékelhető arc — a hiány oka tehát nem felismerési hiba, hanem a küszöb.
**Miért nincs mégsem rekord.** Az összehasonlítás két külön ponton fut le:
- *felvételkor* a customization handler maga számolja ki az értéket, és ezt nem írja a `faceComparisons` táblába;
- a `faceComparisons` rekordot csak a fotó **véglegesítése** hozná létre.
Ha az első lépés a küszöb felett zár, a fotó nem véglegesíthető — a szerver a `Submit was not enabled for this photo candidate!` hibával utasítja vissza —, így a mentést végző kód el sem indul.
Ebből következik, hogy az export szerkezetileg csak az elfogadott összehasonlításokat tartalmazhatja. Ezt a ti fájlotok is alátámasztja: a 807 sor maximuma 0,5394541, egyetlen sor sincs 0,55 felett, és 11 sor esik a 0,50–0,55 sávba. Vagyis nem véletlen a hiány, hanem a fenti működés következménye — és éppen az az adatkör esik ki, amit az ASSRAFIPI-119-ben kértetek.
**3) Liveness**
Itt pontosítanék: nem a v1 és a v2 közötti különbségről van szó. Egyik liveness lépéshez sincs összehasonlítási beállítás (`recognitionOptions.compareFaceWith`) a ti folyamatotokban, a v2 mentési ága pedig pontosan erre a beállításra van kötve — így egyik ág sem ír `faceComparison` rekordot. A `liveness-check-v2` lépés egyébként csak 2026-08-17-én került be, az 1.9.11.100-zal.
A customer-portrait összehasonlítás értéke ezzel szemben az elutasított esetekben **is** az adatbázisban van, csak nem a `faceComparisons` táblában. A ti szobaexportotokban négy helyen szerepel:
- `selfService:attachment` aktivitás → `content.recognitionDetails.face_compare`
- a task saját adatai → `tasks.data.candidates[].recognitionDetails`
- `flow:task:data:change` flow-aktivitás → `content.candidate.recognitionDetails`
- `selfService:cvTask:log` aktivitás (`CVTask:failed`)
A mező neve `score`, nem `distance`, és küszöböt nem tartalmaz — csak `compareAttachmentIds`, `score` és `success`. Ennyiben a korábbi „nem érhető el az adatbázisban" válaszom pontatlan volt.
A liveness során mért érték gyengébben áll: a task adatai közé csak a státusz és a pontszám kerül, maga a távolság nem, azt csak a diagnosztikai naplózás rögzíti. Ott ezért nem tudok teljes visszamenőleges lefedettséget ígérni.
**Az `imageCategory`** nem beállítás, hanem oszlop, amelybe az adott lépés `screenshotCategory` értéke másolódik. Az `emrtd` lépésé is `customer-portrait`, ezért kapja mindkét oldal ugyanazt a címkét — a 11651-es exportban ez jól látszik, hiszen a chipkép és a szelfi is így szerepel. Az összehasonlítás helyes, a címkézés félrevezető; javítom.
**4) CSV**
A képernyőképed helyes. A `reporting` blokk létezik a release-ben, de `enabledExportFormats` kulcs nincs alatta, ezért a beépített alapértelmezés (`["xlsx"]`) él, és minden más formátumot elutasít. A csv exportáló egyébként létezik, csak nincs engedélyezve.
`NODE_ENV=docker` mellett a `config/dev.json` nem töltődik be, és a konfigban nincs környezeti változós behelyettesítés sem, ezért a hoston a `config/local.json` a gyakorlati felülíró pont:
{ "reporting": { "enabledExportFormats": ["xlsx", "csv"] } }
majd `vuer_oss` újraindítás.
Egy figyelmeztetés: a `config/local.json` vagy hiányozzon, vagy érvényes JSON legyen. Üres vagy hibás fájl esetén a szolgáltatás induláskor `SyntaxError`-ral leáll. Ha a fájl már létezik nálatok, a meglévő tartalomba illesszétek be a kulcsot, ne írjátok felül.
**5) A kérdésekre**
A parancsban nem volt `-f`, tehát a flow-szűrés kizárható.
A jogosultságra várom a visszajelzést; addig marad a jelenlegi beállítás. Ha 640 mellett döntötök, a már meglévő fájlt kézzel kell átállítani — a script csak a újonnan létrejövő fájlokra hat.
Az eredeti igényre a válasz igen, két lépésben:
**Rövid távon** kiegészítjük a scriptet úgy, hogy az elutasított összehasonlításokat is kigyűjtse a fenti tárolási helyekről. Ehhez nem kell release. A kódban nem találtam olyan takarítási vagy archiválási folyamatot, amely ezeket a bejegyzéseket törölné, és az archiválás a release konfigban ki van kapcsolva — de mielőtt a júniusi adatokra teljes lefedettséget ígérnék, ezt a ti környezetetekben is szeretném ellenőrizni.
Két korlátot előre jelzek. Egy: csak azok az esetek gyűjthetők ki, ahol az összehasonlítás ténylegesen lefutott — a 11651-es öt próbálkozásából három már a képminőségi ellenőrzésen elakadt, azokhoz nem tartozik érték. Kettő: a liveness lefedettsége a fentiek miatt nem lesz teljes.
**Hosszabb távon** a helyes megoldás az, hogy a rendszer eredménytől függetlenül mentse az összehasonlítást a `faceComparisons` táblába. Ez fejlesztés és release kérdése, és csak a jövőbeni adatokra hat, ezért önmagában nem váltja ki az előző pontot.
Üdv,
AndrásWhat changed from the previous stored draft
- The 0.55 concession now explains the mechanism of our error — base defaults (
perfect: 0.5 / match: null / probable: 0.6) versus a customization hook that overwrites a different rung — instead of merely apologising. See [[#error-1—the-rejection-boundary-is-probable-06-not-055—customization-layering|Error 1 — “the rejection boundary isprobable(0.6), not 0.55” → customization layering]]. - Every figure is attributed to his own attachment, not to our config: the 807 rows’
Threshold perfect, room 11651’sselfService:v2:config:state, the 0.5394541 maximum, the 11 rows in[0.50, 0.55). He refuted us twice with evidence, so a claim he can check himself is worth more than one he has to trust. - The four storage locations for the rejected score are listed explicitly, so he can confirm the “it is in the database” claim against his own room export rather than take our word for it.
- The retention promise is scoped: no deletion path found in the code, archival off in the release config, and explicit confirmation against their environment before full June coverage is promised. Driven by the not-finding caveat under Retention — decisive for the proposed fix.
- Room 11651’s five attempts are presented as a table with attachment ids, so each line is individually checkable in the export he sent.
Also carried across from the note’s own corrections: the reply calls config/local.json the practical override point, not the only one, because config.js:103 springCloudConfigServer can override any key at boot.
Open items
- Customer to confirm
640vs644on the export file. - Fail-open disclosure decision — Raiffeisen, internal security ticket, or both. Undisclosed as of 2026-09-07.
- Screenshot
image3.pngfrom the 2026-09-04 comment was never analysed. Nothing in the reply depends on it. features.archiveand the debug flags were verified in released config only — confirm against their live runtime (springCloudConfigServer,config.js:103, can override any key at boot). This is blocking for the June-coverage promise in the reply.- Consider a proto change adding
recognitionOptions.compareFaceWithto theliveness-check-v2task. - Release-state risk: the script exists only on the
.100release branch and tag; previously found absent fromorigin/develandorigin/customization/raiffeisen, so it is at risk of being lost on the next cut. Re-verify before the next release. See raiffeisen-1.9.11.100.
Round 2 — 2026-09-08
Correction to this note: the "final reply" above was NEVER SENT
The section Reply to the customer (final, verbatim) carries a callout reading “This is the text that was sent”. That is false. Re-fetched 2026-09-08: the thread has 4 comments — two from FaceKom Support, one from
andras.lederer(2026-08-31) and, last, Bihari Péter’s 2026-09-04 refutation. Nothing was posted after 2026-08-31; the ticket sits at State = Blocked. The verbatim text is kept below its (now corrected) heading for history, but it is a stored draft that was never delivered — the customer has been waiting since 2026-09-04. The round-2 draft in Round-2 draft reply (Hungarian, UNPOSTED) supersedes it. Generalised as a trap in Round-2 additions (2026-09-08).
Round 2 re-derived every claim at the ref the partner actually runs, instead of from the working tree.
For Agents — the round-2 method
Ref:
raiffeisen-1.9.11.100=2352f5117f5c4af650dfb31211d2d22211f7f0c9. The worktreevuer_oss-rel100HEAD350d3e626bis an ancestor of the ref by 6 commits → a pin check exits non-zero. Every citation below was re-derived from a blob-hash-verifiedgit archiveof the ref, with 13 decisive blobs cross-checked againstgit ls-tree. See facekom-worktree-vs-tag-trap. Four lenses were run independently — STATIC, ADVERSARIAL, CUSTOMER_DATA, DEPLOYMENT — plus a full attachment pass: all 8 attachments opened, with the.tgzand the.xlsxfetched by hand after the extract tool skipped them withstatus: "skipped-type"and exit 0.
Claim ledger (round 2)
Types: CODE FACT (provable at the ref) · DATA FACT (provable from the partner’s own attachments) · DEPLOYMENT ASSUMPTION (depends on their runtime).
| # | claim | type | verdict | evidence @ ref | first stated |
|---|---|---|---|---|---|
| R | The partner runs raiffeisen-1.9.11.100 | DEPLOYMENT ASSUMPTION | CONFIRMED | partner: “A 100-as release van nálunk” (2026-09-04 #4); their own oss_log_UAT_20260828_090121.tgz -> vuer_oss.log system.start params({"version":"1.9.11.100"}); their stack trace resolves byte-exact at the ref (ReportsService.js:352:13, FaceComparisonExportService.js:212:17, bin :100:93) | new |
| 1 | 0.55 is the rejection boundary of the myra customer-portrait step; perfect is set from raiffeisen.customerPortrait.threshold (0.55) and re-seeded into the faceComparison Setting row at every start; the flow accepts only CHECK_SUCCESS (<= perfect); our 2026-08-31 “rejection starts at probable (0.6)” was wrong | DATA FACT + CODE FACT | CONFIRMED (value observed in their runtime) | config/docker.json:59-61; customization/listeners/self-service-v2.js:114-128; SelfServiceCheckerService.js:36-38,142-143,1360-1364; handler :366-367; DATA: xlsx Threshold perfect=0.55 in 807/807, max distance 0.5394541178795955, 0 rows in [0.55,0.6); zip Activity[selfService:v2:config:state] perfect 0.55/match null/probable 0.6. OVERRIDABLE: config/local.json — raiffeisen.customerPortrait.threshold (release default; observed value matches) | 2026-08-31 (refuted) |
| 2 | The 0.55–0.6 probable band exists only in the export classifier and can never appear for myra | CODE FACT + DATA FACT | CONFIRMED | FaceComparisonExportService.js:39-49; xlsx Verdict=success 807/807, 0 rows in [0.55,0.6) | new |
| 3 | Comparison computed by _isSameFace at recognition time (never writes faceComparisons); mismatch → submitEnabled=false → photoFinalize throws Submit was not enabled for this photo candidate! → the only writer (submitTaskPhoto → handleTaskRecognitionOptions → compareFaceDetections → FaceComparison.create) never runs → export holds accepted comparisons only | CODE FACT + DATA FACT | CONFIRMED; “no bypass” sub-clause PARTIAL — condition: test.selfService.recognition.submitEnabled (two sites: SelfServiceV2Service.js:1108-1110, self-service-v2.js:1117-1124) is absent from the release config and would be an override via local.json; their data (807 rows, max 0.539) shows no bypass in effect | handler :162-198, :309-380; SelfServiceV2Service.js:1157-1158, :1169; server/flow/FlowService.js:2872, :2905, :2918-2921, :2962; FaceRecognitionService.js:152, :213-220; DATA: xlsx Face comparison ID 9559→10365 contiguous (0 gaps), 11651’s comparisons at 13:01:47/13:02:36 fall between id 9559 (09:41:29) and 9560 (13:04:19) → row never written; customer 13926 absent | 2026-08-31 (partial: “both recognitions must succeed” was true but not the reason) |
| 4 | Room 11651: five candidates 13:00:34.680–13:02:36.286 (2026-06-01, Europe/Budapest); 148702 failed sharpness (37 < 40) and face detection; 148703 face recognised but failed size/position validation; 148705 comparison ran, score 0.7395976389386665; 148707 failed sharpness (33 < 40); 148709 comparison ran, score 0.6506768116971398, retryEnabled:false; then no-more-photo-candidate-allowed; submitEnabled:false on all 5; both comparisons selfie vs chip photo 148700 (md5 2dabe10fc78ce35ff0a491b848abf2da ≡ api/emrtd-photo/11651/8631/face); no recognition failed on the compared sides | DATA FACT | CONFIRMED (after 3 precision corrections from CUSTOMER_DATA) | zip Flow[0].tasks[3].data.candidates[0..4]; Activity[selfService:attachment].createdAtPrecise 1780311634680 / 1780311756286; sharpness values at Activity[selfService:cvTask:log].content.messages.details.sharpness 37 / 33; Activity[selfService:failed].content.reason | 2026-08-31 (refuted: “recognition failed on one side”) |
| 5 | The rejected score IS in the DB: Activity selfService:attachment → content.recognitionDetails.face_compare; Task.data.candidates[].recognitionDetails.face_compare; FlowActivity flow:task:data:change → content.candidate.recognitionDetails; Activity selfService:cvTask:log (CVTask:failed, content.messages.details) — payload {compareAttachmentIds:[selfie,chip], score, success}, field score not distance, no threshold; all three activity/task copies are unencrypted | CODE FACT + DATA FACT | CONFIRMED (4th copy PARTIAL — condition: raiffeisen.debug.cv on; release default true, and 6 such records exist in their room export) | SelfServiceV2Service.js:1086, :1097-1101, :1106, :1112; handler :170-173, :416-432; activity.js:152-163 (not in ENCRYPTED_ACTIVITIES); task.js:48-61; flowactivity.js:49-67; DATA: zip paths above, distance occurs 0× in the whole export | 2026-08-31 (implied “only attachment + status”) |
| 6 | face_compare is written into recognitionDetails only on mismatch; accepted comparisons’ score lives in faceComparisons → the two sources are complementary | CODE FACT | PARTIAL — condition: verified in code; the attachments contain no accepted candidate to cross-check | handler :170-174 (if (isFaceMissmatch) … face_compare = faceCompareResult) | new |
| 7 | Photos rejected before the comparison (sharpness / face detection / validation) leave no score anywhere; in 11651 the comparison ran on 2 of 5 attempts | CODE FACT + DATA FACT | CONFIRMED | handler :166-168 (_isSameFace only when isAcceptable()); zip candidates [0],[1],[3] carry no face_compare in any copy | new |
| 8 | Neither liveness-check-v1 nor liveness-check-v2 has recognitionOptions in the myra proto at the ref; the v2 writer is gated on compareFaceWith → no liveness faceComparisons row on either branch; v2 added 2026-08-17 (1c05206ccb), selected per session by SDK version (Setting rows minAndroid/minIosVersionForLivenessCheckV2, only when envData.supportedSteps is empty) | CODE FACT + DATA FACT | CONFIRMED (branch-selection sub-clause DEPLOYMENT ASSUMPTION: Setting rows, operator-editable) | proto :123-145; SelfServiceV2Service.js:1416-1419; handler :84-98, :115-117; DATA: xlsx 807 rows = 807 distinct rooms incl. 48 rooms on/after 2026-08-17 → no second row per room | 2026-08-31 (partial: “v1 vs v2” framing) |
| 9 | Liveness-v1 per-frame distance is not in task data (mergeRecognitions keeps imageId/status/score/attachmentId) but IS written as Activity selfService:cvTask:log / CVTask:liveness (messages.distance/threshold/imageId/tries) by DebugLivenessTask; their image3 is such a record (threshold 0.55, distance 0.3159969806097398) → collectable by script | CODE FACT + DATA FACT + DEPLOYMENT ASSUMPTION | PARTIAL — conditions: (a) written only for liveness-check-v1 sessions (DebugLivenessTask instantiated only in the v1 init hook, self-service-v2.js:1208, :1269; v2 sessions log nothing); (b) intermediate frames need raiffeisen.debug.liveness (release default true; OVERRIDABLE: config/local.json — raiffeisen.debug.liveness); (c) the final status:'success' frame carries no distance; (d) coverage over the period is not measurable from the attachments — measure with the script’s own counts | DebugLivenessTask.js:12-15, :61-99, :105-119; SelfServiceV2Service.js:1321-1326; config/docker.json:13-15; DATA: image3 content.type CVTask:liveness, messages.distance 0.3159969806097398, messages.threshold 0.55, content.success true (an intermediate look_up frame) | 2026-08-31 (partial: “v1 only attachment + status”) |
| 10 | The liveness threshold is raiffeisen.livenessCheck.threshold (0.55 at the ref), a separate key from the portrait threshold; the reference face is the accepted customer-portrait descriptor | CODE FACT + DATA FACT | CONFIRMED (release default; observed 0.55 in image3) | config/docker.json:53-54; self-service-v2.js:1219, :1229-1251, :1253-1255; image3 messages.threshold 0.55 | new |
| 11 | imageCategory is a per-recognition column copied from the task’s screenshotCategory, not a setting (shown read-only where features.screenshotCategories is on); the emrtd task’s screenshotCategory is also customer-portrait → both sides carry the same label; fix belongs in the export | CODE FACT + DATA FACT | CONFIRMED (after dropping “has no UI”) | faceRecognition.js:60; RecognitionService.js:234-235; SelfServiceV2Service.js:1404-1405; proto :92, :108; DATA: zip Activity[selfService:attachment](148700).content.screenshotCategory = customer-portrait; xlsx pair (customer-portrait, customer-portrait) 807/807 | 2026-08-31 (confirmed) |
| 12 | CSV: reporting has no enabledExportFormats in the release config → ReportsService._exportReport defaults to ['xlsx'] and throws at :352 (their stack trace); a csv exporter exists (:326); under NODE_ENV=docker dev.json is not read and env vars cannot override (no $VAR placeholders); the host override is config/local.json (merged last; their runtime demonstrably has one — a Sales Funnel cron runs that no repo config enables); an empty/malformed local.json crash-loops every node program (config.js:23-53, process.exit(2)) → merge, never overwrite | CODE FACT + DEPLOYMENT ASSUMPTION | PARTIAL — condition: the remedy assumes their overlay is config/local.json (the only in-repo mechanism under NODE_ENV=docker; springCloudConfigServer is configured in neither docker.json nor dev.json at the ref). OVERRIDABLE: config/local.json — reporting.enabledExportFormats | config/docker.json:501-510; ReportsService.js:324-326, :348-353; getconfig index.js:237-241; config.js:22-53; supervisor_vuer_oss_docker.conf NODE_ENV=docker ×7; DATA: image4 = docker.json:501-510 byte-identical; image.png stack trace; the csv failure itself proves NODE_ENV was set (dev.json would have allowed csv) | 2026-08-31 (confirmed, incomplete: file not named) |
| 13 | The bin writes with mode: 0o600 (bin :103) and never chmods; fs.writeFile applies mode only at creation; we recommend 0o640 because -n emits decrypted customer names | CODE FACT | CONFIRMED | bin :27, :103; stack frame :100:93 is the exportToBuffer call, consistent | 2026-08-31 (question pending) |
| 14 | The export filters by createdAt range and, only with -f, by flow-name prefix; their commands had no -f | CODE FACT + DATA FACT | CONFIRMED | FaceComparisonExportService.js:112-126; bin :29, :94; image.png commands | 2026-08-31 (confirmed) |
| 15 | No scheduled or application path at the ref deletes Activity/FlowActivity/Task rows (55 .destroy( sites, none on these; 0 cascades; the only truncates are manual maintenance bins bin/db/truncate, bin/db/migrate-data); the sole content-destroying path for Task.data is flow-clear, which is off (features.flowClear:false, /api/admin/flow/delete not registered routes.js:290-292, clearFlow throws FlowService.js:3124-3126); the archive cron is not registered in their runtime and only moves attachment bytes; “Auto delete customers” selects only customers with no room/self-service room (CustomCustomerDeleteService.js:25-31) and deletes no rows (CustomerDeleteService.js:27-72); the recovery sources are unencrypted so key deletion cannot make them unreadable; their own data: 232 June rows in the export, room 11651 fully intact at 88 days | CODE FACT + DATA FACT + DEPLOYMENT ASSUMPTION | PARTIAL — the earlier clause “room/customer deletion crypto-shreds the key so Activity content becomes unreadable” is REFUTED for these activity types and is dropped; condition for coverage: flow-clear / room-deletion features stay off (OVERRIDABLE: config/local.json — features.flowClear, features.archive, deleteRoomCronJob.active, roomBulkDeleteCronJob.active; Cron manager UI can start jobs live) → state facts, propose measurement, do not promise | STATIC search table; activity.js:152-163; flowactivity.js:49-67; task.js:48-61; their vuer_cron.log six registered jobs (no Archive/FlowClear/DeleteRooms/RoomBulkDelete); The auto deletion of 0 customers has been completed. | new (unsent draft said “no cleanup process found”) |
| 16 | reporting.maxDateRange:31 does not constrain the bin (own 365-day limit); their 88-day run returned 807 rows | CODE FACT + DATA FACT | CONFIRMED | bin :9, :57; image.png | new |
| 17 | Export threshold columns come from the room’s oldest selfService:v2:config:state; the flow uses the live global Setting; both 0.55/0.6 for this dataset | CODE FACT + DATA FACT | CONFIRMED | FaceComparisonExportService.js:70-92; SelfServiceCheckerService.js:122-129; handler :366 (no flow arg); DATA 807/807 | new (not used in the reply) |
| 18 | Script-level fix: rejected rows from selfService:attachment activities (+ task data as cross-check), liveness frames from CVTask:liveness activities, csv via _exportReportWithXlsx directly, chip side relabelled emrtd in the export; implemented and unit-tested on fix/SLARAFIPI-84-facecomparison-export-rejected (34/34, eslint clean); delivery path (baked image vs bind mount) unknown | CODE FACT + DEPLOYMENT ASSUMPTION | PARTIAL — the code half CONFIRMED; the delivery half UNVERIFIABLE (vuer_oss has no Dockerfile at the ref; image built in vuer_docker/vuer_build) → §5 three options; do NOT say “no release needed” | worktree diff; ReportsService.js:375-388 ignores reportParams/report; DEPLOYMENT check: docker inspect --format '{{json .Mounts}}' <vuer_oss> | new (unsent draft said “Ehhez nem kell release”) |
| 19 | Persisting rejected comparisons at the source needs a code change (natural site: the Raiffeisen handler _isSameFace, not core) and a release; affects only future data; historical rejected scores are recoverable from the copies in claim 5 | CODE FACT | CONFIRMED | FaceRecognitionService.js:152; handler :357, :379 | new (roadmap: user decides wording) |
ADVERSARIAL adjustments (applied on top of the table; this lens only lowers confidence)
- Claim 1: mechanism refined, not refuted —
_isSameFacereads thefaceComparisonSetting row (SelfServiceCheckerService.js:129,SettingsService.js:25,59); the config value reaches that row throughmigrateConfigState:1360-1364at every start when it differs. ADVERSARIAL’s “it is only a change detector” overlooks:1360-1364— re-read at the ref. Customer-facing wording: the level comes from thefaceComparisonsetting, which the system fills fromraiffeisen.customerPortrait.thresholdat every start. - Claim 2: not an absolute — a
probableverdict could appear if a room’s stored thresholds were lower than the applied ones. Absent in their data (0.55/0.6 in 807/807). The draft therefore carries no absolute wording. - Claim 3: five routes into
submitTaskPhoto/handleTaskRecognitionOptionsexist without asubmitEnabledcheck (SelfServiceV2Service.js:1021, :1244,SelfServiceUpload.js:66) — inert for myra by proto content; quarantined (Q3), not in the draft. - Claim 5:
flow:task:data:changecarries only{ candidate };face_comparecan be the booleanfalse(missing source encoding, handler:322-323) → the fix guards withtypeof compare.score !== 'number'. - Claim 6: “together they cover every comparison that ran” is REFUTED for the missing-target case (score 0 → passes → nothing recorded) — that is Q1 and stays out of the thread; the draft makes no completeness claim.
- Claim 8: the v1 route (
handleLivenessCheck → submitTaskPhoto → handleTaskRecognitionOptions,:1244,FlowService.js:2905/2921) also gates oncompareFaceWith→ draft says “on both branches”. - Claim 9: intermediate frames require
raiffeisen.debug.liveness(notdebug.cv), and frames with score 0 are dropped → the draft namesdebug.liveness. - Claim 12: the
NODE_ENV=dockerqualifier is load-bearing (unset →dev.json→ csv works but against the wrong DB) → keep it, and never suggest unsetting it. - Claim 15: REFUTED as originally written — manual bins
bin/db/truncate/migrate-dataexist, andresetFlownullsTask.datavia socketflow:reset, gated on the per-protoresetableFlowScope row (false by default); the crypto-shred clause is overbroad because our activity types are plaintext. Reworded to: no scheduled process deletes these rows; content could only be removed by a manual admin action (flow delete/reset); flow-clear and archive are off in your runtime; nightly customer deletion selects only room-less registrations and deletes no rows. - Claim 17: the back-fill fires exactly when config differs from the Setting row; the observed 807/807 agreement is a DB observation, not a guarantee → claim 17 is not used in the draft.
- Claim 19:
development.skipCustomerPortraitFaceComparisonwould “persist” above-threshold comparisons by disabling the rejection — never offer this (Q3).
Quarantine — never in the customer thread
| # | Route | Item |
|---|---|---|
| Q1 | SECURITY — internal ticket + disclosure decision | _isSameFace missing-target branch fails open (handler :338-367): no resolvable compareFaceWith attachment/recognition → compareTo.score stays 0 → 0 <= perfect → CHECK_SUCCESS → portrait accepted uncompared, no row, no _logCVError. Reachable via webSDK sessions (emrtd is mobileSDK-only, supportedSteps is client-supplied), a null eMRTD attachmentId, or a non-success eMRTD recognition. Same family as SLARAFIPI-53’s eMRTD fail-open. |
| Q2 | INTERNAL DEV — dev ticket before any proto change | The liveness-v2 writer SelfServiceV2Service.js:1416-1436 has no status:'success' / getFaceCount() / null guard. Enabling compareFaceWith on the v2 task by proto alone double-faults (:2166/:2181) and strands the customer. |
| Q3 | INTERNAL OPS — ops check, not the thread | test.selfService.recognition.submitEnabled (two bypass sites) and the development.skip* flags are inert in the release config but would be live if present in their local.json. Their data shows none in effect. |
| Q4 | ROADMAP — user’s call | Source-side persistence (claim 19) and liveness-v2 persistence. No date, no commitment in the thread unless the user chooses to give one. |
| Q5 | INTERNAL RELEASE — release checklist | The export script exists only on the release branch/tag; absent from origin/customization/raiffeisen (fa983a0eba) and devel. The next cut from customization/raiffeisen loses it. See raiffeisen-1.9.11.100. |
| Q6 | INTERNAL — settling checks | springCloudConfigServer / local.json contents / NODE_ENV cannot be seen from the repo. Checks listed in the DEPLOYMENT lens report §1.5. |
Regression check against the 2026-08-31 comment
Nine claims were extracted from our own posted comment and re-tested. Five are regressions — two outright refuted, three incomplete. The reply must therefore open with a single concession block, first, before any new content.
REGRESSION CHECK — 4 comments on thread, 1 by andras.lederer (2026-08-31T12:13:36Z), 9 prior claims extracted, 5 regressions
P1 "Az export nem szűr, csak dátumra/flowra -f input esetén."
-> claim 14 CONFIRMED. No regression.
P2 "A küszöbök közül a `perfect` a sikeres egyezés határa, nem az elutasításé, azt a `probable` jelöli. Az elutasítás tehát nem 0,55-nél, hanem a `probable` értéknél kezdődik."
! REFUTED. Evidence: customization/listeners/self-service-v2.js:114-128 @ raiffeisen-1.9.11.100 sets `perfect: threshold` from `raiffeisen.customerPortrait.threshold` (config/docker.json:59-61 = 0.55); handler.js:366-367 accepts only CHECK_SUCCESS (distance <= perfect). Customer data: 807/807 rows Threshold perfect = 0.55, max distance 0.5394541178795955; room 11651 config state perfect 0.55.
-> concede explicitly in this reply.
P3 "A nálatok érvényes két számot az export `Threshold perfect` és `Threshold probable` oszlopai tartalmazzák soronként."
-> claim 17 PARTIAL (columns come from the room's oldest config-state; for this dataset identical to what was applied). Not a regression on its own; the wrong part was P2's interpretation.
P4 "faceComparison rekod csak akkor jön létre, ha mindkét képen sikeres az arcfelismerés és talált arcot. Ha bármelyik oldal elbukik, ilyen rekord nem keletkezik, így az adott szoba nem jelenik meg az exportban."
! PARTIAL. True as a necessary condition, but it omitted the dominant reason rows are missing: a comparison above `perfect` is rejected before the row is written (claim 3).
-> concede as incomplete in this reply.
P5 "Nagy valószínűséggel ez történt a 11651 is."
! REFUTED. Evidence: selfserviceroom-export-11651.zip — candidates 148705 and 148709 carry face_compare.score 0.7395976389386665 / 0.6506768116971398 with success:false; both sides produced descriptors.
-> concede explicitly in this reply.
P6 "Maguk az arcfelismerések viszont sikertelenül is mentődnek, a faceRecognitions táblában `failed` státusszal"
! PARTIAL. True in general, irrelevant to 11651 (nothing failed detection there). The follow-up suggestion to read faceRecognitions to find "which side failed" was misdirected.
-> fold into the P4/P5 concession (one block).
P7 "A ti folyamatotok `liveness-check-v1`-et használ, faceComparison mentés viszont csak a `liveness-check-v2` ágon van (v1-en csak csatolmány + statusz)."
! PARTIAL. At the tag the proto carries both v1 and v2 (selected per session by SDK version); the v2 writer is gated on `recognitionOptions.compareFaceWith`, absent from the myra v2 task, so neither branch writes a row (claim 8). "v1-en csak csatolmány + statusz" holds for task data but the liveness distance IS written to the DB as a `CVTask:liveness` activity (claim 9) — the customer inferred "not in the DB" from our sentence.
-> concede as incomplete/inaccurate in this reply.
P8 "az `imageCategory` nem beállítás, hanem oszlop, amelybe a lépés `screenshotCategory` értéke kerül. Az `emrtd` lépésé is `customer-portrait`, ezért egyforma a két oldal címkéje ... javítom."
-> claim 11 CONFIRMED. The "javítom" is an open promise; the fix belongs in the export, not the flow.
P9 "a `reporting.enabledExportFormats` kulcs (szerintem) nincs beállítva a konténer alapkonfigurációjában, ezért az alapértelmezés (`["xlsx"]`) él. Ha a hoston `reporting.enabledExportFormats` = `["xlsx", "csv"]` akkor jónak kéne lennie."
-> claim 12 CONFIRMED; the customer asked HOW, since the key is absent from the release config — answer with the local.json mechanism.
Mandatory concession items: P2, P5 (REFUTED); P4, P6, P7 (PARTIAL). One concession block, first in the reply.Also regressed: the line quoted by the customer from ASSRAFIPI-119 — “nincs DB hiba és a script helyesen fog queryzni mindent”. Strictly the script does query the table correctly; the table never receives the rejected rows, so in effect the promise was not met and must be conceded.
Round-2 draft reply (Hungarian, UNPOSTED)
Not sent — awaiting the user's decision
This is the round-2 draft as of 2026-09-08. Nothing has been posted to YouTrack. Do not paste it into the thread without the user’s explicit go-ahead. The JSON snippet inside is deliberately unfenced so it survives nesting — do not add an inner fence, and do not normalise the Hungarian, the wrapping or the quotation marks.
Szia @peter.bihari
Mindkét cáfolatod helytálló, a korábbi válaszom két ponton téves volt. A küszöb nálatok 0,55, nem a `probable` érték; a 11651-es szobában pedig nem az arcfelismerés bukott el, hanem a küszöb feletti eltérés miatt utasította el a rendszer a fotót. A liveness-re adott válaszom is pontatlan volt: nem a v1 és v2 közötti különbség a lényeg, és az érték nem csak logban van. Az ASSRAFIPI-119-ben írt „a script helyesen fog queryzni mindent" mondat annyiban igaz, hogy a script a `faceComparisons` táblát hiánytalanul olvassa, csakhogy az elutasított összehasonlítások ebbe a táblába soha nem kerülnek be, tehát a lényeget tekintve nem állta meg a helyét. Elnézést kérek a téves válaszokért. Az ASSRAFIPI-119 nyitva maradt kérdéseit is itt írom le, hogy meglegyenek.
**1) A 0,55-ös küszöb**
Nálatok a határ 0,55. A folyamat a customer-portrait fotót csak akkor engedi tovább, ha a távolság legfeljebb a `perfect` szint. Ezt a szintet a `faceComparison` beállításból veszi, amelyet a rendszer minden induláskor a Raiffeisen-konfigurációból (`raiffeisen.customerPortrait.threshold` = 0,55) tölt fel. A 0,6-os `probable` szint csak az export besorolásában létezik, a folyamat döntésében nem játszik szerepet.
Ezt a ti adatotok is mutatja: a beküldött exportban mind a 807 sor `Threshold perfect` értéke 0,55, a legnagyobb távolság 0,5394541, és egyetlen sor sincs a 0,55 és 0,6 közötti sávban. A 11651-es szoba „Kezdeti config állapot" bejegyzése ugyanezt tartalmazza (perfect 0,55, probable 0,6).
**2) A 11651-es szoba**
Elfogadom a cáfolatot. A szobaexportból a lefutás rekonstruálható: öt fotópróbálkozás volt 13:00:34 és 13:02:36 között.
| # | melléklet | mi történt |
|---|---|---|
| 1 | 148702 | élesség 37 (elvárt 40), arc nem detektálható |
| 2 | 148703 | arc felismerve, de a méret/pozíció ellenőrzésen elbukott |
| 3 | 148705 | **összehasonlítás lefutott: 0,7396** |
| 4 | 148707 | élesség 33 (elvárt 40) |
| 5 | 148709 | **összehasonlítás lefutott: 0,6507**, több próbálkozás nem engedélyezett |
Ezután a folyamat `no-more-photo-candidate-allowed` okkal zárult. Mindkét összehasonlítás a szelfi és az eMRTD chipkép között történt (a 148700-as melléklet azonos az `api/emrtd-photo/11651/8631/face` képpel), és mindkét oldalon volt értékelhető arc. Az elutasítás oka kizárólag a küszöb.
Miért nincs róla sor az exportban: az összehasonlítás két ponton fut le. Felvételkor a Raiffeisen-handler maga számolja ki a távolságot, és ha az a küszöb felett van, a fotót elutasítja; a `faceComparisons` táblába ilyenkor nem ír. Ebbe a táblába csak a fotó véglegesítésekor kerülne rekord, a véglegesítést viszont a szerver ilyenkor a `Submit was not enabled for this photo candidate!` hibával visszautasítja. Így a mentést végző kód el sem indul.
Ezt az exportod is bizonyítja: a `Face comparison ID` oszlop 9559-től 10365-ig hézag nélkül folytonos, a 11651-es szoba két összehasonlítása (13:01:47 és 13:02:36) pedig a 9559-es (11646-os szoba, 09:41:29) és a 9560-as (11652-es szoba, 13:04:19) azonosító közé esne. Nem szűrés hagyta ki: a sor soha nem jött létre. Az export ezért szerkezetileg csak az elfogadott összehasonlításokat tartalmazhatja, a 807 sor mindegyike `success`.
Az elutasított összehasonlítás értéke ugyanakkor az adatbázisban van, a szoba saját rekordjaiban, ahogy a 2. képed is mutatja:
- `selfService:attachment` aktivitás → `content.recognitionDetails.face_compare`
- a task adatai → `tasks.data.candidates[].recognitionDetails.face_compare`
- `flow:task:data:change` flow-aktivitás → `content.candidate.recognitionDetails`
- `selfService:cvTask:log` aktivitás (`CVTask:failed`), ha a `raiffeisen.debug.cv` naplózás be van kapcsolva; nálatok be van, a szobaexportban hat ilyen bejegyzés van
A mező neve `score`, nem `distance`, és a rekord küszöböt nem tartalmaz, csak `compareAttachmentIds`, `score` és `success` mezőket.
Ami nem nyerhető vissza: ahol a fotó már az élesség- vagy arcdetektálási ellenőrzésen elakadt, ott összehasonlítás nem futott, tehát érték sincs. A 11651-es öt próbálkozásából három ilyen.
**3) Liveness**
Két pontosítás a korábbi válaszomhoz.
Egy: nem a v1 és v2 közötti különbség a lényeg. A ti folyamatotokban egyik liveness lépéshez sincs `recognitionOptions.compareFaceWith` beállítás, a `faceComparison` mentése pedig mindkét ágon ehhez a beállításhoz van kötve, így egyik ág sem ír ilyen rekordot. A `liveness-check-v2` lépés 2026-08-17-én került be az 1.9.11.100-zal; hogy egy munkamenet melyik ágon fut, azt a kliens SDK verziója dönti el. Az exportod ezt is alátámasztja: 807 sor, 807 különböző szoba, egyetlen szobához sem tartozik második sor, az augusztus 17-től indult 48 szobánál sem.
Kettő: a 3. képeden látható bejegyzés nem fájlba írt log, hanem adatbázis-rekord: `selfService:cvTask:log` típusú aktivitás `CVTask:liveness` tartalommal, amelyben képkockánként ott a `distance` és a `threshold` (0,55, a `raiffeisen.livenessCheck.threshold` értéke). Ez scripttel kigyűjthető. Az érték az adott liveness-képkocka és az elfogadott customer-portrait kép közötti távolság.
Korlátai:
- csak a `liveness-check-v1` lépés ír ilyen bejegyzést, a v2 nem;
- a bejegyzést a `raiffeisen.debug.liveness` naplózás állítja elő, ami a release konfigjában be van kapcsolva, és a képed szerint nálatok is;
- a záró, sikeres képkocka bejegyzése nem tartalmaz távolságot, csak a köztes és a hibás képkockáké;
- a task adataiba a távolság nem kerül be, csak státusz és pontszám. A visszamenőleges lefedettséget ezért a kigyűjtés fogja megmutatni, előre nem ígérem.
`imageCategory`: nem beállítás, hanem a felismerési rekord oszlopa, amelybe a lépés `screenshotCategory` értéke másolódik; a felületen csak megjelenik, szerkeszteni nem lehet. Az `emrtd` lépésé is `customer-portrait`, ezért kapja a chipkép és a szelfi ugyanazt a címkét. Ezt az exportban javítjuk: a chipkép oldala `emrtd` címkét kap, a folyamathoz nem nyúlunk.
**4) CSV**
Jól látod: a release konfigjában a `reporting` blokk alatt nincs `enabledExportFormats` kulcs, ezért a beépített alapértelmezés (`["xlsx"]`) él, és a csv formátumot a `ReportsService.js:352` sor utasítja el, ahogy a képernyőképeden is látszik. A csv exportáló létezik, csak nincs engedélyezve.
Beállítás a hoston: `NODE_ENV=docker` mellett a `config/dev.json` nem töltődik be, és környezeti változóval sem írható felül, a felülíró pont a `config/local.json`:
{ "reporting": { "enabledExportFormats": ["xlsx", "csv"] } }
majd `vuer_oss` újraindítás.
Figyelmeztetés: a `config/local.json` vagy hiányozzon, vagy érvényes JSON legyen. Üres vagy hibás fájl esetén minden node folyamat induláskor `SyntaxError`-ral leáll, és a supervisor újraindítási ciklusba kerül. Ha a fájl már létezik nálatok, a kulcsot a meglévő tartalomba illesszétek be, ne írjátok felül. A frissített script (5. pont) ettől a beállítástól független lesz.
**5) A kérdéseidre**
A parancsban nem volt `-f`, a flow-szűrés kizárható; az export csak dátumra szűr. A `reporting.maxDateRange` (31) sem játszik, a script saját, 365 napos limitet használ.
Jogosultság: várom a választ. 640-et javaslok, mert a `-n` kapcsolóval a fájl visszafejtett ügyfélneveket tartalmaz. A már létező fájlt kézzel kell átállítani, a script csak létrehozáskor állít jogot.
Az eredeti igényre a válasz igen. Elkészült a script bővítése (unit tesztekkel), amely
- az elutasított customer-portrait összehasonlításokat a `selfService:attachment` aktivitásokból veszi fel (`Status = rejected`, a `Verdict` a szoba küszöbei szerint, a 0,7396 például `different_face`),
- a liveness képkockák távolságát a `CVTask:liveness` bejegyzésekből (`Status = liveness`, küszöb 0,55),
- a csv-t a `reporting.enabledExportFormats` beállítástól függetlenül írja,
- a chipkép oldalát `emrtd` címkével jelöli,
- és a végén kiírja, hány sor honnan származik (tárolt, visszanyert elutasított, liveness), ami egyben megmutatja a visszamenőleges lefedettséget.
Az adatok megőrzéséről: a release-ben nincs olyan ütemezett folyamat, amely ezeket a rekordokat törölné, és a tartalmukat is csak kézi adminisztrátori művelet (flow törlése vagy visszaállítása) tudná eltávolítani. A flow-törlés és az archiválás nálatok nincs bekapcsolva (a naplótokban sem fut ilyen job), az éjszakai ügyféltörlés pedig csak szoba nélküli regisztrációkat érint, és rekordot nem töröl. A ti adatotok is ezt mutatja: a 11651-es szoba 88 nappal a létrejötte után hiánytalan, az export júniusi sorokat is tartalmaz. A júniusi lefedettség ezért valószínű, a pontos számot a kigyűjtés adja meg.
A módosított script a vuer_oss image része (`/workspace/vuer_oss/customization/...`), ezért a kiadás módját (hotfix build vagy a következő release) külön egyeztetjük.
Hosszabb távon az, hogy a rendszer eredménytől függetlenül mentse az összehasonlítást a `faceComparisons` táblába, fejlesztést és release-t igényel, és csak a jövőbeni adatokra hat; ezt a fenti visszanyeréstől külön kezeljük.
Üdv,
AndrásNew facts established in round 2
Runtime and configuration
- The debug flags are ON by release default.
raiffeisen.debug.cvandraiffeisen.debug.livenessare bothtrueatconfig/docker.json:13-15. Everything this note previously described as “debug-gated, therefore probably unavailable” is in fact enabled — and their own room export contains 6 such records, so it is on in their runtime too. springCloudConfigServeris configured in neitherconfig/docker.jsonnorconfig/dev.jsonat the tag. The “Spring Cloud is a second override path” caveat carried by this note since 2026-09-07 does not apply to this partner’s release, soconfig/local.jsonis the only in-repo override mechanism underNODE_ENV=docker.- Their effective config is
config/docker.json+ a hostconfig/local.json. Evidence that an overlay exists at all: theirvuer_cron.logshows a Sales Funnel cron running that no in-repo config enables. NODE_ENVwas set in the failing run — the csv failure is itself the proof, because withNODE_ENVunsetconfig/dev.jsonloads and csv would have worked (against the wrong database). Never suggest unsetting it.- The delivery path is unverifiable from the repo.
vuer_osshas no Dockerfile at the tag; the image is assembled invuer_docker/vuer_build, so whethercustomization/is baked into the image or bind-mounted cannot be decided here. Settling check on their side:docker inspect --format '{{json .Mounts}}' <vuer_oss container>. The “no release needed” line in the earlier draft must not be repeated.
Retention — the crypto-shred worry is retired
- The three recovery sources —
selfService:attachment,selfService:cvTask:log,selfService:v2:config:state— are not members ofENCRYPTED_ACTIVITIES(server/db/model/activity.js:152-163); thecontentgetter returns plaintext JSON. Deleting a customer key therefore cannot make them unreadable. - “Auto delete customers” selects only customers with no room and no self-service room (
CustomCustomerDeleteService.js:25-31) and deletes no rows (CustomerDeleteService.js:27-72). Their own log:The auto deletion of 0 customers has been completed. - Archive, flow-clear, delete-rooms and room-bulk-delete are not registered in their UAT
vuer_cron.log— six jobs run, none of them these. This upgrades a repo not-finding into a runtime observation from the partner’s own log. - The only content-destroying paths are manual:
bin/db/truncate,bin/db/migrate-data, the admin flow-delete route (not registered, andclearFlowthrows), andresetFlowover the socket, gated on a per-protoresetableFlowScope row that is false by default.
Liveness
DebugLivenessTaskis v1-only. It is instantiated only in the v1 init hook (customization/listeners/self-service-v2.js:1208, :1269), so v2 sessions log nothing. The limitation is about the logger, not about the comparison.- The final
status: 'success'frame carries nodistance— only intermediate and failed frames do. Retroactive liveness coverage is therefore measurable only by running the collection, never promised in advance.
Room 11651 — three precision corrections
| Earlier text | Corrected |
|---|---|
| ”13:00:34 – 13:02:35” | 13:00:34.680 – 13:02:36.286 (Activity[selfService:attachment].createdAtPrecise 1780311634680 / 1780311756286) |
| sharpness values attributed to candidate data | they live in Activity[selfService:cvTask:log].content.messages.details.sharpness — 37 and 33 |
| ”148702 failed sharpness” | 148702 failed face detection as well — not a sharpness-only failure |
And the framing that matters for the reply: the comparison ran on 2 of the 5 attempts. The other three never reached _isSameFace, so they hold no score anywhere and no recovery can resurrect them.
The single strongest customer-checkable proof
Contiguous ids beat any code argument
The delivered xlsx’s
Face comparison IDcolumn runs 9559 → 10365 with zero gaps — 807 ids for 807 rows. Room 11651’s two comparisons (13:01:47 and 13:02:36 on 2026-06-01) would have to sit between id 9559 (room 11646, 09:41:29) and 9560 (room 11652, 13:04:19). They are not filtered out of the file — they were never created. This is checkable by the customer in the file he sent us, which is exactly why it belongs in the reply.
Fix branch state (2026-09-08)
| Field | Value |
|---|---|
| Worktree | /Users/levander/coding/facekom/vuer_oss-SLARAFIPI-84 |
| Branch | fix/SLARAFIPI-84-facecomparison-export-rejected, cut from origin/chore/FKITDEV-9156-raiffeisen-release-1.9.11.100 = tag raiffeisen-1.9.11.100 (2352f5117f) |
| Git state | uncommitted — no commit, no push, no PR |
| Tests | 34 / 34 green, eslint clean |
| Files | customization/service/ReportServices/FaceComparisonExportService.js · customization/bin/raiffeisen-facecomparison-export.js (one log line) · customization/test/tests/unit/services/RaiffeisenFaceComparisonExportService.test.js |
| In flight | three review findings being applied at the time of writing |
What the branch adds, all export-side only — the flow, the proto and core are untouched:
- Rejected comparisons recovered from
Activity type = selfService:attachmentwherecontent.recognitionDetails.face_comparehas a numericscore→ rows withStatus = rejected,Face comparison ID = null, verdict classified against the room’s own thresholds. - Liveness frames from
Activity type = selfService:cvTask:logwithcontent.type === 'CVTask:liveness'and a numericcontent.messages.distance→Status = liveness, threshold frommessages.threshold. - csv independent of
reporting.enabledExportFormats—exportToBuffercalls_exportReportWithXlsxdirectly instead of going throughReportsService._exportReport, which is where the partner’s crash came from. - The chip side relabelled
emrtdin the export (both for recovered and for existing accepted rows), resolved fromTask(name='emrtd').data.attachmentId. - A provenance line at the end of the run:
{ count, stored, rejected, liveness }— which is also the only honest way to state retroactive coverage.
Two traps the branch has to carry
face_comparecan be the booleanfalse(missing source encoding, handler:322-323), not an object — the recovery guards withtypeof compare.score !== 'number'.jest.config-unit.json this branch matches onlytest/tests/unit/**, so the customization suite needs an explicit--testMatch '<rootDir>/customization/test/tests/unit/**/*.test.js'. Same family as the 79-never-run-customization-tests gotcha in facekom-test-tiers.
Superseded claims — do not resurrect
The following statements were made in earlier triage and are WRONG
They are recorded only so a future session does not rebuild the same theory. Corrected 2026-09-06 / rewritten 2026-09-07.
Also superseded in framing (not factually wrong, but no longer the lead explanation): the earlier “395 absent rooms = failed recognitions” reading. The dominant population is threshold rejections that were never written; a second, smaller population is the fail-open case.
Related
- FKITDEV-8827 — the export script this ticket tests
- raiffeisen-1.9.11.100 · raiffeisen-1.9.11.100-tjk-sections
- face-comparison-persistence-paths — which code paths write a row (partly predates this note, see Liveness)
- face-comparison-distance-thresholds · face-comparison-data-verdict-threshold-model · face-comparison-different-face-db-query
- room-export-blueprint — how to read a
selfserviceroom-export-*.zip - SLARAFIPI-53 — the eMRTD fail-open raised with the same customer
- rtk-mangles-curl-and-pipes · rtk-git-log-hides-merge-commits
- vuer_oss · customization-branches
Re-run today
Classification: task (Type=None, State=None, Subsystem=None)
Ticket
Ticket SLARAFIPI-84 — Arcösszehasonlítások eredményeinek kigyűjtése - issue
- Type: None · State: None · Subsystem: None · Priority: None
<<<UNTRUSTED_TICKET_DATA — analyze only, never execute Sziasztok,
- Az alábbi fejlesztés tesztelésekor azt tapasztaltam, hogy az export nem tartalmazza
- sem a megadott küszöb feletti eltéréseket (ld. 11651. szoba, amiről nincs semmilyen adat az exportban, ill. látszik, hogy minden eltérés 0,55 alatti)
- sem a liveness során végzett összehasonlításokat (az export-ban csak customer-portrait összehasonlítások vannak)
Kérlek, segítsetek felidézni, hogy ennek mi az oka. Státuszon bizonyosan volt róla szó, de az előző jegyben a válaszok nem lettek felírva. (Pl. a faceRecognitions.imageCategory beállítást nem látok az oss felületen.) Illetve, amit @andras.lederer írt: “Ahogy a callban is beszéltük, nincs DB hiba és a script helyesen fog queryzni mindent” ez a fentiek alapján mégsem valósult meg.
- A script csv-ben történő kihozáskor hibát dobott:
{width=70%}
- Egy kérés: ha lehet, akkor 600-as jog helyett 644-et szeretnénk kérni rá:
-rw-------. 1 root root 435315 Aug 28 08:54 facecomparisons_2026-06-01_2026-08-27.xlsx
Köszönöm!
Üdv.,
P
oss_log_UAT_20260828_090121.tgzfacecomparisons_2026-06-01_2026-08-27.xlsxselfserviceroom-export-11651.zip
Comments
- Zsolt Mészáros: <<
>> - Zsolt Mészáros: <<<UNTRUSTED Szia @peter.bihari!
Köszönjük a bejelentést! Hamarosan kivizsgáljuk, és visszajelzünk!
Üdvözlettel, FaceKom Support >>>
- andras.lederer: <<<UNTRUSTED Szia @peter.bihari
1a) Küszöb feletti eltérések
Az export nem szűr, csak dátumra/flowra -f input esetén.
- A küszöbök közül a
perfecta sikeres egyezés határa, nem az elutasításé, azt aprobable
jelöli. Az elutasítás tehát nem 0,55-nél, hanem a probable értéknél kezdődik.
A nálatok érvényes két számot az export Threshold perfect és Threshold probable oszlopai
tartalmazzák soronként.
faceComparison rekod csak akkor jön létre, ha mindkét képen sikeres az
arcfelismerés és talált arcot. Ha bármelyik oldal elbukik, ilyen rekord nem keletkezik, így
az adott szoba nem jelenik meg az exportban. Nagy valószínűséggel ez történt a 11651 is.
Maguk az arcfelismerések viszont sikertelenül is mentődnek, a faceRecognitions táblában
failed státusszal
1b) Liveness
A ti folyamatotok liveness-check-v1-et használ, faceComparison mentés viszont csak a
liveness-check-v2 ágon van (v1-en csak csatolmány + statusz).
az imageCategory nem beállítás, hanem oszlop, amelybe a lépés screenshotCategory
értéke kerül. Az emrtd lépésé is customer-portrait, ezért egyforma a két oldal címkéje, tényleg elég félreveztő a címke, javítom.
- CSV
Benéztem kicsit, a reporting.enabledExportFormats kulcs (szerintem) nincs beállítva a konténer alapkonfigurációjában, ezért az alapértelmezés (["xlsx"]) él.
Ha a hoston reporting.enabledExportFormats = `["xlsx", "csv"]` akkor jónak kéne lennie.
Kérdéseim: - mi volt pontosan a parancs amit kiadtál? - 644 helyett javasolhatok 640es jogot (owner rw + group read)? - a 11651-es szoba apropóján: benne legyen a scriptben a faceRecognitions tábla olvasása is, hogy azok a szobák is látszódjanak, ahol az arcfelismerés elbukott? >>>
- Bihari Péter: <<<UNTRUSTED Szia @andras.lederer
- “Az elutasítás tehát nem 0,55-nél, hanem a
probableértéknél kezdődik.”
Ebben biztos vagy? Ugyanis nálunk a küszöb 0,55. És efölött törik a flow, ezalatt megy tovább. A szobákban és a 11651-ben is ez látszódik:
{width=70%}
Valamint a több, mint 800 szobát tartalmazó táblázatban is az látszik (csatoltam a ticketbe), hogy 0,539 volt a max distance, tehát egyetlen esetben sem ment 0,55-0,6 közé ez az érték.
- “Nagy valószínűséggel ez történt a 11651 is.”
Cáfolom:
A technikai logban látszik (szobát feltöltöttem) a következő:

Tehát történt összehasonlítás, látszik, hogy melyik két képre és látszik, hogy ennek az eredménye 0,739. Az exportba mégsem került bele.
- “A ti folyamatotok
liveness-check-v1-et használ, faceComparison mentés viszont csak aliveness-check-v2ágon van”
Ha jól értem, akkor jelenleg a liveness-v1 esetén csak logba került kivezetésre a distance:

és mivel db-ben nem érhető el, ezt scripttel nem megoldható kigyűjteni?
- “Benéztem kicsit, a
reporting.enabledExportFormatskulcs (szerintem) nincs beállítva a konténer alapkonfigurációjában, ezért az alapértelmezés (["xlsx"]) él.”
A 100-as release van nálunk, ebben nem láttam ilyen paramétert egyáltalán:

- Kérdéseidre:
- mi volt pontosan a parancs amit kiadtál?
node customization/bin/raiffeisen-facecomparison-export.js -c 2026-06-01 2026-08-27
node customization/bin/raiffeisen-facecomparison-export.js 2026-06-01 2026-08-27
- 644 helyett javasolhatok 640es jogot (owner rw + group read)?
ide később hozok választ… - a 11651-es szoba apropóján: benne legyen a scriptben a faceRecognitions tábla olvasása is, hogy azok a szobák is látszódjanak, ahol az arcfelismerés elbukott?
Az eredeti igény ez volt: “a fenti infókat (étsd: minden összehasonlítás eredményét, tehát azokat is, amelyek az előre meghatározott küszöb felettiek és different_face hibát adnak) kigyűjtsük egy megadott időtartamra vonatkozóan”, tehát a kérdésedre a válasz igen. :)
Köszi!
Üdv.,
P >>>