📋 Contrato de Ingeniería Operaciones · PMO ✓ Listo para Architecture Review ⚠ 3 preguntas abiertas

AgentFlow Credit Limits

Plan de Capacidad de Producto · Novalabs SaaS · Funcionalidad enterprise

CAP-2026-001 · dd166b1e
Cliente Novalabs SaaS
Owner producto Ana Torres (CPO)
Servicios afectados agent_runner · billing_service · DB
Fecha 18 Jun 2026
Estado ⚠ Pendiente arch. review

Los workspace admins de Novalabs (plataforma B2B de automatización con IA) tendrán un presupuesto de créditos de IA por workspace que garantiza la detención limpia de cualquier agente cuando el saldo se agote. Después de este cambio: (1) ningún pipeline podrá incurrir en créditos negativos; (2) los admins pueden adquirir créditos extra en autoservicio vía Stripe sin contactar a soporte; (3) los developers pueden definir un límite máximo por agente individual como cota dentro del límite del workspace. El outcome cambia porque hoy un agente runaway puede consumir créditos ilimitados — con esta capacidad el gasto queda acotado, predecible y auditado por workspace.

📊

Métricas de Éxito

0
Casos de overspend en producción
≡ créditos negativos = 0
<500ms
Detención del agente al agotar créditos
desde última llamada LLM
<2min
Disponibilidad de créditos top-up
tras confirmación de pago Stripe
🔒

Restricciones e Invariantes

9 restricciones
Política fija
Sin créditos negativos
El saldo de un workspace nunca puede ir por debajo de 0. Un step en ejecución debe terminar pero el siguiente no puede iniciar si el saldo previo es ≤ 0.
Política fija
Check de créditos < 5ms
El agent_runner ejecuta hasta 200 steps/seg. Cualquier verificación de saldo debe usar Redis como caché — no puede ir a Postgres en cada step.
Política fija
API v1 sin breaking changes
Los campos existentes de la API pública de agentes no pueden cambiar de semántica ni desaparecer. Los nuevos campos deben ser opcionales.
Política arquitectura
agent_runner ↔ billing_service
El agent_runner no puede llamar directamente a Stripe. Todo lo relacionado con billing pasa por billing_service mediante llamada interna síncrona o evento.
Política arquitectura
Datos EU en Supabase EU
Todos los registros de transacciones y facturas de clientes europeos deben permanecer en la región EU de Supabase. La nueva tabla de saldo también.
Política arquitectura
RLS activo en Supabase
Cada nueva tabla debe tener política RLS con workspace_id. Los clientes solo ven sus propios registros; el servicio backend usa service_role.
Restricción técnica
Saldo en Redis (caché canónica)
La tabla workspace_credits en Postgres es fuente de verdad, pero Redis debe tener el saldo disponible para lecturas de baja latencia. Reconciliación periódica.
Restricción técnica
Límite por agente ≤ workspace
Si un agente tiene max_credits_per_run configurado, ese límite no puede exceder el saldo disponible del workspace en el momento de iniciar.
Restricción técnica
Recarga mensual automática
El billing_service debe restaurar available_credits al límite del plan en cada ciclo de renovación. El top-up se suma al saldo existente, no lo reemplaza.
👥

Actores y Superficies

Actor Superficie / Punto de entrada Capacidades nuevas Restricciones de acceso
Workspace Admin Dashboard Settings → Billing → Credits Ver saldo, historial de consumo, comprar top-up vía Stripe Checkout Solo su workspace; rol admin
Developer (miembro workspace) Agent Config UI / API PATCH /agents/:id Configurar max_credits_per_run en cada agente Solo su workspace; rol developer o superior
agent_runner Interno — pre-step hook Check de saldo antes de cada step; detención limpia + registro de motivo Service account; sin acceso directo a Stripe
billing_service Webhook Stripe / Cron mensual Confirmar top-up, recargar saldo mensual, actualizar Redis tras cambio de plan Service account; owner de Stripe y billing data
Stripe checkout.session.completed webhook Trigger de top-up; evento de renovación de suscripción Solo via webhook → billing_service
Operador Novalabs Admin internal panel Ver créditos de cualquier workspace, ajuste manual de saldo (con audit log) Rol platform_admin; audit log obligatorio
🔄

