# Watchdog VFP + diagnostic blocaj "View Parameter" (test_page3_articole.prg)

## 1. Utilitarul: `COMUN\utile\Teste\watchdog_vfp.ps1`

Lanseaza `vfp9.exe -A -T "<script>"`, polleaza la ~700ms ferestrele TOP-LEVEL ale procesului
(`EnumWindows`+`GetWindowThreadProcessId`, filtrate pe PID). Fereastra principala VFP se identifica
prin `Process.MainWindowHandle` (.NET) - **nu** dupa numele clasei: VFP inregistreaza clase diferite
prefixate `vfp9...` atat pentru shell-ul principal (`vfp99400000`) cat si pentru dialogurile lui
proprii (`vfp994000002` pentru "View Parameter") - o clasificare pe clasa a lasat "View Parameter"
nedetectat la prima incercare (corectat).

Pentru fiecare fereastra noua (diferita de `MainWindowHandle`): captura PNG (`PrintWindow`, fallback
`CopyFromScreen` daca iese neagra) + dump text (titlu, clasa, si textul/clasa fiecarui control copil
via `WM_GETTEXT`, cross-proces). Cu `-AutoDismiss`: cauta un buton copil "Cancel"/"Anulare" si
trimite `BM_CLICK`; altfel incearca, in ordine, `WM_COMMAND IDCANCEL` -> ESCAPE ca MESAJ
(`WM_KEYDOWN`/`WM_KEYUP`, tintit pe handle) -> `WM_CLOSE`.

**REGULA OBLIGATORIE, incalcata initial si corectata**: watchdog-ul NU are voie sa foloseasca INPUT
REAL de tastatura/mouse (`keybd_event`, `SetForegroundWindow`, `SendInput`, `mouse_event`) - masina e
PARTAJATA cu utilizatorul. Prima versiune folosea `SetForegroundWindow`+`keybd_event(ESCAPE)` ca
fallback pentru dialogurile proprii VFP owner-drawn (fara controale copil reale) - **acest input NU
are tinta, ajunge in orice fereastra are focus pe masina in acel moment, indiferent al cui proces
e**. **Fapt clarificat de team-lead, dupa investigare**: in acest caz concret, ESC-ul a aterizat de
fapt in PROPRIUL NOSTRU proces de test (eroarea "Variable 'GNAN' is not found" vazuta imediat inainte
de "Execution was canceled by the user" e chiar semnatura testului nostru) - **nu in sesiunea lui
Marius**, deci de fapt nu s-a intrerupt munca nimanui de data asta. Asta NU schimba regula: mecanismul
tot nu are tinta si putea la fel de usor sa ajunga in aplicatia de productie (`roafacturare.exe`/
`roacont.exe`) daca utilizatorul avea focus acolo in acea clipa - o formulare anterioara aici spunea
gresit ca ar fi afectat sesiunea utilizatorului, corectata acum. **Scos complet** din cod (nu mai
exista nicio linie `keybd_event`/`SetForegroundWindow` in `watchdog_vfp.ps1`). Consecinta: pentru
dialogurile owner-drawn care nu raspund la mesaje tintite, `-AutoDismiss` **esueaza cinstit** (dialogul
ramane deschis pana la `-TimeoutSec`, apoi procesul e omorat) - captura (PNG+dump text) ramane de incredere,
dismiss-ul nu e garantat pentru acest tip de dialog. Procesul e omorat mereu la iesire (`finally`),
nu ramane niciodata viu.

Validat intai pe caz banal: `COMUN\utile\Teste\watchdog_selftest.prg` (`MESSAGEBOX` cu OK/Cancel) -
detectie, captura, dump text si auto-dismiss confirmate corecte inainte de a-l rula pe cazul real.

Utilizare: `powershell -ExecutionPolicy Bypass -File watchdog_vfp.ps1 -Script "<test.prg>" -AutoDismiss [-TimeoutSec 90] [-MaxDialogs 10] [-OutDir <cale>]`.

## 2. Dialogurile capturate pe `test_page3_articole.prg`

**Dialog 0** - nativ VFP, clasa `vfp994000002`, titlu **"View Parameter"**, text **"Enter the value
for gnAn:"** (fara controale copil reale - owner-drawn). Screenshot:
`COMUN\utile\Teste\editare_factura\watchdog_out\test_page3_articole_dialog0.png`.

