Inspiration
Opening a restaurant usually starts with a lease, and the lease is signed before anyone checks whether the neighborhood wants that kind of food. TasteFit gives founders and commercial brokers a written first answer: does this concept fit this area, where should it go, and what are the risks.
TasteFit started on GitHub as an MIT project with no CI, no tests and no post-deploy check (last pre-GitLab commit 8c465c1). People act on its verdict with real money, and nobody on the team does DevOps full time. So we asked: can agents look after the project when something happens, while a person still decides what gets merged?
What it does
The product. A founder enters a concept ("Korean fried chicken counter") and a place ("Austin, TX"). An LLM agent runs a tool loop over the official Qloo hackathon harness: find_tags, area_taste, competitors, hotspots, and a local site_math gravity model (demand center, competition center, catchment radius, and a suggested spot on a 41x41 grid that maximizes demand pull minus 0.7 times competitor pull). It writes an investment-style GO / ADJUST / NO-GO memo with a fit score, a map, a positioning plan, key risks and a Limits section. The memo cites only places, tags and numbers returned by Qloo, and no personal data is sent to Qloo.
Two read-only endpoints serve prebuilt city packs: /scan ranks the cells of a city where a concept fits best, and /scan/unit gives the highest monthly rent each tenant type can pay and still reach the city's official 3-year survival rate. It is calibrated on official data for Paris (SIRENE) and Chicago (business licences); other cities say "not calibrated", and cities whose map data fails a check against an official register (Philadelphia, Casablanca) are shipped as data-thin, with no numbers.
The GitLab side. Every commit goes through a 7-stage pipeline (test → build → security → package → deploy → verify → release, 15 jobs), and four custom GitLab Duo Agent Platform flows react to project events without being asked:
- Issue Planner (issue created): rewrites the issue as a scoped plan with acceptance criteria, sets labels and lists the questions a person must answer first.
- Pipeline Medic (any pipeline fails): reads the failing logs (a
log_analystsubagent), classifies the failure, and afixersubagent opens a minimal fix MR checked with the CI linter. It looks for an existing MR first and never opens fixes onmedic/orduo/refs, so it cannot loop. - Domain Reviewer (MR created, or bot assigned as reviewer): checks 9 product rules a generic reviewer cannot know (secrets, Qloo data terms, privacy, Nominatim policy, site-maths tests, memo honesty, stdlib-only server, protected security jobs, agent governance), posts inline suggestions and answers GO / ADJUST / NO-GO, like TasteFit itself.
- Release Scribe (pipeline on
mainpasses): writes release notes for restaurant owners with deployment proof and a security summary, opens a grouped issue for High/Critical findings, and notes on issues the release shipped.
GitLab's own Duo Developer flow turns assigned issues into MRs. Each flow runs as its own service account with an explicit toolset, and no flow has a merge tool.
GitLab stages covered (9):
- Plan: Issue Planner (#15); Duo Developer (#5).
- Create: Duo Developer feature MRs (!4); Medic fix MRs (!1, !9, !11).
- Verify: unit tests with JUnit and coverage,
rufflint,flows-lintof the flow files; Medic checks fixes with the CI linter. - Package: Docker image built, started and health-checked, pushed to the GitLab container registry; CycloneDX SBOM; cosign keyless signature verified in CI.
- Secure: SAST, secret detection, container scanning, dependency scanning, and
cve-gate(fails on Critical/High container findings not in the allowlist). - Release: Render deploy pinned to the commit SHA as the
productionenvironment; GitLab Releases with image digest and SBOM; Release Scribe notes. - Configure: environment-based config, CI/CD variables for deploy credentials, Dockerfile
HEALTHCHECKchecked in CI. - Monitor: smoke test waits until
/healthreturns the deployed SHA; daily scheduled rescan and uptime check with response-time metrics. - Govern: 9 rules in
AGENTS.mdenforced by Domain Reviewer,CODEOWNERS, and apolicy-guardjob that fails agent branches editing agent policy, CI scripts or the CVE allowlist.
How we built it
The app is a standard-library Python HTTP server that streams agent steps to a single-page front end; agent.py holds the LLM tool loop (any OpenAI-compatible endpoint with tool calling), geo.py the site maths. It ships as a Docker image.
- Pipeline (
.gitlab-ci.yml): 7 stages, 15 jobs:unit-tests,lint,flows-lint,image-build,semgrep-sast,secret_detection,dependency-scanning,container_scanning,cve-gate,sbom,sign-image,verify-signature,deploy-render,smoke-test,release. All 15 passed onmainpipeline 2935652252. A daily 06:00 UTC schedule rebuilds, rescans and checks that production is up. - Flows (
.gitlab/duo/flows/): published in the AI Catalog with event triggers; rules shared between humans and agents inAGENTS.mdandskills/tastefit-guardrails/.
The loop, measured. Time from a failed job to the Medic fix MR, on every real failure so far:
| Failure | Fix MR | Delay |
|---|---|---|
smoke-test on main |
!1 | 1 min 39 s |
release on main |
!9 | 1 min 56 s |
| lint on a research branch | !10 | 1 min 48 s |
lint on main |
!11 | 1 min 10 s |
Each got a Domain Reviewer GO and was merged by a person.
Seeded benchmark. We opened eight merge requests through the API, each with one planted problem and no hint in its title or description, and recorded what the agents did without stepping in. All seven planted problems were flagged (six NO-GO, one ADJUST) and the harmless change got GO; every verdict arrived within a minute. All three Medic fixes (!24, !25, !26) were correct and minimal; none edited a test to make it pass. On the duo/ branch that edited AGENTS.md, policy-guard went red and Medic correctly opened no fix.
Plan to production. Issue Planner planned #15 within 90 seconds of its creation and asked for decisions; a person answered and implemented it in !12; Domain Reviewer answered ADJUST (5 findings), then GO after the fix; it was merged, deployed, released as v1.96, and Release Scribe noted the shipment on #15.
Autonomy level: Supervised. Flows start on events, decide what to do, open MRs and issues, and avoid duplicates and loops by themselves. A person merges every change (and assigns the reviewer bot on bot-created MRs). We chose that on purpose: people act on TasteFit's memo with money.
Challenges we ran into
- A false failure in our own smoke test.
curl ... | grep -qfailed in CI withcurlexit 23:grep -qclosed the pipe underpipefail. Pipeline Medic diagnosed it and opened a one-line fix 1 min 39 s later. - A container image full of CVEs. Release Scribe's first notes reported 338 container findings (5 Critical, 67 High) and filed a grouped security issue (#2). The Alpine rebuild (!3) brought the image GitLab builds from the deployed commit to 0.
- A tool that rejected our calls.
create_vulnerability_issuerejected our calls, so Release Scribe groups findings in one issue instead. - Trigger edges. Bot-created MRs do not fire the MR-created trigger, so a person assigns Domain Reviewer on them.
- Our own policy had a gap. The benchmark showed the Reviewer answering only ADJUST on a memo told to give an exact street address, exactly as its rubric said. See below.
Accomplishments that we're proud of
- Four real failures, four fix MRs opened by Pipeline Medic in under 2 minutes each, without being asked.
- The reviewer said no to us, twice. It answered NO-GO when we skipped security jobs on docs-only commits (!5, rule 8), and NO-GO on a real bug: the Chicago rent ceiling was fitted under the Paris rent share; after the fix the restaurant ceiling went from $5,970 to $3,230 (!14).
- The benchmark found a policy gap, and a human closed it. We made rule 6 blocking in !27 (reviewed GO by the Reviewer itself), published the flow as 1.2.0, re-ran it, and the verdict became NO-GO.
- From 338 container findings to 0, and an image 141 MB → 100 MB compressed (−29%).
- Before vs after: no tests, no CI, no scans → 164 unit tests, a 7-stage signed and scanned pipeline, commit-verified deploys, and GitLab Releases with image digest and SBOM.
What we learned
- Event triggers are the difference between an assistant and a teammate: the value came from flows starting on their own when a pipeline failed or passed.
- Small, explicit toolsets keep agents safe. Leaving out a merge tool was the simplest way to keep a person in charge.
- A seeded benchmark tests the policy, not just the agent: the Reviewer followed its rubric exactly, and the rubric was what needed fixing.
- Be precise about what you scanned: Render builds its own image from the same Dockerfile at the same commit; we scan and sign the one GitLab builds.
What's next for TasteFit
- Triage the three Medium SAST findings (SSRF, CWE-918) and the 4 High dependency findings tracked in #12, and clear the stale Vulnerability Report records for the old Debian image (#9).
- Make bot-created MRs get a Domain Reviewer automatically, removing the last manual step before the merge.
- Run the exact image GitLab scans and signs on Google Cloud Run, so the scanned bytes are the ones that serve users.
- Calibrate the rent ceiling for more cities with official survival data.
Links
- GitLab repository: https://gitlab.com/gitlab-ai-hackathon/transcend-october-2026/43239248/tastefit
- Live app (Render): https://tastefit.onrender.com (free instance; the first request after idle can take about 50 seconds)
- Live app (Google Cloud): https://tastefit-776637567140.us-central1.run.app
- Original project: https://github.com/youssefbenzidiya-cmd/tastefit
Built With
- alpine-linux
- cosign
- cyclonedx
- docker
- gitlab
- gitlab-ci
- gitlab-container-registry
- gitlab-container-scanning
- gitlab-dependency-scanning
- gitlab-duo-agent-platform
- gitlab-sast
- gitlab-secret-detection
- google-cloud-run
- html
- javascript
- leaflet.js
- nominatim
- openstreetmap
- python
- qloo
- render
- ruff
- semgrep
Log in or sign up for Devpost to join the conversation.