<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:dc="http://purl.org/dc/elements/1.1/">
  <channel>
    <title>DEV Community: Arek H</title>
    <description>The latest articles on DEV Community by Arek H (@arek_h).</description>
    <link>https://dev.to/arek_h</link>
    <image>
      <url>https://media2.dev.to/dynamic/image/width=90,height=90,fit=cover,gravity=auto,format=auto/https:%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F3228182%2Fef51198d-c0cc-4ea5-bce1-070c1b10aed4.jpg</url>
      <title>DEV Community: Arek H</title>
      <link>https://dev.to/arek_h</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/arek_h"/>
    <language>en</language>
    <item>
      <title>I Built a Full-Stack B2B Marketplace in 3 Days With V0, Next.js 16, and Vercel's AWS Integration — Here's What Actually Happened</title>
      <dc:creator>Arek H</dc:creator>
      <pubDate>Sat, 27 Jun 2026 08:20:08 +0000</pubDate>
      <link>https://dev.to/arek_h/i-built-a-full-stack-b2b-marketplace-in-3-days-with-v0-nextjs-16-and-vercels-aws-integration--4ma4</link>
      <guid>https://dev.to/arek_h/i-built-a-full-stack-b2b-marketplace-in-3-days-with-v0-nextjs-16-and-vercels-aws-integration--4ma4</guid>
      <description>&lt;p&gt;&lt;em&gt;The honest account: what worked, what broke, and what I'd do differently.&lt;/em&gt;&lt;/p&gt;




&lt;p&gt;Three days. One B2B Dutch-auction marketplace. Here's the realistic version of what building with V0, Next.js 16 App Router, Aurora PostgreSQL, and DynamoDB actually looks like when you're going fast.&lt;/p&gt;

&lt;p&gt;This is not a "look how smooth AI coding is" post. It's the honest version.&lt;/p&gt;




&lt;h2&gt;
  
  
  The Stack
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Frontend:&lt;/strong&gt; Next.js 16 (App Router), Tailwind CSS, shadcn/ui, generated by V0&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Backend:&lt;/strong&gt; Next.js API routes, TypeScript&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Databases:&lt;/strong&gt; Aurora PostgreSQL (relational) + DynamoDB (atomic inventory)&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Auth:&lt;/strong&gt; Vercel OIDC Federation → AWS IAM (zero stored passwords)&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Deployment:&lt;/strong&gt; Vercel&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;AI tools:&lt;/strong&gt; V0 for UI generation&lt;/li&gt;
&lt;/ul&gt;




&lt;h2&gt;
  
  
  V0: What It's Actually Good At
&lt;/h2&gt;

&lt;p&gt;Let me start with the honest assessment. V0 is genuinely impressive at one specific thing: generating multi-page UI from a detailed prompt and producing something that's 70–80% of the way to production-quality on the first pass.&lt;/p&gt;

&lt;p&gt;Here's the prompt structure that worked:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Build [app name]. DO NOT provision any databases.

WHAT IT DOES: [one paragraph, clear]
BUILD THESE PAGES: [numbered list, each with specific UI requirements]
DESIGN DIRECTION: [specific hex colors, font choices, component library]
DO NOT: [explicit list of what not to do]
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The "DO NOT" section is not optional. V0 will try to add authentication, spin up a database, create server actions, and add 47 npm packages you didn't ask for if you don't explicitly forbid it. Write the constraint list first.&lt;/p&gt;

