<?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: Nishikanta Ray</title>
    <description>The latest articles on DEV Community by Nishikanta Ray (@nishikantaray).</description>
    <link>https://dev.to/nishikantaray</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%2F478296%2F4472af16-2478-4273-a8cd-42c39effe3c0.jpg</url>
      <title>DEV Community: Nishikanta Ray</title>
      <link>https://dev.to/nishikantaray</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/nishikantaray"/>
    <language>en</language>
    <item>
      <title>S3 Glacier Restore Is Not a Storage-Class Migration</title>
      <dc:creator>Nishikanta Ray</dc:creator>
      <pubDate>Fri, 30 Oct 2026 19:42:58 +0000</pubDate>
      <link>https://dev.to/nishikantaray/s3-glacier-restore-is-not-a-storage-class-migration-3ffo</link>
      <guid>https://dev.to/nishikantaray/s3-glacier-restore-is-not-a-storage-class-migration-3ffo</guid>
      <description>&lt;p&gt;Amazon S3 offers different storage classes to help reduce storage costs. One of them is S3 Glacier, designed for storing data that you rarely need to access.&lt;/p&gt;

&lt;p&gt;But there's an important detail that can cause confusion when working with archived objects:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Restoring an S3 Glacier object does not automatically move it back to S3 Standard.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Let's understand why this happens, the different Glacier storage classes, and how to bring an archived object back to regular storage.&lt;/p&gt;

&lt;h2&gt;
  
  
  1. What is Amazon S3 Glacier?
&lt;/h2&gt;

&lt;p&gt;Amazon S3 Glacier is a set of storage classes designed for long-term data archiving.&lt;/p&gt;

&lt;p&gt;Instead of paying higher storage costs for files that are rarely accessed, you can move them into Glacier storage classes.&lt;/p&gt;

&lt;p&gt;For example:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Old user-uploaded images.&lt;/li&gt;
&lt;li&gt;Historical reports and documents.&lt;/li&gt;
&lt;li&gt;Database backups.&lt;/li&gt;
&lt;li&gt;Application logs.&lt;/li&gt;
&lt;li&gt;Compliance and audit records.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The trade-off is that some Glacier classes have slower retrieval times and additional retrieval costs.&lt;/p&gt;

&lt;h2&gt;
  
  
  2. Understanding the three Glacier storage classes
&lt;/h2&gt;

&lt;p&gt;Not every Glacier storage class works the same way.&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Storage class&lt;/th&gt;
&lt;th&gt;Retrieval time&lt;/th&gt;
&lt;th&gt;Best suited for&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Glacier Instant Retrieval (&lt;code&gt;GLACIER_IR&lt;/code&gt;)&lt;/td&gt;
&lt;td&gt;Milliseconds&lt;/td&gt;
&lt;td&gt;Rarely accessed files that still need immediate access&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Glacier Flexible Retrieval (&lt;code&gt;GLACIER&lt;/code&gt;)&lt;/td&gt;
&lt;td&gt;Minutes to hours&lt;/td&gt;
&lt;td&gt;Backups and disaster recovery&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Glacier Deep Archive (&lt;code&gt;DEEP_ARCHIVE&lt;/code&gt;)&lt;/td&gt;
&lt;td&gt;Hours&lt;/td&gt;
&lt;td&gt;Long-term archives and compliance data&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;A few important differences:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Glacier Instant Retrieval&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Objects are immediately accessible through normal S3 GET requests.&lt;/li&gt;
&lt;li&gt;No restore request is required.&lt;/li&gt;
&lt;li&gt;Designed for data accessed occasionally but requiring immediate availability.&lt;/li&gt;
&lt;li&gt;Minimum storage duration: 90 days.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Glacier Flexible Retrieval&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Objects are archived and cannot be accessed directly.&lt;/li&gt;
&lt;li&gt;You must initiate a restore request before accessing them.&lt;/li&gt;
&lt;li&gt;Supports Expedited, Standard, and Bulk retrieval.&lt;/li&gt;
&lt;li&gt;Minimum storage duration: 90 days.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Glacier Deep Archive&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Designed for data that is rarely accessed.&lt;/li&gt;
&lt;li&gt;Requires a restore request before accessing objects.&lt;/li&gt;
&lt;li&gt;Supports Standard and Bulk retrieval.&lt;/li&gt;
&lt;li&gt;Minimum storage duration: 180 days.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The important distinction is that Instant Retrieval provides immediate access, whereas Flexible Retrieval and Deep Archive require restoration.&lt;/p&gt;

&lt;h2&gt;
  
  
  3. What actually happens when you restore an object?
&lt;/h2&gt;

&lt;p&gt;This is where the confusion starts.&lt;/p&gt;

&lt;p&gt;Imagine an application stores an image in S3. Over time, a lifecycle policy moves that image into Glacier Flexible Retrieval to reduce storage costs.&lt;/p&gt;

&lt;p&gt;Later, the application needs that image again.&lt;/p&gt;

&lt;p&gt;You initiate a restore request.&lt;/p&gt;

&lt;p&gt;AWS processes the request and creates a temporary accessible copy of the archived object. You specify how many days that temporary copy should remain available.&lt;/p&gt;

&lt;p&gt;Once restoration completes, you can access the object.&lt;/p&gt;

&lt;p&gt;However, the original object's storage class remains &lt;code&gt;GLACIER&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;For example, a HEAD request can return:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight json"&gt;&lt;code&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"StorageClass"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"GLACIER"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"Restore"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"ongoing-request=&lt;/span&gt;&lt;span class="se"&gt;\"&lt;/span&gt;&lt;span class="s2"&gt;false&lt;/span&gt;&lt;span class="se"&gt;\"&lt;/span&gt;&lt;span class="s2"&gt;, expiry-date=&lt;/span&gt;&lt;span class="se"&gt;\"&lt;/span&gt;&lt;span class="s2"&gt;...&lt;/span&gt;&lt;span class="se"&gt;\"&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Let's understand these fields:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;StorageClass: GLACIER&lt;/code&gt; means the object is still in Glacier Flexible Retrieval.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;ongoing-request="false"&lt;/code&gt; means restoration has completed.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;expiry-date&lt;/code&gt; indicates when the temporary restored copy expires.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Restoration makes the object accessible. It does not migrate the object to another storage class.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;This distinction is critical when an application expects the object to remain available beyond the restore period.&lt;/p&gt;

&lt;h2&gt;
  
  
  4. How do you permanently move a restored object back to S3 Standard?
&lt;/h2&gt;

&lt;p&gt;If you need the object to remain available without depending on the temporary restore window, you need another operation.&lt;/p&gt;

&lt;p&gt;The solution is to:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Restore the archived object.&lt;/li&gt;
&lt;li&gt;Wait until restoration completes.&lt;/li&gt;
&lt;li&gt;Copy the restored object to the same bucket and key.&lt;/li&gt;
&lt;li&gt;Specify &lt;code&gt;STANDARD&lt;/code&gt; as the destination storage class.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;This creates a new object using S3 Standard storage.&lt;/p&gt;

&lt;h3&gt;
  
  
  Step 1: Check whether restoration is complete
&lt;/h3&gt;

&lt;p&gt;Using AWS CLI:&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="nv"&gt;BUCKET&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;only-for-testing
&lt;span class="nv"&gt;KEY&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;"letsflo-core/dev/_1793103890540/HH0922_166_11.jpg"&lt;/span&gt;

aws s3api head-object &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--bucket&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$BUCKET&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--key&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$KEY&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--query&lt;/span&gt; &lt;span class="s1"&gt;'[StorageClass,Restore]'&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Make sure the restore status is:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;ongoing-request="false"
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Do not attempt the copy before restoration completes.&lt;/p&gt;

&lt;h3&gt;
  
  
  Step 2: Copy the object onto itself
&lt;/h3&gt;

&lt;p&gt;Once restoration is complete:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;aws s3 &lt;span class="nb"&gt;cp&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="s2"&gt;"s3://&lt;/span&gt;&lt;span class="nv"&gt;$BUCKET&lt;/span&gt;&lt;span class="s2"&gt;/&lt;/span&gt;&lt;span class="nv"&gt;$KEY&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="s2"&gt;"s3://&lt;/span&gt;&lt;span class="nv"&gt;$BUCKET&lt;/span&gt;&lt;span class="s2"&gt;/&lt;/span&gt;&lt;span class="nv"&gt;$KEY&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--storage-class&lt;/span&gt; STANDARD &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--force-glacier-transfer&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Why copy the object to itself?&lt;/p&gt;

&lt;p&gt;Because the copy operation creates a new object at the destination, even when the source and destination use the same bucket and key.&lt;/p&gt;

&lt;p&gt;By specifying:&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="nt"&gt;--storage-class&lt;/span&gt; STANDARD
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;we tell S3 to store the resulting object in Standard storage instead of Glacier.&lt;/p&gt;

&lt;h3&gt;
  
  
  Step 3: Verify the storage class
&lt;/h3&gt;

&lt;p&gt;Run the HEAD request again:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;aws s3api head-object &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--bucket&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$BUCKET&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--key&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$KEY&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--query&lt;/span&gt; &lt;span class="s1"&gt;'[StorageClass,Restore]'&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Expected result:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight json"&gt;&lt;code&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="s2"&gt;"STANDARD"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="kc"&gt;null&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;S3 Standard is the default storage class, so AWS may omit the &lt;code&gt;StorageClass&lt;/code&gt; field.&lt;/p&gt;

&lt;p&gt;The temporary restore information should no longer be needed for the new Standard object.&lt;/p&gt;

&lt;h2&gt;
  
  
  5. Performing the same operation using Node.js
&lt;/h2&gt;

&lt;p&gt;In our case, we also needed to perform this operation through the backend's existing AWS configuration.&lt;/p&gt;

&lt;p&gt;Using AWS SDK for JavaScript v3, we can use &lt;code&gt;CopyObjectCommand&lt;/code&gt;.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;cfg&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;require&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;../backend/src/flo/config/dev.js&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nx"&gt;aws&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;S&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;require&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;@aws-sdk/client-s3&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;client&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nx"&gt;S&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nc"&gt;S3Client&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
  &lt;span class="na"&gt;region&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;cfg&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;region&lt;/span&gt; &lt;span class="o"&gt;||&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;eu-west-1&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="na"&gt;credentials&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="na"&gt;accessKeyId&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;cfg&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;accessKeyId&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="na"&gt;secretAccessKey&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;cfg&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;secretAccessKey&lt;/span&gt;
  &lt;span class="p"&gt;},&lt;/span&gt;
  &lt;span class="na"&gt;followRegionRedirects&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="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;Bucket&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;cfg&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;Bucket&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;Key&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;letsflo-core/dev/_1793103890540/HH0922_166_11.jpg&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;head&lt;/span&gt; &lt;span class="o"&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;label&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;result&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;client&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="k"&gt;new&lt;/span&gt; &lt;span class="nx"&gt;S&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nc"&gt;HeadObjectCommand&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt; &lt;span class="nx"&gt;Bucket&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;Key&lt;/span&gt; &lt;span class="p"&gt;})&lt;/span&gt;
  &lt;span class="p"&gt;);&lt;/span&gt;

  &lt;span class="nx"&gt;console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;log&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;label&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="na"&gt;storageClass&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;result&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;StorageClass&lt;/span&gt; &lt;span class="o"&gt;||&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;STANDARD&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="na"&gt;restore&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;result&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;Restore&lt;/span&gt; &lt;span class="o"&gt;||&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;no restore&lt;/span&gt;&lt;span class="dl"&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;result&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;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;before&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;head&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;BEFORE&lt;/span&gt;&lt;span class="dl"&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;before&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;Restore&lt;/span&gt; &lt;span class="o"&gt;||&lt;/span&gt;
    &lt;span class="o"&gt;!&lt;/span&gt;&lt;span class="nx"&gt;before&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;Restore&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;includes&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;ongoing-request="false"&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;throw&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;Error&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Object restoration is not complete.&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="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;client&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="k"&gt;new&lt;/span&gt; &lt;span class="nx"&gt;S&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nc"&gt;CopyObjectCommand&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
      &lt;span class="nx"&gt;Bucket&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="nx"&gt;Key&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="na"&gt;CopySource&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nf"&gt;encodeURIComponent&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;Bucket&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;Key&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="na"&gt;StorageClass&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;STANDARD&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="na"&gt;MetadataDirective&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;COPY&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;after&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;head&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;AFTER&lt;/span&gt;&lt;span class="dl"&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="nx"&gt;after&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;StorageClass&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt;
    &lt;span class="nx"&gt;after&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;StorageClass&lt;/span&gt; &lt;span class="o"&gt;!==&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;STANDARD&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="k"&gt;throw&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;Error&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Storage class verification failed.&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;console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;log&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Object successfully moved to STANDARD.&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="k"&gt;catch&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;error&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nx"&gt;console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;error&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;ERROR:&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;error&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;name&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;error&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;message&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="nx"&gt;process&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;exitCode&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;});&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The implementation performs three important operations:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Checks the existing object's restoration status.&lt;/li&gt;
&lt;li&gt;Copies the restored object using the Standard storage class.&lt;/li&gt;
&lt;li&gt;Verifies the resulting object's storage class.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;For production implementations, also consider multipart copy for objects larger than 5 GB, bucket versioning, object tags, encryption, and permissions.&lt;/p&gt;

&lt;h2&gt;
  
  
  6. Important things to check before doing this in production
&lt;/h2&gt;

&lt;p&gt;Before changing storage classes, consider a few operational details.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Bucket versioning&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;If versioning is enabled, copying an object to the same key creates a new version. The previous Glacier version can still exist and incur storage charges.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Lifecycle policies&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Check whether lifecycle rules automatically move objects back into Glacier. Otherwise, the newly created Standard object might be archived again.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Storage and retrieval costs&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Glacier has minimum storage-duration requirements. Moving or deleting objects before those periods expire can result in early deletion charges.&lt;/p&gt;

&lt;p&gt;Restoration also incurs retrieval costs, and the temporary restored copy incurs Standard storage charges during its availability period.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Metadata and encryption&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Check whether the copy preserves the metadata, tags, encryption settings, and other properties your application depends on. &lt;code&gt;MetadataDirective: "COPY"&lt;/code&gt; preserves user-defined metadata, but production implementations should explicitly verify the required properties.&lt;/p&gt;

&lt;h2&gt;
  
  
  Conclusion
&lt;/h2&gt;

&lt;p&gt;Amazon S3 Glacier is useful for reducing storage costs, but understanding its restoration behavior is equally important.&lt;/p&gt;

&lt;p&gt;Remember these three things:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Glacier Instant Retrieval:&lt;/strong&gt; Access objects immediately without restoring them.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Glacier Flexible Retrieval and Deep Archive:&lt;/strong&gt; Restore objects before accessing them.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Need permanent Standard storage?&lt;/strong&gt; Restore first, then copy the object with &lt;code&gt;STANDARD&lt;/code&gt; as its storage class.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;A successful restore request doesn't necessarily mean your storage migration is complete.&lt;/p&gt;

&lt;p&gt;Always verify the actual storage class rather than relying only on the restoration status.&lt;/p&gt;

&lt;h3&gt;
  
  
  Official AWS references
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a href="https://docs.aws.amazon.com/AmazonS3/latest/userguide/glacier-storage-classes.html" rel="noopener noreferrer"&gt;Understanding S3 Glacier Storage Classes&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://docs.aws.amazon.com/AmazonS3/latest/userguide/restoring-objects.html" rel="noopener noreferrer"&gt;Restoring an Archived Object&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://docs.aws.amazon.com/AmazonS3/latest/userguide/archival-storage.html" rel="noopener noreferrer"&gt;Understanding Archival Storage&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://repost.aws/knowledge-center/restore-s3-object-glacier-storage-class" rel="noopener noreferrer"&gt;AWS re:Post – Restore S3 Objects from Glacier&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

</description>
      <category>aws</category>
      <category>cloud</category>
      <category>infrastructure</category>
      <category>devops</category>
    </item>
    <item>
      <title>How to Divide Work Across Threads in JavaScript: CPU Cores, Workload, and Smart Scheduling</title>
      <dc:creator>Nishikanta Ray</dc:creator>
      <pubDate>Mon, 26 Oct 2026 17:00:46 +0000</pubDate>
      <link>https://dev.to/nishikantaray/how-to-divide-work-across-threads-in-javascript-cpu-cores-workload-and-smart-scheduling-45jb</link>
      <guid>https://dev.to/nishikantaray/how-to-divide-work-across-threads-in-javascript-cpu-cores-workload-and-smart-scheduling-45jb</guid>
      <description>&lt;p&gt;Modern applications often need to process millions of records, generate reports, analyze events, resize images, or perform complex calculations. As workloads grow, running everything on a single JavaScript thread can become a performance bottleneck.&lt;/p&gt;

&lt;p&gt;The solution isn't simply to create more threads. The real challenge is deciding &lt;strong&gt;how many threads to create, how to divide the work, and how to assign tasks efficiently based on the machine's available resources.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;In this guide, we'll explore how JavaScript multithreading works in Node.js, how to divide workloads across CPU cores, and how to design a worker system that can scale efficiently.&lt;/p&gt;

&lt;h2&gt;
  
  
  1. Understanding JavaScript's Execution Model
&lt;/h2&gt;

&lt;p&gt;JavaScript traditionally executes application code on a single main thread. In Node.js, this thread runs the event loop, handles callbacks, and coordinates asynchronous operations.&lt;/p&gt;

&lt;p&gt;Consider a program that processes a large dataset:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;processData&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;records&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="k"&gt;for &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;record&lt;/span&gt; &lt;span class="k"&gt;of&lt;/span&gt; &lt;span class="nx"&gt;records&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="c1"&gt;// Perform expensive calculations&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="nf"&gt;processData&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;largeDataset&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;If &lt;code&gt;processData()&lt;/code&gt; performs CPU-intensive work, the main thread remains occupied until the function finishes. During that time, other JavaScript callbacks cannot execute on the same thread.&lt;/p&gt;

&lt;p&gt;As a result, an application may become unresponsive, requests may take longer to complete, and overall throughput can decrease.&lt;/p&gt;

&lt;p&gt;Node.js provides the &lt;code&gt;worker_threads&lt;/code&gt; module to run JavaScript in separate threads. These workers can execute CPU-intensive tasks in parallel, subject to the machine's available CPU resources.&lt;/p&gt;

&lt;p&gt;The important distinction is that asynchronous programming and multithreading solve different problems:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Asynchronous programming&lt;/strong&gt; allows a thread to continue while waiting for operations such as network or file I/O.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Multithreading&lt;/strong&gt; allows JavaScript code to execute on multiple threads, making parallel CPU computation possible.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Understanding this distinction is the first step toward building an efficient worker system.&lt;/p&gt;

&lt;h2&gt;
  
  
  2. How Does a Machine Determine Its Thread Capacity?
&lt;/h2&gt;

&lt;p&gt;Before distributing work, we need to understand the machine running the application.&lt;/p&gt;

&lt;p&gt;Imagine a server with the following specifications:&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Resource&lt;/th&gt;
&lt;th&gt;Specification&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Logical CPUs&lt;/td&gt;
&lt;td&gt;8&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;RAM&lt;/td&gt;
&lt;td&gt;16 GB&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Workload&lt;/td&gt;
&lt;td&gt;CPU-intensive data processing&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Main thread&lt;/td&gt;
&lt;td&gt;Handles coordination and application logic&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;The eight logical CPUs represent the hardware's reported capacity for simultaneous thread execution. Depending on the processor, logical CPUs may include simultaneous multithreading, such as Intel Hyper-Threading.&lt;/p&gt;

&lt;p&gt;However, eight logical CPUs do not automatically mean that eight worker threads will produce the best performance.&lt;/p&gt;

&lt;p&gt;Performance depends on several factors:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Whether the task is CPU-intensive or I/O-intensive.&lt;/li&gt;
&lt;li&gt;How much memory each worker requires.&lt;/li&gt;
&lt;li&gt;How much work is involved in creating and communicating with workers.&lt;/li&gt;
&lt;li&gt;Whether other applications are competing for CPU resources.&lt;/li&gt;
&lt;li&gt;Whether the application runs inside a container with CPU restrictions.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The operating system schedules threads across available processors. Multiple threads can exist even when fewer processors are available, but excess runnable threads may compete for CPU time.&lt;/p&gt;

&lt;p&gt;Therefore, &lt;strong&gt;CPU count is a useful starting point for selecting a worker count, not a universal rule for determining the optimal number of workers.&lt;/strong&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  3. Four Strategies for Dividing Work
&lt;/h2&gt;

&lt;p&gt;There is no single strategy that fits every workload. The right approach depends on the shape of the data and the amount of computation involved.&lt;/p&gt;

&lt;h3&gt;
  
  
  Strategy 1: Divide work based on CPU availability
&lt;/h3&gt;

&lt;p&gt;For CPU-heavy calculations, the number of available logical CPUs is a reasonable starting point.&lt;/p&gt;

&lt;p&gt;Node.js provides &lt;code&gt;os.availableParallelism()&lt;/code&gt; to estimate how much CPU parallelism the process can use.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;os&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;require&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;node:os&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;availableCPUs&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;os&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;availableParallelism&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;workerCount&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nb"&gt;Math&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;1&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;availableCPUs&lt;/span&gt; &lt;span class="o"&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;console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;log&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Available CPU parallelism:&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;availableCPUs&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="nx"&gt;console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;log&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Initial worker count:&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;workerCount&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;On a machine reporting eight available logical CPUs, this example starts with seven workers.&lt;/p&gt;

&lt;p&gt;Leaving capacity for the main thread can be useful, but it is not always necessary or optimal. The main thread might perform very little CPU work, or the application might run alongside other services that require substantial resources.&lt;/p&gt;

&lt;p&gt;Benchmarking should determine the final worker count.&lt;/p&gt;

&lt;h3&gt;
  
  
  Strategy 2: Divide work based on data size
&lt;/h3&gt;

&lt;p&gt;Suppose an application needs to process one million records using four workers.&lt;/p&gt;

&lt;p&gt;A straightforward approach assigns approximately 250,000 records to each worker.&lt;/p&gt;

&lt;p&gt;The general formula is:&lt;/p&gt;

&lt;p&gt;&lt;code&gt;chunkSize = ceil(totalItems / workerCount)&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;This approach is known as static partitioning because the work is divided before processing begins.&lt;/p&gt;

&lt;p&gt;It works particularly well when each item requires roughly the same amount of computation.&lt;/p&gt;

&lt;p&gt;However, equal numbers of records do not always mean equal amounts of work. For example, processing a record with two fields may take far less time than processing one containing a large nested object.&lt;/p&gt;

&lt;p&gt;When processing costs vary significantly, static partitioning can leave some workers idle while others continue working.&lt;/p&gt;

&lt;h3&gt;
  
  
  Strategy 3: Divide work into smaller tasks
&lt;/h3&gt;

&lt;p&gt;Instead of assigning one large block to each worker, an application can divide the workload into smaller jobs.&lt;/p&gt;

&lt;p&gt;For example, rather than assigning 250,000 records to each worker, it could create batches of 10,000 records.&lt;/p&gt;

&lt;p&gt;Workers retrieve additional batches as they finish their current tasks.&lt;/p&gt;

&lt;p&gt;This is dynamic scheduling.&lt;/p&gt;

&lt;p&gt;It is particularly useful when:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Tasks have unpredictable execution times.&lt;/li&gt;
&lt;li&gt;Records vary in complexity.&lt;/li&gt;
&lt;li&gt;Some jobs occasionally fail or need retries.&lt;/li&gt;
&lt;li&gt;The application processes a continuous stream of incoming work.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Dynamic scheduling introduces additional overhead for managing the task queue, so the batches should not be so small that coordination becomes more expensive than the computation.&lt;/p&gt;

&lt;h3&gt;
  
  
  Strategy 4: Divide work based on resource limits
&lt;/h3&gt;

&lt;p&gt;CPU availability is only one constraint. Memory can become the limiting factor.&lt;/p&gt;

&lt;p&gt;Suppose every worker needs 500 MB of additional memory for its data and intermediate calculations. Creating 20 workers could require roughly 10 GB of additional memory, before accounting for the main application, runtime overhead, and other processes.&lt;/p&gt;

&lt;p&gt;A practical worker system should consider:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;CPU availability.&lt;/li&gt;
&lt;li&gt;Memory available to the process.&lt;/li&gt;
&lt;li&gt;Input and output data size.&lt;/li&gt;
&lt;li&gt;Queue length.&lt;/li&gt;
&lt;li&gt;Task duration.&lt;/li&gt;
&lt;li&gt;Other application activity.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Resource-aware scheduling is especially important in production services and containerized environments.&lt;/p&gt;

&lt;h2&gt;
  
  
  4. A Complete Example: Distributing Work Across Node.js Workers
&lt;/h2&gt;

&lt;p&gt;Let's build a working example that calculates the sum of all integers from 1 to 100 million.&lt;/p&gt;

&lt;p&gt;The goal is to divide the calculation into separate ranges, assign each range to a worker, and combine the results.&lt;/p&gt;

&lt;h3&gt;
  
  
  Project structure
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;parallel-calculation/
├── main.js
└── worker.js
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Step 1: Create the worker
&lt;/h3&gt;

&lt;p&gt;Create &lt;code&gt;worker.js&lt;/code&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&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;parentPort&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="nx"&gt;workerData&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;require&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;node:worker_threads&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="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;start&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;end&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;workerData&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="kd"&gt;let&lt;/span&gt; &lt;span class="nx"&gt;sum&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="k"&gt;for &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kd"&gt;let&lt;/span&gt; &lt;span class="nx"&gt;i&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;start&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="nx"&gt;i&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;=&lt;/span&gt; &lt;span class="nx"&gt;end&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="nx"&gt;i&lt;/span&gt;&lt;span class="o"&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;sum&lt;/span&gt; &lt;span class="o"&gt;+=&lt;/span&gt; &lt;span class="nx"&gt;i&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="nx"&gt;parentPort&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;postMessage&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;sum&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Each worker receives a start and end value, calculates the sum of its assigned range, and sends the result back to the main thread.&lt;/p&gt;

&lt;h3&gt;
  
  
  Step 2: Create the main program
&lt;/h3&gt;

&lt;p&gt;Create &lt;code&gt;main.js&lt;/code&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;os&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;require&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;node:os&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="p"&gt;{&lt;/span&gt;
  &lt;span class="nx"&gt;Worker&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;require&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;node:worker_threads&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;TOTAL&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;100&lt;/span&gt;&lt;span class="nx"&gt;_000_000&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;availableCPUs&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;os&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;availableParallelism&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;

&lt;span class="c1"&gt;// Choose a starting worker count&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;workerCount&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nb"&gt;Math&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;1&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;availableCPUs&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt; &lt;span class="mi"&gt;1&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;chunkSize&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nb"&gt;Math&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;ceil&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;TOTAL&lt;/span&gt; &lt;span class="o"&gt;/&lt;/span&gt; &lt;span class="nx"&gt;workerCount&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

&lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;runWorker&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;start&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;end&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="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;Promise&lt;/span&gt;&lt;span class="p"&gt;((&lt;/span&gt;&lt;span class="nx"&gt;resolve&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;reject&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;worker&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;Worker&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;./worker.js&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="na"&gt;workerData&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;start&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;end&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;
    &lt;span class="p"&gt;});&lt;/span&gt;

    &lt;span class="nx"&gt;worker&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;once&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;message&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;resolve&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="nx"&gt;worker&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;once&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;error&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;reject&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

    &lt;span class="nx"&gt;worker&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;once&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;exit&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;code&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;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;code&lt;/span&gt; &lt;span class="o"&gt;!==&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="nf"&gt;reject&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;Error&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;`Worker exited with code &lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;code&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="p"&gt;);&lt;/span&gt;
      &lt;span class="p"&gt;}&lt;/span&gt;
    &lt;span class="p"&gt;});&lt;/span&gt;
  &lt;span class="p"&gt;});&lt;/span&gt;
&lt;span class="p"&gt;}&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;main&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nx"&gt;console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;log&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Available CPUs:&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;availableCPUs&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="nx"&gt;console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;log&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Workers:&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;workerCount&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;jobs&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[];&lt;/span&gt;

  &lt;span class="k"&gt;for &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kd"&gt;let&lt;/span&gt; &lt;span class="nx"&gt;i&lt;/span&gt; &lt;span class="o"&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;i&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&lt;/span&gt; &lt;span class="nx"&gt;workerCount&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="nx"&gt;i&lt;/span&gt;&lt;span class="o"&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;start&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;i&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="nx"&gt;chunkSize&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="mi"&gt;1&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;end&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nb"&gt;Math&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="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;i&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="nx"&gt;chunkSize&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="nx"&gt;TOTAL&lt;/span&gt;
    &lt;span class="p"&gt;);&lt;/span&gt;

    &lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;start&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;=&lt;/span&gt; &lt;span class="nx"&gt;end&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="nx"&gt;console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;log&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;`Assigning range &lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;start&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;end&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;jobs&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;push&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nf"&gt;runWorker&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;start&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;end&lt;/span&gt;&lt;span class="p"&gt;));&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;

  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;results&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="nx"&gt;jobs&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;totalSum&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;results&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;reduce&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;total&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;result&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;total&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="nx"&gt;result&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;console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;log&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Final sum:&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;totalSum&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="nf"&gt;main&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="nx"&gt;console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;error&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 the program
&lt;/h3&gt;

&lt;p&gt;Install Node.js if necessary, then run:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;node main.js
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The program detects available CPU parallelism, calculates a chunk size, launches workers, and waits for all results.&lt;/p&gt;

&lt;p&gt;The expected result is:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Final sum: 5000000050000000
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The output also displays the CPU count, worker count, and assigned ranges.&lt;/p&gt;

&lt;p&gt;This example demonstrates the essential workflow:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Detect available CPU parallelism.&lt;/li&gt;
&lt;li&gt;Determine the worker count.&lt;/li&gt;
&lt;li&gt;Divide the input into ranges.&lt;/li&gt;
&lt;li&gt;Run the calculations in separate threads.&lt;/li&gt;
&lt;li&gt;Collect the results.&lt;/li&gt;
&lt;li&gt;Combine the partial results.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;For this particular arithmetic problem, a direct mathematical formula would be much faster than either implementation. The example is intended to demonstrate work partitioning, not to suggest that creating workers is always the fastest solution.&lt;/p&gt;

&lt;h2&gt;
  
  
  5. Static Partitioning vs. Dynamic Scheduling
&lt;/h2&gt;

&lt;p&gt;The previous example uses static partitioning: each worker receives a predetermined range.&lt;/p&gt;

&lt;p&gt;Consider a different scenario where an application processes customer analytics events. Some events contain simple properties, while others require expensive transformations.&lt;/p&gt;

&lt;p&gt;Assigning the same number of events to every worker might not produce balanced execution times.&lt;/p&gt;

&lt;p&gt;A dynamic task queue can solve this problem.&lt;/p&gt;

&lt;p&gt;Conceptually, the system works like this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Incoming tasks
      |
      v
    Queue
   /  |  \
  v   v   v
 W1  W2  W3
  |   |   |
  +---+---+
      |
      v
  Completed results
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Whenever a worker finishes its current task, it takes another task from the queue.&lt;/p&gt;

&lt;p&gt;This approach is often called work sharing. It can improve utilization when task durations vary, although the queue itself must be designed to avoid becoming a bottleneck.&lt;/p&gt;

&lt;p&gt;A production implementation might also include retry policies, cancellation, timeouts, task priorities, and backpressure to prevent incoming work from exceeding processing capacity.&lt;/p&gt;

&lt;h2&gt;
  
  
  6. Why Worker Pools Are Better Than Creating a Thread for Every Task
&lt;/h2&gt;

&lt;p&gt;Creating a worker involves initialization, communication setup, and resource allocation. If an application creates a new worker for every small task, this overhead can become significant.&lt;/p&gt;

&lt;p&gt;A worker pool keeps a fixed number of workers alive and reuses them for multiple tasks.&lt;/p&gt;

