RESELLERMASTER coge un lote de fotos mezcladas de ropa y objetos de segunda mano, las agrupa por producto y produce la ficha lista para copiar-pegar en Wallapop y Vinted (título, marca, talla, estado, precio) sin afirmar ni un atributo que no sea legible en una foto. El modo de fallo de este proyecto no es que el código pete: es que el pipeline invente «Nike, talla M, algodón, 25€» con total fluidez y yo lo publique. Una ficha mala no es un bug: es una venta perdida y reputación quemada. Empecé sin saber programar; lo levanté solo, con IA.
RESELLERMASTER takes a batch of mixed photos of second-hand clothes and objects, groups them by product and produces the listing ready to copy-paste into Wallapop and Vinted, title, brand, size, condition, price, without asserting a single attribute that isn't legible in a photo. This project's failure mode isn't the code crashing: it's the pipeline fluently inventing "Nike, size M, cotton, €25" and me publishing it. A bad listing isn't a bug: it's a lost sale and burned reputation. I started not knowing how to code; I built it solo, with AI.
App local de un solo usuario. No automatiza la publicación (va contra los términos de Wallapop/Vinted): llega hasta «copiar y pegar en 2 clics», a propósito.Local single-user app. It does not automate publishing (against Wallapop/Vinted terms): it goes as far as "copy & paste in 2 clicks", on purpose.
El producto es visual, así que la prueba también. Un vídeo del pipeline real: ingesta del lote → curar la agrupación → ficha con cada campo junto a su recorte → export copy-paste por plataforma → finanzas. El clímax es el export cronometrado.
The product is visual, so is the proof. A video of the real pipeline: batch ingest → curate the grouping → listing with each field next to its crop → per-platform copy-paste export → finances. The climax is the timed export.
Guion: ingesta → cremallera con pestillo (curar) → ficha con badges 📷/🧠 → export por plataforma → «5. Finanzas» (Subido → Vendido → beneficio → Excel). El export real se cronometró una vez en ~210 s (n=1; a mano ~285 s, estimado).Script: ingest → latching zipper (curate) → listing with 📷/🧠 badges → per-platform export → "5. Finances" (Uploaded → Sold → profit → Excel). The real export was timed once at ~210 s (n=1; by hand ~285 s, estimated).
Esto es lo que ningún otro proyecto mío tiene: una ficha donde cada dato dice de dónde salió. Un badge 📷 leído significa que el valor está contenido en el texto legible del recorte citado. Un 🧠 inferido es la mejor estimación del modelo, marcada como tal y con confianza baja.
El material sale vacío porque la etiqueta no está en ninguna foto, y eso es un acierto, no un hueco: un campo vacío se recupera; una talla inventada es una devolución.
This is what no other project of mine has: a listing where every value says where it came from. A 📷 read badge means the value is contained in the legible text of the cited crop. A 🧠 inferred one is the model's best guess, flagged as such with low confidence.
Material is left empty because the label isn't in any photo, and that's a win, not a gap: an empty field is recoverable; an invented size is a return.
A mano, cada producto es trabajo íntegro que se repite: fotografiar, teclear, categorizar, tasar, pegar. Con la app, el agrupado, la extracción y la confirmación se hacen en bloque para todo el lote, así que la ventaja no es fija: compone con cada producto que añades.
By hand, each product is full work repeated: shoot, type, categorize, price, paste. With the app, grouping, extraction and confirmation happen in bulk for the whole batch, so the advantage isn't fixed: it compounds with every product you add.
«Extraer TODO el lote» cuesta 2 clics, sea N=1 o N=30. Antes era 2 por producto. Hecho contado, no estimado."Extract the WHOLE batch" costs 2 clicks, whether N=1 or N=30. It used to be 2 per product. A counted fact, not an estimate.
3,4 cts/producto de extracción · 0 al reprocesar (caché por hash de imagen: cada llamada se paga una vez).3.4 cts/product for extraction · 0 on reprocess (cache by image hash: each call is paid once).
A mano cada producto se repite entero; en la app agrupar / extraer / confirmar se amortizan sobre todo el lote.By hand each product repeats in full; in the app grouping / extracting / confirming amortize across the whole batch.
La app está diseñada para que yo confíe y copie rápido, y esa confianza es exactamente lo que convierte una alucinación en una venta perdida. Así que la honestidad no es un eslogan: está incrustada en la arquitectura. Quien AFIRMA es el humano al confirmar; la máquina sólo propone y enseña el píxel.
The app is built so I trust it and copy fast, and that trust is exactly what turns a hallucination into a lost sale. So honesty isn't a slogan: it's baked into the architecture. The one who ASSERTS is the human on confirm; the machine only proposes and shows the pixel.
Cada campo lleva procedencia obligatoria: valor · fuente · evidencia · confianza. Un fuente=foto sin evidencia no es un dato, es un bug.
Un panel adversarial Opus (mi agente listing-audit) cazó justo esto: el if sólo validaba que la cita existiera, no que el valor estuviera en ese píxel: el modelo podía extender una lectura y colarla como leída. Veredicto BLOQUEANTE, corregido con guarda + test.
Una garantía prometida en prosa no está garantizada hasta que un `if` la fuerza.
Every field carries mandatory provenance: value · source · evidence · confidence. A source=photo without evidence isn't a datum, it's a bug.
An adversarial Opus panel (my listing-audit agent) caught exactly this: the if only checked the citation existed, not that the value was in that pixel: the model could extend a reading and pass it as read. BLOCKING verdict, fixed with a guard + test.
A guarantee promised in prose isn't guaranteed until an `if` enforces it.
El precio no es un atributo del producto: es una observación del mercado. Sale de comparables reales con su URL (la mediana de N parecidos, que puedo abrir y comprobar) o es None + motivo. Nunca un número plausible sin fuente. Límite honesto que la propia app declara en su UI: los anuncios publican lo que la gente PIDE, no por cuánto VENDIÓ (los precios de venta no son públicos).
Price is not a product attribute: it's a market observation. It comes from real comparables with their URL (the median of N similar items I can open and check) or it's None + reason. Never a plausible number without a source. An honest limit the app states in its own UI: listings publish what people ASK, not what they SOLD for (sale prices aren't public).
Toda llamada a cualquier proveedor (Claude/OCR/local) pasa por un módulo: contabiliza el coste por producto y cachea por hash de imagen. El proveedor es una decisión reversible, no una dependencia esparcida.Every call to any provider (Claude/OCR/local) goes through one module: it accounts cost per product and caches by image hash. The provider is a reversible decision, not a scattered dependency.
Lee la búsqueda pública por texto, coge ~15 parecidos y devuelve mediana + rango + URLs. Sin n≥5 → None + motivo. Leer ≠ publicar: volumen doméstico, sin herramientas de evasión.Reads the public text search, takes ~15 similar items and returns median + range + URLs. Without n≥5 → None + reason. Reading ≠ publishing: domestic volume, no anti-detection tooling.
Los campos obligatorios de cada plataforma × categoría viven en un esquema declarativo. El LLM RELLENA un esquema; no inventa campos ni olvida los obligatorios. Añadir una categoría es añadir datos, no código.Each platform × category's required fields live in a declarative schema. The LLM FILLS a schema; it doesn't invent fields or forget required ones. Adding a category is adding data, not code.
El activo que no se puede fingir no es el código: es el juicio. Antes de pagar una API o meter una dependencia, mido la alternativa gratuita sobre datos reales. Matar una hipótesis con un dato ( incluso cuando el dato va en mi contra ) es la señal de un proceso serio.
The asset you can't fake isn't the code: it's judgment. Before paying for an API or adding a dependency, I measure the free alternative on real data. Killing a hypothesis with data ( even when the data goes against me ) is the signature of a serious process.
| IdeaIdea | Por qué tentabaWhy it tempted | Qué medíWhat I measured | VeredictoVerdict |
|---|---|---|---|
| Búsqueda por imagen / LensImage search / Lensidentificar el producto por su fotoidentify the product from its photo | «sube la foto y te dice qué es»"upload the photo and it tells you what it is" | todas exigen URL pública; app local sin hostingall require a public URL; local app, no hosting | Descartada · 4 agentesRuled out · 4 agents |
| CLIP · match visualvisual matchagrupar fotos del mismo producto por similitudgroup same-product photos by similarity | embeddings gratis, sin APIfree embeddings, no API | 0.90 de similitud entre dos sudaderas DISTINTAS0.90 similarity between two DIFFERENT hoodies | [INC-004] |
| OCR · clasificar tipo de fotoclassify photo typedetectar etiqueta/metro/plano por su textodetect label/tape/wide-shot by its text | etiqueta y metro SON textolabel and tape ARE text | los caracteres se solapan por completocharacter counts overlap completely | [INC-007] |
| VLM localLocal VLMcorrer un modelo de visión en mi portátilrun a vision model on my laptop | coste 0, sin depender de nadiezero cost, no dependency | RTX 3050 · 4 GB VRAM → no entra→ won't fit | Medido, no intuidoMeasured, not guessed |
| Automatizar la publicaciónAutomate publishingSelenium sobre los formularios de Wallapop/VintedSelenium over Wallapop/Vinted forms | el paso final «gratis»the "free" final step | contra sus términos → baneo de la cuenta que ES el negocioagainst their terms → ban on the account that IS the business | Descartada a propósitoRuled out on purpose |
| Umbral de agrupación 15 s15 s grouping thresholdcuándo dos fotos son productos distintoswhen two photos are different products | barrido completo 5–30 s sobre 33 fotos realesfull 5–30 s sweep over 33 real photos | zona segura 1–23 s → 0 fusiones; acantilado a 24 ssafe zone 1–23 s → 0 merges; cliff at 24 s | En usoIn use |
El flujo es un flywheel de cinco etapas. El estado del curado se escribe en disco, no sólo en memoria: un rerun de Streamlit, un crash o un cierre de pestaña no pueden costarme dos horas de trabajo.
The flow is a five-stage flywheel. Curation state is written to disk, not just memory: a Streamlit rerun, a crash or a closed tab can't cost me two hours of work.
Sin fecha (WhatsApp la borra) la señal primaria no existe → la app avisa en la ingesta.No date (WhatsApp strips it) → the primary signal is gone → the app warns on ingest.
Propone clusters por hueco temporal; Diego confirma. Fusionar dos productos es INEXPRESABLE.Proposes clusters by time gap; Diego confirms. Merging two products is UNEXPRESSABLE.
OCR localiza el texto → recorte → el VLM lee. Cada campo con su badge 📷/🧠.OCR locates text → crop → the VLM reads. Every field with its 📷/🧠 badge.
Mediana de parecidos con URL, editable. Sin n≥5 → None + motivo.Median of similar items with URLs, editable. Without n≥5 → None + reason.
Literales exactos por plataforma, orden del formulario de cada una. Era el ~66% de mi tiempo.Exact platform literals, each one's form order. It was ~66% of my time.
La agrupación no optimiza acierto: optimiza no-fusionar. Partir un producto de más cuesta 5 segundos; fusionar dos mete una foto del producto A en la ficha del B, y eso no lo caza nadie. Por eso una pausa larga puede partir un grupo, pero la ausencia de pausa no confirma nada, y ningún grupo sale con confianza alta desde el reloj. La unidad de la UI es la frontera, no la foto: mezclar dos productos lejanos es inexpresable, no sólo desaconsejado.
Grouping doesn't optimize accuracy: it optimizes not-merging. Over-splitting a product costs 5 seconds; merging two puts a photo of product A into product B's listing, and nobody catches that. So a long pause may split a group, but the absence of a pause confirms nothing, and no group ships with high confidence from the clock. The UI's unit is the boundary, not the photo: mixing two distant products is unexpressable, not merely discouraged.
No le pido a la IA una respuesta. Un orquestador (Opus) delega la construcción en agentes Sonnet en paralelo, y luego un panel escéptico ataca lo construido: bug-hunter busca la causa raíz sin contexto, listing-audit intenta pillar al pipeline mintiendo contra las fotos reales, flow-qa mide la velocidad. El orquestador re-deriva cada veredicto ejecutando, no por informe. Yo juzgo.
I don't ask the AI for an answer. An orchestrator (Opus) delegates building to parallel Sonnet agents, then a skeptical panel attacks what was built: bug-hunter finds the root cause with no context, listing-audit tries to catch the pipeline lying against the real photos, flow-qa measures speed. The orchestrator re-derives every verdict by executing, not by report. I judge.
Pellizca para acercar y leer el detalle ↔Pinch to zoom in and read the detail ↔
Una ficha sobrevive sólo si resiste el ataque. El juicio final es mío, con el píxel delante: es la única capa que verifica verdad, no sólo legibilidad.
A listing survives only if it withstands the attack. The final judgment is mine, with the pixel in front: the only layer that verifies truth, not just legibility.
Ningún «el subagente reportó que OK» cierra nada. Corro yo pytest / /eval / la app y pego la salida REAL en el prompt del siguiente agente. Un informe no es evidencia; una salida sí.No "the subagent reported OK" closes anything. I run pytest / /eval / the app myself and paste the REAL output into the next agent's prompt. A report isn't evidence; output is.
El panel cazó que mi propio plan de cierre probaba un proxy: auditar código verde en vez de medir la métrica primaria del proyecto. Reordené, medir primero, y el número que corona el proyecto (~210 s) apareció ahí, junto a un bug que 904 tests verdes no veían. El rigor es cuestionar tu propio plan, no defenderlo.
The panel caught that my own closing plan was testing a proxy: auditing green code instead of measuring the project's primary metric. I reordered, measure first, and the number that crowns the project (~210 s) showed up there, next to a bug 904 green tests couldn't see. Rigor is questioning your own plan, not defending it.
El resto del método —la refutación asimétrica, el agente ciego que audita mis planes, el ledger que convierte un fallo en regla— es el mismo en los cinco sistemas y está contado una vez: en el índice.The rest of the method —asymmetric refutation, the blind agent auditing my own plans, the ledger that turns a failure into a rule— is the same across all five systems and is told once: on the index.
Si buscas a alguien para applied-AI, automatización o forward-deployed engineering, que construye sistemas para que la IA no se engañe a sí misma, hablemos.
If you're looking for someone in applied-AI, automation or forward-deployed engineering who builds systems so the AI can't fool itself, let's talk.