Estados y Transiciones del Agent Run

Afecta únicamente a la FSM de agent_runs. Se añaden dos nuevos estados terminales: stopped_credit_limit (límite de agente agotado) y stopped_workspace_limit (saldo de workspace agotado). Ambos deben registrar el step exacto donde se produjo la detención y los créditos consumidos hasta ese momento.
QUEUED
En cola Redis
pre-check
RUNNING
Ejecutando steps
step OK + saldo
COMPLETED
Exitoso
RUNNING
Último step OK
saldo ≤ 0
STOPPED_WS
Workspace sin saldo
.
RUNNING
Último step OK
agent limit
STOPPED_AGENT
Límite agente alcanzado

Transición Pre-Step (lógica del agent_runner)

// Ejecutar ANTES de cada step
function preStepCreditCheck(run, step) {
  ws_balance = redis.get(`credits:{run.workspace_id}`) // <1ms
  if (ws_balance <= 0) → STOP(run, 'STOPPED_WORKSPACE_LIMIT')
  if (run.agent.max_credits_per_run) {
    if (run.credits_used >= run.agent.max_credits_per_run) → STOP(run, 'STOPPED_AGENT_LIMIT')
  }
  return PROCEED
}

// Post-step: decrementar saldo atómicamente
redis.decrby(`credits:{workspace_id}`, step.credits_consumed) // <2ms
🗄️

Implicaciones del Modelo de Datos

workspace_credits CREAR
id uuid PK FK → workspaces.id (unique) NUEVO
available_credits int NOT NULL Saldo actual. Nunca < 0. NUEVO
topup_credits int DEFAULT 0 Créditos comprados pendientes de consumir NUEVO
monthly_limit int NOT NULL Derivado de plan_tier NUEVO
last_reset_at timestamptz Última recarga mensual NUEVO
credit_transactions CREAR
id uuid PK NUEVO
workspace_id uuid FK + RLS policy NUEVO
type enum debit | topup | monthly_reset | manual_adj NUEVO
amount int Positivo=crédito, negativo=débito NUEVO
stripe_session_id text nullable Solo para type=topup NUEVO
agent_run_id uuid nullable Solo para type=debit NUEVO
agents MODIFICAR
max_credits_per_run int nullable NULL = sin límite. Validar ≤ ws saldo en runtime. MOD
agent_runs MODIFICAR
stop_reason enum nullable workspace_limit | agent_limit | success | error MOD
credits_used int DEFAULT 0 Ya existe — mantener semántica actual SIN CAMBIO
Nota crítica de datos: Hoy available_credits no existe — el saldo se calcula on-the-fly sumando credits_used. Esta query es lenta e incompatible con el requisito de <5ms. La nueva tabla workspace_credits + caché Redis reemplaza ese patrón. Se necesita migración one-time para backfill del saldo actual.
🔌

Interfaces / Contrato de API

GET /api/v1/workspaces/{workspace_id}/credits NUEVO · sin breaking changes
available_creditsintSaldo disponible actual
monthly_limitintCréditos del plan mensual
topup_creditsintCréditos extra comprados
last_reset_atISO 8601Última recarga automática
POST /api/v1/workspaces/{workspace_id}/credits/topup NUEVO · rol admin
amount_creditsintCréditos a comprar (múltiplo de 100)REQ
success_urlstringRedirect tras pago StripeREQ
→ Devuelve Stripe Checkout URL. billing_service crea la sesión.
PATCH /api/v1/agents/{agent_id} MODIFICADO · campo opcional
max_credits_per_runint|nullNuevo campo opcional. null = sin límite. Validar > 0 si presente.
WEBHOOK Stripe → checkout.session.completed BILLING SERVICE
metadata.workspace_idstringIdentificar el workspace destino del top-upREQ
metadata.credit_amountintCréditos a acreditarREQ
→ billing_service: 1) update workspace_credits, 2) insert credit_transactions, 3) invalidar Redis caché. Idempotente vía stripe_session_id.
🛡️