&lt;p&gt;For example, a pool might maintain four workers:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Worker pool
├── Worker 1 → Task A → Task E
├── Worker 2 → Task B → Task F
├── Worker 3 → Task C → Task G
└── Worker 4 → Task D → Task H
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Tasks can wait in a queue until a worker becomes available.&lt;/p&gt;

&lt;p&gt;A worker pool can provide:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Controlled concurrency.&lt;/li&gt;
&lt;li&gt;Reduced worker initialization overhead.&lt;/li&gt;
&lt;li&gt;More predictable resource usage.&lt;/li&gt;
&lt;li&gt;Centralized error handling.&lt;/li&gt;
&lt;li&gt;Better throughput for repeated CPU-heavy jobs.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;In Node.js, worker pools can be implemented using &lt;code&gt;worker_threads&lt;/code&gt; or libraries such as Piscina.&lt;/p&gt;

&lt;p&gt;The pool size should still be selected according to the workload and measured performance.&lt;/p&gt;

&lt;h2&gt;
  
  
  7. How to Find the Optimal Number of Workers
&lt;/h2&gt;

&lt;p&gt;Suppose a workload runs on a machine reporting eight available logical CPUs.&lt;/p&gt;

&lt;p&gt;You could benchmark it using different worker counts:&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Worker count&lt;/th&gt;
&lt;th&gt;What to evaluate&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;Baseline performance&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;2&lt;/td&gt;
&lt;td&gt;Benefit from initial parallelism&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;4&lt;/td&gt;
&lt;td&gt;Throughput and CPU utilization&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;6&lt;/td&gt;
&lt;td&gt;Additional scaling&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;7&lt;/td&gt;
&lt;td&gt;Potential balance with main-thread work&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;8&lt;/td&gt;
&lt;td&gt;Whether using all available parallelism helps&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;Measure the time required to complete the same workload, as well as throughput, CPU utilization, memory usage, and latency.&lt;/p&gt;

&lt;p&gt;For a CPU-intensive task, adding workers can reduce execution time until the workload becomes limited by processor capacity, memory bandwidth, synchronization, or other overhead.&lt;/p&gt;

&lt;p&gt;Beyond that point, additional workers may offer little improvement or even reduce performance.&lt;/p&gt;

&lt;p&gt;Also remember that worker threads are not independent physical CPUs. They share the machine's underlying resources, and CPU scheduling can affect their performance.&lt;/p&gt;

&lt;p&gt;The best configuration is the one that meets your performance goals within your resource budget.&lt;/p&gt;

&lt;h2&gt;
  
  
  8. Common Mistakes to Avoid
&lt;/h2&gt;

&lt;h3&gt;
  
  
  Creating too many workers
&lt;/h3&gt;

&lt;p&gt;More workers do not always mean faster execution. Excessive concurrency can increase memory consumption and scheduling overhead.&lt;/p&gt;

&lt;h3&gt;
  
  
  Assuming every task takes the same time
&lt;/h3&gt;

&lt;p&gt;Equal-sized chunks work best when processing costs are reasonably uniform. For uneven tasks, dynamic scheduling can improve load balancing.&lt;/p&gt;

&lt;h3&gt;
  
  
  Using workers for ordinary I/O operations
&lt;/h3&gt;

&lt;p&gt;Network requests and many file operations can already be handled asynchronously by Node.js. Workers are most valuable when substantial CPU-intensive JavaScript work would otherwise block the main thread.&lt;/p&gt;

&lt;h3&gt;
  
  
  Ignoring data-transfer costs
&lt;/h3&gt;

&lt;p&gt;Workers normally communicate using messages, which can involve copying data. Large data transfers can offset the benefits of parallel computation. Transferable objects and shared memory can help in appropriate cases, but they require careful ownership and synchronization.&lt;/p&gt;

&lt;h3&gt;
  
  
  Ignoring memory limits
&lt;/h3&gt;

&lt;p&gt;Every worker adds runtime overhead and may allocate its own data. A worker count that works on a development laptop might be inappropriate in a production container with less memory or CPU capacity.&lt;/p&gt;

&lt;h2&gt;
  
  
  9. Final Architecture: A Resource-Aware Worker System
&lt;/h2&gt;

&lt;p&gt;For a production application, a robust design generally combines several of these techniques:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Detect the available CPU parallelism.&lt;/li&gt;
&lt;li&gt;Establish a configurable upper limit for workers.&lt;/li&gt;
&lt;li&gt;Estimate the memory and processing cost of tasks.&lt;/li&gt;
&lt;li&gt;Divide work into appropriately sized batches.&lt;/li&gt;
&lt;li&gt;Use a worker pool to reuse threads.&lt;/li&gt;
&lt;li&gt;Use dynamic scheduling when tasks vary significantly in duration.&lt;/li&gt;
&lt;li&gt;Measure throughput, latency, CPU usage, and memory consumption.&lt;/li&gt;
&lt;li&gt;Adjust the configuration based on observed performance.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;The result is a system that distributes work according to both the machine's capabilities and the workload's characteristics.&lt;/p&gt;

&lt;h2&gt;
  
  
  Conclusion
&lt;/h2&gt;

&lt;p&gt;Multithreading is not just about creating more threads. It is about distributing work efficiently across the resources available to your application.&lt;/p&gt;

&lt;p&gt;CPU-based partitioning provides a useful starting point. Data partitioning works well for uniform workloads. Dynamic scheduling helps balance uneven tasks, while resource-aware limits prevent excessive CPU and memory consumption.&lt;/p&gt;

&lt;p&gt;For JavaScript developers, Node.js &lt;code&gt;worker_threads&lt;/code&gt; provides the foundation for parallel CPU-intensive execution. Combining it with chunking, worker pools, and performance measurement can turn a single-threaded bottleneck into a more scalable processing system.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;The key principle is simple: choose the worker count based on available resources, divide work according to its computational cost, and let real performance measurements guide optimization.&lt;/strong&gt;&lt;/p&gt;

</description>
      <category>javascript</category>
      <category>node</category>
      <category>performance</category>
      <category>programming</category>
    </item>
    <item>
      <title>How Machines Find Meaning: The Math Behind Semantic Search</title>
      <dc:creator>Nishikanta Ray</dc:creator>
      <pubDate>Sun, 25 Oct 2026 16:51:16 +0000</pubDate>
      <link>https://dev.to/nishikantaray/how-machines-find-meaning-the-math-behind-semantic-search-b18</link>
      <guid>https://dev.to/nishikantaray/how-machines-find-meaning-the-math-behind-semantic-search-b18</guid>
      <description>&lt;p&gt;&lt;em&gt;Understanding cosine similarity, Euclidean distance, and how machines find relevant text and images.&lt;/em&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  1. Why do we need semantic search?
&lt;/h2&gt;

&lt;p&gt;Imagine you're building a search feature for your application.&lt;/p&gt;

&lt;p&gt;A user searches:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;How do I make my application faster?&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Your database contains these articles:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;How to optimize database queries&lt;/li&gt;
&lt;li&gt;Improving API performance with caching&lt;/li&gt;
&lt;li&gt;A beginner's guide to JavaScript arrays&lt;/li&gt;
&lt;li&gt;How to bake a chocolate cake&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Which articles should appear first?&lt;/p&gt;

&lt;p&gt;Traditional keyword search looks for matching words. Since the first two articles don't contain the exact phrase "make my application faster," a basic keyword search might rank them poorly.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Semantic search takes a different approach: it searches for meaning, not just matching words.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;It can recognize that "make my application faster," "optimize database queries," and "improve API performance" may describe related ideas.&lt;/p&gt;

&lt;p&gt;To understand how semantic search works, we need three concepts:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Embeddings:&lt;/strong&gt; Representing text or images as numbers.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Vectors:&lt;/strong&gt; Working with those numbers mathematically.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Similarity and distance:&lt;/strong&gt; Comparing vectors to rank relevant results.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Let's build these concepts from scratch using simple mathematics and JavaScript.&lt;/p&gt;

&lt;h2&gt;
  
  
  2. What is a vector?
&lt;/h2&gt;

&lt;p&gt;A vector is an ordered list of numbers.&lt;/p&gt;

&lt;p&gt;For example:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;vector&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;3&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;4&lt;/span&gt;&lt;span class="p"&gt;];&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;We can imagine this vector as a point on a two-dimensional graph:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;3&lt;/code&gt; represents the horizontal coordinate.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;4&lt;/code&gt; represents the vertical coordinate.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The point is therefore located at (3, 4).&lt;/p&gt;

&lt;p&gt;A vector can have two dimensions, three dimensions, or thousands of dimensions.&lt;/p&gt;

&lt;p&gt;For learning purposes, we'll use two-dimensional vectors because they're easy to visualize and calculate by hand.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Important:&lt;/strong&gt; These examples use illustrative numbers. Real embedding dimensions generally don't have simple, individually interpretable meanings.&lt;/p&gt;

&lt;h2&gt;
  
  
  3. What is an embedding?
&lt;/h2&gt;

&lt;p&gt;An embedding is a numerical representation of content generated by a machine-learning model.&lt;/p&gt;

&lt;p&gt;Imagine an embedding model converts three sentences into vectors:&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Sentence&lt;/th&gt;
&lt;th&gt;Illustrative embedding&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;"Make my app faster"&lt;/td&gt;
&lt;td&gt;&lt;code&gt;[0.9, 0.1]&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;"Improve application performance"&lt;/td&gt;
&lt;td&gt;&lt;code&gt;[0.8, 0.2]&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;"Chocolate cake recipe"&lt;/td&gt;
&lt;td&gt;&lt;code&gt;[0.1, 0.9]&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;The first two vectors point in similar directions. The third points in a different direction.&lt;/p&gt;

&lt;p&gt;This gives us a way to compare content mathematically.&lt;/p&gt;

&lt;p&gt;The embedding model learns useful representations during training. Our search system uses those representations to find and rank related content.&lt;/p&gt;

&lt;p&gt;A simplified semantic-search pipeline looks like this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;User query
    |
    v
Embedding model
    |
    v
Query vector
    |
    v
Compare with document vectors
    |
    v
Rank relevant documents
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Now let's explore the mathematics behind those comparisons.&lt;/p&gt;

&lt;h2&gt;
  
  
  4. Vector magnitude: how long is a vector?
&lt;/h2&gt;

&lt;p&gt;Before comparing vectors, let's understand their length, also called their &lt;strong&gt;magnitude&lt;/strong&gt; or &lt;strong&gt;L2 norm&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;Suppose we have:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;a = [3, 4]&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;The magnitude is calculated using the Pythagorean theorem.&lt;/p&gt;

&lt;p&gt;Step 1: Square both coordinates.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;‖a‖₂ = √(3² + 4²)&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Step 2: Simplify the squares.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;‖a‖₂ = √(9 + 16)&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Step 3: Add the values and calculate the square root.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;‖a‖₂ = √25 = 5&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Therefore, the magnitude of vector &lt;code&gt;[3, 4]&lt;/code&gt; is &lt;strong&gt;5&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;In simple terms, the magnitude tells us how far the point is from the origin, &lt;code&gt;(0, 0)&lt;/code&gt;.&lt;/p&gt;

&lt;h3&gt;
  
  
  Implementing magnitude in JavaScript
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;magnitude&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;vector&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;sumOfSquares&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;vector&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;reduce&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;sum&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;value&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;sum&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="nx"&gt;value&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="nx"&gt;value&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="k"&gt;return&lt;/span&gt; &lt;span class="nb"&gt;Math&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;sqrt&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;sumOfSquares&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="nx"&gt;console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;log&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nf"&gt;magnitude&lt;/span&gt;&lt;span class="p"&gt;([&lt;/span&gt;&lt;span class="mi"&gt;3&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;4&lt;/span&gt;&lt;span class="p"&gt;]));&lt;/span&gt; &lt;span class="c1"&gt;// 5&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Here's what the code does:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Squares every value.&lt;/li&gt;
&lt;li&gt;Adds the squared values together.&lt;/li&gt;
&lt;li&gt;Takes the square root of the sum.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;We'll reuse this function when calculating cosine similarity.&lt;/p&gt;

&lt;h2&gt;
  
  
  5. Euclidean distance: how far apart are two vectors?
&lt;/h2&gt;

&lt;p&gt;Magnitude measures the distance from the origin. &lt;strong&gt;Euclidean distance measures the distance between two points.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Suppose we have two points.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Point A&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;&lt;code&gt;a = [1, 2]&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Point B&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;&lt;code&gt;b = [4, 6]&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;How far apart are they?&lt;/p&gt;

&lt;p&gt;First, calculate the difference between their coordinates:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Horizontal difference: 4 − 1 = 3&lt;/li&gt;
&lt;li&gt;Vertical difference: 6 − 2 = 4&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Using the Pythagorean theorem:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;d = √(3² + 4²)&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;d = √(9 + 16)&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;d = √25 = 5&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;The Euclidean distance is &lt;strong&gt;5 units&lt;/strong&gt;.&lt;/p&gt;

&lt;h3&gt;
  
  
  The general L2 distance formula
&lt;/h3&gt;

&lt;p&gt;For two vectors with the same number of dimensions:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;dL2(a, b) = √[Σᵢ₌₁ⁿ (aᵢ − bᵢ)²]&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;You don't need to memorize the notation immediately. Remember the process:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Subtract corresponding coordinates.&lt;/li&gt;
&lt;li&gt;Square each difference.&lt;/li&gt;
&lt;li&gt;Add the squared differences.&lt;/li&gt;
&lt;li&gt;Take the square root.&lt;/li&gt;
&lt;/ol&gt;

&lt;h3&gt;
  
  
  Implementing L2 distance in JavaScript
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;l2Distance&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;a&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;b&lt;/span&gt;&lt;span class="p"&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="nx"&gt;a&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;length&lt;/span&gt; &lt;span class="o"&gt;!==&lt;/span&gt; &lt;span class="nx"&gt;b&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;length&lt;/span&gt;&lt;span class="p"&gt;)&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;Error&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Vectors must have equal dimensions&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;sumOfSquares&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;a&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;reduce&lt;/span&gt;&lt;span class="p"&gt;((&lt;/span&gt;&lt;span class="nx"&gt;sum&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;value&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;i&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;difference&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;value&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt; &lt;span class="nx"&gt;b&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;i&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;sum&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="nx"&gt;difference&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="nx"&gt;difference&lt;/span&gt;&lt;span class="p"&gt;;&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="k"&gt;return&lt;/span&gt; &lt;span class="nb"&gt;Math&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;sqrt&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;sumOfSquares&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;a&lt;/span&gt; &lt;span class="o"&gt;=&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="mi"&gt;2&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;b&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;4&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;6&lt;/span&gt;&lt;span class="p"&gt;];&lt;/span&gt;

&lt;span class="nx"&gt;console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;log&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nf"&gt;l2Distance&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;a&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;b&lt;/span&gt;&lt;span class="p"&gt;));&lt;/span&gt; &lt;span class="c1"&gt;// 5&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  How does this help with semantic search?
&lt;/h3&gt;

&lt;p&gt;Imagine our query has already been converted into a vector:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;query&lt;/span&gt; &lt;span class="o"&gt;=&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="mi"&gt;3&lt;/span&gt;&lt;span class="p"&gt;];&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;We also have two document embeddings:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;documentA&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;4&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;4&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;documentB&lt;/span&gt; &lt;span class="o"&gt;=&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="mi"&gt;5&lt;/span&gt;&lt;span class="p"&gt;];&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Let's calculate the distance from the query to each document.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Document A&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Distance = √((2 − 4)² + (3 − 4)²)&lt;/p&gt;

&lt;p&gt;Distance = √(4 + 1)&lt;/p&gt;

&lt;p&gt;Distance ≈ 2.236&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Document B&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Distance = √((2 − 2)² + (3 − 5)²)&lt;/p&gt;

&lt;p&gt;Distance = √(0 + 4)&lt;/p&gt;

&lt;p&gt;Distance = 2&lt;/p&gt;

&lt;p&gt;Document B is closer to the query because &lt;code&gt;2&lt;/code&gt; is smaller than &lt;code&gt;2.236&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;When ranking by L2 distance, &lt;strong&gt;a smaller distance means the vectors are closer together&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;However, Euclidean distance isn't the only way to compare vectors. Another popular metric is cosine similarity.&lt;/p&gt;

&lt;h2&gt;
  
  
  6. Dot product: multiplying corresponding values
&lt;/h2&gt;

&lt;p&gt;The dot product is an operation used in vector mathematics. It also plays an important role in calculating cosine similarity.&lt;/p&gt;

&lt;p&gt;Suppose we have two vectors:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;a = [2, 3]&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;b = [4, 5]&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Multiply their corresponding values and add the results:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;a · b = (2 × 4) + (3 × 5)&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;a · b = 8 + 15 = 23&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;The dot product is &lt;strong&gt;23&lt;/strong&gt;.&lt;/p&gt;

&lt;h3&gt;
  
  
  Implementing dot product in JavaScript
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;dotProduct&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;a&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;b&lt;/span&gt;&lt;span class="p"&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="nx"&gt;a&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;length&lt;/span&gt; &lt;span class="o"&gt;!==&lt;/span&gt; &lt;span class="nx"&gt;b&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;length&lt;/span&gt;&lt;span class="p"&gt;)&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;Error&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Vectors must have equal dimensions&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="k"&gt;return&lt;/span&gt; &lt;span class="nx"&gt;a&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;reduce&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;sum&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;value&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;i&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;sum&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="nx"&gt;value&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="nx"&gt;b&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;i&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="p"&gt;}&lt;/span&gt;

&lt;span class="nx"&gt;console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;log&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nf"&gt;dotProduct&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="mi"&gt;3&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;4&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;5&lt;/span&gt;&lt;span class="p"&gt;]));&lt;/span&gt; &lt;span class="c1"&gt;// 23&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The dot product combines information from corresponding dimensions. When divided by the magnitudes of both vectors, it gives us the cosine of the angle between them.&lt;/p&gt;

&lt;p&gt;That is the foundation of cosine similarity.&lt;/p&gt;

&lt;h2&gt;
  
  
  7. Cosine similarity: are the vectors pointing in the same direction?
&lt;/h2&gt;

&lt;p&gt;Euclidean distance asks:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;How far apart are two points?&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Cosine similarity asks:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;How closely do two vectors point in the same direction?&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Its formula is:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;cosineSimilarity(a, b) = (a · b) / (‖a‖₂ × ‖b‖₂)&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Let's work through an example.&lt;/p&gt;

&lt;p&gt;Suppose:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;a = [1, 2]&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;b = [2, 4]&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Notice that vector B is twice vector A. Both point in exactly the same direction.&lt;/p&gt;

&lt;h3&gt;
  
  
  Step 1: Calculate the dot product
&lt;/h3&gt;

&lt;p&gt;&lt;strong&gt;a · b = (1 × 2) + (2 × 4) = 10&lt;/strong&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  Step 2: Calculate both magnitudes
&lt;/h3&gt;

&lt;p&gt;For vector A:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;‖a‖₂ = √(1² + 2²) = √5&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;For vector B:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;‖b‖₂ = √(2² + 4²) = √20&lt;/strong&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  Step 3: Calculate cosine similarity
&lt;/h3&gt;

&lt;p&gt;&lt;strong&gt;cosineSimilarity = 10 / (√5 × √20)&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Since √5 × √20 = 10:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;cosineSimilarity = 10 / 10 = 1&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;The cosine similarity is &lt;strong&gt;1&lt;/strong&gt;, meaning the vectors point in exactly the same direction.&lt;/p&gt;

&lt;h3&gt;
  
  
  Interpreting cosine similarity
&lt;/h3&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Score&lt;/th&gt;
&lt;th&gt;Geometric interpretation&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;1&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Same direction&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;0.8&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Strong positive directional alignment&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;0&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Perpendicular directions&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;-1&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Opposite directions&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;These are geometric interpretations, not universal thresholds for semantic relevance. Whether a score indicates similar meaning depends on the embedding model and the data.&lt;/p&gt;

&lt;h3&gt;
  
  
  Implementing cosine similarity in JavaScript
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;magnitude&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;vector&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="nb"&gt;Math&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;sqrt&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="nx"&gt;vector&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;reduce&lt;/span&gt;&lt;span class="p"&gt;((&lt;/span&gt;&lt;span class="nx"&gt;sum&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;value&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;sum&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="nx"&gt;value&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="nx"&gt;value&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="p"&gt;);&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;dotProduct&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;a&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;b&lt;/span&gt;&lt;span class="p"&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="nx"&gt;a&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;length&lt;/span&gt; &lt;span class="o"&gt;!==&lt;/span&gt; &lt;span class="nx"&gt;b&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;length&lt;/span&gt;&lt;span class="p"&gt;)&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;Error&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Vectors must have equal dimensions&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="k"&gt;return&lt;/span&gt; &lt;span class="nx"&gt;a&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;reduce&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;sum&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;value&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;i&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;sum&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="nx"&gt;value&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="nx"&gt;b&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;i&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="p"&gt;}&lt;/span&gt;

&lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;cosineSimilarity&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;a&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;b&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;magnitudeA&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;magnitude&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;a&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;magnitudeB&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;magnitude&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;b&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

  &lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;magnitudeA&lt;/span&gt; &lt;span class="o"&gt;===&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt; &lt;span class="o"&gt;||&lt;/span&gt; &lt;span class="nx"&gt;magnitudeB&lt;/span&gt; &lt;span class="o"&gt;===&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;)&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;Error&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
      &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Cosine similarity is undefined for zero vectors&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="k"&gt;return&lt;/span&gt; &lt;span class="nf"&gt;dotProduct&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;a&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;b&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;/&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;magnitudeA&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="nx"&gt;magnitudeB&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="nx"&gt;console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;log&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nf"&gt;cosineSimilarity&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="mi"&gt;2&lt;/span&gt;&lt;span class="p"&gt;],&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="mi"&gt;4&lt;/span&gt;&lt;span class="p"&gt;]));&lt;/span&gt; &lt;span class="c1"&gt;// 1&lt;/span&gt;
&lt;span class="nx"&gt;console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;log&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nf"&gt;cosineSimilarity&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="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;],&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="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;]));&lt;/span&gt; &lt;span class="c1"&gt;// 0&lt;/span&gt;
&lt;span class="nx"&gt;console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;log&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nf"&gt;cosineSimilarity&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="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="mi"&gt;1&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="c1"&gt;// -1&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Cosine similarity is useful because multiplying a nonzero vector by a positive number doesn't change its direction. Consequently, cosine similarity is unaffected by positive scaling of either vector.&lt;/p&gt;

&lt;p&gt;This can be helpful when comparing embeddings whose lengths differ.&lt;/p&gt;

&lt;h2&gt;
  
  
  8. Cosine distance: turning similarity into distance
&lt;/h2&gt;

&lt;p&gt;Sometimes we want a distance metric where lower values indicate a closer match.&lt;/p&gt;

&lt;p&gt;A common definition is:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;cosineDistance = 1 − cosineSimilarity&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Suppose Document A has a cosine similarity score of &lt;code&gt;0.9&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Distance A = 1 − 0.9 = 0.1&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Document B has a cosine similarity score of &lt;code&gt;0.6&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Distance B = 1 − 0.6 = 0.4&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Document A is the closer match under this metric because its cosine distance is smaller.&lt;/p&gt;

&lt;h3&gt;
  
  
  Implementing cosine distance
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;cosineDistance&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;a&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;b&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="mi"&gt;1&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt; &lt;span class="nf"&gt;cosineSimilarity&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;a&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;b&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="nx"&gt;console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;log&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nf"&gt;cosineDistance&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="mi"&gt;2&lt;/span&gt;&lt;span class="p"&gt;],&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="mi"&gt;4&lt;/span&gt;&lt;span class="p"&gt;]));&lt;/span&gt; &lt;span class="c1"&gt;// 0&lt;/span&gt;
&lt;span class="nx"&gt;console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;log&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nf"&gt;cosineDistance&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="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;],&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="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;]));&lt;/span&gt; &lt;span class="c1"&gt;// 1&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Remember the distinction:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Cosine similarity:&lt;/strong&gt; Higher is better for directional alignment.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Cosine distance:&lt;/strong&gt; Lower is better.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;L2 distance:&lt;/strong&gt; Lower means the points are closer.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Implementations and vector databases may define or report metrics differently, so check the documentation for the system you're using.&lt;/p&gt;

&lt;h2&gt;
  
  
  9. L2 distance vs. cosine similarity
&lt;/h2&gt;

&lt;p&gt;Let's compare two vectors:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;a = [1, 1]&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;b = [10, 10]&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;These vectors point in exactly the same direction, but their magnitudes differ.&lt;/p&gt;

&lt;p&gt;Their cosine similarity is:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;cosineSimilarity(a, b) = 1&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Their cosine distance is:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;cosineDistance = 1 − 1 = 0&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Now calculate their Euclidean distance:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;dL2 = √((1 − 10)² + (1 − 10)²)&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;dL2 = √(81 + 81)&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;dL2 = √162 ≈ 12.728&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;The vectors have perfect directional alignment, yet their Euclidean distance is greater than 12.&lt;/p&gt;

&lt;p&gt;This illustrates the key difference:&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Property&lt;/th&gt;
&lt;th&gt;L2 distance&lt;/th&gt;
&lt;th&gt;Cosine similarity&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Measures&lt;/td&gt;
&lt;td&gt;Distance between coordinates&lt;/td&gt;
&lt;td&gt;Alignment of directions&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Affected by vector magnitude?&lt;/td&gt;
&lt;td&gt;Yes&lt;/td&gt;
&lt;td&gt;Not by positive scaling&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Better score&lt;/td&gt;
&lt;td&gt;Smaller&lt;/td&gt;
&lt;td&gt;Larger&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Useful when&lt;/td&gt;
&lt;td&gt;Coordinate distance matters&lt;/td&gt;
&lt;td&gt;Directional alignment matters&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;Neither metric is always better. The right choice depends on how the embedding model was trained and how its vectors should be compared.&lt;/p&gt;

&lt;h2&gt;
  
  
  10. Build a simple semantic-ranking example
&lt;/h2&gt;

&lt;p&gt;Now let's combine our functions to build a small vector-ranking demonstration in JavaScript.&lt;/p&gt;

&lt;p&gt;Imagine an embedding model has already converted a search query and three documents into two-dimensional vectors.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;query&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mf"&gt;0.8&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mf"&gt;0.6&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;documents&lt;/span&gt; &lt;span class="o"&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;title&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;API performance optimization&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="na"&gt;embedding&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mf"&gt;0.9&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mf"&gt;0.4&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;title&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Database query optimization&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="na"&gt;embedding&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mf"&gt;0.7&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mf"&gt;0.7&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;title&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Chocolate cake recipe&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="na"&gt;embedding&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="mf"&gt;0.5&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mf"&gt;0.8&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;These vectors are invented for demonstration. In a real application, an embedding model would generate them.&lt;/p&gt;

&lt;p&gt;Let's rank the documents by cosine similarity.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;magnitude&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;vector&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="nb"&gt;Math&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;sqrt&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="nx"&gt;vector&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;reduce&lt;/span&gt;&lt;span class="p"&gt;((&lt;/span&gt;&lt;span class="nx"&gt;sum&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;value&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;sum&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="nx"&gt;value&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="nx"&gt;value&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="p"&gt;);&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;cosineSimilarity&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;a&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;b&lt;/span&gt;&lt;span class="p"&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="nx"&gt;a&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;length&lt;/span&gt; &lt;span class="o"&gt;!==&lt;/span&gt; &lt;span class="nx"&gt;b&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;length&lt;/span&gt;&lt;span class="p"&gt;)&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;Error&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Vectors must have equal dimensions&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;dot&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;a&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;reduce&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;sum&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;value&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;i&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;sum&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="nx"&gt;value&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="nx"&gt;b&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;i&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="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;normA&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;magnitude&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;a&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;normB&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;magnitude&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;b&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

  &lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;normA&lt;/span&gt; &lt;span class="o"&gt;===&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt; &lt;span class="o"&gt;||&lt;/span&gt; &lt;span class="nx"&gt;normB&lt;/span&gt; &lt;span class="o"&gt;===&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;)&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;Error&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Zero vectors are not supported&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="k"&gt;return&lt;/span&gt; &lt;span class="nx"&gt;dot&lt;/span&gt; &lt;span class="o"&gt;/&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;normA&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="nx"&gt;normB&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;rankedDocuments&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;documents&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="nb"&gt;document&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;title&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;document&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="na"&gt;score&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nf"&gt;cosineSimilarity&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;query&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nb"&gt;document&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;embedding&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;sort&lt;/span&gt;&lt;span class="p"&gt;((&lt;/span&gt;&lt;span class="nx"&gt;a&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;b&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;b&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;score&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt; &lt;span class="nx"&gt;a&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;score&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

&lt;span class="nx"&gt;console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;log&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;rankedDocuments&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The results are approximately:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="p"&gt;[&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="s2"&gt;Database query optimization&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="na"&gt;score&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mf"&gt;0.990&lt;/span&gt;
  &lt;span class="p"&gt;},&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="s2"&gt;API performance optimization&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="na"&gt;score&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mf"&gt;0.975&lt;/span&gt;
  &lt;span class="p"&gt;},&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="s2"&gt;Chocolate cake recipe&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="na"&gt;score&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mf"&gt;0.085&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;We sort the results in descending order because higher cosine similarity means stronger directional alignment.&lt;/p&gt;

&lt;p&gt;Notice something important: &lt;strong&gt;our JavaScript code doesn't understand language. It only compares vectors.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;The embedding model is responsible for learning useful representations of language. The ranking code uses those representations to order the results.&lt;/p&gt;

&lt;p&gt;Our example demonstrates the mechanics of vector search, not a complete semantic search system.&lt;/p&gt;

&lt;h2&gt;
  
  
  11. Where do real embeddings come from?
&lt;/h2&gt;

&lt;p&gt;So far, we've manually supplied vectors such as &lt;code&gt;[0.8, 0.6]&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;Real applications use an embedding model to generate vectors from text or images.&lt;/p&gt;

&lt;p&gt;A typical workflow looks like this:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Choose an embedding model.&lt;/li&gt;
&lt;li&gt;Convert each document into a vector.&lt;/li&gt;
&lt;li&gt;Store the vectors alongside document IDs and content.&lt;/li&gt;
&lt;li&gt;Convert the user's query into a vector using a compatible model.&lt;/li&gt;
&lt;li&gt;Find the nearest document vectors.&lt;/li&gt;
&lt;li&gt;Return the corresponding documents in ranked order.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;A JavaScript application might call an embedding API like this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="c1"&gt;// Illustrative pseudocode.&lt;/span&gt;
&lt;span class="c1"&gt;// Replace this with your embedding provider's SDK.&lt;/span&gt;

&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;queryEmbedding&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;embeddingModel&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;embed&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
  &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;How do I make my application faster?&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;documentEmbedding&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;embeddingModel&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;embed&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
  &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Improve API performance with caching.&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;score&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;cosineSimilarity&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
  &lt;span class="nx"&gt;queryEmbedding&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="nx"&gt;documentEmbedding&lt;/span&gt;
&lt;span class="p"&gt;);&lt;/span&gt;

&lt;span class="nx"&gt;console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;log&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;score&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The exact API depends on your embedding provider. The key requirement is that both vectors must come from compatible embedding representations.&lt;/p&gt;

&lt;p&gt;For thousands or millions of documents, calculating every distance in application code can become expensive. A vector database or search engine can index embeddings and retrieve nearby candidates more efficiently.&lt;/p&gt;

&lt;h2&gt;
  
  
  12. Does the same mathematics work for image search?
&lt;/h2&gt;

&lt;p&gt;Yes. The comparison mathematics is the same, but the process that generates the vectors differs.&lt;/p&gt;

&lt;p&gt;Imagine you're building an image-search application. A user enters:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;A dog playing on a beach.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;A compatible text-image model converts the query into an embedding. It also converts the available images into embeddings in a shared vector space.&lt;/p&gt;

&lt;p&gt;The application compares the query vector against the image vectors and ranks the images by similarity.&lt;/p&gt;

&lt;p&gt;The process looks like this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Text query
    |
    v
Text encoder
    |
    v
Query embedding
    |
    v
Compare against image embeddings
    |
    v
