georgia@build-log ~ $ cat portfolio.md
Georgia Harvey
👋Hi y’all, I am

Georgia

I sit between the people with the problem and the systems that could solve it, and I make sure to connect the two as the part in the middle.

Eight years of connecting systems, building the pieces that make them work together, and connecting the dots on client solutions that are forward looking, not just short term. Every build below opens up to show the architecture, the decisions, and usually a prototype you can run right here.

1Systems you already run
CRM and supportSalesforce, HubSpot, Zendesk
Payments and POSToast, Melio, billing
Files and spreadsheetsDropbox, Google Workspace
2What I do
Design and build the joindiscovery through launch
Add AI where it payswith a person in the loop
3What you are left with
One set of numbersthat everyone agrees on
Reports people readanswers, not raw exports
A team that runs itwithout needing me

The shape of most of my work

  1. 1Discovery
  2. 2Architecture
  3. 3Build
  4. 4Launch
  5. 5Handoff
Client delivery4

Shipped to enterprise customers

Scoped, built, taken to production, and handed to their team to run. Mock data or changed names are used throughout for privacy where necessary.

spark-ai-workflow-orchestrationProductboard client · production deliveryThousands of items a month

Turn a feedback firehose into weekly digests

The delivered Spark AI engagement for a Productboard enterprise customer - discovery through production go-live and handoff, turning a firehose of feedback into a weekly exec digest.

PythonLangChainLLM APIsREST APIs
1What customers said
Support tickets
Survey comments
Sales call notes
2What I built
Reads and tags itto their own taxonomy
Groups into themesSpark AI plus LangChain
3What leadership gets
Weekly summarysplit by product area
Their team runs ithanded over, not hosted

Thousands of comments a month, all read

The problem

This customer's product team received thousands of feedback items a month across channels. Nobody could read it all, so themes were missed and leadership flew blind on what customers actually wanted.

What changed

A production workflow that classifies and summarizes the full volume into a weekly digest by product area - and the customer's team was trained to own and extend it after handoff.

▸How it was builtProductboard client · production delivery
  1. Ran a discovery workshop with the customer to map their channels, feedback volume, and the product taxonomy the output had to match.
  2. Configured the Spark AI workflow and wrote Python glue to pull and normalize feedback from their sources before processing.
  3. Used LangChain for the multi-step conditional logic Spark didn't cover natively, then validated output quality against a labeled sample with the customer.
  4. Took it to production, documented the config, and trained their team to run and extend it - a deliberate handoff, not a dependency.

This was the actual customer engagement: scoping, delivery, and enablement end to end. The reusable architecture pattern behind it lives in the Solutions Architecture section.

▸ the delivered output - a weekly digest
Press generate to see the kind of digest the customer received.
geolocation-radius-validationPerigon · geolocation radius filterSupport tickets down 24.7%

Prove the location filter is actually correct

A news API sold "articles near this location". If the radius math is subtly wrong, customers get irrelevant results, never file a bug, and quietly stop trusting the feature.

PythonREST APIsJSON SchemaPostman
1What we shipped
Live search resultsarticles near a place
2How I checked
Distance re-measuredmy own maths, written apart
Edge cases replayedzero radius, boundary
3What we promised
Genuinely nearby
Wrongly includedcaught before launch

Checked against maths written separately

The problem

A radius filter that's subtly wrong doesn't throw an error - it just returns results that are almost right. Customers don't report "almost right," they just quietly pull full datasets and filter client-side instead, which spikes load and erodes trust in the API.

What changed

Independently proves the radius math is correct before it ships, so 150+ enterprise clients could trust the filter enough to actually use it - directly cutting support tickets 24.7%.

▸How it was builtPerigon · geolocation radius filter
  1. In VS Code, wrote a Python test script implementing the Haversine formula independently of the production code, as a ground-truth check.
  2. Called the live API with the new radius parameter, then asserted every returned article actually fell within the declared radius using the independent calculation.
  3. Built a Postman collection covering edge cases (boundary of radius, zero radius, antimeridian) for repeatable regression testing.
  4. Wrote the client-facing API docs and ran release communication to the full enterprise client base.

Built at Perigon before shipping a geolocation filter to 150+ enterprise accounts. This is the validation discipline that makes a customer trust a feature enough to actually adopt it - not just ship it.

▸ try it - this is the real formula, live
Actual Haversine math, computed in your browser - not a mockup.
legacy-data-migration-automationProductboard · enterprise onboardingWeeks to same day