**Dialog 1** (apare doar cand dialog 0 e inchis prin ESCAPE real, care functioneaza) - "Open" Win32
standard, filtru "Table/DBF (*.dbf)", folder implicit `ROACONT` (working directory-ul mediului de
test, mostenit din `test_init_env_auto.prg`). Screenshot:
`COMUN\utile\Teste\editare_factura\watchdog_out\test_page3_articole_dialog1.png`.

## 3. PROGRAM()/LINENO() din log (dupa Cancel pe dialog 0)

Din `test_page3_articole_log.txt`, PROGRAM()=`VERIFICA_PAGECOUNT_FORM` (procedura de test) pe
liniile din jurul apelului `loForm = Createobject([frm_modific2024], lnIdSet)`:

```
EROARE 1 [VERIFICA_PAGECOUNT_FORM:165] File 'crsjtvatemp.dbf' does not exist.
EROARE 1924 [VERIFICA_PAGECOUNT_FORM:166..173] LOFORM is not an object.   (cascada, zgomot)
```

## 4. Experimente de izolare (cerute de team-lead) - **INFIRMA ipoteza initiala**

Ipoteza initiala din aceasta sectiune ("`SQLEXEC` din `update_jtva_coloane` nu rezolva `?gnAn`") era
o **deductie**, nu o masuratoare - team-lead a cerut-o verificata direct, corect. Trei experimente
ieftine, in ordine:

**Experiment A - "chiar exista in acel moment?"** `TYPE('gnAn')`/`TRANSFORM(gnAn)` puse imediat
**INAINTE** de apelul `update_jtva_coloane` (linia 161 curenta, nu inainte de `Createobject` cum
fusese verificat prima data):

```
EROARE 12 [VERIFICA_PAGECOUNT_FORM:161] Variable 'GNAN' is not found.
```

- Eroare aparuta DOAR la primul apel al `verifica_pagecount_form` (`cod=1140888`), inainte sa se
  ajunga la `update_jtva_coloane`. **Rezultat: `gnAn` e deja invizibila INAINTE ca `update_jtva_coloane`
  sa fie apelata** - markerele `?gnAn`/`?gnLuna` din `updateserver.prg:597` nu pot fi (macar nu
  singure) cauza, contrazice ipoteza initiala.

**Experiment B - "se reproduce izolat, fara nimic din S4?"** Script nou,
`COMUN\utile\Teste\editare_factura\test_repro_izolat_gnan.prg`: DOAR `test_init_env_auto` +
`update_jtva_coloane("", "crsJtvaTemp", 6)`, fara `IncarcaCursoareModificareNota`, fara
`frm_modific2024`, fara nimic din PAGE3. Rezultat:

```
TYPE(gnAn)=N TRANSFORM(gnAn)=2026 TYPE(gnLuna)=N TRANSFORM(gnLuna)=8
dupa update_jtva_coloane: Used(crsJtvaTemp)=.T. Reccount=120
done
```

**NU reproduce.** Zero dialog, exit curat, cursorul se creeaza corect cu 120 randuri.
`update_jtva_coloane` singura, chemata imediat dupa initul mediului, functioneaza perfect -
**nu e o capcana preexistenta a harness-ului si nici o vina proprie a functiei in izolare**.
Rerulat identic dupa scoaterea input-ului real din watchdog (vezi sectiunea 1) - acelasi rezultat,
deci reconfirmat, nu era un artefact al mecanismului de dismiss.