Rank matching images
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This works directly only when the model is designed to produce comparable text and image embeddings.&lt;/p&gt;

&lt;p&gt;Why not simply compare raw pixels?&lt;/p&gt;

&lt;p&gt;Two photographs of the same dog might have different lighting, camera angles, cropping, or backgrounds. Raw pixel differences may be large even when the images depict similar content.&lt;/p&gt;

&lt;p&gt;Learned image embeddings can capture useful visual patterns that raw pixel distance misses.&lt;/p&gt;

&lt;p&gt;However, the model can still make mistakes. A high similarity score is a ranking signal, not proof that an image contains every detail in the query.&lt;/p&gt;

&lt;h2&gt;
  
  
  13. Why normalized vectors matter
&lt;/h2&gt;

&lt;p&gt;Some embedding models produce vectors with a magnitude of &lt;code&gt;1&lt;/code&gt;. These are called &lt;strong&gt;L2-normalized vectors&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;For unit-normalized vectors, Euclidean distance and cosine similarity are mathematically related:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;L2 distance² = 2 × (1 − cosine similarity)&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Equivalently:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;L2 distance = √[2 × (1 − cosine similarity)]&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;This means that ranking the same unit-normalized vectors by L2 distance or cosine similarity produces the same ordering:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Higher cosine similarity means lower L2 distance.&lt;/li&gt;
&lt;li&gt;Lower cosine similarity means higher L2 distance.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;This relationship can be useful when choosing a metric for a vector index.&lt;/p&gt;

&lt;p&gt;Don't assume every embedding model automatically normalizes its output. Check the model documentation before relying on this property.&lt;/p&gt;

&lt;h2&gt;
  
  
  14. What would I use in a real JavaScript application?
&lt;/h2&gt;

&lt;p&gt;For a practical semantic-search feature, I'd separate the system into four responsibilities.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;1. Embedding model&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Converts text or images into vectors that represent useful features of the content.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;2. Vector database&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Stores the vectors and retrieves nearby candidates efficiently.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;3. Similarity metric&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Determines how the system compares vectors, using cosine similarity, Euclidean distance, or another supported metric.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;4. Application code&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Handles queries, metadata, filtering, ranking, and displaying results to users.&lt;/p&gt;

&lt;p&gt;Start with the metric recommended by your embedding model, then test it against real queries and expected results.&lt;/p&gt;

&lt;p&gt;For example, if you're building documentation search, create a test set of queries such as "make API faster" and identify which articles should appear first. Measure whether those articles show up in the top five results.&lt;/p&gt;

&lt;p&gt;That gives you a practical way to evaluate your search quality rather than choosing a metric based only on intuition.&lt;/p&gt;

&lt;p&gt;You may also want &lt;strong&gt;hybrid search&lt;/strong&gt;, which combines vector similarity with keyword search. This can be especially useful when users search for exact identifiers, error messages, version numbers, or technical terms that must match precisely.&lt;/p&gt;

&lt;h2&gt;
  
  
  Conclusion
&lt;/h2&gt;

&lt;p&gt;Semantic search becomes much easier to understand when we break it into smaller mathematical concepts.&lt;/p&gt;

&lt;p&gt;A vector is an ordered list of numbers. An embedding model converts text or images into vectors. Euclidean distance measures how far apart two vectors are, while cosine similarity measures how closely their directions align. Cosine distance converts similarity into a distance-style score.&lt;/p&gt;

&lt;p&gt;With a few JavaScript functions, we can implement these calculations and build a simple vector-ranking demonstration.&lt;/p&gt;

&lt;p&gt;The most important distinction is this:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Vector mathematics gives us a way to compare representations. The embedding model learns representations that can capture useful relationships in meaning.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Both parts matter when building a useful semantic-search system.&lt;/p&gt;

</description>
      <category>ai</category>
      <category>machinelearning</category>
      <category>search</category>
    </item>
    <item>
      <title>Building InsightTrack with SerpApi for the SerpApi India Hackathon</title>
      <dc:creator>Nishikanta Ray</dc:creator>
      <pubDate>Sat, 03 Oct 2026 15:35:40 +0000</pubDate>
      <link>https://dev.to/nishikantaray/building-insighttrack-with-serpapi-for-the-serpapi-india-hackathon-5o6</link>
      <guid>https://dev.to/nishikantaray/building-insighttrack-with-serpapi-for-the-serpapi-india-hackathon-5o6</guid>
      <description>&lt;p&gt;A few weeks ago, I was looking at a page whose traffic had suddenly dropped.&lt;/p&gt;

&lt;p&gt;The analytics told me exactly what happened:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Traffic was down.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;But then I had the question that analytics couldn't answer:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Why?&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Did the page lose its Google ranking?&lt;br&gt;
Did a competitor move above me?&lt;br&gt;
Did Google change the search results?&lt;br&gt;
Did an AI Overview start answering the query?&lt;/p&gt;

&lt;p&gt;My next step was usually pretty manual. I'd open Google, search the keyword, check my position, look at the competitors, and try to figure out what changed.&lt;/p&gt;

&lt;p&gt;That got me thinking:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;What if I could connect my traffic data with the search results automatically?&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;That's how I started building &lt;strong&gt;InsightTrack&lt;/strong&gt;.&lt;/p&gt;
&lt;h2&gt;
  
  
  Connecting traffic with search
&lt;/h2&gt;

&lt;p&gt;The idea behind InsightTrack is pretty simple.&lt;/p&gt;

&lt;p&gt;I already have one side of the story from my analytics:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;/guides/email-templates

Last week: 1,240 visits
This week:   719 visits
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Now I need the other side.&lt;/p&gt;

&lt;p&gt;What happened in Google?&lt;/p&gt;

&lt;p&gt;That's where &lt;strong&gt;SerpApi&lt;/strong&gt; comes in.&lt;/p&gt;

&lt;p&gt;For the SerpApi India Hackathon, I'm integrating SerpApi with InsightTrack to collect SERP data and track how search visibility changes over time.&lt;/p&gt;

&lt;p&gt;For example:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;"free email templates"

Last week: #3
This week: #9
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Now the two pieces can be connected.&lt;/p&gt;

&lt;p&gt;Instead of simply saying:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;“Traffic dropped 42%.”&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;InsightTrack can say:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;“Traffic dropped because the page moved from #3 to #9, while other websites moved above it.”&lt;/strong&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;That's a much more useful answer.&lt;/p&gt;

&lt;h2&gt;
  
  
  I didn't want it to always invent a reason
&lt;/h2&gt;

&lt;p&gt;This became one of the most important parts of the project.&lt;/p&gt;

&lt;p&gt;Imagine traffic drops by 30%, but the ranking stays exactly the same.&lt;/p&gt;

&lt;p&gt;A system that always needs to produce an explanation might blame search anyway.&lt;/p&gt;

&lt;p&gt;I didn't want that.&lt;/p&gt;

&lt;p&gt;InsightTrack uses deterministic rules to compare traffic and SERP changes.&lt;/p&gt;

&lt;p&gt;If the evidence points to a ranking change, it reports that.&lt;/p&gt;

&lt;p&gt;If a competitor moved above you, it can identify that.&lt;/p&gt;

&lt;p&gt;But if search didn't really change, it can say:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;“Search visibility didn't change enough to explain this. Check your referrers, campaigns, or recent deployment.”&lt;/strong&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;The goal isn't to always have an answer.&lt;/p&gt;

&lt;p&gt;The goal is to have an answer that can be explained.&lt;/p&gt;

&lt;h2&gt;
  
  
  A small example
&lt;/h2&gt;

&lt;p&gt;Suppose we have:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Traffic:       -42%
Ranking:       #3 → #9
Competitor:    moved above us
AI Overview:   appeared
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;InsightTrack combines those observations and produces a finding instead of making me investigate each piece manually.&lt;/p&gt;

&lt;p&gt;Under the hood, the attribution engine is just a pure function:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;traffic data + keyword findings → finding
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;No database or API calls inside that layer.&lt;/p&gt;

&lt;p&gt;That makes it easy to test and, more importantly, makes the result reproducible.&lt;/p&gt;

&lt;h2&gt;
  
  
  Then I added MCP
&lt;/h2&gt;

&lt;p&gt;I also wanted this information to be accessible to AI agents.&lt;/p&gt;

&lt;p&gt;So InsightTrack exposes the same functionality through &lt;strong&gt;MCP&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;Now I can ask:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;“Who is ranking above me for ‘free email templates’, and why did they overtake me?”&lt;/strong&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;The Gemini API handles the natural-language interaction, while InsightTrack provides the underlying search and traffic data.&lt;/p&gt;

&lt;p&gt;The AI doesn't decide the attribution rules.&lt;/p&gt;

&lt;p&gt;It asks the tools for the evidence.&lt;/p&gt;

&lt;h2&gt;
  
  
  The unexpected challenge: search credits
&lt;/h2&gt;

&lt;p&gt;One thing I underestimated was API usage.&lt;/p&gt;

&lt;p&gt;Every SERP check costs credits.&lt;/p&gt;

&lt;p&gt;If I have 8 keywords and check them every day, that's roughly:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;8 × 30 = 240 searches/month
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;So the search budget became part of the product design.&lt;/p&gt;

&lt;p&gt;Instead of making users calculate everything themselves, InsightTrack can work out a collection schedule based on their available credits.&lt;/p&gt;

&lt;p&gt;That was a good reminder that API limits aren't just infrastructure problems. They can affect the product itself.&lt;/p&gt;

&lt;h2&gt;
  
  
  What I'm building next
&lt;/h2&gt;

&lt;p&gt;I want to make the search history more useful over longer periods, improve competitor tracking, and expand AI Overview monitoring.&lt;/p&gt;

&lt;p&gt;Because search is changing.&lt;/p&gt;

&lt;p&gt;A page can keep the same traditional ranking while the actual search experience around it changes dramatically.&lt;/p&gt;

&lt;p&gt;That's the gap I'm trying to explore with InsightTrack.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Analytics tells you what changed.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;SerpApi tells you what changed in search.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;InsightTrack connects the two.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Built for the &lt;strong&gt;SerpApi India Hackathon&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;🔗 &lt;strong&gt;insightstrack.dev&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;GitHub: &lt;strong&gt;github.com/NishikantaRay/InsightTrack&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Built with:&lt;/strong&gt; SerpApi · PostgreSQL · DuckDB · Node.js · React · MCP · Gemini API&lt;/p&gt;

</description>
      <category>analytics</category>
      <category>api</category>
      <category>automation</category>
      <category>devchallenge</category>
    </item>
    <item>
      <title>8 LLMs, 480 Questions, 1 Kaggle Benchmark: Who Can Explain a Traffic Drop?</title>
      <dc:creator>Nishikanta Ray</dc:creator>
      <pubDate>Sun, 27 Sep 2026 18:42:53 +0000</pubDate>
      <link>https://dev.to/nishikantaray/i-gave-8-llms-my-analytics-products-ai-job-the-cheap-ones-either-invent-a-reason-or-shrug-3f41</link>
      <guid>https://dev.to/nishikantaray/i-gave-8-llms-my-analytics-products-ai-job-the-cheap-ones-either-invent-a-reason-or-shrug-3f41</guid>
      <description>&lt;p&gt;&lt;em&gt;This is a submission for the &lt;a href="https://dev.to/challenges/kaggle-2026-09-23"&gt;Kaggle Benchmarking Challenge&lt;/a&gt;&lt;/em&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  What I Benchmarked
&lt;/h2&gt;

&lt;h3&gt;
  
  
  The short version
&lt;/h3&gt;

&lt;p&gt;I build &lt;a href="https://github.com/NishikantaRay/InsightTrack" rel="noopener noreferrer"&gt;InsightTrack&lt;/a&gt;, an open-source web analytics&lt;br&gt;
platform. It has an AI analyst called &lt;strong&gt;Pulse&lt;/strong&gt;. You ask &lt;em&gt;"why did traffic to /pricing drop last&lt;br&gt;
week?"&lt;/em&gt;, and Pulse picks the right tools, reads the numbers, and answers in plain English.&lt;/p&gt;

&lt;p&gt;Pulse needs a language model behind it. I wanted to choose that model with evidence, not vibes, so I&lt;br&gt;
turned Pulse's real job into a benchmark: &lt;strong&gt;480 questions, graded automatically, with answers&lt;br&gt;
that come from InsightTrack's own code.&lt;/strong&gt;&lt;/p&gt;
&lt;h3&gt;
  
  
  The problem: an AI analyst can be confidently wrong
&lt;/h3&gt;

&lt;p&gt;An analytics assistant isn't a chatbot for fun facts. People act on what it says: they rewrite pages,&lt;br&gt;
pause campaigns, or stop worrying. When it's wrong, it's wrong in one of two costly ways:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;It invents a reason.&lt;/strong&gt; Traffic moves for a boring reason (a holiday, a newsletter, plain noise), and the assistant confidently blames Google rankings. You spend a day fixing SEO that was never broken.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;It misses a real problem.&lt;/strong&gt; Traffic falls by half, and the assistant says "nothing significant happened". You find out a month later.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Both are worse than saying "I don't know". A useful analyst needs three habits that don't show up on&lt;br&gt;
general AI leaderboards:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Restraint:&lt;/strong&gt; saying "this is noise" or "the data doesn't show that" when that's the truth.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Rule-following on numbers:&lt;/strong&gt; applying thresholds exactly. A 15% change on 6 visitors is not a trend.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Reading real tool output:&lt;/strong&gt; the exact fields, rounding and formats a product actually returns, not textbook examples.&lt;/li&gt;
&lt;/ul&gt;
&lt;h3&gt;
  
  
  Why general benchmarks don't answer this
&lt;/h3&gt;

&lt;p&gt;Public benchmarks are excellent at telling you which model knows more maths, writes better code, or&lt;br&gt;
calls functions correctly. But choosing a model for a product is a &lt;strong&gt;product decision&lt;/strong&gt;, and it has&lt;br&gt;
three questions they can't answer for you:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Accuracy on &lt;em&gt;my&lt;/em&gt; job:&lt;/strong&gt; my tools, my data formats, my database dialect (DuckDB, not PostgreSQL).&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;The kind of mistakes:&lt;/strong&gt; a model that is wrong 10% of the time by saying "not sure" is safer than one that is wrong 5% of the time by making things up.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Cost:&lt;/strong&gt; is the expensive model worth it, or does a model at a tenth of the price do the same job?&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The only way to answer those is to test models on the actual work. That's what this benchmark does.&lt;/p&gt;
&lt;h3&gt;
  
  
  What's in it
&lt;/h3&gt;

