Gred In LabsDocumentación
← Documentación

Registro de cambios

Changelog

All notable changes to GIMO will be documented in this file.

The format is based on Keep a Changelog,

and this project adheres to Semantic Versioning.

Two changelogs coexist intentionally (clarified 2026-05-07):

  • This file (root CHANGELOG.md) — terse Keep-a-Changelog format in

English. Audience: external integrators, release notes, semver bumps.

Source of truth for shipped versions.

Spanish covering each rollout in detail (motivation, audit findings

addressed, files touched, sub-bugs discovered). Audience: contributors

tracing repo history. Sliced by date and feature cluster.

Bullet entries here should mirror the headline of the corresponding

narrative block in docs/CHANGELOG.md; both files should be updated

together for any release-grade change.

[Unreleased]

Added · Política de compresión híbrida por capacidad (validada) (2026-05-28)

Validación del eje "modelo fuerte" del context-engine (qwen3-coder:480b-cloud,

Ollama, EUR0): TOON vs JSON −4pp (ruido), L3 patch vs full +0pp. Es decir, los

modelos fuertes leen TOON/patches sin penalización, mientras los pequeños

degradan (−11/−60pp). → política gateada por capacidad, con la señal

canónica que GIMO ya tiene: ModelInventoryService.find_model(model).quality_tier

(determinista, sin tokens LLM, refinada por benchmarks + GICS · la misma que usa

ModelRouterService). No se inventa heurística paralela.

Tier bajo (≤2): TOON solo bajo presión, sin L3. Tier alto (≥4): TOON universal

(−37% tokens) + L3 en refetches (−68%). Documentado en

context_engine/README.md + eval/EVAL_RESULTS.md. Implementación de las piezas

de tier alto: diseñada + validada, pendiente de build. Evals ahora

parametrizables vía GIMO_EVAL_MODELS.

Changed · L3 (deltas JSON-Patch) rechazado por eval (2026-05-28)

Eval de task-success de L3 (context_engine/eval/l3_patch_eval.py) en los

modelos pequeños locales: mandar un JSON Patch del refetch en vez del resultado

completo ahorra 68% de tokens pero **degrada la precisión −19/−28pp global y

hasta −60pp en las filas que cambian** (el dato que el refetch quiere conocer).

Falla el gate justo donde habría presión de budget para aplicarlo. No se

implementa — diferido como L1. La disciplina eval-first evitó construir

refetch-detection + emisión de patches + cambio de protocolo por fe.

Added · Context Engine consolidado en main + L2 validado (2026-05-28)

Se rescató la rama local context-engine (stack de 8 capas de compresión de

contexto, nunca mergeada, 111 commits atrás) y se consolidó lo real y validado

en tools/gimo_server/context_engine/ (README decision-record, benchmark

reproducible, eval de task-success). No se portó el scaffolding especulativo de

L4-L8 (distill, don't design).

Validación de L2 (TOON) en los modelos pequeños locales de la mesh (Ollama,

EUR0): TOON degrada vs JSON completo (−11/−12pp en 2 de 3 modelos) → no aplicar

universal; pero gana al truncado lossy —la alternativa real cuando el contexto

no cabe— por +29 a +50pp. Regla validada y ya implementada: TOON solo bajo

presión de budget, nunca en cada call. Estado por capa: L0 hecho, L1 rechazado

(negativo), L2 hecho+validado, L3-L8 diseñados.

Changed · L0 · cache del prefijo de conversación en Anthropic (2026-05-28)

El adapter Anthropic ya cacheaba system + tools (cache_control: ephemeral),

pero el historial de mensajes —que crece cada turno— se re-mandaba sin cachear:

el mayor desperdicio en sesiones largas. Nuevo

_apply_conversation_cache_breakpoint pone un tercer breakpoint en el último

bloque del último mensaje, convirtiendo todo el prefijo en cache read (~10% del

coste) en los turnos siguientes. Usa 3 de los 4 breakpoints de Anthropic; se

salta conversaciones cortas (< PROMPT_CACHE_MIN_CHARS) donde el write

surcharge no amortiza. Lossless. Cierra el gap de L0, la mayor palanca de

ahorro según el benchmark del context-engine (caching domina el stack).

Added · TOON · compactación lossless de tool-results (reduce coste) (2026-05-28)

Port en Python del serializer TOON (Token-Oriented Object Notation) que Janus

ya usa para el árbol de accesibilidad: tools/gimo_server/utils/toon.py. Un

array JSON de objetos uniformes (search hits, listados, filas) se re-serializa

a name[N]{cols}: + filas tipo CSV — recorta ~30-60% de tokens sin pérdida de

información para el modelo (~44% medido en un search_text de 50 hits:

1253→711 tok). El agentic loop lo aplica como Pass 0 lossless en

_trim_messages_to_budget, antes de cualquier truncado lossy. Esto reduce

coste/tokens (no añade un LLM de resumen como Webwright). Solo convierte el

caso de alta ganancia y sin ambigüedad; el resto se deja intacto.

Changed · Sliding window preserva el rastro de turnos descartados (2026-05-28)

El agentic loop, al exceder el presupuesto de contexto en modelos constrained

(≤8192 tokens, p. ej. modelos locales de la mesh), descartaba los turnos

viejos a un hueco ciego — perdiendo qué ficheros se tocaron, qué selectores

/comandos funcionaron y qué errores ya se resolvieron. Ahora

_apply_sliding_window pliega un digest determinista del span descartado

(tools usadas, targets, errores vistos) dentro del system prompt. Sin llamada

LLM extra (cero coste/latencia), acotado por _DIGEST_RESERVE_TOKENS para que

re-añadirlo no rompa el presupuesto. Inspirado en la compactación de

microsoft/Webwright; diseñado como seam para un summarizer LLM futuro.

Added · docs.giltech.dev · documentación unificada multi-producto (2026-05-28)

Nuevo subdominio docs.giltech.dev: un único sitio de documentación para

todas las aplicaciones de Gred In Labs Technologies (GIMO, GICS, GISH, bio),

patrón SOTA multi-producto (dominio centralizado + sección por producto, como

HashiCorp developer.hashicorp.com o Stripe docs.stripe.com).

subdominio vía custom_domain = true. Sirve la landing con el grid de

productos y renderiza el CHANGELOG.md canónico en /changelog

(import de texto · single-source: editar el changelog y re-desplegar lo

refleja).

el enlace de /account/support ahora resuelve a docs.giltech.dev/changelog.

(Mintlify/Fumadocs) se monta encima cuando se elija.

Fixed · R29 Phase 3 clusters C2/C3/C4/C5 + helper script email banned (2026-05-25)

Cierre del backlog R29 Phase 3 que quedaba sin commitear desde el

2026-05-22 (C1 ya había aterrizado en 0a678b65). Cada fix tiene

análisis canónico previo en docs/audits/E2E_ROOT_CAUSE_ANALYSIS_20260522_R29.md.

Política release: 3 pasadas idénticas con 0 errores antes de commit.

R29C2 · POSIX path normalization en project_root()

+ check Path.exists() post-resolve. Cierra el bug donde

git rev-parse --show-toplevel desde PowerShell con un git MSYS/WSL

emite /home/shilo/... y Path().resolve() lo convierte en

C:\home\shilo\... (path inexistente · todo .exists() posterior

miente con False engañoso).

No flagea URI-style con drive letter (/C:/...) ni paths Windows

canónicos.

R29C3 · Provider healthy predicate · not_required state

auth_status in {"ok", "not_required"}. _auth_probe devuelve

"not_required" para providers locales con 4 heurísticas:

(1) requires_auth=False explícito en ProviderEntry,

(2) provider_type == "ollama",

(3) provider_id empieza por ollama/local-/lan-,

(4) base_url contiene localhost/127./0.0.0.0.

extendido para incluir "not_required".

CLI mientras OperatorStatusService lo marcaba como active_provider.

R29C4 · Token mismatch log downgrade WARNING → DEBUG

el log de "file ≠ env divergence" cuando file precedence resuelve

sin error. Post-R28 Change 1 la doctrina canónica es "file wins ·

env override opcional CI/container", por lo que la divergencia ya

no es anomalía operacional sino operación normal.

(level=WARNING NO captura el record, level=DEBUG SÍ).

R29C5 · Paginación /ops/threads

1-10000), offset (default 0), since_updated_ms (default None,

cursor temporal). Devuelve top-N por updated_at descending.

