SOURCE-003 Pilot Load Python Runner 001¶
Fecha local: 2026-06-12
Estado: RUNNER CONTROLADO / SEMANTICA PREFLIGHT VS STATUS DEFINIDA
portal_visible = yes
Scope: tenant
tenant_id: alpuntodeventa
Owner: Gabi / Carlos Canu
Fuente de verdad:
docs/tenants/alpuntodeventa/business-observer/design/SOURCE-003-PILOT-LOAD-PYTHON-RUNNER-001.md
1. Objetivo¶
Crear un runner Python controlado para SOURCE-003 que deje preparada la
futura orquestacion gobernada:
text
preflight -> candidate load -> post-checks -> rollback seguro
Este runner no ejecuta SQL ni toca PostgreSQL en esta tarea. Solo deja la
CLI, las validaciones locales, las guardas de ejecucion y el contrato tecnico
para una revision futura separada.
2. Script¶
Script versionado:
text
scripts/source_003_pilot_load_runner.py
Caracteristicas:
- usa solo Python stdlib;
- valida archivos locales antes de cualquier intento de DB;
- conoce solo la DB, tenant, tabla, batch, fecha y CSV autorizados;
- resuelve
psqlsolo desdePATH; - acepta unicamente un ejecutable resuelto como
psqlopsql.exe; - no acepta rutas arbitrarias ni argumentos embebidos para
psql; - no usa shell execution para futuros modos DB;
- no imprime passwords;
- no imprime connection strings completas;
- leeria secretos solo desde variables de entorno en una revision futura;
- exige
ON_ERROR_STOP=1para cualquierpsqlfuturo; - valida existencia de los
SQL 004; - valida existencia y sha256 del CSV;
- exige evidencia fuerte de identidad de instancia antes de cualquier modo DB futuro;
- sanitiza defensivamente la salida de modos DB y no devuelve
stdout/stderrcompletos; - deja auditoria monotona de contacto DB para que un fallo no pueda reescribir a falso un intento real de probe o de script SQL autorizado;
- valida batch, fecha, tenant,
record_status,source_row_hashy formato dehora_origen_sgc; - define exit codes estables para error de validacion, bloqueo y entorno;
- no ejecuta rollback automaticamente.
3. Comandos¶
Comandos implementados:
planvalidate-filesdry-runpreflightstatusverify-existing-pilotloadpost-checksrollbackfull
Comandos permitidos en esta tarea:
python scripts/source_003_pilot_load_runner.py --helppython scripts/source_003_pilot_load_runner.py plan --dry-runpython scripts/source_003_pilot_load_runner.py validate-files --dry-runpython scripts/source_003_pilot_load_runner.py dry-run
4. Modos permitidos ahora¶
Permitidos y ejecutables sin DB:
planvalidate-filesdry-run
Lectura DB futura con guardas:
preflightstatusverify-existing-pilotpost-checkscomo alias legacy destatus
Modos presentes pero bloqueados en esta revision:
loadrollbackfull
Motivo del bloqueo fuerte:
- la evidencia vigente dice que
openclaw_business_observer_dev.business_observer.source_003_sales_itemsya tiene1886filas del batch1827f887-9499-4579-b4f3-234d54f41f7f - reintentar
loadofullsobre la misma tabla y el mismo batch no debe hacerse sin un nuevo gate - ejecutar
rollbacksobre ese mismo batch tampoco corresponde sin una aprobacion separada nueva
Semantica funcional de lectura DB:
preflightsignificapreflight-before-load: valida en solo lectura que la tabla este vacia antes de una carga nueva autorizadastatussignificaverify-existing-pilot: valida en solo lectura que el piloto ya cargado siga sano con1886filas exactas del batch1827f887-9499-4579-b4f3-234d54f41f7fverify-existing-pilotes alias explicito destatuspost-checksse conserva solo como alias legacy destatuspara no perder compatibilidad con la semantica historica delSQL 004- ningun modo de lectura DB habilita
load,fullnirollback
5. Guardas de ejecucion¶
Los modos que tocan DB exigen simultaneamente:
--execute--i-understand-this-touches-db--confirm-database openclaw_business_observer_dev--confirm-batch 1827f887-9499-4579-b4f3-234d54f41f7f--confirm-csv-sha256 3f16a957a79fc84d4ca37930988287cb81f05166210d057fcf8b45007f184afe--confirm-instance-fingerprint <fingerprint preflight aprobado>
Ademas, antes de tocar DB el runner revalida localmente:
- existencia del CSV autorizado
- sha256 exacto del CSV
1886filas83columnas- fecha unica
2026-06-09 - batch unico
1827f887-9499-4579-b4f3-234d54f41f7f tenant_id = alpuntodeventarecord_status = active0duplicados(tenant_id, line_key)0source_row_hashvacioshora_origen_sgccon formatoHHMMSS- existencia de los cuatro
SQL 004
Y, para cualquier modo DB futuro, la identidad de instancia no puede quedar atada solo al nombre de DB. El runner exige:
APV_BO_EXPECTED_POSTGRES_HOSTAPV_BO_EXPECTED_POSTGRES_PORTAPV_BO_EXPECTED_POSTGRES_DBAPV_BO_EXPECTED_POSTGRES_USERAPV_BO_EXPECTED_POSTGRES_FINGERPRINT
La evidencia de fingerprint debe venir de un preflight separado y controlado. Ademas, para cualquier modo DB futuro, el runner primero debe ejecutar un probe real contra la conexion ya resuelta y comparar esa evidencia runtime contra lo declarado por el operador. La consulta de referencia del probe es:
sql
SELECT json_build_object(
'server_addr', COALESCE(inet_server_addr()::text, 'unix_socket'),
'server_port', inet_server_port(),
'database', current_database(),
'user', current_user,
'server_version', current_setting('server_version'),
'version', version()
)::text;
Con esa evidencia, el runner deriva un fingerprint canonico:
text
<server_addr>:<server_port>|<database>|<user>|<version>
Si la consulta de probe no puede ejecutarse, si devuelve una forma inesperada,
o si host, port, database, user o fingerprint no coinciden con la
evidencia esperada, el runner debe bloquear la ejecucion antes de cualquier
script SQL. El nombre de DB solo no alcanza.
6. Variables esperadas¶
Si en el futuro se aprobara ejecutar modos con DB, el runner esperaria estas variables de entorno:
APV_BO_LOCAL_POSTGRES_HOSTopcional; defaultlocalhostAPV_BO_LOCAL_POSTGRES_PORTopcional; default5432APV_BO_LOCAL_POSTGRES_USERrequeridaAPV_BO_LOCAL_POSTGRES_PASSWORDrequeridaAPV_BO_LOCAL_POSTGRES_SSLMODEopcionalAPV_BO_EXPECTED_POSTGRES_HOSTrequerida para identidad fuerteAPV_BO_EXPECTED_POSTGRES_PORTrequerida para identidad fuerteAPV_BO_EXPECTED_POSTGRES_DBrequerida y debe seropenclaw_business_observer_devAPV_BO_EXPECTED_POSTGRES_USERrequerida para identidad fuerteAPV_BO_EXPECTED_POSTGRES_FINGERPRINTrequerida para identidad fuerte
El runner arma PGHOST, PGPORT, PGUSER, PGPASSWORD y PGDATABASE
internamente para psql, sin imprimir el password ni una connection string
completa. Para futuros modos DB, psql se resuelve solo desde PATH; si no
hay un binario seguro llamado psql o psql.exe, el runner debe fallar.
7. Seguridad¶
Guardas de alcance explicitas:
- no sync diaria
- no carga masiva
- no produccion final
- no otros tenants
- no otros schemas
- no otros batches
- no otros
SQLfuera del paquete004 - no rollback automatico
Guardas adicionales de seguridad del runner:
stdoutystderrcompletos de modos DB no se devuelven en el payload;- solo se expone
exit code,scriptejecutado,PASS/FAIL, lineas sanitizadas minimas y un resumen controlado; - el payload de auditoria distingue explicitamente:
postgresql_touched,postgresql_touch_attempted,fingerprint_probe_attempted,fingerprint_probe_completed,fingerprint_probe_failedysql_script_executed; - esa auditoria es monotona: si un probe o script SQL ya fue intentado, un
handler de error posterior no puede volver esos flags a
false; - la redaccion defensiva cubre
password,PGPASSWORD, connection strings y contexto sensible de conexion; - nombre de DB solo no alcanza para confiar en la instancia destino.
Semantica de auditoria:
- modos seguros (
plan,validate-files,dry-run):postgresql_touched = false,postgresql_touch_attempted = false,fingerprint_probe_attempted = false,sql_script_executed = false - probe futuro de fingerprint:
fingerprint_probe_attempted = trueantes de lanzarpsql; sipsqlefectivamente corre y consulta,postgresql_touched = true; si el probe termina bien,fingerprint_probe_completed = true; si falla en cualquier punto posterior al intento,fingerprint_probe_failed = true - scripts
SQL 004futuros:sql_script_executed = truesolo cuando un script autorizado llega a ejecutarse; no debe marcarse en modos seguros ni en bloqueos previos al lanzamiento del script
Semantica de resultado:
PASStecnico del comando: el runner puede resolverpsql, validar fingerprint y ejecutar elSQL 004autorizado sin error de runtimePASSfuncional depreflight-before-load: la tabla destino sigue vacia y el batch autorizado todavia no esta cargadoBLOQUEADOesperado depreflight-before-load: la tabla ya contiene las1886filas del piloto historico; eso no es error tecnico pero si bloqueo funcional para una carga nuevaPASSfuncional destatus/verify-existing-pilot: existen exactamente1886filas del batch autorizado, fecha unica2026-06-09, metadata exacta,0nulos criticos,0duplicados yline_key/source_row_hashvalidos- estado saludable de piloto ya cargado: debe leerse con
statusy no reinterpretarse como permiso de escritura
Regla documental critica:
- la tabla ya tiene
1886filas del piloto dedicado en verde - ese estado no habilita un nuevo
load - ese mismo estado si puede ser
PASSfuncional parastatus - cualquier nueva ejecucion real requiere un gate futuro separado, nueva decision humana y probablemente nueva parametrizacion
8. Relacion con SQL 004¶
El runner no reemplaza los SQL 004. Los gobierna.
Relaciones:
preflightapunta adesign/sql/004_source_003_dedicated_db_pilot_load_preflight.sqlstatus,verify-existing-pilotypost-checksapuntan adesign/sql/004_source_003_dedicated_db_pilot_load_post_checks.sqlloadapunta adesign/sql/004_source_003_dedicated_db_pilot_load_candidate.sqlrollbackapunta adesign/sql/004_source_003_dedicated_db_pilot_load_rollback.sql
El runner agrega arriba de esos SQL:
- validacion local de archivos;
- confirmacion exacta de DB, batch y CSV sha256;
- validacion declarativa de identidad por
host,port,database,usery fingerprint esperado; - probe runtime obligatorio de fingerprint real antes de ejecutar cualquier script SQL;
- validacion de que el CSV autorizado siga siendo el esperado;
- revalidacion inmediata del SHA256 del CSV justo antes de cualquier futuro
loadofull; - bloqueo duro de los modos de escritura en esta revision.
9. Relacion con el generador Python de CSV¶
Documento relacionado:
text
docs/tenants/alpuntodeventa/business-observer/design/SOURCE-003-PREPARED-CSV-PYTHON-GENERATOR-001.md
Relacion:
scripts/source_003_prepare_csv.pyresuelveraw -> prepared CSVscripts/source_003_pilot_load_runner.pyresuelve la capa de control alrededor del eventual uso del CSV preparado
El runner asume como CSV autorizado:
text
C:\APV\openclawai\snapshots\source-003\prepared\SOURCE-003-PILOT-LOAD-DEDICATED-DB-2026-06-09.csv
Con sha256 autorizado:
text
3f16a957a79fc84d4ca37930988287cb81f05166210d057fcf8b45007f184afe
10. Por que no es sync diaria¶
No es sync diaria porque:
- esta atado a un unico snapshot congelado
2026-06-09 - esta atado a un unico batch autorizado
- no implementa ventana movil
- no implementa
upsert - no implementa comparacion por
source_row_hashentre corridas - no implementa
last_seen_atde refresh diario - no resuelve
missing_from_source - no maneja fechas vivas ni drift operacional de
SGC
11. Por que no es produccion¶
No es produccion porque:
- el alcance sigue siendo piloto dedicado y controlado
- el mismo batch ya fue cargado y queda cerrado como evidencia verde
- la estrategia futura de operacion real sobre la DB dedicada sigue bloqueada
- no existe aprobacion para scheduler, sync diaria ni carga masiva
- no se habilitan otros tenants, otras tablas ni otros entornos
12. Proximo gate necesario¶
Antes de cualquier ejecucion real futura se necesita un nuevo gate explicito que defina como minimo:
- si corresponde solo lectura estricta previa a carga
(
preflight-before-load) o verificacion de piloto ya cargado (status/verify-existing-pilot), o tambien escritura; - si el batch sigue siendo este o debe ser uno nuevo;
- si el CSV autorizado sigue siendo el historico o uno nuevo generado por pipeline;
- si el estado cargado actual de
1886filas se preserva, se audita o se corrige por otra via; - si el runner debe evolucionar a una version nueva para operacion real.