&lt;p&gt;&lt;strong&gt;InsightTrack Analyst Bench&lt;/strong&gt; has 480 auto-graded cases across four tasks. The first three are Pulse's job today; the fourth, SQL, is the step Pulse doesn't take yet: writing a query when no built-in tool fits.&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Task&lt;/th&gt;
&lt;th&gt;What Pulse must do&lt;/th&gt;
&lt;th&gt;The model gets&lt;/th&gt;
&lt;th&gt;It's right when&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Tool choice&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Decide how to answer&lt;/td&gt;
&lt;td&gt;A user question + InsightTrack's real 23-tool catalogue&lt;/td&gt;
&lt;td&gt;It picks the right tool and arguments, or says "none" when no tool can do it&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Data reading&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Understand a result&lt;/td&gt;
&lt;td&gt;A tool result in InsightTrack's real output format&lt;/td&gt;
&lt;td&gt;It gets the number right, or says &lt;code&gt;not_available&lt;/code&gt; instead of inventing one&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Diagnosis&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Explain a change&lt;/td&gt;
&lt;td&gt;8 weeks of page traffic + Google results before and after&lt;/td&gt;
&lt;td&gt;It says whether the change is real, which way it went, and the &lt;strong&gt;exact&lt;/strong&gt; set of causes&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;SQL&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Dig deeper (next on Pulse's roadmap)&lt;/td&gt;
&lt;td&gt;InsightTrack's DuckDB &lt;code&gt;events&lt;/code&gt; and &lt;code&gt;sessions&lt;/code&gt; tables&lt;/td&gt;
&lt;td&gt;Its query, &lt;em&gt;executed&lt;/em&gt; read-only, returns the right rows&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;Each task has &lt;strong&gt;80 standard + 40 hard&lt;/strong&gt; cases. The hard ones are where careless reasoning shows:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;changes that land exactly on a threshold (a 15.0% drop)&lt;/li&gt;
&lt;li&gt;several keywords pointing in different directions&lt;/li&gt;
&lt;li&gt;pages that stopped ranking entirely&lt;/li&gt;
&lt;li&gt;lookalike domains: &lt;code&gt;getsite.com&lt;/code&gt; is a different site, but &lt;code&gt;www.site.com&lt;/code&gt; is yours&lt;/li&gt;
&lt;/ul&gt;
&lt;h3&gt;
  
  
  One case, step by step
&lt;/h3&gt;

&lt;p&gt;Here's what a diagnosis question looks like, simplified:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Weekly pageviews:&lt;/strong&gt; &lt;code&gt;… 495, 517, 347&lt;/code&gt;. The last week dropped by 170 views, or 33%.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Google, before:&lt;/strong&gt; the page ranked &lt;strong&gt;#3&lt;/strong&gt; for its keyword.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Google, now:&lt;/strong&gt; it ranks &lt;strong&gt;#5&lt;/strong&gt;. Nothing else changed.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;The rules&lt;/strong&gt; are all in the prompt. A change counts only if it's at least 15% &lt;strong&gt;and&lt;/strong&gt; at least 20 views. A 1–2 place rank move is normal jitter, never a cause.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The correct answer: &lt;strong&gt;the drop is real, but search didn't cause it.&lt;/strong&gt; In InsightTrack's terms,&lt;br&gt;
&lt;code&gt;{"significant": true, "direction": "drop", "causes": ["unexplained_by_serp"]}&lt;/code&gt;. The tempting wrong&lt;br&gt;
answer is "your rank dropped". A model that says that sends the user to fix SEO that isn't broken.&lt;/p&gt;

&lt;p&gt;That's the heart of the benchmark: &lt;strong&gt;can the model tell a real cause from a coincidence?&lt;/strong&gt;&lt;/p&gt;
&lt;h3&gt;
  
  
  Why the answers can be trusted
&lt;/h3&gt;

&lt;p&gt;No LLM grades another LLM:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Diagnosis:&lt;/strong&gt; the answer key is InsightTrack's own correlation engine, ported to Python. A test runs the &lt;strong&gt;original JavaScript&lt;/strong&gt; on every case plus 1,500 random ones and fails on any disagreement.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;SQL:&lt;/strong&gt; answers are graded by &lt;em&gt;executing&lt;/em&gt; the model's query, so two different but correct queries both pass.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Everything else:&lt;/strong&gt; answers are computed from the same data the model sees.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Data:&lt;/strong&gt; all synthetic (5,940 events, 2,494 sessions), with no real person's data, and the whole set rebuilds byte-for-byte.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Every grader also labels &lt;strong&gt;how&lt;/strong&gt; a model was wrong: &lt;em&gt;invented a cause&lt;/em&gt;, &lt;em&gt;called a real change noise&lt;/em&gt;,&lt;br&gt;
&lt;em&gt;made up a number&lt;/em&gt;, &lt;em&gt;refused an answerable question&lt;/em&gt;, &lt;em&gt;used a tool that doesn't exist&lt;/em&gt;. That turns a&lt;br&gt;
score into something you can act on.&lt;/p&gt;
&lt;h3&gt;
  
  
  Who this helps
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Developers building AI features on analytics or BI data.&lt;/strong&gt; You face the same choice I did. These results, and the failure types above, show what to expect from each class of model.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Anyone picking a model on a budget.&lt;/strong&gt; The results put a price on accuracy: where paying more buys a lot, and where it buys nothing.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;People who build and evaluate models.&lt;/strong&gt; Most tests reward knowing things. This one rewards &lt;em&gt;not&lt;/em&gt; claiming things, which is harder to measure and matters in real products.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;InsightTrack's users.&lt;/strong&gt; Pulse gets its model and its safety checks from evidence instead of guesswork.&lt;/li&gt;
&lt;/ul&gt;
&lt;h2&gt;
  
  
  Models Tested
&lt;/h2&gt;

&lt;p&gt;All models ran through Kaggle's model proxy on the same task version. I picked them in pairs, so each&lt;br&gt;
comparison answers a practical question:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Frontier vs. cheap, same company:&lt;/strong&gt; Claude Sonnet 5 vs. Claude Haiku 4.5, and Gemini 3.7 Flash vs. Gemini 3.5 Flash-Lite. &lt;em&gt;How much do you lose by saving money?&lt;/em&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Reasoning on vs. off, same model:&lt;/strong&gt; Grok 4.20 with and without reasoning. &lt;em&gt;Does "thinking" change the answer?&lt;/em&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;The cheapest option:&lt;/strong&gt; GPT-5.4 nano. &lt;em&gt;Is "good enough" good enough?&lt;/em&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;OpenAI's flagship:&lt;/strong&gt; GPT-5.5, as a reference point.&lt;/li&gt;
&lt;/ul&gt;
&lt;h2&gt;
  
  
  Findings
&lt;/h2&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Model&lt;/th&gt;
&lt;th&gt;Tool choice&lt;/th&gt;
&lt;th&gt;Data reading&lt;/th&gt;
&lt;th&gt;Diagnosis&lt;/th&gt;
&lt;th&gt;SQL&lt;/th&gt;
&lt;th&gt;&lt;strong&gt;Overall&lt;/strong&gt;&lt;/th&gt;
&lt;th&gt;Cost (480 cases)&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Claude Sonnet 5&lt;/td&gt;
&lt;td&gt;100&lt;/td&gt;
&lt;td&gt;100&lt;/td&gt;
&lt;td&gt;100&lt;/td&gt;
&lt;td&gt;100&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;100.0&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;$2.08&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;GPT-5.5&lt;/td&gt;
&lt;td&gt;100&lt;/td&gt;
&lt;td&gt;100&lt;/td&gt;
&lt;td&gt;96.7&lt;/td&gt;
&lt;td&gt;100&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;99.2&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;$3.23&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Grok 4.20 (reasoning)&lt;/td&gt;
&lt;td&gt;100&lt;/td&gt;
&lt;td&gt;96.7&lt;/td&gt;
&lt;td&gt;100&lt;/td&gt;
&lt;td&gt;95.0&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;97.9&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;$1.21&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Gemini 3.7 Flash&lt;/td&gt;
&lt;td&gt;100&lt;/td&gt;
&lt;td&gt;100&lt;/td&gt;
&lt;td&gt;100&lt;/td&gt;
&lt;td&gt;90.8&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;97.7&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;$1.07&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Gemini 3.5 Flash-Lite&lt;/td&gt;
&lt;td&gt;98.3&lt;/td&gt;
&lt;td&gt;88.3&lt;/td&gt;
&lt;td&gt;49.2&lt;/td&gt;
&lt;td&gt;88.3&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;81.0&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;$0.20&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Claude Haiku 4.5&lt;/td&gt;
&lt;td&gt;98.3&lt;/td&gt;
&lt;td&gt;82.5&lt;/td&gt;
&lt;td&gt;44.2&lt;/td&gt;
&lt;td&gt;86.7&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;77.9&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;$0.63&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Grok 4.20 (no reasoning)&lt;/td&gt;
&lt;td&gt;98.3&lt;/td&gt;
&lt;td&gt;82.5&lt;/td&gt;
&lt;td&gt;35.0&lt;/td&gt;
&lt;td&gt;92.5&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;77.1&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;$0.40&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;GPT-5.4 nano&lt;/td&gt;
&lt;td&gt;93.3&lt;/td&gt;
&lt;td&gt;61.7&lt;/td&gt;
&lt;td&gt;20.0&lt;/td&gt;
&lt;td&gt;93.3&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;67.1&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;$0.07&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;
&lt;h3&gt;
  
  
  1. Tool choice is solved. Diagnosis is where models split.
&lt;/h3&gt;

&lt;p&gt;Every model picked the right tool at least 93% of the time. So "can it call tools?" is no longer the&lt;br&gt;
interesting question for an analytics assistant. The real question is whether it understands what the&lt;br&gt;
numbers &lt;em&gt;mean&lt;/em&gt;. On diagnosis, the top three models score 100%, GPT-5.5 scores 96.7%, and every cheaper&lt;br&gt;
model scores between 20% and 49%.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2F4t2xqh5lc6lblx9wmhtn.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2F4t2xqh5lc6lblx9wmhtn.png" alt="Diagnosis accuracy by model" width="800" height="460"&gt;&lt;/a&gt;&lt;/p&gt;
&lt;h3&gt;
  
  
  2. Same model, reasoning on vs. off: 100% vs. 35%
&lt;/h3&gt;

&lt;p&gt;Grok 4.20 with reasoning got &lt;strong&gt;every&lt;/strong&gt; diagnosis case right. The same model without reasoning got&lt;br&gt;
&lt;strong&gt;35%&lt;/strong&gt;. It lost all 16 multi-cause cases, and 9 of the 12 decoys. Nothing else in the results moved this&lt;br&gt;
much. If your assistant answers "why did X change?", reasoning isn't a nice-to-have.&lt;/p&gt;
&lt;h3&gt;
  
  
  3. Cheap models fail in two opposite ways
&lt;/h3&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fxvsg1yr9otsys84ij0x2.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fxvsg1yr9otsys84ij0x2.png" alt="Two ways to be wrong about a traffic change" width="800" height="480"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;The alarmist.&lt;/strong&gt; Without reasoning, Grok invented a search cause 34 times. In one case, traffic&lt;br&gt;
&lt;em&gt;rose&lt;/em&gt; from 437 to 538 views while the page slipped from #3 to #5 on Google. The prompt says plainly&lt;br&gt;
that a 1–2 place move is normal jitter and never a cause. Grok answered:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight json"&gt;&lt;code&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="nl"&gt;"significant"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kc"&gt;true&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;"direction"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"spike"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;"causes"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s2"&gt;"rank_drop"&lt;/span&gt;&lt;span class="p"&gt;]}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;It blamed a rank &lt;em&gt;drop&lt;/em&gt; for a traffic &lt;em&gt;rise&lt;/em&gt;. The right answer was &lt;code&gt;unexplained_by_serp&lt;/code&gt;: this change&lt;br&gt;
didn't come from search.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;The shrugger.&lt;/strong&gt; GPT-5.4 nano went the other way, calling real changes noise 73 times. A page fell&lt;br&gt;
from &lt;strong&gt;209 to 110 views (−47%)&lt;/strong&gt; right after sliding from #14 to #26 on Google. Nano answered:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight json"&gt;&lt;code&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="nl"&gt;"significant"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kc"&gt;false&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;"direction"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kc"&gt;null&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;"causes"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[]}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;For an analytics product, this is the dangerous one. An alarmist wastes your afternoon. A shrugger&lt;br&gt;
tells you everything is fine while your traffic halves.&lt;/p&gt;
&lt;h3&gt;
  
  
  4. Mixed signals break every cheaper model: 0 of 8
&lt;/h3&gt;

&lt;p&gt;In the hard cases, one keyword lost rank while another &lt;em&gt;gained&lt;/em&gt;, and traffic fell. The correct answer&lt;br&gt;
names only the loss, since a gain can't explain a drop. Flash-Lite, Haiku, Grok without reasoning, and&lt;br&gt;
nano &lt;strong&gt;all scored 0 out of 8&lt;/strong&gt;. Haiku, for instance, blamed the drop on &lt;code&gt;ai_citation_gained&lt;/code&gt;, a rule the&lt;br&gt;
prompt says applies only when traffic rises. Real pages rank for many keywords at once, so this is&lt;br&gt;
the everyday case, not an edge case.&lt;/p&gt;
&lt;h3&gt;
  
  
  5. Reading data: the cheaper models refuse and invent
&lt;/h3&gt;

&lt;p&gt;On data reading, all 7 models correctly said &lt;code&gt;not_available&lt;/code&gt; on the 24 standard questions the data&lt;br&gt;
couldn't answer. The traps in the hard set split them:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Made-up numbers:&lt;/strong&gt; asked for the total unique visitors across a list of pages (impossible, because the same person can visit several pages), nano confidently answered &lt;strong&gt;9,149&lt;/strong&gt;.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Refused answerable questions:&lt;/strong&gt; nano did this 13 times, and the other cheaper models 5–7 times each. They answered "not available" when the answer took one calculation.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;A model that can't tell "I can't compute this" from "this can't be computed" isn't safe in either direction.&lt;/p&gt;
&lt;h3&gt;
  
  
  6. The SQL failures were habits, not logic
&lt;/h3&gt;

&lt;p&gt;&lt;strong&gt;Gemini 3.7 Flash scored 0/7 on JSON questions&lt;/strong&gt;, and nearly everything else it got right. Every time,&lt;br&gt;
it wrote:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight sql"&gt;&lt;code&gt;&lt;span class="k"&gt;WHERE&lt;/span&gt; &lt;span class="n"&gt;properties&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&amp;gt;&lt;/span&gt;&lt;span class="s1"&gt;'name'&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s1"&gt;'signup'&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;That's fine in PostgreSQL. In DuckDB, &lt;code&gt;-&amp;gt;&amp;gt;&lt;/code&gt; binds more loosely than &lt;code&gt;=&lt;/code&gt;, so it parses as&lt;br&gt;
&lt;code&gt;properties -&amp;gt;&amp;gt; ('name' = 'signup')&lt;/code&gt; and crashes. One pair of parentheses fixes it. It looks like a&lt;br&gt;
habit carried over from PostgreSQL, which is far more common in examples than DuckDB.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Claude Haiku corrects itself mid-answer.&lt;/strong&gt; In 9 SQL replies it wrote a query, then &lt;em&gt;"Wait, let me&lt;br&gt;
reconsider…"&lt;/em&gt;, then a second query. The prompt asks for the query only, so the grader takes the first&lt;br&gt;
one. In 8 of those 9 cases, the second query was right. Graded on its last query, Haiku's SQL score would&lt;br&gt;
be 93.3% instead of 86.7%. That's a real cost of thinking out loud inside a tool call: whatever sits&lt;br&gt;
between your model and your database reads the first answer.&lt;/p&gt;

&lt;h3&gt;
  
  
  7. GPT-5.5's only misses came from my prompt
&lt;/h3&gt;

&lt;p&gt;GPT-5.5 made exactly four mistakes on diagnosis, and all four are the same one. Each time, a page slid&lt;br&gt;
just past #10 (#10 to #11 twice, #10 to #12 once, #9 to #11 once): it fell off page one, so the&lt;br&gt;
answer is &lt;code&gt;page_one_exit&lt;/code&gt;. GPT-5.5 said&lt;br&gt;
&lt;code&gt;unexplained_by_serp&lt;/code&gt; every time.&lt;/p&gt;

&lt;p&gt;Rereading my prompt, I found two rules that clash:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;page_one_exit&lt;/code&gt;: "position 1–10 before; now below 10".&lt;/li&gt;
&lt;li&gt;"Moves of 1–2 places are normal jitter and are &lt;strong&gt;never a cause&lt;/strong&gt;."&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;I meant the jitter rule to cover &lt;code&gt;rank_drop&lt;/code&gt; and &lt;code&gt;rank_gain&lt;/code&gt;. GPT-5.5 took "never" literally. Claude&lt;br&gt;
Sonnet 5, Gemini 3.7 Flash and Grok with reasoning all read the intent. Neither reading is careless.&lt;br&gt;
One model follows the letter of an instruction and others follow its spirit, and that's worth knowing&lt;br&gt;
about any model you give rules to. It's also a reminder that &lt;strong&gt;a benchmark's prompt is code too&lt;/strong&gt;.&lt;br&gt;
The next version will say "never counts as &lt;code&gt;rank_drop&lt;/code&gt; or &lt;code&gt;rank_gain&lt;/code&gt;", and I'm reporting the&lt;br&gt;
score as it stands rather than quietly fixing it.&lt;/p&gt;

&lt;h3&gt;
  
  
  8. Value: Gemini 3.7 Flash is the sweet spot
&lt;/h3&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2F0o8be9ul3y6qruli0era.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2F0o8be9ul3y6qruli0era.png" alt="What you get for what you pay" width="800" height="500"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Gemini 3.7 Flash reaches 97.7% at about half the cost of Claude Sonnet 5. GPT-5.5 was the most&lt;br&gt;
expensive run ($3.23) and still finished just behind Sonnet. Below the top four there's a cliff: no model&lt;br&gt;
under $0.65 per run scored above 81%, and all of them fell apart on diagnosis.&lt;/p&gt;

&lt;h3&gt;
  
  
  So what will power Pulse?
&lt;/h3&gt;

&lt;p&gt;&lt;strong&gt;Gemini 3.7 Flash by default.&lt;/strong&gt; It scored 100% on the three tasks that are about judgement, at half the&lt;br&gt;
price of the most accurate model. Its one weakness is fixable in code: wrap &lt;code&gt;-&amp;gt;&amp;gt;&lt;/code&gt; comparisons in&lt;br&gt;
parentheses before running any generated SQL.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Claude Sonnet 5 as the fallback&lt;/strong&gt; for anything SQL-heavy, since it was perfect everywhere, and&lt;br&gt;
cheaper than GPT-5.5 for the same job.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;No cheaper model for diagnosis.&lt;/strong&gt; An assistant that shrugs at a 47% drop costs more than it saves.&lt;/p&gt;

&lt;p&gt;The benchmark also backed a design choice InsightTrack already makes. In the product, Pulse's&lt;br&gt;
&lt;code&gt;explain_traffic_change&lt;/code&gt; tool works out significance and causes &lt;strong&gt;in code&lt;/strong&gt;, and the model only&lt;br&gt;
explains the result in plain English. These results show why: left to do that reasoning alone, every&lt;br&gt;
cheaper model got it wrong at least half the time.&lt;/p&gt;

&lt;h3&gt;
  
  
  What this means if you're building an AI assistant
&lt;/h3&gt;

&lt;p&gt;These lessons carry over to any assistant that works with numbers:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;Compute, don't ask.&lt;/strong&gt; Anything with a threshold (significance, "is this a trend?") belongs in code. Let the model explain the result, not decide it.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Make "nothing happened" and "I can't tell" valid answers, and test them.&lt;/strong&gt; If your tests only have cases with a cause, you'll never see the shrugger or the alarmist until a user does.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Use reasoning where the answer is a &lt;em&gt;why&lt;/em&gt;.&lt;/strong&gt; It was the biggest single factor in the results: 100% vs. 35% on the same model.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Check generated SQL against your real database dialect.&lt;/strong&gt; Most failures here were PostgreSQL habits, not wrong logic, and a small rewrite or check catches them.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Don't parse the first thing a model says.&lt;/strong&gt; Ask for a clean final answer, or read the &lt;em&gt;last&lt;/em&gt; one, because models do correct themselves.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Route by task.&lt;/strong&gt; Tool choice is cheap to get right, so a small model can handle it. Diagnosis isn't.&lt;/li&gt;
&lt;/ol&gt;

&lt;h3&gt;
  
  
  What I'd measure next
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Consistency:&lt;/strong&gt; run each model three times and count how often the same case flips.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;The fixed jitter rule:&lt;/strong&gt; rerun every model and see whether GPT-5.5 reaches 100%, and whether any other model's score changes.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Real tool calls:&lt;/strong&gt; multi-turn runs where the model actually calls InsightTrack's tools, instead of naming the one it would call.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Messier evidence:&lt;/strong&gt; seasonality, like a holiday dip that isn't search, alongside keywords that point in different directions.&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Limits, honestly
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;"Correct" means correct by InsightTrack's rules.&lt;/strong&gt; Every rule and threshold is written into the prompt, so this tests careful application, not guessing a hidden convention.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Synthetic data, one run per model&lt;/strong&gt;, all through Kaggle's model proxy.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;SQL is graded strictly on the first query.&lt;/strong&gt; That costs Haiku, as described above.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;One rule in the diagnosis prompt is ambiguous&lt;/strong&gt; (the jitter rule vs. &lt;code&gt;page_one_exit&lt;/code&gt;). It accounts for all four of GPT-5.5's misses. Fixed wording is planned for the next version.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  My Benchmark
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;Kaggle benchmark:&lt;/strong&gt; &lt;a href="https://www.kaggle.com/benchmarks/nishikantaray/insighttrack-analyst-bench" rel="noopener noreferrer"&gt;https://www.kaggle.com/benchmarks/nishikantaray/insighttrack-analyst-bench&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Dataset (cases, sample tables, graders):&lt;/strong&gt; &lt;a href="https://www.kaggle.com/datasets/nishikantaray/insighttrack-analyst-bench" rel="noopener noreferrer"&gt;https://www.kaggle.com/datasets/nishikantaray/insighttrack-analyst-bench&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;The product it's built from:&lt;/strong&gt; &lt;a href="https://github.com/NishikantaRay/InsightTrack" rel="noopener noreferrer"&gt;https://github.com/NishikantaRay/InsightTrack&lt;/a&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  How you can use it
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Test another model:&lt;/strong&gt; the four tasks are public on Kaggle, so anyone can run them against any model in Kaggle's catalogue and compare with the table above.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Reuse the method for your own product:&lt;/strong&gt; take a rule your code already enforces, turn it into generated test cases with the answer from your code, include cases where the right answer is "nothing", and label the &lt;em&gt;kind&lt;/em&gt; of mistake as well as the score.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Built with &lt;a href="https://github.com/Kaggle/kaggle-benchmarks" rel="noopener noreferrer"&gt;Kaggle Benchmarks&lt;/a&gt;. The task pattern follows&lt;br&gt;
its &lt;code&gt;dataset_evaluation.py&lt;/code&gt; example. It covers ground near BFCL and τ-bench (tool calling),&lt;br&gt;
InfiAgent-DABench and DSBench (data analysis), and Spider 2.0 and BIRD (text-to-SQL). It differs by&lt;br&gt;
testing one real product's job end to end, and by scoring whether a model knows when &lt;em&gt;nothing&lt;/em&gt; happened.&lt;/p&gt;

</description>
      <category>devchallenge</category>
      <category>kagglechallenge</category>
      <category>ai</category>
      <category>machinelearning</category>
    </item>
    <item>
      <title>What If Your Coding Agent Could Remember What It Learned Yesterday?</title>
      <dc:creator>Nishikanta Ray</dc:creator>
      <pubDate>Thu, 17 Sep 2026 16:28:47 +0000</pubDate>
      <link>https://dev.to/nishikantaray/what-if-your-coding-agent-could-remember-what-it-learned-yesterday-2okj</link>
      <guid>https://dev.to/nishikantaray/what-if-your-coding-agent-could-remember-what-it-learned-yesterday-2okj</guid>
      <description>&lt;p&gt;&lt;em&gt;I built Attic to help Claude Code and Codex CLI remember what they discover — and now you can save knowledge from your browser too.&lt;/em&gt;&lt;/p&gt;




&lt;p&gt;You ask your AI coding assistant to investigate a bug.&lt;/p&gt;

&lt;p&gt;It searches the repository. Reads five files. Follows a few imports. Runs some commands. Finally, it tells you:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;"Found it. The authentication check happens after the desktop sub-window is created."&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Great. You fix the bug.&lt;/p&gt;

&lt;p&gt;A few days later, you start a new session.&lt;/p&gt;

&lt;p&gt;You ask the assistant to investigate another issue in the same area.&lt;/p&gt;

&lt;p&gt;It searches the same files.&lt;/p&gt;

&lt;p&gt;Reads the same imports.&lt;/p&gt;

&lt;p&gt;Runs the same commands.&lt;/p&gt;

&lt;p&gt;And eventually discovers the same thing you already knew.&lt;/p&gt;

&lt;p&gt;You just paid for the same investigation twice.&lt;/p&gt;

&lt;p&gt;Maybe three times.&lt;/p&gt;

&lt;p&gt;This is the problem I wanted to solve with &lt;strong&gt;Attic&lt;/strong&gt; — an open-source knowledge persistence tool for AI coding assistants.&lt;/p&gt;

&lt;p&gt;But the interesting part isn't simply saving notes.&lt;/p&gt;

&lt;p&gt;It's the design behind making those notes useful across sessions, after context compaction, and now even from your browser.&lt;/p&gt;

&lt;p&gt;GitHub: &lt;a href="https://github.com/NishikantaRay/Attic" rel="noopener noreferrer"&gt;https://github.com/NishikantaRay/Attic&lt;/a&gt;&lt;/p&gt;




&lt;h2&gt;
  
  
  The problem: Your AI knows more than your codebase
&lt;/h2&gt;

&lt;p&gt;Let's start with a simple example.&lt;/p&gt;

&lt;p&gt;Imagine you're working on a JavaScript monorepo.&lt;/p&gt;

&lt;p&gt;You ask Claude Code:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;"Why is the cloud package failing to load?"&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;The assistant investigates and finds:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="c1"&gt;// packages/cloud/index.js&lt;/span&gt;

&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="k"&gt;default&lt;/span&gt; &lt;span class="nx"&gt;cloudClient&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="k"&gt;default&lt;/span&gt; &lt;span class="nx"&gt;cloudConfig&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Two default exports.&lt;/p&gt;

&lt;p&gt;The file throws a syntax error.&lt;/p&gt;

&lt;p&gt;The assistant explains the issue. You fix it.&lt;/p&gt;

&lt;p&gt;But where does that knowledge go?&lt;/p&gt;

&lt;p&gt;If you only keep the conversation, the discovery is trapped inside that session.&lt;/p&gt;

&lt;p&gt;If you start a new session, your assistant may have to rediscover the same problem.&lt;/p&gt;

&lt;p&gt;The codebase contains the bug fix, but it doesn't necessarily contain the &lt;em&gt;reasoning that helped you find it&lt;/em&gt;.&lt;/p&gt;

&lt;p&gt;And reasoning is often what you need the next time.&lt;/p&gt;

&lt;h3&gt;
  
  
  Another example: Documentation versus reality
&lt;/h3&gt;

&lt;p&gt;Suppose your project has a rule:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;"All desktop sub-windows must wait for authentication before rendering."&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;But the actual implementation creates the window before the authentication gate finishes.&lt;/p&gt;

&lt;p&gt;Your assistant investigates and discovers the contradiction.&lt;/p&gt;

&lt;p&gt;That's valuable knowledge.&lt;/p&gt;

&lt;p&gt;Not just:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;There is a bug.
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



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

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;The desktop sub-window can be created before the authentication gate
completes, despite the documented requirement.
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;That tells the next investigation where to look.&lt;/p&gt;

&lt;p&gt;It also tells a teammate what assumption is unsafe.&lt;/p&gt;

&lt;p&gt;It explains &lt;em&gt;why&lt;/em&gt; the code behaves the way it does.&lt;/p&gt;

&lt;p&gt;This kind of knowledge is easy to lose.&lt;/p&gt;

&lt;p&gt;And it isn't limited to bugs.&lt;/p&gt;

&lt;p&gt;It includes:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Architectural decisions.&lt;/li&gt;
&lt;li&gt;Repository conventions.&lt;/li&gt;
&lt;li&gt;Failed approaches.&lt;/li&gt;
&lt;li&gt;Important file locations.&lt;/li&gt;
&lt;li&gt;Authentication assumptions.&lt;/li&gt;
&lt;li&gt;Performance discoveries.&lt;/li&gt;
&lt;li&gt;Why a particular implementation exists.&lt;/li&gt;
&lt;li&gt;Things you should never repeat.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Your codebase is a library of implementation.&lt;/p&gt;

&lt;p&gt;Your AI assistant's discoveries are the notes in the margins.&lt;/p&gt;

&lt;p&gt;The question is: &lt;strong&gt;Where do those notes live?&lt;/strong&gt;&lt;/p&gt;




&lt;h2&gt;
  
  
  Meet Attic: A memory shelf for your coding agent
&lt;/h2&gt;

&lt;p&gt;Attic is an open-source tool designed to help AI coding assistants persist and reuse knowledge.&lt;/p&gt;

&lt;p&gt;It works with:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Claude Code.&lt;/li&gt;
&lt;li&gt;Codex CLI.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The idea is straightforward:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Write what you learn to a file. Keep a small index in context. Retrieve the details when you need them.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;No need to put every discovery into the assistant's context forever.&lt;/p&gt;

&lt;p&gt;No need to rebuild the same understanding from scratch every session.&lt;/p&gt;

&lt;p&gt;And no need to turn your entire repository into a giant collection of instructions.&lt;/p&gt;

&lt;p&gt;Attic gives the agent a place to store useful discoveries.&lt;/p&gt;

&lt;p&gt;Think of it as a small knowledge library inside your project.&lt;/p&gt;




&lt;h2&gt;
  
  
  The library analogy that makes Attic click
&lt;/h2&gt;

&lt;p&gt;Imagine you have a library.&lt;/p&gt;

&lt;p&gt;You have 200 books.&lt;/p&gt;

&lt;p&gt;You don't carry all 200 books around with you.&lt;/p&gt;

&lt;p&gt;You carry a catalogue.&lt;/p&gt;

&lt;p&gt;The catalogue tells you:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Which books exist.&lt;/li&gt;
&lt;li&gt;Where they are.&lt;/li&gt;
&lt;li&gt;Which ones are important.&lt;/li&gt;
&lt;li&gt;What each book is about.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;When you need a particular book, you look it up.&lt;/p&gt;

&lt;p&gt;Attic follows the same idea.&lt;/p&gt;

&lt;h3&gt;
  
  
  The &lt;code&gt;.attic/&lt;/code&gt; folder is the library
&lt;/h3&gt;

&lt;p&gt;A typical Attic knowledge folder contains Markdown files:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;.attic/
├── INDEX.md
├── DECISIONS.md
└── items/
    ├── f1.md
    ├── f2.md
    ├── f3.md
    └── ...
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The exact files and commands depend on the installed version, but the core concept is consistent:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;The knowledge lives on disk. The index helps the agent find it.&lt;/strong&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  &lt;code&gt;INDEX.md&lt;/code&gt; is the catalogue card
&lt;/h3&gt;

&lt;p&gt;The index is not supposed to contain every detail.&lt;/p&gt;

&lt;p&gt;It's a compact representation of what matters.&lt;/p&gt;

&lt;p&gt;For example:&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;# Attic Index&lt;/span&gt;

&lt;span class="gu"&gt;## Pinned&lt;/span&gt;
&lt;span class="p"&gt;
-&lt;/span&gt; f3: Authentication must complete before
  desktop sub-windows are created.

&lt;span class="gu"&gt;## Recent findings&lt;/span&gt;
&lt;span class="p"&gt;
-&lt;/span&gt; f12: Cloud package has duplicate default exports.
&lt;span class="p"&gt;-&lt;/span&gt; f11: API client is initialized in the shared module.
&lt;span class="p"&gt;-&lt;/span&gt; f10: The docs and implementation disagree
  about the authentication lifecycle.

&lt;span class="gu"&gt;## Older items&lt;/span&gt;

95 older items not shown.
Use /attic-recall &lt;span class="nt"&gt;&amp;lt;topic&amp;gt;&lt;/span&gt;.
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This is an illustrative example of the idea, not a claim about the exact generated format.&lt;/p&gt;

&lt;p&gt;The agent can see the important information without loading the entire knowledge base into every prompt.&lt;/p&gt;

&lt;p&gt;When more detail is needed, it can recall the relevant item.&lt;/p&gt;

&lt;p&gt;That's the distinction:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;The index is the catalogue. The Markdown files are the books.&lt;/strong&gt;&lt;/p&gt;




&lt;h2&gt;
  
  
  Why not just put everything in the system prompt?
&lt;/h2&gt;

&lt;p&gt;This is a reasonable question.&lt;/p&gt;

&lt;p&gt;If the assistant needs the information, why not inject all of it?&lt;/p&gt;

&lt;p&gt;Because context is limited.&lt;/p&gt;

&lt;p&gt;And even when you have enough context, more context isn't automatically better.&lt;/p&gt;

&lt;p&gt;Imagine an assistant working on a project with 200 findings.&lt;/p&gt;

&lt;p&gt;If every finding is included in every session, the prompt becomes filled with:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Old discoveries.&lt;/li&gt;
&lt;li&gt;Superseded decisions.&lt;/li&gt;
&lt;li&gt;Unrelated bugs.&lt;/li&gt;
&lt;li&gt;Details from previous tasks.&lt;/li&gt;
&lt;li&gt;Information that may not matter to the current question.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The agent now has to spend attention deciding what matters.&lt;/p&gt;

&lt;p&gt;Attic takes a different approach.&lt;/p&gt;

&lt;h3&gt;
  
  
  Keep the index small
&lt;/h3&gt;

&lt;p&gt;Attic's runtime includes logic for loading the index and keeping its injected representation compact.&lt;/p&gt;

&lt;p&gt;The repository's design notes describe an index that prioritizes pinned findings, recent items, and compact summaries for older knowledge.&lt;/p&gt;

&lt;p&gt;The important idea is not a magic token number.&lt;/p&gt;

&lt;p&gt;It's this:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Don't force the assistant to carry the entire library when a catalogue will do.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h3&gt;
  
  
  Retrieve the details when needed
&lt;/h3&gt;

&lt;p&gt;Suppose the agent is investigating authentication.&lt;/p&gt;

&lt;p&gt;It doesn't need to read every database discovery, UI decision, and deployment note.&lt;/p&gt;

&lt;p&gt;It needs the authentication-related knowledge.&lt;/p&gt;

&lt;p&gt;That's where recall becomes useful.&lt;/p&gt;




&lt;h2&gt;
  
  
  The four commands: Put knowledge in, get knowledge out
&lt;/h2&gt;

&lt;p&gt;Attic's commands are organized around a simple workflow.&lt;/p&gt;

&lt;h3&gt;
  
  
  1. &lt;code&gt;stash&lt;/code&gt; — save what you learned
&lt;/h3&gt;

&lt;p&gt;After an investigation, save the discovery.&lt;/p&gt;

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

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

&lt;/div&gt;



&lt;p&gt;The exact command options depend on the installed version.&lt;/p&gt;

&lt;p&gt;Conceptually, you are turning an ephemeral chat discovery into a persistent Markdown item.&lt;/p&gt;

&lt;p&gt;Instead of:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;"I think the auth gate is somewhere in the desktop code."&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;You save:&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;# Authentication gate and desktop sub-windows&lt;/span&gt;

The desktop sub-window creation path can run
before the authentication gate completes.

When investigating window lifecycle bugs,
check the auth gate before the window creation path.

Tags:
&lt;span class="p"&gt;-&lt;/span&gt; authentication
&lt;span class="p"&gt;-&lt;/span&gt; desktop
&lt;span class="p"&gt;-&lt;/span&gt; lifecycle
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Now that discovery has a home.&lt;/p&gt;

&lt;h3&gt;
  
  
  2. &lt;code&gt;recall&lt;/code&gt; — find what you already know
&lt;/h3&gt;

&lt;p&gt;Later, you can ask Attic to retrieve relevant knowledge.&lt;/p&gt;

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

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;/attic-recall authentication
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The assistant can use the stored findings instead of starting from zero.&lt;/p&gt;

&lt;p&gt;This is particularly useful when:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;A new session starts.&lt;/li&gt;
&lt;li&gt;You run &lt;code&gt;/compact&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;A teammate clones the project.&lt;/li&gt;
&lt;li&gt;You return to an old feature.&lt;/li&gt;
&lt;li&gt;You forgot why a particular design exists.&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  3. &lt;code&gt;index&lt;/code&gt; — inspect the catalogue
&lt;/h3&gt;

&lt;p&gt;The index helps you understand what the agent can see.&lt;/p&gt;

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

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

&lt;/div&gt;



&lt;p&gt;The idea is to inspect the compact catalogue rather than opening every item.&lt;/p&gt;

&lt;h3&gt;
  
  
  4. &lt;code&gt;pin&lt;/code&gt;, &lt;code&gt;prune&lt;/code&gt;, and &lt;code&gt;archive&lt;/code&gt; — manage the shelf
&lt;/h3&gt;

&lt;p&gt;Not every discovery deserves permanent prominence.&lt;/p&gt;

&lt;p&gt;Some knowledge is important forever.&lt;/p&gt;

&lt;p&gt;Some is useful for a week.&lt;/p&gt;

&lt;p&gt;Some becomes obsolete after a refactor.&lt;/p&gt;

&lt;p&gt;Attic provides commands for managing that lifecycle:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;/attic-pin
/attic-prune
/attic-archive
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The exact syntax depends on the version and installed commands.&lt;/p&gt;

&lt;p&gt;The important design decision is that knowledge needs management.&lt;/p&gt;

&lt;p&gt;A memory system that only writes and never cleans up eventually becomes another source of noise.&lt;/p&gt;




&lt;h2&gt;
  
  
  The part I find most interesting: The index is not the memory
&lt;/h2&gt;

&lt;p&gt;This is the design detail worth understanding.&lt;/p&gt;

&lt;p&gt;A common approach to agent memory is:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Store a large amount of information and inject it into the next prompt.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Attic separates storage from injection.&lt;/p&gt;

&lt;h3&gt;
  
  
  Storage
&lt;/h3&gt;

&lt;p&gt;The detailed findings live in Markdown files.&lt;/p&gt;

&lt;p&gt;They can contain context, explanations, file references, and reasoning.&lt;/p&gt;

&lt;h3&gt;
  
  
  Injection
&lt;/h3&gt;

&lt;p&gt;The assistant receives a compact index.&lt;/p&gt;

&lt;p&gt;The index tells it what exists and how to find it.&lt;/p&gt;

&lt;p&gt;This separation gives you a useful property:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;You can have a large knowledge base without making every prompt equally large.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;For example:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Knowledge on disk:

200 findings
│
├── Pinned findings
├── Recent findings
└── Older findings
    └── Available through recall
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The assistant doesn't need all 200 findings in context.&lt;/p&gt;

&lt;p&gt;It needs enough information to navigate the collection.&lt;/p&gt;

&lt;p&gt;This is a familiar pattern in software engineering:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Database versus index.&lt;/li&gt;
&lt;li&gt;Filesystem versus directory listing.&lt;/li&gt;
&lt;li&gt;Library versus catalogue.&lt;/li&gt;
&lt;li&gt;Search engine versus document store.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Attic applies that idea to coding-agent memory.&lt;/p&gt;




&lt;h2&gt;
  
  
  What happens when you use &lt;code&gt;/compact&lt;/code&gt;?
&lt;/h2&gt;

&lt;p&gt;This is one of the reasons persistent memory matters.&lt;/p&gt;

&lt;p&gt;Imagine a long coding session.&lt;/p&gt;

&lt;p&gt;At the beginning, you ask:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;"Understand the authentication flow."&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;The agent investigates.&lt;/p&gt;

&lt;p&gt;It discovers:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;The auth gate lives in one module.&lt;/li&gt;
&lt;li&gt;The desktop window is created in another.&lt;/li&gt;
&lt;li&gt;The window creation can happen before auth finishes.&lt;/li&gt;
&lt;li&gt;The documentation assumes the opposite order.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;You fix the problem.&lt;/p&gt;

&lt;p&gt;Then the conversation becomes huge.&lt;/p&gt;

&lt;p&gt;You use:&lt;br&gt;
&lt;/p&gt;

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

&lt;/div&gt;



&lt;p&gt;The context is reduced.&lt;/p&gt;

&lt;p&gt;The agent may no longer have every detail from the earlier investigation.&lt;/p&gt;

&lt;p&gt;If the key discovery existed only in the conversation, it can be difficult to recover.&lt;/p&gt;

&lt;p&gt;But if you saved it to Attic:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;.attic/items/f3.md
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The knowledge remains on disk.&lt;/p&gt;

&lt;p&gt;A later session can recall it.&lt;/p&gt;

&lt;p&gt;The discovery has survived the conversation.&lt;/p&gt;

&lt;p&gt;This is the central promise of persistent agent memory:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Your knowledge should outlive the context window.&lt;/p&gt;
&lt;/blockquote&gt;




&lt;h1&gt;
  
  
  The browser extension: Knowledge doesn't only happen in your terminal
&lt;/h1&gt;

&lt;p&gt;Now let's talk about the feature that makes Attic particularly interesting.&lt;/p&gt;

&lt;p&gt;Most coding-agent memory tools focus on what happens inside the terminal.&lt;/p&gt;

&lt;p&gt;But developers learn things everywhere.&lt;/p&gt;

&lt;p&gt;You might discover a useful fact while reading:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;A GitHub issue.&lt;/li&gt;
&lt;li&gt;A pull request.&lt;/li&gt;
&lt;li&gt;A Stack Overflow answer.&lt;/li&gt;
&lt;li&gt;A technical blog.&lt;/li&gt;
&lt;li&gt;A framework documentation page.&lt;/li&gt;
&lt;li&gt;A browser tab containing an important explanation.&lt;/li&gt;
&lt;li&gt;A discussion about a design decision.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;You might think:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;"This is useful. I should save it."&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Then you close the tab.&lt;/p&gt;

&lt;p&gt;Or forget.&lt;/p&gt;

&lt;p&gt;Or leave it in your browser history.&lt;/p&gt;

&lt;p&gt;The information never makes it into your project's knowledge base.&lt;/p&gt;

&lt;p&gt;Attic's browser companion is designed to close that gap.&lt;/p&gt;

&lt;p&gt;Instead of only remembering what the coding agent discovers, you can save useful knowledge from the browser into the same Attic shelf.&lt;/p&gt;

&lt;p&gt;That is a powerful extension of the original idea.&lt;/p&gt;




&lt;h2&gt;
  
  
  Browser to project memory
&lt;/h2&gt;

&lt;p&gt;Let's say you're investigating a bug in a Node.js application.&lt;/p&gt;

&lt;p&gt;You open a GitHub issue discussing a subtle behavior in a dependency.&lt;/p&gt;

&lt;p&gt;The issue contains the explanation you need.&lt;/p&gt;

&lt;p&gt;You don't want to copy the entire page into your project.&lt;/p&gt;

&lt;p&gt;You want to save the useful discovery.&lt;/p&gt;

&lt;p&gt;The workflow becomes:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Read useful information in browser
          │
          ▼
       Clip it
          │
          ▼
  Save to Attic knowledge
          │
          ▼
  Recall it in a future session
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The browser becomes another input into your project memory.&lt;/p&gt;

&lt;p&gt;This is important because developers don't learn only by asking an AI agent questions.&lt;/p&gt;

&lt;p&gt;They learn by reading.&lt;/p&gt;

&lt;p&gt;And a lot of that reading happens outside the terminal.&lt;/p&gt;




&lt;h2&gt;
  
  
  What is a clip?
&lt;/h2&gt;

&lt;p&gt;A clip is a way to capture useful browser content and save it as a knowledge item.&lt;/p&gt;

&lt;p&gt;Imagine reading this on a documentation page:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;"The authentication callback must complete before the desktop window is created."&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Instead of copying the entire article into a notes app, you can clip the useful content into Attic.&lt;/p&gt;

&lt;p&gt;The saved item might look like:&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;# Authentication callback lifecycle&lt;/span&gt;

Source: Browser research

The authentication callback must complete
before the desktop window is created.

This matters because creating the window
too early can bypass the expected auth gate.

Tags:
&lt;span class="p"&gt;-&lt;/span&gt; authentication
&lt;span class="p"&gt;-&lt;/span&gt; desktop
&lt;span class="p"&gt;-&lt;/span&gt; lifecycle
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Now your browser research becomes part of the same knowledge collection used by your coding agent.&lt;/p&gt;

&lt;p&gt;The exact clip UI and available options depend on the installed extension version.&lt;/p&gt;




&lt;h2&gt;
  
  
  Why reuse the CLI writer instead of building a second memory system?
&lt;/h2&gt;

&lt;p&gt;This is one of the most important design calls in Attic's browser companion.&lt;/p&gt;

&lt;p&gt;A tempting approach would be to build a separate browser storage system.&lt;/p&gt;

&lt;p&gt;Something like:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Browser extension
       │
       ▼
Browser database
       │
       ▼
Browser-only notes
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Then the CLI would have its own memory system:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;CLI
 │
 ▼
.attic/
 │
 ▼
CLI-only notes
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Now you have two systems.&lt;/p&gt;

&lt;p&gt;Two storage formats.&lt;/p&gt;

&lt;p&gt;Two sets of rules.&lt;/p&gt;

&lt;p&gt;Two places to search.&lt;/p&gt;

&lt;p&gt;Two systems to maintain.&lt;/p&gt;

&lt;p&gt;Attic takes a different approach.&lt;/p&gt;

&lt;p&gt;The browser companion reuses the CLI writer.&lt;/p&gt;

&lt;p&gt;The repository's design notes explicitly call out that the companion uses the existing CLI writer rather than reimplementing it.&lt;/p&gt;

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

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;                 ┌────────────────────┐
                 │  Browser extension │
                 └─────────┬──────────┘
                           │
                           ▼
                 ┌────────────────────┐
                 │   CLI writer       │
                 └─────────┬──────────┘
                           │
                           ▼
                      .attic/
                           │
                 ┌─────────┴──────────┐
                 ▼                    ▼
             INDEX.md             items/*.md
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This is a classic software engineering principle:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Reuse the existing source of truth instead of creating a second implementation.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;The CLI already knows how to write Attic items.&lt;/p&gt;

&lt;p&gt;The browser companion should not need to invent a different way to do the same job.&lt;/p&gt;

&lt;p&gt;That keeps the storage model consistent.&lt;/p&gt;

&lt;p&gt;It also means the knowledge saved from your browser can be used by the CLI.&lt;/p&gt;

&lt;p&gt;And knowledge saved from the CLI can be found in the same collection.&lt;/p&gt;

&lt;p&gt;One shelf.&lt;/p&gt;

&lt;p&gt;Multiple ways to put books on it.&lt;/p&gt;




&lt;h2&gt;
  
  
  The subtle distinction: Edit is not stash
&lt;/h2&gt;

&lt;p&gt;This is another design decision worth talking about.&lt;/p&gt;

&lt;p&gt;At first glance, these two actions might seem similar.&lt;/p&gt;

&lt;h3&gt;
  
  
  Stash
&lt;/h3&gt;

&lt;p&gt;You discover something new.&lt;/p&gt;

&lt;p&gt;You want to add it to the knowledge base.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;New discovery → Add a knowledge item
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Edit
&lt;/h3&gt;

&lt;p&gt;You already have a knowledge item.&lt;/p&gt;

&lt;p&gt;You want to correct or update it.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Existing item → Replace its content
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;These are not the same operation.&lt;/p&gt;

&lt;p&gt;Suppose you have this finding:&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;# Authentication lifecycle&lt;/span&gt;

The desktop window is created before
the authentication callback completes.
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Later, you investigate more deeply and discover that the bug has been fixed.&lt;/p&gt;

&lt;p&gt;You edit the existing item:&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;# Authentication lifecycle&lt;/span&gt;

The desktop window now waits for the
authentication callback to complete.

The previous behavior was caused by
the window creation path running too early.
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;You have updated the knowledge.&lt;/p&gt;

&lt;p&gt;You haven't discovered a second, unrelated fact.&lt;/p&gt;

&lt;h3&gt;
  
  
  Why appending everything is a problem
&lt;/h3&gt;

&lt;p&gt;If editing simply appended another copy, you could end up with:&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;# Authentication lifecycle&lt;/span&gt;

The desktop window is created before auth.
&lt;span class="p"&gt;
---
&lt;/span&gt;
&lt;span class="gh"&gt;# Authentication lifecycle&lt;/span&gt;

The desktop window waits for auth.
&lt;span class="p"&gt;
---
&lt;/span&gt;
&lt;span class="gh"&gt;# Authentication lifecycle&lt;/span&gt;

The previous behavior was caused by...
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Now the agent has to reconcile multiple versions.&lt;/p&gt;

&lt;p&gt;That's noise.&lt;/p&gt;

&lt;p&gt;The repository's design notes explicitly distinguish editing from stashing:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Editing replaces; stashing appends.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;This is a small detail, but it reflects a larger principle:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Memory needs both creation and correction.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;A knowledge system that only adds information eventually becomes cluttered with stale information.&lt;/p&gt;




&lt;h2&gt;
  
  
  Why there is no delete endpoint
&lt;/h2&gt;

&lt;p&gt;This might sound strange.&lt;/p&gt;

&lt;p&gt;If a knowledge item is wrong, why not just delete it?&lt;/p&gt;

&lt;p&gt;The design notes for Attic's browser companion call out that there is no delete endpoint.&lt;/p&gt;

&lt;p&gt;Instead, the system provides management operations such as archive and prune.&lt;/p&gt;

&lt;p&gt;There is a reason to be careful here.&lt;/p&gt;

&lt;p&gt;Deleting knowledge can be destructive.&lt;/p&gt;

&lt;p&gt;Imagine an agent saves an important architectural decision.&lt;/p&gt;

&lt;p&gt;A browser clip accidentally overwrites it.&lt;/p&gt;

&lt;p&gt;Or a user removes an item because it looks old.&lt;/p&gt;

&lt;p&gt;The information may be gone before anyone realizes it was useful.&lt;/p&gt;

&lt;p&gt;A safer approach is to separate:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Editing an existing item.&lt;/li&gt;
&lt;li&gt;Archiving an item.&lt;/li&gt;
&lt;li&gt;Pruning stale knowledge.&lt;/li&gt;
&lt;li&gt;Permanently removing data, if supported through an intentional local workflow.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;This is a design choice, not a universal rule.&lt;/p&gt;

&lt;p&gt;Different tools may reasonably choose different deletion models.&lt;/p&gt;

&lt;p&gt;But the principle is worth considering:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Memory should be managed deliberately, not casually destroyed.&lt;/p&gt;
&lt;/blockquote&gt;




&lt;h1&gt;
  
  
  The browser companion is also a security boundary
&lt;/h1&gt;

&lt;p&gt;The moment a browser extension can write to your project files, security becomes important.&lt;/p&gt;

&lt;p&gt;A browser extension is not the same as a trusted terminal command.&lt;/p&gt;

&lt;p&gt;It runs in a different environment.&lt;/p&gt;

&lt;p&gt;It may interact with content from arbitrary websites.&lt;/p&gt;

&lt;p&gt;It needs a way to communicate with the local Attic server.&lt;/p&gt;

&lt;p&gt;That means the design has to answer:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Which origins can connect?&lt;/li&gt;
&lt;li&gt;Which requests are allowed?&lt;/li&gt;
&lt;li&gt;How is the local service authenticated?&lt;/li&gt;
&lt;li&gt;Where can it write?&lt;/li&gt;
&lt;li&gt;What happens when a user clips sensitive information?&lt;/li&gt;
&lt;li&gt;Can arbitrary websites cause files to be written?&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Attic's design notes identify several of these concerns:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Loopback communication.&lt;/li&gt;
&lt;li&gt;A token.&lt;/li&gt;
&lt;li&gt;A root allowlist.&lt;/li&gt;
&lt;li&gt;Origin checks.&lt;/li&gt;
&lt;li&gt;A credential scan for clips.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;These are important because a browser-to-local-file bridge should not behave like an unrestricted file-writing API.&lt;/p&gt;




&lt;h2&gt;
  
  
  Loopback: Keep communication local
&lt;/h2&gt;

&lt;p&gt;The companion communicates with a local server.&lt;/p&gt;

&lt;p&gt;The goal is to keep the interaction on the developer's machine rather than sending knowledge to a remote cloud service.&lt;/p&gt;

&lt;p&gt;The repository's design notes identify loopback communication as part of the implementation.&lt;/p&gt;

&lt;p&gt;This is aligned with the project's broader local-first approach.&lt;/p&gt;

&lt;p&gt;The knowledge is intended to live in the project's &lt;code&gt;.attic/&lt;/code&gt; directory.&lt;/p&gt;




&lt;h2&gt;
  
  
  Token authentication
&lt;/h2&gt;

&lt;p&gt;A local service still needs protection.&lt;/p&gt;

&lt;p&gt;Just because something runs on &lt;code&gt;localhost&lt;/code&gt; doesn't mean every request should be trusted.&lt;/p&gt;

&lt;p&gt;Attic's browser server uses a token as part of its request validation.&lt;/p&gt;

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

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Browser extension
       │
       │ request + token
       ▼
Local Attic server
       │
       ▼
Validate request
       │
       ▼
Write knowledge item
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The exact request format is an implementation detail.&lt;/p&gt;

&lt;p&gt;The important point is that the server isn't designed as an unauthenticated arbitrary file-writing endpoint.&lt;/p&gt;




&lt;h2&gt;
  
  
  Root allowlist: Where can it write?
&lt;/h2&gt;

&lt;p&gt;This is another useful security concept.&lt;/p&gt;

&lt;p&gt;Suppose the browser companion could tell the server:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Write this content to:

/Users/me/anything/on/my/computer
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;That would be dangerous.&lt;/p&gt;

&lt;p&gt;A project memory tool should not be allowed to write to arbitrary locations.&lt;/p&gt;

&lt;p&gt;Attic's design notes identify a root allowlist in the server.&lt;/p&gt;

&lt;p&gt;The intended principle is:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Restrict file writes to approved project roots.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;That limits the scope of what the companion can modify.&lt;/p&gt;




&lt;h2&gt;
  
  
  Credential scanning: Don't stash secrets
&lt;/h2&gt;

&lt;p&gt;This is particularly important for browser clipping.&lt;/p&gt;

&lt;p&gt;Imagine you are reading a page containing:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;API_KEY=super-secret-value
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;You click "Clip."&lt;/p&gt;

&lt;p&gt;Should that secret be saved to your project's memory?&lt;/p&gt;

&lt;p&gt;No.&lt;/p&gt;

&lt;p&gt;Attic's design notes identify a credential scan that refuses a clip containing a credential.&lt;/p&gt;

&lt;p&gt;This is a practical safeguard.&lt;/p&gt;

&lt;p&gt;But it doesn't mean every secret will be detected.&lt;/p&gt;

&lt;p&gt;You should still avoid clipping:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;API keys.&lt;/li&gt;
&lt;li&gt;Passwords.&lt;/li&gt;
&lt;li&gt;Session tokens.&lt;/li&gt;
&lt;li&gt;Private access tokens.&lt;/li&gt;
&lt;li&gt;Credentials.&lt;/li&gt;
&lt;li&gt;Personal secrets.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;A browser extension that can save content needs to treat sensitive information as a first-class concern.&lt;/p&gt;




&lt;h1&gt;
  
  
  The local-first philosophy
&lt;/h1&gt;

&lt;p&gt;One of the things I like about Attic is that its design stays close to ordinary developer tools.&lt;/p&gt;

&lt;p&gt;The knowledge is stored as Markdown.&lt;/p&gt;

&lt;p&gt;The project can inspect it.&lt;/p&gt;

&lt;p&gt;The files can be version-controlled.&lt;/p&gt;

&lt;p&gt;And the runtime is designed around local filesystem operations rather than a cloud memory service.&lt;/p&gt;

&lt;p&gt;The repository describes the runtime as using Node.js &lt;code&gt;fs&lt;/code&gt; and &lt;code&gt;path&lt;/code&gt;, with no network calls in the plugin runtime.&lt;/p&gt;

&lt;p&gt;That gives the project a straightforward mental model:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Your project
    │
    ├── Source code
    ├── Documentation
    ├── Tests
    └── .attic/
         ├── INDEX.md
         ├── DECISIONS.md
         └── items/
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Your agent's memory is another part of the repository.&lt;/p&gt;

&lt;p&gt;Not a mysterious remote database.&lt;/p&gt;

&lt;p&gt;Not a separate SaaS dashboard.&lt;/p&gt;

&lt;p&gt;Not a service you need to query over the internet every time the agent needs to remember something.&lt;/p&gt;




&lt;h2&gt;
  
  
  But should you commit &lt;code&gt;.attic/&lt;/code&gt; to Git?
&lt;/h2&gt;

&lt;p&gt;This is an important practical question.&lt;/p&gt;

&lt;p&gt;If the knowledge lives in your project, it can potentially be shared with the team.&lt;/p&gt;

&lt;p&gt;For example:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Developer A
    │
    ▼
Saves an architectural discovery
    │
    ▼
Commits .attic/
    │
    ▼
Developer B pulls the changes
    │
    ▼
Agent can recall the discovery
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The repository notes describe &lt;code&gt;.attic/&lt;/code&gt; as surviving a Git clone only if it is committed rather than ignored.&lt;/p&gt;

&lt;p&gt;That distinction matters.&lt;/p&gt;

&lt;p&gt;If you want shared project memory, you need to decide:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Should the knowledge be committed?&lt;/li&gt;
&lt;li&gt;Should some findings remain local?&lt;/li&gt;
&lt;li&gt;How should stale findings be managed?&lt;/li&gt;
&lt;li&gt;How should conflicting changes be merged?&lt;/li&gt;
&lt;li&gt;What information is appropriate for the whole team?&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;For a team project, knowledge management becomes part of the repository workflow.&lt;/p&gt;




&lt;h1&gt;
  
  
  A complete example: From bug investigation to reusable knowledge
&lt;/h1&gt;

&lt;p&gt;Let's walk through a realistic workflow.&lt;/p&gt;

&lt;h2&gt;
  
  
  Step 1: Start a new investigation
&lt;/h2&gt;

&lt;p&gt;You ask Claude Code:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;"Investigate why the desktop sub-window sometimes appears before authentication."&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;The agent searches the codebase.&lt;/p&gt;

&lt;p&gt;It discovers:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;packages/desktop/window.js
packages/auth/gate.js
packages/desktop/bootstrap.js
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;It traces the execution path.&lt;/p&gt;

&lt;h2&gt;
  
  
  Step 2: The agent finds the root cause
&lt;/h2&gt;

&lt;p&gt;The agent explains:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;"The window creation call happens before the authentication callback completes."&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;You fix the issue.&lt;/p&gt;

&lt;h2&gt;
  
  
  Step 3: Save the discovery
&lt;/h2&gt;

&lt;p&gt;You stash the important knowledge:&lt;br&gt;
&lt;/p&gt;

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

&lt;/div&gt;



&lt;p&gt;The saved item contains something like:&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;# Desktop window authentication lifecycle&lt;/span&gt;

The desktop window creation path can run
before the authentication callback completes.

When investigating window lifecycle issues,
check the auth gate before window creation.

Relevant areas:
&lt;span class="p"&gt;-&lt;/span&gt; packages/desktop/window.js
&lt;span class="p"&gt;-&lt;/span&gt; packages/auth/gate.js
&lt;span class="p"&gt;-&lt;/span&gt; packages/desktop/bootstrap.js

Tags:
&lt;span class="p"&gt;-&lt;/span&gt; desktop
&lt;span class="p"&gt;-&lt;/span&gt; authentication
&lt;span class="p"&gt;-&lt;/span&gt; lifecycle
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Step 4: Start a new session
&lt;/h2&gt;

&lt;p&gt;A few days later:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;"Why is the desktop window lifecycle failing?"&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;The agent can inspect the Attic index and recall relevant knowledge.&lt;/p&gt;

&lt;p&gt;Instead of immediately rediscovering the entire execution path, it has a useful starting point.&lt;/p&gt;

&lt;h2&gt;
  
  
  Step 5: Update the knowledge after the fix
&lt;/h2&gt;

&lt;p&gt;You discover a more precise explanation.&lt;/p&gt;

&lt;p&gt;You edit the existing item.&lt;/p&gt;

&lt;p&gt;Now the knowledge stays current.&lt;/p&gt;

&lt;p&gt;This is the lifecycle that matters:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Investigate
    ↓
Discover
    ↓
Stash
    ↓
Recall
    ↓
Update
    ↓
Reuse
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;It's not magic.&lt;/p&gt;

&lt;p&gt;It's a disciplined way to make discoveries persistent.&lt;/p&gt;




&lt;h1&gt;
  
  
  What Attic does not try to do
&lt;/h1&gt;

&lt;p&gt;It's worth being clear about the scope.&lt;/p&gt;

&lt;p&gt;Attic is not a replacement for:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Your source code.&lt;/li&gt;
&lt;li&gt;Your Git history.&lt;/li&gt;
&lt;li&gt;Your issue tracker.&lt;/li&gt;
&lt;li&gt;Your documentation.&lt;/li&gt;
&lt;li&gt;Your test suite.&lt;/li&gt;
&lt;li&gt;Your own engineering judgment.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;It's a knowledge persistence layer for coding-agent workflows.&lt;/p&gt;

&lt;p&gt;It helps preserve discoveries and make them available again.&lt;/p&gt;

&lt;p&gt;It doesn't guarantee that the assistant will always recall the right item.&lt;/p&gt;

&lt;p&gt;It doesn't guarantee that every saved finding is correct.&lt;/p&gt;

&lt;p&gt;It doesn't eliminate the need to verify facts.&lt;/p&gt;

&lt;p&gt;And it doesn't mean the agent should blindly trust old notes.&lt;/p&gt;

&lt;p&gt;A stale memory can be just as dangerous as no memory.&lt;/p&gt;

&lt;p&gt;That's why editing, pruning, and archive workflows matter.&lt;/p&gt;




&lt;h1&gt;
  
  
  The bigger idea: Agents need a knowledge lifecycle
&lt;/h1&gt;

&lt;p&gt;When people talk about AI coding assistants, they often focus on:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Better models.&lt;/li&gt;
&lt;li&gt;Larger context windows.&lt;/li&gt;
&lt;li&gt;Faster inference.&lt;/li&gt;
&lt;li&gt;More tool calls.&lt;/li&gt;
&lt;li&gt;Better code generation.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Those things matter.&lt;/p&gt;

&lt;p&gt;But there is another problem:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;What happens to the useful knowledge after the task ends?&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;A coding agent might discover:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;The API client is initialized in a shared module.
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;That can be valuable.&lt;/p&gt;

&lt;p&gt;But if it exists only in the chat, the next session may have to rediscover it.&lt;/p&gt;

&lt;p&gt;The same is true for:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Why a dependency was chosen.&lt;/li&gt;
&lt;li&gt;Why a workaround exists.&lt;/li&gt;
&lt;li&gt;Why a particular file is important.&lt;/li&gt;
&lt;li&gt;Which approach failed.&lt;/li&gt;
&lt;li&gt;What assumption caused a bug.&lt;/li&gt;
&lt;li&gt;What the team should avoid repeating.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;These are not necessarily code changes.&lt;/p&gt;

&lt;p&gt;They are discoveries about the codebase.&lt;/p&gt;

&lt;p&gt;And they deserve a place to live.&lt;/p&gt;




&lt;h1&gt;
  
  
  Why browser memory is a natural next step
&lt;/h1&gt;

&lt;p&gt;The browser extension expands the idea beyond terminal-based discovery.&lt;/p&gt;

&lt;p&gt;Developers constantly learn from external information.&lt;/p&gt;

&lt;p&gt;They read a blog post.&lt;/p&gt;

&lt;p&gt;Find a GitHub issue.&lt;/p&gt;

&lt;p&gt;Understand a framework quirk.&lt;/p&gt;

&lt;p&gt;Discover a workaround.&lt;/p&gt;

&lt;p&gt;Read a discussion about a bug.&lt;/p&gt;

&lt;p&gt;That knowledge is useful even if the AI agent didn't discover it itself.&lt;/p&gt;

&lt;p&gt;The browser companion makes it possible to capture useful information and bring it into the same local knowledge system.&lt;/p&gt;

&lt;p&gt;This creates a broader workflow:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;             ┌───────────────┐
             │   Terminal    │
             │ Claude/Codex  │
             └───────┬───────┘
                     │
                     ▼
              ┌────────────┐
              │   Attic    │
              │  Knowledge │
              └────────────┘
                     ▲
                     │
             ┌───────┴───────┐
             │               │
      ┌──────┴──────┐ ┌──────┴──────┐
      │ CLI stash   │ │ Browser     │
      │             │ │ clip        │
      └─────────────┘ └─────────────┘
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;One knowledge shelf.&lt;/p&gt;

&lt;p&gt;Different ways to add information.&lt;/p&gt;

&lt;p&gt;The same place to recall it.&lt;/p&gt;




&lt;h1&gt;
  
  
  Getting started
&lt;/h1&gt;

&lt;p&gt;You can explore Attic here:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;GitHub:&lt;/strong&gt; &lt;a href="https://github.com/NishikantaRay/Attic" rel="noopener noreferrer"&gt;https://github.com/NishikantaRay/Attic&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;The project is designed for Claude Code and Codex CLI.&lt;/p&gt;

&lt;p&gt;A typical workflow is:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;1. Install Attic
2. Initialize it in your project
3. Investigate a problem
4. Stash useful discoveries
5. Recall them in later sessions
6. Manage the knowledge as it evolves
7. Use the browser companion to capture useful research
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;For the exact installation and usage commands, follow the repository's current README and release documentation.&lt;/p&gt;




&lt;h1&gt;
  
  
  Final thoughts
&lt;/h1&gt;

&lt;p&gt;I think the most interesting thing about AI coding assistants isn't just how much code they can write.&lt;/p&gt;

&lt;p&gt;It's how much they can discover.&lt;/p&gt;

&lt;p&gt;An agent can investigate a codebase for ten minutes and uncover something that would take a developer much longer to understand.&lt;/p&gt;

&lt;p&gt;But if that discovery disappears when the session ends, you lose part of the value.&lt;/p&gt;

&lt;p&gt;Attic is built around a simple idea:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Don't make your AI coding assistant rediscover what it already learned.&lt;/strong&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Write the discovery to a file.&lt;/p&gt;

&lt;p&gt;Keep a compact index.&lt;/p&gt;

&lt;p&gt;Recall the details when needed.&lt;/p&gt;

&lt;p&gt;And now, when useful knowledge comes from your browser, clip it into the same shelf.&lt;/p&gt;

&lt;p&gt;The future of AI coding assistants may not just be about giving agents more context.&lt;/p&gt;

&lt;p&gt;It may also be about giving them better ways to remember.&lt;/p&gt;




&lt;h2&gt;
  
  
  Try Attic
&lt;/h2&gt;

&lt;p&gt;If you use Claude Code or Codex CLI and you've ever thought:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;"I know we already solved this. Why is the agent investigating it again?"&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Attic is worth exploring.&lt;/p&gt;

&lt;p&gt;GitHub: &lt;a href="https://github.com/NishikantaRay/Attic" rel="noopener noreferrer"&gt;https://github.com/NishikantaRay/Attic&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;If you try it, I'd be interested to hear how you manage your agent's knowledge.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;What would you want your coding assistant to remember permanently?&lt;/strong&gt;&lt;/p&gt;




&lt;h1&gt;
  
  
  Tags
&lt;/h1&gt;

&lt;h1&gt;
  
  
  ai #claudecode #codex #opensource #developer-tools #codingagents #artificialintelligence #productivity
&lt;/h1&gt;

</description>
      <category>ai</category>
      <category>claude</category>
      <category>tooling</category>
      <category>programming</category>
    </item>
    <item>
      <title>AI Skills Are Not Just Prompts: A Practical Architecture for Building, Evaluating, Shipping, and Maintaining Agent Skills</title>
      <dc:creator>Nishikanta Ray</dc:creator>
      <pubDate>Thu, 03 Sep 2026 19:13:59 +0000</pubDate>
      <link>https://dev.to/nishikantaray/ai-skills-are-not-just-prompts-a-practical-architecture-for-building-evaluating-shipping-and-540h</link>
      <guid>https://dev.to/nishikantaray/ai-skills-are-not-just-prompts-a-practical-architecture-for-building-evaluating-shipping-and-540h</guid>
      <description>&lt;p&gt;The current generation of AI coding agents makes it surprisingly easy to create a "skill."&lt;/p&gt;

&lt;p&gt;Write a Markdown file. Add instructions. Give it a name. Put it inside &lt;code&gt;.claude/skills/&lt;/code&gt;. Done.&lt;/p&gt;

&lt;p&gt;Except it isn't.&lt;/p&gt;

&lt;p&gt;As soon as you build more than a handful of skills, a different set of problems appears:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Which skill should activate?&lt;/li&gt;
&lt;li&gt;Why did two skills activate at the same time?&lt;/li&gt;
&lt;li&gt;Why did the agent ignore an important instruction?&lt;/li&gt;
&lt;li&gt;What belongs in a skill versus a rule, agent, hook, or script?&lt;/li&gt;
&lt;li&gt;How much context should the skill load?&lt;/li&gt;
&lt;li&gt;How do you test whether the skill actually works?&lt;/li&gt;
&lt;li&gt;What happens when the underlying framework changes?&lt;/li&gt;
&lt;li&gt;How do you distribute skills?&lt;/li&gt;
&lt;li&gt;How do you retire obsolete skills?&lt;/li&gt;
&lt;li&gt;How do you prevent dozens of skills from becoming an unmaintainable mess?&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;At that point, &lt;strong&gt;AI skills stop looking like prompts and start looking like software systems&lt;/strong&gt;.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;A production-grade AI skill is not merely a Markdown prompt. It is a versioned, testable, routable, enforceable software component with a defined lifecycle.&lt;/strong&gt;&lt;/p&gt;
&lt;/blockquote&gt;




&lt;h2&gt;
  
  
  1. The Skill Lifecycle
&lt;/h2&gt;

&lt;p&gt;A useful way to understand an AI skill is to look at its entire lifecycle:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Runtime
   ↓
Scope / Fit
   ↓
Triggers
   ↓
Architecture
   ↓
Anatomy
   ↓
Content
   ↓
Enforcement
   ↓
Measurement
   ↓
Shipping
   ↓
Maintenance
   ↓
Portfolio
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Each stage answers a different question:&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Stage&lt;/th&gt;
&lt;th&gt;Core question&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Runtime&lt;/td&gt;
&lt;td&gt;How does the skill load and execute?&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Fit &amp;amp; scope&lt;/td&gt;
&lt;td&gt;Should this be a skill at all?&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Typing &amp;amp; triggers&lt;/td&gt;
&lt;td&gt;When should it activate?&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Architecture&lt;/td&gt;
&lt;td&gt;How should the workflow operate?&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Anatomy&lt;/td&gt;
&lt;td&gt;What files make up the skill?&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Content&lt;/td&gt;
&lt;td&gt;How should instructions be written?&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Enforcement&lt;/td&gt;
&lt;td&gt;What can be enforced mechanically?&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Measurement&lt;/td&gt;
&lt;td&gt;How do we know it works?&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Shipping&lt;/td&gt;
&lt;td&gt;How do users receive it?&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Maintenance&lt;/td&gt;
&lt;td&gt;How does it survive change?&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Portfolio&lt;/td&gt;
&lt;td&gt;How do many skills coexist?&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;The important insight is that &lt;strong&gt;skill engineering covers the entire lifecycle, not just the writing of &lt;code&gt;SKILL.md&lt;/code&gt;.&lt;/strong&gt;&lt;/p&gt;




&lt;h2&gt;
  
  
  2. Chapter 00 — How Skills Load and Run
&lt;/h2&gt;

&lt;p&gt;Before designing a skill, understand the runtime.&lt;/p&gt;

&lt;p&gt;A skill may look simple on disk:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;.claude/
└── skills/
    └── code-review/
        └── SKILL.md
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;But conceptually the runtime does something like:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;User request
     ↓
Skill discovery
     ↓
Trigger evaluation
     ↓
Skill activation
     ↓
Instruction loading
     ↓
Reference/tool loading
     ↓
Agent execution
     ↓
Result
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The important question is:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;What does the agent actually see, and when does it see it?&lt;/strong&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;This matters because context is limited.&lt;/p&gt;

&lt;p&gt;A skill might contain:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;SKILL.md
references/
    database.md
    security.md
    examples.md
    architecture.md
scripts/
    validate.js
    check.sh
templates/
    report.md
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;You usually don't want every file loaded for every request.&lt;/p&gt;

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

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Request
   ↓
SKILL.md
   ↓
Determine relevant task
   ↓
Load relevant reference
   ↓
Execute required script
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This is &lt;strong&gt;progressive context loading&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;The main skill file acts as an entry point rather than a giant knowledge dump.&lt;/p&gt;

&lt;h3&gt;
  
  
  Context Is a Resource
&lt;/h3&gt;

&lt;p&gt;One of the biggest mistakes in AI skill design is treating context as free.&lt;/p&gt;

&lt;p&gt;It isn't.&lt;/p&gt;

&lt;h4&gt;
  
  
  Design A — Everything in one file
&lt;/h4&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;SKILL.md
──────────────
3000 lines
50 examples
20 rules
10 workflows
15 edge cases
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h4&gt;
  
  
  Design B — Layered knowledge
&lt;/h4&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;SKILL.md
   ↓
Routing
   ↓
references/
   ├── workflow.md
   ├── security.md
   └── examples.md
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The second design gives you more control over what enters the model's context.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Design skills around context boundaries, not just file boundaries.&lt;/strong&gt;&lt;/p&gt;
&lt;/blockquote&gt;




&lt;h2&gt;
  
  
  3. Chapter 01 — Fit and Scope
&lt;/h2&gt;

&lt;p&gt;The next question is:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Should this behavior be implemented as a skill?&lt;/strong&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;AI development environments often provide multiple primitives:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Skill
Rule
Agent
Hook
Script
Plugin
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;They are not interchangeable.&lt;/p&gt;

&lt;h3&gt;
  
  
  Skill
&lt;/h3&gt;

&lt;p&gt;A skill describes &lt;strong&gt;how to perform a class of task&lt;/strong&gt;.&lt;/p&gt;

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

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

&lt;/div&gt;



&lt;p&gt;It might define:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;1. Inspect schema
2. Inspect migration history
3. Design migration
4. Implement migration
5. Validate migration
6. Test rollback
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Rule
&lt;/h3&gt;

&lt;p&gt;A rule is a constraint:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Never modify production data directly.
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;That is policy, not a workflow.&lt;/p&gt;

&lt;h3&gt;
  
  
  Agent
&lt;/h3&gt;

&lt;p&gt;An agent is useful when you need a distinct reasoning or execution role:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Main Agent
   ├── Research Agent
   ├── Security Agent
   └── Testing Agent
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Hook
&lt;/h3&gt;

&lt;p&gt;A hook responds to an event:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Before tool call
      ↓
Security check
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



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

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;After file modification
      ↓
Formatter
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Script
&lt;/h3&gt;

&lt;p&gt;A script performs deterministic computation:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;validate-schema.js
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The AI may decide &lt;strong&gt;when&lt;/strong&gt; to run it. The script determines &lt;strong&gt;how&lt;/strong&gt; validation works.&lt;/p&gt;




&lt;h2&gt;
  
  
  4. The Boundary Is More Important Than the Skill
&lt;/h2&gt;

&lt;p&gt;A good skill should explicitly state what it does not do.&lt;/p&gt;

&lt;p&gt;For example:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight markdown"&gt;&lt;code&gt;This skill handles:
&lt;span class="p"&gt;
-&lt;/span&gt; PostgreSQL schema migrations
&lt;span class="p"&gt;-&lt;/span&gt; Migration generation
&lt;span class="p"&gt;-&lt;/span&gt; Migration validation
&lt;span class="p"&gt;-&lt;/span&gt; Rollback testing

This skill does not handle:
&lt;span class="p"&gt;
-&lt;/span&gt; Application architecture
&lt;span class="p"&gt;-&lt;/span&gt; Production deployment
&lt;span class="p"&gt;-&lt;/span&gt; Database backups
&lt;span class="p"&gt;-&lt;/span&gt; Infrastructure provisioning
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Why?&lt;/p&gt;

&lt;p&gt;Because skills tend to grow.&lt;/p&gt;

&lt;p&gt;A developer starts with:&lt;br&gt;
&lt;/p&gt;

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

&lt;/div&gt;



&lt;p&gt;Then adds:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;database design
query optimization
backup strategy
production deployment
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Eventually the skill becomes:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;database-engineering-everything
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;and becomes difficult to reason about.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Scope is a defense against skill inflation.&lt;/strong&gt;&lt;/p&gt;
&lt;/blockquote&gt;




&lt;h2&gt;
  
  
  5. Chapter 02 — Typing and Triggers
&lt;/h2&gt;

&lt;p&gt;Once you know what a skill does, you need to answer:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;When should it activate?&lt;/strong&gt;&lt;/p&gt;
&lt;/blockquote&gt;

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

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;frontend-review
backend-review
security-review
performance-review
database-review
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The user says:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;"Review my authentication API."&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Multiple skills may be relevant.&lt;/p&gt;

&lt;p&gt;You now have a routing problem:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;                 User request
                      │
        ┌─────────────┼─────────────┐
        ↓             ↓             ↓
    Backend       Security       Performance
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;If everything activates, the system becomes noisy.&lt;/p&gt;

&lt;p&gt;If nothing activates, the skill is useless.&lt;/p&gt;

&lt;h3&gt;
  
  
  Activation Is a Classification Problem
&lt;/h3&gt;

&lt;p&gt;Think of activation as:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;User request
      ↓
Intent classification
      ↓
Skill candidates
      ↓
Relevance evaluation
      ↓
Activation
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Example:&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Request&lt;/th&gt;
&lt;th&gt;Expected skill&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;"Create a PostgreSQL migration"&lt;/td&gt;
&lt;td&gt;Database migration&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;"Fix this React component"&lt;/td&gt;
&lt;td&gt;Frontend&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;"Audit authentication"&lt;/td&gt;
&lt;td&gt;Security&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;"Why is this query slow?"&lt;/td&gt;
&lt;td&gt;Database performance&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;"Write a technical article"&lt;/td&gt;
&lt;td&gt;Documentation&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;A good skill therefore needs a clear &lt;strong&gt;activation signature&lt;/strong&gt;.&lt;/p&gt;

&lt;h3&gt;
  
  
  False Positives and False Negatives
&lt;/h3&gt;

&lt;h4&gt;
  
  
  False positive
&lt;/h4&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;"Write a blog about PostgreSQL"

→ security skill activates
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h4&gt;
  
  
  False negative
&lt;/h4&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;"Why is my PostgreSQL migration failing?"

→ database migration skill does not activate
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Both are important.&lt;/p&gt;

&lt;p&gt;Therefore, evaluate &lt;strong&gt;activation separately from execution&lt;/strong&gt;.&lt;/p&gt;




&lt;h2&gt;
  
  
  6. Chapter 03 — Shape and Architecture
&lt;/h2&gt;

&lt;p&gt;Once a skill activates, what does it actually do?&lt;/p&gt;

&lt;p&gt;There are several useful workflow shapes.&lt;/p&gt;

&lt;h3&gt;
  
  
  Route
&lt;/h3&gt;

&lt;p&gt;A route chooses a path:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;                 Request
                    ↓
                 classify
              /     |                   /      |                  Bug    Feature   Refactor
            ↓        ↓         ↓
         Debug    Implement  Refactor
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Pipeline
&lt;/h3&gt;

&lt;p&gt;A pipeline is sequential:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Research
   ↓
Analyze
   ↓
Plan
   ↓
Implement
   ↓
Test
   ↓
Review
   ↓
Report
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This is particularly useful for engineering skills.&lt;/p&gt;

&lt;h3&gt;
  
  
  Loop
&lt;/h3&gt;

&lt;p&gt;A loop supports iteration:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Implement
    ↓
Test
    ↓
Failed?
  /    Yes     No
 ↓       ↓
Fix    Complete
 ↓
Test again
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;For coding agents, this is natural:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Write implementation
        ↓
Run tests
        ↓
Read failure
        ↓
Modify implementation
        ↓
Run tests again
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Failure becomes an expected workflow state rather than an exceptional event.&lt;/p&gt;

&lt;h3&gt;
  
  
  Map
&lt;/h3&gt;

&lt;p&gt;A map splits a problem into independent pieces:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;                 Research repository
                        │
          ┌─────────────┼─────────────┐
          ↓             ↓             ↓
       Frontend      Backend       Database
          ↓             ↓             ↓
       Findings      Findings      Findings
          └─────────────┼─────────────┘
                        ↓
                     Synthesis
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Combining Shapes
&lt;/h3&gt;

&lt;p&gt;Real skills often combine these patterns:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Route
  ↓
Pipeline
  ↓
Map
  ↓
Loop
  ↓
Final report
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This is where AI skills start looking like &lt;strong&gt;workflow engines rather than prompts&lt;/strong&gt;.&lt;/p&gt;




&lt;h2&gt;
  
  
  7. Chapter 04 — Anatomy
&lt;/h2&gt;

&lt;p&gt;A mature skill might look like:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;skills/
└── deep-research/
    │
    ├── SKILL.md
    │
    ├── references/
    │   ├── research-methodology.md
    │   ├── source-evaluation.md
    │   └── evidence.md
    │
    ├── scripts/
    │   ├── validate-sources.js
    │   └── generate-report.js
    │
    ├── templates/
    │   └── report.md
    │
    └── examples/
        ├── example-1.md
        └── example-2.md
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Each part has a job.&lt;/p&gt;

&lt;h3&gt;
  
  
  &lt;code&gt;SKILL.md&lt;/code&gt; Is the Hub
&lt;/h3&gt;

&lt;p&gt;Think of &lt;code&gt;SKILL.md&lt;/code&gt; as the router and operating manual.&lt;/p&gt;

&lt;p&gt;It should explain:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;What this skill does
When it activates
What it must accomplish
What references to load
What workflow to follow
What constraints apply
How to validate the result
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;It should not necessarily contain every piece of knowledge.&lt;/p&gt;

&lt;h3&gt;
  
  
  Hub-and-Spoke Architecture
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;                   SKILL.md
                  /   |                    /    |                    ↓     ↓     ↓
          References Scripts Templates
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The hub provides orchestration.&lt;/p&gt;

&lt;p&gt;The spokes provide specialized resources.&lt;/p&gt;

&lt;h3&gt;
  
  
  Data vs Instructions vs Code
&lt;/h3&gt;

&lt;p&gt;Separate them:&lt;/p&gt;

&lt;h4&gt;
  
  
  Instructions
&lt;/h4&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;SKILL.md
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Tell the agent what to do.&lt;/p&gt;

&lt;h4&gt;
  
  
  Data
&lt;/h4&gt;



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

&lt;/div&gt;



&lt;p&gt;Provide knowledge.&lt;/p&gt;

&lt;h4&gt;
  
  
  Code
&lt;/h4&gt;



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

&lt;/div&gt;



&lt;p&gt;Perform deterministic operations.&lt;/p&gt;

&lt;p&gt;This separation makes skills easier to maintain and test.&lt;/p&gt;




&lt;h2&gt;
  
  
  8. Chapter 05 — Writing the Content
&lt;/h2&gt;

&lt;p&gt;Now we reach the instruction layer.&lt;/p&gt;

&lt;p&gt;Compare:&lt;/p&gt;

&lt;h3&gt;
  
  
  Weak
&lt;/h3&gt;

&lt;blockquote&gt;
&lt;p&gt;You might want to check whether tests pass.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h3&gt;
  
  
  Strong
&lt;/h3&gt;

&lt;blockquote&gt;
&lt;p&gt;Run the test suite before declaring the task complete.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h3&gt;
  
  
  Stronger
&lt;/h3&gt;

&lt;blockquote&gt;
&lt;p&gt;Do not declare the task complete until the test suite passes.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;The difference is &lt;strong&gt;binding strength&lt;/strong&gt;.&lt;/p&gt;

&lt;h3&gt;
  
  
  Instructions Have Different Authority
&lt;/h3&gt;

&lt;h4&gt;
  
  
  Preference
&lt;/h4&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Prefer TypeScript.
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h4&gt;
  
  
  Requirement
&lt;/h4&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Use TypeScript for new files.
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h4&gt;
  
  
  Hard constraint
&lt;/h4&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Do not create JavaScript files. New implementation files must use TypeScript.
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Important rules should be unambiguous.&lt;/p&gt;

&lt;h3&gt;
  
  
  Present-Tense Instructions
&lt;/h3&gt;

&lt;p&gt;Instead of:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;The agent should inspect the repository.
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



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

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Inspect the repository before making changes.
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Instead of:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;The agent will run the tests after implementation.
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



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

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Run the tests after implementation.
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Direct instructions are easier to interpret.&lt;/p&gt;

&lt;h3&gt;
  
  
  Don't Overload the Skill With Philosophy
&lt;/h3&gt;

&lt;p&gt;Avoid thousands of words explaining philosophy.&lt;/p&gt;

&lt;p&gt;Prefer operational instructions:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;1. Inspect X.
2. Determine Y.
3. Run Z.
4. If Z fails, investigate.
5. Do not proceed until Y is verified.
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The skill should be &lt;strong&gt;operational&lt;/strong&gt;.&lt;/p&gt;




&lt;h2&gt;
  
  
  9. Chapter 06 — Enforcement
&lt;/h2&gt;

&lt;p&gt;This is one of the most important ideas.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Don't rely on the model to enforce something that software can enforce.&lt;/strong&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Suppose your skill says:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Always run Prettier.
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The agent might comply.&lt;/p&gt;

&lt;p&gt;But it might also forget.&lt;/p&gt;

&lt;p&gt;A stronger design is:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;AI modifies file
      ↓
Hook
      ↓
Prettier
      ↓
Formatted file
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Formatting no longer depends entirely on model memory.&lt;/p&gt;

&lt;h3&gt;
  
  
  AI Instructions vs Mechanical Enforcement
&lt;/h3&gt;

&lt;p&gt;Consider:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Never commit secrets.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;A prompt can say:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;RULE:
Never commit API keys.
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;But a stronger architecture is:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Agent
  ↓
git commit
  ↓
secret scanner
  ↓
Secret found?
  ├── Yes → Block commit
  └── No  → Continue
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Enforcement Hierarchy
&lt;/h3&gt;

&lt;p&gt;A useful model is:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Human judgment
      ↓
AI instruction
      ↓
Automated validation
      ↓
Mechanical enforcement
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The further down the stack you go, the less you rely on model compliance.&lt;/p&gt;

&lt;p&gt;Examples of good candidates for automation:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Formatting
Linting
Type checking
Schema validation
Tests
Secret detection
File naming
Generated artifacts
SQL safety
Permission boundaries
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The AI should focus on tasks requiring judgment.&lt;/p&gt;




&lt;h2&gt;
  
  
  10. Chapter 07 — Measurement
&lt;/h2&gt;

&lt;p&gt;Now ask:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;How do we know the skill works?&lt;/strong&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;A skill should ideally be evaluated, not merely read and trusted.&lt;/p&gt;

&lt;p&gt;There are two major dimensions.&lt;/p&gt;

&lt;h3&gt;
  
  
  Activation Evaluation
&lt;/h3&gt;

&lt;p&gt;Did the correct skill activate?&lt;/p&gt;

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

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Prompt:
"Create a PostgreSQL migration for the users table."

Expected:
database-migration → activated
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;You can maintain an evaluation set:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;┌────────────────────────────────────┐
│ Activation Evaluation              │
├────────────────────────────────────┤
│ Prompt                             │
│ Expected skill                     │
│ Should activate?                   │
│ Actual skill                       │
│ Result                             │
└────────────────────────────────────┘
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Behavior Evaluation
&lt;/h3&gt;

&lt;p&gt;Once activated, did the skill behave correctly?&lt;/p&gt;

&lt;p&gt;Suppose the migration skill requires:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Inspect schema
Check migration history
Create migration
Validate SQL
Test migration
Test rollback
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Behavior evaluation checks those requirements.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;                    Skill
                      ↓
             ┌────────┴────────┐
             ↓                 ↓
       Activation          Behavior
             ↓                 ↓
        Correct skill?    Correct workflow?
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;A skill could have:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;90% activation accuracy
40% behavior accuracy
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;and still be a poor skill.&lt;/p&gt;

&lt;h3&gt;
  
  
  Measuring Skills Like Software
&lt;/h3&gt;

&lt;p&gt;Useful metrics include:&lt;/p&gt;

&lt;h4&gt;
  
  
  Activation accuracy
&lt;/h4&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;correct activations / total activation tests
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h4&gt;
  
  
  False positive rate
&lt;/h4&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;incorrect activations / total tests
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h4&gt;
  
  
  False negative rate
&lt;/h4&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;missed activations / applicable tests
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h4&gt;
  
  
  Task success rate
&lt;/h4&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;successful executions / total executions
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h4&gt;
  
  
  Rule compliance
&lt;/h4&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;required behaviors satisfied / required behaviors
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h4&gt;
  
  
  Regression rate
&lt;/h4&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;previously passing cases now failing
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This turns skill development into an engineering discipline.&lt;/p&gt;




&lt;h2&gt;
  
  
  11. Auditing Against Prior Art
&lt;/h2&gt;

&lt;p&gt;Another useful activity is comparing your skill against existing approaches.&lt;/p&gt;

&lt;p&gt;Suppose you create:&lt;br&gt;
&lt;/p&gt;

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

&lt;/div&gt;



&lt;p&gt;Before declaring it complete, 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 existing research workflows already exist?

What evaluation techniques do they use?

What source-quality rules are common?

What am I missing?

Which practices are unnecessarily complicated?
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This is &lt;strong&gt;prior-art auditing&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;You don't need to reinvent every workflow.&lt;/p&gt;




&lt;h2&gt;
  
  
  12. Chapter 08 — Shipping
&lt;/h2&gt;

&lt;p&gt;A skill isn't useful if nobody can install it.&lt;/p&gt;

&lt;p&gt;A typical flow is:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Development
    ↓
Repository
    ↓
Package / Plugin
    ↓
Distribution
    ↓
Installation
    ↓
Skill available
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Plugin Binding
&lt;/h3&gt;

&lt;p&gt;A plugin can act as a distribution container:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Plugin
│
├── Skills
│   ├── research
│   ├── testing
│   └── code-review
│
├── Hooks
├── Commands
└── Configuration
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This lets users install a cohesive collection rather than manually copying individual files.&lt;/p&gt;

&lt;h3&gt;
  
  
  Versioning
&lt;/h3&gt;

&lt;p&gt;Once users depend on a skill, versioning matters:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;research-skill@1.0.0
research-skill@1.1.0
research-skill@2.0.0
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



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

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

&lt;/div&gt;



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

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;"must use X"
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;can change agent behavior significantly.&lt;/p&gt;

&lt;p&gt;Therefore skill versions should be treated as meaningful behavioral versions.&lt;/p&gt;

&lt;h3&gt;
  
  
  Dormancy
&lt;/h3&gt;

&lt;p&gt;A skill may sit unused for months.&lt;/p&gt;

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

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Developer
   ↓
invokes old skill
   ↓
framework changed
   ↓
skill behaves differently
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This is &lt;strong&gt;skill dormancy&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;Skills need lifecycle states and maintenance expectations.&lt;/p&gt;




&lt;h2&gt;
  
  
  13. Chapter 09 — Maintenance
&lt;/h2&gt;

&lt;p&gt;AI skills exist inside rapidly changing ecosystems.&lt;/p&gt;

&lt;p&gt;Things change:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;AI models
Agent runtimes
APIs
CLI tools
Frameworks
Repository structures
Tool interfaces
Best practices
Security requirements
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Therefore:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;A skill is software, and software drifts.&lt;/strong&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h3&gt;
  
  
  Drift
&lt;/h3&gt;

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

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Tool v1
 ↓
Skill v1
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



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

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Tool v2
 ↓
behavior changed
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Your skill still assumes the old behavior.&lt;/p&gt;

&lt;p&gt;That is drift.&lt;/p&gt;

&lt;h3&gt;
  
  
  Vendored Knowledge
&lt;/h3&gt;

&lt;p&gt;Suppose your skill contains a copy of external documentation:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;external methodology
       ↓
copy
       ↓
your skill
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The external source changes.&lt;/p&gt;

&lt;p&gt;Your copy doesn't.&lt;/p&gt;

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

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Upstream
   ↓
Version 3

Your skill
   ↓
Version 1
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;You have a maintenance obligation.&lt;/p&gt;

&lt;h3&gt;
  
  
  Review Gate
&lt;/h3&gt;

&lt;p&gt;A good update flow is:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Upstream changes
       ↓
Detect change
       ↓
Review
       ↓
Update skill
       ↓
Run evaluation suite
       ↓
Check regressions
       ↓
Release
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



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

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Upstream changed
       ↓
blindly copy everything
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;






&lt;h2&gt;
  
  
  14. Chapter 10 — The Skill Portfolio
&lt;/h2&gt;

&lt;p&gt;One skill is easy.&lt;/p&gt;

&lt;p&gt;Two are manageable.&lt;/p&gt;

&lt;p&gt;Ten are interesting.&lt;/p&gt;

&lt;p&gt;Fifty become an architecture problem.&lt;/p&gt;

&lt;p&gt;You may eventually have:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;skills/
├── research/
├── frontend/
├── backend/
├── database/
├── security/
├── testing/
├── performance/
├── deployment/
├── documentation/
├── architecture/
├── debugging/
└── code-review/
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Now you need portfolio management.&lt;/p&gt;

&lt;h3&gt;
  
  
  Skill Collision
&lt;/h3&gt;

&lt;p&gt;Suppose the user says:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;"Review my API."&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Potential matches:&lt;br&gt;
&lt;/p&gt;

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

&lt;/div&gt;



&lt;p&gt;Which one activates?&lt;/p&gt;

&lt;p&gt;This is a &lt;strong&gt;collision&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;If multiple skills activate, their instructions may conflict.&lt;/p&gt;

&lt;p&gt;For example:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Skill A:
"Keep the implementation minimal."

Skill B:
"Add extensive validation."

Skill C:
"Refactor the architecture."
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;A skill portfolio therefore needs routing and priority policies.&lt;/p&gt;

&lt;h3&gt;
  
  
  Skill Granularity
&lt;/h3&gt;

&lt;p&gt;How large should a skill be?&lt;/p&gt;

&lt;p&gt;Too broad:&lt;br&gt;
&lt;/p&gt;

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

&lt;/div&gt;



&lt;p&gt;with everything inside it.&lt;/p&gt;

&lt;p&gt;Too narrow:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;read-file
write-file
check-import
check-variable
run-test
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;You don't want hundreds of microscopic skills.&lt;/p&gt;

&lt;p&gt;A better structure might be:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Engineering
│
├── Research
├── Implementation
├── Testing
├── Security
└── Deployment
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Each skill owns a meaningful capability.&lt;/p&gt;

&lt;h3&gt;
  
  
  Router Families
&lt;/h3&gt;

&lt;p&gt;A router can first identify the broad family:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;                  User request
                       ↓
                  Main Router
                       ↓
       ┌───────────────┼───────────────┐
       ↓               ↓               ↓
   Research        Engineering      Operations
       ↓               ↓               ↓
   Research        Backend         Deployment
   skill           Security        Monitoring
                   Testing
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Instead of asking:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;"Which of these 100 skills should run?"&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;you ask:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Which family?
     ↓
Which subcategory?
     ↓
Which skill?
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Retirement
&lt;/h3&gt;

&lt;p&gt;A healthy portfolio needs a retirement policy.&lt;/p&gt;

&lt;p&gt;A skill may become obsolete because:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;the underlying tool disappeared&lt;/li&gt;
&lt;li&gt;the workflow became part of another skill&lt;/li&gt;
&lt;li&gt;the framework changed&lt;/li&gt;
&lt;li&gt;the skill is redundant&lt;/li&gt;
&lt;li&gt;another skill superseded it&lt;/li&gt;
&lt;li&gt;nobody uses it anymore&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;A lifecycle might be:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Experimental
     ↓
Active
     ↓
Stable
     ↓
Deprecated
     ↓
Retired
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This prevents the skill directory from becoming a graveyard.&lt;/p&gt;




&lt;h2&gt;
  
  
  15. The Deeper Architecture: Instructions + Tools + Enforcement + Evaluation
&lt;/h2&gt;

&lt;p&gt;A mature skill can be viewed as four major layers:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;┌──────────────────────────────────────┐
│              SKILL                   │
│                                      │
│  ┌────────────────────────────────┐  │
│  │ Instructions                    │  │
│  │ What the agent should do       │  │
│  └────────────────────────────────┘  │
│                  ↓                   │
│  ┌────────────────────────────────┐  │
│  │ Workflow                       │  │
│  │ Route / Pipeline / Loop / Map │  │
│  └────────────────────────────────┘  │
│                  ↓                   │
│  ┌────────────────────────────────┐  │
│  │ Enforcement                    │  │
│  │ Hooks / Scripts / Tests       │  │
│  └────────────────────────────────┘  │
│                  ↓                   │
│  ┌────────────────────────────────┐  │
│  │ Evaluation                     │  │
│  │ Activation / Behavior / QA    │  │
│  └────────────────────────────────┘  │
└──────────────────────────────────────┘
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This is the important shift in thinking.&lt;/p&gt;

&lt;p&gt;A skill is not merely:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;prompt → answer
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;It is closer to:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;intent
  ↓
routing
  ↓
context
  ↓
workflow
  ↓
tools
  ↓
validation
  ↓
feedback
  ↓
result
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;






&lt;h2&gt;
  
  
  16. Example: Building a Deep Research Skill
&lt;/h2&gt;

&lt;p&gt;Suppose you want:&lt;br&gt;
&lt;/p&gt;

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

&lt;/div&gt;



&lt;p&gt;Its directory could be:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;deep-research/
├── SKILL.md
├── references/
│   ├── research-methodology.md
│   ├── source-quality.md
│   ├── evidence-evaluation.md
│   └── synthesis.md
├── scripts/
│   ├── validate-sources.js
│   └── generate-report.js
├── templates/
│   └── research-report.md
└── evals/
    ├── activation.json
    └── behavior.json
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Step 1 — Define Scope
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;This skill performs structured research.

It handles:
- multi-source research
- source evaluation
- evidence synthesis
- contradiction analysis
- structured reporting

It does not handle:
- software implementation
- deployment
- generic writing
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Step 2 — Define Activation
&lt;/h3&gt;

&lt;p&gt;Potential activation prompts:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;"Research this topic deeply."

"Investigate the current state of..."

"Compare these technologies using external sources."

"Find evidence for and against this claim."
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Non-activation examples:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;"Fix this React bug."

"Run the tests."

"Format this file."
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Step 3 — Define the Workflow
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Understand question
       ↓
Decompose question
       ↓
Identify evidence requirements
       ↓
Search
       ↓
Evaluate sources
       ↓
Extract evidence
       ↓
Cross-check claims
       ↓
Synthesize
       ↓
Write report
       ↓
Validate citations
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Step 4 — Add a Loop
&lt;/h3&gt;

&lt;p&gt;If two sources disagree:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Source A → Claim X
Source B → Claim Y
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



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

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Conflict detected
       ↓
Investigate
       ↓
Find additional sources
       ↓
Re-evaluate evidence
       ↓
Resolve / report uncertainty
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Now the skill combines:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Pipeline + Loop
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Step 5 — Add Mechanical Validation
&lt;/h3&gt;

&lt;p&gt;Instead of only telling the AI:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;"Make sure every claim has a citation."&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;build a validator:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Report
  ↓
Citation validator
  ↓
Missing citation?
  ├── Yes → fail
  └── No  → pass
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Step 6 — Evaluate Activation
&lt;/h3&gt;

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

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Prompt:
"Do a deep investigation into DuckDB vs PostgreSQL for analytics."

Expected:
deep-research → YES
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



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

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Prompt:
"Fix the DuckDB connection bug."

Expected:
deep-research → NO
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Step 7 — Evaluate Behavior
&lt;/h3&gt;

&lt;p&gt;For a research request:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;✓ question decomposition
✓ multiple sources
✓ source quality assessment
✓ evidence extraction
✓ conflicting evidence analysis
✓ synthesis
✓ citations
✓ final report
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Now regressions can be detected.&lt;/p&gt;




&lt;h2&gt;
  
  
  17. Applying This to an AI Engineering Harness
&lt;/h2&gt;

&lt;p&gt;This model becomes especially interesting when building a larger AI development harness.&lt;/p&gt;

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

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;                    AI HARNESS
                        │
                        ↓
                  Intent Router
                        │
        ┌───────────────┼───────────────┐
        ↓               ↓               ↓
     Research       Engineering      Operations
        │               │               │
        ↓               ↓               ↓
     Skills           Skills          Skills
        │               │               │
        └───────────────┼───────────────┘
                        ↓
                     Tools
                        ↓
                  Enforcement
                        ↓
                    Evaluation
                        ↓
                    Reporting
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This is much more powerful than simply having a folder full of Markdown files.&lt;/p&gt;




&lt;h2&gt;
  
  
  18. Skills as a Policy Execution Layer
&lt;/h2&gt;

&lt;p&gt;Traditional software:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Code
 ↓
Execution
 ↓
Result
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;AI software:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Intent
 ↓
Skill
 ↓
Reasoning
 ↓
Tools
 ↓
Result
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;But production AI systems need another layer:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Intent
 ↓
Skill
 ↓
Reasoning
 ↓
Tools
 ↓
Policy
 ↓
Validation
 ↓
Result
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The skill becomes a bridge between &lt;strong&gt;natural-language intent and deterministic engineering systems&lt;/strong&gt;.&lt;/p&gt;




&lt;h2&gt;
  
  
  19. The Most Important Design Principle
&lt;/h2&gt;

&lt;p&gt;If there is one idea to take away from all of this, it is:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Use AI for judgment. Use software for certainty.&lt;/strong&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Let the model handle:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Interpretation
Planning
Hypothesis generation
Trade-offs
Synthesis
Creative reasoning
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Let software handle:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Formatting
Validation
Testing
Schema checking
Permissions
Secret detection
Deterministic calculations
Policy enforcement
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;For example:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;AI:
"These three files probably need to change."

Software:
"Does the resulting code compile?"

AI:
"This migration should be safe."

Software:
"Does the migration actually execute successfully?"

AI:
"These sources support the conclusion."

Software:
"Are the required citations present?"
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;That division produces more reliable systems.&lt;/p&gt;




&lt;h2&gt;
  
  
  20. Why Skill Engineering Will Become Important
&lt;/h2&gt;

&lt;p&gt;As AI agents become more capable, the bottleneck increasingly shifts away from:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;"Can the model write code?"&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;toward:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;"Can we reliably control how the model works?"&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;That is a different engineering problem.&lt;/p&gt;

&lt;p&gt;We need to reason about:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Activation
Context
Permissions
Workflow
Tools
Memory
Policies
Evaluation
Regression
Versioning
Distribution
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;These are systems problems.&lt;/p&gt;

&lt;p&gt;That is why skill engineering starts resembling:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;software architecture
        +
prompt engineering
        +
workflow orchestration
        +
testing
        +
policy enforcement
        +
package management
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;






&lt;h2&gt;
  
  
  21. Practical Checklist for Building a Skill
&lt;/h2&gt;

&lt;h3&gt;
  
  
  Scope
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;[ ] Is this actually a skill?&lt;/li&gt;
&lt;li&gt;[ ] Could this be a rule?&lt;/li&gt;
&lt;li&gt;[ ] Could this be a hook?&lt;/li&gt;
&lt;li&gt;[ ] Could this be a script?&lt;/li&gt;
&lt;li&gt;[ ] What does the skill explicitly refuse to do?&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Activation
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;[ ] When should it activate?&lt;/li&gt;
&lt;li&gt;[ ] When should it not activate?&lt;/li&gt;
&lt;li&gt;[ ] What are the ambiguous prompts?&lt;/li&gt;
&lt;li&gt;[ ] Can it collide with another skill?&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Architecture
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;[ ] Is the workflow a route?&lt;/li&gt;
&lt;li&gt;[ ] Pipeline?&lt;/li&gt;
&lt;li&gt;[ ] Loop?&lt;/li&gt;
&lt;li&gt;[ ] Map?&lt;/li&gt;
&lt;li&gt;[ ] Combination of these?&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Context
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;[ ] Is &lt;code&gt;SKILL.md&lt;/code&gt; concise?&lt;/li&gt;
&lt;li&gt;[ ] Can supporting information be loaded progressively?&lt;/li&gt;
&lt;li&gt;[ ] Are references separated from instructions?&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Enforcement
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;[ ] Which rules can be automated?&lt;/li&gt;
&lt;li&gt;[ ] Which checks should be hooks?&lt;/li&gt;
&lt;li&gt;[ ] Which checks should be scripts?&lt;/li&gt;
&lt;li&gt;[ ] Which requirements belong in CI?&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Evaluation
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;[ ] Does the skill activate correctly?&lt;/li&gt;
&lt;li&gt;[ ] Does it perform the correct workflow?&lt;/li&gt;
&lt;li&gt;[ ] Are there regression tests?&lt;/li&gt;
&lt;li&gt;[ ] Are failure cases tested?&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Shipping
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;[ ] Is the skill versioned?&lt;/li&gt;
&lt;li&gt;[ ] Can another developer install it?&lt;/li&gt;
&lt;li&gt;[ ] Is plugin/package integration defined?&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Maintenance
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;[ ] What external dependencies can drift?&lt;/li&gt;
&lt;li&gt;[ ] Are vendored references tracked?&lt;/li&gt;
&lt;li&gt;[ ] Is there a review process?&lt;/li&gt;
&lt;li&gt;[ ] Can the skill become deprecated?&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Portfolio
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;[ ] Does it overlap with another skill?&lt;/li&gt;
&lt;li&gt;[ ] Is the granularity appropriate?&lt;/li&gt;
&lt;li&gt;[ ] Does the router know where it belongs?&lt;/li&gt;
&lt;li&gt;[ ] What happens when the skill is obsolete?&lt;/li&gt;
&lt;/ul&gt;




&lt;h2&gt;
  
  
  22. Final Architecture
&lt;/h2&gt;

&lt;p&gt;Putting everything together:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;                         USER INTENT
                              │
                              ▼
                     ┌────────────────┐
                     │     ROUTER     │
                     └───────┬────────┘
                             │
                             ▼
                    ┌──────────────────┐
                    │      SKILL       │
                    │                  │
                    │ Scope            │
                    │ Instructions     │
                    │ Workflow         │
                    │ References       │
                    └────────┬─────────┘
                             │
                             ▼
                  ┌─────────────────────┐
                  │   AI REASONING      │
                  └──────────┬──────────┘
                             │
                  ┌──────────┴──────────┐
                  ↓                     ↓
             Tools / APIs          Scripts
                  │                     │
                  └──────────┬──────────┘
                             ↓
                    ┌─────────────────┐
                    │   ENFORCEMENT   │
                    │                 │
                    │ Hooks           │
                    │ Policies        │
                    │ Validators      │
                    └────────┬────────┘
                             ↓
                    ┌─────────────────┐
                    │   EVALUATION    │
                    │                 │
                    │ Activation      │
                    │ Behavior        │
                    │ Regression      │
                    └────────┬────────┘
                             ↓
                         RESULT
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;And around the whole system:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;          ┌───────────────────────────────────┐
          │           SKILL LIFECYCLE         │
          │                                   │
          │ Version → Ship → Observe →       │
          │ Maintain → Update → Deprecate    │
          │                                   │
          └───────────────────────────────────┘
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;






&lt;h2&gt;
  
  
  Conclusion: From Prompt Files to AI Engineering
&lt;/h2&gt;

&lt;p&gt;The simplest way to build an AI skill is:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Write SKILL.md
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The professional way is:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Define scope
     ↓
Define activation
     ↓
Design workflow
     ↓
Structure context
     ↓
Write instructions
     ↓
Add tools
     ↓
Mechanically enforce critical rules
     ↓
Evaluate activation
     ↓
Evaluate behavior
     ↓
Version
     ↓
Ship
     ↓
Monitor drift
     ↓
Maintain
     ↓
Retire when necessary
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;That is the fundamental shift.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;AI skills should be treated less like prompts and more like software components.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;A prompt tells an AI what you would &lt;em&gt;like&lt;/em&gt; it to do.&lt;/p&gt;

&lt;p&gt;A well-engineered skill defines:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;when&lt;/strong&gt; it should act,&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;what&lt;/strong&gt; it should do,&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;what context&lt;/strong&gt; it should consume,&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;how&lt;/strong&gt; it should execute,&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;which rules&lt;/strong&gt; are mandatory,&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;what software can enforce&lt;/strong&gt;,&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;how success is measured&lt;/strong&gt;, and&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;how the component evolves over time&lt;/strong&gt;.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Once you start thinking this way, &lt;code&gt;.claude/skills/&lt;/code&gt; stops being a collection of Markdown files.&lt;/p&gt;

&lt;p&gt;It becomes an &lt;strong&gt;AI-native software architecture layer&lt;/strong&gt;.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;The future of agent engineering is not just better prompts — it is better systems around prompts.&lt;/strong&gt;&lt;/p&gt;
&lt;/blockquote&gt;

</description>
      <category>agents</category>
      <category>ai</category>
      <category>architecture</category>
      <category>softwareengineering</category>
    </item>
    <item>
      <title>Your Computer Is Busy When You're Not: Building a Desktop Observability Tool with Electron</title>
      <dc:creator>Nishikanta Ray</dc:creator>
      <pubDate>Sun, 23 Aug 2026 17:14:54 +0000</pubDate>
      <link>https://dev.to/nishikantaray/your-computer-is-busy-when-youre-not-building-a-desktop-observability-tool-with-electron-1m6k</link>
      <guid>https://dev.to/nishikantaray/your-computer-is-busy-when-youre-not-building-a-desktop-observability-tool-with-electron-1m6k</guid>
      <description>&lt;p&gt;You close your laptop.&lt;/p&gt;

&lt;p&gt;The screen goes dark.&lt;/p&gt;

&lt;p&gt;You assume everything stopped.&lt;/p&gt;

&lt;p&gt;But what if it didn't?&lt;/p&gt;

&lt;p&gt;Maybe Docker is still transferring data.&lt;/p&gt;

&lt;p&gt;Maybe Chrome is still downloading something.&lt;/p&gt;

&lt;p&gt;Maybe an application is consuming CPU in the background.&lt;/p&gt;

&lt;p&gt;Maybe your computer woke up at 3 AM, performed some background work, and went back to sleep.&lt;/p&gt;

&lt;p&gt;Most operating systems already expose pieces of this information.&lt;/p&gt;

&lt;p&gt;The interesting part is putting those pieces together.&lt;/p&gt;

&lt;p&gt;That's what I wanted to explore:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Can we build an observability layer for a desktop computer using Electron?&lt;/strong&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Not another CPU/RAM widget.&lt;/p&gt;

&lt;p&gt;Something that answers:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;What is my computer actually doing?&lt;/strong&gt;&lt;/p&gt;
&lt;/blockquote&gt;




&lt;h2&gt;
  
  
  The Idea
&lt;/h2&gt;

&lt;p&gt;Imagine opening your desktop monitoring application and seeing:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;TODAY
────────────────────────────────────

Computer active        7h 42m
Idle                   1h 13m
Sleep                  8h 05m

Network
↓ Download             6.8 GB
↑ Upload               1.2 GB

Top applications
────────────────────────────────────

VS Code                3h 42m
Chrome                 2h 18m
Docker                 1h 31m
Slack                  52m

Network-heavy apps
────────────────────────────────────

Docker                 2.4 GB
Chrome                 1.8 GB
VS Code                920 MB
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Now imagine clicking &lt;strong&gt;"What happened while I was away?"&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;18:42  User became idle
18:51  Screen locked
18:53  Docker network activity
19:07  Background sync completed
19:14  System entered sleep
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;That's much more interesting than a CPU percentage.&lt;/p&gt;




&lt;h2&gt;
  
  
  The npm Package That Makes This Easier
&lt;/h2&gt;

&lt;p&gt;Instead of writing native system integrations for everything ourselves, we can start with:&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;install &lt;/span&gt;systeminformation
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;code&gt;systeminformation&lt;/code&gt; provides APIs for system information, CPU, memory, battery, filesystem, network, processes, Docker and more. It also supports Linux, macOS and Windows, although individual APIs have platform-specific support.&lt;/p&gt;

&lt;p&gt;For example:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;si&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;require&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;systeminformation&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="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;getSystemStats&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;cpu&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;memory&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;battery&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;network&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="nx"&gt;si&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;currentLoad&lt;/span&gt;&lt;span class="p"&gt;(),&lt;/span&gt;
    &lt;span class="nx"&gt;si&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;mem&lt;/span&gt;&lt;span class="p"&gt;(),&lt;/span&gt;
    &lt;span class="nx"&gt;si&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;battery&lt;/span&gt;&lt;span class="p"&gt;(),&lt;/span&gt;
    &lt;span class="nx"&gt;si&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;networkStats&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="na"&gt;cpu&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;cpu&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;currentLoad&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="na"&gt;memory&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;memory&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;active&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="na"&gt;battery&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;battery&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;percent&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="nx"&gt;network&lt;/span&gt;
  &lt;span class="p"&gt;};&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="nx"&gt;console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;log&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;getSystemStats&lt;/span&gt;&lt;span class="p"&gt;());&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Now our Electron main process can periodically collect system information.&lt;/p&gt;




&lt;h2&gt;
  
  
  Monitoring Network Activity
&lt;/h2&gt;

&lt;p&gt;One of the most useful APIs is:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="nx"&gt;si&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;networkStats&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;It provides network statistics including received/transmitted bytes and calculated per-second rates. The package calculates rates based on differences between successive calls, so the first call doesn't provide a meaningful rate yet.&lt;/p&gt;

&lt;p&gt;A simple monitor:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;si&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;require&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;systeminformation&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

&lt;span class="nf"&gt;setInterval&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;stats&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;si&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;networkStats&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;

  &lt;span class="k"&gt;for &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;network&lt;/span&gt; &lt;span class="k"&gt;of&lt;/span&gt; &lt;span class="nx"&gt;stats&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nx"&gt;console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;log&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
      &lt;span class="na"&gt;interface&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;network&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;iface&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="na"&gt;download&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;network&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;rx_sec&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="na"&gt;upload&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;network&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;tx_sec&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="mi"&gt;1000&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Now we have something like:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Wi-Fi
Download: 2.4 MB/s
Upload:   180 KB/s
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;But this is only the beginning.&lt;/p&gt;




&lt;h2&gt;
  
  
  Network Connections
&lt;/h2&gt;

&lt;p&gt;We can also inspect active network connections:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;connections&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;si&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;networkConnections&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;

&lt;span class="nx"&gt;console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;log&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;connections&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This gives us information about active TCP/UDP connections.&lt;/p&gt;

&lt;p&gt;That means we can potentially build:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Active Connections
────────────────────────────

TCP    443    api.example.com
TCP    443    github.com
TCP    443    google.com
UDP    53     DNS
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Now the dashboard starts becoming useful for troubleshooting.&lt;/p&gt;




&lt;h2&gt;
  
  
  Which Application Is Active?
&lt;/h2&gt;

&lt;p&gt;Network traffic alone doesn't tell us &lt;strong&gt;who is responsible&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;We also want to know which application the user is interacting with.&lt;/p&gt;

&lt;p&gt;One option is a native active-window module such as:&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;install&lt;/span&gt; @jannchie/active-window
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The package exposes information about the currently selected window and user idle time on supported Windows, macOS and Linux/Xorg environments. Platform coverage isn't identical everywhere, so this should be treated as an OS-dependent component.&lt;/p&gt;

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

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;activeWindow&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;require&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;@jannchie/active-window&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="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;getActiveApp&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="nb"&gt;window&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;activeWindow&lt;/span&gt;&lt;span class="p"&gt;().&lt;/span&gt;&lt;span class="nf"&gt;getActiveWindow&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="na"&gt;process&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;window&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;windowClass&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="nb"&gt;window&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;windowName&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;Then we can record:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;10:32:14  VS Code
10:32:15  VS Code
10:32:16  VS Code
...
10:45:02  Chrome
10:45:03  Chrome
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Instead of storing every second individually, we can aggregate this into sessions.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;VS Code
10:32 → 10:45
Duration: 13 minutes
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;






&lt;h2&gt;
  
  
  Sleep and Wake
&lt;/h2&gt;

&lt;p&gt;This is where Electron itself becomes useful.&lt;/p&gt;

&lt;p&gt;Electron provides the &lt;code&gt;powerMonitor&lt;/code&gt; module for monitoring system power-state changes, including suspend/resume, AC/battery changes, idle state and lock/unlock events on supported platforms.&lt;/p&gt;

&lt;p&gt;In the Electron main process:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&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;powerMonitor&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;require&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;electron&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

&lt;span class="nx"&gt;powerMonitor&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;on&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;suspend&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="nx"&gt;console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;log&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;System is going to sleep&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;powerMonitor&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;on&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;resume&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="nx"&gt;console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;log&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;System woke up&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;We can turn those events into database records:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;recordEvent&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;type&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&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;insert&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
    &lt;span class="nx"&gt;type&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="na"&gt;timestamp&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="p"&gt;});&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="nx"&gt;powerMonitor&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;on&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;suspend&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;recordEvent&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;system_sleep&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;powerMonitor&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;on&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;resume&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;recordEvent&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;system_wake&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;Now our timeline knows when the machine slept.&lt;/p&gt;




&lt;h2&gt;
  
  
  Idle Detection
&lt;/h2&gt;

&lt;p&gt;Electron also provides:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="nx"&gt;powerMonitor&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;getSystemIdleTime&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



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

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="nx"&gt;powerMonitor&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;getSystemIdleState&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;60&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The latter can return states such as &lt;code&gt;active&lt;/code&gt;, &lt;code&gt;idle&lt;/code&gt;, or &lt;code&gt;locked&lt;/code&gt; on supported systems.&lt;/p&gt;

&lt;p&gt;For example:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;state&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;powerMonitor&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;getSystemIdleState&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;60&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;idleSeconds&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;powerMonitor&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;getSystemIdleTime&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;

&lt;span class="nx"&gt;console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;log&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
  &lt;span class="nx"&gt;state&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="nx"&gt;idleSeconds&lt;/span&gt;
&lt;span class="p"&gt;});&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;We can therefore distinguish:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Active
   ↓
Idle
   ↓
Locked
   ↓
Sleep
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;That's much more meaningful than simply measuring application runtime.&lt;/p&gt;




&lt;h2&gt;
  
  
  Putting Everything Together
&lt;/h2&gt;

&lt;p&gt;Now imagine collecting events from four sources:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;                 Electron App
                      │
       ┌──────────────┼──────────────┐
       │              │              │
  powerMonitor   systeminformation   active-window
       │              │              │
       │         CPU / RAM / Net     Active App
       │              │              │
       └──────────────┼──────────────┘
                      ▼
                Event Collector
                      │
                      ▼
                Local Database
                      │
                      ▼
              Activity Timeline
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;A simple event model could be:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nl"&gt;timestamp&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="nx"&gt;type&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;network&lt;/span&gt;&lt;span class="dl"&gt;"&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="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;docker&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="nx"&gt;downloadBytes&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;1824000&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="nx"&gt;uploadBytes&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;420000&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



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

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nl"&gt;timestamp&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="nx"&gt;type&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;power&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="nx"&gt;state&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;sleep&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



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

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nl"&gt;timestamp&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="nx"&gt;type&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;application&lt;/span&gt;&lt;span class="dl"&gt;"&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="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Code&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="nb"&gt;window&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;server.js - Visual Studio Code&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Now everything becomes queryable.&lt;/p&gt;




&lt;h2&gt;
  
  
  The Interesting Part: Correlation
&lt;/h2&gt;

&lt;p&gt;This is where a normal system monitor becomes a &lt;strong&gt;desktop observability tool&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;Suppose we see:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;18:20  User idle
18:23  CPU 65%
18:24  Network 18 MB/s
18:26  Docker active
18:31  420 MB downloaded
18:42  Screen locked
18:45  Network still active
19:02  System sleep
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;We can generate an explanation:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Docker continued running after the user became idle and transferred approximately 420 MB before the system entered sleep.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;That's an insight.&lt;/p&gt;

&lt;p&gt;Not just a metric.&lt;/p&gt;




&lt;h2&gt;
  
  
  A Simple Collector
&lt;/h2&gt;

&lt;p&gt;We could start with something surprisingly small:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&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;powerMonitor&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;require&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;electron&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;si&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;require&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;systeminformation&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="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;collect&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;cpu&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;memory&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;network&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="nx"&gt;si&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;currentLoad&lt;/span&gt;&lt;span class="p"&gt;(),&lt;/span&gt;
    &lt;span class="nx"&gt;si&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;mem&lt;/span&gt;&lt;span class="p"&gt;(),&lt;/span&gt;
    &lt;span class="nx"&gt;si&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;networkStats&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="na"&gt;timestamp&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="na"&gt;system&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="na"&gt;idle&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;powerMonitor&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;getSystemIdleTime&lt;/span&gt;&lt;span class="p"&gt;(),&lt;/span&gt;
      &lt;span class="na"&gt;idleState&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;powerMonitor&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;getSystemIdleState&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;60&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
      &lt;span class="na"&gt;battery&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;powerMonitor&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;isOnBatteryPower&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
    &lt;span class="p"&gt;},&lt;/span&gt;

    &lt;span class="na"&gt;cpu&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;cpu&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;currentLoad&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;

    &lt;span class="na"&gt;memory&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="na"&gt;active&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;memory&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;active&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="na"&gt;total&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;memory&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;total&lt;/span&gt;
    &lt;span class="p"&gt;},&lt;/span&gt;

    &lt;span class="na"&gt;network&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;network&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;n&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;interface&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;n&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;iface&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="na"&gt;rxPerSecond&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;n&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;rx_sec&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="na"&gt;txPerSecond&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;n&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;tx_sec&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;setInterval&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="nx"&gt;console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;log&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;collect&lt;/span&gt;&lt;span class="p"&gt;());&lt;/span&gt;
&lt;span class="p"&gt;},&lt;/span&gt; &lt;span class="mi"&gt;5000&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Five seconds later, we have the beginning of our own desktop telemetry system.&lt;/p&gt;




&lt;h2&gt;
  
  
  But Don't Collect Everything Every Second
&lt;/h2&gt;

&lt;p&gt;This is an important design decision.&lt;/p&gt;

&lt;p&gt;You don't necessarily want:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Every 1 second
    ↓
Everything
    ↓
Database
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;That can generate enormous amounts of useless data.&lt;/p&gt;

&lt;p&gt;Instead, use different collection intervals.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Power events        Event-driven
App changes         Event-driven
CPU                 5-10 seconds
Memory              10 seconds
Network             1-5 seconds
Connections         10-30 seconds
Daily aggregates    1 minute+
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;And store &lt;strong&gt;state changes&lt;/strong&gt; rather than repeated identical states.&lt;/p&gt;

&lt;p&gt;For example, don't write:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;10:00 VS Code
10:01 VS Code
10:02 VS Code
10:03 VS Code
10:04 VS Code
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



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

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;VS Code
10:00 → 10:04
Duration: 4 minutes
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This makes the database dramatically smaller.&lt;/p&gt;




&lt;h2&gt;
  
  
  What About Electron's Network Logging?
&lt;/h2&gt;

&lt;p&gt;If the thing you're monitoring is specifically &lt;strong&gt;your Electron application's own network traffic&lt;/strong&gt;, Electron has another useful tool: &lt;code&gt;netLog&lt;/code&gt;.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&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;app&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;netLog&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;require&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;electron&lt;/span&gt;&lt;span class="dl"&gt;"&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;whenReady&lt;/span&gt;&lt;span class="p"&gt;().&lt;/span&gt;&lt;span class="nf"&gt;then&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="nx"&gt;netLog&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;startLogging&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;/tmp/electron-network-log&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;Electron's &lt;code&gt;netLog&lt;/code&gt; records network events for a session and supports different capture modes. Be careful with &lt;code&gt;includeSensitive&lt;/code&gt; and &lt;code&gt;everything&lt;/code&gt;, because those modes can capture sensitive information or transferred bytes.&lt;/p&gt;

&lt;p&gt;This is different from &lt;code&gt;systeminformation&lt;/code&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;systeminformation
        ↓
Whole-system information

Electron netLog
        ↓
Your Electron application's network activity
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;That distinction is important.&lt;/p&gt;




&lt;h2&gt;
  
  
  The Product I Would Build
&lt;/h2&gt;

&lt;p&gt;I wouldn't call this another "Task Manager."&lt;/p&gt;

&lt;p&gt;I'd call it something like:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Desktop Observability&lt;/strong&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;The dashboard could have four major views.&lt;/p&gt;

&lt;h3&gt;
  
  
  1. Activity
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;VS Code       4h 12m
Chrome        2h 31m
Slack         58m
Docker        1h 42m
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  2. Network
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Download     8.4 GB
Upload       1.3 GB

Docker       3.1 GB
Chrome       2.7 GB
VS Code      1.2 GB
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  3. Power
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Active       8h 21m
Idle         1h 08m
Sleep        7h 42m

Battery used 34%
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  4. Timeline
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;09:02  Wake
09:05  VS Code
10:32  Chrome
12:14  Idle
12:31  Docker network activity
13:02  Active
18:42  Lock
19:01  Sleep
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;And then the killer feature:&lt;/p&gt;

&lt;h3&gt;
  
  
  Ask the computer
&lt;/h3&gt;

&lt;blockquote&gt;
&lt;p&gt;Why was my laptop slow today?&lt;/p&gt;

&lt;p&gt;What used the most bandwidth?&lt;/p&gt;

&lt;p&gt;What happened while I was away?&lt;/p&gt;

&lt;p&gt;Which applications are active in the background?&lt;/p&gt;

&lt;p&gt;Did anything continue running after I locked the computer?&lt;/p&gt;
&lt;/blockquote&gt;




&lt;h2&gt;
  
  
  The Architecture
&lt;/h2&gt;

&lt;p&gt;A practical first version could be:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Electron
│
├── Main Process
│   │
│   ├── powerMonitor
│   ├── systeminformation
│   ├── active-window
│   └── netLog
│
├── Collector
│   │
│   ├── power events
│   ├── app sessions
│   ├── CPU/memory
│   └── network stats
│
├── SQLite
│
└── React Renderer
    │
    ├── Dashboard
    ├── Timeline
    ├── Applications
    ├── Network
    └── Power
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The key design principle is:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Collect locally. Correlate locally. Explain locally.&lt;/strong&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;You don't need to send a user's entire computer activity to a cloud server just to tell them why their laptop was busy.&lt;/p&gt;




&lt;h2&gt;
  
  
  The Bigger Idea
&lt;/h2&gt;

&lt;p&gt;The interesting thing here isn't &lt;code&gt;systeminformation&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;It isn't Electron's &lt;code&gt;powerMonitor&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;It isn't network monitoring.&lt;/p&gt;

&lt;p&gt;It's the &lt;strong&gt;combination&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;A computer is already generating thousands of signals:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Applications
Processes
CPU
Memory
Network
Power
Sleep
Wake
Idle
Lock
Unlock
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Today, these signals are mostly presented independently.&lt;/p&gt;

&lt;p&gt;A desktop observability layer could connect them.&lt;/p&gt;

&lt;p&gt;And instead of asking:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;"What's my CPU usage?"&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;we can finally ask:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;"What happened on my computer while I wasn't looking?"&lt;/strong&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;That's a much more interesting monitoring problem.&lt;/p&gt;

</description>
      <category>electron</category>
      <category>desktopapps</category>
      <category>power</category>
      <category>tooling</category>
    </item>
    <item>
      <title>Smashing the "Blind Spot" Bug: How We Integrated Sentry to Catch Regressions in Real-Time</title>
      <dc:creator>Nishikanta Ray</dc:creator>
      <pubDate>Sat, 08 Aug 2026 20:38:12 +0000</pubDate>
      <link>https://dev.to/nishikantaray/smashing-the-blind-spot-bug-how-we-integrated-sentry-to-catch-regressions-in-real-time-29m0</link>
      <guid>https://dev.to/nishikantaray/smashing-the-blind-spot-bug-how-we-integrated-sentry-to-catch-regressions-in-real-time-29m0</guid>
      <description>&lt;p&gt;&lt;em&gt;This is a submission for &lt;a href="https://dev.to/bugsmash"&gt;DEV's Summer Bug Smash: Smash Stories&lt;/a&gt; powered by &lt;a href="https://sentry.io/" rel="noopener noreferrer"&gt;Sentry&lt;/a&gt;.&lt;/em&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  The Challenge: Flying Blind in Production
&lt;/h2&gt;

&lt;p&gt;Pull Request - &lt;a href="https://github.com/NishikantaRay/InsightTrack/commit/a70ca0a00c8cd169a93b300cfcb450b5ecbde7f8" rel="noopener noreferrer"&gt;https://github.com/NishikantaRay/InsightTrack/commit/a70ca0a00c8cd169a93b300cfcb450b5ecbde7f8&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fgtdojsxlekuugysxbwc8.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fgtdojsxlekuugysxbwc8.png" alt="Insighttracks" width="800" height="413"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Before this summer, our analytics platform, &lt;strong&gt;InsightTrack&lt;/strong&gt;, had a fundamental flaw in how it handled observability. We were tracking standard JavaScript errors via a basic &lt;code&gt;window.onerror&lt;/code&gt; handler, but it was just noise. We had no stack traces, no grouped fingerprints, and absolutely no release context. &lt;/p&gt;

&lt;p&gt;If a customer integrated 10 different sites into our platform, we couldn't accurately tell them if a specific spike in errors was a brand-new issue or a resurrected bug from three deployments ago. We were flying blind, and our users were feeling the pain of delayed bug resolutions. &lt;/p&gt;

&lt;p&gt;The ultimate "bug" wasn't a single line of broken code; &lt;strong&gt;it was our entire error observability pipeline.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2F34twqm7muckncttlk480.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2F34twqm7muckncttlk480.png" alt="ARCH" width="800" height="437"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  The Solution: A Deep-Dive Sentry Integration
&lt;/h2&gt;

&lt;p&gt;We decided to smash this architectural bug by building a native, robust integration with &lt;strong&gt;Sentry&lt;/strong&gt;. We didn't just want to add a widget; we wanted to bring Sentry's rich context (fingerprinted grouping, permalinks, regression status, and user-impact counts) directly into the InsightTrack dashboard so traffic and bugs could be watched side-by-side.&lt;/p&gt;

&lt;h3&gt;
  
  
  How We Built It
&lt;/h3&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fzuh2qyvl8m1ljsblia9p.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fzuh2qyvl8m1ljsblia9p.png" alt="How we built it" width="800" height="249"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;To make this work seamlessly at scale (where one customer might poll 10 independent Sentry projects simultaneously), we built a dual-path ingestion system:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;The Polling Backstop:&lt;/strong&gt; We set up a bounded worker pool (to prevent slow projects from stalling the fleet) that polls the Sentry API every 5 minutes. To respect rate limits, we built an &lt;strong&gt;adaptive cadence&lt;/strong&gt;—active projects poll frequently, while quiet or erroring projects exponentially back off. &lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;The Near-Real-Time Webhook:&lt;/strong&gt; For instant visibility, we allowed users to point a Sentry Internal Integration webhook at our API. Using HMAC signatures verified in constant time against a stored secret, new or regressed issues hit our database in milliseconds.&lt;/li&gt;
&lt;/ol&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;The Golden Rule of our Architecture:&lt;/strong&gt; &lt;br&gt;
All writes (polls and webhooks) go to &lt;strong&gt;PostgreSQL&lt;/strong&gt;. A background sync then pushes this data to &lt;strong&gt;DuckDB&lt;/strong&gt; every 60 seconds. All dashboard reads and analytics queries come &lt;em&gt;only&lt;/em&gt; from DuckDB for lightning-fast performance.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h2&gt;
  
  
  The "Smash" Moment 🔨
&lt;/h2&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fp8gps25cftdl75hvxgy4.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fp8gps25cftdl75hvxgy4.png" alt="Smash" width="800" height="412"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;We deployed the Sentry integration to production on a Tuesday. On Wednesday morning, it paid off spectacularly. &lt;/p&gt;

&lt;p&gt;A customer pushed a massive UI update to their e-commerce site. Within three minutes, our newly built &lt;strong&gt;Error Spike Alerts&lt;/strong&gt;—which calculate a 2-sigma (2σ) z-score against a 7-day rolling mean of Sentry events—fired off an alarm in their dashboard. &lt;/p&gt;

&lt;p&gt;Because we were pulling in Sentry's &lt;code&gt;substatus&lt;/code&gt; API field, the issue surfaced with a glaring red &lt;strong&gt;Regressed&lt;/strong&gt; badge right in our UI. &lt;/p&gt;

&lt;p&gt;Instead of opening five different tabs to figure out what went wrong, the customer clicked the row in our dashboard. This triggered our &lt;strong&gt;Live Drill-Down&lt;/strong&gt; feature, which makes an on-demand, real-time fetch directly to Sentry's &lt;code&gt;/events/latest/&lt;/code&gt; endpoint. The stack trace, breadcrumbs, and tags populated instantly. &lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2F3qn05i1576zk9931vwl7.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2F3qn05i1576zk9931vwl7.png" alt="an" width="800" height="322"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;The bug? A deprecated checkout component that had been accidentally uncommented during a merge conflict. It was identified, patched, and deployed before most of their users even woke up.&lt;/p&gt;

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

&lt;p&gt;I am incredibly proud of how resilient we made this system. By decoupling the write path (PostgreSQL) from the read path (DuckDB), the dashboard remains incredibly fast even if the polling workers are churning through thousands of Sentry pagination cursors in the background. &lt;/p&gt;

&lt;p&gt;I'm also proud of the &lt;strong&gt;AI Analyst (Pulse)&lt;/strong&gt; tools we built on top of this. Because the data is so cleanly structured in DuckDB, we exposed read-only MCP tools (&lt;code&gt;get_error_summary&lt;/code&gt; and &lt;code&gt;get_error_issues&lt;/code&gt;). Now, users can literally chat with their analytics dashboard and ask, &lt;em&gt;"Did yesterday's deploy cause any regressions?"&lt;/em&gt; and the AI will read the synced Sentry data to give them a perfect answer.&lt;/p&gt;

&lt;h2&gt;
  
  
  What I Learned
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;  &lt;strong&gt;Handling external APIs requires defensive programming.&lt;/strong&gt; Assuming an API will always return what you expect is a recipe for disaster. Building in graceful degradation and adaptive backoffs (like dropping to a 6-hour poll cadence on a 401 Auth Fail) kept our system from DDOSing ourselves with bad tokens.&lt;/li&gt;
&lt;li&gt;  &lt;strong&gt;Webhooks + Polling = The perfect marriage.&lt;/strong&gt; Webhooks are great for speed, but polling is necessary for accurate counts and state reconciliation (e.g., soft-deleting issues that have aged out). Using both gives you the best of both worlds.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Thanks to Sentry and the DEV community for the motivation to finally smash this architectural bug!&lt;/p&gt;

&lt;p&gt;The ultimate "bug" wasn't a single line of broken code; it was our entire error observability pipeline.&lt;br&gt;
Github - &lt;a href="https://github.com/nishikantaray/InsightTrack" rel="noopener noreferrer"&gt;https://github.com/nishikantaray/InsightTrack&lt;/a&gt;&lt;br&gt;
Website - &lt;a href="https://insightstrack.dev" rel="noopener noreferrer"&gt;https://insightstrack.dev&lt;/a&gt;&lt;br&gt;
Pull Request - &lt;a href="https://github.com/NishikantaRay/InsightTrack/commit/a70ca0a00c8cd169a93b300cfcb450b5ecbde7f8" rel="noopener noreferrer"&gt;https://github.com/NishikantaRay/InsightTrack/commit/a70ca0a00c8cd169a93b300cfcb450b5ecbde7f8&lt;/a&gt;&lt;br&gt;
Portfolio - &lt;a href="https://nishikanta.in/" rel="noopener noreferrer"&gt;https://nishikanta.in/&lt;/a&gt;&lt;/p&gt;

</description>
      <category>devchallenge</category>
      <category>bugsmash</category>
    </item>
    <item>
      <title>Building an AI-native Second Brain with Multi-RAG, Knowledge Graphs, and MCP</title>
      <dc:creator>Nishikanta Ray</dc:creator>
      <pubDate>Sat, 08 Aug 2026 02:38:21 +0000</pubDate>
      <link>https://dev.to/nishikantaray/building-an-ai-native-second-brain-with-multi-rag-knowledge-graphs-and-mcp-fmg</link>
      <guid>https://dev.to/nishikantaray/building-an-ai-native-second-brain-with-multi-rag-knowledge-graphs-and-mcp-fmg</guid>
      <description>&lt;p&gt;Claude is incredibly good at reasoning.&lt;/p&gt;

&lt;p&gt;But reasoning is only as useful as the context available to it.&lt;/p&gt;

&lt;p&gt;Your architecture might be in GitHub. Your notes might be in Obsidian. Your decisions might be buried in Slack. Your research might be in PDFs. Your project history might exist across hundreds of conversations.&lt;/p&gt;

&lt;p&gt;The information exists.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;The problem is that your AI doesn't have a unified way to understand it.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;So I started thinking:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;What if Claude had a persistent Second Brain?&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Not another chatbot. Not another vector database.&lt;/p&gt;

&lt;p&gt;A knowledge layer that Claude can query, learn from, and write back to.&lt;/p&gt;




&lt;h2&gt;
  
  
  What Is a Second Brain?
&lt;/h2&gt;

&lt;p&gt;A Second Brain is a personal knowledge management system that helps you capture, organize, connect, and retrieve information outside your biological memory.&lt;/p&gt;

&lt;p&gt;Tools like Obsidian made this idea powerful.&lt;/p&gt;

&lt;p&gt;You can write simple Markdown notes and connect them:&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;InsightTrack
    │
    ├── [[DuckDB]]
    ├── [[PostgreSQL]]
    └── [[MCP]]
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;Obsidian then creates a visual graph of those relationships.&lt;/p&gt;

&lt;p&gt;But there is a limitation:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Obsidian knows the relationships you explicitly create.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;AI can take this much further.&lt;/p&gt;

&lt;p&gt;Imagine having:&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;architecture.md
      │
      └── mentions DuckDB

GitHub PR #481
      │
      └── changes DuckDB queries

Slack discussion
      │
      └── explains why DuckDB was selected

benchmark.md
      │
      └── contains performance results
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;A human may never manually connect all four.&lt;/p&gt;

&lt;p&gt;An AI-native knowledge system can discover those relationships automatically.&lt;/p&gt;




&lt;h2&gt;
  
  
  From Second Brain to AI Brain
&lt;/h2&gt;

&lt;p&gt;A traditional Second Brain mainly answers:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;"Where did I save this?"&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;An AI-native Second Brain should answer:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;"What do we know about this, where did it come from, and how is it connected to everything else?"&lt;/strong&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;That requires more than vector search.&lt;/p&gt;

&lt;p&gt;It requires multiple retrieval strategies working together.&lt;/p&gt;




&lt;h2&gt;
  
  
  Why Traditional RAG Isn't Enough
&lt;/h2&gt;

&lt;p&gt;A typical RAG architecture looks like:&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Documents
    ↓
Chunking
    ↓
Embeddings
    ↓
Vector Search
    ↓
LLM
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;This works extremely well for semantic similarity.&lt;/p&gt;

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

&lt;blockquote&gt;
&lt;p&gt;"What was the reasoning behind our analytics architecture?"&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Vector search can find relevant discussions even if the exact wording is different.&lt;/p&gt;

&lt;p&gt;But now ask:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;"Where is SYNC_BATCH_SIZE defined?"&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;That's an exact identifier.&lt;/p&gt;

&lt;p&gt;Keyword search is better.&lt;/p&gt;

&lt;p&gt;Or ask:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;"What services depend on PostgreSQL?"&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;That's a relationship question.&lt;/p&gt;

&lt;p&gt;A knowledge graph is better.&lt;/p&gt;

&lt;p&gt;This is why I think a serious AI Second Brain needs &lt;strong&gt;Multi-RAG&lt;/strong&gt;.&lt;/p&gt;




&lt;h2&gt;
  
  
  Multi-RAG: Different Questions Need Different Retrieval
&lt;/h2&gt;

&lt;p&gt;Instead of relying on one retrieval technique:&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Semantic Search
       +
Full-Text Search
       +
Knowledge Graph
       +
Metadata Filtering
       +
Reranking
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;Each method solves a different problem.&lt;/p&gt;

&lt;h3&gt;
  
  
  Semantic RAG
&lt;/h3&gt;

&lt;p&gt;Good for understanding concepts.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;"Why did we choose DuckDB?"&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h3&gt;
  
  
  Keyword RAG
&lt;/h3&gt;

&lt;p&gt;Good for exact information.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;"Find references to SYNC_BATCH_SIZE."&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h3&gt;
  
  
  Graph RAG
&lt;/h3&gt;

&lt;p&gt;Good for relationships.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;"Which services depend on PostgreSQL?"&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h3&gt;
  
  
  Memory Retrieval
&lt;/h3&gt;

&lt;p&gt;Good for decisions and persistent context.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;"What did we decide about the analytics architecture?"&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Together, they provide much richer context than vector search alone.&lt;/p&gt;




&lt;h2&gt;
  
  
  The Architecture
&lt;/h2&gt;

&lt;p&gt;The important idea isn't any individual component.&lt;/p&gt;

&lt;p&gt;It's how the components work together.&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Obsidian / Documents / External Sources
                │
                ▼
           Event Queue
                │
                ▼
        Processing Workers
          │      │      │
          ▼      ▼      ▼
       Metadata Embedding Graph
          │      │      │
          └──────┼──────┘
                 ▼
          Knowledge Layer
                 │
      ┌──────────┼──────────┐
      ▼          ▼          ▼
   Vector      Search      Graph
   Search      Index       Store
      │          │          │
      └──────────┼──────────┘
                 ▼
          Retrieval Engine
                 │
                 ▼
              MCP / API
                 │
                 ▼
          Claude / GPT / AI
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;




&lt;h2&gt;
  
  
  Obsidian Is the Human Interface
&lt;/h2&gt;

&lt;p&gt;I don't see Obsidian as the entire Second Brain.&lt;/p&gt;

&lt;p&gt;I see it as one of the best &lt;strong&gt;human-facing knowledge interfaces&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;You can continue writing naturally:&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;# Analytics Architecture

InsightTrack uses [[DuckDB]] for analytical queries.

Data originates in [[PostgreSQL]].

See [[Pulse]] for the AI analytics layer.
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;Obsidian gives you human-readable notes and a native graph.&lt;/p&gt;

&lt;p&gt;The AI system adds another layer on top.&lt;/p&gt;




&lt;h2&gt;
  
  
  Two Graphs, Two Purposes
&lt;/h2&gt;

&lt;p&gt;This architecture can maintain two different graphs.&lt;/p&gt;

&lt;h3&gt;
  
  
  Obsidian Graph
&lt;/h3&gt;

&lt;p&gt;Created from explicit links:&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;[[InsightTrack]]
       │
       ▼
   [[DuckDB]]
       │
       ▼
  [[Analytics]]
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;This is primarily useful for &lt;strong&gt;human navigation&lt;/strong&gt;.&lt;/p&gt;

&lt;h3&gt;
  
  
  System Entity Graph
&lt;/h3&gt;

&lt;p&gt;Automatically extracted from all available knowledge:&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;InsightTrack
      │
     USES
      │
   DuckDB
   /    \
READS   SYNCED_FROM
 /          \
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;Pulse      PostgreSQL&lt;/p&gt;

&lt;p&gt;The system graph can be built from:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Markdown&lt;/li&gt;
&lt;li&gt;Source code&lt;/li&gt;
&lt;li&gt;Git commits&lt;/li&gt;
&lt;li&gt;Pull requests&lt;/li&gt;
&lt;li&gt;Database schemas&lt;/li&gt;
&lt;li&gt;Documentation&lt;/li&gt;
&lt;li&gt;Conversations&lt;/li&gt;
&lt;li&gt;Meeting notes&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;This graph isn't just for visualization.&lt;/p&gt;

&lt;p&gt;It becomes part of retrieval.&lt;/p&gt;




&lt;h2&gt;
  
  
  Knowledge Engineering + Graph Engineering
&lt;/h2&gt;

&lt;p&gt;This is where the architecture becomes more interesting than a normal RAG application.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Knowledge Engineering&lt;/strong&gt; turns raw information into structured knowledge.&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Meeting Transcript
        ↓
     Summary
        ↓
   Action Items
        ↓
Architecture Decision
        ↓
   Project Memory
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;&lt;strong&gt;Graph Engineering&lt;/strong&gt; connects the resulting entities.&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Project
   ↓
Repository
   ↓
  API
   ↓
Database
   ↓
Developer
   ↓
Decision
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;The result isn't just a collection of documents.&lt;/p&gt;

&lt;p&gt;It's a connected knowledge model.&lt;/p&gt;




&lt;h2&gt;
  
  
  The Retrieval Engine
&lt;/h2&gt;

&lt;p&gt;The retrieval engine is arguably the most important part.&lt;/p&gt;

&lt;p&gt;Suppose you ask:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Why did we move analytics queries from PostgreSQL to DuckDB?&lt;/strong&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;The system might retrieve:&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;GitHub PR
      +
Architecture Note
      +
Benchmark
      +
Slack Discussion
      +
Decision Memory
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;Then rerank and combine the results.&lt;/p&gt;

&lt;p&gt;Instead of giving Claude thousands of chunks, the system builds a focused context:&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Decision:
Use DuckDB for analytical workloads.

Evidence:
- Benchmarks showed better aggregation performance.
- PostgreSQL remains the transactional source of truth.
- The architecture PR implemented the separation.
- The Slack discussion explains the operational trade-offs.
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;Now Claude is reasoning over &lt;strong&gt;evidence + relationships + memory&lt;/strong&gt;.&lt;/p&gt;




&lt;h2&gt;
  
  
  Long-Term Memory
&lt;/h2&gt;

&lt;p&gt;A Second Brain shouldn't remember everything.&lt;/p&gt;

&lt;p&gt;Otherwise, it becomes another giant database.&lt;/p&gt;

&lt;p&gt;It should remember what matters.&lt;/p&gt;

&lt;p&gt;For example:&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Decision
────────────────────
PostgreSQL remains the source of truth.

Decision
────────────────────
DuckDB handles analytical reads.

Reason
────────────────────
Better analytical performance and isolation.

Status
────────────────────
Implemented.
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;This is much more valuable than storing thousands of raw conversations.&lt;/p&gt;

&lt;p&gt;The system should distinguish between:&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Raw Data
   ↓
Information
   ↓
Knowledge
   ↓
Memory
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;That's an important part of making AI memory useful.&lt;/p&gt;




&lt;h2&gt;
  
  
  MCP Connects the Second Brain to Claude
&lt;/h2&gt;

&lt;p&gt;This is where the architecture becomes AI-native.&lt;/p&gt;

&lt;p&gt;Instead of building a custom integration directly into Claude, expose the Second Brain through &lt;strong&gt;MCP&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;For example:&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;search()
retrieve()
remember()
ingest()
graph()
timeline()
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;Claude can ask:&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;search(
  "Why did we choose DuckDB?"
)
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;The Second Brain handles the complexity.&lt;/p&gt;

&lt;p&gt;Claude doesn't need to know whether the answer came from:&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;PostgreSQL
pgvector
Search Index
Knowledge Graph
Obsidian
GitHub
Slack
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;It simply receives useful context.&lt;/p&gt;




&lt;h2&gt;
  
  
  Claude Can Write Back
&lt;/h2&gt;

&lt;p&gt;This is where it becomes more than traditional RAG.&lt;/p&gt;

&lt;p&gt;The flow isn't only:&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Knowledge → AI
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;It becomes:&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Knowledge ↔ AI
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;For example:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;"Remember that we decided to keep PostgreSQL as the source of truth."&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Claude calls:&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;remember(...)
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;The system can then:&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Extract Entities
       ↓
Extract Relationships
       ↓
   Create Memory
       ↓
Generate Embedding
       ↓
   Update Graph
       ↓
   Store Source
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;The knowledge base becomes progressively richer.&lt;/p&gt;




&lt;h2&gt;
  
  
  The Model Doesn't Own the Knowledge
&lt;/h2&gt;

&lt;p&gt;This is one of the most important ideas.&lt;/p&gt;

&lt;p&gt;Today:&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Claude
   ↓
Claude Memory
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;Another AI:&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;GPT
   ↓
GPT Memory
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;Another:&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Gemini
   ↓
Gemini Memory
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;Everything is isolated.&lt;/p&gt;

&lt;p&gt;Instead:&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;             Claude
                │
               GPT
                │
             Gemini
                │
              Cursor
                │
                ▼
         ┌───────────────┐
         │  Knowledge OS │
         └───────────────┘
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;The model can change.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;The knowledge stays.&lt;/strong&gt;&lt;/p&gt;




&lt;h2&gt;
  
  
  The Bigger Idea
&lt;/h2&gt;

&lt;p&gt;I don't think the future is simply:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;"Give Claude more context."&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;I think it's:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Build a persistent knowledge layer that every AI can use.&lt;/strong&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Obsidian can remain your human knowledge interface.&lt;/p&gt;

&lt;p&gt;GitHub can remain your source-code system.&lt;/p&gt;

&lt;p&gt;Slack can remain your communication system.&lt;/p&gt;

&lt;p&gt;Your databases can remain your data systems.&lt;/p&gt;

&lt;p&gt;Your AI assistants can remain your reasoning systems.&lt;/p&gt;

&lt;p&gt;The Knowledge OS connects them.&lt;/p&gt;




&lt;h2&gt;
  
  
  Beyond the Second Brain
&lt;/h2&gt;

&lt;p&gt;A traditional Second Brain helps &lt;strong&gt;you remember&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;An AI-native Second Brain helps &lt;strong&gt;AI understand&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;It combines:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Knowledge Engineering&lt;/strong&gt; — turning raw information into structured knowledge&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Graph Engineering&lt;/strong&gt; — discovering entities and relationships&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Hybrid Retrieval&lt;/strong&gt; — combining semantic, keyword, and graph search&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Long-Term Memory&lt;/strong&gt; — preserving decisions and important context&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;MCP&lt;/strong&gt; — making the knowledge available to AI agents and assistants&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The result is something larger than a note-taking application or a vector database.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;An AI-native Knowledge Operating System that combines Knowledge Engineering, Graph Engineering, Hybrid Retrieval, and Long-Term Memory into a unified platform accessible through MCP and APIs.&lt;/strong&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;The LLM provides the reasoning.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;The Knowledge OS provides the memory.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;And MCP becomes the bridge between them.&lt;/p&gt;

</description>
      <category>ai</category>
      <category>mcp</category>
      <category>claude</category>
      <category>rag</category>
    </item>
    <item>
      <title>Mind Maps in MCP: A Powerful Feature or Just Eye Candy?</title>
      <dc:creator>Nishikanta Ray</dc:creator>
      <pubDate>Sat, 01 Aug 2026 18:27:45 +0000</pubDate>
      <link>https://dev.to/nishikantaray/mind-maps-in-mcp-a-powerful-feature-or-just-eye-candy-2oh2</link>
      <guid>https://dev.to/nishikantaray/mind-maps-in-mcp-a-powerful-feature-or-just-eye-candy-2oh2</guid>
      <description>&lt;blockquote&gt;
&lt;p&gt;Mind maps are an excellent addition to an MCP ecosystem when they help users understand relationships between data, tools, and reasoning. They should complement—not replace—traditional dashboards, tables, and chat interfaces.&lt;/p&gt;
&lt;/blockquote&gt;




&lt;h2&gt;
  
  
  Introduction
&lt;/h2&gt;

&lt;p&gt;Large Language Models have become incredibly good at answering questions. Ask about your analytics, codebase, documentation, or infrastructure, and they'll generate a detailed response in seconds.&lt;/p&gt;

&lt;p&gt;But there's a problem.&lt;/p&gt;

&lt;p&gt;Most AI conversations are &lt;strong&gt;linear&lt;/strong&gt;, while the problems we're solving are &lt;strong&gt;non-linear&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;Imagine asking:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Why did traffic drop yesterday?&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;A traditional AI responds with several paragraphs of text.&lt;/p&gt;

&lt;p&gt;A mind map, however, immediately shows the relationships between traffic sources, landing pages, countries, devices, deployments, and JavaScript errors.&lt;/p&gt;

&lt;p&gt;Instead of reading hundreds of words, you understand the entire situation in seconds.&lt;/p&gt;

&lt;p&gt;This naturally raises an interesting question:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Should every MCP server support mind maps?&lt;/strong&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;After exploring different architectures and use cases, my conclusion is simple:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Yes—but only when they help users understand relationships rather than simply displaying information.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Let's explore why.&lt;/p&gt;




&lt;h2&gt;
  
  
  What is a Mind Map in MCP?
&lt;/h2&gt;

&lt;p&gt;An MCP (Model Context Protocol) server exposes tools that AI models can use.&lt;/p&gt;

&lt;p&gt;Normally the workflow looks like this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;User
  │
  ▼
LLM
  │
  ▼
MCP Tool
  │
  ▼
Database
  │
  ▼
Response
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The AI collects data, summarizes it, and returns text.&lt;/p&gt;

&lt;p&gt;Now imagine adding a visualization layer.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;             User
               │
               ▼
          AI Planner
               │
      ┌────────┴────────┐
      ▼                 ▼
 Analytics Tool    Documentation Tool
      ▼                 ▼
 Error Tool        Code Search Tool
      ▼                 ▼
      └────────┬────────┘
               ▼
     Relationship Builder
               ▼
      Interactive Mind Map
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The AI still performs the same work.&lt;/p&gt;

&lt;p&gt;The difference is that users now see &lt;strong&gt;how everything connects&lt;/strong&gt; instead of reading a long explanation.&lt;/p&gt;




&lt;h2&gt;
  
  
  Why Traditional AI Isn't Always Enough
&lt;/h2&gt;

&lt;p&gt;Humans naturally think in relationships.&lt;/p&gt;

&lt;p&gt;When someone asks:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Why are conversions down?&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;They aren't thinking linearly.&lt;/p&gt;

&lt;p&gt;They're exploring multiple possibilities simultaneously.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Conversions
│
├── Traffic
│
├── Checkout
│
├── Marketing
│
├── Performance
│
├── Returning Users
│
└── Errors
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Each branch creates another investigation.&lt;/p&gt;

&lt;p&gt;This is exactly how troubleshooting works.&lt;/p&gt;

&lt;p&gt;A mind map simply visualizes that thought process.&lt;/p&gt;




&lt;h2&gt;
  
  
  Real Analytics Example
&lt;/h2&gt;

&lt;p&gt;Suppose an AI receives this question:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Why did yesterday's traffic decrease?&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Instead of returning paragraphs, it could generate something like:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Traffic Drop
│
├── Organic Search ↓
│
├── Homepage ↓
│
├── India ↓
│
├── JavaScript Errors ↑
│
├── Core Web Vitals ↓
│
└── New Deployment ✓
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Within seconds the user understands:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Organic search decreased&lt;/li&gt;
&lt;li&gt;Homepage was affected&lt;/li&gt;
&lt;li&gt;Most impact came from India&lt;/li&gt;
&lt;li&gt;JavaScript errors increased&lt;/li&gt;
&lt;li&gt;A deployment happened recently&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The graph immediately tells a story.&lt;/p&gt;




&lt;h2&gt;
  
  
  Where Mind Maps Work Best
&lt;/h2&gt;

&lt;h2&gt;
  
  
  1. Analytics Investigation
&lt;/h2&gt;

&lt;p&gt;Traditional dashboards answer:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;How many visitors?&lt;/li&gt;
&lt;li&gt;Which country?&lt;/li&gt;
&lt;li&gt;Which browser?&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Mind maps answer:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Why did traffic change?&lt;/li&gt;
&lt;li&gt;Which metrics influence each other?&lt;/li&gt;
&lt;li&gt;What caused this anomaly?&lt;/li&gt;
&lt;/ul&gt;

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

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Traffic
│
├── Organic
│   ├── Landing Pages
│   ├── Keywords
│   └── Countries
│
├── Paid
│
├── Social
│
└── Referral
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This transforms disconnected metrics into connected insights.&lt;/p&gt;




&lt;h2&gt;
  
  
  2. Documentation Search
&lt;/h2&gt;

&lt;p&gt;Imagine asking:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Explain authentication.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Instead of returning dozens of documents, the AI groups concepts together.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Authentication
│
├── OAuth
├── JWT
├── API Keys
├── Sessions
└── Permissions
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Users instantly understand the overall architecture.&lt;/p&gt;




&lt;h2&gt;
  
  
  3. Codebase Exploration
&lt;/h2&gt;

&lt;p&gt;Large repositories can be overwhelming.&lt;/p&gt;

&lt;p&gt;Instead of thousands of files:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Backend
│
├── API
│
├── Database
│   ├── PostgreSQL
│   └── Prisma
│
├── Authentication
│
└── Workers
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Developers can navigate the repository visually.&lt;/p&gt;




&lt;h2&gt;
  
  
  4. AI Agent Planning
&lt;/h2&gt;

&lt;p&gt;Most AI agents perform many hidden steps.&lt;/p&gt;

&lt;p&gt;A mind map exposes them.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Task
│
├── Research
│
├── Search
│
├── Compare
│
├── Generate
│
└── Validate
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This makes debugging AI workflows dramatically easier.&lt;/p&gt;




&lt;h2&gt;
  
  
  The Biggest Advantages
&lt;/h2&gt;

&lt;h2&gt;
  
  
  Better Understanding
&lt;/h2&gt;

&lt;p&gt;Humans recognize patterns much faster than paragraphs.&lt;/p&gt;

&lt;p&gt;Instead of reading 800 words, users understand the entire structure in seconds.&lt;/p&gt;




&lt;h2&gt;
  
  
  Relationship Discovery
&lt;/h2&gt;

&lt;p&gt;Traditional dashboards display numbers.&lt;/p&gt;

&lt;p&gt;Mind maps display relationships.&lt;/p&gt;

&lt;p&gt;For example:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Deployment
      │
      ▼
JavaScript Errors
      │
      ▼
Checkout Failure
      │
      ▼
Revenue Drop
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;That chain is much harder to discover from charts alone.&lt;/p&gt;




&lt;h2&gt;
  
  
  Easier Navigation
&lt;/h2&gt;

&lt;p&gt;Users don't need every detail immediately.&lt;/p&gt;

&lt;p&gt;They can expand only the sections they care about.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Traffic
  │
  ├── Organic
  │
  ├── Direct
  │
  └── Social
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This keeps complex systems manageable.&lt;/p&gt;




&lt;h2&gt;
  
  
  Better AI Transparency
&lt;/h2&gt;

&lt;p&gt;Instead of wondering why the AI reached a conclusion, users can inspect the reasoning path visually.&lt;/p&gt;

&lt;p&gt;This increases trust in AI-generated answers.&lt;/p&gt;




&lt;h2&gt;
  
  
  Where Mind Maps Fail
&lt;/h2&gt;

&lt;p&gt;Like every visualization, they have limitations.&lt;/p&gt;




&lt;h2&gt;
  
  
  Everything Becomes a Graph
&lt;/h2&gt;

&lt;p&gt;One common mistake is trying to visualize every piece of information.&lt;/p&gt;

&lt;p&gt;Imagine this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Visitors
│
├── Countries
├── Browsers
├── Devices
├── Sessions
├── Events
├── Campaigns
├── Pages
├── Heatmaps
├── Errors
├── Funnels
├── Scroll Depth
├── UTM
├── Performance
└── ...
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;After a few hundred nodes, the graph becomes unreadable.&lt;/p&gt;




&lt;h2&gt;
  
  
  Tables Are Sometimes Better
&lt;/h2&gt;

&lt;p&gt;Suppose someone asks:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Show today's visitors.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;They don't need a graph.&lt;/p&gt;

&lt;p&gt;They need this:&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Hour&lt;/th&gt;
&lt;th&gt;Visitors&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;9 AM&lt;/td&gt;
&lt;td&gt;241&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;10 AM&lt;/td&gt;
&lt;td&gt;318&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;11 AM&lt;/td&gt;
&lt;td&gt;401&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;Not every question benefits from visualization.&lt;/p&gt;




&lt;h2&gt;
  
  
  Performance
&lt;/h2&gt;

&lt;p&gt;Large graphs become expensive.&lt;/p&gt;

&lt;p&gt;Some challenges include:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Thousands of nodes&lt;/li&gt;
&lt;li&gt;Complex edge calculations&lt;/li&gt;
&lt;li&gt;Layout algorithms&lt;/li&gt;
&lt;li&gt;Browser memory usage&lt;/li&gt;
&lt;li&gt;Slow rendering&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Most graph libraries begin struggling once graphs become extremely large unless clustering or virtualization is implemented.&lt;/p&gt;




&lt;h2&gt;
  
  
  Additional Maintenance
&lt;/h2&gt;

&lt;p&gt;Relationships don't build themselves.&lt;/p&gt;

&lt;p&gt;Every new feature means:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;New node types&lt;/li&gt;
&lt;li&gt;New edge rules&lt;/li&gt;
&lt;li&gt;Updated layouts&lt;/li&gt;
&lt;li&gt;Better ranking algorithms&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Graph maintenance quickly becomes its own subsystem.&lt;/p&gt;




&lt;h2&gt;
  
  
  Common Mistakes
&lt;/h2&gt;

&lt;p&gt;Many developers build graphs that simply mirror database tables.&lt;/p&gt;

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

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Country
   │
   ▼
India
   │
   ▼
Users
   │
   ▼
5000
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;That provides almost no value.&lt;/p&gt;

&lt;p&gt;Instead, focus on &lt;strong&gt;relationships&lt;/strong&gt;.&lt;/p&gt;

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

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Traffic Drop
│
├── Homepage
├── Organic Search
├── India
└── JavaScript Errors
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The graph explains &lt;em&gt;why&lt;/em&gt; something happened.&lt;/p&gt;




&lt;h2&gt;
  
  
  Should Every MCP Include Mind Maps?
&lt;/h2&gt;

&lt;p&gt;Not necessarily.&lt;/p&gt;

&lt;p&gt;Here's a simple rule.&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Great Fit&lt;/th&gt;
&lt;th&gt;Poor Fit&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Documentation&lt;/td&gt;
&lt;td&gt;Raw SQL&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;AI reasoning&lt;/td&gt;
&lt;td&gt;CSV exports&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Research&lt;/td&gt;
&lt;td&gt;Tables&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Architecture&lt;/td&gt;
&lt;td&gt;Dashboards&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Agent workflows&lt;/td&gt;
&lt;td&gt;Metric reports&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Knowledge graphs&lt;/td&gt;
&lt;td&gt;Simple searches&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;Mind maps are strongest when users need to understand &lt;strong&gt;connections&lt;/strong&gt;, not just retrieve information.&lt;/p&gt;




&lt;h2&gt;
  
  
  A Better Architecture
&lt;/h2&gt;

&lt;p&gt;Rather than replacing the existing interface, add mind maps as an exploration mode.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Dashboard
│
├── Visitors
├── Revenue
├── Funnels
├── Heatmaps
└── Errors

        │
        ▼

🧠 Explore Relationships

        │
        ▼

Traffic
│
├── Acquisition
├── Geography
├── Pages
├── Errors
└── Performance
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The dashboard remains familiar.&lt;/p&gt;

&lt;p&gt;The graph becomes a discovery tool.&lt;/p&gt;

&lt;p&gt;This hybrid approach gives users the best of both worlds.&lt;/p&gt;




&lt;h2&gt;
  
  
  Recommended Libraries
&lt;/h2&gt;

&lt;p&gt;If you're planning to build interactive graph visualizations, these libraries are worth considering.&lt;/p&gt;

&lt;h3&gt;
  
  
  React Flow
&lt;/h3&gt;

&lt;p&gt;Perfect for:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Interactive editors&lt;/li&gt;
&lt;li&gt;Expandable nodes&lt;/li&gt;
&lt;li&gt;Workflow builders&lt;/li&gt;
&lt;li&gt;AI reasoning graphs&lt;/li&gt;
&lt;/ul&gt;




&lt;h3&gt;
  
  
  Cytoscape.js
&lt;/h3&gt;

&lt;p&gt;Excellent for:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Large relationship graphs&lt;/li&gt;
&lt;li&gt;Knowledge visualization&lt;/li&gt;
&lt;li&gt;Network analysis&lt;/li&gt;
&lt;/ul&gt;




&lt;h3&gt;
  
  
  AntV G6
&lt;/h3&gt;

&lt;p&gt;Great for:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Enterprise dashboards&lt;/li&gt;
&lt;li&gt;Knowledge graphs&lt;/li&gt;
&lt;li&gt;Automatic layouts&lt;/li&gt;
&lt;/ul&gt;




&lt;h3&gt;
  
  
  Sigma.js
&lt;/h3&gt;

&lt;p&gt;Designed for:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Very large graphs&lt;/li&gt;
&lt;li&gt;High-performance rendering&lt;/li&gt;
&lt;li&gt;Thousands of nodes&lt;/li&gt;
&lt;/ul&gt;




&lt;h3&gt;
  
  
  D3.js
&lt;/h3&gt;

&lt;p&gt;Ideal when you need complete customization and don't mind writing more code.&lt;/p&gt;




&lt;h2&gt;
  
  
  My Recommendation
&lt;/h2&gt;

&lt;p&gt;If I were designing an MCP platform today, I wouldn't make the mind map the default interface.&lt;/p&gt;

&lt;p&gt;Instead, I'd treat it as an &lt;strong&gt;optional exploration layer&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;Users should still have access to:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Traditional chat&lt;/li&gt;
&lt;li&gt;Tables&lt;/li&gt;
&lt;li&gt;Dashboards&lt;/li&gt;
&lt;li&gt;Charts&lt;/li&gt;
&lt;li&gt;SQL results&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;When they want to understand relationships, they can switch to the graph view.&lt;/p&gt;

&lt;p&gt;This keeps the interface simple while providing powerful visualization when it's genuinely useful.&lt;/p&gt;




&lt;h2&gt;
  
  
  Final Thoughts
&lt;/h2&gt;

&lt;p&gt;Mind maps aren't valuable because they look impressive.&lt;/p&gt;

&lt;p&gt;They're valuable because they help users understand relationships that are difficult to see in text, tables, or dashboards.&lt;/p&gt;

&lt;p&gt;For MCP ecosystems, they're especially effective for:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;AI reasoning&lt;/li&gt;
&lt;li&gt;Documentation discovery&lt;/li&gt;
&lt;li&gt;Codebase exploration&lt;/li&gt;
&lt;li&gt;Knowledge graphs&lt;/li&gt;
&lt;li&gt;Multi-agent workflows&lt;/li&gt;
&lt;li&gt;Analytics investigations&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;However, they should never replace dashboards or structured data.&lt;/p&gt;

&lt;p&gt;The most effective MCP platforms will combine both approaches:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Chat for conversations&lt;/strong&gt;&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Dashboards for metrics&lt;/strong&gt;&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Mind maps for relationships&lt;/strong&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Use each interface where it excels.&lt;/p&gt;

&lt;p&gt;In the end, the goal isn't to make AI prettier—it's to make complex information easier to understand.&lt;/p&gt;

</description>
      <category>ai</category>
      <category>llm</category>
      <category>mcp</category>
    </item>
    <item>
      <title>Running Hermes Agent with Kokoro TTS: A Local-First AI Assistant Setup</title>
      <dc:creator>Nishikanta Ray</dc:creator>
      <pubDate>Sun, 26 Jul 2026 07:19:14 +0000</pubDate>
      <link>https://dev.to/nishikantaray/running-hermes-agent-with-kokoro-tts-a-local-first-ai-assistant-setup-523h</link>
      <guid>https://dev.to/nishikantaray/running-hermes-agent-with-kokoro-tts-a-local-first-ai-assistant-setup-523h</guid>
      <description>&lt;p&gt;Most AI agents today depend heavily on cloud APIs. They're fast, but every request costs money, depends on an internet connection, and sends your data to external providers.&lt;/p&gt;

&lt;p&gt;Over the weekend, I experimented with &lt;strong&gt;Hermes Agent&lt;/strong&gt; and &lt;strong&gt;Kokoro TTS&lt;/strong&gt;, building a completely local voice-enabled AI assistant. The result was surprisingly good—responses were generated locally, converted into natural speech, and even delivered through Telegram.&lt;/p&gt;

&lt;p&gt;It felt much closer to having a personal AI operating system than simply chatting with an LLM.&lt;/p&gt;




&lt;h2&gt;
  
  
  What is Hermes Agent?
&lt;/h2&gt;

&lt;p&gt;Hermes Agent is an open-source AI agent framework from Nous Research designed for long-running AI workflows. Unlike a traditional chatbot, Hermes can:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Execute tools&lt;/li&gt;
&lt;li&gt;Remember previous interactions&lt;/li&gt;
&lt;li&gt;Learn reusable skills&lt;/li&gt;
&lt;li&gt;Schedule recurring jobs&lt;/li&gt;
&lt;li&gt;Connect with messaging platforms like Telegram&lt;/li&gt;
&lt;li&gt;Work with both cloud and local models&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;It also supports OpenAI-compatible endpoints, making it easy to swap between cloud providers and local inference engines.&lt;/p&gt;




&lt;h2&gt;
  
  
  Why use Kokoro TTS?
&lt;/h2&gt;

&lt;p&gt;Kokoro is a lightweight text-to-speech engine that runs locally and produces surprisingly natural voices.&lt;/p&gt;

&lt;p&gt;Instead of sending text to cloud TTS providers, everything happens on your own machine.&lt;/p&gt;

&lt;h3&gt;
  
  
  Benefits
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;Completely offline&lt;/li&gt;
&lt;li&gt;No API costs&lt;/li&gt;
&lt;li&gt;Low latency&lt;/li&gt;
&lt;li&gt;Privacy-friendly&lt;/li&gt;
&lt;li&gt;Easy integration with local AI workflows&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;For demos, assistants, and automation, it feels almost instant.&lt;/p&gt;




&lt;h2&gt;
  
  
  Architecture
&lt;/h2&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;                User
                  │
          Telegram / CLI
                  │
          ┌───────────────┐
          │ Hermes Agent  │
          └──────┬────────┘
                 │
      ┌──────────┴──────────┐
      │                     │
 Local LLM             Tool Calls
(Qwen/Llama/etc.)      Browser/MCP/etc.
      │
      ▼
 Response Text
      │
      ▼
 Kokoro TTS
      │
      ▼
 Natural Voice
      │
      ▼
 Telegram Voice Message
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The same response can be delivered as:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Text&lt;/li&gt;
&lt;li&gt;Voice&lt;/li&gt;
&lt;li&gt;Or both simultaneously&lt;/li&gt;
&lt;/ul&gt;




&lt;h2&gt;
  
  
  Why run locally?
&lt;/h2&gt;

&lt;p&gt;A local-first setup offers several advantages:&lt;/p&gt;

&lt;h3&gt;
  
  
  Privacy
&lt;/h3&gt;

&lt;p&gt;Sensitive conversations never leave your machine unless you explicitly enable external tools.&lt;/p&gt;

&lt;h3&gt;
  
  
  Zero inference cost
&lt;/h3&gt;

&lt;p&gt;After downloading the models, you no longer pay per request.&lt;/p&gt;

&lt;h3&gt;
  
  
  Faster iteration
&lt;/h3&gt;

&lt;p&gt;Developers can test prompts, agents, and automations without worrying about API limits.&lt;/p&gt;

&lt;h3&gt;
  
  
  Offline capability
&lt;/h3&gt;

&lt;p&gt;Useful for demos, labs, or environments with limited internet access.&lt;/p&gt;




&lt;h2&gt;
  
  
  Typical Setup
&lt;/h2&gt;

&lt;p&gt;A common local stack looks like this:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Hermes Agent&lt;/li&gt;
&lt;li&gt;Ollama or another OpenAI-compatible local model server&lt;/li&gt;
&lt;li&gt;Kokoro TTS&lt;/li&gt;
&lt;li&gt;Telegram Gateway (optional)&lt;/li&gt;
&lt;li&gt;MCP servers&lt;/li&gt;
&lt;li&gt;Local memory and skills&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Hermes can connect to local model servers such as Ollama, LM Studio, vLLM, llama.cpp, and other OpenAI-compatible endpoints.&lt;/p&gt;




&lt;h2&gt;
  
  
  Real Use Cases
&lt;/h2&gt;

&lt;h3&gt;
  
  
  Personal AI Assistant
&lt;/h3&gt;

&lt;p&gt;Ask questions and receive spoken replies.&lt;/p&gt;

&lt;h3&gt;
  
  
  Coding Agent
&lt;/h3&gt;

&lt;p&gt;Read repositories, execute tools, and explain code aloud.&lt;/p&gt;

&lt;h3&gt;
  
  
  Customer Support Bot
&lt;/h3&gt;

&lt;p&gt;Generate text and voice responses without cloud inference costs.&lt;/p&gt;

&lt;h3&gt;
  
  
  Home Automation
&lt;/h3&gt;

&lt;p&gt;Control local services with voice feedback.&lt;/p&gt;

&lt;h3&gt;
  
  
  Content Creation
&lt;/h3&gt;

&lt;p&gt;Generate narration for tutorials, demos, podcasts, and videos.&lt;/p&gt;

&lt;h3&gt;
  
  
  Research Assistant
&lt;/h3&gt;

&lt;p&gt;Summarise documents and listen instead of reading long reports.&lt;/p&gt;




&lt;h2&gt;
  
  
  Pros
&lt;/h2&gt;

&lt;p&gt;✅ Local inference&lt;/p&gt;

&lt;p&gt;✅ Better privacy&lt;/p&gt;

&lt;p&gt;✅ No recurring API costs&lt;/p&gt;

&lt;p&gt;✅ Natural voice responses&lt;/p&gt;

&lt;p&gt;✅ Works with existing OpenAI-compatible APIs&lt;/p&gt;

&lt;p&gt;✅ Flexible model switching&lt;/p&gt;

&lt;p&gt;✅ Extensible through tools and skills&lt;/p&gt;




&lt;h2&gt;
  
  
  Cons
&lt;/h2&gt;

&lt;p&gt;❌ Requires decent CPU/GPU resources&lt;/p&gt;

&lt;p&gt;❌ Initial model downloads can be large&lt;/p&gt;

&lt;p&gt;❌ Local models may be slower than premium cloud models&lt;/p&gt;

&lt;p&gt;❌ Voice quality depends on the selected TTS model&lt;/p&gt;

&lt;p&gt;❌ Some external tools (web search, browser automation, cloud speech) still require internet access if enabled.&lt;/p&gt;




&lt;h2&gt;
  
  
  Who should use this?
&lt;/h2&gt;

&lt;p&gt;This setup is ideal for:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;AI developers&lt;/li&gt;
&lt;li&gt;Open-source enthusiasts&lt;/li&gt;
&lt;li&gt;Privacy-conscious users&lt;/li&gt;
&lt;li&gt;Local LLM experimentation&lt;/li&gt;
&lt;li&gt;Voice assistant projects&lt;/li&gt;
&lt;li&gt;Internal enterprise tooling&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;If you're already using MCP servers or self-hosted AI infrastructure, Hermes integrates naturally into that workflow.&lt;/p&gt;




&lt;h2&gt;
  
  
  Final Thoughts
&lt;/h2&gt;

&lt;p&gt;Hermes Agent combined with Kokoro TTS creates a surprisingly capable local AI assistant. Instead of just generating text, the system can reason, use tools, remember context, and respond with natural speech—all while keeping inference largely on your own machine.&lt;/p&gt;

&lt;p&gt;For developers building AI assistants, coding agents, research tools, or voice-enabled applications, this local-first stack offers an excellent balance between privacy, flexibility, and cost.&lt;/p&gt;

&lt;p&gt;After experimenting with it over the weekend, it's easy to see why local AI workflows are becoming increasingly practical. Cloud models still have their place for the hardest reasoning tasks, but for many day-to-day workflows, a local setup is already more than capable.&lt;/p&gt;

</description>
      <category>agents</category>
      <category>ai</category>
      <category>llm</category>
      <category>opensource</category>
    </item>
  </channel>
</rss>