3 params como query string con validación FastAPI.

servidor en vez de trim client-side. Añade flag --offset para

consumo manual del API.

MCP tool result quota (1 MB) y hacía que gimo_threads_get no

retornase nada útil.

Verificación V1 · Janus/Aleph/Workspace snapshot

Pasada de verificación sobre los 3 subsistemas que MEMORY listaba

como "implementados" pero no se habían auditado esta sesión.

Confirmados con tests existentes: Janus (34/34), Aleph (36/36),

Workspace snapshot endpoint (8/8). No son stale memos · estado real

estable.

Helper · script email account-banned interactivo

public-account-banned por SMTP2GO sin disparar el flow de ban

(no toca Firestore, no escribe banned_hash, no revokeRefreshTokens).

+ confirmación con resumen antes de mandar.

--to/--name/--reason.

email original, QA puntual en producción. NO añade endpoint al

admin compose (deliberado: la plantilla terminal queda fuera del

surface admin público para evitar uso accidental).

Verificación gates · 3 pasadas idénticas con 0 errores:

Added · Unificación bloqueos pricing v2 + ADR-0012 controller (2026-05-25)

Sesión de cierre del backlog "bloqueos pricing v2" documentado en

memory/project_pricing_v2.md. Diagnóstico: 6 de 7 bloqueos eran

stale memos del 2026-05-22 que el código ya tenía resueltos

en sprints posteriores sin sincronizar nota. 1 era drift real entre

TS y Python (capability_registry_export faltaba en tier-defs). 1

es feature aún no implementada (GICS manual editing) y queda

deferred sin bloquear pricing.

ADR-0012 · Controller canonical semantics:

