Implementar el primer executor real del pipeline agentic: ReadonlyExecutor. Ejecuta comandos readonly (ls, cat, curl, docker ps, systemctl status, etc.) a través de subprocess.run con shell=False, validación estricta de argumentos y registro completo en audit trail.

28.1 → planner DAG + permisos + governance
28.2 → readonly executor (comandos seguros, shell=False) ← ESTAMOS AQUÍ
28.3 → rollback engine
28.4 → tool contracts
28.5 → sandbox write simulation
28.6 → rollback automatizado
28.7 → approval execution gates
28.8 → write execution controlada

Catálogo declarativo de comandos readonly permitidos:

  • SAFE_READONLY_COMMANDS: dict[str, ReadonlyCommandSpec] — 27 comandos con category, risk, local_only, requires_args_validation
  • FORBIDDEN_READONLY_COMMANDS: set[str] — 20+ comandos prohibidos
  • FORBIDDEN_READONLY_PATTERNS: set[str] — patrones bloqueados (systemctl restart, docker stop, etc.)
  • DANGEROUS_OPERATORS, DANGEROUS_REDIRECTS, DANGEROUS_TOKENS — tokens bloqueados en readonly
  • FIND_ALLOWED_PATHS/opt/ai-lab, /tmp, /var/log, /home/albert
  • DOCKER_ALLOWED_SUBCOMMANDS — solo ps, stats, inspect, logs
  • RFC1918_PATTERNS — solo IPs locales para curl
  • ExecutionCapability enum: READONLY, SANDBOX_WRITE, SYSTEM_WRITE

Wrapper subprocess.run con shell=False:

  • validate_command() — validación pre-ejecución: shlex parsing, forbidden tokens, redirects, operators, args específicos por comando
  • run_safe() — ejecuta el comando validado, captura stdout/stderr, calcula hashes SHA-256 truncados a 16 chars
  • SafeRunnerResult — dataclass con command, exit_code, stdout, stderr, duration_ms, blocked, blocked_reason, stdout_hash, stderr_hash

Validaciones específicas:

  • curl: no -o/-O/--output, target solo RFC1918
  • find: path explícito requerido, solo en FIND_ALLOWED_PATHS
  • journalctl: no -f/--follow, --lines max 500
  • docker: solo ps/stats/inspect/logs, no exec/cp/compose/etc.
  • systemctl: solo status/is-active/is-enabled, no restart/stop/start/etc.

Contexto de ejecución para el runtime agentic:

  • ExecutionMode enum: SIMULATION, READONLY, SANDBOX_WRITE, AUTONOMOUS
  • CURRENT_EXECUTION_MODE = ExecutionMode.READONLY
  • DryRunReason enum: FEATURE_FLAG, GOVERNANCE_BLOCK, RISK_BLOCK, READONLY_PHASE
  • RuntimeExecutionContext dataclass: execution_id, mode, phase, dry_run, dry_run_reason

Políticas de governance pre-ejecución:

  • check_governance() — verifica intentos prohibidos (restart_service, install_package) vs comandos validados
  • assess_risk() — clasifica riesgo por intent/tool/target
  • check_scope() — verifica targets contra scopes permitidos

Audit trail en JSONL:

  • write_execution_audit() — escribe entrada en runtime/state/execution_audit.jsonl
  • build_audit_entry() — construye entrada con execution_mode, dry_run, dry_run_reason, action, result
  • read_execution_audit() — lee últimas N entradas
  • get_audit_stats() — estadísticas: total, blocked, success, failed

RealReadonlyExecutor — el executor real:

  • Feature flags: ENABLE_EXECUTOR=False, DRY_RUN=True por defecto
  • execute() — itera acciones del plan, aplica governance, ejecuta run_safe(), escribe audit trail
  • Si DRY_RUN=TrueSimulationExecutor.execute_with_context() con dry_run_reason
  • Si ENABLE_EXECUTOR=True y DRY_RUN=False → ejecución real con shell=False

Stub para FASE 28.3:

  • RollbackPlaceholder.rollback() → siempre success=False con reason="rollback_not_implemented_before_FASE_28.3"
  • SimulationExecutor.execute_with_context() — nuevo método estático que simula ejecución registrando dry_run_reason y execution_mode
  • WorkflowState.EXECUTING añadido
  • Transiciones: READY_FOR_EXECUTION → EXECUTING, SIMULATING → EXECUTING, EXECUTING → DONE/FAILED
  • _CURRENT_PHASE actualizado de "28.1" a "28.2"
  • Docstring actualizado a FASE 28.2
  • Import de RealReadonlyExecutor, RuntimeExecutionContext, ExecutionMode, DryRunReason
  • Pipeline agentic (L1433-1502) modificado:
    • Si AI_LAB_ENABLE_PLANNER=True y AI_LAB_PLANNER_DRY_RUN=FalseRealReadonlyExecutor.execute()
    • Si no → SimulationExecutor.execute_with_context() con dry_run_reason
    • Audit event incluye execution_mode, dry_run, dry_run_reason
  • 3 endpoints GET nuevos:
    • GET /agentic/executions — últimos N entries del audit trail
    • GET /agentic/executions/stats — estadísticas de ejecución
    • GET /agentic/state — estado actual del executor
  • 6 métricas nuevas:
    • ailab_executor_commands_total — comandos por resultado/riesgo
    • ailab_executor_blocked_total — bloqueos por razón
    • ailab_executor_governance_blocks_total — governance blocks por intent
    • ailab_executor_dry_run_total — dry-runs por razón
    • ailab_executor_duration_ms — duración por modo
    • ailab_executor_validation_failures_total — fallos de validación por razón

tests/test_executor_28_2.py: 84 tests, 168+ assertions.

SecciónTestsCubre
SafeRunner Validation24allowed/forbidden commands, patterns, operators, redirects, curl/find/journalctl/docker/systemctl
SafeRunner Execution7run, block, hash, to_dict, timeout, shlex fail
ReadonlyRegistry8catalog, spec fields, forbidden overlap, patterns, RFC1918
ExecutionContext8mode enum, dry_run reasons, default context, is_executable, to_dict
ReadonlyPolicies10governance restart/install/run, risk levels, scope check
ExecutionAudit6build entry, write+read, empty, stats, timestamp, phase
ReadonlyExecutor4flags, simulation fallback, empty plan, timeline
RollbackPlaceholder2not_implemented, to_dict
WorkflowState6EXECUTING exists, transitions
PermissionsPhase2825scope allowed, classification
PlannerCompatibility5plan creation, action fields, timeline transitions
ailab_executor_commands_total{result="success|blocked|failed", risk="low|medium|high"}
ailab_executor_blocked_total{reason="forbidden_pattern|command_not_found|etc"}
ailab_executor_governance_blocks_total{intent="restart_service|install_package"}
ailab_executor_dry_run_total{reason="feature_flag|readonly_phase|governance_block|risk_block"}
ailab_executor_duration_ms{mode="readonly|simulating"}
ailab_executor_validation_failures_total{reason="shlex_error|empty_command|etc"}

CP-28.2-READONLY-EXECUTOR-STABLE