Move years of messy records same day

Automated a manual, multi-week enterprise data migration into a same-day process with built-in validation and human-review flagging.

PythonpandasREST APIs
1What they had
Spreadsheet exportsyears of old records
Salesforce dumps
2What I built
Tidies and checksnames, dates, emails
Logs every decisionmoved or rejected, with why
3What they got
Moved the same day
Held for a humanwith the reason attached

Nothing questionable is quietly guessed

The problem

Every enterprise onboarding started with years of legacy feedback data in inconsistent formats - some CSV exports, some Salesforce dumps. Migrating it by hand took weeks and still shipped errors into the new system.

What changed

Collapses a multi-week manual migration into a same-day, repeatable process where bad records are caught and flagged, not discovered by the customer after go-live.

▸How it was builtProductboard · enterprise onboarding
  1. In VS Code, wrote a Python pipeline using pandas to ingest mixed CSV and Salesforce-export sources into one dataframe.
  2. Normalized fields (names, dates, emails), then validated each row against the target JSON schema for the Productboard REST API.
  3. Clean rows were batch-imported via the API; ambiguous rows were written to a review queue with the specific reason flagged.
  4. Logged every insert and rejection so the customer had a full audit of what moved and what needed a human.

Built at Productboard to onboard enterprise customers faster without shipping data errors into their new environment.

▸ try it - run the cleaner on messy rows
salesforce-productboard-two-way-syncProductboard client · enterprise deployment

Stop sales and product keeping two lists

A bi-directional integration connecting a client's Salesforce instance to Productboard - customer requests flowed in, roadmap decisions flowed back out, automatically.

PythonREST APIsWebhooksIdempotency
1Where sales works
Customer requestlogged in Salesforce
2What I built
Copies it acrossfield map, retry, error queue
Sends decisions backstatus onto the record
3What teams see
Request on the listnobody retypes it
Sales sees the callroadmap status, back in CRM

Both teams, one list, updated both ways

The problem

The client's sales team logged feature requests in Salesforce Opportunities, but product lived in Productboard. Someone re-typed every request by hand, and once product made a call, sales had no way of knowing without asking.

What changed

One integration keeps both systems in sync - requests land in Productboard automatically, and roadmap status flows back onto the Salesforce record, so both teams work off the same truth.

▸How it was builtProductboard client · enterprise deployment
  1. In VS Code, stood up a Python webhook listener to receive Salesforce Opportunity events in real time.
  2. On a flagged Opportunity, mapped the record's fields to a structured feature note and created it in Productboard via the Productboard REST API.
  3. Built the reverse path: a sync job read roadmap status and priority changes and wrote them back onto the originating Salesforce record through the Salesforce API.
  4. Added retry with exponential backoff and an error queue so a failed call never silently dropped a record - both teams could trust the data.

Built for a Productboard enterprise client whose sales and product teams were maintaining the same list twice. The kind of CRM-to-system-of-record bridge post-sales customers need across their whole stack.

▸ try it - simulate a sync cycle
Salesforce
Opportunity: Acme Corp
Productboard
awaiting sync...
Opportunity: "Bulk export API"-
Stage: Discovery-
Personal projects3

Systems I built and run

Built for the two Austin venues I co-own, and running there now. The code and the live demos are public.

venue-integrationpersonal projectLuna Rooftop and The LobbyTwo POS systems, one pipeline

Match every booking to what the table actually spent

Reservations-to-POS reconciliation across two different POS systems. A deterministic scoring engine matches each reservation to its check and records why, then reports minimums, no-shows, revenue by section, and deposits.

TypeScriptNode 22SQLiteREST APIs
1What we had
Table bookingsfrom TableList
Sales receiptsfrom Toast or Union POS
2What I built
Matching enginescores table, time, party
Reason for each matchso a number can be traced
3What owners see
Who hit the minimum
Who never showed up
Which section earnedrevenue by section

Runs nightly, one business date at a time

The problem

The booking system and the POS share no common identifier. A party moves tables, one order carries two checks, and a 1:30am check belongs to the previous business date.

What changed

A scored matcher pairs reservations to checks and records the reason for every match, so a disputed number can be traced back to the evidence rather than argued about.