&lt;p&gt;The result after one well-structured prompt:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Dark navy (#0a0f1e) background with amber (#f59e0b) pricing&lt;/li&gt;
&lt;li&gt;4 pages with correct navigation&lt;/li&gt;
&lt;li&gt;Correct shadcn/ui component usage&lt;/li&gt;
&lt;li&gt;Countdown timers in monospace font&lt;/li&gt;
&lt;li&gt;Low-stock red indicators&lt;/li&gt;
&lt;li&gt;Crossed-out start prices + large current price display&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Genuinely good output. The thing that V0 gets right is visual design decisions — the layout hierarchy, the card structure, the urgency indicators. These are the things that take hours to hand-craft and minutes to generate.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;What V0 doesn't handle well:&lt;/strong&gt; data wiring. V0's proxy routes work for simple cases but any non-trivial API shape requires going back and being very explicit. Prompt 2 (wiring to real data) required more back-and-forth than Prompt 1 (building the UI).&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;The annotation feature is underused.&lt;/strong&gt; In V0 Max, you can click any element in the preview and leave an annotation. "Make this number bigger." "This progress bar should be red when below 20%." This is dramatically faster than re-prompting the whole component. Use it.&lt;/p&gt;




&lt;h2&gt;
  
  
  The Next.js 16 Breaking Change Nobody Warned Me About
&lt;/h2&gt;

&lt;p&gt;This one cost an hour.&lt;/p&gt;

&lt;p&gt;In Next.js 15+, route params are now a Promise. If you have a route like &lt;code&gt;app/api/drops/[dropId]/route.ts&lt;/code&gt;, the params signature changed:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="c1"&gt;// Next.js 14 (old)&lt;/span&gt;
&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="k"&gt;async&lt;/span&gt; &lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;GET&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
  &lt;span class="nx"&gt;req&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;NextRequest&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;params&lt;/span&gt; &lt;span class="p"&gt;}:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nl"&gt;params&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;dropId&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kr"&gt;string&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;dropId&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;params&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="c1"&gt;// fine&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="c1"&gt;// Next.js 16 (current)&lt;/span&gt;
&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="k"&gt;async&lt;/span&gt; &lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;GET&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
  &lt;span class="nx"&gt;req&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;NextRequest&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;params&lt;/span&gt; &lt;span class="p"&gt;}:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nl"&gt;params&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;Promise&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;dropId&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kr"&gt;string&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;dropId&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;params&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="c1"&gt;// required&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The error message when you get this wrong is:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="nx"&gt;Type&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;typeof import("...route")&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt; &lt;span class="nx"&gt;does&lt;/span&gt; &lt;span class="nx"&gt;not&lt;/span&gt; &lt;span class="nx"&gt;satisfy&lt;/span&gt; &lt;span class="nx"&gt;the&lt;/span&gt; &lt;span class="nx"&gt;constraint&lt;/span&gt; 
&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;RouteHandlerConfig&amp;lt;"/api/drops/[dropId]"&amp;gt;&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This error is not particularly clear about what's actually wrong. The fix is &lt;code&gt;await params&lt;/code&gt; everywhere you destructure route segment params. I had three routes that needed fixing.&lt;/p&gt;




&lt;h2&gt;
  
  
  The Vercel × AWS Integration: Better Than Expected, With Caveats
&lt;/h2&gt;

&lt;p&gt;The Vercel AWS Marketplace integration is genuinely slick. You click through a wizard in the Vercel dashboard and it:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Provisions an Aurora PostgreSQL cluster (or DynamoDB table)&lt;/li&gt;
&lt;li&gt;Creates an IAM role with least-privilege policies&lt;/li&gt;
&lt;li&gt;Sets up OIDC federation between Vercel and AWS&lt;/li&gt;
&lt;li&gt;Populates your environment variables automatically&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The result: your serverless functions authenticate to AWS databases using short-lived OIDC tokens with zero stored credentials. No Secrets Manager, no password rotation, no long-lived access keys.&lt;/p&gt;

&lt;p&gt;The code is clean:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;awsCredentialsProvider&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;@vercel/functions/oidc&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;Signer&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;@aws-sdk/rds-signer&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;signer&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;Signer&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
  &lt;span class="na"&gt;hostname&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;process&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;env&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;PGHOST&lt;/span&gt;&lt;span class="o"&gt;!&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="na"&gt;port&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nc"&gt;Number&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;process&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;env&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;PGPORT&lt;/span&gt; &lt;span class="o"&gt;??&lt;/span&gt; &lt;span class="mi"&gt;5432&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
  &lt;span class="na"&gt;username&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;process&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;env&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;PGUSER&lt;/span&gt;&lt;span class="o"&gt;!&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="na"&gt;region&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;process&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;env&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;AWS_REGION&lt;/span&gt;&lt;span class="o"&gt;!&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="na"&gt;credentials&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nf"&gt;awsCredentialsProvider&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
    &lt;span class="na"&gt;roleArn&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;process&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;env&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;AWS_ROLE_ARN&lt;/span&gt;&lt;span class="o"&gt;!&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="na"&gt;clientConfig&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;region&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;process&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;env&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;AWS_REGION&lt;/span&gt; &lt;span class="p"&gt;},&lt;/span&gt;
  &lt;span class="p"&gt;}),&lt;/span&gt;