dos límites separados (cap tier vs invariante runtime "1 core

activo a la vez") y la matriz de mapeo entre los 3 vocabularios

(HardwareClass × DeviceRole → DeviceMode) que existían sin bridge

declarado.

tools/gimo_server/services/mesh/device_mapping.py (Python) con

funciones suggestedDeviceMode, controllerSuitability,

isCoreDeviceMode y DEVICE_MAPPING_MATRIX data-driven.

copy actualizado para distinguir "primer controller pinned en

Free" vs "controllers múltiples con traspaso en Operator+".

Badge visual de controllerSuitability por device sin bloquear.

CoreTransferSection con instrucciones reales de traspaso

(gimo stop A → gimo start B). Solo visible para tiers con

controllerTransferable: true y ≥2 controllers registrados.

maxRegisteredControllers, controllerTransferable para que

la UI gate las secciones premium correctamente.

Drift fixes:

capability_registry_export al enum LicenseFeature con doc

explicando qué exporta (capability profile per-model ×

per-task_type acumulado por CapabilityProfileService backed by

GICS keys). Activado en TIER_MESH, TIER_TEAM, TIER_ENTERPRISE.

Backend ya gateaí server-side con HTTP 402 desde sprint anterior;

ahora frontend lo refleja.

tests pre-existentes fallaban en HEAD porque conftest.py pinea

tier a free (cap=1) y el cap-check evalúa antes que el

invariante. Añadido monkeypatch del fixture para forzar tier

ilimitado (team) cuando el objetivo es probar el invariante

runtime aislado del cap. El cap tiene cobertura propia en

test_registered_controllers.py.

Tests añadidos (38 nuevos):

tests TS (cardinalidad, matriz canónica, parametric por fila ADR,

cobertura isCoreDeviceMode invariant).

paralelos · mirror exacto del set TS.

Bloqueos pricing v2 cerrados como stale memos (sin código):

Mesh, no 3 tiers comerciales. DeviceMode enum + doc canónico

ya lo cubren.

docs/archive/SUB_DELEGATION_PROTOCOL.md, cap

max_concurrent_child_runs purgado de tier-defs, no

comercializado. Código interno (child_run_service,

spawn_agents) sigue activo solo para casos del orchestrator.

max_registered_controllers y funcional desde sprint 2026-05-23

(journal:567). Tests + enforcement existen.

usa behavior_confidence_full, backend conserva /ops/trust/*

URLs para no romper MCP/CLI. Documentado en

behavior_confidence.py:3-9.

Workspace pairing es mecanismo técnico intra-cuenta (pairing

code para device del MISMO user). Multi-cuenta = Team tier

features (sso, license_pooling, shared_skills_repo) ya

vendido. Cosas distintas.

(solo GET read-only en gics_patterns_router.py). Definir tier

cuando se construya · no bloquea pricing v2 actual.

Verificación gates · 3 pasadas idénticas con 0 errores:

device_mapping +4 invariant rescatados)

Fixed · Ronda 8 · SAST + dependencies + secrets sweep (2026-05-25)

Pasada con Semgrep (TS+Next+OWASP+JS+Node rulesets) + npm audit + secrets

sweep manual. Suite sigue 350/350 green, tsc clean.

(Server Components DoS). Non-breaking, range vulnerable >=16.0.0 <16.2.5.

0 vulnerabilities (12 → 10 → 8 → 0). Overrides aplicados:

postcss@^8.5.10 (cierra GHSA-7fh5-64p2-3v2j XSS via </style>),

uuid@^11.1.1 (buffer bounds), brace-expansion@^5.0.6 (numeric DoS),

protobufjs@^7.6.0 (recursive JSON DoS). Tests 350/350 verdes tras

overrides · no regression upstream. Next.js 16.2.6 sigue pinneando

postcss@8.4.31 internamente, el override force-dedup a 8.5.15.

scripts/smoke_synth_dispute.py (era el stripe listen local, no

production, pero commited en repo). Movido a env

STRIPE_WEBHOOK_SECRET / STRIPE_LISTEN_SECRET con fail-closed.

en auth/callback/page.tsx:135 (open-redirect / tainted-redirect)

son false positives porque el sanitizer resolveSafeReturn

(líneas 46-60) valida same-origin + path-relativo + fallback default,

pero Semgrep no traza taint cross-function. Marcados con nosemgrep

comments justificados. Auditoría manual de los otros 30+ redirect

call-sites del repo confirma sanitización correcta (regex param

validation, same-origin clones, env-configured bases).

error" + 46 timeouts + 18 syntax errors sobre 1k+ archivos. Probable

bug Semgrep 1.161.0 en Windows con paths largos. La cobertura real

fue ~30% del repo. Estado: aceptable como baseline · próxima pasada

conviene Linux runner o CodeQL para cobertura completa.

Added · CI continuo SAST + npm audit (R8.5 · 2026-05-25)

.github/workflows/ci.yml extendido para que cada push a main y cada

PR ejecute las verificaciones que la ronda 8 hizo a mano:

p/javascript, p/nodejs a los rulesets que ya corrían (p/python,

p/typescript, custom). Severity ERROR gate-blocking; WARNING como

artifact informativo. Linux runner evita el bug de encoding cp1252

que vimos en Windows · cobertura completa del repo.

- GATE bloqueante con --audit-level=high · falla CI si hay

vulnerabilidades critical o high.

- Step informativo separado lista moderate/low con título legible y

sube el JSON como artifact (npm-audit-apps-web).

- Override path documentado: para risk-accept temporal de un HIGH

sin fix patch-level, bajar a --audit-level=critical Y dejar nota

en CHANGELOG (CVE id + razón no-explotable + bloqueo + deadline).

uuid / brace-expansion / protobufjs en chain firebase-admin sin

esperar bump major upstream.

Este bloque cierra el feedback loop: cualquier CVE futuro o secret

nuevo aterrizado en el repo dispara CI antes de merge en main · no más

"esperar a la próxima ronda manual".

Fixed · Ronda 7 adversarial (2026-05-25)

Auditoría adicional con 4 agentes ortogonales (regression, test

coverage, doc drift, edge cases concurrency/legal). 9 hallazgos

resueltos, 0 pospuestos. Suite pasa 350/350 (+27 tests nuevos).

el user tiene deletionCompletedAt/pendingDeletionAt. Sin esto un

user mid-deletion podía extraer una key nueva durante la ventana.

profile y /api/account/undo-deletion` para evitar caché stale-auth

bajo ISR.

cron drip-tick skipean usuarios con pendingDeletionAt/

deletionCompletedAt. Antes el user que pedía cerrar podía recibir

cadena dunning D1/D4 o drip day3/day7 durante los 30d.

encuentra subs canceladas SIN marker `cancelled_by="account_deletion_

request" levanta stripe_warn_unreverted` y el redirect del undo va

a ?status=ok_check_portal con CTA al Portal de facturación.

con tabla, aclarando que el cron emite ≥17 queries (audit×2,

licenses×2, webauthn_credentials sub+parent) sobre 16 colecciones.

pickFingerprintComponents (5), ban-hash (8), upsertLicenseDoc

preserve-consent (4), auth/session con 3 errores nuevos de

upsertUserOnLogin. Los tests **descubrieron y fixearon un bug

real**: safeEqualString usaba .length (UTF-16 code units) en vez

de Buffer.byteLength, lo que hacía que comparaciones con UTF-8

multi-byte ("café" vs "cafe") lanzaran RangeError en lugar de

devolver false limpio.

defense-in-depth para processed_events, audit, dunning_state,

refresh_tokens, revoked_tokens (todas server-only).

está en ventana de cierre · evita drift entre confirmación al user

y purga posterior del tombstone.

fetch` para que el operador entienda el margen 2× anti-tombstone.

Added · GDPR self-service + STRIDE hardening + audit canónico (2026-05-24)

Continuación del bloque legal (commits d8b389a9 / 526f3d70 /

ed62f02c) cerrando 50+ hallazgos de 6 rondas de auditoría adversarial

(initial 4-agent + 5 rondas críticas). Estado final: 323/323 tests

green, tsc clean, zero amenazas explotables.

Added · GDPR Art. 17 self-service closure

cookie).

colecciones · ver docs/runbooks/account-deletion-ops.md y

ADR-0010).

de /account/layout.tsx para no caer bajo auth gate).

Added · Consent persistido v2 (LSSI-CE + Art. 7 RGPD)

(terms_version, privacy_version, immediate_start, accepted_at).

previo (hasConsentMetadata gate evita borrar con objeto vacío).

signups Free (users/{uid}.termsAcceptedVersion etc.).

Added · Pricing v2 yearly + Team seat-addon

segundo line item Stripe (cap 47 extra = 50 total).

detecta env vars y oculta toggles si no configurados).

Added · Audit canónico + 6 admin destructivos

user-triggered + 6 admin destructivos, ambos buckets warn/critical).

reactivate, cold-room/issue, admin/bonds/revoke).

admin_license_revoked, admin_license_created,

admin_user_approved, admin_email_broadcast,

admin_test_email_sent) — Art. 30 RGPD + forensics SOC2.

Added · Helpers

con checkRevoked=true siempre).

a 3 crons + orchestrator/verify + webhooks).

canónicas antes de persistir).

Added · Docs

Changed · Security hardening (STRIDE)

XFF spoofing sin proxy de confianza).

billing/portal) con rate-limit + length caps.

conservan).

Changed · UI + flows

Regenerate con panel rawKey one-shot + copy clipboard).

GIMO_DOMAINS.root).

cuando session devuelve 400.

mailto a privacy@/disputes@/billing@/support@.

noindex si faltan env vars LEGAL_RESPONSIBLE_*`.

Changed · Schema + indexing

licId == auth.uid); subscriptions cubre v1+v2.

_TEAM_SEAT_ADDON_YEARLY.

trae (anti-overwrite).

Changed · Docs operacionales

(env names OPERATOR/MESH/TEAM_BASE/TEAM_SEAT_ADDON + yearly).

(consent, pause_history, enterprise tier, internal_dev no-lifetime).

previo).

webauthn/admin).

Fixed · 50+ hallazgos de 6 rondas auditoría

*Críticos*:

(gate hasConsentMetadata).

TEAM_SEAT_ADDON_YEARLY → tier null → v2 no escrito.

a /undo-result raíz fuera de /account/layout.tsx.

features Mesh sin janus_dev_internal (staff sin Janus). Ahora

isLifetime: false + lifetime UI prioriza v2.is_lifetime.

tombstone si user reintentaba → 410 con guard explícito.

re-fetcha cliente) → panel destacado one-shot con copy + warning.

gimo.giltech.dev.

/undo-result fuera de auth gate.

*Serios*:

v2 → ownerUid resolver cubre v1+v2.

Removed

(huérfanos solo consumidos por su propio test).

2026-05-21).

Manual operator follow-ups (post-deploy):

1. Cloud Scheduler process-deletions-cron cada 6h con

X-CloudScheduler-Token (ver runbook).

2. Stripe Dashboard crear price IDs yearly: OPERATOR_YEARLY,

MESH_YEARLY, TEAM_BASE_YEARLY, TEAM_SEAT_ADDON,

TEAM_SEAT_ADDON_YEARLY → env vars correspondientes en Cloud Build.

3. Env vars LEGAL_RESPONSIBLE_NAME/NIF/ADDRESS para desbloquear las

3 páginas legales (anti-leak banner).

4. Índices compuestos Firestore opcionales para export ordenado:

audit (uid asc, timestamp desc), `sent_emails (uid asc, sentAt

desc), email_events (uid asc, receivedAt desc)`.

5. Cloudflare Email Routing 7 alias (`support/billing/privacy/disputes/

legal/security/hello@giltech.dev`).

Changed · URL centralization + cloudbuild internal_key drop + migrate-licenses test fixture (2026-05-22)

Cleanup pass surfacing three artifacts that drifted out of sync during

the prior B-blocks and T1-CRIT/MAJ sprints. No behavioral change in

production; deuda invisible removed.

exports GIMO_DOMAINS (root / auth / orch / docs / status /

downloads) resolvable at build time via NEXT_PUBLIC_GIMO_*_URL env

vars. 33 hardcoded https://*.giltech.dev literals across 22 source

files migrated to constant refs (account components, login, emails,

webauthn recovery, unsubscribe builder, admin test-transactional,

webauthn config). Account home also collapses the duplicate APK

download href to reuse APK_MESH_URL from the downloads manifest. One

edit to rotate the domain (or override per env for staging /

preview).

in apps/web/cloudbuild.yaml. Runtime call sites already prefer the

bundle via getHmacKey("internal_key"); bundle has been stable since

F1 sprint. Manual destroy of the GCP secret gimo-landing-internal-key

is pending (operator action).

migration-dryrun.test.ts was modeling the pre-pagination .get()`

shape of migrate-licenses.ts; the script was refactored in commit

f742ac4c (perf,refactor) to paginate via `orderBy(documentId()).

limit().startAfter().get() + batch read via adminDb.getAll(...refs)`

+ subscriptions batched via where("licenseId", "in", chunk), leaving

3 tests red. Fixture rewritten to model the new chain (chainable mock,

paginated slices keyed by docId cursor, batch refs stamped with

_uid for getAll, in operator on subscription mock). Global

@/lib/firebase-admin mock in apps/web/src/__tests__/setup.ts

extended with startAfter chain, doc().create(), and

adminDb.getAll() for any future test that hits the paginated path.

Verification:

Commit e3c3a292.

Added · B2/B3/B5/B6 sprint · onboarding, ask-and-wait, features potentes (2026-05-22)

Multi-block sprint post-research masiva (10 agentes producto/UX/sec/cost).

component con 6 steps client (StepWhatIsGimo, StepYourMesh,

StepCoreLocation, StepPlanFlexibility, StepChooseDevice,

StepDownload); explica que en Free el Core se pinned al primer device

y solo paid permite transferir. Backend enforcement

max_registered_controllers por tier en

tools/gimo_server/services/licensing/registered_controllers.py +

integración en mesh/enrollment.py::ensure_core_slot_available.

cuando un agente encuentra una piedra trivial puede pausar el run, crear

un context_request, emitir SSE y esperar la directiva del usuario en

lugar de abortar y forzar relanzamiento (que quema tokens). Wrapper

AgenticLoopService.pause_and_ask_user, extensión

OpsResumeRunRequest.context_directive, nuevo campo

OpsRun.pending_context_request_id, UI agent-question-card.tsx en

orchestrator_ui, checkpoint hook on pause/resume, tests E2E

ask→pause→resume.

- Trust DashboardBehavior Confidence rename + gating real en

PUT /trust/circuit-breaker/{dim} (mesh+) y GET /trust/ids/insights

(operator+) via nuevo helper behavior_confidence.py (HTTP 402

estructurado con CTA payload).

- Shared Skills Repo backend (Firestore

organizations/{orgId}/shared_skills/{skillId}) — 3 endpoints

GET/POST/POST install bajo /api/shared-skills/* con gate tier

team+/enterprise+/internal_dev+. 4 tests.

- Capability Registry exportGET /ops/capabilities/export que

devuelve CapabilityProfile completo, gating mesh+. Helper

capability_registry.py + método CapabilityProfileService.export_all().

- Aleph audit en skillsAlephService.record_skill_event + wire

en install/publish/execute con metadata por acción.

tailscale_auth_key en MeshDeviceInfo + doc

docs/guides/byo-tailscale.md + ADR-0008 (mesh access sin infra

GIMO).

externo + SSO + license pooling + shared_skills_repo);

max_evals_datasets: CAP_UNLIMITED en todos los tiers (no se paywallea

Evals · regla retention engine); rename

reliability_dashboard_fullbehavior_confidence_full (consistente

con backend); max_controllersmax_registered_controllers (la

semántica es "instalaciones controller-capable", no "controllers

concurrentes" — siempre 1 activo por INV-Core-1).

max_registered_controllers=3 no unlimited; audit_anchor_external

solo en Enterprise).

Fixed · Bug pre-existente en handoff Aleph audit (2026-05-22)

handoff_router._aleph_emit llamaba `AlephService.integrate(agent_id=...,

target=..., category=..., payload=...)` pero la signature real es

integrate(ws: AgentWorkspace, target_ref: str). El TypeError resultante

se tragaba en except Exception: pass, así que los 4 eventos de PeerHandoff

(requested/accepted/completed/rejected) **nunca llegaron al ledger

Aleph**. Fix:

(sibling de record_skill_event, mismo patrón GICS emit).

al fallo (antes era pass silente, que es lo que ocultó el bug

durante meses).

Changed · Token discipline doctrine (2026-05-22)

Tras investigación de técnicas de ahorro de tokens en Claude Code (8

fuentes 2026 + docs oficiales):

obligatorias en sesiones >5 archivos o >30 min.

~/.claude/projects/<proj>/memory/reference_token_savings.md con las 3

técnicas de mayor ROI (prompt caching, subagentes para verbose,

Read/Grep con límites) + tabla de antipatrones.

para auditar sesión activa contra patrones malos en 4 ejes.

Changed · Pricing v2 · rename Solo→Operator, Pro→Mesh + waitlist (2026-05-22)

Post-multi-agent analysis (3 specialists + synthesizer/devil's advocate

with corrected sector framing — GIMO = multi-agent orchestrator control

plane, not AI tool):

differentiators (control plane operator + distributed mesh) instead of

generic "Solo/Pro" SaaS noise. LicenseTier union, TIER_DEFS,

validation, all consumers updated.

Aligned to multi-agent sector (LangSmith $39/seat, Letta $20-99,

AgentOps $20-99, Dust $29) — not to AI tools (Cursor/Copilot).

Replaces additive €15/seat over Pro that forced mental math (Linear /

Notion / Vercel pattern).

surfaces the only differentiator no OSS multi-agent framework

(AutoGen, LangGraph, CrewAI, Letta) ships. Backend audit chain has

always been server-wide (ENABLE_AUDIT_CHAIN_V2); the flag makes the

inclusion visible in /pricing without changing runtime behavior.

410 Gone in /api/billing/checkout POST + 302 redirect on GET.

UI card removed from /pricing. Stripe product founding_lifetime_v1

remains in test mode pending manual archive (no customers

pre-revenue). Replacement Founding Annual €119 cap 100 under

evaluation contingent on ≥100 emails in 6-week waitlist; otherwise

plan B (no founder tier).

sendIfPermitted (template public-waitlist). Fail-safe: signup

succeeds even if email fails. Dedupe silent (no spam on re-signup).

(subdued, source-tagged). Tone confident ("te avisaremos cuando tu

acceso esté autorizado"), no counter visible (anti growth-hacky in

dev culture).

7d/24h velocity, source breakdown, last 20 signups, % to threshold)

+ visual dashboard at /account/admin/waitlist with progress bar to

the kill-switch threshold (100).

agentes IA" + sub explicit about BYO inference. Audit chain

dedicated card. Tier comparison table vs AutoGen+DIY / LangSmith

Cloud / Cursor Pro (10 capabilities scored). Enterprise card

"Contactar" as Ariely decoy + compliance lead capture.

(operator_monthly_v2, operator_yearly_v2, mesh_monthly_v2,

mesh_yearly_v2, team_base_v2, team_seat_addon_v2). Legacy

STRIPE_PRICE_SOLO_*/PRO_* env aliases retained in stripe-price-map.ts

fallback for safe transition.

with new tier names + new backward-compat alias coverage.

Files touched: `lib/licensing/{tier-defs,schema,stripe-price-map,index,

__tests__/*}.ts, app/api/{billing/checkout,waitlist,admin/waitlist,

webhooks/stripe}/route.ts, app/pricing/page.tsx`,

components/pricing/WaitlistForm.tsx, app/account/admin/waitlist/page.tsx,

scripts/{create-stripe-products,migrate-licenses}.ts,

tools/gimo_server/security/audit_emitter.py (docstring only).

See ADR-0007 for the full rationale.

Added · Auth/license/subscription productization sprint (2026-05-21)

Largest single-day cluster to date: 14 commits on main + 3 on

context-engine, ~6000 LOC added, 353 tests passing. Productizes

auth/license/subscription end-to-end after a multi-block research

phase (35 agents · 10 innovation candidates × 5 vulnerabilities × 4 UX

patterns × 3 cost estimates) and a separate context-engineering

research (4 deeper agents on academic SOTA, industry landscape,

specialized formats, and multi-turn delta encoding).

Security · P0 + supply-chain

the confused-deputy hole (CVE-2018-0114 pattern) in 19 CLI endpoints.

Pre-Capa-1 legacy bonds without aud are rejected on deploy; users

re-bond via gimo login --web. (fix(security): B1 · 8 regression

tests in test_bond_issuer.py + test_credential_facade.py.)

atomic Firestore transactions closes the C-Squad2 PoC#1 replay

window across Stripe, Resend, SMTP2GO. SMTP2GO event-id derived from

sha256(body) since no per-event id is issued. 12 unit tests including

race-condition with Promise.all. Firestore TTL policy on

ttlUntil must be enabled manually (gcloud command in helper header).

releases. Coexists with legacy release.yml. Public Rekor log;

verification documented in docs/security/VERIFY_RELEASES.md.

enforce_method_scope downgrade to read-only for

cancelled/expired/revoked/paused statuses (Innov 7 backbone).

Default "active" preserves backward compatibility. 18 new tests.

orchestrator-cert (signs device certs) and seat-proof (signs

seat_proofs), separate KV namespaces, separate signing keys. €0 within

CF free tier. Not deployed; ready for wrangler deploy.

(Tailscale TKA style). Behind ENABLE_AUDIT_CHAIN_V2 feature flag,

zero regression when off. Notary hook for E5 (deferred). 22 new tests

· full security suite 238/238 green. See docs/architecture/AUDIT_CHAIN.md.

Licensing · schema v2 with tier+caps+features

pro, team, internal_dev), hard caps + feature flags, Janus locked to

