Skip to content
Dylan San GabrielDylan
Get in touch

GET /api/dylan

Dylan San Gabriel

I ran a phone store, then a restaurant floor. Today, I'm the engineer at RMH3 Dental Supply, where I built the system the company runs its orders, stock and shipping in, and I keep it running.I ran a phone store, then a restaurant floor. Today, I'm the engineer at RMH3 Dental Supply, where I built and run the ERP the company works in. Mostly TypeScript, Node and Postgres: integrations, data correctness, and the deploys.

Before this, software was something I had to use on a shift — a register, a POS, a count that never matched the shelf. Most of what I build now replaces something someone was doing by hand.

role
Software Engineer
location
Chantilly, VA · US Eastern
mostly
Mostly backend · TypeScript, Node, Postgres
open_to
Open to backend / full-stack roles
measured
August 2026
GET/api/dylan

Parameters

Request

curl "https://portfolio.dylansg0318.workers.dev/api/dylan?view=engineer"
source: src/lib/site.ts

Response200 OK · 2.0 KB

{
  "name": "Dylan San Gabriel",
  "role": "Software Engineer",
  "employer": {
    "name": "RMH3 Dental Supply",
    "site": "https://rmh3dental.com"
  },
  "summary": "I ran a phone store, then a restaurant floor. Today, I'm the engineer at RMH3 Dental Supply, where I built and run the ERP the company works in. Mostly TypeScript, Node and Postgres: integrations, data correctness, and the deploys.",
  "view": "engineer",
  "mostly": "Mostly backend",
  "stack": [
    "TypeScript",
    "Node",
    "Postgres"
  ],
  "location": "Chantilly, VA",
  "timezone": "US Eastern",
  "open_to": "Open to backend / full-stack roles",
  "looking_for": "A backend or full-stack role, remote or in Northern Virginia. If that's what you're hiring for, let me know.",
  "principles": [
    {
      "title": "A wrong number is worse than a crash.",
      "body": "If a page breaks, someone tells me that morning. If a number is only a little bit off, it gets used for a month before anyone checks. So most of the work goes into the second one."
    },
    {
      "title": "If they have to export it, it isn't finished.",
      "body": "Somebody dropping the data into a spreadsheet to finish their job means the screen didn't do what they needed. That's the part I go back and fix."
    },
    {
      "title": "Save what the vendor sent, read it later.",
      "body": "Every payload gets stored exactly as it came in, before anything touches it. When someone asks a different question six months later, the answer is already sitting there."
    }
  ],
  "links": {
    "email": "dylansg0318@gmail.com",
    "github": "https://github.com/Dylansg318",
    "linkedin": "https://www.linkedin.com/in/dylan-san-gabriel/",
    "code": "https://github.com/Dylansg318/Portfolio/tree/main/code",
    "resume": "https://portfolio.dylansg0318.workers.dev/resume",
    "resume_pdf": "https://portfolio.dylansg0318.workers.dev/resume.pdf"
  },
  "measured": "August 2026",
  "page": "https://portfolio.dylansg0318.workers.dev/"
}

Live: this is the answer from this site’s API, not a mock. Send re-runs the request with your parameters; if the answer names a page, it opens.

GET /api/work

One at work, one for myself, one on the side.

All projects

The internal ERP that runs a dental supply company

At work · Live · 2026

Everything the company does in a day, in one place. I'm the only engineer on it.

The company sold on six websites using six different tools that never agreed. I built the one system that runs it all, about 500 orders a day.One system for orders, inventory, repricing, shipping, service and books across six sales channels, built from scratch, about 500 orders a day.

Read the project

The ERP's warehouse map: a floor plan of the main building with named rooms and 147 numbered storage locations, a building switcher, a layout editor and a printable map.

Running a dozen AI coding sessions on one repo

Tools for myself · Live · 2026

What keeps five to ten coding agents on one repo from losing work.

