Skip to content
View riccardosapuppo's full-sized avatar

Block or report riccardosapuppo

Block user

Prevent this user from interacting with your repositories and sending you notifications. Learn more about blocking users.

You must be logged in to block users.

Content in all repositories owned by your account will be closed.
Maximum 250 characters. Please don’t include any personal information such as legal names or email addresses. Markdown is supported. This note will only be visible to you.
Report abuse

Contact GitHub support about this user’s behavior. Learn more about reporting abuse.

Report abuse
riccardosapuppo/README.md

Riccardo Sapuppo

Full-stack developer in Catania. Most of what I build ends up in a clinic, a radiology archive, or somebody's back office. In places like those, being approximately right is worse than being obviously wrong, because nobody notices.

I reconstruct that work here, from scratch, so there is something to open and run. Not to show that it runs: to show what it took to be sure it was right. What carries over is the shape of the problem; the code is newly written and the data invented. Not all of it: two projects are mine from the start, and one is a fork of the open-source viewer its original was built on. The note at the bottom says which.


Every project makes a claim, and its own CI can prove it false

That is the one thing these have in common, and it is the point of them. Each repository opens with a sentence that could be wrong, and each has a command that goes red if it becomes wrong. That command is a measurement, not a badge.

Orders rebuilt from a folder of email, read by rules or by a language model.

A value has to point at the words it was read from. A rule knows where it matched. A model is asked for the text it read and never for an offset, because asked for a character offset it will give you a plausible integer. Every quote it gives back is searched for in the message, and what it could not have read is dropped rather than stored with a confidence beside it. Anthropic, Mistral or OpenAI, chosen on the first screen: the checking is the same for all three, because it is about the contract and not about whose model honours it, and npm test runs each of them with the network stubbed out.

Questions about a set of manuals, answered two ways side by side.

A vector search is the wrong tool for a large share of what people ask a manual, and it fails confidently, with a citation. Twelve questions with a known answer, judged on the first result only: similarity alone gets 6 right, looking at the kind of question first gets 9. The difference is all in two places, codes like E-4412 that have no semantic neighbours and questions phrased in the document's own words, and the run fails if any question plain similarity got right is traded away. Where the question's words are not the document's, both score zero, and the page says so.

A job that deletes medical images it cannot get back.

A dry run and a real run choose the same studies. Give the dry run its own branch, the way it usually gets one, and it failed to mention 9 of the 255 studies it was about to delete.

Nothing is deleted because the record system said nothing. An empty result set is an ordinary return value, so it falls through to whatever the last line of the function happens to be. That cost 23 patients' studies, on a run where nothing errored and every total looked plausible.

When the second of two deletions fails, the next run can finish it. Removing the catalogue row first leaves 25 folders that nothing will ever look for again. Same failure, same frequency, and one of them is permanent.

A form is authored as a tree and asked about as a table.

The same 120 submissions, stored three ways, asked the same eight questions. Not speed: whether the answer is right, and whether a wrong answer announces itself. A pile of JSON documents is quietly wrong three times and cannot express three of the questions at all. Flattening fixes all of them but the rename, which needs an id, and that is the whole finding.

A portal where patients read their own reports, and only their own.

Four routes that begin identically: read the signed-in patient from the session, load them, write them into the audit line. Two then put that patient into the question asked of the archive and two do not, and reading the four methods will not tell you which. Over 90 questions, the two that do not hand over 70 documents belonging to somebody else, and write 70 audit lines saying those documents were handed to their owner.

The repair is not four checks. A check is a line that can be absent, and this is a demonstration that an absent line is invisible. The archive has no method that takes a document identifier on its own, so the wrong version does not fail review: it fails to compile.

Analytics over an archive whose schema you do not own and that differs at every installation.

Six installations holding the same studies. The side that reads the schema first gets every question right on all six. The side with the SQL written straight gets 7 answers wrong with nothing to show for it. They are not errors but numbers, four times too high, because a one-to-many join took the storage with it.


The work

My own

a page builder and CMS, on Rails and React · GPS audio guides for Sicilian cities, live at walkaround.cloud

Medical imaging

DICOM viewer, web · DICOM viewer, desktop · archive analytics · archive maintenance

Healthcare, patient-facing

multi-tenant booking · a booking agent that knows when to hand over · reports, to the patient they belong to

Documents