Seguridad, Billing y Observabilidad

Seguridad

ÁreaRequerimiento
RLS SupabaseTodas las tablas nuevas con policy workspace_id = auth.jwt()->>'workspace_id'
Stripe WebhooksVerificar firma con STRIPE_WEBHOOK_SECRET; rechazar sin firma válida
Trust Boundaryagent_runner nunca llama a Stripe directamente; toda operación billing pasa por billing_service
Ajuste manualSolo platform_admin; audit log inmutable con actor + motivo
Race conditionDecremento Redis debe ser atómico (DECRBY). Si llega a 0, el flag se propaga en <500ms

Observabilidad requerida

Evento / MétricaTipo
agent.stopped.workspace_limitEvent log + alert
agent.stopped.agent_limitEvent log
credits.topup.confirmedEvent + audit log
credits.monthly_resetEvent + audit log
Latencia pre-step check (p99)Métrica < 5ms
Redis cache hit rateMétrica > 99%
🚫

Non-Goals (fuera de scope)

Sistema de precios por crédito variable: Esta capacidad no define el coste en créditos por tipo de modelo LLM. El pricing de créditos queda fuera — se asume que agent_runner ya conoce el coste de cada step.
Notificaciones al usuario: Alertas de "te quedan X créditos" o "tu agente fue detenido" por email/Slack no son parte de esta lane. Pertenecen a la capa de notificaciones.
Créditos por usuario individual: El límite es siempre a nivel workspace. Granularidad por miembro individual no está en scope.
Pausa y reanudación de agentes: El agente se detiene definitivamente. Pausar y reanudar desde el step exacto cuando hay saldo de nuevo es una capacidad separada.
Migración de datos históricos de billing: El backfill de workspace_credits.available_credits desde el historial de agent_runs.credits_used no es parte de esta spec — requiere un script de migración propio con revisión manual.

Preguntas Abiertas — Bloqueantes

1
¿Qué pasa con un step que ya está en ejecución cuando el saldo llega a 0 a mitad?
El spec dice "detención limpia, no a mitad de una transacción". El pre-step check garantiza que no se inicia un nuevo step sin saldo. Pero si dos steps corren en paralelo y ambos decrementan simultáneamente llevando el saldo a negativo, ¿se permite ese brief overspend de un step? ¿O se requiere locking pesimista?
Impacto: Si se requiere locking pesimista, la latencia del check puede superar los 5ms bajo carga.
⚠ Decisor: Ana Torres (CPO) + Arch Review
2
¿Los créditos top-up tienen fecha de caducidad?
El PRD no especifica si los créditos comprados (top-up) caducan al final del ciclo mensual o son permanentes. Si caducan, la tabla credit_transactions necesita campo expires_at y la lógica de recarga mensual debe diferenciar entre créditos del plan (se resetean) y top-up (se conservan o caducan).
⚠ Decisor: Ana Torres (CPO)
3
¿Puede un agente con max_credits_per_run mayor que el saldo actual del workspace iniciar la ejecución?
Si un agente tiene max_credits_per_run = 500 pero el workspace solo tiene 200 créditos disponibles, ¿se permite iniciar (y se para en 200) o se bloquea el inicio? Bloquear el inicio daría una mejor UX pero podría impedir runs cortos legítimos cuando el saldo es bajo.
⚠ Decisor: Ana Torres (CPO)
Necesita Architecture Review antes de implementar

Las restricciones de implementación están documentadas y el contrato técnico es suficientemente explícito para iniciar el diseño de arquitectura. Sin embargo, las 3 preguntas abiertas (especialmente #1 sobre concurrencia y #2 sobre caducidad de top-ups) impactan directamente en el modelo de datos y en la estrategia de Redis. Deben resolverse en el Architecture Review antes de crear tickets de implementación.

Siguientes lanes recomendadas:

→ tdd-workflow → verification-loop → project-flow-ops → dashboard-builder (credits UI) → api-connector-builder (Stripe webhook)