&lt;span class="p"&gt;});&lt;/span&gt;

&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;pool&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;Pool&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
  &lt;span class="na"&gt;host&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;process&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;env&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;PGHOST&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="na"&gt;password&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nx"&gt;signer&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;getAuthToken&lt;/span&gt;&lt;span class="p"&gt;(),&lt;/span&gt; &lt;span class="c1"&gt;// fresh token per connection&lt;/span&gt;
  &lt;span class="na"&gt;ssl&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;rejectUnauthorized&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;false&lt;/span&gt; &lt;span class="p"&gt;},&lt;/span&gt;
&lt;span class="p"&gt;});&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;The caveats:&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;1. Environment variable prefix chaos.&lt;/strong&gt; When you install two AWS integrations (we installed both Aurora and DynamoDB), the env var prefixes differ. Aurora uses no prefix (&lt;code&gt;PGHOST&lt;/code&gt;, &lt;code&gt;AWS_ROLE_ARN&lt;/code&gt;). DynamoDB, when installed second, gets a prefix that depends on what you name it during setup — ours ended up as &lt;code&gt;DYB_&lt;/code&gt; (&lt;code&gt;DYB_AWS_ROLE_ARN&lt;/code&gt;, &lt;code&gt;DYB_DYNAMODB_TABLE_NAME&lt;/code&gt;). This isn't documented prominently. Check your actual &lt;code&gt;.env.local&lt;/code&gt; after running &lt;code&gt;vercel env pull&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;2. Development environment checkbox.&lt;/strong&gt; When connecting integrations to your project, there's a checkbox for which environments (Production, Preview, Development) get the env vars. It's easy to miss Development. If &lt;code&gt;vercel env pull&lt;/code&gt; returns only &lt;code&gt;VERCEL_OIDC_TOKEN&lt;/code&gt;, this is why — go back to the integration settings and check Development.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;3. Local OIDC auth depends on the IAM role trust policy.&lt;/strong&gt; The &lt;code&gt;VERCEL_OIDC_TOKEN&lt;/code&gt; that &lt;code&gt;vercel env pull&lt;/code&gt; injects locally is a development token from a different issuer than the production token. Whether it works locally depends on whether the IAM role's trust policy includes the development issuer. Ours didn't initially, so &lt;code&gt;npm run db:test&lt;/code&gt; failed locally even though production worked fine. The pragmatic fix: add &lt;code&gt;dotenv&lt;/code&gt; to &lt;code&gt;aurora.ts&lt;/code&gt; so the module works in both environments, and test primarily against the deployed URL.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;4. The &lt;code&gt;dotenv&lt;/code&gt; import must precede all AWS imports.&lt;/strong&gt; In ES modules, static imports are hoisted and executed before runtime code — including &lt;code&gt;config()&lt;/code&gt;. This means:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="c1"&gt;// WRONG: AWS SDK is imported before .env.local loads&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;awsCredentialsProvider&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;@vercel/functions/oidc&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;config&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;dotenv&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="nf"&gt;config&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt; &lt;span class="na"&gt;path&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;.env.local&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt; &lt;span class="p"&gt;});&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;





&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="c1"&gt;// CORRECT: dotenv first&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;config&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;dotenv&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="nf"&gt;config&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt; &lt;span class="na"&gt;path&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;.env.local&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt; &lt;span class="p"&gt;});&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;awsCredentialsProvider&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;@vercel/functions/oidc&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This caused a &lt;code&gt;Value null at 'roleArn'&lt;/code&gt; error that took longer than it should have to diagnose.&lt;/p&gt;