I use AI helpers to write much of the code, like a chef running a fast kitchen. This is the system of rules and checks that keeps the plates right.How I use AI coding agents at scale without vibe-coding, auto-loading rules, hooks that block the dangerous operations, and review triggered by consequence.

Read the project

Split the check from a photo

On the side · In progress · 2026

The whole thing on my own, from the phone camera to the split.

Photograph the dinner receipt, say who ate what, and it tells everyone what they owe. It checks its own answer against the receipt's printed total and won't show a split that doesn't match.Photograph a receipt, tap who had what, get the split. Runs on your phone with no server, and refuses to show a split that doesn't add up.

Read the project

The splitter after reading the example receipt. On the left, the receipt it was pointed at and, below it, what each person owes: You $28.86, Friend $40.40, summing to the printed $69.26. On the right, the green "Adds up" verdict and the five line items with a name tapped onto each.
GET/api/work

Parameters

Request

curl "https://portfolio.dylansg0318.workers.dev/api/work"
source: src/content/projects/*

Response200 OK · 7.8 KB · Open /projects

{
  "category": "all",
  "count": 8,
  "items": [
    {
      "id": "internal-erp",
      "title": "The internal ERP that runs a dental supply company",
      "status": "live",
      "year": 2026,
      "category": "work",
      "role": "sole engineer · 13 daily users",
      "summary": "One system for orders, inventory, repricing, shipping, service and books across six sales channels, built from scratch, about 500 orders a day.",
      "summary_plain": "The company sold on six websites using six different tools that never agreed. I built the one system that runs it all, about 500 orders a day.",
      "stack": [
        "TypeScript",
        "Node.js",
        "Express",
        "React",
        "PostgreSQL",
        "Railway",
        "GitHub Actions",
        "Docker"
      ],
      "metrics": {
        "orders_day": "~520",
        "products": "32K",
        "sales_channels": "6",
        "daily_users": "13",
        "orders_ingested": "300K+",
        "price_changes_logged": "138K+",
        "automated_tests": "2,721",
        "scheduled_jobs": "180"
      },
      "page": "https://portfolio.dylansg0318.workers.dev/projects/internal-erp"
    },
    {
      "id": "agent-fleet",
      "title": "Running a dozen AI coding sessions on one repo",
      "status": "live",
      "year": 2026,
      "category": "work",
      "role": "sole engineer",
      "summary": "How I use AI coding agents at scale without vibe-coding, auto-loading rules, hooks that block the dangerous operations, and review triggered by consequence.",
      "summary_plain": "I use AI helpers to write much of the code, like a chef running a fast kitchen. This is the system of rules and checks that keeps the plates right.",
      "stack": [
        "Claude Code",
        "Node.js",
        "Git hooks",
        "GitHub Actions",
        "Jest",
        "Markdown"
      ],
      "metrics": {
        "commits_in_6_months": "14K",
        "parallel_sessions": "10+",
        "operating_docs": "300+",
        "models_cross_reviewing": "2"
      },
      "page": "https://portfolio.dylansg0318.workers.dev/projects/agent-fleet"
    },
    {
      "id": "second-brain",
      "title": "A second brain where the agent does the filing",
      "status": "live",
      "year": 2026,
      "category": "tool",
      "role": "solo · personal tooling",
      "summary": "A plain-markdown vault an AI librarian keeps for me — 738,000 messages indexed locally, nothing filed without my approval, no cloud.",
      "summary_plain": "A filing cabinet for my own notes, mail and messages that an AI assistant maintains for me. It proposes what needs doing next; I approve it. Everything stays on my Mac.",
      "stack": [
        "Claude Code",
        "Node.js",
        "SQLite",
        "FTS5",
        "Astro",
        "Markdown",
        "Obsidian"
      ],
      "metrics": {
        "messages_indexed_locally": "738K",
        "text_hidden_in_a_binary_blob": "88%",
        "notes_filed_without_approval": "0",
        "leaks_a_careful_scrub_missed": "3"
      },
      "page": "https://portfolio.dylansg0318.workers.dev/projects/second-brain"
    },
    {
      "id": "receipt-splitter",
      "title": "Split the check from a photo",
      "status": "wip",
      "year": 2026,
      "category": "tool",
      "role": "solo · side project",
      "summary": "Photograph a receipt, tap who had what, get the split. Runs on your phone with no server, and refuses to show a split that doesn't add up.",
      "summary_plain": "Photograph the dinner receipt, say who ate what, and it tells everyone what they owe. It checks its own answer against the receipt's printed total and won't show a split that doesn't match.",
      "stack": [
        "TypeScript",
        "Preact",
        "Tesseract.js",
        "Vite"
      ],
      "metrics": {
        "network_calls_after_load": "0",
        "wrong_splits_on_real_receipts": "0",
        "money_arithmetic": "integer cents"
      },
      "links": {
        "source": "https://github.com/Dylansg318/SplitApp",
        "play": "https://portfolio.dylansg0318.workers.dev/play/receipt-splitter"
      },
      "page": "https://portfolio.dylansg0318.workers.dev/projects/receipt-splitter"
    },
    {
      "id": "erp-test-automation",
      "title": "Testing an ERP by hand, then automating a cloud app",
      "status": "archived",
      "year": 2024,
      "category": "work",
      "role": "contributor · team of 10",
      "summary": "Two sprints on the CYDEO SDET program — fourteen user stories tested by hand against an ERP, then a Cucumber and Selenium suite against a cloud app.",
      "summary_plain": "Ten of us taught robots to re-test a business app every night, after testing it by hand first, which is how we found the bugs the robots would've missed.",
      "stack": [
        "Java",
        "Selenium",
        "Cucumber",
        "Gherkin",
        "REST Assured",
        "JUnit",
        "Maven",
        "Jenkins",
        "Jira"
      ],
      "links": {
        "source": "https://github.com/drwekilowa/B34_G11_Sprint_2CloudAuto"
      },
      "page": "https://portfolio.dylansg0318.workers.dev/projects/erp-test-automation"
    },
    {
      "id": "galaxy-defense",
      "title": "Galaxy Defense",
      "status": "archived",
      "year": 2020,
      "category": "game",
      "role": "solo",
      "summary": "A space shooter built in block code in high school. 790 lines, an economy, and a boss fight, with no arrays or classes available.",
      "summary_plain": "A game I built in high school using drag-and-drop coding blocks. It has a shop, upgrades, a boss, and a teleport ability, all made without the normal tools programmers use to keep code organised.",
      "stack": [
        "JavaScript",
        "Code.org Game Lab",
        "Blockly",
        "Canvas"
      ],
      "metrics": {
        "lines_of_generated_js": "790",
        "sprites_placed_by_hand": "17",
        "arrays_available": "0"
      },
      "links": {
        "play": "https://portfolio.dylansg0318.workers.dev/play/galaxy-defense"
      },
      "page": "https://portfolio.dylansg0318.workers.dev/projects/galaxy-defense"
    },
    {
      "id": "shotcall",
      "title": "Shotcall",
      "status": "live",
      "year": 2026,
      "category": "game",
      "role": "solo",
      "summary": "A daily puzzle. A ball bounces three times and drops into a pocket; you guess which one. Every miss shows you one more bounce.",
      "summary_plain": "Like Wordle, but with a bouncing ball. One puzzle a day: work out which pocket it ends up in. Guess wrong and it shows you one more bounce.",
      "stack": [
        "TypeScript",
        "Astro",
        "SVG",
        "Node"
      ],
      "metrics": {
        "board_shapes_searched": "170,356",
        "boards_that_survive": "1,037",
        "ball_positions_verified": "1.55M"
      },
      "links": {
        "play": "https://portfolio.dylansg0318.workers.dev/play/shotcall"
      },
      "page": "https://portfolio.dylansg0318.workers.dev/projects/shotcall"
    },
    {
      "id": "portfolio-site",
      "title": "This Portfolio",
      "status": "live",
      "year": 2026,
      "category": "tool",
      "role": "solo",
      "summary": "A content site that can also run games, built so the writing stays easy and the demos stay cheap.",
      "summary_plain": "This website. It has two voices, plain English and engineer, and a whole second mode where the portfolio becomes a help desk. Also it can run games.",
      "stack": [
        "Astro",
        "TypeScript",
        "Tailwind",
        "Cloudflare Workers",
        "MDX"
      ],
      "metrics": {
        "js_on_a_write_up": "0 KB",
        "hosting": "$0/mo",
        "demo_lanes": "2"
      },
      "links": {
        "source": "https://github.com/Dylansg318/Portfolio"
      },
      "page": "https://portfolio.dylansg0318.workers.dev/projects/portfolio-site"
    }
  ],
  "page": "https://portfolio.dylansg0318.workers.dev/projects"
}

Live: this is the answer from this site’s API, not a mock. Send re-runs the request with your parameters; if the answer names a page, it opens.

Excerpt · one of the hookscode/session-lock/bash-guard.cjs · 204–219
if (sub === 'add' && !addIsDryRun && !ALLOW_BROAD_ADD.test(cmd)) {
  const broadFlag =
    hasShortFlag(args, 'A') || hasLongFlag(args, '--all') ||
    hasShortFlag(args, 'u') || hasLongFlag(args, '--update');
  // `.` / `./` / `:/` as a whole token means "the entire tree". A path that
  // merely STARTS with a dot (.claude/hooks/x.cjs) is explicit and fine.
  const broadPath = positionals(args, new Set(['--chmod', '--pathspec-from-file']))
    .some((p) => p === '.' || p === './' || p === ':/' || p === '*');

  if (broadFlag || broadPath) {
    block(
      `BLOCKED: broad \`git add\` (${broadFlag ? 'flag' : 'pathspec'}) in a shared checkout.\n\n` +
      'About twenty agent sessions share this working tree and its git index.\n' +
      'A sweeping stage picks up their half-finished work and commits it under\n' +
      'your message — that is how, in one production incident, three dead\n' +
      'sessions left six dirty files that blocked every `git pull` for three days.\n\n' +

Thirteen sanitized production modules with their tests are public:github.com/Dylansg318/Portfolio/code

GET /api/experience

How I got here.

I managed a T-Mobile store, then ran front-of-house at a Korean BBQ restaurant. I was taking CS classes at George Mason the whole time, and finished a software testing bootcamp between shifts.

Jun 2025 — Present

Software Engineer · RMH3 Dental Supply

The only engineer at a family-run dental distributor. Built and run the in-house ERP that replaced off-the-shelf software, spreadsheets and inbox triage across six sales channels.

Aug 2022 — Jun 2025

Head Server · The Qui, Korean BBQ & Grill

Ran front-of-house and trained staff. Promoted twice within months. Studied for the SDET bootcamp on the side.

Jan 2021 — Aug 2022

Store Manager · Wireless Vision (T-Mobile)

Promoted from sales associate to store manager in ten months. Beat performance targets by 30% with perfect customer-satisfaction scores.

The longer story

GET/api/experience

Parameters

Request

curl "https://portfolio.dylansg0318.workers.dev/api/experience"
source: src/lib/site.ts

Response200 OK · 5.7 KB · Open /about

{
  "path": [
    {
      "id": "gmu",
      "when": "2020 – 24",
      "what": "Computer Science coursework, George Mason University",
      "note": "Dean's List. Four years of CS alongside a full-time job — everything below happened while I was in it. I left before the degree was conferred, so what's under it is what I have to show instead."
    },
    {
      "id": "tmobile",
      "when": "2021 – 22",
      "what": "T-Mobile — sales associate, then store manager",
      "note": "My first job. When the store manager was getting ready to leave he decided I was working harder than the rest of the floor and spent his last months training me to replace him. I got the job because the team trusted me to run the place, not because I was the best at selling phones."
    },
    {
      "id": "thequi",
      "when": "2022 – 25",
      "what": "The Qui, Korean BBQ & Grill — head server",
      "note": "Promoted twice within months. I was studying for the CYDEO SDET program between shifts — Selenium, Cucumber, REST Assured. That's where the testing habits came from. I still write the test before I trust the fix."
    },
    {
      "id": "rmh3",
      "when": "2025",
      "what": "RMH3 Dental Supply — hired off the restaurant floor",
      "note": "They were regulars. They watched me work a section, decided I'd be a good fit for their company, and offered me a job. I started in inventory to learn the products, then shipping, and made it known that the technical work was what I wanted."
    },
    {
      "id": "erp",
      "when": "2025 – now",
      "what": "From a customer-service spreadsheet to the ERP",
      "note": "Customer service was entirely online, so consolidating it meant a spreadsheet — and the spreadsheet turned into a system. Pricing came next, because winning the buy box by hand doesn't scale. The ERP grew out of those two problems and now runs the company."
    }
  ],
  "jobs": [
    {
      "title": "Software Engineer",
      "org": "RMH3 Dental Supply",
      "place": "Chantilly, VA",
      "start": "Jun 2025",
      "end": "Present",
      "summary": "The only engineer at a family-run dental distributor. Built and run the in-house ERP that replaced off-the-shelf software, spreadsheets and inbox triage across six sales channels.",
      "bullets": [
        "Sole engineer for the in-house ERP that replaced SellerCloud and a stack of spreadsheets: order intake from six sales channels, inventory, shipping, customer service, returns, invoicing and a two-way QuickBooks Desktop sync.",
        "Daily orders grew from about 270 to about 520 on a 32,000-product catalog with no added operations headcount.",
        "Replaced a morning pricing spreadsheet with a repricing engine that re-evaluates every product every 60 seconds with cooldowns, floors and a no-price-war rule; 138,000+ price changes, each logged with its reason.",
        "Integrated eBay, Amazon SP-API, Shopify, Walmart, Net32 and three carriers: 300,000+ orders ingested, every raw payload stored before parsing, and a source-scan guard so channel code can never write the product catalog.",
        "Put barcodes and scanners on a pen-and-paper warehouse: GS1 parsing, a browser camera scanner, a JSON-to-ZPL label renderer and a self-updating Windows print agent with crash-loop rollback.",
        "Parsed a year of UPS and FedEx invoices into the ERP and surfaced about $26K a year in avoidable address-correction, re-weigh, printer-rental and late fees.",
        "Reconcile the ERP against QuickBooks at line level and settle each discrepancy once in a claims ledger; 2,721 automated tests and ~180 checkpointed scheduled jobs keep app and books in agreement through redeploys.",
        "Run 5–10 parallel Claude Code sessions on one repository behind file locks, hooks that block unrecoverable git operations, and second-model review of consequential changes."
      ]
    },
    {
      "title": "Head Server",
      "org": "The Qui, Korean BBQ & Grill",
      "place": "Fairfax, VA",
      "start": "Aug 2022",
      "end": "Jun 2025",
      "summary": "Ran front-of-house and trained staff. Promoted twice within months. Studied for the SDET bootcamp on the side.",
      "bullets": [
        "Ran front-of-house and trained new servers; promoted twice within months. Completed the CYDEO SDET program alongside full-time shifts."
      ]
    },
    {
      "title": "Store Manager",
      "org": "Wireless Vision (T-Mobile)",
      "place": "Vienna, VA",
      "start": "Jan 2021",
      "end": "Aug 2022",
      "summary": "Promoted from sales associate to store manager in ten months. Beat performance targets by 30% with perfect customer-satisfaction scores.",
      "bullets": [
        "Sales associate promoted to store manager in ten months; ran staffing, cash and inventory, beat targets by 30% over multiple months and raised customer satisfaction from 8.0 to 10/10."
      ]
    }
  ],
  "education": [
    {
      "title": "SDET Bootcamp",
      "org": "CYDEO",
      "place": "Tysons, VA",
      "when": "Feb – Oct 2024",
      "detail": "Java, Selenium, Cucumber/Gherkin, REST Assured, SQL, Jenkins; two ten-person sprint teams automating live web apps."
    },
    {
      "title": "Computer Science coursework",
      "org": "George Mason University",
      "place": "Fairfax, VA",
      "when": "2020 – 2024",
      "detail": "Dean's List. Object-oriented programming, game design, essentials of CS; left before the degree was conferred."
    }
  ],
  "certifications": [
    {
      "name": "Programming using JavaScript",
      "issuer": "Certiport",
      "id": "57SR-4Tp8"
    },
    {
      "name": "Programming using Python",
      "issuer": "Certiport",
      "id": "uadx-XMRJ"
    }
  ],
  "page": "https://portfolio.dylansg0318.workers.dev/about"
}

Live: this is the answer from this site’s API, not a mock. Send re-runs the request with your parameters; if the answer names a page, it opens.

GET /api/away-from-work

Cooking, music, one more side project.

I cook, I play a bit of music, and I'm always trying a restaurant I haven't been to yet. There's usually a side project going that I talked myself into.

Galaxy Defense

2020 · Where it started

A high-school game with a shop, upgrades, and a boss fight. Still playable.

Galaxy Defense running: the player's ship at the bottom, two green aliens descending, three green bunkers, and the lives, points, bombs and FTL readouts.

Take a look around my help desk

GET/api/away-from-work

Parameters

Request

curl "https://portfolio.dylansg0318.workers.dev/api/away-from-work?playable=true"
source: src/lib/site.ts · src/content/projects/galaxy-defense

Response200 OK · 888 B · Open /play/galaxy-defense

{
  "note": "I cook, I play a bit of music, and I'm always trying a restaurant I haven't been to yet. There's usually a side project going that I talked myself into.",
  "interests": [
    "cooking",
    "a bit of music",
    "restaurants I haven't tried yet",
    "one more side project"
  ],
  "first_program": {
    "id": "galaxy-defense",
    "title": "Galaxy Defense",
    "year": 2020,
    "status": "archived",
    "summary": "A space shooter built in block code in high school. 790 lines, an economy, and a boss fight, with no arrays or classes available.",
    "play": "https://portfolio.dylansg0318.workers.dev/play/galaxy-defense",
    "page": "https://portfolio.dylansg0318.workers.dev/projects/galaxy-defense"
  },
  "help_desk": "https://portfolio.dylansg0318.workers.dev/desk",
  "playable": true,
  "page": "https://portfolio.dylansg0318.workers.dev/play/galaxy-defense"
}

Live: this is the answer from this site’s API, not a mock. Send re-runs the request with your parameters; if the answer names a page, it opens.

POST /api/contact

What I'm looking for.

A backend or full-stack role, remote or in Northern Virginia. If that's what you're hiring for, let me know. The console on the right sends me a message; email works too.

POST/api/contact

Body

Request

curl -X POST "https://portfolio.dylansg0318.workers.dev/api/contact" \
  -H "content-type: application/json" \
  -d '{"from":"","message":""}'
source: src/pages/api/contact.ts

ResponseNot sent · 120 B

{
  "to": "dylansg0318@gmail.com",
  "subject": "Hello from your portfolio",
  "message": "(waiting for your message)"
}

Live: the route validates for real. Until mail delivery is configured it answers 503 with a mailto link, and Send opens it with your message in it.