DEV Community

Cover image for We built an agent that keeps our developer portfolios and socials in sync with what we ship
Aditi Anand
Aditi Anand

Posted on

We built an agent that keeps our developer portfolios and socials in sync with what we ship

We built LaunchPad-AI for the All Things Agentic Hackathon, a challenge presented by Google to build autonomous agents that run in the background and do real work, not just chat. This is the story of what we built and how the Google Cloud stack shaped it.

The friction we actually had

Every developer knows this one. You ship a project, feel great for a day, and then your portfolio site and your LinkedIn stay frozen in the past. Ours were months out of date. The work was done but it never made it to the places people actually look.

The annoying part isn't writing one post. It's the decision every single time: is this release even worth showing? Is it a real new project, or a follow-up to something already featured? Is it a dependency bump nobody cares about? That judgment is what we never wanted to make manually again, and the hackathon's "bring your own friction" framing pushed us to build an agent around exactly that personal friction that we as computer science students faced in our daily life.

So we built LaunchPad-AI: you publish a GitHub release, and the agent decides on its own if the release deserves a new portfolio entry, an update to an existing one, or nothing at all. When it decides something's worth showing, it updates the live portfolio and drafts the LinkedIn post. You ship. It handles the rest.

The part that makes it an agent, not a script

It would have been easy to build a bot that posts on every release. That's automation, not an agent. The whole point of ours is the decision.

At the core is a relevance curator built as a real Google ADK LlmAgent, running Gemini 3.5 Flash on Vertex AI, executed through the ADK Runner with a structured output schema. On each release it gets three things: a profile of the shipped repo, the list of projects already featured on the portfolio, and the developer's stated interests. It returns one of three actions — feature_new, update_existing, or skip — plus written reasoning for why.

Using ADK here wasn't decoration. The framework gave us a clean way to run a single-shot, structured decision with a schema-constrained output, so the agent can't return free-form mush, it returns a typed decision we can act on programmatically. And Gemini 3.5's structured output is what makes the reasoning trustworthy enough to gate real actions on: we're not regex-parsing a paragraph, we're reading a validated object.

That reasoning is the thing we're proudest of. The agent doesn't just act; it explains itself in its own words: "This is a dependency bump with no new capability — not notable enough to feature." Watching it correctly decline to do something is more convincing than watching it do something. An agent with judgment knows when not to act — which is where the title comes from. The best thing ours does, half the time, is nothing.

The memory matters too. Because it reads what's already featured before deciding, it never creates a duplicate. Ship a real new feature to a project it already knows about, and it updates the existing entry instead of posting the same project twice. Firestore is what makes that memory work — it holds what's featured, the developer's profile, and a log of every decision with its reasoning. That "memory of your public presence" is what keeps a portfolio coherent over time instead of turning into a pile of redundant cards.

The architecture — and why each Google Cloud piece earned its place

The pipeline is event-driven and decoupled end to end:

GitHub release → webhook → Pub/Sub → Cloud Run → ADK agent (Gemini 3.5 on Vertex) → Firestore.

Here's why each piece is there, not just that it is:

Cloud Run hosts the agent. It scales to zero when nothing's shipping and spins up on demand when a release fires — which is exactly the shape of this workload: idle most of the time, bursty when it isn't. We're not paying for an always-on server to wait for the occasional release. It also handed us a public HTTPS endpoint and container deploys with almost no ops work, which mattered a lot at hackathon speed.
Pub/Sub sits between the webhook and the agent, and it's the reason the system is robust instead of fragile. It gave us retries and a dead-letter queue for free, so a transient failure doesn't lose a release — the message just gets redelivered. Decoupling ingestion from processing this way is what let the webhook return instantly while the agent works asynchronously in the background. That "runs in the background" property is the whole premise of the hackathon, and Pub/Sub is what delivers it.
Vertex AI runs Gemini 3.5 Flash with the reliability and auth story we wanted — service-account credentials, no API keys floating around, and structured output we can gate real actions on.
Firestore is the agent's memory. Single-doc reads for the profile and config, a growing collection of decision records, and a featured-projects list the curator consults before every call. Being a managed, serverless document store meant our "memory layer" needed zero infrastructure — it just worked, and it scales with us.

Every stage also has its own error handling and fails in the safe direction. If the curator errors for any reason, it defaults to skip — because the one thing worse than missing a post is publishing something no one reviewed. The content writer, image generation, portfolio publisher, and LinkedIn drafter each fail independently: if the image generator hiccups, you still get the post; if the portfolio PR fails, the drafted post survives.

The bug that taught us the most

Our favorite lesson came from a bug that only showed up live. Publishing one release was creating three decision records — one "feature" and two "skips."

GitHub fires three webhooks for a single release (published, released, and created), each with a different delivery ID. Our idempotency check keyed on delivery ID, so all three sailed through. The first featured the project; the two right behind it saw it was now already featured and decided to skip. Three parallel Cloud Run instances, one release.

The fix was two-layered: only accept the published action, and behind that, an atomic release-level dedupe keyed on {repo}:{tag} using Firestore's create() (which throws if the doc already exists) rather than a check-then-write — because the duplicate webhooks arrive within milliseconds and a non-atomic check still races. Firestore's atomic create was the perfect tool for that; it's the kind of thing you only find by running the real system, not the tests.
**
The honest hard part

**We want to be straight about a boundary. Updating a portfolio "automatically" sounds simple until you realize real portfolios store projects in wildly different ways — hardcoded JSX, HTML cards, Markdown front-matter, a CMS, or actual JSON. Reliably editing arbitrary hand-written portfolio code, unattended, and pushing it to a live site is genuinely hard — and getting it wrong means breaking someone's live portfolio.

So we drew a line. When the agent can confidently detect where projects live, it prepares a format-matched pull request. When it can't, it prepares the content in a standalone file for the developer to place — and it never blindly merges code it isn't sure about. Knowing when to open a reviewable PR instead of acting autonomously is exactly the kind of judgment a production agent needs. Full unattended editing of any portfolio shape is our roadmap, not a claim we're pretending to have solved.

W*hat we learned*

Three things stuck with us. First, the hard part of an agent is the decision, not the plumbing — and making that decision legible is what makes it feel real. Second, the Google Cloud stack didn't just host our project, it shaped it: Pub/Sub's decoupling is what made "runs in the background" true, Cloud Run's scale-to-zero fit the bursty workload, and Firestore turned "memory" from an infrastructure project into a config detail. Third, live systems surprise you in ways unit tests never will — the triple-webhook bug passed every test we had.

We built this because we were tired of our own shipped work staying invisible. Now we publish a release, and the rest handles itself.

Built for the All Things Agentic Hackathon with Google ADK Gemini 3.5 Flash on Vertex AI, Cloud Run, Pub/Sub, and Firestore. — Aditi & Ameya

Top comments (0)