▸How it was builtLuna Rooftop and The Lobby Social Bar, nightly
  1. Wrote one PosAdapter interface with two implementations, so a venue picks its POS in config rather than forking the codebase.
  2. Built a business date rule into the domain model: the night runs 6pm to 6am, so a 1:30am check belongs to the prior calendar day. Getting this wrong silently splits a night in half.
  3. The matcher scores each candidate on table, time window, party size, and whether the deposit shows up as a tender line, then assigns greedily so one check can never satisfy two reservations.
  4. Every match stores its reasons, and the leftovers surface as no-show, walk-in, or possible split rather than being dropped.

Built for the two venues I co-own because the reports we needed did not exist in either vendor's product. It runs against the real APIs and against recorded fixtures, so the whole pipeline runs offline with no credentials.

venue-table-spend-demopersonal projectNightly, per business dateVerified against the live API

Prove which tables hit their spend minimum

Walks orders to checks to selections and groups by table, because the sales screen only renders the check layer. Every money identity verified against the live API, not taken from the docs.

PythonREST APIsReconciliation
1Inside the POS
Table orderswhere the table is named
Receipts and itemschecks and selections
2What I built
Adds up a tableacross all its orders
Drops voided paymentsso nothing double counts
3What managers see
What the table spent
Minimum met or missed
Every item ordered

The tally no sales screen will do for you

The generated report for one recorded Friday night
The generated report for one recorded Friday night
The problem

The POS model puts money on the check and the table on the order one level up, so the table never appears on the sales screen at all.

What changed

Groups by table across orders, excludes voided and denied payments, and reconciles the money identities so the totals can be trusted against the vendor's own reports.

▸How it was builtPublic version of a nightly tool, synthetic data
  1. Verified against a real night that check.amount is item sales plus the card surcharge, so the number the vendor UI labels subtotal is not pure product sales.
  2. Found the double-count trap: one check carried a voided payment and a captured payment for the same amount, so naive summing inflates revenue.
  3. Made the spend basis configurable across four definitions, because the answer to "what did they spend" legitimately differs depending on who is asking.
  4. Wrote a reconciliation command that proves the pull is complete and the money identities hold, since the API exposes no sales summary to diff against.

The private version runs against a real venue account. This public repo is the same code against a synthetic fixture that reproduces every edge case that bit the original: table moves, split checks, voided payments, and an open check.

hr-compliance-automationpersonal projectWeekly since Aug 202670 employees, 20 contractors

Catch expiring certifications before they lapse

Scanned documents become a one-row-per-employee snapshot in Sheets plus drafted expiry alerts. Runs unattended weekly across two venues with different folder layouts, from one config per venue.

PythonGoogle SheetsApps ScriptScheduled job
1Where it lives
Scanned paperworkphone photos in Dropbox
Staff listnewest scheduling export
2What I built
Weekly jobreads filenames, not scans
Rebuilt from scratchso it cannot drift
3What managers get
Up to date trackerone row per employee
Reminder emailsdrafted, never sent

Unattended, every Thursday, across two venues

The weekly compliance snapshot, rendered from synthetic data
The weekly compliance snapshot, rendered from synthetic data
The problem

Documents arrive named three different ways for the same person across four folders, so a hand-maintained sheet cannot stay in sync with the folder it describes.

What changed

Rebuild from source rather than update in place, so drift is structurally impossible. Manual inactive marks persist in a separate ledger that survives every rebuild.

▸How it was builtTwo venues, weekly, unattended
  1. Hard rule enforced in code and asserted in tests: record presence flags only, read from filenames. No identification numbers or dates of birth are ever stored, and the scans are never opened.
  2. Roster comes from the newest scheduling export minus a persistent inactive ledger, so an owner marking someone inactive overrides the export forever.
  3. One config per venue describes that venue's folder layout and column set, so two structurally different venues run on one code path.
  4. Certifications inside 30 days become a drafted email grouped by urgency, which a manager reviews and sends. The system never sends on its own.

Production for two venues since August 2026. The public repo is the generalized, de-identified version: same code, same sheet layout, same scheduled prompt, with a synthetic roster.

Internal tools7

Tools I built to do the job better

Built between engagements, for scoping, account reviews, and integration debugging. Each one turns a task that used to need a senior engineer into something a team can run.

requirements-to-architecture-translatorProductboard · internal SA tooling

Turn a meeting request into a spec

Type a plain-language business ask, get back a structured technical spec - systems touched, triggers, field mappings, and edge cases - in a hosted internal tool.

