Skip to content

security: auditoria completa 0.8 — vulnerabilitats, hardening i tests#1

Closed
jgoy-labs wants to merge 39 commits into
mainfrom
codex/pre-production
Closed

security: auditoria completa 0.8 — vulnerabilitats, hardening i tests#1
jgoy-labs wants to merge 39 commits into
mainfrom
codex/pre-production

Conversation

@jgoy-labs

@jgoy-labs jgoy-labs commented Feb 21, 2026

Copy link
Copy Markdown
Owner

Resum

Implementació completa del pla d'auditoria de seguretat per Nexe Server 0.8. S'han analitzat ~11.000 fitxers Python i s'han corregit 21 problemes classificats per severitat.

🔴 Crítiques resoltes

  • C-1 — Secrets NEXE_PRIMARY_API_KEY i NEXE_CSRF_SECRET rotats
  • C-3 — Bare except: substituïts per except Exception as e: amb logging a 4 fitxers

🟠 Altes resoltes

  • A-1 — Upload path traversal: sanititzar filename + whitelist d'extensions ({.txt, .md, .pdf, .csv, .rst, .html})
  • A-2 — Qdrant: suport QDRANT_API_KEY per entorns remots
  • A-3/health/ready: retorna {status, timestamp} sense exposar llista de mòduls sense auth
  • A-4 — HTTPException 500: eliminar detail=str(e) (no filtrar internals als clients)
  • A-5 — Qdrant log file: flush() + = None al shutdown
  • A-6SessionManager.cleanup_inactive() implementat amb TTL real
  • A-7 — Streaming Ollama: gestionar asyncio.CancelledError (client desconnectat)

🟡 Mitges resoltes

  • M-2 — Docker healthcheck per al servei nexe a docker-compose.yml
  • M-3qdrant/qdrant:latestqdrant/qdrant:v1.12.0
  • M-4.dockerignore creat (exclou .env, storage/, .git/, tests/)
  • M-5httpx eliminat de requirements-dev.txt (duplicat)
  • M-6pip-audit afegit al CI pipeline

🟢 Tests de seguretat (14/14 passing)

Nou fitxer tests/integration/test_security.py:

  • Path traversal i extensions no permeses a upload
  • Regressió _sanitize_rag_context (truncació + filtratge injeccions)
  • /health/ready no exposa informació de mòduls sense auth
  • Session TTL cleanup

Altres millores incloses a la branca

  • Dockerfile: usuari non-root nexe:nexe, permisos 750
  • runner.py: validate_production_config() per detectar secrets buits en producció
  • scripts/generate_secrets.sh: script per generar secrets segurs
  • knowledge/SECURITY.md, DEPLOYMENT.md: documentació de seguretat

Pla de tests

  • pytest tests/integration/test_security.py -v → 14/14 passing
  • pip-audit -r requirements.txt → sense CVEs crítics
  • docker compose up --build -d → healthcheck OK per ambdós serveis
  • Verificar que /health/ready retorna {status, timestamp} sense detalls de mòduls
  • Verificar que upload de .exe retorna HTTP 400
  • Verificar que upload de ../../etc/passwd retorna HTTP 400

jgoy-labs and others added 30 commits February 4, 2026 23:07
Documentació corregida i actualitzada:
- API.md: Endpoints correctes amb /v1/, dual-key auth, X-API-Key headers
- SECURITY.md: Configuració real (server.toml), paths correctes, MAX_SCAN_LENGTH
- RAG.md: Sistema híbrid embeddings (nomic-embed-text 768d), 3 col·leccions, chunking en caràcters
- USAGE.md: CLI real (memory recall/store/stats/cleanup, knowledge ingest), autenticació obligatòria
- README.md: Stack correcte (Click+Rich), compatibilitat OpenAI parcial, SECURITY.md afegit