0 sessions across all commercial tiers per the Janus dev-only

decision. Migration script migrate-licenses.ts with dry-run by

default, v1→v2 idempotent transformer. 81 new tests. See

apps/web/docs/LICENSE_SCHEMA.md and ADR-0005.

buildLicenseDoc(tier, ...) instead of hardcoded `plan="standard"

+max=2. Mapping price_id → tier via STRIPE_PRICE_MAP` env JSON.

Stripe status → LicenseStatusV2 mapping with past_due → active

grace and pause_collection != null → paused for F1.

Dual-write v1+v2 during coexistence. Fire-and-forget seat-proof

worker call with HMAC auth.

card from TIER_DEFS. Single source of truth · changing a cap in

tier-defs.ts updates the page automatically.

mesh_node caps. Apple Music / Spotify TV style "device limit

reached" message with device labels + last-seen. 60-day inactivity

GC helper. 18 tests.

Identity · OAuth + WebAuthn

for cross-subdomain support. @simplewebauthn/server v13.

Step-up cookie HMAC-signed with 30-day freshness. Recovery flow with

15-min single-use magic link. StepUpGate overlay enforces

registration on first sign-in. New Firestore collections

webauthn_credentials/, webauthn_challenges/,

webauthn_recovery/. 9 new tests. Docs in apps/web/docs/WEBAUTHN.md.