**Experiment C - oprit inainte de a-l rula.** Premisa lui ("copiaza corpul lui `update_jtva_coloane`
cu concatenare in loc de `?param`, ca sa confirmi mecanismul") presupune ca vina e in legarea
`SQLEXEC` a functiei - exact ce B tocmai a infirmat. Nu are sens sa continue in forma ceruta initial
fara o noua directie.

## 5. Cauza CONFIRMATA prin bisectie (nu doar deductie)

Bisectie ceruta de team-lead: `TYPE('gnAn')`/`TRANSFORM(gnAn)` logat in **doua straturi** - (a) in
programul principal, intre fiecare apel de nivel superior, si (b) ca **prima linie** in interiorul
fiecarei proceduri (`verifica_vanzare_nota`, `verifica_coliziune_cod`, `verifica_pagecount_form`).
Helper `bisect_log_gnan` (nou, la coada `test_page3_articole.prg`) - TYPE() e sigur necoditionat,
TRANSFORM() doar daca TYPE()<>'U', ca sa nu produca o eroare noua care ar intrerupe bisectia.

Rezultat brut (`test_page3_articole_log.txt`):

```
[BISECT] main: dupa test_init_env_auto                          :: TYPE(gnAn)=N gnAn=2026
[BISECT] verifica_vanzare_nota ENTRY cod=1140888                :: TYPE(gnAn)=U gnAn=(U)
[BISECT] main: dupa verifica_vanzare_nota #1 (cod=1140888)      :: TYPE(gnAn)=N gnAn=2026
[BISECT] verifica_vanzare_nota ENTRY cod=1140885                :: TYPE(gnAn)=U gnAn=(U)
[BISECT] main: dupa verifica_vanzare_nota #2 (cod=1140885)      :: TYPE(gnAn)=N gnAn=2026
[BISECT] verifica_vanzare_nota ENTRY cod=1125486                :: TYPE(gnAn)=N gnAn=2026   <- diferit!
[BISECT] main: dupa verifica_vanzare_nota #3 (cod=1125486)      :: TYPE(gnAn)=N gnAn=2026
[BISECT] verifica_coliziune_cod ENTRY                            :: TYPE(gnAn)=N gnAn=2026
[BISECT] main: dupa verifica_coliziune_cod                       :: TYPE(gnAn)=N gnAn=2026
[BISECT] main: inainte de verifica_pagecount_form                :: TYPE(gnAn)=N gnAn=2026
[BISECT] verifica_pagecount_form ENTRY cod=1140888                :: TYPE(gnAn)=U gnAn=(U)
[BISECT] verifica_pagecount_form inainte de update_jtva_coloane   :: TYPE(gnAn)=U gnAn=(U)
```

**Tiparul e limpede si consecvent**: `gnAn` e INTOTDEAUNA valid (`N`, `2026`) in scope-ul PRINCIPAL,
la fiecare checkpoint, fara exceptie - deci **NU e "eliberata"** (nu e `CLEAR ALL`/`CLEAR MEMORY`/
`RELEASE ALL EXTENDED` pe undeva). E **`'U'` STRICT la intrarea in proceduri apelate cu `DO ... WITH`
in care `gnAn`/`gnLuna` sunt trecute NEPARANTEZATE ca argumente**, si redevine valid imediat ce
procedura respectiva se termina si controlul revine in principal. Corelatia e exacta cu sintaxa
apelului, nu cu ce face procedura pe dinauntru:

- `verifica_vanzare_nota` apelul #1/#2 (`gnAn` -> `U`): call-site-urile trec `gnAn, gnLuna` DIRECT -
  `test_page3_articole.prg:34` (`DO verifica_vanzare_nota WITH 1140888, gnAn, gnLuna, ...`) si
  `test_page3_articole.prg:36` (`... WITH 1140885, gnAn, gnLuna, ...`).
- `verifica_vanzare_nota` apelul #3 (`gnAn` ramane `N`): `test_page3_articole.prg:38` trece
  `2008, 2` LITERAL, nu `gnAn`/`gnLuna`.
- `verifica_coliziune_cod` (`gnAn` ramane `N`): `test_page3_articole.prg:42` nu trece deloc
  `gnAn`/`gnLuna` (doar `lcLog`).
- `verifica_pagecount_form` primul apel (`gnAn` -> `U`, **exact scenariul blocat**):
  **`test_page3_articole.prg:47`** - `DO verifica_pagecount_form WITH 1140888, gnAn, gnLuna, 'A (factura reala)', lcLog, 3, .T.`.

**Mecanismul**: `DO <procedura> WITH <arg1>, <arg2>, ...` (stilul vechi, folosit peste tot in acest
script) trece variabilele de memorie **BY REFERENCE** implicit (`SET UDFPARMS` e `REFERENCE` in mod
implicit VFP) - `LPARAMETERS tnAn, tnLuna` din procedura primitoare devin ALIAS-uri directe pe
storage-ul lui `gnAn`/`gnLuna`, iar numele ORIGINAL devine inaccesibil (`TYPE()='U'`) **pe toata
durata apelului**, exact cat tine executia procedurii - confirmat empiric de simetria perfecta
"intra U, revine N" la fiecare din cele 3 perechi de apeluri afectate.

**Clasificare in termenii cerutii de team-lead**: e **"umbrire de scope"** (categoria 2), NU
"eliberare" (categoria 1) - dar mecanismul exact nu e o coliziune de nume `PRIVATE`/`LOCAL` in corpul
procedurii (team-lead avea deja dreptate: `verifica_pagecount_form` nu are `gnAn` in `LOCAL`, si nu
exista `PRIVATE gnAn`/`LOCAL gnAn` nicaieri in `COMUN\programe`) - **umbrirea vine din SINTAXA
APELULUI** (`DO...WITH` fara paranteze in jurul lui `gnAn`/`gnLuna`), nu din declaratiile procedurii
apelate.

**Statement-ul vinovat exact, cu fisier:linie**: `COMUN\utile\Teste\editare_factura\test_page3_articole.prg:47`.

**IMPLICATIE IMPORTANTA**: acesta e un tipar din **SCRIPTUL DE TEST**, nu din fluxul real al
aplicatiei - `do_editare_factura` (codul de productie) nu trece prin `verifica_pagecount_form`
(helper propriu testului). Team-lead a confirmat verdictul: **e strict un defect de harness** -
`updateserver.prg`, `omodificari.*` si codul S4 sunt toate nevinovate.

**Remediu APLICAT de team-lead** (3 linii, sub pragul lui de editare directa): argumentele
`gnAn`/`gnLuna` sunt acum parantezate - `(gnAn)`, `(gnLuna)` - la liniile 37, 39 si 50 din
`test_page3_articole.prg` (parantezele forteaza trecere PRIN VALOARE in loc de PRIN REFERINTA),
cu un comentariu explicativ deasupra primei aparitii. Nerulat inca de mine (interzis explicit -
suita o ruleaza team-lead-ul dupa eliberarea `.fxp`-ului).

**Remediul din rundele anterioare ale acestui raport (concatenare in loc de `?gnAn`/`?gnLuna` in
`updateserver.prg:597`) ramane infirmat** - nu era cauza. `updateserver.prg` nu a fost si nu e atins.

## 6. Fisiere atinse

- **Nou**: `COMUN\utile\Teste\watchdog_vfp.ps1`, `COMUN\utile\Teste\watchdog_selftest.prg`,
  `COMUN\utile\Teste\editare_factura\test_repro_izolat_gnan.prg` (experimentul B, izolat).
- **Modificat**: `COMUN\utile\Teste\editare_factura\test_page3_articole.prg`
  - linia 176 (`update_jtva_coloane(..., 6)`, ramane - fix necesar pt. indexul `id_jtva`, independent
    de defectul de mai jos);
  - `PROCEDURE bisect_log_gnan` (noua, la coada fisierului) + apeluri `DO bisect_log_gnan WITH ...`
    inserate in principal (intre apelurile de nivel superior) si ca prima linie in fiecare procedura
    - ramase in cod, sunt dovada bisectiei;
  - **liniile 37, 39, 50 - remediul APLICAT de team-lead**: `gnAn`/`gnLuna` parantezate (`(gnAn)`,
    `(gnLuna)`), forteaza trecere prin valoare in loc de prin referinta in `DO...WITH`.
- **Neatins**: `COMUN\clase\omodificari.vc2/.vcx/.vct`, `COMUN\programe\updateserver.prg` (ambele
  infirmate ca posibila cauza, vezi sectiunea 5).
- Artefacte de rulare (`watchdog_out\*.png/.log`, `*_log.txt`) raman pe disc ca dovada; se pot sterge
  cu `curatenie.ps1` la finalul lucrarii.

## 7. Stare la data acestui raport

**Cauza confirmata prin bisectie (sectiunea 5) si remediul APLICAT de team-lead** (paranteze la
liniile 37/39/50). **Nerulat inca** de nimeni dupa aplicarea remediului - team-lead ruleaza suita
separat, dupa eliberarea `.fxp`-ului (nu s-a mai relansat testul in aceasta sesiune, per interdictia
primita). Ramane deschis, pentru cine continua:

1. Confirma cu o rulare ca remediul chiar elimina dialogul si `verifica_pagecount_form` trece PASS
   pe `PageCount=3`/`lAreArticoleVanzari=.T.` pentru cod=1140888.
2. Optional: verifica daca fluxul REAL de productie (`do_editare_factura` in `ofacturare_comun.vc2`)
   are undeva acelasi tipar `DO...WITH <variabila PUBLIC>` NEPARANTEZAT inainte de un `?param` in
   SQLEXEC - team-lead a verdictuit "defect de harness", dar asta ramane neverificat exhaustiv pe
   codul de productie.
3. Watchdog-ul (`watchdog_vfp.ps1`) ramane instrumentul de verificat orice ipoteza noua fara sa se
   agate procesul si FARA input real (regula obligatorie, sectiunea 1) - reutilizabil pentru orice
   alt blocaj similar in suita.