Canvis principals documentació:
- Models embeddings: Ollama nomic-embed-text (768 dims) + fallbacks documentats
- Col·leccions Qdrant: nexe_chat_memory, nexe_documentation, user_knowledge
- Thresholds diferenciats: 0.4 (docs), 0.35 (knowledge), 0.3 (memory)
- Chunking: 1500/200 chars (text general), 800/100 (RAG endpoint)
- Autenticació X-API-Key obligatòria a tots els endpoints /v1/*
- CLI real: store|recall|stats|cleanup (no search/list/delete)
- Paths correctes: storage/qdrant/, /v1/memory/*, /api/info

Codi NEXE 0.8 complet afegit:
- Core: FastAPI server, CLI, endpoints, loaders
- Plugins: MLX, llama.cpp, Ollama, security, web UI
- Memory: RAG system, embeddings, Qdrant integration
- Personality: i18n, module management, configuration

Eliminats:
- PRODUCTION_PLAN.md
- REVIEW_SUPERCONSULTOR_2026-01-31.md

Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
- Add BaseContract protocol (minimum for all)
- Add ModuleContract protocol (for plugins with get_router())
- Add UnifiedManifest Pydantic model with validation
- Add ContractRegistry singleton (thread-safe)
- Add ContractValidator (multi-layer validation)
- Add 19 unit tests (all passing)

Simplified version for NEXE only (no ManagerContract, no SpecialistContract).

Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
- Create ManifestMigrator to convert old manifests to UnifiedManifest
- Migrate ollama_module, mlx_module, security, llama_cpp_module, web_ui_module
- Preserve custom sections in metadata
- Normalize versions (0.2 → 0.2.0)
- Relax api.prefix validation for short prefixes (/ollama, /mlx, /ui)
- Backup old manifests to .old
- All 5 plugins validate successfully with Pydantic

Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
- Create ContractBridge to connect ModuleManager with ContractRegistry
- Create ModuleContractAdapter to adapt existing modules to BaseContract
- Auto-register modules to ContractRegistry on load
- Auto-unregister modules on stop
- Add 6 integration tests (all passing)
- Maintain full backward compatibility

Integration:
- ModuleLifecycleManager.load_module() → ContractBridge.register_module()
- ModuleLifecycleManager.stop_module() → ContractBridge.unregister_module()

Tests validate:
- Unified manifests load correctly
- Singleton patterns work
- Module registration/unregistration
- Backward compatibility maintained
- Custom metadata preserved

Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
- Document UnifiedManifest schema
- Document BaseContract and ModuleContract protocols
- Document ContractRegistry API
- Add migration guide
- Add plugin creation guide
- Document validation layers
- Add test results and metrics
- List all 5 migrated plugins

Status: ✅ Implemented and validated
Tests: 25 passing (19 unit + 6 integration)
Coverage: >80%

Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
Complete implementation report covering:
- Architecture and components (~2,500 lines)
- 5 plugins migrated (ollama, mlx, security, llama_cpp, web_ui)
- Multi-layer validation (Schema + Runtime + Integration + Static)
- 25 tests (19 unit + 6 integration) - 100% passing
- Performance metrics (<1ms overhead per plugin)
- ContractRegistry integration via ContractBridge
- Full backward compatibility maintained

Status: ✅ System validated and production-ready
Coverage: >80%
Tests: 25/25 passing

Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
- Change test_api_prefix_must_match_name to test_api_prefix_must_start_with_slash
- Add test_api_prefix_can_be_short to validate short prefixes (/ollama vs /ollama_module)
- Fix regex pattern match in ValidationError assertion
- All 26 tests now passing (was 25)

Tests: 26/26 passing ✅

Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
- Add 17 tests for manifest_migrator.py (0% → 78% coverage)
- Add 21 tests for validators.py (35% → 90% coverage)
- Add 24 tests for registry.py (60% → 79% coverage)
- Total coverage improved from 60% to 91%
- All 88 tests passing (26 → 88 tests, +62 new)

Coverage breakdown:
  manifest_migrator.py: 0% → 78% (+78%)
  validators.py: 35% → 90% (+55%)
  registry.py: 60% → 79% (+19%)
  TOTAL: 60% → 91% (+31%)

Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
- Updated test counts: 26 → 88 tests
- Updated coverage: >80% → 91% (real measurement)
- Updated component coverage:
  - manifest_migrator.py: 0% → 78%
  - validators.py: 35% → 90%
  - registry.py: 60% → 79%

Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
Final coverage breakdown:
  - manifest_migrator.py: 78% → 91% (+13%)
  - validators.py: 90% → 94% (+4%)
  - registry.py: 79% → 97% (+18%)
  - models.py: 95% (maintained)
  - base.py: 99% (maintained)

Total improvement: 60% → 95% (+35%)

Tests added:
  - 10 new migration tests (custom capabilities, i18n, storage, etc.)
  - 6 new registry tests (activate/deactivate, error handling)
  - 4 new validator tests (has_warnings, get_errors, get_warnings, to_dict)

Total: 88 → 122 tests (+34 tests)
All 122 tests passing ✅

Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
Updated test counts: 88 → 122 tests (+34)
Updated coverage: 91% → 95% (+4%)

Final component coverage:
  - manifest_migrator.py: 91%
  - validators.py: 94%
  - registry.py: 97%
  - models.py: 95%
  - base.py: 99%

Overall improvement from start: 60% → 95% (+35%)

Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
Updated all documentation to reflect current state:
- Tests: 25/26 → 122 (actual count)
- Coverage: >80% → 95% (measured)
- Removed detailed test lists (too verbose and outdated)
- Simplified test summaries by component

Files cleaned:
- knowledge/PLUGIN_CONTRACT.md: Updated metrics section
- SISTEMA_PLUGINS_INFORME.md: Updated test counts and summaries

All documentation now accurate and consistent ✅

Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
More intuitive name that clearly indicates the file documents
the plugin system, not just the abstract contract system.

Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
Changed validation from requiring prefix to match /{module.name}
to just requiring it starts with /. This allows plugins to use
more intuitive prefixes (e.g., security uses /auth instead of /security).

Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
- Add IMPLEMENTATION_PLAN.md (47KB) - Detailed implementation steps
- Add UNIFIED_CONTRACTS.md (73KB) - Complete technical architecture
- Add UNIFIED_CONTRACTS_PLAN.md (29KB) - Plan with specialists
- Update .gitignore to exclude .coverage and htmlcov/

These documentation files provide comprehensive details about the
unified contracts system implementation for NEXE 0.9.

Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
Fixes crítics:
- C-1: Rotar secrets NEXE_PRIMARY_API_KEY i NEXE_CSRF_SECRET
- C-3: Substituir bare except: per except Exception as e: amb logging
- A-1: Upload path traversal — sanititzar filename + whitelist extensions
- A-2: Qdrant API key — suport QDRANT_API_KEY (opcional, per entorns remots)
- A-3: /health/ready — limitar resposta a {status, timestamp} sense auth
- A-4: HTTPException 500 — no retornar str(e) als clients
- A-5: Qdrant log file — flush() + = None al shutdown
- A-6: SessionManager — implementar cleanup_inactive() amb TTL real
- A-7: Ollama streaming — gestionar asyncio.CancelledError i log JSONDecodeError

Hardening Docker i CI:
- M-2: Afegir healthcheck al servei nexe a docker-compose.yml
- M-3: Pinnar qdrant/qdrant:v1.12.0 (eliminar :latest)
- M-4: Crear .dockerignore (exclou .env, storage/, .git/, tests/)
- M-5: Eliminar httpx duplicat de requirements-dev.txt
- M-6: Afegir pip-audit al CI pipeline

Tests de seguretat (14/14 passing):
- T-1: path traversal, extensions no permeses, filename buit
- T-2: regressió _t_global / _sanitize_rag_context truncació
- T-3: /health/ready no exposa informació de mòduls sense auth
- T-bonus: session TTL cleanup (expirades eliminades, actives conservades)

Altres millores:
- Dockerfile: usuari non-root nexe:nexe, permisos 750
- runner.py: validate_production_config() per detectar secrets buits
- scripts/generate_secrets.sh: script per generar secrets segurs
- knowledge/SECURITY.md, DEPLOYMENT.md: documentació de seguretat
@jgoy-labs jgoy-labs force-pushed the codex/pre-production branch from ea53bbb to aa487bd Compare February 21, 2026 14:57
@jgoy-labs jgoy-labs closed this Feb 21, 2026
@jgoy-labs jgoy-labs deleted the codex/pre-production branch February 21, 2026 15:09
jgoy-labs added a commit that referenced this pull request Mar 9, 2026
#3 loader: fallback només considera classes definides al mòdul
   (afegit attr.__module__ == module.__name__ per evitar classes importades)
#2 singletons: afegits reset_config() i reset_loader() per testing
#1 core/app.py: import os mogut al bloc d'imports del principi
#4 loader.py: docstrings traduïts de català a anglès (unificació open-source)

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
jgoy-labs added a commit that referenced this pull request Apr 12, 2026
Correcció final del sync 8baa187. L'stash original (stash@{0}) del pre-sync
nomes incloïa els canvis dels sprints 0-4 del 01/04. Els meus 27 bugs HOMAD
del 06/04 van tocar fitxers addicionals que no estaven a aquella llista,
aixi que vaig assumir (per error) que el sync ja els cobria.

Fitxers afegits (tots ja committats al dev al commit 6fa8c55):

Core / Bloc 2 Dev A (seguretat Bug 21/22) + Dev C (Bug 13):
- core/crypto/keys.py (Bloc 1 Bug 8 TOCTOU — versio nova amb os.open atomic)
- core/endpoints/bootstrap.py (Bug 22 auth require_api_key a /api/bootstrap/info)
- core/endpoints/chat.py (Bug 21 validate_string_input)
- core/endpoints/modules.py (Bug 22 auth)
- core/endpoints/tests/test_bootstrap.py (Bug 22 dependency_override)
- core/endpoints/tests/test_bootstrap_info.py (Bug 22 dependency_override)
- core/endpoints/tests/test_modules.py (Bug 22 dependency_override)
- core/ingest/ingest_knowledge.py (Bloc 3 Bug 18 _read_text_with_fallback)
- core/metrics/endpoint.py (Bug 22 auth)
- core/metrics/tests/test_endpoint.py (Bug 22 auth override)
- core/qdrant_pool.py (Bloc 2 Bug 13 _flush_client + logger.warning)
- core/server/factory_app.py (Bug 22 docs/redoc/openapi gated)
- core/server/tests/test_server.py (Bug 22 auth override)

Installer / Bloc 1 Dev #1:
- installer/install.py (Bug 7 wiring reinstall_mode)
- installer/installer_catalog_data.py (Bug 29 Phi-3.5 fora del cataleg)
- installer/installer_display.py (Bloc 3 Bug 4 ANSI buit sense TTY)
- installer/installer_setup_qdrant.py (Bloc 3 Bug 5 didactic isatty)
- installer/swift-wizard/Resources/models.json (Bug 29 Phi-3.5 fora wizard)

Memory / Bloc 1 Dev #1/#3 (Bug 10) + Bloc 2 (Bug 22):
- memory/memory/router.py (Bug 22 auth)
- memory/memory/workers/dreaming_cycle.py (Bloc 1 Bug 10 — try/finally +
  close() a 6/6 funcions)
- memory/rag/router.py (Bug 22 auth)

Personality / Bloc 2 Dev C Bug 20 + Dev D Consultor passada 1:
- personality/module_manager/discovery.py (Bug 20 get_cycle_warnings +
  Bug 12 early return)
- personality/module_manager/module_manager.py (Bug 20 expose getter)

Plugins / Bloc 2 Dev B (Bug 19) + Dev A (Bug 22):
- plugins/mlx_module/core/prompt_cache_manager.py (Bug 19 double-checked locking)
- plugins/ollama_module/api/routes.py (Bug 22 auth)
- plugins/ollama_module/tests/unit/test_manifest.py (Bug 22 auth override)
- plugins/ollama_module/tests/unit/test_manifest_coverage.py (Bug 22 auth override)

Despres d'aquest commit, tots els 27 bugs del HOMAD son al gitoss. El
pytest del gitoss hauria de donar valors equivalents al dev (4389 passed,
7 fails pre-existents).
jgoy-labs added a commit that referenced this pull request Apr 12, 2026
Fix-All BUS sobre 3 tracks paral·lels per resoldre tots els bugs del QA
post-BUS de normalització abans del DMG v0.9.0. 8 commits dev consolidats
en aquest sync.

TRACK A — Memory/RAG/Sessions
- Bug #1 (PID file canònic) — single source of truth a storage/run/server.pid
- F5 — 3 col·leccions canòniques (nexe_web_ui, user_knowledge, nexe_documentation)
  creades a get_memory_api() en lloc de només la primera
- F7 — ingest_knowledge defaulteja a nexe_documentation (era user_knowledge)
  i és idempotent (eliminada la sequence delete_collection + create_collection
  destructiva que esborrava docs ad-hoc dels usuaris a cada install)
- F8 — root cause empíric Bug #4: MemoryModule obria un SEGON QdrantClient
  real a storage/vectors/qdrant_local/, divergent del singleton del pool.
  MEM_SAVE escrivia a una col·lecció, MEM_RECALL llegia d'una altra.
  Ara tots dos comparteixen storage/vectors/.
- F1 — _check_duplicate retorna contracte honest (success=False, duplicate=True)
  enlloc de fingir success=True amb document_id=None. Era el segon root cause
  de Bug #4: el dedup bloquejava SAVEs amb fals positius silenciats.
- F2 — typo cols list (nexe_web_ui duplicat)
- F3 — list_memories scroll-based (sense semantic search amb query anglesa)
- Bug #10 — collections= filter a list/save/delete (sidebar checks reals)
- Bug #6 — frontend hydration document attached. Eren 2 bugs encadenats:
  l'endpoint /history no retornava attached_document, i removeFilePreview()
  feia POST /clear-document destructiu cada switch de sessió.
- Bug #3 — MEM_SAVE-only response fallback. Quan el model emet només
  [MEM_SAVE: ...] sense text envoltant, ara genera 'Memòria desada: <fact>'
  perquè el bloc save s'executi i el frontend mostri confirmació.
- auto_save crida eliminada per HOMAD memoria v1 (2026-04-01) — manual
  MEM_SAVE only fins a Part 2.

TRACK B — Tray / Multi-instance / Packaging
- Bug #1 (PID file) compartit amb Track A
- Bug #2 — setproctitle a server i tray (server-nexe / nexe-tray a ps/Activity
  Monitor). Force Quit encara mostra Python perquè requereix CFBundleName via
  .app bundle real (deute v0.9.1).
- Bug #9 — menu polish: server-nexe.com duplicat substituït per
  '📖 Documentació' al main level (3 idiomes), website_item es manté al
  submenú Configuració.

TRACK C — UX cosmètic
- Bug #5 — slow_request middleware exclou /ui/upload (uploads naturalment
  triguen >1s i el log apareixia duplicat amb l'access log d'uvicorn).
- Bug #8 — 3 ⓘ visibles als checkboxes del sidebar de col·leccions amb
  tooltips als 3 idiomes (la infraestructura CSS/i18n ja existia).

Pytest D-1 final: 4424 passed, 0 failed, 35 skipped, 1 xfailed, 86% coverage
en 76.11s. Baseline pre-BUS era 4396. +28 tests nous, ZERO regressions.

Tests nous:
- tests/test_pid_file.py: 7 tests Bug #1
- tests/test_ingest_knowledge_idempotent.py: 8 tests F7 (3 classes)
- plugins/web_ui_module/tests/test_memory_helper_async.py: 1 test F1
- plugins/web_ui_module/tests/test_memory_delete.py: 7 tests F3+Bug#10
- plugins/web_ui_module/tests/test_mem_save_injection.py: 5 tests Bug #3

Out of scope (deute v0.9.1+):
- routes_chat.py 54KB decapitació general (deute formal P0)
- Bundle .app real amb py2app per CFBundleName (deute v0.9.1)
- Resums per capítol (Part 2 redisseny memory)
- RDBMS font de veritat + vector store reconstruïble (HOMAD memoria v1, Part 2)
jgoy-labs added a commit that referenced this pull request Apr 12, 2026
Correcció final del sync 8baa187. L'stash original (stash@{0}) del pre-sync
nomes incloïa els canvis dels sprints 0-4 del 01/04. Els meus 27 bugs HOMAD
del 06/04 van tocar fitxers addicionals que no estaven a aquella llista,
aixi que vaig assumir (per error) que el sync ja els cobria.

Fitxers afegits (tots ja committats al dev al commit 6fa8c55):

Core / Bloc 2 Dev A (seguretat Bug 21/22) + Dev C (Bug 13):
- core/crypto/keys.py (Bloc 1 Bug 8 TOCTOU — versio nova amb os.open atomic)
- core/endpoints/bootstrap.py (Bug 22 auth require_api_key a /api/bootstrap/info)
- core/endpoints/chat.py (Bug 21 validate_string_input)
- core/endpoints/modules.py (Bug 22 auth)
- core/endpoints/tests/test_bootstrap.py (Bug 22 dependency_override)
- core/endpoints/tests/test_bootstrap_info.py (Bug 22 dependency_override)
- core/endpoints/tests/test_modules.py (Bug 22 dependency_override)
- core/ingest/ingest_knowledge.py (Bloc 3 Bug 18 _read_text_with_fallback)
- core/metrics/endpoint.py (Bug 22 auth)
- core/metrics/tests/test_endpoint.py (Bug 22 auth override)
- core/qdrant_pool.py (Bloc 2 Bug 13 _flush_client + logger.warning)
- core/server/factory_app.py (Bug 22 docs/redoc/openapi gated)
- core/server/tests/test_server.py (Bug 22 auth override)

Installer / Bloc 1 Dev #1:
- installer/install.py (Bug 7 wiring reinstall_mode)
- installer/installer_catalog_data.py (Bug 29 Phi-3.5 fora del cataleg)
- installer/installer_display.py (Bloc 3 Bug 4 ANSI buit sense TTY)
- installer/installer_setup_qdrant.py (Bloc 3 Bug 5 didactic isatty)
- installer/swift-wizard/Resources/models.json (Bug 29 Phi-3.5 fora wizard)

Memory / Bloc 1 Dev #1/#3 (Bug 10) + Bloc 2 (Bug 22):
- memory/memory/router.py (Bug 22 auth)
- memory/memory/workers/dreaming_cycle.py (Bloc 1 Bug 10 — try/finally +
  close() a 6/6 funcions)
- memory/rag/router.py (Bug 22 auth)

Personality / Bloc 2 Dev C Bug 20 + Dev D Consultor passada 1:
- personality/module_manager/discovery.py (Bug 20 get_cycle_warnings +
  Bug 12 early return)
- personality/module_manager/module_manager.py (Bug 20 expose getter)

Plugins / Bloc 2 Dev B (Bug 19) + Dev A (Bug 22):
- plugins/mlx_module/core/prompt_cache_manager.py (Bug 19 double-checked locking)
- plugins/ollama_module/api/routes.py (Bug 22 auth)
- plugins/ollama_module/tests/unit/test_manifest.py (Bug 22 auth override)
- plugins/ollama_module/tests/unit/test_manifest_coverage.py (Bug 22 auth override)

Despres d'aquest commit, tots els 27 bugs del HOMAD son al gitoss. El
pytest del gitoss hauria de donar valors equivalents al dev (4389 passed,
7 fails pre-existents).
jgoy-labs added a commit that referenced this pull request Apr 12, 2026
Fix-All BUS sobre 3 tracks paral·lels per resoldre tots els bugs del QA
post-BUS de normalització abans del DMG v0.9.0. 8 commits dev consolidats
en aquest sync.

TRACK A — Memory/RAG/Sessions
- Bug #1 (PID file canònic) — single source of truth a storage/run/server.pid
- F5 — 3 col·leccions canòniques (nexe_web_ui, user_knowledge, nexe_documentation)
  creades a get_memory_api() en lloc de només la primera
- F7 — ingest_knowledge defaulteja a nexe_documentation (era user_knowledge)
  i és idempotent (eliminada la sequence delete_collection + create_collection
  destructiva que esborrava docs ad-hoc dels usuaris a cada install)
- F8 — root cause empíric Bug #4: MemoryModule obria un SEGON QdrantClient
  real a storage/vectors/qdrant_local/, divergent del singleton del pool.
  MEM_SAVE escrivia a una col·lecció, MEM_RECALL llegia d'una altra.
  Ara tots dos comparteixen storage/vectors/.
- F1 — _check_duplicate retorna contracte honest (success=False, duplicate=True)
  enlloc de fingir success=True amb document_id=None. Era el segon root cause
  de Bug #4: el dedup bloquejava SAVEs amb fals positius silenciats.
- F2 — typo cols list (nexe_web_ui duplicat)
- F3 — list_memories scroll-based (sense semantic search amb query anglesa)
- Bug #10 — collections= filter a list/save/delete (sidebar checks reals)
- Bug #6 — frontend hydration document attached. Eren 2 bugs encadenats:
  l'endpoint /history no retornava attached_document, i removeFilePreview()
  feia POST /clear-document destructiu cada switch de sessió.
- Bug #3 — MEM_SAVE-only response fallback. Quan el model emet només
  [MEM_SAVE: ...] sense text envoltant, ara genera 'Memòria desada: <fact>'
  perquè el bloc save s'executi i el frontend mostri confirmació.
- auto_save crida eliminada per HOMAD memoria v1 (2026-04-01) — manual
  MEM_SAVE only fins a Part 2.

TRACK B — Tray / Multi-instance / Packaging
- Bug #1 (PID file) compartit amb Track A
- Bug #2 — setproctitle a server i tray (server-nexe / nexe-tray a ps/Activity
  Monitor). Force Quit encara mostra Python perquè requereix CFBundleName via
  .app bundle real (deute v0.9.1).
- Bug #9 — menu polish: server-nexe.com duplicat substituït per
  '📖 Documentació' al main level (3 idiomes), website_item es manté al
  submenú Configuració.

TRACK C — UX cosmètic
- Bug #5 — slow_request middleware exclou /ui/upload (uploads naturalment
  triguen >1s i el log apareixia duplicat amb l'access log d'uvicorn).
- Bug #8 — 3 ⓘ visibles als checkboxes del sidebar de col·leccions amb
  tooltips als 3 idiomes (la infraestructura CSS/i18n ja existia).

Pytest D-1 final: 4424 passed, 0 failed, 35 skipped, 1 xfailed, 86% coverage
en 76.11s. Baseline pre-BUS era 4396. +28 tests nous, ZERO regressions.

Tests nous:
- tests/test_pid_file.py: 7 tests Bug #1
- tests/test_ingest_knowledge_idempotent.py: 8 tests F7 (3 classes)
- plugins/web_ui_module/tests/test_memory_helper_async.py: 1 test F1
- plugins/web_ui_module/tests/test_memory_delete.py: 7 tests F3+Bug#10
- plugins/web_ui_module/tests/test_mem_save_injection.py: 5 tests Bug #3

Out of scope (deute v0.9.1+):
- routes_chat.py 54KB decapitació general (deute formal P0)
- Bundle .app real amb py2app per CFBundleName (deute v0.9.1)
- Resums per capítol (Part 2 redisseny memory)
- RDBMS font de veritat + vector store reconstruïble (HOMAD memoria v1, Part 2)
jgoy-labs added a commit that referenced this pull request Apr 21, 2026
…rence

Symptom: after a restart the sidebar only listed .json sessions (.enc
were invisible), new sessions were persisted unencrypted even with
encryption enabled, and a reboot's .json->.enc migration could
overwrite an existing .enc belonging to a different conversation with
the same id (collision observed in the wild: "Hola Diana" overwritten
by "Hola Anna").

Root cause is a three-bug chain:

1. Loader early-init (core/loader/manifest_base._get_module):
   calls `instance._init_router()` immediately after `__init__`,
   *before* `initialize()` runs. The plugin must have its routable
   resources ready at construction time.

2. Plugin double-create (plugins/web_ui_module/module.py):
   `__init__` created a SessionManager() without crypto, then
   `initialize()` replaced self.session_manager with a new
   SessionManager(crypto_provider=crypto). Two instances, divergent
   state: #1 loaded only .json, #2 loaded .enc + migrated .json->.enc.

3. Router local reference (plugins/web_ui_module/api/routes.py):
   `create_router()` captured session_mgr = module_instance.session_manager
   into a local at creation time. Because of bug #1, that snapshot was
   the crypto-less #1. When `initialize()` later replaced the attribute,
   the router closures kept pointing at the stale #1.

Net effect: the UI saw #1 (only .json sessions), new sessions went
through #1 and were written unencrypted, and every reboot migrated
those .json files into .enc, overwriting whatever .enc already lived
under the same id.

Fix:

- module.py: remove the premature SessionManager() from __init__; build
  the one real SessionManager(crypto_provider=crypto) in initialize()
  using whatever crypto_provider is available (may be None). One
  instance, one truth, one load-from-disk.

- routes.py: introduce _SessionManagerProxy. Instead of capturing
  session_mgr = module_instance.session_manager, route closures hold
  the proxy and `__getattr__` re-reads module_instance.session_manager
  on every request. Late-binding, so the real SessionManager built in
  initialize() is always what the routes hit. Raises a clear RuntimeError
  if a route fires before initialize() has completed (rather than the
  opaque 'NoneType has no attribute list_sessions').

Verified: after restart, a single "Loaded N sessions from disk" per
reboot (was 2 before), all .enc sessions visible in the sidebar, new
sessions persisted as .enc.

Data note: no encrypted sessions were lost in the wild. All existing
.enc files decrypt cleanly with the current MEK; they were only made
invisible by the stale reference. The only silent loss was the
conversation overwritten by the colliding-id migration, which predates
this fix.
jgoy-labs added a commit that referenced this pull request May 14, 2026
…cache key only)

Onada 1 bandit audit — finding #1 (1/23). compute_system_hash() returns
an 8-char MD5 prefix used as a prefix-cache lookup key in inference
engines, not for authentication or integrity. Pass usedforsecurity=False
so the call:

  - Is correctly classified by bandit (B324 disappears)
  - Does not abort under FIPS-only Python builds where MD5-for-security
    is rejected at runtime

The truncated digest output is byte-identical to the legacy call.

Adds a regression test pinning compute_system_hash("hello") == "5d41402a"
so any future migration to blake2b/sha256 (which would silently
invalidate every existing prefix-cache entry across deployed installs)
breaks loudly at test time.

No behavioural change. Suite: 5126 → 5127 passed.
jgoy-labs added a commit that referenced this pull request May 14, 2026
…1 Cluster 7)

9 independent assignment/arg-type findings closed via minimal annotations
or casts (no behavioural change):

- installer/installer_catalog_data.py:450 — cast(str, value) after
  truthy guard (#1)
- core/config.py:115 — pre-declare found_path: Optional[Path] before
  if/else branch (#4)
- core/cli/client.py:46 — declare self._ssl_context: Optional[
  ssl.SSLContext] = None before populating in if-branch (#13)
- core/cli/output.py:33 — annotate module-level console: Any to bridge
  the dual-decl Console / FallbackConsole (#14)
- core/resources.py:110 — cast(Any, resource_path) so Path() accepts
  the importlib.resources Traversable that exposes __fspath__ (#17)
- core/resilience/circuit_breaker.py:76 — annotate __lock_loop:
  Optional[asyncio.AbstractEventLoop] = None (#18)
- core/dependencies.py:43-44 — annotate rate_limit_tracker /
  start_rate_limit_cleanup_task as Optional[Any] = None in the import
  fallback (#29, #30)
- core/cli/cli.py:215 — annotate found: list[tuple[str, Optional[str],
  list[int]]] = [] (#62)
jgoy-labs added a commit that referenced this pull request May 14, 2026
396 findings → 0. Estratègia validada Director+Turing (AGREE):
- too_many_parameters (91 fitxers): FastAPI DI, dataclass constructors, CLI
- too_many_branches (77 fitxers): parsers, FSMs, routers, CLI handlers
- file_too_long (29 fitxers): tests exhaustius + fitxers prod densos
- too_many_statements + function_too_complex (20 fitxers): workflows complets

Diferit explícit:
- routes_chat.py → Ticket #1 Factoria F1 (façana parcial aplicada BUS Wave 2)
- app.js → nexe-app Fase 1 UI redesign (Turing: whitelist preferit)

Suite: 4987 passed, 4 pre-existents sense canvis.
jgoy-labs added a commit that referenced this pull request May 16, 2026
Correcció final del sync 8baa187. L'stash original (stash@{0}) del pre-sync
nomes incloïa els canvis dels sprints 0-4 del 01/04. Els meus 27 bugs HOMAD
del 06/04 van tocar fitxers addicionals que no estaven a aquella llista,
aixi que vaig assumir (per error) que el sync ja els cobria.

Fitxers afegits (tots ja committats al dev al commit 6fa8c55):

Core / Bloc 2 Dev A (seguretat Bug 21/22) + Dev C (Bug 13):
- core/crypto/keys.py (Bloc 1 Bug 8 TOCTOU — versio nova amb os.open atomic)
- core/endpoints/bootstrap.py (Bug 22 auth require_api_key a /api/bootstrap/info)
- core/endpoints/chat.py (Bug 21 validate_string_input)
- core/endpoints/modules.py (Bug 22 auth)
- core/endpoints/tests/test_bootstrap.py (Bug 22 dependency_override)
- core/endpoints/tests/test_bootstrap_info.py (Bug 22 dependency_override)
- core/endpoints/tests/test_modules.py (Bug 22 dependency_override)
- core/ingest/ingest_knowledge.py (Bloc 3 Bug 18 _read_text_with_fallback)
- core/metrics/endpoint.py (Bug 22 auth)
- core/metrics/tests/test_endpoint.py (Bug 22 auth override)
- core/qdrant_pool.py (Bloc 2 Bug 13 _flush_client + logger.warning)
- core/server/factory_app.py (Bug 22 docs/redoc/openapi gated)
- core/server/tests/test_server.py (Bug 22 auth override)

Installer / Bloc 1 Dev #1:
- installer/install.py (Bug 7 wiring reinstall_mode)
- installer/installer_catalog_data.py (Bug 29 Phi-3.5 fora del cataleg)
- installer/installer_display.py (Bloc 3 Bug 4 ANSI buit sense TTY)
- installer/installer_setup_qdrant.py (Bloc 3 Bug 5 didactic isatty)
- installer/swift-wizard/Resources/models.json (Bug 29 Phi-3.5 fora wizard)

Memory / Bloc 1 Dev #1/#3 (Bug 10) + Bloc 2 (Bug 22):
- memory/memory/router.py (Bug 22 auth)
- memory/memory/workers/dreaming_cycle.py (Bloc 1 Bug 10 — try/finally +
  close() a 6/6 funcions)
- memory/rag/router.py (Bug 22 auth)

Personality / Bloc 2 Dev C Bug 20 + Dev D Consultor passada 1:
- personality/module_manager/discovery.py (Bug 20 get_cycle_warnings +
  Bug 12 early return)
- personality/module_manager/module_manager.py (Bug 20 expose getter)

Plugins / Bloc 2 Dev B (Bug 19) + Dev A (Bug 22):
- plugins/mlx_module/core/prompt_cache_manager.py (Bug 19 double-checked locking)
- plugins/ollama_module/api/routes.py (Bug 22 auth)
- plugins/ollama_module/tests/unit/test_manifest.py (Bug 22 auth override)
- plugins/ollama_module/tests/unit/test_manifest_coverage.py (Bug 22 auth override)

Despres d'aquest commit, tots els 27 bugs del HOMAD son al gitoss. El
pytest del gitoss hauria de donar valors equivalents al dev (4389 passed,
7 fails pre-existents).
jgoy-labs added a commit that referenced this pull request May 16, 2026
Fix-All BUS sobre 3 tracks paral·lels per resoldre tots els bugs del QA
post-BUS de normalització abans del DMG v0.9.0. 8 commits dev consolidats
en aquest sync.

TRACK A — Memory/RAG/Sessions
- Bug #1 (PID file canònic) — single source of truth a storage/run/server.pid
- F5 — 3 col·leccions canòniques (nexe_web_ui, user_knowledge, nexe_documentation)
  creades a get_memory_api() en lloc de només la primera
- F7 — ingest_knowledge defaulteja a nexe_documentation (era user_knowledge)
  i és idempotent (eliminada la sequence delete_collection + create_collection
  destructiva que esborrava docs ad-hoc dels usuaris a cada install)
- F8 — root cause empíric Bug #4: MemoryModule obria un SEGON QdrantClient
  real a storage/vectors/qdrant_local/, divergent del singleton del pool.
  MEM_SAVE escrivia a una col·lecció, MEM_RECALL llegia d'una altra.
  Ara tots dos comparteixen storage/vectors/.
- F1 — _check_duplicate retorna contracte honest (success=False, duplicate=True)
  enlloc de fingir success=True amb document_id=None. Era el segon root cause
  de Bug #4: el dedup bloquejava SAVEs amb fals positius silenciats.
- F2 — typo cols list (nexe_web_ui duplicat)
- F3 — list_memories scroll-based (sense semantic search amb query anglesa)
- Bug #10 — collections= filter a list/save/delete (sidebar checks reals)
- Bug #6 — frontend hydration document attached. Eren 2 bugs encadenats:
  l'endpoint /history no retornava attached_document, i removeFilePreview()
  feia POST /clear-document destructiu cada switch de sessió.
- Bug #3 — MEM_SAVE-only response fallback. Quan el model emet només
  [MEM_SAVE: ...] sense text envoltant, ara genera 'Memòria desada: <fact>'
  perquè el bloc save s'executi i el frontend mostri confirmació.
- auto_save crida eliminada per HOMAD memoria v1 (2026-04-01) — manual
  MEM_SAVE only fins a Part 2.

TRACK B — Tray / Multi-instance / Packaging
- Bug #1 (PID file) compartit amb Track A
- Bug #2 — setproctitle a server i tray (server-nexe / nexe-tray a ps/Activity
  Monitor). Force Quit encara mostra Python perquè requereix CFBundleName via
  .app bundle real (deute v0.9.1).
- Bug #9 — menu polish: server-nexe.com duplicat substituït per
  '📖 Documentació' al main level (3 idiomes), website_item es manté al
  submenú Configuració.

TRACK C — UX cosmètic
- Bug #5 — slow_request middleware exclou /ui/upload (uploads naturalment
  triguen >1s i el log apareixia duplicat amb l'access log d'uvicorn).
- Bug #8 — 3 ⓘ visibles als checkboxes del sidebar de col·leccions amb
  tooltips als 3 idiomes (la infraestructura CSS/i18n ja existia).

Pytest D-1 final: 4424 passed, 0 failed, 35 skipped, 1 xfailed, 86% coverage
en 76.11s. Baseline pre-BUS era 4396. +28 tests nous, ZERO regressions.

Tests nous:
- tests/test_pid_file.py: 7 tests Bug #1
- tests/test_ingest_knowledge_idempotent.py: 8 tests F7 (3 classes)
- plugins/web_ui_module/tests/test_memory_helper_async.py: 1 test F1
- plugins/web_ui_module/tests/test_memory_delete.py: 7 tests F3+Bug#10
- plugins/web_ui_module/tests/test_mem_save_injection.py: 5 tests Bug #3

Out of scope (deute v0.9.1+):
- routes_chat.py 54KB decapitació general (deute formal P0)
- Bundle .app real amb py2app per CFBundleName (deute v0.9.1)
- Resums per capítol (Part 2 redisseny memory)
- RDBMS font de veritat + vector store reconstruïble (HOMAD memoria v1, Part 2)
jgoy-labs added a commit that referenced this pull request May 16, 2026
…rence

Symptom: after a restart the sidebar only listed .json sessions (.enc
were invisible), new sessions were persisted unencrypted even with
encryption enabled, and a reboot's .json->.enc migration could
overwrite an existing .enc belonging to a different conversation with
the same id (collision observed in the wild: "Hola Diana" overwritten
by "Hola Anna").

Root cause is a three-bug chain:

1. Loader early-init (core/loader/manifest_base._get_module):
   calls `instance._init_router()` immediately after `__init__`,
   *before* `initialize()` runs. The plugin must have its routable
   resources ready at construction time.

2. Plugin double-create (plugins/web_ui_module/module.py):
   `__init__` created a SessionManager() without crypto, then
   `initialize()` replaced self.session_manager with a new
   SessionManager(crypto_provider=crypto). Two instances, divergent
   state: #1 loaded only .json, #2 loaded .enc + migrated .json->.enc.

3. Router local reference (plugins/web_ui_module/api/routes.py):
   `create_router()` captured session_mgr = module_instance.session_manager
   into a local at creation time. Because of bug #1, that snapshot was
   the crypto-less #1. When `initialize()` later replaced the attribute,
   the router closures kept pointing at the stale #1.

Net effect: the UI saw #1 (only .json sessions), new sessions went
through #1 and were written unencrypted, and every reboot migrated
those .json files into .enc, overwriting whatever .enc already lived
under the same id.

Fix:

- module.py: remove the premature SessionManager() from __init__; build
  the one real SessionManager(crypto_provider=crypto) in initialize()
  using whatever crypto_provider is available (may be None). One
  instance, one truth, one load-from-disk.

- routes.py: introduce _SessionManagerProxy. Instead of capturing
  session_mgr = module_instance.session_manager, route closures hold
  the proxy and `__getattr__` re-reads module_instance.session_manager
  on every request. Late-binding, so the real SessionManager built in
  initialize() is always what the routes hit. Raises a clear RuntimeError
  if a route fires before initialize() has completed (rather than the
  opaque 'NoneType has no attribute list_sessions').

Verified: after restart, a single "Loaded N sessions from disk" per
reboot (was 2 before), all .enc sessions visible in the sidebar, new
sessions persisted as .enc.

Data note: no encrypted sessions were lost in the wild. All existing
.enc files decrypt cleanly with the current MEK; they were only made
invisible by the stale reference. The only silent loss was the
conversation overwritten by the colliding-id migration, which predates
this fix.
jgoy-labs added a commit that referenced this pull request May 16, 2026
…cache key only)

Onada 1 bandit audit — finding #1 (1/23). compute_system_hash() returns
an 8-char MD5 prefix used as a prefix-cache lookup key in inference
engines, not for authentication or integrity. Pass usedforsecurity=False
so the call:

  - Is correctly classified by bandit (B324 disappears)
  - Does not abort under FIPS-only Python builds where MD5-for-security
    is rejected at runtime

The truncated digest output is byte-identical to the legacy call.

Adds a regression test pinning compute_system_hash("hello") == "5d41402a"
so any future migration to blake2b/sha256 (which would silently
invalidate every existing prefix-cache entry across deployed installs)
breaks loudly at test time.

No behavioural change. Suite: 5126 → 5127 passed.
jgoy-labs added a commit that referenced this pull request May 16, 2026
…1 Cluster 7)

9 independent assignment/arg-type findings closed via minimal annotations
or casts (no behavioural change):

- installer/installer_catalog_data.py:450 — cast(str, value) after
  truthy guard (#1)
- core/config.py:115 — pre-declare found_path: Optional[Path] before
  if/else branch (#4)
- core/cli/client.py:46 — declare self._ssl_context: Optional[
  ssl.SSLContext] = None before populating in if-branch (#13)
- core/cli/output.py:33 — annotate module-level console: Any to bridge
  the dual-decl Console / FallbackConsole (#14)
- core/resources.py:110 — cast(Any, resource_path) so Path() accepts
  the importlib.resources Traversable that exposes __fspath__ (#17)
- core/resilience/circuit_breaker.py:76 — annotate __lock_loop:
  Optional[asyncio.AbstractEventLoop] = None (#18)
- core/dependencies.py:43-44 — annotate rate_limit_tracker /
  start_rate_limit_cleanup_task as Optional[Any] = None in the import
  fallback (#29, #30)
- core/cli/cli.py:215 — annotate found: list[tuple[str, Optional[str],
  list[int]]] = [] (#62)
jgoy-labs added a commit that referenced this pull request May 16, 2026
396 findings → 0. Estratègia validada Director+Turing (AGREE):
- too_many_parameters (91 fitxers): FastAPI DI, dataclass constructors, CLI
- too_many_branches (77 fitxers): parsers, FSMs, routers, CLI handlers
- file_too_long (29 fitxers): tests exhaustius + fitxers prod densos
- too_many_statements + function_too_complex (20 fitxers): workflows complets

Diferit explícit:
- routes_chat.py → Ticket #1 Factoria F1 (façana parcial aplicada BUS Wave 2)
- app.js → nexe-app Fase 1 UI redesign (Turing: whitelist preferit)

Suite: 4987 passed, 4 pre-existents sense canvis.
jgoy-labs added a commit that referenced this pull request May 16, 2026
…rence

Symptom: after a restart the sidebar only listed .json sessions (.enc
were invisible), new sessions were persisted unencrypted even with
encryption enabled, and a reboot's .json->.enc migration could
overwrite an existing .enc belonging to a different conversation with
the same id (collision observed in the wild: "Hola Diana" overwritten
by "Hola Anna").

Root cause is a three-bug chain:

1. Loader early-init (core/loader/manifest_base._get_module):
   calls `instance._init_router()` immediately after `__init__`,
   *before* `initialize()` runs. The plugin must have its routable
   resources ready at construction time.

2. Plugin double-create (plugins/web_ui_module/module.py):
   `__init__` created a SessionManager() without crypto, then
   `initialize()` replaced self.session_manager with a new
   SessionManager(crypto_provider=crypto). Two instances, divergent
   state: #1 loaded only .json, #2 loaded .enc + migrated .json->.enc.

3. Router local reference (plugins/web_ui_module/api/routes.py):
   `create_router()` captured session_mgr = module_instance.session_manager
   into a local at creation time. Because of bug #1, that snapshot was
   the crypto-less #1. When `initialize()` later replaced the attribute,
   the router closures kept pointing at the stale #1.

Net effect: the UI saw #1 (only .json sessions), new sessions went
through #1 and were written unencrypted, and every reboot migrated
those .json files into .enc, overwriting whatever .enc already lived
under the same id.

Fix:

- module.py: remove the premature SessionManager() from __init__; build
  the one real SessionManager(crypto_provider=crypto) in initialize()
  using whatever crypto_provider is available (may be None). One
  instance, one truth, one load-from-disk.

- routes.py: introduce _SessionManagerProxy. Instead of capturing
  session_mgr = module_instance.session_manager, route closures hold
  the proxy and `__getattr__` re-reads module_instance.session_manager
  on every request. Late-binding, so the real SessionManager built in
  initialize() is always what the routes hit. Raises a clear RuntimeError
  if a route fires before initialize() has completed (rather than the
  opaque 'NoneType has no attribute list_sessions').

Verified: after restart, a single "Loaded N sessions from disk" per
reboot (was 2 before), all .enc sessions visible in the sidebar, new
sessions persisted as .enc.

Data note: no encrypted sessions were lost in the wild. All existing
.enc files decrypt cleanly with the current MEK; they were only made
invisible by the stale reference. The only silent loss was the
conversation overwritten by the colliding-id migration, which predates
this fix.
jgoy-labs added a commit that referenced this pull request May 16, 2026
…cache key only)

Onada 1 bandit audit — finding #1 (1/23). compute_system_hash() returns
an 8-char MD5 prefix used as a prefix-cache lookup key in inference
engines, not for authentication or integrity. Pass usedforsecurity=False
so the call:

  - Is correctly classified by bandit (B324 disappears)
  - Does not abort under FIPS-only Python builds where MD5-for-security
    is rejected at runtime

The truncated digest output is byte-identical to the legacy call.

Adds a regression test pinning compute_system_hash("hello") == "5d41402a"
so any future migration to blake2b/sha256 (which would silently
invalidate every existing prefix-cache entry across deployed installs)
breaks loudly at test time.

No behavioural change. Suite: 5126 → 5127 passed.
jgoy-labs added a commit that referenced this pull request May 16, 2026
…1 Cluster 7)

9 independent assignment/arg-type findings closed via minimal annotations
or casts (no behavioural change):

- installer/installer_catalog_data.py:450 — cast(str, value) after
  truthy guard (#1)
- core/config.py:115 — pre-declare found_path: Optional[Path] before
  if/else branch (#4)
- core/cli/client.py:46 — declare self._ssl_context: Optional[
  ssl.SSLContext] = None before populating in if-branch (#13)
- core/cli/output.py:33 — annotate module-level console: Any to bridge
  the dual-decl Console / FallbackConsole (#14)
- core/resources.py:110 — cast(Any, resource_path) so Path() accepts
  the importlib.resources Traversable that exposes __fspath__ (#17)
- core/resilience/circuit_breaker.py:76 — annotate __lock_loop:
  Optional[asyncio.AbstractEventLoop] = None (#18)
- core/dependencies.py:43-44 — annotate rate_limit_tracker /
  start_rate_limit_cleanup_task as Optional[Any] = None in the import
  fallback (#29, #30)
- core/cli/cli.py:215 — annotate found: list[tuple[str, Optional[str],
  list[int]]] = [] (#62)
jgoy-labs added a commit that referenced this pull request May 16, 2026
396 findings → 0. Estratègia validada Director+Turing (AGREE):
- too_many_parameters (91 fitxers): FastAPI DI, dataclass constructors, CLI
- too_many_branches (77 fitxers): parsers, FSMs, routers, CLI handlers
- file_too_long (29 fitxers): tests exhaustius + fitxers prod densos
- too_many_statements + function_too_complex (20 fitxers): workflows complets

Diferit explícit:
- routes_chat.py → Ticket #1 Factoria F1 (façana parcial aplicada BUS Wave 2)
- app.js → nexe-app Fase 1 UI redesign (Turing: whitelist preferit)

Suite: 4987 passed, 4 pre-existents sense canvis.
jgoy-labs added a commit that referenced this pull request May 26, 2026
Synced from dev since
sync-20260519. Highlights:

Security (Tier S — security audit 96/100 PUSH OK):
- S2 XSS api_key UI sanitization (web_ui_module/ui/app.js)
- S3 /installer/finalize legacy gate via idempotency marker
- S9 path traversal reject in _resolve_model_path
- S10 SQLiteStore thread-safe via check_same_thread + RLock
- S1 health URL boot fix
- S6 fsync before close on atomic writes
- S7 CancelledError catch + mid-startup signal logging
- TOCTOU atomic on /installer/finalize idempotency marker
- basename guard applied to all model_id download pipelines

Bug fixes vespre (2026-05-21 sessions):
- Bug #5 fix(qdrant): retry-with-backoff lock acquisition at startup
- Bug #4 test(live): order slow LLM tests after fast ones + 10s cooldown
- Bug #2+#3 test(security): replace fragile inspect-based assertions
- Bug #1 chore(installer): document accepted CVEs + portable OSV checker
- Bug A fix(chat): remove data:[DONE] sentinel from text/plain stream
- Bug B iter-2 fix(chat): natural-language date phrase replaces "Now:" tech

UI productiva (F5.5 revert + post-revert):
- web_ui_module serves full UI in sidecar mode again
- footer i18n + version persistence
- frontend reads nexe_api_key from query string on first launch

F5.3 + F5.4 onboarding wizard:
- HTTP endpoints for wizard (installer/finalize, preflight, progress)
- NEXE_VERSION UI injection
- onboarding_state.py + optional HF token via macOS Keychain (never on disk)
- installer_constants.py + installer_progress.py + check_cves_osv.py

Hygiene:
- 113 hardcoded "Nexe 0.9" → version centralized (UI)
- /v1/system/* blocklist on sidecar URL guard
- ruff/mypy/pyright/typos cleanup (multiple commits)

54 files changed total. Nous tests: 17 (security regression sentinels +
onboarding wizard + qdrant + installer + sqlite concurrent + sqlite store
singleton).
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant