docs: sterge documente de lucru neactuale (handoff, patch, rapoarte, qa_factura)

Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01EvTWjHyBjoBXT43RFSZN4b
This commit is contained in:
2026-09-29 11:29:40 +03:00
parent e0e7bdef28
commit 828577b538
25 changed files with 0 additions and 4401 deletions

View File

@@ -1,101 +0,0 @@
# Pornire pe VM 304 - QA factura/aviz
Pachet pregatit 17.09.2026 de sesiunea de pe masina principala. Contine tot ce ii trebuie unei
sesiuni Claude Code noi ca sa continue de la S1, fara sa refaca analiza.
## Pasul 1 - copiaza fisierele
Din acest pachet, in copia de lucru de pe VM (verifica intai care e calea reala - documentatia
spune `D:\roa\<produs>`, planul presupune `D:\ROA\ROAFACTURARE`; daca difera, calea reala castiga):
| Din pachet | Unde pe VM |
|---|---|
| `docs\*.md` (8 fisiere) | `<radacina ROAFACTURARE>\docs\` |
| `utile\context_watch.ps1` | peste `<radacina ROAGEST>\COMUN\utile\context_watch.ps1` |
Daca pe VM nu exista checkout ROAGEST, pune `context_watch.ps1` oriunde si ajusteaza calea din
settings.json la pasul 2.
## Pasul 2 - hook-urile (optional, dar recomandat)
In `%USERPROFILE%\.claude\settings.json` de pe VM, adauga:
```json
"env": {
"CLAUDE_AUTOCOMPACT_PCT_OVERRIDE": "70"
},
"hooks": {
"SubagentStop": [
{ "hooks": [ { "type": "command",
"command": "powershell -NoProfile -ExecutionPolicy Bypass -File <CALEA CATRE>\\context_watch.ps1 -Subagent -Json" } ] }
]
}
```
Daca `settings.json` are deja `env` sau `hooks`, se completeaza, nu se inlocuieste.
Verifica dupa editare ca fisierul e JSON valid.
Ce face: la fiecare subagent care se termina, masoara contextul ACELUI subagent (citind
`agent_transcript_path` din inputul hook-ului) si avertizeaza orchestratorul la 150k / 200k.
Tace sub prag si tace la `stop_hook_active=true`, ca sa nu intre in bucla.
## Pasul 3 - verifica mediul inainte de orice
Rulare rapida, inainte sa incepi lucrul:
```powershell
Test-Path 'C:\Program Files (x86)\Microsoft Visual FoxPro 9\vfp9.exe'
Test-Path '<radacina>\COMUN\utile\Teste\vfp_ui_harness.ps1'
Test-Path '<radacina>\COMUN\utile\Teste\test_init_env_auto_roafacturare.prg'
Test-Path 'D:\ROA\UTIL\foxbin2prg\vfp_symbols.ps1'
```
Daca vreunul lipseste, **spune-i lui Marius inainte sa incepi** - planul presupune ca exista.
Conexiunea Oracle se probeaza cu:
`DO test_init_env_auto_roafacturare WITH 'CENTRAL','MARIUSM_AUTO','ROMFASTSOFT'`
## Pasul 4 - promptul de pornire
Da-i sesiunii noi exact textul asta:
---
Continua planul din `docs\plan_qa_factura_aviz.md`. Starea curenta e in
`docs\progres_qa_factura.md` - citeste-le pe amandoua inainte de orice.
S0 e terminat (prototipul de context/handoff). Urmeaza **S1: baseline QA**, si e primul story
care se face pe masina asta, cu UI vizibil.
Inainte sa incepi, ruleaza verificarile de mediu din `PORNIRE.md` pasul 3 si spune-mi daca ceva
lipseste.
Reguli care se aplica de la primul pas, nu dupa ce acumulezi context:
- Orice investigatie, editare, rulare de teste se deleaga unui subagent. Tu orchestrezi.
- Nu atinge `combosql` din `COMUN\clase\_cb_base.vc2` - e clasa de baza a tuturor produselor ROA.
Comportamentul nou de cautare se suprascrie in `combosql_cautare` din `ofacturare.vc2`.
- `.vc2` se editeaza cu script Python binar, niciodata `sed -i` (sterge CRLF-urile) si niciodata
Edit pe linii cu octeti >0x7F (diacriticele sunt cp1250, nu cp1252).
- Write-back doar `txt2vcx.ps1 -ProjectRoot <radacina ROAFACTURARE>`. Fara `-ProjectRoot` scrie
in alt produs.
- Dupa fiecare write-back: `MODIFY CLASS` + captura, si inchide designerul inainte de urmatorul.
- Commit dupa fiecare story, pe branch de lucru, fara push. Mandatul e dat: nu cere aprobare per
story. Exceptie: mockup-ul de la S4 se arata lui Marius inainte de implementare.
- Actualizeaza `docs\progres_qa_factura.md` dupa FIECARE story, nu la final.
- La ~250k context: opreste lucrul, scrie handoff pe disc, preda. „Mai am putin" nu e motiv de
amanare.
S1 nu modifica niciun cod. Produce doar dovada vizuala si scenariile, in
`docs\qa_factura_baseline.md` + capturi.
---
## Ce sa NU refaca sesiunea noua
Analiza e platita deja, e in `docs\qa_factura_harta.md` (harta de cod, cu fisier:linie),
`docs\qa_factura_unelte.md` (harness UI, headless, Oracle, hook-uri) si
`docs\qa_factura_hooks_ref.md` (referinta de hook-uri). Se citesc, nu se refac.
Doua lucruri stabilite si neredeschise:
- Pragul de 3 caractere la cautare = proprietatea `ncharcountbegin=2`, nu logica.
- Duplicatele de articol vin din Oracle (`pack_facturare.cursor_preturi`), nu din VFP. S3 e
blocat pana se citeste corpul pachetului din `ALL_SOURCE` pe MARIUSM_AUTO.

View File

@@ -1,227 +0,0 @@
# Diff - conturi 667/709 + analitice pe articol (COMUN)
Data: 2026-09-17. Fisiere SVN de comis: `programe/ofacturare_comun.prg`, `clase/onom_articole.vcx` + `.vct` (regenerate din `.vc2` prin txt2vcx; write-back verificat byte-identic CRLF-normalizat).
```diff
diff --git a/clase/onom_articole.vc2 b/clase/onom_articole.vc2
index 613f562..a263fd3 100644
--- a/clase/onom_articole.vc2
+++ b/clase/onom_articole.vc2
@@ -712,7 +712,9 @@ DEFINE CLASS frm_catalog_articole_nou AS _frmbase OF "_frm_base.vcx"
*< OBJECTDATA: ObjPath="_pageframe1.Page3.Cb_tx_proc_tvav" UniqueID="" Timestamp="" />
*< OBJECTDATA: ObjPath="_pageframe1.Page3.Cb_tx_venit" UniqueID="" Timestamp="" />
*< OBJECTDATA: ObjPath="_pageframe1.Page3.Clb_tx_scd" UniqueID="" Timestamp="" />
+ *< OBJECTDATA: ObjPath="_pageframe1.Page3.Clb_tx_scd.Text1" UniqueID="" Timestamp="" />
*< OBJECTDATA: ObjPath="_pageframe1.Page3.Clb_tx_scc" UniqueID="" Timestamp="" />
+ *< OBJECTDATA: ObjPath="_pageframe1.Page3.Clb_tx_scc.Text1" UniqueID="" Timestamp="" />
*< OBJECTDATA: ObjPath="_pageframe1.Page3.Clb_tx_cont_dedus" UniqueID="" Timestamp="" />
*< OBJECTDATA: ObjPath="_pageframe1.Page3.Lb_info_conturi" UniqueID="" Timestamp="" />
*< OBJECTDATA: ObjPath="Cus_odata_parteneri_articole_coduri" UniqueID="" Timestamp="" />
@@ -1586,6 +1588,18 @@ DEFINE CLASS frm_catalog_articole_nou AS _frmbase OF "_frm_base.vcx"
Lb_simplu1.Name = "Lb_simplu1"
*< END OBJECT: ClassLib="lb_tx.vcx" BaseClass="container" />
+ ADD OBJECT '_pageframe1.Page3.Clb_tx_scc.Text1' AS textbox WITH ;
+ Format = "!k", ;
+ Height = 23, ;
+ Left = 302, ;
+ MaxLength = 4, ;
+ Name = "Text1", ;
+ TabIndex = 2, ;
+ ToolTipText = "Cont analitic (optional)", ;
+ Top = 3, ;
+ Width = 74
+ *< END OBJECT: BaseClass="textbox" />
+
ADD OBJECT '_pageframe1.Page3.Clb_tx_scd' AS clb_tx_simplu WITH ;
Height = 29, ;
Left = 9, ;
@@ -1604,6 +1618,18 @@ DEFINE CLASS frm_catalog_articole_nou AS _frmbase OF "_frm_base.vcx"
Lb_simplu1.Name = "Lb_simplu1"
*< END OBJECT: ClassLib="lb_tx.vcx" BaseClass="container" />
+ ADD OBJECT '_pageframe1.Page3.Clb_tx_scd.Text1' AS textbox WITH ;
+ Format = "!k", ;
+ Height = 23, ;
+ Left = 302, ;
+ MaxLength = 4, ;
+ Name = "Text1", ;
+ TabIndex = 2, ;
+ ToolTipText = "Cont analitic (optional)", ;
+ Top = 3, ;
+ Width = 74
+ *< END OBJECT: BaseClass="textbox" />
+
ADD OBJECT '_pageframe1.Page3.Lb_info_conturi' AS _label WITH ;
Caption = "Necompletate: debit 4111, credit contul de venit al articolului", ;
Left = 9, ;
@@ -1934,6 +1960,8 @@ DEFINE CLASS frm_catalog_articole_nou AS _frmbase OF "_frm_base.vcx"
This._pageframe1.Page3.Clb_tx_pret_ctva.Text_simplu1.ControlSource = "Thisform.oPretNom.nPretCtva"
This._pageframe1.Page3.Clb_tx_scd.Text_simplu1.ControlSource = "Thisform.oPretNom.cScd"
This._pageframe1.Page3.Clb_tx_scc.Text_simplu1.ControlSource = "Thisform.oPretNom.cScc"
+ This._pageframe1.Page3.Clb_tx_scd.Text1.ControlSource = "Thisform.oPretNom.cAscd"
+ This._pageframe1.Page3.Clb_tx_scc.Text1.ControlSource = "Thisform.oPretNom.cAscc"
This._pageframe1.Page3.Clb_tx_cont_dedus.Text_simplu1.ControlSource = "Thisform.oPretNom.cContDedus"
If Used('crsvenchelt')
Use In crsvenchelt
diff --git a/programe/ofacturare_comun.prg b/programe/ofacturare_comun.prg
index 7b6b46b..efd2002 100644
--- a/programe/ofacturare_comun.prg
+++ b/programe/ofacturare_comun.prg
@@ -74,6 +74,10 @@
*!* marius.mutu
*!* + verifica_cont_venit_linie - verificare prietenoasa a contului de venit rezolvat pentru o linie
*!* de factura, la adaugarea articolului
+*!* 17.09.2026
+*!* agent
+*!* cus_pret_nomenclator: conturi analitice (cAscd/cAscc) in nota de vanzare a articolului;
+*!* apelul RPC devine pack_preturi.gaseste_creeaza_nota_vanzare (overload 6 arg cu analitice)
***************************************************************************************************************
**** Clase:
@@ -2375,6 +2379,8 @@ Define Class cus_pret_nomenclator As Custom
nIdNota = .NULL.
cScd = []
cScc = []
+ cAscd = []
+ cAscc = []
cContDedus = []
cEroare = []
@@ -2410,6 +2416,8 @@ Define Class cus_pret_nomenclator As Custom
This.nIdNota = .NULL.
This.cScd = []
This.cScc = []
+ This.cAscd = []
+ This.cAscc = []
This.cContDedus = []
If This.asigura_politica() <= 0
Return .F.
@@ -2450,7 +2458,7 @@ Define Class cus_pret_nomenclator As Custom
Use In crspolart
Endif
If !Isnull(This.nIdNota)
- lcSql = [select n.scd, n.scc from crm_note_vanzari v, note_contabile n ] + ;
+ lcSql = [select n.scd, n.ascd, n.scc, n.ascc from crm_note_vanzari v, note_contabile n ] + ;
[where v.id_nota = ] + Alltrim(Str(This.nIdNota)) + [ and n.id_set = v.id_set]
If Used('crsnotapol')
Use In crsnotapol
@@ -2459,6 +2467,8 @@ Define Class cus_pret_nomenclator As Custom
If lnSucces >= 0 And Reccount('crsnotapol') > 0
This.cScd = Alltrim(Nvl(crsnotapol.scd,[]))
This.cScc = Alltrim(Nvl(crsnotapol.scc,[]))
+ This.cAscd = Alltrim(Nvl(crsnotapol.ascd,[]))
+ This.cAscc = Alltrim(Nvl(crsnotapol.ascc,[]))
Endif
If Used('crsnotapol')
Use In crsnotapol
@@ -2482,11 +2492,20 @@ Define Class cus_pret_nomenclator As Custom
* valideaza conturile si pretul curente; mesaj + .F. la refuz
Procedure valideaza
This.cEroare = []
- If (!Empty(This.cScd) And Empty(This.cScc)) Or (Empty(This.cScd) And !Empty(This.cScc))
- This.cEroare = [Trebuie completate ambele conturi (debit si credit) sau niciunul.]
+ If !Empty(This.cAscd) And Empty(This.cScd)
+ This.cEroare = [Contul analitic debitor nu poate fi completat fara contul sintetic debitor.]
Else
- If This.nPretFtva < 0 Or This.nPretCtva < 0
- This.cEroare = [Pretul nu poate fi negativ.]
+ If !Empty(This.cAscc) And Empty(This.cScc)
+ This.cEroare = [Contul analitic creditor nu poate fi completat fara contul sintetic creditor.]
+ Endif
+ Endif
+ If Empty(This.cEroare)
+ If (!Empty(This.cScd) And Empty(This.cScc)) Or (Empty(This.cScd) And !Empty(This.cScc))
+ This.cEroare = [Trebuie completate ambele conturi (debit si credit) sau niciunul.]
+ Else
+ If This.nPretFtva < 0 Or This.nPretCtva < 0
+ This.cEroare = [Pretul nu poate fi negativ.]
+ Endif
Endif
Endif
If !Empty(This.cEroare)
@@ -2505,14 +2524,16 @@ Define Class cus_pret_nomenclator As Custom
Return .F.
Endif
If !Empty(This.cScd) And !Empty(This.cScc)
- Private pcScd, pcScc, pnIdUtil
+ Private pcScd, pcScc, pcAscd, pcAscc, pnIdUtil
pcScd = This.cScd
pcScc = This.cScc
+ pcAscd = This.cAscd
+ pcAscc = This.cAscc
pnIdUtil = gnIdUtil
pnIdNota = 0
- lcSql = [begin pack_preturi.gaseste_sau_creeaza_nota_vanzare(?pcScd,?pcScc,?pnIdUtil,?@pnIdNota); end;]
+ lcSql = [begin pack_preturi.gaseste_creeaza_nota_vanzare(?pcScd,?pcAscd,?pcScc,?pcAscc,?pnIdUtil,?@pnIdNota); end;]
lnSucces = goExecutor.oExecute(lcSql)
- Release pcScd, pcScc, pnIdUtil
+ Release pcScd, pcScc, pcAscd, pcAscc, pnIdUtil
If lnSucces < 0
AMESSAGEBOX(goExecutor.oPrelucrareEroare(),16,"Eroare")
Release pnIdArticol, pnPretFtva, pnPretCtva, pnProcTvav, pnIdVenchelt, pnIdNota, pnScrieNota
@@ -2555,7 +2576,9 @@ Define Class cus_pret_nomenclator As Custom
Local lcSql, lnSucces
This.cScd = []
This.cScc = []
- lcSql = [select n.scd, n.scc from crm_politici_preturi p, crm_note_vanzari v, note_contabile n ] + ;
+ This.cAscd = []
+ This.cAscc = []
+ lcSql = [select n.scd, n.ascd, n.scc, n.ascc from crm_politici_preturi p, crm_note_vanzari v, note_contabile n ] + ;
[where p.id_pol = ] + Alltrim(Str(tnIdPol)) + [ and v.id_nota = p.id_nota and n.id_set = v.id_set]
If Used('crsnotapolstoc')
Use In crsnotapolstoc
@@ -2564,6 +2587,8 @@ Define Class cus_pret_nomenclator As Custom
If lnSucces >= 0 And Reccount('crsnotapolstoc') > 0
This.cScd = Alltrim(Nvl(crsnotapolstoc.scd,[]))
This.cScc = Alltrim(Nvl(crsnotapolstoc.scc,[]))
+ This.cAscd = Alltrim(Nvl(crsnotapolstoc.ascd,[]))
+ This.cAscc = Alltrim(Nvl(crsnotapolstoc.ascc,[]))
Endif
If Used('crsnotapolstoc')
Use In crsnotapolstoc
@@ -2573,8 +2598,10 @@ Define Class cus_pret_nomenclator As Custom
* valideaza (ambele conturi sau niciunul) si scrie nota implicita a politicii date
Procedure salveaza_nota_politica
- Lparameters tnIdPol, tcScd, tcScc
+ Lparameters tnIdPol, tcScd, tcScc, tcAscd, tcAscc
Local lcSql, lnSucces
+ tcAscd = Iif(Type('tcAscd')='C', tcAscd, [])
+ tcAscc = Iif(Type('tcAscc')='C', tcAscc, [])
If (!Empty(tcScd) And Empty(tcScc)) Or (Empty(tcScd) And !Empty(tcScc))
This.cEroare = [Trebuie completate ambele conturi (debit si credit) sau niciunul.]
AMESSAGEBOX(This.cEroare,0+48,"Atentie")
@@ -2583,16 +2610,18 @@ Define Class cus_pret_nomenclator As Custom
If Empty(tcScd) And Empty(tcScc)
Return .T.
Endif
- Private pcScd, pcScc, pnIdUtil, pnIdNota, pnIdPol
+ Private pcScd, pcScc, pcAscd, pcAscc, pnIdUtil, pnIdNota, pnIdPol
pcScd = tcScd
pcScc = tcScc
+ pcAscd = tcAscd
+ pcAscc = tcAscc
pnIdUtil = gnIdUtil
pnIdNota = 0
- lcSql = [begin pack_preturi.gaseste_sau_creeaza_nota_vanzare(?pcScd,?pcScc,?pnIdUtil,?@pnIdNota); end;]
+ lcSql = [begin pack_preturi.gaseste_creeaza_nota_vanzare(?pcScd,?pcAscd,?pcScc,?pcAscc,?pnIdUtil,?@pnIdNota); end;]
lnSucces = goExecutor.oExecute(lcSql)
If lnSucces < 0
AMESSAGEBOX(goExecutor.oPrelucrareEroare(),16,"Eroare")
- Release pcScd, pcScc, pnIdUtil, pnIdNota, pnIdPol
+ Release pcScd, pcScc, pcAscd, pcAscc, pnIdUtil, pnIdNota, pnIdPol
Return .F.
Endif
pnIdPol = tnIdPol
@@ -2601,7 +2630,7 @@ Define Class cus_pret_nomenclator As Custom
If lnSucces < 0
AMESSAGEBOX(goExecutor.oPrelucrareEroare(),16,"Eroare")
Endif
- Release pcScd, pcScc, pnIdUtil, pnIdNota, pnIdPol
+ Release pcScd, pcScc, pcAscd, pcAscc, pnIdUtil, pnIdNota, pnIdPol
Return (lnSucces >= 0)
Endproc && salveaza_nota_politica
Enddefine
```

View File

@@ -1,12 +0,0 @@
# Erori deschise — formular facturare unificat
Erori observate la rulare, neinvestigate inca. Se sterge intrarea cand e reparata.
## 1. `Alias 'CRSGESTIUNE' is not found.` la parasirea combo-ului de gestiune
- **Unde:** `FRM_FACTURARE_ARTICOLE2.GRD_FACTURA.CGESTIUNE.CCBOGESTIUNE.LOSTFOCUS`, linia 5
- **Linia:** `REPLACE nume_gestiune WITH crsGestiune.nume_gestiune, id_gestiune WITH crsGestiune.id_gestiune IN crsFactura`
- **Mediu:** MARIUSM_AUTO, 09.09.2026
- **De verificat:** cine creeaza `crsGestiune` si pe ce cale de cod ajunge `LostFocus` sa ruleze
inainte (sau dupa inchiderea) cursorului — probabil combo-ul isi pierde focusul intr-un context
in care cursorul nu a fost creat sau a fost deja inchis.

View File

@@ -1,193 +0,0 @@
# Verificare "function hooks" — Claude Code 2.1.274 (nativ, win32-x64)
Sursa de verificat: https://claudefa.st/blog/tools/hooks/function-hooks (blog tert, neoficial).
Metoda: (1) incercare de rulare reala `/plugin-types`; (2) cautare de siruri si context in
binarul nativ instalat, `C:\Users\mmari\.local\bin\claude.exe` (233691808 octeti, Bun-compiled,
comitul `1efcc1361e64`). Fiecare constatare de mai jos e marcata cu sursa ei.
## 1. Rularea comenzii cerute
```
CLAUDE_CODE_ENABLE_FUNCTION_HOOKS=1 claude -p "/plugin-types ./types"
```
Rezultat: **nu s-a generat niciun fisier**. Raspunsul modelului a fost text conversational
("Ready — no task given yet." / la reincercare cu `--output-format json`: `"I'm ready. What
would you like me to work on?"`). Niciun folder `types/` sau `.claude/types/` nu a aparut in
scratchpad dupa rulare (verificat cu `ls`).
**Cauza identificata, nu specifica function hooks**: am testat control cu `claude -p "/help"`
(comanda locala cea mai de baza din tot CLI-ul, intotdeauna inregistrata, fara nicio poarta de
feature-flag). A esuat identic — modelul a primit "/help" ca text mangled si a raspuns
conversational, in loc sa afiseze ajutorul. **Concluzie: invocarea `claude -p "<text>"` din acest
mediu nu ruteaza deloc prin dispecerul de comenzi locale ("slash commands")** — cererea merge
direct la model ca prompt text. Din `--debug-file`, header-ul de atribuire arata
`cc_entrypoint=sdk-cli`, adica sesiunea porneste prin calea SDK, nu prin REPL-ul interactiv complet;
acolo comenzile locale par sa nu fie interceptate. Deci esecul lui `/plugin-types` **nu e dovada
ca function hooks sunt dezactivate** — e o limitare a modului de invocare folosit aici, valabila
si pentru comenzi complet neutre ca `/help`. N-am putut testa varianta interactiva (fara TTY in
acest mediu).
`claude --help` si `claude plugin --help` (rulate separat, capturate integral) **nu listeaza**
nicio comanda `plugin-types` la nivel de CLI top-level — motivul e ca `/plugin-types` e o comanda
*in-sesiune* ("slash command"), nu o subcomanda a executabilului `claude`, deci absenta ei din
`--help` e normala si nu spune nimic despre activare.
## 2. Ce exista REAL in binar (cautare de siruri, cu offset de octet verificabil)
Nu s-au generat `.d.ts`, deci n-am putut citi declaratii TypeScript complete ca "dovada
canonica" ceruta in sarcina. In schimb, am gasit in binar cod sursa minificat (nu doar siruri
izolate) care confirma mecanismul de facto. Tot ce urmeaza e citat verbatim din binar, cu offset.
### 2a. Flag-ul si poarta lui reala
La offset ~103270720 (grep -a -b), blocul de cod al flag-ului:
```
var kYe="tengu_plugin_hooks_modules";
var Azt=()=>!1;
var AFe=()=>a.CLAUDE_CODE_ENABLE_FUNCTION_HOOKS??P(kYe,Azt());
var h8=()=>AFe()&&!qb()&&!Br("hooks")&&!zm();
```
Interpretare directa din cod:
- flag-ul GrowthBook real se numeste **`tengu_plugin_hooks_modules`**, default **oprit** (`Azt=()=>!1`).
- `CLAUDE_CODE_ENABLE_FUNCTION_HOOKS` e o suprascriere locala peste acel flag (`??`) — confirma
exact ce zice blogul despre numele variabilei de mediu.
- **dar** poarta finala folosita in productie, `h8()`, cere si `!qb() && !Br("hooks") && !zm()`
— trei conditii suplimentare. Numele acestor functii sunt minificate si reciclate in zeci de
module din bundle (acelasi nume `qb`/`Br`/`zm` apare cu corpuri complet diferite in alte
scope-uri), deci **nu pot atribui cu certitudine ce verifica exact aici** fara deminificare
completa — onest: aceasta e limita cercetarii, nu o concluzie.
- Alaturi, la acelasi offset, un tabel de atribuire a sursei flag-ului pentru afisare in UI:
`{override:"from a local override", payload:"from GrowthBook (this session's payload)",
disk:"from GrowthBook (the disk cache of an earlier session)", disabled:"from the default
(GrowthBook is off ...)"}` — confirma ca e un flag GrowthBook cu override local, exact
mecanismul descris de blog.
### 2b. Comanda `/plugin-types` — confirmata REAL, dar NEgatata de flag
La offset 203438732, definitia completa a comenzii (citat verbatim):
```
var mer=Object.freeze({type:"local",name:"plugin-types",
description:"Write claude-code.d.ts, claude-code-plugins.d.ts and claude-code-mcp.d.ts:
the plugin API's TypeScript declarations, the enabled plugins' type contracts and the
inputs of the connected MCP tools, for typing a hooks module against this session",
argumentHint:"[dir]", supportsNonInteractive:!0,
load:()=>import("B:/~BUN/root/chunk-0rpsm23r.js")});
```
Important: **acest obiect n-are nicio conditie `isEnabled`/gate legata de `h8()` sau de
flag** — e inregistrata neconditionat. Descrierea confirma explicit sintagma "hooks module"
din blog: comanda exista ca sa tipizeze "a hooks module against this session". Deci comanda in
sine e reala si documentata intern, indiferent de starea flag-ului; problema empirica de mai
sus (sectiunea 1) e doar despre modul de invocare `-p` din acest mediu.
### 2c. Lista REALA de evenimente si API-uri ("scan manifest"), gasita ca date, nu ca documentatie
La offset 223160560 exista un manifest static de tip "scan" folosit (aparent) pentru analiza
statica / sandboxing a unui modul de plugin, cu lista completa si explicita:
```js
scan: {
hooks: ["session.start","ui.render","command.run","ui.close","ui.focus","ui.scroll",
"tool.call","prompt.submit"],
calls: ["clock.after","clock.every","clock.now","command.register","fs.list","fs.read",
"fs.stat","process.run","session.id","session.messages","store.get","store.set",
"telemetry.log","telemetry.mark","ui.close","ui.invalidate","ui.log","ui.open",
"ui.resolve","ui.status"]
}
```
Aceasta e cea mai tare dovada gasita — e o **lista de date**, nu un string de proza, deci
foarte probabil chiar suprafata reala (sau foarte apropiata) a API-ului de hooks module:
- **evenimente de hook confirmate**: `session.start`, `ui.render`, `command.run`, `ui.close`,
`ui.focus`, `ui.scroll`, `tool.call`, `prompt.submit` (8 la numar).
- **`turn.complete` si `turn.start` NU sunt in lista de evenimente de hook**, desi cele doua
siruri exista in alta parte a binarului (24, respectiv 68 aparitii) — apartin mecanismului
intern de motor ("engine turn 1 start/end", vazut si in log-ul de debug la rularea reala),
nu suprafetei de hooks expuse pluginurilor.
- **`prompt.submit` e confirmat**, si separat, la offset 206959908, exista codul intern real
care implementeaza acest hook point: un pipeline `core`/`managed` cu posibilitate de a
rescrie textul promptului (`"prompt.submit: text rewritten by a hook (...)"`) sau de a-l
bloca (`"Prompt dropped by a hook: ..."`). Semnatura exacta gasita e apelul
`.prompt.submit({submission, origin, turnId, shouldWait})` — **nu am gasit litera `$` ca
alias/receiver exact** in siruri; forma `$.prompt.submit({text})` din blog e plauzibila ca
sugar-syntax peste acest mecanism, dar nu e confirmata verbatim.
- **API-uri de tip "calls" confirmate ca namespace-uri cu metode**: `fs.read/list/stat`
(deci `$.fs` probabil e un namespace, nu o valoare simpla), `store.get/set`,
`clock.after/every/now`, `session.id/messages` — coincide cu ce cerea sarcina sa verific
(`$.session.messages()`, `$.fs`, `$.store`, `$.clock.every`), dar iar, prefixul `$.` in sine
nu apare ca sir literal langa aceste nume — e o inferenta rezonabila, nu o citare directa.
### 2d. Tokeni / context / usage / compactare — CAUTATE EXPLICIT, NU GASITE
Am cautat explicit `token`, `usage`, `context`, `compact` in vecinatatea manifestului de mai
sus si in restul zonei de "function hooks". **Niciunul dintre cele 20 de `calls` sau 8 `hooks`
de mai sus nu are legatura cu tokeni, context window, usage sau compactare.** Sirurile
`contextWindow` (8 aparitii) si `compact` (405 aparitii) exista din abundenta in binar, dar in
alte module (afisarea usage-ului in `--output-format json`, autocompact intern al motorului
— vazut si in logul de debug: `autocompact: tokens=[REDACTED] level=ok effectiveWindow=980000`),
**nu ca hook sau call disponibil unui modul de function hooks**. Nu exista dovada in acest
binar ca un modul de hooks ar putea citi tokenii ramasi sau starea de compactare.
### 2e. Forma `register` si `hooks/hooks.json`
Doua descoperiri, ambele reale, dar **doua lucruri diferite**:
1. **`hooks.json` din binar (27 aparitii) e manifestul VECHI/existent de plugin-hooks**
(PreToolUse, SessionStart etc. — cel folosit deja de pluginul `ponytail` din acest proiect,
vazut in logul de debug: `Read manifest hooks for plugin ponytail (enabled=true):
./hooks/claude-codex-hooks.json`). Contextul din binar confirma: sirul e `"plugin
hooks.json"`, langa mesaje despre `SKILL.md` si actualizarea regulilor de auto-mode/permisiuni
— sistemul clasic de hooks pe evenimente de tool-use, nesuprapus cu "function hooks".
2. **Forma `register` pentru "function hooks" (modulul nou)**, gasita in doua exemple interne
reale de pluginuri deja construite cu acest mecanism:
- `tengu_quiet_dolphin` — "panoul de diff ca plugin: /diff", cu
`isAvailable:()=>At` unde `At=()=>h8()&&lu(Rr(),bt)` — **confirma ca h8() (poarta de
function hooks descrisa la 2a) chiar gateaza un plugin real deja construit**, nu doar un
flag mort.
- `tengu_tips_mod` — "spinner tips as a plugin", cu forma exacta:
```js
var P=(e)=>({register:(o)=>{e.registerHooks(o,x())}});
```
adica modulul exporta un `register(engine)` care apeleaza
`engine.registerHooks(implementareHooks, apiSuplimentar)`. E cea mai apropiata dovada
gasita de "forma exacta a functiei register", dar e exemplul unui plugin intern concret,
nu semnatura generica documentata explicit undeva ca atare.
## VERDICT
Se poate construi o bucla "prag context -> scrie handoff -> /clear -> reia din handoff" complet
automat, FARA interventia utilizatorului, folosind acest surface?
**Nu, nu cu ce am vazut efectiv in acest binar.** Motive, punct cu punct:
1. Suprafata de hooks confirmata (`session.start`, `ui.*`, `command.run`, `tool.call`,
`prompt.submit`) **nu contine niciun eveniment sau apel legat de prag de context, tokeni,
usage sau compactare** (sectiunea 2d) — deci un modul de hooks n-ar avea de unde sa afle
"am trecut de 200k tokeni" din interior. Ar trebui sa deduca asta indirect (ex. numarand
caractere din `session.messages()`), fara nicio garantie ca se potriveste cu tokenizarea
reala sau cu `autocompact` intern.
2. Nu exista niciun hook de tipul "inainte de compactare" / "sesiune se inchide" in lista —
deci nu exista un punct de agatare curat pentru "scrie handoff chiar inainte sa se piarda
contextul".
3. `/clear` (sau relansarea sesiunii) nu apare in `calls`-urile confirmate (`command.register`
exista, dar a inregistra o comanda noua nu e totuna cu a declansa `/clear` programatic din
interiorul unui hook).
4. Flag-ul e in continuare oprit implicit (`tengu_plugin_hooks_modules` default `!1`),
marcat "early access" chiar in textul din comanda `/plugin-types` insasi
("module 'claude-code', early access: it may change between releases") — deci si daca
API-ul de mai sus ar acoperi cazul, Anthropic il trateaza explicit ca nestabil.
5. **Nu am reusit sa validam empiric nimic din surface** (n-am putut genera `.d.ts` din cauza
limitarii de mediu descrise la sectiunea 1) — tot ce e mai sus e din citirea codului
minificat, nu din declaratii TypeScript generate si citite ca atare, cum cerea procedura.
Ce ar lipsi/ar trebui reincercat ca sa se stie sigur: rulare **interactiva** reala (TTY, nu
`-p`/SDK) cu `CLAUDE_CODE_ENABLE_FUNCTION_HOOKS=1`, ca sa se vada daca `/plugin-types` chiar
scrie fisierele si daca `h8()` se evalueaza `true` in acel context — asta ar da acces la
declaratiile TypeScript reale si ar inchide toate necunoscutele de mai sus (in special conditiile
`qb()`/`Br("hooks")`/`zm()` neatribuite cu certitudine).

View File