ClaudePrompt templatesJavaScriptVercel
1What someone asks
The ask in plain wordstyped into a web box
2What I built
Saved instructionsversion controlled prompt
3What you get
First draft specsystems, fields, edge cases
On a shared canvasdropped into Figma

A first draft before the call ends

The problem

The hardest part of any implementation is the gap between what a stakeholder asks for in a meeting and a spec engineering can build from. That translation usually lives in one architect's head.

What changed

Turns a business sentence into a first-draft architecture spec in seconds, so solution sessions start from a shared artifact instead of a blank page.

▸How it was builtProductboard · internal SA tooling
  1. Built on Claude with a saved, version-controlled internal prompt template that enforces the output structure - systems, trigger, data flow, field map, edge cases.
  2. Wired in the Figma connector so the generated spec drops straight onto a shared design canvas the customer and engineers annotate together in the session.
  3. Wrapped it in a lightweight web interface (input box, example chips, rendered spec sheet) so a non-technical stakeholder can drive it.
  4. Hosted the interface on Vercel for a shareable internal URL - no local setup, always current.

Built at Productboard to speed up enterprise scoping calls. I was translating vague customer asks into integration specs constantly and wanted the first draft on screen, in a shared canvas, before the meeting ended.

▸ try it - describe a requirement
Salesforce to roadmap
Warehouse sync
Spec will render here.
Instant preview is pattern-matched and always available. "Run live with Claude" calls the real Claude API once per visit - real model output, not scripted.
config-review-ai-agentProductboard · account health reviews

Give every account an expert review

An AI agent that reviews a customer's configuration and returns prioritized gaps, risks, and recommended fixes - an architecture review that runs itself.

PythonClaudeAgent designREST APIs
1What we check
How it is set uppulled live from the API
2What I built
Expert revieweran agent plus a rubric
Weighs by severitynot a flat checklist
3What reviewers get
Ranked list of gaps
The fix for eachbecomes the review agenda

Every account gets the same expert review

The problem

Six months after go-live, most implementations quietly drift - integrations nobody owns, manual steps meant to be automated. Spotting it takes a senior architect manually combing the config, so it rarely happens until something breaks.

What changed

An agent inspects the live config, reasons about it against best practice, and returns a ranked list of gaps with fixes - so every account gets an expert review, not just the squeaky wheels.

▸How it was builtProductboard · account health reviews
  1. In Python, pulled an account's live configuration via the Productboard REST API as structured input for the agent.
  2. Gave a Claude API agent a tool to read config plus a rubric of best-practice checks, each weighted by severity.
  3. The agent reasons over the config, identifies gaps, and returns structured findings - issue, severity, and the specific recommended fix.
  4. Findings render as a ranked scorecard that becomes the agenda for the customer's architecture review.

Built at Productboard to make account reviews objective and scalable. Instead of a static checklist, the agent reasons about each unique config - the same review an SA would run, on every account.

▸ try it - check what's in place, watch the agent score it
▸ try it - describe a real config, let the agent find the gaps
This calls a real Claude agent on the config you describe - one live call per session.
ai-workflow-orchestration-pipelineProductboard · Spark AI, reusable patternWeeks to seconds

Sort thousands of customer comments automatically

Watch one real piece of customer feedback get cleaned, tagged, and routed automatically - the exact five-step pattern behind any "AI over messy customer data" workflow.

PythonLangChainLLM APIsREST APIs
1What comes in
Raw feedbacktickets, surveys, notes
2What it does
Tidied upone common shape
Tagged and groupedLangChain, swappable model
3What comes out
Summary per theme
Sent to the teamseconds, not a week

Five swappable stages, set up per customer

The problem

A support ticket, an NPS comment, and a sales call note all describe the same problem in three different formats. Nobody has time to read all of it, tag it consistently, and get it to the right team - so patterns get missed.

What changed

One message runs through five stages - clean it up, tag it, roll it into a theme, send it to the right team - fully automatically, in seconds instead of sitting in a queue for a week.

▸How it was builtProductboard · Spark AI, reusable pattern
  1. Abstracted the workflow into five swappable stages - ingest, normalize, classify, summarize, deliver - so each could be reconfigured per customer.
  2. In VS Code, wrote Python orchestration where the normalize and classify steps take config, not hardcoded rules.
  3. Used LangChain for the conditional multi-step logic and standardized the LLM API calls so any stage's model or prompt could swap without touching the rest.
  4. Documented it as an internal reference pattern so any engagement could start from the architecture instead of a blank file.