(apps/auth-hosting) alongside Google. Affects entire Gred In Labs

ecosystem (GIMO + GICS + GISH + Mesh). Scopes: read:user,

user:email. buildGithubProvider + startGithubSignIn mirror

Google's API. ProviderDivider between buttons. i18n keys in es/en.

GET /api/account/devices + POST /api/account/devices/[id]/revoke

with ownership chain check (activation → license → user uid).

Lockout guard refuses to revoke the only active device. Current-

device confirmation requires typed REVOKE. UI at /account/devices

with Apple Music / Spotify TV bulk-revoke pattern. NO email

magic-links (anti-phishing per P3 research).

Android · legal scope

internal (dev-only) product flavors. Removes the

AccessibilityService + Janus integration from the public APK and

isolates them to the internal flavor. Janus is dev-tool only per

ADR-0006. All 4 build targets pass assembleX (commercial+internal

× debug+test). See apps/android/gimomesh/FLAVORS.md.

Innovations · retention

pendiente (Stripe webhook → status flip cuando C2 está deployado;

rate-limit cancelled role per C-Squad4 recommendation).

Context Engine · separate context-engine branch (not in main)

Honest disclosure: "60-70% combined savings" is partially validated

through integrated tokenization measurement, but the cache discount

itself is MODELED (60% mid-range from arXiv 2601.06007 "Don't Break

the Cache") not measured against live Anthropic/OpenAI API.

Benchmark v2 covers 3 workloads, 4 layers (L0/L1/L2/L3), sensitivity

bands at 41%/60%/80% cache discounts:

* Tabular-heavy short (10×20t): -73.4% at mid cache

* Mixed distribution (10×20t): -70.2% at mid cache

* Long session (3×100t): -77.8% at mid cache

* Surprise: L1 lazy tools is NEGATIVE (-2 to -4%) once L0 is in

place. Recommendation: skip L1.

* L3 JSON Patch deltas adds +4-8pp on top of L0+L2.

See tools/gimo_server/context_engine/benchmark/REPORT_v2.md for the

go/no-go criteria before merging anything to main.

Pending (not shipped, documented as known follow-ups)

on C1 ✅ — unblocked)