@@ -1,276 +0,0 @@
# Handoff — conturi 667/709 + analitice pe articol (catalog articole)
Data: 2026-09-17. Sesiune: analiza + reproducere + proiectare. **Zero cod de productie modificat.**
## 1. Cerinta
In facturarea din lista de preturi „catalog articole" (politica de stoc), un articol cu
`NOM_ARTICOLE.CONT = 667` (sau `709`) da eroarea „articolul nu are cont de venit".
Se cere:
- `667`/`709` considerate conturi valide (nu blocate);
- nota generata sa fie `667/709 = 4111` **cu suma negativa** (nu `4111 = <cont de venit>` standard);
- pe articol sa se poata completa si **conturile analitice** (`ASCD`/`ASCC`), nu doar debit/credit,
ca nota sa fie completa.
## 2. Decizii luate (NU se redeschid)
- **Fix = nota per-articol creata automat** (nu nota la nivel de politica, care s-ar aplica tuturor
articolelor din lista). Legatura: `CRM_POLITICI_PRET_ART.ID_NOTA`.
- **`SCC` (contul de client) vine din optiunea `RF_CONT_ART_FARA_POL`**, fallback `4111`.
- **Analiticele se salveaza in nota** (`NOTE_CONTABILE.ASCD/ASCC`), nu pe articol/politica.
- Semnul negativ **nu are nevoie de cod nou**: exista deja in `scrie_nota` (vezi §4).
- Se **adauga un overload** la `gaseste_sau_creeaza_nota_vanzare` (6 arg), ca sa nu rupa apelantii
vechi de 4 arg si sa nu fie nevoie de deploy sincronizat EXE/DB.
## 3. Cauza (dovada pe cod)
- Pre-check VFP la adaugarea liniei: `verifica_cont_venit_linie`
(`D:\ROA\ROAFACTURARE\COMUN\programe\ofacturare_comun.prg:842`), apelat din
`COMUN\clase\ofacturare.vc2:18737` si `:18972`. Rezolva
`nvl(case when a.id_pol=stoc and a.id_nota is null then cont_venit_articol_stoc(art) end, d.scc)`;
mesaj cand e NULL.
- Derivare fara politica: `deriva_cont_venit_fara_pol` (`ofacturare_comun.prg:815`) — pentru
`cont` 6xx intoarce `667` (dar e folosit ca SCC, directia e inversa).
- Deducere Oracle: `PACK_FACTURARE.cont_venit_articol_stoc`
(`D:\ROA\DATABASE\SCRIPTURI_CLAR\2026\09\ff_2026_09_16_04_COMUN_PACK_FACTURARE.sql:17794`)
trateaza doar `7xx` (il intoarce) si `2xx/3xx` (prin `CORESP_CONT_VENCHELT`); **`6xx` → NULL**.
- Garda Oracle: FACT-033 cand `V_SCC` e NULL (`ff_2026_09_16_04_...:7896-7901`).
- Nota articolului e deja folosita la facturare: `cursor_articol` ia `D.SCD/D.SCC/D.ASCD/D.ASCC`
(`ff_2026_09_16_04_...:7545-7546, 7864-7865, 7890-7893`), iar `scrie_nota` primeste
`V_ASCD/V_ASCC`.
## 4. Mecanism deja existent (nu se rescrie)
- `scrie_nota`, `ff_2026_09_16_04_...:12838` si `:12879-12887`: daca
`V_SCD IN ('667','267','2678','709')` pe factura → `V_SEMN := -1` (suma negativa).
- `scrie_tva`, `ff_2026_09_16_04_...:13064-13078`: comuta pe `4111/4427` pentru aceleasi conturi.
- Componenta noua in `PACK_PRETURI` (`ff_2026_09_16_05_COMUN_PACK_PRETURI.sql`):
`gaseste_sau_creeaza_nota_vanzare` (`:1305`), `salveaza_pret_nomenclator` (`:1261`),
`asigura_politica_stoc` (`:1391`), `seteaza_nota_politica_stoc` (`:1440`).
Azi `gaseste_sau_creeaza_nota_vanzare` scrie fortat `ascd/ascc` NULL (`:1339-1340, 1387`).
## 5. Plan de implementare (confirmat, NEINCEPUT)
### Pas 1 — Oracle: script nou `D:\ROA\DATABASE\SCRIPTURI_CLAR\2026\09\ff_2026_09_17_01_COMUN_PACK_PRETURI.sql`
- overload `gaseste_creeaza_nota_vanzare(tcScd, tcAscd, tcScc, tcAscc, tnIdUtil, tnIdNota OUT)`;
cautare pe toate 4 conturile; la creare scrie `ascd/ascc` reale.
- pastreaza si signatura veche de 4 arg `(tcScd, tcScc, tnIdUtil, tnIdNota OUT)`, sub acelasi nume
scurtat (numele original de 32 caractere nu compila pe Oracle 10/11).
- procedura noua `asigura_nota_articol_disc(V_ID_ARTICOL, V_ID_UTIL)`:
`cont ∈ (667,267,2678,709)` → `gaseste_sau_creeaza_nota_vanzare(cont, ascd, client, ascc, ...)`,
`client = nvl(pack_sesiune.getoptiunefirma('RF_CONT_ART_FARA_POL'),'4111')`;
`merge into crm_politici_pret_art set id_nota=... where id_pol=pack_facturare.nid_politica_stoc
and id_articol=... and id_nota is null` (nu suprascrie nota pusa de utilizator).
- apel in `salveaza_pret_nomenclator` cand `V_ID_NOTA IS NULL AND V_SCRIE_NOTA = 1`.
- backfill idempotent pentru randurile existente din politica de stoc.
- `exec pack_migrare.UpdateVersiune('ff_2026_09_17_01_COMUN_PACK_PRETURI'); commit;`
- compatibil Oracle 10.2. Vezi skill `roa-oracle-migration`.
### Pas 2 — VFP clasa `cus_pret_nomenclator` (`COMUN\programe\ofacturare_comun.prg`)
- clasa incepe la `:2366`; proprietati: `asigura_politica :2382`, `incarca :2400`,
`valideaza :2483`, `salveaza :2500`, `incarca_nota_politica :2553`, `salveaza_nota_politica :2575`.
- proprietati noi `cAscd`/`cAscc`; `incarca` si `incarca_nota_politica` le citesc din nota;
`valideaza` — analitic optional, dar cer sintetic daca exista analitic; `salveaza`/`salveaza_nota_politica`
le trimit in RPC.
- apeluri RPC de actualizat: `:2513` si `:2591` (`gaseste_creeaza_nota_vanzare`).
- NU se atinge `do_scrie_articole` (argumentul `cont_venit` ramane cum e, `:14279`/`:20246`).
### Pas 3 — VFP formular `COMUN\clase\onom_articole.vc2`, pagina Vanzare (Page3)
- `Clb_tx_scd` (`:1589`, label „Cont debitor") → `ADD ...Text1` legat la `Thisform.oPretNom.cAscd`.
- `Clb_tx_scc` (`:1571`, label „Cont creditor") → `Text1` legat la `cAscc`.
- tipar de copiat: `Clb_tx_cont.Text1` (`:1104`, ControlSource `porec.acont`).
- init `oPretNom` + ControlSource-uri: `:1931-1944`.
- write-back `.vc2 -> .vcx` obligatoriu (`txt2vcx`), cp1252. Vezi skill `roa-vfp-text-edit`.
### Pas 4 — Test headless pe `MARIUSM_AUTO`
- salveaza articol cont=667 fara acont (+ analitice), verifica `verifica_cont_venit_linie` = `[]`
si rezolvarea tip `cursor_articol` = `SCD=667/SCC=4111/ASCD/ASCC`.
- **tranzactie manuala obligatorie** (`SQLSETPROP(gnHandle,"Transactions",2)`), altfel scrierile
se commmit (vezi §7).
## 6. Fisiere create in aceasta sesiune (probe, NU productie)
In `D:\ROA\ROAFACTURARE\COMUN\utile\Teste\facturare_unificat\`:
- `probe_cont_667.prg/.fxp` + `out\probe_cont_667.log`
- `probe_cont_667b.prg/.fxp` + `out\probe_cont_667b.log`
- `probe_cont_667c.prg/.fxp` + `out\probe_cont_667c.log`
- `probe_cont_667d.prg/.fxp` + `out\probe_cont_667d.log`
- `restore_pol41.prg/.fxp` + `out\restore_pol41.log`
Niciun fisier de productie (`.prg`/`.vc2`/`.vcx` din COMUN) nu a fost atins; niciun commit.
## 7. Capcane de mediu platite
- **`goExecutor`/ODBC e pe auto-commit implicit** — `UPDATE` fara
`SQLSETPROP(gnHandle,"Transactions",2)` se **commiteaza**. Am patit-o: `probe_cont_667b` a golit
`id_nota` al politicii 41; **restaurat** la `6` (`restore_pol41.log`). Orice scriere de proba:
`Transactions=2` + `ROLLBACK` la final.
- `vfp9.exe` = `C:\Program Files (x86)\Microsoft Visual FoxPro 9\vfp9.exe`.
- Precompilare izolata obligatorie inainte de rulare:
`powershell -ExecutionPolicy Bypass -File "D:\ROA\ROAFACTURARE\COMUN\utile\Teste\_precompile.ps1" -Prg "<cale.prg>"`
apoi `vfp9.exe -A "<cale.fxp>"` cu `WaitForExit`. `-A` porneste cu SAFETY ON → prima linie `SET SAFETY OFF`.
- Init mediu: `DO "D:\ROA\ROAFACTURARE\COMUN\utile\Teste\test_init_env_auto_roafacturare.prg" WITH 'CENTRAL','MARIUSM_AUTO','ROMFASTSOFT'`, apoi `gnIdUtil = 8`.
- Log-uri: `...\facturare_unificat\out\*.log`.
## 8. Date utile din baza vie (MARIUSM_AUTO, citite 2026-09-17)
- `OPTIUNI.ROAFACTURARE.ID_POL_PRET_STOC = 41` („STOC PRODUSE"), `id_nota=6` (nota 6 = `4111/7015`).
Exista si `id_pol=43` „STOC PRODUSE", tot `id_nota=6`.
- Nota `2` = `DISCOUNT`, `SCD=667`, `SCC=4111` (politica 7 „DISCOUNT").
- 16 articole cu `cont=667` in politica 41, **fara nota per-articol** (nume: DISCOUNT,
VOUCHER DISCOUNT, DISCOUNT 19%, STORNARE DISCOUNT..., etc.).
- Singura nota per-articol existenta: `art=4294507172` („A1", `cont=371`) → `id_nota=7` (`4111/704`).
- `PLCONT` 2026 contine `667`, `709`, `267`, `2678`, `4111`, `704`, `707`.
- Politici fara nota: `32`, `33`, `41` (dupa restaur, 41 are din nou nota 6).
## 9. Interzis / atentie
- Nu `git pull` in COMUN; nu `roa_sync` pe tree murdar; commit **doar** tintit si **doar** cu
aprobarea lui Marius (skill `roa-git-svn-commit`).
- Nu edita `.vcx` direct; doar `.vc2` + `txt2vcx`.
- Nu lasa scrieri necommit-uite/auto-commit pe schema partajata `MARIUSM_AUTO`.
- Deploy: DB inainte de EXE (overload-ul vechi face ordinea toleranta, dar EXE nou + DB vechi ar
esua la apelul cu parametri noi).
## 10. Comanda de test rapida (dupa implementare)
```
powershell -ExecutionPolicy Bypass -File "D:\ROA\ROAFACTURARE\COMUN\utile\Teste\_precompile.ps1" -Prg "D:\ROA\ROAFACTURARE\COMUN\utile\Teste\facturare_unificat\<proba>.prg"
# apoi: Start-Process vfp9.exe -ArgumentList '-A', '"<cale.fxp>"' -Wait (NU '-A -T ...', nu porneste)
# citeste out\<proba>.log
```
## 11. STARE EXECUTIE (2026-09-17, orchestrator + subagenti)
Pas 1-4 EXECUTATE. Zero commit, zero publicare in UPD_DATABASE, zero modificari pe schema in afara
de scriptul de migrare aplicat pe MARIUSM_AUTO.
- **Pas 1** `D:\ROA\DATABASE\SCRIPTURI_CLAR\2026\09\ff_2026_09_17_01_COMUN_PACK_PRETURI.sql` (nou,
2441 linii): overload 6 arg `gaseste_creeaza_nota_vanzare` (cauta pe toate 4 conturile,
`nvl(trim(x),'~')` pentru 10.2), procedura `asigura_nota_articol_disc` (667/267/2678/709 ->
nota `<cont>=<RF_CONT_ART_FARA_POL|4111>`, doar pe rand fara id_nota), apel in
`salveaza_pret_nomenclator` cand `V_ID_NOTA IS NULL AND V_SCRIE_NOTA=1`, backfill idempotent in
politica de stoc, `UpdateVersiune('ff_2026_09_17_01_COMUN_PACK_PRETURI')`. **Aplicat pe
MARIUSM_AUTO** (sqlplus, `@`): `PACK_PRETURI` VALID, 0 erori; backfill a legat toate cele 16
articole 667 la nota 2 (DISCOUNT 667=4111); nota 2 are in continuare un singur rand.
**Compatibilitate Oracle 10.2 (corectie 17.09):** limita de 30 de caractere pe identificator.
In `ff_2026_09_16_05` singurul nume peste limita era `gaseste_sau_creeaza_nota_vanzare` = 32
caractere (pica pe 10g/11g cu PLS-00114; MARIUSM_AUTO e pe o versiune care permite 128, de-aia
compilase). Redenumit in `gaseste_creeaza_nota_vanzare` = 28. `asigura_nota_articol_discount`
era 29 (legal), dar a fost scurtat preventiv la `asigura_nota_articol_disc` = 25. Verificat:
0 identificatori >30 in tot pachetul; fara constructii 11g+ (listagg/regexp/pivot/continue/with
etc.). **Atentie la deploy:** numele vechi dispare din pachet, deci un EXE vechi care chema
`gaseste_sau_creeaza_nota_vanzare` se rupe - componenta e noua (16.09), dar DB-ul nou trebuie
livrat impreuna cu EXE-ul nou (nu mai exista toleranta la ordinea DB-inainte-de-EXE pentru acest
apel). Audit luna 2026-09: acelasi nume de 32 caractere exista si in `ff_2026_09_15_01`
(introducerea procedurii) si `ff_2026_09_16_05` - ambele corectate identic in surse; restul
scripturilor lunii sunt curate (singurele tokenuri >30 sunt numele de script din stringul
`UpdateVersiune` si un blob base64). **Atentie:** arhiva `database_20260901_20260930.zip`
exista deja (generata 2026-09-17 00:07) si contine versiunile vechi - corectia ajunge la clienti
doar dupa republicare.
- **Pas 2** `COMUN\programe\ofacturare_comun.prg` (clasa `cus_pret_nomenclator`, ~2366-2630):
proprietati `cAscd`/`cAscc`; `incarca`/`incarca_nota_politica` le citesc din nota; `valideaza`
refuza analitic fara sintetic; `salveaza` trimite RPC-ul cu 6 argumente; `salveaza_nota_politica`
primeste `tcAscd/tcAscc` optionali (apelantul din `ofacturare.vc2:24524` ramane pe 3 arg).
- **Pas 3** `COMUN\clase\onom_articole.vc2` (+ write-back `.vcx`/`.vct` cu txt2vcx): pe Page3,
`Clb_tx_scd.Text1` si `Clb_tx_scc.Text1` (MaxLength 4, Format `!k`), ControlSource
`Thisform.oPretNom.cAscd` / `cAscc` setat in Init. Round-trip `.vcx -> .vc2` identic, +2 obiecte.
- **Pas 4** trei probe E2E (toate verzi):
- `utile\Teste\facturare_unificat\probe_e2e_cont_667.prg` - salvare reala prin clasa + rezolvare
`cursor_articol` + fallback, in tranzactie manuala cu ROLLBACK: **24 PASS / 0 FAIL**.
- `probe_e2e_cont_667_ui.prg` - smoke UI pe `frm_catalog_articole_nou` Page3 (cele doua Text1
exista si sunt legate): **10 PASS / 0 FAIL**.
- `probe_e2e_cont_667_semn.sql` - `pack_facturare.scrie_nota` cu SCD=667/ASCD=11/SCC=4111/ASCC=12
-> `ACT_TEMP.SUMA=-84.03` (semn negativ), apoi ROLLBACK. Reziduuri verificate: 0.
- Loguri: `out\probe_e2e_cont_667.log`, `out\probe_e2e_cont_667_ui.log`.
### Raman de facut (deliberat, NU in aceasta executie)
- Commit tintit pe `COMUN` (`ofacturare_comun.prg`, `onom_articole.vc2`) + commit script SVN -
**doar cu aprobarea lui Marius** (skill `roa-git-svn-commit`).
- Import/publicare in `UPD_DATABASE` + regenerare arhiva lunara (NU s-a rulat; aprobat de Marius
doar "corecteaza sursele"). Comanda (scrie 3 scripturi + arhiva septembrie, face backup):
```
powershell -ExecutionPolicy Bypass -File "D:\ROA\ROAFACTURARE\COMUN\utile\publicare_scripturi.ps1" -Luna 2026-09
```
`-DryRun` a confirmat: `FF_2026_09_15_01` MODIFICAT, `FF_2026_09_16_05` MODIFICAT,
`FF_2026_09_17_01` NOU, 26 scripturi in arhiva. DB inainte de EXE.
### Capcane noi platite
- `vfp9.exe -A -T "<fxp>"` NU porneste testul (proces mut, log gol). Forma corecta:
`Start-Process vfp9.exe -ArgumentList '-A','"<fxp>"'` (ca in `_precompile.ps1`).
- `scrie_nota` scrie in `ACT_TEMP` (toate coloanele NOT NULL au DEFAULT 0), deci e testabil direct
din sqlplus in tranzactie, fara sa treci prin formular; nu are FK-uri.
## 12. PREDARE catre sesiunea urmatoare (stare la 2026-09-17, ~10:30)
### Livrabile si stare pe disc
| Ce | Cale | Stare |
|---|---|---|
| Migrare noua | `D:\ROA\DATABASE\SCRIPTURI_CLAR\2026\09\ff_2026_09_17_01_COMUN_PACK_PRETURI.sql` | scris + **aplicat pe MARIUSM_AUTO**; `PACK_PRETURI` VALID, 0 erori, 0 identificatori >30 |
| Migrari corectate | `...\ff_2026_09_15_01_COMUN_PACK_PRETURI.sql`, `...\ff_2026_09_16_05_COMUN_PACK_PRETURI.sql` | rename aplicat in sursa; **NU aplicate** (17_01 le inlocuieste), **NU publicate** |
| Clasa VFP | `COMUN\programe\ofacturare_comun.prg` (clasa `cus_pret_nomenclator`, ~2366-2630) | `cAscd/cAscc`, RPC 6 arg la `:2530` si `:2616`; nemodificat de altcineva |
| Formular VFP | `COMUN\clase\onom_articole.vc2` (+ `.vcx`/`.vct` regenerate cu txt2vcx) | `Clb_tx_scd.Text1` si `Clb_tx_scc.Text1` (Page3), ControlSource in Init `:1963-1964` |
| Probe E2E | `COMUN\utile\Teste\facturare_unificat\probe_e2e_cont_667.prg`, `probe_e2e_cont_667_ui.prg`, `probe_e2e_cont_667_semn.sql` | rulate: **24/24**, **10/10**, `SUMA=-84.03` |
| Backup formular | `COMUN\clase\onom_articole.vc2.pre_runda3_cont_analitic.bak` | creat de subagent, git-ignorat |
Nume vehiculate dupa corectia Oracle 10: `pack_preturi.gaseste_creeaza_nota_vanzare` (28) si
`pack_preturi.asigura_nota_articol_disc` (25). Numele vechi (`gaseste_sau_creeaza_nota_vanzare`
= 32, `asigura_nota_articol_discount` = 29) **nu mai exista** nicaieri in cod.
### Stare date MARIUSM_AUTO (commisa, nu de test)
- `OPTIUNI.ROAFACTURARE.ID_POL_PRET_STOC = 41`; politica 41 are `id_nota=6`; nota `2` = DISCOUNT
`667=4111`.
- 16 articole cu `cont 667/709` in politica 41 → toate au acum `id_nota=2` (backfill-ul din 17_01,
**commis** de script). Zero articole fara nota.
- Testele E2E nu au lasat reziduuri (verificat: 0 randuri `ACT_TEMP`/note noi).
- Conexiune: `sqlplus MARIUSM_AUTO/ROMFASTSOFT@ROA_CENTRAL`, `TNS_ADMIN=D:\ROA\instantclient_19_18`.
### Ce NU s-a facut (blocat pe aprobare / decizie)
1. **Niciun commit** (nici `ROAFACTURARE`, nici `COMUN`). Tree murdar: `COMUN` are `M` pe
`clase/onom_articole.vc2` si `programe/ofacturare_comun.prg`, plus probe E2E untracked; binarul
`.vcx`/`.vct` e git-ignorat (by design). `ROAFACTURARE` are untracked doar handoff-ul.
2. **Nicio publicare**: `UPD_DATABASE` + `database_20260901_20260930.zip` (generat 2026-09-17
00:07) au inca versiunile vechi (nume de 32 caractere) → pe Oracle 10/11 update-ul lunii pica.
Comanda de publicare (utilizatorul a ales sa NU o rulez acum):
`powershell -ExecutionPolicy Bypass -File "D:\ROA\ROAFACTURARE\COMUN\utile\publicare_scripturi.ps1" -Luna 2026-09`
`-DryRun` dovedit: 15_01 MODIFICAT, 16_05 MODIFICAT, 17_01 NOU, 26 scripturi in arhiva.
3. **Celelalte produse nu au fost atinse.** Numele vechi traieste inca in working copy-urile COMUN
ale produselor: `ROAACNPRO, ROAAUTO, ROACONT, ROACONTRACTE, ROADEF, ROAGEST, ROAIMOB,
ROAPRETURI, ROAREGISTRATURA` (`<PRODUS>\COMUN\programe\ofacturare_comun.prg:2513` si `:2591`).
Fiecare `<PRODUS>\COMUN` e working copy SVN separat (`.svn` + `.git`). Propagarea se face prin
commit SVN pe COMUN + `roa_sync` in fiecare produs + **rebuild EXE**.
4. **Decizie deschisa**: wrapper cu numele vechi de 32 caractere creat conditionat doar pe
Oracle ≥12.2 (`$IF DBMS_DB_VERSION.VERSION >= 12 $THEN` + `EXECUTE IMMEDIATE`), pentru zero
breakage la clientii ≥12.2 cu EXE vechi. Neconfirmat; daca nu se doreste, ramane doar rename.
### Ordinea recomandata pentru sesiunea urmatoare
1. Marius decide: wrapper conditionat da/nu.
2. Commit tintit pe `COMUN` (`ofacturare_comun.prg`, `onom_articole.vc2`) + commit SVN COMUN
(skill `roa-git-svn-commit`; NU `git pull` in COMUN, NU `roa_sync` pe tree murdar).
3. Publicare septembrie (`publicare_scripturi.ps1 -Luna 2026-09`) dupa aprobare.
4. Sync COMUN in celelalte produse + rebuild EXE; livrare DB + EXE **impreuna** (numele vechi a
disparut, deci EXE vechi = rupere la salvare nomenclator).
### Comenzi de re-rulare a testelor
```powershell
# 1) precompilare izolata
powershell -ExecutionPolicy Bypass -File "D:\ROA\ROAFACTURARE\COMUN\utile\Teste\_precompile.ps1" -Prg "D:\ROA\ROAFACTURARE\COMUN\utile\Teste\facturare_unificat\probe_e2e_cont_667.prg"
# 2) rulare (ATENTIE: NU `-A -T`, porneste mut) + asteapta terminarea + citeste logul
$vfp='C:\Program Files (x86)\Microsoft Visual FoxPro 9\vfp9.exe'
$p=Start-Process -FilePath $vfp -ArgumentList @('-A','"D:\ROA\ROAFACTURARE\COMUN\utile\Teste\facturare_unificat\probe_e2e_cont_667.fxp"') -PassThru
$p.WaitForExit(240000)
Get-Content "D:\ROA\ROAFACTURARE\COMUN\utile\Teste\facturare_unificat\out\probe_e2e_cont_667.log"
# idem pentru probe_e2e_cont_667_ui.prg (log out\probe_e2e_cont_667_ui.log)
# 3) dovada semnului (Oracle, cu ROLLBACK)
sqlplus MARIUSM_AUTO/ROMFASTSOFT@ROA_CENTRAL "@D:\ROA\ROAFACTURARE\COMUN\utile\Teste\facturare_unificat\probe_e2e_cont_667_semn.sql"
```
Clasele/procedurile se incarca prin `test_init_env_auto_roafacturare.prg WITH 'CENTRAL',
'MARIUSM_AUTO','ROMFASTSOFT'` apoi `gnIdUtil=8`. Tranzactie manuala:
`SQLSETPROP(gnHandle,"Transactions",2)` + `SQLROLLBACK` la final (altfel se commiteaza).
### Interzis
- `git pull` in COMUN; `roa_sync` pe tree murdar; commit fara aprobarea lui Marius.
- editare `.vcx` direct (doar `.vc2` + `txt2vcx`, cp1252).
- scrieri necommise sau fara rollback pe `MARIUSM_AUTO`.
- identificatori >30 caractere in orice obiect Oracle nou (10.2 = limita).

View File

@@ -1,52 +0,0 @@
# Handoff — id_set unic + skilluri rutare (2026-09-17, ~13:30)
Stare pentru sesiunea urmatoare. Zero lucrari ramase in stare periculoasa pe disc (doar fisiere necomise, listate mai jos).
## 1. Livrabile terminate si comise
| Ce | Unde | Stare |
|---|---|---|
| Migrare id_set unic | `D:\ROA\DATABASE\SCRIPTURI_CLAR\2026\09\ff_2026_09_17_03_COMUN_ID_SET_UNIC.sql` | SVN r18155, publicat (UPD_DATABASE id 17609 + arhiva lunara) |
| Doc SSH | `COMUN\docs\conexiuni-tunel-ssh-odbc.md` | comis de Marius (r18154) |
| Skill `roa-domeniu-rutare` | `COMUN\skills\roa-domeniu-rutare\SKILL.md` | SVN r18156 + symlink + backstop |
| Skill `roa-actualizare-doc` | `COMUN\skills\roa-actualizare-doc\SKILL.md` | SVN r18156 + symlink + backstop |
| Backstop | `COMUN\utile\backstop_skills.txt` + `ROAFACTURARE\AGENTS.md` | r18156 / r18157 |
## 2. NEFINALIZAT / necomis
- `COMUN\utile\roa-sql.ps1` — **NOU, necomis** (helper determinist sqlplus; parola citita din `COMUN\docs\local\oracle.md`, NU hardcodata). Trebuie `svn add` + commit + roa_sync.
- **Îmbunătățirea `roa-domeniu-rutare`** cu sectiunea "Comenzi directe" — NEÎNCEPUT. Feedback Marius: skillurile trebuie sa aiba **comenzi directe / apelari deterministe de scripturi**, nu doar pointeri.
## 3. Feedback de urmat (de la Marius, sesiunea aceasta)
1. **Skilluri cu comenzi directe**: adauga in `roa-domeniu-rutare` comenzile exacte (sqlplus connect dev+productie, interogari de dictionar `all_source`/`all_triggers`/`all_sequences`/`all_tab_columns`, grep in `SCRIPTURI_CLAR`), nu doar harta descriptiva.
2. **Uita-te la pluginul gstack** din Claude Code (`C:\Users\mmari\.claude\skills\gstack\`) ca sa vezi hook-ul de context: injecteaza dimensiunea contextului si se opreste/salveaza la ~250k. De implementat echivalent (eu am ajuns la 341k fara sa opresc — nerespectand regula 250k din `reguli_lucru.md` pct. 0/9).
3. **Subagenti DeepSeek Flash v4.1** pentru sarcini de executie (mai ieftini), ca in instructiunile din docs — nu am urmat instructiunea, am facut interogarile in sesiunea principala.
4. **Nu hardcoda parole** in scripturi — foloseste `COMUN\docs\local\oracle.md` (NEVERSIONAT in git).
## 4. Comenzi de reluat (deterministe)
```powershell
# SQL pe dev (parola din local/oracle.md, automat)
powershell -File COMUN\utile\roa-sql.ps1 -Schema MARIUSM_AUTO -Sql "select user from dual"
# SQL pe productie (alias TNS)
powershell -File COMUN\utile\roa-sql.ps1 -Con "ROMCONSTRUCT/<parola>@ROA_ROMCONSTRUCT" -Sql "..."
# SSH ROMCONSTRUCT (cheie publica, port 22122, NU 22)
ssh -N -o BatchMode=yes -L 1521:127.0.0.1:1521 romfast@82.76.217.177 -p 22122
# ROMFAST e intern, fara tunel: 10.0.20.36:1521
# commit roa-sql.ps1 (la aprobarea lui Marius)
svn add COMUN\utile\roa-sql.ps1 ; svn commit ... ; roa_sync.bat
```
## 5. Stiut deja (nu se reia)
- **Spatiul ID_SET**: doua lucruri distincte — set de contare `XSETS.ID_SET = ACT.ID_SET` (25000+tip, in `tipuri_documente_facturare.md`) vs nota contabila `NOTE_CONTABILE.ID_SET = CRM_NOTE_VANZARI.ID_SET`. Generatoare: `SEQ_CRM_NOTE_ID_SET` + `SEQ_XSETS` (>=1M). Fix: un singur generator SEQ_XSETS, coloane NUMBER(6)->NUMBER(10). Vezi `roa-domeniu-rutare` + `ff_2026_09_17_03`.
- **Lantul notei vanzare**: `CRM_POLITICI_PRET_ART.ID_NOTA -> CRM_POLITICI_PRETURI.ID_NOTA -> CRM_NOTE_VANZARI.ID_NOTA`; `CRM_NOTE_VANZARI.ID_SET -> NOTE_CONTABILE.ID_SET`.
- **Coloane ID_SET**: majoritatea schemelor erau NUMBER(6) (max 999999), de-aia bump-ul 2007 a mers pe +200000.
- **Capcana**: `sterge_nota_vanzari` face `delete from note_contabile where id_set`; index NONUNIQUE -> coliziune = stergere silentioasa.
## 6. Tuneluri/SSH — NU lasa deschise
Toate inchise la finalul sesiunii. Re-deschiderea: comanda din pct. 4.

View File

@@ -1,71 +0,0 @@
# Handoff — idempotenta script 03 + ColumnNeedModify (2026-09-17, ~15:00)
Stare pentru sesiunea urmatoare. Nimic in stare periculoasa pe disc. Fisierele necomise sunt listate la pct. 4.
## 1. Livrabile terminate
| Ce | Unde | Stare |
|---|---|---|
| Script 03 idempotent | `D:\ROA\DATABASE\SCRIPTURI_CLAR\2026\09\ff_2026_09_17_03_COMUN_ID_SET_UNIC.sql` | editat in loc, PUBLICAT (UPD_DATABASE id 17609 reactualizat) |
| Script 04 nou (PACK_MIGRARE + functia ColumnNeedModify) | `D:\ROA\DATABASE\SCRIPTURI_CLAR\2026\09\ff_2026_09_17_04_COMUN_PACK_MIGRARE.sql` | nou, PUBLICAT (id 17610) |
| Arhiva lunara | `Y:\ROAUPDATE\_UPDATE\` | regenerata (30 scripturi), verificata octet cu octet |
Ambele scripturi: ASCII (0 octeti > 127), CRLF, fara BOM, se termina cu `UpdateVersiune` + `commit;`.
## 2. Ce face scriptul 03 (acum idempotent)
- `alter table ... modify (id_set number(10))` era direct; acum e in `declare` cu guard:
`pack_migrare.ColumnExist(...) = 1` AND `data_type='NUMBER' AND data_precision < 10`.
Pe dev coloanele sunt deja NUMBER(10) (data_precision=10), deci guard-ul sare corect.
- Trigger-ul e `create or replace` (idempotent); renumerotarea `where id_set < 1000000` e no-op la a doua rulare.
## 3. Ce face scriptul 04 (noua functie)
`PACK_MIGRARE.ColumnNeedModify(tcTableName, tcColumnName, tcDataType, tnPrecision, tnScale, tcNullable)`:
intoarce 1 daca coloana NU are definitia ceruta (trebuie ALTER MODIFY), 0 altfel. Orice parametru NULL = aspectul nu se verifica.
Pachetul complet a fost reprodus din DB-ul live (are ramura `when 'RF' THEN` din UpdateVersiune, pe care `.pck`-ul stale o pierduse) + functia noua. Baza: scriptul 2014 `ff_2014_08_18_01_COMUN_PACK_MIGRARE.sql` == DB live (verificat cu diff).
## 4. APROBAT dar NEEXECUTAT (Marius a aprobat in sesiune)
1. **A**: in `COMUN\skills\roa-oracle-migration\SKILL.md` — inlocuieste criteriul CRLF buguit
`($b -join ',').Contains('13,10')` (fals pozitiv: byte 113='q' + 10 => "113,10" contine "13,10")
cu regex-ul `(?<!\r)\n` (acelasi pe care-l foloseste `publicare_scripturi.ps1` linia 543).
2. **B**: capcana noua in `roa-oracle-migration\SKILL.md` + `scripturi-migrare-db.md`: fisierele `.sql`
noi ies cu LF din `write`/`edit`; converteste CRLF inainte de publicare. One-liner:
`$t = [IO.File]::ReadAllText($p); $t = $t -replace "`r`n","`n" -replace "`n","`r`n"; [IO.File]::WriteAllText($p, $t, (New-Object System.Text.ASCIIEncoding))`
3. **C**: in `COMUN\docs\oracle_export.md` — un rand: `.pck` poate fi stale (ex. PACK_MIGRARE fara ramura
RF); verifica echivalenta fata de DB live inainte de a-l folosi.
4. **Sterge `.pck`-urile din `COMUN\docs\`**: `PACK_MIGRARE.pck`, `PACK_UPDATE.pck`, `PACK_DIAG_SPATIU.pck`.
Marius: foloseste doar ultimele versiuni din SCRIPTURI_CLAR, nu exporturi `.pck` din docs.
ATENTIE: `oracle_export.md` si `roa-domeniu-rutare\SKILL.md` trimit la `*.pck` — actualizeaza-le.
## 5. NEFINALIZAT / necomis (din sesiunile trecute)
- `COMUN\utile\roa-sql.ps1` — NOU, necomis (svn add + commit + roa_sync).
- AGENTS.md: sectiunea "Stil de interactiune" adaugata — necomisa.
- `COMUN\docs\reguli_lucru.md`: regula 14 (intreaba cu optiuni in loc de investigatie) adaugata — necomisa.
- `roa-domeniu-rutare\SKILL.md`: sectiunea "Comenzi directe" (comenzi sqlplus deterministe) — NEINCEPUTA.
- Scripturile 03 (M) si 04 (A) din `D:\ROA\DATABASE` — de comis in SVN (`svn add` pt 04).
## 6. Comenzi de reluat
```powershell
# publicare (deja facuta, doar daca re-rulezi):
powershell -File D:\ROA\ROAFACTURARE\COMUN\utile\publicare_scripturi.ps1 -DryRun
# SQL pe dev:
powershell -File COMUN\utile\roa-sql.ps1 -Schema MARIUSM_AUTO -Sql "select user from dual"
# commit SVN in D:\ROA\DATABASE (doar 03 + 04, tintit):
# svn commit 2026\09\ff_2026_09_17_03_COMUN_ID_SET_UNIC.sql 2026\09\ff_2026_09_17_04_COMUN_PACK_MIGRARE.sql -F <mesaj>
# commit COMUN: vezi skill roa-git-svn-commit (doar fisierele aprobate, apoi roa_sync.bat)
```
## 7. Capcane platite (NU se repeta)
- Verificarea CRLF cu `($b -join ',').Contains('13,10')` e FALS-POZITIVA. Foloseste `(?<!\r)\n`.
- Tool-ul `write` scrie `.sql` cu LF, nu CRLF — converteste.
- `.pck`-urile din `COMUN\docs` pot fi stale fata de DB live (PACK_MIGRARE pierduse ramura RF).
- `roa-actualizare-doc` / `roa-domeniu-rutare` NU sunt in lista mea de skill-uri; citeste-le cu `read`, nu invoca `skill`.
- Greseli de disciplina: n-am folosit subagenti (regula 6) si am depasit pragul de 250k (REGULA ZERO).

View File

@@ -1,57 +0,0 @@
# handoff_inject.ps1 - reinjectare handoff la SessionStart
Fisier: `D:\ROA\ROAGEST\COMUN\utile\handoff_inject.ps1`
Branch: `qa-factura-s0` (repo `D:\ROA\ROAGEST\COMUN`)
Commit: `56d1764ed8c9509acef8d3683583aab03b04ae97` — "adauga handoff_inject.ps1 - reinjectare handoff pe SessionStart"
## Ce face
Hook `SessionStart`. Citeste JSON-ul primit pe stdin (`source`, `cwd`). Daca `source` e in lista
acceptata (implicit `compact`, `clear`, `resume`) si `<cwd>\docs\handoff_activ.md` (sau calea data
prin `-CaleHandoff`) exista, emite pe stdout:
```json
{"hookSpecificOutput":{"hookEventName":"SessionStart","additionalContext":"HANDOFF RELUAT automat din <cale> (pornire: <source>). ...\n\n<continut>"}}
```
Tace (exit 0, fara iesire) daca `source` nu e in lista, sau daca fisierul nu exista — nu inventeaza,
nu creeaza nimic. Peste ~9000 de caractere, nu trunchiaza tacut: taie continutul, adauga un rand
final `[TRUNCHIAT - ... Citeste fisierul complet cu Read: <cale>]` si il include in output.
Parametri: `-CaleHandoff <cale>` (optional), `-Surse <lista>` (implicit `compact,clear,resume`).
## Rezultatul probelor
1. `source=startup`, fisier existent -> `exit=0`, `out=[]` (nicio iesire). Confirmat.
2. `source=compact`, fisier inexistent -> `exit=0`, `out=[]`. Confirmat.
3. `source=compact`, continut cu ghilimele duble, apostrof, diacritice (`șăâîț`) -> JSON valid,
`hookEventName=SessionStart`, iar `additionalContext` dupa `ConvertFrom-Json` contine exact
continutul original (`CONTINE ORIGINAL: True`, verificat prin `.Contains()`, nu din ochi).
4. `source=clear`, fisier de 10350 caractere -> JSON valid, `additionalContext` taiat la 9403
caractere, se termina cu randul `[TRUNCHIAT - handoff-ul are 10350 caractere, peste limita de
9000. Citeste fisierul complet cu Read: <cale>]`, calea completa prezenta in text.
Probele au rulat in `$env:TEMP\hoi_probe\p1..p4`, sterse dupa rulare.
## Configurare settings.json (de aplicat de orchestrator)
```json
{
"hooks": {
"SessionStart": [
{
"matcher": "clear,compact,resume",
"hooks": [
{
"type": "command",
"command": "powershell -ExecutionPolicy Bypass -File D:\\ROA\\ROAGEST\\COMUN\\utile\\handoff_inject.ps1"
}
]
}
]
}
}
```
Nu am atins `C:\Users\mmari\.claude\settings.json` — cablarea ramane la orchestrator, conform
interdictiei primite.

View File

@@ -1,341 +0,0 @@
# Handoff: actualizare blocata pe ROMFAST 10.0.20.36 (16.09.2026, seara)
Stare, nu concluzie finala. Nimic modificat pe productie: exclusiv `SELECT`-uri.
## Ce e DOVEDIT
1. **Nu e problema de certificat.** `ORA-29024` vine de pe ramura a doua a lui
`CONTAFIN_ORACLE.PACK_UTILS.URL2Clob`. Cod instalat, citit din `all_source`:
- linia 285: `IF lcPowerShellDownload = '1' THEN` -> ramura 1 (curl extern);
- linia 291: `llDownloaded := DownloadFileOS(tcURL, lcDir, lcFileName);` cu `lcDir := 'DMPDIR'`;
- linia 293-301: daca a mers, citeste si `RETURN`;
- linia 305: `l_clob := HTTPURITYPE.createuri(tcURL).getclob();` <- ramura 2, cade aici;
- linia 307-313: `WHEN OTHERS` -> cleanup -> `RAISE`.
Stiva reala (`PACK_UTILS` 313 si 305) se potriveste exact. Deci ORA-29024 = ramura 1 nu a produs fisierul.
2. **Ramura 1 E activata pe ROMFAST.** `CONTAFIN_ORACLE.SERVER_INFO`:
`POWERSHELLDOWNLOAD = 1`, `POWERSHELLTIMEOUT = 30`,
`POWERSHELLPATH = C:\Windows\System32\WindowsPowerShell\v1.0\powershell.exe`,
`ROAUPDATEPATH = D:\ROAUPDATE`. **Nu exista rand `CURLPATH`**, dar codul are fallback pe
`C:\Windows\System32\curl.exe` (`PACK_UTILS` body 690-692), deci absenta lui nu e fatala.
3. **Esecul dureaza exact cat timeout-ul.** `all_scheduler_job_run_details`, `UPDATEROA_JOB`:
- 21:17:51 SUCCEEDED (error# 0)
- 22:10:36 FAILED, error# 29273 — pornire 22:10:05 => **31 secunde**
- 22:27:42 FAILED, error# 29273 — pornire 22:27:12 => **30 secunde**
`POWERSHELLTIMEOUT = 30`. Bucla de polling (`PACK_UTILS` body 709-713) a mers pana la capat si
`PACK_UTILS_FILE.FileExists` a intors fals. Fisierul **nu a aparut niciodata**, nu a aparut tarziu.
4. **Corelatia temporala cu patch-ul de azi.** `all_objects`, owner `CONTAFIN_ORACLE`:
`PACK_UTILS` (spec+body), `PACK_UTILS_FILE` (spec+body) `last_ddl_time = 16.09 21:17:51`;
`PACK_UPDATE` (spec+body) `21:17:52`. Toate VALID.
Rularea de la 21:17:30-21:20:14 e cea care a **instalat** noul cod; descarcarea din ea s-a facut
inainte de recompilare, deci **cu codul vechi**. Toate rularile de DUPA recompilare (22:10, 22:27)
au esuat. => **noul `PACK_UTILS` nu a descarcat niciodata nimic cu succes pe ROMFAST.**
5. **Serverul de update raspunde.** Masurat de pe statia de lucru, 16.09.2026:
`https://roa.romfast.ro/contafinupdate/roaupdate/` -> HTTP 200 in 0.127s.
`http://10.0.20.122/` -> 404 in 0.0016s de la `Microsoft-HTTPAPI/2.0` (normal, situl real e pe
portul 81 cu ruta `/contafinupdate/...`; 404 de la `http.sys` NU inseamna server picat).
6. **Versiuni** (`SERVER_INFO.VERSIUNE_*`): majoritatea schemelor la `202609150001` (15.09);
`CONTAFIN_ORACLE` la `202609160003`, `ROMFAST` la `202609160005`. Actualizarea de azi a inceput si
s-a oprit pe drum.
7. **`UPD_LOG` se goleste la fiecare rulare** (`last_ddl_time` 22:27:02 pe tabela si indecsi).
Singurul rand ramas: `16.09 22:34:38 ACTUALIZARE INCHEIATA MANUAL`. Nu e sursa de istoric.
8. **`sys.ExecuteScriptOS`** (`all_source`, owner SYS, `last_ddl_time` 05.02.2026, deci **neatins azi**)
creeaza un job `exec_ps_<8hex>` de tip `executable` cu argumentele
`-ExecutionPolicy Bypass -File <script>` si il `ENABLE`-aza. Joburile sunt SYS-owned, deci
`CONTAFIN_ORACLE` **nu le vede** in `all_scheduler_job_run_details` (interogat: zero randuri
`EXEC_PS%`). Aici e gaura de vizibilitate.
## Ce NU e dovedit (ipoteze deschise, in ordinea probabilitatii)
- **A. Patch-ul de azi a rupt ramura 1.** Cel mai probabil, pe baza punctului 4. Ce s-a schimbat azi
(antet `PACK_UTILS` body linia 4): „DownloadFileOS descarca in .part si redenumeste la final, nume
unic de script ps". Scriptul generat (body 694-699):
`& "<curl>" -k -o "<dest>.part" "<url>"` + `if ($LASTEXITCODE -eq 0) { Move-Item ... } else { Remove-Item ... }`.
Scris cu `pack_utils_file.clob2fileX` (701), citit cu `File2ClobX` (295) — ambele functii noi.
**Nedeterminat care linie anume pica.** Nu am comparat inca versiunea instalata cu cea anterioara.
- **B. PowerShell/curl nu ruleaza deloc pe server** (drepturi pe jobul extern, `C:\DMPDIR` nescriibil,
curl absent). Ar da acelasi simptom si ar fi independent de patch — dar atunci ar fi picat si
inainte de 21:17, ceea ce contrazice punctul 3. Slaba, dar nu exclusa.
- **C. Legatura cu incidentul conpress de azi.** Capatul departat al db link-ului `DBL_ROMFAST2` este
chiar 10.0.20.36; la conpress s-a omorat sesiunea de pe partea apropiata (ROA_CENTRAL) fara sa se
priveasca vreodata partea departata. **Neverificata.**
## Urmatorul pas care departajeaza (nerulat)
Pe serverul 10.0.20.36, in afara Oracle: ruleaza manual scriptul pe care il genereaza codul si vezi
codul de iesire al lui `curl.exe` — el e singura informatie pe care lantul o pierde complet
(`$LASTEXITCODE` decide doar redenumirea, valoarea nu se logheaza nicaieri). Si verifica ce ramane in
`C:\DMPDIR` dupa o rulare esuata: daca ramane un `.part`, curl a mers si a picat `Move-Item`; daca nu
ramane nimic, curl a esuat sau PowerShell n-a pornit.
Alternativ, din Oracle ca SYS: `dba_scheduler_job_run_details` pentru joburile `EXEC_PS%` — acolo
sunt esecurile jobului extern, invizibile din `CONTAFIN_ORACLE`.
## Stare periculoasa / de curatat
- **Tunelul Bitvise e RIDICAT** — proces `stnlc`, profil `D:\vm303-profile\romfast.tlp`,
forwardari active pe `127.0.0.1:1521` (ROMFAST, `SID=ROA`), 1522, 1523 si altele.
**De inchis** cand nu mai e nevoie:
`Stop-Process -Id (Get-NetTCPConnection -LocalPort 1521 -State Listen).OwningProcess`.
- Nimic modificat pe productie. Doar `SELECT`-uri. Niciun script relansat, niciun job atins.
- `SERVER_INFO` contine parole (`PASSWORD_SYS`, `PASSWORD_CONTAFIN_ORACLE` base64+gzip,
`EMAIL_PASSWORD` in clar). **Nu le pune in niciun raport, doc sau commit.**
- Modificare necomisa in alt repo: `COMUN/docs/depanare-server-update-roa.md`, +60 linii
(doua sectiuni noi, doar insertii). Neverificata linie cu linie de Marius.
- Lane opencode `src` inca rula la scrierea acestui fisier; livrabilul lui ar fi
`docs/raport_src.md` (diff conceptual intre versiunea de azi a lui PACK_UTILS si cea anterioara).
## Cum se reia conexiunea
```
"C:\Program Files (x86)\Bitvise SSH Client\stnlc.exe" "-profile=D:/vm303-profile/romfast.tlp"
D:\ROA\instantclient_19_18\sqlplus.exe -S -L "CONTAFIN_ORACLE/<parola>@(DESCRIPTION=(ADDRESS=(PROTOCOL=TCP)(HOST=127.0.0.1)(PORT=1521))(CONNECT_DATA=(SID=ROA)))" "@script.sql"
```
Din Git Bash, calea profilului se da cu **bare oblice** — backslash-urile sunt inghitite si dau
„CreateFile(): Windows error 2". Parola se cere de la Marius, nu se ghiceste
(`FAILED_LOGIN_ATTEMPTS` poate bloca schema in productie).
---
## COMPLETARE (dupa lane-ul `src` + `dba_scheduler_job_run_details`)
`CONTAFIN_ORACLE` **are** drept pe vederile DBA. Joburile externe PowerShell sunt vizibile si
schimba diagnosticul:
```
EXEC_PS_40900C5A SUCCEEDED 16.09 22:27:13 error# 0
UPDATEROA_JOB FAILED 16.09 22:27:42 error# 29273
EXEC_PS_804FF579 SUCCEEDED 16.09 22:10:06 error# 0
UPDATEROA_JOB FAILED 16.09 22:10:36 error# 29273
EXEC_PS_B5715BCA / BF32458E / A31FD48B / 6FC1191C / 8E28DF1C / 15128026 SUCCEEDED 16.09 21:17:31-38
UPDATEROA_JOB SUCCEEDED 16.09 21:17:51
EXEC_PS_* x7 SUCCEEDED 16.09 04:00:01-08 (rularea automata, cod vechi)
```
**Ce dovedeste asta:**
1. **PowerShell PORNESTE si iese cu cod 0.** Ipoteza B din handoff (job extern neconfigurat, lipsa
drepturi, `curl` absent) e **INFIRMATA**. `sys.ExecuteScriptOS` functioneaza pe ROMFAST.
2. **Esecul e INAUNTRUL scriptului `.ps1`.** Jobul e `job_type=>'executable'` pe `powershell.exe`;
`SUCCEEDED` inseamna doar ca `powershell.exe` a iesit cu 0. Scriptul iese cu 0 **si pe ramura de
esec**, fiindca `else { Remove-Item ... }` (PACK_UTILS body 698-699) nu propaga nimic.
Deci: ori `curl` a esuat si `.part` a fost sters, ori `curl` a mers si `Move-Item` a picat.
**Ambele sunt inghitite tacit.**
3. **Sub codul vechi mergea, sub cel nou nu.** La 04:00 (automat) au rulat 7 joburi PS, toate
reusite, si actualizarea a mers; la 21:17 sase joburi PS reusite, `UPDATEROA_JOB` SUCCEEDED —
toate **inainte** de recompilarea de la 21:17:51. Dupa recompilare: la 22:10 si 22:27 ruleaza
**exact un singur** job PS, apoi esec la 30 de secunde. Adica **prima descarcare** sub noul cod
pica, si lantul se opreste acolo.
**Diferentele reale introduse azi** (lane `src`, comparatie `co_2026_09_16_02` vs
`co_2026_01_14_01_PACK_UTILS.sql`):
- numele scriptului `.ps1`: fix `download_file.ps1` -> unic `download_<timestamp>.ps1`;
- se sterge fisierul destinatie INAINTE de descarcare (body 685) — nou;
- se descarca in `<dest>.part` si se muta cu `Move-Item -Force` — nou;
- `.ps1` se sterge la final (body 719) — nou;
- in `PACK_UTILS_FILE`, `FileDelete` a devenit best-effort (`when others then null`), deci nu mai
ridica ORA-29283/29291 care inainte semnalau problema.
- **Gardele si preconditiile sunt NESCHIMBATE** — deci nu s-a pierdut nicio configurare.
**Suspectul principal ramas:** scriptul `.ps1` nou. Doua variante, nedepartajate:
(a) `curl.exe -k -o "<dest>.part"` esueaza (dar mergea la 21:17 cu aceeasi retea si acelasi URL);
(b) `Move-Item -Force` din `.part` in destinatie pica — si e exact pasul care NU exista inainte.
Varianta (b) explica de ce simptomul a aparut fix la prima rulare de dupa recompilare.
A treia varianta, de exclus: `clob2fileX` (functie noua) scrie `.ps1`-ul incomplet/gol — powershell
ar rula un fisier gol si ar iesi tot cu 0, exact ce se vede.
**Cum se departajeaza, pe OS, pe 10.0.20.36, in `C:\DMPDIR`:**
- raman fisiere `url2clob_*.tmp.part` -> `curl` a mers, `Move-Item` a picat -> varianta (b);
- nu ramane nimic -> `curl` a esuat -> varianta (a);
- raman `download_*.ps1` -> scriptul nu a fost sters, deci bucla nu a ajuns la capat;
- deschide un `download_*.ps1` ramas si vezi daca are continut -> departajeaza `clob2fileX`.
Rularea manuala a aceluiasi `.ps1` din consola, pe server, arata direct codul de iesire al lui `curl`.
---
## COMPLETARE 2: dovada de pe disc (`\10.0.20.36\c\DMPDIR`)
`roadigi.romfast.ro` ESTE 10.0.20.36; share-ul `\10.0.20.36\c\` e accesibil direct de pe statie.
Continut relevant:
```
url2clob_20260916222712422000000.tmp.part 10863 octeti
CreationTime = LastWriteTime = 16.09 22:27:12.990
```
- Numele contine timestamp-ul generat de PL/SQL: **22:27:12.422**.
- `EXEC_PS_40900C5A` (jobul PowerShell) s-a incheiat SUCCEEDED la **22:27:13**.
- `UPDATEROA_JOB` a esuat la 22:27:42 (30s = `POWERSHELLTIMEOUT`).
- Continutul `.part` este `roa_app.xml` **complet si valid** (`<?xml ... Windows-1252?><VFPData>` ...
`</VFPData>`), lista de programe de la ROAACNPRO la ROAVIN.
- **Niciun `download_*.ps1` ramas** in DMPDIR (stergerea de la body 719 a functionat).
**Ce e DOVEDIT acum:**
1. `curl.exe` a mers perfect: a descarcat tot fisierul, in ~0.5 secunde, in locatia corecta.
Ipoteza (a) — „curl esueaza" — e **INFIRMATA**.
2. Redenumirea `.part` -> destinatie **nu s-a facut**. Nici `Remove-Item` de pe ramura `else` nu
s-a facut, altfel fisierul ar fi disparut.
3. PowerShell a iesit imediat dupa `curl` (22:27:12.990 -> job incheiat 22:27:13), cu cod 0.
4. NTFS pe `C:\DMPDIR`: `Authenticated Users:(M)` — **Modify, deci si drept de stergere**.
Ipoteza „lipsa drept de delete/rename" e **INFIRMATA**.
**Intrebarea ramasa, singura, nedepartajata:** de ce nu s-a executat linia a doua a scriptului.
Doua variante, ambele compatibile cu toate dovezile de mai sus:
- **(b) `Move-Item` a rulat si a esuat** ca eroare ne-terminanta -> scrisa pe stderr, ignorata,
PowerShell iese 0. Motivul esecului ramane nestiut (jobul extern nu captureaza stderr).
- **(c) fisierul `.ps1` a fost scris trunchiat la primul CRLF**, deci PowerShell a executat doar
linia cu `curl` si a iesit. Scrierea se face cu `PACK_UTILS_FILE.Clob2FileX`
(`dbms_xslprocessor.clob2file(clob, dir, file, csid => 0)`), iar scriptul de azi este **primul
script cu mai mult de o linie** — inainte era o singura comanda `curl` direct in destinatie, deci
o trunchiere la primul CRLF ar fi fost invizibila. Asta explica de ce simptomul apare exact la
prima rulare de dupa patch.
**Testul minim care departajeaza (cere o scriere, NU s-a facut):** pe o instanta de test, apeleaza
`PACK_UTILS_FILE.Clob2FileX` cu un clob de doua linii separate prin `chr(13)||chr(10)` si uita-te la
fisierul rezultat: daca are o singura linie -> varianta (c), si reparatia e in felul in care se scrie
`.ps1`-ul, nu in `curl`. Daca are doua linii -> varianta (b), si atunci trebuie capturat stderr-ul
scriptului ca sa se vada de ce pica `Move-Item`.
**Fisierul `.part` de la 22:27 e singura proba fizica ramasa — nu-l stergeti si nu-l redenumiti
pana nu se decide testul.** Cel de la rularea 22:10 nu mai exista (nu se stie cine l-a sters).
---
## CAUZA RADACINA — DEMONSTRATA prin reproducere pe ROMFAST (16.09.2026, 23:07)
Marius a aprobat rularea directa pe productie. Probele au fost rulate prin
`PACK_UTILS_FILE.Clob2FileX` + `sys.ExecuteScriptOS`, adica prin exact acelasi mecanism si sub
exact acelasi cont ca lantul real. Toate fisierele de proba au fost sterse dupa (vezi „Curatenie").
### Proba 1 — `Clob2FileX` scrie corect (varianta (c) INFIRMATA)
Clob de doua linii separate prin `chr(13)||chr(10)` -> fisier de 248 octeti, **ambele linii intacte**,
`\r \n` pastrat, fara BOM, fara trunchiere (`od -c`). Scrierea `.ps1`-ului nu e problema.
### Proba 2 — `Move-Item` functioneaza sub contul jobului (varianta (b) INFIRMATA)
Script rulat prin `ExecuteScriptOS`: creeaza un fisier in `C:\DMPDIR`, il muta, il sterge.
Rezultat in log: `MOVE OK`, `DEL OK`. Si `cwd=C:\`. Deci nici drepturile, nici `Move-Item` nu pica.
(NTFS pe `C:\DMPDIR`: `Authenticated Users:(M)`.)
### Proba 3 — reproducerea scriptului real
Script identic cu cel generat de `DownloadFileOS`, pe URL-ul real
`https://roa.romfast.ro/contafinupdate/default.aspx/updroa/download/1/roa_app.xml`
(`optiuni.UPD_URL_APP` + `sys.auth_detalii.detalii = 1`):
```
lastexit=
part_ramas=False dest_creat=False
erori=1
Cannot find path 'C:\DMPDIR\claude_repro.tmp.part' because it does not exist.
```
`$LASTEXITCODE` **gol**; `Remove-Item` a picat pentru ca fisierul inca nu exista. Fisierul `.part`
a aparut DUPA ce scriptul se terminase.
### Proba 4 — mecanismul, cu ceas
```
t0=23:07:08.671
t1=23:07:08.707 ec=[] exists=False <- la 36 de milisecunde dupa lansarea curl
t2=23:07:13.735 exists=True <- dupa Start-Sleep 5
proc_curl=0
```
### Verdict
**Sub jobul extern DBMS_SCHEDULER (proces fara consola), operatorul `&` din PowerShell nu asteapta
procesul nativ.** Scriptul trece la linia urmatoare in ~36 ms, `$LASTEXITCODE` nu e niciodata setat
(deci `$null -eq 0` = False), se executa ramura `else` pe un fisier inca inexistent, iar cand `curl`
termina in fundal lasa `<destinatie>.part` pe disc **pentru totdeauna**. Fisierul destinatie nu apare
niciodata -> bucla de polling din `DownloadFileOS` expira dupa `POWERSHELLTIMEOUT` (30s) ->
`llDownloaded = FALSE` -> `URL2Clob` cade pe `HTTPURITYPE` (body 305) -> pe `https` fara wallet ->
**ORA-29273 / ORA-29024**.
**De ce mergea inainte:** codul vechi scria cu `curl -o` DIRECT in fisierul destinatie. Nu exista
pas de redenumire, deci nu depindea de terminarea lui `curl`: bucla de polling din Oracle astepta
oricum aparitia fisierului, iar asincronia era inofensiva. Patch-ul de azi a introdus un pas
(`Move-Item`) care presupune ca `curl` s-a terminat — presupunere falsa in acest mediu.
Asta explica exact de ce simptomul apare la prima rulare de dupa recompilarea de la 21:17:51.
### Directia de reparatie (NEAPLICATA, nediscutata inca)
Scriptul trebuie sa astepte explicit procesul, nu sa se bazeze pe `&` si pe `$LASTEXITCODE`. Varianta
minima, in `PACK_UTILS.DownloadFileOS` body ~694-699, inlocuind linia cu `&`:
```powershell
$p = Start-Process -FilePath "<curl>" -ArgumentList '-k','-o','<dest>.part','<url>' -Wait -PassThru -NoNewWindow
if ($p.ExitCode -eq 0) { Move-Item ... } else { Remove-Item ... }
```
Alternativ, `& "<curl>" ... | Out-Null` forteaza consumarea iesirii, deci asteptarea — dar `Start-Process
-Wait -PassThru` e explicit si nu depinde de un efect secundar al pipeline-ului.
**Ambele variante trebuie probate prin acelasi mecanism (`ExecuteScriptOS`) inainte de a fi propuse
pentru commit** — mediul jobului extern e exact ce a invalidat presupunerea initiala.
### Curatenie
Toate fisierele de proba sterse din `C:\DMPDIR` (`test_clob2file_claude.txt`, `diag_claude.*`,
`repro_claude.*`, `claude_repro.*`, `async_claude.*`). A ramas **doar** proba originala
`url2clob_20260916222712422000000.tmp.part` (10863 octeti, 22:27) — nu o stergeti, e dovada fizica
a incidentului.
Nu s-a modificat niciun pachet, niciun job, nicio optiune. Singurele scrieri pe productie au fost
fisiere temporare in `C:\DMPDIR`, toate sterse.
---
## INCHEIERE BLOC (16.09.2026, ~23:40)
### Livrat si comis
| ce | unde | revizie |
|---|---|---|
| `co_2026_09_16_06_COMUN_PACK_UTILS.sql` | `^/DATABASE/Branches/RB-1.00` | r18146 (comis de Marius), r18148 (scoaterea lui `&`) |
| `docs/depanare-server-update-roa.md` | `^/COMUN/Trunk` | r18147 |
### Aplicat pe servere
- **ROMFAST 10.0.20.36**: aplicat 23:19:32, `PACK_UTILS` VALID, `VERSIUNE_CONTAFIN_ORACLE =
202609160006`. `PACK_UPDATE.UpdateROA` rulat de Marius 23:21:50-23:24:19, `UPDATEROA_JOB`
SUCCEEDED, sase joburi `EXEC_PS_*` reusite, `UPD_LOG` fara randuri `ERR`, 65 de scheme pe
`20260916`. `URL2Clob` intoarce documentul complet in ~2s.
- **ROA_CENTRAL 10.0.20.122**: aplicat de Marius. A afisat `SP2-0317: expected symbol name is
missing` — cauza: un `&` la sfarsit de rand intr-un COMENTARIU (linia 756), citit de sqlplus ca
variabila de substitutie fara nume. Efect strict cosmetic: pachetul s-a compilat
(„Package body created"), codul e intact. Reparat in r18148 prin reformularea comentariilor.
**NU e BOM** — fisierul are 0 octeti non-ASCII, verificat la scriere si la commit.
**Nu s-a verificat independent** starea de pe ROA_CENTRAL (nu s-a deschis tunel acolo).
### Capcana de retinut
La aplicarea unui script prin sqlplus, **nu filtra iesirea** — asa am ratat acelasi `SP2-0317` pe
ROMFAST. Un `&` la sfarsit de rand, chiar si in comentariu, produce eroarea; `&` urmat de virgula
sau spatiu e tolerat.
### Ramas deschis
- **Tunelul Bitvise catre ROMFAST e inca RIDICAT** (`stnlc`, profil `D:\vm303-profile\romfast.tlp`,
porturi 1521/1522/1523). De inchis:
`Stop-Process -Id (Get-NetTCPConnection -LocalPort 1521 -State Listen).OwningProcess`.
- `C:\DMPDIR\url2clob_20260916222712422000000.tmp.part` — proba fizica a incidentului, pastrata
intentionat. Acum ca e documentata, poate fi stearsa.
- **Necomise, intentionat** (documente de lucru): `docs/handoff_update_romfast.md`,
`docs/raport_src.md`, `docs/raport_doc.md`.
- **Intrebare deschisa, neinrudita cu reparatia asta**: capatul departat al db link-ului
`DBL_ROMFAST2` este chiar 10.0.20.36; la incidentul conpress s-a omorat sesiunea de pe partea
apropiata (ROA_CENTRAL) fara sa se priveasca partea departata. Cele doua incidente pot avea
radacini diferite — reparatia de azi NU il explica pe cel de la conpress.
- Restul serverelor de client: scriptul ajunge la ele prin fluxul normal de actualizare.
### Stare periculoasa
Niciuna. Pe productie nu a ramas nimic in lucru: niciun job atins, nicio tranzactie deschisa, toate
fisierele de proba sterse din `C:\DMPDIR`.
---
## ACTUALIZARE FINALA (23:45)
**Tunelul Bitvise catre ROMFAST este INCHIS.** Anuleaza randul „Tunelul ... e inca RIDICAT" din
sectiunea „Ramas deschis" de mai sus: procesul `stnlc` a fost oprit, iar porturile 1521/1522/1523
nu mai asculta (verificat cu `Get-NetTCPConnection`). Nu mai exista acces deschis la productie din
aceasta sesiune.
Reluarea conexiunii, cand va fi nevoie:
```
"C:\Program Files (x86)\Bitvise SSH Client\stnlc.exe" "-profile=D:/vm303-profile/romfast.tlp"
```
Din Git Bash calea profilului se da cu bare oblice — backslash-urile sunt inghitite si dau
„CreateFile(): Windows error 2".
Restul sectiunii „Ramas deschis" ramane valabil: proba `.part` din `C:\DMPDIR`, documentele de lucru
necomise, si intrebarea deschisa despre db link-ul `DBL_ROMFAST2`.

View File

@@ -1,173 +0,0 @@
# Hooks ca functii + acces la context consumat — verificare documentatie oficiala
Data verificare: 2026-09-17. Metoda: WebFetch pe paginile oficiale (rezumate de un model
intermediar, nu HTML brut — unde continutul a fost trunchiat, marcat explicit mai jos) +
verificare empirica directa pe un `.jsonl` de pe disc.
## 1. Exista hook-uri definite ca FUNCTII (nu shell command)?
### In Claude Code (`settings.json` / plugin `hooks/hooks.json`) — NU
Pagina `https://code.claude.com/docs/en/hooks` defineste explicit tipurile de handler pentru
hook-uri:
> "Hooks are user-defined shell commands, HTTP endpoints, MCP tool calls, LLM prompts, or
> subagents that execute automatically at specific points in Claude Code's lifecycle."
Cinci tipuri de `type`, toate procese externe sau apeluri la distanta, niciunul „functie in-proces":
1. `"command"` — shell command (Bash/PowerShell)
2. `"http"` — POST catre un endpoint HTTP
3. `"mcp_tool"` — apel catre un tool MCP
4. `"prompt"` — evaluare printr-un prompt LLM single-turn
5. `"agent"` — subagent (experimental)
Confirmat separat pe `https://code.claude.com/docs/en/plugins-reference`, care listeaza acelasi
set de cinci tipuri pentru schema hook-urilor din plugin-uri si spune explicit:
> "There is no `function` type mentioned anywhere in the documentation."
Exemplu de schema (din `plugins-reference`):
```json
{
"hooks": {
"PostToolUse": [
{
"matcher": "Write|Edit",
"hooks": [
{ "type": "command", "command": "\"${CLAUDE_PLUGIN_ROOT}\"/scripts/format-code.sh" }
]
}
]
}
}
```
**Concluzie punct 1a**: in Claude Code (inclusiv plugin-uri), hook-urile NU pot fi functii
JS/TS/Python in-proces — doar comenzi shell, HTTP, MCP tool, prompt LLM sau subagent.
### In Claude Agent SDK (TypeScript) — DA, dar cu rezerva NEDOCUMENTAT pe detalii
Pagina `https://code.claude.com/docs/en/agent-sdk/typescript` (redirect de la
`docs.claude.com/.../agent-sdk/typescript`) arata ca optiunea `hooks` a SDK-ului accepta
callback-uri, nu comenzi shell:
> `hooks` | `Partial<Record<`HookEvent`, `HookCallbackMatcher`[]>>` | `{}` | Hook callbacks for events
Asta e un mecanism DIFERIT de `settings.json` al Claude Code: aici hook-ul e literal o functie
TypeScript data la `query({ ..., hooks: {...} })`, ruland in acelasi proces Node ca aplicatia SDK.
**NEDOCUMENTAT - nu am putut extrage**: definitiile exacte de tip pentru `HookEvent`,
`HookCallback`, `HookCallbackMatcher`, `HookJSONOutput`, sau tipurile de input per eveniment
(`PreToolUseHookInput` etc.) — pagina e mare si WebFetch a trunchiat/rezumat continutul de doua
ori la rand, fara sa gaseasca sectiunea cu type body-urile (doar link-uri ancora `#hookevent`,
`#hookcallbackmatcher`, nerezolvate de rezumator). Nu pot afirma nici ca schema de input e identica
cu a hook-urilor `command`, nici ca difera — necesita citire directa a paginii (curl/browser),
nu WebFetch.
## 2. Primesc function hooks (SDK) un input mai bogat decat hook-urile `command`?
**NEDOCUMENTAT - nu am putut extrage.** Pagina SDK TS nu a livrat campurile exacte ale obiectului
de input trimis catre `HookCallback` (vezi punctul 1). Singurul camp relevant gasit pe acea pagina
a fost `maxThinkingTokens` (o optiune de configurare a sesiunii, nu un camp de input al hook-ului).
Nu exista nicio mentiune gasita de `usage`, `context_window` sau echivalent in continutul extras.
## 3. Ce primeste `statusLine` ca input, si acelasi obiect e disponibil vreunui hook?
Pagina `https://code.claude.com/docs/en/statusline` confirma ca `statusLine` e un mecanism separat
de hook-uri: un script shell propriu, care primeste JSON pe stdin cu date de sesiune, explicit
descris ca fiind pentru monitorizarea folosirii contextului:
> "The status line is a customizable bar at the bottom of Claude Code that runs any shell script
> you configure. It receives JSON session data on stdin and displays whatever your script prints,
> giving you a persistent, at-a-glance view of context usage, costs, git status..."
**NEDOCUMENTAT - nu am putut extrage** schema JSON exacta trimisa pe stdin (campurile
`context_window.used_percentage` etc.) — fetch-ul a livrat doar introducerea paginii, nu tabelul
de schema (posibil mai jos in pagina, trunchiat de rezumator).
Nu am gasit, in niciuna din paginile de hook-uri (`hooks`, `hooks-guide`, `plugins-reference`),
vreo mentiune ca acelasi obiect JSON dat lui `statusLine` ar fi disponibil si unui hook obisnuit.
Structural, `statusLine` e configurat separat de `hooks` in `settings.json` si documentat ca
mecanism de sine statator, nu ca un tip de hook din lista de 5 (`command`/`http`/`mcp_tool`/
`prompt`/`agent`).
## 4. Exista un eveniment de hook dedicat contextului (prag, PreCompact cu date de ocupare)?
Pagina `hooks` listeaza evenimentul `PreCompact` ("Before context compaction") si `PostCompact`,
dar continutul extras nu contine schema de input pentru `PreCompact`:
> `PreCompact` are matcher pe ce a declansat compactarea (`"manual"` sau `"auto"`), dar campurile
> JSON de input nu sunt specificate in continutul extras.
Nu exista, in continutul extras din niciuna dintre pagini, un eveniment de tip "context threshold"
separat de `PreCompact`/`PostCompact`. **NEDOCUMENTAT - nu am putut extrage** schema completa a
`PreCompact` (posibil contine deja procente de ocupare — nu s-a putut confirma nici infirma).
Campurile COMUNE confirmate pentru toate evenimentele de hook (din tabelul extras pe pagina
`hooks`):
```
session_id, prompt_id, transcript_path, cwd, scratchpad_dir, permission_mode,
effort.level, hook_event_name, agent_id (doar subagenti), agent_type (doar subagenti)
```
Niciun camp de tokeni/usage/context in aceasta lista. Coincide cu dovada empirica deja detinuta
(inputul real al `SubagentStop` capturat anterior nu are camp de tokeni).
## 5. Schema unei linii `assistant` din transcriptul `.jsonl` — are `usage`?
**Verificat direct pe disc, DA** — nu doar documentatie, dovada empirica reala:
```
grep -o '"usage":{[^}]*}' bdb0bf8c-a086-4d46-b08d-545a92e5c32c.jsonl | head -3
```
rezultat (identic pe primele linii verificate):
```json
"usage":{"input_tokens":2,"cache_creation_input_tokens":34191,"cache_read_input_tokens":31003,"output_tokens":1710,"output_tokens_details":{"thinking_tokens":198}
```
Deci fiecare linie `assistant` din `.jsonl` are un obiect `message.usage` cu:
`input_tokens`, `cache_creation_input_tokens`, `cache_read_input_tokens`, `output_tokens`,
`output_tokens_details.thinking_tokens`.
Asta e citibil de orice proces cu acces la fisier (inclusiv un hook `command`, daca i s-ar da
calea) — dar hook-ul primeste doar `transcript_path` ca referinta, nu campul de usage direct in
inputul lui JSON. Un hook `command` ar putea *citi singur* fisierul si insuma `usage` peste toate
liniile `assistant` ca sa aproximeze contextul consumat — asta nu necesita „function hooks",
functioneaza si cu un hook shell obisnuit care are `jq`/`python` la indemana si stie
`transcript_path`.
## Verdict
**Poate un hook sa afle contextul consumat, si pe ce cale — da, dar nu prin niciun camp direct din
inputul JSON al hook-ului**, indiferent daca hook-ul e `command` sau (in SDK, nu in Claude Code)
o functie in-proces:
- **Calea documentata si confirmata empiric**: orice hook `command` (sau function-hook din SDK,
daca primeste `transcript_path` in input — nedocumentat exact, dar plauzibil, campul e comun
tuturor evenimentelor conform tabelului din `hooks`) poate **citi singur** `transcript_path` de
pe disc si insuma `message.usage` din liniile `assistant` ca sa aproximeze tokenii consumati.
Asta confirma ce a spus deja Marius implicit: se poate afla, dar prin citire activa a
transcriptului, nu pentru ca hook-ul primeste un camp gata calculat.
- **Nu exista, in ce am putut extrage din documentatie, niciun camp `usage`/`context_window`/
`tokens` in inputul JSON dat direct hook-ului** (nici la `command`, nici — din cate am putut
verifica — mentionat pentru function hooks din SDK).
- **`statusLine` e mecanismul care primeste `context_window.used_percentage` gata calculat**, dar
e un canal separat de `hooks` in `settings.json`, nu un tip de hook; nu am gasit dovada ca acel
obiect ar fi expus si catre hook-uri.
- Afirmatia initiala („niciun hook Claude Code nu poate afla cat context s-a consumat") e
**partial gresita**: un hook nu primeste tokenii de-a gata, dar poate sa-i afle citind singur
`transcript_path` — cale disponibila oricarui hook `command`, nu doar unor ipotetice „function
hooks".
## Goluri ramase (NEDOCUMENTAT, de reverificat cu citire directa a paginii, nu WebFetch)
- Schema completa de tip TypeScript pentru `HookCallback`/`HookCallbackMatcher`/`HookEvent` din
SDK (`code.claude.com/docs/en/agent-sdk/typescript`) — pagina prea mare, WebFetch a trunchiat de
doua ori la rand.
- Schema JSON completa trimisa pe stdin catre `statusLine` (`code.claude.com/docs/en/statusline`).
- Schema completa de input pentru `PreCompact` (`code.claude.com/docs/en/hooks`).

View File

@@ -1,167 +0,0 @@
diff --git a/COMUN/clase/onom_curs.vc2 b/COMUN/clase/onom_curs.vc2
index 4042942..7fb9fd6 100644
--- a/COMUN/clase/onom_curs.vc2
+++ b/COMUN/clase/onom_curs.vc2
@@ -15,6 +15,8 @@ DEFINE CLASS actualizare_curs_bnr AS _custom OF "_baza.vcx"
*m: scrie_curs_bnr
*p: ccursor
*p: clink
+ *p: clinkan
+ *p: clinkarhiva
*p: ctempfile
*p: ddata
*p: ncurs
@@ -26,6 +28,8 @@ DEFINE CLASS actualizare_curs_bnr AS _custom OF "_baza.vcx"
*<PropValue>
ccursor = ccrsbnr
clink = https://curs.bnr.ro/nbrfxrates.xml
+ clinkan = https://curs.bnr.ro/files/xml/years/nbrfxrates%.xml
+ clinkarhiva = https://curs.bnr.ro/nbrfxrates10days.xml
ctempfile = C:\temp\temp.xml
ddata = {}
Height = 17
@@ -37,6 +41,8 @@ DEFINE CLASS actualizare_curs_bnr AS _custom OF "_baza.vcx"
_memberdata = <VFPData>
<memberdata name="ctempfile" display="cTempFile"/>
<memberdata name="clink" display="cLink"/>
+ <memberdata name="clinkan" display="cLinkAn"/>
+ <memberdata name="clinkarhiva" display="cLinkArhiva"/>
<memberdata name="ccursor" display="cCursor"/>
<memberdata name="ddata" display="dData"/>
<memberdata name="ntip" display="nTip"/>
@@ -98,8 +104,13 @@ DEFINE CLASS actualizare_curs_bnr AS _custom OF "_baza.vcx"
ENDPROC
PROCEDURE citeste_curs_bnr
+ Lparameters tnSursa
Local loHTTP As 'winHTTP.winHTTPrequest.5.1'
- Local lcData, lcFisier, lcServer
+ Local lcData, lcFisier, lcServer, lcCubeCautat, ldCandidat, lnIncercari, lnAnCautat, llRetrasAnAnterior, lnStatus
+
+ If Vartype(tnSursa) <> "N"
+ tnSursa = 0
+ Endif
If Empty(This.cCursor)
This.cCursor = [ccrsbnr]
@@ -109,30 +120,90 @@ DEFINE CLASS actualizare_curs_bnr AS _custom OF "_baza.vcx"
Use In (This.cCursor)
Endif
- lcServer = This.cLink
+ Do Case
+ Case tnSursa = 1
+ lcServer = This.cLinkArhiva
+ Case tnSursa = 2
+ lnAnCautat = Year(This.dData - 1)
+ lcServer = Strtran(This.cLinkAn,"%",Alltrim(Str(lnAnCautat)))
+ Otherwise
+ lcServer = This.cLink
+ Endcase
loHTTP = Createobject('winHTTP.winHTTPrequest.5.1')
loHTTP.Open('GET', lcServer, .F.)
+ loHTTP.SetTimeouts(5000,5000,5000,15000)
loHTTP.setRequestHeader("Content-Type", "application/xml;")
poLog.Log(m.lcServer)
- loHTTP.Send()
-
- If loHTTP.Status = 200
+ Try
+ loHTTP.Send()
+ lnStatus = loHTTP.Status
+ Catch
+ lnStatus = 0
+ Endtry
+
+ If lnStatus = 200
lcFisier = loHTTP.Responsebody
lcFisier = Strextract(lcFisier,[</OrigCurrency>],[</Body>])
- lcData = Strextract(lcFisier,[date="],["])
- lcFisier= Strtran(lcFisier,[date="]+lcData+["],[])
+ If tnSursa = 0
+ lcData = Strextract(lcFisier,[date="],["])
+ lcFisier= Strtran(lcFisier,[date="]+lcData+["],[])
+ Else
+ lcCubeCautat = []
+ ldCandidat = This.dData - 1
+ lnIncercari = 0
+ llRetrasAnAnterior = .F.
+ Do While Empty(lcCubeCautat) And lnIncercari < 10
+ If tnSursa = 2 And Year(ldCandidat) < lnAnCautat And !llRetrasAnAnterior
+ llRetrasAnAnterior = .T.
+ lnAnCautat = lnAnCautat - 1
+ lcServer = Strtran(This.cLinkAn,"%",Alltrim(Str(lnAnCautat)))
+ loHTTP = Createobject('winHTTP.winHTTPrequest.5.1')
+ loHTTP.Open('GET', lcServer, .F.)
+ loHTTP.SetTimeouts(5000,5000,5000,15000)
+ loHTTP.setRequestHeader("Content-Type", "application/xml;")
+ poLog.Log(m.lcServer)
+ Try
+ loHTTP.Send()
+ lnStatus = loHTTP.Status
+ Catch
+ lnStatus = 0
+ Endtry
+ If lnStatus <> 200
+ repune_backup_cursoare(This.cCursor)
+ Release loUpdate,loIp,loUrl,lcData,lnSize,lcFisier,lcData
+ Return 1
+ Endif
+ lcFisier = Strextract(loHTTP.Responsebody,[</OrigCurrency>],[</Body>])
+ Endif
+ lcData = Str(Year(ldCandidat),4) + "-" + Padl(Alltrim(Str(Month(ldCandidat))),2,"0") + "-" + Padl(Alltrim(Str(Day(ldCandidat))),2,"0")
+ lcCubeCautat = Strextract(lcFisier,[<Cube date="]+lcData+[">],[</Cube>])
+ ldCandidat = ldCandidat - 1
+ lnIncercari = lnIncercari + 1
+ Enddo
+ If Empty(lcCubeCautat)
+ repune_backup_cursoare(This.cCursor)
+ Release loUpdate,loIp,loUrl,lcData,lnSize,lcFisier,lcData
+ Return 2
+ Endif
+ lcFisier = [<Cube>] + lcCubeCautat + [</Cube>]
+ Endif
lcFisier= Strtran(Strtran(lcFisier,[">],[" curs="]),[</Rate>],[" />])
Xmltocursor(lcFisier,This.cCursor)
- This.dData = Ttod(Ctot(lcData+[T000000]))+1
+ If tnSursa = 0
+ This.dData = Ttod(Ctot(lcData+[T000000]))+1
+ Endif
*!* modificare 03.06.2013
sterge_backup_cursoare(This.cCursor)
Else
repune_backup_cursoare(This.cCursor)
+ Release loUpdate,loIp,loUrl,lcData,lnSize,lcFisier,lcData
+ Return 1
Endif
*!* modificare 03.06.2013 ^
Release loUpdate,loIp,loUrl,lcData,lnSize,lcFisier,lcData
+ Return 0
ENDPROC
PROCEDURE Destroy
@@ -168,9 +239,20 @@ DEFINE CLASS actualizare_curs_bnr AS _custom OF "_baza.vcx"
Case (This.dData = Ttod(ltDataOra) And Hour(ltDataOra) <13) Or (This.dData = Ttod(ltDataOra) + 1 And Hour(ltDataOra) >= 13)
This.citeste_curs_bnr()
Case This.dData < Ttod(ltDataOra) Or This.dData = Ttod(ltDataOra) And Hour(ltDataOra) >= 13
- If amessagebox("Nu poate fi preluat automat cursul BNR. Doriti sa accesati pagina BNR?",4+32,"Confirmare") = 6
- goUrl("http://www.bnr.ro") && din wwutils.prg
+ lnStareBnr = This.citeste_curs_bnr(1)
+ If lnStareBnr = 2
+ lnStareBnr = This.citeste_curs_bnr(2)
Endif
+ Do Case
+ Case lnStareBnr = 0
+ && curs preluat din arhiva sau fisierul anual BNR
+ Case lnStareBnr = 2
+ amessagebox("Nu exista cursul BNR pentru data specificata!",48,"Atentie")
+ Otherwise
+ If amessagebox("Nu poate fi preluat automat cursul BNR. Doriti sa accesati pagina cursbnr.ro?",4+32,"Confirmare") = 6
+ goUrl("https://www.cursbnr.ro/")
+ Endif
+ Endcase
Otherwise
amessagebox("Nu exista cursul BNR pentru data specificata!",48,"Atentie")
Endcase

View File

@@ -1,368 +0,0 @@
# Patch propus (v3) — arhiva 10 zile + fisier anual BNR, link alternativ cursbnr.ro
Tinta: `COMUN\clase\onom_curs.vc2`, clasa `actualizare_curs_bnr`. Investigatie sursa:
`docs\raport_curs_bnr_politici_preturi.md`. Diff unificat aplicabil: `docs\patch_curs_bnr_arhiva.diff`.
Inlocuieste integral v1 si v2 (nu e delta) — aplicat pe fisierul original, neschimbat intre timp.
**Scop restrans de Marius, valabil din v2**: se lucreaza DOAR pe ramura `nTip = 1`, cazul de dupa
ora 13. `nTip = 2` si `nTip = 3` raman neatinse.
Doua verificari live, facute de Marius, folosite ca atare (nereverificate aici):
- `https://curs.bnr.ro/nbrfxrates10days.xml` -> HTTP 200, un `<OrigCurrency>`, mai multe
`<Cube date="YYYY-MM-DD">` descrescator, pana la 10 zile lucratoare (v2).
- `https://curs.bnr.ro/files/xml/years/nbrfxrates2026.xml` -> HTTP 200, ~245 KB, structura
IDENTICA (un `<OrigCurrency>`, apoi `<Cube date="...">` de la 2026-01-05 pana azi, ordine
crescatoare). Acelasi cod de extragere a unui `<Cube>` merge neschimbat.
## 1. Proprietati noi: `cLinkArhiva` si `cLinkAn`
Bloc afectat: definitia clasei (lista `*p:`, `PropValue`, `_memberdata`), `onom_curs.vc2:16-42`.
**VECHI** (`onom_curs.vc2:16-18`, in `*<DefinedPropArrayMethod>`):
```
*p: ccursor
*p: clink
*p: ctempfile
```
**NOU:**
```
*p: ccursor
*p: clink
*p: clinkan
*p: clinkarhiva
*p: ctempfile
```
**VECHI** (`onom_curs.vc2:26-29`, in `*<PropValue>`):
```
ccursor = ccrsbnr
clink = https://curs.bnr.ro/nbrfxrates.xml
ctempfile = C:\temp\temp.xml
```
**NOU:**
```
ccursor = ccrsbnr
clink = https://curs.bnr.ro/nbrfxrates.xml
clinkan = https://curs.bnr.ro/files/xml/years/nbrfxrates%.xml
clinkarhiva = https://curs.bnr.ro/nbrfxrates10days.xml
ctempfile = C:\temp\temp.xml
```
`cLinkAn` tine sablonul cu `%` in locul anului; se compune la rulare cu
`Strtran(This.cLinkAn,"%",Alltrim(Str(lnAn)))`.
**VECHI** (`onom_curs.vc2:37-40`, in `_memberdata`):
```
<memberdata name="ctempfile" display="cTempFile"/>
<memberdata name="clink" display="cLink"/>
<memberdata name="ccursor" display="cCursor"/>
```
**NOU:**
```
<memberdata name="ctempfile" display="cTempFile"/>
<memberdata name="clink" display="cLink"/>
<memberdata name="clinkan" display="cLinkAn"/>
<memberdata name="clinkarhiva" display="cLinkArhiva"/>
<memberdata name="ccursor" display="cCursor"/>
```
## 2. `citeste_curs_bnr` (`onom_curs.vc2:100-136`) — parametru `tnSursa` (0/1/2) + cautare inapoi + reincercare an anterior
Parametrul `tlArhiva` (logic, din v2) devine **`tnSursa`** (numeric): `0` = fisierul zilei (`cLink`,
comportamentul actual, apelul existent `This.citeste_curs_bnr()` fara argumente ramane identic —
parametrul netransmis e `.F.` logic, convertit explicit la `0` cu `Vartype`), `1` = arhiva 10 zile
(`cLinkArhiva`), `2` = fisierul anual (`cLinkAn`, cu anul `Year(This.dData - 1)`). Cod de retur
neschimbat fata de v2: `0` = succes, `1` = eroare HTTP/serviciu indisponibil, `2` = nicio zi din
fereastra cautata nu exista in raspuns.
Cautarea inapoi zi cu zi (max 10 incercari) din v2 e neschimbata pentru `tnSursa=1` si e refolosita
identic pentru `tnSursa=2`. Cazul special pentru `tnSursa=2`: daca in timpul cautarii data candidata
trece in anul precedent fata de anul fisierului deja descarcat (caz real: o factura din primele zile
ale lui ianuarie are nevoie de publicarea din 31 decembrie anul trecut), se face **o singura
reincercare** — un al doilea request HTTP pe fisierul anual anterior — si cautarea continua in noul
raspuns. Nu se reincearca a doua oara peste alt an (`llRetrasAnAnterior` blocheaza a doua trecere).
**VECHI** (`onom_curs.vc2:100-136`, integral):
```
PROCEDURE citeste_curs_bnr
Local loHTTP As 'winHTTP.winHTTPrequest.5.1'
Local lcData, lcFisier, lcServer
If Empty(This.cCursor)
This.cCursor = [ccrsbnr]
Endif
creeaza_backup_cursoare(This.cCursor) && modificare 03.06.2013
If Used(This.cCursor)
Use In (This.cCursor)
Endif
lcServer = This.cLink
loHTTP = Createobject('winHTTP.winHTTPrequest.5.1')
loHTTP.Open('GET', lcServer, .F.)
loHTTP.setRequestHeader("Content-Type", "application/xml;")
poLog.Log(m.lcServer)
loHTTP.Send()
If loHTTP.Status = 200
lcFisier = loHTTP.Responsebody
lcFisier = Strextract(lcFisier,[</OrigCurrency>],[</Body>])
lcData = Strextract(lcFisier,[date="],["])
lcFisier= Strtran(lcFisier,[date="]+lcData+["],[])
lcFisier= Strtran(Strtran(lcFisier,[">],[" curs="]),[</Rate>],[" />])
Xmltocursor(lcFisier,This.cCursor)
This.dData = Ttod(Ctot(lcData+[T000000]))+1
*!* modificare 03.06.2013
sterge_backup_cursoare(This.cCursor)
Else
repune_backup_cursoare(This.cCursor)
Endif
*!* modificare 03.06.2013 ^
Release loUpdate,loIp,loUrl,lcData,lnSize,lcFisier,lcData
ENDPROC
```
**NOU (v4, cu reparatia `Try/Catch` de la sectiunea 7):**
```
PROCEDURE citeste_curs_bnr
Lparameters tnSursa
Local loHTTP As 'winHTTP.winHTTPrequest.5.1'
Local lcData, lcFisier, lcServer, lcCubeCautat, ldCandidat, lnIncercari, lnAnCautat, llRetrasAnAnterior, lnStatus
If Vartype(tnSursa) <> "N"
tnSursa = 0
Endif
If Empty(This.cCursor)
This.cCursor = [ccrsbnr]
Endif
creeaza_backup_cursoare(This.cCursor) && modificare 03.06.2013
If Used(This.cCursor)
Use In (This.cCursor)
Endif
Do Case
Case tnSursa = 1
lcServer = This.cLinkArhiva
Case tnSursa = 2
lnAnCautat = Year(This.dData - 1)
lcServer = Strtran(This.cLinkAn,"%",Alltrim(Str(lnAnCautat)))
Otherwise
lcServer = This.cLink
Endcase
loHTTP = Createobject('winHTTP.winHTTPrequest.5.1')
loHTTP.Open('GET', lcServer, .F.)
loHTTP.SetTimeouts(5000,5000,5000,15000)
loHTTP.setRequestHeader("Content-Type", "application/xml;")
poLog.Log(m.lcServer)
Try
loHTTP.Send()
lnStatus = loHTTP.Status
Catch
lnStatus = 0
Endtry
If lnStatus = 200
lcFisier = loHTTP.Responsebody
lcFisier = Strextract(lcFisier,[</OrigCurrency>],[</Body>])
If tnSursa = 0
lcData = Strextract(lcFisier,[date="],["])
lcFisier= Strtran(lcFisier,[date="]+lcData+["],[])
Else
lcCubeCautat = []
ldCandidat = This.dData - 1
lnIncercari = 0
llRetrasAnAnterior = .F.
Do While Empty(lcCubeCautat) And lnIncercari < 10
If tnSursa = 2 And Year(ldCandidat) < lnAnCautat And !llRetrasAnAnterior
llRetrasAnAnterior = .T.
lnAnCautat = lnAnCautat - 1
lcServer = Strtran(This.cLinkAn,"%",Alltrim(Str(lnAnCautat)))
loHTTP = Createobject('winHTTP.winHTTPrequest.5.1')
loHTTP.Open('GET', lcServer, .F.)
loHTTP.SetTimeouts(5000,5000,5000,15000)
loHTTP.setRequestHeader("Content-Type", "application/xml;")
poLog.Log(m.lcServer)
Try
loHTTP.Send()
lnStatus = loHTTP.Status
Catch
lnStatus = 0
Endtry
If lnStatus <> 200
repune_backup_cursoare(This.cCursor)
Release loUpdate,loIp,loUrl,lcData,lnSize,lcFisier,lcData
Return 1
Endif
lcFisier = Strextract(loHTTP.Responsebody,[</OrigCurrency>],[</Body>])
Endif
lcData = Str(Year(ldCandidat),4) + "-" + Padl(Alltrim(Str(Month(ldCandidat))),2,"0") + "-" + Padl(Alltrim(Str(Day(ldCandidat))),2,"0")
lcCubeCautat = Strextract(lcFisier,[<Cube date="]+lcData+[">],[</Cube>])
ldCandidat = ldCandidat - 1
lnIncercari = lnIncercari + 1
Enddo
If Empty(lcCubeCautat)
repune_backup_cursoare(This.cCursor)
Release loUpdate,loIp,loUrl,lcData,lnSize,lcFisier,lcData
Return 2
Endif
lcFisier = [<Cube>] + lcCubeCautat + [</Cube>]
Endif
lcFisier= Strtran(Strtran(lcFisier,[">],[" curs="]),[</Rate>],[" />])
Xmltocursor(lcFisier,This.cCursor)
If tnSursa = 0
This.dData = Ttod(Ctot(lcData+[T000000]))+1
Endif
*!* modificare 03.06.2013
sterge_backup_cursoare(This.cCursor)
Else
repune_backup_cursoare(This.cCursor)
Release loUpdate,loIp,loUrl,lcData,lnSize,lcFisier,lcData
Return 1
Endif
*!* modificare 03.06.2013 ^
Release loUpdate,loIp,loUrl,lcData,lnSize,lcFisier,lcData
Return 0
ENDPROC
```
Explicatie tehnica pe scurt (nu intra in cod, doar aici):
- `tnSursa=0` (apel normal, nemodificat functional) merge exact pe ramura veche, pastreaza
`This.dData = publishingDate+1`.
- `tnSursa=1` — identic v2: cauta cel mai recent `Cube` cu data `<= This.dData - 1`, mergand inapoi
zi cu zi, max 10 incercari.
- `tnSursa=2` — acelasi mers inapoi zi cu zi, dar pe fisierul anual al anului `Year(This.dData-1)`;
daca data candidata trece in anul precedent fara sa fi gasit inca un `Cube`, face un singur request
suplimentar pe fisierul anului precedent si continua cautarea acolo. Daca acel request esueaza
(`Status<>200`), intoarce direct `1` (indisponibilitate), nu `2`.
- `This.dData` NU se rescrie pentru `tnSursa=1` sau `2` — ramane ziua ceruta, exact ce asteapta
`scrie_curs_bnr`.
## 3. `initializeaza` (`onom_curs.vc2:152-202`) — ramura `nTip=1`, cazul "prea tarziu azi": cascada + link alternativ
Se atinge STRICT ramura de la `onom_curs.vc2:170-173`. `nTip=2` si `nTip=3` raman neatinse.
**VECHI** (`onom_curs.vc2:170-173`):
```
Case This.dData < Ttod(ltDataOra) Or This.dData = Ttod(ltDataOra) And Hour(ltDataOra) >= 13
If amessagebox("Nu poate fi preluat automat cursul BNR. Doriti sa accesati pagina BNR?",4+32,"Confirmare") = 6
goUrl("http://www.bnr.ro") && din wwutils.prg
Endif
```
**NOU:**
```
Case This.dData < Ttod(ltDataOra) Or This.dData = Ttod(ltDataOra) And Hour(ltDataOra) >= 13
lnStareBnr = This.citeste_curs_bnr(1)
If lnStareBnr = 2
lnStareBnr = This.citeste_curs_bnr(2)
Endif
Do Case
Case lnStareBnr = 0
&& curs preluat din arhiva sau fisierul anual BNR
Case lnStareBnr = 2
amessagebox("Nu exista cursul BNR pentru data specificata!",48,"Atentie")
Otherwise
If amessagebox("Nu poate fi preluat automat cursul BNR. Doriti sa accesati pagina cursbnr.ro?",4+32,"Confirmare") = 6
goUrl("https://www.cursbnr.ro/")
Endif
Endcase
```
Cascada, exact cum a cerut Marius:
1. incearca arhiva 10 zile (`citeste_curs_bnr(1)`);
2. daca intoarce `2` (data lipsa din raspuns), incearca fisierul anual (`citeste_curs_bnr(2)`);
3. daca si acela intoarce `2` -> `"Nu exista cursul BNR pentru data specificata!"`;
4. daca oricare din cele doua intoarce `1` (eroare HTTP/timeout) -> mesajul de indisponibilitate,
cu oferta de a deschide **`https://www.cursbnr.ro/`** (nu mai `http://www.bnr.ro`, doar in aceasta
ramura — celelalte doua aparitii ale `goUrl("http://www.bnr.ro")` din fisier, ramura de dinainte
de ora 13 si `Otherwise`-ul cu `nTip` necunoscut, raman neschimbate).
`lnStareBnr` nu e declarata explicit — la fel ca `lcSql`, `lnSucces`, `ltDataOra` deja folosite in
aceeasi metoda fara declarare explicita (scop implicit PRIVATE al VFP); pastrat pentru consistenta
stilistica.
## Raspuns despre `nTip`
Neschimbat fata de v2: se acopera doar `nTip=1`; `nTip=2` si `nTip=3` raman neatinse, prin decizie
explicita a lui Marius.
## 4. Ce acopera
| Scenariu | Rezultat |
|---|---|
| Azi, dupa ora 13, Oracle nu are cursul zilei | arhiva 10 zile gaseste ziua anterioara (`lnStareBnr=0`), fara mesaj |
| Weekend (luni, dupa ora 13, vinerea nu era salvata local) | arhiva gaseste `Cube`-ul de vineri prin mersul inapoi zi cu zi |
| Sarbatoare legala (o zi lucratoare inconjurata de nelucratoare) | arhiva merge inapoi pana gaseste ultima zi publicata, fara lista de sarbatori in cod |
| Data mai veche de 10 zile lucratoare, dar in anul curent sau anul trecut | arhiva 10 zile esueaza cu `2`, se incearca fisierul anual (`cLinkAn`), care acopera restul anului |
| Factura pe inceput de ianuarie (ex. 2 ianuarie), cursul cerut e din 31 decembrie anul trecut | fisierul anual porneste pe anul curerii (`Year(This.dData-1)`), cautarea trece granita anului, se face o singura reincercare pe fisierul anului precedent |
| Data mai veche decat acopera si arhiva, si fisierul anual (inclusiv anul precedent, dupa reincercare) | `"Nu exista cursul BNR pentru data specificata!"` |
| BNR picat / timeout la oricare din cele doua incercari (arhiva sau an) | mesajul de indisponibilitate, cu oferta de a deschide `https://www.cursbnr.ro/` |
## 5. Riscuri deschise
1. **Al doilea request HTTP inline in bucla, doar pentru `tnSursa=2` la trecerea de an** — cost
suplimentar minor (o cerere HTTP in plus, o singura data, doar cand data ceruta cade in primele
zile ale unui an); restul cautarii ramane pe sirul deja descarcat in memorie.
2. **Confirmat prin fetch real**: structura `nbrfxrates2026.xml` e identica per-Cube cu celelalte
doua fisiere BNR, doar cu mai multe elemente `<Cube date="...">`, in ordine crescatoare — extractia
printr-un singur `Cube` inainte de `Xmltocursor` ramane corecta si aici.
3. **`repune_backup_cursoare` pe cursor deja inchis — risc preexistent, nu introdus acum** (aceeasi
observatie ca in v1/v2): `citeste_curs_bnr` inchide `This.cCursor` inainte de orice apel HTTP;
`repune_backup_cursoare` (`oproceduri_comune.prg:6358-6375`) face `Select` pe un alias deja inchis
in acel moment — comportament mostenit, nu nou, si in afara scope-ului acestui patch.
4. **URL-uri BNR pe host `curs.bnr.ro`**, confirmate live acum (`nbrfxrates10days.xml`,
`nbrfxrates2026.xml`); se pot schimba fara preaviz — de reverificat manual daca reapare mesajul de
indisponibilitate des dupa aplicare.
5. **Fisierul anual creste an de an** (~245 KB la mijlocul lui 2026) — cautarea foloseste tot
`Strextract` pe sirul deja incarcat in memorie, fara reincarcare per incercare; cost acceptabil
pentru o operatie declansata manual din UI, nu intr-o bucla de facturare in masa.
## 6. Cum se probeaza (fara sa ruleze acum)
Nu am rulat nimic din ce urmeaza — se lasa pentru proba manuala dupa aprobare si aplicare:
1. Aplica diff-ul (`docs\patch_curs_bnr_arhiva.diff`) pe `COMUN\clase\onom_curs.vc2` prin fluxul
text->binar existent (skill `roa-vfp-text-edit`, `txt2vcx.ps1` cu `-ProjectRoot D:\ROA\ROAFACTURARE`).
2. Caz "azi dupa ora 13": instantiaza `actualizare_curs_bnr` cu `nTip=1`, seteaza data sistemului
sau `ltDataOra` simulat dupa ora 13, cere `citeste_curs(Ttod(get_ora()))` pentru o zi pentru care
Oracle nu are inca rand — verifica in `poLog` ca al doilea link apelat e
`https://curs.bnr.ro/nbrfxrates10days.xml`.
3. Caz "mai vechi de 10 zile, in acelasi an": alege o data acoperita doar de fisierul anual —
verifica in `poLog` ca dupa arhiva urmeaza apelul catre `https://curs.bnr.ro/files/xml/years/nbrfxrates<an>.xml`.
4. Caz trecere de an: cere cursul pentru 2 ianuarie (anul curent), cand 31 decembrie anul trecut nu
e salvat local — verifica cele doua request-uri succesive catre fisierul anual curent si cel
anterior, si ca `This.dData` ramane 2 ianuarie (nu 31 decembrie).
5. Verifica in `This.cCursor` dupa apel: `Reccount()>0`, campurile `Currency`/`Curs`/`multiplier`
populate identic cu un fetch normal.
6. Verifica in Oracle, dupa `scrie_curs_bnr`, ca randul scris in `CURS` are `data` = ziua ceruta.
7. Caz negativ, mai vechi decat orice arhiva: seteaza `This.dData` cu cativa ani in urma — asteapta
mesajul "Nu exista cursul BNR pentru data specificata!".
8. Caz eroare retea: schimba temporar `cLinkArhiva` (sau `cLinkAn`) la un URL invalid — asteapta
mesajul de indisponibilitate si confirma linkul nou `https://www.cursbnr.ro/` in loc de
`http://www.bnr.ro`, doar pe aceasta ramura.
## 7. Reparatie (v4) — `loHTTP.Send()` neprotejat (host nerezolvabil)
Defect gasit de testul headless (Cazul 6, `docs\test_curs_bnr_headless.md`): `loHTTP.Send()` nu era
in `Try/Catch`. La host complet nerezolvabil (DNS esuat, eroare OLE 1429), executia sarea peste
blocul `If loHTTP.Status = 200 ... Else ... Return 1 ... Endif`, intra in bucla de cautare `Cube`
cu `lcFisier` nedefinit si iesea gresit pe `Return 2` ("data lipsa" in loc de "BNR picat").
Reparatie, pe ambele aparitii ale `loHTTP.Send()` din metoda (fisierul zilei/arhiva/an, si
reincercarea pe anul precedent):
```
Try
loHTTP.Send()
lnStatus = loHTTP.Status
Catch
lnStatus = 0
Endtry
```
`Status` se citeste o singura data, in interiorul lui `Try` — citit dupa, a doua exceptie OLE
("The data necessary...") ar fi picat imediat. `lnStatus` inlocuieste toate referintele la
`loHTTP.Status` din metoda si e adaugat la lista `Local` existenta. `Catch` gol, fara logare noua;
pe al doilea `Send()` comportamentul la esec ramane neschimbat (`repune_backup_cursoare` +
`Return 1`). Cod complet, integrat in blocul "NOU" de la sectiunea 2.

View File

@@ -1,133 +0,0 @@
# Plan QA + corectii: adaugare/editare FACTURA si AVIZ
Stare: **APROBAT 17.09.2026**. Mandat de executie continua: toate stories, commit dupa fiecare,
fara aprobare per story; singura exceptie e mockup-ul de la S4, care se arata inainte de
implementare. Handoff la limita de context. Ultima actualizare: 17.09.2026.
Fisier de stare viu asociat: `docs/progres_qa_factura.md` (se actualizeaza dupa FIECARE story).
Harti de pornire (deja scrise, read-only): `docs/qa_factura_harta.md`,
`docs/qa_factura_unelte.md`, `docs/qa_factura_hooks_ref.md`.
## 0. Unde se executa
Executia trece pe **VM 304** (are D:\ROA, VFP 9, acces Oracle ROA_CENTRAL/MARIUSM_AUTO).
Motiv: UI vizibil fara sa fure focusul de pe masina lui Marius. O sesiune noua acolo porneste
din acest fisier + `docs/progres_qa_factura.md`.
Momentul mutarii (decizie Marius): **dupa S0**, cu prototipul de context/handoff deja functional.
S0 se face pe masina curenta (nu cere UI); de la S1 incolo totul e pe VM 304.
Conexiune probe: `DO test_init_env_auto_roafacturare WITH 'CENTRAL','MARIUSM_AUTO','ROMFASTSOFT'`
(`COMUN\utile\Teste\test_init_env_auto_roafacturare.prg:11,16-18`). `gnAn=2026`, `gnLuna=8`,
`gnIdFirma=110`, `gnIdUtil=8` inainte de serii.
## 1. Constatari confirmate pe cod (baza planului)
| # | Constatare | Dovada |
|---|---|---|
| C1 | Ecranul e clasa `frm_facturare_articole2`, nu `.scx`; instantiata doar cand `llFacturareNoua` | `COMUN\clase\ofacturare.vc2:15936-22614`; `COMUN\programe\ofacturare.prg:246-248` |
| C2 | Pragul de 3 caractere = `ncharcountbegin=2` pe cele doua combo-uri + garda `Len(...) <= nCharCountBegin` | `ofacturare.vc2:17524-17537`, `17560-17574`, `463-496` |
| C3 | Cautarea implicita e "incepe cu" (`RefreshData(1)`); "contine" exista dar e ascunsa dupa Ctrl+Enter | `COMUN\clase\_cb_base.vc2:636-649,720`; `ofacturare.vc2:476-489` |
| C4 | Zero feedback in timpul cautarii: `KeyPress` cheama direct `RefreshData`, fara indicator, fara numar de rezultate, fara mesaj "nimic gasit" | `_cb_base.vc2:610-776` |
| C5 | Nu exista debounce: se cauta la fiecare tasta peste prag | idem C4 |
| C6 | Sursa duplicatelor pe articol e **in Oracle**, in `pack_facturare.cursor_preturi` (join cu politicile de pret) - nu in VFP | `ofacturare.vc2:446-461`; tabela `crm_politici_pret_art` folosita la `ofacturare.vc2:21755` |
| C7 | Nu exista niciun selector de lista de preturi in formular; se trimite `poDate.id_gestiune_init`, sau `poDate.listaid` doar cand `tip=45` | `ofacturare.vc2:453-457` |
| C8 | Controalele de jos sunt copii directe ale formularului, fara container, cu Top 742/745/767/789/792 si Anchor mixt 4 vs 12; nimic nu le repozitioneaza la runtime | `ofacturare.vc2:18082-18195`; `Resize` la `21847-21854` nu le atinge |
| C9 | Factura vs aviz = `poDate.nIdTipDoc` 5 vs 6; helper `EsteAviz()` | `COMUN\programe\ofacturare_antet.prg:16-18` |
**Necunoscuta blocanta**: corpul `pack_facturare.cursor_preturi` nu e in working copy. Se citeste
din `ALL_SOURCE` pe MARIUSM_AUTO inainte de orice decizie despre listele de preturi (Story S3).
## 2. Constrangeri de perimetru
- `combosql` (`COMUN\clase\_cb_base.vc2:519`) e clasa de baza folosita de TOATE produsele ROA.
**Nu se modifica.** Tot comportamentul nou de cautare se suprascrie in `combosql_cautare`
(`ofacturare.vc2:407-531`), care e specifica facturarii.
- `ofacturare.vc2` traieste in `COMUN\` - dublu-versionat SVN+git, commit tintit din `COMUN\`.
- `.vc2` se editeaza cu script Python binar (nu `sed -i`, nu Edit pe linii cu octeti >0x7F);
diacriticele sunt cp1250. Write-back doar `txt2vcx.ps1 -ProjectRoot D:\ROA\ROAFACTURARE`.
- Dupa fiecare write-back: `MODIFY CLASS` + captura, apoi designerul se INCHIDE inainte de
urmatorul write-back.
- Un singur agent scrie pe `ofacturare.vc2` la un moment dat. Probele pe aceeasi clasa nu ruleaza
in paralel cu write-back-ul.
## 3. Stories
### S0 - Prototip context/handoff (se face PRIMUL, se foloseste pe restul planului)
Reutilizeaza ce exista: `context_watch.ps1` (in `D:\ROA\ROAGEST\COMUN\utile\`) deja citeste
`usage` din transcriptul JSONL si alerteaza la 250k/275k. Nu se rescrie. Se repara si se extinde:
1. **Bug activ**: hook-ul `SubagentStop` din `~/.claude/settings.json` re-injecteaza acelasi
mesaj runda dupa runda (observat azi, si notat deja in memorie). Cauza documentata: hook-ul
nu citeste `stop_hook_active` din JSON-ul de pe stdin si nu iese cu 0 cand e adevarat
(plafon 8 blocari consecutive). Fix: garda `stop_hook_active` la inceputul hook-ului.
2. **Fisier de stare scris de hook, nu de model**: `context_watch.ps1` primeste `-StareFile` si,
la pragul de avertisment, scrie/actualizeaza un antet in `docs/progres_<subiect>.md` (procent
context, ora, ultimul bloc). Modelul completeaza continutul; hook-ul garanteaza ca fisierul
exista si e datat chiar daca sesiunea moare.
3. **Compactare mai devreme si previzibila**: `CLAUDE_AUTOCOMPACT_PCT_OVERRIDE` in `env` din
settings (ex. 70), ca auto-compact-ul sa cada DUPA pragul de avertisment al lui
`context_watch`, nu inaintea lui. Compactarea NU poate fi declansata programatic - documentat,
deci mecanismul ramane: hook avertizeaza -> modelul scrie handoff -> compactarea vine singura.
4. Se muta in `COMUN` (cerinta Marius) ca sa fie refolosibil de toate produsele ROA.
Proba: o sesiune de proba cu un subagent care se termina => hook-ul ruleaza o singura data;
fisierul de stare exista si are marca de timp.
### S1 - Baseline QA: dovada vizuala si scenarii
Pe VM 304, formular vizibil prin `vfp_ui_harness.ps1`:
- capturi la 1366x768 si la rezolutia de lucru, pentru FACTURA si pentru AVIZ;
- captura zonei de jos (totaluri+discount) cu formularul la inaltime minima si maximizat -
dovada pentru C8 si pentru comportamentul Anchor 4 vs 12;
- scenariul de cautare: tastare progresiva pe un cod cunoscut din MARIUSM_AUTO, captura dupa
fiecare caracter - dovada pentru C2/C4;
- articol prezent in mai multe politici: captura cu randurile duplicate - dovada pentru C6.
Livrabil: `docs/qa_factura_baseline.md` + capturi. Nicio modificare de cod in S1.
### S2 - Cautare articole (toate cele patru cerinte)
In `combosql_cautare`, fara sa atinga `combosql`:
- prag de la primul caracter (`ncharcountbegin=0` pe cele doua instante + garda actualizata);
- cautare "contine" implicita, si pe cod si pe denumire;
- debounce ~250ms + limita de randuri, ca sa nu plece o interogare per tasta;
- feedback: indicator "se cauta...", numarul de rezultate, mesaj explicit la zero potriviri.
Proba: headless pe filtrul construit (asertii pe sirul trimis catre `cursor_preturi`) + proba UI
vizibila pe VM pentru feedback si latenta.
### S3 - Selector de lista de preturi (implicit lista clientului)
**Depinde de citirea `pack_facturare.cursor_preturi` din `ALL_SOURCE` pe MARIUSM_AUTO.**
Decizie Marius 17.09.2026: citirea `ALL_SOURCE` e permisa, iar modificarea pachetului e permisa
**doar pe MARIUSM_AUTO**, prin script `wip13_*.sql.txt` idempotent (`CREATE OR REPLACE`),
inregistrat in registrul de scripturi. Nimic nu pleaca spre clienti.
Tinta functionala: un combo in formular, prefixat cu lista din politica clientului, plus optiunea
"Toate listele"; in modul implicit fiecare articol apare o singura data.
### S4 - Zona totaluri + discount, regandita ca bloc
Mockup HTML inainte de implementare (mockup-urile stau online, nu in `docs/`). Dupa aprobarea
mockup-ului: container dedicat, Anchor unitar, aliniere pe grila, etichete. Atentie: `Anchor=4`
face ca Top-ul design-time sa conteze la rulare - orice schimbare de inaltime se probeaza la
1366 maximizat.
### S5 - Regresie
Suita existenta + probele noi, pe FACTURA si pe AVIZ, adaugare si editare. Raport prin
`raport_teste.ps1`, nu loguri brute.
## 4. Ordine si commit
S0 -> S1 -> S2 -> S3 -> S4 -> S5. Commit pe branch de lucru dupa fiecare story (cod `COMUN` din
`COMUN\`, restul din radacina), fara push; SVN si merge raman la Marius.
`docs/progres_qa_factura.md` se actualizeaza dupa fiecare story, nu la final.
Documentele de lucru din `docs/` nu se comit pe main.
## 5. Datorii deschise cunoscute la scrierea planului
- Corpul `pack_facturare.cursor_preturi` necitit - blocheaza S3.
- Cifra plafonului de blocari repetate ale unui stop hook: 8 (confirmat in ghid) vs 100
(`CLAUDE_CODE_STOP_HOOK_BLOCK_CAP`), nereconciliat.
- Daca `PreCompact` poate bloca compactarea prin exit 2: nedocumentat.

View File

@@ -1,99 +0,0 @@
# Progres QA factura/aviz
Plan: `docs/plan_qa_factura_aviz.md` (aprobat 17.09.2026, mandat de executie continua).
Acest fisier spune **unde s-a ajuns**, nu ce e de facut. Se actualizeaza dupa FIECARE story.
Harti de pornire (read-only, nu se refac):
`docs/qa_factura_harta.md` - harta de cod a formularului
`docs/qa_factura_unelte.md` - harness UI, headless, Oracle, hook-uri existente
`docs/qa_factura_hooks_ref.md` - referinta oficiala de hook-uri
## Stare pe story
| Story | Stare | Unde |
|---|---|---|
| S0 prototip context/handoff | **TERMINAT** 17.09.2026 | `docs/s0_context_hook.md`, `docs/handoff_activ.md` |
| S1 baseline QA cu capturi | de facut, **pe VM 304** | - |
| S2 cautare articole | de facut | - |
| S3 selector lista de preturi | blocat pana se citeste `pack_facturare.cursor_preturi` | - |
| S4 zona totaluri+discount | de facut, mockup inainte | - |
| S5 regresie | de facut | - |
## S0 - terminat (doua runde)
**Runda 2 (dupa ce prima s-a dovedit nedovedita).** Prima varianta a lui S0 a fost probata doar
cu stdin sintetic; in ciclul real bucla a continuat. Am instalat logare temporara pe hook si am
capturat inputul real al lui `SubagentStop`. Ce a iesit:
- `stop_hook_active` EXISTA in input si trece pe `true` la a doua declansare - garda e corecta.
Bucla observata venea de la agenti porniti inainte de fix.
- Mecanismul buclei: mesajul injectat trezeste agentul idle, el raspunde, redevine idle, hook-ul
se declanseaza iar.
- **Inputul contine `agent_transcript_path`** - transcriptul separat al subagentului oprit.
Deci un hook POATE masura contextul fiecarui subagent, nu doar al sesiunii.
Consecinta: `context_watch.ps1` are acum `-Subagent` (citeste `agent_transcript_path`, praguri
150k/200k, mai joase decat la sesiunea principala) si `-Json` (ambaleaza in
`hookSpecificOutput`). Garda `stop_hook_active` a intrat in script, deci comanda hook-ului e o
singura linie. Logarea temporara e scoasa.
Probe pe input real capturat: subagent peste prag -> mesaj cu numele agentului si 40k; sub prag
-> tacere; `stop_hook_active=true` -> tacere. Comis: `9823f1e` pe `qa-factura-s0`.
Compromis acceptat: mesajul vechi "verifica daca subagentul si-a scris starea pe disc" nu mai
apare la fiecare oprire, ci doar in avertismentul de context.
**Corectie de fond**, dupa ce Marius a contestat afirmatia: e fals ca "niciun hook nu poate afla
contextul". Niciun hook nu primeste tokenii de-a gata (nici `command`, nici function hook din
SDK), dar orice hook primeste `transcript_path` si poate citi singur `usage` din JSONL. Doar
`statusLine` primeste `context_window.used_percentage` gata calculat, si nu e hook.
Detalii: `docs/hooks_functii_context.md`, condensate in `docs/handoff_activ.md`.
## S0 - runda 1
Trei modificari, toate probate (rezultatele probelor sunt in `docs/handoff_activ.md`):
1. `C:\Users\mmari\.claude\settings.json`, hook `SubagentStop`: garda `stop_hook_active`. Era un
`echo` static care reinjecta acelasi `additionalContext` la fiecare oprire de subagent - de
aici bucla observata azi (patru agenti invartindu-se in gol). Backup:
`settings.json.bak_20260917`.
2. `D:\ROA\ROAGEST\COMUN\utile\context_watch.ps1`: parametru nou `-StareFile`. La atingerea
pragului scrie/inlocuieste un singur antet pe primul rand al fisierului de stare
(`<!-- context_watch: data ora | tokeni: N | nivel: ... -->`), fara sa atinga restul
continutului. Fara `-StareFile` comportamentul e neschimbat.
3. `settings.json`, `env`: `CLAUDE_AUTOCOMPACT_PCT_OVERRIDE`. Pus initial pe 70 - **gresit**:
fereastra e 1M, deci 70% = 700k, la trei ori distanta de pragul de handoff de 250k. Marius a
prins eroarea. Valoarea corecta, aplicata: **30** (=300k), imediat peste pragul max de 275k.
Ce s-a stabilit si nu se mai rediscuta:
- Compactarea **nu** poate fi declansata programatic dintr-un hook. Mecanismul ramas: hook
avertizeaza -> modelul scrie handoff-ul -> compactarea vine singura la pragul coborat.
- Niciun hook nu primeste numarul de tokeni. Singura sursa e transcriptul JSONL
(`transcript_path`), pe care `context_watch.ps1` il citea deja.
- `D:\ROA\ROAGEST\COMUN` si `D:\ROA\ROAFACTURARE\COMUN` sunt checkout-uri separate ale aceluiasi
repo `comun.git`. Scriptul e deci deja in biblioteca partajata; nu trebuie mutat.
Ramas deschis din S0:
- Proba pe un ciclu real cu subagent viu (garda a fost probata in izolare, cu stdin sintetic).
Se confirma implicit la prima rulare de subagent din sesiunea urmatoare.
- `settings.json` trimite la `context_watch.ps1` prin calea din checkout-ul ROAGEST. Pe VM 304
trebuie verificat ca `D:\ROA\ROAGEST\COMUN\utile\context_watch.ps1` exista acolo, altfel
hook-ul tace fara sa dea eroare.
## VM 304 - ce s-a aflat
Din `docs/vm304_acces.md`: Proxmox VM ID 304 "Win11-Marius", nod `pvemini`, clona lui VM 303.
Acces probat si functional: `ssh root@10.0.20.201 'qm agent 304 ping'` -> raspunde;
comenzi in VM prin `qm guest exec 304 -- <cmd>`. **Nu exista share UNC** catre discul ei.
Documentatia NU mentioneaza VFP sau client Oracle instalat - de verificat pe teren, planul le
presupune. Credentialele sunt documentate in `E:\proiecte\ROMFASTSQL\docs\` (nu se copiaza).
Marius a cerut explicit copierea directa si `roa_sync` rulat pe VM. Pachetul de pornire e
pregatit in scratchpad (`pachet_vm304\` cu `PORNIRE.md` + cele 8 documente + `context_watch.ps1`);
roa_sync pe VM a fost anulat (svn fara credentiale in sesiunea 0); il ruleaza Marius.
## Urmatorul pas
S1, **pe VM 304**: sesiune noua care porneste din `docs/plan_qa_factura_aviz.md` + acest fisier.
Baseline vizual pentru FACTURA si AVIZ, capturi la 1366x768 si maximizat, dovada pentru
constatarile C2, C4, C6 si C8 din plan. Nicio modificare de cod in S1.

View File

@@ -1,161 +0,0 @@
# Harta cod: adaugare/editare FACTURA si AVIZ (read-only)
Sursa: text FoxBin2Prg deja in-tree (`.vc2`/`.prg`), citit cu `Grep`/`Read` si indexat cu
`vfp_symbols.ps1` (`-CacheRoot/-ProjectRoot D:\ROA\ROAFACTURARE`,
`-IndexFile D:\ROA\_vfp_textcache\roafacturare\_symbols.tsv`). Nicio modificare de fisier.
## 1. Formulare si clase implicate
Nu exista formular `.scx` pentru factura/aviz in acest proiect (`.scx`-urile din arbore sunt
doar utilitare: `frm_borderou_facturi.scx`, `frm_import_efactura.scx` etc.). Ecranul de
adaugare/editare e o **clasa `.vcx` instantiata prin `Createobject()`**, nu un `DO FORM`:
- **`frm_facturare_articole2`** — `COMUN\clase\ofacturare.vcx` (text `COMUN\clase\ofacturare.vc2:15936-22614`).
Formularul unificat, curent, folosit in productie pentru factura SI aviz. Instantiat in
`COMUN\programe\ofacturare.prg:248` (`ofrmdetaliifactura = Createobject('frm_facturare_articole2')`)
si in `COMUN\clase\ofacturare_comun.vc2:4028`, doar cand `llFacturareNoua` e adevarat
(`COMUN\programe\ofacturare.prg:246-248`).
- **`frm_facturare_articole`** — acelasi fisier, `ofacturare.vc2:11123-15936`. Varianta mai veche;
in cod de productie nu mai e instantiata (doar in probele din
`COMUN\utile\Teste\facturare_unificat\*.prg` si `COMUN\utile\Teste\editare_factura\creeaza_documente_s8.prg:571`,
`emite_document_stoc_s93.prg:264`) — ramane in clasa dar calea curenta e `frm_facturare_articole2`.
- **Flux vechi, ne-unificat** (`llFacturareNoua = .F.`, `ofacturare.prg:255-266`) — alege un
formular de antet separat pe tip de document, fara grila unificata de articole:
- `frm_date_aviz_lucrare` — `ofacturare.vc2:7775` — pentru `tnTip` 27 sau 30.
- `frm_date_factura` — `ofacturare.vc2:8637` — pentru `tnTip < 21` sau in `(45,48,49,51,52)`.
- `frm_date_aviz` — `ofacturare.vc2:6721` — altfel (aviz).
- **`oAntetFacturare`** (`Define Class ... As Custom`) — `COMUN\programe\ofacturare_antet.prg:14`.
Logica de antet a lui `frm_facturare_articole2`: cautari (`do_cauta_*`), validare
(`valideaza_antet`), schimbarea tipului de document (`alege_tipdoc`, `schimba_tipdoc`).
Header-ul fisierului (`ofacturare_antet.prg:1-7`) spune explicit ca a fost **portata din
`frm_date_factura`/`frm_date_aviz`** cand s-a facut unificarea. Instantiata in
`frm_facturare_articole2.Init`: `This.oAntet = Createobject('oAntetFacturare')` (`ofacturare.vc2:21365`).
- **`oDateFactura`** (`poDate`) — `COMUN\programe\ofacturare_comun.prg:131`. Obiectul de stare al
documentului curent (creat in `ofacturare.prg:196`); tine `nIdTipDoc`, `nIdTipDocFactura`(=5),
`nIdTipDocAvizExpeditie`(=6), `id_gestiune_init`, `listaid`, `id_pol`, `zi_curs` etc.
- **`ofacturare_editare.prg`** (`COMUN\programe\ofacturare_editare.prg`, 1444 linii) — flux **separat**,
pentru editarea liniilor unei facturi/aviz deja emise (nu formularul de creare):
`IncarcaAntetFacturaEditare` (63), `IncarcaLiniiFacturaEditare` (209),
`PregatesteArticoleFacturaEditare` (605), `ScrieArticoleFacturaEditate` (741),
clasa `ArticoleNotaEditor As Custom` (1226). Inregistrat via
`Set Procedure To ofacturare_editare.prg Additive` (`Programe\roafacturare.prg:219`).
- **`combosql_cautare`** — clasa de baza pentru comboboxurile de cautare din grid (vezi pct. 2),
`ofacturare.vc2:407-531`, mostenind `combosql As combobox` din `COMUN\clase\_cb_base.vc2:519`.
## 2. Zona de introducere articole (cautare cod material / denumire)
In grila `grd_factura` a lui `frm_facturare_articole2`, coloanele au cate un combobox de tip
`combosql_cautare`:
- `grd_factura.cCodMat.cboCodmat` — `ADD OBJECT` la `ofacturare.vc2:17524-17537`,
`ncharcountbegin = 2`, `pcursorname = crsCodmat`, `pfieldactiv = codmat`.
- `grd_factura.cDenumire.cCboDenumire` — `ADD OBJECT` la `ofacturare.vc2:17560-17574`,
`ncharcountbegin = 2`, `pcursorname = crsDenumire`, `pfieldactiv = denumire`.
**Pragul de caractere**: logica e in `combosql_cautare.refreshdata` (`ofacturare.vc2:463-496`):
```
lnTip = Iif(Type('tnTip') = 'N', m.tnTip, 0)
If m.lnTip <> 0 And Len(Alltrim(This.cSearchString)) <= This.nCharCountBegin
Return .T. && nu cauta, iese
Endif
```
Cu `ncharcountbegin = 2` pe ambele combo-uri, filtrarea porneste abia cand
`Len(cSearchString) > 2`, adica de la **al treilea caracter tastat**.
**Interactivechange/Keypress**: nu sunt suprascrise in `combosql_cautare` — vin din parintele
`combosql` (`COMUN\clase\_cb_base.vc2:519`):
- `InteractiveChange` (linia 606-608): doar reseteaza `cSearchString`.
- `KeyPress` (linia 610-776): construieste `csearchstring` caracter cu caracter (sageti,
Del/Backspace tratate separat), apoi cheama `This.RefreshData(1)` = cautare "incepe cu"
(linia 720) sau, la Ctrl+Enter, `RefreshData(2)` = cautare "contine" (liniile 636-649).
**Filtrul/SQL aplicat**: `refreshdata` seteaza `cfiltrucod`/`cfiltruden` in functie de tip
(`ofacturare.vc2:476-489`):
```
Case lnTip = 1 (incepe cu): cfiltrucod/cfiltruden = cSearchString + '%'
Case lnTip = 2 (contine): cfiltrucod/cfiltruden = '%' + cSearchString + '%'
```
apoi `cursor_preturi_call()` (`ofacturare.vc2:446-461`) construieste apelul catre pachetul
Oracle `pack_facturare.cursor_preturi(?poDate.zi_curs,?poDate.tip,?poDate.id_valuta,
?poDate.id_gestiune_init sau ?poDate.listaid,?gnLuna,?gnAn,?gnIdUtil,?gnIdSucursala,
?pcFiltruCod,?pcFiltruDen)` (sau `pack_facturare.cursor_gestiune(...)` cand `poDate.tip = 41`),
executat prin `goExecutor.oExecuta` in `selectdata` (linia 514-529). Corpul PL/SQL al
pachetului nu e in acest repo VFP (schema Oracle) — nu l-am putut citi read-only de aici.
Proprietatile `csourcesql`/`csourcewhere` de pe `ADD OBJECT` (ex. `select codmat, denumire, ...
from vnom_articole ... where inactiv = 0`) sunt doar sablonul design-time al cursorului gol
(`creeaza_cursor_gol`, linia 426), nu interogarea reala rulata la tastare.
**Sincronizare cod<->denumire** dupa alegere: `grd_factura.cCodMat.cboCodmat.LostFocus`
(`ofacturare.vc2:22088-22109`) si perechea ei `cDenumire.cCboDenumire.LostFocus`
(22115-22136) cheama `thisform.do_adauga_articol_cautat(loArticol, loArticol.cantitate)`
si fortez re-creerea cursorului celeilalte combo (seteaza `cSearchString` cu valoarea gasita
si reseteaza `RowSource`), ca sa afiseze articolul ales in ambele coloane.
## 3. Lista de preturi / politici de pret
- `poDate.id_gestiune_init` e parametrul implicit trimis la `pack_facturare.cursor_preturi`;
cand `poDate.tip = 45` se trimite `poDate.listaid` in loc (`ofacturare.vc2:453-457`).
- De ce poate aparea un articol de mai multe ori: interogarea serverului (Oracle,
`pack_facturare.cursor_preturi`) uneste nomenclatorul de articole cu politicile de pret —
confirmarea client-side ca politicile sunt cheia vine din `modifica_lista_preturi`
(`ofacturare.vc2:21735-21837`), care interogheaza direct tabelele de politici:
`Select id_pol_art, id_venchelt, proc_tvav from crm_politici_pret_art where id_pol = ...
and id_articol = ...` (linia 21755) si scrie prin `pack_preturi.adauga_politica_pret_art`/
`pack_preturi.modifica_pret_pol_pret_art` (21779, 21792). Corpul exact al join-ului din
`cursor_preturi` e in PL/SQL, nevizibil din acest working copy VFP — afirmatia despre
"un rand per politica aplicabila" e o inferenta din tabelele folosite, nu o citire directa
a interogarii.
## 4. Zona de jos: controale de discount/totaluri
Toate sunt copii directe ale formularului `frm_facturare_articole2` (fara container
intermediar), clasa `_textbox` din `_baza.vcx`, asezate sub grila (Top ~742-792):
| Obiect | Top | Left | Width | Anchor | Note |
|---|---|---|---|---|---|
| `tx_total_baza_nat` (18123) | 745 | 17 | (implicit) | 4 | ReadOnly, `ControlSource=thisform.nbazaron` |
| `tx_total_baza_val` (18137) | 789 | 17 | (implicit) | 4 | ReadOnly, `thisform.nbazaval` |
| `tx_disc_factura_nat` (18082) | 745 | 351 | (implicit) | 4 | ReadOnly, `thisform.ndiscfactron` |
| `tx_disc_factura_procent` (18096) | 767 | 542 | 54 | 4 | editabil, `Value=0` |
| `tx_disc_factura_val` (18109) | 789 | 351 | (implicit) | 4 | ReadOnly, `thisform.ndiscfactval` |
| `tx_total_tva_nat` (18181) | 742 | 689 | (implicit) | 12 | ReadOnly, `thisform.ntvaron` |
| `tx_total_tva_val` (18195) | 792 | 689 | (implicit) | 12 | ReadOnly, `thisform.ntvaval` |
| `tx_total_factura_nat` (18151) | 742 | 833 | (implicit) | 12 | ReadOnly, FontBold, `thisform.ntotalron` |
| `tx_total_factura_val` (18166) | 792 | 835 | (implicit) | 12 | ReadOnly, FontBold, `thisform.ntotalval` |
(linii = `COMUN\clase\ofacturare.vc2`). `Anchor=4` = ancorat de Bottom (coboara odata cu
formularul); `Anchor=12` (4+8) = Bottom+Right, pe coloana totalurilor din dreapta. Niciun
`Height`/`Width` explicit pe majoritatea — mostenesc default-ul clasei `_textbox`; doar
`tx_disc_factura_procent` are `Width=54` explicit. Nu am gasit cod care repozitioneaza
aceste controale la runtime in `frm_facturare_articole2` — pozitionarea e strict design-time
+ `Anchor` (fara logica de `Resize`/`Move` pe ele; `Resize` al formularului, linia
21847-21854, nu le atinge explicit).
## 5. Diferente factura vs aviz pe `frm_facturare_articole2`
- **Discriminator unic**: `poDate.nIdTipDoc`, comparat cu `poDate.nIdTipDocFactura` (=5) /
`poDate.nIdTipDocAvizExpeditie` (=6). Helper: `oAntetFacturare.EsteAviz()`
(`ofacturare_antet.prg:16-18`): `Return poDate.nIdTipDoc = poDate.nIdTipDocAvizExpeditie`.
- **Setare initiala**, dupa `tnTip` primit la deschidere (`COMUN\programe\ofacturare.prg:196-206`):
```
Case tnTip = 27 or 30 -> nIdTipDoc = 6 (AVIZ)
Case tnTip < 21 sau tnTip in (45,48,49,51,52) -> nIdTipDoc = 5 (FACTURA)
Otherwise -> nIdTipDoc = 6 (AVIZ)
```
- **Schimbare interactiva**: combo `clb_fdoc.cboFdoc` -> `do_schimba_tipdoc`
(`ofacturare.vc2:20046-20071`) -> `oAntet.alege_tipdoc(valoare_combo)` mapeaza textul
("FACTURA"/"AVIZ"/"PROFORMA"/"BON FISCAL") pe id-ul de tip, apoi `oAntet.schimba_tipdoc(id)`
(`ofacturare_antet.prg:889-923`) actualizeaza `poDate.nIdTipDoc` si seriile. Exista o garda:
daca documentul e proforma si au fost deja adaugate articole, schimbarea tipului e blocata
cu `amessagebox` (`ofacturare.vc2:20050-20055`).
- **Ramuri `EsteAviz()` in `oAntetFacturare`** (aceleasi metode de cautare/validare servesc
ambele tipuri, dar cu cai diferite): `do_cauta_altele` (20-45), `do_cauta_client` (97-100),
`do_cauta_comanda` (240-243), `do_cauta_contract` (296-299), `do_cauta_gestiune_init`
(518-521), `do_cauta_lucrare` (553-556), `do_cauta_sectie` (633-636), `do_cauta_venchelt`
(708-711), `valideaza_antet` (762-766) — toate in `COMUN\programe\ofacturare_antet.prg`.
- **Fluxul vechi (ne-unificat)** folosea deja formulare separate per tip pentru antet
(`frm_date_factura` / `frm_date_aviz` / `frm_date_aviz_lucrare`, vezi pct. 1) — unificarea
descrisa in header-ul `ofacturare_antet.prg` a mutat logica lor comuna intr-o singura clasa,
ramura aleasa la runtime pe `poDate.nIdTipDoc`.

View File

@@ -1,393 +0,0 @@
# Referinta oficiala hooks Claude Code (pentru diagnosticul "subagent stop violation")
Surse fetch-uite azi (2026-09-17), toate redirecteaza de pe `docs.claude.com` pe `code.claude.com`:
- https://code.claude.com/docs/en/hooks (fost https://docs.claude.com/en/docs/claude-code/hooks)
- https://code.claude.com/docs/en/hooks-guide (fost .../hooks-guide)
- https://code.claude.com/docs/en/settings (fost .../settings)
- https://code.claude.com/docs/en/settings-reference
- https://code.claude.com/docs/en/settings-example
- https://code.claude.com/docs/en/env-vars
**Limitare tehnica intalnita**: paginile `hooks` si `settings-reference` sunt prea mari pentru
fetch-ul folosit (WebFetch trece continutul brut printr-un model mic inainte sa-l intoarca); la
cereri repetate pe aceeasi pagina, portiuni identice (tabelul "Common input fields", tabelul
"Exit code 2 behavior per event" pana la randul `Stop`, exemplele din `hooks-guide`) au iesit
IDENTIC de mai multe ori — acelea sunt tratate mai jos ca sigure/verbatim. O extractie initiala,
mai larga, a produs scheme JSON pentru `SessionStart`/`Stop`/`SubagentStop`/`PreCompact` care NU
s-au mai reprodus la cereri ulterioare tintite pe aceleasi sectiuni (acelea au raspuns explicit
"nu e in continutul furnizat, pagina e trunchiata") — acea extractie e tratata ca **nesigura** si
nu e citata mai jos. Sectiunile marcate **NEDOCUMENTAT** de mai jos sunt cele pe care nu am putut
sa le confirm verbatim, nu neaparat cele care lipsesc din documentatia reala.
## 1. Lista completa a evenimentelor de hook
Tabelul de mai jos e citat verbatim din `hooks-guide` (sectiunea "How hooks work"), confirmat prin
citire directa a continutului brut al fetch-ului (nu prin sumarizare):
> | Event | When it fires |
> | `SessionStart` | When a session begins or resumes |
> | `Setup` | When you start Claude Code with `--init-only`, or with `--init` or `--maintenance` in `-p` mode. For one-time preparation in CI or scripts |
> | `UserPromptSubmit` | When you submit a prompt, before Claude processes it |
> | `UserPromptExpansion` | When a user-typed command expands into a prompt, before it reaches Claude. Can block the expansion |
> | `PreToolUse` | Before a tool call executes. Can block it |
> | `PermissionRequest` | When a tool call needs a permission decision |
> | `PermissionDenied` | When auto mode denies a tool call, including denials without a classifier verdict. ... |
> | `PostToolUse` | After a tool call succeeds |
> | `PostToolUseFailure` | After a tool call fails |
> | `PostToolBatch` | After a full batch of parallel tool calls resolves, before the next model call |
> | `Notification` | When Claude Code sends a notification |
> | `MessageDisplay` | While assistant message text is displayed |
> | `SubagentStart` | When a subagent is spawned |
> | `SubagentStop` | When a subagent finishes |
> | `TaskCreated` | When a task is being created via `TaskCreate` |
> | `TaskCompleted` | When a task is being marked as completed |
> | `Stop` | When Claude finishes responding |
> | `StopFailure` | When the turn ends due to an API error |
> | `TeammateIdle` | When an agent team teammate is about to go idle |
> | `InstructionsLoaded` | When a CLAUDE.md or `.claude/rules/*.md` file is loaded into context. ... |
> | `ConfigChange` | When a configuration file changes during a session |
> | `CwdChanged` | When the working directory changes, for example when Claude executes a `cd` command. ... |
> | `DirectoryAdded` | When a working directory is added mid-session via `/add-dir` or the SDK `register_repo_root` control request |
> | `FileChanged` | When a watched file changes on disk. The `matcher` field specifies which filenames to watch |
> | `WorktreeCreate` | When a worktree is being created via `--worktree`, `isolation: "worktree"`, or for a background session. ... |
> | `WorktreeRemove` | When a worktree is being removed at session exit, when a subagent finishes, or when you delete a background session |
> | `PreCompact` | Before context compaction |
> | `PostCompact` | After context compaction completes |
> | `PreModelSwitch` | Before Claude Code applies a model switch that you or a client requested. Can block the switch |
> | `PostModelSwitch` | After the session's model changes, including changes Claude Code makes on its own, ... |
> | `Elicitation` | When an MCP server requests user input during a tool call |
> | `ElicitationResult` | After a user responds to an MCP elicitation, before the response is sent back to the server |
> | `SessionEnd` | When a session terminates |
Sursa: https://code.claude.com/docs/en/hooks-guide, sectiunea "How hooks work".
### Schema JSON exacta pe stdin/stdout pentru PreCompact / SessionStart / Stop / SubagentStop
**NEDOCUMENTAT (in sensul de mai sus: nu am putut confirma verbatim)**. Fetch-ul repetat pe
`hooks` (inclusiv varianta `.md`) intoarce constant tabelul "Common input fields" (comun tuturor
evenimentelor, vezi mai jos) si tabelul "Exit code 2 behavior per event" doar pana la randul
`Stop`; sectiunile individuale `### SessionStart`, `### PreCompact`, `### Stop`, `### SubagentStop`
cu exemplele lor JSON complete nu au putut fi extrase — raspunsul explicit al fetch-ului a fost
"the actual detailed 'Hook events' section ... appears to be cut off or not included" si "I
cannot find a subsection literally titled 'PreCompact'... in the provided content". Nu inseamna
ca documentatia oficiala nu contine acele scheme (aproape sigur le contine, pagina fiind
"Hooks reference" completa), ci ca uneltele disponibile in aceasta sesiune nu au putut sa le
aduca integral.
Ce **s-a confirmat verbatim** despre campurile comune (tabelul "Common input fields", identic la
doua fetch-uri separate pe `hooks` si pe `hooks.md`):
> | Field | Description |
> | `session_id` | Current session identifier |
> | `prompt_id` | UUID identifying the user prompt currently being processed. ... Absent until the first user input. Requires Claude Code v2.1.196 or later |
> | `transcript_path` | Path to conversation JSON. The transcript file is written asynchronously and may lag the in-memory conversation, so it may not yet include the current turn's most recent messages when a hook fires. Hooks that need the final assistant text of the current turn should use `last_assistant_message` on Stop and SubagentStop instead of reading the transcript |
> | `cwd` | Current working directory when the hook is invoked |
> | `scratchpad_dir` | Path to the session's scratchpad directory, where Claude keeps temporary working files. Absent when the session has no scratchpad or the temp directory is unavailable. Requires Claude Code v2.1.257 or later |
> | `permission_mode` | Current permission mode: `"default"`, `"plan"`, `"acceptEdits"`, `"auto"`, `"dontAsk"`, or `"bypassPermissions"`. ... |
> | `effort` | Object with a `level` field holding the effort level in effect when the hook runs... Present for events that fire within a tool-use context, such as `PreToolUse`, `PostToolUse`, `Stop`, and `SubagentStop`, when the current model supports the effort parameter. |
> | `hook_event_name` | Name of the event that fired |
>
> When running with `--agent` or inside a subagent, two additional fields are included:
>
> | Field | Description |
> | `agent_id` | Unique identifier for the subagent. Present only when the hook fires inside a subagent call. Use this to distinguish subagent hook calls from main-thread calls. |
> | `agent_type` | Agent name (for example, `"Explore"` or `"security-reviewer"`). Present when the session uses `--agent` or the hook fires inside a subagent. For subagents, the subagent's type takes precedence over the session's `--agent` value. |
Sursa: https://code.claude.com/docs/en/hooks, sectiunea "Common input fields" (confirmat identic
si pe varianta .md).
Nota: campul `trigger` (pentru `PreCompact`/`PostCompact`, valori `manual`/`auto`) si `source`
(pentru `SessionStart`, valori `startup`/`resume`/`clear`/`compact`/`fork`) sunt mentionate ca
**valori de matcher**, nu confirmate ca nume exact de camp JSON pe stdin — vezi tabelul de
matchere de la punctul 3.
Ce s-a confirmat despre `SessionStart` prin exemplu real de configurare (verbatim, din
`hooks-guide`, sectiunea "Re-inject context after compaction"):
> When Claude's context window fills up, compaction summarizes the conversation to free space.
> This can lose important details. Use a `SessionStart` hook with a `compact` matcher to
> re-inject critical context after every compaction.
>
> Claude Code adds plain text your command writes to stdout to Claude's context.
```json
{
"hooks": {
"SessionStart": [
{
"matcher": "compact",
"hooks": [
{ "type": "command", "command": "echo 'Reminder: use Bun, not npm. ...'" }
]
}
]
}
}
```
Schema de iesire (`hookSpecificOutput`, `additionalContext`, `systemMessage`, `decision`,
`continue`, `stopReason`) pentru cele 4 evenimente cerute: **NEDOCUMENTAT** in sensul de mai sus
pentru forma completa exacta. S-a confirmat insa, verbatim, forma generala de output pentru
`UserPromptSubmit` (acelasi tipar `hookSpecificOutput.additionalContext`, aplicabil probabil si
altor evenimente, dar nu s-a putut confirma explicit pentru `SessionStart`/`Stop`/`SubagentStop`):
> For `UserPromptSubmit` hooks, use `hookSpecificOutput.additionalContext` instead to inject text
> into Claude's context. Nest `additionalContext` inside `hookSpecificOutput`; if you place it at
> the top level of the JSON, Claude Code silently ignores it.
```json
{
"hookSpecificOutput": {
"hookEventName": "UserPromptSubmit",
"additionalContext": "Current branch: release-42. Deploy freeze until Friday."
}
}
```
> Other events use different decision patterns. For example, `PostToolUse` and `Stop` hooks use a
> top-level `decision: "block"` field, while `PermissionRequest` uses
> `hookSpecificOutput.decision.behavior`.
Sursa: https://code.claude.com/docs/en/hooks-guide, sectiunea "Structured JSON output".
## 2. Tokeni consumati / marimea contextului in input-ul hook-urilor
**Confirmat, de doua ori, cautare pe intreaga pagina `hooks`**: nu exista niciun camp de tokeni
in input-ul hook-urilor.
> Search for "stop_hook_active": No matches found for the term "stop_hook_active" on this page.
> Search for "token", "usage", and "input_tokens": No matches found for these terms on this page.
(cautarea a fost facuta explicit pe pagina "Hooks reference"; rezultatul e consistent la doua
apeluri separate — semnal ca reflecta continutul real, nu o presupunere a modelului de sumarizare)
Concluzie: singura sursa documentata pentru starea conversatiei ramane `transcript_path`
(fisier `.jsonl`), descris astfel (verbatim, vezi campurile comune de mai sus):
> Path to conversation JSON. The transcript file is written asynchronously and may lag the
> in-memory conversation, so it may not yet include the current turn's most recent messages when
> a hook fires.
**NEDOCUMENTAT**: schema exacta a unei linii din `transcript_path` (daca fiecare linie JSONL are
un obiect `usage` cu `input_tokens`/`cache_read_input_tokens`). Nu am gasit nicio sectiune care sa
descrie formatul intern al transcriptului; pagina de hooks il trateaza doar ca „path to
conversation JSON", fara schema de linie.
Singurul loc unde a aparut o notiune de "context folosit" e statusline-ul (alta functionalitate,
nu un hook), in exemplul din `settings-example`:
> "command": "jq -r '\"[\\(.model.display_name)] \\(.context_window.used_percentage // 0)% context\"'"
adica statusline-ul primeste `context_window.used_percentage` — dar acesta e inputul JSON al
comenzii de `statusLine`, nu al vreunui hook (`PreCompact`/`Stop`/etc.). Sursa:
https://code.claude.com/docs/en/settings-example, sectiunea "Your own settings".
## 3. Declansarea programatica a compactarii; PreCompact poate bloca?
Confirmat verbatim (tabelul de matchere, `hooks-guide`, sectiunea "Filter hooks with matchers"):
> | Event | What the matcher filters | Example matcher values |
> | `PreCompact`, `PostCompact` | what triggered compaction | `manual`, `auto` |
Nu exista alta explicatie a diferentei functionale dintre `manual` si `auto` in continutul pe care
am putut sa-l confirm — **NEDOCUMENTAT** in acest fetch (probabil documentat in sectiunea
"Hooks reference" pe care nu am putut-o extrage integral).
Despre blocare: tabelul "Exit code 2 behavior per event" s-a confirmat identic de 3 ori, dar
mereu trunchiat la randul `Stop`:
> | Hook event | Can block? | What happens on exit 2 |
> | `PreToolUse` | Yes | Blocks the tool call |
> | `PermissionRequest` | No | Exit code 2 isn't honored for this event and the permission flow proceeds unchanged. ... |
> | `UserPromptSubmit` | Yes | Blocks prompt processing and erases the prompt |
> | `UserPromptExpansion` | Yes | Blocks the expansion |
> | `Stop` | Yes | Prevents Claude from stopping, continues the conversation |
Randul pentru `PreCompact` (si `SubagentStop`, `SessionStart`) nu a putut fi extras — nici prin
fetch normal, nici prin varianta `.md`, nici prin cereri tintite doar pe randul lipsa.
**NEDOCUMENTAT explicit aici**: daca `PreCompact` poate bloca compactarea prin exit code 2.
(Rationament indirect, NEconfirmat ca fapt: linia generala din ghid — "Some events can't be
blocked: for SessionStart and others, exit 2 shows stderr to the user and execution continues" —
sugereaza ca exista o categorie de evenimente needitabile prin exit 2, dar nu specifica daca
`PreCompact` e in acea categorie sau in cealalta.)
Citat sigur, din `hooks-guide`, care mentioneaza explicit ca `SessionStart` NU poate fi blocat:
> Some events can't be blocked: for `SessionStart` and others, exit 2 shows stderr to the user and
> execution continues.
Declansare programatica a compactarii: **NEDOCUMENTAT** in continutul confirmat — nu am gasit un
flag/comanda explicita de tip "trigger compaction now" in paginile fetch-uite (hooks, hooks-guide,
settings, settings-reference, env-vars). Doar variabila urmatoare influenteaza PRAGUL, nu
declansarea manuala programatica:
> `CLAUDE_AUTOCOMPACT_PCT_OVERRIDE` — Set the percentage (1-100) of the auto-compact window at
> which auto-compaction triggers. Use lower values like `50` to compact earlier; the variable
> can't raise the threshold, so values above the default percentage are ignored. It applies only
> in sessions that compact before the model's context limit. Applies to both main conversations
> and subagents.
Sursa: https://code.claude.com/docs/en/env-vars.
## 4. Hook-uri in subagenti (Task/Agent); SubagentStop; bucla stop_hook_active
**Confirmat verbatim** (hooks-guide, sectiunea "Limitations"):
> Background subagents can't show a prompt in non-interactive mode. Claude Code still runs the
> hooks for their tool calls, and if no hook returns a decision, it denies the call. In an
> interactive session, background subagent prompts surface in your main session and the hooks
> fire as usual.
Deci: hook-urile de tip `PreToolUse`/`PostToolUse` etc. **se declanseaza si in interiorul
subagentilor**, pentru apelurile lor de unelte — confirmat explicit doar pentru cazul
`PermissionRequest`/permisiuni; pentru restul evenimentelor (`PreToolUse`, `PostToolUse` propriu-zise
in subagent) nu am gasit o fraza separata la fel de explicita, dar tabelul de scope de configurare
confirma ca hook-urile de subagent exista ca mecanism dedicat:
> | Location | Scope | Shareable |
> | [Subagent](/docs/en/sub-agents) frontmatter | While that subagent is running | Yes, defined in the subagent file |
Sursa: https://code.claude.com/docs/en/hooks-guide, sectiunea "Configure hook location".
`SubagentStop` — definitie confirmata din tabelul de evenimente (punctul 1):
> `SubagentStop` | When a subagent finishes
**NEDOCUMENTAT** (nesigur): o extractie initiala a afirmat ca exista fraza "Claude Code converts
a `Stop` hook here to `SubagentStop`, the event it fires when a subagent completes" — aceasta
fraza NU s-a mai reprodus la recitirea directa a continutului brut al `hooks-guide` (1065 de
linii citite integral), asa ca nu o citez ca fapt confirmat. Nu neg ca ar fi adevarata (e
plauzibila si consistenta cu restul mecanismului de scope pe subagent), doar ca nu am reusit sa o
verific verbatim in aceasta sesiune.
### stop_hook_active si bucla
**Confirmat verbatim**, din `hooks-guide`, sectiunea "Stop hook hits the block cap":
> Claude keeps working instead of stopping, then ends the turn with a warning that the Stop hook
> blocked too many consecutive times.
>
> Claude Code overrides a Stop hook after it blocks eight times in a row without progress. Your
> hook script needs to check whether it already triggered a continuation. Parse the
> `stop_hook_active` field from the JSON input and exit early if it's `true`:
```bash
#!/bin/bash
INPUT=$(cat)
if [ "$(echo "$INPUT" | jq -r '.stop_hook_active')" = "true" ]; then
exit 0 # Allow Claude to stop
fi
# ... rest of your hook logic
```
> If your hook legitimately needs more than eight iterations to converge, raise the cap with
> `CLAUDE_CODE_STOP_HOOK_BLOCK_CAP`.
Deci: **cauza tipica a buclei** e un hook `Stop`/`SubagentStop` care intoarce mereu o decizie de
blocare (exit 2 / `decision: "block"`) fara sa verifice `stop_hook_active`, astfel incat Claude
Code il tot re-invoca; plafonul e 8 blocari consecutive "fara progres", dupa care Claude Code
suprascrie hook-ul si opreste turul cu un avertisment. Campul `stop_hook_active` exista exact ca
sa permita hook-ului sa detecteze ca a mai fost invocat o data in acelasi ciclu de "stop" si sa
cedeze (`exit 0`) in loc sa continue sa blocheze.
Variabila de mediu asociata, confirmata din `env-vars` (extras separat, cu wording plauzibil dar
NEverificat printr-un al doilea fetch identic — trateaza ca moderat sigur, nu ca sigur):
> `CLAUDE_CODE_STOP_HOOK_BLOCK_CAP` — Maximum number of blocks a stop hook can request before
> Claude Code stops respecting further requests from that hook and logs a warning (default:
> `100`). Useful when a hook inadvertently loops and repeatedly requests stops.
Nota: valoarea implicita citata aici de fetch (`100`) **contrazice** cifra "eight times in a row"
din `hooks-guide` (confirmata de doua ori, sigura). Nu pot reconcilia cele doua cifre din
continutul disponibil — posibil ca 8 sa fie plafonul implicit "fara progres" mentionat in ghid, iar
`100` sa fie un plafon absolut diferit citit gresit de fetch-ul pe `env-vars` (surse nereconciliate,
posibil eroare de extractie pe aceasta a doua cifra). **Trateaza cifra "100" ca NEDOCUMENTAT/de
reverificat manual**, foloseste "8" ca fiind confirmat de doua ori pe pagina oficiala a ghidului.
## 5. Setari relevante in settings.json
Confirmat din tabelul settings-reference (randuri, fara detaliu de default/exemplu — sectiunile
detaliate de sub tabel nu au putut fi extrase, pagina prea mare):
> | Key | Description | Topic | Scope |
> | `autoCompactEnabled` | Turn automatic compaction off or on | Memory and context | Any file |
> | `autoCompactWindow` | Set how full the context gets before Claude Code compacts | Memory and context | Any file |
> | `cleanupPeriodDays` | Choose how many days Claude Code keeps transcripts before deleting them | Privacy and telemetry | Any file |
> | `env` | Set environment variables for every session and its subprocesses | Memory and context | Any file |
Sursa: https://code.claude.com/docs/en/settings-reference. **NEDOCUMENTAT** aici: valorile
implicite exacte pentru `autoCompactEnabled` si `autoCompactWindow` (sectiunile detaliate nu s-au
putut extrage).
Exemple reale confirmate (`cleanupPeriodDays`, `env`), din https://code.claude.com/docs/en/settings-example:
```json
// ~/.claude/settings.json — un dezvoltator
{
"...": "...",
"cleanupPeriodDays": 20
}
```
> Delete session transcripts and other local session data older than 20 days
```json
// .claude/settings.json — o echipa
{
"env": {
"CLAUDE_CODE_ENABLE_TELEMETRY": "1",
"OTEL_METRICS_EXPORTER": "otlp",
"OTEL_EXPORTER_OTLP_PROTOCOL": "grpc",
"OTEL_EXPORTER_OTLP_ENDPOINT": "http://collector.example.com:4317"
},
"hooks": {
"PreToolUse": [
{
"matcher": "Bash",
"hooks": [
{ "type": "command", "command": "${CLAUDE_PROJECT_DIR}/.claude/hooks/block-rm.sh" }
]
}
]
}
}
```
```json
// managed-settings.json — o organizatie
{
"...": "...",
"cleanupPeriodDays": 7
}
```
> Delete session transcripts and other local session data after 7 days
Variabile `CLAUDE_CODE_*` legate de context/compactare, confirmate din
https://code.claude.com/docs/en/env-vars:
> `CLAUDE_AUTOCOMPACT_PCT_OVERRIDE` — Set the percentage (1-100) of the auto-compact window at
> which auto-compaction triggers. Use lower values like `50` to compact earlier; the variable
> can't raise the threshold, so values above the default percentage are ignored. Applies to both
> main conversations and subagents.
> `CLAUDE_CODE_STOP_HOOK_BLOCK_CAP` — vezi punctul 4 (cifra de default nereconciliata).
**NEDOCUMENTAT** in acest fetch: nu am gasit alte variabile `CLAUDE_CODE_*` explicit legate de
"context size" ca numar de tokeni (cautarea pe `env-vars` a fost limitata la cuvintele
COMPACT/CONTEXT/TOKEN/STOP_HOOK; nu a intors nimic cu "CONTEXT" in nume).
## Rezumat pentru cine investigheaza "subagent stop violation"
- Nu exista niciun camp de tokeni/marime-context in inputul niciunui hook (confirmat, cautare
directa pe pagina oficiala). Singura sursa e `transcript_path`, iar formatul intern al liniilor
JSONL nu e documentat in paginile verificate.
- Bucla clasica de `Stop`/`SubagentStop` are o cauza documentata si un mecanism de iesire:
campul `stop_hook_active` pe input, plafon confirmat de "8 blocari la rand fara progres" dupa
care Claude Code preia controlul si opreste turul cu avertisment (`hooks-guide`, sectiunea
"Stop hook hits the block cap").
- Hook-urile ruleaza si in subagenti; input-ul lor primeste in plus `agent_id`/`agent_type` fata de
campurile comune.
- Schemele JSON complete (toate campurile) pentru `PreCompact`/`SessionStart`/`Stop`/`SubagentStop`
si detaliul exact al blocarii pentru `PreCompact` NU au putut fi confirmate verbatim in aceasta
sesiune — pagina oficiala "Hooks reference" (https://code.claude.com/docs/en/hooks) le contine
aproape sigur, dar depaseste ce a putut extrage fetch-ul disponibil; de reluat cu acces direct
(browser) daca e nevoie de schema exacta camp-cu-camp.

View File

@@ -1,191 +0,0 @@
# Inventar unelte QA/testare UI si infrastructura de progres — ROAFACTURARE
Inventar read-only, 17.09.2026. Fara propuneri, doar stare (citate `fisier:linie`).
## 1. `vfp_ui_harness.ps1` — orchestrator UI
Locatie: `D:\ROA\ROAFACTURARE\COMUN\utile\Teste\vfp_ui_harness.ps1`.
Complement in VFP: `D:\ROA\ROAFACTURARE\COMUN\utile\Teste\ui_harness.prg` (proceduri de handshake
incluse in testul .prg via `SET PROCEDURE TO ui_harness ADDITIVE`).
Parametri (`vfp_ui_harness.ps1:20-28`):
- `-TestPrg` (obligatoriu) — calea `.prg` a testului.
- `-Steps` (obligatoriu) — array de etichete, una per pas asteptat (`ready_<n>.txt`/`cont_<n>.txt`).
- `-StepTimeoutSec` (implicit 130), `-ReadyTimeoutSec` (implicit 180, pt. `ready_0` = afisarea formularului).
- `-ShotsDir` (implicit `<folder test>\screenshots`), `-SyncDir` (implicit `<folder test>\uisync`).
- `-Vfp` (implicit `C:\Program Files (x86)\Microsoft Visual FoxPro 9\vfp9.exe`).
Ce face exact:
- Precompileaza izolat (`_precompile.ps1`, proces copil) inainte de lansare (`vfp_ui_harness.ps1:145-154`).
- Lanseaza `.FXP`-ul cu `vfp9.exe -A <test.fxp>` (nu `.prg`), cu pana la 8 incercari daca testul nu
scrie START in 30s (`vfp_ui_harness.ps1:160-190`).
- Fereastra principala e mutata **off-screen** (`x=-4000`, `HWND_BOTTOM`, `SWP_NOACTIVATE`) imediat
ce apare `MainWindowHandle`, si re-impinsa la fiecare pas — **fara furt de focus**, cerinta Marius
17/07/2026 (`vfp_ui_harness.ps1:13-19,73-77`).
- Screenshot per pas via `PrintWindow` pe handle (flag `PW_RENDERFULLCONTENT=2`, fallback flag 0
daca iese gol) — nu `CopyFromScreen` (`vfp_ui_harness.ps1:106-131`).
- Bucla pasi: asteapta `ready_<n>.txt` (scris de test din `ui_harness.prg`), face screenshot, scrie
`cont_<n>.txt` ca sa continue testul (`vfp_ui_harness.ps1:200-221`).
- La final asteapta `done.txt`, omoara doar instantele `vfp9.exe` proprii (linie de comanda contine
folderul testului) (`vfp_ui_harness.ps1:133-140,223-231`).
`ui_harness.prg` — API apelat din testul VFP:
- `HarnessLog(mesaj)` (`ui_harness.prg:19-28`), `HarnessReady(n, nota)` (`ui_harness.prg:30-35`),
`HarnessWaitContinue(n, autoSec=30)` (`ui_harness.prg:37-51`), `HarnessStep(n, nota, autoSec)`
= Ready+WaitContinue (`ui_harness.prg:53-57`), `HarnessDone(status)` (`ui_harness.prg:59-64`).
- `HarnessWaitContinue` are auto-continue dupa `autoSec` (implicit 30s) daca orchestratorul nu
raspunde — testul merge si fara `vfp_ui_harness.ps1`, doar fara capturi (`ui_harness.prg:8-9,43-49`).
- `HarnessInit`: `SET SAFETY OFF` + `SET TALK OFF` defensiv (`ui_harness.prg:11-17`).
Limitari cunoscute:
- **Nu trimite input real** (fara `SendInput`/`keybd_event`/click injectat) — harness-ul doar
citeste semafoare si face screenshot; actiunile UI (click, taste) sunt executate **din interiorul
testului VFP** (apeluri directe de metode/evenimente), nu de PowerShell din afara.
- Watchdog-ul de dialoguri (`watchdog_vfp.ps1`) e un instrument separat, folosit doar pt. dialoguri
native neasteptate; dismiss-ul se face STRICT prin mesaje Windows tintite pe handle (`BM_CLICK`,
`WM_COMMAND IDCANCEL`, `WM_KEYDOWN/UP` ESCAPE, `WM_CLOSE`) — interzis explicit input real de
tastatura/mouse, masina fiind partajata cu utilizatorul (`watchdog_vfp.ps1:16-26`).
- Dialogurile VFP owner-drawn (ex. "View Parameter") pot sa nu raspunda la niciun mesaj — dismiss-ul
esueaza cinstit, ramane deschis pana la timeout (`watchdog_vfp.ps1:23-26`).
- Coloanele de grid nu se materializeaza sub `-A -T`/headless (`ColumnCount=0`, `RecordSource` sunt
artefacte) — cunoscut, documentat separat (memorie `grid-coloane-nu-se-materializeaza-headless`);
simptomul apare si in suitele UI (ex. `test_page3_articole` 14/2 in `docs\progres.md:67`, cele 2
FAIL = artefactul de baseline).
- `watchdog_vfp.ps1` clasifica orice fereastra noua diferita de `MainWindowHandle` ca dialog blocant
(nu dupa numele clasei — VFP refoloseste acelasi prefix de clasa si pt. shell, si pt. dialoguri
proprii) (`watchdog_vfp.ps1:6-9`).
## 2. Harness headless (skill `roa-vfp-headless-test`)
Skill: `D:\ROA\ROAFACTURARE\COMUN\skills\roa-vfp-headless-test\SKILL.md`.
Lansare probe (`SKILL.md:102-103`): `vfp9.exe -A -T "<script.prg>" <param>`, din PowerShell, cu
timeout si `$p.Kill()` daca nu iese (`$p.WaitForExit(120000)`). `-A` si `-T` obligatorii amandoua
(`SKILL.md:45-46`). Precompilare izolata obligatorie inainte (`_precompile.ps1`, proces copil),
altfel `vfp9 -A` poate deschide editorul in loc sa ruleze (`SKILL.md:28-29,99-100`).
Loguri: fiecare test scrie propriul `<test>_log.txt` langa `.prg` (convenit prin `gcUILog`/logica
proprie a testului); sinteza vine din `raport_teste.ps1` (`D:\ROA\ROAFACTURARE\COMUN\utile\Teste\raport_teste.ps1`),
care citeste **doar** fisierele `*_log.txt` dintr-un folder (implicit `achizitie_import`, parametrizabil
cu `-Dir`), numara linii `^PASS` si `^(FAIL|BUG)` si scoate PASS/FAIL per fisier + varsta (minute de
la ultima scriere) (`raport_teste.ps1:9-33`). `-Baseline <fisier>` marcheaza NOU vs. preexistent
(`raport_teste.ps1:7-8,16-19`). Regula: orchestratorul citeste doar acest raport, nu logurile brute
(`raport_teste.ps1:2-3`, trimite la `COMUN\docs\orchestrare-subagenti.md`).
Mock-uri disponibile in `D:\ROA\ROAFACTURARE\COMUN\utile\Teste\`:
- `mock_amessagebox.prg` — inlocuieste `FUNCTION amessagebox` din `oproceduri_comune.prg`; returneaza
6 (Da/OK) implicit, configurabil per apel prin `PUBLIC gnMockRaspuns` (raspuns generic),
`gnMockRaspunsTotal` (raspuns separat pt. mesajul "Actualizati totalul facturii"),
`gcMockUltimMesaj`/`gnMockUltimTip` (captura textul/tipul ultimului dialog, pt. asertii)
(`mock_amessagebox.prg:1-44`). **Trebuie incarcat PRIMUL** in `SET PROCEDURE` (VFP foloseste, la
nume duplicat, fisierul cautat primul, nu cel deschis cel mai recent) — vezi
`test_init_env_auto_roafacturare.prg:119-121` unde e adaugat inaintea listei aplicatiei.
LIMITA 1: nu acopera apeluri intra-fisier (ex. `verifica_partener_show_info` -> `amessagebox` in
acelasi `oproceduri_comune.prg`) — necesita mock dedicat per caz (`mock_amessagebox.prg:13-15`).
LIMITA 2: **nu acopera dialogurile de eroare ale lui `goExecutor`** — o interogare gresita agata
headless fara nicio linie in log (`mock_amessagebox.prg:16-17`). Nu exista un mock generic pentru
`goExecutor` la radacina `Teste\`; exista doar mock-uri punctuale per test in subfoldere (ex.
`achizitie_import\mock_cauta_alfa_tva11.prg`, `achizitie_import\mock_oscrie_in_fisiere.prg`).
- `watchdog_vfp.ps1` — pt. dialoguri native neprinse de ON ERROR/mock-uri (`SKILL.md:97-98`).
Init de mediu specific ROAFACTURARE: `test_init_env_auto_roafacturare.prg` — vezi punctul 3.
## 3. Conectare Oracle in probe (schema MARIUSM_AUTO)
Script: `D:\ROA\ROAFACTURARE\COMUN\utile\Teste\test_init_env_auto_roafacturare.prg`.
Apel: `DO test_init_env_auto_roafacturare WITH 'CENTRAL', 'MARIUSM_AUTO', 'parola'`
(`test_init_env_auto_roafacturare.prg:11`).
Valori implicite daca parametrii sunt goi (`test_init_env_auto_roafacturare.prg:16-18`):
- `tcHost` = `CENTRAL`
- `tcSchema` = `MARIUSM_AUTO`
- `tcPassword` = `ROMFASTSOFT`
Conexiunea propriu-zisa: `goConn = createobject("oConn")` +
`goConn.Connect(tcHost, tcSchema, tcPassword)` (`test_init_env_auto_roafacturare.prg:209-214`),
verificata prin `gnHandle > 0`. Executorul de SQL e `goExecutor = createobject("oExecutor")`
(`test_init_env_auto_roafacturare.prg:209`), folosit apoi pt. `oExecuta(...)`.
An/luna de lucru fixe (independente de `Date()`): `gnAn=2026`, `gnLuna=8` daca nu sunt pasate ca
parametri 4/5 (`test_init_env_auto_roafacturare.prg:36-37`). Firma: `gnIdFirma=110` implicit
(parametru 6 optional) (`test_init_env_auto_roafacturare.prg:39,221-224`).
Cale aplicatie fixa: `gcAppPath = 'D:\ROA\ROAFACTURARE\'` (`test_init_env_auto_roafacturare.prg:57`) —
seteaza `SET PATH`/`SET CLASSLIB`/`SET PROCEDURE` identic cu `roafacturare.prg`, plus mock-ul
`amessagebox` incarcat primul (`test_init_env_auto_roafacturare.prg:82-176`).
Nota din antet: variantele `test_init_env_auto_<produs>.prg` (ROACONT/ROAGEST/ACNPRO/ROADEF) au
cale + SET-uri specifice produsului, dar **partea de conexiune Oracle e identica** in toate
(`test_init_env_auto_roafacturare.prg:6-9`). Alegerea harness-ului gresit incarca alt working copy
silentios (`SKILL.md:21-23`).
## 4. Fisiere de progres/status existente
In `D:\ROA\ROAFACTURARE\docs\`:
- `progres.md` — **fisierul curent de stare**, actualizat de fiecare sesiune inainte sa se incheie;
planurile (`plan_0*.md`) spun *ce*, `progres.md` spune *unde s-a ajuns* (`progres.md:1-7`). Format:
titlu cu punctele acoperite, "Ultima actualizare: DD.MM.YYYY", apoi blocuri
`> **RUNDA DD.MM.YYYY — titlu.**` cu subsectiuni in proza (ce s-a schimbat, decizii numerotate,
teste rulate cu PASS/FAIL, capcane platite, ramas deschis) — nu tabel, istoricul sesiunilor nu se
pastreaza, doar starea la zi (`progres.md:1-9`).
- `handoff_*.md` — 6 fisiere curent (`handoff_update_romfast.md`, `handoff_cont_discount_667_709.md`,
`handoff_id_set_skilluri.md`, `handoff_idempotenta_id_set.md`, plus altele). Format observat in
`handoff_idempotenta_id_set.md:1-13`: titlu cu subiect+data, sectiuni numerotate H2 — "Livrabile
terminate" (tabel Ce/Unde/Stare), "Ce face X", "APROBAT dar NEEXECUTAT", "NEFINALIZAT/necomis",
"Comenzi de reluat" (bloc powershell), incheiat de regula cu inventar fisier:linie si stare
write-back per fisier atins.
- `diff_*.md` / `diff_*.patch` — diff-uri de revizuit inainte de commit (ex.
`diff_cont_discount_667_709.md`).
- `plan_index.md` + `plan_1*.md` — planuri pe story-uri (ex. `plan_10_integrare_contracte.md`).
- `docs\cercetare\*.md` — ~60 de fisiere de cercetare/investigatie punctuala (un subiect per fisier).
- `erori_deschise.md`, `livrare_13.md`, `raport_doc.md`, `raport_src.md`.
In `D:\ROA\ROAFACTURARE\COMUN\docs\` (relevante pt. orchestrare/testare, nu fisiere de progres in
sine ci proceduri):
- `orchestrare-subagenti.md` — procedura Regula zero (predare context), citata de `raport_teste.ps1:2-3`.
- `reguli_lucru.md` — index de reguli de lucru/testare, ruteaza pe zona la skill-ul potrivit.
- `depanare_testare_vfp.md`, `testare-ui-vfp.md` — capcanele detaliate din `SKILL.md` (sec. 6-7 / integral).
Nu exista fisiere de progres/status in `D:\ROA\COMUNROA\` — directorul `COMUNROA` (shared suite-wide,
distinct de `COMUN\` din interiorul ROAFACTURARE) nu are un `docs\progres.md` propriu vizibil din
acest working copy.
## 5. Hook-uri configurate
### `C:\Users\mmari\.claude\settings.json` (nivel user, global)
- `UserPromptSubmit` -> `context_watch.ps1 -Stdout` (mesaj injectat, exit 0).
- `SessionStart` -> `docs_revizie_check.ps1 -Stdout`.
- `PostToolUse` -> `context_watch.ps1 -OSinguraData` (async, timeout 20s).
- `PreCompact` -> mesaj fix de sistem (Regula zero — predare la compactare).
- `PostCompact` -> mesaj fix + `additionalContext` (Regula zero — predare obligatorie dupa compactare).
- `SubagentStop` -> mesaj fix (`additionalContext`) care aminteste orchestratorului sa verifice
daca subagentul si-a scris starea pe disc inainte sa-i dea sarcina urmatoare.
**Da, exista deja un hook `context_watch`**, script:
`D:\ROA\ROAGEST\COMUN\utile\context_watch.ps1` (in COMUNROA/ROAGEST, partajat intre proiecte, invocat
din `settings.json` cu cale absoluta acolo).
Ce face (`context_watch.ps1:1-9,55-84`):
- Citeste ultimul `"usage":{...}` din coada transcriptului JSONL al sesiunii curente (ultimii 400KB
din fisier, nu tot fisierul), insumeaza `input_tokens + cache_creation_input_tokens +
cache_read_input_tokens`.
- Praguri: `-Prag 250000` (implicit, nivel "avertisment") si `-PragMax 275000` (implicit, nivel
"max" — peste limita, predare OBLIGATORIE acum).
- La `avertisment`: mesaj care cere incheierea blocului curent, actualizarea handoff-ului pe disc,
delegarea catre subagenti a oricarei citiri/testari ramase.
- La `max`: mesaj cu procedura completa Regula zero (opreste lucrul, scrie handoff pe disc, confirma
in doua randuri, preda unei sesiuni noi).
- `-Stdout`: scrie mesajul pe stdout si iese cu 0 (folosit pe `UserPromptSubmit`, injecteaza context
fara sa esueze hook-ul). Fara `-Stdout`: scrie pe stderr si iese cu exit 2 (semnaleaza agentului).
- `-OSinguraData` (folosit pe `PostToolUse`): emite alerta o singura data per sesiune+prag, marcaj
fisier in `%TEMP%\claude_ctxwatch_<sesiune>_<nivel>.flag` — altfel mesajul s-ar repeta la fiecare
tool call (`context_watch.ps1:9-10,74-80`).
- Fara `-TranscriptPath` explicit, il citeste din stdin (JSON-ul hook-ului) sau, ca fallback, cauta
cel mai recent `.jsonl` din `~/.claude/projects/<cwd-encodat>\` (`context_watch.ps1:18-34`).
### `D:\ROA\ROAFACTURARE\.claude\` (nivel proiect)
Singurul fisier: `settings.local.json`, continut integral `{"outputStyle": "Concise"}`. **Niciun
hook definit la nivel de proiect** — toate hook-urile active vin din `settings.json` global de user.

View File

@@ -1,230 +0,0 @@
# Raport — mesajul "Nu poate fi preluat cursul BNR" la facturare pe politici de preturi
Investigatie READ-ONLY. Sursa: `D:\ROA\ROAFACTURARE` + `D:\ROA\ROAFACTURARE\COMUN`. Nicio editare,
nicio rulare, niciun write-back.
## 1. Textul exact al mesajului si locatia lui
Gasit in **`COMUN\clase\onom_curs.vc2`**, clasa `actualizare_curs_bnr`, `PROCEDURE initializeaza`:
- `onom_curs.vc2:171` — textul exact reclamat de utilizator:
```
If amessagebox("Nu poate fi preluat automat cursul BNR. Doriti sa accesati pagina BNR?",4+32,"Confirmare") = 6
goUrl("http://www.bnr.ro") && din wwutils.prg
Endif
```
- Variante inrudite, in aceeasi metoda:
- `onom_curs.vc2:175` si `:181` si `:190` — `amessagebox("Nu exista cursul BNR pentru data specificata!",48,"Atentie")`
- `onom_curs.vc2:193` — `amessagebox("Doriti sa accesati pagina BNR?",4+32,"Confirmare")` (ramura `Otherwise`, `nTip` necunoscut)
Nu exista alt loc in `.prg`/`.vc2`/`.sc2` (in ROAFACTURARE sau COMUN) cu text asemanator
("cursul BNR", "nbrfxrates", "curs BNR") legat de un mesaj de eroare — un singur punct de emitere,
in `actualizare_curs_bnr::initializeaza`.
## 2. Lantul de apel complet, de la facturare pana la mesaj
Punct de intrare din meniul de facturare pe lista/politica de preturi:
1. `COMUN\programe\oproceduri_facturare.prg:114-116` —
```
Procedure facturare_lista_de_preturi
Do politica.mpr
Endproc
```
Apelat din butonul `Page2.Cw1.do_actiune` (confirmat deja in `docs\plan_11_integrare_politici_preturi.md:16-18`).
2. `Meniuri\politica.mn2:12,37,43,46` — meniul `politica.mpr` cheama `factureaza(1)` (lei),
`factureaza(5)`/`(6)` (invoice), `factureaza(10)` (factura fiscala in valuta), `factureaza(9)`
(retur valuta) etc.
3. `COMUN\programe\ofacturare.prg:103` — `Procedure factureaza(tnTip, toFactura, toSursa)`. Pentru
tipurile de lista de preturi (1,5,7,10,22,23,29 — vezi comentariul din cod, `ofacturare.prg:140-149`)
populeaza articolele prin SQL server-side care trimite `poDate.zi_curs`:
`ofacturare.prg:266-308` (dupa cercetarea deja existenta `docs\cercetare\s4d_zi_curs_reactiv.md:144-146`):
```
Case Inlist(tnTip, 1,22,5,29,7,10,23) -> cursor_preturi(?poDate.zi_curs, ...)
```
4. Oracle, `pack_facturare.cursor_preturi` cheama **necondiționat**
`pack_facturare.verifica_cursuri_valute(V_DATA_CURS, V_ID_UTIL)`
(`D:\ROA\DATABASE\SCRIPTURI_CLAR\2026\08\ff_2026_08_09_01_COMUN_PACK_FACTURARE.sql:2153`), care
arunca `RAISE_APPLICATION_ERROR(-20005, 'Nu este setat cursul din data de ... pentru <valuta>!')`
(`:16268-16272`) daca vreo valuta din listele de preturi ale utilizatorului nu are curs valabil la
data ceruta (exclude doar moneda nationala).
5. VFP prinde eroarea Oracle: `COMUN\programe\ofacturare.prg:313-317` (identic la `:828-832`):
```
If lnSucces < 0
AMESSAGEBOX(goExecutor.oPrelucrareEroare(), 16, "Eroare")
If goExecutor.nEroare = 20005
vizualizeaza_curs(poDate.zi_curs)
ENDIF
```
6. `COMUN\programe\oproceduri_curs.prg:8-42` — `Procedure vizualizeaza_curs(tdDataCurs)` — deschide
ecranul de administrare a cursurilor: `loFrmCurs = Createobject("frm_curs",tdDataCurs)` (`:34`),
`loFrmCurs.Show(1)` (`:36`, modal). `frm_curs` (clasa de cautare/lista) NU declanseaza singura
preluarea BNR — utilizatorul apasa "Nou" (`But_nou1`) ca sa adauge cursul lipsa, ceea ce deschide
`frm_curs_nou`.
7. `COMUN\clase\onom_curs.vc2:1305-1326` — `frm_curs_nou::Init` leaga schimbarea datei/valutei
inregistrarii noi la fetch automat:
```
onom_curs.vc2:1323-1325
Bindevent(poRec,"data",Thisform,"actualizeaza_curs_bnr",1)
Bindevent(poRec,"iso_valuta",Thisform,"actualizeaza_curs_bnr",1)
Thisform.oactualizare = Createobject("actualizare_curs_bnr",gnTipActualizareCurs)
```
8. `COMUN\clase\onom_curs.vc2:1209-1226` — `frm_curs_nou::actualizeaza_curs_bnr` (declansat la fiecare
schimbare a datei/valutei din formular):
```
onom_curs.vc2:1211
This.oactualizare.citeste_curs(poRec.Data,poRec.iso_valuta)
```
9. `COMUN\clase\onom_curs.vc2:46-62` — `actualizare_curs_bnr::citeste_curs(tdZiua,tcIsoValuta)` —
`:50-52`: `If !Used(This.cCursor) Or tdZiua <> This.dData Then This.initializeaza(tdZiua)`.
10. `COMUN\clase\onom_curs.vc2:152-202` — `actualizare_curs_bnr::initializeaza(tdZiua)` — interogheaza
Oracle (`pack_curs.citeste_cotatii_curs`, `:158-159`); daca **`Reccount(This.cCursor)=0`**
(nimic salvat local pentru data ceruta), intra pe `Do Case` descris la punctul 3 mai jos, unde se
afla mesajul de la punctul 1.
**A doua cale de intrare, directa, manuala**, separata de fluxul de facturare de mai sus, spre acelasi
mesaj: `Clase\ofundal_facturare.vc2:929-931` (fereastra principala de facturare, tab Page1, buton Cw7):
```
PROCEDURE Page1.Cw7.do_actiune
DO vizualizeaza_curs IN oproceduri_curs.prg WITH Ttod(get_ora())
ENDPROC
```
Trimite explicit **data curenta** (`Ttod(get_ora())`), deci un utilizator care apasa acest buton la
ora 19 si cursul zilei nu exista inca in Oracle intra pe acelasi drum (pasii 6-10 de mai sus).
## 3. Cum se decide data pentru care se cere cursul
Doua surse posibile pentru `tdZiua`/`This.dData`, ambele **data curenta a sistemului**, nu data
documentului si nu "data - 1":
- Ruta automata (eroare -20005): `poDate.zi_curs`, care e populat **necondiționat** cu data curenta
la initializarea antetului de factura —
`COMUN\programe\ofacturare_comun.prg:247` (`Init`) si `:496` (`Reset`): `.zi_curs = ldData`, unde
`ldData = Ttod(get_ora())` ajustat doar la luna/anul de facturare deschisa
(`docs\cercetare\zi_curs_validare.md:198-204`, citat ca sursa deja verificata). Nu e data
documentului (`dataact`) decat daca operatorul a sincronizat manual campul (acelasi punct, §4b).
- Ruta manuala (buton Cw7): `Ttod(get_ora())` — literal ora curenta a sistemului, transformata in data
(`ofundal_facturare.vc2:930`).
Decizia efectiva "azi sau nu" si "inainte sau dupa ora 13" se ia in
`actualizare_curs_bnr::initializeaza`, `onom_curs.vc2:164-176`:
```
onom_curs.vc2:164-176
ltDataOra = get_Ora()
Do Case
Case This.nTip = 1
Do Case
Case (This.dData = Ttod(ltDataOra) And Hour(ltDataOra) <13) Or (This.dData = Ttod(ltDataOra) + 1 And Hour(ltDataOra) >= 13)
This.citeste_curs_bnr()
Case This.dData < Ttod(ltDataOra) Or This.dData = Ttod(ltDataOra) And Hour(ltDataOra) >= 13
If amessagebox("Nu poate fi preluat automat cursul BNR. Doriti sa accesati pagina BNR?",4+32,"Confirmare") = 6
goUrl("http://www.bnr.ro")
Endif
Otherwise
amessagebox("Nu exista cursul BNR pentru data specificata!",48,"Atentie")
Endcase
```
`ltDataOra` = ora curenta a sistemului (`get_Ora()`). Pentru scenariul raportat (data ceruta = azi,
ora curenta = 19:00): `This.dData = Ttod(ltDataOra)` si `Hour(ltDataOra) >= 13` -> intra direct pe
**a doua ramura** (linia 170), care **nu incearca deloc** `This.citeste_curs_bnr()` — afiseaza direct
mesajul de la linia 171. Prima ramura (fetch live, linia 168) se executa **doar** cand se cere ziua
curenta inainte de ora 13, sau ziua urmatoare dupa ora 13 — niciodata pentru "azi, dupa ora 13".
## 4. Fallback la curs anterior — nu exista
In toata `actualizare_curs_bnr::initializeaza` (`onom_curs.vc2:152-202`) nu exista nicio ramura care sa
citeasca "ultimul curs disponibil" sau "ziua lucratoare anterioara" cand data ceruta nu are curs local
si e prea tarziu sa se mai incerce live-ul. Cele trei ramuri posibile pentru `nTip=1` (liniile 166-176)
sunt exhaustiv: (a) incearca live fetch, (b) cere confirmare pentru deschidere manuala a bnr.ro, (c)
"Nu exista cursul BNR pentru data specificata!". Niciuna nu cade inapoi pe un rand `data2 >= data
anterioara` din tabela locala. Confirmat si de cercetarea independenta anterioara
(`docs\cercetare\s4d_zi_curs_reactiv.md:160-171`): reteta Oracle de sub `verifica_cursuri_valute`
verifica strict daca exista curs care sa acopere data ceruta — daca nu, arunca -20005, fara fallback pe
partea de baza de date.
Exista o a treia "sursa alternativa" definita in cod, `actualizare_curs_bnr::citeste_curs_altesurse`
(`onom_curs.vc2:64-98`, `nTip=2`), care foloseste clasa `curs` (INFOVALUTAR, nu BNR direct) — dar se
activeaza **doar** cand `This.nTip = 2` sau `= 3` (`onom_curs.vc2:177-191`). Valoarea lui `nTip` vine
din parametrul `gnTipActualizareCurs` transmis la creare (`onom_curs.vc2:1325`, `:1699`). **Nu am gasit
nicio declarare/atribuire a variabilei `gnTipActualizareCurs`** in tot codul text-cache din
`ROAFACTURARE` sau `COMUN` (cautare exhaustiva, case-insensitive) — deci nu se poate confirma din cod
ce valoare are efectiv la rulare; `actualizare_curs_bnr::Init` (`onom_curs.vc2:144-146`) o trateaza ca
`Iif(Empty(tnTip),1,tnTip)`, deci daca variabila e goala/nedefinita comportamentul cade pe `nTip=1`
(ramura fara alte surse, cea din mesajul raportat).
## 5. Sursa BNR: cum se apeleaza, timeout, tratarea erorii
`actualizare_curs_bnr::citeste_curs_bnr` — `onom_curs.vc2:100-136`:
```
onom_curs.vc2:100-118
PROCEDURE citeste_curs_bnr
Local loHTTP As 'winHTTP.winHTTPrequest.5.1'
...
lcServer = This.cLink && "https://curs.bnr.ro/nbrfxrates.xml" (onom_curs.vc2:28)
loHTTP = Createobject('winHTTP.winHTTPrequest.5.1')
loHTTP.Open('GET', lcServer, .F.)
loHTTP.setRequestHeader("Content-Type", "application/xml;")
poLog.Log(m.lcServer)
loHTTP.Send()
If loHTTP.Status = 200
lcFisier = loHTTP.Responsebody
...
Xmltocursor(lcFisier,This.cCursor)
This.dData = Ttod(Ctot(lcData+[T000000]))+1
sterge_backup_cursoare(This.cCursor)
Else
repune_backup_cursoare(This.cCursor)
Endif
```
- Componenta: `WinHTTP.WinHTTPRequest.5.1` (COM), cerere sincrona (`Open(..., .F.)` = async=False).
- URL fix: `https://curs.bnr.ro/nbrfxrates.xml` (`onom_curs.vc2:28`, proprietatea `cLink`).
- **Niciun timeout explicit setat** pe `loHTTP` (nu apare `SetTimeouts`/`.Timeout` in metoda) — foloseste
timeout-ul implicit WinHTTP.
- Tratare eroare: **un singur test**, `If loHTTP.Status = 200` — orice alt rezultat (server jos, DNS,
timeout, 404, XML gol, 500) cade pe `Else -> repune_backup_cursoare(...)` (`:131`), fara mesaj propriu
si fara sa arunce exceptie — pur si simplu `This.cCursor` ramane fara date noi (`Reccount=0`).
- **Important**: aceasta metoda (`citeste_curs_bnr`) e cea care ar face fetch-ul live, dar in scenariul
raportat (azi, ora 19) **nu e apelata deloc** — vezi punctul 3, ramura (b) sare peste ea. Deci mesajul
"Nu poate fi preluat automat cursul BNR" **nu vine dintr-un fetch HTTP esuat** in acest scenariu — vine
dintr-o decizie de orar (`Hour(ltDataOra) >= 13`) care presupune ca preluarea trebuia sa se fi
intamplat deja (manual sau altfel) inainte de ora 13, si nu mai incearca live dupa aceea.
- Consecinta directa la intrebarea 5: mesajul de eroare NU distinge "serviciu indisponibil" de "data
lipsa din raspuns", pentru simplul motiv ca **in cazul raportat nici macar nu se ajunge sa se apeleze
serviciul** — testul e pur temporal (`Hour>=13`), nu bazat pe rezultatul unei incercari HTTP. Pe
ramura care CHIAR apeleaza HTTP (`citeste_curs_bnr`, cazul "azi, inainte de ora 13" sau "maine, dupa
ora 13"), un eventual esec HTTP (status <>200) e complet tacut fata de operator (fara amessagebox) —
duce doar la `Reccount=0`, ceea ce las document fara curs, fara mesaj dedicat de "server indisponibil".
## 6. Stocare si import
- View Oracle citit de UI: `gcS.vcurs` (schema curenta), coloane confirmate in
`oproceduri_curs.prg:20-21`: `id_curs, id_valuta, data, data2, nume_val, curs, multiplicator,
id_valuta_iso, iso_valuta, curs_bnr`.
- Scriere: `actualizare_curs_bnr::scrie_curs_bnr` (`onom_curs.vc2:204-225`) apeleaza
`pack_curs.scrie_cotatii_curs(?pdData,?pcSirCursuri,?gnIdUtil)` — daca ziua e vineri
(`Dow(pdData,2)=6`), scrie si pentru sambata/duminica urmatoare (`:213-216`, curs valabil pe
weekend). Aceasta scriere se intampla **doar dupa** un fetch reusit (`citeste_curs_bnr` cu
status 200), apelata din `initializeaza` la linia 198 (`If Reccount(This.cCursor)>0 Then
This.scrie_curs_bnr()`).
- Citire cotatii curente pentru o data: `pack_curs.citeste_cotatii_curs(?pdData)`
(`onom_curs.vc2:158`).
- **Nu exista, in codul text-cache din `ROAFACTURARE`/`COMUN`, niciun job/scheduler/task separat**
care sa apeleze automat `citeste_curs_bnr`/`scrie_curs_bnr` fara interventia unui utilizator care
deschide un formular de curs (`frm_curs_nou`/`frm_curs_nou_multiplu`) sau declanseaza fluxul de
eroare -20005. Singurele doua puncte de instantiere a clasei `actualizare_curs_bnr` in tot
codul (`onom_curs.vc2:1325`, `:1699`) sunt ambele in formulare UI, niciuna intr-un `.prg` de
pornire/batch/scheduler. Nu am gasit un al doilea produs/proces (in acest repo) care scrie in
aceeasi tabela `CURS` — nu pot confirma sau infirma existenta unui import extern din alt sistem
(ex. ROAPRETURI sau alt exe din suita), care nu are cache text generat si nu a fost cautat aici.
## Rezumat lant, pe scurt
`facturare_lista_de_preturi` (`oproceduri_facturare.prg:114`) -> `politica.mpr` -> `factureaza(tnTip)`
(`ofacturare.prg:103`) -> SQL `cursor_preturi(?poDate.zi_curs)` -> Oracle
`pack_facturare.verifica_cursuri_valute` -> `ORA-20005` daca lipseste cursul zilei curente -> VFP
`ofacturare.prg:313-317` -> `vizualizeaza_curs(poDate.zi_curs)` (`oproceduri_curs.prg:8`) ->
`frm_curs`/`frm_curs_nou` (`onom_curs.vc2`) -> `actualizare_curs_bnr.citeste_curs` (`:46`) ->
`initializeaza` (`:152`) -> la ora >=13 pentru data curenta, ramura care afiseaza direct
**"Nu poate fi preluat automat cursul BNR. Doriti sa accesati pagina BNR?"** (`:171`), fara sa mai
incerce fetch live si fara fallback pe ultimul curs disponibil.

View File

@@ -1,9 +0,0 @@
# raport doc
1. Facut: `COMUN\docs\depanare-server-update-roa.md` - adaugate 2 sectiuni la final (doar insertii, 60 linii; nimic sters/modificat):
- „Anatomia fallback-ului PACK_UTILS: de ce ORA-29024 nu inseamna certificat" (linia 181).
- „Incident 16.09.2026 (2): ROMFAST 10.0.20.36, acelasi simptom" (linia 221, se termina la 239).
2. Verificari: octeti non-ASCII = 0 (scris strict ASCII); CR = 0 (LF pastrat, ca fisierul original); `git diff --stat` = 60 insertions, 0 deletions; liniile 118-160 neat inse.
3. Corectii de linii in PACK_UTILS fata de sarcina: niciuna - 347/298, 353/304, 367/318 corespund exact. O corectie de continut: „codul de iesire nu e capturat" e prea tare - in co_2026_09_16_02 `$LASTEXITCODE` (linia 758) decide redenumirea `.part`, dar valoarea nu se logheaza; am scris exact asa.
4. Nefacut/blocat: nimic. Fara commit, fara write-back, fara Oracle.
5. Stare periculoasa: niciuna.

View File

@@ -1,350 +0,0 @@
# Raport lane src — de ce cade ramura 1 (curl/PowerShell) pe ROMFAST 10.0.20.36
READ ONLY. Nu s-a rulat nimic pe Oracle, nu s-a modificat niciun fisier de cod.
Comanda ceruta pentru teste: niciuna (nu e caz de test). Dovezi = citate din scripturi.
## Fisiere de referinta (aliasuri folosite mai jos)
- **PU** = `D:\ROA\DATABASE\SCRIPTURI_CLAR\2026\09\co_2026_09_16_02_COMUN_PACK_UTILS.sql` (azi)
- **PU_old** = `D:\ROA\DATABASE\SCRIPTURI_CLAR\2026\01\co_2026_01_14_01_PACK_UTILS.sql` (versiunea anterioara)
- **PUF** = `D:\ROA\DATABASE\SCRIPTURI_CLAR\2026\09\co_2026_09_16_01_COMUN_PACK_UTILS_FILE.sql` (azi)
- **PUPD** = `D:\ROA\DATABASE\SCRIPTURI_CLAR\2026\09\co_2026_09_16_03_COMUN_PACK_UPDATE.sql` (azi)
- **ESO** = `D:\ROA\DATABASE\SCRIPTURI_CLAR\2025\09\sys_2025_09_24_03_EXECUTESCRIPTOS.sql`
- **SRV** = `D:\ROA\DATABASE\SCRIPTURI_CLAR\2025\09\co_2025_09_24_02_COMUN_SERVER_INFO.sql`
- **UPD25** = `D:\ROA\DATABASE\SCRIPTURI_CLAR\2025\10\co_2025_10_05_01_COMUN_UPDATE.sql`
- **DMP** = `D:\ROA\DATABASE\SCRIPTURI_CLAR\2026\08\co_2026_08_20_01_DIAG_SPATIU_PACK.sql`
---
## 1. `DownloadFileOS` integral + preconditiile de pe serverul Oracle
### 1.1 Codul (PU:681-786)
```plsql
681: function DownloadFileOS(tcURL in varchar2,
682: tcProgramUPDDirectory in varchar2,
683: tcFileName in varchar2) return boolean as
684: lcScript clob;
685: lcDir varchar2(20) := 'DMPDIR';
686: lcFileName varchar2(60) := 'download_' ||
687: to_char(systimestamp, 'YYYYMMDDHH24MISSFF') ||
688: '.ps1';
689: lcPowerShellPath server_info.value%type;
690: lcCURLPath server_info.value%type;
691: lcDMPDIR varchar2(1000);
692: lcScriptFilePath varchar2(1000);
693: llReturn boolean := False;
694: tcDestination varchar2(1000) := PACK_UTILS_FILE.DirectoryPath(tcProgramUPDDirectory) ||
695: tcFileName;
696: lcTimeOut varchar2(5) := '60';
697: lnTimeOut number(5) := 60;
698: begin
699: BEGIN
700: select value
701: into lcPowerShellPath
702: from SERVER_INFO
703: WHERE UPPER(name) = 'POWERSHELLPATH';
704:
705: EXCEPTION
706: WHEN NO_DATA_FOUND THEN
707: NULL;
708: END;
709:
710: BEGIN
711: select value
712: into lcTimeOut
713: from SERVER_INFO
714: WHERE UPPER(name) = 'POWERSHELLTIMEOUT';
715:
716: EXCEPTION
717: WHEN NO_DATA_FOUND THEN
718: NULL;
719: END;
720: lnTimeOut := GREATEST(TO_NUMBER(lcTimeOut), 5);
721:
722:
723: BEGIN
724: select value
725: into lcCURLPath
726: from SERVER_INFO
727: WHERE UPPER(name) = 'CURLPATH';
728:
729: EXCEPTION
730: WHEN NO_DATA_FOUND THEN
731: NULL;
732: END;
733:
734: lcDMPDIR := PACK_UTILS_FILE.DirectoryPath(lcDir);
735:
736: if lcDMPDIR is not null then
737: lcScriptFilePath := lcDMPDIR || lcFileName;
738: END if;
739:
740: -- Nu se continua daca nu exista calea catre POWERSHELL
741: if lcPowerShellPath is null or lcScriptFilePath is null then
742: goto sfarsit;
743: end if;
744:
745: -- Sterg scriptul ps anterior si fisierul destinatie ramas de la o rulare anterioara
746: pack_utils_file.filedelete(lcDir, lcFileName);
747: pack_utils_file.filedelete(tcProgramUPDDirectory, tcFileName);
748:
749: -- Creez pe disk scriptul ps pentru download
750: -- Descarc in <fisier>.part si redenumesc la final, ca fisierul destinatie
751: -- sa apara doar complet si eliberat de curl (altfel citirea lui da ORA-29283/ORA-29291)
752: if lcCURLPath is null then
753: lcCURLPath := 'C:\Windows\System32\curl.exe';
754: end if;
755:
756: lcScript := '& "' || lcCURLPath || '" -k -o "' || tcDestination ||
757: '.part" "' || tcURL || '"' || chr(13) || chr(10) ||
758: 'if ($LASTEXITCODE -eq 0) { Move-Item -Force -LiteralPath "' ||
759: tcDestination || '.part" -Destination "' || tcDestination ||
760: '" } else { Remove-Item -Force -ErrorAction SilentlyContinue -LiteralPath "' ||
761: tcDestination || '.part" }';
762:
763: pack_utils_file.clob2fileX(lcScript, lcDir, lcFileName);
764:
765: -- Execut scriptul ps
766: sys.ExecuteScriptOS(lcPowerShellPath, lcScriptFilePath);
767:
768: llReturn := True;
769:
770: -- Astept maxim 10 minute sa se descarce fisierul
771: for lnSleep in 1 .. lnTimeOut loop
772: exit when PACK_UTILS_FILE.FileExists(tcProgramUPDDirectory,
773: tcFileName);
774: sys.dbms_lock.sleep(1);
775: end loop;
776:
777: llReturn := PACK_UTILS_FILE.FileExists(tcProgramUPDDirectory,
778: tcFileName);
779:
780: -- Sterg scriptul ps (are nume unic, altfel se aduna in DMPDIR)
781: pack_utils_file.filedelete(lcDir, lcFileName);
782:
783: <<sfarsit>>
784:
785: return llReturn;
786: end DownloadFileOS;
```
Intrarea in functie, dar si toata `URL2Clob` (contractul apelantului), se citeste la PU:333-376; ramura 1 se deschide doar la PU:347 (`IF lcPowerShellDownload = '1' THEN`), citirea fiind PU:341-344.
### 1.2 Fiecare preconditie care trebuie indeplinita PE SERVERUL ORACLE
| # | Preconditie | Unde se citeste/consuma | Valoare asteptata | Daca nu e indeplinita |
|---|---|---|---|---|
| P1 | `SERVER_INFO(NAME='POWERSHELLDOWNLOAD')` | PU:341-347 | `VALUE = '1'` exact | ramura 1 nu se intra deloc → direct `HTTPURITYPE` (PU:367) → ORA-29273/29024. Daca randul lipseste: `NO_DATA_FOUND` neprins, alta eroare. |
| P2 | `SERVER_INFO(NAME='POWERSHELLPATH')` | PU:700-703, garda PU:741 | non-NULL, ex. `C:\Windows\System32\WindowsPowerShell\v1.0\powershell.exe` (SRV:7) | `goto sfarsit` (PU:741-743) → `return FALSE`, FARA nicio urma |
| P3 | directorul Oracle `DMPDIR` exista (si, mai jos, e scriibil) | `DirectoryPath('DMPDIR')` PUF:327-343 → PU:734-738 | rand in `all_directories` | `lcScriptFilePath` ramane NULL → `goto sfarsit` (PU:741) → `FALSE`, fara urma. Scriptul `.ps1` nu se poate scrie (PUF `CLOB2FILEX` → `dbms_xslprocessor.clob2file`) → exceptie |
| P4 | directorul de destinatie `tcProgramUPDDirectory` (ex. `UPD_ROACONT`, `DMPDIR`) exista | `DirectoryPath(...)` PUF:327-343 → PU:694 | rand in `all_directories` | `DirectoryPath` intoarce NULL → `tcDestination = NULL||tcFileName = tcFileName` (doar numele, fara cale, PU:694) → curl scrie in CWD-ul jobului; `FileExists` pe un director inexistent ridica ORA-29280 (PUF:169 `utl_file.fgetattr`) |
| P5 | `sys.ExecuteScriptOS` exista si e executabil din `CONTAFIN_ORACLE` | PU:766, definit in ESO:1-21 | procedura in schema SYS + grant | `ORACLE` nu-l vede / PLS-00201 sau ORA-06550; `ESO:16` in varianta curenta NU mai contine grant-ul (vezi 1.4) |
| P6 | contul OS al jobului extern | DBMS_SCHEDULER job `job_type=>'executable'` (ESO:7-13) | contul de rulare al serviciului OracleJobScheduler are drept de executare `powershell.exe`, citire `.ps1`, scriere in DMPDIR si in directorul destinatie, executare `curl.exe`, acces retea+TLS catre `https://roa.romfast.ro` | jobul extern nu porneste / porneste si nu are drepturi → `.ps1` scris pe disc dar fara efect. **Punctul cel mai probabil de esec pe o instanta neconfigurata.** |
| P7 | `SERVER_INFO(NAME='POWERSHELLTIMEOUT')` | PU:710-720, folosit PU:771 | numeric, ex. `'30'` (SRV:11) sau lipsa → default `'60'` (PU:696) | daca randul EXISTA dar e gol/NULL: `GREATEST(TO_NUMBER(NULL),5)=NULL` → `for lnSleep in 1..NULL` = 0 iteratii → verificarea de existenta se face imediat → `FALSE` (fals negativ), desi descarcarea ar fi reusit in cateva secunde |
| P8 | `SERVER_INFO(NAME='CURLPATH')` (optional) | PU:723-727, folosit PU:752-754 | cale valida la `curl.exe` | NU e preconditie: daca lipseste, codul cade pe `C:\Windows\System32\curl.exe` (PU:753). Daca acel fisier nu exista pe OS, scriptul PowerShell da eroare → fara fisier |
**Criteriul de succes este exclusiv existenta fisierului** (PU:777-778). Codul de iesire al `curl.exe` este folosit doar in interiorul scriptului `.ps1` ca sa decida redenumirea (PU:758) — Oracle nu il vede niciodata. Preconditiile P6/P7/P8 sunt OS, deci SQL nu le poate confirma direct.
### 1.3 Unde scrie `.part` si unde il muta
- `tcDestination` = `DirectoryPath(tcProgramUPDDirectory) || tcFileName` (PU:694-695) — calea FIZICA a directorului Oracle.
- curl scrie in `<tcDestination>.part` (`-o`, PU:756-757), apoi `Move-Item -Force` in `<tcDestination>` doar daca `$LASTEXITCODE -eq 0`; altfel `Remove-Item` pe `.part` (PU:758-761).
- Daca directorul nu exista (P4) → `tcDestination` fara cale → fisierul ajunge in directorul curent al procesului jobului.
- Daca directorul nu e scriibil pentru contul OS (P6): curl da eroare → nu apare `.part` si nu apare nici destinatia → `FALSE`. Pe vechiul cod (fara `.part`) eroarea se manifesta la CITIRE (ORA-29283/29291), pe cel nou se manifesta ca „fisier lipsa“ (vezi sectiunea 3).
### 1.4 `sys.ExecuteScriptOS` — ce e exact si ce cere
Definitie (ESO:1-21):
```plsql
1: create or replace procedure ExecuteScriptOS(tcPowerShellPath in varchar2,
2: tcScriptPath in varchar2) as
3: lcJobName varchar2(500);
4: begin
5: lcJobName := 'exec_ps_' || SUBSTR(SYS_GUID(), 1, 8);
6: DBMS_SCHEDULER.CREATE_JOB(
7: job_name => lcJobName,
8: job_action => tcPowerShellPath,
9: number_of_arguments => 4,
10: job_type => 'executable',
11: enabled => FALSE
12: );
13: DBMS_SCHEDULER.SET_JOB_ARGUMENT_VALUE(lcJobName, 1, '-ExecutionPolicy');
14: DBMS_SCHEDULER.SET_JOB_ARGUMENT_VALUE(lcJobName, 2, 'Bypass');
15: DBMS_SCHEDULER.SET_JOB_ARGUMENT_VALUE(lcJobName, 3, '-File');
16: DBMS_SCHEDULER.SET_JOB_ARGUMENT_VALUE(lcJobName, 4, tcScriptPath);
17: DBMS_SCHEDULER.ENABLE(lcJobName);
18: end ExecuteScriptOS;
```
- **Nu** e Java stored proc, **nu** e DBMS_SCHEDULER „program“ cu credential, **nu** e extproc: e un **DBMS_SCHEDULER external executable job** (`job_type => 'executable'`, ESO:10). Comanda lansata pe OS: `<tcPowerShellPath> -ExecutionPolicy Bypass -File <tcScriptPath>` (ESO:13-16).
- Proprietar: **SYS** — fisierul e `sys_*`, iar `UpdateVersiune('sys_2025_09_24_03_EXECUTESCRIPTOS','','SYS')` (ESO:24); apelul e calificat `sys.ExecuteScriptOS` (PU:766). S-a schimbat de la 1 argument (2023/05 si 2024/02) la 4 argumente (`-ExecutionPolicy Bypass -File`) in `sys_2025_09_24_03`.
- Componente/privilegii cerute:
- `EXECUTE` pe `SYS.ExecuteScriptOS` pentru `CONTAFIN_ORACLE`. In ESO:24 (versiunea din 16.09.2026) **nu mai exista linia de grant** — trimite la `grant execute on EXECUTESCRIPTOS to CONTAFIN_ORACLE` din scriptul de instalare `ALTELE\Creare_server_scripturi\sys_2024_02_22_01_COMUN.sql:16`. Daca procedura a fost recreata de ESO (fara grant), grant-ul mai vechi **se pastreaza** (CREATE OR REPLACE nu-l pierde), dar pe o instanta curata trebuie rulat scriptul de instalare. **Neverificat pe ROMFAST.**
- Componenta `DBMS_SCHEDULER` si `CREATE JOB` — se executa in SYS (droits de definer), deci nu cere `CREATE EXTERNAL JOB` in `CONTAFIN_ORACLE`.
- **Contul OS al jobului extern** — conditionarea reala: procesul `OracleJobScheduler<SID>` (Windows) ruleaza ca un cont de serviciu; acel cont trebuie sa poata executa `powershell.exe`, sa citeasca `.ps1`-ul din DMPDIR, sa scrie in DMPDIR si in directorul destinatie, sa execute `curl.exe` si sa iasa in retea pe `https://roa.romfast.ro`. Fara aceste drepturi, jobul ruleaza si esueaza fara ca Oracle sa vada ceva (codul de iesire nu e citit).
- Nu s-a gasit niciun script in `SCRIPTURI_CLAR` / `ALTELE` care sa acorde `CREATE ANY DIRECTORY` sau `READ,WRITE ON DIRECTORY DMPDIR` (grep pe tot `D:\ROA\DATABASE`): DMPDIR vine din instalare — `create or replace directory DMPDIR as 'C:\DMPDIR'` (`ALTELE\Creare_server_scripturi\sys_2012_06_21_01_FIRMA.sql:119`) si `grant all on directory DMPDIR to CONTAFIN_ORACLE` (`SCRIPTURI_CLAR\2013\01\sys_2013_01_23_02.sql:8`). Atentie: in `SCRIPTURI_CLAR\2012\06\sys_2012_06_21_01_FIRMA.sql:388` linia de grant este **comentata** (`rem grant all on directory DMPDIR to CONTAFIN_ORACLE;`) — pe instantele instalate din acel script, grant-ul poate lipsi. **Neverificat pe ROMFAST.**
- Jobul NU are `auto_drop` (ESO:6-12) → dupa rulare ramane in `DBA_SCHEDULER_JOBS` ca job one-time, cu nume unic `exec_ps_<8 hex>`. Util pentru a dovedi daca jobul a fost lansat vreodata.
### 1.5 Normalizarea directorului — de unde vine `lcDir`
`lcDir` este **hardcodat** `'DMPDIR'` in ambele locuri (PU:336 in URL2Clob, PU:685 in DownloadFileOS); nu vine nici din parametru, nici din `SERVER_INFO`. `PACK_UTILS_FILE.DirectoryPath` (PUF:327-343) il rezolva la calea fizica prin `select DIRECTORY_PATH into lcPath from all_directories where directory_name = UPPER(tcDirectoryName)` si adauga bara (PUF:336). Deci:
- numele `DMPDIR` este un **obiect Oracle DIRECTORY**, nu o cale;
- drepturile semnificative sunt pe obiectul DIRECTORY: `READ` (pentru `FGETATTR`/`FREMOVE`/`READ2CLOB`) si `WRITE` (pentru `CLOBFILE2FILE`/`FREMOVE`) — PUF:169, PUF:185, PUF:302, PUF:321.
---
## 2. Ce se logheaza la esecul ramurii 1
**Raspuns explicit: in ramura 1 nu se logheaza absolut nimic.** `DownloadFileOS` nu are `UPD_LOG`, nu are `DBMS_OUTPUT`, nu are `RAISE`; singurul rezultat al esecului este `return FALSE` (PU:693, PU:777-785). Nu exista nici macar urma ca a fost incercata.
Traseul erorii, in continuare:
1. `URL2Clob` cade pe `HTTPURITYPE.createuri(tcURL).getclob()` (PU:367) → ORA-29273 (peste `https` fara wallet → ORA-29024).
2. Handler-ul `URL2Clob` (PU:369-375) sterge doar fisierul temporar si face `RAISE`; **nu logheaza**.
3. `UPDLOG` nu este apelat pe acest drum inainte de `URL2Clob` — singurul `UPDLOG` din pasul listelor este DUPĂ ambele descarcari, la PUPD:427, deci nu se executa niciodata daca `URL2Clob` a aruncat.
Locul exact al apelului (PUPD:416-427):
```plsql
418: lcListaPrograme := PACK_UTILS.URL2Clob(lcURLAppXml);
...
423: lcListaRoastart := PACK_UTILS.URL2Clob(lcURLRoastartXml);
...
427: UPDLOG(lcText, ldDataOraS);
```
Apelurile 418/423 sunt in corpul procedurii `UpdateApp` (inceput la PUPD:309), **in afara** blocului `begin/exception` de la PUPD:434-520 (care prinde doar bucla de scanare a programelor, PUPD:514-519). `UpdateApp` este `pragma autonomous_transaction` (PUPD:310) si nu are handler propriu → exceptia iese din `UpdateApp`, iar `UpdateROA` **nu are nici el handler** (apel la PUPD:275; `UpdateROA` se termina la PUPD:303) → nu se ajunge nici la `EmailLog` de la PUPD:300-302.
**Unde s-ar fi putut loga:**
- PUPD:427 (primul `UPDLOG`) — dar e dupa `URL2Clob`;
- un `begin/exception` in jurul lui PUPD:418/423 (modelul exista la buletinul informativ, PUPD:729-737, care logheaza `'ERR: ' || lcURL... || sqlcode || sqlerrm`);
- in `DownloadFileOS` sau in handler-ul `URL2Clob` (PU:369-375).
**Ce urme ramane totusi:**
- `UPD_ISTORIC.STARE` = `UPDATE_STARE_APLICATII`, scris la PUPD:274 inainte de `UpdateApp` (`UpdateIstoric`, PUPD:2408-2436) → stare lasata „blocat la aplicatii“.
- Un rand de eroare in jobul DBMS_SCHEDULER `UPDATEROA_JOB` (creat la PUPD:178-186, `job_action => 'PACK_UPDATE.UpdateROA'`) — deci `DBA_SCHEDULER_JOB_LOG` / `_RUN_DETAILS.ADDITIONAL_INFO`, nu `UPD_LOG`.
- **Diferenta importanta de diagnostic:** daca esecul e pe buletinul informativ (PUPD:730), `UPD_LOG` **contine** un rand `'ERR: <url> ORA-29273 ... ORA-29024'` (PUPD:732-736). Daca `UPD_LOG` nu are niciun rand `ERR:` pentru rularea respectiva, esecul e la PUPD:418 sau 423.
---
## 3. Ce a schimbat scriptul de azi fata de versiunea anterioara
Versiunea precedenta a lui PACK_UTILS: **PU_old** (`co_2026_01_14_01_PACK_UTILS.sql`, 14.01.2026). Urmatoarea in istoricul de fisiere: `2025/09/co_2025_09_24_01_COMUN_PACK_UTILS.sql` (introduce DownloadFileOS + POWERSHELLDOWNLOAD).
### 3.1 Diff conceptual (numai `DownloadFileOS`)
| Element | PU_old | PU (azi) |
|---|---|---|
| numele scriptului `.ps1` | `varchar2(30) := 'download_file.ps1'` (PU_old:680) — FIX | `varchar2(60) := 'download_' || to_char(systimestamp,'YYYYMMDDHH24MISSFF') || '.ps1'` (PU:686-688) — unic |
| sterge fisierul destinatie inainte | nu | `pack_utils_file.filedelete(tcProgramUPDDirectory, tcFileName)` (PU:747) |
| scrie in | direct `<dest>` (PU_old:743-747) | `<dest>.part`, apoi `Move-Item -Force` (PU:756-761) |
| rezultat partial | fisier partial vizibil (de aceea bug-ul ORA-29283/29291, PU:750-751) | fisierul final apare doar dupa `Move-Item` reusit |
| sterge `.ps1` la final | nu | `pack_utils_file.filedelete(lcDir, lcFileName)` (PU:781) |
| preconditii de intrare | identice (POWERSHELLDOWNLOAD / POWERSSHELLPATH / DMPDIR / CURLPATH / TIMEOUT) | identice, neschimbate |
| `URL2Clob` / `URL2Blob` | — | neschimbate logic (se schimba doar diacriticele din comentarii) |
### 3.2 Verdict: **NU** — modificarea de azi nu a „rupt“ ramura 1 prin preconditii
Cu citat: garda de intrare din `URL2Clob` este neschimbata (`IF lcPowerShellDownload = '1' THEN` PU:347, identic PU_old:292), garda de iesire din `DownloadFileOS` este neschimbata (`if lcPowerShellPath is null or lcScriptFilePath is null then goto sfarsit;` PU:741-743, identic PU_old:733-735), aceleasi nume de `SERVER_INFO` (PU:703/714/727 vs PU_old:695/706/719) si acelasi `lcDir := 'DMPDIR'` (PU:685 vs PU_old:679). **Niciun director nou, nicio preconditie in minus.**
### 3.3 Ce s-a schimbat, totusi, in comportamentul de esec (relevant pentru simptom)
Modificarea poate fi **neverificat** cauza incidentului, dar nu prin preconditii, ci prin **felul in care se manifesta un esec deja existent**:
- Inainte: curl scria direct in `<dest>`; daca fisierul era tinut deschis de alt proces, citirea dadea **ORA-29283/ORA-29291** (exact motivul notat in comentariul de azi, PU:750-751).
- Acum: curl scrie in `<dest>.part`, iar `<dest>` apare **doar daca `Move-Item` reuseste** (PU:758). Un `Move-Item` care esueaza (fisier destinatie blocat, drepturi, antivirus) este **inghitit tacit** — scriptul nu scrie nimic in log, iar Oracle testeaza doar existenta fisierului (PU:777-778) → `FALSE` → fallback `HTTPURITYPE` → **ORA-29273/ORA-29024**.
- Deci: daca pe ROMFAST conditia de baza era deja marginala, eroarea raportata acum **se schimba** din ORA-29291 in ORA-29024, fara ca vreo preconditie sa fi fost scoasa.
- A doua schimbare de azi, in `PACK_UTILS_FILE` (**PUF**), merge in acelasi sens: `FileDelete` a devenit best-effort (`when others then null`, PUF:187-192), deci nu mai poate masca eroarea originala cu ORA-29291. Nici aceasta nu introduce preconditii.
Concluzie punct 3: **NU** (nu a rupt-o prin preconditii) + **neverificat** daca a contribuit la simptomul de azi (ar cere dovezi OS: exista `.part` sau `.ps1` ramase in DMPDIR?).
---
## 4. Interogari read-only de rulat pe ROMFAST (scrise, NU rulate)
Toate sunt `SELECT`. Pentru Oracle 10.2 compatibil (fara `FETCH FIRST`).
**Q1 — configurarea, cheia de departajare a ipotezelor.**
```sql
SELECT name, value FROM SERVER_INFO
WHERE UPPER(name) IN ('POWERSHELLDOWNLOAD','POWERSHELLPATH','POWERSHELLTIMEOUT',
'CURLPATH','DMPDIR','ROAUPDATEPATH','UPDATEPREREQ')
ORDER BY name;
```
- `POWERSHELLDOWNLOAD` ∈ {`'0'`, `''`, NULL} → **confirma** ca ramura 1 nu se intra (cauza = P1) si ca ORA-29024 e garantat; `='1'` → **infirma**, ramura 1 se incearca.
- `POWERSHELLPATH` NULL/`''` → **confirma** ca `DownloadFileOS` iese imediat cu `FALSE` (P2) — suspectul principal; non-NULL → infirma.
- `POWERSHELLTIMEOUT` prezent dar gol → **confirma** falsul negativ din P7; absent → default 60s (ok).
- `UPDATEPREREQ='1'` → explica de ce DMPDIR/update dirs **nu** mai sunt re-verificate la fiecare update.
**Q2 — directorul Oracle (P3/P4).**
```sql
SELECT directory_name, directory_path FROM all_directories
WHERE directory_name = 'DMPDIR' OR directory_name LIKE 'UPD_%' ORDER BY 1;
```
Lipsa lui `DMPDIR` → **confirma** ca ramura 1 e imposibila (`lcScriptFilePath` NULL); lipsa unui `UPD_<PROGRAM>` → alta eroare (ORA-29280), nu ORA-29024.
**Q3 — drepturile pe DMPDIR si pe SYS.ExecuteScriptOS (P5).**
```sql
SELECT grantee, privilege FROM all_tab_privs
WHERE table_name = 'DMPDIR' AND privilege IN ('READ','WRITE') ORDER BY 1;
SELECT owner, object_name, status FROM all_objects
WHERE object_name = 'EXECUTESCRIPTOS' AND object_type = 'PROCEDURE';
SELECT grantee FROM all_tab_privs
WHERE table_name = 'EXECUTESCRIPTOS' AND privilege = 'EXECUTE';
```
Lipsa `READ`/`WRITE` pentru `CONTAFIN_ORACLE`/`PUBLIC` → confirma ca fisierul nu se poate scrie/citi; `STATUS='INVALID'` sau lipsa grant → confirma P5.
**Q4 — urma din UPD_LOG / UPD_ISTORIC (punctul 2).**
```sql
SELECT secventa, TO_CHAR(dataora,'DD.MM.YYYY HH24:MI:SS') d, SUBSTR(explizatie,1,300) t
FROM UPD_LOG WHERE dataora > SYSDATE - 7 ORDER BY secventa;
SELECT TO_CHAR(dataora_start,'DD.MM.YYYY HH24:MI:SS') s, stare, id_util
FROM UPD_ISTORIC WHERE dataora_start > SYSDATE - 7 ORDER BY dataora_start;
```
Prezenta unui rand `ERR:` cu `ORA-29273`/`ORA-29024` → esecul e pe buletinul informativ (PUPD:730). **Absenta** oricarui `ERR:` + `STARE` = starea „aplicatii“ → confirma esecul la PUPD:418/423 (nelogat).
**Q5 — joburile externe si jobul de update (P6) — dovedeste daca PowerShell a fost lansat vreodata.**
```sql
SELECT job_name, TO_CHAR(log_date,'DD.MM.YYYY HH24:MI:SS') d, status, error#, SUBSTR(additional_info,1,500) info
FROM (SELECT * FROM dba_scheduler_job_log ORDER BY log_date DESC)
WHERE (job_name LIKE 'exec_ps_%' OR job_name = 'UPDATEROA_JOB') AND ROWNUM <= 50;
SELECT job_name, enabled, state, TO_CHAR(last_start_date,'DD.MM.YYYY HH24:MI:SS') last_start
FROM dba_scheduler_jobs WHERE job_name LIKE 'exec_ps_%' OR job_name = 'UPDATEROA_JOB';
```
Existenta joburilor `exec_ps_*` cu `status='SUCCEEDED'` → PowerShell a rulat cel putin o data (**infirma** „instanta neconfigurata“); zero joburi `exec_ps_*` → **confirma** ca `sys.ExecuteScriptOS` nu s-a executat niciodata; `UPDATEROA_JOB` esuat cu ORA-29273/29024 in `additional_info` → confirma locul erorii.
**Q6 — URL-urile reale (ca sa nu banuim degeaba https-ul).**
```sql
SELECT varname, varvalue FROM OPTIUNI WHERE varname LIKE 'UPD_URL%';
SELECT TO_NUMBER(detalii) customerid FROM SYS.AUTH_DETALII;
```
`UPD_URL_APP` fara `https://` sau NULL → s-ar lua default-ul http (PUPD:379/381), unde `HTTPURITYPE` ar putea functiona — deci **infirma** ipoteza „https fara wallet“ si muta cauza in alta parte. `https://roa.romfast.ro` → **confirma** ca esecul ramurii 1 duce obligatoriu in ORA-29024.
**Q7 — identitatea instantei (sa nu raportam de pe alt server).**
```sql
SELECT SYS_CONTEXT('USERENV','SERVER_HOST') host, SYS_CONTEXT('USERENV','DB_NAME') db,
SYS_CONTEXT('USERENV','INSTANCE_NAME') inst FROM dual;
```
### Verificari OS (NU se pot face prin SQL — marcat neverificat pana se fac)
In directorul fizic al `DMPDIR` (Q2/Q1), pe ROMFAST:
- exista fisiere `url2clob_*.tmp.part` sau `url2app_*.part` rămase → curl **a scris**, dar `Move-Item` a esuat (blocare/drepturi) → confirma scenariul din 3.3;
- exista fisiere `download_*.ps1` acumulate → scriptul se scrie, dar jobul extern nu ruleaza/vrea sa-l ruleze → confirma P5/P6;
- nu exista nici `.part`, nici `.ps1` → `DownloadFileOS` nu a ajuns la scriere deloc → confirma P1/P2/P3;
- `C:\Windows\System32\curl.exe` exista si depinde daca `CURLPATH` e setat (PU:752-754).
- in `DBA_SCHEDULER_JOB_RUN_DETAILS.ADDITIONAL_INFO` se vede codul de iesire OS al jobului extern PowerShell (singurul loc unde ajunge `$LASTEXITCODE`-ul real al scenariului).
---
## 5. Nefacut / deschis
- Nu s-a rulat nicio interogare si nicio comanda pe ROMFAST/10.0.20.36 (interzis de sarcina): **cine a produs incidentul de azi ramane neverificat pana la Q1-Q7 + verificarile OS.**
- Nu am putut confirma daca `POWERSHELLPATH` / `CURLPATH` / grant-urile pe `DMPDIR` sunt configurate pe ROMFAST — nu apar in niciun script de migrare (verificat prin grep pe `D:\ROA\DATABASE`), deci sunt setari de instalare/OS.
- Nu s-a facut write-back, nu s-a modificat niciun fisier (in afara de acest raport), nu s-a pornit niciun proces.
- Stare periculoasa: **niciuna**.
GATA src

View File

@@ -1,100 +0,0 @@
# S0 - Prototip context/handoff: fix SubagentStop, -StareFile, compactare mai devreme
Executat 17.09.2026, pe masina curenta (fara UI, conform plan_qa_factura_aviz.md sectiunea S0).
## Ce am gasit
- Hook-ul `SubagentStop` din `C:\Users\mmari\.claude\settings.json` (era la cheia
`hooks.SubagentStop[0].hooks[0].command`) era un simplu `echo '{"hookSpecificOutput":...}'`
static, executat de fiecare data cand un subagent se opreste, fara sa citeasca deloc stdin-ul
hook-ului. Nu bloca explicit (nu avea `decision:block`/exit 2), dar reinjecta acelasi
`additionalContext` la fiecare `SubagentStop`, inclusiv la reincercarile in acelasi ciclu de
stop - de aici bucla observata (patru agenti raportand cicluri repetate in aceeasi sesiune).
Cauza + fix documentate in `docs/qa_factura_hooks_ref.md` sectiunea 4 ("stop_hook_active si
bucla"): campul `stop_hook_active` trebuie citit de pe stdin, iar la `true` hook-ul trebuie sa
iasa imediat cu 0, fara iesire.
- `context_watch.ps1` e in `D:\ROA\ROAGEST\COMUN\utile\context_watch.ps1`. Verificat: acel `COMUN`
e checkout separat al aceluiasi repo `git@gitea.romfast.ro:romfast/comun.git` ca si
`D:\ROA\ROAFACTURARE\COMUN` (acelasi `origin`, ramuri `main`) - deci e intr-adevar biblioteca
partajata, doar clonata separat per produs. Nu l-am mutat: mutarea in `COMUN` (punctul 4 din
planul S0) nu a fost in cele trei sarcini primite de la team lead pentru aceasta sesiune, ramane
neacoperit (vezi mai jos).
- Scriptul deja avea parametrii `-Stdout` (UserPromptSubmit) si `-OSinguraData` (PostToolUse,
dedup prin fisier marcaj in `%TEMP%`); nu avea niciun mecanism de scriere pe disc a starii.
- `env` in `settings.json` nu exista `CLAUDE_AUTOCOMPACT_PCT_OVERRIDE`.
## Ce am schimbat
### 1. Garda `stop_hook_active` pe hook-ul SubagentStop
`C:\Users\mmari\.claude\settings.json`, cheia `hooks.SubagentStop[0].hooks[0].command`
(inainte de orice modificare am facut copie de siguranta la
`C:\Users\mmari\.claude\settings.json.bak_20260917`, verificata pe disc):
Inainte:
```
echo '{"hookSpecificOutput":{"hookEventName":"SubagentStop","additionalContext":"..."}}'
```
Dupa:
```
grep -q '"stop_hook_active"[[:space:]]*:[[:space:]]*true' && exit 0; echo '{"hookSpecificOutput":{"hookEventName":"SubagentStop","additionalContext":"..."}}'
```
Mesajul `additionalContext` a ramas identic (neschimbat), doar prefixat cu garda. `grep -q`
citeste stdin-ul hook-ului (JSON-ul cu `stop_hook_active`) fara dependenta de `jq` (nu e instalat
pe masina - verificat). Daca patternul gaseste `"stop_hook_active":true` (cu sau fara spatiu dupa
`:`), hook-ul iese cu 0 fara sa emita nimic; altfel continua la `echo` ca inainte.
### 2. `-StareFile` in `context_watch.ps1`
`D:\ROA\ROAGEST\COMUN\utile\context_watch.ps1`:
- linia 20: parametru nou `[string]$StareFile`.
- liniile 23-36: functia noua `Scrie-AntetStare` - daca fisierul exista si primul rand se
potriveste cu `^<!-- context_watch:`, il inlocuieste; altfel adauga antetul ca prim rand
(fisier nou sau fisier existent fara antet). Restul continutului ramane neschimbat.
- linia 92: apel `if ($StareFile) { Scrie-AntetStare -cale $StareFile -tokeni $total -nivel $nivel }`
in blocul `if ($mesaj)`, deci se declanseaza exact cand pragul de avertisment sau max e atins
(acelasi punct unde se decide mesajul), indiferent de `-Stdout`/`-OSinguraData`.
- Comportamentul fara `-StareFile` e neschimbat: parametrul e opt, iar apelul e conditionat de
prezenta lui.
### 3. `CLAUDE_AUTOCOMPACT_PCT_OVERRIDE` in settings
`C:\Users\mmari\.claude\settings.json`, cheia `env` (exista deja, avea doar
`CLAUDE_CODE_EXPERIMENTAL_AGENT_TEAMS`): adaugat `"CLAUDE_AUTOCOMPACT_PCT_OVERRIDE": "70"`.
Nicio alta setare atinsa.
## Rezultatul probelor cerute
1. **`settings.json` e JSON valid** dupa modificare:
`Get-Content ... -Raw | ConvertFrom-Json` -> a rulat fara eroare, a scris `JSON_VALID`.
2. **`context_watch.ps1 -StareFile` pe transcript de test** (usage sintetic la 260000 tokeni,
peste pragul implicit de 250000):
- rulare 1: fisier inexistent -> creat, un singur rand:
`<!-- context_watch: 17.09.2026 21:57 | tokeni: 260000 | nivel: avertisment -->`.
- am adaugat manual un rand "text scris de model" dupa antet (simuland continutul modelului).
- rulare 2 (o secunda mai tarziu): antetul de pe primul rand a fost INLOCUIT (ora actualizata),
randul "text scris de model" a ramas neatins, nu s-a adaugat un al doilea antet
(verificat cu citire BOM-aware: 2 linii total, 1 linie cu prefixul antetului).
3. **`context_watch.ps1` fara `-StareFile`**: acelasi transcript de test -> acelasi mesaj de
avertisment pe stderr, exit code 2, identic cu comportamentul dinaintea modificarii (nimic
scris pe disc).
4. **Hook-ul `SubagentStop`**: comanda extrasa din `settings.json` dupa modificare, rulata direct:
- stdin `{"stop_hook_active":true}` -> nicio iesire, exit 0.
- stdin `{"stop_hook_active":false}` -> a scos exact `additionalContext`-ul de dinainte, exit 0.
## Ce a ramas neacoperit
- Punctul 4 din S0 al planului ("se muta in COMUN ca sa fie refolosibil de toate produsele ROA")
NU a fost facut - nu a fost in lista de 3 sarcini primite de la team lead pentru aceasta rulare.
Scriptul ramane in `D:\ROA\ROAGEST\COMUN\utile\context_watch.ps1`, neschimbat ca locatie.
- Proba "ciclu real cu subagent care se termina, o singura rulare a hook-ului" (mentionata in
plan la finalul sectiunii S0) nu a fost reprodusa printr-un subagent real - probele de mai sus
au testat garda si scrierea de stare direct, in izolare, nu printr-o rulare completa
Claude Code cu un subagent viu. Nu am lansat subagenti (interdictie explicita in sarcina).
- Nu am facut niciun commit (interdictie explicita); `settings.json` nu e sub control de versiuni
in acest repo, deci nu exista diff de revizuit acolo - doar backup-ul
`C:\Users\mmari\.claude\settings.json.bak_20260917`. `context_watch.ps1` e in repo-ul `COMUN`
(git) - diff-ul ramane de revizuit acolo inainte de commit, conform regulii "fara commit fara
review".

View File

@@ -1,330 +0,0 @@
\# Referinta oficiala: SessionStart / PostCompact / Stop (surse: code.claude.com/docs)
Toate citatele de mai jos sunt verbatim din versiunile `.md` brute ale paginilor
(`https://code.claude.com/docs/en/hooks.md`, `.../hooks-guide.md`), fetch-uite direct cu
`curl` pe 2026-09-17 (WebFetch trunchiaza paginile astea, sunt prea mari pentru modelul
intern al tool-ului — vezi nota metodologica de la final). Nu am dedus nimic; unde n-am gasit
un raspuns explicit, scriu "NEDOCUMENTAT".
---
## 1. SessionStart — valori de matcher acceptate
**CONFIRMAT, dar lista din brief e incompleta: sunt 5 valori, nu 4.** Lipsea `fork`.
Sursa: `https://code.claude.com/docs/en/hooks.md`, sectiunea `### SessionStart`:
> The matcher value corresponds to how the session was initiated:
>
> | Matcher | When it fires |
> | :-------- | :------------------------------------------------------------------------------------------------------------------------------------- |
> | `startup` | New session |
> | `resume` | `--resume`, `--continue`, or `/resume` |
> | `clear` | `/clear` |
> | `compact` | Auto or manual compaction |
> | `fork` | A new session forked from an existing one: `--fork-session` with `--resume` or `--continue`, the `/fork` background copy, or `/branch` |
>
> Before v2.1.214, forked sessions reported source `"resume"`.
Important pentru mecanismul propus: **`compact` e si el un matcher de `SessionStart`**, separat
de evenimentul `PostCompact` (vezi punctul 4). Adica dupa o compactare (auto sau manuala),
Claude Code re-porneste efectiv un `SessionStart` cu `source: "compact"`, iar acela SUPORTA
`additionalContext` — spre deosebire de `PostCompact` propriu-zis, care nu suporta (punctul 4).
---
## 2. SessionStart — campuri pe stdin, cum distinge `/clear` de pornire normala
Sursa: `https://code.claude.com/docs/en/hooks.md`, `#### SessionStart input`:
> In addition to the [common input fields](#common-input-fields), SessionStart hooks receive
> `source` and optionally `model`, `agent_type`, and `session_title`:
>
> | Field | Description |
> | :-------------- | :--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
> | `source` | How the session started: `"startup"` for new sessions, `"resume"` for resumed sessions, `"clear"` after `/clear`, `"compact"` after compaction, or `"fork"` for a new session forked from an existing one |
> | `model` | The active model identifier. It can be omitted, for example after `/clear` or when a session is restored through conversation recovery, so check for the field before reading it |
> | `agent_type` | The agent name, present when you start Claude Code with `claude --agent <name>` |
> | `session_title` | The current session title if one is already set, for example via `--name` or `/rename`. A hook that emits `sessionTitle` can check `session_title` first to avoid overwriting a title the user set explicitly |
**Distinctia `/clear` vs pornire normala se face STRICT prin campul `source`**: `"clear"` vs
`"startup"`. Nu exista alt semnal necesar.
Campuri comune (din `#### Common input fields`, aceeasi pagina), relevante pentru mecanism:
`session_id`, `prompt_id`, `transcript_path`, `cwd`, `scratchpad_dir`, `permission_mode`,
`effort`, `hook_event_name`. Citat exact pentru `transcript_path`:
> `transcript_path` — Path to conversation JSON. The transcript file is written asynchronously
> and may lag the in-memory conversation, so it may not yet include the current turn's most
> recent messages when a hook fires.
Cand `source` e `"resume"` sau `"fork"` si transcript-ul are cel putin un raspuns Claude, mai
vin 4 campuri (necesare v2.1.251+): `seconds_since_last_response`, `context_tokens`,
`prompt_cache_likely_expired`, `estimated_cache_write_usd`. **Aceste campuri NU apar pentru
`source: "clear"`** — deci hook-ul nu primeste de la Claude Code o estimare gata facuta a
cate token-i "costa" reluarea; asta ramane treaba handoff-ului scris pe disc.
`agent_type` si `agent_id` (subagent-only) — prezente doar cand hook-ul ruleaza intr-un
subagent sau sesiunea foloseste `--agent`.
**Timing**: la pornire/`--resume`/`--continue`/`/clear`, hook-urile `SessionStart` ruleaza in
fundal — poti scrie imediat, dar primul raspuns al lui Claude asteapta sa termine hook-urile.
La `/resume` in interiorul unei sesiuni, switch-ul asteapta hook-urile. Citat:
> When you start an interactive session, resume a conversation at launch with `--continue` or
> `--resume`, or run `/clear`, SessionStart hooks run in the background. You can type right
> away, and a conversation you resumed appears without waiting for the hooks. Claude's first
> response still waits for the hooks to finish, so their context reaches Claude.
---
## 3. SessionStart — cum returneaza context, forma exacta, limita de marime
Sursa: `https://code.claude.com/docs/en/hooks.md`, `#### SessionStart decision control` +
exemplu JSON:
> In addition to the [JSON output fields](#json-output) available to all hooks, you can return
> these event-specific fields:
>
> | Field | Description |
> | :-------------------- | :----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
> | `additionalContext` | String added to Claude's context at the start of the conversation, before the first prompt. |
> | `initialUserMessage` | String used as the first user message of the session. Applies in non-interactive mode with `-p`... |
> | `sessionTitle` | Sets the session title, with the same effect as `/rename`... Applies when `source` is `"startup"`, `"resume"`, or `"fork"`; ignored on `"clear"` and `"compact"` |
> | `watchPaths` | Array of absolute paths to watch for FileChanged events during this session |
> | `reloadSkills` | Boolean. When `true`, re-scans skill/command dirs after SessionStart hooks complete... |
>
> ```json
> {
> "hookSpecificOutput": {
> "hookEventName": "SessionStart",
> "additionalContext": "Current branch: feat/auth-refactor\nUncommitted changes: src/auth.ts, src/login.tsx\nActive issue: #4211 Migrate to OAuth2",
> "sessionTitle": "auth-refactor"
> }
> }
> ```
Confirmat: e `hookSpecificOutput` cu `hookEventName: "SessionStart"` si `additionalContext`
imbricat inauntru — exact forma presupusa in brief.
**Limita de marime (generala, se aplica la SessionStart la fel ca la toate evenimentele)**,
sursa: `https://code.claude.com/docs/en/hooks.md`, `#### Add context for Claude`:
> The `additionalContext` field passes a string from your hook into Claude's context window.
> Claude Code wraps the string in a system reminder and inserts it into the conversation at the
> point where the hook fired.
>
> [...]
>
> If a value exceeds 10,000 characters, Claude Code writes the text to a file in the session
> directory and passes Claude the file path with a short preview instead.
Deci: **10.000 de caractere** e pragul. Peste el, Claude Code NU trunchiaza si NU refuza —
scrie continutul intr-un fisier in directorul sesiunii si trimite lui Claude calea + un preview
scurt. Pentru un handoff mare, asta e de fapt convenabil: hook-ul poate trimite tot textul, iar
peste prag Claude Code il redirectioneaza singur spre fisier.
Unde ajunge textul pentru `SessionStart` specific, din acelasi paragraf:
> * [SessionStart](#sessionstart) and [SubagentStart](#subagentstart): at the start of the
> conversation, before the first prompt
Nota despre re-rulare la resume, aceeasi sectiune:
> `SessionStart` hooks run again on resume with `source` set to `"resume"`, or `"fork"` if you
> added `--fork-session`, so they can refresh their context.
(Nu mentioneaza explicit re-rularea la `/clear`, dar tabelul de matchere de la punctul 1 o
confirma separat: `clear` e chiar unul dintre matcherele native ale evenimentului.)
Exit code: stdout simplu (fara JSON) e tratat ca text simplu si ajunge in context, la fel ca
`additionalContext` — nu trebuie neaparat JSON daca hook-ul nu seteaza si alte campuri. Citat:
> Claude Code adds stdout it treats as plain text to Claude's context. [...] Since plain stdout
> already reaches Claude for this event, a hook that only loads context can print to stdout
> directly without building JSON. Use the JSON form when you need to combine context with other
> fields such as `sessionTitle`.
---
## 4. PostCompact — aceleasi intrebari, plus `manual` vs `auto`
Sursa: `https://code.claude.com/docs/en/hooks.md`, `### PostCompact`:
> Runs after Claude Code completes a compact operation. Use this event to react to the new
> compacted state, for example to log the generated summary or update external state. Claude
> Code discards a PostCompact hook's `systemMessage` and `continue` fields.
>
> The same matcher values apply as for `PreCompact`:
>
> | Matcher | When it fires |
> | :------- | :----------------------------------------------------------------------------------------------------------------------- |
> | `manual` | After `/compact` |
> | `auto` | After auto-compact when the conversation reaches the auto-compact window |
>
> #### PostCompact input
>
> In addition to the common input fields, PostCompact hooks receive `trigger` and
> `compact_summary`. The `compact_summary` field contains the conversation summary generated by
> the compact operation.
>
> ```json
> {
> "session_id": "abc123",
> "transcript_path": "...",
> "cwd": "...",
> "hook_event_name": "PostCompact",
> "trigger": "manual",
> "compact_summary": "Summary of the compacted conversation..."
> }
> ```
>
> **PostCompact hooks have no decision control. They can't affect the compaction result but can
> perform follow-up tasks.**
**Constatare critica pentru mecanismul propus: `PostCompact` NU poate injecta
`additionalContext` si nu poate influenta rezultatul compactarii — e strict pentru efecte
secundare (log, notificare externa etc.).** Daca planul se baza pe "PostCompact reinjecteaza
handoff-ul", premisa e falsa. Calea care CHIAR functioneaza pentru compactare e
`SessionStart` cu matcher `compact` (punctul 1 si 3) — un eveniment diferit, care ruleaza dupa
`PostCompact` si care are `additionalContext`. Exemplul oficial de "re-inject context after
compaction" din `hooks-guide.md` confirma asta explicit:
> When Claude's context window fills up, compaction summarizes the conversation to free space.
> This can lose important details. Use a `SessionStart` hook with a `compact` matcher to
> re-inject critical context after every compaction.
Pentru `PreCompact` (nu a fost cerut explicit, dar e relevant ca sa nu confundati): poate bloca
(`decision: "block"` sau exit 2) si primeste `trigger` + `custom_instructions`, dar la fel
"discards `systemMessage` and `continue`". `PreCompact` nu e mecanismul de reinjectare — ruleaza
INAINTE de compactare.
---
## 5. Poate un hook sa declanseze `/clear` sau `/compact`?
**NU exista niciun mecanism. Confirmat explicit, nu doar prin absenta.**
Sursa: `https://code.claude.com/docs/en/hooks-guide.md`, linia 952:
> Command hooks communicate through stdout, stderr, and exit codes only. **They can't trigger
> `/` commands or tool calls.** Text returned via `additionalContext` is injected as a system
> reminder that Claude reads as plain text. HTTP hooks communicate through the response body
> instead.
Am cautat explicit orice camp de tip `"command"` sau mecanism de rulare a unei comenzi slash
din output-ul unui hook, in tot `hooks.md` (3840 linii) si `hooks-guide.md` (1064 linii) — nu
exista. Singurele cai care declanseaza efectiv o compactare/clear raman actiunile native ale
utilizatorului (`/clear`, `/compact`) sau host-ul/SDK-ul care porneste sesiunea.
---
## 6. Stop hook — poate bloca oprirea si injecta o instructiune?
**Da, in doua moduri diferite**, sursa `https://code.claude.com/docs/en/hooks.md`,
`### Stop` + `#### Stop input` + `#### Stop decision control`:
Input:
> In addition to the common input fields, Stop hooks receive `stop_hook_active`,
> `last_assistant_message`, `background_tasks`, and `session_crons`. The `stop_hook_active`
> field is `true` when Claude Code is already continuing as a result of a stop hook. Check this
> value or process the transcript to avoid blocking on a condition that will never resolve.
> Claude Code overrides the hook and ends the turn after 8 consecutive blocks.
Decision control:
> `Stop` and `SubagentStop` hooks can control whether Claude continues. In addition to the JSON
> output fields available to all hooks, your hook script can return these event-specific
> fields:
>
> | Field | Description |
> | :-------------------------------------- | :------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
> | `decision` | `"block"` prevents Claude from stopping. Omit to allow Claude to stop |
> | `reason` | Required when `decision` is `"block"`. Tells Claude why it should continue |
> | `hookSpecificOutput.additionalContext` | Non-error feedback for Claude. The conversation continues so Claude can act on it, but unlike `decision: "block"` it is shown in the transcript as hook feedback rather than a hook error |
>
> A hook that blocks by exiting 2 routes the same way as `reason`: Claude receives the stderr
> message as the explanation for why it should continue.
>
> ```json
> {
> "decision": "block",
> "reason": "Must be provided when Claude is blocked from stopping"
> }
> ```
>
> Use `additionalContext` when the hook is working as designed and giving Claude guidance, such
> as "run the test suite before finishing". It keeps the conversation going through the same
> loop protections as `decision: "block"`, namely the `stop_hook_active` input and the
> 8-consecutive-continuation cap, but the transcript labels it `Stop hook feedback` and no hook
> error notification is shown.
Deci: `decision: "block"` + `reason` (top-level, NU in `hookSpecificOutput`) forteaza
continuarea si arata `reason` ca eroare de hook; `hookSpecificOutput.additionalContext` face
acelasi lucru dar apare ca "Stop hook feedback", nu ca eroare. **Ambele variante trec prin
aceleasi limite de bucla**: campul `stop_hook_active` (hook-ul trebuie sa verifice singur ca sa
nu se blocheze la infinit) si plafonul intern de **8 blocari consecutive fara progres**, dupa
care Claude Code forteaza oprirea oricum.
Exit code 2 pe `Stop`: din tabelul citat separat in `hooks.md`, poate bloca (spre deosebire de
`SessionStart`, unde exit 2 doar arata mesajul utilizatorului si continua executia).
**Aplicabil direct la ideea "Stop scrie handoff-ul acum": da, se poate implementa** —
hook-ul `Stop` verifica o conditie (context mare, sarcina neterminata etc.), daca handoff-ul nu
exista inca pe disc raspunde cu `decision: "block"` + `reason: "scrie handoff-ul pe disc inainte
de oprire"`, Claude continua turul, scrie handoff-ul, iar hook-ul (cand ruleaza din nou la
urmatoarea incercare de Stop) vede handoff-ul pe disc si lasa oprirea sa treaca. Trebuie
verificat `stop_hook_active` ca sa nu intre in bucla si respectat plafonul de 8.
---
## VERDICT PRACTIC
**Lantul complet "handoff pe disc -> /clear -> reinjectare automata" se poate construi, cu doua
completari fata de premisa initiala:**
1. **Partea automata, confirmata**:
- `SessionStart` cu matcher `clear` (sau, pentru compactare, matcher `compact`) fireste
dupa `/clear`/compactare, primeste `source` explicit (`"clear"` / `"compact"`) — hook-ul
poate citi handoff-ul de pe disc si il injecta prin
`hookSpecificOutput.additionalContext`, INAINTE de primul prompt al sesiunii noi. Asta
merge din prima, fara nicio interventie manuala suplimentara, pentru ambele declansatoare
(`/clear` SI compactare — nu doar `/clear`).
- Nu exista limita blocanta de marime: sub 10.000 caractere textul intra direct in context;
peste, Claude Code il scrie singur intr-un fisier si trimite calea — deci un handoff mare
nu se pierde, doar se livreaza indirect.
- `Stop` poate fi folosit ca plasa de siguranta care FORTEAZA scrierea handoff-ului inainte
de oprire (`decision: "block"` + `reason`), cu conditia sa respecte `stop_hook_active` si
plafonul de 8 blocari.
2. **Ce ramane obligatoriu manual / in afara hook-urilor**:
- **Niciun hook nu poate declansa `/clear` sau `/compact` singur** — confirmat explicit in
documentatie ("can't trigger `/` commands or tool calls"). Utilizatorul (sau orchestrator-ul,
daca ruleaza in headless/SDK cu control asupra sesiunii) trebuie sa emita el actiunea.
- **Niciun hook nu anunta "am ajuns la ~50% context"** — nu exista un eveniment de tip
"context threshold reached". Campurile `context_tokens` etc. apar DOAR pe `SessionStart` cu
`source: "resume"/"fork"`, dupa fapt — nu in timp real, in timpul sesiunii curente. Decizia
de "e timpul sa scriu handoff-ul" ramane a modelului/sesiunii, exact cum descrie deja
regula din `CLAUDE.md` — hook-urile nu o pot automatiza.
- **Premisa gresita de reparat**: daca planul se baza pe `PostCompact` pentru reinjectare,
nu functioneaza — `PostCompact` n-are `decision control` deloc. Mecanismul corect pentru
compactare e `SessionStart` cu matcher `compact`, nu `PostCompact`.
**Pe scurt**: partea "citeste handoff de pe disc si baga-l inapoi in context la (re)pornire" e
100% automata prin `SessionStart`. Partea "declanseaza tu insuti /clear" ramane 100% manuala —
nu exista ocolire documentata. `Stop` poate automatiza doar "nu te opri pana nu ai scris
handoff-ul pe disc", nu si declansarea lui `/clear` dupa aceea.
---
## Nota metodologica
`WebFetch` (tool-ul standard) trunchiaza/rezuma paginile `hooks` si `hooks-guide` inainte sa
ajunga la sectiunile per-eveniment (confirmat empiric: trei incercari diferite de prompt au
esuat sa extraga `### SessionStart`, `### Stop`, `### PostCompact` din pagina `hooks`, desi
tabelele de sus ale paginii ies corect). Documentatia Mintlify expune si varianta bruta:
`https://code.claude.com/docs/en/hooks.md` si `.../hooks-guide.md` (mentionate chiar de pagina:
"Fetch the complete documentation index at: https://code.claude.com/docs/llms.txt"). Am fetch-uit
acele `.md`-uri direct prin `curl` (citire read-only, fara nicio scriere in afara acestui
livrabil) si am citat din ele. Toate citatele de mai sus sunt verbatim din acele fisiere.

View File

@@ -1,170 +0,0 @@
# Test headless — `citeste_curs_bnr` (arhiva 10 zile + fisier anual BNR)
Tinta: `COMUN\clase\onom_curs.vcx`, clasa `actualizare_curs_bnr`, metoda `citeste_curs_bnr`
(patch v3, deja scris in binar — confirmat inainte de test, vezi sectiunea 0). Metoda a fost
apelata **DIRECT**, niciodata prin `initializeaza` — niciun apel la `initializeaza()` sau
`scrie_curs_bnr()` in tot scriptul, deci **nicio scriere in Oracle**. Conexiunea Oracle deschisa de
mediul de test (`test_init_env_auto_roafacturare.prg`) face doar `SELECT`-uri (firma, calendar,
`pack_sesiune.set*`) pentru initializarea sesiunii — nimic legat de cursuri BNR.
## 0. Confirmare inainte de test
`COMUN\clase\onom_curs.vc2` are deja proprietatile `clinkan`/`clinkarhiva` si noua
`citeste_curs_bnr` cu parametrul `tnSursa` (linia 106+), identice cu blocul `NOU` din
`docs\patch_curs_bnr_arhiva.md` — verificat cu grep inainte de a scrie testul. `.vcx`/`.VCT` au
acelasi mtime cu `.vc2` (22:11, 17.09.2026) — write-back-ul din `docs\writeback_curs_bnr.md`
confirmat aplicat, nu s-a atins din nou.
## 1. Artefact de harness gasit si ocolit (NU e defect de cod)
Apeland `citeste_curs_bnr` direct (fara `initializeaza`), primul apel a agatat testul (90s
timeout, 0 dialoguri detectate de `watchdog_vfp.ps1`). Cauza, confirmata prin proba minimala
(`test_curs_bnr_probe2.prg`, sters dupa confirmare):
- `citeste_curs_bnr` incepe cu `creeaza_backup_cursoare(This.cCursor)` (`onom_curs.vc2:118`), care
cheama `copiaza_structura_cursor()` din `COMUN\programe\oproceduri_comune.prg:3024`.
- Acolo, `If Used(tcSursa)` (`oproceduri_comune.prg:3027`) — daca `ccrsbnr` nu e deja deschis (cazul
cand chemi metoda direct, fara sa treci prin `initializeaza`, care il creeaza via
`goExecutor.oExecute`), cade pe `Else` si cheama
`amessagebox("Eroare interna 2 - copiaza structura cursor",...)` (`oproceduri_comune.prg:3036`).
- `amessagebox` (functia REALA, `oproceduri_comune.prg:1205`) e apelata **din acelasi fisier** ca
`copiaza_structura_cursor` — `mock_amessagebox.prg` documenteaza explicit aceasta limita
("LIMITA 1: nu acopera sigur apelurile INTRA-FISIER") — deci mock-ul nu intercepteaza, se
deschide `messagebox_form` real (`MessageBox.vcx`), modal, headless agata.
- In productie nu se intampla: `initializeaza()` creeaza `This.cCursor` cu
`goExecutor.oExecute(lcSql, This.cCursor)` (`onom_curs.vc2:159`) INAINTE de a chema
`citeste_curs_bnr`.
**Reparatia e in test, nu in cod**: inainte de FIECARE apel, testul precreaza `ccrsbnr` gol
(`CREATE CURSOR ccrsbnr (Currency C(10), multiplier N(10,4), Curs N(14,6))` — campurile confirmate
din `scrie_curs_bnr`, `onom_curs.vc2:282-283`, care citeste exact `Currency`/`multiplier`/`Curs`
din cursor). Metoda oricum inchide cursorul (`Use In`) si il reconstruieste din raspunsul HTTP
(`Xmltocursor`), deci structura precreata conteaza doar pentru backup, nu pentru rezultat.
Confirmat cu proba minimala: dupa precreare, Return=0, Reccount=37, EUR curs=5.2637 — fara
agatare. Niciun fisier de productie nu a fost atins pentru asta.
## 2. Rezultate — 7 cazuri cerute + 1 caz suplimentar (cascada)
| # | Caz | Parametri | Return asteptat | Return obtinut | Durata | Rezultat |
|---|---|---|---|---|---|---|
| 1 | sursa 1, azi (zi lucratoare) | `dData=17.09.2026` (joi) | 0 | 0 | 0.238s | **PASS** — Reccount=37, EUR curs=5.2637 |
| 2 | sursa 1, luni recenta | `dData=14.09.2026` (luni) | 0 | 0 | 0.048s | **PASS** — cade pe publicarea de vineri, Reccount=37, EUR curs=5.2557 |
| 3 | sursa 2, martie an curent | `dData=15.03.2026` | 0 | 0 | 0.084s | **PASS** — Reccount=37, EUR curs=5.0947 |
| 4 | sursa 2, 2 ianuarie an curent | `dData=02.01.2026` | 0 | 0 | 0.202s | **PASS** — trecere in 2025, Reccount=38, EUR curs=5.0985 |
| 5 | sursa 1, data mult mai veche decat arhiva | `dData=01.01.2026` | 2 | 2 | 0.047s | **PASS** — "data lipsa" distinct de "BNR picat" |
| 6 | sursa 1, host invalid (indisponibilitate) | `cLinkArhiva`=host `.invalid` inexistent, `dData`=azi | 1 | **2** | 0.181s | **FAIL** — vezi sectiunea 3, defect real in cod |
| 7 | fara parametru (compatibilitate inapoi) | `dData=18.08.2026` | 0 (comportament sursa 0) | 0 | 0.041s | **PASS** — `This.dData` s-a suprascris la 18.09.2026 (publicare+1), ca inainte |
| 8 (suplimentar, cerut de coordonator) | cascada reala: acelasi obiect/`dData` din caz 5 (`Return 2` pe sursa 1), apoi sursa 2 | `dData=01.01.2026` | 0 | 0 | 0.120s | **PASS** — Reccount=38, EUR curs=5.0985, FARA "Eroare interna 2" la al doilea apel |
**Sumar: 7 PASS, 1 FAIL** (cazul 6).
## 3. Cazul 5 — eroare tranzitorie auto-vindecata, nu afecteaza Return
La finalul cazului 5 (Return 2, data prea veche), logul a prins:
```
[ON ERROR] 13 Alias 'CCRSBNR' is not found. in REPUNE_BACKUP_CURSOARE linia 6363
```
Cauza: `citeste_curs_bnr` inchide `This.cCursor` (`Use In`) inainte de apelul HTTP; pe ramura
`Return 2` cheama `repune_backup_cursoare(This.cCursor)` (`onom_curs.vc2:175`), a carei prima
linie utila e `Select (tcOriginal)` (`oproceduri_comune.prg:6363`) pe un alias deja inchis —
**risc preexistent, deja documentat** in `docs\patch_curs_bnr_arhiva.md` sectiunea 5, punctul 3
("`repune_backup_cursoare` pe cursor deja inchis"), nu introdus de acest patch. Confirmat acum ca
se reproduce efectiv. Efect: eroarea e prinsa de `ON ERROR`, executia continua la linia
urmatoare (`lnRecno = Recno()`), iar liniile de dupa (`Select * From bckpccrsbnr Into Cursor
ccrsbnr`) reconstruiesc singure alias-ul `ccrsbnr` — **auto-vindecare in aceeasi rulare**, `Return
2` final e corect si cazul 8 (cascada imediat urmatoare, sursa 2 pe acelasi obiect) confirma ca
`ccrsbnr` ramane intr-o stare corecta (Used, fara sa mai loveasca "Eroare interna 2" la
`creeaza_backup_cursoare` din apelul urmator). Nu s-a reparat codul — doar raportat, cum s-a cerut.
## 4. Cazul 6 — defect REAL, nu artefact de test
**Return obtinut 2, asteptat 1.** Nu e problema harnessului — e un gol real in
`citeste_curs_bnr` pentru cazul specific "host complet nerezolvabil" (nu doar HTTP non-200 sau
timeout pe conexiune).
Secventa exacta din log (prima eroare e cea reala, restul e cascada, exact ca in nota de procedura
despre `ON ERROR`):
```
[ON ERROR] 1429 OLE IDispatch exception ... WinHttp.WinHttpRequest: The server name or address
could not be resolved. in ACTUALIZARE_CURS_BNR.CITESTE_CURS_BNR (linia loHTTP.Send(), onom_curs.vc2:137)
[ON ERROR] 1429 ... The data necessary to complete this operation is not yet available. (citirea
ulterioara a lui loHTTP.Status, onom_curs.vc2:139)
... (cascada: 8x eroare 11 "Function argument value, type, or count is invalid" in bucla
Do While de la onom_curs.vc2:151-173, cu lcFisier ramas needefinit)
[ON ERROR] 13 Alias 'CCRSBNR' is not found. in REPUNE_BACKUP_CURSOARE linia 6363
```
Explicatie: `loHTTP.Send()` (`onom_curs.vc2:137`) NU e in `TRY/CATCH` si NU verifica intai daca
request-ul a esuat la nivel de retea/DNS — arunca direct o exceptie OLE cand hostul nu se rezolva.
Fara `TRY/CATCH` in metoda, `ON ERROR` global preia eroarea si, conform semanticii VFP (executia
continua la linia URMATOARE, nu la ramura `Else` corecta), programul "sare" peste blocul
`If loHTTP.Status = 200 ... Else ... Return 1 ... Endif` fara sa execute nici ramura de succes, nici
`Return 1` — continua direct in bucla de cautare `Cube` cu `lcFisier` gol/nedefinit, epuizeaza cele
10 incercari (cascada de erori 11), si iese pe ramura "nimic gasit" -> `Return 2`. Rezultatul final
e gresit: un host complet indisponibil (DNS esuat) e raportat ca "data lipsa" (2), nu ca
"indisponibilitate" (1) — exact confuzia pe care patch-ul trebuia sa o elimine, dar doar pentru
raspunsuri HTTP non-200 de la un server care RASPUNDE, nu pentru esecul de rezolvare DNS.
**Durata: 0.181s** — nu agata deloc (departe de cele 5s/5s/5s/15s din `SetTimeouts`); esecul de
rezolvare DNS e instant, nu trece prin timeout-uri.
Conform instructiunii explicite: **nu am reparat codul**. Raportez cu dovada de mai sus.
## 5. Cum s-a rulat
Script: `D:\ROA\ROAFACTURARE\COMUN\utile\Teste\test_curs_bnr_headless.prg` (helper-e proprii:
`crea_cursor_gol`, `log_txt`, `log_eroare`, `verifica_caz` — toate in acelasi fisier).
Precompilare izolata:
```
powershell -ExecutionPolicy Bypass -File D:\ROA\ROAFACTURARE\COMUN\utile\Teste\_precompile.ps1 -Prg D:\ROA\ROAFACTURARE\COMUN\utile\Teste\test_curs_bnr_headless.prg
```
Rulare (proces separat, timeout 240s, in PowerShell):
```powershell
$p = Start-Process -FilePath 'C:\Program Files (x86)\Microsoft Visual FoxPro 9\vfp9.exe' `
-ArgumentList '-A','-T','"D:\ROA\ROAFACTURARE\COMUN\utile\Teste\test_curs_bnr_headless.prg"' -PassThru
if (-not $p.WaitForExit(240000)) { $p.Kill() }
```
Log complet (marca unica de rulare in prima linie, `SYS(2015)`):
`D:\ROA\ROAFACTURARE\COMUN\utile\Teste\_test_curs_bnr_out.txt` — ultima rulare confirmata: mtime
17.09.2026 22:26, marca `_7KA1C340N`.
Retea: toate cele 8 sub-cazuri au ajuns efectiv la `curs.bnr.ro` (sau la hostul invalid pentru
cazul 6) — reteaua a functionat, niciun rezultat nu e raportat fals din lipsa de conectivitate.
## 6. Confirmare: nicio scriere in Oracle
Scriptul apeleaza EXCLUSIV `loCurs.citeste_curs_bnr(...)` pe obiecte `actualizare_curs_bnr`
instantiate direct — niciun apel la `initializeaza()` (care ar citi din Oracle si, la
`Reccount=0`, ar declansa cascada de UI) si niciun apel la `scrie_curs_bnr()` (singura metoda a
clasei care scrie in `pack_curs.scrie_cotatii_curs`). Singurele interactiuni cu Oracle din intreaga
rulare sunt cele din `test_init_env_auto_roafacturare.prg` (conectare + `SELECT`-uri de sesiune),
neschimbate fata de orice alt test headless din acest proiect.
## 7. Fisiere temporare de test, sterse dupa confirmare
`test_curs_bnr_probe.prg` / `.fxp` si `test_curs_bnr_probe2.prg` / `.fxp` (folosite doar pentru
diagnostic, sterse din `COMUN\utile\Teste\` dupa ce artefactul de harness a fost confirmat si
reparat in scriptul final). Raman pe disc doar `test_curs_bnr_headless.prg` / `.fxp` si logul
`_test_curs_bnr_out.txt`.
## 8. Rerulare dupa reparatia `Try/Catch` (v4)
Data/ora: 17.09.2026 22:36. Reparatia din `docs\patch_curs_bnr_arhiva.md` sectiunea 7 (`Try/Catch`
in jurul ambelor `loHTTP.Send()` din `citeste_curs_bnr`) e scrisa in binar (write-back confirmat,
fidelity-check pe round-trip identic). `.FXP` vechi al testului sters, precompilat din nou, o
singura rulare vfp9 (`-A -T`), log nou confirmat pe mtime si marca (`_7KA1CGZRZ`, 22:36:54).
| # | Caz | Return inainte (sectiunea 2) | Return dupa | Rezultat |
|---|---|---|---|---|
| 1-4, 7, 8 | neschimbate | 0 | 0 | PASS (neschimbat) |
| 5 | data mult mai veche decat arhiva | 2 | 2 | PASS (neschimbat) |
| 6 | host invalid (indisponibilitate) | **2 (FAIL, asteptat 1)** | **1** | **PASS** |
**Sumar nou: 8 PASS, 0 FAIL** (fata de 7 PASS, 1 FAIL inainte de reparatie). Nicio regresie pe
cazurile 1-5, 7, 8. Cazul 6 intoarce acum `1` ("BNR picat") in loc de `2` ("data lipsa"), deci
`initializeaza` ofera acum linkul `https://www.cursbnr.ro/` in loc de mesajul gresit "Nu exista
cursul BNR pentru data specificata!".

View File

@@ -1,102 +0,0 @@
# VM 304 — ce este si cum se acceseaza
## Ce este
VM 304 = masina virtuala Proxmox cu ID **304**, nume **Win11-Marius**, pe nodul **pvemini**
din clusterul Proxmox ROA. Nu e in HA (`noha`).
Surse:
- `E:\proiecte\ROMFASTSQL\proxmox\cluster\docs\oprire-planificata-cluster.md:293`
`| 2 | pvemini | VM 304 Win11-Marius | Nu | Shutdown |`
- `E:\proiecte\ROMFASTSQL\proxmox\cluster\docs\oprire-planificata-cluster.md:479`
`Guest-uri in afara HA: CT 102, CT 301, VM 302, VM 303, **VM 304**, VM 310.`
- `E:\proiecte\ROMFASTSQL\proxmox\cluster\scripts\cluster-startup.sh:53` `304 # Win11-Marius`
- `E:\proiecte\ROMFASTSQL\proxmox\cluster\scripts\cluster-shutdown.sh:41`
`304 # Win11-Marius — desktop, fara dependente`
- `E:\proiecte\ROMFASTSQL\proxmox\cluster\scripts\.cluster-state.txt:13` `vm pvemini 304 running noha`
E clona lui **VM 303 (Win11-Adina)**:
- `E:\proiecte\ROMFASTSQL\docs\acces-ssh-chei-angajati.md:251`
`| docs/chei-publice/vm304.pub | ... | angajat, romfast@VM-304 (clona lui 303) |`
- `E:\proiecte\ROMFASTSQL\docs\acces-ssh-chei-angajati.md:256-260` — clona a pornit cu aceeasi
cheie privata ca VM 303; a fost regenerata separat (`ssh-keygen -t ed25519 -C "romfast@VM-304"`),
cu stergerea keypair-ului mostenit din Bitvise User keypair manager si regenerarea cheilor de
host `ssh_host_*`, altfel clona continua sa se autentifice cu identitatea VM 303 in loguri.
Nu am gasit un nume de retea/hostname DNS sau un IP direct alocat lui VM 304 in documentatia
cautata (nu e in tabelul de IP-uri interne `10.0.20.x` de la
`E:\proiecte\ROMFASTSQL\docs\acces-ssh-chei-angajati.md:388-394`, care listeaza doar nodurile
Proxmox). Accesul documentat trece prin host-ul Proxmox, nu prin IP propriu al VM-ului (vezi mai jos).
## Cum se acceseaza
**Nu prin RDP** — nu am gasit nicio mentiune de RDP catre VM 304 in fisierele cautate. Accesul
documentat e **headless, prin QEMU guest agent**, de pe LXC 171 (sau orice masina cu acces SSH la
host-ul Proxmox), catre host-ul nodului `pvemini` la `root@10.0.20.201`:
```bash
# ping guest agent, ca sa confirmi ca VM-ul raspunde:
ssh root@10.0.20.201 "qm agent 304 ping"
# executie de comanda Windows in interiorul VM 304:
ssh root@10.0.20.201 "qm guest exec 304 -- <comanda Windows>"
```
Sursa: `E:\proiecte\ROMFASTSQL\docs\drepturi-utilizatori-roa-firme.md:14-19` (documentat generic
pentru `<vmid>`, cu exemplul explicit "304 = Win11-Marius" la linia 14).
Acelasi tipar (guest agent prin `qm agent`/`qm guest exec` de pe host `root@10.0.20.201`) e
folosit si pentru celelalte VM-uri Windows non-HA ale clusterului — vezi si
`E:\proiecte\ROMFASTSQL\proxmox\cluster\docs\oprire-planificata-cluster.md:15` (guest agent
necesar pentru shutdown controlat).
## Cale de retea catre discul ei
Nu am gasit o cale UNC (`\\server\share`) documentata catre VM 304. Ce e documentat e o cale
**locala, in interiorul VM-ului** — `D:\roa\BITVISE\<client>.tlp` (profilele Bitvise) si
`D:\roa\<produs>\...` (working copy ROA, folosita ca sursa pentru scripturi SQL) — accesibila doar
prin comenzi rulate cu `qm guest exec`, nu prin share de retea:
- `E:\proiecte\ROMFASTSQL\docs\drepturi-utilizatori-roa-firme.md:17`
`# Profilul Bitvise al clientului e in D:\roa\BITVISE\<client>.tlp pe VM.`
- `E:\proiecte\ROMFASTSQL\docs\drepturi-utilizatori-roa-firme.md:62`
`Sursa: D:\roa\<produs>\COMUN\Drepturi utilizatori\drepturi_utilizatori.sql din orice working copy ROA`
- `E:\proiecte\ROMFASTSQL\docs\acces-ssh-chei-angajati.md:371-372`
`astea sunt fisierele care pleaca pe VM 303 / VM 304 (pe VM 304 stau in D:\roa\BITVISE\)`
## Ce are instalat (documentat)
- **Bitvise SSH Client**, cale `C:\Program Files (x86)\Bitvise SSH Client\sexec.exe`
(`E:\proiecte\ROMFASTSQL\docs\drepturi-utilizatori-roa-firme.md:19`), cu profile de clienti in
`D:\roa\BITVISE\*.tlp` si cheia globala `C:/Users/romfast/.ssh/id_ed25519`.
- **O copie/working copy ROA** sub `D:\roa\<produs>\...` (produsul e generic in text, nu apare
explicit "ROAFACTURARE" — vezi `drepturi-utilizatori-roa-firme.md:62`).
- Nu am gasit mentiune explicita despre VFP 9 sau Oracle client instalate pe VM 304 in fisierele
cautate (spre deosebire de VM 302, documentat separat ca `oracle-test`,
`E:\proiecte\ROMFASTSQL\proxmox\vm302-oracle-test\`).
## Credentiale — unde sunt documentate (nu le-am copiat)
- Cheia publica SSH pentru angajat pe VM 304: `E:\proiecte\ROMFASTSQL\docs\chei-publice\vm304.pub`,
amprenta SHA-256 listata la `E:\proiecte\ROMFASTSQL\docs\acces-ssh-chei-angajati.md:251`.
- Cheia globala folosita de `sexec.exe` in `qm guest exec`:
`C:/Users/romfast/.ssh/id_ed25519` (in interiorul VM 304 — vezi
`drepturi-utilizatori-roa-firme.md:19`).
- Lista completa a serverelor pe care sunt copiate cheile: `scripts/bitvise-chei.ps1`, variabila
`$Servere` (`E:\proiecte\ROMFASTSQL\docs\acces-ssh-chei-angajati.md:264`).
- Nu am gasit parola sau alta credentiala pentru autentificare directa (RDP/consola) pe VM 304 in
fisierele cautate.
## Unde am cautat
1. `D:\ROA\ROAFACTURARE\COMUN\docs\` (grep `304`, `vm304`, `masina virtuala`, `server de test`,
`infrastructur`) — potriviri gasite pentru `304` erau toate numere de linie in cod PL/SQL
(`PACK_UTILS` linia 304, `PLS-00304`) sau text irelevant, nicio mentiune de VM 304.
2. `D:\ROA\COMUNROA\` — grep pe `304` a expirat (timeout la 20s pe arborele intreg, prea mare);
nu am reluat cautarea pentru ca raspunsul complet a fost deja gasit la pasul 3, conform
instructiunii de oprire la primul raspuns gasit.
3. `E:\proiecte\ROMFASTSQL\` — gasit direct: `docs/chei-publice/vm304.pub`,
`docs/acces-ssh-chei-angajati.md`, `docs/drepturi-utilizatori-roa-firme.md`,
`proxmox/cluster/docs/oprire-planificata-cluster.md`,
`proxmox/cluster/scripts/cluster-{startup,shutdown}.{sh,ps1}`,
`proxmox/cluster/scripts/.cluster-state.txt`.

View File

@@ -1,95 +0,0 @@
# Write-back patch curs BNR arhiva (v3) — onom_curs.vc2
Tinta: `COMUN\clase\onom_curs.vc2`, clasa `actualizare_curs_bnr`. Sursa patch-ului:
`docs\patch_curs_bnr_arhiva.md` (v3) si `docs\patch_curs_bnr_arhiva.diff`.
## 0. Stare de pornire
- `git status --short clase/onom_curs.vc2` (COMUN) — gol, fara modificari necomise.
- `svn status clase/onom_curs.vc2` — `I` (ignorat de SVN, fisier text generat).
- Backup inainte de orice modificare, in scratchpad: `onom_curs.vc2.bak`, `onom_curs.vcx.bak`,
`onom_curs.VCT.bak`.
## 1. Metoda de editare
Editare byte-safe cu script Python (mod binar `rb`/`wb`), fara `Edit`/`sed -i`. Continutul nou
(ASCII pur) a fost extras direct din `docs\patch_curs_bnr_arhiva.md` (blocurile `NOU` pentru
`citeste_curs_bnr`, indentare identica cu fisierul tinta — offset 0) si reconstruit manual pentru
blocul `initializeaza` (indentarea reala din fisier e cu 1 tab mai adanca decat in markdown: linia
`Case This.dData < ...` are 7 tab-uri in fisier, nu 6 ca in document — s-a aplicat +1 tab pe toate
liniile blocului nou, pastrand structura interna neschimbata).
## 2. Linii schimbate efectiv (dupa write-back, confirmate cu `git diff`)
Trei zone, toate in `actualizare_curs_bnr`:
1. **Proprietati noi** `clinkan` / `clinkarhiva` — inserate in ordine alfabetica in
`*<DefinedPropArrayMethod>` (dupa `*p: clink`), in `*<PropValue>` (dupa `clink = ...`) si in
`_memberdata` (dupa `<memberdata name="clink".../>`), exact ca in patch.
2. **`PROCEDURE citeste_curs_bnr`** (era 37 linii, acum 92) — parametru `tnSursa`, `Do Case` pentru
alegerea link-ului (`cLink`/`cLinkArhiva`/`cLinkAn`), cautare inapoi zi cu zi (max 10 incercari),
reincercare pe anul precedent pentru `tnSursa=2`, coduri de retur `0`/`1`/`2`.
3. **`PROCEDURE initializeaza`**, ramura `nTip=1` de dupa ora 13 (era 4 linii, acum 15) — cascada
`citeste_curs_bnr(1)` -> `citeste_curs_bnr(2)` -> mesaje, link nou `https://www.cursbnr.ro/` in
loc de `http://www.bnr.ro` doar pe aceasta ramura.
`git diff --stat` (COMUN): `1 file changed, 79 insertions(+), 7 deletions(-)`. `git diff` integral
verificat — apar EXCLUSIV liniile din patch, nimic in alta zona a fisierului (fara diacritice
schimbate, fara CRLF/reindentari colaterale).
## 3. Cens octeti >= 0x80 (diacritice)
| | inainte | dupa |
|---|---|---|
| octeti >= 0x80 (total) | 0 | 0 |
| CRLF | 1718 | 1790 |
| LF izolate (lf - crlf) | 0 | 0 |
Secventa de octeti inalti e identica (goala in ambele versiuni — fisierul nu contine diacritice in
zonele atinse sau in restul fisierului). Zero LF izolate inainte si dupa — toate liniile noi au
fost inserate cu `\r\n`, consistent cu restul fisierului. Cresterea de 1790-1718=72 linii CRLF
corespunde exact insertiilor nete (2+2+2 proprietati + 11 linii nete in `initializeaza` + 55 linii
nete in `citeste_curs_bnr`).
## 4. Write-back text -> binar
Comanda:
```
powershell -ExecutionPolicy Bypass -File D:\ROA\UTIL\foxbin2prg\txt2vcx.ps1 -TextFile D:\ROA\ROAFACTURARE\COMUN\clase\onom_curs.vc2 -ProjectRoot D:\ROA\ROAFACTURARE -AllowComun
```
Iesire:
```
O,P1,E0,S1,X0,c:\users\mmari\appdata\local\temp\txt2bin_roafacturare\16352-20260917221154630\comun\clase\onom_curs\verify\onom_curs.vc2
--- Sumar txt2vcx ---
OK D:\ROA\ROAFACTURARE\COMUN\clase\onom_curs.vc2
Toate cele 1 fisier(e) au fost scrise cu succes.
```
`onom_curs.vcx` si `onom_curs.VCT` au mtime nou (22:11:55, imediat inaintea `.vc2` la 22:11:56 —
scriere in aceeasi rulare).
## 5. Fidelity check pe binar (independent de mtime)
Reconvertit binarul proaspat scris (`onom_curs.vcx`) intr-un cache TEMPORAR separat, cu
`vcx2txt.ps1 -Source ... -CacheRoot <scratchpad>\fidelity_check -Force`, si comparat textul
regenerat cu textul sursa:
```
cmp fidelity_check\COMUN\clase\onom_curs.vc2 COMUN\clase\onom_curs.vc2
-> FIDELITY OK: byte-identical
```
Rezultat: **identic byte cu byte**. Round-trip confirmat.
## 6. Stare finala
`git status --short` in COMUN:
```
M clase/onom_curs.vc2
```
Doar `.vc2` e urmarit de git (binarele `.vcx`/`.VCT` nu sunt versionate); niciun alt fisier atins.
**Niciun commit, niciun `svn commit`, niciun rebuild de EXE.** Modificarea ramane necomisa, in
asteptarea aprobarii/probei manuale a lui Marius (pasii de proba sunt in
`docs\patch_curs_bnr_arhiva.md`, sectiunea 6).