&lt;h2&gt;
  
  
  Schema Migration Without a Migration Tool
&lt;/h2&gt;

&lt;p&gt;With a 3-day deadline, there was no time to set up Prisma or Drizzle. The approach: a single &lt;code&gt;/api/migrate&lt;/code&gt; route protected by a &lt;code&gt;MIGRATE_SECRET&lt;/code&gt; env var. Hit it once after deploy and all tables are created:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="k"&gt;async&lt;/span&gt; &lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;POST&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;req&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;NextRequest&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;secret&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;req&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;json&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
  &lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;secret&lt;/span&gt; &lt;span class="o"&gt;!==&lt;/span&gt; &lt;span class="nx"&gt;process&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;env&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;MIGRATE_SECRET&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nx"&gt;NextResponse&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;json&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt; &lt;span class="na"&gt;error&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;unauthorized&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt; &lt;span class="p"&gt;},&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;status&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;401&lt;/span&gt; &lt;span class="p"&gt;});&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;
  &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nf"&gt;query&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;`CREATE TABLE IF NOT EXISTS brands (...)`&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nf"&gt;query&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;`CREATE TABLE IF NOT EXISTS lots (...)`&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="c1"&gt;// etc.&lt;/span&gt;
  &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nx"&gt;NextResponse&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;json&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt; &lt;span class="na"&gt;ok&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt; &lt;span class="p"&gt;});&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This is not a production pattern. It's a hackathon pattern that works fine when your schema is stable and you control when migrations run. For a demo with a 3-day deadline, it's the right call.&lt;/p&gt;




&lt;h2&gt;
  
  
  The Self-Healing Demo Problem
&lt;/h2&gt;

&lt;p&gt;Dutch-auction drops expire. A 3-hour drop is great for live demo urgency but terrible for a submitted hackathon project that judges might visit at 11pm three days later.&lt;/p&gt;

