home

Things I'm building

Dossier Builder

prototype

Software for the applicant's side of a drug or medical device authorisation in Colombia. A registration dossier runs to a few hundred pages, and the same fact, a manufacturer's legal name, a storage condition, the population an indication covers, shows up in several of them. Dossier Builder reads the PDFs, turns each fact into a claim with its folio, and checks whether the folios agree.

Dossier Builder's product record, showing the surfactant field mapped to FHIR R5, IDMP, CTD, SmPC and INVIMA, with seven claims from different folios: polysorbate 80 in modules 2 and 3, polysorbate 20 read by OCR in module 5.

The dossier is treated as a database and the documents as views of it. Every field of the product record shows where it lives in the Colombian structure and in the international ones: CTD, FHIR R5 for ISO IDMP, the IMDRF table of contents for devices. The same record can go out as a national submission or as a package another agency's system can read, and a package from elsewhere can come in.

A check in Dossier Builder marked NO COINCIDE: polysorbate 80 on folios 27 to 77 against polysorbate 20 on folios 164 and 166, the second side flagged as coming from OCR, with a note on why it matters.

Nothing is inferred and nothing is rounded. A check shows the folio on each side of a disagreement and stops there. Which side is right is a regulatory decision, and the software has no column for it. The test dossier is fictitious: 171 pages with three real contradictions and two that only look like one. The checks flag the three and leave the two alone.

Dossier Builder's verification queue: two OCR readings from folio 164, each with a crop of the page image and three buttons: yes it says that, no it says something else, cannot be read.

Anything read from a scanned image, or proposed by a language model, waits in a queue for a person to confirm against a crop of the page. The machine's reading is never deleted; the decision is written next to it with a name and a date. It runs on the user's own machine only. A dossier is a trade secret, and it does not go to a server.

CENTINELA

running

A map of active natural hazards across Colombia: earthquakes, fires, volcanoes, storms and floods. It pulls from five public sources, merges them into one feed, and tags every event with the department it falls in and that department's risk score.

CENTINELA's live hazard map of Colombia in day mode, with markers for active earthquakes, fires, and other events, a layer panel on the left, and an event feed below.

The source APIs take a bounding box. Colombia is not a rectangle, so any box that covers it also returns earthquakes from Ecuador, fires from Venezuela, and storms off Peru.

The same CENTINELA map in night mode, with a darker base layer and the same hazard markers and panels.

A module tags each event with a Colombian department and decides whether a foreign one is close enough to matter. The relevance radius is not fixed. It scales by hazard type, and for earthquakes by magnitude. A large quake across the Ecuadorian border matters to Nariño. A small one does not.

CENTINELA's loading screen, showing its name and a pulsing radar icon while the map loads.

Everything else is a Cloudflare Worker polling five sources on two cron schedules, merged into one GeoJSON feed and cached. The frontend is a single static file.

VIBE Ciudad Bolívar

running

VIBE Ciudad Bolívar is a social project in the south of Bogotá dedicated to transforming how the area is seen. It showcases local talent and builds skills, striving to bring in opportunities that do not usually reach that part of the city. I designed the operational system behind its relaunch.

The VIBE Ciudad Bolívar logo, a circular badge with a colourful illustration of the neighbourhood.

Notion is the single source of truth, with a Claude agent and a skills library on top. Built schema first.

Every process was proven by hand before any of it was automated.

Small, and working, at the scale it is at now.