benchmark validation per the REPORT_v2.md go/no-go criteria)

Pre-deploy manual checklist for the founder (see journal entry

for the full step-by-step):

1. Firebase Console — enable GitHub provider (D2)

2. Stripe Dashboard — create real price IDs (Solo €9, Pro €19,

Team €15/seat, Founding Lifetime €129) + set STRIPE_PRICE_MAP

3. GCP Firestore — enable TTL policy on processed_events.ttlUntil (B2)

4. Cloudflare — generate 2 Ed25519 keypairs + 2 KV namespaces +

wrangler secret put × 2 + deploy 2 workers (E3)

5. Set SEAT_PROOF_WORKER_URL + SEAT_PROOF_WORKER_HMAC envs (C2)

6. Run apps/web/scripts/migrate-licenses.ts dry-run, then --commit

7. Optional: export ENABLE_AUDIT_CHAIN_V2=1 to activate E4 chain

Related: ADR-0005 (Licensing schema v2), ADR-0006 (Janus dev-only).

Full narrative + decision log: docs/journal/2026-05.md § entry

2026-05-21.

Added · Email infrastructure expansion (2026-05-20, deadend 1h session)

End-to-end email automation for SaaS B2B 1.0 paying-user readiness. Before

this session, the 4 transactional templates existed in code but were