Built at Productboard as AI Technical Lead - the design abstraction that let one architecture serve many customers. The delivered version for a specific client lives in the Clients section.

▸ try it - watch one message move through all five stages
Press run to watch a real feedback message get cleaned, tagged, and routed.
integration-triage-diagnosticPerigon · enterprise API support

Fix a broken connection without an expert

Scenario: a customer's nightly Salesforce sync has failed three times this week with no obvious pattern. Walk the same questions a senior engineer would ask to reach the actual root cause.

Decision logicRunbook designREST APIsWebhooks
1What broke
The symptomwhat the customer reports
2What I built
Guided questionsthe ones a senior asks
Path recordedso the next person sees it
3What you get
The actual cause
The exact fix

Anyone can walk the expert path

The problem

When a customer integration breaks, the fix often depends on which engineer picks up the ticket - the fastest diagnostic path lives in a couple of senior people's heads, so resolution time is inconsistent and senior staff get pulled into repeat triage.

What changed

Encodes the expert debugging path into a guided flow, so first-touch resolution stops depending on who's available and senior engineers stop re-solving the same failures.

▸How it was builtPerigon · enterprise API support
  1. Catalogued the recurring integration failures across the enterprise API accounts into a set of symptom categories.
  2. For each symptom, mapped the branching questions a senior engineer actually asks to isolate the cause.
  3. Encoded that logic as a decision tree in JavaScript, where each leaf resolves to a specific root cause and the exact fix.
  4. Recorded the path taken on each run so resolutions were self-documenting for the next person.

Built from the escalation patterns I saw supporting Perigon's enterprise API accounts. A guided runbook like this cuts time-to-resolution and scales a support team's expertise across every integration a customer runs.

▸ try it - diagnose the Salesforce sync scenario above
customer-health-risk-dashboardSentieo / AlphaSense · product operations

Spot a customer at risk before renewal

One named account, real-looking usage signals, a computed risk score - then Claude writes the actual talking points a CSM would bring into the renewal conversation.

PythonScoring modelClaude
1What we track
How much they use itactive usage
How well connectedintegration depth
Are admins active
2What I built
Health scoreweighted, from real signals
3What teams see
Accounts slippingmonths before renewal
What to say to themone action per account

Warning months before the renewal call

The problem

By the time a renewal conversation reveals a customer is at risk, it's often too late. The warning signs were in the usage data months earlier, unread - and even when someone notices, turning a risk score into a concrete talking point takes time nobody has.

What changed

Scores account health from real adoption signals, then generates the specific recommendation a CSM can act on today - not just a number, an actual next step.

▸How it was builtSentieo / AlphaSense · product operations
  1. Defined the adoption signals that actually predicted retention (active usage, integration depth, admin engagement).
  2. In Python, built a weighted scoring model combining those signals into one composite health score per account.
  3. Pulled usage data through the platform's analytics/SQL layer and mapped each score band to a recommended intervention.
  4. Surfaced it as a dashboard the CS and product teams reviewed to prioritize outreach.

Built as Product Operations Manager at Sentieo (acquired by AlphaSense) to get ahead of churn. Directly the "proactively identify risks and opportunities within accounts" work a post-sales SA owns.

▸ try it - Acme Manufacturing, renews in 45 days
-
The score above is computed instantly from the sliders. This button asks Claude to turn that score into an actual recommendation - one live call per session.
tool-consolidation-roi-modelProductboard · expansion solutioningFour tools priced in one model

Put a dollar figure on tool overlap

Modeled from a real pattern at Productboard: a customer running four overlapping feedback and roadmap tools nobody had ever added up the true cost of.

PythonCost modelingROI logic
1Their current spend
Four overlapping toolslicences and integrations
2What I built
Cost comparisontoday versus one platform
3What sales shows
Monthly saving
Connections droppedintegrations retired

A business case with a real number

The problem

An enterprise customer was running a standalone feedback inbox, a separate roadmap tool, a survey tool, and a support-ticket tagger - four licenses, four integrations to maintain, and nobody had ever put a number on what the overlap actually cost.

What changed

Puts a real dollar figure on consolidating onto one platform - current spend vs. platform spend, plus the integrations retired - so the SA and Account Manager walk in with a business case, not a pitch.