OCR as an internal service · questions about a document set · rebuilding an order from a folder of email, with rules or a language model · forms as a relational schema

Desktop and devices

teaching somebody a Windows application · printer meters over SNMP · deciding from pixels whether anybody is logged in

The rest

campaigns that cannot write to somebody who said stop · booking a boat in a system the website does not own, at a price the browser cannot set · a TOTP authenticator


What is underneath

Where a standard was the interesting part, it is implemented rather than installed. SMTP is spoken by hand, one line at a time, so the conversation is visible. SNMP is decoded from ASN.1/BER on the standard Printer MIB, not through a vendor's own interface. MIME is parsed with its own byte counts because a {n} in IMAP is bytes and not characters, and getting that wrong gives you mojibake and no error. DICOM dates are VARCHAR(8) and sort lexicographically, which is the one gift that format gives you.

The Node projects have no runtime dependencies at all and no build step. The imports look like node:sqlite, node:http and node:fs, and what Node runs is the TypeScript itself, with erasableSyntaxOnly set so the type check is also the proof that they still run without one. The C# ones carry no packages beyond the test runner, and the PHP one carries none beyond it either.

Three of them also make an argument about types rather than checks. A document archive with no method that takes an identifier on its own; a booking that takes a quote reference and has nowhere to put a price; a receipt for a second factor whose constructor is internal, so a page cannot decide on its own that a code was verified. In each case the dangerous version is not guarded. It cannot be written at all, and a test asserts that by reflection so it stays that way.

TypeScript · Node.js · C# · .NET 9 · ASP.NET Core · WPF · PHP · WordPress · Angular · React · Electron · Python · FastAPI · LangGraph · Anthropic, Mistral and OpenAI APIs · Ruby on Rails · Ruby · PostgreSQL · SQL Server · SQLite · Docker


A note on the originals

Most projects here are based on a real production system I designed or developed. The originals stay in private repositories: confidentiality and intellectual property constraints mean none of them can be published.

Those repositories are reconstructions. Each was rebuilt from scratch so it could be shown and run, preserving the core architecture, the workflows and the technical challenges of the production solution, with newly written code and fictional data. No proprietary source code, confidential data or client assets from an original are included in any of them. What the data describes is invented in the repository itself: no clinic, no patient, no real archive, no customer list.

One is not a reconstruction: the web DICOM viewer is an independent version built on the same open-source base as its original, OHIF, at the same version, and it reads real studies from public research collections, cited with their licences. Its original stays with the client and is not there either.

Two are mine from the start, with no client and no original: Page Builder CMS and Walk Around. Their repositories are the projects themselves, except that Walk Around's leaves out the audio guides, which are the product.

Each README says what the original did, what the reconstruction does differently, and why.

The limits are in the READMEs too, in their own section, and they are real ones. A portfolio piece without them is a brochure.


Write to me: [email protected].

Pinned Loading

  1. medical-dicom-viewer-web medical-dicom-viewer-web Public

    Diagnostic DICOM viewer for any study an archive holds. Adds a subgrid light box inside a viewport, hanging protocols the reader saves and gets back on the next study of the same kind, a reading li…

    TypeScript 1

  2. conversational-booking-agent conversational-booking-agent Public

    An agent that takes booking calls for a diagnostic clinic: finds the exam, holds a slot, and hands over to a person with a named reason. A hundred scored golden calls gate CI; a second reader asks …

    Python

  3. document-chat-assistant document-chat-assistant Public

    Answers questions about a set of documents and knows when a vector search is the wrong tool: literal codes, questions that lean on the last one, and which manual is even being asked about — with th…

    JavaScript

  4. document-ocr-service document-ocr-service Public

    An internal HTTP service that returns the text in an uploaded document, callable synchronously, as a stream, or as a job to poll. OAuth2 client credentials with real scopes, per-client rate limits …

    JavaScript

  5. multi-tenant-booking multi-tenant-booking Public

    A booking platform for a group of diagnostic centres: one shared register for identity, one PostgreSQL database per centre, and four roles whose boundary is a permission rather than four deployment…

    JavaScript

  6. order-email-extraction order-email-extraction Public

    Reads a folder of email, or a mailbox over IMAP, and rebuilds the life of an order from it, pointing at the exact characters every value was read from. Rules by default, with no API key; an LLM (An…

    TypeScript