&lt;p&gt;The fix: a &lt;code&gt;/api/ensure-drops&lt;/code&gt; endpoint that checks how many live, unexpired drops exist and reseeds automatically if the count falls below 3:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="k"&gt;async&lt;/span&gt; &lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;GET&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;countResult&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;query&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;count&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kr"&gt;string&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="s2"&gt;`SELECT COUNT(*) AS count FROM drops
     WHERE status = 'live' AND ends_at &amp;gt; NOW()`&lt;/span&gt;
  &lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;liveCount&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;parseInt&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;countResult&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;].&lt;/span&gt;&lt;span class="nx"&gt;count&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;10&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;liveCount&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;=&lt;/span&gt; &lt;span class="mi"&gt;3&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nx"&gt;NextResponse&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;json&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt; &lt;span class="na"&gt;seeded&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;false&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;liveDrops&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;liveCount&lt;/span&gt; &lt;span class="p"&gt;});&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;
  &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nf"&gt;seedAllDrops&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt; &lt;span class="c1"&gt;// creates 6 new drops with fresh timers&lt;/span&gt;
  &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nx"&gt;NextResponse&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;json&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt; &lt;span class="na"&gt;seeded&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;liveDrops&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;6&lt;/span&gt; &lt;span class="p"&gt;});&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The frontend calls this silently on every page load via &lt;code&gt;useEffect&lt;/code&gt;. A Vercel cron job calls it daily as a backup. Judges always see a live marketplace regardless of when they visit.&lt;/p&gt;

&lt;p&gt;The cron configuration in &lt;code&gt;vercel.json&lt;/code&gt; (note: Hobby plan limits to daily frequency):&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight json"&gt;&lt;code&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"crons"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"path"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"/api/ensure-drops"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"schedule"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"0 0 * * *"&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;






&lt;h2&gt;
  
  
  What the Final Build Looks Like
&lt;/h2&gt;

&lt;p&gt;After 3 days:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Homepage with 6 live drop cards, real-time decaying prices, countdown timers&lt;/li&gt;
&lt;li&gt;Drop detail page with live price, claim form, activity feed, Dutch auction explainer&lt;/li&gt;
&lt;li&gt;Brand analytics dashboard with recovery rate table and chart&lt;/li&gt;
&lt;li&gt;Full dual-write claim endpoint (DynamoDB atomic decrement → Aurora transaction)&lt;/li&gt;
&lt;li&gt;Self-healing seed system so the demo is always live&lt;/li&gt;
&lt;li&gt;OIDC authentication with no stored passwords&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The thing I'm most satisfied with is the claim handler. It does exactly one thing — decrement inventory atomically, record the transaction relationally — and it does it correctly under concurrent load. That's the core of the product.&lt;/p&gt;

&lt;p&gt;The thing I'd do differently: start the database connectivity earlier. The OIDC auth quirks and env var prefix confusion cost nearly a day. If I'd run &lt;code&gt;vercel env pull&lt;/code&gt; and confirmed the variables immediately after installation, the rest of the build would have been smoother.&lt;/p&gt;




&lt;h2&gt;
  
  
  Practical Tips If You're Building on This Stack
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;Use V0 Max for complex multi-page builds.&lt;/strong&gt; The quality difference from Mini is significant. For simple components, Mini is fine.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Check &lt;code&gt;vercel env pull&lt;/code&gt; output immediately after installing any integration.&lt;/strong&gt; Note the exact variable names — they may not be what you expect.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Put &lt;code&gt;dotenv&lt;/code&gt; at the top of any module that initialises AWS clients.&lt;/strong&gt; Don't rely on the calling code to load it first.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Test your API routes against the deployed Vercel URL, not locally, when using OIDC auth.&lt;/strong&gt; Local OIDC works if the role trust policy includes the dev issuer; production OIDC always works.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;&lt;code&gt;await params&lt;/code&gt; in all App Router route handlers.&lt;/strong&gt; It's a breaking change in Next.js 15+. Grep for &lt;code&gt;{ params }:&lt;/code&gt; in your route files and add &lt;code&gt;await&lt;/code&gt; before you deploy.&lt;/p&gt;




&lt;p&gt;&lt;em&gt;ClearLot was built for the H0: Hack the Zero Stack hackathon. Stack: Next.js 16, TypeScript, Tailwind, shadcn/ui, V0, Aurora PostgreSQL, DynamoDB, Vercel. Live at &lt;a href="https://clearlot.vercel.app" rel="noopener noreferrer"&gt;clearlot.vercel.app&lt;/a&gt;.&lt;/em&gt;&lt;/p&gt;

</description>
      <category>h0hackathon</category>
      <category>aws</category>
      <category>hackathon</category>
      <category>vercel</category>
    </item>
    <item>
      <title>Finding the Root Cause of Production Incidents in Seconds with GitLab Orbit &amp; AI</title>
      <dc:creator>Arek H</dc:creator>
      <pubDate>Wed, 24 Jun 2026 08:03:06 +0000</pubDate>
      <link>https://dev.to/arek_h/finding-the-root-cause-of-production-incidents-in-seconds-with-gitlab-orbit-ai-244i</link>
      <guid>https://dev.to/arek_h/finding-the-root-cause-of-production-incidents-in-seconds-with-gitlab-orbit-ai-244i</guid>
      <description>&lt;p&gt;It’s 2:00 AM. Your phone buzzes. Production is down, the checkout service is throwing 500 errors, and your team is scrambling. &lt;/p&gt;

&lt;p&gt;You pull up the incident, and the real pain begins. Which recent merge request broke this? Was it the payment service? The quota logic? A pipeline configuration? You’re manually hopping between GitLab issues, file diffs, CI/CD logs, and deployment histories while revenue bleeds. &lt;/p&gt;

&lt;p&gt;This is the hidden cost of production incidents: the hours of manual, forensic archaeology required before anyone even knows &lt;em&gt;what&lt;/em&gt; to fix.&lt;/p&gt;

&lt;p&gt;For the &lt;strong&gt;GitLab Transcend Hackathon&lt;/strong&gt;, I wanted to solve this exact problem. I built the &lt;strong&gt;Incident Root Cause Flow&lt;/strong&gt;—an open-source, AI-powered agent that natively traverses GitLab's Orbit knowledge graph to find the guilty commit in seconds.&lt;/p&gt;

&lt;p&gt;Here is how it works, how it leverages graph traversal, and how you can install it in your own GitLab projects.&lt;/p&gt;

&lt;h2&gt;
  
  
  The Concept: Traversing the SDLC Graph
&lt;/h2&gt;

&lt;p&gt;The flow is built entirely on the &lt;strong&gt;GitLab Duo Agent Platform&lt;/strong&gt; and powered by &lt;strong&gt;GitLab Orbit&lt;/strong&gt;. &lt;/p&gt;

&lt;p&gt;Orbit is a knowledge graph that maps your code structure (definitions, references, call graphs) and connects it to your SDLC objects (issues, merge requests, pipelines, deployments). Because everything is linked, we can trace a failure backward.&lt;/p&gt;

&lt;p&gt;When a new Incident is created, the agent wakes up and executes this precise traversal path:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;Find Candidates:&lt;/strong&gt; Queries the graph for all MRs merged recently.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Inspect Diffs:&lt;/strong&gt; For each MR, it finds the changed files.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Map the Code:&lt;/strong&gt; It extracts the specific functions/definitions that were altered.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Trace the Blast Radius:&lt;/strong&gt; It follows the &lt;code&gt;CALLS&lt;/code&gt; edge to find exactly which downstream functions rely on the changed code. &lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Cross-Reference CI/CD:&lt;/strong&gt; It checks if the &lt;code&gt;HAS_HEAD_PIPELINE&lt;/code&gt; edge indicates a failed test run before the code was merged.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;The agent scores each MR based on this graph evidence and posts a beautifully formatted Markdown comment directly to the incident, complete with a confidence rating and the exact call-chain that broke.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Incident → Project → MergeRequest (failed pipeline?) → MergeRequestDiffFile → File → Definition ←[CALLS]← callers
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Instead of digging blindly, the on-call engineer has a confidence-scored analysis and a direct link to the likely culprit within 30 seconds.&lt;/p&gt;

&lt;h2&gt;
  
  
  How to Install the Flow
&lt;/h2&gt;

&lt;p&gt;Because this is a native GitLab Duo Flow, there are no external servers, webhooks, or Python scripts to host. It runs securely inside your GitLab environment.&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Navigate to your GitLab project.&lt;/li&gt;
&lt;li&gt;Go to &lt;strong&gt;AI → Flows&lt;/strong&gt; (or search for the AI Catalog).&lt;/li&gt;
&lt;li&gt;Click &lt;strong&gt;New flow&lt;/strong&gt;.&lt;/li&gt;
&lt;li&gt;Give it a name like &lt;code&gt;Incident Root Cause Analyzer&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;Grab the &lt;code&gt;config.yaml&lt;/code&gt; from the &lt;a href="https://gitlab.com/gitlab-ai-hackathon/transcend/8043245" rel="noopener noreferrer"&gt;open-source repository&lt;/a&gt; and paste it into the YAML configuration editor.&lt;/li&gt;
&lt;li&gt;Click &lt;strong&gt;Save&lt;/strong&gt;.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;&lt;em&gt;Note: The YAML dynamically injects your project ID using &lt;code&gt;{{project_id}}&lt;/code&gt;, so it is completely portable out of the box.&lt;/em&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  How to Use It (Attaching the Trigger)
&lt;/h2&gt;

&lt;p&gt;Once the flow is saved, you need to tell GitLab &lt;em&gt;when&lt;/em&gt; to run it. We want this agent to fire automatically the moment a production issue is logged.&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Still in the Flows interface, switch to the &lt;strong&gt;Managed&lt;/strong&gt; tab.&lt;/li&gt;
&lt;li&gt;Find your new flow and click &lt;strong&gt;Enable&lt;/strong&gt;.&lt;/li&gt;
&lt;li&gt;Under the &lt;strong&gt;Add triggers&lt;/strong&gt; section, select &lt;strong&gt;Work item (Created)&lt;/strong&gt;. &lt;/li&gt;
&lt;li&gt;Ensure your project is selected as the target.&lt;/li&gt;
&lt;/ol&gt;

&lt;h3&gt;
  
  
  Testing the Automation
&lt;/h3&gt;

&lt;p&gt;To see it in action:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Go to &lt;strong&gt;Plan → Issues → New Issue&lt;/strong&gt;.&lt;/li&gt;
&lt;li&gt;Change the Type dropdown to &lt;strong&gt;Incident&lt;/strong&gt;.&lt;/li&gt;
&lt;li&gt;Give it a realistic title, like &lt;code&gt;checkout-service returning 500s&lt;/code&gt;, and hit submit.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Wait about 30 seconds, refresh the page, and the agent will have posted its complete root cause analysis as a comment, isolating the exact MR and code definitions that caused the outage. &lt;/p&gt;

&lt;h2&gt;
  
  
  What's Next?
&lt;/h2&gt;

&lt;p&gt;Mean Time to Resolution (MTTR) doesn't have to be bottlenecked by human context-switching. By combining LLM reasoning with strict, deterministic graph traversal via Orbit, we can eliminate the guesswork of incident triage. &lt;/p&gt;

&lt;p&gt;You can check out the full source code, the demo application, and the prompt engineering in the &lt;a href="https://gitlab.com/gitlab-ai-hackathon/transcend/8043245" rel="noopener noreferrer"&gt;GitLab repository here&lt;/a&gt;. &lt;/p&gt;

&lt;p&gt;Happy hacking, and may your on-call shifts be quiet!&lt;/p&gt;

</description>
      <category>gitlab</category>
      <category>hackathon</category>
      <category>devops</category>
      <category>ai</category>
    </item>
    <item>
      <title>System Status: ONLINE – A Terminal-Inspired Portfolio for Backend Architect 🖥️⚡</title>
      <dc:creator>Arek H</dc:creator>
      <pubDate>Fri, 30 Jan 2026 05:51:58 +0000</pubDate>
      <link>https://dev.to/arek_h/system-status-online-a-terminal-inspired-portfolio-for-backend-architect-35h6</link>
      <guid>https://dev.to/arek_h/system-status-online-a-terminal-inspired-portfolio-for-backend-architect-35h6</guid>
      <description>&lt;p&gt;&lt;em&gt;This is a submission for the &lt;a href="https://dev.to/challenges/new-year-new-you-google-ai-2025-12-31"&gt;New Year, New You Portfolio Challenge Presented by Google AI&lt;/a&gt;&lt;/em&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  About Me
&lt;/h2&gt;

&lt;p&gt;Hi, I'm Arkadiusz! 👋&lt;/p&gt;

&lt;p&gt;I'm a &lt;strong&gt;Senior Backend Architect&lt;/strong&gt; who has spent more time looking at terminal windows and Grafana dashboards than pretty UI layouts. &lt;/p&gt;

&lt;p&gt;For the &lt;strong&gt;New Year, New You Portfolio Challenge&lt;/strong&gt;, I wanted to break away from the standard "Hero Image + Bio" template. Instead, I asked myself: &lt;em&gt;"What if my portfolio looked like the tools I use every day?"&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;The result is a High-Availability, resilient portfolio that visualizes my skills as "system resources" and invites visitors to interact via a CLI-style interface.&lt;/p&gt;

&lt;h2&gt;
  
  
  Portfolio
&lt;/h2&gt;

&lt;p&gt;

&lt;/p&gt;
&lt;div class="ltag__cloud-run"&gt;
  &lt;iframe height="600px" src="https://portfolio-website-283882201222.us-central1.run.app/"&gt;
  &lt;/iframe&gt;
&lt;/div&gt;




&lt;h2&gt;
  
  
  How I Built It
&lt;/h2&gt;

&lt;p&gt;I built this project to be as lightweight and resilient as the backend systems I design.&lt;/p&gt;

&lt;h3&gt;
  
  
  &lt;strong&gt;The Stack&lt;/strong&gt;
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;  &lt;strong&gt;Frontend&lt;/strong&gt;: &lt;a href="https://react.dev/" rel="noopener noreferrer"&gt;React&lt;/a&gt; + &lt;a href="https://vitejs.dev/" rel="noopener noreferrer"&gt;Vite&lt;/a&gt; for blazing fast build times.&lt;/li&gt;
&lt;li&gt;  &lt;strong&gt;Styling&lt;/strong&gt;: &lt;strong&gt;Zero UI Frameworks&lt;/strong&gt;. I used pure CSS Variables (Design Tokens) and Flexbox/Grid. No Bootstrap, no Tailwind. Just clean, semantic CSS.&lt;/li&gt;
&lt;li&gt;  &lt;strong&gt;Hosting&lt;/strong&gt;: &lt;strong&gt;Google Cloud Run&lt;/strong&gt;. The app is containerized with Docker and served via Nginx. This allows it to scale down to zero (saving costs) and scale up instantly when traffic hits.&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  &lt;strong&gt;The AI Co-Pilot&lt;/strong&gt;
&lt;/h3&gt;

&lt;p&gt;I paired deeply with &lt;strong&gt;Google's Gemini&lt;/strong&gt; throughout the process. It wasn't just about generating code; it was about rapid prototyping:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;  &lt;strong&gt;SVG Math&lt;/strong&gt;: I used Gemini to calculate the control points for the Bezier curves in the "Blood Pressure" widget graphs.&lt;/li&gt;
&lt;li&gt;  &lt;strong&gt;CSS Glassmorphism&lt;/strong&gt;: Fine-tuning the &lt;code&gt;backdrop-filter&lt;/code&gt; and border transparency to get that perfect "Head-Up Display" look.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  What I'm Most Proud Of
&lt;/h2&gt;

&lt;p&gt;There are three features I'm particularly happy with:&lt;/p&gt;

&lt;h3&gt;
  
  
  1. The "Blood Pressure" Widget 💓
&lt;/h3&gt;

&lt;p&gt;In the top right, instead of a static "Hire Me", I built a live "System Status" monitor. It simulates server load (or my caffeine levels ☕) with a randomized React state machine that transitions between &lt;code&gt;Normal&lt;/code&gt;, &lt;code&gt;Elevated&lt;/code&gt;, and &lt;code&gt;High&lt;/code&gt; load, changing colors and graph shapes in real-time.&lt;/p&gt;

&lt;h3&gt;
  
  
  2. The Interactive Tech Stack 📚
&lt;/h3&gt;

&lt;p&gt;The &lt;code&gt;/stack&lt;/code&gt; page isn't just a list. It's a filtered matrix of my 29+ mastered technologies. I added a custom "Expertise" calculation that aggregates my years of experience across active filters.&lt;/p&gt;

&lt;h3&gt;
  
  
  3. CLI Contact Form ⌨️
&lt;/h3&gt;

&lt;p&gt;The &lt;code&gt;/contact&lt;/code&gt; page is styled like a terminal window (&lt;code&gt;visitor@portfolio:~/contact&lt;/code&gt;). &lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;  &lt;strong&gt;Interactive Buttons&lt;/strong&gt;: The &lt;code&gt;Hire_Me()&lt;/code&gt; button in the nav isn't just a link—it passes a smart query parameter to pre-fill the form with "Project Inquiry".&lt;/li&gt;
&lt;li&gt;  &lt;strong&gt;Quick Actions&lt;/strong&gt;: Buttons like &lt;code&gt;initiate_consultation()&lt;/code&gt; run macros that auto-focus fields or open external scheduling tools.&lt;/li&gt;
&lt;/ul&gt;




&lt;p&gt;&lt;em&gt;Deployed with ❤️ on Google Cloud Run.&lt;/em&gt;&lt;/p&gt;

</description>
      <category>devchallenge</category>
      <category>googleaichallenge</category>
      <category>portfolio</category>
      <category>gemini</category>
    </item>
  </channel>
</rss>