▸How it was builtProductboard · expansion solutioning
  1. In Python, modeled this customer's current-state monthly spend and integration count across their four overlapping tools.
  2. Encoded the consolidation logic - which overlapping tools Productboard's platform absorbs, and the integration points that retire with them.
  3. Output projected platform cost, monthly savings, and the number of integrations eliminated.
  4. Walked the Account Manager through it live before the renewal call - the model became the leave-behind.

Built at Productboard after seeing this exact overlap on a real enterprise account. The "point solutions to a single platform" case a post-sales SA makes to grow an account, backed by numbers instead of enthusiasm.

▸ try it - this customer's actual current stack
analytics-warehouse-pipelineSentieo / AlphaSense · product analyticsAn afternoon to one query

Answer does heavy usage predict renewal

At Sentieo, the product team could see feature clicks but not which research usage patterns actually predicted renewal - because that data lived in three different systems.

SQLSnowflakeETL designData modeling
1Where it lived
Product usagethe app database
Billing recordsaccount tier
Renewal outcomesSalesforce
2What I built
Nightly gatheringincremental ETL job
One tidy table setfact and dimension tables
3What they get
Ask it yourselfone query, no engineer

An afternoon of spreadsheets becomes a query

The problem

Product usage lived in the app database, account tier lived in the billing system, and renewal outcomes lived in Salesforce. Answering "does research-heavy usage predict renewal" meant three exports and a afternoon in a spreadsheet, every single time someone asked.

What changed

A clean analytical schema, fed automatically from all three operational systems, lets product and CS self-serve that question - and any question like it - in a single query.

▸How it was builtSentieo / AlphaSense · product analytics
  1. Designed a dimensional schema (fact + dimension tables) joining product usage, account tier, and renewal outcomes - the three metrics the business actually needed together.
  2. Built an ETL job in Python to extract from all three operational systems incrementally and transform them into that schema.
  3. Loaded into a Snowflake-style warehouse and wrote the SQL views the analytics layer sat on.
  4. Exposed it so product and CS teams could self-serve usage-vs-renewal questions without a data engineer in the loop.

Built at Sentieo (acquired by AlphaSense) to answer the one question leadership kept asking that nobody could answer quickly. The exact data-flow work behind advising a customer on getting their data into Snowflake or Databricks.

▸ try it - run the question against the modeled schema
Usage by account tier
Q/Q active-account growth
Avg integrations per account
Pick a query above to run it against the modeled schema.
Back office automation5

The work nobody wants to do twice

Recurring internal work turned into something that runs on a schedule: documents rebuilt, updates drafted, action items chased, renewals flagged before they lapse.

colleague-ai-productivity-agentProductboard · internal team tooling

One team assistant that knows our accounts

Built and rolled out an internal AI assistant for the team's own workflow - grounded answers on accounts and activity, not a generic chatbot.

PythonLLM APIsPrompt designInternal tooling
1What you ask
A team questionone of six common kinds
2What I built
Matched templateversioned per question type
Pulls the real dataaccount and feature data
3What you get
A grounded answersame quality for everyone

One shared assistant, not six private tabs

The problem

The team kept asking the same categories of questions - "what happened on this account last week," "draft a follow-up to this client," "what's the status of this feature" - each person solving it their own way in their own ChatGPT tab.

What changed

A lightweight shared agent with a small set of grounded actions, wired to the same data sources already in use, with versioned prompt templates so answer quality stayed consistent across the team.

▸How it was builtProductboard · internal team tooling
  1. Catalogued the 5-6 question types the team asked repeatedly, then wrote a versioned Python prompt template for each.
  2. Wired each template to the actual data source it needed - Productboard API for feature status, CRM export for account activity.
  3. Called the Claude / GPT-4 API with the matched template so answers stayed grounded instead of generic.
  4. Shared it as one internal tool with version-controlled prompts, instead of everyone maintaining their own ChatGPT thread.

Built at Productboard so the team stopped re-solving the same prompt-engineering problem individually. Same pattern behind any "internal Slack bot that actually knows your data" a Rippling customer would want.

▸ try it - ask the agent
Summarize my week
Draft a follow-up email
What changed on Project Nova?
agent
Ready when you are.
Chips and typed questions above are instant/scripted. This button sends your typed question to a real Claude call - one live call per session.
internal-ops-document-automationThe Lobby & sister venue · personal operationsTwo venues, one template

Rebuild every venue handbook in one click

