<?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: Abhishek Pandit</title>
    <description>The latest articles on DEV Community by Abhishek Pandit (@panditabhis).</description>
    <link>https://dev.to/panditabhis</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%2F3977867%2F1daa5bf3-0eb3-4693-b8cc-a54c8f313250.png</url>
      <title>DEV Community: Abhishek Pandit</title>
      <link>https://dev.to/panditabhis</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/panditabhis"/>
    <language>en</language>
    <item>
      <title>A Day in the Life: Complete Copilot Workflow Session From Idea to Merged PR</title>
      <dc:creator>Abhishek Pandit</dc:creator>
      <pubDate>Fri, 12 Jun 2026 14:22:24 +0000</pubDate>
      <link>https://dev.to/panditabhis/a-day-in-the-life-complete-copilot-workflow-session-from-idea-to-merged-pr-4cdb</link>
      <guid>https://dev.to/panditabhis/a-day-in-the-life-complete-copilot-workflow-session-from-idea-to-merged-pr-4cdb</guid>
      <description>&lt;p&gt;Everything in this series has been theory. Good theory — I hope — but theory.&lt;/p&gt;

&lt;p&gt;This article is different. We're going to walk through a complete, real session from start to merge. Real prompts, real outputs, real code. Every step of the 8-step workflow, nothing skipped.&lt;/p&gt;

&lt;p&gt;The feature: add a due date to tasks, surface overdue tasks in the active list.&lt;/p&gt;

&lt;p&gt;The starting point: an Express + TypeScript + Prisma backend with an existing task system. The feature doesn't exist yet.&lt;/p&gt;

&lt;p&gt;Let's go.&lt;/p&gt;




&lt;h2&gt;
  
  
  The 8-Step Workflow
&lt;/h2&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;1. /spec chatmode → idea → spec → tasks
2. @test-engineer → write failing tests first
3. Implement → make tests pass
4. @simplifier → readability pass
5. /ship chatmode → SHIP verdict
6. CI green
7. Merge
8. Update CONTEXT.md
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;One session. One feature. One merge.&lt;/p&gt;




&lt;h2&gt;
  
  
  Step 1: /spec Chatmode — Before a Single Line of Code
&lt;/h2&gt;

&lt;p&gt;I open the &lt;code&gt;spec&lt;/code&gt; chatmode in Copilot Chat and describe the feature:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;I want to add due dates to tasks. Tasks past their due date should
be flagged as overdue. The active task list should sort overdue
tasks to the top.
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;@idea-refiner&lt;/strong&gt; asks five questions:&lt;/p&gt;

&lt;blockquote&gt;
&lt;ul&gt;
&lt;li&gt;Should tasks without a due date appear in the overdue list? (Yes/No)&lt;/li&gt;
&lt;li&gt;Is "overdue" calculated server-side or client-side?&lt;/li&gt;
&lt;li&gt;Should overdue status persist (stored in DB) or be computed on-the-fly?&lt;/li&gt;
&lt;li&gt;Do you need due date filtering (e.g., "due this week")?&lt;/li&gt;
&lt;li&gt;Is this sprint backend-only or does the UI need updating too?&lt;/li&gt;
&lt;/ul&gt;
&lt;/blockquote&gt;

&lt;p&gt;My answers: No (tasks without due dates aren't overdue), server-side, computed on-the-fly (no extra column), no filtering this sprint, backend only.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;@spec-writer&lt;/strong&gt; surfaces assumptions then writes the spec:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;ASSUMPTIONS:
1. Due date is optional — tasks without one are never overdue
2. Overdue = dueDate &amp;lt; current timestamp (UTC), checked at query time
3. No new database column needed — status is computed in the query
4. Sorting: overdue tasks first, then by creation date within each group
5. No UI changes this sprint — API only
→ Confirm before I proceed.
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;I confirm. The spec lands:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight markdown"&gt;&lt;code&gt;&lt;span class="gh"&gt;# Spec: Task Due Dates and Overdue Sorting&lt;/span&gt;

&lt;span class="gu"&gt;## Objective&lt;/span&gt;
Allow tasks to have optional due dates. Active task list returns overdue tasks
first (dueAt &amp;lt; NOW()), then remaining tasks by creation date.

&lt;span class="gu"&gt;## Success Criteria&lt;/span&gt;
&lt;span class="p"&gt;-&lt;/span&gt; [ ] POST /api/tasks accepts optional dueAt (ISO 8601 datetime)
&lt;span class="p"&gt;-&lt;/span&gt; [ ] GET /api/tasks returns tasks sorted: overdue first, then by createdAt desc
&lt;span class="p"&gt;-&lt;/span&gt; [ ] PATCH /api/tasks/:id accepts dueAt updates
&lt;span class="p"&gt;-&lt;/span&gt; [ ] A task with no dueAt is never flagged as overdue
&lt;span class="p"&gt;-&lt;/span&gt; [ ] All existing task tests continue to pass

&lt;span class="gu"&gt;## Boundaries&lt;/span&gt;
Always: validate dueAt is a valid future date on creation
Ask first: any change to the Task DB schema beyond adding dueAt column
Never: store computed "isOverdue" flag in database — compute at query time
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;@planner&lt;/strong&gt; produces 3 tasks. I approve the plan.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Time spent: 12 minutes. Nothing built yet. Everything clear.&lt;/strong&gt;&lt;/p&gt;




&lt;h2&gt;
  
  
  Step 2: @test-engineer — Failing Tests First
&lt;/h2&gt;

&lt;p&gt;Task 1 is the schema + service layer. Before touching any implementation:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;@test-engineer Write failing tests for the due date feature.
Spec is approved. Tests must fail with current code.

Requirements:
- POST /api/tasks accepts optional dueAt
- GET /api/tasks returns overdue tasks first, then by createdAt desc
- Tasks without dueAt are never overdue
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The agent produces:&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="nf"&gt;describe&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;Task due dates&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="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nf"&gt;describe&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;POST /api/tasks&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="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nf"&gt;it&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;accepts a valid ISO 8601 due date&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="k"&gt;async &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="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;res&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nf"&gt;request&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;app&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;post&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;/api/tasks&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="nf"&gt;send&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt; &lt;span class="na"&gt;title&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;File taxes&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;dueAt&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;2026-12-31T23:59:00Z&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="nf"&gt;expect&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;201&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

      &lt;span class="nf"&gt;expect&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;res&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;body&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;dueAt&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;toBe&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;2026-12-31T23:59:00.000Z&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="nf"&gt;it&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;rejects a past due date on creation&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="k"&gt;async &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="k"&gt;await&lt;/span&gt; &lt;span class="nf"&gt;request&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;app&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;post&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;/api/tasks&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="nf"&gt;send&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt; &lt;span class="na"&gt;title&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;Old task&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;dueAt&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;2020-01-01T00:00:00Z&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="nf"&gt;expect&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;422&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="p"&gt;});&lt;/span&gt;

    &lt;span class="nf"&gt;it&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;creates task successfully without a due date&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="k"&gt;async &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="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;res&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nf"&gt;request&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;app&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;post&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;/api/tasks&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="nf"&gt;send&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt; &lt;span class="na"&gt;title&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;No deadline&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="nf"&gt;expect&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;201&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

      &lt;span class="nf"&gt;expect&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;res&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;body&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;dueAt&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;toBeNull&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="nf"&gt;describe&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;GET /api/tasks — overdue sorting&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="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nf"&gt;it&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;returns overdue tasks before non-overdue tasks&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="k"&gt;async &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="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;overdueTask&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nf"&gt;createTask&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
        &lt;span class="na"&gt;title&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;Overdue&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="na"&gt;dueAt&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;Date&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nb"&gt;Date&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;now&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt; &lt;span class="mi"&gt;86400000&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="c1"&gt;// yesterday&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;futureTask&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nf"&gt;createTask&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
        &lt;span class="na"&gt;title&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;Future&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="na"&gt;dueAt&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;Date&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nb"&gt;Date&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;now&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="mi"&gt;86400000&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="c1"&gt;// tomorrow&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;noDeadline&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nf"&gt;createTask&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt; &lt;span class="na"&gt;title&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;No deadline&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;res&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nf"&gt;request&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;app&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;/api/tasks&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;expect&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;200&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;titles&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;res&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;body&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;data&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;map&lt;/span&gt;&lt;span class="p"&gt;((&lt;/span&gt;&lt;span class="na"&gt;t&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;Task&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;t&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;title&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

      &lt;span class="nf"&gt;expect&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;titles&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="nf"&gt;toBe&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;Overdue&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
      &lt;span class="c1"&gt;// Future and No deadline can be in either order after overdue&lt;/span&gt;
      &lt;span class="nf"&gt;expect&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;titles&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;toContain&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;Future&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
      &lt;span class="nf"&gt;expect&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;titles&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;toContain&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;No deadline&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="nf"&gt;it&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;never marks a task without dueAt as overdue&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="k"&gt;async &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="k"&gt;await&lt;/span&gt; &lt;span class="nf"&gt;createTask&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt; &lt;span class="na"&gt;title&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;No deadline&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;res&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nf"&gt;request&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;app&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;/api/tasks&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;expect&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;200&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;task&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;res&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;body&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;data&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;find&lt;/span&gt;&lt;span class="p"&gt;((&lt;/span&gt;&lt;span class="na"&gt;t&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;Task&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;t&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;title&lt;/span&gt; &lt;span class="o"&gt;===&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;No deadline&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

      &lt;span class="nf"&gt;expect&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;task&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;isOverdue&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;toBe&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;span class="nf"&gt;it&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;sorts multiple overdue tasks by creation date descending&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="k"&gt;async &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="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;older&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nf"&gt;createTask&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt; &lt;span class="na"&gt;title&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;Older overdue&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;dueAt&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;Date&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nb"&gt;Date&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;now&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt; &lt;span class="mi"&gt;172800000&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;newer&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nf"&gt;createTask&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt; &lt;span class="na"&gt;title&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;Newer overdue&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;dueAt&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;Date&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nb"&gt;Date&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;now&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt; &lt;span class="mi"&gt;86400000&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;res&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nf"&gt;request&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;app&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;/api/tasks&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;expect&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;200&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;overdueTitles&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;res&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;body&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;data&lt;/span&gt;
        &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;filter&lt;/span&gt;&lt;span class="p"&gt;((&lt;/span&gt;&lt;span class="na"&gt;t&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;Task&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;t&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;isOverdue&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;map&lt;/span&gt;&lt;span class="p"&gt;((&lt;/span&gt;&lt;span class="na"&gt;t&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;Task&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;t&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;title&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

      &lt;span class="nf"&gt;expect&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;overdueTitles&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="nf"&gt;toBe&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;Newer overdue&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
      &lt;span class="nf"&gt;expect&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;overdueTitles&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;]).&lt;/span&gt;&lt;span class="nf"&gt;toBe&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;Older overdue&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="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;I run the tests:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;npm &lt;span class="nb"&gt;test&lt;/span&gt; &lt;span class="nt"&gt;--&lt;/span&gt; &lt;span class="nt"&gt;--grep&lt;/span&gt; &lt;span class="s2"&gt;"Task due dates"&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;





&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight console"&gt;&lt;code&gt;&lt;span class="go"&gt;FAIL src/routes/tasks.test.ts
  ✕ accepts a valid ISO 8601 due date — 400 response (unexpected field)
  ✕ rejects a past due date on creation — 201 (no validation)
  ✕ returns overdue tasks before non-overdue tasks — column does not exist
&lt;/span&gt;&lt;span class="c"&gt;  ...
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;All failing. The spec is confirmed in test form. Now I implement.&lt;/p&gt;




&lt;h2&gt;
  
  
  Step 3: Implement — Make the Tests Pass
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;Migration first:&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;npx prisma migrate dev &lt;span class="nt"&gt;--name&lt;/span&gt; add-due-at-to-tasks
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;





&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;model Task {
  id        String   @id @default(cuid())
  title     String
  dueAt     DateTime?     // ← added
  createdAt DateTime @default(now())
  completedAt DateTime?
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Update the Zod schema:&lt;/strong&gt;&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="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;CreateTaskSchema&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;z&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;object&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
  &lt;span class="na"&gt;title&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;z&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;string&lt;/span&gt;&lt;span class="p"&gt;().&lt;/span&gt;&lt;span class="nf"&gt;min&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;max&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;200&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
  &lt;span class="na"&gt;dueAt&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;z&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;string&lt;/span&gt;&lt;span class="p"&gt;().&lt;/span&gt;&lt;span class="nf"&gt;datetime&lt;/span&gt;&lt;span class="p"&gt;().&lt;/span&gt;&lt;span class="nf"&gt;optional&lt;/span&gt;&lt;span class="p"&gt;().&lt;/span&gt;&lt;span class="nf"&gt;refine&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;val&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="o"&gt;!&lt;/span&gt;&lt;span class="nx"&gt;val&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;Date&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;val&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;Date&lt;/span&gt;&lt;span class="p"&gt;(),&lt;/span&gt;
    &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;message&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;Due date must be in the future&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="p"&gt;});&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Update the list query for overdue sorting:&lt;/strong&gt;&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;async&lt;/span&gt; &lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;listActiveTasks&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="nx"&gt;Task&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="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;now&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;Date&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;overdue&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;upcoming&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="nb"&gt;Promise&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;all&lt;/span&gt;&lt;span class="p"&gt;([&lt;/span&gt;
    &lt;span class="c1"&gt;// Overdue: completedAt is null AND dueAt is past&lt;/span&gt;
    &lt;span class="nx"&gt;db&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;task&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;findMany&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
      &lt;span class="na"&gt;where&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;completedAt&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;null&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;dueAt&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;lt&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;now&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;not&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;null&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="p"&gt;},&lt;/span&gt;
      &lt;span class="na"&gt;orderBy&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;createdAt&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;desc&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="c1"&gt;// Non-overdue: either no due date or future due date&lt;/span&gt;
    &lt;span class="nx"&gt;db&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;task&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;findMany&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
      &lt;span class="na"&gt;where&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="na"&gt;completedAt&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;null&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="na"&gt;OR&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[{&lt;/span&gt; &lt;span class="na"&gt;dueAt&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;null&lt;/span&gt; &lt;span class="p"&gt;},&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;dueAt&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;gte&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;now&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="na"&gt;orderBy&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;createdAt&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;desc&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="p"&gt;]);&lt;/span&gt;

  &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;
    &lt;span class="p"&gt;...&lt;/span&gt;&lt;span class="nx"&gt;overdue&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;map&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;t&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="nx"&gt;t&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;isOverdue&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;span class="nx"&gt;upcoming&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;map&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;t&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="nx"&gt;t&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;isOverdue&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;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Run the tests:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;npm &lt;span class="nb"&gt;test&lt;/span&gt; &lt;span class="nt"&gt;--&lt;/span&gt; &lt;span class="nt"&gt;--grep&lt;/span&gt; &lt;span class="s2"&gt;"Task due dates"&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;





&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight console"&gt;&lt;code&gt;&lt;span class="go"&gt;PASS src/routes/tasks.test.ts
  ✓ accepts a valid ISO 8601 due date (45ms)
  ✓ rejects a past due date on creation (12ms)
  ✓ creates task successfully without a due date (11ms)
  ✓ returns overdue tasks before non-overdue tasks (67ms)
  ✓ never marks a task without dueAt as overdue (43ms)
  ✓ sorts multiple overdue tasks by creation date descending (58ms)
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Full suite:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;npm &lt;span class="nb"&gt;test&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;All green. No regressions.&lt;/p&gt;




&lt;h2&gt;
  
  
  Step 4: @simplifier — Readability Pass
&lt;/h2&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;@simplifier Do a readability pass on src/services/task.ts
and src/routes/tasks.ts. Tests all pass. Focus on the new
due date and overdue sorting code.
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The agent flags one thing:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;code&gt;src/services/task.ts:34&lt;/code&gt; — The &lt;code&gt;Promise.all&lt;/code&gt; call with two separate queries is correct but the variable names &lt;code&gt;overdue&lt;/code&gt; and &lt;code&gt;upcoming&lt;/code&gt; could be more precise. Suggest &lt;code&gt;overduetasks&lt;/code&gt; and &lt;code&gt;nonOverdueTasks&lt;/code&gt; to match the domain language in the spec.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;One rename. I apply it. Tests still pass.&lt;/p&gt;




&lt;h2&gt;
  
  
  Step 5: /ship Chatmode — The Verdict
&lt;/h2&gt;

&lt;p&gt;I switch to the &lt;code&gt;ship&lt;/code&gt; chatmode and describe the changes:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;[In /ship chatmode]
Review the due date changes. New Prisma field, updated Zod
validation, new list query with overdue sorting.
Key concern: the parallel query approach — want to confirm
it's correct and won't have race conditions.
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The verdict:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight markdown"&gt;&lt;code&gt;&lt;span class="gu"&gt;## Pre-Merge Review&lt;/span&gt;

&lt;span class="gu"&gt;### Verdict: SHIP ✅&lt;/span&gt;

&lt;span class="gs"&gt;**Summary:**&lt;/span&gt; Due date implementation is clean, well-tested, and
correctly computes overdue status at query time as the spec requires.
No critical issues.

&lt;span class="gu"&gt;### Must Fix Before Merge&lt;/span&gt;
— None —