NEVER wired to product events; the unsub URL pointed to a 404; and

billing webhook handlers updated DB state without sending any user-facing

email. After this session: 11 templates total (4 existing + 4 billing

new + 3 lifecycle drip new), full lifecycle wiring, CAN-SPAM + GDPR

compliance, bounce/complaint handling, open/click tracking. See

ADR 0004 for the

architectural rationale.

{doc, isNewUser}; /api/auth/session fires

buildWelcomeEmail via sendIfPermitted("transactional") on first

sign-up. Idempotent via welcomeSentAt field. Failure NEVER blocks

login (try/catch silent + warn log).

POST /api/admin/users/[uid]/approve formalizes what was previously

manual Firestore-console editing. Idempotent (alreadyApproved if

re-clicked). Fires buildEarlyAccessApprovedEmail automatically.

/account/admin page lists approved==false users (limit 100) with

server-action approve button. Audit logged in sent_emails.

4 categories (transactional locked, product, lifecycle, marketing).

GET/POST /api/account/notifications/preferences. Public

/unsubscribe?uid&category&token page accepts HMAC-SHA256 tokens

(verified timing-safe) and applies opt-out without requiring login —

CAN-SPAM compliant one-click unsub. Tokens never expire (regulation

requires permanent validity).

wired into the webhook handlers: public-payment-receipt (on

invoice.paid), public-payment-failed (on invoice.payment_failed),

public-trial-ending (on customer.subscription.trial_will_end

new event handler), public-subscription-ended (on

customer.subscription.deleted). All via sendIfPermitted; failures

log + continue (NEVER throw to Stripe — would duplicate emails on

retry). All audited in sent_emails.

POST /api/webhooks/resend (svix-signature HMAC verification) and

POST /api/webhooks/smtp2go (X-SMTP2GO-Signature HMAC). On

email.bounced / email.complained / bounce / spam

update users.emailStatus; sendIfPermitted then auto-skips

future automation to that recipient (preserves DKIM reputation).

Fail-closed (503) if RESEND_WEBHOOK_SECRET /

SMTP2GO_WEBHOOK_SECRET missing.

POST /api/cron/drip-tick (auth X-CloudScheduler-Token vs

CRON_TOKEN). Query users in ±12h window around each day target,

filtered by lifecycleStage to ensure each user receives day3 then

day7 in order, never both at once. Batch limit 50 per tick (Cloud

Run 60s timeout safety). Respects emailPreferences.lifecycle.