Built a document-generation pipeline for my own hospitality venues - the kind of internal process tooling a Chief of Staff builds, not requests.

PythonReportLabTemplating
1Written once
Policy texthandbooks, pay tables
Brandinglogo, colours, fonts
2What I built
Layout templatebuilt once in ReportLab
3What staff get
Finished handbookbranded PDF, both venues

Change the words, the documents rebuild

The problem

As co-owner and operator of two Austin venues, operational documents (handbooks, comp structures, compliance policies) needed rebranding and regenerating as the business evolved - redoing them by hand every time was slow and error-prone.

What changed

A pipeline regenerates fully formatted, branded PDFs from structured content on demand, with logo, color system, typography, and legal sections all driven from one source of truth.

▸How it was builtThe Lobby & sister venue · personal operations
  1. In VS Code, wrote a Python script using ReportLab to generate PDFs programmatically instead of editing Word docs by hand.
  2. Separated content (policy text, comp tables) from styling (logo, color, typography) into a single structured source file.
  3. Built the layout template once - headers, section breaks, legal footer - so any content change regenerates a fully branded document instantly.
  4. Reused the same pipeline across both venues by swapping the branding inputs, not rebuilding the template.

Built to solve my own operational pain running two venues - the same instinct that made me build backend tooling instead of doing repetitive work by hand at every company I've worked at.

▸ try it - regenerate a letterhead live
The Lobby Social Bar
Employee Handbook · 2026
board-update-auto-draftDesign study

Hand an exec a written board update

Pull metrics from wherever they already live into a first-draft board or investor update, so a leader edits a draft instead of assembling one from scratch every cycle.

REST APIsScheduled jobsClaude
1Where numbers live
Weekly metricsthe 5 to 8 already tracked
2What I built
Scheduled gatherruns before each cycle
Writes a first draftClaude, from a template
3What execs get
A written updatethe exec edits, not builds
Unusual movementsflagged for explanation

It drafts, it never sends

The problem

Most board decks lose a day of an exec's time every cycle just gathering numbers that already exist somewhere else in the business.

What changed

A scheduled job pulls the metrics leadership already tracks weekly and drafts the narrative summary automatically - flagging anything that moved notably so the exec knows what needs a story, and edits from there.

▸How it was builtDesign concept · applies the Spark AI pattern to exec reporting
  1. Designed a scheduled job to pull the same 5-8 metrics leadership already tracks weekly from wherever they live.
  2. Drop them into a structured template, then call the Claude API to draft the narrative summary paragraph.
  3. Flag anything that moved more than one standard deviation from trend so the exec knows what actually needs a story.
  4. The exec edits and adds color - the system drafts, it never sends anything on its own.

A direct application of the same ingest-classify-summarize orchestration pattern built at Productboard, aimed at a different audience - executive reporting instead of customer feedback.

▸ try it - populate a board slide
Monthly business update - auto-draft
$842k
ARR
1.8%
Net churn
34
Headcount
Narrative draft updates as you edit the numbers →
The line above is instant/scripted. This button asks Claude to write the actual board narrative from these numbers - one live call per session.
meeting-followthrough-agentDesign study

Make sure meeting commitments actually happen

Turn raw meeting notes into owned, tracked action items automatically - the unglamorous work that determines whether meetings actually move anything.

ClaudeEntity extractionREST APIsScheduled nudges
1What was said
Meeting notespasted or pulled in
2What I built
Finds the promiseswho owes what by when
No owner namedflagged, never guessed
3What happens next
A list with owners
Nudge on the daySlack or email

Nothing said out loud gets dropped

The problem

The gap in most meetings isn't decisions, it's follow-through - commitments made out loud that nobody re-surfaces until someone asks "did that happen?" three weeks later.

What changed

Paste meeting notes, get back owned action items automatically, with a nudge at the due date - nothing said out loud gets lost.

▸How it was builtDesign concept · internal process tooling
  1. Take raw meeting notes as input - paste, or auto-pulled from a notes tool.
  2. Run them through an LLM prompt tuned specifically to identify action-bearing sentences vs. general discussion.
  3. Assign an owner where one's named in the text; flag sentences with no clear owner for manual assignment.
  4. Push confirmed items to Slack/email with a nudge scheduled at the stated due date.

The instant version below uses simple keyword matching to demonstrate the extraction logic. The live button calls a real Claude prompt tuned for this exact task.

