Inspiration

I keep losing hackathons through silly things I kept forgetting to double check before a project submission, things like name-dropping a sponsor saying we used their service but not really, or a demo video recorded at 4am that didn't show the thing to its full potential because it was rushed, a "done" feature that was faked behind static data. Every retrospective issue lived in a different Obsidian page that I never reopened and kept forgetting to read. This meant the lesson never made it to the next event.

So I thus opted to build me a tool that no matter what stage of the hackathon process I'm in, I can see previous mistakes, the causes behind them and view a checklist that lets me visualise this all in one quick go. "What am I about to repeat, just before I submit?"

What it does

Autopsy is proactive, not reactive, failure intelligence for hackathon builders. There are two separate flows:

  1. Log a post-mortem. You add a past project with an honest "what actually went wrong" field (the stuff that doesn't make the Devpost). Bedrock extracts the failure patterns, important things that can alter the judge's mind like broken headline feature, fake integrations, required tech used as a token reference and so on, and stores them as concept nodes in an insights tab, as well as a graph so you can see how it all links together. You can also link the project's public GitHub repo, and Autopsy scans it for code-level evidence — so a "Dead Code" node can cite the actual empty file it found, not just your note.

  2. Get a pre-submission checklist. Point Autopsy at the hackathon you're entering, paste in the event's rules, and add a few details about your project. It combines your recurring failure patterns with the event's requirements and generates a checklist that cites both. For example: "You've shipped a faked integration 4x before — in CrimeLens, InboxPilot, SpotOn and Ripple. This event mandates real DynamoDB use, so verify every read/write actually hits it."

The graph compounds: logging a new project whose headline feature didn't really work strengthens the existing "Broken Headline Feature" node instead of creating a duplicate. Frequency becomes a real signal of what keeps biting you, not just keeping a list of documents that keep expanding over and over.

How I built it

Choosing DynamoDB

Each failure-pattern node stores its project memberships as a nested map keyed by projectId inside a single item. Adding a project is one single atomic and idempotent write - SET linkedProjects.<projectId> = {…}. This means:

  • Re-analysing the same project lands in its own key so there's no chance it double-counts
  • Two different projects touch different keys, so concurrent writes never overwrite each other
  • Cleverly, frequency doesn't need to be stored now as it's just the count of map keys, calculated on read so it can't drift out of sync.

Why use DynamoDB for something like this?

Access patterns here are per-user key lookups plus a compounding concept graph. That needs single-digit-millisecond reads without needing relational JOINs, and the document/map model makes the "compound a node atomically" operation a one-liner instead of a transaction. That's the data store, not just a cache: every read and write goes through it.

Canonicalisation My favourite part of this entire project is the canonicalisation mechanic, ie what makes the graph compound. When a new hackathon submission is entered, Autopsy feeds the user's existing concept slugs back to Bedrock and instructs it to reuse them rather than making new near-duplicates. So "dead-code" stays one node across ten projects, allowing it to expand and continuously show you how many times you keep making the same mistake.

Stack: Browser -> Next.js (Vercel) -> API Gateway -> six least-privilege Python Lambdas -> DynamoDB + Amazon Bedrock (Claude Haiku). Auth is verified in the Next.js server (Clerk) and passed to Lambdas via a shared secret, so native crypto is never packaged into Lambda. Two DynamoDB tables (autopsy-concepts, autopsy-projects), native CloudFormation, all done on eu-west-2.

Challenges I ran into

  • Making frequency trustworthy. My first version loaded the whole membership list into Python, mutated it, and wrote it back. This is bad since it could lose data if multiple submissions were concurrently entered, and let a separate counter drift. Moving to an atomic map SET on a single sub-key fixed both at once.

  • Coming up with a way to have the graph compound. Without canonicalisation, the model would mint "faked-integration", "fake-integration" and "mock-integration" as three separate nodes. Feeding it existing concepts on each call collapsed them into one, preventing duplicate nodes and insights, meaning the same failure compounds into one node where the count reflects how often it's happened, instead of being split across near-duplicates.

  • Grounding "what judges critique" in evidence for better results I took some time to research real judging rubrics (MLH, Devpost, ETHGlobal, sponsor events) and adversarially verified the claims, so Autopsy flags failures that actually cost points and ignores code-hygiene noise that judges don't score.

Accomplishments I'm proud of

  • An idempotent, conflict-free, compounding data model that doesn't need to keep a counter to track how many times a mistake has occurred.
  • A checklist that cites your own individual history and the event's rules - Giving the model access to the hackathon information makes it more grounded in its decisions, preventing: waffling, the model returning something less useful and Autopsy from just being a read-back journal.
  • Real demo data: four of my own post-mortems have been absorbed into Autopsy from four different hackathons so you can see a real idea of how it might look.

What I learned

The biggest shift was learning to model for the access pattern, not the entity. My relational instinct was a 1:many concept to project relationship. My first idea was to store a list and a frequency counter, and then change them in app code. However, doing what I suggested leads to read-modify-write conflicts between concurrent ingests and a counter that drifts out of sync with the data due to non-atomic writes. Recalling back to ACID from last year in University, rewriting what I had instead as an atomic SET on a single map key, and then getting the frequency on read instead of storing it made that entire set of bugs impossible to happen, instead of it being something I had to remember to guard against.

The end result: in DynamoDB, the right data model turned that hard operation into a much simpler and easier to understand one-liner. If I'm reaching for a transaction or a lock, I now have a new way to model it.

What's next

One thing in mind is deeper repo analysis that pulls richer failure signals straight from the code (not just empty files), per-event track inference, and team-shared failure graphs — so a whole team's recurring mistakes compound in one place.

Built With

Share this project:

Updates