&lt;span class="gu"&gt;### Should Fix Before Merge&lt;/span&gt;
&lt;span class="p"&gt;-&lt;/span&gt; [IMPORTANT] src/routes/tasks.ts:67 — The 422 validation error for
  past due dates returns the Zod error object directly. Wrap it in the
  project's standard error shape: &lt;span class="sb"&gt;`{ error: { code: 'VALIDATION_ERROR',
  message: '...', details: result.error.flatten() } }`&lt;/span&gt;

&lt;span class="gu"&gt;### Optional Improvements&lt;/span&gt;
&lt;span class="p"&gt;-&lt;/span&gt; [SUGGESTION] Consider adding an index on (dueAt, completedAt) if
  this table grows large — the overdue query will do a full scan otherwise.

&lt;span class="gu"&gt;### Clean ✅&lt;/span&gt;
&lt;span class="p"&gt;-&lt;/span&gt; Correct use of Promise.all — no race condition, both queries read
  independently from the same committed state
&lt;span class="p"&gt;-&lt;/span&gt; Zod validation correctly rejects past dates at the boundary
&lt;span class="p"&gt;-&lt;/span&gt; isOverdue computed at query time — no stale data risk
&lt;span class="p"&gt;-&lt;/span&gt; All edge cases covered by tests
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;One Important issue. I fix the error shape in 2 minutes. Re-run &lt;code&gt;/ship&lt;/code&gt;. Clean verdict. &lt;strong&gt;SHIP.&lt;/strong&gt;&lt;/p&gt;




&lt;h2&gt;
  
  
  Step 6: CI Green
&lt;/h2&gt;

&lt;p&gt;I push the branch. GitHub Actions runs:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;✓ Lint &amp;amp; Type Check
✓ Unit &amp;amp; Integration Tests
✓ Build
✓ Security Audit
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;All green in 3 minutes.&lt;/p&gt;




&lt;h2&gt;
  
  
  Step 7: Merge
&lt;/h2&gt;

&lt;p&gt;PR opened. CI badge green. &lt;code&gt;/ship&lt;/code&gt; verdict documented in the PR description. Merge.&lt;/p&gt;




&lt;h2&gt;
  
  
  Step 8: Update CONTEXT.md
&lt;/h2&gt;

&lt;p&gt;The last step most developers skip — and the one that pays compound interest.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight markdown"&gt;&lt;code&gt;&lt;span class="gu"&gt;## Recent Decisions&lt;/span&gt;

| Date | Decision | Reason |
|------|----------|--------|
| 2026-06-12 | Compute isOverdue at query time, not stored in DB | Avoids stale data, no background job needed |
| 2026-06-12 | Parallel queries for overdue/non-overdue | Single query with CASE sorting was complex; two clean queries are clearer |
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Next session, when I (or a teammate, or an AI agent) opens this project, the context is there. No archaeology required.&lt;/p&gt;




&lt;h2&gt;
  
  
  The Session at a Glance
&lt;/h2&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Step&lt;/th&gt;
&lt;th&gt;Time&lt;/th&gt;
&lt;th&gt;What happened&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;/spec chatmode&lt;/td&gt;
&lt;td&gt;12 min&lt;/td&gt;
&lt;td&gt;Idea → assumptions surfaced → spec → 3 tasks&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;@test-engineer&lt;/td&gt;
&lt;td&gt;8 min&lt;/td&gt;
&lt;td&gt;6 failing tests written&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Implement&lt;/td&gt;
&lt;td&gt;25 min&lt;/td&gt;
&lt;td&gt;Schema, validation, query&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;@simplifier&lt;/td&gt;
&lt;td&gt;3 min&lt;/td&gt;
&lt;td&gt;One rename&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;/ship chatmode&lt;/td&gt;
&lt;td&gt;5 min&lt;/td&gt;
&lt;td&gt;One Important issue found and fixed&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;CI&lt;/td&gt;
&lt;td&gt;3 min&lt;/td&gt;
&lt;td&gt;All green&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Merge + CONTEXT&lt;/td&gt;
&lt;td&gt;2 min&lt;/td&gt;
&lt;td&gt;—&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Total&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;58 min&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Feature shipped with spec, tests, review&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;A working, tested, reviewed feature — in under an hour. With a spec that proves we built the right thing, tests that prove it works, a review that confirmed it's safe, and CI that will catch any regression.&lt;/p&gt;




&lt;h2&gt;
  
  
  Get the Template
&lt;/h2&gt;

&lt;p&gt;Everything in this walkthrough — all 17 agents, 3 chatmodes, CI pipeline, MCP config, CONTEXT.md template — is in one place.&lt;/p&gt;

&lt;p&gt;👉 &lt;strong&gt;&lt;a href="https://github.com/panditAbhis/copilot-workflow" rel="noopener noreferrer"&gt;github.com/panditAbhis/copilot-workflow&lt;/a&gt;&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Click &lt;strong&gt;Use this template&lt;/strong&gt;. Five minutes to set up. The discipline is yours to keep.&lt;/p&gt;




&lt;p&gt;&lt;strong&gt;Series navigation&lt;/strong&gt;&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Part&lt;/th&gt;
&lt;th&gt;Title&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;1&lt;/td&gt;
&lt;td&gt;&lt;a href="https://dev.to/panditabhis/your-copilot-has-no-memory-heres-how-i-fixed-that-in-5-minutes-34l7"&gt;Your Copilot Has No Memory. Here's How I Fixed That in 5 Minutes.&lt;/a&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;2&lt;/td&gt;
&lt;td&gt;&lt;a href="https://dev.to/panditabhis/stop-merging-blind-how-i-use-code-reviewer-before-every-pr-2eji"&gt;Stop Merging Blind: How I Use @code-reviewer Before Every PR&lt;/a&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;3&lt;/td&gt;
&lt;td&gt;&lt;a href="https://dev.to/panditabhis/never-fix-a-bug-without-proof-the-test-engineer-prove-it-pattern-1cmh"&gt;Never Fix a Bug Without Proof: The @test-engineer Prove-It Pattern&lt;/a&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;4&lt;/td&gt;
&lt;td&gt;&lt;a href="https://dev.to/panditabhis/think-like-an-attacker-how-i-use-security-auditor-before-every-production-deploy-5434"&gt;Think Like an Attacker: How I Use @security-auditor Before Every Production Deploy&lt;/a&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;5&lt;/td&gt;
&lt;td&gt;&lt;a href="https://dev.to/panditabhis/one-command-to-rule-them-all-the-ship-chatmode-that-reviews-audits-and-cleans-before-every-merge-5590"&gt;One Command to Rule Them All: The /ship Chatmode&lt;/a&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;6&lt;/td&gt;
&lt;td&gt;&lt;a href="https://dev.to/panditabhis/stop-building-the-wrong-thing-how-i-use-spec-writer-and-planner-before-writing-a-single-line-of-1jd0"&gt;Stop Building the Wrong Thing: @spec-writer and @planner&lt;/a&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;7&lt;/td&gt;
&lt;td&gt;&lt;a href="https://dev.to/panditabhis/a-day-in-the-life-complete-copilot-workflow-session-from-idea-to-merged-pr-4cdb"&gt;A Day in the Life: Complete Session Walkthrough&lt;/a&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

</description>
      <category>github</category>
      <category>githubcopilot</category>
      <category>productivity</category>
      <category>webdev</category>
    </item>
    <item>
      <title>Stop Building the Wrong Thing: How I Use @spec-writer and @planner Before Writing a Single Line of Code</title>
      <dc:creator>Abhishek Pandit</dc:creator>
      <pubDate>Fri, 12 Jun 2026 14:17:13 +0000</pubDate>
      <link>https://dev.to/panditabhis/stop-building-the-wrong-thing-how-i-use-spec-writer-and-planner-before-writing-a-single-line-of-1jd0</link>
      <guid>https://dev.to/panditabhis/stop-building-the-wrong-thing-how-i-use-spec-writer-and-planner-before-writing-a-single-line-of-1jd0</guid>
      <description>&lt;p&gt;I once spent three days building a notification system.&lt;/p&gt;

&lt;p&gt;Real-time WebSocket updates. Notification center with read/unread state. Badge counts. Persistence across sessions. It was clean, well-tested, well-reviewed code.&lt;/p&gt;

&lt;p&gt;Then I showed it to the product team.&lt;/p&gt;

&lt;p&gt;"Oh — we just meant a simple email when someone assigns a task. We don't need any of that in the app."&lt;/p&gt;

&lt;p&gt;Three days. Gone. Not because the code was bad. Because I built the wrong thing.&lt;/p&gt;

&lt;p&gt;This is the most expensive bug in software development — and it's never caught by tests, never caught by code review, never caught by a security audit. It's caught by showing someone the finished product and watching their face.&lt;/p&gt;

&lt;p&gt;&lt;code&gt;@spec-writer&lt;/code&gt; and &lt;code&gt;@planner&lt;/code&gt; exist to catch this bug before you write the first function.&lt;/p&gt;

&lt;p&gt;This is Part 6 of the &lt;strong&gt;copilot-workflow&lt;/strong&gt; series.&lt;/p&gt;




&lt;h2&gt;
  
  
  The Architect's Blueprint Analogy
&lt;/h2&gt;

&lt;p&gt;You wouldn't build a house without blueprints.&lt;/p&gt;

&lt;p&gt;Not because architects are bureaucratic. Because building walls is expensive and tearing them down is more expensive. A blueprint catches design problems when they're still just pencil lines — before concrete gets poured.&lt;/p&gt;

&lt;p&gt;Software is the same. The cost of changing a requirement on paper is five minutes. The cost of changing it in working code is hours. The cost of changing it in deployed production code with real users depending on it is days or weeks.&lt;/p&gt;

&lt;p&gt;&lt;code&gt;@spec-writer&lt;/code&gt; is your blueprint. &lt;code&gt;@planner&lt;/code&gt; is your construction schedule. Neither one writes a line of code. Both prevent you from building the wrong thing.&lt;/p&gt;




&lt;h2&gt;
  
  
  The /spec Chatmode: Three Phases, One Command
&lt;/h2&gt;

&lt;p&gt;The &lt;code&gt;/spec&lt;/code&gt; chatmode orchestrates &lt;code&gt;@spec-writer&lt;/code&gt; and &lt;code&gt;@planner&lt;/code&gt; in sequence. Each phase gates the next — nothing proceeds without approval.&lt;/p&gt;

&lt;p&gt;Here's what happens when you open the &lt;code&gt;spec&lt;/code&gt; chatmode and describe what you want to build.&lt;/p&gt;




&lt;h3&gt;
  
  
  Phase 1: @idea-refiner — Sharpen Before You Specify
&lt;/h3&gt;

&lt;p&gt;Before you write a spec, &lt;code&gt;@idea-refiner&lt;/code&gt; helps you figure out what you actually want.&lt;/p&gt;

&lt;p&gt;Raw idea: "I want to add notifications."&lt;/p&gt;

&lt;p&gt;Refined idea (after the agent asks five sharpening questions):&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Who: task assignees, not managers&lt;/li&gt;
&lt;li&gt;What: email only, no in-app UI this sprint&lt;/li&gt;
&lt;li&gt;Success: assignee receives email within 30 seconds of assignment, 0 missed deliveries&lt;/li&gt;
&lt;li&gt;Not doing: unsubscribe management, notification preferences, real-time push&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;This conversation takes 3 minutes. It just saved you 3 days.&lt;/p&gt;




&lt;h3&gt;
  
  
  Phase 2: @spec-writer — Surface Every Assumption
&lt;/h3&gt;

&lt;p&gt;With a clear direction, &lt;code&gt;@spec-writer&lt;/code&gt; produces the spec. But before writing a single section, it does something critical:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;ASSUMPTIONS I'M MAKING:
1. Email is via an external service, not self-hosted SMTP
2. Notifications only fire on task creation — not on updates or completion
3. Assignee email is always available (users have verified emails)
4. This is a backend-only change — no frontend work this sprint
→ Correct any of these before I continue.
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Assumptions are where projects go wrong. Surfacing them explicitly — before writing the spec — gives you a chance to correct course while it costs nothing.&lt;/p&gt;

&lt;p&gt;After confirmation, the full spec:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight markdown"&gt;&lt;code&gt;&lt;span class="gh"&gt;# Spec: Task Assignment Notifications&lt;/span&gt;

&lt;span class="gu"&gt;## Objective&lt;/span&gt;
Send an email to the task assignee within 30 seconds of assignment.
Success: zero missed deliveries, email arrives in inbox (not spam).

&lt;span class="gu"&gt;## Tech Stack&lt;/span&gt;
Node.js 22, Express, Prisma (PostgreSQL), Resend for email delivery.

&lt;span class="gu"&gt;## Commands&lt;/span&gt;
Build: npm run build
Test: npm test -- --coverage
Dev: npm run dev

&lt;span class="gu"&gt;## Project Structure&lt;/span&gt;
src/
  services/notification.ts   ← new file
  jobs/send-notification.ts  ← new file (BullMQ worker)
  routes/tasks.ts            ← modified (trigger on assignment)
tests/
  services/notification.test.ts
  jobs/send-notification.test.ts

&lt;span class="gu"&gt;## Testing Strategy&lt;/span&gt;
Unit: notification service (mock Resend client)
Integration: route handler triggers queue (real DB, mock queue)
E2E: not required this sprint

&lt;span class="gu"&gt;## Boundaries&lt;/span&gt;
Always: validate assignee email before enqueue, log all sends
Ask first: adding new email templates, changing delivery provider
Never: synchronous email delivery (always async via queue)

&lt;span class="gu"&gt;## Success Criteria&lt;/span&gt;
&lt;span class="p"&gt;-&lt;/span&gt; [ ] Email arrives within 30s of task assignment in manual testing
&lt;span class="p"&gt;-&lt;/span&gt; [ ] Zero missed deliveries in integration tests (100 consecutive)
&lt;span class="p"&gt;-&lt;/span&gt; [ ] No email sent on task updates — only on initial assignment
&lt;span class="p"&gt;-&lt;/span&gt; [ ] Failed delivery retried 3x with exponential backoff

&lt;span class="gu"&gt;## Open Questions&lt;/span&gt;
&lt;span class="p"&gt;-&lt;/span&gt; None — assumptions confirmed above
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;This is the gate.&lt;/strong&gt; The spec goes to the human. Nothing proceeds until it's approved.&lt;/p&gt;




&lt;h3&gt;
  
  
  Phase 3: @planner — Tasks You Can Actually Execute
&lt;/h3&gt;

&lt;p&gt;With an approved spec, &lt;code&gt;@planner&lt;/code&gt; produces the implementation order.&lt;/p&gt;

&lt;p&gt;The key insight: &lt;strong&gt;vertical slices, not horizontal layers&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;Most developers build horizontally: all database first, then all API, then all frontend. This means you have no working feature until everything is done. If something is wrong with the design, you discover it at the end.&lt;/p&gt;

&lt;p&gt;Vertical slices mean each task delivers a working piece of functionality:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight markdown"&gt;&lt;code&gt;&lt;span class="gu"&gt;## Task 1: Notification service with Resend integration&lt;/span&gt;
What: Create src/services/notification.ts that sends an email via Resend.
Acceptance:
&lt;span class="p"&gt;-&lt;/span&gt; [ ] sendTaskAssignmentEmail(task, assignee) sends email via Resend API
&lt;span class="p"&gt;-&lt;/span&gt; [ ] Failed sends throw NotificationError with error details
&lt;span class="p"&gt;-&lt;/span&gt; [ ] Mock Resend client in tests — no real emails in test suite
Verification: npm test -- --grep "notification service"
Files: src/services/notification.ts, tests/services/notification.test.ts
Size: S

&lt;span class="gu"&gt;## Task 2: Queue worker for async delivery&lt;/span&gt;
What: Create src/jobs/send-notification.ts BullMQ worker.
Acceptance:
&lt;span class="p"&gt;-&lt;/span&gt; [ ] Worker processes notification jobs from queue
&lt;span class="p"&gt;-&lt;/span&gt; [ ] Retries 3x with exponential backoff on failure
&lt;span class="p"&gt;-&lt;/span&gt; [ ] Logs success and failure with structured fields
Verification: npm test -- --grep "notification worker"
Dependencies: Task 1
Files: src/jobs/send-notification.ts, tests/jobs/send-notification.test.ts
Size: S

&lt;span class="gu"&gt;## Checkpoint: After Tasks 1-2&lt;/span&gt;
&lt;span class="p"&gt;-&lt;/span&gt; [ ] All tests pass
&lt;span class="p"&gt;-&lt;/span&gt; [ ] Notification sends correctly in isolation
&lt;span class="p"&gt;-&lt;/span&gt; [ ] Review before proceeding

&lt;span class="gu"&gt;## Task 3: Wire assignment route to notification queue&lt;/span&gt;
What: Modify task assignment endpoint to enqueue notification.
Acceptance:
&lt;span class="p"&gt;-&lt;/span&gt; [ ] POST /api/tasks/:id/assign enqueues notification job
&lt;span class="p"&gt;-&lt;/span&gt; [ ] Notification NOT sent on task update — only on assignment
&lt;span class="p"&gt;-&lt;/span&gt; [ ] Assignment succeeds even if queue is unavailable (fire-and-forget)
Verification: npm test -- --grep "task assignment"
Dependencies: Tasks 1, 2
Files: src/routes/tasks.ts, tests/routes/tasks.test.ts
Size: S

&lt;span class="gu"&gt;## Checkpoint: Complete&lt;/span&gt;
&lt;span class="p"&gt;-&lt;/span&gt; [ ] All tests pass
&lt;span class="p"&gt;-&lt;/span&gt; [ ] Email received within 30s in manual test
&lt;span class="p"&gt;-&lt;/span&gt; [ ] No email on task update (verified manually)
&lt;span class="p"&gt;-&lt;/span&gt; [ ] CI green — ready for /ship review
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Three tasks. Each one completable in a focused session. Each one leaves the system working. No XL tasks, no guesswork, no "figure it out as you go."&lt;/p&gt;




&lt;h2&gt;
  
  
  The Anti-Pattern This Prevents
&lt;/h2&gt;

&lt;p&gt;Here's what happens without spec-first development:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;You understand the requirement loosely&lt;/li&gt;
&lt;li&gt;You start coding based on your interpretation&lt;/li&gt;
&lt;li&gt;Three days in, you show someone&lt;/li&gt;
&lt;li&gt;They say "oh, we meant something simpler" (or more complex)&lt;/li&gt;
&lt;li&gt;You rewrite&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;The spec doesn't add time to a project. It removes rework. The 15 minutes it takes to write a spec routinely saves 3-8 hours of misguided implementation.&lt;/p&gt;

&lt;p&gt;The task breakdown saves a different kind of time: it stops you from building in the wrong order, then discovering a dependency you should have built first.&lt;/p&gt;




&lt;h2&gt;
  
  
  Using /spec in Practice
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;For a new feature:&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;[In /spec chatmode]
I want to add CSV export for the task list.
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The chatmode guides you through idea refinement, spec writing, and task breakdown. You approve each phase. Nothing gets implemented until the plan is complete.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;For a bug with complex root cause:&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;[In /spec chatmode]
I need to redesign the task search — it's timing out on tables over 10k rows.
This needs a proper plan before I touch the index structure.
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;For a refactor:&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;[In /spec chatmode]
The auth middleware is 400 lines and doing too many things.
I want to split it into separate concerns but keep behavior identical.
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;






&lt;h2&gt;
  
  
  What @doubter Adds
&lt;/h2&gt;

&lt;p&gt;After &lt;code&gt;@planner&lt;/code&gt; produces the task list, there's one more agent worth invoking for high-stakes decisions: &lt;code&gt;@doubter&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;&lt;code&gt;@doubter&lt;/code&gt; is an adversarial reviewer — it finds what's wrong with your plan. Not "is this good?" but "what could go wrong?"&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;@doubter Here is my implementation plan for task assignment notifications.
ARTIFACT: [paste the task list]
CONTRACT: Zero missed deliveries, email within 30s, no sync blocking of the assignment API
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;It might surface: "Task 3 says assignment succeeds even if queue is unavailable — but if the queue is unavailable for 30 minutes, those notifications are silently lost. Is that acceptable, or do you need a dead-letter queue?"&lt;/p&gt;

&lt;p&gt;That's a decision you want to make in the plan, not discover in production.&lt;/p&gt;




&lt;h2&gt;
  
  
  The Full Workflow in One Picture
&lt;/h2&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Vague idea
    │
    ▼
@idea-refiner → sharp concept + Not-Doing list
    │ human approves direction
    ▼
@spec-writer → assumptions surfaced + spec written
    │ human approves spec
    ▼
@planner → ordered tasks with acceptance criteria
    │ human approves plan
    ▼
@test-engineer → failing tests (Prove-It)
    │
    ▼
Implement → make tests pass
    │
    ▼
/ship chatmode → SHIP / DO NOT SHIP verdict
    │
    ▼
Merge
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Every arrow is a gate. Every gate has a human approval. The code is the last step, not the first.&lt;/p&gt;




&lt;h2&gt;
  
  
  Get the Template
&lt;/h2&gt;

&lt;p&gt;&lt;code&gt;@spec-writer&lt;/code&gt;, &lt;code&gt;@planner&lt;/code&gt;, &lt;code&gt;@idea-refiner&lt;/code&gt;, &lt;code&gt;@doubter&lt;/code&gt;, and the &lt;code&gt;/spec&lt;/code&gt; chatmode are all included.&lt;/p&gt;

&lt;p&gt;👉 &lt;strong&gt;&lt;a href="https://github.com/panditAbhis/copilot-workflow" rel="noopener noreferrer"&gt;github.com/panditAbhis/copilot-workflow&lt;/a&gt;&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Next (and final) in the series:&lt;/strong&gt; Part 7 — A complete session walkthrough. Real feature, all 8 steps, start to merge.&lt;/p&gt;




&lt;p&gt;&lt;strong&gt;Series navigation&lt;/strong&gt;&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Part&lt;/th&gt;
&lt;th&gt;Title&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;1&lt;/td&gt;
&lt;td&gt;&lt;a href="https://dev.to/panditabhis/your-copilot-has-no-memory-heres-how-i-fixed-that-in-5-minutes-34l7"&gt;Your Copilot Has No Memory. Here's How I Fixed That in 5 Minutes.&lt;/a&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;2&lt;/td&gt;
&lt;td&gt;&lt;a href="https://dev.to/panditabhis/stop-merging-blind-how-i-use-code-reviewer-before-every-pr-2eji"&gt;Stop Merging Blind: How I Use @code-reviewer Before Every PR&lt;/a&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;3&lt;/td&gt;
&lt;td&gt;&lt;a href="https://dev.to/panditabhis/never-fix-a-bug-without-proof-the-test-engineer-prove-it-pattern-1cmh"&gt;Never Fix a Bug Without Proof: The @test-engineer Prove-It Pattern&lt;/a&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;4&lt;/td&gt;
&lt;td&gt;&lt;a href="https://dev.to/panditabhis/think-like-an-attacker-how-i-use-security-auditor-before-every-production-deploy-5434"&gt;Think Like an Attacker: How I Use @security-auditor Before Every Production Deploy&lt;/a&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;5&lt;/td&gt;
&lt;td&gt;&lt;a href="https://dev.to/panditabhis/one-command-to-rule-them-all-the-ship-chatmode-that-reviews-audits-and-cleans-before-every-merge-5590"&gt;One Command to Rule Them All: The /ship Chatmode&lt;/a&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;6&lt;/td&gt;
&lt;td&gt;&lt;a href="https://dev.to/panditabhis/stop-building-the-wrong-thing-how-i-use-spec-writer-and-planner-before-writing-a-single-line-of-1jd0"&gt;Stop Building the Wrong Thing: @spec-writer and @planner&lt;/a&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;7&lt;/td&gt;
&lt;td&gt;&lt;a href="https://dev.to/panditabhis/a-day-in-the-life-complete-copilot-workflow-session-from-idea-to-merged-pr-4cdb"&gt;A Day in the Life: Complete Session Walkthrough&lt;/a&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

</description>
      <category>github</category>
      <category>githubcopilot</category>
      <category>productivity</category>
      <category>programming</category>
    </item>
    <item>
      <title>One Command to Rule Them All: The /ship Chatmode That Reviews, Audits, and Cleans Before Every Merge</title>
      <dc:creator>Abhishek Pandit</dc:creator>
      <pubDate>Fri, 12 Jun 2026 14:04:11 +0000</pubDate>
      <link>https://dev.to/panditabhis/one-command-to-rule-them-all-the-ship-chatmode-that-reviews-audits-and-cleans-before-every-merge-5590</link>
      <guid>https://dev.to/panditabhis/one-command-to-rule-them-all-the-ship-chatmode-that-reviews-audits-and-cleans-before-every-merge-5590</guid>
      <description>&lt;p&gt;Here's a problem I had.&lt;/p&gt;

&lt;p&gt;I'd built three specialist agents in Copilot Chat: &lt;code&gt;@code-reviewer&lt;/code&gt;, &lt;code&gt;@security-auditor&lt;/code&gt;, and &lt;code&gt;@simplifier&lt;/code&gt;. Each one was genuinely useful. Each one caught things the others missed.&lt;/p&gt;

&lt;p&gt;But using all three before a merge meant:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Invoking &lt;code&gt;@code-reviewer&lt;/code&gt;, reading the output, addressing findings&lt;/li&gt;
&lt;li&gt;Separately invoking &lt;code&gt;@security-auditor&lt;/code&gt;, reading that output, addressing findings&lt;/li&gt;
&lt;li&gt;Separately invoking &lt;code&gt;@simplifier&lt;/code&gt;, reading that output, addressing findings&lt;/li&gt;
&lt;li&gt;Mentally combining three reports into one decision&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;That's not automation. That's just delegation with extra steps.&lt;/p&gt;

&lt;p&gt;What I actually wanted: &lt;strong&gt;one command that does all three and tells me to ship or not.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;That's what the &lt;code&gt;/ship&lt;/code&gt; chatmode is.&lt;/p&gt;

&lt;p&gt;This is Part 5 of the &lt;strong&gt;copilot-workflow&lt;/strong&gt; series — and it's the part where the individual pieces become a real automated workflow.&lt;/p&gt;




&lt;h2&gt;
  
  
  What's a Chatmode?
&lt;/h2&gt;

&lt;p&gt;Before we get into &lt;code&gt;/ship&lt;/code&gt;, a quick explanation of chatmodes — because it's a feature most Copilot users don't know about.&lt;/p&gt;

&lt;p&gt;Think of Copilot Chat as a radio. By default, it's tuned to a general-purpose frequency. Chatmodes are preset stations — each one configures Copilot with a specific role, tools, and approach before you say anything.&lt;/p&gt;

&lt;p&gt;You switch chatmodes the same way you'd switch radio stations: one click in the chatmode selector in VS Code Copilot Chat, then start talking.&lt;/p&gt;

&lt;p&gt;The difference: when you open the &lt;code&gt;ship&lt;/code&gt; chatmode, Copilot already knows what you're trying to do. It runs the full three-pass review without you having to orchestrate it manually.&lt;/p&gt;




&lt;h2&gt;
  
  
  The Flight Checklist Analogy
&lt;/h2&gt;

&lt;p&gt;Pilots don't improvise pre-flight checks.&lt;/p&gt;

&lt;p&gt;Before every flight, they run through a standardized checklist — the same one, in the same order, every time. Not because pilots are forgetful. Because when something is important enough, you systematize it. You make the right thing the default thing.&lt;/p&gt;

&lt;p&gt;Pilots discovered this the hard way. Before checklists became standard, perfectly competent pilots died in perfectly functional aircraft because they forgot one step under pressure.&lt;/p&gt;

&lt;p&gt;The &lt;code&gt;/ship&lt;/code&gt; chatmode is your pre-merge checklist. Every merge. Same order. No improvising. No forgetting the security pass when you're rushing to meet a deadline.&lt;/p&gt;




&lt;h2&gt;
  
  
  How /ship Works
&lt;/h2&gt;

&lt;p&gt;When you activate the &lt;code&gt;ship&lt;/code&gt; chatmode and describe your changes, three passes run in sequence:&lt;/p&gt;

&lt;h3&gt;
  
  
  Pass 1: Code Review
&lt;/h3&gt;

&lt;p&gt;&lt;code&gt;@code-reviewer&lt;/code&gt; evaluates the changes across five dimensions: correctness, readability, architecture, security, performance.&lt;/p&gt;

&lt;p&gt;Every finding is labeled:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Critical&lt;/strong&gt; — blocks the merge&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Important&lt;/strong&gt; — should fix before merging&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Suggestion&lt;/strong&gt; — optional improvement&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Pass 2: Security Audit
&lt;/h3&gt;

&lt;p&gt;&lt;code&gt;@security-auditor&lt;/code&gt; starts from trust boundaries, runs STRIDE analysis, and maps exploitable vulnerabilities to OWASP Top 10.&lt;/p&gt;

&lt;p&gt;Security-only findings: Critical, High, Medium, Low severity. Every Critical or High includes proof of concept and a specific fix — not vague "consider validating input" advice.&lt;/p&gt;

&lt;h3&gt;
  
  
  Pass 3: Simplification
&lt;/h3&gt;

&lt;p&gt;&lt;code&gt;@simplifier&lt;/code&gt; scans for complexity that can be removed without changing behavior: deeply nested logic, generic names, duplicated code, unnecessary abstractions.&lt;/p&gt;

&lt;p&gt;This is the last pass — not because it matters least, but because it only makes sense after you've verified the code is correct and secure.&lt;/p&gt;

&lt;h3&gt;
  
  
  The Verdict
&lt;/h3&gt;

&lt;p&gt;After all three passes, one consolidated report:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;## Pre-Merge Review

### Verdict: DO NOT SHIP ❌

**Summary:** The payment flow changes contain one critical SQL injection
vulnerability and a missing token expiry check. The logic is otherwise
clean and well-structured.

### Must Fix Before Merge
- [CRITICAL] SQL injection in payment intent creation — payments.ts:47
  Fix: parameterize the amount field in the INSERT statement
- [CRITICAL] Reset token not invalidated after use — auth.ts:123
  Fix: SET reset_token = NULL in the UPDATE statement

### Should Fix Before Merge
- [IMPORTANT] Missing test for payment failure path — payments.test.ts
  The happy path is covered but no test for insufficient funds

### Optional Improvements
- [SUGGESTION] extractPaymentIntent() could be extracted to a helper — payments.ts:40-65

### Clean ✅
- Correct use of parameterized queries in all existing endpoints
- Rate limiting already applied to auth endpoints
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;SHIP = no Critical issues.&lt;/strong&gt; You make the call on Important and Suggestion items — the chatmode doesn't make that judgment for you.&lt;/p&gt;




&lt;h2&gt;
  
  
  Real Scenario: What /ship Catches That You'd Miss
&lt;/h2&gt;

&lt;p&gt;Let me show you three things the ship chatmode caught in a single review on a real feature — a task sharing system.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;What I thought I was shipping:&lt;/strong&gt; A feature that lets users share tasks with teammates. Looked clean. Tests passed. I'd been working on it for two days and was confident in it.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;What /ship found:&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;&lt;em&gt;Code review — correctness:&lt;/em&gt; The permission check was on the wrong side of the async boundary. If two users tried to accept the same share invitation simultaneously, both could succeed — creating a race condition that allowed a task to have two owners.&lt;/p&gt;

&lt;p&gt;&lt;em&gt;Security audit — broken access control:&lt;/em&gt; The &lt;code&gt;GET /api/tasks/:id/share-link&lt;/code&gt; endpoint didn't verify the requester owned the task. Any authenticated user could generate a share link for any task by guessing the ID.&lt;/p&gt;

&lt;p&gt;&lt;em&gt;Simplification:&lt;/em&gt; The permission checking logic was duplicated in three different places. One helper function would have made it easier to maintain — and would have meant fixing the race condition in one place instead of three.&lt;/p&gt;

&lt;p&gt;Three different problem types. One pass caught each one. Single command.&lt;/p&gt;




&lt;h2&gt;
  
  
  Setting Up /ship: Zero Configuration
&lt;/h2&gt;

&lt;p&gt;The chatmode is already in the template. There's nothing to configure.&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Go to &lt;strong&gt;&lt;a href="https://github.com/panditAbhis/copilot-workflow" rel="noopener noreferrer"&gt;github.com/panditAbhis/copilot-workflow&lt;/a&gt;&lt;/strong&gt;
&lt;/li&gt;
&lt;li&gt;Click &lt;strong&gt;Use this template&lt;/strong&gt; → create your repo&lt;/li&gt;
&lt;li&gt;Open VS Code with the Copilot Chat panel&lt;/li&gt;
&lt;li&gt;Click the chatmode selector (the dropdown at the top of Copilot Chat)&lt;/li&gt;
&lt;li&gt;Select &lt;strong&gt;ship&lt;/strong&gt;
&lt;/li&gt;
&lt;li&gt;Describe your changes&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;That's it. The chatmode is defined in &lt;code&gt;.github/chatmodes/ship.chatmode.md&lt;/code&gt; — Copilot picks it up automatically.&lt;/p&gt;




&lt;h2&gt;
  
  
  The Two Other Chatmodes
&lt;/h2&gt;

&lt;p&gt;The &lt;code&gt;/ship&lt;/code&gt; chatmode isn't the only one in the template. There are two others for different phases of development.&lt;/p&gt;

&lt;h3&gt;
  
  
  /spec — Idea to Implementation Plan
&lt;/h3&gt;

&lt;p&gt;Before writing a single line of code, &lt;code&gt;/spec&lt;/code&gt; walks you through:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;Idea refinement&lt;/strong&gt; — sharpens vague concepts, generates variations, forces you to name your "Not Doing" list&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Spec writing&lt;/strong&gt; — produces a structured specification with success criteria, boundaries, and open questions&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Task breakdown&lt;/strong&gt; — converts the spec into ordered, verifiable tasks with acceptance criteria&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Nothing gets implemented until the spec is approved. This sounds slow. It's actually the fastest path — because you're not rewriting code that was built on wrong assumptions.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;[In /spec chatmode]
I want to add email notifications when tasks are assigned to team members.
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The chatmode asks clarifying questions, surfaces assumptions ("should unassigned tasks also trigger notifications?"), proposes 3 approaches with trade-offs, writes the spec, and produces a numbered task list. Then it stops. You implement, guided by the tasks.&lt;/p&gt;

&lt;h3&gt;
  
  
  /debug — Systematic Root Cause Analysis
&lt;/h3&gt;

&lt;p&gt;When something breaks, &lt;code&gt;/debug&lt;/code&gt; runs the Prove-It Pattern:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;Reproduce first&lt;/strong&gt; — make the failure happen reliably before touching code&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Localize&lt;/strong&gt; — which layer? Which commit introduced it? (git bisect)&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Root cause&lt;/strong&gt; — fix the cause, not the symptom&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Regression test&lt;/strong&gt; — write a failing test that proves the bug existed&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Verify&lt;/strong&gt; — full suite passes with the fix applied&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;The chatmode won't let you skip straight to the fix. The reproduction step is mandatory. The regression test is mandatory. This discipline is tedious the first few times. After your first "this fixed it" that actually fixed it — permanently, with a test to prove it — you stop finding it tedious.&lt;/p&gt;




&lt;h2&gt;
  
  
  The Automation Gap
&lt;/h2&gt;

&lt;p&gt;Here's what separates a "copilot user" from a "copilot workflow":&lt;/p&gt;

&lt;p&gt;A copilot user asks Copilot questions. They get answers. Sometimes the answers are good. The quality depends on how well they prompt, how much context they provide, how consistently they remember to check security, how often they run reviews.&lt;/p&gt;

&lt;p&gt;A copilot workflow is systematic. It defines the process once, then enforces it automatically. &lt;code&gt;/ship&lt;/code&gt; runs the same three-pass review every time — not when you remember to, not when you feel like the code needs it, not just on "important" PRs.&lt;/p&gt;

&lt;p&gt;The difference isn't the AI. It's the discipline baked into the tooling.&lt;/p&gt;




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

&lt;p&gt;This template started with three agents and grew to 17. The next phase is agentic workflows — where Copilot doesn't just respond to your prompts but initiates workflows autonomously:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Copilot in GitHub Actions&lt;/strong&gt; — automatic review triggered on every PR open, no human needed to remember&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;MCP server integration&lt;/strong&gt; — agents that read your actual database schema, live error logs, and monitoring metrics when reviewing code&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;The &lt;code&gt;/spec&lt;/code&gt; to production pipeline&lt;/strong&gt; — from idea to deployed feature, with AI assistance at every step and human approval gates between them&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The foundations are already in the template. Watch the repo for updates as each piece ships.&lt;/p&gt;




&lt;h2&gt;
  
  
  Get the Template
&lt;/h2&gt;

&lt;p&gt;Everything in this series — all 17 agents, all 3 chatmodes, the CI pipeline, MCP config — is in the template.&lt;/p&gt;

&lt;p&gt;👉 &lt;strong&gt;&lt;a href="https://github.com/panditAbhis/copilot-workflow" rel="noopener noreferrer"&gt;github.com/panditAbhis/copilot-workflow&lt;/a&gt;&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;One click. Use this template. Every repo you create from it has the full workflow.&lt;/p&gt;




&lt;p&gt;&lt;strong&gt;Series navigation&lt;/strong&gt;&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Part&lt;/th&gt;
&lt;th&gt;Title&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;1&lt;/td&gt;
&lt;td&gt;&lt;a href="https://dev.to/panditabhis/your-copilot-has-no-memory-heres-how-i-fixed-that-in-5-minutes-34l7"&gt;Your Copilot Has No Memory. Here's How I Fixed That in 5 Minutes.&lt;/a&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;2&lt;/td&gt;
&lt;td&gt;&lt;a href="https://dev.to/panditabhis/stop-merging-blind-how-i-use-code-reviewer-before-every-pr-2eji"&gt;Stop Merging Blind: How I Use @code-reviewer Before Every PR&lt;/a&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;3&lt;/td&gt;
&lt;td&gt;&lt;a href="https://dev.to/panditabhis/never-fix-a-bug-without-proof-the-test-engineer-prove-it-pattern-1cmh"&gt;Never Fix a Bug Without Proof: The @test-engineer Prove-It Pattern&lt;/a&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;4&lt;/td&gt;
&lt;td&gt;&lt;a href="https://dev.to/panditabhis/think-like-an-attacker-how-i-use-security-auditor-before-every-production-deploy-5434"&gt;Think Like an Attacker: How I Use @security-auditor Before Every Production Deploy&lt;/a&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;5&lt;/td&gt;
&lt;td&gt;&lt;a href="https://dev.to/panditabhis/one-command-to-rule-them-all-the-ship-chatmode-that-reviews-audits-and-cleans-before-every-merge-5590"&gt;One Command to Rule Them All: The /ship Chatmode&lt;/a&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;6&lt;/td&gt;
&lt;td&gt;&lt;a href="https://dev.to/panditabhis/stop-building-the-wrong-thing-how-i-use-spec-writer-and-planner-before-writing-a-single-line-of-1jd0"&gt;Stop Building the Wrong Thing: @spec-writer and @planner&lt;/a&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;7&lt;/td&gt;
&lt;td&gt;&lt;a href="https://dev.to/panditabhis/a-day-in-the-life-complete-copilot-workflow-session-from-idea-to-merged-pr-4cdb"&gt;A Day in the Life: Complete Session Walkthrough&lt;/a&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

</description>
      <category>github</category>
      <category>githubcopilot</category>
      <category>devops</category>
      <category>productivity</category>
    </item>
    <item>
      <title>Think Like an Attacker: How I Use @security-auditor Before Every Production Deploy</title>
      <dc:creator>Abhishek Pandit</dc:creator>
      <pubDate>Fri, 12 Jun 2026 13:54:58 +0000</pubDate>
      <link>https://dev.to/panditabhis/think-like-an-attacker-how-i-use-security-auditor-before-every-production-deploy-5434</link>
      <guid>https://dev.to/panditabhis/think-like-an-attacker-how-i-use-security-auditor-before-every-production-deploy-5434</guid>
      <description>&lt;p&gt;A lock on your front door doesn't make your house secure.&lt;/p&gt;

&lt;p&gt;Not if the back door is open. Not if the window latch is broken. Not if you're handing spare keys to strangers without realizing it.&lt;/p&gt;

&lt;p&gt;Security works the same way. Adding password hashing to your login endpoint doesn't make your app secure — not if the password reset endpoint doesn't expire tokens, or the file upload handler accepts any file type, or the database query for your admin panel is built by concatenating user input.&lt;/p&gt;

&lt;p&gt;Most developers secure the obvious thing and leave a dozen attack surfaces unchecked. Not because they're careless — because they're only looking at one door at a time.&lt;/p&gt;

&lt;p&gt;&lt;code&gt;@security-auditor&lt;/code&gt; changes that. Instead of checking individual controls, it starts from a map of your entire attack surface.&lt;/p&gt;

&lt;p&gt;This is Part 4 of the &lt;strong&gt;copilot-workflow&lt;/strong&gt; series.&lt;/p&gt;




&lt;h2&gt;
  
  
  The Bank Vault Analogy
&lt;/h2&gt;

&lt;p&gt;Imagine you're designing a bank vault.&lt;/p&gt;

&lt;p&gt;A naive approach: put a very thick door on the vault. Done.&lt;/p&gt;

&lt;p&gt;A security engineer's approach: draw a map of everything that needs protecting, then ask "how would a thief get to it?" They'd check the vault door — but also the air vents, the maintenance tunnels, the manager's office, the cleaning crew's access, and the bank's Wi-Fi network.&lt;/p&gt;

&lt;p&gt;This is &lt;strong&gt;threat modeling&lt;/strong&gt;. You don't start with controls. You start with a map of what you're protecting and all the ways someone could get to it.&lt;/p&gt;

&lt;p&gt;&lt;code&gt;@security-auditor&lt;/code&gt; runs this process on your code automatically. It's not a checklist bot. It's an attacker's mindset, systematically applied.&lt;/p&gt;




&lt;h2&gt;
  
  
  How @security-auditor Thinks
&lt;/h2&gt;

&lt;p&gt;Before flagging a single vulnerability, the agent does two things:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;1. Maps your trust boundaries&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Trust boundaries are places where data crosses from one trust level to another. HTTP requests come in — untrusted. Database data goes out — trusted. A user uploads a file — untrusted. Your auth middleware processes it — trusted (if the middleware is correct).&lt;/p&gt;

&lt;p&gt;Every trust boundary is potential attack surface.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;2. Runs STRIDE&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;STRIDE is a structured way to think about threats at each boundary:&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Letter&lt;/th&gt;
&lt;th&gt;Threat&lt;/th&gt;
&lt;th&gt;Question&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;S&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Spoofing&lt;/td&gt;
&lt;td&gt;Can someone pretend to be a legitimate user or service?&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;T&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Tampering&lt;/td&gt;
&lt;td&gt;Can someone modify data in transit or at rest?&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;R&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Repudiation&lt;/td&gt;
&lt;td&gt;Can someone deny an action they took?&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;I&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Information disclosure&lt;/td&gt;
&lt;td&gt;Can data leak to unauthorized parties?&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;D&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Denial of service&lt;/td&gt;
&lt;td&gt;Can the system be overwhelmed?&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;E&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Elevation of privilege&lt;/td&gt;
&lt;td&gt;Can someone gain more access than they should have?&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;Then it maps findings to OWASP Top 10 — the industry standard list of the most critical web security risks.&lt;/p&gt;




&lt;h2&gt;
  
  
  A Real Example: The Password Reset Trap
&lt;/h2&gt;

&lt;p&gt;Here's an endpoint I've seen in real codebases — a password reset handler:&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;app&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;post&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;/api/auth/reset-password&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="k"&gt;async &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;res&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="kd"&gt;const&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;token&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;newPassword&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="o"&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;body&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;user&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;db&lt;/span&gt;&lt;span class="p"&gt;.&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;`SELECT * FROM users WHERE reset_token = '&lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;token&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;'`&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="o"&gt;!&lt;/span&gt;&lt;span class="nx"&gt;user&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;res&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;status&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;404&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="s1"&gt;Invalid token&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt; &lt;span class="p"&gt;});&lt;/span&gt;
  &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;db&lt;/span&gt;&lt;span class="p"&gt;.&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;`UPDATE users SET password = '&lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;newPassword&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;' WHERE id = &lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;user&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;id&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;`&lt;/span&gt;
  &lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="nx"&gt;res&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;success&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;It "works." Users can reset their passwords. Tests pass. Looks fine.&lt;/p&gt;

&lt;p&gt;Let me show you what &lt;code&gt;@security-auditor&lt;/code&gt; finds:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;@security-auditor Audit this password reset endpoint for security vulnerabilities.
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;






&lt;p&gt;&lt;strong&gt;Security Audit Report&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Critical: 4 | High: 2 | Medium: 1&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;[CRITICAL] SQL Injection — token lookup&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Location: Line 3&lt;/li&gt;
&lt;li&gt;Description: User-supplied &lt;code&gt;token&lt;/code&gt; is concatenated directly into SQL&lt;/li&gt;
&lt;li&gt;Impact: Attacker can bypass authentication entirely. Input &lt;code&gt;' OR '1'='1&lt;/code&gt; retrieves any user.&lt;/li&gt;
&lt;li&gt;Proof of concept: &lt;code&gt;token = "' OR '1'='1"&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;Fix: &lt;code&gt;db.query('SELECT * FROM users WHERE reset_token = $1', [token])&lt;/code&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;[CRITICAL] Plaintext Password Storage&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Location: Line 7&lt;/li&gt;
&lt;li&gt;Description: &lt;code&gt;newPassword&lt;/code&gt; stored as raw string — no hashing&lt;/li&gt;
&lt;li&gt;Impact: Database breach exposes every user's actual password&lt;/li&gt;
&lt;li&gt;Fix: &lt;code&gt;const hashed = await bcrypt.hash(newPassword, 12); db.query('UPDATE users SET password = $1 WHERE id = $2', [hashed, user.id])&lt;/code&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;[CRITICAL] SQL Injection — UPDATE statement&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Location: Line 7&lt;/li&gt;
&lt;li&gt;Description: Both &lt;code&gt;newPassword&lt;/code&gt; and &lt;code&gt;user.id&lt;/code&gt; concatenated into SQL&lt;/li&gt;
&lt;li&gt;Fix: Use parameterized query as shown above&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;[CRITICAL] Reset token not invalidated after use&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Location: Missing&lt;/li&gt;
&lt;li&gt;Description: Token remains valid after password reset — replayable&lt;/li&gt;
&lt;li&gt;Impact: Anyone who intercepts the token can reset the password again&lt;/li&gt;
&lt;li&gt;Fix: &lt;code&gt;UPDATE users SET password = $1, reset_token = NULL WHERE id = $2&lt;/code&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;[HIGH] No token expiry check&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Location: Missing&lt;/li&gt;
&lt;li&gt;Description: Tokens never expire — valid indefinitely&lt;/li&gt;
&lt;li&gt;Fix: Add &lt;code&gt;reset_token_expires_at&lt;/code&gt; column, check &lt;code&gt;&amp;gt; NOW()&lt;/code&gt; in query, expire after 15 minutes&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;[HIGH] No rate limiting&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Location: Missing&lt;/li&gt;
&lt;li&gt;Description: Unlimited attempts — brute-force token guessing is possible&lt;/li&gt;
&lt;li&gt;Fix: &lt;code&gt;app.use('/api/auth/', rateLimit({ windowMs: 900000, max: 10 }))&lt;/code&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;[MEDIUM] Token enumeration via error message&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Location: Line 4&lt;/li&gt;
&lt;li&gt;Description: 404 reveals whether a token exists — helps attackers&lt;/li&gt;
&lt;li&gt;Fix: Return 200 with generic message regardless of outcome&lt;/li&gt;
&lt;/ul&gt;




&lt;p&gt;That endpoint looked functional. The actual security posture: four critical vulnerabilities, any one of which could result in account takeover or full database exposure.&lt;/p&gt;




&lt;h2&gt;
  
  
  The OWASP Top 10: Your Security Baseline
&lt;/h2&gt;

&lt;p&gt;&lt;code&gt;@security-auditor&lt;/code&gt; maps every finding to the OWASP Top 10 — the most critical web application security risks, updated every few years based on real breach data.&lt;/p&gt;

&lt;p&gt;You don't need to memorize it. But understanding the categories helps you recognize when to invoke the agent:&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;#&lt;/th&gt;
&lt;th&gt;Risk&lt;/th&gt;
&lt;th&gt;"Invoke when you're..."&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;A01&lt;/td&gt;
&lt;td&gt;Broken Access Control&lt;/td&gt;
&lt;td&gt;Building any endpoint that checks ownership&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;A02&lt;/td&gt;
&lt;td&gt;Cryptographic Failures&lt;/td&gt;
&lt;td&gt;Storing passwords, tokens, or PII&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;A03&lt;/td&gt;
&lt;td&gt;Injection&lt;/td&gt;
&lt;td&gt;Writing any database query with user input&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;A04&lt;/td&gt;
&lt;td&gt;Insecure Design&lt;/td&gt;
&lt;td&gt;Designing auth flows, payment logic&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;A05&lt;/td&gt;
&lt;td&gt;Security Misconfiguration&lt;/td&gt;
&lt;td&gt;Setting up CORS, headers, error messages&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;A06&lt;/td&gt;
&lt;td&gt;Vulnerable Components&lt;/td&gt;
&lt;td&gt;Adding a new npm dependency&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;A07&lt;/td&gt;
&lt;td&gt;Authentication Failures&lt;/td&gt;
&lt;td&gt;Building login, registration, password reset&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;A08&lt;/td&gt;
&lt;td&gt;Software Integrity&lt;/td&gt;
&lt;td&gt;Setting up CI/CD or deployment pipelines&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;A09&lt;/td&gt;
&lt;td&gt;Logging Failures&lt;/td&gt;
&lt;td&gt;Building audit trails or error handlers&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;A10&lt;/td&gt;
&lt;td&gt;SSRF&lt;/td&gt;
&lt;td&gt;Building webhooks, URL imports, link previews&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;




&lt;h2&gt;
  
  
  The Three-Tier Rule
&lt;/h2&gt;

&lt;p&gt;The most useful mental model from &lt;code&gt;@security-auditor&lt;/code&gt; is the three-tier boundary system. Before writing any security-sensitive code, you know exactly what category it falls into:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Always do (no human approval needed):&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Validate all external input at the boundary&lt;/li&gt;
&lt;li&gt;Parameterize all database queries&lt;/li&gt;
&lt;li&gt;Hash passwords with bcrypt/argon2, ≥12 rounds&lt;/li&gt;
&lt;li&gt;Set security headers (CSP, HSTS, X-Frame-Options)&lt;/li&gt;
&lt;li&gt;Use httpOnly + secure + sameSite cookies for sessions&lt;/li&gt;
&lt;li&gt;Run &lt;code&gt;npm audit&lt;/code&gt; before every release&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Ask first (requires explicit approval):&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Adding new authentication flows&lt;/li&gt;
&lt;li&gt;Storing new categories of sensitive data (PII, payment info)&lt;/li&gt;
&lt;li&gt;Adding new external service integrations&lt;/li&gt;
&lt;li&gt;Changing CORS configuration&lt;/li&gt;
&lt;li&gt;Adding file upload handlers&lt;/li&gt;
&lt;li&gt;Modifying rate limits&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Never do (hard stops):&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Commit secrets to version control&lt;/li&gt;
&lt;li&gt;Log passwords, tokens, or full credit card numbers&lt;/li&gt;
&lt;li&gt;Trust client-side validation as a security boundary&lt;/li&gt;
&lt;li&gt;Use &lt;code&gt;eval()&lt;/code&gt; or &lt;code&gt;innerHTML&lt;/code&gt; with user-provided data&lt;/li&gt;
&lt;li&gt;Store auth tokens in localStorage&lt;/li&gt;
&lt;li&gt;Expose stack traces or internal error details to users&lt;/li&gt;
&lt;/ul&gt;




&lt;h2&gt;
  
  
  When to Invoke @security-auditor
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;Before shipping anything that:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Accepts user input (forms, query params, file uploads)&lt;/li&gt;
&lt;li&gt;Handles authentication or sessions&lt;/li&gt;
&lt;li&gt;Fetches URLs provided by users (webhooks, import-from-URL features)&lt;/li&gt;
&lt;li&gt;Calls external APIs with stored credentials&lt;/li&gt;
&lt;li&gt;Processes payment or PII data&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;After:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Adding a new npm dependency (&lt;code&gt;npm audit&lt;/code&gt; + ask the auditor to check supply chain risk)&lt;/li&gt;
&lt;li&gt;Changing CORS configuration&lt;/li&gt;
&lt;li&gt;Adding any new public endpoint&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Alongside &lt;code&gt;@code-reviewer&lt;/code&gt; for:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;AI-generated code (especially auth logic — Copilot generates plausible but often vulnerable patterns)&lt;/li&gt;
&lt;li&gt;Code you inherited that nobody has security-reviewed&lt;/li&gt;
&lt;/ul&gt;




&lt;h2&gt;
  
  
  The LLM Security Trap
&lt;/h2&gt;

&lt;p&gt;Here's a threat category most security guides don't cover: &lt;strong&gt;your own AI assistant&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;If you're building features that use LLMs — chatbots, summarizers, AI agents — the model's output is untrusted data. Full stop. It must be treated exactly like user input.&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;// DANGEROUS: passing LLM output directly to the database&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;sqlQuery&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;llm&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;generate&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;`Write SQL to find tasks for: &lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;userQuery&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;`&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;db&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;query&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;sqlQuery&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;  &lt;span class="c1"&gt;// arbitrary SQL execution&lt;/span&gt;

&lt;span class="c1"&gt;// SAFE: parse defensively, validate, then act&lt;/span&gt;
&lt;span class="kd"&gt;let&lt;/span&gt; &lt;span class="nx"&gt;intent&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;try&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nx"&gt;intent&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;TaskQuerySchema&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;parse&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;JSON&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;parse&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;llm&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;replyJson&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;userQuery&lt;/span&gt;&lt;span class="p"&gt;)));&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;catch&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="k"&gt;throw&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;ValidationError&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;Could not parse request&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="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;tasks&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;db&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;tasks&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;findMany&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt; &lt;span class="na"&gt;where&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nf"&gt;buildSafeWhere&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;intent&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;code&gt;@security-auditor&lt;/code&gt; checks for this specifically. Prompt injection (an attacker embedding instructions in text your LLM processes), excessive agency (your agent doing things it shouldn't have permission to do), and unbounded consumption (a crafted input that runs up your API costs) are all on its checklist.&lt;/p&gt;




&lt;h2&gt;
  
  
  The Security Mindset Shift
&lt;/h2&gt;

&lt;p&gt;Before using &lt;code&gt;@security-auditor&lt;/code&gt;, I thought about security as a feature — something you add to a working system.&lt;/p&gt;

&lt;p&gt;After: security is a constraint. You don't add it after the code works. You build it into every decision.&lt;/p&gt;

&lt;p&gt;The difference in practice: when I write a database query, I parameterize it as I'm writing it — not after. When I build an endpoint, I add rate limiting and auth checks in the same commit — not later. When I store a token, I immediately ask what happens if that token is compromised.&lt;/p&gt;

&lt;p&gt;The agent didn't teach me to be more careful. It taught me to look in more places.&lt;/p&gt;




&lt;h2&gt;
  
  
  Get the Template
&lt;/h2&gt;

&lt;p&gt;&lt;code&gt;@security-auditor&lt;/code&gt; is included in the &lt;strong&gt;copilot-workflow&lt;/strong&gt; template — one setup, automatic on every session.&lt;/p&gt;

&lt;p&gt;👉 &lt;strong&gt;&lt;a href="https://github.com/panditAbhis/copilot-workflow" rel="noopener noreferrer"&gt;github.com/panditAbhis/copilot-workflow&lt;/a&gt;&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Next in the series:&lt;/strong&gt; Part 5 — The &lt;code&gt;/ship&lt;/code&gt; chatmode. One command that fans out code review, security audit, and simplification in sequence and gives you a single SHIP / DO NOT SHIP verdict.&lt;/p&gt;




&lt;p&gt;&lt;strong&gt;Series navigation&lt;/strong&gt;&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Part&lt;/th&gt;
&lt;th&gt;Title&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;1&lt;/td&gt;
&lt;td&gt;&lt;a href="https://dev.to/panditabhis/your-copilot-has-no-memory-heres-how-i-fixed-that-in-5-minutes-34l7"&gt;Your Copilot Has No Memory. Here's How I Fixed That in 5 Minutes.&lt;/a&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;2&lt;/td&gt;
&lt;td&gt;&lt;a href="https://dev.to/panditabhis/stop-merging-blind-how-i-use-code-reviewer-before-every-pr-2eji"&gt;Stop Merging Blind: How I Use @code-reviewer Before Every PR&lt;/a&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;3&lt;/td&gt;
&lt;td&gt;&lt;a href="https://dev.to/panditabhis/never-fix-a-bug-without-proof-the-test-engineer-prove-it-pattern-1cmh"&gt;Never Fix a Bug Without Proof: The @test-engineer Prove-It Pattern&lt;/a&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;4&lt;/td&gt;
&lt;td&gt;&lt;a href="https://dev.to/panditabhis/think-like-an-attacker-how-i-use-security-auditor-before-every-production-deploy-5434"&gt;Think Like an Attacker: How I Use @security-auditor Before Every Production Deploy&lt;/a&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;5&lt;/td&gt;
&lt;td&gt;&lt;a href="https://dev.to/panditabhis/one-command-to-rule-them-all-the-ship-chatmode-that-reviews-audits-and-cleans-before-every-merge-5590"&gt;One Command to Rule Them All: The /ship Chatmode&lt;/a&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;6&lt;/td&gt;
&lt;td&gt;&lt;a href="https://dev.to/panditabhis/stop-building-the-wrong-thing-how-i-use-spec-writer-and-planner-before-writing-a-single-line-of-1jd0"&gt;Stop Building the Wrong Thing: @spec-writer and @planner&lt;/a&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;7&lt;/td&gt;
&lt;td&gt;&lt;a href="https://dev.to/panditabhis/a-day-in-the-life-complete-copilot-workflow-session-from-idea-to-merged-pr-4cdb"&gt;A Day in the Life: Complete Session Walkthrough&lt;/a&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

</description>
      <category>github</category>
      <category>githubcopilot</category>
      <category>security</category>
      <category>webdev</category>
    </item>
    <item>
      <title>Never Fix a Bug Without Proof: The @test-engineer Prove-It Pattern</title>
      <dc:creator>Abhishek Pandit</dc:creator>
      <pubDate>Fri, 12 Jun 2026 13:49:46 +0000</pubDate>
      <link>https://dev.to/panditabhis/never-fix-a-bug-without-proof-the-test-engineer-prove-it-pattern-1cmh</link>
      <guid>https://dev.to/panditabhis/never-fix-a-bug-without-proof-the-test-engineer-prove-it-pattern-1cmh</guid>
      <description>&lt;p&gt;Let me describe a scene you've lived.&lt;/p&gt;

&lt;p&gt;A bug report lands. "The completed tasks are still showing up in the active list." You look at the code, spot something that looks wrong, change it, refresh the browser — looks fixed. You close the ticket.&lt;/p&gt;

&lt;p&gt;Three weeks later: same bug. Different user. Same ticket reopened.&lt;/p&gt;

&lt;p&gt;What happened? You fixed the symptom. The actual cause was still there. And you have no test to tell you if it comes back.&lt;/p&gt;

&lt;p&gt;This is the most common bug-fix pattern in software. It's also the most expensive one.&lt;/p&gt;

&lt;p&gt;This is Part 3 of the &lt;strong&gt;copilot-workflow&lt;/strong&gt; series. &lt;a href="https://dev.to/panditabhis/your-copilot-has-no-memory-heres-how-i-fixed-that-in-5-minutes-34l7"&gt;Part 1&lt;/a&gt; set up the template. &lt;a href="https://dev.to/panditabhis/stop-merging-blind-how-i-use-code-reviewer-before-every-pr-2eji"&gt;Part 2&lt;/a&gt; covered pre-merge review. This one covers &lt;code&gt;@test-engineer&lt;/code&gt; — the QA engineer persona that forces you to prove bugs exist before fixing them.&lt;/p&gt;




&lt;h2&gt;
  
  
  The Crime Scene Analogy
&lt;/h2&gt;

&lt;p&gt;Think of a bug like a crime scene.&lt;/p&gt;

&lt;p&gt;A bad detective shows up, looks around, decides they know what happened, and closes the case. No evidence. No proof. Just intuition.&lt;/p&gt;

&lt;p&gt;A good detective does something different. They document the evidence first. They establish what actually happened — with proof — before drawing conclusions. Then they verify the conclusion against the evidence.&lt;/p&gt;

&lt;p&gt;&lt;code&gt;@test-engineer&lt;/code&gt; makes you be the good detective.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;The rule: write a test that proves the bug exists before you write a single line of fix code.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;That test is your evidence. If it doesn't fail before your fix, you didn't actually reproduce the bug — you guessed. If it passes after your fix, you have proof the bug is gone and can never silently return.&lt;/p&gt;




&lt;h2&gt;
  
  
  The Prove-It Pattern: Step by Step
&lt;/h2&gt;

&lt;p&gt;Here's exactly what happens when I get a bug report.&lt;/p&gt;

&lt;h3&gt;
  
  
  Step 1: Describe the bug to @test-engineer
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;@test-engineer I have a bug report: completed tasks still appear
in the active task list. The "completedAt" timestamp is being set
correctly, but the list query is returning them anyway.

Write a failing test that proves this bug exists before I fix it.
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Step 2: @test-engineer writes a reproduction test
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="c1"&gt;// This test MUST fail with the current broken code&lt;/span&gt;
&lt;span class="nf"&gt;it&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;excludes completed tasks from the active task list&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="k"&gt;async &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="c1"&gt;// Arrange: create one active and one completed task&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;activeTask&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nf"&gt;createTask&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt; &lt;span class="na"&gt;title&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;Buy groceries&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;completedTask&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nf"&gt;createTask&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt; &lt;span class="na"&gt;title&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;Walk dog&lt;/span&gt;&lt;span class="dl"&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;completeTask&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;completedTask&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;id&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

  &lt;span class="c1"&gt;// Act: fetch the active list&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;activeTasks&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nf"&gt;listActiveTasks&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;

  &lt;span class="c1"&gt;// Assert: completed task must not appear&lt;/span&gt;
  &lt;span class="nf"&gt;expect&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;activeTasks&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;toHaveLength&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="nf"&gt;expect&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;activeTasks&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;id&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;toBe&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;activeTask&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;id&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="nf"&gt;expect&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;activeTasks&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;map&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;t&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nx"&gt;t&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;id&lt;/span&gt;&lt;span class="p"&gt;)).&lt;/span&gt;&lt;span class="nx"&gt;not&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;toContain&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;completedTask&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;id&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;h3&gt;
  
  
  Step 3: Run it. Confirm it fails.
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;npm &lt;span class="nb"&gt;test&lt;/span&gt; &lt;span class="nt"&gt;--&lt;/span&gt; &lt;span class="nt"&gt;--grep&lt;/span&gt; &lt;span class="s2"&gt;"excludes completed tasks"&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;





&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight console"&gt;&lt;code&gt;&lt;span class="go"&gt;FAIL src/tasks.test.ts
  ✕ excludes completed tasks from the active task list
    Expected length: 1
    Received length: 2   ← Bug confirmed. Completed task IS in the list.
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The test fails. The bug is real. Now you have evidence.&lt;/p&gt;

&lt;h3&gt;
  
  
  Step 4: Fix the actual cause
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="c1"&gt;// Before: missing filter — returns ALL tasks&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;listActiveTasks&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="nx"&gt;Task&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="k"&gt;return&lt;/span&gt; &lt;span class="nx"&gt;db&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;tasks&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;findMany&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt; &lt;span class="na"&gt;orderBy&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;createdAt&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;desc&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="p"&gt;}&lt;/span&gt;

&lt;span class="c1"&gt;// After: filter for tasks where completedAt is null&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;listActiveTasks&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="nx"&gt;Task&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="k"&gt;return&lt;/span&gt; &lt;span class="nx"&gt;db&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;tasks&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;findMany&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
    &lt;span class="na"&gt;where&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;completedAt&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;null&lt;/span&gt; &lt;span class="p"&gt;},&lt;/span&gt;
    &lt;span class="na"&gt;orderBy&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;createdAt&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;desc&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="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Step 5: Run the test again. Confirm it passes.
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;npm &lt;span class="nb"&gt;test&lt;/span&gt; &lt;span class="nt"&gt;--&lt;/span&gt; &lt;span class="nt"&gt;--grep&lt;/span&gt; &lt;span class="s2"&gt;"excludes completed tasks"&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;





&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight console"&gt;&lt;code&gt;&lt;span class="go"&gt;PASS src/tasks.test.ts
  ✓ excludes completed tasks from the active task list (23ms)
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Step 6: Run the full suite. No regressions.
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;npm &lt;span class="nb"&gt;test&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;All green. Ship it.&lt;/p&gt;




&lt;h2&gt;
  
  
  Why This Order Matters
&lt;/h2&gt;

&lt;p&gt;You might be thinking: why not just write the fix first, then write a test to verify it?&lt;/p&gt;

&lt;p&gt;Because then you're testing your fix, not the bug.&lt;/p&gt;

&lt;p&gt;If you write the fix first, you unconsciously write a test that confirms your fix works. You're not proving the bug existed — you're proving your solution compiles. Those are completely different things.&lt;/p&gt;

&lt;p&gt;The Prove-It Pattern forces a different discipline:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;The test failing tells you the bug is real.&lt;/strong&gt; Not "I think this is a bug." Not "a user reported this." Actually real, actually reproducible, actually failing right now.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;The test passing tells you the fix is correct.&lt;/strong&gt; Not "I changed something that looks related." Actually correct — the specific behavior that was broken is now working.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;The test existing tells you it can never silently return.&lt;/strong&gt; The next time someone changes the query, the test will catch it immediately. The bug is permanently guarded.&lt;/p&gt;




&lt;h2&gt;
  
  
  Using @test-engineer for New Features (TDD)
&lt;/h2&gt;

&lt;p&gt;The Prove-It Pattern is for bugs. But &lt;code&gt;@test-engineer&lt;/code&gt; also guides you through Test-Driven Development for new features.&lt;/p&gt;

&lt;p&gt;The idea is the same, just inverted: write a failing test that describes what you want the code to do, then make the code pass it.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;@test-engineer I'm building a task priority system. Tasks can be low,
medium, or high priority. The active task list should return tasks
sorted by priority (high first) then by creation date.

Write tests for this feature before I implement it.
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;code&gt;@test-engineer&lt;/code&gt; produces:&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="nf"&gt;describe&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;listActiveTasks with priority sorting&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="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nf"&gt;it&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;returns high priority tasks first&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="k"&gt;async &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="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;low&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nf"&gt;createTask&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt; &lt;span class="na"&gt;title&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;Low&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;priority&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;low&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;high&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nf"&gt;createTask&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt; &lt;span class="na"&gt;title&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;High&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;priority&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;high&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;medium&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nf"&gt;createTask&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt; &lt;span class="na"&gt;title&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;Medium&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;priority&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;medium&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;tasks&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nf"&gt;listActiveTasks&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;

    &lt;span class="nf"&gt;expect&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;tasks&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;priority&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;toBe&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;high&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="nf"&gt;expect&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;tasks&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;].&lt;/span&gt;&lt;span class="nx"&gt;priority&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;toBe&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;medium&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="nf"&gt;expect&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;tasks&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="p"&gt;].&lt;/span&gt;&lt;span class="nx"&gt;priority&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;toBe&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;low&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="nf"&gt;it&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;sorts by creation date within the same priority&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="k"&gt;async &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="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;first&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nf"&gt;createTask&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt; &lt;span class="na"&gt;title&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;First&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;priority&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;high&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;second&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nf"&gt;createTask&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt; &lt;span class="na"&gt;title&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;Second&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;priority&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;high&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;tasks&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nf"&gt;listActiveTasks&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;highPriorityTasks&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;tasks&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;filter&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;t&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nx"&gt;t&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;priority&lt;/span&gt; &lt;span class="o"&gt;===&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;high&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

    &lt;span class="nf"&gt;expect&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;highPriorityTasks&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;id&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;toBe&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;second&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;id&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt; &lt;span class="c1"&gt;// newer first&lt;/span&gt;
    &lt;span class="nf"&gt;expect&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;highPriorityTasks&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;].&lt;/span&gt;&lt;span class="nx"&gt;id&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;toBe&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;first&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;id&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="p"&gt;});&lt;/span&gt;

  &lt;span class="nf"&gt;it&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;excludes completed tasks from the sorted list&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="k"&gt;async &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="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;completed&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nf"&gt;createTask&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt; &lt;span class="na"&gt;title&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;Done&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;priority&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;high&lt;/span&gt;&lt;span class="dl"&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;completeTask&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;completed&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;id&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;active&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nf"&gt;createTask&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt; &lt;span class="na"&gt;title&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;Active&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;priority&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;low&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;tasks&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nf"&gt;listActiveTasks&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;

    &lt;span class="nf"&gt;expect&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;tasks&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;map&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;t&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nx"&gt;t&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;id&lt;/span&gt;&lt;span class="p"&gt;)).&lt;/span&gt;&lt;span class="nx"&gt;not&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;toContain&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;completed&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;id&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;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;All three tests fail — the feature doesn't exist yet. Now you implement &lt;code&gt;listActiveTasks&lt;/code&gt; to make them pass. The tests become the specification.&lt;/p&gt;




&lt;h2&gt;
  
  
  The Test Pyramid: Where Each Test Lives
&lt;/h2&gt;

&lt;p&gt;&lt;code&gt;@test-engineer&lt;/code&gt; knows the right test level for each scenario:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;          ╱╲
         ╱  ╲         E2E Tests (5%)
        ╱    ╲        Real browser, full user flow
       ╱──────╲
      ╱        ╲      Integration Tests (15%)
     ╱          ╲     Real database, real API
    ╱────────────╲
   ╱              ╲   Unit Tests (80%)
  ╱                ╲  Pure logic, no I/O, milliseconds each
 ╱──────────────────╲
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Unit test territory:&lt;/strong&gt; Pure functions, validation logic, data transformations. No database, no network. Runs in milliseconds. The Prove-It Pattern for logic bugs lives here.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Integration test territory:&lt;/strong&gt; API endpoints, database queries, the interaction between your code and your database. The &lt;code&gt;listActiveTasks&lt;/code&gt; example above is an integration test — it hits a real database.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;E2E test territory:&lt;/strong&gt; Critical user paths that must work end-to-end. "User can log in and create a task" is an E2E test. You don't write these for every bug — only for flows so important they justify the maintenance cost.&lt;/p&gt;

&lt;p&gt;When you tell &lt;code&gt;@test-engineer&lt;/code&gt; what you're testing, it automatically picks the right level.&lt;/p&gt;




&lt;h2&gt;
  
  
  What Makes a Good Test Name
&lt;/h2&gt;

&lt;p&gt;&lt;code&gt;@test-engineer&lt;/code&gt; enforces descriptive test names as a specification:&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;// Bad: tells you nothing when it fails&lt;/span&gt;
&lt;span class="nf"&gt;it&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;works correctly&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="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="nf"&gt;it&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;handles the case&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="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="nf"&gt;it&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;test 3&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="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="c1"&gt;// Good: reads like a requirement&lt;/span&gt;
&lt;span class="nf"&gt;it&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;excludes completed tasks from the active task list&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="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="nf"&gt;it&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;sorts high priority tasks before low priority tasks&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="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="nf"&gt;it&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;throws NotFoundError when task ID does not exist&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="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;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;When a test fails, you need to understand what broke from the test name alone — without reading the implementation. Good names make CI failures self-explanatory.&lt;/p&gt;




&lt;h2&gt;
  
  
  The One Rule That Changes Your Debugging Forever
&lt;/h2&gt;

&lt;blockquote&gt;
&lt;p&gt;"A bug fix without a reproduction test is not a bug fix. It's a guess."&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Before &lt;code&gt;@test-engineer&lt;/code&gt;, I fixed bugs by intuition — change the thing that looks wrong, verify it works, move on. Sometimes I was right. Sometimes I was fixing a symptom while the cause festered.&lt;/p&gt;

&lt;p&gt;Now: every bug gets a reproduction test first. Every fix gets verified by that test. Every test stays in the suite permanently.&lt;/p&gt;

&lt;p&gt;The cumulative effect: each bug I fix makes the next bug harder to introduce. The test suite gets smarter with every incident. The codebase gets more resilient over time, not less.&lt;/p&gt;




&lt;h2&gt;
  
  
  Get the Template
&lt;/h2&gt;

&lt;p&gt;&lt;code&gt;@test-engineer&lt;/code&gt; is part of the &lt;strong&gt;copilot-workflow&lt;/strong&gt; template — one setup, every repo.&lt;/p&gt;

&lt;p&gt;👉 &lt;strong&gt;&lt;a href="https://github.com/panditAbhis/copilot-workflow" rel="noopener noreferrer"&gt;github.com/panditAbhis/copilot-workflow&lt;/a&gt;&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Next in the series:&lt;/strong&gt; Part 4 — &lt;code&gt;@security-auditor&lt;/code&gt; and threat modeling. How to think like an attacker before an attacker does.&lt;/p&gt;




&lt;p&gt;&lt;strong&gt;Series navigation&lt;/strong&gt;&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Part&lt;/th&gt;
&lt;th&gt;Title&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;1&lt;/td&gt;
&lt;td&gt;&lt;a href="https://dev.to/panditabhis/your-copilot-has-no-memory-heres-how-i-fixed-that-in-5-minutes-34l7"&gt;Your Copilot Has No Memory. Here's How I Fixed That in 5 Minutes.&lt;/a&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;2&lt;/td&gt;
&lt;td&gt;&lt;a href="https://dev.to/panditabhis/stop-merging-blind-how-i-use-code-reviewer-before-every-pr-2eji"&gt;Stop Merging Blind: How I Use @code-reviewer Before Every PR&lt;/a&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;3&lt;/td&gt;
&lt;td&gt;&lt;a href="https://dev.to/panditabhis/never-fix-a-bug-without-proof-the-test-engineer-prove-it-pattern-1cmh"&gt;Never Fix a Bug Without Proof: The @test-engineer Prove-It Pattern&lt;/a&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;4&lt;/td&gt;
&lt;td&gt;&lt;a href="https://dev.to/panditabhis/think-like-an-attacker-how-i-use-security-auditor-before-every-production-deploy-5434"&gt;Think Like an Attacker: How I Use @security-auditor Before Every Production Deploy&lt;/a&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;5&lt;/td&gt;
&lt;td&gt;&lt;a href="https://dev.to/panditabhis/one-command-to-rule-them-all-the-ship-chatmode-that-reviews-audits-and-cleans-before-every-merge-5590"&gt;One Command to Rule Them All: The /ship Chatmode&lt;/a&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;6&lt;/td&gt;
&lt;td&gt;&lt;a href="https://dev.to/panditabhis/stop-building-the-wrong-thing-how-i-use-spec-writer-and-planner-before-writing-a-single-line-of-1jd0"&gt;Stop Building the Wrong Thing: @spec-writer and @planner&lt;/a&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;7&lt;/td&gt;
&lt;td&gt;&lt;a href="https://dev.to/panditabhis/a-day-in-the-life-complete-copilot-workflow-session-from-idea-to-merged-pr-4cdb"&gt;A Day in the Life: Complete Session Walkthrough&lt;/a&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

</description>
      <category>github</category>
      <category>githubcopilot</category>
      <category>testing</category>
      <category>productivity</category>
    </item>
    <item>
      <title>Stop Merging Blind: How I Use @code-reviewer Before Every PR</title>
      <dc:creator>Abhishek Pandit</dc:creator>
      <pubDate>Fri, 12 Jun 2026 13:06:53 +0000</pubDate>
      <link>https://dev.to/panditabhis/stop-merging-blind-how-i-use-code-reviewer-before-every-pr-2eji</link>
      <guid>https://dev.to/panditabhis/stop-merging-blind-how-i-use-code-reviewer-before-every-pr-2eji</guid>
      <description>&lt;p&gt;Think about the last time you merged a PR without a proper review.&lt;/p&gt;

&lt;p&gt;Maybe tests passed. Maybe it looked fine at a glance. Maybe you were moving fast and told yourself you'd clean it up later.&lt;/p&gt;

&lt;p&gt;Then two weeks later, a bug surfaces. Or a junior dev inherits the code and spends a day trying to understand it. Or a security scanner flags something in a production deploy.&lt;/p&gt;

&lt;p&gt;"Looked fine" is not a review. It's a hope.&lt;/p&gt;

&lt;p&gt;This is Part 2 of the &lt;strong&gt;copilot-workflow&lt;/strong&gt; series. &lt;a href="https://dev.to/panditabhis/your-copilot-has-no-memory-heres-how-i-fixed-that-in-5-minutes-34l7"&gt;Part 1&lt;/a&gt; covered setting up the template. This one covers using &lt;code&gt;@code-reviewer&lt;/code&gt; — the staff engineer persona that reviews every change before it touches main.&lt;/p&gt;




&lt;h2&gt;
  
  
  The Problem With Most Code Review
&lt;/h2&gt;

&lt;p&gt;Code review fails in two ways.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;It gets skipped.&lt;/strong&gt; You're the only dev on a project, or the team is moving fast, or "it's just a small change." The PR merges without anyone really looking at it.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;It gets rubber-stamped.&lt;/strong&gt; Someone glances at the diff, sees nothing obviously on fire, and clicks Approve. This catches maybe 20% of real issues — the obvious ones. The subtle ones sail through.&lt;/p&gt;

&lt;p&gt;What you actually need is a reviewer who checks &lt;strong&gt;multiple dimensions&lt;/strong&gt; systematically. Not just "does this work" but also "is this readable," "does this fit the architecture," "is there a security hole," "will this cause a performance problem at scale."&lt;/p&gt;

&lt;p&gt;That's what &lt;code&gt;@code-reviewer&lt;/code&gt; does.&lt;/p&gt;




&lt;h2&gt;
  
  
  The 5-Axis Review Framework
&lt;/h2&gt;

&lt;p&gt;Think of your code like a restaurant being health-inspected.&lt;/p&gt;

&lt;p&gt;A good inspector doesn't just taste the food. They check the kitchen temperature (correctness), whether the menu is readable (readability), whether the kitchen layout makes sense (architecture), whether hygiene standards are met (security), and whether the kitchen can handle a full service (performance).&lt;/p&gt;

&lt;p&gt;Pass all five. Not just one.&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Axis&lt;/th&gt;
&lt;th&gt;The question&lt;/th&gt;
&lt;th&gt;What it catches&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Correctness&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Does it do what it claims?&lt;/td&gt;
&lt;td&gt;Edge cases, error paths, off-by-one errors, race conditions&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Readability&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Can a stranger understand it?&lt;/td&gt;
&lt;td&gt;Confusing names, deeply nested logic, missing context&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Architecture&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Does it fit the system?&lt;/td&gt;
&lt;td&gt;Circular dependencies, wrong abstraction level, code duplication&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Security&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Can it be exploited?&lt;/td&gt;
&lt;td&gt;Unvalidated input, SQL injection, exposed secrets, missing auth checks&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Performance&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Will it survive load?&lt;/td&gt;
&lt;td&gt;N+1 queries, unbounded loops, missing pagination, blocking operations&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;




&lt;h2&gt;
  
  
  The Labeling System That Changes Everything
&lt;/h2&gt;

&lt;p&gt;Most review feedback is undifferentiated. Everything comes in as a comment and you have no idea what's blocking the merge vs. what's a nice-to-have. So you either fix everything (slow) or fix nothing (wrong).&lt;/p&gt;

&lt;p&gt;&lt;code&gt;@code-reviewer&lt;/code&gt; labels every finding:&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Label&lt;/th&gt;
&lt;th&gt;Meaning&lt;/th&gt;
&lt;th&gt;What to do&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Critical&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Blocks merge. Security vulnerability, data loss risk, broken functionality.&lt;/td&gt;
&lt;td&gt;Fix before the PR goes anywhere.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Important&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Should fix before merge. Wrong abstraction, missing test, poor error handling.&lt;/td&gt;
&lt;td&gt;Fix unless you have a very good reason not to.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Suggestion&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Optional improvement. Naming, style, minor optimization.&lt;/td&gt;
&lt;td&gt;Take it or leave it. Your call.&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;This means you read the review once and immediately know your priority order. No guessing what the reviewer considers urgent.&lt;/p&gt;




&lt;h2&gt;
  
  
  How to Use It
&lt;/h2&gt;

&lt;h3&gt;
  
  
  Basic review
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;@code-reviewer Review the changes in src/auth/login.ts
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Give it context — better output
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;@code-reviewer Review src/auth/login.ts — this adds JWT refresh token rotation. 
Focus on the token storage and the edge case where both tokens expire simultaneously.
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The more context you give, the more targeted the output. Tell it what changed and what you're worried about.&lt;/p&gt;

&lt;h3&gt;
  
  
  Review a whole feature
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;@code-reviewer Review the changes across src/api/users/ and src/middleware/auth.ts — 
this is a new role-based access control system. The spec said admin users should 
be able to manage other users but not delete their own account.
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Paste a diff directly
&lt;/h3&gt;

&lt;p&gt;For small changes, paste the diff straight into chat:&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="p"&gt;@&lt;/span&gt;&lt;span class="nd"&gt;code&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="nx"&gt;reviewer&lt;/span&gt; &lt;span class="nx"&gt;Review&lt;/span&gt; &lt;span class="k"&gt;this&lt;/span&gt; &lt;span class="nx"&gt;change&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;

&lt;span class="o"&gt;-&lt;/span&gt; &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;user&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;db&lt;/span&gt;&lt;span class="p"&gt;.&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;`SELECT * FROM users WHERE id = &lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;userId&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;`&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;user&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;db&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;query&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;SELECT * FROM users WHERE id = $1&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="nx"&gt;userId&lt;/span&gt;&lt;span class="p"&gt;]);&lt;/span&gt;

&lt;span class="nx"&gt;Is&lt;/span&gt; &lt;span class="k"&gt;this&lt;/span&gt; &lt;span class="nx"&gt;fix&lt;/span&gt; &lt;span class="nx"&gt;complete&lt;/span&gt; &lt;span class="nx"&gt;or&lt;/span&gt; &lt;span class="nx"&gt;are&lt;/span&gt; &lt;span class="nx"&gt;there&lt;/span&gt; &lt;span class="nx"&gt;other&lt;/span&gt; &lt;span class="nx"&gt;injection&lt;/span&gt; &lt;span class="nx"&gt;vectors&lt;/span&gt; &lt;span class="k"&gt;in&lt;/span&gt; &lt;span class="k"&gt;this&lt;/span&gt; &lt;span class="nx"&gt;file&lt;/span&gt;&lt;span class="p"&gt;?&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;






&lt;h2&gt;
  
  
  What the Output Looks Like
&lt;/h2&gt;

&lt;p&gt;Here's a real-world example. Imagine you've written a password reset endpoint:&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;app&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;post&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;/api/auth/reset-password&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="k"&gt;async &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;res&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="kd"&gt;const&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;token&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;newPassword&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="o"&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;body&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;user&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;db&lt;/span&gt;&lt;span class="p"&gt;.&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;`SELECT * FROM users WHERE reset_token = '&lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;token&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;'`&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="o"&gt;!&lt;/span&gt;&lt;span class="nx"&gt;user&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;res&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;status&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;404&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="s1"&gt;Invalid token&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt; &lt;span class="p"&gt;});&lt;/span&gt;
  &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;db&lt;/span&gt;&lt;span class="p"&gt;.&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;`UPDATE users SET password = '&lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;newPassword&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;' WHERE id = &lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;user&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;id&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;`&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="nx"&gt;res&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;success&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;Ask &lt;code&gt;@code-reviewer&lt;/code&gt; to review it. You'll get something like this:&lt;/p&gt;




&lt;p&gt;&lt;strong&gt;Verdict: REQUEST CHANGES&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Overview:&lt;/strong&gt; Password reset endpoint with multiple critical security vulnerabilities and missing validation. Do not merge.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Critical Issues&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;Line 3&lt;/code&gt; — SQL injection via string interpolation in token lookup. Use parameterized query: &lt;code&gt;db.query('SELECT * FROM users WHERE reset_token = $1', [token])&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;Line 5&lt;/code&gt; — Password stored as plaintext. Hash with bcrypt before storing: &lt;code&gt;await bcrypt.hash(newPassword, 12)&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;Line 5&lt;/code&gt; — Second SQL injection in UPDATE statement. Parameterize both values.&lt;/li&gt;
&lt;li&gt;Missing — Reset token not invalidated after use. An attacker who intercepts the token can reset the password again.&lt;/li&gt;
&lt;li&gt;Missing — No token expiry check. Tokens should expire after 15-30 minutes.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Important Issues&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Missing — No rate limiting on this endpoint. Brute-force token guessing is possible.&lt;/li&gt;
&lt;li&gt;Missing — No input validation on &lt;code&gt;newPassword&lt;/code&gt;. Minimum length, complexity requirements should be enforced.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;Line 4&lt;/code&gt; — Error message reveals whether a token exists. Return a generic message regardless of outcome to prevent token enumeration.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Suggestions&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Consider logging password reset events to an audit log (user ID, timestamp, IP).&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;What's Done Well&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Endpoint is correctly unauthenticated — password reset flows shouldn't require a logged-in session.&lt;/li&gt;
&lt;/ul&gt;




&lt;p&gt;That is what a real review looks like. Five critical issues. Two important ones. A genuine suggestion. All labeled. Priorities clear.&lt;/p&gt;

&lt;p&gt;Without this review, that code merges and your users' passwords are stored in plaintext with SQL injection vulnerabilities.&lt;/p&gt;




&lt;h2&gt;
  
  
  When to Invoke It
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;Before every PR — no exceptions.&lt;/strong&gt; This is the rule.&lt;/p&gt;

&lt;p&gt;The cost is one &lt;code&gt;@code-reviewer&lt;/code&gt; message. The cost of skipping it is a production incident, a security breach, or a codebase that quietly becomes harder to maintain.&lt;/p&gt;

&lt;p&gt;Three situations where it's especially valuable:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;1. Code you wrote quickly.&lt;/strong&gt; When you're moving fast you make tradeoffs. The reviewer surfaces those tradeoffs before they become permanent.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;2. Code another AI generated.&lt;/strong&gt; Copilot autocomplete, ChatGPT, whatever. AI-generated code is confident and plausible even when wrong. It needs more scrutiny, not less.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;3. Code in unfamiliar territory.&lt;/strong&gt; If you're writing auth logic but auth isn't your specialty, &lt;code&gt;@code-reviewer&lt;/code&gt; + &lt;code&gt;@security-auditor&lt;/code&gt; in combination is an extremely strong safety net.&lt;/p&gt;




&lt;h2&gt;
  
  
  The Mental Shift
&lt;/h2&gt;

&lt;p&gt;Here's what changes when you run &lt;code&gt;@code-reviewer&lt;/code&gt; consistently.&lt;/p&gt;

&lt;p&gt;You stop thinking "does this work?" and start thinking "is this ready?" Those are different questions. Code can work and still be wrong — wrong for maintainability, wrong for security, wrong for the next engineer who has to touch it.&lt;/p&gt;

&lt;p&gt;A passing test suite tells you the code does what you tested. The 5-axis review tells you whether it's actually ready to ship.&lt;/p&gt;




&lt;h2&gt;
  
  
  Get the Template
&lt;/h2&gt;

&lt;p&gt;This agent is part of the &lt;strong&gt;copilot-workflow&lt;/strong&gt; template — one setup, works in every repo you create from it.&lt;/p&gt;

&lt;p&gt;👉 &lt;strong&gt;&lt;a href="https://github.com/panditAbhis/copilot-workflow" rel="noopener noreferrer"&gt;github.com/panditAbhis/copilot-workflow&lt;/a&gt;&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Next in the series:&lt;/strong&gt; Part 3 covers &lt;code&gt;@test-engineer&lt;/code&gt; and the Prove-It Pattern — how to write a failing test that proves a bug exists before you touch a single line of fix code.&lt;/p&gt;

&lt;p&gt;If this was useful, follow for the rest of the series and drop a ⭐ on the repo.&lt;/p&gt;




&lt;p&gt;&lt;strong&gt;Series navigation&lt;/strong&gt;&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Part&lt;/th&gt;
&lt;th&gt;Title&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;1&lt;/td&gt;
&lt;td&gt;&lt;a href="https://dev.to/panditabhis/your-copilot-has-no-memory-heres-how-i-fixed-that-in-5-minutes-34l7"&gt;Your Copilot Has No Memory. Here's How I Fixed That in 5 Minutes.&lt;/a&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;2&lt;/td&gt;
&lt;td&gt;&lt;a href="https://dev.to/panditabhis/stop-merging-blind-how-i-use-code-reviewer-before-every-pr-2eji"&gt;Stop Merging Blind: How I Use @code-reviewer Before Every PR&lt;/a&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;3&lt;/td&gt;
&lt;td&gt;&lt;a href="https://dev.to/panditabhis/never-fix-a-bug-without-proof-the-test-engineer-prove-it-pattern-1cmh"&gt;Never Fix a Bug Without Proof: The @test-engineer Prove-It Pattern&lt;/a&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;4&lt;/td&gt;
&lt;td&gt;&lt;a href="https://dev.to/panditabhis/think-like-an-attacker-how-i-use-security-auditor-before-every-production-deploy-5434"&gt;Think Like an Attacker: How I Use @security-auditor Before Every Production Deploy&lt;/a&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;5&lt;/td&gt;
&lt;td&gt;&lt;a href="https://dev.to/panditabhis/one-command-to-rule-them-all-the-ship-chatmode-that-reviews-audits-and-cleans-before-every-merge-5590"&gt;One Command to Rule Them All: The /ship Chatmode&lt;/a&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;6&lt;/td&gt;
&lt;td&gt;&lt;a href="https://dev.to/panditabhis/stop-building-the-wrong-thing-how-i-use-spec-writer-and-planner-before-writing-a-single-line-of-1jd0"&gt;Stop Building the Wrong Thing: @spec-writer and @planner&lt;/a&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;7&lt;/td&gt;
&lt;td&gt;&lt;a href="https://dev.to/panditabhis/a-day-in-the-life-complete-copilot-workflow-session-from-idea-to-merged-pr-4cdb"&gt;A Day in the Life: Complete Session Walkthrough&lt;/a&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

</description>
      <category>github</category>
      <category>githubcopilot</category>
      <category>codereview</category>
      <category>productivity</category>
    </item>
    <item>
      <title>Your Copilot Has No Memory. Here's How I Fixed That in 5 Minutes.</title>
      <dc:creator>Abhishek Pandit</dc:creator>
      <pubDate>Fri, 12 Jun 2026 12:50:19 +0000</pubDate>
      <link>https://dev.to/panditabhis/your-copilot-has-no-memory-heres-how-i-fixed-that-in-5-minutes-34l7</link>
      <guid>https://dev.to/panditabhis/your-copilot-has-no-memory-heres-how-i-fixed-that-in-5-minutes-34l7</guid>
      <description>&lt;p&gt;Imagine you hire a brilliant contractor.&lt;/p&gt;

&lt;p&gt;Sharp, fast, knows every technology. You spend the first morning briefing them: &lt;em&gt;"We do test-driven development here. Security is non-negotiable. PRs stay under 300 lines. We parameterize every database query — no exceptions."&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;They nod. They get it. The day goes brilliantly.&lt;/p&gt;

&lt;p&gt;Next morning they show up with no memory of any of it.&lt;/p&gt;

&lt;p&gt;You brief them again. They get it again. Day goes great.&lt;/p&gt;

&lt;p&gt;This repeats. Every. Single. Day.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;That is GitHub Copilot without configuration.&lt;/strong&gt;&lt;/p&gt;




&lt;h2&gt;
  
  
  The Problem Nobody Talks About
&lt;/h2&gt;

&lt;p&gt;Copilot is genuinely impressive. But it's stateless. Every session starts from zero. It doesn't know:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Your testing philosophy (TDD? integration-first? mocks or no mocks?)&lt;/li&gt;
&lt;li&gt;Your security rules (parameterized queries, no secrets in code, validate at the boundary)&lt;/li&gt;
&lt;li&gt;Your review standards (what makes a PR approvable vs. blocked)&lt;/li&gt;
&lt;li&gt;Which specialist mindset you need right now (reviewer? tester? security auditor?)&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;So you either re-prompt it every session — which nobody actually does — or you accept generic output that fits nobody's codebase in particular.&lt;/p&gt;

&lt;p&gt;There's a better way.&lt;/p&gt;




&lt;h2&gt;
  
  
  The Fix: Give Copilot an Employee Handbook
&lt;/h2&gt;

&lt;p&gt;GitHub Copilot has a feature most people don't know exists: &lt;strong&gt;if you put a file at &lt;code&gt;.github/copilot-instructions.md&lt;/code&gt; in your repo, Copilot reads it automatically — every session, forever.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;No prompting. No setup ritual. It just knows your rules.&lt;/p&gt;

&lt;p&gt;And there's more. Put specialist personas in &lt;code&gt;.github/agents/&lt;/code&gt; and you can summon them in Copilot Chat with a single &lt;code&gt;@&lt;/code&gt; mention. One message and Copilot becomes a staff engineer doing a 5-axis code review. Another message and it's a QA engineer writing tests using the Prove-It pattern. Another and it's a security auditor running STRIDE analysis against your auth flow.&lt;/p&gt;

&lt;p&gt;I built a template that sets all of this up for you. Here's how it works.&lt;/p&gt;




&lt;h2&gt;
  
  
  What You're Getting
&lt;/h2&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;.github/
  copilot-instructions.md   ← Copilot reads this every session. Your rules, automatically.
  agents/
    code-reviewer.md        ← Staff engineer. 5-axis review before every merge.
    test-engineer.md        ← QA engineer. TDD, coverage gaps, bug reproduction tests.
    security-auditor.md     ← Security engineer. OWASP Top 10. STRIDE. Real vulnerabilities only.
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Three specialists on speed dial. Zero configuration after the first setup.&lt;/p&gt;




&lt;h2&gt;
  
  
  5-Minute Setup
&lt;/h2&gt;

&lt;h3&gt;
  
  
  What you need first
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;GitHub account&lt;/li&gt;
&lt;li&gt;&lt;a href="https://code.visualstudio.com/" rel="noopener noreferrer"&gt;VS Code&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;GitHub Copilot subscription (Individual, Business, or Enterprise)&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://marketplace.visualstudio.com/items?itemName=GitHub.copilot" rel="noopener noreferrer"&gt;GitHub Copilot extension&lt;/a&gt; installed in VS Code&lt;/li&gt;
&lt;li&gt;GitHub Copilot Chat extension installed in VS Code&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;That's it. No CLI tools. No config files to write manually.&lt;/p&gt;




&lt;h3&gt;
  
  
  Step 1 — Create your repo from the template
&lt;/h3&gt;

&lt;p&gt;Go to 👉 &lt;strong&gt;&lt;a href="https://github.com/panditAbhis/copilot-workflow" rel="noopener noreferrer"&gt;github.com/panditAbhis/copilot-workflow&lt;/a&gt;&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Click the green &lt;strong&gt;"Use this template"&lt;/strong&gt; button → &lt;strong&gt;"Create a new repository"&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;Name your repo, set visibility, click &lt;strong&gt;Create repository&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;Your new repo now has the &lt;code&gt;.github/&lt;/code&gt; folder with everything pre-configured. Copilot picks it up automatically — no further action needed.&lt;/p&gt;




&lt;h3&gt;
  
  
  Step 2 — Verify it's working
&lt;/h3&gt;

&lt;p&gt;Open your new repo in VS Code. Open Copilot Chat (&lt;code&gt;Ctrl+Alt+I&lt;/code&gt; on Windows/Linux, &lt;code&gt;Cmd+Alt+I&lt;/code&gt; on Mac).&lt;/p&gt;

&lt;p&gt;Ask:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;What are the coding standards for this project?
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Copilot should describe your testing pyramid, review standards, and security rules — without you typing a single instruction. If it does, you're done with setup.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Doesn't work?&lt;/strong&gt; Open VS Code settings and confirm &lt;code&gt;github.copilot.chat.codeGeneration.useInstructionFiles&lt;/code&gt; is set to &lt;code&gt;true&lt;/code&gt;. It's the default, but worth checking.&lt;/p&gt;
&lt;/blockquote&gt;




&lt;h3&gt;
  
  
  Step 3 — Meet your three specialists
&lt;/h3&gt;

&lt;p&gt;Open Copilot Chat and try these:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;The Code Reviewer:&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;@code-reviewer Review the changes in src/auth/login.ts
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;You get a structured report: Critical issues (blocks merge), Important issues (should fix), Suggestions (optional). Labeled so you know exactly what's required.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;The Test Engineer:&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;@test-engineer I have a bug — users can log in with an expired token. Write a failing test that proves this bug exists before I fix it.
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This is the &lt;strong&gt;Prove-It Pattern&lt;/strong&gt;: write a test that fails with the current broken code, then fix the code until the test passes. The test is your proof the bug existed and is now gone.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;The Security Auditor:&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;@security-auditor Audit my file upload handler. I want OWASP Top 10 coverage and a STRIDE threat analysis.
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Findings are classified by severity (Critical/High/Medium/Low) with proof-of-concept exploitation and specific remediation code — not vague "consider validating input" advice.&lt;/p&gt;




&lt;h2&gt;
  
  
  What the Coding Standards Actually Cover
&lt;/h2&gt;

&lt;p&gt;The &lt;code&gt;copilot-instructions.md&lt;/code&gt; bakes in three areas of discipline. Here's the short version:&lt;/p&gt;

&lt;h3&gt;
  
  
  Testing
&lt;/h3&gt;

&lt;p&gt;Copilot knows the test pyramid: 80% unit tests (pure logic, no database, milliseconds each), 15% integration tests (real database, localhost only), 5% E2E tests (critical user paths only).&lt;/p&gt;

&lt;p&gt;It knows to test &lt;strong&gt;what code does&lt;/strong&gt;, not &lt;strong&gt;how it does it internally&lt;/strong&gt;. Tests that verify method call sequences break when you refactor — even if behavior is unchanged. State-based assertions survive refactoring.&lt;/p&gt;

&lt;h3&gt;
  
  
  Code Quality
&lt;/h3&gt;

&lt;p&gt;PRs under ~100 lines are ideal. Over 1000 lines, it'll tell you to split. Refactoring PRs and feature PRs stay separate — always. No dead code, no "I'll clean this up later" shims. Abstractions don't get written until the third time you need them.&lt;/p&gt;

&lt;h3&gt;
  
  
  Security
&lt;/h3&gt;

&lt;p&gt;Every external input is hostile until validated. Database queries are parameterized — no concatenating user input into SQL, ever. Secrets stay in &lt;code&gt;.env&lt;/code&gt;, never in code. LLM output (yes, even Copilot's output) is treated as untrusted input — it never goes directly into &lt;code&gt;eval&lt;/code&gt;, SQL, or HTML.&lt;/p&gt;




&lt;h2&gt;
  
  
  The Analogy That Clicked for Me
&lt;/h2&gt;

&lt;p&gt;Think of this setup like a &lt;strong&gt;kitchen&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;Vanilla Copilot is like a chef who shows up not knowing your restaurant's cuisine, your signature dishes, your allergen rules, or your prep standards. You explain everything every service.&lt;/p&gt;

&lt;p&gt;This template is the &lt;strong&gt;kitchen bible&lt;/strong&gt; — laminated, on the wall, permanent. The chef reads it once at the start of each shift. You never explain the basics again.&lt;/p&gt;

&lt;p&gt;The three agents are your &lt;strong&gt;sous-chefs&lt;/strong&gt;: a quality inspector who tastes every dish before it leaves the kitchen, a prep specialist who sets up the station correctly before service, and a health inspector who checks that nothing is going to make someone sick.&lt;/p&gt;

&lt;p&gt;You're still the head chef. You make the calls. They make you faster and safer.&lt;/p&gt;




&lt;h2&gt;
  
  
  What's Next in This Series
&lt;/h2&gt;

&lt;p&gt;This is part 1. Here's what's coming:&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Part&lt;/th&gt;
&lt;th&gt;What you'll learn&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;1 — this one&lt;/td&gt;
&lt;td&gt;Setup: instructions file + three agent personas&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;2&lt;/td&gt;
&lt;td&gt;Deep dive: using &lt;code&gt;@code-reviewer&lt;/code&gt; before every PR&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;3&lt;/td&gt;
&lt;td&gt;Deep dive: TDD with &lt;code&gt;@test-engineer&lt;/code&gt; and the Prove-It pattern&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;4&lt;/td&gt;
&lt;td&gt;Deep dive: threat modeling with &lt;code&gt;@security-auditor&lt;/code&gt;
&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;5&lt;/td&gt;
&lt;td&gt;Building agentic workflows — the &lt;code&gt;/ship&lt;/code&gt; command that fans out all three in parallel&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;




&lt;h2&gt;
  
  
  Get the Template
&lt;/h2&gt;

&lt;p&gt;👉 &lt;strong&gt;&lt;a href="https://github.com/panditAbhis/copilot-workflow" rel="noopener noreferrer"&gt;github.com/panditAbhis/copilot-workflow&lt;/a&gt;&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Click &lt;strong&gt;Use this template&lt;/strong&gt;. Five minutes. Done.&lt;/p&gt;

&lt;p&gt;If this helped, follow for the rest of the series — and drop a ⭐ on the repo so others can find it.&lt;/p&gt;




&lt;p&gt;&lt;em&gt;Coding standards in this template are derived from &lt;a href="https://github.com/addyosmani/agent-skills" rel="noopener noreferrer"&gt;Addy Osmani's agent-skills&lt;/a&gt;, Google's Software Engineering practices, and OWASP guidelines.&lt;/em&gt;&lt;/p&gt;




&lt;p&gt;&lt;strong&gt;Series navigation&lt;/strong&gt;&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Part&lt;/th&gt;
&lt;th&gt;Title&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;1&lt;/td&gt;
&lt;td&gt;&lt;a href="https://dev.to/panditabhis/your-copilot-has-no-memory-heres-how-i-fixed-that-in-5-minutes-34l7"&gt;Your Copilot Has No Memory. Here's How I Fixed That in 5 Minutes.&lt;/a&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;2&lt;/td&gt;
&lt;td&gt;&lt;a href="https://dev.to/panditabhis/stop-merging-blind-how-i-use-code-reviewer-before-every-pr-2eji"&gt;Stop Merging Blind: How I Use @code-reviewer Before Every PR&lt;/a&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;3&lt;/td&gt;
&lt;td&gt;&lt;a href="https://dev.to/panditabhis/never-fix-a-bug-without-proof-the-test-engineer-prove-it-pattern-1cmh"&gt;Never Fix a Bug Without Proof: The @test-engineer Prove-It Pattern&lt;/a&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;4&lt;/td&gt;
&lt;td&gt;&lt;a href="https://dev.to/panditabhis/think-like-an-attacker-how-i-use-security-auditor-before-every-production-deploy-5434"&gt;Think Like an Attacker: How I Use @security-auditor Before Every Production Deploy&lt;/a&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;5&lt;/td&gt;
&lt;td&gt;&lt;a href="https://dev.to/panditabhis/one-command-to-rule-them-all-the-ship-chatmode-that-reviews-audits-and-cleans-before-every-merge-5590"&gt;One Command to Rule Them All: The /ship Chatmode&lt;/a&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;6&lt;/td&gt;
&lt;td&gt;&lt;a href="https://dev.to/panditabhis/stop-building-the-wrong-thing-how-i-use-spec-writer-and-planner-before-writing-a-single-line-of-1jd0"&gt;Stop Building the Wrong Thing: @spec-writer and @planner&lt;/a&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;7&lt;/td&gt;
&lt;td&gt;&lt;a href="https://dev.to/panditabhis/a-day-in-the-life-complete-copilot-workflow-session-from-idea-to-merged-pr-4cdb"&gt;A Day in the Life: Complete Session Walkthrough&lt;/a&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

</description>
      <category>github</category>
      <category>githubcopilot</category>
      <category>ai</category>
      <category>productivity</category>
    </item>
    <item>
      <title>A Day in the Life: Complete Claude Code Session Walkthrough</title>
      <dc:creator>Abhishek Pandit</dc:creator>
      <pubDate>Wed, 10 Jun 2026 15:15:04 +0000</pubDate>
      <link>https://dev.to/panditabhis/a-day-in-the-life-complete-claude-code-session-walkthrough-7o9</link>
      <guid>https://dev.to/panditabhis/a-day-in-the-life-complete-claude-code-session-walkthrough-7o9</guid>
      <description>&lt;p&gt;&lt;em&gt;Part 7 of 7 · Series: &lt;a href="https://dev.to/panditabhis/how-i-turned-claude-into-a-disciplined-senior-developer-not-just-a-fast-one-1a59"&gt;Building Your AI Developer Handbook&lt;/a&gt; · &lt;a href="https://github.com/panditAbhis/claude-workflow" rel="noopener noreferrer"&gt;GitHub&lt;/a&gt;&lt;/em&gt;&lt;/p&gt;




&lt;h2&gt;
  
  
  The Scenario
&lt;/h2&gt;

&lt;p&gt;You're building a password reset feature. User enters email → gets a reset link → clicks link → enters new password. Standard flow. Medium complexity.&lt;/p&gt;

&lt;p&gt;Let's walk through every step using the full workflow — as if you're looking over the shoulder of someone who built this system.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;"Show me your workflow and I'll show you your output quality."&lt;/p&gt;
&lt;/blockquote&gt;




&lt;h2&gt;
  
  
  Before You Even Type
&lt;/h2&gt;

&lt;p&gt;Claude loads automatically in the background:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;✓ ~/.claude/CLAUDE.md loaded           ← the global handbook
✓ .claude/CLAUDE.md loaded             ← project rules (TypeScript, pnpm)
✓ memory/MEMORY.md scanned             ← all lessons and preferences
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;You haven't typed anything yet. Claude already knows:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Feature-based folder structure&lt;/li&gt;
&lt;li&gt;State management ladder&lt;/li&gt;
&lt;li&gt;No mocking the database&lt;/li&gt;
&lt;li&gt;No AI attribution in commits&lt;/li&gt;
&lt;li&gt;No &lt;code&gt;useCallback&lt;/code&gt; without profiler evidence&lt;/li&gt;
&lt;/ul&gt;

&lt;blockquote&gt;
&lt;p&gt;"A doctor who reviews your file before you enter the room is more useful than one who asks 'so, remind me who you are?'"&lt;/p&gt;
&lt;/blockquote&gt;




&lt;h2&gt;
  
  
  Step 1: /status — Confirm the Setup
&lt;/h2&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight console"&gt;&lt;code&gt;&lt;span class="go"&gt;/status
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;





&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Model:   claude-sonnet-4-6
Effort:  normal
Plugins: security-guidance ✓
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Thirty seconds. Sometimes the wrong model loads due to overload fallback. Sometimes a plugin fails silently. This check costs 30 seconds and prevents a surprise 30 minutes later.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;"A pilot's first action after sitting in the cockpit isn't to take off. It's to check all instruments are reading correctly."&lt;/p&gt;
&lt;/blockquote&gt;




&lt;h2&gt;
  
  
  Step 2: /cost — Baseline
&lt;/h2&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight console"&gt;&lt;code&gt;&lt;span class="go"&gt;/cost
&lt;/span&gt;&lt;span class="gp"&gt;→ Tokens used: 2,847 | Estimated cost: $&lt;/span&gt;0.004
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Note this number. You'll compare it later before the expensive code review step. A surprise spike means something went wrong.&lt;/p&gt;




&lt;h2&gt;
  
  
  Step 3: /plan — Design Before Coding
&lt;/h2&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;/plan

Build a password reset feature:
- User enters email on /forgot-password
- System sends a reset link (token, expires in 1 hour)
- User clicks link → /reset-password?token=xxx
- User enters new password
- Token validated, password updated, token invalidated
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Claude responds with a plan — &lt;strong&gt;no code yet&lt;/strong&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Proposed approach:

1. DB: Add password_reset_tokens table
   - id, user_id, token (hashed), expires_at, used_at

2. API:
   - POST /api/auth/forgot-password — generate token, send email
   - POST /api/auth/reset-password  — validate token, update password

3. UI:
   - /features/auth/ForgotPasswordForm.tsx
   - /features/auth/ResetPasswordForm.tsx
   - usePasswordReset.ts hook

4. Tests:
   - Unit: token generation/validation logic
   - Integration: full flow with real test DB
   - No E2E (not a critical path)

Questions before I start:
- Email provider?
- Token length and hashing algorithm preference?
- Should used tokens be deleted or marked used?
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;blockquote&gt;
&lt;p&gt;"A plan that asks questions is more valuable than a plan that assumes answers."&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;You answer the questions. You approve the plan. &lt;strong&gt;Only now does coding start.&lt;/strong&gt;&lt;/p&gt;




&lt;h2&gt;
  
  
  Step 4: Code — Supervised Implementation
&lt;/h2&gt;

&lt;p&gt;Claude implements. Because the preferences are loaded, it automatically:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Creates &lt;code&gt;/features/auth/ForgotPasswordForm.tsx&lt;/code&gt; — not &lt;code&gt;/components/ForgotPassword.tsx&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;Uses &lt;code&gt;useState&lt;/code&gt; for form fields, TanStack Query for the API calls&lt;/li&gt;
&lt;li&gt;Writes catch blocks that &lt;code&gt;console.error&lt;/code&gt; and surface toasts&lt;/li&gt;
&lt;li&gt;Does NOT add &lt;code&gt;useCallback&lt;/code&gt; to handlers without profiler evidence&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;You watch. You redirect if something drifts.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;"Supervision isn't distrust — it's how you catch small course corrections before they become major detours."&lt;/p&gt;
&lt;/blockquote&gt;




&lt;h2&gt;
  
  
  Step 5: pnpm test — The Blocking Gate
&lt;/h2&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;pnpm &lt;span class="nb"&gt;test&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Must exit 0. Not "mostly passing." Not "the one failing test is unrelated." &lt;strong&gt;All tests pass.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;If they fail:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight console"&gt;&lt;code&gt;&lt;span class="go"&gt;FAIL features/auth/usePasswordReset.test.ts
  ✗ handles expired token correctly
    Expected: { error: 'Token expired' }
    Received: { error: undefined }
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;You stop. You fix it. You run tests again. Only when green do you proceed.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;"You wouldn't send a letter before proofreading it. You wouldn't ship code before testing it. The test gate is the proofread."&lt;/p&gt;
&lt;/blockquote&gt;




&lt;h2&gt;
  
  
  Step 6: /cost — Delta Check
&lt;/h2&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight console"&gt;&lt;code&gt;&lt;span class="go"&gt;/cost
&lt;/span&gt;&lt;span class="gp"&gt;→ Tokens used: 18,492 | Delta: +15,645 | Cost: $&lt;/span&gt;0.026
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;~15k tokens for a medium feature is normal. If you saw +80k tokens, that's a red flag — Claude may have scanned the whole codebase or looped a tool call. Investigate before spending on the code review.&lt;/p&gt;




&lt;h2&gt;
  
  
  Step 7: /code-review — Bug and Design Audit
&lt;/h2&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;/code-review
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Claude reviews the diff — not the whole codebase, just what changed:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;⚠ HIGH  usePasswordReset.ts:47
  Token comparison uses === (timing attack surface).
  Fix: use crypto.timingSafeEqual() instead.

ℹ LOW   ResetPasswordForm.tsx:23
  Loading state not shown during submission.
  Fix: disable submit button while isPending is true.
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;blockquote&gt;
&lt;p&gt;"A second pair of eyes catches what the first pair stopped seeing. Even if both pairs belong to the same AI."&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Fix the HIGH immediately. Decide on the LOW.&lt;/p&gt;




&lt;h2&gt;
  
  
  Step 8: /simplify — Readability Cleanup
&lt;/h2&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;/simplify
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Code review found bugs. Simplify finds clutter:&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;usePasswordReset&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;ts&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nf"&gt;resetForm&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="nx"&gt;called&lt;/span&gt; &lt;span class="k"&gt;in&lt;/span&gt; &lt;span class="mi"&gt;3&lt;/span&gt; &lt;span class="nx"&gt;places&lt;/span&gt; &lt;span class="err"&gt;—&lt;/span&gt; &lt;span class="nx"&gt;extract&lt;/span&gt; &lt;span class="nx"&gt;to&lt;/span&gt; &lt;span class="nx"&gt;reset&lt;/span&gt; &lt;span class="nx"&gt;handler&lt;/span&gt;
&lt;span class="nx"&gt;ForgotPasswordForm&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;tsx&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;inline&lt;/span&gt; &lt;span class="nx"&gt;styles&lt;/span&gt; &lt;span class="nx"&gt;on&lt;/span&gt; &lt;span class="mi"&gt;2&lt;/span&gt; &lt;span class="nx"&gt;elements&lt;/span&gt; &lt;span class="err"&gt;—&lt;/span&gt; &lt;span class="nx"&gt;move&lt;/span&gt; &lt;span class="nx"&gt;to&lt;/span&gt; &lt;span class="nx"&gt;className&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This pass doesn't hunt for bugs. It hunts for code that works but could be cleaner.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;"A code review checks if the bridge is safe. Simplify checks if the bridge is elegant. Both matter."&lt;/p&gt;
&lt;/blockquote&gt;




&lt;h2&gt;
  
  
  Step 9: /code-review --comment — Post to PR
&lt;/h2&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;/code-review --comment
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Posts findings as inline comments directly on the GitHub PR. Reviewers see the notes in context — right on the lines that matter.&lt;/p&gt;

&lt;p&gt;PR is ready for human review.&lt;/p&gt;




&lt;h2&gt;
  
  
  The Full Session, Compressed
&lt;/h2&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Step&lt;/th&gt;
&lt;th&gt;Command&lt;/th&gt;
&lt;th&gt;Time&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;1&lt;/td&gt;
&lt;td&gt;&lt;code&gt;/status&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;30 sec&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;2&lt;/td&gt;
&lt;td&gt;&lt;code&gt;/cost&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;5 sec&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;3&lt;/td&gt;
&lt;td&gt;&lt;code&gt;/plan&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;10–20 min&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;4&lt;/td&gt;
&lt;td&gt;Code&lt;/td&gt;
&lt;td&gt;varies&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;5&lt;/td&gt;
&lt;td&gt;&lt;code&gt;pnpm test&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;2–5 min&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;6&lt;/td&gt;
&lt;td&gt;&lt;code&gt;/cost&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;5 sec&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;7&lt;/td&gt;
&lt;td&gt;&lt;code&gt;/code-review&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;5–10 min&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;8&lt;/td&gt;
&lt;td&gt;&lt;code&gt;/simplify&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;5–10 min&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;9&lt;/td&gt;
&lt;td&gt;&lt;code&gt;/code-review --comment&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;2 min&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;Total gate overhead: ~30–40 minutes. Defects reaching production: dramatically fewer.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;"The gates don't slow you down. They stop you from having to go back."&lt;/p&gt;
&lt;/blockquote&gt;




&lt;h2&gt;
  
  
  What This Looks Like Without the Workflow
&lt;/h2&gt;

&lt;p&gt;Without the gates, the same feature might take 45 minutes to implement. But:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;The token comparison vulnerability reaches production ← timing attack&lt;/li&gt;
&lt;li&gt;A test failure was "unrelated" and got ignored ← it wasn't&lt;/li&gt;
&lt;li&gt;Folder structure drifted from feature-based ← scattered files&lt;/li&gt;
&lt;li&gt;PR has no inline notes ← reviewer has no context&lt;/li&gt;
&lt;li&gt;Session cost 3x more ← nobody checked the delta&lt;/li&gt;
&lt;/ul&gt;

&lt;blockquote&gt;
&lt;p&gt;"Fast and wrong is slower than right the first time."&lt;/p&gt;
&lt;/blockquote&gt;




&lt;h2&gt;
  
  
  Your Turn — Start With Three Things
&lt;/h2&gt;

&lt;p&gt;You don't need to implement this entire workflow today. Start here:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;Create &lt;code&gt;~/.claude/CLAUDE.md&lt;/code&gt;&lt;/strong&gt; with four rules: think first, simplicity, surgical changes, goal-driven&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Create one memory file&lt;/strong&gt; — next time Claude does something you didn't want, write it down&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Add the test gate&lt;/strong&gt; — never proceed past failing tests&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;The rest follows naturally.&lt;/p&gt;




&lt;h2&gt;
  
  
  The Full Series
&lt;/h2&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Part&lt;/th&gt;
&lt;th&gt;Topic&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;a href="https://dev.to/panditabhis/how-i-turned-claude-into-a-disciplined-senior-developer-not-just-a-fast-one-1a59"&gt;Part 1&lt;/a&gt;&lt;/td&gt;
&lt;td&gt;Overview — the full system&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;a href="https://dev.to/panditabhis/your-ais-employee-handbook-a-deep-dive-into-claudemd-3obc"&gt;Part 2&lt;/a&gt;&lt;/td&gt;
&lt;td&gt;The Handbook (CLAUDE.md)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;a href="https://dev.to/panditabhis/teaching-an-ai-to-never-forget-how-the-memory-system-works-40o3"&gt;Part 3&lt;/a&gt;&lt;/td&gt;
&lt;td&gt;The Memory System&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;a href="https://dev.to/panditabhis/battle-scars-as-rules-inside-the-feedback-files-5ejj"&gt;Part 4&lt;/a&gt;&lt;/td&gt;
&lt;td&gt;Battle Scars as Rules (Feedback Files)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;a href="https://dev.to/panditabhis/your-coding-dna-the-three-files-that-shape-every-line-claude-writes-378g"&gt;Part 5&lt;/a&gt;&lt;/td&gt;
&lt;td&gt;Your Coding DNA (User Preferences)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;a href="https://dev.to/panditabhis/context-is-king-how-project-files-and-templates-keep-claude-on-track-433l"&gt;Part 6&lt;/a&gt;&lt;/td&gt;
&lt;td&gt;Context is King (Project + Reference Files)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;a href="https://dev.to/panditabhis/a-day-in-the-life-complete-claude-code-session-walkthrough-7o9"&gt;Part 7&lt;/a&gt;&lt;/td&gt;
&lt;td&gt;A Day in the Life (Complete Walkthrough)&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;&lt;strong&gt;&lt;a href="https://github.com/panditAbhis/claude-workflow" rel="noopener noreferrer"&gt;All workflow files on GitHub&lt;/a&gt;&lt;/strong&gt;&lt;/p&gt;




&lt;blockquote&gt;
&lt;p&gt;"Discipline is not the enemy of creativity. It's the foundation that lets creativity build something that lasts."&lt;/p&gt;
&lt;/blockquote&gt;




&lt;p&gt;&lt;em&gt;Thanks for following the series. If you build your own version of this workflow, share it — I'd love to see how others adapt these ideas.&lt;/em&gt;&lt;/p&gt;

</description>
      <category>claudecode</category>
      <category>ai</category>
      <category>productivity</category>
      <category>tutorial</category>
    </item>
    <item>
      <title>Context is King: How Project Files and Templates Keep Claude on Track</title>
      <dc:creator>Abhishek Pandit</dc:creator>
      <pubDate>Wed, 10 Jun 2026 15:09:19 +0000</pubDate>
      <link>https://dev.to/panditabhis/context-is-king-how-project-files-and-templates-keep-claude-on-track-433l</link>
      <guid>https://dev.to/panditabhis/context-is-king-how-project-files-and-templates-keep-claude-on-track-433l</guid>
      <description>&lt;p&gt;&lt;em&gt;Part 6 of 7 · Series: &lt;a href="https://dev.to/panditabhis/how-i-turned-claude-into-a-disciplined-senior-developer-not-just-a-fast-one-1a59"&gt;Building Your AI Developer Handbook&lt;/a&gt; · &lt;a href="https://github.com/panditAbhis/claude-workflow" rel="noopener noreferrer"&gt;GitHub&lt;/a&gt;&lt;/em&gt;&lt;/p&gt;




&lt;h2&gt;
  
  
  The Context Problem
&lt;/h2&gt;

&lt;p&gt;Every developer switches between projects. Each project has its own setup, ongoing decisions, deadlines, and "why did we do it this way" history.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;"Imagine a consultant who works with five different clients. Every Monday they need a full briefing — 'who are you, what are we building, why did you make that decision last week?' — before they can do anything useful. Now imagine if that briefing happened every single day."&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;That's Claude without project context files.&lt;/p&gt;

&lt;p&gt;Project files and reference files give Claude the backstory before it starts. Not the code — Claude can read the code. The &lt;em&gt;context behind&lt;/em&gt; the code.&lt;/p&gt;




&lt;h2&gt;
  
  
  The Project Context File
&lt;/h2&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight markdown"&gt;&lt;code&gt;&lt;span class="nn"&gt;---&lt;/span&gt;
&lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;project_auth_rewrite&lt;/span&gt;
&lt;span class="na"&gt;type&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;project&lt;/span&gt;
&lt;span class="nn"&gt;---&lt;/span&gt;

Auth system rewrite in progress. Deadline: 2026-06-20.

&lt;span class="gs"&gt;**Why:**&lt;/span&gt; Legal flagged session token storage as non-compliant
with new data regulations.

&lt;span class="gs"&gt;**How to apply:**&lt;/span&gt; Scope decisions favor compliance over
ergonomics until the audit is complete.
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Every project file has three parts:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;The fact&lt;/strong&gt; — what happened or what was decided&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;The why&lt;/strong&gt; — motivation (deadline, constraint, stakeholder, incident)&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;How to apply&lt;/strong&gt; — what should change about Claude's behavior&lt;/li&gt;
&lt;/ol&gt;

&lt;blockquote&gt;
&lt;p&gt;"A sign that says 'do not open this door' is easy to ignore. A sign that says 'do not open this door — last person who did triggered the fire suppression system' is not."&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;The &lt;code&gt;Why&lt;/code&gt; is what lets Claude judge edge cases. Without it, rules are followed blindly. With it, Claude can ask: "does this new situation actually trigger the same concern?"&lt;/p&gt;




&lt;h2&gt;
  
  
  What Goes in a Project File
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;Good content:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Architectural decisions and the trade-offs that led to them&lt;/li&gt;
&lt;li&gt;Ongoing work — who is doing what, by when (use absolute dates, not "next Thursday")&lt;/li&gt;
&lt;li&gt;Why a particular library was chosen over alternatives&lt;/li&gt;
&lt;li&gt;A constraint not obvious from the code (legal, performance, security)&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Bad content — don't memorize these:&lt;/strong&gt;&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Already exists&lt;/th&gt;
&lt;th&gt;Don't put in project file&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Code patterns&lt;/td&gt;
&lt;td&gt;Read the code&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;File structure&lt;/td&gt;
&lt;td&gt;Read the filesystem&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Git history&lt;/td&gt;
&lt;td&gt;Run &lt;code&gt;git log&lt;/code&gt;
&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Things that change daily&lt;/td&gt;
&lt;td&gt;Too stale too fast&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;blockquote&gt;
&lt;p&gt;"A map is useful. A map from three years ago of a city that's been rebuilt is worse than no map — it gives you false confidence."&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Project files decay. The &lt;code&gt;Why&lt;/code&gt; field tells you whether a memory is still load-bearing or just historical noise.&lt;/p&gt;




&lt;h2&gt;
  
  
  When Project Files Shine
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;Without a project file:&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Session start:
"So just to re-explain the context — we're mid-sprint on auth,
there's a compliance deadline, the approach was chosen because..."
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;With a project file:&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;[Claude reads: auth rewrite, deadline 2026-06-20,
compliance is the priority, no ergonomics shortcuts]

You: "should we add a caching layer to the token check?"
Claude: "Given the compliance requirement, caching token validation
would complicate the audit trail — skip it until after the audit."
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Claude makes the right call without being told why. That's context working.&lt;/p&gt;




&lt;h2&gt;
  
  
  The Reference File
&lt;/h2&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight markdown"&gt;&lt;code&gt;&lt;span class="nn"&gt;---&lt;/span&gt;
&lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;reference_template_location&lt;/span&gt;
&lt;span class="na"&gt;type&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;reference&lt;/span&gt;
&lt;span class="nn"&gt;---&lt;/span&gt;

Template path: ~/.claude/templates/ts-react-project.md

Usage: at new repo init, copy to .claude/CLAUDE.md.
Fill in [Project Name] and Architecture section.

Note: verify library versions at each project init — they drift.
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Reference files are simple: &lt;strong&gt;where to find things that live outside the project.&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Where bugs are tracked (Linear, Jira, GitHub Issues)&lt;/li&gt;
&lt;li&gt;Where design assets live (Figma, Storybook)&lt;/li&gt;
&lt;li&gt;Where dashboards are (Grafana, Datadog)&lt;/li&gt;
&lt;li&gt;Where templates live (local filesystem paths)&lt;/li&gt;
&lt;/ul&gt;

&lt;blockquote&gt;
&lt;p&gt;"A good assistant knows where the filing cabinet is. They don't memorize every document inside it — they just know where to look."&lt;/p&gt;
&lt;/blockquote&gt;




&lt;h2&gt;
  
  
  The Template System
&lt;/h2&gt;

&lt;p&gt;The reference file above points to a template: &lt;code&gt;~/.claude/templates/ts-react-project.md&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;This is a complete project-level &lt;code&gt;CLAUDE.md&lt;/code&gt; for TypeScript/React projects. New repo workflow:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nb"&gt;mkdir&lt;/span&gt; &lt;span class="nt"&gt;-p&lt;/span&gt; .claude
&lt;span class="nb"&gt;cp&lt;/span&gt; ~/.claude/templates/ts-react-project.md .claude/CLAUDE.md
&lt;span class="c"&gt;# Fill in: [Project Name], Architecture section&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Instantly Claude knows: TypeScript project, pnpm, Zod for validation, TanStack Query for server state, Zustand for client state. All project-level rules in place before you write a single line.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;"A chef's mise en place — everything in its place before cooking starts. Setup done right means execution can be clean."&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;The warning: &lt;em&gt;"Verify library versions are still current at each project init — they drift."&lt;/em&gt; A template from six months ago might reference a library that's had a major version bump. Always check.&lt;/p&gt;




&lt;h2&gt;
  
  
  The Full Context Hierarchy
&lt;/h2&gt;

&lt;p&gt;When Claude starts a session, it reads context in layers:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight markdown"&gt;&lt;code&gt;&lt;span class="p"&gt;1.&lt;/span&gt; Global CLAUDE.md       ← who you are, universal rules
&lt;span class="p"&gt;2.&lt;/span&gt; Project CLAUDE.md      ← this stack, this project's rules
&lt;span class="p"&gt;3.&lt;/span&gt; Memory files (index)   ← lessons, preferences, project context
&lt;span class="p"&gt;4.&lt;/span&gt; The code itself        ← what's actually been built
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;blockquote&gt;
&lt;p&gt;"General law → company policy → department rules → today's meeting agenda. Each layer is more specific than the last."&lt;/p&gt;
&lt;/blockquote&gt;




&lt;h2&gt;
  
  
  Writing Your First Project File
&lt;/h2&gt;

&lt;p&gt;You don't write project files upfront. Write them when you notice you're re-explaining context.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Trigger:&lt;/strong&gt; You find yourself starting a session with "so just to re-explain the context..."&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Action:&lt;/strong&gt; Stop. Write a project file:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight markdown"&gt;&lt;code&gt;&lt;span class="nn"&gt;---&lt;/span&gt;
&lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;project_[feature_name]&lt;/span&gt;
&lt;span class="na"&gt;type&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;project&lt;/span&gt;
&lt;span class="nn"&gt;---&lt;/span&gt;

[What's being built, current status]

&lt;span class="gs"&gt;**Why:**&lt;/span&gt; [The motivation — constraint, deadline, stakeholder ask]

&lt;span class="gs"&gt;**How to apply:**&lt;/span&gt; [What Claude should do differently because of this]
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Add it to MEMORY.md. Next session, Claude already knows.&lt;/p&gt;




&lt;h2&gt;
  
  
  Key Takeaway
&lt;/h2&gt;

&lt;p&gt;Project files carry the &lt;strong&gt;backstory&lt;/strong&gt; — decisions, motivations, and constraints not in the code but shaping every line of it.&lt;/p&gt;

&lt;p&gt;Reference files carry the &lt;strong&gt;map&lt;/strong&gt; — where to find things outside the project.&lt;/p&gt;

&lt;p&gt;Together they turn a new session from a cold start into a warm handoff.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;"Context isn't just helpful. Without it, the right answer and the wrong answer can look identical."&lt;/p&gt;
&lt;/blockquote&gt;




&lt;p&gt;&lt;em&gt;Next: &lt;a href="https://dev.to/panditabhis/a-day-in-the-life-complete-claude-code-session-walkthrough-7o9"&gt;Part 7 — A Day in the Life: Complete Claude Code Session Walkthrough&lt;/a&gt;&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;&lt;em&gt;&lt;a href="https://github.com/panditAbhis/claude-workflow" rel="noopener noreferrer"&gt;All workflow files on GitHub&lt;/a&gt;&lt;/em&gt;&lt;/p&gt;

</description>
      <category>claudecode</category>
      <category>ai</category>
      <category>productivity</category>
      <category>beginners</category>
    </item>
    <item>
      <title>Your Coding DNA: The Three Files That Shape Every Line Claude Writes</title>
      <dc:creator>Abhishek Pandit</dc:creator>
      <pubDate>Wed, 10 Jun 2026 15:03:52 +0000</pubDate>
      <link>https://dev.to/panditabhis/your-coding-dna-the-three-files-that-shape-every-line-claude-writes-378g</link>
      <guid>https://dev.to/panditabhis/your-coding-dna-the-three-files-that-shape-every-line-claude-writes-378g</guid>
      <description>&lt;p&gt;&lt;em&gt;Part 5 of 7 · Series: &lt;a href="https://dev.to/panditabhis/how-i-turned-claude-into-a-disciplined-senior-developer-not-just-a-fast-one-1a59"&gt;Building Your AI Developer Handbook&lt;/a&gt; · &lt;a href="https://github.com/panditAbhis/claude-workflow" rel="noopener noreferrer"&gt;GitHub&lt;/a&gt;&lt;/em&gt;&lt;/p&gt;




&lt;h2&gt;
  
  
  What User Preference Files Are
&lt;/h2&gt;

&lt;p&gt;Feedback files record mistakes. User preference files record &lt;em&gt;who you are as a developer&lt;/em&gt;.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;"Two carpenters given the same wood will build different tables — same tools, same materials, different hands, different results. User preference files are what make Claude build YOUR table, not the generic one."&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Three preference files cover the three decisions you make on every single feature:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;How you organize code&lt;/strong&gt; — architecture&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;How you manage data&lt;/strong&gt; — state management&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;How you verify correctness&lt;/strong&gt; — testing&lt;/li&gt;
&lt;/ol&gt;




&lt;h2&gt;
  
  
  Preference 1: Architecture — Feature-Based Folders
&lt;/h2&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;/features/auth/
  AuthForm.tsx
  AuthForm.test.tsx
  useAuth.ts
  auth.types.ts
/components/ui/
  Button.tsx
  Input.tsx
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Two competing philosophies:&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;&lt;em&gt;Layer-based&lt;/em&gt; (what most tutorials teach):&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;/components/
/hooks/
/types/
/tests/
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;em&gt;Feature-based&lt;/em&gt; (what this workflow uses):&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;/features/auth/
/features/payment/
/features/dashboard/
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;blockquote&gt;
&lt;p&gt;"In a layer-based structure, adding a 'login' feature means touching four separate folders. In a feature-based structure, you touch one."&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Layer-based groups files by &lt;em&gt;what they are&lt;/em&gt;. Feature-based groups by &lt;em&gt;what they do&lt;/em&gt;.&lt;/p&gt;

&lt;p&gt;The difference becomes obvious when you need to &lt;strong&gt;delete a feature&lt;/strong&gt;:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Layer-based: hunt through &lt;code&gt;/components&lt;/code&gt;, &lt;code&gt;/hooks&lt;/code&gt;, &lt;code&gt;/types&lt;/code&gt;, &lt;code&gt;/tests&lt;/code&gt; — find and delete each file individually, hope you didn't miss anything&lt;/li&gt;
&lt;li&gt;Feature-based: delete the &lt;code&gt;/features/auth/&lt;/code&gt; folder. Done.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;The shared primitives rule:&lt;/strong&gt;&lt;br&gt;
Buttons, inputs, modals — building blocks shared across all features — live in &lt;code&gt;/components/ui/&lt;/code&gt; only. Never copy a Button into a feature folder.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;"The kitchen is shared. Your desk is yours."&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;&lt;strong&gt;No barrel exports on large modules:&lt;/strong&gt;&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;// Avoid on large modules — slows TS server, hurts tree-shaking&lt;/span&gt;
&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;AuthForm&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="s1"&gt;./AuthForm&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;
&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;useAuth&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="s1"&gt;./useAuth&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Import directly from the file. The extra path is worth it.&lt;/p&gt;




&lt;h2&gt;
  
  
  Preference 2: State Management — The Ladder
&lt;/h2&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight markdown"&gt;&lt;code&gt;&lt;span class="p"&gt;1.&lt;/span&gt; useState    — local UI state, one component
&lt;span class="p"&gt;2.&lt;/span&gt; Zustand     — shared client state across components  
&lt;span class="p"&gt;3.&lt;/span&gt; TanStack Query — anything from a server
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;blockquote&gt;
&lt;p&gt;"State management is like choosing a vehicle. A bicycle for the corner shop, a car for the city, a truck for cross-country. The mistake is driving a truck to the corner shop."&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h3&gt;
  
  
  Step 1: useState — Start Here, Always
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;isOpen&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;setIsOpen&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;useState&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;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;If state is local to one component and doesn't need to be shared — &lt;code&gt;useState&lt;/code&gt; is perfect. Simple, readable, zero overhead.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;"Don't add complexity until complexity is required."&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h3&gt;
  
  
  Step 2: Zustand — Shared Client State Only
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="c1"&gt;// Good: client-only shared state&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;useUIStore&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;create&lt;/span&gt;&lt;span class="p"&gt;((&lt;/span&gt;&lt;span class="kd"&gt;set&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="na"&gt;sidebarOpen&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;toggleSidebar&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="nf"&gt;set&lt;/span&gt;&lt;span class="p"&gt;((&lt;/span&gt;&lt;span class="nx"&gt;s&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="na"&gt;sidebarOpen&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="o"&gt;!&lt;/span&gt;&lt;span class="nx"&gt;s&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;sidebarOpen&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;When state needs sharing across multiple components and it's &lt;strong&gt;client-only&lt;/strong&gt; — not from a server — Zustand is right. Modal state, sidebar flags, user preferences.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;The hard rule: never put server data in Zustand.&lt;/strong&gt;&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 — server data in Zustand&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;useUserStore&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;create&lt;/span&gt;&lt;span class="p"&gt;((&lt;/span&gt;&lt;span class="kd"&gt;set&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="na"&gt;user&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;null&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="na"&gt;fetchUser&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="k"&gt;async &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="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;user&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;api&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;getUser&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
    &lt;span class="nf"&gt;set&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt; &lt;span class="nx"&gt;user&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;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This creates a second copy of the data. They get out of sync. You add refresh logic. Then invalidation logic. Then loading states. You've just rebuilt TanStack Query, badly.&lt;/p&gt;

&lt;h3&gt;
  
  
  Step 3: TanStack Query — Anything From a Server
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="c1"&gt;// RIGHT — server data belongs here&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;data&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;user&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;useQuery&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
  &lt;span class="na"&gt;queryKey&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;user&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;
  &lt;span class="na"&gt;queryFn&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;api&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;getUser&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;Loading states, error states, caching, background refetching, cache invalidation — all free.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;"Zustand is a drawer. TanStack Query is a real-time window to the outside world. Don't put windows in drawers."&lt;/p&gt;
&lt;/blockquote&gt;




&lt;h2&gt;
  
  
  Preference 3: Testing — The Pyramid
&lt;/h2&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Unit tests:        pure logic, no side effects, no internal mocks
Integration tests: real database, real data flows
E2E tests:         critical paths only (login, checkout, core flow)
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;blockquote&gt;
&lt;p&gt;"Testing is like quality control on a car assembly line. You don't test the whole car every time you tighten one bolt. You test the bolt, then the subsystem, then the full car before it ships."&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h3&gt;
  
  
  Unit Tests — Pure Logic Only
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="c1"&gt;// Perfect unit test — pure function, no external dependencies&lt;/span&gt;
&lt;span class="nf"&gt;test&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;formats currency correctly&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="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nf"&gt;expect&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nf"&gt;formatCurrency&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;1000&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;USD&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)).&lt;/span&gt;&lt;span class="nf"&gt;toBe&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;$1,000.00&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;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Belongs here: validators, formatters, reducers, pure utility functions.&lt;br&gt;
Doesn't belong here: anything touching a database, API, filesystem, or network.&lt;/p&gt;

&lt;h3&gt;
  
  
  Integration Tests — Real Database, Always
&lt;/h3&gt;

&lt;blockquote&gt;
&lt;p&gt;"A flight simulator is great for training. But the first time you land a real plane, you discover the simulator lied about the crosswind."&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Mocked database tests pass even when the real migration fails. Integration tests must use a real test database. Yes, they're slower. That's the point — they're testing real behavior.&lt;/p&gt;

&lt;h3&gt;
  
  
  E2E Tests — Critical Paths Only
&lt;/h3&gt;

&lt;p&gt;E2E opens a real browser and clicks through the UI. Most accurate, slowest, most brittle.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;"You don't test every road in the country to verify the highway exists. You just drive the highway."&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;E2E for: login, checkout, the one flow that makes you money. Not for edge cases — those belong in unit and integration tests.&lt;/p&gt;




&lt;h2&gt;
  
  
  How These Three Work Together
&lt;/h2&gt;

&lt;p&gt;When Claude builds a new feature with these files loaded, it automatically:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Creates &lt;code&gt;/features/feature-name/&lt;/code&gt; — not scattered layer folders&lt;/li&gt;
&lt;li&gt;Starts with &lt;code&gt;useState&lt;/code&gt;, reaches for Zustand only if state needs sharing&lt;/li&gt;
&lt;li&gt;Writes unit tests for logic, integration tests for data layer&lt;/li&gt;
&lt;li&gt;Never adds barrel exports or server data to Zustand&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;You don't say any of this. It's already loaded.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;"The best rule is one you never have to repeat."&lt;/p&gt;
&lt;/blockquote&gt;




&lt;h2&gt;
  
  
  Key Takeaway
&lt;/h2&gt;

&lt;p&gt;User preference files transform Claude from a generic code generator into a collaborator that builds code the way &lt;em&gt;you&lt;/em&gt; would build it.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Architecture: &lt;strong&gt;feature-based, co-located, no barrel exports&lt;/strong&gt;
&lt;/li&gt;
&lt;li&gt;State: &lt;strong&gt;useState → Zustand → TanStack Query, never server state in Zustand&lt;/strong&gt;
&lt;/li&gt;
&lt;li&gt;Testing: &lt;strong&gt;unit for logic, real DB for integration, E2E for critical paths only&lt;/strong&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Three files. Every feature they shape.&lt;/p&gt;




&lt;p&gt;&lt;em&gt;Next: &lt;a href="https://dev.to/panditabhis/context-is-king-how-project-files-and-templates-keep-claude-on-track-433l"&gt;Part 6 — Context is King: How Project Files and Templates Keep Claude on Track&lt;/a&gt;&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;&lt;em&gt;&lt;a href="https://github.com/panditAbhis/claude-workflow" rel="noopener noreferrer"&gt;All workflow files on GitHub&lt;/a&gt;&lt;/em&gt;&lt;/p&gt;

</description>
      <category>claudecode</category>
      <category>ai</category>
      <category>webdev</category>
      <category>react</category>
    </item>
    <item>
      <title>Battle Scars as Rules: Inside the Feedback Files</title>
      <dc:creator>Abhishek Pandit</dc:creator>
      <pubDate>Wed, 10 Jun 2026 14:52:10 +0000</pubDate>
      <link>https://dev.to/panditabhis/battle-scars-as-rules-inside-the-feedback-files-5ejj</link>
      <guid>https://dev.to/panditabhis/battle-scars-as-rules-inside-the-feedback-files-5ejj</guid>
      <description>&lt;p&gt;&lt;em&gt;Part 4 of 7 · Series: &lt;a href="https://dev.to/panditabhis/how-i-turned-claude-into-a-disciplined-senior-developer-not-just-a-fast-one-1a59"&gt;Building Your AI Developer Handbook&lt;/a&gt; · &lt;a href="https://github.com/panditAbhis/claude-workflow" rel="noopener noreferrer"&gt;GitHub&lt;/a&gt;&lt;/em&gt;&lt;/p&gt;




&lt;h2&gt;
  
  
  What Are Feedback Files?
&lt;/h2&gt;

&lt;p&gt;Every rule in a feedback file was born from either a mistake or a win.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;"The safety rules on a construction site weren't written by lawyers. They were written in blood — each rule marks the spot where something went wrong."&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;A feedback file looks like this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight markdown"&gt;&lt;code&gt;&lt;span class="gs"&gt;**Rule:**&lt;/span&gt; [what to do or not do]
&lt;span class="gs"&gt;**Why:**&lt;/span&gt; [the incident or reason behind it]
&lt;span class="gs"&gt;**Apply:**&lt;/span&gt; [when and where this kicks in]
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The &lt;code&gt;Why&lt;/code&gt; is the most important part. Without it, you follow rules blindly. With it, you can judge edge cases — "does this situation actually trigger this rule, or is it different enough?"&lt;/p&gt;

&lt;p&gt;Here are 8 real feedback rules, what they say, and the story behind each one.&lt;/p&gt;




&lt;h2&gt;
  
  
  Rule 1: Never Mock the Database in Tests
&lt;/h2&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Rule: Integration tests must hit a real database. No mocks.
Why: Mocked tests passed. Prod migration broke.
Apply: Never mock the DB layer in integration tests.
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;blockquote&gt;
&lt;p&gt;"A fire drill with a fake fire teaches you nothing about real smoke."&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Tests were passing. The mock was set up correctly. The code looked clean. But the mock didn't simulate a specific edge case in the actual database — a constraint that only exists in production. The migration ran, the constraint triggered, and production broke while all tests were green.&lt;/p&gt;

&lt;p&gt;The lesson: mocks are lying witnesses. They tell you what you told them to say, not what the real system does.&lt;/p&gt;

&lt;p&gt;For unit tests (pure functions, no side effects) — mocks are fine. For integration tests that touch data — always use a real test database.&lt;/p&gt;




&lt;h2&gt;
  
  
  Rule 2: No useCallback/useMemo by Default
&lt;/h2&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Rule: Don't add useCallback/useMemo unless:
  1. The child is wrapped in React.memo
  2. React Profiler shows a real re-render problem
Why: Premature optimization clutters code and rarely helps.
Apply: Strip default memoization in code review.
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;blockquote&gt;
&lt;p&gt;"Putting armor on a bicycle because 'it might get hit by a car' doesn't make it safer — it just makes it heavier and harder to ride."&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Developers add &lt;code&gt;useCallback&lt;/code&gt;/&lt;code&gt;useMemo&lt;/code&gt; "just in case" — wrapping every handler by default. Result: harder-to-read code with reference tracking overhead, solving performance problems that don't exist.&lt;/p&gt;

&lt;p&gt;React's default re-render behavior is fast. Most re-renders take under 1ms. The profiler will tell you when something is actually slow. Optimize then — not before.&lt;/p&gt;




&lt;h2&gt;
  
  
  Rule 3: Never Write API Tokens Into Config Files
&lt;/h2&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Rule: Never write tokens, passwords, or secrets into settings.json or any config file.
Why: Config files are plaintext. Plaintext gets committed. Committed secrets get stolen.
Apply: Use .zshrc, secrets managers, or wrapper scripts instead.
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;blockquote&gt;
&lt;p&gt;"Writing your password on a sticky note is convenient until someone walks past your desk."&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;&lt;code&gt;settings.json&lt;/code&gt; files are convenient — set them once, forget. But they're plaintext files that often end up in git. Bots scan GitHub constantly for exposed credentials. An exposed key can be exploited within minutes.&lt;/p&gt;

&lt;p&gt;The safe pattern:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Config files → key names only, no values&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;.env&lt;/code&gt; → values, never committed, in &lt;code&gt;.gitignore&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;Shell profile (&lt;code&gt;.zshrc&lt;/code&gt;) → exported env vars that tools pick up automatically&lt;/li&gt;
&lt;/ul&gt;




&lt;h2&gt;
  
  
  Rule 4: CLAUDE.md — Global vs Project
&lt;/h2&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Rule: Global CLAUDE.md = universal rules only. Stack-specific rules go in project CLAUDE.md.
Why: Hardcoding React/TypeScript rules globally broke Python project sessions.
Apply: Reject stack-specific content in global CLAUDE.md.
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;blockquote&gt;
&lt;p&gt;"The company handbook says 'be on time.' It doesn't say 'use a blue pen' — that's a department rule."&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;One CLAUDE.md tried to cover everything: TypeScript rules, React patterns, pnpm commands, Zod schemas. Then a Python project opened. Suddenly Claude was suggesting &lt;code&gt;pnpm&lt;/code&gt; commands for a pip project.&lt;/p&gt;

&lt;p&gt;The fix: two levels. Global = who you are. Project = what this stack is.&lt;/p&gt;




&lt;h2&gt;
  
  
  Rule 5: Dependency Protocol — Check Before You Add
&lt;/h2&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Rule: Before any new dependency:
  1. bundlephobia → check bundle size impact
  2. pnpm audit → check known vulnerabilities
  3. Repo activity → last commit within 1 year?
Why: Dependencies are long-term liabilities.
Apply: Include audit command whenever suggesting pnpm add.
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;blockquote&gt;
&lt;p&gt;"Adopting a pet is easy. Feeding it for 10 years is the commitment you're actually making."&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Every dependency you add is a dependency you maintain — security patches, breaking changes, version conflicts. A package that solves a problem today but gets abandoned tomorrow is a liability.&lt;/p&gt;

&lt;p&gt;Three checks, 2 minutes, every time. The cost of skipping: potentially hours debugging a supply chain issue.&lt;/p&gt;




&lt;h2&gt;
  
  
  Rule 6: Error Handling — Never Swallow Silently
&lt;/h2&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Rule: Every catch block must at minimum console.error(err).
      User-visible errors go to toast or error boundary.
Why: Silent failures look like missing features — hardest bugs to diagnose.
Apply: Never write catch (e) { /* ignore */ }
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;blockquote&gt;
&lt;p&gt;"A smoke alarm with dead batteries doesn't mean there's no fire. It means you won't know about it until it's too late."&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;&lt;code&gt;catch (e) {}&lt;/code&gt; — empty catch blocks are the most dangerous pattern in production code. An error occurred. Something failed. And you told the computer to pretend nothing happened.&lt;/p&gt;

&lt;p&gt;The user sees a broken UI with no error message. You see no logs. The error is invisible. You spend three hours debugging something that would have taken three seconds with a &lt;code&gt;console.error&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;Minimum: log it. Better: show a toast. Best: let it bubble to an error boundary.&lt;/p&gt;




&lt;h2&gt;
  
  
  Rule 7: Skip Removed Tools Silently
&lt;/h2&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Rule: If a tool has been deliberately removed, never suggest or reference it.
Why: The removal was intentional. Asking about it is friction.
Apply: Skip silently, continue with available tools.
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;blockquote&gt;
&lt;p&gt;"If a chef removes an ingredient from their kitchen, they don't want the sous-chef asking 'but what about the cilantro?' every time they cook."&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Sometimes you remove an integration — for cost, privacy, or preference. The decision was deliberate. You don't want Claude asking about it or suggesting you re-add it every session.&lt;/p&gt;




&lt;h2&gt;
  
  
  Rule 8: Separate Work and Personal Git Identities
&lt;/h2&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Rule: Always use the correct git identity (work vs personal) for the project context.
      Never add AI co-author lines to commits.
Why: Work and personal projects need separate attribution.
Apply: Confirm user.email matches context before every commit.
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;blockquote&gt;
&lt;p&gt;"You wouldn't sign a personal letter with your work signature."&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Two contexts, two identities. The wrong email in a commit doesn't just look sloppy — it can create audit trail problems. The co-author rule: commit history should reflect human authorship decisions.&lt;/p&gt;




&lt;h2&gt;
  
  
  Building Your Own Feedback Files
&lt;/h2&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight markdown"&gt;&lt;code&gt;&lt;span class="nn"&gt;---&lt;/span&gt;
&lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;feedback_rule_name&lt;/span&gt;
&lt;span class="na"&gt;description&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;one-line summary&lt;/span&gt;
&lt;span class="na"&gt;metadata&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;type&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;feedback&lt;/span&gt;
&lt;span class="nn"&gt;---&lt;/span&gt;

&lt;span class="gs"&gt;**Rule:**&lt;/span&gt; [what Claude should do or not do]

&lt;span class="gs"&gt;**Why:**&lt;/span&gt; [the story — what happened or what you observed]

&lt;span class="gs"&gt;**How to apply:**&lt;/span&gt; [when does this rule trigger]
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The trigger for writing a new one: if you've corrected Claude twice for the same thing, it belongs in a feedback file.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;"The first mistake is an accident. The second is a pattern. The third is a choice."&lt;/p&gt;
&lt;/blockquote&gt;




&lt;h2&gt;
  
  
  Key Takeaway
&lt;/h2&gt;

&lt;p&gt;Feedback files are the &lt;strong&gt;institutional memory&lt;/strong&gt; of your AI workflow. They capture hard-won knowledge that would otherwise reset every session. Each rule has a story. Each story prevents a future mistake.&lt;/p&gt;




&lt;p&gt;&lt;em&gt;Next: &lt;a href="https://dev.to/panditabhis/your-coding-dna-the-three-files-that-shape-every-line-claude-writes-378g"&gt;Part 5 — Your Coding DNA: The Three Files That Shape Every Line Claude Writes&lt;/a&gt;&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;&lt;em&gt;&lt;a href="https://github.com/panditAbhis/claude-workflow" rel="noopener noreferrer"&gt;All workflow files on GitHub&lt;/a&gt;&lt;/em&gt;&lt;/p&gt;

</description>
      <category>claudecode</category>
      <category>ai</category>
      <category>productivity</category>
      <category>webdev</category>
    </item>
    <item>
      <title>Teaching an AI to Never Forget: How the Memory System Works</title>
      <dc:creator>Abhishek Pandit</dc:creator>
      <pubDate>Wed, 10 Jun 2026 14:45:23 +0000</pubDate>
      <link>https://dev.to/panditabhis/teaching-an-ai-to-never-forget-how-the-memory-system-works-40o3</link>
      <guid>https://dev.to/panditabhis/teaching-an-ai-to-never-forget-how-the-memory-system-works-40o3</guid>
      <description>&lt;p&gt;&lt;em&gt;Part 3 of 7 · Series: &lt;a href="https://dev.to/panditabhis/how-i-turned-claude-into-a-disciplined-senior-developer-not-just-a-fast-one-1a59"&gt;Building Your AI Developer Handbook&lt;/a&gt; · &lt;a href="https://github.com/panditAbhis/claude-workflow" rel="noopener noreferrer"&gt;GitHub&lt;/a&gt;&lt;/em&gt;&lt;/p&gt;




&lt;h2&gt;
  
  
  The Goldfish Problem
&lt;/h2&gt;

&lt;p&gt;By default, every Claude session starts completely fresh. No memory of last week's conversation. No memory of the rule you explained three times. No memory of the mistake you made together and fixed together.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;"Imagine if your doctor forgot everything about you every time you walked into the clinic. You'd spend 10 minutes re-explaining your history before they could help you."&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;That's Claude without memory files.&lt;/p&gt;

&lt;p&gt;The memory system fixes this. It's a folder of plain markdown files that Claude reads at the start of every session — carrying forward the lessons, preferences, and decisions that would otherwise reset.&lt;/p&gt;




&lt;h2&gt;
  
  
  Where Memory Lives
&lt;/h2&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;~/.claude/projects/your-project/memory/
  MEMORY.md              ← the index (loads automatically every session)
  feedback_rules.md      ← lessons from mistakes
  user_preferences.md    ← how you like to work
  project_context.md     ← what's happening in the project right now
  reference_links.md     ← where to find things outside the project
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Think of it like a filing cabinet:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;MEMORY.md&lt;/strong&gt; is the table of contents — one line per memory, always loaded&lt;/li&gt;
&lt;li&gt;Each individual file is a folder in the cabinet — loaded when relevant&lt;/li&gt;
&lt;/ul&gt;




&lt;h2&gt;
  
  
  The Four Types of Memory
&lt;/h2&gt;

&lt;h3&gt;
  
  
  1. Feedback Memory — "Rules Born From Mistakes"
&lt;/h3&gt;

&lt;blockquote&gt;
&lt;p&gt;"Every rule in a safety manual was written in response to an accident." — Aviation saying&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;These files store corrections and confirmations. Every time Claude does something you didn't want — or something you want it to repeat — that lesson becomes a feedback file.&lt;/p&gt;

&lt;p&gt;Example: You discover mocked database tests passed while a real production migration failed. You tell Claude: "never mock the database in tests." That becomes a rule that applies to every future session.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight markdown"&gt;&lt;code&gt;&lt;span class="gs"&gt;**Rule:**&lt;/span&gt; Integration tests must hit a real database.
&lt;span class="gs"&gt;**Why:**&lt;/span&gt; Mocked tests passed; prod migration broke.
&lt;span class="gs"&gt;**Apply:**&lt;/span&gt; Never suggest mocking the DB layer in tests.
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  2. User Memory — "How You Think and Work"
&lt;/h3&gt;

&lt;blockquote&gt;
&lt;p&gt;"A good assistant doesn't just do tasks — they understand how their manager thinks."&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;These files capture your preferences, style, and philosophy — folder structure, state management approach, testing philosophy. Claude reads these and adjusts its suggestions to match &lt;em&gt;your&lt;/em&gt; way of working.&lt;/p&gt;

&lt;h3&gt;
  
  
  3. Project Memory — "What's Happening Right Now"
&lt;/h3&gt;

&lt;blockquote&gt;
&lt;p&gt;"Context switches are expensive. The more context you can offload to a file, the less you re-explain every session."&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;These files track ongoing work — what feature is being built, why a particular decision was made, what the deadline is. Without these, you start every session re-explaining the backstory.&lt;/p&gt;

&lt;h3&gt;
  
  
  4. Reference Memory — "Where to Look Things Up"
&lt;/h3&gt;

&lt;p&gt;Simple pointers to external resources:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;"Bug tracker is at Linear project INGEST"&lt;/li&gt;
&lt;li&gt;"Design tokens are in Figma file XYZ"&lt;/li&gt;
&lt;li&gt;"Oncall dashboard is at grafana.internal/api-latency"&lt;/li&gt;
&lt;/ul&gt;

&lt;blockquote&gt;
&lt;p&gt;"A good assistant knows where the filing cabinet is. They don't memorize every document — they know where to look."&lt;/p&gt;
&lt;/blockquote&gt;




&lt;h2&gt;
  
  
  The MEMORY.md Index
&lt;/h2&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight markdown"&gt;&lt;code&gt;&lt;span class="gh"&gt;# Memory Index&lt;/span&gt;
&lt;span class="p"&gt;
-&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nv"&gt;No DB mocks&lt;/span&gt;&lt;span class="p"&gt;](&lt;/span&gt;&lt;span class="sx"&gt;feedback_db_testing.md&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; — integration tests use real DB; mocks missed prod migration bug
&lt;span class="p"&gt;-&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nv"&gt;State ladder&lt;/span&gt;&lt;span class="p"&gt;](&lt;/span&gt;&lt;span class="sx"&gt;user_state_management.md&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; — useState→Zustand→TanStack Query; no server state in Zustand
&lt;span class="p"&gt;-&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nv"&gt;Current feature&lt;/span&gt;&lt;span class="p"&gt;](&lt;/span&gt;&lt;span class="sx"&gt;project_auth_sprint.md&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; — building OAuth login, deadline 2026-06-20
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This is the only file that loads automatically on every session. Intentionally short — one line per memory.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;"A good index tells you whether you need to open the drawer. You shouldn't need to read the whole drawer just to find out."&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Each line has three parts:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;The name (links to the full file)&lt;/li&gt;
&lt;li&gt;A one-line hook (enough to know if it's relevant right now)&lt;/li&gt;
&lt;/ol&gt;




&lt;h2&gt;
  
  
  How It Works in Practice
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;Session 1 (no memory):&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;You: "How should I structure the auth feature?"
Claude: [gives generic advice based on training data]
You: "I use feature-based folders, not layer-based"
Claude: [adjusts, gives feature-based advice]
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Same question, Session 2 (with memory):&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;[Claude reads: "feature-based folders — /features/auth/, /features/payment/"]
You: "How should I structure the auth feature?"
Claude: [immediately gives feature-based advice — no re-explaining needed]
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;






&lt;h2&gt;
  
  
  What NOT to Put in Memory
&lt;/h2&gt;

&lt;p&gt;Memory files are for things that aren't in the code itself.&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Already exists&lt;/th&gt;
&lt;th&gt;Don't memorize&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Code patterns&lt;/td&gt;
&lt;td&gt;They're in the code&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Git history&lt;/td&gt;
&lt;td&gt;
&lt;code&gt;git log&lt;/code&gt; knows&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;File structure&lt;/td&gt;
&lt;td&gt;Read the filesystem&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Debugging solutions&lt;/td&gt;
&lt;td&gt;Fix is in the commit&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;blockquote&gt;
&lt;p&gt;"A post-it note on your monitor says 'check Figma before coding UI.' It doesn't copy the entire Figma file onto the post-it."&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Memory files decay. The older they are, the more likely they describe something that's changed. The &lt;code&gt;Why&lt;/code&gt; field is what tells you whether a memory is still load-bearing.&lt;/p&gt;




&lt;h2&gt;
  
  
  Starting Your Own Memory System
&lt;/h2&gt;

&lt;p&gt;Start with one file: &lt;code&gt;feedback.md&lt;/code&gt;. Every time you correct Claude, add a line:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight markdown"&gt;&lt;code&gt;&lt;span class="p"&gt;-&lt;/span&gt; Don't use barrel exports in large modules — slows TS server
&lt;span class="p"&gt;-&lt;/span&gt; Always check bundlephobia before adding a dependency  
&lt;span class="p"&gt;-&lt;/span&gt; State management order: useState first, Zustand only if shared
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Over a few weeks you'll have a precise record of how you like to work. Split into separate files when it gets large. Build the MEMORY.md index.&lt;/p&gt;




&lt;h2&gt;
  
  
  Key Takeaway
&lt;/h2&gt;

&lt;p&gt;Memory files turn Claude from a &lt;strong&gt;stateless tool&lt;/strong&gt; into a &lt;strong&gt;contextual collaborator&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;The difference between "Claude that needs re-teaching every session" and "Claude that gets better the more you use it" is a folder of markdown files.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;"Experience is just lessons that were written down. Wisdom is lessons that were read again."&lt;/p&gt;
&lt;/blockquote&gt;




&lt;p&gt;&lt;em&gt;Next: &lt;a href="https://dev.to/panditabhis/battle-scars-as-rules-inside-the-feedback-files-5ejj"&gt;Part 4 — Battle Scars as Rules: Inside the Feedback Files&lt;/a&gt;&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;&lt;em&gt;&lt;a href="https://github.com/panditAbhis/claude-workflow" rel="noopener noreferrer"&gt;All workflow files on GitHub&lt;/a&gt;&lt;/em&gt;&lt;/p&gt;

</description>
      <category>claudecode</category>
      <category>ai</category>
      <category>productivity</category>
      <category>beginners</category>
    </item>
  </channel>
</rss>