▸ try it - paste rough notes, extract action items
Instant uses keyword matching. Live sends the notes above to a real Claude call - one live call per session.
vendor-renewal-radarDesign studyAlerts at 45, 14, 3 days

Catch a bad contract before it auto-renews

A single view of every contract, subscription, and vendor renewal with automatic lead-time alerts - so nothing auto-renews or lapses by accident.

PythonScheduled alertsSpend tracking
1What we sign
Contract detailsvendor, date, notice, cost
2What I built
Daily date checkdays left versus notice
3What you get
Warning at 45 days
Then 14, then 3colour coded by urgency
One list, all vendorsinstead of every portal

Nothing renews by accident

The problem

A vendor auto-renews at a bad rate because nobody owned the 30-day notice window - a category of thing that quietly costs real money and is entirely preventable.

What changed

One source of truth for every contract's renewal date and notice period, with automatic escalating alerts as the window closes, color-coded by urgency.

▸How it was builtDesign concept · inspired by real venue vendor management
  1. Maintain one structured record per contract - vendor, renewal date, notice-period length, monthly cost.
  2. A scheduled Python job checks days-remaining against the notice period daily.
  3. Escalating alerts fire at 45/14/3 days out, color-coded by urgency, so nothing is a surprise.
  4. A single dashboard view replaces checking each vendor's portal manually.

Directly inspired by running vendor and lease contracts for two venues by hand - the exact kind of preventable miss a Chief of Staff or Solutions Architect is trusted to catch before it costs money.

▸ try it - live status against today's date
Status computed live from today's date (Jul 2, 2026) against each renewal deadline.
Side builds2

Problems I had, solved end to end

Built for myself rather than tolerated.

job-discovery-appIn active development

Swipe through jobs that actually fit you

A Swipe-style job discovery product from architecture through backend logic - full schema, 7 automated trigger functions, AI-powered resume parsing and job scoring.

ReactJavaScriptSupabaseClaudeLangGraphStripe
1What you upload
Your resumeuploaded once
2What I built
Reads your resumeinto skills and roles
Scores each postingagainst live jobs
3What you get
A short ranked listswipe through it daily
Queue refills nightlya daily cron job

It learns from what you swipe

The problem

Job search means manually reading hundreds of postings and re-typing the same info into every application - slow, repetitive, and nothing learns from what you actually liked.

What changed

A swipe interface backed by real scoring logic - upload a resume once, get matched and ranked postings daily, and the app learns from what you swipe.

▸How it was builtPersonal project · in active development
  1. In VS Code, designed the Supabase schema - users, profiles, filters, jobs, a scoring junction table, and an application log.
  2. Built resume upload that triggers Claude API parsing into structured JSON (skills, experience, target roles).
  3. Wrote a batched scoring pipeline in LangChain / LangGraph that matches candidates against live postings in groups of 10.
  4. Built the React swipe UI where actions write to state, and a daily cron refills each user's queue automatically.

Built to solve my own job search problem while job hunting - full-stack, end to end, not a tutorial project.

▸ try it - swipe the queue
visa-appointment-checker-botPersonal project · solved my own visa logisticsRuns unattended daily

Get alerted the second a slot opens

A Telegram bot that monitors Schengen visa appointment slots across consulates and pushes real-time availability alerts.

PythonREST APIsScheduled job
1What it watches
Consulate pagesseveral sites at once
2What I built
Checks on a timera cron schedule
Spots what changeddiffed against last check
3What you get
A text to your phoneTelegram, instantly

Runs unattended, day and night

The problem

Visa appointment slots open and disappear within minutes across multiple consulate sites - checking manually multiple times a day is impractical and slots are consistently missed.

What changed

The bot polls every consulate on a schedule and pushes an alert the instant a slot opens, so you never have to babysit a webpage.

▸How it was builtPersonal project · solved my own visa logistics
  1. In VS Code, wrote a Python script that polls each consulate's appointment API/page on a cron schedule.
  2. Parsed each response for open slots and diffed against the last known state to detect new availability.
  3. On a new slot, called the Telegram Bot API to push an instant alert to my phone.
  4. Stored credentials in environment variables, never hardcoded, and ran it unattended on a schedule.

Built to solve a real, personal logistics problem during my own visa process - small in scope, but it runs unattended and does its job every day.

▸ try it - replay a check cycle
$ waiting to run...
Simulated replay of real bot output - the live bot runs unattended on a schedule.