Three new templates: public-drip-day0/3/7. day0 is manual-only;

day3/day7 fire automatically via cron.

payload now sets X-Track-Opens/X-Track-Clicks headers;

SMTP2GO payload sets track_opens/track_clicks: true. Provider

webhooks append events to email_events collection with

event, email_id, recipient, subject, link, timestamp.

lib/email/send-policy.ts enforcing preferences + bounce/complaint

status. Transactional category bypasses opt-out (CAN-SPAM/GDPR

permit billing/account comms unilaterally). Used by all automatic

email sites (session, stripe webhook, approve, drip-tick).

/account/admin/emails server-rendered view of sent_emails

last 30 days (limit 200). 4 metric blocks (by status, provider,

source, template) + table. No interactive filters (scope minimum).

installed at ~/.claude/skills/deadend/SKILL.md. First end-to-end

use was this session (/deadend 1h). Encodes the workflow:

plan-mode first, post shutdown command before approval, fire

shutdown /s /t <secs> as first Bash after approval, conservative

defaults (no --no-verify, no force-push), mandatory CHANGELOG +

journal + ADR before budget end. Final report template + risk

catalog. Lives in user home, not repo.

Added · Compositor expansion (earlier in same session)

/account/compose now has two independent toggles: entity (Gred In

Labs / GIMO / GICS / GISH · changes header logo + descriptor +

subject prefix) and sender (josecarlos@ / equipo@ / gimo@ /

gics@ / gish@ / hola@ / soporte@ · changes the actual From:

alias). Default sender per entity; manual override available with

reset-to-default button.

end of compositor with a button that fires

POST /api/admin/test-transactional directly (no DevTools, no

fetch snippets). Result shown inline with provider, From header,

and Gmail "signed-by" verification hint.

giltech.dev; SMTP2GO transactional lane on mail.giltech.dev).

Split físico real with isolated DKIM. Documented in

ADR 0004.

_spf.resend.com; DMARC raised from p=nonep=quarantine pct=10

with reports to dmarc@. New DMARC record on _dmarc.mail.giltech.dev.

3 SMTP2GO CNAMEs (em650505.mail, s650505._domainkey.mail,

link.mail).

firstname@company outperforms generic aliases (hola@, team@) by

~6.8× reply rate in B2B outreach studies. Compositor default for

Gred In Labs entity.

cif: null without rendering an empty CIF line.

Manual follow-ups required (post-deploy)

These commands must be run manually by the operator with appropriate

credentials; they are NOT executed by code:

1. Create Cloud Scheduler job for drip-tick cron:

```bash

gcloud scheduler jobs create http gimo-drip-tick \

--location=europe-west1 \

--schedule="0 * * * *" \

--time-zone="Europe/Madrid" \

--uri=https://gimo.giltech.dev/api/cron/drip-tick \

--http-method=POST \

--headers="X-CloudScheduler-Token=$(gcloud secrets versions access latest --secret=gimo-cron-token)"

```

Requires secret gimo-cron-token in Secret Manager (create with

gcloud secrets create gimo-cron-token --replication-policy=automatic,

then add version with a random 32-byte hex value).

2. Configure Resend webhook at https://resend.com/webhooks:

- Endpoint: https://gimo.giltech.dev/api/webhooks/resend

- Events: email.bounced, email.complained, email.opened,

email.clicked, email.delivered

- Copy the signing secret → store as RESEND_WEBHOOK_SECRET in

Secret Manager + add to cloudbuild.yaml --update-secrets.

3. Configure SMTP2GO webhook at https://app.smtp2go.com/settings/webhooks:

- Endpoint: https://gimo.giltech.dev/api/webhooks/smtp2go

- Events: bounce, hardbounce, spam, open, click, processed

- Copy the signing secret → store as SMTP2GO_WEBHOOK_SECRET in

Secret Manager + add to cloudbuild.yaml --update-secrets.

4. Add CRON_TOKEN, RESEND_WEBHOOK_SECRET, SMTP2GO_WEBHOOK_SECRET

to apps/web/cloudbuild.yaml --update-secrets list. Without these

the corresponding endpoints return 503 webhook_not_configured /

cron_not_configured (fail-closed by design — does not silently

accept unauthenticated calls).

5. Smoke-test post-deploy:

- Click "Disparar smoke test transaccional" in /account/compose

receive welcome email in admin Gmail → check headers contain

signed-by mail.giltech.dev (proves DKIM split physical works).

- Visit /account/notifications → toggle marketing → check

Firestore users/{uid}.emailPreferences.marketing == false.

- Visit /account/admin → approve a pending early-access user →

check they receive the email.

Acknowledged debt

(welcome trigger, drip-tick, webhooks, approve, preferences).

Reason: 1h budget. The Stripe webhook had existing tests covering

the DB updates; those still pass. Test design for new endpoints

(especially HMAC signature verification fixtures) needs the user.

docs. sendIfPermitted treats undefined prefs as conservative

defaults (transactional/product/lifecycle on, marketing off), so

existing users get the right behaviour lazily on first email.

Backfill optional if you want consistent docs.

follow-ups #1. Conservative decision (no headless gcloud creds in

agent context).

search, pagination, CSV export). Scope intentional (T1.4 minimum).

Iterate when needed.

Removed

Fixed

Changed

[0.9.2-serverbond] - 2026-03-30

Added

ServerBond Architecture
CLI Commands
Server Endpoints

Changed

CLI Core
Server Core

Fixed

Security

Documentation

Testing

Technical Details

Migration Notes

---

[0.9.1] - Prior Release

Previous features and changes (no changelog maintained prior to 0.9.2).